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