// -*- c-basic-offset: 2 -*-

#include "kfoldertree.h"
#include <tdelocale.h>
#include <tdeio/global.h>
#include <kiconloader.h>
#include <kdebug.h>
#include <kstringhandler.h>
#include <tqpainter.h>
#include <tqapplication.h>
#include <tqheader.h>
#include <tqstyle.h>

//-----------------------------------------------------------------------------
KFolderTreeItem::KFolderTreeItem( KFolderTree *parent, const TQString & label,
				  Protocol protocol, Type type )
  : TDEListViewItem( parent, label ), mProtocol( protocol ), mType( type ),
    mUnread(-1), mTotal(0), mSize(0), mFolderIsCloseToQuota( false )
{
}

//-----------------------------------------------------------------------------
KFolderTreeItem::KFolderTreeItem( KFolderTreeItem *parent,
				  const TQString & label, Protocol protocol, Type type,
          int unread, int total )
    : TDEListViewItem( parent, label ), mProtocol( protocol ), mType( type ),
      mUnread( unread ), mTotal( total ), mSize(0), mFolderIsCloseToQuota( false )
{
}

//-----------------------------------------------------------------------------
int KFolderTreeItem::protocolSortingKey() const
{
  // protocol dependant sorting order:
  // local < imap < news < search < other
  switch ( mProtocol ) {
  case Local:
    return 1;
  case CachedImap:
  case Imap:
    return 2;
  case News:
    return 3;
  case Search:
    return 4;
  default:
    return 42;
  }
}

//-----------------------------------------------------------------------------
int KFolderTreeItem::typeSortingKey() const
{
  // type dependant sorting order:
  // inbox < outbox < sent-mail < trash < drafts
  // < calendar < contacts < notes < tasks
  // < normal folders
  switch ( mType ) {
  case Inbox:
    return 1;
  case Outbox:
    return 2;
  case SentMail:
    return 3;
  case Trash:
    return 4;
  case Drafts:
    return 5;
  case Templates:
    return 6;
  case Calendar:
    return 7;
  case Contacts:
    return 8;
  case Notes:
    return 9;
  case Tasks:
    return 10;
  default:
    return 42;
  }
}

//-----------------------------------------------------------------------------
int KFolderTreeItem::compare( TQListViewItem * i, int col, bool ) const
{
  KFolderTreeItem* other = static_cast<KFolderTreeItem*>( i );

  if (col == 0)
  {
    // sort by folder

    // local root-folder
    if ( depth() == 0 && mProtocol == NONE )
      return -1;
    if ( other->depth() == 0 && other->protocol() == NONE )
      return 1;

    // first compare by protocol
    int thisKey = protocolSortingKey();
    int thatKey = other->protocolSortingKey();
    if ( thisKey < thatKey )
      return -1;
    if ( thisKey > thatKey )
      return 1;

    // then compare by type
    thisKey = typeSortingKey();
    thatKey = other->typeSortingKey();
    if ( thisKey < thatKey )
      return -1;
    if ( thisKey > thatKey )
      return 1;

    // and finally compare by name
    return text( 0 ).localeAwareCompare( other->text( 0 ) );
  }
  else
  {
    // sort by unread or total-column
    TQ_INT64 a = 0, b = 0;
    if (col == static_cast<KFolderTree*>(listView())->unreadIndex())
    {
      a = mUnread;
      b = other->unreadCount();
    }
    else if (col == static_cast<KFolderTree*>(listView())->totalIndex())
    {
      a = mTotal;
      b = other->totalCount();
    }
    else if (col == static_cast<KFolderTree*>(listView())->sizeIndex())
    {
      a = mSize;
      b = other->folderSize();
    }

    if ( a == b )
      return 0;
    else
      return (a < b ? -1 : 1);
  }
}

//-----------------------------------------------------------------------------
void KFolderTreeItem::setUnreadCount( int aUnread )
{
  if ( aUnread < 0 ) return;

  mUnread = aUnread;

  TQString unread = TQString();
  if (mUnread == 0)
    unread = "- ";
  else {
    unread.setNum(mUnread);
    unread += " ";
  }

  setText( static_cast<KFolderTree*>(listView())->unreadIndex(),
      unread );
}

