/*
    KNode, the KDE newsreader
    Copyright (c) 1999-2005 the KNode authors.
    See file AUTHORS for details

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software Foundation,
    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US
*/

#include <tqcursor.h>
#include <tqheader.h>
#include <tqstylesheet.h>
#include <tqtimer.h>

#include <klocale.h>
#include <kdebug.h>
#include <tdeversion.h>
#include <kpopupmenu.h>

#include "knglobals.h"
#include "knconfigmanager.h"
#include "headerview.h"
#include "knhdrviewitem.h"
#include "kngroupmanager.h"
#include "knarticle.h"
#include "knarticlemanager.h"
#include "knmainwidget.h"


KNHeaderView::KNHeaderView(TQWidget *parent, const char *name) :
  KListView(parent,name),
  mSortCol( -1 ),
  mSortAsc( true ),
  mSortByThreadChangeDate( false ),
  mDelayedCenter( -1 ),
  mActiveItem( 0 ),
  mShowingFolder( false ),
  mInitDone( false )
{
  mPaintInfo.subCol    = addColumn( i18n("Subject"), 310 );
  mPaintInfo.senderCol = addColumn( i18n("From"), 115 );
  mPaintInfo.scoreCol  = addColumn( i18n("Score"), 42 );
  mPaintInfo.sizeCol   = addColumn( i18n("Lines"), 42 );
  mPaintInfo.dateCol   = addColumn( i18n("Date"), 102 );

  setDropVisualizer( false );
  setDropHighlighter( false );
  setItemsRenameable( false );
  setItemsMovable( false );
  setAcceptDrops( false );
  setDragEnabled( true );
  setAllColumnsShowFocus( true );
  setSelectionMode( TQListView::Extended );
  setShowSortIndicator( true );
  setShadeSortColumn ( true );
  setRootIsDecorated( true );
  setSorting( mPaintInfo.dateCol );
  header()->setMovingEnabled( true );
  setColumnAlignment( mPaintInfo.sizeCol, TQt::AlignRight );
  setColumnAlignment( mPaintInfo.scoreCol, TQt::AlignRight );

  // due to our own column text squeezing we need to repaint on column resizing
  disconnect( header(), TQT_SIGNAL(sizeChange(int, int, int)) );
  connect( header(), TQT_SIGNAL(sizeChange(int, int, int)),
           TQT_SLOT(slotSizeChanged(int, int, int)) );

  // column selection RMB menu
  mPopup = new KPopupMenu( this );
  mPopup->insertTitle( i18n("View Columns") );
  mPopup->setCheckable( true );
  mPopup->insertItem( i18n("Line Count"),  KPaintInfo::COL_SIZE );
  mPopup->insertItem( i18n("Score"), KPaintInfo::COL_SCORE );

  connect( mPopup, TQT_SIGNAL(activated(int)), this, TQT_SLOT(toggleColumn(int)) );

  // connect to the article manager
  connect( knGlobals.articleManager(), TQT_SIGNAL(aboutToShowGroup()), TQT_SLOT(prepareForGroup()) );
  connect( knGlobals.articleManager(), TQT_SIGNAL(aboutToShowFolder()), TQT_SLOT(prepareForFolder()) );

  new KNHeaderViewToolTip( this );

  installEventFilter( this );
}


KNHeaderView::~KNHeaderView()
{
  // ### crash because KNConfigManager is already deleted here
  // writeConfig();
}


void KNHeaderView::readConfig()
{
  if ( !mInitDone ) {
    TDEConfig *conf = knGlobals.config();
    conf->setGroup( "HeaderView" );
    mSortByThreadChangeDate = conf->readBoolEntry( "sortByThreadChangeDate", false );
    restoreLayout( conf, "HeaderView" );
    mInitDone = true;
  }

  KNConfig::ReadNewsGeneral *rngConf = knGlobals.configManager()->readNewsGeneral();
  toggleColumn( KPaintInfo::COL_SIZE, rngConf->showLines() );
  if ( !mShowingFolder ) // score column is always hidden when showing a folder
    toggleColumn( KPaintInfo::COL_SCORE, rngConf->showScore() );

  mDateFormatter.setCustomFormat( rngConf->dateCustomFormat() );
  mDateFormatter.setFormat( rngConf->dateFormat() );

  KNConfig::Appearance *app = knGlobals.configManager()->appearance();
  TQPalette p = palette();
  p.setColor( TQColorGroup::Base, app->backgroundColor() );
  p.setColor( TQColorGroup::Text, app->textColor() );
  setPalette( p );
  setAlternateBackground( app->alternateBackgroundColor() );
  setFont( app->articleListFont() );
}


