1 #include <QApplication>
2 #include <QSvgGenerator>
5 #include <QtDBus/QDBusConnection>
14 #include <QColorDialog>
15 #include <QFileDialog>
16 #include <QMessageBox>
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"
37 #include "findresultmodel.h"
38 #include "jira-agent.h"
39 #include "lockedfiledialog.h"
40 #include "mainwindow.h"
42 #include "noteeditor.h"
44 #include "scripteditor.h"
45 #include "slideitem.h"
46 #include "slidemodel.h"
47 #include "taskeditor.h"
48 #include "taskmodel.h"
50 #include "vymprocess.h"
51 #include "warningdialog.h"
52 #include "xlinkitem.h"
54 #include "xml-freemind.h"
64 extern bool restoreMode;
65 extern QStringList ignoredLockedFiles;
67 extern Main *mainWindow;
69 extern QDir tmpVymDir;
71 extern NoteEditor *noteEditor;
72 extern TaskEditor *taskEditor;
73 extern ScriptEditor *scriptEditor;
74 extern FlagRowMaster *standardFlagsMaster;
75 extern FlagRowMaster *userFlagsMaster;
77 extern Options options;
79 extern QString clipboardDir;
80 extern QString clipboardFile;
82 extern ImageIO imageIO;
84 extern TaskModel *taskModel;
86 extern QString vymName;
87 extern QString vymVersion;
88 extern QDir vymBaseDir;
90 extern QDir lastImageDir;
91 extern QDir lastMapDir;
92 extern QDir lastExportDir;
94 extern Settings settings;
95 extern QTextStream vout;
97 uint VymModel::idLast = 0; // make instance
101 // qDebug()<< "Const VymModel" << this;
103 rootItem->setModel(this);
104 wrapper = new VymModelWrapper(this);
107 VymModel::~VymModel()
109 // out << "Destr VymModel begin this="<<this<<" "<<mapName<<flush;
111 repositionBlocked = true;
112 autosaveTimer->stop();
113 fileChangedTimer->stop();
116 // qApp->processEvents(); // Update view (scene()->update() is not enough)
117 // qDebug() << "Destr VymModel end this="<<this;
119 vymLock.releaseLock();
124 void VymModel::clear()
126 while (rootItem->childCount() > 0) {
127 // qDebug()<<"VM::clear ri="<<rootItem<<"
128 // ri->count()="<<rootItem->childCount();
129 deleteItem(rootItem->getChildNum(0));
133 void VymModel::init()
138 // Use default author
141 .value("/user/name", tr("unknown user",
142 "default name for map author in settings"))
154 selectionBlocked = false;
155 resetSelectionHistory();
160 makeTmpDirectories();
166 fileName = tr("unnamed");
168 repositionBlocked = false;
169 saveStateBlocked = false;
171 autosaveTimer = new QTimer(this);
172 connect(autosaveTimer, SIGNAL(timeout()), this, SLOT(autosave()));
174 fileChangedTimer = new QTimer(this);
175 connect(fileChangedTimer, SIGNAL(timeout()), this, SLOT(fileChanged()));
176 fileChangedTimer->start(3000);
178 taskAlarmTimer = new QTimer(this);
179 connect(taskAlarmTimer, SIGNAL(timeout()), this, SLOT(updateTasksAlarm()));
180 taskAlarmTimer->start(3000);
182 // animations // FIXME-4 switch to new animation system
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();
189 animationTimer = new QTimer(this);
190 connect(animationTimer, SIGNAL(timeout()), this, SLOT(animate()));
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";
203 hasContextPos = false;
205 hidemode = TreeItem::HideNone;
207 // Animation in MapEditor
211 animCurve = QEasingCurve::OutQuint;
213 // Initialize presentation slides
214 slideModel = new SlideModel(this);
215 blockSlideSelection = false;
217 // Avoid recursions later
218 cleaningUpLinks = false;
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!");
232 void VymModel::makeTmpDirectories()
234 // Create unique temporary directories
235 tmpMapDirPath = tmpVymDir.path() + QString("/model-%1").arg(modelID);
236 histPath = tmpMapDirPath + "/history";
238 d.mkdir(tmpMapDirPath);
241 QString VymModel::tmpDirPath() { return tmpMapDirPath; }
243 MapEditor *VymModel::getMapEditor() { return mapEditor; }
245 VymModelWrapper *VymModel::getWrapper() { return wrapper; }
247 bool VymModel::isRepositionBlocked() { return repositionBlocked; }
249 void VymModel::updateActions()
251 // Tell mainwindow to update states of actions
252 mainWindow->updateActions();
255 bool VymModel::setData(const QModelIndex &, const QVariant &value, int role)
257 if (role != Qt::EditRole)
260 setHeadingPlainText(value.toString());
265 void VymModel::resetUsedFlags()
267 standardFlagsMaster->resetUsedCounter();
268 userFlagsMaster->resetUsedCounter();
271 QString VymModel::saveToDir(const QString &tmpdir, const QString &prefix,
272 FlagRowMaster::WriteMode flagMode, const QPointF &offset,
275 // tmpdir temporary directory to which data will be written
276 // prefix mapname, which will be appended to images etc.
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
287 case LinkableMapObj::Line:
290 case LinkableMapObj::Parabel:
293 case LinkableMapObj::PolyLine:
294 ls = "StylePolyLine";
297 ls = "StylePolyParabel";
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");
307 QString mapAttr = xml.attribut("version", vymVersion);
309 QPen selPen = mapEditor->getSelectionPen();
310 QBrush selBrush = mapEditor->getSelectionBrush();
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())) +
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())) +
341 header += xml.beginElement("vymmap", mapAttr);
344 // Find the used flags while traversing the tree
347 // Temporary list of links
348 QList<Link *> tmpLinks;
351 // Build xml recursivly
353 // Save all mapcenters as complete map, if saveSel not set
354 tree += saveTreeToDir(tmpdir, prefix, offset, tmpLinks);
356 // Save local settings
357 tree += settings.getDataXML(destPath);
360 if (getSelectedItem() && !saveSel)
361 tree += xml.valueElement("select", getSelectString());
364 switch (saveSel->getType()) {
365 case TreeItem::Branch:
367 tree += ((BranchItem *)saveSel)
368 ->saveToDir(tmpdir, prefix, offset, tmpLinks);
370 case TreeItem::MapCenter:
372 tree += ((BranchItem *)saveSel)
373 ->saveToDir(tmpdir, prefix, offset, tmpLinks);
375 case TreeItem::Image:
377 tree += ((ImageItem *)saveSel)->saveToDir(tmpdir, prefix);
380 // other types shouldn't be safed directly...
387 // Write images and definitions of used user flags
388 if (flagMode != FlagRowMaster::NoFlags) {
389 // First find out, which flags are used
391 flags += userFlagsMaster->saveDef(flagMode);
393 userFlagsMaster->saveDataToDir(tmpdir + "flags/user/", flagMode);
394 standardFlagsMaster->saveDataToDir(tmpdir + "flags/standard/",
400 for (int i = 0; i < tmpLinks.count(); ++i)
401 footer += tmpLinks.at(i)->saveToDir();
404 footer += slideModel->saveToDir();
407 footer += xml.endElement("vymmap");
409 return header + flags + tree + footer;
412 QString VymModel::saveTreeToDir(const QString &tmpdir, const QString &prefix,
413 const QPointF &offset, QList<Link *> &tmpLinks)
416 for (int i = 0; i < rootItem->branchCount(); i++)
417 s += rootItem->getBranchNum(i)->saveToDir(tmpdir, prefix, offset,
422 void VymModel::setFilePath(QString fpath, QString destname)
424 if (fpath.isEmpty() || fpath == "") {
430 filePath = fpath; // becomes absolute path
431 fileName = fpath; // gets stripped of path
432 destPath = destname; // needed for vymlinks and during load to reset
435 // If fpath is not an absolute path, complete it
436 filePath = QDir(fpath).absolutePath();
437 fileDir = filePath.left(1 + filePath.lastIndexOf("/"));
439 // Set short name, too. Search from behind:
440 fileName = basename(fileName);
442 // Forget the .vym (or .xml) for name of map
444 fileName.left(fileName.lastIndexOf(".", -1, Qt::CaseSensitive));
448 void VymModel::setFilePath(QString fpath) { setFilePath(fpath, fpath); }
450 QString VymModel::getFileDir() { return fileDir; }
452 QString VymModel::getFilePath() { return filePath; }
454 QString VymModel::getFileName() { return fileName; }
456 QString VymModel::getMapName() { return mapName; }
458 QString VymModel::getDestPath() { return destPath; }
460 bool VymModel::parseVymText(const QString &s)
463 BranchItem *bi = getSelectedBranch();
465 parseBaseHandler *handler = new parseVYMHandler;
467 bool saveStateBlockedOrg = saveStateBlocked;
468 repositionBlocked = true;
469 saveStateBlocked = true;
470 QXmlInputSource source;
472 QXmlSimpleReader reader;
473 reader.setContentHandler(handler);
474 reader.setErrorHandler(handler);
476 handler->setInputString(s);
477 handler->setModel(this);
478 handler->setLoadMode(ImportReplace, 0);
480 ok = reader.parse(source);
481 repositionBlocked = false;
482 saveStateBlocked = saveStateBlockedOrg;
484 if (s.startsWith("<vymnote"))
487 reposition(); // to generate bbox sizes
490 QMessageBox::critical(0, tr("Critical Parse Error"),
491 tr(handler->errorProtocol().toUtf8()));
493 // Still return "success": the map maybe at least
494 // partially read by the parser
500 File::ErrorCode VymModel::loadMap(QString fname, const LoadMode &lmode,
501 const FileType &ftype,
502 const int &contentFilter, int pos)
504 File::ErrorCode err = File::Success;
506 // Get updated zoomFactor, before applying one read from file in the end
508 zoomFactor = mapEditor->getZoomFactorTarget();
509 rotationAngle = mapEditor->getAngleTarget();
512 parseBaseHandler *handler;
516 handler = new parseVYMHandler;
517 ((parseVYMHandler *)handler)->setContentFilter(contentFilter);
520 handler = new parseFreemindHandler;
523 QMessageBox::critical(0, tr("Critical Parse Error"),
524 "Unknown FileType in VymModel::load()");
525 return File::Aborted;
528 if (lmode == NewMap) {
529 // Reset timestamp to check for later updates of file
530 fileChangedTime = QFileInfo(destPath).lastModified();
532 selModel->clearSelection();
535 bool zipped_org = zipped;
537 // Create temporary directory for packing
539 QString tmpZipDir = makeTmpDir(ok, tmpDirPath(), "unzip");
541 QMessageBox::critical(
542 0, tr("Critical Load Error"),
543 tr("Couldn't create temporary directory before load\n"));
544 return File::Aborted;
548 if (fname.right(4) == ".xml" || fname.right(3) == ".mm") {
552 if (lmode == NewMap || lmode == DefaultMap)
557 err = unzipDir(tmpZipDir, fname);
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
572 QStringList flist = QDir(tmpZipDir).entryList(filters);
573 if (flist.count() == 1) {
574 // Only one entry, take this one
575 xmlfile = tmpZipDir + "/" + flist.first();
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
583 // mainWindow->fileLoadFromTmp (flist);
584 // returnCode = 1; // Silently forget this attempt to load
585 qWarning("MainWindow::load (fn) multimap found...");
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"));
594 } // file doesn't exist
596 xmlfile = mfile.fileName();
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()));
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);
620 // We need to set the tmpDir in order to load files with rel. path
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);
631 handler->setLoadMode(lmode, pos);
633 // Here we actually parse the XML file
634 bool ok = reader.parse(source);
637 repositionBlocked = false;
638 saveStateBlocked = saveStateBlockedOrg;
639 mapEditor->setViewportUpdateMode(QGraphicsView::MinimalViewportUpdate);
642 reposition(); // to generate bbox sizes
643 emitSelectionChanged();
645 if (lmode == NewMap) // no lockfile for default map!
650 autosaveTimer->stop();
653 resetSelectionHistory();
655 // Set treeEditor and slideEditor visibilty per map
656 vymView->readSettings();
658 if (!tryVymLock() && debug)
659 qWarning() << "VM::loadMap no lockfile created!";
662 // Recalc priorities and sort
663 taskModel->recalcPriorities();
666 QMessageBox::critical(0, tr("Critical Parse Error"),
667 tr(handler->errorProtocol().toUtf8()));
669 // Still return "success": the map maybe at least
670 // partially read by the parser
675 removeDir(QDir(tmpZipDir));
677 // Restore original zip state
686 mapEditor->setZoomFactorTarget(zoomFactor);
687 mapEditor->setAngleTarget(rotationAngle);
690 qApp->processEvents(); // Update view (scene()->update() is not enough)
694 File::ErrorCode VymModel::save(const SaveMode &savemode)
698 QString saveFilePath;
700 File::ErrorCode err = File::Success;
704 mapFileName = mapName + ".xml";
706 // use name given by user, could be anything
707 mapFileName = fileName;
709 // Look, if we should zip the data:
712 QMessageBox mb(vymName,
713 tr("The map %1\ndid not use the compressed "
714 "vym file format.\nWriting it uncompressed will also "
716 "and flags and thus may overwrite files into the "
717 "given directory\n\nDo you want to write the map")
719 QMessageBox::Warning,
720 QMessageBox::Yes | QMessageBox::Default, QMessageBox::No,
721 QMessageBox::Cancel | QMessageBox::Escape);
722 mb.setButtonText(QMessageBox::Yes, tr("compressed (vym default)"));
725 tr("uncompressed, potentially overwrite existing data"));
726 mb.setButtonText(QMessageBox::Cancel, tr("Cancel"));
728 case QMessageBox::Yes:
729 // save compressed (default file format)
732 case QMessageBox::No:
736 case QMessageBox::Cancel:
738 return File::Aborted;
743 // First backup existing file, we
744 // don't want to add to old zip archives
747 if (settings.value("/system/writeBackupFile").toBool()) {
748 QString backupFileName(destPath + "~");
749 QFile backupFile(backupFileName);
750 if (backupFile.exists() && !backupFile.remove()) {
751 QMessageBox::warning(
753 tr("%1\ncould not be removed before saving")
754 .arg(backupFileName));
757 if (!f.rename(backupFileName)) {
758 QMessageBox::warning(
760 tr("%1\ncould not be renamed before saving")
768 // Create temporary directory for packing
770 tmpZipDir = makeTmpDir(ok, tmpDirPath(), "zip");
772 QMessageBox::critical(
773 0, tr("Critical Save Error"),
774 tr("Couldn't create temporary directory before save\n"));
775 return File::Aborted;
778 saveFilePath = filePath;
779 setFilePath(tmpZipDir + "/" + mapName + ".xml", saveFilePath);
782 // Create mapName and fileDir
783 makeSubDirs(fileDir);
786 if (savemode == CompleteMap || selModel->selection().isEmpty()) {
789 // Use defined name for map within zipfile to avoid problems
790 // with zip library and umlauts (see #98)
792 saveToDir(fileDir, "", FlagRowMaster::UsedFlags, QPointF(), NULL);
794 saveFile = saveToDir(fileDir, mapName + "-", FlagRowMaster::UsedFlags,
798 autosaveTimer->stop();
802 if (selectionType() == TreeItem::Image)
805 saveFile = saveToDir(fileDir, mapName + "-", FlagRowMaster::UsedFlags,
806 QPointF(), getSelectedBranch());
807 // FIXME-3 take care of multiselections when saving parts
812 // Use defined map name "map.xml", if zipped. Introduce in 2.6.6
813 saved = saveStringToDisk(fileDir + "map.xml", saveFile);
815 // Use regular mapName, when saved as XML
816 saved = saveStringToDisk(fileDir + mapFileName, saveFile);
819 qWarning("ME::saveStringToDisk failed!");
824 if (err == File::Success)
825 err = zipDir(tmpZipDir, destPath);
828 removeDir(QDir(tmpZipDir));
830 // Restore original filepath outside of tmp zip dir
831 setFilePath(saveFilePath);
836 fileChangedTime = QFileInfo(destPath).lastModified();
840 ImageItem* VymModel::loadImage(BranchItem *dst, const QString &fn) // FIXME-2 better move filedialog to MainWindow
843 dst = getSelectedBranch();
845 QString filter = QString(tr("Images") +
846 " (*.png *.bmp *.xbm *.jpg *.png *.xpm *.gif "
847 "*.pnm *.svg *.svgz);;" +
848 tr("All", "Filedialog") + " (*.*)");
851 fns = QFileDialog::getOpenFileNames(
852 NULL, vymName + " - " + tr("Load image"), lastImageDir.path(),
857 if (!fns.isEmpty()) {
858 lastImageDir.setPath(
859 fns.first().left(fns.first().lastIndexOf("/")));
861 for (int j = 0; j < fns.count(); 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")
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());
873 LinkableMapObj *parLMO = dst->getLMO();
876 fio->move(parLMO->getChildRefPos());
881 // On default include image // FIXME-4 check, if we change
882 // default settings...
884 setIncludeImagesHor(false);
885 setIncludeImagesVer(true);
891 qWarning() << "vymmodel: Failed to load " + s;
900 void VymModel::saveImage(ImageItem *ii, QString fn)
903 ii = getSelectedImage();
905 QString filter = QString(
907 " (*.png *.bmp *.xbm *.jpg *.png *.xpm *.gif *.pnm *.svg);;" +
908 tr("All", "Filedialog") + " (*.*)");
910 fn = QFileDialog::getSaveFileName(
911 NULL, vymName + " - " + tr("Save image"), lastImageDir.path(),
912 filter, NULL, QFileDialog::DontConfirmOverwrite);
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?")
921 QMessageBox::Warning,
922 QMessageBox::Yes | QMessageBox::Default,
923 QMessageBox::Cancel | QMessageBox::Escape,
924 QMessageBox::NoButton);
926 mb.setButtonText(QMessageBox::Yes, tr("Overwrite"));
927 mb.setButtonText(QMessageBox::No, tr("Cancel"));
929 case QMessageBox::Yes:
932 case QMessageBox::Cancel:
938 if (!ii->saveImage(fn))
939 QMessageBox::critical(0, tr("Critical Error"),
940 tr("Couldn't save %1").arg(fn));
945 void VymModel::importDirInt(BranchItem *dst, QDir d)
947 bool oldSaveState = saveStateBlocked;
948 saveStateBlocked = true;
949 BranchItem *bi = dst;
951 int beginDepth = bi->depth();
953 d.setFilter(QDir::AllEntries | QDir::Hidden);
954 QFileInfoList list = d.entryInfoList();
957 // Traverse directories
958 for (int i = 0; i < list.size(); ++i) {
960 if (fi.isDir() && fi.fileName() != "." && fi.fileName() != "..") {
961 bi = addNewBranchInt(dst, -2);
962 bi->setHeadingPlainText(fi.fileName());
963 bi->setHeadingColor(QColor("blue"));
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()));
971 // Recursively add subdirs
979 for (int i = 0; i < list.size(); ++i) {
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());
991 // Scroll at least some stuff
992 if (dst->branchCount() > 1 && dst->depth() - beginDepth > 2)
995 saveStateBlocked = oldSaveState;
998 void VymModel::importDir(const QString &s)
1000 BranchItem *selbi = getSelectedBranch();
1002 saveStateChangingPart(
1003 selbi, selbi, QString("importDir (\"%1\")").arg(s),
1004 QString("Import directory structure from %1").arg(s));
1007 importDirInt(selbi, d);
1011 void VymModel::importDir()
1013 BranchItem *selbi = getSelectedBranch();
1015 QStringList filters;
1016 filters << "VYM map (*.vym)";
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);
1027 if (fd.exec() == QDialog::Accepted && !fd.selectedFiles().isEmpty()) {
1028 importDir(fd.selectedFiles().first());
1034 bool VymModel::removeVymLock()
1036 if (vymLock.removeLockForced()) {
1037 mainWindow->statusMessage(tr("Removed lockfile for %1").arg(mapName));
1044 bool VymModel::tryVymLock()
1046 // Defaults for author and host in vymLock
1049 .value("/user/name",
1050 tr("unknown user", "Default for lockfiles of maps"))
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"));
1058 vymLock.setHost(defHost);
1061 if (!vymLock.tryLock()) {
1063 qDebug() << "VymModel::tryLock failed!";
1065 if (vymLock.getState() == VymLock::LockedByOther) {
1067 // While restoring maps, existing lockfiles will be ignored for
1068 // loading, but listed in a warning dialog
1069 ignoredLockedFiles << filePath;
1070 return removeVymLock();
1073 LockedFileDialog dia;
1074 QString a = vymLock.getAuthor();
1075 QString h = vymLock.getHost();
1078 tr("Map seems to be already opened in another vym "
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."))
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(
1093 tr("Couldn't remove lockfile for %1").arg(mapName));
1096 if (!tryVymLock()) {
1097 // Was able to remove existing lockfile, but not able to
1099 qWarning() << "VymModel::tryVymLock could not create new lockfile after removing old";
1105 else if (vymLock.getState() == VymLock::NotWritable) {
1107 QString s = QString(tr("Cannot create lockfile of map! "
1108 "It will be opened in readonly mode.\n\n"));
1110 dia.setWindowTitle(tr("Warning", "VymModel"));
1111 dia.showCancelButton(false);
1112 // dia.setShowAgainName("/mainwindow/mapIsLocked");
1120 bool VymModel::renameMap(const QString &newPath)
1122 QString oldPath = filePath;
1123 if (vymLock.getState() == VymLock::LockedByMyself || vymLock.getState() == VymLock::Undefined) {
1124 // vymModel owns the lockfile, try to create new lock
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);
1133 // Change lockfiles now
1134 if (!vymLock.releaseLock())
1135 qWarning() << "VymModel::renameMap failed to release lock for " << oldPath;
1137 setFilePath(newPath);
1140 qWarning() << "VymModel::renameMap failed to get lockfile. state=" << vymLock.getState();
1144 void VymModel::setReadOnly(bool b)
1147 mainWindow->updateTabName(this);
1150 bool VymModel::isReadOnly() { return readonly; }
1152 void VymModel::autosave()
1154 // Check if autosave is disabled due to testmode
1158 << QString("VymModel::autosave disabled in testmode! Current map: %1")
1163 // Check if autosave is disabled globally
1164 if (!mainWindow->useAutosave()) {
1166 << QString("VymModel::autosave disabled globally! Current map: %1")
1171 QDateTime now = QDateTime().currentDateTime();
1173 // Disable autosave, while we have gone back in history
1174 int redosAvail = undoSet.numValue(QString("/history/redosAvail"));
1178 // Also disable autosave for new map without filename
1179 if (filePath.isEmpty()) {
1182 << "VymModel::autosave rejected due to missing filePath\n";
1186 if (mapUnsaved && mapChanged && mainWindow->useAutosave() && !testmode) {
1187 if (QFileInfo(filePath).lastModified() <= fileChangedTime)
1188 mainWindow->fileSave(this);
1190 qDebug() << " ME::autosave rejected, file on disk is newer than "
1195 void VymModel::fileChanged()
1197 // Check if file on disk has changed meanwhile
1198 if (!filePath.isEmpty()) {
1200 // unset readonly if lockfile is gone
1201 if (vymLock.tryLock())
1205 // We could check, if somebody else removed/replaced lockfile
1206 // (A unique vym ID would be needed)
1208 QDateTime tmod = QFileInfo(filePath).lastModified();
1209 if (tmod > fileChangedTime) {
1210 // FIXME-4 VM switch to current mapeditor and finish
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 "
1218 QMessageBox::Question, QMessageBox::Yes,
1219 QMessageBox::Cancel | QMessageBox::Default,
1220 QMessageBox::NoButton);
1222 mb.setButtonText(QMessageBox::Yes, tr("Reload"));
1223 mb.setButtonText(QMessageBox::No, tr("Ignore"));
1224 switch (mb.exec()) {
1225 case QMessageBox::Yes:
1227 mainWindow->initProgressCounter(1);
1229 mainWindow->removeProgressCounter();
1231 case QMessageBox::Cancel:
1233 tmod; // allow autosave to overwrite newer file!
1240 void VymModel::blockReposition()
1242 repositionBlocked = true;
1245 void VymModel::unblockReposition()
1247 repositionBlocked = false;
1251 bool VymModel::isDefault() { return mapDefault; }
1253 void VymModel::makeDefault()
1259 bool VymModel::hasChanged() { return mapChanged; }
1261 void VymModel::setChanged()
1264 autosaveTimer->start(
1265 settings.value("/system/autosave/ms/", 30000).toInt());
1272 QString VymModel::getObjectName(LinkableMapObj *lmo)
1274 if (!lmo || !lmo->getTreeItem())
1276 return getObjectName(lmo->getTreeItem());
1279 QString VymModel::getObjectName(TreeItem *ti)
1283 return QString("Error: NULL has no name!");
1284 s = ti->getHeadingPlain();
1288 return QString("%1 (%2)").arg(ti->getTypeName()).arg(s);
1291 void VymModel::redo()
1293 // Can we undo at all?
1297 bool saveStateBlockedOrg = saveStateBlocked;
1298 saveStateBlocked = true;
1302 if (undosAvail < stepsTotal)
1305 if (curStep > stepsTotal)
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));
1316 undoSet.value(QString("/history/step-%1/comment").arg(curStep));
1317 QString version = undoSet.value("/history/version");
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));
1326 // Find out current undo directory
1327 QString bakMapDir(QString(tmpMapDirPath + "/undo-%1").arg(curStep));
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() << " ---------------------------";
1345 // select object before redo
1346 if (!redoSelection.isEmpty())
1347 select(redoSelection);
1350 QString redoScript =
1351 QString("model = vym.currentMap(); model.%1").arg(redoCommand);
1352 errMsg = QVariant(execute(redoScript)).toString();
1353 saveStateBlocked = saveStateBlockedOrg;
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);
1360 mainWindow->updateHistory(undoSet);
1363 /* TODO remove testing
1364 qDebug() << "ME::redo() end\n";
1365 qDebug() << " undosAvail="<<undosAvail;
1366 qDebug() << " redosAvail="<<redosAvail;
1367 qDebug() << " curStep="<<curStep;
1368 qDebug() << " ---------------------------";
1372 bool VymModel::isRedoAvailable()
1374 if (undoSet.numValue("/history/redosAvail", 0) > 0)
1380 QString VymModel::lastRedoSelection()
1382 if (isUndoAvailable())
1383 return undoSet.value(
1384 QString("/history/step-%1/redoSelection").arg(curStep));
1389 QString VymModel::lastRedoCommand()
1391 if (isUndoAvailable())
1392 return undoSet.value(
1393 QString("/history/step-%1/redoCommand").arg(curStep));
1398 QVariant VymModel::repeatLastCommand()
1400 QString command = "m = vym.currentMap();";
1401 if (isUndoAvailable())
1404 QString("/history/step-%1/redoCommand").arg(curStep)) +
1408 return execute(command);
1411 void VymModel::undo()
1413 // Can we undo at all?
1417 mainWindow->statusMessage(tr("Autosave disabled during undo."));
1419 bool saveStateBlockedOrg = saveStateBlocked;
1420 saveStateBlocked = true;
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));
1431 undoSet.value(QString("/history/step-%1/comment").arg(curStep));
1432 QString version = undoSet.value("/history/version");
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));
1441 // Find out current undo directory
1442 QString bakMapDir(QString(tmpMapDirPath + "/undo-%1").arg(curStep));
1444 // select object before undo
1445 if (!undoSelection.isEmpty() && !select(undoSelection)) {
1446 qWarning("VymModel::undo() Could not select object for undo");
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;
1466 // select object before undo // FIXME-2 double select, see above
1467 if (!undoSelection.isEmpty())
1468 select(undoSelection);
1472 QString undoScript =
1473 QString("model = vym.currentMap(); model.%1").arg(undoCommand);
1474 errMsg = QVariant(execute(undoScript)).toString();
1479 curStep = stepsTotal;
1483 saveStateBlocked = saveStateBlockedOrg;
1485 qDebug() << "VymModel::undo() end\n";
1486 qDebug() << " undosAvail="<<undosAvail;
1487 qDebug() << " redosAvail="<<redosAvail;
1488 qDebug() << " curStep="<<curStep;
1489 qDebug() << " ---------------------------";
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);
1497 mainWindow->updateHistory(undoSet);
1501 bool VymModel::isUndoAvailable()
1503 if (undoSet.numValue("/history/undosAvail", 0) > 0)
1508 void VymModel::gotoHistoryStep(int i)
1510 // Restore variables
1511 int undosAvail = undoSet.numValue(QString("/history/undosAvail"));
1512 int redosAvail = undoSet.numValue(QString("/history/redosAvail"));
1515 i = undosAvail + redosAvail;
1517 // Clicking above current step makes us undo things
1518 if (i < undosAvail) {
1519 for (int j = 0; j < undosAvail - i; j++)
1523 // Clicking below current step makes us redo things
1525 for (int j = undosAvail; j < i; j++) {
1527 qDebug() << "VymModel::gotoHistoryStep redo " << j << "/"
1528 << undosAvail << " i=" << i;
1532 // And ignore clicking the current row ;-)
1535 QString VymModel::getHistoryPath()
1537 QString histName(QString("history-%1").arg(curStep));
1538 return (tmpMapDirPath + "/" + histName);
1541 void VymModel::resetHistory()
1547 stepsTotal = settings.value("/history/stepsTotal", 100).toInt();
1548 undoSet.setValue("/history/stepsTotal", QString::number(stepsTotal));
1549 mainWindow->updateHistory(undoSet);
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)
1557 sendData(redoCom); // FIXME-4 testing
1561 if (saveStateBlocked)
1565 qDebug() << "VM::saveState() for map " << mapName;
1567 QString undoCommand = undoCom;
1568 QString redoCommand = redoCom;
1571 // Increase undo steps, but check for repeated actions
1572 // like editing a vymNote - then do not increase but replace last command
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);
1582 if (undosAvail < stepsTotal)
1586 if (curStep > stepsTotal)
1590 QString histDir = getHistoryPath();
1591 QString bakMapPath = histDir + "/map.xml";
1593 // Create histDir if not available
1596 makeSubDirs(histDir);
1598 // Save depending on how much needs to be saved
1599 QList<Link *> tmpLinks;
1601 dataXML = saveToDir(histDir, mapName + "-", FlagRowMaster::NoFlags, QPointF(),
1604 if (savemode == PartOfMap) {
1605 undoCommand.replace("PATH", bakMapPath);
1606 redoCommand.replace("PATH", bakMapPath);
1609 if (!dataXML.isEmpty())
1610 // Write XML Data to disk
1611 saveStringToDisk(bakMapPath, dataXML);
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
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),
1624 undoSet.setValue(QString("/history/step-%1/undoSelection").arg(curStep),
1626 undoSet.setValue(QString("/history/step-%1/redoCommand").arg(curStep),
1628 undoSet.setValue(QString("/history/step-%1/redoSelection").arg(curStep),
1630 undoSet.setValue(QString("/history/step-%1/comment").arg(curStep), comment);
1631 undoSet.setValue(QString("/history/version"), vymVersion);
1632 undoSet.writeSettings(histPath);
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;
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;
1652 mainWindow->updateHistory(undoSet);
1657 void VymModel::saveStateChangingPart(TreeItem *undoSel, TreeItem *redoSel,
1658 const QString &rc, const QString &comment)
1660 // save the selected part of the map, Undo will replace part of map
1661 QString undoSelection = "";
1663 undoSelection = getSelectString(undoSel);
1665 qWarning("VymModel::saveStateChangingPart no undoSel given!");
1666 QString redoSelection = "";
1668 redoSelection = getSelectString(undoSel);
1670 qWarning("VymModel::saveStateChangingPart no redoSel given!");
1672 saveState(PartOfMap, undoSelection, "addMapReplace (\"PATH\")",
1673 redoSelection, rc, comment, undoSel);
1676 void VymModel::saveStateRemovingPart(TreeItem *redoSel, const QString &comment)
1679 qWarning("VymModel::saveStateRemovingPart no redoSel given!");
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())
1692 redoSelection, "remove ()", comment, redoSel);
1696 void VymModel::saveState(TreeItem *undoSel, const QString &uc,
1697 TreeItem *redoSel, const QString &rc,
1698 const QString &comment)
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
1704 QString redoSelection = "";
1706 redoSelection = getSelectString(redoSel);
1707 QString undoSelection = "";
1709 undoSelection = getSelectString(undoSel);
1711 saveState(UndoCommand, undoSelection, uc, redoSelection, rc, comment, NULL);
1714 void VymModel::saveState(const QString &undoSel, const QString &uc,
1715 const QString &redoSel, const QString &rc,
1716 const QString &comment)
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);
1724 void VymModel::saveState(const QString &uc, const QString &rc,
1725 const QString &comment)
1727 // "Normal" savestate applied to model (no selection needed):
1728 // save commands and comment
1729 saveState(UndoCommand, NULL, uc, NULL, rc, comment, NULL);
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 = "";
1739 redoSelection = getSelectString(redoSel);
1740 QString undoSelection = "";
1742 undoSelection = getSelectString(undoSel);
1744 saveState(UndoCommand, undoSelection, uc, redoSelection, rc, comment, NULL);
1747 void VymModel::saveStateBeforeLoad(LoadMode lmode, const QString &fname)
1749 BranchItem *selbi = getSelectedBranch();
1751 if (lmode == ImportAdd)
1752 saveStateChangingPart(selbi, selbi,
1753 QString("addMapInsert (\"%1\")").arg(fname),
1754 QString("Add map %1 to %2")
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")
1763 .arg(getObjectName(selbi)));
1768 QGraphicsScene *VymModel::getScene() { return mapEditor->getScene(); }
1770 TreeItem *VymModel::findBySelectString(QString s)
1775 // Old maps don't have multiple mapcenters and don't save full path
1776 if (s.left(2) != "mc")
1779 QStringList parts = s.split(",");
1782 TreeItem *ti = rootItem;
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);
1802 TreeItem *VymModel::findID(const uint &id)
1804 BranchItem *cur = NULL;
1805 BranchItem *prev = NULL;
1806 nextBranch(cur, prev);
1808 if (id == cur->getID())
1811 while (j < cur->xlinkCount()) {
1812 XLinkItem *xli = cur->getXLinkItemNum(j);
1813 if (id == xli->getID())
1818 while (j < cur->imageCount()) {
1819 ImageItem *ii = cur->getImageNum(j);
1820 if (id == ii->getID())
1824 nextBranch(cur, prev);
1829 TreeItem *VymModel::findUuid(const QUuid &id)
1831 BranchItem *cur = NULL;
1832 BranchItem *prev = NULL;
1833 nextBranch(cur, prev);
1835 if (id == cur->getUuid())
1838 while (j < cur->xlinkCount()) {
1839 XLinkItem *xli = cur->getXLinkItemNum(j);
1840 if (id == xli->getUuid())
1845 while (j < cur->imageCount()) {
1846 ImageItem *ii = cur->getImageNum(j);
1847 if (id == ii->getUuid())
1851 nextBranch(cur, prev);
1856 //////////////////////////////////////////////
1858 //////////////////////////////////////////////
1859 void VymModel::setVersion(const QString &s) { version = s; }
1861 QString VymModel::getVersion() { return version; }
1863 void VymModel::setTitle(const QString &s)
1865 saveState(QString("setMapTitle (\"%1\")").arg(title),
1866 QString("setMapTitle (\"%1\")").arg(s),
1867 QString("Set title of map to \"%1\"").arg(s));
1871 QString VymModel::getTitle() { return title; }
1873 void VymModel::setAuthor(const QString &s)
1875 saveState(QString("setMapAuthor (\"%1\")").arg(author),
1876 QString("setMapAuthor (\"%1\")").arg(s),
1877 QString("Set author of map to \"%1\"").arg(s));
1881 QString VymModel::getAuthor() { return author; }
1883 void VymModel::setComment(const QString &s)
1885 saveState(QString("setMapComment (\"%1\")").arg(comment),
1886 QString("setMapComment (\"%1\")").arg(s),
1887 QString("Set comment of map"));
1891 QString VymModel::getComment() { return comment; }
1893 QString VymModel::getDate()
1895 return QDate::currentDate().toString("yyyy-MM-dd");
1898 int VymModel::branchCount()
1901 BranchItem *cur = NULL;
1902 BranchItem *prev = NULL;
1903 nextBranch(cur, prev);
1906 nextBranch(cur, prev);
1911 int VymModel::centerCount() { return rootItem->branchCount(); }
1913 void VymModel::setSortFilter(const QString &s)
1916 emit(sortFilterChanged(sortFilter));
1919 QString VymModel::getSortFilter() { return sortFilter; }
1921 void VymModel::setHeading(const VymText &vt, BranchItem *bi)
1926 QString s = vt.getTextASCII();
1928 bi = getSelectedBranch();
1930 h_old = bi->getHeading();
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))
1939 emitDataChanged(bi);
1940 emitUpdateQueries();
1941 mainWindow->updateHeadingEditor(bi); // Update HeadingEditor with new heading
1946 void VymModel::setHeadingPlainText(const QString &s, BranchItem *bi)
1949 bi = getSelectedBranch();
1951 VymText vt = bi->getHeading();
1953 if (bi->getHeading() == vt)
1958 if ((s.startsWith("http://") || s.startsWith("https://")) &&
1959 bi->getURL().isEmpty())
1964 Heading VymModel::getHeading()
1966 TreeItem *selti = getSelectedItem();
1968 return selti->getHeading();
1969 qWarning() << "VymModel::getHeading Nothing selected.";
1973 void VymModel::updateNoteText(const VymText &vt)
1975 bool editorStateChanged = false;
1977 TreeItem *selti = getSelectedItem();
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;
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)));
1998 // Update also flags after changes in NoteEditor
1999 emitDataChanged(selti);
2001 // Only update flag, if state has changed
2002 if (editorStateChanged)
2007 void VymModel::setNote(const VymNote &vn)
2009 TreeItem *selti = getSelectedItem();
2013 n_old = selti->getNote();
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);
2026 VymNote VymModel::getNote()
2028 TreeItem *selti = getSelectedItem();
2030 VymNote n = selti->getNote();
2033 qWarning() << "VymModel::getNote Nothing selected.";
2037 bool VymModel::hasRichTextNote()
2039 TreeItem *selti = getSelectedItem();
2041 return selti->getNote().isRichText();
2046 void VymModel::loadNote(const QString &fn)
2048 BranchItem *selbi = getSelectedBranch();
2051 if (!loadStringFromDisk(fn, n))
2052 qWarning() << "VymModel::loadNote Couldn't load " << fn;
2057 emitDataChanged(selbi);
2058 emitUpdateQueries();
2063 qWarning("VymModel::loadNote no branch selected");
2066 void VymModel::saveNote(const QString &fn)
2068 BranchItem *selbi = getSelectedBranch();
2070 VymNote n = selbi->getNote();
2072 qWarning() << "VymModel::saveNote note is empty, won't save to "
2075 if (!saveStringToDisk(fn, n.saveToDir()))
2076 qWarning() << "VymModel::saveNote Couldn't save " << fn;
2080 qWarning("VymModel::saveNote no branch selected");
2083 void VymModel::findDuplicateURLs() // FIXME-3 needs GUI
2085 // Generate map containing _all_ URLs and branches
2087 QMultiMap<QString, BranchItem *> map;
2088 BranchItem *cur = NULL;
2089 BranchItem *prev = NULL;
2090 nextBranch(cur, prev);
2095 nextBranch(cur, prev);
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();
2109 qDebug() << " - " << i.value() << " - "
2110 << i.value()->getHeading().getText();
2119 bool VymModel::findAll(FindResultModel *rmodel, QString s,
2120 Qt::CaseSensitivity cs, bool searchNotes)
2123 rmodel->setSearchString(s);
2124 rmodel->setSearchFlags(QTextDocument::FindFlags()); // FIXME-4 translate cs to
2125 // QTextDocument::FindFlag
2128 BranchItem *cur = NULL;
2129 BranchItem *prev = NULL;
2130 nextBranch(cur, prev);
2132 FindResultItem *lastParent = NULL;
2135 if (cur->getHeading().getTextASCII().contains(s, cs)) {
2136 lastParent = rmodel->addItem(cur);
2141 QString n = cur->getNoteASCII();
2145 i = n.indexOf(s, i, cs);
2147 // If not there yet, add "parent" item
2149 lastParent = rmodel->addItem(cur);
2153 << "VymModel::findAll still no lastParent?!";
2156 lastParent->setSelectable (false);
2160 // save index of occurence
2161 QString e = n.mid(i - 15, 30);
2162 n.replace('\n', ' ');
2165 QString(tr("Note", "FindAll in VymModel") +
2167 .arg(n.mid(i - 8, 80)),
2174 nextBranch(cur, prev);
2179 void VymModel::setURL(QString url, bool updateFromCloud, BranchItem *bi)
2181 if (!bi) bi = getSelectedBranch();
2182 if (bi->getURL() == url)
2186 QString oldurl = bi->getURL();
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);
2195 if (updateFromCloud) // FIXME-2 use oembed.com also for Youtube and other cloud providers
2196 // Check for Confluence
2197 setHeadingConfluencePageName();
2201 QString VymModel::getURL()
2203 TreeItem *selti = getSelectedItem();
2205 return selti->getURL();
2210 QStringList VymModel::getURLs(bool ignoreScrolled)
2213 BranchItem *selbi = getSelectedBranch();
2214 BranchItem *cur = NULL;
2215 BranchItem *prev = NULL;
2216 nextBranch(cur, prev, true, selbi);
2218 if (!cur->getURL().isEmpty() &&
2219 !(ignoreScrolled && cur->hasScrolledParent()))
2220 urls.append(cur->getURL());
2221 nextBranch(cur, prev, true, selbi);
2226 void VymModel::setFrameType(const FrameObj::FrameType &t)
2228 BranchItem *bi = getSelectedBranch();
2230 BranchObj *bo = (BranchObj *)(bi->getLMO());
2232 QString s = bo->getFrameTypeName();
2233 bo->setFrameType(t);
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));
2239 bo->updateLinkGeometry();
2244 void VymModel::setFrameType(const QString &s)
2246 BranchItem *bi = getSelectedBranch();
2248 BranchObj *bo = (BranchObj *)(bi->getLMO());
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);
2257 bo->updateLinkGeometry();
2262 void VymModel::toggleFrameIncludeChildren()
2264 BranchItem *bi = getSelectedBranch();
2266 bool b = bi->getFrameIncludeChildren();
2267 setFrameIncludeChildren(!b);
2271 void VymModel::setFrameIncludeChildren(bool b)
2273 BranchItem *bi = getSelectedBranch();
2275 QString u = b ? "false" : "true";
2276 QString r = !b ? "false" : "true";
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);
2287 void VymModel::setFramePenColor(
2288 const QColor &c) // FIXME-4 not saved if there is no LMO
2291 BranchItem *bi = getSelectedBranch();
2293 BranchObj *bo = (BranchObj *)(bi->getLMO());
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);
2305 void VymModel::setFrameBrushColor(
2306 const QColor &c) // FIXME-4 not saved if there is no LMO
2308 BranchItem *bi = getSelectedBranch();
2310 BranchObj *bo = (BranchObj *)(bi->getLMO());
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
2320 emitDataChanged(bi); // Notify HeadingEditor to eventually change BG color
2324 void VymModel::setFramePadding(
2327 BranchItem *bi = getSelectedBranch();
2329 BranchObj *bo = (BranchObj *)(bi->getLMO());
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);
2338 bo->updateLinkGeometry();
2343 void VymModel::setFrameBorderWidth(
2346 BranchItem *bi = getSelectedBranch();
2348 BranchObj *bo = (BranchObj *)(bi->getLMO());
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);
2357 bo->updateLinkGeometry();
2362 void VymModel::setIncludeImagesVer(bool b)
2364 BranchItem *bi = getSelectedBranch();
2365 if (bi && b != bi->getIncludeImagesVer()) {
2366 QString u = b ? "false" : "true";
2367 QString r = !b ? "false" : "true";
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);
2379 void VymModel::setIncludeImagesHor(bool b)
2381 BranchItem *bi = getSelectedBranch();
2382 if (bi && b != bi->getIncludeImagesHor()) {
2383 QString u = b ? "false" : "true";
2384 QString r = !b ? "false" : "true";
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);
2396 void VymModel::setChildrenLayout(
2397 BranchItem::LayoutHint layoutHint) // FIXME-3 no savestate yet
2399 BranchItem *bi = getSelectedBranch();
2402 QString u= b ? "false" : "true";
2403 QString r=!b ? "false" : "true";
2407 QString("setIncludeImagesHorizontally (%1)").arg(u),
2409 QString("setIncludeImagesHorizontally (%1)").arg(r),
2410 QString("Include images horizontally in %1").arg(getObjectName(bi))
2413 bi->setChildrenLayout(layoutHint);
2414 emitDataChanged(bi);
2419 void VymModel::setHideLinkUnselected(bool b)
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";
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);
2434 void VymModel::setHideExport(bool b, TreeItem *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";
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))
2449 emitDataChanged(ti);
2450 emitSelectionChanged();
2455 void VymModel::toggleHideExport()
2457 QList<TreeItem *> selItems = getSelectedItems();
2458 if (selItems.count() > 0) {
2459 foreach (TreeItem *ti, selItems) {
2460 bool b = !ti->hideInExport();
2461 setHideExport(b, ti);
2466 void VymModel::toggleTask()
2468 BranchItem *selbi = getSelectedBranch();
2470 saveStateChangingPart(
2471 selbi, selbi, QString("toggleTask()"),
2472 QString("Toggle task of %1").arg(getObjectName(selbi)));
2473 Task *task = selbi->getTask();
2475 task = taskModel->createTask(selbi);
2476 taskEditor->select(task);
2479 taskModel->deleteTask(task);
2481 emitDataChanged(selbi);
2482 emitSelectionChanged();
2487 bool VymModel::cycleTaskStatus(bool reverse)
2489 BranchItem *selbi = getSelectedBranch();
2491 Task *task = selbi->getTask();
2493 saveStateChangingPart(
2494 selbi, selbi, QString("cycleTask()"),
2495 QString("Toggle task of %1").arg(getObjectName(selbi)));
2496 task->cycleStatus(reverse);
2497 task->setDateModification();
2499 // make sure task is still visible
2500 taskEditor->select(task);
2501 emitDataChanged(selbi);
2509 bool VymModel::setTaskSleep(const QString &s)
2512 BranchItem *selbi = getSelectedBranch();
2513 if (selbi && !s.isEmpty()) {
2514 Task *task = selbi->getTask();
2516 QDateTime oldSleep = task->getSleep();
2518 // Parse the string, which could be days, hours or one of several
2522 ok = task->setSecsSleep(0);
2525 QRegExp re("^\\s*(\\d+)\\s*$");
2526 re.setMinimal(false);
2527 int pos = re.indexIn(s);
2529 // Found only digit, considered as days
2530 ok = task->setDaysSleep(re.cap(1).toInt());
2533 QRegExp re("^\\s*(\\d+)\\s*h\\s*$");
2534 pos = re.indexIn(s);
2536 // Found digit followed by "h", considered as hours
2537 ok = task->setHoursSleep(re.cap(1).toInt());
2540 QRegExp re("^\\s*(\\d+)\\s*w\\s*$");
2541 pos = re.indexIn(s);
2543 // Found digit followed by "w", considered as weeks
2544 ok = task->setDaysSleep(7 * re.cap(1).toInt());
2547 QRegExp re("^\\s*(\\d+)\\s*s\\s*$");
2548 pos = re.indexIn(s);
2550 // Found digit followed by "s", considered as
2552 ok = task->setSecsSleep(re.cap(1).toInt());
2555 ok = task->setDateSleep(
2556 s); // ISO date YYYY-MM-DDTHH:mm:ss
2559 QRegExp re("(\\d+)\\.(\\d+)\\.(\\d+)");
2560 re.setMinimal(false);
2561 int pos = re.indexIn(s);
2562 QStringList list = re.capturedTexts();
2566 QDate(list.at(3).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,
2577 re.setPattern("(\\d+)\\.(\\d+)\\.");
2578 pos = re.indexIn(s);
2579 list = re.capturedTexts();
2581 int month = list.at(2).toInt();
2582 int day = list.at(1).toInt();
2584 QDate::currentDate().year();
2586 QDate(year, month, day).startOfDay());
2587 // d = QDate(year, month,
2588 // day).startOfDay();
2589 if (QDateTime::currentDateTime()
2593 QDate(year, month, day).startOfDay());
2594 // d = QDate(year, month,
2595 // day).startOfDay();
2597 ok = task->setDateSleep(
2598 d); // Short German format,
2602 re.setPattern("(\\d+)\\:(\\d+)");
2603 pos = re.indexIn(s);
2604 list = re.capturedTexts();
2606 int hour = list.at(1).toInt();
2607 int min = list.at(2).toInt();
2609 QDate::currentDate(),
2611 ok = task->setDateSleep(
2624 QString oldSleepString;
2625 if (oldSleep.isValid())
2626 oldSleepString = oldSleep.toString(Qt::ISODate);
2629 "1970-01-26T00:00:00"; // Some date long ago
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
2636 selbi, QString("setTaskSleep (\"%1\")").arg(oldSleepString),
2637 selbi, QString("setTaskSleep (\"%1\")").arg(newSleepString),
2638 QString("setTaskSleep (\"%1\")").arg(newSleepString));
2639 emitDataChanged(selbi);
2648 void VymModel::setTaskPriorityDelta(const int &pd, BranchItem *bi)
2650 QList<BranchItem *> selbis;
2654 selbis = getSelectedBranches();
2656 foreach (BranchItem *selbi, selbis) {
2657 Task *task = selbi->getTask();
2660 QString("setTaskPriorityDelta (%1)")
2661 .arg(task->getPriorityDelta()),
2663 QString("setTaskPriorityDelta (%1)")
2665 "Set delta for priority of task");
2666 task->setPriorityDelta(pd);
2667 emitDataChanged(selbi);
2672 int VymModel::getTaskPriorityDelta()
2674 BranchItem *selbi = getSelectedBranch();
2676 Task *task = selbi->getTask();
2678 return task->getPriorityDelta();
2683 int VymModel::taskCount() { return taskModel->count(this); }
2685 void VymModel::updateTasksAlarm(bool force)
2687 if (taskModel->updateAwake(force) || force) {
2692 BranchItem *VymModel::addTimestamp()
2694 BranchItem *selbi = addNewBranch();
2696 QDate today = QDate::currentDate();
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);
2709 void VymModel::copy()
2714 QList<TreeItem *> itemList = getSelectedItems();
2716 QStringList clipboardFiles;
2718 if (itemList.count() > 0) {
2721 foreach (TreeItem *ti, itemList) {
2722 fn = QString("%1/%2-%3.xml")
2726 QString content = saveToDir(clipboardDir, clipboardFile,
2727 FlagRowMaster::NoFlags, QPointF(), ti);
2729 if (!saveStringToDisk(fn, content))
2730 qWarning() << "ME::saveStringToDisk failed: " << fn;
2733 clipboardFiles.append(fn);
2736 QClipboard *clipboard = QApplication::clipboard();
2737 QMimeData *mimeData = new QMimeData;
2738 mimeData->setData("application/x-vym", clipboardFiles.join(",").toLatin1());
2739 clipboard->setMimeData(mimeData);
2743 void VymModel::paste()
2748 BranchItem *selbi = getSelectedBranch();
2751 const QClipboard *clipboard = QApplication::clipboard();
2752 const QMimeData *mimeData = clipboard->mimeData();
2754 if (mimeData->formats().contains("application/x-vym")) {
2755 QStringList clipboardFiles = QString(mimeData->data("application/x-vym")).split(",");
2757 saveStateChangingPart(selbi, selbi, QString("paste ()"),
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;
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";
2773 ImageItem *ii = loadImage(selbi, fn);
2775 setScaleFactor(300.0 / image.width(), ii); // FIXME-2 Better use user-defined fixed width
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...";
2786 qWarning() << "VM::paste Cannot paste data, mimeData->formats=" << mimeData->formats();
2791 void VymModel::cut()
2800 bool VymModel::moveUp(BranchItem *bi)
2805 bool oldState = saveStateBlocked;
2806 saveStateBlocked = true;
2807 bool result = false;
2808 if (bi && bi->canMoveUp())
2810 relinkBranch(bi, (BranchItem *)bi->parent(), bi->num() - 1, false);
2811 saveStateBlocked = oldState;
2815 void VymModel::moveUp()
2817 BranchItem *selbi = getSelectedBranch();
2819 QString oldsel = getSelectString(selbi);
2820 if (moveUp(selbi)) {
2821 saveState(getSelectString(selbi), "moveDown ()", oldsel,
2823 QString("Move up %1").arg(getObjectName(selbi)));
2829 bool VymModel::moveDown(BranchItem *bi)
2834 bool oldState = saveStateBlocked;
2835 saveStateBlocked = true;
2836 bool result = false;
2837 if (bi && bi->canMoveDown())
2839 relinkBranch(bi, (BranchItem *)bi->parent(), bi->num() + 1, false);
2840 saveStateBlocked = oldState;
2844 void VymModel::moveDown()
2846 BranchItem *selbi = getSelectedBranch();
2848 QString oldsel = getSelectString(selbi);
2849 if (moveDown(selbi)) {
2850 saveState(getSelectString(selbi), "moveUp ()", oldsel,
2852 QString("Move down %1").arg(getObjectName(selbi)));
2858 void VymModel::moveUpDiagonally()
2860 BranchItem *selbi = getSelectedBranch();
2862 BranchItem *parent = selbi->parentBranch();
2863 if (parent == rootItem) return;
2865 int n = selbi->num();
2868 BranchItem *dst = parent->getBranchNum(n-1);
2871 relinkBranch(selbi, dst, dst->branchCount() + 1, true);
2875 void VymModel::moveDownDiagonally()
2877 BranchItem *selbi = getSelectedBranch();
2879 BranchItem *parent = selbi->parentBranch();
2880 if (parent == rootItem) return;
2881 BranchItem *parentParent = parent->parentBranch();
2882 int n = parent->num();
2884 relinkBranch(selbi, parentParent, n + 1, true);
2888 void VymModel::detach()
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();
2897 QString oldsel = getSelectString();
2898 int n = selbi->num();
2900 BranchObj *bo = selbi->getBranchObj();
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)")
2911 oldsel, "detach ()",
2912 QString("Detach %1").arg(getObjectName(selbi)));
2916 void VymModel::sortChildren(bool inverse)
2918 BranchItem *selbi = getSelectedBranch();
2920 if (selbi->branchCount() > 1) {
2922 saveStateChangingPart(
2923 selbi, selbi, "sortChildren ()",
2924 QString("Sort children of %1").arg(getObjectName(selbi)));
2926 saveStateChangingPart(selbi, selbi, "sortChildren (false)",
2927 QString("Inverse sort children of %1")
2928 .arg(getObjectName(selbi)));
2930 selbi->sortChildren(inverse);
2937 BranchItem *VymModel::createMapCenter()
2939 BranchItem *newbi = addMapCenter(QPointF(0, 0));
2943 BranchItem *VymModel::createBranch(BranchItem *dst)
2946 return addNewBranchInt(dst, -2);
2951 ImageItem *VymModel::createImage(BranchItem *dst)
2957 ImageItem *newii = new ImageItem();
2958 // newii->setHeading (QApplication::translate("Heading of new image in
2959 // map", "new image"));
2961 emit(layoutAboutToBeChanged());
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);
2971 emit(layoutChanged());
2973 // save scroll state. If scrolled, automatically select
2974 // new branch in order to tmp unscroll parent...
2975 newii->createMapObj();
2976 latestAddedItem = newii;
2983 bool VymModel::createLink(Link *link)
2985 BranchItem *begin = link->getBeginBranch();
2986 BranchItem *end = link->getEndBranch();
2988 if (!begin || !end) {
2989 qWarning() << "VM::createXLinkNew part of XLink is NULL";
2995 qDebug() << "VymModel::createLink begin==end, aborting";
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";
3011 XLinkItem *newli = new XLinkItem();
3012 newli->setLink(link);
3013 link->setBeginLinkItem(newli);
3015 emit(layoutAboutToBeChanged());
3017 parix = index(begin);
3018 n = begin->getRowNumAppend(newli);
3019 beginInsertRows(parix, n, n);
3020 begin->appendChild(newli);
3023 newli = new XLinkItem();
3024 newli->setLink(link);
3025 link->setEndLinkItem(newli);
3028 n = end->getRowNumAppend(newli);
3029 beginInsertRows(parix, n, n);
3030 end->appendChild(newli);
3033 emit(layoutChanged());
3035 xlinks.append(link);
3038 latestAddedItem = newli;
3040 if (!link->getMO()) {
3041 link->createMapObj();
3047 link->setStyleBegin(defXLinkStyleBegin);
3048 link->setStyleEnd(defXLinkStyleEnd);
3052 QColor VymModel::getXLinkColor()
3054 Link *l = getSelectedXLink();
3056 return l->getPen().color();
3061 int VymModel::getXLinkWidth()
3063 Link *l = getSelectedXLink();
3065 return l->getPen().width();
3070 Qt::PenStyle VymModel::getXLinkStyle()
3072 Link *l = getSelectedXLink();
3074 return l->getPen().style();
3079 QString VymModel::getXLinkStyleBegin()
3081 Link *l = getSelectedXLink();
3083 return l->getStyleBeginString();
3088 QString VymModel::getXLinkStyleEnd()
3090 Link *l = getSelectedXLink();
3092 return l->getStyleEndString();
3097 AttributeItem *VymModel::setAttribute() // FIXME-3 Experimental, savestate missing
3100 BranchItem *selbi = getSelectedBranch();
3102 AttributeItem *ai = new AttributeItem();
3103 ai->setAttributeType(AttributeItem::String);
3104 ai->setKey("Foo Attrib");
3105 ai->setValue(QString("Att val"));
3107 return setAttribute(selbi, ai);
3112 AttributeItem *VymModel::setAttribute(BranchItem *dst, AttributeItem *ai_new)
3116 // Check if there is already an attribute with same key
3118 for (int i = 0; i < dst->attributeCount(); i++) {
3119 ai = dst->getAttributeNum(i);
3120 if (ai->getKey() == ai_new->getKey())
3122 // Key exists, overwrite value
3125 // Delete original attribute, this is basically a move...
3127 emitDataChanged(dst);
3132 // Create new attribute
3133 emit(layoutAboutToBeChanged());
3135 QModelIndex parix = index(dst);
3136 int n = dst->getRowNumAppend(ai_new);
3137 beginInsertRows(parix, n, n);
3138 dst->appendChild(ai_new);
3141 emit(layoutChanged());
3143 emitDataChanged(dst);
3144 return ai_new; // FIXME-3 Check if ai is used or deleted - deep copy here?
3149 BranchItem *VymModel::addMapCenter(bool saveStateFlag)
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();
3158 for (int i = 0; i < rootItem->branchCount(); ++i) {
3159 bi = rootItem->getBranchNum(i);
3160 bo = (BranchObj *)bi->getLMO();
3162 contextPos += bo->getAbsPos();
3164 if (rootItem->branchCount() > 1)
3165 contextPos *= 1 / (qreal)(rootItem->branchCount());
3168 BranchItem *bi = addMapCenter(contextPos);
3170 emitShowSelection();
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()));
3183 BranchItem *VymModel::addMapCenter(QPointF absPos)
3184 // createMapCenter could then probably be merged with createBranch
3188 QModelIndex parix = index(rootItem);
3190 BranchItem *newbi = new BranchItem(rootItem);
3191 newbi->setHeadingPlainText(tr("New map", "New map"));
3192 int n = rootItem->getRowNumAppend(newbi);
3194 emit(layoutAboutToBeChanged());
3195 beginInsertRows(parix, n, n);
3197 rootItem->appendChild(newbi);
3200 emit(layoutChanged());
3203 newbi->setPositionMode(MapItem::Absolute);
3204 BranchObj *bo = newbi->createMapObj(mapEditor->getScene());
3211 BranchItem *VymModel::addNewBranchInt(BranchItem *dst, int pos)
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
3220 BranchItem *parbi = dst;
3222 BranchItem *newbi = new BranchItem();
3224 emit(layoutAboutToBeChanged());
3227 n = parbi->getRowNumAppend(newbi);
3228 beginInsertRows(index(parbi), n, n);
3229 parbi->appendChild(newbi);
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);
3241 n = parbi->getRowNumAppend(newbi) - (parbi->branchCount() - pos);
3242 beginInsertRows(index(parbi), n, n);
3243 parbi->insertBranch(pos, newbi);
3246 emit(layoutChanged());
3248 newbi->createMapObj(mapEditor->getScene());
3250 // Set color of heading to that of parent
3251 newbi->setHeadingColor(parbi->getHeadingColor());
3257 BranchItem *VymModel::addNewBranch(BranchItem *bi, int pos)
3259 BranchItem *newbi = NULL;
3261 bi = getSelectedBranch();
3264 QString redosel = getSelectString(bi);
3265 newbi = addNewBranchInt(bi, pos);
3266 QString undosel = getSelectString(newbi);
3269 saveState(undosel, "remove ()", redosel,
3270 QString("addBranch (%1)").arg(pos),
3271 QString("Add new branch to %1").arg(getObjectName(bi)));
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();
3286 BranchItem *VymModel::addNewBranchBefore()
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
3293 // add below selection
3294 newbi = addNewBranchInt(selbi, -1);
3298 newbi, "remove ()", newbi, "addBranchBefore ()",
3299 QString("Add branch before %1").arg(getObjectName(selbi)));
3301 // newbi->move2RelPos (p);
3303 // Move selection to new branch
3304 relinkBranch(selbi, newbi, 0, true);
3306 // Use color of child instead of parent
3307 newbi->setHeadingColor(selbi->getHeadingColor());
3308 emitDataChanged(newbi);
3314 bool VymModel::relinkBranch(BranchItem *branch, BranchItem *dst, int pos,
3315 bool updateSelection, QPointF orgPos)
3317 if (branch && dst) {
3318 // Check if we relink to ourselves
3319 if (dst->isChildOf(branch))
3322 if (updateSelection)
3325 // Do we need to update frame type?
3326 bool keepFrame = true;
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());
3333 emit(layoutAboutToBeChanged());
3334 BranchItem *branchpi = (BranchItem *)branch->parent();
3335 // Remove at current position
3336 int n = branch->childNum();
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--;
3342 beginRemoveRows(index(branchpi), n, n);
3343 branchpi->removeChild(n);
3346 if (pos < 0 || pos > dst->branchCount())
3347 pos = dst->branchCount();
3349 // Append as last branch to dst
3350 if (dst->branchCount() == 0)
3353 n = dst->getFirstBranch()->childNumber();
3355 beginInsertRows(index(dst), n + pos, n + pos);
3356 dst->insertBranch(pos, branch);
3359 // Correct type if necessesary
3360 if (branch->getType() == TreeItem::MapCenter && branch->depth() > 0) {
3361 branch->setType(TreeItem::Branch);
3365 // reset parObj, fonts, frame, etc in related LMO or other view-objects
3366 branch->updateStyles(keepFrame);
3368 emitDataChanged(branch);
3369 reposition(); // both for moveUp/Down and relinking
3372 QString postSelStr = getSelectString(branch);
3373 QString postNum = QString::number(branch->num(), 10);
3376 LinkableMapObj *lmosel = branch->getLMO();
3378 savePos = lmosel->getAbsPos();
3380 if (!saveStateBlocked) { // Don't build strings when moving up/down
3382 "relinkTo (\"" + preParStr + "\"," + preNum + "," +
3383 QString("%1,%2").arg(orgPos.x()).arg(orgPos.y()) + ")";
3386 "relinkTo (\"" + getSelectString(dst) + "\"," + postNum + "," +
3387 QString("%1,%2").arg(savePos.x()).arg(savePos.y()) + ")";
3389 saveState(postSelStr, undoCom, preSelStr, redoCom,
3390 QString("Relink %1 to %2")
3391 .arg(getObjectName(branch))
3392 .arg(getObjectName(dst)));
3395 // New parent might be invisible
3396 branch->updateVisibility();
3398 if (dst->isScrolled()) {
3399 if (updateSelection)
3402 else if (updateSelection)
3409 bool VymModel::relinkImage(ImageItem *image, BranchItem *dst)
3412 emit(layoutAboutToBeChanged());
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);
3423 QModelIndex dstix = index(dst);
3424 n = dst->getRowNumAppend(image);
3425 beginInsertRows(dstix, n, n);
3426 dst->appendChild(image);
3429 // Set new parent also for lmo
3430 if (image->getLMO() && dst->getLMO())
3431 image->getLMO()->setParObj(dst->getLMO());
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)));
3442 bool VymModel::relinkTo(const QString &dest, int num, QPointF pos)
3444 TreeItem *selti = getSelectedItem();
3446 return false; // Nothing selected to relink
3448 TreeItem *dst = findBySelectString(dest);
3450 if (selti->isBranchLikeType()) {
3451 BranchItem *selbi = (BranchItem *)selti;
3453 return false; // Could not find destination
3455 if (dst->getType() == TreeItem::Branch) {
3456 // Now try to relink to branch
3457 if (relinkBranch(selbi, (BranchItem *)dst, num, true)) {
3458 emitSelectionChanged();
3462 return false; // Relinking failed
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();
3472 emitSelectionChanged();
3476 return false; // Relinking failed
3478 else if (selti->getType() == TreeItem::Image) {
3479 if (dst->isBranchLikeType())
3480 if (relinkImage(((ImageItem *)selti), (BranchItem *)dst))
3483 return false; // Relinking failed
3486 void VymModel::cleanupItems()
3488 while (!deleteLaterIDs.isEmpty()) {
3489 TreeItem *ti = findID(deleteLaterIDs.takeFirst());
3495 void VymModel::deleteLater(uint id)
3497 if (!deleteLaterIDs.contains(id))
3498 deleteLaterIDs.append(id);
3501 void VymModel::deleteSelection()
3503 QList<uint> selectedIDs = getSelectedIDs();
3507 foreach (uint id, selectedIDs) {
3508 TreeItem *ti = findID(id);
3510 if (ti->isBranchLikeType()) { // Delete branch
3511 BranchItem *selbi = (BranchItem *)ti;
3512 saveStateRemovingPart(
3513 selbi, QString("remove %1").arg(getObjectName(selbi)));
3515 BranchItem *pi = (BranchItem *)(deleteItem(selbi));
3517 if (pi->isScrolled() && pi->branchCount() == 0)
3519 emitDataChanged(pi);
3523 emitDataChanged(rootItem);
3527 // Delete other item
3528 TreeItem *pi = ti->parent();
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)));
3538 emitDataChanged(pi);
3544 "VymmModel::deleteSelection() unknown type?!");
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...
3555 BranchItem *selbi = getSelectedBranch();
3558 // Don't use this on mapcenter
3559 if (selbi->depth() < 1)
3562 pi = (BranchItem *)(selbi->parent());
3563 // Check if we have children at all to keep
3564 if (selbi->branchCount() == 0) {
3570 if (selbi->getLMO())
3571 p = selbi->getLMO()->getRelPos();
3573 saveStateChangingPart(pi, pi, "removeKeepChildren ()",
3574 QString("Remove %1 and keep its children")
3575 .arg(getObjectName(selbi)));
3577 QString sel = getSelectString(selbi);
3579 bool oldSaveState = saveStateBlocked;
3580 saveStateBlocked = true;
3581 int pos = selbi->num();
3582 BranchItem *bi = selbi->getFirstBranch();
3584 relinkBranch(bi, pi, pos, true);
3585 bi = selbi->getFirstBranch();
3590 emitDataChanged(pi);
3592 BranchObj *bo = getSelectedBranchObj();
3597 saveStateBlocked = oldSaveState;
3601 void VymModel::deleteChildren()
3604 BranchItem *selbi = getSelectedBranch();
3606 saveStateChangingPart(
3607 selbi, selbi, "removeChildren ()",
3608 QString("Remove children of branch %1").arg(getObjectName(selbi)));
3609 emit(layoutAboutToBeChanged());
3611 QModelIndex ix = index(selbi);
3612 int n = selbi->branchCount() - 1;
3613 beginRemoveRows(ix, 0, n);
3614 removeRows(0, n + 1, ix);
3616 if (selbi->isScrolled())
3617 unscrollBranch(selbi);
3618 emit(layoutChanged());
3623 TreeItem *VymModel::deleteItem(TreeItem *ti)
3626 TreeItem *pi = ti->parent();
3627 // qDebug()<<"VM::deleteItem start ti="<<ti<<" "<<ti->getHeading()<<"
3628 // pi="<<pi<<"="<<pi->getHeading();
3630 TreeItem::Type t = ti->getType();
3632 QModelIndex parentIndex = index(pi);
3634 emit(layoutAboutToBeChanged());
3636 int n = ti->childNum();
3637 beginRemoveRows(parentIndex, n, n);
3638 removeRows(n, 1, parentIndex);
3641 // Size of parent branch might change when deleting images
3642 if (t == TreeItem::Image) {
3643 BranchObj *bo = (BranchObj *)(((BranchItem *)pi)->getMO());
3650 emit(layoutChanged());
3651 emitUpdateQueries();
3652 if (!cleaningUpLinks)
3655 // qDebug()<<"VM::deleteItem end ti="<<ti;
3656 if (pi->depth() >= 0)
3662 void VymModel::deleteLink(Link *l)
3664 if (xlinks.removeOne(l))
3668 void VymModel::clearItem(TreeItem *ti)
3671 // Clear task (or other data in item itself)
3674 QModelIndex parentIndex = index(ti);
3675 if (!parentIndex.isValid())
3678 int n = ti->childCount();
3682 emit(layoutAboutToBeChanged());
3684 beginRemoveRows(parentIndex, 0, n - 1);
3685 removeRows(0, n, parentIndex);
3690 emit(layoutChanged());
3695 bool VymModel::scrollBranch(BranchItem *bi)
3698 if (bi->isScrolled())
3700 if (bi->branchCount() == 0)
3702 if (bi->depth() == 0)
3704 if (bi->toggleScroll()) {
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();
3713 mapEditor->getScene()
3714 ->update(); // Needed for _quick_ update, even in 1.13.x
3721 bool VymModel::unscrollBranch(BranchItem *bi)
3724 if (!bi->isScrolled())
3726 if (bi->toggleScroll()) {
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();
3735 mapEditor->getScene()
3736 ->update(); // Needed for _quick_ update, even in 1.13.x
3743 void VymModel::toggleScroll()
3745 BranchItem *selbi = getSelectedBranch();
3747 if (selbi->isScrolled())
3748 unscrollBranch(selbi);
3750 scrollBranch(selbi);
3751 // Note: saveState & reposition are called in above functions
3755 void VymModel::unscrollChildren()
3757 BranchItem *selbi = getSelectedBranch();
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);
3766 if (cur->isScrolled()) {
3767 cur->toggleScroll();
3768 emitDataChanged(cur);
3770 nextBranch(cur, prev, true, selbi);
3774 // Would this help??? emitSelectionChanged();
3778 void VymModel::setScaleFactor(qreal f, ImageItem *selii)
3781 selii = getSelectedImage();
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)));
3793 void VymModel::growSelectionSize() // FIXME-3 Also for heading in BranchItem?
3795 ImageItem *selii = getSelectedImage();
3798 qreal sx = selii->getScaleFactor();
3799 setScaleFactor(sx + f);
3803 void VymModel::shrinkSelectionSize()
3805 ImageItem *selii = getSelectedImage();
3808 qreal sx = selii->getScaleFactor();
3809 setScaleFactor(sx - f);
3813 void VymModel::resetSelectionSize()
3815 ImageItem *selii = getSelectedImage();
3820 void VymModel::emitExpandAll() { emit(expandAll()); }
3822 void VymModel::emitExpandOneLevel() { emit(expandOneLevel()); }
3824 void VymModel::emitCollapseOneLevel() { emit(collapseOneLevel()); }
3826 void VymModel::emitCollapseUnselected() { emit(collapseUnselected()); }
3828 void VymModel::toggleTarget()
3830 foreach (TreeItem *ti, getSelectedItems()) {
3831 if (ti->isBranchLikeType()) {
3832 ((BranchItem*)ti)->toggleTarget();
3833 saveState(ti, "toggleTarget()", ti, "toggleTarget()",
3840 ItemList VymModel::getLinkedMaps()
3844 // rmodel->setSearchString (s);
3846 BranchItem *cur = NULL;
3847 BranchItem *prev = NULL;
3848 nextBranch(cur, prev);
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+"), "");
3862 sl << cur->getVymLink();
3864 targets[cur->getID()] = sl;
3866 nextBranch(cur, prev);
3871 ItemList VymModel::getTargets()
3875 // rmodel->setSearchString (s);
3877 BranchItem *cur = NULL;
3878 BranchItem *prev = NULL;
3879 nextBranch(cur, prev);
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+"), "");
3893 targets[cur->getID()] = sl;
3895 nextBranch(cur, prev);
3900 Flag* VymModel::findFlagByName(const QString &name)
3902 BranchItem *bi = getSelectedBranch();
3905 Flag *f = standardFlagsMaster->findFlagByName(name);
3907 f = userFlagsMaster->findFlagByName(name);
3909 qWarning() << "VymModel::findFlagByName failed for flag named "
3917 // Nothing selected, so no flag found
3921 void VymModel::setFlagByName(const QString &name, bool useGroups)
3923 BranchItem *bi = getSelectedBranch();
3925 if (bi && !bi->hasActiveFlag(name)) {
3926 toggleFlagByName(name, useGroups);
3930 void VymModel::unsetFlagByName(const QString &name)
3932 BranchItem *bi = getSelectedBranch();
3934 if (bi && bi->hasActiveFlag(name)) {
3935 toggleFlagByName(name);
3939 void VymModel::toggleFlagByName(const QString &name, bool useGroups)
3941 BranchItem *bi = getSelectedBranch();
3944 Flag *f = findFlagByName(name);
3947 qWarning() << "VymModel::toggleFlagByName could not find flag named " << name;
3951 toggleFlagByUid(f->getUuid(), useGroups);
3955 void VymModel::toggleFlagByUid(
3958 // FIXME-2 saveState not correct when toggling flags in groups
3959 // (previous flags not saved!)
3961 QStringList itemList = getSelectedUUIDs();
3963 if (itemList.count() > 0) {
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);
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")
3981 .arg(getObjectName(bi)));
3982 emitDataChanged(bi);
3984 qWarning() << "VymModel::toggleFlag failed for flag with uid "
3993 void VymModel::clearFlags()
3995 BranchItem *selbi = getSelectedBranch();
3997 selbi->deactivateAllStandardFlags();
3999 emitDataChanged(selbi);
4004 void VymModel::colorBranch(QColor c)
4006 QList<BranchItem *> selbis = getSelectedBranches();
4007 foreach (BranchItem *selbi, selbis) {
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))
4015 selbi->setHeadingColor(c); // color branch
4016 emitDataChanged(selbi);
4017 taskEditor->showSelection();
4019 mapEditor->getScene()->update();
4022 void VymModel::colorSubtree(QColor c, BranchItem *b)
4024 QList<BranchItem *> selbis;
4028 selbis = getSelectedBranches();
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))
4036 BranchItem *prev = NULL;
4037 BranchItem *cur = NULL;
4038 nextBranch(cur, prev, true, bi);
4040 cur->setHeadingColor(c); // color links, color children
4041 emitDataChanged(cur);
4042 nextBranch(cur, prev, true, bi);
4045 taskEditor->showSelection();
4046 mapEditor->getScene()->update();
4049 QColor VymModel::getCurrentHeadingColor()
4051 BranchItem *selbi = getSelectedBranch();
4053 return selbi->getHeadingColor();
4055 QMessageBox::warning(
4057 "Can't get color of heading,\nthere's no branch selected");
4061 void VymModel::note2URLs()
4063 BranchItem *selbi = getSelectedBranch();
4065 saveStateChangingPart(
4066 selbi, selbi, QString("note2URLs()"),
4067 QString("Extract URLs from note of %1").arg(getObjectName(selbi)));
4069 QString n = selbi->getNoteASCII();
4072 QRegExp re("(http.*)(\\s|\"|')");
4073 re.setMinimal(true);
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();
4087 void VymModel::editHeading2URL()
4089 TreeItem *selti = getSelectedItem();
4091 setURL(selti->getHeadingPlain());
4094 void VymModel::getJiraData(bool subtree) // FIXME-2 update error message, check
4095 // if jiraClientAvail is set correctly
4097 if (!JiraAgent::available()) {
4099 QString w = QObject::tr("JIRA agent not setup.");
4101 dia.setWindowTitle( tr("Warning") + ": " + w);
4102 dia.setShowAgainName("/JiraAgent/notdefined");
4105 if (!mainWindow->settingsJIRA())
4109 BranchItem *selbi = getSelectedBranch();
4110 QRegExp re("(\\w+[-|\\s]\\d+)");
4114 BranchItem *prev = nullptr;
4115 BranchItem *cur = nullptr;
4116 nextBranch(cur, prev, true, selbi);
4118 QString heading = cur->getHeadingPlain();
4120 if (re.indexIn(heading) >= 0) {
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();
4129 if (!agent->setTicket(heading)) {
4130 mainWindow->statusMessage(tr("Could not find Jira ticket pattern in %1", "VymModel").arg(cur->getHeadingPlain()));
4135 //setURL(agent->url(), false, cur);
4137 connect(agent, &JiraAgent::jiraTicketReady, this, &VymModel::updateJiraData);
4139 // Start contacting JIRA in background
4141 mainWindow->statusMessage(tr("Contacting Jira...", "VymModel"));
4146 nextBranch(cur, prev, true, selbi);
4153 void VymModel::updateJiraData(QJsonObject jsobj)
4155 QJsonDocument jsdoc = QJsonDocument (jsobj);
4156 QString key = jsobj["key"].toString();
4157 QJsonObject fields = jsobj["fields"].toObject();
4159 QJsonObject assigneeObj = fields["assignee"].toObject();
4160 QString assignee = assigneeObj["emailAddress"].toString();
4162 QJsonObject reporterObj = fields["reporter"].toObject();
4163 QString reporter = reporterObj["emailAddress"].toString();
4165 QJsonObject resolutionObj = fields["resolution"].toObject();
4166 QString resolution = resolutionObj["name"].toString();
4168 QJsonObject statusObj = fields["status"].toObject();
4169 QString status = statusObj["name"].toString();
4171 QString summary = fields["summary"].toString();
4173 QJsonArray componentsArray = fields["components"].toArray();
4174 QJsonObject compObj;
4176 for (int i = 0; i < componentsArray.size(); ++i) {
4177 compObj = componentsArray[i].toObject();
4178 components += compObj["name"].toString();
4181 int branchID = jsobj["vymBranchId"].toInt();
4183 QStringList solvedStates;
4184 solvedStates << "Verification Done";
4185 solvedStates << "Resolved";
4186 solvedStates << "Closed";
4188 QString keyName = key;
4189 BranchItem *bi = (BranchItem*)findID(branchID);
4191 if (solvedStates.contains(status)) {
4192 keyName = "(" + keyName + ")";
4193 colorSubtree (Qt::blue, bi);
4196 setHeadingPlainText(keyName + ": " + summary, bi);
4197 setURL(jsobj["vymTicketUrl"].toString());
4201 ai = new AttributeItem("JIRA.assignee", assignee);
4202 setAttribute(bi, ai);
4204 ai = new AttributeItem("JIRA.reporter", reporter);
4205 setAttribute(bi, ai);
4207 ai = new AttributeItem("JIRA.resolution", resolution);
4208 setAttribute(bi, ai);
4210 ai = new AttributeItem("JIRA.status", status);
4211 setAttribute(bi, ai);
4213 ai = new AttributeItem("JIRA.components", components);
4214 setAttribute(bi, ai);
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;
4228 mainWindow->statusMessage(tr("Received Jira data.", "VymModel"));
4232 void VymModel::setHeadingConfluencePageName() // FIXME-2 always asks for Confluence credentials when adding any URL
4234 BranchItem *selbi = getSelectedBranch();
4236 QString url = selbi->getURL();
4237 if (!url.isEmpty() &&
4238 settings.contains("/atlassian/confluence/url") &&
4239 url.contains(settings.value("/atlassian/confluence/url").toString())) {
4241 ConfluenceAgent *ca_setHeading = new ConfluenceAgent(selbi);
4242 ca_setHeading->setPageURL(url);
4243 ca_setHeading->setJobType(ConfluenceAgent::CopyPagenameToHeading);
4244 ca_setHeading->startJob();
4249 void VymModel::setVymLink(const QString &s)
4251 if (s.isEmpty()) return;
4253 BranchItem *bi = getSelectedBranch();
4256 bi, "setVymLink (\"" + bi->getVymLink() + "\")", bi,
4257 "setVymLink (\"" + s + "\")",
4258 QString("Set vymlink of %1 to %2").arg(getObjectName(bi)).arg(s));
4260 emitDataChanged(bi);
4265 void VymModel::deleteVymLink()
4267 BranchItem *bi = getSelectedBranch();
4269 saveState(bi, "setVymLink (\"" + bi->getVymLink() + "\")", bi,
4270 "setVymLink (\"\")",
4271 QString("Unset vymlink of %1").arg(getObjectName(bi)));
4273 emitDataChanged(bi);
4279 QString VymModel::getVymLink()
4281 BranchItem *bi = getSelectedBranch();
4283 return bi->getVymLink();
4288 QStringList VymModel::getVymLinks()
4291 BranchItem *selbi = getSelectedBranch();
4292 BranchItem *cur = NULL;
4293 BranchItem *prev = NULL;
4294 nextBranch(cur, prev, true, selbi);
4296 if (!cur->getVymLink().isEmpty())
4297 links.append(cur->getVymLink());
4298 nextBranch(cur, prev, true, selbi);
4303 void VymModel::followXLink(int i)
4305 BranchItem *selbi = getSelectedBranch();
4307 selbi = selbi->getXLinkItemNum(i)->getPartnerBranch();
4313 void VymModel::editXLink()
4315 Link *l = getSelectedXLink();
4317 EditXLinkDialog dia;
4319 if (dia.exec() == QDialog::Accepted) {
4320 if (dia.useSettingsGlobal()) {
4321 setMapDefXLinkPen(l->getPen());
4322 setMapDefXLinkStyleBegin(l->getStyleBeginString());
4323 setMapDefXLinkStyleEnd(l->getStyleEndString());
4329 void VymModel::setXLinkColor(const QString &new_col)
4331 Link *l = getSelectedXLink();
4333 QPen pen = l->getPen();
4334 QColor new_color = QColor(new_col);
4335 QColor old_color = pen.color();
4336 if (new_color == old_color)
4338 pen.setColor(new_color);
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()));
4348 void VymModel::setXLinkStyle(const QString &new_style)
4350 Link *l = getSelectedXLink();
4352 QPen pen = l->getPen();
4353 QString old_style = penStyleToString(pen.style());
4354 if (new_style == old_style)
4357 pen.setStyle(penStyle(new_style, ok));
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));
4367 void VymModel::setXLinkStyleBegin(const QString &new_style)
4369 Link *l = getSelectedXLink();
4371 QString old_style = l->getStyleBeginString();
4372 if (new_style == old_style)
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");
4383 void VymModel::setXLinkStyleEnd(const QString &new_style)
4385 Link *l = getSelectedXLink();
4387 QString old_style = l->getStyleEndString();
4388 if (new_style == old_style)
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");
4399 void VymModel::setXLinkWidth(int new_width)
4401 Link *l = getSelectedXLink();
4403 QPen pen = l->getPen();
4404 int old_width = pen.width();
4405 if (new_width == old_width)
4407 pen.setWidth(new_width);
4410 l->getBeginLinkItem(), QString("setXLinkWidth(%1)").arg(old_width),
4411 l->getBeginLinkItem(), QString("setXLinkWidth(%1)").arg(new_width),
4412 "set width of xlink");
4416 //////////////////////////////////////////////
4418 //////////////////////////////////////////////
4420 QVariant VymModel::execute(
4421 const QString &script) // FIXME-3 still required???
4422 // Called from these places:
4424 // scripts/vym-ruby.rb (and adaptormodel) used for
4425 // testing Main::callMacro Main::checkReleaseNotes
4428 // VymModel::exportLast
4429 // VymModel::updateSlideSelection
4431 // qDebug()<<"VM::execute called: "<<script;
4432 return mainWindow->runScript(script);
4435 void VymModel::setExportMode(bool b)
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);
4442 setHideTmpMode(TreeItem::HideNone);
4445 QPointF VymModel::exportImage(QString fname, bool askName, QString format)
4447 QPointF offset; // set later, when getting image from MapEditor
4451 qWarning("VymModel::exportImage called without filename (and "
4456 fname = lastImageDir.absolutePath() + "/" + getMapName() + ".png";
4461 ex.setName("Image");
4463 ex.setFilePath(fname);
4464 ex.setWindowTitle(tr("Export map as image"));
4466 "PNG (*.png);;All (* *.*)"); // imageIO.getFilters().join(";;")
4468 settings.localValue(filePath, "/export/last/command", "").toString());
4471 if (!ex.execDialog())
4473 fname = ex.getFilePath();
4474 lastImageDir = QDir(fname);
4477 setExportMode(true);
4479 mapEditor->minimizeView();
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);
4488 ex.setResult(ExportBase::Success);
4490 setExportMode(false);
4492 ex.completeExport();
4497 void VymModel::exportPDF(QString fname, bool askName)
4501 qWarning("VymModel::exportPDF called without filename (and "
4506 fname = lastExportDir.absolutePath() + "/" + getMapName() + ".pdf";
4512 ex.setFilePath(fname);
4513 ex.setWindowTitle(tr("Export map as PDF"));
4514 ex.addFilter("PDF (*.pdf);;All (* *.*)");
4516 settings.localValue(filePath, "/export/last/command", "").toString());
4519 if (!ex.execDialog())
4521 fname = ex.getFilePath();
4524 setExportMode(true);
4527 QPrinter pdfPrinter(QPrinter::HighResolution);
4528 pdfPrinter.setOutputFormat(QPrinter::PdfFormat);
4529 pdfPrinter.setOutputFileName(fname);
4530 pdfPrinter.setPageSize(QPageSize(QPageSize::A3));
4532 QRectF bbox = mapEditor->getTotalBBox();
4533 if (bbox.width() > bbox.height())
4534 // recommend landscape
4535 pdfPrinter.setPageOrientation(QPageLayout::Landscape);
4537 // recommend portrait
4538 pdfPrinter.setPageOrientation(QPageLayout::Portrait);
4540 QPainter *pdfPainter = new QPainter(&pdfPrinter);
4541 getScene()->render(pdfPainter);
4545 setExportMode(false);
4547 ex.completeExport();
4550 QPointF VymModel::exportSVG(QString fname, bool askName)
4552 QPointF offset; // FIXME-3 not needed?
4556 qWarning("VymModel::exportSVG called without filename (and "
4561 fname = lastImageDir.absolutePath() + "/" + getMapName() + ".svg";
4567 ex.setFilePath(fname);
4568 ex.setWindowTitle(tr("Export map as SVG"));
4569 ex.addFilter("SVG (*.svg);;All (* *.*)");
4572 if (!ex.execDialog())
4574 fname = ex.getFilePath();
4575 lastImageDir = QDir(fname);
4578 setExportMode(true);
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);
4590 setExportMode(false);
4591 ex.completeExport();
4596 void VymModel::exportXML(QString fpath, QString dpath, bool useDialog)
4601 ex.setWindowTitle(tr("Export map as XML"));
4602 ex.addFilter("XML (*.xml);;All (* *.*)");
4604 settings.localValue(filePath, "/export/last/command", "").toString());
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");
4617 if (fd.exec() != QDialog::Accepted || fd.selectedFiles().isEmpty())
4620 fpath = fd.selectedFiles().first();
4621 dpath = fpath.left(fpath.lastIndexOf("/"));
4623 if (!confirmDirectoryOverwrite(QDir(dpath)))
4626 ex.setFilePath(fpath);
4628 QString mname = basename(fpath);
4630 // Hide stuff during export, if settings want this
4631 setExportMode(true);
4633 // Create subdirectories
4636 // write image and calculate offset (Remember old mapSaved setting while
4638 bool mchanged = mapChanged;
4639 bool munsaved = mapUnsaved;
4642 exportImage(dpath + "/images/" + mname + ".png", false, "PNG");
4644 mapChanged = mchanged;
4645 mapUnsaved = munsaved;
4647 // write to directory //FIXME-3 check totalBBox here...
4649 saveToDir(dpath, mname + "-", FlagRowMaster::NoFlags, offset, NULL);
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()));
4661 // Write it finally, and write in UTF8, no matter what
4662 QTextStream ts(&file);
4663 ts.setCodec("UTF-8");
4667 setExportMode(false);
4672 ex.completeExport(args);
4675 void VymModel::exportAO(QString fname, bool askName)
4680 settings.localValue(filePath, "/export/last/command", "").toString());
4683 ex.setFilePath(mapName + ".txt");
4685 ex.setFilePath(fname);
4688 ex.setDirPath(lastExportDir.absolutePath());
4691 if (!ex.canceled()) {
4692 setExportMode(true);
4694 setExportMode(false);
4698 void VymModel::exportASCII(const QString &fname, bool listTasks, bool askName)
4702 ex.setListTasks(listTasks);
4704 settings.localValue(filePath, "/export/last/command", "").toString());
4707 ex.setFilePath(mapName + ".txt");
4709 ex.setFilePath(fname);
4712 ex.setDirPath(lastExportDir.absolutePath());
4716 if (!ex.canceled()) {
4717 setExportMode(true);
4719 setExportMode(false);
4723 void VymModel::exportCSV(const QString &fname, bool askName)
4728 settings.localValue(filePath, "/export/last/command", "").toString());
4731 ex.setFilePath(mapName + ".csv");
4733 ex.setFilePath(fname);
4736 ex.addFilter("CSV (*.csv);;All (* *.*)");
4737 ex.setDirPath(lastExportDir.absolutePath());
4738 ex.setWindowTitle(vymName + " -" + tr("Export as csv") + " " +
4739 tr("(still experimental)"));
4743 if (!ex.canceled()) {
4744 setExportMode(true);
4746 setExportMode(false);
4750 void VymModel::exportFirefoxBookmarks(const QString &fname, bool askName)
4755 settings.localValue(filePath, "/export/last/command", "").toString());
4758 ex.setFilePath(mapName + ".csv");
4760 ex.setFilePath(fname);
4763 ex.addFilter("JSON (*.json);;All (* *.*)");
4764 ex.setDirPath(lastExportDir.absolutePath());
4765 ex.setWindowTitle(vymName + " -" + tr("Export as csv") + " " +
4766 tr("(still experimental)"));
4770 if (!ex.canceled()) {
4771 setExportMode(true);
4773 setExportMode(false);
4777 void VymModel::exportHTML(const QString &fpath, const QString &dpath,
4780 ExportHTML ex(this);
4782 settings.localValue(filePath, "/export/last/command", "").toString());
4784 if (!dpath.isEmpty())
4785 ex.setDirPath(dpath);
4786 if (!fpath.isEmpty())
4787 ex.setFilePath(fpath);
4789 ex.doExport(useDialog);
4792 void VymModel::exportConfluence(bool createPage, const QString &pageURL,
4793 const QString &pageName, bool useDialog)
4795 ExportConfluence ex(this);
4796 ex.setCreateNewPage(createPage);
4798 ex.setPageName(pageName);
4800 settings.localValue(filePath, "/export/last/command", "").toString());
4802 ex.doExport(useDialog);
4805 void VymModel::exportImpress(const QString &fn, const QString &cf)
4811 settings.localValue(filePath, "/export/last/command", "").toString());
4813 if (ex.setConfigFile(cf)) {
4814 QString lastCommand =
4815 settings.localValue(filePath, "/export/last/command", "")
4818 setExportMode(true);
4819 ex.exportPresentation();
4820 setExportMode(false);
4823 settings.localValue(filePath, "/export/last/command", "")
4825 if (lastCommand != command)
4830 bool VymModel::exportLastAvailable(QString &description, QString &command,
4834 settings.localValue(filePath, "/export/last/command", "").toString();
4836 description = settings.localValue(filePath, "/export/last/description", "")
4838 dest = settings.localValue(filePath, "/export/last/displayedDestination", "")
4840 if (!command.isEmpty() && command.contains("exportMap"))
4846 void VymModel::exportLast()
4848 QString desc, command,
4849 dest; // FIXME-3 better integrate configFile into command
4850 if (exportLastAvailable(desc, command, dest)) {
4851 //qDebug() << "VM::exportLast: " << command;
4856 void VymModel::exportLaTeX(const QString &fname, bool askName)
4861 settings.localValue(filePath, "/export/last/command", "").toString());
4864 ex.setFilePath(mapName + ".tex");
4866 ex.setFilePath(fname);
4870 if (!ex.canceled()) {
4871 setExportMode(true);
4873 setExportMode(false);
4877 void VymModel::exportOrgMode(const QString &fname, bool askName)
4882 settings.localValue(filePath, "/export/last/command", "").toString());
4885 ex.setFilePath(mapName + ".org");
4887 ex.setFilePath(fname);
4890 ex.setDirPath(lastExportDir.absolutePath());
4894 if (!ex.canceled()) {
4895 setExportMode(true);
4897 setExportMode(false);
4901 void VymModel::exportMarkdown(const QString &fname, bool askName)
4906 settings.localValue(filePath, "/export/last/command", "").toString());
4909 ex.setFilePath(mapName + ".md");
4911 ex.setFilePath(fname);
4914 ex.setDirPath(lastExportDir.absolutePath());
4918 if (!ex.canceled()) {
4919 setExportMode(true);
4921 setExportMode(false);
4924 //////////////////////////////////////////////
4926 //////////////////////////////////////////////
4928 void VymModel::registerMapEditor(QWidget *e) { mapEditor = (MapEditor *)e; }
4930 void VymModel::setMapZoomFactor(const double &d)
4933 mapEditor->setZoomFactorTarget(d);
4936 void VymModel::setMapRotationAngle(const double &d)
4939 mapEditor->setAngleTarget(d);
4942 void VymModel::setMapAnimDuration(const int &d) { animDuration = d; }
4944 void VymModel::setMapAnimCurve(const QEasingCurve &c) { animCurve = c; }
4946 bool VymModel::centerOnID(const QString &id)
4948 TreeItem *ti = findUuid(QUuid(id));
4950 LinkableMapObj *lmo = ((MapItem *)ti)->getLMO();
4951 if (zoomFactor > 0 && lmo) {
4952 mapEditor->setViewCenterTarget(lmo->getBBox().center(), zoomFactor,
4953 rotationAngle, animDuration,
4961 void VymModel::setContextPos(QPointF p)
4964 hasContextPos = true;
4967 void VymModel::unsetContextPos()
4969 contextPos = QPointF();
4970 hasContextPos = false;
4973 void VymModel::reposition()
4975 if (repositionBlocked)
4979 for (int i = 0; i < rootItem->branchCount(); i++) {
4980 bo = rootItem->getBranchObjNum(i);
4982 bo->reposition(); // for positioning heading
4984 qDebug() << "VM::reposition bo=0";
4986 mapEditor->getTotalBBox();
4988 // required to *reposition* the selection box. size is already correct:
4989 emitSelectionChanged(); //FIXME-2 better only update selection geometry
4992 bool VymModel::setMapLinkStyle(const QString &s)
4995 switch (linkstyle) {
4996 case LinkableMapObj::Line:
4999 case LinkableMapObj::Parabel:
5000 snow = "StyleParabel";
5002 case LinkableMapObj::PolyLine:
5003 snow = "StylePolyLine";
5005 case LinkableMapObj::PolyParabel:
5006 snow = "StylePolyParabel";
5013 saveState(QString("setMapLinkStyle (\"%1\")").arg(s),
5014 QString("setMapLinkStyle (\"%1\")").arg(snow),
5015 QString("Set map link style (\"%1\")").arg(s));
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;
5026 linkstyle = LinkableMapObj::UndefinedStyle;
5028 BranchItem *cur = NULL;
5029 BranchItem *prev = NULL;
5031 nextBranch(cur, prev);
5033 bo = (BranchObj *)(cur->getLMO());
5034 bo->setLinkStyle(bo->getDefLinkStyle(
5035 cur->parent())); // FIXME-4 better emit dataCHanged and leave the
5037 nextBranch(cur, prev);
5043 LinkableMapObj::Style VymModel::getMapLinkStyle() { return linkstyle; }
5045 uint VymModel::getModelID() { return modelID; }
5047 void VymModel::setView(VymView *vv) { vymView = vv; }
5049 void VymModel::setMapDefLinkColor(QColor col)
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()));
5060 // Set color for "link arrows" in TreeEditor
5061 vymView->setLinkColor(col);
5063 BranchItem *cur = NULL;
5064 BranchItem *prev = NULL;
5066 nextBranch(cur, prev);
5068 bo = (BranchObj *)(cur->getLMO());
5071 for (int i = 0; i < cur->imageCount(); ++i)
5072 cur->getImageNum(i)->getLMO()->setLinkColor();
5074 nextBranch(cur, prev);
5079 void VymModel::setMapLinkColorHintInt()
5081 // called from setMapLinkColorHint(lch) or at end of parse
5082 BranchItem *cur = NULL;
5083 BranchItem *prev = NULL;
5085 nextBranch(cur, prev);
5087 bo = (BranchObj *)(cur->getLMO());
5090 for (int i = 0; i < cur->imageCount(); ++i)
5091 cur->getImageNum(i)->getLMO()->setLinkColor();
5093 nextBranch(cur, prev);
5097 void VymModel::setMapLinkColorHint(LinkableMapObj::ColorHint lch)
5099 linkcolorhint = lch;
5100 setMapLinkColorHintInt();
5103 void VymModel::toggleMapLinkColorHint()
5105 if (linkcolorhint == LinkableMapObj::HeadingColor)
5106 linkcolorhint = LinkableMapObj::DefaultColor;
5108 linkcolorhint = LinkableMapObj::HeadingColor;
5109 BranchItem *cur = NULL;
5110 BranchItem *prev = NULL;
5112 nextBranch(cur, prev);
5114 bo = (BranchObj *)(cur->getLMO());
5117 for (int i = 0; i < cur->imageCount(); ++i)
5118 cur->getImageNum(i)->getLMO()->setLinkColor();
5120 nextBranch(cur, prev);
5125 selectMapBackgroundImage() // FIXME-3 for using background image:
5126 // view.setCacheMode(QGraphicsView::CacheBackground);
5127 // Also this belongs into ME
5129 QStringList filters;
5130 filters << tr("Images") +
5131 " (*.png *.bmp *.xbm *.jpg *.png *.xpm *.gif *.pnm)";
5133 fd.setFileMode(QFileDialog::ExistingFile);
5134 fd.setWindowTitle(vymName + " - " + tr("Load background image"));
5135 fd.setDirectory(lastImageDir);
5136 fd.setAcceptMode(QFileDialog::AcceptOpen);
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());
5145 void VymModel::setMapBackgroundImage(
5146 const QString &fn) // FIXME-3 missing savestate, move to ME
5149 QColor oldcol=mapEditor->getScene()->backgroundBrush().color();
5152 QString ("setMapBackgroundImage (%1)").arg(oldcol.name()),
5154 QString ("setMapBackgroundImage (%1)").arg(col.name()),
5155 QString("Set background color of map to %1").arg(col.name()));
5158 brush.setTextureImage(QImage(fn));
5159 mapEditor->getScene()->setBackgroundBrush(brush);
5162 void VymModel::selectMapBackgroundColor()
5164 QColor col = QColorDialog::getColor(
5165 mapEditor->getScene()->backgroundBrush().color(), NULL);
5168 setMapBackgroundColor(col);
5171 void VymModel::setMapBackgroundColor(QColor col)
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);
5181 QColor VymModel::getMapBackgroundColor() // FIXME-4 move to ME
5183 return mapEditor->getScene()->backgroundBrush().color();
5186 QFont VymModel::getMapDefaultFont() { return defaultFont; }
5188 void VymModel::setMapDefaultFont(const QFont &f) { defaultFont = f; }
5190 LinkableMapObj::ColorHint VymModel::getMapLinkColorHint() // FIXME-4 move to ME
5192 return linkcolorhint;
5195 QColor VymModel::getMapDefLinkColor() // FIXME-4 move to ME
5197 return defLinkColor;
5200 void VymModel::setMapDefXLinkPen(const QPen &p) // FIXME-4 move to ME
5205 QPen VymModel::getMapDefXLinkPen() // FIXME-4 move to ME
5210 void VymModel::setMapDefXLinkStyleBegin(const QString &s)
5212 defXLinkStyleBegin = s;
5215 QString VymModel::getMapDefXLinkStyleBegin() { return defXLinkStyleBegin; }
5217 void VymModel::setMapDefXLinkStyleEnd(const QString &s)
5219 defXLinkStyleEnd = s;
5222 QString VymModel::getMapDefXLinkStyleEnd() { return defXLinkStyleEnd; }
5224 void VymModel::move(const double &x, const double &y)
5226 MapItem *seli = (MapItem *)getSelectedItem();
5228 (seli->isBranchLikeType() || seli->getType() == TreeItem::Image)) {
5229 LinkableMapObj *lmo = seli->getLMO();
5231 QPointF ap(lmo->getAbsPos());
5234 QString ps = qpointFToString(ap);
5235 QString s = getSelectString(seli);
5237 s, "move " + ps, s, "move " + qpointFToString(to),
5238 QString("Move %1 to %2").arg(getObjectName(seli)).arg(ps));
5241 emitSelectionChanged();
5247 void VymModel::moveRel(const double &x, const double &y)
5249 MapItem *seli = (MapItem *)getSelectedItem();
5251 (seli->isBranchLikeType() || seli->getType() == TreeItem::Image)) {
5252 LinkableMapObj *lmo = seli->getLMO();
5254 QPointF rp(lmo->getRelPos());
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))
5264 ((OrnamentedObj *)lmo)->move2RelPos(x, y);
5266 lmo->updateLinkGeometry();
5267 emitSelectionChanged();
5273 void VymModel::animate()
5275 animationTimer->stop();
5278 while (i < animObjList.size()) {
5279 bo = (BranchObj *)animObjList.at(i);
5280 if (!bo->animate()) {
5282 animObjList.removeAt(i);
5289 emitSelectionChanged();
5291 if (!animObjList.isEmpty())
5292 animationTimer->start(animationInterval);
5295 void VymModel::startAnimation(BranchObj *bo, const QPointF &v)
5300 if (bo->getUseRelPos())
5301 startAnimation(bo, bo->getRelPos(), bo->getRelPos() + v);
5303 startAnimation(bo, bo->getAbsPos(), bo->getAbsPos() + v);
5306 void VymModel::startAnimation(BranchObj *bo, const QPointF &start,
5307 const QPointF &dest)
5311 if (bo && bo->getTreeItem()->depth() >= 0) {
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);
5325 void VymModel::stopAnimation(MapObj *mo)
5327 int i = animObjList.indexOf(mo);
5329 animObjList.removeAt(i);
5332 void VymModel::stopAllAnimation()
5336 while (i < animObjList.size()) {
5337 bo = (BranchObj *)animObjList.at(i);
5338 bo->stopAnimation();
5339 bo->requestReposition();
5345 void VymModel::sendSelection()
5347 if (netstate != Server)
5349 sendData(QString("select (\"%1\")").arg(getSelectString()));
5352 void VymModel::newServer()
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();
5364 connect(tcpServer, SIGNAL(newConnection()), this, SLOT(newClient()));
5366 qDebug() << "Server is running on port " << tcpServer->serverPort();
5369 void VymModel::connectToServer()
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)));
5381 qDebug() << "connected to " << qPrintable(server) << " port " << port;
5384 void VymModel::newClient()
5386 QTcpSocket *newClient = tcpServer->nextPendingConnection();
5387 connect(newClient, SIGNAL(disconnected()), newClient, SLOT(deleteLater()));
5389 qDebug() << "ME::newClient at "
5390 << qPrintable(newClient->peerAddress().toString());
5392 clientList.append(newClient);
5395 void VymModel::sendData(const QString &s)
5397 if (clientList.size() == 0)
5400 // Create bytearray to send
5402 QDataStream out(&block, QIODevice::WriteOnly);
5403 out.setVersion(QDataStream::Qt_4_0);
5405 // Reserve some space for blocksize
5408 // Write sendCounter
5409 out << sendCounter++;
5414 // Go back and write blocksize so far
5415 out.device()->seek(0);
5416 quint16 bs = (quint16)(block.size() - 2 * sizeof(quint16));
5420 qDebug() << "ME::sendData bs=" << bs << " counter=" << sendCounter
5421 << " s=" << qPrintable(s);
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);
5430 void VymModel::readData()
5432 while (clientSocket->bytesAvailable() >= (int)sizeof(quint16)) {
5434 qDebug() << "readData bytesAvail="
5435 << clientSocket->bytesAvailable();
5439 QDataStream in(clientSocket);
5440 in.setVersion(QDataStream::Qt_4_0);
5448 qDebug() << "VymModel::readData command=" << qPrintable(t);
5451 // parseAtom (t,noErr,errMsg); //FIXME-4 needs rework using scripts
5456 void VymModel::displayNetworkError(QAbstractSocket::SocketError socketError)
5458 switch (socketError) {
5459 case QAbstractSocket::RemoteHostClosedError:
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.");
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.");
5474 QMessageBox::information(NULL, vymName + " Network client",
5475 QString("The following error occurred: %1.")
5476 .arg(clientSocket->errorString()));
5480 void VymModel::downloadImage(const QUrl &url, BranchItem *bi)
5483 bi = getSelectedBranch();
5485 qWarning("VM::download bi==NULL");
5489 // FIXME-3 download img to tmpfile and delete after running script in
5492 script += QString("m = vym.currentMap();m.selectID(\"%1\");")
5493 .arg(bi->getUuid().toString());
5494 script += QString("m.loadImage(\"$TMPFILE\");");
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()));
5503 void VymModel::selectMapSelectionColor() // FIXME-2 move out of VymModel, consider Pen/Brush
5505 QColor col = QColorDialog::getColor(defLinkColor, NULL);
5506 setSelectionPenColor(col);
5507 setSelectionBrushColor(col);
5510 void VymModel::emitSelectionChanged(const QItemSelection &newsel)
5512 emit(selectionChanged(newsel,
5513 newsel)); // needed e.g. to update geometry in editor
5517 void VymModel::emitSelectionChanged()
5519 QItemSelection newsel = selModel->selection();
5520 emitSelectionChanged(newsel);
5523 void VymModel::setSelectionPenColor(QColor col)
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()));
5534 selPen.setColor(col);
5535 mapEditor->setSelectionPen(selPen);
5538 QColor VymModel::getSelectionPenColor() {
5539 return mapEditor->getSelectionPen().color();
5542 void VymModel::setSelectionPenWidth(qreal w)
5544 QPen selPen = mapEditor->getSelectionPen();
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));
5552 mapEditor->setSelectionPen(selPen);
5553 //vymView->setSelectionColor(col);
5556 qreal VymModel::getSelectionPenWidth() {
5557 return mapEditor->getSelectionPen().width();
5560 void VymModel::setSelectionBrushColor(QColor col)
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()));
5571 selBrush.setColor(col);
5572 vymView->setSelectionBrush(selBrush);
5575 QColor VymModel::getSelectionBrushColor() {
5576 return mapEditor->getSelectionBrush().color();
5579 bool VymModel::initIterator(const QString &iname, bool deepLevelsFirst)
5581 Q_UNUSED(deepLevelsFirst);
5583 // Remove existing iterators first
5584 selIterCur.remove(iname);
5585 selIterPrev.remove(iname);
5586 selIterStart.remove(iname);
5587 selIterActive.remove(iname);
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());
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;
5607 bool VymModel::nextIterator(const QString &iname)
5609 if (selIterCur.keys().indexOf(iname) < 0) {
5611 << QString("VM::nextIterator couldn't find %1 in hash of iterators")
5616 BranchItem *cur = (BranchItem *)(findUuid(selIterCur.value(iname)));
5618 qWarning() << "VM::nextIterator couldn't find cur" << selIterCur;
5622 qDebug() << " " << iname << "selecting " << cur->getHeadingPlain();
5625 if (!selIterActive.value(iname)) {
5626 // Select for the first time
5628 selIterActive[iname] = true;
5632 BranchItem *prev = (BranchItem *)(findUuid(selIterPrev.value(iname)));
5633 BranchItem *start = (BranchItem *)(findUuid(selIterStart.value(iname)));
5635 qWarning() << "VM::nextIterator couldn't find prev"
5636 << selIterPrev.value(iname);
5638 qWarning() << "VM::nextIterator couldn't find start "
5639 << selIterStart.value(iname);
5641 if (cur && prev && start) {
5642 nextBranch(cur, prev, false, start);
5644 selIterCur[iname] = cur->getUuid();
5645 selIterPrev[iname] = prev->getUuid();
5655 void VymModel::setHideTmpMode(TreeItem::HideTmpMode mode)
5658 for (int i = 0; i < rootItem->branchCount(); i++)
5659 rootItem->getBranchNum(i)->setHideTmp(mode);
5661 if (mode == TreeItem::HideExport)
5666 qApp->processEvents();
5669 //////////////////////////////////////////////
5670 // Selection related
5671 //////////////////////////////////////////////
5673 void VymModel::updateSelection(QItemSelection newsel, QItemSelection dsel)
5678 bool do_reposition = false;
5679 foreach (ix, dsel.indexes()) {
5680 mi = static_cast<MapItem *>(ix.internalPointer());
5681 if (mi->isBranchLikeType())
5683 do_reposition || ((BranchItem *)mi)->resetTmpUnscroll();
5684 if (mi->getType() == TreeItem::XLink) {
5685 Link *li = ((XLinkItem *)mi)->getLink();
5686 XLinkObj *xlo = li->getXLinkObj();
5688 xlo->setSelection(XLinkObj::Unselected);
5691 do_reposition || li->getBeginBranch()->resetTmpUnscroll();
5693 do_reposition || li->getEndBranch()->resetTmpUnscroll();
5697 foreach (ix, newsel.indexes()) {
5698 mi = static_cast<MapItem *>(ix.internalPointer());
5699 if (mi->isBranchLikeType()) {
5700 bi = (BranchItem *)mi;
5701 if (bi->hasScrolledParent()) {
5703 do_reposition = true;
5706 if (mi->getType() == TreeItem::XLink) {
5707 ((XLinkItem *)mi)->setSelection();
5709 // begin/end branches need to be tmp unscrolled
5710 Link *li = ((XLinkItem *)mi)->getLink();
5711 bi = li->getBeginBranch();
5712 if (bi->hasScrolledParent()) {
5714 do_reposition = true;
5716 bi = li->getEndBranch();
5717 if (bi->hasScrolledParent()) {
5719 do_reposition = true;
5727 void VymModel::setSelectionModel(QItemSelectionModel *sm) { selModel = sm; }
5729 QItemSelectionModel *VymModel::getSelectionModel() { return selModel; }
5731 void VymModel::setSelectionBlocked(bool b) { selectionBlocked = b; }
5733 bool VymModel::isSelectionBlocked() { return selectionBlocked; }
5735 bool VymModel::select(const QString &s) // FIXME-2 Does not support multiple selections yet
5739 TreeItem *ti = findBySelectString(s);
5741 return select(index(ti));
5745 bool VymModel::selectID(const QString &s)
5749 TreeItem *ti = findUuid(QUuid(s));
5751 return select(index(ti));
5755 bool VymModel::select(LinkableMapObj *lmo)
5757 QItemSelection oldsel = selModel->selection();
5760 return select(lmo->getTreeItem());
5765 bool VymModel::selectToggle(TreeItem *ti)
5768 selModel->select(index(ti), QItemSelectionModel::Toggle);
5769 // appendSelectionToHistory(); // FIXME-4 selection history not implemented yet
5770 // for multiselections
5771 lastToggledUuid = ti->getUuid();
5777 bool VymModel::selectToggle(const QString &selectString)
5779 TreeItem *ti = findBySelectString(selectString);
5780 return selectToggle(ti);
5783 bool VymModel::select(TreeItem *ti)
5786 return select(index(ti));
5791 bool VymModel::select(const QModelIndex &index)
5793 if (index.isValid()) {
5794 TreeItem *ti = getItem(index);
5795 if (ti->isBranchLikeType()) {
5796 if (((BranchItem *)ti)->tmpUnscroll())
5799 selModel->select(index, QItemSelectionModel::ClearAndSelect);
5800 appendSelectionToHistory();
5806 void VymModel::unselectAll() { unselect(selModel->selection()); }
5808 void VymModel::unselect(QItemSelection desel)
5810 if (!desel.isEmpty()) {
5811 lastSelectString = getSelectString();
5812 selModel->clearSelection();
5816 bool VymModel::reselect()
5818 bool b = select(lastSelectString);
5822 bool VymModel::canSelectPrevious()
5824 if (currentSelection > 0)
5830 bool VymModel::selectPrevious()
5832 keepSelectionHistory = true;
5833 bool result = false;
5834 while (currentSelection > 0) {
5836 TreeItem *ti = findID(selectionHistory.at(currentSelection));
5838 result = select(ti);
5842 selectionHistory.removeAt(currentSelection);
5844 keepSelectionHistory = false;
5848 bool VymModel::canSelectNext()
5850 if (currentSelection < selectionHistory.count() - 1)
5856 bool VymModel::selectNext()
5858 keepSelectionHistory = true;
5859 bool result = false;
5860 while (currentSelection < selectionHistory.count() - 1) {
5862 TreeItem *ti = findID(selectionHistory.at(currentSelection));
5864 result = select(ti);
5868 selectionHistory.removeAt(currentSelection);
5870 keepSelectionHistory = false;
5874 void VymModel::resetSelectionHistory()
5876 selectionHistory.clear();
5877 currentSelection = -1;
5878 keepSelectionHistory = false;
5879 appendSelectionToHistory();
5882 void VymModel::appendSelectionToHistory() // FIXME-4 history unable to cope with multiple
5886 TreeItem *ti = getSelectedItem();
5887 if (ti && !keepSelectionHistory) {
5888 if (ti->isBranchLikeType())
5889 ((BranchItem *)ti)->setLastSelectedBranch();
5891 selectionHistory.append(id);
5892 currentSelection = selectionHistory.count() - 1;
5897 void VymModel::emitShowSelection(bool scaled)
5899 if (!repositionBlocked)
5900 emit(showSelection(scaled));
5903 TreeItem* VymModel::lastToggledItem()
5905 return findUuid(lastToggledUuid);
5908 void VymModel::emitNoteChanged(TreeItem *ti)
5910 QModelIndex ix = index(ti);
5911 emit(noteChanged(ix));
5912 mainWindow->updateNoteEditor(ti);
5915 void VymModel::emitDataChanged(TreeItem *ti)
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();
5929 void VymModel::emitUpdateQueries()
5931 // Used to tell MainWindow to update query results
5932 if (repositionBlocked)
5934 emit(updateQueries(this));
5936 void VymModel::emitUpdateLayout()
5938 if (settings.value("/mainwindow/autoLayout/use", "true") == "true")
5939 emit(updateLayout());
5942 bool VymModel::selectFirstBranch()
5944 TreeItem *ti = getSelectedBranch();
5946 TreeItem *par = ti->parent();
5948 TreeItem *ti2 = par->getFirstBranch();
5956 bool VymModel::selectFirstChildBranch()
5958 TreeItem *ti = getSelectedBranch();
5960 BranchItem *bi = ti->getFirstBranch();
5967 bool VymModel::selectLastBranch()
5969 TreeItem *ti = getSelectedBranch();
5971 TreeItem *par = ti->parent();
5973 TreeItem *ti2 = par->getLastBranch();
5981 bool VymModel::selectLastChildBranch()
5983 TreeItem *ti = getSelectedBranch();
5985 BranchItem *bi = ti->getLastBranch();
5992 bool VymModel::selectLastSelectedBranch()
5994 BranchItem *bi = getSelectedBranch();
5996 bi = bi->getLastSelectedBranch();
6003 bool VymModel::selectLastImage()
6005 TreeItem *ti = getSelectedBranch();
6007 TreeItem *par = ti->parent();
6009 TreeItem *ti2 = par->getLastImage();
6017 bool VymModel::selectLatestAdded() { return select(latestAddedItem); }
6019 bool VymModel::selectParent()
6021 TreeItem *ti = getSelectedItem();
6031 TreeItem::Type VymModel::selectionType()
6033 TreeItem *ti = getSelectedItem();
6035 return ti->getType();
6037 return TreeItem::Undefined;
6040 LinkableMapObj *VymModel::getSelectedLMO()
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();
6053 BranchObj *VymModel::getSelectedBranchObj() // convenience function
6055 TreeItem *ti = getSelectedBranch();
6057 return (BranchObj *)(((MapItem *)ti)->getLMO());
6062 BranchItem *VymModel::getSelectedBranch()
6064 TreeItem *ti = getSelectedItem();
6066 TreeItem::Type type = ti->getType();
6067 if (type == TreeItem::Branch || type == TreeItem::MapCenter)
6068 return (BranchItem *)ti;
6073 QList<BranchItem *> VymModel::getSelectedBranches()
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);
6084 ImageItem *VymModel::getSelectedImage()
6086 TreeItem *ti = getSelectedItem();
6087 if (ti && ti->getType() == TreeItem::Image)
6088 return (ImageItem *)ti;
6093 Task *VymModel::getSelectedTask()
6095 BranchItem *selbi = getSelectedBranch();
6097 return selbi->getTask();
6102 Link *VymModel::getSelectedXLink()
6104 XLinkItem *xli = getSelectedXLinkItem();
6106 return xli->getLink();
6110 XLinkItem *VymModel::getSelectedXLinkItem()
6112 TreeItem *ti = getSelectedItem();
6113 if (ti && ti->getType() == TreeItem::XLink)
6114 return (XLinkItem *)ti;
6119 AttributeItem *VymModel::getSelectedAttribute()
6121 TreeItem *ti = getSelectedItem();
6122 if (ti && ti->getType() == TreeItem::Attribute)
6123 return (AttributeItem *)ti;
6128 TreeItem *VymModel::getSelectedItem()
6132 QModelIndexList list = selModel->selectedIndexes();
6133 if (list.count() == 1)
6134 return getItem(list.first());
6139 QList<TreeItem *> VymModel::getSelectedItems()
6141 QList<TreeItem *> l;
6144 QModelIndexList list = selModel->selectedIndexes();
6145 foreach (QModelIndex ix, list)
6146 l.append(getItem(ix));
6150 QModelIndex VymModel::getSelectedIndex()
6152 QModelIndexList list = selModel->selectedIndexes();
6153 if (list.count() == 1)
6154 return list.first();
6156 return QModelIndex();
6159 QList<uint> VymModel::getSelectedIDs()
6162 foreach (TreeItem *ti, getSelectedItems())
6163 uids.append(ti->getID());
6167 QStringList VymModel::getSelectedUUIDs()
6170 foreach (TreeItem *ti, getSelectedItems())
6171 uids.append(ti->getUuid().toString());
6175 bool VymModel::isSelected(TreeItem *ti)
6177 return getSelectedItems().contains(ti);
6180 QString VymModel::getSelectString()
6182 return getSelectString(getSelectedItem());
6185 QString VymModel::getSelectString(
6186 LinkableMapObj *lmo) // only for convenience. Used in MapEditor
6190 return getSelectString(lmo->getTreeItem());
6193 QString VymModel::getSelectString(TreeItem *ti)
6196 if (!ti || ti->depth() < 0)
6198 switch (ti->getType()) {
6199 case TreeItem::MapCenter:
6202 case TreeItem::Branch:
6205 case TreeItem::Image:
6208 case TreeItem::Attribute:
6211 case TreeItem::XLink:
6215 s = "unknown type in VymModel::getSelectString()";
6218 s = s + QString("%1").arg(ti->num());
6219 if (ti->depth() > 0)
6220 // call myself recursively
6221 s = getSelectString(ti->parent()) + "," + s;
6225 QString VymModel::getSelectString(BranchItem *bi)
6227 return getSelectString((TreeItem *)bi);
6230 QString VymModel::getSelectString(const uint &i)
6232 return getSelectString(findID(i));
6235 void VymModel::setLatestAddedItem(TreeItem *ti) { latestAddedItem = ti; }
6237 TreeItem *VymModel::getLatestAddedItem() { return latestAddedItem; }
6239 SlideModel *VymModel::getSlideModel() { return slideModel; }
6241 int VymModel::slideCount() { return slideModel->count(); }
6243 SlideItem *VymModel::addSlide()
6245 SlideItem *si = slideModel->getSelectedItem();
6247 si = slideModel->addSlide(NULL, si->childNumber() + 1);
6249 si = slideModel->addSlide();
6251 TreeItem *seli = getSelectedItem();
6255 if (!loadStringFromDisk(vymBaseDir.path() +
6256 "/macros/slideeditor-snapshot.vys",
6258 qWarning() << "VymModel::addSlide couldn't load template for "
6265 QString().setNum(getMapEditor()->getZoomFactorTarget()));
6266 inScript.replace("CURRENT_ANGLE",
6267 QString().setNum(getMapEditor()->getAngleTarget()));
6268 inScript.replace("CURRENT_ID",
6269 "\"" + seli->getUuid().toString() + "\"");
6271 si->setInScript(inScript);
6272 slideModel->setData(slideModel->index(si), seli->getHeadingPlain());
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,
6283 void VymModel::deleteSlide(SlideItem *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);
6296 void VymModel::deleteSlide(int n) { deleteSlide(slideModel->getSlide(n)); }
6298 void VymModel::relinkSlide(SlideItem *si, int pos)
6301 slideModel->relinkSlide(si, si->parent(), pos);
6304 bool VymModel::moveSlideDown(int n)
6306 SlideItem *si = NULL;
6307 if (n < 0) // default if called without parameters
6309 si = slideModel->getSelectedItem();
6311 n = si->childNumber();
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));
6330 bool VymModel::moveSlideUp(int n)
6332 SlideItem *si = NULL;
6333 if (n < 0) // default if called without parameters
6335 si = slideModel->getSelectedItem();
6337 n = si->childNumber();
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));
6356 void VymModel::updateSlideSelection(QItemSelection newsel, QItemSelection)
6358 if (blockSlideSelection)
6361 foreach (ix, newsel.indexes()) {
6362 SlideItem *si = static_cast<SlideItem *>(ix.internalPointer());
6363 QString inScript = si->getInScript();
6365 // show inScript in ScriptEditor
6366 scriptEditor->setSlideScript(modelID, si->getID(), inScript);