~ubuntu-branches/debian/jessie/basket/jessie

« back to all changes in this revision

Viewing changes to src/archive.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Sune Vuorela
  • Date: 2008-06-25 20:11:23 UTC
  • mfrom: (4.1.11 intrepid)
  • Revision ID: james.westby@ubuntu.com-20080625201123-06wsi9dla3rs3486
Tags: 1.0.2-5
* Also allow automake 1.10 usage - and just build-dep on automake i
  (Closes: #487981)
* Bye bye ana

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/***************************************************************************
 
2
 *   Copyright (C) 2006 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 <qstring.h>
 
22
#include <qstringlist.h>
 
23
#include <qvaluelist.h>
 
24
#include <qmap.h>
 
25
#include <qdir.h>
 
26
#include <ktar.h>
 
27
#include <qdom.h>
 
28
#include <kmessagebox.h>
 
29
#include <qpixmap.h>
 
30
#include <qpainter.h>
 
31
#include <kstandarddirs.h>
 
32
#include <kapplication.h>
 
33
#include <kiconloader.h>
 
34
#include <kprogress.h>
 
35
#include <kmainwindow.h>
 
36
 
 
37
#include "archive.h"
 
38
#include "global.h"
 
39
#include "bnpview.h"
 
40
#include "basket.h"
 
41
#include "basketlistview.h"
 
42
#include "basketfactory.h"
 
43
#include "tag.h"
 
44
#include "xmlwork.h"
 
45
#include "tools.h"
 
46
#include "backgroundmanager.h"
 
47
#include "formatimporter.h"
 
48
 
 
49
#include <iostream>
 
50
 
 
51
void Archive::save(Basket *basket, bool withSubBaskets, const QString &destination)
 
52
{
 
53
        QDir dir;
 
54
 
 
55
        KProgressDialog dialog(0, 0, i18n("Save as Basket Archive"), i18n("Saving as basket archive. Please wait..."), /*Not modal, for password dialogs!*/false);
 
56
        dialog.showCancelButton(false);
 
57
        dialog.setAutoClose(true);
 
58
        dialog.show();
 
59
        KProgress *progress = dialog.progressBar();
 
60
        progress->setTotalSteps(/*Preparation:*/1 + /*Finishing:*/1 + /*Basket:*/1 + /*SubBaskets:*/(withSubBaskets ? Global::bnpView->basketCount(Global::bnpView->listViewItemForBasket(basket)) : 0));
 
61
        progress->setValue(0);
 
62
 
 
63
        // Create the temporar folder:
 
64
        QString tempFolder = Global::savesFolder() + "temp-archive/";
 
65
        dir.mkdir(tempFolder);
 
66
 
 
67
        // Create the temporar archive file:
 
68
        QString tempDestination = tempFolder + "temp-archive.tar.gz";
 
69
        KTar tar(tempDestination, "application/x-gzip");
 
70
        tar.open(IO_WriteOnly);
 
71
        tar.writeDir("baskets", "", "");
 
72
 
 
73
        progress->advance(1); // Preparation finished
 
74
        std::cout << "Preparation finished out of " << progress->totalSteps() << std::endl;
 
75
 
 
76
        // Copy the baskets data into the archive:
 
77
        QStringList backgrounds;
 
78
        saveBasketToArchive(basket, withSubBaskets, &tar, backgrounds, tempFolder, progress);
 
79
 
 
80
        // Create a Small baskets.xml Document:
 
81
        QDomDocument document("basketTree");
 
82
        QDomElement root = document.createElement("basketTree");
 
83
        document.appendChild(root);
 
84
        Global::bnpView->saveSubHierarchy(Global::bnpView->listViewItemForBasket(basket), document, root, withSubBaskets);
 
85
        Basket::safelySaveToFile(tempFolder + "baskets.xml", "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" + document.toString());
 
86
        tar.addLocalFile(tempFolder + "baskets.xml", "baskets/baskets.xml");
 
87
        dir.remove(tempFolder + "baskets.xml");
 
88
 
 
89
        // Save a Small tags.xml Document:
 
90
        QValueList<Tag*> tags;
 
91
        listUsedTags(basket, withSubBaskets, tags);
 
92
        Tag::saveTagsTo(tags, tempFolder + "tags.xml");
 
93
        tar.addLocalFile(tempFolder + "tags.xml", "tags.xml");
 
