/***************************************************************************
    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 "detailedlistview.h"
#include "detailedentryitem.h"
#include "collection.h"
#include "imagefactory.h"
#include "controller.h"
#include "field.h"
#include "entry.h"
#include "gui/ratingwidget.h"
#include "tellico_debug.h"
#include "tellico_kernel.h"
#include "core/tellico_config.h"

#include <tdelocale.h>
#include <tdeconfig.h>
#include <tdeapplication.h>
#include <tdeaction.h>
#include <kiconloader.h>
#include <tdepopupmenu.h>

#include <tqptrlist.h>
#include <tqstringlist.h>
#include <tqvaluelist.h>
#include <tqheader.h>

namespace {
  static const int MIN_COL_WIDTH = 50;
}

using Tellico::DetailedListView;

DetailedListView::DetailedListView(TQWidget* parent_, const char* name_/*=0*/)
    : GUI::ListView(parent_, name_), m_filter(0),
    m_prevSortColumn(-1), m_prev2SortColumn(-1), m_firstSection(-1),
    m_pixWidth(50), m_pixHeight(50) {
//  myDebug() << "DetailedListView()" << endl;
  setAllColumnsShowFocus(true);
  setShowSortIndicator(true);
  setShadeSortColumn(true);

//  connect(this, TQT_SIGNAL(selectionChanged()), TQT_SLOT(slotSelectionChanged()));
  connect(this, TQT_SIGNAL(contextMenuRequested(TQListViewItem*, const TQPoint&, int)),
          TQT_SLOT(contextMenuRequested(TQListViewItem*, const TQPoint&, int)));
  connect(header(), TQT_SIGNAL(indexChange(int, int, int)),
          TQT_SLOT(slotUpdatePixmap()));

  // header menu
  header()->setClickEnabled(true);
  header()->installEventFilter(this);
  connect(header(), TQT_SIGNAL(sizeChange(int, int, int)),
          this, TQT_SLOT(slotCacheColumnWidth(int, int, int)));

  m_headerMenu = new TDEPopupMenu(this);
  m_headerMenu->setCheckable(true);
  m_headerMenu->insertTitle(i18n("View Columns"));
  connect(m_headerMenu, TQT_SIGNAL(activated(int)),
          this, TQT_SLOT(slotHeaderMenuActivated(int)));

  m_checkPix = UserIcon(TQString::fromLatin1("checkmark"));
}

DetailedListView::~DetailedListView() {
}

