/****************************************************************************
** ImageObject class
**
**   Created : Tue Feb 02 22:06:51 2004
**        by : Varol Okan using Kate
** Copyright : (c) Varol Okan
**   License : GPL v 2.0
**
** This class is the encapsulation of the ImageObject.
**
** AN ImageObject is a visible object in a DVD Menu.
**
****************************************************************************/
#include <qpushbutton.h>
#include <qpopupmenu.h>
#include <qfileinfo.h>
#include <qlistview.h>
#include <qlineedit.h>
#include <qpainter.h>
#include <qwmatrix.h>
#include <qpixmap.h>
#include <qdom.h>

#include "xml_dvd.h"
//#include "menuobject.h"
#include "frameobject.h"
#include "imageobject.h"
#include "dialogmovie.h"
#include "messagebox.h"
#include "dialogmatrix.h"
#include "overlayobject.h"
#include "dialoggeometry.h"
#include "dialoganimation.h"
#include "structuretoolbar.h"

// The following three includes required for undo.
#include "global.h"
#include "qdvdauthor.h"
#include "dvdmenuundoobject.h"

ImageObject::ImageData::ImageData ( )
  : MenuObject::Data ( )
{
  m_bHidden   = false;
  m_iDataType = Data::IMAGE;
}

ImageObject::ImageData::~ImageData ( )
{
}

MenuObject::Data &ImageObject::ImageData::operator = ( Data &theOther )
{
  // At this point we are sure both are acutally ImageManipulator objects.
  MenuObject::Data::operator= ( theOther );
  if ( m_iDataType == theOther.m_iDataType )  {
       ImageData *pOther  = (ImageData *)&theOther;
       m_imageManipulator = pOther->m_imageManipulator;
       m_bHidden          = pOther->m_bHidden;
  }
  if ( theOther.m_iDataType == Data::DATA )  {
       m_imageManipulator   = theOther.m_modifiers;
  }
  return *this;
}

bool ImageObject::ImageData::operator == ( Data &theOther )
{
  // At this point we are sure both are acutally ImageManipulator objects.
  bool bReturn = ( MenuObject::Data::operator == ( theOther ) );
  if ( ! bReturn )
    return false;

  if ( m_iDataType == theOther.m_iDataType )  {
    ImageData *pOther = (ImageData *)&theOther;
    bReturn  = ( ( m_imageManipulator == pOther->m_imageManipulator ) &&
                 ( m_bHidden          == pOther->m_bHidden          ) );
  }
  return bReturn;
}

MenuObject::Data *ImageObject::ImageData::clone ( )
{
  ImageData *pData = new ImageData;
  *pData  = *this;
  return pData;
}

ImageObject::ImageObject ( QWidget *pParent )
  : MenuObject ( pParent )
{
  if ( m_pData )
    delete m_pData;        // part of the base class
  m_pData = new ImageData; // will be deleted in base class
  m_qsObjectType = QString ( IMAGE_OBJECT );
}

ImageObject::~ImageObject ( )
{
}

void ImageObject::setImage ( QImage &theImage )
{
  m_image = theImage;
}

void ImageObject::setFile ( QString fileName )
{
	QString   qsExtractionPath;
	QFileInfo fileInfo ( fileName );
	m_qsFileName = fileInfo.fileName ( );
	setName ( m_qsFileName );
	qsExtractionPath = fileInfo.baseName ( true );
	setExtractionPath ( qsExtractionPath );
	m_qsFileName = fileName;
}

void ImageObject::setHidden ( bool bHidden )
{
  imageData ( )->m_bHidden = bHidden;
}

void ImageObject::setZoom ( float fZoom )
{
  // For the purpose of this class the Zoom is alreaduy incorporated into m_pixmap ...
  manipulator ( ).fZoom = fZoom;
}

void ImageObject::setManipulator(ImageManipulator &theOther)
{
  manipulator ( ) = theOther;
}

void ImageObject::setModifiers ( Modifiers &theOther )
{
  Modifiers *pModifier = (Modifiers *)&manipulator ( );
  *pModifier = theOther;
}

ImageManipulator &ImageObject::manipulator()
{
  ImageData *pData = (ImageData *)data ( );
  return     pData->m_imageManipulator;
}

Modifiers *ImageObject::modifiers()
{
  // Return the base class only ...
  // Note: this is needed to use the same MatrixDialog for text and images.
  return (Modifiers *) &manipulator ( );
}

ImageObject::ImageData *ImageObject::imageData ( )
{
  return (ImageData *)m_pData;
}

QString &ImageObject::fileName()
{
	return m_qsFileName;
}

QImage &ImageObject::image ( )
{
  if ( overlay ( ) )  {
    OverlayObject *pOverlayObject = (OverlayObject *)overlay ( );
    return pOverlayObject->image ( m_image );
  }
  return m_image;
}

float ImageObject::zoom ( )
{
        return manipulator ( ).fZoom;
}

bool ImageObject::hidden ( )
{
        return imageData ( )->m_bHidden;
}

