/*************************************************************************** plugin_katexmlcheck.cpp - checks XML files using xmllint ------------------- begin : 2002-07-06 copyright : (C) 2002 by Daniel Naber email : daniel.naber@t-online.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. 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; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ***************************************************************************/ /* -fixme: show dock if "Validate XML" is selected (doesn't currently work when Kate was just started and the dockwidget isn't yet visible) -fixme(?): doesn't correctly disappear when deactivated in config */ #include "plugin_katexmlcheck.h" #include "plugin_katexmlcheck.moc" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include K_EXPORT_COMPONENT_FACTORY( katexmlcheckplugin, KGenericFactory( "katexmlcheck" ) ) PluginKateXMLCheck::PluginKateXMLCheck( TQObject* parent, const char* name, const TQStringList& ) : Kate::Plugin ( (Kate::Application *)parent, name ) { } PluginKateXMLCheck::~PluginKateXMLCheck() { } void PluginKateXMLCheck::addView(Kate::MainWindow *win) { Kate::ToolViewManager *viewmanager = win->toolViewManager(); TQWidget *dock = viewmanager->createToolView("kate_plugin_xmlcheck_ouputview", Kate::ToolViewManager::Bottom, SmallIcon("application-vnd.tde.misc"), i18n("XML Checker Output")); PluginKateXMLCheckView *view = new PluginKateXMLCheckView (dock,win,"katexmlcheck_outputview"); view->dock = dock; win->guiFactory()->addClient(view); view->win = win; m_views.append(view); } void PluginKateXMLCheck::removeView(Kate::MainWindow *win) { for (uint z=0; z < m_views.count(); z++) { if (m_views.at(z)->win == win) { PluginKateXMLCheckView *view = m_views.at(z); m_views.remove (view); win->guiFactory()->removeClient (view); delete view->dock; // this will delete view, too } } } //--------------------------------- PluginKateXMLCheckView::PluginKateXMLCheckView(TQWidget *parent,Kate::MainWindow *mainwin,const char* name) :TQListView(parent,name),KXMLGUIClient(),win(mainwin) { m_tmp_file=0; m_proc=0; (void) new TDEAction ( i18n("Validate XML"), 0, TQT_TQOBJECT(this), TQT_SLOT( slotValidate() ), actionCollection(), "xml_check" ); // TODO?: //(void) new TDEAction ( i18n("Indent XML"), 0, this, // TQT_SLOT( slotIndent() ), actionCollection(), "xml_indent" ); setInstance(new TDEInstance("kate")); setXMLFile("plugins/katexmlcheck/ui.rc"); setFocusPolicy(TQ_NoFocus); addColumn(i18n("#"), -1); addColumn(i18n("Line"), -1); setColumnAlignment(1, AlignRight); addColumn(i18n("Column"), -1); setColumnAlignment(2, AlignRight); addColumn(i18n("Message"), -1); setAllColumnsShowFocus(true); setResizeMode(TQListView::LastColumn); connect(this, TQT_SIGNAL(clicked(TQListViewItem *)), TQT_SLOT(slotClicked(TQListViewItem *))); /* TODO?: invalidate the listview when document has changed Kate::View *kv = application()->activeMainWindow()->viewManager()->activeView(); if( ! kv ) { kdDebug() << "Warning: no Kate::View" << endl; return; } connect(kv, TQT_SIGNAL(modifiedChanged()), this, TQT_SLOT(slotUpdate())); */ m_proc_stderr = ""; m_proc = new TDEProcess(); connect(m_proc, TQT_SIGNAL(processExited(TDEProcess*)), this, TQT_SLOT(slotProcExited(TDEProcess*))); // we currently only want errors: //connect(m_proc, TQT_SIGNAL(receivedStdout(TDEProcess*,char*,int)), // this, TQT_SLOT(receivedProcStdout(TDEProcess*, char*, int))); connect(m_proc, TQT_SIGNAL(receivedStderr(TDEProcess*,char*,int)), this, TQT_SLOT(slotReceivedProcStderr(TDEProcess*, char*, int))); } PluginKateXMLCheckView::~PluginKateXMLCheckView() { delete m_proc; delete m_tmp_file; } void PluginKateXMLCheckView::slotReceivedProcStderr(TDEProcess *, char *result, int len) { m_proc_stderr += TQString::fromLocal8Bit( TQCString(result, len+1) ); } void PluginKateXMLCheckView::slotProcExited(TDEProcess*) { // FIXME: doesn't work correct the first time: //if( m_dockwidget->isDockBackPossible() ) { // m_dockwidget->dockBack(); // } kdDebug() << "slotProcExited()" << endl; //kdDebug() << "output: " << endl << m_proc_stderr << endl << endl; TQApplication::restoreOverrideCursor(); m_tmp_file->unlink(); clear(); uint list_count = 0; uint err_count = 0; if( ! m_validating ) { // no i18n here, so we don't get an ugly English<->Non-english mixup: TQString msg; if( m_dtdname.isEmpty() ) { msg = "No DOCTYPE found, will only check well-formedness."; } else { msg = "'" + m_dtdname + "' not found, will only check well-formedness."; } (void)new TQListViewItem(this, TQString("1").rightJustify(4,' '), "", "", msg); list_count++; } if( ! m_proc_stderr.isEmpty() ) { TQStringList lines = TQStringList::split("\n", m_proc_stderr); TQListViewItem *item = 0; TQString linenumber, msg; uint line_count = 0; for(TQStringList::Iterator it = lines.begin(); it != lines.end(); ++it) { TQString line = *it; line_count++; int semicolon_1 = line.find(':'); int semicolon_2 = line.find(':', semicolon_1+1); int semicolon_3 = line.find(':', semicolon_2+2); int caret_pos = line.find('^'); if( semicolon_1 != -1 && semicolon_2 != -1 && semicolon_3 != -1 ) { linenumber = line.mid(semicolon_1+1, semicolon_2-semicolon_1-1).stripWhiteSpace(); linenumber = linenumber.rightJustify(6, ' '); // for sorting numbers msg = line.mid(semicolon_3+1, line.length()-semicolon_3-1).stripWhiteSpace(); } else if( caret_pos != -1 || line_count == lines.size() ) { // TODO: this fails if "^" occurs in the real text?! if( line_count == lines.size() && caret_pos == -1 ) { msg = msg+"\n"+line; } TQString col = TQString::number(caret_pos); if( col == "-1" ) { col = ""; } err_count++; list_count++; item = new TQListViewItem(this, TQString::number(list_count).rightJustify(4,' '), linenumber, col, msg); item->setMultiLinesEnabled(true); } else { msg = msg+"\n"+line; } } sort(); // TODO?: insert in right order } if( err_count == 0 ) { TQString msg; if( m_validating ) { msg = "No errors found, document is valid."; // no i18n here } else { msg = "No errors found, document is well-formed."; // no i18n here } (void)new TQListViewItem(this, TQString::number(list_count+1).rightJustify(4,' '), "", "", msg); } } void PluginKateXMLCheckView::slotClicked(TQListViewItem *item) { kdDebug() << "slotClicked" << endl; if( item ) { bool ok = true; uint line = item->text(1).toUInt(&ok); bool ok2 = true; uint column = item->text(2).toUInt(&ok); if( ok && ok2 ) { Kate::View *kv = win->viewManager()->activeView(); if( ! kv ) { kdDebug() << "Warning (slotClicked()): no Kate::View" << endl; return; } kv->setCursorPositionReal(line-1, column); } } } void PluginKateXMLCheckView::slotUpdate() { kdDebug() << "slotUpdate() (not implemented yet)" << endl; } bool PluginKateXMLCheckView::slotValidate() { kdDebug() << "slotValidate()" << endl; win->toolViewManager()->showToolView (this); m_proc->clearArguments(); m_proc_stderr = ""; m_validating = false; m_dtdname = ""; Kate::View *kv = win->viewManager()->activeView(); if( ! kv ) { kdDebug() << "Error (slotValidate()): no Kate::View" << endl; return false; } if( ! kv->getDoc() ) { kdDebug() << "Error (slotValidate()): no kv->getDoc()" << endl; return false; } Kate::Document *doc = (Kate::Document*)kv->document(); m_tmp_file = new KTempFile(); if( m_tmp_file->status() != 0 ) { kdDebug() << "Error (slotValidate()): could not create '" << m_tmp_file->name() << "': " << m_tmp_file->status() << endl; KMessageBox::error(0, i18n("Error: Could not create " "temporary file '%1'.").arg(m_tmp_file->name())); delete m_tmp_file; m_tmp_file=0L; return false; } TQTextStream *s = m_tmp_file->textStream(); *s << kv->getDoc()->text(); bool removed = m_tmp_file->close(); if( ! removed ) { kdDebug() << "Warning (slotValidate()): temp file '" << m_tmp_file->name() << "' not deleted: " << m_tmp_file->status() << endl; } TQString exe = TDEStandardDirs::findExe("xmllint"); if( exe.isEmpty() ) { exe = locate("exe", "xmllint"); } // use catalogs for KDE docbook: if( ! getenv("SGML_CATALOG_FILES") ) { TDEInstance ins("katexmlcheckplugin"); TQString catalogs; catalogs += ins.dirs()->findResource("data", "ksgmltools2/customization/catalog"); catalogs += ":"; catalogs += ins.dirs()->findResource("data", "ksgmltools2/docbook/xml-dtd-4.1.2/docbook.cat"); kdDebug() << "catalogs: " << catalogs << endl; setenv("SGML_CATALOG_FILES", TQFile::encodeName( catalogs ).data(), 1); } //kdDebug() << "**catalogs: " << getenv("SGML_CATALOG_FILES") << endl; *m_proc << exe << "--catalogs" << "--noout"; // heuristic: assume that the doctype is in the first 10,000 bytes: TQString text_start = kv->getDoc()->text().left(10000); // remove comments before looking for doctype (as a doctype might be commented out // and needs to be ignored then): TQRegExp re(""); re.setMinimal(true); text_start.replace(re, ""); TQRegExp re_doctype("name(); if( ! m_proc->start(TDEProcess::NotifyOnExit, TDEProcess::AllOutput) ) { KMessageBox::error(0, i18n("Error: Failed to execute xmllint. Please make " "sure that xmllint is installed. It is part of libxml2.")); return false; } TQApplication::setOverrideCursor(KCursor::waitCursor()); return true; }