/***************************************************************************
                             kmymoneyselector.cpp
                             -------------------
    begin                : Thu Jun 29 2006
    copyright            : (C) 2006 by Thomas Baumgart
    email                : Thomas Baumgart <ipwizard@users.sourceforge.net>
 ***************************************************************************/

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

// ----------------------------------------------------------------------------
// QT Includes

#include <tqlayout.h>
#include <tqheader.h>
#include <tqtimer.h>
#include <tqstyle.h>
#include <tqregexp.h>

// ----------------------------------------------------------------------------
// KDE Includes

// ----------------------------------------------------------------------------
// Project Includes

#include <kmymoney/kmymoneyselector.h>
#include <kmymoney/kmymoneylistviewitem.h>
#include <kmymoney/kmymoneychecklistitem.h>

#include "../kmymoneyglobalsettings.h"

KMyMoneySelector::KMyMoneySelector(TQWidget *parent, const char *name, TQWidget::WFlags flags) :
  TQWidget(parent, name, flags)
{
  m_selMode = TQListView::Single;

  m_listView = new TDEListView(this);
  // don't show horizontal scroll bar
  m_listView->setHScrollBarMode(TQScrollView::AlwaysOff);

  m_listView->setSorting(-1);

  if(parent) {
    setFocusProxy(parent->focusProxy());
    m_listView->setFocusProxy(parent->focusProxy());
  }

  m_listView->setAllColumnsShowFocus(true);

  m_layout = new TQHBoxLayout( this, 0, 6);

  m_listView->addColumn( "Hidden" );
  m_listView->header()->hide();
  m_listView->header()->setStretchEnabled(true, -1);
  m_listView->header()->adjustHeaderSize();

  m_layout->addWidget( m_listView );

  // force init
  m_selMode = TQListView::Multi;
  setSelectionMode(TQListView::Single);

  connect(m_listView, TQT_SIGNAL(rightButtonPressed(TQListViewItem* , const TQPoint&, int)), this, TQT_SLOT(slotListRightMouse(TQListViewItem*, const TQPoint&, int)));
}

KMyMoneySelector::~KMyMoneySelector()
{
}

void KMyMoneySelector::clear(void)
{
  m_listView->clear();
  m_visibleItem = 0;
}

void KMyMoneySelector::setSelectionMode(const TQListView::SelectionMode mode)
{
  if(m_selMode != mode) {
    m_selMode = mode;
    clear();

    // make sure, it's either Multi or Single
    if(mode != TQListView::Multi) {
      m_selMode = TQListView::Single;
      connect(m_listView, TQT_SIGNAL(selectionChanged(void)), this, TQT_SIGNAL(stateChanged(void)));
      connect(m_listView, TQT_SIGNAL(executed(TQListViewItem*)), this, TQT_SLOT(slotItemSelected(TQListViewItem*)));
    } else {
      disconnect(m_listView, TQT_SIGNAL(selectionChanged(void)), this, TQT_SIGNAL(stateChanged(void)));
      disconnect(m_listView, TQT_SIGNAL(executed(TQListViewItem*)), this, TQT_SLOT(slotItemSelected(TQListViewItem*)));
    }
  }
  TQWidget::update();
}

void KMyMoneySelector::slotItemSelected(TQListViewItem *item)
{
  if(m_selMode == TQListView::Single) {
    KMyMoneyListViewItem* l_item = dynamic_cast<KMyMoneyListViewItem*>(item);
    if(l_item && l_item->isSelectable()) {
      emit itemSelected(l_item->id());
    }
  }
}

TQListViewItem* KMyMoneySelector::newItem(const TQString& name, TQListViewItem* after, const TQString& key, const TQString& id, TQCheckListItem::Type type)
{
  TQListViewItem* item;
  if(after)
    item = new KMyMoneyCheckListItem(m_listView, after, name, key, id, type);
  else
    item = new KMyMoneyCheckListItem(m_listView, name, key, id, type);

  item->setSelectable(!id.isEmpty());
  item->setOpen(true);
  return item;
}

