Make a branch to make krunner Good Enough For Aaron™.
[kdebase/uwolfer.git] / runtime / khelpcenter / kcmhelpcenter.cpp
blob2281a3ca1c085dbef33976d33674e868e98b2f97
1 /*
2 This file is part of KHelpcenter.
4 Copyright (C) 2002 Cornelius Schumacher <schumacher@kde.org>
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; see the file COPYING. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, USA.
22 #include "kcmhelpcenter.h"
24 #include "htmlsearchconfig.h"
25 #include "docmetainfo.h"
26 #include "prefs.h"
27 #include "searchhandler.h"
28 #include "searchengine.h"
31 #include "kcmhelpcenteradaptor.h"
33 #include <kconfig.h>
34 #include <kdebug.h>
35 #include <klocale.h>
36 #include <kglobal.h>
37 #include <kaboutdata.h>
38 #include <kdialog.h>
39 #include <kstandarddirs.h>
40 #include <kprocess.h>
41 #include <kapplication.h>
42 #include <ktemporaryfile.h>
43 #include <kurlrequester.h>
44 #include <kmessagebox.h>
45 #include <k3listview.h>
46 #include <klineedit.h>
48 #include <QtDBus/QtDBus>
49 #include <QLabel>
50 #include <QLayout>
51 #include <QProgressBar>
52 #include <QTextEdit>
54 #include <unistd.h>
55 #include <sys/types.h>
57 using namespace KHC;
59 IndexDirDialog::IndexDirDialog( QWidget *parent )
60 : KDialog( parent )
62 setModal( true );
63 setCaption( i18n("Change Index Folder") );
64 setButtons( Ok | Cancel );
66 QFrame *topFrame = new QFrame( this );
67 setMainWidget( topFrame );
69 QBoxLayout *urlLayout = new QHBoxLayout( topFrame );
71 QLabel *label = new QLabel( i18n("Index folder:"), topFrame );
72 urlLayout->addWidget( label );
74 mIndexUrlRequester = new KUrlRequester( topFrame );
75 mIndexUrlRequester->setMode( KFile::Directory | KFile::ExistingOnly |
76 KFile::LocalOnly );
77 urlLayout->addWidget( mIndexUrlRequester );
79 mIndexUrlRequester->setUrl( Prefs::indexDirectory() );
80 connect(mIndexUrlRequester->lineEdit(),SIGNAL(textChanged ( const QString & )), this, SLOT(slotUrlChanged( const QString &)));
81 slotUrlChanged( mIndexUrlRequester->lineEdit()->text() );
83 connect( this, SIGNAL( okClicked() ), SLOT( slotOk() ) );
86 void IndexDirDialog::slotUrlChanged( const QString &_url )
88 enableButtonOk( !_url.isEmpty() );
92 void IndexDirDialog::slotOk()
94 Prefs::setIndexDirectory( mIndexUrlRequester->url().url() );
95 accept();
99 IndexProgressDialog::IndexProgressDialog( QWidget *parent )
100 : KDialog( parent ),
101 mFinished( true )
103 setCaption( i18n("Build Search Indices") );
105 QVBoxLayout *topLayout = new QVBoxLayout( mainWidget() );
106 topLayout->setMargin( marginHint() );
107 topLayout->setSpacing( spacingHint() );
109 mLabel = new QLabel( mainWidget() );
110 mLabel->setAlignment( Qt::AlignHCenter );
111 topLayout->addWidget( mLabel );
113 mProgressBar = new QProgressBar( mainWidget() );
114 topLayout->addWidget( mProgressBar );
116 mLogLabel = new QLabel( i18n("Index creation log:"), mainWidget() );
117 topLayout->addWidget( mLogLabel );
119 mLogView = new QTextEdit( mainWidget() );
120 mLogView->setReadOnly( true );
121 mLogView->setTextFormat( Qt::LogText );
122 mLogView->setMinimumHeight( 200 );
123 topLayout->addWidget( mLogView );
125 setButtons( User1 | Close );
126 connect( this, SIGNAL( closeClicked() ), SLOT( slotEnd() ) );
127 connect( this, SIGNAL( user1Clicked() ), SLOT( toggleDetails() ) );
129 hideDetails();
131 setFinished( false );
134 IndexProgressDialog::~IndexProgressDialog()
136 if ( !mLogView->isHidden() ) {
137 KConfigGroup cfg(KGlobal::config(), "indexprogressdialog");
138 cfg.writeEntry( "size", size() );
142 void IndexProgressDialog::setTotalSteps( int steps )
144 mProgressBar->setRange( 0, steps );
145 mProgressBar->setValue( 0 );
146 setFinished( false );
147 mLogView->clear();
150 void IndexProgressDialog::advanceProgress()
152 mProgressBar->setValue( mProgressBar->value() + 1 );
155 void IndexProgressDialog::setLabelText( const QString &text )
157 mLabel->setText( text );
160 void IndexProgressDialog::setMinimumLabelWidth( int width )
162 mLabel->setMinimumWidth( width );
165 void IndexProgressDialog::setFinished( bool finished )
167 if ( finished == mFinished ) return;
169 mFinished = finished;
171 if ( mFinished ) {
172 setButtonText( Close, i18nc("Label for button to close search index progress dialog after successful completion", "Close") );
173 mLabel->setText( i18n("Index creation finished.") );
174 mProgressBar->setValue( mProgressBar->maximum() );
175 } else {
176 setButtonText( Close, i18nc("Label for stopping search index generation before completion", "Stop") );
180 void IndexProgressDialog::appendLog( const QString &text )
182 mLogView->append( text );
185 void IndexProgressDialog::slotEnd()
187 if ( mFinished ) {
188 emit closed();
189 accept();
190 } else {
191 emit cancelled();
192 reject();
196 void IndexProgressDialog::toggleDetails()
198 KConfigGroup cfg(KGlobal::config(), "indexprogressdialog");
199 if ( mLogView->isHidden() ) {
200 mLogLabel->show();
201 mLogView->show();
202 setButtonText( User1, i18n("Details &lt;&lt;") );
203 QSize size = cfg.readEntry( "size", QSize() );
204 if ( !size.isEmpty() ) resize( size );
205 } else {
206 cfg.writeEntry( "size", size() );
207 hideDetails();
211 void IndexProgressDialog::hideDetails()
213 mLogLabel->hide();
214 mLogView->hide();
215 setButtonText( User1, i18n("Details &gt;&gt;") );
216 layout()->activate();
217 adjustSize();
221 KCMHelpCenter::KCMHelpCenter( KHC::SearchEngine *engine, QWidget *parent,
222 const char *name)
223 : KDialog( parent ),
224 mEngine( engine ), mProgressDialog( 0 ), mCmdFile( 0 ),
225 mProcess( 0 ), mIsClosing( false ), mRunAsRoot( false )
227 new KcmhelpcenterAdaptor(this);
228 QDBusConnection::sessionBus().registerObject(QLatin1String("/kcmhelpcenter"), this);
229 setObjectName( name );
230 setCaption( i18n("Build Search Index") );
231 setButtons( Ok | Cancel );
232 showButtonSeparator( true );
234 QWidget *widget = new QWidget( this );
235 setMainWidget( widget );
237 setupMainWidget( widget );
239 setButtonGuiItem( KDialog::Ok, KGuiItem(i18n("Build Index")) );
241 mConfig = KGlobal::config();
243 DocMetaInfo::self()->scanMetaInfo();
245 load();
246 const QString dbusInterface = "org.kde.khelpcenter.kcmhelpcenter";
247 QDBusConnection dbus = QDBusConnection::sessionBus();
248 bool success = dbus.connect(QString(), "/kcmhelpcenter", dbusInterface, "buildIndexProgress", this, SLOT(slotIndexProgress()));
249 if ( !success )
250 kError() << "connect D-Bus signal failed" << endl;
251 success = dbus.connect(QString(), "/kcmhelpcenter", dbusInterface, "buildIndexError", this, SLOT(slotIndexError(const QString&)));
252 if ( !success )
253 kError() << "connect D-Bus signal failed" << endl;
254 KConfigGroup id( mConfig, "IndexDialog" );
255 restoreDialogSize( id );
258 KCMHelpCenter::~KCMHelpCenter()
260 KConfigGroup cg( KGlobal::config(), "IndexDialog" );
261 KDialog::saveDialogSize( cg );
264 void KCMHelpCenter::setupMainWidget( QWidget *parent )
266 QVBoxLayout *topLayout = new QVBoxLayout( parent );
267 topLayout->setSpacing( KDialog::spacingHint() );
269 QString helpText =
270 i18n("To be able to search a document, there needs to exist a search\n"
271 "index. The status column of the list below shows, if an index\n"
272 "for a document exists.\n") +
273 i18n("To create an index check the box in the list and press the\n"
274 "\"Build Index\" button.\n");
276 QLabel *label = new QLabel( helpText, parent );
277 topLayout->addWidget( label );
279 mListView = new K3ListView( parent );
280 mListView->setFullWidth( true );
281 mListView->addColumn( i18n("Search Scope") );
282 mListView->addColumn( i18n("Status") );
283 mListView->setColumnAlignment( 1, Qt::AlignCenter );
284 topLayout->addWidget( mListView );
285 connect( mListView, SIGNAL( clicked( Q3ListViewItem * ) ),
286 SLOT( checkSelection() ) );
288 QBoxLayout *urlLayout = new QHBoxLayout();
289 topLayout->addLayout( urlLayout );
291 QLabel *urlLabel = new QLabel( i18n("Index folder:"), parent );
292 urlLayout->addWidget( urlLabel );
294 mIndexDirLabel = new QLabel( parent );
295 urlLayout->addWidget( mIndexDirLabel, 1 );
297 QPushButton *button = new QPushButton( i18n("Change..."), parent );
298 connect( button, SIGNAL( clicked() ), SLOT( showIndexDirDialog() ) );
299 urlLayout->addWidget( button );
301 QBoxLayout *buttonLayout = new QHBoxLayout();
302 topLayout->addLayout( buttonLayout );
304 buttonLayout->addStretch( 1 );
306 connect( this, SIGNAL( okClicked() ), SLOT( slotOk() ) );
309 void KCMHelpCenter::defaults()
313 bool KCMHelpCenter::save()
315 kDebug(1401) << "KCMHelpCenter::save()";
317 if ( !QFile::exists( Prefs::indexDirectory() ) ) {
318 KMessageBox::sorry( this,
319 i18n("<qt>The folder <b>%1</b> does not exist; unable to create index.</qt>",
320 Prefs::indexDirectory() ) );
321 return false;
322 } else {
323 return buildIndex();
326 return true;
329 void KCMHelpCenter::load()
331 mIndexDirLabel->setText( Prefs::indexDirectory() );
333 mListView->clear();
335 const DocEntry::List &entries = DocMetaInfo::self()->docEntries();
336 DocEntry::List::ConstIterator it;
337 for( it = entries.begin(); it != entries.end(); ++it ) {
338 // kDebug(1401) << "Entry: " << (*it)->name() << " Indexer: '"
339 // << (*it)->indexer() << "'" << endl;
340 if ( mEngine->needsIndex( *it ) ) {
341 ScopeItem *item = new ScopeItem( mListView, *it );
342 item->setOn( (*it)->searchEnabled() );
346 updateStatus();
349 void KCMHelpCenter::updateStatus()
351 Q3ListViewItemIterator it( mListView );
352 while ( it.current() != 0 ) {
353 ScopeItem *item = static_cast<ScopeItem *>( it.current() );
354 QString status;
355 if ( item->entry()->indexExists( Prefs::indexDirectory() ) ) {
356 status = i18nc("Describes the status of a documentation index that is present", "OK");
357 item->setOn( false );
358 } else {
359 status = i18nc("Describes the status of a documentation index that is missing", "Missing");
361 item->setText( 1, status );
363 ++it;
366 checkSelection();
369 bool KCMHelpCenter::buildIndex()
371 kDebug(1401) << "Build Index";
373 kDebug() << "IndexPath: '" << Prefs::indexDirectory() << "'";
375 if ( mProcess ) {
376 kError() << "Error: Index Process still running." << endl;
377 return false;
380 mIndexQueue.clear();
382 QFontMetrics fm( font() );
383 int maxWidth = 0;
385 mCmdFile = new KTemporaryFile;
386 if ( !mCmdFile->open() ) {
387 kError() << "Error opening command file." << endl;
388 deleteCmdFile();
389 return false;
392 QTextStream ts ( mCmdFile );
393 kDebug() << "Writing to file '" << mCmdFile->fileName() << "'";
395 bool hasError = false;
397 Q3ListViewItemIterator it( mListView );
398 while ( it.current() != 0 ) {
399 ScopeItem *item = static_cast<ScopeItem *>( it.current() );
400 if ( item->isOn() ) {
401 DocEntry *entry = item->entry();
403 QString docText = i18nc(" Generic prefix label for error messages when creating documentation index, first arg is the document's identifier, second is the document's name", "Document '%1' (%2):\n",
404 entry->identifier() ,
405 entry->name() );
406 if ( entry->documentType().isEmpty() ) {
407 KMessageBox::sorry( this, docText +
408 i18n("No document type.") );
409 hasError = true;
410 } else {
411 SearchHandler *handler = mEngine->handler( entry->documentType() );
412 if ( !handler ) {
413 KMessageBox::sorry( this, docText +
414 i18n("No search handler available for document type '%1'.",
415 entry->documentType() ) );
416 hasError = true;
417 } else {
418 QString indexer = handler->indexCommand( entry->identifier() );
419 if ( indexer.isEmpty() ) {
420 KMessageBox::sorry( this, docText +
421 i18n("No indexing command specified for document type '%1'.",
422 entry->documentType() ) );
423 hasError = true;
424 } else {
425 indexer.replace( QLatin1String("%i" ), entry->identifier() );
426 indexer.replace( QLatin1String( "%d" ), Prefs::indexDirectory() );
427 indexer.replace( QLatin1String( "%p" ), entry->url() );
428 kDebug() << "INDEXER: " << indexer;
429 ts << indexer << endl;
431 int width = fm.width( entry->name() );
432 if ( width > maxWidth ) maxWidth = width;
434 mIndexQueue.append( entry );
439 ++it;
442 ts.flush();
444 if ( mIndexQueue.isEmpty() ) {
445 deleteCmdFile();
446 return !hasError;
449 mCurrentEntry = mIndexQueue.begin();
450 QString name = (*mCurrentEntry)->name();
452 if ( !mProgressDialog ) {
453 mProgressDialog = new IndexProgressDialog( this );
454 connect( mProgressDialog, SIGNAL( cancelled() ),
455 SLOT( cancelBuildIndex() ) );
456 connect( mProgressDialog, SIGNAL( closed() ),
457 SLOT( slotProgressClosed() ) );
459 mProgressDialog->setLabelText( name );
460 mProgressDialog->setTotalSteps( mIndexQueue.count() );
461 mProgressDialog->setMinimumLabelWidth( maxWidth );
462 mProgressDialog->show();
464 startIndexProcess();
466 return true;
469 void KCMHelpCenter::startIndexProcess()
471 kDebug() << "KCMHelpCenter::startIndexProcess()";
473 mProcess = new KProcess;
474 if ( mRunAsRoot ) {
475 *mProcess << KStandardDirs::findExe("kdesu");
476 kDebug() << "Run as root";
479 *mProcess << KStandardDirs::findExe("khc_indexbuilder");
480 *mProcess << mCmdFile->fileName();
481 *mProcess << Prefs::indexDirectory();
483 mProcess->setOutputChannelMode(KProcess::SeparateChannels);
484 connect( mProcess, SIGNAL( readyReadStandardError() ),
485 SLOT( slotReceivedStdout() ) );
486 connect( mProcess, SIGNAL( readyReadStandardOutput() ),
487 SLOT( slotReceivedStderr() ) );
488 connect( mProcess, SIGNAL( finished(int, QProcess::ExitStatus) ),
489 SLOT( slotIndexFinished(int, QProcess::ExitStatus) ) );
491 mProcess->start();
492 if (!mProcess->waitForStarted()) {
493 kError() << "KCMHelpcenter::startIndexProcess(): Failed to start process."
494 << endl;
495 deleteProcess();
496 deleteCmdFile();
500 void KCMHelpCenter::cancelBuildIndex()
502 kDebug() << "cancelBuildIndex()";
504 deleteProcess();
505 deleteCmdFile();
506 mIndexQueue.clear();
508 if ( mIsClosing ) {
509 mIsClosing = false;
513 void KCMHelpCenter::slotIndexFinished(int exitCode, QProcess::ExitStatus exitStatus)
515 kDebug() << "KCMHelpCenter::slotIndexFinished()";
517 if ( exitStatus == QProcess::NormalExit && exitCode == 2 ) {
518 if ( mRunAsRoot ) {
519 kError() << "Insufficient permissions." << endl;
520 } else {
521 kDebug() << "Insufficient permissions. Trying again as root.";
522 mRunAsRoot = true;
523 deleteProcess();
524 startIndexProcess();
525 return;
527 } else if ( exitStatus != QProcess::NormalExit || exitCode != 0 ) {
528 kDebug() << "KProcess reported an error.";
529 KMessageBox::error( this, i18n("Failed to build index.") );
530 } else {
531 mConfig->group( "Search" ).writeEntry( "IndexExists", true );
532 emit searchIndexUpdated();
535 deleteProcess();
536 deleteCmdFile();
538 if ( mProgressDialog ) {
539 mProgressDialog->setFinished( true );
542 mStdOut.clear();
543 mStdErr.clear();
545 if ( mIsClosing ) {
546 if ( !mProgressDialog || !mProgressDialog->isVisible() ) {
547 mIsClosing = false;
548 accept();
553 void KCMHelpCenter::deleteProcess()
555 delete mProcess;
556 mProcess = 0;
559 void KCMHelpCenter::deleteCmdFile()
561 delete mCmdFile;
562 mCmdFile = 0;
565 void KCMHelpCenter::slotIndexProgress()
567 if( !mProcess )
568 return;
570 kDebug() << "KCMHelpCenter::slotIndexProgress()";
572 updateStatus();
574 advanceProgress();
577 void KCMHelpCenter::slotIndexError( const QString &str )
579 if( !mProcess )
580 return;
582 kDebug() << "KCMHelpCenter::slotIndexError()";
584 KMessageBox::sorry( this, i18n("Error executing indexing build command:\n%1",
585 str ) );
587 if ( mProgressDialog ) {
588 mProgressDialog->appendLog( "<i>" + str + "</i>" );
591 advanceProgress();
594 void KCMHelpCenter::advanceProgress()
596 if ( mProgressDialog && mProgressDialog->isVisible() ) {
597 mProgressDialog->advanceProgress();
598 mCurrentEntry++;
599 if ( mCurrentEntry != mIndexQueue.end() ) {
600 QString name = (*mCurrentEntry)->name();
601 mProgressDialog->setLabelText( name );
606 void KCMHelpCenter::slotReceivedStdout()
608 QByteArray text= mProcess->readAllStandardOutput();
609 int pos = text.lastIndexOf( '\n' );
610 if ( pos < 0 ) {
611 mStdOut.append( text );
612 } else {
613 if ( mProgressDialog ) {
614 mProgressDialog->appendLog( mStdOut + text.left( pos ) );
615 mStdOut = text.mid( pos + 1 );
620 void KCMHelpCenter::slotReceivedStderr( )
622 QByteArray text = mProcess->readAllStandardError();
623 int pos = text.lastIndexOf( '\n' );
624 if ( pos < 0 ) {
625 mStdErr.append( text );
626 } else {
627 if ( mProgressDialog ) {
628 mProgressDialog->appendLog( QLatin1String("<i>") + mStdErr + text.left( pos ) +
629 QLatin1String("</i>"));
630 mStdErr = text.mid( pos + 1 );
635 void KCMHelpCenter::slotOk()
637 if ( buildIndex() ) {
638 if ( !mProcess ) accept();
639 else mIsClosing = true;
643 void KCMHelpCenter::slotProgressClosed()
645 kDebug() << "KCMHelpCenter::slotProgressClosed()";
647 if ( mIsClosing ) accept();
650 void KCMHelpCenter::showIndexDirDialog()
652 IndexDirDialog dlg( this );
653 if ( dlg.exec() == QDialog::Accepted ) {
654 load();
658 void KCMHelpCenter::checkSelection()
660 int count = 0;
662 Q3ListViewItemIterator it( mListView );
663 while ( it.current() != 0 ) {
664 ScopeItem *item = static_cast<ScopeItem *>( it.current() );
665 if ( item->isOn() ) {
666 ++count;
668 ++it;
671 enableButtonOk( count != 0 );
674 #include "kcmhelpcenter.moc"
676 // vim:ts=2:sw=2:et