view demos/browser/tabwidget.d @ 73:7bfd46c330dc

more porting
author mandel
date Fri, 22 May 2009 10:59:00 +0000
parents 71b382c10ef6
children 37caa90ce503
line wrap: on
line source

/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** Contact: Qt Software Information (qt-info@nokia.com)
**
** This file is part of the demonstration applications of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial Usage
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain
** additional rights. These rights are described in the Nokia Qt LGPL
** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
** package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at qt-sales@nokia.com.
** $QT_END_LICENSE$
**
****************************************************************************/

module tabwidget;

import qt.gui.QTabBar;
import qt.gui.QShortcut;
import qt.gui.QClipboard;
import qt.gui.QCompleter;
import qt.gui.QListView;
import qt.gui.QMenu;
import qt.gui.QMessageBox;
import qt.gui.QMouseEvent;
import qt.gui.QStackedWidget;
import qt.gui.QStyle;
import qt.gui.QToolButton;

import qt.core.QDebug;

import browserapplication;
import browsermainwindow;
import history;
import urllineedit;
import webview;

/*
Tab bar with a few more features such as a context menu and shortcuts
 */
class TabBar : public QTabBar
{
	mixin Signal!("newTab");
	mixin Signal!("cloneTab", int index);
	mixin Signal!("closeTab", int index);
	mixin Signal!("closeOtherTabs", int index);
	mixin Signal!("reloadTab", int index);
	mixin Signal!("reloadAllTabs");
	mixin Signal!("tabMoveRequested", int fromIndex, int toIndex);

public:

	this(QWidget parent = null)
	{
		super(parent);
		setContextMenuPolicy(Qt.CustomContextMenu);
		setAcceptDrops(true);
		this.customContextMenuRequested.connect(&this.contextMenuRequested);

		QString alt = QLatin1String("Alt+%1");
		for (int i = 1; i <= 10; ++i) {
			int key = i;
			if (key == 10)
				key = 0;
			QShortcut shortCut = new QShortcut(alt.arg(key), this);
			m_tabShortcuts.append(shortCut);
			shortCut.activated.connect(&this.selectTabAction);
		}
		setTabsClosable(true);
		this.tabCloseRequested.connect(&this.closeTab);
		setSelectionBehaviorOnRemove(QTabBar.SelectPreviousTab);
		setMovable(true);
	}

protected:

	void mousePressEvent(QMouseEvent event)
	{
		if (event.button() == Qt.LeftButton)
			m_dragStartPos = event.pos();
		QTabBar.mousePressEvent(event);
	}

	void mouseMoveEvent(QMouseEvent event)
	{
		if (event.buttons() == Qt.LeftButton) {
			int diffX = event.pos().x() - m_dragStartPos.x();
			int diffY = event.pos().y() - m_dragStartPos.y();
			if ((event.pos() - m_dragStartPos).manhattanLength() > QApplication.startDragDistance() && diffX < 3 && diffX > -3 && diffY < -10) {
				QDrag drag = new QDrag(this);
				QMimeData mimeData = new QMimeData;
				QList<QUrl> urls;
				int index = tabAt(event.pos());
				QUrl url = tabData(index).toUrl();
				urls.append(url);
				mimeData.setUrls(urls);
				mimeData.setText(tabText(index));
				mimeData.setData(QLatin1String("action"), "tab-reordering");
				drag.setMimeData(mimeData);
				drag.exec();
			}
		}
		QTabBar.mouseMoveEvent(event);
	}

private:

	void selectTabAction()
	{
		if (QShortcut shortCut = qobject_cast<QShortcut*>(sender())) {
			int index = m_tabShortcuts.indexOf(shortCut);
			if (index == 0)
				index = 10;
			setCurrentIndex(index);
		}
	}

	void cloneTab();
	{
		if (QAction action = qobject_cast<QAction>(sender())) {
			int index = action.data().toInt();
			emit cloneTab(index);
		}
	}

