From b23ed87b872c6c773210b7e8ff6456d9ce2f407c Mon Sep 17 00:00:00 2001 From: nowrep Date: Wed, 2 Mar 2011 16:57:41 +0100 Subject: [PATCH] Initial commit --- .gitignore | 8 + AUTHORS | 1 + BUILDING | 16 + COPYRIGHT | 159 + GPLv3 | 675 +++++ README | 9 + .../default/profiles/default/background.png | Bin 0 -> 16841 bytes .../default/profiles/default/browsedata.db | Bin 0 -> 14336 bytes bin/data/default/profiles/profiles.ini | 2 + bin/locale/cs_CZ.qm | Bin 0 -> 48232 bytes bin/locale/qt_cs.qm | Bin 0 -> 242190 bytes bin/locale/qt_sk.qm | Bin 0 -> 76963 bytes bin/locale/sk_SK.qm | Bin 0 -> 48768 bytes plugins/TestPlugin/.gitignore | 1 + plugins/TestPlugin/cs_CZ.ts | 37 + plugins/TestPlugin/sk_SK.ts | 37 + plugins/TestPlugin/src/TestPlugin.pro | 21 + plugins/TestPlugin/src/cs_CZ.qm | Bin 0 -> 657 bytes plugins/TestPlugin/src/data.qrc | 7 + plugins/TestPlugin/src/sk_SK.qm | Bin 0 -> 659 bytes plugins/TestPlugin/src/testplugin.cpp | 68 + plugins/TestPlugin/src/testplugin.h | 47 + src/3rdparty/ecwin7.cpp | 96 + src/3rdparty/ecwin7.h | 141 + src/3rdparty/lineedit.cpp | 214 ++ src/3rdparty/lineedit.h | 107 + src/3rdparty/qtlocalpeer.cpp | 203 ++ src/3rdparty/qtlocalpeer.h | 81 + src/3rdparty/qtlockedfile.cpp | 199 ++ src/3rdparty/qtlockedfile.h | 101 + src/3rdparty/qtlockedfile_unix.cpp | 121 + src/3rdparty/qtlockedfile_win.cpp | 213 ++ src/3rdparty/qtsingleapplication.cpp | 352 +++ src/3rdparty/qtsingleapplication.h | 105 + src/3rdparty/qtsingleapplication.pri | 15 + src/3rdparty/qtwin.cpp | 225 ++ src/3rdparty/qtwin.h | 37 + src/3rdparty/squeezelabel.cpp | 19 + src/3rdparty/squeezelabel.h | 52 + src/QupZilla.pro | 168 ++ src/app/appui.cpp | 294 ++ src/app/autosaver.cpp | 17 + src/app/autosaver.h | 37 + src/app/commandlineoptions.cpp | 100 + src/app/commandlineoptions.h | 31 + src/app/mainapplication.cpp | 555 ++++ src/app/mainapplication.h | 108 + src/app/qupzilla.cpp | 703 +++++ src/app/qupzilla.h | 234 ++ src/appicon.rc | 32 + src/autofill/autofillmodel.cpp | 183 ++ src/autofill/autofillmodel.h | 44 + src/autofill/autofillwidget.cpp | 46 + src/autofill/autofillwidget.h | 30 + src/autofill/autofillwidget.ui | 126 + src/bookmarks/bookmarksmanager.cpp | 343 +++ src/bookmarks/bookmarksmanager.h | 53 + src/bookmarks/bookmarksmanager.ui | 81 + src/bookmarks/bookmarksmodel.cpp | 150 + src/bookmarks/bookmarksmodel.h | 44 + src/bookmarks/bookmarkstoolbar.cpp | 125 + src/bookmarks/bookmarkstoolbar.h | 38 + src/bookmarks/bookmarkswidget.cpp | 63 + src/bookmarks/bookmarkswidget.h | 48 + src/bookmarks/bookmarkswidget.ui | 142 + src/cookies/cookiejar.cpp | 112 + src/cookies/cookiejar.h | 43 + src/cookies/cookiemanager.cpp | 171 ++ src/cookies/cookiemanager.h | 41 + src/cookies/cookiemanager.ui | 250 ++ src/data/html.qrc | 6 + src/data/html/errorPage.html | 73 + src/data/html/info.png | Bin 0 -> 2536 bytes src/data/icons.qrc | 68 + src/data/icons/faenza/back.png | Bin 0 -> 533 bytes src/data/icons/faenza/close.png | Bin 0 -> 693 bytes src/data/icons/faenza/forward.png | Bin 0 -> 546 bytes src/data/icons/faenza/home.png | Bin 0 -> 562 bytes src/data/icons/faenza/reload.png | Bin 0 -> 773 bytes src/data/icons/faenza/settings.png | Bin 0 -> 584 bytes src/data/icons/faenza/source.png | Bin 0 -> 601 bytes src/data/icons/faenza/stop.png | Bin 0 -> 1266 bytes src/data/icons/lineedit.psd | Bin 0 -> 31410 bytes src/data/icons/locationbar/accept.png | Bin 0 -> 1202 bytes src/data/icons/locationbar/arrow-down.gif | Bin 0 -> 54 bytes src/data/icons/locationbar/gotoaddress.png | Bin 0 -> 612 bytes src/data/icons/locationbar/lineedit.png | Bin 0 -> 569 bytes .../icons/locationbar/privatebrowsing.png | Bin 0 -> 963 bytes src/data/icons/locationbar/safeline.png | Bin 0 -> 997 bytes src/data/icons/locationbar/search.png | Bin 0 -> 808 bytes src/data/icons/locationbar/searchchoose.png | Bin 0 -> 655 bytes src/data/icons/locationbar/secure.gif | Bin 0 -> 249 bytes src/data/icons/locationbar/star.png | Bin 0 -> 3058 bytes src/data/icons/locationbar/starg.png | Bin 0 -> 3065 bytes src/data/icons/locationbar/unknownpage.png | Bin 0 -> 819 bytes src/data/icons/locationbar/warning.png | Bin 0 -> 1418 bytes src/data/icons/menu/book_open_mark.png | Bin 0 -> 658 bytes src/data/icons/menu/bookmark_add.png | Bin 0 -> 767 bytes src/data/icons/menu/bug_magnify.png | Bin 0 -> 852 bytes src/data/icons/menu/circle.png | Bin 0 -> 459 bytes src/data/icons/menu/copy.png | Bin 0 -> 606 bytes src/data/icons/menu/csfd.png | Bin 0 -> 2977 bytes src/data/icons/menu/cz_seznam.png | Bin 0 -> 697 bytes src/data/icons/menu/flash.png | Bin 0 -> 623 bytes src/data/icons/menu/google.png | Bin 0 -> 1094 bytes src/data/icons/menu/history.png | Bin 0 -> 862 bytes src/data/icons/menu/history_entry.png | Bin 0 -> 878 bytes src/data/icons/menu/icon-wikipedia.png | Bin 0 -> 247 bytes src/data/icons/menu/popup.png | Bin 0 -> 580 bytes src/data/icons/menu/qt.png | Bin 0 -> 744 bytes src/data/icons/menu/rss.png | Bin 0 -> 735 bytes src/data/icons/menu/youtube.png | Bin 0 -> 436 bytes src/data/icons/navigation/close.png | Bin 0 -> 616 bytes src/data/icons/navigation/home.png | Bin 0 -> 377 bytes src/data/icons/navigation/home_.png | Bin 0 -> 515 bytes src/data/icons/navigation/newtab.png | Bin 0 -> 882 bytes src/data/icons/navigation/reload.png | Bin 0 -> 802 bytes src/data/icons/navigation/search.gif | Bin 0 -> 748 bytes src/data/icons/navigation/search.png | Bin 0 -> 696 bytes src/data/icons/navigation/stop.png | Bin 0 -> 253 bytes src/data/icons/navigation/vpred.png | Bin 0 -> 600 bytes src/data/icons/navigation/windowlose.png | Bin 0 -> 1314 bytes src/data/icons/navigation/zpet.png | Bin 0 -> 600 bytes src/data/icons/other/about.png | Bin 0 -> 42603 bytes src/data/icons/other/bigstar.png | Bin 0 -> 4298 bytes src/data/icons/other/feed.png | Bin 0 -> 727 bytes src/data/icons/other/flash.png | Bin 0 -> 3373 bytes src/data/icons/other/flashstart.png | Bin 0 -> 3220 bytes src/data/icons/other/keys.png | Bin 0 -> 4854 bytes src/data/icons/other/plus.png | Bin 0 -> 434 bytes src/data/icons/other/progress.gif | Bin 0 -> 701 bytes src/data/icons/other/unsortedbookmarks.png | Bin 0 -> 481 bytes .../preferences/applications-accessories.png | Bin 0 -> 2441 bytes .../preferences/applications-internet.png | Bin 0 -> 1979 bytes .../icons/preferences/applications-system.png | Bin 0 -> 2073 bytes .../preferences/applications-webbrowsers.png | Bin 0 -> 2376 bytes src/data/icons/preferences/contact-new.png | Bin 0 -> 1645 bytes src/data/icons/preferences/extension.png | Bin 0 -> 1387 bytes .../preferences/gnome-window-manager.png | Bin 0 -> 754 bytes src/data/icons/preferences/history_entry.png | Bin 0 -> 1924 bytes .../icons/preferences/preferences-desktop.png | Bin 0 -> 1582 bytes src/data/icons/preferences/stock_inbox.png | Bin 0 -> 1297 bytes src/data/icons/qupzilla.png | Bin 0 -> 5236 bytes src/data/icons/qupzillaupdate.png | Bin 0 -> 5300 bytes src/data/icons/transp.png | Bin 0 -> 141 bytes src/data/qupzilla.png | Bin 0 -> 7874 bytes src/downloads/downloaditem.cpp | 255 ++ src/downloads/downloaditem.h | 76 + src/downloads/downloaditem.ui | 113 + src/downloads/downloadmanager.cpp | 247 ++ src/downloads/downloadmanager.h | 74 + src/downloads/downloadmanager.ui | 128 + src/downloads/downloadoptionsdialog.cpp | 14 + src/downloads/downloadoptionsdialog.h | 22 + src/downloads/downloadoptionsdialog.ui | 205 ++ src/history/historymanager.cpp | 192 ++ src/history/historymanager.h | 45 + src/history/historymanager.ui | 96 + src/history/historymodel.cpp | 102 + src/history/historymodel.h | 38 + src/icon.ico | Bin 0 -> 9662 bytes src/main.cpp | 25 + src/navigation/locationbar.cpp | 375 +++ src/navigation/locationbar.h | 75 + src/navigation/locationcompleter.cpp | 140 + src/navigation/locationcompleter.h | 35 + src/navigation/websearchbar.cpp | 115 + src/navigation/websearchbar.h | 38 + src/network/networkmanager.cpp | 171 ++ src/network/networkmanager.h | 49 + src/network/networkmanagerproxy.cpp | 46 + src/network/networkmanagerproxy.h | 37 + src/other/aboutdialog.cpp | 59 + src/other/aboutdialog.h | 34 + src/other/aboutdialog.ui | 132 + src/other/clearprivatedata.cpp | 74 + src/other/clearprivatedata.h | 45 + src/other/sourceviewer.cpp | 25 + src/other/sourceviewer.h | 32 + src/other/updater.cpp | 79 + src/other/updater.h | 40 + src/plugins/clicktoflash.cpp | 139 + src/plugins/clicktoflash.h | 67 + src/plugins/plugininterface.h | 44 + src/plugins/pluginproxy.cpp | 70 + src/plugins/pluginproxy.h | 34 + src/plugins/plugins.cpp | 68 + src/plugins/plugins.h | 41 + src/plugins/webpluginfactory.cpp | 53 + src/plugins/webpluginfactory.h | 25 + src/preferences/autofillmanager.cpp | 108 + src/preferences/autofillmanager.h | 45 + src/preferences/autofillmanager.ui | 146 + src/preferences/pluginslist.cpp | 175 ++ src/preferences/pluginslist.h | 42 + src/preferences/pluginslist.ui | 185 ++ src/preferences/preferences.cpp | 470 +++ src/preferences/preferences.h | 69 + src/preferences/preferences.ui | 1523 ++++++++++ src/rss/rssmanager.cpp | 305 ++ src/rss/rssmanager.h | 53 + src/rss/rssmanager.ui | 158 + src/sidebar/sidebar.cpp | 6 + src/sidebar/sidebar.h | 18 + src/webview/searchtoolbar.cpp | 150 + src/webview/searchtoolbar.h | 50 + src/webview/siteinfo.cpp | 66 + src/webview/siteinfo.h | 28 + src/webview/siteinfo.ui | 159 + src/webview/siteinfowidget.cpp | 67 + src/webview/siteinfowidget.h | 27 + src/webview/siteinfowidget.ui | 108 + src/webview/tabbar.cpp | 122 + src/webview/tabbar.h | 55 + src/webview/tabwidget.cpp | 328 +++ src/webview/tabwidget.h | 74 + src/webview/webpage.cpp | 172 ++ src/webview/webpage.h | 47 + src/webview/webtab.cpp | 33 + src/webview/webtab.h | 27 + src/webview/webview.cpp | 679 +++++ src/webview/webview.h | 122 + translations/cs_CZ.ts | 2574 +++++++++++++++++ translations/sk_SK.ts | 2572 ++++++++++++++++ windows/FileAssociation.nsh | 190 ++ windows/install.ico | Bin 0 -> 9662 bytes windows/installer.nsi | 169 ++ windows/uninstall.ico | Bin 0 -> 9662 bytes windows/welcome.bmp | Bin 0 -> 154542 bytes 229 files changed, 22824 insertions(+) create mode 100644 .gitignore create mode 100644 AUTHORS create mode 100644 BUILDING create mode 100644 COPYRIGHT create mode 100644 GPLv3 create mode 100644 README create mode 100644 bin/data/default/profiles/default/background.png create mode 100644 bin/data/default/profiles/default/browsedata.db create mode 100644 bin/data/default/profiles/profiles.ini create mode 100644 bin/locale/cs_CZ.qm create mode 100644 bin/locale/qt_cs.qm create mode 100644 bin/locale/qt_sk.qm create mode 100644 bin/locale/sk_SK.qm create mode 100644 plugins/TestPlugin/.gitignore create mode 100644 plugins/TestPlugin/cs_CZ.ts create mode 100644 plugins/TestPlugin/sk_SK.ts create mode 100644 plugins/TestPlugin/src/TestPlugin.pro create mode 100644 plugins/TestPlugin/src/cs_CZ.qm create mode 100644 plugins/TestPlugin/src/data.qrc create mode 100644 plugins/TestPlugin/src/sk_SK.qm create mode 100644 plugins/TestPlugin/src/testplugin.cpp create mode 100644 plugins/TestPlugin/src/testplugin.h create mode 100644 src/3rdparty/ecwin7.cpp create mode 100644 src/3rdparty/ecwin7.h create mode 100644 src/3rdparty/lineedit.cpp create mode 100644 src/3rdparty/lineedit.h create mode 100644 src/3rdparty/qtlocalpeer.cpp create mode 100644 src/3rdparty/qtlocalpeer.h create mode 100644 src/3rdparty/qtlockedfile.cpp create mode 100644 src/3rdparty/qtlockedfile.h create mode 100644 src/3rdparty/qtlockedfile_unix.cpp create mode 100644 src/3rdparty/qtlockedfile_win.cpp create mode 100644 src/3rdparty/qtsingleapplication.cpp create mode 100644 src/3rdparty/qtsingleapplication.h create mode 100644 src/3rdparty/qtsingleapplication.pri create mode 100644 src/3rdparty/qtwin.cpp create mode 100644 src/3rdparty/qtwin.h create mode 100644 src/3rdparty/squeezelabel.cpp create mode 100644 src/3rdparty/squeezelabel.h create mode 100644 src/QupZilla.pro create mode 100644 src/app/appui.cpp create mode 100644 src/app/autosaver.cpp create mode 100644 src/app/autosaver.h create mode 100644 src/app/commandlineoptions.cpp create mode 100644 src/app/commandlineoptions.h create mode 100644 src/app/mainapplication.cpp create mode 100644 src/app/mainapplication.h create mode 100644 src/app/qupzilla.cpp create mode 100644 src/app/qupzilla.h create mode 100644 src/appicon.rc create mode 100644 src/autofill/autofillmodel.cpp create mode 100644 src/autofill/autofillmodel.h create mode 100644 src/autofill/autofillwidget.cpp create mode 100644 src/autofill/autofillwidget.h create mode 100644 src/autofill/autofillwidget.ui create mode 100644 src/bookmarks/bookmarksmanager.cpp create mode 100644 src/bookmarks/bookmarksmanager.h create mode 100644 src/bookmarks/bookmarksmanager.ui create mode 100644 src/bookmarks/bookmarksmodel.cpp create mode 100644 src/bookmarks/bookmarksmodel.h create mode 100644 src/bookmarks/bookmarkstoolbar.cpp create mode 100644 src/bookmarks/bookmarkstoolbar.h create mode 100644 src/bookmarks/bookmarkswidget.cpp create mode 100644 src/bookmarks/bookmarkswidget.h create mode 100644 src/bookmarks/bookmarkswidget.ui create mode 100644 src/cookies/cookiejar.cpp create mode 100644 src/cookies/cookiejar.h create mode 100644 src/cookies/cookiemanager.cpp create mode 100644 src/cookies/cookiemanager.h create mode 100644 src/cookies/cookiemanager.ui create mode 100644 src/data/html.qrc create mode 100644 src/data/html/errorPage.html create mode 100644 src/data/html/info.png create mode 100644 src/data/icons.qrc create mode 100644 src/data/icons/faenza/back.png create mode 100644 src/data/icons/faenza/close.png create mode 100644 src/data/icons/faenza/forward.png create mode 100644 src/data/icons/faenza/home.png create mode 100644 src/data/icons/faenza/reload.png create mode 100644 src/data/icons/faenza/settings.png create mode 100644 src/data/icons/faenza/source.png create mode 100644 src/data/icons/faenza/stop.png create mode 100644 src/data/icons/lineedit.psd create mode 100644 src/data/icons/locationbar/accept.png create mode 100644 src/data/icons/locationbar/arrow-down.gif create mode 100644 src/data/icons/locationbar/gotoaddress.png create mode 100644 src/data/icons/locationbar/lineedit.png create mode 100644 src/data/icons/locationbar/privatebrowsing.png create mode 100644 src/data/icons/locationbar/safeline.png create mode 100644 src/data/icons/locationbar/search.png create mode 100644 src/data/icons/locationbar/searchchoose.png create mode 100644 src/data/icons/locationbar/secure.gif create mode 100644 src/data/icons/locationbar/star.png create mode 100644 src/data/icons/locationbar/starg.png create mode 100644 src/data/icons/locationbar/unknownpage.png create mode 100644 src/data/icons/locationbar/warning.png create mode 100644 src/data/icons/menu/book_open_mark.png create mode 100644 src/data/icons/menu/bookmark_add.png create mode 100644 src/data/icons/menu/bug_magnify.png create mode 100644 src/data/icons/menu/circle.png create mode 100644 src/data/icons/menu/copy.png create mode 100644 src/data/icons/menu/csfd.png create mode 100644 src/data/icons/menu/cz_seznam.png create mode 100644 src/data/icons/menu/flash.png create mode 100644 src/data/icons/menu/google.png create mode 100644 src/data/icons/menu/history.png create mode 100644 src/data/icons/menu/history_entry.png create mode 100644 src/data/icons/menu/icon-wikipedia.png create mode 100644 src/data/icons/menu/popup.png create mode 100644 src/data/icons/menu/qt.png create mode 100644 src/data/icons/menu/rss.png create mode 100644 src/data/icons/menu/youtube.png create mode 100644 src/data/icons/navigation/close.png create mode 100644 src/data/icons/navigation/home.png create mode 100644 src/data/icons/navigation/home_.png create mode 100644 src/data/icons/navigation/newtab.png create mode 100644 src/data/icons/navigation/reload.png create mode 100644 src/data/icons/navigation/search.gif create mode 100644 src/data/icons/navigation/search.png create mode 100644 src/data/icons/navigation/stop.png create mode 100644 src/data/icons/navigation/vpred.png create mode 100644 src/data/icons/navigation/windowlose.png create mode 100644 src/data/icons/navigation/zpet.png create mode 100644 src/data/icons/other/about.png create mode 100644 src/data/icons/other/bigstar.png create mode 100644 src/data/icons/other/feed.png create mode 100644 src/data/icons/other/flash.png create mode 100644 src/data/icons/other/flashstart.png create mode 100644 src/data/icons/other/keys.png create mode 100644 src/data/icons/other/plus.png create mode 100644 src/data/icons/other/progress.gif create mode 100644 src/data/icons/other/unsortedbookmarks.png create mode 100644 src/data/icons/preferences/applications-accessories.png create mode 100644 src/data/icons/preferences/applications-internet.png create mode 100644 src/data/icons/preferences/applications-system.png create mode 100644 src/data/icons/preferences/applications-webbrowsers.png create mode 100644 src/data/icons/preferences/contact-new.png create mode 100644 src/data/icons/preferences/extension.png create mode 100644 src/data/icons/preferences/gnome-window-manager.png create mode 100644 src/data/icons/preferences/history_entry.png create mode 100644 src/data/icons/preferences/preferences-desktop.png create mode 100644 src/data/icons/preferences/stock_inbox.png create mode 100644 src/data/icons/qupzilla.png create mode 100644 src/data/icons/qupzillaupdate.png create mode 100644 src/data/icons/transp.png create mode 100644 src/data/qupzilla.png create mode 100644 src/downloads/downloaditem.cpp create mode 100644 src/downloads/downloaditem.h create mode 100644 src/downloads/downloaditem.ui create mode 100644 src/downloads/downloadmanager.cpp create mode 100644 src/downloads/downloadmanager.h create mode 100644 src/downloads/downloadmanager.ui create mode 100644 src/downloads/downloadoptionsdialog.cpp create mode 100644 src/downloads/downloadoptionsdialog.h create mode 100644 src/downloads/downloadoptionsdialog.ui create mode 100644 src/history/historymanager.cpp create mode 100644 src/history/historymanager.h create mode 100644 src/history/historymanager.ui create mode 100644 src/history/historymodel.cpp create mode 100644 src/history/historymodel.h create mode 100644 src/icon.ico create mode 100644 src/main.cpp create mode 100644 src/navigation/locationbar.cpp create mode 100644 src/navigation/locationbar.h create mode 100644 src/navigation/locationcompleter.cpp create mode 100644 src/navigation/locationcompleter.h create mode 100644 src/navigation/websearchbar.cpp create mode 100644 src/navigation/websearchbar.h create mode 100644 src/network/networkmanager.cpp create mode 100644 src/network/networkmanager.h create mode 100644 src/network/networkmanagerproxy.cpp create mode 100644 src/network/networkmanagerproxy.h create mode 100644 src/other/aboutdialog.cpp create mode 100644 src/other/aboutdialog.h create mode 100644 src/other/aboutdialog.ui create mode 100644 src/other/clearprivatedata.cpp create mode 100644 src/other/clearprivatedata.h create mode 100644 src/other/sourceviewer.cpp create mode 100644 src/other/sourceviewer.h create mode 100644 src/other/updater.cpp create mode 100644 src/other/updater.h create mode 100644 src/plugins/clicktoflash.cpp create mode 100644 src/plugins/clicktoflash.h create mode 100644 src/plugins/plugininterface.h create mode 100644 src/plugins/pluginproxy.cpp create mode 100644 src/plugins/pluginproxy.h create mode 100644 src/plugins/plugins.cpp create mode 100644 src/plugins/plugins.h create mode 100644 src/plugins/webpluginfactory.cpp create mode 100644 src/plugins/webpluginfactory.h create mode 100644 src/preferences/autofillmanager.cpp create mode 100644 src/preferences/autofillmanager.h create mode 100644 src/preferences/autofillmanager.ui create mode 100644 src/preferences/pluginslist.cpp create mode 100644 src/preferences/pluginslist.h create mode 100644 src/preferences/pluginslist.ui create mode 100644 src/preferences/preferences.cpp create mode 100644 src/preferences/preferences.h create mode 100644 src/preferences/preferences.ui create mode 100644 src/rss/rssmanager.cpp create mode 100644 src/rss/rssmanager.h create mode 100644 src/rss/rssmanager.ui create mode 100644 src/sidebar/sidebar.cpp create mode 100644 src/sidebar/sidebar.h create mode 100644 src/webview/searchtoolbar.cpp create mode 100644 src/webview/searchtoolbar.h create mode 100644 src/webview/siteinfo.cpp create mode 100644 src/webview/siteinfo.h create mode 100644 src/webview/siteinfo.ui create mode 100644 src/webview/siteinfowidget.cpp create mode 100644 src/webview/siteinfowidget.h create mode 100644 src/webview/siteinfowidget.ui create mode 100644 src/webview/tabbar.cpp create mode 100644 src/webview/tabbar.h create mode 100644 src/webview/tabwidget.cpp create mode 100644 src/webview/tabwidget.h create mode 100644 src/webview/webpage.cpp create mode 100644 src/webview/webpage.h create mode 100644 src/webview/webtab.cpp create mode 100644 src/webview/webtab.h create mode 100644 src/webview/webview.cpp create mode 100644 src/webview/webview.h create mode 100644 translations/cs_CZ.ts create mode 100644 translations/sk_SK.ts create mode 100644 windows/FileAssociation.nsh create mode 100644 windows/install.ico create mode 100644 windows/installer.nsi create mode 100644 windows/uninstall.ico create mode 100644 windows/welcome.bmp diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..a370d3289 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +build +DEBIAN +tools +src0.9.4 +src0.9.5 +src0.9.6 +*.deb +*.pro.user diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 000000000..27d51ef84 --- /dev/null +++ b/AUTHORS @@ -0,0 +1 @@ +nowrep diff --git a/BUILDING b/BUILDING new file mode 100644 index 000000000..0a328d196 --- /dev/null +++ b/BUILDING @@ -0,0 +1,16 @@ +####################### +## Building QupZilla ## +####################### + +in src/ folder: + qmake QupZilla.pro + make (unix) or nmake (Windows) +compiled binary is moved to bin folder + +Windows: + execute binary from bin folder + +Unix + move all folders (data, locale, plugins) to /usr/share/qupzilla + move binary to /usr/bin + execute binary here diff --git a/COPYRIGHT b/COPYRIGHT new file mode 100644 index 000000000..d7e8ee6db --- /dev/null +++ b/COPYRIGHT @@ -0,0 +1,159 @@ +********************************************************************* +********************************************************************* +The QupZilla application itself is licensed under following licension +if it is not explicitly defined in source file or in exceptions bellow +********************************************************************* +********************************************************************* + +QupZilla - very fast web browser based on WebKit +Copyright (C) 2010-2011 nowrep + +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 3 of the License, 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. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +****************************************************************** +****************************************************************** +Some files in 3rdparty/ folder are under different licenses + +----------------------------------------------------------- + lineedit.h, lineedit.cpp, squeezelabel.h, squeezelabel.cpp: +----------------------------------------------------------- +* Copyright (c) 2008 - 2009, Benjamin C. Meyer +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* 3. Neither the name of the Benjamin Meyer nor the names of its contributors +* may be used to endorse or promote products derived from this software +* without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +* SUCH DAMAGE. + +----------------------------------------------------------------------------- + qtlocalpeer.h, qtlocalpeer.cpp, qtsingleapplication.h, qtsingleapplication.cpp, + qtlockedfile.h, qtlockedfile_unix.cpp, qtlockedfile_win.cpp: +----------------------------------------------------------------------------- +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Solutions Commercial License Agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.1, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** Please note Third Party Software included with Qt Solutions may impose +** additional restrictions and it is the user's responsibility to ensure +** that they have met the licensing requirements of the GPL, LGPL, or Qt +** Solutions Commercial license and the relevant license of the Third +** Party Software they are using. +** +** If you are unsure which license is appropriate for your use, please +** contact Nokia at qt-info@nokia.com. +** +--------------------------------------------------------------------------- + Files clicktoflash.cpp and clicktoflash.h: +--------------------------------------------------------------------------- +/* ============================================================ +* +* Copyright (C) 2009 by Benjamin C. Meyer +* Copyright (C) 2010 by Matthieu Gicquel +* +* 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 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* 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. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* ============================================================ */ + +----------------------------------------------------------------------------- + Files ecwin7.cpp and ecwin7.h: +--------------------------------------------------------------------------- +/* EcWin7 - Support library for integrating Windows 7 taskbar features + * into any Qt application + * Copyright (C) 2010 Emanuele Colombo + * + * 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 of the License, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +----------------------------------------------------------------------------- + Files qtwin.cpp and qtwin.h are used from + http://labs.qt.nokia.com/2009/09/15/using-blur-behind-on-windows/ +----------------------------------------------------------------------------- + In application are used also some icons from Faenza icon set, which are + licensed under the GNU/GPL license. + More info at http://tiheum.deviantart.com/art/Faenza-Icons-173323228 +----------------------------------------------------------------------------- diff --git a/GPLv3 b/GPLv3 new file mode 100644 index 000000000..10926e87f --- /dev/null +++ b/GPLv3 @@ -0,0 +1,675 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 3 of the License, 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. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. + diff --git a/README b/README new file mode 100644 index 000000000..b032ef0f3 --- /dev/null +++ b/README @@ -0,0 +1,9 @@ +################################# +## QUPZILLA - WEB BROWSER ## +################################# +Version: 0.9.7 + +QupZilla is new and very fast browser based on WebKit core http://www.webkit.org +Written in Qt Framework http://qt.nokia.com + +More informations at http://www.qupzilla.ic.cz (Czech only) diff --git a/bin/data/default/profiles/default/background.png b/bin/data/default/profiles/default/background.png new file mode 100644 index 0000000000000000000000000000000000000000..5678d984aab12a79e0ecf7d7cb88eacc501ce699 GIT binary patch literal 16841 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
    yNN9p_f zwfwABlz8m@$lE2aJHty^#x!m*o~*Pfekx=NcPuHACJJP1jf->70ISV{36*RcF%Q81 zqU#HM5g9DaCb24l=p*VdPQ^Uk){prRF=Ou`rUrRIHy_XFx`=nkCe(LU7@H_>%fpXB zc$URGWUgJnJ6ss>cQ47x2bx0EWE2Vj=AS!eSm{R_GiOI0ogUyPOE%hYTW+6_6DQppf-dAs@BCS}0Qf5)l`Kpz9?Ue* z>v;o}HykCrN96%=t;vHatD@5^gf8`p&wp!>$A)Rt0z%RBwx45Ti48&W zy;L}i(Q^zeDQNMGziCl}%a{GJV9b5;30y;;5iXG~eR{qgFHU1t7H z-yS3Wf{u5Pk*E=B2i4;x&s2nad@Ic>!pq*qI5c6KkvhjEu0~%x2emn^8^jII}BJf#s%M5QzUvyn;M4pfpY;5OfD=2N!Z;C(o5p1w2C zdePyFF4y`wC9yqdRh$T_UjE~_1jcZFU1TfJ-0SeaTZo? zm6-^)H`@*GQaqbkXj~$DE2=CLRLt|k$klr-LHsF7YAd-pp1zIeU$+ag>$n;%aoMvZ zWFxK*gYbMKwAJiC*sV39VP!^JCSm=K7QC?JswJTJ#?t6-qaNF9Ft87~bAWF`V)QLJ zoXadTxdxm>-3g-TFDAbA!Q@}K1ufp18sVN3)E4s<#R8a0B=d8-kbxH@j28 zUBbKgEQPxxbb9kDPfc?eH7vEb%xM|d@$iwd%fPYspM}c+pb=Q*)*Ki8CGiav+#sJv z0(*k`CLdLu@{tYI`N!F7#7?PiR=dl)^N2!t?#u*WePO<;&fXfA>Vs336gIozY32Fg zc0BS(3$tQ~2H!`}sEyjPeG=-weJKMBLbmf zQyb^I$htfgi9}%+TR5~phUg@&o^t;hF5E^aehgJ+^#z@;n&^HUR9tCz;EcgZf}SmwB=m; z$C>tWZ-wB(^}$lZu>y2lql(>K9ld;3L2psuaF0zv_L*pj7}DHSb}Y>cR5g5bYkKDn z?P*=L3p#UGu?J$Fb#MT3^qP;d0a8#}6t)PEy_`J(?xYT=X%}i8Yme=6ndzzp<2cT5 zG_MLQyma=o1sK!oZL@oeWnj?S{Xz-n)XnwO;K_Kvwt9_*x7F^@f=b4Egm9FH*U;t%kv_O)bh#zX7xaOO#ml7xLYU3`TsS18U1E!WmxZBC#4mp& zU-TPo)e;AKg>noP#S@)N(_q}1_e*1H>!H_quXncyMbF$WmeoIe)#fZWyj`pI+V(Xr z2JQBHEf%Zy>Hl5kN=1ql^HhYICPhy`@2yI&NZbNb=fczD1yf>cD|heuH@b>!k8#s6 zM9+@r1gGpc_ZuG-1JeGU9^i*u3v6>gYog*#83jOv{9GpOsadAVEXR!@}W0nDQNMF*_^7eLp*X3DdjY;6&+ zoiRmpVUtn~d@?geA`Dn$U18ZTXW*^kiWJN+ze+cNj&8bsQpY0rYK_)&XPzH?MJUh* zopV%mN|K0Bf{iW^ek_32_iP=GTeNW&TA(RzdJ9; z%szar{Q-Wu(B$m-q|4u6&=IAbx>G^^OYp|qiTS+LdZ&H|Ok6r11=N&u>1_2Hr39Cz ziO0sXKL=}OksUJZo)bVl=PLnWVV79C)$58A#QW75kBFAsud5*;8z=2{ITQ2hm)DoM qHn<0r;uk!?g3Cc|H2${pHzxPZ(afgGod2uo4Q`w2R@{8>D6#qYkP-00^W@0QP^n{6QTEq)7nwf130`8wjLJ0QP^n{6QTEq)CA8f0ldCaG%Ks z96$gFj0b@gHoL&&AM9GzL0hvAoOQj^tM|IE4Aa#1UAOyidHJx{J)_+vqq)>PTP~sO zVtj;ZS>~>0|9KGP|M9roFbg1%27xIm3eW%3Ko6QgAaw$;|5LvNFaaQt8Ub4WXVE!> z&e?hPBX^s7liE~3_+JoE5HIF(=&h^mn);q)wshMGPHaVy%c_jk^5Zp`{N>C44ogN0 zS8J;LOjhtqrMg~LcJK>%r-(aRTgR%rtrkvMK`fP!cf${=^;~O@+;_g#PnvqyHLQ*^ z#Ka}UhB%BCc69qlxBJo_;si0DM_wbL9mdNHRl!nAb2UFe5szA4S96?W%Weg3G0`IK zk=|hWTNy!|okiZPKLy)y$Sa!=CI&2Nt(L8m21Pbn)-_x+!0Mr_AkNJpukHtqZuO%E zpAd=6Sn6N9zf#&NE0yPEMY_APQXmVnO&qrrX#QupZw&W?e82$&fWZGj;5xU6qF;>Q zQ6Rd;EewqGi6Tq$KaV~z>?-?}o98~EpQHoFxDeQw=EYk%bZB&1`pLfGxR!m|u+5-# zHOLQ*TD9JgNfv)1Z{vX%-l&B=iT?1HH!FqO6fe%_&_O()N>frWY$gbqh~+!PqxF** zjpt4C0xO7YGrixBzavQcqOvgz59hi zL*T_i9y$K%!Wv=IFDJs@L}G!VweLfh7+R#DAfStUHzA0RNd1#b#FfMHCn*psizbI^ zT^JV^jg5`#?@aRIojG*u%SW5~sb9cF-RXo_WbS)Wk;oL54|Hj8|35BU9p(W9E}sDG O|I23%ia=mo2>b?p`opsT literal 0 HcmV?d00001 diff --git a/bin/data/default/profiles/profiles.ini b/bin/data/default/profiles/profiles.ini new file mode 100644 index 000000000..2baff5758 --- /dev/null +++ b/bin/data/default/profiles/profiles.ini @@ -0,0 +1,2 @@ +[Profiles] +startProfile="default" diff --git a/bin/locale/cs_CZ.qm b/bin/locale/cs_CZ.qm new file mode 100644 index 0000000000000000000000000000000000000000..f494f1ca0e637f245d19c44872879d86b02b597e GIT binary patch literal 48232 zcmchA3w&Hvo&QNQlgH%MM`%ka?IrY0o1{-MErm2~X-glmNvRYBCYebxWHK|InIuhF zL3t=@dB{URk>xESvI?s!Dy|^@ReUR=tD;6wffaE<*3}jGf4}E<-}g@1F6+XlxtYnm zzw;buF$da6K2- zmAJke*HyUQh3jfuzlQ4?rM4W4>sxSLhwI6>o`=WlmD>E0+}nPtQro_$)Ra@?YMdn3 z*3aYmxKg?KO6?m}>Z&`GI_Hp5(|#`3m1n8QOGfKTDitC@0`aoPoQ$ND@cdO_J{z<8^52@%Oj5F4%qBnh2 zsbfE@q940Xse!zTe&T$k{-I5-pFB=QAIJR{%~8?sf@T+gM%9b}-}}F+PM$v%Gt#D^FBs_kj=I`FnNFrJuoieO;aVVy{wX*QwsizoS(7uT|f> zK#xmr!&2R))V1GILm!GM)%>)|ra_m*HL4K#ic*9Bq7MG?GNrCtt}Yv@Qffn5edr4r zrEc$2_g{(eH$R{r{NgsipP?SP9Po~>RNw0TjZ*7RR^Ps-pwvCDs~dANE zy8U`JzT+5t&Zu862K=|2P&W5u;PdgXmCZZm74Y>*a&4R}*P#`1z3#hZ^YSf9rFWMt zdFn!?>aHw13(K3{!a**oXW0NrRCYb+(Em@9CY=D@pSn-3%WszJd)7=kt{3Zc)9aHKRIXR*gME{h zonH<HTYz}nDok%fZulWq}MlY!sk268_&f! zd-j&kdh{;HQE&Ni_hR0wep!Be(`2P4zq@?ym+<`gHKbz5Jxr&=>PJmA4;A zD0S}k@{UinW1cUUZ=3%GJh!R*AKL!_{Jvj)>)xv%U#aq^n)WF5;rEpPV%kMY-TJoj z@zZhr#QySU9{@cr-B8n8h@$#3hm;=22Q2wV+{6eXbbIV_!b}smRRV4bvL*S>i zk^1|9=cehAV{?;~I_~MniMx}yE{x3o?hlok{jZUgiyM?`>58=f6nx%rNo3<|)0Dd4 z=E%;;d8Iy77unVQ9i{gEEz%jsdi9h=5<4@X%jY8f&wWX$+kO`rcsItodSN90@Hpo8 zSme;Gl}b%-mFvh$k*h!Q492@Ua?6FlcYa;uwk^Hj=Wj*s+6;MGxG!?|mvCKtd*uG5 zSjQ=Ya&7&sT+g~zuDPzr{r7{8AK4K3#=a)-!7RBhKU=QFYvp?RHzVITh`$d%E7u!v zKR@4ilUzUc`^fiB|FTlXMMMbu>jA&!3~z+aHXcumbvIb~d^y13F#*Xms7T>y?_cF4}%o9aR0i==Qt5 zuT=CQxu&N@cU^|h_@-|d`oDHdw0|w&pYi@^f8l(kQeTS}p4tHZ|4#J67QjF4o6!qj zep#urqH@jN8$Gld{5bZgT(9`3Tt9wl^w8$7Dph^ATsNH?y=v06xbJMazIR6SrlBbK z+lYSTikp;bYL)AXOXPZJPV|;r9#bl|Sgt2lL~rf-iBj)c8ol$SW~J6%AN|bbm{-sF z(a)an2c@=tD*EN;v95EvqhDVOJvnbz^n0KA4fNwR(WjPDj@L$ieC02JzY+ae<)4)* zJ`w%Z8U5g+XQSiS{TTW(Ur}Fdg{geB;+W?#|2?0pIPtL#=$+f;+W4|uGi4R?o<0%! z_SY3B{q0=fv%jL{9@xv{vlSa^!8h-`q+-V(Fz!t^RqS~L{CUzNa?M>)aqeA^-^<=v zabD+>&_n-Hk)DF*m)%~G?t~s%_S=e!^R=M&aTV{m{4B`%DHYcwKd991Z&rNp2GD=) z%N5ss6?SLr<%%08<8$_;icc-M6LR`*6_0%@4LXlkd}aFWN`3U>6;E6{RjE_nUGd8o z?*Jb@CD#?xS%LdQYuf_qY3$x^iCSDSv()>vCS@8OwfwdCaKXee12@=km%ubKZ^n|5kbC zNsr_HPgnN#K#%k?<$-q;Ah&l`9{3pMfACMZUIe|dxbm`d zGmzVTmDhajFVHhHDnD}1M%bU%D{ndbI@rHMmG{*B71tkB-m`B7N-Ix9w4C(YaMy7lIBKuB|%%r2rKY;bxU-jS}&?9ZDtG+PzTIh>=tN!1#S&-*5 ztDgAJA41Art@?KPL(u=fuKLb-uKJJm^$_pBR!^OQ z@!tDt^@-OtDz)yJ>bAFHy#^Omcl-yW_0(@yzwJ;ncc2FzsJ`Q&$H6CORp0e~&}-i3tM7U=1HF7# z^~bIQU8@bb_PkpCx%d51sr&XcqC{ zr#^|lFZxRL?{5U0ZBwe>SiecBn%~7LDz1W^dNEdaCFpSWCu7SV#`7(cW9zDmun#k1 zTQlbZzsA_sd%pyFd@$B|eiHEZ$2#u^e#iVQmiXB#kRv0O{3_tyH6H7md#O@${}d~J z>AT>=Z^lOV0nYTF#x6PYdfeX}yK(O~ps(+U-8=ZQQki$f9$3E;^U1{isrR4ZmwY|; z@Is7p-_K(Y|8}QR*Hp*8c&Honx+FHf?_%hUhhopX1buhP=GdP)pg-1stETL;7eQY> zBiGBXlIxA<$n|3n){ac0%D8F#|Zxvl0D zem)qlIsFjl5wGbv?+4)jL{0jGcjNDGugRU$47t0eCO3fjPPe*G4{!^;p;_*OA$^v&z6f=Ra3F>q^+we;BM?|6S1Qu3Kx*NWt$}{bcPK z7Yr&j^M=};=dFgnep~I%i*a3jTJ7E)55jLAuRZHB;Pdvk*1qEdkjE1ru8mhg?<98C z_BMlG&iO@c@kY$6d138E+rbxA57b`!S2m$p<#oje{}py&W8M3x zcb7g`cT02%{Fv|7-TDmhto(Pm&K;6#>zG_WI=AlbPkje=CRumi`=AGBZLYijpP?TX z)zy8b`XR{M*>#V99kJ8d2kX8z271*$E!P>3)KC4^x8WCjv3}ORd64JZ>yK-%hhCdr zKWFw`um_Fx%M(R>-(BC9dja<3_4-Xq{s2Gc&-GiM{vqg)sNcQ%OPK#B>UX!lfzYf_ z-!&WG@B2Z0&yF8~J~!6qS}^~WugLYvcgpp~@5}Yx6YDR2*FntdqxF})buakjL-ltq zx>2cne^URI9<0OV4fT&dkM+NPZT(L_d=>ObTm64L{VMEGZ$s>}|ArXw1i9v)ZkYS( zvr6@CZ)jSB`K|nbTtE7UhI!jDpKCtVa7xpB$mb^;-qBVKy>e+o|8sAJeW`0GJ_+~@ zEpnY%FW1cKhC_dbo=(ng`0_gN#rxJbJa+4+K##{8zIpWzAkWt~e6N2e#`|`|)6d-s zJz3xI?Cp=jKe@Q!dFtV{Ya9Oj%_pI6XE*%iSkR;U#gtN?JOdn}W~z0{P`mK&ZE9HM z)IOC`Y5bo=m;yn=zsc*2_MYr;(RkZ%ZeJ>$j#uK#8vWJAR6L#SFDrio|5^8!J-P0L zjk`L!&+6P{Z0X*y-RRu2Vf)q&qiI=7%ifh8EiD_nH_9JYHMh33Y}(mmGz}Dsxwe*; z(b3W7(Ur~Fe1A*#?v~w~I+hI-htjKBTMET|s<+sjC?=ZL$JX)7_3H+b@x=OdL&;*? z$i#<|r!?)yJp;w$fnt-e@#Ok-Eizb7HZf-CuTE()`Y^__zW7io zJ=V5xPtS0sIJ|JJd|OBzOtvj=%@xhhqsdhNK(TFAYpZnw!M0_x`Js4vEvJ;Tz%JWg zT$YRHQ+vOAh6VV{46}c)mZCSys&E+FEl5?9V;fVlg}9`6`|4D|)`j>*4iN zgx{0S_6{yfWfCA!8&D!i?2iE5gbC;HkJIJcN$%j9g9+AvcY4%%b*4(HygG=VhT4mt zJ?ac_Q4#!PsQJjNtWz!eK3;Qby~QbN_PU<+dy_q9q>9E!GG9n#GsgVo7*~%UI${rC z0fJhfdQ}#`jj6Tf7#wMZ#wU+afKyoE}_HQ~I3>H&I~hKP1jxM7@T8 zS#^OLFFOn9wBcTBtxOd33L2nz8Y{Y1Q+a{v*T4JMvsIAX5l>}|L~};LU0geV#e)9gTJl&ZzWY4*?otJe6~{lpxB$XsfeUpNSCD)lv+11xe?O*+1k+u` zfn;85i+rkQxR~`KTM|R>vUUQ4MA>mzN@6mu@hnA0KOS=hkM4nZX0Tusvu;|I9KLZv zbzOfrWevftRqY^PQDwR4Hjh=d4;KeOgV0rJbw2{mp-gp6v=%nlnI>2xo=F%T(3%#l zON_HoulBGS!UzS3hOGYxej^98;aiG|p*hv9O{Q%aPNfq@F*O7=4pu8kzfRCkm}f*K zw2~@-b)X_ZjU+@N3Cd8F6@UU&1ep(_;!`LCxTm0n=~R2FYufWkV=Oyt6o&CjF$*b3 zCyPlV4y}=k7Yd`}HtP9q~-OKbbG1ah?TpJOtLt z06*ejjhR(#_L!49z&!m)8-80!Xj2tgwFo8^WesxLP-?L-eJR)@s+N)v>eU{ba=^%b zI z%i7OXb(*VEa>QAh%f)txqLxKSsW*bIKU>LA&CE?V95H#}?#Jv4vMHg*gzT{IcKuqZ(bOvOL z5;qL_$(CJYayRr5xHIeK4|S@~AgzaMVL&Fcfa(}j~k@cLWtNfK}Ep);0;8ij%Es8$mNSfOOXz6aVOm(1X~q+zj%#bmfAlS~`1weVT{lKEt& zH(6-rO4Ql9Bnt(kHOW%76I31q7maZSm>=m(7fmQmCCfQyaL=$klW99)#q)4#^MZK+ zj&g|s>M_6=9_LbIT+u`2&TP@hWJgOpc7e$s)GJxNu6dQw8c$Tc<&JcRV~W8pP$_Vb z0fjJbt|iy#AU+9i4Vvq7f<3D>T%UM8lgjkB!AkV3KMTtgPv;R(j2Q#2Wr9}i$z})P zc@HKNCvXOnH<+IbUNuejPJy^$DrgqS)^1Y9+7k(5b2gn2VF?E`Tg!<9$UO^ZRVEYE z&g7_JA@k-it0%Y&!1WylyVHEwxhag6SGk5+x* zG^vLTbk@>Q`PCMT37(zpeHK`eH->;?#>M3}HU(pA#@La}3{OZ=?7D0?(bon*Wjx=an`l$-Fk^FA+b@|4mHlX1% zozRNoNqCB-hgxHr&;p1+a!_%57lp(Tp>s&&h|7!}a1tDfz&AadxHm$hlkBenu5BLe zsa8?}L2^rDd>R}N%glnPfPy#)I*~552x1(cXo4u-v>f#K?U7spI%5}YU^3q|A16#b}#cir5hO!tgpON1?ZwHlWBL}@VdG;bX#g0&jA zk;CD(0FsqQ{zt+PI^vwKUl+L;c2~YXo{Ujvoygb5NrAJvL=(I-E$6+77^^$4_>M2VqXp#|H`+YX&(b%7@e23=8e4Ybog z1coU+#W&;MG+1I&*qci9CyQpnp;ND{1VWC_;Z|uS-J4K|hcm394=oC044g>CLCWo3 zPu2N@ZiZLZyhyfC==BgU%f*Vje27YM&n1CkL2*;37OOlC>xIzK^;mQ#JfG4JHG!vn zvAa*ZJDz=m8HZbgmz+W^2qaVRq+z-cyp(+Af63H@l5~a6%k+RUdFTQr&zP6x&jBqD zmS^iE)SLv?T+@LZ9f%io#5@{@4!4tM;>32Olkt3KJ~a|ACO5{5am}qVssmF;o**{! z{Nf`q>`&2+1vYv7<|I2CHAG`~vX>TPONw##n3lBAp-$Ell)S(}3}I#DSV~|l%>g+q z6VeQUske~1D|5Y0}K&e$e2SifLQab9vibB|Q^ud}9 z*(rrF9I8xX2#u68m{u1P(?FZNRNH}S&0u#{(?+xG1|4VgXn0P+fTF^B&@c=uAt$w4 z+JSFU;HN?OmRV*Xm6Ow7 zH9<~aXi_4hAAv{D|`ViNLLsc1Lk z)|saIVAU?G1x9>$N9)&YI*?1{b)rCP_z)m8w{e~9FS^Wa#b#8lC6q7m%tR(S^pOrM zIEC8n3`9HQ#eq_oHEWwrDIyor`JIr*Y>Ay}+IQB2Ox`K6M6&}UxVYAaS7*W#SS3Ks znFy52sKBviA>lBHP1lBS0Lmk2T3RNJ9T`SJAfE3XD1ploQ~f2Oc5*7IMNPup(psUW zE7{9(S~io`20CO3XKJuadk`7XrnJfqyHyI1QVV+6;zr1zbR+G8h<7NKL`n@c)3i_$ zCNMas&BZ5sRqMNw1>FoW;j(UnRTu;B`E_;y+;{=lS?rZnVWFaBR#w+9Jh~1=e|zL5 z-2-T9usFbogX(5I!e6s2HM?3cTMtH$!-sI-R-7464~viNC#1V|NKJ2t2}5%V>ZF6l zt~~3S&GC^GnW=VTb~MAlZ)-6*q)n-TMbE2YMA;JCi3AT|;${`zp4QZkIBFwlBVmF- z;y)ZDQv(80TSFT_wE##<&>lpE{O5WObY6yk@F8pn^)>`!89asX;U`nJAQP60;S^Yr zh=f>$KQ$e7O(~qw+6ITN-k8lKCm1ybYcK%j=yBzUqMP#hY>Dw|o%>x&T&bTBy&NRa zM)xzok#TrhM^bb^*bKutJGlfak4Yw`9|7Vjr+_ot91Aq1J2IL_=uF?MFO^Q}GEESH zD)>(XGi0wwWHZ{Y!YNAS>&;w>IB8650M$A}h?2c_=YvCrL9xP4$91d`D0b;32&Wm$ z)dLTTTy6C}2z#x-XXaApW3QT_7U8-|Ew=HS4SkMI956dwQ*hl10~z`GD~(0-S1k^i zxJMFOorhPfF@{nZG%6GTWaY}Y?$|&$Yu>ujz;BCIu5KON&{7Bk2&rA7SNb57&p2E% zdVvScxrkxc!4fzgL5Gd^EN5u;au~yz_((jJj`x%%G}CSLrlq4w+D4Eaz_1A$Y)v0> zxP>I#%Y=4tf`pDi449&Z2W~$V9YCi>$@_xyCM zq)DwfRw-!bE6u8YNxaT6yA2rrNXLM`?OAG{tisSMqi0Wls))#N2tWChOEN&EA*Vyd zo(UR4*Y9OA3r!vD0VW6JeW&V+J(<(;=Dw0DHLXgrk=V_Pne03R(tn0$__^(Ihj#UUQLmOaAG} zNNWy)aU1ju=EZeuM(+SBvKdB9Nh6lnJnLdBcEP|t0taIo)>|Vfm zrhRAeT6&=z*U0JtjN4kEZfDk@(NEZA^&rFwSn^(V(0-<(xi{N9JV@ao@sy0&39Y$6XK9?4;+Lpx>>(do!>ik9w5>Qt-FCmw;-wh$Pj)*a#7 z#E3Ai51sTJ7;DP_Np8d$KUg%?Q|vK&#zNtZz+T7UA(LUmOJ~@_-_WdypLC39&pBV- zCmkpJB&7zrO-#zWh17v1MV8a?d=}*o<`v0IS(lHpcoVjB<&8bNw;S<90{d=|anB=` zW1^AW^rvUD{plolPRH{B@=*^vGBo7#J7e2LU`j2pQC17-`C}lW`5&>EV#$7o86Rm4P&U={ z!o3>~>3J8xwqzjs-UhaMEJNIBbpW%EJg>KQ?E9eC*1o;_u!u zm;o*5*dwDuM;V8Xo@^-~ym8Iw0uwB(~yDFebhuvoj$>{mDD2(+I`tCzl;1 zTxyF~$Up9EER!J;jE=axyhDf`?0BI!kCG}BZ`-m(_8zKT*<1np7USq6Dwt_vWMk|{ zlE`j|R!kKOh6d1V>_uZHQn^SYXGL(cQI@HrF(6cVlf%j(pM(HpU#2OEzkXKMy*hJOi4U{NO+Z#WrS{ zV$fzvI=Tt9erK}CM6%lvo^1lPGoGO!>jBbCF_P}3fLRzKX$!u|v)wBc+)RSAt6c`# z7qCHTBESnwEO&w7olYy!4PhiW1_PLFR#H%#5c(CKx`mZdWC8RdEque8jj6HYm=alj z7=KCev)1`(EkbNj^fwJV+mpnUGsqVQ1I~*UkReT=CM1zos3v;}H@6$Z*h^`55a-NI zIht#GlJsWs$^FCNP_QP--1C<=8>mzmY@-9ijuqOBrui$HO0ClpomQ90SxE!xmpJ5+ zri+asOhUWW>~z_*mO3b{%p5+2eHo>OsE2xG4UvigD`d|sW^LLqy~_}mZ1Q-0#Dl#U z?(&#ZTsx+#YrD~r%okIA+y$C6>@+XME(2FHuK#hTV;6cF3=mX0oKmpZY&97NeBpI=e=;3x739o{ivG?CJ2sPT>6zyP-V~SmA_S~(R zh+c_!e#n&S`4Gx;5kZ`qY$PL<7Tj@5k zE7{*0Q8M|B{-d%jYEWnUeY?}`c`ZgR2tBbbcfiX6h#?8Mcf8A zDo%@2V5#gu8)Z=Mx`a~Kz;+Bh5loT0WT@gj+w5(a)5E6_2DNNDBw!+jqSHsm(P`gu z^bVaotJ%x4tCrcJx6ej0Hh9sR2A9RnwhWR}kS&_27q!dTuC*3Z>1;o_2yQE9EZ7W{ z(VAN_%coeT()%nRz0O$VKVr-I4eEO&)opR2)lSEfmAgFt%L!Q% zA)4+`L!5#g&UShNPOG%9p&-YzIt=8?sTiAe3p9!**pW91)2<&dus3)pp24<{^cWq0 zUbfXyMQW>H^&aNXduXuFPuC6t+&4i9IB@T5J7g3Jxdg-&@YOPOM9ZUA31PgWCd@mc z&S`Gk4UE6t=c9>Bz=T#2|L5RiwBUc1<)|^(JHsGk4BStbhBb3Ligs0m7s+N_9Ut&O z7xUN^V^Q)zGYwL4xJO6Px|kclX{s&=ksfTk^LFl4btQ{MEQmzCekD2Eb1I0mmRc&< z37_lDqd+F2=S-xos2j&ITkl#e&Mm7-+_KuaWp(H+F`QuA3cBr3 z8|+)7t6NdU550YoT4Uc?5r08b~A#K4|^Gr;Ez z^VOgD_8P7Wc}2up-=Q3}O-$8`JFW|cYa4juV0Oc!pFPDL??R=DtDV7wV{6|8O6 zC`IXh)sjQaGKcEKAapGgn2>)0Qvh&7ymzoapB>I5*7caDx%fG1sR?SA*4Ol^#k*#1 z?M3Q*nD%4AVXxcTeKu3#_HVr-=$TCdvX19 zpA2YL1+-8So<&7wvZ#W{7Jf*?>-1zMnUAMS!r_ru#A_R%9E8#w3_xiBld2W$I&J*02Nshp0%<&_2g2F{IQmj)Uvlh1yDT}uy=yf`z282 zZYr>U$w92tLKm1N3AClDs24-BhdRhOjRY@w6r+P%7fM35_@+|_dkl}b58O`*QnoXn zW%0Z8LN75Fh*=s(0?Fz0V~~=Alj;XdM7fdN68w@RD7(gl9)9G230Lb$`cQ7YfM^Hb zP<)9My*JI4?fS1T#C=c{%s1)V5{Wj0ge zcZ)6k3P@O29vt;Jb+Yp2vQXJFtq5bdeQ0tQoD6pDIBqRCSpTjXmxa> z8deG~Zoi;wz%0DmFgb~+hW0)RP+D81Hbb9`5SZS)Kii)h}H0jo)8rz7#f;CxMjwDnh!&X=$n{%cL;Eg-G4DAg;CxkkZ zGHZ8wVH=kIOx12fiV37`?Ps=!LRc5T%^lWL87^}6Vd0beudc>h8O4laj71XX=5&~7 zhE_gT6x-i@G#NITm75tw0=>NqPC?L2kDzWtZZ(@3LT4Au1ci-4;b7H^@=Zlf6)zI* zE?u7>b2LY_683J@8j3=Yz(EuoXT-`vuq7A5V=v>0=HMt;an2djKhaw&nG%4emfi!u z^Bklz&3DR7VOo^-m`Z!lnchK>zc39%=tP$s!HSy66?=pk-VsJ&1?*jm0Ws=E{+$|) znd}5C3%vs_7JUS(W1gS6gw&C29}#=2pVCT7p0SFO$mrUroY-C%Gt%3xB>(V8>ctxj|} z1@TNFt(|U^pF(M|0?7)_k8`ptD--6l1GJR#tvGzV%bmuUwt@R#jLvLsPY(GSMr>wo z#iu$Nwc4b2XvTk37dDZqbkGMO2_ooe_dnj;gWY2Xp!PDCN|5l_S8f`S6u)Q+acv}7 zD2`;v3i>iI$C*P1_^Z^w^8y1)M}=(r8}xy1Fu|vWm|QH8?BT9t;!Ed%MGK$9Ym>9V z@-xkva*(P>`cH>Bl~hqrOiRiAlTT>kP$6qNA;>jEfn>g0TjGei(ccKLBh0d6C*J-m~gOX zgS}9q#lnSY4Xiuk)}sv2Z16Km#3iA`lC5$)qZn$(22p8dFG3&b#?+kSXWSZ9v`CRU z%dG8Czc|!pSwQb>(P14K(+s6u-Th1Su`;4aaT;zBOP%UNxPzkwnbhb0OZ+J9qBCjj zU`Nx#jXyXCL9>`So~|!hJa8mfE!0>cEI?Ssc;To}YSQYR(esE_yUZIq4h_|?qugU# z5F@e#Q9_-+9Y;@6H5`;9S#=$STNxyU#OOn6FA{mko_FblqY2}=7$iF*e~+ZODk*EW zX3*G%69O5wvc(Z~;uvI6XFB(0P&QHgY4py`^`=Z)oZDI>3?{(Cic>I5qT(Xy} zF)U#cx>Qq928gQz&~`jG6UVQW9cM$S+7UmH8X6u7_i9>QPg;jGyCMXTgL{k}C*BdT znHF3MnPw>Mv6m1x1KtzGh9tMoY{YL&I32)GXID}Kk8ISZ*uzStW!q|iz;b>b4Un_L z#Ik?P-7z~O{;;#Q!!GSmyDOv56vRrFK)4kWN90Dp4a~=hw9_kLYoe7Mv`FRd!)?%e zKzktSgLd0?U;;}f1TW5psblSxtakw}l~`F3fGa5#OJz=kc%6nnVCv0KMhjAfd;3D^ z;q%<4YT#MgAE^wNaNg=M0SOcI8S(byqxy9BP^A9HhdKvVrQT{eC`0drE2&iJsbcz6 z6Slr+jo}<2?C3i82%)$)BWq;klDIa)Xii;8G#yP5a&cu&{%s~sPePd;N4E^I-0pb# zp`%QOEhlu^b7r)hkfKtb!qgj{5S#34xOM8XRaT2?=x{uv!rLmFD$QL&3Rz;uq*G~j`8ZI2yQw!BLpt)owZ=4(0F?2yO~Gwp2mJfI&eZWcOwXi8qp? zU6L_b4@K|KoLwmF%^|Sq-Lhpcyi_;w1PwiEuyi-^4BJAm=mO!GQyGRD6q`Bm$TRfF zU1;$hF0fQSAxXK#*z#W~k&=vb#kDRQIT@Rq?RNew&eoQ!a_;GZDQ4lFlh$Ej(DEijnI)nj20^GC9vffo}Y8zb70^( z+MN~f)Uv+nhHYqK=%O@Fr48krRv#P@5jqCc(N>a^Q(;7h-T)hNBOyDQTKv?|F;@B7 zasB5Jxf6qQS!v@P&ulu!fl^InKoTpzSHjI{_RR*Y09DP9M)qK^$Q3r1SsX)W&G3Gs zBd|bo5N@$Xz=j4hmc?hw4Ij5@RJV5^iJf4D-aHy-!}Sua3)5>lAl3%W6M@;FDx@_c zl)&g+gudpfX6QQt$59p}k*Rr5FQ)CB+7RpaWl?I>OlKWm6kxM@EC$OS>gUX@G$-sz3p5hH;=*waYubv=Oz9v$h^b{eBtm;{!*1ngiLM$ydvr zkwvy#4`C=z+>Co)boCLh@NA7nBb#9zAz2*GBc#qiAxY^`hs;Qa>KGK6?QB;Cat6_})|^4*q6R)LuwawPMWy4a$_(Vm3!cI`F=;Anw*{ zcbG^+#9`lF-Z31aKaMbg?ZxU%A7$bNSF3&smBQ2cM%+$@^`0W{&GlesW}PHbf6bm= zbQ52_7bFMWO!vuSEE%xS!_1cB|B|+zD1Xxdv@vfUhL!5gC*|x4FCILFj!?YkOhv2{ zHM}>+bUQ9LUpVW5TQfQv=Dl@+r)b>?T&?a~3a{v1_p&xh#acpGV^fA%s}QiUkeC5y zDC;;ONVM5{^Nh6a`ibO(iLr_3GZ|AJ*UrTV0vBjRd!-^FKztBk zXb=RxN^J$-$N{<%*)gc{If?j&(M>R?IcsYt4$d_}0W0qsO z6^>PGseeLvF?((ulS;B5ngfYqSAgtN9tg+TPH`GhwK~JDeT z4gIw`Ysmgmv)JoJ=zr<;1%9Agv4}^$haYczLuF?x_V5Si%CnJ}DdOqK+gX$H0 zS4xxAXlPw%b?*&P9ko!>l5=tQci?neY^Dv4el(d$FAb`!w4|*uqB`Li9AkkmwOGQx zx8Y3I2}cyn-MtLn!PguaNRZHF?p(cz%#G^SL~DqNj2^>Ms)b$~!^qok7F=+mjn;V) z&~`xkI4>TJte9cu^zoRT(P5Y~1v8O~mZ~61lGu^`y?oTEih7Z#?ZR;i#&(oHLv;xc z=5#z6A*9r5Ql zCB1ANBt;_zab@{LmQ#-)y{mqxfKh9ofpr3ERYuR zNm4r}zjtG^Hta8LaG}W49C_S{ej6j35!i-~R0GeUk<*$-vzdiR*z;x@>t@4~l|V&b zpBNb=%PPGD%#0Ix$3+N|#YGT%iH`LnX=4~1N&IIKKz;hY4JFEbBRYBJ<-?Gq$R6~f z1wsE5N+pT3m}po=xz^@WQr_G0z;j!Ogf(UFW>uX|(Y1 zDY{t0))|Nn$|riug13Oe7LR;V=cn+ShLr4~_g`@KBXB809lEI=&7$ve?Yo?IH`xm5)?u9W&han_3omRGI3Smb93*u+P zi4l%-4!jr0HqGvKK%{{LeQbG&i}=P?3C*id-CK|sU_harGQ3++sKQb`~GG2{Z;z@U13Qn zK47Qc><63|JklL=pA_VOu?*KvA5&*PTVr-c_9Z1b;)f7??R5}FoqlZ;d;ixj_?8R}eb^)^m`! zOXGd?Je`mhfalj?+dy2CBYgOyI;zf3h9d~`&CIm_$$B#b!p0nT7fS@W2<;X4ANapH zoYX(+-|=8l|4>I2yZIk>4hnSm&??Dm+iB~NdWt@!gDoB5YJ@GUY?7oeLO<0hblbpf zaMNsXiQyd1$UvL94gmcK?z19Om&DN&(M(y~ZlAuXz43+ko4hlMTNvR!;N50Wh}x5v zte7|jj$r>>2&AI~F*ZT*j|f-ay#B>e(5 ziGV)3bhLxYXpjDiY+R&}J4hb~vla3xaIR1&f{)Yfh0z+vc(CSxKMv^9hhQBR>EOVZ zZ&`;J#&per1#R=aw{T;5E)ZQIQ9N>lAW6(F<3TeMBy8Z`-6%eoHJbOt7WP8$@7^XA z7+H))Ou1FVnSF+{Pv|g4?}v?Jo48epNk^mSYo8$3b6<~uC z!Lgke+ehhG)QRV5?u0i_Y1#=Q=vs-Z>mjP&Bvs-Oy4cLVF}=VZFo=ac=rLPT{R3(I zD;n8e$=u`kt`dM^kY8koAuUdL4!wFnCyYXHWiFkE0!dWM#iLPUuLu(W&SFi$xxDBE zM;jdI$s;2)gN6Wfa(e8pCUG4^s<1eC@~F%=RbyksDMTX&28=}?~Y4a(t|Kh zV3d?3^CnK3HL71d737i$PJ^nq0J~m{%L=%>+Si*RtlWWIkC`0|d+r<{PlVWdB+YA1 z4oglUVXljYp#@C4UbEG>&SlE$r0H$4wNxN@>F4NzldSeO9d%*XKTgQh2Po)Q36lYf z?0+j@y|tDI!*t&fVfyCq#QVjh4bQ>zXwgiVr*}|E4#I-jM~r|_?(-*{gbtH~+6F=e z^K<~(LYfj1R<^2oQ$ucx*g}h_Z6S!7;f7cTU*;Dqsv?@k9@)~wJ=g5wVRDOXAlVZp ztil#oG6upf!X(U3_>TpB-ewwy5r}V|aliQrP04_mOV3mRrh&H&;dOcmPNXC=^t?%Y zN%JGcdHE6GJe)ZatYbROt9w*!Z4uQcbWr}om_7y$;=9ELTrV=Jkk>JlexwNJiRQpD zqfsz|Y(okgWENbB5+yww&6w2hD5@N|Rz5P7my~1kLo%^eds+peI%Mdz3H^IFr@6;M zCnzZ49A@dHr>%^gr{UY4tKH6pLwv!KW=!gae7Izd`(AmaBs0!dUlp|^MO}o8z42Y@X#({kg$iKiCj0gCvEXTTPIsyaEiV%!Eile_^>MZZE8`qlIH zM9=Iumw+F9X@Uq;=(w))yB4`e#tK_ozF0VQBcjjC@?bK@zi$k)+ymkzv?6!9&>C!z zvWfwcwS@q3VrcfzF>s?`NxP;+)|6`+$J>MGxEcxDj6=~N?(0K_)CFa%4OJGqEroJc zZy-bZ;F0qVxj-wNv?nK<@#vLd&9T}@d8qa*^lESA?L0fE-be62Y!hrqTAs$j`WQQ?Z0I;uupp$xSIx9{?&by^ossw2;O zpBZ&LS^U-al+D+@9i)jam74LU91lL0V~N2$EFF2p0lYtue(va~j)8D&9AD1Jx5FSk zg{~)YfRl4sDE!s~vLn!xMsOJR@i ziO}uB_QV4aByzyoN0 zpq?c^um_RL|0~MujDBN}=R2EFe=!T8&J_;aFN@Kqd;!@!dRV z*vAgC4aO~6R=U(W(NZ^*!5`%DWbSaQ06d&c-dS_XoVq&&ywf}pkdG zq^Z?vYOj*bkk#FI1ywu84{+ivU0>rPjGssz%l~jPymfmrgBcv_BrZSzguH@0Q3lR{ z7R`g^EP`+iXeYu&uMwSIc^tI^KAu42W=TK?D6%%rd(@m(s5<0^Nkw@@orN6pI@-OM zAgei zM4goxcReV^U1!ebX3xx9L*Y=6WzUTWyHoFRNI%tq>Mj;}pf?{8bc@yw>?s;HX<8EW zVP4wkLE~`gCeO`B)guyk(sCZ{_}u0dcTU0c*qhBLc!2#KdpJDftU@Y_%H5q9wAPUh zDTS7_;3?PSOY9GK9f34s7yEqm`m&^r@NZO~!5CokBj`}@pyls|_Xq?Ku|g_us%Wx) zF;%R+E0d^QyE; z^PQ3b5+7NkKitN^lk>m>UN^u?ZZDBhz=mzqL^cb09&)gY+z-rRC@&z{HbBaT!3iSe zC2SZhk~0j}QrK3R5={X+cZ?%w^PQ^Ga4;|4USHpAgOEMMv(-jSL(^mnEWn$8-D#sh zM~8s{6Z`p-W(A%b90r0h_%7Nrr8z9 NELN12y%Cud`TzK;3|A_!sx5vhWJ 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 D`IPw- literal 0 HcmV?d00001 diff --git a/bin/locale/qt_sk.qm b/bin/locale/qt_sk.qm new file mode 100644 index 0000000000000000000000000000000000000000..2664279cfe8455498da26ad565dcbadcb25aaeb9 GIT binary patch literal 76963 zcmc(I34B~t_5Yn@Cd*{&29#3DP};PV654bxrI4kiEoqyjrBK#MGD(IeGhvptDT@js zE{KAlpeUdqDhh76uqz;oiW>+&7qo&30xFBBDEj|?@0s`BeQ(}Ok^=tqLvNG5d+xdC zo_p>&=bm%!s&Dm9edI6KT=~O8r{8nt^>_WcS*ZgbQcBHJ>MysVJwmB_>y-+grqtr6 zlnNcK)B(>+JAazAE8C=f$98FNPNLnY)RHo^o0a(Sn? z)N}7u>Vy)&e2-FXt@!tuXg`8>r&8@*N`3DPrM9P(`YGU_a;8%E{9D?;Oj7F9Un@2E zs#0g%r_^HsX`hU!z{1;=I(CUFxd*>5{in3cu2sQ{KY=;jEbW@z(%zd;!K>yg^<9wd zvm;79`;ZFW^cd*cC~c-v+TZ+J1wVHqXz@!Gy!jH~ze@#g1H5C-Q^9YnQEKU2X z{Zm*ftoKhoCheb}Q=xC)rc@v0mUdacv@J1p1nIT;1!>Q@Oxl-kRY!2XQ@<;1`cY|rbGka> z!Pf!r*Xrm=x2QnP&(zulaizY{rjGw8=)3I4YTd3C;KQlXE)T2q&x4=mUnlKXzN#?V+z=Z#*t+$>FxpcldaUtZ&7iUQO<%89UgV=9X-&QBS|3;-=XjCU&f^i?c zOl`Xa`{cKK)wT~~9dG`qv_G1vPI?&fXyq-^zHpg3>HAlMPdn5}Z@jA1k6u$J|K&08 zqf#fo_F>4b3)J?0!28LO>eSN~D|OL*(mvj$PQCCp?ANcUXl8~AlzmH`8$4a9_g$&p zz4Jqm6WgWz%b>dGImo9&XR8nNO;qZ{`_)zJo0OU|q;5RrYrx|l>T}oN_md7(Uta%p z?As=FJNfv=C)LBZC6rolfwbL6N!$OjwAsU@{oo_gUY?Tn8;#O_d%gPpL72y!Bcwg~ za`oe?X5e3`p1$-c%yW(U)5hDCy5{}rq#D0SXl(!Tnkz-?d0S+;sd;2WJ!sX%#C;G6$fq10D<0{2z-D)rBg2kxH$ zIG_1f;N{!FkAqhPUdg7EiXT~0`K@P^`cSlF<{aQO8Z24(&R44I8zrkE_k)gylpK2p{{7V_OE&EQ{2SUzPWv_RoV2y% z%s=g?RO9i|er#RI6*KQvfzZ!NJ{H9Ot$3>B>UXidO4{QuEcr|s_^S4mk{iEzxKd|* zt>g>bpGzN=cF!NAeYvLO>!(BBZ#%N&8|43~>!nR^llG~-CAS}QFzEJp$?bpt6wdS8 zO71+r0rdED$=w%#-=3OTa`!d=QtHREOYS-3Zl&(NN!k}bRdUao-{a@!rG4?YCHM0C zdybIy&ofHyKL_jny9p(aEC)ZAhf98W5ZbNTlHZ(yd0%%~$?w|m{bje5Jaa=psVAFD zUbrBE_4;hdD;I!{jkij>`w(d_TPN+uz9#KAXGwd<^pd~*6?)5_&rAE#&q`ifbA?i! z$4k5Y>C)gX?9=_{OMB-(N+7{2Mf_eTTS(=@Dn^NDKTKd7CosacB zxb!1eR4Vn6C8f7M41W3gr=@-4qS7y4fbahC?b7?^UI%<`FMYK3bfx~Yp!Df5==AX) zls+^6F{OITOP_!Ize;`b{?eC@27cE*7gYZQoqzF%VEsDa-SggHL&c>Kv%d=-b>sc` z`QYIC-O#6wTpjHE{c07c`*ZN*ji5*8)zY4_G&t}sj5Fi;VEivoRAvQ&>2!}$@BBh= zSLi9F?z}hn&O3gB?_UjGaxwVzLk|Wo{Uzn}*5F6Kz6o++NARO}oQChZqHk4_Ghl^5dla%64ggK2O@eemr<>>3XFenuqpev;%14Xg`Y<^L*$TwC_{u z!A7)Kqa8;3d9=5n{WjVc&^`w`e*^75(Y_kIHn?7?M@~YEeh;6C7X2Rn6k7Cq_*ZDr z?~!R}@%_UygV$$UA&(bId)o5gXIq0x{q>>XXOocEx1JIF;`|$NuskAdrb*iS9+LLw zk4gL6Wx;Ph0)F|#f2F0K{|IPzKr`_`@rJ|kE_D>7#pnm)4-Jy|J!7m4WI`mG; z-J6C(mn_NPoINvi3H6{2b)ie{ycFlj_d-`4KO4II6QQf01Ycizbm-#`-UvCkJ@kq9 zUIqDbUFe!4Zb19v&}YAb{-t+>Zqo1$4BeUnemAZQefhoL!8!Y#(3igen_|Z8q1&DU zy!nrZ?rJ#_`=&Yc-6h|L-Lfn6z0Uz|;A&}~crNt)yD{(n_lJJ^$vsL{J{x-Mz&Q5* z_R!-u;XHXF6#B(>?1S2qLVxVLQmGG|Qda$NE%wVm+2r$~kF5KTw40tPJ0OYuQ1zFx zL!ZA#sn`u=NBm`xQs2C^?5I~Sg52ts_RI0Ir9Y~|JbKGo>;9+$rRSF&fA>pDb^NmI z_(uV6;}6T)nzn+z_ms8&%A1{l43iCekLTOL? zK-rFtyRqNjS(g0eCKaf7wru3+2bIeHx$K;>Qt^#cLyZ$EaV}FqL z`Pa(cT>-xT{PAV)`RNSkk%yGM|4-nbe=aWj;Nuu)aADaeF2uTB^Q5%5>@B0Yic-5TE_>wD7-!LW(q468*&~1Kg?+nS+TDMU_VNm8uX?fU2TN8!K0Z_S%*BUb zoo_CC;SSLKlE$+C+}f%mW=e@XcX2R*J-&BF2%ZpAvhIH7#&xd&mL7L-R* z4}dRTFV7tOKJ4%3%g>#Q{qf?~^7D3W2YrqyKkwtSK;JJ*``8WAKL54y4{yI5^of*z z{e)H6*Z(cQtq*qmbFJkMluS|TYctCq{Z}RA`{m`2ABCS!y|n!KJJw)7eY7I5Ul#Ug zRYl3{B}#2-uPFV*7Nx%Q%Zj@A`7mbdD<&U^{qy-3DrR4f^{?Dm(eQVi2UXvySh55C zzxJ+*leUJTmp@d|UDk^I^-@Lm@!wP`(OA)cEzXh2f31lB3HW~a2Nk;)g0H_Duh=uM z5qA9J6&HN27y8_+iuZ4QC*;IeDlYvy#=otj;>rN_*|G1cxP8J9`2Nz0J6`}_?)P-X zU6(urIq;E+2U~C+9Q{zmBVWh&=WLVq<=qv(caFl`9an1}^2A{!tEqb{!blxnbM((Yw zI6DSq{r8oXzqmoEcm1Gp@{5@Fu1;w`++R7p3G%gegR~pIS-I%r81DlQRi1Dt?*gno0ow1575W&9cJ|HBTe9E`rI)cKcH?mip)B6vmR1?N5hJ%2*w zB^m73nr}rWd=+|l zrt(XxUs3Azn<~FVJ)kmK`K>=-9S0tecGov6e|5qiAX}n2Uz$Bfsi!wq zzE%ys4m?r$_d7u6M?M-39E0^Py+2&`Z~Xq_+rx+7_9@sq_lM^l^?R)2HQ{AhjPu6R z;iEtJe@YG4hmV>14A!kO-2C8k;G@mq&R^aFJ@={bmj8f{ztbH)?K1SgS#;r>5^AO8K1@Q!Qe zD>dQ3aH@4C^t0Y@W)=8$`}?Im^TqJ)kAp7nxi)_+IV|Eg+xZ3g%zAnjk5R&DG#75<>Qs;(c`fnIZ}PJCiN*afSrPW#;~r54^<)i>>M z?2l>Ew%$?|Pp*dj_SveTe}S)OTv)a1LGWeGo~n`SQ_ydJUv`B zc-JLj&dkNgL6Yrop6k5$K^*Z;oiGyLwr9nxlYN&DNgs&4(i z4VeGy(yskO)z_ziFUyu!-IHlh>dJ3deRs!Wus3?D?)%xr=>OZQhaQ{^zr;CJk8D~E zdGt`#4{isaw)a#$c4RB~v#sjqPoiJy)T-aH-#uqb`{(YeXSZUWr~R<%uisw`Z(w@0R+8r-QoBEZsms~9EC(o&FediX1|5dMl9(WvC zUfp@(rSK~qAnjjPqP<0_TaK#kYPc5rW@~lVz2KKypR7Ll_n`mFPgkFE+*Jt zkT0)Pf05%YY>~G6)6(udRoY9RmG;VGrTz9@)!&K+m3r?NtM3SpU>&Zm{^gT`sd&tYB)vx~({I=gkH6;@>IES98DJ_XA^~=|4LMtI}PhU_Iegyk_{_2_|R^JZ$ zqNZlSPuD2*`e4n`58tiSZ?36n&)f#T<_prka(PYPvoY8m@0RvYchvNM=|K3oepM4+ z+X8+2)tdN|nCH6f(mrr<&Cm+`{M^555;p*^>Xw?U!a6)UyC(bZ?*NaTHM{upT@BJc zHdor`m(=V!?*{xnA?;%^X`fHmoO=r7hzk|`TH3gYA1XW>pthP+9?OQ>WHG*bF>p?X7*}58uVSXVg9#41q6J)&BaU|5565J+&{>A8^BUbyY8dK4s@f zyLMvTA^7(|<@I$l539gAxT0>>FSg>mc(iWz?TaDjPLp<1Mct7*u|J-ErfxwT`~Lek z*DbjGUiir_tXud=oa+~d>l$}q{@;(*wSF4==8#kB*1i5uz&WFCL+=#edwyNpEb#T; zFRklY0=(`XkoJY2)s0*TzKNV#ch33X%ST?SyYkHE;I}%s?)p<+#W{ai-7QmqU-*)` zTSL&7%G;z}J6w0S+N9KRch~)x{+;WNse7&+{XRRt?jM;($n(F{{p;=-N@cz+?FUbl z_O^Sa{nnFlFwr+kv9b%)Y_(9$R}r+0_%os6Du(tf{bw)!?89#oDyep=DB9;hCJ+$+ zO`5%Meq?rIB$3QSdSiX@M64HESe17yYU=5U4F(8(Ik1drSiKs?9{d>%)DVsyaiJbD z2I$=?tp?TWf%EabIm*YgFa(TQkzvTpk!1^?FEc@AZFuQ z)*2g(Wn#pys$)@WG!xwt?H-J294BZThtVsFIrgfk-7B;|UfO8SpehHp2k6O1o6pWy%H1pQ?ObRpf$^$pc;9*!mad+m?) zt;hEjrP*abvy3oL(z}fKg3=|T zW@_m&QymL9M+qc**oCXk&a7@q#UdlgY$Tn{{bg4?JrKzxAz8R_x%X$zZk&1Ss-y6+ zKaMliiGipl#||~(Vp#q(7x&hO+hf)=4Gu=gi0R1Od58PMs~;6!g9~Pz3A3RP)Ct(u zvS8iVP#HC1;}mYnWK!|&Y^Km6RA4s`t7i$fg!u?G0Iw^3CL#u zF00fD=rF8a1V3qUAD~2_^oeO162ZTbztfNo35aU`t1T7mAttvZhesmGRAggvXDkxc zRQ4aQ0Wx$1NZR`h@Z_L*1ApUK;A$Kea{TPDN0``>ib3v0HpO;D)+PsgV<~_5Vbuz0 zM_D#x_YFe!7uZfy^Un{U0sgf+SBfkGGx`r$ivhVEqd87x>=BwFH>n+N8a zR4Q2jS>r6S2MKZu`h%aQHiDM*njs7}!(uu(R4acsf&r!=|Wpfz{{%{YKBD9^1~LWMx}2xg$~^PZXeIgi!5$ z#~dj?itpm^Xxn#YF)*H;S>pgJxH}`Gz;pI_wE?ZbR-XZ35rF^~Nf`^Q!%sWaNFa(M z$kazBY)-@?y}7M863GtxldKlt3_<8@kiz=L1a}t|u{(v>DdIoSTo7Te8pyFi1nWe_ zJBpo5jn9_mDr-aAuRRsp3BjcWd88$o$ixzv0&2oy4|GcIK8%rqPP7+_LL6MrG0l-2 z-EE?4r1gou0-79WUJtzGtT6Sn^3M9$U^1%hkbI42x@v<+Nnmttod`~r7qzg~CXYSe z%FbA{_sC>oaKxX2GXm9SKR~0Nya)3e#{AMYoUn$oD-|zv-t6xIv=U&<)24y|Ru*LE zDW<-tvDjf8^yO3=BTWU?396Eo+>YK~SgcLzf(F);!AU-fXlu}NsT+$O1v#)RK&l=r zwFAH5F;9!nVKC>Un$$Ir+!d)Gi1+q_ZTqN{`BT0YSjWJ*m?wp?P3d4)d`|&UR14|v zryK{npc(s*GSA%wNDpJYUcF;DpAs zz$`3j8;ZiDh_~qloZ~jd9N2|PHzRN+W**bKb}!VK1QyX)KpR$h*Kn){=A1t@S3(UP z!S1IGN-MSptH*PJros-*D7oE;u?p#XQ@Tcm2IGkxGzUiE!NxHUXLF)Gkve0T!g|Tt zv5tU)U@YXDWQ!7Wf({E0rz7IY*k_o0t?+D6G#M9xjZcFxH(GBOFW8WF z^oY+2hdj2DX|m)oE}SZ+96z`vnH}ua4w4=*Tx@+HES5xN$D+1mPjql?T6;tqiVUB0 z4elOjqBBqu8h}6WVsq2nVjYYA+OB7A2RTDxYQ2u8<3IT6MQ7%5-kH({fA`vZTjie}=;LMurR7)nOEeOJ0vDIAKYE1dTEf zhc0r*60B&A^+mITqH5>$ouJW!U;;rCAe!SH39gF`YAYkJPY}!C_&-a;{h9X_y^48$ zAe&1<^>pu$++h$PwmY+?H=fbVLG7#_XMCPdyuLk}79H5zWhOe1Goo6hlO07fihM$k zqDhVr+^4Y!U@qvh`ZG^md%4J8A`43)h{tyV7ycg>%|Ae2M-<-VSP!LKJ__n4y-z zq})_+(0FDr)*MagW!WFcxZMMLED2hhcKk%$Gp%>hF1^*QaZMfRHT=)Ca|L~pknRxv$y*QcIPS!R#x@!5I>)cLwl$s}9*m9%At~=8 z$vy*neG}5-Og528B?rBejoWNv(taf-xfA?c19lPfS+5`v)@H0m9ye#XNs9GF_sb5$ zK$*TVx&yv$hnkVTEIi<8C?s;9;bj}T-=b^60fXPk63`PNJ`UPRWOu{zGPY@bdp47XB|VfQTyKuIBi-=hlk*eF)R34`-Uaa@ z>Py?V4ADa@L~{#-!*)~*Yr&^_`_TnvOuD)=h7%jtI#46@-I;f4>}WB#*%2NT=_p<^ zZ#N$7l*9BaI2*C7YXqmwkRMC(gp%Fv)_lr00=t~j1I27hGC62T&q=s9;Pj3HN*TU+W&mu*uPLCm?&jbTnnc-f16H>$gC-4(D{G$eFVj&cPC41MxyX zogE&gPooE?eLT@m)fsv;|E3)@rYfDgVDKTp;64$+vat4oC~}riIE%CI4Q%@X{N%Q| z+|xRGEYqSP#@Qs`*tBed!V^JS7?2t7O74NUru&P>uQL(b@!(pE1!F+EKbFxr>lGk_ z`;dIgK4D5DZ`3wNJdgmKUp z?u|z%UlpmhLK56PN=Xb z)0tGXC)1Ve*%1@VbAh(2z>!cqc=qvTht35#CU|&IUo*zGN10-bl8Fz&rvQzSWc3|q zCO9c+yT@t(H!q^fhFrTYiR)ONq&g_7Aj}z{A@!$Nmd zyhppm^b}SaT`h%jKp&So!3S<5h9W$v`bju!L+8TxFlz`grJx}8r=ml1(y&_M3Bwq# z?(mLo9q1NJQ{9DD2k#z_Z40js&7F)oW%McaYb!93cv}Cu$Z%24U$Z;blg*I#BXg*( zrebGjakrk1bYu{TI$!ph{$Wwhh3Q3cwG*|1K`32^z@v0H#_1IWsp9xMJpeSa<3O3) zNBjdo$zI5V*C*hyK#U{fG&o{gygL<5jYNzH81L9+xR%+XI&Bz}JGPwB)^zgbty|7$ zUEjH;Wy|`_o4BX*2|Jeaqq3uEw{W@UgsGV5X4v2q({z6DfMYnR6Z28svUy{qeslL( z*yTFx!vn$BUFi8nLb$s1CYKn%CJ>9nq*wPOjNXFL6Zj2z&DjV0Tl~Qta!NUcHDc_pt zVMwdbK#h8!IJmk#Cw?eAcIq8RG0q^{A^fHfESAD5z=pJ!f4};s-d-C0;&=xQ29v4W z)wlk;;ar{p_`Hs@RA1T47F5RtQiJ0+9q)nJRv6ZFz+&*S_}>dbs;$qVRSXm(Ww&FY zsXK53JIqyHe>`1Fxo{%ybS5G6f%n}h%_i=i+Rf9Glt=E_a7Sx%V`~b6M*K1i1f!-!pfU)LLQ5$!K~FS6YjwyE-l{Q9z`EGL2zq4q zCB+P9EJ(sZ@wXM9$dcy+LynMPqFS-$F(-!{12A~Ln$*mt7GMRydA59->L8Sk zA^s=JJ2leRj|L(Lt=M<&Ad9xtk%iVo4rt-eJrtJqKnFMjbu)x=XEYVJ^jp1l@B`u^ zOdo6WwZOHm9rDMZ^GMrFt`QD$Mu=$@0s#OSC{aTQWwU2vjKVlJ3v1yJ+n1x)j{G9= zjSQ^NNiC*ecSh@rWqJnSGE1d>$7vpA99d3ES5cc@*PBDaIlQAz@s65+@#Z(V1^Oh( zb;mN%A(>A|W5V`A9q1hb5yr_>Z!Sd5yHp!SSt@JOQUA2Kl@r#1l-448c+yzN;Z$rm z3a@g}g$zO;-e}`r(zKBTsp(*p))v@%_T^;|xEkNJ7yizSxh;umKeI2qVS^z`h6{Zc zhMwE1w}hh`g*tff6H)nFPMzz*SwuyH_S8C%iMPh!4103bU4wCi!mrcWQJ&s2a2l~6 zQ?DXu#f^%)S}iP`xUecjAmNsEBQh@Z)u(~LB$qqFeK#9yO(wK8TCP#gCHC-;r?Drl z6*@nTb&%aQ5Y51(*fJ1LA4_CiJ;$Q%PRI?_3g_91|E`5|iTuAW zluzXU_2~IM{{4z%>G*eGU;tyjaG?No);!EYP2IVmNB^FqMLZPh)rIrM>)0~#O#Hiz zuD1C1o9kA0z+N;ycpu2?X2u?fr`3t0oTAqqc57yU*MW$Og#(c+=RbfTM#JB&Wt8KB zw+G(2!aYT?vd}OoAIAQeqlebJ#DCy1M0EIfoTS#nikB-$ue+dZeLt=gi;T+T25}Ej z98)iGq7uy9u<)k(ByE*kT!KF)z80hZr=zNr{@tj$c{LmcHPKPx4xb6(IFIS`&mYn( z9nm1SN?KFGZYdm$GXc=_9i|zS;}3W$oNqgD9iPrAU_5B`ve?28sAW?6HrgLTwHeuM z_X;wiyD;}nGdsDK(Frc8Ac*2iI74RzQfyoNfGA>+hOR{DRW>$NNEIYM)E8xl{c&h8 zlm{7DfVc;La(5c$Di2~LiuK0(;^Jtmgji4E7rFf53IO2M=zu+N@C2xcBtzbpJmO5} zx}Blo`Et}Sa?ke#18+UwjV{uvI$Ghi-WKo0O`bm0Yq4n5RvA!g3Je^m;%><6K_Shs z8XYWU6D713Sr3}o^E!m}(N87(Ec^3d8)Dr^!&)Geg#)Y0@PbyrIiPRJozHncUg+~e z$6!r|_JnQ3NYQ>tv~GrR%hV-zia%pf{aE)_oJDv19sIY>TE6IIs%!z9Of-hxM^f$AO(M z37oj>w8bpPDl@(#h#_s)4)W> z(Ei-2j4{#H-hxyCgo+~kuEUwE!MD0wW?T$h_B17ssuE5<0v zEfJM#SyN_X(oZd`NCk%L&&)0wSX!Ae6!0UFP#qm)%=Snmwi{RW=>}*iIt6=IGyxY% z63%r5wDrUy@r>5c^Jq_%mVrAuvBNOuaj7|w%ta&SbsBrNe_mJmKsRY-EuL6dg=z^81Il%HkiG@XV~<>7KHP25#K^Abh(D9OxupC&s=O6B`FTL; z?I~Q;z$BA%q2-vehq82mQ??+td2-YtG5rZsB$+=CLF<;mE!@5SQ4>)otl!Ac$l+uK z=8X7@RJqb|r?`j)Yq6Xo?Qx51pUD1M`FV30YmR(g$MGb)=XD-eu6thZYVDba2!(hx z{50C7OnoD-L(yFKys!5q)jbag%?|4XEy zmvf62QR-xPnYIM-@ELnHa$eU7UZcRKen{*1zIZlxJ7EdmM6z)nQnfe}jIduDCENw; zm|tI{baECDIbJo}9YRpFj&J7Y6SVRw;mMALYxOz7)8~XS^x5d?vvCZ4ws`t%DbiP)1Zq4 zvF9p?kJYw6MKoLU%LqiWTwsf5L99s2I+1ub$(@sbXP^s}B{M?W@*ffsM@VV0nNT~h zvv_?^wTsFUb!mxK<7v3VuyT^Cx>qwaP;2rtXD^%?>4_$^wbQM$(|Q|L9o4;h)$r;q zFoLpa#xF);3k}3ly32g+n~e0ucHtMeS7vluECoT0e@;e*vx%NeR!8_S0w=X2t&eSg ziZkxPh)}bcu)>sK{$0oKs_}=z0uv0pV0;1NA9&YjE{=6ZIp@KI^@*KP++FC%a?-Hy zW+~zh^2cVp^4%Xwk?JS@9Y%FA`FAVmY4ZQP8WOKv$CxAD(_ZUP{aU0M<>V9oW?0KK zd`^UmAisnHf+%u%JdlL$PAs7Vu0jO_dKT!ZU}RL*F!;huuZyLSqHmoIZV1(C4WcL{ z{K)bBm9OdRgJq%pP(n+ZLd{T9x#8>@}ST4*o4{e5n z+Bq#Y5_G)Ollb;T>b9lo<2uaVNq5NuwiIWiCHMUNG?Z#6Qw8}(c?d6rkuM@QqnwgN-j$&n~fyh?R1(?#=Fo1Ab z@~i`~VXP1_hM8_}#q=VWHwQO&Kka}%4pinbLc3bWJOG4*3!A7iX2MOPqz{R5Vagzq zHCiDO&GZ>HizSfTN;++_9LjN(A6$(b zUQK!}f;Eg4-)$36iXu3ZWjM3na>*!# zNO*uT0+t5qiXS15)4SfXb{%%e_iB#L3RX_h=(~kgQRudzcTOVTX(XXHpt_&1z6IDv_m38tX)in-~moHjNoXUQanRPV)VIs9S{kS``Cv?U=&3kpxC zD4Z39rUj@y@tRG@BgpMbuOw*Jz6m;m9p9iZ(>}1UHt@zqid6;29S;*vwMQ(3G9avU0DV@5I@V?0V%@|5X$b=7SSq0IfK|t& zoEWg*yq;hHdyDd}42W<2X>0%|U3CEw%>~qqm_8dpz}pu5zuhCr9esH6x-DDU_4*@D zBPVCcu?FbzpIiB-;F|*He+(KR_wgJwAS}o?zHzQfzzqE!^lwlG+61+?(t(x{t_j&Q zfYf$Y*h%*4I2B`v)t({t0+^LyL}#R|7gay*4PGoWWHEoh>8=4GW`>ifogNv$jieQO zXm8--^q6$O@SG!8=<^X!c?3%mffP9B)a2ZAb|y|kdzuGrN=8sF2=~rt^et!ZScUTG z;TPHBN!+zkUgoDS6%tAZqnOa{5r#{TQ8fASJLh1<@Nuqt?HSZ{BIk)r9%VG2XamOb ztc7OjCJBn`YaZAEjYTqAxg4g}OcUEr_SbhPBY(*MS$akO&>{m0 zXB*IOLSHatftk@T-KonK9UMeyKSa5?;?#3PA*#?yY*+>DM?yN7v}Rr96ds!LOg~4>UJFnxtfv7U@z@F zB8g?=+Z=1u){_JaraB-_MasV9({`3t|CvTXK}WbWeXXPvhDVv@WZ^)v>NVm~lio-) zm6F;)`GB>$0t5+|;Fxah9l)5LJ%Opt5C{d75w!Qk*mMSOY(Rvsw~#W2$6{13iHo6Na1W_qGfojS7GNcv!SabM(vN>q zODG^$eKSY$7x#Z6#|;vXCTyG|8ybz_emAmm^eOES(x02&_I$g1QqEJ&h`-#7Kl7K2 z8C#bB&0MhkoMfY1+0^E?&HA!3AN~R-000}s@};>qI>*V+3^Q`itjsXI75qszx5zx? zz-1zYIg828EO@|13eRCyVc52B%u!?wxD4Ve0=F2>bUx=FB1R^e1c4PsnwmMuOh9!1 z({`u6N`1px2#H=poFMLp=WBuk< z{gd`)GzZSh4J9?HC7_K*rsH~Y-|>oAFv6#7NqiuO&FUOIPk~r+fM;NyBj&>mGH|YhF?Qr0+L2mu z&0Ud`a_Y!X4Cy%9QB%{go(c-Q^%T*YIEteDb(KT#P4e=b&sX{I82B&nOxo3t46C(a87I1TV8%o)SLHVUEW`XmV6f*t@TJo!2|Yr~F* zF^VCWjXd6g48to|2nC}Q97GFM$;x}{EM84VV=|-p1^umRqfCcoRomVj|M|?z1vGir z#Zn0TTv!IV#0^M(^auYY{6>zTDBw5pl5FuPKC0qnfQ&tsVg|d{o8ujg={j#<4^tDO zO~jBp?~eAXNwB{&FUf4jXz)e*lh4{sGKf^jrzo<#epK_#L9JEu8~9M~z&2+Vo+6V;NT3 z{8q7TSM|Ai)|)56d^$>wq2C*I6?n)(Q@mi$6SWV3#sM-K3_0Wk>G}*fdiJ?br!mx= z@aZ)jDlTiI`Bcg`u&vw}t#_UW@BuUKCkA|ASdrOIGAZ9_G z{AaZ*H^%FoX9;`;9rx1)J}~&$tic`NXx|)<}=Q(z=W8t#TVKT!K;sIIl&ls z%-=agsqV@|;TuXs>bE5I+b}*Djm|)!4$@XT8K~a+xl_xfLZ{vpuFp67`gntz;jZG` zjBjTYx$H12a8{t44eqr%a-r_IjD4rdT|4#3`21fiawpYnw< zX}*-=lQ+X<-{Jn!{91yTr!qi{C*M>YBRT)YN=r#3Q76?sIPyO1j=GYoXFvPs`Zrn~`e*2FvW$UrCI$gwpe< zGyla(5aMyjXeHAvyRrC`@f=Gu!{@6IVF%+@M=szV6GiE=)~h9Si6T9S1w-_)Se!y~ zPskZ@L9F`EQZxW_#3H^99I0YaP|$+{Z1vy86D5?;g&^!@TigT-o2g1^U2vP#p{=?B ztn@E21CT77L7EjC_-q8X^yH3B$XePN%Zrc5U>%7aV;|%W)1to=)S%6=9^dR#OYt{n zCZxRJZF%vmr)Am0O5<{R5~@TY;r&s2KF8pd_1Ms`j{O1YBkS9DF16-{X++^Qi)IiE zKO|XM$iR{>iI zMl1Al){@qan+&cj2BWEdyz#`CEGg>b31wf^okt**wcw*;z(JbqN^f7(p1CfHlUY8tf@VaB9xS8;XmifZNg@GTM9Ny zGMmDS?W4e^6~QbvDeb>6H-`mjv{Y+>rnsY6XFv##0x1N*{mMzmq*$`W(GCwn#G-yL zQ3rbK#MRvMKN4P8aNzl>Nz;bbkt~cjB4le?l=MDjFrA)UE3`cK*g$kA^2)55uLZ}O zU(_f^x)IH{gJF_m z5GeB?=DF)6?v^BwO=5+f@!#UW!}K*8o->rgia~$kb8%u=6eY#zgXI!n3=`dOZ1Mme z<49t3n%CFD~fQ(=9cyU%L@wcfcbF@1%-mWvkafDv*}4hFYuXpTU)k^m3n91ei)oK znC!QTzPqXOKf8)?!>uisuy1HZ;mFGaY# zF&rH>7WTLX&$zEeyhK=)$1|?$2~3Mp%OZ|g{;2q)3MZL?AX;Ww<-#Hd#*&C#i_OD6}u$fZGAdOU5m zK75U;G+O1Nk!?|MR{fE8lBh^FXcg&8P-(7!S| z+beInoq5yciO+;0eNhVJ_WhL!Zccn#reo8hHtM*2@D_=+zj4}zyw;{S4CMQ<(R2;23~VgXt=cKO;UIZ6y)9XPMa z$fR<*@f{t@lzKPo|^FAdi;J4{Nyqsxm@bYlH19#gr+S| z=)63Q7!1VU_hF2FU{6h%1_k$kd*@I^%gxp1_7T0yMS`tdnc`jP%80WAcy&4l5gcc< zZ0+pY+<8XVmZmN1TjoTfY3Cq~L^CMI(v9M74Ut@xR{hWu%b=zr6e5vc{cH@)q`r86 zR(uK+|FRs0{FGbY;t!w{KH`ip^xP5WJb{uo)Z25^I*{)oy?uvV-lW3VnsNc<_>6+jU-WAJahmHF6GvKE(%83Cdo*?2r8!<|chS@oihJQ!b zG4}^HT}O+GrgtX-zvY;%yY2u0^2bCI-FW4NT6QSVcQ-s8UgR$I2fS zCLlOL+~POl$)zu#EFTzI+e=o8uLB67`gr@6r9r~P<3EGD-G(E z$;W#+3QwL54CJv;Z&CF;s_Al-`g@xhX0JLsv)WTn-9@lfN8tmWQTe0O%e??po2!;1 zx}WshjsF(n|I5`1{Ef*XPGtL}rUj9?%NH+xzNVbf=`$Xm|qTtY}A*|7Qq|z_!LEh&`hb5 zI)tCyv>0PAK2dti=+I&qCm}JU2%RH}4#mMCLAB zyqx1;w1RWw`trhjuAU*G%Pa)G7v@sHT(E@50&+o^5y0dM%*CG;$B=%6?uIy_X<20M zGBOOo76dp006ASUAsL4#3TQXDx~An|z(#-r%%ae^BjzHn!D1AF>xH%=GItTV1Rx7S zn*!Vz5k@lzhZo?Qm3yk12IB~IP9y1Rq5rSHxPG|<_&0Rzx zv$zPJGXYS%nExJEASHquxL9zBBq&J ze;H*8F`$T9+OUGtThXw5@n|BZ*%k-H!s122%{xbmgGMb5Fj~Pml3_#~EVqT$Laext zIB14ASWcNmumu6m06-B3OKou=pxp@8HA5UMr8of0qR>Pf2oIA{1g;kv!~yttcLX2{ zLYoC|#uBv#Fep)%+Kbi*Onr{wmL+RI!=+rD#>OJY@GRJcZ8li*Fp>k*jq0S{8Ba1@qe~8RgYcX>kL%fi5 z>nn@EgIqy3i%eOY9E>L|RbIwkDI%}hG(H(^OQZnVixJu0yB$o7{3%l37DEUjD3U`7e57?f3#FnY@(l{kSJo@w zPDP=%K#k2hA#Ge0Vw&97%n8Gs7o*ZcH1~&)pzXie}iL~#zTv=#9*a!trjkWMov%uCMTn_YF zZ-rcl;3T0dkkRM~{B#(YUbHT@)&^koX$HUSR6@oXSx! zFp*1C@awf5)W}c1aUQ9kEY5@W6v}+WcDZWSkIg|f8UT!5)Dcv$e=V3sCk=sN%%Fqv z7QE7q!d_(hDS|!RV=N?`Mra@^T4?8PSe<}M8QPI$)4HrBIgIS_;vFZ@1%+!shB?xy z7Pq^X=N!Vt`%ePnIj%TfsPx(pv^y>x=}RT`yVQlTAOH3*3~T&ipfIekBM$6I>(#>HA-o!z-yOC8Bw$T7X)9Ie(XP~>IgI&e+0A2(NN zgWt#S6i|CMJs{6sYZjZt%(ei1oQ&K@{Rkt%E7UR&lb1xRJ33J+JZbb>uD7x~;MsXa zj#AcQ`w@r^c>u+o->_eIv{%1af{4N>>IA1p5}D|3@@08PXRIF;12ls3M;Qn^)AnUM z{^uzKKA24Ih_WVVA=r(hz-FiyNu#w%d<*m5UOtUJUO9|q5Pb*%TLAqAEim=tG@!{| z(1PkGtMpOT=FjHUya7IUpN$*#STvKxD{%<>)Fp3R)LHDA0ReK$qxc0p&4M|9=gMBT z=P)^*dpsrC!oT_6eJl)C5>9;l0J ziR6MW1R~9?unFRsyTFEoq{oKY);!>>IC>}FV9IvFiUs#R!Jq| zazD*Wh#s3HwNByFNG@x^h3;EajuW9eIOh|(Dxg)4v$g{_Y$$WsXvDU>$xA)w!rb># z&u_%HJ$}b;(6{Jn&zY`$wYLRoXBtl~cY7q`I<8S4Y*se6BwCv$vXxkE zT#(z0X~Yo7H8@TdE1lYJtmRNkttX2u)ll*h##uR*;pE=Ru{AkXH8}Mx+8EpwV;Jr2 z-50*oqn`LSR>t)i^X=Oeo^mKYV@r65S;~v?8H`=4fxOJwckFW}&}-HpJ`+;&gyDnPuqvm%AhY^eCs#-tmY@B?j><*&l)y1qTvn^5nj@RY(W5HcSP+t4C{EAYcLF5lh%gt9aWj>o zk_Cql_$b-)WA9h4`(<^Wytfx$S+%UHD6nPF9H;5L3R|f|9SV)g%s)?mUB(4>-nJYU z2vmst=1WJT2Lgyqnwc;p;35_8AMgd@Hx25SDI1A$Jq>>%)G`+n2EmI^w@iH+ffZ@3 zN&Df=_p=2{|5Xlm64`>wJWY?u7)Nd3_>#U#xT@fi69sD44h$BKa}qtQ{>XOrWwq&f z{mF2w)@hR7f>LM!^(zl9EXYLpOIV$oO~FOe)%q%cPJYyfv>^tYqhh3bzMh2Z zA(j}}0dmodLnMaXr;>ei!B`(`YVD3p5gY^ur_Av)Z4V^P@UY8Qmff9h(Jn13atl zhX>p|AWE}}n~6rbxO|HP#JmA;s}^Y&>U;1E>(8`1!NxVThOPr2YzZ@M7}cd~ z(mg((@#Ro_rWwwd6h6*{C3)1yL%4x5g9J0{ulWkmA_uPj*1g}EM7x5=`5K`~rzI5cBabA7w8DA zG4x*O*L&eudpG*^ZX9dxMSi^(jkPx_0NWI^=e>BWy_fj)UNY9+OZ|E;9c%Ape!Z8C zwfAzr-pj|@dxc-`6=Ut)t@tDX=6;f&^T6G8Ly~rOoypaP&Hsx|OJ)=_LXJ(f+ zkpo2`v=7RYH6935MG2u7XX*e1c`r0b%W3ZquU0V9_p-^XpXi>d=ch|>6u_xkV06ZM zk}2(d_h7}qe{b$tLE3a>q=KCXhLuEPs54d}K;JJ-hC#_dsT=C~vJ5jb^D0U<={Mp$ zUop`ji?z&GhbuN^hYD%KwYgqiUAP>53aG+-C6)t8;8Xr^Rp{Cce_Fxrt{Qy6mpOcG zHqTd}W=W7|5@e0_e4QUTw-WltIPhUT3=jl?7rKIN1BuZy#$~uv+ zlhT!4^1eS&tEO((MM(Q#eC9`Ws|TQIFbw)|y5;jIzC8OXB=;DfjpLp@M6SOqtgw=H;5|n+Y84*X&7;vb};ov)!GW8t@Imi86 zda|$a>*q2q=J_t~=)zMecBUK2MQKQ1r)1H2{ibfWx2yX;`Mg~a{IGX>N==8itLvD& z-L4-0%X)mQu9;rKKVuo=4brd{RE&vmDuF;jO=|_NqN8g#p0LW)(h1DFryiT-gBz3L z0C#^lYCKc0qwDNJ9q}XEBNzGvzLx65dmwM9tdAIphLc*NiJsV?WZXmf&7_Ydt2>}X zg7QW>1;NYirb>n&JS66)bkK;?XN*r$I2{^r;`VBpDDDl})Rd4kEof>OdFKZ9lf}J; z;-?5DN4+lSw;uGXL0uo0j^o&HkSL&TIv-4f#%9CPIOWr`sw;{xeLq4@g@>L+Z?jly znoaqIugkU$qk_H`HO`=g#Dnw27U-o`Ni(^Sg{=l+N{JKex`tVlj8Ef98^^!#$Z)E* z?w~ggjjr71J6z}8nP%*nkD_*0SpG(Zb)kS(xHiYKI z4e+$)hdJ=r@rF5cL8!L@DT(^w>a+YWa~1gLlIb8iM4rsm+0~3i=;~@?;H<9JJ9oBt zjl6iQ9XB4low8UypD;6ga6o<9LE}zQ?ZSKHU^f=Ss9D0zQDjpznB&6|u$RF@ z8!zb(kdbx`FoT8#e#5M%cw!jzZFR1lz+QmfIDf>I-Y z10HI<9*^2%tmcfk_4qKWRI?^Eo-^~`f>uQnD~d{_6 zBZVfvAWtZ=_9_hk7?s*j!YfU`YMn-uiVgsTTuC@d4dIS0tzAY?RRfG)y2Y$#y~)mI zR1Dqr97l(*8U~b6BZ5`1ITb6)&^tu_J@d{y=a+2&=6qn~9k2kb@Q(a@^PRwn+Ms!F zZryj=HmD26SfAml?$`q78LtKrV4~&!7+&Ja@3e1^apl7q&wE_?z2_r&fLBN?%|Yu? z)F=jBFqW2awe{FNOf=bJ+~Z9~dcjDf9#Jei=nFy-cBzP%4}Z*0e&u)NRTZDmz~(Wy z%D8UK@3Ie1e&quhLwG#iLLD^1+-tR|W?#PBS}qc*a{6#pho{SGn6_fla`T`oHkegw zUd$IM<7QQtx5~KXYR}t4pbjwfLc5@RZxWUMsmoYJ*jQPf+HSaGYwxh3var|{;~gaU zTUMR=FL9zu>fBcbJ7c}tDG3mX-sJJAWL9GCxwO*3kv7j5D;4DDxv^axC!v}JH>V|; zV`{ZMpYl~&*6O+yGN4nGXrFP052R;@-l^ln+3C34RI;%zwt_~iP0SE9y0z)YW5kIz zbMU8~&0xung=gk4TIOL; z1u+W(&uDL58);7t=Zeihyh7>YlMyH>&E7#y+9`xU7tLc;bk#OXgOPWzwO&LM7GCLNj~9{8Hk*fL`13% zhZ%kItkNTI(qq$)!-o#WkHNI<+cvpSU=8!}pdke4!j9p6rQ!P?D*?LNO$yind!rLz z)F|SLj%~4ShMnlcV^Y4ZWV2?F8WClKyUR>(7aXrts(5e1b z``7B5GZD9Dh*B6m)+STCqH?m0|@sBv3kR28%$ZO^4r--r4ymY+Mhm^0iv`gpzJ z?*9Aa4tIm_n?PZIxVx{vOA}YLxG>7+>q3{AxSOQcB)m*r|OxUtE%z3Cru zvzldl_vSJk+z`ea^zQDRZ@Odrg4Vwk^6rMQ&jIglF#e=)Gp#$^-QB;)jjH#VwjGS}6SK11=+J#N6(lGhT>(}DR1qmNur#%qwxucVhoJz4;?`w&n z`gO5E9p38dG9ge4KI8-ETK6$+#Ck?AMYN6R>QW2CGJ-CQzH%K;t0MK*@#N|->V1>z zYsU0)U+lz+u-v+HgNt$tLw4S>F>Se7qMw!exA!R1;pOp!)YEm1G|5F_k3HU+OlTqK z>M{Ov+SPpwVZN^K$No}oxMN!~C0P`j6ZmQ^W3lLLpp(fUVtos;JoVJPoyPV?uD911 zAISCV=Bhbs?*`{}TyGyD*og(+1Io=!jkh6@6&ogOwg=U??d#mu7UajB;V&3ZM~y^Id`YQn?^D084R)m zxjP~y98;e@B1V!jy;KU&(`RJfRM4T$NO%Ou;BTz4AyUu3CtL@RCsbkh5TE;|$-l=* zFu0vSypv-I_@H>5)Eh%x4eK2{)I4UiP;?Ly#`Q0;-NX7>vxw_fMnA}T(#AIPQ}TGQ z`X7Zh(kyp*7?%)AJdD^WT`EE>oaIbZH{?A}H2#^7QG51sLeF?}oQ zno3_Jnuy?(kg5`~1nO^S^UT4m*@c@yQEdhD>Ojh|$|@3nt}23Ow2{h7d&hA!OR%xZ zKjg@SK4;!i@x<9VBicBj1d;aw87v~VGF%nvVos9jK{6oTo#;ndJv_hCt!r+j zL2m6Caz;5HP#JE!1^?08gaa9^)Y67D(zT&jq{Q5l{F#0t_YSdYun10)wG2-p?pR~M zjq$`(`(D?uxOcdl9TFXF?^Ny?VB8;r7Yi;=*|@j{&Jju9Z6IqfxeFg*^S5qkb#JSy z^o*#o(~#qZs7P9dhFQ;ykZb?{&GqJhuB5jfk9URq*U^!{&YCT@ zYxeB(Vzpx#%=d74e9Fe92_sG;T|x+K#06yRqV;-9J`3U|#Mad2ST&1Sf?Yo!hTtB& zpsM9@Jdw_%vUWKZhlsO02fB67Em!wB%HsK+Eo{Yo5hKz);MlsMw=n7N@vk3Coa5=Rc@W@2@C zuroNES^IqQn;Xwz9OvksgebNwPDTci@m$W;sdIqs_AG-)ynGF5F9yihCpZD)G~Xxv zsOc@%gQPqe-Mw<3OMgBrNJv^f5K4@7R0?o~{IrQ)ji;<#MN1qKQGh|PY9-K7Ke>4& zyv0eFo?_IJ>alJU-F(_u5SX^{^{LPU&_;1x8^`?(@OCM&aoorWfQqGB=79Nd9f>%gwo5aS4d$ulE}^Xkdyqyl_E=XAHR9N zYQ!ZXqvvdaF019$8Y|IaYTB(kVjW|(?ZjK8@TCay$m8+ zgL|pSknM0(^3s}5cTt!V0nq@QC`~&0o#M%d=XgUwYI}bjnTL)4K^7^9+jJdKD7K~?s&R}b_ltAvajND=y7AgL-fxV|;re=K z$alC+9m+bMxtd}dXzvzQ)_kyLN|W{a8B{o};^&^K@q%BD>#cmVS?YINsv3lPPJHHW zg)o>CrZR1fWzq`8vd)XTe)Hk$y}WA`ZX};NB8t{LfAZS$q8nL8n6pN6IW!7PXb(Y|9Jof?xRE)t z7tYaIvY5jK=ndbCOV|lJ;E1&uq7tPw#pd@TAEMb^{TV`PpFLP7CeTvWFr3KTTvKhR z_F1F@1AR$z9Q>^owYffY8e004Xi&0~q#9(N9cmEg&^5bJZOmE+yvfJ1iae_0xl?Tmir)!0|^1C4CSL8 zXr8LvWip~O&Ozol)`P;SwnlgCEhr8iHsPM%_qy0Q^zn%3NNvav&LxCtTly*E2lL6vO|`~nGQFP(+tQsJVNWp)c+|?-J;K=!V+Y6LrFn4Tm-d$Z9i0w zTeao@Hg@D*vE97c7H4uan+lZaIxmd9BSmJq@bVgP#LJg(C+aI@u2L5JmEJyN#cJ}L zrcT4ldihEe$z{0F@l*ck?x0oQ6~n8Rq}H6%PFpQnmEn6Vrq#9K4%&O6Lyna_J&eFB zhFo*g@Q7l&S5LrwDI|N`L1-odFsvRK{vy72h;u9-$21Jq;dCt9n>4t2D2u$E=I}|^dgEc5dG;z?e%fRE9&)e{fVN#?{}~BoO4c^%GE!9 zAK}xSWagZ;*Is+=_1^1reLOY)iC^CF)+d)N{Om>V`t-9Km1;RxDYZtat(V}s7T34n zx(?TW!Sw`Ozk};~TwlcXM5VSFxSoV-5!aJ(eJidTaQ(Pa^?UHhr2x}{`?o4}*1sxs z)ai0P_7u6E{2p8%Q>ys5Qu`*9x^b6M=U=bXJdDF@=PDJMxk#x8j#H6qZdWRQk6drr zB-i`eRpj1DJpWb|`PgQqx+-$Lzga~-6;Uc)E7#I$75UmSrAj}=b(2!%IV$qZ8n&q7ON$*C_xYORp8?O!dR5J79|2v?|3S@Q`wpev9j!_K7VuwtV$J?% z@b^uvHJ2})2fm5byz|(vDRthynh!N!r__CWYCg0H5i8I>IorACgOQ5pTPQWyMW z#^e)!P-^J-8E>4sPN`Jij5of%pwz&Kc)7V`!MfQAF6$M+f1eAzP5JpXYhQotM<47 z(0R|w+MWZz>!Rmtdmrk-I=r{`tYx3V{c~$C>-h!n%GKVv_Xg~nFV#NNwp*!pG}L}? z&J{}C_08HtXX5(cvf3X#cs2N9ZtX9xT?9PdRr}lbe^03c57+*A&IRDx>m$)8J_7#O z5^1>~c%AuZ#%CiZ9M=kw(iQ3X4)}J?t0S9UoTJqE z`pAx%MWt@LF|u>;E07fzM*0(2r_?=>)Q%kJa9w2Nr=L;k?kgjsufljYek*eQf)kWl zuwJf{J0owp{f8LmO_2vyVO@?Im+Q%2m+Sc}iBA6Dwv_eH+Z|8DT(hR9Rr0M83QANlqt z-=tLR_Q-$r17Ec@@~c09u6_HWb1zz?)GN0|m#o7+H}cW-In4Xko@m#tkc(?~M7y4B zQ7ZbQXwMDJkaNF__TBS!r5Z1kYwp45&a3e^?ij8>4$s^W9XS>7`+gZ6DP5#g_M&L% znT?>|uIQz|{x!yVyIhNBN3TB-{B+rAa=q>xxxW7o(d)N-Ua98Gqc_aB33Be6a((?5 zqqmJml}bDkz5UwTlv;L{Tu&L3>kZ$F-f_pH&?EJ7UA{1S=fJli_r4vy`6+-rmi$7gv*$z~eh%xjBpZGFRLHLt>FC!!`eUUge;s{h71sB?{}uh#8^5pA zmj8@?xBmA^O{|Ol;Or6b!Mpnk`|Fw$>Mrbm8ggQ+ zE;|eNt-ro5+dmK2uh(5!jALItUiZ4!o`-!nv+m958S{{ z)!jN1e_!%M-9yLU4LTjHd-Tg$(D4&>pF8?p*e|Q=p15f?d)2hy7NxZZLEIxqF3SmE9&>W;xVO$`|FcK z*zcKV>MwcaF6@&V>o57?@z^)B>JR)=NvXH3s6X&t%>VMsaJ@pQ4?J6c#SZ{~&BOIq zUy#FoTV4O=e}jA){^$DJ@7<)-TW_ns# z{^_5c33+lx{j)b+34Z={{rCSk1ipAn{m;6=XY;RaXiyQQj_GTd{}!zK2j?`jKLYrR z9+T@OnT9p34`RK()Uf6+_xc@t`<~L&A7i@{G{sf*o@sF{t#xnT+8?kM<3!tw*8QXT> zXE6WGvHpwFfOAc({{i6hvS(w7pFN2AJQPcP_cuzt{hnC*^ML=}H^qh*zXtT15i5V@ ztI+e8#wPXw-h%JNUcKinc&^=eeVN2|T$$!Q;!?91Sz&IaxbLQ<108q59*Q>I z{qK+qm!H~n_n&b6K!4Ny``-$?3HPrSeFA=#bY&JQ>tTTd_fKPCV5?a!5g6mulP;8`>UYad;8*NXJDUn zy(fP5#bZh>d|Q0Sg(oUiI5ocGN?f~I<9oM%7<_tW{Jg7y*S@dB|LHpH!{uL(CmJAk zhW{&`YzMzwI2kYBius*5Gk!%M_$GdJ{HBkA4h`RnzvWxq`23aex23NF9uLRw*n0)+ z)9=RLw`2+Ee@FZyLv7Gw--v&zrUbPWk3TVyggo6G|M$B}eL%9CFdFTP`w}roN&U_nuReqaXZ@o>f_n*>S z{_tnP_xCj45uF8nd`|N_X5Rq)@Q&s?e+Yb=J}cL@Nx8OvS*|A!$@QJ@Y<|x}U%~$T zLi7Evhum7!*Zk4OkHF6Rc=KbAcY*KcH2>Qq=r;Siay{nLEwjJ;q*8ZwwJg}T6#IE^ z%P~DIO1X!R>!*WH*L||} z*`MACzALx>=v|M%?kKfBNBP_Hi`L(N>1pW6=Ue~qGSFl1#*9)A)q+FReAT54wG;o& zQ598C`&33{@qfAjU#dF%o7rXb4CO0jrlir=@ZKK$0NcB_r}Z5zHfRGV6@I`IEqc(J|uw@tWnljn)`svW*B z@7k<(;0Zo8s>=AkpgQ&E6N>-0@vwOhr2y>(}|{ynV{_>RAit2FLQVEi0@@~_kI%q1oqK9yDn@RJxOfkjS# z!k@fvt^T}2L*nR@q%H2`Zv_*U1)&4am+-$}R8XsMUl!OJ>Qelh0EQV2do}JU0GA}5 z@5DC+;F88%CvZ*RuT$~a<$6vXxYHVwbLjvKXJ$jdHU&Hk*ImTfP{ZatR^vC$nLIIx zu~uLehrlPCT?Kz}BCkVvLGjY2WKdZL&poN8Pi>%zczvb(E81{=z!CJp`3 zX>GTG0wt2f{u7|vFyR9Jak`v4$sHt{5drMNuI*NP zz)?l)&enoVZ-e!~&!Td6;AH=Q@p15u*XCnglQ@gYPMcD9aA1{Tjpl_+(V?X4wnR zL1uF%`Z*i7)m??|<;i>jv|qN?ShlWfsMyinlg%2MoF$`}E~Sh6)2Vj-MIf@9Ok{(| zM1e{nJO;FqxKem~NmY%|G9Uzc^%M%(Ofpd>v8sqh3#vTI|iA_!~XBZu;F&GH*}h5+ms%4P~_eCN_;okV#`{;gwgN_Lwt!!DJ(88-8afw7D>>X`Ji_=|xCl ztJ!ekSj6EBv=@a?D2x^ubz~2U8sKC<9od}9ltZ6d;C%`-v}a;NXeI=E7NMY8uoN$9 zz8R+$upf_YK9Ecoh`Z+VKyt?5W5~kh?dKZ$&2*K)a< z2US=%96buFgSd}>C$u{uV%j~o7qRO{ai28;ca!y*YawxmU?Mw&;z{tMtB~VT}f314dQ5O{L3d9OWyJX%rLL%$ODvrNn+*M>8dx|4%oSYxiK@ zD2?VPvYC8_Y-n5x(_nX?0yKoQ1qQ7|vT;0_sJwjMw{Suzfm$_7)v zBVRUh`H7H6FE+V`QYWw1x~Nw})|rZfDuY}x7>qCO7{@BeT2{bevQ7u_nQ)xgYNKk2 zjnYY6qeL;6$&GYE;|z75howqni*QaRjZxQ9LFx|W^JB2$$I_`KoWabE=HDf+7$?(b zKwwce2koI9a>ja6DPv1Mo02UQ3}~U=J#IkGEF4fX32H}r!myAD$82d8K>E@9K}c>Z zi#!hC76Q%z@bF>qJfjM%BWc8Jw{{f&O6h&>tOv#Fn7tOWtT|938H0%-Eg*xb(*kYj z(8Ey;jTVe4o?YmD*5ne=y$%6vY)ZzqoUuKftAvx5Yh}%oYw zw}Sn}IZ;YbPFfP!jl`r&0xmO=po6cCM|iH{_~eJ}?W9FjFg6=Pv9YEf$iyx(crc*+ zB1>C@Fp1M?gs&|5jP?(=blPPDMSHWLZuoO`2wJ*LhT2VOtz?7eIiU zAQy%4w->bW=w1-|@Ycq&_PMiR=52>D;ZOv2?BO>2Tu69Iju$l-nrv@x;(7`r?A>sD z&wznqc?DtuFxogqO#z5htn|bfd*C+W0*fJTZ7>(^JE;@1ncNt)Um{tqB(g?%sYhE-7qS_$fA-qJx=oc8Wz^il=BbX}$w_xYwQoa_)t7 z2r+wiu9Pp9VK3MsCY=6s98&;Hj!D=E)$T4%!JvOXAy2V5ln|FlINMQy9^(YWu(&!{ z+#Y4o!sx9OVHK5OIgM12+&$WnsucIL>tHU9fu@ss_t99r7z+l$0kfOZoBSk^g|xaD zpFx<|l%KUVlS&)g^QE$}Cqv6Bxj98__^02p3-PK%O6yiIQ8)pp zSR7YEz#ZYL4bW1b-m0zhk^6aY1Gluz4d7-scRMs%FaVAy88k~x`fDd(9PAQ4y_aI) zv|8Jyv^SF)NtdcO*UHNACf@X#s5K_Hm|_}ZR8 zWB|@g=rb#|y5Wv}i7F?MsOu5OnZ)L$KS*iFKwUG&VqVl8s5=NvPb+|)lS9MBrV%+Y znkZ{edLjW?ZU@}NnC;D`6UF{wW`Clb-jpaOG}o@t<2o>@+v%_$!tTq#zF^2ff``8W zW*L{mJ6FRqcBPZl8(TB<#wYcD3mj-2xW+vUBaTHa{&IRSrepyg15##a6#As0!MqvwM&JcTf>J^z^ z1&=Wgrr;gHYSKM}T;mj!Vq%F*!=x!Tm8K1qIg`5lTvGeiO4vzMbyQbUF9O0HWupWO zJ_)_9b0y8a`U`i=pe-QRmC->EhR&!<9XqX+zAYvzBuKKw8vRL?2m{;uV{9 zTaY0Z*EZm(!>OylM|#1Hv{KxPG1{LfkA~s4CQ!e5BSaofvnAPVQ`<3{VscQ(G6l!G zT~KY9%_dBN7XmI=CA+}6oH&@948Kh4A-i+E;XZ^xvHxo%J20ys{z1ym8Pa<0p%h6BF$Uifk+Srtz$T8-H1g4QNhBMhT zsvZF?qz16ny5H@8Fz5kRLtIi@85RSnhTa5n$yo_V20nSNHYe=#2w0c?J%ut|r4%+x z0t)*#hzV-i(Scqt4c+FuMxh?O%HIjTX> zeysjX$3Eh~cT`7R+}3*i=3+5loC2GT?#D369azqbv5=HX2+ROe227m5u7u!eJz4U~ z$*nIm2Ll(;3!W$sr3u$Ts>cUveeXX1oH8q=2u-+ot|sAWf<5k70BLm|x9gtQj60+NcDclf-OwGkFW zV>5?>TJ8?-ar4-;m&92f#jyC_-o&j8{ArpSiBfyJ&b9^;ekep4rOh0MM9)h0?q%&i z2V+Qxbsp7aK2&OC8j&oFu{*_c8(}&NP6*1D$|!|1hSC&a76nk23(@8Y_a%mq;<9g$ zLOP4^ZfUmM%!g{9TC?gQ;88RSfrvBL!Qp_`!D1p;Lg9=7M;oPwp_GqlJ)J@|V9us) zbQINnIusB{(-_Pp*6gFa#(t!JJd;ByVE98bTqQFz^24F{#Ig3Mp8#+i3UI)e(g+Ww z!olx@xqu=XRB3X3STIw4#)rm91r%fWPjVxy=46TGxGC8kremJeF4wY=KuvY`UdC%t z&%^>KTVbX0#zdw(D)O9yKo>u8ibq*%xl=0poHIm<#j@^eCJ+`$ zBL-`usvkEu+!T+`58HM&phmsXC{7MD9Xj_CL*y1>1uKIpA`ti&eL;Q?N8c4Ep2rlB zu1HFbqVym~Ny?hA!weR|4s>`u%D}AlMyHr+9IBbD!42&^+Js{k$x#FQHE}oqeMcO8 zWU+1*jO>fZyJW_(Qv?~!c(4`uAYYle&!f#ZqQ>_Cm<5;O%(jx&qW7&j9N#F|30nKnzi{f*Bb)L$ty6vX zFM?90@5Bl{F;s$c=TZTnCplymK?V|WNV~Jhq(JwHM&6AB!+lsL=2xU-inFCtD>*@K zJRC-|GBEaYEdDXnnV3x;&M)t7hj#~K$zI~)Hsg$A(b&DK&q$b%S6xs;Fgygz!@ABHIa(H^Eh)_3-JIm4GM4D_noJHQn{rA8W`{m( zZgXlNRr<7&^Dl>-FAQB2oQc-d^ZvA#s?C(1IF#gBm>7s_{zq(PSu&s(hkaPHDU#sl zVs8fo041!K0(+2IIuQ_DX$JNf(zDL)?F5+$AQP%4aE14x*@;PmL-@p<5+M|Cm{HDf zTk#f}o4kir7p%v~!^*e(w=`fMg7oFm@H0jaoFi0RT9Y^6J5?XBkVWJsbUeK4;Q`oI zf3k=g0EpJk)hq3Tggf(v5)SYtQ1(_b^EW68wjW8ug%PQjDVGclpxxMuDq>_=kT1eH z-h8n*iI4097G-=0n;1KZM<+GeL((UjS4@XhgD%l({x6h5Y zIX&_~YtSf*Ob*zDvBq_9L(ajO#~B=`pxLJUIFDZHx*>faflym^q{}!@Hs&^InFHck z2v?W~3sViU1t85Z3@IN5jC@C6QrzLoM+X3&Pr!C^+Ca?>T<7Tw;Vlsqxe!c_qcDV7 zAt)K}DdE2k>eWdwT89j=VQ>zF(%TBUpbS$7I>E#|g9N74C7O`n3~ey5=dF3SPp{z{ zDr{~hxuc8?NGF+x|Jj6s$7>ObH!|<>h3v-FO`}Fwkf8ri61OIg!Mnv=ZVMC7v(H<|p`h zn!)H2osy~r(gB7bax7K553^}KY`WXrfZ=SqN3)Wn=)7Ss+IDnAc>GkV_o!~YNe(_+ zO%X>8p5*8!>j2<*0tW%p)(KXSqbO{Ubl6|w7y-Mg0i}4_pt8kGWD6`LZj`3}Yy}M_ zN`3Hwv~F8O3(@kYwAN;@o=pq9)?J*IA`0~M(}9ra$+KvLB??lv?B2T#1L<-Z%PEFo zH5T-l@V||3Ees|Z#5t^}(Vd~z*f-Uz3AtshbIaP0Th=+ZtP8p21m~6$LT*{_+_FC8 zmJ^*@P7J&yhSs}lz%AR=M*G(2iEE7Q8v}2jp-!@It2rs;mXn=ZP7b+cgLBJwJuGa#(`nR*yK3Bbw*u5dhUikfDQFMiQ+&rmvWhOlt(tVZ^4> zEP&jYNREvZ^OanxYshTEQH{AY8Ru)q=2kiX4l;l}|CxYiMc+%I5Eqybo$e#g z`5+OqGt;?rF_8_0!_H!4zq5eXK?v9}ACy)==|iJ~3iL=Q7|W<;_Ozmf6BEzR`+#wB zeThmgISLsS3T>5%6a^g}U>st>TJrQbSJ^sxR!z8<0rn2?`-qrRJg$OG5ITruWe<=G zu`XnD`fyD9(kJt;%v=s2w!hKK3h_|b7N^t4uw}fD7+fDyLLU|qvwC^0=Go(`dg@%G z{$idfoN$)0b_{(KXHQduIyB|y)xbGzp8vJPNhnEc9yHf2?98|c86&#njkaI3#Z+ZL zixd>Au7uTRl!C@9v6mC`lu_cs(K4dlMLURXbx17w7Mz`BQOTTjwIV*Pt~nf6zs2~p z;K*^Xv-PuM_?x52Nn5&NH0b5@fSk9rY(J~BK{Z?+;}DlzLZ=iYbm@C=m4u(fwm45( zgjq=XaV)X;H(qEAI?yk&b{K6R;wrJ5aYu&h&vghFG~*M$1#~g|dFmd*7qeSpi=2;R zg{^b<4vNb4-FI{nBOKv~`ALenGfJ|-6}R;Aluv1_%s69q@rOx?+(q$E!zHt#*9OEu_(Y>(@~=>(p|F?4_j2V|vU zCy7(jTtqByhHWt9tgerqJ9G-)31;iCJJ4`;AFXj3qzGt1PK)G603B28L!B?Zbk+&e zSS`8Ed@N(oTY~l$tHJX|+YHnX1FN3CL1QefOQ(G(?ed|-jlv-hj}$v!N*Fm9t)(Vf z<3ULE4kAJvh80f`$Y%E{LB}C$?HT6w$!ZPQY3S+(-{hD)AWQIEi+GBLR?oD;1|YY@ zk90~HW+5qx(~rhkiT#OzWHD3FXAym1SNgzif+xW&2z3j)B2th*^VwoywgBF=W5CeX z7i2=92`lqcuv0<%QpW|3D-lOG<~hy6PAuL%l9TgV;2jCV4ri;F0F&sAkM_(zd6%@JHwA5A zIiE{K<;@s6VT-vk8e*v27iP*Za6Vh6ISw}>6-O4M1}G?-qDwp@TR}6oAP+>Ne_Cj0 z6rhJl7}4#H=_R^LiWc2TzGF><5L6~Da;7z=&+9QA09_#&z-o8FzXDXa8K_4F6r45i zC|sKk^S#3&G;S)X!0GE~pl1WiY9I2fws{E79%G(&j0xx>`#7ABp)3KYQ<$(@MC?%t zTeW{;`Oqd}$oxfYZLx;fZAh4nqRKjB;jV2twx={^u*J|MyT9p>2)ra+*9sn&mLY-j zPGQ$eypt5AZeyQR?p}|^(uP$~H7X62>DQvJq2I}LR5Fo;bwXv9whPk02yA+6B(%JB zDpGxfT#nWt)Vb_%QGl$WsURjNwFCDjJL)>Q;Q2lKk?t0Igq3Pp?Q9@C97vJ%s8wXR zmh-Z7Ji}O=sng-mGD&$K2HtW)L1Qvg$hyL?{&!UnfN zHE5R?TP6@qo|uIYMAThuQ9)V4)e>oQ9QH(22XEZMFIe z?xtf^XlmNYgsKKV#vI(opJ79{Rn3$ZH|=r?LE7c+;8?2(JTne^Fbv=#Xi)MC%^N8g z4wfLTmZfZFjRMz!X=F}T{9N1NvsW?Ji&j`LXyXM7=)ONiy9yiF5V6{SH1?L9tCC@1w5lU@ZJ;1ZZpcL8+5K&o(FHa%v827QQ z@O7DO2~h?Y;PfO_p=7YAgD=MO5_BVjlfax9D^9t~nB=D@W@q`JUx0Saze_S7i@2nMa*mJStAn7=TnFUK`GlijiqNww(KJmCvXIpfCYjf(B z!f@B*paLh6GPI%y*3cVF4ef3V=CCxWC`Jd3CI*Nl&IHrcrsJ&b%v5PM#xZ9aAc4f| zB=OvVG>x?YXAq46E-Qpa5<32}L#Vky;h(_K<|2@nK!^Mcn3~0A`T(Mq-fy{Vi zJlHl`<9e2-cibC`%2-mJj!WQdwD}fXZZ^$Q+WrsOc5Ilztuut5RyhG@PE&_(gmBts zw6d?DZr~2BmCfspMLvd*m82qaD#I;}C`CC|rp8%cv;uUQknx>4-S`e`8G>@VSskwn z2kZr;`@p?Oy{!bEH@;3|~|lEPb- z=p^R_qus2bkDAgm5b0Gu;Bt_o3S^Z9H(LYFQv+#wgc)%>jX=~WW!>5|svDXIBJ>h^ z6Z=@IMJpLQPheBM-i%}fA~4&OC{D*)X9K%O2z+=IVqg-ilK>Ny4($QS$dR>&v^vtA z|7?8bqoofT@Zb#MI2-Xe7JuLvGhuDKpzRGt=G@Lmy>1PZ3=ZwhtSXvwEXoF}94&CT zMOZ^`SsW)tEkdKkv5-(cSxV#NQhGR1$p%Nt{Gh0CtyTxF?FLX%-6kb-fq3PBm!(04 z*iOd{w|S-45k#j3r7)!X$q<312`uf#MIt&B=9Z*kAcLPHGhi>)+5z3?Mz=5KJ1JPz zl*(5aNQcIg=sjvZtZLUGY0&2899;--MnEa4-$tYlgf>b*y958|QH#bwp_h%&F}jG9 znO&m;{dS8JP`QSfiNQLP-A~0oqm^Vas+Ikt8uQdQV_*&n$?US091nQ03~v@3m#mJj zbO^K^6u5Zp0Vw!qZ_7D*t~2#uv9596; z6HOa|5tBz#5qF**c>v{|l@bdNrX*@x%|#kKty%6+E74*5yDpo0CSVrJl>*WNI=iWB zk$g-Vhb*-C!jXcJL9=PG@BU7RKWcC8Ke4(kV}R2{AubFkHU%V1THtgz#A(S&tXQsAHeErSgsV|Oy?keB!Q}(WrR_6 zO`sR;2B59ApuGeUDm$`*_r||<`(E#8)kmK^COD|e?d;Q~_7t46LR#GQ!{PEzh$k*B zRqXbPz3tz9XaRG?;(#k3q2_u>-MK9g?VVN)ey9b)(18hlO_ zH|KO9%X_mod)^O4Vdd7j^+kL#bN8Y>>RhW+C<~E|%{c~~0svdCE2ru<_R*nN8~A?m zE!0)(bf(13Wbm1x-u)n|qm*M)QE8^OA{dlSV#S_!odJz(O=k=KF{gKI+Do#S6Zy4? z5}`%!2Y(XYm)ei%^ne5;gd73z+IEuQb_-vJ(-v&&M|)QD)FD#KW`lG_xBP=0?T-6n z&}zH1QNJwCdP9bHP7MgTF;?YRu?;%p1(V%B5Z@hR`6AUPS7MHn;eSw}iIF zU{MwLi)&nlQ4VW{{JhioUuuo-1-cSaAnb^(d*3_y4(}dAYGxab)%xeU5_+D}gm020 zAJYvTL+`E>u5M_K6lv1X22kyv3=kWXnY20RYM^bDOY5R%EKa@oF*uR{qn8qKcXZL_ zkEo6w`o}q5>%3{1>^7{KI)aEyxVx;ui^Lhz2Mdx)6u_+torodBp1OL{8Z2BL#?1z) zbT5)Nwvlrx@`^q$hO@1M-jEYgaMo-;5@Wjo9`u$r-QEYolx}2KIu*c5jBdLkqxKkh zcO6UC{3~MRyr`rhoV1n6lp0SdJG&JxtShhw)W(V~D)FMoxls{HZL-%kjOqxif7s)z zf)H*tXU;Q25gf<+LIcCO6QGw-0OJX*x$`xnb zNq%}!i4WkEw9$twXQ0eE9viwkn#t2@R-+xxwz~1Xgb{(2yXTE}dY#(ql!9}}+1yVG)JFhIYH<$8_8kZf+F)W5|1$!uiJ97O;Uu+9;gUa* z0lbCTKbD-eq+7|bvS>@j>=@CfktI`;Rsv3@jR7jA`SxJ368bmZYlZIl4gEQ0tECFg zs(&NALr@|-Tgm!*#0Ie|bvjdLlbth(5=vsjM3UQyAq?WIC3H0Pw6suTe**oMKvo{` zU9E^Ku%(1_N1n*%Rv;$No2k&-4NnLHiF=#{E(b&z*61ZT99dihv6ts%)dDnJytA(&m$lt8fp03Skc6 zJC-*RFY6Qt%}stwOjrcT@MCqR*lpuY?9QRzo-WFex*6mu%~C%p)u2r${G_ustfs*R zLk@);Rh31Nh6_4m1qRZP(nItjO3qOWE_$G8$PMa|Pxg{W_GF~q%)8IJFjWije+a^`bTCxE9`W_%7;WU>|4Mq?F=GZvw z-6sx&hsl9DtC8hyYa(veBJ(?yS})=+Hpj7!iHqOq4uCQ($n?de4v_g<+ zAKKJ84TL>0-+XSB9N^&z5xkiL-UwRI>i2VsG%HYLBqzbKx@RQIuXUh6r)j+FwBfIn z$@Z$e96k5K1*lKbC^29q-u^rSG;X7}UPsoq=>jA#o-<)BrSLyVN1xRZeGU4Voxw3G zPgKrE*?BP`4LWzjkm^^Wa3!gNoc9_xnFW-?X$ibkLpU1d5_58*xw zky}C__+Jj~$D&cct|<(M7VT9`6+5fOI=I(Zb*1m~n2n8#%jP^$U@fCP2u+I6f(@!UWZea-dM3UugMi=Xr& zSn0_Vnk5<$FeSw9rf}1z4mD1WcJ3f1?iPH@)~?XKOok9sH%b-V++?`I6RVuKR@2p3 zPQxdzeqqRvSG@tCGkpKpry}T$sQrH=-3H)|fS# zd0q+k(;TKWtgcm&!X+-6WR}s-%x0e)DUi}mk3RC7z}aA~BL!!vy=$o0(H-l@$zj$# z;8>_WHmui}H}M=4g?`RC-H&=DU1h@lOwtQCKa8%?IGUyb(i*5&wmKa`~~ z4rNR_e%20gyMt~vXw}e47N3ey?7`}IosgkZOV{=g&buBS>g?_5asq|mBLFSmLc;QibPWd8A1814+CQz+@#v z?sX)BOvNCMkdq+=9-Os1ujsu`YXIIC1TascG2uxa>X5L5I!|jmkT1s*wGM8KCRp@C z7;u;iPJRj^<3*hmITphw?S4U37#Hc~n=iDK zh=;k%3=Ci*c%uPcYnS3wLNQMLFJVj@bp*yql zZ9s(9s!ywf2ZyM=R+P+BF!G^$rDzs%GA2|`L7`>4vdUnt0>HGUcSBw7gUan?8PhgQ zdo~V4y}?Zn(+%ic1`%$uD_~dMQkp4BrTSVo!gr+Lq z3-6z)^+HT84CJY1KR=)bW{iR!KLb_)oe<$H1k=D_6p!A#k=3cGI(Gz*z~(;#EpsZm zuYfJX&3q08x5?m*n1M^1%PD-PPmUk)`X0D8=CrFGKEfzX)zj1GtEvZIik(;mD(JWd z16f-6lBpplM+p`7GZ>MS5ImO%+iBygOI*zRTpl1k&iwTS(3cd zX;xB2Ld}XJ!tuKq<5!l!II_AB~FUSgmOwt1qVpRym20qn|jIC-B@&so@H%)BN z31s&i$AkqUMU%un92sEJ{3aS&Fsc-Pzp}7euLYAGF2OfAFhqVbsu%hfMtMTN*NF zT+s#7)GpeBVzg5ylvl}~mFy{)t$=sW6<3s;MppI$Yv0O4ub))WOExX&1wN_9i}j{f z4+QcREi(4Tq~|fAxr)s_NM~{w)9o@h3Znx$2*s@_vXA#oi=L)1344hhsY`JpD#}vU z%noriMDLYd^gJN+q(3Tj;wxFKj$e>|I1*S!T>*9hj<)h`tHtyLOg7$yuMSME!dqj} zGNHtmu$TGzJ6mS+;dF64Q|7~1BeN3(D<-g!{FL7kAigOAt;!QU7i(i^qt(rgG(mJc z#^#j8HYOk%?Z$Qp2}e@0ZI@m}8D4AU%#2#p&yw6=a?_jE! zpyMX{Mp;=k3a4l#XQ%<>)uZkl(@YaSE0eV%^uc7kk{feI(?r5kzE6ew%T3tmbyeZH z%BhjB>4TBNec+ZEjzlGgInge+q+p#6tJ#=xrlH8-%0B$e;(viBW)xRZQany6AZ3A- znxVr&X~vymm27a=5g(ekOq*D0R*EF-VbdqGH{<)2Czb3?(Gt z^6&~(GXvAdd9XNyyJQ(TAL&1pHe-J&4)!H~V`Z zt_&F?iHmt8;e0R^-?Rz&t#=z;44u!h8NYE5@1N=6_&$zYpmS?{go=hFv8)xx;BMZX z8_S{Rofr&05UVtZ6uRn)gv2|iFuv|HpeY%=8Rw$btBB4!;N>Yc;R1;T0}eUE{njd{ z^tKtXVbW3FN@bzPSdTVGrik>AjP`wNboRz^SUCme6c1w9sgDIV+piE~S05h&FYU~s z1DyQk1K#F8b_RUKP>_-eCmR&wF*N7gbJgt6##5f-Yk=b|!*HOwvqZBUsP0se2YP!T zmetTLdOJ%yOT#8rLqQ+ry=)#drW2s@JivD&J2oCtq}U_gI_Ii)nR%X`ejLK{*h`5* zcmVxPYdJh4tw&7GDn=F|eG~{3X*x@DQXkl4u9v*A4v2fOIFdsFFe!b@!aQ&_L9D)DB(xWQB+j8BHG$2f zo(GxfQ&)}{kh(!|{789;>qSank*Gnif*PMl$Mb-4Ust4Rwhr4t$#|=NtLD4U6xUQW z*5+}M&tAWnhNcPUTY{jrCG;hN-*M=sgtHCj3VhTEnC~;R!+&ly8FX@khlS}YRUOW< iF_)7YC4CqR+CJr1W79YgVN>l|L?r8KYW@ + + + + TestPlugin + + + Example Plugin + Příkladový doplněk + + + + Example minimal plugin + Ukázkový minimální doplněk + + + + Very simple minimal plugin example + Velice jednoduchý minimální doplněk + + + + My first plugin action + Moje první akce z doplňku + + + + Hello + Ahoj + + + + First plugin action works :-) + První akce funguje :-) + + + diff --git a/plugins/TestPlugin/sk_SK.ts b/plugins/TestPlugin/sk_SK.ts new file mode 100644 index 000000000..5fdca9835 --- /dev/null +++ b/plugins/TestPlugin/sk_SK.ts @@ -0,0 +1,37 @@ + + + + + TestPlugin + + + Example Plugin + Ukázkovy doplnek + + + + Example minimal plugin + Ukázkovy minimálny doplnek + + + + Very simple minimal plugin example + Vel'mi jednoduchy minimálny doplnek + + + + My first plugin action + Moje první akce z doplnku + + + + Hello + Ahoj + + + + First plugin action works :-) + První doplnek funguje :-) + + + diff --git a/plugins/TestPlugin/src/TestPlugin.pro b/plugins/TestPlugin/src/TestPlugin.pro new file mode 100644 index 000000000..34533be62 --- /dev/null +++ b/plugins/TestPlugin/src/TestPlugin.pro @@ -0,0 +1,21 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2011-02-13T10:23:13 +# +#------------------------------------------------- +QT += network webkit + +TEMPLATE = lib + +CONFIG += plugin + +TARGET = ExamplePlugin + +DESTDIR = ../../../bin/plugins + +SOURCES += testplugin.cpp + +HEADERS += testplugin.h + +RESOURCES += \ + data.qrc diff --git a/plugins/TestPlugin/src/cs_CZ.qm b/plugins/TestPlugin/src/cs_CZ.qm new file mode 100644 index 0000000000000000000000000000000000000000..4bb57ebfdbb598540ec4715b8868df5c7ace9991 GIT binary patch literal 657 zcmcE7ks@*G{hX<16=n7(EZlq7iGhK^fWhyuJ_7@z5=XjuDUjaEgpkWUgs(>WSuv~^r zhCCq71&RZW1X_p70x_5cxtV#HxrsRn1vo9zVF+L-0=gDrTq4lAWQJ6r!D$SoK(TZn znFZ!sG3YXA!hIm?mRVF>0yS14F}Wl&KTn}NzbLy{!Ae&Xn};|U92qizUde)+!RnEk zlar54tr3GSNF~_b0<5m9LU^6g1L)>bxT#{kl?rK?j>Be+8$%e-iX32wLc9#JFa_x8 td>~m06w3fdA@QNE6qZ_4sZg8=j#|`^S4f4#IJz@BKq-Qe@jnX_3jph5m7o9s literal 0 HcmV?d00001 diff --git a/plugins/TestPlugin/src/data.qrc b/plugins/TestPlugin/src/data.qrc new file mode 100644 index 000000000..55e60431e --- /dev/null +++ b/plugins/TestPlugin/src/data.qrc @@ -0,0 +1,7 @@ + + + cs_CZ.qm + qupzilla.png + sk_SK.qm + + diff --git a/plugins/TestPlugin/src/sk_SK.qm b/plugins/TestPlugin/src/sk_SK.qm new file mode 100644 index 0000000000000000000000000000000000000000..f50f1a816cae2f8d7b7fe19f4db8fbcc73a4ac54 GIT binary patch literal 659 zcmcE7ks@*G{hX<16=n7(EZlq7iGhK^fWhyuJ_7@z3P-wmDUjaI)_ec>3kqr3bZH# m9L~gty;4|eQKdq0COCpo!(Sm467T5F=m4b)M#ldvOe_F?ew3yF literal 0 HcmV?d00001 diff --git a/plugins/TestPlugin/src/testplugin.cpp b/plugins/TestPlugin/src/testplugin.cpp new file mode 100644 index 000000000..440cdf895 --- /dev/null +++ b/plugins/TestPlugin/src/testplugin.cpp @@ -0,0 +1,68 @@ +#include "testplugin.h" + +void TestPlugin::init(QString sPath) +{ + settingsPath = sPath; + //This function is called right after plugin is loaded + qDebug() << __FUNCTION__ << "called"; +} + +bool TestPlugin::testPlugin() +{ + //This function is loaded right after init() + //There should be some testing if plugin is loaded correctly + //If this function returns false, plugin is automatically unloaded + + return true; +} + +QTranslator* TestPlugin::getTranslator(QString locale) +{ + QTranslator* translator = new QTranslator(); + translator->load(":/"+locale); + return translator; +} + +void TestPlugin::showSettings() +{ + QWidget* widget = new QWidget(); + new QLabel("Example Plugin v0.0.1", widget); + widget->resize(200,200); + widget->setAttribute(Qt::WA_DeleteOnClose); + widget->setWindowModality(Qt::WindowModal); //As the preferences window is modal too + widget->setWindowTitle("Example Plugin Settings"); + widget->setWindowIcon(pluginIcon()); + widget->show(); +} + +void TestPlugin::populateWebViewMenu(QMenu *menu, QWebView *view, QWebHitTestResult r) +{ + Q_UNUSED(view) + QString title; + if (!r.imageUrl().isEmpty()) + title += " on image"; + if (!r.linkUrl().isEmpty()) + title += " on link"; + QWebElement element = r.element(); + if (!element.isNull() && (element.tagName().toLower() == "input" || element.tagName().toLower() == "textarea")) + title += " on input"; + menu->addAction(tr("My first plugin action") + title, this, SLOT(actionSlot())); +} + +void TestPlugin::populateHelpMenu(QMenu *menu) +{ + menu->addAction(tr("My first plugin action"), this, SLOT(actionSlot())); +} + +void TestPlugin::populateToolsMenu(QMenu *menu) +{ + menu->addAction(tr("My first plugin action"), this, SLOT(actionSlot())); +} + +void TestPlugin::actionSlot() +{ + QMessageBox::information(0, tr("Hello"), tr("First plugin action works :-)")); +} + +//Export plugin macro +Q_EXPORT_PLUGIN2(ExamplePlugin, TestPlugin) diff --git a/plugins/TestPlugin/src/testplugin.h b/plugins/TestPlugin/src/testplugin.h new file mode 100644 index 000000000..c6fa093bb --- /dev/null +++ b/plugins/TestPlugin/src/testplugin.h @@ -0,0 +1,47 @@ +#ifndef TESTPLUGIN_H +#define TESTPLUGIN_H + +//Include actual plugininterface.h for your version of QupZilla +//This file is available to download at QupZilla website + +#include "../../../src/plugins/plugininterface.h" + + +//For clean plugin directory, please build necessary files into +//plugin in .qrc data files + +#include +#include +#include +#include + +class TestPlugin : public QObject, public PluginInterface +{ + Q_OBJECT + Q_INTERFACES(PluginInterface) + +public: + QString pluginName() { return tr("Example Plugin"); } + QString pluginInfo() { return tr("Example minimal plugin"); } + QString pluginDescription() { return tr("Very simple minimal plugin example"); } + QString pluginVersion() { return "0.0.1"; } + QString pluginAuthor() { return "nowrep "; } + void init(QString sPath); + bool testPlugin(); + + QTranslator* getTranslator(QString locale); + QIcon pluginIcon() { return QIcon(":/qupzilla.png"); } + bool hasSettings() { return true; } + void showSettings(); + + void populateWebViewMenu(QMenu *menu, QWebView *view, QWebHitTestResult r); + void populateHelpMenu(QMenu *menu); + void populateToolsMenu(QMenu *menu); + +private slots: + void actionSlot(); +private: + QString settingsPath; +}; + +#endif // TESTPLUGIN_H diff --git a/src/3rdparty/ecwin7.cpp b/src/3rdparty/ecwin7.cpp new file mode 100644 index 000000000..76755444b --- /dev/null +++ b/src/3rdparty/ecwin7.cpp @@ -0,0 +1,96 @@ +/* EcWin7 - Support library for integrating Windows 7 taskbar features + * into any Qt application + * Copyright (C) 2010 Emanuele Colombo + * + * 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 of the License, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "ecwin7.h" + +// Windows only definitions +#ifdef Q_WS_WIN +DEFINE_GUID(CLSID_TaskbarList,0x56fdf344,0xfd6d,0x11d0,0x95,0x8a,0x0,0x60,0x97,0xc9,0xa0,0x90); +DEFINE_GUID(IID_ITaskbarList3,0xea1afb91,0x9e28,0x4b86,0x90,0xE9,0x9e,0x9f,0x8a,0x5e,0xef,0xaf); + +// Constructor: variabiles initialization +EcWin7::EcWin7() +{ + mOverlayIcon = NULL; +} + +// Init taskbar communication +void EcWin7::init(WId wid) +{ + mWindowId = wid; + mTaskbarMessageId = RegisterWindowMessage(L"TaskbarButtonCreated"); +} + +// Windows event handler callback function +// (handles taskbar communication initial message) +bool EcWin7::winEvent(MSG * message, long * result) +{ + if (message->message == mTaskbarMessageId) + { + HRESULT hr = CoCreateInstance(CLSID_TaskbarList, + 0, + CLSCTX_INPROC_SERVER, + IID_ITaskbarList3, + reinterpret_cast (&(mTaskbar))); + *result = hr; + return true; + } + return false; +} + +// Set progress bar current value +void EcWin7::setProgressValue(int value, int max) +{ + mTaskbar->SetProgressValue(mWindowId, value, max); + +} + +// Set progress bar current state (active, error, pause, ecc...) +void EcWin7::setProgressState(ToolBarProgressState state) +{ + mTaskbar->SetProgressState(mWindowId, (TBPFLAG)state); +} + +// Set new overlay icon and corresponding description (for accessibility) +// (call with iconName == "" and description == "" to remove any previous overlay icon) +void EcWin7::setOverlayIcon(QString iconName, QString description) +{ + HICON oldIcon = NULL; + if (mOverlayIcon != NULL) oldIcon = mOverlayIcon; + if (iconName == "") + { + mTaskbar->SetOverlayIcon(mWindowId, NULL, NULL); + mOverlayIcon = NULL; + } + else + { + mOverlayIcon = (HICON) LoadImage(GetModuleHandle(NULL), + iconName.toStdWString().c_str(), + IMAGE_ICON, + 0, + 0, + NULL); + mTaskbar->SetOverlayIcon(mWindowId, mOverlayIcon, description.toStdWString().c_str()); + } + if ((oldIcon != NULL) && (oldIcon != mOverlayIcon)) + { + DestroyIcon(oldIcon); + } +} +#endif diff --git a/src/3rdparty/ecwin7.h b/src/3rdparty/ecwin7.h new file mode 100644 index 000000000..1359143e9 --- /dev/null +++ b/src/3rdparty/ecwin7.h @@ -0,0 +1,141 @@ +/* EcWin7 - Support library for integrating Windows 7 taskbar features + * into any Qt application + * Copyright (C) 2010 Emanuele Colombo + * + * 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 of the License, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef ECWIN7_H +#define ECWIN7_H + +#include +#include + +// Windows only data definitions +#ifdef Q_WS_WIN + +#include +#include +#define CMIC_MASK_ASYNCOK SEE_MASK_ASYNCOK + +// Structs types and enums definitions for Windows 7 taskbar + +typedef enum THUMBBUTTONMASK +{ + THB_BITMAP = 0x1, + THB_ICON = 0x2, + THB_TOOLTIP = 0x4, + THB_FLAGS = 0x8 +} THUMBBUTTONMASK; + +typedef enum THUMBBUTTONFLAGS +{ + THBF_ENABLED = 0, + THBF_DISABLED = 0x1, + THBF_DISMISSONCLICK = 0x2, + THBF_NOBACKGROUND = 0x4, + THBF_HIDDEN = 0x8, + THBF_NONINTERACTIVE = 0x10 +} THUMBBUTTONFLAGS; + +typedef struct THUMBBUTTON +{ + THUMBBUTTONMASK dwMask; + UINT iId; + UINT iBitmap; + HICON hIcon; + WCHAR szTip[260]; + THUMBBUTTONFLAGS dwFlags; +} THUMBBUTTON; +typedef struct THUMBBUTTON *LPTHUMBBUTTON; + +typedef enum TBPFLAG +{ + TBPF_NOPROGRESS = 0, + TBPF_INDETERMINATE = 0x1, + TBPF_NORMAL = 0x2, + TBPF_ERROR = 0x4, + TBPF_PAUSED = 0x8 +} TBPFLAG; + +typedef IUnknown *HIMAGELIST; + +// Taskbar interface +DECLARE_INTERFACE_(ITaskbarList3,IUnknown) +{ + // IUnknown + STDMETHOD(QueryInterface) (THIS_ REFIID riid,void **ppv) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + // ITaskbarList + STDMETHOD(HrInit) (THIS) PURE; + STDMETHOD(AddTab) (THIS_ HWND hwnd) PURE; + STDMETHOD(DeleteTab) (THIS_ HWND hwnd) PURE; + STDMETHOD(ActivateTab) (THIS_ HWND hwnd) PURE; + STDMETHOD(SetActiveAlt) (THIS_ HWND hwnd) PURE; + STDMETHOD (MarkFullscreenWindow) (THIS_ HWND hwnd, int fFullscreen) PURE; + // ITaskbarList3 + STDMETHOD (SetProgressValue) (THIS_ HWND hwnd, ULONGLONG ullCompleted, ULONGLONG ullTotal) PURE; + STDMETHOD (SetProgressState) (THIS_ HWND hwnd, TBPFLAG tbpFlags) PURE; + STDMETHOD (RegisterTab) (THIS_ HWND hwndTab,HWND hwndMDI) PURE; + STDMETHOD (UnregisterTab) (THIS_ HWND hwndTab) PURE; + STDMETHOD (SetTabOrder) (THIS_ HWND hwndTab, HWND hwndInsertBefore) PURE; + STDMETHOD (SetTabActive) (THIS_ HWND hwndTab, HWND hwndMDI, DWORD dwReserved) PURE; + STDMETHOD (ThumbBarAddButtons) (THIS_ HWND hwnd, UINT cButtons, LPTHUMBBUTTON pButton) PURE; + STDMETHOD (ThumbBarUpdateButtons) (THIS_ HWND hwnd, UINT cButtons, LPTHUMBBUTTON pButton) PURE; + STDMETHOD (ThumbBarSetImageList) (THIS_ HWND hwnd, HIMAGELIST himl) PURE; + STDMETHOD (SetOverlayIcon) (THIS_ HWND hwnd, HICON hIcon, LPCWSTR pszDescription) PURE; + STDMETHOD (SetThumbnailTooltip) (THIS_ HWND hwnd, LPCWSTR pszTip) PURE; + STDMETHOD (SetThumbnailClip) (THIS_ HWND hwnd, RECT *prcClip) PURE; +}; +typedef ITaskbarList3 *LPITaskbarList3; + + +// ******************************************************************** +// EcWin7 class - Windows 7 taskbar handling for Qt and MinGW + +class EcWin7 +{ +public: + + // Initialization methods + EcWin7(); + void init(WId wid); + bool winEvent(MSG * message, long * result); + + // Overlay icon handling + void setOverlayIcon(QString iconName, QString description); + + // Progress indicator handling + enum ToolBarProgressState { + NoProgress = 0, + Indeterminate = 1, + Normal = 2, + Error = 4, + Paused = 8 + }; + void setProgressValue(int value, int max); + void setProgressState(ToolBarProgressState state); + +private: + WId mWindowId; + UINT mTaskbarMessageId; + ITaskbarList3 *mTaskbar; + HICON mOverlayIcon; +}; +// Windows only data definitions - END +#endif + +#endif // ECWIN7_H diff --git a/src/3rdparty/lineedit.cpp b/src/3rdparty/lineedit.cpp new file mode 100644 index 000000000..0cd2330bf --- /dev/null +++ b/src/3rdparty/lineedit.cpp @@ -0,0 +1,214 @@ +#include "lineedit.h" + +#include +#include +#include +#include +#include + +#include +SideWidget::SideWidget(QWidget *parent) + : QWidget(parent) +{ +} + +bool SideWidget::event(QEvent *event) +{ + if (event->type() == QEvent::LayoutRequest) + emit sizeHintChanged(); + return QWidget::event(event); +} + +LineEdit::LineEdit(QWidget *parent) + : QLineEdit(parent) + , m_leftLayout(0) + , m_rightLayout(0) + , m_leftMargin(0) +{ + init(); +} + +LineEdit::LineEdit(const QString &contents, QWidget *parent) + : QLineEdit(contents, parent) + , m_leftWidget(0) + , m_rightWidget(0) + , m_leftLayout(0) + , m_rightLayout(0) + , m_leftMargin(0) +{ + init(); +} + +void LineEdit::setLeftMargin(int margin) +{ + m_leftMargin = margin; +} + +void LineEdit::init() +{ + m_leftWidget = new SideWidget(this); + m_leftWidget->resize(0, 0); + m_leftLayout = new QHBoxLayout(m_leftWidget); + m_leftLayout->setContentsMargins(0, 0, 0, 0); + + if (isRightToLeft()) + m_leftLayout->setDirection(QBoxLayout::RightToLeft); + else + m_leftLayout->setDirection(QBoxLayout::LeftToRight); + m_leftLayout->setSizeConstraint(QLayout::SetFixedSize); + + m_rightWidget = new SideWidget(this); + m_rightWidget->resize(0, 0); + m_rightLayout = new QHBoxLayout(m_rightWidget); + if (isRightToLeft()) + m_rightLayout->setDirection(QBoxLayout::RightToLeft); + else + m_rightLayout->setDirection(QBoxLayout::LeftToRight); + m_rightLayout->setContentsMargins(0, 0, 0, 0); + + QSpacerItem *horizontalSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum); + m_rightLayout->addItem(horizontalSpacer); + + setWidgetSpacing(3); + connect(m_leftWidget, SIGNAL(sizeHintChanged()), + this, SLOT(updateTextMargins())); + connect(m_rightWidget, SIGNAL(sizeHintChanged()), + this, SLOT(updateTextMargins())); +} + +bool LineEdit::event(QEvent *event) +{ + if (event->type() == QEvent::LayoutDirectionChange) { + if (isRightToLeft()) { + m_leftLayout->setDirection(QBoxLayout::RightToLeft); + m_rightLayout->setDirection(QBoxLayout::RightToLeft); + } else { + m_leftLayout->setDirection(QBoxLayout::LeftToRight); + m_rightLayout->setDirection(QBoxLayout::LeftToRight); + } + } + return QLineEdit::event(event); +} + +void LineEdit::addWidget(QWidget *widget, WidgetPosition position) +{ + if (!widget) + return; + + bool rtl = isRightToLeft(); + if (rtl) + position = (position == LeftSide) ? RightSide : LeftSide; + if (position == LeftSide) { + m_leftLayout->addWidget(widget); + } else { + m_rightLayout->insertWidget(1, widget); + } +} + +void LineEdit::removeWidget(QWidget *widget) +{ + if (!widget) + return; + + m_leftLayout->removeWidget(widget); + m_rightLayout->removeWidget(widget); + widget->hide(); +} + +void LineEdit::setWidgetSpacing(int spacing) +{ + m_leftLayout->setSpacing(spacing); + m_rightLayout->setSpacing(spacing); + updateTextMargins(); +} + +int LineEdit::widgetSpacing() const +{ + return m_leftLayout->spacing(); +} + +int LineEdit::textMargin(WidgetPosition position) const +{ + int spacing = m_rightLayout->spacing(); + int w = 0; + if (position == LeftSide) + w = m_leftWidget->sizeHint().width(); + else + w = m_rightWidget->sizeHint().width(); + if (w == 0) + return 0; + return w + spacing * 2; +} + +void LineEdit::updateTextMargins() +{ + int left; + if (m_leftMargin == 0) + left = textMargin(LineEdit::LeftSide); + else + left = m_leftMargin; + int right = textMargin(LineEdit::RightSide); + int top = 0; + int bottom = 0; + setTextMargins(left, top, right, bottom); + updateSideWidgetLocations(); +} + +void LineEdit::updateSideWidgetLocations() +{ + QStyleOptionFrameV2 opt; + initStyleOption(&opt); + QRect textRect = style()->subElementRect(QStyle::SE_LineEditContents, &opt, this); + int spacing = m_rightLayout->spacing(); + textRect.adjust(spacing, 0, -spacing, 0); + + int left = textMargin(LineEdit::LeftSide); + + int midHeight = textRect.center().y() + 1; + + if (m_leftLayout->count() > 0) { + int leftHeight = midHeight - m_leftWidget->height() / 2; + int leftWidth = m_leftWidget->width(); + if (leftWidth == 0) + leftHeight = midHeight - m_leftWidget->sizeHint().height() / 2; + m_leftWidget->move(textRect.x(), leftHeight); + } + textRect.setX(left); + textRect.setY(midHeight - m_rightWidget->sizeHint().height() / 2); + textRect.setHeight(m_rightWidget->sizeHint().height()); + m_rightWidget->setGeometry(textRect); +} + +void LineEdit::resizeEvent(QResizeEvent *event) +{ + updateSideWidgetLocations(); + QLineEdit::resizeEvent(event); +} + +QString LineEdit::inactiveText() const +{ + return m_inactiveText; +} + +void LineEdit::setInactiveText(const QString &text) +{ + m_inactiveText = text; +} + +void LineEdit::paintEvent(QPaintEvent *event) +{ + QLineEdit::paintEvent(event); + if (text().isEmpty() && !m_inactiveText.isEmpty() && !hasFocus()) { + QStyleOptionFrameV2 panel; + initStyleOption(&panel); + QRect textRect = style()->subElementRect(QStyle::SE_LineEditContents, &panel, this); + int horizontalMargin = 2; + textRect.adjust(horizontalMargin, 0, 0, 0); + int left = textMargin(LineEdit::LeftSide); + int right = textMargin(LineEdit::RightSide); + textRect.adjust(left, 0, -right, 0); + QPainter painter(this); + painter.setPen(palette().brush(QPalette::Disabled, QPalette::Text).color()); + painter.drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, m_inactiveText); + } +} diff --git a/src/3rdparty/lineedit.h b/src/3rdparty/lineedit.h new file mode 100644 index 000000000..62ac72cef --- /dev/null +++ b/src/3rdparty/lineedit.h @@ -0,0 +1,107 @@ +#ifndef LINEEDIT_H +#define LINEEDIT_H +/** +* Copyright (c) 2008 - 2009, Benjamin C. Meyer +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* 3. Neither the name of the Benjamin Meyer nor the names of its contributors +* may be used to endorse or promote products derived from this software +* without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +* SUCH DAMAGE. +*/ + +#include + +class QHBoxLayout; + +/* +LineEdit is a subclass of QLineEdit that provides an easy and simple +way to add widgets on the left or right hand side of the text. + +The layout of the widgets on either side are handled by a QHBoxLayout. +You can set the spacing around the widgets with setWidgetSpacing(). + +As widgets are added to the class they are inserted from the outside +into the center of the widget. +*/ +class SideWidget; +class LineEdit : public QLineEdit +{ + Q_OBJECT + Q_PROPERTY(QString inactiveText READ inactiveText WRITE setInactiveText) + +public: + enum WidgetPosition { + LeftSide, + RightSide + }; + + LineEdit(QWidget *parent = 0); + LineEdit(const QString &contents, QWidget *parent = 0); + + void addWidget(QWidget *widget, WidgetPosition position); + void removeWidget(QWidget *widget); + void setWidgetSpacing(int spacing); + int widgetSpacing() const; + int textMargin(WidgetPosition position) const; + QString inactiveText() const; + void setInactiveText(const QString &text); + + void paintEvent(QPaintEvent *event); + + void setLeftMargin(int margin); + +protected: + void resizeEvent(QResizeEvent *event); + bool event(QEvent *event); + +protected slots: + void updateTextMargins(); + +private: + void init(); + void updateSideWidgetLocations(); + + SideWidget *m_leftWidget; + SideWidget *m_rightWidget; + QHBoxLayout *m_leftLayout; + QHBoxLayout *m_rightLayout; + QString m_inactiveText; + int m_leftMargin; +}; + + +class SideWidget : public QWidget +{ + Q_OBJECT + +signals: + void sizeHintChanged(); + +public: + SideWidget(QWidget *parent = 0); + +protected: + bool event(QEvent *event); + +}; + +#endif // LINEEDIT_H diff --git a/src/3rdparty/qtlocalpeer.cpp b/src/3rdparty/qtlocalpeer.cpp new file mode 100644 index 000000000..f531956f3 --- /dev/null +++ b/src/3rdparty/qtlocalpeer.cpp @@ -0,0 +1,203 @@ +/**************************************************************************** +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Solutions Commercial License Agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.1, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** Please note Third Party Software included with Qt Solutions may impose +** additional restrictions and it is the user's responsibility to ensure +** that they have met the licensing requirements of the GPL, LGPL, or Qt +** Solutions Commercial license and the relevant license of the Third +** Party Software they are using. +** +** If you are unsure which license is appropriate for your use, please +** contact Nokia at qt-info@nokia.com. +** +****************************************************************************/ + + +#include "qtlocalpeer.h" +#include +#include + +#if defined(Q_OS_WIN) +#include +#include +typedef BOOL(WINAPI*PProcessIdToSessionId)(DWORD,DWORD*); +static PProcessIdToSessionId pProcessIdToSessionId = 0; +#endif +#if defined(Q_OS_UNIX) +#include +#endif + +namespace QtLP_Private { +#include "qtlockedfile.cpp" +#if defined(Q_OS_WIN) +#include "qtlockedfile_win.cpp" +#else +#include "qtlockedfile_unix.cpp" +#endif +} + +const char* QtLocalPeer::ack = "ack"; + +QtLocalPeer::QtLocalPeer(QObject* parent, const QString &appId) + : QObject(parent), id(appId) +{ + QString prefix = id; + if (id.isEmpty()) { + id = QCoreApplication::applicationFilePath(); +#if defined(Q_OS_WIN) + id = id.toLower(); +#endif + prefix = id.section(QLatin1Char('/'), -1); + } + prefix.remove(QRegExp("[^a-zA-Z]")); + prefix.truncate(6); + + QByteArray idc = id.toUtf8(); + quint16 idNum = qChecksum(idc.constData(), idc.size()); + socketName = QLatin1String("qtsingleapp-") + prefix + + QLatin1Char('-') + QString::number(idNum, 16); + +#if defined(Q_OS_WIN) + if (!pProcessIdToSessionId) { + QLibrary lib("kernel32"); + pProcessIdToSessionId = (PProcessIdToSessionId)lib.resolve("ProcessIdToSessionId"); + } + if (pProcessIdToSessionId) { + DWORD sessionId = 0; + pProcessIdToSessionId(GetCurrentProcessId(), &sessionId); + socketName += QLatin1Char('-') + QString::number(sessionId, 16); + } +#else + socketName += QLatin1Char('-') + QString::number(::getuid(), 16); +#endif + + server = new QLocalServer(this); + QString lockName = QDir(QDir::tempPath()).absolutePath() + + QLatin1Char('/') + socketName + + QLatin1String("-lockfile"); + lockFile.setFileName(lockName); + lockFile.open(QIODevice::ReadWrite); +} + + + +bool QtLocalPeer::isClient() +{ + if (lockFile.isLocked()) + return false; + + if (!lockFile.lock(QtLP_Private::QtLockedFile::WriteLock, false)) + return true; + + bool res = server->listen(socketName); +#if defined(Q_OS_UNIX) && (QT_VERSION >= QT_VERSION_CHECK(4,5,0)) + // ### Workaround + if (!res && server->serverError() == QAbstractSocket::AddressInUseError) { + QFile::remove(QDir::cleanPath(QDir::tempPath())+QLatin1Char('/')+socketName); + res = server->listen(socketName); + } +#endif + if (!res) + qWarning("QtSingleCoreApplication: listen on local socket failed, %s", qPrintable(server->errorString())); + QObject::connect(server, SIGNAL(newConnection()), SLOT(receiveConnection())); + return false; +} + + +bool QtLocalPeer::sendMessage(const QString &message, int timeout) +{ + if (!isClient()) + return false; + + QLocalSocket socket; + bool connOk = false; + for(int i = 0; i < 2; i++) { + // Try twice, in case the other instance is just starting up + socket.connectToServer(socketName); + connOk = socket.waitForConnected(timeout/2); + if (connOk || i) + break; + int ms = 250; +#if defined(Q_OS_WIN) + Sleep(DWORD(ms)); +#else + struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 }; + nanosleep(&ts, NULL); +#endif + } + if (!connOk) + return false; + + QByteArray uMsg(message.toUtf8()); + QDataStream ds(&socket); + ds.writeBytes(uMsg.constData(), uMsg.size()); + bool res = socket.waitForBytesWritten(timeout); + res &= socket.waitForReadyRead(timeout); // wait for ack + res &= (socket.read(qstrlen(ack)) == ack); + return res; +} + + +void QtLocalPeer::receiveConnection() +{ + QLocalSocket* socket = server->nextPendingConnection(); + if (!socket) + return; + + while (socket->bytesAvailable() < (int)sizeof(quint32)) + socket->waitForReadyRead(); + QDataStream ds(socket); + QByteArray uMsg; + quint32 remaining; + ds >> remaining; + uMsg.resize(remaining); + int got = 0; + char* uMsgBuf = uMsg.data(); + do { + got = ds.readRawData(uMsgBuf, remaining); + remaining -= got; + uMsgBuf += got; + } while (remaining && got >= 0 && socket->waitForReadyRead(2000)); + if (got < 0) { + qWarning() << "QtLocalPeer: Message reception failed" << socket->errorString(); + delete socket; + return; + } + QString message(QString::fromUtf8(uMsg)); + socket->write(ack, qstrlen(ack)); + socket->waitForBytesWritten(1000); + delete socket; + emit messageReceived(message); //### (might take a long time to return) +} diff --git a/src/3rdparty/qtlocalpeer.h b/src/3rdparty/qtlocalpeer.h new file mode 100644 index 000000000..8a54a9b5e --- /dev/null +++ b/src/3rdparty/qtlocalpeer.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Solutions Commercial License Agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.1, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** Please note Third Party Software included with Qt Solutions may impose +** additional restrictions and it is the user's responsibility to ensure +** that they have met the licensing requirements of the GPL, LGPL, or Qt +** Solutions Commercial license and the relevant license of the Third +** Party Software they are using. +** +** If you are unsure which license is appropriate for your use, please +** contact Nokia at qt-info@nokia.com. +** +****************************************************************************/ + + +#include +#include +#include + +namespace QtLP_Private { +#include "qtlockedfile.h" +} + +class QtLocalPeer : public QObject +{ + Q_OBJECT + +public: + QtLocalPeer(QObject *parent = 0, const QString &appId = QString()); + bool isClient(); + bool sendMessage(const QString &message, int timeout); + QString applicationId() const + { return id; } + +Q_SIGNALS: + void messageReceived(const QString &message); + +protected Q_SLOTS: + void receiveConnection(); + +protected: + QString id; + QString socketName; + QLocalServer* server; + QtLP_Private::QtLockedFile lockFile; + +private: + static const char* ack; +}; diff --git a/src/3rdparty/qtlockedfile.cpp b/src/3rdparty/qtlockedfile.cpp new file mode 100644 index 000000000..2cf080584 --- /dev/null +++ b/src/3rdparty/qtlockedfile.cpp @@ -0,0 +1,199 @@ +/**************************************************************************** +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Solutions Commercial License Agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.1, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** Please note Third Party Software included with Qt Solutions may impose +** additional restrictions and it is the user's responsibility to ensure +** that they have met the licensing requirements of the GPL, LGPL, or Qt +** Solutions Commercial license and the relevant license of the Third +** Party Software they are using. +** +** If you are unsure which license is appropriate for your use, please +** contact Nokia at qt-info@nokia.com. +** +****************************************************************************/ + +#include "qtlockedfile.h" + +/*! + \class QtLockedFile + + \brief The QtLockedFile class extends QFile with advisory locking + functions. + + A file may be locked in read or write mode. Multiple instances of + \e QtLockedFile, created in multiple processes running on the same + machine, may have a file locked in read mode. Exactly one instance + may have it locked in write mode. A read and a write lock cannot + exist simultaneously on the same file. + + The file locks are advisory. This means that nothing prevents + another process from manipulating a locked file using QFile or + file system functions offered by the OS. Serialization is only + guaranteed if all processes that access the file use + QLockedFile. Also, while holding a lock on a file, a process + must not open the same file again (through any API), or locks + can be unexpectedly lost. + + The lock provided by an instance of \e QtLockedFile is released + whenever the program terminates. This is true even when the + program crashes and no destructors are called. +*/ + +/*! \enum QtLockedFile::LockMode + + This enum describes the available lock modes. + + \value ReadLock A read lock. + \value WriteLock A write lock. + \value NoLock Neither a read lock nor a write lock. +*/ + +/*! + Constructs an unlocked \e QtLockedFile object. This constructor + behaves in the same way as \e QFile::QFile(). + + \sa QFile::QFile() +*/ +QtLockedFile::QtLockedFile() + : QFile() +{ +#ifdef Q_OS_WIN + wmutex = 0; + rmutex = 0; +#endif + m_lock_mode = NoLock; +} + +/*! + Constructs an unlocked QtLockedFile object with file \a name. This + constructor behaves in the same way as \e QFile::QFile(const + QString&). + + \sa QFile::QFile() +*/ +QtLockedFile::QtLockedFile(const QString &name) + : QFile(name) +{ +#ifdef Q_OS_WIN + wmutex = 0; + rmutex = 0; +#endif + m_lock_mode = NoLock; +} + +/*! + Opens the file in OpenMode \a mode. + + This is identical to QFile::open(), with the one exception that the + Truncate mode flag is disallowed. Truncation would conflict with the + advisory file locking, since the file would be modified before the + write lock is obtained. If truncation is required, use resize(0) + after obtaining the write lock. + + Returns true if successful; otherwise false. + + \sa QFile::open(), QFile::resize() +*/ +bool QtLockedFile::open(OpenMode mode) +{ + if (mode & QIODevice::Truncate) { + qWarning("QtLockedFile::open(): Truncate mode not allowed."); + return false; + } + return QFile::open(mode); +} + +/*! + Returns \e true if this object has a in read or write lock; + otherwise returns \e false. + + \sa lockMode() +*/ +bool QtLockedFile::isLocked() const +{ + return m_lock_mode != NoLock; +} + +/*! + Returns the type of lock currently held by this object, or \e + QtLockedFile::NoLock. + + \sa isLocked() +*/ +QtLockedFile::LockMode QtLockedFile::lockMode() const +{ + return m_lock_mode; +} + +/*! + \fn bool QtLockedFile::lock(LockMode mode, bool block = true) + + Obtains a lock of type \a mode. The file must be opened before it + can be locked. + + If \a block is true, this function will block until the lock is + aquired. If \a block is false, this function returns \e false + immediately if the lock cannot be aquired. + + If this object already has a lock of type \a mode, this function + returns \e true immediately. If this object has a lock of a + different type than \a mode, the lock is first released and then a + new lock is obtained. + + This function returns \e true if, after it executes, the file is + locked by this object, and \e false otherwise. + + \sa unlock(), isLocked(), lockMode() +*/ + +/*! + \fn bool QtLockedFile::unlock() + + Releases a lock. + + If the object has no lock, this function returns immediately. + + This function returns \e true if, after it executes, the file is + not locked by this object, and \e false otherwise. + + \sa lock(), isLocked(), lockMode() +*/ + +/*! + \fn QtLockedFile::~QtLockedFile() + + Destroys the \e QtLockedFile object. If any locks were held, they + are released. +*/ diff --git a/src/3rdparty/qtlockedfile.h b/src/3rdparty/qtlockedfile.h new file mode 100644 index 000000000..1d3b918ec --- /dev/null +++ b/src/3rdparty/qtlockedfile.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Solutions Commercial License Agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.1, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** Please note Third Party Software included with Qt Solutions may impose +** additional restrictions and it is the user's responsibility to ensure +** that they have met the licensing requirements of the GPL, LGPL, or Qt +** Solutions Commercial license and the relevant license of the Third +** Party Software they are using. +** +** If you are unsure which license is appropriate for your use, please +** contact Nokia at qt-info@nokia.com. +** +****************************************************************************/ + +#ifndef QTLOCKEDFILE_H +#define QTLOCKEDFILE_H + +#include +#ifdef Q_OS_WIN +#include +#endif + +#if defined(Q_WS_WIN) +# if !defined(QT_QTLOCKEDFILE_EXPORT) && !defined(QT_QTLOCKEDFILE_IMPORT) +# define QT_QTLOCKEDFILE_EXPORT +# elif defined(QT_QTLOCKEDFILE_IMPORT) +# if defined(QT_QTLOCKEDFILE_EXPORT) +# undef QT_QTLOCKEDFILE_EXPORT +# endif +# define QT_QTLOCKEDFILE_EXPORT __declspec(dllimport) +# elif defined(QT_QTLOCKEDFILE_EXPORT) +# undef QT_QTLOCKEDFILE_EXPORT +# define QT_QTLOCKEDFILE_EXPORT __declspec(dllexport) +# endif +#else +# define QT_QTLOCKEDFILE_EXPORT +#endif + +class QT_QTLOCKEDFILE_EXPORT QtLockedFile : public QFile +{ +public: + enum LockMode { NoLock = 0, ReadLock, WriteLock }; + + QtLockedFile(); + QtLockedFile(const QString &name); + ~QtLockedFile(); + + bool open(OpenMode mode); + + bool lock(LockMode mode, bool block = true); + bool unlock(); + bool isLocked() const; + LockMode lockMode() const; + +private: +#ifdef Q_OS_WIN + Qt::HANDLE wmutex; + Qt::HANDLE rmutex; + QVector rmutexes; + QString mutexname; + + Qt::HANDLE getMutexHandle(int idx, bool doCreate); + bool waitMutex(Qt::HANDLE mutex, bool doBlock); + +#endif + LockMode m_lock_mode; +}; + +#endif diff --git a/src/3rdparty/qtlockedfile_unix.cpp b/src/3rdparty/qtlockedfile_unix.cpp new file mode 100644 index 000000000..2881bdd2c --- /dev/null +++ b/src/3rdparty/qtlockedfile_unix.cpp @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Solutions Commercial License Agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.1, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** Please note Third Party Software included with Qt Solutions may impose +** additional restrictions and it is the user's responsibility to ensure +** that they have met the licensing requirements of the GPL, LGPL, or Qt +** Solutions Commercial license and the relevant license of the Third +** Party Software they are using. +** +** If you are unsure which license is appropriate for your use, please +** contact Nokia at qt-info@nokia.com. +** +****************************************************************************/ + +#include +#include +#include +#include + +#include "qtlockedfile.h" + +bool QtLockedFile::lock(LockMode mode, bool block) +{ + if (!isOpen()) { + qWarning("QtLockedFile::lock(): file is not opened"); + return false; + } + + if (mode == NoLock) + return unlock(); + + if (mode == m_lock_mode) + return true; + + if (m_lock_mode != NoLock) + unlock(); + + struct flock fl; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 0; + fl.l_type = (mode == ReadLock) ? F_RDLCK : F_WRLCK; + int cmd = block ? F_SETLKW : F_SETLK; + int ret = fcntl(handle(), cmd, &fl); + + if (ret == -1) { + if (errno != EINTR && errno != EAGAIN) + qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno)); + return false; + } + + + m_lock_mode = mode; + return true; +} + + +bool QtLockedFile::unlock() +{ + if (!isOpen()) { + qWarning("QtLockedFile::unlock(): file is not opened"); + return false; + } + + if (!isLocked()) + return true; + + struct flock fl; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 0; + fl.l_type = F_UNLCK; + int ret = fcntl(handle(), F_SETLKW, &fl); + + if (ret == -1) { + qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno)); + return false; + } + + m_lock_mode = NoLock; + return true; +} + +QtLockedFile::~QtLockedFile() +{ + if (isOpen()) + unlock(); +} + diff --git a/src/3rdparty/qtlockedfile_win.cpp b/src/3rdparty/qtlockedfile_win.cpp new file mode 100644 index 000000000..d4bf9e141 --- /dev/null +++ b/src/3rdparty/qtlockedfile_win.cpp @@ -0,0 +1,213 @@ +/**************************************************************************** +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Solutions Commercial License Agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.1, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** Please note Third Party Software included with Qt Solutions may impose +** additional restrictions and it is the user's responsibility to ensure +** that they have met the licensing requirements of the GPL, LGPL, or Qt +** Solutions Commercial license and the relevant license of the Third +** Party Software they are using. +** +** If you are unsure which license is appropriate for your use, please +** contact Nokia at qt-info@nokia.com. +** +****************************************************************************/ + +#include "qtlockedfile.h" +#include +#include + +#define MUTEX_PREFIX "QtLockedFile mutex " +// Maximum number of concurrent read locks. Must not be greater than MAXIMUM_WAIT_OBJECTS +#define MAX_READERS MAXIMUM_WAIT_OBJECTS + +Qt::HANDLE QtLockedFile::getMutexHandle(int idx, bool doCreate) +{ + if (mutexname.isEmpty()) { + QFileInfo fi(*this); + mutexname = QString::fromLatin1(MUTEX_PREFIX) + + fi.absoluteFilePath().toLower(); + } + QString mname(mutexname); + if (idx >= 0) + mname += QString::number(idx); + + Qt::HANDLE mutex; + if (doCreate) { + QT_WA( { mutex = CreateMutexW(NULL, FALSE, (TCHAR*)mname.utf16()); }, + { mutex = CreateMutexA(NULL, FALSE, mname.toLocal8Bit().constData()); } ); + if (!mutex) { + qErrnoWarning("QtLockedFile::lock(): CreateMutex failed"); + return 0; + } + } + else { + QT_WA( { mutex = OpenMutexW(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, (TCHAR*)mname.utf16()); }, + { mutex = OpenMutexA(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, mname.toLocal8Bit().constData()); } ); + if (!mutex) { + if (GetLastError() != ERROR_FILE_NOT_FOUND) + qErrnoWarning("QtLockedFile::lock(): OpenMutex failed"); + return 0; + } + } + return mutex; +} + +bool QtLockedFile::waitMutex(Qt::HANDLE mutex, bool doBlock) +{ + Q_ASSERT(mutex); + DWORD res = WaitForSingleObject(mutex, doBlock ? INFINITE : 0); + switch (res) { + case WAIT_OBJECT_0: + case WAIT_ABANDONED: + return true; + break; + case WAIT_TIMEOUT: + break; + default: + qErrnoWarning("QtLockedFile::lock(): WaitForSingleObject failed"); + } + return false; +} + + + +bool QtLockedFile::lock(LockMode mode, bool block) +{ + if (!isOpen()) { + qWarning("QtLockedFile::lock(): file is not opened"); + return false; + } + + if (mode == NoLock) + return unlock(); + + if (mode == m_lock_mode) + return true; + + if (m_lock_mode != NoLock) + unlock(); + + if (!wmutex && !(wmutex = getMutexHandle(-1, true))) + return false; + + if (!waitMutex(wmutex, block)) + return false; + + if (mode == ReadLock) { + int idx = 0; + for (; idx < MAX_READERS; idx++) { + rmutex = getMutexHandle(idx, false); + if (!rmutex || waitMutex(rmutex, false)) + break; + CloseHandle(rmutex); + } + bool ok = true; + if (idx >= MAX_READERS) { + qWarning("QtLockedFile::lock(): too many readers"); + rmutex = 0; + ok = false; + } + else if (!rmutex) { + rmutex = getMutexHandle(idx, true); + if (!rmutex || !waitMutex(rmutex, false)) + ok = false; + } + if (!ok && rmutex) { + CloseHandle(rmutex); + rmutex = 0; + } + ReleaseMutex(wmutex); + if (!ok) + return false; + } + else { + Q_ASSERT(rmutexes.isEmpty()); + for (int i = 0; i < MAX_READERS; i++) { + Qt::HANDLE mutex = getMutexHandle(i, false); + if (mutex) + rmutexes.append(mutex); + } + if (rmutexes.size()) { + DWORD res = WaitForMultipleObjects(rmutexes.size(), rmutexes.constData(), + TRUE, block ? INFINITE : 0); + if (res != WAIT_OBJECT_0 && res != WAIT_ABANDONED) { + if (res != WAIT_TIMEOUT) + qErrnoWarning("QtLockedFile::lock(): WaitForMultipleObjects failed"); + m_lock_mode = WriteLock; // trick unlock() to clean up - semiyucky + unlock(); + return false; + } + } + } + + m_lock_mode = mode; + return true; +} + +bool QtLockedFile::unlock() +{ + if (!isOpen()) { + qWarning("QtLockedFile::unlock(): file is not opened"); + return false; + } + + if (!isLocked()) + return true; + + if (m_lock_mode == ReadLock) { + ReleaseMutex(rmutex); + CloseHandle(rmutex); + rmutex = 0; + } + else { + foreach(Qt::HANDLE mutex, rmutexes) { + ReleaseMutex(mutex); + CloseHandle(mutex); + } + rmutexes.clear(); + ReleaseMutex(wmutex); + } + + m_lock_mode = QtLockedFile::NoLock; + return true; +} + +QtLockedFile::~QtLockedFile() +{ + if (isOpen()) + unlock(); + if (wmutex) + CloseHandle(wmutex); +} diff --git a/src/3rdparty/qtsingleapplication.cpp b/src/3rdparty/qtsingleapplication.cpp new file mode 100644 index 000000000..46204335b --- /dev/null +++ b/src/3rdparty/qtsingleapplication.cpp @@ -0,0 +1,352 @@ +/**************************************************************************** +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Solutions Commercial License Agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.1, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** Please note Third Party Software included with Qt Solutions may impose +** additional restrictions and it is the user's responsibility to ensure +** that they have met the licensing requirements of the GPL, LGPL, or Qt +** Solutions Commercial license and the relevant license of the Third +** Party Software they are using. +** +** If you are unsure which license is appropriate for your use, please +** contact Nokia at qt-info@nokia.com. +** +****************************************************************************/ + + +#include "qtsingleapplication.h" +#include "qtlocalpeer.h" +#include + + +/*! + \class QtSingleApplication qtsingleapplication.h + \brief The QtSingleApplication class provides an API to detect and + communicate with running instances of an application. + + This class allows you to create applications where only one + instance should be running at a time. I.e., if the user tries to + launch another instance, the already running instance will be + activated instead. Another usecase is a client-server system, + where the first started instance will assume the role of server, + and the later instances will act as clients of that server. + + By default, the full path of the executable file is used to + determine whether two processes are instances of the same + application. You can also provide an explicit identifier string + that will be compared instead. + + The application should create the QtSingleApplication object early + in the startup phase, and call isRunning() or sendMessage() to + find out if another instance of this application is already + running. Startup parameters (e.g. the name of the file the user + wanted this new instance to open) can be passed to the running + instance in the sendMessage() function. + + If isRunning() or sendMessage() returns false, it means that no + other instance is running, and this instance has assumed the role + as the running instance. The application should continue with the + initialization of the application user interface before entering + the event loop with exec(), as normal. The messageReceived() + signal will be emitted when the application receives messages from + another instance of the same application. + + If isRunning() or sendMessage() returns true, another instance is + already running, and the application should terminate or enter + client mode. + + If a message is received it might be helpful to the user to raise + the application so that it becomes visible. To facilitate this, + QtSingleApplication provides the setActivationWindow() function + and the activateWindow() slot. + + Here's an example that shows how to convert an existing + application to use QtSingleApplication. It is very simple and does + not make use of all QtSingleApplication's functionality (see the + examples for that). + + \code + // Original + int main(int argc, char **argv) + { + QApplication app(argc, argv); + + MyMainWidget mmw; + + mmw.show(); + return app.exec(); + } + + // Single instance + int main(int argc, char **argv) + { + QtSingleApplication app(argc, argv); + + if (app.isRunning()) + return 0; + + MyMainWidget mmw; + + app.setActivationWindow(&mmw); + + mmw.show(); + return app.exec(); + } + \endcode + + Once this QtSingleApplication instance is destroyed(for example, + when the user quits), when the user next attempts to run the + application this instance will not, of course, be encountered. The + next instance to call isRunning() or sendMessage() will assume the + role as the new running instance. + + For console (non-GUI) applications, QtSingleCoreApplication may be + used instead of this class, to avoid the dependency on the QtGui + library. + + \sa QtSingleCoreApplication +*/ + + +void QtSingleApplication::sysInit(const QString &appId) +{ + actWin = 0; + peer = new QtLocalPeer(this, appId); + connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&))); +} + + +/*! + Creates a QtSingleApplication object. The application identifier + will be QCoreApplication::applicationFilePath(). \a argc, \a + argv, and \a GUIenabled are passed on to the QAppliation constructor. + + If you are creating a console application (i.e. setting \a + GUIenabled to false), you may consider using + QtSingleCoreApplication instead. +*/ + +QtSingleApplication::QtSingleApplication(int &argc, char **argv, bool GUIenabled) + : QApplication(argc, argv, GUIenabled) +{ + sysInit(); +} + + +/*! + Creates a QtSingleApplication object with the application + identifier \a appId. \a argc and \a argv are passed on to the + QAppliation constructor. +*/ + +QtSingleApplication::QtSingleApplication(const QString &appId, int &argc, char **argv) + : QApplication(argc, argv) +{ + sysInit(appId); +} + + +/*! + Creates a QtSingleApplication object. The application identifier + will be QCoreApplication::applicationFilePath(). \a argc, \a + argv, and \a type are passed on to the QAppliation constructor. +*/ +QtSingleApplication::QtSingleApplication(int &argc, char **argv, Type type) + : QApplication(argc, argv, type) +{ + sysInit(); +} + + +#if defined(Q_WS_X11) +/*! + Special constructor for X11, ref. the documentation of + QApplication's corresponding constructor. The application identifier + will be QCoreApplication::applicationFilePath(). \a dpy, \a visual, + and \a cmap are passed on to the QApplication constructor. +*/ +QtSingleApplication::QtSingleApplication(Display* dpy, Qt::HANDLE visual, Qt::HANDLE cmap) + : QApplication(dpy, visual, cmap) +{ + sysInit(); +} + +/*! + Special constructor for X11, ref. the documentation of + QApplication's corresponding constructor. The application identifier + will be QCoreApplication::applicationFilePath(). \a dpy, \a argc, \a + argv, \a visual, and \a cmap are passed on to the QApplication + constructor. +*/ +QtSingleApplication::QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap) + : QApplication(dpy, argc, argv, visual, cmap) +{ + sysInit(); +} + +/*! + Special constructor for X11, ref. the documentation of + QApplication's corresponding constructor. The application identifier + will be \a appId. \a dpy, \a argc, \a + argv, \a visual, and \a cmap are passed on to the QApplication + constructor. +*/ +QtSingleApplication::QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap) + : QApplication(dpy, argc, argv, visual, cmap) +{ + sysInit(appId); +} +#endif + + +/*! + Returns true if another instance of this application is running; + otherwise false. + + This function does not find instances of this application that are + being run by a different user (on Windows: that are running in + another session). + + \sa sendMessage() +*/ + +bool QtSingleApplication::isRunning() +{ + return peer->isClient(); +} + + +/*! + Tries to send the text \a message to the currently running + instance. The QtSingleApplication object in the running instance + will emit the messageReceived() signal when it receives the + message. + + This function returns true if the message has been sent to, and + processed by, the current instance. If there is no instance + currently running, or if the running instance fails to process the + message within \a timeout milliseconds, this function return false. + + \sa isRunning(), messageReceived() +*/ +bool QtSingleApplication::sendMessage(const QString &message, int timeout) +{ + return peer->sendMessage(message, timeout); +} + + +/*! + Returns the application identifier. Two processes with the same + identifier will be regarded as instances of the same application. +*/ +QString QtSingleApplication::id() const +{ + return peer->applicationId(); +} + + +/*! + Sets the activation window of this application to \a aw. The + activation window is the widget that will be activated by + activateWindow(). This is typically the application's main window. + + If \a activateOnMessage is true (the default), the window will be + activated automatically every time a message is received, just prior + to the messageReceived() signal being emitted. + + \sa activateWindow(), messageReceived() +*/ + +void QtSingleApplication::setActivationWindow(QWidget* aw, bool activateOnMessage) +{ + actWin = aw; + if (activateOnMessage) + connect(peer, SIGNAL(messageReceived(const QString&)), this, SLOT(activateWindow())); + else + disconnect(peer, SIGNAL(messageReceived(const QString&)), this, SLOT(activateWindow())); +} + + +/*! + Returns the applications activation window if one has been set by + calling setActivationWindow(), otherwise returns 0. + + \sa setActivationWindow() +*/ +QWidget* QtSingleApplication::activationWindow() const +{ + return actWin; +} + + +/*! + De-minimizes, raises, and activates this application's activation window. + This function does nothing if no activation window has been set. + + This is a convenience function to show the user that this + application instance has been activated when he has tried to start + another instance. + + This function should typically be called in response to the + messageReceived() signal. By default, that will happen + automatically, if an activation window has been set. + + \sa setActivationWindow(), messageReceived(), initialize() +*/ +void QtSingleApplication::activateWindow() +{ + if (actWin) { + actWin->setWindowState(actWin->windowState() & ~Qt::WindowMinimized); + actWin->raise(); + actWin->activateWindow(); + actWin->setFocus(); + } +} + + +/*! + \fn void QtSingleApplication::messageReceived(const QString& message) + + This signal is emitted when the current instance receives a \a + message from another instance of this application. + + \sa sendMessage(), setActivationWindow(), activateWindow() +*/ + + +/*! + \fn void QtSingleApplication::initialize(bool dummy = true) + + \obsolete +*/ diff --git a/src/3rdparty/qtsingleapplication.h b/src/3rdparty/qtsingleapplication.h new file mode 100644 index 000000000..5df916561 --- /dev/null +++ b/src/3rdparty/qtsingleapplication.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Solutions Commercial License Agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.1, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** Please note Third Party Software included with Qt Solutions may impose +** additional restrictions and it is the user's responsibility to ensure +** that they have met the licensing requirements of the GPL, LGPL, or Qt +** Solutions Commercial license and the relevant license of the Third +** Party Software they are using. +** +** If you are unsure which license is appropriate for your use, please +** contact Nokia at qt-info@nokia.com. +** +****************************************************************************/ + + +#include + +class QtLocalPeer; + +#if defined(Q_WS_WIN) +# if !defined(QT_QTSINGLEAPPLICATION_EXPORT) && !defined(QT_QTSINGLEAPPLICATION_IMPORT) +# define QT_QTSINGLEAPPLICATION_EXPORT +# elif defined(QT_QTSINGLEAPPLICATION_IMPORT) +# if defined(QT_QTSINGLEAPPLICATION_EXPORT) +# undef QT_QTSINGLEAPPLICATION_EXPORT +# endif +# define QT_QTSINGLEAPPLICATION_EXPORT __declspec(dllimport) +# elif defined(QT_QTSINGLEAPPLICATION_EXPORT) +# undef QT_QTSINGLEAPPLICATION_EXPORT +# define QT_QTSINGLEAPPLICATION_EXPORT __declspec(dllexport) +# endif +#else +# define QT_QTSINGLEAPPLICATION_EXPORT +#endif + +class QT_QTSINGLEAPPLICATION_EXPORT QtSingleApplication : public QApplication +{ + Q_OBJECT + +public: + QtSingleApplication(int &argc, char **argv, bool GUIenabled = true); + QtSingleApplication(const QString &id, int &argc, char **argv); + QtSingleApplication(int &argc, char **argv, Type type); +#if defined(Q_WS_X11) + QtSingleApplication(Display* dpy, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0); + QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE cmap= 0); + QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0); +#endif + + bool isRunning(); + QString id() const; + + void setActivationWindow(QWidget* aw, bool activateOnMessage = true); + QWidget* activationWindow() const; + + // Obsolete: + void initialize(bool dummy = true) + { isRunning(); Q_UNUSED(dummy) } + +public Q_SLOTS: + bool sendMessage(const QString &message, int timeout = 5000); + void activateWindow(); + + +Q_SIGNALS: + void messageReceived(const QString &message); + + +private: + void sysInit(const QString &appId = QString()); + QtLocalPeer *peer; + QWidget *actWin; +}; diff --git a/src/3rdparty/qtsingleapplication.pri b/src/3rdparty/qtsingleapplication.pri new file mode 100644 index 000000000..02de47e5c --- /dev/null +++ b/src/3rdparty/qtsingleapplication.pri @@ -0,0 +1,15 @@ +INCLUDEPATH += $$PWD +DEPENDPATH += $$PWD +QT *= network + +qtsingleapplication-uselib:!qtsingleapplication-buildlib { + LIBS += -L$$QTSINGLEAPPLICATION_LIBDIR -l$$QTSINGLEAPPLICATION_LIBNAME +} else { + SOURCES += $$PWD/qtsingleapplication.cpp $$PWD/qtlocalpeer.cpp + HEADERS += $$PWD/qtsingleapplication.h $$PWD/qtlocalpeer.h +} + +win32 { + contains(TEMPLATE, lib):contains(CONFIG, shared):DEFINES += QT_QTSINGLEAPPLICATION_EXPORT + else:qtsingleapplication-uselib:DEFINES += QT_QTSINGLEAPPLICATION_IMPORT +} diff --git a/src/3rdparty/qtwin.cpp b/src/3rdparty/qtwin.cpp new file mode 100644 index 000000000..827cdc619 --- /dev/null +++ b/src/3rdparty/qtwin.cpp @@ -0,0 +1,225 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Use, modification and distribution is allowed without limitation, +** warranty, liability or support of any kind. +** +****************************************************************************/ + +#include "qtwin.h" +#include +#include +#include +#include +#include + +#ifdef Q_WS_WIN + +#include + +// Blur behind data structures +#define DWM_BB_ENABLE 0x00000001 // fEnable has been specified +#define DWM_BB_BLURREGION 0x00000002 // hRgnBlur has been specified +#define DWM_BB_TRANSITIONONMAXIMIZED 0x00000004 // fTransitionOnMaximized has been specified +#define WM_DWMCOMPOSITIONCHANGED 0x031E // Composition changed window message + +typedef struct _DWM_BLURBEHIND +{ + DWORD dwFlags; + BOOL fEnable; + HRGN hRgnBlur; + BOOL fTransitionOnMaximized; +} DWM_BLURBEHIND, *PDWM_BLURBEHIND; + +typedef struct _MARGINS +{ + int cxLeftWidth; + int cxRightWidth; + int cyTopHeight; + int cyBottomHeight; +} MARGINS, *PMARGINS; + +typedef HRESULT (WINAPI *PtrDwmIsCompositionEnabled)(BOOL* pfEnabled); +typedef HRESULT (WINAPI *PtrDwmExtendFrameIntoClientArea)(HWND hWnd, const MARGINS* pMarInset); +typedef HRESULT (WINAPI *PtrDwmEnableBlurBehindWindow)(HWND hWnd, const DWM_BLURBEHIND* pBlurBehind); +typedef HRESULT (WINAPI *PtrDwmGetColorizationColor)(DWORD *pcrColorization, BOOL *pfOpaqueBlend); + +static PtrDwmIsCompositionEnabled pDwmIsCompositionEnabled= 0; +static PtrDwmEnableBlurBehindWindow pDwmEnableBlurBehindWindow = 0; +static PtrDwmExtendFrameIntoClientArea pDwmExtendFrameIntoClientArea = 0; +static PtrDwmGetColorizationColor pDwmGetColorizationColor = 0; + + +/* + * Internal helper class that notifies windows if the + * DWM compositing state changes and updates the widget + * flags correspondingly. + */ +class WindowNotifier : public QWidget +{ +public: + WindowNotifier() { winId(); } + void addWidget(QWidget *widget) { widgets.append(widget); } + void removeWidget(QWidget *widget) { widgets.removeAll(widget); } + bool winEvent(MSG *message, long *result); + +private: + QWidgetList widgets; +}; + +static bool resolveLibs() +{ + if (!pDwmIsCompositionEnabled) { + QLibrary dwmLib(QString::fromAscii("dwmapi")); + pDwmIsCompositionEnabled =(PtrDwmIsCompositionEnabled)dwmLib.resolve("DwmIsCompositionEnabled"); + pDwmExtendFrameIntoClientArea = (PtrDwmExtendFrameIntoClientArea)dwmLib.resolve("DwmExtendFrameIntoClientArea"); + pDwmEnableBlurBehindWindow = (PtrDwmEnableBlurBehindWindow)dwmLib.resolve("DwmEnableBlurBehindWindow"); + pDwmGetColorizationColor = (PtrDwmGetColorizationColor)dwmLib.resolve("DwmGetColorizationColor"); + } + return pDwmIsCompositionEnabled != 0; +} + +#endif + +/*! + * Chekcs and returns true if Windows DWM composition + * is currently enabled on the system. + * + * To get live notification on the availability of + * this feature, you will currently have to + * reimplement winEvent() on your widget and listen + * for the WM_DWMCOMPOSITIONCHANGED event to occur. + * + */ +bool QtWin::isCompositionEnabled() +{ +#ifdef Q_WS_WIN + if (resolveLibs()) { + HRESULT hr = S_OK; + BOOL isEnabled = false; + hr = pDwmIsCompositionEnabled(&isEnabled); + if (SUCCEEDED(hr)) + return isEnabled; + } +#endif + return false; +} + +/*! + * Enables Blur behind on a Widget. + * + * \a enable tells if the blur should be enabled or not + */ +bool QtWin::enableBlurBehindWindow(QWidget *widget, bool enable) +{ + Q_UNUSED(enable); + Q_UNUSED(widget); + Q_ASSERT(widget); + bool result = false; +#ifdef Q_WS_WIN + if (resolveLibs()) { + DWM_BLURBEHIND bb = {0}; + HRESULT hr = S_OK; + bb.fEnable = enable; + bb.dwFlags = DWM_BB_ENABLE; + bb.hRgnBlur = NULL; + widget->setAttribute(Qt::WA_TranslucentBackground, enable); + widget->setAttribute(Qt::WA_NoSystemBackground, enable); + hr = pDwmEnableBlurBehindWindow(widget->winId(), &bb); + if (SUCCEEDED(hr)) { + result = true; + windowNotifier()->addWidget(widget); + } + } +#endif + return result; +} + +/*! + * ExtendFrameIntoClientArea. + * + * This controls the rendering of the frame inside the window. + * Note that passing margins of -1 (the default value) will completely + * remove the frame from the window. + * + * \note you should not call enableBlurBehindWindow before calling + * this functions + * + * \a enable tells if the blur should be enabled or not + */ +bool QtWin::extendFrameIntoClientArea(QWidget *widget, int left, int top, int right, int bottom) +{ + + Q_ASSERT(widget); + Q_UNUSED(left); + Q_UNUSED(top); + Q_UNUSED(right); + Q_UNUSED(bottom); + Q_UNUSED(widget); + + bool result = false; +#ifdef Q_WS_WIN + if (resolveLibs()) { + QLibrary dwmLib(QString::fromAscii("dwmapi")); + HRESULT hr = S_OK; + MARGINS m = {left, top, right, bottom}; + hr = pDwmExtendFrameIntoClientArea(widget->winId(), &m); + if (SUCCEEDED(hr)) { + result = true; + windowNotifier()->addWidget(widget); + } + widget->setAttribute(Qt::WA_TranslucentBackground, result); + } +#endif + return result; +} + +/*! + * Returns the current colorizationColor for the window. + * + * \a enable tells if the blur should be enabled or not + */ +QColor QtWin::colorizatinColor() +{ + QColor resultColor = QApplication::palette().window().color(); + +#ifdef Q_WS_WIN + if (resolveLibs()) { + DWORD color = 0; + BOOL opaque = FALSE; + QLibrary dwmLib(QString::fromAscii("dwmapi")); + HRESULT hr = S_OK; + hr = pDwmGetColorizationColor(&color, &opaque); + if (SUCCEEDED(hr)) + resultColor = QColor(color); + } +#endif + return resultColor; +} + +#ifdef Q_WS_WIN +WindowNotifier *QtWin::windowNotifier() +{ + static WindowNotifier *windowNotifierInstance = 0; + if (!windowNotifierInstance) + windowNotifierInstance = new WindowNotifier; + return windowNotifierInstance; +} + + +/* Notify all enabled windows that the DWM state changed */ +bool WindowNotifier::winEvent(MSG *message, long *result) +{ + if (message && message->message == WM_DWMCOMPOSITIONCHANGED) { + bool compositionEnabled = QtWin::isCompositionEnabled(); + foreach(QWidget * widget, widgets) { + if (widget) { + widget->setAttribute(Qt::WA_NoSystemBackground, compositionEnabled); + } + widget->update(); + } + } + return QWidget::winEvent(message, result); +} +#endif diff --git a/src/3rdparty/qtwin.h b/src/3rdparty/qtwin.h new file mode 100644 index 000000000..aa37e458c --- /dev/null +++ b/src/3rdparty/qtwin.h @@ -0,0 +1,37 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Use, modification and distribution is allowed without limitation, +** warranty, liability or support of any kind. +** +****************************************************************************/ + +#ifndef QTWIN_H +#define QTWIN_H + +#include +#include +/** + * This is a helper class for using the Desktop Window Manager + * functionality on Windows 7 and Windows Vista. On other platforms + * these functions will simply not do anything. + */ + +class WindowNotifier; + +class QtWin +{ +public: + static bool enableBlurBehindWindow(QWidget *widget, bool enable = true); + static bool extendFrameIntoClientArea(QWidget *widget, + int left = -1, int top = -1, + int right = -1, int bottom = -1); + static bool isCompositionEnabled(); + static QColor colorizatinColor(); + +private: + static WindowNotifier *windowNotifier(); +}; + +#endif // QTWIN_H diff --git a/src/3rdparty/squeezelabel.cpp b/src/3rdparty/squeezelabel.cpp new file mode 100644 index 000000000..d8a20a10f --- /dev/null +++ b/src/3rdparty/squeezelabel.cpp @@ -0,0 +1,19 @@ +#include "squeezelabel.h" + +SqueezeLabel::SqueezeLabel(QWidget *parent) + : QLabel(parent) +{ +} + +void SqueezeLabel::paintEvent(QPaintEvent *event) +{ + if (m_SqueezedTextCache != text()) { + m_SqueezedTextCache = text(); + QFontMetrics fm = fontMetrics(); + if (fm.width(m_SqueezedTextCache) > contentsRect().width()) { + QString elided = fm.elidedText(text(), Qt::ElideMiddle, width()); + setText(elided); + } + } + QLabel::paintEvent(event); +} diff --git a/src/3rdparty/squeezelabel.h b/src/3rdparty/squeezelabel.h new file mode 100644 index 000000000..1adbb828d --- /dev/null +++ b/src/3rdparty/squeezelabel.h @@ -0,0 +1,52 @@ +#ifndef SQUEEZELABEL_H +#define SQUEEZELABEL_H + +/** +* Copyright (c) 2009, Benjamin C. Meyer +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* 3. Neither the name of the Benjamin Meyer nor the names of its contributors +* may be used to endorse or promote products derived from this software +* without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +* SUCH DAMAGE. +*/ + +#include + +/* +A label that will squeeze the set text to fit within the size of the +widget. The text will be elided in the middle. +*/ +class SqueezeLabel : public QLabel +{ + Q_OBJECT + +public: + SqueezeLabel(QWidget *parent = 0); + +protected: + void paintEvent(QPaintEvent *event); + +private: + QString m_SqueezedTextCache; +}; + +#endif // SQUEEZELABEL_H diff --git a/src/QupZilla.pro b/src/QupZilla.pro new file mode 100644 index 000000000..82156c403 --- /dev/null +++ b/src/QupZilla.pro @@ -0,0 +1,168 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2010-12-18T14:53:41 +# +#------------------------------------------------- + +DESTDIR = ../bin + +QT += core gui webkit sql network + +TARGET = qupzilla + +TEMPLATE = app + +INCLUDEPATH += 3rdparty\ + app\ + autofill\ + bookmarks\ + cookies\ + downloads\ + history\ + navigation\ + network\ + other\ + preferences\ + rss\ + tools\ + utils\ + webview\ + plugins\ + sidebar\ + data\ + +SOURCES += main.cpp\ + 3rdparty/squeezelabel.cpp \ + 3rdparty/qtwin.cpp \ + 3rdparty/lineedit.cpp \ + app/qupzilla.cpp \ + app/mainapplication.cpp \ + app/autosaver.cpp \ + autofill/autofillmodel.cpp \ + preferences/autofillmanager.cpp \ + bookmarks/bookmarkstoolbar.cpp \ + bookmarks/bookmarksmanager.cpp \ + cookies/cookiemanager.cpp \ + cookies/cookiejar.cpp \ + downloads/downloadmanager.cpp \ + history/historymodel.cpp \ + history/historymanager.cpp \ + navigation/websearchbar.cpp \ + navigation/locationcompleter.cpp \ + navigation/locationbar.cpp \ + network/networkmanagerproxy.cpp \ + network/networkmanager.cpp \ + other/updater.cpp \ + other/sourceviewer.cpp \ + preferences/preferences.cpp \ + rss/rssmanager.cpp \ + other/clearprivatedata.cpp \ + webview/webview.cpp \ + webview/webpage.cpp \ + webview/tabwidget.cpp \ + webview/tabbar.cpp \ + webview/siteinfo.cpp \ + webview/searchtoolbar.cpp \ + app/commandlineoptions.cpp \ + other/aboutdialog.cpp \ + plugins/plugins.cpp \ + preferences/pluginslist.cpp \ + plugins/pluginproxy.cpp \ + app/appui.cpp \ + tools/clickablelabel.cpp \ + downloads/downloadoptionsdialog.cpp \ + tools/treewidget.cpp \ + bookmarks/bookmarkswidget.cpp \ + tools/frame.cpp \ + bookmarks/bookmarksmodel.cpp \ + sidebar/sidebar.cpp \ + webview/siteinfowidget.cpp \ + plugins/clicktoflash.cpp \ + plugins/webpluginfactory.cpp \ + downloads/downloaditem.cpp \ + 3rdparty/ecwin7.cpp \ + autofill/autofillwidget.cpp \ + webview/webtab.cpp + +HEADERS += 3rdparty/squeezelabel.h \ + 3rdparty/qtwin.h \ + 3rdparty/lineedit.h \ + app/qupzilla.h \ + app/mainapplication.h \ + app/autosaver.h \ + autofill/autofillmodel.h \ + preferences/autofillmanager.h \ + bookmarks/bookmarkstoolbar.h \ + bookmarks/bookmarksmanager.h \ + cookies/cookiemanager.h \ + cookies/cookiejar.h \ + downloads/downloadmanager.h \ + history/historymodel.h \ + history/historymanager.h \ + navigation/websearchbar.h \ + navigation/locationcompleter.h \ + navigation/locationbar.h \ + network/networkmanagerproxy.h \ + network/networkmanager.h \ + other/updater.h \ + other/sourceviewer.h \ + preferences/preferences.h \ + rss/rssmanager.h \ + other/clearprivatedata.h \ + webview/webview.h \ + webview/webpage.h \ + webview/tabwidget.h \ + webview/tabbar.h \ + webview/siteinfo.h \ + webview/searchtoolbar.h \ + app/commandlineoptions.h \ + other/aboutdialog.h \ + plugins/plugininterface.h \ + plugins/plugins.h \ + preferences/pluginslist.h \ + plugins/pluginproxy.h \ + tools/clickablelabel.h \ + downloads/downloadoptionsdialog.h \ + tools/treewidget.h \ + bookmarks/bookmarkswidget.h \ + tools/frame.h \ + bookmarks/bookmarksmodel.h \ + sidebar/sidebar.h \ + webview/siteinfowidget.h \ + plugins/clicktoflash.h \ + plugins/webpluginfactory.h \ + downloads/downloaditem.h \ + 3rdparty/ecwin7.h \ + autofill/autofillwidget.h \ + webview/webtab.h + +FORMS += \ + preferences/autofillmanager.ui \ + bookmarks/bookmarksmanager.ui \ + cookies/cookiemanager.ui \ + history/historymanager.ui \ + preferences/preferences.ui \ + rss/rssmanager.ui \ + webview/siteinfo.ui \ + other/aboutdialog.ui \ + preferences/pluginslist.ui \ + downloads/downloadoptionsdialog.ui \ + bookmarks/bookmarkswidget.ui \ + webview/siteinfowidget.ui \ + downloads/downloaditem.ui \ + downloads/downloadmanager.ui \ + autofill/autofillwidget.ui + +RESOURCES += \ + data/icons.qrc \ + data/html.qrc + +OTHER_FILES += \ + appicon.rc \ + +TRANSLATIONS +=cs_CZ.ts + +include(3rdparty/qtsingleapplication.pri) + +win32:RC_FILE = appicon.rc +win32:LIBS += libole32 diff --git a/src/app/appui.cpp b/src/app/appui.cpp new file mode 100644 index 000000000..29d0c20a9 --- /dev/null +++ b/src/app/appui.cpp @@ -0,0 +1,294 @@ +#include "qupzilla.h" +#include "autofillmodel.h" +#include "bookmarkstoolbar.h" +#include "locationbar.h" + +void QupZilla::postLaunch() +{ + loadSettings(); + //Open tab from command line argument + bool addTab = true; + QStringList arguments = qApp->arguments(); + for (int i = 0;iarguments().count();i++) { + QString arg = arguments.at(i); + if (arg.startsWith("-url=")) { + m_tabWidget->addView(QUrl(arg.replace("-url=",""))); + addTab = false; + } + } + + QSettings settings(m_activeProfil+"settings.ini", QSettings::IniFormat); + settings.beginGroup("Web-URL-Settings"); + int afterLaunch = settings.value("afterLaunch",1).toInt(); + settings.endGroup(); + settings.beginGroup("SessionRestore"); + bool startingAfterCrash = settings.value("isCrashed",false).toBool(); + settings.endGroup(); + qDebug() << "startingaftercrash" << startingAfterCrash; + + QUrl startUrl; + if (m_tryRestore) { + if (afterLaunch == 0) + startUrl = QUrl(""); + else if (afterLaunch == 1) + startUrl = m_homepage; + else + startUrl = m_homepage; + + if ( startingAfterCrash || (addTab && afterLaunch == 2) ) { + p_mainApp->restoreStateSlot(this); + addTab = false; + } + } else + startUrl = m_homepage; + + if (!m_startingUrl.isEmpty()) { + startUrl = WebView::guessUrlFromString(m_startingUrl.toString()); + addTab = true; + } + + if (addTab) + m_tabWidget->addView(startUrl); + + aboutToShowHistoryMenu(); + aboutToShowBookmarksMenu(); + + if (m_tabWidget->count() == 0) //Something went really wrong .. add one tab + m_tabWidget->addView(m_homepage); + + QApplication::restoreOverrideCursor(); + setUpdatesEnabled(true); + emit startingCompleted(); +} + +void QupZilla::setupUi() +{ + setContentsMargins(0,0,0,0); + + m_navigation = new QToolBar(this); + m_navigation->setWindowTitle(tr("Navigation")); + m_navigation->setObjectName("Navigation bar"); + addToolBar(m_navigation); + m_navigation->setMovable(false); + m_navigation->setStyleSheet("QToolBar{background-image:url(:icons/transp.png); border:none;}"); + + m_buttonBack = new QAction(QIcon(":/icons/navigation/zpet.png"),tr("Back"),this); + m_buttonBack->setEnabled(false); + m_buttonNext = new QAction(QIcon(":/icons/navigation/vpred.png"),tr("Forward"),this); + m_buttonNext->setEnabled(false); + m_buttonStop = new QAction(QIcon(":/icons/navigation/stop.png"),tr("Stop"),this); + m_buttonReload = new QAction(QIcon(":/icons/navigation/reload.png"),tr("Reload"),this); + m_buttonReload->setShortcut(QKeySequence("F5")); + m_buttonHome = new QAction(QIcon(":/icons/navigation/home.png"),tr("Home"),this); + + m_menuBack = new QMenu(); + m_buttonBack->setMenu(m_menuBack); + connect(m_menuBack, SIGNAL(aboutToShow()),this, SLOT(aboutToShowHistoryBackMenu())); + + m_menuForward = new QMenu(); + m_buttonNext->setMenu(m_menuForward); + connect(m_menuForward, SIGNAL(aboutToShow()),this, SLOT(aboutToShowHistoryNextMenu())); + + m_supMenu = new QToolButton(this); + m_supMenu->setPopupMode(QToolButton::InstantPopup); + m_supMenu->setIcon(QIcon(":/icons/qupzilla.png")); + m_supMenu->setToolTip(tr("Main Menu")); + m_superMenu = new QMenu(this); + m_supMenu->setMenu(m_superMenu); + + m_navigation->addAction(m_buttonBack); + m_navigation->addAction(m_buttonNext); + m_navigation->addAction(m_buttonReload); + m_navigation->addAction(m_buttonStop); + m_navigation->addAction(m_buttonHome); + + m_locationBar = new LocationBar(this); + m_searchLine = new WebSearchBar(this); + + m_navigationSplitter = new QSplitter(m_navigation); + m_navigationSplitter->addWidget(m_locationBar); + m_navigationSplitter->addWidget(m_searchLine); + + m_navigationSplitter->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); + m_navigationSplitter->setCollapsible(0, false); + + m_navigation->addWidget(m_navigationSplitter); + int splitterWidth = m_navigationSplitter->width(); + QList sizes; + sizes << (int)((double)splitterWidth * .75) << (int)((double)splitterWidth * .25); + m_navigationSplitter->setSizes(sizes); + + m_actionExitFullscreen = new QAction(tr("Exit Fullscreen"),this); + m_actionExitFullscreen->setVisible(false); + m_navigation->addAction(m_actionExitFullscreen); + m_navigation->addWidget(m_supMenu); + m_navigation->addWidget(new QLabel()); //Elegant spacer -,- + m_navigation->setContextMenuPolicy(Qt::CustomContextMenu); + + m_progressBar = new QProgressBar(this); + m_progressBar->setMaximumSize(QSize(150, 16)); + m_privateBrowsing = new QLabel(this); + m_privateBrowsing->setPixmap(QPixmap(":/icons/locationbar/privatebrowsing.png")); + m_privateBrowsing->setVisible(false); + m_privateBrowsing->setToolTip(tr("Private Browsing Enabled")); + m_allowFlashIcon = new QLabel(this); + m_allowFlashIcon->setPixmap(QPixmap(":/icons/menu/flash.png")); + m_allowFlashIcon->setVisible(false); + m_allowFlashIcon->setToolTip(tr("Flash Plugin Enabled")); + m_ipLabel = new QLabel(this); + m_ipLabel->setStyleSheet("padding-right: 5px;"); + m_ipLabel->setToolTip(tr("IP Address of current page")); + + statusBar()->insertPermanentWidget(0, m_progressBar); + statusBar()->insertPermanentWidget(1, m_ipLabel); + statusBar()->insertPermanentWidget(2, m_privateBrowsing); + statusBar()->insertPermanentWidget(3, m_allowFlashIcon); + + m_bookmarksToolbar = new BookmarksToolbar(this); + addToolBar(m_bookmarksToolbar); + insertToolBarBreak(m_bookmarksToolbar); + + m_tabWidget = new TabWidget(this); + setCentralWidget(m_tabWidget); +} + +void QupZilla::setupMenu() +{ + m_menuTools = new QMenu(tr("Tools")); + m_menuHelp = new QMenu(tr("Help")); + m_menuBookmarks = new QMenu(tr("Bookmarks")); + m_menuHistory = new QMenu(tr("History")); + connect(m_menuHistory, SIGNAL(aboutToShow()), this, SLOT(aboutToShowHistoryMenu())); + connect(m_menuBookmarks, SIGNAL(aboutToShow()), this, SLOT(aboutToShowBookmarksMenu())); + connect(m_menuHelp, SIGNAL(aboutToShow()), this, SLOT(aboutToShowHelpMenu())); + connect(m_menuTools, SIGNAL(aboutToShow()), this, SLOT(aboutToShowToolsMenu())); + + m_menuFile = new QMenu(tr("File")); + m_menuFile->addAction(QIcon::fromTheme("window-new"), tr("New Window"), this, SLOT(newWindow()))->setShortcut(QKeySequence("Ctrl+N")); + m_menuFile->addAction(QIcon(":/icons/menu/popup.png"), tr("New Tab"), this, SLOT(addTab()))->setShortcut(QKeySequence("Ctrl+T")); + m_menuFile->addAction(tr("Open Location"), this, SLOT(openLocation()))->setShortcut(QKeySequence("Ctrl+L")); + m_menuFile->addAction(QIcon::fromTheme("document-open"), tr("Open File"), this, SLOT(openFile()))->setShortcut(QKeySequence("Ctrl+O")); + m_menuFile->addAction(tr("Close Tab"), m_tabWidget, SLOT(closeTab()))->setShortcut(QKeySequence("Ctrl+W")); + m_menuFile->addAction(QIcon::fromTheme("window-close"), tr("Close Window"), this, SLOT(close()))->setShortcut(QKeySequence("Ctrl+Shift+W")); + m_menuFile->addSeparator(); + m_menuFile->addAction(QIcon::fromTheme("document-save"), tr("Save Page As..."), this, SLOT(savePage()))->setShortcut(QKeySequence("Ctrl+S")); + m_menuFile->addAction(tr("Send Link..."), this, SLOT(sendLink())); + m_menuFile->addAction(QIcon::fromTheme("document-print"), tr("Print"), this, SLOT(printPage())); + m_menuFile->addSeparator(); + m_menuFile->addAction(QIcon::fromTheme("application-exit"), tr("Quit"), this, SLOT(quitApp()))->setShortcut(QKeySequence("Ctrl+Q")); + menuBar()->addMenu(m_menuFile); + + m_menuEdit = new QMenu(tr("Edit")); + m_menuEdit->addAction(QIcon::fromTheme("edit-undo"), tr("Undo"))->setShortcut(QKeySequence("Ctrl+Z")); + m_menuEdit->addAction(QIcon::fromTheme("edit-redo"), tr("Redo"))->setShortcut(QKeySequence("Ctrl+Shift+Z")); + m_menuEdit->addSeparator(); + m_menuEdit->addAction(QIcon::fromTheme("edit-cut"), tr("Cut"))->setShortcut(QKeySequence("Ctrl+X")); + m_menuEdit->addAction(QIcon::fromTheme("edit-copy"), tr("Copy"), this, SLOT(copy()))->setShortcut(QKeySequence("Ctrl+C")); + m_menuEdit->addAction(QIcon::fromTheme("edit-paste"), tr("Paste"))->setShortcut(QKeySequence("Ctrl+V")); + m_menuEdit->addAction(QIcon::fromTheme("edit-delete"), tr("Delete"))->setShortcut(QKeySequence("Del")); + m_menuEdit->addSeparator(); + m_menuEdit->addAction(QIcon::fromTheme("edit-select-all"), tr("Select All"), this, SLOT(selectAll()))->setShortcut(QKeySequence("Ctrl+A")); + m_menuEdit->addSeparator(); + m_menuEdit->addAction(QIcon::fromTheme("edit-find"), tr("Find"), this, SLOT(searchOnPage()))->setShortcut(QKeySequence("Ctrl+F")); + menuBar()->addMenu(m_menuEdit); + + m_menuView = new QMenu(tr("View")); + m_actionShowToolbar = new QAction(tr("Navigation Toolbar"), this); + m_actionShowToolbar->setCheckable(true); + connect(m_actionShowToolbar, SIGNAL(triggered(bool)), this, SLOT(showNavigationToolbar())); + m_actionShowBookmarksToolbar = new QAction(tr("Bookmarks Toolbar"), this); + m_actionShowBookmarksToolbar->setCheckable(true); + connect(m_actionShowBookmarksToolbar, SIGNAL(triggered(bool)), this, SLOT(showBookmarksToolbar())); + m_actionShowStatusbar = new QAction(tr("Status Bar"), this); + m_actionShowStatusbar->setCheckable(true); + connect(m_actionShowStatusbar, SIGNAL(triggered(bool)), this, SLOT(showStatusbar())); + m_actionShowMenubar = new QAction(tr("Menu Bar"), this); + m_actionShowMenubar->setCheckable(true); + connect(m_actionShowMenubar, SIGNAL(triggered(bool)), this, SLOT(showMenubar())); + m_actionShowFullScreen = new QAction(tr("Fullscreen"), this); + m_actionShowFullScreen->setCheckable(true); + m_actionShowFullScreen->setShortcut(QKeySequence("F11")); + connect(m_actionShowFullScreen, SIGNAL(triggered(bool)), this, SLOT(fullScreen(bool))); + m_actionStop = new QAction( +#ifdef Q_WS_X11 + style()->standardIcon(QStyle::SP_BrowserStop) +#else + QIcon(":/icons/faenza/stop.png") +#endif + , tr("Stop"), this); + connect(m_actionStop, SIGNAL(triggered()), this, SLOT(stop())); + m_actionStop->setShortcut(QKeySequence("Esc")); + m_actionReload = new QAction( +#ifdef Q_WS_X11 + style()->standardIcon(QStyle::SP_BrowserReload) +#else + QIcon(":/icons/faenza/reload.png") +#endif + , tr("Reload"), this); + connect(m_actionReload, SIGNAL(triggered()), this, SLOT(reload())); + m_actionReload->setShortcut(QKeySequence("Ctrl+R")); + + m_menuView->addAction(m_actionShowMenubar); + m_menuView->addAction(m_actionShowToolbar); + m_menuView->addAction(m_actionShowBookmarksToolbar); + m_menuView->addAction(m_actionShowStatusbar); + m_menuView->addSeparator(); + m_menuView->addAction(m_actionStop); + m_menuView->addAction(m_actionReload); + m_menuView->addSeparator(); + m_menuView->addAction(QIcon::fromTheme("zoom-in"), tr("Zoom In"), this, SLOT(zoomIn()))->setShortcut(QKeySequence("Ctrl++")); + m_menuView->addAction(QIcon::fromTheme("zoom-out"), tr("Zoom Out"), this, SLOT(zoomOut()))->setShortcut(QKeySequence("Ctrl+-")); + m_menuView->addAction(QIcon::fromTheme("zoom-original"), tr("Reset"), this, SLOT(zoomReset()))->setShortcut(QKeySequence("Ctrl+0")); + m_menuView->addSeparator(); + m_menuView->addAction(QIcon::fromTheme("text-html"), tr("Page Source"), this, SLOT(showSource()))->setShortcut(QKeySequence("Ctrl+U")); + m_menuView->addAction(m_actionShowFullScreen); + menuBar()->addMenu(m_menuView); + connect(m_menuView, SIGNAL(aboutToShow()), this, SLOT(aboutToShowViewMenu())); + + menuBar()->addMenu(m_menuHistory); + menuBar()->addMenu(m_menuBookmarks); + menuBar()->addMenu(m_menuTools); + menuBar()->addMenu(m_menuHelp); + + menuBar()->setContextMenuPolicy(Qt::CustomContextMenu); + + aboutToShowToolsMenu(); + aboutToShowHelpMenu(); + + connect(m_locationBar, SIGNAL(returnPressed()), this, SLOT(urlEnter())); + connect(m_buttonBack, SIGNAL(triggered()), this, SLOT(goBack())); + connect(m_buttonNext, SIGNAL(triggered()), this, SLOT(goNext())); + connect(m_buttonStop, SIGNAL(triggered()), this, SLOT(stop())); + connect(m_buttonReload, SIGNAL(triggered()), this, SLOT(reload())); + connect(m_buttonHome, SIGNAL(triggered()), this, SLOT(goHome())); + connect(m_actionExitFullscreen, SIGNAL(triggered(bool)), this, SLOT(fullScreen(bool))); + + //Make shortcuts available even in fullscreen (menu hidden) + QList actions = menuBar()->actions(); + foreach (QAction *action, actions) { + if (action->menu()) + actions += action->menu()->actions(); + addAction(action); + } + + m_superMenu->addMenu(m_menuFile); + m_superMenu->addMenu(m_menuEdit); + m_superMenu->addMenu(m_menuView); + m_superMenu->addMenu(m_menuHistory); + m_superMenu->addMenu(m_menuBookmarks); + m_superMenu->addMenu(m_menuTools); + m_superMenu->addMenu(m_menuHelp); +} + +void QupZilla::setBackground(QColor textColor) +{ + QString color = textColor.name(); + setStyleSheet("QMainWindow { background-image: url("+m_activeProfil+"background.png); background-position: top right; } QToolBar{background-image:url(:icons/transp.png); border:none;}" + "QMenuBar{color:"+color+";background-image:url(:icons/transp.png); border:none;} QStatusBar{background-image:url(:icons/transp.png); border:none; color:"+color+";}" + "QMenuBar:item{spacing: 5px; padding: 2px 6px;background: transparent;}" + "QMenuBar::item:pressed { background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1,stop:0 lightgray, stop:1 darkgray); border: 1px solid darkgrey; border-top-left-radius: 4px;border-top-right-radius: 4px; border-bottom: none;}" + "QSplitter::handle{background-color:transparent;}" + ); + +} + diff --git a/src/app/autosaver.cpp b/src/app/autosaver.cpp new file mode 100644 index 000000000..c7edb462c --- /dev/null +++ b/src/app/autosaver.cpp @@ -0,0 +1,17 @@ +#include "autosaver.h" +#include "mainapplication.h" + +AutoSaver::AutoSaver(QObject *parent) : + QObject(parent) + ,p_mainApp(MainApplication::getInstance()) +{ + m_timer.start(1000*5, this); +} + +void AutoSaver::timerEvent(QTimerEvent *event) +{ + if (event->timerId() == m_timer.timerId() && p_mainApp->isChanged()) + emit saveApp(); + else + QObject::timerEvent(event); +} diff --git a/src/app/autosaver.h b/src/app/autosaver.h new file mode 100644 index 000000000..e1379669f --- /dev/null +++ b/src/app/autosaver.h @@ -0,0 +1,37 @@ +#ifndef AUTOSAVER_H +#define AUTOSAVER_H + +#if defined(QT_NO_DEBUG) & !defined(QT_NO_DEBUG_OUTPUT) +#define QT_NO_DEBUG_OUTPUT +#endif + +#ifdef QT_NO_DEBUG +#ifdef DEVELOPING +#error "TRYING TO RELEASE WITH DEVELOPING FLAG" +#endif +#endif + +#include +#include +#include + +class MainApplication; +class AutoSaver : public QObject +{ + Q_OBJECT +public: + explicit AutoSaver(QObject *parent = 0); + +signals: + void saveApp(); + +public slots: + +private: + void timerEvent(QTimerEvent *); + MainApplication* p_mainApp; + QBasicTimer m_timer; + +}; + +#endif // AUTOSAVER_H diff --git a/src/app/commandlineoptions.cpp b/src/app/commandlineoptions.cpp new file mode 100644 index 000000000..ce7968bd6 --- /dev/null +++ b/src/app/commandlineoptions.cpp @@ -0,0 +1,100 @@ +#include "commandlineoptions.h" +#include "qupzilla.h" + +CommandLineOptions::CommandLineOptions(int &argc, char **argv) : + QObject(0) + ,m_actionString("") + ,m_argc(argc) + ,m_argv(argv) + ,m_action(NoAction) +{ + parseActions(); +} + +void CommandLineOptions::showHelp() +{ + using namespace std; + + const char* help= " Usage: qupzilla [options] URL \n" + "\n" + " QupZilla options:\n" + " -h or -help print this message \n" + " -a or -authors print QupZilla authors \n" + " -v or -version print QupZilla version \n" + " -p or -profile=PROFILE start with specified profile \n" + " -np or -no-plugins start without plugins \n" + "\n" + " QupZilla is a new, fast and secure web browser\n" + " based on WebKit core (http://webkit.org) and\n" + " written in Qt Framework (http://qt.nokia.com) \n" + ; + cout << help << " " << QupZilla::WWWADDRESS.toAscii().data() << endl; +} + +void CommandLineOptions::parseActions() +{ + using namespace std; + + bool found = false; + // Skip first argument (program itself) + for (int i = 1; i < m_argc; i++) { + QString arg(m_argv[i]); + if (arg == "-h" || arg == "-help") { + showHelp(); + found = true; + break; + } + if (arg == "-a" || arg == "-authors") { + cout << "QupZilla authors: " << endl; + cout << " nowrep " << endl; + found = true; + break; + } + if (arg == "-v" || arg == "-version") { + cout << "QupZilla v" << QupZilla::VERSION.toAscii().data() + << "(build " << QupZilla::BUILDTIME.toAscii().data() << ")" + << endl; + found = true; + break; + } + + if (arg.startsWith("-p=") || arg.startsWith("-profile=")) { + arg.remove("-p="); + arg.remove("-profile="); + found = true; + cout << "starting with profile " << arg.toAscii().data() << endl; + m_actionString = arg; + m_action = StartWithProfile; + } + + if (arg.startsWith("-np") || arg.startsWith("-no-plugins")) { + found = true; + m_action = StartWithoutAddons; + } + } + + QString url(m_argv[m_argc-1]); + if (m_argc > 1 && !url.isEmpty() && !url.startsWith("-")) { + found = true; + cout << "starting with url " << url.toAscii().data() << endl; + m_actionString = url; + m_action = OpenUrl; + } + + if (m_argc > 1 && !found) { + cout << "bad arguments!" << endl; + showHelp(); + } + +} +CommandLineOptions::Action CommandLineOptions::getAction() +{ + return m_action; +} + +QString CommandLineOptions::getActionString() +{ + return m_actionString; +} + + diff --git a/src/app/commandlineoptions.h b/src/app/commandlineoptions.h new file mode 100644 index 000000000..0182cdd9e --- /dev/null +++ b/src/app/commandlineoptions.h @@ -0,0 +1,31 @@ +#ifndef COMMANDLINEOPTIONS_H +#define COMMANDLINEOPTIONS_H + +#if defined(QT_NO_DEBUG) & !defined(QT_NO_DEBUG_OUTPUT) +#define QT_NO_DEBUG_OUTPUT +#endif + +#include + +#include + +class CommandLineOptions : public QObject +{ + Q_OBJECT +public: + enum Action {NoAction, OpenUrl, StartWithProfile, StartWithoutAddons}; + explicit CommandLineOptions(int &argc, char **argv); + Action getAction(); + QString getActionString(); + +private: + void showHelp(); + void parseActions(); + + QString m_actionString; + int m_argc; + char **m_argv; + Action m_action; +}; + +#endif // COMMANDLINEOPTIONS_H diff --git a/src/app/mainapplication.cpp b/src/app/mainapplication.cpp new file mode 100644 index 000000000..ffcf3dbdc --- /dev/null +++ b/src/app/mainapplication.cpp @@ -0,0 +1,555 @@ +#include "mainapplication.h" +#include "qupzilla.h" +#include "tabwidget.h" +#include "bookmarkstoolbar.h" +#include "bookmarksmanager.h" +#include "cookiemanager.h" +#include "cookiejar.h" +#include "historymanager.h" +#include "historymodel.h" +#include "networkmanager.h" +#include "rssmanager.h" +#include "updater.h" +#include "autosaver.h" +#include "commandlineoptions.h" +#include "pluginproxy.h" +#include "bookmarksmodel.h" +#include "downloadmanager.h" +#include "autofillmodel.h" + +MainApplication::MainApplication(int &argc, char **argv) + : QtSingleApplication("qupzillaMainApp", argc, argv) + ,m_bookmarksmanager(0) + ,m_cookiemanager(0) + ,m_historymanager(0) + ,m_historymodel(0) + ,m_websettings(0) + ,m_networkmanager(0) + ,m_cookiejar(0) + ,m_rssmanager(0) + ,m_plugins(0) + ,m_bookmarksModel(0) + ,m_downloadManager(0) + ,m_autofill(0) + ,m_isClosing(false) + ,m_isChanged(false) + ,m_isExited(false) +{ +#if defined(Q_WS_X11) & !defined(DEVELOPING) + DATADIR = "/usr/share/qupzilla/"; +#else + DATADIR = qApp->applicationDirPath()+"/"; +#endif + setOverrideCursor(Qt::WaitCursor); + setWindowIcon(QIcon(":/icons/qupzilla.png")); + bool noAddons = false; + QUrl startUrl(""); + QString message; + if (argc > 1) { + CommandLineOptions cmd(argc, argv); + switch (cmd.getAction()) { + case CommandLineOptions::StartWithoutAddons: + noAddons = true; + break; + case CommandLineOptions::OpenUrl: + startUrl = QUrl(cmd.getActionString()); + message = "URL:"+startUrl.toString(); + break; + default: + m_isExited = true; + return; + break; + } + } + + if (isRunning()) { + sendMessage(message); + m_isExited = true; + return; + } + + connect(this, SIGNAL(messageReceived(QString)), this, SLOT(receiveAppMessage(QString))); + + setQuitOnLastWindowClosed(true); + setApplicationName("QupZilla"); + setApplicationVersion(QupZilla::VERSION); + setOrganizationDomain("qupzilla"); + + QString homePath = QDir::homePath(); + homePath+="/.qupzilla/"; + + checkProfileDir(); + + QSettings::setDefaultFormat(QSettings::IniFormat); + QSettings settings(homePath+"profiles/profiles.ini", QSettings::IniFormat); + if (settings.value("Profiles/startProfile","default").toString().contains("/")) + m_activeProfil=homePath+"profiles/default/"; + else + m_activeProfil=homePath+"profiles/"+settings.value("Profiles/startProfile","default").toString()+"/"; + if (!QDir(m_activeProfil).exists()) + m_activeProfil=homePath+"profiles/default/"; + + QSettings settings2(m_activeProfil+"settings.ini", QSettings::IniFormat); + settings2.beginGroup("SessionRestore"); + if (settings2.value("isRunning",false).toBool() ) + settings2.setValue("isCrashed", true); + settings2.setValue("isRunning", true); + settings2.endGroup(); + + QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, m_activeProfil); + + translateApp(); + connectDatabase(); + + QupZilla* qupzilla = new QupZilla(true, startUrl); + m_mainWindows.append(qupzilla); + connect(qupzilla, SIGNAL(message(MainApplication::MessageType,bool)), this, SLOT(sendMessages(MainApplication::MessageType,bool))); + qupzilla->show(); + + AutoSaver* saver = new AutoSaver(); + connect(saver, SIGNAL(saveApp()), this, SLOT(saveStateSlot())); + m_updater = new Updater(qupzilla); + + if (noAddons) { + settings2.setValue("Plugin-Settings/AllowedPlugins",QStringList()); + settings2.setValue("Plugin-Settings/EnablePlugins",false); + } + + networkManager()->loadCertExceptions(); + plugins()->loadPlugins(); + loadSettings(); +} + +void MainApplication::loadSettings() +{ + QSettings settings(m_activeProfil+"settings.ini", QSettings::IniFormat); + webSettings(); + //Web browsing settings + settings.beginGroup("Web-Browser-Settings"); + bool allowFlash = settings.value("allowFlash",true).toBool(); + bool allowJavaScript = settings.value("allowJavaScript",true).toBool(); + bool allowJavaScriptOpenWindow = settings.value("allowJavaScriptOpenWindow",false).toBool(); + bool allowJava = settings.value("allowJava",true).toBool(); + bool allowPersistentStorage = settings.value("allowPersistentStorage",true).toBool(); + bool allowImages = settings.value("autoLoadImages",true).toBool(); + bool dnsPrefetch = settings.value("DNS-Prefetch", false).toBool(); + bool jsClipboard = settings.value("JavaScriptCanAccessClipboard", true).toBool(); + bool linkInFocuschain = settings.value("IncludeLinkInFocusChain", false).toBool(); + bool zoomTextOnly = settings.value("zoomTextOnly", false).toBool(); + bool printElBg = settings.value("PrintElementBackground", true).toBool(); + int maxCachedPages = settings.value("maximumCachedPages",3).toInt(); + int scrollingLines = settings.value("wheelScrollLines", wheelScrollLines()).toInt(); + settings.endGroup(); + + m_websettings->setAttribute(QWebSettings::DeveloperExtrasEnabled, true); + m_websettings->setAttribute(QWebSettings::PluginsEnabled, allowFlash); + m_websettings->setAttribute(QWebSettings::JavascriptEnabled, allowJavaScript); + m_websettings->setAttribute(QWebSettings::JavascriptCanOpenWindows, allowJavaScriptOpenWindow); + m_websettings->setAttribute(QWebSettings::JavaEnabled, allowJava); + m_websettings->setAttribute(QWebSettings::AutoLoadImages, allowImages); + m_websettings->setAttribute(QWebSettings::DnsPrefetchEnabled, dnsPrefetch); + m_websettings->setAttribute(QWebSettings::JavascriptCanAccessClipboard, jsClipboard); + m_websettings->setAttribute(QWebSettings::LinksIncludedInFocusChain, linkInFocuschain); + m_websettings->setAttribute(QWebSettings::ZoomTextOnly, zoomTextOnly); + m_websettings->setAttribute(QWebSettings::PrintElementBackgrounds, printElBg); + + if (allowPersistentStorage) m_websettings->enablePersistentStorage(m_activeProfil); + m_websettings->setMaximumPagesInCache(maxCachedPages); + + setWheelScrollLines(scrollingLines); +} + +QupZilla* MainApplication::getWindow() +{ + QupZilla *active = qobject_cast(QApplication::activeWindow()); + if (active) + return active; + + for(int i=0; isetWindowState(actWin->windowState() & ~Qt::WindowMinimized); + actWin->raise(); + actWin->activateWindow(); + actWin->setFocus(); +} + +void MainApplication::addNewTab(QUrl url) +{ + if (!getWindow()) + return; + getWindow()->tabWidget()->addView(url); +} + +void MainApplication::makeNewWindow(bool tryRestore, const QUrl &startUrl) +{ + QupZilla* newWindow = new QupZilla(tryRestore, startUrl); + newWindow->show(); + connect(newWindow, SIGNAL(message(MainApplication::MessageType,bool)), this, SLOT(sendMessages(MainApplication::MessageType,bool))); + m_mainWindows.append(newWindow); +} + +void MainApplication::connectDatabase() +{ + QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"); + db.setDatabaseName(m_activeProfil+"browsedata.db"); + if (!QFile::exists(m_activeProfil+"browsedata.db")) { + QFile(DATADIR+"data/default/profiles/default/browsedata.db").copy(m_activeProfil+"browsedata.db"); + qWarning("Cannot find SQLite database file! Copying and using the defaults!"); + } + if (!db.open()) + qWarning("Cannot open SQLite database! Continuing without database...."); + +} + +void MainApplication::translateApp() +{ + QLocale locale; + QSettings settings(m_activeProfil+"settings.ini", QSettings::IniFormat); + settings.beginGroup("Browser-Window-Settings"); + QString file = settings.value("language",locale.name()+".qm").toString(); + QString shortLoc = file.left(2); + + if (file == "" || !QFile::exists(DATADIR+"locale/"+file) ) + return; + + QTranslator* app = new QTranslator(); + app->load(DATADIR+"locale/"+file); + QTranslator* sys = new QTranslator(); + + if (QFile::exists(DATADIR+"locale/qt_"+shortLoc+".qm")) + sys->load(DATADIR+"locale/qt_"+shortLoc+".qm"); + + m_activeLanguage = file; + + installTranslator(app); + installTranslator(sys); +} + +void MainApplication::quitApplication() +{ + if (m_downloadManager && !m_downloadManager->canClose()) { + m_downloadManager->show(); + return; + } + + delete m_updater; + m_isClosing = true; + + if (m_mainWindows.count() > 0) + saveStateSlot(); + + qDebug() << __FUNCTION__ << "called"; + QSettings settings(m_activeProfil+"settings.ini", QSettings::IniFormat); + settings.beginGroup("SessionRestore"); + settings.setValue("isRunning",false); + settings.setValue("isCrashed", false); + settings.endGroup(); + + bool deleteCookies = settings.value("Web-Browser-Settings/deleteCookiesOnClose",false).toBool(); + bool deleteHistory = settings.value("Web-Browser-Settings/deleteHistoryOnClose",false).toBool(); + + if (deleteCookies) + QFile::remove(m_activeProfil+"cookies.dat"); + if (deleteHistory) { + QSqlQuery query; + query.exec("DELETE FROM history"); + query.exec("VACUUM"); + } + + cookieJar()->saveCookies(); + m_networkmanager->saveCertExceptions(); + m_plugins->c2f_saveSettings(); + + quit(); +} + +BookmarksManager* MainApplication::bookmarksManager() +{ + if (!m_bookmarksmanager) + m_bookmarksmanager = new BookmarksManager(getWindow()); + return m_bookmarksmanager; +} + +PluginProxy* MainApplication::plugins() +{ + if (!m_plugins) + m_plugins = new PluginProxy(); + return m_plugins; +} + +CookieManager* MainApplication::cookieManager() +{ + if (!m_cookiemanager) + m_cookiemanager = new CookieManager(); + return m_cookiemanager; +} + +HistoryManager* MainApplication::historyManager() +{ + if (!m_historymanager) + m_historymanager = new HistoryManager(getWindow()); + return m_historymanager; +} + +HistoryModel* MainApplication::history() +{ + if (!m_historymodel) + m_historymodel = new HistoryModel(getWindow()); + return m_historymodel; +} + +QWebSettings* MainApplication::webSettings() +{ + if (!m_websettings) + m_websettings = QWebSettings::globalSettings(); + return m_websettings; +} + +NetworkManager* MainApplication::networkManager() +{ + if (!m_networkmanager) + m_networkmanager = new NetworkManager(getWindow()); + return m_networkmanager; +} + +CookieJar* MainApplication::cookieJar() +{ + if (!m_cookiejar) { + m_cookiejar = new CookieJar(getWindow()); + m_cookiejar->restoreCookies(); + } + return m_cookiejar; +} + +RSSManager* MainApplication::rssManager() +{ + if (!m_rssmanager) + m_rssmanager = new RSSManager(getWindow()); + return m_rssmanager; +} + +BookmarksModel* MainApplication::bookmarks() +{ + if (!m_bookmarksModel) + m_bookmarksModel = new BookmarksModel(); + return m_bookmarksModel; +} + +DownloadManager* MainApplication::downManager() +{ + if (!m_downloadManager) + m_downloadManager = new DownloadManager(); + return m_downloadManager; +} + +AutoFillModel* MainApplication::autoFill() +{ + if (!m_autofill) + m_autofill = new AutoFillModel(getWindow()); + return m_autofill; +} + +void MainApplication::aboutToCloseWindow(QupZilla *window) +{ + if (!window) + return; + + m_mainWindows.removeOne(window); + if (m_mainWindows.count() == 0 ) + quitApplication(); +} + +//Version of sessin.dat file +static const int sessionVersion = 0x0002; + +bool MainApplication::saveStateSlot() +{ + if (m_websettings->testAttribute(QWebSettings::PrivateBrowsingEnabled)) + return false; + + qDebug() << "Saving state"; + + QSettings settings(m_activeProfil+"settings.ini", QSettings::IniFormat); + settings.beginGroup("SessionRestore"); + settings.setValue("restoreSession",false); + + QFile file(m_activeProfil+"session.dat"); + file.open(QIODevice::WriteOnly); + QDataStream stream(&file); + + stream << sessionVersion; + stream << m_mainWindows.count(); + for (int i = 0; i < m_mainWindows.count(); i++) { + stream << m_mainWindows.at(i)->tabWidget()->saveState(); + stream << m_mainWindows.at(i)->saveState(); + } + file.close(); + + settings.setValue("restoreSession",true); + settings.endGroup(); + + QupZilla* qupzilla_ = getWindow(); + if (qupzilla_) { + settings.setValue("Browser-View-Settings/showBookmarksToolbar",qupzilla_->bookmarksToolbar()->isVisible()); + settings.setValue("Browser-View-Settings/showNavigationToolbar",qupzilla_->navigationToolbar()->isVisible()); + settings.setValue("Browser-View-Settings/showStatusbar",qupzilla_->statusBar()->isVisible()); + settings.setValue("Browser-View-Settings/showMenubar",qupzilla_->menuBar()->isVisible()); + } + return true; +} + +bool MainApplication::restoreStateSlot(QupZilla *window) +{ + QSettings settings(m_activeProfil+"settings.ini", QSettings::IniFormat); + settings.beginGroup("SessionRestore"); + if (!settings.value("restoreSession",false).toBool()) + return false; + if (settings.value("isCrashed",false).toBool()) { + QMessageBox::StandardButton button = QMessageBox::warning(window, tr("Last session crashed"), + tr("QupZilla crashed :-(
    Oops, last session of QupZilla ends with its crash. We are very sorry. Would you try to restore saved state?"), + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + if (button != QMessageBox::Yes) + return false; + } + if (!QFile::exists(m_activeProfil+"session.dat")) + return false; + + settings.setValue("isCrashed",false); + QFile file(m_activeProfil+"session.dat"); + file.open(QIODevice::ReadOnly); + QDataStream stream(&file); + + QByteArray tabState; + QByteArray qMainWindowState; + int version; + int windowCount; + + stream >> version; + if (version != sessionVersion) + return false; + stream >> windowCount; + stream >> tabState; + stream >> qMainWindowState; + + file.close(); + + window->tabWidget()->restoreState(tabState); + window->restoreState(qMainWindowState); + + settings.endGroup(); + + if (windowCount > 1) { + for (int i = 0; i<(windowCount-1); i++) { + stream >> tabState; + stream >> qMainWindowState; + + QupZilla* window = new QupZilla(false); + m_mainWindows.append(window); + connect(window, SIGNAL(message(MainApplication::MessageType,bool)), this, SLOT(sendMessages(MainApplication::MessageType,bool))); + QEventLoop eLoop; + connect(window, SIGNAL(startingCompleted()), &eLoop, SLOT(quit())); + eLoop.exec(); + + window->tabWidget()->restoreState(tabState); + window->restoreState(qMainWindowState); + window->tabWidget()->closeTab(0); + window->show(); + } + } + + return true; +} + +bool MainApplication::checkProfileDir() +{ + /* + $HOMEDIR + | + .qupzilla/ + | + profiles/----------- + | | + default/ profiles.ini + | + browsedata.db + */ + QString homePath = QDir::homePath(); + homePath+="/.qupzilla/"; + + if (QDir(homePath).exists()) { + QFile versionFile(homePath+"version"); + versionFile.open(QFile::ReadOnly); + if (versionFile.readAll().contains(QupZilla::VERSION.toAscii())) { + versionFile.close(); + return true; + } + versionFile.close(); +#ifdef DEVELOPING + return true; +#endif + } + + std::cout << "Creating new profile directory" << std::endl; + + QDir dir = QDir::home(); + dir.mkdir(".qupzilla"); + dir.cd(".qupzilla"); + + //.qupzilla + QFile(homePath+"version").remove(); + QFile versionFile(homePath+"version"); + versionFile.open(QFile::WriteOnly); + versionFile.write(QupZilla::VERSION.toAscii()); + versionFile.close(); + + dir.mkdir("profiles"); + dir.cd("profiles"); + + //.qupzilla/profiles + QFile(homePath+"profiles/profiles.ini").remove(); + QFile(DATADIR+"data/default/profiles/profiles.ini").copy(homePath+"profiles/profiles.ini"); + + dir.mkdir("default"); + dir.cd("default"); + + //.qupzilla/profiles/default + QFile(homePath+"profiles/default/browsedata.db").remove(); + QFile(DATADIR+"data/default/profiles/default/browsedata.db").copy(homePath+"profiles/default/browsedata.db"); + QFile(homePath+"profiles/default/background.png").remove(); + QFile(DATADIR+"data/default/profiles/default/background.png").copy(homePath+"profiles/default/background.png"); + + return dir.isReadable(); +} diff --git a/src/app/mainapplication.h b/src/app/mainapplication.h new file mode 100644 index 000000000..51fdcb330 --- /dev/null +++ b/src/app/mainapplication.h @@ -0,0 +1,108 @@ +#ifndef MAINAPPLICATION_H +#define MAINAPPLICATION_H + +#if defined(QT_NO_DEBUG) & !defined(QT_NO_DEBUG_OUTPUT) +#define QT_NO_DEBUG_OUTPUT +#endif + +#include +#include +#include +#include +#include + +#include "qtsingleapplication.h" + +class QupZilla; +class BookmarksManager; +class CookieManager; +class HistoryManager; +class HistoryModel; +class NetworkManager; +class CookieJar; +class RSSManager; +class Updater; +class PluginProxy; +class BookmarksModel; +class DownloadManager; +class AutoFillModel; + +class MainApplication : public QtSingleApplication +{ + Q_OBJECT + +public: + QString DATADIR; + explicit MainApplication(int &argc, char **argv); + + enum MessageType{ ShowFlashIcon, CheckPrivateBrowsing }; + + void loadSettings(); + bool restoreStateSlot(QupZilla* window); + void makeNewWindow(bool tryRestore, const QUrl &startUrl=QUrl()); + void addNewTab(QUrl url); + void aboutToCloseWindow(QupZilla* window); + bool isChanged(); + + inline static MainApplication* getInstance() { return static_cast(QCoreApplication::instance()); } + inline QString getActiveProfil() { return m_activeProfil; } + inline QString getActiveLanguage() { return m_activeLanguage; } + inline bool isClosing() { return m_isClosing; } + inline bool isExited() { return m_isExited; } + + bool checkProfileDir(); + + QupZilla* getWindow(); + BookmarksManager* bookmarksManager(); + CookieManager* cookieManager(); + HistoryManager* historyManager(); + HistoryModel* history(); + QWebSettings* webSettings(); + NetworkManager* networkManager(); + CookieJar* cookieJar(); + RSSManager* rssManager(); + PluginProxy* plugins(); + BookmarksModel* bookmarks(); + DownloadManager* downManager(); + AutoFillModel* autoFill(); + +public slots: + bool saveStateSlot(); + void quitApplication(); + void sendMessages(MainApplication::MessageType mes, bool state); + void receiveAppMessage(QString message); + inline void setChanged() { m_isChanged = true; } + +signals: + void message(MainApplication::MessageType mes, bool state); + +private: + void connectDatabase(); + void translateApp(); + void restoreOtherWindows(); + + BookmarksManager* m_bookmarksmanager; + CookieManager* m_cookiemanager; + HistoryManager* m_historymanager; + HistoryModel* m_historymodel; + QWebSettings* m_websettings; + NetworkManager* m_networkmanager; + CookieJar* m_cookiejar; + RSSManager* m_rssmanager; + Updater* m_updater; + PluginProxy* m_plugins; + BookmarksModel* m_bookmarksModel; + DownloadManager* m_downloadManager; + AutoFillModel* m_autofill; + + QList > m_mainWindows; + + QString m_activeProfil; + QString m_activeLanguage; + + bool m_isClosing; + bool m_isChanged; + bool m_isExited; +}; + +#endif // MAINAPPLICATION_H diff --git a/src/app/qupzilla.cpp b/src/app/qupzilla.cpp new file mode 100644 index 000000000..e9770e699 --- /dev/null +++ b/src/app/qupzilla.cpp @@ -0,0 +1,703 @@ +#include "qupzilla.h" +#include "tabwidget.h" +#include "tabbar.h" +#include "webpage.h" +#include "webview.h" +#include "lineedit.h" +#include "historymodel.h" +#include "locationbar.h" +#include "searchtoolbar.h" +#include "websearchbar.h" +#include "downloadmanager.h" +#include "cookiejar.h" +#include "cookiemanager.h" +#include "historymanager.h" +#include "bookmarksmanager.h" +#include "bookmarkstoolbar.h" +#include "clearprivatedata.h" +#include "sourceviewer.h" +#include "siteinfo.h" +#include "preferences.h" +#include "networkmanager.h" +#include "autofillmodel.h" +#include "networkmanagerproxy.h" +#include "rssmanager.h" +#include "mainapplication.h" +#include "aboutdialog.h" +#include "pluginproxy.h" +#include "qtwin.h" + +const QString QupZilla::VERSION="0.9.7"; +const QString QupZilla::BUILDTIME="03/05/2011 14:48"; +const QString QupZilla::AUTHOR="nowrep"; +const QString QupZilla::COPYRIGHT="2010-2011"; +const QString QupZilla::WWWADDRESS="http://qupzilla.ic.cz"; +const QString QupZilla::WEBKITVERSION=qWebKitVersion(); + +QupZilla::QupZilla(bool tryRestore, QUrl startUrl) : + QMainWindow() + ,p_mainApp(MainApplication::getInstance()) + ,m_tryRestore(tryRestore) + ,m_startingUrl(startUrl) + ,m_actionPrivateBrowsing(0) + ,m_webInspectorDock(0) + ,m_webSearchToolbar(0) +{ + this->resize(640,480); + this->setWindowState(Qt::WindowMaximized); + this->setWindowTitle("QupZilla"); + setUpdatesEnabled(false); + + m_activeProfil = p_mainApp->getActiveProfil(); + m_activeLanguage = p_mainApp->getActiveLanguage(); + + QDesktopServices::setUrlHandler("http", this, "loadAddress"); + + setupUi(); + setupMenu(); + QTimer::singleShot(0, this, SLOT(postLaunch())); + connect(p_mainApp, SIGNAL(message(MainApplication::MessageType,bool)), this, SLOT(receiveMessage(MainApplication::MessageType,bool))); +} + +void QupZilla::loadSettings() +{ + QSettings settings(m_activeProfil+"settings.ini", QSettings::IniFormat); + + //Url settings + settings.beginGroup("Web-URL-Settings"); + m_homepage = settings.value("homepage","http://qupzilla.ic.cz/search/").toUrl(); + m_newtab = settings.value("newTabUrl","").toUrl(); + settings.endGroup(); + + QWebSettings* websettings=p_mainApp->webSettings(); + websettings->setAttribute(QWebSettings::JavascriptCanAccessClipboard, true); + //Web browsing settings + settings.beginGroup("Web-Browser-Settings"); + bool allowFlash = settings.value("allowFlash",true).toBool(); + settings.endGroup(); + m_allowFlashIcon->setVisible(allowFlash); + + //Browser Window settings + settings.beginGroup("Browser-View-Settings"); + m_menuTextColor = settings.value("menuTextColor", QColor(Qt::black)).value(); + setBackground(m_menuTextColor); + m_bookmarksToolbar->setColor(m_menuTextColor); + m_ipLabel->setStyleSheet("QLabel {color: "+m_menuTextColor.name()+";}"); + bool showStatusBar = settings.value("showStatusBar",true).toBool(); + bool showHomeIcon = settings.value("showHomeButton",true).toBool(); + bool showBackForwardIcons = settings.value("showBackForwardButtons",true).toBool(); + bool showBookmarksToolbar = settings.value("showBookmarksToolbar",true).toBool(); + bool showNavigationToolbar = settings.value("showNavigationToolbar",true).toBool(); + bool showMenuBar = settings.value("showMenubar",true).toBool(); + bool makeTransparent = settings.value("useTransparentBackground",false).toBool(); + settings.endGroup(); + + statusBar()->setVisible(showStatusBar); + m_actionShowStatusbar->setChecked(showStatusBar); + + m_bookmarksToolbar->setVisible(showBookmarksToolbar); + m_actionShowBookmarksToolbar->setChecked(showBookmarksToolbar); + + m_navigation->setVisible(showNavigationToolbar); + m_actionShowToolbar->setChecked(showNavigationToolbar); + + m_actionShowMenubar->setChecked(showMenuBar); + menuBar()->setVisible(showMenuBar); + m_navigation->actions().at(m_navigation->actions().count()-2)->setVisible(!showMenuBar); + + m_buttonHome->setVisible(showHomeIcon); + m_buttonBack->setVisible(showBackForwardIcons); + m_buttonNext->setVisible(showBackForwardIcons); + + //Private browsing + m_actionPrivateBrowsing->setChecked( p_mainApp->webSettings()->testAttribute(QWebSettings::PrivateBrowsingEnabled) ); + m_privateBrowsing->setVisible( p_mainApp->webSettings()->testAttribute(QWebSettings::PrivateBrowsingEnabled) ); + + if (!makeTransparent) + return; + //Opacity +#ifdef Q_WS_X11 + setAttribute(Qt::WA_TranslucentBackground); + setAttribute(Qt::WA_NoSystemBackground, false); + QPalette pal = palette(); + QColor bg = pal.window().color(); + bg.setAlpha(180); + pal.setColor(QPalette::Window, bg); + setPalette(pal); + ensurePolished(); // workaround Oxygen filling the background + setAttribute(Qt::WA_StyledBackground, false); +#endif + if (QtWin::isCompositionEnabled()) { + QtWin::extendFrameIntoClientArea(this); + setContentsMargins(0, 0, 0, 0); + } + setWindowIcon(QIcon(":/icons/qupzilla.png")); +} + +void QupZilla::receiveMessage(MainApplication::MessageType mes, bool state) +{ + switch (mes) { + case MainApplication::ShowFlashIcon: + m_allowFlashIcon->setVisible(state); + break; + + case MainApplication::CheckPrivateBrowsing: + m_privateBrowsing->setVisible(state); + m_actionPrivateBrowsing->setChecked(state); + break; + + default: + qWarning() << "Unresolved message sent!"; + break; + } +} + +void QupZilla::refreshHistory(int index) +{ + QWebHistory* history; + if (index == -1) + history = weView()->page()->history(); + else + history = weView()->page()->history(); + + if (history->canGoBack()) { + m_buttonBack->setEnabled(true); + }else{ + m_buttonBack->setEnabled(false); + } + + if (history->canGoForward()) { + m_buttonNext->setEnabled(true); + }else{ + m_buttonNext->setEnabled(false); + } +} + +void QupZilla::goAtHistoryIndex() +{ + if (QAction *action = qobject_cast(sender())) { + weView()->page()->history()->goToItem(weView()->page()->history()->itemAt(action->data().toInt())); + } + refreshHistory(); +} + +void QupZilla::aboutToShowHistoryBackMenu() +{ + if (!m_menuBack || !weView()) + return; + m_menuBack->clear(); + QWebHistory* history = weView()->history(); + int curindex = history->currentItemIndex(); + for (int i = curindex-1;i>=0;i--) { + QWebHistoryItem item = history->itemAt(i); + if (item.isValid()) { + QString title = item.title(); + if (title.length()>40) { + title.truncate(40); + title+=".."; + } + QAction* action = m_menuBack->addAction(m_locationBar->icon(item.url()),title, this, SLOT(goAtHistoryIndex())); + action->setData(i); + } + } +} + +void QupZilla::aboutToShowHistoryNextMenu() +{ + if (!m_menuForward || !weView()) + return; + m_menuForward->clear(); + QWebHistory* history = weView()->history(); + int curindex = history->currentItemIndex(); + for (int i = curindex+1;icount();i++) { + QWebHistoryItem item = history->itemAt(i); + if (item.isValid()) { + QString title = item.title(); + if (title.length()>40) { + title.truncate(40); + title+=".."; + } + QAction* action = m_menuForward->addAction(m_locationBar->icon(item.url()),title, this, SLOT(goAtHistoryIndex())); + action->setData(i); + } + } +} + +void QupZilla::aboutToShowBookmarksMenu() +{ + m_menuBookmarks->clear(); + m_menuBookmarks->addAction(tr("Bookmark This Page"), this, SLOT(bookmarkPage()))->setShortcut(QKeySequence("Ctrl+D")); + m_menuBookmarks->addAction(tr("Bookmark All Tabs"), this, SLOT(bookmarkAllTabs())); + m_menuBookmarks->addAction(QIcon::fromTheme("user-bookmarks"), tr("Organize Bookmarks"), this, SLOT(showBookmarksManager()))->setShortcut(QKeySequence("Ctrl+Shift+O")); + m_menuBookmarks->addSeparator(); + if (m_tabWidget->count() == 1) + m_menuBookmarks->actions().at(1)->setEnabled(false); + QSqlQuery query; + query.exec("SELECT title, url FROM bookmarks WHERE folder='bookmarksMenu'"); + while(query.next()) { + QUrl url = query.value(1).toUrl(); + QString title = query.value(0).toString(); + if (title.length()>40) { + title.truncate(40); + title+=".."; + } + m_menuBookmarks->addAction(LocationBar::icon(url), title, this, SLOT(loadActionUrl()))->setData(url); + } + + QMenu* folderBookmarks = new QMenu(tr("Bookmarks In ToolBar"), m_menuBookmarks); + folderBookmarks->setIcon(QIcon(style()->standardIcon(QStyle::SP_DirOpenIcon))); + + query.exec("SELECT title, url FROM bookmarks WHERE folder='bookmarksToolbar'"); + while(query.next()) { + QUrl url = query.value(1).toUrl(); + QString title = query.value(0).toString(); + if (title.length()>40) { + title.truncate(40); + title+=".."; + } + folderBookmarks->addAction(LocationBar::icon(url), title, this, SLOT(loadActionUrl()))->setData(url); + } + if (folderBookmarks->isEmpty()) + folderBookmarks->addAction(tr("Empty")); + m_menuBookmarks->addMenu(folderBookmarks); + + query.exec("SELECT name FROM folders"); + while(query.next()) { + QMenu* tempFolder = new QMenu(query.value(0).toString(), m_menuBookmarks); + tempFolder->setIcon(QIcon(style()->standardIcon(QStyle::SP_DirOpenIcon))); + + QSqlQuery query2; + query2.exec("SELECT title, url FROM bookmarks WHERE folder='"+query.value(0).toString()+"'"); + while(query2.next()) { + QUrl url = query2.value(1).toUrl(); + QString title = query2.value(0).toString(); + if (title.length()>40) { + title.truncate(40); + title+=".."; + } + tempFolder->addAction(LocationBar::icon(url), title, this, SLOT(loadActionUrl()))->setData(url); + } + if (tempFolder->isEmpty()) + tempFolder->addAction(tr("Empty")); + m_menuBookmarks->addMenu(tempFolder); + } + +} + +void QupZilla::aboutToShowHistoryMenu() +{ + if (!weView()) + return; + m_menuHistory->clear(); + m_menuHistory->addAction( +#ifdef Q_WS_X11 + style()->standardIcon(QStyle::SP_ArrowBack) +#else + QIcon(":/icons/faenza/back.png") +#endif + , tr("Back"), this, SLOT(goBack()))->setShortcut(QKeySequence("Ctrl+Left")); + m_menuHistory->addAction( +#ifdef Q_WS_X11 + style()->standardIcon(QStyle::SP_ArrowForward) +#else + QIcon(":/icons/faenza/forward.png") +#endif + , tr("Forward"), this, SLOT(goNext()))->setShortcut(QKeySequence("Ctrl+Right")); + m_menuHistory->addAction( +#ifdef Q_WS_X11 + QIcon::fromTheme("go-home") +#else + QIcon(":/icons/faenza/home.png") +#endif + , tr("Home"), this, SLOT(goHome()))->setShortcut(QKeySequence("Alt+Home")); + + if (!weView()->history()->canGoBack()) + m_menuHistory->actions().at(0)->setEnabled(false); + if (!weView()->history()->canGoForward()) + m_menuHistory->actions().at(1)->setEnabled(false); + + m_menuHistory->addAction(QIcon(":/icons/menu/history.png"), tr("Show All History"), this, SLOT(showHistoryManager()))->setShortcut(QKeySequence("Ctrl+H")); + m_menuHistory->addSeparator(); + + QSqlQuery query; + query.exec("SELECT title, url FROM history ORDER BY date DESC LIMIT 10"); + while(query.next()) { + QUrl url = query.value(1).toUrl(); + QString title = query.value(0).toString(); + if (title.length()>40) { + title.truncate(40); + title+=".."; + } + m_menuHistory->addAction(LocationBar::icon(url), title, this, SLOT(loadActionUrl()))->setData(url); + } +} + +void QupZilla::aboutToShowHelpMenu() +{ + m_menuHelp->clear(); + m_menuHelp->addAction(tr("Report Bug"), this, SLOT(reportBug())); + m_menuHelp->addSeparator(); + p_mainApp->plugins()->populateHelpMenu(m_menuHelp); + m_menuHelp->addAction(QIcon(":/icons/menu/qt.png"), tr("About Qt"), qApp, SLOT(aboutQt())); + m_menuHelp->addAction(QIcon(":/icons/qupzilla.png"), tr("About QupZilla"), this, SLOT(aboutQupZilla())); +} + +void QupZilla::aboutToShowToolsMenu() +{ + m_menuTools->clear(); + m_menuTools->addAction(tr("Web Search"), this, SLOT(webSearch()))->setShortcut(QKeySequence("Ctrl+K")); + m_menuTools->addAction(QIcon::fromTheme("dialog-information"), tr("Page Info"), this, SLOT(showPageInfo()))->setShortcut(QKeySequence("Ctrl+I")); + m_menuTools->addSeparator(); + m_menuTools->addAction(tr("Download Manager"), this, SLOT(showDownloadManager()))->setShortcut(QKeySequence("Ctrl+Y")); + m_menuTools->addAction(tr("Cookies Manager"), this, SLOT(showCookieManager())); + m_menuTools->addAction(QIcon(":/icons/menu/rss.png"), tr("RSS Reader"), this, SLOT(showRSSManager())); + m_menuTools->addAction(QIcon::fromTheme("edit-clear"), tr("Clear Recent History"), this, SLOT(showClearPrivateData())); + m_actionPrivateBrowsing = new QAction(tr("Private Browsing"), this); + m_actionPrivateBrowsing->setCheckable(true); + m_actionPrivateBrowsing->setChecked(p_mainApp->webSettings()->testAttribute(QWebSettings::PrivateBrowsingEnabled)); + connect(m_actionPrivateBrowsing, SIGNAL(triggered(bool)), this, SLOT(startPrivate(bool))); + m_menuTools->addAction(m_actionPrivateBrowsing); + m_menuTools->addSeparator(); + p_mainApp->plugins()->populateToolsMenu(m_menuTools); + m_menuTools->addAction(QIcon(":/icons/faenza/settings.png"), tr("Preferences"), this, SLOT(showPreferences()))->setShortcut(QKeySequence("Ctrl+P")); +} + +void QupZilla::aboutToShowViewMenu() +{ + if (!weView()) + return; + + if (weView()->isLoading()) + m_actionStop->setEnabled(true); + else + m_actionStop->setEnabled(false); +} + +void QupZilla::bookmarkPage() +{ + p_mainApp->bookmarksManager()->addBookmark(weView()); +} + +void QupZilla::addBookmark(const QUrl &url, const QString &title) +{ + p_mainApp->bookmarksManager()->insertBookmark(url, title); +} + +void QupZilla::bookmarkAllTabs() +{ + p_mainApp->bookmarksManager()->insertAllTabs(); +} + +void QupZilla::loadActionUrl() +{ + if (QAction *action = qobject_cast(sender())) { + loadAddress(action->data().toUrl()); + } +} + +void QupZilla::urlEnter() +{ + if (m_locationBar->text().isEmpty()) + return; + loadAddress(QUrl(WebView::guessUrlFromString(m_locationBar->text()))); + weView()->setFocus(); +} + +void QupZilla::showCookieManager() +{ + CookieManager* m = p_mainApp->cookieManager(); + m->refreshTable(); + m->show(); +} + +void QupZilla::showHistoryManager() +{ + HistoryManager* m = p_mainApp->historyManager(); + m->refreshTable(); + m->setMainWindow(this); + m->show(); +} + +void QupZilla::showRSSManager() +{ + RSSManager* m = p_mainApp->rssManager(); + m->refreshTable(); + m->setMainWindow(this); + m->show(); +} + +void QupZilla::showBookmarksManager() +{ + BookmarksManager* m = p_mainApp->bookmarksManager(); + m->refreshTable(); + m->setMainWindow(this); + m->show(); +} + +void QupZilla::showClearPrivateData() +{ + ClearPrivateData clear(this, this); + clear.exec(); +} + +void QupZilla::showDownloadManager() +{ + MainApplication::getInstance()->downManager()->show(); +} + +void QupZilla::showPreferences() +{ + bool flashIconVisibility = m_allowFlashIcon->isVisible(); + Preferences prefs(this, this); + prefs.exec(); + + if (flashIconVisibility != m_allowFlashIcon->isVisible()) + emit message(MainApplication::ShowFlashIcon, m_allowFlashIcon->isVisible()); +} + +void QupZilla::showSource() +{ + SourceViewer* source = new SourceViewer(this); + source->setAttribute(Qt::WA_DeleteOnClose); + source->show(); +} + +void QupZilla::showPageInfo() +{ + SiteInfo* info = new SiteInfo(this, this); + info->setAttribute(Qt::WA_DeleteOnClose); + info->show(); +} + +void QupZilla::showBookmarksToolbar() +{ + if (m_bookmarksToolbar->isVisible()) { + m_bookmarksToolbar->setVisible(false); + m_actionShowBookmarksToolbar->setChecked(false); + }else{ + m_bookmarksToolbar->setVisible(true); + m_actionShowBookmarksToolbar->setChecked(true); + } +} + +void QupZilla::showNavigationToolbar() +{ + if (!menuBar()->isVisible() && !m_actionShowToolbar->isChecked()) + showMenubar(); + + if (m_navigation->isVisible()) { + m_navigation->setVisible(false); + m_actionShowToolbar->setChecked(false); + }else{ + m_navigation->setVisible(true); + m_actionShowToolbar->setChecked(true); + } +} + +void QupZilla::showMenubar() +{ + if (!m_navigation->isVisible() && !m_actionShowMenubar->isChecked()) + showNavigationToolbar(); + + menuBar()->setVisible(!menuBar()->isVisible()); + m_navigation->actions().at(m_navigation->actions().count()-2)->setVisible(!menuBar()->isVisible()); + m_actionShowMenubar->setChecked(menuBar()->isVisible()); +} + +void QupZilla::showStatusbar() +{ + if (statusBar()->isVisible()) { + statusBar()->setVisible(false); + m_actionShowStatusbar->setChecked(false); + }else{ + statusBar()->setVisible(true); + m_actionShowStatusbar->setChecked(true); + } +} + +void QupZilla::showInspector() +{ + if (!m_webInspectorDock) { + m_webInspectorDock = new QDockWidget(this); + if (m_webInspector) + delete m_webInspector; + m_webInspector = new QWebInspector(this); + m_webInspector->setPage(weView()->page()); + addDockWidget(Qt::BottomDockWidgetArea, m_webInspectorDock); + m_webInspectorDock->setWindowTitle(tr("Web Inspector")); + m_webInspectorDock->setObjectName("WebInspector"); + m_webInspectorDock->setWidget(m_webInspector); + m_webInspectorDock->setFeatures(QDockWidget::DockWidgetClosable); + m_webInspectorDock->setContextMenuPolicy(Qt::CustomContextMenu); + } else if (m_webInspectorDock->isVisible()) { //Next tab + m_webInspectorDock->show(); + m_webInspector->setPage(weView()->page()); + m_webInspectorDock->setWidget(m_webInspector); + } else { //Showing hidden dock + m_webInspectorDock->show(); + if (m_webInspector->page() != weView()->page()) { + m_webInspector->setPage(weView()->page()); + m_webInspectorDock->setWidget(m_webInspector); + } + } +} + +void QupZilla::aboutQupZilla() +{ + AboutDialog about(this); + about.exec(); +} + +void QupZilla::searchOnPage() +{ + if (!m_webSearchToolbar) { + m_webSearchToolbar = new SearchToolBar(this); + addToolBar(Qt::BottomToolBarArea, m_webSearchToolbar); + m_webSearchToolbar->showBar(); + return; + } + if (m_webSearchToolbar->isVisible()) { + m_webSearchToolbar->hideBar(); + weView()->setFocus(); + }else{ + m_webSearchToolbar->showBar(); + } +} + +void QupZilla::openFile() +{ + QString filePath = QFileDialog::getOpenFileName(this, tr("Open file..."), QDir::homePath(), "(*.html *.htm *.jpg *.png)"); + if (!filePath.isEmpty()) + loadAddress(QUrl(filePath)); +} + +void QupZilla::fullScreen(bool make) +{ + if (make) { + m_menuBarVisible = menuBar()->isVisible(); + m_statusBarVisible = statusBar()->isVisible(); + setWindowState(windowState() | Qt::WindowFullScreen); + menuBar()->hide(); + statusBar()->hide(); + }else{ + setWindowState(windowState() & ~Qt::WindowFullScreen); + if (m_menuBarVisible) + showMenubar(); + if (m_statusBarVisible) + showStatusbar(); + } + m_actionShowFullScreen->setChecked(make); + m_actionExitFullscreen->setVisible(make); +} + +void QupZilla::savePage() +{ + QNetworkRequest request(weView()->url()); + + DownloadManager* dManager = MainApplication::getInstance()->downManager(); + dManager->download(request); +} + +void QupZilla::printPage() +{ + QPrintPreviewDialog* dialog = new QPrintPreviewDialog(this); + connect(dialog, SIGNAL(paintRequested(QPrinter*)), weView(), SLOT(print(QPrinter*))); + dialog->exec(); + delete dialog; +} + +void QupZilla::startPrivate(bool state) +{ + if (state) { + QString title = tr("Are you sure you want to turn on private browsing?"); + QString text1 = tr("When private browsing is turned on, some actions concerning your privacy will be disabled:"); + + QStringList actions; + actions.append(tr("Webpages are not added to the history.")); + actions.append(tr("New cookies are not stored, but current cookies can be accessed.")); + actions.append(tr("Your session won't be stored.")); + + QString text2 = tr("Until you close the window, you can still click the Back and Forward " + "buttons to return to the webpages you have opened."); + + QString message = QString(QLatin1String("%1

    %2

    • %3

    %4

    ")).arg(title, text1, actions.join(QLatin1String("
  1. ")), text2); + + QMessageBox::StandardButton button = QMessageBox::question(this, tr("Start Private Browsing"), + message, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + if (button != QMessageBox::Yes) + return; + } + p_mainApp->webSettings()->setAttribute(QWebSettings::PrivateBrowsingEnabled, state); + p_mainApp->history()->setSaving(!state); + p_mainApp->cookieJar()->setAllowCookies(!state); + emit message(MainApplication::CheckPrivateBrowsing, state); +} + +void QupZilla::closeEvent(QCloseEvent* event) +{ + if (p_mainApp->isClosing()) + return; + + QSettings settings(m_activeProfil+"settings.ini", QSettings::IniFormat); + settings.beginGroup("Web-URL-Settings"); + + if (settings.value("afterLaunch",0).toInt()!=2 && m_tabWidget->count()>1) { + QMessageBox::StandardButton button = QMessageBox::warning(this, tr("There are still open tabs"), + tr("There are still %1 open tabs and your session won't be stored. Are you sure to quit?").arg(m_tabWidget->count()), QMessageBox::Yes | QMessageBox::No); + if (button != QMessageBox::Yes) { + event->ignore(); + return; + } + } + settings.endGroup(); + + p_mainApp->cookieJar()->saveCookies(); + p_mainApp->saveStateSlot(); + p_mainApp->aboutToCloseWindow(this); + + this->~QupZilla(); + event->accept(); +} + +void QupZilla::quitApp() +{ + QSettings settings(m_activeProfil+"settings.ini", QSettings::IniFormat); + settings.beginGroup("Web-URL-Settings"); + + if (settings.value("afterLaunch",0).toInt()!=2 && m_tabWidget->count()>1) { + QMessageBox::StandardButton button = QMessageBox::warning(this, tr("There are still open tabs"), + tr("There are still %1 open tabs and your session won't be stored. Are you sure to quit?").arg(m_tabWidget->count()), QMessageBox::Yes | QMessageBox::No); + if (button != QMessageBox::Yes) + return; + } + settings.endGroup(); + + p_mainApp->quitApplication(); +} + +QupZilla::~QupZilla() +{ + delete m_tabWidget; + delete m_privateBrowsing; + delete m_allowFlashIcon; + delete m_menuBack; + delete m_menuForward; + delete m_locationBar; + delete m_searchLine; + delete m_bookmarksToolbar; + delete m_webSearchToolbar; + delete m_buttonBack; + delete m_buttonNext; + delete m_buttonHome; + delete m_buttonStop; + delete m_buttonReload; + delete m_actionExitFullscreen; + delete m_navigationSplitter; + delete m_navigation; + delete m_progressBar; + + if (m_webInspectorDock) { + delete m_webInspector; + delete m_webInspectorDock; + } +} diff --git a/src/app/qupzilla.h b/src/app/qupzilla.h new file mode 100644 index 000000000..5eb3fc3b4 --- /dev/null +++ b/src/app/qupzilla.h @@ -0,0 +1,234 @@ +#ifndef QUPZILLA_H +#define QUPZILLA_H + +#if defined(QT_NO_DEBUG) & !defined(QT_NO_DEBUG_OUTPUT) +#define QT_NO_DEBUG_OUTPUT +#endif + +//Comment for release building +#define DEVELOPING +//Check if i don't fuck anything + +#ifdef QT_NO_DEBUG +#ifdef DEVELOPING +#error "TRYING TO RELEASE WITH DEVELOPING FLAG" +#endif +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "qwebkitversion.h" + +#include "webtab.h" +#include "webview.h" +#include "tabwidget.h" +#include "locationbar.h" +#include "mainapplication.h" +#include "websearchbar.h" + +class TabWidget; +class WebView; +class LineEdit; +class LocationBar; +class SearchToolBar; +class WebSearchBar; +class BookmarksToolbar; +class AutoFillModel; +class MainApplication; +class WebTab; +class QupZilla : public QMainWindow +{ + Q_OBJECT + +public: + static const QString VERSION; + static const QString BUILDTIME; + static const QString AUTHOR; + static const QString COPYRIGHT; + static const QString WWWADDRESS; + static const QString WEBKITVERSION; + + explicit QupZilla(bool m_tryRestore=true, QUrl startUrl=QUrl()); + ~QupZilla(); + + void refreshAddressBar(); + void addBookmark(const QUrl &url, const QString &title); + void installTranslator(); + void loadSettings(); + void showInspector(); + void setBackground(QColor textColor); + + inline WebView* weView() const { return qobject_cast(m_tabWidget->widget(m_tabWidget->currentIndex()))->view(); } + inline WebView* weView(int index) const { return qobject_cast(m_tabWidget->widget(index))->view(); } + inline LocationBar* locationBar(){ return m_locationBar; } + inline TabWidget* tabWidget(){ return m_tabWidget; } + inline BookmarksToolbar* bookmarksToolbar(){ return m_bookmarksToolbar; } + inline MainApplication* getMainApp() { return p_mainApp; } + + inline QAction* buttonStop(){ return m_buttonStop; } + inline QAction* buttonReload(){ return m_buttonReload; } + inline QProgressBar* progressBar(){ return m_progressBar; } + inline QToolBar* navigationToolbar(){ return m_navigation; } + inline QString activeProfil(){ return m_activeProfil; } + inline QString activeLanguage(){ return m_activeLanguage; } + inline QDockWidget* inspectorDock(){ return m_webInspectorDock; } + inline QLabel* ipLabel(){ return m_ipLabel; } + inline QColor menuTextColor() { return m_menuTextColor; } + inline QAction* acShowBookmarksToolbar() { return m_actionShowBookmarksToolbar; } + +signals: + void loadHistory(); + void startingCompleted(); + void message(MainApplication::MessageType mes, bool state); + +public slots: + void refreshHistory(int index=-1); + void loadActionUrl(); + void bookmarkPage(); + void loadAddress(QUrl url) { weView()->load(url); m_locationBar->setText(url.toEncoded()); } + void showSource(); + void showPageInfo(); + void receiveMessage(MainApplication::MessageType mes, bool state); + +private slots: + void closeEvent(QCloseEvent* event); + void postLaunch(); + void goAtHistoryIndex(); + void goNext() { weView()->forward(); } + void goBack() { weView()->back(); } + void goHome() { loadAddress(m_homepage); } + void stop() { weView()->stop(); } + void reload() { weView()->reload(); } + void urlEnter(); + void aboutQupZilla(); + void addTab() { m_tabWidget->addView(QUrl(), tr("New tab"), TabWidget::NewTab, true); } + void printPage(); + + void aboutToShowHistoryBackMenu(); + void aboutToShowHistoryNextMenu(); + void aboutToShowHistoryMenu(); + void aboutToShowBookmarksMenu(); + void aboutToShowToolsMenu(); + void aboutToShowHelpMenu(); + void aboutToShowViewMenu(); + + void searchOnPage(); + void showCookieManager(); + void showHistoryManager(); + void showBookmarksManager(); + void showRSSManager(); + void showDownloadManager(); + + void showMenubar(); + void showNavigationToolbar(); + void showBookmarksToolbar(); + void showStatusbar(); + void showClearPrivateData(); + void showPreferences(); + + void bookmarkAllTabs(); + void newWindow() { p_mainApp->makeNewWindow(false); } + + void openLocation() { m_locationBar->setFocus(); m_locationBar->selectAll(); } + void openFile(); + void savePage(); + void sendLink() { QDesktopServices::openUrl(QUrl("mailto:?body="+weView()->url().toString())); } + void webSearch() { m_searchLine->setFocus(); } + + void copy() { QApplication::clipboard()->setText(weView()->selectedText()); } + void selectAll() { weView()->selectAll(); } + void reportBug() { m_tabWidget->addView(QUrl("http://qupzilla.ic.cz/bugzilla/?do=newtask&project=2")); } + + void zoomIn() { weView()->zoomIn(); } + void zoomOut() { weView()->zoomOut(); } + void zoomReset() { weView()->zoomReset(); } + void fullScreen(bool make); + void startPrivate(bool state); + + void quitApp(); + +private: + void setupUi(); + void setupMenu(); + + MainApplication* p_mainApp; + + bool m_tryRestore; + QUrl m_startingUrl; + QUrl m_newtab; + QUrl m_homepage; + + QToolButton* m_supMenu; + QMenu* m_superMenu; + QMenu* m_menuFile; + QMenu* m_menuEdit; + QMenu* m_menuTools; + QMenu* m_menuHelp; + QMenu* m_menuView; + QMenu* m_menuBookmarks; + QMenu* m_menuHistory; + QMenu* m_menuBack; + QMenu* m_menuForward; + QAction* m_actionShowToolbar; + QAction* m_actionShowBookmarksToolbar; + QAction* m_actionShowStatusbar; + QAction* m_actionShowMenubar; + QAction* m_actionShowFullScreen; + QAction* m_actionPrivateBrowsing; + QAction* m_actionStop; + QAction* m_actionReload; + + QLabel* m_privateBrowsing; + QLabel* m_allowFlashIcon; + QPointer m_webInspector; + QPointer m_webInspectorDock; + + WebSearchBar* m_searchLine; + SearchToolBar* m_webSearchToolbar; + BookmarksToolbar* m_bookmarksToolbar; + LocationBar* m_locationBar; + TabWidget* m_tabWidget; + + QSplitter* m_navigationSplitter; + QAction* m_buttonBack; + QAction* m_buttonNext; + QAction* m_buttonHome; + QAction* m_buttonStop; + QAction* m_buttonReload; + QAction* m_actionExitFullscreen; + QProgressBar* m_progressBar; + QLabel* m_ipLabel; + QToolBar* m_navigation; + + QString m_activeProfil; + QString m_activeLanguage; + QColor m_menuTextColor; + + //Used for F11 FullScreen remember visibility + //of menubar and statusbar + bool m_menuBarVisible; + bool m_statusBarVisible; +}; + +#endif // QUPZILLA_H diff --git a/src/appicon.rc b/src/appicon.rc new file mode 100644 index 000000000..e490cd766 --- /dev/null +++ b/src/appicon.rc @@ -0,0 +1,32 @@ +#include "winver.h" + +IDI_ICON1 ICON DISCARDABLE "icon.ico" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 0,9,7,0 + PRODUCTVERSION 0,9,7,0 + FILEFLAGS 0x0L + FILEFLAGSMASK 0x3fL + FILEOS 0x00040004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "000004b0" + BEGIN + VALUE "CompanyName", "QupZilla Association" + VALUE "FileDescription", "QupZilla Web Browser" + VALUE "FileVersion", "0.9.7.0" + VALUE "LegalCopyright", "Copyright (C) 2010-2011 nowrep" + VALUE "InternalName", "qupzilla" + VALUE "OriginalFilename", "qupzilla.exe" + VALUE "ProductName", "QupZilla" + VALUE "ProductVersion", "0.9.7.0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1200 + END +END diff --git a/src/autofill/autofillmodel.cpp b/src/autofill/autofillmodel.cpp new file mode 100644 index 000000000..0129182d9 --- /dev/null +++ b/src/autofill/autofillmodel.cpp @@ -0,0 +1,183 @@ +#include "autofillmodel.h" +#include "qupzilla.h" +#include "webview.h" +#include "mainapplication.h" +#include "autofillwidget.h" + +AutoFillModel::AutoFillModel(QupZilla* mainClass, QObject *parent) : + QObject(parent) + ,p_QupZilla(mainClass) + ,m_isStoring(false) +{ + QTimer::singleShot(0, this, SLOT(loadSettings())); +} + +void AutoFillModel::loadSettings() +{ + QSettings settings(MainApplication::getInstance()->getActiveProfil()+"settings.ini", QSettings::IniFormat); + settings.beginGroup("Web-Browser-Settings"); + m_isStoring = settings.value("AutoFillForms",true).toBool(); + settings.endGroup(); +} + +bool AutoFillModel::isStored(const QUrl &url) +{ + QString server = url.host(); + QSqlQuery query; + query.exec("SELECT count(id) FROM autofill WHERE server='"+server+"'"); + query.next(); + if (query.value(0).toInt()>0) + return true; + return false; +} + +bool AutoFillModel::isStoringEnabled(const QUrl &url) +{ + if (!m_isStoring) + return false; + QString server = url.host(); + QSqlQuery query; + query.exec("SELECT count(id) FROM autofill_exceptions WHERE server='"+server+"'"); + query.next(); + if (query.value(0).toInt()>0) + return false; + return true; +} + +void AutoFillModel::blockStoringfor (const QUrl &url) +{ + QString server = url.host(); + QSqlQuery query; + query.exec("INSERT INTO autofill_exceptions (server) VALUES ('"+server+"')"); +} + +QString AutoFillModel::getUsername(const QUrl &url) +{ + QString server = url.host(); + QSqlQuery query; + query.exec("SELECT username FROM autofill WHERE server='"+server+"'"); + query.next(); + return query.value(0).toString(); +} + +QString AutoFillModel::getPassword(const QUrl &url) +{ + QString server = url.host(); + QSqlQuery query; + query.exec("SELECT password FROM autofill WHERE server='"+server+"'"); + query.next(); + return query.value(0).toString(); +} + +///HTTP Authorization +bool AutoFillModel::addEntry(const QUrl &url, const QString &name, const QString &pass) +{ + QSqlQuery query; + query.exec("SELECT username FROM autofill WHERE server='"+url.host()+"'"); + if (query.next()) + return false; + query.prepare("INSERT INTO autofill (server, username, password) VALUES (?,?,?)"); + query.bindValue(0, url.host()); + query.bindValue(1, name); + query.bindValue(2, pass); + return query.exec(); +} + +///WEB Form +bool AutoFillModel::addEntry(const QUrl &url, const QByteArray &data, const QString &pass) +{ + QSqlQuery query; + query.exec("SELECT data FROM autofill WHERE server='"+url.host()+"'"); + if (query.next()) + return false; + + query.prepare("INSERT INTO autofill (server, data, password) VALUES (?,?,?)"); + query.bindValue(0, url.host()); + query.bindValue(1, data); + query.bindValue(2, pass); + return query.exec(); +} + +void AutoFillModel::completePage(WebView* view) +{ + if (!isStored(view->url())) + return; + + QWebFrame* frame = view->page()->mainFrame(); + QWebElementCollection inputs = frame->findAllElements("input"); + QSqlQuery query; + query.exec("SELECT data FROM autofill WHERE server='"+view->url().host()+"'"); + query.next(); + QByteArray data = query.value(0).toByteArray(); + if (data.isEmpty()) + return; + + QList > arguments = QUrl::fromEncoded(QByteArray("http://bla.com/?"+data)).queryItems(); + for (int i = 0; iwebSettings()->testAttribute(QWebSettings::PrivateBrowsingEnabled)) + return; + m_lastOutgoingData = outgoingData; + + QVariant v = request.attribute((QNetworkRequest::Attribute)(QNetworkRequest::User + 100)); + QWebPage* webPage = (QWebPage*)(v.value()); + v = request.attribute((QNetworkRequest::Attribute)(QNetworkRequest::User + 102)); + WebView* webView = (WebView*)(v.value()); + if (!webPage || !webView) + return; + + v = request.attribute((QNetworkRequest::Attribute)(QNetworkRequest::User + 101)); + QWebPage::NavigationType type = (QWebPage::NavigationType)v.toInt(); + + if (type!=QWebPage::NavigationTypeFormSubmitted) + return; + + QString passwordName=""; + QWebFrame* frame = webPage->mainFrame(); + QWebElementCollection inputs = frame->findAllElements("input"); + for (int i = 0; i > arguments = QUrl::fromEncoded(QByteArray("http://bla.com/?"+outgoingData)).queryItems(); + for (int i = 0; iaddNotification(aWidget); + +} diff --git a/src/autofill/autofillmodel.h b/src/autofill/autofillmodel.h new file mode 100644 index 000000000..58c8f8862 --- /dev/null +++ b/src/autofill/autofillmodel.h @@ -0,0 +1,44 @@ +#ifndef AUTOFILLMODEL_H +#define AUTOFILLMODEL_H + +#if defined(QT_NO_DEBUG) & !defined(QT_NO_DEBUG_OUTPUT) +#define QT_NO_DEBUG_OUTPUT +#endif + +#include +#include +#include + +class QupZilla; +class WebView; +class AutoFillModel : public QObject +{ + Q_OBJECT +public: + explicit AutoFillModel(QupZilla* mainClass, QObject *parent = 0); + void completePage(WebView* view); + + bool isStored(const QUrl &url); + bool isStoringEnabled(const QUrl &url); + void blockStoringfor (const QUrl &url); + + QString getUsername(const QUrl &url); + QString getPassword(const QUrl &url); + bool addEntry(const QUrl &url, const QString &name, const QString &pass); + bool addEntry(const QUrl &url, const QByteArray &data, const QString &pass); + + void post(const QNetworkRequest &request, const QByteArray &outgoingData); + +signals: + +public slots: + void loadSettings(); + +private: + QupZilla* p_QupZilla; + QByteArray m_lastOutgoingData; + bool m_isStoring; + +}; + +#endif // AUTOFILLMODEL_H diff --git a/src/autofill/autofillwidget.cpp b/src/autofill/autofillwidget.cpp new file mode 100644 index 000000000..81ecd109d --- /dev/null +++ b/src/autofill/autofillwidget.cpp @@ -0,0 +1,46 @@ +#include "autofillwidget.h" +#include "ui_autofillwidget.h" +#include "autofillmodel.h" +#include "mainapplication.h" + +AutoFillWidget::AutoFillWidget(QUrl url, QByteArray data, QString pass, QWidget *parent) + :QWidget(parent) + ,ui(new Ui::AutoFillWidget) + ,m_url(url) + ,m_data(data) + ,m_pass(pass) +{ + setAttribute(Qt::WA_DeleteOnClose); + ui->setupUi(this); + ui->label->setText(tr("Do you want QupZilla to remember password on %1?").arg(url.host())); + ui->closeButton->setIcon( +#ifdef Q_WS_X11 + style()->standardIcon(QStyle::SP_DialogCloseButton) +#else + QIcon(":/icons/faenza/close.png") +#endif + ); + + connect(ui->remember, SIGNAL(clicked()), this, SLOT(remember())); + connect(ui->never, SIGNAL(clicked()), this, SLOT(never())); + connect(ui->notnow, SIGNAL(clicked()), this, SLOT(close())); + connect(ui->closeButton, SIGNAL(clicked()), this, SLOT(close())); +} +// addEntry(request.url(), outgoingData, pass); + +void AutoFillWidget::never() +{ + MainApplication::getInstance()->autoFill()->blockStoringfor(m_url); + close(); +} + +void AutoFillWidget::remember() +{ + MainApplication::getInstance()->autoFill()->addEntry(m_url, m_data, m_pass); + close(); +} + +AutoFillWidget::~AutoFillWidget() +{ + delete ui; +} diff --git a/src/autofill/autofillwidget.h b/src/autofill/autofillwidget.h new file mode 100644 index 000000000..3f03adc8b --- /dev/null +++ b/src/autofill/autofillwidget.h @@ -0,0 +1,30 @@ +#ifndef AUTOFILLWIDGET_H +#define AUTOFILLWIDGET_H + +#include +#include + +namespace Ui { + class AutoFillWidget; +} + +class AutoFillWidget : public QWidget +{ + Q_OBJECT + +public: + explicit AutoFillWidget(QUrl url, QByteArray data, QString pass, QWidget *parent = 0); + ~AutoFillWidget(); + +private slots: + void remember(); + void never(); + +private: + Ui::AutoFillWidget *ui; + QUrl m_url; + QByteArray m_data; + QString m_pass; +}; + +#endif // AUTOFILLWIDGET_H diff --git a/src/autofill/autofillwidget.ui b/src/autofill/autofillwidget.ui new file mode 100644 index 000000000..656691efb --- /dev/null +++ b/src/autofill/autofillwidget.ui @@ -0,0 +1,126 @@ + + + AutoFillWidget + + + + 0 + 0 + 897 + 40 + + + + + 0 + 0 + + + + Form + + + + 3 + + + 5 + + + 4 + + + 5 + + + 4 + + + + + + 0 + 0 + + + + + + + :/icons/other/keys.png + + + + + + + + 0 + 0 + + + + + + + + + + + + + + 0 + 0 + + + + Remember + + + + + + + + 0 + 0 + + + + Never For This Site + + + + + + + + 0 + 0 + + + + Not Now + + + + + + + + + + + + true + + + + + + + + + + diff --git a/src/bookmarks/bookmarksmanager.cpp b/src/bookmarks/bookmarksmanager.cpp new file mode 100644 index 000000000..01f4a85de --- /dev/null +++ b/src/bookmarks/bookmarksmanager.cpp @@ -0,0 +1,343 @@ +#include "mainapplication.h" +#include "bookmarksmanager.h" +#include "ui_bookmarksmanager.h" +#include "qupzilla.h" +#include "locationbar.h" +#include "webview.h" +#include "bookmarkstoolbar.h" +#include "tabwidget.h" +#include "bookmarksmodel.h" + +//Won't be bad idea to rewrite bookmarks access via bookmarksmodel + +BookmarksManager::BookmarksManager(QupZilla* mainClass, QWidget *parent) : + QWidget(parent) + ,m_isRefreshing(false) + ,ui(new Ui::BookmarksManager) + ,p_QupZilla(mainClass) + ,m_bookmarksModel(MainApplication::getInstance()->bookmarks()) +{ + ui->setupUi(this); + //CENTER on scren + const QRect screen = QApplication::desktop()->screenGeometry(); + const QRect &size = QWidget::geometry(); + QWidget::move( (screen.width()-size.width())/2, (screen.height()-size.height())/2 ); + + connect(ui->deleteB, SIGNAL(clicked()), this, SLOT(deleteItem())); + connect(ui->close, SIGNAL(clicked(QAbstractButton*)), this, SLOT(hide())); + connect(ui->bookmarksTree, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(itemChanged(QTreeWidgetItem*))); + connect(ui->addFolder, SIGNAL(clicked()), this, SLOT(addFolder())); + connect(ui->bookmarksTree, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(contextMenuRequested(const QPoint &))); + connect(ui->bookmarksTree, SIGNAL(itemControlClicked(QTreeWidgetItem*)), this, SLOT(itemControlClicked(QTreeWidgetItem*))); + //QTimer::singleShot(0, this, SLOT(refreshTable())); +} + +QupZilla* BookmarksManager::getQupZilla() +{ + if (!p_QupZilla) + p_QupZilla = MainApplication::getInstance()->getWindow(); + return p_QupZilla; +} + +void BookmarksManager::setMainWindow(QupZilla *window) +{ + if (window) + p_QupZilla = window; +} + +void BookmarksManager::addFolder() +{ + QString text = QInputDialog::getText(this, tr("Add new folder"), tr("Choose name for new bookmark folder: ")); + if (text.isEmpty()) + return; + QSqlQuery query; + query.exec("INSERT INTO folders (name) VALUES ('"+text+"')"); + refreshTable(); +} + +void BookmarksManager::itemChanged(QTreeWidgetItem *item) +{ + if (!item || m_isRefreshing) + return; + + QString name = item->text(0); + QUrl url = QUrl(item->text(1)); + int id = item->whatsThis(1).toInt(); + + m_bookmarksModel->editBookmark(id, url, name); +} + +void BookmarksManager::itemControlClicked(QTreeWidgetItem *item) +{ + if (!item || item->text(1).isEmpty()) + return; + getQupZilla()->tabWidget()->addView(QUrl(item->text(1))); +} + +void BookmarksManager::loadInNewTab() +{ + if (QAction *action = qobject_cast(sender())) + getQupZilla()->tabWidget()->addView(action->data().toUrl(), tr("New Tab"), TabWidget::NewNotSelectedTab); +} + +void BookmarksManager::deleteItem() +{ + QTreeWidgetItem* item = ui->bookmarksTree->currentItem(); + if (!item) + return; + QSqlQuery query; + + if (item->text(1).isEmpty()) { // Delete folder + QString folder = item->text(0); + if (folder == tr("Bookmarks In Menu") || folder == tr("Bookmarks In ToolBar")) + return; + + query.exec("DELETE FROM folders WHERE name='"+folder+"'"); + query.exec("DELETE FROM bookmarks WHERE folder='"+folder+"'"); + delete item; + return; + } + + QString id = item->whatsThis(1); + + query.exec("DELETE FROM bookmarks WHERE id="+id); + delete item; + getQupZilla()->bookmarksToolbar()->refreshBookmarks(); +} + +void BookmarksManager::addBookmark(WebView *view) +{ + insertBookmark(view->url(), view->title()); +} + +void BookmarksManager::moveBookmark() +{ + QTreeWidgetItem* item = ui->bookmarksTree->currentItem(); + if (!item) + return; + if (QAction *action = qobject_cast(sender())) { + m_bookmarksModel->editBookmark(item->whatsThis(1).toInt(), item->text(0), action->data().toString()); + } + refreshTable(); +} + +void BookmarksManager::contextMenuRequested(const QPoint &position) +{ + if (!ui->bookmarksTree->itemAt(position)) + return; + QString link = ui->bookmarksTree->itemAt(position)->text(1); + if (link.isEmpty()) + return; + + QMenu menu; + menu.addAction(tr("Open link in actual tab"), getQupZilla(), SLOT(loadActionUrl()))->setData(link); + menu.addAction(tr("Open link in new tab"), this, SLOT(loadInNewTab()))->setData(link); + menu.addSeparator(); + + QMenu moveMenu; + moveMenu.setTitle(tr("Move bookmark to folder")); + moveMenu.addAction(QIcon(":icons/other/unsortedbookmarks.png"), tr("Unsorted Bookmarks"), this, SLOT(moveBookmark()))->setData("unsorted"); + moveMenu.addAction(style()->standardIcon(QStyle::SP_DirOpenIcon), tr("Bookmarks In Menu"), this, SLOT(moveBookmark()))->setData("bookmarksMenu"); + moveMenu.addAction(style()->standardIcon(QStyle::SP_DirOpenIcon), tr("Bookmarks In ToolBar"), this, SLOT(moveBookmark()))->setData("bookmarksToolbar"); + QSqlQuery query; + query.exec("SELECT name FROM folders"); + while(query.next()) + moveMenu.addAction(style()->standardIcon(QStyle::SP_DirIcon), query.value(0).toString(), this, SLOT(moveBookmark()))->setData(query.value(0).toString()); + menu.addMenu(&moveMenu); + + menu.addSeparator(); + menu.addAction(tr("Close"), this, SLOT(close())); + + //Prevent choosing first option with double rightclick + QPoint pos = QCursor::pos(); + QPoint p(pos.x(), pos.y()+1); + menu.exec(p); +} + +void BookmarksManager::refreshTable() +{ + m_isRefreshing = true; + ui->bookmarksTree->setUpdatesEnabled(false); + ui->bookmarksTree->clear(); + + QSqlQuery query; + QTreeWidgetItem* newItem = new QTreeWidgetItem(ui->bookmarksTree); + newItem->setText(0, tr("Bookmarks In Menu")); + newItem->setIcon(0, style()->standardIcon(QStyle::SP_DirIcon)); + ui->bookmarksTree->addTopLevelItem(newItem); + + newItem = new QTreeWidgetItem(ui->bookmarksTree); + newItem->setText(0, tr("Bookmarks In ToolBar")); + newItem->setIcon(0, style()->standardIcon(QStyle::SP_DirIcon)); + ui->bookmarksTree->addTopLevelItem(newItem); + + query.exec("SELECT name FROM folders"); + while(query.next()) { + newItem = new QTreeWidgetItem(ui->bookmarksTree); + newItem->setText(0, query.value(0).toString()); + newItem->setIcon(0, style()->standardIcon(QStyle::SP_DirIcon)); + ui->bookmarksTree->addTopLevelItem(newItem); + } + + query.exec("SELECT title, url, id, folder FROM bookmarks"); + while(query.next()) { + QString title = query.value(0).toString(); + QUrl url = query.value(1).toUrl(); + int id = query.value(2).toInt(); + QString folder = query.value(3).toString(); + QTreeWidgetItem* item; + if (folder == "bookmarksMenu") + folder = tr("Bookmarks In Menu"); + if (folder == "bookmarksToolbar") + folder = tr("Bookmarks In ToolBar"); + + if (folder != "unsorted") { + QList findParent = ui->bookmarksTree->findItems(folder, 0); + if (findParent.count() == 1) { + item = new QTreeWidgetItem(findParent.at(0)); + }else{ + QTreeWidgetItem* newParent = new QTreeWidgetItem(ui->bookmarksTree); + newParent->setText(0, folder); + newParent->setIcon(0, style()->standardIcon(QStyle::SP_DirIcon)); + ui->bookmarksTree->addTopLevelItem(newParent); + item = new QTreeWidgetItem(newParent); + } + } else + item = new QTreeWidgetItem(ui->bookmarksTree); + + item->setText(0, title); + item->setText(1, url.toEncoded()); + item->setToolTip(0, title); + item->setToolTip(1, url.toEncoded()); + + item->setWhatsThis(1, QString::number(id)); + item->setIcon(0, LocationBar::icon(url)); + item->setFlags(item->flags() | Qt::ItemIsEditable); + ui->bookmarksTree->addTopLevelItem(item); + } + ui->bookmarksTree->expandAll(); + + ui->bookmarksTree->setUpdatesEnabled(true); + m_isRefreshing = false; +} + +void BookmarksManager::insertBookmark(const QUrl &url, const QString &title) +{ + if (url.isEmpty() || title.isEmpty()) + return; + QDialog* dialog = new QDialog(getQupZilla()); + QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom, dialog); + QLabel* label = new QLabel(dialog); + QLineEdit* edit = new QLineEdit(dialog); + QComboBox* combo = new QComboBox(dialog); + QDialogButtonBox* box = new QDialogButtonBox(dialog); + box->addButton(QDialogButtonBox::Ok); + box->addButton(QDialogButtonBox::Cancel); + connect(box, SIGNAL(rejected()), dialog, SLOT(reject())); + connect(box, SIGNAL(accepted()), dialog, SLOT(accept())); + layout->addWidget(label); + layout->addWidget(edit); + layout->addWidget(combo); + if (m_bookmarksModel->isBookmarked(url)) + layout->addWidget(new QLabel(tr("Warning: You already have this page bookmarked!"))); + layout->addWidget(box); + + combo->addItem(QIcon(":icons/other/unsortedbookmarks.png"), tr("Unsorted Bookmarks")); + combo->addItem(style()->standardIcon(QStyle::SP_DirOpenIcon), tr("Bookmarks In Menu")); + combo->addItem(style()->standardIcon(QStyle::SP_DirOpenIcon), tr("Bookmarks In ToolBar")); + QSqlQuery query; + query.exec("SELECT name FROM folders"); + while(query.next()) + combo->addItem(style()->standardIcon(QStyle::SP_DirIcon), query.value(0).toString()); + + label->setText(tr("Choose name and location of bookmark.")); + edit->setText(title); + edit->setCursorPosition(0); + dialog->setWindowIcon(LocationBar::icon(url)); + dialog->setWindowTitle(tr("Add New Bookmark")); + + QSize size = dialog->size(); + size.setWidth(350); + dialog->resize(size); + dialog->exec(); + if (dialog->result() == QDialog::Rejected) + return; + if (edit->text().isEmpty()) + return; + + query.prepare("INSERT INTO bookmarks (title, url, folder) VALUES (?,?,?)"); + query.bindValue(0, edit->text()); + query.bindValue(1, url.toString()); + if (combo->currentText() == tr("Bookmarks In Menu")) + query.bindValue(2,"bookmarksMenu"); + else if (combo->currentText() == tr("Bookmarks In ToolBar")) + query.bindValue(2,"bookmarksToolbar"); + else if (combo->currentText() == tr("Unsorted Bookmarks")) + query.bindValue(2, "unsorted"); + else query.bindValue(2, combo->currentText()); + query.exec(); + + getQupZilla()->bookmarksToolbar()->refreshBookmarks(); + getQupZilla()->locationBar()->checkBookmark(); + delete dialog; +} + +void BookmarksManager::insertAllTabs() +{ + QDialog* dialog = new QDialog(getQupZilla()); + QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom, dialog); + QLabel* label = new QLabel(dialog); + QComboBox* combo = new QComboBox(dialog); + QDialogButtonBox* box = new QDialogButtonBox(dialog); + box->addButton(QDialogButtonBox::Ok); + box->addButton(QDialogButtonBox::Cancel); + connect(box, SIGNAL(rejected()), dialog, SLOT(reject())); + connect(box, SIGNAL(accepted()), dialog, SLOT(accept())); + layout->addWidget(label); + layout->addWidget(combo); + layout->addWidget(box); + + combo->addItem(QIcon(":icons/other/unsortedbookmarks.png"), tr("Unsorted Bookmarks")); + combo->addItem(style()->standardIcon(QStyle::SP_DirOpenIcon), tr("Bookmarks In Menu")); + combo->addItem(style()->standardIcon(QStyle::SP_DirOpenIcon), tr("Bookmarks In ToolBar")); + QSqlQuery query; + query.exec("SELECT name FROM folders"); + while(query.next()) + combo->addItem(style()->standardIcon(QStyle::SP_DirIcon), query.value(0).toString()); + + label->setText(tr("Choose folder for bookmarks:")); + dialog->setWindowIcon(QIcon(":/icons/qupzilla.png")); + dialog->setWindowTitle(tr("Bookmark All Tabs")); + + QSize size = dialog->size(); + size.setWidth(350); + dialog->resize(size); + dialog->exec(); + if (dialog->result() == QDialog::Rejected) + return; + + for (int i = 0; itabWidget()->count(); i++) { + WebView* view = getQupZilla()->weView(i); + if (!view || view->url().isEmpty()) + continue; + query.prepare("INSERT INTO bookmarks (title, url, folder) VALUES (?,?,?)"); + query.bindValue(0, view->title()); + query.bindValue(1, view->url().toString()); + if (combo->currentText() == tr("Bookmarks In Menu")) + query.bindValue(2,"bookmarksMenu"); + else if (combo->currentText() == tr("Bookmarks In ToolBar")) + query.bindValue(2,"bookmarksToolbar"); + else if (combo->currentText() == tr("Unsorted Bookmarks")) + query.bindValue(2, "unsorted"); + else query.bindValue(2, combo->currentText()); + query.exec(); + } + getQupZilla()->bookmarksToolbar()->refreshBookmarks(); + getQupZilla()->locationBar()->checkBookmark(); + delete dialog; +} + +BookmarksManager::~BookmarksManager() +{ + delete ui; +} diff --git a/src/bookmarks/bookmarksmanager.h b/src/bookmarks/bookmarksmanager.h new file mode 100644 index 000000000..75903addf --- /dev/null +++ b/src/bookmarks/bookmarksmanager.h @@ -0,0 +1,53 @@ +#ifndef BOOKMARKSMANAGER_H +#define BOOKMARKSMANAGER_H + +#if defined(QT_NO_DEBUG) & !defined(QT_NO_DEBUG_OUTPUT) +#define QT_NO_DEBUG_OUTPUT +#endif + +#include +#include +#include +#include + +namespace Ui { + class BookmarksManager; +} + +class WebView; +class QupZilla; +class BookmarksModel; +class BookmarksManager : public QWidget +{ + Q_OBJECT + +public: + explicit BookmarksManager(QupZilla* mainClass, QWidget *parent = 0); + ~BookmarksManager(); + void addBookmark(WebView* view); + void insertBookmark(const QUrl &url, const QString &title); + void setMainWindow(QupZilla* window); + +public slots: + void refreshTable(); + void insertAllTabs(); + +private slots: + void deleteItem(); + void itemChanged(QTreeWidgetItem* item); + void addFolder(); + void contextMenuRequested(const QPoint &position); + void loadInNewTab(); + void itemControlClicked(QTreeWidgetItem* item); + void moveBookmark(); + +private: + QupZilla* getQupZilla(); + + bool m_isRefreshing; + Ui::BookmarksManager *ui; + QPointer p_QupZilla; + BookmarksModel* m_bookmarksModel; +}; + +#endif // BOOKMARKSMANAGER_H diff --git a/src/bookmarks/bookmarksmanager.ui b/src/bookmarks/bookmarksmanager.ui new file mode 100644 index 000000000..49e60bb19 --- /dev/null +++ b/src/bookmarks/bookmarksmanager.ui @@ -0,0 +1,81 @@ + + + BookmarksManager + + + + 0 + 0 + 735 + 547 + + + + Bookmarks + + + + :/icons/qupzilla.png:/icons/qupzilla.png + + + + + + Qt::CustomContextMenu + + + true + + + 330 + + + + Title + + + + + Url + + + + + + + + QDialogButtonBox::Close + + + + + + + Delete + + + Del + + + + + + + Add Folder + + + + + + + + TreeWidget + QTreeWidget +
    treewidget.h
    +
    +
    + + + + +
    diff --git a/src/bookmarks/bookmarksmodel.cpp b/src/bookmarks/bookmarksmodel.cpp new file mode 100644 index 000000000..1df0c3ce1 --- /dev/null +++ b/src/bookmarks/bookmarksmodel.cpp @@ -0,0 +1,150 @@ +#include "mainapplication.h" +#include "bookmarksmodel.h" +#include "webview.h" + +// SQLite DB -> table bookmarks + folders +// Unique in bookmarks table is id +// However from bookmark icon, it is not possible to add more than one bookmark +// Only from Ctrl+D dialog it is possible + +BookmarksModel::BookmarksModel() +{ + loadSettings(); +} + +void BookmarksModel::loadSettings() +{ + QSettings settings(MainApplication::getInstance()->getActiveProfil()+"settings.ini", QSettings::IniFormat); + settings.beginGroup("Web-Browser-Settings"); + m_showMostVisited = settings.value("showMostVisited",true).toBool(); + settings.endGroup(); +} + +void BookmarksModel::setShowingMostVisited(bool state) +{ + QSettings settings(MainApplication::getInstance()->getActiveProfil()+"settings.ini", QSettings::IniFormat); + settings.beginGroup("Web-Browser-Settings"); + settings.setValue("showMostVisited",state); + settings.endGroup(); + m_showMostVisited = state; +} + +bool BookmarksModel::isBookmarked(QUrl url) +{ + QSqlQuery query; + query.prepare("SELECT count(id) FROM bookmarks WHERE url=?"); + query.bindValue(0, url.toString()); + query.exec(); + query.next(); + return query.value(0).toInt()>0; +} + +// Bookmark search priority: +// Bookmarks in menu > bookmarks in toolbar -> user folders and unsorted +int BookmarksModel::bookmarkId(QUrl url) +{ + QSqlQuery query; + query.prepare("SELECT id FROM bookmarks WHERE url=? AND folder='bookmarksMenu' "); + query.bindValue(0, url.toString()); + query.exec(); + if (query.next()) + return query.value(0).toInt(); + + query.prepare("SELECT id FROM bookmarks WHERE url=? AND folder='bookmarksToolbar' "); + query.bindValue(0, url.toString()); + query.exec(); + if (query.next()) + return query.value(0).toInt(); + + query.prepare("SELECT id FROM bookmarks WHERE url=? "); + query.bindValue(0, url.toString()); + query.exec(); + if (query.next()) + return query.value(0).toInt(); + + return -1; +} + +int BookmarksModel::bookmarkId(QUrl url, QString title, QString folder) +{ + QSqlQuery query; + query.prepare("SELECT id FROM bookmarks WHERE url=? AND title=? AND folder=? "); + query.bindValue(0, url.toString()); + query.bindValue(1, title); + query.bindValue(2, folder); + query.exec(); + if (query.next()) + return query.value(0).toInt(); + return -1; +} + +QStringList BookmarksModel::getBookmark(int id) +{ + QSqlQuery query; + query.prepare("SELECT url, title, folder FROM bookmarks WHERE id=?"); + query.bindValue(0, id); + query.exec(); + if (!query.next()) + return QStringList(); + QStringList list; + list.append(query.value(0).toString()); + list.append(query.value(1).toString()); + list.append(query.value(2).toString()); + return list; +} + +bool BookmarksModel::saveBookmark(QUrl url, QString title, QString folder) +{ + if (url.isEmpty() || title.isEmpty() || folder.isEmpty()) + return false; + + QSqlQuery query; + query.prepare("INSERT INTO bookmarks (url, title, folder) VALUES (?,?,?)"); + query.bindValue(0, url.toString()); + query.bindValue(1, title); + query.bindValue(2, folder); + return query.exec(); +} + +bool BookmarksModel::saveBookmark(WebView *view, QString folder) +{ + return saveBookmark(view->url(), view->title(), folder); +} + +bool BookmarksModel::removeBookmark(int id) +{ + QSqlQuery query; + query.prepare("DELETE FROM bookmarks WHERE id = ?"); + query.bindValue(0, id); + return query.exec(); +} + +bool BookmarksModel::removeBookmark(QUrl url) +{ + return removeBookmark(bookmarkId(url)); +} + +bool BookmarksModel::removeBookmark(WebView *view) +{ + return removeBookmark(bookmarkId(view->url())); +} + +bool BookmarksModel::editBookmark(int id, QString title, QString folder) +{ + QSqlQuery query; + query.prepare("UPDATE bookmarks SET title=?, folder=? WHERE id=?"); + query.bindValue(0, title); + query.bindValue(1, folder); + query.bindValue(2, id); + return query.exec(); +} + +bool BookmarksModel::editBookmark(int id, QUrl url, QString title) +{ + QSqlQuery query; + query.prepare("UPDATE bookmarks SET title=?, url=? WHERE id=?"); + query.bindValue(0, title); + query.bindValue(1, url.toString()); + query.bindValue(2, id); + return query.exec(); +} diff --git a/src/bookmarks/bookmarksmodel.h b/src/bookmarks/bookmarksmodel.h new file mode 100644 index 000000000..1dc93f6c8 --- /dev/null +++ b/src/bookmarks/bookmarksmodel.h @@ -0,0 +1,44 @@ +#ifndef BOOKMARKSMODEL_H +#define BOOKMARKSMODEL_H +#if defined(QT_NO_DEBUG) & !defined(QT_NO_DEBUG_OUTPUT) +#define QT_NO_DEBUG_OUTPUT +#endif + +#include +#include +#include + +class WebView; +class BookmarksModel +{ +public: + explicit BookmarksModel(); + void loadSettings(); + inline bool isShowingMostVisited() { return m_showMostVisited; } + void setShowingMostVisited(bool state); + + bool isBookmarked(QUrl url); + int bookmarkId(QUrl url); + int bookmarkId(QUrl url, QString title, QString folder); + QStringList getBookmark(int id); + + bool saveBookmark(QUrl url, QString title, QString folder = "unsorted"); + bool saveBookmark(WebView* view, QString folder = "unsorted"); + + bool removeBookmark(int id); + bool removeBookmark(QUrl url); + bool removeBookmark(WebView* view); + + bool editBookmark(int id, QString title, QString folder); + bool editBookmark(int id, QUrl url, QString title); + +signals: + +public slots: + +private: + bool m_showMostVisited; + +}; + +#endif // BOOKMARKSMODEL_H diff --git a/src/bookmarks/bookmarkstoolbar.cpp b/src/bookmarks/bookmarkstoolbar.cpp new file mode 100644 index 000000000..8ddd5f34f --- /dev/null +++ b/src/bookmarks/bookmarkstoolbar.cpp @@ -0,0 +1,125 @@ +#include "bookmarkstoolbar.h" +#include "qupzilla.h" +#include "locationbar.h" +#include "bookmarksmodel.h" + +BookmarksToolbar::BookmarksToolbar(QupZilla* mainClass, QWidget *parent) : + QToolBar(parent) + ,p_QupZilla(mainClass) + ,m_bookmarksModel(0) +{ + setObjectName("bookmarksToolbar"); + setWindowTitle(tr("Bookmarks")); + setStyleSheet("QToolBar{background-image:url(:icons/transp.png); border:none;}"); + setMovable(false); + setContextMenuPolicy(Qt::CustomContextMenu); + + connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(customContextMenuRequested(QPoint))); + QTimer::singleShot(0, this, SLOT(refreshBookmarks())); +} + +void BookmarksToolbar::setColor(QColor color) +{ + setStyleSheet("QToolButton {color: "+color.name()+";}"); +} + +void BookmarksToolbar::customContextMenuRequested(const QPoint &pos) +{ + if (actionAt(pos)) + return; + + QMenu menu; + menu.addAction(tr("Bookmark Current Page"), p_QupZilla, SLOT(bookmarkPage())); + menu.addAction(tr("Bookmark All Tabs"), p_QupZilla, SLOT(bookmarkAllTabs())); + menu.addAction(QIcon::fromTheme("user-bookmarks"), tr("Organize Bookmarks"), p_QupZilla, SLOT(showBookmarksManager())); + menu.addSeparator(); + menu.addAction( +#ifdef Q_WS_X11 + style()->standardIcon(QStyle::SP_BrowserReload) +#else + QIcon(":/icons/faenza/reload.png") +#endif + ,tr("Reload Toolbar"), this, SLOT(refreshBookmarks())); + menu.addSeparator(); + menu.addAction(m_bookmarksModel->isShowingMostVisited() ? tr("Hide Most Visited") : tr("Show Most Visited"), this, SLOT(showMostVisited())); + menu.addAction(tr("Hide Toolbar"), this, SLOT(hidePanel())); + + //Prevent choosing first option with double rightclick + QPoint position = QCursor::pos(); + QPoint p(position.x(), position.y()+1); + menu.exec(p); +} + +void BookmarksToolbar::showMostVisited() +{ + m_bookmarksModel->setShowingMostVisited(!m_bookmarksModel->isShowingMostVisited()); + refreshBookmarks(); +} + +void BookmarksToolbar::hidePanel() +{ + this->hide(); + p_QupZilla->acShowBookmarksToolbar()->setChecked(false); +} + +void BookmarksToolbar::refreshBookmarks() +{ + if (!m_bookmarksModel) + m_bookmarksModel =MainApplication::getInstance()->bookmarks(); + + clear(); + QSqlQuery query; + query.exec("SELECT title, url FROM bookmarks WHERE folder='bookmarksToolbar'"); + while(query.next()) { + QUrl url = query.value(1).toUrl(); + QString title = query.value(0).toString(); + QAction* action = new QAction(this); + if (title.length()>15) { + title.truncate(13); + title+=".."; + } + + action->setText(title); + action->setData(url); + action->setIcon(LocationBar::icon(url)); + QToolButton* button = new QToolButton(this); + button->setDefaultAction(action); + button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + button->setMaximumHeight(25); + connect(action, SIGNAL(triggered()), p_QupZilla, SLOT(loadActionUrl())); + addWidget(button); + } + + if (!m_bookmarksModel->isShowingMostVisited()) + return; + + QToolButton* mostVis = new QToolButton(this); + mostVis->setPopupMode(QToolButton::InstantPopup); + mostVis->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + mostVis->setMaximumHeight(25); + mostVis->setIcon(style()->standardIcon(QStyle::SP_DirIcon)); + mostVis->setText(tr("Most visited")); + mostVis->setToolTip(tr("Sites You visited the most")); + m_menuMostVisited = new QMenu(); + mostVis->setMenu(m_menuMostVisited); + connect(m_menuMostVisited, SIGNAL(aboutToShow()), this, SLOT(refreshMostVisited())); + + addWidget(mostVis); +} + +void BookmarksToolbar::refreshMostVisited() +{ + m_menuMostVisited->clear(); + + QSqlQuery query; + query.exec("SELECT title, url FROM history ORDER BY count DESC LIMIT 10"); + while(query.next()) { + QUrl url = query.value(1).toUrl(); + QString title = query.value(0).toString(); + if (title.length()>40) { + title.truncate(40); + title+=".."; + } + m_menuMostVisited->addAction(LocationBar::icon(url), title, p_QupZilla, SLOT(loadActionUrl()))->setData(url); + } +} diff --git a/src/bookmarks/bookmarkstoolbar.h b/src/bookmarks/bookmarkstoolbar.h new file mode 100644 index 000000000..01fd1bef5 --- /dev/null +++ b/src/bookmarks/bookmarkstoolbar.h @@ -0,0 +1,38 @@ +#ifndef BOOKMARKSTOOLBAR_H +#define BOOKMARKSTOOLBAR_H + +#if defined(QT_NO_DEBUG) & !defined(QT_NO_DEBUG_OUTPUT) +#define QT_NO_DEBUG_OUTPUT +#endif + +#include +#include +#include + +class QupZilla; +class BookmarksModel; + +class BookmarksToolbar : public QToolBar +{ + Q_OBJECT +public: + explicit BookmarksToolbar(QupZilla* mainClass, QWidget *parent = 0); + void setColor(QColor color); + +signals: + +public slots: + void refreshBookmarks(); + void refreshMostVisited(); + void customContextMenuRequested(const QPoint &pos); + void hidePanel(); + void showMostVisited(); + +private: + QupZilla* p_QupZilla; + BookmarksModel* m_bookmarksModel; + QMenu* m_menuMostVisited; + +}; + +#endif // BOOKMARKSTOOLBAR_H diff --git a/src/bookmarks/bookmarkswidget.cpp b/src/bookmarks/bookmarkswidget.cpp new file mode 100644 index 000000000..2122665c5 --- /dev/null +++ b/src/bookmarks/bookmarkswidget.cpp @@ -0,0 +1,63 @@ +#include "bookmarkswidget.h" +#include "ui_bookmarkswidget.h" +#include "bookmarksmodel.h" +#include "mainapplication.h" + +BookmarksWidget::BookmarksWidget(int bookmarkId, QWidget *parent) : + QMenu(parent) + ,ui(new Ui::BookmarksWidget) + ,m_bookmarkId(bookmarkId) + ,m_bookmarksModel(0) +{ + ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose); + connect(ui->close, SIGNAL(clicked()), this, SLOT(close())); + connect(ui->removeBookmark, SIGNAL(clicked()), this, SLOT(removeBookmark())); + connect(ui->save, SIGNAL(clicked()), this, SLOT(saveBookmark())); + + m_bookmarksModel = MainApplication::getInstance()->bookmarks(); + loadBookmark(); +} + +void BookmarksWidget::loadBookmark() +{ + QStringList bookmark = m_bookmarksModel->getBookmark(m_bookmarkId); + ui->name->setText( bookmark.at(1) ); + + // Bookmark folders + ui->folder->addItem(QIcon(":icons/other/unsortedbookmarks.png"), tr("Unsorted Bookmarks"), "unsorted"); + ui->folder->addItem(style()->standardIcon(QStyle::SP_DirOpenIcon), tr("Bookmarks In Menu"), "bookmarksMenu"); + ui->folder->addItem(style()->standardIcon(QStyle::SP_DirOpenIcon), tr("Bookmarks In ToolBar"), "bookmarksToolbar"); + QSqlQuery query; + query.exec("SELECT name FROM folders"); + while(query.next()) + ui->folder->addItem(style()->standardIcon(QStyle::SP_DirIcon), query.value(0).toString(), query.value(0).toString()); + + ui->folder->setCurrentIndex( ui->folder->findData(bookmark.at(2)) ); + ui->name->setCursorPosition(0); +} + +void BookmarksWidget::removeBookmark() +{ + m_bookmarksModel->removeBookmark(m_bookmarkId); + emit bookmarkDeleted(); + close(); +} + +void BookmarksWidget::saveBookmark() +{ + m_bookmarksModel->editBookmark(m_bookmarkId, ui->name->text(), ui->folder->itemData(ui->folder->currentIndex()).toString() ); + close(); +} + +void BookmarksWidget::showAt(QWidget* _parent) +{ + QPoint p = _parent->mapToGlobal(QPoint(0, 0)); + move( (p.x()+_parent->width() ) - width(), p.y() + _parent->height()); + show(); +} + +BookmarksWidget::~BookmarksWidget() +{ + delete ui; +} diff --git a/src/bookmarks/bookmarkswidget.h b/src/bookmarks/bookmarkswidget.h new file mode 100644 index 000000000..b344c7d23 --- /dev/null +++ b/src/bookmarks/bookmarkswidget.h @@ -0,0 +1,48 @@ +#ifndef BOOKMARKSWIDGET_H +#define BOOKMARKSWIDGET_H +#if defined(QT_NO_DEBUG) & !defined(QT_NO_DEBUG_OUTPUT) +#define QT_NO_DEBUG_OUTPUT +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Ui { + class BookmarksWidget; +} + +class BookmarksModel; +class BookmarksWidget : public QMenu +{ + Q_OBJECT +public: + explicit BookmarksWidget(int bookmarkId, QWidget *parent = 0); + ~BookmarksWidget(); + void showAt(QWidget* _parent); + +signals: + void bookmarkDeleted(); + +public slots: + +private slots: + void removeBookmark(); + void saveBookmark(); + +private: + void loadBookmark(); + + Ui::BookmarksWidget* ui; + int m_bookmarkId; + BookmarksModel* m_bookmarksModel; +}; + +#endif // BOOKMARKSWIDGET_H diff --git a/src/bookmarks/bookmarkswidget.ui b/src/bookmarks/bookmarkswidget.ui new file mode 100644 index 000000000..04f5c5523 --- /dev/null +++ b/src/bookmarks/bookmarkswidget.ui @@ -0,0 +1,142 @@ + + + BookmarksWidget + + + + 0 + 0 + 282 + 166 + + + + + + + + + + QFrame::NoFrame + + + + 0 + + + 0 + + + + + + + + :/icons/other/bigstar.png + + + + + + + Edit This Bookmark + + + + + + + + 0 + 0 + + + + Remove Bookmark + + + + + + + Name: + + + + + + + + + + Folder: + + + + + + + + + + 5 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + Save + + + + + + + + 0 + 0 + + + + Close + + + + + + + + + + + + + Frame + QFrame +
    frame.h
    + 1 +
    +
    + + + + +
    diff --git a/src/cookies/cookiejar.cpp b/src/cookies/cookiejar.cpp new file mode 100644 index 000000000..935fb8750 --- /dev/null +++ b/src/cookies/cookiejar.cpp @@ -0,0 +1,112 @@ +#include "cookiejar.h" +#include "qupzilla.h" +#define COOKIE_DEBUG + +//TODO: black/white listing +CookieJar::CookieJar(QupZilla* mainClass, QObject *parent) : + QNetworkCookieJar(parent) + ,p_QupZilla(mainClass) +{ + loadSettings(); +// activeProfil = MainApplication::getInstance()->getActiveProfil(); + m_activeProfil = MainApplication::getInstance()->getActiveProfil(); +} + +void CookieJar::loadSettings() +{ + QSettings settings(m_activeProfil+"settings.ini", QSettings::IniFormat); + settings.beginGroup("Web-Browser-Settings"); + m_allowCookies = settings.value("allowCookies",true).toBool(); + m_allowCookiesFromDomain = settings.value("allowCookiesFromVisitedDomainOnly",false).toBool(); + m_filterTrackingCookie = settings.value("filterTrackingCookie",false).toBool(); + m_deleteOnClose = settings.value("deleteCookiesOnClose", false).toBool(); +} + +void CookieJar::setAllowCookies(bool allow) +{ + m_allowCookies = allow; +} + +bool CookieJar::setCookiesFromUrl(const QList &cookieList, const QUrl &url) +{ + if (!m_allowCookies) + return QNetworkCookieJar::setCookiesFromUrl(QList(), url); + + QList newList = cookieList; + QDateTime now = QDateTime::currentDateTime(); + + foreach (QNetworkCookie cok, newList) { + if (m_allowCookiesFromDomain && !url.toString().contains(cok.domain())) { +#ifdef COOKIE_DEBUG + qDebug() << "purged for domain mismatch" << cok; +#endif + newList.removeOne(cok); + continue; + } + if (m_filterTrackingCookie && cok.expirationDate() > now.addYears(2)) { +#ifdef COOKIE_DEBUG + qDebug() << "purged as tracking " << cok; +#endif + newList.removeOne(cok); + continue; + } + } + return QNetworkCookieJar::setCookiesFromUrl(newList, url); +} + +void CookieJar::saveCookies() +{ + if (m_deleteOnClose) + return; + + QList allCookies = getAllCookies(); + QFile file(m_activeProfil+"cookies.dat"); + file.open(QIODevice::WriteOnly); + QDataStream stream(&file); + int count = allCookies.count(); + + stream << count; + for (int i = 0; i restoredCookies; + QFile file(m_activeProfil+"cookies.dat"); + file.open(QIODevice::ReadOnly); + QDataStream stream(&file); + int count; + + stream >> count; + for (int i = 0; i> rawForm; + QNetworkCookie cok = QNetworkCookie::parseCookies(rawForm).at(0); + if (cok.expirationDate() < now) + continue; + if (cok.isSessionCookie()) + continue; + restoredCookies.append(cok); + } + + file.close(); + setAllCookies(restoredCookies); +} + +QList CookieJar::getAllCookies() +{ + return QNetworkCookieJar::allCookies(); +} + +void CookieJar::setAllCookies(const QList &cookieList) +{ + QNetworkCookieJar::setAllCookies(cookieList); +} diff --git a/src/cookies/cookiejar.h b/src/cookies/cookiejar.h new file mode 100644 index 000000000..c5d1a5589 --- /dev/null +++ b/src/cookies/cookiejar.h @@ -0,0 +1,43 @@ +#ifndef COOKIEJAR_H +#define COOKIEJAR_H + +#if defined(QT_NO_DEBUG) & !defined(QT_NO_DEBUG_OUTPUT) +#define QT_NO_DEBUG_OUTPUT +#endif + +#include +#include +#include +#include +#include + +class QupZilla; +class CookieJar : public QNetworkCookieJar +{ + Q_OBJECT +public: + explicit CookieJar(QupZilla* mainClass, QObject *parent = 0); + + void loadSettings(); + bool setCookiesFromUrl(const QList &cookieList, const QUrl &url); + QList getAllCookies(); + void setAllCookies(const QList &cookieList); + void saveCookies(); + void restoreCookies(); + void setAllowCookies(bool allow); + +signals: + +public slots: + +private: + QupZilla* p_QupZilla; + bool m_allowCookies; + bool m_filterTrackingCookie; + bool m_allowCookiesFromDomain; + bool m_deleteOnClose; + + QString m_activeProfil; +}; + +#endif // COOKIEJAR_H diff --git a/src/cookies/cookiemanager.cpp b/src/cookies/cookiemanager.cpp new file mode 100644 index 000000000..54e873fd7 --- /dev/null +++ b/src/cookies/cookiemanager.cpp @@ -0,0 +1,171 @@ +#include "cookiemanager.h" +#include "ui_cookiemanager.h" +#include "qupzilla.h" +#include "cookiejar.h" +#include "mainapplication.h" + +CookieManager::CookieManager(QWidget *parent) : + QWidget(parent) + ,ui(new Ui::CookieManager) +{ + ui->setupUi(this); + //CENTER on scren + const QRect screen = QApplication::desktop()->screenGeometry(); + const QRect &size = QWidget::geometry(); + QWidget::move( (screen.width()-size.width())/2, (screen.height()-size.height())/2 ); + + //QTimer::singleShot(0, this, SLOT(refreshTable())); + + connect(ui->cookieTree, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),this, SLOT(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*))); + connect(ui->removeAll, SIGNAL(clicked()), this, SLOT(removeAll())); + connect(ui->removeCookie, SIGNAL(clicked()), this, SLOT(removeCookie())); + connect(ui->close, SIGNAL(clicked(QAbstractButton*)), this, SLOT(hide())); + connect(ui->search, SIGNAL(returnPressed()), this, SLOT(search())); + connect(ui->search, SIGNAL(cursorPositionChanged(int, int)), this, SLOT(search())); + + ui->search->setInactiveText(tr("Search")); +} + +void CookieManager::removeAll() +{ + QMessageBox::StandardButton button = QMessageBox::warning(this, tr("Confirmation"), + tr("Are you sure to delete all cookies on your computer?"), QMessageBox::Yes | QMessageBox::No); + if (button != QMessageBox::Yes) + return; + + m_cookies.clear(); + MainApplication::getInstance()->cookieJar()->setAllCookies(m_cookies); + ui->cookieTree->clear(); +} + +void CookieManager::removeCookie() +{ + QTreeWidgetItem* current = ui->cookieTree->currentItem(); + if (!current) + return; + + if (current->text(1).isEmpty()) { //Remove whole cookie group + QString domain = current->whatsThis(0); + foreach(QNetworkCookie cok, m_cookies) { + if (cok.domain() == domain || cok.domain() == domain.mid(1)) + m_cookies.removeOne(cok); + } + + delete current; + MainApplication::getInstance()->cookieJar()->setAllCookies(m_cookies); + refreshTable(false); + return; + } + + int index = current->whatsThis(1).toInt(); + m_cookies.removeAt(index); + MainApplication::getInstance()->cookieJar()->setAllCookies(m_cookies); + refreshTable(false); + + if (!ui->search->text().isEmpty()) + search(); +} + +void CookieManager::currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *parent) +{ + Q_UNUSED(parent); + if (!current) + return; + + ui->removeCookie->setEnabled(true); + + if (current->text(1).isEmpty()) { + ui->name->setText(tr("")); + ui->value->setText(tr("")); + ui->server->setText(tr("")); + ui->path->setText(tr("")); + ui->secure->setText(tr("")); + ui->expiration->setText(tr("")); + ui->removeCookie->setText(tr("Remove cookies")); + return; + } + ui->removeCookie->setText(tr("Remove cookie")); + + int index = current->whatsThis(1).toInt(); + QNetworkCookie cok = m_cookies.at(index); + + ui->name->setText(cok.name()); + ui->value->setText(cok.value()); + ui->server->setText(cok.domain()); + ui->path->setText(cok.path()); + cok.isSecure() ? ui->secure->setText(tr("Secure only")) : ui->secure->setText(tr("All connections")); + cok.isSessionCookie() ? ui->expiration->setText(tr("Session cookie")) : ui->expiration->setText(QDateTime(cok.expirationDate()).toString("hh:mm:ss dddd d. MMMM yyyy")); + +} + +void CookieManager::refreshTable(bool refreshCookieJar) +{ + if (refreshCookieJar) + m_cookies = MainApplication::getInstance()->cookieJar()->getAllCookies(); + + ui->cookieTree->setUpdatesEnabled(false); + ui->cookieTree->clear(); + + QString cookServer; + for (int i = 0; i findParent = ui->cookieTree->findItems(cookServer, 0); + if (findParent.count() == 1) { + item = new QTreeWidgetItem(findParent.at(0)); + }else{ + QTreeWidgetItem* newParent = new QTreeWidgetItem(ui->cookieTree); + newParent->setText(0, cookServer); + newParent->setIcon(0, style()->standardIcon(QStyle::SP_DirIcon)); + newParent->setWhatsThis(0, cok.domain()); + ui->cookieTree->addTopLevelItem(newParent); + item = new QTreeWidgetItem(newParent); + } + + item->setText(0,"."+cookServer); + item->setText(1, cok.name()); + item->setWhatsThis(1, QString::number(i)); + ui->cookieTree->addTopLevelItem(item); + } + + ui->cookieTree->setUpdatesEnabled(true); +} + +void CookieManager::search() +{ + QString searchText = ui->search->text(); + if (searchText.isEmpty()) { + refreshTable(false); + return; + } + + refreshTable(false); + ui->cookieTree->setUpdatesEnabled(false); + + QList items = ui->cookieTree->findItems(".*"+searchText+"*", Qt::MatchRecursive | Qt::MatchWildcard); + + QList foundItems; + foreach(QTreeWidgetItem* fitem, items) { + if (!fitem->text(0).startsWith(".")) + continue; + QTreeWidgetItem* item = new QTreeWidgetItem(); + item->setText(0, fitem->text(0)); + item->setText(1, fitem->text(1)); + item->setWhatsThis(1, fitem->whatsThis(1)); + foundItems.append(item); + } + ui->cookieTree->clear(); + ui->cookieTree->addTopLevelItems(foundItems); + ui->cookieTree->setUpdatesEnabled(true); +} + +CookieManager::~CookieManager() +{ + qDebug() << __FUNCTION__ << "called"; + delete ui; +} diff --git a/src/cookies/cookiemanager.h b/src/cookies/cookiemanager.h new file mode 100644 index 000000000..fbf9e5c20 --- /dev/null +++ b/src/cookies/cookiemanager.h @@ -0,0 +1,41 @@ +#ifndef COOKIEMANAGER_H +#define COOKIEMANAGER_H + +#if defined(QT_NO_DEBUG) & !defined(QT_NO_DEBUG_OUTPUT) +#define QT_NO_DEBUG_OUTPUT +#endif + +#include +#include +#include +#include + +namespace Ui { + class CookieManager; +} + +class QupZilla; +class CookieManager : public QWidget +{ + Q_OBJECT + +public: + explicit CookieManager(QWidget *parent = 0); + ~CookieManager(); + +public slots: + void refreshTable(bool refreshCookieJar = true); + +private slots: + void currentItemChanged(QTreeWidgetItem* current, QTreeWidgetItem* parent); + void removeCookie(); + void removeAll(); + void search(); + +private: + Ui::CookieManager *ui; + + QList m_cookies; +}; + +#endif // COOKIEMANAGER_H diff --git a/src/cookies/cookiemanager.ui b/src/cookies/cookiemanager.ui new file mode 100644 index 000000000..7a94782b1 --- /dev/null +++ b/src/cookies/cookiemanager.ui @@ -0,0 +1,250 @@ + + + CookieManager + + + + 0 + 0 + 617 + 415 + + + + Cookies + + + + :/icons/qupzilla.png:/icons/qupzilla.png + + + + + + Find: + + + + + + + + + + These cookies are stored on your computer: + + + + + + + true + + + true + + + 200 + + + 22 + + + + Server + + + + + Cookie name + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 6 + + + 3 + + + + + + 0 + 0 + + + + Name: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + Value: + + + + + + + Server: + + + + + + + Path: + + + + + + + Secure: + + + + + + + Expiration: + + + + + + + <cookie not selected> + + + + + + + <cookie not selected> + + + + + + + <cookie not selected> + + + + + + + <cookie not selected> + + + + + + + <cookie not selected> + + + + + + + <cookie not selected> + + + + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + + + Remove all cookies + + + + + + + false + + + Remove cookie + + + Del + + + + + + + + 0 + 0 + + + + QDialogButtonBox::Close + + + + + + + + + + + LineEdit + QLineEdit +
    lineedit.h
    +
    + + SqueezeLabel + QLabel +
    squeezelabel.h
    +
    +
    + + + + +
    diff --git a/src/data/html.qrc b/src/data/html.qrc new file mode 100644 index 000000000..6d80a8347 --- /dev/null +++ b/src/data/html.qrc @@ -0,0 +1,6 @@ + + + html/errorPage.html + html/info.png + + diff --git a/src/data/html/errorPage.html b/src/data/html/errorPage.html new file mode 100644 index 000000000..7aff883d4 --- /dev/null +++ b/src/data/html/errorPage.html @@ -0,0 +1,73 @@ + +%TITLE% + + + + +
    + +

    %HEADING%

    +

    %HEADING2%

    +
      +
    • %LI-1%
    • +
    • %LI-2%
    • +
    • %LI-3%
    • + +
    +
    + diff --git a/src/data/html/info.png b/src/data/html/info.png new file mode 100644 index 0000000000000000000000000000000000000000..fb47ae6967d56e58aaa03fbf783f5d1ce770d0b9 GIT binary patch literal 2536 zcmVP)00001b5ch_0Itp) z=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipP} z5DoxgS%b#_010SGL_t(o!@XErY+Th9UHhE-n3re##7-P1Ca6wAo}vtECm`B+^go8fU-qQVAT(5Vi=en~~OQ3HJ4GlTE_0S zK@b>Y9I*;5{!SbsAVqBi1RydJ84e>h5eH2ZHra?6BLK7}P2{=CceQqu=O{-jM-yVD z1c-njoS=~yjo34Z6C-CM%rO&$9#qTa*;C8YZ_vVto{UPRoRq6whr0bFF&G3Q!p35705CJy01P2O#kQfCTf0E z#t^B|t@}4@-9K~sUo%G^9n70-#_=7pd{S>X^EV$@t_D#Q?+9Zo24y2iB(hEvgw4gJaNXAZ z$@{AaAbdTU-i*u$cr(WFsgEW;PIJK^XEP_YI70S#u4=aV`f-as!L3 zc6AS|-*SxmhQp$!0uoMEtrS|SC^dL}y>=w) zYf_gbe&I-j4O3_$?d8|3)c)cG)mn>D?H9tO$S`RV;ixO|!x#vI5j72z{~0P=Nem>s z_OFzijhY6mpa7GwVzVuCB+SAP=1OrYamlqI5l$ZDY81v&DNYX>n@+(TS$FPqTOlG> zQ+0lFrSn~`clf%SgiqRswX@PF&p2*UMt$0CjS*%Naxmsh`2w(Yy$ zCfsfN?r+^PSCnQ@f9>ht4!EVkyiR+7-A;^f)XuintwC{ptrYd7Fh1l4GiU$#^n|)rzjwJ>jf=#~Wd7XAmmdF-F26sq-s?@n zbEKnwWl3t=R!i2|)(Rk=ropUo$Ncm2^M{}O>G(}Q*mLJEubi(MjTN0EsD5zxZ)c7? zwQ)d?4ST(5N_*69T$6^V+ilra`L^c}Yb-}mAq{6pXMJ3__~EDgarK*_R-QZk# z5DQ2Nh%9fU0ErmYhEy=T`P#2H!Z?}ME0+#Gbxci?s+0;#vlLpo##)!@|V$` z+Ss@ULBxnSPDomjqv5;cDDoWAS{xe{+?w;WSh049&)jB(nUn62Y)fklRq9d>oYc;r z?B07{wPDH^r)EAp^vQcK$>N!bA$O$L$)};L3qpxN5r_bSCJ;&2jHx}vNLu!eXItrX zP7)-pfQSGJnwVeWkE=uZkB%LG=O2~%X;ZtDPpgq0J-pHFNvVu44w>$WM?3!Hit{j$ z#M?V@0HnHmW-!FWLoXr?WL#OV&!IS-2S)oSGpsW{`5I}D9VG{)@fYJS2`I=jM#>vl$t81H)hM714vJir@H5n0yba9 zytESs@X1nIU84Xf-}eiJ!rR47q@1K!wI#QSmwZX9tuWaFAmzMW+*BwOeBXC6naue3 z_^Fe7pFY*tH#)beuL)8hMWg`TeohtCvUtti!aPr%LYO z^TYMwy*G`IXEGUL=DE4K6DLj_J9cbxaB5Bz<#IXf7;gQ)#q+#Ep)fKs(%09gl=>eqphFdf_r72N0000 + + icons/navigation/home.png + icons/navigation/reload.png + icons/navigation/stop.png + icons/navigation/vpred.png + icons/navigation/zpet.png + icons/qupzilla.png + icons/qupzillaupdate.png + icons/preferences/applications-accessories.png + icons/preferences/applications-internet.png + icons/preferences/applications-system.png + icons/preferences/applications-webbrowsers.png + icons/preferences/contact-new.png + icons/preferences/preferences-desktop.png + icons/preferences/gnome-window-manager.png + icons/preferences/history_entry.png + icons/preferences/stock_inbox.png + icons/faenza/back.png + icons/faenza/close.png + icons/faenza/forward.png + icons/faenza/home.png + icons/faenza/reload.png + icons/faenza/source.png + icons/faenza/stop.png + icons/preferences/extension.png + icons/faenza/settings.png + qupzilla.png + icons/locationbar/arrow-down.gif + icons/locationbar/gotoaddress.png + icons/locationbar/lineedit.png + icons/locationbar/privatebrowsing.png + icons/locationbar/safeline.png + icons/locationbar/search.png + icons/locationbar/searchchoose.png + icons/locationbar/secure.gif + icons/locationbar/unknownpage.png + icons/menu/bookmark_add.png + icons/menu/book_open_mark.png + icons/menu/bug_magnify.png + icons/menu/circle.png + icons/menu/csfd.png + icons/menu/cz_seznam.png + icons/menu/flash.png + icons/menu/google.png + icons/menu/history.png + icons/menu/history_entry.png + icons/menu/icon-wikipedia.png + icons/menu/popup.png + icons/menu/qt.png + icons/menu/rss.png + icons/menu/youtube.png + icons/other/about.png + icons/other/feed.png + icons/other/plus.png + icons/other/progress.gif + icons/navigation/search.gif + icons/locationbar/star.png + icons/locationbar/starg.png + icons/other/bigstar.png + icons/other/unsortedbookmarks.png + icons/locationbar/accept.png + icons/locationbar/warning.png + icons/other/flash.png + icons/other/flashstart.png + icons/other/keys.png + + diff --git a/src/data/icons/faenza/back.png b/src/data/icons/faenza/back.png new file mode 100644 index 0000000000000000000000000000000000000000..410c8974b182dd5003b98b92b435875f261f4747 GIT binary patch literal 533 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_*1rX+877lwZyI{s(^d7K3vkw8)2 zYaq-xA-{4CNU*HHF&#*Q^)giWEM3OHz$okK;uvCaIyphY>w*2_dN-dywK=C|O1em@ z7;a6t`eFV@`|9no--X!pd@RLuPR*2X;Wg5C{r~^ifddDA$ZK&WZ&)z<>;bcbdp134 zH#%(HHlL@7$BRdx_5J_-?HP$K=C@Uz=2#pqZ_DT5;pyQqDv;!F+bzJWb0W>(R9pMc zvI8IHJk$^QV0K4m$3dNfr-#Bk)tmm+r&ToAZ~6*DdcE*Bo#7Z0_zUATC6*u=kH0-!~wCBI5{PGHsf8htG{VB+N z^y~en_%0US9h1IyurTcA%PiV!GV3%j)Kp7cBT7;dOH!?pi&B9UgOP!ufv%yUuAya! zp@Ef&xs{Qjwt=CQfdS*u!?`FLa`RI%(<*UmD40=w5vV~DWJ7R%T1k0gQ7S`udAVL@ eUUqSEVnM22eo^}DcQ#T$MGT&B&ITmR#Q5)+M%xUMrv z%DB2r<*v(=iPx6dUs&`%VT&VL0&d^9&mfq4Z=v$4_6nca^;VvTQ=AYOvEBEzX-S zeLwxvRCK#{g6-3VA3a)v?sK$0N@kpJH~TkbLb@6&uU1h-iS~El@4|Vk{U;n-%_b}P zx2J#g=KHZKa8EPCi(U5H_+Qk8Ffde^b|wExVq>_m^h9Lz%-B7Xbu^e(%)G7n_En6} z=5PI64B3-wT6dP^o!PT>a{m*3eV3ii?*$pwIDS%%&bZgMYv+sXZ&6bslMh%Q(8yTI z@a3HrE0h*rdD+Fui3O>8`9)ncBa3Z+qgeKOY84)w}^8Rhoz(?7}i|*_iPW}-zSgR0s{k`i$j*0ZCGkp48TwgJtF+273&By2e6c&{1>-Js# zWkZp#>l}^tZ;yT7YaKq5axW<$L(a~3UV-?}umHt}Wt-eCURitXlddS|&MLk|C9xk$ zH*WoGROt2h^*g(ziyrSD8xtD(O?Q2S!ea_1<#qi*tjE)-;*LR8?Q;{}rZhAB`@7bK{ z^Jivq|7W?lXt95Y%3Xub4@;Wfhni3FocE+pRkhGC>Cj#um(ugM>m%Hs=5FnAec=&( z^W)XaaWl2GLUzp44qU9(oph{3z}aZ)^{B(?BIT>+`TVP4Rbu;;Y^&eNe_X*7 zpfKy@&)oW%yOig=Ids&wbK54i3o3elJFAjT3O>_3f8=5W>!J?9bT?7iCgyh)N3{$W zv^Xd%c)cNo^UCdv0zJo&0(vT80=JSeckx8Cqx?7+M(^NL^}-MbVI( zpOTqYiCaT9gP2NC6cwc)I$z JtaD0e0st!R+|}qB$%PWf?e~ALEwxhRJtMQV zXsv*+7Dv`}BWHc|^aM7S3fKewoarK#F z@ryGy8SY>G^;XX2Nd2mtXZp_b%X`jHo#&eoT2@Q-w5xTrMmSlsQvc~yVD9Wt35afNl3{x2bG z)4!3=XD;yi?|J;g-3PMzzw=Kr8gA-3@^zUvuhEltV(#0#qqDCqZTYmK`^mqRrw_Xb zGN_)}ZeuU1wDrZ2nO7&yUGmtrFmI#IyMsnX?S9wg_<1}+mO6HxsZ3t~eP(FLrj|9i z0h#+`*8aSaH9evBmTb<~(9IjpRpd#oPBL9?_WZEd`N@LCS6FBK`)KVSdG_}sncLlw z^2$?}?p)@W`s0GQmVaWX?pHR>)LtRimtUu?k6W{hb=p-o294c{1@%I&4^FXOkR#yh zb@SfS_qo$u-X@i^?6#2VJSTW}hlz`j%i?zl?m6en`2};A)?YK7b2;S2mV<)FLZw^| zuf2cnkIK6^$sZ4^9WACWYxnC}Q!>*kachug-L1P+nfHmzkGcoSayY Ys+V7sKKq@G6i^X^r>mdKI;Vst07w~2tpET3 literal 0 HcmV?d00001 diff --git a/src/data/icons/faenza/settings.png b/src/data/icons/faenza/settings.png new file mode 100644 index 0000000000000000000000000000000000000000..b06eeff50d22754021dfe29bf06a3da9d504ce05 GIT binary patch literal 584 zcmV-O0=NB%P)KAHW0T z1|hkC;4Zm@IYN%$PTe#LYFv1LM$p9Wrn`QnXJwBP2gVNt#pl=i?^o52x3{<7N~vd{ z%4spSEbGyXH0z+XX0cdM zuh)5feI*P-Mxznsa``V{e*_WX?(QxFESF1wZ&thdqa!3qLZMLL2$K{KdsZ*6Hf%3deC!O5r#TVHh45ob3SntKDw@ zS57H~nWpKx@B6=s#UcQk&4$fpbMWJi<<{#pX_~&9hG9H;p7*S^{>cep7{Hi|HAhLWGde85X8~XN6^I0>u?9NlZdQI*lI$g@jb@b2VzFrS;?`FSeag^m5y_1A@=Z zp3m8SzBWHk?~n15-_ETu$6^$2sb7%Mo4j&T1Dh`=!HiWR%^`4)6e_O&>C=+T{u6Dkoq zia2)Z-9C0W=!&-p!<{3a67n;=75B|kuBf{BeBpFgk53c4HWVyn+oc(O%vtla$$?Ov z|6UC7@`5(CMsk06_^%#1#+}YEXV$J@-6NdOs;pP*&aONr(Z_fF@%(>zf2!E`guZ%u z@|tR=pHtr#>HvV{=Urn zU$%AkmJ33fOM*O_s~Qbs9C&oQIE8XOCYl+3T&@v34tEBL-7py3SO%^ zytYo;;*p^rm94EQsLNS@sZHtjrrh#$JN;c5x+0G2El#Q!Ti)Mu{!IC~-*P)Hx9_^@ zBpBE*v94rl(5L;3nSs0!4dNe`FK=sPnZOcc-n?>l$83iO4_;mM_OJVURXF_d$D5w& zrosn|O6QvkywHsc+n4|I-rn8=6M|0dOl|0h=sb9E&s)`_((7X0tUqP4VNZj3?A!?- z&+mUTiO22G?(C|n`ybL$=Z1T!{ufnHvNE)gxGTh6Kk=c}(!kH}k7=)c+@QTOgyR6~ zIZFZaGwW_`{@Y)%dDA(~r9r}9dpsX;eRw}Je$T=KC0^Y&2ENUgPR)$lUw3-bzlYPc z*xaw%{>b&NO}V(}{IX1qPY#VQr@R9yX8f?+blIETr8|}tJw3;u>>GJv>uN*cn@_@- z*OqSW@AL?8F>-yfa9{cTn)gv>fePBQwZgkPN{t)x;}hSln`UrH%B}C&%Zlt@|8~bb zX>O4EvGCnZch@88N>MIeyQb;?+G5(XrDbAm>(1RSN6HV2Ev-K*EwkI`^5TQn3eH*Ygxw)NF4QI@w9bKaJ=vhneHo9#|6O-5#6w<1^Qmp@T< zUlb!SvFVH0{ZJf#DyZM8dZ+D>C@4tbFgTPkU=NO@!1p|;1;dn+%Nc{{i9 zrYaWg+P?RHVer#Q$9bQoL`c;DmG|tvvzKqew(3LDonLNA{So6at=lKY!MNB;D0@a> zf=p62$IXKHjyVBpVcyg@ zMX*!0=Ux8oKjKH2xi=f;@NPTq!*cz~Hlum|KCB);!$Qp2wY173)Xs1n@VI~SI^UUZ z^_M<>3Ks3@YvYan|H3Zv_ulMRiKkimIj3MhgT>}-Bq7)_0IM? z#>>UHKioE$Bh}V&xMN{}q}n{znF3zXr)Jo#w%qXlNY$3S*)pQRZL)ozKis(OKYe$5 z+4`fUdl-`SB)Gn8-Ogj2I3@h;miW6>9GCw*n|3H~k$!`a)$Hk3zs=7-zW$C$Z{AF9 zhm>EOUGHpj*HtZ?&Dt&9IPZ4XYfgcw_gsw|3<|=lUX?G9x8VKraX#N1&4lf>DffOp z_78DfAfNFfaSyOKP%UwdC`m~yNwrEYN(E93Mh1okx`u|jhL$0Q2395(R)!|p28LD! z20E1+mY`_J%}>cptHiA#R)qsp*hqqG2+mI{DNig)WhgH%*UQYyE>2D?NY%?PN}v7C RMhd8i!PC{xWt~$(69D|nH;DiM literal 0 HcmV?d00001 diff --git a/src/data/icons/lineedit.psd b/src/data/icons/lineedit.psd new file mode 100644 index 0000000000000000000000000000000000000000..d8de8c80e93023cf0bcca8c2bc4a23274b6ff3dc GIT binary patch literal 31410 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 DEVz_g literal 0 HcmV?d00001 diff --git a/src/data/icons/locationbar/accept.png b/src/data/icons/locationbar/accept.png new file mode 100644 index 0000000000000000000000000000000000000000..0a2b4f3258b83717856a3af346e6e731fd6038aa GIT binary patch literal 1202 zcmV;j1Wo&iP)q40(!$75sI>V@#OmF>qs-s=p9p-`v%$wHuPrKvyh4q^$&2CStF%;?-yI43{MIX* zw7F~opLF@Ickr7!!}j?iA;-bU38hVNwgPU)ZIC+fb;n=rbxk$X+$HUS*G>{~UeX$} zhKfuv2@N--X3+4!Ru%%$UlFl~t8On?$!Btmr4+a&XUqFhd*r^T?FX?eNTmVL6%-Lj zQ^W3i{c_0#4tG~SX3OBNpmhm^EI=U+4iE^891XiR{PAgg-jejd$6Zy0Qmfwr(cirq z3Y4a@1op<>@`1OLf6urA@3oaxXFFEa+bVo^=lUP^&LtqV@x_#R(JPb!QR*!ju`gdP zzmNh4GBn|VGnJ3K9mU6^BD5KBy>zIfx+N35sq@jDVSD_ch?s(c6H>E;P?~p#b5FC*k1JnBWLq?_&jVC0)de0UJV!bepO%g&9?ZlA9mH=Xo+C|L}b9jDBWBrXprm9 zS2y+zW)euqI&kKZjex7!AG`od3IHBxxG@$QM8uw|Yi!?|uo6OwBa_h)k5mMqSW0N^ z*gh9LMPM>AdjCjZ08v{&N&`X=gdj-e#OTx@!d9^EgVS4&P0QA9pNdXA7&1c%V`p;& zKl*0H*<*9Tfus_=`2Cs^f^nKa`RN8rZDVEj&B%7;^+oN#T7U3QSK5vz8fD~O^Umgu z$wdhOzzg5q<kv$-M6K;bv}4n0RU(?ez#9)brL{9 zN&*0>1&BBp2+7$*?AwDiebozr0~sE}#+tr5GDF>uvu#>I{$wE+B z&n*-V%=1t9Y)h$6sm$d_J~-pgAKun;XvqWs;K|QReQJ)Ln7t9G{pizu_bdhuEMPls zeyX?MvRH*_!f%>@Wu=B`K?~clW3g~x5u4G&AFW$!TjD1W@-ef^#!@+B`%l#O?)f(Y zX*VN-2rk95FctfXB`xfr%R`3f2`@D3Rf;)m2!^6Yfe-Clsx)>yW_BswOV*&zz z2_OXE@~pr-%b!pR0+;}#*4ic_#;1kjp~;^UBDoC9_Fk~it<8 literal 0 HcmV?d00001 diff --git a/src/data/icons/locationbar/arrow-down.gif b/src/data/icons/locationbar/arrow-down.gif new file mode 100644 index 0000000000000000000000000000000000000000..e4588a9b5e9efc9669ddd77a58cf08cc4dfd3e5a GIT binary patch literal 54 zcmZ?wbhEHb0r5dH3{1Q|{VTg4%$JMkyV)Je$Y2cs DOF#{9 literal 0 HcmV?d00001 diff --git a/src/data/icons/locationbar/gotoaddress.png b/src/data/icons/locationbar/gotoaddress.png new file mode 100644 index 0000000000000000000000000000000000000000..fdefe966beb3bcd5d508880b1f643ab391f204de GIT binary patch literal 612 zcmV-q0-ODbP)kKw97^{dhbeo!VyQUic08pV?^Qcj5&dwS3RXaKy&SV+VI5^>9yl|1E2I4j0x-rPHr+bg&W&=M0Wl`=~UrJNW$ yHuvECF5SIG0X%+oWhqB~wJ`3(@Z5gG06ziWUAf#6pOiWP0000=G`P)Px#24YJ`L;wH)0002_L%V+f000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iXV* z3j_nDa_?XO00Fs4L_t(&-tCz`PQx%1#y|f_+z3^2+NCq6=pm3eM0ZZY3Ag|Y3uoxw zH8T@cmjVq$II$lCP1FSh5*4!heo~|;b|U5b{GR=6vE6QOt+g)z8Za>u01yBV;0(a4 z27vi|{uV-b_TGoD-c(A-@7llP)ZYCMj4@dVVY*x{r3Qe_W;3m->Iwjeh>{!1A^hr} zq?Ce)+L*L3#&nCt;toKe0iZ0)M`MfwaL?H+S}wCJs{ou!Ypo|D#hS#AQc4lgyWHPP zmL!R3Cd>0Y$j>Q6vZ)EM_fbj!LI}Z%q+_a(0L1=cBBu{d+1F$&N&Jk*V>g(}`8TQb zJDAFfq>+BbU@DVX#P&riOOaTl7DdrCO~W}jV!Ez_F@^?Hwbl}!$2UwW3ni{o)1WU+ zhmt16BexTSvMlq?IVAzqWmy)Se&fel`%Xkphb0Z08hNDl*_KUB1n)idC5`SMwr%SG zT(CURu6%tTsTu%gvst@Zt*-Zu;~0ApyWOt-Y2Ui8b*kczMk9Z$Z%-x@IOnWbuh%z+ z4SAjir4$`uB#nRC*BIkdHMMQqzuublLWrD*oDd@Ky6)iv=3}W`(THIG00000NkvXX Hu0mjf*&py@ literal 0 HcmV?d00001 diff --git a/src/data/icons/locationbar/privatebrowsing.png b/src/data/icons/locationbar/privatebrowsing.png new file mode 100644 index 0000000000000000000000000000000000000000..5294896a37f203c7c9d4026956c1d3812413eaf1 GIT binary patch literal 963 zcmV;!13dhRP)KLZ*U+ zMZqD61UKR0ATHv^)-h>I5gck1NwPJ`rHVGW++068`4T>agNvh}Z%}a%oO}c~K^z<$ zB4lt7!84uz8UFmBvEp_7ZetEcxivx0mthpknMbB+0>B_Gwb$=&1LQ@n_`&-#0^n)Y z>-b&%#%!vM1wMhzOqD5!bs#&l+Jd+Q+~i1G5LbX}_JTHuJHQ!MJ^`Gh@)y7{VHAUS z1>9j6#n1q7lgeGdwd&jgvH7ey+o)$(IW?)qYRQsyk zp4P`o20fquC5&QO+1m~R5DSyK)ubop^jn2^`2DVbjPJnEQ_@xV|$`ith~00004XF*Lt007wQ^&Awc0000WV@Og>004R=004l4008tQ004y1 z003=Y008K0002CT000|UgXaf$00060NklF(6o`@luIZDytNx(%M?A1j#=jXeoiDu@ccz2*e<$C`3;J3YWvX%iZ4I&Wyz* zip^mjykZ{j`p^_&uzVaMtbmxxH299CWcNE0ORs30K5e}c50f5^7<;-t0!>q z5ljN56xTTn0PhMYI-oqboKkHrT>J9z-;T{(C7E7?##_)HT21`-4*{UWf$M-M@_q{j zU1FfyYvNG=RDojH#~%Hp7KD;*m^t8Y6!A|e)oA>QU>iB#f lI0v}@&X1^@s64>Dn$00004XF*Lt006JZ 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 literal 0 HcmV?d00001 diff --git a/src/data/icons/locationbar/secure.gif b/src/data/icons/locationbar/secure.gif new file mode 100644 index 0000000000000000000000000000000000000000..f70cc953a51da12b8c8757fbef6df64b6ea97e9a GIT binary patch literal 249 zcmV+9>uj1kAj$NKQO%#srO^T*ZI)!3~X`Ru6Nwj9HM4fp4g z`Pz1^t*!t6|NsC0A^8LW0018VEC2ui01yBW000Gc;3tY+X^tq_t*n=-o+V2~=c1ks zu&ZR_ot-IHyQzexPI2VPbReBZCeu)MF1Mz~p;#mml?etB$YvCn&4w^A2o8vd8Lei! z;SBR4y>7ob8fFQ6c7K5+8H06x2Y^`@2pp1=lZPK34*?Vr3>u!FmP4YVA0Yrc%MX1I literal 0 HcmV?d00001 diff --git a/src/data/icons/locationbar/star.png b/src/data/icons/locationbar/star.png new file mode 100644 index 0000000000000000000000000000000000000000..c11a6144517c32688f4ec2c5b93410ac380d7072 GIT binary patch literal 3058 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 ARR910 literal 0 HcmV?d00001 diff --git a/src/data/icons/locationbar/starg.png b/src/data/icons/locationbar/starg.png new file mode 100644 index 0000000000000000000000000000000000000000..9f7a8db52ef02333a5ea6b8484027c6b39fec492 GIT binary patch literal 3065 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 literal 0 HcmV?d00001 diff --git a/src/data/icons/locationbar/warning.png b/src/data/icons/locationbar/warning.png new file mode 100644 index 0000000000000000000000000000000000000000..01ad7d038405b0ae11ee3b4fb4a4dadf40c8fdce GIT binary patch literal 1418 zcmV;51$Fv~P)=4K4gJJC20{t zUy2|I`cf336g{YnB!eJ`A_z445K&Y_&;vDssOVuq4@RYoY$ggB9rVt1?&F-Z_v*tr zGjr#9&y3awH!Lpup8a3zzyIs(we}HJN+ z4^7x#zEKaJjXycl6g2nACW-p*mvH&@c-1K5?xx%T3a*JJV?Szq>~(FktFl>J|C>PL z6S+vD(#xi@`ew`mG_65$9CfEa<18>fm5XmfcpdcZr8qlfv5K_Jfl4H2ZBac@$>2qgIVDbamVEUGQD&-3T zH*pYb6U@vjqc+7`vL5l5lKk}#R`bOUaf?#-3He!eQwU6NeUebXxv282~bmv ziBmMSKyZC$$l0Y>%_W*zG<7|*le0t<(DuX(tXV^oI;uW6=?znbI0a|c787v6a*b2w ztInE&YaO3~HBM&lgf*xuLS@Pc3>6YkD`6IWo+g=sw(BzlS~bu%Y9nxSVHNE*UP5c>-LD+VV_)>Ny0&oG2uKHAl{@I||9s?uVKxfJqwBzNF1iLlyJ?>(-41XCEn1LXtUKDGu;=W zC%VI(MqtUkKettbdkSS#A6p+M8ToP=0Z0FEp zs=BjhDr}6D7E-zT0VG;X@nIyYOkEi|h9YA21<9J57H$9fw5M}k$;Ngp-Hz(h zr#%osZIYK`MS=Y_>SebTWeneh7{?s!)}itxliMn z=LerU`pBHiy9xLrxf{d+s_G6reBpTu!}!s_=$*bWpm`)!Mal1|Imjy3s_b#%J$wF+ zzrV?S8ZzpdcEuA_#TN;Ig+LVufq<+?8Njm;1CuwbDX+fk^74jN1LlhUNLEC?S!@I^ zwpyEvb!@cee%W(4{&M&J`cL^TswOMW4oHE7+!s(wRpTN8er{e)!T`o*4jTDpLnD4U z@{wlZTHxAb7q$o}DkmLJ%(|sDYqGlS|fAvb(dp&U@*`1aj*KkKxU{@4au{ya9HIknj7r z`glDXYt>~bO1X?oCIidTVHgJYz%)&cwJi&FSS3(B~;LS`v?Q-fPyU*NGKMIT!<7TlS!q3nrTCLuH?)Fsv@7QKv#j)Y8CWC0n+I-C#cu! z>=T)If;P#a;8Ll?_oTgOG}bbfNY=lSyT3PiKnMA`h)HRFMT{X>IXY3=o!o(kySggT$96UOB` si(6Zb37V0CSo}-r`K`&F{@kAU2j^#v+M?!+MF0Q*07*qoM6N<$f)So3ng9R* literal 0 HcmV?d00001 diff --git a/src/data/icons/menu/bookmark_add.png b/src/data/icons/menu/bookmark_add.png new file mode 100644 index 0000000000000000000000000000000000000000..c63960dd75c63ed6f3d15ffdb9bd2add2e7a79f2 GIT binary patch literal 767 zcmV)=wNxcvY5pt5(aDmfF+ZlA0`uj zkmf|9LZz5qmH?;=R~E4yks=fqh7DMwr3Pv*?d{9+a*>-8!nXZKF^_<>TLuF3iw|NlnS#qb%w5wGz(QFel&vqp+JdE!trxK9#6=*3 z(jtKD=|aT&tnYhCsg>PfOrhf;8p(Z%pdgD|TPmGvouAR>BTQV{ha>Gj2m zib5iyH&h}-QA&X*nVOnKZM0%(k+hrb-Mf45KJGoIa|OjL=?}x1ftlaToO3`a#Z*@I zq==S1vM*Epl7~w+I_?)rlXEnfpykbu%!x6L5ReGjzq_u&!p;N|CJ{{19{?&-*V4yD z)nhS<&=9j?nGYT}_v_C?qOp|+9H*xQ5Sj2qrnRbH`oPY!t_EiDZ!IHRa~!tlQ!9^f z>B(6&>vrZJ9IN5fJ;qepU|U8N`dmXae4IAUR5ia6G1@4+_k6*%t{cdkpN+LMSL0;c zMRi)%kb*01wDFYCyq5Lt{VJsRj+N-Cp4+uxGhFUINFfmsM5Fzi6Tf^stjReTyx-$$ zYWO%;&DSxCk#%(y-z~hMBihv|(J5R-S_z!ryCDR5{164m1`~D{RKer-<4S8i;*9j& zA1f5yvc6;x8x){AcRTvNwZk9u!5t8gmsJB(I0L+7gfU8w51waedUGPc@(+JF^XbC) z9oVlH1T*5&4EYJk_&V|#o7O@Xn1uCovw4E_b~nnG(;7X zZU*$4Y4|BhkYL1gITxw%zu*l~cqJV~g&@}GM#;Q#IO|-T5+X;(0a`_SzGI-1jS5J_ z2>YxY@PQz7IxXv>haid=5d^kI{BVRujn?$nf36(|wC(9Q%tbUU*I(Sj8p|q(vVhoV z0~<|;u*^XU%ghIOaU{|2^ZolHbsg$0i!n@VIrsDue)?nxk#`6yW)LD`_RQJCbIi#@ zekF|~!#y~*!LawQk2I!*)!EnOu)Ix?sn$AgA66{h1cjw}1qtE7*I1i00B>KT=kV%bINK*FV eYu&BAXMY2w#*up4hpDmv0000Px$g-Jv~R4C7V%Fj!aaRA5h_xJle&)W9Phq=<#KnDZeVT5`J?GTX=WT7WJB^`tv z^iMDdg7pu0k?InKbm>waqz=Jm4eiG)Qpy<%veEppjjhSrqfg%sNo<$ipX28xRsjux z=cJmpM`~S;VV1pb<=y&=I)X#QDheGlZqIFM1sntliylra{G9kzRfzXE1a8%CzC_~` z-MHA{8zrK|KJQ*=6hxZf=Nj z3-ACxFffX;@}P?fDZDM9p9!~-M87mt-h62U&fYtcJdGtCwbe)>3zJ-Z{=!!U*{sW)Hlm6qEOXb8ps`vb6{gsX=#Yo!1H002ovPDHLkV1lH% B&Q1UT literal 0 HcmV?d00001 diff --git a/src/data/icons/menu/copy.png b/src/data/icons/menu/copy.png new file mode 100644 index 0000000000000000000000000000000000000000..3836257fe907a563afae4c447e0cad2a02ce0927 GIT binary patch literal 606 zcmV-k0-^nhP)eK^(>3%7-n|AZ{BBiR=rqUgi>lAz-rIIXo?H>qsx3K@q zhZRMUtljy%w6x*{0S27=d^w6^*tU&NYin=$Tv7YR(YpTxdh!v*er`gGGo@lSMl^^9 z0}L)N;kqvR{XV`fFR$?VGxkjuqm4bJQNSjO6bdD(Vp%xqbkN${L;Ls`@0v|KsMVTh z-R=kWyr%BuO)N2BY0YM>HNYglx%>0kS=47{GNdZ=BPIf~ z=!5lj^c-hICT3S&T^J;tJ6to0m|RPG>rc0P*Eu;kzdk+v-Z0)8AAkI+(U>zxY3?kE zZ0#Ea>dZYn{E}Xdq-qlrF(-2dsc0hWMAz==X=2bzW$I?%b-fIUisfF&*~kSG6H2f< s8yln6kl*)45ouUO7Z>LoM*j*h052ROJm1I;B>(^b07*qoM6N<$f`R`H!~g&Q literal 0 HcmV?d00001 diff --git a/src/data/icons/menu/csfd.png b/src/data/icons/menu/csfd.png new file mode 100644 index 0000000000000000000000000000000000000000..b50c8194e28d414a1781c969dc646c1821a3e0e4 GIT binary patch literal 2977 zcmV;S3tsezP)KLZ*U+5Lu!Sk^o_Z5E4Meg@_7P6crJiNL9pw)e1;Xm069{HJUZAPk55R%$-RIA z6-eL&AQ0xu!e<4=008gy@A0LT~suv4>S3ILP<0Bm`DLLvaF4FK%)Nj?Pt*r}7;7Xa9z9H|HZjR63e zC`Tj$K)V27Re@400>HumpsYY5E(E}?0f1SyGDiY{y#)Yvj#!WnKwtoXnL;eg03bL5 z07D)V%>y7z1E4U{zu>7~aD})?0RX_umCct+(lZpemCzb@^6=o|A>zVpu|i=NDG+7} zl4`aK{0#b-!z=TL9Wt0BGO&T{GJWpjryhdijfaIQ&2!o}p04JRKYg3k&Tf zVxhe-O!X z{f;To;xw^bEES6JSc$k$B2CA6xl)ltA<32E66t?3@gJ7`36pmX0IY^jz)rRYwaaY4 ze(nJRiw;=Qb^t(r^DT@T3y}a2XEZW-_W%Hszxj_qD**t_m!#tW0KDiJT&R>6OvVTR z07RgHDzHHZ48atvzz&?j9lXF70$~P3Knx_nJP<+#`N z#-MZ2bTkiLfR>_b(HgWKJ%F~Nr_oF3b#wrIijHG|(J>BYjM-sajE6;FiC7vY#};Gd zST$CUHDeuEH+B^pz@B062qXfFfD`NpUW5?BY=V%GM_5c)L#QR}BeW8_2v-S%gfYS= zB9o|3v?Y2H`NVi)In3rTB8+ej^> zQ=~r95NVuDChL%G$=>7$vVg20myx%S50Foi`^m%Pw-h?Xh~i8Mq9jtJloCocWk2Nv zrJpiFnV_ms&8eQ$2&#xWpIS+6pmtC%Q-`S&GF4Q#^mhymh7E(qNMa}%YZ-ePrx>>xFPTiH1=E+A$W$=bG8>s^ zm=Bn5Rah$aDtr}@$`X}2l~$F0mFKEdRdZE8)p@E5RI61Ft6o-prbbn>P~)iy)E2AN zsU20jsWz_8Qg>31P|s0cqrPALg8E|(vWA65poU1JRAaZs8I2(p#xiB`SVGovRs-uS zYnV-9TeA7=Om+qP8+I>yOjAR1s%ETak!GFdam@h^# z)@rS0t$wXH+Irf)+G6c;?H29p+V6F6oj{!|o%K3xI`?%6x;DB|x`n#ibhIR?(H}Q3Gzd138Ei2)WAMz7W9Vy`X}HnwgyEn!VS)>mv$8&{hQn>w4zwy3R}t;BYlZQm5)6pty=DfLrs+A-|>>;~;Q z_F?uV_HFjh9n2gO9o9Q^JA86v({H5aB!kjoO6 zc9$1ZZKsN-Zl8L~mE{`ly3)1N^`o1+o7}D0ZPeY&J;i;i`%NyJ8_8Y6J?}yE@b_5a zam?eLr<8@mESk|3$_SkmS{wQ>%qC18))9_|&j{ZT zes8AvOzF(F2#DZEY>2oYX&IRp`F#{ADl)1r>QS^)ba8a|EY_^#S^HO&t^Rgqwv=MZThqqEWH8 zxJo>d=ABlR_Bh=;eM9Tw|Ih34~oTE|= zX_mAr*D$vzw@+p(E0Yc6dFE}(8oqt`+R{gE3x4zjX+Sb3_cYE^= zgB=w+-tUy`ytONMS8KgRef4hA?t0j zufM;t32jm~jUGrkaOInTZ`zyfns>EuS}G30LFK_G-==(f<51|K&cocp&EJ`SxAh3? zNO>#LI=^+SEu(FqJ)ynt=!~PC9bO$rzPJB=?=j6w@a-(u02P7 zaQ)#(uUl{HW%tYNS3ItC^iAtK(eKlL`f9+{bJzISE?u8_z3;~C8@FyI-5j_jy7l;W z_U#vU3hqqYU3!mrul&B+{ptt$59)uk{;_4iZQ%G|z+lhASr6|H35TBkl>gI*;nGLU zN7W-nBaM%pA0HbH8olyl&XeJ%vZoWz%6?Y=dFykl=imL}`%BMQ{Mhgd`HRoLu6e2R za__6DuR6yg#~-}Tc|Gx_{H@O0eebyMy5GmWADJlpK>kqk(fVV@r_fLLKIeS?{4e)} z^ZO;zpECde00d`2O+f$vv5tKEQIh}w03c&XQcVB=dL;k=fP(-4`Tqa_faw4Lbua(` z>RI+y?e7jKeZ#YO-C0KQ2?K~#9!#FM=W!axv&zY|lWOOezTKEd@Oh+ricNMmW6 z7rL~TmI^iz#0SzMoy8SvWBy5@Cpa)L9DBQavwO;b-~t0u;u}4}5z6%m&K+Pt@+20q zMT-V{XHDiwECxg!=QG({&j4iJg4EvLIv6aP+5&*na?RL}LI5t?T`Nf$)J5h6WL`jO z3;ZaoJy;#~Op{5Kq`t7I*zMqXe++w2d!Z}#J7s@h2h65>h-g9fEWB8WIWD+y&PDMjqSdws*b*A3Yf>7rI5ra0n_jGvM z+2&nUG0EvWLOa9U>+ zWpdFp^4I`@0I@`y1nYyx4k?lH2RYrlpHJ7;&Bbhvlh`ETs36{vJ*BF;HOxoc-bp{P zgs|RwR*-x_&6{z5S%=OT6wWFnLaH_+Kzr+zn)*QS;M~#@p6FU{l2Lg>!Oja8OX8O< zU#E@HT(`%kMFeX*0cuz6!ZK5*B4P@ttF(~wzti)LBNG#zj!b%>o3p1mpbx;}KZ0!K zcDv?sl`A$|LsC@^4Q4W-54qg@dqW$V0){vs11PN8#)mGCFh#I)+%R^G>KaQjnl*tW ffCT`?dMEw?0+s>IQZZ**00000NkvXXu0mjf`zbWp literal 0 HcmV?d00001 diff --git a/src/data/icons/menu/flash.png b/src/data/icons/menu/flash.png new file mode 100644 index 0000000000000000000000000000000000000000..2e3b8916f3d771d7a8b8854d9330952a99f2ab34 GIT binary patch literal 623 zcmV-#0+9WQP)Qy12~r3QB7?MJO$3f=S-rd%DQHmr1&D;B02*o_o%jJHgkjEj`=YV|jBEGb7LQ2cgw! z1pvXPr6py++j^Y^AS@vPAix|72Lp;M%L*V$fJ~|mLLgwe%qNllBa2dr3`iw{veD`yIQRx3lks(5+L?4c8GyL5?9S8>qkd?91cmO>VA|+n-(nO|2)^78( z-6rk#dAYkwzPdVdx3Um>-AgTZX1wTj0Z1<{coc@z!jOTdFiL>jvH8W9W<1{5V7=Sr zw%6m^&JF_Z;&}4x&PJ5D8Hne?dw+b)TBk!k81Q3%pPSLBnupTA$`Esj*y}D}t<&M} z=_x-C54k=+Cw0ls57jgScUTg9URhyO6x=2Wsad7WA8nH+859!Zxd5;3@25qMe1^a& zSXhQj1;MKo=}Bg0nVF>ynTOo0nAvm6%YXA6c*Dcj5cmaL{{yrRgBXhQ$6Npa002ov JPDHLkV1f=`3kCoH literal 0 HcmV?d00001 diff --git a/src/data/icons/menu/google.png b/src/data/icons/menu/google.png new file mode 100644 index 0000000000000000000000000000000000000000..f106c309f64637653e93b56b94a0df6f4b27e5af GIT binary patch literal 1094 zcmX9+3sjQ@6#m9Tgo%J;kcfhaJVGlXP0>UiZewiBfv<>XW*AD)$c)I)lmL;JYy*@x zqNDyl_JueDq0zShTCEd%%0W~qt!`qGgU#y^_93_c7kJ$z@k|aoLDAxd)+^N+- z0b@R=A{7*q#U)kBXaC9}WI^bGR2~kr;Vc`;eB^qV#`++Gg**s!;yK9WA*vTe3~5gy zL8uVt4Y3>IJrUV!cdW(!So8D#B7}A#j~SfXj|?s{Hz&xl4SD zan`Y>>#?Z3>q(h8sUlRQvae!$_7(%=JEq?lB5p$_ee&}9=DAKFn z*fuy}dtu+4C7ziBX&jrJV5wZHEoj#|(t!f5te(Cy+IIAx*Y<+a=_bFq;o;zv2J47Q zWO6J*D#k6SIXt9sUsr05Q0?d01a*oSr!l|X2)i?3ftSzf5#A%KGgcBL()eCik3z`1 zyUQI#Y)NzYfE5O68OUXl-Dq<#oSS(y)t!XEGQ{(R^^aazYsjR}k+KQtTm>;p^Dn9F z7?jki&AwF)j7}lh`@vQRrr?US3Zj-`B}q=n6%$R6*Be1_k01!+gH^J+ofqpa>Fv8b za&7&H8G6=1-bn9y+B?@@h&Y!(Q5yGU{Q(9AZW)ueOv)h&F>e;C$4lk-Hx9`RIi+_7?wXR(2bFf-&QKd78fiNMWm!EuaCK3)zsAAwi2Q)+QX!`!fOt(s)gA z-n+A!X&ONdM$F6>q;Tx|FMqpW*HHQ@!|3;nKf(Hw5l8bEbSBW&95G+|<;D#_$7i-h iAtf$%mzDnT;9eq3iv252nlwgHmjla<>ssO*aQqzCW zO}8MixN((D=Rm!%O}%hVZPH)hU0Z)d*Zu=-O*TiNS9RAN%jQL^l_;VzGY}ArgB%X> z(es`{d$!+s_MZ3oJ>RclZf*_=g#sMM$(mpx+yh`E=L?U z)har=yCISu^83N7)i}#$ah}V=?|nTa?A-wwOCy)*Vx2FFcgpPqN&lqtlKDyB6=K7oSzrq zaM*o}tE7)n5XvZ(N^o}>t|Pj8dgSIY-Ih9yu0WYETT!fnag~$?%YqsSc^62*MWm;b zlL!Wb2rVu`uh+|ExfUTmE~o;35TyUV9q6) zOih$%^gMGQu;{-MiGz4|w;6>@4q9qpgHkn6=NXylHp~_a&eGH?g9(R+M{&kw4!(xN z>ru1Wf@ZCSO8z%E>NFEZYKxj`&zz)Dxs+funy|GMjWRB8wOY%?VnGF(^2o^OU?F#b zN?Al#=PhWp8Ynp)l32rG;siOmv)Sy}+>E^4-i}3S0l)C(xsiBPMx@J`ZL|qPeJcAEcS6@yK z4?iCG@1bF5=TmZR?bZ9ey&s>;pMUrwa~fiAHPIv oo1?BL>7iZF`I3+)T>KGW0IeT`dXo+(7ytkO07*qoM6N<$g6$iUzW@LL literal 0 HcmV?d00001 diff --git a/src/data/icons/menu/history_entry.png b/src/data/icons/menu/history_entry.png new file mode 100644 index 0000000000000000000000000000000000000000..ca070769fddab8a6500f1178efe36a8ec7cfbdcc GIT binary patch literal 878 zcmV-!1CjiRP)eGuMw{ z+DzSQo7>zubMtKH^d2b-Z@TD%hr@T?_kF(SeZKc&bX~{)ES9-Z`e&EN7=K_bu&!8M zTzeW+IUYBg-;Z zR#xU|Zf@Q%GBT2i$KydFj+l@m4-9BT1tA@}-i6j_b(kUIOO0v{{X1aDMO{>PlAQdfQbb5(!k0lHV&Q2>AHsHB>cgBZ6WU z^Lj5d-CKEdl~;iR?Bl@n>Nrrh2RpWIh0o{1k=NdyVjH7WvgT0a@- zRC^LV$2OjER;X_1*&K>WOBad-GLT^ynCR#i&tF0mCHVbshg4M!u+rznNqi_ls@S~J zcScdth3^JGVP&lcqC;E=&L$Lm9vOqhQV5RAmmfdwe?n~xnVn82j7cM;W3gD=?;pmt zZ95w*7Ct`|jADYWb@JN}#N!HiAW$)^hd-iRrRtUP=6OV07*qoM6N<$ Ef*9K`j{DgSZtS^mS#w$tBA# zD>-_yl0q+(9;iv}h(pJURy&2Gz`YsE`pmwadL6?A1I}h^e2de zlXP?wt)fmYg_`2WlDgQY@uQ|q-{&|qmPQCTdoSm3;9T7IIiiPte5^XT>_N;<$Jm;- zxHSnDDf>_S(B=dYFkqg2>rGW!{g8DPCq3h8J7xP38lcV>#^^BIH@+;;{; zgPfhCSu2xN>tMSeGia`i(?4{kErp!hk92a#IgC6hW$@#GL%ztK7YXF?p|A<=Yd63N zFOQU`ZDqDNg~=1sb(*yb%~}P?BV3frElF;%>jsh#LJCBcXxT~nk~ubN6ZDPTBohLS zaPLb0cj{L#4f7HhIPKM-FAf1Li^8r4=_7-kAIW+&-5DuyOal( SL)F~?0000P)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2igV> z4ICiFFZsLx00L-9L_t(I%WacONK{c6$A90wckXq@2R;f#${rS|g!XV@#1L(Qs3;-| zqzI&-g;4EU6ikG2+jHhWm*@jpP zGT=0AMctn;geva&Cf>ykfeBUIOqeewUOx6POHGYb;PDWZ3}t znn4gUo|~7c6ErW2_17dK3Q10ZA?{G9y`K#^08}Da*>YHkC^qo@@lw)x+{cnuUd=63|Bvu1-B@05@RqCoTwD-iY%`mo^1L7}d1=Iy;Up>ZEN_<3w{BVeb z(aXcZHxriF!-&x@&=>qtiN~Vd3oC%6cg?b-8kh#7AN)QM89*6nIs7iMy2;qvBgCTJ a%k^IoitwgaE4&i`0000IU`$e#o_VA-9m(PZZFk*oXyxlWud<$JOkN$?Ky zy0`pq6%@i|0Q-Pa79W3@<@=N33J#I{VUYarhUD1fUBqfpjoc)#v57b_9j1yUCf4x% zB|Eu0hvZ- zJ5wZ$VU#*d%9skme-p%)F`@e+-+zszrjr~UA~_PSMC|$cCPti|He!xNu7efiI$B=B z)7;HSysZf!*Es#5CN(=j%$-WKHK7i%rEmo|)>^`` zQ2_)KkeCd|cueSt@b9|Bwk450@Ohe+m-67ACn0)8R(e2L3Ii983UiPTAkf3&w+vwY z6o>=)sx$KKqcV}>Np@p@8f1$cr&;X2PZcFMzI?z?51v`>ghk-B>T{7nKtO-(ka6d)E%wIZgT{zyP$8Aj}n= RwRZpj002ovPDHLkV1kcRS4IE; literal 0 HcmV?d00001 diff --git a/src/data/icons/menu/youtube.png b/src/data/icons/menu/youtube.png new file mode 100644 index 0000000000000000000000000000000000000000..70455baca88e34ad9522c806f3d382bbaee9bd7f GIT binary patch literal 436 zcmV;l0ZaagP)t_bn^tB9e@#YM%Rx%9407j4jU8E}0= zK7&@^B!)#tAA@b*J=uf7EpI17TIWfInPp~R3qVR?G=>4+p08%$mI`CI@#i0d+^aPV zTtGHV14vARg^4l)j+TfrSoge!8-!uN_ZL7-lA#PYe*a}q2P)bH^nyFs3nzeHFlV?1 z^rAM&HoHumM|_56KNhu*^c8t1C)jSGu(NLkWdjuHlKmv z&Koo_bQgdOz^4#oIAH^z3c~^z7*=gyU{D5XxQoOGf!(_q7&0Rm7^bgaU@$XfV0ieL zfg#8NhXE=g5EsGu!Vvjcvl$rDQXvLjx{S{NVeEXE0dOxdVlf~!n}NaA6(SGfS2i#( z7#c7zY~IYkP?*5LP|?Z2kdlJxg7-h)LCgb&C9YHfk^{#cB*?*p0GkjvA%UEPqzzYE ez#@LpAg@3|CQ02wJZyHo)3f&j~Qd5 zN~LnXTrT$)x7%%mVR%rl*Plx%?-0?=`F#FvwOTcmQYTW%V*?>_*r`CY(^to?rfTbia%lO*|& zWf?f<5JL2X5IxR0vMfWAB%jhWedfCErpQs)3g7o3rJO7li!&m+T`revj4?3Akmos~ zDEi`g-u-H|`eryB7Jj*~VgMkbpYfu09!V*WOQq7U;jP!}g|6#0&N(zqYiu@~@@O=A z4PdvIxYOzE!Y~{W(V?bk%{Y!fdY*T0xm>;?q9aYy>^P3!20`$!*=$atD8k;@whhBD zZYZTrP177WjswSWV4CJYDfQAYj02_A{=x!^&R4&*<%)lY?{^og&qsFIKmY&$B6?I< zbW?9;ba!ELWdHzp+AA+F*UQYyE>2D?NY%?PN(TTo*alb-Q_5Tb0000tUD^1iQ$(gjT}&7;$qGa-t&3#=1ycYHErdcN+1f@Eh7xjSPwK$^Hp z$2HF(SH;>DSXJCqQA#JM)v>hZd`~2H1)hrHF%=l+7)x!C8JKY7r)L`>kO1lorN)q1s-U-6&dAQ`z%U zU4mq7fic%UlUy?aYml`ClJrU<1;O~9EA|BZ;t>aFDjky=L^)}r-@s(~v`~MjkbcBQ z4y?HC7W&3YJ3GfMV?c98P`K4zkZ$H_3_Lj$_ zODu*FE7$IJmzl}7j$0>9SQoG`d5)#Soin_mKKDE>dT|!a z6!5=obFtmPae~Ua6}@xj%Xcif$Tx{Y|G?MN*ZoTRM8yrQN-bLW_HT>LiFqWb$9y(+tQlTR{NnVo+B*sQt!-_MD&8JHPL)$KMO TK7C#Y=x_#4S3j3^P6=BwAP?epWmpH=FSaubE~eYDP~~sQc<~h@nQ`FLtAI3tEp-K#EITMK7~a^ zd-m5CQqJjV6c7n?$*}Ume$t)|Nje%b^QkV#i1m~FBr(B1TY+`-sr-> z!1%_~#WAGfR?g(tWs3|18V>8OXz&mc49gQ-bR%l*(RbTQo8SKr^Y!>}#rF5#lPPv; z>YTy_(jQONT5QnSTP@U6BzRrG;c!Dz$M<)O`;xe3*5 z!=^62W_R#_%vQ-yIldbL_Om8!nACLf!ey%m$BieoUXPz2?;rkB+vwrKOM;?d&(`$k z`Y-19uHG^I7W4Aw3$LxR6RZ7S9;JC-E<60iUPHy5rPsOF>h5_r-TVRLVh^p>Yh2wa zPSZj}R=e2U^;(-Q`tAi6OG)pln8H}Dx6hSXc^zZKJ@?J_Ec-m=bAjEFe>&?qd>I?~ z_U&0ZX;#s>mjcTU{#JF5QR!9`&P!Osg+9_Ec%* zpFJM6*tEE@{L~y4?V6>gDMdyHbyF6J{#$(USwNTlvrV3>PYEymdSv5F&D#fUZ}=HK zaNlzOmzJ9E_f@kpQ?u=>fBotyzh!oNqp0}YyGk?vC{Iu>nw6G$Pr5Qy-i*)7q$6in zUiQBVw!F04vkxgw3iJ5(>qo~Kqs_b5WzSlcZ51}_{~p7Sjk~*ykM(6fnf3k2b_QmK Y1=hKjW`Er)1x!N>p00i_>zopr07&Pp*Z=?k literal 0 HcmV?d00001 diff --git a/src/data/icons/navigation/reload.png b/src/data/icons/navigation/reload.png new file mode 100644 index 0000000000000000000000000000000000000000..2e67f83ec91c2b2b7e6c85381ac797826b49aaf0 GIT binary patch literal 802 zcmV+-1Ks?IP)(W|NrG*7yE6cZXyIm784T)GvdX}OpMx$%uJhj6SwA4)d64U`)uIi z{;A2x_p1(lMfCmH7|4Dp_n>(nQ24FxSb*nMMKL$u5M1Y4tERU}CAH#pEPjhjn z3$n_A53{|`e*FB+oRN{S0O-{(OpJ^Sg8#TRfOI>EjT$9P6M;gER1E=SAe_2g2I!C} zZ)SM}fcPS*+gpIvRih-#{|upTXM3)pi36}?RW?x}-n&31BDXLyOnNolBcCpH!T(jE zznGYq7K5V#h8Y-iNG=H_vv!pNQ?fH8m>8Zj{QI5#bf(WY;<6UwHB_%LlI{RzX2wEb z@g@oP84KGHAT^h$Xkh-&&iEfG-2t;L$+>`uk?9OdA;Z9AMtl{^WQL*_Xcoy10J{GS zay$Y{IB(Uw?Ry51bMvSFOd^yf#>?Ma@W(0%?7XgN@QGFs(y^4^m-*1+8Hm*jFlqkO;mzrPIU zflN*`si#1^^*)cId!s{icy|0SjVli~j@yZHo#$|2d-iKBfO^!vFmK|1H%2 z>-PUGw*SWM|1HA*G^zhNo$^Dr|EuKxF0%hbsrbt5|1GBfII90Ls{ftS{}{6WJhlJV z^8X^5^d7MPzUBY-{Qo+d?tabxcFg}Sn)EoQ|Hp!XoU|1zfkH=+8o?VocxZnIBssA6J_a3zi!~Y7!~K56`Tzg_A^8LW004ggEC2ui z02lxm000O7fPaF6e*z;y69g48gpH0ZYC$V2J1aq26Gx7YR68#PA4NZHA0I0PCYys& z2SXh{8xpb`Z3PG~shb|H9TXB(1bkp_5+5BeBm#~aB4s29Z54zXLKLn5G>#?!BqV1Z zn*_)m1B`?-R}N+qsv3C%Bv&wvPXi7KEvkQ12@Y>ljpU%Yr%9ECelBJ-!jdKhgb4b` zBuKI6OCuU^KtNca&yNd@BN}xX@L)*^bB#FQ00BY|L_$XzYM4rEFgu)!3{jb#}E0J#vyz?wO0*wnC-LajAU=?ALE=M1 z6Cxo=!XPoiL~J7lC{8$G!8gSTA}4Hw2ysD-l9!E+EU==Zu>u$xRP^v7qw<(2`cz5w efdNAj6`3{jc)$Y>htLKUC_s$5wQE&@002AV41T=; literal 0 HcmV?d00001 diff --git a/src/data/icons/navigation/search.png b/src/data/icons/navigation/search.png new file mode 100644 index 0000000000000000000000000000000000000000..12750e733eeb7a85e091802503782e48738630a7 GIT binary patch literal 696 zcmV;p0!RIcP)xW5%o>2ICdlL0IT*x(qkFxm*6UbUEMp;~A)VJ@8ddC2IQ043uw}D%#m~~%#vY6k zx7&s3&slVLDrl1hC=MPQI1A0fgww{Mqg{fUt-xV-AarwhMGtFZ`;pIqWv!?Yg2g2r z?-qbMsUtxhI4XiuGlOEOh*bbZ*$BfhAks)By+%!gVb)PGYH-jq@Sd;M> zP95;VN^jD+5^RD6HX2c&I;hza)Jy@V4|O8){8{Thn0PWy0)bw{-h87vETKxJ5;+E5 zCvbBARz#vt@oDxW5(yP|!}pnWTlxBpYos@D42nkq$5~;TH6&9>#9~pJUG*Lw)t-`c%Jn1bi?J6!SQ?(!u;*@EkhcWU}=e&e;MIr eh-sSmCBOjt&+oUg;ie7%0000V8H-3hFydL?sIX!B^r!ZlRwt}0{r87`xt&^;x}{o8oLjl9j?70@%~kGP!iFc= xMA8&^k`6O?@Wu+6UEM7Amb?8SKLZmFgWvHgn?4*h7h<{|NsBb!01~e z$iTqBkHZ{JW){|^64J^_ng%xffB*buIDca2yRV-=%>MoBM+J z4>JqvatRq_IZb^VzCXWzF`PfX{oS{(pC93#P z-T!ZHxdBthg2&qy9GpDd`~sqkcdwuQ`sBg&tG|AJ_xQiLO3IP)z%htK z6a41}_}70XMn}vNAd;~Sgt0LgTkrRK*U$H~-B}M#N&J%b^t_knd7t*PY zN|#;;D*WHYu`Cx0OU(xHoYRe@aGa(?5#nr-QFxGNWm+U%OtZ5<}^X&b^M0{ zqRJFeo(fcMRlX)LgGN@DpALXUC4y%*$l#@00cdx<48i9^=KKYynv76yvMUKzi^)*@ zbO>sX2kQ1V6n-Cvu&ohHUnR-S1i>PM16cEk)$Y5 z(;-4|RBdGf&)*3{x2p%HS~2ypg3|zv+l=(x2w0%MpBqV*R9%@QXNgrJv3+(zCdpA^ zV&q%s+DQpjmi;RN-R>S(hTfIIS7S7GJ#=lBiY<9B3g+)W!cChUa&uQQC8PAIOB1i`2>t z?8k;=TbjV51a5SjpcNYwa}l45gDoCD%*8HIaT0klhGH;=(Delv_wGf@nJ?Czz1Y|l z_&@m^_HGwdPDj*iRZL-YJ_+{l&>?Q44MZ`IayWo;G(r+e)l)74qtgmgqrO_XN)0ADplWOaG+IO!h+zJ}0d6;DP>zMyM!%*c5zHVs8;A4l zLA0JasUUc8>_db}f!k+?&LAjW5H6U(ro3M6-e#})*1+STEd2N5=p6nEjlHk0 z+oJo&jw5pUCwROfbPfL`c|`eavTGA>-6AR`H0)1f`B4g90(bVlAp_n$`a$LD9sXLj z-8*_5q03j$POWOtDM=p8C%|rYcJj7OHrcqakU*5Cdh76MZ0K79zfItwOINT`E@KP% zTy?%XJ!Kk$YP# zNYnHD=EEqOspmV-4HI}ASNBl@RkgiYgmG1RZJ+$fEbfGp*fBDUwl}LJH-?U4X_Sg- zG{dUZ;5PZ$AMQ53#bv1^rt(Exe;kFIq{k+2RjPU3CBj6(l|5P=(%N+2I~%Pq3+ifXqbacQW)Jr@ndPJ;kxclbmESKpZ7^$uy6Imd;Wah4VhJo} z^Yl7rc+n<-3v%3Kf(U+bw(uPet0Fm+D?q~-EcM_|lGNF!QYmR literal 0 HcmV?d00001 diff --git a/src/data/icons/navigation/zpet.png b/src/data/icons/navigation/zpet.png new file mode 100644 index 0000000000000000000000000000000000000000..d61837ffb36c23fdaa63e4f2dcf87cd67aabbd1e GIT binary patch literal 600 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 literal 0 HcmV?d00001 diff --git a/src/data/icons/other/bigstar.png b/src/data/icons/other/bigstar.png new file mode 100644 index 0000000000000000000000000000000000000000..f20137b8f82f48369ef7b86a7c3fe5c38d66c0fc GIT binary patch literal 4298 zcmV;*5H;_KP)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z000H`NklP@4_u^F;-L`*sz?bzt%^X1AR&k%+CG4IKqXvSDXtpTx!5Fa?1C)^ryJY#+RoN{ z@p|{{nU9Ba)^=Q{YFsrL>Ay2NbNT-B|1Ptt|Mvj-` z0|%Qne7*~K^mY(9lFH~`84OCyyl z#N!rqEqCn$wtt)i4kk7~vL-CU;O0;|;6WUuaFN)$Z2-^T%z=Rqm}>&IE=H{5p*s>S zHc}k+sr%_K2CCCUqZ7nB_H4}#y?n+mP8Wdx`WRi#i1I7p`;2M3{*FkZgK&H!;ih)N z4KZx@GG^v9m|0NZ{RoJFae)XlbWsUxpweW5BH<0t6Fidldv*!EQy_ zaSS0Ub0tbMqs(S5F*?wFtg$KdM9l>+j1Hbz+kU4$5{rdl_!v%U8Wjnl>@ZjX4?5t> zt~_Y%8TRDxda1(f7{+}EF;#F~s8qnU39jBr=4zUu^F1dYe0t!C{^R#91mS1D-}keD zo>RXa8|*>Vtwvlhxja(LdlFLv3u9xJ|6XfO70Kr@r7EiVlZ=kN%jLeF?y-`3c-bu2 z^>qJt2K#zn8l6;Ft#?B(0*-^^vq+`nNiAl<=QW5dd=~>w87bs2#XN$ImAaF6^KnMb zzkV)V;EVeY_g8P21>>_k8tCh3)}P+~g|=09W2OgCQii!%j8jJ0VU%rq?$DNZW7k2d z9&jq&&8sfT3Zavmn8>FXId}5y@v_>r|44t~1M9&4!~NBCp1mXgK7Dq4DuhmK1}o&r zZ4BbfA*CWx$|1$~Jj@o5@|=NdMe5 z$NF5lulv&EOd~{6U;yK#oQOfpBFwv`HZQ2UB-u(kSI_=6GwoXUedQ-_jonlw-20=8 z#Yso)o_hNXDzX|8Kj>Tou6QuUGwRCx&BX`^s7Q*bp4AruqW2HN)4It4=E2mwoNxmg9~qsy{XGGK?H?10^X)%Yo#rmbYBrkLzK zFtHFB3N&rtT4s)kQzt68GH&^b*gzNv z395Dc%rV{>x%`h8Pi3C_-OHoH{vi#3({%s zC0{)7rRw(@>#R@Q$i{N=TYj&W*|g-rpC79m^Z@eZ9jhXL?#>i_!ZjCo@c)u6=Z2u~ s+u8m<0J4?fn?^zCTL1t607*qoM6N<$f)9WzvH$=8 literal 0 HcmV?d00001 diff --git a/src/data/icons/other/feed.png b/src/data/icons/other/feed.png new file mode 100644 index 0000000000000000000000000000000000000000..7d17774494bef79227784dd9d725dd1b9c59d9e8 GIT binary patch literal 727 zcmV;|0x127P)eRFaMWfXbrLS zr%5eET#1O-oy+1%SL#Bf=*DyrP`Y=e6{%oXB1ktPNJT|U@dIvJ1Q!a@+9HWjo0_y$ zYip9`y*D%7nWRPC>cHX6n>qK)y?4$qr4;^Uu=4rD-)lz+{e(V3ijXBt5he)tekVq* z6ye*PR4(jD(lo<&wxwBn8(4D!EE0vP{OncVO1)~KFd*}VQR0jcKS%sOGDyM>XRV%Y zs8|!^+6q*)3@8^Y90hNRf!Qs<(lW%md7xa?ndP=JFemR$LWV4a4&MR`ZKK!)$r@)G6ECWaKM}6 zre;0-hfdHb3UclV)Y>w+g@IX&nz#xpK|JC&oVjJB>sD858bZL!m&`3N@`M>w5-e^+ zc)S^mTM&hkcI2Qo5^or3L=?XQJ`VrhF8DfHz{7ruQQ*-atauE*?sl-C4`SuJ;cvbN z-gpZ+)SQ&a7f0pNDiDroxuoU;rcZ7sq8gM7sb0002ov JPDHLkV1hcsNkISr literal 0 HcmV?d00001 diff --git a/src/data/icons/other/flash.png b/src/data/icons/other/flash.png new file mode 100644 index 0000000000000000000000000000000000000000..590c561f324da63cb61c6e4e5fd627fbfc8df57d GIT binary patch literal 3373 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 D*S&}( literal 0 HcmV?d00001 diff --git a/src/data/icons/other/flashstart.png b/src/data/icons/other/flashstart.png new file mode 100644 index 0000000000000000000000000000000000000000..c930ee7aecaf0d4134f5be49b7d24358cb0a2f65 GIT binary patch literal 3220 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 DD%$+o literal 0 HcmV?d00001 diff --git a/src/data/icons/other/keys.png b/src/data/icons/other/keys.png new file mode 100644 index 0000000000000000000000000000000000000000..621e722ba7c8db02252b9fbbb1b10a2a30474bfb GIT binary patch literal 4854 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 D?eE$f literal 0 HcmV?d00001 diff --git a/src/data/icons/other/unsortedbookmarks.png b/src/data/icons/other/unsortedbookmarks.png new file mode 100644 index 0000000000000000000000000000000000000000..89a94999ae7fe2bc98b1138b7eeea17daede8bdf GIT binary patch literal 481 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 Hu0mjfv*?MF literal 0 HcmV?d00001 diff --git a/src/data/icons/preferences/applications-internet.png b/src/data/icons/preferences/applications-internet.png new file mode 100644 index 0000000000000000000000000000000000000000..ad16eda58c3c837185eb2c493d914eee7c55cdfa GIT binary patch literal 1979 zcmV;s2SoUZP)_f9KwMboabxY)@uz;v|+G3n4=Skq{zC zNbK052nn&vp8tUC*dt}fhFt{9EZBe$NJavM2*t5NBojMv#$%7i^X%zIRaf164vXsU znXxB9Swvi^)VI3p_WADjefOMGVrKk5M>3g+hzGKh-+e_R0F{}=DF6}4fAQ8I-gx$< z>%a8vg>?WqYpFmx5fXR$*D4yLc!J3%NIiKQi|)hww{QJQM1E^#qeMgmSe{$I`sSTp zYt8p7)C>bsC*+ARibg#S&`yQ<+)V)ngS}zoOeL)eZYSmnxdwJsMPt=8n6!d ziQXNLk%|$6kvbvs(8`5=73meQ-1bzp5eyPByc0q+4hI3%CnPgN1kTSSV9-t+-6B#) z!+XJt0d%GwXnbI#-blTWdPu!63W}=HNgZh-h#07md!dZRX15}011yPWC>kL&_j}{#CL>Q^ zx$W5L2j<%z0VEUZ2oXcn0309_1_Uz%L&Wd`sGzZ-a(zCbS4DPXWLPU(T|L8b6DC1$ z#HjR&NbZHXmIn<41)2C|teP9IbKW(^^C(V?JT;zQPMFQXJ7cBecwr^MEE4tXtCbiR zW<8@2C`07ZO2RYqo;n1K@qeR$PGe6QP*pmaaDBZ)QAc+Bky?#QS&9=w5Q-|&N`zsh z%(pzVxucUgvP9^YN)eRn>n&cpe2%+2B_BR27*w&5fHA{BbI2P8rY5YTQU&Q7gSsWKc%V&3f8t%J(nNvbK=Yuar;sE6Td& zU=)bfoQ^6QiJK6A48Vj$1+$$5S$qhCaJ2Z7WJy&}*`lBXp&99Ep4$uOXk3#+p!BEma2_fdn3 zi#hM#9}x9v114Vh@i&(E&-RdErKrJHx8lxjLE?mKt8HGr)Zxx<$)FN2eM$n^>(yXz z^+Fp{<-y^IqE==y$3H&lvDiuY*>}$In;VZ#<#s3V`nNA|r>)l2n9FD%9a=54fPp=0S<7a`}9lJ39s4VW6EkqAGj+nh!Py{NTzwf4SYA z0{HoNFYx~TK8Z6f%%y~Am>ILVDG3CbXLtcJ%hM81|QTgdB=eW05aC4)_3oALP!0KGWQYRsILa(S9 zjv|Ipi~_1k=Ab_erx=jt6hz`!-p}?6wz`f>D=n7V2|<-Sb?o(P-hOwRAANn1&vr{j zG4kh+4p^N_S!gF*T+Z=MxVv*i9gPr;s7j2+Oy<}h)_nZ9;6xL2oC!lzQ~?uEW2DBN z?E!h>_~ErB65o`YPj^bLTxhdCpYg|k@AJyDvuyV&e*e!$TwU#qeIcwZWJERUsAC{T z2+F96j8EYy5BL!EI713e+7QURqYjFi(8)dDyRyJ*R~Gs8pEkJts83lRTk?bZz3DuE zae;Q$6rU;>qguK9ctBL6DFygbr=SiIK|lo@mqI(Ws*G_^6_+lxTK#CJa$=((Kw0d{duz+^e{`am3~GZPLW?(rSmB z8%H>S{Xs?4vzywoii|=ega$xRB}&o?DZ>(DxMV^fbOuStTIAWs;F!o6m&9AcQ^%bDib=55k>HhCb(+=$b>92%Bp5- zzvN(8vwbuo#xDRchz!aMQ{!QGM8Bx%4J(4exJx_*V4Pwb+MN@eHEtGZvfWZA;ls@y z-6HVG&VbZA!WRGtz~=rC?;MW~3!;He4fwPKqES(rpiYm2iwJYAguUK~?QY4vogr#a z^7+9h~9>}{MQztsqIbd zX0m-aBJo1~1)6^z15+R{eR)l8u{gTcI(O-rY-TY}osfIiT$zyi=5?%- zIc9RtZ0?zBCCs%vvn|g|?&)NXcIIg%jy!4Fv@{V?Y1TSvd=(GxA9de-`)~FBXTJk> z#mo?qS>QVGDsb^j@%-h-Ch#6`%gp*RsZ1i0(!8zYUon6YFf_9o;J>erB$V=cL74ym N002ovPDHLkV1gH5qvikr literal 0 HcmV?d00001 diff --git a/src/data/icons/preferences/applications-system.png b/src/data/icons/preferences/applications-system.png new file mode 100644 index 0000000000000000000000000000000000000000..a40ffab1b203314a3b1cf77ad33c4d1e21742feb GIT binary patch literal 2073 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 literal 0 HcmV?d00001 diff --git a/src/data/icons/preferences/contact-new.png b/src/data/icons/preferences/contact-new.png new file mode 100644 index 0000000000000000000000000000000000000000..a61c598c5b4508231cb92f6ea7190cab3c389002 GIT binary patch literal 1645 zcmV-z29o)SP)3Ih2C@;xNEXV#2$O(}$OI?tkEA=9Wco*S zb=Rw@diNd|uiopb>aIzT2p)L6N8fwS`QGpOo^z{3Rr!CKD?UWT0x@8Z*yjNQ$VZ5M zl#c=#&{x$^0uYhpcQ@bq(ZbWqul9nCgxpPl(C_U0;CtWt>Q8_5`t6U9obP|yEZa~vh9+yPU zV?;1Ua3+7BT$p`u_A@UfePi}r#T*Mj=FR@4OPAt@n|lvxi6I0f3rYw%2#-sE1>k|p z$)*|`2$5h1$(=JeE1Wp<+3z~Sg@CIo#}lknKXJ-%;m1G5IgI3>rF2)UIY3!5;4D}pG^!Er?K?VT+yTznZ7K|~N0P{B|#I>La7T~41fu;2CQ zK(P-WZv!wt_wl?va{*yZ9_9S`^VA!4s?{24+M!x~9)L9MU?pcP09X*DWaLl@+<+D# z(DzEO7ueis^X|VM0pQipoS7ny4TONP`M~_cjo;%UhcSjYit)h{M=_nW!;3F|dqA7w zj3TOoQXEkND}z63z-LOg=h^FKJlg4?3bV~V*7cdHIaERN190);ONYudY^sV8P!uBy zQYJ4M7!Y;f&%Iy*VoCij3i0e+uLJyc9vGF zMZI1pO;e_)r)e}AM`1f?1;B(5P*w6b^m?9KfBQG>{Vr*@$J>9u52_duLO2koh)}6i zh~t>)>1lR%cQM9bttCkkWKeKbEuVq^hz6v%mO}^_G2Hy~J3M&2QzrV$JNJ0B+2+DG zo}&N@u(q~FmSrq0EzxSVu-4M=_gP$AEcXn*i>F||1Teyl5#spptsF-NF@pEL47#$i zg0+@zx68?sCkY|2wY7!!9%IaS(nBRUk^w80Z$AGle|+m*9`Eb{Fgw%WD=VME3L^~M zyLXRHr-QW?RW0j%>((t+R#p&^5fEbx9JJvu!bZg*;`#cQ&KzQ)p4brp%gf7$_5hqZ zb?Ol9N3l`1;V=UW^$yf0I}Zog-Q67#<+zOrk9xgc_>WG)BB-?%V+_Wae1uN8eTw~4 zQ&SU@j{^uH4C`lf5>8O+6K!K-gKD*ks#2@f=ytp1+SJt4At6pG!H5V8;S_)-u>g>! zDaIJQ_heZ{nxNI`&z{8?L#eZ_( zE-tdPw8Z@UJp23ms4Ca4UE}iQ%S8@53}9l?;lxLQD11T)W$UVHc_$3VDi|1v2}HmP zH*Vbc#q#p<%y6Rdgde7bVH+3i_@%VAwzhNb+_{&5*Dd+a6W40Bo;iK`^g^@QOn~w< zb?y1 ryCeT^C1z|c{10rnCLU<3Y6kEhxn4+6u63D300000NkvXXu0mjfoJtNH literal 0 HcmV?d00001 diff --git a/src/data/icons/preferences/extension.png b/src/data/icons/preferences/extension.png new file mode 100644 index 0000000000000000000000000000000000000000..369a16712079714e91af6c8e6b1b282fcadd48b5 GIT binary patch literal 1387 zcmV-x1(f=UP)!|T#Sow*S{oPE zq;b)mE4$Ez{tq>FVca$`u8q2@u8h0JNK`f&tTb(-RtC^ope?lw1I)Z%_ubvpku@BKnk>U4w`Sz_EdWfp_Eac#l%*P>!nADlgT4 zfN4b|0HDy@H+&huS48x{03ZPz&t|hdTI<8RVUQmH6u~4!EcK`tz;OVN!h9!~8Bq$d zyE~t77IKd0eiQ=$9RNfCiGywZAOL^_0Boy=1P&|dbXuIre&OkvHN zuIlE`GG16Zb`Sxz69F7K0&8n)SfxMX3HF6q{JIlu-@^73d=(sMtAd>fG!a=+1sZ4- zD@pkB`;S(c8MkI99bt+V9R^y8kZQG>qqSzw^Vst|JwHFMhKGkcIyyQ405@*js3j5! zq*5uVl;Uc&sw|xkHufwyjp@bDagpD{uS?%}-V+*V2(AeB*ai7~KGv9m!NEaXyLPQ| z_UzeC0B~JbjgOCK!g(UXy@$UCKYE{Ry4o`3Ezfn|CT2!1eNsGjH^%}6xHa+(C*}aP z{dWN~!*$&t2m%Np;5g2%`9gQU^^)`9Mk z_3OwVJ89%PkDEdW^W%5gw z%nZXY>C}nQc&7H8r&^ zrKI)sbu~IVnrhXF#bQD!#mr1cARrqF>{?xae}8gtaBy>y2z#y6G)*ioFZ-EHrfp?q zMccN`xm-?K)}d}GyE|c{Xsw}?f>H{;@9)_c48uUVT&_({POch;5fqC>IXykS8lH~( zNr!S3qnGE1dW z!m=#P&d$1HV`Jta{527Xj7p`V6N!XAfBt;N_kAocFS~c|-c=(bBXKF^q5b@85gvra z#YMMND#cyb1ps8TSu2;zSrHDkrbZMw1+`jDhs72mqK~F4S!4%}^@{?R?>^LNhu zC&n1EgdJ2wL;?9|UrHDP17pl6Bp@Qr;o;%CN~Q8)dwaVCu(PxCATPVSyIfpcP_Nfn zUtj0h{yuM)mpLw%yLw~ey@-4?#`FacpmcI_^38SKCyhn}fKsXSAOf9EhibJNwnsmH zAk8K(wzly5{hw=VYp;NYaHB%y{QUguU@%BDc6oUTfYurj!M1I#udneu58wAO#xNd_ z>2|vmi$%ucG4u2D%*@Or+RNwjEG{m-2EGUJp4D2TwN9Ln%jGydJ;ilh=H}*THk$xA zjzhg(XJKIh-}lj4)9dvx#$ekvN~uJ9A_6cAk%YUCfNk5XuC5ZS7|-*Kmi$zMzmTjHa9m@C2(+Xfa|(&^W#{z z{T9TfNedAlR+NAkVS)2(m0zkGQ$n-1_vI~JkUq&dUI668?)vHqK zze}kBdbL`uRW6r{cfAcB3y$MZtJPXS4>0PMfN8he&zF{#N}lIAmSqtXrBwJ;N`?1* z+qPpR7>~}eEDI5d)vMKNXK!!s^V!+iC*W6b2DrdW;0^FJRSBRDd;yM)F~7qtSVVH% kyjGl)3G@Nq7^4CH0LAp{?U9h@^#A|>07*qoM6N<$g4ldUDF6Tf literal 0 HcmV?d00001 diff --git a/src/data/icons/preferences/history_entry.png b/src/data/icons/preferences/history_entry.png new file mode 100644 index 0000000000000000000000000000000000000000..8e5831ab5362ad833999da9e333b6f51f03e3f1c GIT binary patch literal 1924 zcmV-~2YdL5P)Yyu_k z;3x3P3&YL>WSwPJ+4&`sRggst!$LN)4cQ_C5Kc(ZvMgRL%A*<0Om|n+$)daGLOb3W zX`sRGVNIRC{{Q^voDo&!|9QprgNQ_cUH7Pa952~8QA|k;0=H}M>SC^Mb6h#DuI5LO`v)PQR zt1HefF4^1LV|RC#ot+(?ot^RdC!esfxyhY3-=sSj;Jqi$bN=z+hcs=1F@`M52q7>Y zk6GW?Ab4*#H@Dsg{v;yj28e*$0*R_Joz8Hsz*&Z6Gq0E-mjps!Frj zU^E)xL*V6PLc7ypduxkMr^CU)0RZP;f6bG3-(|1c#qRI(_jlgG7X`)`j*pL7>-X8b zb&C)Jd7hIy2SB^oL{;(L1GE5Y0E7@qbRul5uk-x*cXT=(`u#q}7+TFH|GamP;c&>| z@iBgPmo$!%IHoX$hX)5lQN;1l5!2}uF@|opix@*bo3XXIiHM-8gb?N#X#fFCZAVqb zhrrJE7DwIfsal zT1&UrV>o=la*R55&4Bn2=E*WxTSG($-eZixT8oI#>2$a_KSxA}_V@Y4#sW2q3IO6b29)dTYeES4k^xzkz-UE_s&aOIPGwJwF@z8>#?WXq*njv1 zFJHdIK72?c1X_R|9`fSF3r>z7F`G{D&dud?&Ov3>C@xD(70WCGkU9t2?KZ&&uAe_A zj^lZ5SZgsN6h*<|!2#Q6XB2r(!+Sc{*Zk|V&xqp)t=88PDDds|HEV+b-A-pY2UZv$ zR1$;`=ycjlCKFzaMg&z%6j2xKe!tJXzy5_(ggno&-jf2KfBZ3mP`asPvM35hqcP1! zlWwQ85@R_D0Ze_;`tEhRTwGi*olemZ@ZOWADM=i2`rQz*mORUdyhn?ISrU`w8O}LE z@PIHJ4k=tgzt>&aJN%dg^$pPP^|-paVmg}F*^a}H}Q%~p%Qr71<0p{jiI`s<*9 zqA1GZljWtG25SUmv1hgK54&I)VRhHeSYKP?UfLhy)?bv$>XIZGFI_lM|fxL{UUm9SjAN>#Y=9#uWz(W6I1am-bJjhC;yLN=Z9)mM+{cDqEztV+3j z@`{aR2I}c2Di>CWjj00H)PYF3Ns=Tu=Quw*qqng^r{Ck>|2d-7ZebIP_pS;Gp!4pR zWf`F=P*D^?IR?xD$ju~_@7Yoap?pr$l+k!hmS;SB_KY|(G#iaF;X=SU$7C`on;8(t z2{ujf&aKeDQVG5HD;m{J!>{PKSYwlfI7v9axS-W)t!V6=qt{;}u@*rP6jv1U7=8g_ z&VadLksH|yBFytbQ6`fKCXVT}+k}el`g>vw-W3GzD<_y0HkWO<06-leAR=Wu{+fdVt{ zy;s$!&VjH5XxXZ@h3fw`l7!fpxxed%wfvp>y*|A!H<dz?XoN zYIy7bzXW~-yuwd=eFJ<7?5pZn>cS%;3FWVq^d|<80TWd%0R9Ucd+dO^StkGh0000< KMNUMnLSTZe5SFF@ literal 0 HcmV?d00001 diff --git a/src/data/icons/preferences/preferences-desktop.png b/src/data/icons/preferences/preferences-desktop.png new file mode 100644 index 0000000000000000000000000000000000000000..218b3465276c8aae53a471f4baf9d3ae14480921 GIT binary patch literal 1582 zcmV+}2GRM6P)S5eS%#3lIWM+D*yQ^w1Ox0Fr zvU`rO%f3*|bl1Q4eV*rit6CUi_lqS z#Bn?i5Cj4K?{76@jBEDUnh*jhWp8V1>#stHzZqks00eO6#fuj&%jI$_3`2}D2qDl~ zyNSL9K~UJhZ$b$7Yx{)|1VMmO3avF#%HEYLS1tn|BOnA8n$2cQN=csQ04Sx{+}z~s z*|W^e%{hl^wHl2^!#VeBX!uTl`SOL)Xhf}6n*$brp5;4El7uu(9pp)p@bKY7?%uu2 z%*+fz2u7n3tyYVEzyD2?Sc~JB?d@&%2;c|~g22HhgdmP%R#sM+pPw%*mgl*Hdv|x2 zjg1Y`G@YU%O;a{DHt2S{7-P^{lVw?f9_xSrf-K9(vJ9mZ+uPfuY09;0*U(z?>C-3j zJSWRCwAOBu!C=7J+SeML! z-oAZHk|azf6PL?=ls?S~QAH85S28xqSID zQ50dtdX#7XU8mC_NfMM&3U&5L~!$fph21vA({}&6_uwot;G~ z)bgc>esk3t}`HQLR=B z2nZXokAN>Qtu;ajgbBykY$?Ch|-ybQo(G9e5@YPA|lDbh4`2t`rk?<0=Y zfQ{V3d#GAhpe!1tQmMeZwKPpxUtf1R=yW<#VG-@aD}MmX?;dbLUQB^TQy*f(HDPYIk>+#l=M@9vhvM(&-@2b1q%F z1i-<;0h^nfg;Z=dn^db+PM<#QkhHow?kp(WTuz)g;WXe=;op+7EF+F%!Z2h!9wVik zD#v!dU!-gh)?6CqdG16PhM|MmMtf8PPoF;J_3PKy?V7EG{55AzI~eyA3pHx*)wk4x<#YWI2HlxfJNC$<$?n)%?5)3 z^?Kb^GbyF31wH~o2xezzsnu#Un@yJ~{^mBDdK-l^QG--ohP zDzUn{I&~P?wz|4XsZ=UNo~G$k8VVq=@Y(^xUw+}wfBFZ*_wU_xEYEY&GJ=wXo^%z|YQO-K!C)|GwOXgnpFfY*n&EJWD3=kVksEC9i7#KiJod}SYU8-g zjWN{gb>cXt*Xs>{31A|=eyjcb=+UDeZrr%>$9B72wfXB`)9q%}YL#lWTIjc%*fIWv z#dpjY!^e*w51u@E@&oWQkc-2CRp9r)AAqypgzdN6Ht-|xlQCu}yc_NbzJ9Hg{=?+| ghDl&-jL8803G?5^;Ip_xTL1t607*qoM6N<$f_07k9smFU literal 0 HcmV?d00001 diff --git a/src/data/icons/preferences/stock_inbox.png b/src/data/icons/preferences/stock_inbox.png new file mode 100644 index 0000000000000000000000000000000000000000..2ce02e932cb0d7814f6ea81ce26b124ad43656d9 GIT binary patch literal 1297 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 gEsetupUi(this); + ui->button->setPixmap( +#ifdef Q_WS_X11 + style()->standardIcon(QStyle::SP_BrowserStop).pixmap(20,20) +#else + QIcon(":/icons/faenza/stop.png").pixmap(20,20) +#endif + ); + ui->fileName->setText(m_fileName); + ui->downloadInfo->setText(tr("Remaining time unavailable")); + ui->fileIcon->setPixmap(fileIcon); + + setContextMenuPolicy(Qt::CustomContextMenu); + connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(customContextMenuRequested(QPoint))); + connect(m_reply, SIGNAL(finished()), this, SLOT(finished())); + connect(m_reply, SIGNAL(readyRead()), this, SLOT(readyRead())); + connect(m_reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(downloadProgress(qint64, qint64))); + connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(error(QNetworkReply::NetworkError))); + connect(ui->button, SIGNAL(clicked(QPoint)), this, SLOT(stop())); + + m_downloading = true; + m_downTimer.start(); + m_timer.start(1000*1, this); + readyRead(); + QTimer::singleShot(500, this, SLOT(updateDownload())); + + if (m_reply->error() != QNetworkReply::NoError) { + stop(); + error(m_reply->error()); + } + show(); +} + +void DownloadItem::finished() +{ + m_timer.stop(); + ui->downloadInfo->setText(tr("Done - %1").arg(m_reply->url().host())); + ui->progressBar->hide(); + ui->button->hide(); + ui->frame->hide(); + m_outputFile.close(); + + m_item->setSizeHint(sizeHint()); +#if QT_VERSION == 0x040700 // Workaround + ui->button->show(); + ui->button->hide(); +#endif + m_downloading = false; +} + +void DownloadItem::downloadProgress(qint64 received, qint64 total) +{ + qint64 currentValue = 0; + qint64 totalValue = 0; + if (total > 0) { + currentValue = received * 100 / total; + totalValue = 100; + } + ui->progressBar->setValue(currentValue); + ui->progressBar->setMaximum(totalValue); + m_currSpeed = received * 1000.0 / m_downTimer.elapsed(); + m_received = received; + m_total = total; +} + +void DownloadItem::timerEvent(QTimerEvent *event) +{ + if (event->timerId() == m_timer.timerId()) { + updateDownloadInfo(m_currSpeed, m_received, m_total); + } else + QWidget::timerEvent(event); +} + +int DownloadItem::progress() +{ + return ui->progressBar->value(); +} + +bool DownloadItem::isCancelled() +{ + return ui->downloadInfo->text().startsWith(tr("Cancelled")); +} + +QString DownloadItem::remaingTimeToString(QTime time) +{ + if (time1000) { + speed/=1024; + return QString::number(speed, 'g', 3)+" MB/s"; + }else + return QString::number(speed, 'g', 3)+" kB/s"; +} + +QString DownloadItem::fileSizeToString(int size) +{ + size/=1024; + if (size>1000) { + size/=1024; + return QString::number(size)+" MB"; + }else + return QString::number(size)+" kB"; +} + +void DownloadItem::updateDownloadInfo(double currSpeed, qint64 received, qint64 total) +{ + // QString QString QString QString + // | m_remTime | |m_currSize| |m_fileSize| |m_speed| + // Remaining 26 minutes - 339MB of 693 MB (350kB/s) + + int estimatedTime=((total-received)/1024) / (currSpeed/1024); + QString speed = currentSpeedToString(currSpeed); + // We have QString speed now + + QTime time; + time = time.addSecs(estimatedTime); + QString remTime = remaingTimeToString(time); + m_remTime = time; + + QString currSize = fileSizeToString(received); + QString fileSize = fileSizeToString(total); + + ui->downloadInfo->setText(tr("Remaining %1 - %2 of %3 (%4)").arg(remTime, currSize, fileSize, speed)); +} + +void DownloadItem::stop() +{ + m_timer.stop(); + m_reply->abort(); + QString outputfile = QFileInfo(m_outputFile).absoluteFilePath(); + m_outputFile.close(); + ui->downloadInfo->setText(tr("Cancelled - %1").arg(m_reply->url().host())); + ui->progressBar->hide(); + ui->button->hide(); + m_item->setSizeHint(sizeHint()); + +#if QT_VERSION == 0x040700 // Workaround + ui->button->show(); + ui->button->hide(); +#endif + + QFile::remove(outputfile); + m_downloading = false; +} + +void DownloadItem::mouseDoubleClickEvent(QMouseEvent *e) +{ + openFile(); + e->accept(); +} + +void DownloadItem::customContextMenuRequested(QPoint pos) +{ + QMenu menu; + menu.addAction(QIcon::fromTheme("document-open"), tr("Open File"), this, SLOT(openFile())); + + menu.addAction(tr("Open Folder"), this, SLOT(openFolder())); + menu.addSeparator(); + menu.addAction( +#ifdef Q_WS_X11 + style()->standardIcon(QStyle::SP_BrowserStop) +#else + QIcon(":/icons/faenza/stop.png") +#endif + ,tr("Cancel downloading"), this, SLOT(stop())); + menu.addAction(QIcon::fromTheme("window-close"), tr("Clear"), this, SLOT(clear())); + + if (m_downloading || ui->downloadInfo->text().startsWith(tr("Cancelled")) || ui->downloadInfo->text().startsWith(tr("Error"))) + menu.actions().at(0)->setEnabled(false); + if (!m_downloading) + menu.actions().at(3)->setEnabled(false); + if (m_downloading) + menu.actions().at(4)->setEnabled(false); + menu.exec(mapToGlobal(pos)); +} + +void DownloadItem::clear() +{ + emit deleteItem(this); +} + +void DownloadItem::openFile() +{ + if (m_downloading) + return; + QFileInfo info(m_path+m_fileName); + if (info.exists()) + QDesktopServices::openUrl(QUrl::fromLocalFile(info.absoluteFilePath())); + else + QMessageBox::warning(m_item->listWidget(), tr("Not found"), tr("Sorry, the file \n %1 \n is not found!").arg(info.absoluteFilePath())); +} + +void DownloadItem::openFolder() +{ + QDesktopServices::openUrl(QUrl::fromLocalFile(m_path)); +} + +void DownloadItem::readyRead() +{ + if (!m_outputFile.isOpen() && !m_outputFile.open(QIODevice::WriteOnly)) { + stop(); + ui->downloadInfo->setText(tr("Error: Cannot write to file!")); + return; + } + m_outputFile.write(m_reply->readAll()); +} + +void DownloadItem::error(QNetworkReply::NetworkError error) +{ + if (error != QNetworkReply::NoError) + ui->downloadInfo->setText(tr("Error: ")+m_reply->errorString()); +} + +void DownloadItem::updateDownload() +{ + if (ui->progressBar->maximum() == 0 && m_outputFile.isOpen() && m_reply->isFinished()) { + downloadProgress(0,0); + finished(); + } +} + +DownloadItem::~DownloadItem() +{ + delete ui; + delete m_item; +} diff --git a/src/downloads/downloaditem.h b/src/downloads/downloaditem.h new file mode 100644 index 000000000..b304d422f --- /dev/null +++ b/src/downloads/downloaditem.h @@ -0,0 +1,76 @@ +#ifndef DOWNLOADITEM_H +#define DOWNLOADITEM_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Ui { + class DownloadItem; +} + +class DownloadItem : public QWidget +{ + Q_OBJECT + +public: + explicit DownloadItem(QListWidgetItem* item, QNetworkReply* reply ,QString path, QString fileName, QPixmap fileIcon, QWidget *parent = 0); + bool isDownloading() { return m_downloading; } + bool isCancelled(); + QTime remainingTime() { return m_remTime; } + double currentSpeed() { return m_currSpeed; } + int progress(); + ~DownloadItem(); + + static QString remaingTimeToString(QTime time); + static QString currentSpeedToString(double speed); + static QString fileSizeToString(int size); + +signals: + void deleteItem(DownloadItem*); + +private slots: + void finished(); + void downloadProgress(qint64 received, qint64 total); + void stop(); + void openFile(); + void openFolder(); + void readyRead(); + void error(QNetworkReply::NetworkError); + void updateDownload(); + void customContextMenuRequested(QPoint pos); + void clear(); + +private: + void timerEvent(QTimerEvent *event); + void updateDownloadInfo(double currSpeed, qint64 received, qint64 total); + void mouseDoubleClickEvent(QMouseEvent *e); + Ui::DownloadItem *ui; + + QListWidgetItem* m_item; + QNetworkReply* m_reply; + QString m_path; + QString m_fileName; + QTime m_downTimer; + QTime m_remTime; + QBasicTimer m_timer; + QFile m_outputFile; + + bool m_downloading; + double m_currSpeed; + qint64 m_received; + qint64 m_total; +}; + +#endif // DOWNLOADITEM_H diff --git a/src/downloads/downloaditem.ui b/src/downloads/downloaditem.ui new file mode 100644 index 000000000..2e31f7b7c --- /dev/null +++ b/src/downloads/downloaditem.ui @@ -0,0 +1,113 @@ + + + DownloadItem + + + + 0 + 0 + 480 + 73 + + + + Form + + + + 2 + + + 2 + + + + + + 30 + 50 + + + + + + + Qt::AlignCenter + + + + + + + font-size: 13pt; + + + A Clockwork Orange.avi + + + + + + + Remaining 26 minutes - 339MB of 693 MB (350kB/s) + + + + + + + + 16777215 + 20 + + + + + 0 + + + + + margin-bottom: 1px; + + + 0 + + + 0 + + + false + + + + + + + + + + + + + + + + + + + + + SqueezeLabel + QLabel +
    squeezelabel.h
    +
    + + ClickableLabel + QLabel +
    clickablelabel.h
    +
    +
    + + +
    diff --git a/src/downloads/downloadmanager.cpp b/src/downloads/downloadmanager.cpp new file mode 100644 index 000000000..d18374264 --- /dev/null +++ b/src/downloads/downloadmanager.cpp @@ -0,0 +1,247 @@ +#include "downloadmanager.h" +#include "ui_downloadmanager.h" +#include "qupzilla.h" +#include "downloadoptionsdialog.h" +#include "downloaditem.h" +#include "ecwin7.h" + +DownloadManager::DownloadManager(QWidget *parent) : + QWidget(parent) + ,ui(new Ui::DownloadManager) + ,m_isClosing(false) +{ + ui->setupUi(this); + ui->clearButton->setIcon(QIcon::fromTheme("edit-clear")); + //CENTER on screen + const QRect screen = QApplication::desktop()->screenGeometry(); + const QRect &size = QWidget::geometry(); + QWidget::move( (screen.width()-size.width())/2, (screen.height()-size.height())/2 ); + + m_iconProvider = new QFileIconProvider(); + m_networkManager = new QNetworkAccessManager(); + + QSettings settings(MainApplication::getInstance()->getActiveProfil()+"settings.ini", QSettings::IniFormat); + settings.beginGroup("DownloadManager"); + m_downloadPath = settings.value("defaultDownloadPath", QDir::homePath()).toString(); + m_lastDownloadPath = settings.value("lastDownloadPath","").toString(); + settings.endGroup(); + + connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clearList())); + +#ifdef Q_WS_WIN + win7.init(this->winId()); +#endif +} + +#ifdef Q_WS_WIN +bool DownloadManager::winEvent(MSG *message, long *result) +{ + return win7.winEvent(message, result); +} +#endif + +void DownloadManager::timerEvent(QTimerEvent *event) +{ + QList remTimes; + QList progresses; + QList speeds; + + if (event->timerId() == m_timer.timerId()) { + if (!ui->list->count()) { + ui->speedLabel->clear(); + setWindowTitle(tr("Download Manager")); + return; + } + for (int i = 0; i < ui->list->count(); i++) { + DownloadItem* downItem = qobject_cast(ui->list->itemWidget(ui->list->item(i))); + if (!downItem || (downItem && downItem->isCancelled())) + continue; + if (!downItem->isDownloading()) { + progresses.append(100); + continue; + } else + progresses.append(downItem->progress()); + remTimes.append(downItem->remainingTime()); + speeds.append(downItem->currentSpeed()); + } + if (remTimes.isEmpty()) { + ui->speedLabel->clear(); + setWindowTitle(tr("Download Manager")); + return; + } + + QTime remaining; + foreach (QTime time, remTimes) { + if (time > remaining) + remaining = time; + } + + int progress = 0; + foreach (int prog, progresses) + progress+=prog; + progress = progress / progresses.count(); + + double speed = 0.00; + foreach (double spee, speeds) + speed+=spee; + + ui->speedLabel->setText(tr("%1% of %2 files (%3) %4 remaining").arg(QString::number(progress),QString::number(progresses.count()), + DownloadItem::currentSpeedToString(speed), + DownloadItem::remaingTimeToString(remaining))); + setWindowTitle(QString::number(progress) + tr("% - Download Manager")); +#ifdef Q_WS_WIN + win7.setProgressValue(progress, 100); + win7.setProgressState(win7.Normal); +#endif + } else + QWidget::timerEvent(event); +} + +void DownloadManager::clearList() +{ + QList items; + for (int i = 0; i < ui->list->count(); i++) { + DownloadItem* downItem = qobject_cast(ui->list->itemWidget(ui->list->item(i))); + if (!downItem) + continue; + if (downItem->isDownloading()) + continue; + items.append(downItem); + } + qDeleteAll(items); +} + +void DownloadManager::download(const QNetworkRequest &request) +{ + handleUnsupportedContent(m_networkManager->get(request)); +} + +void DownloadManager::handleUnsupportedContent(QNetworkReply *reply) +{ +// DownloadOptionsDialog* dialog = new DownloadOptionsDialog(); +// dialog->show(); +// dialog->setAttribute(Qt::WA_DeleteOnClose); + + QString path; + QString fileName; + + QString userFileName; + + QString _fileName = getFileName(reply); + + if (m_downloadPath.isEmpty()) + userFileName = QFileDialog::getSaveFileName(MainApplication::getInstance()->getWindow(), tr("Save file as..."),m_lastDownloadPath+_fileName); + else + userFileName = m_downloadPath+_fileName; + + if (userFileName.isEmpty()) { + reply->abort(); + return; + } + + int pos = userFileName.lastIndexOf("/"); + if (pos!=-1) { + int size = userFileName.size(); + path = userFileName.left(pos+1); + fileName = userFileName.right(size-pos-1); + } + + m_lastDownloadPath = path; + QSettings settings(MainApplication::getInstance()->getActiveProfil()+"settings.ini", QSettings::IniFormat); + settings.beginGroup("DownloadManager"); + settings.setValue("lastDownloadPath",m_lastDownloadPath); + settings.endGroup(); + + QFileInfo info(reply->url().toString()); + QTemporaryFile tempFile("XXXXXX."+info.suffix()); + tempFile.open(); + QFileInfo tempInfo(tempFile.fileName()); + QPixmap fileIcon = m_iconProvider->icon(tempInfo).pixmap(30,30); + + QListWidgetItem* item = new QListWidgetItem(ui->list); + DownloadItem* downItem = new DownloadItem(item, reply, path, fileName, fileIcon); + connect(downItem, SIGNAL(deleteItem(DownloadItem*)), this, SLOT(deleteItem(DownloadItem*))); + ui->list->setItemWidget(item, downItem); + item->setSizeHint(downItem->sizeHint()); + show(); + activateWindow(); +} + +void DownloadManager::deleteItem(DownloadItem *item) +{ + if (item && !item->isDownloading()) + delete item; +} + +QString DownloadManager::getFileName(QNetworkReply *reply) +{ + QString path; + if (reply->hasRawHeader("Content-Disposition")) { + QString value = reply->rawHeader("Content-Disposition"); + int pos = value.indexOf("filename="); + if (pos!=-1) { + QString name = value.mid(pos + 9); + if (name.startsWith('"') && name.endsWith('"')) + name = name.mid(1, name.size() - 2); + path = name; + } + } + if (path.isEmpty()) + path = reply->url().path(); + + QFileInfo info(path); + QString baseName = info.completeBaseName(); + QString endName = info.suffix(); + + if (baseName.isEmpty()) { + baseName = tr("NoNameDownload"); + } + + if (!endName.isEmpty()) + endName="."+endName; + + return baseName+endName; +} + +bool DownloadManager::canClose() +{ + if (m_isClosing) + return true; + + bool isDownloading = false; + for (int i = 0; i < ui->list->count(); i++) { + DownloadItem* downItem = qobject_cast(ui->list->itemWidget(ui->list->item(i))); + if (!downItem) + continue; + if (downItem->isDownloading()) { + isDownloading = true; + break; + } + } + + return !isDownloading; +} + +void DownloadManager::closeEvent(QCloseEvent *e) +{ + if (!MainApplication::getInstance()->getWindow()) { // No main windows -> we are going to quit + if (!canClose()){ + QMessageBox::StandardButton button = QMessageBox::warning(this, tr("Warning"), + tr("Are you sure to quit? All uncompleted downloads will be cancelled!"), QMessageBox::Yes | QMessageBox::No); + if (button != QMessageBox::Yes) { + e->ignore(); + return; + } + m_isClosing = true; + } + MainApplication::getInstance()->quitApplication(); + } + e->accept(); +} + +DownloadManager::~DownloadManager() +{ + delete ui; + delete m_networkManager; + delete m_iconProvider; +} diff --git a/src/downloads/downloadmanager.h b/src/downloads/downloadmanager.h new file mode 100644 index 000000000..761384dc5 --- /dev/null +++ b/src/downloads/downloadmanager.h @@ -0,0 +1,74 @@ +#ifndef DOWNLOADMANAGER_H +#define DOWNLOADMANAGER_H + +#if defined(QT_NO_DEBUG) & !defined(QT_NO_DEBUG_OUTPUT) +#define QT_NO_DEBUG_OUTPUT +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Ui { + class DownloadManager; +} + +class DownloadItem; +class EcWin7; +class DownloadManager : public QWidget +{ + Q_OBJECT + +public: + explicit DownloadManager(QWidget *parent = 0); + ~DownloadManager(); + + void download(const QNetworkRequest &request); + void handleUnsupportedContent(QNetworkReply* reply); + bool canClose(); + + void show() { m_timer.start(1000*2, this); QWidget::show(); } + +#ifdef Q_WS_WIN +protected: + virtual bool winEvent(MSG *message, long *result); +#endif + +private slots: + void clearList(); + void deleteItem(DownloadItem* item); + +private: +#ifdef Q_WS_WIN + EcWin7 win7; +#endif + void timerEvent(QTimerEvent *event); + QString getFileName(QNetworkReply* reply); + void closeEvent(QCloseEvent *e); + + Ui::DownloadManager *ui; + QNetworkAccessManager* m_networkManager; + QFileIconProvider* m_iconProvider; + + QString m_lastDownloadPath; + QString m_downloadPath; + QBasicTimer m_timer; + + bool m_isClosing; +}; + +#endif // DOWNLOADMANAGER_H diff --git a/src/downloads/downloadmanager.ui b/src/downloads/downloadmanager.ui new file mode 100644 index 000000000..db6575caa --- /dev/null +++ b/src/downloads/downloadmanager.ui @@ -0,0 +1,128 @@ + + + DownloadManager + + + + 0 + 0 + 529 + 315 + + + + Download Manager + + + + :/icons/qupzilla.png:/icons/qupzilla.png + + + + 0 + + + 0 + + + 0 + + + 0 + + + 4 + + + + + border: none; + + + true + + + + + + + Qt::Horizontal + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 2 + 20 + + + + + + + + + 0 + 0 + + + + Clear + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Minimum + + + + 40 + 20 + + + + + + + + + + + + + diff --git a/src/downloads/downloadoptionsdialog.cpp b/src/downloads/downloadoptionsdialog.cpp new file mode 100644 index 000000000..34d8fb3e0 --- /dev/null +++ b/src/downloads/downloadoptionsdialog.cpp @@ -0,0 +1,14 @@ +#include "downloadoptionsdialog.h" +#include "ui_downloadoptionsdialog.h" + +DownloadOptionsDialog::DownloadOptionsDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::DownloadOptionsDialog) +{ + ui->setupUi(this); +} + +DownloadOptionsDialog::~DownloadOptionsDialog() +{ + delete ui; +} diff --git a/src/downloads/downloadoptionsdialog.h b/src/downloads/downloadoptionsdialog.h new file mode 100644 index 000000000..a057261f9 --- /dev/null +++ b/src/downloads/downloadoptionsdialog.h @@ -0,0 +1,22 @@ +#ifndef DOWNLOADOPTIONSDIALOG_H +#define DOWNLOADOPTIONSDIALOG_H + +#include + +namespace Ui { + class DownloadOptionsDialog; +} + +class DownloadOptionsDialog : public QDialog +{ + Q_OBJECT + +public: + explicit DownloadOptionsDialog(QWidget *parent = 0); + ~DownloadOptionsDialog(); + +private: + Ui::DownloadOptionsDialog *ui; +}; + +#endif // DOWNLOADOPTIONSDIALOG_H diff --git a/src/downloads/downloadoptionsdialog.ui b/src/downloads/downloadoptionsdialog.ui new file mode 100644 index 000000000..3c82a2c58 --- /dev/null +++ b/src/downloads/downloadoptionsdialog.ui @@ -0,0 +1,205 @@ + + + DownloadOptionsDialog + + + + 0 + 0 + 416 + 279 + + + + Opening + + + + :/icons/qupzilla.png:/icons/qupzilla.png + + + + + + + + + 0 + 0 + + + + which is a: + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + from: + + + + + + + + + + + + + + + + You have chosen to open + + + + + + + <b>What should QupZilla do with this file?</b> + + + + + + + Open... + + + true + + + + + + + Save File + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + DownloadOptionsDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + DownloadOptionsDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/history/historymanager.cpp b/src/history/historymanager.cpp new file mode 100644 index 000000000..677919ab8 --- /dev/null +++ b/src/history/historymanager.cpp @@ -0,0 +1,192 @@ +#include "historymanager.h" +#include "ui_historymanager.h" +#include "qupzilla.h" +#include "locationbar.h" + +HistoryManager::HistoryManager(QupZilla* mainClass, QWidget *parent) : + QWidget(parent) + ,ui(new Ui::HistoryManager) + ,p_QupZilla(mainClass) +{ + ui->setupUi(this); + //CENTER on scren + const QRect screen = QApplication::desktop()->screenGeometry(); + const QRect &size = QWidget::geometry(); + QWidget::move( (screen.width()-size.width())/2, (screen.height()-size.height())/2 ); + + connect(ui->historyTree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)),this, SLOT(itemDoubleClicked(QTreeWidgetItem*))); + connect(ui->close, SIGNAL(clicked(QAbstractButton*)), this, SLOT(hide())); + connect(ui->deleteB, SIGNAL(clicked()), this, SLOT(deleteItem())); + connect(ui->clearAll, SIGNAL(clicked()), this, SLOT(clearHistory())); + connect(ui->search, SIGNAL(cursorPositionChanged(int, int)), this, SLOT(search())); + connect(ui->historyTree, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(contextMenuRequested(const QPoint &))); + connect(ui->historyTree, SIGNAL(itemControlClicked(QTreeWidgetItem*)), this, SLOT(itemControlClicked(QTreeWidgetItem*))); + + //QTimer::singleShot(0, this, SLOT(refreshTable())); + + ui->search->setInactiveText(tr("Search")); +} + +QupZilla* HistoryManager::getQupZilla() +{ + if (!p_QupZilla) + p_QupZilla = MainApplication::getInstance()->getWindow(); + return p_QupZilla; +} + +void HistoryManager::setMainWindow(QupZilla *window) +{ + if (window) + p_QupZilla = window; +} + +void HistoryManager::itemDoubleClicked(QTreeWidgetItem *item) +{ + if (!item || item->text(1).isEmpty()) + return; + getQupZilla()->loadAddress(QUrl(item->text(1))); +} + +void HistoryManager::itemControlClicked(QTreeWidgetItem *item) +{ + if (!item || item->text(1).isEmpty()) + return; + getQupZilla()->tabWidget()->addView(QUrl(item->text(1))); +} + +void HistoryManager::loadInNewTab() +{ + if (QAction *action = qobject_cast(sender())) + getQupZilla()->tabWidget()->addView(action->data().toUrl(), tr("New Tab"), TabWidget::NewNotSelectedTab); +} + +void HistoryManager::contextMenuRequested(const QPoint &position) +{ + if (!ui->historyTree->itemAt(position)) + return; + QString link = ui->historyTree->itemAt(position)->text(1); + if (link.isEmpty()) + return; + + QMenu menu; + menu.addAction(tr("Open link in actual tab"), getQupZilla(), SLOT(loadActionUrl()))->setData(link); + menu.addAction(tr("Open link in new tab"), this, SLOT(loadInNewTab()))->setData(link); + menu.addSeparator(); + + menu.addSeparator(); + menu.addAction(tr("Close"), this, SLOT(close())); + + //Prevent choosing first option with double rightclick + QPoint pos = QCursor::pos(); + QPoint p(pos.x(), pos.y()+1); + menu.exec(p); +} + +void HistoryManager::deleteItem() +{ + QTreeWidgetItem* item = ui->historyTree->currentItem(); + if (!item) + return; + if (item->text(1).isEmpty()) + return; + + QString id = item->whatsThis(1); + QSqlQuery query; + query.exec("DELETE FROM history WHERE id="+id); + delete item; +} + +void HistoryManager::clearHistory() +{ + QMessageBox::StandardButton button = QMessageBox::warning(this, tr("Confirmation"), + tr("Are you sure to delete all history?"), QMessageBox::Yes | QMessageBox::No); + if (button != QMessageBox::Yes) + return; + + QSqlQuery query; + query.exec("DELETE FROM history"); + ui->historyTree->clear(); + query.exec("VACUUM"); +} + +void HistoryManager::refreshTable() +{ + ui->historyTree->setUpdatesEnabled(false); + ui->historyTree->clear(); + + QLocale locale(getQupZilla()->activeLanguage().remove(".qm")); + + QSqlQuery query; + query.exec("SELECT title, url, id, date FROM history ORDER BY date DESC"); + + while(query.next()) { + QString title = query.value(0).toString(); + QUrl url = query.value(1).toUrl(); + int id = query.value(2).toInt(); + qint64 unixDate = query.value(3).toLongLong(); + QDateTime date = QDateTime(); + date = date.fromMSecsSinceEpoch(unixDate); + + QString localDate; //date.toString("dddd d. MMMM yyyy"); + //QString day = locale.dayName(date.toString("d").toInt()); + + QString month = locale.monthName(date.toString("M").toInt()); + localDate = date.toString(" d. ") + month + date.toString(" yyyy"); + + QTreeWidgetItem* item; + QList findParent = ui->historyTree->findItems(localDate, 0); + if (findParent.count() == 1) { + item = new QTreeWidgetItem(findParent.at(0)); + }else{ + QTreeWidgetItem* newParent = new QTreeWidgetItem(ui->historyTree); + newParent->setText(0, localDate); + newParent->setIcon(0, QIcon(":/icons/menu/history_entry.png")); + ui->historyTree->addTopLevelItem(newParent); + item = new QTreeWidgetItem(newParent); + } + + item->setText(0, title); + item->setText(1, url.toEncoded()); + item->setToolTip(0, title); + item->setToolTip(1, url.toEncoded()); + + item->setWhatsThis(1, QString::number(id)); + item->setIcon(0, LocationBar::icon(url)); + ui->historyTree->addTopLevelItem(item); + } + + ui->historyTree->setUpdatesEnabled(true); +} + +void HistoryManager::search() +{ + QString searchText = ui->search->text(); + if (searchText.isEmpty()) { + refreshTable(); + return; + } + + refreshTable(); + ui->historyTree->setUpdatesEnabled(false); + + QList items = ui->historyTree->findItems("*"+searchText+"*", Qt::MatchRecursive | Qt::MatchWildcard); + + QList foundItems; + foreach(QTreeWidgetItem* fitem, items) { + if (fitem->text(1).isEmpty()) + continue; + QTreeWidgetItem* item = new QTreeWidgetItem(); + item->setText(0, fitem->text(0)); + item->setText(1, fitem->text(1)); + item->setWhatsThis(1, fitem->whatsThis(1)); + foundItems.append(item); + } + ui->historyTree->clear(); + ui->historyTree->addTopLevelItems(foundItems); + ui->historyTree->setUpdatesEnabled(true); +} + +HistoryManager::~HistoryManager() +{ + delete ui; +} diff --git a/src/history/historymanager.h b/src/history/historymanager.h new file mode 100644 index 000000000..f38d84b7b --- /dev/null +++ b/src/history/historymanager.h @@ -0,0 +1,45 @@ +#ifndef HISTORYMANAGER_H +#define HISTORYMANAGER_H + +#if defined(QT_NO_DEBUG) & !defined(QT_NO_DEBUG_OUTPUT) +#define QT_NO_DEBUG_OUTPUT +#endif + +#include +#include +#include + +namespace Ui { + class HistoryManager; +} + +class QupZilla; +class HistoryManager : public QWidget +{ + Q_OBJECT + +public: + explicit HistoryManager(QupZilla* mainClass, QWidget *parent = 0); + ~HistoryManager(); + + void setMainWindow(QupZilla *window); + +public slots: + void refreshTable(); + +private slots: + void itemDoubleClicked(QTreeWidgetItem* item); + void deleteItem(); + void clearHistory(); + void search(); + void contextMenuRequested(const QPoint &position); + void loadInNewTab(); + void itemControlClicked(QTreeWidgetItem* item); + +private: + QupZilla* getQupZilla(); + Ui::HistoryManager *ui; + QPointer p_QupZilla; +}; + +#endif // HISTORYMANAGER_H diff --git a/src/history/historymanager.ui b/src/history/historymanager.ui new file mode 100644 index 000000000..0849ea07a --- /dev/null +++ b/src/history/historymanager.ui @@ -0,0 +1,96 @@ + + + HistoryManager + + + + 0 + 0 + 735 + 547 + + + + History + + + + :/icons/qupzilla.png:/icons/qupzilla.png + + + + + + Search in history: + + + + + + + + + + Qt::CustomContextMenu + + + true + + + 330 + + + + Title + + + + + Url + + + + + + + + Delete + + + Del + + + + + + + Clear All History + + + + + + + QDialogButtonBox::Close + + + + + + + + LineEdit + QLineEdit +
    lineedit.h
    +
    + + TreeWidget + QTreeWidget +
    treewidget.h
    +
    +
    + + + + +
    diff --git a/src/history/historymodel.cpp b/src/history/historymodel.cpp new file mode 100644 index 000000000..22669f570 --- /dev/null +++ b/src/history/historymodel.cpp @@ -0,0 +1,102 @@ +#include "historymodel.h" +#include "webview.h" +#include "qupzilla.h" + +HistoryModel::HistoryModel(QupZilla *mainClass, QObject *parent) + : QObject(parent) + ,m_isSaving(true) + ,p_QupZilla(mainClass) +{ + loadSettings(); +} + +void HistoryModel::loadSettings() +{ + QSettings settings(MainApplication::getInstance()->getActiveProfil()+"settings.ini", QSettings::IniFormat); + settings.beginGroup("Web-Browser-Settings"); + m_isSaving = settings.value("allowHistory",true).toBool(); +} + +int HistoryModel::addHistoryEntry(const QString &url, QString &title) +{ + if (!m_isSaving) + return -2; + if (url.contains("file://") || title.contains(tr("Failed loading page")) || url.isEmpty() || url.contains("about:blank") ) + return -1; + + QSqlQuery query; + query.prepare("SELECT id FROM history WHERE url=?"); + query.bindValue(0, url); + query.exec(); + if (!query.next()) { + QDateTime now = QDateTime::currentDateTime(); + if (title == "") + title=tr("No Named Page"); + query.prepare("INSERT INTO history (count, date, url, title) VALUES (1,?,?,?)"); + query.bindValue(0, now.toMSecsSinceEpoch()); + query.bindValue(1, url); + query.bindValue(2, title); + query.exec(); + }else{ + QDateTime now = QDateTime::currentDateTime(); + if (title == "") + title=tr("No Named Page"); + query.prepare("UPDATE history SET count = count + 1, date=? WHERE url=?"); + query.bindValue(0, now.toMSecsSinceEpoch()); + query.bindValue(1, url); + query.exec(); + } + return query.lastInsertId().toInt(); +} + +int HistoryModel::addHistoryEntry(WebView *view) +{ + if (!m_isSaving) + return -2; + QString url = view->url().toString(); + QString title = view->title(); + return addHistoryEntry(url, title); +} + +bool HistoryModel::deleteHistoryEntry(int index) +{ + QSqlQuery query; + query.prepare("DELETE FROM history WHERE id=?"); + query.bindValue(0, index); + if (query.exec()) + return true; + else return false; +} + +bool HistoryModel::deleteHistoryEntry(const QString &url, const QString &title) +{ + QSqlQuery query; + query.prepare("DELETE FROM history WHERE url=? AND title=?"); + query.bindValue(0, url); + query.bindValue(1, title); + if (query.exec()) + return true; + else return false; +} + +bool HistoryModel::optimizeHistory() +{ + QSqlQuery query; + return query.exec("VACUUM"); +} + +bool HistoryModel::clearHistory() +{ + QSqlQuery query; + return query.exec("DELETE FROM history"); +} + +void HistoryModel::setSaving(bool state) +{ + m_isSaving = state; +} + +bool HistoryModel::isSaving() +{ + return m_isSaving; +} diff --git a/src/history/historymodel.h b/src/history/historymodel.h new file mode 100644 index 000000000..ccee2780e --- /dev/null +++ b/src/history/historymodel.h @@ -0,0 +1,38 @@ +#ifndef HISTORYMODEL_H +#define HISTORYMODEL_H + +#if defined(QT_NO_DEBUG) & !defined(QT_NO_DEBUG_OUTPUT) +#define QT_NO_DEBUG_OUTPUT +#endif + +#include "QtSql/QSqlDatabase" +#include "QSqlQuery" +#include "QDateTime" +#include "QFile" + +class QupZilla; +class WebView; +class HistoryModel : public QObject +{ + Q_OBJECT +public: + HistoryModel(QupZilla* mainClass, QObject* parent = 0); + + int addHistoryEntry(WebView* view); + int addHistoryEntry(const QString &url, QString &title); + bool deleteHistoryEntry(int index); + bool deleteHistoryEntry(const QString &url, const QString &title); + + bool clearHistory(); + bool optimizeHistory(); + bool isSaving(); + void setSaving(bool state); + + void loadSettings(); + +private: + bool m_isSaving; + QupZilla* p_QupZilla; +}; + +#endif // HISTORYMODEL_H diff --git a/src/icon.ico b/src/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..acd93a8a34942eb780199d6f7db89e6f705e08c0 GIT binary patch literal 9662 zcmeHNX_r)0mbLywf3D@K?$S~r2a;q~ORLJRTFYA1U0sePiXtG2rBW)Of+8v)GJ_(M z%tysz(4WmA)0i(O8eOZKj)wP{UY7m`)7aG#?76LPtA_&o>p{ik1KU^*H*n!y_SCNle}-8I(Ki&T8iLjnt=j5%-dWEvbt}-@vKoD~Zr)?)T#G@Dm-C*c zRqR`Vn%kLZsmMWZvph2woxG=}Fq88xLwCb+j?0CvB^T{g%g|MygTeM(v{q!HzBmgV z9Bb_UeX6&4og!n~Dwd$7d)GgS zTBc&@sLfPyb=EFLQ^}*mvIGNdE72p^8`mn@>P(JXin_u_$$tiV8?)(i7P(!{vDxTr z≪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 RUj+UC@&5$_dt?&w{-5?6y+Hr~ literal 0 HcmV?d00001 diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 000000000..964edb6d9 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,25 @@ +#if defined(QT_NO_DEBUG) & !defined(QT_NO_DEBUG_OUTPUT) +#define QT_NO_DEBUG_OUTPUT +#endif + +#include +#include +#include +#include +#include "mainapplication.h" + +int main(int argc, char *argv[]) +{ + Q_INIT_RESOURCE(icons); + Q_INIT_RESOURCE(html); + + QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8")); +#ifdef Q_WS_X11 + QApplication::setGraphicsSystem("raster"); // Better overall performance on X11 +#endif + + MainApplication app(argc, argv); + if (app.isExited()) + return 1; + return app.exec(); +} diff --git a/src/navigation/locationbar.cpp b/src/navigation/locationbar.cpp new file mode 100644 index 000000000..a02e82e5f --- /dev/null +++ b/src/navigation/locationbar.cpp @@ -0,0 +1,375 @@ +#include "locationbar.h" +#include "qupzilla.h" +#include "webview.h" +#include "rssmanager.h" +#include "mainapplication.h" +#include "locationcompleter.h" +#include "clickablelabel.h" +#include "bookmarkswidget.h" +#include "bookmarksmodel.h" +#include "siteinfowidget.h" + +LocationBar::LocationBar(QupZilla* mainClass, QWidget *parent) + : LineEdit(parent) + ,m_selectAllOnDoubleClick(false) + ,m_addComWithCtrl(false) + ,m_addCountryWithAlt(false) + ,p_QupZilla(mainClass) + ,m_bookmarksModel(0) +{ + + m_siteIcon = new QToolButton(this); + m_siteIcon->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + m_siteIcon->setCursor(Qt::ArrowCursor); + m_siteIcon->setMaximumSize(35, 25); + m_siteIcon->setMinimumSize(35, 25); + m_siteIcon->setToolTip(tr("Show informations about this page")); + m_siteIcon->setStyleSheet("QToolButton{border-image: url(:/icons/locationbar/searchchoose.png); margin-left:2px;}"); + + m_rssIcon = new QToolButton(this); + m_rssIcon->setIcon(QIcon(":/icons/menu/rss.png")); + m_rssIcon->setCursor(Qt::ArrowCursor); + m_rssIcon->setMaximumSize(30, 23); + m_rssIcon->setAutoRaise(true); + m_rssIcon->setVisible(false); + m_rssIcon->setPopupMode(QToolButton::InstantPopup); + m_rssIcon->setToolTip(tr("Add RSS from this page...")); + m_rssIcon->setStyleSheet("margin-bottom:2px"); + m_rssMenu = new QMenu(this); + + m_goButton = new ClickableLabel(this); + m_goButton->setPixmap(QPixmap(":/icons/locationbar/gotoaddress.png")); + m_goButton->setCursor(Qt::PointingHandCursor); + m_goButton->setHidden(true); + m_goButton->setStyleSheet("margin-bottom:2px;"); + + m_bookmarkButton = new ClickableLabel(this); + m_bookmarkButton->setPixmap(QPixmap(":/icons/locationbar/starg.png")); + m_bookmarkButton->setCursor(Qt::PointingHandCursor); + m_bookmarkButton->setStyleSheet("margin-bottom: 2px;"); + m_bookmarkButton->setToolTip(tr("Bookmark this Page")); + + ClickableLabel* down = new ClickableLabel(this); + down->setPixmap(QPixmap(":icons/locationbar/arrow-down.gif")); + down->setCursor(Qt::ArrowCursor); + + addWidget(down, LineEdit::RightSide); + addWidget(m_bookmarkButton, LineEdit::RightSide); + addWidget(m_goButton, LineEdit::RightSide); + addWidget(m_rssIcon, LineEdit::RightSide); + + setPlaceholderText(tr("Enter URL address or search on Google.com")); + + setWidgetSpacing(0); + this->setMinimumHeight(25); + this->setMaximumHeight(25); + loadSettings(); + + m_locationCompleter = new LocationCompleter(); + setCompleter(m_locationCompleter); + + connect(this, SIGNAL(textEdited(QString)), this, SLOT(textEdit())); + connect(this, SIGNAL(textEdited(QString)), m_locationCompleter, SLOT(refreshCompleter(QString))); + connect(m_locationCompleter->popup(), SIGNAL(clicked(QModelIndex)), p_QupZilla, SLOT(urlEnter())); + connect(m_siteIcon, SIGNAL(clicked()), this, SLOT(showSiteInfo())); + connect(down, SIGNAL(clicked(QPoint)), this, SLOT(showPopup())); + connect(m_goButton, SIGNAL(clicked(QPoint)), p_QupZilla, SLOT(urlEnter())); + connect(m_bookmarkButton, SIGNAL(clicked(QPoint)), this, SLOT(bookmarkIconClicked())); + + setLeftMargin(33); + + setStyleSheet("QLineEdit { background: transparent; border-image: url(:/icons/locationbar/lineedit.png); border-width:4; color:black;}"); +} + +void LocationBar::loadSettings() +{ + QSettings settings(p_QupZilla->activeProfil()+"settings.ini", QSettings::IniFormat); + settings.beginGroup("AddressBar"); + m_selectAllOnDoubleClick = settings.value("SelectAllTextOnDoubleClick",true).toBool(); + m_addComWithCtrl = settings.value("AddComDomainWithCtrlKey",false).toBool(); + m_addCountryWithAlt = settings.value("AddCountryDomainWithAltKey",true).toBool(); +} + +void LocationBar::textEdit() +{ + m_locationCompleter->popup()->setUpdatesEnabled(false); + showGoButton(); +} + +void LocationBar::showGoButton() +{ + if (m_goButton->isVisible()) + return; + + m_rssIconVisible = m_rssIcon->isVisible(); + + m_bookmarkButton->hide(); + m_rssIcon->hide(); + m_goButton->show(); +} + +void LocationBar::hideGoButton() +{ + if (!m_goButton->isVisible()) + return; + + m_rssIcon->setVisible(m_rssIconVisible); + m_bookmarkButton->show(); + m_goButton->hide(); +} + +void LocationBar::showPopup() +{ + //TODO: Fix to next version + return; + emit textEdited(""); + m_locationCompleter->popup()->showNormal(); +} + +void LocationBar::showSiteInfo() +{ + SiteInfoWidget* info = new SiteInfoWidget(p_QupZilla); + info->showAt(this); +} + +void LocationBar::bookmarkIconClicked() +{ + QUrl url = p_QupZilla->weView()->url(); + + if (m_bookmarksModel->isBookmarked(url)) { + BookmarksWidget* menu = new BookmarksWidget(m_bookmarksModel->bookmarkId(url), this); + menu->showAt(this); + connect(menu, SIGNAL(bookmarkDeleted()), this, SLOT(checkBookmark())); + } else if (m_bookmarksModel->saveBookmark(p_QupZilla->weView())) { + m_bookmarkButton->setPixmap(QPixmap(":/icons/locationbar/star.png")); + m_bookmarkButton->setToolTip(tr("Edit this bookmark")); + } +} + +void LocationBar::checkBookmark() +{ + if (m_bookmarksModel->isBookmarked(QUrl(text()))) { + m_bookmarkButton->setPixmap(QPixmap(":/icons/locationbar/star.png")); + m_bookmarkButton->setToolTip(tr("Edit this bookmark")); + } else { + m_bookmarkButton->setPixmap(QPixmap(":/icons/locationbar/starg.png")); + m_bookmarkButton->setToolTip(tr("Bookmark this Page")); + } +} + +void LocationBar::checkRss() +{ + WebView* view = p_QupZilla->weView(); + if (!view) + return; + QWebFrame* frame = view->page()->mainFrame(); + QWebElementCollection links = frame->findAllElements("link"); + + bool found = false; + m_rssMenu->clear(); + for (int i = 0; isetData(href); + act->setToolTip(title); + m_rssMenu->addAction(act); + found = true; + } + + m_rssMenu->addSeparator(); + m_rssMenu->addAction(QIcon(":/icons/menu/rss.png"), tr("Read RSS news"), p_QupZilla, SLOT(showRSSManager())); + + m_rssIcon->setVisible(found); + m_rssIcon->setMenu(m_rssMenu); +} + +QIcon LocationBar::icon(const QUrl &url) +{ + QUrl url2 = url.scheme() + "://" + url.host(); + url2.host().remove("www"); + + QIcon icon = QWebSettings::iconForUrl(url); + if (icon.isNull()) + icon = QWebSettings::iconForUrl(url2); + + if (icon.isNull()) + icon = QWebSettings::iconForUrl(url2.host().prepend("www")); + + if (!icon.isNull()) + return icon.pixmap(16, 16); + if (icon.isNull()) { + QPixmap pixmap = QWebSettings::webGraphic(QWebSettings::DefaultFrameIconGraphic); + if (pixmap.isNull()) { + pixmap = QPixmap(":icons/locationbar/unknownpage.png"); + QWebSettings::setWebGraphic(QWebSettings::DefaultFrameIconGraphic, pixmap); + } + return pixmap; + } + return icon; +} + +void LocationBar::showUrl(const QUrl &url, bool empty) +{ + if (url.isEmpty() && empty) + return; + + if (url.toEncoded()!=text()) { + setText(url.toEncoded()); + setCursorPosition(0); + } + if (url.scheme() == "https") + setPrivacy(true); + else setPrivacy(false); + + if (p_QupZilla->weView()->isLoading()) { + p_QupZilla->ipLabel()->hide(); + p_QupZilla->progressBar()->setVisible(true); + p_QupZilla->progressBar()->setValue(p_QupZilla->weView()->getLoading()); + p_QupZilla->buttonStop()->setVisible(true); + p_QupZilla->buttonReload()->setVisible(false); + p_QupZilla->statusBar()->showMessage(tr("Loading...")); + }else{ + p_QupZilla->progressBar()->setVisible(false); + p_QupZilla->buttonStop()->setVisible(false); + p_QupZilla->buttonReload()->setVisible(true); + p_QupZilla->statusBar()->showMessage(tr("Done")); + p_QupZilla->ipLabel()->show(); + } + hideGoButton(); + + if (!m_bookmarksModel) + m_bookmarksModel = MainApplication::getInstance()->bookmarks(); + + checkBookmark(); +} + +void LocationBar::siteIconChanged() +{ + const QPixmap* icon_ = 0; + if (!p_QupZilla->weView()->isLoading()) + icon_ = p_QupZilla->weView()->animationLoading( p_QupZilla->tabWidget()->currentIndex(), false)->pixmap(); + + if (!icon_) { + m_siteIcon->setIcon(QIcon(":icons/locationbar/unknownpage.png")); + } else { + QIcon icon = *icon_; + m_siteIcon->setIcon(icon); + } +} + +void LocationBar::setPrivacy(bool state) +{ + if (state) + m_siteIcon->setStyleSheet("QToolButton{border-image: url(:/icons/locationbar/safeline.png); margin-left:2px;}"); + else + m_siteIcon->setStyleSheet("QToolButton{border-image: url(:/icons/locationbar/searchchoose.png); margin-left:2px;}"); +} + +void LocationBar::focusOutEvent(QFocusEvent *e) +{ + QLineEdit::focusOutEvent(e); + if (!selectedText().isEmpty() && e->reason() != Qt::TabFocusReason) + return; + setCursorPosition(0); + hideGoButton(); +} + +void LocationBar::dropEvent(QDropEvent *event) +{ + if (event->mimeData()->hasUrls()) { + QUrl dropUrl = event->mimeData()->urls().at(0); + if (WebView::isUrlValid(dropUrl)) { + setText(dropUrl.toEncoded()); + p_QupZilla->loadAddress(dropUrl); + QLineEdit::focusOutEvent(new QFocusEvent(QFocusEvent::FocusOut)); + return; + } + } + if (event->mimeData()->hasText()) { + QUrl dropUrl = QUrl(event->mimeData()->text()); + if (WebView::isUrlValid(dropUrl)) { + setText(dropUrl.toEncoded()); + p_QupZilla->loadAddress(dropUrl); + QLineEdit::focusOutEvent(new QFocusEvent(QFocusEvent::FocusOut)); + return; + } + + } + QLineEdit::dropEvent(event); +} + +void LocationBar::mouseDoubleClickEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton && m_selectAllOnDoubleClick) + selectAll(); + else + QLineEdit::mouseDoubleClickEvent(event); +} + +void LocationBar::keyPressEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_Escape) { + showUrl(p_QupZilla->weView()->url()); + event->accept(); + return; + } + + QString localDomain = tr(".co.uk","Append domain name on ALT key = Should be different for every country"); + if (event->key() == Qt::Key_Control && m_addComWithCtrl && !text().endsWith(".com")) //Disabled for a while + setText(text().append(".com")); + if (event->key() == Qt::Key_Alt && m_addCountryWithAlt && !text().endsWith(localDomain) && !text().endsWith("/")) + setText(text().append(localDomain)); + + QLineEdit::keyPressEvent(event); +} + +void LocationBar::addRss() +{ + WebView* view = p_QupZilla->weView(); + if(!view) + return; + if (QAction *action = qobject_cast(sender())) { + QUrl url = action->data().toUrl(); + QString urlString = url.toString(); + if(url.host().isEmpty()) { + if(!urlString.startsWith("/")) + urlString="/"+urlString; + urlString = view->url().host()+urlString; + QUrl temp(urlString); + if(temp.scheme().isEmpty()) + urlString="http://"+urlString; + temp = QUrl(urlString); + if(temp.scheme().isEmpty() || temp.host().isEmpty()) + return; + } + if (!url.isValid()) + return; + + QString title; + if (action->toolTip().isEmpty()) + title = view->url().host(); + else + title = action->toolTip(); + + p_QupZilla->getMainApp()->rssManager()->addRssFeed(urlString, title); + } +} + +LocationBar::~LocationBar() +{ + delete m_bookmarkButton; + delete m_goButton; + delete m_siteIcon; + delete m_rssIcon; + delete m_rssMenu; + delete m_locationCompleter; +} diff --git a/src/navigation/locationbar.h b/src/navigation/locationbar.h new file mode 100644 index 000000000..328794f88 --- /dev/null +++ b/src/navigation/locationbar.h @@ -0,0 +1,75 @@ +#ifndef LOCATIONBAR_H +#define LOCATIONBAR_H + +#if defined(QT_NO_DEBUG) & !defined(QT_NO_DEBUG_OUTPUT) +#define QT_NO_DEBUG_OUTPUT +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "lineedit.h" + +class QupZilla; +class LineEdit; +class LocationCompleter; +class ClickableLabel; +class BookmarksModel; + +class LocationBar : public LineEdit +{ + Q_OBJECT; +public: + explicit LocationBar(QupZilla* mainClass, QWidget *parent = 0); + ~LocationBar(); + static QIcon icon(const QUrl &url); + + void loadSettings(); + +public slots: + void checkRss(); + void showUrl(const QUrl &url, bool empty = true); + void siteIconChanged(); + void setPrivacy(bool state); + void addRss(); + void textEdit(); + void showPopup(); + void bookmarkIconClicked(); + void checkBookmark(); + void showSiteInfo(); + +private: + void focusOutEvent(QFocusEvent* e); + void mouseDoubleClickEvent(QMouseEvent *event); + void keyPressEvent(QKeyEvent *event); + void dropEvent(QDropEvent *event); + + void showGoButton(); + void hideGoButton(); + + ClickableLabel* m_bookmarkButton; + ClickableLabel* m_goButton; + QToolButton* m_siteIcon; + QToolButton* m_rssIcon; + QMenu* m_rssMenu; + + bool m_selectAllOnDoubleClick; + bool m_addComWithCtrl; + bool m_addCountryWithAlt; + QupZilla* p_QupZilla; + LocationCompleter* m_locationCompleter; + BookmarksModel* m_bookmarksModel; + + bool m_rssIconVisible; +}; + +#endif // LOCATIONBAR_H diff --git a/src/navigation/locationcompleter.cpp b/src/navigation/locationcompleter.cpp new file mode 100644 index 000000000..450570bb5 --- /dev/null +++ b/src/navigation/locationcompleter.cpp @@ -0,0 +1,140 @@ +#include "locationcompleter.h" +#include "locationbar.h" + +LocationCompleter::LocationCompleter(QObject *parent) : + QCompleter(parent) +{ + setMaxVisibleItems(6); + QStandardItemModel* completeModel = new QStandardItemModel(); + + setModel(completeModel); + QTreeView *treeView = new QTreeView; + + setPopup(treeView); + treeView->setRootIsDecorated(false); + treeView->header()->hide(); + treeView->header()->setStretchLastSection(false); + treeView->header()->setResizeMode(0, QHeaderView::Stretch); + treeView->header()->resizeSection(1,0); + + setCompletionMode(QCompleter::PopupCompletion); + setCaseSensitivity(Qt::CaseInsensitive); + setWrapAround(true); + setCompletionColumn(1); + QTimer::singleShot(0, this, SLOT(loadInitialHistory())); +} + +//QString LocationCompleter::pathFromIndex(const QModelIndex &index) const +//{ +// qDebug() << __FUNCTION__ << "called"; +// return QCompleter::pathFromIndex(index); +//} + +QStringList LocationCompleter::splitPath(const QString &path) const +{ + Q_UNUSED(path); + return QStringList(""); +#if 0 + QStringList returned = QCompleter::splitPath(path); + QStringList returned2; + QSqlQuery query; + query.exec("SELECT url FROM history WHERE title LIKE '%"+path+"%' OR url LIKE '%"+path+"%' ORDER BY count DESC LIMIT 1"); + if (query.next()) { + QString url = query.value(0).toString(); + bool titleSearching = false; + if (!url.contains(path)) + titleSearching = true; + QString prefix = url.mid(0,url.indexOf(path)); + foreach (QString string, returned) { + if (titleSearching) + returned2.append(url); + else + returned2.append(prefix+string); + } + return returned2; + } else { + foreach (QString string, returned) + returned2.append("http://www.google.com/search?client=qupzilla&q=" + string); + return returned2; + } +#endif +} + +void LocationCompleter::loadInitialHistory() +{ + QSqlQuery query; + query.exec("SELECT title, url FROM history LIMIT 5"); + int i = 0; + QStandardItemModel* cModel = (QStandardItemModel*)model(); + + while(query.next()) { + QStandardItem* iconText = new QStandardItem(); + QStandardItem* findUrl = new QStandardItem(); + QString url = query.value(1).toUrl().toEncoded(); + + iconText->setIcon(LocationBar::icon(query.value(1).toUrl()).pixmap(16,16)); + iconText->setText(query.value(0).toString().replace("\n","").append("\n"+url)); + + findUrl->setText(url); + QList items; + items.append(iconText); + items.append(findUrl); + cModel->insertRow(i, items); + i++; + } +} + +void LocationCompleter::refreshCompleter(QString string) +{ + QSqlQuery query; + int limit; + if (string.size() < 3) + limit = 25; + else + limit = 15; + + query.exec("SELECT title, url FROM history WHERE title LIKE '%"+string+"%' OR url LIKE '%"+string+"%' ORDER BY count DESC LIMIT "+QString::number(limit)); + int i = 0; + QStandardItemModel* cModel = (QStandardItemModel*)model(); + QTreeView *treeView = (QTreeView*)popup(); + + cModel->clear(); + while(query.next()) { + QStandardItem* iconText = new QStandardItem(); + QStandardItem* findUrl = new QStandardItem(); + QString url = query.value(1).toUrl().toEncoded(); + + iconText->setIcon(LocationBar::icon(query.value(1).toUrl()).pixmap(16,16)); + iconText->setText(query.value(0).toString().replace("\n","").append("\n"+url)); + + findUrl->setText(url); + QList items; + items.append(iconText); + items.append(findUrl); + cModel->insertRow(i, items); + i++; + } + + if (i == 0) { + QStandardItem* iconText = new QStandardItem(); + QStandardItem* findUrl = new QStandardItem(); + QString url("http://www.google.com/search?client=qupzilla&q="+string); + + iconText->setIcon(QIcon(":/icons/menu/google.png")); + iconText->setText(tr("Search %1 on Google.com\n..........").arg(string)); + findUrl->setText(url); + QList items; + items.append(iconText); + items.append(findUrl); + cModel->insertRow(i, items); + } + + treeView->header()->setResizeMode(0, QHeaderView::Stretch); + treeView->header()->resizeSection(1,0); + + if (i>6) + popup()->setMinimumHeight(190); + else + popup()->setMinimumHeight(0); + popup()->setUpdatesEnabled(true); +} diff --git a/src/navigation/locationcompleter.h b/src/navigation/locationcompleter.h new file mode 100644 index 000000000..aec6035c0 --- /dev/null +++ b/src/navigation/locationcompleter.h @@ -0,0 +1,35 @@ +#ifndef LOCATIONCOMPLETER_H +#define LOCATIONCOMPLETER_H + +#if defined(QT_NO_DEBUG) & !defined(QT_NO_DEBUG_OUTPUT) +#define QT_NO_DEBUG_OUTPUT +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class LocationCompleter : public QCompleter +{ + Q_OBJECT +public: + explicit LocationCompleter(QObject *parent = 0); + + //virtual QString pathFromIndex(const QModelIndex &index) const; + virtual QStringList splitPath(const QString &path) const; + +signals: + +public slots: + void loadInitialHistory(); + void refreshCompleter(QString string); + +}; + +#endif // LOCATIONCOMPLETER_H diff --git a/src/navigation/websearchbar.cpp b/src/navigation/websearchbar.cpp new file mode 100644 index 000000000..1df7b50fa --- /dev/null +++ b/src/navigation/websearchbar.cpp @@ -0,0 +1,115 @@ +#include "websearchbar.h" +#include "qupzilla.h" +#include "webview.h" +#include "clickablelabel.h" + +WebSearchBar::WebSearchBar(QupZilla* mainClass, QWidget *parent) + :LineEdit(parent) + ,p_QupZilla(mainClass) +{ + m_buttonSearch = new ClickableLabel(this); + m_buttonSearch->setPixmap(QPixmap(":/icons/locationbar/search.png")); + m_buttonSearch->setCursor(QCursor(Qt::PointingHandCursor)); + m_buttonSearch->setStyleSheet("QLabel{margin-bottom:2px;}"); + + m_boxSearchType = new QToolButton(this); + m_boxSearchType->setPopupMode(QToolButton::InstantPopup); + m_boxSearchType->setCursor(Qt::ArrowCursor); + m_boxSearchType->setMaximumSize(35, 25); + m_boxSearchType->setMinimumSize(35, 25); + + this->setMinimumHeight(25); + this->setMaximumHeight(25); + m_boxSearchType->setStyleSheet("QToolButton{border-image: url(:/icons/locationbar/searchchoose.png); padding-left:-6px; margin-left:2px;}" + "QToolButton::menu-indicator {background-image: url(:icons/locationbar/arrow-down.gif); background-repeat: no-repeat;}"); + + addWidget(m_buttonSearch, LineEdit::RightSide); + + setupSearchTypes(); + connect(this, SIGNAL(returnPressed()), this, SLOT(search())); + connect(m_buttonSearch, SIGNAL(clicked(QPoint)), this, SLOT(search())); + + setStyleSheet("QLineEdit { background: transparent; border-image: url(:/icons/locationbar/lineedit.png) ;border-width:4;color:black;}"); + + setLeftMargin(33); + setWidgetSpacing(0); +} + +void WebSearchBar::setupSearchTypes() +{ + QMenu* menu = new QMenu(this); + menu->addAction(QIcon(":/icons/menu/google.png"),"Google", this, SLOT(searchChanged()))->setData("Google"); + menu->addAction(QIcon(":/icons/menu/cz_seznam.png"),"Seznam", this, SLOT(searchChanged()))->setData("Seznam"); + menu->addAction(QIcon(":/icons/menu/icon-wikipedia.png"),"Wikipedia (en)", this, SLOT(searchChanged()))->setData("Wikipedia (en)"); + menu->addAction(QIcon(":/icons/menu/icon-wikipedia.png"),"Wikipedia (cs)", this, SLOT(searchChanged()))->setData("Wikipedia (cs)"); + menu->addAction(QIcon(":/icons/menu/csfd.png"),"CSFD", this, SLOT(searchChanged()))->setData("CSFD"); + menu->addAction(QIcon(":/icons/menu/youtube.png"),"Youtube", this, SLOT(searchChanged()))->setData("Youtube"); + + m_boxSearchType->setMenu(menu); + m_boxSearchType->setIcon(QIcon(":/icons/menu/google.png")); + m_boxSearchType->setToolTip("Google"); + + setPlaceholderText("Google"); + +} + +void WebSearchBar::searchChanged() +{ + if (QAction *action = qobject_cast(sender())) { + if (action->data().toString() == "Google") + m_boxSearchType->setIcon(QIcon(":/icons/menu/google.png")); + else if (action->data().toString() == "Seznam") + m_boxSearchType->setIcon(QIcon(":/icons/menu/cz_seznam.png")); + else if (action->data().toString().contains("Wikipedia")) + m_boxSearchType->setIcon(QIcon(":/icons/menu/icon-wikipedia.png")); + else if (action->data().toString() == "CSFD") + m_boxSearchType->setIcon(QIcon(":/icons/menu/csfd.png")); + else if (action->data().toString() == "Youtube") + m_boxSearchType->setIcon(QIcon(":/icons/menu/youtube.png")); + + m_boxSearchType->setToolTip(action->data().toString()); + } +} + +void WebSearchBar::search() +{ + if (text().isEmpty()) + return; + + QUrl searchUrl; + if (m_boxSearchType->toolTip() == "Google") + searchUrl = QUrl("http://www.google.com/search?client=qupzilla&q="+text()); + if (m_boxSearchType->toolTip() == "Seznam") + searchUrl = QUrl("http://search.seznam.cz/?q="+text()); + if (m_boxSearchType->toolTip() == "Wikipedia (cs)") + searchUrl = QUrl("http://cs.wikipedia.org/w/index.php?search="+text()); + if (m_boxSearchType->toolTip() == "Wikipedia (en)") + searchUrl = QUrl("http://en.wikipedia.org/w/index.php?search="+text()); + if (m_boxSearchType->toolTip() == "CSFD") + searchUrl = QUrl("http://www.csfd.cz/hledani-filmu-hercu-reziseru-ve-filmove-databazi/?search="+text()); + if (m_boxSearchType->toolTip() == "Youtube") + searchUrl = QUrl("http://www.youtube.com/results?search_query="+text()); + + p_QupZilla->weView()->load(searchUrl); + p_QupZilla->weView()->setFocus(); +} + +void WebSearchBar::focusOutEvent(QFocusEvent *e) +{ + if (text().isEmpty()) { + QString search = m_boxSearchType->toolTip(); + //clear(); + setPlaceholderText(search); + } + QLineEdit::focusOutEvent(e); +} + +void WebSearchBar::focusInEvent(QFocusEvent *e) +{ + QString search = m_boxSearchType->toolTip(); + + if (text() == search) { + clear(); + } + QLineEdit::focusInEvent(e); +} diff --git a/src/navigation/websearchbar.h b/src/navigation/websearchbar.h new file mode 100644 index 000000000..5a8a15926 --- /dev/null +++ b/src/navigation/websearchbar.h @@ -0,0 +1,38 @@ +#ifndef WEBSEARCHBAR_H +#define WEBSEARCHBAR_H + +#if defined(QT_NO_DEBUG) & !defined(QT_NO_DEBUG_OUTPUT) +#define QT_NO_DEBUG_OUTPUT +#endif + +#include +#include +#include +#include +#include "lineedit.h" + +class QupZilla; +class LineEdit; +class ClickableLabel; +class WebSearchBar : public LineEdit +{ + Q_OBJECT; +public: + explicit WebSearchBar(QupZilla* mainClass, QWidget *parent = 0); + +private slots: + void searchChanged(); + void search(); + +private: + ClickableLabel* m_buttonSearch; + QToolButton* m_boxSearchType; + + void setupSearchTypes(); + void focusInEvent(QFocusEvent* e); + void focusOutEvent(QFocusEvent* e); + + QupZilla* p_QupZilla; +}; + +#endif // WEBSEARCHBAR_H diff --git a/src/network/networkmanager.cpp b/src/network/networkmanager.cpp new file mode 100644 index 000000000..ef948b7b1 --- /dev/null +++ b/src/network/networkmanager.cpp @@ -0,0 +1,171 @@ +#include "networkmanager.h" +#include "qupzilla.h" +#include "autofillmodel.h" +#include "networkmanagerproxy.h" +#include "mainapplication.h" + +NetworkManager::NetworkManager(QupZilla* mainClass, QObject *parent) : + NetworkManagerProxy(mainClass, parent) + ,p_QupZilla(mainClass) +{ + connect(this, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), this, SLOT(authentication(QNetworkReply*, QAuthenticator* ))); + connect(this, SIGNAL(sslErrors(QNetworkReply*,QList)), this, SLOT(sslError(QNetworkReply*,QList))); + + loadSettings(); +} + +void NetworkManager::loadSettings() +{ + QSettings settings(MainApplication::getInstance()->getActiveProfil()+"settings.ini", QSettings::IniFormat); + settings.beginGroup("Web-Browser-Settings"); + + if (settings.value("AllowLocalCache", true).toBool()) { + m_diskCache = new QNetworkDiskCache(this); + m_diskCache->setCacheDirectory(MainApplication::getInstance()->getActiveProfil()+"/networkcache"); + m_diskCache->setMaximumCacheSize(settings.value("MaximumCacheSize",50).toInt() * 1024*1024); //MegaBytes + setCache(m_diskCache); + } + settings.endGroup(); +} + +void NetworkManager::sslError(QNetworkReply *reply, QList errors) +{ + QString title = tr("SSL Certificate Error!"); + QString text1 = tr("The page you trying to access has following errors in SSL Certificate:"); + + QStringList actions; + + foreach (QSslError error, errors) { + if (m_certExceptions.contains(error.certificate())) + continue; + if (error.error() == QSslError::NoError) //Weird behavior on Windows + continue; + + QSslCertificate cert = error.certificate(); + actions.append(tr("Organization: ") + cert.subjectInfo(QSslCertificate::Organization)); + actions.append(tr("Domain Name: ") + cert.subjectInfo(QSslCertificate::CommonName)); + actions.append(tr("Expiration Date: ") + cert.expiryDate().toString("hh:mm:ss dddd d. MMMM yyyy")); + actions.append(tr("Error: ") + error.errorString()); + } + + QString text2 = tr("Would you like to make exception for this certificate?"); + QString message = QString(QLatin1String("%1

    %2

    • %3

    %4

    ")).arg(title, text1, actions.join(QLatin1String("
  2. ")), text2); + + if (!actions.isEmpty()) { + QMessageBox::StandardButton button = QMessageBox::critical(p_QupZilla, tr("SSL Certificate Error"), + message, QMessageBox::Yes | QMessageBox::No); + if (button != QMessageBox::Yes) + return; + } + + foreach (QSslError error, errors) { + if (m_certExceptions.contains(error.certificate())) + continue; + m_certExceptions.append(error.certificate()); + } + + reply->ignoreSslErrors(errors); +} + +void NetworkManager::authentication(QNetworkReply* reply, QAuthenticator* auth) +{ + QDialog* dialog = new QDialog(p_QupZilla); + dialog->setWindowTitle(tr("Authorization required")); + + QFormLayout* formLa = new QFormLayout(dialog); + + QLabel* label = new QLabel(dialog); + QLabel* userLab = new QLabel(dialog); + QLabel* passLab = new QLabel(dialog); + userLab->setText(tr("Username: ")); + passLab->setText(tr("Password: ")); + + QLineEdit* user = new QLineEdit(dialog); + QLineEdit* pass = new QLineEdit(dialog); + QCheckBox* save = new QCheckBox(dialog); + save->setText(tr("Save username and password on this site")); + pass->setEchoMode(QLineEdit::Password); + + QDialogButtonBox* box = new QDialogButtonBox(dialog); + box->addButton(QDialogButtonBox::Ok); + box->addButton(QDialogButtonBox::Cancel); + connect(box, SIGNAL(rejected()), dialog, SLOT(reject())); + connect(box, SIGNAL(accepted()), dialog, SLOT(accept())); + + label->setText(tr("A username and password are being requested by %1. " + "The site says: \"%2\"").arg(reply->url().toEncoded(), auth->realm())); + formLa->addRow(label); + + formLa->addRow(userLab, user); + formLa->addRow(passLab, pass); + formLa->addRow(save); + + formLa->addWidget(box); + AutoFillModel* fill = MainApplication::getInstance()->autoFill(); + if (fill->isStored(reply->url())) { + save->setChecked(true); + user->setText(fill->getUsername(reply->url())); + pass->setText(fill->getPassword(reply->url())); + } + emit wantsFocus(reply->url()); + + //Do not save when private browsing is enabled + if (p_QupZilla->getMainApp()->webSettings()->testAttribute(QWebSettings::PrivateBrowsingEnabled)) + save->setVisible(false); + + if (!dialog->exec() == QDialog::Accepted) + return; + auth->setUser(user->text()); + auth->setPassword(pass->text()); + + if (save->isChecked()) + fill->addEntry(reply->url(), user->text(), pass->text()); +} + +QNetworkReply *NetworkManager::createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest &request, QIODevice *outgoingData) +{ + if (op == PostOperation && outgoingData) { + QByteArray outgoingDataByteArray = outgoingData->peek(1024 * 1024); + MainApplication::getInstance()->autoFill()->post(request, outgoingDataByteArray); + } + + QNetworkRequest req = request; + req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); + QNetworkReply* reply = QNetworkAccessManager::createRequest(op, req, outgoingData); + //emit requestCreated(op, request, reply); + return reply; +} + +void NetworkManager::saveCertExceptions() +{ + QFile file(MainApplication::getInstance()->getActiveProfil()+"sslexceptions.dat"); + file.open(QIODevice::WriteOnly); + QDataStream stream(&file); + + int count = m_certExceptions.count(); + stream << count; + + for (int i = 0; i < count; i++) { + stream << m_certExceptions.at(i).toPem(); + } + + file.close(); +} + +void NetworkManager::loadCertExceptions() +{ + QFile file(MainApplication::getInstance()->getActiveProfil()+"sslexceptions.dat"); + file.open(QIODevice::ReadOnly); + QDataStream stream(&file); + + int count; + stream >> count; + QByteArray cert; + + for (int i = 0; i < count; i++) { + stream >> cert; + m_certExceptions.append(QSslCertificate::fromData(cert)); + } + + file.close(); +} diff --git a/src/network/networkmanager.h b/src/network/networkmanager.h new file mode 100644 index 000000000..051181ca7 --- /dev/null +++ b/src/network/networkmanager.h @@ -0,0 +1,49 @@ +#ifndef NETWORKMANAGER_H +#define NETWORKMANAGER_H + +#if defined(QT_NO_DEBUG) & !defined(QT_NO_DEBUG_OUTPUT) +#define QT_NO_DEBUG_OUTPUT +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "networkmanagerproxy.h" + +class QupZilla; +class NetworkManager : public NetworkManagerProxy +{ + Q_OBJECT +public: + explicit NetworkManager(QupZilla* mainClass, QObject *parent = 0); + QNetworkReply *createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest &request, QIODevice *outgoingData); + + void saveCertExceptions(); + void loadCertExceptions(); + void loadSettings(); + +signals: + void finishLoading(bool state); + void wantsFocus(const QUrl &url); + void sslDialogClosed(); + +public slots: + void authentication(QNetworkReply* reply, QAuthenticator* auth); + void sslError(QNetworkReply* reply, QList errors); + +private: + QupZilla* p_QupZilla; + QList m_certExceptions; + QNetworkDiskCache* m_diskCache; + +}; + +#endif // NETWORKMANAGER_H diff --git a/src/network/networkmanagerproxy.cpp b/src/network/networkmanagerproxy.cpp new file mode 100644 index 000000000..f133bdaf3 --- /dev/null +++ b/src/network/networkmanagerproxy.cpp @@ -0,0 +1,46 @@ +#include "networkmanagerproxy.h" +#include "networkmanager.h" +#include "webpage.h" +#include "qupzilla.h" +#include "cookiejar.h" +#include "mainapplication.h" + +NetworkManagerProxy::NetworkManagerProxy(QupZilla* mainClass, QObject *parent) : + QNetworkAccessManager(parent) + ,p_QupZilla(mainClass) + ,m_view(0) + ,m_page(0) +{ + setCookieJar(MainApplication::getInstance()->cookieJar()); +} + +void NetworkManagerProxy::populateNetworkRequest(QNetworkRequest &request) +{ + qDebug() << __FUNCTION__ << "called"; + QVariant variant = qVariantFromValue((void *) m_page); + request.setAttribute((QNetworkRequest::Attribute)(QNetworkRequest::User + 100), variant); +} + +void NetworkManagerProxy::setPrimaryNetworkAccessManager(NetworkManagerProxy *manager) +{ + Q_ASSERT(manager); + m_manager = manager; + + connect(this, SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)), + manager, SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*))); + connect(this, SIGNAL(finished(QNetworkReply *)), + manager, SIGNAL(finished(QNetworkReply *))); + + connect(this, SIGNAL(sslErrors(QNetworkReply*, const QList&)), + manager, SIGNAL(sslErrors(QNetworkReply*, const QList&))); +} + +QNetworkReply *NetworkManagerProxy::createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest &request, QIODevice *outgoingData) +{ + if (m_manager && m_page) { + QNetworkRequest pageRequest = request; + m_page->populateNetworkRequest(pageRequest); + return m_manager->createRequest(op, pageRequest, outgoingData); + } + return QNetworkAccessManager::createRequest(op, request, outgoingData); +} diff --git a/src/network/networkmanagerproxy.h b/src/network/networkmanagerproxy.h new file mode 100644 index 000000000..2a6bce0f3 --- /dev/null +++ b/src/network/networkmanagerproxy.h @@ -0,0 +1,37 @@ +#ifndef NETWORKMANAGERPROXY_H +#define NETWORKMANAGERPROXY_H + +#if defined(QT_NO_DEBUG) & !defined(QT_NO_DEBUG_OUTPUT) +#define QT_NO_DEBUG_OUTPUT +#endif + +#include +#include +#include + +class WebView; +class WebPage; +class QupZilla; + +class NetworkManagerProxy : public QNetworkAccessManager +{ + Q_OBJECT +public: + explicit NetworkManagerProxy(QupZilla* mainClass, QObject *parent = 0); + void setView(WebView* view) { m_view = view; } + void setPage(WebPage* page) { m_page = page; } + + void setPrimaryNetworkAccessManager(NetworkManagerProxy *manager); + QNetworkReply* createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest &request, QIODevice *outgoingData); + +protected: + virtual void populateNetworkRequest(QNetworkRequest &request); + +private: + QupZilla* p_QupZilla; + WebView* m_view; + WebPage* m_page; + NetworkManagerProxy *m_manager; +}; + +#endif // NETWORKMANAGERPROXY_H diff --git a/src/other/aboutdialog.cpp b/src/other/aboutdialog.cpp new file mode 100644 index 000000000..2137362ad --- /dev/null +++ b/src/other/aboutdialog.cpp @@ -0,0 +1,59 @@ +#include "aboutdialog.h" +#include "ui_aboutdialog.h" +#include "qupzilla.h" +#include "webview.h" +#include "webpage.h" + +AboutDialog::AboutDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::AboutDialog) +{ + ui->setupUi(this); + + connect(ui->buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(close())); + connect(ui->authorsButton, SIGNAL(clicked()), this, SLOT(buttonClicked())); + + showAbout(); +} + +void AboutDialog::buttonClicked() +{ + if (ui->authorsButton->text() == tr("Authors and Contributors")) + showAuthors(); + else if (ui->authorsButton->text() == tr("< About QupZilla")) + showAbout(); +} + +void AboutDialog::showAbout() +{ + ui->authorsButton->setText(tr("Authors and Contributors")); + if (m_aboutHtml.isEmpty()) { + m_aboutHtml.append(""); + } + ui->textBrowser->setHtml(m_aboutHtml); +} + +void AboutDialog::showAuthors() +{ + ui->authorsButton->setText(tr("< About QupZilla")); + if (m_authorsHtml.isEmpty()) { + m_authorsHtml.append("
    "); + m_authorsHtml.append(tr("

    Main developers:
    %1 <%2>

    ").arg(QupZilla::AUTHOR, "nowrep@gmail.com")); + m_authorsHtml.append(tr("

    Other contributors:
    %1

    ").arg("Rajny :: Graphics
    Mikino :: Slovakia Translation")); + m_authorsHtml.append(tr("

    Thanks to:
    %1

    ").arg("Patrick :: First User")); + m_authorsHtml.append("
    "); + } + ui->textBrowser->setHtml(m_authorsHtml); +} + +AboutDialog::~AboutDialog() +{ + delete ui; +} diff --git a/src/other/aboutdialog.h b/src/other/aboutdialog.h new file mode 100644 index 000000000..e175a49e9 --- /dev/null +++ b/src/other/aboutdialog.h @@ -0,0 +1,34 @@ +#ifndef ABOUTDIALOG_H +#define ABOUTDIALOG_H + +#if defined(QT_NO_DEBUG) & !defined(QT_NO_DEBUG_OUTPUT) +#define QT_NO_DEBUG_OUTPUT +#endif + +#include + +namespace Ui { + class AboutDialog; +} + +class AboutDialog : public QDialog +{ + Q_OBJECT + +public: + explicit AboutDialog(QWidget *parent = 0); + ~AboutDialog(); + +private slots: + void showAbout(); + void showAuthors(); + void buttonClicked(); + +private: + Ui::AboutDialog *ui; + + QString m_aboutHtml; + QString m_authorsHtml; +}; + +#endif // ABOUTDIALOG_H diff --git a/src/other/aboutdialog.ui b/src/other/aboutdialog.ui new file mode 100644 index 000000000..737e55174 --- /dev/null +++ b/src/other/aboutdialog.ui @@ -0,0 +1,132 @@ + + + AboutDialog + + + + 0 + 0 + 271 + 440 + + + + About QupZilla + + + + :/icons/qupzilla.png:/icons/qupzilla.png + + + + 0 + + + + + background:white; + + + + + + :/icons/other/about.png + + + Qt::AlignCenter + + + + + + + border:none; + + + Qt::ScrollBarAlwaysOff + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Ubuntu'; font-size:10pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> + + + true + + + true + + + + + + + 5 + + + + + + 0 + 0 + + + + Authors + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + + + + + + buttonBox + accepted() + AboutDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + AboutDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/other/clearprivatedata.cpp b/src/other/clearprivatedata.cpp new file mode 100644 index 000000000..27c3a800d --- /dev/null +++ b/src/other/clearprivatedata.cpp @@ -0,0 +1,74 @@ +#include "clearprivatedata.h" +#include "qupzilla.h" +#include "cookiejar.h" +#include "mainapplication.h" +#include "networkmanager.h" +#include "clickablelabel.h" + +ClearPrivateData::ClearPrivateData(QupZilla* mainClass, QWidget *parent) : + QDialog(parent) + ,p_QupZilla(mainClass) +{ + setWindowTitle(tr("Clear Recent History")); + setWindowIcon(QIcon(":/icons/qupzilla.png")); + m_layout = new QBoxLayout(QBoxLayout::TopToBottom, this); + m_label = new QLabel(this); + m_buttonBox = new QDialogButtonBox(this); + m_buttonBox->addButton(QDialogButtonBox::Ok); + m_buttonBox->addButton(QDialogButtonBox::Cancel); + connect(m_buttonBox, SIGNAL(accepted()), this, SLOT(dialogAccepted())); + connect(m_buttonBox, SIGNAL(rejected()), this, SLOT(close())); + m_layout->addWidget(m_label); + m_label->setText(tr("Choose what you want to delete:")); + m_layout->addWidget(m_buttonBox); + + m_clearHistory = new QCheckBox(tr("Clear history"), this); + m_clearCookies = new QCheckBox(tr("Clear cookies"), this); + m_clearCache = new QCheckBox(tr("Clear cache"), this); + m_clearIcons = new QCheckBox(tr("Clear icons"), this); + m_clearFlashCookies = new ClickableLabel(this); + m_clearFlashCookies->setText(tr("Clear cookies from Adobe Flash Player")); + + m_clearHistory->setChecked(true); + m_clearCookies->setChecked(true); + m_clearCache->setChecked(true); + m_clearIcons->setChecked(true); + m_clearFlashCookies->setStyleSheet("color: blue; text-decoration: underline;"); + m_clearFlashCookies->setCursor(Qt::PointingHandCursor); + + m_layout->addWidget(m_clearHistory); + m_layout->addWidget(m_clearCookies); + m_layout->addWidget(m_clearCache); + m_layout->addWidget(m_clearIcons); + m_layout->addWidget(m_clearFlashCookies); + + m_layout->addWidget(m_buttonBox); + + connect(m_clearFlashCookies, SIGNAL(clicked(QPoint)), this, SLOT(clearFlash())); +} + +void ClearPrivateData::clearFlash() +{ + p_QupZilla->loadAddress(QUrl("http://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager07.html")); +} + +void ClearPrivateData::dialogAccepted() +{ + if (m_clearHistory->isChecked()) { + QSqlQuery query; + query.exec("DELETE FROM history"); + query.exec("VACUUM"); + } + if (m_clearCookies->isChecked()) { + QList cookies; + p_QupZilla->getMainApp()->cookieJar()->setAllCookies(cookies); + } + if (m_clearCache->isChecked()) { + p_QupZilla->getMainApp()->webSettings()->clearMemoryCaches(); + p_QupZilla->getMainApp()->networkManager()->cache()->clear(); + } + if (m_clearIcons->isChecked()) { + p_QupZilla->getMainApp()->webSettings()->clearIconDatabase(); + } + close(); +} diff --git a/src/other/clearprivatedata.h b/src/other/clearprivatedata.h new file mode 100644 index 000000000..40ea0c88a --- /dev/null +++ b/src/other/clearprivatedata.h @@ -0,0 +1,45 @@ +#ifndef CLEARPRIVATEDATA_H +#define CLEARPRIVATEDATA_H + +#if defined(QT_NO_DEBUG) & !defined(QT_NO_DEBUG_OUTPUT) +#define QT_NO_DEBUG_OUTPUT +#endif + +#include +#include +#include +#include +#include + +class ClickableLabel; +class QupZilla; +class ClearPrivateData : public QDialog +{ + Q_OBJECT +public: + explicit ClearPrivateData(QupZilla* mainClass, QWidget *parent = 0); + +signals: + +public slots: + +private slots: + void dialogAccepted(); + void clearFlash(); + +private: + QupZilla* p_QupZilla; + + QBoxLayout* m_layout; + QLabel* m_label; + QDialogButtonBox* m_buttonBox; + + QCheckBox* m_clearHistory; + QCheckBox* m_clearCookies; + QCheckBox* m_clearCache; + QCheckBox* m_clearIcons; + + ClickableLabel* m_clearFlashCookies; +}; + +#endif // CLEARPRIVATEDATA_H diff --git a/src/other/sourceviewer.cpp b/src/other/sourceviewer.cpp new file mode 100644 index 000000000..15cf23242 --- /dev/null +++ b/src/other/sourceviewer.cpp @@ -0,0 +1,25 @@ +#include "sourceviewer.h" +#include "qupzilla.h" +#include "webview.h" +SourceViewer::SourceViewer(QupZilla* mainClass, QWidget *parent) : + QWidget(parent) + ,p_QupZilla(mainClass) +{ + setWindowTitle(tr("Source of ")+p_QupZilla->weView()->url().toString()); + setWindowIcon(QIcon(":/icons/qupzilla.png")); + m_layout = new QBoxLayout(QBoxLayout::TopToBottom, this); + m_sourceEdit = new QTextEdit(this); + + m_layout->addWidget(m_sourceEdit); + m_layout->setContentsMargins(1, 2, 1, 2); + + m_sourceEdit->insertPlainText(p_QupZilla->weView()->page()->mainFrame()->toHtml()); + + this->resize(650, 600); + m_sourceEdit->setReadOnly(true); + m_sourceEdit->moveCursor(QTextCursor::Start); + //CENTER on scren + const QRect screen = QApplication::desktop()->screenGeometry(); + const QRect &size = QWidget::geometry(); + QWidget::move( (screen.width()-size.width())/2, (screen.height()-size.height())/2 ); +} diff --git a/src/other/sourceviewer.h b/src/other/sourceviewer.h new file mode 100644 index 000000000..70bd0936f --- /dev/null +++ b/src/other/sourceviewer.h @@ -0,0 +1,32 @@ +#ifndef SOURCEVIEWER_H +#define SOURCEVIEWER_H + +#if defined(QT_NO_DEBUG) & !defined(QT_NO_DEBUG_OUTPUT) +#define QT_NO_DEBUG_OUTPUT +#endif + +#include +#include +#include +#include + +class QupZilla; +class SourceViewer : public QWidget +{ + Q_OBJECT +public: + explicit SourceViewer(QupZilla* mainClass, QWidget *parent = 0); + +signals: + +public slots: + +private: + QupZilla* p_QupZilla; + + QBoxLayout* m_layout; + QTextEdit* m_sourceEdit; + +}; + +#endif // SOURCEVIEWER_H diff --git a/src/other/updater.cpp b/src/other/updater.cpp new file mode 100644 index 000000000..e2577d7e5 --- /dev/null +++ b/src/other/updater.cpp @@ -0,0 +1,79 @@ +#include "updater.h" +#include "qupzilla.h" +#include "tabwidget.h" + +Updater::Updater(QupZilla* mainClass, QObject *parent) : + QObject(parent) + ,p_QupZilla(mainClass) +{ + createTrayIcon(); +#ifndef DEVELOPING + QTimer::singleShot(60*1000, this, SLOT(start()) ); //Start checking after 1 minute +#endif +} + +void Updater::start() +{ + startDownloadingUpdateInfo(QUrl(QupZilla::WWWADDRESS+"/update.php?i=actualversion")); +} + +void Updater::createTrayIcon() +{ + m_trayIcon = new QSystemTrayIcon(this); + m_trayIconMenu = new QMenu(); + m_trayIconMenu->addAction(tr("Go to download page"), p_QupZilla, SLOT(loadActionUrl()))->setData(QUrl(QupZilla::WWWADDRESS+"/download.php")); + m_trayIconMenu->addAction(tr("Go to QupZilla website"), p_QupZilla, SLOT(loadActionUrl()))->setData(QUrl(QupZilla::WWWADDRESS)); + m_trayIconMenu->addSeparator(); + m_trayIconMenu->addAction(tr("Hide notification"), m_trayIcon, SLOT(hide())); + + + m_trayIcon->setContextMenu(m_trayIconMenu); + m_trayIcon->setIcon(QIcon(":/icons/qupzillaupdate.png")); + m_trayIcon->setToolTip(tr("QupZilla is checking for updates")); +} +void Updater::startDownloadingUpdateInfo(const QUrl &url) +{ +// trayIcon->show(); // Disabled, it was getting focus, so mainwindow lost focus + QNetworkAccessManager* manager = new QNetworkAccessManager(); + QNetworkReply* reply; + reply=manager->get(QNetworkRequest(QUrl(url))); + + connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(downCompleted(QNetworkReply *))); +} + +void Updater::downCompleted(QNetworkReply* reply) +{ + m_trayIcon->show(); + QString html = QString(reply->readAll()); + if (html.startsWith("Version:")){ + html.remove("Version:"); + if (html != QupZilla::VERSION) { + connect(m_trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(clicked(QSystemTrayIcon::ActivationReason))); + connect(m_trayIcon, SIGNAL(messageClicked()), this, SLOT(goUpdate())); + m_trayIcon->setToolTip(tr("QupZilla found a new version!")); + m_trayIcon->showMessage(tr("New version is available"), tr("New version of QupZilla %1 is available!").arg(html)); + } + else + m_trayIcon->hide(); + } + + reply->manager()->deleteLater(); +} + +void Updater::goUpdate() +{ + p_QupZilla->tabWidget()->addView(QUrl(QupZilla::WWWADDRESS+"/download.php"), tr("QupZilla Update")); +} + +void Updater::clicked(QSystemTrayIcon::ActivationReason reason) +{ + if (reason == QSystemTrayIcon::DoubleClick) + p_QupZilla->tabWidget()->addView(QUrl(QupZilla::WWWADDRESS+"/download.php"), tr("QupZilla Update")); +} + +Updater::~Updater() +{ + m_trayIcon->hide(); + delete m_trayIconMenu; + delete m_trayIcon; +} diff --git a/src/other/updater.h b/src/other/updater.h new file mode 100644 index 000000000..d1312a48b --- /dev/null +++ b/src/other/updater.h @@ -0,0 +1,40 @@ +#ifndef UPDATER_H +#define UPDATER_H + +#if defined(QT_NO_DEBUG) & !defined(QT_NO_DEBUG_OUTPUT) +#define QT_NO_DEBUG_OUTPUT +#endif + +#include +#include +#include +#include + +class QupZilla; +class Updater : public QObject +{ + Q_OBJECT +public: + explicit Updater(QupZilla* mainClass, QObject *parent = 0); + ~Updater(); + +signals: + +public slots: + void downCompleted(QNetworkReply* reply); + void start(); + void goUpdate(); + void clicked(QSystemTrayIcon::ActivationReason reason); + +private: + void createTrayIcon(); + void startDownloadingUpdateInfo(const QUrl &url); + + QSystemTrayIcon* m_trayIcon; + QMenu* m_trayIconMenu; + + QupZilla* p_QupZilla; + +}; + +#endif // UPDATER_H diff --git a/src/plugins/clicktoflash.cpp b/src/plugins/clicktoflash.cpp new file mode 100644 index 000000000..6b0a87450 --- /dev/null +++ b/src/plugins/clicktoflash.cpp @@ -0,0 +1,139 @@ +/* ============================================================ +* +* Copyright (C) 2009 by Benjamin C. Meyer +* Copyright (C) 2010 by Matthieu Gicquel +* +* 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 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* 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. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* ============================================================ */ + +#include "clicktoflash.h" +#include "clickablelabel.h" +#include "mainapplication.h" +#include "pluginproxy.h" + +ClickToFlash::ClickToFlash(const QUrl &pluginUrl, QWidget *parent) + : QWidget(parent) + , m_url(pluginUrl) +{ + QHBoxLayout *horizontalLayout; + QFrame *frame; + QHBoxLayout *horizontalLayout_2; + QToolButton *toolButton; + + horizontalLayout = new QHBoxLayout(this); + frame = new QFrame(this); + frame->setStyleSheet("QFrame { border: 1px solid #e8e8e8; }"); + frame->setContentsMargins(0,0,0,0); + horizontalLayout_2 = new QHBoxLayout(frame); + toolButton = new QToolButton(frame); + toolButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + toolButton->setStyleSheet("QToolButton { background: url(:/icons/other/flash.png) no-repeat;\n" + "background-position: center; border: none;}\n" + "QToolButton:hover { background: url(:/icons/other/flashstart.png) no-repeat; \n" + "background-position: center; border:none;}"); + toolButton->setCursor(Qt::PointingHandCursor); + horizontalLayout_2->addWidget(toolButton); + horizontalLayout->addWidget(frame); + horizontalLayout->setContentsMargins(0,0,0,0); + horizontalLayout_2->setContentsMargins(0,0,0,0); + + connect(toolButton, SIGNAL(clicked(bool)), this, SLOT(load())); + setMinimumSize(27,27); + + setContextMenuPolicy(Qt::CustomContextMenu); + connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(customContextMenuRequested(QPoint))); +} + +void ClickToFlash::customContextMenuRequested(const QPoint &pos) +{ + QMenu menu; + menu.addAction(tr("Flash blocked by ClickToFlash")); + menu.addSeparator(); + menu.addAction(tr("Add %1 to whitelist").arg(m_url.host()), this, SLOT(toWhitelist())); + menu.actions().at(0)->setEnabled(false); + menu.exec(mapToGlobal(pos)); +} + +void ClickToFlash::toWhitelist() +{ + MainApplication::getInstance()->plugins()->c2f_addWhitelist(m_url.host()); + load(); +} + +void ClickToFlash::load() +{ + QWidget *parent = parentWidget(); + QWebView *view = 0; + while (parent) { + if (QWebView *aView = qobject_cast(parent)) { + view = aView; + break; + } + parent = parent->parentWidget(); + } + if (!view) + return; + + const QString selector = "%1[type=\"application/x-shockwave-flash\"]"; + hide(); + + QList frames; + frames.append(view->page()->mainFrame()); + while (!frames.isEmpty()) { + QWebFrame *frame = frames.takeFirst(); + QWebElement docElement = frame->documentElement(); + + QWebElementCollection elements; + elements.append(docElement.findAll(selector.arg("object"))); + elements.append(docElement.findAll(selector.arg("embed"))); + + foreach(QWebElement element, elements) { + if (checkElement(element)) { + QWebElement substitute = element.clone(); + emit signalLoadClickToFlash(true); + element.replace(substitute); + deleteLater(); + return; + } + } + frames += frame->childFrames(); + } +} + + +bool ClickToFlash::checkElement(QWebElement el) +{ + QString checkString; + QString urlString; + checkString = QUrl(el.attribute("src")).toString(QUrl::RemoveQuery); + urlString = m_url.toString(QUrl::RemoveQuery); + + if (urlString.contains(checkString)) + return true; + QWebElementCollection collec = el.findAll("*"); + int i = 0; + while (i < collec.count()) { + QWebElement el = collec.at(i); + checkString = QUrl(el.attribute("src")).toString(QUrl::RemoveQuery); + urlString = m_url.toString(QUrl::RemoveQuery); + if (urlString.contains(checkString)) + return true; + i++; + } + return false; +} diff --git a/src/plugins/clicktoflash.h b/src/plugins/clicktoflash.h new file mode 100644 index 000000000..ce9c74215 --- /dev/null +++ b/src/plugins/clicktoflash.h @@ -0,0 +1,67 @@ +/* ============================================================ +* +* Copyright (C) 2009 by Benjamin C. Meyer +* Copyright (C) 2010 by Matthieu Gicquel +* +* +* 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 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* 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. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* ============================================================ */ + +#ifndef CLICKTOFLASH_H +#define CLICKTOFLASH_H + +// Qt Includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class QWebElement; +class ClickToFlash : public QWidget +{ + Q_OBJECT + +public: + explicit ClickToFlash(const QUrl &pluginUrl, QWidget *parent = 0); + +signals: + void signalLoadClickToFlash(bool); + +private slots: + void load(); + void customContextMenuRequested(const QPoint &pos); + void toWhitelist(); + +private: + bool checkElement(QWebElement el); + + /** + used to find the right QWebElement between the ones of the different plugins + */ + const QUrl m_url; +}; + +#endif // CLICKTOFLASH_H + diff --git a/src/plugins/plugininterface.h b/src/plugins/plugininterface.h new file mode 100644 index 000000000..52d6b8edf --- /dev/null +++ b/src/plugins/plugininterface.h @@ -0,0 +1,44 @@ +#ifndef PLUGININTERFACE_H +#define PLUGININTERFACE_H + +#include +#include +#include +#include +#include +#include +#include + +class PluginInterface +{ +public: + //Plugin Necessary Init Functions + //You have to reimplement those functions, otherwise QupZilla crash + virtual ~PluginInterface() { } + virtual QString pluginName() = 0; + virtual QString pluginInfo() = 0; + virtual QString pluginDescription() = 0; + virtual QString pluginVersion() = 0; + virtual QString pluginAuthor() = 0; + virtual void init(QString settingsPath) = 0; + virtual bool testPlugin() = 0; + //End Plugin Necessary Init Functions + + virtual QTranslator* getTranslator(QString locale) { Q_UNUSED(locale) return 0; } + virtual QIcon pluginIcon() { return QIcon(); } + virtual bool hasSettings() { return false; } + virtual void showSettings() { } + + virtual void populateToolsMenu(QMenu* menu) { Q_UNUSED(menu) } + virtual void populateHelpMenu(QMenu* menu) { Q_UNUSED(menu) } + virtual void populateWebViewMenu(QMenu* menu, QWebView* view, QWebHitTestResult r) { Q_UNUSED(menu) Q_UNUSED(view) Q_UNUSED(r) } + + virtual void formSent(const QNetworkRequest &request, const QByteArray &outgoingData) { Q_UNUSED(request) Q_UNUSED(outgoingData)} + virtual void pageLoaded(QWebView* view) { Q_UNUSED(view) } + virtual void downloadRequested(QWidget* requestWidget) { Q_UNUSED(requestWidget) } +}; + + Q_DECLARE_INTERFACE(PluginInterface, "Qupzilla.Browser.PluginInterface/1.0") + + +#endif // PLUGININTERFACE_H diff --git a/src/plugins/pluginproxy.cpp b/src/plugins/pluginproxy.cpp new file mode 100644 index 000000000..bb82b866a --- /dev/null +++ b/src/plugins/pluginproxy.cpp @@ -0,0 +1,70 @@ +#include "pluginproxy.h" +#include "plugininterface.h" +#include "mainapplication.h" + +PluginProxy::PluginProxy() : + Plugins() +{ + c2f_loadSettings(); +} + +void PluginProxy::populateWebViewMenu(QMenu *menu, QWebView *view, QWebHitTestResult r) +{ + if (!menu || !view || loadedPlugins.count() == 0) + return; + + menu->addSeparator(); + int count = menu->actions().count(); + + foreach(PluginInterface* iPlugin, loadedPlugins) + iPlugin->populateWebViewMenu(menu, view, r); + + if (menu->actions().count() == count) + menu->removeAction(menu->actions().at(count)); +} + +void PluginProxy::populateToolsMenu(QMenu *menu) +{ + if (!menu || loadedPlugins.count() == 0) + return; + + int count = menu->actions().count(); + + foreach(PluginInterface* iPlugin, loadedPlugins) + iPlugin->populateToolsMenu(menu); + + if (menu->actions().count() != count) + menu->addSeparator(); +} + +void PluginProxy::populateHelpMenu(QMenu *menu) +{ + if (!menu || loadedPlugins.count() == 0) + return; + + int count = menu->actions().count(); + + foreach(PluginInterface* iPlugin, loadedPlugins) + iPlugin->populateHelpMenu(menu); + + if (menu->actions().count() != count) + menu->addSeparator(); +} + +void PluginProxy::c2f_loadSettings() +{ + QSettings settings(MainApplication::getInstance()->getActiveProfil()+"settings.ini", QSettings::IniFormat); + settings.beginGroup("ClickToFlash"); + c2f_whitelist = settings.value("whitelist", QStringList()).toStringList(); + c2f_enabled = settings.value("Enabled", true).toBool(); + settings.endGroup(); +} + +void PluginProxy::c2f_saveSettings() +{ + QSettings settings(MainApplication::getInstance()->getActiveProfil()+"settings.ini", QSettings::IniFormat); + settings.beginGroup("ClickToFlash"); + settings.setValue("whitelist", c2f_whitelist); + settings.setValue("Enabled", c2f_enabled); + settings.endGroup(); +} diff --git a/src/plugins/pluginproxy.h b/src/plugins/pluginproxy.h new file mode 100644 index 000000000..dacd4015b --- /dev/null +++ b/src/plugins/pluginproxy.h @@ -0,0 +1,34 @@ +#ifndef PLUGINPROXY_H +#define PLUGINPROXY_H +#if defined(QT_NO_DEBUG) & !defined(QT_NO_DEBUG_OUTPUT) +#define QT_NO_DEBUG_OUTPUT +#endif + +#include +#include +#include +#include + +#include "plugins.h" + +class PluginProxy : public Plugins +{ +public: + PluginProxy(); + void populateWebViewMenu(QMenu* menu, QWebView* view, QWebHitTestResult r); + void populateToolsMenu(QMenu* menu); + void populateHelpMenu(QMenu* menu); + + // CLick2Flash + void c2f_loadSettings(); + void c2f_saveSettings(); + void c2f_addWhitelist(QString page) { c2f_whitelist.append(page); } + void c2f_removeWhitelist(QString page) { c2f_whitelist.removeOne(page); } + void c2f_setEnabled(bool en) { c2f_enabled = en; } + bool c2f_isEnabled() { return c2f_enabled; } + QStringList c2f_getWhiteList() { return c2f_whitelist; } + QStringList c2f_whitelist; + bool c2f_enabled; +}; + +#endif // PLUGINPROXY_H diff --git a/src/plugins/plugins.cpp b/src/plugins/plugins.cpp new file mode 100644 index 000000000..1be2ca8a7 --- /dev/null +++ b/src/plugins/plugins.cpp @@ -0,0 +1,68 @@ +#include "pluginproxy.h" +#include "plugininterface.h" +#include "mainapplication.h" + +Plugins::Plugins(QObject *parent) : + QObject(parent) +{ + loadSettings(); +} + +void Plugins::loadSettings() +{ + m_allowedPluginFileNames.clear(); + + QSettings settings(MainApplication::getInstance()->getActiveProfil()+"settings.ini", QSettings::IniFormat); + settings.beginGroup("Plugin-Settings"); + m_pluginsEnabled = settings.value("EnablePlugins", true).toBool(); + m_allowedPluginFileNames = settings.value("AllowedPlugins", QStringList()).toStringList(); + settings.endGroup(); +} + +void Plugins::loadPlugins() +{ + if (!m_pluginsEnabled) + return; + + m_availablePluginFileNames.clear(); + loadedPlugins.clear(); + + QDir pluginsDir = QDir(MainApplication::getInstance()->DATADIR+"plugins/"); + + foreach (QString fileName, pluginsDir.entryList(QDir::Files)) { + m_availablePluginFileNames.append(fileName); + + if (!m_allowedPluginFileNames.contains(fileName)) + continue; + + QPluginLoader loader(pluginsDir.absoluteFilePath(fileName)); + QObject *plugin = loader.instance(); + if (plugin) { + PluginInterface *iPlugin = qobject_cast(plugin); + iPlugin->init(MainApplication::getInstance()->getActiveProfil()+"plugins.ini"); + if (!iPlugin->testPlugin()) { + loader.unload(); + continue; + } + + qApp->installTranslator(iPlugin->getTranslator(MainApplication::getInstance()->getActiveLanguage())); + loadedPlugins.append(iPlugin); + m_loadedPluginFileNames.append(fileName); + } + } + qDebug() << loadedPlugins.count() << "plugins loaded"; +} + +PluginInterface* Plugins::getPlugin(QString pluginFileName) +{ + QString path = MainApplication::getInstance()->DATADIR+"plugins/"+pluginFileName; + if (!QFile::exists(path)) + return 0; + QPluginLoader loader(path); + QObject *plugin = loader.instance(); + if (plugin) { + PluginInterface *iPlugin = qobject_cast(plugin); + return iPlugin; + } else + return 0; +} diff --git a/src/plugins/plugins.h b/src/plugins/plugins.h new file mode 100644 index 000000000..42299afce --- /dev/null +++ b/src/plugins/plugins.h @@ -0,0 +1,41 @@ +#ifndef PLUGINLOADER_H +#define PLUGINLOADER_H +#if defined(QT_NO_DEBUG) & !defined(QT_NO_DEBUG_OUTPUT) +#define QT_NO_DEBUG_OUTPUT +#endif + +#include +#include +#include +#include +#include +#include +#include + +class PluginInterface; +class Plugins : public QObject +{ + Q_OBJECT +public: + explicit Plugins(QObject *parent = 0); + + QStringList getAvailablePlugins() { return m_availablePluginFileNames; } + QStringList getAllowedPlugins () { return m_allowedPluginFileNames; } + PluginInterface* getPlugin(QString pluginFileName); + //void setPluginsAllowed(bool state) { pluginsEnabled = state; qDebug() << state;} + +public slots: + void loadSettings(); + void loadPlugins(); + +protected: + QList loadedPlugins; + +private: + QStringList m_availablePluginFileNames; + QStringList m_allowedPluginFileNames; + QStringList m_loadedPluginFileNames; + bool m_pluginsEnabled; +}; + +#endif // PLUGINLOADER_H diff --git a/src/plugins/webpluginfactory.cpp b/src/plugins/webpluginfactory.cpp new file mode 100644 index 000000000..81f8e8f7a --- /dev/null +++ b/src/plugins/webpluginfactory.cpp @@ -0,0 +1,53 @@ +#include "webpluginfactory.h" +#include "clicktoflash.h" +#include "mainapplication.h" +#include "pluginproxy.h" + +WebPluginFactory::WebPluginFactory(QObject *parent) + : QWebPluginFactory(parent) + ,m_loadClickToFlash(false) +{ + connect(this, SIGNAL(signalLoadClickToFlash(bool)), SLOT(setLoadClickToFlash(bool))); +} + +QObject* WebPluginFactory::create(const QString &mimeType, const QUrl &url, const QStringList &argumentNames, const QStringList &argumentValues) const +{ + Q_UNUSED(argumentNames) + Q_UNUSED(argumentValues) + + if (mimeType != "application/x-shockwave-flash") { + qDebug() << mimeType; + return 0; + } + + if (!MainApplication::getInstance()->plugins()->c2f_isEnabled()) + return 0; + + //Click2Flash whitelist + QStringList whitelist = MainApplication::getInstance()->plugins()->c2f_getWhiteList(); + if (whitelist.contains(url.host()) || whitelist.contains("www."+url.host()) || whitelist.contains(url.host().remove("www."))) + return 0; + + if (m_loadClickToFlash) { + emit signalLoadClickToFlash(false); + return 0; + } else { + ClickToFlash* ctf = new ClickToFlash(url); + connect(ctf, SIGNAL(signalLoadClickToFlash(bool)), this, SLOT(setLoadClickToFlash(bool))); + return ctf; + } +} + +QList WebPluginFactory::plugins() const +{ + QList plugins; + return plugins; +// QWebPluginFactory::Plugin plugin; +// plugin.name = QLatin1String("ClickToFlashPlugin"); // Mmmm, it should return this, +// QWebPluginFactory::MimeType mimeType; // but with WebKit 533.3, click2flash +// mimeType.fileExtensions << QLatin1String("swf"); // fails on some pages, like youtube.com +// mimeType.name = QLatin1String("application/x-shockwave-flash"); // so we will return empty QList +// plugin.mimeTypes.append(mimeType); // On some pages it also force to load non-flash +// plugins.append(plugin); // content -> in most cases advertisements. +// return plugins; // Not bad to have it hidden :-) +} diff --git a/src/plugins/webpluginfactory.h b/src/plugins/webpluginfactory.h new file mode 100644 index 000000000..6a32d1af9 --- /dev/null +++ b/src/plugins/webpluginfactory.h @@ -0,0 +1,25 @@ +#ifndef WEB_PLUGIN_FACTORY_H +#define WEB_PLUGIN_FACTORY_H + +#include +#include + +class WebPluginFactory : public QWebPluginFactory +{ + Q_OBJECT + +public: + WebPluginFactory(QObject *parent); + virtual QObject*create (const QString &mimeType, const QUrl &url, const QStringList &argumentNames, const QStringList &argumentValues) const; + QList plugins() const; + +signals: + void signalLoadClickToFlash(bool) const; + +public slots: + void setLoadClickToFlash(bool load) { m_loadClickToFlash = load; } + +private: + bool m_loadClickToFlash; +}; +#endif // WEB_PLUGIN_FACTORY_H diff --git a/src/preferences/autofillmanager.cpp b/src/preferences/autofillmanager.cpp new file mode 100644 index 000000000..ecdd1ee11 --- /dev/null +++ b/src/preferences/autofillmanager.cpp @@ -0,0 +1,108 @@ +#include "autofillmanager.h" +#include "ui_autofillmanager.h" + +AutoFillManager::AutoFillManager(QWidget *parent) : + QDialog(parent), + ui(new Ui::AutoFillManager) +{ + ui->setupUi(this); + + connect(ui->removePass, SIGNAL(clicked()), this, SLOT(removePass())); + connect(ui->removeAllPass, SIGNAL(clicked()), this, SLOT(removeAllPass())); + connect(ui->editPass, SIGNAL(clicked()), this, SLOT(editPass())); + + connect(ui->removeExcept, SIGNAL(clicked()), this, SLOT(removeExcept())); + connect(ui->removeAllExcept, SIGNAL(clicked()), this, SLOT(removeAllExcept())); + + QTimer::singleShot(0, this, SLOT(loadPasswords())); +} + +void AutoFillManager::loadPasswords() +{ + QSqlQuery query; + query.exec("SELECT server, password, id FROM autofill"); + ui->treePass->clear(); + while(query.next()) { + QTreeWidgetItem* item = new QTreeWidgetItem(ui->treePass); + item->setText(0, query.value(0).toString()); + item->setText(1, query.value(1).toString()); + item->setWhatsThis(1, query.value(2).toString()); + ui->treePass->addTopLevelItem(item); + } + + query.exec("SELECT server, id FROM autofill_exceptions"); + ui->treeExcept->clear(); + while(query.next()) { + QTreeWidgetItem* item = new QTreeWidgetItem(ui->treeExcept); + item->setText(0, query.value(0).toString()); + item->setWhatsThis(0, query.value(1).toString()); + ui->treeExcept->addTopLevelItem(item); + } +} + +void AutoFillManager::removePass() +{ + QTreeWidgetItem* curItem = ui->treePass->currentItem(); + if (!curItem) + return; + QString id = curItem->whatsThis(1); + QSqlQuery query; + query.exec("DELETE FROM autofill WHERE id="+id); + loadPasswords(); +} + +void AutoFillManager::removeAllPass() +{ + QMessageBox::StandardButton button = QMessageBox::warning(this, tr("Confirmation"), + tr("Are you sure to delete all passwords on your computer?"), QMessageBox::Yes | QMessageBox::No); + if (button != QMessageBox::Yes) + return; + + QSqlQuery query; + query.exec("DELETE FROM autofill"); + loadPasswords(); +} + +void AutoFillManager::editPass() +{ + QTreeWidgetItem* curItem = ui->treePass->currentItem(); + if (!curItem) + return; + bool ok; + QString text = QInputDialog::getText(this, tr("Edit password"), tr("Change password:"), QLineEdit::Normal, curItem->text(1), &ok); + + if (ok && !text.isEmpty()) { + QSqlQuery query; + query.exec("UPDATE autofill SET password='"+text+"' WHERE id="+curItem->whatsThis(1)); + loadPasswords(); + } +} + +void AutoFillManager::removeExcept() +{ + QTreeWidgetItem* curItem = ui->treeExcept->currentItem(); + if (!curItem) + return; + QString id = curItem->whatsThis(0); + QSqlQuery query; + qDebug() << id; + query.exec("DELETE FROM autofill_exceptions WHERE id="+id); + loadPasswords(); +} + +void AutoFillManager::removeAllExcept() +{ + QSqlQuery query; + query.exec("DELETE FROM autofill_exceptions"); + loadPasswords(); +} + +void AutoFillManager::showExceptions() +{ + ui->tabWidget->setCurrentIndex(1); +} + +AutoFillManager::~AutoFillManager() +{ + delete ui; +} diff --git a/src/preferences/autofillmanager.h b/src/preferences/autofillmanager.h new file mode 100644 index 000000000..b4d20034d --- /dev/null +++ b/src/preferences/autofillmanager.h @@ -0,0 +1,45 @@ +#ifndef AUTOFILLMANAGER_H +#define AUTOFILLMANAGER_H + +#if defined(QT_NO_DEBUG) & !defined(QT_NO_DEBUG_OUTPUT) +#define QT_NO_DEBUG_OUTPUT +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Ui { + class AutoFillManager; +} + +class AutoFillManager : public QDialog +{ + Q_OBJECT + +public: + explicit AutoFillManager(QWidget *parent = 0); + ~AutoFillManager(); + + void showExceptions(); + +private slots: + void loadPasswords(); + + void removePass(); + void removeAllPass(); + void editPass(); + + void removeExcept(); + void removeAllExcept(); + +private: + Ui::AutoFillManager *ui; +}; + +#endif // AUTOFILLMANAGER_H diff --git a/src/preferences/autofillmanager.ui b/src/preferences/autofillmanager.ui new file mode 100644 index 000000000..2419251f1 --- /dev/null +++ b/src/preferences/autofillmanager.ui @@ -0,0 +1,146 @@ + + + AutoFillManager + + + + 0 + 0 + 524 + 412 + + + + Password Manager + + + + :/icons/qupzilla.png:/icons/qupzilla.png + + + + + + Qt::NoFocus + + + 0 + + + + Passwords + + + + + + 200 + + + + Server + + + + + Password + + + + + + + + + + Remove + + + + + + + Edit + + + + + + + Remove All + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + Exceptions + + + + + + + Server + + + + + + + + + + Remove + + + + + + + Remove All + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + + diff --git a/src/preferences/pluginslist.cpp b/src/preferences/pluginslist.cpp new file mode 100644 index 000000000..91ed28f11 --- /dev/null +++ b/src/preferences/pluginslist.cpp @@ -0,0 +1,175 @@ +#include "pluginslist.h" +#include "ui_pluginslist.h" +#include "pluginproxy.h" +#include "mainapplication.h" +#include "plugininterface.h" + +PluginsList::PluginsList(QWidget *parent) : + QWidget(parent), + ui(new Ui::PluginsList) +{ + ui->setupUi(this); + + //Application Extensions + refresh(); + connect(ui->butSettings, SIGNAL(clicked()), this, SLOT(settingsClicked())); + connect(ui->list, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), this, SLOT(currentChanged(QListWidgetItem*))); + connect(ui->butLoad, SIGNAL(clicked()), this, SLOT(reloadPlugins())); + connect(ui->allowAppPlugins, SIGNAL(clicked(bool)), this, SLOT(allowAppPluginsChanged(bool))); + + QSettings settings(MainApplication::getInstance()->getActiveProfil()+"settings.ini", QSettings::IniFormat); + settings.beginGroup("Plugin-Settings"); + ui->allowAppPlugins->setChecked( settings.value("EnablePlugins",true).toBool() ); + settings.endGroup(); + allowAppPluginsChanged(ui->allowAppPlugins->isChecked()); + + //WebKit Plugins + connect(ui->add, SIGNAL(clicked()), this, SLOT(addWhitelist())); + connect(ui->remove, SIGNAL(clicked()), this, SLOT(removeWhitelist())); + connect(ui->allowClick2Flash, SIGNAL(clicked(bool)), this, SLOT(allowC2FChanged(bool))); + + settings.beginGroup("ClickToFlash"); + QStringList whitelist = MainApplication::getInstance()->plugins()->c2f_getWhiteList(); + ui->allowClick2Flash->setChecked( settings.value("Enable",true).toBool() ); + settings.endGroup(); + foreach (QString site, whitelist) { + QTreeWidgetItem* item = new QTreeWidgetItem(ui->whitelist); + item->setText(0, site); + } + allowC2FChanged(ui->allowClick2Flash->isChecked()); +} + +void PluginsList::addWhitelist() +{ + QString site = QInputDialog::getText(this, tr("Add site to whitelist"), tr("Server without http:// (ex. youtube.com)")); + if (site.isEmpty()) + return; + + MainApplication::getInstance()->plugins()->c2f_addWhitelist(site); + ui->whitelist->insertTopLevelItem(0, new QTreeWidgetItem(QStringList(site))); +} + +void PluginsList::removeWhitelist() +{ + QTreeWidgetItem* item = ui->whitelist->currentItem(); + if (!item) + return; + + MainApplication::getInstance()->plugins()->c2f_removeWhitelist(item->text(0)); + delete item; +} + +void PluginsList::save() +{ + QSettings settings(MainApplication::getInstance()->getActiveProfil()+"settings.ini", QSettings::IniFormat); + settings.beginGroup("Plugin-Settings"); + settings.setValue("EnablePlugins",ui->allowAppPlugins->isChecked()); + settings.endGroup(); + + reloadPlugins(); +} + +void PluginsList::allowAppPluginsChanged(bool state) +{ + QSettings settings(MainApplication::getInstance()->getActiveProfil()+"settings.ini", QSettings::IniFormat); + settings.beginGroup("Plugin-Settings"); + settings.setValue("EnablePlugins", state); + settings.endGroup(); + + ui->verticalFrame->setEnabled(state); +} + +void PluginsList::allowC2FChanged(bool state) +{ + QSettings settings(MainApplication::getInstance()->getActiveProfil()+"settings.ini", QSettings::IniFormat); + settings.beginGroup("ClickToFlash"); + settings.setValue("Enable", state); + settings.endGroup(); + + ui->whitelist->setEnabled(state); + ui->add->setEnabled(state); + ui->remove->setEnabled(state); + + MainApplication::getInstance()->plugins()->c2f_setEnabled(state); +} + +void PluginsList::refresh() +{ + ui->list->clear(); + ui->butSettings->setEnabled(false); + + QStringList availablePlugins = MainApplication::getInstance()->plugins()->getAvailablePlugins(); + QStringList allowedPlugins = MainApplication::getInstance()->plugins()->getAllowedPlugins(); + foreach (QString fileName, availablePlugins) { + PluginInterface* plugin = MainApplication::getInstance()->plugins()->getPlugin(fileName); + if (!plugin) + continue; + + QListWidgetItem* item = new QListWidgetItem(ui->list); + item->setText(""+plugin->pluginName()+" ("+plugin->pluginVersion()+") by "+plugin->pluginAuthor()+"\n" + +plugin->pluginInfo()+"\n"+plugin->pluginDescription() ); + + QIcon icon = plugin->pluginIcon(); + if (icon.isNull()) + icon = QIcon(":/icons/preferences/extension.png"); + item->setIcon(icon); + + item->setFlags(item->flags() | Qt::ItemIsUserCheckable); + item->setCheckState( (allowedPlugins.contains(fileName) ) ? Qt::Checked : Qt::Unchecked); + item->setWhatsThis(plugin->hasSettings() ? "1" : "0"); + item->setToolTip(fileName); + + ui->list->addItem(item); + } +} + +void PluginsList::currentChanged(QListWidgetItem *item) +{ + if (!item) + return; + + QString has = item->whatsThis(); + bool show; + if (has == "1") + show = true; + else + show = false; + + if(item->checkState() == Qt::Unchecked) + show = false; + + ui->butSettings->setEnabled(show); +} + +void PluginsList::settingsClicked() +{ + if (!ui->list->currentItem()) + return; + + QString name = ui->list->currentItem()->toolTip(); + PluginInterface* plugin = MainApplication::getInstance()->plugins()->getPlugin(name); + plugin->showSettings(); +} + +void PluginsList::reloadPlugins() +{ + QStringList allowedPlugins; + for (int i = 0; i < ui->list->count(); i++) { + if (ui->list->item(i)->checkState() == Qt::Checked) + allowedPlugins.append(ui->list->item(i)->toolTip()); + } + QSettings settings(MainApplication::getInstance()->getActiveProfil()+"settings.ini", QSettings::IniFormat); + settings.beginGroup("Plugin-Settings"); + settings.setValue("AllowedPlugins",allowedPlugins); + settings.endGroup(); + + MainApplication::getInstance()->plugins()->loadSettings(); + MainApplication::getInstance()->plugins()->loadPlugins(); + + refresh(); +} + +PluginsList::~PluginsList() +{ + delete ui; +} diff --git a/src/preferences/pluginslist.h b/src/preferences/pluginslist.h new file mode 100644 index 000000000..9b64b3139 --- /dev/null +++ b/src/preferences/pluginslist.h @@ -0,0 +1,42 @@ +#ifndef PLUGINSLIST_H +#define PLUGINSLIST_H +#if defined(QT_NO_DEBUG) & !defined(QT_NO_DEBUG_OUTPUT) +#define QT_NO_DEBUG_OUTPUT +#endif + +#include +#include +#include + +namespace Ui { + class PluginsList; +} + +class PluginsList : public QWidget +{ + Q_OBJECT + +public: + explicit PluginsList(QWidget *parent = 0); + ~PluginsList(); + void save(); + +public slots: + void reloadPlugins(); + +private slots: + //App extension + void settingsClicked(); + void currentChanged(QListWidgetItem* item); + void allowAppPluginsChanged(bool state); + //WebKit plugins + void addWhitelist(); + void removeWhitelist(); + void allowC2FChanged(bool state); + +private: + void refresh(); + Ui::PluginsList *ui; +}; + +#endif // PLUGINSLIST_H diff --git a/src/preferences/pluginslist.ui b/src/preferences/pluginslist.ui new file mode 100644 index 000000000..ca549d51a --- /dev/null +++ b/src/preferences/pluginslist.ui @@ -0,0 +1,185 @@ + + + PluginsList + + + + 0 + 0 + 623 + 462 + + + + Form + + + + + + 0 + + + + Application Extensions + + + + + + Allow Application Extensions to be loaded + + + + + + + + + + true + + + true + + + + + + + + + false + + + + 0 + 0 + + + + Settings + + + + :/icons/preferences/preferences-desktop.png:/icons/preferences/preferences-desktop.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + Load Plugins + + + + :/icons/faenza/reload.png:/icons/faenza/reload.png + + + + + + + + + + + + + WebKit Plugins + + + + + + <b>Click To Flash Plugin</b> + + + + + + + Click To Flash is a plugin which blocks auto loading of Flash content at page. You can always load it manually by clicking on the Flash play icon. + + + true + + + + + + + + + + Whitelist + + + + + + + + Add + + + + + + + Remove + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Allow Click To Flash + + + + + + + + + + + + + + diff --git a/src/preferences/preferences.cpp b/src/preferences/preferences.cpp new file mode 100644 index 000000000..553a54281 --- /dev/null +++ b/src/preferences/preferences.cpp @@ -0,0 +1,470 @@ +#include "preferences.h" +#include "ui_preferences.h" +#include "qupzilla.h" +#include "bookmarkstoolbar.h" +#include "historymodel.h" +#include "tabwidget.h" +#include "cookiejar.h" +#include "locationbar.h" +#include "autofillmanager.h" +#include "mainapplication.h" +#include "cookiemanager.h" +#include "pluginslist.h" +#include "qtwin.h" +#include "pluginproxy.h" + +Preferences::Preferences(QupZilla* mainClass, QWidget *parent) : + QDialog(parent) + ,ui(new Ui::Preferences) + ,p_QupZilla(mainClass) + ,m_pluginsList(0) +{ + ui->setupUi(this); + m_bgLabelSize = this->sizeHint(); + + QSettings settings(MainApplication::getInstance()->getActiveProfil()+"settings.ini", QSettings::IniFormat); + //GENERAL URLs + settings.beginGroup("Web-URL-Settings"); + m_homepage = settings.value("homepage","http://qupzilla.ic.cz/search/").toString(); + m_newTabUrl = settings.value("newTabUrl","").toString(); + ui->homepage->setText(m_homepage); + ui->newTabUrl->setText(m_newTabUrl); + int afterLaunch = settings.value("afterLaunch",1).toInt(); + ui->afterLaunch->setCurrentIndex(afterLaunch); + + ui->newTabFrame->setVisible(false); + if (m_newTabUrl.isEmpty()) + ui->newTab->setCurrentIndex(0); + else if (m_newTabUrl == m_homepage) + ui->newTab->setCurrentIndex(1); + else{ + ui->newTab->setCurrentIndex(2); + ui->newTabFrame->setVisible(true); + } + connect(ui->newTab, SIGNAL(currentIndexChanged(int)), this, SLOT(newTabChanged())); + connect(ui->useActualBut, SIGNAL(clicked()), this, SLOT(useActualHomepage())); + connect(ui->newTabUseActual, SIGNAL(clicked()), this, SLOT(useActualNewTab())); + + //PROFILES + ui->startPRofile->setEnabled(false); + ui->createProfile->setEnabled(false); + ui->deleteProfile->setEnabled(false); + settings.endGroup(); + + //WINDOW + settings.beginGroup("Browser-View-Settings"); + ui->showStatusbar->setChecked( settings.value("showStatusBar",true).toBool() ); + ui->showBookmarksToolbar->setChecked( p_QupZilla->bookmarksToolbar()->isVisible() ); + ui->showNavigationToolbar->setChecked( p_QupZilla->navigationToolbar()->isVisible() ); + ui->showHome->setChecked( settings.value("showHomeButton",true).toBool() ); + ui->showBackForward->setChecked( settings.value("showBackForwardButtons",true).toBool() ); + if (settings.value("useTransparentBackground",false).toBool()) + ui->useTransparentBg->setChecked(true); + else + ui->useBgImage->setChecked(true); + + m_menuTextColor = settings.value("menuTextColor", QColor(Qt::black)).value(); + ui->textColor->setStyleSheet("color: "+m_menuTextColor.name()+";"); + useBgImageChanged(ui->useBgImage->isChecked()); + settings.endGroup(); +#ifdef Q_WS_WIN + ui->useTransparentBg->setEnabled(QtWin::isCompositionEnabled()); +#endif + connect(ui->useBgImage, SIGNAL(toggled(bool)), this, SLOT(useBgImageChanged(bool))); + connect(ui->backgroundButton, SIGNAL(clicked()), this, SLOT(chooseBackgroundPath())); + connect(ui->resetDefaultBgButton, SIGNAL(clicked()), this, SLOT(resetBackground())); + connect(ui->textColorChooser, SIGNAL(clicked()), this, SLOT(chooseColor())); + updateBgLabel(); + + //TABS + settings.beginGroup("Browser-Tabs-Settings"); + ui->makeMovable->setChecked( settings.value("makeTabsMovable",true).toBool() ); + ui->hideCloseOnTab->setChecked( settings.value("hideCloseButtonWithOneTab",false).toBool() ); + ui->hideTabsOnTab->setChecked( settings.value("hideTabsWithOneTab",false).toBool() ); + ui->activateLastTab->setChecked( settings.value("ActivateLastTabWhenClosingActual", false).toBool() ); + settings.endGroup(); + //AddressBar + settings.beginGroup("AddressBar"); + ui->selectAllOnFocus->setChecked( settings.value("SelectAllTextOnDoubleClick",true).toBool() ); + ui->addComWithCtrl->setChecked( settings.value("AddComDomainWithCtrlKey",false).toBool() ); + ui->addCountryWithAlt->setChecked( settings.value("AddCountryDomainWithAltKey",true).toBool() ); + settings.endGroup(); + + //BROWSING + settings.beginGroup("Web-Browser-Settings"); + ui->allowPlugins->setChecked( settings.value("allowFlash",true).toBool() ); + ui->allowJavaScript->setChecked( settings.value("allowJavaScript",true).toBool() ); + ui->blockPopup->setChecked( !settings.value("allowJavaScriptOpenWindow", false).toBool() ); + ui->allowJava->setChecked( settings.value("allowJava",true).toBool() ); + ui->loadImages->setChecked( settings.value("autoLoadImages",true).toBool() ); + ui->allowDNSPrefetch->setChecked( settings.value("DNS-Prefetch", false).toBool() ); + ui->jscanAccessClipboard->setChecked( settings.value("JavaScriptCanAccessClipboard", true).toBool() ); + ui->linksInFocusChain->setChecked( settings.value("IncludeLinkInFocusChain", false).toBool() ); + ui->zoomTextOnly->setChecked( settings.value("zoomTextOnly", false).toBool() ); + ui->printEBackground->setChecked( settings.value("PrintElementBackground", true).toBool() ); + ui->wheelScroll->setValue( settings.value("wheelScrollLines", qApp->wheelScrollLines()).toInt() ); + + if (!ui->allowJavaScript->isChecked()) + ui->blockPopup->setEnabled(false); + connect(ui->allowJavaScript, SIGNAL(toggled(bool)), this, SLOT(allowJavaScriptChanged(bool))); + //Cache + ui->pagesInCache->setValue( settings.value("maximumCachedPages",3).toInt() ); + connect(ui->pagesInCache, SIGNAL(valueChanged(int)), this, SLOT(pageCacheValueChanged(int))); + ui->pageCacheLabel->setText(QString::number(ui->pagesInCache->value())); + + ui->allowCache->setChecked( settings.value("AllowLocalCache",true).toBool() ); + ui->cacheMB->setValue( settings.value("LocalCacheSize", 50).toInt() ); + ui->MBlabel->setText( settings.value("LocalCacheSize", 50).toString() + " MB"); + connect(ui->allowCache, SIGNAL(clicked(bool)), this, SLOT(allowCacheChanged(bool))); + connect(ui->cacheMB, SIGNAL(valueChanged(int)), this, SLOT(cacheValueChanged(int)) ); + allowCacheChanged(ui->allowCache->isChecked()); + + //PASSWORD MANAGER + ui->allowPassManager->setChecked(settings.value("AutoFillForms",true).toBool()); + connect(ui->allowPassManager, SIGNAL(toggled(bool)), this, SLOT(showPassManager(bool))); + + m_autoFillManager = new AutoFillManager(this); + ui->autoFillFrame->addWidget(m_autoFillManager); + + //PRIVACY + //Web storage + ui->storeIcons->setChecked( settings.value("allowPersistentStorage",true).toBool() ); + ui->saveHistory->setChecked( p_QupZilla->getMainApp()->history()->isSaving() ); + ui->deleteHistoryOnClose->setChecked( settings.value("deleteHistoryOnClose",false).toBool() ); + if (!ui->saveHistory->isChecked()) + ui->deleteHistoryOnClose->setEnabled(false); + connect(ui->saveHistory, SIGNAL(toggled(bool)), this, SLOT(saveHistoryChanged(bool))); + //Cookies + ui->saveCookies->setChecked( settings.value("allowCookies",true).toBool() ); + if (!ui->saveCookies->isChecked()) + ui->deleteCookiesOnClose->setEnabled(false); + connect(ui->saveCookies, SIGNAL(toggled(bool)), this, SLOT(saveCookiesChanged(bool))); + ui->deleteCookiesOnClose->setChecked( settings.value("deleteCookiesOnClose", false).toBool() ); + ui->matchExactly->setChecked( settings.value("allowCookiesFromVisitedDomainOnly",false).toBool() ); + ui->filterTracking->setChecked( settings.value("filterTrackingCookie",false).toBool() ); + settings.endGroup(); + + //DOWNLOADS + settings.beginGroup("DownloadManager"); + ui->downLoc->setText( settings.value("defaultDownloadPath","").toString() ); + if (ui->downLoc->text().isEmpty()) + ui->askEverytime->setChecked(true); + else + ui->useDefined->setChecked(true); + connect(ui->useDefined, SIGNAL(toggled(bool)), this, SLOT(downLocChanged(bool))); + ui->closeDownDialogOnFinish->setChecked( settings.value("autoCloseOnFinish",false).toBool() ); + connect(ui->downButt, SIGNAL(clicked()), this, SLOT(chooseDownPath())); + downLocChanged(ui->useDefined->isChecked()); + settings.endGroup(); + + //PLUGINS + m_pluginsList = new PluginsList(this); + ui->pluginsFrame->addWidget(m_pluginsList); + + //OTHER + //Languages + QString activeLanguage=""; + if (!p_QupZilla->activeLanguage().isEmpty()) { + activeLanguage = p_QupZilla->activeLanguage(); + QString loc = activeLanguage; + loc.remove(".qm"); + QLocale locale(loc); + QString country = QLocale::countryToString(locale.country()); + QString language = QLocale::languageToString(locale.language()); + ui->languages->addItem(language+", "+country+" ("+loc+")", activeLanguage); + } + ui->languages->addItem("English (en_US)"); + + QDir lanDir(MainApplication::getInstance()->DATADIR+"locale"); + QStringList list = lanDir.entryList(QStringList("*.qm")); + foreach(QString name, list) { + if (name.startsWith("qt_") || name == activeLanguage) + continue; + + QString loc = name; + loc.remove(".qm"); + QLocale locale(loc); + QString country = QLocale::countryToString(locale.country()); + QString language = QLocale::languageToString(locale.language()); + ui->languages->addItem(language+", "+country+" ("+loc+")", name); + } + + connect(ui->buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(buttonClicked(QAbstractButton*))); + connect(ui->cookieManagerBut, SIGNAL(clicked()), this, SLOT(showCookieManager())); + + connect(ui->listWidget, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), this, SLOT(showStackedPage(QListWidgetItem*))); + ui->listWidget->setItemSelected(ui->listWidget->itemAt(5,5), true); + + ui->version->setText(" QupZilla v"+QupZilla::VERSION); +} + +void Preferences::showStackedPage(QListWidgetItem *item) +{ + if (!item) + return; + ui->caption->setText(""+item->text()+""); + ui->stackedWidget->setCurrentIndex(item->whatsThis().toInt()); +} + +void Preferences::chooseColor() +{ + m_menuTextColor = QColorDialog::getColor(Qt::black, this); + ui->textColor->setStyleSheet("color: "+m_menuTextColor.name()+";"); +} + +void Preferences::allowCacheChanged(bool state) +{ + ui->cacheFrame->setEnabled(state); + ui->cacheMB->setEnabled(state); +} + +void Preferences::useActualHomepage() +{ + ui->homepage->setText(p_QupZilla->weView()->url().toString()); +} + +void Preferences::useActualNewTab() +{ + ui->newTabUrl->setText(p_QupZilla->weView()->url().toString()); +} + +void Preferences::resetBackground() +{ + QFile::remove(p_QupZilla->activeProfil()+"background.png"); + QFile(MainApplication::getInstance()->DATADIR+"data/default/profiles/default/background.png").copy(p_QupZilla->activeProfil()+"background.png"); + + m_menuTextColor = QColor(Qt::black); + ui->textColor->setStyleSheet("color: "+m_menuTextColor.name()+";"); + + updateBgLabel(); +} + +void Preferences::updateBgLabel() +{ + ui->bgLabel->setStyleSheet("#bgLabel {background: url("+p_QupZilla->activeProfil()+"background.png) top right;}"); +} + +void Preferences::chooseDownPath() +{ + QString userFileName = QFileDialog::getExistingDirectory(p_QupZilla, tr("Choose download location..."), QDir::homePath()); + if (userFileName.isEmpty()) + return; + ui->downLoc->setText(userFileName); +} + +void Preferences::chooseBackgroundPath() +{ + QString file = QFileDialog::getOpenFileName(p_QupZilla, tr("Choose background location..."), QDir::homePath(), "*.png"); + if (file.isEmpty()) + return; + QFile::remove(p_QupZilla->activeProfil()+"background.png"); + QFile(file).copy(p_QupZilla->activeProfil()+"background.png"); + + updateBgLabel(); +} + +void Preferences::newTabChanged() +{ + if (ui->newTab->currentIndex() == 2) + ui->newTabFrame->setVisible(true); + else + ui->newTabFrame->setVisible(false); +} + +void Preferences::useBgImageChanged(bool state) +{ + ui->bgLabel->setEnabled(state); +} + +void Preferences::downLocChanged(bool state) +{ + ui->downButt->setEnabled(state); + ui->downLoc->setEnabled(state); +} + +void Preferences::allowJavaScriptChanged(bool stat) +{ + ui->blockPopup->setEnabled(stat); +} + +void Preferences::saveHistoryChanged(bool stat) +{ + ui->deleteHistoryOnClose->setEnabled(stat); +} + +void Preferences::saveCookiesChanged(bool stat) +{ + ui->deleteCookiesOnClose->setEnabled(stat); +} + +void Preferences::showCookieManager() +{ + CookieManager* m = new CookieManager(); + m->refreshTable(); + m->setAttribute(Qt::WA_DeleteOnClose); + m->setWindowModality(Qt::WindowModal); + m->show(); +} + +void Preferences::cacheValueChanged(int value) +{ + ui->MBlabel->setText(QString::number(value) + " MB"); + if (value == 0) { + ui->allowCache->setChecked(false); + allowCacheChanged(false); + } + else if (!ui->allowCache->isChecked()) { + ui->allowCache->setChecked(true); + allowCacheChanged(true); + } +} + +void Preferences::pageCacheValueChanged(int value) +{ + ui->pageCacheLabel->setText(QString::number(value)); +} + +void Preferences::showPassManager(bool state) +{ + m_autoFillManager->setVisible(state); +} + +void Preferences::buttonClicked(QAbstractButton *button) +{ + switch (ui->buttonBox->buttonRole(button)) { + case QDialogButtonBox::ApplyRole: + saveSettings(); + break; + + case QDialogButtonBox::RejectRole: + close(); + break; + + case QDialogButtonBox::AcceptRole: + saveSettings(); + close(); + break; + + default: + break; + } +} + +void Preferences::saveSettings() +{ + QSettings settings(MainApplication::getInstance()->getActiveProfil()+"settings.ini", QSettings::IniFormat); + //GENERAL URLs + settings.beginGroup("Web-URL-Settings"); + settings.setValue("homepage",ui->homepage->text()); + + QString homepage = ui->homepage->text(); + settings.setValue("afterLaunch",ui->afterLaunch->currentIndex() ); + + + if (ui->newTab->currentIndex() == 0) + settings.setValue("newTabUrl",""); + else if (ui->newTab->currentIndex() == 1) + settings.setValue("newTabUrl",homepage); + else + settings.setValue("newTabUrl",ui->newTabUrl->text()); + + settings.endGroup(); + //PROFILES + /* + * + * + * + */ + + //WINDOW + settings.beginGroup("Browser-View-Settings"); + + settings.setValue("showStatusbar",ui->showStatusbar->isChecked()); + settings.setValue("showBookmarksToolbar", ui->showBookmarksToolbar->isChecked()); + settings.setValue("showNavigationToolbar", ui->showNavigationToolbar->isChecked()); + settings.setValue("showHomeButton", ui->showHome->isChecked()); + settings.setValue("showBackForwardButtons",ui->showBackForward->isChecked()); + settings.setValue("useTransparentBackground", ui->useTransparentBg->isChecked()); + settings.setValue("menuTextColor", m_menuTextColor); + settings.endGroup(); + + //TABS + settings.beginGroup("Browser-Tabs-Settings"); + settings.setValue("makeTabsMovable",ui->makeMovable->isChecked() ); + settings.setValue("hideCloseButtonWithOneTab",ui->hideCloseOnTab->isChecked()); + settings.setValue("hideTabsWithOneTab",ui->hideTabsOnTab->isChecked() ); + settings.setValue("ActivateLastTabWhenClosingActual", ui->activateLastTab->isChecked()); + settings.endGroup(); + //Downloads + settings.beginGroup("DownloadManager"); + if (ui->askEverytime->isChecked()) + settings.setValue("defaultDownloadPath",""); + else{ + QString text = ui->downLoc->text(); + if (!text.endsWith("/")) + text+="/"; + settings.setValue("defaultDownloadPath",text); + } + settings.setValue("autoCloseOnFinish",ui->closeDownDialogOnFinish->isChecked()); + settings.endGroup(); + + //BROWSING + settings.beginGroup("Web-Browser-Settings"); + settings.setValue("allowFlash",ui->allowPlugins->isChecked()); + settings.setValue("allowJavaScript",ui->allowJavaScript->isChecked()); + settings.setValue("allowJavaScriptOpenWindow", !ui->blockPopup->isChecked()); + settings.setValue("allowJava",ui->allowJava->isChecked()); + settings.setValue("autoLoadImages",ui->loadImages->isChecked()); + settings.setValue("maximumCachedPages",ui->pagesInCache->value()); + settings.setValue("DNS-Prefetch", ui->allowDNSPrefetch->isChecked()); + settings.setValue("JavaScriptCanAccessClipboard", ui->jscanAccessClipboard->isChecked()); + settings.setValue("IncludeLinkInFocusChain", ui->linksInFocusChain->isChecked()); + settings.setValue("zoomTextOnly", ui->zoomTextOnly->isChecked()); + settings.setValue("PrintElementBackground", ui->printEBackground->isChecked()); + settings.setValue("wheelScrollLines", ui->wheelScroll->value()); + //Cache + settings.setValue("AllowLocalCache", ui->allowCache->isChecked()); + settings.setValue("LocalCacheSize", ui->cacheMB->value()); + + //PRIVACY + //Web storage + settings.setValue("allowPersistentStorage", ui->storeIcons->isChecked()); + //ui->saveHistory->setChecked( p_QupZilla->history->isSaving() ); + settings.setValue("deleteHistoryOnClose",ui->deleteHistoryOnClose->isChecked()); + + //Cookies + settings.setValue("allowCookies",ui->saveCookies->isChecked()); + settings.setValue("deleteCookiesOnClose", ui->deleteCookiesOnClose->isChecked()); + settings.setValue("allowCookiesFromVisitedDomainOnly",ui->matchExactly->isChecked() ); + settings.setValue("filterTrackingCookie",ui->filterTracking->isChecked() ); + settings.endGroup(); + + //OTHER + //AddressBar + settings.beginGroup("AddressBar"); + settings.setValue("SelectAllTextOnDoubleClick",ui->selectAllOnFocus->isChecked() ); + settings.setValue("AddComDomainWithCtrlKey",ui->addComWithCtrl->isChecked() ); + settings.setValue("AddCountryDomainWithAltKey", ui->addCountryWithAlt->isChecked() ); + settings.endGroup(); + //Languages + settings.beginGroup("Browser-Window-Settings"); + settings.setValue("language",ui->languages->itemData(ui->languages->currentIndex()).toString()); + settings.endGroup(); + + m_pluginsList->save(); + p_QupZilla->loadSettings(); + p_QupZilla->tabWidget()->loadSettings(); + p_QupZilla->getMainApp()->cookieJar()->loadSettings(); + p_QupZilla->getMainApp()->history()->loadSettings(); + p_QupZilla->locationBar()->loadSettings(); + MainApplication::getInstance()->loadSettings(); + MainApplication::getInstance()->plugins()->c2f_saveSettings(); +} + +Preferences::~Preferences() +{ + qDebug() << __FUNCTION__ << "called"; + delete ui; + delete m_autoFillManager; + delete m_pluginsList; +} diff --git a/src/preferences/preferences.h b/src/preferences/preferences.h new file mode 100644 index 000000000..2823e27ac --- /dev/null +++ b/src/preferences/preferences.h @@ -0,0 +1,69 @@ +#ifndef PREFERENCES_H +#define PREFERENCES_H + +#if defined(QT_NO_DEBUG) & !defined(QT_NO_DEBUG_OUTPUT) +#define QT_NO_DEBUG_OUTPUT +#endif + +#include +#include +#include +#include +#include + +namespace Ui { + class Preferences; +} + +class AutoFillManager; +class QupZilla; +class PluginsList; + +class Preferences : public QDialog +{ + Q_OBJECT + +public: + explicit Preferences(QupZilla* mainClass, QWidget *parent = 0); + ~Preferences(); + +private slots: + void saveSettings(); + void buttonClicked(QAbstractButton* button); + + void showStackedPage(QListWidgetItem* item); + void newTabChanged(); + void chooseDownPath(); + void showCookieManager(); + void chooseBackgroundPath(); + void useActualHomepage(); + void useActualNewTab(); + void resetBackground(); + void chooseColor(); + + void allowJavaScriptChanged(bool stat); + void saveHistoryChanged(bool stat); + void saveCookiesChanged(bool stat); + void downLocChanged(bool state); + void allowCacheChanged(bool state); + void showPassManager(bool state); + void useBgImageChanged(bool state); + void cacheValueChanged(int value); + void pageCacheValueChanged(int value); + +private: + void updateBgLabel(); + Ui::Preferences *ui; + QupZilla* p_QupZilla; + AutoFillManager* m_autoFillManager; + PluginsList* m_pluginsList; + + QColor m_menuTextColor; + QString m_homepage; + QString m_newTabUrl; + int m_afterLaunch; + int m_onNewTab; + QSize m_bgLabelSize; +}; + +#endif // PREFERENCES_H diff --git a/src/preferences/preferences.ui b/src/preferences/preferences.ui new file mode 100644 index 000000000..17e846c2f --- /dev/null +++ b/src/preferences/preferences.ui @@ -0,0 +1,1523 @@ + + + Preferences + + + + 0 + 0 + 754 + 483 + + + + Preferences + + + + :/icons/qupzilla.png:/icons/qupzilla.png + + + + + + + 200 + 16777215 + + + + false + + + + 32 + 32 + + + + QListView::Static + + + 2 + + + true + + + true + + + false + + + + General + + + 0 + + + + :/icons/preferences/preferences-desktop.png:/icons/preferences/preferences-desktop.png + + + + + Window + + + 1 + + + + :/icons/preferences/gnome-window-manager.png:/icons/preferences/gnome-window-manager.png + + + + + Tabs + + + 2 + + + + :/icons/preferences/applications-internet.png:/icons/preferences/applications-internet.png + + + + + Browsing + + + 3 + + + + :/icons/preferences/applications-webbrowsers.png:/icons/preferences/applications-webbrowsers.png + + + + + Downloads + + + 4 + + + + :/icons/preferences/stock_inbox.png:/icons/preferences/stock_inbox.png + + + + + Password Manager + + + 5 + + + + :/icons/preferences/contact-new.png:/icons/preferences/contact-new.png + + + + + Privacy + + + 6 + + + + :/icons/preferences/history_entry.png:/icons/preferences/history_entry.png + + + + + Plugins + + + 7 + + + + :/icons/preferences/extension.png:/icons/preferences/extension.png + + + + + Other + + + 8 + + + + :/icons/preferences/applications-system.png:/icons/preferences/applications-system.png + + + + + + + + <b>General</b> + + + + + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + + + + + QupZilla + + + + + + + QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + Qt::NoFocus + + + 0 + + + + + + + <b>Launching</b> + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + After launch: + + + + + + + + Open blank page + + + + + Open homepage + + + + + Restore session + + + + + + + + Homepage: + + + + + + + + + + + + Use actual + + + + + + + + + On new tab: + + + + + + + + Open blank tab + + + + + Open homepage + + + + + Open other page... + + + + + + + + <b>Profiles</b> + + + + + + + Startup profile: + + + + + + + + default + + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 20 + + + 0 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + Create New + + + + + + + + 0 + 0 + + + + Delete + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Expanding + + + + 20 + 40 + + + + + + + + + 0 + + + + + + + + Use actual + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + <b>Browser Window</b> + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + Show StatusBar on start + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Show Bookmarks ToolBar on start + + + + + + + Show Navigation ToolBar on start + + + + + + + <b>Navigation ToolBar</b> + + + + + + + Show Home button + + + + + + + Show Back / Forward buttons + + + + + + + <b>Background<b/> + + + + + + + Use transparent background + + + + + + + Use background image + + + + + + + + 0 + 0 + + + + + + + + 9 + + + 0 + + + + + Reset default + + + + + + + Choose image... + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + This is text color used in Menu + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + + + Menu text color + + + + + + + + + + + <b>Tabs behavior</b> + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + Make tabs movable + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Hide close button if there is only one tab + + + + + + + Hide tabs when if there is only one tab + + + + + + + <b>Address Bar behaviour</b> + + + + + + + Select all text by double clicking in address bar + + + + + + + Add .com domain by pressing CTRL key + + + + + + + Add .co.uk domain by pressing ALT key + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Activate last tab when closing active tab + + + + + + + + + + + + + Load images + + + + + + + Allow JAVA + + + + + + + Allow JavaScript + + + + + + + <b>WebKit</b> + + + + + + + Allow Plugins (Flash plugin) + + + + + + + Block PopUp windows + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + Allow DNS Prefetch + + + + + + + JavaScript can access clipboard + + + + + + + Include links in focus chain + + + + + + + Zoom text only + + + + + + + Print element background + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Wheel scrolls + + + + + + + 1 + + + + + + + lines + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + <b>Network Cache</b> + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + Maximum pages in cache: + + + + + + + + 0 + 0 + + + + + 20 + 0 + + + + 1 + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 6 + + + 0 + + + 6 + + + + + 20 + + + Qt::Horizontal + + + + + + + + + + Allow storing network cache on disk + + + + + + + + + + Maximum + + + + + + + 50 MB + + + + + + + + + + 10 + + + 50 + + + Qt::Horizontal + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + <b>Downloads</b> + + + + + + + Automatically close download dialog after finish + + + + + + + Ask everytime for download location + + + + + + + Use defined location: + + + + + + + + + + + + ... + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + + + + + <b>AutoFill options</b> + + + + + + + Allow saving passwords from sites + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + QLayout::SetMinAndMaxSize + + + + + + + + + + + + + + <b>Cookies</b> + + + + + + + Filter Tracking Cookies + + + + + + + Allow storing of cookies + + + + + + + Delete cookies on close + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Match domain exactly + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + <b>Warning:</b> Match domain exactly and Filter Tracking Cookies options can lead to deny some cookies from sites. If you have problems with cookies, try to disable this options first! + + + true + + + + + + + + 0 + 0 + + + + Cookies Manager + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + <b>Web storage</b> + + + + + + + Allow storing web icons + + + + + + + Allow saving history + + + + + + + Delete history on close + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + 0 + 0 + + + + + 0 + + + + + + + + + + + + <b>Language</b> + + + + + + + Available translations: + + + + + + + + + + In order to change language, you must restart browser. + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + SqueezeLabel + QLabel +
    squeezelabel.h
    +
    +
    + + + + +
    diff --git a/src/rss/rssmanager.cpp b/src/rss/rssmanager.cpp new file mode 100644 index 000000000..69d87c4cb --- /dev/null +++ b/src/rss/rssmanager.cpp @@ -0,0 +1,305 @@ +#include "rssmanager.h" +#include "ui_rssmanager.h" +#include "qupzilla.h" +#include "locationbar.h" +#include "tabwidget.h" +#include "mainapplication.h" +#include "treewidget.h" + +RSSManager::RSSManager(QupZilla* mainClass, QWidget *parent) : + QWidget(parent) + ,ui(new Ui::RSSManager) + ,p_QupZilla(mainClass) +{ + ui->setupUi(this); +// CENTER on scren + const QRect screen = QApplication::desktop()->screenGeometry(); + const QRect &size = geometry(); + QWidget::move( (screen.width()-size.width())/2, (screen.height()-size.height())/2 ); + + ui->tabWidget->setElideMode(Qt::ElideRight); + m_networkManager = new QNetworkAccessManager(); + connect(ui->buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(close())); + connect(ui->reload, SIGNAL(clicked()), this, SLOT(reloadFeed())); + connect(ui->deletebutton, SIGNAL(clicked()), this, SLOT(deleteFeed())); + connect(ui->edit, SIGNAL(clicked()), this, SLOT(editFeed())); +} + +QupZilla* RSSManager::getQupZilla() +{ + if (!p_QupZilla) + p_QupZilla = MainApplication::getInstance()->getWindow(); + return p_QupZilla; +} + +void RSSManager::setMainWindow(QupZilla *window) +{ + if (window) + p_QupZilla = window; +} + +void RSSManager::refreshTable() +{ + QSqlQuery query; + query.exec("SELECT count(id) FROM rss"); + if (!query.next()) + return; + if (query.value(0).toInt() == 0) + return; + + ui->tabWidget->clear(); + query.exec("SELECT address, title FROM rss"); + int i = 0; + while (query.next()) { + QUrl address = query.value(0).toUrl(); + QString title = query.value(1).toString(); + TreeWidget* tree = new TreeWidget(); + tree->setHeaderLabel(tr("News")); + tree->setContextMenuPolicy(Qt::CustomContextMenu); + connect(tree, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(customContextMenuRequested(const QPoint &))); + + ui->tabWidget->addTab(tree, title); + ui->tabWidget->setTabToolTip(i, address.toString()); + connect(tree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(loadFeed(QTreeWidgetItem*))); + connect(tree, SIGNAL(itemControlClicked(QTreeWidgetItem*)), this, SLOT(controlLoadFeed(QTreeWidgetItem*))); + QTreeWidgetItem* item = new QTreeWidgetItem(); + item->setText(0, tr("Loading...")); + tree->addTopLevelItem(item); + + QIcon icon = LocationBar::icon(address); + + if (icon.pixmap(16,16).toImage() == QIcon(":/icons/locationbar/unknownpage.png").pixmap(16,16).toImage()) + icon = QIcon(":/icons/menu/rss.png"); + ui->tabWidget->setTabIcon(i, icon ); + beginToLoadSlot(address); + i++; + } + if (i > 0) { + ui->deletebutton->setEnabled(true); + ui->reload->setEnabled(true); + ui->edit->setEnabled(true); + } +} + +void RSSManager::reloadFeed() +{ + TreeWidget* treeWidget = qobject_cast(ui->tabWidget->widget(ui->tabWidget->currentIndex())); + if (!treeWidget) + return; + treeWidget->clear(); + QTreeWidgetItem* item = new QTreeWidgetItem(); + item->setText(0, tr("Loading...")); + treeWidget->addTopLevelItem(item); + + beginToLoadSlot( QUrl(ui->tabWidget->tabToolTip(ui->tabWidget->currentIndex())) ); +} + +void RSSManager::deleteFeed() +{ + QString url = ui->tabWidget->tabToolTip(ui->tabWidget->currentIndex()); + if (url.isEmpty()) + return; + QSqlQuery query; + query.exec("DELETE FROM rss WHERE address='"+url+"'"); + + ui->tabWidget->removeTab(ui->tabWidget->currentIndex()); + if (ui->tabWidget->count() == 0) { + ui->deletebutton->setEnabled(false); + ui->reload->setEnabled(false); + ui->edit->setEnabled(false); + refreshTable(); + } +} + +void RSSManager::editFeed() +{ + QString url = ui->tabWidget->tabToolTip(ui->tabWidget->currentIndex()); + if (url.isEmpty()) + return; + + QDialog* dialog = new QDialog(this); + QFormLayout* layout = new QFormLayout(dialog); + QLabel* label = new QLabel(dialog); + QLineEdit* editUrl = new QLineEdit(dialog); + QLineEdit* editTitle = new QLineEdit(dialog); + QDialogButtonBox* box = new QDialogButtonBox(dialog); + box->addButton(QDialogButtonBox::Ok); + box->addButton(QDialogButtonBox::Cancel); + connect(box, SIGNAL(rejected()), dialog, SLOT(reject())); + connect(box, SIGNAL(accepted()), dialog, SLOT(accept())); + + label->setText(tr("Fill title and URL of a feed: ")); + layout->addRow(label); + layout->addRow(new QLabel(tr("Feed title: ")), editTitle); + layout->addRow(new QLabel(tr("Feed URL: ")), editUrl); + layout->addRow(box); + + editUrl->setText( ui->tabWidget->tabToolTip(ui->tabWidget->currentIndex()) ); + editTitle->setText( ui->tabWidget->tabText(ui->tabWidget->currentIndex()) ); + + dialog->setWindowTitle(tr("Edit RSS Feed")); + dialog->setMinimumSize(400, 100); + dialog->exec(); + if (dialog->result() == QDialog::Rejected) + return; + + QString address = editUrl->text(); + QString title = editTitle->text(); + + if (address.isEmpty() || title.isEmpty()) + return; + + QSqlQuery query; + query.prepare("UPDATE rss SET address=?, title=? WHERE address=?"); + query.bindValue(0, address); + query.bindValue(1, title); + query.bindValue(2, url); + query.exec(); + + refreshTable(); + +} + +void RSSManager::customContextMenuRequested(const QPoint &position) +{ + TreeWidget* treeWidget = qobject_cast(ui->tabWidget->widget(ui->tabWidget->currentIndex())); + if (!treeWidget) + return; + + if (!treeWidget->itemAt(position)) + return; + + QString link = treeWidget->itemAt(position)->toolTip(0); + if (link.isEmpty()) + return; + + QMenu menu; + menu.addAction(tr("Open link in actual tab"), getQupZilla(), SLOT(loadActionUrl()))->setData(link); + menu.addAction(tr("Open link in new tab"), this, SLOT(loadFeedInNewTab()))->setData(link); + menu.addSeparator(); + menu.addAction(tr("Close"), this, SLOT(close())); + + //Prevent choosing first option with double rightclick + QPoint pos = QCursor::pos(); + QPoint p(pos.x(), pos.y()+1); + menu.exec(p); +} + +void RSSManager::loadFeed(QTreeWidgetItem *item) +{ + if (!item) + return; + if (item->whatsThis(0).isEmpty()) + return; + getQupZilla()->loadAddress(QUrl(item->whatsThis(0))); +} + +void RSSManager::controlLoadFeed(QTreeWidgetItem *item) +{ + if (!item) + return; + if (item->whatsThis(0).isEmpty()) + return; + getQupZilla()->tabWidget()->addView(QUrl(item->whatsThis(0)), tr("New Tab"), TabWidget::NewNotSelectedTab); +} + +void RSSManager::loadFeedInNewTab() +{ + if (QAction *action = qobject_cast(sender())) + getQupZilla()->tabWidget()->addView(action->data().toUrl(), tr("New Tab"), TabWidget::NewNotSelectedTab); +} + +void RSSManager::beginToLoadSlot(const QUrl &url) +{ + QNetworkReply* reply; + reply=m_networkManager->get(QNetworkRequest(QUrl(url))); + + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(finished(QNetworkReply *))); +} + +void RSSManager::finished(QNetworkReply *reply) +{ + if (m_networkReplies.contains(reply)) + return; + + QString currentTag; + QString linkString; + QString titleString; + QXmlStreamReader xml; + xml.addData(reply->readAll()); + + int tabIndex = -1; + for (int i=0; itabWidget->count(); i++) { + QString replyUrl = reply->url().toString(); + if (replyUrl == ui->tabWidget->tabToolTip(i)) { + tabIndex = i; + break; + } + } + + if (tabIndex == -1) + return; + + TreeWidget* treeWidget = qobject_cast(ui->tabWidget->widget(tabIndex)); + if (!treeWidget) + return; + treeWidget->clear(); + + while (!xml.atEnd()) { + xml.readNext(); + if (xml.isStartElement()) { + if (xml.name() == "item") + linkString = xml.attributes().value("rss:about").toString(); + currentTag = xml.name().toString(); + } else if (xml.isEndElement()) { + if (xml.name() == "item") { + QTreeWidgetItem *item = new QTreeWidgetItem; + item->setText(0, titleString); + item->setWhatsThis(0, linkString); + item->setIcon(0, QIcon(":/icons/other/feed.png")); + item->setToolTip(0, linkString); + treeWidget->addTopLevelItem(item); + + titleString.clear(); + linkString.clear(); + } + } else if (xml.isCharacters() && !xml.isWhitespace()) { + if (currentTag == "title") + titleString = xml.text().toString(); + else if (currentTag == "link") + linkString += xml.text().toString(); + } + } + + if (treeWidget->topLevelItemCount() == 0) { + QTreeWidgetItem *item = new QTreeWidgetItem; + item->setText(0, tr("Error in fetching feed")); + treeWidget->addTopLevelItem(item); + } + + m_networkReplies.append(reply); +} + +bool RSSManager::addRssFeed(const QString &address, const QString &title) +{ + if (address.isEmpty()) + return false; + QSqlQuery query; + query.exec("SELECT id FROM rss WHERE address='"+address+"'"); + if (!query.next()) { + query.prepare("INSERT INTO rss (address, title) VALUES(?,?)"); + query.bindValue(0, address); + query.bindValue(1, title); + query.exec(); + QMessageBox::information(getQupZilla(), tr("RSS feed added"), tr("RSS with title '%1' has been successfuly added.").arg(title)); + return true; + } else { + QMessageBox::warning(getQupZilla(), tr("RSS feed duplicated"), tr("You already have this feed.")); + } + return false; +} + +RSSManager::~RSSManager() +{ + delete ui; +} diff --git a/src/rss/rssmanager.h b/src/rss/rssmanager.h new file mode 100644 index 000000000..1e9050751 --- /dev/null +++ b/src/rss/rssmanager.h @@ -0,0 +1,53 @@ +#ifndef RSSMANAGER_H +#define RSSMANAGER_H + +#if defined(QT_NO_DEBUG) & !defined(QT_NO_DEBUG_OUTPUT) +#define QT_NO_DEBUG_OUTPUT +#endif + +#include +#include +#include +#include +#include +#include + +namespace Ui { + class RSSManager; +} + +class QupZilla; +class RSSManager : public QWidget +{ + Q_OBJECT + +public: + explicit RSSManager(QupZilla* mainClass, QWidget *parent = 0); + ~RSSManager(); + + bool addRssFeed(const QString &address, const QString &title); + void setMainWindow(QupZilla* window); + +public slots: + void refreshTable(); + +private slots: + void beginToLoadSlot(const QUrl &url); + void finished(QNetworkReply* reply); + void loadFeed(QTreeWidgetItem* item); + void controlLoadFeed(QTreeWidgetItem* item); + void reloadFeed(); + void deleteFeed(); + void editFeed(); + void customContextMenuRequested(const QPoint &position); + void loadFeedInNewTab(); + +private: + QupZilla* getQupZilla(); + QList m_networkReplies; + QNetworkAccessManager* m_networkManager; + Ui::RSSManager *ui; + QPointer p_QupZilla; +}; + +#endif // RSSMANAGER_H diff --git a/src/rss/rssmanager.ui b/src/rss/rssmanager.ui new file mode 100644 index 000000000..114ce1e53 --- /dev/null +++ b/src/rss/rssmanager.ui @@ -0,0 +1,158 @@ + + + RSSManager + + + + 0 + 0 + 600 + 431 + + + + RSS Reader + + + + :/icons/qupzilla.png:/icons/qupzilla.png + + + + + + Qt::NoFocus + + + QTabBar::tab{ max-width:100px;} + + + false + + + true + + + + Empty + + + + 0 + + + 0 + + + 0 + + + + + background: white; + + + QFrame::NoFrame + + + QFrame::Raised + + + + + + + 0 + 0 + + + + + + + :/icons/menu/rss.png + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + You don't have any RSS Feeds.<br/> +Please add some with RSS icon in navigation bar on site which offers feeds. + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + + + + + + + + + + + false + + + Reload + + + + + + + false + + + Edit feed + + + + + + + false + + + Delete feed + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + QDialogButtonBox::Close + + + + + + + + + + + + diff --git a/src/sidebar/sidebar.cpp b/src/sidebar/sidebar.cpp new file mode 100644 index 000000000..4a87a86e0 --- /dev/null +++ b/src/sidebar/sidebar.cpp @@ -0,0 +1,6 @@ +#include "sidebar.h" + +SideBar::SideBar(QWidget *parent) : + QDockWidget(parent) +{ +} diff --git a/src/sidebar/sidebar.h b/src/sidebar/sidebar.h new file mode 100644 index 000000000..61e0a18e2 --- /dev/null +++ b/src/sidebar/sidebar.h @@ -0,0 +1,18 @@ +#ifndef SIDEBAR_H +#define SIDEBAR_H + +#include + +class SideBar : public QDockWidget +{ + Q_OBJECT +public: + explicit SideBar(QWidget *parent = 0); + +signals: + +public slots: + +}; + +#endif // SIDEBAR_H diff --git a/src/webview/searchtoolbar.cpp b/src/webview/searchtoolbar.cpp new file mode 100644 index 000000000..f0d2ef1b8 --- /dev/null +++ b/src/webview/searchtoolbar.cpp @@ -0,0 +1,150 @@ +#include "searchtoolbar.h" +#include "qupzilla.h" +#include "webview.h" +#include "lineedit.h" + +SearchToolBar::SearchToolBar(QupZilla* mainClass, QWidget *parent) : + QToolBar(parent) + ,p_QupZilla(mainClass) + ,m_findFlags(0) +{ + setContextMenuPolicy(Qt::CustomContextMenu); + setObjectName("webSearchToolbar"); + setWindowTitle(tr("Search")); + setMovable(false); + + m_searchLine = new LineEdit(this); + m_searchLine->setInactiveText(tr("Search")); + m_searchLine->setMaximumWidth(250); + connect(m_searchLine, SIGNAL(returnPressed()), this, SLOT(findNext())); + + m_closeButton = new QAction(this); +#ifdef Q_WS_X11 + m_closeButton->setIcon(QIcon(style()->standardIcon(QStyle::SP_DialogCloseButton).pixmap(16,16))); +#else + closeButton->setIcon(QIcon(QIcon(":/icons/faenza/close.png").pixmap(16,16))); +#endif + connect(m_closeButton, SIGNAL(triggered()), this, SLOT(hideBar())); + + m_highlightButton = new QAction(tr("Highlight occurrences"),this); + m_highlightButton->setCheckable(true); + connect(m_highlightButton, SIGNAL(triggered(bool)), this, SLOT(refreshFindFlags(bool))); + + m_nextButton = new QAction(tr("Next"),this); +#ifdef Q_WS_X11 + m_nextButton->setIcon(style()->standardIcon(QStyle::SP_ArrowForward)); +#else + nextButton->setIcon(QIcon(":/icons/faenza/forward.png")); +#endif + + connect(m_nextButton, SIGNAL(triggered()), this, SLOT(findNext())); + + m_previousButton = new QAction(tr("Previous"),this); +#ifdef Q_WS_X11 + m_previousButton->setIcon(style()->standardIcon(QStyle::SP_ArrowBack)); +#else + previousButton->setIcon(QIcon(":/icons/faenza/back.png")); +#endif + connect(m_previousButton, SIGNAL(triggered()), this, SLOT(findPrevious())); + + m_caseSensitiveButton = new QAction(tr("Case sensitive"),this); + m_caseSensitiveButton->setCheckable(true); + connect(m_caseSensitiveButton, SIGNAL(triggered(bool)), this, SLOT(refreshFindFlags(bool))); + + m_searchResults = new QLabel(this); + + addAction(m_closeButton); + addWidget(new QLabel(tr("Find:"))); + addWidget(m_searchLine); + addSeparator(); + addAction(m_previousButton); + addAction(m_nextButton); + addAction(m_highlightButton); + addAction(m_caseSensitiveButton); + addWidget(m_searchResults); + + frameChanged(0); + connect(m_searchLine, SIGNAL(textChanged(QString)), this, SLOT(searchText(QString))); + + m_animation = new QTimeLine(300, this); + connect(m_animation, SIGNAL(frameChanged(int)),this, SLOT(frameChanged(int))); +} + +void SearchToolBar::showBar() +{ + setStyleSheet("QLabel, QToolButton {color: "+p_QupZilla->menuTextColor().name()+";}"); + m_animation->setFrameRange(0, 35); + m_animation->setDirection(QTimeLine::Forward); + disconnect(m_animation, SIGNAL(finished()),this, SLOT(hide())); + + m_animation->stop(); + m_animation->start(); + + m_searchLine->setFocus(); + + QToolBar::show(); +} + +void SearchToolBar::hideBar() +{ + m_animation->setDirection(QTimeLine::Backward); + + m_animation->stop(); + m_animation->start(); + connect(m_animation, SIGNAL(finished()), this, SLOT(hide())); + + m_searchLine->clear(); + p_QupZilla->weView()->setFocus(); +} + +void SearchToolBar::frameChanged(int frame) +{ + setMinimumHeight(frame); + setMaximumHeight(frame); +} + +void SearchToolBar::findNext() +{ + refreshFindFlags(true); + m_findFlags+=4; + searchText(m_searchLine->text()); + +} + +void SearchToolBar::findPrevious() +{ + refreshFindFlags(true); + m_findFlags+=5; + searchText(m_searchLine->text()); +} + +void SearchToolBar::refreshFindFlags(bool b) +{ + Q_UNUSED(b); + m_findFlags = 0; + if (m_highlightButton->isChecked()) { + m_findFlags+=8; + searchText(m_searchLine->text()); + }else{ + m_findFlags+=8; + searchText(""); + m_findFlags-=8; + } + if (m_caseSensitiveButton->isChecked()) { + m_findFlags+=2; + searchText(m_searchLine->text()); + } +} + +void SearchToolBar::searchText(const QString &text) +{ + bool found = p_QupZilla->weView()->findText(text, QFlags(m_findFlags)); + if (!found && !m_searchLine->text().isEmpty()) { + m_searchLine->setStyleSheet("background-color: #ff6666;"); + m_searchResults->setText(tr("No results found.")); + } + else{ + m_searchLine->setStyleSheet(""); + m_searchResults->clear(); + } +} diff --git a/src/webview/searchtoolbar.h b/src/webview/searchtoolbar.h new file mode 100644 index 000000000..38302125d --- /dev/null +++ b/src/webview/searchtoolbar.h @@ -0,0 +1,50 @@ +#ifndef SEARCHTOOLBAR_H +#define SEARCHTOOLBAR_H + +#if defined(QT_NO_DEBUG) & !defined(QT_NO_DEBUG_OUTPUT) +#define QT_NO_DEBUG_OUTPUT +#endif + +#include +#include +#include +#include +#include +#include +#include + +class QupZilla; +class LineEdit; +class SearchToolBar : public QToolBar +{ + Q_OBJECT +public: + explicit SearchToolBar(QupZilla* mainClass, QWidget *parent = 0); + LineEdit* searchLine(){ return m_searchLine; } + +signals: + +public slots: + void showBar(); + void hideBar(); + void searchText(const QString &text); + void refreshFindFlags(bool b); + void findNext(); + void findPrevious(); + void frameChanged(int frame); + +private: + QupZilla* p_QupZilla; + + LineEdit* m_searchLine; + QAction* m_closeButton; + QAction* m_highlightButton; + QAction* m_caseSensitiveButton; + QAction* m_nextButton; + QAction* m_previousButton; + QLabel* m_searchResults; + QTimeLine* m_animation; + int m_findFlags; +}; + +#endif // SEARCHTOOLBAR_H diff --git a/src/webview/siteinfo.cpp b/src/webview/siteinfo.cpp new file mode 100644 index 000000000..3aa947c3c --- /dev/null +++ b/src/webview/siteinfo.cpp @@ -0,0 +1,66 @@ +#include "siteinfo.h" +#include "ui_siteinfo.h" +#include "qupzilla.h" +#include "webview.h" + +SiteInfo::SiteInfo(QupZilla* mainClass, QWidget *parent) : + QDialog(parent) + ,ui(new Ui::SiteInfo) + ,p_QupZilla(mainClass) +{ + ui->setupUi(this); + WebView* view = p_QupZilla->weView(); + QWebFrame* frame = view->page()->mainFrame(); + QString title = view->title(); + if (title.isEmpty()) + title = tr("No Named Page"); + + ui->siteName->setText(title); + ui->siteAddress->setText(frame->baseUrl().toString()); + + QWebElementCollection meta = frame->findAllElements("meta"); + for (int i = 0; itreeTags); + item->setText(0, name); + item->setText(1, content); + ui->treeTags->addTopLevelItem(item); + } + + QWebElementCollection img = frame->findAllElements("img"); + for (int i = 0; itreeImages); + item->setText(0, alt); + item->setText(1, src); + ui->treeImages->addTopLevelItem(item); + } + +} + +SiteInfo::~SiteInfo() +{ + delete ui; +} diff --git a/src/webview/siteinfo.h b/src/webview/siteinfo.h new file mode 100644 index 000000000..3fdf6c6b8 --- /dev/null +++ b/src/webview/siteinfo.h @@ -0,0 +1,28 @@ +#ifndef SITEINFO_H +#define SITEINFO_H + +#if defined(QT_NO_DEBUG) & !defined(QT_NO_DEBUG_OUTPUT) +#define QT_NO_DEBUG_OUTPUT +#endif + +#include + +namespace Ui { + class SiteInfo; +} + +class QupZilla; +class SiteInfo : public QDialog +{ + Q_OBJECT + +public: + explicit SiteInfo(QupZilla* mainClass, QWidget *parent = 0); + ~SiteInfo(); + +private: + Ui::SiteInfo *ui; + QupZilla* p_QupZilla; +}; + +#endif // SITEINFO_H diff --git a/src/webview/siteinfo.ui b/src/webview/siteinfo.ui new file mode 100644 index 000000000..a69247ce9 --- /dev/null +++ b/src/webview/siteinfo.ui @@ -0,0 +1,159 @@ + + + SiteInfo + + + + 0 + 0 + 727 + 571 + + + + Site Info + + + + :/icons/qupzilla.png:/icons/qupzilla.png + + + + + + 200 + + + + Tag + + + + + Value + + + + + + + + 200 + + + + Image + + + + + Image address + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + Site address: + + + + + + + + 0 + 0 + + + + Site name: + + + + + + + + + + + + + + + + + + + + + Meta tags of site: + + + + + + + Images on site: + + + + + + + + SqueezeLabel + QLabel +
    squeezelabel.h
    +
    +
    + + + + + + buttonBox + accepted() + SiteInfo + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + SiteInfo + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
    diff --git a/src/webview/siteinfowidget.cpp b/src/webview/siteinfowidget.cpp new file mode 100644 index 000000000..b9f820b7a --- /dev/null +++ b/src/webview/siteinfowidget.cpp @@ -0,0 +1,67 @@ +#include "siteinfowidget.h" +#include "ui_siteinfowidget.h" +#include "qupzilla.h" + +SiteInfoWidget::SiteInfoWidget(QupZilla* mainClass, QWidget *parent) : + QMenu(parent) + ,ui(new Ui::SiteInfoWidget) + ,p_QupZilla(mainClass) +{ + QUrl url = p_QupZilla->weView()->url(); + if (url.isEmpty()) + return; + + this->setAttribute(Qt::WA_DeleteOnClose); + ui->setupUi(this); + + QString scheme = url.scheme(); + if (scheme == "https") { + ui->secureLabel->setText(tr("Your connection to this site is secured.")); + ui->secureIcon->setPixmap(QPixmap(":/icons/locationbar/accept.png")); + } + else { + ui->secureLabel->setText(tr("Your connection to this site is unsecured.")); + ui->secureIcon->setPixmap(QPixmap(":/icons/locationbar/warning.png")); + } + + QSqlQuery query; + QString host = url.host(); + QString host2 = host; + if (host.startsWith("www.")) + host2 = url.host().remove("www."); + + query.exec("SELECT sum(count) FROM history WHERE url LIKE '"+scheme+"://"+host+"%' "); + if (query.next()) { + int count = query.value(0).toInt(); + if (count > 3) { + ui->historyLabel->setText(tr("This is Your %1. visit of this site.").arg(count)); + ui->historyIcon->setPixmap(QPixmap(":/icons/locationbar/accept.png")); + } else if (count == 0) { + ui->historyLabel->setText(tr("You have never visited this site before.")); + ui->historyIcon->setPixmap(QPixmap(":/icons/locationbar/warning.png")); + } else { + ui->historyIcon->setPixmap(QPixmap(":/icons/locationbar/warning.png")); + QString text; + if (count == 1) + text = tr("first"); + else if (count == 2) + text = tr("second"); + else if (count == 3) + text = tr("third"); + ui->historyLabel->setText(tr("This is Your %1 visit of this site.").arg(text)); + } + } + connect(ui->pushButton, SIGNAL(clicked()), p_QupZilla, SLOT(showPageInfo())); +} + +void SiteInfoWidget::showAt(QWidget* _parent) +{ + QPoint p = _parent->mapToGlobal(QPoint(0, 0)); + move(p.x(), p.y() + _parent->height()); + show(); +} + +SiteInfoWidget::~SiteInfoWidget() +{ + delete ui; +} diff --git a/src/webview/siteinfowidget.h b/src/webview/siteinfowidget.h new file mode 100644 index 000000000..b3062f27c --- /dev/null +++ b/src/webview/siteinfowidget.h @@ -0,0 +1,27 @@ +#ifndef SITEINFOWIDGET_H +#define SITEINFOWIDGET_H + +#include +#include + +namespace Ui { + class SiteInfoWidget; +} + +class QupZilla; +class SiteInfoWidget : public QMenu +{ + Q_OBJECT + +public: + explicit SiteInfoWidget(QupZilla* mainClass, QWidget *parent = 0); + ~SiteInfoWidget(); + + void showAt(QWidget* _parent); + +private: + Ui::SiteInfoWidget *ui; + QupZilla* p_QupZilla; +}; + +#endif // SITEINFOWIDGET_H diff --git a/src/webview/siteinfowidget.ui b/src/webview/siteinfowidget.ui new file mode 100644 index 000000000..e392ffa0d --- /dev/null +++ b/src/webview/siteinfowidget.ui @@ -0,0 +1,108 @@ + + + SiteInfoWidget + + + + 0 + 0 + 213 + 140 + + + + Form + + + + 0 + + + + + QFrame::NoFrame + + + + 15 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 6 + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 10 + 10 + + + + + + + + + 0 + 0 + + + + More... + + + + + + + + + + + + + Frame + QFrame +
    frame.h
    + 1 +
    +
    + + +
    diff --git a/src/webview/tabbar.cpp b/src/webview/tabbar.cpp new file mode 100644 index 000000000..23491716a --- /dev/null +++ b/src/webview/tabbar.cpp @@ -0,0 +1,122 @@ +#include "tabbar.h" +#include "tabwidget.h" +#include "qupzilla.h" + +TabBar::TabBar(QupZilla* mainClass, QWidget *parent) : + QTabBar(parent) + ,p_QupZilla(mainClass) + ,m_clickedTab(0) +{ + setContextMenuPolicy(Qt::CustomContextMenu); + setObjectName("tabBar"); + setTabsClosable(true); + setElideMode(Qt::ElideRight); + setDocumentMode(true); + loadSettings(); + + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(contextMenuRequested(const QPoint &))); + +} + +void TabBar::loadSettings() +{ + QSettings settings(MainApplication::getInstance()->getActiveProfil()+"settings.ini", QSettings::IniFormat); + settings.beginGroup("Browser-Tabs-Settings"); + + setMovable( settings.value("makeTabsMovable",true).toBool() ); + if (settings.value("ActivateLastTabWhenClosingActual", false).toBool()) + setSelectionBehaviorOnRemove(QTabBar::SelectPreviousTab); + + settings.endGroup(); +} + +void TabBar::contextMenuRequested(const QPoint &position) +{ + TabWidget *tabWidget = qobject_cast(parentWidget()); + if (!tabWidget) + return; + int index = tabAt(position); + m_clickedTab = index; + + QMenu menu; + menu.addAction(QIcon(":/icons/menu/popup.png"),tr("New tab"), p_QupZilla, SLOT(addTab())); + menu.addSeparator(); + + if (index!=-1) { + menu.addAction( +#ifdef Q_WS_X11 + style()->standardIcon(QStyle::SP_ArrowBack) +#else + QIcon(":/icons/faenza/back.png") +#endif + ,tr("Back"), this, SLOT(backTab())); + menu.addAction( +#ifdef Q_WS_X11 + style()->standardIcon(QStyle::SP_ArrowForward) +#else + QIcon(":/icons/faenza/forward.png") +#endif + ,tr("Forward"), this, SLOT(forwardTab())); + menu.addAction( +#ifdef Q_WS_X11 + style()->standardIcon(QStyle::SP_BrowserStop) +#else + QIcon(":/icons/faenza/stop.png") +#endif + ,tr("Stop Tab"), this, SLOT(stopTab())); + menu.addAction( +#ifdef Q_WS_X11 + style()->standardIcon(QStyle::SP_BrowserReload) +#else + QIcon(":/icons/faenza/reload.png") +#endif + ,tr("Reload Tab"), this, SLOT(reloadTab())); + menu.addAction(tr("Reload All Tabs"), tabWidget, SLOT(reloadAllTabs())); + menu.addAction(tr("Bookmark This Tab"), this, SLOT(bookmarkTab())); + menu.addAction(tr("Bookmark All Tabs"), p_QupZilla, SLOT(bookmarkAllTabs())); + menu.addSeparator(); + QAction* action = menu.addAction(QIcon::fromTheme("user-trash"),tr("Restore Closed Tab"), tabWidget, SLOT(restoreClosedTab())); + tabWidget->canRestoreTab() ? action->setEnabled(true) : action->setEnabled(false); + menu.addSeparator(); + menu.addAction(tr("Close Other Tabs"), this, SLOT(closeAllButCurrent())); + menu.addAction(QIcon::fromTheme("window-close"),tr("Close"), this, SLOT(closeTab())); + menu.addSeparator(); + + if (!p_QupZilla->weView(m_clickedTab)->history()->canGoBack()) + menu.actions().at(2)->setEnabled(false); + + if (!p_QupZilla->weView(m_clickedTab)->history()->canGoForward()) + menu.actions().at(3)->setEnabled(false); + + if (!p_QupZilla->weView(m_clickedTab)->isLoading()) + menu.actions().at(4)->setEnabled(false); + }else{ + menu.addAction(tr("Reload All Tabs"), tabWidget, SLOT(reloadAllTabs())); + menu.addAction(tr("Bookmark All Tabs"), p_QupZilla, SLOT(bookmarkAllTabs())); + menu.addSeparator(); + QAction* action = menu.addAction(QIcon::fromTheme("user-trash"),tr("Restore Closed Tab"), tabWidget, SLOT(restoreClosedTab())); + tabWidget->canRestoreTab() ? action->setEnabled(true) : action->setEnabled(false); + } + + //Prevent choosing first option with double rightclick + QPoint pos = QCursor::pos(); + QPoint p(pos.x(), pos.y()+1); + menu.exec(p); +} + +void TabBar::bookmarkTab() +{ + p_QupZilla->addBookmark(p_QupZilla->weView(m_clickedTab)->url(), p_QupZilla->weView(m_clickedTab)->title()); +} + +void TabBar::mouseDoubleClickEvent(QMouseEvent *event) +{ + TabWidget *tabWidget = qobject_cast(parentWidget()); + if (!tabWidget) + return; + if (event->button() == Qt::LeftButton && tabAt(event->pos()) == -1) { + tabWidget->addView(QUrl(),tr("New tab"), TabWidget::NewTab, true); + return; + } + QTabBar::mouseDoubleClickEvent(event); +} diff --git a/src/webview/tabbar.h b/src/webview/tabbar.h new file mode 100644 index 000000000..718d2ee8a --- /dev/null +++ b/src/webview/tabbar.h @@ -0,0 +1,55 @@ +#ifndef TABBAR_H +#define TABBAR_H + +#if defined(QT_NO_DEBUG) & !defined(QT_NO_DEBUG_OUTPUT) +#define QT_NO_DEBUG_OUTPUT +#endif + +#include +#include +#include +#include +#include +#include +#include + +class QupZilla; +class TabBar : public QTabBar +{ + Q_OBJECT +public: + explicit TabBar(QupZilla* mainClass, QWidget *parent = 0); + +signals: + void reloadTab(int index); + void stopTab(int index); + void backTab(int index); + void forwardTab(int index); + void closeAllButCurrent(int index); + void closeTab(int index); + +public slots: + +public: + void loadSettings(); + +private slots: + void contextMenuRequested(const QPoint &position); + void reloadTab() { emit reloadTab(m_clickedTab); } + void stopTab() { emit stopTab(m_clickedTab); } + void backTab() { emit backTab(m_clickedTab); } + void forwardTab() { emit forwardTab(m_clickedTab); } + void closeAllButCurrent() { emit closeAllButCurrent(m_clickedTab); } + void closeTab() { emit closeTab(m_clickedTab); } + void bookmarkTab(); +private: + void mouseDoubleClickEvent(QMouseEvent *event); + + QupZilla* p_QupZilla; + bool m_showCloseButtonWithOneTab; + bool m_showTabBarWithOneTab; + int m_clickedTab; + +}; + +#endif // TABBAR_H diff --git a/src/webview/tabwidget.cpp b/src/webview/tabwidget.cpp new file mode 100644 index 000000000..a9b36777f --- /dev/null +++ b/src/webview/tabwidget.cpp @@ -0,0 +1,328 @@ +#include "webview.h" +#include "webpage.h" +#include "qupzilla.h" +#include "tabwidget.h" +#include "tabbar.h" +#include "locationbar.h" +#include "mainapplication.h" +#include "webtab.h" + +TabWidget::TabWidget(QupZilla* mainClass, QWidget *parent) : + QTabWidget(parent) + ,p_QupZilla(mainClass) + ,m_canRestoreTab(false) + ,m_lastTabIndex(0) + ,m_lastTabUrl(0) + ,m_lastTabHistory(0) +{ + m_tabBar = new TabBar(p_QupZilla); + setTabBar(m_tabBar); + setObjectName("tabWidget"); + setStyleSheet(" QTabBar::tab{max-width:250px; height: 28px;}"); + + loadSettings(); + + connect(this, SIGNAL(currentChanged(int)), this, SLOT(tabChanged(int))); + connect(this, SIGNAL(currentChanged(int)), p_QupZilla, SLOT(refreshHistory())); + connect(this, SIGNAL(currentChanged(int)), p_QupZilla->locationBar(), SLOT(checkRss())); + connect(this, SIGNAL(currentChanged(int)), p_QupZilla->locationBar(), SLOT(siteIconChanged())); + + connect(this, SIGNAL(tabCloseRequested(int)), this, SLOT(closeTab(int))); + connect(m_tabBar, SIGNAL(backTab(int)), this, SLOT(backTab(int))); + connect(m_tabBar, SIGNAL(forwardTab(int)), this, SLOT(forwardTab(int))); + connect(m_tabBar, SIGNAL(reloadTab(int)), this, SLOT(reloadTab(int))); + connect(m_tabBar, SIGNAL(stopTab(int)), this, SLOT(stopTab(int))); + connect(m_tabBar, SIGNAL(closeTab(int)), this, SLOT(closeTab(int))); + connect(m_tabBar, SIGNAL(closeAllButCurrent(int)), this, SLOT(closeAllButCurrent(int))); + + m_buttonListTabs = new QToolButton(this); + m_menuTabs = new QMenu(); + m_buttonListTabs->setMenu(m_menuTabs); + m_buttonListTabs->setToolButtonStyle(Qt::ToolButtonTextOnly); + m_buttonListTabs->setPopupMode(QToolButton::InstantPopup); + m_buttonListTabs->setAutoRaise(true); + m_buttonListTabs->setToolTip(tr("Show list of opened tabs")); + connect(m_menuTabs, SIGNAL(aboutToShow()), this, SLOT(aboutToShowTabsMenu())); + setCornerWidget(m_buttonListTabs); + + m_buttonAddTab = new QToolButton(this); + m_buttonAddTab->setIcon(QIcon(":/icons/other/plus.png")); + m_buttonAddTab->setToolTip(tr("Add Tab")); + m_buttonAddTab->setAutoRaise(true); + connect(m_buttonAddTab, SIGNAL(clicked()), p_QupZilla, SLOT(addTab())); + setCornerWidget(m_buttonAddTab, Qt::TopLeftCorner); +} + +void TabWidget::loadSettings() +{ + QSettings settings(MainApplication::getInstance()->getActiveProfil()+"settings.ini", QSettings::IniFormat); + settings.beginGroup("Browser-Tabs-Settings"); + m_hideCloseButtonWithOneTab = settings.value("hideCloseButtonWithOneTab",false).toBool(); + m_hideTabBarWithOneTab = settings.value("hideTabsWithOneTab",false).toBool(); + settings.endGroup(); + settings.beginGroup("Web-URL-Settings"); + m_urlOnNewTab = settings.value("newTabUrl","").toUrl(); + settings.endGroup(); + + m_tabBar->loadSettings(); +} + +void TabWidget::aboutToShowTabsMenu() +{ + m_menuTabs->clear(); + WebView* actView = weView(); + if (!actView) + return; + for (int i = 0; isetIcon(QIcon(":/icons/menu/circle.png")); + else + action->setIcon(LocationBar::icon(view->url())); + if (view->title().isEmpty()) { + if (view->isLoading()) { + action->setText(tr("Loading...")); + action->setIcon(QIcon(":/icons/other/progress.gif")); + }else + action->setText(tr("No Named Page")); + } + else{ + QString title = view->title(); + if (title.length()>40) { + title.truncate(40); + title+=".."; + } + action->setText(title); + } + action->setData(i); + connect(action, SIGNAL(triggered()), this, SLOT(actionChangeIndex())); + + m_menuTabs->addAction(action); + } + m_menuTabs->addSeparator(); + m_menuTabs->addAction(tr("Actually You have %1 opened tabs").arg(count()))->setEnabled(false); +} + +void TabWidget::actionChangeIndex() +{ + if (QAction *action = qobject_cast(sender())) { + setCurrentIndex(action->data().toInt()); + } +} + +int TabWidget::addView(QUrl url, QString title, OpenUrlIn openIn, bool selectLine) +{ + if (url.isEmpty()) + url = m_urlOnNewTab; + + int index = addTab(new WebTab(p_QupZilla),""); + setTabText(index, title); + weView(index)->animationLoading(index, true)->movie()->stop(); + weView(index)->animationLoading(index, false)->setPixmap(LocationBar::icon(url).pixmap(16,16)); + + if (openIn == TabWidget::NewSelectedTab) { + setCurrentIndex(index); + p_QupZilla->locationBar()->setText(url.toEncoded()); + p_QupZilla->locationBar()->setCursorPosition(0); + } + if (count() == 1 && m_hideTabBarWithOneTab) + tabBar()->setVisible(false); + else tabBar()->setVisible(true); + + if (count() == 1 && m_hideCloseButtonWithOneTab) + tabBar()->setTabsClosable(false); + else tabBar()->setTabsClosable(true); + + connect(weView(index), SIGNAL(siteIconChanged()), p_QupZilla->locationBar(), SLOT(siteIconChanged())); + connect(weView(index), SIGNAL(showUrl(QUrl)), p_QupZilla->locationBar(), SLOT(showUrl(QUrl))); + connect(weView(index), SIGNAL(checkRss()), p_QupZilla->locationBar(), SLOT(checkRss())); + connect(weView(index), SIGNAL(wantsCloseTab(int)), this, SLOT(closeTab(int))); + connect(weView(index), SIGNAL(changed()), p_QupZilla->getMainApp(), SLOT(setChanged())); + connect(weView(index), SIGNAL(ipChanged(QString)), p_QupZilla->ipLabel(), SLOT(setText(QString))); + + if (url.isValid()) + weView(index)->load(url); + if (selectLine) + p_QupZilla->locationBar()->setFocus(); + + return index; +} + +void TabWidget::setTabText(int index, const QString& text) +{ + QString newtext = text + " "; + QTabWidget::setTabText(index, newtext); +} + +void TabWidget::closeTab(int index) +{ + if (count() == 1) + return; + if (index == -1) + index = currentIndex(); + else m_lastTabIndex-=1; + + if (weView(index)) { + disconnect(weView(index), SIGNAL(siteIconChanged()), p_QupZilla->locationBar(), SLOT(siteIconChanged())); + disconnect(weView(index), SIGNAL(showUrl(QUrl)), p_QupZilla->locationBar(), SLOT(showUrl(QUrl))); + disconnect(weView(index), SIGNAL(checkRss()), p_QupZilla->locationBar(), SLOT(checkRss())); + disconnect(weView(index), SIGNAL(wantsCloseTab(int)), this, SLOT(closeTab(int))); + disconnect(weView(index), SIGNAL(changed()), p_QupZilla->getMainApp(), SLOT(setChanged())); + disconnect(weView(index), SIGNAL(ipChanged(QString)), p_QupZilla->ipLabel(), SLOT(setText(QString))); + //Save last tab url and history + if (!weView(index)->url().isEmpty()) { + m_lastTabUrl = weView(index)->url().toString(); + QDataStream tabHistoryStream(&m_lastTabHistory, QIODevice::WriteOnly); + tabHistoryStream << *weView(index)->history(); + m_canRestoreTab = true; + } + //weView(index)->page()->~QWebPage(); + //weView(index)->~QWebView(); + delete weView(index); + removeTab(index); + + if (count() == 1 && m_hideCloseButtonWithOneTab) + tabBar()->setTabsClosable(false); + if (count() == 1 && m_hideTabBarWithOneTab) + tabBar()->setVisible(false); + } +// if (count() < 1) +// p_QupZilla->close(); +} + +void TabWidget::tabChanged(int index) +{ + if (index<0) + return; + + QString title = p_QupZilla->weView()->title(); + if (title.isEmpty()) + title = tr("No Named Page"); + + p_QupZilla->setWindowTitle(title + " - QupZilla"); + p_QupZilla->locationBar()->showUrl(weView()->url(),false); + p_QupZilla->ipLabel()->setText(weView()->getIp()); + + if (p_QupZilla->inspectorDock() && p_QupZilla->inspectorDock()->isVisible()) + p_QupZilla->showInspector(); + + weView()->setFocus(); + + m_lastTabIndex = index; +} + +void TabWidget::reloadAllTabs() +{ + for (int i = 0;i(widget(index)); + + int cycleCounter = 0; // Only tab count * 1.6 attempts to + int maxCycles = count()*1.6; // close tabs -> it sometimes hangs here + while(count()!=1) { + for (int i = 0;i<=count();i++) { + if (widget(i) == akt) + continue; + closeTab(i); + cycleCounter++; + + if (cycleCounter >= maxCycles) + break; + } + if (cycleCounter >= maxCycles) + break; + } +} + +void TabWidget::restoreClosedTab() +{ + if (m_lastTabUrl.isEmpty()) + return; + int index = addView(QUrl()); + QDataStream historyStream(m_lastTabHistory); + historyStream >> *weView(index)->history(); + weView(index)->load(m_lastTabUrl); + m_canRestoreTab = false; +} + +QByteArray TabWidget::saveState() +{ + QByteArray data; + QDataStream stream(&data, QIODevice::WriteOnly); + + QStringList tabs; + QList tabsHistory; + for (int i = 0; i < count(); ++i) { + if (WebView *tab = weView(i)) { + tabs.append(QString::fromUtf8(tab->url().toEncoded())); + if (tab->history()->count() != 0) { + QByteArray tabHistory; + QDataStream tabHistoryStream(&tabHistory, QIODevice::WriteOnly); + tabHistoryStream << *tab->history(); + tabsHistory.append(tabHistory); + } else { + tabsHistory << QByteArray(); + } + } else { + tabs.append(QString::null); + tabsHistory.append(QByteArray()); + } + } + stream << tabs; + stream << currentIndex(); + stream << tabsHistory; + + return data; +} + +bool TabWidget::restoreState(const QByteArray &state) +{ + QByteArray sd = state; + QDataStream stream(&sd, QIODevice::ReadOnly); + if (stream.atEnd()) + return false; + + QStringList openTabs; + stream >> openTabs; + + int currentTab; + stream >> currentTab; + setCurrentIndex(currentTab); + QList tabHistory; + stream >> tabHistory; + + + for (int i = 0; i < openTabs.count(); ++i) { + QUrl url = QUrl::fromEncoded(openTabs.at(i).toUtf8()); + //TabWidget::OpenUrlIn tab = + + QByteArray historyState = tabHistory.value(i); + if (!historyState.isEmpty()) { + int index = addView(QUrl()); + QDataStream historyStream(historyState); + historyStream >> *weView(index)->history(); + weView(index)->load(url); + } else { + addView(url); + } + } + return true; +} + +TabWidget::~TabWidget() +{ + int index = currentIndex(); + closeAllButCurrent(index); + closeTab(index); + delete m_menuTabs; + delete m_buttonAddTab; + delete m_buttonListTabs; +} diff --git a/src/webview/tabwidget.h b/src/webview/tabwidget.h new file mode 100644 index 000000000..0c13b5608 --- /dev/null +++ b/src/webview/tabwidget.h @@ -0,0 +1,74 @@ +#ifndef TABWIDGET_H +#define TABWIDGET_H + +#if defined(QT_NO_DEBUG) & !defined(QT_NO_DEBUG_OUTPUT) +#define QT_NO_DEBUG_OUTPUT +#endif + +#include "webview.h" +#include "webtab.h" +#include +#include +#include +#include + +class QupZilla; +class WebView; +class TabBar; +class WebTab; + +class TabWidget : public QTabWidget +{ + Q_OBJECT +public: + explicit TabWidget(QupZilla* mainclass, QWidget *parent = 0); + ~TabWidget(); + enum OpenUrlIn{ CurrentTab, NewSelectedTab, NewNotSelectedTab, NewTab = NewSelectedTab }; + + QByteArray saveState(); + bool restoreState(const QByteArray &state); + void setTabText(int index, const QString& text); + void loadSettings(); + + inline TabBar* getTabBar() { return m_tabBar; } + inline bool canRestoreTab() { return m_canRestoreTab; } + + +public slots: + void closeTab(int index=-1); + int addView(QUrl url = QUrl(), QString title = tr("New tab"), OpenUrlIn openIn = NewTab, bool selectLine = false); + void reloadTab(int index) { weView(index)->reload(); } + void reloadAllTabs(); + void stopTab(int index) { weView(index)->stop(); } + void backTab(int index) { weView(index)->back(); } + void forwardTab(int index) { weView(index)->forward(); } + void closeAllButCurrent(int index); + void restoreClosedTab(); + +private slots: + void tabChanged(int index); + void aboutToShowTabsMenu(); + void actionChangeIndex(); + +private: + inline WebView* weView() { return qobject_cast(widget(currentIndex()))->view(); } + inline WebView* weView(int index) { return qobject_cast(widget(index))->view(); } + + bool m_hideCloseButtonWithOneTab; + bool m_hideTabBarWithOneTab; + QUrl m_urlOnNewTab; + QupZilla* p_QupZilla; + + bool m_canRestoreTab; + int m_lastTabIndex; + QUrl m_lastTabUrl; + QByteArray m_lastTabHistory; + + TabBar* m_tabBar; + + QMenu* m_menuTabs; + QToolButton* m_buttonAddTab; + QToolButton* m_buttonListTabs; +}; + +#endif // TABWIDGET_H diff --git a/src/webview/webpage.cpp b/src/webview/webpage.cpp new file mode 100644 index 000000000..f6d437757 --- /dev/null +++ b/src/webview/webpage.cpp @@ -0,0 +1,172 @@ +#include "webpage.h" +#include "webview.h" +#include "tabwidget.h" +#include "qupzilla.h" +#include "downloadmanager.h" +#include "webpluginfactory.h" +#include "mainapplication.h" + +WebPage::WebPage(WebView *parent, QupZilla* mainClass) + : QWebPage(parent) + ,p_QupZilla(mainClass) + ,m_view(parent) +{ + setForwardUnsupportedContent(true); + setPluginFactory(new WebPluginFactory(this)); + connect(this, SIGNAL(unsupportedContent(QNetworkReply*)), SLOT(handleUnsupportedContent(QNetworkReply*))); +} + +void WebPage::handleUnsupportedContent(QNetworkReply* reply) +{ + if (!reply) + return; + QUrl url = reply->url(); + + switch(reply->error()) { + case QNetworkReply::NoError: + if (reply->header(QNetworkRequest::ContentTypeHeader).isValid()) { + DownloadManager* dManager = MainApplication::getInstance()->downManager(); + dManager->handleUnsupportedContent(reply); + return; + } + break; + case QNetworkReply::ProtocolUnknownError: + qDebug() << url << "ProtocolUnknowError"; + QDesktopServices::openUrl(url); + return; + break; + default: + qDebug() << reply->errorString(); + break; + } + qDebug() << "error" << reply->errorString(); +} + +bool WebPage::acceptNavigationRequest(QWebFrame *frame, const QNetworkRequest &request, NavigationType type) +{ + m_lastRequest = request; + m_lastRequestType = type; + QString scheme = request.url().scheme(); + if (scheme == "mailto" || scheme == "ftp") { + QDesktopServices::openUrl(request.url()); + return false; + } + + if (type == QWebPage::NavigationTypeFormResubmitted) { + QMessageBox::StandardButton button = QMessageBox::warning(view(), tr("Confirmation"), + tr("To show this page, QupZilla must resend request witch do it again " + "(like searching on making an shoping, witch has been already done."), QMessageBox::Yes | QMessageBox::No); + if (button != QMessageBox::Yes) + return false; + } + + TabWidget::OpenUrlIn openIn= frame ? TabWidget::CurrentTab: TabWidget::NewTab; + + bool accept = QWebPage::acceptNavigationRequest(frame, request, type); + if (accept && openIn == TabWidget::NewTab) { + //p_QupZilla->tabWidget()->addView(request.url(),tr("New tab"), openIn); + } + return accept; +} + +QString WebPage::userAgentForUrl(const QUrl &url) const +{ + return QWebPage::userAgentForUrl(url); +} + +void WebPage::populateNetworkRequest(QNetworkRequest &request) +{ + QVariant variant = qVariantFromValue((void *) this); + request.setAttribute((QNetworkRequest::Attribute)(QNetworkRequest::User + 100), variant); + request.setAttribute((QNetworkRequest::Attribute)(QNetworkRequest::User + 101), m_lastRequestType); + + variant = qVariantFromValue((void *) m_view); + request.setAttribute((QNetworkRequest::Attribute)(QNetworkRequest::User + 102), variant); +} + +QWebPage* WebPage::createWindow(QWebPage::WebWindowType type) +{ + Q_UNUSED(type); + int index = p_QupZilla->tabWidget()->addView(); + return p_QupZilla->weView(index)->page(); +} + +bool WebPage::extension(Extension extension, const ExtensionOption *option, ExtensionReturn *output) +{ + if (extension == ChooseMultipleFilesExtension) + return QWebPage::extension(extension, option, output); + + const ErrorPageExtensionOption* exOption = static_cast(option); + ErrorPageExtensionReturn* exReturn = static_cast(output); + + QString errorString; + if (exOption->domain == QWebPage::QtNetwork) { + switch (exOption->error) { + case QNetworkReply::ConnectionRefusedError: + errorString = tr("Server refused the connection"); + break; + case QNetworkReply::RemoteHostClosedError: + errorString = tr("Server closed the connection"); + break; + case QNetworkReply::HostNotFoundError: + errorString = tr("Server not found"); + break; + case QNetworkReply::TimeoutError: + errorString = tr("Connection timed out"); + break; + case QNetworkReply::SslHandshakeFailedError: + errorString = tr("Untrusted connection"); + break; + default: + //errorString = exOption->error; + if (errorString.isEmpty()) + errorString = tr("Unknown error"); + break; + } + } + else if (exOption->domain == QWebPage::Http) { + errorString = tr("Error code %1").arg(exOption->error); + } + else if (exOption->domain == QWebPage::WebKit) + return false; // Downloads + + QString loadedUrl = exOption->url.toString(); + exReturn->baseUrl = loadedUrl; + + QFile file(":/html/errorPage.html"); + file.open(QFile::ReadOnly); + QString errString = file.readAll(); + errString.replace("%TITLE%", tr("Failed loading page")); + + //QPixmap pixmap = QIcon::fromTheme("dialog-warning").pixmap(45,45); + QPixmap pixmap = MainApplication::style()->standardIcon(QStyle::SP_MessageBoxWarning).pixmap(45,45); + QByteArray bytes; + QBuffer buffer(&bytes); + buffer.open(QIODevice::WriteOnly); + if (pixmap.save(&buffer, "PNG")) + errString.replace("%IMAGE%", buffer.buffer().toBase64()); + + //pixmap = QIcon::fromTheme("dialog-warning").pixmap(16,16); + pixmap = MainApplication::style()->standardIcon(QStyle::SP_MessageBoxWarning).pixmap(16,16); + bytes.clear(); + QBuffer buffer2(&bytes); + buffer2.open(QIODevice::WriteOnly); + if (pixmap.save(&buffer2, "PNG")) + errString.replace("%FAVICON%", buffer.buffer().toBase64()); + + errString.replace("%HEADING%", errorString); + errString.replace("%HEADING2%", tr("QupZilla can't load page at %1.").arg(loadedUrl)); + errString.replace("%LI-1%", tr("Check the address for typing errors such as ww.example.com instead of www.example.com")); + errString.replace("%LI-2%", tr("If you are unable to load any pages, check your computer's network connection.")); + errString.replace("%LI-3%", tr("If your computer or network is protected by a firewall or proxy, make sure that QupZilla is permitted to access the Web.")); + errString.replace("%TRY-AGAIN%", tr("Try Again")); + + exReturn->content = errString.toUtf8(); + return true; +} + +WebPage::~WebPage() +{ + setNetworkAccessManager(0); + mainFrame()->deleteLater(); +} diff --git a/src/webview/webpage.h b/src/webview/webpage.h new file mode 100644 index 000000000..782cf3f5e --- /dev/null +++ b/src/webview/webpage.h @@ -0,0 +1,47 @@ +#ifndef WEBPAGE_H +#define WEBPAGE_H + +#if defined(QT_NO_DEBUG) & !defined(QT_NO_DEBUG_OUTPUT) +#define QT_NO_DEBUG_OUTPUT +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class QupZilla; +class WebView; +class WebPage : public QWebPage +{ + Q_OBJECT +public: + WebPage(WebView *parent, QupZilla* mainClass); + void populateNetworkRequest(QNetworkRequest &request); + ~WebPage(); + + QString userAgentForUrl(const QUrl &url) const; + bool supportsExtension(Extension extension) const { return (extension == ErrorPageExtension); } + bool extension(Extension extension, const ExtensionOption *option, ExtensionReturn *output); + +protected slots: + QWebPage* createWindow(QWebPage::WebWindowType type); + void handleUnsupportedContent(QNetworkReply* url); + +protected: + bool acceptNavigationRequest(QWebFrame *frame, const QNetworkRequest &request, NavigationType type); + + QupZilla* p_QupZilla; + QNetworkRequest m_lastRequest; + QWebPage::NavigationType m_lastRequestType; + WebView* m_view; +}; + +#endif // WEBPAGE_H diff --git a/src/webview/webtab.cpp b/src/webview/webtab.cpp new file mode 100644 index 000000000..3b42f3d88 --- /dev/null +++ b/src/webview/webtab.cpp @@ -0,0 +1,33 @@ +#include "webtab.h" +#include "qupzilla.h" +#include "webview.h" + +WebTab::WebTab(QupZilla* mainClass, QWidget *parent) + :QWidget(parent) + ,p_QupZilla(mainClass) + ,m_view(0) +{ + m_layout = new QVBoxLayout(this); + setLayout(m_layout); + m_layout->setContentsMargins(0,0,0,0); + m_layout->setSpacing(0); + m_view = new WebView(p_QupZilla); + m_layout->addWidget(m_view); + + setAutoFillBackground(true); // We don't want opaque this + + connect(m_view, SIGNAL(showNotification(QWidget*)), this, SLOT(showNotification(QWidget*))); +} + +void WebTab::showNotification(QWidget *notif) +{ + if (m_layout->count() > 1) + delete m_layout->itemAt(0)->widget(); + + m_layout->insertWidget(0, notif); +} + +WebTab::~WebTab() +{ + delete m_view; +} diff --git a/src/webview/webtab.h b/src/webview/webtab.h new file mode 100644 index 000000000..3c02feb9a --- /dev/null +++ b/src/webview/webtab.h @@ -0,0 +1,27 @@ +#ifndef WEBTAB_H +#define WEBTAB_H + +#include +#include +#include +#include "webview.h" + +class QupZilla; +class WebTab : public QWidget +{ + Q_OBJECT +public: + explicit WebTab(QupZilla* mainClass, QWidget *parent = 0); + ~WebTab(); + WebView* view() { return m_view; } + +private slots: + void showNotification(QWidget* notif); + +private: + QupZilla* p_QupZilla; + QPointer m_view; + QVBoxLayout* m_layout; +}; + +#endif // WEBTAB_H diff --git a/src/webview/webview.cpp b/src/webview/webview.cpp new file mode 100644 index 000000000..fe946e9b5 --- /dev/null +++ b/src/webview/webview.cpp @@ -0,0 +1,679 @@ +#include "webview.h" +#include "qupzilla.h" +#include "webpage.h" +#include "tabwidget.h" +#include "historymodel.h" +#include "locationbar.h" +#include "downloadmanager.h" +#include "networkmanager.h" +#include "autofillmodel.h" +#include "networkmanagerproxy.h" +#include "mainapplication.h" +#include "tabbar.h" +#include "pluginproxy.h" +#include "webtab.h" + +WebView::WebView(QupZilla* mainClass, QWidget *parent) + : QWebView(parent) + ,p_QupZilla(mainClass) + ,m_progress(0) + ,m_isLoading(false) + ,m_currentZoom(100) + ,m_aboutToLoadUrl(QUrl()) + ,m_wantsClose(false) + ,m_page(new WebPage(this, p_QupZilla)) + //,m_loadingTimer(0) +{ + m_networkProxy = new NetworkManagerProxy(p_QupZilla); + m_networkProxy->setPrimaryNetworkAccessManager(p_QupZilla->getMainApp()->networkManager()); + m_networkProxy->setPage(m_page); + m_networkProxy->setView(this); + m_page->setNetworkAccessManager(m_networkProxy); + m_page->setView(this); + setPage(m_page); + + + connect(this, SIGNAL(loadStarted()), this, SLOT(loadStarted())); + connect(this, SIGNAL(loadProgress(int)), this, SLOT(setProgress(int))); + connect(this, SIGNAL(loadFinished(bool)), this, SLOT(loadFinished(bool))); + + connect(this, SIGNAL(linkClicked(QUrl)), this, SLOT(linkClicked(QUrl))); + connect(this, SIGNAL(urlChanged(QUrl)), this, SLOT(urlChanged(QUrl))); + connect(this, SIGNAL(titleChanged(QString)), this, SLOT(titleChanged(QString))); + + connect(this, SIGNAL(statusBarMessage(QString)), p_QupZilla->statusBar(), SLOT(showMessage(QString))); + + connect(page(), SIGNAL(linkHovered(QString, QString, QString)), this, SLOT(linkHovered(QString, QString, QString))); + connect(page(), SIGNAL(windowCloseRequested()), this, SLOT(closeTab())); + connect(page(), SIGNAL(downloadRequested(const QNetworkRequest &)), this, SLOT(downloadRequested(const QNetworkRequest &))); + + connect(p_QupZilla->getMainApp()->networkManager(), SIGNAL(finishLoading(bool)), this, SLOT(loadFinished(bool))); + connect(p_QupZilla->getMainApp()->networkManager(), SIGNAL(wantsFocus(QUrl)), this, SLOT(getFocus(QUrl))); + + //Zoom levels same as in firefox + m_zoomLevels << 30 << 50 << 67 << 80 << 90 << 100 << 110 << 120 << 133 << 150 << 170 << 200 << 240 << 300; +} + +WebPage* WebView::getPage() const +{ + return m_page; +} + +bool WebView::isCurrent() +{ + if (!tabWidget()) + return false; + if (qobject_cast(tabWidget()->widget(tabWidget()->currentIndex()))->view() == this) + return true; + + return false; +} + +void WebView::urlChanged(const QUrl &url) +{ + if (isCurrent()) { + emit showUrl(url); + p_QupZilla->refreshHistory(); + } + emit changed(); +} + +void WebView::linkClicked(const QUrl &url) +{ + qDebug() << __FUNCTION__ << "called"; + if (isCurrent()) + emit showUrl(url); +} + +void WebView::setProgress(int prog) +{ + m_progress = prog; + if (isCurrent()) { + emit showUrl(url()); + } +} + +void WebView::loadStarted() +{ + m_progress = 0; + m_isLoading = true; + + animationLoading(tabIndex(),true); + if (title().isNull()) + tabWidget()->setTabText(tabIndex(),tr("Loading...")); + + if (isCurrent()) { + emit showUrl(url()); + } + + m_currentIp.clear(); + +// if (m_loadingTimer) +// delete m_loadingTimer; +// m_loadingTimer = new QTimer(); +// connect(m_loadingTimer, SIGNAL(timeout()), this, SLOT(stopAnimation())); +// m_loadingTimer->start(1000*20); //20 seconds timeout to automatically "stop" loading animation +} + +QLabel *WebView::animationLoading(int index, bool addMovie) +{ + if (-1 == index) + return 0; + + QLabel *loadingAnimation = qobject_cast(tabWidget()->getTabBar()->tabButton(index, QTabBar::LeftSide)); + if (!loadingAnimation) { + loadingAnimation = new QLabel(this); + } + if (addMovie && !loadingAnimation->movie()) { + QMovie *movie = new QMovie(":icons/other/progress.gif", QByteArray(), loadingAnimation); + movie->setSpeed(70); + loadingAnimation->setMovie(movie); + movie->start(); + } + else if (loadingAnimation->movie()) + loadingAnimation->movie()->stop(); + + tabWidget()->getTabBar()->setTabButton(index, QTabBar::LeftSide, 0); + tabWidget()->getTabBar()->setTabButton(index, QTabBar::LeftSide, loadingAnimation); + return loadingAnimation; +} + +void WebView::stopAnimation() +{ + //m_loadingTimer->stop(); + QMovie* mov = animationLoading(tabIndex(), false)->movie(); + if (mov) { + mov->stop(); + iconChanged(); + } +} + +void WebView::setIp(QHostInfo info) +{ + if (info.addresses().isEmpty()) + return; + m_currentIp = info.hostName() + " ("+info.addresses().at(0).toString()+")"; + + if (isCurrent()) + emit ipChanged(m_currentIp); +} + +void WebView::loadFinished(bool state) +{ + Q_UNUSED(state); + if (!animationLoading(tabIndex(), false)) + return; + + if (animationLoading(tabIndex(), false)->movie()) + animationLoading(tabIndex(), false)->movie()->stop(); + if (m_progress>100) qDebug() << "bug"; //cannot be more than 100 + m_isLoading = false; + + p_QupZilla->getMainApp()->history()->addHistoryEntry(this); + if (isCurrent()) { + emit showUrl(url()); + emit checkRss(); + } + + iconChanged(); + + if (!p_QupZilla->locationBar()->hasFocus()) + setFocus(); + + //Fix the bug where sometimes icon is not available at the moment + if (icon().isNull()) + QTimer::singleShot(1000, this, SLOT(iconChanged())); + + titleChanged(title()); + MainApplication::getInstance()->autoFill()->completePage(this); + QHostInfo::lookupHost(url().host(), this, SLOT(setIp(QHostInfo))); +} + +void WebView::titleChanged(QString title) +{ + if (title.isEmpty()) title = url().host(); + if (title.isEmpty()) title = tr("No Named Page"); + QString title2 = title; + tabWidget()->setTabToolTip(tabIndex(),title2); + + title2+=" - QupZilla"; + if (isCurrent()) + p_QupZilla->setWindowTitle(title2); + + tabWidget()->setTabText(tabIndex(),title); +} + +void WebView::iconChanged() +{ + QIcon icon_ = icon(); + if (!icon_.isNull()) + animationLoading(tabIndex(), false)->setPixmap(icon_.pixmap(16,16)); + else + animationLoading(tabIndex(), false)->setPixmap(QIcon(":icons/locationbar/unknownpage.png").pixmap(16,16)); + + if (isCurrent()) + emit siteIconChanged(); +} + +void WebView::linkHovered(const QString &link, const QString &title, const QString &content) +{ + Q_UNUSED(title); + Q_UNUSED(content); + if (isCurrent()) { + if (link!="") { + p_QupZilla->statusBar()->showMessage(link); + }else{ + isLoading() ? p_QupZilla->statusBar()->showMessage(tr("Loading...")) : p_QupZilla->statusBar()->showMessage(tr("Done")); + } + } + m_hoveredLink = link; +} + +TabWidget* WebView::tabWidget() const +{ + QObject *widget = this->parent(); + while (widget) { + if (TabWidget *tw = qobject_cast(widget)) + return tw; + widget = widget->parent(); + } + return 0; +} + +int WebView::tabIndex() const +{ + TabWidget* tabWid = tabWidget(); + if (!tabWid) + return -1; + + int i = 0; + while(qobject_cast(tabWid->widget(i))->view()!=this) { + i++; + } + return i; +} + +QUrl WebView::guessUrlFromString(const QString &string) +{ + QString trimmedString = string.trimmed(); + + // Check the most common case of a valid url with scheme and host first + QUrl url = QUrl::fromEncoded(trimmedString.toUtf8(), QUrl::TolerantMode); + if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) + return url; + + // Absolute files that exists + if (QDir::isAbsolutePath(trimmedString) && QFile::exists(trimmedString)) + return QUrl::fromLocalFile(trimmedString); + + // If the string is missing the scheme or the scheme is not valid prepend a scheme + QString scheme = url.scheme(); + if (scheme.isEmpty() || scheme.contains(QLatin1Char('.')) || scheme == QLatin1String("localhost")) { + // Do not do anything for strings such as "foo", only "foo.com" + int dotIndex = trimmedString.indexOf(QLatin1Char('.')); + if (dotIndex != -1 || trimmedString.startsWith(QLatin1String("localhost"))) { + const QString hostscheme = trimmedString.left(dotIndex).toLower(); + QByteArray scheme = (hostscheme == QLatin1String("ftp")) ? "ftp" : "http"; + trimmedString = QLatin1String(scheme) + QLatin1String("://") + trimmedString; + } + url = QUrl::fromEncoded(trimmedString.toUtf8(), QUrl::TolerantMode); + } + + if (url.isValid()) + return url; + + return QUrl(); +} + +void WebView::mousePressEvent(QMouseEvent *event) +{ + switch (event->button()) { + case Qt::XButton1: + back(); + break; + case Qt::XButton2: + forward(); + break; + case Qt::MiddleButton: + if (isUrlValid(QUrl(m_hoveredLink))) + tabWidget()->addView(QUrl(m_hoveredLink),tr("New tab"), TabWidget::NewNotSelectedTab); + break; + case Qt::LeftButton: + if (event->modifiers() == Qt::ControlModifier && isUrlValid(QUrl(m_hoveredLink))) { + tabWidget()->addView(QUrl(m_hoveredLink),tr("New tab"), TabWidget::NewNotSelectedTab); + return; + } + default: + QWebView::mousePressEvent(event); + break; + } +} + +void WebView::mouseReleaseEvent(QMouseEvent* event) +{ + //Workaround for crash in mouseReleaseEvent when closing tab from javascript :/ + if (!m_wantsClose) + QWebView::mouseReleaseEvent(event); +} + +void WebView::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Back: + back(); + event->accept(); + break; + case Qt::Key_Forward: + forward(); + event->accept(); + break; + case Qt::Key_Stop: + stop(); + event->accept(); + break; + case Qt::Key_Refresh: + reload(); + event->accept(); + break; + default: + QWebView::keyPressEvent(event); + return; + } +} + +void WebView::contextMenuEvent(QContextMenuEvent *event) +{ + QMenu *menu = new QMenu(this); + + QWebHitTestResult r = page()->mainFrame()->hitTestContent(event->pos()); + + if (!r.linkUrl().isEmpty()) { + if (page()->selectedText() == r.linkText()) + findText(""); + menu->addAction(tr("Open link in new window"), this, SLOT(openUrlInNewWindow()))->setData(r.linkUrl()); + menu->addAction(QIcon(":/icons/menu/popup.png"), tr("Open link in new tab"), this, SLOT(openUrlInNewTab()))->setData(r.linkUrl()); + menu->addSeparator(); + menu->addAction(QIcon::fromTheme("user-bookmarks"), tr("Bookmark link"), this, SLOT(bookmarkLink()))->setData(r.linkUrl()); + menu->addAction(QIcon::fromTheme("document-save"), tr("Save link as..."), this, SLOT(downloadLinkToDisk()))->setData(r.linkUrl()); + menu->addAction(tr("Send link..."), this, SLOT(sendLinkByMail()))->setData(r.linkUrl()); + menu->addAction(QIcon::fromTheme("edit-copy"), tr("Copy link address"), this, SLOT(copyLinkToClipboard()))->setData(r.linkUrl()); + menu->addSeparator(); + if (!page()->selectedText().isEmpty()) + menu->addAction(pageAction(QWebPage::Copy)); + } + + if (!r.imageUrl().isEmpty()) { + if (!menu->isEmpty()) + menu->addSeparator(); + menu->addAction(tr("Show image"), this, SLOT(showImage()))->setData(r.imageUrl()); + menu->addAction(tr("Copy image"), this, SLOT(copyImageToClipboard()))->setData(r.imageUrl()); + menu->addAction(QIcon::fromTheme("edit-copy"), tr("Copy image address"), this, SLOT(copyLinkToClipboard()))->setData(r.imageUrl()); + menu->addSeparator(); + menu->addAction(QIcon::fromTheme("document-save"), tr("Save image as..."), this, SLOT(downloadImageToDisk())); + menu->addAction(tr("Send image..."), this, SLOT(sendLinkByMail()))->setData(r.linkUrl()); + menu->addSeparator(); + //menu->addAction(tr("Block image"), this, SLOT(blockImage()))->setData(r.imageUrl().toString()); + if (!page()->selectedText().isEmpty()) + menu->addAction(pageAction(QWebPage::Copy)); + } + + QWebElement element = r.element(); + if (!element.isNull() && (element.tagName().toLower() == "input" || element.tagName().toLower() == "textarea")) { + if (menu->isEmpty()) { + delete menu; + menu = page()->createStandardContextMenu(); + } + } + + if (menu->isEmpty()) { + QAction* action = menu->addAction(tr("Back"), this, SLOT(back())); +#ifdef Q_WS_X11 + action->setIcon(style()->standardIcon(QStyle::SP_ArrowBack)); +#else + action->setIcon(QIcon(":/icons/faenza/back.png")); +#endif + history()->canGoBack() ? action->setEnabled(true) : action->setEnabled(false); + + action = menu->addAction(tr("Forward"), this, SLOT(forward())); +#ifdef Q_WS_X11 + action->setIcon(style()->standardIcon(QStyle::SP_ArrowForward)); +#else + action->setIcon(QIcon(":/icons/faenza/forward.png")); +#endif + history()->canGoForward() ? action->setEnabled(true) : action->setEnabled(false); + + menu->addAction( +#ifdef Q_WS_X11 + style()->standardIcon(QStyle::SP_BrowserReload) +#else + QIcon(":/icons/faenza/reload.png") +#endif + ,tr("Reload"), this, SLOT(slotReload())); + action = menu->addAction( +#ifdef Q_WS_X11 + style()->standardIcon(QStyle::SP_BrowserStop) +#else + QIcon(":/icons/faenza/stop.png") +#endif + ,tr("Stop"), this, SLOT(stop())); + isLoading() ? action->setEnabled(true) : action->setEnabled(false); + + menu->addSeparator(); + menu->addAction(QIcon::fromTheme("user-bookmarks"), tr("Bookmark page"), this, SLOT(bookmarkLink())); + menu->addAction(QIcon::fromTheme("document-save"), tr("Save page as..."), this, SLOT(downloadLinkToDisk()))->setData(url()); + menu->addAction(tr("Send page..."), this, SLOT(sendLinkByMail()))->setData(url()); + menu->addSeparator(); + menu->addAction(QIcon::fromTheme("edit-select-all"), tr("Select all"), this, SLOT(selectAll())); + if (!page()->selectedText().isEmpty()) + menu->addAction(pageAction(QWebPage::Copy)); + + menu->addSeparator(); + menu->addAction(QIcon::fromTheme("text-html"),tr("Show source code"), this, SLOT(showSource())); + menu->addAction(QIcon::fromTheme("dialog-information"),tr("Show info about site"), this, SLOT(showSiteInfo()))->setData(url()); + } + + MainApplication::getInstance()->plugins()->populateWebViewMenu(menu, this, r); + menu->addAction(tr("Show Web Inspector"), this, SLOT(showInspector())); + + if (!page()->selectedText().isEmpty()) { + menu->addSeparator(); + QString selectedText = page()->selectedText(); + selectedText.truncate(20); + menu->addAction(QIcon(":icons/used/google.png"), tr("Search ")+selectedText+tr("... on Google"), this, SLOT(searchOnGoogle()))->setData(page()->selectedText()); + } + + if (!menu->isEmpty()) { + //Prevent choosing first option with double rightclick + QPoint pos = QCursor::pos(); + QPoint p(pos.x(), pos.y()+1); + menu->exec(p); + delete menu; + return; + } + + QWebView::contextMenuEvent(event); +} + +void WebView::addNotification(QWidget *notif) +{ + emit showNotification(notif); +} + +void WebView::openUrlInNewTab() +{ + if (QAction *action = qobject_cast(sender())) { + tabWidget()->addView(action->data().toUrl(), tr("New tab"), TabWidget::NewNotSelectedTab); + } +} + +void WebView::openUrlInNewWindow() +{ + if (QAction *action = qobject_cast(sender())) { + p_QupZilla->getMainApp()->makeNewWindow(false, action->data().toString()); + } +} + +void WebView::sendLinkByMail() +{ + if (QAction *action = qobject_cast(sender())) { + QDesktopServices::openUrl(QUrl("mailto:?body="+action->data().toString())); + } +} + +void WebView::copyLinkToClipboard() +{ + if (QAction *action = qobject_cast(sender())) { + QApplication::clipboard()->setText(action->data().toString()); + } +} + +void WebView::searchOnGoogle() +{ + if (QAction *action = qobject_cast(sender())) { + load(QUrl("http://www.google.com/search?client=qupzilla&q="+action->data().toString())); + } +} + +void WebView::selectAll() +{ + triggerPageAction(QWebPage::SelectAll); +} + +void WebView::downloadImageToDisk() +{ + triggerPageAction(QWebPage::DownloadImageToDisk); +} + +void WebView::copyImageToClipboard() +{ + triggerPageAction(QWebPage::CopyImageToClipboard); +} + +void WebView::showImage() +{ + if (QAction *action = qobject_cast(sender())) { + load(QUrl(action->data().toString())); + } +} + +void WebView::showSource() +{ + p_QupZilla->showSource(); +} + +void WebView::downloadLinkToDisk() +{ + if (QAction *action = qobject_cast(sender())) { + QNetworkRequest request(action->data().toUrl()); + DownloadManager* dManager = MainApplication::getInstance()->downManager(); + dManager->download(request); + } +} + +void WebView::downloadRequested(const QNetworkRequest &request) +{ + DownloadManager* dManager = MainApplication::getInstance()->downManager(); + dManager->download(request); +} + +void WebView::bookmarkLink() +{ + if (QAction *action = qobject_cast(sender())) { + if (action->data().isNull()) + p_QupZilla->bookmarkPage(); + else + p_QupZilla->addBookmark(action->data().toUrl(), action->data().toString()); + } +} + +void WebView::showInspector() +{ + p_QupZilla->showInspector(); +} + +void WebView::showSiteInfo() +{ + p_QupZilla->showPageInfo(); +} + +void WebView::applyZoom() +{ + setZoomFactor(qreal(m_currentZoom) / 100.0); +} + +void WebView::zoomIn() +{ + int i = m_zoomLevels.indexOf(m_currentZoom); + + if (i < m_zoomLevels.count() - 1) + m_currentZoom = m_zoomLevels[i + 1]; + applyZoom(); +} + +void WebView::zoomOut() +{ + int i = m_zoomLevels.indexOf(m_currentZoom); + + if (i > 0) + m_currentZoom = m_zoomLevels[i - 1]; + applyZoom(); +} + +void WebView::zoomReset() +{ + m_currentZoom = 100; + applyZoom(); +} + +void WebView::wheelEvent(QWheelEvent *event) +{ + if (event->modifiers() & Qt::ControlModifier) { + int numDegrees = event->delta() / 8; + int numSteps = numDegrees / 15; + if (numSteps == 1) + zoomIn(); + else + zoomOut(); + event->accept(); + return; + } + QWebView::wheelEvent(event); +} + +void WebView::getFocus(const QUrl &urla) +{ + if (urla == url()) + tabWidget()->setCurrentWidget(this); +} + +void WebView::closeTab() +{ + if (m_wantsClose) + emit wantsCloseTab(tabIndex()); + else { + m_wantsClose = true; + QTimer::singleShot(20, this, SLOT(closeTab())); + } +} + +void WebView::load(QUrl url) +{ + if (url.toString().startsWith("javascript:")) { + page()->mainFrame()->evaluateJavaScript(url.toString()); + return; + } + if (isUrlValid(url)) { + QWebView::load(url); + m_aboutToLoadUrl = url; + return; + } + +#ifdef Q_WS_WIN + if (QFile::exists(url.path().mid(1))) // From QUrl(file:///C:/Bla/ble/foo.html it returns + // /C:/Bla/ble/foo.html ... so we cut first char +#else + if (QFile::exists(url.path())) +#endif + QWebView::load(url); + else + QWebView::load(QUrl("http://www.google.com/search?client=qupzilla&q="+url.toString())); +} + +QUrl WebView::url() const +{ + QUrl ur = QWebView::url(); + if (ur.isEmpty() && !m_aboutToLoadUrl.isEmpty()) + return m_aboutToLoadUrl; + return ur; +} + +QString WebView::title() const +{ + QString title = QWebView::title(); + if (title.isEmpty()) + return tr("No Named Page"); + return title; +} + +void WebView::reload() +{ + if (QWebView::url().isEmpty() && !m_aboutToLoadUrl.isEmpty()) { + qDebug() << "loading about to load"; + load(m_aboutToLoadUrl); + return; + } + QWebView::reload(); +} + +bool WebView::isUrlValid(const QUrl &url) +{ + if (url.isValid() && !url.host().isEmpty() && !url.scheme().isEmpty()) + return true; + return false; +} + +WebView::~WebView() +{ + history()->clear(); + delete m_page; +} diff --git a/src/webview/webview.h b/src/webview/webview.h new file mode 100644 index 000000000..0b59fec89 --- /dev/null +++ b/src/webview/webview.h @@ -0,0 +1,122 @@ +#ifndef WEBVIEW_H +#define WEBVIEW_H + +#if defined(QT_NO_DEBUG) & !defined(QT_NO_DEBUG_OUTPUT) +#define QT_NO_DEBUG_OUTPUT +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class QupZilla; +class TabWidget; +class WebPage; +class NetworkManagerProxy; + +class WebView : public QWebView +{ + Q_OBJECT +public: + explicit WebView(QupZilla* mainClass, QWidget *parent = 0); + ~WebView(); + bool isLoading() { return m_isLoading;} + int getLoading() { return m_progress; } + + void zoomReset(); + void load(QUrl url); + QUrl url() const; + QString title() const; + void reload(); + WebPage* getPage() const; + QString getIp() { return m_currentIp; } + QLabel* animationLoading(int index, bool addMovie); + void addNotification(QWidget* notif); + + static QUrl guessUrlFromString(const QString &string); + static bool isUrlValid(const QUrl &url); + +public slots: + void stop(){ if (page()) {emit ipChanged(m_currentIp); page()->triggerAction(QWebPage::Stop); loadFinished(true);} } + void back(){ if (page()) {emit ipChanged(m_currentIp); page()->triggerAction(QWebPage::Back);} } + void forward(){ if (page()) {emit ipChanged(m_currentIp); page()->triggerAction(QWebPage::Forward);} } + void slotReload(){ if (page()) {emit ipChanged(m_currentIp); page()->triggerAction(QWebPage::Reload);} } + void iconChanged(); + void selectAll(); + + void zoomIn(); + void zoomOut(); + +private slots: + void showImage(); + void copyImageToClipboard(); + void downloadImageToDisk(); + void searchOnGoogle(); + void copyLinkToClipboard(); + void loadStarted(); + void downloadRequested(const QNetworkRequest &request); + void setProgress(int prog); + void loadFinished(bool state); + void linkClicked(const QUrl &url); + void urlChanged(const QUrl &url); + void titleChanged(QString title); + void linkHovered(const QString &link, const QString &title, const QString &content); + void openUrlInNewWindow(); + void openUrlInNewTab(); + void closeTab(); + void downloadLinkToDisk(); + void sendLinkByMail(); + void bookmarkLink(); + void showSource(); + void showSiteInfo(); + void getFocus(const QUrl &urla); + void showInspector(); + void stopAnimation(); + void setIp(QHostInfo info); + +private: + void keyPressEvent(QKeyEvent *event); + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void contextMenuEvent(QContextMenuEvent *event); + void wheelEvent(QWheelEvent *event); + TabWidget* tabWidget() const; + int tabIndex() const; + bool isCurrent(); + void applyZoom(); + + QupZilla* p_QupZilla; + int m_progress; + bool m_isLoading; + QString m_hoveredLink; + QList m_zoomLevels; + int m_currentZoom; + QUrl m_aboutToLoadUrl; + bool m_wantsClose; + QString m_currentIp; + + WebPage* m_page; + NetworkManagerProxy* m_networkProxy; + //QTimer* m_loadingTimer; //Too confusing + +signals: + void showUrl(QUrl url); + void siteIconChanged(); + void setPrivacy(bool state); + void checkRss(); + void wantsCloseTab(int index); + void changed(); + void ipChanged(QString ip); + void showNotification(QWidget* notif); +}; + +#endif // WEBVIEW_H diff --git a/translations/cs_CZ.ts b/translations/cs_CZ.ts new file mode 100644 index 000000000..77b52c125 --- /dev/null +++ b/translations/cs_CZ.ts @@ -0,0 +1,2574 @@ + + + + + AboutDialog + + + About QupZilla + O QupZille + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Ubuntu'; font-size:10pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> + + + + + Authors + Autoři + + + + + Authors and Contributors + Autoři a Spolupracovníci + + + + + < About QupZilla + < O QupZille + + + + <p><b>Application version %1</b><br/> + <p><b>Verze aplikace %1</b><br/> + + + + <b>WebKit version %1</b></p> + <b>Verze WebKitu %1</b></p> + + + + <p>&copy; %1 %2<br/>All rights reserved.<br/> + <p>&copy; %1 %2<br/>Všechna práva vyhrazena.<br/> + + + + Build time: %1 </p> + Datum sestavení: %1 </p> + + + + <p><b>Main developers:</b><br/>%1 &lt;%2&gt;</p> + <p><b>Hlavní vývojáři:</b><br/>%1 &lt;%2&gt;</p> + + + + <p><b>Other contributors:</b><br/>%1</p> + <p><b>Ostatní spolupracovníci:</b><br/>%1</p> + + + + <p><b>Thanks to:</b><br/>%1</p> + <p><b>Poděkování:</b><br/>%1</p> + + + + AutoFillManager + + + Password Manager + Správce hesel + + + + Passwords + Hesla + + + + + Server + Server + + + + Password + Heslo + + + + + Remove + Odstranit + + + + Edit + Upravit + + + + + Remove All + Odstranit vše + + + + Exceptions + Vyjímky + + + + Confirmation + Potvrzení + + + + Are you sure to delete all passwords on your computer? + Opravdu si přejete smazat všechna hesla? + + + + Edit password + Upravit heslo + + + + Change password: + Změnit heslo: + + + + AutoFillModel + + + <b>Would you like to save this password?</b><br>To show all stored passwords open the Password Manager panel in preferences. + <b>Would you like to save this password?</b><br>To show all stored passwords open the AutoFill panel in preferences. + <b>Přejete si uložit toto heslo?</b><br>K zobrazení všech uložených hesel otevřete panel Správce hesel v předvolbách. + + + + Never for this site + Nikdy pro tuto stránku + + + + Not now + Nyní ne + + + + BookmarksManager + + + Bookmarks + Záložky + + + + Title + Titulek + + + + Url + Adresa + + + + Delete + Odstranit + + + + Del + Del + + + + Add Folder + Přidat složku + + + + Add new folder + Přidat složku + + + + Choose name for new bookmark folder: + Zvolte jméno pro novou složku: + + + + New Tab + Nový panel + + + + + + + + + + + Bookmarks In Menu + Záložky v menu + + + + + + + + + + + Bookmarks In ToolBar + Panel záložek + + + + Open link in actual tab + Otevřít odkaz v aktuálním panelu + + + + Open link in new tab + Otevřít odkaz v novém panelu + + + + Move bookmark to folder + Přesunout záložku do složky + + + + + + + + Unsorted Bookmarks + Nesetříděné záložky + + + + Close + Zavřít + + + + <b>Warning: </b>You already have this page bookmarked! + <b>Upozornění: </b>Tuto stránku již máte v záložkách! + + + + Choose name and location of bookmark. + Zvolte jméno a umístění záložky. + + + + Add New Bookmark + Přidat záložku + + + + Choose folder for bookmarks: + Zvolte složku pro záložky: + + + + Bookmark All Tabs + Přidat všechny panely do záložek + + + + BookmarksToolbar + + + Bookmarks + Záložky + + + + Bookmark Current Page + Přidat stránku do záložek + + + + Bookmark All Tabs + Přidat všechny panely do záložek + + + + Organize Bookmarks + Organizovat záložky + + + + Reload Toolbar + Obnovit panel + + + + Hide Most Visited + Skrýt Nejnavštěvovanější + + + + Show Most Visited + Zobrazit Nejnavštěvovanější + + + + Hide Toolbar + Skrýt panel + + + + Most visited + Nejnavštěvovanější + + + + Sites You visited the most + Nejvíce navštěvované stránky + + + + BookmarksWidget + + + Edit This Bookmark + Upravit tuto záložku + + + + Remove Bookmark + Odstranit záložku + + + + Name: + Název: + + + + Folder: + Složka: + + + + Save + Uložit + + + + Close + Zavřít + + + + Bookmarks In Menu + Záložky v menu + + + + Bookmarks In ToolBar + Panel záložek + + + + Unsorted Bookmarks + Nesetříděné záložky + + + + ClearPrivateData + + + Clear Recent History + Vymazat nedávnou historii + + + + Choose what you want to delete: + Vyberte co chcete smazat: + + + + Clear history + Smazat historii + + + + Clear cookies + Smazat cookies + + + + Clear cache + Vyprázdnit vyrovnávací paměť + + + + Clear icons + Smazat ikony + + + + Clear cookies from Adobe Flash Player + Smazat cookies z Adobe Flash Playeru + + + + ClickToFlash + + + Flash blocked by ClickToFlash + Zablokováno pluginem ClickToFlash + + + + Add %1 to whitelist + Přidat %1 na bílou listinu + + + + CookieManager + + + Cookies + Cookies - QupZilla + Cookies + + + + Find: + Najít: + + + + These cookies are stored on your computer: + Tyto cookies jsou uloženy v počítači: + + + + Server + Server + + + + Cookie name + Název cookies + + + + Name: + Název: + + + + Value: + Obsah: + + + + Server: + Server: + + + + Path: + Cesta: + + + + Secure: + Zasláno pro: + + + + Expiration: + Platnost do: + + + + + + + + + + + + + + + <cookie not selected> + <nebylo zvoleno cookie> + + + + Remove all cookies + Odebrat všechny cookies + + + + + Remove cookie + Odebrat cookie + + + + Del + Del + + + + Search + Vyhledávání + + + + Confirmation + Potvrzení + + + + Are you sure to delete all cookies on your computer? + Opravdu chcete vymazat všechny cookies z počítače? + + + + Remove cookies + Odebrat cookies + + + + Secure only + Zabezpečené připojení + + + + All connections + Libovolný typ připojení + + + + Session cookie + konce relace + + + + DownloadItem + + + Form + Form + + + + A Clockwork Orange.avi + + + + + Remaining 26 minutes - 339MB of 693 MB (350kB/s) + + + + + Remaining time unavailable + Neznámý zbývající čas + + + + Done - %1 + Hotovo - %1 + + + + + Cancelled + Zrušeno + + + + few seconds + několik sekund + + + + seconds + sekund + + + + minutes + minut + + + + hours + hodin + + + + Remaining %1 - %2 of %3 (%4) + Zbývá %1 - %2 z %3 (%4) + + + + Cancelled - %1 + Zrušeno - %1 + + + + Open File + Otevřít soubor + + + + Open Folder + Otevřít složku + + + + Cancel downloading + Zrušit stahování + + + + Clear + Vyčistit + + + + Error + Chyba + + + + Error: Cannot write to file! + Chyba: Nelze zapisovat do souboru! + + + + Error: + Chyba: + + + + DownloadManager + + + %1% of %2 files (%3) %4 remaining + %1% z %2 souborů (%3) %4 zbyvá + + + + % - Download Manager + % - Správce stahování + + + + Save file as... + Uložit soubor jako... + + + + Warning + Varování + + + + Are you sure to quit? All uncompleted downloads will be cancelled! + Jste si jistý že chcete skončit? Všechna nedokončená stahování budou zrušena! + + + + NoNameDownload + BezNazvu + + + + + + Download Manager + Správce stahování + + + + Clear + Vyčistit + + + + DownloadOptionsDialog + + + Opening + Otevírám + + + + which is a: + což je: + + + + from: + z: + + + + You have chosen to open + Zvolili jste otevřít + + + + <b>What should QupZilla do with this file?</b> + <b>Co má QupZilla udělat s tímto souborem?</b> + + + + Open... + Otevřít... + + + + Save File + Uložit soubor + + + + HistoryManager + + + History + Historie + + + + Search in history: + Vyhledávat v historii: + + + + Title + Titulek + + + + Url + Adresa + + + + Delete + Odstranit + + + + Del + Del + + + + Clear All History + Vymazat celou historii + + + + Search + Vyhledávání + + + + New Tab + Nový panel + + + + Open link in actual tab + Otevřít odkaz v aktuálním panelu + + + + Open link in new tab + Otevřít odkaz v novém panelu + + + + Close + Zavřít + + + + Confirmation + Potvrzení + + + + Are you sure to delete all history? + Opravdu chcete vymazat celou historii? + + + + HistoryModel + + + Failed loading page + Chyba při načítání stránky + + + + + No Named Page + Bezejmenná stránka + + + + LocationBar + + + Show informations about this page + Zobrazit informace o stránce + + + + Add RSS from this page... + Přidat RSS kanál ... + + + + + Bookmark this Page + Přidat stránku do záložek + + + + Enter URL address or search on Google.com + Zadejte internetovou adresu nebo vyhledávejte na Google.com + + + + Add RSS channel with title ' + Přidat kanál s titulkem ' + + + + Read RSS news + Číst RSS novinky + + + + Loading... + Načítám... + + + + Done + Hotovo + + + + + Edit this bookmark + Upravit záložku + + + + .co.uk + .cz + Append domain name on ALT key = Should be different for every country + .cz + + + + LocationCompleter + + + Search %1 on Google.com +.......... + Hledat %1 na Google.com +.......... + + + + MainApplication + + + Last session crashed + Poslední relace spadla + + + + <b>QupZilla crashed :-(</b><br/>Oops, last session of QupZilla ends with its crash. We are very sorry. Would you try to restore saved state? + <b>QupZilla spadla :-(</b><br/>Oops, poslední relace QupZilly skončila jejím pádem. Velice se omlouváme. Přejete si obnovit uložený stav? + + + + NetworkManager + + + SSL Certificate Error! + Chyba zabezpečení! + + + + The page you trying to access has following errors in SSL Certificate: + Stránka kterou se snažíte navštívit zaslala SSL Certifikát s těmito chybami: + + + + <b>Organization: </b> + <b>Organizace: </b> + + + + <b>Domain Name: </b> + <b>Doména: </b> + + + + <b>Expiration Date: </b> + <b>Vyprší: </b> + + + + <b>Error: </b> + <b>Chyba: </b> + + + + Would you like to make exception for this certificate? + Chcete udělit vyjímku tomuto certifikátu? + + + + SSL Certificate Error + Chyba zabezpečení + + + + Authorization required + Vyžadována autorizace + + + + Username: + Uživatelské jméno: + + + + Password: + Heslo: + + + + Save username and password on this site + Uložit jméno a heslo pro tuto stránku + + + + A username and password are being requested by %1. The site says: "%2" + Server %1 požaduje vaše uživatelské jméno a heslo s komentářem: "%2" + + + + PluginsList + + + Form + Form + + + + Application Extensions + Doplnky aplikace + + + + Allow Application Extensions to be loaded + Povolit načítání doplňků aplikace + + + + Settings + Nastavení + + + + Load Plugins + Načíst doplňky + + + + WebKit Plugins + WebKit Pluginy + + + + <b>Click To Flash Plugin</b> + <b>Click To Flash Plugin</b> + + + + Click To Flash is a plugin which blocks auto loading of Flash content at page. You can always load it manually by clicking on the Flash play icon. + Click To Flash je plugin který blokuje automatické načítání Flash animací. Avšak vždy je můžete manuálně načíst kliknutím na ikonku Flashe. + + + + Whitelist + Bílá listina + + + + Add + Přidat + + + + Remove + Odstranit + + + + Allow Click To Flash + Povolit Click To Flash + + + + Add site to whitelist + Přidat stránku na bílou listinu + + + + Server without http:// (ex. youtube.com) + Server bez http:// (např. youtube.com) + + + + Preferences + + + Preferences + Předvolby + + + + General + Obecné + + + + 0 + 0 + + + + + 1 + 1 + + + + 2 + 2 + + + + 3 + 3 + + + + Downloads + Stahování + + + + 4 + 4 + + + + 5 + 5 + + + + 6 + 6 + + + + Plugins + Doplňky + + + + 7 + 7 + + + + 8 + 8 + + + + After launch: + Po spuštění: + + + + Open blank page + Otevřít prázdnou stránku + + + + + Open homepage + Otevřít domovskou stránku + + + + Restore session + Obnovit relaci + + + + Homepage: + Domovská stránka: + + + + On new tab: + Při otevření nového panelu: + + + + Open blank tab + Otevřít prázdný panel + + + + Open other page... + Otevřít jinou stránku... + + + + + Use actual + Použít aktuální + + + + <b>Navigation ToolBar</b> + <b>Navigační panel</b> + + + + <b>Background<b/> + <b>Pozadí</b> + + + + Use background image + Použít obrázek na pozadí + + + + Use transparent background + Použít průhledné pozadí + + + + Choose image... + Vyberte obrázek... + + + + Reset default + Obnovit původní + + + + Maximum + Maximálně + + + + 50 MB + 50 MB + + + + <b>Downloads</b> + <b>Stahování</b> + + + + QupZilla + QupZilla + + + + <b>Network Cache</b> + <b>Síťová cache</b> + + + + Allow storing network cache on disk + Povolit ukládání cache na disk + + + + <b>Cookies</b> + <b>Cookies</b> + + + + <b>Web storage</b> + <b>Webové úložiště</b> + + + + <b>Address Bar behaviour</b> + <b>Chování adresního řádku</b> + + + + <b>Language</b> + <b>Jazyk</b> + + + + <b>General</b> + <b>Hlavní</b> + + + + Startup profile: + Startovní profil: + + + + default + default + + + + Create New + Nový profil + + + + Delete + Odstranit + + + + Window + Okno prohlížeče + + + + Show StatusBar on start + Zobrazit StatusBar při startu + + + + <b>Profiles</b> + <b>Profily</b> + + + + Show Bookmarks ToolBar on start + Zobrazit panel záložek při startu + + + + Show Navigation ToolBar on start + Zobrazit navigační panel při startu + + + + Show Home button + Zobrazit tlačítko Domů + + + + Show Back / Forward buttons + Zobrazit tlačítka Zpět / Vpřed + + + + <b>Browser Window</b> + <b>Okno prohlížeče</b> + + + + Tabs + Panely + + + + <b>Launching</b> + <b>Spouštění</b> + + + + This is text color used in Menu + Toto je text používaný v menu + + + + Menu text color + Barva menu textu + + + + <b>Tabs behavior</b> + <b>Chování panelů</b> + + + + Make tabs movable + Přesouvat panely přetažením + + + + Hide close button if there is only one tab + Skrýt zavírací tlačítko při jediném panelu + + + + Hide tabs when if there is only one tab + Skrýt seznam panelů při jediném panelu + + + + Activate last tab when closing active tab + Aktivovat poslední panel při zavírání aktuálního + + + + <b>WebKit</b> + <b>WebKit</b> + + + + Block PopUp windows + Blokovat vyskakovací okna + + + + Allow DNS Prefetch + Povolit DNS Prefetch + + + + JavaScript can access clipboard + Povolit JavaScriptu přístup do schránky + + + + Include links in focus chain + Označovat odkazy tabulátorem + + + + Zoom text only + Přibližovat pouze text + + + + Print element background + Tisknout pozadí objektů + + + + Wheel scrolls + Kolečko posouvá + + + + lines + řádků + + + + Automatically close download dialog after finish + Automaticky zavírat dialog po skončení stahování + + + + Ask everytime for download location + U každého souboru se dotázat kam ho uložit + + + + Use defined location: + Uložit všechny soubory do: + + + + ... + ... + + + + Browsing + Prohlížení + + + + Load images + Nahrát obrázky + + + + Allow JAVA + Povolit JAVA + + + + Allow JavaScript + Povolit JavaScript + + + + Allow Plugins (Flash plugin) + Povolit pluginy (Flash plugin) + + + + Maximum pages in cache: + Maximum stránek v cache: + + + + Password Manager + Správce hesel + + + + <b>AutoFill options</b> + <b>Možnosti doplňování</b> + + + + Allow saving passwords from sites + Povolit ukládání hesel ze stránek + + + + Privacy + Soukromí + + + + Filter Tracking Cookies + Filtrovat sledovací cookies + + + + Allow storing of cookies + Povolit přijímání cookies + + + + Delete cookies on close + Vymazat cookies při zavření prohlížeče + + + + Match domain exactly + Vyžadovat přesnou shodu domény + + + + <b>Warning:</b> Match domain exactly and Filter Tracking Cookies options can lead to deny some cookies from sites. If you have problems with cookies, try to disable this options first! + <b>Upozornění:</b> Možnosti vyžadovat přesnou shodu domény a filtrovat sledovací cookies mohou vést k odmítnutí některých cookies. Pokud máte problémy s cookies, zkuste nejdříve tyto možnosti zakázat! + + + + Cookies Manager + Správce cookies + + + + Allow storing web icons + Povolit ukládání ikon + + + + Allow saving history + Povolit ukládání historie + + + + Delete history on close + Vymazat historii při zavření prohlížeče + + + + Other + Ostatní + + + + Select all text by double clicking in address bar + Select all text by clicking at address bar + Označit vše při dvojitém kliknutí do adresního řádku + + + + Add .com domain by pressing CTRL key + Přidat .com doménu stisknutím CTRL klávesy + + + + Add .co.uk domain by pressing ALT key + Přidat .cz doménu stísknutím ALT klávesy + + + + Available translations: + Dostupné překlady: + + + + In order to change language, you must restart browser. + Ke změně jazyku je nutný restart prohlížeče. + + + + Choose download location... + Vyberte složku pro stahování... + + + + Choose background location... + + + + + QupZilla + + + File + Soubor + + + + Open File + Otevřít soubor + + + + Edit + Úpravy + + + + Tools + Nástroje + + + + Help + Nápověda + + + + View + Zobrazení + + + + Bookmarks + Záložky + + + + History + Historie + + + + Save Page As... + Uložit stránku jako... + + + + Print + Tisk + + + + Quit + Konec + + + + New Tab + Nový panel + + + + Undo + Zpět + + + + Redo + Vpřed + + + + Preferences + Předvolby + + + + About QupZilla + O QupZille + + + + About Qt + O Qt + + + + Close Tab + Zavřít panel + + + + Find + Najít + + + + Cut + Vyjmout + + + + IP Address of current page + IP Adresa aktuální stránky + + + + Copy + Kopírovat + + + + Paste + Vložit + + + + Delete + Odstranit + + + + Select All + Vybrat vše + + + + Navigation Toolbar + Navigační lišta + + + + Status Bar + Status bar + + + + + Stop + Zastavit + + + + + Reload + Obnovit + + + + Zoom In + Zoom + + + + + Zoom Out + Zoom - + + + + Reset + Původní + + + + Page Source + Zdrojový kód stránky + + + + Fullscreen + Celá obrazovka + + + + + Back + Zpět + + + + + Forward + Vpřed + + + + + Home + Domů + + + + Show All History + Zobrazit celou historii + + + + Bookmark This Page + Přidat stránku do záložek + + + + Bookmark All Tabs + Přidat všechny panely do záložek + + + + Organize Bookmarks + Organizovat záložky + + + + New Window + Nové okno + + + + Close Window + Zavřít okno + + + + Open Location + Otevřít adresu + + + + Send Link... + Poslat odkaz... + + + + Report Bug + Nahlásit bug + + + + Web Search + Hledání na webu + + + + Page Info + Informace o stránce + + + + Download Manager + Správce stahování + + + + Clear Recent History + Vymazat nedávnou historii + + + + Private Browsing + Soukromé prohlížení + + + + RSS Reader + RSS čtečka + + + + Start Private Browsing + Spustit anonymní prohlížení + + + + Cookies Manager + Správce cookies + + + + Bookmarks Toolbar + Panel záložek + + + + Navigation + Navigace + + + + Main Menu + Hlavní menu + + + + Exit Fullscreen + Zrušit celou obrazovku + + + + Private Browsing Enabled + Soukromé prohlížení zapnuto + + + + Flash Plugin Enabled + Flash Plugin zapnut + + + + Menu Bar + Menu + + + + Bookmarks In ToolBar + Bookmarks In Toolbar + Panel záložek + + + + + Empty + Prázdný + + + + New tab + Nový panel + + + + Web Inspector + Web Inspektor + + + + Open file... + Otevřít soubor... + + + + Are you sure you want to turn on private browsing? + Jste si jistý že chcete zapnout soukromé prohlížení? + + + + When private browsing is turned on, some actions concerning your privacy will be disabled: + Se zapnutým soukromým prohlížením jsou některé akce týkající se soukromí vypnuty: + + + + Webpages are not added to the history. + Stránky nejsou přidávány do historie. + + + + New cookies are not stored, but current cookies can be accessed. + Nové cookies nejsou přijímány, ale současné cookies jsou zasílány. + + + + Your session won't be stored. + Vaše relace nebude uložena. + + + + Until you close the window, you can still click the Back and Forward buttons to return to the webpages you have opened. + Než zavřete prohlížeč, stále můžete použít tlačítka Zpět a Vpřed k vrácení se na stránky které jste otevřeli. + + + + + There are still open tabs + Stále jsou otevřeny panely + + + + + There are still %1 open tabs and your session won't be stored. Are you sure to quit? + Ještě jsou otevřeny %1 panely a Vaše relace nebude uložena. Opravdu chcete skončit? + + + + RSSManager + + + RSS Reader + RSS čtečka + + + + Empty + Prázdný + + + + You don't have any RSS Feeds.<br/> +Please add some with RSS icon in navigation bar on site which offers feeds. + Nemáte žádný RSS kanál.<br/> +Prosím přidejte si nějaký kliknutím na RSS ikonku v navigačním řádku. + + + + Reload + Obnovit + + + + Edit feed + Upravit kanál + + + + Delete feed + Smazat kanál + + + + News + Novinky + + + + + Loading... + Načítám... + + + + Fill title and URL of a feed: + Vyplňte titulek a adresu kanálu: + + + + Feed title: + Titulek kanálu: + + + + Feed URL: + Adresa kanálu: + + + + Edit RSS Feed + Upravit kanál + + + + Open link in actual tab + Otevřít odkaz v aktuálním panelu + + + + Open link in new tab + Otevřít odkaz v novém panelu + + + + Close + Zavřít + + + + + New Tab + Nový panel + + + + Error in fetching feed + Chyba při stahování kanálu + + + + RSS feed added + RSS kanál přidán + + + + RSS with title '%1' has been successfuly added. + RSS kanál s titulkem '%1' byl úspěšně přidán. + + + + RSS feed duplicated + Duplikovaný kanál + + + + You already have this feed. + Tento kanál již odebíráte. + + + + SearchToolBar + + + + Search + Vyhledávání + + + + Highlight occurrences + Zvýraznit + + + + Next + Další + + + + Previous + Předchozí + + + + Case sensitive + Rozlišovat velikost + + + + Find: + Najít: + + + + No results found. + Nic nenalezeno. + + + + SiteInfo + + + Site Info + Informace o stránce + + + + Tag + Tag + + + + Value + Hodnota + + + + Image + Obrázek + + + + Image address + Adresa obrázku + + + + Site address: + Site address + Adresa stránky: + + + + Site name: + Jméno stránky: + + + + Meta tags of site: + Meta tagy na stránce: + + + + Images on site: + Obrázky na stránce: + + + + No Named Page + Bezejmenná stránka + + + + SiteInfoWidget + + + Form + Form + + + + More... + Více... + + + + Your connection to this site is <b>secured</b>. + Připojení k této stránce je <b>zabezpečené</b>. + + + + Your connection to this site is <b>unsecured</b>. + Připojení k této stránce je <b>nezabezpečené</b>. + + + + This is Your <b>%1.</b> visit of this site. + Toto je Vaše <b>%1.</b> návštěva této stránky. + + + + first + první + + + + second + druhá + + + + third + třetí + + + + This is Your <b>%1</b> visit of this site. + Toto je Vaše <b>%1</b> návštěva této stránky. + + + + You have <b>never</b> visited this site before. + <b>Nikdy</b> dříve jste nenavštívili tuto stránku. + + + + SourceViewer + + + Source of + Zdrojový kód + + + + TabBar + + + + New tab + Nový panel + + + + Back + Zpět + + + + Forward + Vpřed + + + + Stop Tab + Zastavit panel + + + + Reload Tab + Obnovit panel + + + + + Reload All Tabs + Close other tabs + Obnovit všechny panely + + + + Bookmark This Tab + Přidat panel do záložek + + + + + Bookmark All Tabs + Přidat všechny panely do záložek + + + + + Restore Closed Tab + Obnovit zavřený panel + + + + Close Other Tabs + Zavřít ostatní panely + + + + Close + Zavřít + + + + TabWidget + + + Show list of opened tabs + Zobrazit seznam otevřených panelů + + + + Add Tab + Nový panel + + + + Loading... + Načítám... + + + + + No Named Page + Bezejmenná stránka + + + + Actually You have %1 opened tabs + Dohromady máte otevřeno %1 panelů + + + + New tab + Nový panel + + + + Updater + + + Go to download page + Přejít ke stažení + + + + Go to QupZilla website + Přejít na stránku QupZilly + + + + Hide notification + Skrýt upozornění + + + + QupZilla is checking for updates + QupZilla kontroluje aktualizace + + + + QupZilla found a new version! + QupZilla nalezla novou verzi! + + + + New version is available + Nová verze je dostupná + + + + New version of QupZilla %1 is available! + Nová verze QupZilly %1 je dostupná! + + + + + QupZilla Update + QupZilla Update + + + + WebPage + + + Confirmation + Potvrzení + + + + To show this page, QupZilla must resend request witch do it again (like searching on making an shoping, witch has been already done. + Pro zobrazení této stránky musí QupZilla znovu odeslat požadavek, který zopakuje akci (jako např. hledání nebo potvrzení objednávky), která byla provedena již dříve. + + + + Server refused the connection + Server odmítl spojení + + + + Server closed the connection + Serve ukončil spojení + + + + Server not found + Server nenalezen + + + + Connection timed out + Spojení vypršelo + + + + Untrusted connection + Nedůvěryhodné spojení + + + + Unknown error + Neznámá chyba + + + + Error code %1 + Chybový kód %1 + + + + Failed loading page + Chyba při načítání stránky + + + + QupZilla can't load page at %1. + QupZilla nemůže načíst stránku %1. + + + + Check the address for typing errors such as <b>ww.</b>example.com instead of <b>www.</b>example.com + Zkontrolujte, zda je adresa napsána správně a neobsahuje chyby jako <b>ww.</b>server.cz místo <b>www</b>.server.cz + + + + If you are unable to load any pages, check your computer's network connection. + Pokud se vám nezobrazují ani ostatní stránky, zkontrolujte síťové připojení svého počítače. + + + + If your computer or network is protected by a firewall or proxy, make sure that QupZilla is permitted to access the Web. + Pokud je váš počítač chráněn firewallem a nebo proxy serverem, zkontrolujte, zda má QupZilla přístup na Internet. + + + + Try Again + Zkusit znovu + + + + WebView + + + + Loading... + Načítám... + + + + + No Named Page + Bezejmenná stránka + + + + Done + Hotovo + + + + + + New tab + Nový panel + + + + Open link in new window + Otevřít odkaz v novém okně + + + + Open link in new tab + Otevřít odkaz v novém panelu + + + + Bookmark link + Přidat odkaz do záložek + + + + Save link as... + Uložit odkaz jako... + + + + Send link... + Odeslat odkaz... + + + + Copy link address + Kopírovat adresu odkazu + + + + Show image + Zobrazit obrázek + + + + Copy image + Kopírovat obrázek + + + + Copy image address + Coyp image address + Kopírovat adresu obrázku + + + + Save image as... + Uložit obrázek jako... + + + + Send image... + Odeslat obrázek... + + + + Back + Zpět + + + + Forward + Vpřed + + + + Reload + Obnovit + + + + Stop + Zastavit + + + + Bookmark page + Přidat stránku do záložek + + + + Save page as... + Uložit stránku jako... + + + + Send page... + Odeslat stránku... + + + + Select all + Vybrat vše + + + + Show source code + Zobrazit zdrojový kód + + + + Show info about site + Zobrazit informace o stránce + + + + Show Web Inspector + Zobrazit Web Inspektor + + + + Search + Hledat + + + + ... on Google + ... na Googlu + + + diff --git a/translations/sk_SK.ts b/translations/sk_SK.ts new file mode 100644 index 000000000..422483e8b --- /dev/null +++ b/translations/sk_SK.ts @@ -0,0 +1,2572 @@ + + + + + AboutDialog + + + About QupZilla + O QupZille + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Ubuntu'; font-size:10pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Ubuntu'; font-size:10pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> + + + + Authors + Autori + + + + + Authors and Contributors + Autori a prispievatelia + + + + + < About QupZilla + < O QupZille + + + + <p><b>Application version %1</b><br/> + <p><b>Verzia aplikácie %1</b><br/> + + + + <b>WebKit version %1</b></p> + <b>Verzia WebKitu %1</b></p> + + + + <p>&copy; %1 %2<br/>All rights reserved.<br/> + <p>&copy; %1 %2<br/>Všetky práva vyhradené.<br/> + + + + Build time: %1 </p> + Čas vytvorenia: %1 </p> + + + + <p><b>Main developers:</b><br/>%1 &lt;%2&gt;</p> + <p><b>Hlavní vývojári:</b><br/>%1 &lt;%2&gt;</p> + + + + <p><b>Other contributors:</b><br/>%1</p> + <p><b>Ostatní prispievatelia:</b><br/>%1</p> + + + + <p><b>Thanks to:</b><br/>%1</p> + <p><b>Poďakovanie:</b><br/>%1</p> + + + + AutoFillManager + + + Password Manager + Správca hesiel + + + + Passwords + Heslá + + + + + Server + Server + + + + Password + Heslo + + + + + Remove + Odstrániť + + + + Edit + Upraviť + + + + + Remove All + Odstrániť všetko + + + + Exceptions + Výnimky + + + + Confirmation + Potvrdenie + + + + Are you sure to delete all passwords on your computer? + Ste si istý, že chcete vymazať všetky heslá vo Vašom počítači? + + + + Edit password + Upraviť heslo + + + + Change password: + Zmeniť heslo: + + + + AutoFillModel + + + <b>Would you like to save this password?</b><br>To show all stored passwords open the Password Manager panel in preferences. + <b>Prajete si uložiť toto heslo?</b><br>Na zobrazenie všetkých uložených hesiel si otvorte panel Správca hesiel v predvoľbách. + + + + Never for this site + Nikdy pre túto stránku + + + + Not now + Nie teraz + + + + BookmarksManager + + + Bookmarks + Záložky + + + + Title + Nadpis + + + + Url + Adresa + + + + Delete + Odstrániť + + + + Del + Del + + + + Add Folder + Pridať zložku + + + + Add new folder + Pridať novú zložku + + + + Choose name for new bookmark folder: + Zvoľte meno pre novú zložku: + + + + New Tab + Nový panel + + + + + + + + + + + Bookmarks In Menu + Záložky v menu + + + + + + + + + + + Bookmarks In ToolBar + Panel záložiek + + + + Open link in actual tab + Otvoriť odkaz v aktuálnom panely + + + + Open link in new tab + Otvoriť odkaz na novom panely + + + + Move bookmark to folder + Presunúť záložku do zložky + + + + + + + + Unsorted Bookmarks + Netriedené záložky + + + + Close + Zavrieť + + + + <b>Warning: </b>You already have this page bookmarked! + <b>Upozornenie: </b>Túto stránku máte už v záložkách! + + + + Choose name and location of bookmark. + Zvoľte meno a umiestnenie záložky. + + + + Add New Bookmark + Pridať záložku + + + + Choose folder for bookmarks: + Zvoľte zložku pre záložku: + + + + Bookmark All Tabs + Pridať všetky panely do záložiek + + + + BookmarksToolbar + + + Bookmarks + Záložky + + + + Bookmark Current Page + Pridať stránku do záložiek + + + + Bookmark All Tabs + Pridať všetky panely do záložiek + + + + Organize Bookmarks + Organizovať záložky + + + + Reload Toolbar + Obnoviť panel + + + + Hide Most Visited + Skryť Najnavštevovanejšie + + + + Show Most Visited + Zobraziť Najnavštevovanejšie + + + + Hide Toolbar + Skryť panel + + + + Most visited + Najnavštevovanejšie + + + + Sites You visited the most + Najnavštívenejšie stránky + + + + BookmarksWidget + + + Edit This Bookmark + Upraviť túto záložku + + + + Remove Bookmark + Odstrániť záložku + + + + Name: + Názov: + + + + Folder: + Zložka: + + + + Save + Uložiť + + + + Close + Zavrieť + + + + Bookmarks In Menu + Záložky v menu + + + + Bookmarks In ToolBar + Panel záložiek + + + + Unsorted Bookmarks + Nezotriedené záložky + + + + ClearPrivateData + + + Clear Recent History + Vymazať nedávnu históriu + + + + Choose what you want to delete: + Vyberte, čo chcete zmazať: + + + + Clear history + Zmazať históriu + + + + Clear cookies + Zmazať cookies + + + + Clear cache + Zmazať vyrovnávaciu pamäť + + + + Clear icons + Zmazať ikony + + + + Clear cookies from Adobe Flash Player + Zmazať cookies z Adobe Flash Playeru + + + + ClickToFlash + + + Flash blocked by ClickToFlash + + + + + Add %1 to whitelist + + + + + CookieManager + + + Cookies + Cookies + + + + Find: + Hľadať: + + + + These cookies are stored on your computer: + Tieto cookies sú uložené v počítači: + + + + Server + Server + + + + Cookie name + Názov cookies + + + + Name: + Názov: + + + + Value: + Hodnota: + + + + Server: + Server: + + + + Path: + Cesta: + + + + Secure: + Zasielane pre: + + + + Expiration: + Platnosť do: + + + + + + + + + + + + + + + <cookie not selected> + <nebolo zvolene cookie> + + + + Remove all cookies + Odstrániť všetky cookies + + + + + Remove cookie + Odstrániť cookie + + + + Del + Del + + + + Search + Hľadať + + + + Confirmation + Potvrdenie + + + + Are you sure to delete all cookies on your computer? + Skutočne chcete vymazať všetky cookies z počítača? + + + + Remove cookies + Odstrániť cookies + + + + Secure only + Zabezpečené pripojenie + + + + All connections + Všetky pripojenia + + + + Session cookie + Konca relácie + + + + DownloadItem + + + Form + Form + + + + A Clockwork Orange.avi + + + + + Remaining 26 minutes - 339MB of 693 MB (350kB/s) + + + + + Remaining time unavailable + + + + + Done - %1 + + + + + + Cancelled + + + + + few seconds + + + + + seconds + sekúnd + + + + minutes + minút + + + + hours + hodín + + + + Remaining %1 - %2 of %3 (%4) + + + + + Cancelled - %1 + + + + + Open File + Otvoriť súbor + + + + Open Folder + + + + + Cancel downloading + + + + + Clear + + + + + Error + + + + + Error: Cannot write to file! + Chyba: Nejde zapisovať do súboru! + + + + Error: + Chyba: + + + + DownloadManager + + + %1% of %2 files (%3) %4 remaining + + + + + % - Download Manager + + + + + Save file as... + Uložiť súbor ako... + + + + Warning + + + + + Are you sure to quit? All uncompleted downloads will be cancelled! + + + + + NoNameDownload + BezNázvu + + + + + + Download Manager + + + + + Clear + + + + + DownloadOptionsDialog + + + Opening + Otváram + + + + which is a: + čo je: + + + + from: + z: + + + + You have chosen to open + Zvolili ste si otvoriť + + + + <b>What should QupZilla do with this file?</b> + <b>Čo má QupZilla urobiť s týmto súborom?</b> + + + + Open... + Otvoriť... + + + + Save File + Uložiť súbor + + + + HistoryManager + + + History + História + + + + Search in history: + Hľadať v histórii: + + + + Title + Názov + + + + Url + Adresa + + + + Delete + Vymazať + + + + Del + Del + + + + Clear All History + Vymazať celú históriu + + + + Search + Hľadať + + + + New Tab + Nový panel + + + + Open link in actual tab + Otvoriť odkaz v aktuálnom panely + + + + Open link in new tab + Otvoriť odkaz na novom panely + + + + Close + Zavrieť + + + + Confirmation + Potvrdenie + + + + Are you sure to delete all history? + Skutočne chcete vymazať celú históriu? + + + + HistoryModel + + + Failed loading page + Chyba pri načítaní stránky + + + + + No Named Page + Stránka bez mena + + + + LocationBar + + + Show informations about this page + Zobraziť informácie o tejto stránke + + + + Add RSS from this page... + Pridať RSS kanál... + + + + + Bookmark this Page + Pridať stránku do záložiek + + + + Enter URL address or search on Google.com + Zadajte URL adresu alebo vyhľadajte na Google.com + + + + Add RSS channel with title ' + Pridať RSS kanál s názvom + + + + Read RSS news + Čítať RSS novinky + + + + Loading... + Nahrávam... + + + + Done + Hotovo + + + + + Edit this bookmark + Upraviť záložku + + + + .co.uk + Append domain name on ALT key = Should be different for every country + .sk + + + + LocationCompleter + + + Search %1 on Google.com +.......... + Vyhľadajte %1 na Google.com +.......... + + + + MainApplication + + + Last session crashed + Posledná relácia spadla + + + + <b>QupZilla crashed :-(</b><br/>Oops, last session of QupZilla ends with its crash. We are very sorry. Would you try to restore saved state? + <b>QupZilla spadla :-(</b><br/>Oops, posledná relácia QupZilly skončila chybou. Prepáčte. Chcete obnoviť uložený stav? + + + + NetworkManager + + + SSL Certificate Error! + Chyba zabezpečenia! + + + + The page you trying to access has following errors in SSL Certificate: + Stránka na ktorú sa pokušáte pripojiť obsahuje následujúce chyby v SSL Certifikáte: + + + + <b>Organization: </b> + <b>Organizácia: </b> + + + + <b>Domain Name: </b> + <b>Doména: </b> + + + + <b>Expiration Date: </b> + <b>Platnosť do: </b> + + + + <b>Error: </b> + <b>Chyba: </b> + + + + Would you like to make exception for this certificate? + Chcete urobiť vynímku pre tento certifikát? + + + + SSL Certificate Error + Chyba zabezpečenia + + + + Authorization required + Požadovaná autorizácia + + + + Username: + Uživateľské meno: + + + + Password: + Heslo: + + + + Save username and password on this site + Uložiť meno a heslo pre túto sieť + + + + A username and password are being requested by %1. The site says: "%2" + Server %1 požaduje vaše uživateľské meno a heslo s komentárom: "%2" + + + + PluginsList + + + Form + Form + + + + Application Extensions + + + + + Allow Application Extensions to be loaded + + + + + Settings + Nastavenia + + + + Load Plugins + Načítať doplnky + + + + WebKit Plugins + + + + + <b>Click To Flash Plugin</b> + + + + + Click To Flash is a plugin which blocks auto loading of Flash content at page. You can always load it manually by clicking on the Flash play icon. + + + + + Whitelist + + + + + Add + + + + + Remove + Odstrániť + + + + Allow Click To Flash + + + + + Add site to whitelist + + + + + Server without http:// (ex. youtube.com) + + + + + Preferences + + + Preferences + Predvoľby + + + + General + Hlavné + + + + 0 + 0 + + + + + 1 + 1 + + + + 2 + 2 + + + + 3 + 3 + + + + Downloads + Sťahovanie + + + + 4 + 4 + + + + 5 + 5 + + + + 6 + 6 + + + + Plugins + Doplnky + + + + 7 + 7 + + + + 8 + 8 + + + + After launch: + Po spustení: + + + + Open blank page + Otvoriť prádznu stránku + + + + + Open homepage + Otvoriť domovskú stránku + + + + Restore session + Obnoviť reláciu + + + + Homepage: + Domovská stránka: + + + + On new tab: + Pri otvorení nového panelu: + + + + Open blank tab + Otvoriť prádzny panel + + + + Open other page... + Otvoriť inú stránku... + + + + <b>Profiles</b> + <b>Profily</b> + + + + Startup profile: + Štartovný profil: + + + + default + default + + + + Create New + Nový profil + + + + Delete + Vymazať + + + + <b>Launching</b> + <b>Spustení</b> + + + + QupZilla + QupZilla + + + + <b>General</b> + <b>Hlavné</b> + + + + Window + Okno prehliadača + + + + Show StatusBar on start + Zobraziť StatusBar pri štarte + + + + Show Bookmarks ToolBar on start + Zobraziť panel záložiek pri štarte + + + + Show Navigation ToolBar on start + Zobraziť navigačný panel pri štarte + + + + <b>Navigation ToolBar</b> + <b>Navigačný panel</b> + + + + <b>Downloads</b> + <b>Sťahovanie</b> + + + + <b>Network Cache</b> + <b>Sieťová cache</b> + + + + Allow storing network cache on disk + Povoliť ukladanie sieťovej cache na disk + + + + <b>Cookies</b> + <b>Cookies</b> + + + + <b>Web storage</b> + <b>Webové úložisko</b> + + + + <b>Address Bar behaviour</b> + <b>Chovanie adresového riadku</b> + + + + <b>Language</b> + <b>Jazyk</b> + + + + Show Home button + Zobraziť tlačidlo domov + + + + Show Back / Forward buttons + Zobraziť tlačidlo Späť / Dopredu + + + + <b>Browser Window</b> + <b>Okno prehliadača</b> + + + + Tabs + Panely + + + + + Use actual + Použiť aktuálny + + + + <b>Background<b/> + <b>Pozadie<b/> + + + + Use background image + Použiť pozadie + + + + Use transparent background + Použiť priehľadné pozadie + + + + Choose image... + Zvoľte obrázok... + + + + Reset default + Obnoviť povodné + + + + <b>Tabs behavior</b> + <b>Chovanie panelov</b> + + + + Make tabs movable + Presunúť panely pretiahnutím + + + + Hide close button if there is only one tab + Skryť zavieracie tlačidlo pri jednom panely + + + + Hide tabs when if there is only one tab + Skryť zoznam panelov pri jednom panely + + + + Maximum + Maximálne + + + + 50 MB + 50 MB + + + + Automatically close download dialog after finish + Automaticky zatvárať dialóg po skončení sťahovania + + + + Ask everytime for download location + Pri každom súbore sa spýtať kde ho uložiť + + + + Use defined location: + Uložiť všetky súbory do: + + + + ... + ... + + + + Browsing + Prehliadanie + + + + Load images + Nahrať obrázky + + + + Allow JAVA + Povoliť JAVA + + + + Allow JavaScript + Povoliť JavaScript + + + + Allow Plugins (Flash plugin) + Povoliť pluginy (Flash plugin) + + + + Maximum pages in cache: + Maximum stránok v cache: + + + + Password Manager + Správca hesiel + + + + <b>AutoFill options</b> + <b>Možnosťi doplňovania</b> + + + + Allow saving passwords from sites + Povoliť ukladanie hesiel zo stránok + + + + Privacy + Súkromie + + + + This is text color used in Menu + Toto je text používaný v menu + + + + Menu text color + Farba menu textu + + + + Activate last tab when closing active tab + Aktivovať posledný panel pri zatváraní aktuálneho + + + + <b>WebKit</b> + <b>WebKit</b> + + + + Block PopUp windows + Blokovať vyskakovacie okna + + + + Allow DNS Prefetch + Povoliť DNS Prefetch + + + + JavaScript can access clipboard + Povoliť JavaScriptu prístup do schránky + + + + Include links in focus chain + Označovať odkazy tabulátorom + + + + Zoom text only + Približovať len text + + + + Print element background + Tlačiť pozadie objektu + + + + Wheel scrolls + Koliesko posúva + + + + lines + riadky + + + + Filter Tracking Cookies + Filtrovať sledovacie cookies + + + + Allow storing of cookies + Povoliť prijímanie cookies + + + + Delete cookies on close + Vymazať cookies pri zavrení prehliadača + + + + Match domain exactly + Požadovať presnú zhodu domény + + + + <b>Warning:</b> Match domain exactly and Filter Tracking Cookies options can lead to deny some cookies from sites. If you have problems with cookies, try to disable this options first! + <b>Upozornenie:</b> Možnosti požadovanie presnej zhody domény a filtrovať sledovacie cookies môžú viesť k odmietnutiu niektorých cookies zo stránok. Ak máte problémy s cookies, skuste tieto možnosti najskôr zakázať! + + + + Cookies Manager + Správca cookies + + + + Allow storing web icons + Povoliť ukládanie ikon + + + + Allow saving history + Povoliť ukladanie histórie + + + + Delete history on close + Vymazať históriu pri zavretí prehliadača + + + + Other + Ostatné + + + + Select all text by double clicking in address bar + Select all text by clicking at address bar + Označiť všetko pri dvojtom kliknutí do adresného riadku + + + + Add .com domain by pressing CTRL key + Pridať .com doménu stlačením CTRL klávesy + + + + Add .co.uk domain by pressing ALT key + Pridať .sk doménu stlačením ALT klávesy + + + + Available translations: + Dostupné preklady: + + + + In order to change language, you must restart browser. + K zmene jazyku je potrebný reštart prehliadača. + + + + Choose download location... + Vyberte zložku pre sťahovanie... + + + + Choose background location... + Zvoľte lokáciu pozadia... + + + + QupZilla + + + File + Súbor + + + + Edit + Upraviť + + + + Tools + Nástroje + + + + Help + Pomocník + + + + Open File + Otvoriť súbor + + + + View + Zobraziť + + + + Bookmarks + Záložky + + + + History + História + + + + Save Page As... + Uložiť stránku ako... + + + + Print + Tlačiť + + + + Quit + Koniec + + + + New Tab + Nový panel + + + + Undo + Späť + + + + Redo + Dopredu + + + + Preferences + Predvoľby + + + + About QupZilla + O QupZille + + + + About Qt + O Qt + + + + Close Tab + Zavriet panel + + + + Find + Hľadať + + + + IP Address of current page + IP Adresa aktuálnej stránky + + + + Cut + Vystrihnúť + + + + Copy + Kopírovať + + + + Paste + Prilepiť + + + + Delete + Vymazať + + + + Select All + Vybrať všetko + + + + Navigation Toolbar + Navigačná lišta + + + + Status Bar + Status bar + + + + + Stop + Zastaviť + + + + + Reload + Obnoviť + + + + Zoom In + Priblížiť + + + + Zoom Out + Oddialiť + + + + Reset + Resetovať + + + + Page Source + Zdrojový kód stránky + + + + Fullscreen + Celá obrazovka + + + + + Back + Späť + + + + + Forward + Dopredu + + + + + Home + Domov + + + + Show All History + Zobraziť celú hstóriu + + + + Bookmark This Page + Pridať stránku do záložiek + + + + Bookmark All Tabs + Pridať všetky panely do záložiek + + + + Organize Bookmarks + Organizovať záložky + + + + New Window + Nové okno + + + + Close Window + Zavrieť okno + + + + Open Location + Otvoriť adresu + + + + Send Link... + Poslať odkaz... + + + + Report Bug + Nahlásiť chybu + + + + Web Search + Hladať na webu + + + + Page Info + Informácie o stránke + + + + Download Manager + + + + + Clear Recent History + Vymazať nedávnu históriu + + + + Private Browsing + Súkromné prehliadanie + + + + RSS Reader + RSS čítač + + + + Start Private Browsing + Spustiť anonymné prehliadanie + + + + Cookies Manager + Správca cookies + + + + Bookmarks Toolbar + Panel záložiek + + + + Navigation + Navigácia + + + + Main Menu + Hlavné menu + + + + Exit Fullscreen + Ukončiť režím Fullscreen + + + + Private Browsing Enabled + Súkromné prehliadanie je zapnuté + + + + Flash Plugin Enabled + Flash Plugin zapnutý + + + + Menu Bar + Menu + + + + Bookmarks In ToolBar + Panel záložiek + + + + + Empty + Prázdny + + + + New tab + Nový panel + + + + Web Inspector + Web inšpektor + + + + Open file... + Otvoriť súbor... + + + + Are you sure you want to turn on private browsing? + Ste si istý, že chcete zapnúť súkromné prehliadanie? + + + + When private browsing is turned on, some actions concerning your privacy will be disabled: + So zapnutým súkromným prehliadaním sú niektoré akcie týkajúce sa súkromia vypnuté: + + + + Webpages are not added to the history. + Stránky nie sú pridávané do histórie. + + + + New cookies are not stored, but current cookies can be accessed. + Nové cookies nie sú prijímané, ale súčasné cookies sú zasielané. + + + + Your session won't be stored. + Vaša relácia nebude uložená. + + + + Until you close the window, you can still click the Back and Forward buttons to return to the webpages you have opened. + Dokiaľ nezavriete prehliadač, tak stále môžete používať tlačidla Späť a Dopredu k vráteniu sa na stránky, ktoré ste mali otvorené. + + + + + There are still open tabs + Stále sú otvorené panely + + + + + There are still %1 open tabs and your session won't be stored. Are you sure to quit? + Stále sú otvorené %1 panely a Vaša relácia nebude uložená. Skutočne chcete skončiť? + + + + RSSManager + + + RSS Reader + RSS čítačka + + + + Empty + Prázdny + + + + You don't have any RSS Feeds.<br/> +Please add some with RSS icon in navigation bar on site which offers feeds. + Nemáte žiadny RSS kanál.<br/> +Prosím pridajte si nejaký kliknutím na RSS ikonku v navigačnom riadku. + + + + Reload + Obnoviť + + + + Edit feed + Upraviť kanál + + + + Delete feed + Zmazať kanál + + + + News + Novinky + + + + + Loading... + Nahrávam... + + + + Fill title and URL of a feed: + Vyplnte názov a adresu kanálu: + + + + Feed title: + Názov kanálu: + + + + Feed URL: + Adresa kanálu: + + + + Edit RSS Feed + Upraviť RSS + + + + Open link in actual tab + Otvoriť odkaz v aktuálnom panely + + + + Open link in new tab + Otvoriť odkaz na novom panely + + + + Close + Zavrieť + + + + + New Tab + Nový panel + + + + Error in fetching feed + Chyba pri sťahovaní kanálu + + + + RSS feed added + RSS kanál pridaný + + + + RSS with title '%1' has been successfuly added. + RSS kanál s názvom '%1' bol úspešne pridaný. + + + + RSS feed duplicated + Duplikovaný kanál + + + + You already have this feed. + Tento kanál už odoberáte. + + + + SearchToolBar + + + + Search + Hľadať + + + + Highlight occurrences + Zvýrazniť + + + + Next + Ďalší + + + + Previous + Predchádzajúci + + + + Case sensitive + Rozlišovať veľkosť + + + + Find: + Nájsť: + + + + No results found. + Žiadny výsledok. + + + + SiteInfo + + + Site Info + Informácie o stránke + + + + Tag + Tag + + + + Value + Hodnota + + + + Image + Obrázok + + + + Image address + Adresa obrázku + + + + Site address: + Adresa stránky: + + + + Site name: + Méno stránky: + + + + Meta tags of site: + Meta tágy na stránke: + + + + Images on site: + Orázky na stránke: + + + + No Named Page + Stránka bez mena + + + + SiteInfoWidget + + + Form + Form + + + + More... + Viac... + + + + Your connection to this site is <b>secured</b>. + Vaše pripojenie na túto sieť je <b>chránené</b>. + + + + Your connection to this site is <b>unsecured</b>. + Vaše pripojenie na túto sieť je <b>nechránené</b>. + + + + This is Your <b>%1.</b> visit of this site. + Toto je Vaša <b>%1.</b> návšteva tejto siete. + + + + first + prvá + + + + second + druhá + + + + third + tretia + + + + This is Your <b>%1</b> visit of this site. + Toto je Vaša <b>%1</b> návšteva tejto siete. + + + + You have <b>never</b> visited this site before. + Túto sieť ste <b>nikdy</b> nenavštívili. + + + + SourceViewer + + + Source of + Zdrojový kód + + + + TabBar + + + + New tab + Nový panel + + + + Back + Späť + + + + Forward + Dopredu + + + + Stop Tab + Zastaviť panel + + + + Reload Tab + Obnoviť panel + + + + Bookmark This Tab + Založiť tento panel + + + + + Bookmark All Tabs + Založiť všetky panely + + + + + Restore Closed Tab + Obnoviť zavretý panel + + + + Close Other Tabs + Zavrieť ostatné panely + + + + + Reload All Tabs + Restore closed tab + Obnoviť všetky panely + + + + Close + Zavrieť + + + + TabWidget + + + Show list of opened tabs + Zobraziť zoznam otvorených panelov + + + + Add Tab + Pridať panel + + + + Loading... + Nahrávam... + + + + + No Named Page + Stránka bez mena + + + + Actually You have %1 opened tabs + Momentálne otvorených %1 panelov + + + + New tab + Nový panel + + + + Updater + + + Go to download page + Prejsť k sťahovaniu + + + + Go to QupZilla website + Ísť na stránku QupZilly + + + + Hide notification + Skryť upozornenia + + + + QupZilla is checking for updates + QupZilla kontroluje aktualizácie + + + + QupZilla found a new version! + QupZilla našla novú verziu! + + + + New version is available + Dostupná nová verzia + + + + New version of QupZilla %1 is available! + Nová verzia QupZily %1 je dostupná! + + + + + QupZilla Update + QupZilla Update + + + + WebPage + + + Confirmation + Potvrdenie + + + + To show this page, QupZilla must resend request witch do it again (like searching on making an shoping, witch has been already done. + Na zobrazenie tejto stránky musí QuipZilla znovu odoslať požiadavok, ktorý zopakuje akciu (napr. hľadanie alebo potvrdenie objednávky), ktorá bola vykonaná už skorej. + + + + Server refused the connection + Server odmietol spojenie + + + + Server closed the connection + Server ukončil spojenie + + + + Server not found + Server nebol nájdený + + + + Connection timed out + Spojenie vypršalo + + + + Untrusted connection + Nedôveryhodné spojenie + + + + Unknown error + Neznáma chyba + + + + Error code %1 + Chybový kód %1 + + + + Failed loading page + Chyba pri načítaní stránky + + + + QupZilla can't load page at %1. + QupZilla nemôže načítať stránku %1. + + + + Check the address for typing errors such as <b>ww.</b>example.com instead of <b>www.</b>example.com + Skontrolujte, či je adresa napísaná správne a neobsahuje chyby ako <b>ww.</b>server.sk miesto <b>www.</b>server.sk + + + + If you are unable to load any pages, check your computer's network connection. + Pokiaľ sa vám nezobrazujú ani ostatné stránky, tak skontrolujte sieťové pripojenie svojho počítača. + + + + If your computer or network is protected by a firewall or proxy, make sure that QupZilla is permitted to access the Web. + Pokiaľ je váš počítač chránený firewallom alebo proxy severom, tak skontrolujte či má QupZilla prístup na internet. + + + + Try Again + Skúsiť znova + + + + WebView + + + + Loading... + Načítavam... + + + + + No Named Page + Stránka bez mena + + + + Done + Hotovo + + + + + + New tab + Nový panel + + + + Open link in new window + Otvoriť odkaz v novom okne + + + + Open link in new tab + Otvoriť odkaz na novom panely + + + + Bookmark link + Pridať odkaz do záložiek + + + + Save link as... + Odoslať odkaz... + + + + Send link... + Odoslať odkaz... + + + + Copy link address + Kopírovať adresu odkazu + + + + Show image + Zobraziť obrázok + + + + Copy image + Kopírovať obrázok + + + + Copy image address + Kopírovať adresu obrázku + + + + Save image as... + Uložiť obrázok ako... + + + + Send image... + Odoslať obrázok... + + + + Back + Späť + + + + Forward + Dopredu + + + + Reload + Obnoviť + + + + Stop + Zastaviť + + + + Bookmark page + Pridať stránku do záložiek + + + + Save page as... + Uložiť stránku ako... + + + + Send page... + Odoslať stránku... + + + + Select all + Vybrať všetko + + + + Show source code + Zobraziť zdrojový kód + + + + Show info about site + Zobraziť informácie o stránke + + + + Show Web Inspector + Zobraziť Web inšpektora + + + + Search + Hľadať + + + + ... on Google + ... na Googlu + + + diff --git a/windows/FileAssociation.nsh b/windows/FileAssociation.nsh new file mode 100644 index 000000000..71a9162ef --- /dev/null +++ b/windows/FileAssociation.nsh @@ -0,0 +1,190 @@ +/* +_____________________________________________________________________________ + + File Association +_____________________________________________________________________________ + + Based on code taken from http://nsis.sourceforge.net/File_Association + + Usage in script: + 1. !include "FileAssociation.nsh" + 2. [Section|Function] + ${FileAssociationFunction} "Param1" "Param2" "..." $var + [SectionEnd|FunctionEnd] + + FileAssociationFunction=[RegisterExtension|UnRegisterExtension] + +_____________________________________________________________________________ + + ${RegisterExtension} "[executable]" "[extension]" "[description]" + +"[executable]" ; executable which opens the file format + ; +"[extension]" ; extension, which represents the file format to open + ; +"[description]" ; description for the extension. This will be display in Windows Explorer. + ; + + + ${UnRegisterExtension} "[extension]" "[description]" + +"[extension]" ; extension, which represents the file format to open + ; +"[description]" ; description for the extension. This will be display in Windows Explorer. + ; + +_____________________________________________________________________________ + + Macros +_____________________________________________________________________________ + + Change log window verbosity (default: 3=no script) + + Example: + !include "FileAssociation.nsh" + !insertmacro RegisterExtension + ${FileAssociation_VERBOSE} 4 # all verbosity + !insertmacro UnRegisterExtension + ${FileAssociation_VERBOSE} 3 # no script +*/ + + +!ifndef FileAssociation_INCLUDED +!define FileAssociation_INCLUDED + +!include Util.nsh + +!verbose push +!verbose 3 +!ifndef _FileAssociation_VERBOSE + !define _FileAssociation_VERBOSE 3 +!endif +!verbose ${_FileAssociation_VERBOSE} +!define FileAssociation_VERBOSE `!insertmacro FileAssociation_VERBOSE` +!verbose pop + +!macro FileAssociation_VERBOSE _VERBOSE + !verbose push + !verbose 3 + !undef _FileAssociation_VERBOSE + !define _FileAssociation_VERBOSE ${_VERBOSE} + !verbose pop +!macroend + + + +!macro RegisterExtensionCall _EXECUTABLE _EXTENSION _DESCRIPTION + !verbose push + !verbose ${_FileAssociation_VERBOSE} + Push `${_DESCRIPTION}` + Push `${_EXTENSION}` + Push `${_EXECUTABLE}` + ${CallArtificialFunction} RegisterExtension_ + !verbose pop +!macroend + +!macro UnRegisterExtensionCall _EXTENSION _DESCRIPTION + !verbose push + !verbose ${_FileAssociation_VERBOSE} + Push `${_EXTENSION}` + Push `${_DESCRIPTION}` + ${CallArtificialFunction} UnRegisterExtension_ + !verbose pop +!macroend + + + +!define RegisterExtension `!insertmacro RegisterExtensionCall` +!define un.RegisterExtension `!insertmacro RegisterExtensionCall` + +!macro RegisterExtension +!macroend + +!macro un.RegisterExtension +!macroend + +!macro RegisterExtension_ + !verbose push + !verbose ${_FileAssociation_VERBOSE} + + Exch $R2 ;exe + Exch + Exch $R1 ;ext + Exch + Exch 2 + Exch $R0 ;desc + Exch 2 + Push $0 + Push $1 + + ReadRegStr $1 HKCR $R1 "" ; read current file association + StrCmp "$1" "" NoBackup ; is it empty + StrCmp "$1" "$R0" NoBackup ; is it our own + WriteRegStr HKCR $R1 "backup_val" "$1" ; backup current value +NoBackup: + WriteRegStr HKCR $R1 "" "$R0" ; set our file association + + ReadRegStr $0 HKCR $R0 "" + StrCmp $0 "" 0 Skip + WriteRegStr HKCR "$R0" "" "$R0" + WriteRegStr HKCR "$R0\shell" "" "open" + WriteRegStr HKCR "$R0\DefaultIcon" "" "$R2,0" +Skip: + WriteRegStr HKCR "$R0\shell\open\command" "" '"$R2" "%1"' + WriteRegStr HKCR "$R0\shell\edit" "" "Edit $R0" + WriteRegStr HKCR "$R0\shell\edit\command" "" '"$R2" "%1"' + + Pop $1 + Pop $0 + Pop $R2 + Pop $R1 + Pop $R0 + + !verbose pop +!macroend + + + +!define UnRegisterExtension `!insertmacro UnRegisterExtensionCall` +!define un.UnRegisterExtension `!insertmacro UnRegisterExtensionCall` + +!macro UnRegisterExtension +!macroend + +!macro un.UnRegisterExtension +!macroend + +!macro UnRegisterExtension_ + !verbose push + !verbose ${_FileAssociation_VERBOSE} + + Exch $R1 ;desc + Exch + Exch $R0 ;ext + Exch + Push $0 + Push $1 + + ReadRegStr $1 HKCR $R0 "" + StrCmp $1 $R1 0 NoOwn ; only do this if we own it + ReadRegStr $1 HKCR $R0 "backup_val" + StrCmp $1 "" 0 Restore ; if backup="" then delete the whole key + DeleteRegKey HKCR $R0 + Goto NoOwn + +Restore: + WriteRegStr HKCR $R0 "" $1 + DeleteRegValue HKCR $R0 "backup_val" + DeleteRegKey HKCR $R1 ;Delete key with association name settings + +NoOwn: + + Pop $1 + Pop $0 + Pop $R1 + Pop $R0 + + !verbose pop +!macroend + +!endif # !FileAssociation_INCLUDED diff --git a/windows/install.ico b/windows/install.ico new file mode 100644 index 0000000000000000000000000000000000000000..0520a17e81400b9a29e310da72118ed074d54747 GIT binary patch literal 9662 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 literal 0 HcmV?d00001 diff --git a/windows/installer.nsi b/windows/installer.nsi new file mode 100644 index 000000000..8e821c040 --- /dev/null +++ b/windows/installer.nsi @@ -0,0 +1,169 @@ +!include "FileAssociation.nsh" + +SetCompressor /SOLID /FINAL lzma + +!define PRODUCT_NAME "QupZilla" +!define /date PRODUCT_VERSION "0.9.7" +!define PRODUCT_DIR_REGKEY "Software\Microsoft\Windows\CurrentVersion\App Paths\qupzilla.exe" +!define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" +!define PRODUCT_UNINST_ROOT_KEY "HKLM" + +!include "MUI.nsh" +!define MUI_ABORTWARNING +!define MUI_ICON "wininstall\install.ico" +!define MUI_UNICON "wininstall\uninstall.ico" +!define MUI_WELCOMEFINISHPAGE_BITMAP "wininstall\welcome.bmp" +!define MUI_UNWELCOMEFINISHPAGE_BITMAP "wininstall\welcome.bmp" + +!insertmacro MUI_PAGE_WELCOME +!insertmacro MUI_PAGE_DIRECTORY +!insertmacro MUI_PAGE_INSTFILES + +!define MUI_FINISHPAGE_RUN "$INSTDIR\qupzilla.exe" +!insertmacro MUI_PAGE_FINISH + +!insertmacro MUI_UNPAGE_WELCOME +!insertmacro MUI_UNPAGE_CONFIRM +!insertmacro MUI_UNPAGE_INSTFILES +!insertmacro MUI_UNPAGE_FINISH + +!insertmacro MUI_LANGUAGE "English" +!insertmacro MUI_LANGUAGE "Czech" +!insertmacro MUI_LANGUAGE "Slovak" +!insertmacro MUI_LANGUAGE "Dutch" +!insertmacro MUI_LANGUAGE "French" +!insertmacro MUI_LANGUAGE "German" +!insertmacro MUI_LANGUAGE "Korean" +!insertmacro MUI_LANGUAGE "Russian" +!insertmacro MUI_LANGUAGE "Spanish" +!insertmacro MUI_LANGUAGE "Swedish" +!insertmacro MUI_LANGUAGE "Tradchinese" +!insertmacro MUI_LANGUAGE "Simpchinese" + +Name "${PRODUCT_NAME} ${PRODUCT_VERSION}" +OutFile "${PRODUCT_NAME} ${PRODUCT_VERSION} Installer.exe" +InstallDir "$PROGRAMFILES\${PRODUCT_NAME}\" +InstallDirRegKey HKLM "${PRODUCT_DIR_REGKEY}" "" +ShowInstDetails show +ShowUnInstDetails show + +Section "Main Components" + KillProcDLL::KillProc "qupzilla.exe" + Sleep 100 + SetOverwrite on + + SetOutPath "$INSTDIR" + File "qupzilla.exe" + File "AUTHORS" + File "COPYRIGHT" + File "GPLv3" + File "README" + File "libeay32.dll" + File "ssleay32.dll" + File "libgcc_s_dw2-1.dll" + File "libssl32.dll" + File "mingwm10.dll" + File "phonon4.dll" + File "QtCore4.dll" + File "QtGui4.dll" + File "QtNetwork4.dll" + File "QtSql4.dll" + File "QtWebKit4.dll" + + SetOutPath "$INSTDIR\data\default\profiles" + File "data\default\profiles\profiles.ini" + + SetOutPath "$INSTDIR\data\default\profiles\default" + File "data\default\profiles\default\background.png" + File "data\default\profiles\default\browsedata.db" + + SetOutPath "$INSTDIR\imageformats" + File "imageformats\qico4.dll" + File "imageformats\qsvg4.dll" + File "imageformats\qgif4.dll" + File "imageformats\qjpeg4.dll" + File "imageformats\qtiff4.dll" + File "imageformats\qmng4.dll" + + SetOutPath "$INSTDIR\locale" + File "locale\cs_CZ.qm" + File "locale\qt_cs.qm" + File "locale\sk_SK.qm" + File "locale\qt_sk.qm" + + SetOutPath "$INSTDIR\plugins" + File "plugins\ExamplePlugin.dll" + + SetOutPath "$INSTDIR\sqldrivers" + File "sqldrivers\qsqlite4.dll" + File "sqldrivers\qsqlodbc4.dll" + +SectionEnd + +Section Icons + SetOutPath "$INSTDIR" + CreateShortCut "$SMPROGRAMS\QupZilla.lnk" "$INSTDIR\qupzilla.exe" "" + CreateShortCut "$DESKTOP\QupZilla.lnk" "$INSTDIR\qupzilla.exe" "" + ${registerExtension} "$INSTDIR\qupzilla.exe" ".htm" "HTM_FILE" + ${registerExtension} "$INSTDIR\qupzilla.exe" ".html" "HTML_FILE" +SectionEnd + +Section Uninstaller + WriteUninstaller "$INSTDIR\uninstall.exe" + WriteRegStr HKLM "${PRODUCT_DIR_REGKEY}" "" "$INSTDIR\qupzilla.exe" + WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayName" "$(^Name)" + WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\uninstall.exe" + WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayIcon" "$INSTDIR\qupzilla.exe" + WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_VERSION}" +SectionEnd + +Section Uninstall + KillProcDLL::KillProc "qupzilla.exe" + Sleep 100 + Delete "$SMPROGRAMS\QupZilla.lnk" + Delete "$DESKTOP\QupZilla.lnk" + RMDir /r "$INSTDIR" + DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" + DeleteRegKey HKLM "${PRODUCT_DIR_REGKEY}" + ${unregisterExtension} ".htm" "HTM_FILE" + ${unregisterExtension} ".html" "HTML_FILE" +SectionEnd + +BrandingText "${PRODUCT_NAME} ${PRODUCT_VERSION} Installer" + +Function .onInit + ;Language selection dialog + + Push "" + Push ${LANG_ENGLISH} + Push English + Push ${LANG_CZECH} + Push Czech + Push ${LANG_DUTCH} + Push Dutch + Push ${LANG_FRENCH} + Push French + Push ${LANG_GERMAN} + Push German + Push ${LANG_KOREAN} + Push Korean + Push ${LANG_RUSSIAN} + Push Russian + Push ${LANG_SPANISH} + Push Spanish + Push ${LANG_SWEDISH} + Push Swedish + Push ${LANG_TRADCHINESE} + Push "Traditional Chinese" + Push ${LANG_SIMPCHINESE} + Push "Simplified Chinese" + Push ${LANG_SLOVAK} + Push Slovak + Push A ; A means auto count languages + ; for the auto count to work the first empty push (Push "") must remain + LangDLL::LangDialog "Installer Language" "Please select the language of the installer" + + Pop $LANGUAGE + StrCmp $LANGUAGE "cancel" 0 +2 + Abort +FunctionEnd diff --git a/windows/uninstall.ico b/windows/uninstall.ico new file mode 100644 index 0000000000000000000000000000000000000000..f31b71786862ce4f4f727af29da3fdfd54078196 GIT binary patch literal 9662 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 OfB*y_0D-R