/***************************************************************************
                  treewidget.cpp - Tree/detailed list widget
                             -------------------
    copyright            : (C) 2002-2003 Marc Britton <consume@optusnet.com.au>
                           (C) 2004      Michal Rudolf <mrudolf@kdewebdev.org>
                           (C) 2008      Andras Mantia <amantia@kdewebdev.org>
                           (C) 2008      Eric Laffoon  <eric@kdewebdev.org>
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

/* KDE INCLUDES */
#include <tdelocale.h>
#include <tdeglobal.h>
#include <kiconloader.h>
#include <tdelistview.h>

/* QT INCLUDES */
#include <tqstring.h>
#include <tqwidget.h>
#include <tqstringlist.h>
#include <tqevent.h>
#include <tqlistview.h>

/* OTHER INCLUDES */
#include <specials.h>
#include "treewidget.h"
#include "kommanderplugin.h"
#include "specials.h"

#define TW_FUNCTION 275
#define addColumnTree TW_FUNCTION+1
#define setAltBackground TW_FUNCTION+2
#define setColAlignment TW_FUNCTION+3
//#define colCount TW_FUNCTION+3
#define colCaption TW_FUNCTION+4
#define setColWidth TW_FUNCTION+5
#define setSortCol TW_FUNCTION+6
#define TW_LAST_FUNCTION setSortCol

enum Functions {
  FirstFunction = 189,
  SelectedIndexes,
  TW_childCount,
  TW_setOpen,
  TW_isOpen,
  LastFunction
};

TreeWidget::TreeWidget(TQWidget *a_parent, const char *a_name)
  : TDEListView(a_parent, a_name), KommanderWidget(TQT_TQOBJECT(this))
{
  TQStringList states;
  states << "default";
  setStates(states);
  setDisplayStates(states);
  setPathSeparator("/");
  KommanderPlugin::setDefaultGroup(Group::DCOP);
  KommanderPlugin::registerFunction(SelectedIndexes, "selectedIndexes(TQString widget)",  "", 1);
  KommanderPlugin::registerFunction(addColumnTree, "addColumn(TQString widget, const TQString & label, int width = -1 )", i18n("Add column at end with column header"), 2, 3);
  KommanderPlugin::registerFunction(setSortCol, "setSortColumn(TQString widget, int column, bool ascending=true)", i18n("Set sorting for a column"), 2, 3);
  //KommanderPlugin::registerFunction(setAltBackground, "setAltBackground(TQString widget, const TQColor & c)",  i18n("Alternate colors in list view"), 2);
//  KommanderPlugin::registerFunction(colCount, "colCount(TQString widget)", i18n("Get the column count"), 1);
  KommanderPlugin::registerFunction(colCaption, "columnCaption(TQString widget, int column)", i18n("Get the column caption for column index"), 2);
  KommanderPlugin::registerFunction(setColWidth, "setColWidth(TQString widget, int column, int width)", i18n("Set the pixel width for column index - use 0 to hide"), 3);
  KommanderPlugin::registerFunction(setColAlignment, "setColumnAlignment(TQString widget, int column, TQString Alignment)", i18n("Set to <i>left</i>, <i>right</i> or <i>center</i>, case insensitive "), 3);
  KommanderPlugin::registerFunction(TW_childCount, "childCount(TQString widget)", i18n("Get the count of top level items."), 1);
  KommanderPlugin::registerFunction(TW_setOpen, "setOpen(TQString widget, int Index, bool Open)", i18n("Expand or collapse a node."), 3);
  KommanderPlugin::registerFunction(TW_isOpen, "isOpen(TQString widget, int Index)", i18n("See if node is open or closed."), 2);
}

TreeWidget::~TreeWidget()
{
}

TQString TreeWidget::pathSeparator() const
{
  return m_pathSeparator;
}

void TreeWidget::setPathSeparator(const TQString& a_pathSep)
{
  m_pathSeparator = a_pathSep;
}

