]> git.sven.stormbind.net Git - sven/vym.git/blob - exports.cpp
4542441a4221dcf2c427735e1aaea6bbb41d8160
[sven/vym.git] / exports.cpp
1 #include "exports.h"
2
3 #include <cstdlib>
4
5 #include <QDebug>
6 #include <QFileDialog>
7 #include <QMessageBox>
8
9 #include "branchitem.h"
10 #include "file.h"
11 #include "linkablemapobj.h"
12 #include "misc.h"
13 #include "mainwindow.h"
14 #include "warningdialog.h"
15 #include "xsltproc.h"
16 #include "vymprocess.h"
17
18
19 extern Main *mainWindow;
20 extern QDir vymBaseDir;
21 extern QString flagsPath;
22 extern QString vymName;
23 extern QString vymVersion;
24 extern QString vymHome;
25 extern Settings settings;
26 extern QDir lastExportDir;
27
28 ExportBase::ExportBase()
29 {
30     init();
31 }
32
33 ExportBase::ExportBase(VymModel *m)
34 {
35     model=m;
36     init();
37 }
38
39 ExportBase::~ExportBase()
40 {
41     // Cleanup tmpdir
42     removeDir (tmpDir);
43
44     // Remember current directory
45     lastExportDir = QDir(dirPath);
46 }
47
48 void ExportBase::init()
49 {
50     indentPerDepth = "  ";
51     exportName     = "unnamed";
52     lastCommand    = "";
53     bool ok;
54     tmpDir.setPath (makeTmpDir(ok,"vym-export"));
55     if (!tmpDir.exists() || !ok)
56         QMessageBox::critical( 0, QObject::tr( "Error" ),
57                                QObject::tr("Couldn't access temporary directory\n"));
58     cancelFlag = false;
59     defaultDirPath = lastExportDir.absolutePath();
60     dirPath = defaultDirPath;
61 }
62
63 void ExportBase::setDirPath (const QString &s)
64 {
65     if (!s.isEmpty())
66         dirPath=s;
67     // Otherwise lastExportDir is used, which defaults to current dir
68 }
69
70 QString ExportBase::getDirPath()
71 {
72     return dirPath;
73 }
74
75 void ExportBase::setFilePath (const QString &s)
76 {
77     if(!s.isEmpty())
78     {
79         filePath=s;
80         if (!filePath.contains("/"))
81             // Absolute path
82             filePath=lastExportDir.absolutePath() + "/" + filePath;
83     }
84 }
85
86 QString ExportBase::getFilePath ()
87 {
88     if (!filePath.isEmpty())
89         return filePath;
90     else
91         return dirPath + "/" + model->getMapName() + extension;
92 }
93
94 QString ExportBase::getMapName ()
95 {
96     QString fn=basename(filePath);
97     return fn.left(fn.lastIndexOf("."));
98 }
99
100 void ExportBase::setModel(VymModel *m)
101 {
102     model=m;
103 }
104
105 void ExportBase::setWindowTitle (const QString &s)
106 {
107     caption = s;
108 }
109
110 void ExportBase::setName (const QString &s)
111 {
112     exportName = s;
113 }
114
115 QString ExportBase::getName ()
116 {
117     return exportName;
118 }
119
120 void ExportBase::addFilter(const QString &s)
121 {
122     filter=s;
123 }
124
125 void ExportBase::setListTasks(bool b)
126 {
127     listTasks = b;
128 }
129
130 bool ExportBase::execDialog()
131 {
132     QString fn=QFileDialog::getSaveFileName(
133                 NULL,
134                 caption,
135                 dirPath,
136                 filter,
137                 NULL,
138                 QFileDialog::DontConfirmOverwrite);
139
140     if (!fn.isEmpty() )
141     {
142         if (QFile (fn).exists() )
143         {
144             WarningDialog dia;
145             dia.showCancelButton (true);
146             dia.setCaption(QObject::tr("Warning: Overwriting file"));
147             dia.setText(QObject::tr("Exporting to %1 will overwrite the existing file:\n%2").arg(exportName).arg(fn));
148             dia.setShowAgainName("/exports/overwrite/" + exportName);
149             if (!(dia.exec()==QDialog::Accepted))
150             {
151                 cancelFlag=true;
152                 return false;
153             }
154         }
155         dirPath=fn.left(fn.lastIndexOf ("/"));
156         filePath=fn;
157         return true;
158     }
159     return false;
160 }
161
162 bool ExportBase::canceled()
163 {
164     return cancelFlag;
165 }
166
167 void ExportBase::setLastCommand( const QString &s)
168 {
169     lastCommand = s;
170 }
171
172 void ExportBase::completeExport(QString args) 
173 {
174     QString command;
175     if (args.isEmpty()) 
176         // Add at least filepath as argument. exportName is added anyway
177         command = QString("export%1(\"%2\")").arg(exportName).arg(filePath);
178         // new syntax: command = QString("vym.currentMap().exportMap(\"%1\",\"%2\")").arg(exportName).arg(filePath);
179     else
180         command = QString("export%1(%2)").arg(exportName).arg(args);
181         // new syntax: command = QString("vym.currentMap().exportMap(\"%1\",%2)").arg(exportName).arg(args);
182
183     settings.setLocalValue ( model->getFilePath(), "/export/last/exportPath", filePath);
184     settings.setLocalValue ( model->getFilePath(), "/export/last/command", command);
185     settings.setLocalValue ( model->getFilePath(), "/export/last/description", exportName);
186
187     // Trigger saving of export command if it has changed
188     if (model && (lastCommand != command) ) model->setChanged();
189
190     mainWindow->statusMessage(QString("Exported as %1: %2").arg(exportName).arg(filePath));
191 }
192
193 QString ExportBase::getSectionString(TreeItem *start)
194 {
195     // Make prefix like "2.5.3" for "bo:2,bo:5,bo:3"
196     QString r;
197     TreeItem *ti=start;
198     int depth=ti->depth();
199     while (depth>0)
200     {
201         r=QString("%1").arg(1+ti->num(),0,10)+"." + r;
202         ti=ti->parent();
203         depth=ti->depth();
204     }
205     if (r.isEmpty())
206         return r;
207     else
208         return r + " ";
209 }
210
211 QString ExportBase::indent (const int &n, bool useBullet)
212 {
213     QString s;
214     for (int i=0; i<n; i++) s += indentPerDepth;
215     if (useBullet && s.length() >= 2 && bulletPoints.count() > n)
216         s.replace( s.length() - 2, 1, bulletPoints.at(n) );
217     return s;
218 }
219
220 ////////////////////////////////////////////////////////////////////////
221 ExportAO::ExportAO()
222 {
223     exportName="AO";
224     filter="TXT (*.txt);;All (* *.*)";
225     caption=vymName+ " -" +QObject::tr("Export as ASCII")+" "+QObject::tr("(still experimental)");
226     indentPerDepth="   ";
227     bulletPoints.clear();
228     for (int i=0; i<10; i++)
229         bulletPoints << "-";
230 }
231
232 void ExportAO::doExport()   
233 {
234     QFile file (filePath);
235     if ( !file.open( QIODevice::WriteOnly ) )
236     {
237         QMessageBox::critical (0, QObject::tr("Critical Export Error"), QObject::tr("Could not export as AO to %1").arg(filePath));
238         mainWindow->statusMessage(QString(QObject::tr("Export failed.")));
239         return;
240     }
241
242     settings.setLocalValue ( model->getFilePath(), "/export/last/command","exportAO");
243     settings.setLocalValue ( model->getFilePath(), "/export/last/description","A&O report");
244
245     QTextStream ts( &file );
246     ts.setCodec("UTF-8");
247
248     // Main loop over all branches
249     QString s;
250     QString curIndent;
251     QString dashIndent;
252
253     int i;
254     BranchItem *cur=NULL;
255     BranchItem *prev=NULL;
256
257     model->nextBranch (cur,prev);
258     while (cur)
259     {
260         QString line;
261         QString colString="";
262         QString noColString;
263         QString statusString ="";
264         QColor col;
265
266         if (cur->getType()==TreeItem::Branch || cur->getType()==TreeItem::MapCenter)
267         {
268             // Make indentstring
269             curIndent=indent(cur->depth()-4,true);
270
271             if (!cur->hasHiddenExportParent() )
272             {
273                 col=cur->getHeadingColor();
274                 if (col==QColor (255,0,0))
275                     colString="[R] ";
276                 else if (col==QColor (217,81,0))
277                     colString="[O] ";
278                 else if (col==QColor (0,85,0))
279                     colString="[G] ";
280                 else if (cur->depth()==4)
281                     colString=" *  ";
282                 else
283                     colString="    ";
284
285                 noColString=QString(" ").repeated(colString.length() );
286
287                 dashIndent="";
288                 switch (cur->depth())
289                 {
290                 case 0: break;  // Mapcenter (Ignored)
291                 case 1: break;  // Mainbranch "Archive" (Ignored)
292                 case 2: // Title: "Current week number..."
293                     ts << "\n";
294                     ts << underline ( cur->getHeadingPlain(), QString("=") );
295                     ts << "\n";
296                     break;
297                 case 3: // Headings: "Achievement", "Bonus", "Objective", ...
298                     ts << "\n";
299                     ts << underline ( cur->getHeadingPlain(), "-");
300                     ts << "\n";
301                     break;
302                 default:    // depth 4 and higher are the items we need to know
303                     Task *task=cur->getTask();
304                     if (task)
305                     {
306                         // Task status overrides other flags
307                         switch ( task->getStatus() )
308                         {
309                         case Task::NotStarted:
310                             statusString="[NOT STARTED]";
311                             break;
312                         case Task::WIP:
313                             statusString="[WIP]";
314                             break;
315                         case Task::Finished:
316                             statusString="[DONE]";
317                             break;
318                         }
319                     } else
320                     {
321                         if (cur->hasActiveStandardFlag ("hook-green") )
322                             statusString="[DONE]";
323                         else if (cur->hasActiveStandardFlag ("wip"))
324                             statusString="[WIP]";
325                         else if (cur->hasActiveStandardFlag ("cross-red"))
326                             statusString="[NOT STARTED]";
327                     }
328
329                     line += colString;
330                     line += curIndent;
331                     if (cur->depth() >3)
332                         line += cur->getHeadingPlain();
333
334                     // Pad line width before status
335                     i = 80 - line.length() - statusString.length() -1;
336                     for (int j=0; j<i; j++) line += " ";
337                     line += " "  + statusString + "\n";
338
339                     ts << line;
340
341                     // If necessary, write URL
342                     if (!cur->getURL().isEmpty())
343                         ts << noColString << indent(cur->depth()-4, false) + cur->getURL() + "\n";
344
345                     // If necessary, write note
346                     if (!cur->isNoteEmpty())
347                     {
348                         curIndent = noColString + indent(cur->depth()-4,false) + "| ";
349                         s=cur->getNoteASCII( curIndent, 80);
350                         ts << s + "\n";
351                     }
352                     break;
353                 }
354             }
355         }
356         model->nextBranch(cur,prev);
357     }
358     file.close();
359     completeExport();
360 }
361
362 QString ExportAO::underline (const QString &text, const QString &line)
363 {
364     QString r=text + "\n";
365     for (int j=0;j<text.length();j++) r+=line;
366     return r;
367 }
368
369
370 ////////////////////////////////////////////////////////////////////////
371 ExportASCII::ExportASCII() 
372 {
373     exportName = "ASCII";
374     filter = "TXT (*.txt);;All (* *.*)";
375     caption = vymName + " -" + QObject::tr("Export as ASCII");
376 }
377
378 void ExportASCII::doExport()
379 {
380     QFile file (filePath);
381     if ( !file.open( QIODevice::WriteOnly ) )
382     {
383         QMessageBox::critical (0, QObject::tr("Critical Export Error"), QObject::tr("Could not export as ASCII to %1").arg(filePath));
384         mainWindow->statusMessage(QString(QObject::tr("Export failed.")));
385         return;
386     }
387     QTextStream ts( &file );
388     ts.setCodec("UTF-8");
389
390     // Main loop over all branches
391     QString s;
392     QString curIndent;
393     QString dashIndent;
394     int i;
395     BranchItem *cur=NULL;
396     BranchItem *prev=NULL;
397
398     int lastDepth=0;
399
400     QStringList tasks;
401
402     model->nextBranch (cur,prev);
403     while (cur)
404     {
405         if (cur->getType()==TreeItem::Branch || cur->getType()==TreeItem::MapCenter)
406         {
407             // Insert newline after previous list
408             if ( cur->depth() < lastDepth ) ts << "\n";
409
410             // Make indentstring
411             curIndent="";
412             for (i=1;i<cur->depth()-1;i++) curIndent+= indentPerDepth;
413
414             if (!cur->hasHiddenExportParent() )
415             {
416                 //qDebug() << "ExportASCII::  "<<curIndent.toStdString()<<cur->getHeadingPlain().toStdString();
417
418                 dashIndent="";
419                 switch (cur->depth())
420                 {
421                 case 0:
422                     ts << underline (cur->getHeadingPlain(),QString("="));
423                     ts << "\n";
424                     break;
425                 case 1:
426                     ts << "\n";
427                     ts << (underline (getSectionString(cur) + cur->getHeadingPlain(), QString("-") ) );
428                     ts << "\n";
429                     break;
430                 case 2:
431                     ts << "\n";
432                     ts << (curIndent + "* " + cur->getHeadingPlain());
433                     ts << "\n";
434                     dashIndent="  ";
435                     break;
436                 case 3:
437                     ts << (curIndent + "- " + cur->getHeadingPlain());
438                     ts << "\n";
439                     dashIndent="  ";
440                     break;
441                 default:
442                     ts << (curIndent + "- " + cur->getHeadingPlain());
443                     ts << "\n";
444                     dashIndent="  ";
445                     break;
446                 }
447
448                 // If there is a task, save it for potential later display
449                 if (listTasks && cur->getTask() )
450                 {
451                     tasks.append( QString("[%1]: %2").arg(cur->getTask()->getStatusString()).arg(cur->getHeadingPlain() ) );
452                 }
453
454                 // If necessary, write URL
455                 if (!cur->getURL().isEmpty())
456                     ts << (curIndent + dashIndent + cur->getURL()) +"\n";
457
458                 // If necessary, write vymlink
459                 if (!cur->getVymLink().isEmpty())
460                     ts << (curIndent + dashIndent + cur->getVymLink()) +" (vym mindmap)\n";
461
462                 // If necessary, write note
463                 if (!cur->isNoteEmpty())
464                 {
465                     // curIndent +="  | ";
466                     // Only indent for bullet points
467                     if (cur->depth() > 2) curIndent +="  ";
468                     ts << '\n' +  cur->getNoteASCII(curIndent, 80) ;
469                 }
470                 lastDepth = cur->depth();
471             }
472         }
473         model->nextBranch(cur,prev);
474     }
475
476     if (listTasks)
477     {
478         ts << "\n\nTasks\n-----\n\n";
479
480
481         foreach (QString t, tasks)
482         {
483             ts << " - " << t << "\n";
484         }
485     }
486     file.close();
487
488     QString listTasksString = listTasks ? "true" : "false";
489     completeExport( QString("\"%1\",%2").arg(filePath).arg(listTasksString) );
490 }
491
492 QString ExportASCII::underline (const QString &text, const QString &line)
493 {
494     QString r=text + "\n";
495     for (int j=0;j<text.length();j++) r+=line;
496     return r;
497 }
498
499
500 ////////////////////////////////////////////////////////////////////////
501 ExportCSV::ExportCSV() 
502 {
503     exportName="CSV";
504     filter="CSV (*.csv);;All (* *.*)";
505     caption=vymName+ " -" +QObject::tr("Export as CSV");
506 }
507
508 void ExportCSV::doExport()
509 {
510     QFile file (filePath);
511     if ( !file.open( QIODevice::WriteOnly ) )
512     {
513         QMessageBox::critical (0, QObject::tr("Critical Export Error"), QObject::tr("Could not export as CSV to %1").arg(filePath));
514         mainWindow->statusMessage(QString(QObject::tr("Export failed.")));
515         return;
516     }
517     QTextStream ts( &file );
518     ts.setCodec("UTF-8");
519
520     // Write header
521     ts << "\"Note\""  <<endl;
522
523     // Main loop over all branches
524     QString s;
525     QString curIndent("");
526     int i;
527     BranchItem *cur=NULL;
528     BranchItem *prev=NULL;
529     model->nextBranch (cur,prev);
530     while (cur)
531     {
532         if (!cur->hasHiddenExportParent() )
533         {
534             // If necessary, write note
535             if (!cur->isNoteEmpty())
536             {
537                 s =cur->getNoteASCII();
538                 s=s.replace ("\n","\n"+curIndent);
539                 ts << ("\""+s+"\",");
540             } else
541                 ts <<"\"\",";
542
543             // Make indentstring
544             for (i=0;i<cur->depth();i++) curIndent+= "\"\",";
545
546             // Write heading
547             ts << curIndent << "\"" << cur->getHeadingPlain()<<"\""<<endl;
548         }
549
550         model->nextBranch(cur,prev);
551         curIndent="";
552     }
553     file.close();
554     completeExport();
555 }
556
557 ////////////////////////////////////////////////////////////////////////
558 void ExportKDE4Bookmarks::doExport() 
559 {
560     WarningDialog dia;
561     dia.showCancelButton (true);
562     dia.setText(QObject::tr("Exporting the %1 bookmarks will overwrite\nyour existing bookmarks file.").arg("KDE 4"));
563     dia.setCaption(QObject::tr("Warning: Overwriting %1 bookmarks").arg("KDE"));
564     dia.setShowAgainName("/exports/overwrite/KDE4Bookmarks");
565     if (dia.exec()==QDialog::Accepted)
566     {
567         model->exportXML("",tmpDir.path(),false);
568
569         XSLTProc p;
570         p.setInputFile (tmpDir.path()+"/"+model->getMapName()+".xml");
571         p.setOutputFile (tmpDir.home().path()+"/.kde4/share/apps/konqueror/bookmarks.xml");
572         p.setXSLFile (vymBaseDir.path()+"/styles/vym2kdebookmarks.xsl");
573         p.process();
574
575         QString ub=vymBaseDir.path()+"/scripts/update-bookmarks";
576         QProcess *proc= new QProcess ;
577         proc->start( ub);
578         if (!proc->waitForStarted())
579         {
580             QMessageBox::warning(0,
581                                  QObject::tr("Warning"),
582                                  QObject::tr("Couldn't find script %1\nto notifiy Browsers of changed bookmarks.").arg(ub));
583         }
584     }
585 }
586
587 ////////////////////////////////////////////////////////////////////////
588 void ExportFirefoxBookmarks::doExport() 
589 {
590     WarningDialog dia;
591     dia.showCancelButton (true);
592     dia.setText(QObject::tr("Exporting the %1 bookmarks will overwrite\nyour existing bookmarks file.").arg("Firefox"));
593     dia.setCaption(QObject::tr("Warning: Overwriting %1 bookmarks").arg("Firefox"));
594     dia.setShowAgainName("/vym/warnings/overwriteImportBookmarks");
595     if (dia.exec()==QDialog::Accepted)
596     {
597         model->exportXML("",tmpDir.path(),false);
598
599         /*
600     XSLTProc p;
601     p.setInputFile (tmpDir.path()+"/"+me->getMapName()+".xml");
602     p.setOutputFile (tmpDir.home().path()+"/.kde/share/apps/konqueror/bookmarks.xml");
603     p.setXSLFile (vymBaseDir.path()+"/styles/vym2kdebookmarks.xsl");
604     p.process();
605
606     QString ub=vymBaseDir.path()+"/scripts/update-bookmarks";
607     QProcess *proc = new QProcess( );
608     proc->addArgument(ub);
609
610     if ( !proc->start() )
611     {
612         QMessageBox::warning(0,
613         QObject::tr("Warning"),
614         QObject::tr("Couldn't find script %1\nto notifiy Browsers of changed bookmarks.").arg(ub));
615     }
616
617 */
618     }
619 }
620
621 ////////////////////////////////////////////////////////////////////////
622 ExportHTML::ExportHTML():ExportBase()
623 {
624     init();
625 }
626
627 ExportHTML::ExportHTML(VymModel *m):ExportBase(m)
628 {
629     init();
630 }
631
632 void ExportHTML::init()
633 {
634     exportName="HTML";
635     extension=".html";
636     frameURLs=true;
637 }
638
639 QString ExportHTML::getBranchText(BranchItem *current)
640 {
641     if (current)
642     {
643         bool vis = false;
644         QRectF hr;
645         LinkableMapObj *lmo = current->getLMO();
646         if (lmo)
647         {
648             hr = ((BranchObj*)lmo)->getBBoxHeading();
649             vis = lmo->isVisibleObj();
650         }
651         QString col;
652         QString id = model->getSelectString(current);
653         if (dia.useTextColor)
654             col = QString("style='color:%1'").arg(current->getHeadingColor().name());
655         QString s = QString("<span class='vym-branch-%1' %2 id='%3'>")
656                 .arg(current->depth())
657                 .arg(col)
658                 .arg(id);
659         QString url = current->getURL();
660         QString heading = quotemeta(current->getHeadingPlain());
661
662         // Task flags
663         QString taskFlags;
664         if (dia.useTaskFlags)
665         {
666             Task *task = current->getTask();
667             if (task)
668             {
669                 QString taskName = task->getIconString();
670                 taskFlags += QString("<img src=\"flags/flag-%1.png\" alt=\"%2\">")
671                     .arg(taskName)
672                     .arg(QObject::tr("Flag: %1","Alt tag in HTML export").arg(taskName));
673             }
674         }
675
676         // User flags
677         QString userFlags;
678         if (dia.useUserFlags)
679         {
680             foreach (QString flag, current->activeStandardFlagNames())
681                 userFlags += QString("<img src=\"flags/flag-%1.png\" alt=\"%2\">")
682                     .arg(flag)
683                     .arg(QObject::tr("Flag: %1","Alt tag in HTML export").arg(flag));
684         }
685
686         // Numbering
687         QString number;
688         if (dia.useNumbering) number = getSectionString(current) + " ";
689         
690         // URL
691         if (!url.isEmpty())
692         {
693             s += QString ("<a href=\"%1\">%2<img src=\"flags/flag-url.png\" alt=\"%3\"></a>")
694                     .arg(url)
695                     .arg(number + taskFlags + heading + userFlags)
696                     .arg(QObject::tr("Flag: url","Alt tag in HTML export"));
697
698             QRectF fbox = current->getBBoxURLFlag ();
699             if (vis)
700                 imageMap += QString("  <area shape='rect' coords='%1,%2,%3,%4' href='%5' alt='%6'>\n")
701                         .arg(fbox.left()   - offset.x())
702                         .arg(fbox.top()    - offset.y())
703                         .arg(fbox.right()  - offset.x())
704                         .arg(fbox.bottom() - offset.y())
705                         .arg(url)
706                         .arg(QObject::tr("External link: %1","Alt tag in HTML export").arg(heading));
707         } else
708             s += number + taskFlags + heading + userFlags;
709
710         s += "</span>";
711
712         // Create imagemap
713         if (vis && dia.includeMapImage)
714             imageMap += QString("  <area shape='rect' coords='%1,%2,%3,%4' href='#%5' alt='%6'>\n")
715                     .arg(hr.left()   - offset.x())
716                     .arg(hr.top()    - offset.y())
717                     .arg(hr.right()  - offset.x())
718                     .arg(hr.bottom() - offset.y())
719                     .arg(id)
720                     .arg(heading);
721
722         // Include images experimental
723         if (dia.includeImages)
724         {
725             int imageCount = current->imageCount();
726             ImageItem *image; 
727             QString imagePath;
728             for (int i=0; i< imageCount; i++)
729             {
730                 image = current->getImageNum(i);
731                 imagePath =  "image-" + image->getUuid().toString() + ".png";
732                 image->save( dirPath + "/" + imagePath, "PNG");
733                 s += "</br><img src=\"" + imagePath;
734                 s += "\" alt=\"" + QObject::tr("Image: %1","Alt tag in HTML export").arg(image->getOriginalFilename());
735                 s += "\"></br>";
736             }
737         }
738
739         // Include note
740         if (!current->isNoteEmpty())
741         {
742             VymNote  note = current->getNote();
743             QString n;
744             if (note.isRichText())
745             {
746                 n = note.getText();
747                 QRegExp re("<p.*>");
748                 re.setMinimal (true);
749                 if (current->getNote().getFontHint() == "fixed")
750                     n.replace(re,"<p class=\"vym-fixed-note-paragraph\">");
751                 else
752                     n.replace(re,"<p class=\"vym-note-paragraph\">");
753
754                 re.setPattern("</?html>");
755                 n.replace(re,"");
756
757                 re.setPattern("</?head.*>");
758                 n.replace(re,"");
759
760                 re.setPattern("</?body.*>");
761                 n.replace(re,"");
762
763                 re.setPattern("</?meta.*>");
764                 n.replace(re,"");
765
766                 re.setPattern("<style.*>.*</style>");
767                 n.replace(re,"");
768
769                 //re.setPattern("<!DOCTYPE.*>");
770                 //n.replace(re,"");
771             }
772             else
773             {
774                 n = current->getNoteASCII().replace ("<","&lt;").replace (">","&gt;");
775                 n.replace("\n","<br/>");
776                 if (current->getNote().getFontHint()=="fixed")
777                     n = "<pre>" + n + "</pre>";
778             } 
779             s += "\n<table class=\"vym-note\"><tr><td class=\"vym-note-flag\">\n<td>\n" + n + "\n</td></tr></table>\n";
780         }
781         return s;
782     }
783     return QString();
784 }
785
786 QString ExportHTML::buildList (BranchItem *current)
787 {
788     QString r;
789
790     uint i = 0;
791     uint visChilds = 0;
792
793     BranchItem *bi = current->getFirstBranch();
794
795     QString ind = "\n" + indent(current->depth() + 1, false);
796
797     QString sectionBegin;
798     QString sectionEnd;
799     QString itemBegin;
800     QString itemEnd;
801
802     switch (current->depth() + 1)
803     {
804     case 0:
805         sectionBegin = "";
806         sectionEnd   = "";
807         itemBegin    = "<h1>";
808         itemEnd      = "</h1>";
809         break;
810     case 1:
811         sectionBegin = "";
812         sectionEnd   = "";
813         itemBegin    = "<h2>";
814         itemEnd      = "</h2>";
815         break;
816     default:
817         sectionBegin = "<ul " + QString("class=\"vym-list-ul-%1\"").arg(current->depth() + 1)  +">";
818         sectionEnd   = "</ul>";
819         itemBegin    = "  <li>";
820         itemEnd      = "  </li>";
821         break;
822     }
823     
824     if (bi && !bi->hasHiddenExportParent() && !bi->isHidden() )
825     {
826         r += ind + sectionBegin;
827         while (bi)
828         {
829             if (!bi->hasHiddenExportParent() && !bi->isHidden())
830             {
831                 visChilds++;
832                 r += ind + itemBegin;
833                 r += getBranchText (bi);
834
835                 if (itemBegin.startsWith("<h") )
836                     r += itemEnd + buildList (bi);
837                 else
838                     r += buildList (bi) + itemEnd;
839             }
840             i++;
841             bi = current->getBranchNum(i);
842         }
843         r += ind + sectionEnd;
844     }
845
846     return r;
847 }
848
849 QString ExportHTML::createTOC()
850 {
851     QString toc;
852     QString number;
853     toc += "<table class=\"vym-toc\">\n";
854     toc += "<tr><td class=\"vym-toc-title\">\n";
855     toc += QObject::tr("Contents:","Used in HTML export");
856     toc += "\n";
857     toc += "</td></tr>\n";
858     toc += "<tr><td>\n";
859     BranchItem *cur  = NULL;
860     BranchItem *prev = NULL;
861     model->nextBranch(cur, prev);
862     while (cur)
863     {
864         if (!cur->hasHiddenExportParent() && !cur->hasScrolledParent() )
865         {
866             if (dia.useNumbering) number = getSectionString(cur);
867             toc += QString("<div class=\"vym-toc-branch-%1\">").arg(cur->depth());
868             toc += QString("<a href=\"#%1\"> %2 %3</a></br>\n")
869                     .arg(model->getSelectString(cur))
870                     .arg(number)
871                     .arg(quotemeta( cur->getHeadingPlain() ));
872             toc += "</div>";
873         }
874         model->nextBranch(cur,prev);
875     }
876     toc += "</td></tr>\n";
877     toc += "</table>\n";
878     return toc;
879 }
880
881 void ExportHTML::doExport(bool useDialog) 
882 {
883     // Setup dialog and read settings
884     dia.setMapName (model->getMapName());
885     dia.setFilePath (model->getFilePath());
886     dia.readSettings();
887
888     if (dirPath != defaultDirPath)
889         dia.setDirectory(dirPath);
890
891     if (useDialog)
892     {
893         if (dia.exec()!=QDialog::Accepted) return;
894         model->setChanged();
895     }
896
897     // Check, if warnings should be used before overwriting
898     // the output directory
899     if (dia.getDir().exists() && dia.getDir().count()>0)
900     {
901         WarningDialog warn;
902         warn.showCancelButton (true);
903         warn.setText(QString(
904                          "The directory %1 is not empty.\n"
905                          "Do you risk to overwrite some of its contents?").arg(dia.getDir().absolutePath() ));
906         warn.setCaption("Warning: Directory not empty");
907         warn.setShowAgainName("mainwindow/export-XML-overwrite-dir");
908
909         if (warn.exec()!=QDialog::Accepted)
910         {
911             mainWindow->statusMessage(QString(QObject::tr("Export aborted.")));
912             return;
913         }
914     }
915
916     dirPath=dia.getDir().absolutePath();
917     filePath=getFilePath();
918     
919     // Copy CSS file
920     if (dia.css_copy)
921     {
922         cssSrc=dia.getCssSrc();
923         cssDst=dirPath + "/" + basename(dia.getCssDst());
924         if (cssSrc.isEmpty() )
925         {
926             QMessageBox::critical( 0,
927                                    QObject:: tr( "Critical" ),
928                                    QObject::tr("Could not find stylesheet %1").arg(cssSrc));
929             return;
930         }
931         QFile src(cssSrc);
932         QFile dst(cssDst);
933         if (dst.exists() ) dst.remove();
934
935         if (!src.copy(cssDst))
936         {
937             QMessageBox::critical (0,
938                                    QObject::tr( "Error","ExportHTML" ),
939                                    QObject::tr("Could not copy\n%1 to\n%2","ExportHTML").arg(cssSrc).arg(cssDst));
940             return;
941         }
942     }
943
944     // Copy flags
945     QDir flagsDst(dia.getDir().absolutePath() + "/flags");
946     if (!flagsDst.exists())
947     {
948         if (!dia.getDir().mkdir("flags"))
949         {
950             QMessageBox::critical( 0,
951                                    QObject::tr( "Critical" ),
952                                    QObject::tr("Trying to create directory for flags:") + "\n\n" +
953                                    QObject::tr("Could not create %1").arg(flagsDst.absolutePath()));
954             return;
955         }
956     }
957
958     QDir flagsSrc(flagsPath);   // FIXME-3 don't use flagsPath as source anymore, but copy required flags directly from memory
959     if (!copyDir(flagsSrc, flagsDst, true))
960     {
961         QMessageBox::critical( 0,
962                                QObject::tr( "Critical" ),
963                                QObject::tr("Could not copy %1 to %2").arg(flagsSrc.absolutePath()).arg(flagsDst.absolutePath()));
964         return;
965     }
966
967     // Open file for writing
968     QFile file (filePath);
969     if ( !file.open( QIODevice::WriteOnly ) )
970     {
971         QMessageBox::critical (0,
972                                QObject::tr("Critical Export Error"),
973                                QObject::tr("Trying to save HTML file:") + "\n\n"+
974                                QObject::tr("Could not write %1").arg(filePath));
975         mainWindow->statusMessage(QString(QObject::tr("Export failed.")));
976         return;
977     }
978     QTextStream ts( &file );
979     ts.setCodec("UTF-8");
980
981     // Hide stuff during export
982     model->setExportMode (true);
983
984     // Write header
985     ts << "<html>";
986     ts << "\n<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\"> ";
987     ts << "\n<meta name=\"generator=\" content=\" vym - view your mind - " + vymVersion + " - " + vymHome + "\">";
988     ts << "\n<meta name=\"author\" content=\"" + quotemeta(model->getAuthor()) + "\"> ";
989     ts << "\n<meta name=\"description\" content=\"" + quotemeta(model->getComment()) + "\"> ";
990     ts << "\n<link rel='stylesheet' id='css.stylesheet' href='" << basename(cssDst) << "' />\n";
991     QString title=model->getTitle();
992     if (title.isEmpty()) title=model->getMapName();
993     ts << "\n<head><title>" + quotemeta(title) + "</title></head>";
994     ts << "\n<body>\n";
995
996     // Include image
997     // (be careful: this resets Export mode, so call before exporting branches)
998     if (dia.includeMapImage)
999     {
1000         QString mapName = getMapName();
1001         ts << "<center><img src=\"" << mapName << ".png\"";
1002         ts << "alt=\"" << QObject::tr("Image of map: %1.vym","Alt tag in HTML export").arg(mapName) << "\"";
1003         ts << " usemap='#imagemap'></center>\n";
1004         offset = model->exportImage (dirPath + "/" + mapName + ".png", false, "PNG");
1005     }
1006
1007     // Include table of contents
1008     if (dia.useTOC) ts << createTOC();
1009
1010     // Main loop over all mapcenters
1011     ts << buildList(model->getRootItem()) << "\n";
1012
1013     // Imagemap
1014     ts << "<map name='imagemap'>\n" + imageMap + "</map>\n";
1015
1016     // Write footer
1017     ts << "<hr/>\n";
1018     ts << "<table class=\"vym-footer\">   \n\
1019         <tr> \n\
1020         <td class=\"vym-footerL\">" + filePath + "</td> \n\
1021             <td class=\"vym-footerC\">" + model->getDate() + "</td> \n\
1022             <td class=\"vym-footerR\"> <a href='" + vymHome + "'>vym " + vymVersion + "</a></td> \n\
1023             </tr> \n \
1024             </table>\n";
1025             ts << "</body></html>";
1026     file.close();
1027
1028     if (!dia.postscript.isEmpty())
1029     {
1030         VymProcess p;
1031         p.runScript (dia.postscript,dirPath + "/" + filePath);  
1032     }
1033
1034     completeExport( QString("\"%1\",\"%2\"").arg(dirPath).arg(filePath));
1035
1036     dia.saveSettings();
1037     model->setExportMode (false);
1038 }
1039
1040 ////////////////////////////////////////////////////////////////////////
1041 void ExportTaskjuggler::doExport() 
1042 {
1043     model->exportXML("",tmpDir.path(),false);
1044
1045     XSLTProc p;
1046     p.setInputFile (tmpDir.path()+"/"+model->getMapName()+".xml");
1047     p.setOutputFile (filePath);
1048     p.setXSLFile (vymBaseDir.path()+"/styles/vym2taskjuggler.xsl");
1049     p.process();
1050
1051     completeExport();
1052 }
1053
1054 ////////////////////////////////////////////////////////////////////////
1055 ExportOrgMode::ExportOrgMode() 
1056 {
1057     exportName="OrgMode";
1058     filter="org-mode (*.org);;All (* *.*)";
1059 }
1060
1061 void ExportOrgMode::doExport() 
1062 {
1063     // Exports a map to an org-mode file.
1064     // This file needs to be read
1065     // by EMACS into an org mode buffer
1066     QFile file (filePath);
1067     if ( !file.open( QIODevice::WriteOnly ) )
1068     {
1069         QMessageBox::critical (0, QObject::tr("Critical Export Error"), QObject::tr("Could not export as OrgMode to %1").arg(filePath));
1070         mainWindow->statusMessage(QString(QObject::tr("Export failed.")));
1071         return;
1072     }
1073     QTextStream ts( &file );
1074     ts.setCodec("UTF-8");
1075
1076     // Main loop over all branches
1077     QString s;
1078     int i;
1079     BranchItem *cur=NULL;
1080     BranchItem *prev=NULL;
1081     model->nextBranch(cur,prev);
1082     while (cur)
1083     {
1084         if (!cur->hasHiddenExportParent() )
1085         {
1086             for(i=0;i<=cur->depth();i++)
1087                 ts << ("*");
1088             ts << (" " + cur->getHeadingPlain()+ "\n");
1089             // If necessary, write note
1090             if (!cur->isNoteEmpty())
1091             {
1092                 ts << (cur->getNoteASCII());
1093                 ts << ("\n");
1094             }
1095         }
1096         model->nextBranch(cur,prev);
1097     }
1098     file.close();
1099
1100     completeExport();
1101 }
1102
1103 ////////////////////////////////////////////////////////////////////////
1104 ExportLaTeX::ExportLaTeX()
1105 {
1106     exportName="LaTeX";
1107     filter="LaTeX files (*.tex);;All (* *.*)";
1108
1109     // Note: key in hash on left side is the regular expression, which
1110     // will be replaced by string on right side
1111     // E.g. a literal $ will be replaced by \$
1112     esc["\\$"]="\\$";
1113     esc["\\^"]="\\^";
1114     esc["%"]="\\%";
1115     esc["&"]="\\&";
1116     esc["~"]="\\~";
1117     esc["_"]="\\_";
1118     esc["\\\\"]="\\";
1119     esc["\\{"]="\\{";
1120     esc["\\}"]="\\}";
1121 }
1122
1123 QString ExportLaTeX::escapeLaTeX(const QString &s)
1124 {
1125     QString r=s;
1126
1127     QRegExp rx;
1128     rx.setMinimal(true);
1129
1130     foreach (QString p,esc.keys() )
1131     {
1132         rx.setPattern (p);
1133         r.replace (rx, esc[p] );
1134     }
1135     return r;
1136 }
1137
1138 void ExportLaTeX::doExport()
1139 {
1140     // Exports a map to a LaTex file.
1141     // This file needs to be included
1142     // or inported into a LaTex document
1143     // it will not add a preamble, or anything
1144     // that makes a full LaTex document.
1145     QFile file (filePath);
1146     if ( !file.open( QIODevice::WriteOnly ) ) {
1147         QMessageBox::critical (
1148                     0,
1149                     QObject::tr("Critical Export Error"),
1150                     QObject::tr("Could not export as LaTeX to %1").arg(filePath));
1151         mainWindow->statusMessage(QString(QObject::tr("Export failed.")));
1152         return;
1153     }
1154     QTextStream ts( &file );
1155     ts.setCodec("UTF-8");
1156
1157     // Read default section names
1158     QStringList sectionNames;
1159     sectionNames << ""
1160                  << "chapter"
1161                  << "section"
1162                  << "subsection"
1163                  << "subsubsection"
1164                  << "paragraph";
1165
1166     for (int i=0; i<6; i++)
1167         sectionNames.replace(i,settings.value(
1168                                  QString("/export/latex/sectionName-%1").arg(i),sectionNames.at(i)).toString() );
1169
1170     // Main loop over all branches
1171     QString s;
1172     BranchItem *cur=NULL;
1173     BranchItem *prev=NULL;
1174     model->nextBranch(cur,prev);
1175     while (cur)
1176     {
1177         if (!cur->hasHiddenExportParent() )
1178         {
1179             int d=cur->depth();
1180             s=escapeLaTeX (cur->getHeadingPlain() );
1181             if ( sectionNames.at(d).isEmpty() || d>=sectionNames.count() )
1182                 ts << s << endl;
1183             else
1184                 ts << endl
1185                    << "\\"
1186                    << sectionNames.at(d)
1187                    << "{"
1188                    << s
1189                    << "}"
1190                    << endl;
1191
1192             // If necessary, write note
1193             if (!cur->isNoteEmpty()) {
1194                 ts << (cur->getNoteASCII());
1195                 ts << endl;
1196             }
1197         }
1198         model->nextBranch(cur,prev);
1199     }
1200     
1201     file.close();
1202     completeExport();
1203 }
1204
1205 ////////////////////////////////////////////////////////////////////////
1206 ExportOO::ExportOO()
1207 {
1208     exportName="Impress";
1209     filter="LibreOffice Impress (*.odp);;All (* *.*)";
1210     caption=vymName+ " -" +QObject::tr("Export as LibreOffice Impress presentation");
1211     useSections=false;
1212 }
1213
1214 ExportOO::~ExportOO()
1215 {
1216 }   
1217
1218 QString ExportOO::buildList (TreeItem *current)
1219 {
1220     QString r;
1221
1222     uint i=0;
1223     BranchItem *bi=current->getFirstBranch();
1224     if (bi)
1225     {
1226         // Start list
1227         r+="<text:list text:style-name=\"vym-list\">\n";
1228         while (bi)
1229         {
1230             if (!bi->hasHiddenExportParent() )
1231             {
1232                 r += "<text:list-item><text:p >";
1233                 r += quotemeta(bi->getHeadingPlain());
1234                 // If necessary, write note
1235                 if (! bi->isNoteEmpty())
1236                     r += "<text:line-break/>" + bi->getNoteASCII();
1237                 r += "</text:p>";
1238                 r += buildList (bi);  // recursivly add deeper branches
1239                 r += "</text:list-item>\n";
1240             }
1241             i++;
1242             bi = current->getBranchNum(i);
1243         }
1244         r += "</text:list>\n";
1245     }
1246     return r;
1247 }
1248
1249
1250 void ExportOO::exportPresentation()
1251 {
1252     QString allPages;
1253
1254     BranchItem *firstMCO=(BranchItem*)(model->getRootItem()->getFirstBranch());
1255     if (!firstMCO)
1256     {
1257         QMessageBox::critical (0,QObject::tr("Critical Export Error"),QObject::tr("No objects in map!"));
1258         return;
1259     }
1260
1261     // Insert new content
1262     // FIXME add extra title in mapinfo for vym 1.13.x
1263     content.replace ("<!-- INSERT TITLE -->",quotemeta(firstMCO->getHeadingPlain()));
1264     content.replace ("<!-- INSERT AUTHOR -->",quotemeta(model->getAuthor()));
1265
1266     QString onePage;
1267     QString list;
1268     
1269     BranchItem *sectionBI;
1270     int i=0;
1271     BranchItem *pagesBI;
1272     int j=0;
1273
1274     int mapcenters=model->getRootItem()->branchCount();
1275
1276     // useSections already has been set in setConfigFile
1277     if (mapcenters>1)
1278         sectionBI=firstMCO;
1279     else
1280         sectionBI=firstMCO->getFirstBranch();
1281
1282     // Walk sections
1283     while (sectionBI && !sectionBI->hasHiddenExportParent() )
1284     {
1285         if (useSections)
1286         {
1287             // Add page with section title
1288             onePage=sectionTemplate;
1289             onePage.replace ("<!-- INSERT PAGE HEADING -->", quotemeta(sectionBI->getHeadingPlain() ) );
1290             allPages+=onePage;
1291             pagesBI=sectionBI->getFirstBranch();
1292         } else
1293         {
1294             //i=-2; // only use inner loop to
1295             // turn mainbranches into pages
1296             //sectionBI=firstMCO;
1297             pagesBI=sectionBI;
1298         }
1299
1300         j=0;
1301         while (pagesBI && !pagesBI->hasHiddenExportParent() )
1302         {
1303             // Add page with list of items
1304             onePage=pageTemplate;
1305             onePage.replace ("<!-- INSERT PAGE HEADING -->", quotemeta (pagesBI->getHeadingPlain() ) );
1306             list=buildList (pagesBI);
1307             onePage.replace ("<!-- INSERT LIST -->", list);
1308             allPages+=onePage;
1309             if (pagesBI!=sectionBI)
1310             {
1311                 j++;
1312                 pagesBI=((BranchItem*)pagesBI->parent())->getBranchNum(j);
1313             } else
1314                 pagesBI=NULL;    // We are already iterating over the sectionBIs
1315         }
1316         i++;
1317         if (mapcenters>1 )
1318             sectionBI=model->getRootItem()->getBranchNum (i);
1319         else
1320             sectionBI=firstMCO->getBranchNum (i);
1321     }
1322     
1323     content.replace ("<!-- INSERT PAGES -->",allPages);
1324
1325     // Write modified content
1326     QFile f (contentFile);
1327     if ( !f.open( QIODevice::WriteOnly ) )
1328     {
1329         QMessageBox::critical (0,QObject::tr("Critical Export Error"),QObject::tr("Could not write %1").arg(contentFile));
1330         mainWindow->statusMessage(QString(QObject::tr("Export failed.")));
1331         return;
1332     }
1333
1334     QTextStream t( &f );
1335     t.setCodec("UTF-8");
1336     t << content;
1337     f.close();
1338
1339     // zip tmpdir to destination
1340     zipDir (tmpDir,filePath);
1341
1342     completeExport(QString("\"%1\", \"%2\"").arg(filePath).arg(configFile) );
1343 }
1344
1345 bool ExportOO::setConfigFile (const QString &cf)
1346 {
1347     configFile=cf;
1348     int i=cf.lastIndexOf ("/");
1349     if (i>=0) configDir=cf.left(i);
1350     SimpleSettings set;
1351
1352     if (!set.readSettings(configFile))
1353     {
1354         QMessageBox::critical (0,QObject::tr("Critical Export Error"),QObject::tr("Couldn't read settings from \"%1\"").arg(configFile));
1355         return false;
1356     }
1357
1358     // set paths
1359     templateDir=configDir+"/"+set.value ("Template");
1360
1361     QDir d (templateDir);
1362     if (!d.exists())
1363     {
1364         QMessageBox::critical (0,QObject::tr("Critical Export Error"),QObject::tr("Check \"%1\" in\n%2").arg("Template="+set.value ("Template")).arg(configFile));
1365         return false;
1366
1367     }
1368
1369     contentTemplateFile = templateDir + "content-template.xml";
1370     pageTemplateFile    = templateDir + "page-template.xml";
1371     sectionTemplateFile = templateDir + "section-template.xml";
1372     contentFile         = tmpDir.path() + "/content.xml";
1373
1374     if (set.value("useSections").contains("yes"))
1375         useSections=true;
1376
1377     // Copy template to tmpdir
1378     copyDir (templateDir,tmpDir);
1379
1380     // Read content-template
1381     if (!loadStringFromDisk (contentTemplateFile,content))
1382     {
1383         QMessageBox::critical (0,QObject::tr("Critical Export Error"),QObject::tr("Could not read %1").arg(contentTemplateFile));
1384         return false;
1385     }
1386
1387     // Read page-template
1388     if (!loadStringFromDisk (pageTemplateFile,pageTemplate))
1389     {
1390         QMessageBox::critical (0,QObject::tr("Critical Export Error"),QObject::tr("Could not read %1").arg(pageTemplateFile));
1391         return false;
1392     }
1393     
1394     // Read section-template
1395     if (useSections && !loadStringFromDisk (sectionTemplateFile,sectionTemplate))
1396     {
1397         QMessageBox::critical (0,QObject::tr("Critical Export Error"),QObject::tr("Could not read %1").arg(sectionTemplateFile));
1398         return false;
1399     }
1400     return true;
1401 }
1402