/****************************************************************************
** Dooble - The Secure Internet Web Browser
**
** Copyright (c) 2008, 2009, 2010, 2011, 2012 Alexis Megas, Bernd Stramm,
** 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 <QMenu>
#include <QMovie>
#include <QTabBar>
#include <QSettings>
#include <QDropEvent>
#include <QToolButton>

#include "dmisc.h"
#include "dooble.h"
#include "dtabwidget.h"

dtabbar::dtabbar(QWidget *parent):QTabBar(parent)
{
  setAcceptDrops(true);
  setDocumentMode(true);
  setElideMode(Qt::ElideRight);
  setExpanding(true);
  setUsesScrollButtons(true);
}

void dtabbar::dropEvent(QDropEvent *event)
{
  if(event && event->source() &&
     event->source()->objectName() == "dooble_history_sidebar" &&
     event->mimeData() && event->mimeData()->hasUrls())
    {
      int index = tabAt(event->pos());

      event->accept();

      if(index != -1)
	emit urlsReceivedViaDrop(event->mimeData()->urls());

      return;
    }

  QTabBar::dropEvent(event);
}

void dtabbar::dragMoveEvent(QDragMoveEvent *event)
{
  if(event && event->source() &&
     event->source()->objectName() == "dooble_history_sidebar" &&
     event->mimeData() && event->mimeData()->hasUrls())
    {
      int index = tabAt(event->pos());

      if(index != -1)
	setCurrentIndex(index);

      event->accept();
      return;
    }

  QTabBar::dragMoveEvent(event);
}

void dtabbar::dragEnterEvent(QDragEnterEvent *event)
{
  if(event && event->source() &&
     event->source()->objectName() == "dooble_history_sidebar" &&
     event->mimeData() && event->mimeData()->hasUrls())
    {
      int index = tabAt(event->pos());

      if(index != -1)
	setCurrentIndex(index);

      event->accept();
      return;
    }

  QTabBar::dragEnterEvent(event);
}

void dtabbar::mouseDoubleClickEvent(QMouseEvent *event)
{
  if(event && event->button() == Qt::LeftButton)
    if(tabAt(event->pos()) == -1)
      if(dooble::s_settings.value("settingsWindow/addTabWithDoubleClick",
				  true).toBool())
	emit createTab();

  QTabBar::mouseDoubleClickEvent(event);
}

QSize dtabbar::tabSizeHint(int index) const
{
  int preferredTabWidth = 200;
  QSize size(QTabBar::tabSizeHint(index));

  /*
  ** We all dream. One of my dreams is that all of the tabs have
  ** uniform widths.
  */

  if(count() * rect().width() < parentWidget()->size().width())
    preferredTabWidth = 200;
  else
    preferredTabWidth = qBound
      (100,
       qMax(size.width(), rect().width() / count()),
       200);

  size.setWidth(preferredTabWidth);
  return size;
}

