From c0190e41f7f2e5fe30fa8556fa35f43950afbfdb Mon Sep 17 00:00:00 2001 From: adjam Date: Sun, 3 May 2009 23:51:22 +0000 Subject: Importing recode (rekonq code). I'm sorry I coudn't perform this with gitsvn or tailor.. but I cannot lose all the evening just for this. And I need to sleep now.. git-svn-id: svn+ssh://svn.kde.org/home/kde/trunk/playground/network/rekonq@963146 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- src/CMakeLists.txt | 63 ++ src/application.cpp | 206 ++++++ src/application.h | 115 ++++ src/autosaver.cpp | 90 +++ src/autosaver.h | 55 ++ src/bookmarks.cpp | 278 ++++++++ src/bookmarks.h | 238 +++++++ src/cookiejar.cpp | 841 ++++++++++++++++++++++++ src/cookiejar.h | 200 ++++++ src/cookies.ui | 107 ++++ src/cookiesexceptions.ui | 179 ++++++ src/download.cpp | 224 +++++++ src/download.h | 141 ++++ src/edittableview.cpp | 61 ++ src/edittableview.h | 42 ++ src/edittreeview.cpp | 61 ++ src/edittreeview.h | 41 ++ src/findbar.cpp | 137 ++++ src/findbar.h | 61 ++ src/history.cpp | 1459 ++++++++++++++++++++++++++++++++++++++++++ src/history.h | 402 ++++++++++++ src/history.ui | 101 +++ src/lineedit.cpp | 85 +++ src/lineedit.h | 50 ++ src/main.cpp | 98 +++ src/mainview.cpp | 716 +++++++++++++++++++++ src/mainview.h | 198 ++++++ src/mainwindow.cpp | 930 +++++++++++++++++++++++++++ src/mainwindow.h | 153 +++++ src/modelmenu.cpp | 228 +++++++ src/modelmenu.h | 92 +++ src/networkaccessmanager.cpp | 165 +++++ src/networkaccessmanager.h | 47 ++ src/panelhistory.cpp | 95 +++ src/panelhistory.h | 55 ++ src/password.ui | 69 ++ src/proxy.ui | 62 ++ src/rekonq.kcfg | 119 ++++ src/rekonq.kcfgc | 5 + src/rekonqui.rc | 104 +++ src/searchbar.cpp | 129 ++++ src/searchbar.h | 67 ++ src/settings.cpp | 212 ++++++ src/settings.h | 55 ++ src/settings_fonts.ui | 97 +++ src/settings_general.ui | 142 ++++ src/settings_privacy.ui | 168 +++++ src/settings_proxy.ui | 152 +++++ src/settings_webkit.ui | 134 ++++ src/sidepanel.cpp | 53 ++ src/sidepanel.h | 50 ++ src/stackedurlbar.cpp | 152 +++++ src/stackedurlbar.h | 64 ++ src/tabbar.cpp | 145 +++++ src/tabbar.h | 78 +++ src/urlbar.cpp | 240 +++++++ src/urlbar.h | 99 +++ src/webview.cpp | 504 +++++++++++++++ src/webview.h | 155 +++++ 59 files changed, 11069 insertions(+) create mode 100644 src/CMakeLists.txt create mode 100644 src/application.cpp create mode 100644 src/application.h create mode 100644 src/autosaver.cpp create mode 100644 src/autosaver.h create mode 100644 src/bookmarks.cpp create mode 100644 src/bookmarks.h create mode 100644 src/cookiejar.cpp create mode 100644 src/cookiejar.h create mode 100644 src/cookies.ui create mode 100644 src/cookiesexceptions.ui create mode 100644 src/download.cpp create mode 100644 src/download.h create mode 100644 src/edittableview.cpp create mode 100644 src/edittableview.h create mode 100644 src/edittreeview.cpp create mode 100644 src/edittreeview.h create mode 100644 src/findbar.cpp create mode 100644 src/findbar.h create mode 100644 src/history.cpp create mode 100644 src/history.h create mode 100644 src/history.ui create mode 100644 src/lineedit.cpp create mode 100644 src/lineedit.h create mode 100644 src/main.cpp create mode 100644 src/mainview.cpp create mode 100644 src/mainview.h create mode 100644 src/mainwindow.cpp create mode 100644 src/mainwindow.h create mode 100644 src/modelmenu.cpp create mode 100644 src/modelmenu.h create mode 100644 src/networkaccessmanager.cpp create mode 100644 src/networkaccessmanager.h create mode 100644 src/panelhistory.cpp create mode 100644 src/panelhistory.h create mode 100644 src/password.ui create mode 100644 src/proxy.ui create mode 100644 src/rekonq.kcfg create mode 100644 src/rekonq.kcfgc create mode 100644 src/rekonqui.rc create mode 100644 src/searchbar.cpp create mode 100644 src/searchbar.h create mode 100644 src/settings.cpp create mode 100644 src/settings.h create mode 100644 src/settings_fonts.ui create mode 100644 src/settings_general.ui create mode 100644 src/settings_privacy.ui create mode 100644 src/settings_proxy.ui create mode 100644 src/settings_webkit.ui create mode 100644 src/sidepanel.cpp create mode 100644 src/sidepanel.h create mode 100644 src/stackedurlbar.cpp create mode 100644 src/stackedurlbar.h create mode 100644 src/tabbar.cpp create mode 100644 src/tabbar.h create mode 100644 src/urlbar.cpp create mode 100644 src/urlbar.h create mode 100644 src/webview.cpp create mode 100644 src/webview.h (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 00000000..a64cc747 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,63 @@ +SET( rekonq_SRCS + autosaver.cpp + application.cpp + mainwindow.cpp + mainview.cpp + tabbar.cpp + cookiejar.cpp + edittableview.cpp + edittreeview.cpp + history.cpp + download.cpp + bookmarks.cpp + modelmenu.cpp + networkaccessmanager.cpp + urlbar.cpp + findbar.cpp + searchbar.cpp + settings.cpp + webview.cpp + main.cpp + sidepanel.cpp + panelhistory.cpp + lineedit.cpp + stackedurlbar.cpp +) + +KDE4_ADD_UI_FILES( rekonq_SRCS + cookies.ui + cookiesexceptions.ui + history.ui + password.ui + proxy.ui + settings_general.ui + settings_fonts.ui + settings_privacy.ui + settings_proxy.ui + settings_webkit.ui + ) + +KDE4_ADD_KCFG_FILES( rekonq_SRCS rekonq.kcfgc ) + +### ------------------------------------------ + +ADD_DEFINITIONS( ${KDE4_DEFINITIONS} ) + +KDE4_ADD_EXECUTABLE( rekonq ${rekonq_SRCS} ) + +TARGET_LINK_LIBRARIES( rekonq + ${QT_LIBRARIES} + ${QT_QTNETWORK_LIBRARY} + ${QT_QTWEBKIT_LIBRARY} + ${QT_QTUITOOLS_LIBRARY} + ${KDE4_KUTILS_LIBS} + ${KDE4_KDEUI_LIBS} + ${KDE4_KIO_LIBS} +) + +INSTALL( TARGETS rekonq ${INSTALL_TARGETS_DEFAULT_ARGS} ) + +########### install files ############### + +INSTALL( FILES rekonq.kcfg DESTINATION ${KCFG_INSTALL_DIR} ) +INSTALL( FILES rekonqui.rc DESTINATION ${DATA_INSTALL_DIR}/rekonq ) diff --git a/src/application.cpp b/src/application.cpp new file mode 100644 index 00000000..ec24691e --- /dev/null +++ b/src/application.cpp @@ -0,0 +1,206 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2008 by Andrea Diamantini +* Copyright (C) 2009 by Paweł Prażak +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + + +// Self Includes +#include "application.h" +#include "application.moc" + +// Auto Includes +#include "rekonq.h" + +// Local Includes +#include "mainwindow.h" +#include "cookiejar.h" +#include "history.h" +#include "networkaccessmanager.h" +#include "mainview.h" +#include "webview.h" +#include "download.h" + +// KDE Includes +#include +#include +#include +#include +#include +#include + +// Qt Includes +#include +#include + + +QPointer Application::s_historyManager; +QPointer Application::s_networkAccessManager; +QPointer Application::s_downloadManager; +QPointer Application::s_bookmarkProvider; + + + +Application::Application() + : KUniqueApplication() +{ +} + + +Application::~Application() +{ + delete m_mainWindow; + delete s_bookmarkProvider; + delete s_networkAccessManager; + delete s_historyManager; +} + + +int Application::newInstance() +{ + KCmdLineArgs::setCwd(QDir::currentPath().toUtf8()); + KCmdLineArgs* args = KCmdLineArgs::parsedArgs(); + + if (!m_mainWindow) + { + m_mainWindow = new MainWindow(); + + m_mainWindow->setObjectName("MainWindow"); + setWindowIcon(KIcon("rekonq")); + + m_mainWindow->show(); + + QTimer::singleShot(0, this, SLOT(postLaunch())); + } + + if (args->count() > 0) + { + for (int i = 0; i < args->count(); ++i) + { + KUrl url = MainWindow::guessUrlFromString(args->arg(i)); + newWebView(); + mainWindow()->loadUrl(url); + } + args->clear(); + } + else + { + newWebView(); + mainWindow()->slotHome(); + } + + return 0; +} + + +Application *Application::instance() +{ + return (static_cast(QCoreApplication::instance())); +} + + +void Application::postLaunch() +{ + // set Icon Database Path to store "favicons" associated with web sites + QString directory = KStandardDirs::locateLocal("cache" , "" , true); + if (directory.isEmpty()) + { + directory = QDir::homePath() + QLatin1String("/.") + QCoreApplication::applicationName(); + } + QWebSettings::setIconDatabasePath(directory); + + Application::historyManager(); +} + + + +void Application::slotSaveConfiguration() const +{ + ReKonfig::self()->writeConfig(); +} + + +MainWindow *Application::mainWindow() +{ + return m_mainWindow; +} + + +WebView *Application::newWebView(Rekonq::OpenType type) +{ + return m_mainWindow->mainView()->newWebView(type); +} + + +CookieJar *Application::cookieJar() +{ + return (CookieJar*)networkAccessManager()->cookieJar(); +} + + +NetworkAccessManager *Application::networkAccessManager() +{ + if (!s_networkAccessManager) + { + s_networkAccessManager = new NetworkAccessManager(); + s_networkAccessManager->setCookieJar(new CookieJar); + } + return s_networkAccessManager; +} + + +HistoryManager *Application::historyManager() +{ + if (!s_historyManager) + { + s_historyManager = new HistoryManager(); + QWebHistoryInterface::setDefaultInterface(s_historyManager); + } + return s_historyManager; +} + + +DownloadManager *Application::downloadManager() +{ + if (!s_downloadManager) + { + s_downloadManager = new DownloadManager(); + } + return s_downloadManager; +} + + +BookmarkProvider *Application::bookmarkProvider() +{ + if (!s_bookmarkProvider) + { + s_bookmarkProvider = new BookmarkProvider(); + } + return s_bookmarkProvider; +} + + +KIcon Application::icon(const KUrl &url) const +{ + KIcon icon = KIcon(QWebSettings::iconForUrl(url)); + if (icon.isNull()) + { + icon = KIcon("kde"); + } + return icon; +} diff --git a/src/application.h b/src/application.h new file mode 100644 index 00000000..61f4af81 --- /dev/null +++ b/src/application.h @@ -0,0 +1,115 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2008 by Andrea Diamantini +* Copyright (C) 2009 by Paweł Prażak +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + + +#ifndef APPLICATION_H +#define APPLICATION_H + +// Qt Includes +#include + +// KDE Includes +#include +#include +#include +#include +#include +#include +#include + + +// Forward Declarations +class KIcon; +class KUrl; +class BookmarkProvider; +class CookieJar; +class DownloadManager; +class HistoryManager; +class MainWindow; +class NetworkAccessManager; +class WebView; + + +namespace Rekonq +{ + /** + * @short Open link options + * Different modes of opening new tab + */ + enum OpenType + { + Default, ///< open url according to users settings + New, ///< open url in new tab and make it current + Background ///< open url in new tab in background + }; +} + + +/** + * + */ +class Application : public KUniqueApplication +{ + Q_OBJECT + +public: + Application(); + ~Application(); + int newInstance(); + static Application *instance(); + + MainWindow *mainWindow(); + WebView* newWebView(Rekonq::OpenType type = Rekonq::Default); + + KIcon icon(const KUrl &url) const; + + static HistoryManager *historyManager(); + static CookieJar *cookieJar(); + static NetworkAccessManager *networkAccessManager(); + static DownloadManager *downloadManager(); + static BookmarkProvider *bookmarkProvider(); + +public slots: + /** + * Save application's configuration + * @see ReKonfig::self()->writeConfig(); + */ + void slotSaveConfiguration() const; + + +private slots: + + /** + * Any actions that can be delayed until the window is visible + */ + void postLaunch(); + + +private: + static QPointer s_historyManager; + static QPointer s_networkAccessManager; + static QPointer s_downloadManager; + static QPointer s_bookmarkProvider; + + QPointer m_mainWindow; +}; + +#endif // APPLICATION_H diff --git a/src/autosaver.cpp b/src/autosaver.cpp new file mode 100644 index 00000000..25bf9016 --- /dev/null +++ b/src/autosaver.cpp @@ -0,0 +1,90 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2007-2008 Trolltech ASA. All rights reserved +* Copyright (C) 2008-2009 by Andrea Diamantini +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + + +// Local Includes +#include "autosaver.h" + +// KDE Includes +#include + +// Qt Includes +#include + + +#define AUTOSAVE_IN 1000 * 3 // seconds +#define MAXWAIT 1000 * 15 // seconds + + +AutoSaver::AutoSaver(QObject *parent) : QObject(parent) +{ + Q_ASSERT(parent); +} + + +AutoSaver::~AutoSaver() +{ + if (m_timer.isActive()) + kWarning() << "AutoSaver: still active when destroyed, changes not saved."; +} + + +void AutoSaver::changeOccurred() +{ + if (m_firstChange.isNull()) + m_firstChange.start(); + + if (m_firstChange.elapsed() > MAXWAIT) + { + saveIfNeccessary(); + } + else + { + m_timer.start(AUTOSAVE_IN, this); + } +} + + +void AutoSaver::timerEvent(QTimerEvent *event) +{ + if (event->timerId() == m_timer.timerId()) + { + saveIfNeccessary(); + } + else + { + QObject::timerEvent(event); + } +} + + +void AutoSaver::saveIfNeccessary() +{ + if (!m_timer.isActive()) + return; + m_timer.stop(); + m_firstChange = QTime(); + if (!QMetaObject::invokeMethod(parent(), "save", Qt::DirectConnection)) + { + kWarning() << "AutoSaver: error invoking slot save() on parent"; + } +} + diff --git a/src/autosaver.h b/src/autosaver.h new file mode 100644 index 00000000..8931da13 --- /dev/null +++ b/src/autosaver.h @@ -0,0 +1,55 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2007-2008 Trolltech ASA. All rights reserved +* Copyright (C) 2008-2009 by Andrea Diamantini +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + + +#ifndef AUTOSAVER_H +#define AUTOSAVER_H + +#include + +/* + This class will call the save() slot on the parent object when the parent changes. + It will wait several seconds after changed() to combining multiple changes and + prevent continuous writing to disk. + */ +class AutoSaver : public QObject +{ + Q_OBJECT + +public: + AutoSaver(QObject *parent); + ~AutoSaver(); + void saveIfNeccessary(); + +public slots: + void changeOccurred(); + +protected: + void timerEvent(QTimerEvent *event); + +private: + QBasicTimer m_timer; + QTime m_firstChange; + +}; + +#endif // AUTOSAVER_H + diff --git a/src/bookmarks.cpp b/src/bookmarks.cpp new file mode 100644 index 00000000..c7f4da98 --- /dev/null +++ b/src/bookmarks.cpp @@ -0,0 +1,278 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2008-2009 by Andrea Diamantini +* Copyright (C) 2009 by Paweł Prażak +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + + +// Self Includes +#include "bookmarks.h" +#include "bookmarks.moc" + +// Local Includes +#include "mainwindow.h" +#include "webview.h" +#include "application.h" + +// KDE Includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Qt Includes +#include +#include + + +BookmarkOwner::BookmarkOwner(QObject *parent) + : QObject(parent) + , KBookmarkOwner() +{ +} + + +void BookmarkOwner::openBookmark(const KBookmark & bookmark, + Qt::MouseButtons mouseButtons, + Qt::KeyboardModifiers keyboardModifiers) +{ + Q_UNUSED(mouseButtons) + Q_UNUSED(keyboardModifiers) + + emit openUrl(bookmark.url()); +} + + +QString BookmarkOwner::currentUrl() const +{ + return Application::instance()->mainWindow()->currentTab()->url().url(); +} + + +QString BookmarkOwner::currentTitle() const +{ + return Application::instance()->mainWindow()->currentTab()->title(); +} + + +void BookmarkOwner::openFolderinTabs(const KBookmarkGroup &bm) +{ + QList urlList = bm.groupUrlList(); + QList::iterator url; + Application* app = Application::instance(); + for (url = urlList.begin(); url != urlList.end(); ++url) + { + app->newWebView(); + app->mainWindow()->loadUrl(*url); + } +} + + +// ------------------------------------------------------------------------------------------------------ + + +BookmarkMenu::BookmarkMenu(KBookmarkManager *manager, + KBookmarkOwner *owner, + KMenu *menu, + KActionCollection* actionCollection) + : KBookmarkMenu(manager, owner, menu, actionCollection) + +{ + actionCollection->addAction(KStandardAction::AddBookmark, + QLatin1String("add_bookmark_payload"), + this, SLOT(slotAddBookmark())); + +} + +BookmarkMenu::~BookmarkMenu() +{ +} + + +KMenu *BookmarkMenu::viewContextMenu(QAction *action) +{ + return contextMenu(action); +} + + +void BookmarkMenu::slotAddBookmark() +{ + KAction *action = qobject_cast(sender()); + if (action && !action->data().isNull()) + { + KBookmarkGroup parentBookmark = manager()->findByAddress(parentAddress()).toGroup(); + /// TODO Add bookmark Icon + parentBookmark.addBookmark(owner()->currentTitle(), action->data().toUrl()); + manager()->emitChanged(); + return; + } + + KBookmarkMenu::slotAddBookmark(); +} + + +// ------------------------------------------------------------------------------------------------------ + + +BookmarkProvider::BookmarkProvider(QWidget *parent) + : QWidget(parent) + , m_manager(0) + , m_owner(0) + , m_menu(new KMenu(this)) + , m_actionCollection(new KActionCollection(this)) + , m_bookmarkMenu(0) + , m_bookmarkToolBar(0) +{ + KUrl bookfile = KUrl("~/.kde/share/apps/konqueror/bookmarks.xml"); // share konqueror bookmarks + + if (!QFile::exists(bookfile.path())) + { + bookfile = KUrl("~/.kde4/share/apps/konqueror/bookmarks.xml"); + if (!QFile::exists(bookfile.path())) + { + QString bookmarksDefaultPath = KStandardDirs::locate("appdata" , "defaultbookmarks.xbel"); + kWarning() << bookmarksDefaultPath; + QFile bkms(bookmarksDefaultPath); + QString bookmarksPath = KStandardDirs::locateLocal("appdata", "bookmarks.xml", true); + bookmarksPath.replace("rekonq", "konqueror"); + bkms.copy(bookmarksPath); + + bookfile = KUrl(bookmarksPath); + } + } + m_manager = KBookmarkManager::managerForExternalFile(bookfile.path()); + connect(m_manager, SIGNAL(changed(const QString &, const QString &)), + this, SLOT(slotBookmarksChanged(const QString &, const QString &))); + + // setup menu + m_owner = new BookmarkOwner(this); + connect(m_owner, SIGNAL(openUrl(const KUrl&)), this, SIGNAL(openUrl(const KUrl&))); + m_bookmarkMenu = new BookmarkMenu(m_manager, m_owner, m_menu, m_actionCollection); + + // setup toolbar + setupToolBar(); +} + + +BookmarkProvider::~BookmarkProvider() +{ + delete m_bookmarkToolBar; + delete m_bookmarkMenu; + delete m_actionCollection; + delete m_menu; + delete m_owner; + delete m_manager; +} + + +void BookmarkProvider::setupToolBar() +{ + m_bookmarkToolBar = new KToolBar(this); + m_bookmarkToolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + m_bookmarkToolBar->setIconDimensions(16); + m_bookmarkToolBar->setAcceptDrops(true); + m_bookmarkToolBar->setContentsMargins(0, 0, 0, 0); + m_bookmarkToolBar->setMinimumHeight(16); + m_bookmarkToolBar->setContextMenuPolicy(Qt::CustomContextMenu); + connect(m_bookmarkToolBar, SIGNAL(customContextMenuRequested(const QPoint &)), + this, SLOT(contextMenu(const QPoint &))); + + slotBookmarksChanged("", ""); +} + + +void BookmarkProvider::slotBookmarksChanged(const QString &group, const QString &caller) +{ + Q_UNUSED(group) + Q_UNUSED(caller) + + if (!m_bookmarkToolBar) + { + kWarning() << "There is no bookmark toolbar"; + return; + } + + KActionCollection bookmarkCollection(this); + + KBookmarkGroup toolBarGroup = m_manager->toolbar(); + if (toolBarGroup.isNull()) + return; + + KBookmark bookmark = toolBarGroup.first(); + while (!bookmark.isNull()) + { + if (!bookmark.isGroup()) + { + KAction *action = new KBookmarkAction(bookmark, m_owner, this); + QString text = bookmark.address(); + bookmarkCollection.addAction(text, action); + } + bookmark = toolBarGroup.next(bookmark); + } + m_bookmarkToolBar->clear(); + m_bookmarkToolBar->addActions(bookmarkCollection.actions()); +} + + +QAction *BookmarkProvider::actionByName(const QString &name) +{ + QAction *action = m_actionCollection->action(name); + if (action) + return action; + /* else */ + kWarning() << "Action named: " << name << " not found, returning empty action."; + return new QAction(this); // return empty object instead of NULL pointer +} + + +void BookmarkProvider::contextMenu(const QPoint &point) +{ + KAction* action = dynamic_cast(m_bookmarkToolBar->actionAt(point)); + if (!action) + return; + KMenu *menu = m_bookmarkMenu->viewContextMenu(action); + menu->popup(m_bookmarkToolBar->mapToGlobal(point)); +} + + +KActionMenu* BookmarkProvider::bookmarkActionMenu() +{ + KActionMenu *bookmarkActionMenu = new KActionMenu(this); + bookmarkActionMenu->setMenu(m_menu); + bookmarkActionMenu->setText(i18n("&Bookmarks")); + return bookmarkActionMenu; +} + + +KAction* BookmarkProvider::bookmarkToolBarAction() +{ + KAction *bookmarkToolBarAction = new KAction(this); + bookmarkToolBarAction->setDefaultWidget(m_bookmarkToolBar); // The ownership is transferred to action + bookmarkToolBarAction->setText(i18n("Bookmarks Bar")); + bookmarkToolBarAction->setShortcutConfigurable(false); + return bookmarkToolBarAction; +} + diff --git a/src/bookmarks.h b/src/bookmarks.h new file mode 100644 index 00000000..d7213cb7 --- /dev/null +++ b/src/bookmarks.h @@ -0,0 +1,238 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2008-2009 by Andrea Diamantini +* Copyright (C) 2009 by Paweł Prażak +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + + +#ifndef BOOKMARKS_H +#define BOOKMARKS_H + +// Qt Includes +#include + +// KDE Includes +#include + +// Forward Declarations +class BookmarkProvider; + +class KAction; +class KActionCollection; +class KActionMenu; +class KUrl; +class KToolBar; +class KBookmarkManager; + + +/** + * Reimplementation of KBookmarkOwner, this class allows to manage + * bookmarks as actions + * + */ +class BookmarkOwner : public QObject , public KBookmarkOwner +{ + Q_OBJECT + +public: + + /** + * @short The class constructor. + * + * @param parent the pointer parent Bookmark provider. We need it + * to get pointer to MainWindow + */ + BookmarkOwner(QObject *parent = 0); + virtual ~BookmarkOwner() {} + + /** + * This function is called when a bookmark is selected and belongs to + * the ancestor class. + * This method actually emits signal to load bookmark's url. + * + * @param bookmark the bookmark to open + * @param mouseButtons the mouse buttons clicked to select the bookmark + * @param keyboardModifiers the keyboard modifiers pushed when the bookmark was selected + */ + virtual void openBookmark(const KBookmark &bookmark, + Qt::MouseButtons mouseButtons, + Qt::KeyboardModifiers keyboardModifiers); + + + /** + * this method, from KBookmarkOwner interface, allows to add the current page + * to the bookmark list, returning the URL page as QString. + * + * @return the current page's URL + */ + virtual QString currentUrl() const; + + /** + * this method, from KBookmarkOwner interface, allows to add the current page + * to the bookmark list, returning the title's page as QString. + * + * @return the current page's title + */ + virtual QString currentTitle() const; + + /** + * This function returns whether the owner supports tabs. + */ + virtual bool supportsTabs() const + { + return true; + } + + /** + * Called if the user wants to open every bookmark in this folder in a new tab. + * The default implementation does nothing. + * This is only called if supportsTabs() returns true + */ + virtual void openFolderinTabs(const KBookmarkGroup &bm); + +signals: + /** + * This signal is emitted when an url has to be loaded + * + * @param url the URL to load + * + */ + void openUrl(const KUrl &); + +private: + +}; + +// ------------------------------------------------------------------------------ + + +#include + + +/** + * This class represent the rekonq bookmarks menu. + * It's just a simple class inherited from KBookmarkMenu + * + */ +class BookmarkMenu : public KBookmarkMenu +{ + Q_OBJECT + +public: + BookmarkMenu(KBookmarkManager* manager, + KBookmarkOwner* owner, + KMenu* menu, + KActionCollection* actionCollection); + ~BookmarkMenu(); + + virtual KMenu *viewContextMenu(QAction* action); + +protected slots: + void slotAddBookmark(); + +}; + + +// ------------------------------------------------------------------------------ + + +/** + * This class represent the interface to rekonq bookmarks system. + * All rekonq needs (Bookmarks Menu, Bookmarks Toolbar) is provided + * from this class. + * So it implements code to have each one + * + * + */ +class BookmarkProvider : public QWidget +{ + Q_OBJECT + +public: + /** + * @short Class constructor. + * Connect BookmarksProvider with bookmarks source + * (actually konqueror's bookmarks) + * @param parent The MainWindow to provide bookmarks objects + * + */ + BookmarkProvider(QWidget* parent = 0); + ~BookmarkProvider(); + + /** + * @short Get the Bookmarks Menu Action + * @return the Bookmarks Menu + */ + KActionMenu *bookmarkActionMenu(); + + + /** + * @short Get the Bookmarks Toolbar Action + * @return the Bookmarks Toolbar Action + */ + KAction *bookmarkToolBarAction(); + + + /** + * @short Get action by name + * This method returns poiner bookmark action of given name. + * @pre m_actionCollection != NULL + * @param name Name of action you want to get + * @return It returns actions if one exists or empty object + */ + QAction *actionByName(const QString &name); + +signals: + /** + * @short This signal is emitted when an url has to be loaded + * + * @param url the URL to load + */ + void openUrl(const KUrl &url); + + +public slots: + /** + * @short Opens the context menu on given position + * @param point Point on whitch you want to open this menu + */ + void contextMenu(const QPoint &point); + + /** + * @short Waits for signal that the group with the address has been modified by the caller. + * Waits for signal that the group (or any of its children) with the address + * @p groupAddress (e.g. "/4/5") has been modified by the caller @p caller. + * + * @param group bookmark group adress + * @param caller caller that modified the bookmarks + * @see KBookmarkManager::changed + */ + void slotBookmarksChanged(const QString &group, const QString &caller); + +private: + void setupToolBar(); + + KBookmarkManager *m_manager; + BookmarkOwner *m_owner; + KMenu *m_menu; + KActionCollection *m_actionCollection; + BookmarkMenu *m_bookmarkMenu; + KToolBar *m_bookmarkToolBar; +}; + +#endif diff --git a/src/cookiejar.cpp b/src/cookiejar.cpp new file mode 100644 index 00000000..de7d7d7e --- /dev/null +++ b/src/cookiejar.cpp @@ -0,0 +1,841 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2007-2008 Trolltech ASA. All rights reserved +* Copyright (C) 2008-2009 by Andrea Diamantini +* Copyright (C) 2009 by Domrachev Alexandr +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + + +// Self Includes +#include "cookiejar.h" +#include "cookiejar.moc" + +// Auto Includes +#include "rekonq.h" + +// Local Includes +#include "autosaver.h" + +// KDE Includes +#include +#include +#include + +// Qt Includes +#include +#include +#include + + +static const unsigned int JAR_VERSION = 23; + + +QDataStream &operator<<(QDataStream &stream, const QList &list) +{ + stream << JAR_VERSION; + stream << quint32(list.size()); + for (int i = 0; i < list.size(); ++i) + stream << list.at(i).toRawForm(); + return stream; +} + + +QDataStream &operator>>(QDataStream &stream, QList &list) +{ + list.clear(); + + quint32 version; + stream >> version; + + if (version != JAR_VERSION) + return stream; + + quint32 count; + stream >> count; + for (quint32 i = 0; i < count; ++i) + { + QByteArray value; + stream >> value; + QList newCookies = QNetworkCookie::parseCookies(value); + if (newCookies.count() == 0 && value.length() != 0) + { + kWarning() << "CookieJar: Unable to parse saved cookie:" << value; + } + for (int j = 0; j < newCookies.count(); ++j) + list.append(newCookies.at(j)); + if (stream.atEnd()) + break; + } + return stream; +} + + +CookieJar::CookieJar(QObject *parent) + : QNetworkCookieJar(parent) + , m_loaded(false) + , m_saveTimer(new AutoSaver(this)) + , m_acceptCookies(AcceptOnlyFromSitesNavigatedTo) +{ + load(); +} + + +CookieJar::~CookieJar() +{ + if (m_keepCookies == KeepUntilExit) + clear(); + m_saveTimer->saveIfNeccessary(); +} + + +void CookieJar::clear() +{ + setAllCookies(QList()); + m_saveTimer->changeOccurred(); + emit cookiesChanged(); +} + + +void CookieJar::load() +{ + if (m_loaded) + return; + + // load cookies and exceptions + QString filepath = KStandardDirs::locateLocal("appdata", "cookies.ini"); + qRegisterMetaTypeStreamOperators >("QList"); + QSettings cookieSettings(filepath, QSettings::IniFormat); + setAllCookies(qvariant_cast >(cookieSettings.value(QLatin1String("cookies")))); + cookieSettings.beginGroup(QLatin1String("Exceptions")); + m_exceptions_block = cookieSettings.value(QLatin1String("block")).toStringList(); + m_exceptions_allow = cookieSettings.value(QLatin1String("allow")).toStringList(); + m_exceptions_allowForSession = cookieSettings.value(QLatin1String("allowForSession")).toStringList(); + qSort(m_exceptions_block.begin(), m_exceptions_block.end()); + qSort(m_exceptions_allow.begin(), m_exceptions_allow.end()); + qSort(m_exceptions_allowForSession.begin(), m_exceptions_allowForSession.end()); + + loadSettings(); + + save(); +} + + +void CookieJar::loadSettings() +{ + int canAcceptCookies = ReKonfig::acceptCookies(); + + switch (canAcceptCookies) + { + case 0: + m_acceptCookies = AcceptAlways; + break; + case 1: + m_acceptCookies = AcceptNever; + break; + case 2: + default: + m_acceptCookies = AcceptOnlyFromSitesNavigatedTo; + break; + } + + int canKeepCookiesUntil = ReKonfig::keepCookiesUntil(); + + switch (canKeepCookiesUntil) + { + default: + case 0: + m_keepCookies = KeepUntilExpire; + break; + case 1: + m_keepCookies = KeepUntilExit; + setAllCookies(QList()); + break; + case 2: + m_keepCookies = KeepUntilTimeLimit; + break; + } + + m_loaded = true; + emit cookiesChanged(); +} + + +void CookieJar::save() +{ + if (!m_loaded) + return; + purgeOldCookies(); + + QString filepath = KStandardDirs::locateLocal("appdata", "cookies.ini"); + QSettings cookieSettings(filepath, QSettings::IniFormat); + QList cookies = allCookies(); + for (int i = cookies.count() - 1; i >= 0; --i) + { + if (cookies.at(i).isSessionCookie()) + cookies.removeAt(i); + } + + cookieSettings.setValue(QLatin1String("cookies"), qVariantFromValue >(cookies)); + cookieSettings.beginGroup(QLatin1String("Exceptions")); + cookieSettings.setValue(QLatin1String("block"), m_exceptions_block); + cookieSettings.setValue(QLatin1String("allow"), m_exceptions_allow); + cookieSettings.setValue(QLatin1String("allowForSession"), m_exceptions_allowForSession); + + // save cookie settings + int n; + switch (m_acceptCookies) + { + case AcceptAlways: + n = 0; + break; + case AcceptNever: + n = 1; + break; + case AcceptOnlyFromSitesNavigatedTo: + default: + n = 2; + break; + } + ReKonfig::setAcceptCookies(n); + + + switch (m_keepCookies) + { + default: + case KeepUntilExpire: + n = 0; + break; + case KeepUntilExit: + n = 1; + break; + case KeepUntilTimeLimit: + n = 2; + break; + } + ReKonfig::setKeepCookiesUntil(n); +} + + +void CookieJar::purgeOldCookies() +{ + QList cookies = allCookies(); + if (cookies.isEmpty()) + return; + int oldCount = cookies.count(); + QDateTime now = QDateTime::currentDateTime(); + for (int i = cookies.count() - 1; i >= 0; --i) + { + if (!cookies.at(i).isSessionCookie() && cookies.at(i).expirationDate() < now) + cookies.removeAt(i); + } + if (oldCount == cookies.count()) + return; + setAllCookies(cookies); + emit cookiesChanged(); +} + + +QList CookieJar::cookiesForUrl(const QUrl &url) const +{ + CookieJar *that = const_cast(this); + if (!m_loaded) + that->load(); + + QWebSettings *globalSettings = QWebSettings::globalSettings(); + if (globalSettings->testAttribute(QWebSettings::PrivateBrowsingEnabled)) + { + QList noCookies; + return noCookies; + } + + return QNetworkCookieJar::cookiesForUrl(url); +} + + +bool CookieJar::setCookiesFromUrl(const QList &cookieList, const QUrl &url) +{ + if (!m_loaded) + load(); + + QWebSettings *globalSettings = QWebSettings::globalSettings(); + if (globalSettings->testAttribute(QWebSettings::PrivateBrowsingEnabled)) + return false; + + QString host = url.host(); + bool eBlock = qBinaryFind(m_exceptions_block.begin(), m_exceptions_block.end(), host) != m_exceptions_block.end(); + bool eAllow = qBinaryFind(m_exceptions_allow.begin(), m_exceptions_allow.end(), host) != m_exceptions_allow.end(); + bool eAllowSession = qBinaryFind(m_exceptions_allowForSession.begin(), m_exceptions_allowForSession.end(), host) != m_exceptions_allowForSession.end(); + + bool addedCookies = false; + // pass exceptions + bool acceptInitially = (m_acceptCookies != AcceptNever); + if ((acceptInitially && !eBlock) || (!acceptInitially && (eAllow || eAllowSession))) + { + // pass url domain == cookie domain + QDateTime soon = QDateTime::currentDateTime(); + soon = soon.addDays(90); + foreach(QNetworkCookie cookie, cookieList) + { + QList lst; + if (m_keepCookies == KeepUntilTimeLimit + && !cookie.isSessionCookie() + && cookie.expirationDate() > soon) + { + cookie.setExpirationDate(soon); + } + lst += cookie; + if (QNetworkCookieJar::setCookiesFromUrl(lst, url)) + { + addedCookies = true; + } + else + { + // finally force it in if wanted + if (m_acceptCookies == AcceptAlways) + { + QList cookies = allCookies(); + cookies += cookie; + setAllCookies(cookies); + addedCookies = true; + } +#if 0 + else + kWarning() << "setCookiesFromUrl failed" << url << cookieList.value(0).toRawForm(); +#endif + } + } + } + + if (addedCookies) + { + m_saveTimer->changeOccurred(); + emit cookiesChanged(); + } + return addedCookies; +} + + +CookieJar::AcceptPolicy CookieJar::acceptPolicy() const +{ + if (!m_loaded) + (const_cast(this))->load(); + return m_acceptCookies; +} + + +void CookieJar::setAcceptPolicy(AcceptPolicy policy) +{ + if (!m_loaded) + load(); + if (policy == m_acceptCookies) + return; + m_acceptCookies = policy; + m_saveTimer->changeOccurred(); +} + + +CookieJar::KeepPolicy CookieJar::keepPolicy() const +{ + if (!m_loaded) + (const_cast(this))->load(); + return m_keepCookies; +} + + +void CookieJar::setKeepPolicy(KeepPolicy policy) +{ + if (!m_loaded) + load(); + if (policy == m_keepCookies) + return; + m_keepCookies = policy; + m_saveTimer->changeOccurred(); +} + + +QStringList CookieJar::blockedCookies() const +{ + if (!m_loaded) + (const_cast(this))->load(); + return m_exceptions_block; +} + + +QStringList CookieJar::allowedCookies() const +{ + if (!m_loaded) + (const_cast(this))->load(); + return m_exceptions_allow; +} + + +QStringList CookieJar::allowForSessionCookies() const +{ + if (!m_loaded) + (const_cast(this))->load(); + return m_exceptions_allowForSession; +} + + +void CookieJar::setBlockedCookies(const QStringList &list) +{ + if (!m_loaded) + load(); + m_exceptions_block = list; + qSort(m_exceptions_block.begin(), m_exceptions_block.end()); + m_saveTimer->changeOccurred(); +} + + +void CookieJar::setAllowedCookies(const QStringList &list) +{ + if (!m_loaded) + load(); + m_exceptions_allow = list; + qSort(m_exceptions_allow.begin(), m_exceptions_allow.end()); + m_saveTimer->changeOccurred(); +} + + +void CookieJar::setAllowForSessionCookies(const QStringList &list) +{ + if (!m_loaded) + load(); + m_exceptions_allowForSession = list; + qSort(m_exceptions_allowForSession.begin(), m_exceptions_allowForSession.end()); + m_saveTimer->changeOccurred(); +} + + +// ------------------------------------------------------------------------------------------- + + +CookieModel::CookieModel(CookieJar *cookieJar, QObject *parent) + : QAbstractTableModel(parent) + , m_cookieJar(cookieJar) +{ + connect(m_cookieJar, SIGNAL(cookiesChanged()), this, SLOT(cookiesChanged())); + m_cookieJar->load(); +} + + +QVariant CookieModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role == Qt::SizeHintRole) + { + QFont font; + font.setPointSize(10); + QFontMetrics fm(font); + int height = fm.height() + fm.height() / 3; + int width = fm.width(headerData(section, orientation, Qt::DisplayRole).toString()); + return QSize(width, height); + } + + if (orientation == Qt::Horizontal) + { + if (role != Qt::DisplayRole) + return QVariant(); + + switch (section) + { + case 0: + return i18n("Website"); + case 1: + return i18n("Name"); + case 2: + return i18n("Path"); + case 3: + return i18n("Secure"); + case 4: + return i18n("Expires"); + case 5: + return i18n("Contents"); + default: + return QVariant(); + } + } + return QAbstractTableModel::headerData(section, orientation, role); +} + + +QVariant CookieModel::data(const QModelIndex &index, int role) const +{ + QList lst; + if (m_cookieJar) + lst = m_cookieJar->allCookies(); + if (index.row() < 0 || index.row() >= lst.size()) + return QVariant(); + + switch (role) + { + case Qt::DisplayRole: + case Qt::EditRole: + { + QNetworkCookie cookie = lst.at(index.row()); + switch (index.column()) + { + case 0: + return cookie.domain(); + case 1: + return cookie.name(); + case 2: + return cookie.path(); + case 3: + return cookie.isSecure(); + case 4: + return cookie.expirationDate(); + case 5: + return cookie.value(); + } + } + case Qt::FontRole: + { + QFont font; + font.setPointSize(10); + return font; + } + } + + return QVariant(); +} + + +int CookieModel::columnCount(const QModelIndex &parent) const +{ + return (parent.isValid()) ? 0 : 6; +} + + +int CookieModel::rowCount(const QModelIndex &parent) const +{ + return (parent.isValid() || !m_cookieJar) ? 0 : m_cookieJar->allCookies().count(); +} + + +bool CookieModel::removeRows(int row, int count, const QModelIndex &parent) +{ + if (parent.isValid() || !m_cookieJar) + return false; + int lastRow = row + count - 1; + beginRemoveRows(parent, row, lastRow); + QList lst = m_cookieJar->allCookies(); + for (int i = lastRow; i >= row; --i) + { + lst.removeAt(i); + } + m_cookieJar->setAllCookies(lst); + endRemoveRows(); + return true; +} + + +void CookieModel::cookiesChanged() +{ + reset(); +} + + + +// ------------------------------------------------------------------------------------------------ + + +CookiesDialog::CookiesDialog(CookieJar *cookieJar, QWidget *parent) + : QDialog(parent) +{ + setupUi(this); + setWindowFlags(Qt::Sheet); + CookieModel *model = new CookieModel(cookieJar, this); + m_proxyModel = new QSortFilterProxyModel(this); + connect(search, SIGNAL(textChanged(QString)), + m_proxyModel, SLOT(setFilterFixedString(QString))); + connect(removeButton, SIGNAL(clicked()), cookiesTable, SLOT(removeOne())); + connect(removeAllButton, SIGNAL(clicked()), cookiesTable, SLOT(removeAll())); + m_proxyModel->setSourceModel(model); + cookiesTable->verticalHeader()->hide(); + cookiesTable->setSelectionBehavior(QAbstractItemView::SelectRows); + cookiesTable->setModel(m_proxyModel); + cookiesTable->setAlternatingRowColors(true); + cookiesTable->setTextElideMode(Qt::ElideMiddle); + cookiesTable->setShowGrid(false); + cookiesTable->setSortingEnabled(true); + QFont f = font(); + f.setPointSize(10); + QFontMetrics fm(f); + int height = fm.height() + fm.height() / 3; + cookiesTable->verticalHeader()->setDefaultSectionSize(height); + cookiesTable->verticalHeader()->setMinimumSectionSize(-1); + for (int i = 0; i < model->columnCount(); ++i) + { + int header = cookiesTable->horizontalHeader()->sectionSizeHint(i); + switch (i) + { + case 0: + header = fm.width(QLatin1String("averagehost.domain.com")); + break; + case 1: + header = fm.width(QLatin1String("_session_id")); + break; + case 4: + header = fm.width(QDateTime::currentDateTime().toString(Qt::LocalDate)); + break; + } + int buffer = fm.width(QLatin1String("xx")); + header += buffer; + cookiesTable->horizontalHeader()->resizeSection(i, header); + } + cookiesTable->horizontalHeader()->setStretchLastSection(true); +} + + +// --------------------------------------------------------------------------------------------------- + + +CookieExceptionsModel::CookieExceptionsModel(CookieJar *cookiejar, QObject *parent) + : QAbstractTableModel(parent) + , m_cookieJar(cookiejar) +{ + m_allowedCookies = m_cookieJar->allowedCookies(); + m_blockedCookies = m_cookieJar->blockedCookies(); + m_sessionCookies = m_cookieJar->allowForSessionCookies(); +} + + +QVariant CookieExceptionsModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role == Qt::SizeHintRole) + { + QFont font; + font.setPointSize(10); + QFontMetrics fm(font); + int height = fm.height() + fm.height() / 3; + int width = fm.width(headerData(section, orientation, Qt::DisplayRole).toString()); + return QSize(width, height); + } + + if (orientation == Qt::Horizontal + && role == Qt::DisplayRole) + { + switch (section) + { + case 0: + return i18n("Website"); + case 1: + return i18n("Status"); + } + } + return QAbstractTableModel::headerData(section, orientation, role); +} + + +QVariant CookieExceptionsModel::data(const QModelIndex &index, int role) const +{ + if (index.row() < 0 || index.row() >= rowCount()) + return QVariant(); + + switch (role) + { + case Qt::DisplayRole: + case Qt::EditRole: + { + int row = index.row(); + if (row < m_allowedCookies.count()) + { + switch (index.column()) + { + case 0: + return m_allowedCookies.at(row); + case 1: + return i18n("Allow"); + } + } + row = row - m_allowedCookies.count(); + if (row < m_blockedCookies.count()) + { + switch (index.column()) + { + case 0: + return m_blockedCookies.at(row); + case 1: + return i18n("Block"); + } + } + row = row - m_blockedCookies.count(); + if (row < m_sessionCookies.count()) + { + switch (index.column()) + { + case 0: + return m_sessionCookies.at(row); + case 1: + return i18n("Allow For Session"); + } + } + } + case Qt::FontRole: + { + QFont font; + font.setPointSize(10); + return font; + } + } + return QVariant(); +} + + +int CookieExceptionsModel::columnCount(const QModelIndex &parent) const +{ + return (parent.isValid()) ? 0 : 2; +} + + +int CookieExceptionsModel::rowCount(const QModelIndex &parent) const +{ + return (parent.isValid() || !m_cookieJar) ? 0 : m_allowedCookies.count() + m_blockedCookies.count() + m_sessionCookies.count(); +} + + +bool CookieExceptionsModel::removeRows(int row, int count, const QModelIndex &parent) +{ + if (parent.isValid() || !m_cookieJar) + return false; + + int lastRow = row + count - 1; + beginRemoveRows(parent, row, lastRow); + for (int i = lastRow; i >= row; --i) + { + if (i < m_allowedCookies.count()) + { + m_allowedCookies.removeAt(row); + continue; + } + i = i - m_allowedCookies.count(); + if (i < m_blockedCookies.count()) + { + m_blockedCookies.removeAt(row); + continue; + } + i = i - m_blockedCookies.count(); + if (i < m_sessionCookies.count()) + { + m_sessionCookies.removeAt(row); + continue; + } + } + m_cookieJar->setAllowedCookies(m_allowedCookies); + m_cookieJar->setBlockedCookies(m_blockedCookies); + m_cookieJar->setAllowForSessionCookies(m_sessionCookies); + endRemoveRows(); + return true; +} + + +// ---------------------------------------------------------------------------------------------------------------- + + +CookiesExceptionsDialog::CookiesExceptionsDialog(CookieJar *cookieJar, QWidget *parent) + : QDialog(parent) + , m_cookieJar(cookieJar) +{ + setupUi(this); + setWindowFlags(Qt::Sheet); + connect(removeButton, SIGNAL(clicked()), exceptionTable, SLOT(removeOne())); + connect(removeAllButton, SIGNAL(clicked()), exceptionTable, SLOT(removeAll())); + exceptionTable->verticalHeader()->hide(); + exceptionTable->setSelectionBehavior(QAbstractItemView::SelectRows); + exceptionTable->setAlternatingRowColors(true); + exceptionTable->setTextElideMode(Qt::ElideMiddle); + exceptionTable->setShowGrid(false); + exceptionTable->setSortingEnabled(true); + m_exceptionsModel = new CookieExceptionsModel(cookieJar, this); + m_proxyModel = new QSortFilterProxyModel(this); + m_proxyModel->setSourceModel(m_exceptionsModel); + connect(search, SIGNAL(textChanged(QString)), + m_proxyModel, SLOT(setFilterFixedString(QString))); + exceptionTable->setModel(m_proxyModel); + + CookieModel *cookieModel = new CookieModel(cookieJar, this); + domainLineEdit->setCompleter(new QCompleter(cookieModel, domainLineEdit)); + + connect(domainLineEdit, SIGNAL(textChanged(const QString &)), + this, SLOT(textChanged(const QString &))); + connect(blockButton, SIGNAL(clicked()), this, SLOT(block())); + connect(allowButton, SIGNAL(clicked()), this, SLOT(allow())); + connect(allowForSessionButton, SIGNAL(clicked()), this, SLOT(allowForSession())); + + QFont f = font(); + f.setPointSize(10); + QFontMetrics fm(f); + int height = fm.height() + fm.height() / 3; + exceptionTable->verticalHeader()->setDefaultSectionSize(height); + exceptionTable->verticalHeader()->setMinimumSectionSize(-1); + for (int i = 0; i < m_exceptionsModel->columnCount(); ++i) + { + int header = exceptionTable->horizontalHeader()->sectionSizeHint(i); + switch (i) + { + case 0: + header = fm.width(QLatin1String("averagebiglonghost.domain.com")); + break; + case 1: + header = fm.width(QLatin1String("Allow For Session")); + break; + } + int buffer = fm.width(QLatin1String("xx")); + header += buffer; + exceptionTable->horizontalHeader()->resizeSection(i, header); + } +} + + +void CookiesExceptionsDialog::textChanged(const QString &text) +{ + bool enabled = !text.isEmpty(); + blockButton->setEnabled(enabled); + allowButton->setEnabled(enabled); + allowForSessionButton->setEnabled(enabled); +} + + +void CookiesExceptionsDialog::block() +{ + if (domainLineEdit->text().isEmpty()) + return; + m_exceptionsModel->m_blockedCookies.append(domainLineEdit->text()); + m_cookieJar->setBlockedCookies(m_exceptionsModel->m_blockedCookies); + m_exceptionsModel->reset(); +} + + +void CookiesExceptionsDialog::allow() +{ + if (domainLineEdit->text().isEmpty()) + return; + m_exceptionsModel->m_allowedCookies.append(domainLineEdit->text()); + m_cookieJar->setAllowedCookies(m_exceptionsModel->m_allowedCookies); + m_exceptionsModel->reset(); +} + + +void CookiesExceptionsDialog::allowForSession() +{ + if (domainLineEdit->text().isEmpty()) + return; + m_exceptionsModel->m_sessionCookies.append(domainLineEdit->text()); + m_cookieJar->setAllowForSessionCookies(m_exceptionsModel->m_sessionCookies); + m_exceptionsModel->reset(); +} diff --git a/src/cookiejar.h b/src/cookiejar.h new file mode 100644 index 00000000..27071b6d --- /dev/null +++ b/src/cookiejar.h @@ -0,0 +1,200 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2007-2008 Trolltech ASA. All rights reserved +* Copyright (C) 2008-2009 by Andrea Diamantini +* Copyright (C) 2009 by Domrachev Alexandr +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + +#ifndef COOKIEJAR_H +#define COOKIEJAR_H + + +// KDE Includes +#include + +// Qt Includes +#include +#include +#include +#include + +// Forward Declarations +class QSortFilterProxyModel; +class QKeyEvent; +class AutoSaver; + + +class CookieJar : public QNetworkCookieJar +{ + friend class CookieModel; + Q_OBJECT + Q_PROPERTY(AcceptPolicy acceptPolicy READ acceptPolicy WRITE setAcceptPolicy) + Q_PROPERTY(KeepPolicy keepPolicy READ keepPolicy WRITE setKeepPolicy) + Q_PROPERTY(QStringList blockedCookies READ blockedCookies WRITE setBlockedCookies) + Q_PROPERTY(QStringList allowedCookies READ allowedCookies WRITE setAllowedCookies) + Q_PROPERTY(QStringList allowForSessionCookies READ allowForSessionCookies WRITE setAllowForSessionCookies) + Q_ENUMS(KeepPolicy) + Q_ENUMS(AcceptPolicy) + +signals: + void cookiesChanged(); + +public: + enum AcceptPolicy + { + AcceptAlways, + AcceptNever, + AcceptOnlyFromSitesNavigatedTo + }; + + enum KeepPolicy + { + KeepUntilExpire, + KeepUntilExit, + KeepUntilTimeLimit + }; + + CookieJar(QObject *parent = 0); + ~CookieJar(); + + QList cookiesForUrl(const QUrl &url) const; + bool setCookiesFromUrl(const QList &cookieList, const QUrl &url); + + AcceptPolicy acceptPolicy() const; + void setAcceptPolicy(AcceptPolicy policy); + + KeepPolicy keepPolicy() const; + void setKeepPolicy(KeepPolicy policy); + + QStringList blockedCookies() const; + QStringList allowedCookies() const; + QStringList allowForSessionCookies() const; + + void setBlockedCookies(const QStringList &list); + void setAllowedCookies(const QStringList &list); + void setAllowForSessionCookies(const QStringList &list); + +public slots: + void clear(); + void loadSettings(); + +private slots: + void save(); + +private: + void purgeOldCookies(); + void load(); + bool m_loaded; + AutoSaver *m_saveTimer; + + AcceptPolicy m_acceptCookies; + KeepPolicy m_keepCookies; + + QStringList m_exceptions_block; + QStringList m_exceptions_allow; + QStringList m_exceptions_allowForSession; +}; + + +// ------------------------------------------------------------------------------------------------------------- + + +class CookieModel : public QAbstractTableModel +{ + Q_OBJECT + +public: + CookieModel(CookieJar *jar, QObject *parent = 0); + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); + +private slots: + void cookiesChanged(); + +private: + CookieJar *m_cookieJar; +}; + + +// ---------------------------------------------------------------------------------------------------------------------- + + +#include "ui_cookies.h" + +class CookiesDialog : public QDialog, public Ui_CookiesDialog +{ + Q_OBJECT + +public: + CookiesDialog(CookieJar *cookieJar, QWidget *parent = 0); + +private: + QSortFilterProxyModel *m_proxyModel; +}; + +class CookieExceptionsModel : public QAbstractTableModel +{ + Q_OBJECT + friend class CookiesExceptionsDialog; + +public: + CookieExceptionsModel(CookieJar *cookieJar, QObject *parent = 0); + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); + +private: + CookieJar *m_cookieJar; + + // Domains we allow, Domains we block, Domains we allow for this session + QStringList m_allowedCookies; + QStringList m_blockedCookies; + QStringList m_sessionCookies; +}; + + +// ----------------------------------------------------------------------------------------------------------------- + + +#include "ui_cookiesexceptions.h" + +class CookiesExceptionsDialog : public QDialog, public Ui_CookiesExceptionsDialog +{ + Q_OBJECT + +public: + CookiesExceptionsDialog(CookieJar *cookieJar, QWidget *parent = 0); + +private slots: + void block(); + void allow(); + void allowForSession(); + void textChanged(const QString &text); + +private: + CookieExceptionsModel *m_exceptionsModel; + QSortFilterProxyModel *m_proxyModel; + CookieJar *m_cookieJar; +}; + +#endif // COOKIEJAR_H diff --git a/src/cookies.ui b/src/cookies.ui new file mode 100644 index 00000000..49ad3a96 --- /dev/null +++ b/src/cookies.ui @@ -0,0 +1,107 @@ + + + CookiesDialog + + + + 0 + 0 + 550 + 370 + + + + Cookies + + + + + + Qt::Horizontal + + + + 252 + 20 + + + + + + + + + + + + + + + + &Remove + + + + + + + Remove &All Cookies + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + QDialogButtonBox::Ok + + + + + + + + + + KLineEdit + QLineEdit +
klineedit.h
+
+ + EditTableView + QTableView +
edittableview.h
+
+
+ + + + buttonBox + accepted() + CookiesDialog + accept() + + + 472 + 329 + + + 461 + 356 + + + + +
diff --git a/src/cookiesexceptions.ui b/src/cookiesexceptions.ui new file mode 100644 index 00000000..de59eee0 --- /dev/null +++ b/src/cookiesexceptions.ui @@ -0,0 +1,179 @@ + + CookiesExceptionsDialog + + + + 0 + 0 + 466 + 446 + + + + Cookie Exceptions + + + + + + New Exception + + + + + + + + Domain: + + + + + + + + + + + + + + Qt::Horizontal + + + + 81 + 25 + + + + + + + + false + + + Block + + + + + + + false + + + Allow For Session + + + + + + + false + + + Allow + + + + + + + + + + + + Exceptions + + + + + + Qt::Horizontal + + + + 252 + 20 + + + + + + + + + + + + + + &Remove + + + + + + + Remove &All + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + + EditTableView + QTableView +
edittableview.h
+
+
+ + + + buttonBox + accepted() + CookiesExceptionsDialog + accept() + + + 381 + 428 + + + 336 + 443 + + + + +
diff --git a/src/download.cpp b/src/download.cpp new file mode 100644 index 00000000..9489b270 --- /dev/null +++ b/src/download.cpp @@ -0,0 +1,224 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2007 Lukas Appelhans +* Copyright (C) 2008-2009 by Andrea Diamantini +* Copyright (C) 2009 by Paweł Prażak +* Copyright (C) 2009 by Domrachev Alexandr +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + +// local Includes +#include "download.h" +#include "download.moc" + +// KDE Includes +#include +#include +#include +#include +#include +#include + +// Qt Includes +#include +#include + +// Local Includes +#include "application.h" +#include "mainwindow.h" + + +DownloadManager::DownloadManager() + : QObject() +{ +} + + +DownloadManager::~DownloadManager() +{ + foreach(Download *download, m_downloads) + { + // cancel all unfinished downloads + download->cancel(); + delete download; + } +} + + +void DownloadManager::newDownload(const KUrl &srcUrl, const KUrl &destUrl) +{ + KUrl destination = destUrl; + Download::DownloadType type; + + KSharedPtr mimeType = KMimeType::findByPath(srcUrl.prettyUrl()); + + QString typeText = KMimeType::extractKnownExtension(srcUrl.fileName()); + typeText += " (" + mimeType->name() + ")"; + + int answer = KMessageBox::questionYesNoCancel(NULL, + i18n("Download '%1'?\n""Type: %2", srcUrl.prettyUrl(), typeText), + i18n("Download '%1'...", srcUrl.fileName()), + KStandardGuiItem::save(), + KStandardGuiItem::open(), + KStandardGuiItem::cancel(), + "showOpenSaveDownloadDialog" + ); + + switch (answer) + { + case KMessageBox::Cancel: + return; + break; + + case KMessageBox::Yes: // ----- SAVE + // if destination is empty than ask for download path (if showOpenSaveDownloadDialog setting enabled) + if (destination.isEmpty()) + { + destination = downloadDestination(srcUrl.fileName()); + } + type = Download::Save; + break; + + case KMessageBox::No: // ----- OPEN + // Download file to tmp dir + destination.setDirectory(KStandardDirs::locateLocal("tmp", "", true)); + destination.addPath(srcUrl.fileName()); + type = Download::Open; + break; + + default: + // impossible + break; + }; + + // if user canceled download than abort + if (destination.isEmpty()) + return; + + Download *download = new Download(srcUrl, destination, type); + connect(download, SIGNAL(downloadFinished(int)), this, SLOT(slotDownloadFinished(int))); + m_downloads.append(download); +} + + +const QList &DownloadManager::downloads() const +{ + return m_downloads; +}; + + +KUrl DownloadManager::downloadDestination(const QString &filename) +{ + KUrl destination = ReKonfig::downloadDir(); + if (destination.isEmpty()) + destination = KGlobalSettings::downloadPath(); + destination.addPath(filename); + + if (!ReKonfig::downloadToDefaultDir()) + { + destination = KFileDialog::getSaveUrl(destination); + // if user canceled the download return empty url + if (destination.isEmpty()) + return KUrl(); + } + return destination; +} + + +void DownloadManager::slotDownloadFinished(int errorCode) +{ + Q_UNUSED(errorCode) + + // if sender exists and list contains it - (open and) delete it + Download *download = qobject_cast(sender()); + if (download && m_downloads.contains(download)) + { + if (download->type() == Download::Open) + { + KSharedPtr mimeType = KMimeType::findByPath(download->destUrl().prettyUrl()); + KRun::runUrl(download->destUrl(), mimeType->name(), NULL, true); + } + disconnect(download, SIGNAL(downloadFinished(int)), this, SLOT(slotDownloadFinished(int))); + int index = m_downloads.indexOf(download); + delete m_downloads.takeAt(index); + return; + } + kWarning() << "Could not find download or invalid sender. Sender:" << sender(); +} + + +//---- + +#include +#include +#include + + +Download::Download(const KUrl &srcUrl, const KUrl &destUrl, DownloadType type) + : QObject() + , m_srcUrl(srcUrl) + , m_destUrl(destUrl) + , m_type(type) +{ + Q_ASSERT(!m_srcUrl.isEmpty()); + Q_ASSERT(!m_destUrl.isEmpty()); + kDebug() << "DownloadFile: " << m_srcUrl.url() << " to dest: " << m_destUrl.url(); + + m_copyJob = KIO::file_copy(m_srcUrl, m_destUrl); + connect(m_copyJob, SIGNAL(result(KJob *)), SLOT(slotResult(KJob *))); +} + + +Download::~Download() +{ +} + + +void Download::cancel() +{ + bool result = m_copyJob->kill(KJob::EmitResult); + Q_ASSERT(result); +} + + +void Download::slotResult(KJob *job) +{ + switch (job->error()) + { + case 0: //The download has finished + { + kDebug() << "Downloading successfully finished: " << m_destUrl.url(); + break; + } + case KIO::ERR_FILE_ALREADY_EXIST: + { + kWarning() << "ERROR - File already exists"; + break; + } + case KIO::ERR_USER_CANCELED: + { + kWarning() << "ERROR - User canceled the downlaod"; + break; + } + default: + kWarning() << "We are sorry to say you, that there were errors while downloading :("; + break; + } + + // inform the world + emit downloadFinished(job->error()); +} diff --git a/src/download.h b/src/download.h new file mode 100644 index 00000000..0ad01e69 --- /dev/null +++ b/src/download.h @@ -0,0 +1,141 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2007 Lukas Appelhans +* Copyright (C) 2008-2009 by Andrea Diamantini +* Copyright (C) 2009 by Paweł Prażak +* Copyright (C) 2009 by Domrachev Alexandr +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + +#ifndef DOWNLOAD_H +#define DOWNLOAD_H + +// Auto Includes +#include "rekonq.h" + +// KDE Includes +#include + +// Qt Includes +#include + +// Forward Declarations +class KJob; + +namespace KIO +{ +class Job; +} + + +/** + * This class lets rekonq to download an object from the network. + * Creating a new object, you can continue downloading a file also + * when rekonq is closed. + * + */ +class Download : public QObject +{ + Q_OBJECT + +public: + enum DownloadType { Save, Open }; + + /** + * Class constructor. This is the unique method we need to + * use this class. In fact Download class needs to know just + * "where" catch the file to download and where it has to put it + * + * @param srcUrl the source url + * @param destUrl the destination url + * + */ + Download(const KUrl &srcUrl, const KUrl &destUrl, DownloadType type); + + /** + * class destructor + */ + ~Download(); + + KUrl srcUrl() const + { + return m_srcUrl; + } + KUrl destUrl() const + { + return m_destUrl; + } + DownloadType type() const + { + return m_type; + } + void cancel(); + +signals: + void downloadFinished(int errorCode); + +private slots: + void slotResult(KJob *job); + +private: + KIO::FileCopyJob *m_copyJob; + KUrl m_srcUrl; + KUrl m_destUrl; + KUrl m_destFile; + QByteArray m_data; + DownloadType m_type; +}; + + +// ---------------------- + + +class DownloadManager : public QObject +{ + Q_OBJECT + +public: + DownloadManager(); + ~DownloadManager(); + + /** + * @short Creates new download job. + * This method lets you to download a file from a remote source url + * to a local destination url. + * + * @param srcUrl the source url + * @param destUrl the destination url (default value is your default download destination setting) + * + */ + void newDownload(const KUrl &srcUrl, const KUrl &destUrl = KUrl()); + + const QList &downloads() const; + +public slots: + void slotDownloadFinished(int errorCode); + +private: + KUrl downloadDestination(const QString &filename); + + QList m_downloads; +}; + + +//-- + + +#endif diff --git a/src/edittableview.cpp b/src/edittableview.cpp new file mode 100644 index 00000000..bf1ef370 --- /dev/null +++ b/src/edittableview.cpp @@ -0,0 +1,61 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2007-2008 Trolltech ASA. All rights reserved +* Copyright (C) 2008-2009 by Andrea Diamantini +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + + +#include "edittableview.h" +#include + +EditTableView::EditTableView(QWidget *parent) + : QTableView(parent) +{ +} + +void EditTableView::keyPressEvent(QKeyEvent *event) +{ + if ((event->key() == Qt::Key_Delete + || event->key() == Qt::Key_Backspace) + && model()) + { + removeOne(); + } + else + { + QAbstractItemView::keyPressEvent(event); + } +} + +void EditTableView::removeOne() +{ + if (!model() || !selectionModel()) + return; + int row = currentIndex().row(); + model()->removeRow(row, rootIndex()); + QModelIndex idx = model()->index(row, 0, rootIndex()); + if (!idx.isValid()) + idx = model()->index(row - 1, 0, rootIndex()); + selectionModel()->select(idx, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); +} + +void EditTableView::removeAll() +{ + if (model()) + model()->removeRows(0, model()->rowCount(rootIndex()), rootIndex()); +} diff --git a/src/edittableview.h b/src/edittableview.h new file mode 100644 index 00000000..dbc9a145 --- /dev/null +++ b/src/edittableview.h @@ -0,0 +1,42 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2007-2008 Trolltech ASA. All rights reserved +* Copyright (C) 2008-2009 by Andrea Diamantini +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + + +#ifndef EDITTABLEVIEW_H +#define EDITTABLEVIEW_H + +#include + +class EditTableView : public QTableView +{ + Q_OBJECT + +public: + EditTableView(QWidget *parent = 0); + void keyPressEvent(QKeyEvent *event); + +public slots: + void removeOne(); + void removeAll(); +}; + +#endif // EDITTABLEVIEW_H + diff --git a/src/edittreeview.cpp b/src/edittreeview.cpp new file mode 100644 index 00000000..49730d67 --- /dev/null +++ b/src/edittreeview.cpp @@ -0,0 +1,61 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2007-2008 Trolltech ASA. All rights reserved +* Copyright (C) 2008-2009 by Andrea Diamantini +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + + +#include "edittreeview.h" + +#include + +EditTreeView::EditTreeView(QWidget *parent) + : QTreeView(parent) +{ +} + +void EditTreeView::keyPressEvent(QKeyEvent *event) +{ + if ((event->key() == Qt::Key_Delete + || event->key() == Qt::Key_Backspace) + && model()) + { + removeOne(); + } + else + { + QAbstractItemView::keyPressEvent(event); + } +} + +void EditTreeView::removeOne() +{ + if (!model()) + return; + QModelIndex ci = currentIndex(); + int row = ci.row(); + model()->removeRow(row, ci.parent()); +} + +void EditTreeView::removeAll() +{ + if (!model()) + return; + model()->removeRows(0, model()->rowCount(rootIndex()), rootIndex()); +} + diff --git a/src/edittreeview.h b/src/edittreeview.h new file mode 100644 index 00000000..5be0dc45 --- /dev/null +++ b/src/edittreeview.h @@ -0,0 +1,41 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2007-2008 Trolltech ASA. All rights reserved +* Copyright (C) 2008-2009 by Andrea Diamantini +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + +#ifndef EDITTREEVIEW_H +#define EDITTREEVIEW_H + +#include + +class EditTreeView : public QTreeView +{ + Q_OBJECT + +public: + EditTreeView(QWidget *parent = 0); + void keyPressEvent(QKeyEvent *event); + +public slots: + void removeOne(); + void removeAll(); +}; + +#endif // EDITTREEVIEW_H + diff --git a/src/findbar.cpp b/src/findbar.cpp new file mode 100644 index 00000000..11d890c1 --- /dev/null +++ b/src/findbar.cpp @@ -0,0 +1,137 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2008-2009 by Andrea Diamantini +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + +// Self Includes +#include "findbar.h" +#include "findbar.moc" + +// KDE Includes +#include +#include +#include +#include +#include + +// Qt Includes +#include + + +FindBar::FindBar(KXmlGuiWindow *mainwindow) + : QWidget(mainwindow) + , m_lineEdit(new KLineEdit(this)) + , m_matchCase(new QCheckBox(i18n("&Match case"), this)) +{ + QHBoxLayout *layout = new QHBoxLayout; + + // cosmetic + layout->setContentsMargins(2, 0, 2, 0); + + // hide button + QToolButton *hideButton = new QToolButton(this); + hideButton->setAutoRaise(true); + hideButton->setIcon(KIcon("dialog-close")); + connect(hideButton, SIGNAL(clicked()), this, SLOT(hide())); + layout->addWidget(hideButton); + layout->setAlignment(hideButton, Qt::AlignLeft | Qt::AlignTop); + + // label + QLabel *label = new QLabel(i18n("Find: ")); + layout->addWidget(label); + + // lineEdit, focusProxy + setFocusProxy(m_lineEdit); + m_lineEdit->setMaximumWidth(250); + connect(m_lineEdit, SIGNAL(textChanged(const QString &)), mainwindow, SLOT(slotFind(const QString &))); + layout->addWidget(m_lineEdit); + + // buttons + KPushButton *findNext = new KPushButton(KIcon("go-down"), i18n("&Next"), this); + KPushButton *findPrev = new KPushButton(KIcon("go-up"), i18n("&Previous"), this); + connect(findNext, SIGNAL(clicked()), mainwindow, SLOT(slotFindNext())); + connect(findPrev, SIGNAL(clicked()), mainwindow, SLOT(slotFindPrevious())); + layout->addWidget(findNext); + layout->addWidget(findPrev); + + // Case sensitivity. Deliberately set so this is off by default. + m_matchCase->setCheckState(Qt::Unchecked); + m_matchCase->setTristate(false); + layout->addWidget(m_matchCase); + + // stretching widget on the left + layout->addStretch(); + + setLayout(layout); + + // we start off hidden + hide(); +} + + +FindBar::~FindBar() +{ +} + + +KLineEdit *FindBar::lineEdit() const +{ + return m_lineEdit; +} + + +bool FindBar::matchCase() const +{ + return m_matchCase->isChecked(); +} + + +void FindBar::clear() +{ + m_lineEdit->setText(QString()); +} + + +void FindBar::showFindBar() +{ + // show findbar if not visible + if (!isVisible()) + { + show(); + } + // set focus to findbar if user select showFindBar shortcut + m_lineEdit->setFocus(); + m_lineEdit->selectAll(); +} + + +void FindBar::keyPressEvent(QKeyEvent* event) +{ + if (event->key() == Qt::Key_Escape) + { + hide(); + return; + } + if (event->key() == Qt::Key_Return && !m_lineEdit->text().isEmpty()) + { + emit searchString(m_lineEdit->text()); + return; + } + QWidget::keyPressEvent(event); +} + diff --git a/src/findbar.h b/src/findbar.h new file mode 100644 index 00000000..15a82e1e --- /dev/null +++ b/src/findbar.h @@ -0,0 +1,61 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2008-2009 by Andrea Diamantini +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + + +#ifndef FINDBAR_H +#define FINDBAR_H + +// KDE Includes +#include +#include +#include + +// Qt Includes +#include +#include + + +class FindBar : public QWidget +{ + Q_OBJECT + +public: + FindBar(KXmlGuiWindow *mainwindow); + ~FindBar(); + KLineEdit *lineEdit() const; + bool matchCase() const; + +public slots: + void clear(); + void showFindBar(); + +protected Q_SLOTS: + void keyPressEvent(QKeyEvent* event); + +signals: + void searchString(const QString &); + +private: + KLineEdit *m_lineEdit; + QCheckBox *m_matchCase; +}; + + +#endif diff --git a/src/history.cpp b/src/history.cpp new file mode 100644 index 00000000..afcb477a --- /dev/null +++ b/src/history.cpp @@ -0,0 +1,1459 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2007-2008 Trolltech ASA. All rights reserved +* Copyright (C) 2008 Benjamin C. Meyer +* Copyright (C) 2008-2009 by Andrea Diamantini +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + +// Self Includes +#include "history.h" +#include "history.moc" + +// Auto Includes +#include "rekonq.h" + +// Local Includes +#include "autosaver.h" +#include "application.h" + +// KDE Includes +#include +#include + +// Qt Includes +#include +#include +#include + +#include + + +static const unsigned int HISTORY_VERSION = 23; + + +HistoryManager::HistoryManager(QObject *parent) + : QWebHistoryInterface(parent) + , m_saveTimer(new AutoSaver(this)) + , m_historyLimit(30) + , m_historyModel(0) + , m_historyFilterModel(0) + , m_historyTreeModel(0) +{ + m_expiredTimer.setSingleShot(true); + connect(&m_expiredTimer, SIGNAL(timeout()), this, SLOT(checkForExpired())); + connect(this, SIGNAL(entryAdded(const HistoryItem &)), m_saveTimer, SLOT(changeOccurred())); + connect(this, SIGNAL(entryRemoved(const HistoryItem &)), m_saveTimer, SLOT(changeOccurred())); + load(); + + m_historyModel = new HistoryModel(this, this); + m_historyFilterModel = new HistoryFilterModel(m_historyModel, this); + m_historyTreeModel = new HistoryTreeModel(m_historyFilterModel, this); + + // QWebHistoryInterface will delete the history manager + QWebHistoryInterface::setDefaultInterface(this); +} + + +HistoryManager::~HistoryManager() +{ + m_saveTimer->saveIfNeccessary(); +} + + +QList HistoryManager::history() const +{ + return m_history; +} + + +bool HistoryManager::historyContains(const QString &url) const +{ + return m_historyFilterModel->historyContains(url); +} + + +void HistoryManager::addHistoryEntry(const QString &url) +{ + QUrl cleanUrl(url); + cleanUrl.setPassword(QString()); + cleanUrl.setHost(cleanUrl.host().toLower()); + HistoryItem item(cleanUrl.toString(), QDateTime::currentDateTime()); + addHistoryEntry(item); +} + + +void HistoryManager::setHistory(const QList &history, bool loadedAndSorted) +{ + m_history = history; + + // verify that it is sorted by date + if (!loadedAndSorted) + qSort(m_history.begin(), m_history.end()); + + checkForExpired(); + + if (loadedAndSorted) + { + m_lastSavedUrl = m_history.value(0).url; + } + else + { + m_lastSavedUrl = QString(); + m_saveTimer->changeOccurred(); + } + emit historyReset(); +} + + +HistoryModel *HistoryManager::historyModel() const +{ + return m_historyModel; +} + + +HistoryFilterModel *HistoryManager::historyFilterModel() const +{ + return m_historyFilterModel; +} + + +HistoryTreeModel *HistoryManager::historyTreeModel() const +{ + return m_historyTreeModel; +} + + +void HistoryManager::checkForExpired() +{ + if (m_historyLimit < 0 || m_history.isEmpty()) + return; + + QDateTime now = QDateTime::currentDateTime(); + int nextTimeout = 0; + + while (!m_history.isEmpty()) + { + QDateTime checkForExpired = m_history.last().dateTime; + checkForExpired.setDate(checkForExpired.date().addDays(m_historyLimit)); + if (now.daysTo(checkForExpired) > 7) + { + // check at most in a week to prevent int overflows on the timer + nextTimeout = 7 * 86400; + } + else + { + nextTimeout = now.secsTo(checkForExpired); + } + if (nextTimeout > 0) + break; + HistoryItem item = m_history.takeLast(); + // remove from saved file also + m_lastSavedUrl = QString(); + emit entryRemoved(item); + } + + if (nextTimeout > 0) + m_expiredTimer.start(nextTimeout * 1000); +} + + +void HistoryManager::addHistoryEntry(const HistoryItem &item) +{ + QWebSettings *globalSettings = QWebSettings::globalSettings(); + if (globalSettings->testAttribute(QWebSettings::PrivateBrowsingEnabled)) + return; + + m_history.prepend(item); + emit entryAdded(item); + if (m_history.count() == 1) + checkForExpired(); +} + + +void HistoryManager::updateHistoryEntry(const KUrl &url, const QString &title) +{ + for (int i = 0; i < m_history.count(); ++i) + { + if (url == m_history.at(i).url) + { + m_history[i].title = title; + m_saveTimer->changeOccurred(); + if (m_lastSavedUrl.isEmpty()) + m_lastSavedUrl = m_history.at(i).url; + emit entryUpdated(i); + break; + } + } +} + + +void HistoryManager::removeHistoryEntry(const HistoryItem &item) +{ + m_lastSavedUrl.clear(); + m_history.removeOne(item); + emit entryRemoved(item); +} + + +void HistoryManager::removeHistoryEntry(const KUrl &url, const QString &title) +{ + for (int i = 0; i < m_history.count(); ++i) + { + if (url == m_history.at(i).url + && (title.isEmpty() || title == m_history.at(i).title)) + { + removeHistoryEntry(m_history.at(i)); + break; + } + } +} + + +int HistoryManager::historyLimit() const +{ + return m_historyLimit; +} + + +void HistoryManager::setHistoryLimit(int limit) +{ + if (m_historyLimit == limit) + return; + m_historyLimit = limit; + checkForExpired(); + m_saveTimer->changeOccurred(); +} + + +void HistoryManager::clear() +{ + m_history.clear(); + m_lastSavedUrl = QString(); + m_saveTimer->changeOccurred(); + m_saveTimer->saveIfNeccessary(); + historyReset(); +} + + +void HistoryManager::loadSettings() +{ + int historyExpire = ReKonfig::expireHistory(); + int days; + switch (historyExpire) + { + case 0: days = 1; break; + case 1: days = 7; break; + case 2: days = 14; break; + case 3: days = 30; break; + case 4: days = 365; break; + case 5: days = -1; break; + default: days = -1; + } + m_historyLimit = days; +} + + +void HistoryManager::load() +{ + loadSettings(); + + QString historyFilePath = KStandardDirs::locateLocal("appdata" , "history"); + QFile historyFile(historyFilePath); + if (!historyFile.exists()) + return; + if (!historyFile.open(QFile::ReadOnly)) + { + kWarning() << "Unable to open history file" << historyFile.fileName(); + return; + } + + QList list; + QDataStream in(&historyFile); + // Double check that the history file is sorted as it is read in + bool needToSort = false; + HistoryItem lastInsertedItem; + QByteArray data; + QDataStream stream; + QBuffer buffer; + stream.setDevice(&buffer); + while (!historyFile.atEnd()) + { + in >> data; + buffer.close(); + buffer.setBuffer(&data); + buffer.open(QIODevice::ReadOnly); + quint32 ver; + stream >> ver; + if (ver != HISTORY_VERSION) + continue; + HistoryItem item; + stream >> item.url; + stream >> item.dateTime; + stream >> item.title; + + if (!item.dateTime.isValid()) + continue; + + if (item == lastInsertedItem) + { + if (lastInsertedItem.title.isEmpty() && !list.isEmpty()) + list[0].title = item.title; + continue; + } + + if (!needToSort && !list.isEmpty() && lastInsertedItem < item) + needToSort = true; + + list.prepend(item); + lastInsertedItem = item; + } + if (needToSort) + qSort(list.begin(), list.end()); + + setHistory(list, true); + + // If we had to sort re-write the whole history sorted + if (needToSort) + { + m_lastSavedUrl = QString(); + m_saveTimer->changeOccurred(); + } +} + + +void HistoryManager::save() +{ + bool saveAll = m_lastSavedUrl.isEmpty(); + int first = m_history.count() - 1; + if (!saveAll) + { + // find the first one to save + for (int i = 0; i < m_history.count(); ++i) + { + if (m_history.at(i).url == m_lastSavedUrl) + { + first = i - 1; + break; + } + } + } + if (first == m_history.count() - 1) + saveAll = true; + + QString historyFilePath = KStandardDirs::locateLocal("appdata" , "history"); + QFile historyFile(historyFilePath); + + // When saving everything use a temporary file to prevent possible data loss. + QTemporaryFile tempFile; + tempFile.setAutoRemove(false); + bool open = false; + if (saveAll) + { + open = tempFile.open(); + } + else + { + open = historyFile.open(QFile::Append); + } + + if (!open) + { + kWarning() << "Unable to open history file for saving" + << (saveAll ? tempFile.fileName() : historyFile.fileName()); + return; + } + + QDataStream out(saveAll ? &tempFile : &historyFile); + for (int i = first; i >= 0; --i) + { + QByteArray data; + QDataStream stream(&data, QIODevice::WriteOnly); + HistoryItem item = m_history.at(i); + stream << HISTORY_VERSION << item.url << item.dateTime << item.title; + out << data; + } + tempFile.close(); + + if (saveAll) + { + if (historyFile.exists() && !historyFile.remove()) + kWarning() << "History: error removing old history." << historyFile.errorString(); + if (!tempFile.rename(historyFile.fileName())) + kWarning() << "History: error moving new history over old." << tempFile.errorString() << historyFile.fileName(); + } + m_lastSavedUrl = m_history.value(0).url; +} + + +// -------------------------------------------------------------------------------------------------------------------------- + + +HistoryModel::HistoryModel(HistoryManager *history, QObject *parent) + : QAbstractTableModel(parent) + , m_history(history) +{ + Q_ASSERT(m_history); + connect(m_history, SIGNAL(historyReset()), this, SLOT(historyReset())); + connect(m_history, SIGNAL(entryRemoved(const HistoryItem &)), this, SLOT(historyReset())); + connect(m_history, SIGNAL(entryAdded(const HistoryItem &)), this, SLOT(entryAdded())); + connect(m_history, SIGNAL(entryUpdated(int)), this, SLOT(entryUpdated(int))); +} + + +void HistoryModel::historyReset() +{ + reset(); +} + + +void HistoryModel::entryAdded() +{ + beginInsertRows(QModelIndex(), 0, 0); + endInsertRows(); +} + + +void HistoryModel::entryUpdated(int offset) +{ + QModelIndex idx = index(offset, 0); + emit dataChanged(idx, idx); +} + + +QVariant HistoryModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal + && role == Qt::DisplayRole) + { + switch (section) + { + case 0: return i18n("Title"); + case 1: return i18n("Address"); + } + } + return QAbstractTableModel::headerData(section, orientation, role); +} + + +QVariant HistoryModel::data(const QModelIndex &index, int role) const +{ + QList lst = m_history->history(); + if (index.row() < 0 || index.row() >= lst.size()) + return QVariant(); + + const HistoryItem &item = lst.at(index.row()); + switch (role) + { + case DateTimeRole: + return item.dateTime; + case DateRole: + return item.dateTime.date(); + case UrlRole: + return QUrl(item.url); + case UrlStringRole: + return item.url; + case Qt::DisplayRole: + case Qt::EditRole: + { + switch (index.column()) + { + case 0: + // when there is no title try to generate one from the url + if (item.title.isEmpty()) + { + QString page = QFileInfo(QUrl(item.url).path()).fileName(); + if (!page.isEmpty()) + return page; + return item.url; + } + return item.title; + case 1: + return item.url; + } + } + case Qt::DecorationRole: + if (index.column() == 0) + { + return Application::instance()->icon(item.url); + } + } + return QVariant(); +} + + +int HistoryModel::columnCount(const QModelIndex &parent) const +{ + return (parent.isValid()) ? 0 : 2; +} + + +int HistoryModel::rowCount(const QModelIndex &parent) const +{ + return (parent.isValid()) ? 0 : m_history->history().count(); +} + + +bool HistoryModel::removeRows(int row, int count, const QModelIndex &parent) +{ + if (parent.isValid()) + return false; + int lastRow = row + count - 1; + beginRemoveRows(parent, row, lastRow); + QList lst = m_history->history(); + for (int i = lastRow; i >= row; --i) + lst.removeAt(i); + disconnect(m_history, SIGNAL(historyReset()), this, SLOT(historyReset())); + m_history->setHistory(lst); + connect(m_history, SIGNAL(historyReset()), this, SLOT(historyReset())); + endRemoveRows(); + return true; +} + + + +// ----------------------------------------------------------------------------------------------- + + +#define MOVEDROWS 10 + + +/* + Maps the first bunch of items of the source model to the root +*/ +HistoryMenuModel::HistoryMenuModel(HistoryTreeModel *sourceModel, QObject *parent) + : QAbstractProxyModel(parent) + , m_treeModel(sourceModel) +{ + setSourceModel(sourceModel); +} + + +int HistoryMenuModel::bumpedRows() const +{ + QModelIndex first = m_treeModel->index(0, 0); + if (!first.isValid()) + return 0; + return qMin(m_treeModel->rowCount(first), MOVEDROWS); +} + + +int HistoryMenuModel::columnCount(const QModelIndex &parent) const +{ + return m_treeModel->columnCount(mapToSource(parent)); +} + + +int HistoryMenuModel::rowCount(const QModelIndex &parent) const +{ + if (parent.column() > 0) + return 0; + + if (!parent.isValid()) + { + int folders = sourceModel()->rowCount(); + int bumpedItems = bumpedRows(); + if (bumpedItems <= MOVEDROWS + && bumpedItems == sourceModel()->rowCount(sourceModel()->index(0, 0))) + --folders; + return bumpedItems + folders; + } + + if (parent.internalId() == -1) + { + if (parent.row() < bumpedRows()) + return 0; + } + + QModelIndex idx = mapToSource(parent); + int defaultCount = sourceModel()->rowCount(idx); + if (idx == sourceModel()->index(0, 0)) + return defaultCount - bumpedRows(); + return defaultCount; +} + + +QModelIndex HistoryMenuModel::mapFromSource(const QModelIndex &sourceIndex) const +{ + // currently not used or autotested + Q_ASSERT(false); + int sr = m_treeModel->mapToSource(sourceIndex).row(); + return createIndex(sourceIndex.row(), sourceIndex.column(), sr); +} + + +QModelIndex HistoryMenuModel::mapToSource(const QModelIndex &proxyIndex) const +{ + if (!proxyIndex.isValid()) + return QModelIndex(); + + if (proxyIndex.internalId() == -1) + { + int bumpedItems = bumpedRows(); + if (proxyIndex.row() < bumpedItems) + return m_treeModel->index(proxyIndex.row(), proxyIndex.column(), m_treeModel->index(0, 0)); + if (bumpedItems <= MOVEDROWS && bumpedItems == sourceModel()->rowCount(m_treeModel->index(0, 0))) + --bumpedItems; + return m_treeModel->index(proxyIndex.row() - bumpedItems, proxyIndex.column()); + } + + QModelIndex historyIndex = m_treeModel->sourceModel()->index(proxyIndex.internalId(), proxyIndex.column()); + QModelIndex treeIndex = m_treeModel->mapFromSource(historyIndex); + return treeIndex; +} + + +QModelIndex HistoryMenuModel::index(int row, int column, const QModelIndex &parent) const +{ + if (row < 0 + || column < 0 || column >= columnCount(parent) + || parent.column() > 0) + return QModelIndex(); + if (!parent.isValid()) + return createIndex(row, column, -1); + + QModelIndex treeIndexParent = mapToSource(parent); + + int bumpedItems = 0; + if (treeIndexParent == m_treeModel->index(0, 0)) + bumpedItems = bumpedRows(); + QModelIndex treeIndex = m_treeModel->index(row + bumpedItems, column, treeIndexParent); + QModelIndex historyIndex = m_treeModel->mapToSource(treeIndex); + int historyRow = historyIndex.row(); + if (historyRow == -1) + historyRow = treeIndex.row(); + return createIndex(row, column, historyRow); +} + + +QModelIndex HistoryMenuModel::parent(const QModelIndex &index) const +{ + int offset = index.internalId(); + if (offset == -1 || !index.isValid()) + return QModelIndex(); + + QModelIndex historyIndex = m_treeModel->sourceModel()->index(index.internalId(), 0); + QModelIndex treeIndex = m_treeModel->mapFromSource(historyIndex); + QModelIndex treeIndexParent = treeIndex.parent(); + + int sr = m_treeModel->mapToSource(treeIndexParent).row(); + int bumpedItems = bumpedRows(); + if (bumpedItems <= MOVEDROWS && bumpedItems == sourceModel()->rowCount(sourceModel()->index(0, 0))) + --bumpedItems; + return createIndex(bumpedItems + treeIndexParent.row(), treeIndexParent.column(), sr); +} + + +// ------------------------------------------------------------------------------------------------------------- + + +HistoryMenu::HistoryMenu(QWidget *parent) + : ModelMenu(parent) + , m_history(0) +{ + connect(this, SIGNAL(activated(const QModelIndex &)), this, SLOT(activated(const QModelIndex &))); + setHoverRole(HistoryModel::UrlStringRole); +} + + +void HistoryMenu::activated(const QModelIndex &index) +{ + emit openUrl(index.data(HistoryModel::UrlRole).toUrl()); +} + + +bool HistoryMenu::prePopulated() +{ + if (!m_history) + { + m_history = Application::historyManager(); + m_historyMenuModel = new HistoryMenuModel(m_history->historyTreeModel(), this); + setModel(m_historyMenuModel); + } + // initial actions + for (int i = 0; i < m_initialActions.count(); ++i) + addAction(m_initialActions.at(i)); + if (!m_initialActions.isEmpty()) + addSeparator(); + setFirstSeparator(m_historyMenuModel->bumpedRows()); + + return false; +} + + +void HistoryMenu::postPopulated() +{ + if (m_history->history().count() > 0) + addSeparator(); + + KAction *showAllAction = new KAction(i18n("Show All History"), this); + connect(showAllAction, SIGNAL(triggered()), this, SLOT(showHistoryDialog())); + addAction(showAllAction); + + KAction *clearAction = new KAction(i18n("Clear History"), this); + connect(clearAction, SIGNAL(triggered()), m_history, SLOT(clear())); + addAction(clearAction); +} + + +void HistoryMenu::showHistoryDialog() +{ + HistoryDialog *dialog = new HistoryDialog(this); + connect(dialog, SIGNAL(openUrl(const KUrl&)), this, SIGNAL(openUrl(const KUrl&))); + dialog->show(); +} + + +void HistoryMenu::setInitialActions(QList actions) +{ + m_initialActions = actions; + for (int i = 0; i < m_initialActions.count(); ++i) + addAction(m_initialActions.at(i)); +} + + +// -------------------------------------------------------------------------------------------------------------- + + +TreeProxyModel::TreeProxyModel(QObject *parent) : QSortFilterProxyModel(parent) +{ + setSortRole(HistoryModel::DateTimeRole); + setFilterCaseSensitivity(Qt::CaseInsensitive); +} + + +bool TreeProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const +{ + if (!source_parent.isValid()) + return true; + return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); +} + + +// ----------------------------------------------------------------------------------------------------- + + +HistoryDialog::HistoryDialog(QWidget *parent, HistoryManager *setHistory) : QDialog(parent) +{ + HistoryManager *history = setHistory; + if (!history) + history = Application::historyManager(); + setupUi(this); + tree->setUniformRowHeights(true); + tree->setSelectionBehavior(QAbstractItemView::SelectRows); + tree->setTextElideMode(Qt::ElideMiddle); + QAbstractItemModel *model = history->historyTreeModel(); + TreeProxyModel *proxyModel = new TreeProxyModel(this); + connect(search, SIGNAL(textChanged(QString)), + proxyModel, SLOT(setFilterFixedString(QString))); + connect(removeButton, SIGNAL(clicked()), tree, SLOT(removeOne())); + connect(removeAllButton, SIGNAL(clicked()), history, SLOT(clear())); + proxyModel->setSourceModel(model); + tree->setModel(proxyModel); + tree->setExpanded(proxyModel->index(0, 0), true); + tree->setAlternatingRowColors(true); + QFontMetrics fm(font()); + int header = fm.width(QLatin1Char('m')) * 40; + tree->header()->resizeSection(0, header); + tree->header()->setStretchLastSection(true); + connect(tree, SIGNAL(activated(const QModelIndex&)), + this, SLOT(open())); + tree->setContextMenuPolicy(Qt::CustomContextMenu); + connect(tree, SIGNAL(customContextMenuRequested(const QPoint &)), + this, SLOT(customContextMenuRequested(const QPoint &))); +} + + +void HistoryDialog::customContextMenuRequested(const QPoint &pos) +{ + QMenu menu; + QModelIndex index = tree->indexAt(pos); + index = index.sibling(index.row(), 0); + if (index.isValid() && !tree->model()->hasChildren(index)) + { + menu.addAction(i18n("Open"), this, SLOT(open())); + menu.addSeparator(); + menu.addAction(i18n("Copy"), this, SLOT(copy())); + } + menu.addAction(i18n("Delete"), tree, SLOT(removeOne())); + menu.exec(QCursor::pos()); +} + + +void HistoryDialog::open() +{ + QModelIndex index = tree->currentIndex(); + if (!index.parent().isValid()) + return; + emit openUrl(index.data(HistoryModel::UrlRole).toUrl()); +} + + +void HistoryDialog::copy() +{ + QModelIndex index = tree->currentIndex(); + if (!index.parent().isValid()) + return; + QString url = index.data(HistoryModel::UrlStringRole).toString(); + + QClipboard *clipboard = QApplication::clipboard(); + clipboard->setText(url); +} + + +// --------------------------------------------------------------------------------------------------------------- + +HistoryFilterModel::HistoryFilterModel(QAbstractItemModel *sourceModel, QObject *parent) + : QAbstractProxyModel(parent), + m_loaded(false) +{ + setSourceModel(sourceModel); +} + + +int HistoryFilterModel::historyLocation(const QString &url) const +{ + load(); + if (!m_historyHash.contains(url)) + return 0; + return sourceModel()->rowCount() - m_historyHash.value(url); +} + + +QVariant HistoryFilterModel::data(const QModelIndex &index, int role) const +{ + return QAbstractProxyModel::data(index, role); +} + + +void HistoryFilterModel::setSourceModel(QAbstractItemModel *newSourceModel) +{ + if (sourceModel()) + { + disconnect(sourceModel(), SIGNAL(modelReset()), this, SLOT(sourceReset())); + disconnect(sourceModel(), SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), + this, SLOT(dataChanged(const QModelIndex &, const QModelIndex &))); + disconnect(sourceModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(sourceRowsInserted(const QModelIndex &, int, int))); + disconnect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(sourceRowsRemoved(const QModelIndex &, int, int))); + } + + QAbstractProxyModel::setSourceModel(newSourceModel); + + if (sourceModel()) + { + m_loaded = false; + connect(sourceModel(), SIGNAL(modelReset()), this, SLOT(sourceReset())); + connect(sourceModel(), SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), + this, SLOT(sourceDataChanged(const QModelIndex &, const QModelIndex &))); + connect(sourceModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(sourceRowsInserted(const QModelIndex &, int, int))); + connect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(sourceRowsRemoved(const QModelIndex &, int, int))); + } +} + + +void HistoryFilterModel::sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) +{ + emit dataChanged(mapFromSource(topLeft), mapFromSource(bottomRight)); +} + + +QVariant HistoryFilterModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + return sourceModel()->headerData(section, orientation, role); +} + + +void HistoryFilterModel::sourceReset() +{ + m_loaded = false; + reset(); +} + + +int HistoryFilterModel::rowCount(const QModelIndex &parent) const +{ + load(); + if (parent.isValid()) + return 0; + return m_historyHash.count(); +} + + +int HistoryFilterModel::columnCount(const QModelIndex &parent) const +{ + return (parent.isValid()) ? 0 : 2; +} + + +QModelIndex HistoryFilterModel::mapToSource(const QModelIndex &proxyIndex) const +{ + load(); + int sourceRow = sourceModel()->rowCount() - proxyIndex.internalId(); + return sourceModel()->index(sourceRow, proxyIndex.column()); +} + + +QModelIndex HistoryFilterModel::mapFromSource(const QModelIndex &sourceIndex) const +{ + load(); + QString url = sourceIndex.data(HistoryModel::UrlStringRole).toString(); + if (!m_historyHash.contains(url)) + return QModelIndex(); + + // This can be done in a binary search, but we can't use qBinary find + // because it can't take: qBinaryFind(m_sourceRow.end(), m_sourceRow.begin(), v); + // so if this is a performance bottlneck then convert to binary search, until then + // the cleaner/easier to read code wins the day. + int realRow = -1; + int sourceModelRow = sourceModel()->rowCount() - sourceIndex.row(); + + for (int i = 0; i < m_sourceRow.count(); ++i) + { + if (m_sourceRow.at(i) == sourceModelRow) + { + realRow = i; + break; + } + } + if (realRow == -1) + return QModelIndex(); + + return createIndex(realRow, sourceIndex.column(), sourceModel()->rowCount() - sourceIndex.row()); +} + + +QModelIndex HistoryFilterModel::index(int row, int column, const QModelIndex &parent) const +{ + load(); + if (row < 0 || row >= rowCount(parent) + || column < 0 || column >= columnCount(parent)) + return QModelIndex(); + + return createIndex(row, column, m_sourceRow[row]); +} + + +QModelIndex HistoryFilterModel::parent(const QModelIndex &) const +{ + return QModelIndex(); +} + + +void HistoryFilterModel::load() const +{ + if (m_loaded) + return; + m_sourceRow.clear(); + m_historyHash.clear(); + m_historyHash.reserve(sourceModel()->rowCount()); + for (int i = 0; i < sourceModel()->rowCount(); ++i) + { + QModelIndex idx = sourceModel()->index(i, 0); + QString url = idx.data(HistoryModel::UrlStringRole).toString(); + if (!m_historyHash.contains(url)) + { + m_sourceRow.append(sourceModel()->rowCount() - i); + m_historyHash[url] = sourceModel()->rowCount() - i; + } + } + m_loaded = true; +} + + +void HistoryFilterModel::sourceRowsInserted(const QModelIndex &parent, int start, int end) +{ + Q_ASSERT(start == end && start == 0); + Q_UNUSED(end); + if (!m_loaded) + return; + QModelIndex idx = sourceModel()->index(start, 0, parent); + QString url = idx.data(HistoryModel::UrlStringRole).toString(); + if (m_historyHash.contains(url)) + { + int sourceRow = sourceModel()->rowCount() - m_historyHash[url]; + int realRow = mapFromSource(sourceModel()->index(sourceRow, 0)).row(); + beginRemoveRows(QModelIndex(), realRow, realRow); + m_sourceRow.removeAt(realRow); + m_historyHash.remove(url); + endRemoveRows(); + } + beginInsertRows(QModelIndex(), 0, 0); + m_historyHash.insert(url, sourceModel()->rowCount() - start); + m_sourceRow.insert(0, sourceModel()->rowCount()); + endInsertRows(); +} + + +void HistoryFilterModel::sourceRowsRemoved(const QModelIndex &, int start, int end) +{ + Q_UNUSED(start); + Q_UNUSED(end); + sourceReset(); +} + + +/* + Removing a continuous block of rows will remove filtered rows too as this is + the users intention. +*/ +bool HistoryFilterModel::removeRows(int row, int count, const QModelIndex &parent) +{ + if (row < 0 || count <= 0 || row + count > rowCount(parent) || parent.isValid()) + return false; + int lastRow = row + count - 1; + disconnect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(sourceRowsRemoved(const QModelIndex &, int, int))); + beginRemoveRows(parent, row, lastRow); + int oldCount = rowCount(); + int start = sourceModel()->rowCount() - m_sourceRow.value(row); + int end = sourceModel()->rowCount() - m_sourceRow.value(lastRow); + sourceModel()->removeRows(start, end - start + 1); + endRemoveRows(); + connect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(sourceRowsRemoved(const QModelIndex &, int, int))); + m_loaded = false; + if (oldCount - count != rowCount()) + reset(); + return true; +} + + +// ------------------------------------------------------------------------------------------------------ + + +HistoryCompletionModel::HistoryCompletionModel(QObject *parent) + : QAbstractProxyModel(parent) +{ +} + + +QVariant HistoryCompletionModel::data(const QModelIndex &index, int role) const +{ + if (sourceModel() + && (role == Qt::EditRole || role == Qt::DisplayRole) + && index.isValid()) + { + QModelIndex idx = mapToSource(index); + idx = idx.sibling(idx.row(), 1); + QString urlString = idx.data(HistoryModel::UrlStringRole).toString(); + if (index.row() % 2) + { + QUrl url = urlString; + QString s = url.toString(QUrl::RemoveScheme + | QUrl::RemoveUserInfo + | QUrl::StripTrailingSlash); + return s.mid(2); // strip // from the front + } + return urlString; + } + return QAbstractProxyModel::data(index, role); +} + + +int HistoryCompletionModel::rowCount(const QModelIndex &parent) const +{ + return (parent.isValid() || !sourceModel()) ? 0 : sourceModel()->rowCount(parent) * 2; +} + + +int HistoryCompletionModel::columnCount(const QModelIndex &parent) const +{ + return (parent.isValid()) ? 0 : 1; +} + + +QModelIndex HistoryCompletionModel::mapFromSource(const QModelIndex &sourceIndex) const +{ + int row = sourceIndex.row() * 2; + return index(row, sourceIndex.column()); +} + + +QModelIndex HistoryCompletionModel::mapToSource(const QModelIndex &proxyIndex) const +{ + if (!sourceModel()) + return QModelIndex(); + int row = proxyIndex.row() / 2; + return sourceModel()->index(row, proxyIndex.column()); +} + + +QModelIndex HistoryCompletionModel::index(int row, int column, const QModelIndex &parent) const +{ + if (row < 0 || row >= rowCount(parent) + || column < 0 || column >= columnCount(parent)) + return QModelIndex(); + return createIndex(row, column, 0); +} + + +QModelIndex HistoryCompletionModel::parent(const QModelIndex &) const +{ + return QModelIndex(); +} + + +void HistoryCompletionModel::setSourceModel(QAbstractItemModel *newSourceModel) +{ + if (sourceModel()) + { + disconnect(sourceModel(), SIGNAL(modelReset()), this, SLOT(sourceReset())); + disconnect(sourceModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(sourceReset())); + disconnect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(sourceReset())); + } + + QAbstractProxyModel::setSourceModel(newSourceModel); + + if (newSourceModel) + { + connect(newSourceModel, SIGNAL(modelReset()), this, SLOT(sourceReset())); + connect(sourceModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(sourceReset())); + connect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(sourceReset())); + } + + reset(); +} + + +void HistoryCompletionModel::sourceReset() +{ + reset(); +} + + +// ------------------------------------------------------------------------------------------------------ + + +HistoryTreeModel::HistoryTreeModel(QAbstractItemModel *sourceModel, QObject *parent) + : QAbstractProxyModel(parent) +{ + setSourceModel(sourceModel); +} + + +QVariant HistoryTreeModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + return sourceModel()->headerData(section, orientation, role); +} + + +QVariant HistoryTreeModel::data(const QModelIndex &index, int role) const +{ + if ((role == Qt::EditRole || role == Qt::DisplayRole)) + { + int start = index.internalId(); + if (start == 0) + { + int offset = sourceDateRow(index.row()); + if (index.column() == 0) + { + QModelIndex idx = sourceModel()->index(offset, 0); + QDate date = idx.data(HistoryModel::DateRole).toDate(); + if (date == QDate::currentDate()) + return i18n("Earlier Today"); + return date.toString(QLatin1String("dddd, MMMM d, yyyy")); + } + if (index.column() == 1) + { + return i18np("1 item", "%1 items", rowCount(index.sibling(index.row(), 0))); + } + } + } + if (role == Qt::DecorationRole && index.column() == 0 && !index.parent().isValid()) + return KIcon("view-history"); + if (role == HistoryModel::DateRole && index.column() == 0 && index.internalId() == 0) + { + int offset = sourceDateRow(index.row()); + QModelIndex idx = sourceModel()->index(offset, 0); + return idx.data(HistoryModel::DateRole); + } + + return QAbstractProxyModel::data(index, role); +} + + +int HistoryTreeModel::columnCount(const QModelIndex &parent) const +{ + return sourceModel()->columnCount(mapToSource(parent)); +} + + +int HistoryTreeModel::rowCount(const QModelIndex &parent) const +{ + if (parent.internalId() != 0 + || parent.column() > 0 + || !sourceModel()) + return 0; + + // row count OF dates + if (!parent.isValid()) + { + if (!m_sourceRowCache.isEmpty()) + return m_sourceRowCache.count(); + QDate currentDate; + int rows = 0; + int totalRows = sourceModel()->rowCount(); + + for (int i = 0; i < totalRows; ++i) + { + QDate rowDate = sourceModel()->index(i, 0).data(HistoryModel::DateRole).toDate(); + if (rowDate != currentDate) + { + m_sourceRowCache.append(i); + currentDate = rowDate; + ++rows; + } + } + Q_ASSERT(m_sourceRowCache.count() == rows); + return rows; + } + + // row count FOR a date + int start = sourceDateRow(parent.row()); + int end = sourceDateRow(parent.row() + 1); + return (end - start); +} + + +// Translate the top level date row into the offset where that date starts +int HistoryTreeModel::sourceDateRow(int row) const +{ + if (row <= 0) + return 0; + + if (m_sourceRowCache.isEmpty()) + rowCount(QModelIndex()); + + if (row >= m_sourceRowCache.count()) + { + if (!sourceModel()) + return 0; + return sourceModel()->rowCount(); + } + return m_sourceRowCache.at(row); +} + + +QModelIndex HistoryTreeModel::mapToSource(const QModelIndex &proxyIndex) const +{ + int offset = proxyIndex.internalId(); + if (offset == 0) + return QModelIndex(); + int startDateRow = sourceDateRow(offset - 1); + return sourceModel()->index(startDateRow + proxyIndex.row(), proxyIndex.column()); +} + + +QModelIndex HistoryTreeModel::index(int row, int column, const QModelIndex &parent) const +{ + if (row < 0 + || column < 0 || column >= columnCount(parent) + || parent.column() > 0) + return QModelIndex(); + + if (!parent.isValid()) + return createIndex(row, column, 0); + return createIndex(row, column, parent.row() + 1); +} + + +QModelIndex HistoryTreeModel::parent(const QModelIndex &index) const +{ + int offset = index.internalId(); + if (offset == 0 || !index.isValid()) + return QModelIndex(); + return createIndex(offset - 1, 0, 0); +} + + +bool HistoryTreeModel::hasChildren(const QModelIndex &parent) const +{ + QModelIndex grandparent = parent.parent(); + if (!grandparent.isValid()) + return true; + return false; +} + + +Qt::ItemFlags HistoryTreeModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::NoItemFlags; + return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled; +} + + +bool HistoryTreeModel::removeRows(int row, int count, const QModelIndex &parent) +{ + if (row < 0 || count <= 0 || row + count > rowCount(parent)) + return false; + + if (parent.isValid()) + { + // removing pages + int offset = sourceDateRow(parent.row()); + return sourceModel()->removeRows(offset + row, count); + } + else + { + // removing whole dates + for (int i = row + count - 1; i >= row; --i) + { + QModelIndex dateParent = index(i, 0); + int offset = sourceDateRow(dateParent.row()); + if (!sourceModel()->removeRows(offset, rowCount(dateParent))) + return false; + } + } + return true; +} + + +void HistoryTreeModel::setSourceModel(QAbstractItemModel *newSourceModel) +{ + if (sourceModel()) + { + disconnect(sourceModel(), SIGNAL(modelReset()), this, SLOT(sourceReset())); + disconnect(sourceModel(), SIGNAL(layoutChanged()), this, SLOT(sourceReset())); + disconnect(sourceModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(sourceRowsInserted(const QModelIndex &, int, int))); + disconnect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(sourceRowsRemoved(const QModelIndex &, int, int))); + } + + QAbstractProxyModel::setSourceModel(newSourceModel); + + if (newSourceModel) + { + connect(sourceModel(), SIGNAL(modelReset()), this, SLOT(sourceReset())); + connect(sourceModel(), SIGNAL(layoutChanged()), this, SLOT(sourceReset())); + connect(sourceModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(sourceRowsInserted(const QModelIndex &, int, int))); + connect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(sourceRowsRemoved(const QModelIndex &, int, int))); + } + + reset(); +} + + +void HistoryTreeModel::sourceReset() +{ + m_sourceRowCache.clear(); + reset(); +} + + +void HistoryTreeModel::sourceRowsInserted(const QModelIndex &parent, int start, int end) +{ + Q_UNUSED(parent); // Avoid warnings when compiling release + Q_ASSERT(!parent.isValid()); + if (start != 0 || start != end) + { + m_sourceRowCache.clear(); + reset(); + return; + } + + m_sourceRowCache.clear(); + QModelIndex treeIndex = mapFromSource(sourceModel()->index(start, 0)); + QModelIndex treeParent = treeIndex.parent(); + if (rowCount(treeParent) == 1) + { + beginInsertRows(QModelIndex(), 0, 0); + endInsertRows(); + } + else + { + beginInsertRows(treeParent, treeIndex.row(), treeIndex.row()); + endInsertRows(); + } +} + + +QModelIndex HistoryTreeModel::mapFromSource(const QModelIndex &sourceIndex) const +{ + if (!sourceIndex.isValid()) + return QModelIndex(); + + if (m_sourceRowCache.isEmpty()) + rowCount(QModelIndex()); + + QList::iterator it; + it = qLowerBound(m_sourceRowCache.begin(), m_sourceRowCache.end(), sourceIndex.row()); + if (*it != sourceIndex.row()) + --it; + + int dateRow = qMax(0, it - m_sourceRowCache.begin()); + // FIXME fix crach on history submenu open. BUG:'ASSERT failure in QList::at: "index out of range"' + // it crashes when dateRow == 1 + // kDebug() << m_sourceRowCache << dateRow; + int row = sourceIndex.row() - m_sourceRowCache.at(dateRow); + return createIndex(row, sourceIndex.column(), dateRow + 1); +} + + +void HistoryTreeModel::sourceRowsRemoved(const QModelIndex &parent, int start, int end) +{ + Q_UNUSED(parent); // Avoid warnings when compiling release + Q_ASSERT(!parent.isValid()); + if (m_sourceRowCache.isEmpty()) + return; + for (int i = end; i >= start;) + { + QList::iterator it; + it = qLowerBound(m_sourceRowCache.begin(), m_sourceRowCache.end(), i); + // playing it safe + if (it == m_sourceRowCache.end()) + { + m_sourceRowCache.clear(); + reset(); + return; + } + + if (*it != i) + --it; + int row = qMax(0, it - m_sourceRowCache.begin()); + int offset = m_sourceRowCache[row]; + QModelIndex dateParent = index(row, 0); + // If we can remove all the rows in the date do that and skip over them + int rc = rowCount(dateParent); + if (i - rc + 1 == offset && start <= i - rc + 1) + { + beginRemoveRows(QModelIndex(), row, row); + m_sourceRowCache.removeAt(row); + i -= rc + 1; + } + else + { + beginRemoveRows(dateParent, i - offset, i - offset); + ++row; + --i; + } + for (int j = row; j < m_sourceRowCache.count(); ++j) + --m_sourceRowCache[j]; + endRemoveRows(); + } +} diff --git a/src/history.h b/src/history.h new file mode 100644 index 00000000..ecae00c3 --- /dev/null +++ b/src/history.h @@ -0,0 +1,402 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2007-2008 Trolltech ASA. All rights reserved +* Copyright (C) 2008 Benjamin C. Meyer +* Copyright (C) 2008-2009 by Andrea Diamantini +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + +#ifndef HISTORY_H +#define HISTORY_H + +// Local Includes +#include "modelmenu.h" + +// KDE Includes +#include +#include + +// Qt Includes +#include +#include +#include +#include +#include +#include + + +/** + * Elements in this class represent an history item + * + */ + +class HistoryItem +{ +public: + HistoryItem() {} + HistoryItem(const QString &u, + const QDateTime &d = QDateTime(), const QString &t = QString()) + : title(t), url(u), dateTime(d) {} + + inline bool operator==(const HistoryItem &other) const + { + return other.title == title + && other.url == url && other.dateTime == dateTime; + } + + // history is sorted in reverse + inline bool operator <(const HistoryItem &other) const + { + return dateTime > other.dateTime; + } + + QString title; + QString url; + QDateTime dateTime; +}; + + + +// --------------------------------------------------------------------------------------------------------------- + + +class AutoSaver; +class HistoryModel; +class HistoryFilterModel; +class HistoryTreeModel; + + +class HistoryManager : public QWebHistoryInterface +{ + Q_OBJECT + Q_PROPERTY(int historyLimit READ historyLimit WRITE setHistoryLimit) + +signals: + void historyReset(); + void entryAdded(const HistoryItem &item); + void entryRemoved(const HistoryItem &item); + void entryUpdated(int offset); + +public: + HistoryManager(QObject *parent = 0); + ~HistoryManager(); + + bool historyContains(const QString &url) const; + void addHistoryEntry(const QString &url); + void updateHistoryEntry(const KUrl &url, const QString &title); + void removeHistoryEntry(const KUrl &url, const QString &title = QString()); + + int historyLimit() const; + void setHistoryLimit(int limit); + + QList history() const; + void setHistory(const QList &history, bool loadedAndSorted = false); + + // History manager keeps around these models for use by the completer and other classes + HistoryModel *historyModel() const; + HistoryFilterModel *historyFilterModel() const; + HistoryTreeModel *historyTreeModel() const; + +public slots: + void clear(); + void loadSettings(); + +private slots: + void save(); + void checkForExpired(); + +protected: + void addHistoryEntry(const HistoryItem &item); + void removeHistoryEntry(const HistoryItem &item); + +private: + void load(); + + AutoSaver *m_saveTimer; + int m_historyLimit; + QTimer m_expiredTimer; + QList m_history; + QString m_lastSavedUrl; + + HistoryModel *m_historyModel; + HistoryFilterModel *m_historyFilterModel; + HistoryTreeModel *m_historyTreeModel; +}; + + +// -------------------------------------------------------------------------------------------------------- + + +class HistoryModel : public QAbstractTableModel +{ + Q_OBJECT + +public slots: + void historyReset(); + void entryAdded(); + void entryUpdated(int offset); + +public: + enum Roles + { + DateRole = Qt::UserRole + 1, + DateTimeRole = Qt::UserRole + 2, + UrlRole = Qt::UserRole + 3, + UrlStringRole = Qt::UserRole + 4 + }; + + HistoryModel(HistoryManager *history, QObject *parent = 0); + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); + +private: + HistoryManager *m_history; +}; + + +// ---------------------------------------------------------------------------------------------------- + +/** + * Proxy model that will remove any duplicate entries. + * Both m_sourceRow and m_historyHash store their offsets not from + * the front of the list, but as offsets from the back. + * + */ + +class HistoryFilterModel : public QAbstractProxyModel +{ + Q_OBJECT + +public: + HistoryFilterModel(QAbstractItemModel *sourceModel, QObject *parent = 0); + + inline bool historyContains(const QString &url) const + { + load(); return m_historyHash.contains(url); + } + int historyLocation(const QString &url) const; + + QModelIndex mapFromSource(const QModelIndex &sourceIndex) const; + QModelIndex mapToSource(const QModelIndex &proxyIndex) const; + void setSourceModel(QAbstractItemModel *sourceModel); + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QModelIndex index(int, int, const QModelIndex& = QModelIndex()) const; + QModelIndex parent(const QModelIndex& index = QModelIndex()) const; + bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + +private slots: + void sourceReset(); + void sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); + void sourceRowsInserted(const QModelIndex &parent, int start, int end); + void sourceRowsRemoved(const QModelIndex &, int, int); + +private: + void load() const; + + mutable QList m_sourceRow; + mutable QHash m_historyHash; + mutable bool m_loaded; +}; + + +// ---------------------------------------------------------------------------------------------------------------------- + +/** + * The history menu + * - Removes the first twenty entries and puts them as children of the top level. + * - If there are less then twenty entries then the first folder is also removed. + * + * The mapping is done by knowing that HistoryTreeModel is over a table + * We store that row offset in our index's private data. + * + */ + +class HistoryMenuModel : public QAbstractProxyModel +{ + Q_OBJECT + +public: + HistoryMenuModel(HistoryTreeModel *sourceModel, QObject *parent = 0); + int columnCount(const QModelIndex &parent) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + QModelIndex mapFromSource(const QModelIndex & sourceIndex) const; + QModelIndex mapToSource(const QModelIndex & proxyIndex) const; + QModelIndex index(int, int, const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &index = QModelIndex()) const; + + int bumpedRows() const; + +private: + HistoryTreeModel *m_treeModel; +}; + + +// --------------------------------------------------------------------------------------------- + +/** + * Menu that is dynamically populated from the history + * + */ + +class HistoryMenu : public ModelMenu +{ + Q_OBJECT + +signals: + void openUrl(const KUrl &url); + +public: + HistoryMenu(QWidget *parent = 0); + void setInitialActions(QList actions); + +protected: + bool prePopulated(); + void postPopulated(); + +private slots: + void activated(const QModelIndex &index); + void showHistoryDialog(); + +private: + HistoryManager *m_history; + HistoryMenuModel *m_historyMenuModel; + QList m_initialActions; +}; + + +// ---------------------------------------------------------------------------------------- + +/** + * Proxy model for the history model that + * exposes each url http://www.foo.com and + * it url starting at the host www.foo.com + * + */ + +class HistoryCompletionModel : public QAbstractProxyModel +{ + Q_OBJECT + +public: + HistoryCompletionModel(QObject *parent = 0); + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QModelIndex mapFromSource(const QModelIndex &sourceIndex) const; + QModelIndex mapToSource(const QModelIndex &proxyIndex) const; + QModelIndex index(int, int, const QModelIndex& = QModelIndex()) const; + QModelIndex parent(const QModelIndex& index = QModelIndex()) const; + void setSourceModel(QAbstractItemModel *sourceModel); + +private slots: + void sourceReset(); + +}; + + +// --------------------------------------------------------------------------------------- + +/** + * Proxy model for the history model that converts the list + * into a tree, one top level node per day. + * Used in the HistoryDialog. + * + */ + +class HistoryTreeModel : public QAbstractProxyModel +{ + Q_OBJECT + +public: + HistoryTreeModel(QAbstractItemModel *sourceModel, QObject *parent = 0); + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + int columnCount(const QModelIndex &parent) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + QModelIndex mapFromSource(const QModelIndex &sourceIndex) const; + QModelIndex mapToSource(const QModelIndex &proxyIndex) const; + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &index = QModelIndex()) const; + bool hasChildren(const QModelIndex &parent = QModelIndex()) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + + void setSourceModel(QAbstractItemModel *sourceModel); + +private slots: + void sourceReset(); + void sourceRowsInserted(const QModelIndex &parent, int start, int end); + void sourceRowsRemoved(const QModelIndex &parent, int start, int end); + +private: + int sourceDateRow(int row) const; + mutable QList m_sourceRowCache; + +}; + + +// ----------------------------------------------------------------------------------------------------------------- + +/** + * A modified QSortFilterProxyModel that always accepts + * the root nodes in the tree + * so filtering is only done on the children. + * Used in the HistoryDialog. + * + */ + +class TreeProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT + +public: + TreeProxyModel(QObject *parent = 0); + +protected: + bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const; +}; + + +// ------------------------------------------------------------------------------------------ + +#include "ui_history.h" + +class HistoryDialog : public QDialog, public Ui_HistoryDialog +{ + Q_OBJECT + +signals: + void openUrl(const KUrl &url); + +public: + HistoryDialog(QWidget *parent = 0, HistoryManager *history = 0); + +private slots: + void customContextMenuRequested(const QPoint &pos); + void open(); + void copy(); + +}; + +#endif // HISTORY_H + diff --git a/src/history.ui b/src/history.ui new file mode 100644 index 00000000..806bc9ad --- /dev/null +++ b/src/history.ui @@ -0,0 +1,101 @@ + + HistoryDialog + + + + 0 + 0 + 758 + 450 + + + + History + + + + + + Qt::Horizontal + + + + 252 + 20 + + + + + + + + + + + + + + + + &Remove + + + + + + + Remove &All + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + QDialogButtonBox::Ok + + + + + + + + + + EditTreeView + QTreeView +
edittreeview.h
+
+
+ + + + buttonBox + accepted() + HistoryDialog + accept() + + + 472 + 329 + + + 461 + 356 + + + + +
diff --git a/src/lineedit.cpp b/src/lineedit.cpp new file mode 100644 index 00000000..c9df7bfa --- /dev/null +++ b/src/lineedit.cpp @@ -0,0 +1,85 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2009 by Paweł Prażak +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + +// Self Includes +#include "lineedit.h" +#include "lineedit.moc" + +// Qt Includes +#include +#include +#include + +// KDE Includes +#include + +// Local Includes + +LineEdit::LineEdit(QWidget* parent) + : KLineEdit(parent) +{ + setMinimumWidth(180); + setFocusPolicy(Qt::WheelFocus); + + setHandleSignals(true); +} + + +LineEdit::~LineEdit() +{ +} + + +void LineEdit::keyPressEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_Escape) + { + clearFocus(); + event->accept(); + } + + KLineEdit::keyPressEvent(event); +} + + +void LineEdit::contextMenuEvent(QContextMenuEvent *event) +{ + KLineEdit::contextMenuEvent(event); +} + + +void LineEdit::focusInEvent(QFocusEvent *event) +{ + selectAll(); + + KLineEdit::focusInEvent(event); +} + + +void LineEdit::focusOutEvent(QFocusEvent *event) +{ + KLineEdit::focusOutEvent(event); + + // reset cursor state and deselect + setCursorPosition(0); + deselect(); +} + + + diff --git a/src/lineedit.h b/src/lineedit.h new file mode 100644 index 00000000..f1bd8f88 --- /dev/null +++ b/src/lineedit.h @@ -0,0 +1,50 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2009 by Paweł Prażak +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + +#ifndef LINEEDIT_H +#define LINEEDIT_H + +// Qt Includes + +// KDE Includes +#include + +// Local Includes + +class QContextMenuEvent; +class QFocusEvent; +class QKeyEvent; + +class LineEdit : public KLineEdit +{ + Q_OBJECT + +public: + explicit LineEdit(QWidget *parent = 0); + virtual ~LineEdit(); + +protected: + virtual void keyPressEvent(QKeyEvent*); + virtual void contextMenuEvent(QContextMenuEvent*); + virtual void focusInEvent(QFocusEvent*); + virtual void focusOutEvent(QFocusEvent*); +}; + +#endif // LINEEDIT_H diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 00000000..5ada94b4 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,98 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2008-2009 by Andrea Diamantini +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + +#include "application.h" + +#include +#include +#include + + +static const char description[] = + I18N_NOOP("KDE Browser Webkit Based"); + + +static const char version[] = "0.1alpha"; + + +int main(int argc, char **argv) +{ + KAboutData about("rekonq", + 0, + ki18n("rekonq"), + version, + ki18n(description), + KAboutData::License_GPL_V3, + ki18n("(C) 2008-2009 Andrea Diamantini"), + KLocalizedString(), + "http://rekonq.sourceforge.net", + "rekonq@kde.org" + ); + + // about authors + about.addAuthor(ki18n("Andrea Diamantini"), + ki18n("Project Lead, Developer, Italian translation"), + "adjam7@gmail.com", + "http://www.adjam.org"); + + about.addAuthor(ki18n("Domrachev Alexandr"), + ki18n("Developer, Russian translation"), + "alexandr.domrachev@gmail.com", + ""); + + about.addAuthor(ki18n("Pawel Prazak"), + ki18n("Developer"), + "kojots350@gmail.com", + ""); + + about.addAuthor(ki18n("Panagiotis Papadopoulos"), + ki18n("German translation"), + "pano_90@gmx.net", + ""); + +// about.addAuthor(ki18n("your name"), +// ki18n("your role"), +// "your mail", +// "your site"); + + // Initialize command line args + KCmdLineArgs::init(argc, argv, &about); + + // Define the command line options using KCmdLineOptions + KCmdLineOptions options; + + // adding URL option + options.add("+[URL]" , ki18n("Location to open")); + + // Register the supported options + KCmdLineArgs::addCmdLineOptions(options); + + // Add options from Application class + Application::addCmdLineOptions(); + + if (!Application::start()) + { + kWarning() << "rekonq is already running!"; + return 0; + } + + Application app; + return app.exec(); +} diff --git a/src/mainview.cpp b/src/mainview.cpp new file mode 100644 index 00000000..d4edeb19 --- /dev/null +++ b/src/mainview.cpp @@ -0,0 +1,716 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2008 by Andrea Diamantini +* Copyright (C) 2009 by Paweł Prażak +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + + +// Self Includes +#include "mainview.h" +#include "mainview.moc" + +// Auto Includes +#include "rekonq.h" + +// Local Includes +#include "tabbar.h" +#include "application.h" +#include "mainwindow.h" +#include "history.h" +#include "stackedurlbar.h" +#include "urlbar.h" +#include "webview.h" + +// KDE Includes +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Qt Includes +#include +#include +#include + + + +MainView::MainView(QWidget *parent) + : KTabWidget(parent) + , m_recentlyClosedTabsAction(0) + , m_recentlyClosedTabsMenu(new KMenu(this)) + , m_urlBars(new StackedUrlBar(this)) + , m_tabBar(new TabBar(this)) +{ + // setting tabbar + setTabBar(m_tabBar); + + // loading pixmap path + m_loadingGitPath = KStandardDirs::locate("appdata" , "pics/loading.gif"); + + // connecting tabbar signals + connect(m_tabBar, SIGNAL(closeTab(int)), this, SLOT(slotCloseTab(int))); + connect(m_tabBar, SIGNAL(cloneTab(int)), this, SLOT(slotCloneTab(int))); + connect(m_tabBar, SIGNAL(closeOtherTabs(int)), this, SLOT(slotCloseOtherTabs(int))); + connect(m_tabBar, SIGNAL(reloadTab(int)), this, SLOT(slotReloadTab(int))); + connect(m_tabBar, SIGNAL(reloadAllTabs()), this, SLOT(slotReloadAllTabs())); + connect(m_tabBar, SIGNAL(tabMoved(int, int)), this, SLOT(moveTab(int, int))); + + // current page index changing + connect(this, SIGNAL(currentChanged(int)), this, SLOT(slotCurrentChanged(int))); + + setTabsClosable(true); + connect(m_tabBar, SIGNAL(tabCloseRequested(int)), this, SLOT(slotCloseTab(int))); + + QTimer::singleShot(0, this, SLOT(postLaunch())); +} + + +MainView::~MainView() +{ +} + + +void MainView::postLaunch() +{ + // Recently Closed Tab Action + connect(m_recentlyClosedTabsMenu, SIGNAL(aboutToShow()), this, SLOT(aboutToShowRecentTabsMenu())); + connect(m_recentlyClosedTabsMenu, SIGNAL(triggered(QAction *)), this, SLOT(aboutToShowRecentTriggeredAction(QAction *))); + m_recentlyClosedTabsAction = new KAction(i18n("Recently Closed Tabs"), this); + m_recentlyClosedTabsAction->setMenu(m_recentlyClosedTabsMenu); + m_recentlyClosedTabsAction->setEnabled(false); +} + +void MainView::showTabBar() +{ + if (ReKonfig::alwaysShowTabBar()) + { + if (m_tabBar->isHidden()) + { + m_tabBar->show(); + } + return; + } + + if (m_tabBar->count() == 1) + { + m_tabBar->hide(); + } + else + { + if (m_tabBar->isHidden()) + { + m_tabBar->show(); + } + } +} + + +void MainView::slotWebReload() +{ + WebView *webView = currentWebView(); + QWebPage *currentParent = webView->webPage(); + QAction *action = currentParent->action(QWebPage::Reload); + action->trigger(); +} + + +void MainView::slotWebStop() +{ + WebView *webView = currentWebView(); + QWebPage *currentParent = webView->webPage(); + QAction *action = currentParent->action(QWebPage::Stop); + action->trigger(); +} + + +void MainView::slotWebBack() +{ + WebView *webView = currentWebView(); + QWebPage *currentParent = webView->webPage(); + QAction *action = currentParent->action(QWebPage::Back); + action->trigger(); +} + + +void MainView::slotWebForward() +{ + WebView *webView = currentWebView(); + QWebPage *currentParent = webView->webPage(); + QAction *action = currentParent->action(QWebPage::Forward); + action->trigger(); +} + + +void MainView::slotWebUndo() +{ + WebView *webView = currentWebView(); + QWebPage *currentParent = webView->webPage(); + QAction *action = currentParent->action(QWebPage::Undo); + action->trigger(); +} + + +void MainView::slotWebRedo() +{ + WebView *webView = currentWebView(); + QWebPage *currentParent = webView->webPage(); + QAction *action = currentParent->action(QWebPage::Redo); + action->trigger(); +} + + +void MainView::slotWebCut() +{ + WebView *webView = currentWebView(); + QWebPage *currentParent = webView->webPage(); + QAction *action = currentParent->action(QWebPage::Cut); + action->trigger(); +} + + +void MainView::slotWebCopy() +{ + WebView *webView = currentWebView(); + QWebPage *currentParent = webView->webPage(); + QAction *action = currentParent->action(QWebPage::Copy); + action->trigger(); +} + + +void MainView::slotWebPaste() +{ + WebView *webView = currentWebView(); + QWebPage *currentParent = webView->webPage(); + QAction *action = currentParent->action(QWebPage::Paste); + action->trigger(); +} + + +void MainView::clear() +{ + // clear the recently closed tabs + m_recentlyClosedTabs.clear(); + // clear the line edit history + for (int i = 0; i < m_urlBars->count(); ++i) + { + /// TODO What exacly do we need to clear here? + urlBar(i)->clearHistory(); + urlBar(i)->clear(); + } +} + + +// When index is -1 index chooses the current tab +void MainView::slotReloadTab(int index) +{ + if (index < 0) + index = currentIndex(); + if (index < 0 || index >= count()) + return; + + QWidget *widget = this->widget(index); + if (WebView *tab = qobject_cast(widget)) + tab->reload(); +} + + +void MainView::slotCurrentChanged(int index) +{ + WebView *webView = this->webView(index); + if (!webView) + return; + + Q_ASSERT(m_urlBars->count() == count()); + + WebView *oldWebView = this->webView(m_urlBars->currentIndex()); + if (oldWebView) + { + disconnect(oldWebView, SIGNAL(statusBarMessage(const QString&)), + this, SIGNAL(showStatusBarMessage(const QString&))); + disconnect(oldWebView->page(), SIGNAL(linkHovered(const QString&, const QString&, const QString&)), + this, SIGNAL(linkHovered(const QString&))); + disconnect(oldWebView, SIGNAL(loadProgress(int)), + this, SIGNAL(loadProgress(int))); + } + + connect(webView, SIGNAL(statusBarMessage(const QString&)), this, SIGNAL(showStatusBarMessage(const QString&))); + connect(webView->page(), SIGNAL(linkHovered(const QString&, const QString&, const QString&)), this, SIGNAL(linkHovered(const QString&))); + connect(webView, SIGNAL(loadProgress(int)), this, SIGNAL(loadProgress(int))); + + emit setCurrentTitle(webView->title()); + m_urlBars->setCurrentIndex(index); + emit loadProgress(webView->progress()); + emit showStatusBarMessage(webView->lastStatusBarText()); + + // set focus to the current webview + webView->setFocus(); +} + + +UrlBar *MainView::urlBar(int index) const +{ + if (index == -1) + { + index = m_urlBars->currentIndex(); + } + UrlBar *urlBar = m_urlBars->urlBar(index); + if (urlBar) + { + return urlBar; + } + kWarning() << "URL bar with index" << index << "not found. Returning NULL. (line:" << __LINE__ << ")"; + return NULL; +} + + +WebView *MainView::webView(int index) const +{ + QWidget *widget = this->widget(index); + if (WebView *webView = qobject_cast(widget)) + { + return webView; + } + + kWarning() << "WebView with index " << index << "not found. Returning NULL." ; + return 0; +} + + +WebView *MainView::newWebView(Rekonq::OpenType type) +{ + // line edit + UrlBar *urlBar = new UrlBar; // Ownership of widget is passed on to the QStackedWidget (addWidget method). + connect(urlBar, SIGNAL(activated(const KUrl&)), this, SLOT(loadUrl(const KUrl&))); + m_urlBars->addUrlBar(urlBar); + + WebView *webView = new WebView; // should be deleted on tab close + + // connecting webview with urlbar + connect(webView, SIGNAL(loadProgress(int)), urlBar, SLOT(slotUpdateProgress(int))); + connect(webView, SIGNAL(loadFinished(bool)), urlBar, SLOT(slotLoadFinished(bool))); + connect(webView, SIGNAL(urlChanged(const QUrl &)), urlBar, SLOT(setUrl(const QUrl &))); + connect(webView, SIGNAL(iconChanged()), urlBar, SLOT(slotUpdateUrl())); + + // connecting webview with mainview + connect(webView, SIGNAL(loadStarted()), this, SLOT(webViewLoadStarted())); + connect(webView, SIGNAL(loadProgress(int)), this, SLOT(webViewLoadProgress(int))); + connect(webView, SIGNAL(loadFinished(bool)), this, SLOT(webViewLoadFinished(bool))); + connect(webView, SIGNAL(iconChanged()), this, SLOT(webViewIconChanged())); + connect(webView, SIGNAL(titleChanged(const QString &)), this, SLOT(webViewTitleChanged(const QString &))); + connect(webView, SIGNAL(urlChanged(const QUrl &)), this, SLOT(webViewUrlChanged(const QUrl &))); + + connect(webView, SIGNAL(ctrlTabPressed()), this, SLOT(nextTab())); + connect(webView, SIGNAL(shiftCtrlTabPressed()), this, SLOT(previousTab())); + + // connecting webPage signals with mainview + connect(webView->page(), SIGNAL(windowCloseRequested()), + this, SLOT(windowCloseRequested())); + connect(webView->page(), SIGNAL(geometryChangeRequested(const QRect &)), + this, SIGNAL(geometryChangeRequested(const QRect &))); + connect(webView->page(), SIGNAL(printRequested(QWebFrame *)), + this, SIGNAL(printRequested(QWebFrame *))); + connect(webView->page(), SIGNAL(menuBarVisibilityChangeRequested(bool)), + this, SIGNAL(menuBarVisibilityChangeRequested(bool))); + connect(webView->page(), SIGNAL(statusBarVisibilityChangeRequested(bool)), + this, SIGNAL(statusBarVisibilityChangeRequested(bool))); + connect(webView->page(), SIGNAL(toolBarVisibilityChangeRequested(bool)), + this, SIGNAL(toolBarVisibilityChangeRequested(bool))); + + addTab(webView, i18n("(Untitled)")); + + switch(type) + { + case Rekonq::Default: + if (!m_makeBackTab) + { + setCurrentWidget(webView); // this method does NOT take ownership of webView + urlBar->setFocus(); + } + break; + case Rekonq::New: + setCurrentWidget(webView); // this method does NOT take ownership of webView + urlBar->setFocus(); + break; + case Rekonq::Background: + break; + }; + + emit tabsChanged(); + + showTabBar(); + + return webView; +} + + +void MainView::slotReloadAllTabs() +{ + for (int i = 0; i < count(); ++i) + { + QWidget *tabWidget = widget(i); + if (WebView *tab = qobject_cast(tabWidget)) + { + tab->reload(); + } + } +} + + +void MainView::windowCloseRequested() +{ + + WebPage *webPage = qobject_cast(sender()); + WebView *webView = qobject_cast(webPage->view()); + int index = webViewIndex(webView); + + if (index >= 0) + { + if (count() == 1) + { + Application::instance()->mainWindow()->close(); + } + else + { + slotCloseTab(index); + } + } + else + { + kWarning() << "Invalid tab index" << "line:" << __LINE__; + } +} + + +void MainView::slotCloseOtherTabs(int index) +{ + if (-1 == index) + return; + + for (int i = count() - 1; i > index; --i) + { + slotCloseTab(i); + } + + for (int i = index - 1; i >= 0; --i) + { + slotCloseTab(i); + } + + showTabBar(); +} + + +// When index is -1 index chooses the current tab +void MainView::slotCloneTab(int index) +{ + if (index < 0) + index = currentIndex(); + if (index < 0 || index >= count()) + return; + WebView *tab = newWebView(); + tab->setUrl(webView(index)->url()); + + showTabBar(); +} + + +// When index is -1 index chooses the current tab +void MainView::slotCloseTab(int index) +{ + // do nothing if just one tab is opened + if (count() == 1) + return; + + if (index < 0) + index = currentIndex(); + if (index < 0 || index >= count()) + return; + + bool hasFocus = false; + if (WebView *tab = webView(index)) + { + if (tab->isModified()) + { + int risp = KMessageBox::questionYesNo(this , + i18n("You have modified this page and when closing it you would lose the modification.\n" + "Do you really want to close this page?\n"), + i18n("Do you really want to close this page?") + ); + if (risp == KMessageBox::No) + return; + } + hasFocus = tab->hasFocus(); + + m_recentlyClosedTabsAction->setEnabled(true); + m_recentlyClosedTabs.prepend(tab->url()); + + // don't add empty urls + if (tab->url().isValid()) + { + m_recentlyClosedTabs.prepend(tab->url()); + } + + if (m_recentlyClosedTabs.size() >= MainView::m_recentlyClosedTabsSize) + { + m_recentlyClosedTabs.removeLast(); + } + } + + QWidget *urlBar = m_urlBars->urlBar(index); + m_urlBars->removeWidget(urlBar); + urlBar->deleteLater(); // urlBar is scheduled for deletion. + + QWidget *webView = widget(index); + removeTab(index); + webView->deleteLater(); // webView is scheduled for deletion. + + emit tabsChanged(); + + if (hasFocus && count() > 0) + { + currentWebView()->setFocus(); + } + + showTabBar(); +} + + +void MainView::webViewLoadStarted() +{ + WebView *webView = qobject_cast(sender()); + int index = webViewIndex(webView); + if (-1 != index) + { + QLabel *label = animatedLoading(index, true); + if (label->movie()) + { + label->movie()->start(); + } + } + + if (index != currentIndex()) + return; + + emit showStatusBarMessage(i18n("Loading...")); +} + + +void MainView::webViewLoadProgress(int progress) +{ + WebView *webView = qobject_cast(sender()); + int index = webViewIndex(webView); + if (index != currentIndex() || index < 0) + { + return; + } + + double totalBytes = static_cast(webView->webPage()->totalBytes() / 1024); + + QString message = i18n("Loading %1% (%2 %3)...", progress, totalBytes, QLatin1String("kB")); + emit showStatusBarMessage(message); +} + + +void MainView::webViewLoadFinished(bool ok) +{ + WebView *webView = qobject_cast(sender()); + int index = webViewIndex(webView); + + if (-1 != index) + { + QLabel *label = animatedLoading(index, true); + QMovie *movie = label->movie(); + if (movie) + movie->stop(); + } + + webViewIconChanged(); + + // don't display messages for background tabs + if (index != currentIndex()) + { + return; + } + + if (ok) + emit showStatusBarMessage(i18n("Done")); + else + emit showStatusBarMessage(i18n("Failed to load")); +} + + +void MainView::webViewIconChanged() +{ + WebView *webView = qobject_cast(sender()); + int index = webViewIndex(webView); + if (-1 != index) + { + QIcon icon = Application::instance()->icon(webView->url()); + QLabel *label = animatedLoading(index, false); + QMovie *movie = label->movie(); + delete movie; + label->setMovie(0); + label->setPixmap(icon.pixmap(16, 16)); + } +} + + +void MainView::webViewTitleChanged(const QString &title) +{ + QString tabTitle = title; + if (title.isEmpty()) + { + tabTitle = i18n("(Untitled)"); + } + WebView *webView = qobject_cast(sender()); + int index = webViewIndex(webView); + if (-1 != index) + { + setTabText(index, tabTitle); + } + if (currentIndex() == index) + { + emit setCurrentTitle(tabTitle); + } + Application::historyManager()->updateHistoryEntry(webView->url(), tabTitle); +} + + +void MainView::webViewUrlChanged(const QUrl &url) +{ + WebView *webView = qobject_cast(sender()); + int index = webViewIndex(webView); + if (-1 != index) + { + m_tabBar->setTabData(index, url); + } + emit tabsChanged(); +} + + +void MainView::aboutToShowRecentTabsMenu() +{ + m_recentlyClosedTabsMenu->clear(); + for (int i = 0; i < m_recentlyClosedTabs.count(); ++i) + { + KAction *action = new KAction(m_recentlyClosedTabsMenu); + action->setData(m_recentlyClosedTabs.at(i)); + QIcon icon = Application::instance()->icon(m_recentlyClosedTabs.at(i)); + action->setIcon(icon); + action->setText(m_recentlyClosedTabs.at(i).prettyUrl()); + m_recentlyClosedTabsMenu->addAction(action); + } +} + + +void MainView::aboutToShowRecentTriggeredAction(QAction *action) +{ + KUrl url = action->data().toUrl(); + loadUrl(url); +} + + +void MainView::loadUrl(const KUrl &url) +{ + if (url.isEmpty()) + return; + + currentUrlBar()->setUrl(url.prettyUrl()); + + WebView *webView = currentWebView(); + + KUrl loadingUrl(url); + + if (loadingUrl.isRelative()) + { + QString fn = loadingUrl.url(KUrl::RemoveTrailingSlash); + loadingUrl.setUrl("//" + fn); + loadingUrl.setScheme("http"); + } + + if (webView) + { + webView->load(loadingUrl); + webView->setFocus(); + } +} + + +void MainView::nextTab() +{ + int next = currentIndex() + 1; + if (next == count()) + next = 0; + setCurrentIndex(next); +} + + +void MainView::previousTab() +{ + int next = currentIndex() - 1; + if (next < 0) + next = count() - 1; + setCurrentIndex(next); +} + + +void MainView::moveTab(int fromIndex, int toIndex) +{ + QWidget *lineEdit = m_urlBars->widget(fromIndex); + m_urlBars->removeWidget(lineEdit); + m_urlBars->insertWidget(toIndex, lineEdit); +} + + +QLabel *MainView::animatedLoading(int index, bool addMovie) +{ + if (index == -1) + return 0; + + QLabel *label = qobject_cast(m_tabBar->tabButton(index, QTabBar::LeftSide)); + if (!label) + { + label = new QLabel(this); + } + if (addMovie && !label->movie()) + { + QMovie *movie = new QMovie(m_loadingGitPath, QByteArray(), label); + movie->setSpeed(50); + label->setMovie(movie); + movie->start(); + } + m_tabBar->setTabButton(index, QTabBar::LeftSide, 0); + m_tabBar->setTabButton(index, QTabBar::LeftSide, label); + return label; +} + + +void MainView::mouseDoubleClickEvent(QMouseEvent *event) +{ + if (!childAt(event->pos())) + { + newWebView(Rekonq::New); + return; + } + KTabWidget::mouseDoubleClickEvent(event); +} \ No newline at end of file diff --git a/src/mainview.h b/src/mainview.h new file mode 100644 index 00000000..372902dd --- /dev/null +++ b/src/mainview.h @@ -0,0 +1,198 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2008 by Andrea Diamantini +* Copyright (C) 2009 by Paweł Prażak +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + + + +#ifndef TABWIDGET_H +#define TABWIDGET_H + +// Local Includes +#include "webview.h" +#include "application.h" + +// KDE Includes +#include + +// Forward Declarations +class QLineEdit; +class QUrl; +class QWebFrame; +class QLabel; + +class KAction; +class KCompletion; +class KMenu; +class KUrl; + +class HistoryCompletionModel; +class StackedUrlBar; +class TabBar; +class UrlBar; + + +/** + * This class represent rekonq Main View. It contains all WebViews and a stack widget + * of associated line edits. + * + */ + +class MainView : public KTabWidget +{ + Q_OBJECT + +public: + MainView(QWidget *parent = 0); + ~MainView(); + +public: + + UrlBar *urlBar(int index) const; + UrlBar *currentUrlBar() const { return urlBar(-1); } + WebView *webView(int index) const; + QList tabs(); // ? + + // inlines + TabBar *tabBar() const { return m_tabBar; } + StackedUrlBar *urlBarStack() const { return m_urlBars; } + WebView *currentWebView() const { return webView(currentIndex()); } + int webViewIndex(WebView *webView) const { return indexOf(webView); } + KAction *recentlyClosedTabsAction() const { return m_recentlyClosedTabsAction; } + void setMakeBackTab(bool b) { m_makeBackTab = b; } + + /** + * show and hide TabBar if user doesn't choose + * "Always Show TabBar" option + * + */ + void showTabBar(); + void clear(); + + +signals: + // tab widget signals + void tabsChanged(); + void lastTabClosed(); + + // current tab signals + void setCurrentTitle(const QString &url); + void showStatusBarMessage(const QString &message); + void linkHovered(const QString &link); + void loadProgress(int progress); + + void geometryChangeRequested(const QRect &geometry); + void menuBarVisibilityChangeRequested(bool visible); + void statusBarVisibilityChangeRequested(bool visible); + void toolBarVisibilityChangeRequested(bool visible); + void printRequested(QWebFrame *frame); + +public slots: + /** + * Core browser slot. This create a new tab with a WebView inside + * for browsing. + * + * @return a pointer to the new WebView + */ + WebView *newWebView(Rekonq::OpenType type = Rekonq::Default); + + /** + * Core browser slot. Load an url in a webview + * + * @param url The url to load + */ + void loadUrl(const KUrl &url); + void slotCloneTab(int index = -1); + void slotCloseTab(int index = -1); + void slotCloseOtherTabs(int index); + void slotReloadTab(int index = -1); + void slotReloadAllTabs(); + void nextTab(); + void previousTab(); + + // WEB slot actions + void slotWebReload(); + void slotWebStop(); + void slotWebBack(); + void slotWebForward(); + void slotWebUndo(); + void slotWebRedo(); + void slotWebCut(); + void slotWebCopy(); + void slotWebPaste(); + +private slots: + void slotCurrentChanged(int index); + void aboutToShowRecentTabsMenu(); + void aboutToShowRecentTriggeredAction(QAction *action); // need QAction! + + void webViewLoadStarted(); + void webViewLoadProgress(int progress); + void webViewLoadFinished(bool ok); + void webViewIconChanged(); + void webViewTitleChanged(const QString &title); + void webViewUrlChanged(const QUrl &url); + + void windowCloseRequested(); + + /** + * This functions move tab informations "from index to index" + * + * @param fromIndex the index from which we move + * + * @param toIndex the index to which we move + */ + void moveTab(int fromIndex, int toIndex); + + void postLaunch(); + +protected: + + virtual void mouseDoubleClickEvent(QMouseEvent *event); + + +private: + + /** + * This function creates (if not exists) and returns a QLabel + * with a loading QMovie. + * Imported from Arora's code. + * + * @param index the tab index where inserting the animated label + * @param addMovie creates or not a loading movie + * + * @return animated label's pointer + */ + QLabel *animatedLoading(int index, bool addMovie); + + static const int m_recentlyClosedTabsSize = 10; + KAction *m_recentlyClosedTabsAction; + + KMenu *m_recentlyClosedTabsMenu; + QList m_recentlyClosedTabs; + + StackedUrlBar *m_urlBars; + TabBar *m_tabBar; + + QString m_loadingGitPath; + + bool m_makeBackTab; +}; + +#endif diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp new file mode 100644 index 00000000..d1ab721f --- /dev/null +++ b/src/mainwindow.cpp @@ -0,0 +1,930 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2007-2008 Trolltech ASA. All rights reserved +* Copyright (C) 2008-2009 by Andrea Diamantini +* Copyright (C) 2009 by Paweł Prażak +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + +// Self Includes +#include "mainwindow.h" +#include "mainwindow.moc" + +// Auto Includes +#include "rekonq.h" + +// Local Includes +#include "application.h" +#include "settings.h" +#include "history.h" +#include "cookiejar.h" +#include "networkaccessmanager.h" +#include "bookmarks.h" +#include "webview.h" +#include "mainview.h" +#include "bookmarks.h" +#include "download.h" +#include "findbar.h" +#include "sidepanel.h" +#include "urlbar.h" +#include "stackedurlbar.h" + +// KDE Includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +// Qt Includes +#include +#include +#include + + +MainWindow::MainWindow() + : KXmlGuiWindow() + , m_view(new MainView(this)) + , m_findBar(new FindBar(this)) + , m_searchBar(new SearchBar(this)) + , m_sidePanel(0) +{ + // updating rekonq configuration + slotUpdateConfiguration(); + + // creating a centralWidget containing panel, m_view and the hidden findbar + QWidget *centralWidget = new QWidget; + centralWidget->setContentsMargins(0, 0, 0, 0); + + // setting layout + QVBoxLayout *layout = new QVBoxLayout; + layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(m_view); + layout->addWidget(m_findBar); + centralWidget->setLayout(layout); + + // central widget + setCentralWidget(centralWidget); + + // setting size policies + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + // then, setup our actions + setupActions(); + + // setting up toolbars: this has to be done BEFORE setupGUI!! + setupToolBars(); + + // Bookmark Menu + KActionMenu *bmMenu = Application::bookmarkProvider()->bookmarkActionMenu(); + actionCollection()->addAction(QLatin1String("bookmarks"), bmMenu); + + // Side Panel: this has to be done BEFORE setupGUI!! + setupSidePanel(); + + // a call to KXmlGuiWindow::setupGUI() populates the GUI + // with actions, using KXMLGUI. + // It also applies the saved mainwindow settings, if any, and ask the + // mainwindow to automatically save settings if changed: window size, + // toolbar position, icon size, etc. + setupGUI(); + + QTimer::singleShot(0, this, SLOT(postLaunch())); +} + + +MainWindow::~MainWindow() +{ + delete m_view; +} + + +void MainWindow::postLaunch() +{ + // setup history menu: this has to be done AFTER setupGUI!! + setupHistoryMenu(); + + // --------- connect signals and slots + connect(m_view, SIGNAL(setCurrentTitle(const QString &)), this, SLOT(slotUpdateWindowTitle(const QString &))); + connect(m_view, SIGNAL(loadProgress(int)), this, SLOT(slotLoadProgress(int))); + connect(m_view, SIGNAL(geometryChangeRequested(const QRect &)), this, SLOT(geometryChangeRequested(const QRect &))); + connect(m_view, SIGNAL(printRequested(QWebFrame *)), this, SLOT(printRequested(QWebFrame *))); + connect(m_view, SIGNAL(menuBarVisibilityChangeRequested(bool)), menuBar(), SLOT(setVisible(bool))); + connect(m_view, SIGNAL(statusBarVisibilityChangeRequested(bool)), statusBar(), SLOT(setVisible(bool))); + + // status bar messages + connect(m_view, SIGNAL(showStatusBarMessage(const QString&)), statusBar(), SLOT(showMessage(const QString&))); + connect(m_view, SIGNAL(linkHovered(const QString&)), statusBar(), SLOT(showMessage(const QString&))); + + // update toolbar actions signals + connect(m_view, SIGNAL(tabsChanged()), this, SLOT(slotUpdateActions())); + connect(m_view, SIGNAL(currentChanged(int)), this, SLOT(slotUpdateActions())); + + // Find Bar signal + connect(m_findBar, SIGNAL(searchString(const QString &)), this, SLOT(slotFind(const QString &))); + + // bookmarks loading + connect(Application::bookmarkProvider(), SIGNAL(openUrl(const KUrl&)), this, SLOT(loadUrl(const KUrl&))); + + // setting up toolbars to NOT have context menu enabled + setContextMenuPolicy(Qt::DefaultContextMenu); + + // accept d'n'd + setAcceptDrops(true); +} + + +QSize MainWindow::sizeHint() const +{ + QRect desktopRect = QApplication::desktop()->screenGeometry(); + QSize size = desktopRect.size() * 0.8; + return size; +} + + +void MainWindow::setupToolBars() +{ + KAction *a; + + // location bar + a = new KAction(i18n("Location Bar"), this); + a->setShortcut(KShortcut(Qt::Key_F6)); + a->setDefaultWidget(m_view->urlBarStack()); + actionCollection()->addAction(QLatin1String("url_bar"), a); + + // search bar + a = new KAction(i18n("Search Bar"), this); + a->setShortcut(KShortcut(Qt::CTRL + Qt::Key_K)); + a->setDefaultWidget(m_searchBar); + connect(m_searchBar, SIGNAL(search(const KUrl&)), this, SLOT(loadUrl(const KUrl&))); + actionCollection()->addAction(QLatin1String("search_bar"), a); + + // bookmarks bar + KAction *bookmarkBarAction = Application::bookmarkProvider()->bookmarkToolBarAction(); + a = actionCollection()->addAction(QLatin1String("bookmarks_bar"), bookmarkBarAction); +} + + +void MainWindow::setupActions() +{ + KAction *a; + + // Standard Actions + KStandardAction::open(this, SLOT(slotFileOpen()), actionCollection()); + KStandardAction::saveAs(this, SLOT(slotFileSaveAs()), actionCollection()); + KStandardAction::printPreview(this, SLOT(slotFilePrintPreview()), actionCollection()); + KStandardAction::print(this, SLOT(slotFilePrint()), actionCollection()); + KStandardAction::quit(this , SLOT(close()), actionCollection()); + KStandardAction::find(this, SLOT(slotViewFindBar()) , actionCollection()); + KStandardAction::findNext(this, SLOT(slotFindNext()) , actionCollection()); + KStandardAction::findPrev(this, SLOT(slotFindPrevious()) , actionCollection()); + KStandardAction::fullScreen(this, SLOT(slotViewFullScreen(bool)), this, actionCollection()); + KStandardAction::home(this, SLOT(slotHome()), actionCollection()); + KStandardAction::preferences(this, SLOT(slotPreferences()), actionCollection()); + KStandardAction::showMenubar(this, SLOT(slotShowMenubar(bool)), actionCollection()); + + // WEB Actions (NO KStandardActions..) + a = KStandardAction::redisplay(m_view, SLOT(slotWebReload()), actionCollection()); + a->setText(i18n("Reload")); + KStandardAction::back(m_view, SLOT(slotWebBack()), actionCollection()); + KStandardAction::forward(m_view, SLOT(slotWebForward()), actionCollection()); + KStandardAction::undo(m_view, SLOT(slotWebUndo()), actionCollection()); + KStandardAction::redo(m_view, SLOT(slotWebRedo()), actionCollection()); + KStandardAction::cut(m_view, SLOT(slotWebCut()), actionCollection()); + KStandardAction::copy(m_view, SLOT(slotWebCopy()), actionCollection()); + KStandardAction::paste(m_view, SLOT(slotWebPaste()), actionCollection()); + + a = new KAction(KIcon("process-stop"), i18n("&Stop"), this); + a->setShortcut(KShortcut(Qt::CTRL | Qt::Key_Period)); + actionCollection()->addAction(QLatin1String("stop"), a); + connect(a, SIGNAL(triggered(bool)), m_view, SLOT(slotWebStop())); + + // stop reload Action + m_stopReloadAction = new KAction(KIcon("view-refresh"), i18n("Reload"), this); + actionCollection()->addAction(QLatin1String("stop_reload") , m_stopReloadAction); + m_stopReloadAction->setShortcutConfigurable(false); + + // ============== Custom Actions + a = new KAction(KIcon("document-open-remote"), i18n("Open Location"), this); + a->setShortcut(Qt::CTRL + Qt::Key_L); + actionCollection()->addAction(QLatin1String("open_location"), a); + connect(a, SIGNAL(triggered(bool)) , this, SLOT(slotOpenLocation())); + + a = new KAction(KIcon("zoom-in"), i18n("&Enlarge Font"), this); + a->setShortcut(KShortcut(Qt::CTRL | Qt::Key_Plus)); + actionCollection()->addAction(QLatin1String("bigger_font"), a); + connect(a, SIGNAL(triggered(bool)), this, SLOT(slotViewTextBigger())); + + a = new KAction(KIcon("zoom-original"), i18n("&Normal Font"), this); + a->setShortcut(KShortcut(Qt::CTRL | Qt::Key_0)); + actionCollection()->addAction(QLatin1String("normal_font"), a); + connect(a, SIGNAL(triggered(bool)), this, SLOT(slotViewTextNormal())); + + a = new KAction(KIcon("zoom-out"), i18n("&Shrink Font"), this); + a->setShortcut(KShortcut(Qt::CTRL | Qt::Key_Minus)); + actionCollection()->addAction(QLatin1String("smaller_font"), a); + connect(a, SIGNAL(triggered(bool)), this, SLOT(slotViewTextSmaller())); + + a = new KAction(i18n("Page S&ource"), this); + actionCollection()->addAction(QLatin1String("page_source"), a); + connect(a, SIGNAL(triggered(bool)), this, SLOT(slotViewPageSource())); + + // ================ Tools (WebKit) Actions + a = new KAction(KIcon("tools-report-bug"), i18n("Web &Inspector"), this); + a->setCheckable(true); + actionCollection()->addAction(QLatin1String("web_inspector"), a); + connect(a, SIGNAL(triggered(bool)), this, SLOT(slotToggleInspector(bool))); + + a = new KAction(KIcon("view-media-artist"), i18n("Private &Browsing"), this); + a->setCheckable(true); + actionCollection()->addAction(QLatin1String("private_browsing"), a); + connect(a, SIGNAL(triggered(bool)) , this, SLOT(slotPrivateBrowsing(bool))); + + // ================ history related actions + m_historyBackAction = new KAction(KIcon("go-previous"), i18n("Back"), this); + m_historyBackMenu = new KMenu(this); + m_historyBackAction->setMenu(m_historyBackMenu); + connect(m_historyBackAction, SIGNAL(triggered(bool)), this, SLOT(slotOpenPrevious())); + connect(m_historyBackMenu, SIGNAL(aboutToShow()), this, SLOT(slotAboutToShowBackMenu())); + connect(m_historyBackMenu, SIGNAL(triggered(QAction *)), this, SLOT(slotOpenActionUrl(QAction *))); + actionCollection()->addAction(QLatin1String("history_back"), m_historyBackAction); + + m_historyForwardAction = new KAction(KIcon("go-next"), i18n("Forward"), this); + connect(m_historyForwardAction, SIGNAL(triggered(bool)), this, SLOT(slotOpenNext())); + actionCollection()->addAction(QLatin1String("history_forward"), m_historyForwardAction); + + // =================== Tab Actions + a = new KAction(KIcon("tab-new"), i18n("New &Tab"), this); + QList newTabShortcutList; + newTabShortcutList << QKeySequence(QKeySequence::New); + newTabShortcutList << QKeySequence(QKeySequence::AddTab); + a->setShortcut(KShortcut(newTabShortcutList)); + actionCollection()->addAction(QLatin1String("new_tab"), a); + connect(a, SIGNAL(triggered(bool)), m_view, SLOT(newWebView())); + + a = new KAction(KIcon("tab-close"), i18n("&Close Tab"), this); + a->setShortcut(KShortcut(Qt::CTRL + Qt::Key_W)); + actionCollection()->addAction(QLatin1String("close_tab"), a); + connect(a, SIGNAL(triggered(bool)), m_view, SLOT(slotCloseTab())); + + a = new KAction(i18n("Show Next Tab"), this); + a->setShortcuts(QApplication::isRightToLeft() ? KStandardShortcut::tabPrev() : KStandardShortcut::tabNext()); + actionCollection()->addAction(QLatin1String("show_next_tab"), a); + connect(a, SIGNAL(triggered(bool)), m_view, SLOT(nextTab())); + + a = new KAction(i18n("Show Previous Tab"), this); + a->setShortcuts(QApplication::isRightToLeft() ? KStandardShortcut::tabNext() : KStandardShortcut::tabPrev()); + actionCollection()->addAction(QLatin1String("show_prev_tab"), a); + connect(a, SIGNAL(triggered(bool)), m_view, SLOT(previousTab())); +} + + +void MainWindow::setupSidePanel() +{ + // Setup history side panel + m_sidePanel = new SidePanel(i18n("History"), this); + connect(m_sidePanel, SIGNAL(openUrl(const KUrl&)), this, SLOT(loadUrl(const KUrl&))); + connect(m_sidePanel, SIGNAL(destroyed()), Application::instance(), SLOT(slotSaveConfiguration())); + + addDockWidget(Qt::LeftDockWidgetArea, m_sidePanel); + + // setup side panel actions + KAction* a = new KAction(this); + a->setText(i18n("History")); + a->setCheckable(true); + a->setChecked(ReKonfig::showSideBar()); + a->setShortcut(KShortcut(Qt::CTRL + Qt::Key_H)); + actionCollection()->addAction(QLatin1String("show_history_panel"), a); + + // connect to toogle action + connect(a, SIGNAL(triggered(bool)), m_sidePanel->toggleViewAction(), SLOT(trigger())); +} + + +void MainWindow::setupHistoryMenu() +{ + HistoryMenu *historyMenu = new HistoryMenu(this); + connect(historyMenu, SIGNAL(openUrl(const KUrl&)), this, SLOT(loadUrl(const KUrl&))); + connect(historyMenu, SIGNAL(hovered(const QString&)), this, SLOT(slotUpdateStatusbar(const QString&))); + historyMenu->setTitle(i18n("&History")); + + // setting history menu position + menuBar()->insertMenu(actionCollection()->action("bookmarks"), historyMenu); + + // setting initial actions + QList historyActions; + historyActions.append(actionCollection()->action("history_back")); + historyActions.append(actionCollection()->action("history_forward")); + historyActions.append(m_view->recentlyClosedTabsAction()); + historyMenu->setInitialActions(historyActions); +} + + +void MainWindow::slotUpdateConfiguration() +{ + // ============== General ================== + m_homePage = ReKonfig::homePage(); + mainView()->showTabBar(); + mainView()->setMakeBackTab( ReKonfig::openTabsBack() ); + + // =========== Fonts ============== + QWebSettings *defaultSettings = QWebSettings::globalSettings(); + + int fnSize = ReKonfig::fontSize(); + + QFont standardFont = ReKonfig::standardFont(); + defaultSettings->setFontFamily(QWebSettings::StandardFont, standardFont.family()); + defaultSettings->setFontSize(QWebSettings::DefaultFontSize, fnSize); + + QFont fixedFont = ReKonfig::fixedFont(); + defaultSettings->setFontFamily(QWebSettings::FixedFont, fixedFont.family()); + defaultSettings->setFontSize(QWebSettings::DefaultFixedFontSize, fnSize); + + // ================ WebKit ============================ + defaultSettings->setAttribute(QWebSettings::AutoLoadImages, ReKonfig::autoLoadImages()); + defaultSettings->setAttribute(QWebSettings::JavascriptEnabled, ReKonfig::javascriptEnabled()); + defaultSettings->setAttribute(QWebSettings::JavaEnabled, ReKonfig::javaEnabled()); + defaultSettings->setAttribute(QWebSettings::PluginsEnabled, ReKonfig::pluginsEnabled()); + defaultSettings->setAttribute(QWebSettings::JavascriptCanOpenWindows, ReKonfig::javascriptCanOpenWindows()); + defaultSettings->setAttribute(QWebSettings::JavascriptCanAccessClipboard, ReKonfig::javascriptCanAccessClipboard()); + defaultSettings->setAttribute(QWebSettings::LinksIncludedInFocusChain, ReKonfig::linksIncludedInFocusChain()); + defaultSettings->setAttribute(QWebSettings::ZoomTextOnly, ReKonfig::zoomTextOnly()); + defaultSettings->setAttribute(QWebSettings::PrintElementBackgrounds, ReKonfig::printElementBackgrounds()); + defaultSettings->setAttribute(QWebSettings::OfflineStorageDatabaseEnabled, ReKonfig::offlineStorageDatabaseEnabled()); + defaultSettings->setAttribute(QWebSettings::OfflineWebApplicationCacheEnabled, ReKonfig::offlineWebApplicationCacheEnabled()); + defaultSettings->setAttribute(QWebSettings::LocalStorageDatabaseEnabled, ReKonfig::localStorageDatabaseEnabled()); + + // ====== load Settings on main classes + Application::networkAccessManager()->loadSettings(); + Application::cookieJar()->loadSettings(); + Application::historyManager()->loadSettings(); +} + + +void MainWindow::slotUpdateBrowser() +{ + slotUpdateConfiguration(); + mainView()->slotReloadAllTabs(); +} + + +KUrl MainWindow::guessUrlFromString(const QString &string) +{ + QString urlStr = string.trimmed(); + QRegExp test(QLatin1String("^[a-zA-Z]+\\:.*")); + + // Check if it looks like a qualified URL. Try parsing it and see. + bool hasSchema = test.exactMatch(urlStr); + + if (hasSchema) + { + QUrl qurl(urlStr, QUrl::TolerantMode); + KUrl url(qurl); + + if (url.isValid()) + { + return url; + } + } + + // Might be a file. + if (QFile::exists(urlStr)) + { + QFileInfo info(urlStr); + return KUrl::fromPath(info.absoluteFilePath()); + } + + // Might be a shorturl - try to detect the schema. + if (!hasSchema) + { + int dotIndex = urlStr.indexOf(QLatin1Char('.')); + + if (dotIndex != -1) + { + QString prefix = urlStr.left(dotIndex).toLower(); + QString schema = (prefix == QLatin1String("ftp")) ? prefix : QLatin1String("http"); + QUrl qurl(schema + QLatin1String("://") + urlStr, QUrl::TolerantMode); + KUrl url(qurl); + + if (url.isValid()) + { + return url; + } + } + } + + // Fall back to QUrl's own tolerant parser. + QUrl qurl = QUrl(string, QUrl::TolerantMode); + KUrl url(qurl); + + // finally for cases where the user just types in a hostname add http + if (qurl.scheme().isEmpty()) + { + qurl = QUrl(QLatin1String("http://") + string, QUrl::TolerantMode); + url = KUrl(qurl); + } + return url; +} + + +void MainWindow::loadUrl(const KUrl &url) +{ + m_view->loadUrl(url); +} + + +void MainWindow::slotOpenLocation() +{ + m_view->currentUrlBar()->selectAll(); + m_view->currentUrlBar()->setFocus(); +} + + +void MainWindow::slotFileSaveAs() +{ + KUrl srcUrl = currentTab()->url(); + Application::downloadManager()->newDownload(srcUrl); +} + + +void MainWindow::slotPreferences() +{ + // an instance the dialog could be already created and could be cached, + // in which case you want to display the cached dialog + if (SettingsDialog::showDialog("rekonfig")) + return; + + // we didn't find an instance of this dialog, so lets create it + SettingsDialog *s = new SettingsDialog(this); + + // keep us informed when the user changes settings + connect(s, SIGNAL(settingsChanged(const QString&)), this, SLOT(slotUpdateBrowser())); + + s->exec(); +} + + +void MainWindow::slotUpdateStatusbar(const QString &string) +{ + statusBar()->showMessage(string, 2000); +} + + +void MainWindow::slotUpdateActions() +{ + m_historyBackAction->setEnabled(currentTab()->history()->canGoBack()); + m_historyForwardAction->setEnabled(currentTab()->history()->canGoForward()); +} + + +void MainWindow::slotUpdateWindowTitle(const QString &title) +{ + if (title.isEmpty()) + { + setWindowTitle("rekonq"); + } + else + { + setWindowTitle(title + " - rekonq"); + } +} + + +void MainWindow::slotFileOpen() +{ + QString filePath = KFileDialog::getOpenFileName(KUrl(), + i18n("Web Resources (*.html *.htm *.svg *.png *.gif *.svgz); All files (*.*)"), + this, + i18n("Open Web Resource") + ); + + if (filePath.isEmpty()) + return; + + loadUrl(guessUrlFromString(filePath)); +} + + +void MainWindow::slotFilePrintPreview() +{ + if (!currentTab()) + return; + + QPrinter printer; + KPrintPreview previewdlg(&printer, this); + currentTab()->print(&printer); + previewdlg.exec(); +} + + +void MainWindow::slotFilePrint() +{ + if (!currentTab()) + return; + printRequested(currentTab()->page()->mainFrame()); +} + + +void MainWindow::printRequested(QWebFrame *frame) +{ + QPrinter printer; + + QPrintDialog *dialog = KdePrint::createPrintDialog(&printer, this); + if (dialog->exec() != QDialog::Accepted) + return; + frame->print(&printer); +} + + +void MainWindow::slotPrivateBrowsing(bool enable) +{ + QWebSettings *settings = QWebSettings::globalSettings(); + if (enable) + { + QString title = i18n("Are you sure you want to turn on private browsing?"); + QString text = "" + title + i18n("

