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