void TreeWidget::addItemFromString(const TQString& s)
{
  TQStringList elements = TQStringList::split(m_pathSeparator, s, true);
  if (elements.count() > 1)
    setRootIsDecorated(true);
  TQListViewItem* parent = 0;
  if (m_lastPath.size() < elements.count())
    m_lastPath.resize(elements.count());
  uint i = 0;
  for (TQStringList::ConstIterator it = elements.begin(); it != elements.end(); ++it) 
  {
    if (m_lastPath[i] && m_lastPath[i]->text(0) == elements[i])
    {
      parent = m_lastPath[i];
      i++;
      continue;
    }
    else 
    {
      TQListViewItem* item = (i>0) ? parent->firstChild() : firstChild();
      while (item)
      {
        if (item->text(0) == *it)
          break;
        item = item->nextSibling(); 
      }
      if (item)
        parent = item;
      else 
        parent = itemFromString(parent, *it);
      m_lastPath.insert(i, parent); 
      i++;
    }
  }
}

TQListViewItem* TreeWidget::itemFromString(TQListViewItem* parent, const TQString& s)
{
  TQStringList elements;
  if (s.contains("\t"))
    elements = TQStringList::split("\t", s, true);
  else
    elements = TQStringList::split("\\t", s, true);
  int cols = elements.count();
  if (cols >= columns())
    cols = columns();
  TQListViewItem* item;
  if (parent)
    item = new TQListViewItem(parent);
  else
    item = new TQListViewItem(this);
  int i = 0;
  for (TQStringList::ConstIterator it = elements.constBegin(); it != elements.constEnd(); ++it) 
    item->setText(i++, *it);
  return item;
}

int TreeWidget::itemToIndex(TQListViewItem* item)
{
//  if (!item->isSelected())
//    return -1;
  TQListViewItemIterator it(this);
  int index = 0;
  while (it.current()) {
    if (it.current() == item)
      return index;
    ++it;
    ++index;
  }
  return -1;
}

int TreeWidget::itemToIndexSafe(TQListViewItem* item)
{
  TQListViewItemIterator it(this);
  int index = 0;
  while (it.current()) {
    if (it.current() == item)
      return index;
    ++it;
    ++index;
  }
  return -1;
}

TQListViewItem* TreeWidget::indexToItem(int item)
{
  TQListViewItemIterator it(this);
  int index = 0;
  while (it.current()) {
    if (index == item)
      return it.current();
    ++it;
    ++index;
  }
  return 0;
}

TQString TreeWidget::itemText(TQListViewItem* item) const
{
  if (!item)
    return TQString();
  TQStringList items;
  for (int i=0; i<columns(); i++)
    items.append(item->text(i));
  return items.join("\t");
}

TQString TreeWidget::itemsText()
{
  TQStringList items;
  TQListViewItemIterator it(this);
  while (it.current()) 
  {
    TQString path = itemPath(it.current());
    if (path.isEmpty())
      items.append(itemText(it.current()));
    else 
      items.append(TQString("%1%2%3").arg(path).arg(m_pathSeparator)
        .arg(itemText(it.current())));
    ++it;
  }
  return items.join("\n");
}

TQString TreeWidget::itemPath(TQListViewItem* item) const
{
  if (!item)
    return TQString();
  item = item->parent();
  if (!item)
    return TQString();
  TQStringList path;
  while (item)
  {
    path.prepend(item->text(0));
    item = item->parent();
  }
  return path.join(m_pathSeparator);
}

TQString TreeWidget::currentState() const
{
  return TQString("default");
}

bool TreeWidget::isKommanderWidget() const
{
  return true;
}

void TreeWidget::setCurrentItem(TQListViewItem* item)
{
  TDEListView::setCurrentItem(item);
  setSelected(item, true);
  ensureItemVisible(item);
}

TQStringList TreeWidget::associatedText() const
{
  return KommanderWidget::associatedText();
}

void TreeWidget::setAssociatedText(const TQStringList& a_at)
{
  KommanderWidget::setAssociatedText(a_at);
}

void TreeWidget::setPopulationText(const TQString& a_text)
{
  KommanderWidget::setPopulationText( a_text );
}

