libyui-qt-pkg  2.47.2
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 <QElapsedTimer>
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
186 YQPkgSearchFilterView::keyPressEvent( QKeyEvent * event )
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  QElapsedTimer 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 
YQPkgSearchFilterView::filterFinished
void filterFinished()
Emitted when filtering is finished.
YQPkgSearchFilterView::keyPressEvent
virtual void keyPressEvent(QKeyEvent *event)
Key press event: Execute search upon 'Return' Reimplemented from QVBox / QWidget.
Definition: YQPkgSearchFilterView.cc:185
YQPkgSearchFilterView::YQPkgSearchFilterView
YQPkgSearchFilterView(QWidget *parent)
Constructor.
Definition: YQPkgSearchFilterView.cc:69
YQPkgSearchFilterView::filter
void filter()
Filter according to the view's rules and current selection.
Definition: YQPkgSearchFilterView.cc:229
YQPkgSearchFilterView::filterIfVisible
void filterIfVisible()
Same as filter(), but only if this widget is currently visible.
Definition: YQPkgSearchFilterView.cc:221
YQPkgSearchFilterView::~YQPkgSearchFilterView
virtual ~YQPkgSearchFilterView()
Destructor.
Definition: YQPkgSearchFilterView.cc:178
YQPkgSearchFilterView::filterStart
void filterStart()
Emitted when the filtering starts.
YQPkgSearchFilterView::setFocus
void setFocus()
Set the keyboard focus into this view's input field.
Definition: YQPkgSearchFilterView.cc:207
YQPkgSearchFilterView::filterMatch
void filterMatch(ZyppSel selectable, ZyppPkg pkg)
Emitted during filtering for each pkg that matches the filter.
YQPkgSearchFilterView::minimumSizeHint
virtual QSize minimumSizeHint() const
Returns the minimum size required for this widget.
Definition: YQPkgSearchFilterView.cc:214
YQPkgSearchFilterView::message
void message(const QString &text)
Send a short message about unsuccessful searches.
YQPkgSearchFilterView::check
bool check(ZyppSel selectable, ZyppObj zyppObj)
Check one ResObject against the currently selected values.
Definition: YQPkgSearchFilterView.cc:372