From c851eb75951d64470bfa479ffc801f950dab1c6f Mon Sep 17 00:00:00 2001
From: tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>
Date: Wed, 18 Aug 2010 20:56:45 +0000
Subject: Added search function to Kicker Closes bug 221

git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdebase@1165317 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
---
 kicker/kicker/ui/Makefile.am       |  5 ++-
 kicker/kicker/ui/clicklineedit.cpp | 85 ++++++++++++++++++++++++++++++++++++++
 kicker/kicker/ui/clicklineedit.h   | 63 ++++++++++++++++++++++++++++
 kicker/kicker/ui/k_mnu.cpp         | 85 ++++++++++++++++++++++++++++++++++++++
 kicker/kicker/ui/k_mnu.h           | 12 ++++++
 kicker/kicker/ui/service_mnu.cpp   | 80 +++++++++++++++++++++++++++++++++++
 kicker/kicker/ui/service_mnu.h     |  9 ++++
 7 files changed, 337 insertions(+), 2 deletions(-)
 create mode 100644 kicker/kicker/ui/clicklineedit.cpp
 create mode 100644 kicker/kicker/ui/clicklineedit.h

diff --git a/kicker/kicker/ui/Makefile.am b/kicker/kicker/ui/Makefile.am
index e60e99bfd..1bd54777e 100644
--- a/kicker/kicker/ui/Makefile.am
+++ b/kicker/kicker/ui/Makefile.am
@@ -13,7 +13,7 @@ libkicker_ui_la_SOURCES = addbutton_mnu.cpp appletitem.ui appletview.ui addapple
         recentapps.cpp browser_dlg.cpp \
         removeapplet_mnu.cpp removeextension_mnu.cpp removecontainer_mnu.cpp \
         removebutton_mnu.cpp popupmenutitle.cpp hidebutton.cpp \
-        addappletvisualfeedback.cpp
+        addappletvisualfeedback.cpp clicklineedit.cpp
 
 libkicker_ui_la_LIBADD = $(top_builddir)/libkonq/libkonq.la $(top_builddir)/kdmlib/libdmctl.la
 
@@ -25,7 +25,8 @@ noinst_HEADERS = addapplet.h appletwidget.h addbutton_mnu.h addapplet_mnu.h appl
 		addextension_mnu.h extensionop_mnu.h \
 		recentapps.h browser_dlg.h \
 		removeapplet_mnu.h removeextension_mnu.h removecontainer_mnu.h \
-		removebutton_mnu.h popupmenutitle.h hidebutton.h addappletvisualfeedback.h
+		removebutton_mnu.h popupmenutitle.h hidebutton.h \
+		addappletvisualfeedback.h clicklineedit.h
 
 removecontainer_mnu.lo: ../../libkicker/kickerSettings.h
 removeextension_mnu.lo: ../../libkicker/kickerSettings.h
