1 /***************************************************************************
2 * Copyright (C) 2003 by Sébastien Laoût *
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. *
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. *
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"
23 #include "basketlistview.h"
30 #include <kapplication.h>
33 #include <kiconloader.h>
34 #include <kfiledialog.h>
35 #include <kmessagebox.h>
40 #include <Q3TextStream>
41 #include <Q3ValueList>
43 #include <kglobalsettings.h>
44 #include <kprogress.h>
46 HTMLExporter::HTMLExporter(Basket
*basket
)
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
; ) {
61 destination
= KFileDialog::getSaveFileName(destination
, filter
, 0, i18n("Export to HTML"));
63 if (destination
.isEmpty())
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
)
76 else if (result
== KMessageBox::Yes
)
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);
87 progress
= dialog
.progressBar();
89 // Remember the last folder used for HTML exporation:
90 config
->writeEntry("lastFolder", KURL(destination
).directory());
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:
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
);
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
136 void HTMLExporter::exportBasket(Basket
*basket
, bool isSubBasket
)
138 if (!basket
->isLoaded()) {
142 // Compute the absolute & relative paths for this basket:
143 filesFolderPath
= i18n("HTML export folder (files)", "%1_files").arg(filePath
) + "/";
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
= "";
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:
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
);
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
);
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
))
204 stream
.setDevice(&file
);
205 stream
.setEncoding(Q3TextStream::UnicodeUTF8
);
207 // Compute the colors to draw dragient for notes:
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();
217 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\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"
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
) {
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";
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();
268 for (State::List::Iterator it
= states
.begin(); it
!= states
.end(); ++it
)
269 statesCss
+= (*it
)->toCSS(imagesFolderPath
, imagesFolderName
, basket
->Q3ScrollView::font());
272 " .credits { text-align: right; margin: 3px 0 0 0; _margin-top: -17px; font-size: 80%; color: " << borderColor
<< "; }\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);
281 columnHandle
.save(imagesFolderPath
+ "column_handle_" + backgroundColorName
+ ".png", "PNG");
286 " <h1><img src=\"" << basketIcon32
<< "\" width=\"32\" height=\"32\" alt=\"\"> " << Tools::textToHTMLWithoutP(basket
->basketName()) << "</h1>\n";
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)
296 // " <p>" << i18n("Notes matching the filter "%1":").arg(Tools::textToHTMLWithoutP(decoration()->filterData().string)) << "</p>\n";
299 " <div class=\"basketSurrounder\">\n";
301 if (basket
->isColumnsLayout())
303 " <table class=\"basket\">\n"
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())
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);
346 " <!--[if lt IE 7]>\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"
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"
360 " img.attachEvent(\"onload\", function() { fixPng(window.event.srcElement); });\n"
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
)
384 if (note
->isColumn()) {
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
);
401 if (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);
414 stream
<< spaces
.fill(' ', indent
) << "</td>\n";
415 if (note
->hasResizer())
416 stream
<< spaces
.fill(' ', indent
) << "<td class=\"columnHandle\"></td>\n";
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
427 for (Note
*child
= note
->firstChild(); child
; child
= child
->next()) {
428 stream
<< spaces
.fill(' ', indent
);
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";
433 stream
<< " <tr><td class=\"freeSpace\" rowspan=\"" << note
->countDirectChilds() << "\"></td>\n";
436 stream
<< spaces
.fill(' ', indent
) << " <td>";
437 exportNote(child
, indent
+ 3);
439 << spaces
.fill(' ', indent
) << " </tr>\n";
442 stream
<< '\n' << spaces
.fill(' ', indent
) << "</table>\n" /*<< spaces.fill(' ', indent - 1)*/;
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()) {
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>";
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:
482 QString cssClass
= (basket
== currentBasket
? " class=\"current\"" : "");
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
;
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:
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) {
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);
513 spaces
.fill(' ', indent
) << " </ul>\n" <<
514 spaces
.fill(' ', indent
) << "</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())
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");
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
;
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
))
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 );
561 /*KIO::CopyJob *copyJob = */KIO::copy(KURL(srcPath
), KURL(fullPath
)); // Do it as before