]> git.sven.stormbind.net Git - sven/vym.git/blob - xml-vym.cpp
0bdb12b0fc200ad44fd75511151ec0f4e48e985e
[sven/vym.git] / xml-vym.cpp
1 #include "xml-vym.h"
2
3 #include <QMessageBox>
4 #include <QColor>
5 #include <QTextStream>
6 #include <typeinfo>
7
8 #include "attributeitem.h"
9 #include "branchitem.h"
10 #include "misc.h"
11 #include "settings.h"
12 #include "linkablemapobj.h"
13 #include "mainwindow.h"
14 #include "slideitem.h"
15 #include "task.h"
16 #include "taskmodel.h"
17 #include "version.h"
18 #include "xlinkitem.h"
19
20 extern Main *mainWindow;
21 extern Settings settings;
22 extern TaskModel *taskModel;
23 extern QString vymVersion;
24
25 parseVYMHandler::parseVYMHandler()
26 {
27     // Default is to load everything
28     contentFilter = 0x0000; // TODO  use filters for all content types below
29 }
30
31 void parseVYMHandler::setContentFilter (const int &c)
32 {
33     contentFilter=c;
34 }
35
36 bool parseVYMHandler::startDocument()
37 {
38     errorProt = "";
39     state = StateInit;
40     stateStack.clear();
41     stateStack.append(StateInit);
42     htmldata="";
43     isVymPart=false;
44     useProgress=false;
45     return true;
46 }
47
48 bool parseVYMHandler::startElement  ( const QString&, const QString&,
49                     const QString& eName, const QXmlAttributes& atts ) 
50 {
51     QColor col;
52     /* Testing
53     qDebug()<< "startElement: <"<< eName
54             << ">     state="<<state 
55             << "  laststate="<<stateStack.last()
56             << "   loadMode="<<loadMode
57             //<<"       line="<<QXmlDefaultHandler::lineNumber();
58         <<"contentFilter="<<contentFilter;
59     */        
60     stateStack.append (state);        
61     if ( state == StateInit && (eName == "vymmap")  ) 
62     {
63         state = StateMap;
64         branchesTotal=0;        
65         branchesCounter=0;
66
67         if (loadMode==NewMap )
68         {
69             // Create mapCenter
70             model->clear();
71             lastBranch=NULL;
72
73             readMapAttr (atts);
74         }   
75         // Check version
76         if (!atts.value( "version").isEmpty() ) 
77         {
78             version = atts.value("version");
79             if (!versionLowerOrEqualThanVym( version ))
80                 QMessageBox::warning( 0, QObject::tr("Warning: Version Problem") , 
81                    QObject::tr("<h3>Map is newer than VYM</h3>"
82                    "<p>The map you are just trying to load was "
83                    "saved using vym %1. "
84                    "The version of this vym is %2. " 
85                    "If you run into problems after pressing "
86                    "the ok-button below, updating vym should help.</p>").arg(version).arg(vymVersion));
87             else       
88                 model->setVersion(version);
89
90         }
91
92     } else if ( eName == "select" && state == StateMap ) 
93     {
94         state=StateMapSelect;
95     } else if ( eName == "setting" && state == StateMap ) 
96     {
97         state=StateMapSetting;
98         if (loadMode==NewMap)
99         {
100             htmldata.clear();
101             readSettingAttr (atts);
102         }
103     } else if ( eName == "slide" && state == StateMap )
104     {
105         state=StateMapSlide;
106         if (!  contentFilter && SlideContent)  
107         {   
108             // Ignore slides during paste
109             lastSlide=model->addSlide();
110             if (insertPos>=0)
111             model->relinkSlide (lastSlide, insertPos);
112             
113             readSlideAttr(atts);
114         }
115     } else if ( eName == "mapcenter" && state == StateMap ) 
116     {
117         state=StateMapCenter;
118         if (loadMode==NewMap)
119         {   
120             // Really use the found mapcenter as MCO in a new map
121             lastBranch=model->createMapCenter(); 
122         } else
123         {
124             // Treat the found mapcenter as a branch 
125             // in an existing map
126             BranchItem *bi=model->getSelectedBranch();        
127             if (bi)
128             {
129                 lastBranch=bi;
130                 if (loadMode==ImportAdd)
131                 {
132                     // Import Add
133                     if (insertPos<0) 
134                         lastBranch=model->createBranch(lastBranch);
135                     else
136                     {
137                         lastBranch=model->addNewBranch(lastBranch, insertPos);
138                         insertPos++;
139                     }
140                 } else  
141                 {
142                     // Import Replace 
143                     if (insertPos<0)
144                     {
145                         insertPos=lastBranch->num() +1;
146                         model->clearItem (lastBranch);
147                     } else
148                     {
149                         BranchItem *pi=bi->parentBranch();
150                         lastBranch=model->addNewBranch(pi, insertPos);
151                         insertPos++;
152                     }
153                 }
154             } else
155                 // if nothing selected, add mapCenter without parent
156                 lastBranch=model->createMapCenter(); 
157         }        
158         readBranchAttr (atts);
159     } else if ( 
160         (eName == "standardflag" ||eName == "standardFlag") && 
161         (state == StateMapCenter || state==StateBranch)) 
162     {
163         state=StateStandardFlag;
164     } else if ( eName == "heading" && (state == StateMapCenter||state==StateBranch || state == StateInit))
165     {
166         if (state == StateInit)
167         {
168             // Only read some stuff like VymNote or Heading
169             // e.g. for undo/redo
170             lastBranch = model->getSelectedBranch();
171             if (version.isEmpty() ) version = "0.0.0";
172         }
173         if (!lastBranch) return false;
174
175         state=StateHeading;
176         htmldata.clear();
177         vymtext.clear();
178         if (!atts.value( "fonthint").isEmpty() )
179             vymtext.setFontHint(atts.value ("fonthint") );
180         if (!atts.value( "textMode").isEmpty() )
181         {
182             if (atts.value ("textMode") == "richText" )
183                 vymtext.setRichText(true);
184             else
185                 vymtext.setRichText(false);
186         }
187         if (!atts.value( "textColor").isEmpty() )
188         {
189             // For compatibility with <= 2.4.0 set both branch and
190             // heading color
191             col.setNamedColor(atts.value("textColor"));
192             lastBranch->setHeadingColor(col );
193             vymtext.setColor(col);
194         }        
195     } else if ( eName == "task" && (state == StateMapCenter||state==StateBranch)) 
196     {
197         state=StateTask;
198         lastTask=taskModel->createTask (lastBranch);
199         if (!readTaskAttr(atts)) return false;
200     } else if ( eName == "note" && 
201                 (state == StateMapCenter ||state==StateBranch))
202     {        // only for backward compatibility (<1.4.6). Use htmlnote now.
203         state=StateNote;
204         htmldata.clear();
205         vymtext.clear();
206         if (!readNoteAttr (atts) ) return false;
207     } else if ( eName == "htmlnote" && state == StateMapCenter) 
208     {   // only for backward compatibility. Use vymnote now
209         state=StateHtmlNote;
210         vymtext.clear();
211         if (!atts.value( "fonthint").isEmpty() ) 
212             vymtext.setFontHint(atts.value ("fonthint") );
213     } else if ( eName == "vymnote" && (state == StateMapCenter || state==StateBranch || state == StateInit))
214     {
215         if (state == StateInit)
216             // Only read some stuff like VymNote or Heading
217             // e.g. for undo/redo
218         {
219             lastBranch = model->getSelectedBranch();
220             if (version.isEmpty() ) version = "0.0.0";
221         }
222         state=StateVymNote;
223         htmldata.clear();
224         vymtext.clear();
225         if (!atts.value( "fonthint").isEmpty() ) 
226             vymtext.setFontHint(atts.value ("fonthint") );
227         if (!atts.value( "textMode").isEmpty() )
228         {
229             if (atts.value ("textMode") == "richText" )
230                 vymtext.setRichText(true);
231             else
232                 vymtext.setRichText(false);
233         }
234     } else if ( eName == "floatimage" &&
235                 (state == StateMapCenter ||state==StateBranch)) 
236     {
237         state=StateImage;
238         lastImage=model->createImage(lastBranch);
239         if (!readImageAttr(atts)) return false;
240     } else if ( (eName == "branch"||eName=="floatimage") && state == StateMap) 
241     {
242         // This is used in vymparts, which have no mapcenter or for undo
243         isVymPart=true;
244         TreeItem *ti=model->getSelectedItem();
245         if (!ti)
246         {
247             // If a vym part is _loaded_ (not imported), 
248             // selection==lmo==NULL
249             // Treat it like ImportAdd then...
250             loadMode=ImportAdd;
251             // we really have no MCO at this time
252             lastBranch=model->createMapCenter();
253             model->select (lastBranch);
254             model->setHeadingPlainText("Import");
255             ti=lastBranch;
256         }   
257         if (ti && ti->isBranchLikeType() )
258         {
259             lastBranch=(BranchItem*)ti;
260             if (eName=="branch")
261             {
262                 state=StateBranch;
263                 if (loadMode==ImportAdd)
264                 {
265                     lastBranch=model->createBranch(lastBranch);
266                     if (insertPos>=0)
267                         model->relinkBranch (lastBranch,(BranchItem*)ti,insertPos);
268                 } else
269                     model->clearItem (lastBranch);
270                 readBranchAttr (atts);
271             } else if (eName=="floatimage")
272             {
273                 state=StateImage;
274                 lastImage=model->createImage (lastBranch);
275                 if (!readImageAttr(atts)) return false;
276             } else return false;
277         } else return false;
278     } else if ( eName == "branch" && state == StateMapCenter) 
279     {
280         state=StateBranch;
281         lastBranch=model->createBranch(lastBranch);
282         readBranchAttr (atts);
283     } else if ( eName == "htmlnote" && state == StateBranch) 
284     {   // only for backward compatibility. Use vymnote now
285         state=StateHtmlNote;
286         vymtext.clear();
287         if (!atts.value( "fonthint").isEmpty() ) 
288             vymtext.setFontHint(atts.value ("fonthint") );
289     } else if ( eName == "frame" && (state == StateBranch||state==StateMapCenter)) 
290     {
291         state=StateFrame;
292         if (!readFrameAttr(atts)) return false;
293     } else if ( eName == "xlink" && state == StateBranch ) 
294     {
295         // Obsolete after 1.13.2
296         state=StateBranchXLink;
297         if (!readXLinkAttr (atts)) return false;
298     } else if ( eName == "xlink" && state == StateMap) 
299     {
300         state=StateLink;
301         if (!readLinkNewAttr (atts)) return false;
302     } else if ( eName == "branch" && state == StateBranch ) 
303     {
304         lastBranch=model->createBranch(lastBranch);
305         readBranchAttr (atts);
306     } else if ( eName == "html" && 
307         (state == StateHtmlNote || state == StateVymNote) ) 
308     {
309         state=StateHtml;
310         htmldata="<"+eName;
311         readHtmlAttr(atts);
312         htmldata+=">";
313     } else if ( eName == "attribute" && 
314         (state == StateBranch || state == StateMapCenter ) ) 
315     {
316         state=StateAttribute;
317         QList<QVariant> cData;
318         cData << "new attribute" << "undef";
319         AttributeItem *ai=new AttributeItem (cData);
320         if (ai)
321         {
322             if (!atts.value("type").isEmpty())
323                 ai->setKey(atts.value("type"));
324             if (!atts.value("key").isEmpty())
325                 ai->setKey(atts.value("key"));
326             if (!atts.value("value").isEmpty())
327                 ai->setKey(atts.value("value"));
328         } 
329             
330     } else if ( state == StateHtml ) 
331     {
332         // accept all while in html mode,
333         htmldata+="<"+eName;
334         readHtmlAttr(atts);
335         htmldata+=">";
336     } else
337         return false;   // Error
338     return true;
339 }
340
341 bool parseVYMHandler::endElement  ( const QString&, const QString&, const QString &eName)
342 {
343     //qDebug()<< "endElement </" <<eName <<">  state=" <<state ;
344
345     switch ( state ) 
346     {
347         case StateMap:
348             break;
349         case StateMapCenter: 
350             model->emitDataChanged (lastBranch);
351             lastBranch=(BranchItem*)(lastBranch->parent());
352             break;
353         case StateBranch: 
354             // Empty branches may not be scrolled 
355             // (happens if bookmarks are imported)
356             if (lastBranch->isScrolled() && lastBranch->branchCount()==0) 
357                 lastBranch->unScroll();
358             model->emitDataChanged (lastBranch);
359
360             lastBranch=(BranchItem*)(lastBranch->parent());
361             lastBranch->setLastSelectedBranch (0);  
362             break;
363         case StateHeading:
364             if ( versionLowerOrEqual( version, "2.4.99")  && htmldata.contains("<html>") )
365                 // versions before 2.5.0 didn't use CDATA to save richtext
366                 vymtext.setAutoText(htmldata);
367             else
368                 vymtext.setText (htmldata);
369             lastBranch->setHeading (vymtext);
370             break;
371         case StateHtmlNote: // Richtext note, needed anyway for backward compatibility
372             vymtext.setRichText (htmldata);
373             lastBranch->setNote (vymtext);
374             break;
375         case StateMapSlide: 
376             lastSlide=NULL;
377             break;  
378         case StateNote:
379             // version < 1.4.6
380             vymtext.setText (htmldata);
381             lastBranch->setNote (vymtext);
382             break;
383         case StateMapSetting:
384             // version >= 2.5.0  previously value only as attribut
385             settings.setLocalValue(model->getDestPath(), lastSetting, htmldata);
386             break;
387         case StateVymNote:            // Might be richtext or plaintext with
388             // version >= 1.13.8
389             if ( versionLowerOrEqual( version, "2.4.99")  && htmldata.contains("<html>") )
390                 // versions before 2.5.0 didn't use CDATA to save richtext
391                 vymtext.setAutoText(htmldata);
392             else
393                 vymtext.setText (htmldata);
394             lastBranch->setNote (vymtext);  
395             break;
396         case StateHtml:
397             htmldata+="</"+eName+">";
398             if (eName=="html")
399                 htmldata.replace ("<br></br>","<br />");
400             break;
401         default:
402             break;
403     }  
404     state=stateStack.takeLast();    
405     return true;
406 }
407
408 bool parseVYMHandler::characters   ( const QString& ch)
409 {
410 //    qDebug()<< "xml-vym: characters "<<ch<<"  state="<<state;
411
412     QString ch_org=quotemeta (ch);
413     QString ch_simplified=ch.simplified();
414     //if ( ch_simplified.isEmpty() ) return true;
415
416     switch ( state ) 
417     {
418         case StateInit: break;
419         case StateMap: break; 
420         case StateMapSelect:
421             model->select(ch_simplified);
422             break;
423         case StateMapSetting:
424             htmldata += ch;
425             break;
426         case StateMapCenter: break;
427         case StateNote:            // only in vym <1.4.6
428             htmldata += ch_simplified;
429             break;
430         case StateBranch: break;
431         case StateStandardFlag: 
432             lastBranch->activateStandardFlag(ch_simplified); 
433             break;
434         case StateImage: break;
435         case StateVymNote: 
436             htmldata += ch;
437             break;
438         case StateHtmlNote: // Only for compatibility
439             htmldata = ch;
440             break;
441         case StateHtml:
442             htmldata += ch_org;
443             break;
444         case StateHeading: 
445             htmldata += ch;
446             break;
447         default: 
448             return false;
449     }
450     return true;
451 }
452
453 QString parseVYMHandler::errorString() 
454 {
455     return "the document is not in the VYM file format";
456 }
457
458 bool parseVYMHandler::readMapAttr (const QXmlAttributes& a)        
459 {
460     QColor col;
461     if (!a.value( "author").isEmpty() )  
462         model->setAuthor(a.value( "author" ) );
463     if (!a.value( "title").isEmpty() )
464         model->setTitle (a.value( "title" ) );
465     if (!a.value( "comment").isEmpty() )
466         model->setComment (a.value( "comment" ) );
467     if (!a.value( "branchCount").isEmpty() )
468     {
469         branchesTotal=a.value("branchCount").toInt();
470         if (branchesTotal>10)
471         {
472             useProgress=true;
473             mainWindow->setProgressMaximum (branchesTotal);
474         }
475     } 
476         
477     if (!a.value( "backgroundColor").isEmpty() )
478     {
479         col.setNamedColor(a.value("backgroundColor"));
480         model->getScene()->setBackgroundBrush(col);
481     }            
482     if (!a.value( "defaultFont").isEmpty() )
483     {
484         QFont font;
485         font.fromString(a.value("defaultFont"));
486         model->setMapDefaultFont (font);
487     }            
488     if (!a.value( "selectionColor").isEmpty() )
489     {
490         col.setNamedColor(a.value("selectionColor"));
491         model->setSelectionColor(col);
492     }            
493     if (!a.value( "linkColorHint").isEmpty() ) 
494     {
495         if (a.value("linkColorHint")=="HeadingColor")
496             model->setMapLinkColorHint(LinkableMapObj::HeadingColor);
497         else
498             model->setMapLinkColorHint(LinkableMapObj::DefaultColor);
499     }
500     if (!a.value( "linkStyle").isEmpty() ) 
501         model->setMapLinkStyle(a.value("linkStyle"));
502     if (!a.value( "linkColor").isEmpty() ) 
503     {
504         col.setNamedColor(a.value("linkColor"));
505         model->setMapDefLinkColor(col);
506     }        
507
508     QPen pen (model->getMapDefXLinkPen() );
509     if (!a.value( "defXLinkColor").isEmpty() ) 
510     {
511         col.setNamedColor(a.value("defXLinkColor"));
512         pen.setColor(col);
513     }        
514     if (!a.value( "defXLinkWidth").isEmpty() ) 
515         pen.setWidth(a.value("defXLinkWidth").toInt ());
516     if (!a.value( "defXLinkPenStyle").isEmpty() ) 
517     {        
518         bool ok;
519         Qt::PenStyle ps=penStyle (a.value("defXLinkPenStyle"),ok );
520         if (!ok) return false;
521         pen.setStyle (ps);
522     }
523     model->setMapDefXLinkPen (pen);
524
525     if (!a.value( "defXLinkStyleBegin").isEmpty() ) 
526         model->setMapDefXLinkStyleBegin( a.value( "defXLinkStyleBegin" ) );
527     if (!a.value( "defXLinkStyleEnd").isEmpty() ) 
528         model->setMapDefXLinkStyleEnd( a.value( "defXLinkStyleEnd" ) );
529
530     if (!a.value( "mapZoomFactor").isEmpty() ) 
531         model->setMapZoomFactor(a.value("mapZoomFactor").toDouble());
532     if (!a.value( "mapRotationAngle").isEmpty() ) 
533         model->setMapRotationAngle(a.value("mapRotationAngle").toDouble());
534     return true;
535 }
536
537 bool parseVYMHandler::readBranchAttr (const QXmlAttributes& a)        
538 {
539     branchesCounter++;
540     if (useProgress) 
541         mainWindow->addProgressValue ((float)branchesCounter/branchesTotal);        
542
543     lastMI=lastBranch;
544
545     if (!readOOAttr(a)) return false;
546
547     if (!a.value( "scrolled").isEmpty() )
548         lastBranch->toggleScroll(); 
549         // (interesting for import of KDE bookmarks)
550
551     if (!a.value( "incImgV").isEmpty() ) 
552     {        
553         if (a.value("incImgV")=="true")
554             lastBranch->setIncludeImagesVer(true);
555         else        
556             lastBranch->setIncludeImagesVer(false);
557     }        
558     if (!a.value( "incImgH").isEmpty() ) 
559     {        
560         if (a.value("incImgH")=="true")
561             lastBranch->setIncludeImagesHor(true);
562         else        
563             lastBranch->setIncludeImagesHor(false);
564     }        
565     if (a.value("childrenFreePos")=="true")
566         lastBranch->setChildrenLayout(BranchItem::FreePositioning);
567     return true;    
568 }
569
570 bool parseVYMHandler::readFrameAttr (const QXmlAttributes& a)       
571 {
572     if (lastMI)
573     {
574         OrnamentedObj* oo=(OrnamentedObj*)(lastMI->getLMO()); 
575         if (oo)
576         {
577             bool ok;
578             int x;
579             {
580                 if (!a.value( "frameType").isEmpty() ) 
581                     oo->setFrameType (a.value("frameType"));
582                 if (!a.value( "penColor").isEmpty() ) 
583                     oo->setFramePenColor (a.value("penColor"));
584                 if (!a.value( "brushColor").isEmpty() ) 
585                 {
586                     oo->setFrameBrushColor (a.value("brushColor"));
587                     lastMI->setBackgroundColor (a.value("brushColor"));
588                 }
589                 if (!a.value( "padding").isEmpty() ) 
590                 {
591                     x=a.value("padding").toInt(&ok);
592                     if (ok) oo->setFramePadding(x);
593                 }   
594                 if (!a.value( "borderWidth").isEmpty() ) 
595                 {
596                     x=a.value("borderWidth").toInt(&ok);
597                     if (ok) oo->setFrameBorderWidth(x);
598                 }   
599                 if (!a.value( "includeChildren").isEmpty() ) 
600                 {
601                     if (a.value("includeChildren")=="true")
602                         oo->setFrameIncludeChildren(true);
603                     else        
604                         oo->setFrameIncludeChildren(false);
605                 }   
606             }            
607             return true;
608         }
609     }
610     return false;
611 }
612
613 bool parseVYMHandler::readOOAttr (const QXmlAttributes& a)
614 {
615     if (lastMI)
616     {
617         bool okx,oky;
618         float x,y;
619         if (!a.value( "relPosX").isEmpty() ) 
620         {
621             if (!a.value( "relPosY").isEmpty() ) 
622             {
623                 x=a.value("relPosX").toFloat (&okx);
624                 y=a.value("relPosY").toFloat (&oky);
625                 if (okx && oky  )
626                     lastMI->setRelPos (QPointF(x,y));
627                 else
628                     return false;   // Couldn't read relPos
629             }           
630         }           
631         if (!a.value( "absPosX").isEmpty() ) 
632         {
633             if (!a.value( "absPosY").isEmpty() ) 
634             {
635                 x=a.value("absPosX").toFloat (&okx);
636                 y=a.value("absPosY").toFloat (&oky);
637                 if (okx && oky  )
638                     lastMI->setAbsPos (QPointF(x,y));
639                 else
640                     return false;   // Couldn't read absPos
641             }           
642         }           
643         if (!a.value( "url").isEmpty() ) 
644             lastMI->setURL (a.value ("url"));
645         if (!a.value( "vymLink").isEmpty() ) 
646             lastMI->setVymLink (a.value ("vymLink"));
647         if (!a.value( "hideInExport").isEmpty() ) 
648             if (a.value("hideInExport")=="true")
649                 lastMI->setHideInExport(true);
650
651         if (!a.value( "hideLink").isEmpty()) 
652         {
653             if (a.value ("hideLink") =="true")
654                 lastMI->setHideLinkUnselected(true);
655             else    
656                 lastMI->setHideLinkUnselected(false);
657         }   
658
659         if (!a.value( "localTarget").isEmpty() )
660             if (a.value ("localTarget")=="true")
661                 lastMI->toggleTarget();
662         if (!a.value( "rotation").isEmpty() ) 
663         {
664             x=a.value("rotation").toFloat (&okx);
665             if (okx )
666                 lastMI->setRotation (x);
667             else        
668                 return false;   // Couldn't read rotation
669         }           
670
671         if (!a.value( "uuid").isEmpty() )  
672         {
673             // While pasting, check for existing UUID
674             if (loadMode!=ImportAdd && !model->findUuid(a.value( "uuid")))
675                 lastMI->setUuid (a.value( "uuid") );
676         }
677     }
678     return true;    
679 }
680
681 bool parseVYMHandler::readNoteAttr (const QXmlAttributes& a)
682 {   // only for backward compatibility (<1.4.6). Use htmlnote now.
683     vymtext.clear();
684     QString fn;
685     if (!a.value( "href").isEmpty() ) 
686     {
687         // Load note
688         fn=parseHREF(a.value ("href") );
689         QFile file (fn);
690         QString s;                        // Reading a note
691
692         if ( !file.open( QIODevice::ReadOnly) )
693         {
694             qWarning ()<<"parseVYMHandler::readNoteAttr:  Couldn't load "+fn;
695             return false;
696         }   
697         QTextStream stream( &file );
698         stream.setCodec("UTF-8");
699         QString lines;
700         while ( !stream.atEnd() ) {
701             lines += stream.readLine()+"\n"; 
702         }
703         file.close();
704
705     lines ="<html><head><meta name=\"qrichtext\" content=\"1\" /></head><body>" + lines + "</p></body></html>";
706     vymtext.setText (lines);   // this probably should set type, too...
707     }            
708     if (!a.value( "fonthint").isEmpty() ) 
709         vymtext.setFontHint(a.value ("fonthint") );
710     lastBranch->setNote(vymtext);
711     return true;
712 }
713
714 bool parseVYMHandler::readImageAttr (const QXmlAttributes& a)
715 {
716     lastMI=lastImage;
717     
718     if (!readOOAttr(a)) return false;  
719
720     if (!a.value( "href").isEmpty() )
721     {
722         // Load Image
723         if (!lastImage->load (parseHREF(a.value ("href") ) ))
724         {
725             QMessageBox::warning( 0, "Warning: " ,
726                 "Couldn't load image\n"+parseHREF(a.value ("href") ));
727             lastImage=NULL;
728             return true;
729         }
730         
731     }        
732     if (!a.value( "zPlane").isEmpty() ) 
733         lastImage->setZValue (a.value("zPlane").toInt ());
734     float x,y;
735     bool okx,oky;
736     if (!a.value( "relPosX").isEmpty() ) 
737     {
738         if (!a.value( "relPosY").isEmpty() ) 
739         {
740             // read relPos
741             x=a.value("relPosX").toFloat (&okx);
742             y=a.value("relPosY").toFloat (&oky);
743             if (okx && oky) 
744                 lastImage->setRelPos (QPointF (x,y) );
745             else
746                 // Couldn't read relPos
747                 return false;  
748         }           
749     }        
750     
751     // Scale image        
752     x=y=1;
753     if (!a.value( "scaleX").isEmpty() ) 
754     {
755         x=a.value("scaleX").toFloat (&okx);
756         if (!okx ) return false;  
757     }        
758     
759     if (!a.value( "scaleY").isEmpty() ) 
760     {
761         y=a.value("scaleY").toFloat (&oky);
762         if (!oky ) return false;  
763     }        
764     if (x!=1 || y!=1)
765         lastImage->setScale (x,y);
766     
767     if (!readOOAttr(a)) return false;
768
769     if (!a.value ("originalName").isEmpty() )
770     {
771         lastImage->setOriginalFilename (a.value("originalName"));
772     }
773     return true;
774 }
775
776 bool parseVYMHandler::readXLinkAttr (const QXmlAttributes& a) 
777 {
778     // Obsolete, see also readLinkAttr
779
780     if (!a.value( "beginID").isEmpty() ) 
781     { 
782         if (!a.value( "endID").isEmpty() ) 
783         {
784             TreeItem *beginBI=model->findBySelectString (a.value( "beginID"));
785             TreeItem   *endBI=model->findBySelectString (a.value( "endID"));
786             if (beginBI && endBI && beginBI->isBranchLikeType() && endBI->isBranchLikeType() )
787             {
788                 Link *li=new Link (model);
789                 li->setBeginBranch ( (BranchItem*)beginBI );
790                 li->setEndBranch ( (BranchItem*)endBI);
791                 QPen pen=li->getPen();
792
793                 if (!a.value( "color").isEmpty() ) 
794                 {
795                     QColor col;
796                     col.setNamedColor(a.value("color"));
797                     pen.setColor (col);
798                 }
799
800                 if (!a.value( "width").isEmpty() ) 
801                 {
802                     bool okx;
803                     pen.setWidth(a.value ("width").toInt (&okx, 10));
804                 }
805                 model->createLink (li);
806             }
807         }           
808     }        
809     return true;    
810 }
811
812 bool parseVYMHandler::readLinkNewAttr (const QXmlAttributes& a)        
813 {
814     // object ID is used starting in version 1.8.76
815     // (before there was beginBranch and endBranch)
816     //
817     // Starting in 1.13.2 xlinks are no longer subitems of branches,
818     // but listed at the end of the data in a map. This makes handling 
819     // of links much safer and easier
820
821     if (!a.value( "beginID").isEmpty() ) 
822     { 
823         if (!a.value( "endID").isEmpty() ) 
824         {
825             TreeItem *beginBI=model->findBySelectString (a.value( "beginID"));
826             TreeItem   *endBI=model->findBySelectString (a.value( "endID"));
827             if (beginBI && endBI && beginBI->isBranchLikeType() && endBI->isBranchLikeType() )
828             {
829                 Link *li=new Link (model);
830                 li->setBeginBranch ( (BranchItem*)beginBI );
831                 li->setEndBranch ( (BranchItem*)endBI);
832
833                 model->createLink (li);
834
835                 bool okx;
836                 QPen pen=li->getPen();
837                 if (!a.value( "type").isEmpty() ) 
838                 {
839                     li->setLinkType (a.value( "type") );
840                 }
841                 if (!a.value( "color").isEmpty() ) 
842                 {
843                     QColor col;
844                     col.setNamedColor(a.value("color"));
845                     pen.setColor (col);
846                 }
847                 if (!a.value( "width").isEmpty() ) 
848                 {
849                     pen.setWidth(a.value ("width").toInt (&okx, 10));
850                 }
851                 if (!a.value( "penstyle").isEmpty() ) 
852                 {
853                     pen.setStyle( penStyle (a.value ("penstyle"), okx));
854                 }
855                 li->setPen (pen);
856
857                 if (!a.value( "styleBegin").isEmpty() ) 
858                     li->setStyleBegin( a.value( "styleBegin" ) );
859                 if (!a.value( "styleEnd").isEmpty() ) 
860                     li->setStyleEnd( a.value( "styleEnd" ) );
861
862
863                 XLinkObj *xlo=(XLinkObj*)(li->getMO() );
864                 if (xlo && !a.value( "c0").isEmpty() )
865                 {
866                     QPointF p=point(a.value("c0"),okx );
867                     if (okx) xlo->setC0 (p);
868                 }
869                 if (xlo && !a.value( "c1").isEmpty() )
870                 {
871                     QPointF p=point(a.value("c1"),okx );
872                     if (okx) xlo->setC1 (p);
873                 }
874             }
875         }           
876     }        
877     return true;    
878 }
879
880 bool parseVYMHandler::readSettingAttr (const QXmlAttributes& a)
881 {
882     if (!a.value( "key").isEmpty() ) 
883     {
884         lastSetting = a.value( "key" );
885         if (!a.value( "value").isEmpty() ) 
886             // Beginning with 2.5.0 value is stored as between tags,
887             // no  longer as attribute
888             settings.setLocalValue(model->getDestPath(), a.value ("key"), a.value ("value"));
889         else
890             return false;
891         
892     } else
893         return false;
894     
895     return true;
896 }
897
898 bool parseVYMHandler::readSlideAttr (const QXmlAttributes& a)
899 {
900     QStringList scriptlines;        // FIXME-3 needed for switching to inScript
901                                 // Most attributes are obsolete with inScript
902     if (!lastSlide) return false;
903     {
904         if (!a.value( "name").isEmpty() ) 
905             lastSlide->setName (a.value( "name" ) );
906         if (!a.value( "zoom").isEmpty() ) 
907         {
908             bool ok;
909             qreal z=a.value ("zoom").toDouble(&ok);
910             if (!ok) return false;
911             scriptlines.append( QString("setMapZoom(%1)").arg(z) );
912         }
913         if (!a.value( "rotation").isEmpty() ) 
914         {
915             bool ok;
916             qreal z=a.value ("rotation").toDouble(&ok);
917             if (!ok) return false;
918             scriptlines.append( QString("setMapRotation(%1)").arg(z) );
919         }
920         if (!a.value( "duration").isEmpty() ) 
921         {
922             bool ok;
923             int d=a.value ("duration").toInt(&ok);
924             if (!ok) return false;
925             scriptlines.append( QString("setMapAnimDuration(%1)").arg(d) );
926         }
927         if (!a.value( "curve").isEmpty() ) 
928         {
929             bool ok;
930             int i=a.value ("curve").toInt(&ok);
931             if (!ok ) return false;
932             if (i<0 || i>QEasingCurve::OutInBounce) return false;
933             scriptlines.append( QString("setMapAnimCurve(%1)").arg(i) );
934         }
935         if (!a.value( "mapitem").isEmpty() ) 
936         {
937             TreeItem *ti=model->findBySelectString ( a.value( "mapitem") );
938             if (!ti) return false;
939             scriptlines.append( QString("centerOnID(\"%1\")").arg(ti->getUuid().toString() ) );
940         }
941         if (!a.value( "inScript").isEmpty() ) 
942         {
943             lastSlide->setInScript( unquotemeta( a.value( "inScript") ) );
944         } else
945             lastSlide->setInScript( unquotemeta( scriptlines.join(";\n") ) );
946
947         if (!a.value( "outScript").isEmpty() ) 
948         {
949             lastSlide->setOutScript( unquotemeta( a.value( "outScript") ) );
950         }
951     }
952     return true;
953 }
954
955 bool parseVYMHandler::readTaskAttr (const QXmlAttributes& a)
956 {
957     if (!lastTask) return false;
958     {
959         if (!a.value( "status").isEmpty() ) 
960             lastTask->setStatus (a.value( "status" ) );
961         if (!a.value( "awake").isEmpty() ) 
962             lastTask->setAwake (a.value( "awake" ) );
963         if (!a.value( "date_creation").isEmpty() ) 
964             lastTask->setDateCreation ( a.value( "date_creation" ) );
965         if (!a.value( "date_modified").isEmpty() ) 
966             lastTask->setDateModified( a.value( "date_modified" ) );
967         if (!a.value( "date_sleep").isEmpty() ) 
968             lastTask->setDateSleep( a.value( "date_sleep" ) );
969     }
970     return true;
971 }
972