/****************************************************************************
** Dooble - The Secure Internet Web Browser
**
** Copyright (c) 2008, 2009, 2010, 2011 Alexis Megas,
** Gunther van Dooble, and the Dooble Team.
** All rights reserved.
**
** License: GPL2 only:
** 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; version 2 of the License only.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
** or see here: http://www.gnu.org/licenses/gpl.html
**
** For the WebKit library, please see: http://webkit.org.
**
** THE CODE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY
** EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
** ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
** GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
** IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
** ARISING IN ANY WAY OUT OF THE USE OF THIS APPLICATION, EVEN IF ADVISED
** OF THE POSSIBILITY OF SUCH DAMAGE.
**
** Please report all praise, requests, bugs, and problems to the project
** team and administrators: http://sf.net/projects/dooble.
**
** You can find us listed at our project page. New team members are welcome.
** The name of the authors should not be used to endorse or promote products
** derived from Dooble without specific prior written permission.
** If you use this code for other projects, please let us know.
**
** Web sites:
**   http://sf.net/projects/dooble
**   http://dooble.sf.net
****************************************************************************/

#include <QDir>
#include <QUrl>
#include <QIcon>
#include <QMenu>
#include <QBuffer>
#include <QSettings>
#include <QSqlQuery>
#include <QScrollBar>
#include <QSqlRecord>

#include "dooble.h"
#include "dhistory.h"

