diff options
Diffstat (limited to 'khelpcenter/kcmhelpcenter.cpp')
-rw-r--r-- | khelpcenter/kcmhelpcenter.cpp | 680 |
1 files changed, 680 insertions, 0 deletions
diff --git a/khelpcenter/kcmhelpcenter.cpp b/khelpcenter/kcmhelpcenter.cpp new file mode 100644 index 000000000..3c122b3b3 --- /dev/null +++ b/khelpcenter/kcmhelpcenter.cpp @@ -0,0 +1,680 @@ +/* + This file is part of KHelpcenter. + + Copyright (C) 2002 Cornelius Schumacher <schumacher@kde.org> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License version 2 as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kcmhelpcenter.h" + +#include "htmlsearchconfig.h" +#include "docmetainfo.h" +#include "prefs.h" +#include "searchhandler.h" +#include "searchengine.h" + +#include <kconfig.h> +#include <kdebug.h> +#include <klocale.h> +#include <kglobal.h> +#include <kaboutdata.h> +#include <kdialog.h> +#include <kstandarddirs.h> +#include <kprocess.h> +#include <kapplication.h> +#include <dcopclient.h> +#include <ktempfile.h> +#include <kurlrequester.h> +#include <kmessagebox.h> +#include <klistview.h> +#include <klineedit.h> +#include <qlayout.h> +#include <qpushbutton.h> +#include <qdir.h> +#include <qtabwidget.h> +#include <qprogressbar.h> +#include <qfile.h> +#include <qlabel.h> +#include <qvbox.h> +#include <qtextedit.h> +#include <qregexp.h> + +#include <unistd.h> +#include <sys/types.h> + +using namespace KHC; + +IndexDirDialog::IndexDirDialog( QWidget *parent ) + : KDialogBase( parent, 0, true, i18n("Change Index Folder"), Ok | Cancel ) +{ + QFrame *topFrame = makeMainWidget(); + + QBoxLayout *urlLayout = new QHBoxLayout( topFrame ); + + QLabel *label = new QLabel( i18n("Index folder:"), topFrame ); + urlLayout->addWidget( label ); + + mIndexUrlRequester = new KURLRequester( topFrame ); + mIndexUrlRequester->setMode( KFile::Directory | KFile::ExistingOnly | + KFile::LocalOnly ); + urlLayout->addWidget( mIndexUrlRequester ); + + mIndexUrlRequester->setURL( Prefs::indexDirectory() ); + connect(mIndexUrlRequester->lineEdit(),SIGNAL(textChanged ( const QString & )), this, SLOT(slotUrlChanged( const QString &))); + slotUrlChanged( mIndexUrlRequester->lineEdit()->text() ); +} + +void IndexDirDialog::slotUrlChanged( const QString &_url ) +{ + enableButtonOK( !_url.isEmpty() ); +} + + +void IndexDirDialog::slotOk() +{ + Prefs::setIndexDirectory( mIndexUrlRequester->url() ); + accept(); +} + + +IndexProgressDialog::IndexProgressDialog( QWidget *parent ) + : KDialog( parent, "IndexProgressDialog", true ), + mFinished( true ) +{ + setCaption( i18n("Build Search Indices") ); + + QBoxLayout *topLayout = new QVBoxLayout( this ); + topLayout->setMargin( marginHint() ); + topLayout->setSpacing( spacingHint() ); + + mLabel = new QLabel( this ); + mLabel->setAlignment( AlignHCenter ); + topLayout->addWidget( mLabel ); + + mProgressBar = new QProgressBar( this ); + topLayout->addWidget( mProgressBar ); + + mLogLabel = new QLabel( i18n("Index creation log:"), this ); + topLayout->addWidget( mLogLabel ); + + mLogView = new QTextEdit( this ); + mLogView->setTextFormat( LogText ); + mLogView->setMinimumHeight( 200 ); + topLayout->addWidget( mLogView, 1 ); + + QBoxLayout *buttonLayout = new QHBoxLayout( topLayout ); + + buttonLayout->addStretch( 1 ); + + mDetailsButton = new QPushButton( this ); + connect( mDetailsButton, SIGNAL( clicked() ), SLOT( toggleDetails() ) ); + buttonLayout->addWidget( mDetailsButton ); + + hideDetails(); + + mEndButton = new QPushButton( this ); + connect( mEndButton, SIGNAL( clicked() ), SLOT( slotEnd() ) ); + buttonLayout->addWidget( mEndButton ); + + setFinished( false ); +} + +IndexProgressDialog::~IndexProgressDialog() +{ + if ( !mLogView->isHidden() ) { + KConfig *cfg = KGlobal::config(); + cfg->setGroup( "indexprogressdialog" ); + cfg->writeEntry( "size", size() ); + } +} + +void IndexProgressDialog::setTotalSteps( int steps ) +{ + mProgressBar->setTotalSteps( steps ); + mProgressBar->setProgress( 0 ); + setFinished( false ); + mLogView->clear(); +} + +void IndexProgressDialog::advanceProgress() +{ + mProgressBar->setProgress( mProgressBar->progress() + 1 ); +} + +void IndexProgressDialog::setLabelText( const QString &text ) +{ + mLabel->setText( text ); +} + +void IndexProgressDialog::setMinimumLabelWidth( int width ) +{ + mLabel->setMinimumWidth( width ); +} + +void IndexProgressDialog::setFinished( bool finished ) +{ + if ( finished == mFinished ) return; + + mFinished = finished; + + if ( mFinished ) { + mEndButton->setText( i18n("Close") ); + mLabel->setText( i18n("Index creation finished.") ); + } else { + mEndButton->setText( i18n("Stop") ); + } +} + +void IndexProgressDialog::appendLog( const QString &text ) +{ + mLogView->append( text ); +} + +void IndexProgressDialog::slotEnd() +{ + if ( mFinished ) { + emit closed(); + accept(); + } else { + emit cancelled(); + reject(); + } +} + +void IndexProgressDialog::toggleDetails() +{ + KConfig *cfg = KGlobal::config(); + cfg->setGroup( "indexprogressdialog" ); + if ( mLogView->isHidden() ) { + mLogLabel->show(); + mLogView->show(); + mDetailsButton->setText( i18n("Details <<") ); + QSize size = cfg->readSizeEntry( "size" ); + if ( !size.isEmpty() ) resize( size ); + } else { + cfg->writeEntry( "size", size() ); + hideDetails(); + } +} + +void IndexProgressDialog::hideDetails() +{ + mLogLabel->hide(); + mLogView->hide(); + mDetailsButton->setText( i18n("Details >>") ); + layout()->activate(); + adjustSize(); +} + + +KCMHelpCenter::KCMHelpCenter( KHC::SearchEngine *engine, QWidget *parent, + const char *name) + : DCOPObject( "kcmhelpcenter" ), + KDialogBase( parent, name, false, i18n("Build Search Index"), + Ok | Cancel, Ok, true ), + mEngine( engine ), mProgressDialog( 0 ), mCurrentEntry( 0 ), mCmdFile( 0 ), + mProcess( 0 ), mIsClosing( false ), mRunAsRoot( false ) +{ + QWidget *widget = makeMainWidget(); + + setupMainWidget( widget ); + + setButtonOK( i18n("Build Index") ); + + mConfig = KGlobal::config(); + + DocMetaInfo::self()->scanMetaInfo(); + + load(); + + bool success = kapp->dcopClient()->connectDCOPSignal( "khc_indexbuilder", + 0, "buildIndexProgress()", "kcmhelpcenter", + "slotIndexProgress()", false ); + if ( !success ) kdError() << "connect DCOP signal failed" << endl; + + success = kapp->dcopClient()->connectDCOPSignal( "khc_indexbuilder", + 0, "buildIndexError(QString)", "kcmhelpcenter", + "slotIndexError(QString)", false ); + if ( !success ) kdError() << "connect DCOP signal failed" << endl; + + resize( configDialogSize( "IndexDialog" ) ); +} + +KCMHelpCenter::~KCMHelpCenter() +{ + saveDialogSize( "IndexDialog" ); +} + +void KCMHelpCenter::setupMainWidget( QWidget *parent ) +{ + QVBoxLayout *topLayout = new QVBoxLayout( parent ); + topLayout->setSpacing( KDialog::spacingHint() ); + + QString helpText = + i18n("To be able to search a document, there needs to exist a search\n" + "index. The status column of the list below shows, if an index\n" + "for a document exists.\n") + + i18n("To create an index check the box in the list and press the\n" + "\"Build Index\" button.\n"); + + QLabel *label = new QLabel( helpText, parent ); + topLayout->addWidget( label ); + + mListView = new KListView( parent ); + mListView->setFullWidth( true ); + mListView->addColumn( i18n("Search Scope") ); + mListView->addColumn( i18n("Status") ); + mListView->setColumnAlignment( 1, AlignCenter ); + topLayout->addWidget( mListView ); + connect( mListView, SIGNAL( clicked( QListViewItem * ) ), + SLOT( checkSelection() ) ); + + QBoxLayout *urlLayout = new QHBoxLayout( topLayout ); + + QLabel *urlLabel = new QLabel( i18n("Index folder:"), parent ); + urlLayout->addWidget( urlLabel ); + + mIndexDirLabel = new QLabel( parent ); + urlLayout->addWidget( mIndexDirLabel, 1 ); + + QPushButton *button = new QPushButton( i18n("Change..."), parent ); + connect( button, SIGNAL( clicked() ), SLOT( showIndexDirDialog() ) ); + urlLayout->addWidget( button ); + + QBoxLayout *buttonLayout = new QHBoxLayout( topLayout ); + + buttonLayout->addStretch( 1 ); +} + +void KCMHelpCenter::defaults() +{ +} + +bool KCMHelpCenter::save() +{ + kdDebug(1401) << "KCMHelpCenter::save()" << endl; + + if ( !QFile::exists( Prefs::indexDirectory() ) ) { + KMessageBox::sorry( this, + i18n("<qt>The folder <b>%1</b> does not exist; unable to create index.</qt>") + .arg( Prefs::indexDirectory() ) ); + return false; + } else { + return buildIndex(); + } + + return true; +} + +void KCMHelpCenter::load() +{ + mIndexDirLabel->setText( Prefs::indexDirectory() ); + + mListView->clear(); + + DocEntry::List entries = DocMetaInfo::self()->docEntries(); + DocEntry::List::ConstIterator it; + for( it = entries.begin(); it != entries.end(); ++it ) { +// kdDebug(1401) << "Entry: " << (*it)->name() << " Indexer: '" +// << (*it)->indexer() << "'" << endl; + if ( mEngine->canSearch( *it ) && mEngine->needsIndex( *it ) ) { + ScopeItem *item = new ScopeItem( mListView, *it ); + item->setOn( (*it)->searchEnabled() ); + } + } + + updateStatus(); +} + +void KCMHelpCenter::updateStatus() +{ + QListViewItemIterator it( mListView ); + while ( it.current() != 0 ) { + ScopeItem *item = static_cast<ScopeItem *>( it.current() ); + QString status; + if ( item->entry()->indexExists( Prefs::indexDirectory() ) ) { + status = i18n("OK"); + item->setOn( false ); + } else { + status = i18n("Missing"); + } + item->setText( 1, status ); + + ++it; + } + + checkSelection(); +} + +bool KCMHelpCenter::buildIndex() +{ + kdDebug(1401) << "Build Index" << endl; + + kdDebug() << "IndexPath: '" << Prefs::indexDirectory() << "'" << endl; + + if ( mProcess ) { + kdError() << "Error: Index Process still running." << endl; + return false; + } + + mIndexQueue.clear(); + + QFontMetrics fm( font() ); + int maxWidth = 0; + + mCmdFile = new KTempFile; + mCmdFile->setAutoDelete( true ); + QTextStream *ts = mCmdFile->textStream(); + if ( !ts ) { + kdError() << "Error opening command file." << endl; + deleteCmdFile(); + return false; + } else { + kdDebug() << "Writing to file '" << mCmdFile->name() << "'" << endl; + } + + bool hasError = false; + + QListViewItemIterator it( mListView ); + while ( it.current() != 0 ) { + ScopeItem *item = static_cast<ScopeItem *>( it.current() ); + if ( item->isOn() ) { + DocEntry *entry = item->entry(); + + QString docText = i18n("Document '%1' (%2):\n") + .arg( entry->identifier() ) + .arg( entry->name() ); + if ( entry->documentType().isEmpty() ) { + KMessageBox::sorry( this, docText + + i18n("No document type.") ); + hasError = true; + } else { + SearchHandler *handler = mEngine->handler( entry->documentType() ); + if ( !handler ) { + KMessageBox::sorry( this, docText + + i18n("No search handler available for document type '%1'.") + .arg( entry->documentType() ) ); + hasError = true; + } else { + QString indexer = handler->indexCommand( entry->identifier() ); + if ( indexer.isEmpty() ) { + KMessageBox::sorry( this, docText + + i18n("No indexing command specified for document type '%1'.") + .arg( entry->documentType() ) ); + hasError = true; + } else { + indexer.replace( QRegExp( "%i" ), entry->identifier() ); + indexer.replace( QRegExp( "%d" ), Prefs::indexDirectory() ); + indexer.replace( QRegExp( "%p" ), entry->url() ); + kdDebug() << "INDEXER: " << indexer << endl; + *ts << indexer << endl; + + int width = fm.width( entry->name() ); + if ( width > maxWidth ) maxWidth = width; + + mIndexQueue.append( entry ); + } + } + } + } + ++it; + } + + mCmdFile->close(); + + if ( mIndexQueue.isEmpty() ) { + deleteCmdFile(); + return !hasError; + } + + mCurrentEntry = mIndexQueue.begin(); + QString name = (*mCurrentEntry)->name(); + + if ( !mProgressDialog ) { + mProgressDialog = new IndexProgressDialog( this ); + connect( mProgressDialog, SIGNAL( cancelled() ), + SLOT( cancelBuildIndex() ) ); + connect( mProgressDialog, SIGNAL( closed() ), + SLOT( slotProgressClosed() ) ); + } + mProgressDialog->setLabelText( name ); + mProgressDialog->setTotalSteps( mIndexQueue.count() ); + mProgressDialog->setMinimumLabelWidth( maxWidth ); + mProgressDialog->show(); + + startIndexProcess(); + + return true; +} + +void KCMHelpCenter::startIndexProcess() +{ + kdDebug() << "KCMHelpCenter::startIndexProcess()" << endl; + + mProcess = new KProcess; + + if ( mRunAsRoot ) { + *mProcess << "kdesu" << "--nonewdcop"; + kdDebug() << "Run as root" << endl; + } + + *mProcess << locate("exe", "khc_indexbuilder"); + *mProcess << mCmdFile->name(); + *mProcess << Prefs::indexDirectory(); + + connect( mProcess, SIGNAL( processExited( KProcess * ) ), + SLOT( slotIndexFinished( KProcess * ) ) ); + connect( mProcess, SIGNAL( receivedStdout( KProcess *, char *, int ) ), + SLOT( slotReceivedStdout(KProcess *, char *, int ) ) ); + connect( mProcess, SIGNAL( receivedStderr( KProcess *, char *, int ) ), + SLOT( slotReceivedStderr( KProcess *, char *, int ) ) ); + + if ( !mProcess->start( KProcess::NotifyOnExit, KProcess::AllOutput ) ) { + kdError() << "KCMHelpcenter::startIndexProcess(): Failed to start process." + << endl; + } +} + +void KCMHelpCenter::cancelBuildIndex() +{ + kdDebug() << "cancelBuildIndex()" << endl; + + deleteProcess(); + deleteCmdFile(); + mIndexQueue.clear(); + + if ( mIsClosing ) { + mIsClosing = false; + } +} + +void KCMHelpCenter::slotIndexFinished( KProcess *proc ) +{ + kdDebug() << "KCMHelpCenter::slotIndexFinished()" << endl; + + if ( proc == 0 ) { + kdWarning() << "Process null." << endl; + return; + } + + if ( proc != mProcess ) { + kdError() << "Unexpected Process finished." << endl; + return; + } + + if ( mProcess->normalExit() && mProcess->exitStatus() == 2 ) { + if ( mRunAsRoot ) { + kdError() << "Insufficient permissions." << endl; + } else { + kdDebug() << "Insufficient permissions. Trying again as root." << endl; + mRunAsRoot = true; + deleteProcess(); + startIndexProcess(); + return; + } + } else if ( !mProcess->normalExit() || mProcess->exitStatus() != 0 ) { + kdDebug() << "KProcess reported an error." << endl; + KMessageBox::error( this, i18n("Failed to build index.") ); + } else { + mConfig->setGroup( "Search" ); + mConfig->writeEntry( "IndexExists", true ); + emit searchIndexUpdated(); + } + + deleteProcess(); + deleteCmdFile(); + + mCurrentEntry = 0; + + if ( mProgressDialog ) { + mProgressDialog->setFinished( true ); + } + + mStdOut = QString(); + mStdErr = QString(); + + if ( mIsClosing ) { + if ( !mProgressDialog->isVisible() ) { + mIsClosing = false; + accept(); + } + } +} + +void KCMHelpCenter::deleteProcess() +{ + delete mProcess; + mProcess = 0; +} + +void KCMHelpCenter::deleteCmdFile() +{ + delete mCmdFile; + mCmdFile = 0; +} + +void KCMHelpCenter::slotIndexProgress() +{ + if( !mProcess ) + return; + + kdDebug() << "KCMHelpCenter::slotIndexProgress()" << endl; + + updateStatus(); + + advanceProgress(); +} + +void KCMHelpCenter::slotIndexError( const QString &str ) +{ + if( !mProcess ) + return; + + kdDebug() << "KCMHelpCenter::slotIndexError()" << endl; + + KMessageBox::sorry( this, i18n("Error executing indexing build command:\n%1") + .arg( str ) ); + + if ( mProgressDialog ) { + mProgressDialog->appendLog( "<i>" + str + "</i>" ); + } + + advanceProgress(); +} + +void KCMHelpCenter::advanceProgress() +{ + if ( mProgressDialog && mProgressDialog->isVisible() ) { + mProgressDialog->advanceProgress(); + mCurrentEntry++; + if ( mCurrentEntry != mIndexQueue.end() ) { + QString name = (*mCurrentEntry)->name(); + mProgressDialog->setLabelText( name ); + } + } +} + +void KCMHelpCenter::slotReceivedStdout( KProcess *, char *buffer, int buflen ) +{ + QString text = QString::fromLocal8Bit( buffer, buflen ); + int pos = text.findRev( '\n' ); + if ( pos < 0 ) { + mStdOut.append( text ); + } else { + if ( mProgressDialog ) { + mProgressDialog->appendLog( mStdOut + text.left( pos ) ); + mStdOut = text.mid( pos + 1 ); + } + } +} + +void KCMHelpCenter::slotReceivedStderr( KProcess *, char *buffer, int buflen ) +{ + QString text = QString::fromLocal8Bit( buffer, buflen ); + int pos = text.findRev( '\n' ); + if ( pos < 0 ) { + mStdErr.append( text ); + } else { + if ( mProgressDialog ) { + mProgressDialog->appendLog( "<i>" + mStdErr + text.left( pos ) + + "</i>"); + mStdErr = text.mid( pos + 1 ); + } + } +} + +void KCMHelpCenter::slotOk() +{ + if ( buildIndex() ) { + if ( !mProcess ) accept(); + else mIsClosing = true; + } +} + +void KCMHelpCenter::slotProgressClosed() +{ + kdDebug() << "KCMHelpCenter::slotProgressClosed()" << endl; + + if ( mIsClosing ) accept(); +} + +void KCMHelpCenter::showIndexDirDialog() +{ + IndexDirDialog dlg( this ); + if ( dlg.exec() == QDialog::Accepted ) { + load(); + } +} + +void KCMHelpCenter::checkSelection() +{ + int count = 0; + + QListViewItemIterator it( mListView ); + while ( it.current() != 0 ) { + ScopeItem *item = static_cast<ScopeItem *>( it.current() ); + if ( item->isOn() ) { + ++count; + } + ++it; + } + + enableButtonOK( count != 0 ); +} + +#include "kcmhelpcenter.moc" + +// vim:ts=2:sw=2:et |