/****************************************************************************
** Form implementation generated from reading ui file 'dialogundostack.ui'
**
**   Created : Tue Dec 21 09:13:25 2004
**        by : The User Interface Compiler ($Id: dialogundostack.cpp,v 1.72 2009/09/22 11:37:19 qdvdauthor Exp $)
** Copyright : (c) Varol Okan
**   License : GPL v 2.0
**
** WARNING! All changes made in this file will be lost!
****************************************************************************/

#include <qvariant.h>
#include <qpushbutton.h>
#include <qheader.h>
#include <qlayout.h>
#include <qtooltip.h>
#include <qwhatsthis.h>
#include <qimage.h>
#include <qpixmap.h>
#include <qtimer.h>

#include "global.h"
#include "qdvdauthor.h"
#include "messagebox.h"
#include "dialogundostack.h"
#include "dvdmenuundoobject.h"
#include "menuobject.h"

bool DialogUndoStack::Item::m_bStartAdjust = true;

DialogUndoStack::Item::Item ( QListView *pListView, int iCounter, bool bAlt, QString qsType, QString qsName )
  : QListViewItem ( pListView, qsType, "", qsName )
{
  m_bAlternateColor = bAlt;
  m_iCounter        = iCounter;
  if ( iCounter != 0 )
    setExpandable ( true );
}

DialogUndoStack::Item::Item ( QListViewItem *pItem, int iCounter, bool bAlt, QString qsType, QString qsName )
  : QListViewItem ( pItem, qsType, QString ( "%1" ).arg ( iCounter ), qsName )
{
  m_bAlternateColor = bAlt;
  m_iCounter        = iCounter;
}

DialogUndoStack::Item::~Item ( )
{
}

bool DialogUndoStack::Item::alternate ( )
{
  return m_bAlternateColor;
}

int DialogUndoStack::Item::counter ( )
{
  return m_iCounter;
}

void DialogUndoStack::Item::paintCell ( QPainter *pPainter, const QColorGroup &colorGroup, int iColumn, int iWidth, int iAlign )
{
  bool bAlternateColor =   m_bAlternateColor;
  if ( DialogUndoStack::Item::m_bStartAdjust )
       bAlternateColor = ! m_bAlternateColor;

  QColorGroup theColors = colorGroup;
  if ( isSelected ( ) )   {
    theColors.setColor ( QColorGroup::Base, theColors.highlight       ( ) );
    theColors.setColor ( QColorGroup::Text, theColors.highlightedText ( ) );
  }
  QColor colorAlternate ( 255, 255, 150 );
  if ( m_iCounter == 0 )
       colorAlternate  = QColor ( 135, 235, 135 );
  else if ( bAlternateColor )   // every second file we change the color slightly
       colorAlternate  = QColor ( 230, 230, 255 );
  theColors.setColor ( QColorGroup::Base, colorAlternate );
  QListViewItem::paintCell ( pPainter, theColors, iColumn, iWidth, iAlign );
}

DialogUndoStack::DialogUndoStack ( UndoBuffer *pUndoBuffer, QWidget* parent, const char* name, bool, WFlags fl)
    : QDialog ( parent, name, FALSE, fl )
{
  m_pUndoBuffer = pUndoBuffer;
  m_pBaseItem   = NULL;
  m_iCounter    = 0;
  if ( !name )
    setName ( "DialogUndoStack" );
  setSizeGripEnabled ( TRUE );
  DialogUndoStackLayout = new QGridLayout( this, 1, 1, 11, 6, "DialogUndoStackLayout"); 

  Layout1 = new QHBoxLayout( 0, 0, 6, "Layout1"); 

  buttonHelp = new QPushButton( this, "buttonHelp" );
  buttonHelp->setAutoDefault( TRUE );
  Layout1->addWidget( buttonHelp );
  Horizontal_Spacing2 = new QSpacerItem( 20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum );
  Layout1->addItem( Horizontal_Spacing2 );

  buttonOk = new QPushButton( this, "buttonOk" );
  buttonOk->setAutoDefault( TRUE );
  buttonOk->setDefault( TRUE );
  Layout1->addWidget( buttonOk );

  buttonCancel = new QPushButton( this, "buttonCancel" );
  buttonCancel->setAutoDefault( TRUE );
  Layout1->addWidget( buttonCancel );

  DialogUndoStackLayout->addLayout( Layout1, 1, 0 );

  m_pListViewUndo = new QListView ( this, "m_pListViewUndo" );
  m_pListViewUndo->setSorting ( -1 );
  m_pListViewUndo->addColumn  ( tr ( "Type"   ) );
  m_pListViewUndo->addColumn  ( tr ( "Nr"     ) );
  m_pListViewUndo->addColumn  ( tr ( "Object" ) );
  m_pListViewUndo->setRootIsDecorated  ( TRUE );

  DialogUndoStackLayout->addWidget( m_pListViewUndo, 0, 0 );
  languageChange();
  resize( QSize(200, 350).expandedTo(minimumSizeHint()) );
  clearWState ( Qt::WState_Polished );
//    clearWState ( Qt::WStyle_StaysOnTop );
    // signals and slots connections
  connect ( m_pListViewUndo, SIGNAL ( clicked ( QListViewItem *, const QPoint &, int ) ), this, SLOT ( slotClickedOnList ( QListViewItem *, const QPoint &, int ) ) );
  connect ( buttonOk,     SIGNAL ( clicked ( ) ), this, SLOT ( accept ( ) ) );
  connect ( buttonCancel, SIGNAL ( clicked ( ) ), this, SLOT ( reject ( ) ) );
}

