+#include "xml-vym.h"
+
+#include <QColor>
+#include <QMessageBox>
+#include <QTextStream>
+#include <typeinfo>
+
+#include "attributeitem.h"
+#include "branchitem.h"
+#include "flag.h"
+#include "linkablemapobj.h"
+#include "mainwindow.h"
+#include "misc.h"
+#include "settings.h"
+#include "slideitem.h"
+#include "task.h"
+#include "taskmodel.h"
+#include "xlinkitem.h"
+
+extern Main *mainWindow;
+extern Settings settings;
+extern TaskModel *taskModel;
+extern QString vymVersion;
+
+parseVYMHandler::parseVYMHandler()
+{
+ // Default is to load everything
+ contentFilter = 0x0000; // TODO use filters for all content types below
+}
+
+void parseVYMHandler::setContentFilter(const int &c) { contentFilter = c; }
+
+bool parseVYMHandler::startDocument()
+{
+ errorProt = "";
+ state = StateInit;
+ stateStack.clear();
+ stateStack.append(StateInit);
+ htmldata = "";
+ isVymPart = false;
+ useProgress = false;
+ return true;
+}
+
+bool parseVYMHandler::startElement(const QString &, const QString &,
+ const QString &eName,
+ const QXmlAttributes &atts)
+{
+ QColor col;
+ /* Testing
+ qDebug() << "startElement: <" << eName
+ << "> state=" << state
+ << " laststate=" << stateStack.last()
+ << " loadMode=" << loadMode
+ //<<" line=" << QXmlDefaultHandler::lineNumber();
+ << "contentFilter=" << contentFilter;
+ */
+
+ stateStack.append(state);
+ if (state == StateInit && (eName == "vymmap")) {
+ state = StateMap;
+ branchesTotal = 0;
+ branchesCounter = 0;
+
+ if (loadMode == NewMap || loadMode == DefaultMap) {
+ // Create mapCenter
+ model->clear();
+ lastBranch = NULL;
+
+ readMapAttr(atts);
+ }
+ // Check version
+ if (!atts.value("version").isEmpty()) {
+ version = atts.value("version");
+ if (!versionLowerOrEqualThanVym(version)) {
+ QMessageBox::warning(
+ 0, QObject::tr("Warning: Version Problem"),
+ QObject::tr(
+ "<h3>Map is newer than VYM</h3>"
+ "<p>The map you are just trying to load was "
+ "saved using vym %1. "
+ "The version of this vym is %2. "
+ "If you run into problems after pressing "
+ "the ok-button below, updating vym should help.</p>")
+ .arg(version)
+ .arg(vymVersion) +
+ QObject::tr(
+ "<p>The map will be opened readonly, because not "
+ "all information from new maps can be saved with this "
+ "version of vym. Please be careful!"));
+ model->setReadOnly(true);
+ }
+ else
+ model->setVersion(version);
+ }
+ }
+ else if (eName == "mapdesign" && state == StateMap) {
+ state = StateMapDesign;
+ }
+ else if (eName == "md" && state == StateMapDesign) {
+ state = StateMD;
+ readMapDesignCompatibleAttr(atts);
+ }
+ else if (eName == "select" && state == StateMap) {
+ state = StateMapSelect;
+ }
+ else if (eName == "setting" && state == StateMap) {
+ state = StateMapSetting;
+ if (loadMode == NewMap) {
+ htmldata.clear();
+ readSettingAttr(atts);
+ }
+ }
+ else if (eName == "slide" && state == StateMap) {
+ state = StateMapSlide;
+ if (!(contentFilter & SlideContent)) {
+ // Ignore slides during paste
+ lastSlide = model->addSlide();
+ if (insertPos >= 0)
+ model->relinkSlide(lastSlide, insertPos);
+
+ readSlideAttr(atts);
+ }
+ }
+ else if (eName == "mapcenter" && state == StateMap) {
+ state = StateMapCenter;
+ if (loadMode == NewMap) {
+ // Really use the found mapcenter as MCO in a new map
+ lastBranch = model->createMapCenter();
+ }
+ else {
+ // Treat the found mapcenter as a branch
+ // in an existing map
+ BranchItem *bi = model->getSelectedBranch();
+ if (bi) {
+ lastBranch = bi;
+ if (loadMode == ImportAdd) {
+ // Import Add
+ if (insertPos < 0)
+ lastBranch = model->createBranch(lastBranch);
+ else {
+ lastBranch = model->addNewBranch(lastBranch, insertPos);
+ insertPos++;
+ }
+ }
+ else {
+ // Import Replace
+ if (insertPos < 0) {
+ insertPos = lastBranch->num() + 1;
+ model->clearItem(lastBranch);
+ }
+ else {
+ BranchItem *pi = bi->parentBranch();
+ lastBranch = model->addNewBranch(pi, insertPos);
+ insertPos++;
+ }
+ }
+ }
+ else
+ // if nothing selected, add mapCenter without parent
+ lastBranch = model->createMapCenter();
+ }
+ readBranchAttr(atts);
+ }
+ else if ((eName == "standardflag" || eName == "standardFlag") &&
+ (state == StateMapCenter || state == StateBranch)) {
+ state = StateStandardFlag;
+ }
+ else if (eName == "userflagdef" && state == StateMap) {
+ state = StateUserFlagDef;
+ return (readUserFlagDefAttr(atts));
+ }
+ else if (eName == "userflag" &&
+ (state == StateMapCenter || state == StateBranch)) {
+ state = StateUserFlag;
+ return (readUserFlagAttr(atts));
+ }
+ else if (eName == "heading" &&
+ (state == StateMapCenter || state == StateBranch ||
+ state == StateInit)) {
+ if (state == StateInit) {
+ // Only read some stuff like VymNote or Heading
+ // e.g. for undo/redo
+ lastBranch = model->getSelectedBranch();
+ if (version.isEmpty())
+ version = "0.0.0";
+ }
+ if (!lastBranch)
+ return false;
+
+ state = StateHeading;
+ htmldata.clear();
+ vymtext.clear();
+ if (!atts.value("fonthint").isEmpty())
+ vymtext.setFontHint(atts.value("fonthint"));
+ if (!atts.value("textMode").isEmpty()) {
+ if (atts.value("textMode") == "richText")
+ vymtext.setRichText(true);
+ else
+ vymtext.setRichText(false);
+ }
+ if (!atts.value("textColor").isEmpty()) {
+ // For compatibility with <= 2.4.0 set both branch and
+ // heading color
+ col.setNamedColor(atts.value("textColor"));
+ lastBranch->setHeadingColor(col);
+ vymtext.setColor(col);
+ }
+ if (!atts.value("text").isEmpty())
+ vymtext.setText(unquoteQuotes(atts.value("text")));
+ }
+ else if (eName == "task" &&
+ (state == StateMapCenter || state == StateBranch)) {
+ state = StateTask;
+ lastTask = taskModel->createTask(lastBranch);
+ if (!readTaskAttr(atts))
+ return false;
+ }
+ else if (eName == "note" &&
+ (state == StateMapCenter ||
+ state == StateBranch)) { // only for backward compatibility
+ // (<1.4.6). Use htmlnote now.
+ state = StateNote;
+ htmldata.clear();
+ vymtext.clear();
+ if (!readNoteAttr(atts))
+ return false;
+ }
+ else if (eName == "htmlnote" &&
+ state == StateMapCenter) { // only for backward compatibility. Use
+ // vymnote now
+ state = StateHtmlNote;
+ vymtext.clear();
+ if (!atts.value("fonthint").isEmpty())
+ vymtext.setFontHint(atts.value("fonthint"));
+ }
+ else if (eName == "vymnote" &&
+ (state == StateMapCenter || state == StateBranch ||
+ state == StateInit)) {
+ if (state == StateInit)
+ // Only read some stuff like VymNote or Heading
+ // e.g. for undo/redo
+ {
+ lastBranch = model->getSelectedBranch();
+ if (version.isEmpty())
+ version = "0.0.0";
+ }
+ state = StateVymNote;
+ htmldata.clear();
+ vymtext.clear();
+ if (!atts.value("fonthint").isEmpty())
+ vymtext.setFontHint(atts.value("fonthint"));
+ if (!atts.value("textMode").isEmpty()) {
+ if (atts.value("textMode") == "richText")
+ vymtext.setRichText(true);
+ else
+ vymtext.setRichText(false);
+ }
+ if (!atts.value("text").isEmpty())
+ vymtext.setText(unquoteQuotes(atts.value("text")));
+ }
+ else if (eName == "floatimage" &&
+ (state == StateMapCenter || state == StateBranch)) {
+ state = StateImage;
+ lastImage = model->createImage(lastBranch);
+ if (!readImageAttr(atts))
+ return false;
+ }
+ else if ((eName == "branch" || eName == "floatimage") &&
+ state == StateMap) {
+ // This is used in vymparts, which have no mapcenter or for undo
+ isVymPart = true;
+ TreeItem *ti = model->getSelectedItem();
+ if (!ti) {
+ // If a vym part is _loaded_ (not imported),
+ // selection==lmo==NULL
+ // Treat it like ImportAdd then...
+ loadMode = ImportAdd;
+ // we really have no MCO at this time
+ lastBranch = model->createMapCenter();
+ model->select(lastBranch);
+ model->setHeadingPlainText("Import");
+ ti = lastBranch;
+ }
+ if (ti && ti->isBranchLikeType()) {
+ lastBranch = (BranchItem *)ti;
+ if (eName == "branch") {
+ state = StateBranch;
+ if (loadMode == ImportAdd) {
+ lastBranch = model->createBranch(lastBranch);
+ model->setLatestAddedItem(lastBranch);
+ if (insertPos >= 0)
+ model->relinkBranch(lastBranch, (BranchItem *)ti,
+ insertPos);
+ }
+ else
+ model->clearItem(lastBranch);
+ readBranchAttr(atts);
+ }
+ else if (eName == "floatimage") {
+ state = StateImage;
+ lastImage = model->createImage(lastBranch);
+ model->setLatestAddedItem(lastImage);
+ if (!readImageAttr(atts))
+ return false;
+ }
+ else
+ return false;
+ }
+ else
+ return false;
+ }
+ else if (eName == "branch" && state == StateMapCenter) {
+ state = StateBranch;
+ lastBranch = model->createBranch(lastBranch);
+ readBranchAttr(atts);
+ }
+ else if (eName == "htmlnote" &&
+ state == StateBranch) { // only for backward compatibility. Use
+ // vymnote now
+ state = StateHtmlNote;
+ vymtext.clear();
+ if (!atts.value("fonthint").isEmpty())
+ vymtext.setFontHint(atts.value("fonthint"));
+ }
+ else if (eName == "frame" &&
+ (state == StateBranch || state == StateMapCenter)) {
+ state = StateFrame;
+ if (!readFrameAttr(atts))
+ return false;
+ }
+ else if (eName == "xlink" && state == StateBranch) {
+ // Obsolete after 1.13.2
+ state = StateBranchXLink;
+ if (!readXLinkAttr(atts))
+ return false;
+ }
+ else if (eName == "xlink" && state == StateMap) {
+ state = StateLink;
+ if (!readLinkNewAttr(atts))
+ return false;
+ }
+ else if (eName == "branch" && state == StateBranch) {
+ lastBranch = model->createBranch(lastBranch);
+ readBranchAttr(atts);
+ }
+ else if (eName == "html" &&
+ (state == StateHtmlNote ||
+ state == StateNote ||
+ state == StateVymNote)) { // Only for backward compatibility
+ state = StateHtml;
+ htmldata = "<" + eName;
+ readHtmlAttr(atts);
+ htmldata += ">";
+ }
+ else if (eName == "attribute" &&
+ (state == StateBranch || state == StateMapCenter)) {
+ state = StateAttribute;
+ AttributeItem *ai = new AttributeItem(lastBranch);
+ if (ai) {
+ if (!atts.value("key").isEmpty())
+ ai->setKey(atts.value("key"));
+
+ QString type = atts.value("type");
+ QString val = atts.value("value");
+ if (!type.isEmpty()) {
+ if (type == "Integer")
+ ai->setValue(val.toInt());
+ else if (type == "String")
+ ai->setValue(val);
+ else if (type == "Undefined") {
+ ai->setValue(val);
+ ai->setAttributeType(AttributeItem::Undefined);
+ qWarning() << "Found attribute type 'Undefined'";
+ } else if (type == "DateTime") {
+ ai->setValue(QDateTime::fromString(val, Qt::ISODate));
+ } else
+ qWarning() << "Found unknown attribute type: " << type;
+ } else {
+ if (!atts.value("value").isEmpty())
+ ai->setValue(atts.value("value"));
+ }
+ }
+ model->setAttribute(lastBranch, ai);
+ }
+ else if (state == StateHtml) {
+ // Only for backward compatibility
+ // accept all while in html mode,
+ htmldata += "<" + eName;
+ readHtmlAttr(atts);
+ htmldata += ">";
+ }
+ else
+ return false; // Error
+ return true;
+}
+
+bool parseVYMHandler::endElement(const QString &, const QString &,
+ const QString &eName)
+{
+ /* Testing
+ QString h;
+ lastBranch ? h = lastBranch->getHeadingPlain() : h = "";
+ qDebug() << "endElement </" << eName << "> state=" << state << " lastBranch=" << h;
+ */
+
+ switch (state) {
+ case StateMap:
+ case StateMapDesign:
+ case StateMD:
+ break;
+ case StateMapCenter:
+ model->emitDataChanged(lastBranch);
+ lastBranch = (BranchItem *)(lastBranch->parent());
+ break;
+ case StateBranch:
+ // Empty branches may not be scrolled
+ // (happens if bookmarks are imported)
+ if (lastBranch->isScrolled() && lastBranch->branchCount() == 0)
+ lastBranch->unScroll();
+
+ model->emitDataChanged(lastBranch);
+ lastBranch = (BranchItem *)(lastBranch->parent());
+ lastBranch->setLastSelectedBranch(0);
+ break;
+ case StateTask:
+ break;
+ case StateHeading:
+ if (versionLowerOrEqual(version, "2.4.99") &&
+ htmldata.contains("<html>"))
+ // versions before 2.5.0 didn't use CDATA to save richtext
+ vymtext.setAutoText(htmldata);
+ else {
+ // Versions 2.5.0 to 2.7.562 had HTML data encoded as CDATA
+ // Later versions use the <vymnote text="..."> attribute,
+ // which is set already in begin element
+ // If both htmldata and vymtext are already available, use the
+ // vymtext
+ if (vymtext.isEmpty())
+ vymtext.setText(htmldata);
+ }
+ lastBranch->setHeading(vymtext);
+ break;
+ case StateHtmlNote: // Richtext note, needed anyway for backward
+ // compatibility
+ if (htmldata.contains("<html"))
+ vymtext.setRichText(htmldata);
+ else
+ vymtext.setPlainText(htmldata);
+ lastBranch->setNote(vymtext);
+ break;
+ case StateMapSlide:
+ lastSlide = NULL;
+ break;
+ case StateNote:
+ // version < 1.4.6
+ if (!htmldata.isEmpty()) {
+ if (htmldata.contains("<html"))
+ vymtext.setRichText(htmldata);
+ else
+ vymtext.setPlainText(htmldata);
+ }
+ lastBranch->setNote(vymtext);
+ break;
+ case StateMapSetting:
+ // version >= 2.5.0 previously value only as attribut
+ settings.setLocalValue(model->getDestPath(), lastSetting, htmldata);
+ break;
+ case StateVymNote: // Might be richtext or plaintext with
+ // version >= 1.13.8
+ if (versionLowerOrEqual(version, "2.4.99") &&
+ htmldata.contains("<html>"))
+ // versions before 2.5.0 didn't use CDATA to save richtext
+ vymtext.setAutoText(htmldata);
+ else {
+ // Versions 2.5.0 to 2.7.562 had HTML data encoded as CDATA
+ // Later versions use the <vymnote text="..."> attribute,
+ // which is set already in begin element
+ // If both htmldata and vymtext are already available, use the
+ // vymtext
+ if (vymtext.isEmpty())
+ vymtext.setText(htmldata);
+ }
+ lastBranch->setNote(vymtext);
+ break;
+ case StateHtml:
+ htmldata += "</" + eName + ">";
+ if (eName == "html")
+ htmldata.replace("<br></br>", "<br />");
+ break;
+ default:
+ break;
+ }
+ state = stateStack.takeLast();
+ return true;
+}
+
+bool parseVYMHandler::characters(const QString &ch)
+{
+ // qDebug()<< "xml-vym: characters " << ch << " state=" << state;
+
+ QString ch_org = quoteMeta(ch);
+ QString ch_simplified = ch.simplified();
+
+ switch (state) {
+ case StateInit:
+ case StateMap:
+ case StateMapDesign:
+ case StateMD:
+ break;
+ case StateMapSelect:
+ model->select(ch_simplified);
+ break;
+ case StateMapSetting:
+ htmldata += ch;
+ break;
+ case StateMapCenter:
+ break;
+ case StateNote: // only in vym <1.4.6
+ htmldata += ch_simplified;
+ break;
+ case StateBranch:
+ break;
+ case StateStandardFlag:
+ lastBranch->activateStandardFlagByName(ch_simplified);
+ break;
+ case StateImage:
+ break;
+ case StateVymNote:
+ htmldata += ch;
+ break;
+ case StateHtmlNote: // Only for compatibility
+ htmldata += ch;
+ break;
+ case StateHtml:
+ htmldata += ch_simplified;
+ break;
+ case StateHeading:
+ htmldata += ch;
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+QString parseVYMHandler::errorString()
+{
+ return "the document is not in the VYM file format";
+}
+
+bool parseVYMHandler::readMapAttr( const QXmlAttributes &a)
+{
+ if (!a.value("author").isEmpty())
+ model->setAuthor(a.value("author"));
+ if (!a.value("title").isEmpty())
+ model->setTitle(a.value("title"));
+ if (!a.value("comment").isEmpty())
+ model->setComment(unquoteMeta(a.value("comment")));
+ if (!a.value("branchCount").isEmpty()) {
+ branchesTotal = a.value("branchCount").toInt();
+ if (branchesTotal > 10) {
+ useProgress = true;
+ mainWindow->setProgressMaximum(branchesTotal);
+ }
+ }
+
+ if (!a.value("mapZoomFactor").isEmpty())
+ model->setMapZoomFactor(a.value("mapZoomFactor").toDouble());
+ if (!a.value("mapRotationAngle").isEmpty())
+ model->setMapRotationAngle(a.value("mapRotationAngle").toDouble());
+
+ return readMapDesignCompatibleAttr(a);
+}
+
+bool parseVYMHandler::readMapDesignCompatibleAttr( const QXmlAttributes &a)
+{
+ // Some attributes moved in version 2.9.514 from
+ // <vymmap> to <mapdesign> and <md>
+ // This code here will allow to parse also newer maps.
+ // Some elements though are not available in older versions, especially
+ // <frame frameUsage="outerFrame" ...>
+ // <branch rotHeading=... rotContent=... >
+
+ QColor col;
+ if (!a.value("backgroundColor").isEmpty()) {
+ col.setNamedColor(a.value("backgroundColor"));
+ model->setMapBackgroundColor(col);
+ }
+ if (!a.value("defaultFont").isEmpty()) {
+ QFont font;
+ font.fromString(a.value("defaultFont"));
+ model->setMapDefaultFont(font);
+ }
+ if (!a.value("selectionColor").isEmpty()) {
+ // Only for compatibility
+ col.setNamedColor(a.value("selectionColor"));
+ model->setSelectionBrushColor(col);
+ model->setSelectionPenColor(col);
+ model->setSelectionPenWidth(1);
+ }
+ if (!a.value("selectionPenColor").isEmpty()) {
+ // Introduced in 2.9.12
+ col.setNamedColor(a.value("selectionPenColor"));
+ model->setSelectionPenColor(col);
+ }
+ if (!a.value("selectionPenWidth").isEmpty()) {
+ // Introduced in 2.9.12
+ bool ok;
+ qreal w = a.value("selectionPenWidth").toFloat(&ok);
+ if (ok)
+ model->setSelectionPenWidth(w);
+ }
+ if (!a.value("selectionBrushColor").isEmpty()) {
+ // Introduced in 2.9.12
+ col.setNamedColor(a.value("selectionBrushColor"));
+ model->setSelectionBrushColor(col);
+ }
+ if (!a.value("linkColorHint").isEmpty()) {
+ if (a.value("linkColorHint") == "HeadingColor")
+ model->setMapLinkColorHint(LinkableMapObj::HeadingColor);
+ else
+ model->setMapLinkColorHint(LinkableMapObj::DefaultColor);
+ }
+ if (!a.value("linkStyle").isEmpty())
+ model->setMapLinkStyle(a.value("linkStyle"));
+ if (!a.value("linkColor").isEmpty()) {
+ col.setNamedColor(a.value("linkColor"));
+ model->setMapDefLinkColor(col);
+ }
+
+ QPen pen(model->getMapDefXLinkPen());
+ if (!a.value("defXLinkColor").isEmpty()) {
+ col.setNamedColor(a.value("defXLinkColor"));
+ pen.setColor(col);
+ }
+ if (!a.value("defXLinkWidth").isEmpty())
+ pen.setWidth(a.value("defXLinkWidth").toInt());
+ if (!a.value("defXLinkPenStyle").isEmpty()) {
+ bool ok;
+ Qt::PenStyle ps = penStyle(a.value("defXLinkPenStyle"), ok);
+ if (!ok)
+ return false;
+ pen.setStyle(ps);
+ }
+ model->setMapDefXLinkPen(pen);
+
+ if (!a.value("defXLinkStyleBegin").isEmpty())
+ model->setMapDefXLinkStyleBegin(a.value("defXLinkStyleBegin"));
+ if (!a.value("defXLinkStyleEnd").isEmpty())
+ model->setMapDefXLinkStyleEnd(a.value("defXLinkStyleEnd"));
+ return true;
+}
+
+bool parseVYMHandler::readBranchAttr(const QXmlAttributes &a)
+{
+ branchesCounter++;
+ if (useProgress)
+ mainWindow->addProgressValue((float)branchesCounter / branchesTotal);
+
+ lastMI = lastBranch;
+
+ if (!readOOAttr(a))
+ return false;
+
+ if (!a.value("scrolled").isEmpty())
+ lastBranch->toggleScroll();
+
+ if (!a.value("incImgV").isEmpty()) {
+ if (a.value("incImgV") == "true")
+ lastBranch->setIncludeImagesVer(true);
+ else
+ lastBranch->setIncludeImagesVer(false);
+ }
+ if (!a.value("incImgH").isEmpty()) {
+ if (a.value("incImgH") == "true")
+ lastBranch->setIncludeImagesHor(true);
+ else
+ lastBranch->setIncludeImagesHor(false);
+ }
+ if (a.value("childrenFreePos") == "true")
+ lastBranch->setChildrenLayout(BranchItem::FreePositioning);
+
+ return true;
+}
+
+bool parseVYMHandler::readFrameAttr(const QXmlAttributes &a)
+{
+ if (lastMI) {
+ OrnamentedObj *oo = (OrnamentedObj *)(lastMI->getLMO());
+ if (oo) {
+ bool ok;
+ int x;
+ {
+ if (!a.value("frameType").isEmpty())
+ oo->setFrameType(a.value("frameType"));
+ if (!a.value("penColor").isEmpty())
+ oo->setFramePenColor(a.value("penColor"));
+ if (!a.value("brushColor").isEmpty()) {
+ oo->setFrameBrushColor(a.value("brushColor"));
+ lastMI->setBackgroundColor(a.value("brushColor"));
+ }
+ if (!a.value("padding").isEmpty()) {
+ x = a.value("padding").toInt(&ok);
+ if (ok)
+ oo->setFramePadding(x);
+ }
+ if (!a.value("borderWidth").isEmpty()) {
+ x = a.value("borderWidth").toInt(&ok);
+ if (ok)
+ oo->setFrameBorderWidth(x);
+ }
+ if (!a.value("includeChildren").isEmpty()) {
+ if (a.value("includeChildren") == "true")
+ oo->setFrameIncludeChildren(true);
+ else
+ oo->setFrameIncludeChildren(false);
+ }
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+bool parseVYMHandler::readOOAttr(const QXmlAttributes &a)
+{
+ if (lastMI) {
+ bool okx, oky;
+ float x, y;
+ if (!a.value("posX").isEmpty()) { // Introduced in 2.9.501, added here for file compatibility
+ if (!a.value("posY").isEmpty()) {
+ x = a.value("posX").toFloat(&okx);
+ y = a.value("posY").toFloat(&oky);
+ if (okx && oky)
+ lastMI->setRelPos(QPointF(x, y));
+ else
+ return false; // Couldn't read relPos
+ }
+ }
+ if (!a.value("relPosX").isEmpty()) {
+ if (!a.value("relPosY").isEmpty()) {
+ x = a.value("relPosX").toFloat(&okx);
+ y = a.value("relPosY").toFloat(&oky);
+ if (okx && oky)
+ lastMI->setRelPos(QPointF(x, y));
+ else
+ return false; // Couldn't read relPos
+ }
+ }
+ if (!a.value("absPosX").isEmpty()) {
+ if (!a.value("absPosY").isEmpty()) {
+ x = a.value("absPosX").toFloat(&okx);
+ y = a.value("absPosY").toFloat(&oky);
+ if (okx && oky)
+ lastMI->setAbsPos(QPointF(x, y));
+ else
+ return false; // Couldn't read absPos
+ }
+ }
+ if (!a.value("url").isEmpty())
+ lastMI->setURL(a.value("url"));
+ if (!a.value("vymLink").isEmpty())
+ lastMI->setVymLink(a.value("vymLink"));
+ if (!a.value("hideInExport").isEmpty())
+ if (a.value("hideInExport") == "true")
+ lastMI->setHideInExport(true);
+
+ if (!a.value("hideLink").isEmpty()) {
+ if (a.value("hideLink") == "true")
+ lastMI->setHideLinkUnselected(true);
+ else
+ lastMI->setHideLinkUnselected(false);
+ }
+
+ if (!a.value("localTarget").isEmpty())
+ if (a.value("localTarget") == "true")
+ lastMI->toggleTarget();
+ if (!a.value("rotation").isEmpty()) {
+ x = a.value("rotation").toFloat(&okx);
+ if (okx)
+ lastMI->setRotation(x);
+ else
+ return false; // Couldn't read rotation
+ }
+
+ if (!a.value("uuid").isEmpty()) {
+ // While pasting, check for existing UUID
+ if (loadMode != ImportAdd && !model->findUuid(a.value("uuid")))
+ lastMI->setUuid(a.value("uuid"));
+ }
+ }
+ return true;
+}
+
+bool parseVYMHandler::readNoteAttr(const QXmlAttributes &a)
+{ // only for backward compatibility (<1.4.6). Use htmlnote now.
+ vymtext.clear();
+ QString fn;
+ if (!a.value("href").isEmpty()) {
+ // Load note
+ fn = parseHREF(a.value("href"));
+ QFile file(fn);
+ QString s; // Reading a note
+
+ if (!file.open(QIODevice::ReadOnly)) {
+ qWarning() << "parseVYMHandler::readNoteAttr: Couldn't load " + fn;
+ return false;
+ }
+ QTextStream stream(&file);
+ stream.setCodec("UTF-8");
+ QString lines;
+ while (!stream.atEnd()) {
+ lines += stream.readLine() + "\n";
+ }
+ file.close();
+
+ if (lines.contains("<html")) {
+ vymtext.setRichText(lines);
+ } else
+ vymtext.setPlainText(lines);
+ }
+ if (!a.value("fonthint").isEmpty())
+ vymtext.setFontHint(a.value("fonthint"));
+ return true;
+}
+
+bool parseVYMHandler::readImageAttr(const QXmlAttributes &a)
+{
+ lastMI = lastImage;
+
+ if (!readOOAttr(a))
+ return false;
+
+ if (!a.value("href").isEmpty()) {
+ // Load Image
+ if (!lastImage->load(parseHREF(a.value("href")))) {
+ QMessageBox::warning(0, "Warning: ",
+ "Couldn't load image\n" +
+ parseHREF(a.value("href")));
+ lastImage = NULL;
+ return true;
+ }
+ }
+ if (!a.value("zPlane").isEmpty())
+ lastImage->setZValue(a.value("zPlane").toInt());
+ float x, y;
+ bool okx, oky;
+ if (!a.value("posX").isEmpty()) { // Introduced in 2.9.501, added here for file compatibility
+ if (!a.value("posY").isEmpty()) {
+ x = a.value("posX").toFloat(&okx);
+ y = a.value("posY").toFloat(&oky);
+ if (okx && oky)
+ lastImage->setRelPos(QPointF(x, y));
+ else
+ return false; // Couldn't read relPos
+ }
+ }
+ if (!a.value("relPosX").isEmpty()) {
+ if (!a.value("relPosY").isEmpty()) {
+ // read relPos
+ x = a.value("relPosX").toFloat(&okx);
+ y = a.value("relPosY").toFloat(&oky);
+ if (okx && oky)
+ lastImage->setRelPos(QPointF(x, y));
+ else
+ // Couldn't read relPos
+ return false;
+ }
+ }
+
+ // Scale image
+ // scaleX and scaleY are no longer used since 2.7.509 and replaced by
+ // scaleFactor
+ x = y = 1;
+ if (!a.value("scaleX").isEmpty()) {
+ x = a.value("scaleX").toFloat(&okx);
+ if (!okx)
+ return false;
+ }
+
+ if (!a.value("scaleY").isEmpty()) {
+ x = a.value("scaleY").toFloat(&oky);
+ if (!oky)
+ return false;
+ }
+
+ if (!a.value("scaleFactor").isEmpty()) {
+ x = a.value("scaleFactor").toFloat(&okx);
+ if (!okx)
+ return false;
+ }
+
+ if (x != 1)
+ lastImage->setScaleFactor(x);
+
+ if (!readOOAttr(a))
+ return false;
+
+ if (!a.value("originalName").isEmpty())
+ {
+ lastImage->setOriginalFilename(a.value("originalName"));
+ }
+ return true;
+}
+
+bool parseVYMHandler::readXLinkAttr(const QXmlAttributes &a)
+{
+ // Obsolete, see also readLinkAttr
+
+ if (!a.value("beginID").isEmpty()) {
+ if (!a.value("endID").isEmpty()) {
+ TreeItem *beginBI = model->findBySelectString(a.value("beginID"));
+ TreeItem *endBI = model->findBySelectString(a.value("endID"));
+ if (beginBI && endBI && beginBI->isBranchLikeType() &&
+ endBI->isBranchLikeType()) {
+ Link *li = new Link(model);
+ li->setBeginBranch((BranchItem *)beginBI);
+ li->setEndBranch((BranchItem *)endBI);
+ QPen pen = li->getPen();
+
+ if (!a.value("color").isEmpty()) {
+ QColor col;
+ col.setNamedColor(a.value("color"));
+ pen.setColor(col);
+ }
+
+ if (!a.value("width").isEmpty()) {
+ bool okx;
+ pen.setWidth(a.value("width").toInt(&okx, 10));
+ }
+ li->setPen(pen);
+ model->createLink(li);
+ }
+ }
+ }
+ return true;
+}
+
+bool parseVYMHandler::readLinkNewAttr(const QXmlAttributes &a)
+{
+ // object ID is used starting in version 1.8.76
+ // (before there was beginBranch and endBranch)
+ //
+ // Starting in 1.13.2 xlinks are no longer subitems of branches,
+ // but listed at the end of the data in a map. This makes handling
+ // of links much safer and easier
+
+ if (!a.value("beginID").isEmpty()) {
+ if (!a.value("endID").isEmpty()) {
+ TreeItem *beginBI = model->findBySelectString(a.value("beginID"));
+ TreeItem *endBI = model->findBySelectString(a.value("endID"));
+ if (beginBI && endBI && beginBI->isBranchLikeType() &&
+ endBI->isBranchLikeType()) {
+ Link *li = new Link(model);
+ li->setBeginBranch((BranchItem *)beginBI);
+ li->setEndBranch((BranchItem *)endBI);
+
+ model->createLink(li);
+
+ bool okx;
+ QPen pen = li->getPen();
+ if (!a.value("type").isEmpty()) {
+ li->setLinkType(a.value("type"));
+ }
+ if (!a.value("color").isEmpty()) {
+ QColor col;
+ col.setNamedColor(a.value("color"));
+ pen.setColor(col);
+ }
+ if (!a.value("width").isEmpty()) {
+ pen.setWidth(a.value("width").toInt(&okx, 10));
+ }
+ if (!a.value("penstyle").isEmpty()) {
+ pen.setStyle(penStyle(a.value("penstyle"), okx));
+ }
+ li->setPen(pen);
+
+ if (!a.value("styleBegin").isEmpty())
+ li->setStyleBegin(a.value("styleBegin"));
+ if (!a.value("styleEnd").isEmpty())
+ li->setStyleEnd(a.value("styleEnd"));
+
+ XLinkObj *xlo = (XLinkObj *)(li->getMO());
+ if (xlo && !a.value("c0").isEmpty()) {
+ QPointF p = point(a.value("c0"), okx);
+ if (okx)
+ xlo->setC0(p);
+ }
+ if (xlo && !a.value("c1").isEmpty()) {
+ QPointF p = point(a.value("c1"), okx);
+ if (okx)
+ xlo->setC1(p);
+ }
+ }
+ }
+ }
+ return true;
+}
+
+bool parseVYMHandler::readSettingAttr(const QXmlAttributes &a)
+{
+ if (!a.value("key").isEmpty()) {
+ lastSetting = a.value("key");
+ if (!a.value("value").isEmpty())
+ settings.setLocalValue(
+ model->getDestPath(), a.value("key"),
+ a.value("value"));
+ else
+ return false;
+ }
+ else
+ return false;
+
+ return true;
+}
+
+bool parseVYMHandler::readSlideAttr(const QXmlAttributes &a)
+{
+ QStringList scriptlines; // FIXME-3 needed for switching to inScript
+ // Most attributes are obsolete with inScript
+ if (!lastSlide)
+ return false;
+ {
+ if (!a.value("name").isEmpty())
+ lastSlide->setName(a.value("name"));
+ if (!a.value("zoom").isEmpty()) {
+ bool ok;
+ qreal z = a.value("zoom").toDouble(&ok);
+ if (!ok)
+ return false;
+ scriptlines.append(QString("setMapZoom(%1)").arg(z));
+ }
+ if (!a.value("rotation").isEmpty()) {
+ bool ok;
+ qreal z = a.value("rotation").toDouble(&ok);
+ if (!ok)
+ return false;
+ scriptlines.append(QString("setMapRotation(%1)").arg(z));
+ }
+ if (!a.value("duration").isEmpty()) {
+ bool ok;
+ int d = a.value("duration").toInt(&ok);
+ if (!ok)
+ return false;
+ scriptlines.append(QString("setMapAnimDuration(%1)").arg(d));
+ }
+ if (!a.value("curve").isEmpty()) {
+ bool ok;
+ int i = a.value("curve").toInt(&ok);
+ if (!ok)
+ return false;
+ if (i < 0 || i > QEasingCurve::OutInBounce)
+ return false;
+ scriptlines.append(QString("setMapAnimCurve(%1)").arg(i));
+ }
+ if (!a.value("mapitem").isEmpty()) {
+ TreeItem *ti = model->findBySelectString(a.value("mapitem"));
+ if (!ti)
+ return false;
+ scriptlines.append(
+ QString("centerOnID(\"%1\")").arg(ti->getUuid().toString()));
+ }
+ if (!a.value("inScript").isEmpty()) {
+ lastSlide->setInScript(unquoteMeta(a.value("inScript")));
+ }
+ else
+ lastSlide->setInScript(unquoteMeta(scriptlines.join(";\n")));
+
+ if (!a.value("outScript").isEmpty()) {
+ lastSlide->setOutScript(unquoteMeta(a.value("outScript")));
+ }
+ }
+ return true;
+}
+
+bool parseVYMHandler::readTaskAttr(const QXmlAttributes &a)
+{
+ if (!lastTask)
+ return false;
+ {
+ if (!a.value("status").isEmpty())
+ lastTask->setStatus(a.value("status"));
+ if (!a.value("awake").isEmpty())
+ lastTask->setAwake(a.value("awake"));
+ if (!a.value("date_creation").isEmpty())
+ lastTask->setDateCreation(a.value("date_creation"));
+ if (!a.value("date_modification").isEmpty())
+ lastTask->setDateModification(a.value("date_modification"));
+ if (!a.value("date_sleep").isEmpty()) {
+ if (!lastTask->setDateSleep(a.value("date_sleep")))
+ return false;
+ }
+ if (!a.value("prio_delta").isEmpty()) {
+ lastTask->setPriorityDelta(a.value("prio_delta").toInt());
+ }
+ }
+ return true;
+}
+
+bool parseVYMHandler::readUserFlagDefAttr(const QXmlAttributes &a)
+{
+ QString name;
+ QString path;
+ QString tooltip;
+ QUuid uid;
+
+ if (!a.value("name").isEmpty())
+ name = a.value("name");
+ if (!a.value("tooltip").isEmpty())
+ tooltip = a.value("tooltip");
+ if (!a.value("uuid").isEmpty())
+ uid = QUuid(a.value("uuid"));
+
+ Flag *flag;
+
+ if (!a.value("href").isEmpty()) {
+ // Setup flag with image
+ flag = mainWindow->setupFlag(parseHREF(a.value("href")), Flag::UserFlag,
+ name, tooltip, uid);
+ }
+ else {
+ qWarning() << "readUserFlagDefAttr: Couldn't read href of flag "
+ << a.value("name");
+ return false;
+ }
+
+ if (!a.value("group").isEmpty())
+ flag->setGroup(a.value("group"));
+
+ return true;
+}
+
+bool parseVYMHandler::readUserFlagAttr(const QXmlAttributes &a)
+{
+ QString name;
+ QString uuid;
+
+ if (!a.value("name").isEmpty())
+ name = a.value("name");
+ if (!a.value("uuid").isEmpty())
+ uuid = a.value("uuid");
+
+ lastBranch->toggleFlagByUid(QUuid(uuid));
+
+ return true;
+}