94
        dir.remove(tempFolder + "tags.xml");
 
95
 
 
96
        // Save Tag Emblems (in case they are loaded on a computer that do not have those icons):
 
97
        QString tempIconFile = tempFolder + "icon.png";
 
98
        for (Tag::List::iterator it = tags.begin(); it != tags.end(); ++it) {
 
99
                State::List states = (*it)->states();
 
100
                for (State::List::iterator it2 = states.begin(); it2 != states.end(); ++it2) {
 
101
                        State *state = (*it2);
 
102
                        QPixmap icon = kapp->iconLoader()->loadIcon(state->emblem(), KIcon::Small, 16, KIcon::DefaultState, /*path_store=*/0L, /*canReturnNull=*/true);
 
103
                        if (!icon.isNull()) {
 
104
                                icon.save(tempIconFile, "PNG");
 
105
                                QString iconFileName = state->emblem().replace('/', '_');
 
106
                                tar.addLocalFile(tempIconFile, "tag-emblems/" + iconFileName);
 
107
                        }
 
108
                }
 
109
        }
 
110
        dir.remove(tempIconFile);
 
111
 
 
112
        // Finish Tar.Gz Exportation:
 
113
        tar.close();
 
114
 
 
115
        // Computing the File Preview:
 
116
        Basket *previewBasket = basket; // FIXME: Use the first non-empty basket!
 
117
        QPixmap previewPixmap(previewBasket->visibleWidth(), previewBasket->visibleHeight());
 
118
        QPainter painter(&previewPixmap);
 
119
        // Save old state, and make the look clean ("smile, you are filmed!"):
 
120
        NoteSelection *selection = previewBasket->selectedNotes();
 
121
        previewBasket->unselectAll();
 
122
        Note *focusedNote = previewBasket->focusedNote();
 
123
        previewBasket->setFocusedNote(0);
 
124
        previewBasket->doHoverEffects(0, Note::None);
 
125
        // Take the screenshot:
 
126
        previewBasket->drawContents(&painter, 0, 0, previewPixmap.width(), previewPixmap.height());
 
127
        // Go back to the old look:
 
128
        previewBasket->selectSelection(selection);
 
129
        previewBasket->setFocusedNote(focusedNote);
 
130
        previewBasket->doHoverEffects();
 
131
        // End and save our splandid painting:
 
132
        painter.end();
 
133
        QImage previewImage = previewPixmap.convertToImage();
 
134
        const int PREVIEW_SIZE = 256;
 
135
        previewImage = previewImage.scale(PREVIEW_SIZE, PREVIEW_SIZE, QImage::ScaleMin);
 
136
        previewImage.save(tempFolder + "preview.png", "PNG");
 
137
 
 
138
        // Finaly Save to the Real Destination file:
 
139
        QFile file(destination);
 
140
        if (file.open(IO_WriteOnly)) {
 
141
                ulong previewSize = QFile(tempFolder + "preview.png").size();
 
142
                ulong archiveSize = QFile(tempDestination).size();
 
143
                QTextStream stream(&file);
 
144
                stream.setEncoding(QTextStream::Latin1);
 
145
                stream << "BasKetNP:archive\n"
 
146
                       << "version:0.6.1\n"
 
147
//                     << "read-compatible:0.6.1\n"
 
148
//                     << "write-compatible:0.6.1\n"
 
149
                       << "preview*:" << previewSize << "\n";
 
150
                // Copy the Preview File:
 
151
                const Q_ULONG BUFFER_SIZE = 1024;
 
152
                char *buffer = new char[BUFFER_SIZE];
 
153
                Q_LONG sizeRead;
 
154
                QFile previewFile(tempFolder + "preview.png");
 
155
                if (previewFile.open(IO_ReadOnly)) {
 
156
                        while ((sizeRead = previewFile.readBlock(buffer, BUFFER_SIZE)) > 0)
 
157
                                file.writeBlock(buffer, sizeRead);
 
158
                }
 
159
                stream << "archive*:" << archiveSize << "\n";
 
160
                // Copy the Archive File:
 
161
                QFile archiveFile(tempDestination);
 
162
                if (archiveFile.open(IO_ReadOnly)) {
 
163
                        while ((sizeRead = archiveFile.readBlock(buffer, BUFFER_SIZE)) > 0)
 
164
                                file.writeBlock(buffer, sizeRead);
 
165
                }
 
166
                // Clean Up:
 
167
                delete buffer;
 
168
                buffer = 0;
 
169
                file.close();
 
170
        }
 
