/***************************************************************************
    copyright            : (C) 2003-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 "controller.h"
#include "mainwindow.h"
#include "groupview.h"
#include "detailedlistview.h"
#include "entryeditdialog.h"
#include "viewstack.h"
#include "entryview.h"
#include "entryiconview.h"
#include "entry.h"
#include "field.h"
#include "filter.h"
#include "filterdialog.h"
#include "tellico_kernel.h"
#include "latin1literal.h"
#include "collection.h"
#include "document.h"
#include "borrower.h"
#include "filterview.h"
#include "loanview.h"
#include "entryitem.h"
#include "gui/tabcontrol.h"
#include "calendarhandler.h"
#include "tellico_debug.h"
#include "groupiterator.h"
#include "tellico_utils.h"
#include "entryupdater.h"
#include "entrymerger.h"

#include <tdelocale.h>
#include <tdemessagebox.h>
#include <tdeaction.h>
#include <tdetoolbarbutton.h>

#include <tqpopupmenu.h>

using Tellico::Controller;

Controller* Controller::s_self = 0;

Controller::Controller(MainWindow* parent_, const char* name_)
    : TQObject(parent_, name_), m_mainWindow(parent_), m_working (false), m_widgetWithSelection(0) {
}

void Controller::addObserver(Observer* obs) {
  m_observers.push_back(obs);
}

void Controller::removeObserver(Observer* obs) {
  m_observers.remove(obs);
}

Tellico::GroupIterator Controller::groupIterator() const {
  return GroupIterator(m_mainWindow->m_groupView);
}

TQString Controller::groupBy() const {
  return m_mainWindow->m_groupView->groupBy();
}

TQStringList Controller::expandedGroupBy() const {
  TQStringList g = groupBy();
  // special case for pseudo-group
  if(g[0] == Data::Collection::s_peopleGroupName) {
    g.clear();
    Data::FieldVec fields = Data::Document::self()->collection()->peopleFields();
    for(Data::FieldVec::Iterator it = fields.begin(); it != fields.end(); ++it) {
      g << it->name();
    }
  }
  // special case for no groups
  if(g[0].isEmpty()) {
    g.clear();
  }
  return g;
}

TQStringList Controller::sortTitles() const {
  TQStringList list;
  list << m_mainWindow->m_detailedView->sortColumnTitle1();
  list << m_mainWindow->m_detailedView->sortColumnTitle2();
  list << m_mainWindow->m_detailedView->sortColumnTitle3();
  return list;
}

TQStringList Controller::visibleColumns() const {
  return m_mainWindow->m_detailedView->visibleColumns();
}

Tellico::Data::EntryVec Controller::visibleEntries() {
  return m_mainWindow->m_detailedView->visibleEntries();
}

void Controller::slotCollectionAdded(Data::CollPtr coll_) {
//  myDebug() << "Controller::slotCollectionAdded()" << endl;
  // at start-up, this might get called too early, so check and bail
  if(!m_mainWindow->m_groupView) {
    return;
  }

  // do this first because the group view will need it later
  m_mainWindow->readCollectionOptions(coll_);
  m_mainWindow->slotUpdateToolbarIcons();
  m_mainWindow->updateEntrySources(); // has to be called before all the addCollection()
  // calls in the widgets since they may want menu updates

//  blockAllSignals(true);
  m_mainWindow->m_detailedView->addCollection(coll_);
  m_mainWindow->m_groupView->addCollection(coll_);
  m_mainWindow->m_editDialog->setLayout(coll_);
  if(!coll_->filters().isEmpty()) {
    m_mainWindow->addFilterView();
    m_mainWindow->m_filterView->addCollection(coll_);
    m_mainWindow->m_viewTabs->setTabBarHidden(false);
  }
  if(!coll_->borrowers().isEmpty()) {
    m_mainWindow->addLoanView();
    m_mainWindow->m_loanView->addCollection(coll_);
    m_mainWindow->m_viewTabs->setTabBarHidden(false);
  }
//  blockAllSignals(false);

  m_mainWindow->slotStatusMsg(i18n("Ready."));

  m_selectedEntries.clear();
  m_mainWindow->slotEntryCount();

  emit collectionAdded(coll_->type());

  updateActions();

  connect(coll_, TQ_SIGNAL(signalGroupsModified(Tellico::Data::CollPtr, PtrVector<Tellico::Data::EntryGroup>)),
          m_mainWindow->m_groupView, TQ_SLOT(slotModifyGroups(Tellico::Data::CollPtr, PtrVector<Tellico::Data::EntryGroup>)));

  connect(coll_, TQ_SIGNAL(signalRefreshField(Tellico::Data::FieldPtr)),
          this, TQ_SLOT(slotRefreshField(Tellico::Data::FieldPtr)));
}

void Controller::slotCollectionModified(Data::CollPtr coll_) {
  // easiest thing is to signal collection deleted, then added?
  // FIXME: Signals for delete collection and then added are yucky
  slotCollectionDeleted(coll_);
  slotCollectionAdded(coll_);
}

void Controller::slotCollectionDeleted(Data::CollPtr coll_) {
//  myDebug() << "Controller::slotCollectionDeleted()" << endl;

  blockAllSignals(true);
  m_mainWindow->saveCollectionOptions(coll_);
  m_mainWindow->m_groupView->removeCollection(coll_);
  if(m_mainWindow->m_filterView) {
    m_mainWindow->m_filterView->clear();
  }
  if(m_mainWindow->m_loanView) {
    m_mainWindow->m_loanView->clear();
  }
  m_mainWindow->m_detailedView->removeCollection(coll_);
  m_mainWindow->m_viewStack->clear();
  blockAllSignals(false);

  // disconnect all signals from the collection
  // this is needed because the Collection::appendCollection() and mergeCollection()
  // functions signal collection deleted then added for the same collection
  coll_->disconnect();
}

void Controller::addedEntries(Data::EntryVec entries_) {
  blockAllSignals(true);
  for(ObserverVec::Iterator it = m_observers.begin(); it != m_observers.end(); ++it) {
    it->addEntries(entries_);
  }
  m_mainWindow->slotQueueFilter();
  blockAllSignals(false);
}

void Controller::modifiedEntries(Data::EntryVec entries_) {
  // when a new document is being loaded, loans are added to borrowers, which
  // end up calling Entry::checkIn() which called Document::saveEntry() which calls here
  // ignore that
  if(!m_mainWindow->m_initialized) {
    return;
  }
  blockAllSignals(true);
  for(ObserverVec::Iterator it = m_observers.begin(); it != m_observers.end(); ++it) {
    it->modifyEntries(entries_);
  }
  m_mainWindow->m_viewStack->entryView()->slotRefresh(); // special case
  m_mainWindow->slotQueueFilter();
  blockAllSignals(false);
}

void Controller::removedEntries(Data::EntryVec entries_) {
  blockAllSignals(true);
  for(ObserverVec::Iterator it = m_observers.begin(); it != m_observers.end(); ++it) {
    it->removeEntries(entries_);
  }
  for(Data::EntryVecIt it = entries_.begin(); it != entries_.end(); ++it) {
    m_selectedEntries.remove(it);
    m_currentEntries.remove(it);
  }
  if(m_currentEntries.isEmpty()) {
    m_mainWindow->m_viewStack->entryView()->clear();
  }
  m_mainWindow->slotEntryCount();
  m_mainWindow->slotQueueFilter();
  blockAllSignals(false);
}

void Controller::addedField(Data::CollPtr coll_, Data::FieldPtr field_) {
  for(ObserverVec::Iterator it = m_observers.begin(); it != m_observers.end(); ++it) {
    it->addField(coll_, field_);
  }
  m_mainWindow->m_viewStack->refresh();
  m_mainWindow->slotUpdateCollectionToolBar(coll_);
  m_mainWindow->slotQueueFilter();
}

void Controller::removedField(Data::CollPtr coll_, Data::FieldPtr field_) {
//  myDebug() << "Controller::removedField() - " << field_->name() << endl;
  for(ObserverVec::Iterator it = m_observers.begin(); it != m_observers.end(); ++it) {
    it->removeField(coll_, field_);
  }
  m_mainWindow->m_viewStack->refresh();
  m_mainWindow->slotUpdateCollectionToolBar(coll_);
  m_mainWindow->slotQueueFilter();
}

void Controller::modifiedField(Data::CollPtr coll_, Data::FieldPtr oldField_, Data::FieldPtr newField_) {
  for(ObserverVec::Iterator it = m_observers.begin(); it != m_observers.end(); ++it) {
    it->modifyField(coll_, oldField_, newField_);
  }
  m_mainWindow->m_viewStack->refresh();
  m_mainWindow->slotUpdateCollectionToolBar(coll_);
  m_mainWindow->slotQueueFilter();
}

void Controller::reorderedFields(Data::CollPtr coll_) {
  m_mainWindow->m_editDialog->setLayout(coll_);
  m_mainWindow->m_detailedView->reorderFields(coll_->fields());
  m_mainWindow->slotUpdateCollectionToolBar(coll_);
  m_mainWindow->m_viewStack->refresh();
}

void Controller::slotClearSelection() {
  if(m_working) {
    return;
  }

  m_working = true;
  blockAllSignals(true);

  m_mainWindow->m_detailedView->clearSelection();
  m_mainWindow->m_groupView->clearSelection();
  if(m_mainWindow->m_loanView) {
    m_mainWindow->m_loanView->clearSelection();
  }
//  m_mainWindow->m_editDialog->clear(); let this stay
//  m_mainWindow->m_viewStack->clear(); let this stay

  blockAllSignals(false);

  m_selectedEntries.clear();
  updateActions();
  m_mainWindow->slotEntryCount();
  m_working = false;
}

void Controller::slotUpdateSelection(TQWidget* widget_, const Data::EntryVec& entries_) {
  if(m_working) {
    return;
  }
  m_working = true;

  if(widget_) {
    m_widgetWithSelection = widget_;
  }
//  myDebug() << "Controller::slotUpdateSelection() entryList - " << entries_.count() << endl;

  blockAllSignals(true);
// in the list view and group view, if entries are selected in one, clear selection in other
  if(m_widgetWithSelection != m_mainWindow->m_detailedView) {
    m_mainWindow->m_detailedView->clearSelection();
  }
  if(m_widgetWithSelection != m_mainWindow->m_groupView) {
    m_mainWindow->m_groupView->clearSelection();
  }
  if(m_mainWindow->m_filterView && m_widgetWithSelection != m_mainWindow->m_filterView) {
    m_mainWindow->m_filterView->clearSelection();
  }
  if(m_mainWindow->m_loanView && m_widgetWithSelection != m_mainWindow->m_loanView) {
    m_mainWindow->m_loanView->clearSelection();
  }
  if(m_widgetWithSelection != m_mainWindow->m_editDialog) {
    m_mainWindow->m_editDialog->setContents(entries_);
  }
  // only show first one
  if(m_widgetWithSelection && m_widgetWithSelection != m_mainWindow->m_viewStack->iconView()) {
    if(entries_.count() > 1) {
      m_mainWindow->m_viewStack->showEntries(entries_);
    } else if(entries_.count() > 0) {
      m_mainWindow->m_viewStack->showEntry(entries_[0]);
    }
  }
  blockAllSignals(false);

  m_selectedEntries = entries_;
  updateActions();
  m_mainWindow->slotEntryCount();
  m_working = false;
}

void Controller::slotGoPrevEntry() {
  goEntrySibling(PrevEntry);
}

void Controller::slotGoNextEntry() {
  goEntrySibling(NextEntry);
}

void Controller::goEntrySibling(EntryDirection dir_) {
  // if there are currently multiple selected, then do nothing
  if(m_selectedEntries.count() != 1) {
    return;
  }
  // find the widget that has an entry selected
  GUI::ListView* view = ::tqt_cast<GUI::ListView*>(m_widgetWithSelection);
  if(!view) {
    return;
  }

  GUI::ListViewItemList items = view->selectedItems();
  if(items.count() != 1) {
    return;
  }
  GUI::ListViewItem* item = items.first();
  if(item->isEntryItem()) {
    bool looped = false;
    // check sanity
    if(m_selectedEntries.front() != static_cast<EntryItem*>(item)->entry()) {
      myDebug() << "Controller::slotGoNextEntry() - entries don't match!" << endl;
    }
    GUI::ListViewItem* nextItem = static_cast<GUI::ListViewItem*>(dir_ == PrevEntry
                                                                         ? item->itemAbove()
                                                                         : item->itemBelow());
    if(!nextItem) {
      // cycle through
      nextItem = static_cast<GUI::ListViewItem*>(dir_ == PrevEntry
                                                       ? view->lastItem()
                                                       : view->firstChild());
      looped = true;
    }
    while(!nextItem->isVisible()) {
      nextItem = static_cast<GUI::ListViewItem*>(dir_ == PrevEntry
                                                       ? nextItem->itemAbove()
                                                       : nextItem->itemBelow());
    }
    while(nextItem && !nextItem->isEntryItem()) {
      nextItem->setOpen(true); // have to be open to find the next one
      nextItem = static_cast<GUI::ListViewItem*>(dir_ == PrevEntry
                                                       ? nextItem->itemAbove()
                                                       : nextItem->itemBelow());
      if(!nextItem && !looped) {
        // cycle through
        nextItem = static_cast<GUI::ListViewItem*>(dir_ == PrevEntry
                                                         ? view->lastItem()
                                                         : view->firstChild());
        looped = true;
      }
    }
    if(nextItem) {
      Data::EntryPtr e = static_cast<EntryItem*>(nextItem)->entry();
      view->blockSignals(true);
      view->setSelected(item, false);
      view->setSelected(nextItem, true);
      view->ensureItemVisible(nextItem);
      view->blockSignals(false);
      slotUpdateSelection(view, e);
    }
  }
}

void Controller::slotUpdateCurrent(const Data::EntryVec& entries_) {
  if(m_working) {
    return;
  }
  m_working = true;

  blockAllSignals(true);
  m_mainWindow->m_viewStack->showEntries(entries_);
  blockAllSignals(false);

  m_currentEntries = entries_;
  m_working = false;
}

void Controller::slotUpdateSelectedEntries(const TQString& source_) {
  if(m_selectedEntries.isEmpty()) {
    return;
  }

  // it deletes itself when done
  // signal mapper strings can't be empty, "_all" is set in mainwindow
  if(source_.isEmpty() || source_ == Latin1Literal("_all")) {
    new EntryUpdater(m_selectedEntries.front()->collection(), m_selectedEntries, this);
  } else {
    new EntryUpdater(source_, m_selectedEntries.front()->collection(), m_selectedEntries, this);
  }
}

void Controller::slotDeleteSelectedEntries() {
  if(m_selectedEntries.isEmpty()) {
    return;
  }

  m_working = true;

  // confirm delete
  if(m_selectedEntries.count() == 1) {
    TQString str = i18n("Do you really want to delete this entry?");
    TQString dontAsk = TQString::fromLatin1("DeleteEntry");
    int ret = KMessageBox::warningContinueCancel(Kernel::self()->widget(), str, i18n("Delete Entry"),
                                                 KGuiItem(i18n("&Delete"), TQString::fromLatin1("edit-delete")), dontAsk);
    if(ret != KMessageBox::Continue) {
      m_working = false;
      return;
    }
  } else {
    TQStringList names;
    for(Data::EntryVecIt entry = m_selectedEntries.begin(); entry != m_selectedEntries.end(); ++entry) {
      names += entry->title();
    }
    TQString str = i18n("Do you really want to delete these entries?");
    // historically called DeleteMultipleBooks, don't change
    TQString dontAsk = TQString::fromLatin1("DeleteMultipleBooks");
    int ret = KMessageBox::warningContinueCancelList(Kernel::self()->widget(), str, names,
                                                     i18n("Delete Multiple Entries"),
                                                     KGuiItem(i18n("&Delete"), TQString::fromLatin1("edit-delete")), dontAsk);
    if(ret != KMessageBox::Continue) {
      m_working = false;
      return;
    }
  }

  GUI::CursorSaver cs;
  Kernel::self()->removeEntries(m_selectedEntries);
  updateActions();

  m_working = false;

  // special case, the detailed list view selects the next item, so handle that
//  Data::EntryList newList;
//  for(GUI::ListViewItemListIt it(m_mainWindow->m_detailedView->selectedItems()); it.current(); ++it) {
//    newList.append(static_cast<EntryItem*>(it.current())->entry());
//  }
//  slotUpdateSelection(m_mainWindow->m_detailedView, newList);
  slotClearSelection();
}

void Controller::slotMergeSelectedEntries() {
  // merge requires at least 2 entries
  if(m_selectedEntries.count() < 2) {
    return;
  }

  new EntryMerger(m_selectedEntries, this);
}

void Controller::slotRefreshField(Data::FieldPtr field_) {
//  myDebug() << "Controller::slotRefreshField()" << endl;
  // group view only needs to refresh if it's the title
  if(field_->name() == Latin1Literal("title")) {
    m_mainWindow->m_groupView->populateCollection();
  }
  m_mainWindow->m_detailedView->slotRefresh();
  m_mainWindow->m_viewStack->refresh();
}

void Controller::slotCopySelectedEntries() {
  if(m_selectedEntries.isEmpty()) {
    return;
  }

  // keep copy of selected entries
  Data::EntryVec old = m_selectedEntries;

  GUI::CursorSaver cs;
  // need to create copies
  Data::EntryVec entries;
  for(Data::EntryVecIt it = m_selectedEntries.begin(); it != m_selectedEntries.end(); ++it) {
    entries.append(new Data::Entry(*it));
  }
  Kernel::self()->addEntries(entries, false);
  slotUpdateSelection(0, old);
}

void Controller::blockAllSignals(bool block_) const {
// sanity check
  if(!m_mainWindow->m_initialized) {
    return;
  }
  m_mainWindow->m_detailedView->blockSignals(block_);
  m_mainWindow->m_groupView->blockSignals(block_);
  if(m_mainWindow->m_loanView) {
    m_mainWindow->m_loanView->blockSignals(block_);
  }
  if(m_mainWindow->m_filterView) {
    m_mainWindow->m_filterView->blockSignals(block_);
  }
  m_mainWindow->m_editDialog->blockSignals(block_);
  m_mainWindow->m_viewStack->iconView()->blockSignals(block_);
}

void Controller::slotUpdateFilter(FilterPtr filter_) {
//  myDebug() << "Controller::slotUpdateFilter()" << endl;
  blockAllSignals(true);

  // the view takes over ownership of the filter
  if(filter_ && !filter_->isEmpty()) {
    // clear the icon view selection only
    // the detailed view takes care of itself
    m_mainWindow->m_viewStack->iconView()->clearSelection();
    m_selectedEntries.clear();
  }
  updateActions();

  m_mainWindow->m_detailedView->setFilter(filter_); // takes ownership

  blockAllSignals(false);

  m_mainWindow->slotEntryCount();
}

void Controller::editEntry(Data::EntryPtr) const {
  m_mainWindow->slotShowEntryEditor();
}

void Controller::plugCollectionActions(TQPopupMenu* popup_) {
  if(!popup_) {
    return;
  }

  m_mainWindow->action("coll_rename_collection")->plug(popup_);
  m_mainWindow->action("coll_fields")->plug(popup_);
  m_mainWindow->action("change_entry_grouping")->plug(popup_);
}

void Controller::plugEntryActions(TQPopupMenu* popup_) {
  if(!popup_) {
    return;
  }

//  m_mainWindow->m_newEntry->plug(popup_);
  m_mainWindow->m_editEntry->plug(popup_);
  m_mainWindow->m_copyEntry->plug(popup_);
  m_mainWindow->m_deleteEntry->plug(popup_);
  m_mainWindow->m_mergeEntry->plug(popup_);
  m_mainWindow->m_updateEntryMenu->plug(popup_);
  // there's a bug in TDEActionMenu with KXMLGUIFactory::plugActionList
  // pluging the menu action isn't enough to have the popup get populated
  plugUpdateMenu(popup_);
  popup_->insertSeparator();
  m_mainWindow->m_checkOutEntry->plug(popup_);
}

void Controller::plugUpdateMenu(TQPopupMenu* popup_) {
  TQPopupMenu* updatePopup = 0;
  const uint count = popup_->count();
  for(uint i = 0; i < count; ++i) {
    TQMenuItem* item = popup_->findItem(popup_->idAt(i));
    if(item && item->text() == m_mainWindow->m_updateEntryMenu->text()) {
      updatePopup = item->popup();
      break;
    }
  }

  if(!updatePopup) {
    return;
  }

  // I can't figure out why the actions get duplicated, but they do
  // so clear them all
  m_mainWindow->m_updateAll->unplug(updatePopup);
  for(TQPtrListIterator<TDEAction> it(m_mainWindow->m_fetchActions); it.current(); ++it) {
    it.current()->unplug(updatePopup);
  }

  // clear separator, too
  updatePopup->clear();

  m_mainWindow->m_updateAll->plug(updatePopup);
  updatePopup->insertSeparator();
  for(TQPtrListIterator<TDEAction> it(m_mainWindow->m_fetchActions); it.current(); ++it) {
    it.current()->plug(updatePopup);
  }
}

void Controller::updateActions() const {
  bool emptySelection = m_selectedEntries.isEmpty();
  m_mainWindow->stateChanged(TQString::fromLatin1("empty_selection"),
                             emptySelection ? KXMLGUIClient::StateNoReverse : KXMLGUIClient::StateReverse);
  for(TQPtrListIterator<TDEAction> it(m_mainWindow->m_fetchActions); it.current(); ++it) {
    it.current()->setEnabled(!emptySelection);
  }
  //only enable citation items when it's a bibliography
  bool isBibtex = Kernel::self()->collectionType() == Data::Collection::Bibtex;
  if(isBibtex) {
    m_mainWindow->action("cite_clipboard")->setEnabled(!emptySelection);
    m_mainWindow->action("cite_lyxpipe")->setEnabled(!emptySelection);
    m_mainWindow->action("cite_openoffice")->setEnabled(!emptySelection);
  }
  m_mainWindow->m_checkInEntry->setEnabled(canCheckIn());

  if(m_selectedEntries.count() < 2) {
    m_mainWindow->m_editEntry->setText(i18n("&Edit Entry..."));
    m_mainWindow->m_copyEntry->setText(i18n("D&uplicate Entry"));
    m_mainWindow->m_updateEntryMenu->setText(i18n("&Update Entry"));
    m_mainWindow->m_deleteEntry->setText(i18n("&Delete Entry"));
    m_mainWindow->m_mergeEntry->setEnabled(false);
  } else {
    m_mainWindow->m_editEntry->setText(i18n("&Edit Entries..."));
    m_mainWindow->m_copyEntry->setText(i18n("D&uplicate Entries"));
    m_mainWindow->m_updateEntryMenu->setText(i18n("&Update Entries"));
    m_mainWindow->m_deleteEntry->setText(i18n("&Delete Entries"));
    m_mainWindow->m_mergeEntry->setEnabled(true);
  }
}

void Controller::addedBorrower(Data::BorrowerPtr borrower_) {
  m_mainWindow->addLoanView(); // just in case
  for(ObserverVec::Iterator it = m_observers.begin(); it != m_observers.end(); ++it) {
    it->addBorrower(borrower_);
  }
  m_mainWindow->m_viewTabs->setTabBarHidden(false);
}

void Controller::modifiedBorrower(Data::BorrowerPtr borrower_) {
  for(ObserverVec::Iterator it = m_observers.begin(); it != m_observers.end(); ++it) {
    it->modifyBorrower(borrower_);
  }
  hideTabs();
}

void Controller::addedFilter(FilterPtr filter_) {
  m_mainWindow->addFilterView(); // just in case
  for(ObserverVec::Iterator it = m_observers.begin(); it != m_observers.end(); ++it) {
    it->addFilter(filter_);
  }
  m_mainWindow->m_viewTabs->setTabBarHidden(false);
}

void Controller::removedFilter(FilterPtr filter_) {
  for(ObserverVec::Iterator it = m_observers.begin(); it != m_observers.end(); ++it) {
    it->removeFilter(filter_);
  }
  hideTabs();
}

void Controller::slotCheckOut() {
  if(m_selectedEntries.isEmpty()) {
    return;
  }

  Data::EntryVec loanedEntries = m_selectedEntries;

  // check to see if any of the entries are already on-loan, and warn user
  TQMap<TQString, Data::EntryPtr> alreadyLoaned;
  const Data::BorrowerVec& borrowers = Data::Document::self()->collection()->borrowers();
  for(Data::BorrowerVec::ConstIterator it = borrowers.begin(); it != borrowers.end(); ++it) {
    const Data::LoanVec& loans = it->loans();
    for(Data::LoanVec::ConstIterator it2 = loans.begin(); it2 != loans.end(); ++it2) {
      if(m_selectedEntries.contains(it2->entry())) {
        alreadyLoaned.insert(it2->entry()->title(), it2->entry());
      }
    }
  }
  if(!alreadyLoaned.isEmpty()) {
    KMessageBox::informationList(Kernel::self()->widget(),
                                 i18n("The following items are already loaned, but Tellico "
                                      "does not currently support lending an item multiple "
                                      "times. They will be removed from the list of items "
                                      "to lend."),
                                      alreadyLoaned.keys());
    TQMapConstIterator<TQString, Data::EntryPtr> it = alreadyLoaned.constBegin();
    TQMapConstIterator<TQString, Data::EntryPtr> end = alreadyLoaned.constEnd();
    for( ; it != end; ++it) {
      loanedEntries.remove(it.data());
    }
    if(loanedEntries.isEmpty()) {
      return;
    }
  }

  if(Kernel::self()->addLoans(loanedEntries)) {
    m_mainWindow->m_checkInEntry->setEnabled(true);
  }
}

void Controller::slotCheckIn() {
  slotCheckIn(m_selectedEntries);
}

void Controller::slotCheckIn(const Data::EntryVec& entries_) {
  if(entries_.isEmpty()) {
    return;
  }

  Data::LoanVec loans;
  for(Data::EntryVec::ConstIterator it = entries_.begin(); it != entries_.end(); ++it) {
    // these have to be in the loop since if a borrower gets empty
    // it will be deleted, so the vector could change, for every entry iterator
    Data::BorrowerVec vec = Data::Document::self()->collection()->borrowers();
    // vec.end() must be in the loop, do NOT cache the value, it could change!
    for(Data::BorrowerVec::Iterator bIt = vec.begin(); bIt != vec.end(); ++bIt) {
      Data::LoanPtr l = bIt->loan(it.data());
      if(l) {
        loans.append(l);
        // assume it's only loaned once
        break;
      }
    }
  }

  if(Kernel::self()->removeLoans(loans)) {
    m_mainWindow->m_checkInEntry->setEnabled(false);
  }
  hideTabs();
}

void Controller::hideTabs() const {
  if((!m_mainWindow->m_filterView || m_mainWindow->m_filterView->childCount() == 0) &&
     (!m_mainWindow->m_loanView || m_mainWindow->m_loanView->childCount() == 0)) {
    m_mainWindow->m_viewTabs->showPage(m_mainWindow->m_groupView);
    m_mainWindow->m_viewTabs->setTabBarHidden(true);
  }
}

inline
bool Controller::canCheckIn() const {
  for(Data::EntryVec::ConstIterator entry = m_selectedEntries.begin(); entry != m_selectedEntries.end(); ++entry) {
    if(entry->field(TQString::fromLatin1("loaned")) == Latin1Literal("true")) {
      return true;
    }
  }
  return false;
}

void Controller::updatedFetchers() {
  m_mainWindow->updateEntrySources();
}

#include "controller.moc"
