Fix the end value of the zoom slider with mouse wheel zoom
[rekonq:nikhilms-mainline.git] / src / webview.cpp
1 /* ============================================================
2 *
3 * This file is a part of the rekonq project
4 *
5 * Copyright (C) 2008-2010 by Andrea Diamantini <adjam7 at gmail dot com>
6 * Copyright (C) 2009-2010 by Lionel Chauvin <megabigbug@yahoo.fr>
7 *
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of
12 * the License or (at your option) version 3 or any later version
13 * accepted by the membership of KDE e.V. (or its successor approved
14 * by the membership of KDE e.V.), which shall act as a proxy
15 * defined in Section 14 of version 3 of the license.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
24 *
25 * ============================================================ */
26
27
28 // Self Includes
29 #include "webview.h"
30 #include "webview.moc"
31
32 // Auto Includes
33 #include "rekonq.h"
34
35 // Local Includes
36 #include "mainwindow.h"
37 #include "mainview.h"
38 #include "webpage.h"
39 #include "bookmarksmanager.h"
40 #include "searchengine.h"
41
42 // KDE Includes
43 #include <KService>
44 #include <KUriFilterData>
45 #include <KStandardShortcut>
46 #include <KMenu>
47 #include <KActionMenu>
48 #include <ktoolinvocation.h>
49
50 // Qt Includes
51 #include <QtCore/QDir>
52
53 #include <QtGui/QAction>
54 #include <QtGui/QContextMenuEvent>
55 #include <QtGui/QWheelEvent>
56 #include <QtGui/QMouseEvent>
57 #include <QtGui/QClipboard>
58 #include <QtGui/QKeyEvent>
59 #include <QtGui/QLayout>
60
61 #include <QtDBus/QDBusConnectionInterface>
62 #include <QtDBus/QDBusInterface>
63 #include <QtDBus/QDBusReply>
64
65
66 WebView::WebView(QWidget* parent)
67         : KWebView(parent, false)
68         , _mousePos(QPoint(0, 0))
69         , _scrollTimer(new QTimer(this))
70         , _VScrollSpeed(0)
71         , _HScrollSpeed(0)
72         , _canEnableAutoScroll(true)
73         , _isAutoScrollEnabled(false)
74 {
75     WebPage *page = new WebPage(this);
76     setPage(page);
77
78     // download system
79     connect(this, SIGNAL(linkShiftClicked(const KUrl &)), page, SLOT(downloadUrl(const KUrl &)));
80     connect(page, SIGNAL(downloadRequested(const QNetworkRequest &)), page, SLOT(downloadRequest(const QNetworkRequest &)));
81
82     // middle click || ctrl + click signal
83     connect(this, SIGNAL(linkMiddleOrCtrlClicked(const KUrl &)), this, SLOT(loadUrlInNewTab(const KUrl &)));
84
85     // loadUrl signal
86     connect(this, SIGNAL(loadUrl(const KUrl &, const Rekonq::OpenType &)),
87             Application::instance(), SLOT(loadUrl(const KUrl &, const Rekonq::OpenType &)));
88
89     // scrolling timer
90     connect(_scrollTimer, SIGNAL(timeout()), this, SLOT(scrollFrameChanged()));
91     _scrollTimer->setInterval(100);
92 }
93
94
95 WebView::~WebView()
96 {
97     delete _scrollTimer;
98     disconnect();
99 }
100
101
102 WebPage *WebView::page()
103 {
104     WebPage *page = qobject_cast<WebPage *>(KWebView::page());
105     return page;
106 }
107
108
109 void WebView::contextMenuEvent(QContextMenuEvent *event)
110 {
111     QWebHitTestResult result = page()->mainFrame()->hitTestContent(event->pos());
112     MainWindow *mainwindow = Application::instance()->mainWindow();
113
114     KMenu menu(this);
115     QAction *a;
116
117     // is a link?
118     if (!result.linkUrl().isEmpty())
119     {
120         // link actions
121         a = new KAction(KIcon("tab-new"), i18n("Open in New &Tab"), this);
122         a->setData(result.linkUrl());
123         connect(a, SIGNAL(triggered(bool)), this, SLOT(openLinkInNewTab()));
124         menu.addAction(a);
125
126         a = new KAction(KIcon("window-new"), i18n("Open in New &Window"), this);
127         a->setData(result.linkUrl());
128         connect(a, SIGNAL(triggered(bool)), this, SLOT(openLinkInNewWindow()));
129         menu.addAction(a);
130
131         menu.addAction(pageAction(KWebPage::DownloadLinkToDisk));
132         menu.addAction(pageAction(KWebPage::CopyLinkToClipboard));
133         menu.addSeparator();
134     }
135
136     // is content editable && selected? Add CUT
137     if (result.isContentEditable() && result.isContentSelected())
138     {
139         // actions for text selected in field
140         menu.addAction(pageAction(KWebPage::Cut));
141     }
142
143     // is content selected) Add COPY
144     if (result.isContentSelected())
145     {
146         a = pageAction(KWebPage::Copy);
147         if (!result.linkUrl().isEmpty())
148             a->setText(i18n("Copy Text")); //for link
149         else
150             a->setText(i18n("Copy"));
151         menu.addAction(a);
152     }
153
154     // is content editable? Add PASTE
155     if (result.isContentEditable())
156     {
157         menu.addAction(pageAction(KWebPage::Paste));
158     }
159
160     // is content selected? Add SEARCH actions
161     if (result.isContentSelected())
162     {
163         KActionMenu *searchMenu = new KActionMenu(KIcon("edit-find"), i18n("Search with"), this);
164
165         foreach(KService::Ptr engine, SearchEngine::favorites())
166         {
167             a = new KAction(engine->name(), this);
168             a->setIcon(Application::icon(SearchEngine::buildQuery(engine, "")));
169             a->setData(engine->entryPath());
170             connect(a, SIGNAL(triggered(bool)), this, SLOT(search()));
171             searchMenu->addAction(a);
172         }
173
174         if (!searchMenu->menu()->isEmpty())
175         {
176             menu.addAction(searchMenu);
177         }
178
179         menu.addSeparator();
180         // TODO Add translate, show translation
181     }
182
183     // is an image?
184     if (!result.pixmap().isNull())
185     {
186         menu.addSeparator();
187
188         // TODO remove copy_this_image action
189         a = new KAction(KIcon("view-media-visualization"), i18n("&View Image"), this);
190         a->setData(result.imageUrl());
191         connect(a, SIGNAL(triggered(Qt::MouseButtons, Qt::KeyboardModifiers)), this, SLOT(viewImage(Qt::MouseButtons, Qt::KeyboardModifiers)));
192         menu.addAction(a);
193
194         menu.addAction(pageAction(KWebPage::DownloadImageToDisk));
195         menu.addAction(pageAction(KWebPage::CopyImageToClipboard));
196         menu.addSeparator();
197     }
198
199     // Open url text in new tab/window
200     if (result.linkUrl().isEmpty())
201     {
202
203         QString text = selectedText();
204         if (text.startsWith(QL1S("http://"))
205                 || text.startsWith(QL1S("https://"))
206                 || text.startsWith(QL1S("www."))
207            )
208         {
209             QString truncatedURL = text;
210             if (text.length() > 18)
211             {
212                 truncatedURL.truncate(15);
213                 truncatedURL += "...";
214             }
215
216             //open selected text url in a new tab
217             a = new KAction(KIcon("tab-new"), i18n("Open '%1' in New Tab", truncatedURL), this);
218             a->setData(QUrl(text));
219             connect(a, SIGNAL(triggered(bool)), this, SLOT(openLinkInNewTab()));
220             menu.addAction(a);
221
222             //open selected text url in a new window
223             a = new KAction(KIcon("window-new"), i18n("Open '%1' in New Window", truncatedURL), this);
224             a->setData(QUrl(text));
225             connect(a, SIGNAL(triggered(bool)), this, SLOT(openLinkInNewWindow()));
226             menu.addAction(a);
227
228             menu.addSeparator();
229         }
230
231     }
232
233     // page actions
234     if (!result.isContentSelected() && result.linkUrl().isEmpty())
235     {
236
237         // navigation
238         QWebHistory *history = page()->history();
239         if (history->canGoBack())
240         {
241             menu.addAction(pageAction(KWebPage::Back));
242         }
243
244         if (history->canGoForward())
245         {
246             menu.addAction(pageAction(KWebPage::Forward));
247         }
248
249         menu.addAction(mainwindow->actionByName("view_redisplay"));
250
251         if (result.pixmap().isNull())
252         {
253             menu.addSeparator();
254
255             menu.addAction(mainwindow->actionByName("new_tab"));
256             menu.addAction(mainwindow->actionByName("new_window"));
257
258             menu.addSeparator();
259
260             //Frame
261             KActionMenu *frameMenu = new KActionMenu(i18n("Current Frame"), this);
262
263             frameMenu->addAction(pageAction(KWebPage::OpenFrameInNewWindow));
264
265             a = new KAction(KIcon("document-print-frame"), i18n("Print Frame"), this);
266             connect(a, SIGNAL(triggered()), this, SLOT(printFrame()));
267             frameMenu->addAction(a);
268
269             menu.addAction(frameMenu);
270
271             menu.addSeparator();
272
273             // Page Actions
274             menu.addAction(pageAction(KWebPage::SelectAll));
275
276             menu.addAction(mainwindow->actionByName(KStandardAction::name(KStandardAction::SaveAs)));
277
278             if (ReKonfig::kgetList())
279             {
280                 a = new KAction(KIcon("kget"), i18n("List All Links"), this);
281                 connect(a, SIGNAL(triggered(bool)), page(), SLOT(downloadAllContentsWithKGet()));
282                 menu.addAction(a);
283             }
284
285             menu.addAction(mainwindow->actionByName("page_source"));
286
287             a = new KAction(KIcon("layer-visible-on"), i18n("Inspect Element"), this);
288             connect(a, SIGNAL(triggered(bool)), this, SLOT(inspect()));
289             menu.addAction(a);
290
291             a = Application::bookmarkProvider()->actionByName("rekonq_add_bookmark");
292             menu.addAction(a);
293         }
294
295         if (mainwindow->isFullScreen())
296         {
297             menu.addSeparator();
298             menu.addAction(mainwindow->actionByName("fullscreen"));
299         }
300     }
301
302     menu.exec(mapToGlobal(event->pos()));
303 }
304
305
306 void WebView::mousePressEvent(QMouseEvent *event)
307 {
308     if (_isAutoScrollEnabled)
309     {
310         setCursor(Qt::ArrowCursor);
311         _VScrollSpeed = 0;
312         _HScrollSpeed = 0;
313         _scrollTimer->stop();
314         _isAutoScrollEnabled = false;
315         return;
316     }
317
318     QWebHitTestResult result = page()->mainFrame()->hitTestContent(event->pos());
319     _canEnableAutoScroll = ReKonfig::autoScroll() && !result.isContentEditable()  && result.linkUrl().isEmpty();
320
321     switch (event->button())
322     {
323     case Qt::XButton1:
324         triggerPageAction(KWebPage::Back);
325         break;
326
327     case Qt::XButton2:
328         triggerPageAction(KWebPage::Forward);
329         break;
330
331     case Qt::MidButton:
332         if (_canEnableAutoScroll && !_isAutoScrollEnabled)
333         {
334             setCursor(KIcon("transform-move").pixmap(32));
335             _clickPos = event->pos();
336             _isAutoScrollEnabled = true;
337         }
338         break;
339
340     default:
341         break;
342     };
343     KWebView::mousePressEvent(event);
344 }
345
346
347 void WebView::mouseMoveEvent(QMouseEvent *event)
348 {
349     _mousePos = event->pos();
350
351     if (_isAutoScrollEnabled)
352     {
353         QPoint r = _mousePos - _clickPos;
354         _HScrollSpeed = r.x() / 2;  // you are too fast..
355         _VScrollSpeed = r.y() / 2;
356         if (!_scrollTimer->isActive())
357             _scrollTimer->start();
358
359         return;
360     }
361
362     if (Application::instance()->mainWindow()->isFullScreen())
363     {
364         if (event->pos().y() >= 0 && event->pos().y() <= 4)
365         {
366             Application::instance()->mainWindow()->setWidgetsVisible(true);
367         }
368         else
369         {
370             Application::instance()->mainWindow()->setWidgetsVisible(false);
371         }
372     }
373     KWebView::mouseMoveEvent(event);
374 }
375
376
377 void WebView::search()
378 {
379     KAction *a = qobject_cast<KAction*>(sender());
380     KService::Ptr engine = KService::serviceByDesktopPath(a->data().toString());
381     KUrl urlSearch = KUrl(SearchEngine::buildQuery(engine, selectedText()));
382
383     emit loadUrl(urlSearch, Rekonq::NewCurrentTab);
384 }
385
386
387 void WebView::printFrame()
388 {
389     Application::instance()->mainWindow()->printRequested(page()->currentFrame());
390 }
391
392
393 void WebView::viewImage(Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers)
394 {
395     KAction *a = qobject_cast<KAction*>(sender());
396     KUrl url(a->data().toUrl());
397
398     if (modifiers & Qt::ControlModifier || buttons == Qt::MidButton)
399     {
400         emit loadUrl(url, Rekonq::SettingOpenTab);
401     }
402     else
403     {
404         emit loadUrl(url, Rekonq::CurrentTab);
405     }
406 }
407
408
409 void WebView::openLinkInNewWindow()
410 {
411     KAction *a = qobject_cast<KAction*>(sender());
412     KUrl url(a->data().toUrl());
413
414     emit loadUrl(url, Rekonq::NewWindow);
415 }
416
417
418 void WebView::openLinkInNewTab()
419 {
420     KAction *a = qobject_cast<KAction*>(sender());
421     KUrl url(a->data().toUrl());
422
423     emit loadUrl(url, Rekonq::SettingOpenTab);
424 }
425
426
427 void WebView::keyPressEvent(QKeyEvent *event)
428 {
429     if (event->modifiers() == Qt::ControlModifier)
430     {
431         if (event->key() == Qt::Key_C)
432         {
433             triggerPageAction(KWebPage::Copy);
434             return;
435         }
436
437         if (event->key() == Qt::Key_A)
438         {
439             triggerPageAction(KWebPage::SelectAll);
440             return;
441         }
442     }
443
444     if (!_canEnableAutoScroll)
445     {
446         KWebView::keyPressEvent(event);
447         return;
448     }
449
450     // Auto Scrolling
451     if (event->modifiers() == Qt::ShiftModifier)
452     {
453         if (event->key() == Qt::Key_Up)
454         {
455             _VScrollSpeed--;
456             if (!_scrollTimer->isActive())
457                 _scrollTimer->start();
458             return;
459         }
460
461         if (event->key() == Qt::Key_Down)
462         {
463             _VScrollSpeed++;
464             if (!_scrollTimer->isActive())
465                 _scrollTimer->start();
466             return;
467         }
468
469         if (event->key() == Qt::Key_Right)
470         {
471             _HScrollSpeed++;
472             if (!_scrollTimer->isActive())
473                 _scrollTimer->start();
474             return;
475         }
476
477         if (event->key() == Qt::Key_Left)
478         {
479             _HScrollSpeed--;
480             if (!_scrollTimer->isActive())
481                 _scrollTimer->start();
482             return;
483         }
484
485         if (_scrollTimer->isActive())
486         {
487             _scrollTimer->stop();
488         }
489         else
490         {
491             if (_VScrollSpeed || _HScrollSpeed)
492                 _scrollTimer->start();
493         }
494     }
495
496     KWebView::keyPressEvent(event);
497 }
498
499 void WebView::wheelEvent(QWheelEvent *event)
500 {
501     KWebView::wheelEvent(event);
502
503
504     // Sync with the zoom slider
505     if (event->modifiers() == Qt::ControlModifier)
506     {
507         // Limits of the slider
508         if (zoomFactor() > 1.9)
509             setZoomFactor(1.9);
510         else if (zoomFactor() < 0.1)
511             setZoomFactor(0.1);
512
513         // Round the factor (Fix slider's end value)
514         int newFactor = zoomFactor() * 10;
515         if ((zoomFactor() * 10 - newFactor) > 0.5)
516             newFactor++;
517
518
519         emit zoomChanged((qreal)newFactor / 10);
520     }
521     
522
523
524
525 }
526
527
528 void WebView::inspect()
529 {
530     QAction *a = Application::instance()->mainWindow()->actionByName("web_inspector");
531     if (a && !a->isChecked())
532         a->trigger();
533     pageAction(QWebPage::InspectElement)->trigger();
534 }
535
536
537 void WebView::loadUrlInNewTab(const KUrl &url)
538 {
539     emit loadUrl(url, Rekonq::SettingOpenTab);
540 }
541
542
543 void WebView::scrollFrameChanged()
544 {
545     // do the scrolling
546     page()->currentFrame()->scroll(_HScrollSpeed, _VScrollSpeed);
547
548     // check if we reached the end
549     int y = page()->currentFrame()->scrollPosition().y();
550     if (y == 0 || y == page()->currentFrame()->scrollBarMaximum(Qt::Vertical))
551         _VScrollSpeed = 0;
552
553     int x = page()->currentFrame()->scrollPosition().x();
554     if (x == 0 || x == page()->currentFrame()->scrollBarMaximum(Qt::Horizontal))
555         _HScrollSpeed = 0;
556 }