/*  This file is part of the KDE Libraries
 *  Copyright (C) 1999-2000 Espen Sand (espensa@online.no)
 *  Copyright (C) 2003 Ravikiran Rajagopal (ravi@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 <tqbitmap.h>
#include <tqgrid.h>
#include <tqhbox.h>
#include <tqheader.h>
#include <tqlabel.h>
#include <tqlayout.h>
#include <tqobjectlist.h>
#include <tqpixmap.h>
#include <tqsplitter.h>
#include <tqtabwidget.h>
#include <tqvbox.h>
#include <tqwidgetstack.h>
#include <tqpainter.h>
#include <tqstyle.h>

#include <kapplication.h>
#include <kdialog.h> // Access to some static members
#include <klocale.h>
#include <kglobal.h>
#include <kglobalsettings.h>
#include <kseparator.h>
#include <kdebug.h>
#include "kjanuswidget.h"
#include <klistview.h>
#include "kpushbutton.h"
#include "kguiitem.h"

class KJanusWidget::IconListItem : public TQListBoxItem
{
  public:
    IconListItem( TQListBox *listbox, const TQPixmap &pixmap,
                  const TQString &text );
    virtual int height( const TQListBox *lb ) const;
    virtual int width( const TQListBox *lb ) const;
    int expandMinimumWidth( int width );
    void highlight( bool erase );        

  protected:
    const TQPixmap &defaultPixmap();
    void paint( TQPainter *painter );
    
  private:
    void paintContents( TQPainter *painter );  
  
    TQPixmap mPixmap;
    int mMinimumWidth;
};

class KJanusWidget::KJanusWidgetPrivate
{
public:
  KJanusWidgetPrivate() : mNextPageIndex(0), mListFrame( 0 ) { }

  int mNextPageIndex; // The next page index.

  // Dictionary for multipage modes.
  TQMap<int,TQWidget*> mIntToPage;
  // Reverse dictionary. Used because showPage() may be performance critical.
  TQMap<TQWidget*,int> mPageToInt;
  // Dictionary of title string associated with page.
  TQMap<int, TQString> mIntToTitle;

  TQWidget * mListFrame;
  TQSplitter * mSplitter;
};

template class TQPtrList<TQListViewItem>;


KJanusWidget::KJanusWidget( TQWidget *parent, const char *name, int face )
  : TQWidget( parent, name, 0 ),
    mValid(false), mPageList(0),
    mTitleList(0), mFace(face), mTitleLabel(0), mActivePageWidget(0),
    mShowIconsInTreeList(false), d(0)
{
  TQVBoxLayout *topLayout = new TQVBoxLayout( this );

  if( mFace == TreeList || mFace == IconList )
  {
    d = new KJanusWidgetPrivate;
    d->mSplitter = 0;

    TQFrame *page;
    if( mFace == TreeList )
    {
      d->mSplitter = new TQSplitter( this );
      topLayout->addWidget( d->mSplitter, 10 );
      mTreeListResizeMode = TQSplitter::KeepSize;

      d->mListFrame = new TQWidget( d->mSplitter );
      TQVBoxLayout *dummy = new TQVBoxLayout( d->mListFrame, 0, KDialog::spacingHint() );
      dummy->setAutoAdd( true );
      mTreeList = new KListView( d->mListFrame );
      mTreeList->addColumn( TQString::null );
      mTreeList->header()->hide();
      mTreeList->setRootIsDecorated(true);
      mTreeList->setSorting( -1 );
      connect( mTreeList, TQT_SIGNAL(selectionChanged()), TQT_SLOT(slotShowPage()) );
      connect( mTreeList, TQT_SIGNAL(clicked(TQListViewItem *)), TQT_SLOT(slotItemClicked(TQListViewItem *)));

      //
      // Page area. Title at top with a separator below and a pagestack using
      // all available space at bottom.
      //
      TQFrame *p = new TQFrame( d->mSplitter );

      TQHBoxLayout *hbox = new TQHBoxLayout( p, 0, 0 );

      page = new TQFrame( p );
      hbox->addWidget( page, 10 );
    }
    else
    {
      TQHBoxLayout *hbox = new TQHBoxLayout( topLayout );
      d->mListFrame = new TQWidget( this );
      hbox->addWidget( d->mListFrame );

      ( new TQVBoxLayout( d->mListFrame, 0, 0 ) )->setAutoAdd( true );
      mIconList = new IconListBox( d->mListFrame );

      TQFont listFont( mIconList->font() );
      listFont.setBold( true );
      mIconList->setFont( listFont );

      mIconList->verticalScrollBar()->installEventFilter( this );
      connect( mIconList, TQT_SIGNAL(selectionChanged()), TQT_SLOT(slotShowPage()));
      connect( mIconList, TQT_SIGNAL(onItem(TQListBoxItem *)), TQT_SLOT(slotOnItem(TQListBoxItem *)));

      hbox->addSpacing( KDialog::marginHint() );
      page = new TQFrame( this );
      hbox->addWidget( page, 10 );
    }

    //
    // Rest of page area. Title at top with a separator below and a
    // pagestack using all available space at bottom.
    //

    TQVBoxLayout *vbox = new TQVBoxLayout( page, 0, KDialog::spacingHint() );

    mTitleLabel = new TQLabel( i18n("Empty Page"), page, "KJanusWidgetTitleLabel" );
    vbox->addWidget( mTitleLabel, 0, TQApplication::reverseLayout() ? AlignRight : AlignLeft );

    TQFont titleFont( mTitleLabel->font() );
    titleFont.setBold( true );
    mTitleLabel->setFont( titleFont );

    mTitleSep = new KSeparator( page );
    mTitleSep->setFrameStyle( TQFrame::HLine|TQFrame::Plain );
    vbox->addWidget( mTitleSep );

    mPageStack = new TQWidgetStack( page );
    connect(mPageStack, TQT_SIGNAL(aboutToShow(TQWidget *)),
            TQT_SIGNAL(aboutToShowPage(TQWidget *)));
    vbox->addWidget( mPageStack, 10 );
  }
  else if( mFace == Tabbed )
  {
    d = new KJanusWidgetPrivate;

    mTabControl = new TQTabWidget( this );
    mTabControl->setMargin (KDialog::marginHint());
    connect(mTabControl, TQT_SIGNAL(currentChanged(TQWidget *)),
            TQT_SIGNAL(aboutToShowPage(TQWidget *)));
    topLayout->addWidget( mTabControl, 10 );
  }
  else if( mFace == Swallow )
  {
    mSwallowPage = new TQWidget( this );
    topLayout->addWidget( mSwallowPage, 10 );
  }
  else
  {
    mFace = Plain;
    mPlainPage = new TQFrame( this );
    topLayout->addWidget( mPlainPage, 10 );
  }

  if ( kapp )
    connect(kapp,TQT_SIGNAL(kdisplayFontChanged()),TQT_SLOT(slotFontChanged()));
  mValid = true;

  setSwallowedWidget(0); // Set default size if 'mFace' is Swallow.
}


KJanusWidget::~KJanusWidget()
{
  delete d;
}


bool KJanusWidget::isValid() const
{
  return mValid;
}


TQFrame *KJanusWidget::plainPage()
{
  return mPlainPage;
}


int KJanusWidget::face() const
{
  return mFace;
}

TQWidget *KJanusWidget::FindParent()
{
  if( mFace == Tabbed ) {
    return mTabControl;
  }
  else {
    return this;
  }
}

TQFrame *KJanusWidget::addPage( const TQStringList &items, const TQString &header,
			       const TQPixmap &pixmap )
{
  if( !mValid )
  {
    kdDebug() << "addPage: Invalid object" << endl;
    return 0;
  }

  TQFrame *page = new TQFrame( FindParent(), "page" );
  addPageWidget( page, items, header, pixmap );

  return page;
}

void KJanusWidget::pageGone( TQObject *obj )
{
  removePage( TQT_TQWIDGET( obj ) );
}

void KJanusWidget::slotReopen( TQListViewItem * item )
{
  if( item )
    item->setOpen( true );
}

TQFrame *KJanusWidget::addPage( const TQString &itemName, const TQString &header,
          const TQPixmap &pixmap )
{
  TQStringList items;
  items << itemName;
  return addPage(items, header, pixmap);
}



TQVBox *KJanusWidget::addVBoxPage( const TQStringList &items,
          const TQString &header,
          const TQPixmap &pixmap )
{
  if( !mValid )
  {
    kdDebug() << "addPage: Invalid object" << endl;
    return 0;
  }

  TQVBox *page = new TQVBox(FindParent() , "page" );
  page->setSpacing( KDialog::spacingHint() );
  addPageWidget( page, items, header, pixmap );

  return page;
}

TQVBox *KJanusWidget::addVBoxPage( const TQString &itemName,
				  const TQString &header,
				  const TQPixmap &pixmap )
{
  TQStringList items;
  items << itemName;
  return addVBoxPage(items, header, pixmap);
}

TQHBox *KJanusWidget::addHBoxPage( const TQStringList &items,
				  const TQString &header,
				  const TQPixmap &pixmap )
{
  if( !mValid ) {
    kdDebug() << "addPage: Invalid object" << endl;
    return 0;
  }

  TQHBox *page = new TQHBox(FindParent(), "page");
  page->setSpacing( KDialog::spacingHint() );
  addPageWidget( page, items, header, pixmap );

  return page;
}

TQHBox *KJanusWidget::addHBoxPage( const TQString &itemName,
				  const TQString &header,
				  const TQPixmap &pixmap )
{
  TQStringList items;
  items << itemName;
  return addHBoxPage(items, header, pixmap);
}

TQGrid *KJanusWidget::addGridPage( int n, Orientation dir,
				  const TQStringList &items,
				  const TQString &header,
				  const TQPixmap &pixmap )
{
  if( !mValid )
  {
    kdDebug() << "addPage: Invalid object" << endl;
    return 0;
  }

  TQGrid *page = new TQGrid( n, dir, FindParent(), "page" );
  page->setSpacing( KDialog::spacingHint() );
  addPageWidget( page, items, header, pixmap );

  return page;
}


TQGrid *KJanusWidget::addGridPage( int n, Orientation dir,
				  const TQString &itemName,
				  const TQString &header,
				  const TQPixmap &pixmap )
{
  TQStringList items;
  items << itemName;
  return addGridPage(n, dir, items, header, pixmap);
}

void KJanusWidget::InsertTreeListItem(const TQStringList &items, const TQPixmap &pixmap, TQFrame *page)
{
  bool isTop = true;
  TQListViewItem *curTop = 0, *child, *last, *newChild;
  unsigned int index = 1;
  TQStringList curPath;

  for ( TQStringList::ConstIterator it = items.begin(); it != items.end(); ++it, index++ ) {
    TQString name = (*it);
    bool isPath = ( index != items.count() );

    // Find the first child.
    if (isTop) {
      child = mTreeList->firstChild();
    }
    else {
      child = curTop->firstChild();
    }

    // Now search for a child with the current Name, and if it we doesn't
    // find it, then remember the location of the last child.
    for (last = 0; child && child->text(0) != name ; last = child, child = child->nextSibling());

    if (!last && !child) {
      // This node didn't have any children at all, lets just insert the
      // new child.
      if (isTop)
        newChild = new TQListViewItem(mTreeList, name);
      else
        newChild = new TQListViewItem(curTop, name);

    }
    else if (child) {
      // we found the given name in this child.
      if (!isPath) {
        kdDebug() << "The element inserted was already in the TreeList box!" << endl;
        return;
      }
      else {
        // Ok we found the folder
        newChild  = child;
      }
    }
    else {
      // the node had some children, but we didn't find the given name
      if (isTop)
        newChild = new TQListViewItem(mTreeList, last, name);
      else
        newChild = new TQListViewItem(curTop, last, name);
    }

    // Now make the element expandable if it is a path component, and make
    // ready for next loop
    if (isPath) {
      newChild->setExpandable(true);
      curTop = newChild;
      isTop = false;
      curPath << name;

      TQString key = curPath.join("_/_");
      if (mFolderIconMap.contains(key)) {
        TQPixmap p = mFolderIconMap[key];
        newChild->setPixmap(0,p);
      }
    }
    else {
      if (mShowIconsInTreeList) {
        newChild->setPixmap(0, pixmap);
      }
      mTreeListToPageStack.insert(newChild, page);
    }
  }
}

void KJanusWidget::addPageWidget( TQFrame *page, const TQStringList &items,
				  const TQString &header,const TQPixmap &pixmap )
{
  connect(page, TQT_SIGNAL(destroyed(TQObject*)), TQT_SLOT(pageGone(TQObject*)));

  if( mFace == Tabbed )
  {
    mTabControl->addTab (page, items.last());
    d->mIntToPage[d->mNextPageIndex] = static_cast<TQWidget*>(page);
    d->mPageToInt[static_cast<TQWidget*>(page)] = d->mNextPageIndex;
    d->mNextPageIndex++;
  }
  else if( mFace == TreeList || mFace == IconList )
  {
    d->mIntToPage[d->mNextPageIndex] = static_cast<TQWidget*>(page);
    d->mPageToInt[static_cast<TQWidget*>(page)] = d->mNextPageIndex;
    mPageStack->addWidget( page, 0 );

    if (items.isEmpty()) {
      kdDebug() << "Invalid TQStringList, with zero items" << endl;
      return;
    }

    if( mFace == TreeList )
    {
      InsertTreeListItem(items, pixmap, page);
    }
    else // mFace == IconList
    {
      TQString itemName = items.last();
      IconListItem *item = new IconListItem( mIconList, pixmap, itemName );
      mIconListToPageStack.insert(item, page);
      mIconList->invalidateHeight();
      mIconList->invalidateWidth();

      if (mIconList->isVisible())
        mIconList->updateWidth();
    }

    //
    // Make sure the title label is sufficiently wide
    //
    TQString lastName = items.last();
    const TQString &title = (!header.isNull() ? header : lastName);
    TQRect r = mTitleLabel->fontMetrics().boundingRect( title );
    if( mTitleLabel->minimumWidth() < r.width() )
    {
      mTitleLabel->setMinimumWidth( r.width() );
    }
    d->mIntToTitle[d->mNextPageIndex] = title;
    if( d->mIntToTitle.count() == 1 )
    {
      showPage(0);
    }
    d->mNextPageIndex++;
  }
  else
  {
    kdDebug() << "KJanusWidget::addPageWidget: can only add a page in Tabbed, TreeList or IconList modes" << endl;
  }

}

void KJanusWidget::setFolderIcon(const TQStringList &path, const TQPixmap &pixmap)
{
  TQString key = path.join("_/_");
  mFolderIconMap.insert(key,pixmap);
}



bool KJanusWidget::setSwallowedWidget( TQWidget *widget )
{
  if( mFace != Swallow || !mValid )
  {
    return false;
  }

  //
  // Remove current layout and make a new.
  //
  delete mSwallowPage->layout();

  TQGridLayout *gbox = new TQGridLayout( mSwallowPage, 1, 1, 0 );

  //
  // Hide old children
  //
  TQObjectList l = mSwallowPage->childrenListObject(); // silence please
  for( uint i=0; i < l.count(); i++ )
  {
    TQObject *o = l.at(i);
    if( o->isWidgetType() )
    {
      ((TQWidget*)o)->hide();
    }
  }

  //
  // Add new child or make default size
  //
  if( !widget )
  {
    gbox->addRowSpacing(0,100);
    gbox->addColSpacing(0,100);
    mSwallowPage->setMinimumSize(100,100);
  }
  else
  {
    if( TQT_BASE_OBJECT(widget->parent()) != TQT_BASE_OBJECT(mSwallowPage) )
    {
      widget->reparent( mSwallowPage, 0, TQPoint(0,0) );
    }
    gbox->addWidget(widget, 0, 0 );
    gbox->activate();
    mSwallowPage->setMinimumSize( widget->minimumSize() );
  }

  return true;
}

bool KJanusWidget::slotShowPage()
{
  if( !mValid )
  {
    return false;
  }

  if( mFace == TreeList )
  {
    TQListViewItem *node = mTreeList->selectedItem();
    if( !node ) { return false; }

    TQWidget *stackItem = mTreeListToPageStack[node];
    // Make sure to call through the virtual function showPage(int)
    return showPage(d->mPageToInt[stackItem]);
  }
  else if( mFace == IconList )
  {
    TQListBoxItem *node = mIconList->item( mIconList->currentItem() );
    if( !node ) { return false; }
    TQWidget *stackItem = mIconListToPageStack[node];
    // Make sure to call through the virtual function showPage(int)
    return showPage(d->mPageToInt[stackItem]);
  }

  return false;
}


bool KJanusWidget::showPage( int index )
{
  if( !d || !mValid )
  {
    return false;
  }
  else
  {
    return showPage(d->mIntToPage[index]);
  }
}


bool KJanusWidget::showPage( TQWidget *w )
{
  if( !w || !mValid )
  {
    return false;
  }

  if( mFace == TreeList || mFace == IconList )
  {
    mPageStack->raiseWidget( w );
    mActivePageWidget = w;

    int index = d->mPageToInt[w];
    mTitleLabel->setText( d->mIntToTitle[index] );
    if( mFace == TreeList )
    {
      TQMap<TQListViewItem *, TQWidget *>::Iterator it;
      for (it = mTreeListToPageStack.begin(); it != mTreeListToPageStack.end(); ++it){
        TQListViewItem *key = it.key();
        TQWidget *val = it.data();
        if (val == w) {
          mTreeList->setSelected(key, true );
          break;
        }
      }
    }
    else
    {
      TQMap<TQListBoxItem *, TQWidget *>::Iterator it;
      for (it = mIconListToPageStack.begin(); it != mIconListToPageStack.end(); ++it){
        TQListBoxItem *key = it.key();
        TQWidget *val = it.data();
        if (val == w) {
          mIconList->setSelected( key, true );
          break;
        }
      }
    }
  }
  else if( mFace == Tabbed )
  {
    mTabControl->showPage(w);
    mActivePageWidget = w;
  }
  else
  {
    return false;
  }

  return true;
}


int KJanusWidget::activePageIndex() const
{
  if( mFace == TreeList) {
    TQListViewItem *node = mTreeList->selectedItem();
    if( !node ) { return -1; }
    TQWidget *stackItem = mTreeListToPageStack[node];
    return d->mPageToInt[stackItem];
  }
  else if (mFace == IconList) {
    TQListBoxItem *node = mIconList->item( mIconList->currentItem() );
    if( !node ) { return false; }
    TQWidget *stackItem = mIconListToPageStack[node];
    return d->mPageToInt[stackItem];
  }
  else if( mFace == Tabbed ) {
    TQWidget *widget = mTabControl->currentPage();
    return ( !widget ? -1 : d->mPageToInt[widget] );
  }
  else {
    return -1;
  }
}


int KJanusWidget::pageIndex( TQWidget *widget ) const
{
  if( !widget )
  {
    return -1;
  }
  else if( mFace == TreeList || mFace == IconList )
  {
    return d->mPageToInt[widget];
  }
  else if( mFace == Tabbed )
  {
    //
    // The user gets the real page widget with addVBoxPage(), addHBoxPage()
    // and addGridPage() but not with addPage() which returns a child of
    // the toplevel page. addPage() returns a TQFrame so I check for that.
    //
    if( widget->isA(TQFRAME_OBJECT_NAME_STRING) )
    {
      return d->mPageToInt[widget->parentWidget()];
    }
    else
    {
      return d->mPageToInt[widget];
    }
  }
  else
  {
    return -1;
  }
}

void KJanusWidget::slotFontChanged()
{
  if( mTitleLabel )
  {
    mTitleLabel->setFont( KGlobalSettings::generalFont() );
    TQFont titleFont( mTitleLabel->font() );
    titleFont.setBold( true );
    mTitleLabel->setFont( titleFont );
  }

  if( mFace == IconList )
  {
    TQFont listFont( mIconList->font() );
    listFont.setBold( true );
    mIconList->setFont( listFont );
    mIconList->invalidateHeight();
    mIconList->invalidateWidth();
  }
}

// makes the treelist behave like the list of kcontrol
void KJanusWidget::slotItemClicked(TQListViewItem *it)
{
  if(it && (it->childCount()>0))
    it->setOpen(!it->isOpen());
}

// hack because qt does not support Q_OBJECT in nested classes
void KJanusWidget::slotOnItem(TQListBoxItem *qitem)
{
  mIconList->slotOnItem( qitem );
}  

void KJanusWidget::setFocus()
{
  if( !mValid ) { return; }
  if( mFace == TreeList )
  {
    mTreeList->setFocus();
  }
  if( mFace == IconList )
  {
    mIconList->setFocus();
  }
  else if( mFace == Tabbed )
  {
    mTabControl->setFocus();
  }
  else if( mFace == Swallow )
  {
    mSwallowPage->setFocus();
  }
  else if( mFace == Plain )
  {
    mPlainPage->setFocus();
  }
}


TQSize KJanusWidget::minimumSizeHint() const
{
  if( mFace == TreeList || mFace == IconList )
  {
    TQSize s1( KDialog::spacingHint(), KDialog::spacingHint()*2 );
    TQSize s2(0,0);
    TQSize s3(0,0);
    TQSize s4( mPageStack->sizeHint() );

    if( mFace == TreeList )
    {
      s1.rwidth() += style().pixelMetric( TQStyle::PM_SplitterWidth );
      s2 = mTreeList->minimumSize();
    }
    else
    {
      mIconList->updateMinimumHeight();
      mIconList->updateWidth();
      s2 = mIconList->minimumSize();
    }

    if( mTitleLabel->isVisible() )
    {
      s3 += mTitleLabel->sizeHint();
      s3.rheight() += mTitleSep->minimumSize().height();
    }

    //
    // Select the tallest item. It has only effect in IconList mode
    //
    int h1 = s1.rheight() + s3.rheight() + s4.height();
    int h2 = QMAX( h1, s2.rheight() );

    return TQSize( s1.width()+s2.width()+QMAX(s3.width(),s4.width()), h2 );
  }
  else if( mFace == Tabbed )
  {
    return mTabControl->sizeHint();
  }
  else if( mFace == Swallow )
  {
    return mSwallowPage->minimumSize();
  }
  else if( mFace == Plain )
  {
    return mPlainPage->sizeHint();
  }
  else
  {
    return TQSize( 100, 100 ); // Should never happen though.
  }

}


TQSize KJanusWidget::sizeHint() const
{
  return minimumSizeHint();
}


void KJanusWidget::setTreeListAutoResize( bool state )
{
  if( mFace == TreeList )
  {
    mTreeListResizeMode = !state ?
      TQSplitter::KeepSize : TQSplitter::Stretch;
    if( d->mSplitter )
      d->mSplitter->setResizeMode( d->mListFrame, mTreeListResizeMode );
  }
}


void KJanusWidget::setIconListAllVisible( bool state )
{
  if( mFace == IconList )
  {
    mIconList->setShowAll( state );
  }
}

void KJanusWidget::setShowIconsInTreeList( bool state )
{
  mShowIconsInTreeList = state;
}

void KJanusWidget::setRootIsDecorated( bool state )
{
  if( mFace == TreeList ) {
    mTreeList->setRootIsDecorated(state);
  }
}

void KJanusWidget::unfoldTreeList( bool persist )
{
  if( mFace == TreeList )
  {
    if( persist )
      connect( mTreeList, TQT_SIGNAL( collapsed( TQListViewItem * ) ), this, TQT_SLOT( slotReopen( TQListViewItem * ) ) );
    else
      disconnect( mTreeList, TQT_SIGNAL( collapsed( TQListViewItem * ) ), this, TQT_SLOT( slotReopen( TQListViewItem * ) ) );

    for( TQListViewItem * item = mTreeList->firstChild(); item; item = item->itemBelow() )
      item->setOpen( true );
  }
}

void KJanusWidget::addWidgetBelowList( TQWidget * widget )
{
  if( ( mFace == TreeList || mFace == IconList ) && d->mListFrame )
  {
    widget->reparent( d->mListFrame, TQPoint() );
  }
}

void KJanusWidget::addButtonBelowList( const TQString & text, TQObject * recv, const char * slot )
{
  if( ( mFace == TreeList || mFace == IconList ) && d->mListFrame )
  {
    TQPushButton * button = new TQPushButton( text, d->mListFrame, "KJanusWidget::buttonBelowList" );
    connect( button, TQT_SIGNAL( clicked() ), recv, slot );
  }
}

void KJanusWidget::addButtonBelowList( const KGuiItem & item, TQObject * recv, const char * slot )
{
  if( ( mFace == TreeList || mFace == IconList ) && d->mListFrame )
  {
    KPushButton * button = new KPushButton( item, d->mListFrame, "KJanusWidget::buttonBelowList" );
    connect( button, TQT_SIGNAL( clicked() ), recv, slot );
  }
}

void KJanusWidget::showEvent( TQShowEvent * )
{
  if( mFace == TreeList )
  {
    if( d->mSplitter )
      d->mSplitter->setResizeMode( d->mListFrame, mTreeListResizeMode );
  }
}


//
// 2000-13-02 Espen Sand
// It should be obvious that this eventfilter must only be
// be installed on the vertical scrollbar of the mIconList.
//
bool KJanusWidget::eventFilter( TQObject *o, TQEvent *e )
{
  if( e->type() == TQEvent::Show )
  {
    IconListItem *item = (IconListItem*)mIconList->item(0);
    if( item )
    {
      int lw = item->width( mIconList );
      int sw = mIconList->verticalScrollBar()->sizeHint().width();
      mIconList->setFixedWidth( lw+sw+mIconList->frameWidth()*2 );
    }
  }
  else if( e->type() == TQEvent::Hide )
  {
    IconListItem *item = (IconListItem*)mIconList->item(0);
    if( item )
    {
      int lw = item->width( mIconList );
      mIconList->setFixedWidth( lw+mIconList->frameWidth()*2 );
    }
  }
  return TQWidget::eventFilter( o, e );
}



//
// Code for the icon list box
//


KJanusWidget::IconListBox::IconListBox( TQWidget *parent, const char *name,
					WFlags f )
  :KListBox( parent, name, f ), mShowAll(false), mHeightValid(false),
   mWidthValid(false),
   mOldItem(0) 
{
}

void KJanusWidget::IconListBox::updateMinimumHeight()
{
  if( mShowAll && !mHeightValid )
  {
    int h = frameWidth()*2;
    for( TQListBoxItem *i = item(0); i; i = i->next() )
    {
      h += i->height( this );
    }
    setMinimumHeight( h );
    mHeightValid = true;
  }
}


void KJanusWidget::IconListBox::updateWidth()
{
  if( !mWidthValid )
  {
    int maxWidth = 10;
    for( TQListBoxItem *i = item(0); i; i = i->next() )
    {
      int w = ((IconListItem *)i)->width(this);
      maxWidth = QMAX( w, maxWidth );
    }

    for( TQListBoxItem *i = item(0); i; i = i->next() )
    {
      ((IconListItem *)i)->expandMinimumWidth( maxWidth );
    }

    if( verticalScrollBar()->isVisible() )
    {
      maxWidth += verticalScrollBar()->sizeHint().width();
    }

    setFixedWidth( maxWidth + frameWidth()*2 );
    mWidthValid = true;
  }
}


void KJanusWidget::IconListBox::invalidateHeight()
{
  mHeightValid = false;
}


void KJanusWidget::IconListBox::invalidateWidth()
{
  mWidthValid = false;
}


void KJanusWidget::IconListBox::setShowAll( bool showAll )
{
  mShowAll = showAll;
  mHeightValid = false;
}


void KJanusWidget::IconListBox::leaveEvent( TQEvent *ev )
{
  KListBox::leaveEvent( ev ); 

  if ( mOldItem && !mOldItem->isSelected() )
  {
    ((KJanusWidget::IconListItem *) mOldItem)->highlight( true );
    mOldItem = 0;
  }
} 

// hack because qt does not support Q_OBJECT in nested classes
void KJanusWidget::IconListBox::slotOnItem(TQListBoxItem *qitem)
{
  KListBox::slotOnItem( qitem );

  if ( qitem == mOldItem )
  {
    return;
  }
 
  if ( mOldItem && !mOldItem->isSelected() )
  {
    ((KJanusWidget::IconListItem *) mOldItem)->highlight( true );
  }

  KJanusWidget::IconListItem *item = dynamic_cast< KJanusWidget::IconListItem * >( qitem );
  if ( item && !item->isSelected() )
  {      
    item->highlight( false );
    mOldItem = item;
  }
  else
  {
    mOldItem = 0;
  }
}  



KJanusWidget::IconListItem::IconListItem( TQListBox *listbox, const TQPixmap &pixmap,
                                          const TQString &text )
  : TQListBoxItem( listbox )
{
  mPixmap = pixmap;
  if( mPixmap.isNull() )
  {
    mPixmap = defaultPixmap();
  }
  setText( text );
  setCustomHighlighting( true );
  mMinimumWidth = 0;
}


int KJanusWidget::IconListItem::expandMinimumWidth( int width )
{
  mMinimumWidth = QMAX( mMinimumWidth, width );
  return mMinimumWidth;
}


void KJanusWidget::IconListItem::highlight( bool erase )
{
   // FIXME: Add configuration option to disable highlighting
   // For now, always disable highlighting
   erase = true;

   TQRect r = listBox()->itemRect( this );
   r.addCoords( 1, 1, -1, -1 );

   TQPainter p( listBox()->viewport() );
   p.setClipRegion( r );

   const TQColorGroup &cg = listBox()->colorGroup();
   if ( erase )
   {
      p.setPen( cg.base() );
      p.setBrush( cg.base() );
      p.drawRect( r );
   }
   else
   {
      p.setBrush( cg.highlight().light( 120 ) );
      p.drawRect( r );

      p.setPen( cg.highlight().dark( 140 ) );
      p.drawRect( r );
   }

   p.setPen( cg.foreground() );
   p.translate( r.x() - 1, r.y() - 1 );
   paintContents( &p );
}


const TQPixmap &KJanusWidget::IconListItem::defaultPixmap()
{
  static TQPixmap *pix=0;
  if( !pix )
  {
    pix = new TQPixmap( 32, 32 );
    TQPainter p( pix );
    p.eraseRect( 0, 0, pix->width(), pix->height() );
    p.setPen( Qt::red );
    p.drawRect ( 0, 0, pix->width(), pix->height() );
    p.end();

    TQBitmap mask( pix->width(), pix->height(), true );
    mask.fill( Qt::black );
    p.begin( &mask );
    p.setPen( Qt::white );
    p.drawRect ( 0, 0, pix->width(), pix->height() );
    p.end();

    pix->setMask( mask );
  }
  return *pix;
}


void KJanusWidget::IconListItem::paint( TQPainter *painter )
{
  TQRect itemPaintRegion( listBox()->itemRect( this ) );
  TQRect r( 1, 1, itemPaintRegion.width() - 2, itemPaintRegion.height() - 2);

  if ( isSelected() )
  {
    painter->eraseRect( r );

    painter->save();
    painter->setPen( listBox()->colorGroup().highlight().dark( 160 ) );
    painter->drawRect( r );
    painter->restore();
  }

  paintContents( painter );
}


void KJanusWidget::IconListItem::paintContents( TQPainter *painter )
{
  TQFontMetrics fm = painter->fontMetrics();
  int ht = fm.boundingRect( 0, 0, 0, 0, Qt::AlignCenter, text() ).height();
  int wp = mPixmap.width();
  int hp = mPixmap.height();
  painter->drawPixmap( (mMinimumWidth - wp) / 2, 5, mPixmap );

  if( !text().isEmpty() )
  {
    painter->drawText( 1, hp + 7, mMinimumWidth - 2, ht, Qt::AlignCenter, text() );
  }
}

int KJanusWidget::IconListItem::height( const TQListBox *lb ) const
{
  if( text().isEmpty() )
  {
    return mPixmap.height();
  }
  else
  {
    int ht = lb->fontMetrics().boundingRect( 0, 0, 0, 0, Qt::AlignCenter, text() ).height();
    return (mPixmap.height() + ht + 10);
  }
}


int KJanusWidget::IconListItem::width( const TQListBox *lb ) const
{
  int wt = lb->fontMetrics().boundingRect( 0, 0, 0, 0, Qt::AlignCenter, text() ).width() + 10;
  int wp = mPixmap.width() + 10;
  int w  = QMAX( wt, wp );
  return QMAX( w, mMinimumWidth );
}


void KJanusWidget::virtual_hook( int, void* )
{ /*BASE::virtual_hook( id, data );*/ }