//-----------------------------------------------------------------------------
void KFolderTreeItem::setTotalCount( int aTotal )
{
  if ( aTotal < 0 ) return;

  mTotal = aTotal;

  TQString total = TQString();
  if (mTotal == 0)
    total = "- ";
  else {
    total.setNum(mTotal);
    total += " ";
  }

  setText( static_cast<KFolderTree*>(listView())->totalIndex(),
      total );
}

//-----------------------------------------------------------------------------
void KFolderTreeItem::setFolderSize( TQ_INT64 aSize )
{
  if ( aSize < 0 ) return;  // we need to update even if nothing changed, kids ...

  mSize = aSize;

  TQString size;
  if (mType != Root) {
      if (mSize == 0 && (childCount() == 0 || isOpen() ) )
          size = "- ";
      else
          size = TDEIO::convertSize(mSize);
  }
  if ( childCount() > 0 && !isOpen() ) {
      TQ_INT64 recursiveSize = recursiveFolderSize();
      if ( recursiveSize != mSize ) {
            if ( mType != Root )
                size += TQString::fromLatin1(" + %1").arg( TDEIO::convertSize( recursiveSize - mSize ) );
            else 
                size = TDEIO::convertSize( recursiveSize );
      }
  }
  size += " ";

  setText( static_cast<KFolderTree*>(listView())->sizeIndex(), size );
}

//-----------------------------------------------------------------------------
TQ_INT64 KFolderTreeItem::recursiveFolderSize() const
{
  TQ_INT64 size = mSize;

  for ( TQListViewItem *item = firstChild() ;
      item ; item = item->nextSibling() )
  {
    size += static_cast<KFolderTreeItem*>(item)->recursiveFolderSize();
  }
  return size;
}



//-----------------------------------------------------------------------------
int KFolderTreeItem::countUnreadRecursive()
{
  int count = (mUnread > 0) ? mUnread : 0;

  for ( TQListViewItem *item = firstChild() ;
      item ; item = item->nextSibling() )
  {
    count += static_cast<KFolderTreeItem*>(item)->countUnreadRecursive();
  }

  return count;
}

//-----------------------------------------------------------------------------
void KFolderTreeItem::paintCell( TQPainter * p, const TQColorGroup & cg,
                                  int column, int width, int align )
{
  KFolderTree *ft = static_cast<KFolderTree*>(listView());

  const int unreadRecursiveCount = countUnreadRecursive();
  const int unreadCount = ( mUnread > 0 ) ? mUnread : 0;


  // use a special color for folders which are close to their quota
  TQColorGroup mycg = cg;
  if ( ( column == 0 || column == ft->sizeIndex() ) && folderIsCloseToQuota() )
  {
    mycg.setColor( TQColorGroup::Text, ft->paintInfo().colCloseToQuota );
  }
 
  // use a bold-font for the folder- and the unread-columns
  if ( (column == 0 || column == ft->unreadIndex())
        && ( unreadCount > 0
        || ( !isOpen() && unreadRecursiveCount > 0 ) ) )
  {
    TQFont f = p->font();
    f.setWeight(TQFont::Bold);
    p->setFont(f);
  }


  // most cells can be handled by TDEListView::paintCell, we only need to
  // deal with the folder column if the unread column is not shown

  /* The below is exceedingly silly, but Ingo insists that the unread
   * count that is shown in parenthesis after the folder name must
   * be configurable in color. That means that paintCell needs to do
   * two painting passes which flickers. Since that flicker is not
   * needed when there is the unread column, special case that. */
  if ( ft->isUnreadActive() || column != 0 ) {
    TDEListViewItem::paintCell( p, mycg, column, width, align );
  } else {
    TQListView *lv = listView();
    TQString oldText = text(column);

    // set an empty text so that we can have our own implementation (see further down)
    // but still benefit from TDEListView::paintCell
    setText( column, "" );

    TDEListViewItem::paintCell( p, mycg, column, width, align );

    const TQPixmap *icon = pixmap( column );
    int marg = lv ? lv->itemMargin() : 1;
    int r = marg;

    setText( column, oldText );
    if ( isSelected() )
      p->setPen( mycg.highlightedText() );
    else
      p->setPen( mycg.color( TQColorGroup::Text ) );

    if ( icon ) {
      r += icon->width() + marg;
    }
    TQString t = text( column );
    if (t.isEmpty())
      return;

    // draw the unread-count if the unread-column is not active
    TQString unread;

    if ( unreadCount > 0 || ( !isOpen() && unreadRecursiveCount > 0 ) ) {
      if ( isOpen() )
        unread = " (" + TQString::number( unreadCount ) + ")";
      else if ( unreadRecursiveCount == unreadCount || mType == Root )
        unread = " (" + TQString::number( unreadRecursiveCount ) + ")";
      else
        unread = " (" + TQString::number( unreadCount ) + " + " +
          TQString::number( unreadRecursiveCount-unreadCount ) + ")";
    }

    // check if the text needs to be squeezed
    TQFontMetrics fm( p->fontMetrics() );
    int unreadWidth = fm.width( unread );
    if ( fm.width( t ) + marg + r + unreadWidth > width )
      t = squeezeFolderName( t, fm, width - marg - r - unreadWidth );

    TQRect br;
    p->drawText( r, 0, width-marg-r, height(),
        align | AlignVCenter, t, -1, &br );

    if ( !unread.isEmpty() ) {
      if (!isSelected())
        p->setPen( ft->paintInfo().colUnread );
      p->drawText( br.right(), 0, width-marg-br.right(), height(),
          align | AlignVCenter, unread );
    }
  }
}