void KNHeaderView::writeConfig()
{
  TDEConfig *conf = knGlobals.config();
  conf->setGroup( "HeaderView" );
  conf->writeEntry( "sortByThreadChangeDate", mSortByThreadChangeDate );
  saveLayout( conf, "HeaderView" );

  KNConfig::ReadNewsGeneral *rngConf = knGlobals.configManager()->readNewsGeneral();
  rngConf->setShowLines( mPaintInfo.showSize );
  if ( !mShowingFolder ) // score column is always hidden when showing a folder
    rngConf->setShowScore( mPaintInfo.showScore );
}


void KNHeaderView::setActive( TQListViewItem *i )
{
  KNHdrViewItem *item = static_cast<KNHdrViewItem*>( i );

  if ( !item || item->isActive() )
    return;

  if ( mActiveItem ) {
    mActiveItem->setActive( false );
    repaintItem( mActiveItem );
    mActiveItem = 0;
  }

  item->setActive( true );
  setSelected( item, true );
  setCurrentItem( i );
  ensureItemVisibleWithMargin( i );
  mActiveItem = item;
  emit( itemSelected(item) );
}


void KNHeaderView::clear()
{
  mActiveItem = 0;
  TQListView::clear();
}


void KNHeaderView::ensureItemVisibleWithMargin( const TQListViewItem *i )
{
  if ( !i )
    return;

 TQListViewItem *parent = i->parent();
  while ( parent ) {
    if ( !parent->isOpen() )
      parent->setOpen( true );
    parent = parent->parent();
  }

  mDelayedCenter = -1;
  int y = itemPos( i );
  int h = i->height();

  if ( knGlobals.configManager()->readNewsGeneral()->smartScrolling() &&
      ((y + h + 5) >= (contentsY() + visibleHeight()) ||
       (y - 5 < contentsY())) )
  {
    ensureVisible( contentsX(), y + h/2, 0, h/2 );
    mDelayedCenter = y + h/2;
    TQTimer::singleShot( 300, this, TQT_SLOT(slotCenterDelayed()) );
  } else {
    ensureVisible( contentsX(), y + h/2, 0, h/2 );
  }
}


void KNHeaderView::slotCenterDelayed()
{
  if ( mDelayedCenter != -1 )
    ensureVisible( contentsX(), mDelayedCenter, 0, visibleHeight() / 2 );
}


void KNHeaderView::setSorting( int column, bool ascending )
{
  if ( column == mSortCol ) {
    mSortAsc = ascending;
    if ( mInitDone && column == mPaintInfo.dateCol && ascending )
      mSortByThreadChangeDate = !mSortByThreadChangeDate;
  } else {
    mSortCol = column;
    emit sortingChanged( column );
  }

  KListView::setSorting( column, ascending );

  if ( currentItem() )
    ensureItemVisible( currentItem() );

  if ( mSortByThreadChangeDate )
    setColumnText( mPaintInfo.dateCol , i18n("Date (thread changed)") );
  else
    setColumnText( mPaintInfo.dateCol, i18n("Date") );
}


void KNHeaderView::nextArticle()
{
  KNHdrViewItem *it = static_cast<KNHdrViewItem*>( currentItem() );

  if (it) {
    if (it->isActive()) {  // take current article, if not selected
      if (it->isExpandable())
        it->setOpen(true);
      it = static_cast<KNHdrViewItem*>(it->itemBelow());
    }
  } else
    it = static_cast<KNHdrViewItem*>( firstChild() );

  if(it) {
    clearSelection();
    setActive( it );
    setSelectionAnchor( currentItem() );
  }
}


void KNHeaderView::prevArticle()
{
  KNHdrViewItem *it = static_cast<KNHdrViewItem*>( currentItem() );

  if (it && it->isActive()) {  // take current article, if not selected
    if (it)
      it = static_cast<KNHdrViewItem*>(it->itemAbove());
    else
      it = static_cast<KNHdrViewItem*>( firstChild() );
  }

  if (it) {
    clearSelection();
    setActive( it );
    setSelectionAnchor( currentItem() );
  }
}


void KNHeaderView::incCurrentArticle()
{
  TQListViewItem *lvi = currentItem();
  if ( lvi && lvi->isExpandable() )
    lvi->setOpen( true );
  if ( lvi && lvi->itemBelow() ) {
    setCurrentItem( lvi->itemBelow() );
    ensureItemVisible( currentItem() );
    setFocus();
  }
}