171
 
 
172
        progress->advance(1); // Finishing finished
 
173
        std::cout << "Finishing finished" << std::endl;
 
174
 
 
175
        // Clean Up Everything:
 
176
        dir.remove(tempFolder + "preview.png");
 
177
        dir.remove(tempDestination);
 
178
        dir.rmdir(tempFolder);
 
179
}
 
180
 
 
181
void Archive::saveBasketToArchive(Basket *basket, bool recursive, KTar *tar, QStringList &backgrounds, const QString &tempFolder, KProgress *progress)
 
182
{
 
183
        // Basket need to be loaded for tags exportation.
 
184
        // We load it NOW so that the progress bar really reflect the state of the exportation:
 
185
        if (!basket->isLoaded()) {
 
186
                basket->load();
 
187
        }
 
188
 
 
189
        QDir dir;
 
190
        // Save basket data:
 
191
        tar->addLocalDirectory(basket->fullPath(), "baskets/" + basket->folderName());
 
192
        tar->addLocalFile(basket->fullPath() + ".basket", "baskets/" + basket->folderName() + ".basket"); // The hidden files were not added
 
193
        // Save basket icon:
 
194
        QString tempIconFile = tempFolder + "icon.png";
 
195
        if (!basket->icon().isEmpty() && basket->icon() != "basket") {
 
196
                QPixmap icon = kapp->iconLoader()->loadIcon(basket->icon(), KIcon::Small, 16, KIcon::DefaultState, /*path_store=*/0L, /*canReturnNull=*/true);
 
197
                if (!icon.isNull()) {
 
198
                        icon.save(tempIconFile, "PNG");
 
199
                        QString iconFileName = basket->icon().replace('/', '_');
 
200
                        tar->addLocalFile(tempIconFile, "basket-icons/" + iconFileName);
 
201
                }
 
202
        }
 
203
        // Save basket backgorund image:
 
204
        QString imageName = basket->backgroundImageName();
 
205
        if (!basket->backgroundImageName().isEmpty() && !backgrounds.contains(imageName)) {
 
206
                QString backgroundPath = Global::backgroundManager->pathForImageName(imageName);
 
207
                if (!backgroundPath.isEmpty()) {
 
208
                        // Save the background image:
 
209
                        tar->addLocalFile(backgroundPath, "backgrounds/" + imageName);
 
210
                        // Save the preview image:
 
211
                        QString previewPath = Global::backgroundManager->previewPathForImageName(imageName);
 
212
                        if (!previewPath.isEmpty())
 
213
                                tar->addLocalFile(previewPath, "backgrounds/previews/" + imageName);
 
214
                        // Save the configuration file:
 
215
                        QString configPath = backgroundPath + ".config";
 
216
                        if (dir.exists(configPath))
 
217
                                tar->addLocalFile(configPath, "backgrounds/" + imageName + ".config");
 
218
                }
 
219
                backgrounds.append(imageName);
 
220
        }
 
221
 
 
222
        progress->advance(1); // Basket exportation finished
 
223
        std::cout << basket->basketName() << " finished" << std::endl;
 
224
 
 
225
        // Recursively save child baskets:
 
226
        BasketListViewItem *item = Global::bnpView->listViewItemForBasket(basket);
 
227
        if (recursive && item->firstChild()) {
 
228
                for (BasketListViewItem *child = (BasketListViewItem*) item->firstChild(); child; child = (BasketListViewItem*) child->nextSibling()) {
 
229
                        saveBasketToArchive(child->basket(), recursive, tar, backgrounds, tempFolder, progress);
 
230
                }
 
231
        }
 
232
}
 
233
 
 
234
void Archive::listUsedTags(Basket *basket, bool recursive, QValueList<Tag*> &list)
 
235
{
 
236
        basket->listUsedTags(list);
 
237
        BasketListViewItem *item = Global::bnpView->listViewItemForBasket(basket);
 
238
        if (recursive && item->firstChild()) {
 
239
                for (BasketListViewItem *child = (BasketListViewItem*) item->firstChild(); child; child = (BasketListViewItem*) child->nextSibling()) {
 
240
                        listUsedTags(child->basket(), recursive, list);
 
241
                }
 
242
        }
 
243
}
 