void ImageObject::drawContents ( QPainter *painter )
{
	QWMatrix theMatrix;
	// Here we calculate the center point of gravity (rotation)
	QPoint centerPos;
	centerPos.setX ( rect ( ).x ( ) + (int)( (float)rect ( ).width  ( ) / 2.0 ) );
	centerPos.setY ( rect ( ).y ( ) + (int)( (float)rect ( ).height ( ) / 2.0 ) );
	// Here we define the cenetered rect.
	QRect theRect  ( (int)-( rect ( ).width ( )/ 2.0 ), (int)-( rect ( ).height ( ) / 2.0 ), rect ( ).width ( ), rect ( ).height ( ) );

	QPen     thePen ( QColor ( 255, 0, 0 ), 2, Qt::DashDotLine );
	painter->setPen ( thePen );

	// From the Qt documentation ...
	// This transformation engine is a three-step pipeline
	// The first step uses the world transformation matrix.
	// The second step uses the window.
	// The third step uses the viewport.

	// First we translate to the appropriate location,
	theMatrix.translate ( (double)centerPos.x ( ), (double)centerPos.y ( ) );

	//. then we apply the other modifications ...
	//theMatrix.scale  ( modifiers ( )->fScaleX, modifiers ( )->fScaleY );
	theMatrix.scale  ( manipulator ( ).fScaleX, manipulator ( ).fScaleY );
	theMatrix.shear  ( manipulator ( ).fShearX, manipulator ( ).fShearY );
	theMatrix.rotate ( manipulator ( ).fRotate );

	// Here we get the rect that encompasses the button for this object.
	m_boundingRect = theMatrix.mapRect ( theRect );

	if ( isActive ( ) )
	  drawActiveFrame ( painter );

	painter->setWorldMatrix ( theMatrix );

	// Here we re-set the world matrix and translate the newly drawn ... thing ...
	if ( isActive ( ) )
	  painter->drawRect(theRect);
	
	theMatrix.reset ( );
	painter->setWorldMatrix ( theMatrix );
	
	// check if we have a complete transparency, in which case we don't even need to bother...
	if ( manipulator ( ).fTransparency == 1.0 )
		return;

	if ( ( ! isActive ( ) ) && ( ! hidden ( ) ) ) {
                if ( m_pShadow )
	             m_pShadow->drawContents ( painter );

		int t, x, y;
		QRgb thePixel;
		int iSourceHeight = image ( ).height ( );
		int iSourceWidth  = image ( ).width  ( );
		int iSourceStartX = 0;
		int iSourceStartY = 0;
		int iTargetStartX = painter->viewport ( ).x ( ) + m_boundingRect.x ( );
		int iTargetStartY = painter->viewport ( ).y ( ) + m_boundingRect.y ( );
		// Use the fast method whenever possible
//		if (	( manipulator ( ).fRotate        == 0.0 ) &&
//			( manipulator ( ).fShearX        == 0.0 ) &&
//			( manipulator ( ).fShearY        == 0.0 ) &&
//			( manipulator ( ).fTransparency  == 0.0 ) &&
//			( manipulator ( ).listColorKeys.count ( ) < 1 ) ) {
		if (	( manipulator ( ).fTransparency  == 0.0 ) &&
			( manipulator ( ).listColorKeys.count ( ) < 1 ) ) {
		if (	( manipulator ( ).fRotate        == 0.0 ) &&
			( manipulator ( ).fShearX        == 0.0 ) &&
			( manipulator ( ).fShearY        == 0.0 ) )  {
		  QWMatrix theMatrix;
//		  theMatrix.scale  ( manipulator ( ).fScaleX, manipulator ( ).fScaleY );
		  theMatrix.shear  ( manipulator ( ).fShearX, manipulator ( ).fShearY );
		  theMatrix.rotate ( manipulator ( ).fRotate );
		  QImage temp = image ( ).xForm  ( theMatrix );
		  painter->drawImage  ( iTargetStartX, iTargetStartY, temp, iSourceStartX, iSourceStartY, iSourceWidth, iSourceHeight, Qt::CopyROP );
		}
		else
		  painter->drawImage ( iTargetStartX, iTargetStartY, image ( ), iSourceStartX, iSourceStartY, iSourceWidth, iSourceHeight, Qt::CopyROP );
		}
		//iSourceStartX, iSourceStartY, iSourceWidth, iSourceHeight, Qt::CopyROP, true);
		// Okay if we can not use bitBlt we have to go through the exact calculations ...
		else	{
			// First we need to create a Pixmap which holds the Target pixels
			QImage theImage = image ( );
			QColor sourceColor, targetColor, theColor;
			QRgb   sourcePixel;
			QImage sourceImg;
			float  fTargetTrans, fSourceTrans;
			fTargetTrans = 1.0 - manipulator ( ).fTransparency;
			fSourceTrans =       manipulator ( ).fTransparency;
			if ( fTargetTrans != 0.0 )	{
				QPixmap sourcePix ( iSourceWidth, iSourceHeight );
				bitBlt (&sourcePix, 0, 0, painter->device(), iTargetStartX, iTargetStartY, iSourceWidth, iSourceHeight, Qt::CopyROP, true);
				// To get Pixel information we need a QImage however ...
				sourceImg = sourcePix.convertToImage();
				// Now we can get the original Pixel information ...
//				Note: This ought to be done in the updatePixmap () - function
//					- Also there we leave all info untouched which is (0,0,0)
//					- Also when we move AND when fTransparency != 0.0 then we need to do updatePixmap everytime.
			}			
			// Note that this function is actually using the viewport - transformation provided by Qt. The previous bitBlt's
			// did paint directly onto the device, thus we had to manually add the viewport coords. Now we have this info already
			// in the painter and thus we can substract them here.
			iTargetStartX -= painter->viewport().x();
			iTargetStartY -= painter->viewport().y();
			
			// Next we create the min/max Color keys, to check if we ought to filter the pixel out or not
			QValueList<QColor *>listColorKeyMin;
			QValueList<QColor *>listColorKeyMax;
			QColor *pColorMin, *pColorMax;
			if (manipulator().listColorKeys.count() > 0)	{
				int iDelta, iRed, iGreen, iBlue;
				for (t=0;t<(int)manipulator().listColorKeys.count();t++)	{
					iDelta = (int)(manipulator().listColorKeys[t]->fDeltaColor * 255.0);
					theColor = QColor (manipulator().listColorKeys[t]->theColor);
					iRed = theColor.red() - iDelta; iGreen = theColor.green() - iDelta; iBlue = theColor.blue() - iDelta;
					iRed = (iRed < 0) ? 0 : iRed; iGreen = (iGreen < 0) ? 0 : iGreen; iBlue = (iBlue < 0) ? 0 : iBlue;
					pColorMin = new QColor (iRed, iGreen, iBlue);
					iRed = theColor.red() + iDelta; iGreen = theColor.green() + iDelta; iBlue = theColor.blue() + iDelta;
					iRed = (iRed > 255) ? 255 : iRed; iGreen = (iGreen > 255) ? 255 : iGreen; iBlue = (iBlue > 255) ? 255 : iBlue;
					pColorMax = new QColor (iRed, iGreen, iBlue);

					listColorKeyMin.append (pColorMin);
					listColorKeyMax.append (pColorMax);
				}
			}
			for (y=0; y < iSourceHeight; y++)	{
				for (x=0; x < iSourceWidth; x++)	{
					thePixel = theImage.pixel(x+iSourceStartX, y+iSourceStartY);
					thePixel &= 0x00ffffff;			// filter out the transparency part of the color (not used in Qt 3.x)
					if (thePixel != 0x000000)	{	// And check if this is a pixel we need to handle ...
						// If there is a transparency part, or some color we need to filter out, 
						// then  we need to go through this slow loop.
						if ( (fTargetTrans != 0.0) || (manipulator().listColorKeys.count ()) )	{ 
							sourcePixel = sourceImg.pixel(x, y);	// calculate the mix of background and foreground.
							sourceColor = QColor (sourcePixel);
							targetColor = QColor ( thePixel );
							if (filterColorKey ( &targetColor, &listColorKeyMin, &listColorKeyMax ))
								theColor = sourceColor;
							else
								theColor = QColor (
									(int)(fSourceTrans * sourceColor.red()   + (int)(fTargetTrans * targetColor.red())),
									(int)(fSourceTrans * sourceColor.green() + (int)(fTargetTrans * targetColor.green())),
									(int)(fSourceTrans * sourceColor.blue()  + (int)(fTargetTrans * targetColor.blue()))
								); 
							painter->setPen ( theColor );
						}
						else	// No transparency is slightly easier ...
							painter->setPen ( QColor ( thePixel ) );
						painter->drawPoint(x+iTargetStartX, y+iTargetStartY);
					}
				}
			}
			if (manipulator().listColorKeys.count() > 0)	{
				for (t=0;t<(int)manipulator().listColorKeys.count();t++)	{
					delete listColorKeyMin[t];
					delete listColorKeyMax[t];
				}
			}
		}
	}
	// Phew ... This funtion has grown over time ...
}