dhistory::dhistory(void):QMainWindow()
{
  ui.setupUi(this);
#ifdef Q_WS_MAC
  setAttribute(Qt::WA_MacMetalStyle, true);
#endif
  m_model = new QStandardItemModel(this);
  m_model->setSortRole(Qt::UserRole);

  QStringList list;

  list << tr("Name") << tr("Host")
       << tr("Location") << tr("Last Time Visited")
       << tr("Visits");
  m_model->setHorizontalHeaderLabels(list);
  list.clear();
  ui.history->setModel(m_model);
  ui.history->setSelectionMode(QAbstractItemView::ExtendedSelection);
  ui.history->setSortingEnabled(true);
  ui.history->horizontalHeader()->setSortIndicator(0, Qt::AscendingOrder);
  ui.history->horizontalHeader()->setSortIndicatorShown(true);
  ui.history->horizontalHeader()->setStretchLastSection(true);

  for(int i = 0; i < ui.history->horizontalHeader()->count(); i++)
    ui.history->resizeColumnToContents(i);

  ui.history->horizontalHeader()->setResizeMode(QHeaderView::Interactive);
  ui.searchLineEdit->setPlaceholderText("Search History");
  slotSetIcons();
  slotTimeout();
  connect(ui.history->horizontalHeader(),
	  SIGNAL(sortIndicatorChanged(int, Qt::SortOrder)),
	  this,
	  SLOT(slotSort(int, Qt::SortOrder)));
  connect(ui.deletePushButton,
	  SIGNAL(clicked(void)),
	  this,
	  SLOT(slotDeletePage(void)));
  connect(ui.deleteAllPushButton,
	  SIGNAL(clicked(void)),
	  this,
	  SLOT(slotDeleteAll(void)));
  connect(ui.treeWidget,
	  SIGNAL(itemSelectionChanged(void)),
	  this,
	  SLOT(slotItemSelectionChanged(void)));
  connect(this,
	  SIGNAL(iconsChanged(void)),
	  ui.searchLineEdit,
	  SLOT(slotSetIcons(void)));
  connect(ui.searchLineEdit, SIGNAL(textEdited(const QString &)),
	  this, SLOT(slotTextChanged(const QString &)));
  connect(ui.history,
	  SIGNAL(doubleClicked(const QModelIndex &)),
	  this,
	  SLOT(slotItemDoubleClicked(const QModelIndex &)));
  connect(ui.closePushButton,
	  SIGNAL(clicked(void)),
	  this,
	  SLOT(close(void)));
  connect(ui.history->selectionModel(),
	  SIGNAL(selectionChanged(const QItemSelection &,
				  const QItemSelection &)),
	  this,
	  SLOT(slotItemsSelected(const QItemSelection &,
				 const QItemSelection &)));
  ui.history->setContextMenuPolicy(Qt::CustomContextMenu);
  connect(ui.history, SIGNAL(customContextMenuRequested(const QPoint &)),
	  SLOT(slotShowContextMenu(const QPoint &)));

  m_timer = new QTimer(this);
  m_timer->setInterval(1000);
  connect(m_timer,
	  SIGNAL(timeout(void)),
	  this,
	  SLOT(slotTimeout(void)));
  ui.splitter->setStretchFactor(0, 0);
  ui.splitter->setStretchFactor(1, 1);

  QTreeWidgetItem *item1 = 0;
  QList<QTreeWidgetItem *> items;

  item1 = new QTreeWidgetItem((QTreeWidget *) 0,
			      QStringList(tr("All")));
  item1->setData(0, Qt::UserRole, "");
  items << item1;
  item1 = new QTreeWidgetItem((QTreeWidget *) 0,
			      QStringList(tr("Today")));
  item1->setData
    (0, Qt::UserRole, QDate::currentDate().toString(Qt::ISODate));
  items << item1;
  item1 = new QTreeWidgetItem((QTreeWidget *) 0,
			      QStringList(tr("Yesterday")));
  item1->setData
    (0, Qt::UserRole, QDate::currentDate().addDays(-1).
     toString(Qt::ISODate));
  items << item1;

  QDate currentMonth(QDate::currentDate());
  QTreeWidgetItem *item2 = new QTreeWidgetItem
    ((QTreeWidget *) 0,
     QStringList(currentMonth.toString("MMMM").toAscii().data()));

  item2->setData
    (0, Qt::UserRole, currentMonth.toString(Qt::ISODate).mid(0, 7));
  items << item2;

  QDate lastMonth(QDate::currentDate().addMonths(-1));
  QTreeWidgetItem *item3 = new QTreeWidgetItem
    ((QTreeWidget *) 0,
     QStringList(tr(lastMonth.toString("MMMM").toAscii().data())));

  item3->setData
    (0, Qt::UserRole, lastMonth.toString(Qt::ISODate).mid(0, 7));
  items << item3;
  ui.treeWidget->insertTopLevelItems(0, items);

  for(int i = 0; i < currentMonth.daysInMonth(); i++)
    {
      QDate date;

      date.setDate(currentMonth.year(), currentMonth.month(), i + 1);
      item1 = new QTreeWidgetItem
	(item2, QStringList(tr(QString::number(i + 1).toAscii().data())));
      item1->setData(0, Qt::UserRole, date.toString(Qt::ISODate));
    }

  for(int i = 0; i < lastMonth.daysInMonth(); i++)
    {
      QDate date;

      date.setDate(lastMonth.year(), lastMonth.month(), i + 1);
      item1 = new QTreeWidgetItem
	(item3, QStringList(tr(QString::number(i + 1).toAscii().data())));
      item1->setData(0, Qt::UserRole, date.toString(Qt::ISODate));
    }

  items.at(1)->setSelected(true);
  ui.treeWidget->setFocus();

  if(dooble::s_settings.contains("historyWindow/size"))
    resize(dooble::s_settings.value("historyWindow/size").toSize());

  if(dooble::s_settings.contains("historyWindow/splitterState"))
    ui.splitter->restoreState
      (dooble::s_settings.value("historyWindow/splitterState",
				"").toByteArray());

  if(dooble::s_settings.contains("historyWindow/tableColumnsState"))
    if(!ui.history->horizontalHeader()->restoreState
       (dooble::s_settings.value("historyWindow/tableColumnsState",
				 "").toByteArray()))
      {
	ui.history->horizontalHeader()->setSortIndicator(0,
							 Qt::AscendingOrder);
	ui.history->horizontalHeader()->setSortIndicatorShown(true);
	ui.history->horizontalHeader()->setStretchLastSection(true);
	ui.history->horizontalHeader()->setResizeMode
	  (QHeaderView::Interactive);
      }

  ui.history->horizontalHeader()->setMovable(true);
}