TQListViewItem* KMyMoneySelector::newItem(const TQString& name, const TQString& key, const TQString& id, TQCheckListItem::Type type)
{
  return newItem(name, 0, key, id, type);
}

TQListViewItem* KMyMoneySelector::newTopItem(const TQString& name, const TQString& key, const TQString& id)
{
  TQListViewItem* p;

  if(m_selMode == TQListView::Multi) {
    KMyMoneyCheckListItem* q = new KMyMoneyCheckListItem(m_listView, name, key, id);
    connect(q, TQT_SIGNAL(stateChanged(bool)), this, TQT_SIGNAL(stateChanged(void)));
    p = static_cast<TQListViewItem*> (q);

  } else {
    KMyMoneyListViewItem* q = new KMyMoneyListViewItem(m_listView, name, key, id);
    p = static_cast<TQListViewItem*> (q);
  }

  return p;
}

TQListViewItem* KMyMoneySelector::newItem(TQListViewItem* parent, const TQString& name, const TQString& key, const TQString& id)
{
  TQListViewItem* p;

  if(m_selMode == TQListView::Multi) {
    KMyMoneyCheckListItem* q = new KMyMoneyCheckListItem(parent, name, key, id);
    connect(q, TQT_SIGNAL(stateChanged(bool)), this, TQT_SIGNAL(stateChanged(void)));
    p = static_cast<TQListViewItem*> (q);

  } else {
    KMyMoneyListViewItem* q = new KMyMoneyListViewItem(parent, name, key, id);
    p = static_cast<TQListViewItem*> (q);
  }

  return p;
}

void KMyMoneySelector::protectItem(const TQString& itemId, const bool protect)
{
  TQListViewItemIterator it(m_listView, TQListViewItemIterator::Selectable);
  TQListViewItem* it_v;
  KMyMoneyListViewItem* it_l;
  KMyMoneyCheckListItem* it_c;

  // scan items
  while((it_v = it.current()) != 0) {
    it_l = dynamic_cast<KMyMoneyListViewItem*>(it_v);
    if(it_l) {
      if(it_l->id() == itemId) {
        it_l->setSelectable(!protect);
        break;
      }
    } else {
      it_c = dynamic_cast<KMyMoneyCheckListItem*>(it_v);
      if(it_c) {
        if(it_c->id() == itemId) {
          it_c->setSelectable(!protect);
          break;
        }
      }
    }
    ++it;
  }
}

TQListViewItem* KMyMoneySelector::item(const TQString& id) const
{
  TQListViewItemIterator it(m_listView, TQListViewItemIterator::Selectable);
  TQListViewItem* it_v;
  KMyMoneyListViewItem* it_l;
  KMyMoneyCheckListItem* it_c;

  while((it_v = it.current()) != 0) {
    it_l = dynamic_cast<KMyMoneyListViewItem*>(it_v);
    if(it_l) {
      if(it_l->id() == id)
        break;
    } else {
      it_c = dynamic_cast<KMyMoneyCheckListItem*>(it_v);
      if(it_c->id() == id)
        break;
    }
    ++it;
  }
  return it_v;
}

int KMyMoneySelector::optimizedWidth(void) const
{
  TQListViewItemIterator it(m_listView, TQListViewItemIterator::Selectable);
  TQListViewItem* it_v;
  KMyMoneyListViewItem* it_l;
  KMyMoneyCheckListItem* it_c;

  // scan items
  int w = 0;
#ifndef KMM_DESIGNER
  TQFontMetrics fm( KMyMoneyGlobalSettings::listCellFont());
#else
  TQFontMetrics fm( font() );
#endif
  while((it_v = it.current()) != 0) {
    it_l = dynamic_cast<KMyMoneyListViewItem*>(it_v);
    int nw = 0;
    if(it_l) {
      nw = it_l->width(fm, m_listView, 0);
    } else {
      it_c = dynamic_cast<KMyMoneyCheckListItem*>(it_v);
      if(it_c) {
        nw = it_c->width(fm, m_listView, 0);
      }
    }
    if(nw > w)
      w = nw;
    ++it;
  }
  return w;
}

