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