244
 
 
245
void Archive::open(const QString &path)
 
246
{
 
247
        // Create the temporar folder:
 
248
        QString tempFolder = Global::savesFolder() + "temp-archive/";
 
249
        QDir dir;
 
250
        dir.mkdir(tempFolder);
 
251
        const Q_ULONG BUFFER_SIZE = 1024;
 
252
 
 
253
        QFile file(path);
 
254
        if (file.open(IO_ReadOnly)) {
 
255
                QTextStream stream(&file);
 
256
                stream.setEncoding(QTextStream::Latin1);
 
257
                QString line = stream.readLine();
 
258
                if (line != "BasKetNP:archive") {
 
259
                        KMessageBox::error(0, i18n("This file is not a basket archive."), i18n("Basket Archive Error"));
 
260
                        file.close();
 
261
                        Tools::deleteRecursively(tempFolder);
 
262
                        return;
 
263
                }
 
264
                QString version;
 
265
                QStringList readCompatibleVersions;
 
266
                QStringList writeCompatibleVersions;
 
267
                while (!stream.atEnd()) {
 
268
                        // Get Key/Value Pair From the Line to Read:
 
269
                        line = stream.readLine();
 
270
                        int index = line.find(':');
 
271
                        QString key;
 
272
                        QString value;
 
273
                        if (index >= 0) {
 
274
                                key = line.left(index);
 
275
                                value = line.right(line.length() - index - 1);
 
276
                        } else {
 
277
                                key = line;
 
278
                                value = "";
 
279
                        }
 
280
                        if (key == "version") {
 
281
                                version = value;
 
282
                        } else if (key == "read-compatible") {
 
283
                                readCompatibleVersions = QStringList::split(value, ";");
 
284
                        } else if (key == "write-compatible") {
 
285
                                writeCompatibleVersions = QStringList::split(value, ";");
 
286
                        } else if (key == "preview*") {
 
287
                                bool ok;
 
288
                                ulong size = value.toULong(&ok);
 
289
                                if (!ok) {
 
290
                                        KMessageBox::error(0, i18n("This file is corrupted. It can not be opened."), i18n("Basket Archive Error"));
 
291
                                        file.close();
 
292
                                        Tools::deleteRecursively(tempFolder);
 
293
                                        return;
 
294
                                }
 
295
                                // Get the preview file:
 
296
//FIXME: We do not need the preview for now
 
297
//                              QFile previewFile(tempFolder + "preview.png");
 
298
//                              if (previewFile.open(IO_WriteOnly)) {
 
299
                                        char *buffer = new char[BUFFER_SIZE];
 
300
                                        Q_LONG sizeRead;
 
301
                                        while ((sizeRead = file.readBlock(buffer, QMIN(BUFFER_SIZE, size))) > 0) {
 
302
//                                              previewFile.writeBlock(buffer, sizeRead);
 
303
                                                size -= sizeRead;
 
304
                                        }
 
305
//                                      previewFile.close();
 
306
                                        delete buffer;
 
307
//                              }
 
308
                        } else if (key == "archive*") {
 
309
                                if (version != "0.6.1" && readCompatibleVersions.contains("0.6.1") && !writeCompatibleVersions.contains("0.6.1")) {
 
310
                                        KMessageBox::information(
 
311
                                                0,
 
312
                                                i18n("This file was created with a recent version of %1. "
 
313
                                                     "It can be opened but not every information will be available to you. "
 
314
                                                     "For instance, some notes may be missing because they are of a type only available in new versions. "
 
315
                                                     "When saving the file back, consider to save it to another file, to preserve the original one.")
 
316
                                                        .arg(kapp->aboutData()->programName()),
 
317
                                                i18n("Basket Archive Error")
 
318
                                        );
 
319
                                }
 
320
                                if (version != "0.6.1" && !readCompatibleVersions.contains("0.6.1") && !writeCompatibleVersions.contains("0.6.1")) {
 
321
                                        KMessageBox::error(
 
322
                                                0,
 
323
                                                i18n("This file was created with a recent version of %1. Please upgrade to a newer version to be able to open that file.")
 
324
                                                        .arg(kapp->aboutData()->programName()),
 
325
                                                i18n("Basket Archive Error")
 
326
                                        );
 
327
                                        file.close();
 
328
                                        Tools::deleteRecursively(tempFolder);
 
329
                                        return;
 
330
                                }
 
331
 
 
332
                                bool ok;
 
333
                                ulong size = value.toULong(&ok);
 
334
                                if (!ok) {
 
335
                                        KMessageBox::error(0, i18n("This file is corrupted. It can not be opened."), i18n("Basket Archive Error"));
 
336
                                        file.close();
 
337
                                        Tools::deleteRecursively(tempFolder);
 
338
                                        return;
 
339
                                }
 
340
 
 
341
                                Global::mainWindow()->raise();
 
342
 
 
343
                                // Get the archive file:
 
344
                                QString tempArchive = tempFolder + "temp-archive.tar.gz";
 
345
                                QFile archiveFile(tempArchive);
 
346
                                if (archiveFile.open(IO_WriteOnly)) {
 
347
                                        char *buffer = new char[BUFFER_SIZE];
 
348
                                        Q_LONG sizeRead;
 
349
                                        while ((sizeRead = file.readBlock(buffer, QMIN(BUFFER_SIZE, size))) > 0) {
 
350
                                                archiveFile.writeBlock(buffer, sizeRead);
 
351
                                                size -= sizeRead;
 
352
                                        }
 
353
                                        archiveFile.close();
 
354
                                        delete buffer;
 
355
 
 
356
                                        // Extract the Archive:
 
357
                                        QString extractionFolder = tempFolder + "extraction/";
 
358
                                        QDir dir;
 
359
                                        dir.mkdir(extractionFolder);
 
360
                                        KTar tar(tempArchive, "application/x-gzip");
 
361
                                        tar.open(IO_ReadOnly);
 
362
                                        tar.directory()->copyTo(extractionFolder);
 
363
                                        tar.close();
 
364
 
 
365
                                        // Import the Tags:
 
366
                                        importTagEmblems(extractionFolder); // Import and rename tag emblems BEFORE loading them!
 
367
                                        QMap<QString, QString> mergedStates = Tag::loadTags(extractionFolder + "tags.xml");
 
368
                                        QMap<QString, QString>::Iterator it;
 
369
                                        if (mergedStates.count() > 0) {
 
370
                                                Tag::saveTags();
 
371
                                        }
 
372
 
 
373
                                        // Import the Background Images:
 
374
                                        importArchivedBackgroundImages(extractionFolder);
 
375
 
 
376
                                        // Import the Baskets:
 
377
                                        renameBasketFolders(extractionFolder, mergedStates);
 
378
 
 
379
                                }
 
380
                        } else if (key.endsWith("*")) {
 
381
                                // We do not know what it is, but we should read the embedded-file in order to discard it:
 
382
                                bool ok;
 
383
                                ulong size = value.toULong(&ok);
 
384
                                if (!ok) {
 
385
                                        KMessageBox::error(0, i18n("This file is corrupted. It can not be opened."), i18n("Basket Archive Error"));
 
386
                                        file.close();
 
387
                                        Tools::deleteRecursively(tempFolder);
 
388
                                        return;
 
389
                                }
 
390
                                // Get the archive file:
 
391
                                char *buffer = new char[BUFFER_SIZE];
 
392
                                Q_LONG sizeRead;
 
393
                                while ((sizeRead = file.readBlock(buffer, QMIN(BUFFER_SIZE, size))) > 0) {
 
394
                                        size -= sizeRead;
 
395
                                }
 
396
                                delete buffer;
 
397
                        } else {
 
398
                                // We do not know what it is, and we do not care.
 
399
                        }
 
400
                        // Analyse the Value, if Understood:
 
401
                }
 
402
                file.close();
 
403
        }
 
404
        Tools::deleteRecursively(tempFolder);
 
405
}
 