dtabwidget::dtabwidget(QWidget *parent):QTabWidget(parent)
{
  if(tabBar())
    tabBar()->deleteLater();

  m_tabBar = new dtabbar(this);
  setTabBar(m_tabBar);
  m_addTabButton = new QToolButton(this);
  m_addTabButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
#ifndef Q_WS_MAC
  m_addTabButton->setAutoRaise(true);
#else
  m_addTabButton->setStyleSheet("QToolButton {"
				"border: none; "
				"background-color: transparent; "
				"border-color: transparent; "
				"padding-top: 2px; "
				"padding-bottom: 2px; "
				"}");
#endif
  m_addTabButton->setToolTip(tr("Add New Page"));
  setAcceptDrops(true);
  m_selectedTabIndex = -1;
  setStyleSheet("QTabWidget::tab-bar {"
		"alignment: left; "
		"}");
  tabBar()->setStyleSheet
    ("QTabBar::tear {"
     "image: none; "
     "}");
  tabBar()->setContextMenuPolicy(Qt::CustomContextMenu);
  tabBar()->setSelectionBehaviorOnRemove(QTabBar::SelectPreviousTab);
  connect(tabBar(), SIGNAL(customContextMenuRequested(const QPoint &)),
	  this,
	  SLOT(slotShowContextMenu(const QPoint &)));
  connect(tabBar(), SIGNAL(tabMoved(int, int)),
	  this,
	  SIGNAL(tabMoved(int, int)));
  connect(tabBar(), SIGNAL(urlsReceivedViaDrop(const QList<QUrl> &)),
	  this, SIGNAL(urlsReceivedViaDrop(const QList<QUrl> &)));
  connect(tabBar(), SIGNAL(createTab(void)),
	  this, SIGNAL(createTab(void)));
  setMovable(true);
  setTabsClosable(true);
  connect(this,
	  SIGNAL(tabCloseRequested(int)),
	  this,
	  SLOT(slotCloseTab(int)));
  connect(m_addTabButton,
	  SIGNAL(clicked(void)),
	  this,
	  SIGNAL(createTab(void)));
#ifdef Q_WS_MAC
  /*
  ** The directional arrows (left and right) have
  ** transparent backgrounds, causing the foreground
  ** of the tab beneath the buttons to seep into the
  ** foreground.
  */

  foreach(QToolButton *toolButton, findChildren
	  <QToolButton *> ())
    if(toolButton != m_addTabButton)
      toolButton->setStyleSheet
	("QToolButton {background-color: lightgray;}"
	 "QToolButton::menu-button {border: none;}");
#endif
  slotSetIcons();
}

dtabwidget::~dtabwidget()
{
}

void dtabwidget::slotShowContextMenu(const QPoint &point)
{
  m_selectedTabIndex = tabBar()->tabAt(point);

  if(m_selectedTabIndex > -1)
    {
      if(qobject_cast<dview *> (widget(m_selectedTabIndex)))
	{
	  QMenu menu(this);
	  QAction *action = 0;

	  menu.addAction(tr("&Bookmark"),
			 this, SLOT(slotBookmark(void)));
	  menu.addSeparator();
	  action = menu.addAction(tr("Close Ta&b"),
				  this, SLOT(slotCloseTab(void)));
	  action->setEnabled(count() != 1);
	  action = menu.addAction(tr("Close &Other Tabs"),
				  this, SLOT(slotCloseOtherTabs(void)));
	  action->setEnabled(count() > 1);
	  menu.addSeparator();
	  menu.addAction(tr("New &Tab"),
			 this, SLOT(slotCreateTab(void)));
	  action = menu.addAction(tr("Open in &New Window"),
				  this, SLOT(slotOpenInNewWindow(void)));
	  action->setEnabled(count() > 1);
	  menu.addSeparator();
	  menu.addAction(tr("Reload &Tab"),
			 this, SLOT(slotReloadTab(void)));
	  menu.exec(tabBar()->mapToGlobal(point));
	}
      else
	{
	  QMenu menu(this);
	  QAction *action = 0;

	  action = menu.addAction(tr("Close Ta&b"),
				  this, SLOT(slotCloseTab(void)));
	  action->setEnabled(count() != 1);
	  action = menu.addAction(tr("Close &Other Tabs"),
				  this, SLOT(slotCloseOtherTabs(void)));
	  action->setEnabled(count() > 1);
	  menu.addSeparator();
	  menu.addAction(tr("New &Tab"),
			 this, SLOT(slotCreateTab(void)));
	  menu.exec(tabBar()->mapToGlobal(point));
	}
    }
}

void dtabwidget::slotCloseTab(int index)
{
  if(index >= 0 && index < count())
    emit closeTab(index);
}

void dtabwidget::slotCloseTab(void)
{
  emit closeTab(m_selectedTabIndex);
  m_selectedTabIndex = -1;
}

void dtabwidget::slotCreateTab(void)
{
  emit createTab();
}

void dtabwidget::slotOpenInNewWindow(void)
{
  emit openInNewWindow(m_selectedTabIndex);
  m_selectedTabIndex = -1;
}

