/* This file is part of the KDE project
   Copyright (C) 1998, 1999 Reginald Stadlbauer <reggie@kde.org>
   Copyright (C) 2001 Lukas Tinkl <lukas@kde.org>
   Copyright (C) 2002 Ariya Hidayat <ariya@kde.org>
   Copyright (C) 2005 Laurent Montel <montel@kde.org>
   Copyright (C) 2005 Thorsten Zachmann <zachmann@kde.org>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
*/

#include <tqheader.h>
#include <tqtimer.h>
#include <tqpopupmenu.h>
#include <tqimage.h>
#include <tqtabwidget.h>
#include <tqtooltip.h>

#include <kwordwrap.h>
#include <tdemessagebox.h>
#include <tdelocale.h>
#include <kinputdialog.h>
#include <knotifyclient.h>
#include <kiconview.h>
#include <kdebug.h>

#include "KPrSideBar.h"
#include "KPrView.h"
#include "KPrDocument.h"
#include "KPrCanvas.h"
#include "KPrPage.h"
#include "KPrObject.h"
#include <tqapplication.h>
#include "KPrCommand.h"
#include <tqvalidator.h>
#include "KPrFreehandObject.h"
#include "KPrBezierCurveObject.h"
#include "KPrTextObject.h"
#include "KPrPolylineObject.h"
#include "KPrClosedLineObject.h"
#include "KPrGroupObject.h"


TQValidator::State KPrRenamePageValidator::validate( TQString & input, int& ) const
{
  TQString str = input.stripWhiteSpace();
  if ( str.isEmpty() ) // we want to allow empty titles. Empty == automatic.
    return Acceptable;

  if ( mStringList.find( str ) == mStringList.end() )
    return Acceptable;
  else
    return Intermediate;
}

class ThumbToolTip : public TQToolTip
{
public:
    ThumbToolTip( KPrThumbBar *parent )
        : TQToolTip( parent->viewport() )
        , m_thumbBar( parent )
        {}

protected:
    void maybeTip(const TQPoint &pos)
    {
        TQString title;
        TQRect r( m_thumbBar->tip( pos, title ) );
        if (!r.isValid())
            return;

        tip(r, title);
    }
private:
    KPrThumbBar *m_thumbBar;
};


class OutlineSlideItem: public TDEListViewItem
{
public:
    OutlineSlideItem( TDEListView * parent, KPrPage* page, bool _masterPage );
    OutlineSlideItem( TDEListView * parent, OutlineSlideItem *after, KPrPage* page, bool _masterPage );

    KPrPage* page() const { return m_page; }

    void setPage( KPrPage* p );

    void update();
    void updateTitle();

private:
    KPrPage* m_page;
    bool m_masterPage;
};

class OutlineObjectItem: public TDEListViewItem
{
public:
    OutlineObjectItem( OutlineSlideItem * parent, KPrObject* object,
                       const TQString& name = TQString() );

    KPrObject* object() const { return m_object; }

    void setObject( KPrObject* o );

private:
    KPrObject* m_object;
};

class ThumbItem : public TQIconViewItem
{
public:
    ThumbItem( TQIconView *parent, const TQString & text, const TQPixmap & icon )
        : TQIconViewItem( parent, text, icon )
        { uptodate = true; }
    ThumbItem( TQIconView *parent, TQIconViewItem *after, const TQString & text, const TQPixmap & icon )
        : TQIconViewItem( parent, after, text, icon )
        { uptodate = true; }

    virtual bool isUptodate() { return uptodate; };
    virtual void setUptodate( bool _uptodate) { uptodate = _uptodate; };

private:
    bool uptodate;
};

KPrSideBar::KPrSideBar(TQWidget *parent, KPrDocument *d, KPrView *v)
    :TQTabWidget(parent), m_doc(d), m_view(v)
{
    setTabPosition(TQTabWidget::Top);
    setTabShape(TQTabWidget::Triangular);

    m_outline = new KPrOutline(this, m_doc, m_view);
    addTab(m_outline, i18n("Structure of the presentation", "Outline"));

    m_thb = new KPrThumbBar(this, m_doc, m_view);
    addTab(m_thb,i18n("Preview"));


    //TODO find a better way
    connect(m_outline, TQ_SIGNAL(showPage(int)),
            this, TQ_SIGNAL(showPage(int)));

    connect(m_thb, TQ_SIGNAL(showPage(int)),
            this, TQ_SIGNAL(showPage(int)));

    connect(m_outline, TQ_SIGNAL(movePage(int,int)),
            this, TQ_SIGNAL(movePage(int,int)));

    connect(m_outline, TQ_SIGNAL(selectPage(int,bool)),
            this, TQ_SIGNAL(selectPage(int,bool)));

    connect(this, TQ_SIGNAL(currentChanged(TQWidget *)),
            this, TQ_SLOT(currentChanged(TQWidget *)));

}