void DetailedListView::addCollection(Data::CollPtr coll_) {
  if(!coll_) {
    return;
  }

  m_imageColumns.clear();
//  myDebug() << "DetailedListView::addCollection()" << endl;

  TDEConfigGroup config(kapp->config(), TQString::fromLatin1("Options - %1").arg(coll_->typeName()));

  TQString configN;
  if(coll_->type() == Data::Collection::Base) {
    KURL url = Kernel::self()->URL();
    for(uint i = 0; i < Config::maxCustomURLSettings(); ++i) {
      KURL u = config.readEntry(TQString::fromLatin1("URL_%1").arg(i));
      if(u == url) {
        configN = TQString::fromLatin1("_%1").arg(i);
        break;
      }
    }
  }

  TQStringList colNames = config.readListEntry("ColumnNames" + configN);
  if(colNames.isEmpty()) {
    colNames = TQString::fromLatin1("title");
  }

  // this block compensates for the chance that the user added a field and it wasn't
  // written to the widths. Also compensates for 0.5.x to 0.6.x column layout changes
  TQValueList<int> colWidths = config.readIntListEntry("ColumnWidths" + configN);
  if(colWidths.empty()) {
    colWidths.insert(colWidths.begin(), colNames.count(), -1); // automatic width
  }

  TQValueList<int> colOrder = config.readIntListEntry("ColumnOrder" + configN);

  // need to remove values for fields which don't exist in the current collection
  TQStringList newCols;
  TQValueList<int> newWidths, removeCols;
  for(uint i = 0; i < colNames.count(); ++i) {
    if(!colNames[i].isEmpty() && coll_->hasField(colNames[i])) {
      newCols += colNames[i];
      newWidths += colWidths[i];
    } else {
      removeCols += i;
    }
  }
  colNames = newCols;
  colWidths = newWidths;

  qHeapSort(removeCols);
  // now need to shift values in the order if any columns were removed
  // only need to shift by number of "holes"
  TQValueList<int> newOrder;
  for(TQValueList<int>::ConstIterator it = colOrder.begin(); it != colOrder.end(); ++it) {
    if(removeCols.findIndex(*it) == -1) {
      int i = *it;
      for(uint j = 0; j < removeCols.count() && removeCols[j] < i; ++j) {
        --i;
      }
      newOrder += i;
    }
  }
  colOrder = newOrder;

  bool none = true;
  Data::FieldVec fields = coll_->fields();
  for(Data::FieldVec::Iterator fIt = fields.begin(); fIt != fields.end(); ++fIt) {
    if(colNames.findIndex(fIt->name()) > -1 && colWidths.count() > 0) {
      addField(fIt, colWidths.front());
      if(none && colWidths.front() != 0) {
        none = false;
      }
      colWidths.pop_front();
    } else {
      addField(fIt, 0);
    }
  }
  if(none && columns() > 0 && !colNames.isEmpty()) {
    showColumn(coll_->fieldNames().findIndex(colNames[0]));
  }

  TQValueList<int>::ConstIterator it = colOrder.begin();
  for(int i = 0; it != colOrder.end(); ++it) {
    header()->moveSection(i++, *it);
  }
  slotUpdatePixmap();

  int sortCol = config.readNumEntry("SortColumn" + configN, 0);
  bool sortAsc = config.readBoolEntry("SortAscending" + configN, true);
  setSorting(sortCol, sortAsc);
  int prevSortCol = config.readNumEntry("PrevSortColumn" + configN, -1);
  int prev2SortCol = config.readNumEntry("Prev2SortColumn" + configN, -1);
  setPrevSortedColumn(prevSortCol, prev2SortCol);

  triggerUpdate();
  kapp->processEvents();
  setUpdatesEnabled(false);

  m_loadingCollection = true;
  Data::EntryVec entries = coll_->entries();
  for(Data::EntryVecIt entry = entries.begin(); entry != entries.end(); ++entry) {
    addEntryInternal(entry);
  }
  m_loadingCollection = false;

  setUpdatesEnabled(true);
  triggerUpdate();
}

void DetailedListView::slotReset() {
//  myDebug() << "DetailedListView::slotReset()" << endl;
  //clear() does not remove columns
  clear();
//  while(columns() > 0) {
//    removeColumn(0);
//  }
  m_filter = 0;
}

void DetailedListView::addEntries(Data::EntryVec entries_) {
  if(entries_.isEmpty()) {
    return;
  }

//  myDebug() << "DetailedListView::addEntry() - " << entry_->title() << endl;

  DetailedEntryItem* item = 0;
  for(Data::EntryVecIt entry = entries_.begin(); entry != entries_.end(); ++entry) {
    item = addEntryInternal(entry);
    item->setState(DetailedEntryItem::New);
    item->setVisible(!m_filter || m_filter->matches(entry.data()));
  }

  if(isUpdatesEnabled() && item && item->isVisible()) {
    sort();
    ensureItemVisible(item);
    setCurrentItem(item);
    if(!selectedItems().isEmpty()) {
      blockSignals(true);
      clearSelection();
      setSelected(item, true);
      blockSignals(false);
    }
  } else {
    triggerUpdate();
  }
}

Tellico::DetailedEntryItem* DetailedListView::addEntryInternal(Data::EntryPtr entry_) {
  if(m_entryPix.isNull()) {
    m_entryPix = UserIcon(entry_->collection()->typeName());
    if(m_entryPix.isNull()) {
      kdWarning() << "DetailedListView::addEntryInternal() - can't find entry pix" << endl;
    }
  }

  DetailedEntryItem* item = new DetailedEntryItem(this, entry_);
  populateItem(item);
  return item;
}