406
 
 
407
/**
 
408
 * When opening a basket archive that come from another computer,
 
409
 * it can contains tags that use icons (emblems) that are not present on that computer.
 
410
 * Fortunately, basket archives contains a copy of every used icons.
 
411
 * This method check for every emblems and import the missing ones.
 
412
 * It also modify the tags.xml copy for the emblems to point to the absolute path of the impported icons.
 
413
 */
 
414
void Archive::importTagEmblems(const QString &extractionFolder)
 
415
{
 
416
        QDomDocument *document = XMLWork::openFile("basketTags", extractionFolder + "tags.xml");
 
417
        if (document == 0)
 
418
                return;
 
419
        QDomElement docElem = document->documentElement();
 
420
 
 
421
        QDir dir;
 
422
        dir.mkdir(Global::savesFolder() + "tag-emblems/");
 
423
        FormatImporter copier; // Only used to copy files synchronously
 
424
 
 
425
        QDomNode node = docElem.firstChild();
 
426
        while (!node.isNull()) {
 
427
                QDomElement element = node.toElement();
 
428
                if ( (!element.isNull()) && element.tagName() == "tag" ) {
 
429
                        QDomNode subNode = element.firstChild();
 
430
                        while (!subNode.isNull()) {
 
431
                                QDomElement subElement = subNode.toElement();
 
432
                                if ( (!subElement.isNull()) && subElement.tagName() == "state" ) {
 
433
                                        QString emblemName = XMLWork::getElementText(subElement, "emblem");
 
434
                                        if (!emblemName.isEmpty()) {
 
435
                                                QPixmap emblem = kapp->iconLoader()->loadIcon(emblemName, KIcon::NoGroup, 16, KIcon::DefaultState, 0L, /*canReturnNull=*/true);
 
436
                                                // The icon does not exists on that computer, import it:
 
437
                                                if (emblem.isNull()) {
 
438
                                                        // Of the emblem path was eg. "/home/seb/emblem.png", it was exported as "tag-emblems/_home_seb_emblem.png".
 
439
                                                        // So we need to copy that image to "~/.kde/share/apps/basket/tag-emblems/emblem.png":
 
440
                                                        int slashIndex = emblemName.findRev("/");
 
441
                                                        QString emblemFileName = (slashIndex < 0 ? emblemName : emblemName.right(slashIndex - 2));
 
442
                                                        QString source      = extractionFolder + "tag-emblems/" + emblemName.replace('/', '_');
 
443
                                                        QString destination = Global::savesFolder() + "tag-emblems/" + emblemFileName;
 
444
                                                        if (!dir.exists(destination))
 
445
                                                                copier.copyFolder(source, destination);
 
446
                                                        // Replace the emblem path in the tags.xml copy:
 
447
                                                        QDomElement emblemElement = XMLWork::getElement(subElement, "emblem");
 
448
                                                        subElement.removeChild(emblemElement);
 
449
                                                        XMLWork::addElement(*document, subElement, "emblem", destination);
 
450
                                                }
 
451
                                        }
 
452
                                }
 
453
                                subNode = subNode.nextSibling();
 
454
                        }
 
455
                }
 
456
                node = node.nextSibling();
 
457
        }
 
458
        Basket::safelySaveToFile(extractionFolder + "tags.xml", document->toString());
 
459
}
 