void KNHeaderView::decCurrentArticle()
{
  TQListViewItem *lvi = currentItem();
  if ( lvi && lvi->itemAbove() ) {
    if ( lvi->itemAbove()->isExpandable() )
      lvi->itemAbove()->setOpen( true );
    setCurrentItem( lvi->itemAbove() );
    ensureItemVisible( currentItem() );
    setFocus();
  }
}


void KNHeaderView::selectCurrentArticle()
{
  clearSelection();
  setActive( currentItem() );
}


bool KNHeaderView::nextUnreadArticle()
{
  if ( !knGlobals.groupManager()->currentGroup() )
    return false;

  KNHdrViewItem *next, *current;
  KNRemoteArticle *art;

  current = static_cast<KNHdrViewItem*>( currentItem() );
  if ( !current )
    current = static_cast<KNHdrViewItem*>( firstChild() );

  if(!current)
    return false;

  art = static_cast<KNRemoteArticle*>( current->art );

  if ( !current->isActive() && !art->isRead() ) // take current article, if unread & not selected
    next = current;
  else {
    if ( current->isExpandable() && art->hasUnreadFollowUps() && !current->isOpen() )
      setOpen( current, true );
    next = static_cast<KNHdrViewItem*>( current->itemBelow() );
  }

  while ( next ) {
    art = static_cast<KNRemoteArticle*>( next->art );
    if ( !art->isRead() )
      break;
    else {
      if ( next->isExpandable() && art->hasUnreadFollowUps() && !next->isOpen() )
        setOpen( next, true );
      next = static_cast<KNHdrViewItem*>( next->itemBelow() );
    }
  }

  if ( next ) {
    clearSelection();
    setActive( next );
    setSelectionAnchor( currentItem() );
    return true;
  }
  return false;
}


bool KNHeaderView::nextUnreadThread()
{
  KNHdrViewItem *next, *current;
  KNRemoteArticle *art;

  if ( !knGlobals.groupManager()->currentGroup() )
    return false;

  current = static_cast<KNHdrViewItem*>( currentItem() );
  if ( !current )
    current = static_cast<KNHdrViewItem*>( firstChild() );

  if ( !current )
    return false;

  art = static_cast<KNRemoteArticle*>( current->art );

  if ( current->depth() == 0 && !current->isActive() && (!art->isRead() || art->hasUnreadFollowUps()) )
    next = current; // take current article, if unread & not selected
  else
    next = static_cast<KNHdrViewItem*>( current->itemBelow() );

  while ( next ) {
    art = static_cast<KNRemoteArticle*>( next->art );

    if ( next->depth() == 0 ) {
      if ( !art->isRead() || art->hasUnreadFollowUps() )
        break;
    }
    next = static_cast<KNHdrViewItem*>( next->itemBelow() );
  }

  if ( next ) {
    setCurrentItem( next );
    if ( art->isRead() )
      nextUnreadArticle();
    else {
      clearSelection();
      setActive( next );
      setSelectionAnchor( currentItem() );
    }
    return true;
  }
  return false;
}


void KNHeaderView::toggleColumn( int column, int mode )
{
  bool *show = 0;
  int  *col  = 0;
  int  width = 0;

  switch ( static_cast<KPaintInfo::ColumnIds>( column ) )
  {
    case KPaintInfo::COL_SIZE:
      show  = &mPaintInfo.showSize;
      col   = &mPaintInfo.sizeCol;
      width = 42;
      break;
    case KPaintInfo::COL_SCORE:
      show  = &mPaintInfo.showScore;
      col   = &mPaintInfo.scoreCol;
      width = 42;
      break;
    default:
      return;
  }

  if ( mode == -1 )
    *show = !*show;
  else
    *show = mode;

  mPopup->setItemChecked( column, *show );

  if (*show) {
    header()->setResizeEnabled( true, *col );
    setColumnWidth( *col, width );
  }
  else {
    header()->setResizeEnabled( false, *col );
    header()->setStretchEnabled( false, *col );
    hideColumn( *col );
  }

  if ( mode == -1 ) // save config when toggled
    writeConfig();
}


void KNHeaderView::prepareForGroup()
{
  mShowingFolder = false;
  header()->setLabel( mPaintInfo.senderCol, i18n("From") );
  KNConfig::ReadNewsGeneral *rngConf = knGlobals.configManager()->readNewsGeneral();
  toggleColumn( KPaintInfo::COL_SCORE, rngConf->showScore() );
}


void KNHeaderView::prepareForFolder()
{
  mShowingFolder = true;
  header()->setLabel( mPaintInfo.senderCol, i18n("Newsgroups / To") );
  toggleColumn( KPaintInfo::COL_SCORE, false ); // local folders have no score
}


