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
50namespace KHC {
51
52SearchWidget::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
127SearchWidget::~SearchWidget()
128{
129 writeConfig( KGlobal::config().data() );
130}
131
132
133void 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
160void 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
181void 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
195void 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
225QString SearchWidget::method()
226{
227 QString m = "and";
228 if ( mMethodCombo->currentIndex() == 1)
229 m = "or";
230
231 return m;
232}
233
234int SearchWidget::pages()
235{
236 int p = mPagesCombo->currentText().toInt();
237
238 return p;
239}
240
241QString 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
260class 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
325int ScopeTraverser::mNestingLevel = 2;
326
327void SearchWidget::searchIndexUpdated()
328{
329 KGlobal::config()->reparseConfiguration();
330 updateScopeList();
331 update();
332}
333
334void SearchWidget::updateScopeList()
335{
336 mScopeListView->clear();
337
338 ScopeTraverser t( this, 0 );
339 DocMetaInfo::self()->traverseEntries( &t );
340
341 checkScope();
342}
343
344void 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
356void SearchWidget::scopeClicked( QTreeWidgetItem* )
357{
358 checkScope();
359
360 mScopeCombo->setCurrentIndex( ScopeCustom );
361}
362
363QString 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
379void 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
398int SearchWidget::scopeCount() const
399{
400 return mScopeCount;
401}
402
403}
404
405#include "searchwidget.moc"
406
407// vim:ts=2:sw=2:et
408