void KPrSideBar::currentChanged(TQWidget *tab)
{
    if (tab == m_thb) {
        if (!m_thb->uptodate && m_thb->isVisible())
            m_thb->rebuildItems();
        else
            m_thb->refreshItems();
    }
}

void KPrSideBar::addItem( int pos )
{
    m_outline->addItem( pos );
    m_thb->addItem( pos );
}

void KPrSideBar::moveItem( int oldPos, int newPos )
{
    m_outline->moveItem( oldPos, newPos );
    m_thb->moveItem( oldPos, newPos );
}

void KPrSideBar::removeItem( int pos )
{
    m_outline->removeItem( pos );
    m_thb->removeItem( pos );
}

void KPrSideBar::updateItem( KPrPage *page )
{
    bool sticky = false;
    int pos = 0;
    if ( page == m_doc->masterPage() )
    {
        pos = -1;
        sticky = true;
    }
    else
    {
        pos = m_doc->pageList().findRef( page );
    }

    m_outline->updateItem( pos, sticky );
    m_thb->updateItem( pos, sticky );
}

void KPrSideBar::setViewMasterPage( bool _masterPage )
{
    m_outline->setViewMasterPage( _masterPage );
    m_thb->setViewMasterPage( _masterPage );
    m_outline->rebuildItems();
    m_thb->rebuildItems();
}

KPrSideBarBase::KPrSideBarBase(KPrDocument *_doc, KPrView *_view)
    : m_doc( _doc ), m_view( _view ), m_viewMasterPage( false )
{
}

void KPrSideBarBase::setViewMasterPage( bool _b )
{
    m_viewMasterPage = _b;
}

KPrThumbBar::KPrThumbBar(TQWidget *parent, KPrDocument *d, KPrView *v)
    :TDEIconView(parent), KPrSideBarBase( d,v)
{
    uptodate = false;
    m_offsetX = 0;
    m_offsetY = 0;

    setArrangement(TQIconView::LeftToRight);
    setAutoArrange(true);
    setSorting(false);
    setItemsMovable(false);
    setResizeMode(TQIconView::Adjust);

    m_thumbTip = new ThumbToolTip(this);

    connect(this, TQ_SIGNAL(currentChanged(TQIconViewItem *)),
            this, TQ_SLOT(itemClicked(TQIconViewItem *)));
    connect(this, TQ_SIGNAL(contentsMoving(int, int)),
            this, TQ_SLOT(slotContentsMoving(int, int)));
}

KPrThumbBar::~KPrThumbBar()
{
    delete m_thumbTip;
}

void KPrThumbBar::setCurrentPage( int pg )
{
    for ( TQIconViewItem *it = firstItem(); it; it = it->nextItem() )
    {
        if ( it->text().toInt() - 1 == pg ) {
            blockSignals( true );
            setCurrentItem( it );
            setSelected( it, false ); // to avoid the blue "selected"-mark
            ensureItemVisible(it);
            refreshItems();
            blockSignals( false );
            return;
        }
    }
}

TQRect KPrThumbBar::tip(const TQPoint &pos, TQString &title)
{
    TQIconViewItem *item = findItem(viewportToContents(pos));
    if (!item)
        return TQRect(0, 0, -1, -1);

    int pagenr =  item->index();
    title = m_doc->pageList().at(pagenr)->pageTitle();

    TQRect r = item->pixmapRect(false);
    r = TQRect(contentsToViewport(TQPoint(r.x(), r.y())), TQSize(r.width(), r.height()));
    return r;
}