dhistory::~dhistory()
{
  saveState();

  if(!dooble::s_settings.value("settingsWindow/rememberHistory",
			       false).toBool())
    QFile::remove(QDir::homePath() + QDir::separator() +
		  ".dooble" + QDir::separator() + "history.db");
  else
    purge();
}

void dhistory::show(QWidget *parent)
{
  if(!isVisible())
    populate();

  m_parent = parent;
  disconnect(m_parent,
	     SIGNAL(destroyed(void)),
	     this,
	     SLOT(slotParentDestroyed(void)));
  connect(m_parent,
	  SIGNAL(destroyed(void)),
	  this,
	  SLOT(slotParentDestroyed(void)));

  if(!isVisible())
   {
     /*
     ** Don't annoy the user.
     */

     if(dooble::s_settings.contains("historyWindow/geometry"))
       setGeometry(dooble::s_settings.value("historyWindow/geometry",
					    "").toRect());
   }

  QMainWindow::showNormal();
  raise();

  if(m_timer && !m_timer->isActive())
    m_timer->start();
}

void dhistory::purge(void)
{
  if(m_timer)
    m_timer->stop();

  {
    QSqlDatabase db = QSqlDatabase::addDatabase
      ("QSQLITE", "history_window_purge_all");

    db.setDatabaseName(QDir::homePath() + QDir::separator() +
		       ".dooble" + QDir::separator() + "history.db");

    if(db.open())
      {
	QDateTime date(QDateTime::currentDateTime());
	QSqlQuery query(db);

	date = date.addDays
	  (-dooble::s_settings.value("settingsWindow/historyDays", 1).toInt());
	query.exec("PRAGMA synchronous = OFF");
	query.exec(QString("DELETE FROM history WHERE date(last_visited) <= "
			   "date('%1')").arg(date.toString(Qt::ISODate)));
      }

    db.close();
  }

  QSqlDatabase::removeDatabase("history_window_purge_all");
  statusBar()->showMessage(QString(tr("%1 item(s) - %2 selected")).
			   arg(m_model->rowCount()).
			   arg(ui.history->selectionModel()->
			       selectedRows(0).size()));

  if(m_timer)
    m_timer->start();
}

void dhistory::slotSort(int column, Qt::SortOrder order)
{
  m_model->sort(column, order);
}

void dhistory::slotSetIcons(void)
{
  QSettings settings
    (dooble::s_settings.value("iconSet").toString(), QSettings::IniFormat);

  setWindowIcon
    (QIcon(settings.value("historyWindow/windowIcon").toString()));
  ui.closePushButton->setIcon
    (QIcon(settings.value("cancelButtonIcon").toString()));
  ui.deletePushButton->setIcon
    (QIcon(settings.value("historyWindow/deleteButtonIcon").toString()));
  ui.deleteAllPushButton->setIcon
    (QIcon(settings.value("historyWindow/deleteAllButtonIcon").toString()));
  emit iconsChanged();
}

void dhistory::slotTimeout(void)
{
  if(!isVisible())
    return;

  purge();

  QFileInfo fileInfo(QDir::homePath() + QDir::separator() +
		     ".dooble" + QDir::separator() + "history.db");
  static QDateTime lastModificationTime(fileInfo.lastModified());

  if(fileInfo.exists())
    {
      if(fileInfo.lastModified() <= lastModificationTime)
	return;
      else
	lastModificationTime = fileInfo.lastModified();
    }
  else
    lastModificationTime = QDateTime::currentDateTime();

  populate();
}