void ImageObject::drawContents (QPainter *pPainter, int iRenderFrameNumber, int)
{
  // Next is to set the AnimationAttributes
  bool bRefresh = false;
  for (uint t=0; t<m_listAnimationAttributes.count ( ); t++)
    bRefresh |= m_listAnimationAttributes[t]->setValue ( iRenderFrameNumber );

  //  printf ( "[%d] : Start<%d> Stop<%d>\n", iRenderFrameNumber, modifiers ( )->iStartFrame, modifiers ( )->iStopFrame );
  // If start stop have been set then we check the range
  if ( ( ( iRenderFrameNumber < modifiers ( )->iStartFrame ) && ( modifiers ( )->iStartFrame != -1 ) ) || 
       ( ( iRenderFrameNumber > modifiers ( )->iStopFrame  ) && ( modifiers ( )->iStopFrame  != -1 ) ) ) 
    return;

  if ( overlay ( ) )
       overlay ( )->drawContents ( pPainter, iRenderFrameNumber, 0 );
  if ( shadow  ( ) )
       shadow  ( )->drawContents ( pPainter, iRenderFrameNumber, 0 );

  // And then we ought to refresh the pixmap ...
  if ( bRefresh ) {
    updatePixmap ( );
    updateShadow ( );
  }
  // before we can go ahead and draw the contents with the newly set attributes.
  drawContents (pPainter);
}

// This function checks if pColor is within the min/max of one of the color keys.
bool ImageObject::filterColorKey ( QColor *pColor, QValueList<QColor *> *pListColorMin, QValueList<QColor *>*pListColorMax )
{
  int iRed, iGreen, iBlue, t, iColorKeyCount;
  iColorKeyCount = manipulator().listColorKeys.count ();
  if ( ( iColorKeyCount < 1 ) || ( ! pListColorMin ) || ( ! pListColorMax ) )
    return false;

  // If image is rotated then pitch balck ought to be removed ...
  if ( ( ( modifiers ( )->fShearX != 0.0 ) || 
	 ( modifiers ( )->fShearX != 0.0 ) || 
	 ( modifiers ( )->fRotate != 0.0 ) ) && 
       ( *pColor == QColor ( 0, 0, 0 ) ) )
    return true;

  iRed = pColor->red(); iGreen = pColor->green(); iBlue = pColor->blue ();
  for (t=0;t<iColorKeyCount;t++)	{
    if ( (iRed   >= (*pListColorMin)[t]->red  () && iRed   <= (*pListColorMax)[t]->red  ()) && 
	 (iGreen >= (*pListColorMin)[t]->green() && iGreen <= (*pListColorMax)[t]->green()) &&
	 (iBlue  >= (*pListColorMin)[t]->blue () && iBlue  <= (*pListColorMax)[t]->blue ()) )
      return true;
  }
  return false;
}