void KPrThumbBar::rebuildItems()
{
    kdDebug()<<" void KPrThumbBar::rebuildItems() beofre \n";
    if( !isVisible())
        return;
    kdDebug(33001) << "KPrThumbBar::rebuildItems" << endl;

    TQApplication::setOverrideCursor( TQt::waitCursor );

    clear();
    if ( m_viewMasterPage )
    {
    }
    else
    {
        for ( unsigned int i = 0; i < m_doc->getPageNums(); i++ ) {
            // calculate the size of the thumb
            TQRect rect = m_doc->pageList().at(i)->getZoomPageRect( );

            int w = rect.width();
            int h = rect.height();
            if ( w > h ) {
                w = 130;
                float diff = (float)rect.width() / (float)w;
                h = (int) (rect.height() / diff);
                if ( h > 120 ) {
                    h = 120;
                    float diff = (float)rect.height() / (float)h;
                    w = (int) (rect.width() / diff);
                }
            }
            else if ( w < h ) {
                h = 130;
                float diff = (float)rect.height() / (float)h;
                w = (int) (rect.width() / diff);
                if ( w > 120 ) {
                    w = 120;
                    float diff = (float)rect.width() / (float)w;
                    h = (int) (rect.height() / diff);
                }
            }
            else if ( w == h ) {
                w = 130;
                h = 130;
            }

            // draw an empty thumb
            TQPixmap pix(w, h);
            pix.fill( TQt::white );

            TQPainter p(&pix);
            p.setPen(TQt::black);
            p.drawRect(pix.rect());

            ThumbItem *item = new ThumbItem(static_cast<TQIconView *>(this), TQString::number(i+1), pix);
            item->setUptodate( false );
            item->setDragEnabled(false);  //no dragging for now
        }

        TQTimer::singleShot( 10, this, TQ_SLOT( slotRefreshItems() ) );
    }
    uptodate = true;

    TQApplication::restoreOverrideCursor();
}

void KPrThumbBar::refreshItems(bool offset)
{
    TQRect vRect = visibleRect();
    if ( offset )
        vRect.moveBy( m_offsetX, m_offsetY );
    else
        vRect.moveBy( contentsX(), contentsY() );

    TQIconViewItem *it = findFirstVisibleItem( vRect );
    while ( it )
    {
        kdDebug(33001) << "visible page = " << it->text().toInt() << endl;
        if ( ! dynamic_cast<ThumbItem *>(it)->isUptodate( ) ){
            //todo refresh picture
            it->setPixmap( getSlideThumb( it->text().toInt() - 1 ) );
            static_cast<ThumbItem *>(it)->setUptodate( true );
        }

        if ( it == findLastVisibleItem( vRect ) )
            break;
        it = it->nextItem();
    }

    m_offsetX = 0;
    m_offsetY = 0;
}

void KPrThumbBar::updateItem( int pagenr /* 0-based */, bool sticky )
{
    if ( m_viewMasterPage )
        return;
    if ( !uptodate )
        return;
    int pagecnt = 0;
    // calculate rect of visible objects
    TQRect vRect = visibleRect();
    vRect.moveBy( contentsX(), contentsY() );

    // Find icon
    TQIconViewItem *it = firstItem();
    do
    {
        if ( it == findFirstVisibleItem( vRect ) ) {
            do
            {
                if ( sticky || it->text().toInt() == pagenr + 1 ) {
                    it->setPixmap(getSlideThumb( pagecnt ));
                    static_cast<ThumbItem *>(it)->setUptodate( true );

                    if ( !sticky )
                        return;
                }
                if ( it == findLastVisibleItem( vRect ) )
                    break;
                pagecnt++;
                it = it->nextItem();
            } while ( true );
        }
        else if ( sticky || it->text().toInt() == pagenr + 1 ) {
            static_cast<ThumbItem *>(it)->setUptodate( false );
            if ( !sticky )
                return;
        }
        pagecnt++;
        it = it->nextItem();
    } while ( it );

    if ( ! sticky )
        kdWarning(33001) << "Item for page " << pagenr << " not found" << endl;
}

// add a thumb item without recreating all thumbs
void KPrThumbBar::addItem( int pos )
{
    kdDebug(33001)<< "KPrThumbBar::addItem" << endl;
    int page = 0;
    for ( TQIconViewItem *it = firstItem(); it; it = it->nextItem() ) {
        // find page which should move
        // do stuff because a item can not be insert at the beginning
        if ( pos == 0 && page == pos ){
            ThumbItem *item = new ThumbItem(static_cast<TQIconView *>(this), it, TQString::number(2), getSlideThumb(1));
            item->setDragEnabled(false);  //no dragging for now
            it->setPixmap(getSlideThumb( 0 ));
            // move on to next item as we have inserted one
            it = it->nextItem();
        }
        else if ( (page + 1) == pos ) {
            ThumbItem *item = new ThumbItem(static_cast<TQIconView *>(this), it, TQString::number(pos+1), getSlideThumb(pos));
            item->setDragEnabled(false);  //no dragging for now
            it = it->nextItem();
        }
        // update page numbers
        if ( page >= pos )
            it->setText( TQString::number(page+2) );
        page++;
    }
}