	void closeTab();
	{
		if (QAction action = qobject_cast<QAction>(sender())) {
			int index = action.data().toInt();
			emit closeTab(index);
		}
	}

	void closeOtherTabs()
	{
		if (QAction action = qobject_cast<QAction>(sender())) {
			int index = action.data().toInt();
			emit closeOtherTabs(index);
		}
	}

	void reloadTab()
	{
		if (QAction action = qobject_cast<QAction>(sender())) {
			int index = action.data().toInt();
			emit reloadTab(index);
		}
	}

	void contextMenuRequested(QPoint position)
	{
		QMenu menu;
		menu.addAction(tr("New &Tab"), this, SIGNAL(newTab()), QKeySequence.AddTab);
		int index = tabAt(position);
		if (-1 != index) {
			QAction action = menu.addAction(tr("Clone Tab"), this, SLOT(cloneTab()));
			action.setData(index);

			menu.addSeparator();

			action = menu.addAction(tr("&Close Tab"), this, SLOT(closeTab()), QKeySequence.Close);
			action.setData(index);

			action = menu.addAction(tr("Close &Other Tabs"), this, SLOT(closeOtherTabs()));
			action.setData(index);

			menu.addSeparator();

			action = menu.addAction(tr("Reload Tab"), this, SLOT(reloadTab()), QKeySequence.Refresh);
			action.setData(index);
		} else {
			menu.addSeparator();
		}
		menu.addAction(tr("Reload All Tabs"), this, SIGNAL(reloadAllTabs()));
		menu.exec(QCursor.pos());
	}

	private:
	QList<QShortcut> m_tabShortcuts;
	friend class TabWidget;

	QPoint m_dragStartPos;
	int m_dragCurrentIndex;
}

import QtWebKit.QWebPage;

/*!
A proxy object that connects a single browser action
to one child webpage action at a time.

Example usage: used to keep the main window stop action in sync with
the current tabs webview's stop action.
*/
class WebActionMapper : public QObject
{
public:

	this(QAction root, QWebPage.WebAction webAction, QObject parent)
	{
		super(parent);	
		m_currentParent = 0;
		m_root = root;
		m_webAction = webAction;
		if (!m_root)
			return;
		m_root.triggered.connect(&this.rootTriggered);
		root.destroyed.connect(&this.rootDestroyed);
		root.setEnabled(false);
	}

	QWebPage.WebAction webAction() const
	{
		return m_webAction;
	}

	void addChild(QAction action)
	{
		if (!action)
			return;
		action.changed.connect(&this.childChanged);
	}

	void updateCurrent(QWebPage currentParent)
	{
		if (m_currentParent)
			m_currentParent.destroyed.disconnect(&this.currentDestroyed);

		m_currentParent = currentParent;
		if (!m_root)
			return;
		if (!m_currentParent) {
			m_root.setEnabled(false);
			m_root.setChecked(false);
			return;
		}
		QAction source = m_currentParent.action(m_webAction);
		m_root.setChecked(source.isChecked());
		m_root.setEnabled(source.isEnabled());
		m_currentParent.destroyed.connect(&this.currentDestroyed);
	}

private:

	void rootTriggered()
	{
		if (m_currentParent) {
			QAction gotoAction = m_currentParent.action(m_webAction);
			gotoAction.trigger();
		}
	}

	void childChanged()
	{
		if (QAction source = qobject_cast<QAction>(sender())) {
			if (m_root && m_currentParent && source.parent() == m_currentParent) {
					m_root.setChecked(source.isChecked());
					m_root.setEnabled(source.isEnabled());
			}
		}
	}

	void rootDestroyed()
	{
		m_root = 0;
	}
	
	void currentDestroyed()
	{
		updateCurrent(0);
	}

private:

	QWebPage m_currentParent;
	QAction m_root;
	QWebPage.WebAction m_webAction;
}


import qt.core.QUrl;
import qt.gui.QTabWidget;


