/*************************************************************************** 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kiledebug.h" #include #include #include namespace KileDialog { //BEGIN TexDocDialog TexDocDialog::TexDocDialog(TQWidget *tqparent, const char *name) : KDialogBase( tqparent,name, true, i18n("Documentation Browser"), Close | Help, #if KDE_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; isetPixmap(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].tqfind(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.tqfind("%%%%") == 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.tqfind("\\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->tqparent() ) return; TQString package = item->text(1); KILE_DEBUG() << "\tselect child: " << item->text(0) << endl << "\tis package: " << package << endl; if ( ! m_dictDocuments.tqcontains( 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 tqfind '%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.tqcontains(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 -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("
") + i18n("Could not determine the search paths of TexLive/teTeX or file 'texdoctk.dat'.
So this dialog is useless.") + i18n("
"),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'.
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.tqfind('\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"