/***************************************************************************
    copyright            : (C) 2001-2006 by Robby Stephenson
    email                : robby@periapsis.org
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of version 2 of the GNU General Public License as  *
 *   published by the Free Software Foundation;                            *
 *                                                                         *
 ***************************************************************************/

#include "listview.h"
#include "../controller.h"
#include "../tellico_utils.h"
#include "../tellico_debug.h"

#include <kapplication.h>

#include <tqpainter.h>
#include <tqpixmap.h>
#include <tqheader.h>

using Tellico::GUI::ListView;
using Tellico::GUI::ListViewItem;

ListView::ListView(TQWidget* parent_, const char* name_) : KListView(parent_, name_/*=0*/),
    m_sortStyle(SortByText), m_isClear(true) {
  setSelectionMode(TQListView::Extended);
  connect(this, TQT_SIGNAL(selectionChanged()),
          TQT_SLOT(slotSelectionChanged()));
  connect(this, TQT_SIGNAL(doubleClicked(TQListViewItem*)),
          TQT_SLOT(slotDoubleClicked(TQListViewItem*)));
#if !KDE_IS_VERSION(3,3,90)
  m_shadeSortColumn = false;
  // call it once to initialize
  slotUpdateColors();
#endif
  connect(kapp, TQT_SIGNAL(kdisplayPaletteChanged()), TQT_SLOT(slotUpdateColors()));
  m_comparisons.setAutoDelete(true);
}

ListView::~ListView() {
}

void ListView::clearSelection() {
  if(m_selectedItems.isEmpty()) {
    // nothing to do;
    return;
  }
  bool b = signalsBlocked();
  blockSignals(true);
  selectAll(false);
  blockSignals(b);
  emit selectionChanged();
}

void ListView::updateSelected(ListViewItem* item_, bool selected_) {
  if(selected_) {
    m_selectedItems.append(item_);
  } else {
    m_selectedItems.removeRef(item_);
  }
}

bool ListView::isSelectable(ListViewItem* item_) const {
  // don't allow hidden items to be selected
  if(!item_->isVisible()) {
    return false;
  }

  // selecting multiple items is ok
  // only when parent is open. Be careful to check for existence of parent
  if(item_->parent() && !item_->parent()->isOpen()) {
    return false;
  }

  // just selecting a single item is always ok
  if(m_selectedItems.isEmpty()) {
    return true;
  }

  // not allowed is something other than an entry is selected and current is entry
  if(m_selectedItems.getFirst()->isEntryItem() != item_->isEntryItem()) {
    return false;
  }

  return true;
}

int ListView::firstVisibleColumn() const {
  int col = 0;
  while(col < columns() && columnWidth(header()->mapToSection(col)) == 0) {
    ++col;
  }
  if(col == columns()) {
    return -1;
  }
  return header()->mapToSection(col);
}

int ListView::lastVisibleColumn() const {
  int col = columns()-1;
  while(col < columns() && columnWidth(header()->mapToSection(col)) == 0) {
    --col;
  }
  if(col == columns()) {
    return -1;
  }
  return header()->mapToSection(col);
}

void ListView::setColumnText(int column, const TQString& label) {
  ListViewComparison* comp = m_comparisons.take(columnText(column));
  KListView::setColumnText(column, label);
  if(comp) {
    m_comparisons.insert(columnText(column), comp);
  }
}

void ListView::setComparison(int column, ListViewComparison* comp) {
  if(comp) {
    m_comparisons.replace(columnText(column), comp);
  }
}

void ListView::removeComparison(int column) {
  m_comparisons.remove(columnText(column));
}

void ListView::clearComparisons() {
  m_comparisons.clear();
}

int ListView::compare(int col, const GUI::ListViewItem* item1, GUI::ListViewItem* item2, bool asc) {
  if(col >= 0 && col < static_cast<int>(m_comparisons.count())) {
    ListViewComparison* com = m_comparisons.find(columnText(col));
    if(com) {
      return com->compare(col, item1, item2, asc);
    }
  }
  return 0;
}

#if !KDE_IS_VERSION(3,3,90)
void ListView::setShadeSortColumn(bool shade_) {
  if(m_shadeSortColumn != shade_) {
    m_shadeSortColumn = shade_;
    repaint();
  }
}
#endif

void ListView::slotUpdateColors() {
#if !KDE_IS_VERSION(3,3,90)
  m_backColor2 = viewport()->colorGroup().base();
  if(m_backColor2 == TQt::black) {
    m_backColor2 = TQColor(50, 50, 50);  // dark gray
  } else {
    int h,s,v;
    m_backColor2.hsv(&h, &s, &v);
    if(v > 175) {
      m_backColor2 = m_backColor2.dark(105);
    } else {
      m_backColor2 = m_backColor2.light(120);
    }
  }

  m_altColor2 = alternateBackground();
  if(m_altColor2 == TQt::black) {
    m_altColor2 = TQColor(50, 50, 50);  // dark gray
  } else {
    int h,s,v;
    m_altColor2.hsv(&h, &s, &v);
    if(v > 175) {
      m_altColor2 = m_altColor2.dark(105);
    } else {
      m_altColor2 = m_altColor2.light(120);
    }
  }
#endif
  Tellico::updateContrastColor(viewport()->colorGroup());
  repaint();
}