TQString TreeWidget::populationText() const
{
  return KommanderWidget::populationText();
}

void TreeWidget::populate()
{
  setWidgetText(KommanderWidget::evalAssociatedText( populationText()));
}

void TreeWidget::setWidgetText(const TQString &a_text)
{
  handleDCOP(DCOP::setText, a_text);
  emit widgetTextChanged(a_text);
}

void TreeWidget::showEvent(TQShowEvent* e)
{
    TQListView::showEvent( e );
    emit widgetOpened();
}

void TreeWidget::contextMenuEvent( TQContextMenuEvent * e )
{
  e->accept();
  TQPoint p = e->globalPos();
  emit contextMenuRequested(p.x(), p.y());
}

void TreeWidget::setColAlign(int column, const TQString& align)
{
  if (align.lower() == "left")
    setColumnAlignment (column, TQt::AlignLeft);
  else if (align.lower() == "right")
    setColumnAlignment (column, TQt::AlignRight);
  else if (align.lower() == "center")
    setColumnAlignment (column, TQt::AlignCenter);
}

bool TreeWidget::isFunctionSupported(int f)
{
  return f == DCOP::insertItem || f == DCOP::text || f == DCOP::setText || f == DCOP::insertItems ||
    f == DCOP::selection || f == DCOP::setSelection || f == DCOP::clear || f == DCOP::removeItem || 
    f == DCOP::currentItem || f == DCOP::setCurrentItem || f == DCOP::findItem || f == DCOP::item || 
      f == DCOP::itemPath || f == DCOP::itemDepth || f == DCOP::setPixmap || f == DCOP::setColumnCaption || f == DCOP::removeColumn || f == DCOP::columnCount || f == DCOP::geometry || f == DCOP::hasFocus || f == DCOP::getBackgroundColor || f == DCOP::setBackgroundColor  || (f > FirstFunction && f < LastFunction) || (f >= TW_FUNCTION && f <= TW_LAST_FUNCTION);
}

