]> icculus.org git repositories - duncan/yast2-qt4.git/blob - src/pkg/YQPkgSearchFilterView.cc
clicking packages work! so the package selector is now
[duncan/yast2-qt4.git] / src / pkg / YQPkgSearchFilterView.cc
1 /*---------------------------------------------------------------------\
2 |                                                                      |
3 |                      __   __    ____ _____ ____                      |
4 |                      \ \ / /_ _/ ___|_   _|___ \                     |
5 |                       \ V / _` \___ \ | |   __) |                    |
6 |                        | | (_| |___) || |  / __/                     |
7 |                        |_|\__,_|____/ |_| |_____|                    |
8 |                                                                      |
9 |                               core system                            |
10 |                                                        (C) SuSE GmbH |
11 \----------------------------------------------------------------------/
12
13   File:       YQPkgSearchFilterView.cc
14
15   Author:     Stefan Hundhammer <sh@suse.de>
16
17   Textdomain "packages-qt"
18
19 /-*/
20
21 #include <QCheckBox>
22 #include <QComboBox>
23 #include <QLabel>
24 #include <QLayout>
25 #include <QPushButton>
26 #include <QRadioButton>
27 #include <QGroupBox>
28 #include <QProgressDialog>
29 #include <QDateTime>
30 #include <QKeyEvent>
31
32 #define y2log_component "qt-pkg"
33 #include <ycp/y2log.h>
34
35 #include "YQPkgSearchFilterView.h"
36 #include "QY2LayoutUtils.h"
37 #include "YQi18n.h"
38 #include "utf8.h"
39 #include "YQApplication.h"
40 #include "YQUI.h"
41
42 using std::list;
43 using std::string;
44
45
46 #define SPACING                 6       // between subwidgets
47 #define MARGIN                  4       // around the widget
48
49
50 YQPkgSearchFilterView::YQPkgSearchFilterView( QWidget * parent )
51     : QWidget( parent )
52 {
53     QVBoxLayout *layout = new QVBoxLayout;
54     setLayout(layout);
55     _matchCount = 0;
56     layout->setMargin( MARGIN );
57     layout->setSpacing( SPACING );
58
59     layout->addStretch();
60
61     // Headline
62     QLabel * label = new QLabel( _( "Searc&h:" ), this );
63     Q_CHECK_PTR( label );
64     layout->addWidget(label);
65     label->setFont( YQUI::yqApp()->headingFont() );
66
67     // Input field ( combo box ) for search text
68     _searchText = new QComboBox( this );
69     Q_CHECK_PTR( _searchText );
70     layout->addWidget(_searchText);
71     _searchText->setEditable( true );
72     label->setBuddy( _searchText );
73
74
75     // Box for search button
76     QHBoxLayout * hbox = new QHBoxLayout();
77     Q_CHECK_PTR( hbox );
78     layout->addLayout(hbox);
79     hbox->addStretch();
80
81     // Search button
82     _searchButton = new QPushButton( _( "&Search" ), this );
83     Q_CHECK_PTR( _searchButton );
84     hbox->addWidget(_searchButton);
85
86     connect( _searchButton, SIGNAL( clicked() ),
87              this,          SLOT  ( filter()  ) );
88
89     layout->addStretch();
90
91     //
92     // Where to search
93     //
94     
95     QGroupBox * gbox = new QGroupBox( _( "Search in" ), this );
96     Q_CHECK_PTR( gbox );
97     layout->addWidget(gbox);
98     QVBoxLayout *vlayout = new QVBoxLayout;
99     gbox->setLayout(vlayout);
100
101     _searchInName        = new QCheckBox( _( "&Name"            ), gbox ); Q_CHECK_PTR( _searchInName        );
102     vlayout->addWidget(_searchInName);
103     _searchInSummary     = new QCheckBox( _( "Su&mmary"         ), gbox ); Q_CHECK_PTR( _searchInSummary     );
104     vlayout->addWidget(_searchInSummary);
105     _searchInDescription = new QCheckBox( _( "Descr&iption"     ), gbox ); Q_CHECK_PTR( _searchInDescription );
106     vlayout->addWidget(_searchInDescription);
107
108     vlayout->addStretch();
109
110     // Intentionally NOT marking RPM tags for translation
111     _searchInProvides    = new QCheckBox(  "RPM \"&Provides\""   , gbox ); Q_CHECK_PTR( _searchInProvides    );
112     vlayout->addWidget(_searchInProvides);
113     _searchInRequires    = new QCheckBox(  "RPM \"Re&quires\""   , gbox ); Q_CHECK_PTR( _searchInRequires    );
114     vlayout->addWidget(_searchInRequires);
115
116     _searchInName->setChecked( true );
117     _searchInSummary->setChecked( true );
118
119     layout->addStretch();
120
121
122     //
123     // Search mode
124     //
125
126     label = new QLabel( _( "Search &Mode:" ), this );
127     Q_CHECK_PTR( label );
128     layout->addWidget(label);
129
130     _searchMode = new QComboBox( this );
131     Q_CHECK_PTR( _searchMode );
132     layout->addWidget(_searchMode);
133
134     _searchMode->setEditable( false );
135
136     label->setBuddy( _searchMode );
137
138     // Caution: combo box items must be inserted in the same order as enum SearchMode!
139     _searchMode->addItem( _( "Contains"          ) );
140     _searchMode->addItem( _( "Begins with"               ) );
141     _searchMode->addItem( _( "Exact Match"               ) );
142     _searchMode->addItem( _( "Use Wild Cards"    ) );
143     _searchMode->addItem( _( "Use Regular Expression" ) );
144
145     _searchMode->setCurrentIndex( Contains );
146
147
148     layout->addStretch();
149
150     _caseSensitive = new QCheckBox( _( "Case Sensiti&ve" ), this );
151     Q_CHECK_PTR( _caseSensitive );
152     layout->addWidget(_caseSensitive);
153
154     for ( int i=0; i < 6; i++ )
155       layout->addStretch();
156 }
157
158
159 YQPkgSearchFilterView::~YQPkgSearchFilterView()
160 {
161     // NOP
162 }
163
164
165 void
166 YQPkgSearchFilterView::keyPressEvent( QKeyEvent * event )
167 {
168     if ( event )
169     {
170         if ( event->modifiers() == Qt::NoModifier )     // No Ctrl / Alt / Shift etc. pressed
171         {
172             if ( event->key() == Qt::Key_Return ||
173                  event->key() == Qt::Key_Enter    )
174             {
175                 _searchButton->animateClick();
176                 return;
177             }
178         }
179
180     }
181
182     QWidget::keyPressEvent( event );
183 }
184
185
186 void
187 YQPkgSearchFilterView::setFocus()
188 {
189     _searchText->setFocus();
190 }
191
192
193 QSize
194 YQPkgSearchFilterView::minimumSizeHint() const
195 {
196     return QSize( 0, 0 );
197 }
198
199
200 void
201 YQPkgSearchFilterView::filterIfVisible()
202 {
203     if ( isVisible() )
204         filter();
205 }
206
207
208 void
209 YQPkgSearchFilterView::filter()
210 {
211     emit filterStart();
212     _matchCount = 0;
213
214     if ( ! _searchText->currentText().isEmpty() )
215     {
216         // Create a progress dialog that is only displayed if the search takes
217         // longer than a couple of seconds ( default: 4 ).
218
219         QProgressDialog progress( _( "Searching..." ),                  // text
220                                   _( "&Cancel" ),                       // cancelButtonLabel
221           0,
222                                   1000,
223                                   this                  // parent
224                                   );
225         progress.setWindowTitle( "" );
226         progress.setMinimumDuration( 2000 ); // millisec
227         QTime timer;
228
229         QRegExp regexp( _searchText->currentText() );
230         regexp.setCaseSensitivity( _caseSensitive->isChecked() ? Qt::CaseSensitive : Qt::CaseInsensitive );
231   regexp.setPatternSyntax( (_searchMode->currentIndex() == UseWildcards) ? QRegExp::Wildcard : QRegExp::RegExp);
232
233         timer.start();
234
235
236         int count = 0;
237
238         for ( ZyppPoolIterator it = zyppPkgBegin();
239               it != zyppPkgEnd() && ! progress.wasCanceled();
240               ++it )
241         {
242             ZyppSel selectable = *it;
243
244             bool match =
245                 check( selectable, selectable->candidateObj(), regexp ) ||
246                 check( selectable, selectable->installedObj(), regexp );
247
248             // If there is neither an installed nor a candidate package, check
249             // any other instance.
250
251             if ( ! match                      &&
252                  ! selectable->candidateObj() &&
253                  ! selectable->installedObj()   )
254                 check( selectable, selectable->theObj(), regexp );
255
256
257             progress.setValue( count++ );
258
259             if ( timer.elapsed() > 300 ) // milisec
260             {
261                 // Process events only every 300 milliseconds - this is very
262                 // expensive since both the progress dialog and the package
263                 // list change all the time, thus display updates are necessary
264                 // each time.
265
266                 qApp->processEvents();
267                 timer.restart();
268             }
269         }
270
271         if ( _matchCount == 0 )
272             emit message( _( "No Results." ) );
273     }
274
275     emit filterFinished();
276 }
277
278
279 bool
280 YQPkgSearchFilterView::check( ZyppSel   selectable,
281                               ZyppObj   zyppObj )
282 {
283     QRegExp regexp( _searchText->currentText() );
284     regexp.setCaseSensitivity( _caseSensitive->isChecked() ? Qt::CaseSensitive : Qt::CaseInsensitive );
285     regexp.setPatternSyntax( (_searchMode->currentIndex() == UseWildcards) ? QRegExp::Wildcard : QRegExp::RegExp);
286     return check( selectable, zyppObj, regexp );
287 }
288
289
290 bool
291 YQPkgSearchFilterView::check( ZyppSel           selectable,
292                               ZyppObj           zyppObj,
293                               const QRegExp &   regexp )
294 {
295     if ( ! zyppObj )
296         return false;
297
298     bool match =
299         ( _searchInName->isChecked()        && check( zyppObj->name(),        regexp ) ) ||
300         ( _searchInSummary->isChecked()     && check( zyppObj->summary(),     regexp ) ) ||
301         ( _searchInDescription->isChecked() && check( zyppObj->description(), regexp ) ) ||
302         ( _searchInProvides->isChecked()    && check( zyppObj->dep( zypp::Dep::PROVIDES ), regexp ) ) ||
303         ( _searchInRequires->isChecked()    && check( zyppObj->dep( zypp::Dep::REQUIRES ), regexp ) );
304
305     if ( match )
306     {
307         ZyppPkg zyppPkg = tryCastToZyppPkg( zyppObj );
308
309         if ( zyppPkg )
310         {
311             _matchCount++;
312             emit filterMatch( selectable, zyppPkg );
313         }
314     }
315
316     return match;
317 }
318
319
320 bool
321 YQPkgSearchFilterView::check( const string &    attribute,
322                               const QRegExp &   regexp )
323 {
324     QString att         = fromUTF8( attribute );
325     QString searchText  = _searchText->currentText();
326     bool match          = false;
327
328     switch ( _searchMode->currentIndex() )
329     {
330         case Contains:
331             match = att.contains( searchText, _caseSensitive->isChecked() ? Qt::CaseSensitive : Qt::CaseInsensitive);
332             break;
333
334         case BeginsWith:
335             match = att.startsWith( searchText );       // only case sensitive
336             break;
337
338         case ExactMatch:
339             match = ( att == searchText );
340             break;
341
342         case UseWildcards:
343         case UseRegExp:
344             // Both cases differ in how the regexp is set up during initialization
345             match = att.contains( regexp );
346             break;
347
348             // Intentionally omitting "default" branch - let gcc watch for unhandled enums
349     }
350
351     return match;
352 }
353
354
355 bool
356 YQPkgSearchFilterView::check( const zypp::CapSet & capSet, const QRegExp & regexp )
357 {
358     for ( zypp::CapSet::const_iterator it = capSet.begin();
359           it != capSet.end();
360           ++it )
361     {
362         if ( check( ( *it).index(), regexp ) )
363         {
364             // y2debug( "Match for %s", (*it).asString().c_str() );
365             return true;
366         }
367     }
368
369     return false;
370 }
371
372
373
374 #include "YQPkgSearchFilterView.moc"