void dhistory::slotDeletePage(void)
{
  /*
  ** Delete an entry from the history table.
  */

  if(m_timer)
    m_timer->stop();

  {
    QSqlDatabase db = QSqlDatabase::addDatabase
      ("QSQLITE", "history_window_delete");

    db.setDatabaseName(QDir::homePath() + QDir::separator() +
		       ".dooble" + QDir::separator() + "history.db");

    if(db.open())
      {
	int removedRows = 0;
	QSqlQuery query(db);
	QModelIndexList list(ui.history->selectionModel()->selectedRows(2));

	/*
	** Sort the list by row number. If the list is not sorted,
	** the following while-loop misbehaves.
	*/

	qSort(list);
	query.exec("PRAGMA synchronous = OFF");

	while(!list.isEmpty())
	  {
	    QStandardItem *item = m_model->item
	      (list.takeFirst().row() - removedRows, 1);

	    if(item)
	      if(query.exec(QString("DELETE FROM history "
				    "WHERE url = '%1'").arg(item->text())))
		if(m_model->removeRow(item->row()))
		   removedRows += 1;
	  }
      }

    db.close();
  }

  QSqlDatabase::removeDatabase("history_window_delete");
  statusBar()->showMessage(QString(tr("%1 item(s) - %2 selected")).
			   arg(m_model->rowCount()).
			   arg(ui.history->selectionModel()->
			       selectedRows(0).size()));

  if(m_timer)
    m_timer->start();
}

void dhistory::slotOpen(void)
{
  QStandardItem *item = 0;
  QModelIndexList list(ui.history->selectionModel()->selectedRows(2));

  item = m_model->itemFromIndex(list.takeFirst());

  if(item)
    emit open(QUrl(item->text()));

  list.clear();
}

void dhistory::slotOpenInNewTab(void)
{
  QModelIndexList list(ui.history->selectionModel()->selectedRows(2));

  while(!list.isEmpty())
    {
      QUrl url;
      QStandardItem *item = m_model->itemFromIndex(list.takeFirst());

      if(item)
	emit createTab(QUrl(item->text()));
    }
}

void dhistory::slotOpenInNewWindow(void)
{
  QStandardItem *item = 0;
  QModelIndexList list(ui.history->selectionModel()->selectedRows(2));

  item = m_model->itemFromIndex(list.takeFirst());

  if(item)
    emit openInNewWindow(QUrl(item->text()));

  list.clear();
}

void dhistory::slotShowContextMenu(const QPoint &point)
{
  if(!ui.history->selectionModel()->selectedRows().isEmpty())
    {
      QMenu menu(this);

      if(ui.history->selectionModel()->selectedRows().size() == 1 &&
	 ui.history->indexAt(point).isValid())
	{
	  menu.addAction(tr("&Delete Page"),
			 this, SLOT(slotDeletePage(void)));
	  menu.addSeparator();
	  menu.addAction(tr("Open In &Current Tab"),
			 this, SLOT(slotOpen(void)));
	  menu.addAction(tr("Open In New &Tab"),
			 this, SLOT(slotOpenInNewTab(void)));
	  menu.addAction(tr("Open In New &Window"),
			 this, SLOT(slotOpenInNewWindow(void)));
	}
      else
	{
	  menu.addAction(tr("&Delete Pages"),
			 this, SLOT(slotDeletePage(void)));
	  menu.addSeparator();
	  menu.addAction(tr("Open In &New Tabs"),
			 this, SLOT(slotOpenInNewTab(void)));
	}

      menu.exec(ui.history->mapToGlobal(point));
    }
}

void dhistory::slotItemDoubleClicked(const QModelIndex &index)
{
  QStandardItem *item1 = 0;

  item1 = m_model->itemFromIndex(index);

  if(item1)
    {
      QStandardItem *item2 = m_model->item(item1->row(), 2);

      if(item2)
	emit open(QUrl(item2->text()));
    }
}