void ImageObject::updatePixmap()
{
	// This function will take the input image and pass it through the whole modifications pipeline,
	// so we get a nice and clean rotated / colored image again.

	// In order to speed up the whole procedure, we set the matrix first to scale the image,
	// then we handle the color functions on the smaller image and lastly we apply the rotation,shearing...
	int    iRed, iGreen, iBlue, iAlpha;
	int    x, y;
	int    iLighter = 0;
	float  fAdjust  = 0.0;
	QColor theColor;
	QRgb   thePix;	// QRgb defined as :  0xAARRGGBB;
	Rgba   rgba;
	QWMatrix theMatrix;
	// Here we calculate the center point of gravity (rotation)
	QPoint centerPos;

	if ( fileName ( ).isEmpty ( ) )
		return;

	QImage theImage ( fileName ( ) );

	if ( theImage.isNull ( ) )
		return;

	// the kenBurns is taken care of here ...
	if ( ( manipulator().fKenBurnsX1 > -1.0 ) || ( manipulator().fKenBurnsY1 > -1.0 ) ) {
	  float fKen[4];
	  int   iKen[4];
	  // if X2 and Y2 from manipulator is -1 then we go all the way to the max ext of the image
	  fKen[0] = manipulator ( ).fKenBurnsX1;
	  fKen[1] = manipulator ( ).fKenBurnsY1;
	  fKen[2] = manipulator ( ).fKenBurnsX2;
	  fKen[3] = manipulator ( ).fKenBurnsY2;
	  iKen[0] = 0;
	  iKen[1] = 0;
	  iKen[2] = theImage.width  ( );
	  iKen[3] = theImage.height ( );
	  
	  if ( fKen[0] > fKen[2] ) {
	       fKen[0] = fKen[2];
               fKen[2] = manipulator ( ).fKenBurnsX1;
	  }
	  if ( fKen[1] > fKen[3] ) {
  	       fKen[1] = fKen[3];
               fKen[3] = manipulator ( ).fKenBurnsY1;
	  }
	  if ( fKen[2] < 0.0 )
               fKen[2] = 1.0;
	  if ( fKen[3] < 0.0 )
               fKen[3] = 1.0;
	  iKen[0] = (int)(   fKen[0] * iKen[2] );
	  iKen[1] = (int)(   fKen[1] * iKen[3] );
	  iKen[2] = (int)( ( fKen[2] - fKen[0] ) * iKen[2] );
	  iKen[3] = (int)( ( fKen[3] - fKen[1] ) * iKen[3] );
	  if ( iKen[0] > theImage.width ( ) )
	       iKen[0] = 0;
	  if ( iKen[1] > theImage.height  ( ) )
	       iKen[1] = 0;
	  if ( iKen[0] + iKen[2] > theImage.width  ( ) )
	       iKen[2] = theImage.width  ( ) - iKen[0];
	  if ( iKen[1] + iKen[3] > theImage.height ( ) )
	       iKen[3] = theImage.height ( ) - iKen[1];
	  
	  theImage = theImage.copy ( iKen[0], iKen[1], iKen[2], iKen[3] );
	}

	theImage = theImage.smoothScale ( rect ().width(), rect().height(), QImage::ScaleFree);

	centerPos.setX (rect().x() + (int)((float)rect().width () / 2.0));
	centerPos.setY (rect().y() + (int)((float)rect().height() / 2.0));
	// First we translate to the appropriate location,
	theMatrix.translate ((double)centerPos.x(), (double)centerPos.y());
	// then we apply the other modifications ...
	theMatrix.scale  ( manipulator ( ).fScaleX, manipulator ( ).fScaleY );

	// Here we create the scaled image ...
	theImage = theImage.xForm ( theMatrix );
	// create empty image.
	QImage imageColored = QImage (theImage.width(), theImage.height(), theImage.depth());
	imageColored.setAlphaBuffer ( true );
	if ( ( manipulator ( ).fBrightness != 0.0 ) ||
	     ( manipulator ( ).fRed        != 1.0 ) ||
	     ( manipulator ( ).fGreen      != 1.0 ) ||
	     ( manipulator ( ).fBlue       != 1.0 ) ) {
	  if ( manipulator ( ).fBrightness > 0.0 )	{
	    iLighter = 1;
	    fAdjust = 100 + manipulator ( ).fBrightness * 300;
	  }
	  else	{
	    iLighter = -1;
	    fAdjust  = 100 + manipulator ( ).fBrightness * -300;
	  }
	  for (y=0;y<theImage.height();y++)	{
	    for (x=0;x<theImage.width();x++)	{
	      thePix = theImage.pixel ( x, y );
	      iRed   = (int)((float)qRed  (thePix) * manipulator ( ).fRed   );
	      iBlue  = (int)((float)qBlue (thePix) * manipulator ( ).fBlue  );
	      iGreen = (int)((float)qGreen(thePix) * manipulator ( ).fGreen );
	      iAlpha = qAlpha ( thePix );

	      theColor =  QColor ( iRed, iGreen, iBlue );
	      if ( iLighter != 0 ) {
		if ( iLighter == 1 )
		  theColor = theColor.light ( (int)fAdjust );
		else 
		  theColor = theColor.dark  ( (int)fAdjust );
	      }
	      // This test is to mask out the transparency color (0, 0, 0)
	      if ( theColor == QColor ( 0, 0, 0 ) )
		rgba      = QColor ( 0, 0, 1 );
	      else {
		rgba.setRgb   ( theColor.rgb ( ) );
		rgba.setAlpha ( iAlpha );
	      }
	      imageColored.setPixel ( x, y, rgba.rgb ( ) );
	    }
	  }
	}
	else
	  imageColored = theImage;
	// Next we re-set the matrix ...
	theMatrix.reset ();
	// and plug in the remaining operations ...
	theMatrix.shear  ( manipulator ( ).fShearX, manipulator ( ).fShearY );
	theMatrix.rotate ( manipulator ( ).fRotate );
	m_image = imageColored.xForm   ( theMatrix );
	emit ( signalUpdatePixmap ( ) );
}

