Merge commit 'refs/merge-requests/2275' of git://gitorious.org/rekonq/mainline into...
[rekonq:nikhilms-mainline.git] / src / mainview.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 by Paweł Prażak <pawelprazak at gmail dot com>
7 * Copyright (C) 2009-2010 by Lionel Chauvin <megabigbug@yahoo.fr>
8 * Copyright (C) 2010 by Matthieu Gicquel <matgic78 at gmail dot com>
9 *
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License as
13 * published by the Free Software Foundation; either version 2 of
14 * the License or (at your option) version 3 or any later version
15 * accepted by the membership of KDE e.V. (or its successor approved
16 * by the membership of KDE e.V.), which shall act as a proxy
17 * defined in Section 14 of version 3 of the license.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
26 *
27 * ============================================================ */
28
29
30 // Self Includes
31 #include "mainview.h"
32 #include "mainview.moc"
33
34 // Auto Includes
35 #include "rekonq.h"
36
37 // Local Includes
38 #include "tabbar.h"
39 #include "urlbar.h"
40 #include "sessionmanager.h"
41
42 // KDE Includes
43 #include <KUrl>
44 #include <KAction>
45 #include <KShortcut>
46 #include <KStandardShortcut>
47 #include <KMessageBox>
48 #include <KStandardDirs>
49 #include <KPassivePopup>
50 #include <KLocalizedString>
51
52 // Qt Includes
53 #include <QTimer>
54 #include <QString>
55 #include <QAction>
56 #include <QIcon>
57 #include <QLabel>
58 #include <QMovie>
59 #include <QWidget>
60 #include <QVBoxLayout>
61
62
63 MainView::MainView(MainWindow *parent)
64         : KTabWidget(parent)
65         , _widgetBar(new StackedUrlBar(this))
66         , m_addTabButton(0)
67         , m_currentTabIndex(0)
68         , m_parentWindow(parent)
69 {
70     // setting tabbar
71     TabBar *tabBar = new TabBar(this);
72     m_addTabButton = new QToolButton(this);
73     setTabBar(tabBar);
74
75     // set mouse tracking for tab previews
76     setMouseTracking(true);
77
78     // loading pixmap path
79     m_loadingGitPath = KStandardDirs::locate("appdata" , "pics/loading.mng");
80
81     // connecting tabbar signals
82     connect(tabBar, SIGNAL(closeTab(int)),          this,   SLOT(closeTab(int)));
83     connect(tabBar, SIGNAL(mouseMiddleClick(int)),  this,   SLOT(closeTab(int)));
84     connect(tabBar, SIGNAL(newTabRequest()),        this,   SLOT(newTab()));
85
86     connect(tabBar, SIGNAL(cloneTab(int)),          this,   SLOT(cloneTab(int)));
87     connect(tabBar, SIGNAL(closeOtherTabs(int)),    this,   SLOT(closeOtherTabs(int)));
88     connect(tabBar, SIGNAL(reloadTab(int)),         this,   SLOT(reloadTab(int)));
89     connect(tabBar, SIGNAL(reloadAllTabs()),        this,   SLOT(reloadAllTabs()));
90     connect(tabBar, SIGNAL(detachTab(int)),         this,   SLOT(detachTab(int)));
91
92     connect(tabBar, SIGNAL(tabCloseRequested(int)), this,   SLOT(closeTab(int)));
93     connect(tabBar, SIGNAL(tabMoved(int, int)),     _widgetBar, SLOT(moveBar(int, int)));
94
95     // current page index changing
96     connect(this, SIGNAL(currentChanged(int)), this, SLOT(currentChanged(int)));
97
98     QTimer::singleShot(0, this, SLOT(postLaunch()));
99 }
100
101
102 MainView::~MainView()
103 {
104     delete _widgetBar;
105     delete m_addTabButton;
106 }
107
108
109 void MainView::postLaunch()
110 {
111     QStringList list = Application::sessionManager()->closedSites();
112     foreach(const QString &line, list)
113     {
114         if(line.startsWith( QL1S("about") ))
115             break;
116         QString title = line;
117         QString url = title;
118         HistoryItem item(url, QDateTime::currentDateTime(), title);
119         m_recentlyClosedTabs.removeAll(item);
120         m_recentlyClosedTabs.prepend(item);
121     }
122     
123     // Session Manager
124     connect(this, SIGNAL(tabsChanged()), Application::sessionManager(), SLOT(saveSession()));
125     connect(this, SIGNAL(currentChanged(int)), Application::sessionManager(), SLOT(saveSession()));
126
127     m_addTabButton->setDefaultAction(m_parentWindow->actionByName("new_tab"));
128
129     m_addTabButton->setAutoRaise(true);
130     m_addTabButton->setToolButtonStyle(Qt::ToolButtonIconOnly);
131 }
132
133
134 void MainView::updateTabButtonPosition()
135 {
136     static bool ButtonInCorner = false;
137
138     int tabWidgetWidth = frameSize().width();
139     int tabBarWidth = tabBar()->tabSizeHint(0).width() * tabBar()->count();
140
141     if (tabBarWidth + m_addTabButton->width() > tabWidgetWidth)
142     {
143         if (ButtonInCorner)
144             return;
145         setCornerWidget(m_addTabButton);
146         ButtonInCorner = true;
147     }
148     else
149     {
150         if (ButtonInCorner)
151         {
152             setCornerWidget(0);
153             m_addTabButton->show();
154             ButtonInCorner = false;
155         }
156
157         // detecting X position
158         int newPosX = tabBarWidth;
159         int tabWidthHint = tabBar()->tabSizeHint(0).width();
160         if (tabWidthHint < sizeHint().width() / 4)
161             newPosX = tabWidgetWidth - m_addTabButton->width();
162
163         m_addTabButton->move(newPosX, 0);
164     }
165 }
166
167
168 TabBar *MainView::tabBar() const
169 {
170     TabBar *tabBar = qobject_cast<TabBar *>(KTabWidget::tabBar());
171     return tabBar;
172 }
173
174
175 UrlBar *MainView::urlBar() const
176 {
177     return _widgetBar->urlBar(m_currentTabIndex);
178 }
179
180
181 WebTab *MainView::currentWebTab() const
182 {
183     return webTab(currentIndex());
184 }
185
186
187 void MainView::updateTabBar()
188 {
189     if (ReKonfig::alwaysShowTabBar())
190     {
191         if (!isTabBarHidden())
192         {
193             if (tabBar()->isHidden())
194             {
195                 tabBar()->show();
196                 m_addTabButton->show();
197             }
198             updateTabButtonPosition();
199         }
200         return;
201     }
202
203     if (tabBar()->count() == 1)
204     {
205         tabBar()->hide();
206         m_addTabButton->hide();
207     }
208     else if (!isTabBarHidden())
209     {
210         if (tabBar()->isHidden())
211         {
212             tabBar()->show();
213             m_addTabButton->show();
214         }
215         updateTabButtonPosition();
216     }
217 }
218
219
220 void MainView::setTabBarHidden(bool hide)
221 {
222     m_addTabButton->setVisible(!hide);
223     KTabWidget::setTabBarHidden(hide);
224 }
225
226
227 void MainView::webReload()
228 {
229     WebTab *webTab = currentWebTab();
230     QAction *action = webTab->view()->page()->action(QWebPage::Reload);
231     action->trigger();
232 }
233
234
235 void MainView::webStop()
236 {
237     WebTab *webTab = currentWebTab();
238     QAction *action = webTab->view()->page()->action(QWebPage::Stop);
239     action->trigger();
240 }
241
242
243 // When index is -1 index chooses the current tab
244 void MainView::reloadTab(int index)
245 {
246     if (index < 0)
247         index = currentIndex();
248     if (index < 0 || index >= count())
249         return;
250
251     webTab(index)->view()->reload();
252 }
253
254
255 // this slot is called on tab switching
256 void MainView::currentChanged(int index)
257 {
258     // retrieve the webview related to the index
259     WebTab *tab = this->webTab(index);
260     if (!tab)
261         return;
262
263     // retrieve the old webview (that where we move from)
264     WebTab *oldTab = this->webTab(m_currentTabIndex);
265
266     // set current index
267     m_currentTabIndex = index;
268
269     if (oldTab)
270     {
271         // disconnecting webpage from mainview
272         disconnect(oldTab->page(), SIGNAL(statusBarMessage(const QString&)),
273                    this, SIGNAL(showStatusBarMessage(const QString&)));
274         disconnect(oldTab->page(), SIGNAL(linkHovered(const QString&, const QString&, const QString&)),
275                    this, SIGNAL(linkHovered(const QString&)));
276     }
277
278     connect(tab->page(), SIGNAL(statusBarMessage(const QString&)),
279             this, SIGNAL(showStatusBarMessage(const QString&)));
280     connect(tab->page(), SIGNAL(linkHovered(const QString&, const QString&, const QString&)),
281             this, SIGNAL(linkHovered(const QString&)));
282
283     emit currentTitle(tab->view()->title());
284     _widgetBar->setCurrentIndex(index);
285
286     // clean up "status bar"
287     emit showStatusBarMessage(QString());
288
289     // notify UI to eventually switch stop/reload button
290     int progr = tab->progress();
291     if (progr == 0)
292         emit browserTabLoading(false);
293     else
294         emit browserTabLoading(true);
295
296     // update zoom slider
297     if (!Application::instance()->mainWindowList().isEmpty())
298         Application::instance()->mainWindow()->setZoomSliderFactor(tab->view()->zoomFactor());
299
300     // set focus to the current webview
301     if (tab->url().scheme() == QL1S("about"))
302         _widgetBar->currentWidget()->setFocus();
303     else
304         tab->view()->setFocus();
305 }
306
307
308 WebTab *MainView::webTab(int index) const
309 {
310     WebTab *tab = qobject_cast<WebTab *>(this->widget(index));
311     if (tab)
312     {
313         return tab;
314     }
315
316     kDebug() << "WebTab with index " << index << "not found. Returning NULL." ;
317     return 0;
318 }
319
320
321 WebTab *MainView::newWebTab(bool focused, bool nearParent)
322 {
323     WebTab* tab = new WebTab(this);
324     UrlBar *bar = new UrlBar(tab);
325
326     // connecting webview with mainview
327     connect(tab->view(), SIGNAL(loadStarted()), this, SLOT(webViewLoadStarted()));
328     connect(tab->view(), SIGNAL(loadFinished(bool)), this, SLOT(webViewLoadFinished(bool)));
329     connect(tab->view(), SIGNAL(iconChanged()), this, SLOT(webViewIconChanged()));
330     connect(tab->view(), SIGNAL(titleChanged(const QString &)), this, SLOT(webViewTitleChanged(const QString &)));
331     connect(tab->view(), SIGNAL(urlChanged(const QUrl &)), this, SLOT(webViewUrlChanged(const QUrl &)));
332
333     // connecting webPage signals with mainview
334     connect(tab->view()->page(), SIGNAL(windowCloseRequested()), this, SLOT(windowCloseRequested()));
335     connect(tab->view()->page(), SIGNAL(printRequested(QWebFrame *)), this, SIGNAL(printRequested(QWebFrame *)));
336
337     if (nearParent)
338     {
339         insertTab(currentIndex() + 1, tab, i18n("(Untitled)"));
340         _widgetBar->insertWidget(currentIndex() + 1, bar);
341     }
342     else
343     {
344         addTab(tab, i18n("(Untitled)"));
345         _widgetBar->addWidget(bar);
346     }
347     updateTabBar();
348
349     if (focused)
350     {
351         setCurrentWidget(tab);
352     }
353
354     emit tabsChanged();
355
356     return tab;
357 }
358
359
360 void MainView::newTab()
361 {
362     WebView *w = newWebTab()->view();
363
364     switch (ReKonfig::newTabsBehaviour())
365     {
366     case 0: // new tab page
367         w->load(KUrl("about:home"));
368         break;
369     case 1: // blank page
370         urlBar()->clear();
371         break;
372     case 2: // homepage
373         w->load(KUrl(ReKonfig::homePage()));
374         break;
375     default:
376         break;
377     }
378     urlBar()->setFocus();
379 }
380
381
382 void MainView::reloadAllTabs()
383 {
384     for (int i = 0; i < count(); ++i)
385     {
386         webTab(i)->view()->reload();
387     }
388 }
389
390
391 void MainView::windowCloseRequested()
392 {
393     WebPage *page = qobject_cast<WebPage *>(sender());
394     WebView *view = qobject_cast<WebView *>(page->view());
395     int index = indexOf(view->parentWidget());
396
397     if (index >= 0)
398     {
399         if (count() == 1)
400         {
401             m_parentWindow->close();
402         }
403         else
404         {
405             closeTab(index);
406         }
407         return;
408     }
409     kDebug() << "Invalid tab index" << "line:" << __LINE__;
410 }
411
412
413 void MainView::closeOtherTabs(int index)
414 {
415     if (-1 == index)
416         return;
417
418     for (int i = count() - 1; i > index; --i)
419     {
420         closeTab(i);
421     }
422
423     for (int i = index - 1; i >= 0; --i)
424     {
425         closeTab(i);
426     }
427
428     updateTabBar();
429 }
430
431
432 // When index is -1 index chooses the current tab
433 void MainView::cloneTab(int index)
434 {
435     if (index < 0)
436         index = currentIndex();
437     if (index < 0 || index >= count())
438         return;
439
440     WebTab *tab = newWebTab();
441     KUrl url = webTab(index)->url();
442
443     // workaround against bug in webkit:
444     // only set url if it is not empty
445     // otherwise the current working directory will be used
446     if (!url.isEmpty())
447         tab->view()->setUrl(url);
448
449     updateTabBar();
450 }
451
452
453 // When index is -1 index chooses the current tab
454 void MainView::closeTab(int index, bool del)
455 {
456     // open default homePage if just one tab is opened
457     if (count() == 1)
458     {
459         WebView *w = currentWebTab()->view();
460         
461         if( currentWebTab()->url().protocol() == QL1S("about") )
462             return;
463         
464         switch (ReKonfig::newTabsBehaviour())
465         {
466         case 0: // new tab page
467         case 1: // blank page
468             w->load(KUrl("about:home"));
469             urlBar()->setFocus();
470             break;
471         case 2: // homepage
472             w->load(KUrl(ReKonfig::homePage()));
473             break;
474         default:
475             break;
476         }
477         return;
478     }
479
480     if (index < 0)
481         index = currentIndex();
482     if (index < 0 || index >= count())
483         return;
484
485     WebTab *tab = webTab(index);
486     if (!tab)
487         return;
488
489     if (tab->view()->isModified())
490     {
491         int risp = KMessageBox::warningContinueCancel(this,
492                    i18n("This tab contains changes that have not been submitted.\n"
493                         "Closing the tab will discard these changes.\n"
494                         "Do you really want to close this tab?\n"),
495                    i18n("Closing Modified Tab"), KGuiItem(i18n("Close &Tab"), "tab-close"), KStandardGuiItem::cancel());
496         if (risp != KMessageBox::Continue)
497             return;
498     }
499
500     if (!tab->url().isEmpty())
501     {
502         QString title = tab->view()->title();
503         QString url = tab->url().prettyUrl();
504         HistoryItem item(url, QDateTime::currentDateTime(), title);
505         m_recentlyClosedTabs.removeAll(item);
506         m_recentlyClosedTabs.prepend(item);
507     }
508
509     removeTab(index);
510     updateTabBar();        // UI operation: do it ASAP!!
511
512     UrlBar *urlbar = _widgetBar->urlBar(index);
513     _widgetBar->removeWidget(urlbar);
514
515     _widgetBar->setCurrentIndex(m_currentTabIndex);
516
517     if (del)
518     {
519         tab->deleteLater();    // tab is scheduled for deletion.
520         urlbar->deleteLater();
521     }
522
523     emit tabsChanged();
524 }
525
526
527 void MainView::webViewLoadStarted()
528 {
529     WebView *view = qobject_cast<WebView *>(sender());
530     int index = indexOf(view->parentWidget());
531     if (-1 != index)
532     {
533         QLabel *label = animatedLoading(index, true);
534         if (label->movie())
535         {
536             label->movie()->start();
537         }
538     }
539
540     if (index != currentIndex())
541         return;
542
543     emit browserTabLoading(true);
544     emit showStatusBarMessage(i18n("Loading..."));
545 }
546
547
548 void MainView::webViewLoadFinished(bool ok)
549 {
550     WebView *view = qobject_cast<WebView *>(sender());
551     int index = -1;
552     if (view)
553         index = indexOf(view->parentWidget());
554
555     if (-1 != index)
556     {
557         QLabel *label = animatedLoading(index, true);
558         QMovie *movie = label->movie();
559         if (movie)
560             movie->stop();
561     }
562
563     webViewIconChanged();
564     emit browserTabLoading(false);
565
566     // don't display messages for background tabs
567     if (index != currentIndex())
568     {
569         return;
570     }
571
572     if (ok)
573         emit showStatusBarMessage(i18n("Done"), Rekonq::Success);
574 //     else
575 //         emit showStatusBarMessage(i18n("Failed to load"), Rekonq::Error);
576 }
577
578
579 void MainView::webViewIconChanged()
580 {
581     WebView *view = qobject_cast<WebView *>(sender());
582     int index = indexOf(view->parentWidget());
583     if (-1 != index)
584     {
585         KIcon icon = Application::icon(view->url());
586         QLabel *label = animatedLoading(index, false);
587         QMovie *movie = label->movie();
588         delete movie;
589         label->setMovie(0);
590         label->setPixmap(icon.pixmap(16, 16));
591     }
592 }
593
594
595 void MainView::webViewTitleChanged(const QString &title)
596 {
597     QString tabTitle = title;
598     if (title.isEmpty())
599     {
600         tabTitle = i18n("(Untitled)");
601     }
602     WebView *view = qobject_cast<WebView *>(sender());
603     int index = indexOf(view->parentWidget());
604     if (-1 != index)
605     {
606         setTabText(index, tabTitle);
607     }
608     if (currentIndex() == index)
609     {
610         emit currentTitle(tabTitle);
611     }
612     Application::historyManager()->updateHistoryEntry(view->url(), tabTitle);
613 }
614
615
616 void MainView::webViewUrlChanged(const QUrl &url)
617 {
618     WebView *view = qobject_cast<WebView *>(sender());
619     int index = indexOf(view->parentWidget());
620     if (-1 != index)
621     {
622         tabBar()->setTabData(index, url);
623     }
624     emit tabsChanged();
625 }
626
627
628 void MainView::nextTab()
629 {
630     int next = currentIndex() + 1;
631     if (next == count())
632         next = 0;
633     setCurrentIndex(next);
634 }
635
636
637 void MainView::previousTab()
638 {
639     int next = currentIndex() - 1;
640     if (next < 0)
641         next = count() - 1;
642     setCurrentIndex(next);
643 }
644
645 void MainView::openClosedTabs()
646 {
647     foreach (const HistoryItem &item, recentlyClosedTabs())
648     {
649         Application::instance()->loadUrl( KUrl(item.url), Rekonq::SettingOpenTab);
650     }
651 }
652
653 void MainView::openClosedTab()
654 {
655     KAction *action = qobject_cast<KAction *>(sender());
656     if (action)
657     {
658         Application::instance()->loadUrl(action->data().toUrl(), Rekonq::SettingOpenTab);
659     }
660 }
661
662 void MainView::switchToTab()
663 {
664     // uses the sender to determine the tab index
665     QAction *sender = static_cast<QAction*>(QObject::sender());
666     int index = sender->data().toInt();
667     index -= 1; // to compensate for off by 1 presented to the user
668     if( index < 0 || index >= count() )
669         return;
670     setCurrentIndex( index );
671 }
672
673 QLabel *MainView::animatedLoading(int index, bool addMovie)
674 {
675     if (index == -1)
676         return 0;
677
678     QLabel *label = qobject_cast<QLabel* >(tabBar()->tabButton(index, QTabBar::LeftSide));
679     if (!label)
680     {
681         label = new QLabel(this);
682     }
683     if (addMovie && !label->movie())
684     {
685         QMovie *movie = new QMovie(m_loadingGitPath, QByteArray(), label);
686         movie->setSpeed(50);
687         label->setMovie(movie);
688         movie->start();
689     }
690     tabBar()->setTabButton(index, QTabBar::LeftSide, 0);
691     tabBar()->setTabButton(index, QTabBar::LeftSide, label);
692     return label;
693 }
694
695
696 void MainView::resizeEvent(QResizeEvent *event)
697 {
698     updateTabBar();
699     KTabWidget::resizeEvent(event);
700 }
701
702
703 void MainView::detachTab(int index)
704 {
705     if (index < 0)
706         index = currentIndex();
707     if (index < 0 || index >= count())
708         return;
709
710     WebTab *tab = webTab(index);
711     KUrl u = tab->url();
712     kDebug() << u;
713     if (u.scheme() == QL1S("about"))
714     {
715         closeTab(index);
716         Application::instance()->loadUrl(u, Rekonq::NewWindow);
717     }
718     else
719     {
720         QString label = tab->view()->title();
721         UrlBar *bar = _widgetBar->urlBar(index);
722         closeTab(index, false);
723
724         MainWindow *w = Application::instance()->newMainWindow(false);
725         w->mainView()->addTab(tab, Application::icon(u), label);
726         _widgetBar->insertWidget(0, bar);
727         w->mainView()->updateTabBar();
728     }
729 }