/*!
TabWidget that contains WebViews and a stack widget of associated line edits.

Connects up the current tab's signals to this class's signal and uses WebActionMapper
to proxy the actions.
*/
class TabWidget : public QTabWidget
{
	// tab widget signals
	mixin Singal!("loadPage", QString url);
	mixin Singal!("tabsChanged");
	mixin Singal!("lastTabClosed");

	// current tab signals
	mixin Singal!("setCurrentTitle", QString url);
	mixin Singal!("showStatusBarMessage", QString message);
	mixin Singal!("linkHovered", QString link);
	mixin Singal!("loadProgress", int progress);
	mixin Singal!("geometryChangeRequested", QRect geometry);
	mixin Singal!("menuBarVisibilityChangeRequested", bool visible);
	mixin Singal!("statusBarVisibilityChangeRequested", bool visible);
	mixin Singal!("toolBarVisibilityChangeRequested", bool visible);
	mixin Singal!("printRequested", QWebFrame frame);

public:

	this(QWidget parent = null)
	{
		QTabWidget(parent)
		m_recentlyClosedTabsAction = 0;
		m_newTabAction = 0;
		m_closeTabAction = 0;
		m_nextTabAction = 0;
		m_previousTabAction = 0;
		m_recentlyClosedTabsMenu = 0;
		m_lineEditCompleter = 0;
		m_lineEdits = 0;
		m_tabBar = new TabBar(this);

		setElideMode(Qt.ElideRight);

		m_tabBar.newTab.connect(&this.newTab);
		m_tabBar.closeTab.connect(&this.closeTab(int)));
		m_tabBar.cloneTab.connect(&this.cloneTab(int)));
		m_tabBar.closeOtherTabs.connect(&this.closeOtherTabs(int)));
		m_tabBar.reloadTab.connect(&this.reloadTab(int)));
		m_tabBar.reloadAllTabs.connect(&this.reloadAllTabs()));
		m_tabBar.tabMoved.connect(&this.moveTab(int, int)));
		setTabBar(m_tabBar);
		setDocumentMode(true);

		// Actions
		m_newTabAction = new QAction(QIcon(QLatin1String(":addtab.png")), tr("New &Tab"), this);
		m_newTabAction.setShortcuts(QKeySequence.AddTab);
		m_newTabAction.setIconVisibleInMenu(false);
		m_newTabAction.triggered.connect(&this.newTab);

		m_closeTabAction = new QAction(QIcon(QLatin1String(":closetab.png")), tr("&Close Tab"), this);
		m_closeTabAction.setShortcuts(QKeySequence.Close);
		m_closeTabAction.setIconVisibleInMenu(false);
		m_closeTabAction.triggered.connect(&this.closeTab);

		m_nextTabAction = new QAction(tr("Show Next Tab"), this);
		QList<QKeySequence> shortcuts;
		shortcuts.append(QKeySequence(Qt.CTRL | Qt.Key_BraceRight));
		shortcuts.append(QKeySequence(Qt.CTRL | Qt.Key_PageDown));
		shortcuts.append(QKeySequence(Qt.CTRL | Qt.Key_BracketRight));
		shortcuts.append(QKeySequence(Qt.CTRL | Qt.Key_Less));
		m_nextTabAction.setShortcuts(shortcuts);
		m_nextTabAction.triggered.connect(&this.nextTab);

		m_previousTabAction = new QAction(tr("Show Previous Tab"), this);
		shortcuts.clear();
		shortcuts.append(QKeySequence(Qt.CTRL | Qt.Key_BraceLeft));
		shortcuts.append(QKeySequence(Qt.CTRL | Qt.Key_PageUp));
		shortcuts.append(QKeySequence(Qt.CTRL | Qt.Key_BracketLeft));
		shortcuts.append(QKeySequence(Qt.CTRL | Qt.Key_Greater));
		m_previousTabAction.setShortcuts(shortcuts);
		m_previousTabAction.triggered.connect(&this.previousTab);

		m_recentlyClosedTabsMenu = new QMenu(this);
		m_recentlyClosedTabsMenu.aboutToShow.connect(&this.aboutToShowRecentTabsMenu);
		m_recentlyClosedTabsMenu.triggered.connect(&this.aboutToShowRecentTriggeredAction);
		m_recentlyClosedTabsAction = new QAction(tr("Recently Closed Tabs"), this);
		m_recentlyClosedTabsAction.setMenu(m_recentlyClosedTabsMenu);
		m_recentlyClosedTabsAction.setEnabled(false);

		this.currentChanged.connect(&this.currentChanged);

		m_lineEdits = new QStackedWidget(this);
	}

	void clear()
	{
		// clear the recently closed tabs
		m_recentlyClosedTabs.clear();
		// clear the line edit history
		for (int i = 0; i < m_lineEdits.count(); ++i) {
			QLineEdit qLineEdit = lineEdit(i);
			qLineEdit.setText(qLineEdit.text());
		}
	}

	void addWebAction(QAction action, QWebPage.WebAction webAction)
	{
		if (!action)
			return;
		m_actions.append(new WebActionMapper(action, webAction, this));
	}

	QAction newTabAction() const;
	{
		return m_newTabAction;
	}

	QAction closeTabAction() const;
	{
		return m_closeTabAction;
	}
	
	QAction recentlyClosedTabsAction() const;
	{
		return m_recentlyClosedTabsAction;
	}

	QAction nextTabAction() const
	{
		return m_nextTabAction;
	}

	QAction previousTabAction() const
	{
		return m_previousTabAction;
	}

	QWidget lineEditStack() const
	{
		return m_lineEdits;
	}

	QLineEdit currentLineEdit() const
	{
		return lineEdit(m_lineEdits.currentIndex());
	}

	WebView currentWebView() const
	{
		return webView(currentIndex());
	}

	WebView webView(int index) const
	{
		QWidget widget = this.widget(index);
		if (WebView webView = qobject_cast<WebView>(widget)) {
			return webView;
		} else {
			// optimization to delay creating the first webview
			if (count() == 1) {
				TabWidget that = const_cast<TabWidget>(this);
				that.setUpdatesEnabled(false);
				that.newTab();
				that.closeTab(0);
				that.setUpdatesEnabled(true);
				return currentWebView();
			}
		}
		return 0;
	}

	QLineEdit lineEdit(int index) const
	{
		UrlLineEdit urlLineEdit = qobject_cast<UrlLineEdit>(m_lineEdits.widget(index));
		if (urlLineEdit)
			return urlLineEdit.lineEdit();
		return 0;
	}

	int webViewIndex(WebView webView) const
	{
		int index = indexOf(webView);
		return index;
	}

    
