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 >>") );
218 //layout()->activate();
223 KCMHelpCenter::KCMHelpCenter( KHC::SearchEngine
*engine
, QWidget
*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();
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()));
252 kError() << "connect D-Bus signal failed" << endl
;
253 success
= dbus
.connect(QString(), "/kcmhelpcenter", dbusInterface
, "buildIndexError", this, SLOT(slotIndexError(const QString
&)));
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() );
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() ) );
331 void KCMHelpCenter::load()
333 mIndexDirLabel
->setText( Prefs::indexDirectory() );
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() );
351 void KCMHelpCenter::updateStatus()
353 Q3ListViewItemIterator
it( mListView
);
354 while ( it
.current() != 0 ) {
355 ScopeItem
*item
= static_cast<ScopeItem
*>( it
.current() );
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 );
361 status
= i18nc("Describes the status of a documentation index that is missing", "Missing");
363 item
->setText( 1, status
);
371 bool KCMHelpCenter::buildIndex()
373 kDebug(1401) << "Build Index";
375 kDebug() << "IndexPath: '" << Prefs::indexDirectory() << "'";
378 kError() << "Error: Index Process still running." << endl
;
384 QFontMetrics
fm( font() );
387 mCmdFile
= new KTemporaryFile
;
388 if ( !mCmdFile
->open() ) {
389 kError() << "Error opening command file." << endl
;
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() ,
408 if ( entry
->documentType().isEmpty() ) {
409 KMessageBox::sorry( this, docText
+
410 i18n("No document type.") );
413 SearchHandler
*handler
= mEngine
->handler( entry
->documentType() );
415 KMessageBox::sorry( this, docText
+
416 i18n("No search handler available for document type '%1'.",
417 entry
->documentType() ) );
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() ) );
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
);
446 if ( mIndexQueue
.isEmpty() ) {
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();
471 void KCMHelpCenter::startIndexProcess()
473 kDebug() << "KCMHelpCenter::startIndexProcess()";
475 mProcess
= new KProcess
;
478 QString kdesu
= KStandardDirs::findExe("kdesu");
479 if(kdesu
.isEmpty()) {
480 kError() << "Failed to run index process as root - could not find kdesu";
484 *mProcess
<< "--attach" << QString::number(parentWidget()->window()->winId());
485 kDebug() << "Run as root, attaching kdesu to winid " << QString::number(parentWidget()->window()->winId());
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
) ) );
505 if (!mProcess
->waitForStarted()) {
506 kError() << "KCMHelpcenter::startIndexProcess(): Failed to start process.";
512 void KCMHelpCenter::cancelBuildIndex()
514 kDebug() << "cancelBuildIndex()";
525 void KCMHelpCenter::slotIndexFinished(int exitCode
, QProcess::ExitStatus exitStatus
)
527 kDebug() << "KCMHelpCenter::slotIndexFinished()";
529 if ( exitStatus
== QProcess::NormalExit
&& exitCode
== 2 ) {
531 kError() << "Insufficient permissions." << endl
;
533 kDebug() << "Insufficient permissions. Trying again as root.";
539 } else if ( exitStatus
!= QProcess::NormalExit
|| exitCode
!= 0 ) {
540 kDebug() << "KProcess reported an error.";
541 KMessageBox::error( this, i18n("Failed to build index.") );
543 mConfig
->group( "Search" ).writeEntry( "IndexExists", true );
544 emit
searchIndexUpdated();
550 if ( mProgressDialog
) {
551 mProgressDialog
->setFinished( true );
558 if ( !mProgressDialog
|| !mProgressDialog
->isVisible() ) {
565 void KCMHelpCenter::deleteProcess()
571 void KCMHelpCenter::deleteCmdFile()
577 void KCMHelpCenter::slotIndexProgress()
582 kDebug() << "KCMHelpCenter::slotIndexProgress()";
589 void KCMHelpCenter::slotIndexError( const QString
&str
)
594 kDebug() << "KCMHelpCenter::slotIndexError()";
596 KMessageBox::sorry( this, i18n("Error executing indexing build command:\n%1",
599 if ( mProgressDialog
) {
600 mProgressDialog
->appendLog( "<i>" + str
+ "</i>" );
606 void KCMHelpCenter::advanceProgress()
608 if ( mProgressDialog
&& mProgressDialog
->isVisible() ) {
609 mProgressDialog
->advanceProgress();
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' );
623 mStdOut
.append( text
);
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' );
637 mStdErr
.append( text
);
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
) {
670 void KCMHelpCenter::checkSelection()
674 Q3ListViewItemIterator
it( mListView
);
675 while ( it
.current() != 0 ) {
676 ScopeItem
*item
= static_cast<ScopeItem
*>( it
.current() );
677 if ( item
->isOn() ) {
683 enableButtonOk( count
!= 0 );
686 #include "kcmhelpcenter.moc"