void DetailedListView::modifyEntries(Data::EntryVec entries_) {
  if(entries_.isEmpty()) {
    return;
  }

  DetailedEntryItem* item = 0;
  for(Data::EntryVecIt entry = entries_.begin(); entry != entries_.end(); ++entry) {
    item = locateItem(entry.data());
    if(!item) {
      kdWarning() << "DetailedListView::modifyEntries() - no item found for " << entry->title() << endl;
      continue;
    }

    populateItem(item);
    item->setState(DetailedEntryItem::Modified);
    item->setVisible(!m_filter || m_filter->matches(entry.data()));
  }

  if(isUpdatesEnabled() && item && item->isVisible()) {
    sort();
  }

  if(item && !item->isSelected() && !selectedItems().isEmpty()) {
    blockSignals(true);
    clearSelection();
    setSelected(item, true);
    blockSignals(false);
  }
}

void DetailedListView::removeEntries(Data::EntryVec entries_) {
  if(entries_.isEmpty()) {
    return;
  }

//  myDebug() << "DetailedListView::removeEntries() - " << entry_->title() << endl;

  for(Data::EntryVecIt entry = entries_.begin(); entry != entries_.end(); ++entry) {
    delete locateItem(entry);
  }
  // update is required
  triggerUpdate();
}

void DetailedListView::removeCollection(Data::CollPtr coll_) {
  if(!coll_) {
    kdWarning() << "DetailedListView::removeCollection() - null coll pointer!" << endl;
    return;
  }

//  myDebug() << "DetailedListView::removeCollection() - " << coll_->title() << endl;

  clear();
  while(columns() > 0) {
    removeColumn(0);
  }

  m_headerMenu->clear();
  m_headerMenu->insertTitle(i18n("View Columns"));

  m_columnWidths.clear();
  clearComparisons();
  m_entryPix = TQPixmap();

  // clear the filter, too
  m_filter = 0;
}

void DetailedListView::populateColumn(int col_) {
  if(childCount() == 0) {
    return;
  }
//  myDebug() << "DetailedListView::populateColumn() - " << columnText(col_) << endl;
  DetailedEntryItem* item = static_cast<DetailedEntryItem*>(firstChild());
  Data::FieldPtr field = item->entry()->collection()->fieldByTitle(columnText(col_));
  for( ; item; item = static_cast<DetailedEntryItem*>(item->nextSibling())) {
    setPixmapAndText(item, col_, field);
  }
  m_isDirty[col_] = false;
}

void DetailedListView::populateItem(DetailedEntryItem* item_) {
  Data::EntryPtr entry = item_->entry();
  if(!entry) {
    return;
  }

  for(int colNum = 0; colNum < columns(); ++colNum) {
    if(columnWidth(colNum) > 0) {
      Data::FieldPtr field = entry->collection()->fieldByTitle(columnText(colNum));
      if(!field) {
        kdWarning() << "DetailedListView::populateItem() - no field found for " << columnText(colNum) << endl;
        continue;
      }
      setPixmapAndText(item_, colNum, field);
    } else {
      m_isDirty[colNum] = true;
    }
  }
}

void DetailedListView::contextMenuRequested(TQListViewItem* item_, const TQPoint& point_, int) {
  if(!item_) {
    return;
  }
  TDEPopupMenu menu(this);
  Controller::self()->plugEntryActions(&menu);
  menu.exec(point_);
}

// don't shadow TQListView::setSelected
void DetailedListView::setEntrySelected(Data::EntryPtr entry_) {
//  myDebug() << "DetailedListView::setEntrySelected()" << endl;
  // if entry_ is null pointer, just return
  if(!entry_) {
    return;
  }

  DetailedEntryItem* item = locateItem(entry_);

  blockSignals(true);
  clearSelection();
  setSelected(item, true);
  setCurrentItem(item);
  blockSignals(false);
  ensureItemVisible(item);
}

