linkablemapobj.cpp
author insilmaril
Tue, 08 Feb 2005 14:07:19 +0000
changeset 89 9db3eaa21237
parent 83 e90f5bef70c8
child 93 31c6ce8efbc7
permissions -rw-r--r--
added contextmenu to follow links
     1 //#include <math.h>
     2 
     3 #include "linkablemapobj.h"
     4 #include "branchobj.h"
     5 #include "mapeditor.h"
     6 
     7 #include "version.h"
     8 
     9 
    10 /////////////////////////////////////////////////////////////////
    11 // LinkableMapObj
    12 /////////////////////////////////////////////////////////////////
    13 
    14 LinkableMapObj::LinkableMapObj():MapObj()
    15 {
    16   //  cout << "Const LinkableMapObj ()\n";
    17     init ();
    18 }
    19 
    20 LinkableMapObj::LinkableMapObj(QCanvas* c) :MapObj(c)
    21 {
    22 //    cout << "Const LinkableMapObj\n";
    23     init ();
    24 }
    25 
    26 LinkableMapObj::LinkableMapObj (LinkableMapObj* lmo) : MapObj (lmo->canvas)
    27 {
    28     copy (lmo);
    29 }
    30 
    31 LinkableMapObj::~LinkableMapObj()
    32 {
    33     delete (bottomline);
    34     delete (selbox);
    35 	delete (frame);
    36 	delLink();
    37 }
    38 
    39 void LinkableMapObj::delLink()
    40 {
    41 	switch (style)
    42 	{
    43 		case StyleLine:
    44 			delete (l);
    45 			break;
    46 		case StyleParabel:
    47 			segment.clear();
    48 			break;
    49 		case StylePolyLine:
    50 			delete (p);
    51 			delete (l);
    52 			break;
    53 		case StylePolyParabel:
    54 			delete (p);
    55 			segment.clear();
    56 			break;
    57 		default:
    58 			break;
    59 	}		
    60 }
    61 
    62 void LinkableMapObj::init ()
    63 {
    64     depth=-1;	
    65     childObj=NULL;
    66     parObj=NULL;
    67     parObjTmpBuf=NULL;
    68     parPos=QPoint(0,0);
    69     childPos=QPoint(0,0);
    70 	link2ParPos=false;
    71     l=NULL;
    72     orientation=OrientUndef;
    73     linkwidth=20;		
    74 	thickness_start=8;
    75     style=StyleUndef;
    76 	linkpos=LinkBottom;
    77     segment.setAutoDelete (TRUE);
    78     arcsegs=13;
    79 	QPointArray pa(arcsegs*2+2);
    80     
    81     bottomline=new QCanvasLine(canvas);
    82     bottomline->setPen( QPen(linkcolor, 1) );
    83     bottomline->setZ(Z_LINK);
    84     bottomline->show();
    85 
    86     // Prepare showing the selection of a MapObj
    87     selbox = new QCanvasRectangle (canvas);
    88     selbox->setZ(Z_SELBOX);
    89     selbox->setBrush( QColor(255,255,0) );
    90     selbox->setPen( QPen(QColor(255,255,0) ));
    91     selbox->hide();
    92     selected=false;
    93 
    94 	// initialize frame
    95 	frame = new FrameObj (canvas);
    96 	
    97 	repositionRequest=false;
    98 }
    99 
   100 void LinkableMapObj::copy (LinkableMapObj* other)
   101 {
   102     MapObj::copy(other);
   103 	bboxTotal=other->bboxTotal;
   104 //    linkwidth=other->linkwidth;		
   105 
   106     setLinkStyle(other->style);
   107     setLinkColor (other->linkcolor);
   108 }
   109 
   110 void LinkableMapObj::setChildObj(LinkableMapObj* o)
   111 {
   112     childObj=o;
   113 }
   114 
   115 void LinkableMapObj::setParObj(LinkableMapObj* o)
   116 {
   117     parObj=o;
   118 	mapEditor=parObj->getMapEditor();
   119 }
   120 
   121 void LinkableMapObj::setParObjTmp(LinkableMapObj*,QPoint,int)
   122 {
   123 }
   124 
   125 void LinkableMapObj::unsetParObjTmp()
   126 {
   127 }
   128 
   129 LinkStyle LinkableMapObj::getDefLinkStyle ()
   130 {
   131 	LinkStyle ls=mapEditor->getLinkStyle();
   132 	switch (ls)
   133 	{
   134 		case StyleLine: 
   135 			return ls;
   136 			break;
   137 		case StyleParabel:
   138 			return ls;
   139 			break;
   140 		case StylePolyLine:	
   141 			if (depth>1)
   142 				return StyleLine;
   143 			else	
   144 				return ls;
   145 			break;
   146 		case StylePolyParabel:	
   147 			if (depth>1)
   148 				return StyleParabel;
   149 			else	
   150 				return ls;
   151 			break;
   152 		default: 
   153 			break;	
   154 	}	
   155 	return StyleUndef;
   156 }
   157 
   158 void LinkableMapObj::setLinkStyle(LinkStyle newstyle)
   159 {
   160 	//if (newstyle=style) return;
   161 	delLink();
   162 		
   163 	style=newstyle;
   164 
   165     if (childObj!=NULL && parObj != NULL)
   166     {
   167 		int i;
   168 		QCanvasLine* cl;
   169 		switch (style)
   170 		{
   171 			case StyleUndef:
   172 				bottomline->hide();
   173 				break;
   174 			case StyleLine: 
   175 				l = new QCanvasLine(canvas);
   176 				l->setPen( QPen(linkcolor, 1) );
   177 				l->setZ(Z_LINK);
   178 				if (visible)
   179 					l->show();
   180 				else
   181 					l->hide();
   182 				break;
   183 			case StyleParabel:
   184 				for (i=0;i<arcsegs;i++)
   185 				{
   186 					cl = new QCanvasLine(canvas);
   187 					cl->setPen( QPen(linkcolor, 1) );
   188 					cl->setPoints( 0,0,i*10,100);
   189 					cl->setZ(Z_LINK);
   190 					if (visible)
   191 						cl->show();
   192 					else
   193 						cl->hide();
   194 					segment.append(cl);
   195 				}
   196 				pa0.resize (arcsegs+1);
   197 				break;
   198 			case StylePolyLine:	
   199 				p = new QCanvasPolygon(canvas);
   200 				p->setBrush( linkcolor );
   201 				p->setZ(Z_LINK);
   202 				if (visible)
   203 					p->show();
   204 				else
   205 					p->hide();
   206 				pa0.resize (3);
   207 				// TODO
   208 				// a bit awkward: draw the lines additionally to polygon, to avoid
   209 				// missing pixels, when polygon is extremly flat
   210 				l = new QCanvasLine(canvas);
   211 				l->setPen( QPen(linkcolor, 1) );
   212 				l->setZ(Z_LINK);
   213 				if (visible)
   214 					l->show();
   215 				else
   216 					l->hide();
   217 				break;
   218 			case StylePolyParabel:	
   219 				p = new QCanvasPolygon(canvas);
   220 				p->setBrush( linkcolor );
   221 				p->setZ(Z_LINK);
   222 				if (visible)
   223 					p->show();
   224 				else
   225 					p->hide();
   226 				pa0.resize (arcsegs*2+2);
   227 				pa1.resize (arcsegs+1);
   228 				pa2.resize (arcsegs+1);
   229 
   230 				// TODO
   231 				// a bit awkward: draw the lines additionally 
   232 				// to polygon, to avoid missing pixels, 
   233 				// if polygon is extremly flat
   234 				for (i=0;i<arcsegs;i++)
   235 				{
   236 					cl = new QCanvasLine(canvas);
   237 					cl->setPen( QPen(linkcolor, 1) );
   238 					cl->setPoints( 0,0,i*10,100);
   239 					cl->setZ(Z_LINK);
   240 					if (visible)
   241 						cl->show();
   242 					else
   243 						cl->hide();
   244 					segment.append(cl);
   245 				}
   246 				break;
   247 			default: 
   248 				break;	
   249 		}	
   250 		updateLink();
   251 	} else
   252 	{
   253 		qWarning ("Error: ChildObj or parObj == NULL in LinkableMapObj::setLinkStyle\n");
   254 	}
   255 }
   256 
   257 LinkStyle LinkableMapObj::getLinkStyle()
   258 {
   259 	return style;
   260 }
   261 
   262 void LinkableMapObj::setLinkPos(LinkPos lp)
   263 {
   264 	linkpos=lp;
   265 }
   266 
   267 LinkPos LinkableMapObj::getLinkPos()
   268 {
   269 	return linkpos;
   270 }
   271 
   272 
   273 void LinkableMapObj::setLinkColor()
   274 {
   275 	// Overloaded in BranchObj and childs
   276 	// here only set default color
   277 	setLinkColor (mapEditor->getDefLinkColor());
   278 }
   279 
   280 void LinkableMapObj::setLinkColor(QColor col)
   281 {
   282 	linkcolor=col;
   283     bottomline->setPen( QPen(linkcolor, 1) );
   284 	QCanvasLine *cl;
   285 	switch (style)
   286 	{
   287 		case StyleLine:
   288 			l->setPen( QPen(col,1));
   289 			break;	
   290 		case StyleParabel:	
   291 			for (cl=segment.first(); cl; cl=segment.next() )
   292 				cl->setPen( QPen(col,1));
   293 			break;
   294 		case StylePolyLine:
   295 			p->setBrush( QBrush(col));
   296 			l->setPen( QPen(col,1));
   297 			break;
   298 		case StylePolyParabel:	
   299 			p->setBrush( QBrush(col));
   300 			for (cl=segment.first(); cl; cl=segment.next() )
   301 				cl->setPen( QPen(col,1));
   302 			break;
   303 		default:
   304 			break;
   305 	} // switch (style)	
   306 	updateLink();
   307 }
   308 
   309 QColor LinkableMapObj::getLinkColor()
   310 {
   311 	return linkcolor;
   312 }
   313 
   314 FrameType LinkableMapObj::getFrameType()
   315 {
   316 	return frame->getFrameType();
   317 }
   318 
   319 void LinkableMapObj::setFrameType(const FrameType &t)
   320 {
   321 	frame->setFrameType(t);
   322 	calcBBoxSize();
   323 	positionBBox();
   324 	requestReposition();
   325 }
   326 
   327 void LinkableMapObj::setFrameType(const QString &t)
   328 {
   329 	frame->setFrameType(t);
   330 	calcBBoxSize();
   331 	positionBBox();
   332 	requestReposition();
   333 }
   334 
   335 void LinkableMapObj::setVisibility (bool v)
   336 {
   337 	MapObj::setVisibility (v);
   338 	if (visible) 
   339 	{
   340 		bottomline->show();
   341 		// FIXME lines and segments should be done in LMO?
   342 		if (style==StyleLine && l) 
   343 		{
   344 			l->show();
   345 		} else
   346 		{
   347 			QCanvasLine* cl;
   348 			for (cl=segment.first(); cl; cl=segment.next() )
   349 				cl->show();
   350 		} 
   351 	} else 
   352 	{
   353 		bottomline->hide();
   354 		if (style==StyleLine && l) 
   355 		{
   356 			l->hide();
   357 		} else
   358 		{
   359 			QCanvasLine* cl;
   360 			for (cl=segment.first(); cl; cl=segment.next() )
   361 				cl->hide();
   362 		} 
   363 	}	
   364 }
   365 
   366 void LinkableMapObj::updateLink()
   367 {
   368     // needs:
   369     //	childPos of parent
   370     //	orient   of parent
   371     //	style
   372     // 
   373     // sets:
   374     //	orientation
   375     //	childPos
   376     //	parPos
   377 	//  offset
   378     //	drawing of the link itself
   379 
   380 
   381 	// updateLink is called from move, but called from constructor we don't
   382 	// have parents yet...
   383 	if (style==StyleUndef) return;	
   384 
   385 	if (frame->getFrameType() == NoFrame)
   386 		linkpos=LinkBottom;
   387 	else	
   388 		linkpos=LinkMiddle;
   389 	switch (linkpos)
   390 	{
   391 		case LinkMiddle:
   392 			offset=bbox.height() /2;
   393 			break;
   394 		default :
   395 			offset=bbox.height()-1;			// draw link to bottom of bbox
   396 			break;
   397 	}
   398 	
   399     double p2x,p2y;								// Set P2 Before setting
   400 	if (!link2ParPos)
   401 	{
   402 		p2x=QPoint( parObj->getChildPos() ).x();	// P1, we have to look at
   403 		p2y=QPoint( parObj->getChildPos() ).y();	// orientation
   404 	} else	
   405 	{
   406 		p2x=QPoint( parObj->getParPos() ).x();	
   407 		p2y=QPoint( parObj->getParPos() ).y();
   408 	} 
   409 
   410 	LinkOrient orientOld=orientation;
   411 
   412     // Set orientation, first look for orientation of parent
   413     if (parObj->getOrientation() != OrientUndef ) 
   414 		// use the orientation of the parent:
   415 		orientation=parObj->getOrientation();
   416     else
   417     {
   418 		// calc orientation depending on position rel to mapCenter
   419 		if (absPos.x() < QPoint(parObj->getChildPos() ).x() )
   420 			orientation=OrientLeftOfCenter; 
   421 		else
   422 			orientation=OrientRightOfCenter;
   423     }
   424 
   425 	if ((orientation!=orientOld) && (orientOld!= OrientUndef))
   426 	{
   427 		// Orientation just changed. Reorient this subbranch, because move is called
   428 		// before updateLink => Position is still the old one, which could lead to 
   429 		// linking of subranch to itself => segfault
   430 		//
   431 		// Also possible: called in BranchObj::init(), then orientOld==OrientUndef,
   432 		// no need to reposition now
   433 		reposition();
   434 	}
   435 	
   436     if (orientation==OrientLeftOfCenter )
   437     {
   438 		childPos=QPoint (absPos.x(),absPos.y()+offset);
   439 		parPos=QPoint (absPos.x()+ bbox.width(), absPos.y() + offset );
   440     } else
   441     {
   442 		childPos=QPoint (absPos.x()+ bbox.width(), absPos.y() + offset ); 
   443 		parPos=QPoint (absPos.x(),absPos.y()+offset);
   444     }
   445 	/* FIXME
   446 		cout << "      LMO::updateLink   absPos="<<absPos<<endl;
   447 		cout << "      LMO::updateLink childPos="<<childPos<<endl;
   448 		cout << "      LMO::updateLink   parPos="<<parPos<<endl;
   449 		cout << "      LMO::updateLink   offset="<<offset<<endl;
   450 		cout << "      LMO::updateLink   bbox.w="<<bbox.width()<<endl;
   451 		cout << "      LMO::updateLink   bbox.h="<<bbox.height()<<endl;
   452 	*/	
   453 
   454 	double p1x=parPos.x();	// Link is drawn from P1 to P2
   455 	double p1y=parPos.y();
   456 
   457 	double vx=p2x - p1x;	// V=P2-P1
   458 	double vy=p2y - p1y;
   459 
   460 	// Draw the horizontal line below heading (from ChildPos to ParPos)
   461 	bottomline->setPoints (lrint(childPos.x()),
   462 		lrint(childPos.y()),
   463 		lrint(p1x),
   464 		lrint(p1y) );
   465 
   466 	double a;	// angle
   467 	if (vx > -0.000001 && vx < 0.000001)
   468 		a=M_PI_2;
   469 	else
   470 		a=atan( vy / vx );
   471 	// "turning point" for drawing polygonal links
   472 	QPoint tp (-lrint(sin (a)*thickness_start), lrint(cos (a)*thickness_start));	
   473 	
   474 	QCanvasLine *cl;
   475 
   476 	int i;
   477 
   478     // Draw the link
   479 	switch (style)
   480 	{
   481 		case StyleLine:
   482 			l->setPoints( lrint (parPos.x()),
   483 				lrint(parPos.y()),
   484 				lrint(p2x),
   485 				lrint(p2y) );
   486 			break;	
   487 		case StyleParabel:	
   488 			parabel (pa0, p1x,p1y,p2x,p2y);
   489 			i=0;
   490 			for (cl=segment.first(); cl; cl=segment.next() )
   491 			{	
   492 				cl->setPoints( pa0.point(i).x(), pa0.point(i).y(),pa0.point(i+1).x(),pa0.point(i+1).y());
   493 				i++;
   494 			}
   495 			break;
   496 		case StylePolyLine:
   497 			pa0[0]=QPoint (lrint(p2x+tp.x()), lrint(p2y+tp.y()));
   498 			pa0[1]=QPoint (lrint(p2x-tp.x()), lrint(p2y-tp.y()));
   499 			pa0[2]=QPoint (lrint (parPos.x()), lrint(parPos.y()) );
   500 			p->setPoints (pa0);
   501 			// here too, draw line to avoid missing pixels
   502 			l->setPoints( lrint (parPos.x()),
   503 				lrint(parPos.y()),
   504 				lrint(p2x),
   505 				lrint(p2y) );
   506 			break;
   507 		case StylePolyParabel:	
   508 			parabel (pa1, p1x,p1y,p2x+tp.x(),p2y+tp.y());
   509 			parabel (pa2, p1x,p1y,p2x-tp.x(),p2y-tp.y());
   510 			for (i=0;i<=arcsegs;i++)
   511 			{
   512 				// Combine the arrays to a single one
   513 				pa0[i]=pa1[i];
   514 				pa0[i+arcsegs+1]=pa2[arcsegs-i];
   515 			}	
   516 			p->setPoints (pa0);
   517 			i=0;
   518 			for (cl=segment.first(); cl; cl=segment.next() )
   519 			{	
   520 				cl->setPoints( pa1.point(i).x(), pa1.point(i).y(),pa1.point(i+1).x(),pa1.point(i+1).y());
   521 				i++;
   522 			}
   523 			break;
   524 		default:
   525 			break;
   526 	} // switch (style)	
   527 }
   528 	
   529 LinkableMapObj* LinkableMapObj::getChildObj()
   530 {
   531     return childObj;
   532 }
   533 
   534 LinkableMapObj* LinkableMapObj::getParObj()
   535 {
   536     return parObj;
   537 }
   538 
   539 QPoint LinkableMapObj::getChildPos()
   540 {
   541     return childPos;
   542 }
   543 
   544 QPoint LinkableMapObj::getParPos()
   545 {
   546     return parPos;
   547 }
   548 
   549 QPoint LinkableMapObj::getRelPos()
   550 {
   551 	if (!parObj) return QPoint (0,0);
   552     return QPoint(
   553 		absPos.x() - parObj->x(),
   554 		absPos.y() - parObj->y()
   555 	);
   556 }
   557 
   558 LinkOrient LinkableMapObj::getOrientation()
   559 {
   560     return orientation;
   561 }
   562 
   563 int LinkableMapObj::getDepth()
   564 {
   565     return depth;
   566 }
   567 
   568 void LinkableMapObj::setMapEditor (MapEditor *me)
   569 {
   570 	mapEditor=me;
   571 }
   572 
   573 MapEditor* LinkableMapObj::getMapEditor ()
   574 {
   575 	return mapEditor;
   576 }
   577 
   578 QPoint LinkableMapObj::getRandPos()
   579 {
   580 	// Choose a random position with given distance to parent:
   581 	double a=rand()%360 * 2 * M_PI / 360;
   582     return QPoint ( (int)( + 150*cos (a)),
   583                     (int)( + 150*sin (a)));
   584 }
   585 
   586 void LinkableMapObj::alignRelativeTo (QPoint ref)
   587 {
   588 }
   589 
   590 void LinkableMapObj::reposition()
   591 {
   592 	if (depth==0)
   593 	{
   594 		// only calculate the sizes once. If the deepest LMO changes its height,
   595 		// all upper LMOs have to change, too.
   596 		calcBBoxSizeWithChilds();
   597 
   598 	    alignRelativeTo ( QPoint (absPos.x(),
   599 							absPos.y()-(bboxTotal.height()-bbox.height())/2) );
   600 	} else
   601 	{
   602 		// This is only important for moving branches:
   603 		// For editing a branch it isn't called...
   604 	    alignRelativeTo ( QPoint (absPos.x(),
   605 							absPos.y()-(bboxTotal.height()-bbox.height())/2) );
   606 	}
   607 }
   608 
   609 void LinkableMapObj::requestReposition()
   610 {
   611 	if (!repositionRequest)
   612 	{
   613 		// Pass on the request to parental objects, if this hasn't
   614 		// been done yet
   615 		repositionRequest=true;
   616 		if (parObj) parObj->requestReposition();
   617 	}
   618 }
   619 
   620 void LinkableMapObj::forceReposition()
   621 {
   622 	// Sometimes a reposition has to be done immediatly: For example
   623 	// if the note editor flag changes, there is no user event in mapeditor
   624 	// which could collect requests for a reposition.
   625 	// Then we have to call forceReposition()
   626 	// But no rule without exception: While loading a map or undoing it,
   627 	// we want to block expensive repositioning, but just do it once at
   628 	// the end, thus check first:
   629 
   630 	if (mapEditor->blockReposition()) return;
   631 	
   632 	// Pass on the request to parental objects, if this hasn't been done yet
   633 	
   634 	if (parObj) 
   635 		parObj->forceReposition(); 
   636 	else 
   637 		reposition(); 
   638 }
   639 
   640 bool LinkableMapObj::repositionRequested()
   641 {
   642 	return repositionRequest;
   643 }
   644 
   645 
   646 void LinkableMapObj::setSelBox()
   647 {
   648     selbox->setX (bbox.x() );
   649     selbox->setY (bbox.y() );
   650     selbox->setSize (bbox.width(), bbox.height() );
   651 }
   652 
   653 void LinkableMapObj::select()
   654 {
   655 	setSelBox();
   656     selected=true;
   657     selbox->show();
   658 }
   659 
   660 
   661 void LinkableMapObj::unselect()
   662 {
   663     selected=false;
   664     selbox->hide();
   665 }
   666 
   667 void LinkableMapObj::parabel (QPointArray &ya, double p1x, double p1y, double p2x, double p2y)
   668 
   669 {
   670 	double vx=p2x - p1x;	// V=P2-P1
   671 	double vy=p2y - p1y;
   672 
   673 	double dx;				// delta x during calculation of parabel
   674 	
   675 	double pnx;				// next point
   676 	double pny;
   677 	double m;
   678 
   679 	if (vx > -0.0001 && vx < 0.0001)
   680 		m=0;
   681 	else	
   682 		m=(vy / (vx*vx));
   683 	dx=vx/(arcsegs);
   684 	int i;
   685 	ya.setPoint (0,QPoint (lrint(p1x),lrint(p1y)));
   686 	for (i=1;i<=arcsegs;i++)
   687 	{	
   688 		pnx=p1x+dx;
   689 		pny=m*(pnx-parPos.x())*(pnx-parPos.x())+parPos.y();
   690 		ya.setPoint (i,QPoint (lrint(pnx),lrint(pny)));
   691 		p1x=pnx;
   692 		p1y=pny;
   693 	}	
   694 }
   695