bool ImageObject::readProjectFile  (QDomNode &theNode)
{
	QDomElement theElement = theNode.toElement();
	// The video node has only attributes and nothing else.
	QDomAttr attribute = theElement.attributeNode ( IMAGE_OBJECT_FILE_NAME );
	if ( ! attribute.isNull ( ) )
		setFile ( attribute.value ( ) );

	setHidden ( false );
	attribute = theElement.attributeNode ( IMAGE_OBJECT_HIDDEN );
	if ( ! attribute.isNull ( ) && attribute.value ( ) == "true" )
		setHidden  ( true );

	QDomNode  manNode = theNode.firstChild ( );
	while ( ! manNode.isNull ( ) )  {
		QDomElement manElement = manNode.toElement();
		if (manElement.tagName() == MANIPULATOR)
			manipulator().readProjectFile (manNode);
		else if (manElement.tagName() == MENU_OBJECT)
			MenuObject::readProjectFile( theNode );

		manNode = manNode.nextSibling();
	}
	this->updatePixmap ();
	return true;
}

bool ImageObject::writeProjectFile (QDomElement &theElement)
{
	QDomDocument xmlDoc = theElement.ownerDocument();
	QDomElement imageNode = xmlDoc.createElement( IMAGE_OBJECT );	// <FrameObject>
	// Here we set the attributes of the <dvdauthor> tag
	if (!m_qsFileName.isNull())
		imageNode.setAttribute ( IMAGE_OBJECT_FILE_NAME, m_qsFileName );
	if ( hidden ( ) )
		imageNode.setAttribute ( IMAGE_OBJECT_HIDDEN, "true" );

	manipulator().writeProjectFile (imageNode);

	theElement.appendChild( imageNode );
	// And here we write the base class ...
	return MenuObject::writeProjectFile( imageNode );
}

void ImageObject::resetStructureItem ( )
{
  if ( m_pShadow )
       m_pShadow->resetStructureItem ( );

  if ( m_pOverlay )
       m_pOverlay->resetStructureItem ( );

  if ( m_pStructureItem )
    delete m_pStructureItem;
  m_pStructureItem = NULL;
  manipulator ( ).m_pStructureItem = NULL;
}

StructureItem *ImageObject::createStructure ( StructureItem *pParentItem )
{
  StructureItem *pTemp=NULL;
  QFileInfo fileInfo ( m_qsFileName );
  QString qsName;

  if ( ! pParentItem )  {
    if ( m_pOverlay  )
      pTemp = m_pOverlay->createStructure ( NULL );
    if ( m_pShadow )
      pTemp = m_pShadow->createStructure  ( NULL );
    manipulator ( ).createStructure ( NULL );
    m_pStructureItem = NULL;
    return NULL;
  }

  qsName = tr( "Image Object" );

  if ( ! m_pStructureItem ) {
    m_pStructureItem = new StructureItem ( this, StructureItem::Image, pParentItem, pParentItem->lastChild ( ), qsName, fileInfo.fileName ( ) );
    m_pStructureItem->setExpandable      ( TRUE );
  }

  //  pTemp = (StructureItem *)m_pStructureItem->firstChild ( );
  //  if ( pTemp ) printf ( "ImageObject : <%s>\n", pTemp->text ( 0 ).ascii() );
  //  else         printf ( "ImageObject : No First for <%s>.\n", fileInfo.fileName().ascii() );

  pTemp = m_pStructureItem->setSibling ( this, NULL, tr ( "Geometry" ), QString ( "%1, %2, %3, %4" ).
       	         arg ( rect().x()).arg(rect().y()).arg(rect().width()).arg(rect().height()) );

  if ( m_pOverlay )
    pTemp = m_pOverlay->createStructure ( m_pStructureItem );

  if ( m_pShadow )
    pTemp = m_pShadow->createStructure ( m_pStructureItem );

  manipulator ( ).createStructure ( m_pStructureItem, pTemp );

  return m_pStructureItem;
}

bool ImageObject::mousePressEvent (QMouseEvent *pEvent)
{
  m_currentMousePos = pEvent->pos ( );
  if ( pEvent->button ( ) == Qt::RightButton )  {
    QPoint globalPos = pEvent->globalPos ( );
    return createContextMenu ( globalPos );
  }
  else {
    m_bDrawRect = true;
  }

  return false;
}

bool ImageObject::mouseReleaseEvent ( QMouseEvent * )
{
  UndoBuffer *pUndo = Global::pApp->getUndoBuffer ( );
  pUndo->removeUnchanged ( );
  m_bDrawRect = false;
  updatePixmap ( );
  emit ( signalUpdatePixmap    ( ) );
  emit ( signalUpdateStructure ( ) );
  return false;
}

bool ImageObject::mouseDoubleClickEvent (QMouseEvent *)
{
  // Back to the MenuPreview - class, where I can handle this request ...
  emit ( signalModifyMe ( this ) );
  return false;
}