static const qint32 TabWidgetMagic = 0xaa;

	QByteArray saveState() const;
	{
		int version = 1;
		QByteArray data;
		QDataStream stream(data, QIODevice.WriteOnly);

		stream << qint32(TabWidgetMagic);
		stream << qint32(version);

		QStringList tabs;
		for (int i = 0; i < count(); ++i) {
			if (WebView tab = qobject_cast<WebView>(widget(i))) {
				tabs.append(tab.url().toString());
			} else {
				tabs.append(QString.null);
			}
		}
		stream << tabs;
		stream << currentIndex();
		return data;
	}

	bool restoreState(QByteArray state)
	{
		int version_ = 1;
		QByteArray sd = state;
		QDataStream stream(sd, QIODevice.ReadOnly);
		if (stream.atEnd())
			return false;

		qint32 marker;
		qint32 v;
		stream >> marker;
		stream >> v;
		if (marker != TabWidgetMagic || v != version_)
			return false;

		QStringList openTabs;
		stream >> openTabs;

		for (int i = 0; i < openTabs.count(); ++i) {
			if (i != 0)
				newTab();
			loadPage(openTabs.at(i));
		}

		int currentTab;
		stream >> currentTab;
		setCurrentIndex(currentTab);

		return true;
	}

protected:

	void mouseDoubleClickEvent(QMouseEvent event);
	{
		if (!childAt(event.pos())
			// Remove the line below when QTabWidget does not have a one pixel frame
			&& event.pos().y() < (tabBar().y() + tabBar().height())) {
			newTab();
			return;
		}
		QTabWidget.mouseDoubleClickEvent(event);
	}


	void contextMenuEvent(QContextMenuEvent event)
	{
		if (!childAt(event.pos())) {
			m_tabBar.contextMenuRequested(event.pos());
			return;
		}
		QTabWidget.contextMenuEvent(event);
	}

	void mouseReleaseEvent(QMouseEvent event)
	{
		if (event.button() == Qt.MidButton && !childAt(event.pos())
		// Remove the line below when QTabWidget does not have a one pixel frame
		&& event.pos().y() < (tabBar().y() + tabBar().height())) {
			QUrl url(QApplication.clipboard().text(QClipboard.Selection));
			if (!url.isEmpty() && url.isValid() && !url.scheme().isEmpty()) {
				WebView webView = newTab();
				webView.setUrl(url);
			}
		}
	}