// moves a item without recreating all pages
void KPrThumbBar::moveItem( int oldPos, int newPos )
{
    kdDebug(33001)<< "KPrThumbBar::moveItem " << oldPos << " to " << newPos << endl;
    int page = 0;
    TQIconViewItem *after = 0;
    TQIconViewItem *take = 0;
    for ( TQIconViewItem *it = firstItem(); it; it = it->nextItem() ) {
        // find page which should move
        if ( page == oldPos )
            take = it;
        // find position where page should be insert
        // as a page can not be insert at the beginning get the first one
        // the page to get depends on if a page is moved forward / backwards
        if ( page == newPos )
            after = page == 0 ? it : newPos > oldPos ? it : it->prevItem();
        page++;
    }

    if ( ! take )
        return;

    // workaround for a bug in qt 3.1.1 insertItem dose not work.
    // TODO remove workaround when qt 3.1.2 comes out tz
    //takeItem( take );
    //insertItem( take, after);
    ThumbItem *item = new ThumbItem( static_cast<TQIconView *>(this), after, TQString::number( newPos ), *(take->pixmap()) );
    item->setDragEnabled(false);  //no dragging for now
    delete take;
    // update the thumbs if new pos was 0
    // because it was insert after the first one
    if ( newPos == 0 ) {
        //todo do not recreate the pics
        after->setPixmap(getSlideThumb( 0 ));
        //take->setPixmap(getSlideThumb( 1 ));
        item->setPixmap(getSlideThumb( 1 ));
    }

    //write the new page numbers
    int lowPage = oldPos > newPos ? newPos : oldPos;
    int highPage = oldPos < newPos ? newPos : oldPos;
    page = 0;
    for ( TQIconViewItem *it = firstItem(); it; it = it->nextItem() ) {
        if ( page >= lowPage && page <= highPage)
            it->setText( TQString::number(page+1) );
        page++;
    }
}

void KPrThumbBar::removeItem( int pos )
{
    kdDebug(33001)<< "KPrThumbBar::removeItem" << endl;
    int page = 0;
    bool change = false;
    TQIconViewItem *itemToDelete = 0;

    for ( TQIconViewItem *it = firstItem(); it; it = it->nextItem() ) {
        if ( page == pos ) {
            itemToDelete = it;
            if ( it->nextItem() )
                it = it->nextItem();
            change = true;
        }
        if ( change )
            it->setText( TQString::number( page + 1 ) );
        page++;
    }
    delete itemToDelete;
}

TQPixmap KPrThumbBar::getSlideThumb(int slideNr) const
{
    //kdDebug(33001) << "KPrThumbBar::getSlideThumb: " << slideNr << endl;
    TQPixmap pix( 10, 10 );

    m_view->getCanvas()->drawPageInPix( pix, slideNr, 60 );

    int w = pix.width();
    int h = pix.height();

    if ( w > h ) {
        w = 130;
        h = 120;
    }
    else if ( w < h ) {
        w = 120;
        h = 130;
    }
    else if ( w == h ) {
        w = 130;
        h = 130;
    }

    const TQImage img(pix.convertToImage().smoothScale( w, h, TQImage::ScaleMin ));
    pix.convertFromImage(img);

    // draw a frame around the thumb to show its size
    TQPainter p(&pix);
    p.setPen(TQt::black);
    p.drawRect(pix.rect());

    return pix;
}

void KPrThumbBar::itemClicked(TQIconViewItem *i)
{
    if ( !i )
        return;
    emit showPage( i->index() );
}

void KPrThumbBar::slotContentsMoving(int x, int y)
{
    m_offsetX = x;
    m_offsetY = y;
    kdDebug(33001) << "offset x,y = " << x << ", " << y << endl;
    refreshItems( true );
}

void KPrThumbBar::slotRefreshItems()
{
    refreshItems();
}

OutlineSlideItem::OutlineSlideItem( TDEListView* parent, KPrPage* _page, bool _masterPage )
    : TDEListViewItem( parent ), m_page( _page ), m_masterPage( _masterPage )
{
    setDragEnabled(true);
    setPage( _page );
    setPixmap( 0, KPBarIcon( "slide" ) );
}

