Ran qt3to4
[basket4.git] / src / htmlexporter.cpp
blob7d08d6d9b824320b6221d85e3d039d7b46043bde
1 /***************************************************************************
2 * Copyright (C) 2003 by Sébastien Laoût *
3 * slaout@linux62.org *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
21 #include "htmlexporter.h"
22 #include "bnpview.h"
23 #include "basketlistview.h"
24 #include "basket.h"
25 #include "note.h"
26 #include "tools.h"
27 #include "config.h"
28 #include "tag.h"
30 #include <kapplication.h>
31 #include <kglobal.h>
32 #include <kconfig.h>
33 #include <kiconloader.h>
34 #include <kfiledialog.h>
35 #include <kmessagebox.h>
36 #include <qdir.h>
37 #include <qfile.h>
38 #include <qpainter.h>
39 //Added by qt3to4:
40 #include <Q3TextStream>
41 #include <Q3ValueList>
42 #include <QPixmap>
43 #include <kglobalsettings.h>
44 #include <kprogress.h>
46 HTMLExporter::HTMLExporter(Basket *basket)
48 QDir dir;
50 // Compute a default file name & path:
51 KConfig *config = KGlobal::config();
52 config->setGroup("Export to HTML");
53 QString folder = config->readEntry("lastFolder", QDir::homeDirPath()) + "/";
54 QString url = folder + QString(basket->basketName()).replace("/", "_") + ".html";
56 // Ask a file name & path to the user:
57 QString filter = "*.html *.htm|" + i18n("HTML Documents") + "\n*|" + i18n("All Files");
58 QString destination = url;
59 for (bool askAgain = true; askAgain; ) {
60 // Ask:
61 destination = KFileDialog::getSaveFileName(destination, filter, 0, i18n("Export to HTML"));
62 // User canceled?
63 if (destination.isEmpty())
64 return;
65 // File already existing? Ask for overriding:
66 if (dir.exists(destination)) {
67 int result = KMessageBox::questionYesNoCancel(
69 "<qt>" + i18n("The file <b>%1</b> already exists. Do you really want to override it?")
70 .arg(KURL(destination).fileName()),
71 i18n("Override File?"),
72 KGuiItem(i18n("&Override"), "filesave")
74 if (result == KMessageBox::Cancel)
75 return;
76 else if (result == KMessageBox::Yes)
77 askAgain = false;
78 } else
79 askAgain = false;
82 // Create the progress dialog that will always be shown during the export:
83 KProgressDialog dialog(0, 0, i18n("Export to HTML"), i18n("Exporting to HTML. Please wait..."), /*Not modal, for password dialogs!*/false);
84 dialog.showCancelButton(false);
85 dialog.setAutoClose(true);
86 dialog.show();
87 progress = dialog.progressBar();
89 // Remember the last folder used for HTML exporation:
90 config->writeEntry("lastFolder", KURL(destination).directory());
91 config->sync();
93 prepareExport(basket, destination);
94 exportBasket(basket, /*isSubBasket*/false);
96 progress->advance(1); // Finishing finished
99 HTMLExporter::~HTMLExporter()
103 void HTMLExporter::prepareExport(Basket *basket, const QString &fullPath)
105 progress->setTotalSteps(/*Preparation:*/1 + /*Finishing:*/1 + /*Basket:*/1 + /*SubBaskets:*/Global::bnpView->basketCount(Global::bnpView->listViewItemForBasket(basket)));
106 progress->setValue(0);
107 kapp->processEvents();
109 // Remember the file path choosen by the user:
110 filePath = fullPath;
111 fileName = KURL(fullPath).fileName();
112 exportedBasket = basket;
114 BasketListViewItem *item = Global::bnpView->listViewItemForBasket(basket);
115 withBasketTree = (item->firstChild() != 0);
117 // Create and empty the files folder:
118 QString filesFolderPath = i18n("HTML export folder (files)", "%1_files").arg(filePath) + "/"; // eg.: "/home/seb/foo.html_files/"
119 Tools::deleteRecursively(filesFolderPath);
120 QDir dir;
121 dir.mkdir(filesFolderPath);
123 // Create sub-folders:
124 iconsFolderPath = filesFolderPath + i18n("HTML export folder (icons)", "icons") + "/"; // eg.: "/home/seb/foo.html_files/icons/"
125 imagesFolderPath = filesFolderPath + i18n("HTML export folder (images)", "images") + "/"; // eg.: "/home/seb/foo.html_files/images/"
126 basketsFolderPath = filesFolderPath + i18n("HTML export folder (baskets)", "baskets") + "/"; // eg.: "/home/seb/foo.html_files/baskets/"
127 dir.mkdir(iconsFolderPath);
128 dir.mkdir(imagesFolderPath);
129 dir.mkdir(basketsFolderPath);
131 progress->advance(1); // Preparation finished
134 #include <iostream>
136 void HTMLExporter::exportBasket(Basket *basket, bool isSubBasket)
138 if (!basket->isLoaded()) {
139 basket->load();
142 // Compute the absolute & relative paths for this basket:
143 filesFolderPath = i18n("HTML export folder (files)", "%1_files").arg(filePath) + "/";
144 if (isSubBasket) {
145 basketFilePath = basketsFolderPath + basket->folderName().left(basket->folderName().length() - 1) + ".html";
146 filesFolderName = "../";
147 dataFolderName = basket->folderName().left(basket->folderName().length() - 1) + "-" + i18n("HTML export folder (data)", "data") + "/";
148 dataFolderPath = basketsFolderPath + dataFolderName;
149 basketsFolderName = "";
150 } else {
151 basketFilePath = filePath;
152 filesFolderName = i18n("HTML export folder (files)", "%1_files").arg(KURL(filePath).fileName()) + "/";
153 dataFolderName = filesFolderName + i18n("HTML export folder (data)", "data") + "/";
154 dataFolderPath = filesFolderPath + i18n("HTML export folder (data)", "data") + "/";
155 basketsFolderName = filesFolderName + i18n("HTML export folder (baskets)", "baskets") + "/";
157 iconsFolderName = (isSubBasket ? "../" : filesFolderName) + i18n("HTML export folder (icons)", "icons") + "/"; // eg.: "foo.html_files/icons/" or "../icons/"
158 imagesFolderName = (isSubBasket ? "../" : filesFolderName) + i18n("HTML export folder (images)", "images") + "/"; // eg.: "foo.html_files/images/" or "../images/"
160 std::cout << "Exporting ================================================" << std::endl;
161 std::cout << " filePath:" << filePath << std::endl;
162 std::cout << " basketFilePath:" << basketFilePath << std::endl;
163 std::cout << " filesFolderPath:" << filesFolderPath << std::endl;
164 std::cout << " filesFolderName:" << filesFolderName << std::endl;
165 std::cout << " iconsFolderPath:" << iconsFolderPath << std::endl;
166 std::cout << " iconsFolderName:" << iconsFolderName << std::endl;
167 std::cout << " imagesFolderPath:" << imagesFolderPath << std::endl;
168 std::cout << " imagesFolderName:" << imagesFolderName << std::endl;
169 std::cout << " dataFolderPath:" << dataFolderPath << std::endl;
170 std::cout << " dataFolderName:" << dataFolderName << std::endl;
171 std::cout << " basketsFolderPath:" << basketsFolderPath << std::endl;
172 std::cout << " basketsFolderName:" << basketsFolderName << std::endl;
174 // Create the data folder for this basket:
175 QDir dir;
176 dir.mkdir(dataFolderPath);
178 backgroundColorName = basket->backgroundColor().name().lower().mid(1);
180 // Generate basket icons:
181 QString basketIcon16 = iconsFolderName + copyIcon(basket->icon(), 16);
182 QString basketIcon32 = iconsFolderName + copyIcon(basket->icon(), 32);
184 // Generate the [+] image for groups:
185 QPixmap expandGroup(Note::EXPANDER_WIDTH, Note::EXPANDER_HEIGHT);
186 expandGroup.fill(basket->backgroundColor());
187 QPainter painter(&expandGroup);
188 Note::drawExpander(&painter, 0, 0, basket->backgroundColor(), /*expand=*/true, basket);
189 painter.end();
190 expandGroup.save(imagesFolderPath + "expand_group_" + backgroundColorName + ".png", "PNG");
192 // Generate the [-] image for groups:
193 QPixmap foldGroup(Note::EXPANDER_WIDTH, Note::EXPANDER_HEIGHT);
194 foldGroup.fill(basket->backgroundColor());
195 painter.begin(&foldGroup);
196 Note::drawExpander(&painter, 0, 0, basket->backgroundColor(), /*expand=*/false, basket);
197 painter.end();
198 foldGroup.save(imagesFolderPath + "fold_group_" + backgroundColorName + ".png", "PNG");
200 // Open the file to write:
201 QFile file(basketFilePath);
202 if (!file.open(QIODevice::WriteOnly))
203 return;
204 stream.setDevice(&file);
205 stream.setEncoding(Q3TextStream::UnicodeUTF8);
207 // Compute the colors to draw dragient for notes:
208 QColor topBgColor;
209 QColor bottomBgColor;
210 Note::getGradientColors(basket->backgroundColor(), &topBgColor, &bottomBgColor);
211 // Compute the gradient image for notes:
212 QString gradientImageFileName = Basket::saveGradientBackground(basket->backgroundColor(), basket->Q3ScrollView::font(), imagesFolderPath);
214 // Output the header:
215 QString borderColor = Tools::mixColor(basket->backgroundColor(), basket->textColor()).name();
216 stream <<
217 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n"
218 "<html>\n"
219 " <head>\n"
220 " <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n"
221 " <meta name=\"Generator\" content=\"" << kapp->aboutData()->programName() << " " << VERSION << " http://basket.kde.org/\">\n"
222 " <style type=\"text/css\">\n"
223 // " @media print {\n"
224 // " span.printable { display: inline; }\n"
225 // " }\n"
226 " body { margin: 10px; font: 11px sans-serif; }\n" // TODO: Use user font
227 " h1 { text-align: center; }\n"
228 " img { border: none; vertical-align: middle; }\n";
229 if (withBasketTree) {
230 stream <<
231 " .tree { margin: 0; padding: 1px 0 1px 1px; width: 150px; _width: 149px; overflow: hidden; float: left; }\n"
232 " .tree ul { margin: 0 0 0 10px; padding: 0; }\n"
233 " .tree li { padding: 0; margin: 0; list-style: none; }\n"
234 " .tree a { display: block; padding: 1px; height: 16px; text-decoration: none;\n"
235 " white-space: nowrap; word-wrap: normal; text-wrap: suppress; color: black; }\n"
236 " .tree span { -moz-border-radius: 6px; display: block; float: left;\n"
237 " line-height: 16px; height: 16px; vertical-align: middle; padding: 0 1px; }\n"
238 " .tree img { vertical-align: top; padding-right: 1px; }\n"
239 " .tree .current { background-color: " << KGlobalSettings::highlightColor().name() << "; "
240 "-moz-border-radius: 3px 0 0 3px; border-radius: 3px 0 0 3px; color: " << KGlobalSettings::highlightedTextColor().name() << "; }\n"
241 " .basketSurrounder { margin-left: 152px; _margin: 0; _float: right; }\n";
243 stream <<
244 " .basket { background-color: " << basket->backgroundColor().name() << "; border: solid " << borderColor << " 1px; "
245 "font: " << Tools::cssFontDefinition(basket->Q3ScrollView::font()) << "; color: " << basket->textColor().name() << "; padding: 1px; width: 100%; }\n"
246 " table.basket { border-collapse: collapse; }\n"
247 " .basket * { padding: 0; margin: 0; }\n"
248 " .basket table { width: 100%; border-spacing: 0; _border-collapse: collapse; }\n"
249 " .column { vertical-align: top; }\n"
250 " .columnHandle { width: " << Note::RESIZER_WIDTH << "px; background: transparent url('" << imagesFolderName << "column_handle_" << backgroundColorName << ".png') repeat-y; }\n"
251 " .group { margin: 0; padding: 0; border-collapse: collapse; width: 100% }\n"
252 " .groupHandle { margin: 0; width: " << Note::GROUP_WIDTH << "px; text-align: center; }\n"
253 " .note { padding: 1px 2px; background: " << bottomBgColor.name() << " url('" << imagesFolderName << gradientImageFileName << "')"
254 " repeat-x; border-top: solid " << topBgColor.name() <<
255 " 1px; border-bottom: solid " << Tools::mixColor(topBgColor, bottomBgColor).name() <<
256 " 1px; width: 100%; }\n"
257 " .tags { width: 1px; white-space: nowrap; }\n"
258 " .tags img { padding-right: 2px; }\n"
259 << LinkLook::soundLook->toCSS("sound", basket->textColor())
260 << LinkLook::fileLook->toCSS("file", basket->textColor())
261 << LinkLook::localLinkLook->toCSS("local", basket->textColor())
262 << LinkLook::networkLinkLook->toCSS("network", basket->textColor())
263 << LinkLook::launcherLook->toCSS("launcher", basket->textColor())
265 " .unknown { margin: 1px 2px; border: 1px solid " << borderColor << "; -moz-border-radius: 4px; }\n";
266 Q3ValueList<State*> states = basket->usedStates();
267 QString statesCss;
268 for (State::List::Iterator it = states.begin(); it != states.end(); ++it)
269 statesCss += (*it)->toCSS(imagesFolderPath, imagesFolderName, basket->Q3ScrollView::font());
270 stream <<
271 statesCss <<
272 " .credits { text-align: right; margin: 3px 0 0 0; _margin-top: -17px; font-size: 80%; color: " << borderColor << "; }\n"
273 " </style>\n"
274 " <title>" << Tools::textToHTMLWithoutP(basket->basketName()) << "</title>\n"
275 " <link rel=\"shortcut icon\" type=\"image/png\" href=\"" << basketIcon16 << "\">\n";
276 // Create the column handle image:
277 QPixmap columnHandle(Note::RESIZER_WIDTH, 50);
278 painter.begin(&columnHandle);
279 Note::drawInactiveResizer(&painter, 0, 0, columnHandle.height(), basket->backgroundColor(), /*column=*/true);
280 painter.end();
281 columnHandle.save(imagesFolderPath + "column_handle_" + backgroundColorName + ".png", "PNG");
283 stream <<
284 " </head>\n"
285 " <body>\n"
286 " <h1><img src=\"" << basketIcon32 << "\" width=\"32\" height=\"32\" alt=\"\"> " << Tools::textToHTMLWithoutP(basket->basketName()) << "</h1>\n";
288 if (withBasketTree)
289 writeBasketTree(basket);
291 // If filtering, only export filtered notes, inform to the user:
292 // TODO: Filtering tags too!!
293 // TODO: Make sure only filtered notes are exported!
294 // if (decoration()->filterData().isFiltering)
295 // stream <<
296 // " <p>" << i18n("Notes matching the filter &quot;%1&quot;:").arg(Tools::textToHTMLWithoutP(decoration()->filterData().string)) << "</p>\n";
298 stream <<
299 " <div class=\"basketSurrounder\">\n";
301 if (basket->isColumnsLayout())
302 stream <<
303 " <table class=\"basket\">\n"
304 " <tr>\n";
305 else
306 stream <<
307 " <div class=\"basket\" style=\"position: relative; height: " << basket->contentsHeight() << "px; width: " << basket->contentsWidth() << "px; min-width: 100%;\">\n";
309 for (Note *note = basket->firstNote(); note; note = note->next())
310 exportNote(note, /*indent=*/(basket->isFreeLayout() ? 4 : 5));
312 // Output the footer:
313 if (basket->isColumnsLayout())
314 stream <<
315 " </tr>\n"
316 " </table>\n";
317 else
318 stream <<
319 " </div>\n";
320 stream << QString(
321 " </div>\n"
322 " <p class=\"credits\">%1</p>\n").arg(
323 i18n("Made with %1, a KDE tool to take notes and keep information at hand.")
324 .arg("<a href=\"http://basket.kde.org/\">%1</a> %2")
325 .arg(kapp->aboutData()->programName(), VERSION));
327 // Copy a transparent GIF image in the folder, needed for the JavaScript hack:
328 QString gifFileName = "spacer.gif";
329 QFile transGIF(imagesFolderPath + gifFileName);
330 if (!transGIF.exists() && transGIF.open(QIODevice::WriteOnly)) {
331 QDataStream streamGIF(&transGIF);
332 // This is a 1px*1px transparent GIF image:
333 const uchar blankGIF[] = {
334 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x0a, 0x00, 0x0a, 0x00,
335 0x80, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x21,
336 0xfe, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20,
337 0x77, 0x69, 0x74, 0x68, 0x20, 0x54, 0x68, 0x65, 0x20, 0x47,
338 0x49, 0x4d, 0x50, 0x00, 0x21, 0xf9, 0x04, 0x01, 0x0a, 0x00,
339 0x01, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a,
340 0x00, 0x00, 0x02, 0x08, 0x8c, 0x8f, 0xa9, 0xcb, 0xed, 0x0f,
341 0x63, 0x2b, 0x00, 0x3b };
342 streamGIF.writeRawBytes((const char*)blankGIF, (unsigned int)74);
343 transGIF.close();
345 stream <<
346 " <!--[if lt IE 7]>\n"
347 " <script>\n"
348 " function fixPng(img) {\n"
349 " if (!img.style.filter) {\n"
350 " img.style.filter = \"progid:DXImageTransform.Microsoft.AlphaImageLoader(src='\" + img.src + \"')\";\n"
351 " img.src = \"" << imagesFolderName << gifFileName << "\";\n"
352 " }\n"
353 " }\n"
354 " for (i = document.images.length - 1; i >= 0; i -= 1) {\n"
355 " var img = document.images[i];\n"
356 " if (img.src.substr(img.src.length - 4) == \".png\")\n"
357 " if (img.complete)\n"
358 " fixPng(img);\n"
359 " else\n"
360 " img.attachEvent(\"onload\", function() { fixPng(window.event.srcElement); });\n"
361 " }\n"
362 " </script>\n"
363 " <![endif]-->\n"
364 " </body>\n"
365 "</html>\n";
367 file.close();
368 stream.unsetDevice();
369 progress->advance(1); // Basket exportation finished
371 // Recursively export child baskets:
372 BasketListViewItem *item = Global::bnpView->listViewItemForBasket(basket);
373 if (item->firstChild()) {
374 for (BasketListViewItem *child = (BasketListViewItem*) item->firstChild(); child; child = (BasketListViewItem*) child->nextSibling()) {
375 exportBasket(child->basket(), /*isSubBasket=*/true);
380 void HTMLExporter::exportNote(Note *note, int indent)
382 QString spaces;
384 if (note->isColumn()) {
385 QString width = "";
386 if (false/*TODO: DEBUG AND REENABLE: hasResizer()*/) {
387 // As we cannot be precise in CSS (say eg. "width: 50%-40px;"),
388 // we output a percentage that is approximatively correct.
389 // For instance, we compute the currently used percentage of width in the basket
390 // and try make make it the same on a 1024*768 display in a Web browser:
391 int availableSpaceForColumnsInThisBasket = note->basket()->contentsWidth() - (note->basket()->columnsCount() - 1) * Note::RESIZER_WIDTH;
392 int availableSpaceForColumnsInBrowser = 1024 /* typical screen width */
393 - 25 /* window border and scrollbar width */
394 - 2 * 5 /* page margin */
395 - (note->basket()->columnsCount() - 1) * Note::RESIZER_WIDTH;
396 if (availableSpaceForColumnsInThisBasket <= 0)
397 availableSpaceForColumnsInThisBasket = 1;
398 int widthValue = (int)(availableSpaceForColumnsInBrowser * (double) note->groupWidth() / availableSpaceForColumnsInThisBasket);
399 if (widthValue <= 0)
400 widthValue = 1;
401 if (widthValue > 100)
402 widthValue = 100;
403 width = QString(" width=\"%1%\"").arg(QString::number(widthValue));
405 stream << spaces.fill(' ', indent) << "<td class=\"column\"" << width << ">\n";
407 // Export child notes:
408 for (Note *child = note->firstChild(); child; child = child->next()) {
409 stream << spaces.fill(' ', indent + 1);
410 exportNote(child, indent + 1);
411 stream << '\n';
414 stream << spaces.fill(' ', indent) << "</td>\n";
415 if (note->hasResizer())
416 stream << spaces.fill(' ', indent) << "<td class=\"columnHandle\"></td>\n";
417 return;
420 QString freeStyle;
421 if (note->isFree())
422 freeStyle = " style=\"position: absolute; left: " + QString::number(note->x()) + "px; top: " + QString::number(note->y()) + "px; width: " + QString::number(note->groupWidth()) + "px\"";
424 if (note->isGroup()) {
425 stream << '\n' << spaces.fill(' ', indent) << "<table" << freeStyle << ">\n"; // Note content is expected to be on the same HTML line, but NOT groups
426 int i = 0;
427 for (Note *child = note->firstChild(); child; child = child->next()) {
428 stream << spaces.fill(' ', indent);
429 if (i == 0)
430 stream << " <tr><td class=\"groupHandle\"><img src=\"" << imagesFolderName << (note->isFolded() ? "expand_group_" : "fold_group_") << backgroundColorName << ".png"
431 << "\" width=\"" << Note::EXPANDER_WIDTH << "\" height=\"" << Note::EXPANDER_HEIGHT << "\"></td>\n";
432 else if (i == 1)
433 stream << " <tr><td class=\"freeSpace\" rowspan=\"" << note->countDirectChilds() << "\"></td>\n";
434 else
435 stream << " <tr>\n";
436 stream << spaces.fill(' ', indent) << " <td>";
437 exportNote(child, indent + 3);
438 stream << "</td>\n"
439 << spaces.fill(' ', indent) << " </tr>\n";
440 ++i;
442 stream << '\n' << spaces.fill(' ', indent) << "</table>\n" /*<< spaces.fill(' ', indent - 1)*/;
443 } else {
444 // Additionnal class for the content (link, netword, color...):
445 QString additionnalClasses = note->content()->cssClass();
446 if (!additionnalClasses.isEmpty())
447 additionnalClasses = " " + additionnalClasses;
448 // Assign the style of each associted tags:
449 for (State::List::Iterator it = note->states().begin(); it != note->states().end(); ++it)
450 additionnalClasses += " tag_" + (*it)->id();
451 //stream << spaces.fill(' ', indent);
452 stream << "<table class=\"note" << additionnalClasses << "\"" << freeStyle << "><tr>";
453 if (note->emblemsCount() > 0) {
454 stream << "<td class=\"tags\"><nobr>";
455 for (State::List::Iterator it = note->states().begin(); it != note->states().end(); ++it)
456 if (!(*it)->emblem().isEmpty()) {
457 int emblemSize = 16;
458 QString iconFileName = copyIcon((*it)->emblem(), emblemSize);
459 stream << "<img src=\"" << iconsFolderName << iconFileName
460 << "\" width=\"" << emblemSize << "\" height=\"" << emblemSize
461 << "\" alt=\"" << (*it)->textEquivalent() << "\" title=\"" << (*it)->fullName() << "\">";
463 stream << "</nobr></td>";
465 stream << "<td>";
466 note->content()->exportToHTML(this, indent);
467 stream << "</td></tr></table>";
471 void HTMLExporter::writeBasketTree(Basket *currentBasket)
473 stream << " <ul class=\"tree\">\n";
474 writeBasketTree(currentBasket, exportedBasket, 3);
475 stream << " </ul>\n";
478 void HTMLExporter::writeBasketTree(Basket *currentBasket, Basket *basket, int indent)
480 // Compute variable HTML code:
481 QString spaces;
482 QString cssClass = (basket == currentBasket ? " class=\"current\"" : "");
483 QString link = "#";
484 if (currentBasket != basket) {
485 if (currentBasket == exportedBasket) {
486 link = basketsFolderName + basket->folderName().left(basket->folderName().length() - 1) + ".html";
487 } else if (basket == exportedBasket) {
488 link = "../../" + fileName;
489 } else {
490 link = basket->folderName().left(basket->folderName().length() - 1) + ".html";
493 QString spanStyle = "";
494 if (basket->backgroundColorSetting().isValid() || basket->textColorSetting().isValid()) {
495 spanStyle = " style=\"background-color: " + basket->backgroundColor().name() + "; color: " + basket->textColor().name() + "\"";
498 // Write the basket tree line:
499 stream <<
500 spaces.fill(' ', indent) << "<li><a" << cssClass << " href=\"" << link << "\">"
501 "<span" << spanStyle << " title=\"" << Tools::textToHTMLWithoutP(basket->basketName()) << "\">"
502 "<img src=\"" << iconsFolderName << copyIcon(basket->icon(), 16) << "\" width=\"16\" height=\"16\" alt=\"\">" << Tools::textToHTMLWithoutP(basket->basketName()) << "</span></a>";
504 // Write the sub-baskets lines & end the current one:
505 BasketListViewItem *item = Global::bnpView->listViewItemForBasket(basket);
506 if (item->firstChild() != 0) {
507 stream <<
508 "\n" <<
509 spaces.fill(' ', indent) << " <ul>\n";
510 for (BasketListViewItem *child = (BasketListViewItem*) item->firstChild(); child; child = (BasketListViewItem*) child->nextSibling())
511 writeBasketTree(currentBasket, child->basket(), indent + 2);
512 stream <<
513 spaces.fill(' ', indent) << " </ul>\n" <<
514 spaces.fill(' ', indent) << "</li>\n";
515 } else {
516 stream << "</li>\n";
520 /** Save an icon to a folder.
521 * If an icon with the same name already exist in the destination,
522 * it is assumed the icon is already copied, so no action is took.
523 * It is optimized so that you can have an empty folder receiving the icons
524 * and call copyIcon() each time you encounter one during export process.
526 QString HTMLExporter::copyIcon(const QString &iconName, int size)
528 if (iconName.isEmpty())
529 return "";
531 // Sometimes icon can be "favicons/www.kde.org", we replace the '/' with a '_'
532 QString fileName = iconName; // QString::replace() isn't const, so I must copy the string before
533 fileName = "ico" + QString::number(size) + "_" + fileName.replace("/", "_") + ".png";
534 QString fullPath = iconsFolderPath + fileName;
535 if (!QFile::exists(fullPath))
536 DesktopIcon(iconName, size).save(fullPath, "PNG");
537 return fileName;
540 /** Done: Sometimes we can call two times copyFile() with the same srcPath and dataFolderPath
541 * (eg. when exporting basket to HTML with two links to same filename
542 * (but not necesary same path, as in "/home/foo.txt" and "/foo.txt") )
543 * The first copy isn't yet started, so the dest file isn't created and this method
544 * returns the same filename !!!!!!!!!!!!!!!!!!!!
546 QString HTMLExporter::copyFile(const QString &srcPath, bool createIt)
548 QString fileName = Tools::fileNameForNewFile(KURL(srcPath).fileName(), dataFolderPath);
549 QString fullPath = dataFolderPath + fileName;
551 if (createIt) {
552 // We create the file to be sure another very near call to copyFile() willn't choose the same name:
553 QFile file(KURL(fullPath).path());
554 if (file.open(QIODevice::WriteOnly))
555 file.close();
556 // And then we copy the file AND overwriting the file we juste created:
557 new KIO::FileCopyJob(
558 KURL(srcPath), KURL(fullPath), 0666, /*move=*/false,
559 /*overwrite=*/true, /*resume=*/true, /*showProgress=*/false );
560 } else
561 /*KIO::CopyJob *copyJob = */KIO::copy(KURL(srcPath), KURL(fullPath)); // Do it as before
563 return fileName;