libyui-qt-pkg  2.45.28
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  : QScrollArea( parent )
72 {
73  QWidget * content = new QWidget;
74  QVBoxLayout * layout = new QVBoxLayout;
75  YUI_CHECK_NEW( layout );
76  content->setLayout( layout );
77  _matchCount = 0;
78 
79  // Box for search button
80  QHBoxLayout * hbox = new QHBoxLayout();
81  YUI_CHECK_NEW( hbox );
82  layout->addLayout(hbox);
83 
84  // Input field ( combo box ) for search text
85  _searchText = new QComboBox( content );
86  YUI_CHECK_NEW( _searchText );
87  _searchText->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Minimum ) );
88 
89  hbox->addWidget(_searchText);
90  _searchText->setEditable( true );
91 
92  // Search button
93  _searchButton = new QPushButton( _( "&Search" ), content );
94  YUI_CHECK_NEW( _searchButton );
95  hbox->addWidget(_searchButton);
96 
97  connect( _searchButton, SIGNAL( clicked() ),
98  this, SLOT ( filter() ) );
99 
100  layout->addStretch();
101 
102  //
103  // Where to search
104  //
105 
106  QGroupBox * gbox = new QGroupBox( _( "Search in" ), content );
107  YUI_CHECK_NEW( gbox );
108  layout->addWidget( gbox );
109  QVBoxLayout *vLayout = new QVBoxLayout;
110  gbox->setLayout( vLayout );
111 
112  _searchInName = new QCheckBox( _( "Nam&e" ), gbox ); YUI_CHECK_NEW( _searchInName );
113  vLayout->addWidget(_searchInName);
114  _searchInKeywords = new QCheckBox( _( "&Keywords" ), gbox ); YUI_CHECK_NEW( _searchInKeywords );
115  vLayout->addWidget(_searchInKeywords);
116  _searchInSummary = new QCheckBox( _( "Su&mmary" ), gbox ); YUI_CHECK_NEW( _searchInSummary );
117  vLayout->addWidget(_searchInSummary);
118  _searchInDescription = new QCheckBox( _( "Descr&iption" ), gbox ); YUI_CHECK_NEW( _searchInDescription );
119  vLayout->addWidget(_searchInDescription);
120 
121  vLayout->addStretch();
122 
123  _searchInProvides = new QCheckBox( _( "RPM \"P&rovides\""), gbox ); YUI_CHECK_NEW( _searchInProvides );
124  vLayout->addWidget(_searchInProvides);
125  _searchInRequires = new QCheckBox( _( "RPM \"Re&quires\""), gbox ); YUI_CHECK_NEW( _searchInRequires );
126  vLayout->addWidget(_searchInRequires);
127 
128  _searchInFileList = new QCheckBox( _( "File list" ), gbox ); YUI_CHECK_NEW( _searchInFileList );
129  vLayout->addWidget(_searchInFileList);
130 
131 
132  _searchInName->setChecked( true );
133  _searchInKeywords->setChecked( true );
134  _searchInSummary->setChecked( true );
135 
136  layout->addStretch();
137 
138 
139  //
140  // Search mode
141  //
142 
143  QLabel * label = new QLabel( _( "Search &Mode:" ), content );
144  YUI_CHECK_NEW( label );
145  layout->addWidget( label );
146 
147  _searchMode = new QComboBox( content );
148  YUI_CHECK_NEW( _searchMode );
149  layout->addWidget( _searchMode );
150 
151  _searchMode->setEditable( false );
152 
153  label->setBuddy( _searchMode );
154 
155  // Caution: combo box items must be inserted in the same order as enum SearchMode!
156  _searchMode->addItem( _( "Contains" ) );
157  _searchMode->addItem( _( "Begins with" ) );
158  _searchMode->addItem( _( "Exact Match" ) );
159  _searchMode->addItem( _( "Use Wild Cards" ) );
160  _searchMode->addItem( _( "Use Regular Expression" ) );
161 
162  _searchMode->setCurrentIndex( Contains );
163 
164 
165  layout->addStretch();
166 
167  _caseSensitive = new QCheckBox( _( "Case Sensiti&ve" ), content );
168  YUI_CHECK_NEW( _caseSensitive );
169  layout->addWidget(_caseSensitive);
170 
171  for ( int i=0; i < 6; i++ )
172  layout->addStretch();
173 
174  setWidgetResizable(true);
175  setWidget(content);
176 }
177 
178 
180 {
181  // NOP
182 }
183 
184 
185 void
187 {
188  if ( event )
189  {
190  if ( event->modifiers() == Qt::NoModifier || // No Ctrl / Alt / Shift etc. pressed
191  event->modifiers() == Qt::KeypadModifier )
192  {
193  if ( event->key() == Qt::Key_Return ||
194  event->key() == Qt::Key_Enter )
195  {
196  _searchButton->animateClick();
197  return;
198  }
199  }
200 
201  }
202 
203  QWidget::keyPressEvent( event );
204 }
205 
206 
207 void
209 {
210  _searchText->setFocus();
211 }
212 
213 
214 QSize
216 {
217  return QSize( 0, 0 );
218 }
219 
220 
221 void
223 {
224  if ( isVisible() )
225  filter();
226 }
227 
228 
229 void
231 {
232  emit filterStart();
233  _matchCount = 0;
234 
235  try
236  {
237  if ( ! _searchText->currentText().isEmpty() )
238  {
239  // Create a progress dialog that is only displayed if the search takes
240  // longer than a couple of seconds ( default: 4 ).
241 
242 
243  zypp::PoolQuery query;
244  query.addKind(zypp::ResKind::package);
245 
246  string searchtext = _searchText->currentText().toUtf8().data();
247 
248  QProgressDialog progress( _( "Searching..." ), // text
249  _( "&Cancel" ), // cancelButtonLabel
250  0,
251  1000,
252  this // parent
253  );
254  progress.setWindowTitle( "" );
255  progress.setMinimumDuration( 1500 ); // millisec
256 
257  // HACK, this should go to YQPackageSelector
258  parentWidget()->parentWidget()->setCursor(Qt::WaitCursor);
259  progress.setCursor(Qt::ArrowCursor);
260 
261  QTime timer;
262  query.setCaseSensitive( _caseSensitive->isChecked() );
263 
264  switch ( _searchMode->currentIndex() )
265  {
266  case Contains:
267  query.setMatchSubstring();
268  break;
269  case BeginsWith:
270  query.setMatchRegex();
271  searchtext = "^" + searchtext;
272  break;
273  case ExactMatch:
274  query.setMatchExact();
275  break;
276  case UseWildcards:
277  query.setMatchGlob();
278  break;
279  case UseRegExp:
280  query.setMatchRegex();
281  break;
282 
283  // Intentionally omitting "default" branch - let gcc watch for unhandled enums
284  }
285 
286  query.addString( searchtext );
287 
288  if ( _searchInName->isChecked() ) query.addAttribute( zypp::sat::SolvAttr::name );
289  if ( _searchInDescription->isChecked() ) query.addAttribute( zypp::sat::SolvAttr::description );
290  if ( _searchInSummary->isChecked() ) query.addAttribute( zypp::sat::SolvAttr::summary );
291  if ( _searchInRequires->isChecked() ) query.addAttribute( zypp::sat::SolvAttr("solvable:requires") );
292  if ( _searchInProvides->isChecked() ) query.addAttribute( zypp::sat::SolvAttr("solvable:provides") );
293  if ( _searchInFileList->isChecked() ) query.addAttribute( zypp::sat::SolvAttr::filelist );
294  if ( _searchInKeywords->isChecked() ) query.addAttribute( zypp::sat::SolvAttr::keywords );
295 
296  _searchText->setEnabled(false);
297  _searchButton->setEnabled(false);
298 
299  timer.start();
300 
301  int count = 0;
302 
303  for ( zypp::PoolQuery::Selectable_iterator it = query.selectableBegin();
304  it != query.selectableEnd() && ! progress.wasCanceled();
305  ++it )
306  {
307  ZyppSel selectable = *it;
308  ZyppPkg zyppPkg = tryCastToZyppPkg( selectable->theObj() );
309 
310  if ( zyppPkg )
311  {
312  _matchCount++;
313  emit filterMatch( selectable, zyppPkg );
314  }
315 
316  if ( progress.wasCanceled() )
317  break;
318 
319  progress.setValue( count++ );
320 
321  if ( timer.elapsed() > 300 ) // milisec
322  {
323  // Process events only every 300 milliseconds - this is very
324  // expensive since both the progress dialog and the package
325  // list change all the time, thus display updates are necessary
326  // each time.
327 
328  qApp->processEvents();
329  timer.restart();
330  }
331  }
332 
333  if ( _matchCount == 0 )
334  emit message( _( "No Results." ) );
335  }
336  }
337  catch ( const std::exception & exception )
338  {
339  yuiWarning() << "CAUGHT zypp exception: " << exception.what() << std::endl;
340 
341  QMessageBox msgBox;
342 
343  // Translators: This is a (short) text indicating that something went
344  // wrong while searching for packages. At this point, it is not clear
345  // if it's a user error (e.g., syntax error in regular expression) or
346  // an internal error. But there is a "Details" button that will return
347  // the original (translated) error message.
348 
349  QString heading = _( "Query Error" );
350 
351  if ( heading.length() < 25 ) // Avoid very narrow message boxes
352  {
353  QString blanks;
354  blanks.fill( ' ', 50 - heading.length() );
355  heading += blanks;
356  }
357 
358  msgBox.setText( heading );
359  msgBox.setIcon( QMessageBox::Warning );
360  msgBox.setInformativeText( fromUTF8( exception.what() ) );
361  msgBox.exec();
362  }
363 
364  _searchText->setEnabled(true);
365  _searchButton->setEnabled(true);
366  parentWidget()->parentWidget()->setCursor(Qt::ArrowCursor);
367 
368  emit filterFinished();
369 }
370 
371 
372 bool
373 YQPkgSearchFilterView::check( ZyppSel selectable,
374  ZyppObj zyppObj )
375 {
376  QRegExp regexp( _searchText->currentText() );
377  regexp.setCaseSensitivity( _caseSensitive->isChecked() ? Qt::CaseSensitive : Qt::CaseInsensitive );
378  regexp.setPatternSyntax( (_searchMode->currentIndex() == UseWildcards) ? QRegExp::Wildcard : QRegExp::RegExp);
379  return check( selectable, zyppObj, regexp );
380 }
381 
382 
383 bool
384 YQPkgSearchFilterView::check( ZyppSel selectable,
385  ZyppObj zyppObj,
386  const QRegExp & regexp )
387 {
388  if ( ! zyppObj )
389  return false;
390 
391  bool match =
392  ( _searchInName->isChecked() && check( zyppObj->name(), regexp ) ) ||
393  ( _searchInSummary->isChecked() && check( zyppObj->summary(), regexp ) ) ||
394  ( _searchInDescription->isChecked() && check( zyppObj->description(), regexp ) ) ||
395  ( _searchInProvides->isChecked() && check( zyppObj->dep( zypp::Dep::PROVIDES ), regexp ) ) ||
396  ( _searchInRequires->isChecked() && check( zyppObj->dep( zypp::Dep::REQUIRES ), regexp ) );
397 
398  if ( match )
399  {
400  ZyppPkg zyppPkg = tryCastToZyppPkg( zyppObj );
401 
402  if ( zyppPkg )
403  {
404  _matchCount++;
405  emit filterMatch( selectable, zyppPkg );
406  }
407  }
408 
409  return match;
410 }
411 
412 
413 bool
414 YQPkgSearchFilterView::check( const string & attribute,
415  const QRegExp & regexp )
416 {
417  QString att = fromUTF8( attribute );
418  QString searchText = _searchText->currentText();
419  bool match = false;
420 
421  switch ( _searchMode->currentIndex() )
422  {
423  case Contains:
424  match = att.contains( searchText, _caseSensitive->isChecked() ? Qt::CaseSensitive : Qt::CaseInsensitive);
425  break;
426 
427  case BeginsWith:
428  match = att.startsWith( searchText ); // only case sensitive
429  break;
430 
431  case ExactMatch:
432  match = ( att == searchText );
433  break;
434 
435  case UseWildcards:
436  case UseRegExp:
437  // Both cases differ in how the regexp is set up during initialization
438  match = att.contains( regexp );
439  break;
440 
441  // Intentionally omitting "default" branch - let gcc watch for unhandled enums
442  }
443 
444  return match;
445 }
446 
447 
448 bool
449 YQPkgSearchFilterView::check( const zypp::Capabilities& capSet, const QRegExp & regexp )
450 {
451  for ( zypp::Capabilities::const_iterator it = capSet.begin();
452  it != capSet.end();
453  ++it )
454  {
455  zypp::CapDetail cap( *it );
456 
457  if ( cap.isSimple() && check( cap.name().asString(), regexp ) )
458  {
459  // yuiDebug() << "Match for " << (*it).asString() << std::endl;
460  return true;
461  }
462  }
463 
464  return false;
465 }
466 
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 &#39;Return&#39; Reimplemented from QVBox / QWidget. ...
void filterStart()
Emitted when the filtering starts.
void setFocus()
Set the keyboard focus into this view&#39;s input field.
void filter()
Filter according to the view&#39;s rules and current selection.