DialogUndoStack::~DialogUndoStack ( )
{
    // no need to delete child widgets, Qt does it all for us
}

void DialogUndoStack::languageChange ( )
{
  setCaption             ( tr ( "Undo Stack" ) );
  buttonCancel->setText  ( tr ( "&Cancel"    ) );
  buttonHelp->setText    ( tr ( "&Help"      ) );
  buttonOk->setText      ( tr ( "&OK"        ) );
  buttonHelp->setAccel   ( QKeySequence ( tr ( "F1" ) ) );
  buttonOk->setAccel     ( QKeySequence ( QString::null ) );
  buttonCancel->setAccel ( QKeySequence ( QString::null ) );
  m_pListViewUndo->clear ( );
}

void DialogUndoStack::setUndoBuffer ( UndoBuffer *pUndoBuffer )
{
  m_pUndoBuffer  = pUndoBuffer;
  buildUndoTree ( );
}

void DialogUndoStack::buildUndoTree ( )
{
  int iContentsY = m_pListViewUndo->contentsY ( );
  m_pListViewUndo->clear ( );
  if ( ! m_pUndoBuffer )
    return;

  if ( ( m_pUndoBuffer->count ( ) < 1 ) && ( m_pUndoBuffer->countRedo ( ) < 1 ) )
    return;

  int t, iCounter, iMaxUndoSize, iMaxRedoSize;
  UndoObject *pUndoObject, *pNextUndoObject;
  m_bAlternate = true; //startAlternae ( );// ( m_pUndoBuffer->countRedo ( ) + m_pUndoBuffer->count ( ) )%1;

  // Here we ensure that only the specified depth is shown
  iMaxUndoSize = MAX_TREE_SIZE - m_pUndoBuffer->countRedo();
  if ( iMaxUndoSize < 0 )
       iMaxUndoSize = 0;
  else if ( iMaxUndoSize > (int)m_pUndoBuffer->count ( ) )
       iMaxUndoSize = m_pUndoBuffer->count ( );

  // Since the ListView adds reverted, we have to start with the redo - steps.
//  if ( iMaxUndoSize == 0 )
//    iMaxRedoSize = MAX_TREE_SIZE;
//  else
  iMaxRedoSize = m_pUndoBuffer->countRedo ( );

//printf ( "%s::%d > undo<%d>  maxUndo<%d> redo<%d> maxRedo<%d>\n", __FILE__, __LINE__, m_pUndoBuffer->count ( ), iMaxUndoSize, m_pUndoBuffer->countRedo ( ), iMaxRedoSize );

  iCounter = -iMaxRedoSize;
  m_pBaseItem = NULL;
  for ( t=0; (int)t<iMaxRedoSize; t++ )  {
    pNextUndoObject = NULL;
    pUndoObject     = m_pUndoBuffer->atRedo ( t );
    if ( t < iMaxRedoSize - 1 )
         pNextUndoObject = m_pUndoBuffer->atRedo ( t + 1 );
    appendUndoStep ( pUndoObject, pNextUndoObject, iCounter++ );
  }  setUpdatesEnabled( FALSE );


  Item *pCurrent = new Item ( m_pListViewUndo, 0, "", tr ( "Current Pos" ) ); //, tr ( "Position" ) );

  iCounter = 1;
  m_pBaseItem = NULL;
  for ( t=iMaxUndoSize-1; t>=0; t-- )  {
    pNextUndoObject = NULL;
    pUndoObject     = m_pUndoBuffer->at ( t );
    if ( t > 0 )
      pNextUndoObject = m_pUndoBuffer->at ( t - 1 );
    appendUndoStep ( pUndoObject, pNextUndoObject, iCounter++ );
  }
  Item *pItem = (Item *)m_pListViewUndo->firstChild ( );
  DialogUndoStack::Item::m_bStartAdjust = pItem->alternate ( );

  if ( iContentsY > 0 )
    m_pListViewUndo->scrollBy ( 0, iContentsY );

  if ( ( pCurrent->itemPos ( ) <= m_pListViewUndo->contentsY ( ) ) ||
       ( pCurrent->itemPos ( ) >= m_pListViewUndo->contentsY ( ) + m_pListViewUndo->visibleHeight ( ) - 20 ) )  {
    m_pListViewUndo->ensureItemVisible( pCurrent );
  }
}

