Mercurial > projects > qtd
diff demos/browser/tabwidget.d @ 45:71b382c10ef6
add coarse and incomplete QT browser port
author | mandel |
---|---|
date | Sun, 17 May 2009 18:49:59 +0000 |
parents | |
children | 7bfd46c330dc |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/demos/browser/tabwidget.d Sun May 17 18:49:59 2009 +0000 @@ -0,0 +1,952 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +import QtGui.QTabBar; + +import QtGui.QShortcut; + +import tabwidget; + +import browserapplication; +import browsermainwindow; +import history; +import urllineedit; +import webview; + +import QtGui.QClipboard; +import QtGui.QCompleter; +import QtGui.QListView; +import QtGui.QMenu; +import QtGui.QMessageBox; +import QtGui.QMouseEvent; +import QtGui.QStackedWidget; +import QtGui.QStyle; +import QtGui.QToolButton; + +import QtCore.QDebug; + +/* + Tab bar with a few more features such as a context menu and shortcuts + */ +class TabBar : public QTabBar +{ + Q_OBJECT + +signals: + void newTab(); + void cloneTab(int index); + void closeTab(int index); + 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); +} + + void reloadTab(int index); + void reloadAllTabs(); + void tabMoveRequested(int fromIndex, int toIndex); + +public: + this(QWidget *parent = null) +{ + super(parent); + setContextMenuPolicy(Qt.CustomContextMenu); + setAcceptDrops(true); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), + this, SLOT(contextMenuRequested(const QPoint &))); + + 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); + connect(shortCut, SIGNAL(activated()), this, SLOT(selectTabAction())); + } + setTabsClosable(true); + connect(this, SIGNAL(tabCloseRequested(int)), + this, SIGNAL(closeTab(int))); + 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 slots: + 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(const 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; + +/* +QT_BEGIN_NAMESPACE +class QAction; +QT_END_NAMESPACE +class WebView; +*/ + +/*! + 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 +{ + Q_OBJECT + +public: + this(QAction *root, QWebPage::WebAction webAction, QObject *parent) +{ +super(parent); + m_currentParent = 0; + m_root = root; + m_webAction = webAction; + if (!m_root) + return; + connect(m_root, SIGNAL(triggered()), this, SLOT(rootTriggered())); + connect(root, SIGNAL(destroyed(QObject *)), this, SLOT(rootDestroyed())); + root.setEnabled(false); +} + + QWebPage::WebAction webAction() const +{ + return m_webAction; +} + + void addChild(QAction *action) +{ + if (!action) + return; + connect(action, SIGNAL(changed()), this, SLOT(childChanged())); +} + + + void updateCurrent(QWebPage *currentParent) +{ + if (m_currentParent) + disconnect(m_currentParent, SIGNAL(destroyed(QObject *)), + this, SLOT(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()); + connect(m_currentParent, SIGNAL(destroyed(QObject *)), + this, SLOT(currentDestroyed())); +} + +private slots: + 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 QtCore.QUrl; +import QtGui.QTabWidget; +/* +QT_BEGIN_NAMESPACE +class QCompleter; +class QLineEdit; +class QMenu; +class QStackedWidget; +QT_END_NAMESPACE +*/ + +/*! + 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 +{ + Q_OBJECT + +signals: + // tab widget signals + void loadPage(const QString &url); + void tabsChanged(); + void lastTabClosed(); + + // current tab signals + void setCurrentTitle(const QString &url); + void showStatusBarMessage(const QString &message); + void linkHovered(const QString &link); + void loadProgress(int progress); + void geometryChangeRequested(const QRect &geometry); + void menuBarVisibilityChangeRequested(bool visible); + void statusBarVisibilityChangeRequested(bool visible); + void toolBarVisibilityChangeRequested(bool visible); + void 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); + + connect(m_tabBar, SIGNAL(newTab()), this, SLOT(newTab())); + connect(m_tabBar, SIGNAL(closeTab(int)), this, SLOT(closeTab(int))); + connect(m_tabBar, SIGNAL(cloneTab(int)), this, SLOT(cloneTab(int))); + connect(m_tabBar, SIGNAL(closeOtherTabs(int)), this, SLOT(closeOtherTabs(int))); + connect(m_tabBar, SIGNAL(reloadTab(int)), this, SLOT(reloadTab(int))); + connect(m_tabBar, SIGNAL(reloadAllTabs()), this, SLOT(reloadAllTabs())); + connect(m_tabBar, SIGNAL(tabMoved(int, int)), this, SLOT(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); + connect(m_newTabAction, SIGNAL(triggered()), this, SLOT(newTab())); + + m_closeTabAction = new QAction(QIcon(QLatin1String(":closetab.png")), tr("&Close Tab"), this); + m_closeTabAction.setShortcuts(QKeySequence::Close); + m_closeTabAction.setIconVisibleInMenu(false); + connect(m_closeTabAction, SIGNAL(triggered()), this, SLOT(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); + connect(m_nextTabAction, SIGNAL(triggered()), this, SLOT(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); + connect(m_previousTabAction, SIGNAL(triggered()), this, SLOT(previousTab())); + + m_recentlyClosedTabsMenu = new QMenu(this); + connect(m_recentlyClosedTabsMenu, SIGNAL(aboutToShow()), + this, SLOT(aboutToShowRecentTabsMenu())); + connect(m_recentlyClosedTabsMenu, SIGNAL(triggered(QAction *)), + this, SLOT(aboutToShowRecentTriggeredAction(QAction *))); + m_recentlyClosedTabsAction = new QAction(tr("Recently Closed Tabs"), this); + m_recentlyClosedTabsAction.setMenu(m_recentlyClosedTabsMenu); + m_recentlyClosedTabsAction.setEnabled(false); + + connect(this, SIGNAL(currentChanged(int)), + this, SLOT(currentChanged(int))); + + 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(const 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 slots: + void loadUrlInCurrentTab(const 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); + connect(lineEdit, SIGNAL(returnPressed()), this, SLOT(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); + disconnect(this, SIGNAL(currentChanged(int)), + this, SLOT(currentChanged(int))); + addTab(emptyWidget, tr("(Untitled)")); + connect(this, SIGNAL(currentChanged(int)), + this, SLOT(currentChanged(int))); + return 0; + } + + // webview + WebView *webView = new WebView; + urlLineEdit.setWebView(webView); + connect(webView, SIGNAL(loadStarted()), + this, SLOT(webViewLoadStarted())); + connect(webView, SIGNAL(loadFinished(bool)), + this, SLOT(webViewIconChanged())); + connect(webView, SIGNAL(iconChanged()), + this, SLOT(webViewIconChanged())); + connect(webView, SIGNAL(titleChanged(const QString &)), + this, SLOT(webViewTitleChanged(const QString &))); + connect(webView, SIGNAL(urlChanged(const QUrl &)), + this, SLOT(webViewUrlChanged(const QUrl &))); + connect(webView.page(), SIGNAL(windowCloseRequested()), + this, SLOT(windowCloseRequested())); + connect(webView.page(), SIGNAL(geometryChangeRequested(const QRect &)), + this, SIGNAL(geometryChangeRequested(const QRect &))); + connect(webView.page(), SIGNAL(printRequested(QWebFrame *)), + this, SIGNAL(printRequested(QWebFrame *))); + connect(webView.page(), SIGNAL(menuBarVisibilityChangeRequested(bool)), + this, SIGNAL(menuBarVisibilityChangeRequested(bool))); + connect(webView.page(), SIGNAL(statusBarVisibilityChangeRequested(bool)), + this, SIGNAL(statusBarVisibilityChangeRequested(bool))); + connect(webView.page(), SIGNAL(toolBarVisibilityChangeRequested(bool)), + this, SIGNAL(toolBarVisibilityChangeRequested(bool))); + 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); +// 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 slots: + 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) { + disconnect(oldWebView, SIGNAL(statusBarMessage(const QString&)), + this, SIGNAL(showStatusBarMessage(const QString&))); + disconnect(oldWebView.page(), SIGNAL(linkHovered(const QString&, const QString&, const QString&)), + this, SIGNAL(linkHovered(const QString&))); + disconnect(oldWebView, SIGNAL(loadProgress(int)), + this, SIGNAL(loadProgress(int))); + } + + connect(webView, SIGNAL(statusBarMessage(const QString&)), + this, SIGNAL(showStatusBarMessage(const QString&))); + connect(webView.page(), SIGNAL(linkHovered(const QString&, const QString&, const QString&)), + this, SIGNAL(linkHovered(const QString&))); + connect(webView, SIGNAL(loadProgress(int)), + this, SIGNAL(loadProgress(int))); + + 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(const 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(const 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; +}