460
 
 
461
void Archive::importArchivedBackgroundImages(const QString &extractionFolder)
 
462
{
 
463
        FormatImporter copier; // Only used to copy files synchronously
 
464
        QString destFolder = KGlobal::dirs()->saveLocation("data", "basket/backgrounds/");
 
465
 
 
466
        QDir dir(extractionFolder + "backgrounds/", /*nameFilder=*/"*.png", /*sortSpec=*/QDir::Name | QDir::IgnoreCase, /*filterSpec=*/QDir::Files | QDir::NoSymLinks);
 
467
        QStringList files = dir.entryList();
 
468
        for (QStringList::Iterator it = files.begin(); it != files.end(); ++it) {
 
469
                QString image = *it;
 
470
                if (!Global::backgroundManager->exists(image)) {
 
471
                        // Copy images:
 
472
                        QString imageSource = extractionFolder + "backgrounds/" + image;
 
473
                        QString imageDest   = destFolder + image;
 
474
                        copier.copyFolder(imageSource, imageDest);
 
475
                        // Copy configuration file:
 
476
                        QString configSource = extractionFolder + "backgrounds/" + image + ".config";
 
477
                        QString configDest   = destFolder + image;
 
478
                        if (dir.exists(configSource))
 
479
                                copier.copyFolder(configSource, configDest);
 
480
                        // Copy preview:
 
481
                        QString previewSource = extractionFolder + "backgrounds/previews/" + image;
 
482
                        QString previewDest   = destFolder + "previews/" + image;
 
483
                        if (dir.exists(previewSource)) {
 
484
                                dir.mkdir(destFolder + "previews/"); // Make sure the folder exists!
 
485
                                copier.copyFolder(previewSource, previewDest);
 
486
                        }
 
487
                        // Append image to database:
 
488
                        Global::backgroundManager->addImage(imageDest);
 
489
                }
 
490
        }
 
491
}
 