void dhistory::slotParentDestroyed(void)
{
  /*
  ** Locate another Dooble window (parent).
  */

  foreach(QWidget *widget, QApplication::allWidgets())
    if(qobject_cast<dooble *> (widget))
      {
	m_parent = widget;
	connect(m_parent,
		SIGNAL(destroyed(void)),
		this,
		SLOT(slotParentDestroyed(void)));
	disconnect(this, SIGNAL(open(const QUrl &)),
		   m_parent, SLOT(slotLoadPage(const QUrl &)));
	disconnect(this, SIGNAL(createTab(const QUrl &)),
		   m_parent, SLOT(slotOpenLinkInNewTab(const QUrl &)));
	disconnect(this, SIGNAL(openInNewWindow(const QUrl &)),
		   m_parent, SLOT(slotOpenLinkInNewWindow(const QUrl &)));
	connect(this, SIGNAL(open(const QUrl &)),
		m_parent, SLOT(slotLoadPage(const QUrl &)));
	connect(this, SIGNAL(createTab(const QUrl &)),
		m_parent, SLOT(slotOpenLinkInNewTab(const QUrl &)));
	connect(this, SIGNAL(openInNewWindow(const QUrl &)),
		m_parent, SLOT(slotOpenLinkInNewWindow(const QUrl &)));
	break;
      }
}

void dhistory::slotDeleteAll(void)
{
  if(m_timer)
    m_timer->stop();

  {
    QSqlDatabase db = QSqlDatabase::addDatabase
      ("QSQLITE", "history_window_delete_all");

    db.setDatabaseName(QDir::homePath() + QDir::separator() +
		       ".dooble" + QDir::separator() + "history.db");

    if(db.open())
      {
	QString whereClause("");
	QSqlQuery query(db);
	QList<QTreeWidgetItem *> list(ui.treeWidget->selectedItems());

	if(!list.isEmpty())
	  {
	    QString date("");
	    QTreeWidgetItem *item = list.at(0);

	    date = item->data(0, Qt::UserRole).toString();

	    if(!date.isEmpty())
	      whereClause = QString
		("WHERE SUBSTR(last_visited, 1, %1) = '%2'").
		arg(date.length()).arg(date);
	  }

	query.exec("PRAGMA synchronous = OFF");

	if(query.exec(QString("DELETE FROM history %1").arg(whereClause)))
	  m_model->removeRows(0, m_model->rowCount());
      }

    db.close();
  }

  QSqlDatabase::removeDatabase("history_window_delete_all");
  statusBar()->showMessage(QString(tr("%1 item(s) - %2 selected")).
			   arg(m_model->rowCount()).
			   arg(ui.history->selectionModel()->
			       selectedRows(0).size()));

  if(m_timer)
    m_timer->start();
}