OutlineSlideItem::OutlineSlideItem( TDEListView* parent, OutlineSlideItem * after, KPrPage* _page, bool _masterPage )
    : TDEListViewItem( parent, after ), m_page( _page ), m_masterPage( _masterPage )
{
    setDragEnabled(true);
    setPage( _page );
    setPixmap( 0, KPBarIcon( "slide" ) );
}

void OutlineSlideItem::setPage( KPrPage* p )
{
    if( !p ) return;
    m_page = p;
    update();
}

void OutlineSlideItem::update()
{
    if( !m_page ) return;
    KPrDocument *doc = m_page->kPresenterDoc();
    updateTitle();

    // add all objects
    OutlineObjectItem *ooi = 0;
    while ( ( ooi = dynamic_cast<OutlineObjectItem*>( this->firstChild() ) ) )
        delete ooi;

    // keep selected object
    ooi = 0;

    TQPtrListIterator<KPrObject> it( m_page->objectList() );

    if ( !m_masterPage )
    {
        for ( ; it.current(); ++it ) {
            OutlineObjectItem *item = new OutlineObjectItem( this, it.current() );
            item->setDragEnabled( false );
            if ( it.current()->isSelected() )
                ooi = item;
        }
    }
    else
    {
        KPrObject* header = 0;
        KPrObject* footer = 0;

        // add sticky objects, exclude header and footer
        it = doc->masterPage()->objectList();
        for ( ; it.current() ; ++it )
        {
            KPrObject* object = it.current();

            if( m_page->hasHeader() && doc->isHeader( object ) )
                header = object;
            else if( m_page->hasFooter() && doc->isFooter( object ) )
                footer = object;
            else if( !doc->isHeader( object ) && !doc->isFooter( object ) ) {
                OutlineObjectItem *item = new OutlineObjectItem( this, object );
                if ( object->isSelected() )
                    ooi = item;
            }

        }

        // add header and footer (if any)
        if ( footer ) {
            OutlineObjectItem *item = new OutlineObjectItem( this, footer, i18n("Footer") );
            if ( footer->isSelected() )
                ooi = item;
        }

        if ( header ) {
            OutlineObjectItem *item = new OutlineObjectItem( this, header, i18n("Header") );
            if ( header->isSelected() )
                ooi = item;
        }
    }

    // select selected object the page is necessary that a
    // sticky object is selected on the active page
    if ( ooi && doc->activePage() == m_page )
        (ooi->listView())->setSelected( ooi, true );
}

void OutlineSlideItem::updateTitle()
{
    TQString title = m_page->pageTitle();
    if ( ! m_page->isSlideSelected() )
        title = i18n( "(%1)" ).arg( title );
    setText( 0, title );
}

OutlineObjectItem::OutlineObjectItem( OutlineSlideItem* parent, KPrObject* _object,
                                      const TQString& name )
    : TDEListViewItem( parent ), m_object( _object )
{
    setObject( m_object );
    setDragEnabled( false );

    TQString objectName = name.isEmpty() ? m_object->getObjectName() : name;
    //if( sticky ) objectName += i18n(" (Sticky)" );
    setText( 0, objectName );
}

void OutlineObjectItem::setObject( KPrObject* object )
{
    if( !object ) return;
    m_object = object;

    switch ( m_object->getType() ) {
    case OT_PICTURE:
        setPixmap( 0, KPBarIcon( "frame_image" ) );
        break;
    case OT_LINE:
        setPixmap( 0, KPBarIcon( "mini_line" ) );
        break;
    case OT_RECT:
        setPixmap( 0, KPBarIcon( "mini_rect" ) );
        break;
    case OT_ELLIPSE:
        setPixmap( 0, KPBarIcon( "mini_circle" ) );
        break;
    case OT_TEXT:
        setPixmap( 0, KPBarIcon( "frame_text" ) );
        break;
    case OT_AUTOFORM:
        setPixmap( 0, KPBarIcon( "mini_autoform" ) );
        break;
    case OT_CLIPART:
        setPixmap( 0, KPBarIcon( "mini_clipart" ) );
        break;
    case OT_PIE:
        setPixmap( 0, KPBarIcon( "mini_pie" ) );
        break;
    case OT_PART:
        setPixmap( 0, KPBarIcon( "frame_query" ) );
        break;
    case OT_FREEHAND:
        setPixmap( 0, KPBarIcon( "freehand" ) );
        break;
    case OT_POLYLINE:
        setPixmap( 0, KPBarIcon( "polyline" ) );
        break;
    case OT_QUADRICBEZIERCURVE:
        setPixmap( 0, KPBarIcon( "quadricbeziercurve" ) );
        break;
    case OT_CUBICBEZIERCURVE:
        setPixmap( 0, KPBarIcon( "cubicbeziercurve" ) );
        break;
    case OT_POLYGON:
        setPixmap( 0, KPBarIcon( "mini_polygon" ) );
        break;
    case OT_CLOSED_LINE: {
        TQString name = m_object->getTypeString();
        if ( name == i18n( "Closed Freehand" ) )
            setPixmap( 0, KPBarIcon( "closed_freehand" ) );
        else if ( name == i18n( "Closed Polyline" ) )
            setPixmap( 0, KPBarIcon( "closed_polyline" ) );
        else if ( name == i18n( "Closed Quadric Bezier Curve" ) )
            setPixmap( 0, KPBarIcon( "closed_quadricbeziercurve" ) );
        else if ( name == i18n( "Closed Cubic Bezier Curve" ) )
            setPixmap( 0, KPBarIcon( "closed_cubicbeziercurve" ) );
    } break;
    case OT_GROUP:
        setPixmap( 0, KPBarIcon( "group" ) );
        break;
    default:
        break;
    }
}