Tellico::DetailedEntryItem* DetailedListView::locateItem(Data::EntryPtr entry_) {
  for(TQListViewItemIterator it(this); it.current(); ++it) {
    DetailedEntryItem* item = static_cast<DetailedEntryItem*>(it.current());
    if(item->entry() == entry_) {
      return item;
    }
  }

  return 0;
}

bool DetailedListView::eventFilter(TQObject* obj_, TQEvent* ev_) {
  if(ev_->type() == TQEvent::MouseButtonPress
      && TQT_TQMOUSEEVENT(ev_)->button() == Qt::RightButton
      && TQT_BASE_OBJECT(obj_) == TQT_BASE_OBJECT(header())) {
    m_headerMenu->popup(TQT_TQMOUSEEVENT(ev_)->globalPos());
    return true;
  }
  return GUI::ListView::eventFilter(obj_, ev_);
}

void DetailedListView::slotHeaderMenuActivated(int id_) {
//  myDebug() << "DetailedListView::slotHeaderMenuActivated() - " << m_headerMenu->text(id_) << endl;
  bool checked = m_headerMenu->isItemChecked(id_);
  checked = !checked; // toggle
  m_headerMenu->setItemChecked(id_, checked);

  int col = m_headerMenu->indexOf(id_) - 1;  // subtract 1 because there's a title item

  if(checked) { // add a column
    showColumn(col);
  } else {
    hideColumn(col);
  }
  slotUpdatePixmap();
}

void DetailedListView::slotRefresh() {
  if(childCount() == 0) {
    return;
  }

  // the algorithm here is to iterate over each column, then over every list item
  Data::CollPtr coll = static_cast<DetailedEntryItem*>(firstChild())->entry()->collection();
  Data::FieldPtr field;
  DetailedEntryItem* item;

  for(int colNum = 0; colNum < columns(); ++colNum) {
    field = coll->fieldByTitle(columnText(colNum));

    // iterate over all items

    for(TQListViewItemIterator it(this); it.current(); ++it) {
      item = static_cast<DetailedEntryItem*>(it.current());

      setPixmapAndText(item, colNum, field);

      // if we're doing this for the first time, go ahead and pass through filter
      if(colNum == 0) {
        if(m_filter && !m_filter->matches(item->entry())) {
          item->setVisible(false);
        } else {
          item->setVisible(true);
        }
      }
    }
  }
}

void DetailedListView::slotRefreshImages() {
  for(TQValueVector<int>::const_iterator it = m_imageColumns.begin(); it != m_imageColumns.end(); ++it) {
    if(columnWidth(*it) > 0) {
      populateColumn(*it);
    }
  }
}

void DetailedListView::setPixmapAndText(DetailedEntryItem* item_, int col_, Data::FieldPtr field_) {
  if(!item_ || !field_) {
    return;
  }

  // if the bool is not empty, show the checkmark pixmap
  if(field_->type() == Data::Field::Bool) {
    const TQString value = item_->entry()->field(field_);
    item_->setPixmap(col_, value.isEmpty() ? TQPixmap() : m_checkPix);
    item_->setText(col_, TQString());
  } else if(field_->type() == Data::Field::Image) {
    // if we're currently loading a collection
    // don't load the image just yet, it'll get refreshed later
    if(m_loadingCollection || columnWidth(col_) == 0) {
      item_->setPixmap(col_, TQPixmap());
      item_->setText(col_, TQString());
    } else {
      item_->setPixmap(col_, ImageFactory::pixmap(item_->entry()->field(field_), m_pixWidth, m_pixHeight));
      item_->setText(col_, TQString());
    }
  } else if(field_->type() == Data::Field::Rating) {
    item_->setPixmap(col_, GUI::RatingWidget::pixmap(item_->entry()->field(field_)));
    item_->setText(col_, TQString());
  } else { // for everything else, there's no pixmap, unless it's the first column
    item_->setPixmap(col_, col_ == m_firstSection ? m_entryPix : TQPixmap());
    item_->setText(col_, item_->entry()->formattedField(field_));
  }
  item_->widthChanged(col_);
}

