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