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