libyui-qt-pkg  2.44.7
 All Classes Functions Variables Enumerations
YQPkgSearchFilterView.cc
1 /**************************************************************************
2 Copyright (C) 2000 - 2010 Novell, Inc.
3 All Rights Reserved.
4 
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 
19 **************************************************************************/
20 
21 
22 /*---------------------------------------------------------------------\
23 | |
24 | __ __ ____ _____ ____ |
25 | \ \ / /_ _/ ___|_ _|___ \ |
26 | \ V / _` \___ \ | | __) | |
27 | | | (_| |___) || | / __/ |
28 | |_|\__,_|____/ |_| |_____| |
29 | |
30 | core system |
31 | (C) SuSE GmbH |
32 \----------------------------------------------------------------------/
33 
34  File: YQPkgSearchFilterView.cc
35 
36  Author: Stefan Hundhammer <sh@suse.de>
37 
38  Textdomain "qt-pkg"
39 
40 /-*/
41 
42 #include <QCheckBox>
43 #include <QComboBox>
44 #include <QLabel>
45 #include <QLayout>
46 #include <QPushButton>
47 #include <QRadioButton>
48 #include <QGroupBox>
49 #include <QProgressDialog>
50 #include <QDateTime>
51 #include <QKeyEvent>
52 #include <QMessageBox>
53 
54 #include <zypp/PoolQuery.h>
55 
56 #define YUILogComponent "qt-pkg"
57 #include <YUILog.h>
58 
59 #include "YQPackageSelector.h"
60 #include "YQPkgSearchFilterView.h"
61 #include "QY2LayoutUtils.h"
62 #include "YQi18n.h"
63 #include "utf8.h"
64 #include "YQApplication.h"
65 #include "YQUI.h"
66 
67 using std::list;
68 using std::string;
69 
71  : QWidget( parent )
72 {
73  QVBoxLayout * layout = new QVBoxLayout;
74  YUI_CHECK_NEW( layout );
75  setLayout( layout );
76  _matchCount = 0;
77 
78  // Box for search button
79  QHBoxLayout * hbox = new QHBoxLayout();
80  YUI_CHECK_NEW( hbox );
81  layout->addLayout(hbox);
82 
83  // Input field ( combo box ) for search text
84  _searchText = new QComboBox( this );
85  YUI_CHECK_NEW( _searchText );
86  _searchText->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Minimum ) );
87 
88  hbox->addWidget(_searchText);
89  _searchText->setEditable( true );
90 
91  // Search button
92  _searchButton = new QPushButton( _( "&Search" ), this );
93  YUI_CHECK_NEW( _searchButton );
94  hbox->addWidget(_searchButton);
95 
96  connect( _searchButton, SIGNAL( clicked() ),
97  this, SLOT ( filter() ) );
98 
99  layout->addStretch();
100 
101  //
102  // Where to search
103  //
104 
105  QGroupBox * gbox = new QGroupBox( _( "Search in" ), this );
106  YUI_CHECK_NEW( gbox );
107  layout->addWidget( gbox );
108  QVBoxLayout *vLayout = new QVBoxLayout;
109  gbox->setLayout( vLayout );
110 
111  _searchInName = new QCheckBox( _( "Nam&e" ), gbox ); YUI_CHECK_NEW( _searchInName );
112  vLayout->addWidget(_searchInName);
113  _searchInKeywords = new QCheckBox( _( "&Keywords" ), gbox ); YUI_CHECK_NEW( _searchInKeywords );
114  vLayout->addWidget(_searchInKeywords);
115  _searchInSummary = new QCheckBox( _( "Su&mmary" ), gbox ); YUI_CHECK_NEW( _searchInSummary );
116  vLayout->addWidget(_searchInSummary);
117  _searchInDescription = new QCheckBox( _( "Descr&iption" ), gbox ); YUI_CHECK_NEW( _searchInDescription );
118  vLayout->addWidget(_searchInDescription);
119 
120  vLayout->addStretch();
121 
122  _searchInProvides = new QCheckBox( _( "RPM \"P&rovides\""), gbox ); YUI_CHECK_NEW( _searchInProvides );
123  vLayout->addWidget(_searchInProvides);
124  _searchInRequires = new QCheckBox( _( "RPM \"Re&quires\""), gbox ); YUI_CHECK_NEW( _searchInRequires );
125  vLayout->addWidget(_searchInRequires);
126 
127  _searchInFileList = new QCheckBox( _( "File list" ), gbox ); YUI_CHECK_NEW( _searchInFileList );
128  vLayout->addWidget(_searchInFileList);
129 
130 
131  _searchInName->setChecked( true );
132  _searchInKeywords->setChecked( true );
133  _searchInSummary->setChecked( true );
134 
135  layout->addStretch();
136 
137 
138  //
139  // Search mode
140  //
141 
142  QLabel * label = new QLabel( _( "Search &Mode:" ), this );
143  YUI_CHECK_NEW( label );
144  layout->addWidget( label );
145 
146  _searchMode = new QComboBox( this );
147  YUI_CHECK_NEW( _searchMode );
148  layout->addWidget( _searchMode );
149 
150  _searchMode->setEditable( false );
151 
152  label->setBuddy( _searchMode );
153 
154  // Caution: combo box items must be inserted in the same order as enum SearchMode!
155  _searchMode->addItem( _( "Contains" ) );
156  _searchMode->addItem( _( "Begins with" ) );
157  _searchMode->addItem( _( "Exact Match" ) );
158  _searchMode->addItem( _( "Use Wild Cards" ) );
159  _searchMode->addItem( _( "Use Regular Expression" ) );
160 
161  _searchMode->setCurrentIndex( Contains );
162 
163 
164  layout->addStretch();
165 
166  _caseSensitive = new QCheckBox( _( "Case Sensiti&ve" ), this );
167  YUI_CHECK_NEW( _caseSensitive );
168  layout->addWidget(_caseSensitive);
169 
170  for ( int i=0; i < 6; i++ )
171  layout->addStretch();
172 }
173 
174 
176 {
177  // NOP
178 }
179 
180 
181 void
183 {
184  if ( event )
185  {
186  if ( event->modifiers() == Qt::NoModifier || // No Ctrl / Alt / Shift etc. pressed
187  event->modifiers() == Qt::KeypadModifier )
188  {
189  if ( event->key() == Qt::Key_Return ||
190  event->key() == Qt::Key_Enter )
191  {
192  _searchButton->animateClick();
193  return;
194  }
195  }
196 
197  }
198 
199  QWidget::keyPressEvent( event );
200 }
201 
202 
203 void
205 {
206  _searchText->setFocus();
207 }
208 
209 
210 QSize
212 {
213  return QSize( 0, 0 );
214 }
215 
216 
217 void
219 {
220  if ( isVisible() )
221  filter();
222 }
223 
224 
225 void
227 {
228  emit filterStart();
229  _matchCount = 0;
230 
231  try
232  {
233  if ( ! _searchText->currentText().isEmpty() )
234  {
235  // Create a progress dialog that is only displayed if the search takes
236  // longer than a couple of seconds ( default: 4 ).
237 
238 
239  zypp::PoolQuery query;
240  query.addKind(zypp::ResKind::package);
241 
242  string searchtext = _searchText->currentText().toUtf8().data();
243 
244  QProgressDialog progress( _( "Searching..." ), // text
245  _( "&Cancel" ), // cancelButtonLabel
246  0,
247  1000,
248  this // parent
249  );
250  progress.setWindowTitle( "" );
251  progress.setMinimumDuration( 1500 ); // millisec
252 
253  // HACK, this should go to YQPackageSelector
254  parentWidget()->parentWidget()->setCursor(Qt::WaitCursor);
255  progress.setCursor(Qt::ArrowCursor);
256 
257  QTime timer;
258  query.setCaseSensitive( _caseSensitive->isChecked() );
259 
260  switch ( _searchMode->currentIndex() )
261  {
262  case Contains:
263  query.setMatchSubstring();
264  break;
265  case BeginsWith:
266  query.setMatchRegex();
267  searchtext = "^" + searchtext;
268  break;
269  case ExactMatch:
270  query.setMatchExact();
271  break;
272  case UseWildcards:
273  query.setMatchGlob();
274  break;
275  case UseRegExp:
276  query.setMatchRegex();
277  break;
278 
279  // Intentionally omitting "default" branch - let gcc watch for unhandled enums
280  }
281 
282  query.addString( searchtext );
283 
284  if ( _searchInName->isChecked() ) query.addAttribute( zypp::sat::SolvAttr::name );
285  if ( _searchInDescription->isChecked() ) query.addAttribute( zypp::sat::SolvAttr::description );
286  if ( _searchInSummary->isChecked() ) query.addAttribute( zypp::sat::SolvAttr::summary );
287  if ( _searchInRequires->isChecked() ) query.addAttribute( zypp::sat::SolvAttr("solvable:requires") );
288  if ( _searchInProvides->isChecked() ) query.addAttribute( zypp::sat::SolvAttr("solvable:provides") );
289  if ( _searchInFileList->isChecked() ) query.addAttribute( zypp::sat::SolvAttr::filelist );
290  if ( _searchInKeywords->isChecked() ) query.addAttribute( zypp::sat::SolvAttr::keywords );
291 
292  _searchText->setEnabled(false);
293  _searchButton->setEnabled(false);
294 
295  timer.start();
296 
297  int count = 0;
298 
299  for ( zypp::PoolQuery::Selectable_iterator it = query.selectableBegin();
300  it != query.selectableEnd() && ! progress.wasCanceled();
301  ++it )
302  {
303  ZyppSel selectable = *it;
304  ZyppPkg zyppPkg = tryCastToZyppPkg( selectable->theObj() );
305 
306  if ( zyppPkg )
307  {
308  _matchCount++;
309  emit filterMatch( selectable, zyppPkg );
310  }
311 
312  if ( progress.wasCanceled() )
313  break;
314 
315  progress.setValue( count++ );
316 
317  if ( timer.elapsed() > 300 ) // milisec
318  {
319  // Process events only every 300 milliseconds - this is very
320  // expensive since both the progress dialog and the package
321  // list change all the time, thus display updates are necessary
322  // each time.
323 
324  qApp->processEvents();
325  timer.restart();
326  }
327  }
328 
329  if ( _matchCount == 0 )
330  emit message( _( "No Results." ) );
331  }
332  }
333  catch ( const std::exception & exception )
334  {
335  yuiWarning() << "CAUGHT zypp exception: " << exception.what() << std::endl;
336 
337  QMessageBox msgBox;
338 
339  // Translators: This is a (short) text indicating that something went
340  // wrong while searching for packages. At this point, it is not clear
341  // if it's a user error (e.g., syntax error in regular expression) or
342  // an internal error. But there is a "Details" button that will return
343  // the original (translated) error message.
344 
345  QString heading = _( "Query Error" );
346 
347  if ( heading.length() < 25 ) // Avoid very narrow message boxes
348  {
349  QString blanks;
350  blanks.fill( ' ', 50 - heading.length() );
351  heading += blanks;
352  }
353 
354  msgBox.setText( heading );
355  msgBox.setIcon( QMessageBox::Warning );
356  msgBox.setInformativeText( fromUTF8( exception.what() ) );
357  msgBox.exec();
358  }
359 
360  _searchText->setEnabled(true);
361  _searchButton->setEnabled(true);
362  parentWidget()->parentWidget()->setCursor(Qt::ArrowCursor);
363 
364  emit filterFinished();
365 }
366 
367 
368 bool
369 YQPkgSearchFilterView::check( ZyppSel selectable,
370  ZyppObj zyppObj )
371 {
372  QRegExp regexp( _searchText->currentText() );
373  regexp.setCaseSensitivity( _caseSensitive->isChecked() ? Qt::CaseSensitive : Qt::CaseInsensitive );
374  regexp.setPatternSyntax( (_searchMode->currentIndex() == UseWildcards) ? QRegExp::Wildcard : QRegExp::RegExp);
375  return check( selectable, zyppObj, regexp );
376 }
377 
378 
379 bool
380 YQPkgSearchFilterView::check( ZyppSel selectable,
381  ZyppObj zyppObj,
382  const QRegExp & regexp )
383 {
384  if ( ! zyppObj )
385  return false;
386 
387  bool match =
388  ( _searchInName->isChecked() && check( zyppObj->name(), regexp ) ) ||
389  ( _searchInSummary->isChecked() && check( zyppObj->summary(), regexp ) ) ||
390  ( _searchInDescription->isChecked() && check( zyppObj->description(), regexp ) ) ||
391  ( _searchInProvides->isChecked() && check( zyppObj->dep( zypp::Dep::PROVIDES ), regexp ) ) ||
392  ( _searchInRequires->isChecked() && check( zyppObj->dep( zypp::Dep::REQUIRES ), regexp ) );
393 
394  if ( match )
395  {
396  ZyppPkg zyppPkg = tryCastToZyppPkg( zyppObj );
397 
398  if ( zyppPkg )
399  {
400  _matchCount++;
401  emit filterMatch( selectable, zyppPkg );
402  }
403  }
404 
405  return match;
406 }
407 
408 
409 bool
410 YQPkgSearchFilterView::check( const string & attribute,
411  const QRegExp & regexp )
412 {
413  QString att = fromUTF8( attribute );
414  QString searchText = _searchText->currentText();
415  bool match = false;
416 
417  switch ( _searchMode->currentIndex() )
418  {
419  case Contains:
420  match = att.contains( searchText, _caseSensitive->isChecked() ? Qt::CaseSensitive : Qt::CaseInsensitive);
421  break;
422 
423  case BeginsWith:
424  match = att.startsWith( searchText ); // only case sensitive
425  break;
426 
427  case ExactMatch:
428  match = ( att == searchText );
429  break;
430 
431  case UseWildcards:
432  case UseRegExp:
433  // Both cases differ in how the regexp is set up during initialization
434  match = att.contains( regexp );
435  break;
436 
437  // Intentionally omitting "default" branch - let gcc watch for unhandled enums
438  }
439 
440  return match;
441 }
442 
443 
444 bool
445 YQPkgSearchFilterView::check( const zypp::Capabilities& capSet, const QRegExp & regexp )
446 {
447  for ( zypp::Capabilities::const_iterator it = capSet.begin();
448  it != capSet.end();
449  ++it )
450  {
451  zypp::CapDetail cap( *it );
452 
453  if ( cap.isSimple() && check( cap.name().asString(), regexp ) )
454  {
455  // yuiDebug() << "Match for " << (*it).asString() << std::endl;
456  return true;
457  }
458  }
459 
460  return false;
461 }
462 
463 #include "YQPkgSearchFilterView.moc"
void filterMatch(ZyppSel selectable, ZyppPkg pkg)
Emitted during filtering for each pkg that matches the filter.
bool check(ZyppSel selectable, ZyppObj zyppObj)
Check one ResObject against the currently selected values.
void filterIfVisible()
Same as filter(), but only if this widget is currently visible.
YQPkgSearchFilterView(QWidget *parent)
Constructor.
virtual QSize minimumSizeHint() const
Returns the minimum size required for this widget.
void message(const QString &text)
Send a short message about unsuccessful searches.
void filterFinished()
Emitted when filtering is finished.
virtual ~YQPkgSearchFilterView()
Destructor.
virtual void keyPressEvent(QKeyEvent *event)
Key press event: Execute search upon 'Return' Reimplemented from QVBox / QWidget. ...
void filterStart()
Emitted when the filtering starts.
void setFocus()
Set the keyboard focus into this view's input field.
void filter()
Filter according to the view's rules and current selection.