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);
1142 qWarning() << "VymModel::renameMap failed to get lockfile. state=" << vymLock.getState();
1146 void VymModel::setReadOnly(bool b)
1149 mainWindow->updateTabName(this);
1152 bool VymModel::isReadOnly() { return readonly; }
1154 void VymModel::autosave()
1156 // Check if autosave is disabled due to testmode
1160 << QString("VymModel::autosave disabled in testmode! Current map: %1")
1165 // Check if autosave is disabled globally
1166 if (!mainWindow->useAutosave()) {
1168 << QString("VymModel::autosave disabled globally! Current map: %1")
1173 QDateTime now = QDateTime().currentDateTime();
1175 // Disable autosave, while we have gone back in history
1176 int redosAvail = undoSet.numValue(QString("/history/redosAvail"));
1180 // Also disable autosave for new map without filename
1181 if (filePath.isEmpty()) {
1184 << "VymModel::autosave rejected due to missing filePath\n";
1188 if (mapUnsaved && mapChanged && mainWindow->useAutosave() && !testmode) {
1189 if (QFileInfo(filePath).lastModified() <= fileChangedTime)
1190 mainWindow->fileSave(this);
1192 qDebug() << " ME::autosave rejected, file on disk is newer than "
1197 void VymModel::fileChanged()
1199 // Check if file on disk has changed meanwhile
1200 if (!filePath.isEmpty()) {
1201 if (readonly && vymLock.getState() != VymLock::LockedByMyself) {
1202 // unset readonly if lockfile is gone
1203 // but only, if map was LockedByOther before
1204 if (vymLock.tryLock())
1208 // FIXME-5 We could check, if somebody else removed/replaced lockfile
1209 // (A unique vym ID would be needed)
1211 QDateTime tmod = QFileInfo(filePath).lastModified();
1212 if (tmod > fileChangedTime) {
1213 // FIXME-4 VM switch to current mapeditor and finish
1217 tr("The file of the map on disk has changed:\n\n"
1218 " %1\n\nDo you want to reload that map with the new "
1221 QMessageBox::Question, QMessageBox::Yes,
1222 QMessageBox::Cancel | QMessageBox::Default,
1223 QMessageBox::NoButton);
1225 mb.setButtonText(QMessageBox::Yes, tr("Reload"));
1226 mb.setButtonText(QMessageBox::No, tr("Ignore"));
1227 switch (mb.exec()) {
1228 case QMessageBox::Yes:
1230 mainWindow->initProgressCounter(1);
1232 mainWindow->removeProgressCounter();
1234 case QMessageBox::Cancel:
1236 tmod; // allow autosave to overwrite newer file!
1243 void VymModel::blockReposition()
1245 repositionBlocked = true;
1248 void VymModel::unblockReposition()
1250 repositionBlocked = false;
1254 bool VymModel::isDefault() { return mapDefault; }
1256 void VymModel::makeDefault()
1262 bool VymModel::hasChanged() { return mapChanged; }
1264 void VymModel::setChanged()
1267 autosaveTimer->start(
1268 settings.value("/system/autosave/ms/", 30000).toInt());
1275 QString VymModel::getObjectName(LinkableMapObj *lmo)
1277 if (!lmo || !lmo->getTreeItem())
1279 return getObjectName(lmo->getTreeItem());
1282 QString VymModel::getObjectName(TreeItem *ti)
1286 return QString("Error: NULL has no name!");
1287 s = ti->getHeadingPlain();
1291 return QString("%1 (%2)").arg(ti->getTypeName()).arg(s);
1294 void VymModel::redo()
1296 // Can we undo at all?
1300 bool saveStateBlockedOrg = saveStateBlocked;
1301 saveStateBlocked = true;
1305 if (undosAvail < stepsTotal)
1308 if (curStep > stepsTotal)
1310 QString undoCommand =
1311 undoSet.value(QString("/history/step-%1/undoCommand").arg(curStep));
1312 QString undoSelection =
1313 undoSet.value(QString("/history/step-%1/undoSelection").arg(curStep));
1314 QString redoCommand =
1315 undoSet.value(QString("/history/step-%1/redoCommand").arg(curStep));
1316 QString redoSelection =
1317 undoSet.value(QString("/history/step-%1/redoSelection").arg(curStep));
1319 undoSet.value(QString("/history/step-%1/comment").arg(curStep));
1320 QString version = undoSet.value("/history/version");
1322 /* TODO Maybe check for version, if we save the history
1323 if (!checkVersion(version))
1324 QMessageBox::warning(0,tr("Warning"),
1325 tr("Version %1 of saved undo/redo data\ndoes not match current vym
1326 version %2.").arg(version).arg(vymVersion));
1329 // Find out current undo directory
1330 QString bakMapDir(QString(tmpMapDirPath + "/undo-%1").arg(curStep));
1333 qDebug() << "VymModel::redo() begin\n";
1334 qDebug() << " undosAvail=" << undosAvail;
1335 qDebug() << " redosAvail=" << redosAvail;
1336 qDebug() << " curStep=" << curStep;
1337 qDebug() << " ---------------------------";
1338 qDebug() << " comment=" << comment;
1339 qDebug() << " undoSel=" << undoSelection;
1340 qDebug() << " redoSel=" << redoSelection;
1341 qDebug() << " undoCom:";
1342 cout << qPrintable(undoCommand);
1343 qDebug() << " redoCom=";
1344 cout << qPrintable(redoCommand);
1345 qDebug() << " ---------------------------";
1348 // select object before redo
1349 if (!redoSelection.isEmpty())
1350 select(redoSelection);
1353 QString redoScript =
1354 QString("model = vym.currentMap(); model.%1").arg(redoCommand);
1355 errMsg = QVariant(execute(redoScript)).toString();
1356 saveStateBlocked = saveStateBlockedOrg;
1358 undoSet.setValue("/history/undosAvail", QString::number(undosAvail));
1359 undoSet.setValue("/history/redosAvail", QString::number(redosAvail));
1360 undoSet.setValue("/history/curStep", QString::number(curStep));
1361 undoSet.writeSettings(histPath);
1363 mainWindow->updateHistory(undoSet);
1366 /* TODO remove testing
1367 qDebug() << "ME::redo() end\n";
1368 qDebug() << " undosAvail="<<undosAvail;
1369 qDebug() << " redosAvail="<<redosAvail;
1370 qDebug() << " curStep="<<curStep;
1371 qDebug() << " ---------------------------";
1375 bool VymModel::isRedoAvailable()
1377 if (undoSet.numValue("/history/redosAvail", 0) > 0)
1383 QString VymModel::lastRedoSelection()
1385 if (isUndoAvailable())
1386 return undoSet.value(
1387 QString("/history/step-%1/redoSelection").arg(curStep));
1392 QString VymModel::lastRedoCommand()
1394 if (isUndoAvailable())
1395 return undoSet.value(
1396 QString("/history/step-%1/redoCommand").arg(curStep));
1401 QVariant VymModel::repeatLastCommand()
1403 QString command = "m = vym.currentMap();";
1404 if (isUndoAvailable())
1407 QString("/history/step-%1/redoCommand").arg(curStep)) +
1411 return execute(command);
1414 void VymModel::undo()
1416 // Can we undo at all?
1420 mainWindow->statusMessage(tr("Autosave disabled during undo."));
1422 bool saveStateBlockedOrg = saveStateBlocked;
1423 saveStateBlocked = true;
1425 QString undoCommand =
1426 undoSet.value(QString("/history/step-%1/undoCommand").arg(curStep));
1427 QString undoSelection =
1428 undoSet.value(QString("/history/step-%1/undoSelection").arg(curStep));
1429 QString redoCommand =
1430 undoSet.value(QString("/history/step-%1/redoCommand").arg(curStep));
1431 QString redoSelection =
1432 undoSet.value(QString("/history/step-%1/redoSelection").arg(curStep));
1434 undoSet.value(QString("/history/step-%1/comment").arg(curStep));
1435 QString version = undoSet.value("/history/version");
1437 /* TODO Maybe check for version, if we save the history
1438 if (!checkVersion(version))
1439 QMessageBox::warning(0,tr("Warning"),
1440 tr("Version %1 of saved undo/redo data\ndoes not match current vym
1441 version %2.").arg(version).arg(vymVersion));
1444 // Find out current undo directory
1445 QString bakMapDir(QString(tmpMapDirPath + "/undo-%1").arg(curStep));
1447 // select object before undo
1448 if (!undoSelection.isEmpty() && !select(undoSelection)) {
1449 qWarning("VymModel::undo() Could not select object for undo");
1454 qDebug() << "VymModel::undo() begin\n";
1455 qDebug() << " undosAvail=" << undosAvail;
1456 qDebug() << " redosAvail=" << redosAvail;
1457 qDebug() << " curStep=" << curStep;
1458 cout << " ---------------------------" << endl;
1459 qDebug() << " comment=" << comment;
1460 qDebug() << " undoSel=" << undoSelection;
1461 qDebug() << " redoSel=" << redoSelection;
1462 cout << " undoCom:" << endl;
1463 cout << qPrintable(undoCommand) << endl;
1464 cout << " redoCom:" << endl;
1465 cout << qPrintable(redoCommand) << endl;
1466 cout << " ---------------------------" << endl;
1469 // select object before undo // FIXME-2 double select, see above
1470 if (!undoSelection.isEmpty())
1471 select(undoSelection);
1475 QString undoScript =
1476 QString("model = vym.currentMap(); model.%1").arg(undoCommand);
1477 errMsg = QVariant(execute(undoScript)).toString();
1482 curStep = stepsTotal;
1486 saveStateBlocked = saveStateBlockedOrg;
1488 qDebug() << "VymModel::undo() end\n";
1489 qDebug() << " undosAvail="<<undosAvail;
1490 qDebug() << " redosAvail="<<redosAvail;
1491 qDebug() << " curStep="<<curStep;
1492 qDebug() << " ---------------------------";
1495 undoSet.setValue("/history/undosAvail", QString::number(undosAvail));
1496 undoSet.setValue("/history/redosAvail", QString::number(redosAvail));
1497 undoSet.setValue("/history/curStep", QString::number(curStep));
1498 undoSet.writeSettings(histPath);
1500 mainWindow->updateHistory(undoSet);
1504 bool VymModel::isUndoAvailable()
1506 if (undoSet.numValue("/history/undosAvail", 0) > 0)
1511 void VymModel::gotoHistoryStep(int i)
1513 // Restore variables
1514 int undosAvail = undoSet.numValue(QString("/history/undosAvail"));
1515 int redosAvail = undoSet.numValue(QString("/history/redosAvail"));
1518 i = undosAvail + redosAvail;
1520 // Clicking above current step makes us undo things
1521 if (i < undosAvail) {
1522 for (int j = 0; j < undosAvail - i; j++)
1526 // Clicking below current step makes us redo things
1528 for (int j = undosAvail; j < i; j++) {
1530 qDebug() << "VymModel::gotoHistoryStep redo " << j << "/"
1531 << undosAvail << " i=" << i;
1535 // And ignore clicking the current row ;-)
1538 QString VymModel::getHistoryPath()
1540 QString histName(QString("history-%1").arg(curStep));
1541 return (tmpMapDirPath + "/" + histName);
1544 void VymModel::resetHistory()
1550 stepsTotal = settings.value("/history/stepsTotal", 100).toInt();
1551 undoSet.setValue("/history/stepsTotal", QString::number(stepsTotal));
1552 mainWindow->updateHistory(undoSet);
1555 void VymModel::saveState(const SaveMode &savemode, const QString &undoSelection,
1556 const QString &undoCom, const QString &redoSelection,
1557 const QString &redoCom, const QString &comment,
1558 TreeItem *saveSel, QString dataXML)
1560 sendData(redoCom); // FIXME-4 testing
1564 if (saveStateBlocked)
1568 qDebug() << "VM::saveState() for map " << mapName;
1570 QString undoCommand = undoCom;
1571 QString redoCommand = redoCom;
1574 // Increase undo steps, but check for repeated actions
1575 // like editing a vymNote - then do not increase but replace last command
1577 QRegExp re ("parseVymText.*\\(.*vymnote");
1578 if (curStep > 0 && redoSelection == lastRedoSelection() &&
1579 lastRedoCommand().contains(re)) {
1580 undoCommand = undoSet.value(
1581 QString("/history/step-%1/undoCommand").arg(curStep), undoCommand);
1585 if (undosAvail < stepsTotal)
1589 if (curStep > stepsTotal)
1593 QString histDir = getHistoryPath();
1594 QString bakMapPath = histDir + "/map.xml";
1596 // Create histDir if not available
1599 makeSubDirs(histDir);
1601 // Save depending on how much needs to be saved
1602 QList<Link *> tmpLinks;
1604 dataXML = saveToDir(histDir, mapName + "-", FlagRowMaster::NoFlags, QPointF(),
1607 if (savemode == PartOfMap) {
1608 undoCommand.replace("PATH", bakMapPath);
1609 redoCommand.replace("PATH", bakMapPath);
1612 if (!dataXML.isEmpty())
1613 // Write XML Data to disk
1614 saveStringToDisk(bakMapPath, dataXML);
1616 // We would have to save all actions in a tree, to keep track of
1617 // possible redos after a action. Possible, but we are too lazy: forget
1621 // Write the current state to disk
1622 undoSet.setValue("/history/undosAvail", QString::number(undosAvail));
1623 undoSet.setValue("/history/redosAvail", QString::number(redosAvail));
1624 undoSet.setValue("/history/curStep", QString::number(curStep));
1625 undoSet.setValue(QString("/history/step-%1/undoCommand").arg(curStep),
1627 undoSet.setValue(QString("/history/step-%1/undoSelection").arg(curStep),
1629 undoSet.setValue(QString("/history/step-%1/redoCommand").arg(curStep),
1631 undoSet.setValue(QString("/history/step-%1/redoSelection").arg(curStep),
1633 undoSet.setValue(QString("/history/step-%1/comment").arg(curStep), comment);
1634 undoSet.setValue(QString("/history/version"), vymVersion);
1635 undoSet.writeSettings(histPath);
1638 // qDebug() << " into="<< histPath;
1639 qDebug() << " stepsTotal=" << stepsTotal
1640 << ", undosAvail=" << undosAvail
1641 << ", redosAvail=" << redosAvail << ", curStep=" << curStep;
1642 cout << " ---------------------------" << endl;
1643 qDebug() << " comment=" << comment;
1644 qDebug() << " undoSel=" << undoSelection;
1645 qDebug() << " redoSel=" << redoSelection;
1647 qDebug() << " saveSel=" << qPrintable(getSelectString(saveSel));
1648 cout << " undoCom:" << endl;
1649 cout << qPrintable(undoCommand) << endl;
1650 cout << " redoCom:" << endl;
1651 cout << qPrintable(redoCommand) << endl;
1652 cout << " ---------------------------" << endl;
1655 mainWindow->updateHistory(undoSet);
1660 void VymModel::saveStateChangingPart(TreeItem *undoSel, TreeItem *redoSel,
1661 const QString &rc, const QString &comment)
1663 // save the selected part of the map, Undo will replace part of map
1664 QString undoSelection = "";
1666 undoSelection = getSelectString(undoSel);
1668 qWarning("VymModel::saveStateChangingPart no undoSel given!");
1669 QString redoSelection = "";
1671 redoSelection = getSelectString(undoSel);
1673 qWarning("VymModel::saveStateChangingPart no redoSel given!");
1675 saveState(PartOfMap, undoSelection, "addMapReplace (\"PATH\")",
1676 redoSelection, rc, comment, undoSel);
1679 void VymModel::saveStateRemovingPart(TreeItem *redoSel, const QString &comment)
1682 qWarning("VymModel::saveStateRemovingPart no redoSel given!");
1685 QString undoSelection;
1686 QString redoSelection = getSelectString(redoSel);
1687 if (redoSel->isBranchLikeType()) {
1688 // save the selected branch of the map, Undo will insert part of map
1689 if (redoSel->depth() > 0)
1690 undoSelection = getSelectString(redoSel->parent());
1691 saveState(PartOfMap, undoSelection,
1692 QString("addMapInsert (\"PATH\",%1,%2)")
1693 .arg(redoSel->num())
1695 redoSelection, "remove ()", comment, redoSel);
1699 void VymModel::saveState(TreeItem *undoSel, const QString &uc,
1700 TreeItem *redoSel, const QString &rc,
1701 const QString &comment)
1703 // "Normal" savestate: save commands, selections and comment
1704 // so just save commands for undo and redo
1705 // and use current selection, if empty parameter passed
1707 QString redoSelection = "";
1709 redoSelection = getSelectString(redoSel);
1710 QString undoSelection = "";
1712 undoSelection = getSelectString(undoSel);
1714 saveState(UndoCommand, undoSelection, uc, redoSelection, rc, comment, NULL);
1717 void VymModel::saveState(const QString &undoSel, const QString &uc,
1718 const QString &redoSel, const QString &rc,
1719 const QString &comment)
1721 // "Normal" savestate: save commands, selections and comment
1722 // so just save commands for undo and redo
1723 // and use current selection
1724 saveState(UndoCommand, undoSel, uc, redoSel, rc, comment, NULL);
1727 void VymModel::saveState(const QString &uc, const QString &rc,
1728 const QString &comment)
1730 // "Normal" savestate applied to model (no selection needed):
1731 // save commands and comment
1732 saveState(UndoCommand, NULL, uc, NULL, rc, comment, NULL);
1735 void VymModel::saveStateMinimal(TreeItem *undoSel, const QString &uc,
1736 TreeItem *redoSel, const QString &rc,
1737 const QString &comment)
1738 { // Save a change in string and merge
1739 // minor sequential changes */
1740 QString redoSelection = "";
1742 redoSelection = getSelectString(redoSel);
1743 QString undoSelection = "";
1745 undoSelection = getSelectString(undoSel);
1747 saveState(UndoCommand, undoSelection, uc, redoSelection, rc, comment, NULL);
1750 void VymModel::saveStateBeforeLoad(LoadMode lmode, const QString &fname)
1752 BranchItem *selbi = getSelectedBranch();
1754 if (lmode == ImportAdd)
1755 saveStateChangingPart(selbi, selbi,
1756 QString("addMapInsert (\"%1\")").arg(fname),
1757 QString("Add map %1 to %2")
1759 .arg(getObjectName(selbi)));
1760 if (lmode == ImportReplace) {
1761 BranchItem *pi = (BranchItem *)(selbi->parent());
1762 saveStateChangingPart(pi, pi,
1763 QString("addMapReplace(%1)").arg(fname),
1764 QString("Add map %1 to %2")
1766 .arg(getObjectName(selbi)));
1771 QGraphicsScene *VymModel::getScene() { return mapEditor->getScene(); }
1773 TreeItem *VymModel::findBySelectString(QString s)
1778 // Old maps don't have multiple mapcenters and don't save full path
1779 if (s.left(2) != "mc")
1782 QStringList parts = s.split(",");
1785 TreeItem *ti = rootItem;
1787 while (!parts.isEmpty()) {
1788 typ = parts.first().left(2);
1789 n = parts.first().right(parts.first().length() - 3).toInt();
1790 parts.removeFirst();
1791 if (typ == "mc" || typ == "bo")
1792 ti = ti->getBranchNum(n);
1793 else if (typ == "fi")
1794 ti = ti->getImageNum(n);
1795 else if (typ == "ai")
1796 ti = ti->getAttributeNum(n);
1797 else if (typ == "xl")
1798 ti = ti->getXLinkItemNum(n);
1805 TreeItem *VymModel::findID(const uint &id)
1807 BranchItem *cur = NULL;
1808 BranchItem *prev = NULL;
1809 nextBranch(cur, prev);
1811 if (id == cur->getID())
1814 while (j < cur->xlinkCount()) {
1815 XLinkItem *xli = cur->getXLinkItemNum(j);
1816 if (id == xli->getID())
1821 while (j < cur->imageCount()) {
1822 ImageItem *ii = cur->getImageNum(j);
1823 if (id == ii->getID())
1827 nextBranch(cur, prev);
1832 TreeItem *VymModel::findUuid(const QUuid &id)
1834 BranchItem *cur = NULL;
1835 BranchItem *prev = NULL;
1836 nextBranch(cur, prev);
1838 if (id == cur->getUuid())
1841 while (j < cur->xlinkCount()) {
1842 XLinkItem *xli = cur->getXLinkItemNum(j);
1843 if (id == xli->getUuid())
1848 while (j < cur->imageCount()) {
1849 ImageItem *ii = cur->getImageNum(j);
1850 if (id == ii->getUuid())
1854 nextBranch(cur, prev);
1859 //////////////////////////////////////////////
1861 //////////////////////////////////////////////
1862 void VymModel::setVersion(const QString &s) { version = s; }
1864 QString VymModel::getVersion() { return version; }
1866 void VymModel::setTitle(const QString &s)
1868 saveState(QString("setMapTitle (\"%1\")").arg(title),
1869 QString("setMapTitle (\"%1\")").arg(s),
1870 QString("Set title of map to \"%1\"").arg(s));
1874 QString VymModel::getTitle() { return title; }
1876 void VymModel::setAuthor(const QString &s)
1878 saveState(QString("setMapAuthor (\"%1\")").arg(author),
1879 QString("setMapAuthor (\"%1\")").arg(s),
1880 QString("Set author of map to \"%1\"").arg(s));
1884 QString VymModel::getAuthor() { return author; }
1886 void VymModel::setComment(const QString &s)
1888 saveState(QString("setMapComment (\"%1\")").arg(comment),
1889 QString("setMapComment (\"%1\")").arg(s),
1890 QString("Set comment of map"));
1894 QString VymModel::getComment() { return comment; }
1896 QString VymModel::getDate()
1898 return QDate::currentDate().toString("yyyy-MM-dd");
1901 int VymModel::branchCount()
1904 BranchItem *cur = NULL;
1905 BranchItem *prev = NULL;
1906 nextBranch(cur, prev);
1909 nextBranch(cur, prev);
1914 int VymModel::centerCount() { return rootItem->branchCount(); }
1916 void VymModel::setSortFilter(const QString &s)
1919 emit(sortFilterChanged(sortFilter));
1922 QString VymModel::getSortFilter() { return sortFilter; }
1924 void VymModel::setHeading(const VymText &vt, BranchItem *bi)
1929 QString s = vt.getTextASCII();
1931 bi = getSelectedBranch();
1933 h_old = bi->getHeading();
1936 saveState(bi, "parseVymText (\"" + quoteQuotes(h_old.saveToDir()) + "\")", bi,
1937 "parseVymText (\"" + quoteQuotes(h_new.saveToDir()) + "\")",
1938 QString("Set heading of %1 to \"%2\"")
1939 .arg(getObjectName(bi))
1942 emitDataChanged(bi);
1943 emitUpdateQueries();
1944 mainWindow->updateHeadingEditor(bi); // Update HeadingEditor with new heading
1949 void VymModel::setHeadingPlainText(const QString &s, BranchItem *bi)
1952 bi = getSelectedBranch();
1954 VymText vt = bi->getHeading();
1956 if (bi->getHeading() == vt)
1961 if ((s.startsWith("http://") || s.startsWith("https://")) &&
1962 bi->getURL().isEmpty())
1967 Heading VymModel::getHeading()
1969 TreeItem *selti = getSelectedItem();
1971 return selti->getHeading();
1972 qWarning() << "VymModel::getHeading Nothing selected.";
1976 void VymModel::updateNoteText(const VymText &vt)
1978 bool editorStateChanged = false;
1980 TreeItem *selti = getSelectedItem();
1982 VymNote note_old = selti->getNote();
1983 VymNote note_new(vt);
1984 if (note_new.getText() != note_old.getText()) {
1985 if ((note_new.isEmpty() && !note_old.isEmpty()) ||
1986 (!note_new.isEmpty() && note_old.isEmpty()))
1987 editorStateChanged = true;
1992 saveState(selti, "parseVymText (\"" + quoteQuotes(note_old.saveToDir()) + "\")",
1993 selti, "parseVymText (\"" + quoteQuotes(note_new.saveToDir()) + "\")",
1994 QString("Set note of %1 to \"%2\"")
1995 .arg(getObjectName(selti))
1996 .arg(note_new.getTextASCII().left(20)));
2001 // Update also flags after changes in NoteEditor
2002 emitDataChanged(selti);
2004 // Only update flag, if state has changed
2005 if (editorStateChanged)
2010 void VymModel::setNote(const VymNote &vn)
2012 TreeItem *selti = getSelectedItem();
2016 n_old = selti->getNote();
2018 saveState(selti, "parseVymText (\"" + quoteQuotes(n_old.saveToDir()) + "\")", selti,
2019 "parseVymText (\"" + quoteQuotes(n_new.saveToDir()) + "\")",
2020 QString("Set note of %1 to \"%2\"")
2021 .arg(getObjectName(selti))
2022 .arg(n_new.getTextASCII().left(40)));
2023 selti->setNote(n_new);
2024 emitNoteChanged(selti);
2025 emitDataChanged(selti);
2029 VymNote VymModel::getNote()
2031 TreeItem *selti = getSelectedItem();
2033 VymNote n = selti->getNote();
2036 qWarning() << "VymModel::getNote Nothing selected.";
2040 bool VymModel::hasRichTextNote()
2042 TreeItem *selti = getSelectedItem();
2044 return selti->getNote().isRichText();
2049 void VymModel::loadNote(const QString &fn)
2051 BranchItem *selbi = getSelectedBranch();
2054 if (!loadStringFromDisk(fn, n))
2055 qWarning() << "VymModel::loadNote Couldn't load " << fn;
2060 emitDataChanged(selbi);
2061 emitUpdateQueries();
2066 qWarning("VymModel::loadNote no branch selected");
2069 void VymModel::saveNote(const QString &fn)
2071 BranchItem *selbi = getSelectedBranch();
2073 VymNote n = selbi->getNote();
2075 qWarning() << "VymModel::saveNote note is empty, won't save to "
2078 if (!saveStringToDisk(fn, n.saveToDir()))
2079 qWarning() << "VymModel::saveNote Couldn't save " << fn;
2083 qWarning("VymModel::saveNote no branch selected");
2086 void VymModel::findDuplicateURLs() // FIXME-3 needs GUI
2088 // Generate map containing _all_ URLs and branches
2090 QMultiMap<QString, BranchItem *> map;
2091 BranchItem *cur = NULL;
2092 BranchItem *prev = NULL;
2093 nextBranch(cur, prev);
2098 nextBranch(cur, prev);
2101 // Extract duplicate URLs
2102 QMultiMap<QString, BranchItem *>::const_iterator i = map.constBegin();
2103 QMultiMap<QString, BranchItem *>::const_iterator firstdup =
2104 map.constEnd(); // invalid
2105 while (i != map.constEnd()) {
2106 if (i != map.constBegin() && i.key() == firstdup.key()) {
2107 if (i - 1 == firstdup) {
2108 qDebug() << firstdup.key();
2109 qDebug() << " - " << firstdup.value() << " - "
2110 << firstdup.value()->getHeading().getText();
2112 qDebug() << " - " << i.value() << " - "
2113 << i.value()->getHeading().getText();
2122 bool VymModel::findAll(FindResultModel *rmodel, QString s,
2123 Qt::CaseSensitivity cs, bool searchNotes)
2126 rmodel->setSearchString(s);
2127 rmodel->setSearchFlags(QTextDocument::FindFlags()); // FIXME-4 translate cs to
2128 // QTextDocument::FindFlag
2131 BranchItem *cur = NULL;
2132 BranchItem *prev = NULL;
2133 nextBranch(cur, prev);
2135 FindResultItem *lastParent = NULL;
2138 if (cur->getHeading().getTextASCII().contains(s, cs)) {
2139 lastParent = rmodel->addItem(cur);
2144 QString n = cur->getNoteASCII();
2148 i = n.indexOf(s, i, cs);
2150 // If not there yet, add "parent" item
2152 lastParent = rmodel->addItem(cur);
2156 << "VymModel::findAll still no lastParent?!";
2159 lastParent->setSelectable (false);
2163 // save index of occurence
2164 QString e = n.mid(i - 15, 30);
2165 n.replace('\n', ' ');
2168 QString(tr("Note", "FindAll in VymModel") +
2170 .arg(n.mid(i - 8, 80)),
2177 nextBranch(cur, prev);
2182 void VymModel::setURL(QString url, bool updateFromCloud, BranchItem *bi)
2184 if (!bi) bi = getSelectedBranch();
2185 if (bi->getURL() == url)
2189 QString oldurl = bi->getURL();
2192 bi, QString("setURL (\"%1\")").arg(oldurl), bi,
2193 QString("setURL (\"%1\")").arg(url),
2194 QString("set URL of %1 to %2").arg(getObjectName(bi)).arg(url));
2195 emitDataChanged(bi);
2198 if (updateFromCloud) // FIXME-2 use oembed.com also for Youtube and other cloud providers
2199 // Check for Confluence
2200 setHeadingConfluencePageName();
2204 QString VymModel::getURL()
2206 TreeItem *selti = getSelectedItem();
2208 return selti->getURL();
2213 QStringList VymModel::getURLs(bool ignoreScrolled)
2216 BranchItem *selbi = getSelectedBranch();
2217 BranchItem *cur = NULL;
2218 BranchItem *prev = NULL;
2219 nextBranch(cur, prev, true, selbi);
2221 if (!cur->getURL().isEmpty() &&
2222 !(ignoreScrolled && cur->hasScrolledParent()))
2223 urls.append(cur->getURL());
2224 nextBranch(cur, prev, true, selbi);
2229 void VymModel::setFrameType(const FrameObj::FrameType &t)
2231 BranchItem *bi = getSelectedBranch();
2233 BranchObj *bo = (BranchObj *)(bi->getLMO());
2235 QString s = bo->getFrameTypeName();
2236 bo->setFrameType(t);
2238 bi, QString("setFrameType (\"%1\")").arg(s), bi,
2239 QString("setFrameType (\"%1\")").arg(bo->getFrameTypeName()),
2240 QString("set type of frame to %1").arg(s));
2242 bo->updateLinkGeometry();
2247 void VymModel::setFrameType(const QString &s)
2249 BranchItem *bi = getSelectedBranch();
2251 BranchObj *bo = (BranchObj *)(bi->getLMO());
2255 QString("setFrameType (\"%1\")").arg(bo->getFrameTypeName()),
2256 bi, QString("setFrameType (\"%1\")").arg(s),
2257 QString("set type of frame to %1").arg(s));
2258 bo->setFrameType(s);
2260 bo->updateLinkGeometry();
2265 void VymModel::toggleFrameIncludeChildren()
2267 BranchItem *bi = getSelectedBranch();
2269 bool b = bi->getFrameIncludeChildren();
2270 setFrameIncludeChildren(!b);
2274 void VymModel::setFrameIncludeChildren(bool b)
2276 BranchItem *bi = getSelectedBranch();
2278 QString u = b ? "false" : "true";
2279 QString r = !b ? "false" : "true";
2281 saveState(bi, QString("setFrameIncludeChildren(%1)").arg(u), bi,
2282 QString("setFrameIncludeChildren(%1)").arg(r),
2283 QString("Include children in %1").arg(getObjectName(bi)));
2284 bi->setFrameIncludeChildren(b);
2285 emitDataChanged(bi);
2290 void VymModel::setFramePenColor(
2291 const QColor &c) // FIXME-4 not saved if there is no LMO
2294 BranchItem *bi = getSelectedBranch();
2296 BranchObj *bo = (BranchObj *)(bi->getLMO());
2299 QString("setFramePenColor (\"%1\")")
2300 .arg(bo->getFramePenColor().name()),
2301 bi, QString("setFramePenColor (\"%1\")").arg(c.name()),
2302 QString("set pen color of frame to %1").arg(c.name()));
2303 bo->setFramePenColor(c);
2308 void VymModel::setFrameBrushColor(
2309 const QColor &c) // FIXME-4 not saved if there is no LMO
2311 BranchItem *bi = getSelectedBranch();
2313 BranchObj *bo = (BranchObj *)(bi->getLMO());
2316 QString("setFrameBrushColor (\"%1\")")
2317 .arg(bo->getFrameBrushColor().name()),
2318 bi, QString("setFrameBrushColor (\"%1\")").arg(c.name()),
2319 QString("set brush color of frame to %1").arg(c.name()));
2320 bo->setFrameBrushColor(c);
2321 bi->setBackgroundColor(c); // FIXME-4 redundant with above
2323 emitDataChanged(bi); // Notify HeadingEditor to eventually change BG color
2327 void VymModel::setFramePadding(
2330 BranchItem *bi = getSelectedBranch();
2332 BranchObj *bo = (BranchObj *)(bi->getLMO());
2336 QString("setFramePadding (\"%1\")").arg(bo->getFramePadding()),
2337 bi, QString("setFramePadding (\"%1\")").arg(i),
2338 QString("set brush color of frame to %1").arg(i));
2339 bo->setFramePadding(i);
2341 bo->updateLinkGeometry();
2346 void VymModel::setFrameBorderWidth(
2349 BranchItem *bi = getSelectedBranch();
2351 BranchObj *bo = (BranchObj *)(bi->getLMO());
2354 QString("setFrameBorderWidth (\"%1\")")
2355 .arg(bo->getFrameBorderWidth()),
2356 bi, QString("setFrameBorderWidth (\"%1\")").arg(i),
2357 QString("set border width of frame to %1").arg(i));
2358 bo->setFrameBorderWidth(i);
2360 bo->updateLinkGeometry();
2365 void VymModel::setIncludeImagesVer(bool b)
2367 BranchItem *bi = getSelectedBranch();
2368 if (bi && b != bi->getIncludeImagesVer()) {
2369 QString u = b ? "false" : "true";
2370 QString r = !b ? "false" : "true";
2373 bi, QString("setIncludeImagesVertically (%1)").arg(u), bi,
2374 QString("setIncludeImagesVertically (%1)").arg(r),
2375 QString("Include images vertically in %1").arg(getObjectName(bi)));
2376 bi->setIncludeImagesVer(b);
2377 emitDataChanged(bi);
2382 void VymModel::setIncludeImagesHor(bool b)
2384 BranchItem *bi = getSelectedBranch();
2385 if (bi && b != bi->getIncludeImagesHor()) {
2386 QString u = b ? "false" : "true";
2387 QString r = !b ? "false" : "true";
2389 saveState(bi, QString("setIncludeImagesHorizontally (%1)").arg(u), bi,
2390 QString("setIncludeImagesHorizontally (%1)").arg(r),
2391 QString("Include images horizontally in %1")
2392 .arg(getObjectName(bi)));
2393 bi->setIncludeImagesHor(b);
2394 emitDataChanged(bi);
2399 void VymModel::setChildrenLayout(
2400 BranchItem::LayoutHint layoutHint) // FIXME-3 no savestate yet
2402 BranchItem *bi = getSelectedBranch();
2405 QString u= b ? "false" : "true";
2406 QString r=!b ? "false" : "true";
2410 QString("setIncludeImagesHorizontally (%1)").arg(u),
2412 QString("setIncludeImagesHorizontally (%1)").arg(r),
2413 QString("Include images horizontally in %1").arg(getObjectName(bi))
2416 bi->setChildrenLayout(layoutHint);
2417 emitDataChanged(bi);
2422 void VymModel::setHideLinkUnselected(bool b)
2424 TreeItem *ti = getSelectedItem();
2425 if (ti && (ti->getType() == TreeItem::Image || ti->isBranchLikeType())) {
2426 QString u = b ? "false" : "true";
2427 QString r = !b ? "false" : "true";
2430 ti, QString("setHideLinkUnselected (%1)").arg(u), ti,
2431 QString("setHideLinkUnselected (%1)").arg(r),
2432 QString("Hide link of %1 if unselected").arg(getObjectName(ti)));
2433 ((MapItem *)ti)->setHideLinkUnselected(b);
2437 void VymModel::setHideExport(bool b, TreeItem *ti)
2440 ti = getSelectedItem();
2441 if (ti && (ti->getType() == TreeItem::Image || ti->isBranchLikeType()) &&
2442 ti->hideInExport() != b) {
2443 ti->setHideInExport(b);
2444 QString u = b ? "false" : "true";
2445 QString r = !b ? "false" : "true";
2447 saveState(ti, QString("setHideExport (%1)").arg(u), ti,
2448 QString("setHideExport (%1)").arg(r),
2449 QString("Set HideExport flag of %1 to %2")
2450 .arg(getObjectName(ti))
2452 emitDataChanged(ti);
2453 emitSelectionChanged();
2458 void VymModel::toggleHideExport()
2460 QList<TreeItem *> selItems = getSelectedItems();
2461 if (selItems.count() > 0) {
2462 foreach (TreeItem *ti, selItems) {
2463 bool b = !ti->hideInExport();
2464 setHideExport(b, ti);
2469 void VymModel::toggleTask()
2471 BranchItem *selbi = getSelectedBranch();
2473 saveStateChangingPart(
2474 selbi, selbi, QString("toggleTask()"),
2475 QString("Toggle task of %1").arg(getObjectName(selbi)));
2476 Task *task = selbi->getTask();
2478 task = taskModel->createTask(selbi);
2479 taskEditor->select(task);
2482 taskModel->deleteTask(task);
2484 emitDataChanged(selbi);
2485 emitSelectionChanged();
2490 bool VymModel::cycleTaskStatus(bool reverse)
2492 BranchItem *selbi = getSelectedBranch();
2494 Task *task = selbi->getTask();
2496 saveStateChangingPart(
2497 selbi, selbi, QString("cycleTask()"),
2498 QString("Toggle task of %1").arg(getObjectName(selbi)));
2499 task->cycleStatus(reverse);
2500 task->setDateModification();
2502 // make sure task is still visible
2503 taskEditor->select(task);
2504 emitDataChanged(selbi);
2512 bool VymModel::setTaskSleep(const QString &s)
2515 BranchItem *selbi = getSelectedBranch();
2516 if (selbi && !s.isEmpty()) {
2517 Task *task = selbi->getTask();
2519 QDateTime oldSleep = task->getSleep();
2521 // Parse the string, which could be days, hours or one of several
2525 ok = task->setSecsSleep(0);
2528 QRegExp re("^\\s*(\\d+)\\s*$");
2529 re.setMinimal(false);
2530 int pos = re.indexIn(s);
2532 // Found only digit, considered as days
2533 ok = task->setDaysSleep(re.cap(1).toInt());
2536 QRegExp re("^\\s*(\\d+)\\s*h\\s*$");
2537 pos = re.indexIn(s);
2539 // Found digit followed by "h", considered as hours
2540 ok = task->setHoursSleep(re.cap(1).toInt());
2543 QRegExp re("^\\s*(\\d+)\\s*w\\s*$");
2544 pos = re.indexIn(s);
2546 // Found digit followed by "w", considered as weeks
2547 ok = task->setDaysSleep(7 * re.cap(1).toInt());
2550 QRegExp re("^\\s*(\\d+)\\s*s\\s*$");
2551 pos = re.indexIn(s);
2553 // Found digit followed by "s", considered as
2555 ok = task->setSecsSleep(re.cap(1).toInt());
2558 ok = task->setDateSleep(
2559 s); // ISO date YYYY-MM-DDTHH:mm:ss
2562 QRegExp re("(\\d+)\\.(\\d+)\\.(\\d+)");
2563 re.setMinimal(false);
2564 int pos = re.indexIn(s);
2565 QStringList list = re.capturedTexts();
2569 QDate(list.at(3).toInt(),
2571 list.at(1).toInt()).startOfDay());
2572 // d = QDate(list.at(3).toInt(),
2573 // list.at(2).toInt(),
2574 // list.at(1).toInt()).startOfDay();
2575 ok = task->setDateSleep(
2576 d); // German format,
2580 re.setPattern("(\\d+)\\.(\\d+)\\.");
2581 pos = re.indexIn(s);
2582 list = re.capturedTexts();
2584 int month = list.at(2).toInt();
2585 int day = list.at(1).toInt();
2587 QDate::currentDate().year();
2589 QDate(year, month, day).startOfDay());
2590 // d = QDate(year, month,
2591 // day).startOfDay();
2592 if (QDateTime::currentDateTime()
2596 QDate(year, month, day).startOfDay());
2597 // d = QDate(year, month,
2598 // day).startOfDay();
2600 ok = task->setDateSleep(
2601 d); // Short German format,
2605 re.setPattern("(\\d+)\\:(\\d+)");
2606 pos = re.indexIn(s);
2607 list = re.capturedTexts();
2609 int hour = list.at(1).toInt();
2610 int min = list.at(2).toInt();
2612 QDate::currentDate(),
2614 ok = task->setDateSleep(
2627 QString oldSleepString;
2628 if (oldSleep.isValid())
2629 oldSleepString = oldSleep.toString(Qt::ISODate);
2632 "1970-01-26T00:00:00"; // Some date long ago
2634 QString newSleepString = task->getSleep().toString(Qt::ISODate);
2635 task->setDateModification();
2636 selbi->updateTaskFlag(); // If tasks changes awake mode, then
2637 // flag needs to change
2639 selbi, QString("setTaskSleep (\"%1\")").arg(oldSleepString),
2640 selbi, QString("setTaskSleep (\"%1\")").arg(newSleepString),
2641 QString("setTaskSleep (\"%1\")").arg(newSleepString));
2642 emitDataChanged(selbi);
2651 void VymModel::setTaskPriorityDelta(const int &pd, BranchItem *bi)
2653 QList<BranchItem *> selbis;
2657 selbis = getSelectedBranches();
2659 foreach (BranchItem *selbi, selbis) {
2660 Task *task = selbi->getTask();
2663 QString("setTaskPriorityDelta (%1)")
2664 .arg(task->getPriorityDelta()),
2666 QString("setTaskPriorityDelta (%1)")
2668 "Set delta for priority of task");
2669 task->setPriorityDelta(pd);
2670 emitDataChanged(selbi);
2675 int VymModel::getTaskPriorityDelta()
2677 BranchItem *selbi = getSelectedBranch();
2679 Task *task = selbi->getTask();
2681 return task->getPriorityDelta();
2686 int VymModel::taskCount() { return taskModel->count(this); }
2688 void VymModel::updateTasksAlarm(bool force)
2690 if (taskModel->updateAwake(force) || force) {
2695 BranchItem *VymModel::addTimestamp()
2697 BranchItem *selbi = addNewBranch();
2699 QDate today = QDate::currentDate();
2701 selbi->setHeadingPlainText(QString("%1-%2-%3")
2702 .arg(today.year(), 4, 10, c)
2703 .arg(today.month(), 2, 10, c)
2704 .arg(today.day(), 2, 10, c));
2705 emitDataChanged(selbi);
2712 void VymModel::copy()
2717 QList<TreeItem *> itemList = getSelectedItems();
2719 QStringList clipboardFiles;
2721 if (itemList.count() > 0) {
2724 foreach (TreeItem *ti, itemList) {
2725 fn = QString("%1/%2-%3.xml")
2729 QString content = saveToDir(clipboardDir, clipboardFile,
2730 FlagRowMaster::NoFlags, QPointF(), ti);
2732 if (!saveStringToDisk(fn, content))
2733 qWarning() << "ME::saveStringToDisk failed: " << fn;
2736 clipboardFiles.append(fn);
2739 QClipboard *clipboard = QApplication::clipboard();
2740 QMimeData *mimeData = new QMimeData;
2741 mimeData->setData("application/x-vym", clipboardFiles.join(",").toLatin1());
2742 clipboard->setMimeData(mimeData);
2746 void VymModel::paste()
2751 BranchItem *selbi = getSelectedBranch();
2754 const QClipboard *clipboard = QApplication::clipboard();
2755 const QMimeData *mimeData = clipboard->mimeData();
2757 if (mimeData->formats().contains("application/x-vym")) {
2758 QStringList clipboardFiles = QString(mimeData->data("application/x-vym")).split(",");
2760 saveStateChangingPart(selbi, selbi, QString("paste ()"),
2763 bool zippedOrg = zipped;
2764 foreach(QString fn, clipboardFiles) {
2765 if (File::Success != loadMap(fn, ImportAdd, VymMap, SlideContent))
2766 qWarning() << "VM::paste Loading clipboard failed: " << fn;
2770 } else if (mimeData->hasImage()) {
2771 QImage image = qvariant_cast<QImage>(mimeData->imageData());
2772 QString fn = clipboardDir + "/" + "image.png";
2773 if (!image.save(fn))
2774 qWarning() << "VM::paste Could not save copy of image in system clipboard";
2776 ImageItem *ii = loadImage(selbi, fn);
2778 setScaleFactor(300.0 / image.width(), ii); // FIXME-2 Better use user-defined fixed width
2780 } else if (mimeData->hasHtml()) {
2781 //setText(mimeData->html());
2782 //setTextFormat(Qt::RichText);
2783 qDebug() << "VM::paste found html...";
2784 } else if (mimeData->hasText()) {
2785 //setText(mimeData->text());
2786 //setTextFormat(Qt::PlainText);
2787 qDebug() << "VM::paste found text...";
2789 qWarning() << "VM::paste Cannot paste data, mimeData->formats=" << mimeData->formats();
2794 void VymModel::cut()
2803 bool VymModel::moveUp(BranchItem *bi)
2808 bool oldState = saveStateBlocked;
2809 saveStateBlocked = true;
2810 bool result = false;
2811 if (bi && bi->canMoveUp())
2813 relinkBranch(bi, (BranchItem *)bi->parent(), bi->num() - 1, false);
2814 saveStateBlocked = oldState;
2818 void VymModel::moveUp()
2820 BranchItem *selbi = getSelectedBranch();
2822 QString oldsel = getSelectString(selbi);
2823 if (moveUp(selbi)) {
2824 saveState(getSelectString(selbi), "moveDown ()", oldsel,
2826 QString("Move up %1").arg(getObjectName(selbi)));
2832 bool VymModel::moveDown(BranchItem *bi)
2837 bool oldState = saveStateBlocked;
2838 saveStateBlocked = true;
2839 bool result = false;
2840 if (bi && bi->canMoveDown())
2842 relinkBranch(bi, (BranchItem *)bi->parent(), bi->num() + 1, false);
2843 saveStateBlocked = oldState;
2847 void VymModel::moveDown()
2849 BranchItem *selbi = getSelectedBranch();
2851 QString oldsel = getSelectString(selbi);
2852 if (moveDown(selbi)) {
2853 saveState(getSelectString(selbi), "moveUp ()", oldsel,
2855 QString("Move down %1").arg(getObjectName(selbi)));
2861 void VymModel::moveUpDiagonally()
2863 BranchItem *selbi = getSelectedBranch();
2865 BranchItem *parent = selbi->parentBranch();
2866 if (parent == rootItem) return;
2868 int n = selbi->num();
2871 BranchItem *dst = parent->getBranchNum(n-1);
2874 relinkBranch(selbi, dst, dst->branchCount() + 1, true);
2878 void VymModel::moveDownDiagonally()
2880 BranchItem *selbi = getSelectedBranch();
2882 BranchItem *parent = selbi->parentBranch();
2883 if (parent == rootItem) return;
2884 BranchItem *parentParent = parent->parentBranch();
2885 int n = parent->num();
2887 relinkBranch(selbi, parentParent, n + 1, true);
2891 void VymModel::detach()
2893 BranchItem *selbi = getSelectedBranch();
2894 if (selbi && selbi->depth() > 0) {
2895 // if no relPos have been set before, try to use current rel positions
2896 if (selbi->getLMO())
2897 for (int i = 0; i < selbi->branchCount(); ++i)
2898 selbi->getBranchNum(i)->getBranchObj()->setRelPos();
2900 QString oldsel = getSelectString();
2901 int n = selbi->num();
2903 BranchObj *bo = selbi->getBranchObj();
2905 p = bo->getAbsPos();
2906 QString parsel = getSelectString(selbi->parent());
2907 if (relinkBranch(selbi, rootItem, -1, true))
2908 saveState(getSelectString(selbi),
2909 QString("relinkTo (\"%1\",%2,%3,%4)")
2914 oldsel, "detach ()",
2915 QString("Detach %1").arg(getObjectName(selbi)));
2919 void VymModel::sortChildren(bool inverse)
2921 BranchItem *selbi = getSelectedBranch();
2923 if (selbi->branchCount() > 1) {
2925 saveStateChangingPart(
2926 selbi, selbi, "sortChildren ()",
2927 QString("Sort children of %1").arg(getObjectName(selbi)));
2929 saveStateChangingPart(selbi, selbi, "sortChildren (false)",
2930 QString("Inverse sort children of %1")
2931 .arg(getObjectName(selbi)));
2933 selbi->sortChildren(inverse);
2940 BranchItem *VymModel::createMapCenter()
2942 BranchItem *newbi = addMapCenter(QPointF(0, 0));
2946 BranchItem *VymModel::createBranch(BranchItem *dst)
2949 return addNewBranchInt(dst, -2);
2954 ImageItem *VymModel::createImage(BranchItem *dst)
2960 ImageItem *newii = new ImageItem();
2961 // newii->setHeading (QApplication::translate("Heading of new image in
2962 // map", "new image"));
2964 emit(layoutAboutToBeChanged());
2967 if (!parix.isValid())
2968 qDebug() << "VM::createII invalid index\n";
2969 n = dst->getRowNumAppend(newii);
2970 beginInsertRows(parix, n, n);
2971 dst->appendChild(newii);
2974 emit(layoutChanged());
2976 // save scroll state. If scrolled, automatically select
2977 // new branch in order to tmp unscroll parent...
2978 newii->createMapObj();
2979 latestAddedItem = newii;
2986 bool VymModel::createLink(Link *link)
2988 BranchItem *begin = link->getBeginBranch();
2989 BranchItem *end = link->getEndBranch();
2991 if (!begin || !end) {
2992 qWarning() << "VM::createXLinkNew part of XLink is NULL";
2998 qDebug() << "VymModel::createLink begin==end, aborting";
3002 // check, if link already exists
3003 foreach (Link *l, xlinks) {
3004 if ((l->getBeginBranch() == begin && l->getEndBranch() == end) ||
3005 (l->getBeginBranch() == end && l->getEndBranch() == begin)) {
3006 qWarning() << "VymModel::createLink link exists already, aborting";
3014 XLinkItem *newli = new XLinkItem();
3015 newli->setLink(link);
3016 link->setBeginLinkItem(newli);
3018 emit(layoutAboutToBeChanged());
3020 parix = index(begin);
3021 n = begin->getRowNumAppend(newli);
3022 beginInsertRows(parix, n, n);
3023 begin->appendChild(newli);
3026 newli = new XLinkItem();
3027 newli->setLink(link);
3028 link->setEndLinkItem(newli);
3031 n = end->getRowNumAppend(newli);
3032 beginInsertRows(parix, n, n);
3033 end->appendChild(newli);
3036 emit(layoutChanged());
3038 xlinks.append(link);
3041 latestAddedItem = newli;
3043 if (!link->getMO()) {
3044 link->createMapObj();
3050 link->setStyleBegin(defXLinkStyleBegin);
3051 link->setStyleEnd(defXLinkStyleEnd);
3055 QColor VymModel::getXLinkColor()
3057 Link *l = getSelectedXLink();
3059 return l->getPen().color();
3064 int VymModel::getXLinkWidth()
3066 Link *l = getSelectedXLink();
3068 return l->getPen().width();
3073 Qt::PenStyle VymModel::getXLinkStyle()
3075 Link *l = getSelectedXLink();
3077 return l->getPen().style();
3082 QString VymModel::getXLinkStyleBegin()
3084 Link *l = getSelectedXLink();
3086 return l->getStyleBeginString();
3091 QString VymModel::getXLinkStyleEnd()
3093 Link *l = getSelectedXLink();
3095 return l->getStyleEndString();
3100 AttributeItem *VymModel::setAttribute() // FIXME-3 Experimental, savestate missing
3103 BranchItem *selbi = getSelectedBranch();
3105 AttributeItem *ai = new AttributeItem();
3106 ai->setAttributeType(AttributeItem::String);
3107 ai->setKey("Foo Attrib");
3108 ai->setValue(QString("Att val"));
3110 return setAttribute(selbi, ai);
3115 AttributeItem *VymModel::setAttribute(BranchItem *dst, AttributeItem *ai_new)
3119 // Check if there is already an attribute with same key
3121 for (int i = 0; i < dst->attributeCount(); i++) {
3122 ai = dst->getAttributeNum(i);
3123 if (ai->getKey() == ai_new->getKey())
3125 // Key exists, overwrite value
3128 // Delete original attribute, this is basically a move...
3130 emitDataChanged(dst);
3135 // Create new attribute
3136 emit(layoutAboutToBeChanged());
3138 QModelIndex parix = index(dst);
3139 int n = dst->getRowNumAppend(ai_new);
3140 beginInsertRows(parix, n, n);
3141 dst->appendChild(ai_new);
3144 emit(layoutChanged());
3146 emitDataChanged(dst);
3147 return ai_new; // FIXME-3 Check if ai is used or deleted - deep copy here?
3152 BranchItem *VymModel::addMapCenter(bool saveStateFlag)
3154 if (!hasContextPos) {
3155 // E.g. when called via keypresss:
3156 // Place new MCO in middle of existing ones,
3157 // Useful for "brainstorming" mode...
3158 contextPos = QPointF();
3161 for (int i = 0; i < rootItem->branchCount(); ++i) {
3162 bi = rootItem->getBranchNum(i);
3163 bo = (BranchObj *)bi->getLMO();
3165 contextPos += bo->getAbsPos();
3167 if (rootItem->branchCount() > 1)
3168 contextPos *= 1 / (qreal)(rootItem->branchCount());
3171 BranchItem *bi = addMapCenter(contextPos);
3173 emitShowSelection();
3175 saveState(bi, "remove()", NULL,
3176 QString("addMapCenter (%1,%2)")
3177 .arg(contextPos.x())
3178 .arg(contextPos.y()),
3179 QString("Adding MapCenter to (%1,%2)")
3180 .arg(contextPos.x())
3181 .arg(contextPos.y()));
3186 BranchItem *VymModel::addMapCenter(QPointF absPos)
3187 // createMapCenter could then probably be merged with createBranch
3191 QModelIndex parix = index(rootItem);
3193 BranchItem *newbi = new BranchItem(rootItem);
3194 newbi->setHeadingPlainText(tr("New map", "New map"));
3195 int n = rootItem->getRowNumAppend(newbi);
3197 emit(layoutAboutToBeChanged());
3198 beginInsertRows(parix, n, n);
3200 rootItem->appendChild(newbi);
3203 emit(layoutChanged());
3206 newbi->setPositionMode(MapItem::Absolute);
3207 BranchObj *bo = newbi->createMapObj(mapEditor->getScene());
3214 BranchItem *VymModel::addNewBranchInt(BranchItem *dst, int pos)
3216 // Depending on pos:
3217 // -3 insert in children of parent above selection
3218 // -2 add branch to selection
3219 // -1 insert in children of parent below selection
3220 // 0..n insert in children of parent at pos
3223 BranchItem *parbi = dst;
3225 BranchItem *newbi = new BranchItem();
3227 emit(layoutAboutToBeChanged());
3230 n = parbi->getRowNumAppend(newbi);
3231 beginInsertRows(index(parbi), n, n);
3232 parbi->appendChild(newbi);
3235 else if (pos == -1 || pos == -3) {
3236 // insert below selection
3237 parbi = (BranchItem *)dst->parent();
3238 n = dst->childNumber() + (3 + pos) / 2; //-1 |-> 1;-3 |-> 0
3239 beginInsertRows(index(parbi), n, n);
3240 parbi->insertBranch(n, newbi);
3244 n = parbi->getRowNumAppend(newbi) - (parbi->branchCount() - pos);
3245 beginInsertRows(index(parbi), n, n);
3246 parbi->insertBranch(pos, newbi);
3249 emit(layoutChanged());
3251 newbi->createMapObj(mapEditor->getScene());
3253 // Set color of heading to that of parent
3254 newbi->setHeadingColor(parbi->getHeadingColor());
3260 BranchItem *VymModel::addNewBranch(BranchItem *bi, int pos)
3262 BranchItem *newbi = NULL;
3264 bi = getSelectedBranch();
3267 QString redosel = getSelectString(bi);
3268 newbi = addNewBranchInt(bi, pos);
3269 QString undosel = getSelectString(newbi);
3272 saveState(undosel, "remove ()", redosel,
3273 QString("addBranch (%1)").arg(pos),
3274 QString("Add new branch to %1").arg(getObjectName(bi)));
3276 latestAddedItem = newbi;
3277 // In Network mode, the client needs to know where the new branch
3278 // is, so we have to pass on this information via saveState.
3279 // TODO: Get rid of this positioning workaround
3280 /* FIXME-4 network problem: QString ps=qpointfToString
3281 (newbo->getAbsPos()); sendData ("selectLatestAdded ()"); sendData
3282 (QString("move %1").arg(ps)); sendSelection();
3289 BranchItem *VymModel::addNewBranchBefore()
3291 BranchItem *newbi = NULL;
3292 BranchItem *selbi = getSelectedBranch();
3293 if (selbi && selbi->getType() == TreeItem::Branch)
3294 // We accept no MapCenter here, so we _have_ a parent
3296 // add below selection
3297 newbi = addNewBranchInt(selbi, -1);
3301 newbi, "remove ()", newbi, "addBranchBefore ()",
3302 QString("Add branch before %1").arg(getObjectName(selbi)));
3304 // newbi->move2RelPos (p);
3306 // Move selection to new branch
3307 relinkBranch(selbi, newbi, 0, true);
3309 // Use color of child instead of parent
3310 newbi->setHeadingColor(selbi->getHeadingColor());
3311 emitDataChanged(newbi);
3317 bool VymModel::relinkBranch(BranchItem *branch, BranchItem *dst, int pos,
3318 bool updateSelection, QPointF orgPos)
3320 if (branch && dst) {
3321 // Check if we relink to ourselves
3322 if (dst->isChildOf(branch))
3325 if (updateSelection)
3328 // Do we need to update frame type?
3329 bool keepFrame = true;
3331 // Save old position for savestate
3332 QString preSelStr = getSelectString(branch);
3333 QString preNum = QString::number(branch->num(), 10);
3334 QString preParStr = getSelectString(branch->parent());
3336 emit(layoutAboutToBeChanged());
3337 BranchItem *branchpi = (BranchItem *)branch->parent();
3338 // Remove at current position
3339 int n = branch->childNum();
3341 // If branch and dst have same parent, then pos needs to be adjusted
3342 // after removing branch
3343 if (branchpi == dst && pos - 1 > n ) pos--;
3345 beginRemoveRows(index(branchpi), n, n);
3346 branchpi->removeChild(n);
3349 if (pos < 0 || pos > dst->branchCount())
3350 pos = dst->branchCount();
3352 // Append as last branch to dst
3353 if (dst->branchCount() == 0)
3356 n = dst->getFirstBranch()->childNumber();
3358 beginInsertRows(index(dst), n + pos, n + pos);
3359 dst->insertBranch(pos, branch);
3362 // Correct type if necessesary
3363 if (branch->getType() == TreeItem::MapCenter && branch->depth() > 0) {
3364 branch->setType(TreeItem::Branch);
3368 // reset parObj, fonts, frame, etc in related LMO or other view-objects
3369 branch->updateStyles(keepFrame);
3371 emitDataChanged(branch);
3372 reposition(); // both for moveUp/Down and relinking
3375 QString postSelStr = getSelectString(branch);
3376 QString postNum = QString::number(branch->num(), 10);
3379 LinkableMapObj *lmosel = branch->getLMO();
3381 savePos = lmosel->getAbsPos();
3383 if (!saveStateBlocked) { // Don't build strings when moving up/down
3385 "relinkTo (\"" + preParStr + "\"," + preNum + "," +
3386 QString("%1,%2").arg(orgPos.x()).arg(orgPos.y()) + ")";
3389 "relinkTo (\"" + getSelectString(dst) + "\"," + postNum + "," +
3390 QString("%1,%2").arg(savePos.x()).arg(savePos.y()) + ")";
3392 saveState(postSelStr, undoCom, preSelStr, redoCom,
3393 QString("Relink %1 to %2")
3394 .arg(getObjectName(branch))
3395 .arg(getObjectName(dst)));
3398 // New parent might be invisible
3399 branch->updateVisibility();
3401 if (dst->isScrolled()) {
3402 if (updateSelection)
3405 else if (updateSelection)
3412 bool VymModel::relinkImage(ImageItem *image, BranchItem *dst)
3415 emit(layoutAboutToBeChanged());
3417 BranchItem *pi = (BranchItem *)(image->parent());
3418 QString oldParString = getSelectString(pi);
3419 // Remove at current position
3420 int n = image->childNum();
3421 beginRemoveRows(index(pi), n, n);
3426 QModelIndex dstix = index(dst);
3427 n = dst->getRowNumAppend(image);
3428 beginInsertRows(dstix, n, n);
3429 dst->appendChild(image);
3432 // Set new parent also for lmo
3433 if (image->getLMO() && dst->getLMO())
3434 image->getLMO()->setParObj(dst->getLMO());
3436 emit(layoutChanged());
3437 saveState(image, QString("relinkTo (\"%1\")").arg(oldParString), image,
3438 QString("relinkTo (\"%1\")").arg(getSelectString(dst)),
3439 QString("Relink floatimage to %1").arg(getObjectName(dst)));
3445 bool VymModel::relinkTo(const QString &dest, int num, QPointF pos)
3447 TreeItem *selti = getSelectedItem();
3449 return false; // Nothing selected to relink
3451 TreeItem *dst = findBySelectString(dest);
3453 if (selti->isBranchLikeType()) {
3454 BranchItem *selbi = (BranchItem *)selti;
3456 return false; // Could not find destination
3458 if (dst->getType() == TreeItem::Branch) {
3459 // Now try to relink to branch
3460 if (relinkBranch(selbi, (BranchItem *)dst, num, true)) {
3461 emitSelectionChanged();
3465 return false; // Relinking failed
3467 else if (dst->getType() == TreeItem::MapCenter) {
3468 if (relinkBranch(selbi, (BranchItem *)dst, -1, true)) {
3469 // Get coordinates of mainbranch
3470 if (selbi->getLMO()) {
3471 ((BranchObj *)selbi->getLMO())->move(pos);
3472 ((BranchObj *)selbi->getLMO())->setRelPos();
3475 emitSelectionChanged();
3479 return false; // Relinking failed
3481 else if (selti->getType() == TreeItem::Image) {
3482 if (dst->isBranchLikeType())
3483 if (relinkImage(((ImageItem *)selti), (BranchItem *)dst))
3486 return false; // Relinking failed
3489 void VymModel::cleanupItems()
3491 while (!deleteLaterIDs.isEmpty()) {
3492 TreeItem *ti = findID(deleteLaterIDs.takeFirst());
3498 void VymModel::deleteLater(uint id)
3500 if (!deleteLaterIDs.contains(id))
3501 deleteLaterIDs.append(id);
3504 void VymModel::deleteSelection()
3506 QList<uint> selectedIDs = getSelectedIDs();
3510 foreach (uint id, selectedIDs) {
3511 TreeItem *ti = findID(id);
3513 if (ti->isBranchLikeType()) { // Delete branch
3514 BranchItem *selbi = (BranchItem *)ti;
3515 saveStateRemovingPart(
3516 selbi, QString("remove %1").arg(getObjectName(selbi)));
3518 BranchItem *pi = (BranchItem *)(deleteItem(selbi));
3520 if (pi->isScrolled() && pi->branchCount() == 0)
3522 emitDataChanged(pi);
3526 emitDataChanged(rootItem);
3530 // Delete other item
3531 TreeItem *pi = ti->parent();
3533 if (ti->getType() == TreeItem::Image ||
3534 ti->getType() == TreeItem::Attribute ||
3535 ti->getType() == TreeItem::XLink) {
3536 saveStateChangingPart(
3537 pi, ti, "remove ()",
3538 QString("Remove %1").arg(getObjectName(ti)));
3541 emitDataChanged(pi);
3547 "VymmModel::deleteSelection() unknown type?!");
3554 void VymModel::deleteKeepChildren(bool saveStateFlag)
3555 // deleteKeepChildren FIXME-3+ does not work yet for mapcenters
3556 // deleteKeepChildren FIXME-3+ children of scrolled branch stay invisible...
3558 BranchItem *selbi = getSelectedBranch();
3561 // Don't use this on mapcenter
3562 if (selbi->depth() < 1)
3565 pi = (BranchItem *)(selbi->parent());
3566 // Check if we have children at all to keep
3567 if (selbi->branchCount() == 0) {
3573 if (selbi->getLMO())
3574 p = selbi->getLMO()->getRelPos();
3576 saveStateChangingPart(pi, pi, "removeKeepChildren ()",
3577 QString("Remove %1 and keep its children")
3578 .arg(getObjectName(selbi)));
3580 QString sel = getSelectString(selbi);
3582 bool oldSaveState = saveStateBlocked;
3583 saveStateBlocked = true;
3584 int pos = selbi->num();
3585 BranchItem *bi = selbi->getFirstBranch();
3587 relinkBranch(bi, pi, pos, true);
3588 bi = selbi->getFirstBranch();
3593 emitDataChanged(pi);
3595 BranchObj *bo = getSelectedBranchObj();
3600 saveStateBlocked = oldSaveState;
3604 void VymModel::deleteChildren()
3607 BranchItem *selbi = getSelectedBranch();
3609 saveStateChangingPart(
3610 selbi, selbi, "removeChildren ()",
3611 QString("Remove children of branch %1").arg(getObjectName(selbi)));
3612 emit(layoutAboutToBeChanged());
3614 QModelIndex ix = index(selbi);
3615 int n = selbi->branchCount() - 1;
3616 beginRemoveRows(ix, 0, n);
3617 removeRows(0, n + 1, ix);
3619 if (selbi->isScrolled())
3620 unscrollBranch(selbi);
3621 emit(layoutChanged());
3626 TreeItem *VymModel::deleteItem(TreeItem *ti)
3629 TreeItem *pi = ti->parent();
3630 // qDebug()<<"VM::deleteItem start ti="<<ti<<" "<<ti->getHeading()<<"
3631 // pi="<<pi<<"="<<pi->getHeading();
3633 TreeItem::Type t = ti->getType();
3635 QModelIndex parentIndex = index(pi);
3637 emit(layoutAboutToBeChanged());
3639 int n = ti->childNum();
3640 beginRemoveRows(parentIndex, n, n);
3641 removeRows(n, 1, parentIndex);
3644 // Size of parent branch might change when deleting images
3645 if (t == TreeItem::Image) {
3646 BranchObj *bo = (BranchObj *)(((BranchItem *)pi)->getMO());
3653 emit(layoutChanged());
3654 emitUpdateQueries();
3655 if (!cleaningUpLinks)
3658 // qDebug()<<"VM::deleteItem end ti="<<ti;
3659 if (pi->depth() >= 0)
3665 void VymModel::deleteLink(Link *l)
3667 if (xlinks.removeOne(l))
3671 void VymModel::clearItem(TreeItem *ti)
3674 // Clear task (or other data in item itself)
3677 QModelIndex parentIndex = index(ti);
3678 if (!parentIndex.isValid())
3681 int n = ti->childCount();
3685 emit(layoutAboutToBeChanged());
3687 beginRemoveRows(parentIndex, 0, n - 1);
3688 removeRows(0, n, parentIndex);
3693 emit(layoutChanged());
3698 bool VymModel::scrollBranch(BranchItem *bi)
3701 if (bi->isScrolled())
3703 if (bi->branchCount() == 0)
3705 if (bi->depth() == 0)
3707 if (bi->toggleScroll()) {
3711 saveState(bi, QString("%1 ()").arg(u), bi, QString("%1 ()").arg(r),
3712 QString("%1 %2").arg(r).arg(getObjectName(bi)));
3713 emitDataChanged(bi);
3714 emitSelectionChanged();
3716 mapEditor->getScene()
3717 ->update(); // Needed for _quick_ update, even in 1.13.x
3724 bool VymModel::unscrollBranch(BranchItem *bi)
3727 if (!bi->isScrolled())
3729 if (bi->toggleScroll()) {
3733 saveState(bi, QString("%1 ()").arg(u), bi, QString("%1 ()").arg(r),
3734 QString("%1 %2").arg(r).arg(getObjectName(bi)));
3735 emitDataChanged(bi);
3736 emitSelectionChanged();
3738 mapEditor->getScene()
3739 ->update(); // Needed for _quick_ update, even in 1.13.x
3746 void VymModel::toggleScroll()
3748 BranchItem *selbi = getSelectedBranch();
3750 if (selbi->isScrolled())
3751 unscrollBranch(selbi);
3753 scrollBranch(selbi);
3754 // Note: saveState & reposition are called in above functions
3758 void VymModel::unscrollChildren()
3760 BranchItem *selbi = getSelectedBranch();
3762 saveStateChangingPart(
3763 selbi, selbi, QString("unscrollChildren ()"),
3764 QString("unscroll all children of %1").arg(getObjectName(selbi)));
3765 BranchItem *prev = NULL;
3766 BranchItem *cur = NULL;
3767 nextBranch(cur, prev, true, selbi);
3769 if (cur->isScrolled()) {
3770 cur->toggleScroll();
3771 emitDataChanged(cur);
3773 nextBranch(cur, prev, true, selbi);
3777 // Would this help??? emitSelectionChanged();
3781 void VymModel::setScaleFactor(qreal f, ImageItem *selii)
3784 selii = getSelectedImage();
3787 qreal f_old = selii->getScaleFactor();
3788 selii->setScaleFactor(f);
3789 saveState(selii, QString("setScaleFactor(%1)").arg(f_old), selii,
3790 QString("setScaleFactor(%1)").arg(f),
3791 QString("Scale %1").arg(getObjectName(selii)));
3796 void VymModel::growSelectionSize() // FIXME-3 Also for heading in BranchItem?
3798 ImageItem *selii = getSelectedImage();
3801 qreal sx = selii->getScaleFactor();
3802 setScaleFactor(sx + f);
3806 void VymModel::shrinkSelectionSize()
3808 ImageItem *selii = getSelectedImage();
3811 qreal sx = selii->getScaleFactor();
3812 setScaleFactor(sx - f);
3816 void VymModel::resetSelectionSize()
3818 ImageItem *selii = getSelectedImage();
3823 void VymModel::emitExpandAll() { emit(expandAll()); }
3825 void VymModel::emitExpandOneLevel() { emit(expandOneLevel()); }
3827 void VymModel::emitCollapseOneLevel() { emit(collapseOneLevel()); }
3829 void VymModel::emitCollapseUnselected() { emit(collapseUnselected()); }
3831 void VymModel::toggleTarget()
3833 foreach (TreeItem *ti, getSelectedItems()) {
3834 if (ti->isBranchLikeType()) {
3835 ((BranchItem*)ti)->toggleTarget();
3836 saveState(ti, "toggleTarget()", ti, "toggleTarget()",
3843 ItemList VymModel::getLinkedMaps()
3847 // rmodel->setSearchString (s);
3849 BranchItem *cur = NULL;
3850 BranchItem *prev = NULL;
3851 nextBranch(cur, prev);
3856 if (cur->hasActiveSystemFlag("system-target") &&
3857 !cur->getVymLink().isEmpty()) {
3858 s = cur->getHeading().getTextASCII();
3859 s.replace(QRegularExpression("\n+"), " ");
3860 s.replace(QRegularExpression("\\s+"), " ");
3861 s.replace(QRegularExpression("^\\s+"), "");
3865 sl << cur->getVymLink();
3867 targets[cur->getID()] = sl;
3869 nextBranch(cur, prev);
3874 ItemList VymModel::getTargets()
3878 // rmodel->setSearchString (s);
3880 BranchItem *cur = NULL;
3881 BranchItem *prev = NULL;
3882 nextBranch(cur, prev);
3887 if (cur->hasActiveSystemFlag("system-target")) {
3888 s = cur->getHeading().getTextASCII();
3889 s.replace(QRegularExpression("\n+"), " ");
3890 s.replace(QRegularExpression("\\s+"), " ");
3891 s.replace(QRegularExpression("^\\s+"), "");
3896 targets[cur->getID()] = sl;
3898 nextBranch(cur, prev);
3903 Flag* VymModel::findFlagByName(const QString &name)
3905 BranchItem *bi = getSelectedBranch();
3908 Flag *f = standardFlagsMaster->findFlagByName(name);
3910 f = userFlagsMaster->findFlagByName(name);
3912 qWarning() << "VymModel::findFlagByName failed for flag named "
3920 // Nothing selected, so no flag found
3924 void VymModel::setFlagByName(const QString &name, bool useGroups)
3926 BranchItem *bi = getSelectedBranch();
3928 if (bi && !bi->hasActiveFlag(name)) {
3929 toggleFlagByName(name, useGroups);
3933 void VymModel::unsetFlagByName(const QString &name)
3935 BranchItem *bi = getSelectedBranch();
3937 if (bi && bi->hasActiveFlag(name)) {
3938 toggleFlagByName(name);
3942 void VymModel::toggleFlagByName(const QString &name, bool useGroups)
3944 BranchItem *bi = getSelectedBranch();
3947 Flag *f = findFlagByName(name);
3950 qWarning() << "VymModel::toggleFlagByName could not find flag named " << name;
3954 toggleFlagByUid(f->getUuid(), useGroups);
3958 void VymModel::toggleFlagByUid(
3961 // FIXME-2 saveState not correct when toggling flags in groups
3962 // (previous flags not saved!)
3964 QStringList itemList = getSelectedUUIDs();
3966 if (itemList.count() > 0) {
3971 foreach (QString id, itemList) {
3972 ti = findUuid(QUuid(id));
3973 if (ti && ti->isBranchLikeType()) {
3974 bi = (BranchItem*)ti;
3975 f = bi->toggleFlagByUid(uid, useGroups);
3978 QString u = "toggleFlagByUid";
3979 QString name = f->getName();
3980 saveState(bi, QString("%1 (\"%2\")").arg(u).arg(uid.toString()), bi,
3981 QString("%1 (\"%2\")").arg(u).arg(uid.toString()),
3982 QString("Toggling flag \"%1\" of %2")
3984 .arg(getObjectName(bi)));
3985 emitDataChanged(bi);
3987 qWarning() << "VymModel::toggleFlag failed for flag with uid "
3996 void VymModel::clearFlags()
3998 BranchItem *selbi = getSelectedBranch();
4000 selbi->deactivateAllStandardFlags();
4002 emitDataChanged(selbi);
4007 void VymModel::colorBranch(QColor c)
4009 QList<BranchItem *> selbis = getSelectedBranches();
4010 foreach (BranchItem *selbi, selbis) {
4012 QString("colorBranch (\"%1\")")
4013 .arg(selbi->getHeadingColor().name()),
4014 selbi, QString("colorBranch (\"%1\")").arg(c.name()),
4015 QString("Set color of %1 to %2")
4016 .arg(getObjectName(selbi))
4018 selbi->setHeadingColor(c); // color branch
4019 emitDataChanged(selbi);
4020 taskEditor->showSelection();
4022 mapEditor->getScene()->update();
4025 void VymModel::colorSubtree(QColor c, BranchItem *b)
4027 QList<BranchItem *> selbis;
4031 selbis = getSelectedBranches();
4033 foreach (BranchItem *bi, selbis) {
4034 saveStateChangingPart(bi, bi,
4035 QString("colorSubtree (\"%1\")").arg(c.name()),
4036 QString("Set color of %1 and children to %2")
4037 .arg(getObjectName(bi))
4039 BranchItem *prev = NULL;
4040 BranchItem *cur = NULL;
4041 nextBranch(cur, prev, true, bi);
4043 cur->setHeadingColor(c); // color links, color children
4044 emitDataChanged(cur);
4045 nextBranch(cur, prev, true, bi);
4048 taskEditor->showSelection();
4049 mapEditor->getScene()->update();
4052 QColor VymModel::getCurrentHeadingColor()
4054 BranchItem *selbi = getSelectedBranch();
4056 return selbi->getHeadingColor();
4058 QMessageBox::warning(
4060 "Can't get color of heading,\nthere's no branch selected");
4064 void VymModel::note2URLs()
4066 BranchItem *selbi = getSelectedBranch();
4068 saveStateChangingPart(
4069 selbi, selbi, QString("note2URLs()"),
4070 QString("Extract URLs from note of %1").arg(getObjectName(selbi)));
4072 QString n = selbi->getNoteASCII();
4075 QRegExp re("(http.*)(\\s|\"|')");
4076 re.setMinimal(true);
4080 while ((pos = re.indexIn(n, pos)) != -1) {
4081 bi = createBranch(selbi);
4082 bi->setHeadingPlainText(re.cap(1));
4083 bi->setURL(re.cap(1));
4084 emitDataChanged(bi);
4085 pos += re.matchedLength();
4090 void VymModel::editHeading2URL()
4092 TreeItem *selti = getSelectedItem();
4094 setURL(selti->getHeadingPlain());
4097 void VymModel::getJiraData(bool subtree) // FIXME-2 update error message, check
4098 // if jiraClientAvail is set correctly
4100 if (!JiraAgent::available()) {
4102 QString w = QObject::tr("JIRA agent not setup.");
4104 dia.setWindowTitle( tr("Warning") + ": " + w);
4105 dia.setShowAgainName("/JiraAgent/notdefined");
4108 if (!mainWindow->settingsJIRA())
4112 BranchItem *selbi = getSelectedBranch();
4113 QRegExp re("(\\w+[-|\\s]\\d+)");
4117 BranchItem *prev = nullptr;
4118 BranchItem *cur = nullptr;
4119 nextBranch(cur, prev, true, selbi);
4121 QString heading = cur->getHeadingPlain();
4123 if (re.indexIn(heading) >= 0) {
4125 JiraAgent *agent = new JiraAgent;
4126 agent->setJobType(JiraAgent::GetTicketInfo);
4127 if (!agent->setBranch(cur)) {
4128 qWarning () << "Could not set branch in JiraAgent to " << cur->getHeadingPlain();
4132 if (!agent->setTicket(heading)) {
4133 mainWindow->statusMessage(tr("Could not find Jira ticket pattern in %1", "VymModel").arg(cur->getHeadingPlain()));
4138 //setURL(agent->url(), false, cur);
4140 connect(agent, &JiraAgent::jiraTicketReady, this, &VymModel::updateJiraData);
4142 // Start contacting JIRA in background
4144 mainWindow->statusMessage(tr("Contacting Jira...", "VymModel"));
4149 nextBranch(cur, prev, true, selbi);
4156 void VymModel::updateJiraData(QJsonObject jsobj)
4158 QJsonDocument jsdoc = QJsonDocument (jsobj);
4159 QString key = jsobj["key"].toString();
4160 QJsonObject fields = jsobj["fields"].toObject();
4162 QJsonObject assigneeObj = fields["assignee"].toObject();
4163 QString assignee = assigneeObj["emailAddress"].toString();
4165 QJsonObject reporterObj = fields["reporter"].toObject();
4166 QString reporter = reporterObj["emailAddress"].toString();
4168 QJsonObject resolutionObj = fields["resolution"].toObject();
4169 QString resolution = resolutionObj["name"].toString();
4171 QJsonObject statusObj = fields["status"].toObject();
4172 QString status = statusObj["name"].toString();
4174 QString summary = fields["summary"].toString();
4176 QJsonArray componentsArray = fields["components"].toArray();
4177 QJsonObject compObj;
4179 for (int i = 0; i < componentsArray.size(); ++i) {
4180 compObj = componentsArray[i].toObject();
4181 components += compObj["name"].toString();
4184 int branchID = jsobj["vymBranchId"].toInt();
4186 QStringList solvedStates;
4187 solvedStates << "Verification Done";
4188 solvedStates << "Resolved";
4189 solvedStates << "Closed";
4191 QString keyName = key;
4192 BranchItem *bi = (BranchItem*)findID(branchID);
4194 if (solvedStates.contains(status)) {
4195 keyName = "(" + keyName + ")";
4196 colorSubtree (Qt::blue, bi);
4199 setHeadingPlainText(keyName + ": " + summary, bi);
4200 setURL(jsobj["vymTicketUrl"].toString());
4204 ai = new AttributeItem("JIRA.assignee", assignee);
4205 setAttribute(bi, ai);
4207 ai = new AttributeItem("JIRA.reporter", reporter);
4208 setAttribute(bi, ai);
4210 ai = new AttributeItem("JIRA.resolution", resolution);
4211 setAttribute(bi, ai);
4213 ai = new AttributeItem("JIRA.status", status);
4214 setAttribute(bi, ai);
4216 ai = new AttributeItem("JIRA.components", components);
4217 setAttribute(bi, ai);
4220 /* Pretty print JIRA ticket
4221 vout << jsdoc.toJson(QJsonDocument::Indented) << endl;
4222 vout << " Key: " + key << endl;
4223 vout << " Desc: " + summary << endl;
4224 vout << " Assignee: " + assignee << endl;
4225 vout << "Components: " + components << endl;
4226 vout << " Reporter: " + reporter << endl;
4227 vout << "Resolution: " + resolution << endl;
4228 vout << " Status: " + status << endl;
4231 mainWindow->statusMessage(tr("Received Jira data.", "VymModel"));
4235 void VymModel::setHeadingConfluencePageName() // FIXME-2 always asks for Confluence credentials when adding any URL
4237 BranchItem *selbi = getSelectedBranch();
4239 QString url = selbi->getURL();
4240 if (!url.isEmpty() &&
4241 settings.contains("/atlassian/confluence/url") &&
4242 url.contains(settings.value("/atlassian/confluence/url").toString())) {
4244 ConfluenceAgent *ca_setHeading = new ConfluenceAgent(selbi);
4245 ca_setHeading->setPageURL(url);
4246 ca_setHeading->setJobType(ConfluenceAgent::CopyPagenameToHeading);
4247 ca_setHeading->startJob();
4252 void VymModel::setVymLink(const QString &s)
4254 if (s.isEmpty()) return;
4256 BranchItem *bi = getSelectedBranch();
4259 bi, "setVymLink (\"" + bi->getVymLink() + "\")", bi,
4260 "setVymLink (\"" + s + "\")",
4261 QString("Set vymlink of %1 to %2").arg(getObjectName(bi)).arg(s));
4263 emitDataChanged(bi);
4268 void VymModel::deleteVymLink()
4270 BranchItem *bi = getSelectedBranch();
4272 saveState(bi, "setVymLink (\"" + bi->getVymLink() + "\")", bi,
4273 "setVymLink (\"\")",
4274 QString("Unset vymlink of %1").arg(getObjectName(bi)));
4276 emitDataChanged(bi);
4282 QString VymModel::getVymLink()
4284 BranchItem *bi = getSelectedBranch();
4286 return bi->getVymLink();
4291 QStringList VymModel::getVymLinks()
4294 BranchItem *selbi = getSelectedBranch();
4295 BranchItem *cur = NULL;
4296 BranchItem *prev = NULL;
4297 nextBranch(cur, prev, true, selbi);
4299 if (!cur->getVymLink().isEmpty())
4300 links.append(cur->getVymLink());
4301 nextBranch(cur, prev, true, selbi);
4306 void VymModel::followXLink(int i)
4308 BranchItem *selbi = getSelectedBranch();
4310 selbi = selbi->getXLinkItemNum(i)->getPartnerBranch();
4316 void VymModel::editXLink()
4318 Link *l = getSelectedXLink();
4320 EditXLinkDialog dia;
4322 if (dia.exec() == QDialog::Accepted) {
4323 if (dia.useSettingsGlobal()) {
4324 setMapDefXLinkPen(l->getPen());
4325 setMapDefXLinkStyleBegin(l->getStyleBeginString());
4326 setMapDefXLinkStyleEnd(l->getStyleEndString());
4332 void VymModel::setXLinkColor(const QString &new_col)
4334 Link *l = getSelectedXLink();
4336 QPen pen = l->getPen();
4337 QColor new_color = QColor(new_col);
4338 QColor old_color = pen.color();
4339 if (new_color == old_color)
4341 pen.setColor(new_color);
4343 saveState(l->getBeginLinkItem(),
4344 QString("setXLinkColor(\"%1\")").arg(old_color.name()),
4345 l->getBeginLinkItem(),
4346 QString("setXLinkColor(\"%1\")").arg(new_color.name()),
4347 QString("set color of xlink to %1").arg(new_color.name()));
4351 void VymModel::setXLinkStyle(const QString &new_style)
4353 Link *l = getSelectedXLink();
4355 QPen pen = l->getPen();
4356 QString old_style = penStyleToString(pen.style());
4357 if (new_style == old_style)
4360 pen.setStyle(penStyle(new_style, ok));
4362 saveState(l->getBeginLinkItem(),
4363 QString("setXLinkStyle(\"%1\")").arg(old_style),
4364 l->getBeginLinkItem(),
4365 QString("setXLinkStyle(\"%1\")").arg(new_style),
4366 QString("set style of xlink to %1").arg(new_style));
4370 void VymModel::setXLinkStyleBegin(const QString &new_style)
4372 Link *l = getSelectedXLink();
4374 QString old_style = l->getStyleBeginString();
4375 if (new_style == old_style)
4377 l->setStyleBegin(new_style);
4378 saveState(l->getBeginLinkItem(),
4379 QString("setXLinkStyleBegin(\"%1\")").arg(old_style),
4380 l->getBeginLinkItem(),
4381 QString("setXLinkStyleBegin(\"%1\")").arg(new_style),
4382 "set style of xlink begin");
4386 void VymModel::setXLinkStyleEnd(const QString &new_style)
4388 Link *l = getSelectedXLink();
4390 QString old_style = l->getStyleEndString();
4391 if (new_style == old_style)
4393 l->setStyleEnd(new_style);
4394 saveState(l->getBeginLinkItem(),
4395 QString("setXLinkStyleEnd(\"%1\")").arg(old_style),
4396 l->getBeginLinkItem(),
4397 QString("setXLinkStyleEnd(\"%1\")").arg(new_style),
4398 "set style of xlink end");
4402 void VymModel::setXLinkWidth(int new_width)
4404 Link *l = getSelectedXLink();
4406 QPen pen = l->getPen();
4407 int old_width = pen.width();
4408 if (new_width == old_width)
4410 pen.setWidth(new_width);
4413 l->getBeginLinkItem(), QString("setXLinkWidth(%1)").arg(old_width),
4414 l->getBeginLinkItem(), QString("setXLinkWidth(%1)").arg(new_width),
4415 "set width of xlink");
4419 //////////////////////////////////////////////
4421 //////////////////////////////////////////////
4423 QVariant VymModel::execute(
4424 const QString &script) // FIXME-3 still required???
4425 // Called from these places:
4427 // scripts/vym-ruby.rb (and adaptormodel) used for
4428 // testing Main::callMacro Main::checkReleaseNotes
4431 // VymModel::exportLast
4432 // VymModel::updateSlideSelection
4434 // qDebug()<<"VM::execute called: "<<script;
4435 return mainWindow->runScript(script);
4438 void VymModel::setExportMode(bool b)
4440 // should be called before and after exports
4441 // depending on the settings
4442 if (b && settings.value("/export/useHideExport", "true") == "true")
4443 setHideTmpMode(TreeItem::HideExport);
4445 setHideTmpMode(TreeItem::HideNone);
4448 QPointF VymModel::exportImage(QString fname, bool askName, QString format)
4450 QPointF offset; // set later, when getting image from MapEditor
4454 qWarning("VymModel::exportImage called without filename (and "
4459 fname = lastImageDir.absolutePath() + "/" + getMapName() + ".png";
4464 ex.setName("Image");
4466 ex.setFilePath(fname);
4467 ex.setWindowTitle(tr("Export map as image"));
4469 "PNG (*.png);;All (* *.*)"); // imageIO.getFilters().join(";;")
4471 settings.localValue(filePath, "/export/last/command", "").toString());
4474 if (!ex.execDialog())
4476 fname = ex.getFilePath();
4477 lastImageDir = QDir(fname);
4480 setExportMode(true);
4482 mapEditor->minimizeView();
4484 QImage img(mapEditor->getImage(offset));
4485 if (!img.save(fname, format.toLocal8Bit())) {
4486 QMessageBox::critical(
4487 0, tr("Critical Error"),
4488 tr("Couldn't save QImage %1 in format %2").arg(fname).arg(format));
4489 ex.setResult(ExportBase::Failed);
4491 ex.setResult(ExportBase::Success);
4493 setExportMode(false);
4495 ex.completeExport();
4500 void VymModel::exportPDF(QString fname, bool askName)
4504 qWarning("VymModel::exportPDF called without filename (and "
4509 fname = lastExportDir.absolutePath() + "/" + getMapName() + ".pdf";
4515 ex.setFilePath(fname);
4516 ex.setWindowTitle(tr("Export map as PDF"));
4517 ex.addFilter("PDF (*.pdf);;All (* *.*)");
4519 settings.localValue(filePath, "/export/last/command", "").toString());
4522 if (!ex.execDialog())
4524 fname = ex.getFilePath();
4527 setExportMode(true);
4530 QPrinter pdfPrinter(QPrinter::HighResolution);
4531 pdfPrinter.setOutputFormat(QPrinter::PdfFormat);
4532 pdfPrinter.setOutputFileName(fname);
4533 pdfPrinter.setPageSize(QPageSize(QPageSize::A3));
4535 QRectF bbox = mapEditor->getTotalBBox();
4536 if (bbox.width() > bbox.height())
4537 // recommend landscape
4538 pdfPrinter.setPageOrientation(QPageLayout::Landscape);
4540 // recommend portrait
4541 pdfPrinter.setPageOrientation(QPageLayout::Portrait);
4543 QPainter *pdfPainter = new QPainter(&pdfPrinter);
4544 getScene()->render(pdfPainter);
4548 setExportMode(false);
4550 ex.completeExport();
4553 QPointF VymModel::exportSVG(QString fname, bool askName)
4555 QPointF offset; // FIXME-3 not needed?
4559 qWarning("VymModel::exportSVG called without filename (and "
4564 fname = lastImageDir.absolutePath() + "/" + getMapName() + ".svg";
4570 ex.setFilePath(fname);
4571 ex.setWindowTitle(tr("Export map as SVG"));
4572 ex.addFilter("SVG (*.svg);;All (* *.*)");
4575 if (!ex.execDialog())
4577 fname = ex.getFilePath();
4578 lastImageDir = QDir(fname);
4581 setExportMode(true);
4583 QSvgGenerator generator;
4584 generator.setFileName(fname);
4585 QSize sceneSize = getScene()->sceneRect().size().toSize();
4586 generator.setSize(sceneSize);
4587 generator.setViewBox(QRect(0, 0, sceneSize.width(), sceneSize.height()));
4588 QPainter *svgPainter = new QPainter(&generator);
4589 getScene()->render(svgPainter);
4593 setExportMode(false);
4594 ex.completeExport();
4599 void VymModel::exportXML(QString fpath, QString dpath, bool useDialog)
4604 ex.setWindowTitle(tr("Export map as XML"));
4605 ex.addFilter("XML (*.xml);;All (* *.*)");
4607 settings.localValue(filePath, "/export/last/command", "").toString());
4611 fd.setWindowTitle(vymName + " - " + tr("Export XML to directory"));
4612 QStringList filters;
4613 filters << "XML data (*.xml)";
4614 fd.setNameFilters(filters);
4615 fd.setOption(QFileDialog::DontConfirmOverwrite, true);
4616 fd.setAcceptMode(QFileDialog::AcceptSave);
4617 fd.selectFile(mapName + ".xml");
4620 if (fd.exec() != QDialog::Accepted || fd.selectedFiles().isEmpty())
4623 fpath = fd.selectedFiles().first();
4624 dpath = fpath.left(fpath.lastIndexOf("/"));
4626 if (!confirmDirectoryOverwrite(QDir(dpath)))
4629 ex.setFilePath(fpath);
4631 QString mname = basename(fpath);
4633 // Hide stuff during export, if settings want this
4634 setExportMode(true);
4636 // Create subdirectories
4639 // write image and calculate offset (Remember old mapSaved setting while
4641 bool mchanged = mapChanged;
4642 bool munsaved = mapUnsaved;
4645 exportImage(dpath + "/images/" + mname + ".png", false, "PNG");
4647 mapChanged = mchanged;
4648 mapUnsaved = munsaved;
4650 // write to directory //FIXME-3 check totalBBox here...
4652 saveToDir(dpath, mname + "-", FlagRowMaster::NoFlags, offset, NULL);
4655 file.setFileName(fpath);
4656 if (!file.open(QIODevice::WriteOnly)) {
4657 // This should neverever happen
4658 QMessageBox::critical(0, tr("Critical Export Error"),
4659 QString("VymModel::exportXML couldn't open %1")
4660 .arg(file.fileName()));
4664 // Write it finally, and write in UTF8, no matter what
4665 QTextStream ts(&file);
4666 ts.setCodec("UTF-8");
4670 setExportMode(false);
4675 ex.completeExport(args);
4678 void VymModel::exportAO(QString fname, bool askName)
4683 settings.localValue(filePath, "/export/last/command", "").toString());
4686 ex.setFilePath(mapName + ".txt");
4688 ex.setFilePath(fname);
4691 ex.setDirPath(lastExportDir.absolutePath());
4694 if (!ex.canceled()) {
4695 setExportMode(true);
4697 setExportMode(false);
4701 void VymModel::exportASCII(const QString &fname, bool listTasks, bool askName)
4705 ex.setListTasks(listTasks);
4707 settings.localValue(filePath, "/export/last/command", "").toString());
4710 ex.setFilePath(mapName + ".txt");
4712 ex.setFilePath(fname);
4715 ex.setDirPath(lastExportDir.absolutePath());
4719 if (!ex.canceled()) {
4720 setExportMode(true);
4722 setExportMode(false);
4726 void VymModel::exportCSV(const QString &fname, bool askName)
4731 settings.localValue(filePath, "/export/last/command", "").toString());
4734 ex.setFilePath(mapName + ".csv");
4736 ex.setFilePath(fname);
4739 ex.addFilter("CSV (*.csv);;All (* *.*)");
4740 ex.setDirPath(lastExportDir.absolutePath());
4741 ex.setWindowTitle(vymName + " -" + tr("Export as csv") + " " +
4742 tr("(still experimental)"));
4746 if (!ex.canceled()) {
4747 setExportMode(true);
4749 setExportMode(false);
4753 void VymModel::exportFirefoxBookmarks(const QString &fname, bool askName)
4758 settings.localValue(filePath, "/export/last/command", "").toString());
4761 ex.setFilePath(mapName + ".csv");
4763 ex.setFilePath(fname);
4766 ex.addFilter("JSON (*.json);;All (* *.*)");
4767 ex.setDirPath(lastExportDir.absolutePath());
4768 ex.setWindowTitle(vymName + " -" + tr("Export as csv") + " " +
4769 tr("(still experimental)"));
4773 if (!ex.canceled()) {
4774 setExportMode(true);
4776 setExportMode(false);
4780 void VymModel::exportHTML(const QString &fpath, const QString &dpath,
4783 ExportHTML ex(this);
4785 settings.localValue(filePath, "/export/last/command", "").toString());
4787 if (!dpath.isEmpty())
4788 ex.setDirPath(dpath);
4789 if (!fpath.isEmpty())
4790 ex.setFilePath(fpath);
4792 ex.doExport(useDialog);
4795 void VymModel::exportConfluence(bool createPage, const QString &pageURL,
4796 const QString &pageName, bool useDialog)
4798 ExportConfluence ex(this);
4799 ex.setCreateNewPage(createPage);
4801 ex.setPageName(pageName);
4803 settings.localValue(filePath, "/export/last/command", "").toString());
4805 ex.doExport(useDialog);
4808 void VymModel::exportImpress(const QString &fn, const QString &cf)
4814 settings.localValue(filePath, "/export/last/command", "").toString());
4816 if (ex.setConfigFile(cf)) {
4817 QString lastCommand =
4818 settings.localValue(filePath, "/export/last/command", "")
4821 setExportMode(true);
4822 ex.exportPresentation();
4823 setExportMode(false);
4826 settings.localValue(filePath, "/export/last/command", "")
4828 if (lastCommand != command)
4833 bool VymModel::exportLastAvailable(QString &description, QString &command,
4837 settings.localValue(filePath, "/export/last/command", "").toString();
4839 description = settings.localValue(filePath, "/export/last/description", "")
4841 dest = settings.localValue(filePath, "/export/last/displayedDestination", "")
4843 if (!command.isEmpty() && command.contains("exportMap"))
4849 void VymModel::setExportLastCommand(const QString &cmd)
4851 settings.setLocalValue(filePath, "/export/last/command", cmd);
4854 void VymModel::setExportLastDescription(const QString &desc)
4856 settings.setLocalValue(filePath, "/export/last/description", desc);
4859 void VymModel::setExportLastDestination(const QString &displayedDest)
4861 settings.setLocalValue(filePath, "/export/last/displayedDestination", displayedDest);
4864 void VymModel::exportLast()
4866 QString desc, command,
4867 dest; // FIXME-3 better integrate configFile into command
4868 if (exportLastAvailable(desc, command, dest)) {
4869 //qDebug() << "VM::exportLast: " << command;
4874 void VymModel::exportLaTeX(const QString &fname, bool askName)
4879 settings.localValue(filePath, "/export/last/command", "").toString());
4882 ex.setFilePath(mapName + ".tex");
4884 ex.setFilePath(fname);
4888 if (!ex.canceled()) {
4889 setExportMode(true);
4891 setExportMode(false);
4895 void VymModel::exportOrgMode(const QString &fname, bool askName)
4900 settings.localValue(filePath, "/export/last/command", "").toString());
4903 ex.setFilePath(mapName + ".org");
4905 ex.setFilePath(fname);
4908 ex.setDirPath(lastExportDir.absolutePath());
4912 if (!ex.canceled()) {
4913 setExportMode(true);
4915 setExportMode(false);
4919 void VymModel::exportMarkdown(const QString &fname, bool askName)
4924 settings.localValue(filePath, "/export/last/command", "").toString());
4927 ex.setFilePath(mapName + ".md");
4929 ex.setFilePath(fname);
4932 ex.setDirPath(lastExportDir.absolutePath());
4936 if (!ex.canceled()) {
4937 setExportMode(true);
4939 setExportMode(false);
4942 //////////////////////////////////////////////
4944 //////////////////////////////////////////////
4946 void VymModel::registerMapEditor(QWidget *e) { mapEditor = (MapEditor *)e; }
4948 void VymModel::setMapZoomFactor(const double &d)
4951 mapEditor->setZoomFactorTarget(d);
4954 void VymModel::setMapRotationAngle(const double &d)
4957 mapEditor->setAngleTarget(d);
4960 void VymModel::setMapAnimDuration(const int &d) { animDuration = d; }
4962 void VymModel::setMapAnimCurve(const QEasingCurve &c) { animCurve = c; }
4964 bool VymModel::centerOnID(const QString &id)
4966 TreeItem *ti = findUuid(QUuid(id));
4968 LinkableMapObj *lmo = ((MapItem *)ti)->getLMO();
4969 if (zoomFactor > 0 && lmo) {
4970 mapEditor->setViewCenterTarget(lmo->getBBox().center(), zoomFactor,
4971 rotationAngle, animDuration,
4979 void VymModel::setContextPos(QPointF p)
4982 hasContextPos = true;
4985 void VymModel::unsetContextPos()
4987 contextPos = QPointF();
4988 hasContextPos = false;
4991 void VymModel::reposition()
4993 if (repositionBlocked)
4997 for (int i = 0; i < rootItem->branchCount(); i++) {
4998 bo = rootItem->getBranchObjNum(i);
5000 bo->reposition(); // for positioning heading
5002 qDebug() << "VM::reposition bo=0";
5004 mapEditor->getTotalBBox();
5006 // required to *reposition* the selection box. size is already correct:
5007 emitSelectionChanged(); //FIXME-2 better only update selection geometry
5010 bool VymModel::setMapLinkStyle(const QString &s)
5013 switch (linkstyle) {
5014 case LinkableMapObj::Line:
5017 case LinkableMapObj::Parabel:
5018 snow = "StyleParabel";
5020 case LinkableMapObj::PolyLine:
5021 snow = "StylePolyLine";
5023 case LinkableMapObj::PolyParabel:
5024 snow = "StylePolyParabel";
5031 saveState(QString("setMapLinkStyle (\"%1\")").arg(s),
5032 QString("setMapLinkStyle (\"%1\")").arg(snow),
5033 QString("Set map link style (\"%1\")").arg(s));
5035 if (s == "StyleLine")
5036 linkstyle = LinkableMapObj::Line;
5037 else if (s == "StyleParabel")
5038 linkstyle = LinkableMapObj::Parabel;
5039 else if (s == "StylePolyLine")
5040 linkstyle = LinkableMapObj::PolyLine;
5041 else if (s == "StylePolyParabel")
5042 linkstyle = LinkableMapObj::PolyParabel;
5044 linkstyle = LinkableMapObj::UndefinedStyle;
5046 BranchItem *cur = NULL;
5047 BranchItem *prev = NULL;
5049 nextBranch(cur, prev);
5051 bo = (BranchObj *)(cur->getLMO());
5052 bo->setLinkStyle(bo->getDefLinkStyle(
5053 cur->parent())); // FIXME-4 better emit dataCHanged and leave the
5055 nextBranch(cur, prev);
5061 LinkableMapObj::Style VymModel::getMapLinkStyle() { return linkstyle; }
5063 uint VymModel::getModelID() { return modelID; }
5065 void VymModel::setView(VymView *vv) { vymView = vv; }
5067 void VymModel::setMapDefLinkColor(QColor col)
5072 QString("setMapDefLinkColor (\"%1\")").arg(getMapDefLinkColor().name()),
5073 QString("setMapDefLinkColor (\"%1\")").arg(col.name()),
5074 QString("Set map link color to %1").arg(col.name()));
5078 // Set color for "link arrows" in TreeEditor
5079 vymView->setLinkColor(col);
5081 BranchItem *cur = NULL;
5082 BranchItem *prev = NULL;
5084 nextBranch(cur, prev);
5086 bo = (BranchObj *)(cur->getLMO());
5089 for (int i = 0; i < cur->imageCount(); ++i)
5090 cur->getImageNum(i)->getLMO()->setLinkColor();
5092 nextBranch(cur, prev);
5097 void VymModel::setMapLinkColorHintInt()
5099 // called from setMapLinkColorHint(lch) or at end of parse
5100 BranchItem *cur = NULL;
5101 BranchItem *prev = NULL;
5103 nextBranch(cur, prev);
5105 bo = (BranchObj *)(cur->getLMO());
5108 for (int i = 0; i < cur->imageCount(); ++i)
5109 cur->getImageNum(i)->getLMO()->setLinkColor();
5111 nextBranch(cur, prev);
5115 void VymModel::setMapLinkColorHint(LinkableMapObj::ColorHint lch)
5117 linkcolorhint = lch;
5118 setMapLinkColorHintInt();
5121 void VymModel::toggleMapLinkColorHint()
5123 if (linkcolorhint == LinkableMapObj::HeadingColor)
5124 linkcolorhint = LinkableMapObj::DefaultColor;
5126 linkcolorhint = LinkableMapObj::HeadingColor;
5127 BranchItem *cur = NULL;
5128 BranchItem *prev = NULL;
5130 nextBranch(cur, prev);
5132 bo = (BranchObj *)(cur->getLMO());
5135 for (int i = 0; i < cur->imageCount(); ++i)
5136 cur->getImageNum(i)->getLMO()->setLinkColor();
5138 nextBranch(cur, prev);
5143 selectMapBackgroundImage() // FIXME-3 for using background image:
5144 // view.setCacheMode(QGraphicsView::CacheBackground);
5145 // Also this belongs into ME
5147 QStringList filters;
5148 filters << tr("Images") +
5149 " (*.png *.bmp *.xbm *.jpg *.png *.xpm *.gif *.pnm)";
5151 fd.setFileMode(QFileDialog::ExistingFile);
5152 fd.setWindowTitle(vymName + " - " + tr("Load background image"));
5153 fd.setDirectory(lastImageDir);
5154 fd.setAcceptMode(QFileDialog::AcceptOpen);
5156 if (fd.exec() == QDialog::Accepted && !fd.selectedFiles().isEmpty()) {
5157 // TODO selectMapBackgroundImg in QT4 use: lastImageDir=fd.directory();
5158 lastImageDir = QDir(fd.directory().path());
5159 setMapBackgroundImage(fd.selectedFiles().first());
5163 void VymModel::setMapBackgroundImage(
5164 const QString &fn) // FIXME-3 missing savestate, move to ME
5167 QColor oldcol=mapEditor->getScene()->backgroundBrush().color();
5170 QString ("setMapBackgroundImage (%1)").arg(oldcol.name()),
5172 QString ("setMapBackgroundImage (%1)").arg(col.name()),
5173 QString("Set background color of map to %1").arg(col.name()));
5176 brush.setTextureImage(QImage(fn));
5177 mapEditor->getScene()->setBackgroundBrush(brush);
5180 void VymModel::selectMapBackgroundColor()
5182 QColor col = QColorDialog::getColor(
5183 mapEditor->getScene()->backgroundBrush().color(), NULL);
5186 setMapBackgroundColor(col);
5189 void VymModel::setMapBackgroundColor(QColor col)
5191 QColor oldcol = mapEditor->getScene()->backgroundBrush().color();
5192 saveState(QString("setMapBackgroundColor (\"%1\")").arg(oldcol.name()),
5193 QString("setMapBackgroundColor (\"%1\")").arg(col.name()),
5194 QString("Set background color of map to %1").arg(col.name()));
5195 backgroundColor = col; // Used for backroundRole in TreeModel::data()
5196 vymView->setBackgroundColor(backgroundColor);
5199 QColor VymModel::getMapBackgroundColor() // FIXME-4 move to ME
5201 return mapEditor->getScene()->backgroundBrush().color();
5204 QFont VymModel::getMapDefaultFont() { return defaultFont; }
5206 void VymModel::setMapDefaultFont(const QFont &f) { defaultFont = f; }
5208 LinkableMapObj::ColorHint VymModel::getMapLinkColorHint() // FIXME-4 move to ME
5210 return linkcolorhint;
5213 QColor VymModel::getMapDefLinkColor() // FIXME-4 move to ME
5215 return defLinkColor;
5218 void VymModel::setMapDefXLinkPen(const QPen &p) // FIXME-4 move to ME
5223 QPen VymModel::getMapDefXLinkPen() // FIXME-4 move to ME
5228 void VymModel::setMapDefXLinkStyleBegin(const QString &s)
5230 defXLinkStyleBegin = s;
5233 QString VymModel::getMapDefXLinkStyleBegin() { return defXLinkStyleBegin; }
5235 void VymModel::setMapDefXLinkStyleEnd(const QString &s)
5237 defXLinkStyleEnd = s;
5240 QString VymModel::getMapDefXLinkStyleEnd() { return defXLinkStyleEnd; }
5242 void VymModel::move(const double &x, const double &y)
5244 MapItem *seli = (MapItem *)getSelectedItem();
5246 (seli->isBranchLikeType() || seli->getType() == TreeItem::Image)) {
5247 LinkableMapObj *lmo = seli->getLMO();
5249 QPointF ap(lmo->getAbsPos());
5252 QString ps = qpointFToString(ap);
5253 QString s = getSelectString(seli);
5255 s, "move " + ps, s, "move " + qpointFToString(to),
5256 QString("Move %1 to %2").arg(getObjectName(seli)).arg(ps));
5259 emitSelectionChanged();
5265 void VymModel::moveRel(const double &x, const double &y)
5267 MapItem *seli = (MapItem *)getSelectedItem();
5269 (seli->isBranchLikeType() || seli->getType() == TreeItem::Image)) {
5270 LinkableMapObj *lmo = seli->getLMO();
5272 QPointF rp(lmo->getRelPos());
5275 QString ps = qpointFToString(lmo->getRelPos());
5276 QString s = getSelectString(seli);
5277 saveState(s, "moveRel " + ps, s,
5278 "moveRel " + qpointFToString(to),
5279 QString("Move %1 to relative position %2")
5280 .arg(getObjectName(seli))
5282 ((OrnamentedObj *)lmo)->move2RelPos(x, y);
5284 lmo->updateLinkGeometry();
5285 emitSelectionChanged();
5291 void VymModel::animate()
5293 animationTimer->stop();
5296 while (i < animObjList.size()) {
5297 bo = (BranchObj *)animObjList.at(i);
5298 if (!bo->animate()) {
5300 animObjList.removeAt(i);
5307 emitSelectionChanged();
5309 if (!animObjList.isEmpty())
5310 animationTimer->start(animationInterval);
5313 void VymModel::startAnimation(BranchObj *bo, const QPointF &v)
5318 if (bo->getUseRelPos())
5319 startAnimation(bo, bo->getRelPos(), bo->getRelPos() + v);
5321 startAnimation(bo, bo->getAbsPos(), bo->getAbsPos() + v);
5324 void VymModel::startAnimation(BranchObj *bo, const QPointF &start,
5325 const QPointF &dest)
5329 if (bo && bo->getTreeItem()->depth() >= 0) {
5333 ap.setTicks(animationTicks);
5334 ap.setAnimated(true);
5335 bo->setAnimation(ap);
5336 if (!animObjList.contains(bo))
5337 animObjList.append(bo);
5338 animationTimer->setSingleShot(true);
5339 animationTimer->start(animationInterval);
5343 void VymModel::stopAnimation(MapObj *mo)
5345 int i = animObjList.indexOf(mo);
5347 animObjList.removeAt(i);
5350 void VymModel::stopAllAnimation()
5354 while (i < animObjList.size()) {
5355 bo = (BranchObj *)animObjList.at(i);
5356 bo->stopAnimation();
5357 bo->requestReposition();
5363 void VymModel::sendSelection()
5365 if (netstate != Server)
5367 sendData(QString("select (\"%1\")").arg(getSelectString()));
5370 void VymModel::newServer()
5374 tcpServer = new QTcpServer(this);
5375 if (!tcpServer->listen(QHostAddress::Any, port)) {
5376 QMessageBox::critical(NULL, "vym server",
5377 QString("Unable to start the server: %1.")
5378 .arg(tcpServer->errorString()));
5379 // FIXME-3 needed? we are no widget any longer... close();
5382 connect(tcpServer, SIGNAL(newConnection()), this, SLOT(newClient()));
5384 qDebug() << "Server is running on port " << tcpServer->serverPort();
5387 void VymModel::connectToServer()
5390 server = "salam.suse.de";
5391 server = "localhost";
5392 clientSocket = new QTcpSocket(this);
5393 clientSocket->abort();
5394 clientSocket->connectToHost(server, port);
5395 connect(clientSocket, SIGNAL(readyRead()), this, SLOT(readData()));
5396 connect(clientSocket, SIGNAL(error(QAbstractSocket::SocketError)), this,
5397 SLOT(displayNetworkError(QAbstractSocket::SocketError)));
5399 qDebug() << "connected to " << qPrintable(server) << " port " << port;
5402 void VymModel::newClient()
5404 QTcpSocket *newClient = tcpServer->nextPendingConnection();
5405 connect(newClient, SIGNAL(disconnected()), newClient, SLOT(deleteLater()));
5407 qDebug() << "ME::newClient at "
5408 << qPrintable(newClient->peerAddress().toString());
5410 clientList.append(newClient);
5413 void VymModel::sendData(const QString &s)
5415 if (clientList.size() == 0)
5418 // Create bytearray to send
5420 QDataStream out(&block, QIODevice::WriteOnly);
5421 out.setVersion(QDataStream::Qt_4_0);
5423 // Reserve some space for blocksize
5426 // Write sendCounter
5427 out << sendCounter++;
5432 // Go back and write blocksize so far
5433 out.device()->seek(0);
5434 quint16 bs = (quint16)(block.size() - 2 * sizeof(quint16));
5438 qDebug() << "ME::sendData bs=" << bs << " counter=" << sendCounter
5439 << " s=" << qPrintable(s);
5441 for (int i = 0; i < clientList.size(); ++i) {
5442 // qDebug() << "Sending \""<<qPrintable (s)<<"\" to "<<qPrintable
5443 // (clientList.at(i)->peerAddress().toString());
5444 clientList.at(i)->write(block);
5448 void VymModel::readData()
5450 while (clientSocket->bytesAvailable() >= (int)sizeof(quint16)) {
5452 qDebug() << "readData bytesAvail="
5453 << clientSocket->bytesAvailable();
5457 QDataStream in(clientSocket);
5458 in.setVersion(QDataStream::Qt_4_0);
5466 qDebug() << "VymModel::readData command=" << qPrintable(t);
5469 // parseAtom (t,noErr,errMsg); //FIXME-4 needs rework using scripts
5474 void VymModel::displayNetworkError(QAbstractSocket::SocketError socketError)
5476 switch (socketError) {
5477 case QAbstractSocket::RemoteHostClosedError:
5479 case QAbstractSocket::HostNotFoundError:
5480 QMessageBox::information(NULL, vymName + " Network client",
5481 "The host was not found. Please check the "
5482 "host name and port settings.");
5484 case QAbstractSocket::ConnectionRefusedError:
5485 QMessageBox::information(NULL, vymName + " Network client",
5486 "The connection was refused by the peer. "
5487 "Make sure the fortune server is running, "
5488 "and check that the host name and port "
5489 "settings are correct.");
5492 QMessageBox::information(NULL, vymName + " Network client",
5493 QString("The following error occurred: %1.")
5494 .arg(clientSocket->errorString()));
5498 void VymModel::downloadImage(const QUrl &url, BranchItem *bi)
5501 bi = getSelectedBranch();
5503 qWarning("VM::download bi==NULL");
5507 // FIXME-3 download img to tmpfile and delete after running script in
5510 script += QString("m = vym.currentMap();m.selectID(\"%1\");")
5511 .arg(bi->getUuid().toString());
5512 script += QString("m.loadImage(\"$TMPFILE\");");
5514 DownloadAgent *agent = new DownloadAgent(url);
5515 agent->setFinishedAction(this, script);
5516 connect(agent, SIGNAL(downloadFinished()), mainWindow,
5517 SLOT(downloadFinished()));
5518 QTimer::singleShot(0, agent, SLOT(execute()));
5521 void VymModel::selectMapSelectionColor() // FIXME-2 move out of VymModel, consider Pen/Brush
5523 QColor col = QColorDialog::getColor(defLinkColor, NULL);
5524 setSelectionPenColor(col);
5525 setSelectionBrushColor(col);
5528 void VymModel::emitSelectionChanged(const QItemSelection &newsel)
5530 emit(selectionChanged(newsel,
5531 newsel)); // needed e.g. to update geometry in editor
5535 void VymModel::emitSelectionChanged()
5537 QItemSelection newsel = selModel->selection();
5538 emitSelectionChanged(newsel);
5541 void VymModel::setSelectionPenColor(QColor col)
5546 QPen selPen = mapEditor->getSelectionPen();
5547 saveState(QString("setSelectionPenColor (\"%1\")")
5548 .arg(selPen.color().name()),
5549 QString("setSelectionPenColor (\"%1\")").arg(col.name()),
5550 QString("Set pen color of selection box to %1").arg(col.name()));
5552 selPen.setColor(col);
5553 mapEditor->setSelectionPen(selPen);
5556 QColor VymModel::getSelectionPenColor() {
5557 return mapEditor->getSelectionPen().color();
5560 void VymModel::setSelectionPenWidth(qreal w)
5562 QPen selPen = mapEditor->getSelectionPen();
5564 saveState(QString("setSelectionPenWidth (\"%1\")")
5565 .arg(mapEditor->getSelectionPen().width()),
5566 QString("setSelectionPenWidth (\"%1\")").arg(w),
5567 QString("Set pen width of selection box to %1").arg(w));
5570 mapEditor->setSelectionPen(selPen);
5571 //vymView->setSelectionColor(col);
5574 qreal VymModel::getSelectionPenWidth() {
5575 return mapEditor->getSelectionPen().width();
5578 void VymModel::setSelectionBrushColor(QColor col)
5583 QBrush selBrush = mapEditor->getSelectionBrush();
5584 saveState(QString("setSelectionBrushColor (\"%1\")")
5585 .arg(selBrush.color().name()),
5586 QString("setSelectionBrushColor (\"%1\")").arg(col.name()),
5587 QString("Set Brush color of selection box to %1").arg(col.name()));
5589 selBrush.setColor(col);
5590 vymView->setSelectionBrush(selBrush);
5593 QColor VymModel::getSelectionBrushColor() {
5594 return mapEditor->getSelectionBrush().color();
5597 bool VymModel::initIterator(const QString &iname, bool deepLevelsFirst)
5599 Q_UNUSED(deepLevelsFirst);
5601 // Remove existing iterators first
5602 selIterCur.remove(iname);
5603 selIterPrev.remove(iname);
5604 selIterStart.remove(iname);
5605 selIterActive.remove(iname);
5607 QList<BranchItem *> selbis;
5608 selbis = getSelectedBranches();
5609 if (selbis.count() == 1) {
5610 BranchItem *prev = NULL;
5611 BranchItem *cur = NULL;
5612 nextBranch(cur, prev, false, selbis.first());
5614 selIterCur.insert(iname, cur->getUuid());
5615 selIterPrev.insert(iname, prev->getUuid());
5616 selIterStart.insert(iname, selbis.first()->getUuid());
5617 selIterActive.insert(iname, false);
5618 // qDebug() << "Created iterator " << iname;
5625 bool VymModel::nextIterator(const QString &iname)
5627 if (selIterCur.keys().indexOf(iname) < 0) {
5629 << QString("VM::nextIterator couldn't find %1 in hash of iterators")
5634 BranchItem *cur = (BranchItem *)(findUuid(selIterCur.value(iname)));
5636 qWarning() << "VM::nextIterator couldn't find cur" << selIterCur;
5640 qDebug() << " " << iname << "selecting " << cur->getHeadingPlain();
5643 if (!selIterActive.value(iname)) {
5644 // Select for the first time
5646 selIterActive[iname] = true;
5650 BranchItem *prev = (BranchItem *)(findUuid(selIterPrev.value(iname)));
5651 BranchItem *start = (BranchItem *)(findUuid(selIterStart.value(iname)));
5653 qWarning() << "VM::nextIterator couldn't find prev"
5654 << selIterPrev.value(iname);
5656 qWarning() << "VM::nextIterator couldn't find start "
5657 << selIterStart.value(iname);
5659 if (cur && prev && start) {
5660 nextBranch(cur, prev, false, start);
5662 selIterCur[iname] = cur->getUuid();
5663 selIterPrev[iname] = prev->getUuid();
5673 void VymModel::setHideTmpMode(TreeItem::HideTmpMode mode)
5676 for (int i = 0; i < rootItem->branchCount(); i++)
5677 rootItem->getBranchNum(i)->setHideTmp(mode);
5679 if (mode == TreeItem::HideExport)
5684 qApp->processEvents();
5687 //////////////////////////////////////////////
5688 // Selection related
5689 //////////////////////////////////////////////
5691 void VymModel::updateSelection(QItemSelection newsel, QItemSelection dsel)
5696 bool do_reposition = false;
5697 foreach (ix, dsel.indexes()) {
5698 mi = static_cast<MapItem *>(ix.internalPointer());
5699 if (mi->isBranchLikeType())
5701 do_reposition || ((BranchItem *)mi)->resetTmpUnscroll();
5702 if (mi->getType() == TreeItem::XLink) {
5703 Link *li = ((XLinkItem *)mi)->getLink();
5704 XLinkObj *xlo = li->getXLinkObj();
5706 xlo->setSelection(XLinkObj::Unselected);
5709 do_reposition || li->getBeginBranch()->resetTmpUnscroll();
5711 do_reposition || li->getEndBranch()->resetTmpUnscroll();
5715 foreach (ix, newsel.indexes()) {
5716 mi = static_cast<MapItem *>(ix.internalPointer());
5717 if (mi->isBranchLikeType()) {
5718 bi = (BranchItem *)mi;
5719 if (bi->hasScrolledParent()) {
5721 do_reposition = true;
5724 if (mi->getType() == TreeItem::XLink) {
5725 ((XLinkItem *)mi)->setSelection();
5727 // begin/end branches need to be tmp unscrolled
5728 Link *li = ((XLinkItem *)mi)->getLink();
5729 bi = li->getBeginBranch();
5730 if (bi->hasScrolledParent()) {
5732 do_reposition = true;
5734 bi = li->getEndBranch();
5735 if (bi->hasScrolledParent()) {
5737 do_reposition = true;
5745 void VymModel::setSelectionModel(QItemSelectionModel *sm) { selModel = sm; }
5747 QItemSelectionModel *VymModel::getSelectionModel() { return selModel; }
5749 void VymModel::setSelectionBlocked(bool b) { selectionBlocked = b; }
5751 bool VymModel::isSelectionBlocked() { return selectionBlocked; }
5753 bool VymModel::select(const QString &s) // FIXME-2 Does not support multiple selections yet
5757 TreeItem *ti = findBySelectString(s);
5759 return select(index(ti));
5763 bool VymModel::selectID(const QString &s)
5767 TreeItem *ti = findUuid(QUuid(s));
5769 return select(index(ti));
5773 bool VymModel::select(LinkableMapObj *lmo)
5775 QItemSelection oldsel = selModel->selection();
5778 return select(lmo->getTreeItem());
5783 bool VymModel::selectToggle(TreeItem *ti)
5786 selModel->select(index(ti), QItemSelectionModel::Toggle);
5787 // appendSelectionToHistory(); // FIXME-4 selection history not implemented yet
5788 // for multiselections
5789 lastToggledUuid = ti->getUuid();
5795 bool VymModel::selectToggle(const QString &selectString)
5797 TreeItem *ti = findBySelectString(selectString);
5798 return selectToggle(ti);
5801 bool VymModel::select(TreeItem *ti)
5804 return select(index(ti));
5809 bool VymModel::select(const QModelIndex &index)
5811 if (index.isValid()) {
5812 TreeItem *ti = getItem(index);
5813 if (ti->isBranchLikeType()) {
5814 if (((BranchItem *)ti)->tmpUnscroll())
5817 selModel->select(index, QItemSelectionModel::ClearAndSelect);
5818 appendSelectionToHistory();
5824 void VymModel::unselectAll() { unselect(selModel->selection()); }
5826 void VymModel::unselect(QItemSelection desel)
5828 if (!desel.isEmpty()) {
5829 lastSelectString = getSelectString();
5830 selModel->clearSelection();
5834 bool VymModel::reselect()
5836 bool b = select(lastSelectString);
5840 bool VymModel::canSelectPrevious()
5842 if (currentSelection > 0)
5848 bool VymModel::selectPrevious()
5850 keepSelectionHistory = true;
5851 bool result = false;
5852 while (currentSelection > 0) {
5854 TreeItem *ti = findID(selectionHistory.at(currentSelection));
5856 result = select(ti);
5860 selectionHistory.removeAt(currentSelection);
5862 keepSelectionHistory = false;
5866 bool VymModel::canSelectNext()
5868 if (currentSelection < selectionHistory.count() - 1)
5874 bool VymModel::selectNext()
5876 keepSelectionHistory = true;
5877 bool result = false;
5878 while (currentSelection < selectionHistory.count() - 1) {
5880 TreeItem *ti = findID(selectionHistory.at(currentSelection));
5882 result = select(ti);
5886 selectionHistory.removeAt(currentSelection);
5888 keepSelectionHistory = false;
5892 void VymModel::resetSelectionHistory()
5894 selectionHistory.clear();
5895 currentSelection = -1;
5896 keepSelectionHistory = false;
5897 appendSelectionToHistory();
5900 void VymModel::appendSelectionToHistory() // FIXME-4 history unable to cope with multiple
5904 TreeItem *ti = getSelectedItem();
5905 if (ti && !keepSelectionHistory) {
5906 if (ti->isBranchLikeType())
5907 ((BranchItem *)ti)->setLastSelectedBranch();
5909 selectionHistory.append(id);
5910 currentSelection = selectionHistory.count() - 1;
5915 void VymModel::emitShowSelection(bool scaled)
5917 if (!repositionBlocked)
5918 emit(showSelection(scaled));
5921 TreeItem* VymModel::lastToggledItem()
5923 return findUuid(lastToggledUuid);
5926 void VymModel::emitNoteChanged(TreeItem *ti)
5928 QModelIndex ix = index(ti);
5929 emit(noteChanged(ix));
5930 mainWindow->updateNoteEditor(ti);
5933 void VymModel::emitDataChanged(TreeItem *ti)
5935 QModelIndex ix = index(ti);
5936 emit(dataChanged(ix, ix));
5937 emitSelectionChanged();
5938 if (!repositionBlocked) {
5939 // Update taskmodel and recalc priorities there
5940 if (ti->isBranchLikeType() && ((BranchItem *)ti)->getTask()) {
5941 taskModel->emitDataChanged(((BranchItem *)ti)->getTask());
5942 taskModel->recalcPriorities();
5947 void VymModel::emitUpdateQueries()
5949 // Used to tell MainWindow to update query results
5950 if (repositionBlocked)
5952 emit(updateQueries(this));
5954 void VymModel::emitUpdateLayout()
5956 if (settings.value("/mainwindow/autoLayout/use", "true") == "true")
5957 emit(updateLayout());
5960 bool VymModel::selectFirstBranch()
5962 TreeItem *ti = getSelectedBranch();
5964 TreeItem *par = ti->parent();
5966 TreeItem *ti2 = par->getFirstBranch();
5974 bool VymModel::selectFirstChildBranch()
5976 TreeItem *ti = getSelectedBranch();
5978 BranchItem *bi = ti->getFirstBranch();
5985 bool VymModel::selectLastBranch()
5987 TreeItem *ti = getSelectedBranch();
5989 TreeItem *par = ti->parent();
5991 TreeItem *ti2 = par->getLastBranch();
5999 bool VymModel::selectLastChildBranch()
6001 TreeItem *ti = getSelectedBranch();
6003 BranchItem *bi = ti->getLastBranch();
6010 bool VymModel::selectLastSelectedBranch()
6012 BranchItem *bi = getSelectedBranch();
6014 bi = bi->getLastSelectedBranch();
6021 bool VymModel::selectLastImage()
6023 TreeItem *ti = getSelectedBranch();
6025 TreeItem *par = ti->parent();
6027 TreeItem *ti2 = par->getLastImage();
6035 bool VymModel::selectLatestAdded() { return select(latestAddedItem); }
6037 bool VymModel::selectParent()
6039 TreeItem *ti = getSelectedItem();
6049 TreeItem::Type VymModel::selectionType()
6051 TreeItem *ti = getSelectedItem();
6053 return ti->getType();
6055 return TreeItem::Undefined;
6058 LinkableMapObj *VymModel::getSelectedLMO()
6060 QModelIndexList list = selModel->selectedIndexes();
6061 if (list.count() == 1) {
6062 TreeItem *ti = getItem(list.first());
6063 TreeItem::Type type = ti->getType();
6064 if (type == TreeItem::Branch || type == TreeItem::MapCenter ||
6065 type == TreeItem::Image)
6066 return ((MapItem *)ti)->getLMO();
6071 BranchObj *VymModel::getSelectedBranchObj() // convenience function
6073 TreeItem *ti = getSelectedBranch();
6075 return (BranchObj *)(((MapItem *)ti)->getLMO());
6080 BranchItem *VymModel::getSelectedBranch()
6082 TreeItem *ti = getSelectedItem();
6084 TreeItem::Type type = ti->getType();
6085 if (type == TreeItem::Branch || type == TreeItem::MapCenter)
6086 return (BranchItem *)ti;
6091 QList<BranchItem *> VymModel::getSelectedBranches()
6093 QList<BranchItem *> bis;
6094 foreach (TreeItem *ti, getSelectedItems()) {
6095 TreeItem::Type type = ti->getType();
6096 if (type == TreeItem::Branch || type == TreeItem::MapCenter)
6097 bis.append((BranchItem *)ti);
6102 ImageItem *VymModel::getSelectedImage()
6104 TreeItem *ti = getSelectedItem();
6105 if (ti && ti->getType() == TreeItem::Image)
6106 return (ImageItem *)ti;
6111 Task *VymModel::getSelectedTask()
6113 BranchItem *selbi = getSelectedBranch();
6115 return selbi->getTask();
6120 Link *VymModel::getSelectedXLink()
6122 XLinkItem *xli = getSelectedXLinkItem();
6124 return xli->getLink();
6128 XLinkItem *VymModel::getSelectedXLinkItem()
6130 TreeItem *ti = getSelectedItem();
6131 if (ti && ti->getType() == TreeItem::XLink)
6132 return (XLinkItem *)ti;
6137 AttributeItem *VymModel::getSelectedAttribute()
6139 TreeItem *ti = getSelectedItem();
6140 if (ti && ti->getType() == TreeItem::Attribute)
6141 return (AttributeItem *)ti;
6146 TreeItem *VymModel::getSelectedItem()
6150 QModelIndexList list = selModel->selectedIndexes();
6151 if (list.count() == 1)
6152 return getItem(list.first());
6157 QList<TreeItem *> VymModel::getSelectedItems()
6159 QList<TreeItem *> l;
6162 QModelIndexList list = selModel->selectedIndexes();
6163 foreach (QModelIndex ix, list)
6164 l.append(getItem(ix));
6168 QModelIndex VymModel::getSelectedIndex()
6170 QModelIndexList list = selModel->selectedIndexes();
6171 if (list.count() == 1)
6172 return list.first();
6174 return QModelIndex();
6177 QList<uint> VymModel::getSelectedIDs()
6180 foreach (TreeItem *ti, getSelectedItems())
6181 uids.append(ti->getID());
6185 QStringList VymModel::getSelectedUUIDs()
6188 foreach (TreeItem *ti, getSelectedItems())
6189 uids.append(ti->getUuid().toString());
6193 bool VymModel::isSelected(TreeItem *ti)
6195 return getSelectedItems().contains(ti);
6198 QString VymModel::getSelectString()
6200 return getSelectString(getSelectedItem());
6203 QString VymModel::getSelectString(
6204 LinkableMapObj *lmo) // only for convenience. Used in MapEditor
6208 return getSelectString(lmo->getTreeItem());
6211 QString VymModel::getSelectString(TreeItem *ti)
6214 if (!ti || ti->depth() < 0)
6216 switch (ti->getType()) {
6217 case TreeItem::MapCenter:
6220 case TreeItem::Branch:
6223 case TreeItem::Image:
6226 case TreeItem::Attribute:
6229 case TreeItem::XLink:
6233 s = "unknown type in VymModel::getSelectString()";
6236 s = s + QString("%1").arg(ti->num());
6237 if (ti->depth() > 0)
6238 // call myself recursively
6239 s = getSelectString(ti->parent()) + "," + s;
6243 QString VymModel::getSelectString(BranchItem *bi)
6245 return getSelectString((TreeItem *)bi);
6248 QString VymModel::getSelectString(const uint &i)
6250 return getSelectString(findID(i));
6253 void VymModel::setLatestAddedItem(TreeItem *ti) { latestAddedItem = ti; }
6255 TreeItem *VymModel::getLatestAddedItem() { return latestAddedItem; }
6257 SlideModel *VymModel::getSlideModel() { return slideModel; }
6259 int VymModel::slideCount() { return slideModel->count(); }
6261 SlideItem *VymModel::addSlide()
6263 SlideItem *si = slideModel->getSelectedItem();
6265 si = slideModel->addSlide(NULL, si->childNumber() + 1);
6267 si = slideModel->addSlide();
6269 TreeItem *seli = getSelectedItem();
6273 if (!loadStringFromDisk(vymBaseDir.path() +
6274 "/macros/slideeditor-snapshot.vys",
6276 qWarning() << "VymModel::addSlide couldn't load template for "
6283 QString().setNum(getMapEditor()->getZoomFactorTarget()));
6284 inScript.replace("CURRENT_ANGLE",
6285 QString().setNum(getMapEditor()->getAngleTarget()));
6286 inScript.replace("CURRENT_ID",
6287 "\"" + seli->getUuid().toString() + "\"");
6289 si->setInScript(inScript);
6290 slideModel->setData(slideModel->index(si), seli->getHeadingPlain());
6292 QString s = "<vymmap>" + si->saveToDir() + "</vymmap>";
6293 int pos = si->childNumber();
6294 saveState(PartOfMap, getSelectString(),
6295 QString("removeSlide (%1)").arg(pos), getSelectString(),
6296 QString("addMapInsert (\"PATH\",%1)").arg(pos), "Add slide", NULL,
6301 void VymModel::deleteSlide(SlideItem *si)
6304 QString s = "<vymmap>" + si->saveToDir() + "</vymmap>";
6305 int pos = si->childNumber();
6306 saveState(PartOfMap, getSelectString(),
6307 QString("addMapInsert (\"PATH\",%1)").arg(pos),
6308 getSelectString(), QString("removeSlide (%1)").arg(pos),
6309 "Remove slide", NULL, s);
6310 slideModel->deleteSlide(si);
6314 void VymModel::deleteSlide(int n) { deleteSlide(slideModel->getSlide(n)); }
6316 void VymModel::relinkSlide(SlideItem *si, int pos)
6319 slideModel->relinkSlide(si, si->parent(), pos);
6322 bool VymModel::moveSlideDown(int n)
6324 SlideItem *si = NULL;
6325 if (n < 0) // default if called without parameters
6327 si = slideModel->getSelectedItem();
6329 n = si->childNumber();
6334 si = slideModel->getSlide(n);
6335 if (si && n >= 0 && n < slideModel->count() - 1) {
6336 blockSlideSelection = true;
6337 slideModel->relinkSlide(si, si->parent(), n + 1);
6338 blockSlideSelection = false;
6339 saveState(getSelectString(), QString("moveSlideUp (%1)").arg(n + 1),
6340 getSelectString(), QString("moveSlideDown (%1)").arg(n),
6341 QString("Move slide %1 down").arg(n));
6348 bool VymModel::moveSlideUp(int n)
6350 SlideItem *si = NULL;
6351 if (n < 0) // default if called without parameters
6353 si = slideModel->getSelectedItem();
6355 n = si->childNumber();
6360 si = slideModel->getSlide(n);
6361 if (si && n > 0 && n < slideModel->count()) {
6362 blockSlideSelection = true;
6363 slideModel->relinkSlide(si, si->parent(), n - 1);
6364 blockSlideSelection = false;
6365 saveState(getSelectString(), QString("moveSlideDown (%1)").arg(n - 1),
6366 getSelectString(), QString("moveSlideUp (%1)").arg(n),
6367 QString("Move slide %1 up").arg(n));
6374 void VymModel::updateSlideSelection(QItemSelection newsel, QItemSelection)
6376 if (blockSlideSelection)
6379 foreach (ix, newsel.indexes()) {
6380 SlideItem *si = static_cast<SlideItem *>(ix.internalPointer());
6381 QString inScript = si->getInScript();
6383 // show inScript in ScriptEditor
6384 scriptEditor->setSlideScript(modelID, si->getID(), inScript);