void dtabwidget::setBarVisible(const bool state)
{
  m_addTabButton->setVisible(state);
  m_tabBar->setVisible(state);
  positionAddTabButton();
}

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

  settings.beginGroup("mainWindow");
  m_addTabButton->setIcon(QIcon(settings.value("addTab").toString()));
}

void dtabwidget::mousePressEvent(QMouseEvent *event)
{
  if(event && event->button() == Qt::MidButton)
    {
      if(!dooble::s_settings.value("settingsWindow/closeViaMiddle",
				   true).toBool())
	return;

      int index = tabBar()->tabAt(event->pos());

      if(index != -1)
	emit closeTab(index);
    }

  QTabWidget::mousePressEvent(event);
}

void dtabwidget::slotBookmark(void)
{
  int index = m_selectedTabIndex;

  if(index == -1)
    return;

  emit bookmark(index);
}

void dtabwidget::slotCloseOtherTabs(void)
{
  int index = m_selectedTabIndex;

  if(index == -1)
    return;

  /*
  ** The slot that's connected to the closeTab() signal issues
  ** deleteLater() calls. Timing should not be a concern.
  */

  for(int i = count() - 1; i > index; i--)
    emit closeTab(i);

  for(int i = index - 1; i >= 0; i--)
    emit closeTab(i);

  m_selectedTabIndex = -1;
}

void dtabwidget::setTabButton(int index)
{
  QTabBar::ButtonPosition side = (QTabBar::ButtonPosition) style()->styleHint
    (QStyle::SH_TabBar_CloseButtonPosition, 0, tabBar());

  side = (side == QTabBar::LeftSide) ? QTabBar::RightSide : QTabBar::LeftSide;

  QLabel *label = qobject_cast<QLabel *> (tabBar()->tabButton(index, side));

  if(!label)
    {
      label = new QLabel(tabBar());

      QPixmap pixmap(16, 16);

      pixmap.fill(tabBar()->backgroundRole());
      label->setPixmap(pixmap);
    }

  tabBar()->setTabButton(index, side, 0);
  tabBar()->setTabButton(index, side, label);
}

void dtabwidget::animateIndex(const int index, const bool state,
			      const QIcon &icon)
{
  QTabBar::ButtonPosition side = (QTabBar::ButtonPosition) style()->styleHint
    (QStyle::SH_TabBar_CloseButtonPosition, 0, tabBar());

  side = (side == QTabBar::LeftSide) ? QTabBar::RightSide : QTabBar::LeftSide;

  QLabel *label = qobject_cast<QLabel*>
    (tabBar()->tabButton(index, side));

  if(label)
    {
      if(state)
	{
	  QMovie *movie = label->movie();

	  if(!movie)
	    {
	      movie = new QMovie
		("Icons/AxB/spinning_wheel.gif", QByteArray(), label);
	      movie->setScaledSize(QSize(16, 16));
	      label->setMovie(movie);
	      movie->start();
	    }
	  else
	    {
	      if(movie->state() != QMovie::Running)
		movie->start();
	    }
	}
      else
	{
	  if(label->movie())
	    {
	      label->movie()->stop();
	      label->movie()->deleteLater();
	      label->setMovie(0);
	    }

	  label->setPixmap(icon.pixmap(16, 16));
	}
    }
}

void dtabwidget::dropEvent(QDropEvent *event)
{
  if(event && event->source() &&
     event->source()->objectName() == "dooble_history_sidebar" &&
     event->mimeData() && event->mimeData()->hasUrls())
    {
      QList<QUrl> list(event->mimeData()->urls());

      while(!list.isEmpty())
	emit openLinkInNewTab(list.takeFirst());

      return;
    }

  QTabWidget::dropEvent(event);
}

void dtabwidget::dragMoveEvent(QDragMoveEvent *event)
{
  if(event && event->source() &&
     event->source()->objectName() == "dooble_history_sidebar" &&
     event->mimeData() && event->mimeData()->hasUrls())
    {
      event->accept();
      return;
    }

  QTabWidget::dragMoveEvent(event);
}

