summaryrefslogtreecommitdiffstats
path: root/src/htmlexporter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/htmlexporter.cpp')
-rw-r--r--src/htmlexporter.cpp560
1 files changed, 560 insertions, 0 deletions
diff --git a/src/htmlexporter.cpp b/src/htmlexporter.cpp
new file mode 100644
index 0000000..9e41d35
--- /dev/null
+++ b/src/htmlexporter.cpp
@@ -0,0 +1,560 @@
+/***************************************************************************
+ * Copyright (C) 2003 by Sébastien Laoût *
+ * slaout@linux62.org *
+ * *
+ * 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. *
+ ***************************************************************************/
+
+#include "htmlexporter.h"
+#include "bnpview.h"
+#include "basketlistview.h"
+#include "basket.h"
+#include "note.h"
+#include "tools.h"
+#include "config.h"
+#include "tag.h"
+
+#include <kapplication.h>
+#include <kglobal.h>
+#include <kconfig.h>
+#include <kiconloader.h>
+#include <kfiledialog.h>
+#include <kmessagebox.h>
+#include <qdir.h>
+#include <qfile.h>
+#include <qpainter.h>
+#include <kglobalsettings.h>
+#include <kprogress.h>
+
+HTMLExporter::HTMLExporter(Basket *basket)
+{
+ QDir dir;
+
+ // Compute a default file name & path:
+ KConfig *config = KGlobal::config();
+ config->setGroup("Export to HTML");
+ QString folder = config->readEntry("lastFolder", QDir::homeDirPath()) + "/";
+ QString url = folder + QString(basket->basketName()).replace("/", "_") + ".html";
+
+ // Ask a file name & path to the user:
+ QString filter = "*.html *.htm|" + i18n("HTML Documents") + "\n*|" + i18n("All Files");
+ QString destination = url;
+ for (bool askAgain = true; askAgain; ) {
+ // Ask:
+ destination = KFileDialog::getSaveFileName(destination, filter, 0, i18n("Export to HTML"));
+ // User canceled?
+ if (destination.isEmpty())
+ return;
+ // File already existing? Ask for overriding:
+ if (dir.exists(destination)) {
+ int result = KMessageBox::questionYesNoCancel(
+ 0,
+ "<qt>" + i18n("The file <b>%1</b> already exists. Do you really want to override it?")
+ .arg(KURL(destination).fileName()),
+ i18n("Override File?"),
+ KGuiItem(i18n("&Override"), "filesave")
+ );
+ if (result == KMessageBox::Cancel)
+ return;
+ else if (result == KMessageBox::Yes)
+ askAgain = false;
+ } else
+ askAgain = false;
+ }
+
+ // Create the progress dialog that will always be shown during the export:
+ KProgressDialog dialog(0, 0, i18n("Export to HTML"), i18n("Exporting to HTML. Please wait..."), /*Not modal, for password dialogs!*/false);
+ dialog.showCancelButton(false);
+ dialog.setAutoClose(true);
+ dialog.show();
+ progress = dialog.progressBar();
+
+ // Remember the last folder used for HTML exporation:
+ config->writeEntry("lastFolder", KURL(destination).directory());
+ config->sync();
+
+ prepareExport(basket, destination);
+ exportBasket(basket, /*isSubBasket*/false);
+
+ progress->advance(1); // Finishing finished
+}
+
+HTMLExporter::~HTMLExporter()
+{
+}
+
+void HTMLExporter::prepareExport(Basket *basket, const QString &fullPath)
+{
+ progress->setTotalSteps(/*Preparation:*/1 + /*Finishing:*/1 + /*Basket:*/1 + /*SubBaskets:*/Global::bnpView->basketCount(Global::bnpView->listViewItemForBasket(basket)));
+ progress->setValue(0);
+ kapp->processEvents();
+
+ // Remember the file path choosen by the user:
+ filePath = fullPath;
+ fileName = KURL(fullPath).fileName();
+ exportedBasket = basket;
+
+ BasketListViewItem *item = Global::bnpView->listViewItemForBasket(basket);
+ withBasketTree = (item->firstChild() != 0);
+
+ // Create and empty the files folder:
+ QString filesFolderPath = i18n("HTML export folder (files)", "%1_files").arg(filePath) + "/"; // eg.: "/home/seb/foo.html_files/"
+ Tools::deleteRecursively(filesFolderPath);
+ QDir dir;
+ dir.mkdir(filesFolderPath);
+
+ // Create sub-folders:
+ iconsFolderPath = filesFolderPath + i18n("HTML export folder (icons)", "icons") + "/"; // eg.: "/home/seb/foo.html_files/icons/"
+ imagesFolderPath = filesFolderPath + i18n("HTML export folder (images)", "images") + "/"; // eg.: "/home/seb/foo.html_files/images/"
+ basketsFolderPath = filesFolderPath + i18n("HTML export folder (baskets)", "baskets") + "/"; // eg.: "/home/seb/foo.html_files/baskets/"
+ dir.mkdir(iconsFolderPath);
+ dir.mkdir(imagesFolderPath);
+ dir.mkdir(basketsFolderPath);
+
+ progress->advance(1); // Preparation finished
+}
+
+#include <iostream>
+
+void HTMLExporter::exportBasket(Basket *basket, bool isSubBasket)
+{
+ if (!basket->isLoaded()) {
+ basket->load();
+ }
+
+ // Compute the absolute & relative paths for this basket:
+ filesFolderPath = i18n("HTML export folder (files)", "%1_files").arg(filePath) + "/";
+ if (isSubBasket) {
+ basketFilePath = basketsFolderPath + basket->folderName().left(basket->folderName().length() - 1) + ".html";
+ filesFolderName = "../";
+ dataFolderName = basket->folderName().left(basket->folderName().length() - 1) + "-" + i18n("HTML export folder (data)", "data") + "/";
+ dataFolderPath = basketsFolderPath + dataFolderName;
+ basketsFolderName = "";
+ } else {
+ basketFilePath = filePath;
+ filesFolderName = i18n("HTML export folder (files)", "%1_files").arg(KURL(filePath).fileName()) + "/";
+ dataFolderName = filesFolderName + i18n("HTML export folder (data)", "data") + "/";
+ dataFolderPath = filesFolderPath + i18n("HTML export folder (data)", "data") + "/";
+ basketsFolderName = filesFolderName + i18n("HTML export folder (baskets)", "baskets") + "/";
+ }
+ iconsFolderName = (isSubBasket ? "../" : filesFolderName) + i18n("HTML export folder (icons)", "icons") + "/"; // eg.: "foo.html_files/icons/" or "../icons/"
+ imagesFolderName = (isSubBasket ? "../" : filesFolderName) + i18n("HTML export folder (images)", "images") + "/"; // eg.: "foo.html_files/images/" or "../images/"
+
+ std::cout << "Exporting ================================================" << std::endl;
+ std::cout << " filePath:" << filePath << std::endl;
+ std::cout << " basketFilePath:" << basketFilePath << std::endl;
+ std::cout << " filesFolderPath:" << filesFolderPath << std::endl;
+ std::cout << " filesFolderName:" << filesFolderName << std::endl;
+ std::cout << " iconsFolderPath:" << iconsFolderPath << std::endl;
+ std::cout << " iconsFolderName:" << iconsFolderName << std::endl;
+ std::cout << " imagesFolderPath:" << imagesFolderPath << std::endl;
+ std::cout << " imagesFolderName:" << imagesFolderName << std::endl;
+ std::cout << " dataFolderPath:" << dataFolderPath << std::endl;
+ std::cout << " dataFolderName:" << dataFolderName << std::endl;
+ std::cout << " basketsFolderPath:" << basketsFolderPath << std::endl;
+ std::cout << " basketsFolderName:" << basketsFolderName << std::endl;
+
+ // Create the data folder for this basket:
+ QDir dir;
+ dir.mkdir(dataFolderPath);
+
+ backgroundColorName = basket->backgroundColor().name().lower().mid(1);
+
+ // Generate basket icons:
+ QString basketIcon16 = iconsFolderName + copyIcon(basket->icon(), 16);
+ QString basketIcon32 = iconsFolderName + copyIcon(basket->icon(), 32);
+
+ // Generate the [+] image for groups:
+ QPixmap expandGroup(Note::EXPANDER_WIDTH, Note::EXPANDER_HEIGHT);
+ expandGroup.fill(basket->backgroundColor());
+ QPainter painter(&expandGroup);
+ Note::drawExpander(&painter, 0, 0, basket->backgroundColor(), /*expand=*/true, basket);
+ painter.end();
+ expandGroup.save(imagesFolderPath + "expand_group_" + backgroundColorName + ".png", "PNG");
+
+ // Generate the [-] image for groups:
+ QPixmap foldGroup(Note::EXPANDER_WIDTH, Note::EXPANDER_HEIGHT);
+ foldGroup.fill(basket->backgroundColor());
+ painter.begin(&foldGroup);
+ Note::drawExpander(&painter, 0, 0, basket->backgroundColor(), /*expand=*/false, basket);
+ painter.end();
+ foldGroup.save(imagesFolderPath + "fold_group_" + backgroundColorName + ".png", "PNG");
+
+ // Open the file to write:
+ QFile file(basketFilePath);
+ if (!file.open(IO_WriteOnly))
+ return;
+ stream.setDevice(&file);
+ stream.setEncoding(QTextStream::UnicodeUTF8);
+
+ // Compute the colors to draw dragient for notes:
+ QColor topBgColor;
+ QColor bottomBgColor;
+ Note::getGradientColors(basket->backgroundColor(), &topBgColor, &bottomBgColor);
+ // Compute the gradient image for notes:
+ QString gradientImageFileName = Basket::saveGradientBackground(basket->backgroundColor(), basket->QScrollView::font(), imagesFolderPath);
+
+ // Output the header:
+ QString borderColor = Tools::mixColor(basket->backgroundColor(), basket->textColor()).name();
+ stream <<
+ "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n"
+ "<html>\n"
+ " <head>\n"
+ " <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n"
+ " <meta name=\"Generator\" content=\"" << kapp->aboutData()->programName() << " " << VERSION << " http://basket.kde.org/\">\n"
+ " <style type=\"text/css\">\n"
+// " @media print {\n"
+// " span.printable { display: inline; }\n"
+// " }\n"
+ " body { margin: 10px; font: 11px sans-serif; }\n" // TODO: Use user font
+ " h1 { text-align: center; }\n"
+ " img { border: none; vertical-align: middle; }\n";
+ if (withBasketTree) {
+ stream <<
+ " .tree { margin: 0; padding: 1px 0 1px 1px; width: 150px; _width: 149px; overflow: hidden; float: left; }\n"
+ " .tree ul { margin: 0 0 0 10px; padding: 0; }\n"
+ " .tree li { padding: 0; margin: 0; list-style: none; }\n"
+ " .tree a { display: block; padding: 1px; height: 16px; text-decoration: none;\n"
+ " white-space: nowrap; word-wrap: normal; text-wrap: suppress; color: black; }\n"
+ " .tree span { -moz-border-radius: 6px; display: block; float: left;\n"
+ " line-height: 16px; height: 16px; vertical-align: middle; padding: 0 1px; }\n"
+ " .tree img { vertical-align: top; padding-right: 1px; }\n"
+ " .tree .current { background-color: " << KGlobalSettings::highlightColor().name() << "; "
+ "-moz-border-radius: 3px 0 0 3px; border-radius: 3px 0 0 3px; color: " << KGlobalSettings::highlightedTextColor().name() << "; }\n"
+ " .basketSurrounder { margin-left: 152px; _margin: 0; _float: right; }\n";
+ }
+ stream <<
+ " .basket { background-color: " << basket->backgroundColor().name() << "; border: solid " << borderColor << " 1px; "
+ "font: " << Tools::cssFontDefinition(basket->QScrollView::font()) << "; color: " << basket->textColor().name() << "; padding: 1px; width: 100%; }\n"
+ " table.basket { border-collapse: collapse; }\n"
+ " .basket * { padding: 0; margin: 0; }\n"
+ " .basket table { width: 100%; border-spacing: 0; _border-collapse: collapse; }\n"
+ " .column { vertical-align: top; }\n"
+ " .columnHandle { width: " << Note::RESIZER_WIDTH << "px; background: transparent url('" << imagesFolderName << "column_handle_" << backgroundColorName << ".png') repeat-y; }\n"
+ " .group { margin: 0; padding: 0; border-collapse: collapse; width: 100% }\n"
+ " .groupHandle { margin: 0; width: " << Note::GROUP_WIDTH << "px; text-align: center; }\n"
+ " .note { padding: 1px 2px; background: " << bottomBgColor.name() << " url('" << imagesFolderName << gradientImageFileName << "')"
+ " repeat-x; border-top: solid " << topBgColor.name() <<
+ " 1px; border-bottom: solid " << Tools::mixColor(topBgColor, bottomBgColor).name() <<
+ " 1px; width: 100%; }\n"
+ " .tags { width: 1px; white-space: nowrap; }\n"
+ " .tags img { padding-right: 2px; }\n"
+ << LinkLook::soundLook->toCSS("sound", basket->textColor())
+ << LinkLook::fileLook->toCSS("file", basket->textColor())
+ << LinkLook::localLinkLook->toCSS("local", basket->textColor())
+ << LinkLook::networkLinkLook->toCSS("network", basket->textColor())
+ << LinkLook::launcherLook->toCSS("launcher", basket->textColor())
+ <<
+ " .unknown { margin: 1px 2px; border: 1px solid " << borderColor << "; -moz-border-radius: 4px; }\n";
+ QValueList<State*> states = basket->usedStates();
+ QString statesCss;
+ for (State::List::Iterator it = states.begin(); it != states.end(); ++it)
+ statesCss += (*it)->toCSS(imagesFolderPath, imagesFolderName, basket->QScrollView::font());
+ stream <<
+ statesCss <<
+ " .credits { text-align: right; margin: 3px 0 0 0; _margin-top: -17px; font-size: 80%; color: " << borderColor << "; }\n"
+ " </style>\n"
+ " <title>" << Tools::textToHTMLWithoutP(basket->basketName()) << "</title>\n"
+ " <link rel=\"shortcut icon\" type=\"image/png\" href=\"" << basketIcon16 << "\">\n";
+ // Create the column handle image:
+ QPixmap columnHandle(Note::RESIZER_WIDTH, 50);
+ painter.begin(&columnHandle);
+ Note::drawInactiveResizer(&painter, 0, 0, columnHandle.height(), basket->backgroundColor(), /*column=*/true);
+ painter.end();
+ columnHandle.save(imagesFolderPath + "column_handle_" + backgroundColorName + ".png", "PNG");
+
+ stream <<
+ " </head>\n"
+ " <body>\n"
+ " <h1><img src=\"" << basketIcon32 << "\" width=\"32\" height=\"32\" alt=\"\"> " << Tools::textToHTMLWithoutP(basket->basketName()) << "</h1>\n";
+
+ if (withBasketTree)
+ writeBasketTree(basket);
+
+ // If filtering, only export filtered notes, inform to the user:
+ // TODO: Filtering tags too!!
+ // TODO: Make sure only filtered notes are exported!
+// if (decoration()->filterData().isFiltering)
+// stream <<
+// " <p>" << i18n("Notes matching the filter &quot;%1&quot;:").arg(Tools::textToHTMLWithoutP(decoration()->filterData().string)) << "</p>\n";
+
+ stream <<
+ " <div class=\"basketSurrounder\">\n";
+
+ if (basket->isColumnsLayout())
+ stream <<
+ " <table class=\"basket\">\n"
+ " <tr>\n";
+ else
+ stream <<
+ " <div class=\"basket\" style=\"position: relative; height: " << basket->contentsHeight() << "px; width: " << basket->contentsWidth() << "px; min-width: 100%;\">\n";
+
+ for (Note *note = basket->firstNote(); note; note = note->next())
+ exportNote(note, /*indent=*/(basket->isFreeLayout() ? 4 : 5));
+
+ // Output the footer:
+ if (basket->isColumnsLayout())
+ stream <<
+ " </tr>\n"
+ " </table>\n";
+ else
+ stream <<
+ " </div>\n";
+ stream << QString(
+ " </div>\n"
+ " <p class=\"credits\">%1</p>\n").arg(
+ i18n("Made with %1, a KDE tool to take notes and keep information at hand.")
+ .arg("<a href=\"http://basket.kde.org/\">%1</a> %2")
+ .arg(kapp->aboutData()->programName(), VERSION));
+
+ // Copy a transparent GIF image in the folder, needed for the JavaScript hack:
+ QString gifFileName = "spacer.gif";
+ QFile transGIF(imagesFolderPath + gifFileName);
+ if (!transGIF.exists() && transGIF.open(IO_WriteOnly)) {
+ QDataStream streamGIF(&transGIF);
+ // This is a 1px*1px transparent GIF image:
+ const uchar blankGIF[] = {
+ 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x0a, 0x00, 0x0a, 0x00,
+ 0x80, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x21,
+ 0xfe, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20,
+ 0x77, 0x69, 0x74, 0x68, 0x20, 0x54, 0x68, 0x65, 0x20, 0x47,
+ 0x49, 0x4d, 0x50, 0x00, 0x21, 0xf9, 0x04, 0x01, 0x0a, 0x00,
+ 0x01, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a,
+ 0x00, 0x00, 0x02, 0x08, 0x8c, 0x8f, 0xa9, 0xcb, 0xed, 0x0f,
+ 0x63, 0x2b, 0x00, 0x3b };
+ streamGIF.writeRawBytes((const char*)blankGIF, (unsigned int)74);
+ transGIF.close();
+ }
+ stream <<
+ " <!--[if lt IE 7]>\n"
+ " <script>\n"
+ " function fixPng(img) {\n"
+ " if (!img.style.filter) {\n"
+ " img.style.filter = \"progid:DXImageTransform.Microsoft.AlphaImageLoader(src='\" + img.src + \"')\";\n"
+ " img.src = \"" << imagesFolderName << gifFileName << "\";\n"
+ " }\n"
+ " }\n"
+ " for (i = document.images.length - 1; i >= 0; i -= 1) {\n"
+ " var img = document.images[i];\n"
+ " if (img.src.substr(img.src.length - 4) == \".png\")\n"
+ " if (img.complete)\n"
+ " fixPng(img);\n"
+ " else\n"
+ " img.attachEvent(\"onload\", function() { fixPng(window.event.srcElement); });\n"
+ " }\n"
+ " </script>\n"
+ " <![endif]-->\n"
+ " </body>\n"
+ "</html>\n";
+
+ file.close();
+ stream.unsetDevice();
+ progress->advance(1); // Basket exportation finished
+
+ // Recursively export child baskets:
+ BasketListViewItem *item = Global::bnpView->listViewItemForBasket(basket);
+ if (item->firstChild()) {
+ for (BasketListViewItem *child = (BasketListViewItem*) item->firstChild(); child; child = (BasketListViewItem*) child->nextSibling()) {
+ exportBasket(child->basket(), /*isSubBasket=*/true);
+ }
+ }
+}
+
+void HTMLExporter::exportNote(Note *note, int indent)
+{
+ QString spaces;
+
+ if (note->isColumn()) {
+ QString width = "";
+ if (false/*TODO: DEBUG AND REENABLE: hasResizer()*/) {
+ // As we cannot be precise in CSS (say eg. "width: 50%-40px;"),
+ // we output a percentage that is approximatively correct.
+ // For instance, we compute the currently used percentage of width in the basket
+ // and try make make it the same on a 1024*768 display in a Web browser:
+ int availableSpaceForColumnsInThisBasket = note->basket()->contentsWidth() - (note->basket()->columnsCount() - 1) * Note::RESIZER_WIDTH;
+ int availableSpaceForColumnsInBrowser = 1024 /* typical screen width */
+ - 25 /* window border and scrollbar width */
+ - 2 * 5 /* page margin */
+ - (note->basket()->columnsCount() - 1) * Note::RESIZER_WIDTH;
+ if (availableSpaceForColumnsInThisBasket <= 0)
+ availableSpaceForColumnsInThisBasket = 1;
+ int widthValue = (int)(availableSpaceForColumnsInBrowser * (double) note->groupWidth() / availableSpaceForColumnsInThisBasket);
+ if (widthValue <= 0)
+ widthValue = 1;
+ if (widthValue > 100)
+ widthValue = 100;
+ width = QString(" width=\"%1%\"").arg(QString::number(widthValue));
+ }
+ stream << spaces.fill(' ', indent) << "<td class=\"column\"" << width << ">\n";
+
+ // Export child notes:
+ for (Note *child = note->firstChild(); child; child = child->next()) {
+ stream << spaces.fill(' ', indent + 1);
+ exportNote(child, indent + 1);
+ stream << '\n';
+ }
+
+ stream << spaces.fill(' ', indent) << "</td>\n";
+ if (note->hasResizer())
+ stream << spaces.fill(' ', indent) << "<td class=\"columnHandle\"></td>\n";
+ return;
+ }
+
+ QString freeStyle;
+ if (note->isFree())
+ freeStyle = " style=\"position: absolute; left: " + QString::number(note->x()) + "px; top: " + QString::number(note->y()) + "px; width: " + QString::number(note->groupWidth()) + "px\"";
+
+ if (note->isGroup()) {
+ stream << '\n' << spaces.fill(' ', indent) << "<table" << freeStyle << ">\n"; // Note content is expected to be on the same HTML line, but NOT groups
+ int i = 0;
+ for (Note *child = note->firstChild(); child; child = child->next()) {
+ stream << spaces.fill(' ', indent);
+ if (i == 0)
+ stream << " <tr><td class=\"groupHandle\"><img src=\"" << imagesFolderName << (note->isFolded() ? "expand_group_" : "fold_group_") << backgroundColorName << ".png"
+ << "\" width=\"" << Note::EXPANDER_WIDTH << "\" height=\"" << Note::EXPANDER_HEIGHT << "\"></td>\n";
+ else if (i == 1)
+ stream << " <tr><td class=\"freeSpace\" rowspan=\"" << note->countDirectChilds() << "\"></td>\n";
+ else
+ stream << " <tr>\n";
+ stream << spaces.fill(' ', indent) << " <td>";
+ exportNote(child, indent + 3);
+ stream << "</td>\n"
+ << spaces.fill(' ', indent) << " </tr>\n";
+ ++i;
+ }
+ stream << '\n' << spaces.fill(' ', indent) << "</table>\n" /*<< spaces.fill(' ', indent - 1)*/;
+ } else {
+ // Additionnal class for the content (link, netword, color...):
+ QString additionnalClasses = note->content()->cssClass();
+ if (!additionnalClasses.isEmpty())
+ additionnalClasses = " " + additionnalClasses;
+ // Assign the style of each associted tags:
+ for (State::List::Iterator it = note->states().begin(); it != note->states().end(); ++it)
+ additionnalClasses += " tag_" + (*it)->id();
+ //stream << spaces.fill(' ', indent);
+ stream << "<table class=\"note" << additionnalClasses << "\"" << freeStyle << "><tr>";
+ if (note->emblemsCount() > 0) {
+ stream << "<td class=\"tags\"><nobr>";
+ for (State::List::Iterator it = note->states().begin(); it != note->states().end(); ++it)
+ if (!(*it)->emblem().isEmpty()) {
+ int emblemSize = 16;
+ QString iconFileName = copyIcon((*it)->emblem(), emblemSize);
+ stream << "<img src=\"" << iconsFolderName << iconFileName
+ << "\" width=\"" << emblemSize << "\" height=\"" << emblemSize
+ << "\" alt=\"" << (*it)->textEquivalent() << "\" title=\"" << (*it)->fullName() << "\">";
+ }
+ stream << "</nobr></td>";
+ }
+ stream << "<td>";
+ note->content()->exportToHTML(this, indent);
+ stream << "</td></tr></table>";
+ }
+}
+
+void HTMLExporter::writeBasketTree(Basket *currentBasket)
+{
+ stream << " <ul class=\"tree\">\n";
+ writeBasketTree(currentBasket, exportedBasket, 3);
+ stream << " </ul>\n";
+}
+
+void HTMLExporter::writeBasketTree(Basket *currentBasket, Basket *basket, int indent)
+{
+ // Compute variable HTML code:
+ QString spaces;
+ QString cssClass = (basket == currentBasket ? " class=\"current\"" : "");
+ QString link = "#";
+ if (currentBasket != basket) {
+ if (currentBasket == exportedBasket) {
+ link = basketsFolderName + basket->folderName().left(basket->folderName().length() - 1) + ".html";
+ } else if (basket == exportedBasket) {
+ link = "../../" + fileName;
+ } else {
+ link = basket->folderName().left(basket->folderName().length() - 1) + ".html";
+ }
+ }
+ QString spanStyle = "";
+ if (basket->backgroundColorSetting().isValid() || basket->textColorSetting().isValid()) {
+ spanStyle = " style=\"background-color: " + basket->backgroundColor().name() + "; color: " + basket->textColor().name() + "\"";
+ }
+
+ // Write the basket tree line:
+ stream <<
+ spaces.fill(' ', indent) << "<li><a" << cssClass << " href=\"" << link << "\">"
+ "<span" << spanStyle << " title=\"" << Tools::textToHTMLWithoutP(basket->basketName()) << "\">"
+ "<img src=\"" << iconsFolderName << copyIcon(basket->icon(), 16) << "\" width=\"16\" height=\"16\" alt=\"\">" << Tools::textToHTMLWithoutP(basket->basketName()) << "</span></a>";
+
+ // Write the sub-baskets lines & end the current one:
+ BasketListViewItem *item = Global::bnpView->listViewItemForBasket(basket);
+ if (item->firstChild() != 0) {
+ stream <<
+ "\n" <<
+ spaces.fill(' ', indent) << " <ul>\n";
+ for (BasketListViewItem *child = (BasketListViewItem*) item->firstChild(); child; child = (BasketListViewItem*) child->nextSibling())
+ writeBasketTree(currentBasket, child->basket(), indent + 2);
+ stream <<
+ spaces.fill(' ', indent) << " </ul>\n" <<
+ spaces.fill(' ', indent) << "</li>\n";
+ } else {
+ stream << "</li>\n";
+ }
+}
+
+/** Save an icon to a folder.
+ * If an icon with the same name already exist in the destination,
+ * it is assumed the icon is already copied, so no action is took.
+ * It is optimized so that you can have an empty folder receiving the icons
+ * and call copyIcon() each time you encounter one during export process.
+ */
+QString HTMLExporter::copyIcon(const QString &iconName, int size)
+{
+ if (iconName.isEmpty())
+ return "";
+
+ // Sometimes icon can be "favicons/www.kde.org", we replace the '/' with a '_'
+ QString fileName = iconName; // QString::replace() isn't const, so I must copy the string before
+ fileName = "ico" + QString::number(size) + "_" + fileName.replace("/", "_") + ".png";
+ QString fullPath = iconsFolderPath + fileName;
+ if (!QFile::exists(fullPath))
+ DesktopIcon(iconName, size).save(fullPath, "PNG");
+ return fileName;
+}
+
+/** Done: Sometimes we can call two times copyFile() with the same srcPath and dataFolderPath
+ * (eg. when exporting basket to HTML with two links to same filename
+ * (but not necesary same path, as in "/home/foo.txt" and "/foo.txt") )
+ * The first copy isn't yet started, so the dest file isn't created and this method
+ * returns the same filename !!!!!!!!!!!!!!!!!!!!
+ */
+QString HTMLExporter::copyFile(const QString &srcPath, bool createIt)
+{
+ QString fileName = Tools::fileNameForNewFile(KURL(srcPath).fileName(), dataFolderPath);
+ QString fullPath = dataFolderPath + fileName;
+
+ if (createIt) {
+ // We create the file to be sure another very near call to copyFile() willn't choose the same name:
+ QFile file(KURL(fullPath).path());
+ if (file.open(IO_WriteOnly))
+ file.close();
+ // And then we copy the file AND overwriting the file we juste created:
+ new KIO::FileCopyJob(
+ KURL(srcPath), KURL(fullPath), 0666, /*move=*/false,
+ /*overwrite=*/true, /*resume=*/true, /*showProgress=*/false );
+ } else
+ /*KIO::CopyJob *copyJob = */KIO::copy(KURL(srcPath), KURL(fullPath)); // Do it as before
+
+ return fileName;
+}