void DialogUndoStack::appendUndoStep ( UndoObject *pUndoObject, UndoObject *pNextUndoObject, int iCounter )
{
  QString qsUndoType, qsName, qsEmpty;
  MenuObject *pMenuObject, *pNextMenuObject;

  pMenuObject     = NULL;
  pNextMenuObject = NULL;
  qsName          = QString ( );
  qsUndoType      = tr ( "Undefined" );
  if ( pUndoObject )  {  // Here we check if have gotten MenuObject
    qsUndoType = pUndoObject->getTypeString ( );
    pMenuObject = ((DVDMenuUndoObject *)pUndoObject)->getMenuObject ( );
    if ( pMenuObject ) // All's fine, lets set the name ...
      qsName = pMenuObject->name ( );
  }

  // Next step is to look forward to be able to determine if we should group things together ...
  if ( pNextUndoObject )
       pNextMenuObject = ((DVDMenuUndoObject *)pNextUndoObject)->getMenuObject ( );

  // The next step is to figure out if the current MenuObject is the same as the previous
  // in which case we want to group them together

  // First let us see if we have the BaseItem set, meaning we previously found curr and prev belong together
  if ( m_pBaseItem )
       new Item ( m_pBaseItem, iCounter, m_bAlternate, qsUndoType, qsEmpty );
  else if ( pMenuObject == pNextMenuObject )  {
    // Otherwise, if no masterItem set and the ext is the same as this, let us create one masterItem
    if ( ! m_pBaseItem )  {
      m_pBaseItem = new Item ( m_pListViewUndo, iCounter, m_bAlternate, qsEmpty, qsName );
      m_pBaseItem->setOpen ( true );
    }
    new Item ( m_pBaseItem, iCounter, m_bAlternate, qsUndoType, qsEmpty );
  }
  // In case the next item is different we still want to attach the current item to the MasterItem
  if ( pMenuObject != pNextMenuObject )  {
    if ( ! m_pBaseItem )  // This one is only generated if we not already added it in the first if - case
      new Item ( m_pListViewUndo, iCounter, m_bAlternate, qsUndoType, qsName );
    m_pBaseItem  = NULL;
    m_bAlternate = ! m_bAlternate;
  }
}

void DialogUndoStack::slotClickedOnList ( QListViewItem *pViewItem, const QPoint &, int )
{
  if ( ( ! pViewItem ) || ( m_iCounter > 0 ) )
    return;
  // In order to go to any particular pos we ought to calc the delta between the current and the undo or redo pos
  Item *pItem  = (Item *)pViewItem;
  int  iDelta  = pItem->counter ( );
  if ( iDelta == 0 )
    return;

  // Undo is handled slightly different if we click on a header.
  if ( ( iDelta > 0 ) && ( pItem->text ( 1 ) == "" ) )  {
    pItem  = (Item *)pItem->firstChild ( );
    if ( pItem )
         iDelta = pItem->counter ( );
  }

  if ( m_pUndoBuffer )  {
    m_iCounter = iDelta;
    if  ( iDelta > 0 )
          slotUndo ( );
    else  {
      m_iCounter = -iDelta;
      slotRedo ( );
    }
  }
}

void DialogUndoStack::slotUndo ( )
{
  if ( ! Global::pApp->pleaseWait ( ) )  {
    Global::pApp->editUndo ( );
    m_iCounter--;
  }
  if ( m_iCounter > 0 )
       QTimer::singleShot ( 200, this, SLOT ( slotUndo ( ) ) );
}

void DialogUndoStack::slotRedo ( )
{
  if ( ! Global::pApp->pleaseWait ( ) )  {
    Global::pApp->editRedo ( );
    m_iCounter--;
  }
  if ( m_iCounter > 0 )
       QTimer::singleShot ( 200, this, SLOT ( slotRedo ( ) ) );
}