void KMyMoneySelector::setOptimizedWidth(void)
{
  int w = optimizedWidth();

  m_listView->setMinimumWidth(w+30);
  m_listView->setMaximumWidth(w+30);
  m_listView->setColumnWidth(0, w+28);
}

bool KMyMoneySelector::allItemsSelected(void) const
{
  TQListViewItem* it_v;

  if(m_selMode == TQListView::Single)
    return false;

  for(it_v = m_listView->firstChild(); it_v != 0; it_v = it_v->nextSibling()) {
    if(it_v->rtti() == 1) {
      TQCheckListItem* it_c = dynamic_cast<TQCheckListItem*>(it_v);
      if(it_c->type() == TQCheckListItem::CheckBox) {
        if(!(it_c->isOn() && allItemsSelected(it_v)))
          return false;
      } else {
        if(!allItemsSelected(it_v))
          return false;
      }
    }
  }
  return true;
}

bool KMyMoneySelector::allItemsSelected(const TQListViewItem *item) const
{
  TQListViewItem* it_v;

  for(it_v = item->firstChild(); it_v != 0; it_v = it_v->nextSibling()) {
    if(it_v->rtti() == 1) {
      TQCheckListItem* it_c = static_cast<TQCheckListItem*>(it_v);
      if(!(it_c->isOn() && allItemsSelected(it_v)))
        return false;
    }
  }
  return true;
}

void KMyMoneySelector::removeItem(const TQString& id)
{
  TQListViewItem* it_v;
  TQListViewItemIterator it;

  it = TQListViewItemIterator(m_listView);
  while((it_v = it.current()) != 0) {
    if(it_v->rtti() == 1) {
      KMyMoneyCheckListItem* it_c = dynamic_cast<KMyMoneyCheckListItem*>(it_v);
      if(it_c->type() == TQCheckListItem::CheckBox) {
        if(id == it_c->id()) {
          if(it_c->firstChild()) {
            it_c->setSelectable(false);
          } else {
            delete it_c;
          }
        }
      }
    } else if(it_v->rtti() == 0) {
      KMyMoneyListViewItem* it_c = dynamic_cast<KMyMoneyListViewItem*>(it_v);
      if(id == it_c->id()) {
        if(it_c->firstChild()) {
          it_c->setSelectable(false);
        } else {
          delete it_c;
        }
      }
    }
    it++;
  }

  // get rid of top items that just lost the last children (e.g. Favorites)
  it = TQListViewItemIterator(m_listView, TQListViewItemIterator::NotSelectable);
  while((it_v = it.current()) != 0) {
    if(it_v->rtti() == 1) {
      KMyMoneyCheckListItem* it_c = dynamic_cast<KMyMoneyCheckListItem*>(it_v);
      if(it_c->childCount() == 0)
        delete it_c;
    }
    it++;
  }

  return;
}


void KMyMoneySelector::selectAllItems(const bool state)
{
  TQListViewItem* it_v;

  for(it_v = m_listView->firstChild(); it_v != 0; it_v = it_v->nextSibling()) {
    if(it_v->rtti() == 1) {
      TQCheckListItem* it_c = dynamic_cast<TQCheckListItem*>(it_v);
      if(it_c->type() == TQCheckListItem::CheckBox) {
        it_c->setOn(state);
      }
      selectAllSubItems(it_v, state);
    }
  }
  emit stateChanged();
}

void KMyMoneySelector::selectItems(const TQStringList& itemList, const bool state)
{
  TQListViewItem* it_v;

  for(it_v = m_listView->firstChild(); it_v != 0; it_v = it_v->nextSibling()) {
    if(it_v->rtti() == 1) {
      KMyMoneyCheckListItem* it_c = dynamic_cast<KMyMoneyCheckListItem*>(it_v);
      if(it_c->type() == TQCheckListItem::CheckBox && itemList.contains(it_c->id())) {
        it_c->setOn(state);
      }
      selectSubItems(it_v, itemList, state);
    }
  }
  emit stateChanged();
}

