1 | |
2 | /* |
3 | * searchwidget.cpp - part of the KDE Help Center |
4 | * |
5 | * Copyright (C) 1999 Matthias Elter (me@kde.org) |
6 | * (C) 2000 Matthias Hoelzer-Kluepfel (hoelzer@kde.org) |
7 | * |
8 | * This program is free software; you can redistribute it and/or modify |
9 | * it under the terms of the GNU General Public License as published by |
10 | * the Free Software Foundation; either version 2 of the License, or |
11 | * (at your option) any later version. |
12 | * |
13 | * This program is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | * GNU General Public License for more details. |
17 | * |
18 | * You should have received a copy of the GNU General Public License |
19 | * along with this program; if not, write to the Free Software |
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
21 | */ |
22 | |
23 | #include "searchwidget.h" |
24 | |
25 | #include <stdlib.h> |
26 | #include <unistd.h> |
27 | #include <errno.h> |
28 | |
29 | #include <QLabel> |
30 | #include <QPushButton> |
31 | #include <QComboBox> |
32 | #include <QLayout> |
33 | #include <QVBoxLayout> |
34 | #include <QHBoxLayout> |
35 | #include <QBoxLayout> |
36 | #include <QtDBus/QDBusConnection> |
37 | |
38 | #include <KConfig> |
39 | #include <KApplication> |
40 | #include <KLocale> |
41 | #include <KDebug> |
42 | #include <KIconLoader> |
43 | |
44 | #include "scopeitem.h" |
45 | #include "docentrytraverser.h" |
46 | #include "kcmhelpcenter.h" |
47 | #include "prefs.h" |
48 | #include "searchengine.h" |
49 | |
50 | namespace KHC { |
51 | |
52 | SearchWidget::SearchWidget( SearchEngine *engine, QWidget *parent ) |
53 | : QWidget( parent ), mEngine( engine ), |
54 | mScopeCount( 0 ) |
55 | { |
56 | QDBusConnection::sessionBus().registerObject("/SearchWidget" , this, QDBusConnection::ExportScriptableSlots); |
57 | |
58 | QBoxLayout *topLayout = new QVBoxLayout( this ); |
59 | topLayout->setMargin( 2 ); |
60 | topLayout->setSpacing( 2 ); |
61 | |
62 | QBoxLayout *hLayout = new QHBoxLayout(); |
63 | topLayout->addLayout( hLayout ); |
64 | |
65 | mMethodCombo = new QComboBox( this ); |
66 | mMethodCombo->addItem( i18n("and" ) ); |
67 | mMethodCombo->addItem( i18n("or" ) ); |
68 | |
69 | QLabel *l = new QLabel( i18n("&Method:" ), this ); |
70 | l->setBuddy( mMethodCombo ); |
71 | |
72 | hLayout->addWidget( l ); |
73 | hLayout->addWidget( mMethodCombo ); |
74 | |
75 | hLayout = new QHBoxLayout(); |
76 | topLayout->addLayout( hLayout ); |
77 | |
78 | mPagesCombo = new QComboBox( this ); |
79 | mPagesCombo->addItem( QLatin1String("5" ) ); |
80 | mPagesCombo->addItem( QLatin1String("10" ) ); |
81 | mPagesCombo->addItem( QLatin1String("25" ) ); |
82 | mPagesCombo->addItem( QLatin1String("50" ) ); |
83 | mPagesCombo->addItem( QLatin1String("1000" ) ); |
84 | |
85 | l = new QLabel( i18n("Max. &results:" ), this ); |
86 | l->setBuddy( mPagesCombo ); |
87 | |
88 | hLayout->addWidget( l ); |
89 | hLayout->addWidget( mPagesCombo ); |
90 | |
91 | hLayout = new QHBoxLayout(); |
92 | topLayout->addLayout( hLayout ); |
93 | |
94 | mScopeCombo = new QComboBox( this ); |
95 | for (int i=0; i < ScopeNum; ++i ) { |
96 | mScopeCombo->addItem( scopeSelectionLabel( i ) ); |
97 | } |
98 | connect( mScopeCombo, SIGNAL( activated( int ) ), |
99 | SLOT( scopeSelectionChanged( int ) ) ); |
100 | |
101 | l = new QLabel( i18n("&Scope selection:" ), this ); |
102 | l->setBuddy( mScopeCombo ); |
103 | |
104 | hLayout->addWidget( l ); |
105 | hLayout->addWidget( mScopeCombo ); |
106 | |
107 | mScopeListView = new QTreeWidget( this ); |
108 | mScopeListView->setColumnCount( 1 ); |
109 | mScopeListView->setHeaderLabels( QStringList() << i18n("Scope" ) ); |
110 | topLayout->addWidget( mScopeListView, 1 ); |
111 | |
112 | QPushButton *indexButton = new QPushButton( i18n("Build Search &Index..." ), |
113 | this ); |
114 | connect( indexButton, SIGNAL( clicked() ), SIGNAL( showIndexDialog() ) ); |
115 | topLayout->addWidget( indexButton ); |
116 | |
117 | // FIXME: Use SearchHandler on double-clicked document |
118 | #if 0 |
119 | connect( mScopeListView, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int) ), |
120 | SLOT(scopeDoubleClicked(QTreeWidgetItem*)) ); |
121 | #endif |
122 | connect( mScopeListView, SIGNAL(itemClicked(QTreeWidgetItem*,int)), |
123 | SLOT(scopeClicked(QTreeWidgetItem*)) ); |
124 | } |
125 | |
126 | |
127 | SearchWidget::~SearchWidget() |
128 | { |
129 | writeConfig( KGlobal::config().data() ); |
130 | } |
131 | |
132 | |
133 | void SearchWidget::readConfig( KConfig *cfg ) |
134 | { |
135 | KConfigGroup searchGroup(cfg, "Search" ); |
136 | int scopeSelection = searchGroup.readEntry( "ScopeSelection" , (int)ScopeDefault ); |
137 | mScopeCombo->setCurrentIndex( scopeSelection ); |
138 | if ( scopeSelection != ScopeDefault ) scopeSelectionChanged( scopeSelection ); |
139 | |
140 | mMethodCombo->setCurrentIndex( Prefs::method() ); |
141 | mPagesCombo->setCurrentIndex( Prefs::maxCount() ); |
142 | |
143 | if ( scopeSelection == ScopeCustom ) { |
144 | KConfigGroup searchScopeGroup(cfg, "Custom Search Scope" ); |
145 | QTreeWidgetItemIterator it( mScopeListView ); |
146 | while( *it ) { |
147 | if ( (*it)->type() == ScopeItem::rttiId() ) { |
148 | ScopeItem *item = static_cast<ScopeItem *>(*it); |
149 | item->setOn( searchScopeGroup.readEntry( |
150 | item->entry()->identifier(), |
151 | item->isOn() ) ); |
152 | } |
153 | ++it; |
154 | } |
155 | } |
156 | |
157 | checkScope(); |
158 | } |
159 | |
160 | void SearchWidget::writeConfig( KConfig *cfg ) |
161 | { |
162 | KConfigGroup cg (cfg, "Search" ); |
163 | |
164 | cg.writeEntry( "ScopeSelection" , mScopeCombo->currentIndex() ); |
165 | Prefs::setMethod( mMethodCombo->currentIndex() ); |
166 | Prefs::setMaxCount( mPagesCombo->currentIndex() ); |
167 | |
168 | if ( mScopeCombo->currentIndex() == ScopeCustom ) { |
169 | KConfigGroup cg2 (cfg, "Custom Search Scope" ); |
170 | QTreeWidgetItemIterator it( mScopeListView ); |
171 | while( (*it) ) { |
172 | if ( (*it)->type() == ScopeItem::rttiId() ) { |
173 | ScopeItem *item = static_cast<ScopeItem *>( (*it) ); |
174 | cg2.writeEntry( item->entry()->identifier(), item->isOn() ); |
175 | } |
176 | ++it; |
177 | } |
178 | } |
179 | } |
180 | |
181 | void SearchWidget::slotSwitchBoxes() |
182 | { |
183 | QTreeWidgetItemIterator it( mScopeListView ); |
184 | while( (*it) ) { |
185 | if ( (*it)->type() == ScopeItem::rttiId() ) { |
186 | ScopeItem *item = static_cast<ScopeItem *>( (*it) ); |
187 | item->setOn( !item->isOn() ); |
188 | } |
189 | ++it; |
190 | } |
191 | |
192 | checkScope(); |
193 | } |
194 | |
195 | void SearchWidget::scopeSelectionChanged( int id ) |
196 | { |
197 | QTreeWidgetItemIterator it( mScopeListView ); |
198 | while( (*it) ) { |
199 | if ( (*it)->type() == ScopeItem::rttiId() ) { |
200 | ScopeItem *item = static_cast<ScopeItem *>( (*it) ); |
201 | bool state = item->isOn(); |
202 | switch( id ) { |
203 | case ScopeDefault: |
204 | state = item->entry()->searchEnabledDefault(); |
205 | break; |
206 | case ScopeAll: |
207 | state = true; |
208 | break; |
209 | case ScopeNone: |
210 | state = false; |
211 | break; |
212 | default: |
213 | break; |
214 | } |
215 | if ( state != item->isOn() ) { |
216 | item->setOn( state ); |
217 | } |
218 | } |
219 | ++it; |
220 | } |
221 | |
222 | checkScope(); |
223 | } |
224 | |
225 | QString SearchWidget::method() |
226 | { |
227 | QString m = "and" ; |
228 | if ( mMethodCombo->currentIndex() == 1) |
229 | m = "or" ; |
230 | |
231 | return m; |
232 | } |
233 | |
234 | int SearchWidget::pages() |
235 | { |
236 | int p = mPagesCombo->currentText().toInt(); |
237 | |
238 | return p; |
239 | } |
240 | |
241 | QString SearchWidget::scope() |
242 | { |
243 | QString scope; |
244 | |
245 | QTreeWidgetItemIterator it( mScopeListView ); |
246 | while( (*it) ) { |
247 | if ( (*it)->type() == ScopeItem::rttiId() ) { |
248 | ScopeItem *item = static_cast<ScopeItem *>( (*it) ); |
249 | if ( item->isOn() ) { |
250 | if ( !scope.isEmpty() ) scope += '&'; |
251 | scope += QLatin1String("scope=" ) + item->entry()->identifier(); |
252 | } |
253 | } |
254 | ++it; |
255 | } |
256 | |
257 | return scope; |
258 | } |
259 | |
260 | class ScopeTraverser : public DocEntryTraverser |
261 | { |
262 | public: |
263 | ScopeTraverser( SearchWidget *widget, int level ) : |
264 | mWidget( widget ), mLevel( level ), mParentItem( 0 ) {} |
265 | |
266 | ~ScopeTraverser() |
267 | { |
268 | if( mParentItem && !mParentItem->childCount() ) delete mParentItem; |
269 | } |
270 | |
271 | void process( DocEntry *entry ) |
272 | { |
273 | if ( mWidget->engine()->canSearch( entry ) && |
274 | ( !mWidget->engine()->needsIndex( entry ) || |
275 | entry->indexExists( Prefs::indexDirectory() ) ) ) { |
276 | ScopeItem *item = 0; |
277 | if ( mParentItem ) { |
278 | item = new ScopeItem( mParentItem, entry ); |
279 | } else { |
280 | item = new ScopeItem( mWidget->listView(), entry ); |
281 | } |
282 | item->setOn( entry->searchEnabled() ); |
283 | } |
284 | } |
285 | |
286 | DocEntryTraverser *createChild( DocEntry *entry ) |
287 | { |
288 | if ( mLevel >= mNestingLevel ) { |
289 | ++mLevel; |
290 | return this; |
291 | } else { |
292 | ScopeTraverser *t = new ScopeTraverser( mWidget, mLevel + 1 ); |
293 | QTreeWidgetItem *item = 0; |
294 | if ( mParentItem ) { |
295 | item = new QTreeWidgetItem( mParentItem, QStringList() << entry->name() ); |
296 | } else { |
297 | item = new QTreeWidgetItem( mWidget->listView(), QStringList() << entry->name() ); |
298 | } |
299 | item->setExpanded( true ); |
300 | t->mParentItem = item; |
301 | return t; |
302 | } |
303 | } |
304 | |
305 | DocEntryTraverser *parentTraverser() |
306 | { |
307 | if ( mLevel > mNestingLevel ) return this; |
308 | else return mParent; |
309 | } |
310 | |
311 | void deleteTraverser() |
312 | { |
313 | if ( mLevel > mNestingLevel ) --mLevel; |
314 | else delete this; |
315 | } |
316 | |
317 | private: |
318 | SearchWidget *mWidget; |
319 | int mLevel; |
320 | QTreeWidgetItem *mParentItem; |
321 | |
322 | static int mNestingLevel; |
323 | }; |
324 | |
325 | int ScopeTraverser::mNestingLevel = 2; |
326 | |
327 | void SearchWidget::searchIndexUpdated() |
328 | { |
329 | KGlobal::config()->reparseConfiguration(); |
330 | updateScopeList(); |
331 | update(); |
332 | } |
333 | |
334 | void SearchWidget::updateScopeList() |
335 | { |
336 | mScopeListView->clear(); |
337 | |
338 | ScopeTraverser t( this, 0 ); |
339 | DocMetaInfo::self()->traverseEntries( &t ); |
340 | |
341 | checkScope(); |
342 | } |
343 | |
344 | void SearchWidget::scopeDoubleClicked( QTreeWidgetItem* item ) |
345 | { |
346 | if ( !item || item->type() != ScopeItem::rttiId() ) return; |
347 | ScopeItem *scopeItem = static_cast<ScopeItem *>( item ); |
348 | |
349 | QString searchUrl = scopeItem->entry()->search(); |
350 | |
351 | kDebug() << "DoubleClick: " << searchUrl; |
352 | |
353 | emit searchResult( searchUrl ); |
354 | } |
355 | |
356 | void SearchWidget::scopeClicked( QTreeWidgetItem* ) |
357 | { |
358 | checkScope(); |
359 | |
360 | mScopeCombo->setCurrentIndex( ScopeCustom ); |
361 | } |
362 | |
363 | QString SearchWidget::scopeSelectionLabel( int id ) const |
364 | { |
365 | switch( id ) { |
366 | case ScopeCustom: |
367 | return i18nc("Label for searching documentation using custom (user defined) scope" , "Custom" ); |
368 | case ScopeDefault: |
369 | return i18nc("Label for searching documentation using default search scope" , "Default" ); |
370 | case ScopeAll: |
371 | return i18nc("Label for searching documentation in all subsections" , "All" ); |
372 | case ScopeNone: |
373 | return i18nc("Label for scope that deselects all search subsections" , "None" ); |
374 | default: |
375 | return i18nc("Label for Unknown search scope, that should never appear" , "unknown" ); |
376 | } |
377 | } |
378 | |
379 | void SearchWidget::checkScope() |
380 | { |
381 | mScopeCount = 0; |
382 | |
383 | QTreeWidgetItemIterator it( mScopeListView ); |
384 | while( (*it) ) { |
385 | if ( (*it)->type() == ScopeItem::rttiId() ) { |
386 | ScopeItem *item = static_cast<ScopeItem *>( (*it) ); |
387 | if ( item->isOn() ) { |
388 | ++mScopeCount; |
389 | } |
390 | item->entry()->enableSearch( item->isOn() ); |
391 | } |
392 | ++it; |
393 | } |
394 | |
395 | emit scopeCountChanged( mScopeCount ); |
396 | } |
397 | |
398 | int SearchWidget::scopeCount() const |
399 | { |
400 | return mScopeCount; |
401 | } |
402 | |
403 | } |
404 | |
405 | #include "searchwidget.moc" |
406 | |
407 | // vim:ts=2:sw=2:et |
408 | |