void ListView::slotSelectionChanged() {
  if(m_selectedItems.isEmpty()) {
    if(m_isClear) {
      return; // nothing to do
    }
    m_isClear = true;
    Controller::self()->slotClearSelection();
    return;
  }
  m_isClear = false;

  Data::EntryVec entries;
  // now just find all the children or grandchildren that are entry items
  for(GUI::ListViewItemListIt it(m_selectedItems); it.current(); ++it) {
    Data::EntryVec more = it.current()->entries();
    for(Data::EntryVecIt entry = more.begin(); entry != more.end(); ++entry) {
      if(!entries.contains(entry)) {
        entries.append(entry);
      }
    }
  }
//  Controller::self()->slotUpdateCurrent(entries); // just update current, don't change selection
  Controller::self()->slotUpdateSelection(this, entries);
}

void ListView::slotDoubleClicked(TQListViewItem* item_) {
  if(!item_) {
    return;
  }

  // if it has children, just open it
  // but some items delay children creation
  if(static_cast<ListViewItem*>(item_)->realChildCount() > 0) {
    item_->setOpen(!item_->isOpen());
  }

  GUI::ListViewItem* item = static_cast<GUI::ListViewItem*>(item_);
  item->doubleClicked();
}

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

/* ****************** ListViewItem ********************* */

ListViewItem::~ListViewItem() {
  // I think there's a bug in qt where the children of this item are deleted after the item itself
  // as a result, there is no listView() pointer for the children, that obvious causes
  // a problem with updating the selection. So we MUST call clear() here ourselves!
  clear();
  // be sure to remove from selected list when it's deleted
  ListView* lv = listView();
  if(lv) {
    lv->updateSelected(this, false);
  }
}

void ListViewItem::clear() {
  TQListViewItem* item = firstChild();
  while(item) {
    delete item;
    item = firstChild();
  }
}

int ListViewItem::compare(TQListViewItem* item_, int col_, bool asc_) const {
  int res = compareWeight(item_, col_, asc_);
  if(res != 0) {
    return res;
  }
  res = listView()->compare(col_, this, static_cast<GUI::ListViewItem*>(item_), asc_);
  return res == 0 ? KListViewItem::compare(item_, col_, asc_) : res;
}

int ListViewItem::compareWeight(TQListViewItem* item_, int col_, bool asc_) const {
  Q_UNUSED(col_);
  // I want the sorting to be independent of sort order
  GUI::ListViewItem* i = static_cast<GUI::ListViewItem*>(item_);
  int res = 0;
  if(m_sortWeight < i->sortWeight()) {
    res = -1;
  } else if(m_sortWeight > i->sortWeight()) {
    res = 1;
  }
  if(asc_) {
    res *= -1; // reverse, heavier weights will come first always
  }
  return res;
}

void ListViewItem::setSelected(bool s_) {
  ListView* lv = listView();
  if(!lv) {
    return;
  }
  if(s_ && !lv->isSelectable(this)) {
    return;
  }
  if(s_ != isSelected()) {
    lv->updateSelected(this, s_);
    KListViewItem::setSelected(s_);
  }
}

TQColor ListViewItem::backgroundColor(int column_) {
#if KDE_IS_VERSION(3,3,90)
  return KListViewItem::backgroundColor(column_);
#else
  ListView* view = listView();
  if(view->columns() > 1 && view->shadeSortColumn() && column_ == view->sortColumn()) {
    return isAlternate() ? view->alternateBackground2() : view->background2();
  }
  return isAlternate() ? view->alternateBackground() : view->viewport()->colorGroup().base();
#endif
}

void ListViewItem::paintCell(TQPainter* p_, const TQColorGroup& cg_,
                             int column_, int width_, int align_) {
  // taken from klistview.cpp
  // I can't call KListViewItem::paintCell since KListViewItem::backgroundCOlor(int) is
  // not virtual. I need to be sure to call ListViewItem::backgroundColor(int);
  TQColorGroup cg = cg_;
  const TQPixmap* pm = listView()->viewport()->backgroundPixmap();
  if(pm && !pm->isNull()) {
    cg.setBrush(TQColorGroup::Base, TQBrush(backgroundColor(column_), *pm));
    TQPoint o = p_->brushOrigin();
    p_->setBrushOrigin(o.x()-listView()->contentsX(), o.y()-listView()->contentsY());
  } else {
    cg.setColor(listView()->viewport()->backgroundMode() == TQt::FixedColor ?
                TQColorGroup::Background : TQColorGroup::Base,
                backgroundColor(column_));
  }

  // don't call KListViewItem::paintCell() since that also does alternate painting, etc...
  TQListViewItem::paintCell(p_, cg, column_, width_, align_);

  // borrowed from amarok, draw line to left of cell
  if(!isSelected()) {
    p_->setPen(TQPen(listView()->alternateBackground(), 0, TQt::SolidLine));
    p_->drawLine(width_-1, 0, width_-1, height()-1);
  }
}

Tellico::Data::EntryVec ListViewItem::entries() const {
  Data::EntryVec entries;
  for(TQListViewItem* child = firstChild(); child; child = child->nextSibling()) {
    Data::EntryVec more = static_cast<GUI::ListViewItem*>(child)->entries();
    for(Data::EntryVecIt entry = more.begin(); entry != more.end(); ++entry) {
      if(!entries.contains(entry)) {
        entries.append(entry);
      }
    }
  }
  return entries;
}

#include "listview.moc"
