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