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