You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kile/src/kile/texdocdialog.cpp

661 lines
18 KiB

/***************************************************************************
texdocdialog.cpp
----------------
date : Feb 15 2007
version : 0.14
copyright : (C) 2005-2007 by Holger Danielsson
email : holger.danielsson@versanet.de
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "texdocdialog.h"
#include <tqlayout.h>
#include <tqstringlist.h>
#include <tqfile.h>
#include <tqfileinfo.h>
#include <tqtextstream.h>
#include <tqgroupbox.h>
#include <tqregexp.h>
#include <tqwhatsthis.h>
#include <kapplication.h>
#include <tqdesktopwidget.h>
#include <kurl.h>
#include <krun.h>
#include <kmimetype.h>
#include <kiconloader.h>
#include <kmessagebox.h>
#include <klocale.h>
#include "kiledebug.h"
#include <tdeversion.h>
#include <ktrader.h>
#include <kservice.h>
namespace KileDialog
{
//BEGIN TexDocDialog
TexDocDialog::TexDocDialog(TQWidget *parent, const char *name)
: KDialogBase( parent,name, true, i18n("Documentation Browser"), Close | Help,
#if TDE_VERSION >= KDE_MAKE_VERSION(3,3,0)
NoDefault
#else
Close
#endif
, true ),
m_tempfile(0), m_proc(0)
{
TQWidget *page = new TQWidget( this );
setMainWidget(page);
TQVBoxLayout *vbox = new TQVBoxLayout(page,8,8);
// listview
m_texdocs = new KListView(page);
m_texdocs->setRootIsDecorated(true);
m_texdocs->addColumn(i18n("Table of Contents"));
// groupbox
TQGroupBox *groupbox = new TQGroupBox( i18n("Search"), page, "groupbox" );
groupbox->tqsetSizePolicy( TQSizePolicy( (TQSizePolicy::SizeType)5, (TQSizePolicy::SizeType)1, 0, 0, groupbox->sizePolicy().hasHeightForWidth() ) );
groupbox->setColumnLayout(0, Qt::Vertical );
groupbox->tqlayout()->setSpacing( 6 );
groupbox->tqlayout()->setMargin( 11 );
TQGridLayout *groupboxLayout = new TQGridLayout( groupbox->tqlayout() );
groupboxLayout->tqsetAlignment( TQt::AlignTop );
TQLabel *label = new TQLabel( i18n("&Keyword:"), groupbox, "label");
m_leKeywords = new KLineEdit("",groupbox);
m_pbSearch = new KPushButton(i18n("&Search"),groupbox);
label->setBuddy(m_leKeywords);
groupboxLayout->addWidget(label,0,0);
groupboxLayout->addWidget(m_leKeywords,0,1);
groupboxLayout->addWidget(m_pbSearch,1,0);
vbox->addWidget(m_texdocs);
vbox->addWidget(groupbox);
TQWhatsThis::add(m_texdocs,i18n("A list of avaiblable documents, which are listed in 'texdoctk.dat', coming with TexLive/teTeX. A double click with the mouse or pressing the space key will open a viewer to show this file."));
TQWhatsThis::add(m_leKeywords,i18n("You can choose a keyword to show only document files, which are related to this keyword."));
TQWhatsThis::add(m_pbSearch,i18n("Start the search for the chosen keyword."));
TQWhatsThis::add(actionButton(Help),i18n("Reset TOC to show all available files."));
setButtonText(Help,i18n("Reset &TOC"));
m_pbSearch->setEnabled(false);
enableButton(Help,false);
// catch some Return/Enter events
m_texdocs->installEventFilter(this);
m_leKeywords->installEventFilter(this);
connect(m_texdocs, TQT_SIGNAL(doubleClicked(TQListViewItem *,const TQPoint &,int)),
this, TQT_SLOT(slotListViewDoubleClicked(TQListViewItem *,const TQPoint &,int)));
connect(m_pbSearch, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotSearchClicked()));
connect(m_leKeywords, TQT_SIGNAL(textChanged(const TQString &)), this, TQT_SLOT(slotTextChanged(const TQString &)));
// kpsewhich --expand-path='$TEXMF'
//m_texmfPath = "/usr/local/share/texmf:/usr/local/lib/texmf:/var/lib/texmf:/usr/share/texmf";
// kpsewhich --expand-path='$TEXMF/doc'
//m_texmfdocPath = "/usr/local/share/texmf/doc:/usr/local/lib/texmf/doc:/usr/share/texmf/doc";
// kpsewhich --progname=texdoctk --format='other text files' texdoctk.dat
//m_texdoctkPath = "/usr/share/texmf/texdoctk/texdoctk.dat";
m_texmfPath = TQString();
m_texmfdocPath = TQString();
m_texdoctkPath = TQString();
TQDesktopWidget *desktop = KApplication::desktop();
int w = desktop->screenGeometry(0).width();
if ( w >= 1024 )
w = 550;
else if ( w >= 800 )
w = 500;
else
w = 450;
int h = desktop->screenGeometry(0).height() ;
if ( h >= 768 )
h = 550;
else if ( h >= 600 )
h = 500;
else
h = 450;
resize(w,h);
connect(this, TQT_SIGNAL(processFinished()), this, TQT_SLOT(slotInitToc()));
executeScript(
"kpsewhich --progname=texdoctk --format='other text files' texdoctk.dat && "
"kpsewhich --expand-path='$TEXMF/doc' && "
"kpsewhich --expand-path='$TEXMF'"
);
//readToc();
//slotHelp();
}
TexDocDialog::~TexDocDialog()
{
if (m_proc )
delete m_proc;
if ( m_tempfile )
delete m_tempfile;
}
////////////////////// TOC //////////////////////
void TexDocDialog::readToc()
{
// open to read
TQFile fin( m_texdoctkPath );
if ( !fin.exists() || !fin.open(IO_ReadOnly) )
{
KMessageBox::error(this,i18n("Could not read 'texdoctk.dat'."));
return;
}
// use a textstream to read all data
TQString textline;
TQTextStream data(&fin);
while ( ! data.eof() )
{
textline = data.readLine();
if ( ! (textline.isEmpty() || textline[0]=='#') )
{
// save the whole entry
m_tocList.append( textline );
// list entries 0,1,basename(2),3 are needed for keyword search
// (key,title,filepath,keywords)
TQStringList list = TQStringList::split(';',textline,true);
// get basename of help file
TQString basename;
if ( list.count() > 2 )
{
TQFileInfo fi(list[2]);
basename = fi.baseName().lower();
}
TQString entry = list[0] + ';' + list[1];
if ( ! basename.isEmpty() )
entry += ';' + basename;
if ( list.count() > 3 )
entry += ';' + list[3];
m_tocSearchList.append(entry);
}
}
}
void TexDocDialog::showToc(const TQString &caption,const TQStringList &doclist, bool toc)
{
TQString section,textline;
TQStringList keylist;
KListViewItem *itemsection = 0L;
setUpdatesEnabled( false );
m_texdocs->setColumnText(0,caption);
for (uint i=0; i<doclist.count(); i++ )
{
if ( doclist[i][0] == '@' )
{
section = doclist[i];
itemsection = new KListViewItem(m_texdocs,section.remove(0,1));
}
else
{
keylist = TQStringList::split(';',doclist[i],true);
if ( itemsection )
{
KListViewItem *item = new KListViewItem(itemsection,keylist[1],keylist[0]);
item->setPixmap(0, SmallIcon(getIconName(keylist[2])) );
// save filename in dictionary
m_dictDocuments[keylist[0]] = keylist[2];
// search for special keywords
TQRegExp reg( "^\\s*(-\\d-)" );
if ( keylist[3].find(reg,0) == 0 )
{
m_dictStyleCodes[keylist[0]] = reg.cap(1);
}
}
}
}
setUpdatesEnabled( true );
if ( toc )
m_pbSearch->setEnabled(false);
enableButton(Help,!toc);
m_texdocs->setFocus();
}
bool TexDocDialog::eventFilter(TQObject *o, TQEvent *e)
{
// catch KeyPress events
if ( e->type() == TQEvent::KeyPress )
{
TQKeyEvent *kev = (TQKeyEvent*) e;
// ListView:
// - space: enable start of viewer
// - return: ignore
if ( TQT_BASE_OBJECT(o) == TQT_BASE_OBJECT(m_texdocs) )
{
if ( kev->key() == TQt::Key_Space )
{
slotListViewDoubleClicked(m_texdocs->currentItem(), TQPoint(0,0), 0) ;
return true;
}
if ( kev->key()==TQt::Key_Return || kev->key()==TQt::Key_Enter )
return true;
}
// LineEdit
// - return: start search, if button is enabled
if ( TQT_BASE_OBJECT(o) == TQT_BASE_OBJECT(m_leKeywords) )
{
if ( kev->key()==TQt::Key_Return || kev->key()==TQt::Key_Enter )
{
callSearch();
return true;
}
}
}
return false;
}
////////////////////// prepare document file //////////////////////
TQString TexDocDialog::searchFile(const TQString &docfilename,const TQString &listofpathes, const TQString &subdir)
{
TQStringList pathlist = TQStringList::split(':',listofpathes);
TQStringList extlist = TQStringList::split(',',",.gz,.bz2",true);
TQString filename;
for ( TQStringList::Iterator itp = pathlist.begin(); itp!=pathlist.end(); ++itp )
{
for ( TQStringList::Iterator ite = extlist.begin(); ite!=extlist.end(); ++ite )
{
filename = ( subdir.isEmpty() ) ? (*itp) + '/' + docfilename + (*ite)
: (*itp) + '/' + subdir + '/' + docfilename + (*ite);
// KILE_DEBUG() << "search file: " << filename << endl;
if ( TQFile::exists(filename) )
return filename;
}
}
return TQString();
}
void TexDocDialog::decompressFile(const TQString &docfile,const TQString &command)
{
TQString ext = TQFileInfo(docfile).extension(false).lower();
if ( ! ( ext=="dvi" || ext=="pdf" || ext=="ps" || ext=="html") )
ext = "txt";
if ( m_tempfile )
delete m_tempfile;
m_tempfile = new KTempFile(TQString(), '.' + ext);
m_tempfile->setAutoDelete(true);
m_filename = m_tempfile->name();
KILE_DEBUG() << "\tdecompress file: " << command + " > " + m_tempfile->name() << endl;
connect(this, TQT_SIGNAL(processFinished()), this, TQT_SLOT(slotShowFile()));
executeScript(command + " > " + m_tempfile->name());
}
void TexDocDialog::showStyleFile(const TQString &filename,const TQString &stylecode)
{
KILE_DEBUG() << "\tshow style file: "<< filename << endl;
if ( ! TQFile::exists(filename) )
return;
// open to read
TQFile fin( filename );
if ( !fin.exists() || !fin.open(IO_ReadOnly) )
{
KMessageBox::error(this,i18n("Could not read the style file."));
return;
}
if ( m_tempfile )
delete m_tempfile;
m_tempfile = new KTempFile(TQString(),".txt");
m_tempfile->setAutoDelete(true);
// use a textstream to write to the temporary file
TQFile tmpfile(m_tempfile->name());
if ( ! tmpfile.open( IO_WriteOnly ) )
{
KMessageBox::error(this,i18n("Could not create a temporary file."));
return ;
}
TQTextStream stream(&tmpfile);
// use another textstream to read from the style file
TQTextStream sty( &fin );
// there are four mode to read from the style file
TQString textline;
if ( stylecode == "-3-" )
{
// mode 3: read everything up to the first empty line
while ( ! sty.eof() ) {
textline = sty.readLine().stripWhiteSpace();
if ( textline.isEmpty() )
break;
stream << textline << "\n";
}
}
else if ( stylecode == "-2-" )
{
// mode 2: read everything up to a line starting with at least 4 '%' characters
for ( int i=0; i<9; ++i )
stream << sty.readLine() << "\n";
while ( ! sty.eof() )
{
textline = sty.readLine();
if ( textline.find("%%%%") == 0 )
break;
stream << textline << "\n";
}
}
else if ( stylecode == "-1-" )
{
// mode 1: read all lines at the end behind \endinput
while ( ! sty.eof() )
{
textline = sty.readLine().stripWhiteSpace();
if ( textline.find("\\endinput") == 0 )
break;
}
while ( ! sty.eof() )
{
stream << sty.readLine() << "\n";
}
}
else
{
// mode 0: read everything except empty lines and comments
while ( ! sty.eof() )
{
textline = sty.readLine();
if ( !textline.isEmpty() && textline[0]!='%' )
stream << textline << "\n";
}
}
tmpfile.close();
// start the viewer
showFile(m_tempfile->name());
}
void TexDocDialog::showFile(const TQString &filename)
{
KILE_DEBUG() << "\tshow file: "<< filename << endl;
if ( TQFile::exists(filename) )
{
KURL url;
url.setPath(filename);
KTrader::OfferList offers = KTrader::self()->query( getMimeType(filename),"Type == 'Application'");
if ( offers.isEmpty() )
{
KMessageBox::error(this,i18n("No KDE service found for this file."));
return;
}
KService::Ptr ptr = offers.first();
KURL::List lst;
lst.append(url);
KRun::run(*ptr, lst, true);
}
}
////////////////////// Slots //////////////////////
void TexDocDialog::slotListViewDoubleClicked(TQListViewItem *item,const TQPoint &,int)
{
if ( ! item->parent() )
return;
TQString package = item->text(1);
KILE_DEBUG() << "\tselect child: " << item->text(0) << endl
<< "\tis package: " << package << endl;
if ( ! m_dictDocuments.contains( package ) )
return;
TQString texdocfile = m_dictDocuments[package];
KILE_DEBUG() << "\tis texdocfile: " << texdocfile << endl;
// search for the file in the documentation directories
TQString filename = searchFile(texdocfile,m_texmfdocPath);
if ( filename.isEmpty() )
{
// not found: search it elsewhere
filename = searchFile(texdocfile,m_texmfPath,"tex");
if ( filename.isEmpty() )
{
KMessageBox::error(this,i18n("Could not find '%1'").tqarg(filename));
return;
}
}
KILE_DEBUG() << "\tfound file: " << filename << endl;
TQString ext = TQFileInfo(filename).extension(false).lower();
m_filename = TQString();
if ( ext == "gz" )
decompressFile(m_dictDocuments[package],"gzip -cd "+filename);
else if ( ext == "bz2" )
decompressFile(m_dictDocuments[package],"bzip2 -cd "+filename);
else if ( ext=="sty" && m_dictStyleCodes.contains(package) )
showStyleFile(filename,m_dictStyleCodes[package]);
else
showFile(filename);
}
void TexDocDialog::slotTextChanged(const TQString &text)
{
m_pbSearch->setEnabled( ! text.stripWhiteSpace().isEmpty() );
}
void TexDocDialog::slotSearchClicked()
{
TQString keyword = m_leKeywords->text().stripWhiteSpace();
if ( keyword.isEmpty() )
{
KMessageBox::error(this,i18n("No keyword given."));
return;
}
TQString section;
bool writesection = true;
TQStringList searchlist;
for (uint i=0; i<m_tocList.count(); i++ )
{
if ( m_tocList[i][0] == '@' )
{
section = m_tocList[i];
writesection = true;
}
else if ( m_tocSearchList[i].find(keyword,0,false) > -1 )
{
if ( writesection )
searchlist.append(section);
searchlist.append(m_tocList[i]);
writesection = false;
}
}
if ( searchlist.count() > 0 )
{
m_texdocs->clear();
showToc(i18n("Search results for keyword '%1'").tqarg(keyword),searchlist,false);
}
else
KMessageBox::error(this,i18n("No documents found for keyword '%1'.").tqarg(keyword));
}
void TexDocDialog::slotHelp()
{
m_leKeywords->setText(TQString());
m_texdocs->clear();
showToc(i18n("Table of Contents"),m_tocList,true);
}
void TexDocDialog::callSearch()
{
if ( m_pbSearch->isEnabled() )
slotSearchClicked();
}
////////////////////// execute shell script //////////////////////
void TexDocDialog::executeScript(const TQString &command)
{
if ( m_proc )
delete m_proc;
m_proc = new KShellProcess("/bin/sh");
m_proc->clearArguments();
(*m_proc) << TQStringList::split(' ',command);
m_output = TQString();
connect(m_proc, TQT_SIGNAL(receivedStdout(KProcess*,char*,int)),
this, TQT_SLOT(slotProcessOutput(KProcess*,char*,int)) );
connect(m_proc, TQT_SIGNAL(receivedStderr(KProcess*,char*,int)),
this, TQT_SLOT(slotProcessOutput(KProcess*,char*,int)) );
connect(m_proc, TQT_SIGNAL(processExited(KProcess*)),
this, TQT_SLOT(slotProcessExited(KProcess*)) );
KILE_DEBUG() << "=== TexDocDialog::runShellSkript() ====================" << endl;
KILE_DEBUG() << " execute: " << command << endl;
if ( ! m_proc->start(KProcess::NotifyOnExit, KProcess::AllOutput) )
KILE_DEBUG() << "\tstart of shell process failed" << endl;
}
void TexDocDialog::slotProcessOutput(KProcess*,char* buf,int len)
{
m_output += TQString::fromLocal8Bit(buf,len);
}
void TexDocDialog::slotProcessExited(KProcess *proc)
{
if ( proc->normalExit() && !proc->exitStatus() )
{
//showFile(m_filename);
emit( processFinished() );
}
else
{
KMessageBox::error( this,i18n("<center>") + i18n("Could not determine the search paths of TexLive/teTeX or file 'texdoctk.dat'.<br> So this dialog is useless.") + i18n("</center>"),i18n("TexDoc Dialog") );
}
}
////////////////////// process slots, when finished //////////////////////
void TexDocDialog::slotInitToc()
{
disconnect(this, TQT_SIGNAL(processFinished()), this, TQT_SLOT(slotInitToc()));
TQStringList results = TQStringList::split('\n',m_output,true);
if ( results.count() < 3 )
{
KMessageBox::error(this,i18n("Could not determine the search paths of TexLive/teTeX or file 'texdoctk.dat'.<br> So this dialog is useless."));
return;
}
m_texdoctkPath = results[0];
m_texmfdocPath = results[1];
m_texmfPath = results[2];
KILE_DEBUG() << "\ttexdoctk path: " << m_texdoctkPath << endl;
KILE_DEBUG() << "\ttexmfdoc path: " << m_texmfdocPath << endl;
KILE_DEBUG() << "\ttexmf path: " << m_texmfPath << endl;
if ( m_texdoctkPath.find('\n',-1) > -1 )
{
m_texdoctkPath.truncate(m_texdoctkPath.length()-1);
}
// read data and initialize listview
readToc();
slotHelp();
}
void TexDocDialog::slotShowFile()
{
disconnect(this, TQT_SIGNAL(processFinished()), this, TQT_SLOT(slotShowFile()));
showFile(m_filename);
}
////////////////////// Icon/Mime //////////////////////
TQString TexDocDialog::getMimeType(const TQString &filename)
{
TQFileInfo fi(filename);
TQString basename = fi.baseName().lower();
TQString ext = fi.extension(false).lower();
TQString mimetype;
if ( ext=="txt" || ext=="faq" || ext=="sty" || basename=="readme" || basename=="00readme" )
{
mimetype = "text/plain";
}
else
{
KURL mimeurl;
mimeurl.setPath(filename);
KMimeType::Ptr pMime = KMimeType::findByURL(mimeurl);
mimetype = pMime->name();
}
KILE_DEBUG() << "\tmime = " << mimetype << " " << endl;
return mimetype;
}
TQString TexDocDialog::getIconName(const TQString &filename)
{
TQFileInfo fi( filename );
TQString basename = fi.baseName().lower();
TQString ext = fi.extension(false).lower();
TQString icon;
if ( ext=="dvi" || ext=="pdf" || ext=="html" || ext == "htm" || ext == "txt")
icon = ext;
else if ( ext == "ps" )
icon = "postscript";
else if ( ext == "sty" )
icon = "tex";
else if ( ext == "faq" || basename=="readme" || basename=="00readme" )
icon = "readme";
else
icon = "ascii";
return icon;
}
//END TexDocDialog
}
#include "texdocdialog.moc"