public:

	void loadUrlInCurrentTab(QUrl url)
	{
		WebView webView = currentWebView();
		if (webView) {
			webView.loadUrl(url);
			webView.setFocus();
		}
	}

	WebView newTab(bool makeCurrent = true)
	{
		// line edit
		UrlLineEdit urlLineEdit = new UrlLineEdit;
		QLineEdit lineEdit = urlLineEdit.lineEdit();
		if (!m_lineEditCompleter && count() > 0) {
			HistoryCompletionModel completionModel = new HistoryCompletionModel(this);
			completionModel.setSourceModel(BrowserApplication.historyManager().historyFilterModel());
			m_lineEditCompleter = new QCompleter(completionModel, this);
			// Should this be in Qt by default?
			QAbstractItemView popup = m_lineEditCompleter.popup();
			QListView listView = qobject_cast<QListView*>(popup);
			if (listView)
				listView.setUniformItemSizes(true);
		}
		lineEdit.setCompleter(m_lineEditCompleter);
		lineEdit.returnPressed.connect(&this.lineEditReturnPressed);
		m_lineEdits.addWidget(urlLineEdit);
		m_lineEdits.setSizePolicy(lineEdit.sizePolicy());

		// optimization to delay creating the more expensive WebView, history, etc
		if (count() == 0) {
			QWidget emptyWidget = new QWidget;
			QPalette p = emptyWidget.palette();
			p.setColor(QPalette.Window, palette().color(QPalette.Base));
			emptyWidget.setPalette(p);
			emptyWidget.setAutoFillBackground(true);
			this.currentChanged.disconnect(&this.currentChanged);
			addTab(emptyWidget, tr("(Untitled)"));
			this.currentChanged.connect(&this.currentChanged);
			return 0;
		}

		// webview
		WebView webView = new WebView;
		urlLineEdit.setWebView(webView);
		webView.loadStarted().connect(&this.webViewLoadStarted);
		webView.loadFinished.connect(&this.webViewIconChanged);
		webView.iconChanged.connect(&this.webViewIconChanged);
		webView.titleChanged.connect(&this.webViewTitleChanged);
		webView.urlChanged.connect(&this.webViewUrlChanged);
		webView.page().windowCloseRequested.connect(&this.windowCloseRequested);
		webView.page().geometryChangeRequested.connect(&this.geometryChangeRequested);
		webView.page().printRequested.connect(&this.printRequested);
		webView.page().menuBarVisibilityChangeRequested.connect(&this.menuBarVisibilityChangeRequested);
		webView.page().statusBarVisibilityChangeRequested.connect(&this.statusBarVisibilityChangeRequested);
		webView.page().toolBarVisibilityChangeRequested.connect(&this.toolBarVisibilityChangeRequested);
		addTab(webView, tr("(Untitled)"));
		if (makeCurrent)
			setCurrentWidget(webView);

		// webview actions
		for (int i = 0; i < m_actions.count(); ++i) {
			WebActionMapper mapper = m_actions[i];
			mapper.addChild(webView.page().action(mapper.webAction()));
		}

		if (count() == 1)
			currentChanged(currentIndex());
		emit tabsChanged();
		return webView;
	}

	// When index is -1 index chooses the current tab
	void cloneTab(int index = -1)
	{
		if (index < 0)
			index = currentIndex();
		if (index < 0 || index >= count())
			return;
		WebView tab = newTab(false);
		tab.setUrl(webView(index).url());
	}

	// When index is -1 index chooses the current tab
	void closeTab(int index = -1);
	{
		if (index < 0)
			index = currentIndex();
		if (index < 0 || index >= count())
			return;

		bool hasFocus = false;
		if (WebView tab = webView(index)) {
			if (tab.isModified()) {
				QMessageBox closeConfirmation(tab);
				closeConfirmation.setWindowFlags(Qt.Sheet);
				closeConfirmation.setWindowTitle(tr("Do you really want to close this page?"));
				closeConfirmation.setInformativeText(tr("You have modified this page and when closing it you would lose the modification.\n"
							     "Do you really want to close this page?\n"));
				closeConfirmation.setIcon(QMessageBox.Question);
				closeConfirmation.addButton(QMessageBox.Yes);
				closeConfirmation.addButton(QMessageBox.No);
				closeConfirmation.setEscapeButton(QMessageBox.No);
				if (closeConfirmation.exec() == QMessageBox.No)
				return;
			}
			hasFocus = tab.hasFocus();

			m_recentlyClosedTabsAction.setEnabled(true);
			m_recentlyClosedTabs.prepend(tab.url());
			if (m_recentlyClosedTabs.size() >= TabWidget.m_recentlyClosedTabsSize)
				m_recentlyClosedTabs.removeLast();
		}
		QWidget lineEdit = m_lineEdits.widget(index);
		m_lineEdits.removeWidget(lineEdit);
		lineEdit.deleteLater();
		QWidget webView = widget(index);
		removeTab(index);
		webView.deleteLater();
		emit tabsChanged();
		if (hasFocus && count() > 0)
			currentWebView().setFocus();
		if (count() == 0)
			emit lastTabClosed();
	}

	void closeOtherTabs(int index)
	{
		if (-1 == index)
			return;
		for (int i = count() - 1; i > index; --i)
			closeTab(i);
		for (int i = index - 1; i >= 0; --i)
			closeTab(i);
	}

	// When index is -1 index chooses the current tab
	void reloadTab(int index = -1);
	{
		if (index < 0)
			index = currentIndex();
		if (index < 0 || index >= count())
			return;

		QWidget widget = this.widget(index);
		if (WebView tab = qobject_cast<WebView>(widget))
			tab.reload();
	}

	void reloadAllTabs();
	{
		for (int i = 0; i < count(); ++i) {
			QWidget tabWidget = widget(i);
			if (WebView tab = qobject_cast<WebView>(tabWidget)) {
				tab.reload();
			}
		}
	}
	
	void nextTab()
	{
		int next = currentIndex() + 1;
		if (next == count())
			next = 0;
		setCurrentIndex(next);
	}

	void previousTab()
	{
		int next = currentIndex() - 1;
		if (next < 0)
			next = count() - 1;
		setCurrentIndex(next);
	}

