]> icculus.org git repositories - duncan/yast2-qt4.git/blob - src/pkg/YQPkgSearchFilterView.cc
don't create QObjects in ycp thread - make timeouts work
[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( this );
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     QVBoxLayout *vlayout = new QVBoxLayout;
98     gbox->setLayout(vlayout);
99
100     _searchInName        = new QCheckBox( _( "&Name"            ), gbox ); Q_CHECK_PTR( _searchInName        );
101     vlayout->addWidget(_searchInName);
102     _searchInSummary     = new QCheckBox( _( "Su&mmary"         ), gbox ); Q_CHECK_PTR( _searchInSummary     );
103     vlayout->addWidget(_searchInSummary);
104     _searchInDescription = new QCheckBox( _( "Descr&iption"     ), gbox ); Q_CHECK_PTR( _searchInDescription );
105     vlayout->addWidget(_searchInDescription);
106
107     vlayout->addStretch();
108
109     // Intentionally NOT marking RPM tags for translation
110     _searchInProvides    = new QCheckBox(  "RPM \"&Provides\""   , gbox ); Q_CHECK_PTR( _searchInProvides    );
111     vlayout->addWidget(_searchInProvides);
112     _searchInRequires    = new QCheckBox(  "RPM \"Re&quires\""   , gbox ); Q_CHECK_PTR( _searchInRequires    );
113     vlayout->addWidget(_searchInRequires);
114
115     _searchInName->setChecked( true );
116     _searchInSummary->setChecked( true );
117
118     layout->addStretch();
119
120
121     //
122     // Search mode
123     //
124
125     label = new QLabel( _( "Search &Mode:" ), this );
126     Q_CHECK_PTR( label );
127     layout->addWidget(label);
128
129     _searchMode = new QComboBox( this );
130     Q_CHECK_PTR( _searchMode );
131     layout->addWidget(_searchMode);
132
133     _searchMode->setEditable( false );
134
135     label->setBuddy( _searchMode );
136
137     // Caution: combo box items must be inserted in the same order as enum SearchMode!
138     _searchMode->addItem( _( "Contains"          ) );
139     _searchMode->addItem( _( "Begins with"               ) );
140     _searchMode->addItem( _( "Exact Match"               ) );
141     _searchMode->addItem( _( "Use Wild Cards"    ) );
142     _searchMode->addItem( _( "Use Regular Expression" ) );
143
144     _searchMode->setCurrentIndex( Contains );
145
146
147     layout->addStretch();
148
149     _caseSensitive = new QCheckBox( _( "Case Sensiti&ve" ), this );
150     Q_CHECK_PTR( _caseSensitive );
151     layout->addWidget(_caseSensitive);
152
153     for ( int i=0; i < 6; i++ )
154       layout->addStretch();
155 }
156
157
158 YQPkgSearchFilterView::~YQPkgSearchFilterView()
159 {
160     // NOP
161 }
162
163
164 void
165 YQPkgSearchFilterView::keyPressEvent( QKeyEvent * event )
166 {
167     if ( event )
168     {
169         if ( event->modifiers() == Qt::NoModifier )     // No Ctrl / Alt / Shift etc. pressed
170         {
171             if ( event->key() == Qt::Key_Return ||
172                  event->key() == Qt::Key_Enter    )
173             {
174                 _searchButton->animateClick();
175                 return;
176             }
177         }
178
179     }
180
181     QWidget::keyPressEvent( event );
182 }
183
184
185 void
186 YQPkgSearchFilterView::setFocus()
187 {
188     _searchText->setFocus();
189 }
190
191
192 QSize
193 YQPkgSearchFilterView::minimumSizeHint() const
194 {
195     return QSize( 0, 0 );
196 }
197
198
199 void
200 YQPkgSearchFilterView::filterIfVisible()
201 {
202     if ( isVisible() )
203         filter();
204 }
205
206
207 void
208 YQPkgSearchFilterView::filter()
209 {
210     emit filterStart();
211     _matchCount = 0;
212
213     if ( ! _searchText->currentText().isEmpty() )
214     {
215         // Create a progress dialog that is only displayed if the search takes
216         // longer than a couple of seconds ( default: 4 ).
217
218         QProgressDialog progress( _( "Searching..." ),                  // text
219                                   _( "&Cancel" ),                       // cancelButtonLabel
220           0,
221                                   1000,
222                                   this                  // parent
223                                   );
224         progress.setWindowTitle( "" );
225         progress.setMinimumDuration( 2000 ); // millisec
226         QTime timer;
227
228         QRegExp regexp( _searchText->currentText() );
229         regexp.setCaseSensitivity( _caseSensitive->isChecked() ? Qt::CaseSensitive : Qt::CaseInsensitive );
230   regexp.setPatternSyntax( (_searchMode->currentIndex() == UseWildcards) ? QRegExp::Wildcard : QRegExp::RegExp);
231
232         timer.start();
233
234
235         int count = 0;
236
237         for ( ZyppPoolIterator it = zyppPkgBegin();
238               it != zyppPkgEnd() && ! progress.wasCanceled();
239               ++it )
240         {
241             ZyppSel selectable = *it;
242
243             bool match =
244                 check( selectable, selectable->candidateObj(), regexp ) ||
245                 check( selectable, selectable->installedObj(), regexp );
246
247             // If there is neither an installed nor a candidate package, check
248             // any other instance.
249
250             if ( ! match                      &&
251                  ! selectable->candidateObj() &&
252                  ! selectable->installedObj()   )
253                 check( selectable, selectable->theObj(), regexp );
254
255
256             progress.setValue( count++ );
257
258             if ( timer.elapsed() > 300 ) // milisec
259             {
260                 // Process events only every 300 milliseconds - this is very
261                 // expensive since both the progress dialog and the package
262                 // list change all the time, thus display updates are necessary
263                 // each time.
264
265                 qApp->processEvents();
266                 timer.restart();
267             }
268         }
269
270         if ( _matchCount == 0 )
271             emit message( _( "No Results." ) );
272     }
273
274     emit filterFinished();
275 }
276
277
278 bool
279 YQPkgSearchFilterView::check( ZyppSel   selectable,
280                               ZyppObj   zyppObj )
281 {
282     QRegExp regexp( _searchText->currentText() );
283     regexp.setCaseSensitivity( _caseSensitive->isChecked() ? Qt::CaseSensitive : Qt::CaseInsensitive );
284     regexp.setPatternSyntax( (_searchMode->currentIndex() == UseWildcards) ? QRegExp::Wildcard : QRegExp::RegExp);
285     return check( selectable, zyppObj, regexp );
286 }
287
288
289 bool
290 YQPkgSearchFilterView::check( ZyppSel           selectable,
291                               ZyppObj           zyppObj,
292                               const QRegExp &   regexp )
293 {
294     if ( ! zyppObj )
295         return false;
296
297     bool match =
298         ( _searchInName->isChecked()        && check( zyppObj->name(),        regexp ) ) ||
299         ( _searchInSummary->isChecked()     && check( zyppObj->summary(),     regexp ) ) ||
300         ( _searchInDescription->isChecked() && check( zyppObj->description(), regexp ) ) ||
301         ( _searchInProvides->isChecked()    && check( zyppObj->dep( zypp::Dep::PROVIDES ), regexp ) ) ||
302         ( _searchInRequires->isChecked()    && check( zyppObj->dep( zypp::Dep::REQUIRES ), regexp ) );
303
304     if ( match )
305     {
306         ZyppPkg zyppPkg = tryCastToZyppPkg( zyppObj );
307
308         if ( zyppPkg )
309         {
310             _matchCount++;
311             emit filterMatch( selectable, zyppPkg );
312         }
313     }
314
315     return match;
316 }
317
318
319 bool
320 YQPkgSearchFilterView::check( const string &    attribute,
321                               const QRegExp &   regexp )
322 {
323     QString att         = fromUTF8( attribute );
324     QString searchText  = _searchText->currentText();
325     bool match          = false;
326
327     switch ( _searchMode->currentIndex() )
328     {
329         case Contains:
330             match = att.contains( searchText, _caseSensitive->isChecked() ? Qt::CaseSensitive : Qt::CaseInsensitive);
331             break;
332
333         case BeginsWith:
334             match = att.startsWith( searchText );       // only case sensitive
335             break;
336
337         case ExactMatch:
338             match = ( att == searchText );
339             break;
340
341         case UseWildcards:
342         case UseRegExp:
343             // Both cases differ in how the regexp is set up during initialization
344             match = att.contains( regexp );
345             break;
346
347             // Intentionally omitting "default" branch - let gcc watch for unhandled enums
348     }
349
350     return match;
351 }
352
353
354 bool
355 YQPkgSearchFilterView::check( const zypp::CapSet & capSet, const QRegExp & regexp )
356 {
357     for ( zypp::CapSet::const_iterator it = capSet.begin();
358           it != capSet.end();
359           ++it )
360     {
361         if ( check( ( *it).index(), regexp ) )
362         {
363             // y2debug( "Match for %s", (*it).asString().c_str() );
364             return true;
365         }
366     }
367
368     return false;
369 }
370
371
372
373 #include "YQPkgSearchFilterView.moc"