linkablemapobj.cpp
author insilmaril
Mon, 30 Jul 2007 09:47:15 +0000
changeset 577 6f090a2b10e3
parent 531 be24af55da40
child 579 1c487b3dd40f
permissions -rw-r--r--
1.9.4 New base class for XML based file parsing (vym & Freemind)
     1 #include <math.h>
     2 
     3 #include "linkablemapobj.h"
     4 #include "branchobj.h"
     5 #include "mapeditor.h"
     6 
     7 /////////////////////////////////////////////////////////////////
     8 // LinkableMapObj
     9 /////////////////////////////////////////////////////////////////
    10 
    11 LinkableMapObj::LinkableMapObj():MapObj()
    12 {
    13   //  cout << "Const LinkableMapObj ()\n";
    14     init ();
    15 }
    16 
    17 LinkableMapObj::LinkableMapObj(QGraphicsScene* s) :MapObj(s)
    18 {
    19 //    cout << "Const LinkableMapObj (s)\n";
    20     init ();
    21 }
    22 
    23 LinkableMapObj::LinkableMapObj (LinkableMapObj* lmo) : MapObj (lmo->scene)
    24 {
    25     copy (lmo);
    26 }
    27 
    28 LinkableMapObj::~LinkableMapObj()
    29 {
    30     delete (bottomline);
    31 	delLink();
    32 }
    33 
    34 void LinkableMapObj::delLink()
    35 {
    36 	switch (style)
    37 	{
    38 		case Line:
    39 			delete (l);
    40 			break;
    41 		case Parabel:
    42 			while (!segment.isEmpty()) delete segment.takeFirst();
    43 			break;
    44 		case PolyLine:
    45 			delete (p);
    46 			break;
    47 		case PolyParabel:
    48 			delete (p);
    49 			break;
    50 		default:
    51 			break;
    52 	}		
    53 }
    54 
    55 void LinkableMapObj::init ()
    56 {
    57     depth=-1;	
    58 	mapEditor=NULL;
    59     childObj=NULL;
    60     parObj=NULL;
    61     parObjTmpBuf=NULL;
    62     parPos=QPointF(0,0);
    63     childPos=QPointF(0,0);
    64 	link2ParPos=false;
    65     l=NULL;
    66     orientation=UndefinedOrientation;
    67     linkwidth=20;		
    68 	thickness_start=8;
    69     style=UndefinedStyle;
    70 	linkpos=Bottom;
    71     arcsegs=13;
    72     
    73 // TODO instead of linkcolor pen.color() could be used	all around
    74 	pen.setWidth (1);
    75 	pen.setColor (linkcolor);
    76 	pen.setCapStyle ( Qt::RoundCap );
    77 	bottomline=scene->addLine(QLineF(1,1,1,1),pen);
    78     bottomline->setZValue(Z_LINK);
    79     bottomline->show();
    80 
    81     // Prepare showing the selection of a MapObj
    82     selected=false;
    83 
    84 	hideLinkUnselected=false;
    85 
    86 	topPad=botPad=leftPad=rightPad=0;
    87 
    88 	repositionRequest=false;
    89 
    90 	// Rel Positions
    91 	relPos=QPointF(0,0);
    92 	useRelPos=false;
    93 	useOrientation=true;
    94 
    95 	// Reset ID
    96 	id="";
    97 }
    98 
    99 void LinkableMapObj::copy (LinkableMapObj* other)
   100 {
   101     MapObj::copy(other);
   102 	bboxTotal=other->bboxTotal;
   103     setLinkStyle(other->style);
   104     setLinkColor (other->linkcolor);
   105 	relPos=other->relPos;
   106 	useOrientation=other->useOrientation;
   107 
   108 }
   109 
   110 void LinkableMapObj::setChildObj(LinkableMapObj* o)
   111 {
   112     childObj=o;
   113 }
   114 
   115 void LinkableMapObj::setParObj(LinkableMapObj* o)
   116 {
   117     parObj=o;
   118 	mapEditor=parObj->getMapEditor();
   119 }
   120 
   121 void LinkableMapObj::setParObjTmp(LinkableMapObj*,QPointF,int)
   122 {
   123 }
   124 
   125 void LinkableMapObj::unsetParObjTmp()
   126 {
   127 }
   128 
   129 bool LinkableMapObj::hasParObjTmp()
   130 {
   131 	if (parObjTmpBuf) return true;
   132 	return false;
   133 }
   134 
   135 void LinkableMapObj::setUseRelPos (const bool &b)
   136 {
   137 	useRelPos=b;
   138 }
   139 
   140 void LinkableMapObj::setRelPos()
   141 {
   142 	if (parObj)
   143 	{	
   144 		relPos.setX (absPos.x() - parObj->getChildPos().x() );
   145 		relPos.setY (absPos.y() - parObj->getChildPos().y() );
   146 		parObj->calcBBoxSize();
   147 	}	
   148 }
   149 
   150 void LinkableMapObj::setRelPos(const QPointF &p)
   151 {
   152 	relPos=p;
   153 	if (parObj)
   154 	{		
   155 		parObj->calcBBoxSize();
   156 		requestReposition();
   157 	}
   158 }
   159 
   160 QPointF LinkableMapObj::getRelPos()
   161 {
   162 	if (!parObj) return QPointF();
   163 	return relPos;
   164 }
   165 
   166 qreal LinkableMapObj::getTopPad()
   167 {
   168 	return topPad;
   169 }
   170 
   171 qreal LinkableMapObj::getLeftPad()
   172 {
   173 	return leftPad;
   174 }
   175 
   176 qreal LinkableMapObj::getRightPad()
   177 {
   178 	return rightPad;
   179 }
   180 
   181 LinkableMapObj::Style LinkableMapObj::getDefLinkStyle ()
   182 {
   183 	if (mapEditor==0) return UndefinedStyle;
   184 	Style ls=mapEditor->getMapLinkStyle();
   185 	switch (ls)
   186 	{
   187 		case Line: 
   188 			return ls;
   189 			break;
   190 		case Parabel:
   191 			return ls;
   192 			break;
   193 		case PolyLine:	
   194 			if (depth>1)
   195 				return Line;
   196 			else	
   197 				return ls;
   198 			break;
   199 		case PolyParabel:	
   200 			if (depth>1)
   201 				return Parabel;
   202 			else	
   203 				return ls;
   204 			break;
   205 		default: 
   206 			break;	
   207 	}	
   208 	return UndefinedStyle;
   209 }
   210 
   211 void LinkableMapObj::setLinkStyle(Style newstyle)
   212 {
   213 	//if (newstyle=style) return;
   214 	delLink();
   215 		
   216 	style=newstyle;
   217 
   218     if (childObj!=NULL && parObj != NULL)
   219     {
   220 		QGraphicsLineItem *cl;
   221 		switch (style)
   222 		{
   223 			case UndefinedStyle:
   224 				bottomline->hide();
   225 				break;
   226 			case Line: 
   227 				l = scene->addLine(QLineF(1,1,1,1),pen);
   228 				l->setZValue(Z_LINK);
   229 				if (visible)
   230 					l->show();
   231 				else
   232 					l->hide();
   233 				break;
   234 			case Parabel:
   235 				for (int i=0;i<arcsegs;i++)
   236 				{
   237 					cl = scene->addLine(QLineF(i*5,0,i*10,100),pen);
   238 					cl->setZValue(Z_LINK);
   239 					if (visible)
   240 						cl->show();
   241 					else
   242 						cl->hide();
   243 					segment.append(cl);
   244 				}
   245 				pa0.resize (arcsegs+1);
   246 				break;
   247 			case PolyLine:	
   248 				p =scene->addPolygon(QPolygonF(),pen,linkcolor);
   249 				p->setZValue(Z_LINK);
   250 				if (visible)
   251 					p->show();
   252 				else
   253 					p->hide();
   254 				pa0.resize (3);
   255 				break;
   256 			case PolyParabel:	
   257 				p = scene->addPolygon(QPolygonF(),pen,linkcolor);
   258 				p->setZValue(Z_LINK);
   259 				if (visible)
   260 					p->show();
   261 				else
   262 					p->hide();
   263 				pa0.resize (arcsegs*2+2);
   264 				pa1.resize (arcsegs+1);
   265 				pa2.resize (arcsegs+1);
   266 				break;
   267 			default: 
   268 				break;	
   269 		}	
   270 	} 
   271 }
   272 
   273 LinkableMapObj::Style LinkableMapObj::getLinkStyle()
   274 {
   275 	return style;
   276 }
   277 
   278 void LinkableMapObj::setHideLinkUnselected(bool b)
   279 {
   280 	hideLinkUnselected=b;
   281 	setVisibility (visible);
   282 	updateLink();
   283 }
   284 
   285 bool LinkableMapObj::getHideLinkUnselected()
   286 {
   287 	return hideLinkUnselected;
   288 }
   289 
   290 void LinkableMapObj::setLinkPos(Position lp)
   291 {
   292 	linkpos=lp;
   293 }
   294 
   295 LinkableMapObj::Position LinkableMapObj::getLinkPos()
   296 {
   297 	return linkpos;
   298 }
   299 
   300 void LinkableMapObj::setID (const QString &s)
   301 {
   302 	id=s;
   303 }
   304 
   305 QString LinkableMapObj::getID()
   306 {
   307 	return id;
   308 }
   309 
   310 void LinkableMapObj::setLinkColor()
   311 {
   312 	// Overloaded in BranchObj and childs
   313 	// here only set default color
   314 	if (mapEditor)
   315 		setLinkColor (mapEditor->getMapDefLinkColor());
   316 }
   317 
   318 void LinkableMapObj::setLinkColor(QColor col)
   319 {
   320 	linkcolor=col;
   321 	pen.setColor(col);
   322     bottomline->setPen( pen );
   323 	switch (style)
   324 	{
   325 		case Line:
   326 			l->setPen( pen);
   327 			break;	
   328 		case Parabel:	
   329 			for (int i=0; i<segment.size(); ++i)
   330 				segment.at(i)->setPen( pen);
   331 			break;
   332 		case PolyLine:
   333 			p->setBrush( QBrush(col));
   334 			p->setPen( pen);
   335 			break;
   336 		case PolyParabel:	
   337 			p->setBrush( QBrush(col));
   338 			p->setPen( pen);
   339 			break;
   340 		default:
   341 			break;
   342 	} // switch (style)	
   343 }
   344 
   345 QColor LinkableMapObj::getLinkColor()
   346 {
   347 	return linkcolor;
   348 }
   349 
   350 void LinkableMapObj::setVisibility (bool v)
   351 {
   352 	MapObj::setVisibility (v);
   353 	bool visnow=visible;
   354 
   355 	// We can hide the link, while object is not selected
   356 	if (hideLinkUnselected && !selected)
   357 		visnow=false;
   358 
   359 	if (visnow) 
   360 	{
   361 		bottomline->show();
   362 		switch (style)
   363 		{
   364 			case Line:
   365 				if (l) l->show();
   366 				break;
   367 			case Parabel:	
   368 				for (int i=0; i<segment.size(); ++i)
   369 					segment.at(i)->show();
   370 				break;	
   371 			case PolyLine:
   372 				if (p) p->show();
   373 				break;
   374 			case PolyParabel:	
   375 				if (p) p->show();
   376 				break;
   377 			default:
   378 				break;
   379 		}
   380 	} else 
   381 	{
   382 		bottomline->hide();
   383 		switch (style)
   384 		{
   385 			case Line:
   386 				if (l) l->hide();
   387 				break;
   388 			case Parabel:	
   389 				for (int i=0; i<segment.size(); ++i)
   390 					segment.at(i)->hide();
   391 				break;	
   392 			case PolyLine:
   393 				if (p) p->hide();
   394 				break;
   395 			case PolyParabel:	
   396 				if (p) p->hide();
   397 				break;
   398 			default:
   399 				break;
   400 		}
   401 	}	
   402 }
   403 
   404 void LinkableMapObj::setOrientation()
   405 {
   406 	Orientation orientOld=orientation;
   407 
   408 	if (!parObj) 
   409 	{
   410 		orientation=UndefinedOrientation;
   411 		return;
   412 	}
   413 		
   414     // Set orientation, first look for orientation of parent
   415     if (parObj->getOrientation() != UndefinedOrientation ) 
   416 		// use the orientation of the parent:
   417 		orientation=parObj->getOrientation();
   418     else
   419     {
   420 		// calc orientation depending on position rel to parent
   421 		if (absPos.x() < QPointF(parObj->getChildPos() ).x() )
   422 			orientation=LeftOfCenter; 
   423 		else
   424 			orientation=RightOfCenter;
   425     }
   426 	if (orientOld!=orientation) requestReposition();
   427 }
   428 
   429 void LinkableMapObj::updateLink()
   430 {
   431     // needs:
   432     //	childPos of parent
   433     //	orient   of parent
   434     //	style
   435     // 
   436     // sets:
   437     //	orientation
   438     //	childPos	(by calling setDockPos())
   439     //	parPos		(by calling setDockPos())
   440 	//  bottomlineY
   441     //	drawing of the link itself
   442 
   443 	// updateLink is called from move, but called from constructor we don't
   444 	// have parents yet...
   445 	if (style==UndefinedStyle) return;	
   446 
   447 	switch (linkpos)
   448 	{
   449 		case Middle:
   450 			bottomlineY=bbox.top() + bbox.height()/2;	// draw link to middle (of frame)
   451 			break;
   452 		case Bottom:
   453 			bottomlineY=bbox.bottom()-1;	// draw link to bottom of box
   454 			break;
   455 	}
   456 	
   457     double p2x,p2y;								// Set P2 Before setting
   458 	if (!link2ParPos)
   459 	{
   460 		p2x=QPointF( parObj->getChildPos() ).x();	// P1, we have to look at
   461 		p2y=QPointF( parObj->getChildPos() ).y();	// orientation
   462 	} else	
   463 	{
   464 		p2x=QPointF( parObj->getParPos() ).x();	
   465 		p2y=QPointF( parObj->getParPos() ).y();
   466 	} 
   467 
   468 	setDockPos(); // Call overloaded method
   469 	setOrientation();
   470 
   471 	double p1x=parPos.x();	// Link is drawn from P1 to P2
   472 	double p1y=parPos.y();
   473 
   474 	double vx=p2x - p1x;	// V=P2-P1
   475 	double vy=p2y - p1y;
   476 
   477 	// Draw the horizontal line below heading (from ChildPos to ParPos)
   478 	//bottomline->prepareGeometryChange();
   479 	bottomline->setLine (QLine (qRound(childPos.x()),
   480 		qRound(childPos.y()),
   481 		qRound(p1x),
   482 		qRound(p1y) ));
   483 
   484 	double a;	// angle
   485 	if (vx > -0.000001 && vx < 0.000001)
   486 		a=M_PI_2;
   487 	else
   488 		a=atan( vy / vx );
   489 	// "turning point" for drawing polygonal links
   490 	QPointF tp (-qRound(sin (a)*thickness_start), qRound(cos (a)*thickness_start));	
   491 	
   492     // Draw the link
   493 	switch (style)
   494 	{
   495 		case Line:
   496 			//l->prepareGeometryChange();
   497 			l->setLine( QLine(qRound (parPos.x()),
   498 				qRound(parPos.y()),
   499 				qRound(p2x),
   500 				qRound(p2y) ));
   501 			break;	
   502 		case Parabel:	
   503 			parabel (pa0, p1x,p1y,p2x,p2y);
   504 			for (int i=0; i<segment.size(); ++i)
   505 			{
   506 				//segment.at(i)->prepareGeometryChange();
   507 				segment.at(i)->setLine(QLineF( pa0.at(i).x(), pa0.at(i).y(),pa0.at(i+1).x(),pa0.at(i+1).y()));
   508 			}	
   509 			break;
   510 		case PolyLine:
   511 			pa0.clear();
   512 			pa0<<QPointF (qRound(p2x+tp.x()), qRound(p2y+tp.y()));
   513 			pa0<<QPointF (qRound(p2x-tp.x()), qRound(p2y-tp.y()));
   514 			pa0<<QPointF (qRound (parPos.x()), qRound(parPos.y()) );
   515 			//p->prepareGeometryChange();
   516 			p->setPolygon(QPolygonF (pa0));
   517 			break;
   518 		case PolyParabel:	
   519 			parabel (pa1, p1x,p1y,p2x+tp.x(),p2y+tp.y());
   520 			parabel (pa2, p1x,p1y,p2x-tp.x(),p2y-tp.y());
   521 			pa0.clear();
   522 			for (int i=0;i<=arcsegs;i++)
   523 				pa0 << QPointF (pa1.at(i));
   524 			for (int i=0;i<=arcsegs;i++)
   525 				pa0 << QPointF (pa2.at(arcsegs-i));
   526 			//p->prepareGeometryChange();
   527 			p->setPolygon(QPolygonF (pa0));
   528 			break;
   529 		default:
   530 			break;
   531 	} // switch (style)	
   532 }
   533 	
   534 LinkableMapObj* LinkableMapObj::getChildObj()
   535 {
   536     return childObj;
   537 }
   538 
   539 LinkableMapObj* LinkableMapObj::getParObj()
   540 {
   541     return parObj;
   542 }
   543 
   544 LinkableMapObj* LinkableMapObj::findObjBySelect (QString s)
   545 {
   546 	LinkableMapObj *lmo=this;
   547 	QString part;
   548 	QString typ;
   549 	QString num;
   550 	while (!s.isEmpty() )
   551 	{
   552 		part=s.section(",",0,0);
   553 		typ=part.left (3);
   554 		num=part.right(part.length() - 3);
   555 		if (typ=="mc:")
   556 		{
   557 			if (depth>0)
   558 				return false;	// in a subtree there is no center
   559 			else
   560 				break;
   561 		} else
   562 			if (typ=="bo:")
   563 				lmo=((BranchObj*)(lmo))->getBranchNum (num.toInt());
   564 			else
   565 				if (typ=="fi:")
   566 					lmo=((BranchObj*)(lmo))->getFloatImageNum (num.toUInt());
   567 		if (!lmo) break;
   568 		
   569 		if (s.contains(","))
   570 			s=s.right(s.length() - part.length() -1 );
   571 		else	
   572 			break;
   573 	}
   574 	return lmo;
   575 }
   576 
   577 QPointF LinkableMapObj::getChildPos()
   578 {
   579     return childPos;
   580 }
   581 
   582 QPointF LinkableMapObj::getParPos()
   583 {
   584     return parPos;
   585 }
   586 
   587 void LinkableMapObj::setUseOrientation (const bool &b)
   588 {	
   589 	if (useOrientation!=b)
   590 	{
   591 		useOrientation=b;
   592 		requestReposition();
   593 	}	
   594 }
   595 
   596 LinkableMapObj::Orientation LinkableMapObj::getOrientation()
   597 {
   598     return orientation;
   599 }
   600 
   601 int LinkableMapObj::getDepth()
   602 {
   603     return depth;
   604 }
   605 
   606 void LinkableMapObj::setMapEditor (MapEditor *me)
   607 {
   608 	mapEditor=me;
   609 }
   610 
   611 MapEditor* LinkableMapObj::getMapEditor ()
   612 {
   613 	return mapEditor;
   614 }
   615 
   616 QPointF LinkableMapObj::getRandPos()
   617 {
   618 	// Choose a random position with given distance to parent:
   619 	double a=rand()%360 * 2 * M_PI / 360;
   620     return QPointF ( (int)( + 150*cos (a)),
   621                     (int)( + 150*sin (a)));
   622 }
   623 
   624 void LinkableMapObj::reposition()
   625 {
   626 }
   627 
   628 void LinkableMapObj::requestReposition()
   629 {
   630 	if (!repositionRequest)
   631 	{
   632 		// Pass on the request to parental objects, if this hasn't
   633 		// been done yet
   634 		repositionRequest=true;
   635 		if (parObj) parObj->requestReposition();
   636 	}
   637 }
   638 
   639 void LinkableMapObj::forceReposition()
   640 {
   641 	// Sometimes a reposition has to be done immediatly: For example
   642 	// if the note editor flag changes, there is no user event in mapeditor
   643 	// which could collect requests for a reposition.
   644 	// Then we have to call forceReposition()
   645 	// But no rule without exception: While loading a map or undoing it,
   646 	// we want to block expensive repositioning, but just do it once at
   647 	// the end, thus check first:
   648 
   649 	if (mapEditor->isRepositionBlocked()) return;
   650 	
   651 	// Pass on the request to parental objects, if this hasn't been done yet
   652 	
   653 	if (parObj) 
   654 		parObj->forceReposition(); 
   655 	else 
   656 		reposition(); 
   657 }
   658 
   659 bool LinkableMapObj::repositionRequested()
   660 {
   661 	return repositionRequest;
   662 }
   663 
   664 
   665 void LinkableMapObj::select()
   666 {
   667 	// select and unselect are still needed to
   668 	// handle hiding of links
   669     selected=true;
   670 	setVisibility (visible);
   671 }
   672 
   673 
   674 void LinkableMapObj::unselect()
   675 {
   676     selected=false;
   677 	// Maybe we have to hide the link:
   678 	setVisibility (visible);
   679 }
   680 
   681 void LinkableMapObj::parabel (QPolygonF &ya, double p1x, double p1y, double p2x, double p2y)
   682 
   683 {
   684 	double vx=p2x - p1x;	// V=P2-P1
   685 	double vy=p2y - p1y;
   686 
   687 	double dx;				// delta x during calculation of parabel
   688 	
   689 	double pnx;				// next point
   690 	double pny;
   691 	double m;
   692 
   693 	if (vx > -0.0001 && vx < 0.0001)
   694 		m=0;
   695 	else	
   696 		m=(vy / (vx*vx));
   697 	dx=vx/(arcsegs);
   698 	ya.clear();
   699 	ya<<QPointF (qRound(p1x),qRound(p1y));
   700 	for (int i=1;i<=arcsegs;i++)
   701 	{	
   702 		pnx=p1x+dx;
   703 		pny=m*(pnx-parPos.x())*(pnx-parPos.x())+parPos.y();
   704 		ya<<QPointF (qRound(pnx),qRound(pny));
   705 		p1x=pnx;
   706 		p1y=pny;
   707 	}	
   708 }
   709 
   710 QString LinkableMapObj::getLinkAttr ()
   711 {
   712 	if (hideLinkUnselected)
   713 		return attribut ("hideLink","true");
   714 	else
   715 		return attribut ("hideLink","false");
   716 	
   717 }
   718