private:

	void currentChanged(int index)
	{
		WebView webView = this.webView(index);
		if (!webView)
			return;

		assert(m_lineEdits.count() == count());

		WebView oldWebView = this.webView(m_lineEdits.currentIndex());
		if (oldWebView) {
			oldWebView.statusBarMessage.disconnect(&this.showStatusBarMessage);
			oldWebView.page().linkHovered.disconnect(&this.linkHovered);
			oldWebView.loadProgress().disconnect(&this.loadProgress);
		}

		webView.statusBarMessage.connect(&this.showStatusBarMessage);
		webView.page().linkHovered.connect(&this.linkHovered);
		webView.loadProgress.connect(&this.loadProgress);

		for (int i = 0; i < m_actions.count(); ++i) {
			WebActionMapper mapper = m_actions[i];
			mapper.updateCurrent(webView.page());
		}
		emit setCurrentTitle(webView.title());
		m_lineEdits.setCurrentIndex(index);
		emit loadProgress(webView.progress());
		emit showStatusBarMessage(webView.lastStatusBarText());
		if (webView.url().isEmpty())
			m_lineEdits.currentWidget().setFocus();
		else
			webView.setFocus();
	}

	void aboutToShowRecentTabsMenu()
	{
		m_recentlyClosedTabsMenu.clear();
		for (int i = 0; i < m_recentlyClosedTabs.count(); ++i) {
			QAction action = new QAction(m_recentlyClosedTabsMenu);
			action.setData(m_recentlyClosedTabs.at(i));
			QIcon icon = BrowserApplication.instance().icon(m_recentlyClosedTabs.at(i));
			action.setIcon(icon);
			action.setText(m_recentlyClosedTabs.at(i).toString());
			m_recentlyClosedTabsMenu.addAction(action);
		}
	}

	void aboutToShowRecentTriggeredAction(QAction action)
	{
		QUrl url = action.data().toUrl();
		loadUrlInCurrentTab(url);
	}

	void webViewLoadStarted()
	{
		WebView webView = qobject_cast<WebView>(sender());
		int index = webViewIndex(webView);
		if (-1 != index) {
			QIcon icon(QLatin1String(":loading.gif"));
			setTabIcon(index, icon);
		}
	}

	void webViewIconChanged()
	{
		WebView webView = qobject_cast<WebView>(sender());
		int index = webViewIndex(webView);
		if (-1 != index) {
			QIcon icon = BrowserApplication.instance().icon(webView.url());
			setTabIcon(index, icon);
		}
	}

	void webViewTitleChanged(QString title)
	{
		WebView webView = qobject_cast<WebView>(sender());
		int index = webViewIndex(webView);
		if (-1 != index) {
			setTabText(index, title);
		}
		if (currentIndex() == index)
			emit setCurrentTitle(title);
		BrowserApplication.historyManager().updateHistoryItem(webView.url(), title);
	}

	void webViewUrlChanged(QUrl url)
	{
		WebView webView = qobject_cast<WebView>(sender());
		int index = webViewIndex(webView);
		if (-1 != index) {
			m_tabBar.setTabData(index, url);
		}
		emit tabsChanged();
	}

	void lineEditReturnPressed()
	{
		if (QLineEdit lineEdit = qobject_cast<QLineEdit*>(sender())) {
			emit loadPage(lineEdit.text());
			if (m_lineEdits.currentWidget() == lineEdit)
				currentWebView().setFocus();
		}
	}

	void windowCloseRequested()
	{
		WebPage webPage = qobject_cast<WebPage>(sender());
		WebView webView = qobject_cast<WebView>(webPage.view());
		int index = webViewIndex(webView);
		if (index >= 0) {
			if (count() == 1)
				webView.webPage().mainWindow().close();
			else
				closeTab(index);
		}
	}

	void moveTab(int fromIndex, int toIndex)
	{
		QWidget lineEdit = m_lineEdits.widget(fromIndex);
		m_lineEdits.removeWidget(lineEdit);
		m_lineEdits.insertWidget(toIndex, lineEdit);
	}

private:

	QAction m_recentlyClosedTabsAction;
	QAction m_newTabAction;
	QAction m_closeTabAction;
	QAction m_nextTabAction;
	QAction m_previousTabAction;

	QMenu m_recentlyClosedTabsMenu;
	static const int m_recentlyClosedTabsSize = 10;
	QList<QUrl> m_recentlyClosedTabs;
	QList<WebActionMapper> m_actions;

	QCompleter m_lineEditCompleter;
	QStackedWidget m_lineEdits;
	TabBar m_tabBar;
}