void dhistory::populate(void)
{
  /*
  ** Remember the user's current selection. After the table
  ** is populated, highlight approximate selections.
  */

  int hPos = ui.history->horizontalScrollBar()->value();
  int vPos = ui.history->verticalScrollBar()->value();
  QModelIndexList list(ui.history->selectionModel()->selectedRows());

  {
    QFileInfo fileInfo(QDir::homePath() + QDir::separator() +
		       ".dooble" + QDir::separator() + "history.db");
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", "history_window");

    db.setDatabaseName(fileInfo.filePath());

    if(db.open())
      {
	m_model->removeRows(0, m_model->rowCount());

	QString order("");
	QString whereClause("");
	QSqlQuery query(db);
	QList<QTreeWidgetItem *> list(ui.treeWidget->selectedItems());

	if(ui.history->horizontalHeader()->sortIndicatorOrder() ==
	   Qt::AscendingOrder)
	  order = "ASC";
	else
	  order = "DESC";

	if(!list.isEmpty())
	  {
	    QString date("");
	    QTreeWidgetItem *item = list.at(0);

	    date = item->data(0, Qt::UserRole).toString();

	    if(!date.isEmpty())
	      whereClause = QString
		("WHERE SUBSTR(last_visited, 1, %1) = '%2'").
		arg(date.length()).arg(date);
	  }

	list.clear();

	if(!ui.searchLineEdit->text().trimmed().isEmpty())
	  {
	    if(whereClause.isEmpty())
	      whereClause = QString
		("WHERE title LIKE '%%1%' OR "
		 "host LIKE '%%1%' OR "
		 "url LIKE '%%1%'").arg
		(ui.searchLineEdit->text().trimmed());
	    else
	      whereClause += QString
		(" AND (title LIKE '%%1%' OR "
		 "host LIKE '%%1%' OR "
		 "url LIKE '%%1%')").arg
		(ui.searchLineEdit->text().trimmed());
	  }

	if(query.exec(QString("SELECT title, host, url, last_visited, visits, "
			      "icon FROM "
			      "history %1 "
			      "ORDER BY %2 %3").
		      arg(whereClause).
		      arg(ui.history->horizontalHeader()->
			  sortIndicatorSection() + 1).
		      arg(order)))
	  while(query.next())
	    for(int i = 0; i < 5; i++)
	      {
		QStandardItem *item = 0;

		if(i == 3)
		  {
		    QDateTime dateTime
		      (QDateTime::fromString(query.value(i).toString(),
					     Qt::ISODate));

		    item = new QStandardItem
		      (dateTime.toString("MM/dd/yyyy hh:mm:ss AP"));

		    /*
		    ** Sorting dates is complicated enough. By
		    ** inserting dates in the below format,
		    ** we'll guarantee that the date sort will behave.
		    */

		    item->setData(dateTime.toString("yyyy/MM/dd hh:mm:ss"),
				  Qt::UserRole);
		  }
		else
		  {
		    item = new QStandardItem
		      (query.value(i).toString());

		    if(i == 4)
		      item->setData(query.value(i).toInt(),
				    Qt::UserRole);
		    else
		      item->setData(query.value(i).toString(),
				    Qt::UserRole);
		  }

		if(i == 0)
		  {
		    QIcon icon;
		    QBuffer buffer;
		    QByteArray bytes;

		    bytes = QByteArray::fromBase64
		      (query.value(query.record().count() - 1).toByteArray());
		    buffer.setBuffer(&bytes);
		    buffer.open(QIODevice::ReadOnly);

		    QDataStream in(&buffer);

		    in >> icon;
		    buffer.close();
		    item->setIcon(icon);
		    m_model->setRowCount(m_model->rowCount() + 1);
		  }

		item->setEditable(false);
		m_model->setItem(m_model->rowCount() - 1, i, item);
	      }
      }

    db.close();
  }

  QSqlDatabase::removeDatabase("history_window");
  statusBar()->showMessage(QString(tr("%1 item(s) - %2 selected")).
			   arg(m_model->rowCount()).
			   arg(ui.history->selectionModel()->
			       selectedRows(0).size()));
  ui.history->horizontalHeader()->setStretchLastSection(true);
  ui.history->setSelectionMode(QAbstractItemView::MultiSelection);

  while(!list.isEmpty())
    ui.history->selectRow(list.takeFirst().row());

  ui.history->setSelectionMode(QAbstractItemView::ExtendedSelection);
  ui.history->horizontalScrollBar()->setValue(hPos);
  ui.history->verticalScrollBar()->setValue(vPos);
}

void dhistory::slotItemSelectionChanged(void)
{
  ui.searchLineEdit->clear();
  populate();
}

void dhistory::slotTextChanged(const QString &text)
{
  Q_UNUSED(text);
  populate();
}

void dhistory::closeEvent(QCloseEvent *event)
{
  if(m_timer)
    m_timer->stop();

  saveState();
  QMainWindow::closeEvent(event);
}

void dhistory::saveState(void)
{
  if(isVisible())
    dooble::s_settings["historyWindow/geometry"] = geometry();

  dooble::s_settings["historyWindow/splitterState"] =
    ui.splitter->saveState();
  dooble::s_settings["historyWindow/tableColumnsState"] =
    ui.history->horizontalHeader()->saveState();

  QSettings settings;

  if(isVisible())
    settings.setValue("historyWindow/geometry", geometry());

  settings.setValue("historyWindow/splitterState",
		    ui.splitter->saveState());
  settings.setValue("historyWindow/tableColumnsState",
		    ui.history->horizontalHeader()->
		    saveState());
}

void dhistory::slotItemsSelected(const QItemSelection &selected,
				 const QItemSelection &deselected)
{
  Q_UNUSED(selected);
  Q_UNUSED(deselected);
  statusBar()->showMessage(QString(tr("%1 item(s) - %2 selected")).
			   arg(m_model->rowCount()).
			   arg(ui.history->selectionModel()->
			       selectedRows(0).size()));
}