void DetailedListView::showColumn(int col_) {
//  myDebug() << "DetailedListView::showColumn() - " << columnText(col_) << endl;
  int w = m_columnWidths[col_]; // this should be safe - all were initialized to 0
  if(w == 0) {
    setColumnWidthMode(col_, TQListView::Maximum);
    for(TQListViewItemIterator it(this); it.current(); ++it) {
      w = TQMAX(it.current()->width(fontMetrics(), this, col_), w);
    }
    w = TQMAX(w, MIN_COL_WIDTH);
  } else {
    setColumnWidthMode(col_, TQListView::Manual);
  }

  setColumnWidth(col_, w);
  if(m_isDirty[col_]) {
    populateColumn(col_);
  }
  header()->setResizeEnabled(true, col_);
  triggerUpdate();
}

void DetailedListView::hideColumn(int col_) {
//  myDebug() << "DetailedListView::hideColumn() - " << columnText(col_) << endl;
  setColumnWidthMode(col_, TQListView::Manual);
  setColumnWidth(col_, 0);
  header()->setResizeEnabled(false, col_);

  // special case for images, I don't want all the items to be tall, so remove pixmaps
  if(childCount() > 0) {
    Data::EntryPtr entry = static_cast<DetailedEntryItem*>(firstChild())->entry();
    if(entry) {
      Data::FieldPtr field = entry->collection()->fieldByTitle(columnText(col_));
      if(field && field->type() == Data::Field::Image) {
        m_isDirty[col_] = true;
        for(TQListViewItemIterator it(this); it.current(); ++it) {
          it.current()->setPixmap(col_, TQPixmap());
        }
      }
    }
  }

  triggerUpdate();
}

void DetailedListView::slotCacheColumnWidth(int section_, int oldSize_, int newSize_) {
  // if the old size was 0, update the menu
  if(oldSize_ == 0 && newSize_ > 0) {
    m_headerMenu->setItemChecked(m_headerMenu->idAt(section_+1), true); // add 1 for title item
  }

  if(newSize_ > 0) {
    m_columnWidths[section_] = newSize_;
  }
  setColumnWidthMode(section_, TQListView::Manual);
}

void DetailedListView::setFilter(FilterPtr filter_) {
  if(m_filter.data() != filter_) { // might just be updating
    m_filter = filter_;
  }
//  clearSelection();

  int count = 0;
  // iterate over all items
  DetailedEntryItem* item;
  for(TQListViewItemIterator it(this); it.current(); ++it) {
    item = static_cast<DetailedEntryItem*>(it.current());
    if(m_filter && !m_filter->matches(item->entry())) {
      item->setVisible(false);
      setSelected(item, false);
    } else {
      item->setVisible(true);
      ++count;
    }
  }
  m_visibleItems = count;
}

void DetailedListView::addField(Data::CollPtr, Data::FieldPtr field) {
  addField(field, 0); /* field is hidden by default */
}

void DetailedListView::addField(Data::FieldPtr field_, int width_) {
//  myDebug() << "DetailedListView::addField() - " << field_->title() << endl;
  int col = addColumn(field_->title());

  // Bools, images, and numbers should be centered
  if(field_->type() == Data::Field::Bool
     || field_->type() == Data::Field::Number
     || field_->type() == Data::Field::Image) {
    setColumnAlignment(col, TQt::AlignHCenter | TQt::AlignVCenter);
    if(field_->type() == Data::Field::Image) {
      m_imageColumns.push_back(col);
    }
  } else {
    setColumnAlignment(col, TQt::AlignLeft | TQt::AlignVCenter);
  }

  // width might be -1, which means set the width to maximum
  // but m_columnWidths is the cached width, so just set it to 0
  m_columnWidths.push_back(TQMAX(width_, 0));

  m_isDirty.push_back(true);

  int id = m_headerMenu->insertItem(field_->title());
  if(width_ == 0) {
    m_headerMenu->setItemChecked(id, false);
    hideColumn(col);
  } else {
    m_headerMenu->setItemChecked(id, true);
    showColumn(col);
  }
  setComparison(col, ListViewComparison::create(field_));
  resetComparisons();
}