// TODO: In TreeList, if the last child of a node is removed, and there is no corrsponding widget for that node, allow the caller to
// delete the node.
void KJanusWidget::removePage( TQWidget *page )
{
  if (!d || !d->mPageToInt.contains(page))
    return;

  int index = d->mPageToInt[page];

  if ( mFace == TreeList )
  {
    TQMap<TQListViewItem*, TQWidget *>::Iterator i;
    for( i = mTreeListToPageStack.begin(); i != mTreeListToPageStack.end(); ++i )
      if (i.data()==page)
      {
        delete i.key();
        mPageStack->removeWidget(page);
        mTreeListToPageStack.remove(i);
        d->mIntToTitle.remove(index);
        d->mPageToInt.remove(page);
        d->mIntToPage.remove(index);
        break;
      }
  }
  else if ( mFace == IconList )
  {
    TQMap<TQListBoxItem*, TQWidget *>::Iterator i;
    for( i = mIconListToPageStack.begin(); i != mIconListToPageStack.end(); ++i )
      if (i.data()==page)
      {
        delete i.key();
        mPageStack->removeWidget(page);
        mIconListToPageStack.remove(i);
        d->mIntToTitle.remove(index);
        d->mPageToInt.remove(page);
        d->mIntToPage.remove(index);
        break;
      }
  }
  else // Tabbed
  {
    mTabControl->removePage(page);
    d->mPageToInt.remove(page);
    d->mIntToPage.remove(index);
  }
}


TQString KJanusWidget::pageTitle(int index) const
{
  if (!d || !d->mIntToTitle.contains(index))
    return TQString::null;
  else
    return d->mIntToTitle[index];
}


TQWidget *KJanusWidget::pageWidget(int index) const
{
  if (!d || !d->mIntToPage.contains(index))
    return 0;
  else
    return d->mIntToPage[index];
}

#include "kjanuswidget.moc"