bool ImageObject::createContextMenu ( QPoint globalPos )
{
  QString qsShadow, qsOverlay;
  QPoint globalPos2 = globalPos;
  QPopupMenu *pStackMenu = new QPopupMenu(m_pParent);
  pStackMenu->insertItem ( tr ("Cut") , this, SLOT ( slotCut  ( ) ) );
  pStackMenu->insertItem ( tr ("Copy"), this, SLOT ( slotCopy ( ) ) );
  pStackMenu->insertSeparator ( );
  pStackMenu->insertItem ( tr ("To Front")  , this, SLOT ( slotToFront ( ) ) );
  pStackMenu->insertItem ( tr ("To Back")   , this, SLOT ( slotToBack  ( ) ) );
  globalPos.setY ( globalPos.y ( ) - 25 );
  globalPos.setX ( globalPos.x ( ) - pStackMenu->sizeHint().width()); // -100);
  pStackMenu->popup ( globalPos, 1 );

  qsShadow   = tr ( "Add Shadow ..."  );
  if ( shadow ( ) )
    qsShadow = tr ( "Edit Shadow ..." );

  qsOverlay   = tr ( "Add Mask ..."  );
  if ( overlay ( ) )
    qsOverlay = tr ( "Edit Mask ..." );


  if ( m_pContextMenu )
    delete m_pContextMenu;
  m_pContextMenu = new QPopupMenu ( m_pParent );
  m_pContextMenu->setCheckable ( true );
  m_pContextMenu->insertItem ( tr ("Edit ..." )      , this, SLOT ( slotEdit      ( ) ) );
  m_pContextMenu->insertItem ( tr ("Properties ...") , this, SLOT ( slotProperties( ) ) );
  m_pContextMenu->insertItem ( tr ("Matrix ...")     , this, SLOT ( slotMatrix    ( ) ) );
  m_pContextMenu->insertItem ( tr ("Delete")         , this, SLOT ( slotDelete    ( ) ) );
  m_pContextMenu->insertSeparator ( );
  m_pContextMenu->insertItem ( tr ("Rotate 90")      , this, SLOT ( slotRotate90  ( ) ) );
  m_pContextMenu->insertItem ( tr ("Rotate 180")     , this, SLOT ( slotRotate180 ( ) ) );
  m_pContextMenu->insertItem ( tr ("Rotate 270")     , this, SLOT ( slotRotate270 ( ) ) );
  m_pContextMenu->insertSeparator ( );
  m_pContextMenu->insertItem ( qsShadow              , this, SLOT ( slotAddShadow ( ) ) );
  if ( shadow ( ) )
    m_pContextMenu->insertItem ( tr ("Delete Shadow"), this, SLOT ( slotDeleteShadow ( ) ) );

  m_pContextMenu->insertItem ( qsOverlay             , this, SLOT ( slotAddOverlay   ( ) ) );
  if ( overlay ( ) )
    m_pContextMenu->insertItem ( tr ("Delete Mask"),   this, SLOT ( slotDeleteOverlay( ) ) );

  //	m_pContextMenu->insertItem ( tr ("Add Frame")      , this, SLOT ( slotAddFrame  ( ) ) );
  //	m_pContextMenu->insertItem ( tr ("Add Text")       , this, SLOT ( slotAddText   ( ) ) );
  m_pContextMenu->insertSeparator ( );
  m_pContextMenu->insertItem ( tr ("Define as Button"), this, SLOT( slotDefineAsButton ( ) ) );
  // Blocking call.
  m_pContextMenu->exec(globalPos2, 6);
  delete pStackMenu;
  if ( m_pContextMenu )
    delete m_pContextMenu;
  m_pContextMenu = NULL;
  return true;
}

bool ImageObject::mouseMoveEvent (QMouseEvent *pEvent)
{
  // Slightly different behaviour for images.
  // if we stretch X and/or Y separately we'll mess rotation up
  // this is why we have to prevent this. 
  // So the stretch will be reset and ShearX will be used instead ScaleX if rotated.
  int iPos = 0;

  switch ( m_objectState ) {
  case MenuObject::StateScaleLeftX: 
  case MenuObject::StateScaleRightX: {
    iPos = pEvent->pos().x() - m_currentMousePos.x();
    double fZoom   = (double)( iPos / 100.0 );
    if (fZoom > 1.0)
      fZoom = fZoom * 2.0 - 1.0;

    if ( m_objectState == MenuObject::StateScaleLeftX )
      fZoom *= -1;
    if ( modifiers()->fRotate == 0.0 )
      modifiers( )->fScaleX *= ( 1.0 + fZoom );
    else
      modifiers( )->fShearX -= fZoom;
    update   ( );
  }
  break;
  case MenuObject::StateScaleTopY:
  case MenuObject::StateScaleBottomY: {
    iPos = pEvent->pos().y() - m_currentMousePos.y();
    double fZoom   = (double)( iPos / 100.0 );
    if (fZoom > 1.0)
      fZoom = fZoom * 2.0 - 1.0;

    if ( m_objectState == MenuObject::StateScaleTopY )
      fZoom *= -1;
    if ( modifiers()->fRotate == 0.0 )
      modifiers( )->fScaleY *= ( 1.0 + fZoom );
    else
      modifiers( )->fShearY += fZoom;
    update   ( );
  }
  break;
  case MenuObject::StateRotate:
    modifiers()->fScaleX = 1.0;
    modifiers()->fScaleY = 1.0;    
  default:
    return MenuObject::mouseMoveEvent ( pEvent );
  }
  m_currentMousePos = pEvent->pos();
  return false;
}


