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"
27 #include "searchhandler.h"
28 #include "searchengine.h"
31 #include "kcmhelpcenteradaptor.h"
37 #include <kaboutdata.h>
39 #include <kstandarddirs.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>
51 #include <QProgressBar>
55 #include <sys/types.h>
59 IndexDirDialog::IndexDirDialog( QWidget
*parent
)
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
|
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() );
99 IndexProgressDialog::IndexProgressDialog( QWidget
*parent
)
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() ) );
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 );
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
;
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() );
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()
196 void IndexProgressDialog::toggleDetails()
198 KConfigGroup
cfg(KGlobal::config(), "indexprogressdialog");
199 if ( mLogView
->isHidden() ) {
202 setButtonText( User1
, i18n("Details <<") );
203 QSize size
= cfg
.readEntry( "size", QSize() );
204 if ( !size
.isEmpty() ) resize( size
);
206 cfg
.writeEntry( "size", size() );
211 void IndexProgressDialog::hideDetails()
215 setButtonText( User1
, i18n("Details >>") );
216 layout()->activate();
221 KCMHelpCenter::KCMHelpCenter( KHC::SearchEngine
*engine
, QWidget
*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();
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()));
250 kError() << "connect D-Bus signal failed" << endl
;
251 success
= dbus
.connect(QString(), "/kcmhelpcenter", dbusInterface
, "buildIndexError", this, SLOT(slotIndexError(const QString
&)));
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() );
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() ) );
329 void KCMHelpCenter::load()
331 mIndexDirLabel
->setText( Prefs::indexDirectory() );
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() );
349 void KCMHelpCenter::updateStatus()
351 Q3ListViewItemIterator
it( mListView
);
352 while ( it
.current() != 0 ) {
353 ScopeItem
*item
= static_cast<ScopeItem
*>( it
.current() );
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 );
359 status
= i18nc("Describes the status of a documentation index that is missing", "Missing");
361 item
->setText( 1, status
);
369 bool KCMHelpCenter::buildIndex()
371 kDebug(1401) << "Build Index";
373 kDebug() << "IndexPath: '" << Prefs::indexDirectory() << "'";
376 kError() << "Error: Index Process still running." << endl
;
382 QFontMetrics
fm( font() );
385 mCmdFile
= new KTemporaryFile
;
386 if ( !mCmdFile
->open() ) {
387 kError() << "Error opening command file." << endl
;
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() ,
406 if ( entry
->documentType().isEmpty() ) {
407 KMessageBox::sorry( this, docText
+
408 i18n("No document type.") );
411 SearchHandler
*handler
= mEngine
->handler( entry
->documentType() );
413 KMessageBox::sorry( this, docText
+
414 i18n("No search handler available for document type '%1'.",
415 entry
->documentType() ) );
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() ) );
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
);
444 if ( mIndexQueue
.isEmpty() ) {
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();
469 void KCMHelpCenter::startIndexProcess()
471 kDebug() << "KCMHelpCenter::startIndexProcess()";
473 mProcess
= new KProcess
;
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
) ) );
492 if (!mProcess
->waitForStarted()) {
493 kError() << "KCMHelpcenter::startIndexProcess(): Failed to start process."
500 void KCMHelpCenter::cancelBuildIndex()
502 kDebug() << "cancelBuildIndex()";
513 void KCMHelpCenter::slotIndexFinished(int exitCode
, QProcess::ExitStatus exitStatus
)
515 kDebug() << "KCMHelpCenter::slotIndexFinished()";
517 if ( exitStatus
== QProcess::NormalExit
&& exitCode
== 2 ) {
519 kError() << "Insufficient permissions." << endl
;
521 kDebug() << "Insufficient permissions. Trying again as root.";
527 } else if ( exitStatus
!= QProcess::NormalExit
|| exitCode
!= 0 ) {
528 kDebug() << "KProcess reported an error.";
529 KMessageBox::error( this, i18n("Failed to build index.") );
531 mConfig
->group( "Search" ).writeEntry( "IndexExists", true );
532 emit
searchIndexUpdated();
538 if ( mProgressDialog
) {
539 mProgressDialog
->setFinished( true );
546 if ( !mProgressDialog
|| !mProgressDialog
->isVisible() ) {
553 void KCMHelpCenter::deleteProcess()
559 void KCMHelpCenter::deleteCmdFile()
565 void KCMHelpCenter::slotIndexProgress()
570 kDebug() << "KCMHelpCenter::slotIndexProgress()";
577 void KCMHelpCenter::slotIndexError( const QString
&str
)
582 kDebug() << "KCMHelpCenter::slotIndexError()";
584 KMessageBox::sorry( this, i18n("Error executing indexing build command:\n%1",
587 if ( mProgressDialog
) {
588 mProgressDialog
->appendLog( "<i>" + str
+ "</i>" );
594 void KCMHelpCenter::advanceProgress()
596 if ( mProgressDialog
&& mProgressDialog
->isVisible() ) {
597 mProgressDialog
->advanceProgress();
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' );
611 mStdOut
.append( text
);
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' );
625 mStdErr
.append( text
);
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
) {
658 void KCMHelpCenter::checkSelection()
662 Q3ListViewItemIterator
it( mListView
);
663 while ( it
.current() != 0 ) {
664 ScopeItem
*item
= static_cast<ScopeItem
*>( it
.current() );
665 if ( item
->isOn() ) {
671 enableButtonOk( count
!= 0 );
674 #include "kcmhelpcenter.moc"