OpenWalnut  1.5.0dev
WQtMenuFiltered.cpp
1 //---------------------------------------------------------------------------
2 //
3 // Project: OpenWalnut ( http://www.openwalnut.org )
4 //
5 // Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
6 // For more information see http://www.openwalnut.org/copying
7 //
8 // This file is part of OpenWalnut.
9 //
10 // OpenWalnut is free software: you can redistribute it and/or modify
11 // it under the terms of the GNU Lesser General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // OpenWalnut is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU Lesser General Public License for more details.
19 //
20 // You should have received a copy of the GNU Lesser General Public License
21 // along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
22 //
23 //---------------------------------------------------------------------------
24 
25 #include <vector>
26 #include <string>
27 
28 #include <QToolButton>
29 
30 #include "core/common/WLogger.h"
31 #include "core/common/WStringUtils.h"
32 
33 #include "WQtMenuFiltered.h"
34 
35 WQtMenuFiltered::WQtMenuFiltered( QAction* config, QWidget* parent ):
36  QMenu( parent )
37 {
38  WQtMenuFiltered::setupFilter( this, config );
39 }
40 
41 WQtMenuFiltered::WQtMenuFiltered( const QString& title, QAction* config, QWidget* parent ):
42  QMenu( title, parent )
43 {
44  WQtMenuFiltered::setupFilter( this, config );
45 }
46 
48 {
49  // cleanup
50 }
51 
53 {
54  // the widget contains the label and text filter box
55  QWidget* widget = new QWidget( to );
56  QHBoxLayout* layout = new QHBoxLayout( widget );
57  widget->setLayout( layout );
58 
59  QLabel* label = new QLabel( "Filter:", widget );
60  to->m_edit = new QLineEdit( widget );
61 
62  // add label, edit and config action
63  layout->addWidget( label );
64  layout->addWidget( to->m_edit );
65  if( config )
66  {
67  QToolButton* cfg = new QToolButton();
68  cfg->setDefaultAction( config );
69  cfg->setToolButtonStyle( Qt::ToolButtonIconOnly );
70  cfg->setAutoRaise( true );
71  layout->addWidget( cfg );
72  }
73 
74  // Add the widget as QWidgetAction
75  QWidgetAction* action = new QWidgetAction( to );
76  action->setDefaultWidget( widget );
77  to->addAction( action );
78 
79  // give focus by default
80  to->m_edit->setFocus();
81 
82  // update filter if text changes
83  connect( to->m_edit, SIGNAL( textChanged( const QString& ) ), to, SLOT( filterUpdate() ) );
84 }
85 
86 void WQtMenuFiltered::keyPressEvent( QKeyEvent* e )
87 {
88  // Fake default QMenu feeling for these keys:
89  // Tab and Cursor
90  // Enter
91  // Escape
92  // But handle keys a-z,A-Z
93  if( ( ( e->key() >= Qt::Key_A ) && ( e->key() <= Qt::Key_Z ) ) ||
94  ( ( e->key() >= Qt::Key_0 ) && ( e->key() <= Qt::Key_9 ) ) ||
95  ( e->key() == Qt::Key_Underscore ) ||
96  ( e->key() == Qt::Key_Space ) ||
97  ( e->key() == Qt::Key_Minus ) ||
98  ( e->key() == Qt::Key_Plus )
99  )
100  {
101  m_edit->setText( m_edit->text() + e->text() );
102  }
103  else if( ( e->key() == Qt::Key_Backspace ) )
104  {
105  m_edit->backspace();
106  }
107  else if( ( e->key() == Qt::Key_Escape ) ) // if escape and currently a filter is active: remove filter
108  {
109  if( m_edit->text() != "" )
110  {
111  resetFilter();
112  }
113  }
114  QMenu::keyPressEvent( e );
115 }
116 
117 void WQtMenuFiltered::hideEvent( QHideEvent* e )
118 {
119  resetFilter();
120  QMenu::hideEvent( e );
121 }
122 
124 {
125  std::vector< std::string > filter = string_utils::tokenize( m_edit->text().toStdString() );
126 
127  // NOTE: we ignore the first element. It is the filter widget.
128 
129  // Important: we need to use a COPY of the action list as Qt sometimes removes actions there. I do not fully understand why.
130  typedef QList< QAction* > Actions;
131  Actions allActions = this->actions();
132  size_t nbLeft = 0; // keep track how many items are left
133  QAction* lastVisibleAction = NULL;
134  for( Actions::const_iterator a = allActions.begin() + 1; a != allActions.end(); ++a )
135  {
136  // strange but I had the issue that there was a NULL item in this list.
137  if( ( *a ) != NULL )
138  {
139  QString s = ( *a )->text();
140 
141  // check each token in the filter
142  bool match = true;
143  for( std::vector< std::string >::const_iterator iter = filter.begin(); iter != filter.end(); ++iter )
144  {
145  match = match && s.contains( QString::fromStdString( *iter ), Qt::CaseInsensitive );
146  }
147 
148  // match value against filter
149  if( match )
150  {
151  #ifdef __APPLE__
152  ( *a )->setDisabled( false );
153  #else
154  ( *a )->setVisible( true );
155  #endif
156  nbLeft++;
157  lastVisibleAction = *a;
158  }
159  else
160  {
161  #ifdef __APPLE__
162  ( *a )->setDisabled( true );
163  #else
164  ( *a )->setVisible( false );
165  #endif
166  }
167  }
168  }
169 
170  // if only one item is left, select it automatically
171  if( nbLeft == 1 )
172  {
173  setActiveAction( lastVisibleAction );
174  }
175 }
176 
178 {
179  // reset text filter. This triggers filterUpdate!
180  m_edit->setText( "" );
181 }
182 
A menu derived from QMenu with additional filtering.
void hideEvent(QHideEvent *e)
Called when hiding the menu.
virtual ~WQtMenuFiltered()
Destructor.
WQtMenuFiltered(QAction *config=0, QWidget *parent=0)
Create filtered menu.
virtual void keyPressEvent(QKeyEvent *e)
Grab key events.
static void setupFilter(WQtMenuFiltered *to, QAction *config=0)
Setup filter on given menu.
void resetFilter()
Resets the filter.
void filterUpdate()
Updates the filter.
QLineEdit * m_edit
Filter textfield.
std::vector< std::string > tokenize(const std::string &source, const std::string &delim=WHITESPACE, bool compress=true)
Splits the given string into a vector of strings (so called tokens).