void ImageObject::slotEdit()
{
	mouseDoubleClickEvent(NULL);
}

void ImageObject::slotProperties ( )
{
  ImageManipulator *pMan= &manipulator();

  DialogGeometry *pGeometry = new DialogGeometry ( );
  pGeometry->initMe ( rect ( ), pMan );
  connect ( pGeometry->m_pButtonAnimation, SIGNAL ( clicked ( ) ), this, SLOT ( slotAnimateMe ( ) ) );

  if ( pGeometry->exec ( ) == QDialog::Accepted )  {
    DVDMenuUndoObject *pUndo = new DVDMenuUndoObject ( this, MenuObject::StateUnknown );
    int iX, iY, iWidth, iHeight;
    iX      = pGeometry->m_pEditX->text().toInt();
    iY      = pGeometry->m_pEditY->text().toInt();
    iWidth  = pGeometry->m_pEditWidth ->text().toInt();
    iHeight = pGeometry->m_pEditHeight->text().toInt();
    QRect rect ( iX, iY, iWidth, iHeight );

    ImageManipulator::colorKeying *pColorKey;
    ListViewColorItem *pItem;
    uint t;
    float  fDelta;
    Rgba   theColor;

    // First we should remove all entries in the color Key
    for ( t=0; t<pMan->listColorKeys.count ( ); t++ )
      delete pMan->listColorKeys[t];
    pMan->listColorKeys.clear ( );
    // And then we add whats currently in the widget.
    if ( pGeometry->m_pListViewCK->childCount ( ) )	{
      pItem = (ListViewColorItem *)pGeometry->m_pListViewCK->firstChild ( );
      while (pItem)	{
        theColor.fromString ( pItem->text ( 0 ) );	// The Color name is stored in the first text
        fDelta = pItem->text ( 1 ).toFloat ( );		// The delta value is stored in the second column.
        pColorKey = new ImageManipulator::colorKeying ( theColor.rgb ( ), fDelta );
        pMan->listColorKeys.append ( pColorKey );
        pItem = (ListViewColorItem *)pItem->itemBelow ();
      }
    }
    setRect ( rect );
    updatePixmap ( );
    updateShadow ( );
    emit ( signalUpdatePixmap ( ) );
    if   ( pUndo->hasChanged  ( ) )  {
      UndoBuffer *pBuffer = Global::pApp->getUndoBuffer ( );
      if ( pBuffer )
           pBuffer->push ( pUndo );
    }
    else
      delete pUndo;
  }
  delete pGeometry;
}

void ImageObject::slotAnimateMe ( )
{
  animationDialog ( animation ( ), DialogAnimation::TypeImage );
}

void ImageObject::slotRotate90()
{
	rotate (90.0);
}

void ImageObject::slotRotate180()
{
	rotate (180.0);
}

void ImageObject::slotRotate270()
{
	rotate (270.0);
}

void ImageObject::rotate(float fRot)
{
  QWMatrix theMatrix;
  theMatrix.rotate ( fRot );
  float fRotate = manipulator ( ).fRotate + fRot;
  if ( fRotate  > 180.0 )
       fRotate -= 360.0;
  manipulator ( ).fRotate = fRotate;
  m_image  = m_image.xForm    ( theMatrix );

  emit   ( signalUpdatePixmap ( ) );
  parent ( )->update ( );
}

void ImageObject::slotMatrix()
{
  if ( m_pMatrixDialog )
    return;

  m_pMatrixDialog = new DialogMatrix(MenuObject::parent());
  m_pMatrixDialog->initMe ( this );
  connect ( this, SIGNAL ( signalDeleteMe ( MenuObject * ) ), m_pMatrixDialog, SLOT ( slotDeleteObject ( MenuObject * ) ) );
  m_pMatrixDialog->show   ( );
}

void ImageObject::slotAddFrame()
{
	MessageBox::warning (NULL, QString ("Not Implemented yet."), QString ("Not Implemented yet."),
		QMessageBox::Ok ,  QMessageBox::Cancel);
}

void ImageObject::slotAddText()
{
	MessageBox::warning (NULL, QString ("Not Implemented yet."), QString ("Not Implemented yet."),
		QMessageBox::Ok ,  QMessageBox::Cancel);
}

void ImageObject::slotDefineAsButton ( )
{
  emit ( signalDefineAsButton ( this ) );
}

MenuObject *ImageObject::clone ( QWidget *pParent, MenuObject * )
{
  if ( !pParent )
    pParent = MenuObject::parent  ( );
  ImageObject *pNewObject = new ImageObject ( pParent );

  pNewObject->setImage ( image    ( ) );
  pNewObject->setFile  ( fileName ( ) );
  pNewObject->setZoom  ( zoom     ( ) );
  // The following two are not really needed, since we get those
  // informations solely from the Normal State - objects ...
  pNewObject->setRect        ( rect        ( ) );
  pNewObject->setManipulator ( manipulator ( ) );
  pNewObject->setAnimation   ( animation   ( ) );
  pNewObject->setHidden      ( hidden      ( ) );

  MenuObject *pShadow = shadow  ( );
  if ( pShadow )
       pShadow = pShadow->clone ( pParent, pNewObject );
  pNewObject->setShadow ( pShadow );

  MenuObject *pOverlay = overlay ( );
  if ( pOverlay )
       pOverlay = pOverlay->clone ( pParent, pNewObject );
  pNewObject->setOverlay ( pOverlay );

  return pNewObject;
}