KPrOutline::KPrOutline( TQWidget *parent, KPrDocument *d, KPrView *v )
    : TDEListView( parent ), KPrSideBarBase( d, v)
{
    rebuildItems();
    setSorting( -1 );
    header()->hide();
    addColumn( i18n( "Slide" ) );
    setSizePolicy( TQSizePolicy( TQSizePolicy::Minimum, TQSizePolicy::Expanding ) );

    connect( this, TQ_SIGNAL( currentChanged( TQListViewItem * ) ), this, TQ_SLOT( itemClicked( TQListViewItem * ) ) );
    connect( this, TQ_SIGNAL( rightButtonPressed( TQListViewItem *, const TQPoint &, int ) ),
             this, TQ_SLOT( rightButtonPressed( TQListViewItem *, const TQPoint &, int ) ) );
    connect( this, TQ_SIGNAL( contextMenu( TDEListView*, TQListViewItem*, const TQPoint& ) ),
             this, TQ_SLOT( slotContextMenu( TDEListView*, TQListViewItem*, const TQPoint&) ) );

    connect( this, TQ_SIGNAL( doubleClicked ( TQListViewItem * )),
             this, TQ_SLOT(renamePageTitle()));
    connect( this, TQ_SIGNAL( dropped( TQDropEvent*, TQListViewItem*, TQListViewItem* ) ),
             this, TQ_SLOT( slotDropped( TQDropEvent*, TQListViewItem*, TQListViewItem*  ) ));

    setItemsMovable( false );
    setDragEnabled( true );
    setAcceptDrops( true );
    setDropVisualizer( true );
    setFullWidth( true );
    this->setRootIsDecorated( true );
}

KPrOutline::~KPrOutline()
{
}

void KPrOutline::rebuildItems()
{
    clear();
    if ( m_viewMasterPage )
    {
        KPrPage *page=m_doc->masterPage();
        new OutlineSlideItem( this, page, true );
    }
    else
    {
        // Rebuild all the items
        for ( int i = m_doc->getPageNums() - 1; i >= 0; --i ) {
            KPrPage *page=m_doc->pageList().at( i );
            new OutlineSlideItem( this, page, false );
        }
    }
}

// given the page number (0-based), find associated slide item
// returns 0 upon stupid things (e.g. invalid page number)
OutlineSlideItem* KPrOutline::slideItem( int pageNumber )
{
    TQListViewItem* item = firstChild();
    for( int index = 0; item; ++index, item = item->nextSibling() ) {
        if( index == pageNumber )
            return dynamic_cast<OutlineSlideItem*>( item );
    }

    return 0;
}


// update the KPrOutline item, the title may have changed
void KPrOutline::updateItem( int pagenr /* 0-based */, bool sticky )
{
    if ( ! sticky ) {
        OutlineSlideItem *item = slideItem( pagenr );
        if( item ) {
            blockSignals(true);
            item->update();
            blockSignals(false);
        }
    } else {
        blockSignals(true);
        for( TQListViewItem *item = this->firstChild(); item; item = item->nextSibling() )
            dynamic_cast<OutlineSlideItem*>(item)->update();
        blockSignals(false);
    }
}