492
 
 
493
void Archive::renameBasketFolders(const QString &extractionFolder, QMap<QString, QString> &mergedStates)
 
494
{
 
495
        QDomDocument *doc = XMLWork::openFile("basketTree", extractionFolder + "baskets/baskets.xml");
 
496
        if (doc != 0) {
 
497
                QMap<QString, QString> folderMap;
 
498
                QDomElement docElem = doc->documentElement();
 
499
                QDomNode node = docElem.firstChild();
 
500
                renameBasketFolder(extractionFolder, node, folderMap, mergedStates);
 
501
                loadExtractedBaskets(extractionFolder, node, folderMap, 0);
 
502
        }
 
503
}
 
504
 
 
505
void Archive::renameBasketFolder(const QString &extractionFolder, QDomNode &basketNode, QMap<QString, QString> &folderMap, QMap<QString, QString> &mergedStates)
 
506
{
 
507
        QDomNode n = basketNode;
 
508
        while ( ! n.isNull() ) {
 
509
                QDomElement element = n.toElement();
 
510
                if ( (!element.isNull()) && element.tagName() == "basket" ) {
 
511
                        QString folderName = element.attribute("folderName");
 
512
                        if (!folderName.isEmpty()) {
 
513
                                // Find a folder name:
 
514
                                QString newFolderName = BasketFactory::newFolderName();
 
515
                                folderMap[folderName] = newFolderName;
 
516
                                // Reserve the folder name:
 
517
                                QDir dir;
 
518
                                dir.mkdir(Global::basketsFolder() + newFolderName);
 
519
                                // Rename the merged tag ids:
 
520
//                              if (mergedStates.count() > 0) {
 
521
                                        renameMergedStatesAndBasketIcon(extractionFolder + "baskets/" + folderName + ".basket", mergedStates, extractionFolder);
 
522
//                              }
 
523
                                // Child baskets:
 
524
                                QDomNode node = element.firstChild();
 
525
                                renameBasketFolder(extractionFolder, node, folderMap, mergedStates);
 
526
                        }
 
527
                }
 
528
                n = n.nextSibling();
 
529
        }
 
530
}
 
531
 
 
532
void Archive::renameMergedStatesAndBasketIcon(const QString &fullPath, QMap<QString, QString> &mergedStates, const QString &extractionFolder)
 
533
{
 
534
        QDomDocument *doc = XMLWork::openFile("basket", fullPath);
 
535
        if (doc == 0)
 
536
                return;
 
537
        QDomElement docElem = doc->documentElement();
 
538
        QDomElement properties = XMLWork::getElement(docElem, "properties");
 
539
        importBasketIcon(properties, extractionFolder);
 
540
        QDomElement notes = XMLWork::getElement(docElem, "notes");
 
541
        if (mergedStates.count() > 0)
 
542
                renameMergedStates(notes, mergedStates);
 
543
        Basket::safelySaveToFile(fullPath, /*"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" + */doc->toString());
 
544
}
 
545
 
 
546
void Archive::importBasketIcon(QDomElement properties, const QString &extractionFolder)
 
547
{
 
548
        QString iconName = XMLWork::getElementText(properties, "icon");
 
549
        if (!iconName.isEmpty() && iconName != "basket") {
 
550
                QPixmap icon = kapp->iconLoader()->loadIcon(iconName, KIcon::NoGroup, 16, KIcon::DefaultState, 0L, /*canReturnNull=*/true);
 
551
                // The icon does not exists on that computer, import it:
 
552
                if (icon.isNull()) {
 
553
                        QDir dir;
 
554
                        dir.mkdir(Global::savesFolder() + "basket-icons/");
 
555
                        FormatImporter copier; // Only used to copy files synchronously
 
556
                        // Of the icon path was eg. "/home/seb/icon.png", it was exported as "basket-icons/_home_seb_icon.png".
 
557
                        // So we need to copy that image to "~/.kde/share/apps/basket/basket-icons/icon.png":
 
558
                        int slashIndex = iconName.findRev("/");
 
559
                        QString iconFileName = (slashIndex < 0 ? iconName : iconName.right(slashIndex - 2));
 
560
                        QString source       = extractionFolder + "basket-icons/" + iconName.replace('/', '_');
 
561
                        QString destination = Global::savesFolder() + "basket-icons/" + iconFileName;
 
562
                        if (!dir.exists(destination))
 
563
                                copier.copyFolder(source, destination);
 
564
                        // Replace the emblem path in the tags.xml copy:
 
565
                        QDomElement iconElement = XMLWork::getElement(properties, "icon");
 
566
                        properties.removeChild(iconElement);
 
567
                        QDomDocument document = properties.ownerDocument();
 
568
                        XMLWork::addElement(document, properties, "icon", destination);
 
569
                }
 
570
        }
 
571
}
 
