diff options
Diffstat (limited to 'kdeprint/kxmlcommand.cpp')
-rw-r--r-- | kdeprint/kxmlcommand.cpp | 830 |
1 files changed, 830 insertions, 0 deletions
diff --git a/kdeprint/kxmlcommand.cpp b/kdeprint/kxmlcommand.cpp new file mode 100644 index 000000000..c937e3443 --- /dev/null +++ b/kdeprint/kxmlcommand.cpp @@ -0,0 +1,830 @@ +/* + * This file is part of the KDE libraries + * Copyright (c) 2001 Michael Goffioul <kdeprint@swing.be> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License version 2 as published by the Free Software Foundation. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + **/ + +#include "kxmlcommand.h" +#include "driver.h" +#include "kmfactory.h" +#include "kdeprintcheck.h" +#include "driverview.h" + +#include <qfile.h> +#include <qregexp.h> +#include <qdir.h> +#include <qinputdialog.h> +#include <qmap.h> +#include <qvaluelist.h> +#include <kstandarddirs.h> +#include <klocale.h> +#include <ksimpleconfig.h> +#include <kdialogbase.h> +#include <kdebug.h> +#include <kprocess.h> +#include <klibloader.h> +#include <kmessagebox.h> + +static void setOptionText(DrBase *opt, const QString& s) +{ + if (s.isEmpty()) + opt->set("text", opt->name()); + else + opt->set("text", i18n(s.utf8())); +} + +class KXmlCommand::KXmlCommandPrivate +{ +public: + QString m_name; + QString m_command; + DrMain *m_driver; + struct + { + QString m_format[2]; // 0 -> file, 1 -> pipe + } m_io[2]; // 0 -> input, 1 -> output + QString m_description; + QString m_outputMime; + QStringList m_inputMime; + QStringList m_requirements; + bool m_loaded[2]; // 0 -> Desktop, 1 -> XML + QString m_comment; +}; + +KXmlCommand::KXmlCommand(const QString& xmlId) +: QObject(KXmlCommandManager::self(), "XmlCommand") +{ + init(); + d->m_name = xmlId; +} + +KXmlCommand::~KXmlCommand() +{ + //kdDebug(500) << "deleting driver" << endl; + delete d->m_driver; + //kdDebug(500) << "deleting private data" << endl; + delete d; + //kdDebug(500) << "finished" << endl; +} + +void KXmlCommand::init() +{ + d = new KXmlCommandPrivate; + d->m_driver = 0; + d->m_loaded[0] = d->m_loaded[1] = false; +} + +QString KXmlCommand::name() const +{ return d->m_name; } + +void KXmlCommand::setName(const QString& s) +{ d->m_name = s; } + +QString KXmlCommand::command() +{ + check(true); + return d->m_command; +} + +void KXmlCommand::setCommand(const QString& s) +{ + d->m_command = s; +} + +DrMain* KXmlCommand::driver() +{ + check(true); + return d->m_driver; +} + +DrMain* KXmlCommand::takeDriver() +{ + check(true); + DrMain *dr = d->m_driver; + d->m_driver = 0; + d->m_loaded[1] = false; + return dr; +} + +void KXmlCommand::setDriver(DrMain *driver) +{ + delete d->m_driver; + d->m_driver = driver; +} + +QString KXmlCommand::io(bool io_input, bool io_pipe) +{ + check(true); + return d->m_io[(io_input?0:1)].m_format[(io_pipe?1:0)]; +} + +void KXmlCommand::setIo(const QString& s, bool io_input, bool io_pipe) +{ + d->m_io[(io_input?0:1)].m_format[(io_pipe?1:0)] = s; +} + +QString KXmlCommand::description() +{ + check(); + return d->m_description; +} + +void KXmlCommand::setDescription(const QString& s) +{ + d->m_description = s; +} + +QString KXmlCommand::mimeType() +{ + check(); + return d->m_outputMime; +} + +void KXmlCommand::setMimeType(const QString& s) +{ + d->m_outputMime = s; +} + +bool KXmlCommand::acceptMimeType(const QString& s) +{ + check(); + return (d->m_inputMime.find(s) != d->m_inputMime.end()); +} + +QStringList KXmlCommand::inputMimeTypes() +{ + check(); + return d->m_inputMime; +} + +void KXmlCommand::setInputMimeTypes(const QStringList& l) +{ + d->m_inputMime = l; +} + +QStringList KXmlCommand::requirements() +{ + check(); + return d->m_requirements; +} + +void KXmlCommand::setRequirements(const QStringList& l) +{ + d->m_requirements = l; +} + +QString KXmlCommand::comment() +{ + check(); + return d->m_comment; +} + +void KXmlCommand::setComment( const QString& s ) +{ + d->m_comment = s; +} + +bool KXmlCommand::isValid() +{ + return (!locate("data", "kdeprint/filters/"+name()+".desktop").isEmpty()); +} + +void KXmlCommand::check(bool use_xml) +{ + if (!d->m_loaded[0]) + { + loadDesktop(); + d->m_loaded[0] = true; + } + if (use_xml && !d->m_loaded[1]) + { + loadXml(); + d->m_loaded[1] = true; + } +} + +void KXmlCommand::loadDesktop() +{ + KSimpleConfig conf(locate("data", "kdeprint/filters/"+name()+".desktop")); + conf.setGroup("KDE Print Filter Entry"); + d->m_description = conf.readEntry("Comment"); + d->m_outputMime = conf.readEntry("MimeTypeOut"); + d->m_inputMime = conf.readListEntry("MimeTypeIn"); + d->m_requirements = conf.readListEntry("Require"); + d->m_comment = conf.readEntry( "Description" ); +} + +void KXmlCommand::saveDesktop() +{ + KSimpleConfig conf(locateLocal("data", "kdeprint/filters/"+name()+".desktop")); + conf.setGroup("KDE Print Filter Entry"); + conf.writeEntry("Comment", d->m_description); + conf.writeEntry("MimeTypeIn", d->m_inputMime); + conf.writeEntry("MimeTypeOut", d->m_outputMime); + conf.writeEntry("Require", d->m_requirements); + conf.writeEntry( "Description", d->m_comment ); +} + +void KXmlCommand::loadXml() +{ + QFile f(locate("data", "kdeprint/filters/"+name()+".xml")); + QDomDocument doc; + if (f.open(IO_ReadOnly) && doc.setContent(&f) && doc.documentElement().tagName() == "kprintfilter") + { + QDomElement e, docElem = doc.documentElement(); + d->m_name = docElem.attribute("name"); + + // command + e = docElem.namedItem("filtercommand").toElement(); + if (!e.isNull()) + d->m_command = e.attribute("data"); + + // arguments + e = docElem.namedItem("filterargs").toElement(); + if (!e.isNull()) + { + d->m_driver = new DrMain; + d->m_driver->setName(d->m_name); + parseGroup(e, d->m_driver); + setOptionText(d->m_driver, d->m_description); + } + + // input/output + e = docElem.namedItem("filterinput").toElement(); + if (!e.isNull()) + parseIO(e, 0); + e = docElem.namedItem("filteroutput").toElement(); + if (!e.isNull()) + parseIO(e, 1); + } +} + +void KXmlCommand::parseIO(const QDomElement& e, int n) +{ + QDomElement elem = e.firstChild().toElement(); + while (!elem.isNull()) + { + if (elem.tagName() == "filterarg") + { + int format = (elem.attribute("name") == "file" ? 0 : 1); + d->m_io[n].m_format[format] = elem.attribute("format"); + } + elem = elem.nextSibling().toElement(); + } +} + +DrGroup* KXmlCommand::parseGroup(const QDomElement& e, DrGroup *grp) +{ + if (!grp) + grp = new DrGroup; + grp->setName(e.attribute("name")); + setOptionText(grp, e.attribute("description")); + + QDomElement elem = e.firstChild().toElement(); + while (!elem.isNull()) + { + if (elem.tagName() == "filterarg") + { + DrBase *opt = parseArgument(elem); + if (opt) + grp->addOption(opt); + } + else if (elem.tagName() == "filtergroup") + { + DrGroup *group = parseGroup(elem, 0); + if (group) + grp->addGroup(group); + } + elem = elem.nextSibling().toElement(); + } + + return grp; +} + +DrBase* KXmlCommand::parseArgument(const QDomElement& e) +{ + DrBase *opt(0); + QString type = e.attribute("type"); + + if (type == "int" || type == "float") + { + if (type == "int") + opt = new DrIntegerOption; + else + opt = new DrFloatOption; + opt->set("minval", e.attribute("min")); + opt->set("maxval", e.attribute("max")); + } + else if (type == "string") + opt = new DrStringOption; + else if (type == "list" || type == "bool") + { + if (type == "list") + opt = new DrListOption; + else + opt = new DrBooleanOption; + DrListOption *lopt = static_cast<DrListOption*>(opt); + QDomElement elem = e.firstChild().toElement(); + while (!elem.isNull()) + { + if (elem.tagName() == "value") + { + DrBase *choice = new DrBase; + choice->setName(elem.attribute("name")); + setOptionText(choice, elem.attribute("description")); + lopt->addChoice(choice); + } + elem = elem.nextSibling().toElement(); + } + } + else + return 0; + + opt->setName("_kde-" + d->m_name + "-" + e.attribute("name")); + setOptionText(opt, e.attribute("description")); + opt->set("format", e.attribute("format")); + opt->set("default", e.attribute("default")); + opt->set( "persistent", e.attribute( "persistent" ) ); + opt->setValueText(opt->get("default")); + + return opt; +} + +QString KXmlCommand::buildCommand(const QMap<QString,QString>& opts, bool pipein, bool pipeout) +{ + check(true); + + QString str, cmd = d->m_command; + QString re( "%value" ), quotedRe( "'%value'" ); + + if (d->m_driver) + { + QMap<QString,QString> fopts; + + d->m_driver->setOptions(opts); + d->m_driver->getOptions(fopts, false); + for (QMap<QString,QString>::ConstIterator it=fopts.begin(); it!=fopts.end(); ++it) + { + DrBase *dopt = d->m_driver->findOption(it.key()); + if (dopt) + { + QString format = dopt->get("format"); + QString value = dopt->valueText(); + if ( format.find( quotedRe ) != -1 ) + { + if ( ( value.right( 1 ) == "'" && value.left( 1 ) == "'" ) || + ( value.right( 1 ) == "\"" && value.left( 1 ) == "\"" ) ) + format.replace( quotedRe, value ); + else + format.replace( re, value ); + } + else + { + format.replace( re, KProcess::quote( dopt->valueText() ) ); + } + str.append(format).append(" "); + } + } + cmd.replace("%filterargs", str); + } + + cmd.replace("%filterinput", d->m_io[0].m_format[(pipein?1:0)]); + cmd.replace("%filteroutput", d->m_io[1].m_format[(pipeout?1:0)]); + + return cmd; +} + +void KXmlCommand::setOptions(const QMap<QString,QString>& opts) +{ + if (opts.count() == 0) + return; + // force loading the driver if needed + if (driver()) + d->m_driver->setOptions(opts); +} + +void KXmlCommand::getOptions(QMap<QString,QString>& opts, bool incldef) +{ + // force loading the driver + if (driver()) + d->m_driver->getOptions(opts, incldef); +} + +void KXmlCommand::saveXml() +{ + QFile f(locateLocal("data", "kdeprint/filters/"+name()+".xml")); + if (!f.open(IO_WriteOnly)) + return; + + QDomDocument doc("kprintfilter"); + QDomElement root = doc.createElement("kprintfilter"), elem; + + root.setAttribute("name", d->m_name); + doc.appendChild(root); + + // command + elem = doc.createElement("filtercommand"); + elem.setAttribute("data", d->m_command); + root.appendChild(elem); + + // options + if (d->m_driver) + { + elem = createGroup(doc, d->m_driver); + elem.setTagName("filterargs"); + root.appendChild(elem); + } + + // IO + if (!(elem=createIO(doc, 0, "filterinput")).isNull()) + root.appendChild(elem); + if (!(elem=createIO(doc, 1, "filteroutput")).isNull()) + root.appendChild(elem); + + // save to file (and close it) + QTextStream t(&f); + t << doc.toString(); + f.close(); +} + +QDomElement KXmlCommand::createIO(QDomDocument& doc, int n, const QString& tag) +{ + QDomElement elem = doc.createElement(tag); + if (d->m_command.find("%"+tag) != -1) + { + for (int i=0; i<2; i++) + { + QDomElement io = doc.createElement("filterarg"); + io.setAttribute("name", (i ? "pipe" : "file")); + io.setAttribute("format", d->m_io[n].m_format[i]); + elem.appendChild(io); + } + } + + return elem; +} + +QDomElement KXmlCommand::createGroup(QDomDocument& doc, DrGroup *group) +{ + QDomElement elem = doc.createElement("filtergroup"); + elem.setAttribute("name", group->name()); + elem.setAttribute("description", group->get("text")); + + QPtrListIterator<DrGroup> git(group->groups()); + for (; git.current(); ++git) + elem.appendChild(createGroup(doc, git.current())); + + QPtrListIterator<DrBase> oit(group->options()); + for (; oit.current(); ++oit) + elem.appendChild(createElement(doc, oit.current())); + + return elem; +} + +QDomElement KXmlCommand::createElement(QDomDocument& doc, DrBase *opt) +{ + QDomElement elem = doc.createElement("filterarg"); + QString elemName = opt->name(); + if (elemName.startsWith("_kde-")) + elemName.replace(0, name().length()+6, ""); + elem.setAttribute("name", elemName); + elem.setAttribute("format", opt->get("format")); + elem.setAttribute("description", opt->get("text")); + elem.setAttribute("default", opt->get("default")); + elem.setAttribute( "persistent", opt->get( "persistent" ) == "1" ? "1" : "0"); + + switch (opt->type()) + { + case DrBase::String: + elem.setAttribute("type", "string"); + break; + case DrBase::Integer: + case DrBase::Float: + elem.setAttribute("type", (opt->type() == DrBase::Integer ? "int" : "float")); + elem.setAttribute("min", opt->get("minval")); + elem.setAttribute("max", opt->get("maxval")); + break; + case DrBase::Boolean: + case DrBase::List: + elem.setAttribute("type", (opt->type() == DrBase::List ? "list" : "bool")); + { + QPtrListIterator<DrBase> it(*(static_cast<DrListOption*>(opt)->choices())); + for (; it.current(); ++it) + { + QDomElement chElem = doc.createElement("value"); + chElem.setAttribute("name", it.current()->name()); + chElem.setAttribute("description", it.current()->get("text")); + elem.appendChild(chElem); + } + } + break; + default: + break; + } + + return elem; +} + +//--------------------------------------------------------------------------------------------------- + +class KXmlCommandManager::KXmlCommandManagerPrivate +{ +public: + QStringList m_cmdlist; + QMap<QString, QValueList<KXmlCommand*> > m_mimemap; + QMap<QString, KXmlCommand*> m_cmdmap; +}; + +KXmlCommandManager* KXmlCommandManager::m_self = 0; + +KXmlCommandManager* KXmlCommandManager::self() +{ + if (!m_self) + { + m_self = new KXmlCommandManager; + Q_CHECK_PTR(m_self); + } + return m_self; +} + +KXmlCommandManager::KXmlCommandManager() +: QObject(KMFactory::self(), "XmlCommandManager") +{ + d = new KXmlCommandManagerPrivate; +} + +KXmlCommandManager::~KXmlCommandManager() +{ + cleanUp(); + delete d; +} + +KXmlCommand* KXmlCommandManager::loadCommand(const QString& xmlId, bool check) +{ + if (check) + { + QString desktopFile = locate("data", "kdeprint/filters/"+xmlId+".desktop"); + if (desktopFile.isEmpty()) + return 0; + } + return new KXmlCommand(xmlId); +} + +void KXmlCommandManager::saveCommand(KXmlCommand *xmlCmd) +{ + xmlCmd->saveDesktop(); + xmlCmd->saveXml(); + + cleanUp(); +} + +void KXmlCommandManager::cleanUp() +{ + for (QMap<QString, KXmlCommand*>::ConstIterator it=d->m_cmdmap.begin(); it!=d->m_cmdmap.end(); ++it) + delete (*it); + d->m_cmdmap.clear(); + d->m_mimemap.clear(); + d->m_cmdlist.clear(); +} + +void KXmlCommandManager::preload() +{ + if (d->m_cmdmap.count() == 0) + { + commandList(); + for (QStringList::Iterator it=d->m_cmdlist.begin(); it!=d->m_cmdlist.end(); ++it) + { + KXmlCommand *xmlCmd = loadCommand(*it); + if (!xmlCmd) continue; // Error! + + QStringList inputMime = xmlCmd->inputMimeTypes(); + for (QStringList::ConstIterator mime=inputMime.begin(); mime!=inputMime.end(); ++mime) + { + d->m_mimemap[*mime].append(xmlCmd); + d->m_cmdmap[*it] = xmlCmd; + } + } + } +} + +QStringList KXmlCommandManager::commandList() +{ + if (d->m_cmdlist.isEmpty()) + { + QStringList dirs = KGlobal::dirs()->findDirs("data", "kdeprint/filters/"); + + for (QStringList::ConstIterator it=dirs.begin(); it!=dirs.end(); ++it) + { + QStringList list = QDir(*it).entryList("*.desktop", QDir::Files, QDir::Unsorted); + for (QStringList::ConstIterator it2=list.begin(); it2!=list.end(); ++it2) + { + QString cmdName = (*it2).left((*it2).length()-8); + if (d->m_cmdlist.find(cmdName) == d->m_cmdlist.end()) + d->m_cmdlist.append(cmdName); + } + } + + d->m_cmdlist.sort(); + } + + return d->m_cmdlist; +} + +QStringList KXmlCommandManager::commandListWithDescription() +{ + preload(); + QStringList l; + for (QMap<QString,KXmlCommand*>::ConstIterator it=d->m_cmdmap.begin(); it!=d->m_cmdmap.end(); ++it) + l << (*it)->name() << (*it)->description(); + + return l; +} + +QString KXmlCommandManager::selectCommand(QWidget *parent) +{ + KLibrary *lib = KLibLoader::self()->library( "libkdeprint_management_module" ); + if ( !lib ) + { + KMessageBox::error( parent, i18n( "Unable to load KDE print management library: %1" ).arg( KLibLoader::self()->lastErrorMessage() ) ); + return QString::null; + } + else + { + QString ( *func )( QWidget* ) = ( QString( * )( QWidget* ) )lib->symbol( "select_command" ); + if ( !func ) + { + KMessageBox::error( parent, i18n( "Unable to find wizard object in management library." ) ); + return QString::null; + } + else + return func( parent ); + } +} + +KXmlCommand* KXmlCommandManager::command(const QString& xmlId) const +{ + return (d->m_cmdmap.contains(xmlId) ? d->m_cmdmap[xmlId] : 0); +} + +int KXmlCommandManager::insertCommand(QStringList& list, const QString& filtername, bool defaultToStart) +{ + preload(); + + int pos(0); + KXmlCommand *f1 = command(filtername), *f2 = 0; + if (f1 && f1->inputMimeTypes().count() > 0) + { + QString mimetype = f1->inputMimeTypes()[0]; + for (QStringList::Iterator it=list.begin(); it!=list.end(); ++it, pos++) + { + f2 = command(*it); + if (!f2) + return -1; // Shouldn't happen + + if (f2->acceptMimeType(f1->mimeType()) && f1->acceptMimeType(mimetype)) + { + list.insert(it, filtername); + break; + } + else + { + mimetype = f2->mimeType(); + f2 = 0; + } + } + if (pos == (int)(list.count())) + { + if (list.count() == 0 || f1->acceptMimeType(mimetype)) + list.append(filtername); + else if (defaultToStart) + { + pos = 0; + list.prepend(filtername); + } + else + pos = -1; + } + } + return pos; +} + +QStringList KXmlCommandManager::autoConvert(const QString& mimesrc, const QString& mimedest) +{ + QStringList chain; + uint score(0); + + preload(); + + if (d->m_mimemap.contains(mimesrc)) + { + const QValueList<KXmlCommand*> l = d->m_mimemap[mimesrc]; + for (QValueList<KXmlCommand*>::ConstIterator it=l.begin(); it!=l.end(); ++it) + { + // check filter availability + if (!KdeprintChecker::check((*it)->requirements())) + continue; + + // direct filter: shortest path => return immediately + if ((*it)->mimeType() == mimedest) + { + chain = QStringList((*it)->name()); + break; + } + // non direct filter: find the shortest way between + // its output and mimedest (do not consider cyling filters) + else if ((*it)->mimeType() != mimesrc) + { + QStringList subchain = autoConvert((*it)->mimeType(), mimedest); + // If chain length is 0, then there's no possible filter between those 2 + // mime types. Just discard it. If the subchain contains also the current + // considered filter, then discard it: it denotes of a cycle in filter + // chain. + if (subchain.count() > 0 && subchain.findIndex((*it)->name()) == -1) + { + subchain.prepend((*it)->name()); + if (subchain.count() < score || score == 0) + { + chain = subchain; + score = subchain.count(); + } + } + } + } + } + // At this point, either we have the shortest path, or empty + // list if nothing could be found + return chain; +} + +bool KXmlCommandManager::checkCommand(const QString& xmlId, int inputCheck, int outputCheck, QString *msg) +{ + KXmlCommand *xmlCmd = command(xmlId); + QString errmsg; + bool needDestroy(false); + //kdDebug(500) << "checking command: " << xmlId << " (" << (xmlCmd != NULL) << ")" << endl; + if (!xmlCmd) + { + xmlCmd = loadCommand(xmlId, true); + needDestroy = (xmlCmd != 0); + } + + bool status(true); + if (xmlCmd) + { + status = (xmlCmd->isValid() && KdeprintChecker::check(xmlCmd->requirements())); + if (!status) + errmsg = i18n("One of the command object's requirements is not met."); + } + QString cmd = (xmlCmd ? xmlCmd->command() : xmlId); + if (status && !cmd.isEmpty() && (inputCheck > None || outputCheck > None)) + { + if (inputCheck > None && (cmd.find("%in") == -1 || inputCheck == Advanced) && cmd.find("%filterinput") == -1) + { + status = false; + errmsg = i18n("The command does not contain the required tag %1.").arg(inputCheck == Advanced ? "%filterinput" : "{%in,%filterinput}"); + } + if (status && outputCheck > None && (cmd.find("%out") == -1 || outputCheck == Advanced) && cmd.find("filteroutput") == -1) + { + status = false; + errmsg = i18n("The command does not contain the required tag %1.").arg(outputCheck == Advanced ? "%filteroutput" : "{%out,%filteroutput}"); + } + } + + if (needDestroy) + delete xmlCmd; + + if (msg) + *msg = errmsg; + + return status; +} + +bool KXmlCommandManager::configure(KXmlCommand *xmlCmd, QWidget *parent) +{ + if (xmlCmd->driver()) + { + KDialogBase dlg(parent, 0, true, xmlCmd->description(), KDialogBase::Ok); + DriverView view(&dlg); + + dlg.setMainWidget(&view); + view.setDriver(xmlCmd->driver()); + dlg.resize(350,400); + dlg.exec(); + + return true; + } + return false; +} |