void KPrOutline::addItem( int pos )
{
    kdDebug(33001)<< "KPrOutline::addItem" << endl;

    KPrPage *page=m_doc->pageList().at( pos );
    OutlineSlideItem *item;
    if ( pos == 0 ) {
        item = new OutlineSlideItem( this, page,m_viewMasterPage );
    }
    else {
        OutlineSlideItem *after = slideItem( pos - 1 );
        item = new OutlineSlideItem( this, after, page,m_viewMasterPage );
    }

    item = dynamic_cast<OutlineSlideItem*>( item->nextSibling() );
    // update title
    for( ; item; item = dynamic_cast<OutlineSlideItem*>( item->nextSibling() ) )
        item->updateTitle();
}

// move an KPrOutline Item so that not the hole list has to be recreated
void KPrOutline::moveItem( int oldPos, int newPos )
{
    kdDebug(33001)<< "KPrOutline::moveItem " << oldPos << " to " << newPos << endl;

    int lowPage = oldPos > newPos ? newPos : oldPos;
    int highPage = oldPos < newPos ? newPos : oldPos;

    OutlineSlideItem *item = dynamic_cast<OutlineSlideItem*>( firstChild() );
    TQListViewItem *itemToMove = 0;
    TQListViewItem *itemAfter = 0;

    // moving backwards
    if ( newPos < oldPos )
        newPos--;

    for ( int index = 0; item; ++index, item = dynamic_cast<OutlineSlideItem*>( item->nextSibling() ) )
    {
        if ( index == oldPos )
            itemToMove = item;
        if ( index == newPos )
            itemAfter = item;
        if ( index >= lowPage && index <= highPage )
            item->updateTitle();
    }

    TDEListView::moveItem( itemToMove, 0, itemAfter );
}

void KPrOutline::removeItem( int pos )
{
    kdDebug(33001)<< "KPrOutline::removeItem" << endl;

    OutlineSlideItem* item = slideItem( pos );
    if( !item ) return;
    OutlineSlideItem* temp = dynamic_cast<OutlineSlideItem*>(item->nextSibling());

    delete item;

    for ( item = temp; item; item = dynamic_cast<OutlineSlideItem*>( item->nextSibling() ) )
        item->updateTitle();
}

void KPrOutline::itemClicked( TQListViewItem *item )
{
    if( !item ) return;

    // check if we need to show chosen slide
    OutlineSlideItem* slideItem = dynamic_cast<OutlineSlideItem*>(item);
    if( slideItem )
    {
        KPrPage* page = slideItem->page();
        if( !page ) return;
        if ( !m_viewMasterPage )
            emit showPage( m_doc->pageList().findRef( page ) );
    }

    // check if we need to show chosen object
    OutlineObjectItem* objectItem = dynamic_cast<OutlineObjectItem*>(item);
    if( objectItem )
    {
        KPrObject *object = objectItem->object();
        if( !object ) return;

        // ensure the owner slide is shown first
        OutlineSlideItem* slideItem = dynamic_cast<OutlineSlideItem*>(objectItem->parent());
        if( slideItem && m_doc->activePage() != slideItem->page() )
        {
            KPrPage* page = slideItem->page();
            if( !page ) return;
            if ( !m_viewMasterPage )
                emit showPage( m_doc->pageList().findRef( page ) );
        }

        // select the object, make sure it's visible
        m_doc->deSelectAllObj();
        m_view->getCanvas()->selectObj( object );
        m_view->showObjectRect( object );
        m_doc->repaint( false );
    }
}

/**
 * The listview no longer moves the item by itself. It just calls m_doc->movePage
 * which then moves the item. At the moment the method only works as long as
 * only one object is moves.
 * When an item is about to move (using drag-and-drop), it makes shure that
 * it's not moved right after an object.
 */
void KPrOutline::slotDropped( TQDropEvent * /* e */, TQListViewItem *parent, TQListViewItem *target )
{
    kdDebug(33001) << "slotDropped" << endl;
    /* slide doesn't have parent (always 0)
     * Only slides can move at the moment, objects can't. */
    if ( parent )
        return;

    // This code is taken from TDEListView
    for (TQListViewItem *i = firstChild(), *iNext = 0; i != 0; i = iNext)
    {
        iNext = i->itemBelow();
        if ( !i->isSelected() )
            continue;

        // don't drop an item after itself, or else
        // it moves to the top of the list
        if ( i == target )
            continue;

        i->setSelected( false );

        // don't move the item as it is allready
        moveItem(i, parent, target );

        // Only one item can be moved
        break;
    }
}