void dtabwidget::dragEnterEvent(QDragEnterEvent *event)
{
  if(event && event->source() &&
     event->source()->objectName() == "dooble_history_sidebar" &&
     event->mimeData() && event->mimeData()->hasUrls())
    {
      event->accept();
      return;
    }

  QTabWidget::dragEnterEvent(event);
}

void dtabwidget::slotIconChange(QWidget *tab, const QIcon &icon)
{
  int tabno = indexOf(tab);

  if(tabno >= 0)
    {
      setTabIcon(tabno, icon);
    }
}

void dtabwidget::slotTitleChange(QWidget *tab, const QString &title)
{
  int tabno = indexOf(tab);

  if(tabno >= 0)
    {
      setTabText(tabno, title);
      setTabToolTip(tabno, title);
    }
}

void dtabwidget::tabRemoved(int index)
{
  Q_UNUSED(index);
  qint64 id = -1;
  dooble *dbl = 0;
  QObject *prnt(this);

  do
    {
      prnt = prnt->parent();
      dbl = qobject_cast<dooble *> (prnt);
    }
  while(prnt != 0 && dbl == 0);

  if(dbl)
    id = dbl->id();

  if(id > -1)
    {
      dmisc::removeRestorationFiles(dooble::s_id, id);

      for(int i = 0; i < count(); i++)
	{
	  dview *p = qobject_cast<dview *> (widget(i));

	  if(p)
	    p->recordRestorationHistory();
	}
    }

  positionAddTabButton();
}

#ifndef Q_WS_MAC
void dtabwidget::mouseDoubleClickEvent(QMouseEvent *event)
{
  if(event && event->button() == Qt::LeftButton)
    if(tabBar()->tabAt(event->pos()) == -1)
      {
	QRect rect(0, 0, width(), tabBar()->size().height());

	if(rect.contains(event->pos()))
	  if(dooble::s_settings.value("settingsWindow/addTabWithDoubleClick",
				      true).toBool())
	    emit createTab();
      }

  QTabWidget::mouseDoubleClickEvent(event);
}
#endif

void dtabwidget::slotReloadTab(void)
{
  emit reloadTab(m_selectedTabIndex);
}

void dtabwidget::tabInserted(int index)
{
  positionAddTabButton();
  QTabWidget::tabInserted(index);
}

void dtabwidget::positionAddTabButton(void)
{
  m_addTabButton->raise();

  if(count() > 0 && size().width() > 0)
    {
      int x = 0;

      for(int i = 0; i < count(); i++)
	x += m_tabBar->tabSizeHint(i).width();

      bool cornerWidgetSet = cornerWidget() == 0 ? false : true;

      if(m_tabBar->isVisible() &&
	 x + m_addTabButton->width() >= size().width() -
	 m_addTabButton->width())
	{
	  setCornerWidget(m_addTabButton);

#ifndef Q_WS_MAC
	  if(dmisc::isKDE())
	    m_addTabButton->setStyleSheet
	      ("QToolButton {padding-bottom: 5px;}");
#else
	  m_addTabButton->setStyleSheet
	    ("QToolButton {background-color: lightgray;}");
#endif
	  m_addTabButton->setVisible(m_tabBar->isVisible());
	}
      else
	{
	  setCornerWidget(0);
	  m_addTabButton->move(x + 1, 0);
#ifdef Q_WS_MAC
	  m_addTabButton->setStyleSheet("QToolButton {"
					"border: none; "
					"background-color: transparent; "
					"border-color: transparent; "
					"padding-top: 2px; "
					"padding-bottom: 2px; "
					"}");
#endif
	}

      if(!dmisc::isKDE())
	{
	  m_addTabButton->resize(m_addTabButton->sizeHint().width(),
				 m_tabBar->sizeHint().height() - 1);
	  m_addTabButton->resize(m_addTabButton->width(),
				 m_tabBar->sizeHint().height() - 1);
	}

      if(cornerWidgetSet && !cornerWidget())
	m_addTabButton->setVisible(m_tabBar->isVisible());
    }
}

void dtabwidget::resizeEvent(QResizeEvent *event)
{
  positionAddTabButton();
  QTabWidget::resizeEvent(event);
}
