/*
    KNode, the KDE newsreader
    Copyright (c) 2004-2005 Volker Krause <volker.krause@rwth-aachen.de>

    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 <kiconloader.h>
#include <tdelistview.h>
#include <tdelocale.h>
#include <tdepopupmenu.h>

#include "knglobals.h"
#include "knconfig.h"
#include "knconfigmanager.h"
#include "knnntpaccount.h"
#include "knaccountmanager.h"
#include "kngroup.h"
#include "kngroupmanager.h"
#include "knfolder.h"
#include "knfoldermanager.h"
#include "headerview.h"
#include "kncollectionview.h"
#include "kncollectionviewitem.h"

KNCollectionView::KNCollectionView(TQWidget *parent, const char* name) :
  KFolderTree(parent, name),
  mActiveItem( 0 ),
  mPopup( 0 )
{
  setDragEnabled(true);
  addAcceptableDropMimetype("x-knode-drag/article", false);
  addAcceptableDropMimetype("x-knode-drag/folder", true);
  addColumn(i18n("Name"),162);
  setDropHighlighter(true);

  // popup menu to enable/disable unread and total columns
  header()->setClickEnabled( true );
  header()->installEventFilter( this );
  mPopup = new TDEPopupMenu( this );
  mPopup->insertTitle( i18n("View Columns") );
  mPopup->setCheckable( true );
  mUnreadPop = mPopup->insertItem( i18n("Unread Column"), this, TQ_SLOT(toggleUnreadColumn()) );
  mTotalPop = mPopup->insertItem( i18n("Total Column"), this, TQ_SLOT(toggleTotalColumn()) );

  // add unread and total columns if necessary
  readConfig();

  // load accounts and folders
  reloadAccounts();
  reloadFolders();

  // connect to the account manager
  KNAccountManager* am = knGlobals.accountManager();
  connect(am, TQ_SIGNAL(accountAdded(KNNntpAccount*)), TQ_SLOT(addAccount(KNNntpAccount*)));
  connect(am, TQ_SIGNAL(accountRemoved(KNNntpAccount*)), TQ_SLOT(removeAccount(KNNntpAccount*)));
  connect(am, TQ_SIGNAL(accountModified(KNNntpAccount*)), TQ_SLOT(updateAccount(KNNntpAccount*)));

  // connect to the group manager
  KNGroupManager* gm = knGlobals.groupManager();
  connect(gm, TQ_SIGNAL(groupAdded(KNGroup*)), TQ_SLOT(addGroup(KNGroup*)));
  connect(gm, TQ_SIGNAL(groupRemoved(KNGroup*)), TQ_SLOT(removeGroup(KNGroup*)));
  connect(gm, TQ_SIGNAL(groupUpdated(KNGroup*)), TQ_SLOT(updateGroup(KNGroup*)));

  // connect to the folder manager
  KNFolderManager* fm = knGlobals.folderManager();
  connect(fm, TQ_SIGNAL(folderAdded(KNFolder*)), TQ_SLOT(addPendingFolders()));
  connect(fm, TQ_SIGNAL(folderRemoved(KNFolder*)), TQ_SLOT(removeFolder(KNFolder*)));
  connect(fm, TQ_SIGNAL(folderActivated(KNFolder*)), TQ_SLOT(activateFolder(KNFolder*)));

  installEventFilter(this);
}


KNCollectionView::~KNCollectionView()
{
  writeConfig();
}



void KNCollectionView::readConfig()
{
  TDEConfig *conf = knGlobals.config();
  conf->setGroup( "GroupView" );

  // execute the listview layout stuff only once
  static bool initDone = false;
  if (!initDone) {
    initDone = true;
    const int unreadColumn = conf->readNumEntry("UnreadColumn", 1);
    const int totalColumn = conf->readNumEntry("TotalColumn", 2);

    // we need to _activate_ them in the correct order
    // this is ugly because we can't use header()->moveSection
    // but otherwise the restoreLayout doesn't know that to do
    if (unreadColumn != -1 && unreadColumn < totalColumn)
      addUnreadColumn( i18n("Unread"), 48 );
    if (totalColumn != -1)
      addTotalColumn( i18n("Total"), 36 );
    if (unreadColumn != -1 && unreadColumn > totalColumn)
      addUnreadColumn( i18n("Unread"), 48 );
    updatePopup();

    restoreLayout( knGlobals.config(), "GroupView" );
  }

  // font & color settings
  KNConfig::Appearance *app = knGlobals.configManager()->appearance();
  setFont( app->groupListFont() );

  TQPalette p = palette();
  p.setColor( TQColorGroup::Base, app->backgroundColor() );
  p.setColor( TQColorGroup::Text, app->textColor() );
  setPalette( p );
  setAlternateBackground( app->backgroundColor() );
  // FIXME: make this configurable
  mPaintInfo.colUnread = TQColor( "blue" );
  mPaintInfo.colFore = app->textColor();
  mPaintInfo.colBack = app->backgroundColor();
}


void KNCollectionView::writeConfig()
{
  TDEConfig *conf = knGlobals.config();
  conf->setGroup( "GroupView" );
  saveLayout( knGlobals.config(), "GroupView" );
  conf->writeEntry( "UnreadColumn", unreadIndex() );
  conf->writeEntry( "TotalColumn", totalIndex() );
}



void KNCollectionView::addAccount(KNNntpAccount *a)
{
  // add account item
  KNCollectionViewItem* item = new KNCollectionViewItem( this, KFolderTreeItem::News );
  a->setListItem( item );
  item->setOpen( a->wasOpen() );

  // add groups for this account
  TQValueList<KNGroup*> groups = knGlobals.groupManager()->groupsOfAccount( a );
  for ( TQValueList<KNGroup*>::Iterator it = groups.begin(); it != groups.end(); ++it ) {
    KNCollectionViewItem *gitem = new KNCollectionViewItem( item, KFolderTreeItem::News );
    (*it)->setListItem( gitem );
    (*it)->updateListItem();
  }
}


void KNCollectionView::removeAccount(KNNntpAccount *a)
{
  if(!a->listItem())
    return;
  KNCollectionViewItem *child = 0, *aitem = a->listItem();
  while((child = static_cast<KNCollectionViewItem*>(aitem->firstChild())))
    removeGroup(static_cast<KNGroup*>(child->coll));
  delete aitem;
  a->setListItem(0);
}


void KNCollectionView::updateAccount(KNNntpAccount *a)
{
  a->updateListItem();
}


void KNCollectionView::reloadAccounts()
{
  KNAccountManager* am = knGlobals.accountManager();
  TQValueList<KNNntpAccount*>::Iterator it;
  for ( it = am->begin(); it != am->end(); ++it ) {
    removeAccount( *it );
    addAccount( *it );
  }
}



void KNCollectionView::addGroup(KNGroup *g)
{
  if (!g->account()->listItem())
    return;

  KNCollectionViewItem *gitem =
      new KNCollectionViewItem( g->account()->listItem(), KFolderTreeItem::News );
  g->setListItem(gitem);
  updateGroup(g);
}


void KNCollectionView::removeGroup(KNGroup *g)
{
  if (!g->listItem())
    return;

  delete g->listItem();
  g->setListItem(0);
}


void KNCollectionView::updateGroup(KNGroup *g)
{
  g->updateListItem();
}



void KNCollectionView::addFolder(KNFolder *f)
{
  KNCollectionViewItem *it;

  if (!f->parent()) {
    // root folder
    it = new KNCollectionViewItem(this, KFolderTreeItem::Local);
  } else {
    // make sure the parent folder has already been added
    if (!f->parent()->listItem())
      addFolder( static_cast<KNFolder*>(f->parent()) );
    // handle special folders
    KFolderTreeItem::Type type = KFolderTreeItem::Other;
    switch ( f->id() ) {
      case 1:
        type = KFolderTreeItem::Drafts; break;
      case 2:
        type = KFolderTreeItem::Outbox; break;
      case 3:
        type = KFolderTreeItem::SentMail; break;
    }
    it = new KNCollectionViewItem( f->parent()->listItem(), KFolderTreeItem::Local, type );
  }
  f->setListItem( it );
  updateFolder( f );
}


void KNCollectionView::removeFolder(KNFolder* f)
{
  if(!f->listItem())
    return;
  KNCollectionViewItem *child = 0, *it = f->listItem();
  while((child = static_cast<KNCollectionViewItem*>(it->firstChild())))
    removeFolder(static_cast<KNFolder*>(child->coll));
  delete f->listItem();
  f->setListItem(0);
}


void KNCollectionView::reloadFolders()
{
  // remove existing folder items
  removeFolder(knGlobals.folderManager()->root());

  // add folder items
  addPendingFolders();
}


void KNCollectionView::addPendingFolders()
{
  TQValueList<KNFolder*> folders = knGlobals.folderManager()->folders();
  for ( TQValueList<KNFolder*>::Iterator it = folders.begin(); it != folders.end(); ++it )
    if ( !(*it)->listItem() )
      addFolder( (*it) );
  // now open the folders if they were open in the last session
  for ( TQValueList<KNFolder*>::Iterator it = folders.begin(); it != folders.end(); ++it )
    if ( (*it)->listItem())
      (*it)->listItem()->setOpen( (*it)->wasOpen() );
}


void KNCollectionView::activateFolder(KNFolder* f)
{
  if(f->listItem())
    setActive( f->listItem() );
}


void KNCollectionView::updateFolder(KNFolder* f)
{
  f->updateListItem();
}


void KNCollectionView::reload()
{
  reloadAccounts();
  reloadFolders();
}

void KNCollectionView::setActive( TQListViewItem *i )
{
  if (!i || mActiveItem == i)
    return;

  clearSelection();
  setSelected( i, true );
  setCurrentItem( i );
  mActiveItem = i;
  emit( selectionChanged( i ) );
}


void KNCollectionView::nextGroup()
{
  incCurrentFolder();
  setActive( currentItem() );
}


void KNCollectionView::prevGroup()
{
  decCurrentFolder();
  setActive( currentItem() );
}


void KNCollectionView::decCurrentFolder()
{
  TQListViewItemIterator it( currentItem() );
  --it;
  KFolderTreeItem* fti = static_cast<KFolderTreeItem*>(it.current());
  if (fti) {
    ensureItemVisible( fti );
    setFocus();
    setCurrentItem( fti );
  }
}


void KNCollectionView::incCurrentFolder()
{
  TQListViewItemIterator it( currentItem() );
  ++it;
  KFolderTreeItem* fti = static_cast<KFolderTreeItem*>(it.current());
  if (fti) {
    ensureItemVisible( fti );
    setFocus();
    setCurrentItem( fti );
  }
}


void KNCollectionView::selectCurrentFolder()
{
  KFolderTreeItem* fti = static_cast<KFolderTreeItem*>( currentItem() );
  if (fti) {
    ensureItemVisible( fti );
    setActive( fti );
  }
}


TQDragObject* KNCollectionView::dragObject()
{
  KFolderTreeItem *item = static_cast<KFolderTreeItem*>
      (itemAt(viewport()->mapFromGlobal(TQCursor::pos())));
  if ( item && item->protocol() == KFolderTreeItem::Local && item->type() == KFolderTreeItem::Other ) {
    TQDragObject *d = new TQStoredDrag( "x-knode-drag/folder", viewport() );
    d->setPixmap( SmallIcon("folder") );
    return d;
  }
  return 0;
}


void KNCollectionView::contentsDropEvent( TQDropEvent *e )
{
  cleanItemHighlighter(); // necessary since we overwrite TDEListView::contentsDropEvent()
  TQListViewItem *item = itemAt( contentsToViewport(e->pos()) );
  KNCollectionViewItem *fti = static_cast<KNCollectionViewItem*>(item);
  if (fti && (fti->coll) && acceptDrag(e)) {
    emit folderDrop( e, fti );
    e->accept( true );
  }
  else
    e->accept( false );
}



void KNCollectionView::toggleUnreadColumn()
{
  if ( isUnreadActive() )
    removeUnreadColumn();
  else
    addUnreadColumn( i18n("Unread"), 48 );
  mPopup->setItemChecked( mUnreadPop, isUnreadActive() );
  reload();
}


void KNCollectionView::toggleTotalColumn()
{
  if ( isTotalActive() )
    removeTotalColumn();
  else
    addTotalColumn( i18n("Total"), 36 );
  mPopup->setItemChecked( mTotalPop, isTotalActive() );
  reload();
}

void KNCollectionView::updatePopup() const
{
  mPopup->setItemChecked( mUnreadPop, isUnreadActive() );
  mPopup->setItemChecked( mTotalPop, isTotalActive() );
}



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

  // header popup menu
  if ( e->type() == TQEvent::MouseButtonPress &&
       static_cast<TQMouseEvent*>(e)->button() == TQt::RightButton &&
       o->isA("TQHeader") )
  {
    mPopup->popup( static_cast<TQMouseEvent*>(e)->globalPos() );
    return true;
  }

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


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


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


#include "kncollectionview.moc"