When private browsing in turned on," + " webpages are not added to the history," + " new cookies are not stored, current cookies cannot be accessed," \ + " site icons will not be stored, session will not be saved, " \ + " and searches are not addded to the pop-up menu in the Google search box." \ + " Until you close the window, you can still click the Back and Forward buttons" \ + " to return to the webpages you have opened."); + + int button = KMessageBox::questionYesNo(this, text, title); + if (button == KMessageBox::Ok) + { + settings->setAttribute(QWebSettings::PrivateBrowsingEnabled, true); + } + else + { + actionCollection()->action("private_browsing")->setChecked(false); + } + } + else + { + settings->setAttribute(QWebSettings::PrivateBrowsingEnabled, false); + + MainWindow* win = Application::instance()->mainWindow(); + win->m_lastSearch = QString::null; + win->mainView()->clear(); + } +} + +void MainWindow::slotFind(const QString & search) +{ + if (!currentTab()) + return; + m_lastSearch = search; + slotFindNext(); +} + + +void MainWindow::slotViewFindBar() +{ + m_findBar->showFindBar(); +} + + +void MainWindow::slotFindNext() +{ + if (!currentTab() && m_lastSearch.isEmpty()) + return; + + QWebPage::FindFlags options; + if (m_findBar->matchCase()) + { + options = QWebPage::FindCaseSensitively | QWebPage::FindWrapsAroundDocument; + } + else + { + options = QWebPage::FindWrapsAroundDocument; + } + + if (!currentTab()->findText(m_lastSearch, options)) + { + slotUpdateStatusbar(QString(m_lastSearch) + i18n(" not found.")); + } +} + + +void MainWindow::slotFindPrevious() +{ + if (!currentTab() && m_lastSearch.isEmpty()) + return; + + QWebPage::FindFlags options; + if (m_findBar->matchCase()) + { + options = QWebPage::FindCaseSensitively | QWebPage::FindBackward | QWebPage::FindWrapsAroundDocument; + } + else + { + options = QWebPage::FindBackward | QWebPage::FindWrapsAroundDocument; + } + + if (!currentTab()->findText(m_lastSearch, options)) + { + slotUpdateStatusbar(QString(m_lastSearch) + i18n(" not found.")); + } +} + + +void MainWindow::slotViewTextBigger() +{ + if (!currentTab()) + return; + currentTab()->setTextSizeMultiplier(currentTab()->textSizeMultiplier() + 0.1); +} + + +void MainWindow::slotViewTextNormal() +{ + if (!currentTab()) + return; + currentTab()->setTextSizeMultiplier(1.0); +} + + +void MainWindow::slotViewTextSmaller() +{ + if (!currentTab()) + return; + currentTab()->setTextSizeMultiplier(currentTab()->textSizeMultiplier() - 0.1); +} + + +void MainWindow::slotViewFullScreen(bool makeFullScreen) +{ + // state flags + static bool menubarFlag; + static bool mainToolBarFlag; + static bool bookmarksToolBarFlag; + static bool statusBarFlag; + static bool sidePanelFlag; + + if (makeFullScreen == true) + { + // save current state + menubarFlag = menuBar()->isHidden(); + mainToolBarFlag = toolBar("mainToolBar")->isHidden(); + bookmarksToolBarFlag = toolBar("bookmarksToolBar")->isHidden(); + statusBarFlag = statusBar()->isHidden(); + sidePanelFlag = sidePanel()->isHidden(); + + menuBar()->hide(); + toolBar("mainToolBar")->hide(); + toolBar("bookmarksToolBar")->hide(); + statusBar()->hide(); + sidePanel()->hide(); + } + else + { + if (!menubarFlag) + menuBar()->show(); + if (!mainToolBarFlag) + toolBar("mainToolBar")->show(); + if (!bookmarksToolBarFlag) + toolBar("bookmarksToolBar")->show(); + if (!statusBarFlag) + statusBar()->show(); + if (!sidePanelFlag) + sidePanel()->show(); + } + + KToggleFullScreenAction::setFullScreen(this, makeFullScreen); +} + + +void MainWindow::slotViewPageSource() +{ + if (!currentTab()) + return; + + KUrl url(currentTab()->url()); + bool isTempFile = false; + if (!url.isLocalFile()) + { + KTemporaryFile sourceFile; + + /// TODO: autochoose tempfile suffix + sourceFile.setSuffix(QString(".html")); + sourceFile.setAutoRemove(false); + + if (sourceFile.open()) + { + QDataStream stream(&sourceFile); + stream << currentTab()->page()->mainFrame()->toHtml().toUtf8(); + + url = KUrl(); + url.setPath(sourceFile.fileName()); + isTempFile = true; + } + } + KRun::runUrl(url, QLatin1String("text/plain"), this, isTempFile); +} + + +void MainWindow::slotHome() +{ + loadUrl(KUrl(m_homePage)); +} + + +void MainWindow::slotToggleInspector(bool enable) +{ + QWebSettings::globalSettings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, enable); + if (enable) + { + int result = KMessageBox::questionYesNo(this, + i18n("The web inspector will only work correctly for pages that were loaded after enabling.\n" + "Do you want to reload all pages?"), + i18n("Web Inspector") + ); + + if (result == KMessageBox::Yes) + { + m_view->slotReloadAllTabs(); + } + } +} + + +MainView *MainWindow::mainView() const +{ + return m_view; +} + + + +WebView *MainWindow::currentTab() const +{ + return m_view->currentWebView(); +} + + +void MainWindow::slotLoadProgress(int progress) +{ + QAction *stop = actionCollection()->action("stop"); + QAction *reload = actionCollection()->action("view_redisplay"); + if (progress < 100 && progress > 0) + { + disconnect(m_stopReloadAction, SIGNAL(triggered(bool)), reload , SIGNAL(triggered(bool))); + m_stopReloadAction->setIcon(KIcon("process-stop")); + m_stopReloadAction->setToolTip(i18n("Stop loading the current page")); + m_stopReloadAction->setText(i18n("Stop")); + connect(m_stopReloadAction, SIGNAL(triggered(bool)), stop, SIGNAL(triggered(bool))); + } + else + { + disconnect(m_stopReloadAction, SIGNAL(triggered(bool)), stop , SIGNAL(triggered(bool))); + m_stopReloadAction->setIcon(KIcon("view-refresh")); + m_stopReloadAction->setToolTip(i18n("Reload the current page")); + m_stopReloadAction->setText(i18n("Reload")); + connect(m_stopReloadAction, SIGNAL(triggered(bool)), reload, SIGNAL(triggered(bool))); + + } +} + + +void MainWindow::slotAboutToShowBackMenu() +{ + m_historyBackMenu->clear(); + if (!currentTab()) + return; + QWebHistory *history = currentTab()->history(); + int historyCount = history->count(); + for (int i = history->backItems(historyCount).count() - 1; i >= 0; --i) + { + QWebHistoryItem item = history->backItems(history->count()).at(i); + KAction *action = new KAction(this); + action->setData(-1*(historyCount - i - 1)); + QIcon icon = Application::instance()->icon(item.url()); + action->setIcon(icon); + action->setText(item.title()); + m_historyBackMenu->addAction(action); + } +} + + +void MainWindow::slotOpenActionUrl(QAction *action) +{ + int offset = action->data().toInt(); + QWebHistory *history = currentTab()->history(); + if (offset < 0) + { + history->goToItem(history->backItems(-1*offset).first()); // back + } + else + { + if (offset > 0) + { + history->goToItem(history->forwardItems(history->count() - offset + 1).back()); // forward + } + } +} + + +void MainWindow::slotOpenPrevious() +{ + QWebHistory *history = currentTab()->history(); + if (history->canGoBack()) + history->goToItem(history->backItem()); +} + + +void MainWindow::slotOpenNext() +{ + QWebHistory *history = currentTab()->history(); + if (history->canGoForward()) + history->goToItem(history->forwardItem()); +} + + +void MainWindow::geometryChangeRequested(const QRect &geometry) +{ + setGeometry(geometry); +} + + +void MainWindow::slotShowMenubar(bool enable) +{ + if (enable) + menuBar()->show(); + else + menuBar()->hide(); +} + + +bool MainWindow::queryClose() +{ + if (m_view->count() > 1) + { + + int answer = KMessageBox::questionYesNoCancel( + this, + i18np("Are you sure you want to close the window?\n" "You have 1 tab open","Are you sure you want to close the window?\n" "You have %1 tabs open" , m_view->count()), + i18n("Are you sure you want to close the window?"), + KStandardGuiItem::quit(), + KGuiItem(i18n("C&lose Current Tab"), KIcon("tab-close")), + KStandardGuiItem::cancel(), + "confirmClosingMultipleTabs" + ); + + switch (answer) + { + case KMessageBox::Yes: + // Quit + return true; + break; + case KMessageBox::No: + // Close only the current tab + m_view->slotCloseTab(); + default: + return false; + } + } + + return true; +} + + +QAction *MainWindow::actionByName(const QString name) +{ + QAction *ret = actionCollection()->action(name); + + if (ret) + return ret; + + /* else */ + kWarning() << "Action named: " << name << " not found, returning empty action."; + + return new QAction(this); // return empty object instead of NULL pointer +} + diff --git a/src/mainwindow.h b/src/mainwindow.h new file mode 100644 index 00000000..cdb7151d --- /dev/null +++ b/src/mainwindow.h @@ -0,0 +1,153 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2007-2008 Trolltech ASA. All rights reserved +* Copyright (C) 2008-2009 by Andrea Diamantini +* Copyright (C) 2009 by Paweł Prażak +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +// Local Includes +#include "searchbar.h" +#include "bookmarks.h" +#include "mainview.h" +#include "webview.h" + +// KDE Includes +#include +#include + +// Forward Declarations +class QWebFrame; + +class KUrl; +class KAction; +class KActionMenu; +class KIcon; +class KMenu; + +class FindBar; +class HistoryMenu; +class SidePanel; +class WebView; + + +/** + * This class serves as the main window for rekonq. + * It handles the menus, toolbars, and status bars. + * + */ +class MainWindow : public KXmlGuiWindow +{ + Q_OBJECT + +public: + MainWindow(); + ~MainWindow(); + + static KUrl guessUrlFromString(const QString &url); + MainView *mainView() const; + WebView *currentTab() const; + QAction *actionByName(const QString name); + virtual QSize sizeHint() const; + +private: + void setupActions(); + void setupHistoryMenu(); + void setupToolBars(); + void setupSidePanel(); + SidePanel *sidePanel() + { + return m_sidePanel; + } + +public slots: + void slotHome(); + void loadUrl(const KUrl &url); + void slotUpdateBrowser(); + +protected: + bool queryClose(); + +private slots: + void postLaunch(); + void slotUpdateConfiguration(); + void slotLoadProgress(int); + void slotUpdateStatusbar(const QString &string); + void slotUpdateActions(); + void slotUpdateWindowTitle(const QString &title = QString()); + void slotOpenLocation(); + void slotAboutToShowBackMenu(); + void geometryChangeRequested(const QRect &geometry); + + // history related + void slotOpenActionUrl(QAction *action); + void slotOpenPrevious(); + void slotOpenNext(); + + // File Menu slots + void slotFileOpen(); + void slotFilePrintPreview(); + void slotFilePrint(); + void slotPrivateBrowsing(bool); + void slotFileSaveAs(); + void printRequested(QWebFrame *frame); + + // Edit Menu slots + void slotFind(const QString &); + void slotFindNext(); + void slotFindPrevious(); + + // View Menu slots + void slotViewTextBigger(); + void slotViewTextNormal(); + void slotViewTextSmaller(); + void slotViewPageSource(); + void slotViewFullScreen(bool enable); + void slotViewFindBar(); + + // Tools Menu slots + void slotToggleInspector(bool enable); + + // Settings Menu slots + void slotShowMenubar(bool enable); + void slotPreferences(); + +private: + MainView *m_view; + FindBar *m_findBar; + SearchBar *m_searchBar; + SidePanel *m_sidePanel; + + KMenu *m_historyBackMenu; + KMenu *m_windowMenu; + KActionMenu *m_historyActionMenu; + + KAction *m_stopReloadAction; + KAction *m_stopAction; + KAction *m_reloadAction; + KAction *m_historyBackAction; + KAction *m_historyForwardAction; + + QString m_lastSearch; + QString m_homePage; +}; + +#endif // MAINWINDOW_H diff --git a/src/modelmenu.cpp b/src/modelmenu.cpp new file mode 100644 index 00000000..cc43a3f3 --- /dev/null +++ b/src/modelmenu.cpp @@ -0,0 +1,228 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2007-2008 Trolltech ASA. All rights reserved +* Copyright (C) 2008-2009 by Andrea Diamantini +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + +#include "modelmenu.h" + + +ModelMenu::ModelMenu(QWidget * parent) + : KMenu(parent) + , m_maxRows(7) + , m_firstSeparator(-1) + , m_maxWidth(-1) + , m_hoverRole(0) + , m_separatorRole(0) + , m_model(0) +{ + connect(this, SIGNAL(aboutToShow()), this, SLOT(aboutToShow())); +} + + +bool ModelMenu::prePopulated() +{ + return false; +} + + +void ModelMenu::postPopulated() +{ +} + + +void ModelMenu::setModel(QAbstractItemModel *model) +{ + m_model = model; +} + + +QAbstractItemModel *ModelMenu::model() const +{ + return m_model; +} + + +void ModelMenu::setMaxRows(int max) +{ + m_maxRows = max; +} + + +int ModelMenu::maxRows() const +{ + return m_maxRows; +} + + +void ModelMenu::setFirstSeparator(int offset) +{ + m_firstSeparator = offset; +} + + +int ModelMenu::firstSeparator() const +{ + return m_firstSeparator; +} + + +void ModelMenu::setRootIndex(const QModelIndex &index) +{ + m_root = index; +} + + +QModelIndex ModelMenu::rootIndex() const +{ + return m_root; +} + + +void ModelMenu::setHoverRole(int role) +{ + m_hoverRole = role; +} + + +int ModelMenu::hoverRole() const +{ + return m_hoverRole; +} + + +void ModelMenu::setSeparatorRole(int role) +{ + m_separatorRole = role; +} + + +int ModelMenu::separatorRole() const +{ + return m_separatorRole; +} + + +Q_DECLARE_METATYPE(QModelIndex) +void ModelMenu::aboutToShow() +{ + if (QMenu *menu = qobject_cast(sender())) + { + QVariant v = menu->menuAction()->data(); + if (v.canConvert()) + { + QModelIndex idx = qvariant_cast(v); + createMenu(idx, -1, menu, menu); + disconnect(menu, SIGNAL(aboutToShow()), this, SLOT(aboutToShow())); + return; + } + } + + clear(); + if (prePopulated()) + addSeparator(); + int max = m_maxRows; + if (max != -1) + max += m_firstSeparator; + createMenu(m_root, max, this, this); + postPopulated(); +} + +void ModelMenu::createMenu(const QModelIndex &parent, int max, QMenu *parentMenu, QMenu *menu) +{ + if (!menu) + { + QString title = parent.data().toString(); + menu = new QMenu(title, this); + QIcon icon = qvariant_cast(parent.data(Qt::DecorationRole)); + menu->setIcon(icon); + parentMenu->addMenu(menu); + QVariant v; + v.setValue(parent); + menu->menuAction()->setData(v); + connect(menu, SIGNAL(aboutToShow()), this, SLOT(aboutToShow())); + return; + } + + int end = m_model->rowCount(parent); + if (max != -1) + end = qMin(max, end); + + connect(menu, SIGNAL(triggered(QAction*)), this, SLOT(triggered(QAction*))); + connect(menu, SIGNAL(hovered(QAction*)), this, SLOT(hovered(QAction*))); + + for (int i = 0; i < end; ++i) + { + QModelIndex idx = m_model->index(i, 0, parent); + if (m_model->hasChildren(idx)) + { + createMenu(idx, -1, menu); + } + else + { + if (m_separatorRole != 0 + && idx.data(m_separatorRole).toBool()) + addSeparator(); + else + menu->addAction(makeAction(idx)); + } + if (menu == this && i == m_firstSeparator - 1) + addSeparator(); + } +} + +KAction *ModelMenu::makeAction(const QModelIndex &index) +{ + QIcon icon = qvariant_cast(index.data(Qt::DecorationRole)); + KAction *action = (KAction *) makeAction(KIcon(icon), index.data().toString(), this); + QVariant v; + v.setValue(index); + action->setData(v); + return action; +} + +KAction *ModelMenu::makeAction(const KIcon &icon, const QString &text, QObject *parent) +{ + QFontMetrics fm(font()); + if (-1 == m_maxWidth) + m_maxWidth = fm.width(QLatin1Char('m')) * 30; + QString smallText = fm.elidedText(text, Qt::ElideMiddle, m_maxWidth); + return new KAction(icon, smallText, parent); +} + +void ModelMenu::triggered(QAction *action) +{ + QVariant v = action->data(); + if (v.canConvert()) + { + QModelIndex idx = qvariant_cast(v); + emit activated(idx); + } +} + +void ModelMenu::hovered(QAction *action) +{ + QVariant v = action->data(); + if (v.canConvert()) + { + QModelIndex idx = qvariant_cast(v); + QString hoveredString = idx.data(m_hoverRole).toString(); + if (!hoveredString.isEmpty()) + emit hovered(hoveredString); + } +} diff --git a/src/modelmenu.h b/src/modelmenu.h new file mode 100644 index 00000000..3cc657b3 --- /dev/null +++ b/src/modelmenu.h @@ -0,0 +1,92 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2007-2008 Trolltech ASA. All rights reserved +* Copyright (C) 2008-2009 by Andrea Diamantini +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + + +#ifndef MODELMENU_H +#define MODELMENU_H + +// Qt Includes +#include +#include + +// KDE Includes +#include +#include +#include + +// A QMenu that is dynamically populated from a QAbstractItemModel +class ModelMenu : public KMenu +{ + Q_OBJECT + +signals: + void activated(const QModelIndex &index); + void hovered(const QString &text); + +public: + ModelMenu(QWidget *parent = 0); + + void setModel(QAbstractItemModel *model); + QAbstractItemModel *model() const; + + void setMaxRows(int max); + int maxRows() const; + + void setFirstSeparator(int offset); + int firstSeparator() const; + + void setRootIndex(const QModelIndex &index); + QModelIndex rootIndex() const; + + void setHoverRole(int role); + int hoverRole() const; + + void setSeparatorRole(int role); + int separatorRole() const; + + KAction *makeAction(const KIcon &icon, const QString &text, QObject *parent); + +protected: + // add any actions before the tree, return true if any actions are added. + virtual bool prePopulated(); + // add any actions after the tree + virtual void postPopulated(); + // put all of the children of parent into menu up to max + void createMenu(const QModelIndex &parent, int max, QMenu *parentMenu = 0, QMenu *menu = 0); + +private slots: + void aboutToShow(); + void triggered(QAction *action); + void hovered(QAction *action); + +private: + KAction *makeAction(const QModelIndex &index); + int m_maxRows; + int m_firstSeparator; + int m_maxWidth; + int m_hoverRole; + int m_separatorRole; + QAbstractItemModel *m_model; + QPersistentModelIndex m_root; +}; + +#endif // MODELMENU_H + diff --git a/src/networkaccessmanager.cpp b/src/networkaccessmanager.cpp new file mode 100644 index 00000000..d92edbd6 --- /dev/null +++ b/src/networkaccessmanager.cpp @@ -0,0 +1,165 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2007-2008 Trolltech ASA. All rights reserved +* Copyright (C) 2008-2009 by Andrea Diamantini +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + +// Self Includes +#include "networkaccessmanager.h" +#include "networkaccessmanager.moc" + +// Local Includes +#include "application.h" +#include "mainwindow.h" + +// Auto Includes +#include "rekonq.h" + +// Ui Includes +#include "ui_password.h" +#include "ui_proxy.h" + +// KDE Includes +#include +#include + +// Qt Includes +#include +#include +#include +#include +#include +#include +#include + + +NetworkAccessManager::NetworkAccessManager(QObject *parent) + : QNetworkAccessManager(parent) +{ + connect(this, SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)), + SLOT(authenticationRequired(QNetworkReply*, QAuthenticator*))); + connect(this, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*)), + SLOT(proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*))); + +#ifndef QT_NO_OPENSSL + connect(this, SIGNAL(sslErrors(QNetworkReply*, const QList&)), + SLOT(sslErrors(QNetworkReply*, const QList&))); +#endif + + loadSettings(); + + QNetworkDiskCache *diskCache = new QNetworkDiskCache(this); + QString location = KStandardDirs::locateLocal("cache", "", true); + diskCache->setCacheDirectory(location); + setCache(diskCache); +} + + +void NetworkAccessManager::loadSettings() +{ + QNetworkProxy proxy; + if (ReKonfig::isProxyEnabled()) + { + if (ReKonfig::proxyType() == 0) + { + proxy.setType(QNetworkProxy::Socks5Proxy); + } + else + { + proxy.setType(QNetworkProxy::HttpProxy); + } + proxy.setHostName(ReKonfig::proxyHostName()); + proxy.setPort(ReKonfig::proxyPort()); + proxy.setUser(ReKonfig::proxyUserName()); + proxy.setPassword(ReKonfig::proxyPassword()); + } + setProxy(proxy); +} + + + +void NetworkAccessManager::authenticationRequired(QNetworkReply *reply, QAuthenticator *auth) +{ + MainWindow *mainWindow = Application::instance()->mainWindow(); + + KDialog dialog(mainWindow, Qt::Sheet); + dialog.setButtons(KDialog::Ok | KDialog::Cancel); + + Ui::passwordWidget passwordWidget; + QWidget widget; + passwordWidget.setupUi(&widget); + + dialog.setMainWidget(&widget); + + passwordWidget.iconLabel->setText(QString()); + passwordWidget.iconLabel->setPixmap(mainWindow->style()->standardIcon(QStyle::SP_MessageBoxQuestion, 0, mainWindow).pixmap(32, 32)); + + QString introMessage = i18n("Enter username and password for ") + + Qt::escape(reply->url().toString()) + i18n(" at ") + Qt::escape(reply->url().toString()) + ""; + passwordWidget.introLabel->setText(introMessage); + passwordWidget.introLabel->setWordWrap(true); + + if (dialog.exec() == QDialog::Accepted) + { + auth->setUser(passwordWidget.userNameLineEdit->text()); + auth->setPassword(passwordWidget.passwordLineEdit->text()); + } +} + +void NetworkAccessManager::proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth) +{ + MainWindow *mainWindow = Application::instance()->mainWindow(); + + KDialog dialog(mainWindow, Qt::Sheet); + dialog.setButtons(KDialog::Ok | KDialog::Cancel); + + Ui::proxyWidget proxyWdg; + QWidget widget; + proxyWdg.setupUi(&widget); + + dialog.setMainWidget(&widget); + + proxyWdg.iconLabel->setText(QString()); + proxyWdg.iconLabel->setPixmap(mainWindow->style()->standardIcon(QStyle::SP_MessageBoxQuestion, 0, mainWindow).pixmap(32, 32)); + + QString introMessage = i18n("Connect to proxy ") + Qt::escape(proxy.hostName()) + i18n(" using:"); + proxyWdg.introLabel->setText(introMessage); + proxyWdg.introLabel->setWordWrap(true); + + if (dialog.exec() == QDialog::Accepted) + { + auth->setUser(proxyWdg.userNameLineEdit->text()); + auth->setPassword(proxyWdg.passwordLineEdit->text()); + } +} + +#ifndef QT_NO_OPENSSL +void NetworkAccessManager::sslErrors(QNetworkReply *reply, const QList &error) +{ + MainWindow *mainWindow = Application::instance()->mainWindow(); + + QStringList errorStrings; + for (int i = 0; i < error.count(); ++i) + errorStrings += error.at(i).errorString(); + QString errors = errorStrings.join(QLatin1String("\n")); + int ret = KMessageBox::warningYesNo(mainWindow, i18n("SSL Errors:\n\n") + reply->url().toString() + "\n\n" + QString(errors) + "\n\n"); + if (ret == KMessageBox::Yes) + reply->ignoreSslErrors(); +} +#endif + diff --git a/src/networkaccessmanager.h b/src/networkaccessmanager.h new file mode 100644 index 00000000..ebcd952d --- /dev/null +++ b/src/networkaccessmanager.h @@ -0,0 +1,47 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2007-2008 Trolltech ASA. All rights reserved +* Copyright (C) 2008-2009 by Andrea Diamantini +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + +#ifndef NETWORKACCESSMANAGER_H +#define NETWORKACCESSMANAGER_H + + +#include + + +class NetworkAccessManager : public QNetworkAccessManager +{ + Q_OBJECT + +public: + NetworkAccessManager(QObject *parent = 0); + +public slots: + void loadSettings(); + +private slots: + void authenticationRequired(QNetworkReply *reply, QAuthenticator *auth); + void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth); +#ifndef QT_NO_OPENSSL + void sslErrors(QNetworkReply *reply, const QList &error); +#endif +}; + +#endif // NETWORKACCESSMANAGER_H diff --git a/src/panelhistory.cpp b/src/panelhistory.cpp new file mode 100644 index 00000000..72507663 --- /dev/null +++ b/src/panelhistory.cpp @@ -0,0 +1,95 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2009 by Domrachev Alexandr +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + +// Self Includes +#include "panelhistory.h" +#include "panelhistory.moc" + +// QT Includes +#include + +// KDE Includes +#include +#include +#include + +// Local Includes +#include "history.h" + + +PanelHistory::PanelHistory(QWidget *parent) + : QWidget(parent) + , m_historyTreeView(new QTreeView) + , m_treeProxyModel(new TreeProxyModel(this)) +{ + m_historyTreeView->setUniformRowHeights(true); + m_historyTreeView->setSelectionBehavior(QAbstractItemView::SelectRows); + m_historyTreeView->setTextElideMode(Qt::ElideMiddle); + m_historyTreeView->setAlternatingRowColors(true); + + // add search bar + QHBoxLayout *hBoxLayout = new QHBoxLayout; + hBoxLayout->setContentsMargins(5, 0, 0, 0); + QLabel *searchLabel = new QLabel(i18n("Search: ")); + hBoxLayout->addWidget(searchLabel); + KLineEdit *search = new KLineEdit; + hBoxLayout->addWidget(search); + QWidget *searchBar = new QWidget; + searchBar->setLayout(hBoxLayout); + + // setup view + QVBoxLayout *vBoxLayout = new QVBoxLayout; + vBoxLayout->setContentsMargins(0, 0, 0, 0); + vBoxLayout->addWidget(searchBar); + vBoxLayout->addWidget(m_historyTreeView); + setLayout(vBoxLayout); + + //- + HistoryManager *historyManager = Application::historyManager(); + QAbstractItemModel *model = historyManager->historyTreeModel(); + + m_treeProxyModel->setSourceModel(model); + m_historyTreeView->setModel(m_treeProxyModel); + m_historyTreeView->setExpanded(m_treeProxyModel->index(0, 0), true); + m_historyTreeView->header()->hideSection(1); + QFontMetrics fm(font()); + int header = fm.width(QLatin1Char('m')) * 40; + m_historyTreeView->header()->resizeSection(0, header); + + connect(search, SIGNAL(textChanged(QString)), m_treeProxyModel, SLOT(setFilterFixedString(QString))); + connect(m_historyTreeView, SIGNAL(activated(const QModelIndex&)), this, SLOT(open())); +} + + +PanelHistory::~PanelHistory() +{ + delete m_treeProxyModel; + delete m_historyTreeView; +} + + +void PanelHistory::open() +{ + QModelIndex index = m_historyTreeView->currentIndex(); + if (!index.parent().isValid()) + return; + emit openUrl(index.data(HistoryModel::UrlRole).toUrl()); +} + diff --git a/src/panelhistory.h b/src/panelhistory.h new file mode 100644 index 00000000..89759a84 --- /dev/null +++ b/src/panelhistory.h @@ -0,0 +1,55 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2009 by Domrachev Alexandr +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + +#ifndef PANELHISTORY_H +#define PANELHISTORY_H + +// Qt Includes +#include + +// Local Includes +#include "application.h" + +class QTreeView; +class KUrl; +class TreeProxyModel; + + +class PanelHistory : public QWidget +{ + Q_OBJECT + Q_DISABLE_COPY(PanelHistory) + +public: + explicit PanelHistory(QWidget *parent = 0); + virtual ~PanelHistory(); + +signals: + void openUrl(const KUrl &); + +private slots: + void open(); + +private: + QTreeView *m_historyTreeView; + TreeProxyModel *m_treeProxyModel; + +}; + +#endif // PANELHISTORY_H diff --git a/src/password.ui b/src/password.ui new file mode 100644 index 00000000..028e1683 --- /dev/null +++ b/src/password.ui @@ -0,0 +1,69 @@ + + + passwordWidget + + + + 0 + 0 + 343 + 193 + + + + Form + + + + + + + + DUMMY ICON + + + + + + + + 0 + 0 + + + + INTRO TEXT DUMMY + + + + + + + + + Username: + + + + + + + + + + Password: + + + + + + + QLineEdit::Password + + + + + + + + diff --git a/src/proxy.ui b/src/proxy.ui new file mode 100644 index 00000000..e7440862 --- /dev/null +++ b/src/proxy.ui @@ -0,0 +1,62 @@ + + + proxyWidget + + + + 0 + 0 + 264 + 153 + + + + Form + + + + + + ICON + + + + + + + Connect to proxy + + + true + + + + + + + Username: + + + + + + + + + + Password: + + + + + + + QLineEdit::Password + + + + + + + + diff --git a/src/rekonq.kcfg b/src/rekonq.kcfg new file mode 100644 index 00000000..499258d2 --- /dev/null +++ b/src/rekonq.kcfg @@ -0,0 +1,119 @@ + + + + + +QtWebKit +KUrl + + + + + http://www.kde.org/ + + + $HOME + + + false + + + true + + + false + + + false + + + + + + + QFont(QWebSettings::globalSettings()->fontFamily(QWebSettings::StandardFont)) + + + QFont(QWebSettings::globalSettings()->fontFamily(QWebSettings::FixedFont)) + + + 12 + + + + + + + 1 + + + 2 + + + 0 + + + + + + + false + + + 0 + + + + + 8080 + + + + + + + + + + + true + + + true + + + true + + + true + + + true + + + false + + + true + + + false + + + true + + + true + + + true + + + true + + + + diff --git a/src/rekonq.kcfgc b/src/rekonq.kcfgc new file mode 100644 index 00000000..50a9817d --- /dev/null +++ b/src/rekonq.kcfgc @@ -0,0 +1,5 @@ +File=rekonq.kcfg +ClassName=ReKonfig +Singleton=true +Mutators=true +UseEnumTypes=true diff --git a/src/rekonqui.rc b/src/rekonqui.rc new file mode 100644 index 00000000..aa372538 --- /dev/null +++ b/src/rekonqui.rc @@ -0,0 +1,104 @@ + + + + + + + +&File + + + + + + + + + + + + + + + +&Edit + + + + + + + + + + + + + +&View + + + + + + + + + + + + + + + +Hi&story + + + +&Bookmarks + + + +&Tools + + + + + +&Settings + + + + + Side &Panels + + + + + + + + + + + + + + +Main Toolbar + + + + + + + + + + + + + diff --git a/src/searchbar.cpp b/src/searchbar.cpp new file mode 100644 index 00000000..fad35748 --- /dev/null +++ b/src/searchbar.cpp @@ -0,0 +1,129 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2008-2009 by Andrea Diamantini +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + +// Self Includes +#include "searchbar.h" +#include "searchbar.moc" + +// Local Includes +#include "application.h" +#include "mainwindow.h" + +// KDE Includes +#include + +// Qt Includes +#include +#include +#include + + +SearchBar::SearchBar(QWidget *parent) : + KLineEdit(parent) + , m_networkAccessManager(new QNetworkAccessManager(this)) + , m_timer(new QTimer(this)) +{ + setMinimumWidth(180); + + setFocusPolicy(Qt::WheelFocus); + setMouseTracking(true); + setAcceptDrops(true); + + QSizePolicy policy = sizePolicy(); + setSizePolicy(QSizePolicy::Preferred, policy.verticalPolicy()); + + setClearButtonShown(true); + + // better solution than using QPalette(s).. + setClickMessage(i18n("Search..")); + + // setting QNetworkAccessManager.. + connect(m_networkAccessManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(handleNetworkData(QNetworkReply*))); + + // setting QTimer.. + m_timer->setSingleShot(true); + m_timer->setInterval(200); + connect(m_timer, SIGNAL(timeout()), SLOT(autoSuggest())); + connect(this, SIGNAL(textEdited(QString)), m_timer, SLOT(start())); + + // connect searchNow slot.. + connect(this, SIGNAL(returnPressed()) , this , SLOT(searchNow())); +} + + +SearchBar::~SearchBar() +{ +} + + +void SearchBar::searchNow() +{ + m_timer->stop(); + QString searchText = text(); + + KUrl url(QLatin1String("http://www.google.com/search")); + url.addQueryItem(QLatin1String("q"), searchText); + url.addQueryItem(QLatin1String("ie"), QLatin1String("UTF-8")); + url.addQueryItem(QLatin1String("oe"), QLatin1String("UTF-8")); + url.addQueryItem(QLatin1String("client"), QLatin1String("rekonq")); + emit search(url); +} + + +void SearchBar::focusInEvent(QFocusEvent *event) +{ + KLineEdit::focusInEvent(event); + clear(); +} + + +void SearchBar::autoSuggest() +{ + QString str = text(); + QString url = QString("http://google.com/complete/search?output=toolbar&q=%1").arg(str); + m_networkAccessManager->get(QNetworkRequest(QString(url))); +} + + +void SearchBar::handleNetworkData(QNetworkReply *networkReply) +{ + QUrl url = networkReply->url(); + if (!networkReply->error()) + { + QStringList choices; + + QString response(networkReply->readAll()); + QXmlStreamReader xml(response); + while (!xml.atEnd()) + { + xml.readNext(); + if (xml.tokenType() == QXmlStreamReader::StartElement) + if (xml.name() == "suggestion") + { + QStringRef str = xml.attributes().value("data"); + choices << str.toString(); + } + } + + setCompletedItems(choices, true); + } + + networkReply->deleteLater(); +} diff --git a/src/searchbar.h b/src/searchbar.h new file mode 100644 index 00000000..3fdf55b4 --- /dev/null +++ b/src/searchbar.h @@ -0,0 +1,67 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2008-2009 by Andrea Diamantini +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + + +#ifndef SEARCHBAR_H +#define SEARCHBAR_H + +// KDE Includes +#include + +// Forward Declarations +class KUrl; +class QFocusEvent; +class QTimer; +class QNetworkAccessManager; +class QNetworkReply; + +/** + * This class defines an internet search bar. + */ +class SearchBar : public KLineEdit +{ + Q_OBJECT + +public: + SearchBar(QWidget *parent = 0); + ~SearchBar(); + +public slots: + void autoSuggest(); + void handleNetworkData(QNetworkReply *networkReply); + + /** + * Use this slot to perform one search in one search engine + * + */ + void searchNow(); + +protected: + void focusInEvent(QFocusEvent *); + +signals: + void search(const KUrl &); + +private: + QNetworkAccessManager *m_networkAccessManager; + QTimer *m_timer; +}; + +#endif diff --git a/src/settings.cpp b/src/settings.cpp new file mode 100644 index 00000000..1e42413f --- /dev/null +++ b/src/settings.cpp @@ -0,0 +1,212 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2007-2008 Trolltech ASA. All rights reserved +* Copyright (C) 2008-2009 by Andrea Diamantini +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + + +// Self Includes +#include "settings.h" +#include "settings.moc" + +// Auto Includes +#include "rekonq.h" + +// Local Includes +#include "application.h" +#include "mainwindow.h" +#include "cookiejar.h" +#include "history.h" +#include "networkaccessmanager.h" +#include "webview.h" + +//Ui Includes +#include "ui_settings_general.h" +#include "ui_settings_fonts.h" +#include "ui_settings_privacy.h" +#include "ui_settings_proxy.h" +#include "ui_settings_webkit.h" + +// KDE Includes +#include +#include +#include +#include +#include +#include + +// Qt Includes +#include +#include + + +class Private +{ +private: + Ui::general generalUi; + Ui::fonts fontsUi; + Ui::privacy privacyUi; + Ui::proxy proxyUi; + Ui::webkit webkitUi; + + Private(SettingsDialog *parent); + + friend class SettingsDialog; +}; + + +Private::Private(SettingsDialog *parent) +{ + QWidget *widget; + KPageWidgetItem *pageItem; + + widget = new QWidget; + generalUi.setupUi(widget); + widget->layout()->setMargin(0); + pageItem = parent->addPage(widget , i18n("General")); + pageItem->setIcon(KIcon("rekonq")); + + widget = new QWidget; + fontsUi.setupUi(widget); + widget->layout()->setMargin(0); + pageItem = parent->addPage(widget , i18n("Fonts")); + pageItem->setIcon(KIcon("preferences-desktop-font")); + + widget = new QWidget; + privacyUi.setupUi(widget); + widget->layout()->setMargin(0); + pageItem = parent->addPage(widget , i18n("Privacy")); + pageItem->setIcon(KIcon("preferences-desktop-personal")); + + widget = new QWidget; + proxyUi.setupUi(widget); + widget->layout()->setMargin(0); + pageItem = parent->addPage(widget , i18n("Proxy")); + pageItem->setIcon(KIcon("preferences-system-network")); + + widget = new QWidget; + webkitUi.setupUi(widget); + widget->layout()->setMargin(0); + pageItem = parent->addPage(widget , i18n("Webkit")); + QString webkitIconPath = KStandardDirs::locate("appdata", "pics/webkit-icon.png"); + kWarning() << webkitIconPath; + KIcon webkitIcon = KIcon(QIcon(webkitIconPath)); + pageItem->setIcon(webkitIcon); +} + +// ----------------------------------------------------------------------------------------------------- + +SettingsDialog::SettingsDialog(QWidget *parent) + : KConfigDialog(parent, "rekonfig", ReKonfig::self()) + , d(new Private(this)) +{ + setFaceType(KPageDialog::List); + showButtonSeparator(true); + + setWindowTitle(i18n("rekonfig..")); + setModal(true); + + readConfig(); + + connect(d->generalUi.setHomeToCurrentPageButton, SIGNAL(clicked()), this, SLOT(setHomeToCurrentPage())); + connect(d->privacyUi.exceptionsButton, SIGNAL(clicked()), this, SLOT(showExceptions())); + connect(d->privacyUi.cookiesButton, SIGNAL(clicked()), this, SLOT(showCookies())); + + setWebSettingsToolTips(); +} + + + +SettingsDialog::~SettingsDialog() +{ + delete d; +} + + +void SettingsDialog::setWebSettingsToolTips() +{ + d->webkitUi.kcfg_autoLoadImages->setToolTip(i18n("Specifies whether images are automatically loaded in web pages")); + d->webkitUi.kcfg_javascriptEnabled->setToolTip(i18n("Enables the running of JavaScript programs.")); + d->webkitUi.kcfg_javaEnabled->setToolTip(i18n("Enables Java applets.")); + d->webkitUi.kcfg_pluginsEnabled->setToolTip(i18n("Enables plugins in web pages.")); + d->webkitUi.kcfg_javascriptCanOpenWindows->setToolTip(i18n("Allows JavaScript programs to opening new windows.")); + d->webkitUi.kcfg_javascriptCanAccessClipboard->setToolTip(i18n("Allows JavaScript programs to reading/writing to the clipboard.")); + d->webkitUi.kcfg_linksIncludedInFocusChain->setToolTip(i18n("Includes hyperlinks in the keyboard focus chain.")); + d->webkitUi.kcfg_zoomTextOnly->setToolTip(i18n("Applies the zoom factor on a frame to only the text or all content.")); + d->webkitUi.kcfg_printElementBackgrounds->setToolTip(i18n("Draws also background color and images when the page is printed.")); + d->webkitUi.kcfg_offlineStorageDatabaseEnabled->setToolTip(i18n("Support for the HTML 5 offline storage feature.")); + d->webkitUi.kcfg_offlineWebApplicationCacheEnabled->setToolTip(i18n("Support for the HTML 5 web application cache feature.")); + d->webkitUi.kcfg_localStorageDatabaseEnabled->setToolTip(i18n("Support for the HTML 5 local storage feature.")); +} + + +// we need this function to UPDATE the config widget data.. +void SettingsDialog::readConfig() +{ + // ======= General + d->generalUi.downloadDirUrlRequester->setMode(KFile::Directory | KFile::ExistingOnly | KFile::LocalOnly); + d->generalUi.downloadDirUrlRequester->setUrl(ReKonfig::downloadDir()); + connect(d->generalUi.downloadDirUrlRequester, SIGNAL(textChanged(QString)), this, SLOT(saveSettings())); + + // ======= Fonts + d->fontsUi.kcfg_fixedFont->setOnlyFixed(true); + + // ======= Proxy + bool proxyEnabled = ReKonfig::isProxyEnabled(); + d->proxyUi.groupBox->setEnabled(proxyEnabled); + connect(d->proxyUi.kcfg_isProxyEnabled, SIGNAL(clicked(bool)), d->proxyUi.groupBox, SLOT(setEnabled(bool))); +} + + +// we need this function to SAVE settings in rc file.. +void SettingsDialog::saveSettings() +{ + // General + ReKonfig::setDownloadDir(d->generalUi.downloadDirUrlRequester->url().prettyUrl()); + + // Save + ReKonfig::self()->writeConfig(); +} + + +// ---------------------------------------------------------------------------------------------- + + +void SettingsDialog::showCookies() +{ + CookiesDialog *dialog = new CookiesDialog(Application::cookieJar(), this); + dialog->exec(); +} + + +void SettingsDialog::showExceptions() +{ + CookiesExceptionsDialog *dialog = new CookiesExceptionsDialog(Application::cookieJar(), this); + dialog->exec(); +} + + +void SettingsDialog::setHomeToCurrentPage() +{ + MainWindow *mw = static_cast(parent()); + WebView *webView = mw->currentTab(); + if (webView) + { + d->generalUi.kcfg_homePage->setText(webView->url().prettyUrl()); + } +} diff --git a/src/settings.h b/src/settings.h new file mode 100644 index 00000000..26fd2d16 --- /dev/null +++ b/src/settings.h @@ -0,0 +1,55 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2007-2008 Trolltech ASA. All rights reserved +* Copyright (C) 2008-2009 by Andrea Diamantini +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + +#ifndef SETTINGS_H +#define SETTINGS_H + +// KDE Includes +#include + +// Forward Declarations +class QWidget; +class Private; + + +class SettingsDialog : public KConfigDialog +{ + Q_OBJECT + +public: + SettingsDialog(QWidget *parent = 0); + ~SettingsDialog(); + +private: + Private* const d; + + void setWebSettingsToolTips(); + +private slots: + void readConfig(); + void saveSettings(); + + void setHomeToCurrentPage(); + void showCookies(); + void showExceptions(); +}; + +#endif // SETTINGS_H diff --git a/src/settings_fonts.ui b/src/settings_fonts.ui new file mode 100644 index 00000000..de258cad --- /dev/null +++ b/src/settings_fonts.ui @@ -0,0 +1,97 @@ + + + fonts + + + + 0 + 0 + 414 + 298 + + + + Appearance + + + + + + Fonts + + + + + + + + Standard Font + + + + + + + Fixed Font + + + + + + + + + + + + + + + + + + Dimension + + + + + + + + Font Size + + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + KFontComboBox + KComboBox +
kfontcombobox.h
+
+
+ + +
diff --git a/src/settings_general.ui b/src/settings_general.ui new file mode 100644 index 00000000..44561b02 --- /dev/null +++ b/src/settings_general.ui @@ -0,0 +1,142 @@ + + + general + + + + 0 + 0 + 515 + 415 + + + + General + + + + + + Places + + + + + + Home Page: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + + + + + + Set to current page + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + Save downloads to: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + ask where saving downloads + + + + + + + + + + Tabbed Browsing + + + + + + Always show tab bar + + + + + + + Open tabs in the background + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + KLineEdit + QLineEdit +
klineedit.h
+
+ + KUrlRequester + QFrame +
kurlrequester.h
+
+
+ + +
diff --git a/src/settings_privacy.ui b/src/settings_privacy.ui new file mode 100644 index 00000000..78202688 --- /dev/null +++ b/src/settings_privacy.ui @@ -0,0 +1,168 @@ + + + privacy + + + + 0 + 0 + 461 + 313 + + + + Privacy + + + + + + History + + + + + + Remove history items: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + After one day + + + + + After one week + + + + + After two weeks + + + + + After one month + + + + + After one year + + + + + Manually + + + + + + + + + + + Cookies + + + + + + Accept Cookies: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Always + + + + + Never + + + + + Only from sites you visit + + + + + + + + Exceptions... + + + + + + + Keep until: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + They expire + + + + + I exit the application + + + + + At most 90 days + + + + + + + + Cookies... + + + + + + + + + + Qt::Vertical + + + + 20 + 136 + + + + + + + + + diff --git a/src/settings_proxy.ui b/src/settings_proxy.ui new file mode 100644 index 00000000..90e308a2 --- /dev/null +++ b/src/settings_proxy.ui @@ -0,0 +1,152 @@ + + proxy + + + + 0 + 0 + 440 + 223 + + + + Proxy + + + + + + Enable proxy + + + + + + + Proxy Settings + + + + + + Type: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + SOCKS 5 + + + + + HTTP + + + + + + + + Host: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Port: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 10000 + + + 1080 + + + + + + + Qt::Horizontal + + + + 293 + 20 + + + + + + + + Username: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Password: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Qt::Vertical + + + + 20 + 8 + + + + + + + + + + + + KLineEdit + QLineEdit +
klineedit.h
+
+
+ + +
diff --git a/src/settings_webkit.ui b/src/settings_webkit.ui new file mode 100644 index 00000000..362e392a --- /dev/null +++ b/src/settings_webkit.ui @@ -0,0 +1,134 @@ + + + webkit + + + + 0 + 0 + 564 + 360 + + + + Form + + + + + + WebKit Settings + + + + + + Auto Load Images + + + + + + + Qt::Vertical + + + + + + + Links included in focus chain + + + + + + + Javascript support + + + + + + + Zoom Text Only + + + + + + + Java support + + + + + + + Print element Backgrounds + + + + + + + Plugins + + + + + + + Offline storage Database + + + + + + + Javascript can open windows + + + + + + + Offline Web Application Cache + + + + + + + Javascript can access clipboard + + + + + + + Local storage database + + + + + + + + + + Qt::Vertical + + + + 20 + 146 + + + + + + + + + diff --git a/src/sidepanel.cpp b/src/sidepanel.cpp new file mode 100644 index 00000000..0fab81c0 --- /dev/null +++ b/src/sidepanel.cpp @@ -0,0 +1,53 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2009 by Paweł Prażak +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + +// Self Includes +#include "sidepanel.h" +#include "sidepanel.moc" + +// Auto Includes +#include "rekonq.h" + +// Local Includes +#include "panelhistory.h" + + +SidePanel::SidePanel(const QString &title, QWidget *parent, Qt::WindowFlags flags) + : QDockWidget(title, parent, flags) + , m_panelHistory(new PanelHistory(this)) +{ + setObjectName("sidePanel"); + setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); + + setShown(ReKonfig::showSideBar()); + + connect(m_panelHistory, SIGNAL(openUrl(const KUrl&)), this, SIGNAL(openUrl(const KUrl&))); + + setWidget(m_panelHistory); +} + + +SidePanel::~SidePanel() +{ + // Save side panel's state + ReKonfig::setShowSideBar(!isHidden()); + + delete m_panelHistory; +} + diff --git a/src/sidepanel.h b/src/sidepanel.h new file mode 100644 index 00000000..c0af7827 --- /dev/null +++ b/src/sidepanel.h @@ -0,0 +1,50 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2009 by Paweł Prażak +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + +#ifndef SIDEPANEL_H +#define SIDEPANEL_H + +// Qt Includes +#include + +// Local Includes +#include "application.h" + +class KUrl; +class PanelHistory; + + +class SidePanel : public QDockWidget +{ + Q_OBJECT + Q_DISABLE_COPY(SidePanel) + +public: + explicit SidePanel(const QString &title, QWidget *parent = 0, Qt::WindowFlags flags = 0); + ~SidePanel(); + +signals: + void openUrl(const KUrl &); + +private: + PanelHistory *m_panelHistory; + +}; + +#endif // SIDEPANEL_H diff --git a/src/stackedurlbar.cpp b/src/stackedurlbar.cpp new file mode 100644 index 00000000..113c8769 --- /dev/null +++ b/src/stackedurlbar.cpp @@ -0,0 +1,152 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2009 by Paweł Prażak +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + +// Self Includes +#include "stackedurlbar.h" +#include "stackedurlbar.moc" + +// KDE Includes +#include "kdebug.h" + +// Local Includes +#include "application.h" +#include "history.h" +#include "urlbar.h" + + +StackedUrlBar::StackedUrlBar(QWidget *parent) + : QStackedWidget(parent) + , m_completion(0) + , m_completionModel(0) +{ +} + + +StackedUrlBar::~StackedUrlBar() +{ + delete m_completion; + delete m_completionModel; +} + + +UrlBar *StackedUrlBar::currentUrlBar() +{ + return urlBar(currentIndex()); +} + + +UrlBar *StackedUrlBar::urlBar(int index) +{ + UrlBar *urlBar = qobject_cast(QStackedWidget::widget(index)); + if (!urlBar) + { + kWarning() << "URL bar with index" << index << "not found. Returning NULL. line:" << __LINE__; + } + + return urlBar; +} + + +void StackedUrlBar::addUrlBar(UrlBar* urlBar) +{ + QStackedWidget::addWidget(urlBar); + + // setup completion objects + urlBar->setCompletionObject(completion()); +} + + +void StackedUrlBar::setCurrentUrlBar(UrlBar* urlBar) +{ + QStackedWidget::setCurrentWidget(urlBar); +} + + +void StackedUrlBar::removeUrlBar(UrlBar* urlBar) +{ + QStackedWidget::removeWidget(urlBar); +} + + +void StackedUrlBar::clear() +{ + currentUrlBar()->clearHistory(); + + for (int i = 0; i < count(); ++i) + { + urlBar(i)->clear(); + } +} + + +QList StackedUrlBar::urlBars() +{ + QList list; + for (int i = 0; i < count(); ++i) + { + const UrlBar* u = urlBar(i); + list.append(u); + } + return list; +} + + +KCompletion *StackedUrlBar::completion() +{ + // make sure completion was created + if (!m_completion) + { + m_completion = new KCompletion(); + m_completion->setCompletionMode(KGlobalSettings::CompletionPopupAuto); + m_completion->setOrder(KCompletion::Weighted); + m_completion->setIgnoreCase(true); + + kDebug() << "Initialize completion list..."; + + HistoryCompletionModel *model = completionModel(); + int count = model->rowCount(); + + kDebug() << "...initialize history items" << count; + + // change order to insertion to avoid confusion of the addItem method + // in weighted it expects format string:number and it thinks http it the whole string + m_completion->setOrder(KCompletion::Insertion); + for (int i = 0; i < count; ++i) + { + QString item = model->data(model->index(i, 0)).toString(); + item.remove(QRegExp("^http://|/$")); + m_completion->addItem(item); + } + m_completion->setOrder(KCompletion::Weighted); + } + + return m_completion; +} + + +HistoryCompletionModel *StackedUrlBar::completionModel() +{ + if (!m_completionModel) + { + m_completionModel = new HistoryCompletionModel(this); + m_completionModel->setSourceModel(Application::historyManager()->historyFilterModel()); + } + return m_completionModel; +} diff --git a/src/stackedurlbar.h b/src/stackedurlbar.h new file mode 100644 index 00000000..d3aea16f --- /dev/null +++ b/src/stackedurlbar.h @@ -0,0 +1,64 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2008 by Andrea Diamantini +* Copyright (C) 2009 by Paweł Prażak +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + + +#ifndef STACKEDURLBAR_H +#define STACKEDURLBAR_H + +// Qt Includes +#include + +class KCompletion; +class HistoryCompletionModel; +class UrlBar; + +class StackedUrlBar : public QStackedWidget +{ + Q_OBJECT + +public: + StackedUrlBar(QWidget *parent = 0); + ~StackedUrlBar(); + +public: + UrlBar *currentUrlBar(); + UrlBar *urlBar(int index); + void addUrlBar(UrlBar *urlBar); + void setCurrentUrlBar(UrlBar *urlBar); + void removeUrlBar(UrlBar *urlBar); + + QList urlBars(); + + KCompletion *completion(); + HistoryCompletionModel *completionModel(); + +public slots: + void clear(); + +private: + Q_DISABLE_COPY(StackedUrlBar) + + KCompletion *m_completion; + HistoryCompletionModel *m_completionModel; +}; + +#endif // STACKEDURLBAR_H + diff --git a/src/tabbar.cpp b/src/tabbar.cpp new file mode 100644 index 00000000..be1b05fd --- /dev/null +++ b/src/tabbar.cpp @@ -0,0 +1,145 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2007-2008 Trolltech ASA. All rights reserved +* Copyright (C) 2008-2009 by Andrea Diamantini +* Copyright (C) 2009 by Paweł Prażak +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + +//Self Includes +#include "tabbar.h" +#include "tabbar.moc" + +// Local Includes +#include "application.h" +#include "mainwindow.h" +#include "history.h" +#include "urlbar.h" +#include "webview.h" + +// KDE Includes +#include +#include +#include +#include +#include +#include + +// Qt Includes +#include + + +TabBar::TabBar(QWidget *parent) + : KTabBar(parent) + , m_parent(parent) +{ + setElideMode(Qt::ElideRight); + setContextMenuPolicy(Qt::CustomContextMenu); + setMovable(true); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, + SLOT(contextMenuRequested(const QPoint &))); + + // tabbar font + QFont standardFont = KGlobalSettings::generalFont(); + QString fontFamily = standardFont.family(); + int dim = standardFont.pointSize(); + setFont(QFont(fontFamily, dim - 1)); +} + + +TabBar::~TabBar() +{ +} + + +QSize TabBar::tabSizeHint(int index) const +{ + QSize s = m_parent->sizeHint(); + int w; + + int n = count(); + + if (n > 6) + { + w = s.width() / 5; + } + else + { + if (n > 3) + { + w = s.width() / 4; + } + else + { + w = s.width() / 3; + } + } + int h = KTabBar::tabSizeHint(index).height(); + + QSize ts = QSize(w, h); + return ts; +} + + +void TabBar::contextMenuRequested(const QPoint &position) +{ + KMenu menu; + MainWindow *mainWindow = Application::instance()->mainWindow(); + + menu.addAction(mainWindow->actionByName(QLatin1String("new_tab"))); + int index = tabAt(position); + if (-1 != index) + { + m_actualIndex = index; + + menu.addAction(KIcon("tab-duplicate"), i18n("Clone Tab"), this, SLOT(cloneTab())); + menu.addSeparator(); + menu.addAction(KIcon("tab-close"), i18n("&Close Tab"), this, SLOT(closeTab())); + menu.addAction(KIcon("tab-close-other"), i18n("Close &Other Tabs"), this, SLOT(closeOtherTabs())); + menu.addSeparator(); + menu.addAction(KIcon("view-refresh"), i18n("Reload Tab"), this, SLOT(reloadTab())); + } + else + { + menu.addSeparator(); + } + menu.addAction(i18n("Reload All Tabs"), this, SIGNAL(reloadAllTabs())); + menu.exec(QCursor::pos()); +} + + +void TabBar::cloneTab() +{ + emit cloneTab(m_actualIndex); +} + + +void TabBar::closeTab() +{ + emit closeTab(m_actualIndex); +} + + +void TabBar::closeOtherTabs() +{ + emit closeOtherTabs(m_actualIndex); +} + + +void TabBar::reloadTab() +{ + emit reloadTab(m_actualIndex); +} diff --git a/src/tabbar.h b/src/tabbar.h new file mode 100644 index 00000000..8563793f --- /dev/null +++ b/src/tabbar.h @@ -0,0 +1,78 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2007-2008 Trolltech ASA. All rights reserved +* Copyright (C) 2008-2009 by Andrea Diamantini +* Copyright (C) 2009 by Paweł Prażak +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + +#ifndef TABBAR_H +#define TABBAR_H + +// KDE Includes +#include + +// Qt Includes +#include + +/** + * Tab bar with a few more features such as + * a context menu and shortcuts + * + */ + +class TabBar : public KTabBar +{ + Q_OBJECT + + +public: + TabBar(QWidget *parent = 0); + ~TabBar(); + +signals: + void cloneTab(int index); + void closeTab(int index); + void closeOtherTabs(int index); + void reloadTab(int index); + void reloadAllTabs(); + +protected: + /** + * Added to fix tab dimension + */ + virtual QSize tabSizeHint(int index) const; + +private slots: + void cloneTab(); + void closeTab(); + void closeOtherTabs(); + void reloadTab(); + void contextMenuRequested(const QPoint &position); + +private: + friend class MainView; + + QWidget *m_parent; + + /** + * the index in which we are seeing a Context menu + */ + int m_actualIndex; +}; + +#endif diff --git a/src/urlbar.cpp b/src/urlbar.cpp new file mode 100644 index 00000000..29eaed70 --- /dev/null +++ b/src/urlbar.cpp @@ -0,0 +1,240 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2008-2009 by Andrea Diamantini +* Copyright (C) 2009 by Domrachev Alexandr +* Copyright (C) 2009 by Paweł Prażak +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + +// Self Includes +#include "urlbar.h" +#include "urlbar.moc" + +// Qt Includes +#include +#include + +// KDE Includes +#include +#include +#include + +// Local Includes +#include "application.h" +#include "history.h" +#include "lineedit.h" +#include "mainwindow.h" +#include "webview.h" + + +QColor UrlBar::s_defaultBaseColor; + +UrlBar::UrlBar(QWidget *parent) + : KHistoryComboBox(true, parent) + , m_lineEdit(new LineEdit) + , m_progress(0) +{ + setUrlDropsEnabled(true); + setAutoDeleteCompletionObject(true); + setMinimumWidth(180); + + setTrapReturnKey(true); + + setupLineEdit(); + + // add every item to history + connect(this, SIGNAL(returnPressed(const QString&)), SLOT(slotActivated(const QString&))); + connect(completionBox(), SIGNAL(activated(const QString&)), SLOT(slotActivated(const QString&))); + + connect(this, SIGNAL(cleared()), SLOT(slotCleared())); + + // setup completion box + completionBox()->setTabHandling(true); // Konqueror bug #167135 + + // set dropdown list background + QPalette p = view()->palette(); + p.setColor(QPalette::Base, palette().color(QPalette::Base)); + view()->setPalette(p); + + // set empty item with default icon + slotUpdateUrl(); +} + + +UrlBar::~UrlBar() +{ +} + + +void UrlBar::setupLineEdit() +{ + // Make m_lineEdit background transparent + QPalette p = m_lineEdit->palette(); + p.setColor(QPalette::Base, Qt::transparent); + m_lineEdit->setPalette(p); + + if (!s_defaultBaseColor.isValid()) + { + s_defaultBaseColor = palette().color(QPalette::Base); + } + + setLineEdit(m_lineEdit); + + // Make the lineedit consume the Qt::Key_Enter event... + lineEdit()->setTrapReturnKey(true); + + lineEdit()->setHandleSignals(true); + + // clear the URL bar + lineEdit()->clear(); +} + + +void UrlBar::setUrl(const QUrl& url) +{ + if (url.isEmpty()) + return; + + m_currentUrl = url; + slotUpdateUrl(); +} + + +void UrlBar::slotUpdateUrl() +{ + if (count()) + { + changeUrl(0, Application::instance()->icon(m_currentUrl), m_currentUrl); + } + else + { + insertUrl(0, Application::instance()->icon(m_currentUrl), m_currentUrl); + } + + setCurrentIndex(0); + + // important security consideration: always display the beginning + // of the url rather than its end to prevent spoofing attempts. + // Must be AFTER setCurrentIndex + if (!hasFocus()) + { + lineEdit()->setCursorPosition(0); + } +} + + +inline void UrlBar::slotActivated(const QString& url) +{ + if (url.isEmpty()) + return; + + setUrl(url); + + Application::historyManager()->addHistoryEntry(url); + + emit activated(m_currentUrl); +} + + +inline void UrlBar::slotCleared() +{ + // clear the history on user's request from context menu + clear(); + Application::historyManager()->clear(); +} + + +inline void UrlBar::slotLoadFinished(bool) +{ + // reset progress bar after small delay + m_progress = 0; + QTimer::singleShot(200, this, SLOT(repaint())); +} + + +inline void UrlBar::slotUpdateProgress(int progress) +{ + m_progress = progress; + repaint(); +} + + +void UrlBar::paintEvent(QPaintEvent *event) +{ + // set background color of UrlBar + QPalette p = palette(); + p.setColor(QPalette::Base, s_defaultBaseColor); + setPalette(p); + + KHistoryComboBox::paintEvent(event); + + if (!hasFocus()) + { + QPainter painter(this); + + QColor loadingColor; + if (m_currentUrl.scheme() == QLatin1String("https")) + { + loadingColor = QColor(248, 248, 100); + } + else + { + loadingColor = QColor(116, 192, 250); + } + painter.setBrush(generateGradient(loadingColor, height())); + painter.setPen(Qt::transparent); + + QRect backgroundRect = lineEdit()->frameGeometry(); + int mid = backgroundRect.width() / 100 * m_progress; + QRect progressRect(backgroundRect.x(), backgroundRect.y(), mid, backgroundRect.height()); + painter.drawRect(progressRect); + painter.end(); + } +} + + +void UrlBar::focusOutEvent(QFocusEvent *event) +{ + // set back last loaded url in case user cleared it + setUrl(m_currentUrl); + + KHistoryComboBox::focusOutEvent(event); +} + + +QSize UrlBar::sizeHint() const +{ + QSize size(lineEdit()->sizeHint()); + // make it (more or less) the same height with search bar (at least on oxygen) +// size.setHeight(size.height() + 2); + return size; +} + +QLinearGradient UrlBar::generateGradient(const QColor &color, int height) +{ + QColor base = s_defaultBaseColor; + base.setAlpha(0); + QColor barColor = color; + barColor.setAlpha(200); + QLinearGradient gradient(0, 0, 0, height); + gradient.setColorAt(0, base); + gradient.setColorAt(0.25, barColor.lighter(120)); + gradient.setColorAt(0.5, barColor); + gradient.setColorAt(0.75, barColor.lighter(120)); + gradient.setColorAt(1, base); + return gradient; +} diff --git a/src/urlbar.h b/src/urlbar.h new file mode 100644 index 00000000..236792bd --- /dev/null +++ b/src/urlbar.h @@ -0,0 +1,99 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2008-2009 by Andrea Diamantini +* Copyright (C) 2009 by Domrachev Alexandr +* Copyright (C) 2009 by Paweł Prażak +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + +#ifndef URLBAR_H +#define URLBAR_H + +// Qt Includes +#include +#include +#include + +// KDE Includes +#include +#include + +// Local Includes +#include "lineedit.h" + + +// Forward Declarations +class QLinearGradient; +class QWidget; + + +class UrlBar : public KHistoryComboBox +{ + Q_OBJECT + +public: + UrlBar(QWidget *parent = 0); + ~UrlBar(); + + void selectAll() const + { + lineEdit()->selectAll(); + } + KUrl url() const + { + return m_currentUrl; + } + + QSize sizeHint() const; + +signals: + void activated(const KUrl&); + +public slots: + void setUrl(const QUrl &url); + void slotUpdateProgress(int progress); + +private slots: + void slotActivated(const QString&); + void slotLoadFinished(bool); + void slotCleared(); + void slotUpdateUrl(); + +protected: + virtual void paintEvent(QPaintEvent *event); + virtual void focusOutEvent(QFocusEvent *event); + +private: + void setupLineEdit(); + + KLineEdit *lineEdit() const + { + return m_lineEdit; + } + + static QLinearGradient generateGradient(const QColor &color, int height); + + static QColor s_defaultBaseColor; + + LineEdit *m_lineEdit; + + QIcon m_currentIcon; + KUrl m_currentUrl; + int m_progress; +}; + +#endif diff --git a/src/webview.cpp b/src/webview.cpp new file mode 100644 index 00000000..5f42112b --- /dev/null +++ b/src/webview.cpp @@ -0,0 +1,504 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2007-2008 Trolltech ASA. All rights reserved +* Copyright (C) 2008 Benjamin C. Meyer +* Copyright (C) 2008-2009 by Andrea Diamantini +* Copyright (C) 2009 by Paweł Prażak +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + +// Self Includes +#include "webview.h" +#include "webview.moc" + +// Auto Includes +#include "rekonq.h" + +// Local Includes +#include "application.h" +#include "mainwindow.h" +#include "mainview.h" +#include "cookiejar.h" +#include "networkaccessmanager.h" +#include "download.h" +#include "history.h" + + +// KDE Includes +#include +#include +#include +#include +#include + +// Qt Includes +#include +#include +#include +#include + + +WebPage::WebPage(QObject *parent) + : QWebPage(parent) + , m_keyboardModifiers(Qt::NoModifier) + , m_pressedButtons(Qt::NoButton) + , m_openInNewTab(false) +{ + setNetworkAccessManager(Application::networkAccessManager()); + + setForwardUnsupportedContent(true); + connect(this, SIGNAL(unsupportedContent(QNetworkReply *)), this, SLOT(handleUnsupportedContent(QNetworkReply *))); +} + + +WebPage::~WebPage() +{ +} + + +bool WebPage::acceptNavigationRequest(QWebFrame *frame, const QNetworkRequest &request, NavigationType type) +{ + QString scheme = request.url().scheme(); + if (scheme == QLatin1String("mailto")) + { + KToolInvocation::invokeMailer(request.url()); + return false; + } + + WebView *webView; + + switch (type) + { + + // user clicked on a link or pressed return on a focused link. + case QWebPage::NavigationTypeLinkClicked: + + if (m_keyboardModifiers & Qt::ControlModifier || m_pressedButtons == Qt::MidButton) + { + webView = Application::instance()->newWebView(); + webView->setFocus(); + webView->load(request); + m_keyboardModifiers = Qt::NoModifier; + m_pressedButtons = Qt::NoButton; + return false; + } + + if (frame == mainFrame()) + { + m_loadingUrl = request.url(); + emit loadingUrl(m_loadingUrl); + } + else + { + // if frame doesn't exists (perhaps) we are pointing to a blank target.. + if (!frame) + { + webView = Application::instance()->newWebView(); + webView->setFocus(); + webView->load(request); + return false; + } + } + break; + + // user activated a submit button for an HTML form. + case QWebPage::NavigationTypeFormSubmitted: + break; + + // Navigation to a previously shown document in the back or forward history is requested. + case QWebPage::NavigationTypeBackOrForward: + break; + + // user activated the reload action. + case QWebPage::NavigationTypeReload: + +#if QT_VERSION <= 040500 + // HACK Ported from Arora + // A short term hack until QtWebKit can get a reload without cache QAction + // *FYI* currently type is never NavigationTypeReload + // See: https://bugs.webkit.org/show_bug.cgi?id=24283 + if (qApp->keyboardModifiers() & Qt::ShiftModifier) + { + QNetworkRequest newRequest(request); + newRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, + QNetworkRequest::AlwaysNetwork); + mainFrame()->load(request); + return false; + } +#endif + + break; + + // An HTML form was submitted a second time. + case QWebPage::NavigationTypeFormResubmitted: + break; + + // A navigation to another document using a method not listed above. + case QWebPage::NavigationTypeOther: + break; + + // should be nothing.. + default: + break; + } + + return QWebPage::acceptNavigationRequest(frame, request, type); +} + + +QWebPage *WebPage::createWindow(QWebPage::WebWindowType type) +{ + // added to manage web modal dialogs + if (type == QWebPage::WebModalDialog) + { + // FIXME : need a "real" implementation.. + kWarning() << "Modal Dialog ---------------------------------------"; + QWebView *w = new QWebView(); + w->show(); + return w->page(); + } + + if (m_keyboardModifiers & Qt::ControlModifier || m_pressedButtons == Qt::MidButton) + { + m_openInNewTab = true; + } + + if (m_openInNewTab) + { + m_openInNewTab = false; + return Application::instance()->newWebView()->page(); + } + + MainWindow *mainWindow = Application::instance()->mainWindow(); + return mainWindow->currentTab()->page(); +} + + +QObject *WebPage::createPlugin(const QString &classId, const QUrl &url, const QStringList ¶mNames, const QStringList ¶mValues) +{ + kWarning() << "creating PLUGIN for rekonq "; + kWarning() << "classId = " << classId; + kWarning() << "url = " << url; + kWarning() << "Param Names = " << paramNames; + kWarning() << "Param Values = " << paramValues; + + QUiLoader loader; + return loader.createWidget(classId, view()); +} + + +void WebPage::handleUnsupportedContent(QNetworkReply *reply) +{ + // create convenience fake api:// protocol for KDE apidox search and Qt docs + if (reply->url().scheme() == "api") + { + QString path; + QString className = reply->url().host().toLower(); + if (className[0] == 'k') + { + path = QString("http://api.kde.org/new.classmapper.php?class=%1").arg(className); + } + else if (className[0] == 'q') + { + path = QString("http://doc.trolltech.com/4.5/%1.html").arg(className); + } + QUrl url(path); + + Application::instance()->mainWindow()->loadUrl(url); + return; + } + + if (reply->error() == QNetworkReply::NoError) + { + // st iframe unwanted download fix + if (reply->header(QNetworkRequest::ContentTypeHeader).isValid()) + { + KUrl srcUrl = reply->url(); + Application::downloadManager()->newDownload(srcUrl); + } + return; + } + + // display "not found" page + QString notfoundFilePath = KStandardDirs::locate("data", "rekonq/htmls/notfound.html"); + QFile file(notfoundFilePath); + bool isOpened = file.open(QIODevice::ReadOnly); + if (!isOpened) + { + kWarning() << "Couldn't open the notfound.html file"; + return; + } + QString title = i18n("Error loading page: ") + reply->url().toString(); + + QString imagePath = KIconLoader::global()->iconPath("rekonq", KIconLoader::NoGroup, false); + + QString html = QString(QLatin1String(file.readAll())) + .arg(title) + .arg("file://" + imagePath) + .arg(reply->errorString()) + .arg(reply->url().toString()); + + QList frames; + frames.append(mainFrame()); + while (!frames.isEmpty()) + { + QWebFrame *firstFrame = frames.takeFirst(); + if (firstFrame->url() == reply->url()) + { + firstFrame->setHtml(html, reply->url()); + return; + } + QList children = firstFrame->childFrames(); + foreach(QWebFrame *frame, children) + { + frames.append(frame); + } + } + if (m_loadingUrl == reply->url()) + { + mainFrame()->setHtml(html, reply->url()); + // Don't put error pages to the history. + Application::historyManager()->removeHistoryEntry(reply->url(), mainFrame()->title()); + } +} + + +// ----------------------------------------------------------------------------------------------------------------- + + +KActionCollection* WebView::s_webActionCollection; + + +WebView::WebView(QWidget* parent) + : QWebView(parent) + , m_page(new WebPage(this)) + , m_progress(0) +{ + setPage(m_page); + connect(page(), SIGNAL(statusBarMessage(const QString&)), this, SLOT(setStatusBarText(const QString&))); + connect(this, SIGNAL(loadProgress(int)), this, SLOT(setProgress(int))); + connect(this, SIGNAL(loadFinished(bool)), this, SLOT(loadFinished())); + connect(page(), SIGNAL(loadingUrl(const QUrl&)), this, SIGNAL(urlChanged(const QUrl &))); + connect(page(), SIGNAL(downloadRequested(const QNetworkRequest &)), this, SLOT(downloadRequested(const QNetworkRequest &))); + page()->setForwardUnsupportedContent(true); +} + + +KActionCollection* WebView::webActions() +{ + if (!s_webActionCollection) + { + s_webActionCollection = new KActionCollection(this); + + QAction *a; + + a = new KAction(KIcon("tab-new"), i18n("Open Link in New &Tab"), this); + connect(a, SIGNAL(triggered()), this, SLOT(openLinkInNewTab())); + s_webActionCollection->addAction(QLatin1String("open_link_in_new_tab"), a); + + a = pageAction(QWebPage::Cut); + a->setIcon(KIcon("edit-cut")); + a->setText(i18n("Cu&t")); + s_webActionCollection->addAction(QLatin1String("edit_cut"), a); + + a = pageAction(QWebPage::Copy); + a->setIcon(KIcon("edit-copy")); + a->setText(i18n("&Copy")); + s_webActionCollection->addAction(QLatin1String("edit_copy"), a); + + a = pageAction(QWebPage::Paste); + a->setIcon(KIcon("edit-paste")); + a->setText(i18n("&Paste")); + s_webActionCollection->addAction(QLatin1String("edit_paste"), a); + + a = pageAction(QWebPage::DownloadImageToDisk); + a->setIcon(KIcon("folder-image")); + a->setText(i18n("&Save Image As...")); + s_webActionCollection->addAction(QLatin1String("save_image_as"), a); + + a = pageAction(QWebPage::CopyImageToClipboard); + a->setIcon(KIcon("insert-image")); + a->setText(i18n("&Copy This Image")); + s_webActionCollection->addAction(QLatin1String("copy_this_image"), a); + + a = pageAction(QWebPage::DownloadLinkToDisk); + a->setIcon(KIcon("folder-downloads")); + a->setText(i18n("&Save Link As...")); + s_webActionCollection->addAction(QLatin1String("save_link_as"), a); + + a = pageAction(QWebPage::CopyLinkToClipboard); + a->setIcon(KIcon("insert-link")); + a->setText(i18n("&Copy Link Location")); + s_webActionCollection->addAction(QLatin1String("copy_link_location"), a); + + a = pageAction(QWebPage::InspectElement); + a->setIcon(KIcon("tools-report-bug")); + a->setText(i18n("&Inspect Element")); + s_webActionCollection->addAction(QLatin1String("inspect_element"), a); + } + + return s_webActionCollection; +} + + +void WebView::contextMenuEvent(QContextMenuEvent *event) +{ + QWebHitTestResult result = page()->mainFrame()->hitTestContent(event->pos()); + MainWindow *mainwindow = Application::instance()->mainWindow(); + + QAction *addBookmarkAction = Application::bookmarkProvider()->actionByName("add_bookmark_payload"); + addBookmarkAction->setText(i18n("Bookmark This Page")); + addBookmarkAction->setData(QVariant()); + KMenu menu(this); + + bool linkIsEmpty = result.linkUrl().isEmpty(); + if (!linkIsEmpty) + { + menu.addAction(webActions()->action("open_link_in_new_tab")); + } + else + { + menu.addAction(mainwindow->actionByName("new_tab")); + } + menu.addAction(mainwindow->actionByName("view_redisplay")); + menu.addSeparator(); + + if (page()->settings()->testAttribute(QWebSettings::DeveloperExtrasEnabled)) + { + menu.addAction(webActions()->action("inspect_element")); + menu.addSeparator(); + } + + menu.addAction(mainwindow->actionByName("history_back")); + menu.addAction(mainwindow->actionByName("history_forward")); + menu.addSeparator(); + + if (result.isContentSelected() && result.isContentEditable()) + { + menu.addAction(webActions()->action("edit_cut")); + } + + if (result.isContentSelected()) + { + menu.addAction(webActions()->action("edit_copy")); + } + + if (result.isContentEditable()) + { + menu.addAction(webActions()->action("edit_paste")); + } + + if (!linkIsEmpty) + { + menu.addSeparator(); + if (!result.pixmap().isNull()) + { + // TODO Add "View Image" + menu.addAction(webActions()->action("save_image_as")); + menu.addAction(webActions()->action("copy_this_image")); + } + menu.addAction(webActions()->action("save_link_as")); + menu.addAction(webActions()->action("copy_link_location")); + addBookmarkAction->setData(result.linkUrl()); + addBookmarkAction->setText(i18n("&Bookmark This Link")); + } + menu.addSeparator(); + + menu.addAction(addBookmarkAction); + menu.exec(mapToGlobal(event->pos())); +} + + +void WebView::wheelEvent(QWheelEvent *event) +{ + if (QApplication::keyboardModifiers() & Qt::ControlModifier) + { + int numDegrees = event->delta() / 8; + int numSteps = numDegrees / 15; + setTextSizeMultiplier(textSizeMultiplier() + numSteps * 0.1); + event->accept(); + return; + } + QWebView::wheelEvent(event); +} + + +void WebView::openLinkInNewTab() +{ + m_page->m_openInNewTab = true; + pageAction(QWebPage::OpenLinkInNewWindow)->trigger(); +} + + +void WebView::loadFinished() +{ + if (m_progress != 100) + { + kWarning() << "Recieved finished signal while progress is still:" << progress() + << "Url:" << url(); + } + m_progress = 0; +} + + +void WebView::mousePressEvent(QMouseEvent *event) +{ + m_page->m_pressedButtons = event->buttons(); + m_page->m_keyboardModifiers = event->modifiers(); + QWebView::mousePressEvent(event); +} + + +void WebView::mouseReleaseEvent(QMouseEvent *event) +{ + QWebView::mouseReleaseEvent(event); + if (!event->isAccepted() && (m_page->m_pressedButtons & Qt::MidButton)) + { + KUrl url(QApplication::clipboard()->text(QClipboard::Selection)); + if (!url.isEmpty() && url.isValid() && !url.scheme().isEmpty()) + { + setUrl(url); + } + } +} + + +void WebView::downloadRequested(const QNetworkRequest &request) +{ + KUrl srcUrl = request.url(); + QString path = ReKonfig::downloadDir() + QString("/") + srcUrl.fileName(); + KUrl destUrl = KUrl(path); + Application::downloadManager()->newDownload(srcUrl); +} + + +void WebView::keyPressEvent(QKeyEvent *event) +{ + if ((event->modifiers() == Qt::ControlModifier) && (event->key() == Qt::Key_Tab)) + { + emit ctrlTabPressed(); + return; + } + + if ((event->modifiers() == Qt::ControlModifier + Qt::ShiftModifier) && (event->key() == Qt::Key_Backtab)) + { + emit shiftCtrlTabPressed(); + return; + } + + QWebView::keyPressEvent(event); +} diff --git a/src/webview.h b/src/webview.h new file mode 100644 index 00000000..11f04571 --- /dev/null +++ b/src/webview.h @@ -0,0 +1,155 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2007-2008 Trolltech ASA. All rights reserved +* Copyright (C) 2008 Benjamin C. Meyer +* Copyright (C) 2008-2009 by Andrea Diamantini +* Copyright (C) 2009 by Paweł Prażak +* +* +* This program is free software; you can redistribute it +* and/or modify it under the terms of the GNU General +* Public License as published by the Free Software Foundation; +* either version 2, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* ============================================================ */ + + +#ifndef WEBVIEW_H +#define WEBVIEW_H + +// KDE Includes +#include + +// Qt Includes +#include + +// Forward Declarations +class MainWindow; +class Application; + +class KActionCollection; + +class QWebFrame; +class QAuthenticator; +class QMouseEvent; +class QNetworkProxy; +class QNetworkReply; +class QSslError; + + +class WebPage : public QWebPage +{ + Q_OBJECT + +signals: + void loadingUrl(const QUrl &url); // WARNING has to be QUrl!! + +public: + WebPage(QObject *parent = 0); + ~WebPage(); + +protected: + bool acceptNavigationRequest(QWebFrame *frame, + const QNetworkRequest &request, + NavigationType type); + + QWebPage *createWindow(QWebPage::WebWindowType type); + QObject *createPlugin(const QString &classId, + const QUrl &url, + const QStringList ¶mNames, + const QStringList ¶mValues); + +private slots: + void handleUnsupportedContent(QNetworkReply *reply); + +private: + friend class WebView; + + // set the webview mousepressedevent + Qt::KeyboardModifiers m_keyboardModifiers; + Qt::MouseButtons m_pressedButtons; + bool m_openInNewTab; + KUrl m_loadingUrl; +}; + + +// ---------------------------------------------------------------------------------------------------- + +// Qt Includes +#include + + +class WebView : public QWebView +{ + Q_OBJECT + +public: + WebView(QWidget *parent = 0); + + KActionCollection* webActions(); + + // inline + WebPage *webPage() const + { + return m_page; + } + KUrl url() const + { + return KUrl(QWebView::url()); + } + QString lastStatusBarText() const + { + return m_statusBarText; + } + int progress() const + { + return m_progress; + } + +signals: + // switching tabs + void ctrlTabPressed(); + void shiftCtrlTabPressed(); + +protected: + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void contextMenuEvent(QContextMenuEvent *event); + void wheelEvent(QWheelEvent *event); + + /** + * Filters (SHIFT + ) CTRL + TAB events and emit (shift)ctrlTabPressed() + * to make switch tab + */ + void keyPressEvent(QKeyEvent *event); + +private slots: + void setProgress(int progress) + { + m_progress = progress; + } + void loadFinished(); + void setStatusBarText(const QString &string) + { + m_statusBarText = string; + } + void downloadRequested(const QNetworkRequest &request); + void openLinkInNewTab(); + +private: + static KActionCollection* s_webActionCollection; + + WebPage *m_page; + + int m_progress; + QString m_statusBarText; +}; + +#endif -- cgit v1.2.1