void DetailedListView::modifyField(Tellico::Data::CollPtr, Data::FieldPtr oldField_, Data::FieldPtr newField_) {
  int sec; // I need it for after the loop
  for(sec = 0; sec < columns(); ++sec) {
    if(header()->label(sec) == oldField_->title()) {
      break;
    }
  }

  // I thought this would have to be mapped to index, but not the case
  setColumnText(sec, newField_->title());
  if(newField_->type() == Data::Field::Bool
     || newField_->type() == Data::Field::Number
     || newField_->type() == Data::Field::Image) {
    setColumnAlignment(sec, TQt::AlignHCenter | TQt::AlignVCenter);
    if(oldField_->type() == Data::Field::Image) {
      TQValueVector<int>::iterator it = tqFind(m_imageColumns.begin(), m_imageColumns.end(), sec);
      if(it != m_imageColumns.end()) {
        m_imageColumns.erase(it);
      }
    }
    if(newField_->type() == Data::Field::Image) {
      m_imageColumns.push_back(sec);
    }
  } else {
    setColumnAlignment(sec, TQt::AlignLeft | TQt::AlignVCenter);
  }
  m_headerMenu->changeItem(m_headerMenu->idAt(sec+1), newField_->title()); // add 1 since menu has title
  setComparison(sec, ListViewComparison::create(newField_));
  resetComparisons();
}

void DetailedListView::removeField(Tellico::Data::CollPtr, Data::FieldPtr field_) {
//  myDebug() << "DetailedListView::removeField() - " << field_->name() << endl;

  int sec; // I need it for after the loop
  for(sec = 0; sec < columns(); ++sec) {
    if(header()->label(sec) == field_->title()) {
//      myDebug() << "Removing section " << sec << endl;
      break;
    }
  }

  if(sec == columns()) {
    kdWarning() << "DetailedListView::removeField() - no column named " << field_->title() << endl;
    return;
  }

  m_headerMenu->removeItem(m_headerMenu->idAt(sec+1)); // add 1 since menu has title

  m_columnWidths.erase(&m_columnWidths[sec]);
  m_isDirty.erase(&m_isDirty[sec]);

  // I thought this would have to be mapped to index, but not the case
  removeComparison(sec); // must be before removeColumn();
  removeColumn(sec);

  // sometimes resizeEnabled gets messed up
  for(int i = sec; i < columns(); ++i) {
    header()->setResizeEnabled(columnWidth(i) > 0, header()->mapToSection(i));
  }
  resetComparisons();
  slotUpdatePixmap();
  triggerUpdate();
}

void DetailedListView::reorderFields(const Data::FieldVec& fields_) {
//  myDebug() << "DetailedListView::reorderFields()" << endl;
  // find the first out of place field
  int sec = 0;
  Data::FieldVec::ConstIterator it = fields_.begin();
  for(sec = 0; it != fields_.end() && sec < columns(); ++sec, ++it) {
    if(header()->label(sec) != it->title()) {
      break;
    }
  }

  TQStringList visible = visibleColumns();
  for( ; it != fields_.end() && sec < columns();  ++sec, ++it) {
    header()->setLabel(sec, it->title());
    bool isVisible = (visible.findIndex(it->title()) > -1);
    m_headerMenu->changeItem(m_headerMenu->idAt(sec+1), it->title());
    m_headerMenu->setItemChecked(m_headerMenu->idAt(sec+1), isVisible);
    m_columnWidths[sec] = 0;
    if(it->type() == Data::Field::Bool
       || it->type() == Data::Field::Number
       || it->type() == Data::Field::Image) {
      setColumnAlignment(sec, TQt::AlignHCenter | TQt::AlignVCenter);
    } else {
      setColumnAlignment(sec, TQt::AlignLeft | TQt::AlignVCenter);
    }

    if(isVisible) {
      showColumn(sec);
    } else {
      hideColumn(sec);
    }
  }

  slotRefresh();
  slotUpdatePixmap();
  triggerUpdate();
}

int DetailedListView::prevSortedColumn() const {
  return m_prevSortColumn;
}

