Merge commit 'refs/merge-requests/133' 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     connect(tab->view(), SIGNAL(zoomChanged(qreal)), m_parentWindow, SLOT(setZoomSliderFactor(qreal)));
333
334     // connecting webPage signals with mainview
335     connect(tab->view()->page(), SIGNAL(windowCloseRequested()), this, SLOT(windowCloseRequested()));
336     connect(tab->view()->page(), SIGNAL(printRequested(QWebFrame *)), this, SIGNAL(printRequested(QWebFrame *)));
337
338     if (nearParent)
339     {
340         insertTab(currentIndex() + 1, tab, i18n("(Untitled)"));
341         _widgetBar->insertWidget(currentIndex() + 1, bar);
342     }
343     else
344     {
345         addTab(tab, i18n("(Untitled)"));
346         _widgetBar->addWidget(bar);
347     }
348     updateTabBar();
349
350     if (focused)
351     {
352         setCurrentWidget(tab);
353     }
354
355     emit tabsChanged();
356
357     return tab;
358 }
359
360
361 void MainView::newTab()
362 {
363     WebView *w = newWebTab()->view();
364
365     switch (ReKonfig::newTabsBehaviour())
366     {
367     case 0: // new tab page
368         w->load(KUrl("about:home"));
369         break;
370     case 1: // blank page
371         urlBar()->clear();
372         break;
373     case 2: // homepage
374         w->load(KUrl(ReKonfig::homePage()));
375         break;
376     default:
377         break;
378     }
379     urlBar()->setFocus();
380 }
381
382
383 void MainView::reloadAllTabs()
384 {
385     for (int i = 0; i < count(); ++i)
386     {
387         webTab(i)->view()->reload();
388     }
389 }
390
391
392 void MainView::windowCloseRequested()
393 {
394     WebPage *page = qobject_cast<WebPage *>(sender());
395     WebView *view = qobject_cast<WebView *>(page->view());
396     int index = indexOf(view->parentWidget());
397
398     if (index >= 0)
399     {
400         if (count() == 1)
401         {
402             m_parentWindow->close();
403         }
404         else
405         {
406             closeTab(index);
407         }
408         return;
409     }
410     kDebug() << "Invalid tab index" << "line:" << __LINE__;
411 }
412
413
414 void MainView::closeOtherTabs(int index)
415 {
416     if (index < 0)
417         index = currentIndex();
418     if (index < 0 || index >= count())
419         return;
420     
421     for (int i = count() - 1; i > index; --i)
422     {
423         closeTab(i);
424     }
425
426     for (int i = index - 1; i >= 0; --i)
427     {
428         closeTab(i);
429     }
430
431     updateTabBar();
432 }
433
434
435 void MainView::cloneTab(int index)
436 {
437     if (index < 0)
438         index = currentIndex();
439     if (index < 0 || index >= count())
440         return;
441
442     WebTab *tab = newWebTab();
443     KUrl url = webTab(index)->url();
444
445     // workaround against bug in webkit:
446     // only set url if it is not empty
447     // otherwise the current working directory will be used
448     if (!url.isEmpty())
449         tab->view()->setUrl(url);
450
451     updateTabBar();
452 }
453
454
455 // When index is -1 index chooses the current tab
456 void MainView::closeTab(int index, bool del)
457 {
458     // open default homePage if just one tab is opened
459     if (count() == 1)
460     {
461         WebView *w = currentWebTab()->view();
462         
463         if( currentWebTab()->url().protocol() == QL1S("about") )
464             return;
465         
466         switch (ReKonfig::newTabsBehaviour())
467         {
468         case 0: // new tab page
469         case 1: // blank page
470             w->load(KUrl("about:home"));
471             urlBar()->setFocus();
472             break;
473         case 2: // homepage
474             w->load(KUrl(ReKonfig::homePage()));
475             break;
476         default:
477             break;
478         }
479         return;
480     }
481
482     if (index < 0)
483         index = currentIndex();
484     if (index < 0 || index >= count())
485         return;
486
487     WebTab *tab = webTab(index);
488     if (!tab)
489         return;
490
491     if (tab->view()->isModified())
492     {
493         int risp = KMessageBox::warningContinueCancel(this,
494                    i18n("This tab contains changes that have not been submitted.\n"
495                         "Closing the tab will discard these changes.\n"
496                         "Do you really want to close this tab?\n"),
497                    i18n("Closing Modified Tab"), KGuiItem(i18n("Close &Tab"), "tab-close"), KStandardGuiItem::cancel());
498         if (risp != KMessageBox::Continue)
499             return;
500     }
501
502     if (!tab->url().isEmpty())
503     {
504         QString title = tab->view()->title();
505         QString url = tab->url().prettyUrl();
506         HistoryItem item(url, QDateTime::currentDateTime(), title);
507         m_recentlyClosedTabs.removeAll(item);
508         m_recentlyClosedTabs.prepend(item);
509     }
510
511     removeTab(index);
512     updateTabBar();        // UI operation: do it ASAP!!
513
514     UrlBar *urlbar = _widgetBar->urlBar(index);
515     _widgetBar->removeWidget(urlbar);
516
517     _widgetBar->setCurrentIndex(m_currentTabIndex);
518
519     if (del)
520     {
521         tab->deleteLater();    // tab is scheduled for deletion.
522         urlbar->deleteLater();
523     }
524
525     emit tabsChanged();
526 }
527
528
529 void MainView::webViewLoadStarted()
530 {
531     WebView *view = qobject_cast<WebView *>(sender());
532     int index = indexOf(view->parentWidget());
533     if (-1 != index)
534     {
535         QLabel *label = animatedLoading(index, true);
536         if (label->movie())
537         {
538             label->movie()->start();
539         }
540     }
541
542     if (index != currentIndex())
543         return;
544
545     emit browserTabLoading(true);
546     emit showStatusBarMessage(i18n("Loading..."));
547 }
548
549
550 void MainView::webViewLoadFinished(bool ok)
551 {
552     WebView *view = qobject_cast<WebView *>(sender());
553     int index = -1;
554     if (view)
555         index = indexOf(view->parentWidget());
556
557     if (-1 != index)
558     {
559         QLabel *label = animatedLoading(index, true);
560         QMovie *movie = label->movie();
561         if (movie)
562             movie->stop();
563     }
564
565     webViewIconChanged();
566     emit browserTabLoading(false);
567
568     // don't display messages for background tabs
569     if (index != currentIndex())
570     {
571         return;
572     }
573
574     if (ok)
575         emit showStatusBarMessage(i18n("Done"), Rekonq::Success);
576 //     else
577 //         emit showStatusBarMessage(i18n("Failed to load"), Rekonq::Error);
578 }
579
580
581 void MainView::webViewIconChanged()
582 {
583     WebView *view = qobject_cast<WebView *>(sender());
584     int index = indexOf(view->parentWidget());
585     if (-1 != index)
586     {
587         KIcon icon = Application::icon(view->url());
588         QLabel *label = animatedLoading(index, false);
589         QMovie *movie = label->movie();
590         delete movie;
591         label->setMovie(0);
592         label->setPixmap(icon.pixmap(16, 16));
593     }
594 }
595
596
597 void MainView::webViewTitleChanged(const QString &title)
598 {
599     QString viewTitle = title.isEmpty()? i18n("(Untitled)") : title;
600     QString tabTitle = viewTitle;
601     tabTitle.replace('&', "&&");
602     
603     WebView *view = qobject_cast<WebView *>(sender());
604     int index = indexOf(view->parentWidget());
605     if (-1 != index)
606     {
607         setTabText(index, tabTitle);
608     }
609     if (currentIndex() == index)
610     {
611         emit currentTitle(viewTitle);
612     }
613     Application::historyManager()->updateHistoryEntry(view->url(), tabTitle);
614 }
615
616
617 void MainView::webViewUrlChanged(const QUrl &url)
618 {
619     WebView *view = qobject_cast<WebView *>(sender());
620     int index = indexOf(view->parentWidget());
621     if (-1 != index)
622     {
623         tabBar()->setTabData(index, url);
624     }
625     emit tabsChanged();
626 }
627
628
629 void MainView::nextTab()
630 {
631     int next = currentIndex() + 1;
632     if (next == count())
633         next = 0;
634     setCurrentIndex(next);
635 }
636
637
638 void MainView::previousTab()
639 {
640     int next = currentIndex() - 1;
641     if (next < 0)
642         next = count() - 1;
643     setCurrentIndex(next);
644 }
645
646 void MainView::openClosedTabs()
647 {
648     foreach (const HistoryItem &item, recentlyClosedTabs())
649     {
650         Application::instance()->loadUrl( KUrl(item.url), Rekonq::SettingOpenTab);
651     }
652 }
653
654 void MainView::openClosedTab()
655 {
656     KAction *action = qobject_cast<KAction *>(sender());
657     if (action)
658     {
659         Application::instance()->loadUrl(action->data().toUrl(), Rekonq::SettingOpenTab);
660     }
661 }
662
663 void MainView::switchToTab()
664 {
665     // uses the sender to determine the tab index
666     QAction *sender = static_cast<QAction*>(QObject::sender());
667     int index = sender->data().toInt();
668     index -= 1; // to compensate for off by 1 presented to the user
669     if( index < 0 || index >= count() )
670         return;
671     setCurrentIndex( index );
672 }
673
674 QLabel *MainView::animatedLoading(int index, bool addMovie)
675 {
676     if (index == -1)
677         return 0;
678
679     QLabel *label = qobject_cast<QLabel* >(tabBar()->tabButton(index, QTabBar::LeftSide));
680     if (!label)
681     {
682         label = new QLabel(this);
683     }
684     if (addMovie && !label->movie())
685     {
686         QMovie *movie = new QMovie(m_loadingGitPath, QByteArray(), label);
687         movie->setSpeed(50);
688         label->setMovie(movie);
689         movie->start();
690     }
691     tabBar()->setTabButton(index, QTabBar::LeftSide, 0);
692     tabBar()->setTabButton(index, QTabBar::LeftSide, label);
693     return label;
694 }
695
696
697 void MainView::resizeEvent(QResizeEvent *event)
698 {
699     updateTabBar();
700     KTabWidget::resizeEvent(event);
701 }
702
703
704 void MainView::detachTab(int index)
705 {
706     if (index < 0)
707         index = currentIndex();
708     if (index < 0 || index >= count())
709         return;
710
711     WebTab *tab = webTab(index);
712     KUrl u = tab->url();
713     kDebug() << u;
714     if (u.scheme() == QL1S("about"))
715     {
716         closeTab(index);
717         Application::instance()->loadUrl(u, Rekonq::NewWindow);
718     }
719     else
720     {
721         QString label = tab->view()->title();
722         UrlBar *bar = _widgetBar->urlBar(index);
723         closeTab(index, false);
724
725         MainWindow *w = Application::instance()->newMainWindow(false);
726         w->mainView()->addTab(tab, Application::icon(u), label);
727         _widgetBar->insertWidget(0, bar);
728         w->mainView()->updateTabBar();
729     }
730 }