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 "softwareimporters.h"
22 
23 #include <QtCore/QString>
24 #include <QtCore/QDir>
25 #include <QtCore/QTextStream>
26 #include <QtCore/QStack>
27 #include <QGroupBox>
28 #include <QHBoxLayout>
29 #include <QVBoxLayout>
30 #include <QRadioButton>
31 #include <QtXml/QDomDocument>
32 #include <QRegExp>
33 #include <QLocale>
34 #include <QDialogButtonBox>
35 #include <QPushButton>
36 #include <QVBoxLayout>
37 #include <QStandardPaths>
38 #include <QFileDialog>
39 
40 #include <KMessageBox>
41 #include <KTextEdit>
42 #include <KConfigGroup>
43 #include <KLocalizedString>
44 
45 #include "basketscene.h"
46 #include "basketfactory.h"
47 #include "notefactory.h"
48 #include "global.h"
49 #include "bnpview.h"
50 #include "xmlwork.h"
51 #include "tools.h"
52 #include "icon_names.h"
53 #include "debugwindow.h"
54 
55 /** class TreeImportDialog: */
56 
TreeImportDialog(QWidget * parent)57 TreeImportDialog::TreeImportDialog(QWidget *parent)
58         : QDialog(parent)
59 {
60     QWidget *page = new QWidget(this);
61     QVBoxLayout *topLayout = new QVBoxLayout(page);
62 
63     // QDialog options
64     setWindowTitle(i18n("Import Hierarchy"));
65 
66     QWidget *mainWidget = new QWidget(this);
67     QVBoxLayout *mainLayout = new QVBoxLayout;
68     setLayout(mainLayout);
69     mainLayout->addWidget(mainWidget);
70 
71     setObjectName("ImportHeirachy");
72     setModal(true);
73 
74     m_choices = new QGroupBox(i18n("How to Import the Notes?"), page);
75     m_choiceLayout = new QVBoxLayout();
76     m_choices->setLayout(m_choiceLayout);
77 
78     m_hierarchy_choice = new QRadioButton(i18n("&Keep original hierarchy (all notes in separate baskets)"), m_choices);
79     m_separate_baskets_choice = new QRadioButton(i18n("&First level notes in separate baskets"),            m_choices);
80     m_one_basket_choice = new QRadioButton(i18n("&All notes in one basket"),                                m_choices);
81 
82     m_hierarchy_choice->setChecked(true);
83     m_choiceLayout->addWidget(m_hierarchy_choice);
84     m_choiceLayout->addWidget(m_separate_baskets_choice);
85     m_choiceLayout->addWidget(m_one_basket_choice);
86 
87     topLayout->addWidget(m_choices);
88     topLayout->addStretch(10);
89 
90     mainLayout->addWidget(page);
91 
92     QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel, this);
93     QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok);
94     okButton->setDefault(true);
95     okButton->setShortcut(Qt::CTRL | Qt::Key_Return);
96     connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
97     connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
98     mainLayout->addWidget(buttonBox);
99 }
100 
~TreeImportDialog()101 TreeImportDialog::~TreeImportDialog()
102 {
103 }
104 
choice()105 int TreeImportDialog::choice()
106 {
107     if (m_hierarchy_choice->isChecked())
108         return 1;
109     else if (m_separate_baskets_choice->isChecked())
110         return 2;
111     else
112         return 3;
113 }
114 
115 /** class TextFileImportDialog: */
116 
TextFileImportDialog(QWidget * parent)117 TextFileImportDialog::TextFileImportDialog(QWidget *parent)
118         : QDialog(parent)
119 {
120     QWidget *page = new QWidget(this);
121     QVBoxLayout *topLayout = new QVBoxLayout(page);
122     QVBoxLayout *mainLayout = new QVBoxLayout;
123     setLayout(mainLayout);
124 
125     // QDialog options
126     setWindowTitle(i18n("Import Text File"));
127     setObjectName("ImportTextFile");
128     setModal(true);
129 
130 
131     m_choices = new QGroupBox(i18n("Format of the Text File"), page);
132     mainLayout->addWidget(m_choices);
133     m_choiceLayout = new QVBoxLayout;
134     m_choices->setLayout(m_choiceLayout);
135 
136     m_emptyline_choice = new QRadioButton(i18n("Notes separated by an &empty line"), m_choices);
137     m_newline_choice = new QRadioButton(i18n("One &note per line"),                  m_choices);
138     m_dash_choice = new QRadioButton(i18n("Notes begin with a &dash (-)"),           m_choices);
139     m_star_choice = new QRadioButton(i18n("Notes begin with a &star (*)"),           m_choices);
140     m_anotherSeparator = new QRadioButton(i18n("&Use another separator:"),           m_choices);
141 
142     m_choiceLayout->addWidget(m_emptyline_choice);
143     m_choiceLayout->addWidget(m_newline_choice);
144     m_choiceLayout->addWidget(m_dash_choice);
145     m_choiceLayout->addWidget(m_star_choice);
146     m_choiceLayout->addWidget(m_anotherSeparator);
147 
148     QWidget *indentedTextEdit = new QWidget(m_choices);
149     m_choiceLayout->addWidget(indentedTextEdit);
150 
151     QHBoxLayout *hLayout = new QHBoxLayout(indentedTextEdit);
152     hLayout->addSpacing(20);
153     m_customSeparator = new KTextEdit(indentedTextEdit);
154     hLayout->addWidget(m_customSeparator);
155 
156     m_all_in_one_choice = new QRadioButton(i18n("&All in one note"),                  m_choices);
157     m_choiceLayout->addWidget(m_all_in_one_choice);
158 
159     m_emptyline_choice->setChecked(true);
160     topLayout->addWidget(m_choices);
161 
162     connect(m_customSeparator, SIGNAL(textChanged()), this, SLOT(customSeparatorChanged()));
163 
164     mainLayout->addWidget(page);
165 
166     QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel, this);
167     QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok);
168     okButton->setDefault(true);
169     okButton->setShortcut(Qt::CTRL | Qt::Key_Return);
170     connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
171     connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
172     mainLayout->addWidget(buttonBox);
173 }
174 
~TextFileImportDialog()175 TextFileImportDialog::~TextFileImportDialog()
176 {
177 }
178 
separator()179 QString TextFileImportDialog::separator()
180 {
181     if (m_emptyline_choice->isChecked())
182         return "\n\n";
183     else if (m_newline_choice->isChecked())
184         return "\n";
185     else if (m_dash_choice->isChecked())
186         return "\n-";
187     else if (m_star_choice->isChecked())
188         return "\n*";
189     else if (m_anotherSeparator->isChecked())
190         return m_customSeparator->toPlainText();
191     else if (m_all_in_one_choice->isChecked())
192         return "";
193     else
194         return "\n\n";
195 }
196 
customSeparatorChanged()197 void TextFileImportDialog::customSeparatorChanged()
198 {
199     if (!m_anotherSeparator->isChecked())
200         m_anotherSeparator->toggle();
201 }
202 
203 /** namespace SoftwareImporters: */
204 
fromICS(const QString & ics)205 QString SoftwareImporters::fromICS(const QString &ics)
206 {
207     QString result = ics;
208 
209     // Remove escaped '\' characters and append the text to the body
210     int pos = 0;
211     while ((pos = result.indexOf('\\', pos)) != -1) {
212         if (pos == result.length() - 1) // End of string
213             break;
214         if (result[pos+1] == 'n') {
215             result.replace(pos, 2, '\n');
216         } else if (result[pos+1] == 'r') {
217             result.replace(pos, 2, '\r');
218         } else if (result[pos+1] == 't') {
219             result.replace(pos, 2, '\t');
220         } else if (result[pos] == '\\') {
221             result.remove(pos, 1); // Take care of "\\", "\,", "\;" and other escaped characters I haven't noticed
222             ++pos;
223         }
224     }
225 
226     return result;
227 }
228 
fromTomboy(QString tomboy)229 QString SoftwareImporters::fromTomboy(QString tomboy)
230 {
231     // The first line is the note title, and we already have it, so we remove it (yes, that's pretty stupid to duplicate it in the content...):
232     tomboy = tomboy.mid(tomboy.indexOf("\n")).trimmed();
233 
234     // Font styles and decorations:
235     tomboy.replace("<bold>",           "<b>");
236     tomboy.replace("</bold>",          "</b>");
237     tomboy.replace("<italic>",         "<i>");
238     tomboy.replace("</italic>",        "</i>");
239     tomboy.replace("<strikethrough>",  "<span style='text-decoration: line-through'>");
240     tomboy.replace("</strikethrough>", "</span>");
241 
242     // Highlight not supported by KTextEdit:
243     //TODO: implement this marker-style yellow highlight?
244     tomboy.replace("<highlight>",      "<span style='color:#ff0080'>");
245     tomboy.replace("</highlight>",     "</span>");
246 
247     // Font sizes:
248     tomboy.replace("<size:small>",     "<span style='font-size: 7pt'>");
249     tomboy.replace("</size:small>",    "</span>");
250     tomboy.replace("<size:large>",     "<span style='font-size: 16pt'>");
251     tomboy.replace("</size:large>",    "</span>");
252     tomboy.replace("<size:huge>",      "<span style='font-size: 20pt'>");
253     tomboy.replace("</size:huge>",     "</span>");
254 
255     // Internal links to other notes aren't supported yet by BasKet Note Pads:
256     tomboy.replace("<link:internal>",  "");
257     tomboy.replace("</link:internal>", "");
258 
259     // Bullets
260     tomboy.replace("<list>",           "<ul>");
261     tomboy.replace("</list>",          "</ul>");
262 
263     QRegExp tagRegex("<list-item.*>");
264     tagRegex.setMinimal(true); // non-greedy
265     tomboy.replace(QRegExp(tagRegex),  "<li>");
266     tomboy.replace("</list-item>",     "</li>");
267 
268     // In the Tomboy file, new lines are "\n" and not "<br>":
269     tomboy.replace("\n", "<br>\n");
270 
271     // Preserve consecutive spaces:
272     return "<html><head><meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\"><meta name=\"qrichtext\" content=\"1\" /></head><body>" + tomboy + "</body></html>";
273 }
274 
getFirstTomboyTag(const QDomElement & docElem)275 QString SoftwareImporters::getFirstTomboyTag(const QDomElement& docElem)
276 {
277     // Would be better to take the last tag (latest user choice), but that requires more DOM code
278 
279    QString tag = XMLWork::getElementText(docElem, "tags/tag");
280    if (tag.length() > 0) {
281 
282        // Carefully peel the markup
283        const QString SYSTEM_PREFIX = "system:";
284        const QString NOTEBOOK_PREFIX = "notebook:";
285 
286        if (tag.startsWith(SYSTEM_PREFIX))
287            tag.remove(0, SYSTEM_PREFIX.length());
288 
289        if (tag.startsWith(NOTEBOOK_PREFIX))
290            tag.remove(0, NOTEBOOK_PREFIX.length());
291    }
292 
293    return tag;
294 }
295 
insertTitledNote(BasketScene * parent,const QString & title,const QString & content,Qt::TextFormat format,Note * parentNote)296 Note* SoftwareImporters::insertTitledNote(BasketScene *parent, const QString &title, const QString &content, Qt::TextFormat format/* = Qt::PlainText*/, Note *parentNote/* = 0*/)
297 {
298     Note *nGroup = new Note(parent);
299 
300     Note *nTitle = NoteFactory::createNoteText(title, parent);
301     nTitle->addState(Tag::stateForId("title"));
302 
303     Note *nContent;
304     if (format == Qt::PlainText)
305         nContent = NoteFactory::createNoteText(content, parent);
306     else
307         nContent = NoteFactory::createNoteHtml(content, parent);
308 
309     if (parentNote == 0)
310         parentNote = parent->firstNote(); // In the first column!
311     parent->insertNote(nGroup,   parentNote, Note::BottomColumn, QPoint(), /*animate=*/false);
312     parent->insertNote(nTitle,   nGroup,     Note::BottomColumn, QPoint(), /*animate=*/false);
313     parent->insertNote(nContent, nTitle,     Note::BottomInsert, QPoint(), /*animate=*/false);
314 
315     return nGroup;
316 }
317 
finishImport(BasketScene * basket)318 void SoftwareImporters::finishImport(BasketScene *basket)
319 {
320     // Unselect the last inserted group:
321     basket->unselectAll();
322 
323     // Focus the FIRST note (the last inserted note is currently focused!):
324     basket->setFocusedNote(basket->firstNoteShownInStack());
325 
326     // Relayout every notes at theire new place and simulate a load animation (because already loaded just after the creation).
327     // Without a relayouting, notes on the bottom would comes from the top (because they were inserted on top) and clutter the animation load:
328     basket->relayoutNotes(/*animate=*/false);
329     basket->animateLoad();
330     basket->save();
331 }
332 
333 
334 
importKJots()335 void SoftwareImporters::importKJots()
336 {
337     //This code is out-of-date. KJots (KDE4) and KNotes (KDE4, 5) now store data in Akonadi and MIME files in .local/share/notes
338     QString dirPath = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/../kjots/"; // I think the assumption is good
339     QDir dir(dirPath, QString::null, QDir::Name | QDir::IgnoreCase, QDir::Files | QDir::NoSymLinks);
340 
341     QStringList list = dir.entryList();
342     if (list.isEmpty())
343         return;
344 
345     BasketFactory::newBasket(/*icon=*/"kjots", /*name=*/i18n("From KJots"), /*backgroundImage=*/"", /*backgroundColor=*/QColor(), /*textColor=*/QColor(), /*templateName=*/"1column", /*createIn=*/0);
346     BasketScene *kjotsBasket = Global::bnpView->currentBasket();
347 
348     for (QStringList::Iterator it = list.begin(); it != list.end(); ++it) {   // For each file
349         QFile file(dirPath + *it);
350         if (file.open(QIODevice::ReadOnly)) {
351             QTextStream stream(&file);
352             QString buf = stream.readLine();
353 
354             // IT IS A NOTEBOOK FILE, AT THE VERION 0.6.x and older:
355             if (!buf.isNull() && buf.left(9) == "\\NewEntry") {
356 
357                 // First create a basket for it:
358                 BasketFactory::newBasket(/*icon=*/"kjots", /*name=*/QUrl::fromLocalFile(file.fileName()).fileName(), /*backgroundImage=*/"", /*backgroundColor=*/QColor(), /*textColor=*/QColor(), /*templateName=*/"1column", /*createIn=*/kjotsBasket);
359                 BasketScene *basket = Global::bnpView->currentBasket();
360                 basket->load();
361 
362                 QString title, body;
363                 bool haveAnEntry = false;
364                 while (1) {
365                     if (buf.left(9) == "\\NewEntry") {
366                         if (haveAnEntry) // Do not add note the first time
367                             insertTitledNote(basket, title, Tools::stripEndWhiteSpaces(body));
368                         title = buf.mid(10, buf.length());          // Problem : basket will be saved
369                         body = ""; // New note will then be created //  EACH time a note is imported
370                         haveAnEntry = true;
371                     } else if (buf.left(3) != "\\ID") { // Don't care of the ID
372                         // Remove escaped '\' characters and append the text to the body
373                         int pos = 0;
374                         while ((pos = buf.indexOf('\\', pos)) != -1)
375                             if (buf[++pos] == '\\')
376                                 buf.remove(pos, 1);
377                         body.append(buf + "\n");
378                     }
379                     buf = stream.readLine();
380                     if (buf.isNull()) // OEF
381                         break;
382                 }
383                 // Add the ending note (there isn't any other "\\NewEntry" to do it):
384                 if (haveAnEntry)
385                     insertTitledNote(basket, title, Tools::stripEndWhiteSpaces(body));
386                 finishImport(basket);
387 
388                 // IT IS A NOTEBOOK XML FILE, AT THE VERION 0.7.0 and later:
389             } else if ((*it).endsWith(QLatin1String(".book")) /*&& !buf.isNull() && (buf.left(2) == "<!" / *<!DOCTYPE...* / || buf.left(2) == "<?" / *<?xml...* /)*/) {
390 
391                 QDomDocument *doc = XMLWork::openFile("KJots", dirPath + *it);
392                 if (doc == 0)
393                     continue;
394 
395                 QString bookTitle = XMLWork::getElementText(doc->documentElement(), "KJotsBook/Title");
396 
397                 // First create a basket for it:
398                 BasketFactory::newBasket(/*icon=*/"kjots", /*name=*/bookTitle, /*backgroundImage=*/"", /*backgroundColor=*/QColor(), /*textColor=*/QColor(), /*templateName=*/"1column", /*createIn=*/kjotsBasket);
399                 BasketScene *basket = Global::bnpView->currentBasket();
400                 basket->load();
401 
402                 QDomElement docElem = XMLWork::getElement(doc->documentElement(), "KJotsBook");
403                 for (QDomNode n = docElem.firstChild(); !n.isNull(); n = n.nextSibling()) {
404                     QDomElement e = n.toElement();
405                     if ((!e.isNull()) && e.tagName() == "KJotsPage")
406                         insertTitledNote(basket, XMLWork::getElementText(e, "Title"), XMLWork::getElementText(e, "Text"));
407                 }
408                 finishImport(basket);
409 
410             }
411 
412             file.close();
413         }
414     }
415 }
416 
importKNotes()417 void SoftwareImporters::importKNotes()
418 {
419     QString dirPath = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/../knotes/"; // I think the assumption is good
420     QDir dir(dirPath, QString::null, QDir::Name | QDir::IgnoreCase, QDir::Files | QDir::NoSymLinks);
421 
422     QStringList list = dir.entryList();
423     for (QStringList::Iterator it = list.begin(); it != list.end(); ++it) {   // For each file
424         if (!(*it).endsWith(QLatin1String(".ics")))    // Don't process *.ics~ and other files
425             continue;
426         QFile file(dirPath + *it);
427         if (file.open(QIODevice::ReadOnly)) {
428             QTextStream stream(&file);
429             stream.setCodec("UTF-8");
430 
431             // First create a basket for it:
432             BasketFactory::newBasket(/*icon=*/"knotes", /*name=*/i18n("From KNotes"), /*backgroundImage=*/"", /*backgroundColor=*/QColor(), /*textColor=*/QColor(), /*templateName=*/"1column", /*createIn=*/0);
433             BasketScene *basket = Global::bnpView->currentBasket();
434             basket->load();
435 
436             bool inVJournal    = false;
437             bool inDescription = false;
438             bool isRichText    = false;
439             QString title, body;
440             QString buf;
441             while (1) {
442                 buf = stream.readLine();
443                 if (buf.isNull()) // OEF
444                     break;
445 
446                 if (!buf.isNull() && buf == "BEGIN:VJOURNAL") {
447                     inVJournal = true;
448                 } else if (inVJournal && buf.startsWith(QLatin1String("SUMMARY:"))) {
449                     title = buf.mid(8, buf.length());
450                 } else if (inVJournal && buf.startsWith(QLatin1String("DESCRIPTION:"))) {
451                     body = buf.mid(12, buf.length());
452                     inDescription = true;
453                 } else if (inDescription && buf.startsWith(QLatin1String(" "))) {
454                     body += buf.mid(1, buf.length());
455                 } else if (buf.startsWith(QLatin1String("X-KDE-KNotes-RichText:"))) {
456                     isRichText = XMLWork::trueOrFalse(buf.mid(22, buf.length() - 22).trimmed(), "false");
457                 } else if (buf == "END:VJOURNAL") {
458                     insertTitledNote(basket, fromICS(title), fromICS(body), (isRichText ? Qt::RichText : Qt::PlainText));
459                     inVJournal    = false;
460                     inDescription = false;
461                     isRichText    = false;
462                     title = "";
463                     body = "";
464                 } else
465                     inDescription = false;
466             }
467 
468             // Bouh : duplicate code
469             // In case of unvalide ICAL file!
470             if (! body.isEmpty())   // Add the ending note
471                 insertTitledNote(basket, fromICS(title), fromICS(body), (isRichText ? Qt::RichText : Qt::PlainText));
472             file.close();
473             finishImport(basket);
474         }
475     }
476 }
477 
importStickyNotes()478 void SoftwareImporters::importStickyNotes()
479 {
480     // Sticky Notes file is usually located in ~/.gnome2/stickynotes_applet
481     // We will search all directories in "~/" that contain "gnome" in the name,
482     // and will search for "stickynotes_applet" file (that should be XML file with <stickynotes> root.
483     QDir dir(QDir::home().absolutePath(), QString::null, QDir::Name | QDir::IgnoreCase,
484              QDir::Dirs | QDir::NoSymLinks | QDir::Hidden);
485     QStringList founds;
486 
487     QStringList list = dir.entryList();
488     for (QStringList::Iterator it = list.begin(); it != list.end(); ++it) {   // For each folder
489         if ((*it).contains("gnome", Qt::CaseInsensitive)) {
490             QString fullPath = QDir::home().absolutePath() + "/" + (*it) + "/stickynotes_applet";
491             if (dir.exists(fullPath))
492                 founds += fullPath;
493         }
494     }
495 
496     for (QStringList::Iterator it = founds.begin(); it != founds.end(); ++it) {   // For each file
497         QFile file(*it);
498         QDomDocument *doc = XMLWork::openFile("stickynotes", *it);
499         if (doc == 0)
500             continue;
501 
502         // First create a basket for it:
503         BasketFactory::newBasket(/*icon=*/"gnome", /*name=*/i18n("From Sticky Notes"), /*backgroundImage=*/"", /*backgroundColor=*/QColor(), /*textColor=*/QColor(), /*templateName=*/"1column", /*createIn=*/0);
504         BasketScene *basket = Global::bnpView->currentBasket();
505         basket->load();
506 
507         QDomElement docElem = doc->documentElement();
508         for (QDomNode n = docElem.firstChild(); !n.isNull(); n = n.nextSibling()) {
509             QDomElement e = n.toElement();
510             if ((!e.isNull()) && e.tagName() == "note")
511                 insertTitledNote(basket, e.attribute("title"), e.text());
512         }
513         finishImport(basket);
514     }
515 }
516 
517 
518 
519 // TODO: FIXME: Code duplicated from notecontent.cpp but with UTF-8 encoding.
520 // TODO: FIXME: Later, merge!
loadUtf8FileToString(const QString & fileName)521 QString loadUtf8FileToString(const QString &fileName)
522 {
523     QFile file(fileName);
524     if (file.open(QIODevice::ReadOnly)) {
525         QTextStream stream(&file);
526         stream.setCodec("UTF-8");
527         QString text;
528         text = stream.readAll();
529         file.close();
530         return text;
531     } else
532         return "";
533 }
534 
535 
importTomboy()536 void SoftwareImporters::importTomboy()
537 {
538     /* A notebook becomes a basket.
539     What can be improved: separate big text into notes by "\n\n";
540     remove "\n" from lists - we don't need <br> between two <li>s */
541 
542     BasketScene *basketFromTomboy = 0; // Create the basket ONLY if we found at least one note to add!
543     BasketScene *subBasket; // Where are we adding current note? (can equal basketFromTomboy if the note is in generic notebook)
544     QMap<QString, BasketScene*> subBuskets; // "notebook name - basket" correspondence
545 
546     QString possibleLocations[2] = { "/.tomboy/", "/.local/share/tomboy/" };
547     for (int L=0; L<2; L++) {
548 
549         QString dirPath = QDir::home().absolutePath() + possibleLocations[L];
550         QDir dir(dirPath, QString::null, QDir::Name | QDir::IgnoreCase, QDir::Files | QDir::NoSymLinks);
551         DEBUG_WIN << "Tomboy import: Checking " + dirPath;
552 
553         QStringList list = dir.entryList();
554         for (QStringList::Iterator it = list.begin(); it != list.end(); ++it) {   // For each file
555             if (!(*it).endsWith(QLatin1String(".note")))
556                 continue;
557             QDomDocument *doc = XMLWork::openFile("note", dirPath + *it);
558             if (doc == 0)
559                 continue;
560 
561             if (basketFromTomboy == 0) {
562                 // First create a basket for it:
563                 BasketFactory::newBasket(/*icon=*/IconNames::TOMBOY, /*name=*/i18n("From Tomboy"), /*backgroundImage=*/"", /*backgroundColor=*/QColor(), /*textColor=*/QColor(), /*templateName=*/"1column", /*createIn=*/0);
564                 basketFromTomboy = Global::bnpView->currentBasket();
565                 basketFromTomboy->load();
566             }
567 
568             QDomElement docElem = doc->documentElement();
569             QString title = XMLWork::getElementText(docElem, "title");
570             QString notebook = getFirstTomboyTag(docElem); //!< Tomboy notebook name
571 
572             // Find subbasket or create a new one
573             if (notebook.length() > 0) {
574                 if (subBuskets.contains(notebook))
575                     subBasket = subBuskets[notebook]; else
576                 {
577                     BasketFactory::newBasket(IconNames::TOMBOY, notebook, "", QColor(), QColor(), "1column", /*createIn=*/basketFromTomboy);
578                     subBasket = Global::bnpView->currentBasket(); //TODO: ? change newBasket() so that it returns pointer and we don't have to load it
579                     subBasket->load();
580 
581                     subBuskets.insert(notebook, subBasket);
582                 }
583             } else
584                 subBasket = basketFromTomboy; // will insert to the root basket
585 
586 
587             // DOES NOT REALLY WORKS:
588             //QDomElement contentElement = XMLWork::getElement(docElem, "text/note-content");
589             //QString content = XMLWork::innerXml(contentElement);
590 
591             // Isolate "<note-content version="0.1">CONTENT</note-content>"!
592             QString xml = loadUtf8FileToString(dirPath + *it);
593             xml = xml.mid(xml.indexOf("<note-content"));
594             xml = xml.mid(xml.indexOf(">") + 1);
595             xml = xml.mid(0, xml.indexOf("</note-content>"));
596 
597             if (!title.isEmpty() && !/*content*/xml.isEmpty()) {
598                 insertTitledNote(subBasket, title, fromTomboy(xml/*content*/), Qt::RichText);
599                 DEBUG_WIN << QString("Tomboy import: Inserting note '%1' into '%2'").arg(title, notebook);
600             }
601         }
602 
603     }
604 
605     if (basketFromTomboy)
606         finishImport(basketFromTomboy);
607 }
608 
importJreepadFile()609 void SoftwareImporters::importJreepadFile(){
610     typedef QPair<BasketScene *, QDomElement> basketAndElementPair;
611 
612     QString fileName = QFileDialog::getOpenFileName(0, QString(), "kfiledialog:///:ImportJreepadFile",
613                                                     "*.xml|XML files");
614     if (fileName.isEmpty()) {
615         return;
616     }
617 
618     basketAndElementPair newElement;
619     basketAndElementPair currentElement;
620     QList<basketAndElementPair> elements;
621     QList<BasketScene*> basketList;
622 
623     QDomDocument *doc = XMLWork::openFile("node", fileName);
624     newElement.second = doc->documentElement();
625 
626     BasketScene *basket = 0;
627     BasketFactory::newBasket(/*icon=*/"xml", /*name=*/doc->documentElement().attribute("title"),
628                              /*backgroundImage=*/"", /*backgroundColor=*/QColor(),
629                              /*textColor=*/QColor(), /*templateName=*/"1column", /*createIn=*/0);
630     basket = Global::bnpView->currentBasket();
631     basket->load();
632     basketList << basket;
633     newElement.first = basket;
634 
635     elements << newElement;
636 
637     while ( !elements.isEmpty() ) {
638         currentElement = elements.takeFirst();
639         for (QDomNode n = currentElement.second.firstChild(); !n.isNull(); n = n.nextSibling()) {
640             if ( n.isText() ) {
641                 basket = currentElement.first;
642                 Note *note = NoteFactory::createNoteFromText(n.toText().data(), basket);
643                 basket->insertNote(note, basket->firstNote(),
644                                    Note::BottomColumn, QPoint(), /*animate=*/false);
645             } else if ( n.isElement() ) {
646                 BasketFactory::newBasket(/*icon=*/"xml", /*name=*/n.toElement().attribute("title"),
647                                          /*backgroundImage=*/"", /*backgroundColor=*/QColor(),
648                                          /*textColor=*/QColor(), /*templateName=*/"1column",
649                                          /*createIn=*/currentElement.first);
650                 basket = Global::bnpView->currentBasket();
651                 basket->load();
652                 basketList << basket;
653                 newElement.first = basket;
654                 newElement.second = n.toElement();
655                 elements << newElement;
656             }
657         }
658     }
659 
660     foreach (basket, basketList) {
661         finishImport(basket);
662     }
663 }
664 
importTextFile()665 void SoftwareImporters::importTextFile()
666 {
667     QString fileName = QFileDialog::getOpenFileName(0, QString(), "kfiledialog:///:ImportTextFile", "*|All files");
668     if (fileName.isEmpty())
669         return;
670 
671     TextFileImportDialog dialog;
672     if (dialog.exec() == QDialog::Rejected)
673         return;
674     QString separator = dialog.separator();
675 
676 
677     QFile file(fileName);
678     if (file.open(QIODevice::ReadOnly)) {
679         QTextStream stream(&file);
680         QString content = stream.readAll();
681         QStringList list = (separator.isEmpty()
682                             ? QStringList(content)
683                             : content.split(separator)
684                            );
685 
686         // First create a basket for it:
687         QString title = i18nc("From TextFile.txt", "From %1", QUrl::fromLocalFile(fileName).fileName());
688         BasketFactory::newBasket(/*icon=*/"txt", title, /*backgroundImage=*/"", /*backgroundColor=*/QColor(), /*textColor=*/QColor(), /*templateName=*/"1column", /*createIn=*/0);
689         BasketScene *basket = Global::bnpView->currentBasket();
690         basket->load();
691 
692         // Import every notes:
693         for (QStringList::Iterator it = list.begin(); it != list.end(); ++it) {
694             Note *note = NoteFactory::createNoteFromText((*it).trimmed(), basket);
695             basket->insertNote(note, basket->firstNote(), Note::BottomColumn, QPoint(), /*animate=*/false);
696         }
697 
698         // Finish the export:
699         finishImport(basket);
700     }
701 }
702 
703 /** @author Petri Damsten <petri.damsten@iki.fi>
704   */
importKnowIt()705 void SoftwareImporters::importKnowIt()
706 {
707     QUrl url = QFileDialog::getOpenFileUrl(NULL, "", QUrl("kfiledialog:///:ImportKnowIt"),
708                                        "*.kno|KnowIt files\n*|All files");
709     if (!url.isEmpty()) {
710         QFile file(url.path());
711         QFileInfo info(url.path());
712         BasketScene* basket = 0;
713         QStack<BasketScene*> baskets;
714         QString text;
715         int hierarchy = 0;
716 
717         TreeImportDialog dialog;
718         if (dialog.exec() == QDialog::Rejected)
719             return;
720 
721         hierarchy = dialog.choice();
722 
723         BasketFactory::newBasket(/*icon=*/"knowit",
724                                           /*name=*/info.baseName(),
725                                           /*backgroundImage=*/"",
726                                           /*backgroundColor=*/QColor(),
727                                           /*textColor=*/QColor(),
728                                           /*templateName=*/"1column",
729                                           /*createIn=*/0);
730         basket = Global::bnpView->currentBasket();
731         basket->load();
732         baskets.push(basket);
733 
734         if (file.open(QIODevice::ReadOnly)) {
735             QTextStream stream(&file);
736             int level = 0;
737             QString name;
738             QString line;
739             QStringList links;
740             QStringList descriptions;
741 
742             stream.setCodec("UTF-8");
743             while (1) {
744                 line = stream.readLine();
745 
746                 if (line.startsWith(QLatin1String("\\NewEntry")) ||
747                         line.startsWith(QLatin1String("\\CurrentEntry")) || stream.atEnd()) {
748                     while (level + 1 < baskets.size() - baskets.count(0))
749                         baskets.pop();
750                     if (level + 1 > baskets.size() - baskets.count(0))
751                         baskets.push(basket);
752 
753                     if (!name.isEmpty()) {
754                         if ((level == 0 && hierarchy == 1) ||
755                                 (hierarchy == 0)) {
756                             BasketFactory::newBasket(/*icon=*/"knowit",
757                                                               /*name=*/name,
758                                                               /*backgroundImage=*/"",
759                                                               /*backgroundColor=*/QColor(),
760                                                               /*textColor=*/QColor(),
761                                                               /*templateName=*/"1column",
762                                                               /*createIn=*/baskets.top());
763                             basket = Global::bnpView->currentBasket();
764                             basket->load();
765                         }
766 
767                         if (!text.trimmed().isEmpty() ||
768                                 hierarchy == 2 ||
769                                 (hierarchy == 1 && level > 0)) {
770                             insertTitledNote(basket, name, text, Qt::RichText);
771                         }
772                         for (int j = 0; j < links.count(); ++j) {
773                             Note* link;
774                             if (descriptions.count() < j + 1 || descriptions[j].isEmpty())
775                                 link = NoteFactory::createNoteLink(links[j], basket);
776                             else
777                                 link = NoteFactory::createNoteLink(links[j],
778                                                                    descriptions[j], basket);
779                             basket->insertCreatedNote(link);
780                         }
781                         finishImport(basket);
782                     }
783                     if (stream.atEnd())
784                         break;
785 
786                     int i = line.indexOf("Entry") + 6;
787                     int n = line.indexOf(' ', i);
788                     level = line.mid(i, n - i).toInt();
789                     name = line.mid(n + 1);
790                     text = "";
791                     links.clear();
792                     descriptions.clear();
793                 } else if (line.startsWith(QLatin1String("\\Link"))) {
794                     links.append(line.mid(6));
795                 } else if (line.startsWith(QLatin1String("\\Descr"))) {
796                     while (descriptions.count() < links.count() - 1)
797                         descriptions.append("");
798                     descriptions.append(line.mid(7));
799                 } else {
800                     text += line + "\n";
801                 }
802             }
803             file.close();
804         }
805     }
806 }
807 
importTuxCards()808 void SoftwareImporters::importTuxCards()
809 {
810     QString fileName = QFileDialog::getOpenFileName(0, QString(), "kfiledialog:///:ImportTuxCards", "*|All files");
811     if (fileName.isEmpty())
812         return;
813 
814     TreeImportDialog dialog;
815     if (dialog.exec() == QDialog::Rejected)
816         return;
817 
818     int hierarchy = dialog.choice();
819 
820     QDomDocument *document = XMLWork::openFile("tuxcards_data_file"/*"InformationCollection"*/, fileName);
821     if (document == 0) {
822         KMessageBox::error(0, i18n("Can not import that file. It is either corrupted or not a TuxCards file."), i18n("Bad File Format"));
823         return;
824     }
825 
826     QDomElement collection = document->documentElement();
827     int remainingHierarchy = (hierarchy == 0 ? 65000 : 3 - hierarchy);
828     importTuxCardsNode(collection, /*parentBasket=*/0, /*parentNote=*/0, remainingHierarchy);
829 }
830 
831 // TODO: <InformationElement isOpen="true" isEncripted="false"
832 
importTuxCardsNode(const QDomElement & element,BasketScene * parentBasket,Note * parentNote,int remainingHierarchy)833 void SoftwareImporters::importTuxCardsNode(const QDomElement &element, BasketScene *parentBasket, Note *parentNote, int remainingHierarchy)
834 {
835     for (QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling()) {
836         QDomElement e = n.toElement();
837         if (e.isNull() || e.tagName() != "InformationElement") // Cannot handle that!
838             continue;
839 
840         QString icon        = e.attribute("iconFileName");
841         QString name        = XMLWork::getElementText(e, "Description");
842         QString content     = XMLWork::getElementText(e, "Information");
843         bool    isRichText  = (e.attribute("informationFormat") == "RTF");
844         bool    isEncrypted = (e.attribute("isEncripted") == "true");
845         if (icon.isEmpty() || icon == "none")
846             icon = "tuxcards";
847         Note *nContent;
848 
849         if (isEncrypted) {
850             KMessageBox::information(0, i18n("A note is encrypted. The importer does not yet support encrypted notes. Please remove the encryption with TuxCards and re-import the file."), i18n("Encrypted Notes not Supported Yet"));
851             isRichText = true;
852             content = i18n("<font color='red'><b>Encrypted note.</b><br>The importer do not support encrypted notes yet. Please remove the encryption with TuxCards and re-import the file.</font>");
853         }
854 
855         if (remainingHierarchy > 0) {
856             BasketFactory::newBasket(icon, name, /*backgroundImage=*/"", /*backgroundColor=*/QColor(), /*textColor=*/QColor(), /*templateName=*/"1column", parentBasket);
857             BasketScene *basket = Global::bnpView->currentBasket();
858             basket->load();
859 
860             if (isRichText)
861                 nContent = NoteFactory::createNoteHtml(content, basket);
862             else
863                 nContent = NoteFactory::createNoteText(content, basket);
864             basket->insertNote(nContent, basket->firstNote(), Note::BottomColumn, QPoint(), /*animate=*/false);
865 
866             importTuxCardsNode(e, basket, 0, remainingHierarchy - 1);
867             finishImport(basket);
868         } else {
869             Note *nGroup = insertTitledNote(parentBasket, name, content, (isRichText ? Qt::RichText : Qt::PlainText), parentNote);
870             importTuxCardsNode(e, parentBasket, nGroup, remainingHierarchy - 1);
871         }
872     }
873 }
874 
875