delay a few things on startup, such as setting the visibility mode, which ensures...
[personal-kdebase.git] / runtime / khelpcenter / kcmhelpcenter.cpp
blob705984d684ff736b5e14ee2b3b2d04c36642226b
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;") );
217 // causes bug 166343
218 //layout()->activate();
219 adjustSize();
223 KCMHelpCenter::KCMHelpCenter( KHC::SearchEngine *engine, QWidget *parent,
224 const char *name)
225 : KDialog( parent ),
226 mEngine( engine ), mProgressDialog( 0 ), mCmdFile( 0 ),
227 mProcess( 0 ), mIsClosing( false ), mRunAsRoot( false )
229 new KcmhelpcenterAdaptor(this);
230 QDBusConnection::sessionBus().registerObject(QLatin1String("/kcmhelpcenter"), this);
231 setObjectName( name );
232 setCaption( i18n("Build Search Index") );
233 setButtons( Ok | Cancel );
234 showButtonSeparator( true );
236 QWidget *widget = new QWidget( this );
237 setMainWidget( widget );
239 setupMainWidget( widget );
241 setButtonGuiItem( KDialog::Ok, KGuiItem(i18n("Build Index")) );
243 mConfig = KGlobal::config();
245 DocMetaInfo::self()->scanMetaInfo();
247 load();
248 const QString dbusInterface = "org.kde.khelpcenter.kcmhelpcenter";
249 QDBusConnection dbus = QDBusConnection::sessionBus();
250 bool success = dbus.connect(QString(), "/kcmhelpcenter", dbusInterface, "buildIndexProgress", this, SLOT(slotIndexProgress()));
251 if ( !success )
252 kError() << "connect D-Bus signal failed" << endl;
253 success = dbus.connect(QString(), "/kcmhelpcenter", dbusInterface, "buildIndexError", this, SLOT(slotIndexError(const QString&)));
254 if ( !success )
255 kError() << "connect D-Bus signal failed" << endl;
256 KConfigGroup id( mConfig, "IndexDialog" );
257 restoreDialogSize( id );
260 KCMHelpCenter::~KCMHelpCenter()
262 KConfigGroup cg( KGlobal::config(), "IndexDialog" );
263 KDialog::saveDialogSize( cg );
266 void KCMHelpCenter::setupMainWidget( QWidget *parent )
268 QVBoxLayout *topLayout = new QVBoxLayout( parent );
269 topLayout->setSpacing( KDialog::spacingHint() );
271 QString helpText =
272 i18n("To be able to search a document, a search\n"
273 "index needs to exist. The status column of the list below shows whether an index\n"
274 "for a document exists.\n") +
275 i18n("To create an index, check the box in the list and press the\n"
276 "\"Build Index\" button.\n");
278 QLabel *label = new QLabel( helpText, parent );
279 topLayout->addWidget( label );
281 mListView = new K3ListView( parent );
282 mListView->setFullWidth( true );
283 mListView->addColumn( i18n("Search Scope") );
284 mListView->addColumn( i18n("Status") );
285 mListView->setColumnAlignment( 1, Qt::AlignCenter );
286 topLayout->addWidget( mListView );
287 connect( mListView, SIGNAL( clicked( Q3ListViewItem * ) ),
288 SLOT( checkSelection() ) );
290 QBoxLayout *urlLayout = new QHBoxLayout();
291 topLayout->addLayout( urlLayout );
293 QLabel *urlLabel = new QLabel( i18n("Index folder:"), parent );
294 urlLayout->addWidget( urlLabel );
296 mIndexDirLabel = new QLabel( parent );
297 urlLayout->addWidget( mIndexDirLabel, 1 );
299 QPushButton *button = new QPushButton( i18n("Change..."), parent );
300 connect( button, SIGNAL( clicked() ), SLOT( showIndexDirDialog() ) );
301 urlLayout->addWidget( button );
303 QBoxLayout *buttonLayout = new QHBoxLayout();
304 topLayout->addLayout( buttonLayout );
306 buttonLayout->addStretch( 1 );
308 connect( this, SIGNAL( okClicked() ), SLOT( slotOk() ) );
311 void KCMHelpCenter::defaults()
315 bool KCMHelpCenter::save()
317 kDebug(1401) << "KCMHelpCenter::save()";
319 if ( !QFile::exists( Prefs::indexDirectory() ) ) {
320 KMessageBox::sorry( this,
321 i18n("<qt>The folder <b>%1</b> does not exist; unable to create index.</qt>",
322 Prefs::indexDirectory() ) );
323 return false;
324 } else {
325 return buildIndex();
328 return true;
331 void KCMHelpCenter::load()
333 mIndexDirLabel->setText( Prefs::indexDirectory() );
335 mListView->clear();
337 const DocEntry::List &entries = DocMetaInfo::self()->docEntries();
338 DocEntry::List::ConstIterator it;
339 for( it = entries.begin(); it != entries.end(); ++it ) {
340 // kDebug(1401) << "Entry: " << (*it)->name() << " Indexer: '"
341 // << (*it)->indexer() << "'" << endl;
342 if ( mEngine->needsIndex( *it ) ) {
343 ScopeItem *item = new ScopeItem( mListView, *it );
344 item->setOn( (*it)->searchEnabled() );
348 updateStatus();
351 void KCMHelpCenter::updateStatus()
353 Q3ListViewItemIterator it( mListView );
354 while ( it.current() != 0 ) {
355 ScopeItem *item = static_cast<ScopeItem *>( it.current() );
356 QString status;
357 if ( item->entry()->indexExists( Prefs::indexDirectory() ) ) {
358 status = i18nc("Describes the status of a documentation index that is present", "OK");
359 item->setOn( false );
360 } else {
361 status = i18nc("Describes the status of a documentation index that is missing", "Missing");
363 item->setText( 1, status );
365 ++it;
368 checkSelection();
371 bool KCMHelpCenter::buildIndex()
373 kDebug(1401) << "Build Index";
375 kDebug() << "IndexPath: '" << Prefs::indexDirectory() << "'";
377 if ( mProcess ) {
378 kError() << "Error: Index Process still running." << endl;
379 return false;
382 mIndexQueue.clear();
384 QFontMetrics fm( font() );
385 int maxWidth = 0;
387 mCmdFile = new KTemporaryFile;
388 if ( !mCmdFile->open() ) {
389 kError() << "Error opening command file." << endl;
390 deleteCmdFile();
391 return false;
394 QTextStream ts ( mCmdFile );
395 kDebug() << "Writing to file '" << mCmdFile->fileName() << "'";
397 bool hasError = false;
399 Q3ListViewItemIterator it( mListView );
400 while ( it.current() != 0 ) {
401 ScopeItem *item = static_cast<ScopeItem *>( it.current() );
402 if ( item->isOn() ) {
403 DocEntry *entry = item->entry();
405 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",
406 entry->identifier() ,
407 entry->name() );
408 if ( entry->documentType().isEmpty() ) {
409 KMessageBox::sorry( this, docText +
410 i18n("No document type.") );
411 hasError = true;
412 } else {
413 SearchHandler *handler = mEngine->handler( entry->documentType() );
414 if ( !handler ) {
415 KMessageBox::sorry( this, docText +
416 i18n("No search handler available for document type '%1'.",
417 entry->documentType() ) );
418 hasError = true;
419 } else {
420 QString indexer = handler->indexCommand( entry->identifier() );
421 if ( indexer.isEmpty() ) {
422 KMessageBox::sorry( this, docText +
423 i18n("No indexing command specified for document type '%1'.",
424 entry->documentType() ) );
425 hasError = true;
426 } else {
427 indexer.replace( QLatin1String("%i" ), entry->identifier() );
428 indexer.replace( QLatin1String( "%d" ), Prefs::indexDirectory() );
429 indexer.replace( QLatin1String( "%p" ), entry->url() );
430 kDebug() << "INDEXER: " << indexer;
431 ts << indexer << endl;
433 int width = fm.width( entry->name() );
434 if ( width > maxWidth ) maxWidth = width;
436 mIndexQueue.append( entry );
441 ++it;
444 ts.flush();
446 if ( mIndexQueue.isEmpty() ) {
447 deleteCmdFile();
448 return !hasError;
451 mCurrentEntry = mIndexQueue.constBegin();
452 QString name = (*mCurrentEntry)->name();
454 if ( !mProgressDialog ) {
455 mProgressDialog = new IndexProgressDialog( parentWidget() );
456 connect( mProgressDialog, SIGNAL( cancelled() ),
457 SLOT( cancelBuildIndex() ) );
458 connect( mProgressDialog, SIGNAL( closed() ),
459 SLOT( slotProgressClosed() ) );
461 mProgressDialog->setLabelText( name );
462 mProgressDialog->setTotalSteps( mIndexQueue.count() );
463 mProgressDialog->setMinimumLabelWidth( maxWidth );
464 mProgressDialog->show();
466 startIndexProcess();
468 return true;
471 void KCMHelpCenter::startIndexProcess()
473 kDebug() << "KCMHelpCenter::startIndexProcess()";
475 mProcess = new KProcess;
476 #ifndef Q_WS_WIN
477 if ( mRunAsRoot ) {
478 QString kdesu = KStandardDirs::findExe("kdesu");
479 if(kdesu.isEmpty()) {
480 kError() << "Failed to run index process as root - could not find kdesu";
481 } else {
482 *mProcess << kdesu;
483 if(parentWidget()) {
484 *mProcess << "--attach" << QString::number(parentWidget()->window()->winId());
485 kDebug() << "Run as root, attaching kdesu to winid " << QString::number(parentWidget()->window()->winId());
487 *mProcess << "--";
490 #endif
492 *mProcess << KStandardDirs::findExe("khc_indexbuilder");
493 *mProcess << mCmdFile->fileName();
494 *mProcess << Prefs::indexDirectory();
496 mProcess->setOutputChannelMode(KProcess::SeparateChannels);
497 connect( mProcess, SIGNAL( readyReadStandardError() ),
498 SLOT( slotReceivedStderr() ) );
499 connect( mProcess, SIGNAL( readyReadStandardOutput() ),
500 SLOT( slotReceivedStdout() ) );
501 connect( mProcess, SIGNAL( finished(int, QProcess::ExitStatus) ),
502 SLOT( slotIndexFinished(int, QProcess::ExitStatus) ) );
504 mProcess->start();
505 if (!mProcess->waitForStarted()) {
506 kError() << "KCMHelpcenter::startIndexProcess(): Failed to start process.";
507 deleteProcess();
508 deleteCmdFile();
512 void KCMHelpCenter::cancelBuildIndex()
514 kDebug() << "cancelBuildIndex()";
516 deleteProcess();
517 deleteCmdFile();
518 mIndexQueue.clear();
520 if ( mIsClosing ) {
521 mIsClosing = false;
525 void KCMHelpCenter::slotIndexFinished(int exitCode, QProcess::ExitStatus exitStatus)
527 kDebug() << "KCMHelpCenter::slotIndexFinished()";
529 if ( exitStatus == QProcess::NormalExit && exitCode == 2 ) {
530 if ( mRunAsRoot ) {
531 kError() << "Insufficient permissions." << endl;
532 } else {
533 kDebug() << "Insufficient permissions. Trying again as root.";
534 mRunAsRoot = true;
535 deleteProcess();
536 startIndexProcess();
537 return;
539 } else if ( exitStatus != QProcess::NormalExit || exitCode != 0 ) {
540 kDebug() << "KProcess reported an error.";
541 KMessageBox::error( this, i18n("Failed to build index.") );
542 } else {
543 mConfig->group( "Search" ).writeEntry( "IndexExists", true );
544 emit searchIndexUpdated();
547 deleteProcess();
548 deleteCmdFile();
550 if ( mProgressDialog ) {
551 mProgressDialog->setFinished( true );
554 mStdOut.clear();
555 mStdErr.clear();
557 if ( mIsClosing ) {
558 if ( !mProgressDialog || !mProgressDialog->isVisible() ) {
559 mIsClosing = false;
560 accept();
565 void KCMHelpCenter::deleteProcess()
567 delete mProcess;
568 mProcess = 0;
571 void KCMHelpCenter::deleteCmdFile()
573 delete mCmdFile;
574 mCmdFile = 0;
577 void KCMHelpCenter::slotIndexProgress()
579 if( !mProcess )
580 return;
582 kDebug() << "KCMHelpCenter::slotIndexProgress()";
584 updateStatus();
586 advanceProgress();
589 void KCMHelpCenter::slotIndexError( const QString &str )
591 if( !mProcess )
592 return;
594 kDebug() << "KCMHelpCenter::slotIndexError()";
596 KMessageBox::sorry( this, i18n("Error executing indexing build command:\n%1",
597 str ) );
599 if ( mProgressDialog ) {
600 mProgressDialog->appendLog( "<i>" + str + "</i>" );
603 advanceProgress();
606 void KCMHelpCenter::advanceProgress()
608 if ( mProgressDialog && mProgressDialog->isVisible() ) {
609 mProgressDialog->advanceProgress();
610 mCurrentEntry++;
611 if ( mCurrentEntry != mIndexQueue.constEnd() ) {
612 QString name = (*mCurrentEntry)->name();
613 mProgressDialog->setLabelText( name );
618 void KCMHelpCenter::slotReceivedStdout()
620 QByteArray text= mProcess->readAllStandardOutput();
621 int pos = text.lastIndexOf( '\n' );
622 if ( pos < 0 ) {
623 mStdOut.append( text );
624 } else {
625 if ( mProgressDialog ) {
626 mProgressDialog->appendLog( mStdOut + text.left( pos ) );
627 mStdOut = text.mid( pos + 1 );
632 void KCMHelpCenter::slotReceivedStderr( )
634 QByteArray text = mProcess->readAllStandardError();
635 int pos = text.lastIndexOf( '\n' );
636 if ( pos < 0 ) {
637 mStdErr.append( text );
638 } else {
639 if ( mProgressDialog ) {
640 mProgressDialog->appendLog( QLatin1String("<i>") + mStdErr + text.left( pos ) +
641 QLatin1String("</i>"));
642 mStdErr = text.mid( pos + 1 );
647 void KCMHelpCenter::slotOk()
649 if ( buildIndex() ) {
650 if ( !mProcess ) accept();
651 else mIsClosing = true;
655 void KCMHelpCenter::slotProgressClosed()
657 kDebug() << "KCMHelpCenter::slotProgressClosed()";
659 if ( mIsClosing ) accept();
662 void KCMHelpCenter::showIndexDirDialog()
664 IndexDirDialog dlg( this );
665 if ( dlg.exec() == QDialog::Accepted ) {
666 load();
670 void KCMHelpCenter::checkSelection()
672 int count = 0;
674 Q3ListViewItemIterator it( mListView );
675 while ( it.current() != 0 ) {
676 ScopeItem *item = static_cast<ScopeItem *>( it.current() );
677 if ( item->isOn() ) {
678 ++count;
680 ++it;
683 enableButtonOk( count != 0 );
686 #include "kcmhelpcenter.moc"
688 // vim:ts=2:sw=2:et