1 /*
2 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
3 * Copyright (C) 2010 University of Szeged
4 *
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
24 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "BrowserWindow.h"
30
31 #include "UrlLoader.h"
32 #include "qwkpreferences.h"
33
newPageFunction(QWKPage * page)34 static QWKPage* newPageFunction(QWKPage* page)
35 {
36 BrowserWindow* window = new BrowserWindow(page->context());
37 return window->page();
38 }
39
40 QVector<qreal> BrowserWindow::m_zoomLevels;
41
BrowserWindow(QWKContext * context,WindowOptions * options)42 BrowserWindow::BrowserWindow(QWKContext* context, WindowOptions* options)
43 : m_isZoomTextOnly(false)
44 , m_currentZoom(1)
45 , m_urlLoader(0)
46 , m_context(context)
47 {
48 if (options)
49 m_windowOptions = *options;
50 else {
51 WindowOptions tmpOptions;
52 m_windowOptions = tmpOptions;
53 }
54
55 if (m_windowOptions.useTiledBackingStore)
56 m_browser = new BrowserView(QGraphicsWKView::Tiled, context);
57 else
58 m_browser = new BrowserView(QGraphicsWKView::Simple, context);
59
60 setAttribute(Qt::WA_DeleteOnClose);
61
62 connect(m_browser->view(), SIGNAL(loadProgress(int)), SLOT(loadProgress(int)));
63 connect(m_browser->view(), SIGNAL(titleChanged(const QString&)), SLOT(setWindowTitle(const QString&)));
64 connect(m_browser->view(), SIGNAL(urlChanged(const QUrl&)), SLOT(urlChanged(const QUrl&)));
65
66 if (m_windowOptions.printLoadedUrls)
67 connect(page(), SIGNAL(urlChanged(QUrl)), this, SLOT(printURL(QUrl)));
68
69 this->setCentralWidget(m_browser);
70 m_browser->setFocus(Qt::OtherFocusReason);
71
72 QMenu* fileMenu = menuBar()->addMenu("&File");
73 fileMenu->addAction("New Window", this, SLOT(newWindow()), QKeySequence::New);
74 fileMenu->addAction("Open File", this, SLOT(openFile()), QKeySequence::Open);
75 fileMenu->addSeparator();
76 fileMenu->addAction("Quit", this, SLOT(close()));
77
78 QMenu* viewMenu = menuBar()->addMenu("&View");
79 viewMenu->addAction(page()->action(QWKPage::Stop));
80 viewMenu->addAction(page()->action(QWKPage::Reload));
81 viewMenu->addSeparator();
82 QAction* zoomIn = viewMenu->addAction("Zoom &In", this, SLOT(zoomIn()));
83 QAction* zoomOut = viewMenu->addAction("Zoom &Out", this, SLOT(zoomOut()));
84 QAction* resetZoom = viewMenu->addAction("Reset Zoom", this, SLOT(resetZoom()));
85 QAction* zoomText = viewMenu->addAction("Zoom Text Only", this, SLOT(toggleZoomTextOnly(bool)));
86 zoomText->setCheckable(true);
87 zoomText->setChecked(false);
88 viewMenu->addSeparator();
89 viewMenu->addAction("Take Screen Shot...", this, SLOT(screenshot()));
90
91 zoomIn->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_Plus));
92 zoomOut->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_Minus));
93 resetZoom->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_0));
94
95 QMenu* windowMenu = menuBar()->addMenu("&Window");
96 QAction* toggleFullScreen = windowMenu->addAction("Toggle FullScreen", this, SIGNAL(enteredFullScreenMode(bool)));
97 toggleFullScreen->setShortcut(Qt::Key_F11);
98 toggleFullScreen->setCheckable(true);
99 toggleFullScreen->setChecked(false);
100 // When exit fullscreen mode by clicking on the exit area (bottom right corner) we must
101 // uncheck the Toggle FullScreen action.
102 toggleFullScreen->connect(this, SIGNAL(enteredFullScreenMode(bool)), SLOT(setChecked(bool)));
103 connect(this, SIGNAL(enteredFullScreenMode(bool)), this, SLOT(toggleFullScreenMode(bool)));
104
105 QMenu* toolsMenu = menuBar()->addMenu("&Develop");
106 QAction* toggleFrameFlattening = toolsMenu->addAction("Toggle Frame Flattening", this, SLOT(toggleFrameFlattening(bool)));
107 toggleFrameFlattening->setCheckable(true);
108 toggleFrameFlattening->setChecked(false);
109 toolsMenu->addSeparator();
110 toolsMenu->addAction("Change User Agent", this, SLOT(showUserAgentDialog()));
111 toolsMenu->addSeparator();
112 toolsMenu->addAction("Load URLs from file", this, SLOT(loadURLListFromFile()));
113
114 QMenu* settingsMenu = menuBar()->addMenu("&Settings");
115 QAction* toggleAutoLoadImages = settingsMenu->addAction("Disable Auto Load Images", this, SLOT(toggleAutoLoadImages(bool)));
116 toggleAutoLoadImages->setCheckable(true);
117 toggleAutoLoadImages->setChecked(false);
118 QAction* toggleDisableJavaScript = settingsMenu->addAction("Disable JavaScript", this, SLOT(toggleDisableJavaScript(bool)));
119 toggleDisableJavaScript->setCheckable(true);
120 toggleDisableJavaScript->setChecked(false);
121
122 m_addressBar = new QLineEdit();
123 connect(m_addressBar, SIGNAL(returnPressed()), SLOT(changeLocation()));
124
125 QToolBar* bar = addToolBar("Navigation");
126 bar->addAction(page()->action(QWKPage::Back));
127 bar->addAction(page()->action(QWKPage::Forward));
128 bar->addAction(page()->action(QWKPage::Reload));
129 bar->addAction(page()->action(QWKPage::Stop));
130 bar->addWidget(m_addressBar);
131
132 QShortcut* selectAddressBar = new QShortcut(Qt::CTRL | Qt::Key_L, this);
133 connect(selectAddressBar, SIGNAL(activated()), this, SLOT(openLocation()));
134
135 page()->setCreateNewPageFunction(newPageFunction);
136
137 // the zoom values are chosen to be like in Mozilla Firefox 3
138 if (!m_zoomLevels.count()) {
139 m_zoomLevels << 0.3 << 0.5 << 0.67 << 0.8 << 0.9;
140 m_zoomLevels << 1;
141 m_zoomLevels << 1.1 << 1.2 << 1.33 << 1.5 << 1.7 << 2 << 2.4 << 3;
142 }
143
144 if (m_windowOptions.startMaximized)
145 setWindowState(windowState() | Qt::WindowMaximized);
146 else
147 resize(800, 600);
148 show();
149 }
150
load(const QString & url)151 void BrowserWindow::load(const QString& url)
152 {
153 m_addressBar->setText(url);
154 m_browser->load(url);
155 }
156
page()157 QWKPage* BrowserWindow::page()
158 {
159 return m_browser->view()->page();
160 }
161
newWindow(const QString & url)162 BrowserWindow* BrowserWindow::newWindow(const QString& url)
163 {
164 BrowserWindow* window;
165 if (m_windowOptions.useSeparateWebProcessPerWindow) {
166 QWKContext* context = new QWKContext();
167 window = new BrowserWindow(context);
168 context->setParent(window);
169 } else
170 window = new BrowserWindow(m_context);
171
172 window->load(url);
173 return window;
174 }
175
openLocation()176 void BrowserWindow::openLocation()
177 {
178 m_addressBar->selectAll();
179 m_addressBar->setFocus();
180 }
181
changeLocation()182 void BrowserWindow::changeLocation()
183 {
184 QString string = m_addressBar->text();
185 m_browser->load(string);
186 }
187
loadProgress(int progress)188 void BrowserWindow::loadProgress(int progress)
189 {
190 QColor backgroundColor = QApplication::palette().color(QPalette::Base);
191 QColor progressColor = QColor(120, 180, 240);
192 QPalette pallete = m_addressBar->palette();
193
194 if (progress <= 0 || progress >= 100)
195 pallete.setBrush(QPalette::Base, backgroundColor);
196 else {
197 QLinearGradient gradient(0, 0, width(), 0);
198 gradient.setColorAt(0, progressColor);
199 gradient.setColorAt(((double) progress) / 100, progressColor);
200 if (progress != 100)
201 gradient.setColorAt((double) progress / 100 + 0.001, backgroundColor);
202 pallete.setBrush(QPalette::Base, gradient);
203 }
204 m_addressBar->setPalette(pallete);
205 }
206
urlChanged(const QUrl & url)207 void BrowserWindow::urlChanged(const QUrl& url)
208 {
209 m_addressBar->setText(url.toString());
210 m_browser->setFocus();
211 m_browser->view()->setFocus();
212 }
213
openFile()214 void BrowserWindow::openFile()
215 {
216 #ifndef QT_NO_FILEDIALOG
217 static const QString filter("HTML Files (*.htm *.html *.xhtml);;Text Files (*.txt);;Image Files (*.gif *.jpg *.png);;SVG Files (*.svg);;All Files (*)");
218
219 QFileDialog fileDialog(this, tr("Open"), QString(), filter);
220 fileDialog.setAcceptMode(QFileDialog::AcceptOpen);
221 fileDialog.setFileMode(QFileDialog::ExistingFile);
222 fileDialog.setOptions(QFileDialog::ReadOnly);
223
224 if (fileDialog.exec()) {
225 QString selectedFile = fileDialog.selectedFiles()[0];
226 if (!selectedFile.isEmpty())
227 load(selectedFile);
228 }
229 #endif
230 }
231
screenshot()232 void BrowserWindow::screenshot()
233 {
234 QPixmap pixmap = QPixmap::grabWidget(m_browser);
235 QLabel* label = 0;
236 #if !defined(Q_OS_SYMBIAN)
237 label = new QLabel;
238 label->setAttribute(Qt::WA_DeleteOnClose);
239 label->setWindowTitle("Screenshot - Preview");
240 label->setPixmap(pixmap);
241 label->show();
242 #endif
243
244 #ifndef QT_NO_FILEDIALOG
245 QString fileName = QFileDialog::getSaveFileName(label, "Screenshot", QString(), QString("PNG File (.png)"));
246 if (!fileName.isEmpty()) {
247 QRegExp rx("*.png");
248 rx.setCaseSensitivity(Qt::CaseInsensitive);
249 rx.setPatternSyntax(QRegExp::Wildcard);
250
251 if (!rx.exactMatch(fileName))
252 fileName += ".png";
253
254 pixmap.save(fileName, "png");
255 if (label)
256 label->setWindowTitle(QString("Screenshot - Saved at %1").arg(fileName));
257 }
258 #endif
259 }
260
zoomIn()261 void BrowserWindow::zoomIn()
262 {
263 if (m_isZoomTextOnly)
264 m_currentZoom = page()->textZoomFactor();
265 else
266 m_currentZoom = page()->pageZoomFactor();
267
268 int i = m_zoomLevels.indexOf(m_currentZoom);
269 Q_ASSERT(i >= 0);
270 if (i < m_zoomLevels.count() - 1)
271 m_currentZoom = m_zoomLevels[i + 1];
272
273 applyZoom();
274 }
275
zoomOut()276 void BrowserWindow::zoomOut()
277 {
278 if (m_isZoomTextOnly)
279 m_currentZoom = page()->textZoomFactor();
280 else
281 m_currentZoom = page()->pageZoomFactor();
282
283 int i = m_zoomLevels.indexOf(m_currentZoom);
284 Q_ASSERT(i >= 0);
285 if (i > 0)
286 m_currentZoom = m_zoomLevels[i - 1];
287
288 applyZoom();
289 }
290
resetZoom()291 void BrowserWindow::resetZoom()
292 {
293 m_currentZoom = 1;
294 applyZoom();
295 }
296
toggleZoomTextOnly(bool b)297 void BrowserWindow::toggleZoomTextOnly(bool b)
298 {
299 m_isZoomTextOnly = b;
300 }
301
toggleFullScreenMode(bool enable)302 void BrowserWindow::toggleFullScreenMode(bool enable)
303 {
304 bool alreadyEnabled = windowState() & Qt::WindowFullScreen;
305 if (enable ^ alreadyEnabled)
306 setWindowState(windowState() ^ Qt::WindowFullScreen);
307 }
308
toggleFrameFlattening(bool toggle)309 void BrowserWindow::toggleFrameFlattening(bool toggle)
310 {
311 page()->preferences()->setAttribute(QWKPreferences::FrameFlatteningEnabled, toggle);
312 }
313
314
showUserAgentDialog()315 void BrowserWindow::showUserAgentDialog()
316 {
317 updateUserAgentList();
318
319 QDialog dialog(this);
320 dialog.setWindowTitle("Change User Agent");
321 dialog.resize(size().width() * 0.7, dialog.size().height());
322 QVBoxLayout* layout = new QVBoxLayout(&dialog);
323 dialog.setLayout(layout);
324
325 QComboBox* combo = new QComboBox(&dialog);
326 combo->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength);
327 combo->setEditable(true);
328 combo->insertItems(0, m_userAgentList);
329 layout->addWidget(combo);
330
331 int index = combo->findText(page()->customUserAgent());
332 combo->setCurrentIndex(index);
333
334 QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel
335 , Qt::Horizontal, &dialog);
336 connect(buttonBox, SIGNAL(accepted()), &dialog, SLOT(accept()));
337 connect(buttonBox, SIGNAL(rejected()), &dialog, SLOT(reject()));
338 layout->addWidget(buttonBox);
339
340 if (dialog.exec() && !combo->currentText().isEmpty())
341 page()->setCustomUserAgent(combo->currentText());
342 }
343
loadURLListFromFile()344 void BrowserWindow::loadURLListFromFile()
345 {
346 QString selectedFile;
347 #ifndef QT_NO_FILEDIALOG
348 selectedFile = QFileDialog::getOpenFileName(this, tr("Load URL list from file")
349 , QString(), tr("Text Files (*.txt);;All Files (*)"));
350 #endif
351 if (selectedFile.isEmpty())
352 return;
353
354 m_urlLoader = new UrlLoader(this, selectedFile, 0, 0);
355 m_urlLoader->loadNext();
356 }
357
printURL(const QUrl & url)358 void BrowserWindow::printURL(const QUrl& url)
359 {
360 QTextStream output(stdout);
361 output << "Loaded: " << url.toString() << endl;
362 }
363
toggleDisableJavaScript(bool enable)364 void BrowserWindow::toggleDisableJavaScript(bool enable)
365 {
366 page()->preferences()->setAttribute(QWKPreferences::JavascriptEnabled, !enable);
367 }
368
toggleAutoLoadImages(bool enable)369 void BrowserWindow::toggleAutoLoadImages(bool enable)
370 {
371 page()->preferences()->setAttribute(QWKPreferences::AutoLoadImages, !enable);
372 }
373
updateUserAgentList()374 void BrowserWindow::updateUserAgentList()
375 {
376 QFile file(":/useragentlist.txt");
377
378 if (file.open(QIODevice::ReadOnly)) {
379 while (!file.atEnd()) {
380 QString agent = file.readLine().trimmed();
381 if (!m_userAgentList.contains(agent))
382 m_userAgentList << agent;
383 }
384 file.close();
385 }
386
387 Q_ASSERT(!m_userAgentList.isEmpty());
388 QWKPage* wkPage = page();
389 if (!(wkPage->customUserAgent().isEmpty() || m_userAgentList.contains(wkPage->customUserAgent())))
390 m_userAgentList << wkPage->customUserAgent();
391 }
392
applyZoom()393 void BrowserWindow::applyZoom()
394 {
395 if (m_isZoomTextOnly)
396 page()->setTextZoomFactor(m_currentZoom);
397 else
398 page()->setPageZoomFactor(m_currentZoom);
399 }
400
~BrowserWindow()401 BrowserWindow::~BrowserWindow()
402 {
403 delete m_urlLoader;
404 delete m_addressBar;
405 delete m_browser;
406 }
407