summaryrefslogtreecommitdiffstats
path: root/khelpcenter/kcmhelpcenter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'khelpcenter/kcmhelpcenter.cpp')
-rw-r--r--khelpcenter/kcmhelpcenter.cpp680
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 <[email protected]>
+
+ 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