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