Mercurial > projects > qtd
comparison 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 |
comparison
equal
deleted
inserted
replaced
44:3cb15c92ac28 | 45:71b382c10ef6 |
---|---|
1 /**************************************************************************** | |
2 ** | |
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). | |
4 ** Contact: Qt Software Information (qt-info@nokia.com) | |
5 ** | |
6 ** This file is part of the demonstration applications of the Qt Toolkit. | |
7 ** | |
8 ** $QT_BEGIN_LICENSE:LGPL$ | |
9 ** Commercial Usage | |
10 ** Licensees holding valid Qt Commercial licenses may use this file in | |
11 ** accordance with the Qt Commercial License Agreement provided with the | |
12 ** Software or, alternatively, in accordance with the terms contained in | |
13 ** a written agreement between you and Nokia. | |
14 ** | |
15 ** GNU Lesser General Public License Usage | |
16 ** Alternatively, this file may be used under the terms of the GNU Lesser | |
17 ** General Public License version 2.1 as published by the Free Software | |
18 ** Foundation and appearing in the file LICENSE.LGPL included in the | |
19 ** packaging of this file. Please review the following information to | |
20 ** ensure the GNU Lesser General Public License version 2.1 requirements | |
21 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. | |
22 ** | |
23 ** In addition, as a special exception, Nokia gives you certain | |
24 ** additional rights. These rights are described in the Nokia Qt LGPL | |
25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this | |
26 ** package. | |
27 ** | |
28 ** GNU General Public License Usage | |
29 ** Alternatively, this file may be used under the terms of the GNU | |
30 ** General Public License version 3.0 as published by the Free Software | |
31 ** Foundation and appearing in the file LICENSE.GPL included in the | |
32 ** packaging of this file. Please review the following information to | |
33 ** ensure the GNU General Public License version 3.0 requirements will be | |
34 ** met: http://www.gnu.org/copyleft/gpl.html. | |
35 ** | |
36 ** If you are unsure which license is appropriate for your use, please | |
37 ** contact the sales department at qt-sales@nokia.com. | |
38 ** $QT_END_LICENSE$ | |
39 ** | |
40 ****************************************************************************/ | |
41 | |
42 import QtGui.QTabBar; | |
43 | |
44 import QtGui.QShortcut; | |
45 | |
46 import tabwidget; | |
47 | |
48 import browserapplication; | |
49 import browsermainwindow; | |
50 import history; | |
51 import urllineedit; | |
52 import webview; | |
53 | |
54 import QtGui.QClipboard; | |
55 import QtGui.QCompleter; | |
56 import QtGui.QListView; | |
57 import QtGui.QMenu; | |
58 import QtGui.QMessageBox; | |
59 import QtGui.QMouseEvent; | |
60 import QtGui.QStackedWidget; | |
61 import QtGui.QStyle; | |
62 import QtGui.QToolButton; | |
63 | |
64 import QtCore.QDebug; | |
65 | |
66 /* | |
67 Tab bar with a few more features such as a context menu and shortcuts | |
68 */ | |
69 class TabBar : public QTabBar | |
70 { | |
71 Q_OBJECT | |
72 | |
73 signals: | |
74 void newTab(); | |
75 void cloneTab(int index); | |
76 void closeTab(int index); | |
77 void closeOtherTabs(int index) | |
78 { | |
79 if (-1 == index) | |
80 return; | |
81 for (int i = count() - 1; i > index; --i) | |
82 closeTab(i); | |
83 for (int i = index - 1; i >= 0; --i) | |
84 closeTab(i); | |
85 } | |
86 | |
87 void reloadTab(int index); | |
88 void reloadAllTabs(); | |
89 void tabMoveRequested(int fromIndex, int toIndex); | |
90 | |
91 public: | |
92 this(QWidget *parent = null) | |
93 { | |
94 super(parent); | |
95 setContextMenuPolicy(Qt.CustomContextMenu); | |
96 setAcceptDrops(true); | |
97 connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), | |
98 this, SLOT(contextMenuRequested(const QPoint &))); | |
99 | |
100 QString alt = QLatin1String("Alt+%1"); | |
101 for (int i = 1; i <= 10; ++i) { | |
102 int key = i; | |
103 if (key == 10) | |
104 key = 0; | |
105 QShortcut *shortCut = new QShortcut(alt.arg(key), this); | |
106 m_tabShortcuts.append(shortCut); | |
107 connect(shortCut, SIGNAL(activated()), this, SLOT(selectTabAction())); | |
108 } | |
109 setTabsClosable(true); | |
110 connect(this, SIGNAL(tabCloseRequested(int)), | |
111 this, SIGNAL(closeTab(int))); | |
112 setSelectionBehaviorOnRemove(QTabBar::SelectPreviousTab); | |
113 setMovable(true); | |
114 } | |
115 | |
116 protected: | |
117 void mousePressEvent(QMouseEvent* event) | |
118 { | |
119 if (event.button() == Qt.LeftButton) | |
120 m_dragStartPos = event.pos(); | |
121 QTabBar::mousePressEvent(event); | |
122 } | |
123 | |
124 void mouseMoveEvent(QMouseEvent* event) | |
125 { | |
126 if (event.buttons() == Qt.LeftButton) { | |
127 int diffX = event.pos().x() - m_dragStartPos.x(); | |
128 int diffY = event.pos().y() - m_dragStartPos.y(); | |
129 if ((event.pos() - m_dragStartPos).manhattanLength() > QApplication::startDragDistance() | |
130 && diffX < 3 && diffX > -3 | |
131 && diffY < -10) { | |
132 QDrag *drag = new QDrag(this); | |
133 QMimeData *mimeData = new QMimeData; | |
134 QList<QUrl> urls; | |
135 int index = tabAt(event.pos()); | |
136 QUrl url = tabData(index).toUrl(); | |
137 urls.append(url); | |
138 mimeData.setUrls(urls); | |
139 mimeData.setText(tabText(index)); | |
140 mimeData.setData(QLatin1String("action"), "tab-reordering"); | |
141 drag.setMimeData(mimeData); | |
142 drag.exec(); | |
143 } | |
144 } | |
145 QTabBar::mouseMoveEvent(event); | |
146 } | |
147 | |
148 private slots: | |
149 void selectTabAction() | |
150 { | |
151 if (QShortcut *shortCut = qobject_cast<QShortcut*>(sender())) { | |
152 int index = m_tabShortcuts.indexOf(shortCut); | |
153 if (index == 0) | |
154 index = 10; | |
155 setCurrentIndex(index); | |
156 } | |
157 } | |
158 void cloneTab(); | |
159 { | |
160 if (QAction *action = qobject_cast<QAction*>(sender())) { | |
161 int index = action.data().toInt(); | |
162 emit cloneTab(index); | |
163 } | |
164 } | |
165 | |
166 void closeTab(); | |
167 { | |
168 if (QAction *action = qobject_cast<QAction*>(sender())) { | |
169 int index = action.data().toInt(); | |
170 emit closeTab(index); | |
171 } | |
172 } | |
173 | |
174 void closeOtherTabs() | |
175 { | |
176 if (QAction *action = qobject_cast<QAction*>(sender())) { | |
177 int index = action.data().toInt(); | |
178 emit closeOtherTabs(index); | |
179 } | |
180 } | |
181 | |
182 void reloadTab() | |
183 { | |
184 if (QAction *action = qobject_cast<QAction*>(sender())) { | |
185 int index = action.data().toInt(); | |
186 emit reloadTab(index); | |
187 } | |
188 } | |
189 | |
190 void contextMenuRequested(const QPoint &position) | |
191 { | |
192 QMenu menu; | |
193 menu.addAction(tr("New &Tab"), this, SIGNAL(newTab()), QKeySequence::AddTab); | |
194 int index = tabAt(position); | |
195 if (-1 != index) { | |
196 QAction *action = menu.addAction(tr("Clone Tab"), | |
197 this, SLOT(cloneTab())); | |
198 action.setData(index); | |
199 | |
200 menu.addSeparator(); | |
201 | |
202 action = menu.addAction(tr("&Close Tab"), | |
203 this, SLOT(closeTab()), QKeySequence::Close); | |
204 action.setData(index); | |
205 | |
206 action = menu.addAction(tr("Close &Other Tabs"), | |
207 this, SLOT(closeOtherTabs())); | |
208 action.setData(index); | |
209 | |
210 menu.addSeparator(); | |
211 | |
212 action = menu.addAction(tr("Reload Tab"), | |
213 this, SLOT(reloadTab()), QKeySequence::Refresh); | |
214 action.setData(index); | |
215 } else { | |
216 menu.addSeparator(); | |
217 } | |
218 menu.addAction(tr("Reload All Tabs"), this, SIGNAL(reloadAllTabs())); | |
219 menu.exec(QCursor::pos()); | |
220 } | |
221 | |
222 private: | |
223 QList<QShortcut*> m_tabShortcuts; | |
224 friend class TabWidget; | |
225 | |
226 QPoint m_dragStartPos; | |
227 int m_dragCurrentIndex; | |
228 } | |
229 | |
230 import QtWebKit.QWebPage; | |
231 | |
232 /* | |
233 QT_BEGIN_NAMESPACE | |
234 class QAction; | |
235 QT_END_NAMESPACE | |
236 class WebView; | |
237 */ | |
238 | |
239 /*! | |
240 A proxy object that connects a single browser action | |
241 to one child webpage action at a time. | |
242 | |
243 Example usage: used to keep the main window stop action in sync with | |
244 the current tabs webview's stop action. | |
245 */ | |
246 class WebActionMapper : public QObject | |
247 { | |
248 Q_OBJECT | |
249 | |
250 public: | |
251 this(QAction *root, QWebPage::WebAction webAction, QObject *parent) | |
252 { | |
253 super(parent); | |
254 m_currentParent = 0; | |
255 m_root = root; | |
256 m_webAction = webAction; | |
257 if (!m_root) | |
258 return; | |
259 connect(m_root, SIGNAL(triggered()), this, SLOT(rootTriggered())); | |
260 connect(root, SIGNAL(destroyed(QObject *)), this, SLOT(rootDestroyed())); | |
261 root.setEnabled(false); | |
262 } | |
263 | |
264 QWebPage::WebAction webAction() const | |
265 { | |
266 return m_webAction; | |
267 } | |
268 | |
269 void addChild(QAction *action) | |
270 { | |
271 if (!action) | |
272 return; | |
273 connect(action, SIGNAL(changed()), this, SLOT(childChanged())); | |
274 } | |
275 | |
276 | |
277 void updateCurrent(QWebPage *currentParent) | |
278 { | |
279 if (m_currentParent) | |
280 disconnect(m_currentParent, SIGNAL(destroyed(QObject *)), | |
281 this, SLOT(currentDestroyed())); | |
282 | |
283 m_currentParent = currentParent; | |
284 if (!m_root) | |
285 return; | |
286 if (!m_currentParent) { | |
287 m_root.setEnabled(false); | |
288 m_root.setChecked(false); | |
289 return; | |
290 } | |
291 QAction *source = m_currentParent.action(m_webAction); | |
292 m_root.setChecked(source.isChecked()); | |
293 m_root.setEnabled(source.isEnabled()); | |
294 connect(m_currentParent, SIGNAL(destroyed(QObject *)), | |
295 this, SLOT(currentDestroyed())); | |
296 } | |
297 | |
298 private slots: | |
299 void rootTriggered() | |
300 { | |
301 if (m_currentParent) { | |
302 QAction *gotoAction = m_currentParent.action(m_webAction); | |
303 gotoAction.trigger(); | |
304 } | |
305 } | |
306 | |
307 void childChanged() | |
308 { | |
309 if (QAction *source = qobject_cast<QAction*>(sender())) { | |
310 if (m_root | |
311 && m_currentParent | |
312 && source.parent() == m_currentParent) { | |
313 m_root.setChecked(source.isChecked()); | |
314 m_root.setEnabled(source.isEnabled()); | |
315 } | |
316 } | |
317 } | |
318 | |
319 void rootDestroyed() | |
320 { | |
321 m_root = 0; | |
322 } | |
323 void currentDestroyed() | |
324 { | |
325 updateCurrent(0); | |
326 } | |
327 | |
328 private: | |
329 QWebPage *m_currentParent; | |
330 QAction *m_root; | |
331 QWebPage::WebAction m_webAction; | |
332 }; | |
333 | |
334 import QtCore.QUrl; | |
335 import QtGui.QTabWidget; | |
336 /* | |
337 QT_BEGIN_NAMESPACE | |
338 class QCompleter; | |
339 class QLineEdit; | |
340 class QMenu; | |
341 class QStackedWidget; | |
342 QT_END_NAMESPACE | |
343 */ | |
344 | |
345 /*! | |
346 TabWidget that contains WebViews and a stack widget of associated line edits. | |
347 | |
348 Connects up the current tab's signals to this class's signal and uses WebActionMapper | |
349 to proxy the actions. | |
350 */ | |
351 class TabWidget : public QTabWidget | |
352 { | |
353 Q_OBJECT | |
354 | |
355 signals: | |
356 // tab widget signals | |
357 void loadPage(const QString &url); | |
358 void tabsChanged(); | |
359 void lastTabClosed(); | |
360 | |
361 // current tab signals | |
362 void setCurrentTitle(const QString &url); | |
363 void showStatusBarMessage(const QString &message); | |
364 void linkHovered(const QString &link); | |
365 void loadProgress(int progress); | |
366 void geometryChangeRequested(const QRect &geometry); | |
367 void menuBarVisibilityChangeRequested(bool visible); | |
368 void statusBarVisibilityChangeRequested(bool visible); | |
369 void toolBarVisibilityChangeRequested(bool visible); | |
370 void printRequested(QWebFrame *frame); | |
371 | |
372 public: | |
373 this(QWidget *parent = null) | |
374 { | |
375 QTabWidget(parent) | |
376 m_recentlyClosedTabsAction = 0; | |
377 m_newTabAction = 0; | |
378 m_closeTabAction = 0; | |
379 m_nextTabAction = 0; | |
380 m_previousTabAction = 0; | |
381 m_recentlyClosedTabsMenu = 0; | |
382 m_lineEditCompleter = 0; | |
383 m_lineEdits = 0; | |
384 m_tabBar = new TabBar(this); | |
385 | |
386 | |
387 setElideMode(Qt.ElideRight); | |
388 | |
389 connect(m_tabBar, SIGNAL(newTab()), this, SLOT(newTab())); | |
390 connect(m_tabBar, SIGNAL(closeTab(int)), this, SLOT(closeTab(int))); | |
391 connect(m_tabBar, SIGNAL(cloneTab(int)), this, SLOT(cloneTab(int))); | |
392 connect(m_tabBar, SIGNAL(closeOtherTabs(int)), this, SLOT(closeOtherTabs(int))); | |
393 connect(m_tabBar, SIGNAL(reloadTab(int)), this, SLOT(reloadTab(int))); | |
394 connect(m_tabBar, SIGNAL(reloadAllTabs()), this, SLOT(reloadAllTabs())); | |
395 connect(m_tabBar, SIGNAL(tabMoved(int, int)), this, SLOT(moveTab(int, int))); | |
396 setTabBar(m_tabBar); | |
397 setDocumentMode(true); | |
398 | |
399 // Actions | |
400 m_newTabAction = new QAction(QIcon(QLatin1String(":addtab.png")), tr("New &Tab"), this); | |
401 m_newTabAction.setShortcuts(QKeySequence::AddTab); | |
402 m_newTabAction.setIconVisibleInMenu(false); | |
403 connect(m_newTabAction, SIGNAL(triggered()), this, SLOT(newTab())); | |
404 | |
405 m_closeTabAction = new QAction(QIcon(QLatin1String(":closetab.png")), tr("&Close Tab"), this); | |
406 m_closeTabAction.setShortcuts(QKeySequence::Close); | |
407 m_closeTabAction.setIconVisibleInMenu(false); | |
408 connect(m_closeTabAction, SIGNAL(triggered()), this, SLOT(closeTab())); | |
409 | |
410 m_nextTabAction = new QAction(tr("Show Next Tab"), this); | |
411 QList<QKeySequence> shortcuts; | |
412 shortcuts.append(QKeySequence(Qt.CTRL | Qt.Key_BraceRight)); | |
413 shortcuts.append(QKeySequence(Qt.CTRL | Qt.Key_PageDown)); | |
414 shortcuts.append(QKeySequence(Qt.CTRL | Qt.Key_BracketRight)); | |
415 shortcuts.append(QKeySequence(Qt.CTRL | Qt.Key_Less)); | |
416 m_nextTabAction.setShortcuts(shortcuts); | |
417 connect(m_nextTabAction, SIGNAL(triggered()), this, SLOT(nextTab())); | |
418 | |
419 m_previousTabAction = new QAction(tr("Show Previous Tab"), this); | |
420 shortcuts.clear(); | |
421 shortcuts.append(QKeySequence(Qt.CTRL | Qt.Key_BraceLeft)); | |
422 shortcuts.append(QKeySequence(Qt.CTRL | Qt.Key_PageUp)); | |
423 shortcuts.append(QKeySequence(Qt.CTRL | Qt.Key_BracketLeft)); | |
424 shortcuts.append(QKeySequence(Qt.CTRL | Qt.Key_Greater)); | |
425 m_previousTabAction.setShortcuts(shortcuts); | |
426 connect(m_previousTabAction, SIGNAL(triggered()), this, SLOT(previousTab())); | |
427 | |
428 m_recentlyClosedTabsMenu = new QMenu(this); | |
429 connect(m_recentlyClosedTabsMenu, SIGNAL(aboutToShow()), | |
430 this, SLOT(aboutToShowRecentTabsMenu())); | |
431 connect(m_recentlyClosedTabsMenu, SIGNAL(triggered(QAction *)), | |
432 this, SLOT(aboutToShowRecentTriggeredAction(QAction *))); | |
433 m_recentlyClosedTabsAction = new QAction(tr("Recently Closed Tabs"), this); | |
434 m_recentlyClosedTabsAction.setMenu(m_recentlyClosedTabsMenu); | |
435 m_recentlyClosedTabsAction.setEnabled(false); | |
436 | |
437 connect(this, SIGNAL(currentChanged(int)), | |
438 this, SLOT(currentChanged(int))); | |
439 | |
440 m_lineEdits = new QStackedWidget(this); | |
441 } | |
442 | |
443 void clear() | |
444 { | |
445 // clear the recently closed tabs | |
446 m_recentlyClosedTabs.clear(); | |
447 // clear the line edit history | |
448 for (int i = 0; i < m_lineEdits.count(); ++i) { | |
449 QLineEdit *qLineEdit = lineEdit(i); | |
450 qLineEdit.setText(qLineEdit.text()); | |
451 } | |
452 } | |
453 | |
454 void addWebAction(QAction *action, QWebPage::WebAction webAction) | |
455 { | |
456 if (!action) | |
457 return; | |
458 m_actions.append(new WebActionMapper(action, webAction, this)); | |
459 } | |
460 | |
461 | |
462 QAction *newTabAction() const; | |
463 { | |
464 return m_newTabAction; | |
465 } | |
466 | |
467 QAction *closeTabAction() const; | |
468 { | |
469 return m_closeTabAction; | |
470 } | |
471 QAction *recentlyClosedTabsAction() const; | |
472 { | |
473 return m_recentlyClosedTabsAction; | |
474 } | |
475 | |
476 QAction *nextTabAction() const | |
477 { | |
478 return m_nextTabAction; | |
479 } | |
480 QAction *previousTabAction() const | |
481 { | |
482 return m_previousTabAction; | |
483 } | |
484 | |
485 QWidget *lineEditStack() const | |
486 { | |
487 return m_lineEdits; | |
488 } | |
489 | |
490 QLineEdit *currentLineEdit() const | |
491 { | |
492 return lineEdit(m_lineEdits.currentIndex()); | |
493 } | |
494 | |
495 WebView *currentWebView() const | |
496 { | |
497 return webView(currentIndex()); | |
498 } | |
499 | |
500 WebView *webView(int index) const | |
501 { | |
502 QWidget *widget = this.widget(index); | |
503 if (WebView *webView = qobject_cast<WebView*>(widget)) { | |
504 return webView; | |
505 } else { | |
506 // optimization to delay creating the first webview | |
507 if (count() == 1) { | |
508 TabWidget *that = const_cast<TabWidget*>(this); | |
509 that.setUpdatesEnabled(false); | |
510 that.newTab(); | |
511 that.closeTab(0); | |
512 that.setUpdatesEnabled(true); | |
513 return currentWebView(); | |
514 } | |
515 } | |
516 return 0; | |
517 } | |
518 | |
519 QLineEdit *lineEdit(int index) const | |
520 { | |
521 UrlLineEdit *urlLineEdit = qobject_cast<UrlLineEdit*>(m_lineEdits.widget(index)); | |
522 if (urlLineEdit) | |
523 return urlLineEdit.lineEdit(); | |
524 return 0; | |
525 } | |
526 | |
527 int webViewIndex(WebView *webView) const | |
528 { | |
529 int index = indexOf(webView); | |
530 return index; | |
531 } | |
532 | |
533 | |
534 static const qint32 TabWidgetMagic = 0xaa; | |
535 | |
536 QByteArray saveState() const; | |
537 { | |
538 int version = 1; | |
539 QByteArray data; | |
540 QDataStream stream(&data, QIODevice::WriteOnly); | |
541 | |
542 stream << qint32(TabWidgetMagic); | |
543 stream << qint32(version); | |
544 | |
545 QStringList tabs; | |
546 for (int i = 0; i < count(); ++i) { | |
547 if (WebView *tab = qobject_cast<WebView*>(widget(i))) { | |
548 tabs.append(tab.url().toString()); | |
549 } else { | |
550 tabs.append(QString::null); | |
551 } | |
552 } | |
553 stream << tabs; | |
554 stream << currentIndex(); | |
555 return data; | |
556 } | |
557 | |
558 bool restoreState(const QByteArray &state) | |
559 { | |
560 int version = 1; | |
561 QByteArray sd = state; | |
562 QDataStream stream(&sd, QIODevice::ReadOnly); | |
563 if (stream.atEnd()) | |
564 return false; | |
565 | |
566 qint32 marker; | |
567 qint32 v; | |
568 stream >> marker; | |
569 stream >> v; | |
570 if (marker != TabWidgetMagic || v != version) | |
571 return false; | |
572 | |
573 QStringList openTabs; | |
574 stream >> openTabs; | |
575 | |
576 for (int i = 0; i < openTabs.count(); ++i) { | |
577 if (i != 0) | |
578 newTab(); | |
579 loadPage(openTabs.at(i)); | |
580 } | |
581 | |
582 int currentTab; | |
583 stream >> currentTab; | |
584 setCurrentIndex(currentTab); | |
585 | |
586 return true; | |
587 } | |
588 | |
589 protected: | |
590 void mouseDoubleClickEvent(QMouseEvent *event); | |
591 { | |
592 if (!childAt(event.pos()) | |
593 // Remove the line below when QTabWidget does not have a one pixel frame | |
594 && event.pos().y() < (tabBar().y() + tabBar().height())) { | |
595 newTab(); | |
596 return; | |
597 } | |
598 QTabWidget::mouseDoubleClickEvent(event); | |
599 } | |
600 | |
601 | |
602 void contextMenuEvent(QContextMenuEvent *event) | |
603 { | |
604 if (!childAt(event.pos())) { | |
605 m_tabBar.contextMenuRequested(event.pos()); | |
606 return; | |
607 } | |
608 QTabWidget::contextMenuEvent(event); | |
609 } | |
610 | |
611 void mouseReleaseEvent(QMouseEvent *event) | |
612 { | |
613 if (event.button() == Qt.MidButton && !childAt(event.pos()) | |
614 // Remove the line below when QTabWidget does not have a one pixel frame | |
615 && event.pos().y() < (tabBar().y() + tabBar().height())) { | |
616 QUrl url(QApplication::clipboard().text(QClipboard::Selection)); | |
617 if (!url.isEmpty() && url.isValid() && !url.scheme().isEmpty()) { | |
618 WebView *webView = newTab(); | |
619 webView.setUrl(url); | |
620 } | |
621 } | |
622 } | |
623 | |
624 public slots: | |
625 void loadUrlInCurrentTab(const QUrl &url) | |
626 { | |
627 WebView *webView = currentWebView(); | |
628 if (webView) { | |
629 webView.loadUrl(url); | |
630 webView.setFocus(); | |
631 } | |
632 } | |
633 | |
634 WebView *newTab(bool makeCurrent = true) | |
635 { | |
636 // line edit | |
637 UrlLineEdit *urlLineEdit = new UrlLineEdit; | |
638 QLineEdit *lineEdit = urlLineEdit.lineEdit(); | |
639 if (!m_lineEditCompleter && count() > 0) { | |
640 HistoryCompletionModel *completionModel = new HistoryCompletionModel(this); | |
641 completionModel.setSourceModel(BrowserApplication::historyManager().historyFilterModel()); | |
642 m_lineEditCompleter = new QCompleter(completionModel, this); | |
643 // Should this be in Qt by default? | |
644 QAbstractItemView *popup = m_lineEditCompleter.popup(); | |
645 QListView *listView = qobject_cast<QListView*>(popup); | |
646 if (listView) | |
647 listView.setUniformItemSizes(true); | |
648 } | |
649 lineEdit.setCompleter(m_lineEditCompleter); | |
650 connect(lineEdit, SIGNAL(returnPressed()), this, SLOT(lineEditReturnPressed())); | |
651 m_lineEdits.addWidget(urlLineEdit); | |
652 m_lineEdits.setSizePolicy(lineEdit.sizePolicy()); | |
653 | |
654 // optimization to delay creating the more expensive WebView, history, etc | |
655 if (count() == 0) { | |
656 QWidget *emptyWidget = new QWidget; | |
657 QPalette p = emptyWidget.palette(); | |
658 p.setColor(QPalette::Window, palette().color(QPalette::Base)); | |
659 emptyWidget.setPalette(p); | |
660 emptyWidget.setAutoFillBackground(true); | |
661 disconnect(this, SIGNAL(currentChanged(int)), | |
662 this, SLOT(currentChanged(int))); | |
663 addTab(emptyWidget, tr("(Untitled)")); | |
664 connect(this, SIGNAL(currentChanged(int)), | |
665 this, SLOT(currentChanged(int))); | |
666 return 0; | |
667 } | |
668 | |
669 // webview | |
670 WebView *webView = new WebView; | |
671 urlLineEdit.setWebView(webView); | |
672 connect(webView, SIGNAL(loadStarted()), | |
673 this, SLOT(webViewLoadStarted())); | |
674 connect(webView, SIGNAL(loadFinished(bool)), | |
675 this, SLOT(webViewIconChanged())); | |
676 connect(webView, SIGNAL(iconChanged()), | |
677 this, SLOT(webViewIconChanged())); | |
678 connect(webView, SIGNAL(titleChanged(const QString &)), | |
679 this, SLOT(webViewTitleChanged(const QString &))); | |
680 connect(webView, SIGNAL(urlChanged(const QUrl &)), | |
681 this, SLOT(webViewUrlChanged(const QUrl &))); | |
682 connect(webView.page(), SIGNAL(windowCloseRequested()), | |
683 this, SLOT(windowCloseRequested())); | |
684 connect(webView.page(), SIGNAL(geometryChangeRequested(const QRect &)), | |
685 this, SIGNAL(geometryChangeRequested(const QRect &))); | |
686 connect(webView.page(), SIGNAL(printRequested(QWebFrame *)), | |
687 this, SIGNAL(printRequested(QWebFrame *))); | |
688 connect(webView.page(), SIGNAL(menuBarVisibilityChangeRequested(bool)), | |
689 this, SIGNAL(menuBarVisibilityChangeRequested(bool))); | |
690 connect(webView.page(), SIGNAL(statusBarVisibilityChangeRequested(bool)), | |
691 this, SIGNAL(statusBarVisibilityChangeRequested(bool))); | |
692 connect(webView.page(), SIGNAL(toolBarVisibilityChangeRequested(bool)), | |
693 this, SIGNAL(toolBarVisibilityChangeRequested(bool))); | |
694 addTab(webView, tr("(Untitled)")); | |
695 if (makeCurrent) | |
696 setCurrentWidget(webView); | |
697 | |
698 // webview actions | |
699 for (int i = 0; i < m_actions.count(); ++i) { | |
700 WebActionMapper *mapper = m_actions[i]; | |
701 mapper.addChild(webView.page().action(mapper.webAction())); | |
702 } | |
703 | |
704 if (count() == 1) | |
705 currentChanged(currentIndex()); | |
706 emit tabsChanged(); | |
707 return webView; | |
708 } | |
709 | |
710 | |
711 // When index is -1 index chooses the current tab | |
712 void cloneTab(int index = -1) | |
713 { | |
714 if (index < 0) | |
715 index = currentIndex(); | |
716 if (index < 0 || index >= count()) | |
717 return; | |
718 WebView *tab = newTab(false); | |
719 tab.setUrl(webView(index).url()); | |
720 } | |
721 | |
722 | |
723 | |
724 // When index is -1 index chooses the current tab | |
725 void closeTab(int index = -1); | |
726 { | |
727 if (index < 0) | |
728 index = currentIndex(); | |
729 if (index < 0 || index >= count()) | |
730 return; | |
731 | |
732 bool hasFocus = false; | |
733 if (WebView *tab = webView(index)) { | |
734 if (tab.isModified()) { | |
735 QMessageBox closeConfirmation(tab); | |
736 closeConfirmation.setWindowFlags(Qt.Sheet); | |
737 closeConfirmation.setWindowTitle(tr("Do you really want to close this page?")); | |
738 closeConfirmation.setInformativeText(tr("You have modified this page and when closing it you would lose the modification.\n" | |
739 "Do you really want to close this page?\n")); | |
740 closeConfirmation.setIcon(QMessageBox::Question); | |
741 closeConfirmation.addButton(QMessageBox::Yes); | |
742 closeConfirmation.addButton(QMessageBox::No); | |
743 closeConfirmation.setEscapeButton(QMessageBox::No); | |
744 if (closeConfirmation.exec() == QMessageBox::No) | |
745 return; | |
746 } | |
747 hasFocus = tab.hasFocus(); | |
748 | |
749 m_recentlyClosedTabsAction.setEnabled(true); | |
750 m_recentlyClosedTabs.prepend(tab.url()); | |
751 if (m_recentlyClosedTabs.size() >= TabWidget::m_recentlyClosedTabsSize) | |
752 m_recentlyClosedTabs.removeLast(); | |
753 } | |
754 QWidget *lineEdit = m_lineEdits.widget(index); | |
755 m_lineEdits.removeWidget(lineEdit); | |
756 lineEdit.deleteLater(); | |
757 QWidget *webView = widget(index); | |
758 removeTab(index); | |
759 webView.deleteLater(); | |
760 emit tabsChanged(); | |
761 if (hasFocus && count() > 0) | |
762 currentWebView().setFocus(); | |
763 if (count() == 0) | |
764 emit lastTabClosed(); | |
765 } | |
766 | |
767 void closeOtherTabs(int index); | |
768 // When index is -1 index chooses the current tab | |
769 void reloadTab(int index = -1); | |
770 { | |
771 if (index < 0) | |
772 index = currentIndex(); | |
773 if (index < 0 || index >= count()) | |
774 return; | |
775 | |
776 QWidget *widget = this.widget(index); | |
777 if (WebView *tab = qobject_cast<WebView*>(widget)) | |
778 tab.reload(); | |
779 } | |
780 | |
781 void reloadAllTabs(); | |
782 { | |
783 for (int i = 0; i < count(); ++i) { | |
784 QWidget *tabWidget = widget(i); | |
785 if (WebView *tab = qobject_cast<WebView*>(tabWidget)) { | |
786 tab.reload(); | |
787 } | |
788 } | |
789 } | |
790 void nextTab() | |
791 { | |
792 int next = currentIndex() + 1; | |
793 if (next == count()) | |
794 next = 0; | |
795 setCurrentIndex(next); | |
796 } | |
797 | |
798 | |
799 void previousTab() | |
800 { | |
801 int next = currentIndex() - 1; | |
802 if (next < 0) | |
803 next = count() - 1; | |
804 setCurrentIndex(next); | |
805 } | |
806 | |
807 private slots: | |
808 void currentChanged(int index) | |
809 { | |
810 WebView *webView = this.webView(index); | |
811 if (!webView) | |
812 return; | |
813 | |
814 assert(m_lineEdits.count() == count()); | |
815 | |
816 WebView *oldWebView = this.webView(m_lineEdits.currentIndex()); | |
817 if (oldWebView) { | |
818 disconnect(oldWebView, SIGNAL(statusBarMessage(const QString&)), | |
819 this, SIGNAL(showStatusBarMessage(const QString&))); | |
820 disconnect(oldWebView.page(), SIGNAL(linkHovered(const QString&, const QString&, const QString&)), | |
821 this, SIGNAL(linkHovered(const QString&))); | |
822 disconnect(oldWebView, SIGNAL(loadProgress(int)), | |
823 this, SIGNAL(loadProgress(int))); | |
824 } | |
825 | |
826 connect(webView, SIGNAL(statusBarMessage(const QString&)), | |
827 this, SIGNAL(showStatusBarMessage(const QString&))); | |
828 connect(webView.page(), SIGNAL(linkHovered(const QString&, const QString&, const QString&)), | |
829 this, SIGNAL(linkHovered(const QString&))); | |
830 connect(webView, SIGNAL(loadProgress(int)), | |
831 this, SIGNAL(loadProgress(int))); | |
832 | |
833 for (int i = 0; i < m_actions.count(); ++i) { | |
834 WebActionMapper *mapper = m_actions[i]; | |
835 mapper.updateCurrent(webView.page()); | |
836 } | |
837 emit setCurrentTitle(webView.title()); | |
838 m_lineEdits.setCurrentIndex(index); | |
839 emit loadProgress(webView.progress()); | |
840 emit showStatusBarMessage(webView.lastStatusBarText()); | |
841 if (webView.url().isEmpty()) | |
842 m_lineEdits.currentWidget().setFocus(); | |
843 else | |
844 webView.setFocus(); | |
845 } | |
846 | |
847 void aboutToShowRecentTabsMenu() | |
848 { | |
849 m_recentlyClosedTabsMenu.clear(); | |
850 for (int i = 0; i < m_recentlyClosedTabs.count(); ++i) { | |
851 QAction *action = new QAction(m_recentlyClosedTabsMenu); | |
852 action.setData(m_recentlyClosedTabs.at(i)); | |
853 QIcon icon = BrowserApplication::instance().icon(m_recentlyClosedTabs.at(i)); | |
854 action.setIcon(icon); | |
855 action.setText(m_recentlyClosedTabs.at(i).toString()); | |
856 m_recentlyClosedTabsMenu.addAction(action); | |
857 } | |
858 } | |
859 | |
860 void aboutToShowRecentTriggeredAction(QAction *action) | |
861 { | |
862 QUrl url = action.data().toUrl(); | |
863 loadUrlInCurrentTab(url); | |
864 } | |
865 | |
866 void webViewLoadStarted() | |
867 { | |
868 WebView *webView = qobject_cast<WebView*>(sender()); | |
869 int index = webViewIndex(webView); | |
870 if (-1 != index) { | |
871 QIcon icon(QLatin1String(":loading.gif")); | |
872 setTabIcon(index, icon); | |
873 } | |
874 } | |
875 | |
876 void webViewIconChanged() | |
877 { | |
878 WebView *webView = qobject_cast<WebView*>(sender()); | |
879 int index = webViewIndex(webView); | |
880 if (-1 != index) { | |
881 QIcon icon = BrowserApplication::instance().icon(webView.url()); | |
882 setTabIcon(index, icon); | |
883 } | |
884 } | |
885 | |
886 void webViewTitleChanged(const QString &title) | |
887 { | |
888 WebView *webView = qobject_cast<WebView*>(sender()); | |
889 int index = webViewIndex(webView); | |
890 if (-1 != index) { | |
891 setTabText(index, title); | |
892 } | |
893 if (currentIndex() == index) | |
894 emit setCurrentTitle(title); | |
895 BrowserApplication::historyManager().updateHistoryItem(webView.url(), title); | |
896 } | |
897 | |
898 void webViewUrlChanged(const QUrl &url) | |
899 { | |
900 WebView *webView = qobject_cast<WebView*>(sender()); | |
901 int index = webViewIndex(webView); | |
902 if (-1 != index) { | |
903 m_tabBar.setTabData(index, url); | |
904 } | |
905 emit tabsChanged(); | |
906 } | |
907 | |
908 void lineEditReturnPressed() | |
909 { | |
910 if (QLineEdit *lineEdit = qobject_cast<QLineEdit*>(sender())) { | |
911 emit loadPage(lineEdit.text()); | |
912 if (m_lineEdits.currentWidget() == lineEdit) | |
913 currentWebView().setFocus(); | |
914 } | |
915 } | |
916 | |
917 void windowCloseRequested() | |
918 { | |
919 WebPage *webPage = qobject_cast<WebPage*>(sender()); | |
920 WebView *webView = qobject_cast<WebView*>(webPage.view()); | |
921 int index = webViewIndex(webView); | |
922 if (index >= 0) { | |
923 if (count() == 1) | |
924 webView.webPage().mainWindow().close(); | |
925 else | |
926 closeTab(index); | |
927 } | |
928 } | |
929 | |
930 void moveTab(int fromIndex, int toIndex) | |
931 { | |
932 QWidget *lineEdit = m_lineEdits.widget(fromIndex); | |
933 m_lineEdits.removeWidget(lineEdit); | |
934 m_lineEdits.insertWidget(toIndex, lineEdit); | |
935 } | |
936 | |
937 private: | |
938 QAction *m_recentlyClosedTabsAction; | |
939 QAction *m_newTabAction; | |
940 QAction *m_closeTabAction; | |
941 QAction *m_nextTabAction; | |
942 QAction *m_previousTabAction; | |
943 | |
944 QMenu *m_recentlyClosedTabsMenu; | |
945 static const int m_recentlyClosedTabsSize = 10; | |
946 QList<QUrl> m_recentlyClosedTabs; | |
947 QList<WebActionMapper*> m_actions; | |
948 | |
949 QCompleter *m_lineEditCompleter; | |
950 QStackedWidget *m_lineEdits; | |
951 TabBar *m_tabBar; | |
952 } |