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