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 ***************************************************************************/
20
21 #include "htmlexporter.h"
22
23 #include "bnpview.h"
24 #include "basketlistview.h"
25 #include "basketscene.h"
26 #include "note.h"
27 #include "tools.h"
28 #include "config.h"
29 #include "linklabel.h"
30 #include "notecontent.h"
31
32 #include <KAboutData>
33 #include <KConfig>
34 #include <KConfigGroup>
35 #include <KMessageBox>
36 #include <KLocalizedString>
37 #include <KIconLoader>
38
39 #include <KIO/Job> //For KIO::file_copy
40 #include <KIO/CopyJob> //For KIO::copy
41
42 #include <QtCore/QDir>
43 #include <QtCore/QFile>
44 #include <QtCore/QTextStream>
45 #include <QtCore/QList>
46 #include <QtGui/QPainter>
47 #include <QtGui/QPixmap>
48 #include <QApplication>
49 #include <QFileDialog>
50 #include <QLocale>
51 #include <QProgressDialog>
52
HTMLExporter(BasketScene * basket)53 HTMLExporter::HTMLExporter(BasketScene *basket) : dialog(new QProgressDialog())
54 {
55 QDir dir;
56
57 // Compute a default file name & path:
58 KConfigGroup config = Global::config()->group("Export to HTML");
59 QString folder = config.readEntry("lastFolder", QDir::homePath()) + "/";
60 QString url = folder + QString(basket->basketName()).replace("/", "_") + ".html";
61
62 // Ask a file name & path to the user:
63 QString filter = "*.html *.htm|" + i18n("HTML Documents") + "\n*|" + i18n("All Files");
64 QString destination = url;
65 for (bool askAgain = true; askAgain;) {
66 // Ask:
67 destination = QFileDialog::getSaveFileName(NULL, i18n("Export to HTML"), destination, filter);
68 // User canceled?
69 if (destination.isEmpty())
70 return;
71 // File already existing? Ask for overriding:
72 if (dir.exists(destination)) {
73 int result = KMessageBox::questionYesNoCancel(
74 0,
75 "<qt>" + i18n("The file <b>%1</b> already exists. Do you really want to override it?",
76 QUrl::fromLocalFile(destination).fileName()),
77 i18n("Override File?"),
78 KGuiItem(i18n("&Override"), "document-save")
79 );
80 if (result == KMessageBox::Cancel)
81 return;
82 else if (result == KMessageBox::Yes)
83 askAgain = false;
84 } else
85 askAgain = false;
86 }
87
88 // Create the progress dialog that will always be shown during the export:
89 dialog->setWindowTitle(i18n("Export to HTML"));
90 dialog->setLabelText(i18n("Exporting to HTML. Please wait..."));
91 dialog->setCancelButton(NULL);
92 dialog->setAutoClose(true);
93 dialog->show();
94
95 // Remember the last folder used for HTML exporation:
96 config.writeEntry("lastFolder", QUrl::fromLocalFile(destination).adjusted(QUrl::RemoveFilename).path());
97 config.sync();
98
99 prepareExport(basket, destination);
100 exportBasket(basket, /*isSubBasketScene*/false);
101
102 dialog->setValue(dialog->value() + 1); // Finishing finished
103 }
104
~HTMLExporter()105 HTMLExporter::~HTMLExporter()
106 {
107 }
108
prepareExport(BasketScene * basket,const QString & fullPath)109 void HTMLExporter::prepareExport(BasketScene *basket, const QString &fullPath)
110 {
111 dialog->setRange(0,/*Preparation:*/1 + /*Finishing:*/1 + /*Basket:*/1 + /*SubBaskets:*/Global::bnpView->basketCount(Global::bnpView->listViewItemForBasket(basket)));
112 dialog->setValue(0);
113 qApp->processEvents();
114
115 // Remember the file path chozen by the user:
116 filePath = fullPath;
117 fileName = QUrl::fromLocalFile(fullPath).fileName();
118 exportedBasket = basket;
119 currentBasket = 0;
120
121 BasketListViewItem *item = Global::bnpView->listViewItemForBasket(basket);
122 withBasketTree = (item->childCount() >= 0);
123
124 // Create and empty the files folder:
125 QString filesFolderPath = i18nc("HTML export folder (files)", "%1_files", filePath) + "/"; // eg.: "/home/seb/foo.html_files/"
126 Tools::deleteRecursively(filesFolderPath);
127 QDir dir;
128 dir.mkdir(filesFolderPath);
129
130 // Create sub-folders:
131 iconsFolderPath = filesFolderPath + i18nc("HTML export folder (icons)", "icons") + "/"; // eg.: "/home/seb/foo.html_files/icons/"
132 imagesFolderPath = filesFolderPath + i18nc("HTML export folder (images)", "images") + "/"; // eg.: "/home/seb/foo.html_files/images/"
133 basketsFolderPath = filesFolderPath + i18nc("HTML export folder (baskets)", "baskets") + "/"; // eg.: "/home/seb/foo.html_files/baskets/"
134 dir.mkdir(iconsFolderPath);
135 dir.mkdir(imagesFolderPath);
136 dir.mkdir(basketsFolderPath);
137
138 dialog->setValue(dialog->value() + 1); // Preparation finished
139 }
140
exportBasket(BasketScene * basket,bool isSubBasket)141 void HTMLExporter::exportBasket(BasketScene *basket, bool isSubBasket)
142 {
143 if (!basket->isLoaded()) {
144 basket->load();
145 }
146
147 currentBasket = basket;
148
149 // Compute the absolute & relative paths for this basket:
150 filesFolderPath = i18nc("HTML export folder (files)", "%1_files", filePath) + "/";
151 if (isSubBasket) {
152 basketFilePath = basketsFolderPath + basket->folderName().left(basket->folderName().length() - 1) + ".html";
153 filesFolderName = "../";
154 dataFolderName = basket->folderName().left(basket->folderName().length() - 1) + "-" + i18nc("HTML export folder (data)", "data") + "/";
155 dataFolderPath = basketsFolderPath + dataFolderName;
156 basketsFolderName = "";
157 } else {
158 basketFilePath = filePath;
159 filesFolderName = i18nc("HTML export folder (files)", "%1_files", QUrl::fromLocalFile(filePath).fileName()) + "/";
160 dataFolderName = filesFolderName + i18nc("HTML export folder (data)", "data") + "/";
161 dataFolderPath = filesFolderPath + i18nc("HTML export folder (data)", "data") + "/";
162 basketsFolderName = filesFolderName + i18nc("HTML export folder (baskets)", "baskets") + "/";
163 }
164 iconsFolderName = (isSubBasket ? "../" : filesFolderName) + i18nc("HTML export folder (icons)", "icons") + "/"; // eg.: "foo.html_files/icons/" or "../icons/"
165 imagesFolderName = (isSubBasket ? "../" : filesFolderName) + i18nc("HTML export folder (images)", "images") + "/"; // eg.: "foo.html_files/images/" or "../images/"
166
167 qDebug() << "Exporting ================================================";
168 qDebug() << " filePath:" << filePath;
169 qDebug() << " basketFilePath:" << basketFilePath;
170 qDebug() << " filesFolderPath:" << filesFolderPath;
171 qDebug() << " filesFolderName:" << filesFolderName;
172 qDebug() << " iconsFolderPath:" << iconsFolderPath;
173 qDebug() << " iconsFolderName:" << iconsFolderName;
174 qDebug() << " imagesFolderPath:" << imagesFolderPath;
175 qDebug() << " imagesFolderName:" << imagesFolderName;
176 qDebug() << " dataFolderPath:" << dataFolderPath;
177 qDebug() << " dataFolderName:" << dataFolderName;
178 qDebug() << " basketsFolderPath:" << basketsFolderPath;
179 qDebug() << " basketsFolderName:" << basketsFolderName;
180
181 // Create the data folder for this basket:
182 QDir dir;
183 dir.mkdir(dataFolderPath);
184
185 backgroundColorName = basket->backgroundColor().name().toLower().mid(1);
186
187 // Generate basket icons:
188 QString basketIcon16 = iconsFolderName + copyIcon(basket->icon(), 16);
189 QString basketIcon32 = iconsFolderName + copyIcon(basket->icon(), 32);
190
191 // Generate the [+] image for groups:
192 QPixmap expandGroup(Note::EXPANDER_WIDTH, Note::EXPANDER_HEIGHT);
193 expandGroup.fill(basket->backgroundColor());
194 QPainter painter(&expandGroup);
195 Note::drawExpander(&painter, 0, 0, basket->backgroundColor(), /*expand=*/true, basket);
196 painter.end();
197 expandGroup.save(imagesFolderPath + "expand_group_" + backgroundColorName + ".png", "PNG");
198
199 // Generate the [-] image for groups:
200 QPixmap foldGroup(Note::EXPANDER_WIDTH, Note::EXPANDER_HEIGHT);
201 foldGroup.fill(basket->backgroundColor());
202 painter.begin(&foldGroup);
203 Note::drawExpander(&painter, 0, 0, basket->backgroundColor(), /*expand=*/false, basket);
204 painter.end();
205 foldGroup.save(imagesFolderPath + "fold_group_" + backgroundColorName + ".png", "PNG");
206
207 // Open the file to write:
208 QFile file(basketFilePath);
209 if (!file.open(QIODevice::WriteOnly))
210 return;
211 stream.setDevice(&file);
212 stream.setCodec("UTF-8");
213
214 // Compute the colors to draw dragient for notes:
215 QColor topBgColor;
216 QColor bottomBgColor;
217 Note::getGradientColors(basket->backgroundColor(), &topBgColor, &bottomBgColor);
218 // Compute the gradient image for notes:
219 QString gradientImageFileName = BasketScene::saveGradientBackground(basket->backgroundColor(), basket->QGraphicsScene::font(), imagesFolderPath);
220
221 // Output the header:
222 QString borderColor = Tools::mixColor(basket->backgroundColor(), basket->textColor()).name();
223 stream <<
224 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n"
225 "<html>\n"
226 " <head>\n"
227 " <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n"
228 " <meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\"><meta name=\"Generator\" content=\""
229 << QGuiApplication::applicationDisplayName() << " " << VERSION << " " << KAboutData::applicationData().homepage() << "\">\n"
230 " <style type=\"text/css\">\n"
231 // " @media print {\n"
232 // " span.printable { display: inline; }\n"
233 // " }\n"
234 " body { margin: 10px; font: 11px sans-serif; }\n" // TODO: Use user font
235 " h1 { text-align: center; }\n"
236 " img { border: none; vertical-align: middle; }\n";
237 if (withBasketTree) {
238 stream <<
239 " .tree { margin: 0; padding: 1px 0 1px 1px; width: 150px; _width: 149px; overflow: hidden; float: left; }\n"
240 " .tree ul { margin: 0 0 0 10px; padding: 0; }\n"
241 " .tree li { padding: 0; margin: 0; list-style: none; }\n"
242 " .tree a { display: block; padding: 1px; height: 16px; text-decoration: none;\n"
243 " white-space: nowrap; word-wrap: normal; text-wrap: suppress; color: black; }\n"
244 " .tree span { -moz-border-radius: 6px; display: block; float: left;\n"
245 " line-height: 16px; height: 16px; vertical-align: middle; padding: 0 1px; }\n"
246 " .tree img { vertical-align: top; padding-right: 1px; }\n"
247 " .tree .current { background-color: " << qApp->palette().color(QPalette::Highlight).name() << "; "
248 "-moz-border-radius: 3px 0 0 3px; border-radius: 3px 0 0 3px; color: " << qApp->palette().color(QPalette::Highlight).name() << "; }\n"
249 " .basketSurrounder { margin-left: 152px; _margin: 0; _float: right; }\n";
250 }
251 stream <<
252 " .basket { background-color: " << basket->backgroundColor().name() << "; border: solid " << borderColor << " 1px; "
253 "font: " << Tools::cssFontDefinition(basket->QGraphicsScene::font()) << "; color: " << basket->textColor().name() << "; padding: 1px; width: 100%; }\n"
254 " table.basket { border-collapse: collapse; }\n"
255 " .basket * { padding: 0; margin: 0; }\n"
256 " .basket table { width: 100%; border-spacing: 0; _border-collapse: collapse; }\n"
257 " .column { vertical-align: top; }\n"
258 " .columnHandle { width: " << Note::RESIZER_WIDTH << "px; background: transparent url('" << imagesFolderName << "column_handle_" << backgroundColorName << ".png') repeat-y; }\n"
259 " .group { margin: 0; padding: 0; border-collapse: collapse; width: 100% }\n"
260 " .groupHandle { margin: 0; width: " << Note::GROUP_WIDTH << "px; text-align: center; }\n"
261 " .note { padding: 1px 2px; background: " << bottomBgColor.name() << " url('" << imagesFolderName << gradientImageFileName << "')"
262 " repeat-x; border-top: solid " << topBgColor.name() <<
263 " 1px; border-bottom: solid " << Tools::mixColor(topBgColor, bottomBgColor).name() <<
264 " 1px; width: 100%; }\n"
265 " .tags { width: 1px; white-space: nowrap; }\n"
266 " .tags img { padding-right: 2px; }\n"
267 << LinkLook::soundLook->toCSS("sound", basket->textColor())
268 << LinkLook::fileLook->toCSS("file", basket->textColor())
269 << LinkLook::localLinkLook->toCSS("local", basket->textColor())
270 << LinkLook::networkLinkLook->toCSS("network", basket->textColor())
271 << LinkLook::launcherLook->toCSS("launcher", basket->textColor())
272 << LinkLook::crossReferenceLook->toCSS("cross_reference", basket->textColor())
273 <<
274 " .unknown { margin: 1px 2px; border: 1px solid " << borderColor << "; -moz-border-radius: 4px; }\n";
275 QList<State*> states = basket->usedStates();
276 QString statesCss;
277 for (State::List::Iterator it = states.begin(); it != states.end(); ++it)
278 statesCss += (*it)->toCSS(imagesFolderPath, imagesFolderName, basket->QGraphicsScene::font());
279 stream <<
280 statesCss <<
281 " .credits { text-align: right; margin: 3px 0 0 0; _margin-top: -17px; font-size: 80%; color: " << borderColor << "; }\n"
282 " </style>\n"
283 " <title>" << Tools::textToHTMLWithoutP(basket->basketName()) << "</title>\n"
284 " <link rel=\"shortcut icon\" type=\"image/png\" href=\"" << basketIcon16 << "\">\n";
285 // Create the column handle image:
286 QPixmap columnHandle(Note::RESIZER_WIDTH, 50);
287 painter.begin(&columnHandle);
288 Note::drawInactiveResizer(&painter, 0, 0, columnHandle.height(), basket->backgroundColor(), /*column=*/true);
289 painter.end();
290 columnHandle.save(imagesFolderPath + "column_handle_" + backgroundColorName + ".png", "PNG");
291
292 stream <<
293 " </head>\n"
294 " <body>\n"
295 " <h1><img src=\"" << basketIcon32 << "\" width=\"32\" height=\"32\" alt=\"\"> " << Tools::textToHTMLWithoutP(basket->basketName()) << "</h1>\n";
296
297 if (withBasketTree)
298 writeBasketTree(basket);
299
300 // If filtering, only export filtered notes, inform to the user:
301 // TODO: Filtering tags too!!
302 // TODO: Make sure only filtered notes are exported!
303 // if (decoration()->filterData().isFiltering)
304 // stream <<
305 // " <p>" << i18n("Notes matching the filter "%1":", Tools::textToHTMLWithoutP(decoration()->filterData().string)) << "</p>\n";
306
307 stream <<
308 " <div class=\"basketSurrounder\">\n";
309
310 if (basket->isColumnsLayout())
311 stream <<
312 " <table class=\"basket\">\n"
313 " <tr>\n";
314 else
315 stream <<
316 " <div class=\"basket\" style=\"position: relative; height: " << basket->sceneRect().height() << "px; width: " << basket->sceneRect().width() << "px; min-width: 100%;\">\n";
317
318 for (Note *note = basket->firstNote(); note; note = note->next())
319 exportNote(note, /*indent=*/(basket->isFreeLayout() ? 4 : 5));
320
321 // Output the footer:
322 if (basket->isColumnsLayout())
323 stream <<
324 " </tr>\n"
325 " </table>\n";
326 else
327 stream <<
328 " </div>\n";
329 stream << QString(
330 " </div>\n"
331 " <p class=\"credits\">%1</p>\n").arg(
332 i18n("Made with <a href=\"%1\">%2</a> %3, a tool to take notes and keep information at hand.",
333 KAboutData::applicationData().homepage(), QGuiApplication::applicationDisplayName(), VERSION));
334
335 stream <<
336 " </body>\n"
337 "</html>\n";
338
339 file.close();
340 stream.setDevice(0);
341 dialog->setValue(dialog->value() + 1); // Basket exportation finished
342
343 // Recursively export child baskets:
344 BasketListViewItem *item = Global::bnpView->listViewItemForBasket(basket);
345 if (item->childCount() >= 0) {
346 for (int i = 0; i < item->childCount(); i++) {
347 exportBasket(((BasketListViewItem *)item->child(i))->basket(), /*isSubBasket=*/true);
348 }
349 }
350 }
351
exportNote(Note * note,int indent)352 void HTMLExporter::exportNote(Note *note, int indent)
353 {
354 QString spaces;
355
356 if (note->isColumn()) {
357 QString width = "";
358 if (false/*TODO: DEBUG AND REENABLE: hasResizer()*/) {
359 // As we cannot be precise in CSS (say eg. "width: 50%-40px;"),
360 // we output a percentage that is approximatively correct.
361 // For instance, we compute the currently used percentage of width in the basket
362 // and try make make it the same on a 1024*768 display in a Web browser:
363 int availableSpaceForColumnsInThisBasket = note->basket()->sceneRect().width() - (note->basket()->columnsCount() - 1) * Note::RESIZER_WIDTH;
364 int availableSpaceForColumnsInBrowser = 1024 /* typical screen width */
365 - 25 /* window border and scrollbar width */
366 - 2 * 5 /* page margin */
367 - (note->basket()->columnsCount() - 1) * Note::RESIZER_WIDTH;
368 if (availableSpaceForColumnsInThisBasket <= 0)
369 availableSpaceForColumnsInThisBasket = 1;
370 int widthValue = (int)(availableSpaceForColumnsInBrowser * (double) note->groupWidth() / availableSpaceForColumnsInThisBasket);
371 if (widthValue <= 0)
372 widthValue = 1;
373 if (widthValue > 100)
374 widthValue = 100;
375 width = QString(" width=\"%1%\"").arg(QString::number(widthValue));
376 }
377 stream << spaces.fill(' ', indent) << "<td class=\"column\"" << width << ">\n";
378
379 // Export child notes:
380 for (Note *child = note->firstChild(); child; child = child->next()) {
381 stream << spaces.fill(' ', indent + 1);
382 exportNote(child, indent + 1);
383 stream << '\n';
384 }
385
386 stream << spaces.fill(' ', indent) << "</td>\n";
387 if (note->hasResizer())
388 stream << spaces.fill(' ', indent) << "<td class=\"columnHandle\"></td>\n";
389 return;
390 }
391
392 QString freeStyle;
393 if (note->isFree())
394 freeStyle = " style=\"position: absolute; left: " + QString::number(note->x()) + "px; top: " + QString::number(note->y()) + "px; width: " + QString::number(note->groupWidth()) + "px\"";
395
396 if (note->isGroup()) {
397 stream << '\n' << spaces.fill(' ', indent) << "<table" << freeStyle << ">\n"; // Note content is expected to be on the same HTML line, but NOT groups
398 int i = 0;
399 for (Note *child = note->firstChild(); child; child = child->next()) {
400 stream << spaces.fill(' ', indent);
401 if (i == 0)
402 stream << " <tr><td class=\"groupHandle\"><img src=\"" << imagesFolderName << (note->isFolded() ? "expand_group_" : "fold_group_") << backgroundColorName << ".png"
403 << "\" width=\"" << Note::EXPANDER_WIDTH << "\" height=\"" << Note::EXPANDER_HEIGHT << "\"></td>\n";
404 else if (i == 1)
405 stream << " <tr><td class=\"freeSpace\" rowspan=\"" << note->countDirectChilds() << "\"></td>\n";
406 else
407 stream << " <tr>\n";
408 stream << spaces.fill(' ', indent) << " <td>";
409 exportNote(child, indent + 3);
410 stream << "</td>\n"
411 << spaces.fill(' ', indent) << " </tr>\n";
412 ++i;
413 }
414 stream << '\n' << spaces.fill(' ', indent) << "</table>\n" /*<< spaces.fill(' ', indent - 1)*/;
415 } else {
416 // Additional class for the content (link, netword, color...):
417 QString additionalClasses = note->content()->cssClass();
418 if (!additionalClasses.isEmpty())
419 additionalClasses = " " + additionalClasses;
420 // Assign the style of each associted tags:
421 for (State::List::Iterator it = note->states().begin(); it != note->states().end(); ++it)
422 additionalClasses += " tag_" + (*it)->id();
423 //stream << spaces.fill(' ', indent);
424 stream << "<table class=\"note" << additionalClasses << "\"" << freeStyle << "><tr>";
425 if (note->emblemsCount() > 0) {
426 stream << "<td class=\"tags\"><nobr>";
427 for (State::List::Iterator it = note->states().begin(); it != note->states().end(); ++it)
428 if (!(*it)->emblem().isEmpty()) {
429 int emblemSize = 16;
430 QString iconFileName = copyIcon((*it)->emblem(), emblemSize);
431 stream << "<img src=\"" << iconsFolderName << iconFileName
432 << "\" width=\"" << emblemSize << "\" height=\"" << emblemSize
433 << "\" alt=\"" << (*it)->textEquivalent() << "\" title=\"" << (*it)->fullName() << "\">";
434 }
435 stream << "</nobr></td>";
436 }
437 stream << "<td>";
438 note->content()->exportToHTML(this, indent);
439 stream << "</td></tr></table>";
440 }
441 }
442
writeBasketTree(BasketScene * currentBasket)443 void HTMLExporter::writeBasketTree(BasketScene *currentBasket)
444 {
445 stream << " <ul class=\"tree\">\n";
446 writeBasketTree(currentBasket, exportedBasket, 3);
447 stream << " </ul>\n";
448 }
449
writeBasketTree(BasketScene * currentBasket,BasketScene * basket,int indent)450 void HTMLExporter::writeBasketTree(BasketScene *currentBasket, BasketScene *basket, int indent)
451 {
452 // Compute variable HTML code:
453 QString spaces;
454 QString cssClass = (basket == currentBasket ? " class=\"current\"" : "");
455 QString link = "#";
456 if (currentBasket != basket) {
457 if (currentBasket == exportedBasket) {
458 link = basketsFolderName + basket->folderName().left(basket->folderName().length() - 1) + ".html";
459 } else if (basket == exportedBasket) {
460 link = "../../" + fileName;
461 } else {
462 link = basket->folderName().left(basket->folderName().length() - 1) + ".html";
463 }
464 }
465 QString spanStyle = "";
466 if (basket->backgroundColorSetting().isValid() || basket->textColorSetting().isValid()) {
467 spanStyle = " style=\"background-color: " + basket->backgroundColor().name() + "; color: " + basket->textColor().name() + "\"";
468 }
469
470 // Write the basket tree line:
471 stream <<
472 spaces.fill(' ', indent) << "<li><a" << cssClass << " href=\"" << link << "\">"
473 "<span" << spanStyle << " title=\"" << Tools::textToHTMLWithoutP(basket->basketName()) << "\">"
474 "<img src=\"" << iconsFolderName << copyIcon(basket->icon(), 16) << "\" width=\"16\" height=\"16\" alt=\"\">" << Tools::textToHTMLWithoutP(basket->basketName()) << "</span></a>";
475
476 // Write the sub-baskets lines & end the current one:
477 BasketListViewItem *item = Global::bnpView->listViewItemForBasket(basket);
478 if (item->childCount() >= 0) {
479 stream <<
480 "\n" <<
481 spaces.fill(' ', indent) << " <ul>\n";
482 for (int i = 0; i < item->childCount(); i++)
483 writeBasketTree(currentBasket, ((BasketListViewItem*)item->child(i))->basket(), indent + 2);
484 stream <<
485 spaces.fill(' ', indent) << " </ul>\n" <<
486 spaces.fill(' ', indent) << "</li>\n";
487 } else {
488 stream << "</li>\n";
489 }
490 }
491
492 /** Save an icon to a folder.
493 * If an icon with the same name already exist in the destination,
494 * it is assumed the icon is already copied, so no action is took.
495 * It is optimized so that you can have an empty folder receiving the icons
496 * and call copyIcon() each time you encounter one during export process.
497 */
copyIcon(const QString & iconName,int size)498 QString HTMLExporter::copyIcon(const QString &iconName, int size)
499 {
500 if (iconName.isEmpty())
501 return "";
502
503 // Sometimes icon can be "favicons/www.kde.org", we replace the '/' with a '_'
504 QString fileName = iconName; // QString::replace() isn't const, so I must copy the string before
505 fileName = "ico" + QString::number(size) + "_" + fileName.replace("/", "_") + ".png";
506 QString fullPath = iconsFolderPath + fileName;
507 if (!QFile::exists(fullPath))
508 DesktopIcon(iconName, size).save(fullPath, "PNG");
509 return fileName;
510 }
511
512 /** Done: Sometimes we can call two times copyFile() with the same srcPath and dataFolderPath
513 * (eg. when exporting basket to HTML with two links to same filename
514 * (but not necesary same path, as in "/home/foo.txt" and "/foo.txt") )
515 * The first copy isn't yet started, so the dest file isn't created and this method
516 * returns the same filename !!!!!!!!!!!!!!!!!!!!
517 */
copyFile(const QString & srcPath,bool createIt)518 QString HTMLExporter::copyFile(const QString &srcPath, bool createIt)
519 {
520 QString fileName = Tools::fileNameForNewFile(QUrl::fromLocalFile(srcPath).fileName(), dataFolderPath);
521 QString fullPath = dataFolderPath + fileName;
522
523 if (!currentBasket->isEncrypted()){
524 if (createIt) {
525 // We create the file to be sure another very near call to copyFile() willn't choose the same name:
526 QFile file(QUrl::fromLocalFile(fullPath).path());
527 if (file.open(QIODevice::WriteOnly))
528 file.close();
529 // And then we copy the file AND overwriting the file we juste created:
530 KIO::file_copy(QUrl::fromLocalFile(srcPath), QUrl::fromLocalFile(fullPath), 0666, KIO::HideProgressInfo | KIO::Resume | KIO::Overwrite);
531 } else {
532 /*KIO::CopyJob *copyJob = */KIO::copy(QUrl::fromLocalFile(srcPath), QUrl::fromLocalFile(fullPath), KIO::DefaultFlags); // Do it as before
533 }
534 } else {
535 QByteArray array;
536 bool success = currentBasket->loadFromFile(srcPath, &array);
537
538 if (success){
539 saveToFile(fullPath, array);
540 } else {
541 qDebug() << "Unable to load encrypted file " << srcPath;
542 }
543 }
544
545 return fileName;
546 }
547
saveToFile(const QString & fullPath,const QByteArray & array)548 void HTMLExporter::saveToFile(const QString& fullPath, const QByteArray& array)
549 {
550 QFile file(QUrl::fromLocalFile(fullPath).path());
551 if (file.open(QIODevice::WriteOnly)){
552 file.write(array, array.size());
553 file.close();
554 } else {
555 qDebug() << "Unable to open file for writing: " << fullPath;
556 }
557 }
558