diff --git a/kicker/kicker/ui/clicklineedit.cpp b/kicker/kicker/ui/clicklineedit.cpp
new file mode 100644
index 000000000..52cb8776d
--- /dev/null
+++ b/kicker/kicker/ui/clicklineedit.cpp
@@ -0,0 +1,85 @@
+/*
+    This file is part of libkdepim.
+    Copyright (c) 2004 Daniel Molkentin <molkentin@kde.org>
+    based on code by Cornelius Schumacher <schumacher@kde.org> 
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+    Boston, MA 02111-1307, USA.
+*/
+
+
+#include "clicklineedit.h"
+
+#include "qpainter.h"
+
+using namespace KPIM;
+
+ClickLineEdit::ClickLineEdit(TQWidget *parent, const TQString &msg, const char* name) :
+  KLineEdit(parent, name) 
+{
+  mDrawClickMsg = true;
+  setClickMessage( msg ); 
+}
+
+ClickLineEdit::~ClickLineEdit() {}
+
+
+void ClickLineEdit::setClickMessage( const TQString &msg )
+{
+  mClickMessage = msg;
+  repaint();
+}
+
+void ClickLineEdit::setText( const TQString &txt )
+{
+  mDrawClickMsg = txt.isEmpty();
+  repaint();
+  KLineEdit::setText( txt );
+}
+
+void ClickLineEdit::drawContents( TQPainter *p )
+{
+  KLineEdit::drawContents( p );
+
+  if ( mDrawClickMsg == true && !hasFocus() ) {
+    TQPen tmp = p->pen();
+    p->setPen( gray );
+    TQRect cr = contentsRect();
+    p->drawText( cr, AlignAuto|AlignVCenter, mClickMessage );
+    p->setPen( tmp );
+  }
+}
+
+void ClickLineEdit::focusInEvent( TQFocusEvent *ev )
+{
+  if ( mDrawClickMsg == true ) 
+  { 
+    mDrawClickMsg = false;
+    repaint();
+  }
+  TQLineEdit::focusInEvent( ev );
+}
+
+void ClickLineEdit::focusOutEvent( TQFocusEvent *ev )
+{
+  if ( text().isEmpty() )
+  {
+    mDrawClickMsg = true;
+    repaint();
+  }
+  TQLineEdit::focusOutEvent( ev );
+}
+
+#include "clicklineedit.moc"
diff --git a/kicker/kicker/ui/clicklineedit.h b/kicker/kicker/ui/clicklineedit.h
new file mode 100644
index 000000000..cadafae4c
--- /dev/null
+++ b/kicker/kicker/ui/clicklineedit.h
@@ -0,0 +1,63 @@
+/*
+    This file is part of libkdepim.
+    Copyright (c) 2004 Daniel Molkentin <molkentin@kde.org> 
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+    Boston, MA 02111-1307, USA.
+*/
+
+#ifndef CLICKLINEEDIT_H
+#define CLICKLINEEDIT_H
+
+#include <klineedit.h>
+
+
+namespace KPIM {
+
+/** 
+  This class provides a KLineEdit which contains a greyed-out hinting
+  text as long as the user didn't enter any text
+
+  @short LineEdit with customizable "Click here" text
+  @author Daniel Molkentin
+*/
+class KDE_EXPORT ClickLineEdit : public KLineEdit
+{
+  Q_OBJECT
+  public:
+    ClickLineEdit( TQWidget *parent, const TQString &msg = TQString::null, const char* name = 0 );
+    ~ClickLineEdit();
+
+    void setClickMessage( const TQString &msg );
+    TQString clickMessage() const { return mClickMessage; } 
+  
+    virtual void setText( const TQString& txt );
+
+  protected:
+    virtual void drawContents( TQPainter *p );
+    virtual void focusInEvent( TQFocusEvent *ev );
+    virtual void focusOutEvent( TQFocusEvent *ev );
+
+  private:
+    TQString mClickMessage;
+    bool mDrawClickMsg;
+
+};
+
+}
+
+#endif // CLICKLINEEDIT_H
+
+
diff --git a/kicker/kicker/ui/k_mnu.cpp b/kicker/kicker/ui/k_mnu.cpp
index b2e3d59bb..589ed74e8 100644
--- a/kicker/kicker/ui/k_mnu.cpp
+++ b/kicker/kicker/ui/k_mnu.cpp
@@ -26,9 +26,13 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 #include <unistd.h>
 #include <dmctl.h>
 
+#include <tqhbox.h>
 #include <tqimage.h>
+#include <tqlabel.h>
 #include <tqpainter.h>
 #include <tqstyle.h>
+#include <tqtimer.h>
+#include <tqtooltip.h>
 
 #include <dcopclient.h>
 #include <kapplication.h>
@@ -40,9 +44,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 #include <kglobal.h>
 #include <kglobalsettings.h>
 #include <kiconloader.h>
+#include <klineedit.h>
 #include <klocale.h>
 #include <kmessagebox.h>
 #include <kstandarddirs.h>
+#include <ktoolbarbutton.h>
 #include <kwin.h>
 
 #include "client_mnu.h"
@@ -57,10 +63,13 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 #include "popupmenutitle.h"
 #include "quickbrowser_mnu.h"
 #include "recentapps.h"
+#include "clicklineedit.h"
 
 #include "k_mnu.h"
 #include "k_mnu.moc"
 