// We have to overwrite this method as it checks if an item is movable
// and we have disabled it.
bool KPrOutline::acceptDrag( TQDropEvent* e ) const
{
    return acceptDrops() && (e->source()==viewport());
}


void KPrOutline::setCurrentPage( int pg )
{
    OutlineSlideItem *item = slideItem( pg );
    if( item && ( item!=currentItem()->parent() ) )
    {
        blockSignals( true );
        setCurrentItem( item );
        setSelected( item, true );
        ensureItemVisible( item );
        blockSignals( false );
    }
}

void KPrOutline::contentsDropEvent( TQDropEvent *e )
{
    disconnect( this, TQ_SIGNAL( currentChanged( TQListViewItem * ) ), this, TQ_SLOT( itemClicked( TQListViewItem * ) ) );
    TDEListView::contentsDropEvent( e );
    connect( this, TQ_SIGNAL( currentChanged( TQListViewItem * ) ), this, TQ_SLOT( itemClicked( TQListViewItem * ) ) );
}

void KPrOutline::moveItem( TQListViewItem *i, TQListViewItem *, TQListViewItem *newAfter )
{
    OutlineSlideItem* srcItem = dynamic_cast<OutlineSlideItem*>( i );
    if ( !srcItem )
        return;

    int num = m_doc->pageList().findRef( srcItem->page() );

    int numNow = 0;
    if ( newAfter )
    {
        OutlineSlideItem* dstItem = dynamic_cast<OutlineSlideItem*>( newAfter );
        if( !dstItem )
            return;

        numNow = m_doc->pageList().findRef( dstItem->page() );
        if ( numNow < num )
            numNow++;
    }

    if ( num!=numNow )
        m_doc->movePage( num, numNow );
}

void KPrOutline::rightButtonPressed( TQListViewItem *, const TQPoint &pnt, int )
{
    if ( !m_doc->isReadWrite() || m_viewMasterPage ) return;

    TQListViewItem *item = TQListView::selectedItem();
    if( !item ) return;

    OutlineSlideItem* slideItem = dynamic_cast<OutlineSlideItem*>(item);

    if( slideItem ) {
        m_view->openPopupMenuSideBar(pnt);
    } else {
        OutlineObjectItem* objectItem = dynamic_cast<OutlineObjectItem*>(item);
        if( objectItem ) 
        {
            KPrObject * kpobject = objectItem->object();

            if( !kpobject ) {
                return;
            }

            KPrCanvas* canvas = static_cast<KPrCanvas*>(m_view->canvas());
            canvas->deSelectAllObj();
            canvas->selectObj( kpobject );

            canvas->objectPopup( kpobject, pnt );
        }
    }
}

void KPrOutline::slotContextMenu( TDEListView*, TQListViewItem* item, const TQPoint& p )
{
    rightButtonPressed( item, p, 0 );
}

void KPrOutline::renamePageTitle()
{
    TQListViewItem *item = TQListView::selectedItem();
    if( !item || m_viewMasterPage) return;

    OutlineSlideItem* slideItem = dynamic_cast<OutlineSlideItem*>(item);
    if( !slideItem ) return;

    KPrPage* page = slideItem->page();
    if( !page ) return;

    bool ok = false;
    TQString activeTitle = item->text( 0 );

    TQStringList page_titles;
    KPrPage *it;
    for ( it = m_doc->pageList().first(); it; it = m_doc->pageList().next() )
      if ( it->pageTitle() != activeTitle )
        page_titles.append( it->pageTitle() );

    KPrRenamePageValidator validator( page_titles );
    TQString newTitle = KInputDialog::getText( i18n("Rename Slide"),
                                              i18n("Slide title:"), activeTitle, &ok, this, 0,
                                              &validator );

    // Have a different name ?
    if ( ok ) { // User pushed an OK button.
        if ( newTitle != activeTitle ) { // Title changed.
            KPrChangeTitlePageNameCommand *cmd=new KPrChangeTitlePageNameCommand( i18n("Rename Slide"),
                                                                                  m_doc, activeTitle, newTitle.stripWhiteSpace(), page  );
            cmd->execute();
            m_doc->addCommand(cmd);
        }
    }
}

TQDragObject* KPrOutline::dragObject()
{
    if( !selectedItem()->dragEnabled() ) {
      return 0;
    }

    return TDEListView::dragObject();
}

#include "KPrSideBar.moc"