// Called from xyz, when creating the AnimationAttribute objects, which hold the list of values for the attribute to be set.
// Note: only those AnimationAttribute objects are created which are modified during the animation (Menu - video).
AnimationAttribute *ImageObject::getSpecificAttributes (long iMaxNumberOfFrames, QString qsProperty)
{
  AnimationAttribute *pAnimAttr = NULL;

  if (qsProperty == "zoom")
    pAnimAttr = new AnimationAttribute (iMaxNumberOfFrames, qsProperty, manipulator ( ).fZoom, &manipulator ( ).fZoom);
  else if (qsProperty == "rotate")
    pAnimAttr = new AnimationAttribute (iMaxNumberOfFrames, qsProperty, manipulator ( ).fRotate, &manipulator ( ).fRotate);
  else if (qsProperty == "shear.x")
    pAnimAttr = new AnimationAttribute (iMaxNumberOfFrames, qsProperty, manipulator ( ).fShearX, &manipulator ( ).fShearX);
  else if (qsProperty == "shear.y")
    pAnimAttr = new AnimationAttribute (iMaxNumberOfFrames, qsProperty, manipulator ( ).fShearY, &manipulator ( ).fShearY);
  else if (qsProperty == "scale.x")
    pAnimAttr = new AnimationAttribute (iMaxNumberOfFrames, qsProperty, manipulator ( ).fScaleX, &manipulator ( ).fScaleX);
  else if (qsProperty == "scale.y")
    pAnimAttr = new AnimationAttribute (iMaxNumberOfFrames, qsProperty, manipulator ( ).fScaleY, &manipulator ( ).fScaleY);
  else if (qsProperty == "transparency")
    pAnimAttr = new AnimationAttribute (iMaxNumberOfFrames, qsProperty, manipulator ( ).fTransparency, &manipulator ( ).fTransparency);
  else if (qsProperty == "visibleFromTo.startFrame") {
    pAnimAttr = new AnimationAttribute (iMaxNumberOfFrames, qsProperty, manipulator ( ).iStartFrame, &manipulator ( ).iStartFrame);
    pAnimAttr->setNeedRefresh ( false );
  }
  else if (qsProperty == "visibleFromTo.stopFrame") {
    pAnimAttr = new AnimationAttribute (iMaxNumberOfFrames, qsProperty, manipulator ( ).iStopFrame, &manipulator ( ).iStopFrame);
    pAnimAttr->setNeedRefresh ( false );
  }
  //	else if (qsProperty == "backgroundFileName")
  //		pAnimAttr = new AnimationAttribute (iMaxNumberOfFrames, qsProperty, manipulator ( ).backgroundFileName, &manipulator ( ).backgroundFileName);
  //	else if (qsProperty == "showBackground")
  //		pAnimAttr = new AnimationAttribute (iMaxNumberOfFrames, qsProperty, manipulator ( ).bShowBackground, &manipulator ( ).bShowBackground);
  else if (qsProperty == "kenBurns.x1")
    pAnimAttr = new AnimationAttribute (iMaxNumberOfFrames, qsProperty, manipulator ( ).fKenBurnsX1, &manipulator ( ).fKenBurnsX1);
  else if (qsProperty == "kenBurns.y1")
    pAnimAttr = new AnimationAttribute (iMaxNumberOfFrames, qsProperty, manipulator ( ).fKenBurnsY1, &manipulator ( ).fKenBurnsY1);
  else if (qsProperty == "kenBurns.x2")
    pAnimAttr = new AnimationAttribute (iMaxNumberOfFrames, qsProperty, manipulator ( ).fKenBurnsX2, &manipulator ( ).fKenBurnsX2);
  else if (qsProperty == "kenBurns.y2")
    pAnimAttr = new AnimationAttribute (iMaxNumberOfFrames, qsProperty, manipulator ( ).fKenBurnsY2, &manipulator ( ).fKenBurnsY2);
  //	else if (qsProperty == "res.x")
  //		pAnimAttr = new AnimationAttribute (iMaxNumberOfFrames, qsProperty, manipulator ( ).iXRes, &manipulator ( ).iXRes);
  //	else if (qsProperty == "res.y")
  //		pAnimAttr = new AnimationAttribute (iMaxNumberOfFrames, qsProperty, manipulator ( ).iYRes, &manipulator ( ).iYRes);
  //	else if (qsProperty == "aspectRatio")
  //		pAnimAttr = new AnimationAttribute (iMaxNumberOfFrames, qsProperty, manipulator ( ).fAspectRatio, &manipulator ( ).fAspectRatio);
  else if (qsProperty == "blendColor.red")
    pAnimAttr = new AnimationAttribute (iMaxNumberOfFrames, qsProperty, manipulator ( ).fRed, &manipulator ( ).fRed);
  else if (qsProperty == "blendColor.green")
    pAnimAttr = new AnimationAttribute (iMaxNumberOfFrames, qsProperty, manipulator ( ).fGreen, &manipulator ( ).fGreen);
  else if (qsProperty == "blendColor.blue")
    pAnimAttr = new AnimationAttribute (iMaxNumberOfFrames, qsProperty, manipulator ( ).fBlue, &manipulator ( ).fBlue);
  else if (qsProperty == "brightness")
    pAnimAttr = new AnimationAttribute (iMaxNumberOfFrames, qsProperty, manipulator ( ).fBrightness, &manipulator ( ).fBrightness);
  else if (qsProperty == "fileName")
    pAnimAttr = new AnimationAttribute (iMaxNumberOfFrames, qsProperty, m_qsFileName, &m_qsFileName);

  return pAnimAttr;
}

