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