int DetailedListView::prev2SortedColumn() const {
  return m_prev2SortColumn;
}

void DetailedListView::setPrevSortedColumn(int prev1_, int prev2_) {
  m_prevSortColumn = prev1_;
  m_prev2SortColumn = prev2_;
}

void DetailedListView::setSorting(int column_, bool ascending_/*=true*/) {
//  DEBUG_BLOCK;
  if(column_ != columnSorted()) {
    m_prev2SortColumn = m_prevSortColumn;
    m_prevSortColumn = columnSorted();
  }
  GUI::ListView::setSorting(column_, ascending_);
}

void DetailedListView::updateFirstSection() {
  for(int numCol = 0; numCol < columns(); ++numCol) {
    if(columnWidth(header()->mapToSection(numCol)) > 0) {
      m_firstSection = header()->mapToSection(numCol);
      break;
    }
  }
}

void DetailedListView::slotUpdatePixmap() {
  int oldSection = m_firstSection;
  updateFirstSection();
  if(childCount() == 0 || oldSection == m_firstSection) {
    return;
  }

  Data::EntryPtr entry = static_cast<DetailedEntryItem*>(firstChild())->entry();
  if(!entry) {
    return;
  }

  Data::FieldPtr field1 = entry->collection()->fieldByTitle(columnText(oldSection));
  Data::FieldPtr field2 = entry->collection()->fieldByTitle(columnText(m_firstSection));
  if(!field1 || !field2) {
    kdWarning() << "DetailedListView::slotUpdatePixmap() - no field found." << endl;
    return;
  }

  for(TQListViewItemIterator it(this); it.current(); ++it) {
    setPixmapAndText(static_cast<DetailedEntryItem*>(it.current()), oldSection, field1);
    setPixmapAndText(static_cast<DetailedEntryItem*>(it.current()), m_firstSection, field2);
  }
}

void DetailedListView::saveConfig(Tellico::Data::CollPtr coll_, int configIndex_) {
  TDEConfigGroup config(kapp->config(), TQString::fromLatin1("Options - %1").arg(coll_->typeName()));

  // all of this is to have custom settings on a per file basis
  TQString configN;
  if(coll_->type() == Data::Collection::Base) {
    TQValueList<ConfigInfo> info;
    for(uint i = 0; i < Config::maxCustomURLSettings(); ++i) {
      KURL u = config.readEntry(TQString::fromLatin1("URL_%1").arg(i));
      if(!u.isEmpty() && static_cast<int>(i) != configIndex_) {
        configN = TQString::fromLatin1("_%1").arg(i);
        ConfigInfo ci;
        ci.cols      = config.readListEntry("ColumnNames" + configN);
        ci.widths    = config.readIntListEntry("ColumnWidths" + configN);
        ci.order     = config.readIntListEntry("ColumnOrder" + configN);
        ci.colSorted = config.readNumEntry("SortColumn" + configN);
        ci.ascSort   = config.readBoolEntry("SortAscending" + configN);
        ci.prevSort  = config.readNumEntry("PrevSortColumn" + configN);
        ci.prev2Sort = config.readNumEntry("Prev2SortColumn" + configN);
        info.append(ci);
      }
    }
    // subtract one since we're writing the current settings, too
    uint limit = TQMIN(info.count(), Config::maxCustomURLSettings()-1);
    for(uint i = 0; i < limit; ++i) {
      // starts at one since the current config will be written below
      configN = TQString::fromLatin1("_%1").arg(i+1);
      config.writeEntry("ColumnNames"     + configN, info[i].cols);
      config.writeEntry("ColumnWidths"    + configN, info[i].widths);
      config.writeEntry("ColumnOrder"     + configN, info[i].order);
      config.writeEntry("SortColumn"      + configN, info[i].colSorted);
      config.writeEntry("SortAscending"   + configN, info[i].ascSort);
      config.writeEntry("PrevSortColumn"  + configN, info[i].prevSort);
      config.writeEntry("Prev2SortColumn" + configN, info[i].prev2Sort);
    }
    configN = TQString::fromLatin1("_0");
  }

  TQValueList<int> widths, order;
  for(int i = 0; i < columns(); ++i) {
    if(columnWidthMode(i) == TQListView::Manual) {
      widths += columnWidth(i);
    } else {
      widths += -1; // Maximum width mode
    }
    order += header()->mapToIndex(i);
  }

  config.writeEntry("ColumnWidths"    + configN, widths);
  config.writeEntry("ColumnOrder"     + configN, order);
  config.writeEntry("SortColumn"      + configN, columnSorted());
  config.writeEntry("SortAscending"   + configN, ascendingSort());
  config.writeEntry("PrevSortColumn"  + configN, prevSortedColumn());
  config.writeEntry("Prev2SortColumn" + configN, prev2SortedColumn());

  TQStringList colNames;
  for(int col = 0; col < columns(); ++col) {
    colNames += coll_->fieldNameByTitle(columnText(col));
  }
  config.writeEntry("ColumnNames" + configN, colNames);
}