+const int PanelKMenu::searchLineID(23140 /*whatever*/);
+
 PanelKMenu::PanelKMenu()
   : PanelServiceMenu(TQString::null, TQString::null, 0, "KMenu")
   , bookmarkMenu(0)
@@ -165,6 +174,25 @@ void PanelKMenu::paletteChanged()
     }
 }
 
+/* A MenuHBox is supposed to be inserted into a menu.
+ * You can set a special widget in the hbox which will
+ * get the focus if the user moves up or down with the 
+ * cursor keys
+ */ 
+class MenuHBox : public TQHBox {
+public:
+    MenuHBox(PanelKMenu* parent) : TQHBox(parent)
+    {
+    }
+
+    virtual void keyPressEvent(TQKeyEvent *e)
+    {
+
+    }
+private:
+    PanelKMenu *parent;
+};
+
 void PanelKMenu::initialize()
 {
 //    kdDebug(1210) << "PanelKMenu::initialize()" << endl;
@@ -191,6 +219,22 @@ void PanelKMenu::initialize()
     // add services
     PanelServiceMenu::initialize();
 
+    // Insert search field
+    TQHBox* hbox = new TQHBox( this );
+    KToolBarButton *clearButton = new KToolBarButton( "locationbar_erase", 0, hbox );
+    searchEdit = new KPIM::ClickLineEdit(hbox, " "+i18n("Press '/' to search..."));
+    hbox->setFocusPolicy(TQWidget::StrongFocus);
+    hbox->setFocusProxy(searchEdit);
+    hbox->setSpacing( 3 );
+    connect(clearButton, TQT_SIGNAL(clicked()), searchEdit, TQT_SLOT(clear()));
+    connect(this, TQT_SIGNAL(aboutToHide()), this, TQT_SLOT(slotClearSearch())); 
+    connect(searchEdit, TQT_SIGNAL(textChanged(const TQString&)),
+        this, TQT_SLOT( slotUpdateSearch( const TQString&)));
+    insertItem(hbox, searchLineID, 0);
+
+    //TQToolTip::add(clearButton, i18n("Clear Search"));
+    //TQToolTip::add(searchEdit, i18n("Enter the name of an application"));
+
     if (KickerSettings::showMenuTitles())
     {
         int id;
@@ -581,6 +625,47 @@ void PanelKMenu::mouseMoveEvent(TQMouseEvent *e)
     PanelServiceMenu::mouseMoveEvent( &newEvent );
 }
 
+void PanelKMenu::slotUpdateSearch(const TQString& searchString)
+{
+    kdDebug() << "Searching for " << searchString << endl;
+    setSearchString(searchString);
+}
+
+void PanelKMenu::slotClearSearch()
+{
+    if (searchEdit && searchEdit->text().isEmpty() == false) {
+        TQTimer::singleShot(0, searchEdit, TQT_SLOT(clear()));
+    }
+}
+
+void PanelKMenu::keyPressEvent(TQKeyEvent* e)
+{
+    // We move the focus to the search field if the
+    // user presses '/'. This is the same shortcut as
+    // konqueror is using, and afaik it's hardcoded both
+    // here and there. This sucks badly for many non-us
+    // keyboard layouts, but for the sake of consistency
+    // we follow konqueror.
+    if (!searchEdit) return KPanelMenu::keyPressEvent(e);
+
+    if (e->key() == TQt::Key_Slash && !searchEdit->hasFocus()) {
+        if (indexOf(searchLineID) >=0 ) {
+            setActiveItem(indexOf(searchLineID));
+        }
+    }
+    else if (e->key() == TQt::Key_Escape && searchEdit->text().isEmpty() == false) {
+        searchEdit->clear();
+    }
+    else if (e->key() == TQt::Key_Delete && !searchEdit->hasFocus() && 
+        searchEdit->text().isEmpty() == false)
+    {
+        searchEdit->clear();
+    }
+    else {
+        KPanelMenu::keyPressEvent(e);
+    }
+}
+
 void PanelKMenu::configChanged()
 {
     RecentlyLaunchedApps::the().m_bNeedToUpdate = false;
diff --git a/kicker/kicker/ui/k_mnu.h b/kicker/kicker/ui/k_mnu.h
index 412648f1b..91904e0d0 100644
--- a/kicker/kicker/ui/k_mnu.h
+++ b/kicker/kicker/ui/k_mnu.h
@@ -30,6 +30,12 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
 #include "service_mnu.h"
 
+namespace KPIM {
+    // Yes, ClickLineEdit was copied from libkdepim.
+    // Can we have it in kdelibs please?
+    class ClickLineEdit;
+}
+
 class KickerClientMenu;
 class KBookmarkMenu;
 class KActionCollection;
@@ -73,6 +79,8 @@ protected slots:
     void slotSaveSession();
     void slotRunCommand();
     void slotEditUserContact();
+    void slotUpdateSearch(const TQString &searchtext);
+    void slotClearSearch();
     void paletteChanged();
     virtual void configChanged();
     void updateRecent();
@@ -87,6 +95,8 @@ protected:
     void mouseMoveEvent(TQMouseEvent *);
     bool loadSidePixmap();
     void doNewSession(bool lock);
+    void filterMenu(PanelServiceMenu* menu, const TQString &searchString);
+    void keyPressEvent(TQKeyEvent* e);
     void createRecentMenuItems();
     virtual void clearSubmenus();
 
@@ -101,6 +111,8 @@ private:
     KActionCollection          *actionCollection;
     KBookmarkOwner             *bookmarkOwner;
     PopupMenuList               dynamicSubMenus;
+    KPIM::ClickLineEdit        *searchEdit;
+    static const int            searchLineID;
 };
 
 #endif
diff --git a/kicker/kicker/ui/service_mnu.cpp b/kicker/kicker/ui/service_mnu.cpp
index 9b46b2396..fa18a7038 100644
--- a/kicker/kicker/ui/service_mnu.cpp
+++ b/kicker/kicker/ui/service_mnu.cpp
@@ -26,6 +26,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 #include <tqbitmap.h>
 #include <tqpixmap.h>
 #include <tqimage.h>
+#include <tqmap.h>
 
 #include <dcopclient.h>
 #include <kapplication.h>
@@ -100,6 +101,8 @@ void PanelServiceMenu::initialize()
     clear();
 
     clearSubmenus();
+    searchSubMenuIDs.clear();
+    searchMenuItems.clear();
     doInitialize();
 }
 
