diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-03-01 19:17:32 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-03-01 19:17:32 +0000 |
commit | e38d2351b83fa65c66ccde443777647ef5cb6cff (patch) | |
tree | 1897fc20e9f73a81c520a5b9f76f8ed042124883 /src/translators/htmlexporter.cpp | |
download | tellico-e38d2351b83fa65c66ccde443777647ef5cb6cff.tar.gz tellico-e38d2351b83fa65c66ccde443777647ef5cb6cff.zip |
Added KDE3 version of Tellico
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/tellico@1097620 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'src/translators/htmlexporter.cpp')
-rw-r--r-- | src/translators/htmlexporter.cpp | 815 |
1 files changed, 815 insertions, 0 deletions
diff --git a/src/translators/htmlexporter.cpp b/src/translators/htmlexporter.cpp new file mode 100644 index 0000000..e947793 --- /dev/null +++ b/src/translators/htmlexporter.cpp @@ -0,0 +1,815 @@ +/*************************************************************************** + copyright : (C) 2003-2006 by Robby Stephenson + email : robby@periapsis.org + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#include "htmlexporter.h" +#include "xslthandler.h" +#include "tellicoxmlexporter.h" +#include "../document.h" +#include "../collection.h" +#include "../filehandler.h" +#include "../imagefactory.h" +#include "../latin1literal.h" +#include "../tellico_kernel.h" +#include "../tellico_utils.h" +#include "../progressmanager.h" +#include "../core/tellico_config.h" +#include "../tellico_debug.h" + +#include <kstandarddirs.h> +#include <kconfig.h> +#include <kglobal.h> +#include <kio/netaccess.h> +#include <kapplication.h> +#include <klocale.h> + +#include <qdom.h> +#include <qgroupbox.h> +#include <qlayout.h> +#include <qcheckbox.h> +#include <qwhatsthis.h> +#include <qfile.h> +#include <qhbox.h> +#include <qlabel.h> + +extern "C" { +#include <libxml/HTMLparser.h> +#include <libxml/HTMLtree.h> +} + +using Tellico::Export::HTMLExporter; + +HTMLExporter::HTMLExporter() : Tellico::Export::Exporter(), + m_handler(0), + m_printHeaders(true), + m_printGrouped(false), + m_exportEntryFiles(false), + m_cancelled(false), + m_parseDOM(true), + m_checkCreateDir(true), + m_imageWidth(0), + m_imageHeight(0), + m_widget(0), + m_xsltFile(QString::fromLatin1("tellico2html.xsl")) { +} + +HTMLExporter::HTMLExporter(Data::CollPtr coll_) : Tellico::Export::Exporter(coll_), + m_handler(0), + m_printHeaders(true), + m_printGrouped(false), + m_exportEntryFiles(false), + m_cancelled(false), + m_parseDOM(true), + m_checkCreateDir(true), + m_imageWidth(0), + m_imageHeight(0), + m_widget(0), + m_xsltFile(QString::fromLatin1("tellico2html.xsl")) { +} + +HTMLExporter::~HTMLExporter() { + delete m_handler; + m_handler = 0; +} + +QString HTMLExporter::formatString() const { + return i18n("HTML"); +} + +QString HTMLExporter::fileFilter() const { + return i18n("*.html|HTML Files (*.html)") + QChar('\n') + i18n("*|All Files"); +} + +void HTMLExporter::reset() { + // since the ExportUTF8 option may have changed, need to delete handler + delete m_handler; + m_handler = 0; + m_files.clear(); + m_links.clear(); + m_copiedFiles.clear(); +} + +bool HTMLExporter::exec() { + if(url().isEmpty() || !url().isValid()) { + kdWarning() << "HTMLExporter::exec() - trying to export to invalid URL" << endl; + return false; + } + + // check file exists first + // if we're not forcing, ask use + bool force = (options() & Export::ExportForce) || FileHandler::queryExists(url()); + if(!force) { + return false; + } + + if(!m_parseDOM) { + return FileHandler::writeTextURL(url(), text(), options() & Export::ExportUTF8, force); + } + + m_cancelled = false; + // TODO: maybe need label? + if(options() & ExportProgress) { + ProgressItem& item = ProgressManager::self()->newProgressItem(this, QString::null, true); + item.setTotalSteps(100); + connect(&item, SIGNAL(signalCancelled(ProgressItem*)), SLOT(slotCancel())); + } + // ok if not ExportProgress, no worries + ProgressItem::Done done(this); + + htmlDocPtr htmlDoc = htmlParseDoc(reinterpret_cast<xmlChar*>(text().utf8().data()), NULL); + xmlNodePtr root = xmlDocGetRootElement(htmlDoc); + if(root == 0) { + myDebug() << "HTMLExporter::exec() - no root" << endl; + return false; + } + parseDOM(root); + + if(m_cancelled) { + return true; // intentionally cancelled + } + ProgressManager::self()->setProgress(this, 15); + + xmlChar* c; + int bytes; + htmlDocDumpMemory(htmlDoc, &c, &bytes); + QString allText; + if(bytes > 0) { + allText = QString::fromUtf8(reinterpret_cast<const char*>(c), bytes); + xmlFree(c); + } + + if(m_cancelled) { + return true; // intentionally cancelled + } + ProgressManager::self()->setProgress(this, 20); + + bool success = FileHandler::writeTextURL(url(), allText, options() & Export::ExportUTF8, force); + success &= copyFiles() && (!m_exportEntryFiles || writeEntryFiles()); + return success; +} + +bool HTMLExporter::loadXSLTFile() { + QString xsltfile = locate("appdata", m_xsltFile); + if(xsltfile.isNull()) { + myDebug() << "HTMLExporter::loadXSLTFile() - no xslt file for " << m_xsltFile << endl; + return false; + } + + KURL u; + u.setPath(xsltfile); + // do NOT do namespace processing, it messes up the XSL declaration since + // QDom thinks there are no elements in the Tellico namespace and as a result + // removes the namespace declaration + QDomDocument dom = FileHandler::readXMLFile(u, false); + if(dom.isNull()) { + myDebug() << "HTMLExporter::loadXSLTFile() - error loading xslt file: " << xsltfile << endl; + return false; + } + + // notes about utf-8 encoding: + // all params should be passed to XSLTHandler in utf8 + // input string to XSLTHandler should be in utf-8, EVEN IF DOM STRING SAYS OTHERWISE + + // the stylesheet prints utf-8 by default, if using locale encoding, need + // to change the encoding attribute on the xsl:output element + if(!(options() & Export::ExportUTF8)) { + XSLTHandler::setLocaleEncoding(dom); + } + + delete m_handler; + m_handler = new XSLTHandler(dom, QFile::encodeName(xsltfile), true /*translate*/); + if(!m_handler->isValid()) { + delete m_handler; + m_handler = 0; + return false; + } + + if(m_exportEntryFiles) { + // export entries to same place as all the other date files + m_handler->addStringParam("entrydir", QFile::encodeName(fileDir().fileName())+ '/'); + // be sure to link all the entries + m_handler->addParam("link-entries", "true()"); + } + + if(!m_collectionURL.isEmpty()) { + QString s = QString::fromLatin1("../") + m_collectionURL.fileName(); + m_handler->addStringParam("collection-file", s.utf8()); + } + + // look for a file that gets installed to know the installation directory + // if parseDOM, that means we want the locations to be the actual location + // otherwise, we assume it'll be relative + if(m_parseDOM && m_dataDir.isEmpty()) { + m_dataDir = KGlobal::dirs()->findResourceDir("appdata", QString::fromLatin1("pics/tellico.png")); + } else if(!m_parseDOM) { + m_dataDir.truncate(0); + } + if(!m_dataDir.isEmpty()) { + m_handler->addStringParam("datadir", QFile::encodeName(m_dataDir)); + } + + setFormattingOptions(collection()); + + return m_handler->isValid(); +} + +QString HTMLExporter::text() { + if((!m_handler || !m_handler->isValid()) && !loadXSLTFile()) { + kdWarning() << "HTMLExporter::text() - error loading xslt file: " << m_xsltFile << endl; + return QString::null; + } + + Data::CollPtr coll = collection(); + if(!coll) { + myDebug() << "HTMLExporter::text() - no collection pointer!" << endl; + return QString::null; + } + + if(m_groupBy.isEmpty()) { + m_printGrouped = false; // can't group if no groups exist + } + + GUI::CursorSaver cs; + writeImages(coll); + + // now grab the XML + TellicoXMLExporter exporter(coll); + exporter.setURL(url()); + exporter.setEntries(entries()); + exporter.setIncludeGroups(m_printGrouped); +// yes, this should be in utf8, always + exporter.setOptions(options() | Export::ExportUTF8 | Export::ExportImages); + QDomDocument output = exporter.exportXML(); +#if 0 + QFile f(QString::fromLatin1("/tmp/test.xml")); + if(f.open(IO_WriteOnly)) { + QTextStream t(&f); + t << output.toString(); + } + f.close(); +#endif + + QString text = m_handler->applyStylesheet(output.toString()); +#if 0 + QFile f2(QString::fromLatin1("/tmp/test.html")); + if(f2.open(IO_WriteOnly)) { + QTextStream t(&f2); + t << text; +// t << "\n\n-------------------------------------------------------\n\n"; +// t << Tellico::i18nReplace(text); + } + f2.close(); +#endif + // the XSLT file gets translated instead +// return Tellico::i18nReplace(text); + return text; +} + +void HTMLExporter::setFormattingOptions(Data::CollPtr coll) { + QString file = Kernel::self()->URL().fileName(); + if(file != i18n("Untitled")) { + m_handler->addStringParam("filename", QFile::encodeName(file)); + } + m_handler->addStringParam("cdate", KGlobal::locale()->formatDate(QDate::currentDate()).utf8()); + m_handler->addParam("show-headers", m_printHeaders ? "true()" : "false()"); + m_handler->addParam("group-entries", m_printGrouped ? "true()" : "false()"); + + QStringList sortTitles; + if(!m_sort1.isEmpty()) { + sortTitles << m_sort1; + } + if(!m_sort2.isEmpty()) { + sortTitles << m_sort2; + } + + // the third sort column may be same as first + if(!m_sort3.isEmpty() && sortTitles.findIndex(m_sort3) == -1) { + sortTitles << m_sort3; + } + + if(sortTitles.count() > 0) { + m_handler->addStringParam("sort-name1", coll->fieldNameByTitle(sortTitles[0]).utf8()); + if(sortTitles.count() > 1) { + m_handler->addStringParam("sort-name2", coll->fieldNameByTitle(sortTitles[1]).utf8()); + if(sortTitles.count() > 2) { + m_handler->addStringParam("sort-name3", coll->fieldNameByTitle(sortTitles[2]).utf8()); + } + } + } + + // no longer showing "sorted by..." since the column headers are clickable + // but still use "grouped by" + QString sortString; + if(m_printGrouped) { + QString s; + // if more than one, then it's the People pseudo-group + if(m_groupBy.count() > 1) { + s = i18n("People"); + } else { + s = coll->fieldTitleByName(m_groupBy[0]); + } + sortString = i18n("(grouped by %1)").arg(s); + + QString groupFields; + for(QStringList::ConstIterator it = m_groupBy.begin(); it != m_groupBy.end(); ++it) { + Data::FieldPtr f = coll->fieldByName(*it); + if(!f) { + continue; + } + if(f->flags() & Data::Field::AllowMultiple) { + groupFields += QString::fromLatin1("tc:") + *it + QString::fromLatin1("s/tc:") + *it; + } else { + groupFields += QString::fromLatin1("tc:") + *it; + } + int ncols = 0; + if(f->type() == Data::Field::Table) { + bool ok; + ncols = Tellico::toUInt(f->property(QString::fromLatin1("columns")), &ok); + if(!ok) { + ncols = 1; + } + } + if(ncols > 1) { + groupFields += QString::fromLatin1("/tc:column[1]"); + } + if(*it != m_groupBy.last()) { + groupFields += '|'; + } + } +// myDebug() << groupFields << endl; + m_handler->addStringParam("group-fields", groupFields.utf8()); + m_handler->addStringParam("sort-title", sortString.utf8()); + } + + QString pageTitle = coll->title(); + pageTitle += QChar(' ') + sortString; + m_handler->addStringParam("page-title", pageTitle.utf8()); + + QStringList showFields; + for(QStringList::ConstIterator it = m_columns.begin(); it != m_columns.end(); ++it) { + showFields << coll->fieldNameByTitle(*it); + } + m_handler->addStringParam("column-names", showFields.join(QChar(' ')).utf8()); + + if(m_imageWidth > 0 && m_imageHeight > 0) { + m_handler->addParam("image-width", QCString().setNum(m_imageWidth)); + m_handler->addParam("image-height", QCString().setNum(m_imageHeight)); + } + + // add system colors to stylesheet + const int type = coll->type(); + m_handler->addStringParam("font", Config::templateFont(type).family().latin1()); + m_handler->addStringParam("fontsize", QCString().setNum(Config::templateFont(type).pointSize())); + m_handler->addStringParam("bgcolor", Config::templateBaseColor(type).name().latin1()); + m_handler->addStringParam("fgcolor", Config::templateTextColor(type).name().latin1()); + m_handler->addStringParam("color1", Config::templateHighlightedTextColor(type).name().latin1()); + m_handler->addStringParam("color2", Config::templateHighlightedBaseColor(type).name().latin1()); + + // add locale code to stylesheet (for sorting) + m_handler->addStringParam("lang", KGlobal::locale()->languagesTwoAlpha().first().utf8()); +} + +void HTMLExporter::writeImages(Data::CollPtr coll_) { + // keep track of which image fields to write, this is for field names + StringSet imageFields; + for(QStringList::ConstIterator it = m_columns.begin(); it != m_columns.end(); ++it) { + if(coll_->fieldByTitle(*it)->type() == Data::Field::Image) { + imageFields.add(*it); + } + } + + // all the images potentially used in the HTML export need to be written to disk + // if we're exporting entry files, then we'll certainly want all the image fields written + // if we're not exporting to a file, then we might be exporting an entry template file + // and so we need to write all of them too. + if(m_exportEntryFiles || url().isEmpty()) { + // add all image fields to string list + Data::FieldVec fields = coll_->imageFields(); + for(Data::FieldVec::Iterator fieldIt = fields.begin(); fieldIt != fields.end(); ++fieldIt) { + imageFields.add(fieldIt->name()); + } + } + + // all of them are going to get written to tmp file + bool useTemp = url().isEmpty(); + KURL imgDir; + QString imgDirRelative; + // really some convoluted logic here + // basically, four cases. 1) we're writing to a tmp file, for printing probably + // so then write all the images to the tmp directory, 2) we're exporting to HTML, and + // this is the main collection file, in which case m_parseDOM is always true; + // 3) we're exporting HTML, and this is the first entry file, for which parseDOM is true + // and exportEntryFiles is false. Then the image file will get copied in copyFiles() and is + // probably an image in the entry template. 4) we're exporting HTML, and this is not the + // first entry file, in which case, we want to refer directly to the target dir + if(useTemp) { // everything goes in the tmp dir + imgDir.setPath(ImageFactory::tempDir()); + imgDirRelative = imgDir.path(); + } else if(m_parseDOM) { + imgDir = fileDir(); // copy to fileDir + imgDirRelative = Data::Document::self()->allImagesOnDisk() ? ImageFactory::dataDir() : ImageFactory::tempDir(); + createDir(); + } else { + imgDir = fileDir(); + imgDirRelative = KURL::relativeURL(url(), imgDir); + createDir(); + } + m_handler->addStringParam("imgdir", QFile::encodeName(imgDirRelative)); + + int count = 0; + const int processCount = 100; // process after every 100 events + + QStringList fieldsList = imageFields.toList(); + StringSet imageSet; // track which images are written + for(QStringList::ConstIterator fieldName = fieldsList.begin(); fieldName != fieldsList.end(); ++fieldName) { + for(Data::EntryVec::ConstIterator entryIt = entries().begin(); entryIt != entries().end(); ++entryIt) { + QString id = entryIt->field(*fieldName); + // if no id or is already writen, continue + if(id.isEmpty() || imageSet.has(id)) { + continue; + } + imageSet.add(id); + // try writing + bool success = useTemp ? ImageFactory::writeCachedImage(id, ImageFactory::TempDir) + : ImageFactory::writeImage(id, imgDir, true); + if(!success) { + kdWarning() << "HTMLExporter::writeImages() - unable to write image file: " + << imgDir.path() << id << endl; + } + + if(++count == processCount) { + kapp->processEvents(); + count = 0; + } + } + } +} + +QWidget* HTMLExporter::widget(QWidget* parent_, const char* name_/*=0*/) { + if(m_widget && m_widget->parent() == parent_) { + return m_widget; + } + + m_widget = new QWidget(parent_, name_); + QVBoxLayout* l = new QVBoxLayout(m_widget); + + QGroupBox* box = new QGroupBox(1, Qt::Horizontal, i18n("HTML Options"), m_widget); + l->addWidget(box); + + m_checkPrintHeaders = new QCheckBox(i18n("Print field headers"), box); + QWhatsThis::add(m_checkPrintHeaders, i18n("If checked, the field names will be " + "printed as table headers.")); + m_checkPrintHeaders->setChecked(m_printHeaders); + + m_checkPrintGrouped = new QCheckBox(i18n("Group the entries"), box); + QWhatsThis::add(m_checkPrintGrouped, i18n("If checked, the entries will be grouped by " + "the selected field.")); + m_checkPrintGrouped->setChecked(m_printGrouped); + + m_checkExportEntryFiles = new QCheckBox(i18n("Export individual entry files"), box); + QWhatsThis::add(m_checkExportEntryFiles, i18n("If checked, individual files will be created for each entry.")); + m_checkExportEntryFiles->setChecked(m_exportEntryFiles); + + l->addStretch(1); + return m_widget; +} + +void HTMLExporter::readOptions(KConfig* config_) { + KConfigGroup exportConfig(config_, QString::fromLatin1("ExportOptions - %1").arg(formatString())); + m_printHeaders = exportConfig.readBoolEntry("Print Field Headers", m_printHeaders); + m_printGrouped = exportConfig.readBoolEntry("Print Grouped", m_printGrouped); + m_exportEntryFiles = exportConfig.readBoolEntry("Export Entry Files", m_exportEntryFiles); + + // read current entry export template + m_entryXSLTFile = Config::templateName(collection()->type()); + m_entryXSLTFile = locate("appdata", QString::fromLatin1("entry-templates/") + + m_entryXSLTFile + QString::fromLatin1(".xsl")); +} + +void HTMLExporter::saveOptions(KConfig* config_) { + KConfigGroup cfg(config_, QString::fromLatin1("ExportOptions - %1").arg(formatString())); + m_printHeaders = m_checkPrintHeaders->isChecked(); + cfg.writeEntry("Print Field Headers", m_printHeaders); + m_printGrouped = m_checkPrintGrouped->isChecked(); + cfg.writeEntry("Print Grouped", m_printGrouped); + m_exportEntryFiles = m_checkExportEntryFiles->isChecked(); + cfg.writeEntry("Export Entry Files", m_exportEntryFiles); +} + +void HTMLExporter::setXSLTFile(const QString& filename_) { + if(m_xsltFile == filename_) { + return; + } + + m_xsltFile = filename_; + m_xsltFilePath = QString::null; + reset(); +} + +KURL HTMLExporter::fileDir() const { + if(url().isEmpty()) { + return KURL(); + } + KURL fileDir = url(); + // cd to directory of target URL + fileDir.cd(QString::fromLatin1("..")); + fileDir.addPath(fileDirName()); + return fileDir; +} + +QString HTMLExporter::fileDirName() const { + if(!m_collectionURL.isEmpty()) { + return QString::fromLatin1("/"); + } + return url().fileName().section('.', 0, 0) + QString::fromLatin1("_files/"); +} + +// how ugly is this? +const xmlChar* HTMLExporter::handleLink(const xmlChar* link_) { + return reinterpret_cast<xmlChar*>(qstrdup(handleLink(QString::fromUtf8(reinterpret_cast<const char*>(link_))).utf8())); +} + +QString HTMLExporter::handleLink(const QString& link_) { + if(m_links.contains(link_)) { + return m_links[link_]; + } + // assume that if the link_ is not relative, then we don't need to copy it + if(!KURL::isRelativeURL(link_)) { + return link_; + } + + if(m_xsltFilePath.isEmpty()) { + m_xsltFilePath = locate("appdata", m_xsltFile); + if(m_xsltFilePath.isNull()) { + kdWarning() << "HTMLExporter::handleLink() - no xslt file for " << m_xsltFile << endl; + } + } + + KURL u; + u.setPath(m_xsltFilePath); + u = KURL(u, link_); + + // one of the "quirks" of the html export is that img src urls are set to point to + // the tmpDir() when exporting entry files from a collection, but those images + // don't actually exist, and they get copied in writeImages() instead. + // so we only need to keep track of the url if it exists + const bool exists = KIO::NetAccess::exists(u, false, 0); + if(exists) { + m_files.append(u); + } + + // if we're exporting entry files, we want pics/ to + // go in pics/ + const bool isPic = link_.startsWith(m_dataDir + QString::fromLatin1("pics/")); + QString midDir; + if(m_exportEntryFiles && isPic) { + midDir = QString::fromLatin1("pics/"); + } + // pictures are special since they might not exist when the HTML is exported, since they might get copied later + // on the other hand, don't change the file location if it doesn't exist + if(isPic || exists) { + m_links.insert(link_, fileDirName() + midDir + u.fileName()); + } else { + m_links.insert(link_, link_); + } + return m_links[link_]; +} + +const xmlChar* HTMLExporter::analyzeInternalCSS(const xmlChar* str_) { + return reinterpret_cast<xmlChar*>(qstrdup(analyzeInternalCSS(QString::fromUtf8(reinterpret_cast<const char*>(str_))).utf8())); +} + +QString HTMLExporter::analyzeInternalCSS(const QString& str_) { + QString str = str_; + int start = 0; + int end = 0; + const QString url = QString::fromLatin1("url("); + for(int pos = str.find(url); pos >= 0; pos = str.find(url, pos+1)) { + pos += 4; // url( + if(str[pos] == '"' || str[pos] == '\'') { + ++pos; + } + + start = pos; + pos = str.find(')', start); + end = pos; + if(str[pos-1] == '"' || str[pos-1] == '\'') { + --end; + } + + str.replace(start, end-start, handleLink(str.mid(start, end-start))); + } + return str; +} + +void HTMLExporter::createDir() { + if(!m_checkCreateDir) { + return; + } + KURL dir = fileDir(); + if(dir.isEmpty()) { + myDebug() << "HTMLExporter::createDir() - called on empty URL!" << endl; + return; + } + if(KIO::NetAccess::exists(dir, false, 0)) { + m_checkCreateDir = false; + } else { + m_checkCreateDir = !KIO::NetAccess::mkdir(dir, m_widget); + } +} + +bool HTMLExporter::copyFiles() { + if(m_files.isEmpty()) { + return true; + } + const uint start = 20; + const uint maxProgress = m_exportEntryFiles ? 40 : 80; + const uint stepSize = QMAX(1, m_files.count()/maxProgress); + uint j = 0; + + createDir(); + KURL target; + for(KURL::List::ConstIterator it = m_files.begin(); it != m_files.end() && !m_cancelled; ++it, ++j) { + if(m_copiedFiles.has((*it).url())) { + continue; + } + + if(target.isEmpty()) { + target = fileDir(); + } + target.setFileName((*it).fileName()); + bool success = KIO::NetAccess::file_copy(*it, target, -1, true /* overwrite */, false /* resume */, m_widget); + if(success) { + m_copiedFiles.add((*it).url()); + } else { + kdWarning() << "HTMLExporter::copyFiles() - can't copy " << target << endl; + kdWarning() << KIO::NetAccess::lastErrorString() << endl; + } + if(j%stepSize == 0) { + if(options() & ExportProgress) { + ProgressManager::self()->setProgress(this, QMIN(start+j/stepSize, 99)); + } + kapp->processEvents(); + } + } + return true; +} + +bool HTMLExporter::writeEntryFiles() { + if(m_entryXSLTFile.isEmpty()) { + kdWarning() << "HTMLExporter::writeEntryFiles() - no entry XSLT file" << endl; + return false; + } + + const uint start = 60; + const uint stepSize = QMAX(1, entries().count()/40); + uint j = 0; + + // now worry about actually exporting entry files + // I can't reliable encode a string as a URI, so I'm punting, and I'll just replace everything but + // a-zA-Z0-9 with an underscore. This MUST match the filename template in tellico2html.xsl + // the id is used so uniqueness is guaranteed + const QRegExp badChars(QString::fromLatin1("[^-a-zA-Z0-9]")); + bool formatted = options() & Export::ExportFormatted; + + KURL outputFile = fileDir(); + + GUI::CursorSaver cs(Qt::waitCursor); + + HTMLExporter exporter(collection()); + long opt = options() | Export::ExportForce; + opt &= ~ExportProgress; + exporter.setOptions(opt); + exporter.setXSLTFile(m_entryXSLTFile); + exporter.setCollectionURL(url()); + bool parseDOM = true; + + const QString title = QString::fromLatin1("title"); + const QString html = QString::fromLatin1(".html"); + bool multipleTitles = collection()->fieldByName(title)->flags() & Data::Field::AllowMultiple; + Data::EntryVec entries = this->entries(); // not const since the pointer has to be copied + for(Data::EntryVecIt entryIt = entries.begin(); entryIt != entries.end() && !m_cancelled; ++entryIt, ++j) { + QString file = entryIt->field(title, formatted); + + // but only use the first title if it has multiple + if(multipleTitles) { + file = file.section(';', 0, 0); + } + file.replace(badChars, QChar('_')); + file += QChar('-') + QString::number(entryIt->id()) + html; + outputFile.setFileName(file); + + exporter.setEntries(Data::EntryVec(entryIt)); + exporter.setURL(outputFile); + exporter.exec(); + + // no longer need to parse DOM + if(parseDOM) { + parseDOM = false; + exporter.setParseDOM(false); + // this is rather stupid, but I'm too lazy to figure out the better way + // since we parsed the DOM for the first entry file to grab any + // images used in the template, need to resave it so the image links + // get written correctly + exporter.exec(); + } + + if(j%stepSize == 0) { + if(options() & ExportProgress) { + ProgressManager::self()->setProgress(this, QMIN(start+j/stepSize, 99)); + } + kapp->processEvents(); + } + } + // the images in "pics/" are special data images, copy them always + // since the entry files may refer to them, but we don't know that + QStringList dataImages; + dataImages << QString::fromLatin1("checkmark.png"); + for(uint i = 1; i <= 10; ++i) { + dataImages << QString::fromLatin1("stars%1.png").arg(i); + } + KURL dataDir; + dataDir.setPath(KGlobal::dirs()->findResourceDir("appdata", QString::fromLatin1("pics/tellico.png")) + "pics/"); + KURL target = fileDir(); + target.addPath(QString::fromLatin1("pics/")); + KIO::NetAccess::mkdir(target, m_widget); + for(QStringList::ConstIterator it = dataImages.begin(); it != dataImages.end(); ++it) { + dataDir.setFileName(*it); + target.setFileName(*it); + KIO::NetAccess::copy(dataDir, target, m_widget); + } + + return true; +} + +void HTMLExporter::slotCancel() { + m_cancelled = true; +} + +void HTMLExporter::parseDOM(xmlNode* node_) { + if(node_ == 0) { + myDebug() << "HTMLExporter::parseDOM() - no node" << endl; + return; + } + + bool parseChildren = true; + + if(node_->type == XML_ELEMENT_NODE) { + const QCString nodeName = QCString(reinterpret_cast<const char*>(node_->name)).upper(); + xmlElement* elem = reinterpret_cast<xmlElement*>(node_); + // to speed up things, check now for nodename + if(nodeName == "IMG" || nodeName == "SCRIPT" || nodeName == "LINK") { + for(xmlAttribute* attr = elem->attributes; attr; attr = reinterpret_cast<xmlAttribute*>(attr->next)) { + QCString attrName = QCString(reinterpret_cast<const char*>(attr->name)).upper(); + + if( (attrName == "SRC" && (nodeName == "IMG" || nodeName == "SCRIPT")) || + (attrName == "HREF" && nodeName == "LINK")) { +/* (attrName == "BACKGROUND" && (nodeName == "BODY" || + nodeName == "TABLE" || + nodeName == "TH" || + nodeName == "TD"))) */ + xmlChar* value = xmlGetProp(node_, attr->name); + if(value) { + xmlSetProp(node_, attr->name, handleLink(value)); + xmlFree(value); + } + // each node only has one significant attribute, so break now + break; + } + } + } else if(nodeName == "STYLE") { + // if the first child is a CDATA, use it, otherwise replace complete node + xmlNode* nodeToReplace = node_; + xmlNode* child = node_->children; + if(child && child->type == XML_CDATA_SECTION_NODE) { + nodeToReplace = child; + } + xmlChar* value = xmlNodeGetContent(nodeToReplace); + if(value) { + xmlNodeSetContent(nodeToReplace, analyzeInternalCSS(value)); + xmlFree(value); + } + // no longer need to parse child text nodes + parseChildren = false; + } + } + + if(parseChildren) { + xmlNode* child = node_->children; + while(child) { + parseDOM(child); + child = child->next; + } + } +} + +#include "htmlexporter.moc" |