TQString DetailedListView::sortColumnTitle1() const {
  return columnSorted() == -1 ? TQString() : header()->label(columnSorted());
}

TQString DetailedListView::sortColumnTitle2() const {
  return prevSortedColumn() == -1 ? TQString() : header()->label(prevSortedColumn());
}

TQString DetailedListView::sortColumnTitle3() const {
  return prev2SortedColumn() == -1 ? TQString() : header()->label(prev2SortedColumn());
}

TQStringList DetailedListView::visibleColumns() const {
  TQStringList titles;
  for(int i = 0; i < columns(); ++i) {
    if(columnWidth(header()->mapToSection(i)) > 0) {
      titles << columnText(header()->mapToSection(i));
    }
  }
  return titles;
}

// can't be const
Tellico::Data::EntryVec DetailedListView::visibleEntries() {
  // We could just return the full collection entry list if the filter is 0
  // but printing depends on the sorted order
  Data::EntryVec entries;
  for(TQListViewItemIterator it(this); it.current(); ++it) {
    if(it.current()->isVisible()) {
      entries.append(static_cast<DetailedEntryItem*>(it.current())->entry());
    }
  }
  return entries;
}

void DetailedListView::selectAllVisible() {
  blockSignals(true);
  for(TQListViewItemIterator it(this); it.current(); ++it) {
    if(it.current()->isVisible()) {
      setSelected(it.current(), true);
    }
  }
  blockSignals(false);
  // FIXME: not right with MultiSelectionListView
  slotSelectionChanged();
}

void DetailedListView::resetEntryStatus() {
  for(TQListViewItemIterator it(this); it.current(); ++it) {
    static_cast<DetailedEntryItem*>(it.current())->setState(DetailedEntryItem::Normal);
  }
  triggerUpdate();
}

int DetailedListView::compare(int col_, const GUI::ListViewItem* item1_, GUI::ListViewItem* item2_, bool asc_) {
  DetailedEntryItem* item2 = static_cast<DetailedEntryItem*>(item2_);
  int res = 0;
  return (res = compareColumn(col_,              item1_, item2, asc_)) != 0 ? res :
         (res = compareColumn(m_prevSortColumn,  item1_, item2, asc_)) != 0 ? res :
         (res = compareColumn(m_prev2SortColumn, item1_, item2, asc_)) != 0 ? res : 0;
}

int DetailedListView::compareColumn(int col, const GUI::ListViewItem* item1, GUI::ListViewItem* item2, bool asc) {
  return GUI::ListView::compare(col, item1, item2, asc);
}

void DetailedListView::resetComparisons() {
  // this is only allowed when the view is not empty, so we can grab a collection ptr
  if(childCount() == 0) {
    return;
  }
  Data::CollPtr coll = static_cast<DetailedEntryItem*>(firstChild())->entry()->collection();
  if(!coll) {
    return;
  }
  for(int i = 0; i < columns(); ++i) {
    Data::FieldPtr f = coll->fieldByTitle(header()->label(i));
    if(f) {
      setComparison(i, ListViewComparison::create(f));
    }
  }
}

#include "detailedlistview.moc"
