libyui-qt-pkg  2.42.5
 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"