572
 
 
573
void Archive::renameMergedStates(QDomNode notes, QMap<QString, QString> &mergedStates)
 
574
{
 
575
        QDomNode n = notes.firstChild();
 
576
        while ( ! n.isNull() ) {
 
577
                QDomElement element = n.toElement();
 
578
                if (!element.isNull()) {
 
579
                        if (element.tagName() == "group" ) {
 
580
                                renameMergedStates(n, mergedStates);
 
581
                        } else if (element.tagName() == "note") {
 
582
                                QString tags = XMLWork::getElementText(element, "tags");
 
583
                                if (!tags.isEmpty()) {
 
584
                                        QStringList tagNames = QStringList::split(";", tags);
 
585
                                        for (QStringList::Iterator it = tagNames.begin(); it != tagNames.end(); ++it) {
 
586
                                                QString &tag = *it;
 
587
                                                if (mergedStates.contains(tag)) {
 
588
                                                        tag = mergedStates[tag];
 
589
                                                }
 
590
                                        }
 
591
                                        QString newTags = tagNames.join(";");
 
592
                                        QDomElement tagsElement = XMLWork::getElement(element, "tags");
 
593
                                        element.removeChild(tagsElement);
 
594
                                        QDomDocument document = element.ownerDocument();
 
595
                                        XMLWork::addElement(document, element, "tags", newTags);
 
596
                                }
 
597
                        }
 
598
                }
 
599
                n = n.nextSibling();
 
600
        }
 
601
}
 
602
 
 
603
void Archive::loadExtractedBaskets(const QString &extractionFolder, QDomNode &basketNode, QMap<QString, QString> &folderMap, Basket *parent)
 
604
{
 
605
        bool basketSetAsCurrent = (parent != 0);
 
606
        QDomNode n = basketNode;
 
607
        while ( ! n.isNull() ) {
 
608
                QDomElement element = n.toElement();
 
609
                if ( (!element.isNull()) && element.tagName() == "basket" ) {
 
610
                        QString folderName = element.attribute("folderName");
 
611
                        if (!folderName.isEmpty()) {
 
612
                                // Move the basket folder to its destination, while renaming it uniquely:
 
613
                                QString newFolderName = folderMap[folderName];
 
614
                                FormatImporter copier;
 
615
                                // The folder has been "reserved" by creating it. Avoid asking the user to override:
 
616
                                QDir dir;
 
617
                                dir.rmdir(Global::basketsFolder() + newFolderName);
 
618
                                copier.moveFolder(extractionFolder + "baskets/" + folderName, Global::basketsFolder() + newFolderName);
 
619
                                // Append and load the basket in the tree:
 
620
                                Basket *basket = Global::bnpView->loadBasket(newFolderName);
 
621
                                BasketListViewItem *basketItem = Global::bnpView->appendBasket(basket, (basket && parent ? Global::bnpView->listViewItemForBasket(parent) : 0));
 
622
                                basketItem->setOpen(!XMLWork::trueOrFalse(element.attribute("folded", "false"), false));
 
623
                                QDomElement properties = XMLWork::getElement(element, "properties");
 
624
                                importBasketIcon(properties, extractionFolder); // Rename the icon fileName if necessary
 
625
                                basket->loadProperties(properties);
 
626
                                // Open the first basket of the archive:
 
627
                                if (!basketSetAsCurrent) {
 
628
                                        Global::bnpView->setCurrentBasket(basket);
 
629
                                        basketSetAsCurrent = true;
 
630
                                }
 
631
                                QDomNode node = element.firstChild();
 
632
                                loadExtractedBaskets(extractionFolder, node, folderMap, basket);
 
633
                        }
 
634
                }
 
635
                n = n.nextSibling();
 
636
        }
 
637
}