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