@@ -202,6 +205,10 @@ void PanelServiceMenu::fillMenu(KServiceGroup::Ptr& _root,
 
             int newId = insertItem(iconset, groupCaption, m, id++);
             entryMap_.insert(newId, static_cast<KSycocaEntry*>(g));
+            // This submenu will be searched when applying a search string
+            searchSubMenuIDs[m] = newId;
+            // Also search the submenu name itself
+            searchMenuItems.insert(newId);
             // We have to delete the sub menu our selves! (See Qt docs.)
             subMenus.append(m);
         }
@@ -214,6 +221,7 @@ void PanelServiceMenu::fillMenu(KServiceGroup::Ptr& _root,
             }
 
             KService::Ptr s(static_cast<KService *>(e));
+            searchMenuItems.insert(id);
             insertMenuItem(s, id++, -1, &suppressGenericNames);
         }
         else if (e->isType(KST_KServiceSeparator))
@@ -797,6 +805,8 @@ void PanelServiceMenu::slotClear()
         delete *it;
     }
     subMenus.clear();
+    searchSubMenuIDs.clear();
+    searchMenuItems.clear();
 }
 
 void PanelServiceMenu::selectFirstItem()
@@ -804,6 +814,76 @@ void PanelServiceMenu::selectFirstItem()
     setActiveItem(indexOf(serviceMenuStartId()));
 }
 