TQString KFolderTreeItem::squeezeFolderName( const TQString &text,
                                            const TQFontMetrics &fm,
                                            uint width ) const
{
  return KStringHandler::rPixelSqueeze( text, fm, width );
}

bool KFolderTreeItem::folderIsCloseToQuota() const
{
  return mFolderIsCloseToQuota;
}

void KFolderTreeItem::setFolderIsCloseToQuota( bool v )
{
  if ( mFolderIsCloseToQuota != v) {
    mFolderIsCloseToQuota = v;
    repaint();
  }
}


//=============================================================================


KFolderTree::KFolderTree( TQWidget *parent, const char* name )
  : TDEListView( parent, name ), mUnreadIndex(-1), mTotalIndex(-1), mSizeIndex(-1)
{
  // GUI-options
  setStyleDependantFrameWidth();
  setAcceptDrops(true);
  setDropVisualizer(false);
  setAllColumnsShowFocus(true);
  setShowSortIndicator(true);
  setUpdatesEnabled(true);
  setItemsRenameable(false);
  setRootIsDecorated(true);
  setSelectionModeExt(Extended);
  setAlternateBackground(TQColor());
#if KDE_IS_VERSION( 3, 3, 90 )
  setShadeSortColumn ( false );
#endif
  setFullWidth(true);
  disableAutoSelection();
  setColumnWidth( 0, 120 ); //reasonable default size

  disconnect( header(), TQT_SIGNAL( sizeChange( int, int, int ) ) );
  connect( header(), TQT_SIGNAL( sizeChange( int, int, int ) ),
           TQT_SLOT( slotSizeChanged( int, int, int ) ) );
}

//-----------------------------------------------------------------------------
void KFolderTree::setStyleDependantFrameWidth()
{
  // set the width of the frame to a reasonable value for the current GUI style
  int frameWidth;
  if( style().isA("KeramikStyle") )
    frameWidth = style().pixelMetric( TQStyle::PM_DefaultFrameWidth ) - 1;
  else
    frameWidth = style().pixelMetric( TQStyle::PM_DefaultFrameWidth );
  if ( frameWidth < 0 )
    frameWidth = 0;
  if ( frameWidth != lineWidth() )
    setLineWidth( frameWidth );
}

//-----------------------------------------------------------------------------
void KFolderTree::styleChange( TQStyle& oldStyle )
{
  setStyleDependantFrameWidth();
  TDEListView::styleChange( oldStyle );
}

//-----------------------------------------------------------------------------
void KFolderTree::drawContentsOffset( TQPainter * p, int ox, int oy,
                                       int cx, int cy, int cw, int ch )
{
  bool oldUpdatesEnabled = isUpdatesEnabled();
  setUpdatesEnabled(false);
  TDEListView::drawContentsOffset( p, ox, oy, cx, cy, cw, ch );
  setUpdatesEnabled(oldUpdatesEnabled);
}

