initial import
[vym] / xml-vym.cpp
1 #include "xml-vym.h"
2
3 #include <QMessageBox>
4 #include <QColor>
5 #include <QTextStream>
6 #include <iostream>
7 #include <typeinfo>
8
9 #include "misc.h"
10 #include "settings.h"
11 #include "linkablemapobj.h"
12 #include "version.h"
13
14 static BranchObj *lastBranch;
15 static FloatObj *lastFloat;
16 static OrnamentedObj *lastOO;
17
18 extern Settings settings;
19 extern QString vymVersion;
20
21 /*
22 parseVYMHandler::parseVYMHandler() {}
23
24 parseVYMHandler::~parseVYMHandler() {}
25
26 QString parseVYMHandler::errorProtocol() { return errorProt; }
27
28 */
29
30 bool parseVYMHandler::startDocument()
31 {
32     errorProt = "";
33     state = StateInit;
34     laststate = StateInit;
35         stateStack.clear();
36         stateStack.append(StateInit);
37         htmldata="";
38         isVymPart=false;
39     return true;
40 }
41
42
43 /*
44 QString parseVYMHandler::parseHREF(QString href)
45 {
46         QString type=href.section(":",0,0);
47         QString path=href.section(":",1,1);
48         if (!tmpDir.endsWith("/"))
49                 return tmpDir + "/" + path;
50         else    
51                 return tmpDir + path;
52 }
53 */
54 bool parseVYMHandler::startElement  ( const QString&, const QString&,
55                     const QString& eName, const QXmlAttributes& atts ) 
56 {
57     QColor col;
58         /* Testing
59         cout << "startElement <"<< qPrintable(eName)
60                 <<">  state="<<state 
61                 <<"  laststate="<<stateStack.last()
62                 <<"   loadMode="<<loadMode
63                 <<"       line="<<QXmlDefaultHandler::lineNumber()
64                 <<endl;
65         */      
66         stateStack.append (state);      
67     if ( state == StateInit && (eName == "vymmap")  ) 
68         {
69         state = StateMap;
70
71                 if (loadMode==NewMap)
72                         model->clear(); // remove existing mapCenter
73
74                 // Check version
75                 if (!atts.value( "version").isEmpty() ) 
76                 {
77                         if (!checkVersion(atts.value("version")))
78                                 QMessageBox::warning( 0, "Warning: Version Problem" ,
79                                    "<h3>Map is newer than VYM</h3>"
80                                    "<p>The map you are just trying to load was "
81                                    "saved using vym " +atts.value("version")+". "
82                                    "The version of this vym is " + vymVersion + 
83                                    ". If you run into problems after pressing "
84                                    "the ok-button below, updating vym should help.");
85                         else       
86                                 model->setVersion(atts.value( "version" ));
87
88                 }
89
90                 if (loadMode==NewMap )
91                 {
92                         // Create mapCenter
93                         model->clear();
94                         lastBranch=model->first();      // avoid empty pointer
95
96                         if (!atts.value( "author").isEmpty() )
97                                 model->setAuthor(atts.value( "author" ) );
98                         if (!atts.value( "comment").isEmpty() )
99                                 model->setComment (atts.value( "comment" ) );
100                         if (!atts.value( "backgroundColor").isEmpty() )
101                         {
102                                 col.setNamedColor(atts.value("backgroundColor"));
103                                 model->getScene()->setBackgroundBrush(col);
104                         }           
105                         if (!atts.value( "selectionColor").isEmpty() )
106                         {
107                                 col.setNamedColor(atts.value("selectionColor"));
108                                 model->getMapEditor()->setSelectionColor(col);
109                         }           
110                         if (!atts.value( "linkColorHint").isEmpty() ) 
111                         {
112                                 if (atts.value("linkColorHint")=="HeadingColor")
113                                         model->getMapEditor()->setMapLinkColorHint(LinkableMapObj::HeadingColor);
114                                 else
115                                         model->getMapEditor()->setMapLinkColorHint(LinkableMapObj::DefaultColor);
116                         }
117                         if (!atts.value( "linkStyle").isEmpty() ) 
118                                 model->getMapEditor()->setMapLinkStyle(atts.value("linkStyle"));
119                         if (!atts.value( "linkColor").isEmpty() ) 
120                         {
121                                 col.setNamedColor(atts.value("linkColor"));
122                                 model->getMapEditor()->setMapDefLinkColor(col);
123                         }       
124                         if (!atts.value( "defXLinkColor").isEmpty() ) 
125                         {
126                                 col.setNamedColor(atts.value("defXLinkColor"));
127                                 model->getMapEditor()->setMapDefXLinkColor(col);
128                         }       
129                         if (!atts.value( "defXLinkWidth").isEmpty() ) 
130                                 model->getMapEditor()->setMapDefXLinkWidth(atts.value("defXLinkWidth").toInt ());
131                 }       
132         } else if ( eName == "select" && state == StateMap ) 
133         {
134                 state=StateMapSelect;
135         } else if ( eName == "setting" && state == StateMap ) 
136         {
137                 state=StateMapSetting;
138                 if (loadMode==NewMap)
139                         readSettingAttr (atts);
140         } else if ( eName == "mapcenter" && state == StateMap ) 
141         {
142                 state=StateMapCenter;
143                 if (loadMode==NewMap)
144                 {       
145                         // Really use the found mapcenter as MCO in a new map
146
147                         lastBranch=model->addMapCenter(); 
148                 } else
149                 {
150                         // Treat the found mapcenter as a branch 
151                         // in an existing map
152                         LinkableMapObj* lmo=model->getSelection();
153                         if (lmo && ((typeid(*lmo) == typeid(BranchObj) ) 
154                                 ||  typeid(*lmo) == typeid(MapCenterObj) ) )
155                         {
156                                 lastBranch=(BranchObj*)lmo;
157                                 if (loadMode==ImportAdd)
158                                 {
159                                         lastBranch->addBranch();
160                                         lastBranch=lastBranch->getLastBranch();
161                                 } else
162                                         lastBranch->clear();
163                         } else
164                                 return false;
165                 }
166                 readBranchAttr (atts);
167         } else if ( 
168                 (eName == "standardflag" ||eName == "standardFlag") && 
169                 (state == StateMapCenter || state==StateBranch)) 
170         {
171                 state=StateStandardFlag;
172         } else if ( eName == "heading" && (state == StateMapCenter||state==StateBranch)) 
173         {
174                 laststate=state;
175                 state=StateHeading;
176                 if (!atts.value( "textColor").isEmpty() ) 
177                 {
178                         col.setNamedColor(atts.value("textColor"));
179                         lastBranch->setColor(col );
180                 }           
181         } else if ( eName == "note" && 
182                                 (state == StateMapCenter ||state==StateBranch))
183         {       // only for backward compatibility (<1.4.6). Use htmlnote now.
184                 state=StateNote;
185                 if (!readNoteAttr (atts) ) return false;
186         } else if ( eName == "htmlnote" && state == StateMapCenter) 
187         {
188                 laststate=state;
189                 state=StateHtmlNote;
190     } else if ( eName == "floatimage" && 
191                                 (state == StateMapCenter ||state==StateBranch)) 
192         {
193                 state=StateFloatImage;
194         lastBranch->addFloatImage();
195                 lastFloat=lastBranch->getLastFloatImage();
196                 if (!readFloatImageAttr(atts)) return false;
197         } else if ( (eName == "branch"||eName=="floatimage") && state == StateMap) 
198         {
199                 // This is used in vymparts, which have no mapcenter!
200                 isVymPart=true;
201                 LinkableMapObj* lmo=model->getSelection();
202                 if (!lmo)
203                 {
204                         // If a vym part is _loaded_ (not imported), 
205                         // selection==lmo==NULL
206                         // Treat it like ImportAdd then...
207                         loadMode=ImportAdd;
208                         lmo=model->first();             // FIXME this used to be lmo=mc before
209                 }       
210                 if (lmo && ((typeid(*lmo) == typeid(BranchObj) ) 
211                                 ||  typeid(*lmo) == typeid(MapCenterObj) ) )
212                 {
213                         lastBranch=(BranchObj*)(lmo);
214                         if (eName=="branch")
215                         {
216                                 state=StateBranch;
217                                 if (loadMode==ImportAdd)
218                                 {
219                                         lastBranch->addBranch();
220                                         lastBranch=lastBranch->getLastBranch();
221                                         
222                                 } else
223                                         lastBranch->clear();
224                                 readBranchAttr (atts);
225                         } else if (eName=="floatimage")
226                         {
227                                 state=StateFloatImage;
228                                 lastBranch->addFloatImage();
229                                 lastFloat=lastBranch->getLastFloatImage();
230                                 if (!readFloatImageAttr(atts)) return false;
231                         } else return false;
232                 } else return false;
233         } else if ( eName == "branch" && state == StateMapCenter) 
234         {
235                 state=StateBranch;
236                 lastBranch->addBranch();
237                 lastBranch=lastBranch->getLastBranch();
238                 readBranchAttr (atts);
239         } else if ( eName == "htmlnote" && state == StateBranch) 
240         {
241                 laststate=state;
242                 state=StateHtmlNote;
243                 no.clear();
244                 if (!atts.value( "fonthint").isEmpty() ) 
245                         no.setFontHint(atts.value ("fonthint") );
246         } else if ( eName == "frame" && (state == StateBranch||state==StateMapCenter)) 
247         {
248                 laststate=state;
249                 state=StateFrame;
250                 if (!readFrameAttr(atts)) return false;
251     } else if ( eName == "xlink" && state == StateBranch ) 
252         {
253                 state=StateBranchXLink;
254                 if (!readXLinkAttr (atts)) return false;
255     } else if ( eName == "branch" && state == StateBranch ) 
256         {
257         lastBranch->addBranch();
258                 lastBranch=lastBranch->getLastBranch();         
259                 readBranchAttr (atts);
260     } else if ( eName == "html" && state == StateHtmlNote ) 
261         {
262                 state=StateHtml;
263                 htmldata="<"+eName;
264                 readHtmlAttr(atts);
265                 htmldata+=">";
266     } else if ( state == StateHtml ) 
267         {
268                 // accept all while in html mode,
269                 htmldata+="<"+eName;
270                 readHtmlAttr(atts);
271                 htmldata+=">";
272     } else
273         return false;   // Error
274     return true;
275 }
276
277 bool parseVYMHandler::endElement  ( const QString&, const QString&, const QString &eName)
278 {
279         /* Testing
280         cout << "endElement </" <<qPrintable(eName)
281                 <<">  state=" <<state 
282                 <<"  laststate=" <<laststate
283                 <<"  stateStack="<<stateStack.last() 
284                 <<endl;
285         */
286     switch ( state ) 
287         {
288         case StateBranch: 
289                         lastBranch=(BranchObj*)(lastBranch->getParObj());
290             break;
291         case StateHtml: 
292                         htmldata+="</"+eName+">";
293                         if (eName=="html")
294                         {
295                                 state=StateHtmlNote;  
296                                 htmldata.replace ("<br></br>","<br />");
297                                 no.setNote (htmldata);
298                                 lastBranch->setNote (no);
299                         }       
300                         break;
301                 default: 
302                         break;
303     }  
304         state=stateStack.takeLast();    
305         return true;
306 }
307
308 bool parseVYMHandler::characters   ( const QString& ch)
309 {
310         //cout << "characters \""<<ch<<"\"  state="<<state <<"  laststate="<<laststate<<endl;
311
312         QString ch_org=quotemeta (ch);
313     QString ch_simplified=ch.simplifyWhiteSpace();
314     if ( ch_simplified.isEmpty() ) return true;
315
316     switch ( state ) 
317     {
318         case StateInit: break;
319         case StateMap: break; 
320                 case StateMapSelect:
321                         model->select(ch_simplified);
322                         break;
323                 case StateMapSetting:break;
324         case StateMapCenter: break;
325         case StateNote:
326                         lastBranch->setNote(ch_simplified);
327                         break;
328         case StateBranch: break;
329         case StateStandardFlag: 
330             lastBranch->activateStandardFlag(ch_simplified); 
331             break;
332         case StateFloatImage: break;
333         case StateHtmlNote: break;
334         case StateHtml:
335                         htmldata+=ch_org;
336                         break;
337         case StateHeading: 
338             lastBranch->setHeading(ch_simplified);
339             break;
340         default: 
341                         return false;
342     }
343     return true;
344 }
345
346 QString parseVYMHandler::errorString() 
347 {
348     return "the document is not in the VYM file format";
349 }
350
351 bool parseVYMHandler::readBranchAttr (const QXmlAttributes& a)
352 {
353         lastOO=lastBranch;
354         if (!readOOAttr(a)) return false;
355
356         if (!a.value( "scrolled").isEmpty() )
357                 lastBranch->toggleScroll();
358         if (!a.value( "frameType").isEmpty() ) 
359                 lastOO->setFrameType (a.value("frameType")); //Compatibility 1.8.1
360
361         if (!a.value( "incImgV").isEmpty() ) 
362         {       
363                 if (a.value("incImgV")=="true")
364                         lastBranch->setIncludeImagesVer(true);
365                 else    
366                         lastBranch->setIncludeImagesVer(false);
367         }       
368         if (!a.value( "incImgH").isEmpty() ) 
369         {       
370                 if (a.value("incImgH")=="true")
371                         lastBranch->setIncludeImagesHor(true);
372                 else    
373                         lastBranch->setIncludeImagesHor(false);
374         }       
375         return true;    
376 }
377
378 bool parseVYMHandler::readFrameAttr (const QXmlAttributes& a)
379 {
380         bool ok;
381         int x;
382         if (lastOO)
383         {
384                 if (!a.value( "frameType").isEmpty() ) 
385                         lastOO->setFrameType (a.value("frameType"));
386                 if (!a.value( "penColor").isEmpty() ) 
387                         lastOO->setFramePenColor (a.value("penColor"));
388                 if (!a.value( "brushColor").isEmpty() ) 
389                         lastOO->setFrameBrushColor (a.value("brushColor"));
390                 if (!a.value( "padding").isEmpty() ) 
391                 {
392                         x=a.value("padding").toInt(&ok);
393                         if (ok) lastOO->setFramePadding(x);
394                 }       
395                 if (!a.value( "borderWidth").isEmpty() ) 
396                 {
397                         x=a.value("borderWidth").toInt(&ok);
398                         if (ok) lastOO->setFrameBorderWidth(x);
399                 }       
400         }               
401         return true;
402 }
403
404 bool parseVYMHandler::readOOAttr (const QXmlAttributes& a)
405 {
406         if (lastOO)
407         {
408                 bool okx,oky;
409                 float x,y;
410                 if (!a.value( "relPosX").isEmpty() ) 
411                 {
412                         if (!a.value( "relPosY").isEmpty() ) 
413                         {
414                                 x=a.value("relPosX").toFloat (&okx);
415                                 y=a.value("relPosY").toFloat (&oky);
416                                 if (okx && oky  )
417                                 {
418                                         lastOO->setUseRelPos (true);
419                                         lastOO->move2RelPos (x,y);
420                                 }       
421                                 else
422                                         return false;   // Couldn't read relPos
423                         }           
424                 }           
425                 if (!a.value( "absPosX").isEmpty() && loadMode==NewMap ) 
426                 {
427                         if (!a.value( "absPosY").isEmpty() ) 
428                         {
429                                 x=a.value("absPosX").toFloat (&okx);
430                                 y=a.value("absPosY").toFloat (&oky);
431                                 if (okx && oky  )
432                                         lastOO->move(x,y);
433                                 else
434                                         return false;   // Couldn't read absPos
435                         }           
436                 }           
437                 if (!a.value( "id").isEmpty() ) 
438                         lastOO->setID (a.value ("id"));
439                 if (!a.value( "url").isEmpty() ) 
440                         lastOO->setURL (a.value ("url"));
441                 if (!a.value( "vymLink").isEmpty() ) 
442                         lastOO->setVymLink (a.value ("vymLink"));
443                 if (!a.value( "hideInExport").isEmpty() ) 
444                         if (a.value("hideInExport")=="true")
445                                 lastOO->setHideInExport(true);
446
447                 if (!a.value( "hideLink").isEmpty()) 
448                 {
449                         if (a.value ("hideLink") =="true")
450                                 lastOO->setHideLinkUnselected(true);
451                         else    
452                                 lastOO->setHideLinkUnselected(false);
453                 }       
454         }
455         return true;    
456 }
457
458 bool parseVYMHandler::readNoteAttr (const QXmlAttributes& a)
459 {       // only for backward compatibility (<1.4.6). Use htmlnote now.
460         no.clear();
461         QString fn;
462         if (!a.value( "href").isEmpty() ) 
463         {
464                 // Load note
465                 fn=parseHREF(a.value ("href") );
466                 QFile file (fn);
467                 QString s;                                              // Reading a note
468
469                 if ( !file.open( QIODevice::ReadOnly) )
470                 {
471                         qWarning ("parseVYMHandler::readNoteAttr:  Couldn't load "+fn);
472                         return false;
473                 }       
474                 QTextStream stream( &file );
475                 QString lines;
476                 while ( !stream.atEnd() ) {
477                         lines += stream.readLine()+"\n"; 
478                 }
479                 file.close();
480
481                 lines ="<html><head><meta name=\"qrichtext\" content=\"1\" /></head><body>"+lines + "</p></body></html>";
482                 no.setNote (lines);
483         }               
484         if (!a.value( "fonthint").isEmpty() ) 
485                 no.setFontHint(a.value ("fonthint") );
486         lastBranch->setNote(no);
487         return true;
488 }
489
490 bool parseVYMHandler::readFloatImageAttr (const QXmlAttributes& a)
491 {
492         lastOO=lastFloat;
493         
494         //if (!readOOAttr(a)) return false;
495
496         if (!a.value( "useOrientation").isEmpty() ) 
497         {
498                 if (a.value ("useOrientation") =="true")
499                         lastFloat->setUseOrientation (true);
500                 else    
501                         lastFloat->setUseOrientation (false);
502         }       
503         if (!a.value( "href").isEmpty() )
504         {
505                 // Load FloatImage
506                 if (!lastFloat->load (parseHREF(a.value ("href") ) ))
507                 {
508                         QMessageBox::warning( 0, "Warning: " ,
509                                 "Couldn't load float image\n"+parseHREF(a.value ("href") ));
510                         lastBranch->removeFloatImage(((FloatImageObj*)(lastFloat)));
511                         lastFloat=NULL;
512                         return true;
513                 }
514                 
515         }       
516         if (!a.value( "floatExport").isEmpty() ) 
517         {
518                 // Only for compatibility. THis is not used since 1.7.11 
519                 if (a.value ("floatExport") =="true")
520                         lastFloat->setFloatExport(true);
521                 else    
522                         lastFloat->setFloatExport (false);
523         }       
524         if (!a.value( "zPlane").isEmpty() ) 
525                 lastFloat->setZValue (a.value("zPlane").toInt ());
526     float x,y;
527     bool okx,oky;
528         if (!a.value( "relPosX").isEmpty() ) 
529         {
530                 if (!a.value( "relPosY").isEmpty() ) 
531                 {
532                         // read relPos
533                         x=a.value("relPosX").toFloat (&okx);
534                         y=a.value("relPosY").toFloat (&oky);
535                         if (okx && oky) 
536                                 
537                                 {
538                                         lastFloat->setRelPos (QPointF (x,y) );
539                                         // make sure floats in mapcenter are repositioned to relative pos
540                                         if (lastBranch->getDepth()==0) lastBranch->positionContents();
541                                 }
542                         else
543                                 // Couldn't read relPos
544                                 return false;  
545                 }           
546         }       
547         
548         if (!readOOAttr(a)) return false;
549
550         if (!a.value ("orgName").isEmpty() )
551         {
552                 ((FloatImageObj*)(lastFloat))->setOriginalFilename (a.value("orgName"));
553         }
554         return true;
555 }
556
557 bool parseVYMHandler::readXLinkAttr (const QXmlAttributes& a)
558 {
559         QColor col;
560         bool okx;
561         bool success=false;
562         XLinkObj *xlo=new XLinkObj (model->getScene());
563         if (!a.value( "color").isEmpty() ) 
564         {
565                 col.setNamedColor(a.value("color"));
566                 xlo->setColor (col);
567         }
568
569         if (!a.value( "width").isEmpty() ) 
570         {
571                 xlo->setWidth(a.value ("width").toInt (&okx, 10));
572         }
573
574         // Connecting by select string for compatibility with version < 1.8.76
575         if (!a.value( "beginBranch").isEmpty() ) 
576         { 
577                 if (!a.value( "endBranch").isEmpty() ) 
578                 {
579                         LinkableMapObj *lmo=model->findObjBySelect (a.value( "beginBranch"));
580                         if (lmo && typeid (*lmo)==typeid (BranchObj))
581                         {
582                                 xlo->setBegin ((BranchObj*)lmo);
583                                 lmo=model->findObjBySelect (a.value( "endBranch"));
584                                 if (lmo && typeid (*lmo)==typeid (BranchObj))
585                                 {
586                                         xlo->setEnd ((BranchObj*)(lmo));
587                                         xlo->activate();
588                                         success=true;
589                                 }
590                         }
591                 }           
592         }       
593
594         // object ID is used starting in version 1.8.76
595         if (!a.value( "beginID").isEmpty() ) 
596         { 
597                 if (!a.value( "endID").isEmpty() ) 
598                 {
599                         LinkableMapObj *lmo=model->findID (a.value( "beginID"));
600                         if (lmo && typeid (*lmo)==typeid (BranchObj))
601                         {
602                                 xlo->setBegin ((BranchObj*)lmo);
603                                 lmo=model->findID (a.value( "endID"));
604                                 if (lmo && typeid (*lmo)==typeid (BranchObj))
605                                 {
606                                         xlo->setEnd ((BranchObj*)(lmo));
607                                         xlo->activate();
608                                         success=true;
609                                 }
610                         }
611                 }           
612         }       
613         if (!success) delete (xlo);
614         return true;    // xLinks can only be established at the "end branch", return true
615 }
616
617 bool parseVYMHandler::readHtmlAttr (const QXmlAttributes& a)
618 {
619         for (int i=1; i<=a.count(); i++)
620                 htmldata+=" "+a.localName(i-1)+"=\""+a.value(i-1)+"\"";
621         return true;
622 }
623
624 bool parseVYMHandler::readSettingAttr (const QXmlAttributes& a)
625 {
626         if (!a.value( "key").isEmpty() ) 
627         {
628                 if (!a.value( "value").isEmpty() ) 
629                         settings.setLocalEntry (model->getMapEditor()->getDestPath(), a.value ("key"), a.value ("value"));
630                 else
631                         return false;
632                 
633         } else
634                 return false;
635         
636         return true;
637 }