]> git.sven.stormbind.net Git - sven/vym.git/blob - vymmodel.cpp
Import Upstream version 2.6.11
[sven/vym.git] / vymmodel.cpp
1 #include <QApplication>
2 #include <QSvgGenerator>
3
4 #if defined(VYM_DBUS)
5 #include <QtDBus/QDBusConnection>
6 #endif
7
8 #ifndef Q_OS_WIN
9 #include <unistd.h>
10 #else
11 #define sleep Sleep
12 #endif
13
14 #include <QColorDialog>
15 #include <QFileDialog>
16 #include <QMessageBox>
17 #include <QPrinter>
18
19 #include "vymmodel.h"
20
21 #include "attributeitem.h"
22 #include "treeitem.h"
23 #include "branchitem.h"
24 #include "bugagent.h"
25 #include "downloadagent.h"
26 #include "editxlinkdialog.h"
27 #include "exports.h"
28 #include "exporthtmldialog.h"
29 #include "file.h"
30 #include "findresultmodel.h"
31 #include "lockedfiledialog.h"
32 #include "mainwindow.h"
33 #include "misc.h"
34 #include "noteeditor.h"
35 #include "options.h"
36 #include "parser.h"
37 #include "vymprocess.h"
38 #include "scripteditor.h" 
39 #include "slideitem.h"
40 #include "slidemodel.h"
41 #include "taskeditor.h"
42 #include "taskmodel.h"
43 #include "warningdialog.h"
44 #include "xlinkitem.h"
45 #include "xlinkobj.h"
46 #include "xml-freemind.h"
47 #include "xmlobj.h"
48 #include "xml-vym.h"
49
50 #ifdef Q_OS_WIN
51 #include <windows.h>
52 #endif
53
54 extern bool debug;
55 extern bool testmode;
56 extern Main *mainWindow;
57
58 extern Settings settings;
59 extern QString tmpVymDir;
60 extern QString macroPath;
61
62 extern NoteEditor *noteEditor;
63 extern TaskEditor *taskEditor;
64 extern ScriptEditor *scriptEditor;
65 extern FlagRow *standardFlagsMaster;
66
67 extern Options options;
68
69 extern QString clipboardDir;
70 extern QString clipboardFile;
71 extern bool clipboardEmpty;
72
73 extern ImageIO imageIO;
74
75 extern TaskModel* taskModel;
76
77 extern QString vymName;
78 extern QString vymVersion;
79 extern QDir vymBaseDir;
80
81 extern QDir lastImageDir;
82 extern QDir lastMapDir;
83 extern QDir lastExportDir;
84
85 extern bool bugzillaClientAvailable;
86
87 extern Settings settings;
88
89 uint VymModel::idLast=0;    // make instance
90
91 VymModel::VymModel()
92 {
93     //qDebug()<< "Const VymModel";
94     init();
95     rootItem->setModel (this);
96 }
97
98 VymModel::~VymModel() 
99 {
100     //out << "Destr VymModel begin this="<<this<<"  "<<mapName<<flush;
101     mapEditor=NULL;
102     blockReposition=true;
103     autosaveTimer->stop();
104     fileChangedTimer->stop();
105     stopAllAnimation();
106
107     //qApp->processEvents();    // Update view (scene()->update() is not enough)
108     //qDebug() << "Destr VymModel end   this="<<this;
109 }   
110
111 void VymModel::clear() 
112 {
113     while (rootItem->childCount() >0)
114     {
115         //qDebug()<<"VM::clear  ri="<<rootItem<<"  ri->count()="<<rootItem->childCount();
116         deleteItem (rootItem->getChildNum(0) );
117     }
118 }
119
120 void VymModel::init () 
121 {
122     // No MapEditor yet
123     mapEditor       = NULL;
124
125     // Use default author
126     author = settings.value("/user/name", tr("unknown user","default name for map author in settings")).toString();
127
128     // States and IDs
129     idLast++;
130     modelID         = idLast;
131     mapChanged      = false;
132     mapDefault      = true;
133     mapUnsaved      = false;
134
135     // Selection history
136     selModel        = NULL;
137     selectionBlocked= false;
138     resetSelectionHistory();
139
140     resetHistory();
141
142     // Create tmp dirs
143     makeTmpDirectories();
144     
145     // Files
146     readonly        = false;
147     zipped          = true;
148     filePath        = "";
149     fileName        = tr("unnamed");
150     mapName         = fileName;
151     blockReposition = false;
152     blockSaveState  = false;
153
154     autosaveTimer   = new QTimer (this);
155     connect(autosaveTimer, SIGNAL(timeout()), this, SLOT(autosave()));
156
157     fileChangedTimer= new QTimer (this);        
158     fileChangedTimer->start(3000);
159     connect(fileChangedTimer, SIGNAL(timeout()), this, SLOT(fileChanged()));
160
161     // find routine
162     findReset();
163
164     // animations   // FIXME-4 switch to new animation system 
165     animationUse    = settings.value ("/animation/use",false).toBool();    // FIXME-4 add options to control _what_ is animated
166     animationTicks  = settings.value("/animation/ticks",20).toInt();
167     animationInterval=settings.value("/animation/interval",5).toInt();
168     animObjList.clear();    
169     animationTimer  = new QTimer (this);
170     connect(animationTimer, SIGNAL(timeout()), this, SLOT(animate()));
171
172     // View - map
173     defaultFont.setPointSizeF (16);
174     defLinkColor    = QColor (0,0,255);
175     linkcolorhint   = LinkableMapObj::DefaultColor;
176     linkstyle       = LinkableMapObj::PolyParabel;
177     defXLinkPen.setWidth (1);
178     defXLinkPen.setColor ( QColor (50,50,255) );
179     defXLinkPen.setStyle ( Qt::DashLine );
180     defXLinkStyleBegin = "HeadFull";
181     defXLinkStyleEnd   = "HeadFull";
182
183     hasContextPos   = false;
184
185     hidemode        = TreeItem::HideNone;
186
187     // Animation in MapEditor
188     zoomFactor      = 1;
189     rotationAngle   = 0;
190     animDuration    = 2000;
191     animCurve       = QEasingCurve::OutQuint;
192
193     // Initialize presentation slides
194     slideModel      = new SlideModel (this);
195     blockSlideSelection=false;
196
197     // Avoid recursions later
198     cleaningUpLinks = false;
199
200     // Network
201     netstate        = Offline;
202
203 #if defined(VYM_DBUS)
204      // Announce myself on DBUS
205     new AdaptorModel(this);    // Created and not deleted as documented in Qt
206     if (!QDBusConnection::sessionBus().registerObject (QString("/vymmodel_%1").arg(modelID),this))
207         qWarning ("VymModel: Couldn't register DBUS object!");
208 #endif
209 }
210
211 void VymModel::makeTmpDirectories()
212 {
213     // Create unique temporary directories
214     tmpMapDir = tmpVymDir+QString("/model-%1").arg(modelID);
215     histPath = tmpMapDir+"/history";
216     QDir d;
217     d.mkdir (tmpMapDir);
218 }
219
220 MapEditor* VymModel::getMapEditor() 
221 {
222     return mapEditor;
223 }
224
225 bool VymModel::isRepositionBlocked()
226 {
227     return blockReposition;
228 }
229
230 void VymModel::updateActions()  
231 {
232     // Tell mainwindow to update states of actions
233     mainWindow->updateActions();
234 }
235
236
237
238 QString VymModel::saveToDir(const QString &tmpdir, const QString &prefix, bool writeflags, const QPointF &offset, TreeItem *saveSel)
239 {
240     // tmpdir       temporary directory to which data will be written
241     // prefix       mapname, which will be appended to images etc.
242     // 
243     // writeflags   Only write flags for "real" save of map, not undo
244     // offset       offset of bbox of whole map in scene. 
245     //              Needed for XML export
246
247     XMLObj xml;
248
249     // Save Header
250     QString ls;
251     switch (linkstyle)
252     {
253         case LinkableMapObj::Line: 
254             ls="StyleLine";
255             break;
256         case LinkableMapObj::Parabel:
257             ls="StyleParabel";
258             break;
259         case LinkableMapObj::PolyLine:  
260             ls="StylePolyLine";
261             break;
262         default:
263             ls="StylePolyParabel";
264             break;
265     }   
266
267     QString s="<?xml version=\"1.0\" encoding=\"utf-8\"?><!DOCTYPE vymmap>\n";
268     QString colhint="";
269     if (linkcolorhint==LinkableMapObj::HeadingColor) 
270         colhint=xml.attribut("linkColorHint","HeadingColor");
271
272     QString mapAttr=xml.attribut("version",vymVersion);
273     if (!saveSel)
274         mapAttr+= xml.attribut("author",author) +
275                   xml.attribut("title",title) +
276                   xml.attribut("comment",comment) +
277                   xml.attribut("date",getDate()) +
278                   xml.attribut("branchCount", QString().number(branchCount())) +
279                   xml.attribut("backgroundColor", mapEditor->getScene()->backgroundBrush().color().name() ) +
280                   xml.attribut("defaultFont", defaultFont.toString() ) +
281                   xml.attribut("selectionColor", mapEditor->getSelectionColor().name() ) +
282                   xml.attribut("linkStyle", ls ) +
283                   xml.attribut("linkColor", defLinkColor.name() ) +
284                   xml.attribut("defXLinkColor", defXLinkPen.color().name() ) +
285                   xml.attribut("defXLinkWidth", QString().setNum(defXLinkPen.width(),10) ) +
286                   xml.attribut("defXLinkPenStyle", penStyleToString (defXLinkPen.style() )) +
287                   xml.attribut("defXLinkStyleBegin", defXLinkStyleBegin) +
288                   xml.attribut("defXLinkStyleEnd", defXLinkStyleEnd) +
289                   xml.attribut("mapZoomFactor", QString().setNum(mapEditor->getZoomFactorTarget()) ) +
290                   xml.attribut("mapRotationAngle", QString().setNum(mapEditor->getAngleTarget()) ) +
291                   colhint; 
292     s+=xml.beginElement("vymmap",mapAttr); 
293     xml.incIndent();
294
295     // Find the used flags while traversing the tree    
296     standardFlagsMaster->resetUsedCounter();
297     
298
299     // Temporary list of links
300     QList <Link*> tmpLinks;
301
302     // Build xml recursivly
303     if (!saveSel)
304     {
305         // Save all mapcenters as complete map, if saveSel not set
306         s+=saveTreeToDir(tmpdir,prefix,offset,tmpLinks);
307
308         // Save local settings
309         s+=settings.getDataXML (destPath);
310
311         // Save selection
312         if (getSelectedItem() && !saveSel ) 
313             s+=xml.valueElement("select",getSelectString());
314
315     } else
316     {
317         switch (saveSel->getType())
318         {
319             case TreeItem::Branch:
320                 // Save Subtree
321                 s+=((BranchItem*)saveSel)->saveToDir(tmpdir,prefix,offset,tmpLinks);
322                 break;
323             case TreeItem::MapCenter:
324                 // Save Subtree
325                 s+=((BranchItem*)saveSel)->saveToDir(tmpdir,prefix,offset,tmpLinks);
326                 break;
327             case TreeItem::Image:
328                 // Save Image
329                 s+=((ImageItem*)saveSel)->saveToDir(tmpdir,prefix);
330                 break;
331             default: 
332                 // other types shouldn't be safed directly...
333                 break;
334         }
335     }
336
337     // Save XLinks
338     for (int i=0; i<tmpLinks.count();++i)
339         s+=tmpLinks.at(i)->saveToDir();
340
341     // Save slides  
342     s+=slideModel->saveToDir(); 
343
344     xml.decIndent();
345     s+=xml.endElement("vymmap");
346
347     if (writeflags) standardFlagsMaster->saveToDir (tmpdir+"/flags/","",writeflags);
348     return s;
349 }
350
351 QString VymModel::saveTreeToDir (const QString &tmpdir,const QString &prefix, const QPointF &offset, QList <Link*> &tmpLinks)
352 {
353     QString s;
354     for (int i=0; i<rootItem->branchCount(); i++)
355         s+=rootItem->getBranchNum(i)->saveToDir (tmpdir,prefix,offset,tmpLinks);
356     return s;
357 }
358
359 void VymModel::setFilePath(QString fpath, QString destname)
360 {
361     if (fpath.isEmpty() || fpath=="")
362     {
363         filePath="";
364         fileName="";
365         destPath="";
366     } else
367     {
368         filePath=fpath;     // becomes absolute path
369         fileName=fpath;     // gets stripped of path
370         destPath=destname;  // needed for vymlinks and during load to reset fileChangedTime
371
372         // If fpath is not an absolute path, complete it
373         filePath=QDir(fpath).absolutePath();
374         fileDir=filePath.left (1+filePath.lastIndexOf ("/"));
375
376         // Set short name, too. Search from behind:
377         fileName=basename(fileName);
378
379         // Forget the .vym (or .xml) for name of map
380         mapName=fileName.left(fileName.lastIndexOf(".",-1,Qt::CaseSensitive) );
381     }
382 }
383
384 void VymModel::setFilePath(QString fpath)
385 {
386     setFilePath (fpath,fpath);
387 }
388
389 QString VymModel::getFileDir()
390 {
391     return fileDir;
392 }
393
394 QString VymModel::getFilePath()
395 {
396     return filePath;
397 }
398
399 QString VymModel::getFileName()
400 {
401     return fileName;
402 }
403
404 QString VymModel::getMapName()
405 {
406     return mapName;
407 }
408
409 QString VymModel::getDestPath()
410 {
411     return destPath;
412 }
413
414 bool VymModel::parseVymText (const QString &s)
415 {
416     bool ok = false;
417     BranchItem *bi = getSelectedBranch();
418     if (bi)
419     {
420         parseBaseHandler *handler = new parseVYMHandler;
421
422         bool blockSaveStateOrg=blockSaveState;
423         blockReposition=true;
424         blockSaveState=true;
425         QXmlInputSource source;
426         source.setData( s );
427         QXmlSimpleReader reader;
428         reader.setContentHandler( handler );
429         reader.setErrorHandler( handler );
430
431         handler->setInputString (s);
432         handler->setModel ( this );
433         handler->setLoadMode (ImportReplace, 0);
434
435         ok = reader.parse( source );
436         blockReposition=false;
437         blockSaveState=blockSaveStateOrg;
438         if ( ok )
439         {
440             emitNoteChanged( bi );
441             emitDataChanged( bi );
442             reposition();   // to generate bbox sizes
443         } else
444         {
445             QMessageBox::critical( 0, tr( "Critical Parse Error" ),
446                                    tr( handler->errorProtocol().toUtf8() ) );
447             // returnCode=1;
448             // Still return "success": the map maybe at least
449             // partially read by the parser
450         }
451     }
452     return ok;
453 }
454
455 File::ErrorCode VymModel::loadMap (
456     QString fname,
457     const LoadMode &lmode, 
458     const FileType &ftype,
459     const int &contentFilter,
460     int pos)
461 {
462     File::ErrorCode err = File::Success;
463
464     // Get updated zoomFactor, before applying one read from file in the end
465     if (mapEditor) 
466     {
467         zoomFactor = mapEditor->getZoomFactorTarget();  
468         rotationAngle = mapEditor->getAngleTarget();
469     }
470
471     parseBaseHandler *handler;
472     fileType=ftype;
473     switch (fileType)
474     {
475         case VymMap: 
476             handler = new parseVYMHandler; 
477             ((parseVYMHandler*)handler)->setContentFilter (contentFilter);
478             break;
479         case FreemindMap : handler = new parseFreemindHandler; break;
480         default: 
481             QMessageBox::critical( 0, tr( "Critical Parse Error" ),
482                    "Unknown FileType in VymModel::load()");
483         return File::Aborted;   
484     }
485
486     bool zipped_org = zipped;
487
488     if (lmode == NewMap)
489     {
490         // Reset timestamp to check for later updates of file
491         fileChangedTime = QFileInfo (destPath).lastModified();
492
493         selModel->clearSelection();
494     } 
495
496     // Create temporary directory for packing
497     bool ok;
498     QString tmpZipDir = makeTmpDir (ok,"vym-pack");
499     if (!ok)
500     {
501         QMessageBox::critical( 0, tr( "Critical Load Error" ),
502            tr("Couldn't create temporary directory before load\n"));
503         return File::Aborted; 
504     }
505
506     if (fname.right(4) == ".xml" || fname.right(3) == ".mm")
507         err = File::NoZip;
508     else
509     {
510         // Try to unzip file
511         err = unzipDir (tmpZipDir,fname);
512     }
513     QString xmlfile;
514     if (err == File::NoZip)
515     {
516         xmlfile = fname;
517         zipped = false;
518     } else
519     {
520         zipped = true;
521         
522         // Look for mapname.xml
523         xmlfile = fname.left(fname.lastIndexOf(".", -1, Qt::CaseSensitive));
524         xmlfile = xmlfile.section( '/', -1 );
525         QFile mfile( tmpZipDir + "/" + xmlfile + ".xml");
526         if (!mfile.exists() )
527         {
528             // mapname.xml does not exist, well, 
529             // maybe someone renamed the mapname.vym file...
530             // Try to find any .xml in the toplevel 
531             // directory of the .vym file
532             QStringList filters;
533             filters<<"*.xml";
534             QStringList flist = QDir (tmpZipDir).entryList(filters);
535             if (flist.count() == 1) 
536             {
537                 // Only one entry, take this one
538                 xmlfile = tmpZipDir + "/"+flist.first();
539         } else
540         {
541             for ( QStringList::Iterator it = flist.begin(); it != flist.end(); ++it )
542                 *it=tmpZipDir + "/" + *it;
543             // FIXME-4 Multiple entries, load all (but only the first one into this ME)
544             //mainWindow->fileLoadFromTmp (flist);
545             //returnCode=1;     // Silently forget this attempt to load
546             qWarning ("MainWindow::load (fn)  multimap found...");
547         }
548
549         if (flist.isEmpty() )
550         {
551             QMessageBox::critical( 0, tr( "Critical Load Error" ),
552                                    tr("Couldn't find a map (*.xml) in .vym archive.\n"));
553             err=File::Aborted;
554         }
555         } //file doesn't exist  
556         else
557             xmlfile=mfile.fileName();
558     }
559
560     QFile file( xmlfile);
561
562     // I am paranoid: file should exist anyway
563     // according to check in mainwindow.
564     if (!file.exists() )
565     {
566         QMessageBox::critical( 0, tr( "Critical Parse Error" ),
567                    tr(QString("Couldn't open map %1").arg(file.fileName()).toUtf8()));
568         err=File::Aborted;      
569     } else
570     {
571         bool blockSaveStateOrg = blockSaveState;
572         blockReposition = true;
573         blockSaveState  = true;
574         mapEditor->setViewportUpdateMode (QGraphicsView::NoViewportUpdate);
575         QXmlInputSource source( &file);
576         QXmlSimpleReader reader;
577         reader.setContentHandler( handler );
578         reader.setErrorHandler( handler );
579         handler->setModel ( this);
580
581         // We need to set the tmpDir in order  to load files with rel. path
582         QString tmpdir;
583         if (zipped)
584             tmpdir = tmpZipDir;
585         else
586             tmpdir = fname.left(fname.lastIndexOf("/", -1));    
587         handler->setTmpDir (tmpdir);
588         handler->setInputFile (file.fileName());
589         if (lmode == ImportReplace)
590             handler->setLoadMode (ImportReplace, pos);
591         else    
592             handler->setLoadMode (lmode, pos);
593
594         bool ok = reader.parse( source );
595         blockReposition = false;
596         blockSaveState  = blockSaveStateOrg;
597         mapEditor->setViewportUpdateMode (QGraphicsView::MinimalViewportUpdate);
598         file.close();
599         if ( ok ) 
600         {
601             reposition();   // to generate bbox sizes
602             emitSelectionChanged();
603
604             if (lmode == NewMap)
605             {
606                 mapDefault = false;
607                 mapChanged = false;
608                 mapUnsaved = false;
609                 autosaveTimer->stop();
610
611                 resetHistory();
612                 resetSelectionHistory();
613
614                 if (! tryVymLock() && debug ) 
615                     qWarning() << "VM::loadMap  no lockfile created!";
616             }
617
618             // Recalc priorities and sort   
619             taskModel->recalcPriorities();
620         } else 
621         {
622             QMessageBox::critical( 0, tr( "Critical Parse Error" ),
623                        tr( handler->errorProtocol().toUtf8() ) );
624             // returnCode=1;    
625             // Still return "success": the map maybe at least
626             // partially read by the parser
627         }   
628     }   
629
630     // Delete tmpZipDir
631     removeDir (QDir(tmpZipDir));
632
633     // Restore original zip state
634     zipped = zipped_org;
635
636     updateActions();
637     
638     if (lmode!=NewMap) emitUpdateQueries();
639
640     if (mapEditor) 
641     {
642         mapEditor->setZoomFactorTarget (zoomFactor);
643         mapEditor->setAngleTarget (rotationAngle);
644     }
645
646     if (vymView) vymView->readSettings();  
647
648     qApp->processEvents();  // Update view (scene()->update() is not enough)
649     return err;
650 }
651
652 File::ErrorCode VymModel::save (const SaveMode &savemode)
653 {
654     QString tmpZipDir;
655     QString mapFileName;
656     QString safeFilePath;
657
658     File::ErrorCode err=File::Success;
659
660     if (zipped)
661         // save as .xml
662         mapFileName=mapName+".xml";
663     else
664         // use name given by user, even if he chooses .doc
665         mapFileName=fileName;
666
667     // Look, if we should zip the data:
668     if (!zipped)
669     {
670         QMessageBox mb( vymName,
671             tr("The map %1\ndid not use the compressed "
672             "vym file format.\nWriting it uncompressed will also write images \n"
673             "and flags and thus may overwrite files in the "
674             "given directory\n\nDo you want to write the map").arg(filePath),
675             QMessageBox::Warning,
676             QMessageBox::Yes | QMessageBox::Default,
677             QMessageBox::No ,
678             QMessageBox::Cancel | QMessageBox::Escape);
679         mb.setButtonText( QMessageBox::Yes, tr("compressed (vym default)") );
680         mb.setButtonText( QMessageBox::No, tr("uncompressed") );
681         mb.setButtonText( QMessageBox::Cancel, tr("Cancel"));
682         switch( mb.exec() ) 
683         {
684             case QMessageBox::Yes:
685                 // save compressed (default file format)
686                 zipped=true;
687                 break;
688             case QMessageBox::No:
689                 // save uncompressed
690                 zipped=false;
691                 break;
692             case QMessageBox::Cancel:
693                 // do nothing
694                 return File::Aborted;
695                 break;
696         }
697     }
698
699     // First backup existing file, we 
700     // don't want to add to old zip archives
701     QFile f(destPath);
702     if (f.exists())
703     {
704         if ( settings.value ("/mapeditor/writeBackupFile").toBool())
705         {
706             QString backupFileName(destPath + "~");
707             QFile backupFile(backupFileName);
708             if (backupFile.exists() && !backupFile.remove())
709             {
710                 QMessageBox::warning(0, tr("Save Error"),
711                                      tr("%1\ncould not be removed before saving").arg(backupFileName));
712             }
713             else if (!f.rename(backupFileName))
714             {
715                 QMessageBox::warning(0, tr("Save Error"),
716                                      tr("%1\ncould not be renamed before saving").arg(destPath));
717             }
718         }
719     }
720
721     if (zipped)
722     {
723         // Create temporary directory for packing
724         bool ok;
725         tmpZipDir=makeTmpDir (ok,"vym-zip");
726         if (!ok)
727         {
728             QMessageBox::critical( 0, tr( "Critical Save Error" ),
729                tr("Couldn't create temporary directory before save\n"));
730             return File::Aborted; 
731         }
732
733         safeFilePath=filePath;
734         setFilePath (tmpZipDir+"/"+ mapName+ ".xml", safeFilePath);
735     } // zipped
736
737     // Create mapName and fileDir
738     makeSubDirs (fileDir);
739
740     QString saveFile;
741     if (savemode==CompleteMap || selModel->selection().isEmpty())
742     {
743         // Save complete map
744         if (zipped)
745             // Use defined name for map within zipfile to avoid problems
746             //with zip library and umlauts (see #98)
747             saveFile=saveToDir (fileDir, "", true, QPointF(), NULL);
748         else
749             saveFile=saveToDir (fileDir, mapName + "-", true, QPointF(), NULL);
750     mapChanged=false;
751         mapUnsaved=false;
752         autosaveTimer->stop();
753     }
754     else    
755     {
756         // Save part of map
757     if (selectionType() == TreeItem::Image)
758             saveImage();
759         else    
760         saveFile = saveToDir (fileDir, mapName + "-", true, QPointF(), getSelectedBranch());
761         // TODO take care of multiselections
762     }   
763
764     bool saved;
765     if (zipped)
766         // Use defined map name "map.xml", if zipped. Introduce in 2.6.6
767         saved = saveStringToDisk(fileDir + "map.xml", saveFile);
768     else
769         // Use regular mapName, when saved as XML
770         saved = saveStringToDisk(fileDir + mapFileName, saveFile);
771     if (!saved)
772     {
773         err=File::Aborted;
774         qWarning ("ME::saveStringToDisk failed!");
775     }
776
777     if (zipped)
778     {
779         // zip
780         if (err==File::Success) err=zipDir (tmpZipDir,destPath);
781
782         // Delete tmpDir
783         removeDir (QDir(tmpZipDir));
784
785         // Restore original filepath outside of tmp zip dir
786         setFilePath (safeFilePath);
787     }
788
789     updateActions();
790     fileChangedTime=QFileInfo (destPath).lastModified();
791     return err;
792 }
793
794 void VymModel::loadImage (BranchItem *dst,const QString &fn)
795 {
796     if (!dst) dst=getSelectedBranch();
797     if (dst)
798     {
799         QString filter=QString (tr("Images") + " (*.png *.bmp *.xbm *.jpg *.png *.xpm *.gif *.pnm *.svg);;"+tr("All","Filedialog") +" (*.*)");
800         QStringList fns;
801         if (fn.isEmpty() )
802             fns=QFileDialog::getOpenFileNames(
803                         NULL,
804                         vymName+" - " + tr("Load image"),
805                         lastImageDir.path(),
806                         filter);
807         else
808             fns.append (fn);
809
810         if (!fns.isEmpty() )
811         {
812             lastImageDir.setPath(fns.first().left(fns.first().lastIndexOf ("/")) );
813             QString s;
814             for (int j=0; j<fns.count(); j++)
815             {
816                 s=fns.at(j);
817                 ImageItem *ii=createImage(dst);
818                 if (ii && ii->load (s) )
819                 {
820                     saveState(
821                                 (TreeItem*)ii,
822                                 "delete ()",
823                                 dst,
824                                 QString ("loadImage (\"%1\")").arg(s ),
825                                 QString("Add image %1 to %2").arg(s).arg(getObjectName(dst))
826                                 );
827                     // Find nice position
828                     FloatImageObj *fio=(FloatImageObj*)(ii->getMO() );
829                     if (fio)
830                         fio->move2RelPos (0,0);
831
832                     // On default include image // FIXME-4 check, if we change default settings...
833                     select(dst);
834                     setIncludeImagesHor (false);
835                     setIncludeImagesVer (true);
836
837                     reposition();
838                 } else
839                     // FIXME-4 loadFIO error handling
840                     qWarning ()<<"Failed to load "+s;
841             }
842
843         }
844     }
845 }
846
847 void VymModel::saveImage (ImageItem *ii, QString format, QString fn)                                                    
848 {
849     if (!ii) ii=getSelectedImage();
850     if (ii)
851     {
852         QString filter=QString (tr("Images") + " (*.png *.bmp *.xbm *.jpg *.png *.xpm *.gif *.pnm *.svg);;"+tr("All","Filedialog") +" (*.*)");
853         if (fn.isEmpty() )
854             fn=QFileDialog::getSaveFileName(
855                         NULL,
856                         vymName+" - " + tr("Save image"),
857                         lastImageDir.path(),
858                         filter,
859                         NULL,
860                         QFileDialog::DontConfirmOverwrite);
861
862         if (!fn.isEmpty() )
863         {
864             lastImageDir.setPath(fn.left(fn.lastIndexOf ("/")) );
865             if (QFile (fn).exists() )
866             {
867                 QMessageBox mb( vymName,
868                                 tr("The file %1 exists already.\n"
869                                    "Do you want to overwrite it?").arg(fn),
870                                 QMessageBox::Warning,
871                                 QMessageBox::Yes | QMessageBox::Default,
872                                 QMessageBox::Cancel | QMessageBox::Escape,
873                                 QMessageBox::NoButton );
874
875                 mb.setButtonText( QMessageBox::Yes, tr("Overwrite") );
876                 mb.setButtonText( QMessageBox::No, tr("Cancel"));
877                 switch( mb.exec() )
878                 {
879                 case QMessageBox::Yes:
880                     // save
881                     break;
882                 case QMessageBox::Cancel:
883                     // do nothing
884                     return;
885                     break;
886                 }
887             }
888             if (format.isEmpty() ) format=imageIO.guessType(fn);
889             if (format.isEmpty())
890                 QMessageBox::critical (0,tr("Critical Error"),tr("Unsupported format in %1").arg(fn));
891             else if (!ii->save (fn, format) )
892                 QMessageBox::critical (0,tr("Critical Error"),tr("Couldn't save %1").arg(fn));
893         }
894     }
895 }
896
897
898 void VymModel::importDirInt(BranchItem *dst, QDir d) 
899 {
900     bool oldSaveState = blockSaveState;
901     blockSaveState = true;
902     BranchItem *bi = dst;
903     if (bi)
904     {
905         int beginDepth = bi->depth();
906
907         d.setFilter(QDir::AllEntries | QDir::Hidden);
908         QFileInfoList list = d.entryInfoList();
909         QFileInfo fi;
910
911         // Traverse directories
912         for (int i = 0; i < list.size(); ++i) 
913         {
914             fi = list.at(i);
915             if (fi.isDir() && fi.fileName() != "." && fi.fileName() != ".." )
916             {
917                 bi = addNewBranchInt(dst, -2);
918                 bi->setHeadingPlainText (fi.fileName() );
919                 bi->setHeadingColor (QColor("blue"));
920                 if ( !d.cd(fi.fileName()) ) 
921                     QMessageBox::critical (0,tr("Critical Import Error"),tr("Cannot find the directory %1").arg(fi.fileName()));
922                 else 
923                 {
924                     // Recursively add subdirs
925                     qDebug() << "Add subdir " << bi->getHeadingPlain();
926                     importDirInt (bi, d);
927                     d.cdUp();
928                 }
929                 emitDataChanged(bi);
930             }   
931         }       
932
933         for (int i = 0; i < list.size(); ++i) 
934         {
935             fi = list.at(i);
936             if (fi.isFile())
937             {
938                 bi = addNewBranchInt (dst,-2);
939                 bi->setHeadingPlainText (fi.fileName() );
940                 bi->setHeadingColor (QColor("black"));
941                 if (fi.fileName().right(4) == ".vym" )
942                     bi->setVymLink (fi.filePath());
943                 emitDataChanged(bi);
944             }
945         }   
946
947         // Scroll at least some stuff
948         if (dst->branchCount()>1 && dst->depth()-beginDepth>2)
949             dst->toggleScroll();
950     }       
951     blockSaveState=oldSaveState;
952 }
953
954 void VymModel::importDirInt (const QString &s)  
955 {
956     BranchItem *selbi = getSelectedBranch();
957     if (selbi)
958     {
959         saveStateChangingPart (selbi, selbi, QString ("importDir (\"%1\")").arg(s),QString("Import directory structure from %1").arg(s));
960
961         QDir d(s);
962         importDirInt (selbi, d);
963     }
964 }   
965
966 void VymModel::importDir()  
967 {
968     BranchItem *selbi = getSelectedBranch();
969     if (selbi)
970     {
971         QStringList filters;
972         filters <<"VYM map (*.vym)";
973         QFileDialog fd;
974         fd.setWindowTitle (vymName + " - " +tr("Choose directory structure to import"));
975         fd.setFileMode (QFileDialog::DirectoryOnly);
976         fd.setNameFilters (filters);
977         fd.setWindowTitle(vymName + " - " + tr("Choose directory structure to import"));
978         fd.setAcceptMode (QFileDialog::AcceptOpen);
979
980         QString fn;
981         if ( fd.exec() == QDialog::Accepted &&!fd.selectedFiles().isEmpty() )
982         {
983             importDirInt (fd.selectedFiles().first() );
984             reposition();
985         }
986     }   
987 }
988
989 bool VymModel::tryVymLock()
990 {
991     // Defaults for author and host in vymLock
992     QString defAuthor = settings.value("/user/name", tr( "unknown user", "Default for lockfiles of maps") ).toString();
993     QString defHost   = QHostInfo::localHostName();      
994     vymLock.setMapPath( filePath );
995     vymLock.setAuthor( settings.value( "/user/name", defAuthor ).toString() ); 
996     if ( getenv("HOST") != 0 ) 
997         vymLock.setHost( getenv("HOST") );
998     else
999         vymLock.setHost( defHost );
1000     
1001     // Now try to lock
1002     if (!vymLock.tryLock() )
1003     {
1004         if (debug) qDebug() << "VymModel::tryLock failed!";
1005         setReadOnly( true );
1006         if (vymLock.getState() == VymLock::lockedByOther)
1007         {
1008             LockedFileDialog dia;
1009             QString a = vymLock.getAuthor();
1010             QString h = vymLock.getHost();
1011             QString s = QString( tr("Map seems to be already opened in another vym instance!\n\n "
1012                    "Map is locked by \"%1\" on \"%2\"\n\n"
1013                    "Please only delete the lockfile, if you are sure nobody else is currently working on this map." )) .arg(a).arg(h);
1014             dia.setText( s );
1015             dia.setWindowTitle(tr("Warning: Map already opended","VymModel"));
1016             if (dia.execDialog() == LockedFileDialog::DeleteLockfile)
1017             {
1018                 if (vymLock.removeLock() )
1019                 {
1020                     mainWindow->statusMessage (tr("Removed lockfile for %1").arg(mapName));
1021                     setReadOnly(false);
1022                     return true;
1023                 } else
1024                     QMessageBox::warning(0,
1025                              tr("Warning"),
1026                              tr("Couldn't remove lockfile for %1").arg(mapName));
1027             }
1028         } else if (vymLock.getState() == VymLock::notWritable)
1029         {
1030             WarningDialog dia;
1031             QString a = vymLock.getAuthor();
1032             QString h = vymLock.getHost();
1033             QString s = QString( tr("Cannot create lockfile of map! "
1034                         "It will be opened in readonly mode.\n\n" ));
1035             dia.setText( s );
1036             dia.setWindowTitle(tr("Warning","VymModel"));
1037             dia.showCancelButton( false );
1038             //dia.setShowAgainName("/mainwindow/mapIsLocked");
1039             dia.exec();
1040         }
1041         return false;
1042     }
1043     return true;
1044 }
1045
1046 bool VymModel::renameMap( const QString &newPath)
1047 {
1048     QString oldPath = filePath;
1049     setFilePath ( newPath );
1050     if (vymLock.getState() == VymLock::lockedByMyself)
1051     {
1052         // vymModel owns the lockfile, try to rename it
1053         if (! vymLock.rename( fileName ))
1054         {
1055             qWarning ("Warning: VymModel::renameMap failed");
1056             setFilePath( oldPath );
1057             return false;
1058         } else
1059             return true;
1060     }
1061
1062     // try to create new lockfile for the lock states: lockedByOther and notWritable
1063     return tryVymLock();
1064 }
1065
1066 void VymModel::setReadOnly( bool b )
1067 {
1068     readonly = b;
1069     mainWindow->updateTabName( this );
1070 }
1071
1072 bool VymModel::isReadOnly()
1073 {
1074     return readonly;
1075 }
1076
1077 void VymModel::autosave()
1078 {
1079     if (filePath=="") 
1080     {
1081         if (debug)
1082             qDebug() << "VymModel::autosave rejected due to missing filePath\n";
1083     }
1084
1085     QDateTime now=QDateTime().currentDateTime();
1086
1087     // Disable autosave, while we have gone back in history
1088     int redosAvail=undoSet.readNumValue (QString("/history/redosAvail"));
1089     if (redosAvail>0) return;
1090
1091     // Also disable autosave for new map without filename
1092     if (filePath.isEmpty()) return;
1093
1094
1095     if (mapUnsaved 
1096         && mapChanged 
1097         && mainWindow->useAutosave() 
1098         && !testmode)
1099     {
1100         if (QFileInfo(filePath).lastModified()<=fileChangedTime) 
1101             mainWindow->fileSave (this);
1102         else
1103             if (debug)
1104                 qDebug() <<"  ME::autosave  rejected, file on disk is newer than last save.\n"; 
1105
1106     }   
1107 }
1108
1109 void VymModel::fileChanged()
1110 {
1111     // Check if file on disk has changed meanwhile
1112     if (!filePath.isEmpty())
1113     {
1114         if (readonly)
1115         {
1116             // unset readonly if lockfile is gone
1117             if (vymLock.tryLock() ) setReadOnly( false );
1118         } else
1119         {
1120             // FIXME-0 Check, if somebody else removed/replaced lockfile
1121             // Here a unique vym ID would be needed to be checked
1122             
1123             QDateTime tmod = QFileInfo (filePath).lastModified();
1124             if (tmod > fileChangedTime)
1125             {
1126                 // FIXME-4 VM switch to current mapeditor and finish lineedits...
1127                 QMessageBox mb( vymName,
1128                     tr("The file of the map  on disk has changed:\n\n"  
1129                        "   %1\n\nDo you want to reload that map with the new file?").arg(filePath),
1130                     QMessageBox::Question,
1131                     QMessageBox::Yes ,
1132                     QMessageBox::Cancel | QMessageBox::Default,
1133                     QMessageBox::NoButton );
1134
1135                 mb.setButtonText( QMessageBox::Yes, tr("Reload"));
1136                 mb.setButtonText( QMessageBox::No, tr("Ignore"));
1137                 switch( mb.exec() ) 
1138                 {
1139                     case QMessageBox::Yes:
1140                         // Reload map
1141                         mainWindow->initProgressCounter (1);
1142                         loadMap (filePath);  
1143                         mainWindow->removeProgressCounter ();
1144                         break;
1145                     case QMessageBox::Cancel:
1146                         fileChangedTime=tmod; // allow autosave to overwrite newer file!
1147                 }
1148             }
1149         }
1150     }   
1151 }
1152
1153 bool VymModel::isDefault()
1154 {
1155     return mapDefault;
1156 }
1157
1158 void VymModel::makeDefault()
1159 {
1160     mapChanged=false;
1161     mapDefault=true;
1162 }
1163
1164 bool VymModel::hasChanged()
1165 {
1166     return mapChanged;
1167 }
1168
1169 void VymModel::setChanged()
1170 {
1171     if (!mapChanged)
1172         autosaveTimer->start(settings.value("/system/autosave/ms/",300000).toInt());
1173     mapChanged=true;
1174     mapDefault=false;
1175     mapUnsaved=true;
1176     findReset();
1177     updateActions();
1178 }
1179
1180 QString VymModel::getObjectName (LinkableMapObj *lmo)
1181 {
1182     if (!lmo || !lmo->getTreeItem() ) return QString();
1183     return getObjectName (lmo->getTreeItem() );
1184 }
1185
1186
1187 QString VymModel::getObjectName (TreeItem *ti)
1188 {
1189     QString s;
1190     if (!ti) return QString("Error: NULL has no name!");
1191     s=ti->getHeadingPlain();
1192     if (s=="") s="unnamed";
1193
1194     return QString ("%1 (%2)").arg(ti->getTypeName()).arg(s);
1195 }
1196
1197 void VymModel::redo()
1198 {
1199     // Can we undo at all?
1200     if (redosAvail<1) return;
1201
1202     bool blockSaveStateOrg=blockSaveState;
1203     blockSaveState=true;
1204     
1205     redosAvail--;
1206
1207     if (undosAvail<stepsTotal) undosAvail++;
1208     curStep++;
1209     if (curStep>stepsTotal) curStep=1;
1210     QString undoCommand=  undoSet.value (QString("/history/step-%1/undoCommand").arg(curStep));
1211     QString undoSelection=undoSet.value (QString("/history/step-%1/undoSelection").arg(curStep));
1212     QString redoCommand=  undoSet.value (QString("/history/step-%1/redoCommand").arg(curStep));
1213     QString redoSelection=undoSet.value (QString("/history/step-%1/redoSelection").arg(curStep));
1214     QString comment=undoSet.value (QString("/history/step-%1/comment").arg(curStep));
1215     QString version=undoSet.value ("/history/version");
1216
1217     /* TODO Maybe check for version, if we save the history
1218     if (!checkVersion(version))
1219         QMessageBox::warning(0,tr("Warning"),
1220             tr("Version %1 of saved undo/redo data\ndoes not match current vym version %2.").arg(version).arg(vymVersion));
1221     */ 
1222
1223     // Find out current undo directory
1224     QString bakMapDir(QString(tmpMapDir+"/undo-%1").arg(curStep));
1225
1226     if (debug)
1227     {
1228         qDebug() << "VymModel::redo() begin\n";
1229         qDebug() << "    undosAvail="<<undosAvail;
1230         qDebug() << "    redosAvail="<<redosAvail;
1231         qDebug() << "       curStep="<<curStep;
1232         qDebug() << "    ---------------------------";
1233         qDebug() << "    comment="<<comment;
1234         qDebug() << "    undoCom="<<undoCommand;
1235         qDebug() << "    undoSel="<<undoSelection;
1236         qDebug() << "    redoCom="<<redoCommand;
1237         qDebug() << "    redoSel="<<redoSelection;
1238         qDebug() << "    ---------------------------";
1239     }
1240
1241     // select  object before redo
1242     if (!redoSelection.isEmpty())
1243         select (redoSelection);
1244
1245     bool noErr;
1246     QString errMsg;
1247     parseAtom (redoCommand,noErr,errMsg);
1248     if (!noErr) 
1249     {
1250         if (!options.isOn("batch") )
1251             QMessageBox::warning(0,tr("Warning"),tr("Redo failed:\n%1").arg(errMsg));
1252         qWarning()<< "VM::redo aborted:\n"<<errMsg;
1253     }
1254
1255     blockSaveState=blockSaveStateOrg;
1256
1257     undoSet.setValue ("/history/undosAvail",QString::number(undosAvail));
1258     undoSet.setValue ("/history/redosAvail",QString::number(redosAvail));
1259     undoSet.setValue ("/history/curStep",QString::number(curStep));
1260     undoSet.writeSettings(histPath);
1261
1262     mainWindow->updateHistory (undoSet);
1263     updateActions();
1264
1265     /* TODO remove testing
1266     qDebug() << "ME::redo() end\n";
1267     qDebug() << "    undosAvail="<<undosAvail;
1268     qDebug() << "    redosAvail="<<redosAvail;
1269     qDebug() << "       curStep="<<curStep;
1270     qDebug() << "    ---------------------------";
1271     */
1272 }
1273
1274 bool VymModel::isRedoAvailable()
1275 {
1276     if (undoSet.readNumValue("/history/redosAvail",0)>0)
1277         return true;
1278     return false;
1279 }
1280
1281 void VymModel::undo()   
1282 {
1283     // Can we undo at all?
1284     if (undosAvail<1) return;
1285
1286     mainWindow->statusMessage (tr("Autosave disabled during undo."));
1287
1288     bool blockSaveStateOrg=blockSaveState;
1289     blockSaveState=true;
1290     
1291     QString undoCommand=  undoSet.value (QString("/history/step-%1/undoCommand").arg(curStep));
1292     QString undoSelection=undoSet.value (QString("/history/step-%1/undoSelection").arg(curStep));
1293     QString redoCommand=  undoSet.value (QString("/history/step-%1/redoCommand").arg(curStep));
1294     QString redoSelection=undoSet.value (QString("/history/step-%1/redoSelection").arg(curStep));
1295     QString comment=undoSet.value (QString("/history/step-%1/comment").arg(curStep));
1296     QString version=undoSet.value ("/history/version");
1297
1298     /* TODO Maybe check for version, if we save the history
1299     if (!checkVersion(version))
1300         QMessageBox::warning(0,tr("Warning"),
1301             tr("Version %1 of saved undo/redo data\ndoes not match current vym version %2.").arg(version).arg(vymVersion));
1302     */
1303
1304     // Find out current undo directory
1305     QString bakMapDir(QString(tmpMapDir+"/undo-%1").arg(curStep));
1306
1307     // select  object before undo
1308     if (!undoSelection.isEmpty() && !select (undoSelection))
1309     {
1310         qWarning ("VymModel::undo()  Could not select object for undo");
1311         return;
1312     }
1313
1314     if (debug)
1315     {
1316         qDebug() << "VymModel::undo() begin\n";
1317         qDebug() << "    undosAvail="<<undosAvail;
1318         qDebug() << "    redosAvail="<<redosAvail;
1319         qDebug() << "       curStep="<<curStep;
1320         qDebug() << "    ---------------------------";
1321         qDebug() << "    comment="<<comment;
1322         qDebug() << "    undoCom="<<undoCommand;
1323         qDebug() << "    undoSel="<<undoSelection;
1324         qDebug() << "    redoCom="<<redoCommand;
1325         qDebug() << "    redoSel="<<redoSelection;
1326         qDebug() << "    ---------------------------";
1327     }   
1328
1329     // select  object before undo
1330     if (!undoSelection.isEmpty())
1331         select (undoSelection);
1332
1333     // bool noErr;
1334     QString errMsg;
1335     //parseAtom (undoCommand,noErr,errMsg);
1336     errMsg = QVariant(execute(undoCommand)).toString();
1337     // FIXME-3 add noErr to parameters of execute above or ignore (error message already within parseAtom)
1338     // 
1339     /*
1340     if (!noErr)
1341     {
1342         if (!options.isOn("batch") )
1343             QMessageBox::warning(0,tr("Warning"),tr("Undo failed:\n%1").arg(errMsg));
1344         qWarning()<< "VM::undo failed:\n"<<errMsg;
1345     }
1346     */
1347
1348     undosAvail--;
1349     curStep--; 
1350     if (curStep<1) curStep = stepsTotal;
1351
1352     redosAvail++;
1353
1354     blockSaveState = blockSaveStateOrg;
1355 /* testing only
1356     qDebug() << "VymModel::undo() end\n";
1357     qDebug() << "    undosAvail="<<undosAvail;
1358     qDebug() << "    redosAvail="<<redosAvail;
1359     qDebug() << "       curStep="<<curStep;
1360     qDebug() << "    ---------------------------";
1361 */
1362
1363     undoSet.setValue ("/history/undosAvail",QString::number(undosAvail));
1364     undoSet.setValue ("/history/redosAvail",QString::number(redosAvail));
1365     undoSet.setValue ("/history/curStep",QString::number(curStep));
1366     undoSet.writeSettings(histPath);
1367
1368     mainWindow->updateHistory (undoSet);
1369     updateActions();
1370     //emitSelectionChanged();
1371 }
1372
1373 bool VymModel::isUndoAvailable()
1374 {
1375     if (undoSet.readNumValue("/history/undosAvail",0)>0)
1376         return true;
1377     return false;
1378 }
1379
1380 void VymModel::gotoHistoryStep (int i)
1381 {
1382     // Restore variables
1383     int undosAvail=undoSet.readNumValue (QString("/history/undosAvail"));
1384     int redosAvail=undoSet.readNumValue (QString("/history/redosAvail"));
1385
1386     if (i<0) i=undosAvail+redosAvail;
1387
1388     // Clicking above current step makes us undo things
1389     if (i<undosAvail) 
1390     {   
1391         for (int j=0; j<undosAvail-i; j++) undo();
1392         return;
1393     }   
1394     // Clicking below current step makes us redo things
1395     if (i>undosAvail) 
1396         for (int j=undosAvail; j<i; j++) 
1397         {
1398             if (debug) qDebug() << "VymModel::gotoHistoryStep redo "<<j<<"/"<<undosAvail<<" i="<<i;
1399             redo();
1400         }
1401
1402     // And ignore clicking the current row ;-)  
1403 }
1404
1405
1406 QString VymModel::getHistoryPath()
1407 {
1408     QString histName(QString("history-%1").arg(curStep));
1409     return (tmpMapDir+"/"+histName);
1410 }
1411
1412 void VymModel::resetHistory()
1413 {
1414     curStep=0;
1415     redosAvail=0;
1416     undosAvail=0;
1417
1418     stepsTotal=settings.value("/history/stepsTotal",100).toInt();
1419     undoSet.setValue ("/history/stepsTotal",QString::number(stepsTotal));
1420     mainWindow->updateHistory (undoSet);
1421 }
1422
1423 void VymModel::saveState(
1424     const SaveMode &savemode, 
1425     const QString &undoSelection, 
1426     const QString &undoCom, 
1427     const QString &redoSelection, 
1428     const QString &redoCom, 
1429     const QString &comment, 
1430     TreeItem *saveSel, 
1431     QString dataXML)
1432 {
1433     sendData(redoCom);  //FIXME-4 testing
1434
1435     // Main saveState
1436
1437     if (blockSaveState) return;
1438
1439     if (debug) qDebug() << "VM::saveState() for  "<<mapName;
1440     
1441     // Find out current undo directory
1442     if (undosAvail<stepsTotal) undosAvail++;
1443     curStep++;
1444     if (curStep>stepsTotal) curStep=1;
1445     
1446     QString histDir=getHistoryPath();
1447     QString bakMapPath=histDir+"/map.xml";
1448
1449     // Create histDir if not available
1450     QDir d(histDir);
1451     if (!d.exists()) 
1452         makeSubDirs (histDir);
1453
1454     // Save depending on how much needs to be saved 
1455     QList <Link*> tmpLinks;
1456     if (saveSel)
1457         dataXML=saveToDir (histDir,mapName+"-",false, QPointF (),saveSel);
1458         
1459     QString undoCommand=undoCom;
1460     QString redoCommand=redoCom;
1461     if (savemode==PartOfMap )
1462     {
1463         undoCommand.replace ("PATH",bakMapPath);
1464         redoCommand.replace ("PATH",bakMapPath);
1465     }
1466
1467     if (!dataXML.isEmpty())
1468         // Write XML Data to disk
1469         saveStringToDisk (bakMapPath,dataXML);
1470
1471     // We would have to save all actions in a tree, to keep track of 
1472     // possible redos after a action. Possible, but we are too lazy: forget about redos.
1473     redosAvail=0;
1474
1475     // Write the current state to disk
1476     undoSet.setValue ("/history/undosAvail",QString::number(undosAvail));
1477     undoSet.setValue ("/history/redosAvail",QString::number(redosAvail));
1478     undoSet.setValue ("/history/curStep",QString::number(curStep));
1479     undoSet.setValue (QString("/history/step-%1/undoCommand").arg(curStep),undoCommand);
1480     undoSet.setValue (QString("/history/step-%1/undoSelection").arg(curStep),undoSelection);
1481     undoSet.setValue (QString("/history/step-%1/redoCommand").arg(curStep),redoCommand);
1482     undoSet.setValue (QString("/history/step-%1/redoSelection").arg(curStep),redoSelection);
1483     undoSet.setValue (QString("/history/step-%1/comment").arg(curStep),comment);
1484     undoSet.setValue (QString("/history/version"),vymVersion);
1485     undoSet.writeSettings(histPath);
1486
1487     if (debug)
1488     {
1489         //qDebug() << "          into="<< histPath;
1490         qDebug() << "    stepsTotal="<<stepsTotal<<
1491         ", undosAvail="<<undosAvail<<
1492         ", redosAvail="<<redosAvail<<
1493         ", curStep="<<curStep;
1494         qDebug() << "    ---------------------------";
1495         qDebug() << "    comment="<<comment;
1496         qDebug() << "    undoCom="<<undoCommand;
1497         qDebug() << "    undoSel="<<undoSelection;
1498         qDebug() << "    redoCom="<<redoCommand;
1499         qDebug() << "    redoSel="<<redoSelection;
1500         if (saveSel) qDebug() << "    saveSel="<<qPrintable (getSelectString(saveSel));
1501         qDebug() << "    ---------------------------";
1502     }
1503
1504     mainWindow->updateHistory (undoSet);
1505
1506     setChanged();
1507     updateActions();
1508 }
1509
1510
1511 void VymModel::saveStateChangingPart(TreeItem *undoSel, TreeItem* redoSel, const QString &rc, const QString &comment)
1512 {
1513     // save the selected part of the map, Undo will replace part of map 
1514     QString undoSelection="";
1515     if (undoSel)
1516         undoSelection=getSelectString(undoSel);
1517     else
1518         qWarning ("VymModel::saveStateChangingPart  no undoSel given!");
1519     QString redoSelection="";
1520     if (redoSel)
1521         redoSelection=getSelectString(undoSel);
1522     else
1523         qWarning ("VymModel::saveStateChangingPart  no redoSel given!");
1524         
1525
1526     saveState (PartOfMap,
1527         undoSelection, "addMapReplace (\"PATH\")",
1528         redoSelection, rc, 
1529         comment, 
1530         undoSel);
1531 }
1532
1533 void VymModel::saveStateRemovingPart(TreeItem* redoSel, const QString &comment)
1534 {
1535     if (!redoSel)
1536     {
1537         qWarning ("VymModel::saveStateRemovingPart  no redoSel given!");
1538         return;
1539     }
1540     QString undoSelection;
1541     QString redoSelection=getSelectString(redoSel);
1542     if (redoSel->isBranchLikeType() )
1543     {
1544         // save the selected branch of the map, Undo will insert part of map 
1545         if (redoSel->depth()>0)
1546             undoSelection=getSelectString (redoSel->parent());
1547         saveState (PartOfMap,
1548             undoSelection, QString("addMapInsert (\"PATH\",%1,%2)").arg(redoSel->num()).arg(SlideContent),
1549             redoSelection, "delete ()", 
1550             comment, 
1551             redoSel);
1552     }
1553 }
1554
1555 void VymModel::saveState(TreeItem *undoSel, const QString &uc, TreeItem *redoSel, const QString &rc, const QString &comment) 
1556 {
1557     // "Normal" savestate: save commands, selections and comment
1558     // so just save commands for undo and redo
1559     // and use current selection, if empty parameter passed
1560
1561     QString redoSelection="";
1562     if (redoSel) redoSelection=getSelectString(redoSel);
1563     QString undoSelection="";
1564     if (undoSel) undoSelection=getSelectString(undoSel);
1565
1566     saveState (UndoCommand,
1567         undoSelection, uc,
1568         redoSelection, rc, 
1569         comment, 
1570         NULL);
1571 }
1572
1573 void VymModel::saveState(const QString &undoSel, const QString &uc, const QString &redoSel, const QString &rc, const QString &comment) 
1574 {
1575     // "Normal" savestate: save commands, selections and comment
1576     // so just save commands for undo and redo
1577     // and use current selection
1578     saveState (UndoCommand,
1579         undoSel, uc,
1580         redoSel, rc, 
1581         comment, 
1582         NULL);
1583 }
1584
1585 void VymModel::saveState(const QString &uc, const QString &rc, const QString &comment) 
1586 {
1587     // "Normal" savestate applied to model (no selection needed): 
1588     // save commands  and comment
1589     saveState (UndoCommand,
1590         NULL, uc,
1591         NULL, rc, 
1592         comment, 
1593         NULL);
1594 }
1595
1596 void VymModel::saveStateMinimal(TreeItem *undoSel, const QString &uc, TreeItem *redoSel, const QString &rc, const QString &comment) 
1597 {   //  Save a change in string and merge
1598     //  minor sequential  changes  */
1599     QString redoSelection="";
1600     if (redoSel) redoSelection=getSelectString(redoSel);
1601     QString undoSelection="";
1602     if (undoSel) undoSelection=getSelectString(undoSel);
1603
1604     saveState (UndoCommand,
1605         undoSelection, uc,
1606         redoSelection, rc, 
1607         comment, 
1608         NULL);
1609 }
1610
1611 void VymModel::saveStateBeforeLoad (LoadMode lmode, const QString &fname)
1612
1613     BranchItem *selbi=getSelectedBranch();
1614     if (selbi) 
1615     {
1616         if (lmode==ImportAdd)
1617             saveStateChangingPart(
1618                 selbi,
1619                 selbi,
1620                 QString("addMapInsert (\"%1\")").arg(fname),
1621                 QString("Add map %1 to %2").arg(fname).arg(getObjectName(selbi)));
1622         if (lmode==ImportReplace)
1623         {
1624             BranchItem *pi=(BranchItem*)(selbi->parent());
1625             saveStateChangingPart(
1626                 pi,
1627                 pi,
1628                 QString("addMapReplace(%1)").arg(fname),
1629                 QString("Add map %1 to %2").arg(fname).arg(getObjectName(selbi)));
1630         }
1631     }
1632 }
1633
1634
1635
1636 QGraphicsScene* VymModel::getScene ()
1637 {
1638     return mapEditor->getScene();
1639 }
1640
1641 TreeItem* VymModel::findBySelectString(QString s)
1642 {
1643     if (s.isEmpty() ) return NULL;
1644
1645     // Old maps don't have multiple mapcenters and don't save full path
1646     if (s.left(2) !="mc") s="mc:0,"+s;
1647
1648     QStringList parts=s.split (",");
1649     QString typ;
1650     int n;
1651     TreeItem *ti=rootItem;
1652
1653     while (!parts.isEmpty() )
1654     {
1655         typ=parts.first().left(2);
1656         n=parts.first().right(parts.first().length() - 3).toInt();
1657         parts.removeFirst();
1658         if (typ=="mc" || typ=="bo")
1659             ti=ti->getBranchNum (n);
1660         else if (typ=="fi")
1661             ti=ti->getImageNum (n);
1662         else if (typ=="ai")
1663             ti=ti->getAttributeNum (n);
1664         else if (typ=="xl")
1665             ti=ti->getXLinkItemNum (n);
1666         if(!ti) return NULL;        
1667     }
1668     return  ti;
1669 }
1670
1671 TreeItem* VymModel::findID (const uint &id)  
1672 {
1673     BranchItem *cur=NULL;
1674     BranchItem *prev=NULL;
1675     nextBranch(cur,prev);
1676     while (cur) 
1677     {
1678         if (id==cur->getID() ) return cur;
1679         int j=0;
1680         while (j<cur->xlinkCount() )
1681         {
1682             XLinkItem *xli=cur->getXLinkItemNum (j);
1683             if (id==xli->getID() ) return xli;
1684             j++;
1685         }
1686         j=0;
1687         while (j<cur->imageCount() )
1688         {
1689             ImageItem *ii=cur->getImageNum (j);
1690             if (id==ii->getID() ) return ii;
1691             j++;
1692         }
1693         nextBranch(cur,prev);
1694     }
1695     return NULL;
1696 }
1697
1698 TreeItem* VymModel::findUuid (const QUuid &id)  
1699 {
1700     BranchItem *cur=NULL;
1701     BranchItem *prev=NULL;
1702     nextBranch(cur,prev);
1703     while (cur) 
1704     {
1705         if (id==cur->getUuid() ) return cur;
1706         int j=0;
1707         while (j<cur->xlinkCount() )
1708         {
1709             XLinkItem *xli=cur->getXLinkItemNum (j);
1710             if (id==xli->getUuid() ) return xli;
1711             j++;
1712         }
1713         j=0;
1714         while (j<cur->imageCount() )
1715         {
1716             ImageItem *ii=cur->getImageNum (j);
1717             if (id==ii->getUuid() ) return ii;
1718             j++;
1719         }
1720         nextBranch(cur,prev);
1721     }
1722     return NULL;
1723 }
1724
1725 //////////////////////////////////////////////
1726 // Interface 
1727 //////////////////////////////////////////////
1728 void VymModel::setVersion (const QString &s)
1729 {
1730     version=s;
1731 }
1732
1733 QString VymModel::getVersion()
1734 {
1735     return version;
1736 }
1737
1738 void VymModel::setTitle (const QString &s)
1739 {
1740     saveState (
1741         QString ("setMapTitle (\"%1\")").arg(title),
1742         QString ("setMapTitle (\"%1\")").arg(s),
1743         QString ("Set title of map to \"%1\"").arg(s)
1744     );
1745     title=s;
1746 }
1747
1748 QString VymModel::getTitle()
1749 {
1750     return title;
1751 }
1752
1753 void VymModel::setAuthor (const QString &s)
1754 {
1755     saveState (
1756         QString ("setMapAuthor (\"%1\")").arg(author),
1757         QString ("setMapAuthor (\"%1\")").arg(s),
1758         QString ("Set author of map to \"%1\"").arg(s)
1759     );
1760     author=s;
1761 }
1762
1763 QString VymModel::getAuthor()
1764 {
1765     return author;
1766 }
1767
1768 void VymModel::setComment (const QString &s)
1769 {
1770     saveState (
1771         QString ("setMapComment (\"%1\")").arg(comment),
1772         QString ("setMapComment (\"%1\")").arg(s),
1773         QString ("Set comment of map")
1774     );
1775     comment=s;
1776 }
1777
1778 QString VymModel::getComment ()
1779 {
1780     return comment;
1781 }
1782
1783 QString VymModel::getDate ()
1784 {
1785     return QDate::currentDate().toString ("yyyy-MM-dd");
1786 }
1787
1788 int VymModel::branchCount() 
1789 {
1790     int c=0;
1791     BranchItem *cur=NULL;
1792     BranchItem *prev=NULL;
1793     nextBranch(cur,prev);
1794     while (cur) 
1795     {
1796         c++;
1797         nextBranch(cur,prev);
1798     }
1799     return c;
1800 }
1801
1802 void VymModel::setSortFilter (const QString &s)
1803 {
1804     sortFilter=s;
1805     emit (sortFilterChanged (sortFilter));
1806 }
1807
1808 QString VymModel::getSortFilter ()
1809 {
1810     return sortFilter;
1811 }
1812
1813 void VymModel::setHeading(const VymText &vt, BranchItem *bi)
1814 {
1815     Heading h_old;
1816     Heading h_new;
1817     h_new = vt;
1818     QString s = vt.getTextASCII();
1819     if (!bi) bi=getSelectedBranch();
1820     if (bi)
1821     {
1822         h_old = bi->getHeading();
1823         if (h_old == h_new) return;
1824         saveState(
1825             bi, "parseVymText ('" +  h_old.saveToDir() + "')",
1826             bi, "parseVymText ('" +  h_new.saveToDir() + "')",
1827             QString("Set heading of %1 to \"%2\"").arg(getObjectName(bi)).arg(s) );
1828         bi->setHeading(vt);
1829         emitDataChanged ( bi);
1830         emitUpdateQueries ();
1831         reposition();
1832     }
1833 }
1834
1835 void VymModel::setHeadingPlainText(const QString &s, BranchItem *bi)
1836 {
1837     if (!bi) bi=getSelectedBranch();
1838     if (bi)
1839     {
1840         VymText vt = bi->getHeading();
1841         vt.setPlainText(s);
1842         if (bi->getHeading() == vt) return;
1843         setHeading (vt, bi);
1844     }
1845 }
1846
1847 Heading VymModel::getHeading()
1848 {
1849     TreeItem *selti=getSelectedItem();
1850     if (selti) return  selti->getHeading();
1851     qWarning() << "VymModel::getHeading Nothing selected.";
1852     return Heading();
1853 }
1854
1855 void VymModel::setNote(const  VymNote &vn)
1856 {
1857     TreeItem *selti=getSelectedItem();
1858     if (selti) 
1859     {
1860         VymNote n_old;
1861         VymNote n_new;
1862         n_old = selti->getNote();
1863         n_new = vn;
1864         saveState(
1865             selti,
1866             "parseVymText ('" + n_old.saveToDir() + "')",
1867             selti,
1868             "parseVymText ('" + n_new.saveToDir() + "')",
1869             QString("Set note of %1 to \"%2\"").arg(getObjectName(selti)).arg(n_new.getTextASCII().left(20) ) );
1870         selti->setNote( n_new );
1871         emitNoteChanged( selti );
1872         emitDataChanged( selti );
1873     }
1874 }
1875
1876 VymNote VymModel::getNote()
1877 {
1878     TreeItem *selti=getSelectedItem();
1879     if (selti)
1880     {
1881         VymNote n = selti->getNote();
1882         return n;
1883     }
1884     qWarning() << "VymModel::getNote Nothing selected.";
1885     return VymNote();
1886 }
1887
1888 bool VymModel::hasRichTextNote()
1889 {
1890     TreeItem *selti=getSelectedItem();
1891     if (selti)
1892     {
1893         return selti->getNote().isRichText();
1894     }
1895     return false;
1896 }
1897
1898 void VymModel::loadNote (const QString &fn)
1899 {
1900     BranchItem *selbi=getSelectedBranch();
1901     if (selbi)
1902     {
1903         QString n;
1904         if (!loadStringFromDisk (fn,n))
1905             qWarning ()<<"VymModel::loadNote Couldn't load "<<fn;
1906         else
1907         {
1908             VymNote vn;
1909             vn.setAutoText(n);
1910             setNote (vn);
1911         }
1912     } else
1913         qWarning ("VymModel::loadNote no branch selected");
1914 }
1915
1916 void VymModel::saveNote (const QString &fn)
1917 {
1918     BranchItem *selbi = getSelectedBranch();
1919     if (selbi)
1920     {
1921         VymNote n = selbi->getNote();
1922         if ( n.isEmpty() )
1923             qWarning ()<<"VymModel::saveNote  note is empty, won't save to "<<fn;
1924         else
1925         {
1926             if (!saveStringToDisk (fn, n.saveToDir() ))
1927                 qWarning ()<<"VymModel::saveNote Couldn't save "<<fn;
1928         }
1929     } else
1930         qWarning ("VymModel::saveNote no branch selected");
1931 }
1932
1933 void VymModel::findDuplicateURLs()  // FIXME-3 needs GUI
1934 {
1935     // Generate map containing _all_ URLs and branches
1936     QString u;
1937     QMap <QString,BranchItem*> map;
1938     BranchItem *cur=NULL;
1939     BranchItem *prev=NULL;
1940     nextBranch(cur,prev);
1941     while (cur) 
1942     {
1943         u=cur->getURL();
1944         if (!u.isEmpty() )
1945             map.insertMulti (u,cur);
1946         nextBranch(cur,prev);
1947     }
1948
1949     // Extract duplicate URLs
1950     QMap <QString, BranchItem*>::const_iterator i=map.constBegin();
1951     QMap <QString, BranchItem*>::const_iterator firstdup=map.constEnd();    //invalid
1952     while (i != map.constEnd())
1953     {
1954         if (i!=map.constBegin() && i.key()==firstdup.key())
1955         {
1956             if (  i-1==firstdup )
1957             {
1958                 qDebug() << firstdup.key();
1959         qDebug() << " - "<< firstdup.value() <<" - "<<firstdup.value()->getHeading().getText();
1960             }   
1961         qDebug() << " - "<< i.value() <<" - "<<i.value()->getHeading().getText();
1962         } else
1963             firstdup=i;
1964
1965         ++i;
1966     }
1967 }
1968
1969 bool  VymModel::findAll (FindResultModel *rmodel, QString s, Qt::CaseSensitivity cs)   
1970 {
1971     rmodel->clear();
1972     rmodel->setSearchString (s);
1973     rmodel->setSearchFlags (0); //FIXME-4 translate cs to QTextDocument::FindFlag
1974     bool hit = false;
1975
1976     BranchItem *cur  = NULL;
1977     BranchItem *prev = NULL;
1978     nextBranch(cur,prev);
1979
1980     FindResultItem *lastParent = NULL;
1981     while (cur) 
1982     {
1983         lastParent = NULL;
1984         if (cur->getHeading().getTextASCII().contains (s,cs))
1985             {
1986                 lastParent = rmodel->addItem (cur);
1987                 hit = true;
1988             }
1989         QString n = cur->getNoteASCII();
1990         int i = 0;
1991         int j = 0;
1992         while ( i >= 0)
1993         {
1994             i = n.indexOf (s,i,cs); 
1995             if (i >= 0) 
1996             {
1997                 // If not there yet, add "parent" item
1998                 if (!lastParent)
1999                 {
2000                     lastParent = rmodel->addItem (cur);
2001                     hit = true;
2002                     if (!lastParent)
2003                         qWarning() << "VymModel::findAll still no lastParent?!";
2004                     /*
2005                     else
2006                         lastParent->setSelectable (false);
2007                     */  
2008                 }   
2009
2010                 // save index of occurence
2011                 QString e = n.mid(i-15, 30);
2012                 n.replace('\n', ' ');
2013                 rmodel->addSubItem (lastParent, QString(tr("Note", "FindAll in VymModel") + ": \"...%1...\"").arg(n.mid(i-8,80)), cur, j);
2014                 j++;
2015                 i++;
2016             }
2017         } 
2018         nextBranch(cur, prev);
2019     }
2020     return hit;
2021 }
2022
2023 BranchItem* VymModel::findText (QString s,Qt::CaseSensitivity cs)
2024 {
2025     if (!s.isEmpty() && s!=findString)
2026     {
2027         findReset();
2028         findString=s;
2029     }
2030
2031     QTextDocument::FindFlags flags=0;
2032     if (cs==Qt::CaseSensitive) flags=QTextDocument::FindCaseSensitively;
2033
2034     if (!findCurrent) 
2035     {   // Nothing found or new find process
2036         if (EOFind)
2037             // nothing found, start again
2038             EOFind=false;
2039         findCurrent=NULL;   
2040         findPrevious=NULL;  
2041         nextBranch (findCurrent,findPrevious);
2042     }   
2043     bool searching=true;
2044     bool foundNote=false;
2045     while (searching && !EOFind)
2046     {
2047         if (findCurrent)
2048         {
2049             // Searching in Note
2050         if (findCurrent->getNoteASCII().contains(findString,cs))
2051             {
2052                 select (findCurrent);
2053                 if (noteEditor->findText(findString,flags)) 
2054                 {
2055                     searching=false;
2056                     foundNote=true;
2057                 }   
2058             }
2059             // Searching in Heading
2060         if (searching && findCurrent->getHeading().getTextASCII().contains (findString,cs) )
2061             {
2062                 select(findCurrent);
2063                 searching=false;
2064             }
2065         }   
2066         if (!foundNote)
2067         {
2068             nextBranch(findCurrent,findPrevious);
2069             if (!findCurrent) EOFind=true;
2070         }
2071     }   
2072     if (!searching)
2073         return getSelectedBranch();
2074     else
2075         return NULL;
2076 }
2077
2078 void VymModel::findReset()
2079 {   // Necessary if text to find changes during a find process
2080     findString.clear();
2081     findCurrent=NULL;
2082     findPrevious=NULL;
2083     EOFind=false;
2084 }
2085
2086 void VymModel::setURL(QString url) 
2087 {
2088     TreeItem *selti=getSelectedItem();
2089     if (selti->getURL()==url) return;
2090     if (selti)
2091     {
2092         QString oldurl=selti->getURL();
2093         selti->setURL (url);
2094         saveState (
2095             selti,
2096             QString ("setURL (\"%1\")").arg(oldurl),
2097             selti,
2098             QString ("setURL (\"%1\")").arg(url),
2099             QString ("set URL of %1 to %2").arg(getObjectName(selti)).arg(url)
2100         );
2101         if (url.contains("bugzilla.novell.com/"))
2102             getBugzillaData(false);
2103         emitDataChanged (selti);
2104         reposition();
2105     }
2106 }   
2107
2108 QString VymModel::getURL()  
2109 {
2110     TreeItem *selti=getSelectedItem();
2111     if (selti)
2112         return selti->getURL();
2113     else    
2114         return QString();
2115 }
2116
2117 QStringList VymModel::getURLs(bool ignoreScrolled)  
2118 {
2119     QStringList urls;
2120     BranchItem *selbi=getSelectedBranch();
2121     BranchItem *cur=NULL;
2122     BranchItem *prev=NULL;
2123     nextBranch (cur,prev,true,selbi);
2124     while (cur) 
2125     {
2126         if (!cur->getURL().isEmpty()  && !(ignoreScrolled && cur->hasScrolledParent() )) 
2127             urls.append( cur->getURL());
2128         nextBranch(cur,prev,true,selbi);
2129     }   
2130     return urls;
2131 }
2132
2133
2134 void VymModel::setFrameType(const FrameObj::FrameType &t)   
2135 {
2136     BranchItem *bi=getSelectedBranch();
2137     if (bi)
2138     {
2139         BranchObj *bo=(BranchObj*)(bi->getLMO());
2140         if (bo)
2141         {
2142             QString s=bo->getFrameTypeName();
2143             bo->setFrameType (t);
2144             saveState (bi, QString("setFrameType (\"%1\")").arg(s),
2145                 bi, QString ("setFrameType (\"%1\")").arg(bo->getFrameTypeName()),QString ("set type of frame to %1").arg(s));
2146             reposition();
2147             bo->updateLinkGeometry();
2148         }
2149     }
2150 }
2151
2152 void VymModel::setFrameType(const QString &s)   
2153 {
2154     BranchItem *bi=getSelectedBranch();
2155     if (bi)
2156     {
2157         BranchObj *bo=(BranchObj*)(bi->getLMO());
2158         if (bo)
2159         {
2160             saveState (bi, QString("setFrameType (\"%1\")").arg(bo->getFrameTypeName()),
2161                 bi, QString ("setFrameType (\"%1\")").arg(s),QString ("set type of frame to %1").arg(s));
2162             bo->setFrameType (s);
2163             reposition();
2164             bo->updateLinkGeometry();
2165         }
2166     }
2167 }
2168
2169 void VymModel::toggleFrameIncludeChildren ()      
2170 {
2171     BranchItem *bi=getSelectedBranch();
2172     if (bi)
2173     {
2174         bool b=bi->getFrameIncludeChildren();
2175         setFrameIncludeChildren (!b);
2176     }
2177 }
2178
2179 void VymModel::setFrameIncludeChildren (bool b)     
2180 {
2181     BranchItem *bi=getSelectedBranch();
2182     if (bi)
2183     {
2184         QString u= b ? "false" : "true";
2185         QString r=!b ? "false" : "true";
2186         
2187         saveState(
2188             bi,
2189             QString("setFrameIncludeChildren(%1)").arg(u),
2190             bi, 
2191             QString("setFrameIncludeChildren(%1)").arg(r),
2192             QString("Include children in %1").arg(getObjectName(bi))
2193         );  
2194         bi->setFrameIncludeChildren (b);
2195         emitDataChanged (bi);
2196         reposition();
2197     }
2198 }
2199
2200 void VymModel::setFramePenColor(const QColor &c)    //FIXME-4 not saved if there is no LMO
2201
2202 {
2203     BranchItem *bi=getSelectedBranch();
2204     if (bi)
2205     {
2206         BranchObj *bo=(BranchObj*)(bi->getLMO());
2207         if (bo)
2208         {
2209             saveState (bi, QString("setFramePenColor (\"%1\")").arg(bo->getFramePenColor().name() ),
2210                 bi, QString ("setFramePenColor (\"%1\")").arg(c.name() ),QString ("set pen color of frame to %1").arg(c.name() ));
2211             bo->setFramePenColor (c);
2212         }   
2213     }   
2214 }
2215
2216 void VymModel::setFrameBrushColor(const QColor &c)  //FIXME-4 not saved if there is no LMO
2217 {
2218     BranchItem *bi=getSelectedBranch();
2219     if (bi)
2220     {
2221         BranchObj *bo=(BranchObj*)(bi->getLMO());
2222         if (bo)
2223         {
2224             saveState (bi, QString("setFrameBrushColor (\"%1\")").arg(bo->getFrameBrushColor().name() ),
2225                 bi, QString ("setFrameBrushColor (\"%1\")").arg(c.name() ),QString ("set brush color of frame to %1").arg(c.name() ));
2226             bo->setFrameBrushColor (c);
2227             bi->setBackgroundColor (c); //FIXME-4 redundant with above
2228         }   
2229     }   
2230 }
2231
2232 void VymModel::setFramePadding (const int &i) //FIXME-4 not saved if there is no LMO
2233 {
2234     BranchItem *bi=getSelectedBranch();
2235     if (bi)
2236     {
2237         BranchObj *bo=(BranchObj*)(bi->getLMO());
2238         if (bo)
2239         {
2240             saveState (bi, QString("setFramePadding (\"%1\")").arg(bo->getFramePadding() ),
2241                 bi, QString ("setFramePadding (\"%1\")").arg(i),QString ("set brush color of frame to %1").arg(i));
2242             bo->setFramePadding (i);
2243             reposition();
2244             bo->updateLinkGeometry();
2245         }   
2246     }   
2247 }
2248
2249 void VymModel::setFrameBorderWidth(const int &i) //FIXME-4 not saved if there is no LMO
2250 {
2251     BranchItem *bi=getSelectedBranch();
2252     if (bi)
2253     {
2254         BranchObj *bo=(BranchObj*)(bi->getLMO());
2255         if (bo)
2256         {
2257             saveState (bi, QString("setFrameBorderWidth (\"%1\")").arg(bo->getFrameBorderWidth() ),
2258                 bi, QString ("setFrameBorderWidth (\"%1\")").arg(i),QString ("set border width of frame to %1").arg(i));
2259             bo->setFrameBorderWidth (i);
2260             reposition();
2261             bo->updateLinkGeometry();
2262         }   
2263     }   
2264 }
2265
2266 void VymModel::setIncludeImagesVer(bool b)
2267 {
2268     BranchItem *bi=getSelectedBranch();
2269     if (bi && b!=bi->getIncludeImagesVer() )
2270     {
2271         QString u= b ? "false" : "true";
2272         QString r=!b ? "false" : "true";
2273         
2274         saveState(
2275             bi,
2276             QString("setIncludeImagesVertically (%1)").arg(u),
2277             bi, 
2278             QString("setIncludeImagesVertically (%1)").arg(r),
2279             QString("Include images vertically in %1").arg(getObjectName(bi))
2280         );  
2281         bi->setIncludeImagesVer(b);
2282         emitDataChanged ( bi);   
2283         reposition();
2284     }   
2285 }
2286
2287 void VymModel::setIncludeImagesHor(bool b)  
2288 {
2289     BranchItem *bi=getSelectedBranch();
2290     if (bi && b!=bi->getIncludeImagesHor() )
2291     {
2292         QString u= b ? "false" : "true";
2293         QString r=!b ? "false" : "true";
2294         
2295         saveState(
2296             bi,
2297             QString("setIncludeImagesHorizontally (%1)").arg(u),
2298             bi, 
2299             QString("setIncludeImagesHorizontally (%1)").arg(r),
2300             QString("Include images horizontally in %1").arg(getObjectName(bi))
2301         );  
2302         bi->setIncludeImagesHor(b);
2303         emitDataChanged ( bi);
2304         reposition();
2305     }   
2306 }
2307
2308 void VymModel::setChildrenLayout(BranchItem::LayoutHint layoutHint) // FIXME-3 no savestate yet
2309 {
2310     BranchItem *bi=getSelectedBranch();
2311     if (bi)
2312     {
2313         /*
2314         QString u= b ? "false" : "true";
2315         QString r=!b ? "false" : "true";
2316
2317         saveState(      
2318             bi,
2319             QString("setIncludeImagesHorizontally (%1)").arg(u),
2320             bi,
2321             QString("setIncludeImagesHorizontally (%1)").arg(r),
2322             QString("Include images horizontally in %1").arg(getObjectName(bi))
2323         );
2324         */
2325         bi->setChildrenLayout(layoutHint);
2326         emitDataChanged ( bi);
2327         reposition();
2328     }
2329 }
2330
2331 void VymModel::setHideLinkUnselected (bool b)
2332 {
2333     TreeItem *ti=getSelectedItem();
2334     if (ti && (ti->getType()==TreeItem::Image ||ti->isBranchLikeType()))
2335     {
2336         QString u= b ? "false" : "true";
2337         QString r=!b ? "false" : "true";
2338         
2339         saveState(
2340             ti,
2341             QString("setHideLinkUnselected (%1)").arg(u),
2342             ti, 
2343             QString("setHideLinkUnselected (%1)").arg(r),
2344             QString("Hide link of %1 if unselected").arg(getObjectName(ti))
2345         );  
2346         ((MapItem*)ti)->setHideLinkUnselected(b);
2347     }
2348 }
2349
2350 void VymModel::setHideExport(bool b, TreeItem *ti)
2351 {
2352     if (!ti) ti=getSelectedItem();
2353     if (ti && 
2354         (ti->getType()==TreeItem::Image ||ti->isBranchLikeType()) &&
2355         ti->hideInExport() !=b
2356         )
2357     {
2358         ti->setHideInExport (b);
2359         QString u= b ? "false" : "true";
2360         QString r=!b ? "false" : "true";
2361         
2362         saveState(
2363             ti,
2364             QString ("setHideExport (%1)").arg(u),
2365             ti,
2366             QString ("setHideExport (%1)").arg(r),
2367             QString ("Set HideExport flag of %1 to %2").arg(getObjectName(ti)).arg (r)
2368         );  
2369             emitDataChanged(ti);
2370             emitSelectionChanged();
2371         reposition(); 
2372     }
2373 }
2374
2375 void VymModel::toggleHideExport()
2376 {
2377     QList <TreeItem*> selItems=getSelectedItems();
2378     if (selItems.count()>0 )
2379     {
2380         foreach (TreeItem* ti, selItems)
2381         {
2382             bool b=!ti->hideInExport();
2383             setHideExport (b,ti );
2384         }
2385     }
2386 }
2387
2388 void VymModel::toggleTask() 
2389 {
2390     BranchItem *selbi=getSelectedBranch();
2391     if (selbi) 
2392     {
2393         saveStateChangingPart (
2394             selbi,
2395             selbi,
2396             QString ("toggleTask()"),
2397             QString ("Toggle task of %1").arg(getObjectName (selbi)) );
2398         Task *task=selbi->getTask();
2399         if (!task )
2400         {
2401             task=taskModel->createTask (selbi);
2402             taskEditor->select(task); 
2403         }
2404         else
2405             taskModel->deleteTask (task);
2406
2407         emitDataChanged(selbi);
2408         emitSelectionChanged();
2409         reposition();
2410     }
2411 }
2412
2413 void VymModel::cycleTaskStatus(bool reverse)
2414 {
2415     BranchItem *selbi=getSelectedBranch();
2416     if (selbi) 
2417     {
2418         Task *task=selbi->getTask();
2419         if (task) 
2420         {
2421             saveStateChangingPart (
2422                 selbi,
2423                 selbi,
2424                 QString ("cycleTask()"),
2425                 QString ("Toggle task of %1").arg(getObjectName (selbi)) );
2426             task->cycleStatus(reverse);
2427             task->setDateModified();
2428             
2429             // make sure task is still visible
2430             taskEditor->select (task);
2431             emitDataChanged(selbi);
2432             reposition();
2433         }
2434     }
2435 }
2436
2437 bool VymModel::setTaskSleep(const QString &s) 
2438 {
2439     BranchItem *selbi=getSelectedBranch();
2440     if (selbi && !s.isEmpty() ) 
2441     {
2442         Task *task=selbi->getTask();
2443         if (task ) 
2444         {
2445             bool ok;
2446             int n=s.toInt(&ok);
2447             if (!ok)
2448             {
2449                 // Is s a date?
2450                 QDate d=QDate::fromString(s,Qt::ISODate);
2451                 d=QDate::fromString(s,Qt::ISODate);
2452                 if (d.isValid())
2453                     // ISO date YYYY-MM-DD
2454                     ok=true;
2455                 else
2456                 {
2457                     d=QDate::fromString(s,Qt::DefaultLocaleShortDate);
2458                     if (d.isValid()) 
2459                         // Locale date, e.g. 24 Dec 2012
2460                         ok=true;
2461                     else
2462                     {
2463                         QRegExp re ("(\\d+).(\\d+).(\\d+)");
2464                         re.setMinimal(false);
2465                         int pos=re.indexIn(s);
2466                         QStringList list=re.capturedTexts();
2467                         if (pos>=0)
2468                         {
2469                             // German formate, e.g. 24.12.2012
2470                             d=QDate(list.at(3).toInt(), list.at(2).toInt(), list.at(1).toInt());
2471                             ok=true;
2472                         } else
2473                         {
2474                             re.setPattern("(\\d+).(\\d+).");
2475                             pos=re.indexIn(s);
2476                             list=re.capturedTexts();
2477                             if (pos>=0)
2478                             {
2479                                 // Short German formate, e.g. 24.12.
2480                                 int month=list.at(2).toInt();
2481                                 int day=list.at(1).toInt();
2482                                 int year=QDate::currentDate().year();
2483                                 d=QDate(year, month, day);
2484                                 if (QDate::currentDate().daysTo(d) < 0)
2485                                 {
2486                                     year++;
2487                                     d=QDate(year, month, day);
2488                                 }
2489                                 ok=true;
2490                             } else
2491                             {
2492                                 re.setPattern("(\\d+).(\\d+).");
2493                             }
2494                         }
2495                     }
2496                 }
2497                 if (ok) n=QDate::currentDate().daysTo(d);
2498             }
2499
2500             if (ok)
2501             {
2502                 int oldsleep=task->getDaysSleep();
2503                 task->setDateSleep (n);
2504                 task->setDateModified();
2505                 saveState (
2506                     selbi,
2507                     QString("setTaskSleep (%1)").arg(oldsleep),
2508                     selbi,
2509                     QString("setTaskSleep (%1)").arg(n),
2510                     QString("setTaskSleep (%1)").arg(n) );
2511                 emitDataChanged (selbi);
2512                 reposition();
2513                 return true;
2514             }
2515         }
2516     }
2517     return false;
2518 }
2519
2520 int VymModel::taskCount()
2521 {
2522     return taskModel->count (this);
2523 }
2524
2525 void VymModel::addTimestamp()   //FIXME-4 new function, localize
2526 {
2527     BranchItem *selbi=addNewBranch();
2528     if (selbi)
2529     {
2530         QDate today=QDate::currentDate();
2531         QChar c='0';
2532         selbi->setHeadingPlainText (
2533         QString ("%1-%2-%3")
2534             .arg(today.year(),4,10,c)
2535             .arg(today.month(),2,10,c)
2536             .arg(today.day(),2,10,c));
2537         emitDataChanged ( selbi);       
2538         reposition();
2539         select (selbi);
2540     }
2541 }
2542
2543
2544 void VymModel::copy()   
2545 {
2546     if (readonly) return;
2547
2548     TreeItem *selti=getSelectedItem();
2549     if (selti &&
2550         (selti->getType() == TreeItem::Branch || 
2551         selti->getType() == TreeItem::MapCenter  ||
2552         selti->getType() == TreeItem::Image ))
2553     {
2554         // Copy to global clipboard
2555         QString saveFile=saveToDir (clipboardDir, clipboardFile, true, QPointF(), selti);
2556         if (!saveStringToDisk(clipboardDir + "/" + clipboardFile,saveFile))
2557             qWarning ("ME::saveStringToDisk failed!");
2558
2559         clipboardEmpty=false;
2560
2561         if (redosAvail == 0)
2562         {
2563             // Copy also to history
2564             QString s=getSelectString(selti);
2565             saveState (PartOfMap, s, "nop ()", s, "copy ()","Copy selection to clipboard",selti  );
2566             curClipboard=curStep;
2567         }
2568         updateActions();
2569     }       
2570 }
2571
2572 void VymModel::paste()  
2573 {   
2574     if (readonly) return;
2575
2576     BranchItem *selbi=getSelectedBranch();
2577     if (selbi)
2578     {
2579         saveStateChangingPart(
2580             selbi,
2581             selbi,
2582             QString ("paste ()"),
2583             QString("Paste")
2584         );
2585         bool zippedOrg=zipped;
2586         loadMap (clipboardDir+"/"+clipboardFile,ImportAdd, VymMap,SlideContent);
2587         zipped=zippedOrg;
2588         reposition();
2589     }
2590 }
2591
2592 void VymModel::cut()    
2593 {
2594     if (readonly) return;
2595
2596     TreeItem *selti=getSelectedItem();
2597     if ( selti && (selti->isBranchLikeType() ||selti->getType()==TreeItem::Image))
2598     {
2599         copy();
2600         deleteSelection();
2601         reposition();
2602     }
2603 }
2604
2605 bool VymModel::moveUp(BranchItem *bi)
2606 {
2607     if (readonly) return false;
2608
2609     bool oldState=blockSaveState;
2610     blockSaveState=true;
2611     bool result=false;
2612     if (bi && bi->canMoveUp()) 
2613         result=relinkBranch (bi,(BranchItem*)bi->parent(),bi->num()-1,false);
2614     blockSaveState=oldState;
2615     return result;
2616 }
2617
2618 void VymModel::moveUp() 
2619 {
2620     BranchItem *selbi=getSelectedBranch();
2621     if (selbi)
2622     {
2623         QString oldsel=getSelectString(selbi);
2624         if (moveUp (selbi))
2625         {
2626             saveState (
2627                 getSelectString(selbi),"moveDown ()",
2628                 oldsel,"moveUp ()",
2629                 QString("Move up %1").arg(getObjectName(selbi)));
2630             select (selbi);             
2631         }
2632     }
2633 }
2634
2635 bool VymModel::moveDown(BranchItem *bi) 
2636 {
2637     if (readonly) return false;
2638
2639     bool oldState=blockSaveState;
2640     blockSaveState=true;
2641     bool result=false;
2642     if (bi && bi->canMoveDown()) 
2643         result=relinkBranch (bi,(BranchItem*)bi->parent(),bi->num()+1,false);
2644     blockSaveState=oldState;
2645     return result;
2646 }
2647
2648 void VymModel::moveDown()   
2649 {
2650     BranchItem *selbi=getSelectedBranch();
2651     if (selbi)
2652     {
2653         QString oldsel=getSelectString(selbi);
2654         if ( moveDown(selbi))
2655         {
2656             saveState (
2657                 getSelectString(selbi),"moveUp ()",
2658                 oldsel,"moveDown ()",
2659                 QString("Move down %1").arg(getObjectName(selbi)));
2660             select (selbi);
2661         }
2662     }
2663 }
2664
2665 void VymModel::detach() 
2666 {
2667     BranchItem *selbi=getSelectedBranch();
2668     if (selbi && selbi->depth()>0)
2669     {
2670         // if no relPos have been set before, try to use current rel positions   
2671         if (selbi->getLMO())
2672             for (int i=0; i<selbi->branchCount();++i)
2673                 selbi->getBranchNum(i)->getBranchObj()->setRelPos();
2674         
2675         QString oldsel=getSelectString();
2676         int n=selbi->num();
2677         QPointF p;
2678         BranchObj *bo=selbi->getBranchObj();
2679         if (bo) p=bo->getAbsPos();
2680         QString parsel=getSelectString(selbi->parent());
2681         if ( relinkBranch (selbi,rootItem,-1,true) )    
2682             saveState (
2683                 getSelectString (selbi),
2684                 QString("relinkTo (\"%1\",%2,%3,%4)").arg(parsel).arg(n).arg(p.x()).arg(p.y()),
2685                 oldsel,
2686                 "detach ()",
2687                 QString("Detach %1").arg(getObjectName(selbi))
2688             );
2689     }
2690 }
2691
2692 void VymModel::sortChildren(bool inverse) 
2693 {
2694     BranchItem* selbi=getSelectedBranch();
2695     if (selbi)
2696     {
2697         if(selbi->branchCount()>1)
2698         {
2699             if (!inverse)
2700                 saveStateChangingPart(
2701                     selbi,selbi, "sortChildren ()",
2702                     QString("Sort children of %1").arg(getObjectName(selbi)));
2703             else            
2704                 saveStateChangingPart(
2705                     selbi,selbi, "sortChildren (false)",
2706                     QString("Inverse sort children of %1").arg(getObjectName(selbi)));
2707
2708             selbi->sortChildren(inverse);
2709             select(selbi);
2710             reposition();
2711         }
2712     }
2713 }
2714
2715 BranchItem* VymModel::createMapCenter()
2716 {
2717     BranchItem *newbi=addMapCenter (QPointF (0,0) );
2718     return newbi;
2719 }
2720
2721 BranchItem* VymModel::createBranch(BranchItem *dst) 
2722 {
2723     if (dst)
2724         return addNewBranchInt (dst,-2);
2725     else
2726         return NULL;
2727 }
2728
2729 ImageItem* VymModel::createImage(BranchItem *dst)
2730 {
2731     if (dst)
2732     {
2733         QModelIndex parix;
2734         int n;
2735
2736         QList<QVariant> cData;
2737         cData << tr("Image","Default name for new image") << "undef";
2738
2739         ImageItem *newii=new ImageItem(cData) ;
2740         //newii->setHeading (QApplication::translate("Heading of new image in map", "new image"));
2741
2742         emit (layoutAboutToBeChanged() );
2743
2744         parix=index(dst);
2745         if (!parix.isValid()) qDebug() << "VM::createII invalid index\n";
2746         n=dst->getRowNumAppend(newii);
2747         beginInsertRows (parix,n,n);
2748         dst->appendChild (newii);
2749         endInsertRows ();
2750
2751         emit (layoutChanged() );
2752
2753         // save scroll state. If scrolled, automatically select
2754         // new branch in order to tmp unscroll parent...
2755         newii->createMapObj();
2756         latestAddedItem=newii;
2757         reposition();
2758         return newii;
2759     }
2760     return NULL;
2761 }
2762
2763 bool VymModel::createLink(Link *link)
2764 {
2765     BranchItem *begin=link->getBeginBranch();
2766     BranchItem *end  =link->getEndBranch();
2767
2768     if (!begin || !end)
2769     {
2770         qWarning ()<<"VM::createXLinkNew part of XLink is NULL";
2771         return false;
2772     }
2773
2774     if (begin==end)
2775     {
2776         if (debug) qDebug()<<"VymModel::createLink begin==end, aborting";
2777         return false;
2778     }
2779
2780     // check, if link already exists
2781     foreach (Link* l, xlinks)
2782     {
2783         if ( (l->getBeginBranch()==begin && l->getEndBranch()==end ) ||
2784              (l->getBeginBranch()==end   && l->getEndBranch()==begin) )
2785         {
2786             qWarning()<<"VymModel::createLink link exists already, aborting";
2787             return false;
2788         }
2789     }
2790
2791     QModelIndex parix;
2792     int n;
2793
2794     QList<QVariant> cData;
2795
2796     cData << "new Link begin"<<"undef";
2797     XLinkItem *newli=new XLinkItem(cData) ;     
2798     newli->setLink (link);
2799     link->setBeginLinkItem (newli);
2800
2801     emit (layoutAboutToBeChanged() );
2802
2803         parix=index(begin);
2804         n=begin->getRowNumAppend(newli);
2805         beginInsertRows (parix,n,n);
2806         begin->appendChild (newli);     
2807         endInsertRows ();
2808
2809     cData.clear();
2810     cData << "new Link end"<<"undef";
2811     newli=new XLinkItem(cData) ;        
2812     newli->setLink (link);
2813     link->setEndLinkItem (newli);
2814
2815         parix=index(end);
2816         n=end->getRowNumAppend(newli);
2817         beginInsertRows (parix,n,n);
2818         end->appendChild (newli);       
2819         endInsertRows ();
2820
2821     emit (layoutChanged() );
2822
2823     xlinks.append (link);
2824     link->activate();
2825
2826     latestAddedItem=newli;
2827
2828     if (!link->getMO() ) 
2829     {
2830         link->createMapObj();
2831         reposition();
2832     } else
2833         link->updateLink();
2834
2835     link->setStyleBegin( defXLinkStyleBegin );
2836     link->setStyleEnd  ( defXLinkStyleEnd );
2837     return true;
2838 }
2839
2840 QColor VymModel::getXLinkColor()
2841 {
2842     Link *l=getSelectedXLink();
2843     if (l)
2844         return l->getPen().color();
2845     else
2846         return QColor();
2847 }
2848
2849 int VymModel::getXLinkWidth()
2850 {
2851     Link *l=getSelectedXLink();
2852     if (l)
2853         return l->getPen().width();
2854     else
2855         return -1;
2856 }
2857
2858 Qt::PenStyle VymModel::getXLinkPenStyle()
2859 {
2860     Link *l=getSelectedXLink();
2861     if (l)
2862         return l->getPen().style();
2863     else
2864         return Qt::NoPen;
2865 }
2866
2867 QString VymModel::getXLinkStyleBegin()
2868 {
2869     Link *l=getSelectedXLink();
2870     if (l)
2871         return l->getStyleBeginString();
2872     else
2873         return QString();
2874 }
2875
2876 QString VymModel::getXLinkStyleEnd()
2877 {
2878     Link *l=getSelectedXLink();
2879     if (l)
2880         return l->getStyleEndString();
2881     else
2882         return QString();
2883 }
2884
2885 AttributeItem* VymModel::addAttribute()     // Experimental, savestate missing
2886
2887 {
2888     BranchItem* selbi=getSelectedBranch();
2889     if (selbi)
2890     {
2891         QList<QVariant> cData;
2892         cData << "new attribute" << "undef";
2893         AttributeItem *a=new AttributeItem (cData);
2894         a->setType (AttributeItem::FreeString);
2895         a->setKey   ("Foo Attrib");
2896         a->setValue ("Att val");
2897
2898         if (addAttribute (selbi,a)) return a;
2899     }
2900     return NULL;
2901 }
2902
2903 AttributeItem* VymModel::addAttribute(BranchItem *dst,AttributeItem *ai){
2904     if (dst)
2905     {
2906         emit (layoutAboutToBeChanged() );
2907
2908         QModelIndex parix=index(dst);
2909         int n=dst->getRowNumAppend (ai);
2910         beginInsertRows (parix,n,n);    
2911         dst->appendChild (ai);  
2912         endInsertRows ();
2913
2914         emit (layoutChanged() );
2915
2916         ai->createMapObj(mapEditor->getScene() );   
2917         reposition();
2918         return ai;
2919     }
2920     return NULL;
2921 }
2922
2923 BranchItem* VymModel::addMapCenter (bool saveStateFlag)
2924 {
2925     if (!hasContextPos) 
2926     {
2927         // E.g. when called via keypresss:
2928         // Place new MCO in middle of existing ones,
2929         // Useful for "brainstorming" mode...
2930         contextPos=QPointF();
2931         BranchItem *bi;
2932         BranchObj *bo;
2933         for (int i=0;i<rootItem->branchCount();++i)
2934         {
2935             bi=rootItem->getBranchNum (i);
2936             bo=(BranchObj*)bi->getLMO();
2937             if (bo) contextPos+=bo->getAbsPos();
2938             
2939         }           
2940         if (rootItem->branchCount()>1) 
2941             contextPos*=1/(qreal)(rootItem->branchCount());
2942     }
2943
2944
2945     BranchItem *bi=addMapCenter (contextPos);
2946     updateActions();
2947     emitShowSelection();
2948     if (saveStateFlag)
2949         saveState (
2950             bi,
2951             "delete()",
2952             NULL,
2953             QString ("addMapCenter (%1,%2)").arg (contextPos.x()).arg(contextPos.y()),
2954             QString ("Adding MapCenter to (%1,%2)").arg (contextPos.x()).arg(contextPos.y())
2955         );
2956             emitUpdateLayout();
2957     return bi;
2958 }
2959
2960 BranchItem* VymModel::addMapCenter(QPointF absPos)  
2961 // createMapCenter could then probably be merged with createBranch
2962 {
2963
2964     // Create TreeItem
2965     QModelIndex parix=index(rootItem);
2966
2967     QList<QVariant> cData;
2968     cData << "VM:addMapCenter" << "undef";
2969     BranchItem *newbi=new BranchItem (cData,rootItem);
2970     newbi->setHeadingPlainText (tr("New map", "New map"));
2971     int n=rootItem->getRowNumAppend (newbi);
2972
2973     emit (layoutAboutToBeChanged() );
2974     beginInsertRows (parix,n,n);
2975
2976     rootItem->appendChild (newbi);
2977
2978     endInsertRows();
2979     emit (layoutChanged() );
2980
2981     // Create MapObj
2982     newbi->setPositionMode (MapItem::Absolute);
2983     BranchObj *bo=newbi->createMapObj(mapEditor->getScene() );
2984     if (bo) bo->move (absPos);
2985         
2986     return newbi;
2987 }
2988
2989 BranchItem* VymModel::addNewBranchInt(BranchItem *dst,int pos)
2990 {
2991     // Depending on pos:
2992     // -3       insert in children of parent  above selection 
2993     // -2       add branch to selection 
2994     // -1       insert in children of parent below selection 
2995     // 0..n     insert in children of parent at pos
2996
2997     // Create TreeItem
2998     QList<QVariant> cData;
2999     cData << "" << "undef";
3000
3001     BranchItem *parbi = dst;
3002     int n;
3003     BranchItem *newbi = new BranchItem (cData); 
3004
3005     emit (layoutAboutToBeChanged() );
3006
3007     if (pos == -2)
3008     {
3009         n = parbi->getRowNumAppend (newbi);
3010         beginInsertRows (index(parbi), n, n);   
3011         parbi->appendChild (newbi); 
3012         endInsertRows ();
3013     }else if (pos == -1 || pos == -3)
3014     {
3015         // insert below selection
3016         parbi=(BranchItem*)dst->parent();
3017         n=dst->childNumber() + (3+pos)/2;   //-1 |-> 1;-3 |-> 0
3018         beginInsertRows (index(parbi), n, n);   
3019         parbi->insertBranch(n,newbi);   
3020         endInsertRows ();
3021     } else  
3022     {   // pos >= 0
3023         n=parbi->getRowNumAppend (newbi) - (parbi->branchCount()-pos);
3024         beginInsertRows (index(parbi), n, n);   
3025         parbi->insertBranch(pos,newbi); 
3026         endInsertRows ();
3027     }
3028     emit (layoutChanged() );
3029
3030     newbi->createMapObj(mapEditor->getScene());
3031     
3032     // Set color of heading to that of parent
3033     newbi->setHeadingColor (parbi->getHeadingColor());
3034
3035     reposition();
3036     return newbi;
3037 }   
3038
3039 BranchItem* VymModel::addNewBranch(BranchItem *bi, int pos)
3040 {
3041     BranchItem *newbi=NULL;
3042     if (!bi) bi=getSelectedBranch();
3043
3044     if (bi)
3045     {
3046         QString redosel=getSelectString(bi);
3047         newbi=addNewBranchInt (bi,pos);
3048         QString undosel=getSelectString(newbi);
3049
3050         if (newbi)
3051         {
3052             saveState(
3053                 undosel,        
3054                 "delete ()",
3055                 redosel,
3056                 QString ("addBranch (%1)").arg(pos),
3057                 QString ("Add new branch to %1").arg(getObjectName(bi)));       
3058
3059             reposition();
3060             latestAddedItem=newbi;
3061             // In Network mode, the client needs to know where the new branch is,
3062             // so we have to pass on this information via saveState.
3063             // TODO: Get rid of this positioning workaround
3064             /* FIXME-4  network problem:  QString ps=qpointfToString (newbo->getAbsPos());
3065             sendData ("selectLatestAdded ()");
3066             sendData (QString("move %1").arg(ps));
3067             sendSelection();
3068             */
3069         }
3070     }   
3071     return newbi;
3072 }
3073
3074
3075 BranchItem* VymModel::addNewBranchBefore()  
3076 {
3077     BranchItem *newbi=NULL;
3078     BranchItem *selbi=getSelectedBranch();
3079     if (selbi && selbi->getType()==TreeItem::Branch)
3080          // We accept no MapCenter here, so we _have_ a parent
3081     {
3082         // add below selection
3083         newbi=addNewBranchInt (selbi,-1);
3084
3085         if (newbi)
3086         {
3087             //newbi->move2RelPos (p);
3088
3089             // Move selection to new branch
3090             relinkBranch (selbi,newbi,0,true);
3091
3092             // Use color of child instead of parent
3093             newbi->setHeadingColor (selbi->getHeadingColor() );
3094             emitDataChanged (newbi);
3095
3096             saveState (newbi, "deleteKeepChildren ()", newbi, "addBranchBefore ()", 
3097                 QString ("Add branch before %1").arg(getObjectName(selbi)));
3098         }
3099     }   
3100     return newbi;
3101 }
3102
3103 bool VymModel::relinkBranch (
3104     BranchItem *branch, 
3105     BranchItem *dst, 
3106     int pos, 
3107     bool updateSelection,
3108     QPointF orgPos)
3109 {
3110     if (branch && dst)
3111     {
3112         // Check if we relink to ourselves
3113         if (dst->isChildOf (branch) ) return false;
3114          
3115         if (updateSelection) unselectAll();
3116  
3117         // Do we need to update frame type?
3118         bool keepFrame=true;
3119          
3120         // Save old position for savestate
3121         QString preSelStr=getSelectString (branch);
3122         QString preNum=QString::number (branch->num(),10);
3123         QString preParStr=getSelectString (branch->parent());
3124
3125         emit (layoutAboutToBeChanged() );
3126         BranchItem *branchpi=(BranchItem*)branch->parent();
3127         // Remove at current position
3128         int n=branch->childNum();
3129
3130         beginRemoveRows (index(branchpi),n,n);
3131         branchpi->removeChild (n);
3132         endRemoveRows();
3133
3134         if (pos<0 ||pos>dst->branchCount() ) pos=dst->branchCount();
3135
3136         // Append as last branch to dst
3137         if (dst->branchCount()==0)
3138             n=0;
3139         else    
3140             n=dst->getFirstBranch()->childNumber(); 
3141         beginInsertRows (index(dst),n+pos,n+pos);
3142         dst->insertBranch (pos,branch);
3143         endInsertRows();
3144
3145         // Correct type if necessesary
3146         if ( branch->getType()==TreeItem::MapCenter && branch->depth() >0 ) 
3147         {
3148             branch->setType(TreeItem::Branch);
3149             keepFrame=false;
3150         }
3151
3152         // reset parObj, fonts, frame, etc in related LMO or other view-objects
3153         branch->updateStyles(keepFrame);
3154
3155         emitDataChanged( branch );
3156         reposition();   // both for moveUp/Down and relinking
3157
3158         // Savestate
3159         QString postSelStr=getSelectString(branch);
3160         QString postNum=QString::number (branch->num(),10);
3161
3162         QPointF savePos;
3163         LinkableMapObj *lmosel=branch->getLMO();
3164         if (lmosel) savePos=lmosel->getAbsPos();
3165
3166         if (!blockSaveState)
3167         {   // Don't build strings when moving up/down
3168             QString undoCom="relinkTo (\""+ 
3169                 preParStr+ "\"," + preNum  +"," + 
3170                 QString ("%1,%2").arg(orgPos.x()).arg(orgPos.y())+ ")";
3171
3172             QString redoCom="relinkTo (\""+ 
3173                 getSelectString (dst)  + "\"," + postNum + "," +
3174                 QString ("%1,%2").arg(savePos.x()).arg(savePos.y())+ ")";
3175
3176             saveState (
3177                 postSelStr,undoCom,
3178                 preSelStr, redoCom,
3179                 QString("Relink %1 to %2").arg(getObjectName(branch)).arg(getObjectName(dst)) );
3180         }
3181
3182         // New parent might be invisible
3183         branch->updateVisibility();
3184
3185         if (dst->isScrolled() )
3186         {
3187             if (updateSelection) select (dst);
3188         }
3189         else    
3190             if (updateSelection) select (branch);
3191         return true;
3192     }
3193     return false;
3194 }
3195
3196 bool VymModel::relinkImage (ImageItem *image, BranchItem *dst)
3197 {
3198     if (image && dst)
3199     {
3200         emit (layoutAboutToBeChanged() );
3201
3202         BranchItem *pi=(BranchItem*)(image->parent());
3203         QString oldParString=getSelectString (pi);
3204         // Remove at current position
3205         int n=image->childNum();
3206         beginRemoveRows (index(pi),n,n);
3207         pi->removeChild (n);
3208         endRemoveRows();
3209
3210         // Add at dst
3211         QModelIndex dstix=index(dst);
3212         n=dst->getRowNumAppend (image);
3213         beginInsertRows (dstix,n,n+1);  
3214         dst->appendChild (image);   
3215         endInsertRows ();
3216
3217         // Set new parent also for lmo
3218         if (image->getLMO() && dst->getLMO() )
3219             image->getLMO()->setParObj (dst->getLMO() );
3220
3221         emit (layoutChanged() );
3222         saveState(
3223             image,
3224             QString("relinkTo (\"%1\")").arg(oldParString), 
3225             image,
3226             QString ("relinkTo (\"%1\")").arg(getSelectString (dst)),
3227             QString ("Relink floatimage to %1").arg(getObjectName(dst)));
3228         return true;    
3229     }
3230     return false;
3231 }
3232
3233 void VymModel::cleanupItems()
3234 {
3235     while (!deleteLaterIDs.isEmpty())
3236     {
3237         TreeItem *ti=findID (deleteLaterIDs.takeFirst());
3238         if (ti) deleteItem (ti);
3239     }
3240 }
3241
3242 void VymModel::deleteLater(uint id)     
3243 {
3244     if (!deleteLaterIDs.contains(id))
3245         deleteLaterIDs.append (id);
3246 }
3247
3248 void VymModel::deleteSelection()    
3249 {
3250     QList <uint> selectedIDs=getSelectedIDs();
3251     foreach (uint id, selectedIDs)
3252     {
3253         TreeItem *ti=findID (id);
3254         if (ti && ti->isBranchLikeType ())
3255         {   // Delete branch
3256             BranchItem *selbi=(BranchItem*)ti;
3257             unselectAll();
3258             saveStateRemovingPart (selbi, QString ("Delete %1").arg(getObjectName(selbi)));
3259
3260             BranchItem *pi=(BranchItem*)(deleteItem (selbi));
3261             if (pi)
3262             {
3263                 if (pi->isScrolled() && pi->branchCount()==0)
3264                     pi->unScroll();
3265                 emitDataChanged(pi);
3266                 select (pi);
3267             } else
3268                 emitDataChanged(rootItem); 
3269             ti=NULL;            
3270         }
3271
3272         // Delete other item
3273         if (ti)
3274         {
3275             TreeItem *pi=ti->parent(); 
3276             if (!pi) return;
3277             if (ti->getType()==TreeItem::Image || ti->getType()==TreeItem::Attribute||ti->getType()==TreeItem::XLink)
3278             {
3279                 saveStateChangingPart(
3280                     pi, 
3281                     ti,
3282                     "delete ()",
3283                     QString("Delete %1").arg(getObjectName(ti))
3284                 );
3285                 unselectAll();
3286                 deleteItem (ti);
3287                 emitDataChanged (pi);
3288                 select (pi);
3289                 reposition();
3290             } else
3291                 qWarning ("VymmModel::deleteSelection()  unknown type?!");
3292         }
3293     }
3294 }
3295
3296 void VymModel::deleteKeepChildren(bool saveStateFlag)
3297 //deleteKeepChildren FIXME-3+ does not work yet for mapcenters 
3298 //deleteKeepChildren FIXME-3+ children of scrolled branch stay invisible...
3299 {
3300     BranchItem *selbi=getSelectedBranch();
3301     BranchItem *pi;
3302     if (selbi)
3303     {
3304         // Don't use this on mapcenter
3305         if (selbi->depth()<1) return;
3306
3307         pi=(BranchItem*)(selbi->parent());
3308         // Check if we have children at all to keep
3309         if (selbi->branchCount()==0) 
3310         {
3311             deleteSelection();
3312             return;
3313         }
3314
3315         QPointF p;
3316         if (selbi->getLMO()) p=selbi->getLMO()->getRelPos();
3317         if (saveStateFlag) saveStateChangingPart(
3318             pi,
3319             pi,
3320             "deleteKeepChildren ()",
3321             QString("Remove %1 and keep its children").arg(getObjectName(selbi))
3322         );
3323
3324         QString sel=getSelectString(selbi);
3325         unselectAll();
3326         bool oldSaveState=blockSaveState;
3327         blockSaveState=true;
3328         int pos=selbi->num();
3329         BranchItem *bi=selbi->getFirstBranch();
3330         while (bi)
3331         {
3332             relinkBranch (bi,pi,pos,true);
3333             bi=selbi->getFirstBranch();
3334             pos++;
3335         }
3336         deleteItem (selbi);
3337         reposition();
3338         emitDataChanged(pi);
3339         select (sel);
3340         BranchObj *bo=getSelectedBranchObj();
3341         if (bo) 
3342         {
3343             bo->move2RelPos (p);
3344             reposition();
3345         }
3346         blockSaveState=oldSaveState;
3347     }   
3348 }
3349
3350 void VymModel::deleteChildren()     
3351
3352 {
3353     BranchItem *selbi=getSelectedBranch();
3354     if (selbi)
3355     {       
3356         saveStateChangingPart(
3357             selbi, 
3358             selbi,
3359             "deleteChildren ()",
3360             QString( "Remove children of branch %1").arg(getObjectName(selbi))
3361         );
3362         emit (layoutAboutToBeChanged() );
3363
3364         QModelIndex ix=index (selbi);
3365         int n=selbi->branchCount()-1;
3366         beginRemoveRows (ix,0,n);
3367         removeRows (0,n+1,ix);
3368         endRemoveRows();
3369         if (selbi->isScrolled()) unscrollBranch (selbi);
3370         emit (layoutChanged() );
3371         reposition();
3372     }   
3373 }
3374
3375 TreeItem* VymModel::deleteItem (TreeItem *ti)
3376 {
3377     if (ti)
3378     {
3379         TreeItem *pi=ti->parent();
3380         //qDebug()<<"VM::deleteItem  start ti="<<ti<<"  "<<ti->getHeading()<<"  pi="<<pi<<"="<<pi->getHeading();
3381
3382         TreeItem::Type t=ti->getType();
3383         
3384         QModelIndex parentIndex=index(pi);
3385
3386         emit (layoutAboutToBeChanged() );
3387
3388         int n=ti->childNum();
3389         beginRemoveRows (parentIndex,n,n);
3390         removeRows (n,1,parentIndex);
3391         endRemoveRows();
3392
3393         // Size of parent branch might change when deleting images
3394         if (t==TreeItem::Image)
3395         {
3396             BranchObj *bo=(BranchObj*) ( ((BranchItem*)pi)->getMO() );
3397             if (bo) bo->calcBBoxSize();
3398         }
3399
3400         reposition();
3401
3402         emit (layoutChanged() );
3403         emitUpdateQueries ();
3404         if (!cleaningUpLinks) cleanupItems();
3405
3406         //qDebug()<<"VM::deleteItem  end   ti="<<ti;
3407         if (pi->depth()>=0) return pi;
3408     }   
3409     return NULL;
3410 }
3411
3412 void VymModel::deleteLink(Link* l)  
3413 {
3414     if (xlinks.removeOne (l)) delete (l);
3415 }
3416
3417 void VymModel::clearItem (TreeItem *ti)
3418 {
3419     if (ti)
3420     {
3421         // Clear task (or other data in item itself)
3422         ti->clear();
3423
3424         QModelIndex parentIndex=index(ti);
3425         if (!parentIndex.isValid()) return;
3426
3427         int n=ti->childCount();
3428         if (n==0) return;
3429
3430         emit (layoutAboutToBeChanged() );
3431
3432         beginRemoveRows (parentIndex,0,n-1);
3433         removeRows (0,n,parentIndex);
3434         endRemoveRows();
3435
3436
3437         reposition();
3438
3439         emit (layoutChanged() );
3440
3441     }   
3442     return ;
3443 }
3444
3445 bool VymModel::scrollBranch(BranchItem *bi)
3446 {
3447     if (bi) 
3448     {
3449         if (bi->isScrolled()) return false;
3450         if (bi->branchCount()==0) return false;
3451         if (bi->depth()==0) return false;
3452         if (bi->toggleScroll())
3453         {
3454             QString u,r;
3455             r="scroll";
3456             u="unscroll";
3457             saveState(
3458                 bi,
3459                 QString ("%1 ()").arg(u),
3460                 bi,
3461                 QString ("%1 ()").arg(r),
3462                 QString ("%1 %2").arg(r).arg(getObjectName(bi))
3463             );
3464             emitDataChanged(bi);
3465             emitSelectionChanged();
3466             reposition();
3467             mapEditor->getScene()->update(); //Needed for _quick_ update,  even in 1.13.x 
3468             return true;
3469         }
3470     }   
3471     return false;
3472 }
3473
3474 bool VymModel::unscrollBranch(BranchItem *bi)
3475 {
3476     if (bi)
3477     {
3478         if (!bi->isScrolled()) return false;
3479         if (bi->toggleScroll())
3480         {
3481             QString u,r;
3482             u="scroll";
3483             r="unscroll";
3484             saveState(
3485                 bi,
3486                 QString ("%1 ()").arg(u),
3487                 bi,
3488                 QString ("%1 ()").arg(r),
3489                 QString ("%1 %2").arg(r).arg(getObjectName(bi))
3490             );
3491             emitDataChanged(bi);
3492             emitSelectionChanged();
3493             reposition();
3494             mapEditor->getScene()->update(); //Needed for _quick_ update,  even in 1.13.x 
3495             return true;
3496         }   
3497     }   
3498     return false;
3499 }
3500
3501 void VymModel::toggleScroll()   
3502 {
3503     BranchItem *selbi=getSelectedBranch();
3504     if (selbi)
3505     {
3506         if (selbi->isScrolled())
3507             unscrollBranch (selbi);
3508         else
3509             scrollBranch (selbi);
3510         // Note: saveState & reposition are called in above functions
3511     }
3512 }
3513
3514 void VymModel::unscrollChildren() 
3515 {
3516     BranchItem *selbi=getSelectedBranch();
3517     if (selbi)
3518     {
3519         saveStateChangingPart(
3520             selbi,
3521             selbi,
3522             QString ("unscrollChildren ()"),
3523             QString ("unscroll all children of %1").arg(getObjectName(selbi))
3524         );  
3525         BranchItem *prev=NULL;
3526         BranchItem *cur=NULL;
3527         nextBranch (cur,prev,true,selbi);
3528         while (cur) 
3529         {
3530             if (cur->isScrolled())
3531             {
3532                 cur->toggleScroll(); 
3533                 emitDataChanged (cur);
3534             }
3535             nextBranch (cur,prev,true,selbi);
3536         }   
3537         updateActions();
3538         reposition();
3539         // Would this help??? emitSelectionChanged();   
3540     }   
3541 }
3542
3543 void VymModel::setScale(qreal xn, qreal yn) 
3544 {
3545     ImageItem *selii=getSelectedImage();
3546     if (selii)
3547     {
3548         qreal sx=selii->getScaleX();
3549         qreal sy=selii->getScaleY();
3550         selii->setScale (xn,yn);
3551         saveState ( 
3552             selii,
3553             QString ("setScale(%1,%2)").arg(sx).arg(sy),
3554             selii,
3555             QString ("setScale(%1,%2)").arg(xn).arg(yn),
3556             QString ("Scale %1").arg(getObjectName(selii))
3557         );  
3558         reposition();
3559     }   
3560 }
3561
3562 void VymModel::growSelectionSize()  //FIXME-3 Also for heading in BranchItem?
3563 {
3564     ImageItem *selii=getSelectedImage();
3565     if (selii)
3566     {
3567         qreal f=0.05;
3568         qreal sx=selii->getScaleX();
3569         qreal sy=selii->getScaleY();
3570         setScale (sx+f,sy+f);
3571     }   
3572 }
3573
3574 void VymModel::shrinkSelectionSize() 
3575 {
3576     ImageItem *selii=getSelectedImage();
3577     if (selii)
3578     {
3579         qreal f=0.05;
3580         qreal sx=selii->getScaleX();
3581         qreal sy=selii->getScaleY();
3582         setScale (sx-f,sy-f);
3583     }   
3584 }
3585
3586 void VymModel::resetSelectionSize() 
3587 {
3588     ImageItem *selii=getSelectedImage();
3589     if (selii) setScale (1,1);
3590 }
3591
3592 void VymModel::emitExpandAll()  
3593 {
3594     emit (expandAll() );
3595 }
3596
3597 void VymModel::emitExpandOneLevel() 
3598 {
3599     emit (expandOneLevel () );
3600 }
3601
3602 void VymModel::emitCollapseOneLevel()   
3603 {
3604     emit (collapseOneLevel () );
3605 }
3606
3607 void VymModel::emitCollapseUnselected() 
3608 {
3609     emit (collapseUnselected() );
3610 }
3611
3612 void VymModel::toggleTarget()   
3613 {
3614     BranchItem *selbi=getSelectedBranch();
3615     if (selbi)
3616     {
3617         selbi->toggleTarget(); 
3618         reposition();
3619         saveState ( 
3620             selbi,
3621             "toggleTarget()",
3622             selbi,
3623             "toggleTarget",
3624             "Toggle target");
3625     }
3626 }
3627
3628 ItemList VymModel::getTargets() 
3629 {
3630     ItemList targets;
3631     
3632     //rmodel->setSearchString (s);
3633
3634     BranchItem *cur=NULL;
3635     BranchItem *prev=NULL;
3636     nextBranch(cur,prev);
3637
3638     while (cur) 
3639     {
3640         if (cur->hasActiveSystemFlag("system-target"))
3641             targets[cur->getID()] = (cur->getHeading()).getTextASCII();
3642         nextBranch(cur,prev);
3643     }
3644     return targets; 
3645 }
3646
3647 void VymModel::toggleStandardFlag (const QString &name, FlagRow *master)
3648 {
3649     BranchItem *bi=getSelectedBranch();
3650     if (bi) 
3651     {
3652         QString u,r;
3653         if (bi->hasActiveStandardFlag(name))
3654         {
3655             r="unsetFlag";
3656             u="setFlag";
3657         }   
3658         else
3659         {
3660             u="unsetFlag";
3661             r="setFlag";
3662         }   
3663         saveState(
3664             bi,
3665             QString("%1 (\"%2\")").arg(u).arg(name), 
3666             bi,
3667             QString("%1 (\"%2\")").arg(r).arg(name),
3668             QString("Toggling standard flag \"%1\" of %2").arg(name).arg(getObjectName(bi)));
3669             bi->toggleStandardFlag (name, master);
3670         emitDataChanged (bi);
3671         reposition();
3672     }
3673 }
3674
3675 void VymModel::addFloatImage (const QImage &img) 
3676 {
3677     BranchItem *selbi=getSelectedBranch();
3678     if (selbi)
3679     {
3680         ImageItem *ii=createImage (selbi);
3681         ii->load(img);
3682         ii->setOriginalFilename("No original filename (image added by dropevent)"); 
3683         QString s=getSelectString(selbi);
3684         saveState (PartOfMap, s, "nop ()", s, "copy ()","Copy dropped image to clipboard",ii  );
3685         saveState (ii,"delete ()", selbi,QString("paste(%1)").arg(curStep),"Pasting dropped image");
3686         reposition();
3687     }
3688 }
3689
3690
3691 void VymModel::colorBranch (QColor c)   
3692 {
3693     QList <BranchItem*> selbis=getSelectedBranches();
3694     foreach (BranchItem* selbi, selbis)
3695     {
3696         saveState(
3697             selbi, 
3698             QString ("colorBranch (\"%1\")").arg(selbi->getHeadingColor().name()),
3699             selbi,
3700             QString ("colorBranch (\"%1\")").arg(c.name()),
3701             QString("Set color of %1 to %2").arg(getObjectName(selbi)).arg(c.name())
3702         );  
3703         selbi->setHeadingColor(c); // color branch
3704         emitDataChanged (selbi);
3705         taskEditor->showSelection();
3706     }
3707     mapEditor->getScene()->update();    
3708 }
3709
3710 void VymModel::colorSubtree (QColor c, BranchItem *b) 
3711 {
3712     QList <BranchItem*> selbis;
3713     if (b) 
3714         selbis.append (b);
3715     else
3716         selbis=getSelectedBranches();
3717     foreach (BranchItem *bi,selbis)
3718     {
3719         saveStateChangingPart(
3720             bi,
3721             bi,
3722             QString ("colorSubtree (\"%1\")").arg(c.name()),
3723             QString ("Set color of %1 and children to %2").arg(getObjectName(bi)).arg(c.name())
3724         );  
3725         BranchItem *prev=NULL;
3726         BranchItem *cur=NULL;
3727         nextBranch (cur,prev,true,bi);
3728         while (cur) 
3729         {
3730             cur->setHeadingColor(c); // color links, color children
3731             emitDataChanged (cur);
3732             nextBranch (cur,prev,true,bi);
3733         }   
3734     }
3735     taskEditor->showSelection();
3736     mapEditor->getScene()->update();
3737 }
3738
3739 QColor VymModel::getCurrentHeadingColor()   
3740 {
3741     BranchItem *selbi=getSelectedBranch();
3742     if (selbi)  return selbi->getHeadingColor();
3743         
3744     QMessageBox::warning(0,"Warning","Can't get color of heading,\nthere's no branch selected");
3745     return Qt::black;
3746 }
3747
3748 void VymModel::note2URLs()    
3749 {
3750     BranchItem *selbi=getSelectedBranch();
3751     if (selbi)
3752     {       
3753         saveStateChangingPart(
3754             selbi,
3755             selbi,
3756             QString ("note2URLs()"),
3757             QString ("Extract URLs from note of %1").arg(getObjectName(selbi))
3758         );  
3759
3760     QString n = selbi->getNoteASCII();
3761         if (n.isEmpty()) return;
3762         QRegExp re ("(http.*)(\\s|\"|')");
3763         re.setMinimal (true);
3764
3765         BranchItem *bi;
3766         int pos = 0;
3767         while ((pos = re.indexIn(n, pos)) != -1) 
3768         {
3769             bi=createBranch (selbi);
3770             bi->setHeadingPlainText (re.cap(1));
3771             bi->setURL (re.cap(1));
3772             emitDataChanged (bi);
3773             pos += re.matchedLength();
3774         }
3775         
3776     }
3777 }
3778
3779 void VymModel::editHeading2URL() 
3780 {
3781     TreeItem *selti=getSelectedItem();
3782     if (selti)
3783     setURL (selti->getHeadingPlain());
3784 }   
3785
3786 void VymModel::editBugzilla2URL()   
3787 {
3788     TreeItem *selti=getSelectedItem();
3789     if (selti)
3790     {       
3791     QString h=selti->getHeadingPlain();
3792         QRegExp rx("(\\d+)");
3793         if (rx.indexIn(h) !=-1)
3794             setURL ("https://bugzilla.novell.com/show_bug.cgi?id="+rx.cap(1) );
3795     }
3796 }   
3797
3798 void VymModel::getBugzillaData(bool subtree)    
3799 {
3800     if (!bugzillaClientAvailable)
3801     {
3802         WarningDialog dia;
3803         dia.setText(
3804             QObject::tr("No Bugzilla client found. "
3805             " For openSUSE you can install by (running as root):\n\n","VymModel, how to install Bugzilla client module")+
3806             "  zypper ar http://download.opensuse.org/repositories/openSUSE:/Tools/openSUSE_XX.Y/ openSUSE:Tools_XX.Y\n"+
3807             "  zypper in perl-SUSE-BugzillaClient\n\n"+
3808             "  and replace XX.Y with your version of openSUSE, e.g. 11.4\n\n"+
3809             QObject::tr("Alternatively you can also add the repository\n"
3810             "and install the perl module for Bugzilla access using YaST","VymModel, how to install Bugzilla client module")
3811         );
3812         dia.setWindowTitle(QObject::tr("Warning: Couldn't find Bugzilla client","VymModel"));
3813         dia.setShowAgainName("/BugzillaClient/missing");
3814         dia.exec();
3815         return;
3816     }
3817     
3818     BranchItem *selbi=getSelectedBranch();
3819     if (selbi)
3820     {       
3821         QString url;
3822         BranchItem *prev=NULL;
3823         BranchItem *cur=NULL;
3824         nextBranch (cur,prev,true,selbi);
3825         while (cur) 
3826         {
3827             url=cur->getURL();
3828             if (!url.isEmpty())
3829             {
3830                 // Don't run query again if we are in update mode
3831                 if (!subtree || ! url.contains("buglist.cgi") )
3832                 {
3833                     new BugAgent (cur,url);
3834                     mainWindow->statusMessage (tr("Contacting Bugzilla...","VymModel"));
3835                 }
3836             }
3837             if (subtree) 
3838                 nextBranch (cur,prev,true,selbi);
3839             else
3840                 cur=NULL;
3841         }   
3842     }
3843 }   
3844
3845 void VymModel::editFATE2URL()
3846 {
3847     TreeItem *selti=getSelectedItem();
3848     if (selti)
3849     {       
3850     QString url= "http://keeper.suse.de:8080/webfate/match/id?value=ID"+selti->getHeadingPlain();
3851         saveState(
3852             selti,
3853             "setURL (\""+selti->getURL()+"\")",
3854             selti,
3855             "setURL (\""+url+"\")",
3856             QString("Use heading of %1 as link to FATE").arg(getObjectName(selti))
3857         );  
3858         selti->setURL (url);
3859         // FIXME-4 updateActions();
3860     }
3861 }   
3862
3863 void VymModel::setVymLink (const QString &s)    //FIXME-4 fail, if s does not exist
3864 {
3865     BranchItem *bi=getSelectedBranch();
3866     if (bi)
3867     {
3868         saveState(
3869             bi,
3870             "setVymLink (\""+bi->getVymLink()+"\")", 
3871             bi,
3872             "setVymLink (\""+s+"\")", 
3873             QString("Set vymlink of %1 to %2").arg(getObjectName(bi)).arg(s)
3874         );  
3875         bi->setVymLink(s);
3876         emitDataChanged (bi);
3877         reposition();
3878     }
3879 }
3880
3881 void VymModel::deleteVymLink()
3882 {
3883     BranchItem *bi=getSelectedBranch();
3884     if (bi)
3885     {       
3886         saveState(
3887             bi,
3888             "setVymLink (\""+bi->getVymLink()+"\")", 
3889             bi,
3890             "setVymLink (\"\")",
3891             QString("Unset vymlink of %1").arg(getObjectName(bi))
3892         );  
3893         bi->setVymLink ("");
3894         emitDataChanged (bi);
3895         reposition();
3896         updateActions();
3897     }
3898 }
3899
3900 QString VymModel::getVymLink()
3901 {
3902     BranchItem *bi=getSelectedBranch();
3903     if (bi)
3904         return bi->getVymLink();
3905     else    
3906         return "";
3907     
3908 }
3909
3910 QStringList VymModel::getVymLinks() 
3911 {
3912     QStringList links;
3913     BranchItem *selbi=getSelectedBranch();
3914     BranchItem *cur=NULL;
3915     BranchItem *prev=NULL;
3916     nextBranch (cur,prev,true,selbi);
3917     while (cur) 
3918     {
3919         if (!cur->getVymLink().isEmpty()) links.append( cur->getVymLink());
3920         nextBranch (cur,prev,true,selbi);
3921     }   
3922     return links;
3923 }
3924
3925
3926 void VymModel::followXLink(int i)   
3927 {
3928     BranchItem *selbi=getSelectedBranch();
3929     if (selbi)
3930     {
3931         selbi=selbi->getXLinkItemNum(i)->getPartnerBranch();
3932         if (selbi) select (selbi);
3933     }
3934 }
3935
3936 void VymModel::editXLink()
3937 {
3938     Link *l=getSelectedXLink();
3939     if (l) 
3940     {
3941         EditXLinkDialog dia;
3942         dia.setLink (l);
3943         if (dia.exec() == QDialog::Accepted)
3944         {
3945             if (dia.useSettingsGlobal() )
3946             {
3947                 setMapDefXLinkPen( l->getPen() );
3948                 setMapDefXLinkStyleBegin( l->getStyleBeginString() );
3949                 setMapDefXLinkStyleEnd( l->getStyleEndString() );
3950             }
3951         }
3952     }   
3953 }
3954
3955 void VymModel::setXLinkColor(const QString &new_col)
3956 {
3957     Link *l = getSelectedXLink();
3958     if (l) 
3959     {
3960         QPen pen = l->getPen();
3961         QColor new_color = QColor( new_col );
3962         QColor old_color = pen.color();
3963         if (new_color == old_color) return;
3964         pen.setColor( new_color);
3965         l->setPen( pen );
3966         saveState(
3967                 l->getBeginLinkItem(),
3968                 QString("setXLinkColor(\"%1\")").arg(old_color.name() ),
3969                 l->getBeginLinkItem(),
3970                 QString("setXLinkColor(\"%1\")").arg(new_color.name() ),
3971                 QString("set color of xlink to %1").arg(new_color.name() ) );
3972     }   
3973 }
3974
3975 void VymModel::setXLinkLineStyle(const QString &new_style)
3976 {
3977     Link *l = getSelectedXLink();
3978     if (l) 
3979     {
3980         QPen pen = l->getPen();
3981         QString old_style = penStyleToString( pen.style() );
3982         if (new_style == old_style) return;
3983         bool ok;
3984         pen.setStyle( penStyle(new_style, ok) );
3985         l->setPen( pen );
3986         saveState(
3987                 l->getBeginLinkItem(),
3988                 QString("setXLinkLineStyle(\"%1\")").arg(old_style),
3989                 l->getBeginLinkItem(),
3990                 QString("setXLinkLineStyle(\"%1\")").arg(new_style),
3991                 QString("set style of xlink to %1").arg(new_style) );
3992     }   
3993 }
3994
3995 void VymModel::setXLinkStyleBegin(const QString &new_style)
3996 {
3997     Link *l=getSelectedXLink();
3998     if (l) 
3999     {
4000         QString old_style = l->getStyleBeginString();
4001         if (new_style == old_style) return;
4002         l->setStyleBegin( new_style );
4003         saveState(
4004                 l->getBeginLinkItem(),
4005                 QString("setXLinkStyleBegin(\"%1\")").arg(old_style),
4006                 l->getBeginLinkItem(),
4007                 QString("setXLinkStyleBegin(\"%1\")").arg(new_style),
4008                 "set style of xlink begin");
4009     }   
4010 }
4011
4012 void VymModel::setXLinkStyleEnd(const QString &new_style)
4013 {
4014     Link *l=getSelectedXLink();
4015     if (l) 
4016     {
4017         QString old_style = l->getStyleEndString();
4018         if (new_style == old_style) return;
4019         l->setStyleEnd( new_style );
4020         saveState(
4021                 l->getBeginLinkItem(),
4022                 QString("setXLinkStyleEnd(\"%1\")").arg(old_style),
4023                 l->getBeginLinkItem(),
4024                 QString("setXLinkStyleEnd(\"%1\")").arg(new_style),
4025                 "set style of xlink end");
4026     }   
4027 }
4028
4029 void VymModel::setXLinkWidth(int new_width)
4030 {
4031     Link *l=getSelectedXLink();
4032     if (l) 
4033     {
4034         QPen pen = l->getPen();
4035         int old_width = pen.width();
4036         if (new_width == old_width) return;
4037         pen.setWidth( new_width);
4038         l->setPen( pen );
4039         saveState(
4040                 l->getBeginLinkItem(),
4041                 QString("setXLinkWidth(%1)").arg(old_width),
4042                 l->getBeginLinkItem(),
4043                 QString("setXLinkWidth(%1)").arg(new_width),
4044                 "set width of xlink");
4045     }   
4046 }
4047
4048 //////////////////////////////////////////////
4049 // Scripting
4050 //////////////////////////////////////////////
4051
4052 QVariant VymModel::parseAtom(const QString &atom, bool &noErr, QString &errorMsg)
4053 {
4054     TreeItem* selti=getSelectedItem();
4055     BranchItem *selbi=getSelectedBranch();
4056     QString s,t;
4057     double x,y;
4058     int n;
4059     bool b,ok;
4060     QVariant returnValue="";
4061
4062     // Split string s into command and parameters
4063     parser.parseAtom (atom);
4064
4065     if (parser.getCommand().length() == 0)
4066     {
4067         errorMsg.clear();
4068         noErr=true;
4069         return returnValue;
4070     }
4071
4072     // Check set of parameters
4073     if (parser.errorLevel()==NoError && parser.checkParameters(selti) )
4074     {
4075         QString com=parser.getCommand();
4076     // - MS VS2013 compiler has nested if limit - work around that with
4077     //   do{}while(0); and breaks
4078     do {
4079         /////////////////////////////////////////////////////////////////////
4080         if (com=="addBranch")  
4081         {
4082             if (parser.parCount()==0)
4083                 addNewBranch ();
4084             else
4085                 addNewBranch ( selbi,parser.parInt (ok,0) );
4086         break;
4087     }
4088         /////////////////////////////////////////////////////////////////////
4089     if (com=="addBranchBefore")
4090         {
4091             addNewBranchBefore ();
4092         break;
4093     }
4094         /////////////////////////////////////////////////////////////////////
4095     if (com==QString("addMapCenter"))
4096         {
4097             x=parser.parDouble (ok,0);
4098             y=parser.parDouble (ok,1);
4099         break;
4100     }
4101         /////////////////////////////////////////////////////////////////////
4102     if (com==QString("addMapInsert"))
4103         {
4104             t=parser.parString (ok,0);  // path to map
4105             int contentFilter=0x0000;
4106
4107             int pc=parser.parCount();
4108             int pos=-1; 
4109             // Get position
4110             if (pc>1)
4111             {
4112                 pos=parser.parInt(ok,1);            // position
4113                 if (!ok)
4114                     parser.setError (Aborted,"Couldn't read position");
4115             }
4116
4117             // Get contentFilter (to filter e.g. slides)
4118             if (pc>2)
4119             {
4120                 contentFilter=parser.parInt (ok,2);
4121                 if (!ok)
4122                     parser.setError (Aborted,"Couldn't read content Filter");
4123             }
4124             
4125             if (parser.errorLevel() == NoError)
4126             {
4127                 if (QDir::isRelativePath(t)) 
4128                     t=(QDir::currentPath() + "/"+t);
4129                 saveStateBeforeLoad (ImportAdd, t);
4130                 if (File::Aborted==loadMap (t,ImportAdd,VymMap,contentFilter,pos) )
4131                     parser.setError (Aborted,QString("Couldn't load %1").arg(t) );
4132             }   
4133         break;
4134     }
4135         /////////////////////////////////////////////////////////////////////
4136     if (com==QString("addMapReplace"))
4137         {
4138             t=parser.parString (ok,0);  // path to map
4139             if (QDir::isRelativePath(t)) 
4140                 t=(QDir::currentPath() + "/"+t);
4141             saveStateBeforeLoad (ImportReplace, t);
4142             if (File::Aborted==loadMap (t,ImportReplace,VymMap) )
4143                 parser.setError (Aborted,QString("Couldn't load %1").arg(t) );
4144         break;
4145     }
4146         /////////////////////////////////////////////////////////////////////
4147     if (com==QString("addSlide"))
4148         {
4149             addSlide();
4150         break;
4151     }
4152         /////////////////////////////////////////////////////////////////////
4153     if (com==QString("addXLink"))
4154         {
4155             s=parser.parString (ok,0);  // begin
4156             t=parser.parString (ok,1);  // end
4157             BranchItem *begin=(BranchItem*)findBySelectString(s);
4158             BranchItem *end=(BranchItem*)findBySelectString(t);
4159             if (begin && end)
4160             {
4161                 if (begin->isBranchLikeType() && end->isBranchLikeType())
4162                 {
4163                     Link *li=new Link (this);
4164                     li->setBeginBranch ( (BranchItem*)begin );
4165                     li->setEndBranch ( (BranchItem*)end);
4166
4167                     createLink (li);
4168                     QPen pen=li->getPen();
4169                     if (parser.parCount()>2)
4170                     {
4171                         int w=parser.parInt (ok,2); 
4172                         if (ok) pen.setWidth(w);
4173                     }
4174                     if (parser.parCount()>3)
4175                     {
4176                         QColor col=parser.parColor (ok,3);
4177                         if (ok) pen.setColor (col);
4178                     }
4179                     if (parser.parCount()>4)
4180                     {
4181                         QString st0=parser.parString (ok,4);
4182                         if (ok)
4183                         {
4184                             Qt::PenStyle st1=penStyle (st0,ok);
4185                             if (ok) 
4186                                 pen.setStyle (st1);
4187                             else        
4188                                 parser.setError (Aborted, "Couldn't read penstyle");
4189                         }
4190                     }
4191                     if (ok) li->setPen(pen);    
4192                 }
4193                 else
4194                     parser.setError (Aborted,"begin or end of xLink are not branch or mapcenter");
4195                 
4196             } else
4197                 parser.setError (Aborted,"Couldn't find begin or end of xLink");
4198         break;
4199     }
4200         /////////////////////////////////////////////////////////////////////
4201     if (com=="branchCount")
4202         { 
4203             returnValue=selti->branchCount();
4204         break;
4205     }
4206         /////////////////////////////////////////////////////////////////////
4207     if (com=="centerCount")
4208         { 
4209             returnValue=rootItem->branchCount();
4210         break;
4211     }
4212         /////////////////////////////////////////////////////////////////////
4213     if (com=="centerOnID")
4214         {
4215             s=parser.parString(ok,0);
4216             TreeItem *ti=findUuid(QUuid(s));
4217             if (ti)
4218             {
4219                 LinkableMapObj *lmo=((MapItem*)ti)->getLMO();
4220                 if (zoomFactor>0 && lmo)
4221                     mapEditor->setViewCenterTarget (
4222                         lmo->getBBox().center(),
4223                         zoomFactor,
4224                         rotationAngle,
4225                         animDuration,
4226                         animCurve);
4227                 else
4228                     qWarning()<<"VymModel::centerOnID failed!";
4229             } else
4230                 parser.setError(Aborted,QString("Could not find ID: \"%1\"").arg(s));
4231         break;
4232     }
4233         /////////////////////////////////////////////////////////////////////
4234     if (com=="clearFlags")
4235     {
4236         selbi->deactivateAllStandardFlags();
4237         reposition();
4238         emitDataChanged(selbi);
4239         setChanged();
4240         break;
4241     }
4242         /////////////////////////////////////////////////////////////////////
4243     if (com=="colorBranch")
4244     {
4245         QColor c=parser.parColor (ok,0);
4246         colorBranch (c);
4247         break;
4248     }
4249         /////////////////////////////////////////////////////////////////////
4250     if (com=="colorSubtree")
4251     {
4252         QColor c=parser.parColor (ok,0);
4253         colorSubtree (c);
4254         break;
4255     }
4256         /////////////////////////////////////////////////////////////////////
4257     if (com=="copy")
4258         {
4259             copy();
4260         break;
4261     }
4262         /////////////////////////////////////////////////////////////////////
4263     if (com=="cut")
4264         {
4265                 cut();
4266         break;
4267     }
4268         /////////////////////////////////////////////////////////////////////
4269     if (com=="cycleTask")
4270         {
4271             ok=true;
4272             if (parser.parCount()==0) b=false;
4273             if (parser.parCount()==1) b=parser.parBool(ok,0);
4274             if (ok) cycleTaskStatus (b);
4275         break;
4276     }
4277         /////////////////////////////////////////////////////////////////////
4278     if (com=="delete")
4279         {
4280             deleteSelection();
4281         break;
4282     }
4283         /////////////////////////////////////////////////////////////////////
4284     if (com=="deleteKeepChildren")
4285         {
4286             deleteKeepChildren();
4287         break;
4288     }
4289         /////////////////////////////////////////////////////////////////////
4290     if (com=="deleteChildren")
4291         {
4292             deleteChildren();
4293         break;
4294     }
4295         /////////////////////////////////////////////////////////////////////
4296     if (com=="deleteSlide")
4297         {
4298             n = parser.parInt (ok,0);
4299             if (!ok || n < 0 || n >= slideModel->count() - 1)
4300                 parser.setError (Aborted,"Index out of range");
4301             else    
4302                 deleteSlide(n);
4303         break;
4304     }
4305         /////////////////////////////////////////////////////////////////////
4306     if (com=="exportAO")
4307         {
4308             QString fname=parser.parString(ok,0); 
4309             exportAO (fname,false);
4310         break;
4311     }
4312         /////////////////////////////////////////////////////////////////////
4313     if (com=="exportASCII")
4314         {
4315        QString fname  = parser.parString(ok, 0);
4316        bool listTasks = parser.parBool(ok, 1);
4317        exportASCII (listTasks, fname, false);
4318        break;
4319     }
4320         /////////////////////////////////////////////////////////////////////
4321     if (com=="exportCSV")
4322         {
4323            QString fname=parser.parString(ok,0); 
4324            exportCSV (fname,false);
4325        break;
4326     }
4327         /////////////////////////////////////////////////////////////////////
4328     if (com=="exportHTML")
4329         {
4330             QString path=parser.parString(ok,0); 
4331             QString fname=parser.parString(ok,1); 
4332             exportHTML (path,fname,false);
4333         break;
4334     }
4335         /////////////////////////////////////////////////////////////////////
4336     if (com=="exportImage")
4337         {
4338             QString fname=parser.parString(ok,0); 
4339             QString format="PNG";
4340             if (parser.parCount()>=2)
4341                 format=parser.parString(ok,1);
4342             exportImage (fname,false,format);
4343         break;
4344     }
4345         /////////////////////////////////////////////////////////////////////
4346     if (com=="exportImpress")
4347         {
4348             QString fn=parser.parString(ok,0); 
4349             QString cf=parser.parString(ok,1); 
4350             exportImpress (fn,cf);
4351         break;
4352     }
4353         /////////////////////////////////////////////////////////////////////
4354     if (com=="exportLast")
4355         {
4356             exportLast ();
4357         break;
4358     }
4359         /////////////////////////////////////////////////////////////////////
4360     if (com=="exportLaTeX")
4361         {
4362             QString fname=parser.parString(ok,0); 
4363             exportLaTeX (fname,false);
4364         break;
4365     }
4366         /////////////////////////////////////////////////////////////////////
4367     if (com=="exportOrgMode")
4368         {
4369             QString fname=parser.parString(ok,0); 
4370             exportOrgMode (fname,false);
4371         break;
4372     }
4373         /////////////////////////////////////////////////////////////////////
4374     if (com=="exportPDF")
4375         {
4376             QString fname=parser.parString(ok,0); 
4377             exportPDF(fname,false);
4378         break;
4379     }
4380         /////////////////////////////////////////////////////////////////////
4381     if (com=="exportSVG")
4382         {
4383             QString fname=parser.parString(ok,0); 
4384             exportSVG(fname,false);
4385         break;
4386     }
4387         /////////////////////////////////////////////////////////////////////
4388     if (com=="exportXML")
4389         {
4390             QString dpath=parser.parString(ok,0); 
4391             QString fpath=parser.parString(ok,1); 
4392             exportXML (dpath,fpath,false);
4393         break;
4394     }
4395         /////////////////////////////////////////////////////////////////////
4396     if (com=="getDestPath")
4397         { 
4398             returnValue=getDestPath();
4399         break;
4400     }
4401         /////////////////////////////////////////////////////////////////////
4402     if (com=="getFileDir")
4403         { 
4404             returnValue=getFileDir();
4405         break;
4406     }
4407         /////////////////////////////////////////////////////////////////////
4408     if (com=="getFrameType")
4409         { 
4410             BranchObj *bo=(BranchObj*)(selbi->getLMO());
4411             if (!bo)
4412                 parser.setError (Aborted,"No BranchObj");
4413             else
4414                 returnValue=bo->getFrame()->getFrameTypeName();
4415         break;
4416     }
4417         /////////////////////////////////////////////////////////////////////
4418     if (com=="getHeadingPlainText")
4419         { 
4420             returnValue = getHeading().getTextASCII();
4421             break;
4422     }
4423         /////////////////////////////////////////////////////////////////////
4424     if (com=="getHeadingXML")
4425         { 
4426             returnValue = getHeading().saveToDir();
4427             break;
4428     }
4429         /////////////////////////////////////////////////////////////////////
4430     if (com=="getMapAuthor")
4431         { 
4432             returnValue=author;
4433         break;
4434     }
4435         /////////////////////////////////////////////////////////////////////
4436     if (com=="getMapComment")
4437         { 
4438             returnValue=comment;
4439         break;
4440     }
4441         /////////////////////////////////////////////////////////////////////
4442     if (com=="getMapTitle")
4443         { 
4444             returnValue=title;
4445         break;
4446     }
4447         /////////////////////////////////////////////////////////////////////
4448     if (com=="getNotePlainText")
4449         { 
4450             returnValue= getNote().getTextASCII();
4451             break;
4452     }
4453         /////////////////////////////////////////////////////////////////////
4454     if (com=="getNoteXML")
4455         { 
4456             returnValue= getNote().saveToDir();
4457             break;
4458     }
4459         /////////////////////////////////////////////////////////////////////
4460     if (com=="getSelectString")
4461         { 
4462             returnValue=getSelectString();
4463         break;
4464     }
4465         /////////////////////////////////////////////////////////////////////
4466     if (com=="getTaskSleepDays")
4467         { 
4468             Task *task=selbi->getTask();
4469             if (task)
4470                 returnValue=task->getDaysSleep();
4471             else
4472                 parser.setError (Aborted,"Branch has no task set");
4473             break;
4474     }
4475         /////////////////////////////////////////////////////////////////////
4476     if (com=="getURL")
4477         { 
4478             returnValue=selti->getURL();
4479         break;
4480     }
4481         /////////////////////////////////////////////////////////////////////
4482     if (com=="getVymLink")
4483         { 
4484             returnValue=selti->getVymLink();
4485         break;
4486     }
4487         /////////////////////////////////////////////////////////////////////
4488     if (com=="getXLinkColor")
4489         { 
4490             returnValue=getXLinkColor().name();
4491         break;
4492     }
4493         /////////////////////////////////////////////////////////////////////
4494     if (com=="getXLinkWidth")
4495         { 
4496             returnValue=getXLinkWidth();
4497         break;
4498     }
4499         /////////////////////////////////////////////////////////////////////
4500     if (com=="getXLinkPenStyle")
4501         { 
4502             returnValue=penStyleToString( getXLinkPenStyle() );
4503         break;
4504     }
4505         /////////////////////////////////////////////////////////////////////
4506     if (com=="getXLinkStyleBegin")
4507         { 
4508             returnValue = getXLinkStyleBegin();
4509         break;
4510     }
4511         /////////////////////////////////////////////////////////////////////
4512     if (com=="getXLinkStyleEnd")
4513         { 
4514             returnValue = getXLinkStyleEnd();
4515         break;
4516     }
4517         /////////////////////////////////////////////////////////////////////
4518     if (com=="hasActiveFlag")
4519         { 
4520             s=parser.parString(ok,0);
4521             returnValue=selti->hasActiveStandardFlag(s);
4522         break;
4523     }
4524         /////////////////////////////////////////////////////////////////////
4525     if (com=="hasNote")
4526         {
4527             returnValue = !getNote().isEmpty();
4528         break;
4529     }
4530         /////////////////////////////////////////////////////////////////////
4531     if (com=="hasRichTextNote")
4532         {
4533             returnValue=hasRichTextNote();
4534         break;
4535     }
4536         /////////////////////////////////////////////////////////////////////
4537     if (com=="hasTask")
4538         { 
4539             if (selbi && selbi->getTask() )
4540                 returnValue=true;
4541             else
4542                 returnValue=false;
4543             break;
4544     }
4545         /////////////////////////////////////////////////////////////////////
4546     if (com=="importDir")
4547         {
4548             s=parser.parString(ok,0);
4549             importDirInt(s);
4550         break;
4551     }
4552         /////////////////////////////////////////////////////////////////////
4553     if (com=="isScrolled")
4554         {
4555             returnValue=selbi->isScrolled();
4556         break;
4557     }
4558         /////////////////////////////////////////////////////////////////////
4559     if (com=="loadImage")
4560         {
4561             s=parser.parString(ok,0);
4562             loadImage (selbi,s);
4563         break;
4564     }
4565         /////////////////////////////////////////////////////////////////////
4566     if (com=="loadNote")
4567         {
4568             s=parser.parString(ok,0);
4569             loadNote (s);
4570         break;
4571     }
4572         /////////////////////////////////////////////////////////////////////
4573     if (com=="moveDown")
4574         {
4575             moveDown();
4576         break;
4577     }
4578         /////////////////////////////////////////////////////////////////////
4579     if (com=="moveUp")
4580         {
4581             moveUp();
4582         break;
4583     }
4584         /////////////////////////////////////////////////////////////////////
4585     if (com=="moveSlideUp")
4586         {
4587             n = parser.parInt (ok, 0);
4588             if (!ok || n < 0 || n >= slideModel->count() - 1)
4589                 parser.setError (Aborted,"Index out of range");
4590             else    
4591                 moveSlideUp(n);
4592         break;
4593     }
4594         /////////////////////////////////////////////////////////////////////
4595     if (com=="moveSlideDown")
4596         {
4597             n = parser.parInt (ok, 0);
4598             if (!ok || n < 0 || n >= slideModel->count() - 1)
4599                 parser.setError (Aborted,"Index out of range");
4600             else    
4601                 moveSlideDown(n);
4602         break;
4603     }
4604         /////////////////////////////////////////////////////////////////////
4605     if (com=="move")
4606         {
4607             x=parser.parDouble (ok,0);
4608             y=parser.parDouble (ok,1);
4609             move (x,y);
4610         break;
4611     }
4612         /////////////////////////////////////////////////////////////////////
4613     if (com=="moveRel")
4614         {
4615             x=parser.parDouble (ok,0);
4616             y=parser.parDouble (ok,1);
4617             moveRel (x,y);
4618         break;
4619     }
4620         /////////////////////////////////////////////////////////////////////
4621     if (com=="nop")
4622         {
4623         break;
4624     }
4625         /////////////////////////////////////////////////////////////////////
4626     if (com=="note2URLs")
4627         {
4628             note2URLs();
4629         break;
4630     }
4631         /////////////////////////////////////////////////////////////////////
4632     if (com=="parseVymText")
4633         {
4634             s = parser.parString(ok,0);
4635             parseVymText( s );
4636         break;
4637     }
4638         /////////////////////////////////////////////////////////////////////
4639     if (com=="paste")
4640         {
4641             paste();
4642         break;
4643     }
4644         /////////////////////////////////////////////////////////////////////
4645     if (com=="redo")
4646         {
4647             redo();
4648         break;
4649     }
4650         /////////////////////////////////////////////////////////////////////
4651     if (com=="relinkTo")
4652         {
4653             if (!selti)
4654             {
4655                 parser.setError (Aborted,"Nothing selected");
4656             } else if ( selbi)
4657             {
4658                 if (parser.checkParCount(4))
4659                 {
4660                     // 0        selectstring of parent
4661                     // 1        num in parent (for branches)
4662                     // 2,3      x,y of mainbranch or mapcenter (for images)
4663                     s=parser.parString(ok,0);
4664                     TreeItem *dst=findBySelectString (s);
4665                     if (dst)
4666                     {   
4667                         if (dst->getType()==TreeItem::Branch ) 
4668                         {
4669                             // Get number in parent
4670                             n=parser.parInt (ok,1);
4671                             if (ok)
4672                             {
4673                                 if (relinkBranch (selbi,(BranchItem*)dst,n,true))
4674                                     emitSelectionChanged();
4675                                 else
4676                                     parser.setError(Aborted,"Relinking failed");
4677                             }   
4678                         } else if (dst->getType()==TreeItem::MapCenter) 
4679                         {
4680                             if (relinkBranch (selbi,(BranchItem*)dst,-1,true))
4681                             {
4682                                 // Get coordinates of mainbranch
4683                                 x=parser.parDouble(ok,2);
4684                                 if (ok)
4685                                 {
4686                                     y=parser.parDouble(ok,3);
4687                                     if (ok) 
4688                                     {
4689                                         if (selbi->getLMO()) 
4690                                         {
4691                                             ((BranchObj*)selbi->getLMO())->move (x,y);
4692                                             ((BranchObj*)selbi->getLMO())->setRelPos();
4693                                         }
4694                                     }
4695                                 }
4696                                 reposition();
4697                                 emitSelectionChanged();
4698                             } else
4699                                 parser.setError(Aborted,"Relinking failed");
4700                         }       
4701                     } else
4702                         parser.setError (Aborted,"Couldn't find destination branch");
4703                 }       
4704             } else if ( selti->getType() == TreeItem::Image) 
4705             {
4706                 if (parser.checkParCount(1))
4707                 {
4708                     // 0        selectstring of parent
4709                     s=parser.parString(ok,0);
4710                     TreeItem *dst=findBySelectString (s);
4711                     if (dst)
4712                     {   
4713                         if (dst->isBranchLikeType())
4714                             if (!relinkImage ( ((ImageItem*)selti),(BranchItem*)dst))
4715                                 parser.setError(Aborted,"Relinking failed");
4716
4717                     } else      
4718                         parser.setError (Aborted,"Destination is not a branch");
4719                 }           
4720             } else
4721                 parser.setError (Aborted,"Type of selection is not a floatimage or branch");
4722         break;
4723     }
4724         /////////////////////////////////////////////////////////////////////
4725     if (com=="saveImage")
4726         {
4727             ImageItem *ii=getSelectedImage();
4728             s=parser.parString(ok,0);
4729             t=parser.parString(ok,1);
4730             saveImage (ii,t,s);
4731         break;
4732     }
4733         /////////////////////////////////////////////////////////////////////
4734     if (com=="saveNote")
4735         {
4736             s=parser.parString(ok,0);
4737             saveNote (s);
4738         break;
4739     }
4740         /////////////////////////////////////////////////////////////////////
4741     if (com=="scroll")
4742         {
4743             if (!scrollBranch (selbi))  
4744                 parser.setError (Aborted,"Could not scroll branch");
4745         break;
4746     }
4747         /////////////////////////////////////////////////////////////////////
4748     if (com=="select")
4749         {
4750             s=parser.parString(ok,0);
4751             if (!select (s))
4752                 parser.setError(Aborted,QString("Could not select \"%1\"").arg(s));
4753         break;
4754     }
4755         /////////////////////////////////////////////////////////////////////
4756     if (com=="selectID")
4757         {
4758             s=parser.parString(ok,0);
4759             if (!selectID (s))
4760                 parser.setError(Aborted,QString("Could not select ID: \"%1\"").arg(s));
4761         break;
4762     }
4763         /////////////////////////////////////////////////////////////////////
4764     if (com=="selectLastBranch")
4765         {
4766             BranchItem *bi=selbi->getLastBranch();
4767             if (!bi)
4768                 parser.setError (Aborted,"Could not select last branch");
4769             select (bi);
4770         break;
4771     }
4772         /////////////////////////////////////////////////////////////////////
4773     if (com=="selectLastImage")
4774         {
4775             ImageItem *ii=selbi->getLastImage();
4776             if (!ii)
4777                 parser.setError (Aborted,"Could not select last image");
4778             select (ii);
4779         break;
4780     }
4781         /////////////////////////////////////////////////////////////////////
4782     if (com=="selectParent")
4783         {
4784             selectParent ();
4785         break;
4786     }
4787         /////////////////////////////////////////////////////////////////////
4788     if (com=="selectLatestAdded")
4789         {
4790             if (!latestAddedItem)
4791             {
4792                 parser.setError (Aborted,"No latest added object");
4793             } else
4794             {   
4795                 if (!select (latestAddedItem))
4796                     parser.setError (Aborted,"Could not select latest added object ");
4797             }   
4798         break;
4799     }
4800         /////////////////////////////////////////////////////////////////////
4801     if (com=="setFlag")
4802         {
4803             s=parser.parString(ok,0);
4804             selbi->activateStandardFlag(s);
4805         break;
4806     }
4807         /////////////////////////////////////////////////////////////////////
4808     if (com=="setTaskSleep")
4809         {
4810             s=parser.parString(ok,0);
4811             returnValue=setTaskSleep (s);
4812         break;
4813     }
4814         /////////////////////////////////////////////////////////////////////
4815     if (com=="setFrameIncludeChildren")
4816         {
4817             b=parser.parBool(ok,0);
4818             setFrameIncludeChildren(b);
4819         break;
4820     }
4821         /////////////////////////////////////////////////////////////////////
4822     if (com=="setFrameType")
4823         {
4824             s=parser.parString(ok,0);
4825             setFrameType (s);
4826         break;
4827     }
4828         /////////////////////////////////////////////////////////////////////
4829     if (com=="setFramePenColor")
4830         {
4831             QColor c=parser.parColor(ok,0);
4832             setFramePenColor (c);
4833         break;
4834     }
4835         /////////////////////////////////////////////////////////////////////
4836     if (com=="setFrameBrushColor")
4837         {
4838             QColor c=parser.parColor(ok,0);
4839             setFrameBrushColor (c);
4840         break;
4841     }
4842         /////////////////////////////////////////////////////////////////////
4843     if (com=="setFramePadding")
4844         {
4845             n=parser.parInt(ok,0);
4846             setFramePadding(n);
4847         break;
4848     }
4849         /////////////////////////////////////////////////////////////////////
4850     if (com=="setFrameBorderWidth")
4851         {
4852             n=parser.parInt(ok,0);
4853             setFrameBorderWidth (n);
4854         break;
4855     }
4856         /////////////////////////////////////////////////////////////////////
4857     if (com=="setHeadingPlainText")
4858         {
4859             s=parser.parString (ok,0);
4860             setHeadingPlainText (s); // FIXME-3  what about RT? Nothing implemented.
4861         break;
4862     }
4863         /////////////////////////////////////////////////////////////////////
4864     if (com=="setHideExport")
4865         {
4866             b=parser.parBool(ok,0);
4867             setHideExport (b);
4868         break;
4869     }
4870         /////////////////////////////////////////////////////////////////////
4871     if (com=="setIncludeImagesHorizontally")
4872         { 
4873             b=parser.parBool(ok,0);
4874             setIncludeImagesHor(b);
4875         break;
4876     }
4877         /////////////////////////////////////////////////////////////////////
4878     if (com=="setIncludeImagesVertically")
4879         {
4880             b=parser.parBool(ok,0);
4881             if (ok) setIncludeImagesVer(b);
4882         break;
4883     }
4884         /////////////////////////////////////////////////////////////////////
4885     if (com=="setHideLinkUnselected")
4886         {
4887             b=parser.parBool(ok,0);
4888             setHideLinkUnselected(b);
4889         break;
4890     }
4891         /////////////////////////////////////////////////////////////////////
4892     if (com=="setMapAnimCurve")
4893         {
4894             n=parser.parInt(ok,0);
4895             if (n<0 || n>QEasingCurve::OutInBounce)
4896                 parser.setError (Aborted,"Unknown link style");
4897             else
4898             {
4899                 QEasingCurve c;
4900                 c.setType ( (QEasingCurve::Type) n);
4901                 setMapAnimCurve(c);
4902             }
4903         break;
4904     }
4905         /////////////////////////////////////////////////////////////////////
4906     if (com=="setMapAnimDuration")
4907         {
4908             n=parser.parInt(ok,0);
4909             setMapAnimDuration(n);
4910         break;
4911     }
4912         /////////////////////////////////////////////////////////////////////
4913     if (com=="setMapAuthor")
4914         {
4915             s=parser.parString(ok,0);
4916             setAuthor (s);
4917         break;
4918     }
4919         /////////////////////////////////////////////////////////////////////
4920     if (com=="setMapComment")
4921         {
4922             s=parser.parString(ok,0);
4923             if (ok) setComment(s);
4924         break;
4925     }
4926     if (com=="setMapTitle")
4927         {
4928             s=parser.parString(ok,0);
4929             if (ok) setTitle(s);
4930         break;
4931     }
4932         /////////////////////////////////////////////////////////////////////
4933     if (com=="setMapBackgroundColor")
4934         {
4935             QColor c=parser.parColor (ok,0);
4936             setMapBackgroundColor (c);
4937         break;
4938     }
4939         /////////////////////////////////////////////////////////////////////
4940     if (com=="setMapDefLinkColor")
4941         {
4942             QColor c=parser.parColor (ok,0);
4943             setMapDefLinkColor (c);
4944         break;
4945     }
4946         /////////////////////////////////////////////////////////////////////
4947     if (com=="setMapLinkStyle")
4948         {
4949             s=parser.parString (ok,0);
4950             if (!setMapLinkStyle(s) )
4951                 parser.setError (Aborted,"Unknown link style");
4952         break;
4953     }
4954         /////////////////////////////////////////////////////////////////////
4955     if (com=="setMapRotation")
4956         {
4957             x=parser.parDouble (ok,0);
4958             setMapRotationAngle(x);
4959             mapEditor->setAngleTarget(x);
4960         break;
4961     }
4962         /////////////////////////////////////////////////////////////////////
4963     if (com=="setMapZoom")
4964         {
4965             x=parser.parDouble (ok,0);
4966             setMapZoomFactor(x);
4967             mapEditor->setZoomFactorTarget(x);
4968         break;
4969     }
4970         /////////////////////////////////////////////////////////////////////
4971     if (com=="setNotePlainText")
4972         {
4973             s=parser.parString (ok,0);
4974             VymNote vn;
4975             vn.setPlainText(s);
4976             setNote (vn);
4977         break;
4978     }
4979         /////////////////////////////////////////////////////////////////////
4980     if (com=="setScale")
4981         {
4982             x=parser.parDouble (ok,0);
4983             y=parser.parDouble (ok,1);
4984             setScale (x,y);
4985         break;
4986     }
4987         /////////////////////////////////////////////////////////////////////
4988     if (com=="setSelectionColor")
4989         {
4990             QColor c=parser.parColor (ok,0);
4991             setSelectionColorInt (c);
4992         break;
4993     }
4994         /////////////////////////////////////////////////////////////////////
4995     if (com=="setURL")
4996         {
4997             s=parser.parString (ok,0);
4998             setURL(s);
4999         break;
5000     }
5001         /////////////////////////////////////////////////////////////////////
5002     if (com=="setVymLink")
5003         {
5004             s=parser.parString (ok,0);
5005             setVymLink(s);
5006         break;
5007     }
5008         /////////////////////////////////////////////////////////////////////
5009     if (com=="setXLinkColor")
5010         {
5011             s=parser.parString (ok,0);
5012             setXLinkColor(s);     
5013         break;
5014     }
5015         /////////////////////////////////////////////////////////////////////
5016     if (com=="setXLinkLineStyle")
5017         {
5018             s=parser.parString (ok,0);
5019             setXLinkLineStyle(s);     
5020         break;
5021     }
5022         /////////////////////////////////////////////////////////////////////
5023     if (com=="setXLinkStyleBegin")
5024         {
5025             s=parser.parString (ok,0);
5026             setXLinkStyleBegin(s);     
5027         break;
5028     }
5029         /////////////////////////////////////////////////////////////////////
5030     if (com=="setXLinkStyleEnd")
5031         {
5032             s=parser.parString (ok,0);
5033             setXLinkStyleEnd(s);     
5034         break;
5035     }
5036         /////////////////////////////////////////////////////////////////////
5037     if (com=="setXLinkWidth")
5038         {
5039             n=parser.parInt (ok,0);
5040             setXLinkWidth(n);
5041         break;
5042     }
5043         /////////////////////////////////////////////////////////////////////
5044     if (com=="sleep")
5045         {
5046             n=parser.parInt (ok,0);
5047             sleep (n);
5048         break;
5049     }
5050         /////////////////////////////////////////////////////////////////////
5051     if (com=="sortChildren")
5052         {
5053             b=false;
5054             if (parser.parCount()==1)
5055                 b=parser.parBool(ok,0);
5056             sortChildren(b);
5057         break;
5058     }
5059         /////////////////////////////////////////////////////////////////////
5060     if (com=="toggleFlag")
5061         {
5062             s=parser.parString(ok,0);
5063             toggleStandardFlag (s);
5064         break;
5065     }
5066         /////////////////////////////////////////////////////////////////////
5067     if (com=="toggleFrameIncludeChildren")
5068         {
5069             toggleFrameIncludeChildren();
5070         break;
5071     }
5072         /////////////////////////////////////////////////////////////////////
5073     if (com=="toggleScroll")
5074         {
5075             toggleScroll();     
5076         break;
5077     }
5078         /////////////////////////////////////////////////////////////////////
5079     if (com=="toggleTarget")
5080         {
5081             toggleTarget();     
5082         break;
5083     }
5084         /////////////////////////////////////////////////////////////////////
5085     if (com=="toggleTask")
5086         {
5087             toggleTask();       
5088         break;
5089     }
5090         /////////////////////////////////////////////////////////////////////
5091     if (com=="undo")
5092         {
5093             undo();
5094         break;
5095     }
5096         /////////////////////////////////////////////////////////////////////
5097     if (com=="unscroll")
5098         {
5099             if (!unscrollBranch (selbi))    
5100                 parser.setError (Aborted,"Could not unscroll branch");
5101         break;
5102     }
5103         /////////////////////////////////////////////////////////////////////
5104     if (com=="unscrollChildren")
5105         {
5106             unscrollChildren ();
5107         break;
5108     }
5109         /////////////////////////////////////////////////////////////////////
5110     if (com=="unselectAll")
5111         {
5112             unselectAll();
5113         break;
5114     }
5115         /////////////////////////////////////////////////////////////////////
5116     if (com=="unsetFlag")
5117         {
5118             s=parser.parString(ok,0);
5119             selbi->deactivateStandardFlag(s);
5120         break;
5121     }
5122         /////////////////////////////////////////////////////////////////////
5123     //else must be an Unknown command
5124             parser.setError (Aborted,"Unknown command");
5125     break;
5126
5127     } while(0); // End of do{}while(0); MS VS2013 nested if bug workaround
5128
5129     } // end check set of parameters if
5130     // Any errors?
5131     if (parser.errorLevel()==NoError)
5132     {
5133         reposition();
5134         errorMsg.clear();
5135         noErr=true;
5136     }
5137     else
5138     {
5139         // TODO Error handling
5140         noErr=false;
5141         errorMsg=parser.errorMessage();
5142         returnValue=errorMsg;
5143     }
5144     return returnValue;
5145 }
5146
5147 QVariant VymModel::execute (const QString &script)
5148 {
5149     parser.setScript (script);
5150     parser.execute ();
5151     QVariant r;
5152     bool noErr=true;
5153     QString errMsg;
5154     while (parser.next() && noErr) 
5155     {
5156         r=parseAtom(parser.getAtom(),noErr,errMsg);
5157         if (!noErr)
5158         {
5159             if (!options.isOn("batch") && !testmode )
5160                 QMessageBox::warning(0,tr("Warning"),tr("Script aborted:\n%1").arg(errMsg));
5161             qWarning()<< QString("VM::execute aborted: "+errMsg + "\n" + script);
5162         }
5163     }
5164     return r;
5165 }
5166
5167 void VymModel::setExportMode (bool b)
5168 {
5169     // should be called before and after exports
5170     // depending on the settings
5171     if (b && settings.value("/export/useHideExport","true")=="true")
5172         setHideTmpMode (TreeItem::HideExport);
5173     else    
5174         setHideTmpMode (TreeItem::HideNone);
5175 }
5176
5177 QPointF VymModel::exportImage(QString fname, bool askName, QString format)  
5178 {
5179     QPointF offset; // set later, when getting image from MapEditor
5180
5181     if (fname=="")
5182     {
5183         if (!askName) 
5184         {
5185             qWarning("VymModel::exportImage called without filename (and askName==false)");
5186             return offset;
5187         }
5188
5189         fname=lastImageDir.absolutePath() + "/" + getMapName()+".png";
5190         format="PNG";
5191     }   
5192
5193     ExportBase ex;
5194     ex.setName( "Image" );
5195     ex.setModel( this );
5196     ex.setFilePath( fname );
5197     ex.setWindowTitle ( tr("Export map as image") );
5198     ex.addFilter( "PNG (*.png);;All (* *.*)" ); //  imageIO.getFilters().join(";;")
5199     ex.setLastCommand( settings.localValue(filePath,"/export/last/command","").toString() );
5200
5201     if (askName)
5202     {
5203         if (! ex.execDialog() ) return offset;
5204         fname = ex.getFilePath();
5205         lastImageDir=dirname(fname);
5206     }
5207
5208     setExportMode (true);
5209
5210     QImage img (mapEditor->getImage(offset));
5211     if (!img.save(fname, format.toLocal8Bit()))
5212         QMessageBox::critical (0,tr("Critical Error"),tr("Couldn't save QImage %1 in format %2").arg(fname).arg(format));
5213     setExportMode (false);
5214
5215     ex.completeExport();
5216
5217     return offset;
5218 }
5219
5220 void VymModel::exportPDF (QString fname, bool askName)
5221 {
5222     if (fname == "")
5223     {
5224         if (!askName) 
5225         {
5226             qWarning("VymModel::exportPDF called without filename (and askName==false)");
5227             return;
5228         }
5229
5230         fname = lastExportDir.absolutePath() + "/" + getMapName()+".pdf";
5231     }   
5232
5233     ExportBase ex;
5234     ex.setName( "PDF" );
5235     ex.setModel( this );
5236     ex.setFilePath( fname );
5237     ex.setWindowTitle ( tr("Export map as PDF") );
5238     ex.addFilter( "PDF (*.pdf);;All (* *.*)" );
5239     ex.setLastCommand( settings.localValue(filePath,"/export/last/command","").toString() );
5240
5241     if (askName)
5242     {
5243         if (! ex.execDialog() ) return;
5244         fname = ex.getFilePath();
5245     }
5246
5247     setExportMode (true);
5248
5249     // To PDF
5250     QPrinter printer(QPrinter::HighResolution);
5251     printer.setOutputFormat(QPrinter::PdfFormat);
5252     printer.setOutputFileName(fname);
5253     printer.setPageSize(QPrinter::A3);
5254
5255     QRectF bbox=mapEditor->getTotalBBox();
5256     if (bbox.width()>bbox.height())
5257         // recommend landscape
5258         printer.setOrientation (QPrinter::Landscape);
5259     else    
5260         // recommend portrait
5261         printer.setOrientation (QPrinter::Portrait);
5262
5263     QPainter *pdfPainter = new QPainter(&printer);
5264     getScene()->render(pdfPainter);
5265     pdfPainter->end();
5266     delete pdfPainter;
5267
5268     setExportMode (false);
5269
5270     ex.completeExport();
5271 }
5272
5273 QPointF VymModel::exportSVG (QString fname, bool askName) 
5274 {
5275     QPointF offset; // FIXME-3 not needed?
5276
5277     if (fname=="")
5278     {
5279         if (!askName) 
5280         {
5281             qWarning("VymModel::exportSVG called without filename (and askName==false)");
5282             return offset;
5283         }
5284
5285         fname=lastImageDir.absolutePath() + "/" + getMapName()+".png";
5286     }   
5287
5288     ExportBase ex;
5289     ex.setName( "SVG" );
5290     ex.setModel( this );
5291     ex.setFilePath( fname );
5292     ex.setWindowTitle ( tr("Export map as SVG") );
5293     ex.addFilter( "SVG (*.svg);;All (* *.*)" );
5294     ex.setLastCommand( settings.localValue(filePath,"/export/last/command","").toString() );
5295
5296     if (askName)
5297     {
5298         if (! ex.execDialog() ) return offset;
5299         fname = ex.getFilePath();
5300         lastImageDir = dirname(fname);
5301     }
5302
5303     setExportMode (true);
5304
5305     QSvgGenerator generator;
5306     generator.setFileName(fname);
5307     QSize sceneSize = getScene()->sceneRect().size().toSize();
5308     generator.setSize(sceneSize);
5309     generator.setViewBox(QRect(0, 0, sceneSize.width(), sceneSize.height()));
5310     QPainter *svgPainter = new QPainter(&generator);
5311     getScene()->render(svgPainter);
5312     svgPainter->end();
5313     delete svgPainter;
5314
5315     setExportMode (false);
5316     ex.completeExport();
5317
5318     return offset;
5319 }
5320
5321 void VymModel::exportXML (QString dpath, QString fpath, bool useDialog)
5322 {
5323     ExportBase ex;
5324     ex.setName( "XML" );
5325     ex.setModel( this );
5326     ex.setWindowTitle ( tr("Export map as XML") );
5327     ex.addFilter( "XML (*.xml);;All (* *.*)" );
5328     ex.setLastCommand( settings.localValue(filePath,"/export/last/command","").toString() );
5329
5330     if (useDialog)
5331     {
5332         QFileDialog fd;
5333         fd.setWindowTitle (vymName+ " - " + tr("Export XML to directory"));
5334         fd.setFileMode (QFileDialog::DirectoryOnly);
5335         QStringList filters;
5336         filters << "XML data (*.xml)";
5337         fd.setNameFilters (filters);
5338         fd.setConfirmOverwrite (false);
5339         fd.setAcceptMode (QFileDialog::AcceptSave);
5340
5341         QString fn;
5342         if (fd.exec() != QDialog::Accepted || fd.selectedFiles().isEmpty() ) return;
5343
5344         dpath = fd.selectedFiles().first();
5345         //dpath = dpath.left(dpath.lastIndexOf("/"));
5346         fpath = dpath + "/" + mapName + ".xml";
5347
5348         if (!reallyWriteDirectory(dpath) ) return;
5349     }
5350     ex.setFilePath( fpath );
5351
5352     QString mname = basename(fpath);
5353
5354     // Hide stuff during export, if settings want this
5355     setExportMode (true);
5356
5357     // Create subdirectories
5358     makeSubDirs (dpath);
5359
5360     // write image and calculate offset (Remember old mapSaved setting while exporting image)
5361     bool mchanged = mapChanged;
5362     bool munsaved = mapUnsaved;
5363
5364     QPointF offset = exportImage (dpath + "/images/" + mname + ".png",false,"PNG");
5365
5366     mapChanged = mchanged;
5367     mapUnsaved = munsaved;
5368
5369     // write to directory   //FIXME-3 check totalBBox here...
5370     QString saveFile=saveToDir (dpath , mname + "-", true, offset, NULL); 
5371     QFile file;
5372
5373     file.setFileName (fpath);
5374     if ( !file.open( QIODevice::WriteOnly ) )
5375     {
5376         // This should neverever happen
5377         QMessageBox::critical (
5378                 0,
5379                 tr("Critical Export Error"),
5380                 QString("VymModel::exportXML couldn't open %1").arg(file.fileName())
5381         );
5382         return;
5383     }   
5384
5385     // Write it finally, and write in UTF8, no matter what 
5386     QTextStream ts( &file );
5387     ts.setCodec("UTF-8");
5388     ts << saveFile;
5389     file.close();
5390
5391     setExportMode (false);
5392
5393     ex.completeExport( QString("\"%1\",\"%2\"").arg(dpath).arg(fpath) );
5394 }
5395
5396 void VymModel::exportAO (QString fname,bool askName)
5397 {
5398     ExportAO ex;
5399     ex.setModel (this);
5400     ex.setLastCommand( settings.localValue(filePath,"/export/last/command","").toString() );
5401
5402     if (fname=="") 
5403         ex.setFilePath (mapName+".txt");        
5404     else
5405         ex.setFilePath (fname);
5406
5407     if (askName)
5408     {
5409         ex.setDirPath (lastExportDir.absolutePath());
5410         ex.execDialog();
5411     } 
5412     if (!ex.canceled())
5413     {
5414         setExportMode(true);
5415         ex.doExport();
5416         setExportMode(false);
5417     }
5418 }
5419
5420 void VymModel::exportASCII(bool listTasks, const QString &fname, bool askName)
5421 {
5422     ExportASCII ex;
5423     ex.setModel (this);
5424     ex.setListTasks(listTasks);
5425     ex.setLastCommand( settings.localValue(filePath,"/export/last/command","").toString() );
5426
5427     if (fname=="")
5428         ex.setFilePath (mapName+".txt");
5429     else
5430         ex.setFilePath (fname);
5431
5432     if (askName)
5433     {
5434         ex.setDirPath (lastExportDir.absolutePath());
5435         ex.execDialog() ;
5436     }
5437
5438     if (!ex.canceled())
5439     {
5440         setExportMode(true);
5441         ex.doExport();
5442         setExportMode(false);
5443     }
5444 }
5445
5446 void VymModel::exportCSV(const QString &fname, bool askName)
5447 {
5448     ExportCSV ex;
5449     ex.setModel (this);
5450     ex.setLastCommand( settings.localValue(filePath,"/export/last/command","").toString() );
5451
5452     if (fname=="")
5453         ex.setFilePath (mapName+".csv");
5454     else
5455         ex.setFilePath (fname);
5456
5457     if (askName)
5458     {
5459         ex.addFilter ("CSV (*.csb);;All (* *.*)");
5460         ex.setDirPath (lastExportDir.absolutePath());
5461         ex.setWindowTitle(vymName+ " -" +tr("Export as csv")+" "+tr("(still experimental)"));
5462         ex.execDialog() ;
5463     }
5464
5465     if (!ex.canceled())
5466     {
5467         setExportMode(true);
5468         ex.doExport();
5469         setExportMode(false);
5470     }
5471 }
5472
5473 void VymModel::exportHTML (const QString &dpath, const QString &fpath,bool useDialog)
5474 {
5475     ExportHTML ex (this);
5476     ex.setLastCommand( settings.localValue(filePath,"/export/last/command","").toString() );
5477
5478     if (!dpath.isEmpty()) ex.setDirPath (dpath);
5479     if (!fpath.isEmpty()) ex.setFilePath (fpath);
5480     setExportMode(true);
5481     ex.doExport(useDialog);
5482     setExportMode(false);
5483 }
5484
5485 void VymModel::exportImpress(const QString &fn, const QString &cf) 
5486 {
5487     ExportOO ex;
5488     ex.setFilePath (fn);
5489     ex.setModel (this);
5490     ex.setLastCommand( settings.localValue(filePath,"/export/last/command","").toString() );
5491
5492     if (ex.setConfigFile(cf)) 
5493     {
5494         QString lastCommand = settings.localValue(filePath,"/export/last/command","").toString();
5495
5496         setExportMode (true);
5497         ex.exportPresentation();
5498         setExportMode (false);
5499
5500         QString command = settings.localValue(filePath,"/export/last/command","").toString();
5501         if (lastCommand != command) setChanged();
5502     }
5503 }
5504
5505 bool VymModel::exportLastAvailable(QString &description, QString &command, QString &path, QString &configFile)
5506 {
5507     command     = settings.localValue(filePath,"/export/last/command","").toString();
5508     description = settings.localValue(filePath,"/export/last/description","").toString();
5509     path        = settings.localValue(filePath,"/export/last/exportPath","").toString();
5510     configFile  = settings.localValue(filePath,"/export/last/configFile","").toString();
5511     if (!command.isEmpty() && command.startsWith("export")) 
5512         return true;
5513     else
5514         return false;
5515 }   
5516
5517 void VymModel::exportLast()
5518 {
5519     QString desc, command, path, configFile;  //FIXME-3 better integrate configFile into command
5520     if (exportLastAvailable(desc, command, path, configFile) )
5521     {
5522         execute (command);
5523         /*
5524         if (!configFile.isEmpty() && command=="exportImpress")
5525             execute (QString ("%1 (\"%2\",\"%3\")").arg(command).arg(path).arg(configFile) );
5526         else    
5527             execute (QString ("%1 (\"%2\")").arg(command).arg(path) );
5528         */
5529     }       
5530 }
5531
5532 void VymModel::exportLaTeX (const QString &fname,bool askName)
5533 {
5534     ExportLaTeX ex;
5535     ex.setModel (this);
5536     ex.setLastCommand( settings.localValue(filePath,"/export/last/command","").toString() );
5537
5538     if (fname=="") 
5539         ex.setFilePath (mapName+".tex");        
5540     else
5541         ex.setFilePath (fname);
5542
5543     if (askName) ex.execDialog() ; 
5544     if (!ex.canceled())
5545     {
5546         setExportMode(true);
5547         ex.doExport();
5548         setExportMode(false);
5549     }
5550 }
5551
5552 void VymModel::exportOrgMode (const QString &fname, bool askName)
5553 {
5554     ExportOrgMode ex;
5555     ex.setModel (this);
5556     ex.setLastCommand( settings.localValue(filePath,"/export/last/command","").toString() );
5557
5558     if (fname=="") 
5559         ex.setFilePath (mapName+".org");        
5560     else
5561         ex.setFilePath (fname);
5562
5563     if (askName) 
5564     {
5565         ex.setDirPath (lastExportDir.absolutePath());
5566         ex.execDialog();
5567     }
5568
5569     if (!ex.canceled())
5570     {
5571         setExportMode(true);
5572         ex.doExport();
5573         setExportMode(false);
5574     }
5575 }
5576
5577
5578 //////////////////////////////////////////////
5579 // View related
5580 //////////////////////////////////////////////
5581
5582 void VymModel::registerEditor(QWidget *me)
5583 {
5584     mapEditor=(MapEditor*)me;
5585 }
5586
5587 void VymModel::unregisterEditor(QWidget *)
5588 {
5589     mapEditor=NULL;
5590 }
5591
5592 void VymModel::setMapZoomFactor (const double &d)
5593 {
5594     zoomFactor=d;
5595 }
5596
5597 void VymModel::setMapRotationAngle(const double &d)
5598 {
5599     rotationAngle=d;
5600 }
5601
5602 void VymModel::setMapAnimDuration(const int &d)
5603 {
5604     animDuration=d;
5605 }
5606
5607 void VymModel::setMapAnimCurve(const QEasingCurve &c)
5608 {
5609     animCurve=c;
5610 }
5611
5612 void VymModel::setContextPos(QPointF p)
5613 {
5614     contextPos=p;
5615     hasContextPos=true;
5616 }
5617
5618 void VymModel::unsetContextPos()
5619 {
5620     contextPos=QPointF();
5621     hasContextPos=false;
5622 }
5623
5624 void VymModel::updateNoteFlag()
5625 {
5626     TreeItem *selti=getSelectedItem();
5627     if (selti)
5628     {
5629         if (!mapChanged)
5630         {
5631             setChanged();
5632             updateActions();
5633         }
5634
5635         if (noteEditor->isEmpty())
5636             selti->clearNote();
5637         else
5638             selti->setNote(noteEditor->getNote());
5639         emitDataChanged(selti);
5640         reposition();
5641     }
5642 }
5643
5644 void VymModel::reposition() //FIXME-4 VM should have no need to reposition, but the views...
5645 {
5646     if (blockReposition) return;
5647
5648     BranchObj *bo;
5649     for (int i=0;i<rootItem->branchCount(); i++)
5650     {
5651         bo=rootItem->getBranchObjNum(i);
5652         if (bo)
5653             bo->reposition();   //  for positioning heading
5654         else
5655             qDebug()<<"VM::reposition bo=0";
5656     }   
5657     mapEditor->getTotalBBox();  
5658     emitSelectionChanged();
5659 }
5660
5661
5662 bool VymModel::setMapLinkStyle (const QString & s)
5663 {
5664     QString snow;
5665     switch (linkstyle)
5666     {
5667         case LinkableMapObj::Line :
5668             snow="StyleLine";
5669             break;
5670         case LinkableMapObj::Parabel:
5671             snow="StyleParabel";
5672             break;
5673         case LinkableMapObj::PolyLine:
5674             snow="StylePolyLine";
5675             break;
5676         case LinkableMapObj::PolyParabel:
5677             snow="StylePolyParabel";
5678             break;
5679         default:    
5680             return false;
5681             break;
5682     }
5683
5684     saveState (
5685         QString("setMapLinkStyle (\"%1\")").arg(s),
5686         QString("setMapLinkStyle (\"%1\")").arg(snow),
5687         QString("Set map link style (\"%1\")").arg(s)
5688     );  
5689
5690     if (s=="StyleLine")
5691         linkstyle=LinkableMapObj::Line;
5692     else if (s=="StyleParabel")
5693         linkstyle=LinkableMapObj::Parabel;
5694     else if (s=="StylePolyLine")
5695         linkstyle=LinkableMapObj::PolyLine;
5696     else if (s=="StylePolyParabel") 
5697         linkstyle=LinkableMapObj::PolyParabel;
5698     else
5699         linkstyle=LinkableMapObj::UndefinedStyle;
5700
5701     BranchItem *cur=NULL;
5702     BranchItem *prev=NULL;
5703     BranchObj *bo;
5704     nextBranch (cur,prev);
5705     while (cur) 
5706     {
5707         bo=(BranchObj*)(cur->getLMO() );
5708         bo->setLinkStyle(bo->getDefLinkStyle(cur->parent() ));  //FIXME-4 better emit dataCHanged and leave the changes to View
5709         nextBranch(cur,prev);
5710     }
5711     reposition();
5712     return true;
5713 }
5714
5715 LinkableMapObj::Style VymModel::getMapLinkStyle ()
5716 {
5717     return linkstyle;
5718 }   
5719
5720 uint VymModel::getModelID()
5721 {
5722     return modelID;
5723 }
5724
5725 void VymModel::setView (VymView *vv)
5726 {
5727     vymView=vv;
5728 }
5729
5730 void VymModel::setMapDefLinkColor(QColor col)
5731 {
5732     if ( !col.isValid() ) return;
5733     saveState (
5734         QString("setMapDefLinkColor (\"%1\")").arg(getMapDefLinkColor().name()),
5735         QString("setMapDefLinkColor (\"%1\")").arg(col.name()),
5736         QString("Set map link color to %1").arg(col.name())
5737     );
5738
5739     defLinkColor=col;
5740     BranchItem *cur=NULL;
5741     BranchItem *prev=NULL;
5742     BranchObj *bo;
5743     nextBranch(cur,prev);
5744     while (cur) 
5745     {
5746         bo=(BranchObj*)(cur->getLMO() );
5747         bo->setLinkColor();
5748         nextBranch(cur,prev);
5749     }
5750     updateActions();
5751 }
5752
5753 void VymModel::setMapLinkColorHintInt()
5754 {
5755     // called from setMapLinkColorHint(lch) or at end of parse
5756     BranchItem *cur=NULL;
5757     BranchItem *prev=NULL;
5758     BranchObj *bo;
5759     nextBranch(cur,prev);
5760     while (cur) 
5761     {
5762         bo=(BranchObj*)(cur->getLMO() );
5763         bo->setLinkColor();
5764         nextBranch(cur,prev);
5765     }
5766 }
5767
5768 void VymModel::setMapLinkColorHint(LinkableMapObj::ColorHint lch)
5769 {
5770     linkcolorhint=lch;
5771     setMapLinkColorHintInt();
5772 }
5773
5774 void VymModel::toggleMapLinkColorHint()
5775 {
5776     if (linkcolorhint==LinkableMapObj::HeadingColor)
5777         linkcolorhint=LinkableMapObj::DefaultColor;
5778     else    
5779         linkcolorhint=LinkableMapObj::HeadingColor;
5780     BranchItem *cur=NULL;
5781     BranchItem *prev=NULL;
5782     BranchObj *bo;
5783     nextBranch(cur,prev);
5784     while (cur) 
5785     {
5786         bo=(BranchObj*)(cur->getLMO() );
5787         bo->setLinkColor();
5788         nextBranch(cur,prev);
5789     }
5790 }
5791
5792 void VymModel::selectMapBackgroundImage ()  // FIXME-3 for using background image: view.setCacheMode(QGraphicsView::CacheBackground);  Also this belongs into ME
5793 {
5794     QStringList filters;
5795     filters<< tr("Images") + " (*.png *.bmp *.xbm *.jpg *.png *.xpm *.gif *.pnm)";
5796     QFileDialog fd;
5797     fd.setFileMode (QFileDialog::ExistingFile);
5798     fd.setWindowTitle(vymName + " - " + tr("Load background image"));
5799     fd.setDirectory (lastImageDir);
5800     fd.setAcceptMode (QFileDialog::AcceptOpen);
5801
5802     if ( fd.exec() == QDialog::Accepted &&!fd.selectedFiles().isEmpty())
5803     {
5804         // TODO selectMapBackgroundImg in QT4 use:  lastImageDir=fd.directory();
5805         lastImageDir=QDir ( fd.directory().path() );
5806         setMapBackgroundImage ( fd.selectedFiles().first() );
5807     }
5808 }   
5809
5810 void VymModel::setMapBackgroundImage (const QString &fn)    //FIXME-3 missing savestate, move to ME
5811 {
5812     /*
5813     QColor oldcol=mapEditor->getScene()->backgroundBrush().color();
5814     saveState(
5815         selection,
5816         QString ("setMapBackgroundImage (%1)").arg(oldcol.name()),
5817         selection,
5818         QString ("setMapBackgroundImage (%1)").arg(col.name()),
5819         QString("Set background color of map to %1").arg(col.name()));
5820     */  
5821     QBrush brush;
5822     brush.setTextureImage (QImage (fn));
5823     mapEditor->getScene()->setBackgroundBrush(brush);
5824 }
5825
5826 void VymModel::selectMapBackgroundColor() 
5827 {
5828     QColor col = QColorDialog::getColor( mapEditor->getScene()->backgroundBrush().color(), NULL);
5829     if ( !col.isValid() ) return;
5830     setMapBackgroundColor( col );
5831 }
5832
5833
5834 void VymModel::setMapBackgroundColor(QColor col)    // FIXME-4 move to ME
5835 {
5836     QColor oldcol=mapEditor->getScene()->backgroundBrush().color();
5837     saveState(
5838         QString ("setMapBackgroundColor (\"%1\")").arg(oldcol.name()),
5839         QString ("setMapBackgroundColor (\"%1\")").arg(col.name()),
5840         QString("Set background color of map to %1").arg(col.name()));
5841     mapEditor->getScene()->setBackgroundBrush(col);
5842 }
5843
5844 QColor VymModel::getMapBackgroundColor()    // FIXME-4 move to ME
5845 {
5846     return mapEditor->getScene()->backgroundBrush().color();
5847 }
5848
5849 QFont VymModel::getMapDefaultFont ()  
5850 {
5851     return defaultFont;
5852 }
5853
5854 void VymModel::setMapDefaultFont (const QFont &f)  
5855 {
5856     defaultFont=f;
5857 }
5858
5859 LinkableMapObj::ColorHint VymModel::getMapLinkColorHint()   // FIXME-4 move to ME
5860 {
5861     return linkcolorhint;
5862 }
5863
5864 QColor VymModel::getMapDefLinkColor()   // FIXME-4 move to ME
5865 {
5866     return defLinkColor;
5867 }
5868
5869 void VymModel::setMapDefXLinkPen (const QPen &p)  // FIXME-4 move to ME
5870 {
5871     defXLinkPen=p;
5872 }
5873
5874 QPen VymModel::getMapDefXLinkPen()      // FIXME-4 move to ME
5875 {
5876     return defXLinkPen;
5877 }
5878
5879 void VymModel::setMapDefXLinkStyleBegin( const QString &s)
5880 {
5881     defXLinkStyleBegin = s;
5882 }
5883
5884 QString VymModel::getMapDefXLinkStyleBegin()
5885 {
5886     return defXLinkStyleBegin;
5887 }
5888
5889 void VymModel::setMapDefXLinkStyleEnd( const QString &s)
5890 {
5891     defXLinkStyleEnd = s;
5892 }
5893
5894 QString VymModel::getMapDefXLinkStyleEnd()
5895 {
5896     return defXLinkStyleEnd;
5897 }
5898
5899 void VymModel::move(const double &x, const double &y)
5900 {
5901     MapItem *seli = (MapItem*)getSelectedItem();
5902     if (seli && (seli->isBranchLikeType() || seli->getType()==TreeItem::Image))
5903     {
5904         LinkableMapObj *lmo=seli->getLMO();
5905         if (lmo)
5906         {
5907             QPointF ap(lmo->getAbsPos());
5908             QPointF to(x, y);
5909             if (ap != to)
5910             {
5911                 QString ps=qpointFToString(ap);
5912                 QString s=getSelectString(seli);
5913                 saveState(
5914                     s, "move "+ps, 
5915                     s, "move "+qpointFToString(to), 
5916                     QString("Move %1 to %2").arg(getObjectName(seli)).arg(ps));
5917                 lmo->move(x,y);
5918                 reposition();
5919                 emitSelectionChanged();
5920             }
5921         }
5922     }
5923 }
5924
5925 void VymModel::moveRel (const double &x, const double &y)   
5926 {
5927     MapItem *seli = (MapItem*)getSelectedItem();
5928     if (seli && (seli->isBranchLikeType() || seli->getType()==TreeItem::Image))
5929     {
5930         LinkableMapObj *lmo=seli->getLMO();
5931         if (lmo)
5932         {
5933             QPointF rp(lmo->getRelPos());
5934             QPointF to(x, y);
5935             if (rp != to)
5936             {
5937                 QString ps=qpointFToString (lmo->getRelPos());
5938                 QString s=getSelectString(seli);
5939                 saveState(
5940                     s, "moveRel "+ps, 
5941                     s, "moveRel "+qpointFToString(to), 
5942                     QString("Move %1 to relative position %2").arg(getObjectName(seli)).arg(ps));
5943                 ((OrnamentedObj*)lmo)->move2RelPos (x,y);
5944                 reposition();
5945                 lmo->updateLinkGeometry();
5946                 emitSelectionChanged();
5947             }
5948         }   
5949     }
5950 }
5951
5952
5953 void VymModel::animate()   
5954 {
5955     animationTimer->stop();
5956     BranchObj *bo;
5957     int i=0;
5958     while (i<animObjList.size() )
5959     {
5960         bo=(BranchObj*)animObjList.at(i);
5961         if (!bo->animate())
5962         {
5963             if (i>=0) 
5964             {   
5965                 animObjList.removeAt(i);
5966                 i--;
5967             }
5968         }
5969         bo->reposition();
5970         i++;
5971     } 
5972     emitSelectionChanged();
5973
5974     if (!animObjList.isEmpty()) animationTimer->start(animationInterval);
5975 }
5976
5977
5978 void VymModel::startAnimation(BranchObj *bo, const QPointF &v)
5979 {
5980     if (!bo) return;
5981
5982     if (bo->getUseRelPos())
5983         startAnimation (bo,bo->getRelPos(),bo->getRelPos()+v);
5984     else
5985         startAnimation (bo,bo->getAbsPos(),bo->getAbsPos()+v);
5986 }
5987
5988 void VymModel::startAnimation(BranchObj *bo, const QPointF &start, const QPointF &dest)
5989 {
5990     if (start==dest) return;
5991     if (bo && bo->getTreeItem()->depth()>=0) 
5992     {
5993         AnimPoint ap;
5994         ap.setStart (start);
5995         ap.setDest  (dest);
5996         ap.setTicks (animationTicks);
5997         ap.setAnimated (true);
5998         bo->setAnimation (ap);
5999         if (!animObjList.contains(bo))
6000             animObjList.append( bo );
6001         animationTimer->setSingleShot (true);
6002         animationTimer->start(animationInterval);
6003     }
6004 }
6005
6006 void VymModel::stopAnimation (MapObj *mo)
6007 {
6008     int i=animObjList.indexOf(mo);
6009     if (i>=0)
6010         animObjList.removeAt (i);
6011 }
6012
6013 void VymModel::stopAllAnimation ()
6014 {
6015     BranchObj *bo;
6016     int i=0;
6017     while (i<animObjList.size() )
6018     {
6019         bo=(BranchObj*)animObjList.at(i);
6020         bo->stopAnimation();
6021         bo->requestReposition();
6022         i++;
6023     } 
6024     reposition();
6025 }
6026
6027 void VymModel::sendSelection()
6028 {
6029     if (netstate!=Server) return;
6030     sendData (QString("select (\"%1\")").arg(getSelectString()) );
6031 }
6032
6033 void VymModel::newServer()
6034 {
6035     port=54321;
6036     sendCounter=0;
6037     tcpServer = new QTcpServer(this);
6038     if (!tcpServer->listen(QHostAddress::Any,port)) {
6039         QMessageBox::critical(NULL, "vym server",
6040                               QString("Unable to start the server: %1.").arg(tcpServer->errorString()));
6041         //FIXME-3 needed? we are no widget any longer... close();
6042         return;
6043     }
6044     connect(tcpServer, SIGNAL(newConnection()), this, SLOT(newClient()));
6045     netstate=Server;
6046     qDebug()<<"Server is running on port "<<tcpServer->serverPort();
6047 }
6048
6049 void VymModel::connectToServer()
6050 {
6051     port=54321;
6052     server="salam.suse.de";
6053     server="localhost";
6054     clientSocket = new QTcpSocket (this);
6055     clientSocket->abort();
6056     clientSocket->connectToHost(server ,port);
6057     connect(clientSocket, SIGNAL(readyRead()), this, SLOT(readData()));
6058     connect(clientSocket, SIGNAL(error(QAbstractSocket::SocketError)),
6059             this, SLOT(displayNetworkError(QAbstractSocket::SocketError)));
6060     netstate=Client;        
6061     qDebug()<<"connected to "<<qPrintable (server)<<" port "<<port;
6062
6063     
6064 }
6065
6066 void VymModel::newClient()
6067 {
6068     QTcpSocket *newClient = tcpServer->nextPendingConnection();
6069     connect(newClient, SIGNAL(disconnected()),
6070             newClient, SLOT(deleteLater()));
6071
6072     qDebug() <<"ME::newClient  at "<<qPrintable( newClient->peerAddress().toString() );
6073
6074     clientList.append (newClient);
6075 }
6076
6077
6078 void VymModel::sendData(const QString &s)
6079 {
6080     if (clientList.size()==0) return;
6081
6082     // Create bytearray to send
6083     QByteArray block;
6084     QDataStream out(&block, QIODevice::WriteOnly);
6085     out.setVersion(QDataStream::Qt_4_0);
6086
6087     // Reserve some space for blocksize
6088     out << (quint16)0;
6089
6090     // Write sendCounter
6091     out << sendCounter++;
6092
6093     // Write data
6094     out << s;
6095
6096     // Go back and write blocksize so far
6097     out.device()->seek(0);
6098     quint16 bs=(quint16)(block.size() - 2*sizeof(quint16));
6099     out << bs;
6100
6101     if (debug)
6102         qDebug() << "ME::sendData  bs="<<bs<<"  counter="<<sendCounter<<"  s="<<qPrintable(s);
6103
6104     for (int i=0; i<clientList.size(); ++i)
6105     {
6106         //qDebug() << "Sending \""<<qPrintable (s)<<"\" to "<<qPrintable (clientList.at(i)->peerAddress().toString());
6107         clientList.at(i)->write (block);
6108     }
6109 }
6110
6111 void VymModel::readData ()
6112 {
6113     while (clientSocket->bytesAvailable() >=(int)sizeof(quint16) )
6114     {
6115         if (debug)
6116             qDebug() <<"readData  bytesAvail="<<clientSocket->bytesAvailable();
6117         quint16 recCounter;
6118         quint16 blockSize;
6119
6120         QDataStream in(clientSocket);
6121         in.setVersion(QDataStream::Qt_4_0);
6122
6123         in >> blockSize;
6124         in >> recCounter;
6125         
6126         QString t;
6127         in >>t;
6128         if (debug)
6129             qDebug() << "VymModel::readData  command="<<qPrintable (t);
6130         bool noErr;
6131         QString errMsg;
6132         parseAtom (t,noErr,errMsg);
6133
6134     }
6135     return;
6136 }
6137
6138 void VymModel::displayNetworkError(QAbstractSocket::SocketError socketError)
6139 {
6140     switch (socketError) {
6141     case QAbstractSocket::RemoteHostClosedError:
6142         break;
6143     case QAbstractSocket::HostNotFoundError:
6144         QMessageBox::information(NULL, vymName +" Network client",
6145                                  "The host was not found. Please check the "
6146                                     "host name and port settings.");
6147         break;
6148     case QAbstractSocket::ConnectionRefusedError:
6149         QMessageBox::information(NULL, vymName + " Network client",
6150                                  "The connection was refused by the peer. "
6151                                     "Make sure the fortune server is running, "
6152                                     "and check that the host name and port "
6153                                     "settings are correct.");
6154         break;
6155     default:
6156         QMessageBox::information(NULL, vymName + " Network client",
6157                                  QString("The following error occurred: %1.")
6158                                  .arg(clientSocket->errorString()));
6159     }
6160 }
6161
6162 void VymModel::downloadImage (const QUrl &url, BranchItem *bi) 
6163 {
6164     if (!bi) bi=getSelectedBranch();
6165     if (!bi) 
6166     {
6167         qWarning ("VM::download bi==NULL");
6168         return;
6169     }
6170
6171     // FIXME-3 download img to tmpfile and delete after running script in mainWindow
6172     QString script;
6173     script += QString("selectID(\"%1\");").arg(bi->getUuid().toString());
6174     script += QString("loadImage(\"$TMPFILE\");");
6175
6176     DownloadAgent *agent = new DownloadAgent(url);
6177     agent->setFinishedAction (this, script);
6178     connect (agent, SIGNAL (downloadFinished()), mainWindow, SLOT (downloadFinished()));
6179     QTimer::singleShot(0, agent, SLOT(execute()));
6180 }
6181
6182 void VymModel::selectMapSelectionColor()
6183 {
6184     QColor col = QColorDialog::getColor( defLinkColor, NULL);
6185     setSelectionColor (col);
6186 }
6187
6188 void VymModel::setSelectionColorInt (QColor col)
6189 {
6190     if ( !col.isValid() ) return;
6191     saveState (
6192         QString("setSelectionColor (\"%1\")").arg(mapEditor->getSelectionColor().name()),
6193         QString("setSelectionColor (\"%1\")").arg(col.name()),
6194         QString("Set color of selection box to %1").arg(col.name())
6195     );
6196
6197     mapEditor->setSelectionColor (col);
6198 }
6199
6200 void VymModel::emitSelectionChanged(const QItemSelection &newsel)
6201 {
6202     emit (selectionChanged(newsel,newsel)); // needed e.g. to update geometry in editor
6203     sendSelection();
6204 }
6205
6206 void VymModel::emitSelectionChanged()
6207 {
6208     QItemSelection newsel=selModel->selection();
6209     emitSelectionChanged (newsel);
6210 }
6211
6212 void VymModel::setSelectionColor(QColor col)
6213 {
6214     if ( !col.isValid() ) return;
6215     setSelectionColorInt (col);
6216 }
6217
6218 QColor VymModel::getSelectionColor()
6219 {
6220     return mapEditor->getSelectionColor();
6221 }
6222
6223 void VymModel::setHideTmpMode (TreeItem::HideTmpMode mode)  
6224 {
6225     hidemode=mode;
6226     for (int i=0;i<rootItem->branchCount();i++)
6227         rootItem->getBranchNum(i)->setHideTmp (mode);
6228     reposition();
6229     if (mode==TreeItem::HideExport)
6230         unselectAll();
6231     else
6232         reselect();
6233
6234     qApp->processEvents();
6235 }
6236
6237 //////////////////////////////////////////////
6238 // Selection related
6239 //////////////////////////////////////////////
6240
6241 void VymModel::updateSelection(QItemSelection newsel,QItemSelection dsel)       
6242 {
6243     QModelIndex ix;
6244     MapItem *mi;
6245     BranchItem *bi;
6246     bool do_reposition=false;
6247     foreach (ix, dsel.indexes() )
6248     {
6249         mi = static_cast<MapItem*>(ix.internalPointer());
6250         if (mi->isBranchLikeType() )
6251             do_reposition=do_reposition || ((BranchItem*)mi)->resetTmpUnscroll();
6252         if (mi->getType()==TreeItem::XLink)
6253         {
6254             Link *li=((XLinkItem*)mi)->getLink();
6255             XLinkObj *xlo=li->getXLinkObj();
6256             if (xlo) 
6257                 xlo->setSelection (XLinkObj::Unselected);
6258
6259             do_reposition=do_reposition || li->getBeginBranch()->resetTmpUnscroll();
6260             do_reposition=do_reposition || li->getEndBranch()->resetTmpUnscroll();
6261         }
6262     }    
6263
6264     foreach (ix, newsel.indexes() )
6265     {
6266         mi = static_cast<MapItem*>(ix.internalPointer());
6267         if (mi->isBranchLikeType() )
6268         {
6269             bi=(BranchItem*)mi;
6270             if (bi->hasScrolledParent() )
6271             {
6272                 bi->tmpUnscroll();
6273                 do_reposition=true;
6274             }
6275         }
6276         if (mi->getType()==TreeItem::XLink)
6277         {
6278             ((XLinkItem*)mi)->setSelection();
6279
6280             // begin/end branches need to be tmp unscrolled
6281             Link *li=((XLinkItem*)mi)->getLink();
6282             bi=li->getBeginBranch();
6283             if (bi->hasScrolledParent() )
6284             {
6285                 bi->tmpUnscroll();
6286                 do_reposition=true;
6287             }
6288             bi=li->getEndBranch();
6289             if (bi->hasScrolledParent() )
6290             {
6291                 bi->tmpUnscroll();
6292                 do_reposition=true;
6293             }
6294         }
6295     }    
6296     if ( do_reposition ) reposition();
6297 }
6298
6299 void VymModel::setSelectionModel (QItemSelectionModel *sm)
6300 {
6301     selModel=sm;
6302 }
6303
6304 QItemSelectionModel* VymModel::getSelectionModel()
6305 {
6306     return selModel;
6307 }
6308
6309 void VymModel::setSelectionBlocked (bool b)
6310 {
6311     selectionBlocked=b;
6312 }
6313
6314 bool VymModel::isSelectionBlocked()
6315 {
6316     return selectionBlocked;
6317 }
6318
6319 bool VymModel::select (const QString &s)
6320 {
6321     if (s.isEmpty()) return false;
6322     TreeItem *ti=findBySelectString(s);
6323     if (ti) return select (index(ti));
6324     return false;
6325 }
6326
6327 bool VymModel::selectID (const QString &s)
6328 {
6329     if (s.isEmpty()) return false;
6330     TreeItem *ti=findUuid(QUuid(s));
6331     if (ti) return select (index(ti));
6332     return false;
6333 }
6334
6335 bool VymModel::select (LinkableMapObj *lmo)
6336 {
6337     QItemSelection oldsel=selModel->selection();
6338
6339     if (lmo)
6340         return select (lmo->getTreeItem() );
6341     else    
6342         return false;
6343 }
6344
6345 bool VymModel::selectToggle (TreeItem *ti)
6346 {
6347     if (ti) 
6348     { 
6349         selModel->select ( index(ti), QItemSelectionModel::Toggle);
6350         //appendSelection();    // FIXME-4 selection history not implemented yet for multiselections 
6351         return true;
6352     }
6353     return false;
6354 }
6355
6356 bool VymModel::select (TreeItem *ti)
6357 {
6358     if (ti) 
6359         return select (index(ti));
6360     else
6361         return false;
6362 }
6363
6364 bool VymModel::select (const QModelIndex &index) 
6365 {
6366     if (index.isValid() )
6367     {
6368         TreeItem *ti=getItem (index);
6369         if (ti->isBranchLikeType() )
6370             ((BranchItem*)ti)->tmpUnscroll();
6371         reposition();
6372         selModel->select (index,QItemSelectionModel::ClearAndSelect  );
6373         appendSelection();
6374         return true;
6375     }
6376     return false;
6377 }
6378
6379 void VymModel::unselectAll ()    
6380 {
6381     unselect (selModel->selection() );
6382 }
6383
6384 void VymModel::unselect(QItemSelection desel)  
6385 {
6386     if (!desel.isEmpty())
6387     {
6388         lastSelectString=getSelectString();
6389         selModel->clearSelection(); 
6390     }
6391 }   
6392
6393 bool VymModel::reselect()
6394 {
6395     bool b=select (lastSelectString);
6396     return b;
6397 }   
6398
6399 bool VymModel::canSelectPrevious()
6400 {
6401     if (currentSelection>0)
6402         return true;
6403     else
6404         return false;
6405 }
6406
6407 bool VymModel::selectPrevious()
6408 {
6409     keepSelectionHistory=true;
6410     bool result=false;
6411     while (currentSelection>0)
6412     {
6413         currentSelection--;
6414         TreeItem *ti=findID (selectionHistory.at(currentSelection));
6415         if (ti) 
6416         {
6417             result=select (ti);
6418             break;
6419         } else
6420             selectionHistory.removeAt (currentSelection);
6421     } 
6422     keepSelectionHistory=false;
6423     return result;
6424 }   
6425
6426 bool VymModel::canSelectNext()
6427 {
6428     if (currentSelection < selectionHistory.count()-1 )
6429         return true;
6430     else
6431         return false;
6432 }
6433
6434 bool VymModel::selectNext()
6435 {
6436     keepSelectionHistory=true;
6437     bool result=false;
6438     while (currentSelection<selectionHistory.count()-1)
6439     {
6440         currentSelection++;
6441         TreeItem *ti=findID (selectionHistory.at(currentSelection));
6442         if (ti) 
6443         {
6444             result=select (ti);
6445             break;
6446         } else
6447             selectionHistory.removeAt (currentSelection);
6448     } 
6449     keepSelectionHistory=false;
6450     return result;
6451 }   
6452
6453 void VymModel::resetSelectionHistory()
6454 {
6455     selectionHistory.clear();
6456     currentSelection=-1;
6457     keepSelectionHistory=false;
6458     appendSelection();
6459 }
6460
6461 void VymModel::appendSelection()    // FIXME-4 history unable to cope with multiple selections
6462 {
6463     uint id=0;
6464     TreeItem *ti=getSelectedItem();
6465     if (ti && !keepSelectionHistory) 
6466     {
6467         if (ti->isBranchLikeType())
6468             ((BranchItem*)ti)->setLastSelectedBranch();
6469         id=ti->getID(); 
6470         selectionHistory.append (id);
6471         currentSelection=selectionHistory.count()-1;
6472         updateActions();
6473     }
6474 }
6475
6476 void VymModel::emitShowSelection()  
6477 {
6478     if (!blockReposition) emit (showSelection() );
6479 }
6480
6481 void VymModel::emitNoteChanged (TreeItem *ti)
6482 {
6483     QModelIndex ix=index(ti);
6484     emit (noteChanged (ix) );
6485 }
6486
6487 void VymModel::emitDataChanged (TreeItem *ti)    
6488 {
6489     QModelIndex ix=index(ti);
6490     emit ( dataChanged (ix,ix) );
6491     if (!blockReposition)
6492     {
6493         if ( ti->isBranchLikeType() && ((BranchItem*)ti)->getTask()  )
6494         {
6495             taskModel->emitDataChanged ( ((BranchItem*)ti)->getTask() );
6496             taskModel->recalcPriorities();
6497         }
6498     }
6499 }
6500
6501 void VymModel::emitUpdateQueries ()
6502 {
6503     // Used to tell MainWindow to update query results
6504     if (blockReposition) return; 
6505     emit (updateQueries (this) );
6506 }
6507 void VymModel::emitUpdateLayout()
6508 {
6509     if (settings.value("/mainwindow/autoLayout/use","true")=="true")
6510         emit (updateLayout());
6511 }
6512
6513 bool VymModel::selectFirstBranch()
6514 {
6515     TreeItem *ti=getSelectedBranch();
6516     if (ti)
6517     {
6518         TreeItem *par=ti->parent();
6519         if (par) 
6520         {
6521             TreeItem *ti2=par->getFirstBranch();
6522             if (ti2) return  select(ti2);
6523         }
6524     }       
6525     return false;
6526 }
6527
6528 bool VymModel::selectLastBranch()
6529 {
6530     TreeItem *ti=getSelectedBranch();
6531     if (ti)
6532     {
6533         TreeItem *par=ti->parent();
6534         if (par) 
6535         {
6536             TreeItem *ti2=par->getLastBranch();
6537             if (ti2) return select(ti2);
6538         }
6539     }       
6540     return false;
6541 }
6542
6543 bool VymModel::selectLastSelectedBranch()
6544 {
6545     BranchItem *bi=getSelectedBranch();
6546     if (bi)
6547     {
6548         bi=bi->getLastSelectedBranch();
6549         if (bi) return select (bi);
6550     }       
6551     return false;
6552 }
6553
6554 bool VymModel::selectLastImage()    
6555 {
6556     TreeItem *ti=getSelectedBranch();
6557     if (ti)
6558     {
6559         TreeItem *par=ti->parent();
6560         if (par) 
6561         {
6562             TreeItem *ti2=par->getLastImage();
6563             if (ti2) return select(ti2);
6564         }
6565     }       
6566     return false;
6567 }
6568
6569 bool VymModel::selectParent()
6570 {
6571     TreeItem *ti=getSelectedItem();
6572     TreeItem *par;
6573     if (ti)
6574     {
6575         par=ti->parent();
6576         if (par) 
6577             return select(par);
6578     }       
6579     return false;
6580 }
6581
6582 TreeItem::Type VymModel::selectionType()
6583 {
6584     TreeItem *ti = getSelectedItem();
6585     if (ti)
6586         return ti->getType();
6587     else
6588         return TreeItem::Undefined;
6589 }
6590
6591 LinkableMapObj* VymModel::getSelectedLMO()
6592 {
6593     QModelIndexList list=selModel->selectedIndexes();
6594     if (list.count()==1 )
6595     {
6596         TreeItem *ti = getItem (list.first() );
6597         TreeItem::Type type=ti->getType();
6598         if (type ==TreeItem::Branch || type==TreeItem::MapCenter || type==TreeItem::Image)
6599             return ((MapItem*)ti)->getLMO();
6600     }
6601     return NULL;
6602 }
6603
6604 BranchObj* VymModel::getSelectedBranchObj() // convenience function
6605 {
6606     TreeItem *ti = getSelectedBranch();
6607     if (ti)
6608         return (BranchObj*)(  ((MapItem*)ti)->getLMO());
6609     else    
6610         return NULL;
6611 }
6612
6613 BranchItem* VymModel::getSelectedBranch()
6614 {
6615     TreeItem *ti=getSelectedItem();
6616     if (ti)
6617     {
6618         TreeItem::Type type=ti->getType();
6619         if (type ==TreeItem::Branch || type==TreeItem::MapCenter)
6620             return (BranchItem*)ti;
6621     }
6622     return NULL;
6623 }
6624
6625 QList <BranchItem*> VymModel::getSelectedBranches()
6626 {
6627     QList <BranchItem*> bis;
6628     foreach (TreeItem *ti,getSelectedItems() )
6629     {
6630         TreeItem::Type type=ti->getType();
6631         if (type ==TreeItem::Branch || type==TreeItem::MapCenter)
6632             bis.append ( (BranchItem*)ti );
6633     }
6634     return bis;
6635 }
6636
6637 ImageItem* VymModel::getSelectedImage()
6638 {
6639     TreeItem *ti=getSelectedItem();
6640     if (ti && ti->getType()==TreeItem::Image)
6641         return (ImageItem*)ti;
6642     else
6643         return NULL;
6644 }
6645
6646 Task* VymModel::getSelectedTask()
6647 {
6648     BranchItem *selbi=getSelectedBranch();
6649     if (selbi)
6650         return selbi->getTask();
6651     else
6652         return NULL;
6653 }
6654
6655 Link* VymModel::getSelectedXLink()
6656 {
6657     XLinkItem *xli=getSelectedXLinkItem();
6658     if (xli) return xli->getLink();
6659     return NULL;
6660 }
6661
6662 XLinkItem* VymModel::getSelectedXLinkItem()
6663 {
6664     TreeItem *ti=getSelectedItem();
6665     if (ti && ti->getType()==TreeItem::XLink)
6666         return (XLinkItem*)ti;
6667     else
6668         return NULL;
6669 }
6670
6671 AttributeItem* VymModel::getSelectedAttribute() 
6672 {
6673     TreeItem *ti=getSelectedItem();
6674     if (ti && ti->getType()==TreeItem::Attribute)
6675         return (AttributeItem*)ti;
6676     else
6677         return NULL;
6678 }
6679
6680 TreeItem* VymModel::getSelectedItem()   
6681 {
6682     if (!selModel) return NULL;
6683     QModelIndexList list=selModel->selectedIndexes();
6684     if (list.count()==1 )
6685         return getItem (list.first() );
6686     else    
6687         return NULL;
6688 }
6689
6690 QList <TreeItem*> VymModel::getSelectedItems()  
6691 {
6692     QList <TreeItem*> l;
6693     if (!selModel) return l;
6694     QModelIndexList list=selModel->selectedIndexes();
6695     foreach (QModelIndex ix,list)
6696         l.append (getItem (ix) );
6697     return l;
6698 }
6699
6700 QModelIndex VymModel::getSelectedIndex()
6701 {
6702     QModelIndexList list=selModel->selectedIndexes();
6703     if (list.count()==1 )
6704         return list.first();
6705     else
6706         return QModelIndex();
6707 }
6708
6709 QList <uint> VymModel::getSelectedIDs()
6710 {
6711     QList <uint> uids;
6712     foreach (TreeItem* ti,getSelectedItems() )
6713         uids.append (ti->getID() );
6714     return uids;        
6715 }
6716
6717 bool VymModel::isSelected(TreeItem *ti)
6718 {
6719     return getSelectedItems().contains (ti);
6720 }
6721
6722 QString VymModel::getSelectString ()
6723 {
6724     return getSelectString (getSelectedItem());
6725 }
6726
6727 QString VymModel::getSelectString (LinkableMapObj *lmo) // only for convenience. Used in MapEditor
6728 {
6729     if (!lmo) return QString();
6730     return getSelectString (lmo->getTreeItem() );
6731 }
6732
6733 QString VymModel::getSelectString (TreeItem *ti) 
6734 {
6735     QString s;
6736     if (!ti || ti->depth()<0) return s;    
6737     switch (ti->getType())
6738     {
6739         case TreeItem::MapCenter: s="mc:"; break;
6740         case TreeItem::Branch: s="bo:";break;
6741         case TreeItem::Image: s="fi:";break;
6742         case TreeItem::Attribute: s="ai:";break;
6743         case TreeItem::XLink: s="xl:";break;
6744         default:
6745             s="unknown type in VymModel::getSelectString()";
6746             break;
6747     }
6748     s=  s + QString("%1").arg(ti->num());
6749     if (ti->depth() >0)
6750         // call myself recursively
6751         s= getSelectString(ti->parent()) +","+s;
6752     return s;
6753 }
6754
6755 QString VymModel::getSelectString (BranchItem *bi) 
6756 {
6757     return getSelectString ((TreeItem*)bi);
6758 }
6759
6760 QString VymModel::getSelectString (const uint &i)
6761 {
6762     return getSelectString ( findID (i) );
6763 }
6764
6765 SlideModel* VymModel::getSlideModel()
6766 {
6767     return slideModel;
6768 }
6769
6770 int VymModel::slideCount() 
6771 {
6772     return slideModel->count();
6773 }
6774
6775 SlideItem* VymModel::addSlide()  
6776 {
6777     SlideItem *si=slideModel->getSelectedItem();  
6778     if (si)
6779         si=slideModel->addSlide (NULL,si->childNumber()+1 );
6780     else
6781         si=slideModel->addSlide();
6782     
6783     TreeItem *seli=getSelectedItem();
6784
6785     if (si && seli)
6786     {
6787         QString inScript;
6788         if (!loadStringFromDisk(macroPath + "slideeditor-snapshot.vys", inScript) )
6789         {
6790             qWarning()<<"VymModel::addSlide couldn't load template for taking snapshot";
6791             return NULL;
6792         }
6793
6794         inScript.replace("CURRENT_ZOOM", QString().setNum(getMapEditor()->getZoomFactorTarget()) );
6795         inScript.replace("CURRENT_ANGLE", QString().setNum(getMapEditor()->getAngleTarget()) );
6796         inScript.replace("CURRENT_ID", "\"" + seli->getUuid().toString() + "\"");
6797
6798         si->setInScript(inScript);
6799     slideModel->setData ( slideModel->index(si), seli->getHeadingPlain() );
6800     }
6801     QString s="<vymmap>" + si->saveToDir() + "</vymmap>";
6802     int pos=si->childNumber();
6803     saveState (
6804         PartOfMap,
6805         getSelectString(), QString("deleteSlide (%1)").arg(pos),
6806         getSelectString(), QString("addMapInsert (\"PATH\",%1)").arg(pos),
6807         "Add slide",
6808         NULL,
6809         s );
6810     return si;
6811 }
6812
6813 void VymModel::deleteSlide(SlideItem *si)  
6814 {
6815     if (si)
6816     {
6817         QString s="<vymmap>" + si->saveToDir() + "</vymmap>";
6818         int pos=si->childNumber();
6819         saveState (
6820             PartOfMap,
6821             getSelectString(), QString("addMapInsert (\"PATH\",%1)").arg(pos),
6822             getSelectString(), QString("deleteSlide (%1)").arg(pos),
6823             "Delete slide",
6824             NULL,
6825             s );
6826         slideModel->deleteSlide (si);
6827     }
6828 }
6829
6830 void VymModel::deleteSlide(int n)  
6831 {
6832     deleteSlide (slideModel->getSlide (n));
6833 }
6834
6835 void VymModel::relinkSlide(SlideItem *si, int pos)
6836 {
6837     if (si && pos>=0) 
6838         slideModel->relinkSlide (si, si->parent(), pos);
6839 }
6840
6841 void VymModel::moveSlideUp(int n)  
6842 {
6843     SlideItem *si=NULL;
6844     if (n<0) // default if called without parameters
6845     {
6846         si=slideModel->getSelectedItem();
6847         if (si) n=si->childNumber();
6848     } else
6849         si=slideModel->getSlide(n);
6850     if (si && n>0 && n<slideModel->count())
6851     {
6852         blockSlideSelection=true;
6853         slideModel->relinkSlide (si, si->parent(), n-1);
6854         blockSlideSelection=false;
6855         saveState (
6856             getSelectString(),QString("moveSlideDown (%1)").arg(n-1),
6857             getSelectString(),QString("moveSlideUp (%1)").arg(n),
6858             QString("Move slide %1 up").arg(n));
6859     }
6860 }
6861
6862 void VymModel::moveSlideDown(int n)   
6863 {
6864     SlideItem *si=NULL;
6865     if (n<0) // default if called without parameters
6866     {
6867         si=slideModel->getSelectedItem();
6868         if (si) n=si->childNumber();
6869     } else
6870         si=slideModel->getSlide(n);
6871     if (si && n>=0 && n < slideModel->count()-1)
6872     {
6873         blockSlideSelection=true;
6874         slideModel->relinkSlide (si, si->parent(), n+1);
6875         blockSlideSelection=false;
6876         saveState (
6877             getSelectString(),QString("moveSlideUp (%1)").arg(n+1),
6878             getSelectString(),QString("moveSlideDown (%1)").arg(n),
6879             QString("Move slide %1 down").arg(n));
6880     }
6881 }
6882
6883 void VymModel::updateSlideSelection (QItemSelection newsel,QItemSelection)
6884 {
6885     if (blockSlideSelection) return;
6886     QModelIndex ix;
6887     foreach (ix,newsel.indexes() )
6888     {
6889         SlideItem *si= static_cast<SlideItem*>(ix.internalPointer());
6890         QString inScript=si->getInScript();
6891
6892         // show inScript in ScriptEditor
6893         scriptEditor->setSlideScript(modelID, si->getID(), inScript );
6894
6895         // Execute inScript 
6896         execute (inScript);
6897     }
6898 }