//-----------------------------------------------------------------------------
void KFolderTree::contentsMousePressEvent( TQMouseEvent *e )
{
    setSelectionModeExt(Single);
    TDEListView::contentsMousePressEvent(e);
}

//-----------------------------------------------------------------------------
void KFolderTree::contentsMouseReleaseEvent( TQMouseEvent *e )
{
    TDEListView::contentsMouseReleaseEvent(e);
    setSelectionModeExt(Extended);
}

//-----------------------------------------------------------------------------
void KFolderTree::addAcceptableDropMimetype( const char *mimeType, bool outsideOk )
{
  int oldSize = mAcceptableDropMimetypes.size();
  mAcceptableDropMimetypes.resize(oldSize+1);
  mAcceptOutside.resize(oldSize+1);

  mAcceptableDropMimetypes.at(oldSize) =  mimeType;
  mAcceptOutside.setBit(oldSize, outsideOk);
}

//-----------------------------------------------------------------------------
bool KFolderTree::acceptDrag( TQDropEvent* event ) const
{
  TQListViewItem* item = itemAt(contentsToViewport(event->pos()));

  for (uint i = 0; i < mAcceptableDropMimetypes.size(); i++)
  {
    if (event->provides(mAcceptableDropMimetypes[i]))
    {
      if (item)
        return (static_cast<KFolderTreeItem*>(item))->acceptDrag(event);
      else
        return mAcceptOutside[i];
    }
  }
  return false;
}

//-----------------------------------------------------------------------------
void KFolderTree::addUnreadColumn( const TQString & name, int width )
{
  mUnreadIndex = addColumn( name, width );
  setColumnAlignment( mUnreadIndex, tqApp->reverseLayout() ? TQt::AlignLeft : TQt::AlignRight );
  header()->adjustHeaderSize();
}

//-----------------------------------------------------------------------------
void KFolderTree::addTotalColumn( const TQString & name, int width )
{
  mTotalIndex = addColumn( name, width );
  setColumnAlignment( mTotalIndex, tqApp->reverseLayout() ? TQt::AlignLeft : TQt::AlignRight );
  header()->adjustHeaderSize();
}

//-----------------------------------------------------------------------------
void KFolderTree::removeUnreadColumn()
{
  if ( !isUnreadActive() ) return;
  removeColumn( mUnreadIndex );
  if ( isTotalActive() && mTotalIndex > mUnreadIndex )
    mTotalIndex--;
  if ( isSizeActive() && mSizeIndex > mUnreadIndex )
    mSizeIndex--;

  mUnreadIndex = -1;
  header()->adjustHeaderSize();
}

//-----------------------------------------------------------------------------
void KFolderTree::removeTotalColumn()
{
  if ( !isTotalActive() ) return;
  removeColumn( mTotalIndex );
  if ( isUnreadActive() && mTotalIndex < mUnreadIndex )
    mUnreadIndex--;
  if ( isSizeActive() && mTotalIndex < mSizeIndex )
    mSizeIndex--;
  mTotalIndex = -1;
  header()->adjustHeaderSize();
}

//-----------------------------------------------------------------------------
void KFolderTree::addSizeColumn( const TQString & name, int width )
{
  mSizeIndex = addColumn( name, width );
  setColumnAlignment( mSizeIndex, tqApp->reverseLayout() ? TQt::AlignLeft : TQt::AlignRight );
  header()->adjustHeaderSize();
}

//-----------------------------------------------------------------------------
void KFolderTree::removeSizeColumn()
{
  if ( !isSizeActive() ) return;
  removeColumn( mSizeIndex );
  if ( isUnreadActive() && mSizeIndex < mUnreadIndex )
    mUnreadIndex--;
  if ( isTotalActive() && mSizeIndex < mTotalIndex )
    mTotalIndex--;
  mSizeIndex = -1;
  header()->adjustHeaderSize();
}


//-----------------------------------------------------------------------------
void KFolderTree::setFullWidth( bool fullWidth )
{
  if (fullWidth)
    header()->setStretchEnabled( true, 0 );
}

//-----------------------------------------------------------------------------
void KFolderTree::slotSizeChanged( int section, int, int newSize )
{
  viewport()->repaint(
      header()->sectionPos(section), 0, newSize, visibleHeight(), false );
}

#include "kfoldertree.moc"