bool KNHeaderView::event( TQEvent *e )
{
  // we don't want to have the alternate list background restored
  // to the system defaults!
  if (e->type() == TQEvent::ApplicationPaletteChange)
    return TQListView::event(e);
  else
    return KListView::event(e);
}

void KNHeaderView::contentsMousePressEvent( TQMouseEvent *e )
{
  if (!e) return;

  bool selectMode=(( e->state() & ShiftButton ) || ( e->state() & ControlButton ));

  TQPoint vp = contentsToViewport(e->pos());
  TQListViewItem *i = itemAt(vp);

  KListView::contentsMousePressEvent( e );

  if ( i ) {
    int decoLeft = header()->sectionPos( 0 ) +
        treeStepSize() * ( (i->depth() - 1) + ( rootIsDecorated() ? 1 : 0) );
    int decoRight = kMin( decoLeft + treeStepSize() + itemMargin(),
        header()->sectionPos( 0 ) + header()->sectionSize( 0 ) );
    bool rootDecoClicked = vp.x() > decoLeft && vp.x() < decoRight;

    if( !selectMode && i->isSelected() && !rootDecoClicked )
      setActive( i );
  }
}


void KNHeaderView::contentsMouseDoubleClickEvent( TQMouseEvent *e )
{
  if (!e) return;

  TQListViewItem *i = itemAt( contentsToViewport(e->pos()) );
  if (i) {
    emit doubleClick( i );
    return;
  }

  KListView::contentsMouseDoubleClickEvent( e );
}


void KNHeaderView::keyPressEvent(TQKeyEvent *e)
{
  if (!e) return;

  TQListViewItem *i = currentItem();

  switch(e->key()) {
    case Key_Space:
    case Key_Backspace:
    case Key_Delete:
      e->ignore(); // don't eat them
    break;
    case Key_Enter:
    case Key_Return:
      setActive( i );
    break;

    default:
      KListView::keyPressEvent (e);
  }
}


TQDragObject* KNHeaderView::dragObject()
{
  KNHdrViewItem *item = static_cast<KNHdrViewItem*>( itemAt(viewport()->mapFromGlobal(TQCursor::pos())) );
  if (item)
    return item->dragObject();
  else
    return 0;
}


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


bool KNHeaderView::eventFilter(TQObject *o, TQEvent *e)
{
  if ((e->type() == TQEvent::KeyPress) && (TQT_TQKEYEVENT(e)->key() == Key_Tab)) {
    emit(focusChangeRequest(this));
    if (!hasFocus())  // focusChangeRequest was successful
      return true;
  }

  // right click on header
  if ( e->type() == TQEvent::MouseButtonPress &&
       TQT_TQMOUSEEVENT(e)->button() == Qt::RightButton &&
       o->isA(TQHEADER_OBJECT_NAME_STRING) )
  {
    mPopup->popup( TQT_TQMOUSEEVENT(e)->globalPos() );
    return true;
  }

  return KListView::eventFilter(o, e);
}


void KNHeaderView::focusInEvent(TQFocusEvent *e)
{
  TQListView::focusInEvent(e);
  emit focusChanged(e);
}


void KNHeaderView::focusOutEvent(TQFocusEvent *e)
{
  TQListView::focusOutEvent(e);
  emit focusChanged(e);
}


void KNHeaderView::resetCurrentTime()
{
  mDateFormatter.reset();
  TQTimer::singleShot( 1000, this, TQT_SLOT(resetCurrentTime()) );
}


//BEGIN: KNHeaderViewToolTip ==================================================

KNHeaderViewToolTip::KNHeaderViewToolTip( KNHeaderView *parent ) :
  TQToolTip( parent->viewport() ),
  listView( parent )
{
}


void KNHeaderViewToolTip::maybeTip( const TQPoint &p )
{
  const KNHdrViewItem *item = static_cast<KNHdrViewItem*>( listView->itemAt( p ) );
  if ( !item )
    return;
  const int column = listView->header()->sectionAt( p.x() );
  if ( column == -1 )
    return;

  if ( !item->showToolTip( column ) )
    return;

  const TQRect itemRect = listView->itemRect( item );
  if ( !itemRect.isValid() )
    return;
  const TQRect headerRect = listView->header()->sectionRect( column );
  if ( !headerRect.isValid() )
    return;

  tip( TQRect( headerRect.left(), itemRect.top(), headerRect.width(), itemRect.height() ),
       TQStyleSheet::escape( item->text( column ) ) );
}

//END: KNHeaderViewToolTip ====================================================

#include "headerview.moc"