void KMyMoneySelector::selectSubItems(TQListViewItem* item, const TQStringList& itemList, const bool state)
{
  TQListViewItem* it_v;

  for(it_v = item->firstChild(); it_v != 0; it_v = it_v->nextSibling()) {
    if(it_v->rtti() == 1) {
      KMyMoneyCheckListItem* it_c = dynamic_cast<KMyMoneyCheckListItem*>(it_v);
      if(it_c->type() == TQCheckListItem::CheckBox && itemList.contains(it_c->id())) {
        it_c->setOn(state);
      }
      selectSubItems(it_v, itemList, state);
    }
  }
}

void KMyMoneySelector::selectAllSubItems(TQListViewItem* item, const bool state)
{
  TQListViewItem* it_v;

  for(it_v = item->firstChild(); it_v != 0; it_v = it_v->nextSibling()) {
    if(it_v->rtti() == 1) {
      TQCheckListItem* it_c = dynamic_cast<TQCheckListItem*>(it_v);
      if(it_c->type() == TQCheckListItem::CheckBox) {
        it_c->setOn(state);
      }
      selectAllSubItems(it_v, state);
    }
  }
}

void KMyMoneySelector::selectedItems(TQStringList& list) const
{
  TQListViewItem*  it_v;

  list.clear();
  if(m_selMode == TQListView::Single) {
    KMyMoneyListViewItem* it_c = dynamic_cast<KMyMoneyListViewItem*>(m_listView->selectedItem());
    if(it_c != 0)
      list << it_c->id();

  } else {
    for(it_v = m_listView->firstChild(); it_v != 0; it_v = it_v->nextSibling()) {
      if(it_v->rtti() == 1) {
        KMyMoneyCheckListItem* it_c = dynamic_cast<KMyMoneyCheckListItem*>(it_v);
        if(it_c->type() == TQCheckListItem::CheckBox) {
          if(it_c->isOn())
            list << (*it_c).id();
        }
        selectedItems(list, it_v);
      }
    }
  }
}

void KMyMoneySelector::selectedItems(TQStringList& list, TQListViewItem* item) const
{
  TQListViewItem* it_v;

  for(it_v = item->firstChild(); it_v != 0; it_v = it_v->nextSibling()) {
    if(it_v->rtti() == 1) {
      KMyMoneyCheckListItem* it_c = dynamic_cast<KMyMoneyCheckListItem*>(it_v);
      if(it_c->type() == TQCheckListItem::CheckBox) {
        if(it_c->isOn())
          list << (*it_c).id();
        selectedItems(list, it_v);
      }
    }
  }
}

void KMyMoneySelector::itemList(TQStringList& list) const
{
  TQListViewItemIterator it;
  TQListViewItem* it_v;

  it = TQListViewItemIterator(m_listView, TQListViewItemIterator::Selectable);
  while((it_v = it.current()) != 0) {
    {
      if(it_v->rtti() == 1) {
        KMyMoneyCheckListItem* it_c = dynamic_cast<KMyMoneyCheckListItem*>(it_v);
        if(it_c->type() == TQCheckListItem::CheckBox) {
          list << it_c->id();
        }
      } else if(it_v->rtti() == 0) {
        KMyMoneyListViewItem* it_c = dynamic_cast<KMyMoneyListViewItem*>(it_v);
        list << it_c->id();
      }
    }
    it++;
  }
}

