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