+void PanelServiceMenu::setSearchString(const TQString &searchString)
+{
+    // We must initialize the menu, because it might have not been opened before
+    initialize();
+
+    bool foundSomething = false;
+    std::set<int> nonemptyMenus;
+    std::set<int>::const_iterator menuItemIt(searchMenuItems.begin());
+    // Apply the filter on this menu
+    for (; menuItemIt != searchMenuItems.end(); ++menuItemIt) {
+        int id = *menuItemIt;
+        KService* s = dynamic_cast< KService* >( static_cast< KSycocaEntry* >( entryMap_[ id ]));
+        TQString menuText = text(id);
+        if (menuText.contains(searchString, false) > 0
+            || ( s != NULL && ( s->name().contains(searchString, false) > 0
+                               || s->exec().contains(searchString, false) > 0
+                               || s->comment().contains(searchString, false) > 0
+                               || s->genericName().contains(searchString, false) > 0
+                               || s->exec().contains(searchString, false) > 0 )
+                )) {
+            setItemEnabled(id, true);
+            foundSomething = true;
+            nonemptyMenus.insert(id);
+        }
+        else {
+            setItemEnabled(id, false);
+        }
+    }
+    // Apply the filter on this menu
+    /*for (int i=count()-1; i>=0; --i) {
+        int id = idAt(i);
+        TQString menuText = text(id);
+        if (menuText.contains(searchString, false) > 0) {
+            setItemEnabled(id, true);
+            foundSomething = true;
+            nonemptyMenus.insert(id);
+        }
+        else {
+            setItemEnabled(id, false);
+        }
+    }*/
+
+    PanelServiceMenuMap::iterator it(searchSubMenuIDs.begin());
+    // Apply the search filter on submenus
+    for (; it != searchSubMenuIDs.end(); ++it) {
+        it.key()->setSearchString(searchString);
+        if (nonemptyMenus.find(it.data()) != nonemptyMenus.end()) {
+            // if the current menu is a match already, we don't
+            // block access to the contained items
+            setItemEnabled(it.data(), true);
+            it.key()->setSearchString(TQString());
+            foundSomething = true;
+        }
+        else if (it.key()->hasSearchResults()) {
+            setItemEnabled(it.data(), true);
+            foundSomething = true;
+        }
+        else {
+            setItemEnabled(it.data(), false);
+        }
+    }
+
+    hasSearchResults_ = foundSomething;
+}
+
+bool PanelServiceMenu::hasSearchResults()
+{
+    return hasSearchResults_;
+}
+
 // updates "recent" section of KMenu
 void PanelServiceMenu::updateRecentlyUsedApps(KService::Ptr &service)
 {
diff --git a/kicker/kicker/ui/service_mnu.h b/kicker/kicker/ui/service_mnu.h
index 3bc112348..193e4faf8 100644
--- a/kicker/kicker/ui/service_mnu.h
+++ b/kicker/kicker/ui/service_mnu.h
@@ -25,6 +25,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 #define SERVICE_MENU_H
 
 #include <tqmap.h>
+#include <set>
 #include <tqvaluevector.h>
 
 #include <ksycocaentry.h>
@@ -41,8 +42,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  * @author Rik Hemsley <rik@kde.org>
  */
 
+class KLineEdit;
 typedef TQMap<int, KSycocaEntry::Ptr> EntryMap;
 typedef TQValueVector<TQPopupMenu*> PopupMenuList;
+class PanelServiceMenu;
+typedef TQMap<PanelServiceMenu*,int> PanelServiceMenuMap;
 
 class KDE_EXPORT PanelServiceMenu : public KPanelMenu
 {
@@ -63,6 +67,8 @@ public:
     virtual void showMenu();
     bool highlightMenuItem( const TQString &menuId );
     void selectFirstItem();
+    void setSearchString(const TQString& searchString);
+    bool hasSearchResults();
 
 private:
     void fillMenu( KServiceGroup::Ptr &_root, KServiceGroup::List &_list,
@@ -114,6 +120,9 @@ protected:
     bool addmenumode_;
     TQPoint startPos_;
     PopupMenuList subMenus;
+    PanelServiceMenuMap searchSubMenuIDs;
+    bool hasSearchResults_;
+    std::set<int> searchMenuItems;
 
 private slots:
     void slotContextMenu(int);
-- 
cgit v1.2.3