void KMyMoneySelector::setSelected(const TQString& id, const bool state)
{
  TQListViewItemIterator it;
  TQListViewItem* it_v;
  TQListViewItem* it_visible = 0;

  it = TQListViewItemIterator(m_listView, TQListViewItemIterator::Selectable);
  while((it_v = it.current()) != 0) {
    if(it_v->rtti() == 1) {
      KMyMoneyCheckListItem* it_c = dynamic_cast<KMyMoneyCheckListItem*>(it_v);
      TQ_CHECK_PTR(it_c);
      if(it_c->type() == TQCheckListItem::CheckBox) {
        if(it_c->id() == id) {
          it_c->setOn(state);
          m_listView->setSelected(it_v, true);
          if(!it_visible)
            it_visible = it_v;
        }
      }
    } else if(it_v->rtti() == 0) {
      KMyMoneyListViewItem* it_c = dynamic_cast<KMyMoneyListViewItem*>(it_v);
      TQ_CHECK_PTR(it_c);
      if(it_c->id() == id) {
        m_listView->setSelected(it_v, true);
        if(!it_visible)
          it_visible = it_v;
        ensureItemVisible(it_v);
        return;
      }
    }
    it++;
  }

  // make sure the first one found is visible
  if(it_visible)
    ensureItemVisible(it_visible);
}

void KMyMoneySelector::ensureItemVisible(const TQListViewItem *it_v)
{
  // for some reason, I could only use the ensureItemVisible() method
  // of TQListView successfully, after the widget was drawn on the screen.
  // If called before it had no effect (if the item was not visible).
  //
  // The solution was to store the item we wanted to see in a local var
  // and call TQListView::ensureItemVisible() about 10ms later in
  // the slot slotShowSelected.  (ipwizard, 12/29/2003)
  m_visibleItem = it_v;

  TQTimer::singleShot(100, this, TQT_SLOT(slotShowSelected()));
}

void KMyMoneySelector::slotShowSelected(void)
{
  if(m_listView && m_visibleItem)
    m_listView->ensureItemVisible(m_visibleItem);
}

int KMyMoneySelector::slotMakeCompletion(const TQString& _txt)
{
  TQString txt(TQRegExp::escape(_txt));
  if(KMyMoneyGlobalSettings::stringMatchFromStart() && this->isA("KMyMoneySelector") )
    txt.prepend('^');
  return slotMakeCompletion(TQRegExp(txt, false));
}

bool KMyMoneySelector::match(const TQRegExp& exp, TQListViewItem* item) const
{
  return exp.search(item->text(0)) != -1;
}

int KMyMoneySelector::slotMakeCompletion(const TQRegExp& exp)
{
  TQListViewItemIterator it(m_listView, TQListViewItemIterator::Selectable);

  TQListViewItem* it_v;

  // The logic used here seems to be awkward. The problem is, that
  // TQListViewItem::setVisible works recursively on all it's children
  // and grand-children.
  //
  // The way out of this is as follows: Make all items visible.
  // Then go through the list again and perform the checks.
  // If an item does not have any children (last leaf in the tree view)
  // perform the check. Then check recursively on the parent of this
  // leaf that it has no visible children. If that is the case, make the
  // parent invisible and continue this check with it's parent.
  while((it_v = it.current()) != 0) {
    it_v->setVisible(true);
    ++it;
  }

  TQListViewItem* firstMatch = 0;

  if(!exp.pattern().isEmpty()) {
    it = TQListViewItemIterator(m_listView, TQListViewItemIterator::Selectable);
    while((it_v = it.current()) != 0) {
      if(it_v->firstChild() == 0) {
        if(!match(exp, it_v)) {
          // this is a node which does not contain the
          // text and does not have children. So we can
          // safely hide it. Then we check, if the parent
          // has more children which are still visible. If
          // none are found, the parent node is hidden also. We
          // continue until the top of the tree or until we
          // find a node that still has visible children.
          bool hide = true;
          while(hide) {
            it_v->setVisible(false);
            it_v = it_v->parent();
            if(it_v && it_v->isSelectable()) {
              hide = !match(exp, it_v);
              TQListViewItem* child = it_v->firstChild();
              for(; child && hide; child = child->nextSibling()) {
                if(child->isVisible())
                  hide = false;
              }
            } else
              hide = false;
          }
        } else if(!firstMatch) {
          firstMatch = it_v;
        }
        ++it;

      } else if(match(exp, it_v)) {
        if(!firstMatch) {
          firstMatch = it_v;
        }
        // a node with children contains the text. We want
        // to display all child nodes in this case, so we need
        // to advance the iterator to the next sibling of the
        // current node. This could well be the sibling of a
        // parent or grandparent node.
        TQListViewItem* curr = it_v;
        TQListViewItem* item;
        while((item = curr->nextSibling()) == 0) {
          curr = curr->parent();
          if(curr == 0)
            break;
          if(match(exp, curr))
            firstMatch = curr;
        }
        do {
          ++it;
        } while(it.current() && it.current() != item);

      } else {
        // It's a node with children that does not match. We don't
        // change it's status here.
        ++it;
      }
    }
  }

  // make the first match the one that is selected
  // if we have no match, make sure none is selected
  if(m_selMode == TQListView::Single) {
    if(firstMatch) {
      m_listView->setSelected(firstMatch, true);
      ensureItemVisible(firstMatch);
    } else
      m_listView->selectAll(false);
  }

  // Get the number of visible nodes for the return code
  int cnt = 0;

  it = TQListViewItemIterator(m_listView, TQListViewItemIterator::Selectable | TQListViewItemIterator::Visible);
  while((it_v = it.current()) != 0) {
    cnt++;
    it++;
  }
  return cnt;
}