TQString TreeWidget::handleDCOP(int function, const TQStringList& args)
{
  switch (function) {
    case DCOP::insertItem:
      addItemFromString(args[0]);
      break;
    case DCOP::text:
      return itemsText();
    case DCOP::setText:
      clear();       /* break omitted: setText is equivalent to clear and insertItems */
      m_lastPath.clear();
    case DCOP::insertItems:
    {
      TQStringList items(TQStringList::split("\n", args[0], true));
      for (TQStringList::ConstIterator it = items.constBegin(); it != items.constEnd(); ++it) 
        addItemFromString(*it);
      break;
    }
    case TW_setOpen:
      setOpen(indexToItem(args[0].toInt()), args[1].toInt());
      break;
    case TW_isOpen:
      return TQString::number(isOpen(indexToItem(args[0].toInt())));
      break;
    case SelectedIndexes:
    {
      TQString selection = "";
      TQListViewItemIterator it(this);
      while (it.current()) 
      {
        if (it.current()->isSelected())
        {        
          selection.append(TQString("%1\n").arg(itemToIndexSafe(it.current())));
        }
        ++it;
      }
      if (!selection.isEmpty())
        selection = selection.left(selection.length() - 1);
      return selection;
      break;
    }
    case DCOP::selection:
    {
      TQString selection = "";
      TQListViewItemIterator it(this);
      while (it.current()) 
      {
        if (it.current()->isSelected())
            selection.append(itemText(it.current()) + "\n");
        ++it;
      }
      if (!selection.isEmpty())
        selection = selection.left(selection.length() - 1);
      return selection;
      break;
    }
    case DCOP::setSelection:
      if (selectionModeExt() == Single || selectionModeExt() == NoSelection)
        setCurrentItem(findItem(args[0], 0));
      else
      {
        clearSelection();
        TQStringList items(TQStringList::split("\n", args[0]));
        for (TQStringList::ConstIterator it = items.begin(); it != items.end(); ++it)
        {
          TQListViewItem* item = findItem(*it, 0);
          if (item)
          {
            item->setSelected(true);
            ensureItemVisible(item);
          }
        }
      }
      break;
    case DCOP::clear:
      clear();
      m_lastPath.clear();
      break;
    case DCOP::removeItem:
    {
      if (args[0].toInt() >= 0 )
      {
        delete indexToItem(args[0].toInt());
        m_lastPath.clear();
      }
      break;
    }
    case DCOP::currentItem:
      return TQString::number(itemToIndexSafe(currentItem()));
      break;
    case DCOP::setCurrentItem:
      setCurrentItem(indexToItem(args[0].toInt()));
      break;
    case DCOP::findItem:
      if (!args[1])
        return TQString::number(itemToIndexSafe(findItem(args[0], 0)));
      else
      {
        if (args[2].toUInt() && args[3].toUInt())
          return TQString::number(itemToIndexSafe(findItem(args[0], args[1].toInt())));
        else if (args[2].toUInt())
          return TQString::number(itemToIndexSafe(findItem(args[0], args[1].toInt(), TQt::CaseSensitive | TQt::Contains)));
        else if (args[3].toUInt())
          return TQString::number(itemToIndexSafe(findItem(args[0], args[1].toInt(), TQt::ExactMatch)));
        else
          return TQString::number(itemToIndexSafe(findItem(args[0], args[1].toInt(), TQt::Contains)));
      }
      break;
    case DCOP::item:
      return itemText(indexToItem(args[0].toInt()));
      break;
    case DCOP::itemPath:
      return itemPath(indexToItem(args[0].toInt()));
      break;
    case DCOP::itemDepth:
    {
      TQListViewItem* item = indexToItem(args[0].toInt());
      return (item) ? TQString::number(item->depth()) : TQString::number(-1);
    }
    case DCOP::setPixmap:
    {
      TQPixmap pixmap = TDEGlobal::iconLoader()->loadIcon(args[0], TDEIcon::Small);
      if (args[1].toInt() == -1)
        for (TQListViewItemIterator it(this); it.current(); ++it)
          it.current()->setPixmap(0, pixmap);
      else 
      { 
        TQListViewItem* item = indexToItem(args[1].toInt());
        if (item)
          item->setPixmap(0, pixmap);
      }
      break;
    }
    case DCOP::setColumnCaption:
      if (columns() >= args[0].toInt())
        setColumnText(args[0].toInt(), args[1]);
      break;
    case DCOP::getBackgroundColor:
      return this->paletteBackgroundColor().name();
      break;
    case DCOP::setBackgroundColor:
    {
      TQColor color;
      color.setNamedColor(args[0]);
      this->setPaletteBackgroundColor(color);
      break;
    }
    case addColumnTree:
      return TQString::number(TDEListView::addColumn(args[0], args[1].toInt()));
      break;
    case setSortCol:
      TDEListView::setSorting(args[0].toInt(), args[1].toInt());
      break;
    case DCOP::columnCount:
      return TQString::number(TQListView::columns() );
      break;
    case colCaption:
       return TQListView::columnText(args[0].toInt()) ;
      break;
    case setColWidth:
      TQListView::setColumnWidth(args[0].toInt(), args[1].toInt());
      break;
    case setColAlignment:
      setColAlign(args[0].toInt(), args[1]);
      break;
    case setAltBackground:
      TDEListView::setAlternateBackground(TQColor(args[0]));
      break;
    case DCOP::removeColumn:
    {
      if (!args[1].toInt())
        removeColumn(args[0].toInt());
      else
      {
        int column = args[0].toInt();
        int lines = args[1].toInt();
        for (int i = 0; i < lines; i++)
          removeColumn(column);
      }
      break;
    }  
    case TW_childCount:
      return TQString::number(childCount());
      break;
    case DCOP::geometry:
    {
      TQString geo = TQString::number(this->x())+" "+TQString::number(this->y())+" "+TQString::number(this->width())+" "+TQString::number(this->height());
      return geo;
      break;
    }
    case DCOP::hasFocus:
      return TQString::number(this->hasFocus());
      break;
    default:
      return KommanderWidget::handleDCOP(function, args);
  }
  return TQString();
}

#include "treewidget.moc"