bool KMyMoneySelector::contains(const TQString& txt) const
{
  TQListViewItemIterator it(m_listView, TQListViewItemIterator::Selectable);
  TQListViewItem* it_v;
  while((it_v = it.current()) != 0) {
    if(it_v->rtti() == 1) {
      KMyMoneyCheckListItem* it_c = dynamic_cast<KMyMoneyCheckListItem*>(it_v);
      if(it_c->text() == txt) {
        return true;
      }
    } else if(it_v->rtti() == 0) {
      KMyMoneyListViewItem* it_c = dynamic_cast<KMyMoneyListViewItem*>(it_v);
      if(it_c->text(0) == txt) {
        return true;
      }
    }
    it++;
  }
  return false;
}

void KMyMoneySelector::slotListRightMouse(TQListViewItem* it_v, const TQPoint& pos, int /* col */)
{
  if(it_v && (it_v->rtti() == 1)) {
    KMyMoneyCheckListItem* it_c = static_cast<KMyMoneyCheckListItem*>(it_v);
    if(it_c->type() == TQCheckListItem::CheckBox) {
      // the following is copied from TQCheckListItem::activate() et al
      int boxsize = m_listView->style().pixelMetric(TQStyle::PM_CheckListButtonSize, m_listView);
      int align = m_listView->columnAlignment( 0 );
      int marg = m_listView->itemMargin();
      int y = 0;

      if ( align & AlignVCenter )
        y = ( ( height() - boxsize ) / 2 ) + marg;
      else
        y = (m_listView->fontMetrics().height() + 2 + marg - boxsize) / 2;

      TQRect r( 0, y, boxsize-3, boxsize-3 );
      // columns might have been swapped
      r.moveBy( m_listView->header()->sectionPos( 0 ), 0 );

      TQPoint topLeft = m_listView->itemRect(it_v).topLeft(); //### inefficient?
      TQPoint p = m_listView->mapFromGlobal( pos ) - topLeft;

      int xdepth = m_listView->treeStepSize() * (it_v->depth() + (m_listView->rootIsDecorated() ? 1 : 0))
                   + m_listView->itemMargin();
      xdepth += m_listView->header()->sectionPos( m_listView->header()->mapToSection( 0 ) );
      p.rx() -= xdepth;
      // copy ends around here

      if ( r.contains( p ) ) {
        // we get down here, if we have a right click onto the checkbox
        selectAllSubItems(it_c, it_c->isOn());
      }
    }
  }
}

TQStringList KMyMoneySelector::selectedItems(void) const
{
  TQStringList list;
  selectedItems(list);
  return list;
}

TQStringList KMyMoneySelector::itemList(void) const
{
  TQStringList list;
  itemList(list);
  return list;
}

#include "kmymoneyselector.moc"
