From: Soren Stoutner Date: Wed, 27 Mar 2024 23:03:36 +0000 (-0700) Subject: Add a default folder icon to the edit folder dialog. https://redmine.stoutner.com... X-Git-Url: https://gitweb.stoutner.com/?p=PrivacyBrowserPC.git;a=commitdiff_plain;h=refs%2Fheads%2Fmaster;hp=8292f934246cb283e1b0c32f4388c674f275c7aa Add a default folder icon to the edit folder dialog. https://redmine.stoutner.com/issues/1179 --- diff --git a/.gitignore b/.gitignore index 9c087ba..eea15f5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ +/bin /build /.kdev4 diff --git a/CMakeLists.txt b/CMakeLists.txt index ff4b909..42b28dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright © 2022 Soren Stoutner . +# Copyright 2022-2023 Soren Stoutner . # # This file is part of Privacy Browser PC . # @@ -42,10 +42,20 @@ include(ECMInstallIcons) include(ECMQtDeclareLoggingCategory) include(FeatureSummary) +# Include the Position Independent Executable checker. +include(CheckPIESupported) + +# Check to see if Position Independent Executable is supported in the current linker and environment. +check_pie_supported() + +# Add the Position Independent Executable compiler flag if it is supported in the current linker and environment. +set(CMAKE_POSITION_INDEPENDENT_CODE TRUE) + # Load the Qt components. find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS Core Gui + PrintSupport Sql WebEngineCore WebEngineWidgets @@ -61,6 +71,7 @@ find_package(KF5 ${KDE_FRAMEWORKS_MIN_VERSION} REQUIRED COMPONENTS DBusAddons DocTools I18n + Notifications KIO XmlGui ) @@ -69,9 +80,9 @@ find_package(KF5 ${KDE_FRAMEWORKS_MIN_VERSION} REQUIRED COMPONENTS add_subdirectory(doc) add_subdirectory(src) -# Make it possible to use the PO files fetched by the fetch-translations step. -ki18n_install(po) -kdoctools_install(po) +# Install the PO files. These should be enabled once there are PO translations. +#ki18n_install(po) +#kdoctools_install(po) # Generate a summary. feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/COPYING b/COPYING index 3b01333..7cf0ecb 100644 --- a/COPYING +++ b/COPYING @@ -1,4 +1,4 @@ -Privacy Browser PC copyright © 2016-2017,2021-2022 Soren Stoutner . +Privacy Browser PC copyright 2016-2017,2021-2024 Soren Stoutner . This file is part of Privacy Browser PC . diff --git a/changelog b/changelog new file mode 100644 index 0000000..6ec8ce3 --- /dev/null +++ b/changelog @@ -0,0 +1,34 @@ +# Version 0.5 (12 October 2023) + * Add bookmarks. + * Add zoom controls to the status bar and a default zoom shortcut. + * Add keyboard shortcuts for the URL toolbar actions. + * Add an action to view page source. + * Change the domain settings combo boxes to list enabled above disabled. + * File downloads can now show the size before the download begins. + * Add PIE (Position Independent Executable) compiler flags. + * Fix a bug that sometimes allowed multiple domain settings to be created. + * Fix the download notification not being cleared on Xfce. + +# Version 0.4 (13 June 2023) + * Add a setting to control spatial navigation. + * Add an action to reload and bypass cache. + * Fix a crash if one Privacy Browser window is closed while a tab within it is loading. + * Add keyboard+click commands to the Handbook. + +# Version 0.3 (8 May 2023) + * Add the changelog to the Handbook + * Add the missing current domain settings icon on Gnome and Xfce. + * Make changes to build on Guix. + +# Version 0.2 (17 April 2023) + * Fix a crash on GNOME when downloading a file with local storage disabled. + * Fix problems with missing icons on GNOME. + * Display an animated favorite icon while a webpage is loading. + * Fix the Handbook on non-KDE systems. + * Change the order of entries in the WebEngine context menu. + * Make spellcheck languages easier to click on. + * Only generate a HTTP ping dialog if the request is made by the current tab. + * Add a section to the Handbook about HTTP pings. + +# Version 0.1 (11 March 2023) + * Initial release. diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index ffd9ed3..1f54e4a 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -1 +1,22 @@ +# Copyright 2023 Soren Stoutner . +# +# This file is part of Privacy Browser PC . +# +# Privacy Browser PC 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. +# +# Privacy Browser PC 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 Privacy Browser PC. If not, see . + +# Create the documentation. kdoctools_create_handbook(index.docbook INSTALL_DESTINATION ${KDE_INSTALL_DOCBUNDLEDIR}/en SUBDIR privacybrowser) + +# Install extra graphic files (the automatic installer doesn't pick up .gif files). +install(FILES loading.gif DESTINATION ${KDE_INSTALL_DOCBUNDLEDIR}/en/privacybrowser) diff --git a/doc/cookies.png b/doc/cookies.png new file mode 100644 index 0000000..fecf17c Binary files /dev/null and b/doc/cookies.png differ diff --git a/doc/index.docbook b/doc/index.docbook index b135352..27ee1fc 100644 --- a/doc/index.docbook +++ b/doc/index.docbook @@ -1,550 +1,1955 @@ - - privacybrowserpc"> - Translatable Entity"> - - - - -]> - - - - - - - - - - - - - - - - -The &kmyapplication; Handbook - - - - - -George -N. -Ugnacious - -gnu@kde.org - - - - - -2015 -George N. Ugnacious - -&CCBYSA4Notice; - - -2016-04-23 - - -Frameworks xx.yy or Plasma xx.yy or Applications xx.yy or xx.yy (Applications xx.yy) or $applicationname xx.yy - - - - - -&kmyapplication; is an application specially designed to do nothing you would -ever want. - - - - - - -KDE -kdeutils -Kapp -nothing -nothing else - - - - - - - - - -Introduction - - - - -&kmyapplication; is a program that lets you do absolutely nothing. Please report -any problems or feature requests to the &kde; mailing lists. - - - - -Using &kmyapplication; - - - - - - - - -Here's a screenshot of &kmyapplication; - - - - - - Screenshot - - - - - - - -More &kmyapplication; features - -It slices! It dices! and it comes with a free toaster! - -The Squiggle Tool - - - - - Squiggle - - is used to draw squiggly lines all over -the &kmyapplication; main window. It's not a bug, it's a feature! - - - - - - -Command Reference - - Enables search for menu items - -For Applications with default menu items and some special items where user -needs additional information use something like: -"Apart from the common KDE menu items you find these action in the menu: - -File -> Special Action: Explanation of special action - -Tools -> Special Tool: Explanation of special tool - -Use variablelist markup for this ---> - - -The main &kmyapplication; window - - -The File Menu - - - - - - -&Ctrl;N - -File -New - -Creates a new document - - - - - -&Ctrl;S - -File -Save - -Saves the document - - - - - -&Ctrl;Q - -File -Quit - -Quits &kmyapplication; - - - - - - - - - -The Settings and Help Menu - -&kmyapplication; has the common &kde; Settings and Help -menu items, for more information read the sections about the Settings Menu and Help Menu -of the &kde; Fundamentals. - - - - -The Help Menu - -&kmyapplication; has the common &kde; Help menu item, for more information read the section -about the Help Menu of the &kde; Fundamentals. - - - - -Menu Items -Apart from the common &kde; menus described in the Menu -chapter of the &kde; Fundamentals documentation &kmyapplication; has these application specific menu entries: - - - - - -The Help Menu -&kmyapplication; has a default &kde; Help menu as described in the -&kde; Fundamentals -with two additional entries: - - - - - - - -Developer's Guide to &kmyapplication; - - - - -Programming &kmyapplication; plugins is a joy to behold. - - - - - - -Questions and Answers - - - - - - -My Mouse doesn't work. How do I quit &kmyapplication;? - - -You silly goose! Check out the Commands -Section for the answer. - - - - -Why can I not twiddle my documents? - - -You can only twiddle your documents if you have the foobar.lib -installed. - - - - - - - - - -Credits and License - - -&kmyapplication; - - -Program copyright 2010-2014 John Q. Hacker jqh@kde.org - - -Contributors: - -Konqui the &kde; Dragon konqui@kde.org - -Tux the &Linux; Penguin tux@linux.org - - - - - -Documentation Copyright © 2012-2014 George N. Ugnacious gnu@kde.org - - - - - - -&underCCBYSA4; - - - - - - -&underGPL; -&underBSDLicense; -&underArtisticLicense; -&underX11License; - - + Copyright 2023-2024 Soren Stoutner . - + You should have received a copy of the GNU Free Documentation License + along with Privacy Browser PC. If not, see . --> - -Installation - - -How to obtain &kmyapplication; - - - -&install.intro.documentation; + + Privacy Browser"> - + + SorenStoutner"> + soren@stoutner.com"> - -Requirements + + +]> - - - -In order to successfully use &kmyapplication;, you need few libraries from -&kf5; 5.1 (bar, baz). Foobar.lib is -required in order to support the advanced &kmyapplication; features. &kmyapplication; uses -about 5 megs of memory to run, but this may vary depending on your -platform and configuration. - - - -All required libraries as well as &kmyapplication; itself can be found -on The &kmyapplication; home page. - - - - -You can find a list of changes at http://apps.kde.org/kapp. - - - - -Compilation and Installation - - - - - -&install.compile.documentation; - - - - -Configuration - -Don't forget to tell your system to start the dtd -dicer-toaster daemon first, or &kmyapplication; won't work! - - - - - -&documentation.index; + + + The &privacybrowser; Handbook + + + &Soren.Stoutner; &Soren.Stoutner.mail; + + + + + + 2016-2017, 2021-2024 + &Soren.Stoutner; + + + + &FDLNotice; + + + 2024-01-06 + + + &privacybrowser; version 0.5 + + + + + &privacybrowser; is a web browser that respects your privacy. + + + + The only way to prevent data from being abused is to prevent it from being collected in the first place. + + + + + + KDE + privacy + browser + + + + + + Introduction + + + &privacybrowser; is currently in an early alpha state. + Most of the features are not yet implemented, but I thought it would be useful to publish it so that users can track the progress and submit feedback. + + + + To distinguish between the Android and the PC version, the website, issue tracker, + and code base refer to this version as Privacy Browser PC. + + + + The best place to discuss the development of Privacy Browser is on the forum. + I also frequently post on my Mastodon account regarding the development status. + + + + + Qt WebEngine + + + Privacy Browser uses Qt WebEngine to render websites. + Qt WebEngine is based on the Chromium Blink source code. + Because Privacy Browser is built on the KDE Framework, + it currently uses the Qt 5 packages. + + + + The current Qt 5 packages are in long-term support mode. + From a feature perspective, Qt WebEngine 5.15.x is based on Chromium 87.0.4280.144. + Security fixes are backported every few months with each release. + + + + + + Bugs and Missing Features + + + There is a list of feature requests and known bugs at redmine.stoutner.com. + Users should anticipate that all the current features of Privacy Browser Android + will also be implemented in Privacy Browser PC. + There is no need at this point to create features requests for these as they will be added as I start working on each feature and have a better idea of how they will be implemented. + However, each feature that has already been implemented should be bug free. + If you discover a bug that is not already documented at redmine.stoutner.com please add it. + + + + Below is a list of known prominent bugs or missing features in this alpha release. + + + + The page zoom is momentarily reset every time a new URL is loaded. + If domain settings change the user agent, loading of the new URL is interrupted and the + previous site is reloaded. + Browser error messages are not displayed unless JavaScript is enabled. + Filter lists are not yet implemented. + + + + + + + Using &privacybrowser; + + + + + + + + + Screenshot + + + + + + + JavaScript + + + JavaScript allows web pages to run scripts (programs) on your device. It allows web pages to function more like apps, but it also allows web pages to spy on you. + Most of the tracking on the internet does not work when JavaScript is disabled. + JavaScript can be toggled by clicking on the privacy shield, which is blue if JavaScript is disabled and red when it is enabled. + + + + + + + JavaScript + + + + + + + + Local Storage + + + Local storage + in Privacy Browser encompasses cookies, DOM storage, IndexedDB, service workers, and the filesystem API. + Local storage can be toggled through an action on the toolbar. + + + + + Cookies + + + Cookies + allow websites to store small pieces of information for a specific host that are sent in the HTTP header every time the browser connects to that host. + Privacy Browser allows a maximum of 180 cookies with a maximum size of 4096 bytes per cookie to be set per domain. + Cookies are often used to track users across the web, particularly third-party cookies (which are completely blocked in Privacy Browser). + They are also used as a security mechanism on websites where you log in to identify it is you as you browse from page to page on a site. + + + + + + + + + + Cookies + + + + + + The cookies dialog is opened from . + Durable cookies are shared with all tabs that are opened after they are made durable and are preserved even when Privacy Browser is restarted. + This allows users to stay logged in to sites of their choosing. No cookies are durable by default. Making a cookie durable requires specific user interaction. + + + + All other cookies are specific to the tab where they are created and are destroyed when the tab is closed. + + + + + + DOM storage + + + DOM (Document Object Model) storage, also knows as web storage, + allows web pages to store information on a client device. + The storage capacity is larger than for cookies and the data is not automatically sent in the headers with every HTTP request. + In Privacy Browser, each website is allowed to store a 5 MB of data in DOM storage. + DOM storage requires JavaScript to function, and, in addition, requires an extra toggle to be enabled. + In Privacy Browser, DOM storage is limited to the tab where it is created and is destroyed when the tab is closed. + + + + + + IndexedDB + + + IndexedDB + provides web pages with a local database where they can store “significant amounts of structured data”. + There is disagreement on the internet about the maximum size of an IndexedDB database, probably because the various rendering engines keep changing their mind. + But it is usually listed at somewhere between 20% and 80% of your entire hard drive with each individual domain limited to some segment of that. + IndexedDB requires JavaScript to function. + In Privacy Browser, this database is limited to the tab where it is created and is destroyed when the tab is closed. + + + + + + Service Workers + + + Service workers are offline JavaScript proxies of a website. + They have their own cache that is usually hidden and hard to clear. + They were designed by people who want the web browser to become the operating system and run full “apps”. + In Privacy Browser, service workers are limited to the tab where they are created and are destroyed when the tab is closed. + + + + + + Filesystem API + + + The filesystem API grants the browser direct access to the files on your system. + Like service workers, the filesystem API is a summarily bad idea thought up by those who want the browser to become an operating system. + Even when JavaScript and local storage are enabled, the filesystem API does not work in Privacy Browser. + + + + + + + User Agent + + + The user agent is a text string that is sent as part of every HTTP header that identifies the browser to the web server. + Privacy Browser's default user agent is PrivacyBrowser/1.0. + Qt WebEngine 5.15.15’s default user agent is Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) QtWebEngine/5.15.15 Chrome/87.0.4280.144 Safari/537.36. + + + + Over the years user agents have become quite lengthy, + partially because they tend to include a brief history of the internet. + In the modern world they serve almost no good purpose, but some web developers still think they need them so they can send different version of their website to different browsers. + Some servers refuse to function correctly if they don't like the user agent that is sent. + + + + At some point in the future Privacy Browser will send no user agent by default. + Not only is that currently impossible because the Qt WebEngine doesn't allow you to not send a user agent (I will probably have to fork it to enable that functionality), + but even web servers that don't care what the user agent is often refuse to send an answer if there is no user agent at all. + Getting rid of this relic of the internet is going to take some time and a retraining of common expectations. + + + + + + Domain Settings + + + Domain setting make it easy to automatically change JavaScript, local storage, user agent, and other settings when the domain changes. + Domain settings for the current domain can be accessed through the domain settings button at the far right of the URL line edit. + Domain settings for all domains can be accessed through . When domain settings are active, the URL line edit will have a green background. + + + + + + HTTP Pings + + + HTTP hyperlinks can have an extra ping attribute that sends a POST request to a different URL. + These HTTP pings are commonly used for tracking. + Most browsers send HTTP pings without informing the user + in any way + (despite what The Register article says, current versions of FireFox also happily send HTTP pings). + + + + Privacy Browser blocks all HTTP pings and presents a dialog informing the user every time the current tab attempts to send one. + Hopefully, as more people become aware of what HTTP pings are, web sites will become more embarrassed about using them. + This is a classic example of how those who wrote the HTTP specs do not have the best interests of internet users at heart. + I would like the day to come when this is removed from both the internet and the spec. + Here is an example of me raising the issue with F-Droid. + + + + + + Spell Checking + + + Privacy Browser uses the binary .bdic Hunspell dictionary format + utilized by Qt WebEngine + which was created by Google for Chromium. + Debian’s Hunspell dictionary language packages are slowly adding support for the .bdic format. Those which have are listed by Privacy Browser as suggested packages. + + + + Once a .bdic dictionary is installed, it can be enabled in Privacy Browser’s settings. + + + + + + + Command Reference + + + + Main Window + + + + File + + + + + + + + &Ctrl;T + + + File + + New Tab + + + + + + Create a new tab. + + + + + + + + + + &Ctrl;N + + + File + + New Window + + + + + + Create a new window. + + + + + + + + + + &Ctrl;A + + + File + + Save Archive + + + + + + Save the webpage as an MHT (MIME encapsulation of aggregate HTML documents) archive. + + + + + + + + + + &Ctrl;P + + + File + + Print + + + + + + Print the document. + + + + + + + + + + &Ctrl;&Shift;P + + + File + + Print Preview + + + + + + Display the print preview dialog. + + + + + + + + + + &Ctrl;Q + + + File + + Quit + + + + + + Quit &privacybrowser;. + + + + + + + + + Edit + + + + + + + + &Ctrl;F + + + Edit + + Find + + + + + + Display the find toolbar and focus the find text line edit. If the toolbar is already displayed, the find text line edit is refocused and the text it contains is selected. + + + + + + + + + + + F3 + + + Edit + + Find Next + + + + + + Find the next entry on the page that matches the specified text. This action is only displayed if the find toolbar is visible. + + + + + + + + + + &Shift;F3 + + + Edit + + Find Previous + + + + + + Find the previous entry on the page that matches the specified text. This action is only displayed if the find toolbar is visible. + + + + + + + + + View + + + + + + + + &Ctrl;0 + + + View + + Zoom Default + + + + + + Return to either the app or the domain default zoom factor. + + + + + + + + + + &Ctrl;+ + + + View + + Zoom In + + + + + + Increment the zoom factor by 0.25. Valid factors range from 0.25 to 5.00. + + + + + + + + + + + &Ctrl;- + + + View + + Zoom Out + + + + + + Decrement the zoom factor by 0.25. Valid factors range from 0.25 to 5.00. + + + + + + + + + + F5 + + + View + + Refresh + + + + + + Reload the website in the current tab. When Refresh is visible, is hidden. + + + + + + + + + + &Ctrl;F5 + + + View + + Reload and Bypass Cache + + + + + + Reload the website in the current tab bypassing any information in the cache and loading everything from the webserver. + + + + + + + + + + &Ctrl;&Shift;X + + + View + + Stop + + + + + + Stop the loading of the website in the current tab. When Stop is visible, is hidden. + + + + + + + + + + &Ctrl;U + + + View + + View Source + + + + + + Toggle between viewing the source and viewing the rendered website. + + + + + + + + + + &Ctrl;&Shift;U + + + View + + View Source in New Tab> + + + + + + Open a new tab displaying the source of the current tab, or a new tab displaying the rendered version if the source is already displayed. + Note that right-clicking on the background of a rendered website will display a context menu with a "View page source" entry, which performs the first of these actions. + + + + + + + + + + F12 + + + View + + Developer Tools + + + + + + Display the developer tools, which are used to debug websites. + + + + + + + + + + &Ctrl;&Shift;F + + + View + + Full Screen Mode + + + + + + Toggle full screen mode. This performs the same action as . + + + + + + + + + Go + + + + + + + + &Alt;Left + + + Go + + Back + + + + + + Navigate back one step in the current tab’s history. The action is disabled if it is not possible to go back. + + + + + + + + + + &Alt;Right + + + Go + + Forward + + + + + + Navigate forward one step in the current tab’s history. The action is disabled if it is not possible to go forward. + + + + + + + + + + &Alt;Home + + + Go + + Home + + + + + + Load the home page in the current tab. + + + + + + + + + On-The-Fly Settings + + + + + + + + &Ctrl;J + + + On-The-Fly Settings + + JavaScript + + + + + + Toggle JavaScript in the current tab. + + + + + + + + + + &Ctrl;L + + + On-The-Fly Settings + + Local Storage + + + + + + Toggle local storage in the current tab. + + + + + + + + + + &Ctrl;D + + + On-The-Fly Settings + + DOM Storage + + + + + + Toggle DOM storage in the current tab. + + + + + + + + User Agent + + + + + + + + &Ctrl;&Alt;P + + + On-The-Fly Settings + + User Agent + + Privacy Browser + + + + + + Use the Privacy Browser user agent for the current tab. + + + + + + + + + + &Ctrl;&Alt;W + + + On-The-Fly Settings + + User Agent + + WebEngine Default + + + + + + Use the WebEngine Default user agent for the current tab. + + + + + + + + + + &Ctrl;&Alt;F + + + On-The-Fly Settings + + User Agent + + Firefox on Linux + + + + + + Use the Firefox on Linux user agent for the current tab. + + + + + + + + + + &Ctrl;&Alt;C + + + On-The-Fly Settings + + User Agent + + Chromium on Linux + + + + + + Use the Chromium on Linux user agent for the current tab. + + + + + + + + + + &Ctrl;&Alt;&Shift;F + + + On-The-Fly Settings + + User Agent + + Firefox on Windows + + + + + + Use the Firefox on Windows user agent for the current tab. + + + + + + + + + + &Ctrl;&Alt;&Shift;C + + + On-The-Fly Settings + + User Agent + + Chrome on Windows + + + + + + Use the Chrome on Windows user agent for the current tab. + + + + + + + + + + &Ctrl;&Alt;E + + + On-The-Fly Settings + + User Agent + + Edge on Windows + + + + + + Use the Edge on Windows user agent for the current tab. + + + + + + + + + + &Ctrl;&Alt;S + + + On-The-Fly Settings + + User Agent + + Safari on macOS + + + + + + Use the Safari on macOS user agent for the current tab. + + + + + + + + + + &Alt;&Shift;C + + + On-The-Fly Settings + + User Agent + + Custom + + + + + + Use the custom user agent for the current tab. This action is only active if a custom user agent is set in the settings. + + + + + + + + + Zoom + + + + + + + + &Ctrl;&Alt;Z + + + On-The-Fly Settings + + Zoom Factor + + + + + + Display the zoom factor dialog. + + + + + + + + + Search Engine + + + + + + + + &Ctrl;&Shift;M + + + On-The-Fly Settings + + Search Engine + + Mojeek + + + + + + Use Mojeek as the search engine. + + + + + + + + + + &Ctrl;&Shift;O + + + On-The-Fly Settings + + Search Engine + + Monocles + + + + + + Use Mojeek as the search engine. + + + + + + + + + + &Ctrl;&Shift;E + + + On-The-Fly Settings + + Search Engine + + MetaGer + + + + + + Use MetaGer as the search engine. + + + + + + + + + + &Ctrl;&Shift;G + + + On-The-Fly Settings + + Search Engine + + Google + + + + + + Use Google as the search engine. + + + + + + + + + + &Ctrl;&Shift;B + + + On-The-Fly Settings + + Search Engine + + Bing + + + + + + Use Bing as the search engine. + + + + + + + + + + &Ctrl;&Shift;Y + + + On-The-Fly Settings + + Search Engine + + Yahoo + + + + + + Use Yahoo as the search engine. + + + + + + + + + + &Ctrl;&Shift;C + + + On-The-Fly Settings + + Search Engine + + Custom + + + + + + Use the custom search engine. This action is only active if a custom search engine is set in the settings. + + + + + + + + + + Bookmarks + + + + + + + + &Ctrl;&Alt;&Shift;B + + + Bookmarks + + Edit Bookamrks + + + + + + Open the bookmark editing dialog. + + + + + + + + + + &Ctrl;&Alt;B + + + Bookmarks + + View Bookmarks Toolbar + + + + + + Toggle the visibility of the bookmarks toolbar. + + + + + + + + + + &Ctrl;B + + + Bookmarks + + Add Bookmark + + + + + + Add a new bookmark. + + + + + + + + + + &Meta;F + + + Bookmarks + + Add Folder + + + + + + Add a bookmark folder. The meta key on most keyboards is the Windows key. + + + + + + + + + Settings + + + + + + + + &Ctrl;&Shift;D + + + Settings + + Domain Settings + + + + + + + Display the domain settings dialog. + + + + + + + + + + &Ctrl;; + + + Settings + + Cookies + + + + + + Display the cookies dialog. + + + + + + + + + Common Settings and Help Entries + + + &privacybrowser; has many common &kde; Settings and Help menu items. + For more information read the sections about the Settings Menu + and Help Menu. + + + + + + Other Commands + + + + + + + &Ctrl;+S + + + + + + Toggle find case sensitive if the find toolbar is visible. + + + + + + + + + &Ctrl;+&Shift;+Click + + + + + + Open link in new tab. This performs the same action as right-clicking on the link and selecting “Open link in new tab”. + + + + + + + + + &Ctrl;+Click + + + + + + Open link in new background tab. This performs the same action as right-clicking on the link and selecting “Open link in new background tab”. + + + + + + + + + &Shift;+Click + + + + + + Open link in new window. This performs the same action as right-clicking on the link and selecting “Open link in new window”. + + + + + + + + + &Alt;+Click + + + + + + Save link. This performs the same action as right-clicking on the link and selecting “Save link”. + + + + + + + + + F11 + + + + + + Toggle full screen mode. This performs the same action as . + + + + + + + + + &Esc; + + + + + + Escape runs the first command that applies from the following list. + + + + + + Exit full screen browsing. + + + + + + Clear the find text line edit and the find text highlights. + + + + + + Hide the find text toolbar. + + + + + + + + + + + + Cookies Dialog + + + The cookies dialog is accessed through (see ). + + + + Commands + + + + + + + A + + + + + + Open the add cookie dialog. + + + + + + + + + E + + + + + + Open the edit cookie dialog. This command is only valid if a cookie is currently selected. + + + + + + + + + D or + + + + + + Delete the currently selected cookies. A dialog will confirm the deletion before it is processed. + + + + + + + + + L + + + + + + Delete all the cookies. A dialog will confirm the deletion before it is processed. + + + + + + + + + C or &Ctrl;Q + + + + + + Close the cookies dialog. + + + + + + + + + + Save Dialog + + + The save dialog pops up when a download is initiated. + + + + Commands + + + + + + + S + + + + + + Save the file. + + + + + + + + + C or &Ctrl;Q + + + + + + Close the save dialog without saving the file. + + + + + + + + + + + Changelog + + + + <ulink url="https://www.stoutner.com/privacy-browser-pc-0-5/">0.5</ulink> - + <ulink url="https://gitweb.stoutner.com/?p=PrivacyBrowserPC.git;a=commitdiff;h=a385ca128cb7bcc34ab5467edf21eb5e5664471a;ds=sidebyside">12 October 2023</ulink> + + + Add bookmarks. + Add zoom controls to the status bar and a + default zoom shortcut. + Add keyboard shortcuts for the URL toolbar actions. + Add an action to view page source. + Change the domain settings combo boxes to list + enabled above disabled. + File downloads can now show the size before the download begins. + Add PIE (Position Independent Executable) compiler flags. + Fix a bug that sometimes allowed multiple domain settings to be created. + Fix the download notification not being cleared on Xfce. + + + + + + <ulink url="https://www.stoutner.com/privacy-browser-pc-0-4/">0.4</ulink> - + <ulink url="https://gitweb.stoutner.com/?p=PrivacyBrowserPC.git;a=commitdiff;h=b4c8c8d02113d14c2a07751eb3b0c1bdeec7abb4">13 June 2023</ulink> + + + Add a setting to control spatial navigation. + Add an action to reload and bypass cache. + Fix a crash if one Privacy Browser window is closed while a tab within it is loading. + Add keyboard+click commands to the Handbook. + + + + + + <ulink url="https://www.stoutner.com/privacy-browser-pc-0-3/">0.3</ulink> - + <ulink url="https://gitweb.stoutner.com/?p=PrivacyBrowserPC.git;a=commitdiff;h=ace098e8677ac0d6468b825c73e65b82c0d6993e">8 May 2023</ulink> + + + Add the changelog to the Handbook. + Add the missing current domain settings icon on Gnome and Xfce. + Make changes to build on Guix. + + + + + + <ulink url="https://www.stoutner.com/privacy-browser-pc-0-2/">0.2</ulink> - + <ulink url="https://gitweb.stoutner.com/?p=PrivacyBrowserPC.git;a=commitdiff;h=44b5d3a1f6a5e7fc2aa8530845f26eba7bc26f9a">17 April 2023</ulink> + + + Fix a crash on GNOME when downloading a file with local storage disabled. + Fix problems with missing + icons on GNOME. + Display an animated favorite icon while a webpage is loading. + Fix the Handbook on non-KDE systems. + Change the order of entries in the WebEngine context menu. + Make spellcheck languages easier to click on. + Only generate a HTTP ping dialog if the request is made by the current tab. + Add a section to the Handbook about HTTP pings. + + + + + + <ulink url="https://www.stoutner.com/privacy-browser-pc-0-1/">0.1</ulink> - + <ulink url="https://gitweb.stoutner.com/?p=PrivacyBrowserPC.git;a=commitdiff;h=73459c6685ec5e58d776f2594c9b587802363b1c">11 March 2023</ulink> + + + Initial release. + + + + + + + Questions and Answers + + + + + + + Why are there no questions? + + + + + + Because Privacy Browser is so perfectly designed that none have ever been asked. Also, because this is an alpha release and nobody has had a chance to ask them yet. ;) + + + + + + + + + Credits and License + + + Privacy Browser PC copyright 2016-2017,2021-2024 Soren Stoutner soren@stoutner.com. + + + + + Program + + + The source code is available at gitweb.stoutner.com + or by running git clone https://git.stoutner.com/PrivacyBrowserPC.git. + + + + Translators: + + + + Translations will be added in a future release. + + + + + + + &underGPL; + + + + + Documentation + + + doc/index.docbook and src/com.stoutner.privacybrowser.appdata.xml are released under the + GFDL-1.3 license with no Front-Cover or Back-Cover Texts or Invariant Sections. + All other documentation is released under the GPLv3+ license. + + + + &underFDL; + + + + + Icons + + + + + + + + + + Privacy Browser + + + + + + + + + + + + JavaScript + + + + + + + + + + + + Privacy Browser Monochrome + + + + + are derived from security and language, which are part of the Android Material icon set + and are released under the Apache License 2.0. Modifications copyright 2016-2017,2021-2023 Soren Stoutner. + The resulting images are released under the GPLv3+ license. + + + + + + + + + + + Loading + + + + + comes from FlutterLoadingGIFs + where it is named cupertino_activity_indicator_selective.gif. + It is released under the Zero-Clause BSD License. + + + - - diff --git a/doc/javascript.png b/doc/javascript.png new file mode 100644 index 0000000..b06507a Binary files /dev/null and b/doc/javascript.png differ diff --git a/doc/loading.gif b/doc/loading.gif new file mode 100644 index 0000000..b2dd625 Binary files /dev/null and b/doc/loading.gif differ diff --git a/doc/privacybrowser-monochrome.png b/doc/privacybrowser-monochrome.png new file mode 100644 index 0000000..6042a59 Binary files /dev/null and b/doc/privacybrowser-monochrome.png differ diff --git a/doc/privacybrowser-window.png b/doc/privacybrowser-window.png new file mode 100644 index 0000000..e753474 Binary files /dev/null and b/doc/privacybrowser-window.png differ diff --git a/doc/privacybrowser.png b/doc/privacybrowser.png new file mode 100644 index 0000000..100693c Binary files /dev/null and b/doc/privacybrowser.png differ diff --git a/po/privacybrowser.pot b/po/privacybrowser.pot index 4cf4476..4fbbcbc 100644 --- a/po/privacybrowser.pot +++ b/po/privacybrowser.pot @@ -1,4 +1,4 @@ -# Copyright © 2022 Soren Stoutner . +# Copyright 2022-2023 Soren Stoutner . # # This file is part of Privacy Browser PC . # @@ -15,79 +15,1497 @@ # You should have received a copy of the GNU General Public License # along with Privacy Browser PC. If not, see . - #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-01-29 11:34-0700\n" +"POT-Creation-Date: 2023-05-08 14:31-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" +"Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" -#: src/mainwindow.cpp:74 +#: src/widgets/TabWidget.cpp:174 #, kde-format -msgctxt "@title:tab" -msgid "General" +msgctxt "New tab label." +msgid "New Tab" +msgstr "" + +#: src/widgets/TabWidget.cpp:325 +#, kde-format +msgctxt "HTTP Ping blocked dialog title" +msgid "HTTP Ping Blocked" +msgstr "" + +#: src/widgets/TabWidget.cpp:328 +#, kde-format +msgctxt "HTTP Ping blocked dialog text" +msgid "" +"This request has been blocked because it sends a naughty HTTP ping to %1." +msgstr "" + +#: src/widgets/TabWidget.cpp:838 src/widgets/TabWidget.cpp:1086 +#, kde-format +msgctxt "Save file dialog caption" +msgid "Save File" +msgstr "" + +#: src/widgets/TabWidget.cpp:858 src/widgets/TabWidget.cpp:888 +#, kde-format +msgctxt "Download notification title" +msgid "Download" +msgstr "" + +#: src/widgets/TabWidget.cpp:861 +#, kde-format +msgctxt "Downloading notification text" +msgid "Downloading %1" +msgstr "" + +#: src/widgets/TabWidget.cpp:867 +#, kde-format +msgctxt "Download notification action" +msgid "Cancel" +msgstr "" + +#: src/widgets/TabWidget.cpp:891 +#, kde-format +msgctxt "Download canceled notification" +msgid "%1 download canceled" +msgstr "" + +#: src/widgets/TabWidget.cpp:910 +#, kde-format +msgctxt "Download progress notification text" +msgid "%1% of %2 downloaded (%3 of %4 bytes)" +msgstr "" + +#: src/widgets/TabWidget.cpp:916 +#, kde-format +msgctxt "Download progress notification text" +msgid "%1: %2 bytes downloaded" +msgstr "" + +#: src/widgets/TabWidget.cpp:927 +#, kde-format +msgctxt "Download finished notification text" +msgid "%1 download finished" +msgstr "" + +#: src/dialogs/SaveDialog.cpp:36 +#, kde-format +msgctxt "The save dialog window title" +msgid "Save" +msgstr "" + +#: src/dialogs/SaveDialog.cpp:84 +#, kde-format +msgctxt "Unknown download file size. The bold style should be preserved." +msgid "unknown" +msgstr "" + +#: src/dialogs/SaveDialog.cpp:86 +#, kde-format +msgctxt "Download file size. The bold style should be preserved." +msgid "%1 bytes" +msgstr "" + +#: src/dialogs/SaveDialog.cpp:89 +#, kde-format +msgctxt "The save key shortcut." +msgid "s" +msgstr "" + +#: src/dialogs/SaveDialog.cpp:90 src/dialogs/CookiesDialog.cpp:281 +#, kde-format +msgctxt "The close key shortcut." +msgid "c" +msgstr "" + +#: src/dialogs/DurableCookiesDialog.cpp:32 +#, kde-format +msgctxt "The durable cookies dialog window title" +msgid "Durable Cookies" +msgstr "" + +#: src/dialogs/DomainSettingsDialog.cpp:40 +#, kde-format +msgctxt "The domain settings dialog window title" +msgid "Domain Settings" +msgstr "" + +#: src/dialogs/DomainSettingsDialog.cpp:382 +#, kde-format +msgctxt "Domain settings DOM storage label." +msgid "DOM storage enabled" +msgstr "" + +#: src/dialogs/DomainSettingsDialog.cpp:384 +#, kde-format +msgctxt "Domain settings DOM storage label." +msgid "DOM storage disabled" +msgstr "" + +#: src/dialogs/DomainSettingsDialog.cpp:395 +#, kde-format +msgctxt "Domain settings DOM storage label. The tags should be retained." +msgid "DOM storage disabled" +msgstr "" + +#: src/dialogs/DomainSettingsDialog.cpp:406 +#, kde-format +msgctxt "Domain settings DOM storage label. The tags should be retained." +msgid "DOM storage enabled" +msgstr "" + +#: src/dialogs/DomainSettingsDialog.cpp:425 +#, kde-format +msgctxt "Domain settings JavaScript label." +msgid "JavaScript enabled" +msgstr "" + +#: src/dialogs/DomainSettingsDialog.cpp:427 +#, kde-format +msgctxt "Domain settings JavaScript label." +msgid "JavaScript disabled" +msgstr "" + +#: src/dialogs/DomainSettingsDialog.cpp:438 +#, kde-format +msgctxt "Domain settings JavaScript label. The tags should be retained." +msgid "JavaScript disabled" +msgstr "" + +#: src/dialogs/DomainSettingsDialog.cpp:449 +#, kde-format +msgctxt "Domain settings JavaScript label. The tags should be retained." +msgid "JavaScript enabled" +msgstr "" + +#: src/dialogs/DomainSettingsDialog.cpp:468 +#, kde-format +msgctxt "Domain settings local storage label." +msgid "Local storage enabled" +msgstr "" + +#: src/dialogs/DomainSettingsDialog.cpp:470 +#, kde-format +msgctxt "Domain settings local storage label." +msgid "Local storage disabled" +msgstr "" + +#: src/dialogs/DomainSettingsDialog.cpp:481 +#, kde-format +msgctxt "" +"Domain settings local storage label. The tags should be retained." +msgid "Local storage disabled" +msgstr "" + +#: src/dialogs/DomainSettingsDialog.cpp:492 +#, kde-format +msgctxt "" +"Domain settings local storage label. The tabs should be retained." +msgid "Local storage enabled" +msgstr "" + +#: src/dialogs/DomainSettingsDialog.cpp:542 +#, kde-format +msgctxt "Add domain dialog title" +msgid "Add Domain" +msgstr "" + +#: src/dialogs/DomainSettingsDialog.cpp:544 +#, kde-format +msgctxt "" +"Add domain message. The \n" +"\n" +" are newline codes that should be retained" +msgid "" +"Add a new domain. Doing so will also save any pending changes that have " +"been made to other domains.\n" +"\n" +"*. may be prepended to a domain to include all subdomains (eg. *.stoutner." +"com)." +msgstr "" + +#: src/dialogs/DomainSettingsDialog.cpp:561 +#, kde-format +msgctxt "Delete domain dialog title" +msgid "Delete Domain" +msgstr "" + +#: src/dialogs/DomainSettingsDialog.cpp:564 +#, kde-format +msgctxt "Delete domain dialog main message" +msgid "Delete the current domain?" +msgstr "" + +#: src/dialogs/DomainSettingsDialog.cpp:567 +#, kde-format +msgctxt "Delete domain dialog secondary message" +msgid "" +"Doing so will also save any pending changes that have been made to other " +"domains." +msgstr "" + +#: src/dialogs/AddOrEditCookieDialog.cpp:41 +#, kde-format +msgctxt "The add cookie dialog window title." +msgid "Add Cookie" +msgstr "" + +#: src/dialogs/AddOrEditCookieDialog.cpp:43 +#, kde-format +msgctxt "The edit cookie dialog window title." +msgid "Edit Cookie" +msgstr "" + +#: src/dialogs/CookiesDialog.cpp:124 +#, kde-format +msgctxt "The cookies dialog window title" +msgid "Cookies" +msgstr "" + +#: src/dialogs/CookiesDialog.cpp:145 +#, kde-format +msgctxt "The cookie Name header." +msgid "Name" +msgstr "" + +#: src/dialogs/CookiesDialog.cpp:146 +#, kde-format +msgctxt "The cookie Durable header." +msgid "Durable" +msgstr "" + +#: src/dialogs/CookiesDialog.cpp:147 +#, kde-format +msgctxt "The cookie Path header." +msgid "Path" +msgstr "" + +#: src/dialogs/CookiesDialog.cpp:148 +#, kde-format +msgctxt "The cookie Expiration Date header." +msgid "Expiration Date" msgstr "" -#: src/main.cpp:46 +#: src/dialogs/CookiesDialog.cpp:149 #, kde-format +msgctxt "The cookie HTTP Only header." +msgid "HTTP Only" +msgstr "" + +#: src/dialogs/CookiesDialog.cpp:150 +#, kde-format +msgctxt "The cookie Secure header." +msgid "Secure" +msgstr "" + +#: src/dialogs/CookiesDialog.cpp:151 +#, kde-format +msgctxt "The cookie Value header." +msgid "Value" +msgstr "" + +#: src/dialogs/CookiesDialog.cpp:155 +#, kde-format +msgctxt "The cookie Name tool tip." +msgid "" +"The name identifies the cookie. Each cookie has a unique combination of " +"domain, name, and path." +msgstr "" + +#: src/dialogs/CookiesDialog.cpp:157 +#, kde-format +msgctxt "The cookie Durable tool tip" +msgid "" +"Durable cookies persist across restarts, irrespective of the expiration " +"date. All other cookies are deleted when Privacy Browser closes, " +"irrespective of the expiration date." +msgstr "" + +#: src/dialogs/CookiesDialog.cpp:158 +#, kde-format +msgctxt "The cookie Path tool tip." +msgid "Websites can restrict cookie access to subpath of their URL." +msgstr "" + +#: src/dialogs/CookiesDialog.cpp:160 +#, kde-format +msgctxt "The cookie Expiration Date tool tip." +msgid "" +"Cookies without an expiration date are known as session cookies and are " +"expected to be deleted every time the browser closes." +msgstr "" + +#: src/dialogs/CookiesDialog.cpp:162 +#, kde-format +msgctxt "The cookie HTTP Only tool tip." +msgid "" +"Restrict cookie access to HTTP (and HTTPS). This prevents JavaScript from " +"accessing the cookie, which hardens it against cross-site scripting attacks." +msgstr "" + +#: src/dialogs/CookiesDialog.cpp:163 +#, kde-format +msgctxt "The cookie Secure tool tip." +msgid "" +"Only allow the cookie to be transferred across HTTPS (as opposed to HTTP)." +msgstr "" + +#: src/dialogs/CookiesDialog.cpp:164 +#, kde-format +msgctxt "The cookie Value tool tip." +msgid "The value contains the cookie data." +msgstr "" + +#: src/dialogs/CookiesDialog.cpp:205 src/dialogs/CookiesDialog.cpp:208 +#: src/dialogs/CookiesDialog.cpp:209 src/dialogs/CookiesDialog.cpp:409 +#: src/dialogs/CookiesDialog.cpp:412 src/dialogs/CookiesDialog.cpp:413 +#: src/dialogs/CookiesDialog.cpp:786 +#, kde-format +msgid "yes" +msgstr "" + +#: src/dialogs/CookiesDialog.cpp:205 src/dialogs/CookiesDialog.cpp:208 +#: src/dialogs/CookiesDialog.cpp:209 src/dialogs/CookiesDialog.cpp:409 +#: src/dialogs/CookiesDialog.cpp:412 src/dialogs/CookiesDialog.cpp:413 +#, kde-format +msgid "no" +msgstr "" + +#: src/dialogs/CookiesDialog.cpp:258 src/dialogs/CookiesDialog.cpp:827 +#, kde-format +msgctxt "View the durable cookies button" +msgid "Durable cookies - %1" +msgstr "" + +#: src/dialogs/CookiesDialog.cpp:276 +#, kde-format +msgctxt "The add cookie key shortcut." +msgid "a" +msgstr "" + +#: src/dialogs/CookiesDialog.cpp:277 +#, kde-format +msgctxt "The edit cookie key shortcut." +msgid "e" +msgstr "" + +#: src/dialogs/CookiesDialog.cpp:278 +#, kde-format +msgctxt "The delete cookie key shortcut." +msgid "d" +msgstr "" + +#: src/dialogs/CookiesDialog.cpp:280 +#, kde-format +msgctxt "The delete all key shortcut." +msgid "l" +msgstr "" + +#: src/dialogs/CookiesDialog.cpp:618 +#, kde-format +msgctxt "Delete all cookies dialog title" +msgid "Delete All Cookies" +msgstr "" + +#: src/dialogs/CookiesDialog.cpp:621 +#, kde-format +msgctxt "Delete all cookies dialog text" +msgid "Delete all cookies?" +msgstr "" + +#: src/dialogs/CookiesDialog.cpp:624 src/dialogs/CookiesDialog.cpp:686 +#, kde-format +msgctxt "Delete durable cookies check box" +msgid "Delete even if durable" +msgstr "" + +#: src/dialogs/CookiesDialog.cpp:694 +#, kde-format +msgctxt "Delete cookies dialog title" +msgid "Delete %1 Cookie" +msgid_plural "Delete 1% Cookies" +msgstr[0] "" +msgstr[1] "" + +#: src/dialogs/CookiesDialog.cpp:697 +#, kde-format +msgctxt "Delete cookies dialog text" +msgid "Delete %1 cookie?" +msgid_plural "Delete %1 cookies?" +msgstr[0] "" +msgstr[1] "" + +#: src/dialogs/CookiesDialog.cpp:702 +#, kde-format +msgctxt "Delete cookie dialog title" +msgid "Delete 1 Cookie" +msgstr "" + +#: src/dialogs/CookiesDialog.cpp:705 +#, kde-format +msgctxt "Delete cookie dialog text" +msgid "Delete 1 cookie?" +msgstr "" + +#: src/dialogs/CookiesDialog.cpp:812 +#, kde-format +msgctxt "Delete cookies button." +msgid "&Delete %1 cookie" +msgid_plural "&Delete %1 cookies" +msgstr[0] "" +msgstr[1] "" + +#: src/dialogs/CookiesDialog.cpp:817 +#, kde-format +msgctxt "Delete cookies button." +msgid "&Delete 1 cookie" +msgstr "" + +#: src/dialogs/CookiesDialog.cpp:823 +#, kde-format +msgctxt "Delete cookie button." +msgid "&Delete cookie" +msgstr "" + +#: src/main.cpp:48 +#, kde-format +msgctxt "Program Name" msgid "Privacy Browser" msgstr "" -#: src/main.cpp:49 +#: src/main.cpp:51 #, kde-format +msgctxt "Developer Information" msgid "Soren Stoutner" msgstr "" -#: src/main.cpp:49 +#: src/main.cpp:51 #, kde-format +msgctxt "Developer Information" msgid "Principal developer" msgstr "" -#: src/main.cpp:53 +#: src/main.cpp:56 #, kde-format -msgid "Copyright © 2016-2017,2021-2022 Soren Stoutner " +msgctxt "Copyright" +msgid "Copyright 2016-2017,2021-2023 Soren Stoutner " msgstr "" -#: src/main.cpp:58 +#: src/main.cpp:62 #, kde-format +msgctxt "Tagline" msgid "A web browser that respects your privacy." msgstr "" -#: rc.cpp:3 +#: src/helpers/UserAgentHelper.cpp:51 #, kde-format -msgid "Zoom factor" +msgctxt "User agents" +msgid "System default" msgstr "" -#: rc.cpp:6 +#: src/helpers/UserAgentHelper.cpp:52 #, kde-format -msgid "Set the zoom factor between 0.25 and 5.00." +msgctxt "User agents" +msgid "Privacy Browser" msgstr "" -#: rc.cpp:9 +#: src/helpers/UserAgentHelper.cpp:53 #, kde-format -msgid "Set the zoom factor between 0.25 and 5.00. The default is 1.00." +msgctxt "User agents" +msgid "WebEngine default" msgstr "" -#: rc.cpp:12 +#: src/helpers/UserAgentHelper.cpp:54 +#, kde-format +msgctxt "User agents" +msgid "Firefox on Linux" +msgstr "" + +#: src/helpers/UserAgentHelper.cpp:55 +#, kde-format +msgctxt "User agents" +msgid "Chromium on Linux" +msgstr "" + +#: src/helpers/UserAgentHelper.cpp:56 +#, kde-format +msgctxt "User agents" +msgid "Firefox on Windows" +msgstr "" + +#: src/helpers/UserAgentHelper.cpp:57 +#, kde-format +msgctxt "User agents" +msgid "Chrome on Windows" +msgstr "" + +#: src/helpers/UserAgentHelper.cpp:58 +#, kde-format +msgctxt "User agents" +msgid "Edge on Windows" +msgstr "" + +#: src/helpers/UserAgentHelper.cpp:59 +#, kde-format +msgctxt "User agents" +msgid "Safari on macOS" +msgstr "" + +#: src/windows/BrowserWindow.cpp:151 +#, kde-format +msgctxt "New tab action" +msgid "New Tab" +msgstr "" + +#: src/windows/BrowserWindow.cpp:152 +#, kde-format +msgctxt "New window action" +msgid "New Window" +msgstr "" + +#: src/windows/BrowserWindow.cpp:161 +#, kde-format +msgctxt "Search engine" +msgid "Mojeek" +msgstr "" + +#: src/windows/BrowserWindow.cpp:162 +#, kde-format +msgctxt "Search engine" +msgid "Monocles" +msgstr "" + +#: src/windows/BrowserWindow.cpp:163 +#, kde-format +msgctxt "Search engine" +msgid "MetaGer" +msgstr "" + +#: src/windows/BrowserWindow.cpp:164 +#, kde-format +msgctxt "Search engine" +msgid "Google" +msgstr "" + +#: src/windows/BrowserWindow.cpp:165 +#, kde-format +msgctxt "Search engine" +msgid "Bing" +msgstr "" + +#: src/windows/BrowserWindow.cpp:166 +#, kde-format +msgctxt "Search engine" +msgid "Yahoo" +msgstr "" + +#: src/windows/BrowserWindow.cpp:167 +#, kde-format +msgctxt "Domain Settings action" +msgid "Domain Settings" +msgstr "" + +#: src/windows/BrowserWindow.cpp:168 src/windows/BrowserWindow.cpp:983 +#, kde-format +msgctxt "The Cookies action, which also displays the number of cookies" +msgid "Cookies - %1" +msgstr "" + +#: src/windows/BrowserWindow.cpp:169 +#, kde-format +msgctxt "JavaScript action" +msgid "JavaScript" +msgstr "" + +#: src/windows/BrowserWindow.cpp:170 +#, kde-format +msgctxt "The Local Storage action" +msgid "Local Storage" +msgstr "" + +#: src/windows/BrowserWindow.cpp:171 +#, kde-format +msgctxt "DOM Storage action" +msgid "DOM Storage" +msgstr "" + +#: src/windows/BrowserWindow.cpp:172 +#, kde-format +msgctxt "Find Case Sensitive action" +msgid "Find Case Sensitive" +msgstr "" + +#: src/windows/BrowserWindow.cpp:173 +#, kde-format +msgctxt "Hide Find Text action" +msgid "Hide Find Text" +msgstr "" + +#: src/windows/BrowserWindow.cpp:208 +#, kde-format +msgctxt "The open new tab key sequence." +msgid "Ctrl+T" +msgstr "" + +#: src/windows/BrowserWindow.cpp:209 +#, kde-format +msgctxt "The open new window key sequence." +msgid "Ctrl+N" +msgstr "" + +#: src/windows/BrowserWindow.cpp:210 +#, kde-format +msgctxt "The print preview key sequence." +msgid "Ctrl+Shift+P" +msgstr "" + +#: src/windows/BrowserWindow.cpp:211 +#, kde-format +msgctxt "The Privacy Browser user agent key sequence." +msgid "Ctrl+Alt+P" +msgstr "" + +#: src/windows/BrowserWindow.cpp:212 +#, kde-format +msgctxt "The WebEngine Default user agent key sequence." +msgid "Ctrl+Alt+W" +msgstr "" + +#: src/windows/BrowserWindow.cpp:213 +#, kde-format +msgctxt "The Firefox on Linux user agent key sequence." +msgid "Ctrl+Alt+F" +msgstr "" + +#: src/windows/BrowserWindow.cpp:214 +#, kde-format +msgctxt "The Chromium on Linux user agent key sequence." +msgid "Ctrl+Alt+C" +msgstr "" + +#: src/windows/BrowserWindow.cpp:215 +#, kde-format +msgctxt "The Firefox on Windows user agent key sequence." +msgid "Ctrl+Alt+Shift+F" +msgstr "" + +#: src/windows/BrowserWindow.cpp:216 +#, kde-format +msgctxt "The Chrome on Windows user agent key sequence." +msgid "Ctrl+Alt+Shift+C" +msgstr "" + +#: src/windows/BrowserWindow.cpp:217 +#, kde-format +msgctxt "The Edge on Windows user agent key sequence." +msgid "Ctrl+Alt+E" +msgstr "" + +#: src/windows/BrowserWindow.cpp:218 +#, kde-format +msgctxt "The Safari on macOS user agent key sequence." +msgid "Ctrl+Alt+S" +msgstr "" + +#: src/windows/BrowserWindow.cpp:219 +#, kde-format +msgctxt "The custom user agent key sequence." +msgid "Alt+Shift+C" +msgstr "" + +#: src/windows/BrowserWindow.cpp:220 +#, kde-format +msgctxt "The zoom factor key sequence." +msgid "Ctrl+Alt+Z" +msgstr "" + +#: src/windows/BrowserWindow.cpp:221 +#, kde-format +msgctxt "The Mojeek search engine key sequence." +msgid "Ctrl+Shift+M" +msgstr "" + +#: src/windows/BrowserWindow.cpp:222 +#, kde-format +msgctxt "The Monocles search engine key sequence." +msgid "Ctrl+Shift+O" +msgstr "" + +#: src/windows/BrowserWindow.cpp:223 +#, kde-format +msgctxt "The MetaGer search engine key sequence." +msgid "Ctrl+Shift+E" +msgstr "" + +#: src/windows/BrowserWindow.cpp:224 +#, kde-format +msgctxt "The Google search engine key sequence." +msgid "Ctrl+Shift+G" +msgstr "" + +#: src/windows/BrowserWindow.cpp:225 +#, kde-format +msgctxt "The Bing search engine key sequence." +msgid "Ctrl+Shift+B" +msgstr "" + +#: src/windows/BrowserWindow.cpp:226 +#, kde-format +msgctxt "The Yahoo search engine key sequence." +msgid "Ctrl+Shift+Y" +msgstr "" + +#: src/windows/BrowserWindow.cpp:227 +#, kde-format +msgctxt "The custom search engine key sequence." +msgid "Ctrl+Shift+C" +msgstr "" + +#: src/windows/BrowserWindow.cpp:228 +#, kde-format +msgctxt "The domain settings key sequence." +msgid "Ctrl+D" +msgstr "" + +#: src/windows/BrowserWindow.cpp:229 +#, kde-format +msgctxt "The cookies dialog key sequence." +msgid "Ctrl+;" +msgstr "" + +#: src/windows/BrowserWindow.cpp:335 +#, kde-format +msgctxt "The URL line edit placeholder text" +msgid "URL or Search Terms" +msgstr "" + +#: src/windows/BrowserWindow.cpp:336 +#, kde-format +msgctxt "The find line edit placeholder text" +msgid "Find Text" +msgstr "" + +#: src/windows/BrowserWindow.cpp:352 +#, kde-format +msgctxt "Default find results." +msgid "0/0" +msgstr "" + +#: src/windows/BrowserWindow.cpp:418 +#, kde-format +msgctxt "The toggle full screen shortcut." +msgid "F11" +msgstr "" + +#: src/windows/BrowserWindow.cpp:462 +#, kde-format +msgctxt "The domain settings dialog title" +msgid "Domain Settings" +msgstr "" + +#: src/windows/BrowserWindow.cpp:576 +#, kde-format +msgctxt "The on-the-fly zoom factor dialog title" +msgid "On-The-Fly Zoom Factor" +msgstr "" + +#: src/windows/BrowserWindow.cpp:577 +#, kde-format +msgctxt "The instruction text of the on-the-fly zoom factor dialog" +msgid "Enter a zoom factor between 0.25 and 5.00" +msgstr "" + +#: src/windows/BrowserWindow.cpp:665 +#, kde-format +msgctxt "Select download location dialog caption" +msgid "Select Download Location" +msgstr "" + +#: src/windows/BrowserWindow.cpp:814 +#, kde-format +msgctxt "Settings tab title" +msgid "Privacy" +msgstr "" + +#: src/windows/BrowserWindow.cpp:815 +#, kde-format +msgctxt "Settings tab title" +msgid "General" +msgstr "" + +#: src/windows/BrowserWindow.cpp:816 +#, kde-format +msgctxt "Settings tab title" +msgid "Spell Check" +msgstr "" + +#: src/windows/BrowserWindow.cpp:1074 +#, kde-format +msgctxt "The main search engine menu action" +msgid "Search Engine - Mojeek" +msgstr "" + +#: src/windows/BrowserWindow.cpp:1085 +#, kde-format +msgctxt "The main search engine menu action" +msgid "Search Engine - Monocles" +msgstr "" + +#: src/windows/BrowserWindow.cpp:1096 +#, kde-format +msgctxt "The main search engine menu action" +msgid "Search Engine - MetaGer" +msgstr "" + +#: src/windows/BrowserWindow.cpp:1107 +#, kde-format +msgctxt "The main search engine menu action" +msgid "Search Engine - Google" +msgstr "" + +#: src/windows/BrowserWindow.cpp:1118 +#, kde-format +msgctxt "The main search engine menu action" +msgid "Search Engine - Bing" +msgstr "" + +#: src/windows/BrowserWindow.cpp:1129 +#, kde-format +msgctxt "The main search engine menu action" +msgid "Search Engine - Yahoo" +msgstr "" + +#: src/windows/BrowserWindow.cpp:1140 +#, kde-format +msgctxt "The main search engine menu action" +msgid "Search Engine - Custom" +msgstr "" + +#: src/windows/BrowserWindow.cpp:1165 src/windows/BrowserWindow.cpp:1299 +#, kde-format +msgctxt "@action" +msgid "Custom" +msgstr "" + +#: src/windows/BrowserWindow.cpp:1184 +#, kde-format +msgctxt "The main user agent menu action" +msgid "User Agent - Privacy Browser" +msgstr "" + +#: src/windows/BrowserWindow.cpp:1195 +#, kde-format +msgctxt "The main user agent menu action" +msgid "User Agent - WebEngine default" +msgstr "" + +#: src/windows/BrowserWindow.cpp:1206 +#, kde-format +msgctxt "The main user agent menu action" +msgid "User Agent - Firefox on Linux" +msgstr "" + +#: src/windows/BrowserWindow.cpp:1217 +#, kde-format +msgctxt "The main user agent menu action" +msgid "User Agent - Chromium on Linux" +msgstr "" + +#: src/windows/BrowserWindow.cpp:1228 +#, kde-format +msgctxt "The main user agent menu action" +msgid "User Agent - Firefox on Windows" +msgstr "" + +#: src/windows/BrowserWindow.cpp:1239 +#, kde-format +msgctxt "The main user agent menu action" +msgid "User Agent - Chrome on Windows" +msgstr "" + +#: src/windows/BrowserWindow.cpp:1250 +#, kde-format +msgctxt "The main user agent menu action" +msgid "User Agent - Edge on Windows" +msgstr "" + +#: src/windows/BrowserWindow.cpp:1261 +#, kde-format +msgctxt "The main user agent menu action" +msgid "User Agent - Safari on macOS" +msgstr "" + +#: src/windows/BrowserWindow.cpp:1272 +#, kde-format +msgctxt "The main user agent menu action" +msgid "User Agent - Custom" +msgstr "" + +#: src/windows/BrowserWindow.cpp:1309 +#, kde-format +msgctxt "@action" +msgid "Zoom Factor - %1" +msgstr "" + +#: rc.cpp:3 +#, kde-format +msgid "On-The-Fly Settings" +msgstr "" + +#: rc.cpp:6 +#, kde-format +msgid "Navigation Toolbar" +msgstr "" + +#: rc.cpp:9 +#, kde-format +msgid "URL Toolbar" +msgstr "" + +#: rc.cpp:12 +#, kde-format +msgid "Add cookie" +msgstr "" + +#: rc.cpp:15 +#, kde-format +msgid "Edit cookie" +msgstr "" + +#: rc.cpp:18 +#, kde-format +msgid "Delete cookie" +msgstr "" + +#: rc.cpp:21 +#, kde-format +msgid "Delete all" +msgstr "" + +#: rc.cpp:24 +#, kde-format +msgid "Add domain" +msgstr "" + +#: rc.cpp:27 +#, kde-format +msgid "Delete domain" +msgstr "" + +#: rc.cpp:30 +#, kde-format +msgid "Domain name" +msgstr "" + +#: rc.cpp:33 +#, kde-format +msgid "" +"*. may be prepended to a domain to include all subdomains (eg. *.stoutner." +"com)." +msgstr "" + +#: rc.cpp:36 rc.cpp:126 +#, kde-format +msgid "JavaScript" +msgstr "" + +#: rc.cpp:39 +#, kde-format +msgid "JavaScript allows websites to run programs (scripts) on the device." +msgstr "" + +#: rc.cpp:42 rc.cpp:57 rc.cpp:72 rc.cpp:87 rc.cpp:120 +#, kde-format +msgid "System default" +msgstr "" + +#: rc.cpp:45 +#, kde-format +msgid "JavaScript disabled" +msgstr "" + +#: rc.cpp:48 +#, kde-format +msgid "JavaScript enabled" +msgstr "" + +#: rc.cpp:51 rc.cpp:132 +#, kde-format +msgid "Local storage" +msgstr "" + +#: rc.cpp:54 rc.cpp:135 +#, kde-format +msgid "" +"Local storage includes cookies, DOM storage, IndexedDB, service workers, and " +"the filesystem API. DOM storage also requires a separate control to be " +"enabled. Local storage is disabled by default." +msgstr "" + +#: rc.cpp:60 +#, kde-format +msgid "Local storage disabled" +msgstr "" + +#: rc.cpp:63 +#, kde-format +msgid "Local storage enabled" +msgstr "" + +#: rc.cpp:66 rc.cpp:138 +#, kde-format +msgid "DOM storage" +msgstr "" + +#: rc.cpp:69 +#, kde-format +msgid "" +"DOM storage, sometimes called web storage, is like cookies on steroids. To " +"function, it requires that both JavaScript and local storage be enabled." +msgstr "" + +#: rc.cpp:75 +#, kde-format +msgid "DOM storage disabled" +msgstr "" + +#: rc.cpp:78 +#, kde-format +msgid "DOM storage enabled" +msgstr "" + +#: rc.cpp:81 rc.cpp:144 +#, kde-format +msgid "User agent" +msgstr "" + +#: rc.cpp:84 +#, kde-format +msgid "" +"The user agent identifies the browser to the web server. It serves no " +"useful purpose, but many web servers refuse to return the web page if they " +"don't see a user agent they like." +msgstr "" + +#: rc.cpp:90 rc.cpp:150 +#, kde-format +msgid "Privacy Browser" +msgstr "" + +#: rc.cpp:93 +#, kde-format +msgid "WebEngine default" +msgstr "" + +#: rc.cpp:96 +#, kde-format +msgid "Firefox on Linux" +msgstr "" + +#: rc.cpp:99 +#, kde-format +msgid "Chromium on Linux" +msgstr "" + +#: rc.cpp:102 +#, kde-format +msgid "Firefox on Windows" +msgstr "" + +#: rc.cpp:105 +#, kde-format +msgid "Chrome on Windows" +msgstr "" + +#: rc.cpp:108 +#, kde-format +msgid "Edge on Windows" +msgstr "" + +#: rc.cpp:111 +#, kde-format +msgid "Safari on macOS" +msgstr "" + +#: rc.cpp:114 rc.cpp:207 +#, kde-format +msgid "Zoom factor" +msgstr "" + +#: rc.cpp:117 +#, kde-format +msgid "Valid values for the zoom factor are between 0.25 and 5.00." +msgstr "" + +#: rc.cpp:123 +#, kde-format +msgid "Custom" +msgstr "" + +#: rc.cpp:129 +#, kde-format +msgid "" +"JavaScript allows websites to run programs (scripts) on the device. The " +"default is disabled." +msgstr "" + +#: rc.cpp:141 +#, kde-format +msgid "" +"DOM storage, sometimes called web storage, is like cookies on steroids. To " +"function, it requires that both JavaScript and local storage be enabled. It " +"is disabled by default." +msgstr "" + +#: rc.cpp:147 +#, kde-format +msgid "" +"The user agent identifies the browser to the web server. It serves no " +"useful purpose, but many web servers refuse to return the web page if they " +"don't see a user agent they like. The default is Privacy Browser." +msgstr "" + +#: rc.cpp:153 +#, kde-format +msgid "WebEngine Default" +msgstr "" + +#: rc.cpp:156 +#, kde-format +msgid "Firefox Linux" +msgstr "" + +#: rc.cpp:159 +#, kde-format +msgid "Chromium Linux" +msgstr "" + +#: rc.cpp:162 +#, kde-format +msgid "Firefox Windows" +msgstr "" + +#: rc.cpp:165 +#, kde-format +msgid "Chrome Windows" +msgstr "" + +#: rc.cpp:168 +#, kde-format +msgid "Edge Windows" +msgstr "" + +#: rc.cpp:171 +#, kde-format +msgid "Safari macOS" +msgstr "" + +#: rc.cpp:174 +#, kde-format +msgid "" +"Spell checking languages can be added by installing the Hunspell language " +"packages. One or more languages can be selected. All selected languages will " +"be checked simultaneously." +msgstr "" + +#: rc.cpp:177 +#, kde-format +msgid "Homepage" +msgstr "" + +#: rc.cpp:180 +#, kde-format +msgid "The default is https://www.mojeek.com/." +msgstr "" + +#: rc.cpp:183 +#, kde-format +msgid "Search engine" +msgstr "" + +#: rc.cpp:186 +#, kde-format +msgid "The default is Mojeek." +msgstr "" + +#: rc.cpp:189 +#, kde-format +msgid "Mojeek" +msgstr "" + +#: rc.cpp:192 +#, kde-format +msgid "Monocles" +msgstr "" + +#: rc.cpp:195 +#, kde-format +msgid "MetaGer" +msgstr "" + +#: rc.cpp:198 +#, kde-format +msgid "Google" +msgstr "" + +#: rc.cpp:201 +#, kde-format +msgid "Bing" +msgstr "" + +#: rc.cpp:204 +#, kde-format +msgid "Yahoo" +msgstr "" + +#: rc.cpp:210 +#, kde-format +msgid "" +"Valid values for the zoom factor are between 0.25 and 5.00. The default is " +"1.00." +msgstr "" + +#: rc.cpp:213 +#, kde-format +msgid "Download Location" +msgstr "" + +#: rc.cpp:216 +#, kde-format +msgid "The default is System Download Directory." +msgstr "" + +#: rc.cpp:219 +#, kde-format +msgid "System Download Directory" +msgstr "" + +#: rc.cpp:222 +#, kde-format +msgid "Browse" +msgstr "" + +#: rc.cpp:225 +#, kde-format +msgid "Tabs on top" +msgstr "" + +#: rc.cpp:228 +#, kde-format +msgid "Display the tabs at the top of the screen. The default is enabled." +msgstr "" + +#: rc.cpp:231 +#, kde-format +msgid "Full Screen Browsing" +msgstr "" + +#: rc.cpp:234 +#, kde-format +msgid "Hide menu bar" +msgstr "" + +#: rc.cpp:237 +#, kde-format +msgid "Hide the menu bar when browsing full screen. The default is enabled." +msgstr "" + +#: rc.cpp:240 +#, kde-format +msgid "Hide toolbars" +msgstr "" + +#: rc.cpp:243 +#, kde-format +msgid "Hide the toolbars when browsing full screen. The default is enabled." +msgstr "" + +#: rc.cpp:246 +#, kde-format +msgid "Hide tab bar" +msgstr "" + +#: rc.cpp:249 +#, kde-format +msgid "Hide the tab bar when browsing full screen. The default is enabled." +msgstr "" + +#: rc.cpp:252 +#, kde-format +msgid "Hide status bar" +msgstr "" + +#: rc.cpp:255 +#, kde-format +msgid "Hide the status bar when browsing full screen. The default is enabled." +msgstr "" + +#: rc.cpp:258 +#, kde-format +msgid "Would you like to download the following file?" +msgstr "" + +#: rc.cpp:261 +#, kde-format +msgid "File details" +msgstr "" + +#: rc.cpp:264 +#, kde-format +msgid "URL:" +msgstr "" + +#: rc.cpp:267 +#, kde-format +msgid "File type:" +msgstr "" + +#: rc.cpp:270 +#, kde-format +msgid "MIME type:" +msgstr "" + +#: rc.cpp:273 +#, kde-format +msgid "Size:" +msgstr "" + +#: rc.cpp:276 +#, kde-format +msgid "&Add cookie" +msgstr "" + +#: rc.cpp:279 +#, kde-format +msgid "&Delete cookie" +msgstr "" + +#: rc.cpp:282 +#, kde-format +msgid "Delete &all" +msgstr "" + +#: rc.cpp:285 +#, kde-format +msgid "Cookies prepended by a period are accessible to all subdomains." +msgstr "" + +#: rc.cpp:288 +#, kde-format +msgid "Domain" +msgstr "" + +#: rc.cpp:291 rc.cpp:309 rc.cpp:321 rc.cpp:336 rc.cpp:348 +#, kde-format +msgid "   " +msgstr "" + +#: rc.cpp:294 +#, kde-format +msgid "" +"The name identifies the cookie. Each cookie has a unique combination of " +"domain, name, and path." +msgstr "" + +#: rc.cpp:297 +#, kde-format +msgid "Name" +msgstr "" + +#: rc.cpp:300 rc.cpp:306 +#, kde-format +msgid "" +"Durable cookies pursist across restarts, irrespective of the expiration " +"date. All other cookies are deleted when Privacy Browser closes, " +"irrespective of the expiration date." +msgstr "" + +#: rc.cpp:303 +#, kde-format +msgid "Durable" +msgstr "" + +#: rc.cpp:312 +#, kde-format +msgid "Websites can restrict cookie access to subpath of their URL." +msgstr "" + +#: rc.cpp:315 +#, kde-format +msgid "Path" +msgstr "" + +#: rc.cpp:318 +#, kde-format +msgid "/" +msgstr "" + +#: rc.cpp:324 rc.cpp:330 +#, kde-format +msgid "" +"Cookies without an expiration date are known as session cookies and are " +"expected to be deleted every time the browser closes." +msgstr "" + +#: rc.cpp:327 +#, kde-format +msgid "Expiration date" +msgstr "" + +#: rc.cpp:333 +#, kde-format +msgid " " +msgstr "" + +#: rc.cpp:339 rc.cpp:345 +#, kde-format +msgid "" +"Restrict cookie access to HTTP (and HTTPS). This prevents JavaScript from " +"accessing the cookie, which hardens it against cross-site scripting attacks." +msgstr "" + +#: rc.cpp:342 +#, kde-format +msgid "HTTP only" +msgstr "" + +#: rc.cpp:351 rc.cpp:357 +#, kde-format +msgid "" +"Only allow the cookie to be transferred across HTTPS (as opposed to HTTP)." +msgstr "" + +#: rc.cpp:354 +#, kde-format +msgid "Secure" +msgstr "" + +#: rc.cpp:360 +#, kde-format +msgid "The value contains the cookie data." +msgstr "" + +#: rc.cpp:363 #, kde-format -msgid "Main Toolbar" +msgid "Value" msgstr "" -#: rc.cpp:13 +#: rc.cpp:364 #, kde-format msgctxt "NAME OF TRANSLATORS" msgid "Your names" msgstr "" -#: rc.cpp:14 +#: rc.cpp:365 #, kde-format msgctxt "EMAIL OF TRANSLATORS" msgid "Your emails" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d80c89d..837e42a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright © 2022 Soren Stoutner . +# Copyright 2022-2024 Soren Stoutner . # # This file is part of Privacy Browser PC . # @@ -16,41 +16,49 @@ # along with Privacy Browser PC. If not, see . # Create the executable and add the resources. -add_executable(privacy-browser resources.qrc) +add_executable(privacybrowser resources.qrc) # List the sources to include in the executable. -target_sources(privacy-browser PRIVATE +target_sources(privacybrowser PRIVATE main.cpp ) # Add the Qt logging category. This will create the `debug.h` header file. -ecm_qt_declare_logging_category(privacy-browser +ecm_qt_declare_logging_category(privacybrowser HEADER debug.h IDENTIFIER PRIVACYBROWSER CATEGORY_NAME "privacybrowser" ) # Include the KConfig controller file. -kconfig_add_kcfg_files(privacy-browser settings/Settings.kcfgc) +kconfig_add_kcfg_files(privacybrowser settings/Settings.kcfgc) # Use KDE Frameworks to handle internationalization of the following UI files. -ki18n_wrap_ui(privacy-browser +ki18n_wrap_ui(privacybrowser + uis/AddBookmarkDialog.ui + uis/AddFolderDialog.ui uis/AddOrEditCookieDialog.ui uis/AddTabWidget.ui + uis/BookmarksDialog.ui uis/CookiesDialog.ui uis/DomainSettingsDialog.ui uis/DurableCookiesDialog.ui + uis/EditBookmarkDialog.ui + uis/EditFolderDialog.ui + uis/HttpAuthenticationDialog.ui uis/SaveDialog.ui uis/SettingsGeneral.ui uis/SettingsPrivacy.ui + uis/SettingsSpellCheck.ui uis/TabWidget.ui ) # Link the following libraries. -target_link_libraries(privacy-browser +target_link_libraries(privacybrowser Qt5::Core Qt5::Gui Qt5::Sql + Qt5::PrintSupport Qt5::Widgets Qt5::WebEngineCore Qt5::WebEngineWidgets @@ -61,6 +69,7 @@ target_link_libraries(privacy-browser KF5::DBusAddons KF5::DocTools KF5::I18n + KF5::Notifications KF5::KIOCore KF5::KIOWidgets KF5::XmlGui @@ -78,13 +87,29 @@ add_subdirectory(widgets) add_subdirectory(windows) # Install Privacy Browser using the default KDE arguments. -install(TARGETS privacy-browser ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) +install(TARGETS privacybrowser ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) # Install Privacy Browser according to the instructions in the desktop file, specifying the permissions. install(PROGRAMS com.stoutner.privacybrowser.desktop DESTINATION ${KDE_INSTALL_APPDIR} PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ) -# Install Privacy Browser's metadata file. +# Install the metadata file. install(FILES com.stoutner.privacybrowser.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR}) -# Install Privacy Browser's icon. -ecm_install_icons(ICONS icons/sc-apps-privacy-browser.svg DESTINATION ${KDE_INSTALL_ICONDIR}) +# Install the notifyrc file. +install(FILES privacybrowser.notifyrc DESTINATION ${KDE_INSTALL_KNOTIFYRCDIR}) + +# Install the standard icons. +ecm_install_icons(ICONS icons/sc-apps-privacybrowser.svg DESTINATION ${KDE_INSTALL_ICONDIR}) +ecm_install_icons(ICONS icons/16-apps-privacybrowser.png DESTINATION ${KDE_INSTALL_ICONDIR}) +ecm_install_icons(ICONS icons/22-apps-privacybrowser.png DESTINATION ${KDE_INSTALL_ICONDIR}) +ecm_install_icons(ICONS icons/24-apps-privacybrowser.png DESTINATION ${KDE_INSTALL_ICONDIR}) +ecm_install_icons(ICONS icons/32-apps-privacybrowser.png DESTINATION ${KDE_INSTALL_ICONDIR}) +ecm_install_icons(ICONS icons/48-apps-privacybrowser.png DESTINATION ${KDE_INSTALL_ICONDIR}) +ecm_install_icons(ICONS icons/64-apps-privacybrowser.png DESTINATION ${KDE_INSTALL_ICONDIR}) +ecm_install_icons(ICONS icons/128-apps-privacybrowser.png DESTINATION ${KDE_INSTALL_ICONDIR}) +ecm_install_icons(ICONS icons/256-apps-privacybrowser.png DESTINATION ${KDE_INSTALL_ICONDIR}) +ecm_install_icons(ICONS icons/512-apps-privacybrowser.png DESTINATION ${KDE_INSTALL_ICONDIR}) +ecm_install_icons(ICONS icons/1024-apps-privacybrowser.png DESTINATION ${KDE_INSTALL_ICONDIR}) + +# Install the symbolic icon (used by Gnome). +install(FILES icons/privacybrowser-symbolic.svg DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/symbolic/apps/) diff --git a/src/com.stoutner.privacybrowser.appdata.xml b/src/com.stoutner.privacybrowser.appdata.xml index 49704a9..99e921f 100644 --- a/src/com.stoutner.privacybrowser.appdata.xml +++ b/src/com.stoutner.privacybrowser.appdata.xml @@ -1,9 +1,9 @@ + com.stoutner.privacybrowser @@ -21,7 +22,14 @@ Privacy Browser - A web browser that respects your privacy. + A web browser that respects your privacy + + +

+ Privacy Browser is a web browser based on Qt WebEngine with a focus on privacy and security. + Features like JavaScript and cookies are disabled by default but are easy to automatically enable on-the-fly or by domain. +

+
Network @@ -36,24 +44,74 @@ com.stoutner.privacybrowser.desktop - - + + +

Add bookmarks.

+

Add zoom controls to the status bar and a default zoom shortcut.

+

Add keyboard shortcuts for the URL toolbar actions.

+

Add an action to view page source.

+

Change the domain settings combo boxes to list enabled above disabled.

+

File downloads can now show the size before the download begins.

+

Add PIE (Position Independent Executable) compiler flags.

+

Fix a bug that sometimes allowed multiple domain settings to be created.

+

Fix the download notification not being cleared on Xfce.

+
+
+ + + +

Add a setting to control spatial navigation.

+

Add an action to reload and bypass cache.

+

Fix a crash if one Privacy Browser window is closed while a tab within it is loading.

+

Add keyboard+click commands to the Handbook.

+
+
+ + + +

Add the changelog to the Handbook.

+

Add the missing current domain settings icon on Gnome and Xfce.

+

Make changes to build on Guix.

+
+
+ + + +

Fix a crash on GNOME when downloading a file with local storage disabled.

+

Fix problems with missing icons on GNOME.

+

Display an animated favorite icon while a webpage is loading.

+

Fix the Handbook on non-KDE systems.

+

Change the order of entries in the WebEngine context menu.

+

Make spellcheck languages easier to click on.

+

Only generate a HTTP ping dialog if the request is made by the current tab.

+

Add a section to the Handbook about HTTP pings.

+
+
+ + + +

Initial release.

+
+
- privacy-browser + privacybrowser GPL-3.0-or-later Soren Stoutner - - https://www.freedesktop.org/software/appstream/docs/chap-Metadata.html#tag-screenshots + https://gitweb.stoutner.com/?p=PrivacyBrowserPC.git;a=blob_plain;f=doc/privacybrowser-window.png;hb=HEAD + The main Privacy Browser window. soren@stoutner.com + + +
diff --git a/src/com.stoutner.privacybrowser.desktop b/src/com.stoutner.privacybrowser.desktop index a7ca9d6..6132f6f 100644 --- a/src/com.stoutner.privacybrowser.desktop +++ b/src/com.stoutner.privacybrowser.desktop @@ -1,4 +1,4 @@ -# Copyright © 2022 Soren Stoutner . +# Copyright 2022-2023 Soren Stoutner . # # This file is part of Privacy Browser PC . # @@ -16,6 +16,7 @@ # along with Privacy Browser PC. If not, see . # The specification for the .desktop file can be found at . +# The file can be validated by running `desktop-file-validate com.stoutner.privacybrowser.desktop`. [Desktop Entry] # Set the type. Type=Application @@ -30,16 +31,16 @@ GenericName=Web Browser Categories=Network;WebBrowser; # Set the icon. -Icon=privacy-browser.svg +Icon=privacybrowser # Set the executable with the optional URL argument. -Exec=privacy-browser %u +Exec=privacybrowser %u # Specify the WM class, which groups windows of the same application together. -StartupWMClass=privacy-browser +StartupWMClass=privacybrowser -# TODO. Set the document path. +# Set the handbook docbook path. X-DocPath=privacybrowser/index.html # Enumerate the supported MIME types. -MimeType=text/html;text/xml;application/xhtml+xml;application/xml;x-scheme-handler/http;x-scheme-handler/https; +MimeType=text/html;application/xml;application/xhtml+xml;application/x-mimearchive;x-scheme-handler/http;x-scheme-handler/https; diff --git a/src/databases/BookmarksDatabase.cpp b/src/databases/BookmarksDatabase.cpp new file mode 100644 index 0000000..1050ba7 --- /dev/null +++ b/src/databases/BookmarksDatabase.cpp @@ -0,0 +1,1015 @@ +/* + * Copyright 2023 Soren Stoutner . + * + * This file is part of Privacy Browser PC . + * + * Privacy Browser PC 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. + * + * Privacy Browser PC 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 Privacy Browser PC. If not, see . + */ + +// Application headers. +#include "BookmarksDatabase.h" + +// Define the private static schema constants. +const int BookmarksDatabase::SCHEMA_VERSION = 0; + +// Define the public static constants. +const QString BookmarksDatabase::CONNECTION_NAME = "bookmarks_database"; +const QString BookmarksDatabase::BOOKMARK_NAME = "bookmark_name"; +const QString BookmarksDatabase::BOOKMARKS_TABLE = "bookmarks"; +const QString BookmarksDatabase::BOOKMARK_URL = "bookmark_url"; +const QString BookmarksDatabase::DISPLAY_ORDER = "display_order"; +const QString BookmarksDatabase::FAVORITE_ICON = "favorite_icon"; +const QString BookmarksDatabase::FOLDER_ID = "folder_id"; +const QString BookmarksDatabase::ID = "_id"; +const QString BookmarksDatabase::IS_FOLDER = "is_folder"; +const QString BookmarksDatabase::PARENT_FOLDER_ID = "parent_folder_id"; + +// Construct the class. +BookmarksDatabase::BookmarksDatabase() {} + +void BookmarksDatabase::addDatabase() +{ + // Add the bookmarks database. + QSqlDatabase bookmarksDatabase = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), CONNECTION_NAME); + + // Set the database name. + bookmarksDatabase.setDatabaseName(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/bookmarks.db"); + + // Open the database. + if (bookmarksDatabase.open()) // Opening the database succeeded. + { + // Check to see if the bookmarks table already exists. + if (bookmarksDatabase.tables().contains(BOOKMARKS_TABLE)) // The bookmarks table already exists. + { + // Query the database schema version. + QSqlQuery schemaVersionQuery = bookmarksDatabase.exec(QStringLiteral("PRAGMA user_version")); + + // Move to the first record. + schemaVersionQuery.first(); + + // Get the current schema version. + int currentSchemaVersion = schemaVersionQuery.value(0).toInt(); + + // Check to see if the schema has been updated. + if (currentSchemaVersion < SCHEMA_VERSION) + { + // Run the schema update code. + + // Update the schema version. + bookmarksDatabase.exec("PRAGMA user_version = " + QString::number(SCHEMA_VERSION)); + } + } + else // The bookmarks table does not exist. + { + // Instantiate a create table query. + QSqlQuery createTableQuery(bookmarksDatabase); + + // Populate the create table query. + createTableQuery.prepare("CREATE TABLE " + BOOKMARKS_TABLE + "(" + + ID + " INTEGER PRIMARY KEY, " + + BOOKMARK_NAME + " TEXT, " + + BOOKMARK_URL + " TEXT, " + + PARENT_FOLDER_ID + " INTEGER DEFAULT 0, " + + DISPLAY_ORDER + " INTEGER DEFAULT 0, " + + IS_FOLDER + " BOOLEAN DEFAULT FALSE, " + + FOLDER_ID + " INTEGER DEFAULT 0, " + + FAVORITE_ICON + " BLOB)"); + + // Execute the query. + if (!createTableQuery.exec()) + { + // Log any errors. + qDebug().noquote().nospace() << "Error creating table: " << bookmarksDatabase.lastError(); + } + + // Set the schema version. + bookmarksDatabase.exec("PRAGMA user_version = " + QString::number(SCHEMA_VERSION)); + } + } + else // Opening the database failed. + { + // Write the last database error message to the debug output. + qDebug().noquote().nospace() << "Error opening database: " << bookmarksDatabase.lastError(); + } +}; + +void BookmarksDatabase::addBookmark(const BookmarkStruct *bookmarkStructPointer) +{ + // Get a handle for the bookmarks database. + QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME); + + // Get the folder item count. + int folderItemCount = getFolderItemCount(bookmarkStructPointer->parentFolderId); + + // Instantiate an add bookmark query. + QSqlQuery addBookmarkQuery(bookmarksDatabase); + + // Prepare the add bookmark query. + addBookmarkQuery.prepare("INSERT INTO " + BOOKMARKS_TABLE + " (" + + BOOKMARK_NAME + ", " + + BOOKMARK_URL + ", " + + PARENT_FOLDER_ID + ", " + + DISPLAY_ORDER + ", " + + FAVORITE_ICON + ") " + + "VALUES (:bookmark_name, :bookmark_url, :parent_folder_id, :display_order, :favorite_icon)" + ); + + // Bind the query values. + addBookmarkQuery.bindValue(":bookmark_name", bookmarkStructPointer->name); + addBookmarkQuery.bindValue(":bookmark_url", bookmarkStructPointer->url); + addBookmarkQuery.bindValue(":parent_folder_id", bookmarkStructPointer->parentFolderId); + addBookmarkQuery.bindValue(":display_order", folderItemCount); + addBookmarkQuery.bindValue(":favorite_icon", getFavoriteIconBase64String(bookmarkStructPointer->favoriteIcon)); + + // Execute the add bookmark query. + addBookmarkQuery.exec(); +} + +void BookmarksDatabase::addFolder(const BookmarkStruct *bookmarkStructPointer) +{ + // Get a handle for the bookmarks database. + QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME); + + // Get the folder item count. + int folderItemCount = getFolderItemCount(bookmarkStructPointer->parentFolderId); + + // Instantiate an add folder query. + QSqlQuery addFolderQuery(bookmarksDatabase); + + // Prepare the add folder query. + addFolderQuery.prepare("INSERT INTO " + BOOKMARKS_TABLE + " (" + + BOOKMARK_NAME + ", " + + PARENT_FOLDER_ID + ", " + + DISPLAY_ORDER + ", " + + IS_FOLDER + ", " + + FOLDER_ID + ", " + + FAVORITE_ICON + ") " + + "VALUES (:bookmark_name, :parent_folder_id, :display_order, :is_folder, :folder_id, :favorite_icon)" + ); + + // Bind the query values. + addFolderQuery.bindValue(":bookmark_name", bookmarkStructPointer->name); + addFolderQuery.bindValue(":parent_folder_id", bookmarkStructPointer->parentFolderId); + addFolderQuery.bindValue(":display_order", folderItemCount); + addFolderQuery.bindValue(":is_folder", 1); + addFolderQuery.bindValue(":folder_id", generateFolderId()); + addFolderQuery.bindValue(":favorite_icon", getFavoriteIconBase64String(bookmarkStructPointer->favoriteIcon)); + + // Execute the add folder query. + addFolderQuery.exec(); +} + +void BookmarksDatabase::deleteBookmark(const int databaseId) +{ + // Get a handle for the bookmarks database. + QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME); + + // Instantiate a delete bookmark query. + QSqlQuery deleteBookmarkQuery(bookmarksDatabase); + + // Prepare the delete bookmark query. + deleteBookmarkQuery.prepare("DELETE FROM " + BOOKMARKS_TABLE + " WHERE " + ID + " = :id"); + + // Bind the query values. + deleteBookmarkQuery.bindValue(":id", databaseId); + + // Execute the query. + deleteBookmarkQuery.exec(); +} + +void BookmarksDatabase::deleteBookmarks(const QString url) +{ + // Get a handle for the bookmarks database. + QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME); + + // Instantiate a parent folder IDs query. + QSqlQuery parentFolderIdsQuery(bookmarksDatabase); + + // Prepare the parent folder IDs query. + parentFolderIdsQuery.prepare("SELECT " + PARENT_FOLDER_ID + " FROM " + BOOKMARKS_TABLE + " WHERE " + IS_FOLDER + " = 0 AND " + BOOKMARK_URL + " = :url"); + + // Bind the query values. + parentFolderIdsQuery.bindValue(":url", url); + + // Execute the query. + parentFolderIdsQuery.exec(); + + // Instantiate a delete bookmarks query. + QSqlQuery deleteBookmarksQuery(bookmarksDatabase); + + // Prepare the delete bookmark query. + deleteBookmarksQuery.prepare("DELETE FROM " + BOOKMARKS_TABLE + " WHERE " + IS_FOLDER + " = 0 AND " + BOOKMARK_URL + " = :url"); + + // Bind the query values. + deleteBookmarksQuery.bindValue(":url", url); + + // Execute the query. + deleteBookmarksQuery.exec(); + + // Create a parent folder IDs list. A standard list can be sorted and deduplicated. + std::list parentFolderIdsList; + + // Populate the parent folder IDs list. + while (parentFolderIdsQuery.next()) + { + // Add the parent folder ID to the list. + parentFolderIdsList.push_back(parentFolderIdsQuery.value(PARENT_FOLDER_ID).toDouble()); + } + + // Sort the parent folder IDs list. + parentFolderIdsList.sort(); + + // Remove duplicate entries from the parent folder IDs list. + parentFolderIdsList.unique(); + + // Update the display order of each folder where a bookmark was deleted. + for (const double parentFolderId : parentFolderIdsList) + updateFolderContentsDisplayOrder(parentFolderId); +} + +double BookmarksDatabase::generateFolderId() +{ + // Get the current time in epoch format (milliseconds). + double possibleFolderId = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + + // Get a handle for the bookmarks database. + QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME); + + // Instantiate a existing folder query. + QSqlQuery existingFolderQuery(bookmarksDatabase); + + // Prepare the existing folder query. + existingFolderQuery.prepare("SELECT " + ID + " FROM " + BOOKMARKS_TABLE + " WHERE " + FOLDER_ID + " = :possible_folder_id"); + + // Bind the query values. + existingFolderQuery.bindValue(":possible_folder_id", possibleFolderId); + + // Execute the query. + existingFolderQuery.exec(); + + // Generate a new folder ID if this one is not unique. The existing folder query will only be valid if there is at least one item. + if (existingFolderQuery.isValid()) + possibleFolderId = generateFolderId(); + + return possibleFolderId; +} + +QList* BookmarksDatabase::getAllFolderUrls(const double folderId) +{ + // Get a handle for the bookmarks database. + QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME); + + // Instantiate a folder URLs query. + QSqlQuery folderUrlsQuery(bookmarksDatabase); + + // Set the query to be forward only, which is more performant. + folderUrlsQuery.setForwardOnly(true); + + // Prepare the folder URLs query. + folderUrlsQuery.prepare("SELECT " + BOOKMARK_URL + ", " + IS_FOLDER + ", " + FOLDER_ID + " FROM " + BOOKMARKS_TABLE + " WHERE " + PARENT_FOLDER_ID + " = :parent_folder_id"); + + // Bind the query values. + folderUrlsQuery.bindValue(":parent_folder_id", folderId); + + // Execute the query. + folderUrlsQuery.exec(); + + // Create a folder URLs list. + QList *folderUrlsListPointer = new QList; + + // Populate the folder URLs list. + while (folderUrlsQuery.next()) + { + // Process the entry according to the type. + if (folderUrlsQuery.value(IS_FOLDER).toBool()) // This is a folder. + { + // Get the subfolder URLs to the list. + folderUrlsListPointer->append(*getAllFolderUrls(folderUrlsQuery.value(FOLDER_ID).toDouble())); + } + else // This is a bookmark. + { + // Add the URL to the list. + folderUrlsListPointer->append(folderUrlsQuery.value(BOOKMARK_URL).toString()); + } + } + + // Return the folder URLs list. + return folderUrlsListPointer; +} + +BookmarkStruct* BookmarksDatabase::getBookmark(const int databaseId) +{ + // Get a handle for the bookmarks database. + QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME); + + // Instantiate a bookmark query. + QSqlQuery bookmarkQuery(bookmarksDatabase); + + // Set the query to be forward only, which is more performant. + bookmarkQuery.setForwardOnly(true); + + // Prepare the bookmark query. + bookmarkQuery.prepare("SELECT * FROM " + BOOKMARKS_TABLE + " WHERE " + ID + " = :id"); + + // Bind the query values. + bookmarkQuery.bindValue(":id", databaseId); + + // Execute the query. + bookmarkQuery.exec(); + + // Move to the first entry. + bookmarkQuery.first(); + + // Create a bookmark struct. + struct BookmarkStruct *bookmarkStructPointer = new BookmarkStruct(); + + // Get the favorite icon base 64 byte array. + QByteArray favoriteIconByteArray = QByteArray::fromBase64(bookmarkQuery.value(FAVORITE_ICON).toByteArray()); + + // Create a favorite icon pixmap. + QPixmap favoriteIconPixmap; + + // Load the pixmap from byte array. + favoriteIconPixmap.loadFromData(favoriteIconByteArray); + + // Populate the bookmark struct. + bookmarkStructPointer->databaseId = bookmarkQuery.value(ID).toInt(); + bookmarkStructPointer->name = bookmarkQuery.value(BOOKMARK_NAME).toString(); + bookmarkStructPointer->url = bookmarkQuery.value(BOOKMARK_URL).toString(); + bookmarkStructPointer->parentFolderId = bookmarkQuery.value(PARENT_FOLDER_ID).toDouble(); + bookmarkStructPointer->displayOrder = bookmarkQuery.value(DISPLAY_ORDER).toInt(); + bookmarkStructPointer->isFolder = bookmarkQuery.value(IS_FOLDER).toBool(); + bookmarkStructPointer->folderId = bookmarkQuery.value(FOLDER_ID).toDouble(); + bookmarkStructPointer->favoriteIcon = QIcon(favoriteIconPixmap); + + // Return the bookmark struct pointer. + return bookmarkStructPointer; +} + +std::list* BookmarksDatabase::getBookmarks() +{ + // Get a handle for the bookmarks database. + QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME); + + // Instantiate a bookmarks query. + QSqlQuery bookmarksQuery(bookmarksDatabase); + + // Set the query to be forward only, which is more performant. + bookmarksQuery.setForwardOnly(true); + + // Prepare the bookmarks query. + bookmarksQuery.prepare("SELECT * FROM " + BOOKMARKS_TABLE + " ORDER BY " + DISPLAY_ORDER + " ASC"); + + // Execute the query. + bookmarksQuery.exec(); + + // Create a bookmark list. + std::list *bookmarkListPointer = new std::list; + + // Populate the bookmark list. + while (bookmarksQuery.next()) + { + // Create a bookmark struct. + struct BookmarkStruct bookmarkStruct; + + // Get the favorite icon base 64 byte array. + QByteArray favoriteIconByteArray = QByteArray::fromBase64(bookmarksQuery.value(FAVORITE_ICON).toByteArray()); + + // Create a favorite icon pixmap. + QPixmap favoriteIconPixmap; + + // Load the pixmap from byte array. + favoriteIconPixmap.loadFromData(favoriteIconByteArray); + + // Populate the bookmark struct. + bookmarkStruct.databaseId = bookmarksQuery.value(ID).toInt(); + bookmarkStruct.name = bookmarksQuery.value(BOOKMARK_NAME).toString(); + bookmarkStruct.url = bookmarksQuery.value(BOOKMARK_URL).toString(); + bookmarkStruct.parentFolderId = bookmarksQuery.value(PARENT_FOLDER_ID).toDouble(); + bookmarkStruct.displayOrder = bookmarksQuery.value(DISPLAY_ORDER).toInt(); + bookmarkStruct.isFolder = bookmarksQuery.value(IS_FOLDER).toBool(); + bookmarkStruct.folderId = bookmarksQuery.value(FOLDER_ID).toDouble(); + bookmarkStruct.favoriteIcon = QIcon(favoriteIconPixmap); + + // Add the bookmark to the list. + bookmarkListPointer->push_back(bookmarkStruct); + } + + // Return the bookmark list. + return bookmarkListPointer; +} + +QList* BookmarksDatabase::getBookmarksInFolderExcept(const double folderId, QList *exceptDatabaseIdsListPointer) +{ + // Get a handle for the bookmarks database. + QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME); + + // Instantiate a bookmarks query. + QSqlQuery bookmarksQuery(bookmarksDatabase); + + // Set the query to be forward only, which is more performant. + bookmarksQuery.setForwardOnly(true); + + // Create an IDs not to get string. + QString idsNotToGetString; + + for (const int databaseId : *exceptDatabaseIdsListPointer) + { + // Check to see if there the string already has at least one number. + if (!idsNotToGetString.isEmpty()) + { + // This is not the first number, so add a `,`. + idsNotToGetString.append(QLatin1Char(',')); + } + + // Append the database ID. + idsNotToGetString.append(QString::number(databaseId)); + } + + // Prepare the bookmarks query. + bookmarksQuery.prepare("SELECT * FROM " + BOOKMARKS_TABLE + " WHERE " + PARENT_FOLDER_ID + " = :parent_folder_id AND " + ID + " NOT IN (" + idsNotToGetString + ") ORDER BY " + DISPLAY_ORDER + " ASC"); + + // Bind the query values. + bookmarksQuery.bindValue(":parent_folder_id", folderId); + + // Execute the query. + bookmarksQuery.exec(); + + // Create a bookmark list. + QList *bookmarkListPointer = new QList; + + // Populate the bookmark list. + while (bookmarksQuery.next()) + { + // Create a bookmark struct. + struct BookmarkStruct bookmarkStruct; + + // Get the favorite icon base 64 byte array. + QByteArray favoriteIconByteArray = QByteArray::fromBase64(bookmarksQuery.value(FAVORITE_ICON).toByteArray()); + + // Create a favorite icon pixmap. + QPixmap favoriteIconPixmap; + + // Load the pixmap from byte array. + favoriteIconPixmap.loadFromData(favoriteIconByteArray); + + // Populate the bookmark struct. + bookmarkStruct.databaseId = bookmarksQuery.value(ID).toInt(); + bookmarkStruct.name = bookmarksQuery.value(BOOKMARK_NAME).toString(); + bookmarkStruct.url = bookmarksQuery.value(BOOKMARK_URL).toString(); + bookmarkStruct.parentFolderId = bookmarksQuery.value(PARENT_FOLDER_ID).toDouble(); + bookmarkStruct.displayOrder = bookmarksQuery.value(DISPLAY_ORDER).toInt(); + bookmarkStruct.isFolder = bookmarksQuery.value(IS_FOLDER).toBool(); + bookmarkStruct.folderId = bookmarksQuery.value(FOLDER_ID).toDouble(); + bookmarkStruct.favoriteIcon = QIcon(favoriteIconPixmap); + + // Add the bookmark to the list. + bookmarkListPointer->push_back(bookmarkStruct); + } + + // Return the bookmark list. + return bookmarkListPointer; +} + +QString BookmarksDatabase::getFavoriteIconBase64String(const QIcon &favoriteIcon) +{ + // Get a favorite icon pixmap. + QPixmap favoriteIconPixmap = favoriteIcon.pixmap(32, 32); + + // Create a favorite icon byte array. + QByteArray favoriteIconByteArray; + + // Create a favorite icon buffer. + QBuffer favoriteIconBuffer(&favoriteIconByteArray); + + // Open the buffer. + favoriteIconBuffer.open(QIODevice::WriteOnly); + + // Convert the favorite icon pixmap into a byte array in PNG format. + favoriteIconPixmap.save(&favoriteIconBuffer, "PNG"); + + // Close the buffer. + favoriteIconBuffer.close(); + + // Convert the favorite icon byte array to a base 64 string. + QString favoriteIconBase64String = favoriteIconByteArray.toBase64(); + + // Return the favorite icon base 64 string. + return favoriteIconBase64String; +} + +QList* BookmarksDatabase::getFolderContents(const double folderId) +{ + // Get a handle for the bookmarks database. + QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME); + + // Instantiate a folder contents query. + QSqlQuery folderContentsQuery(bookmarksDatabase); + + // Set the query to be forward only, which is more performant. + folderContentsQuery.setForwardOnly(true); + + // Prepare the folder contents query. + folderContentsQuery.prepare("SELECT * FROM " + BOOKMARKS_TABLE + " WHERE " + PARENT_FOLDER_ID + " = :parent_folder_id ORDER BY " + DISPLAY_ORDER + " ASC"); + + // Bind the query values. + folderContentsQuery.bindValue(":parent_folder_id", folderId); + + // Execute the query. + folderContentsQuery.exec(); + + // Create a folder contents list. + QList *folderContentsListPointer = new QList; + + // Populate the folder contents list. + while (folderContentsQuery.next()) + { + // Create a bookmark struct. + struct BookmarkStruct bookmarkStruct; + + // Get the favorite icon base 64 byte array. + QByteArray favoriteIconByteArray = QByteArray::fromBase64(folderContentsQuery.value(FAVORITE_ICON).toByteArray()); + + // Create a favorite icon pixmap. + QPixmap favoriteIconPixmap; + + // Load the pixmap from byte array. + favoriteIconPixmap.loadFromData(favoriteIconByteArray); + + // Populate the bookmark struct. + bookmarkStruct.databaseId = folderContentsQuery.value(ID).toInt(); + bookmarkStruct.name = folderContentsQuery.value(BOOKMARK_NAME).toString(); + bookmarkStruct.url = folderContentsQuery.value(BOOKMARK_URL).toString(); + bookmarkStruct.parentFolderId = folderContentsQuery.value(PARENT_FOLDER_ID).toDouble(); + bookmarkStruct.displayOrder = folderContentsQuery.value(DISPLAY_ORDER).toInt(); + bookmarkStruct.isFolder = folderContentsQuery.value(IS_FOLDER).toBool(); + bookmarkStruct.folderId = folderContentsQuery.value(FOLDER_ID).toDouble(); + bookmarkStruct.favoriteIcon = QIcon(favoriteIconPixmap); + + // Add the item to the list. + folderContentsListPointer->append(bookmarkStruct); + } + + // Return the folder contents list. + return folderContentsListPointer; +} + +QList* BookmarksDatabase::getFolderContentsDatabaseIds(const double folderId) +{ + // Get a handle for the bookmarks database. + QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME); + + // Instantiate a folder contents query. + QSqlQuery folderContentsQuery(bookmarksDatabase); + + // Set the query to be forward only, which is more performant. + folderContentsQuery.setForwardOnly(true); + + // Prepare the folder contents query. + folderContentsQuery.prepare("SELECT " + ID + " FROM " + BOOKMARKS_TABLE + " WHERE " + PARENT_FOLDER_ID + " = :parent_folder_id"); + + // Bind the query values. + folderContentsQuery.bindValue(":parent_folder_id", folderId); + + // Execute the query. + folderContentsQuery.exec(); + + // Create a folder contents database ID list. + QList *folderContentsDatabaseIdsListPointer = new QList; + + // Populate the folder contents list. + while (folderContentsQuery.next()) + { + // Add the database ID to the list. + folderContentsDatabaseIdsListPointer->append(folderContentsQuery.value(ID).toInt()); + } + + // Return the folder contents database ID list. + return folderContentsDatabaseIdsListPointer; +} + +QList *BookmarksDatabase::getFolderContentsDatabaseIdsRecursively(const double folderId) +{ + // Get a handle for the bookmarks database. + QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME); + + // Instantiate a folder contents query. + QSqlQuery folderContentsQuery(bookmarksDatabase); + + // Set the query to be forward only, which is more performant. + folderContentsQuery.setForwardOnly(true); + + // Prepare the folder contents query. + folderContentsQuery.prepare("SELECT " + ID + ", " + IS_FOLDER + ", " + FOLDER_ID + " FROM " + BOOKMARKS_TABLE + " WHERE " + PARENT_FOLDER_ID + " = :parent_folder_id"); + + // Bind the query values. + folderContentsQuery.bindValue(":parent_folder_id", folderId); + + // Execute the query. + folderContentsQuery.exec(); + + // Create a folder contents database ID list. + QList *folderContentsDatabaseIdsListPointer = new QList; + + // Populate the folder contents list. + while (folderContentsQuery.next()) + { + // Add the database ID to the list. + folderContentsDatabaseIdsListPointer->append(folderContentsQuery.value(ID).toInt()); + + // Recursively get the contents if this is a subfolder. + if (folderContentsQuery.value(IS_FOLDER).toBool()) + folderContentsDatabaseIdsListPointer->append(*getFolderContentsDatabaseIdsRecursively(folderContentsQuery.value(FOLDER_ID).toDouble())); + } + + // Return the folder contents database ID list. + return folderContentsDatabaseIdsListPointer; +} + +int BookmarksDatabase::getFolderDatabaseId(const double folderId) +{ + // Get a handle for the bookmarks database. + QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME); + + // Instantiate a folder database ID query. + QSqlQuery folderDatabaseIdQuery(bookmarksDatabase); + + // Set the query to be forward only, which is more performant. + folderDatabaseIdQuery.setForwardOnly(true); + + // Prepare the folder database ID query. + folderDatabaseIdQuery.prepare("SELECT " + ID + " FROM " + BOOKMARKS_TABLE + " WHERE " + FOLDER_ID + " = :folder_id"); + + // Bind the query values. + folderDatabaseIdQuery.bindValue(":folder_id", folderId); + + // Execute the query. + folderDatabaseIdQuery.exec(); + + // Move to the first entry. + folderDatabaseIdQuery.first(); + + // Return the folder database ID. + return folderDatabaseIdQuery.value(ID).toInt(); +} + +double BookmarksDatabase::getFolderId(const int databaseId) +{ + // Get a handle for the bookmarks database. + QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME); + + // Instantiate a folder ID query. + QSqlQuery folderIdQuery(bookmarksDatabase); + + // Set the query to be forward only, which is more performant. + folderIdQuery.setForwardOnly(true); + + // Prepare the folder ID query. + folderIdQuery.prepare("SELECT " + FOLDER_ID + " FROM " + BOOKMARKS_TABLE + " WHERE " + ID + " = :database_id"); + + // Bind the query values. + folderIdQuery.bindValue(":database_id", databaseId); + + // Execute the query. + folderIdQuery.exec(); + + // Move to the first entry. + folderIdQuery.first(); + + // Return the folder ID. + return folderIdQuery.value(FOLDER_ID).toDouble(); +} + +int BookmarksDatabase::getFolderItemCount(const double folderId) +{ + // Get a handle for the bookmarks database. + QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME); + + // Instantiate a folder contents query. + QSqlQuery folderContentsQuery(bookmarksDatabase); + + // Set the query to be forward only, which is more performant. + folderContentsQuery.setForwardOnly(true); + + // Prepare the folder contents query. + folderContentsQuery.prepare("SELECT " + ID + " FROM " + BOOKMARKS_TABLE + " WHERE " + PARENT_FOLDER_ID + " = :parent_folder_id"); + + // Bind the query values. + folderContentsQuery.bindValue(":parent_folder_id", folderId); + + // Execute the query. + folderContentsQuery.exec(); + + // Move to the last row. + folderContentsQuery.last(); + + // Initialize an item count variable. + int itemCount = 0; + + // Check to see if the query is valid (there is at least one item). + if (folderContentsQuery.isValid()) + { + // Get the number of rows (which is zero based) and add one to calculate the number of bookmarks. + itemCount = folderContentsQuery.at() + 1; + } + + // Return the item count. + return itemCount; +} + +double BookmarksDatabase::getParentFolderId(const int databaseId) +{ + // Get a handle for the bookmarks database. + QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME); + + // Instantiate a parent folder ID query. + QSqlQuery parentFolderIdQuery(bookmarksDatabase); + + // Set the query to be forward only, which is more performant. + parentFolderIdQuery.setForwardOnly(true); + + // Prepare the parent folder ID query. + parentFolderIdQuery.prepare("SELECT " + PARENT_FOLDER_ID + " FROM " + BOOKMARKS_TABLE + " WHERE " + ID + " = :database_id"); + + // Bind the query values. + parentFolderIdQuery.bindValue(":database_id", databaseId); + + // Execute the query. + parentFolderIdQuery.exec(); + + // Move to the first entry. + parentFolderIdQuery.first(); + + // Return the parent folder ID. + return parentFolderIdQuery.value(PARENT_FOLDER_ID).toDouble(); +} + +QList* BookmarksDatabase::getSubfolders(const double folderId) +{ + // Get a handle for the bookmarks database. + QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME); + + // Instantiate a subfolders query. + QSqlQuery subfoldersQuery(bookmarksDatabase); + + // Set the query to be forward only, which is more performant. + subfoldersQuery.setForwardOnly(true); + + // Prepare the subfolders query. + subfoldersQuery.prepare("SELECT * FROM " + BOOKMARKS_TABLE + " WHERE " + IS_FOLDER + " = 1 AND " + PARENT_FOLDER_ID + " = :parent_folder_id ORDER BY " + DISPLAY_ORDER + " ASC"); + + // Bind the query values. + subfoldersQuery.bindValue(":parent_folder_id", folderId); + + // Execute the query. + subfoldersQuery.exec(); + + // Create a subfolder list. + QList *subfoldersListPointer = new QList; + + // Populate the subfolder list. + while (subfoldersQuery.next()) + { + // Create a bookmark struct. + struct BookmarkStruct bookmarkStruct; + + // Get the favorite icon base 64 byte array. + QByteArray favoriteIconByteArray = QByteArray::fromBase64(subfoldersQuery.value(FAVORITE_ICON).toByteArray()); + + // Create a favorite icon pixmap. + QPixmap favoriteIconPixmap; + + // Load the pixmap from byte array. + favoriteIconPixmap.loadFromData(favoriteIconByteArray); + + // Populate the bookmark struct. + bookmarkStruct.databaseId = subfoldersQuery.value(ID).toInt(); + bookmarkStruct.name = subfoldersQuery.value(BOOKMARK_NAME).toString(); + bookmarkStruct.parentFolderId = subfoldersQuery.value(PARENT_FOLDER_ID).toDouble(); + bookmarkStruct.displayOrder = subfoldersQuery.value(DISPLAY_ORDER).toInt(); + bookmarkStruct.isFolder = subfoldersQuery.value(IS_FOLDER).toBool(); + bookmarkStruct.folderId = subfoldersQuery.value(FOLDER_ID).toDouble(); + bookmarkStruct.favoriteIcon = QIcon(favoriteIconPixmap); + + // Add the subfolder to the list. + subfoldersListPointer->append(bookmarkStruct); + } + + // Return the subfolders list. + return subfoldersListPointer; +} + +bool BookmarksDatabase::isBookmarked(const QString url) +{ + // Get a handle for the bookmarks database. + QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME); + + // Instantiate an is bookmarked query. + QSqlQuery isBookmarkedQuery(bookmarksDatabase); + + // Set the query to be forward only, which is more performant. + isBookmarkedQuery.setForwardOnly(true); + + // Prepare the is bookmarked query. + isBookmarkedQuery.prepare("SELECT " + ID + " FROM " + BOOKMARKS_TABLE + " WHERE " + IS_FOLDER + " = 0 AND " + BOOKMARK_URL + " = :url"); + + // Bind the query values. + isBookmarkedQuery.bindValue(":url", url); + + // Execute the query. + isBookmarkedQuery.exec(); + + // Move to the first entry. + isBookmarkedQuery.first(); + + // Return true if the query is valid (there is at least one item). + return isBookmarkedQuery.isValid(); +} + +bool BookmarksDatabase::isFolder(const int databaseId) +{ + // Get a handle for the bookmarks database. + QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME); + + // Instantiate an is folder query. + QSqlQuery isFolderQuery(bookmarksDatabase); + + // Set the query to be forward only, which is more performant. + isFolderQuery.setForwardOnly(true); + + // Prepare the is folder query. + isFolderQuery.prepare("SELECT " + IS_FOLDER + " FROM " + BOOKMARKS_TABLE + " WHERE " + ID + " = :id"); + + // Bind the query values. + isFolderQuery.bindValue(":id", databaseId); + + // Execute the query. + isFolderQuery.exec(); + + // Move to the first entry. + isFolderQuery.first(); + + // Return the folder status. + return isFolderQuery.value(IS_FOLDER).toBool(); +} + +void BookmarksDatabase::updateBookmark(const BookmarkStruct *bookmarkStructPointer) +{ + // Get a handle for the bookmarks database. + QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME); + + // Instantiate an update bookmark query. + QSqlQuery updateBookmarkQuery(bookmarksDatabase); + + // Prepare the update bookmark query. + updateBookmarkQuery.prepare("UPDATE " + BOOKMARKS_TABLE + " SET " + + BOOKMARK_NAME + " = :bookmark_name, " + + BOOKMARK_URL + " = :bookmark_url, " + + PARENT_FOLDER_ID + " = :parent_folder_id, " + + DISPLAY_ORDER + " = :display_order, " + + FAVORITE_ICON + "= :favorite_icon " + + "WHERE " + ID + " = :id"); + + // Bind the query values. + updateBookmarkQuery.bindValue(":bookmark_name", bookmarkStructPointer->name); + updateBookmarkQuery.bindValue(":bookmark_url", bookmarkStructPointer->url); + updateBookmarkQuery.bindValue(":parent_folder_id", bookmarkStructPointer->parentFolderId); + updateBookmarkQuery.bindValue(":display_order", bookmarkStructPointer->displayOrder); + updateBookmarkQuery.bindValue(":favorite_icon", getFavoriteIconBase64String(bookmarkStructPointer->favoriteIcon)); + updateBookmarkQuery.bindValue(":id", bookmarkStructPointer->databaseId); + + // Execute the query. + updateBookmarkQuery.exec(); +} + +void BookmarksDatabase::updateBookmarkName(const int databaseId, const QString &bookmarkName) +{ + // Get a handle for the bookmarks database. + QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME); + + // Instantiate an update bookmark name query. + QSqlQuery updateBookmarkNameQuery(bookmarksDatabase); + + // Prepare the update bookmark name query. + updateBookmarkNameQuery.prepare("UPDATE " + BOOKMARKS_TABLE + + " SET " + BOOKMARK_NAME + " = :bookmark_name " + + "WHERE " + ID + " = :id"); + + // Bind the query values. + updateBookmarkNameQuery.bindValue(":bookmark_name", bookmarkName); + updateBookmarkNameQuery.bindValue(":id", databaseId); + + // Execute the query. + updateBookmarkNameQuery.exec(); +} + +void BookmarksDatabase::updateBookmarkUrl(const int databaseId, const QString &bookmarkUrl) +{ + // Get a handle for the bookmarks database. + QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME); + + // Instantiate an update bookmark URL query. + QSqlQuery updateBookmarkUrlQuery(bookmarksDatabase); + + // Prepare the update bookmark URL query. + updateBookmarkUrlQuery.prepare("UPDATE " + BOOKMARKS_TABLE + + " SET " + BOOKMARK_URL + " = :bookmark_url " + + "WHERE " + ID + " = :id"); + + // Bind the query values. + updateBookmarkUrlQuery.bindValue(":bookmark_url", bookmarkUrl); + updateBookmarkUrlQuery.bindValue(":id", databaseId); + + // Execute the query. + updateBookmarkUrlQuery.exec(); +} + +void BookmarksDatabase::updateDisplayOrder(const int databaseId, const int displayOrder) +{ + // Get a handle for the bookmarks database. + QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME); + + // Instantiate an update bookmark display order query. + QSqlQuery updateBookmarkDisplayOrderQuery(bookmarksDatabase); + + // Prepare the update bookmark display order query. + updateBookmarkDisplayOrderQuery.prepare("UPDATE " + BOOKMARKS_TABLE + + " SET " + DISPLAY_ORDER + " = :display_order " + + "WHERE " + ID + " = :id"); + + // Bind the query values. + updateBookmarkDisplayOrderQuery.bindValue(":display_order", displayOrder); + updateBookmarkDisplayOrderQuery.bindValue(":id", databaseId); + + // Execute the query. + updateBookmarkDisplayOrderQuery.exec(); +} + +void BookmarksDatabase::updateFolderContentsDisplayOrder(const double folderId) +{ + // Get a handle for the bookmarks database. + QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME); + + // Instantiate a folder contents query. + QSqlQuery folderContentsQuery(bookmarksDatabase); + + // Set the query to be forward only, which is more performant. + folderContentsQuery.setForwardOnly(true); + + // Prepare the folder contents query. + folderContentsQuery.prepare("SELECT " + ID + ", " + DISPLAY_ORDER + " FROM " + BOOKMARKS_TABLE + " WHERE " + PARENT_FOLDER_ID + " = :parent_folder_id ORDER BY " + DISPLAY_ORDER + " ASC"); + + // Bind the query values. + folderContentsQuery.bindValue(":parent_folder_id", folderId); + + // Execute the query. + folderContentsQuery.exec(); + + // Define a new display order int. + int newDisplayOrder = 0; + + // Populate the subfolder list. + while (folderContentsQuery.next()) + { + // Update the display order if it has changed. + if (folderContentsQuery.value(DISPLAY_ORDER).toInt() != newDisplayOrder) + updateDisplayOrder(folderContentsQuery.value(ID).toInt(), newDisplayOrder); + + // Increment the new display order. + ++newDisplayOrder; + } +} + +void BookmarksDatabase::updateParentFolderAndDisplayOrder(const int databaseId, const double parentFolderId, const int displayOrder) +{ + // Get a handle for the bookmarks database. + QSqlDatabase bookmarksDatabase = QSqlDatabase::database(CONNECTION_NAME); + + // Instantiate an update bookmark display order query. + QSqlQuery updateBookmarkDisplayOrderQuery(bookmarksDatabase); + + // Prepare the update bookmark display order query. + updateBookmarkDisplayOrderQuery.prepare("UPDATE " + BOOKMARKS_TABLE + + " SET " + PARENT_FOLDER_ID + " = :parent_folder_id " + + ", " + DISPLAY_ORDER + " = :display_order " + + "WHERE " + ID + " = :id"); + + // Bind the query values. + updateBookmarkDisplayOrderQuery.bindValue(":parent_folder_id", parentFolderId); + updateBookmarkDisplayOrderQuery.bindValue(":display_order", displayOrder); + updateBookmarkDisplayOrderQuery.bindValue(":id", databaseId); + + // Execute the query. + updateBookmarkDisplayOrderQuery.exec(); +} diff --git a/src/databases/BookmarksDatabase.h b/src/databases/BookmarksDatabase.h new file mode 100644 index 0000000..932a1e4 --- /dev/null +++ b/src/databases/BookmarksDatabase.h @@ -0,0 +1,82 @@ +/* + * Copyright 2023 Soren Stoutner . + * + * This file is part of Privacy Browser PC . + * + * Privacy Browser PC 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. + * + * Privacy Browser PC 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 Privacy Browser PC. If not, see . + */ + +#ifndef BOOKMARKSDATABASE_H +#define BOOKMARKSDATABASE_H + +// Application headers. +#include "structs/BookmarkStruct.h" + +// Qt framework headers. +#include + +class BookmarksDatabase +{ +public: + // The default constructor. + BookmarksDatabase(); + + // The public functions. + static void addBookmark(const BookmarkStruct *bookmarkStructPointer); + static void addDatabase(); + static void addFolder(const BookmarkStruct *bookmarkStructPointer); + static void deleteBookmark(const int databaseId); + static void deleteBookmarks(const QString url); + static QList* getAllFolderUrls(const double folderId); + static BookmarkStruct* getBookmark(const int databaseId); + static std::list* getBookmarks(); + static QList* getBookmarksInFolderExcept(const double folderId, QList *exceptDatabaseIdsListPointer); + static QList* getFolderContentsDatabaseIds(const double folderId); + static QList* getFolderContentsDatabaseIdsRecursively(const double folderId); + static QList* getFolderContents(const double folderId); + static int getFolderDatabaseId(const double folderId); + static double getFolderId(const int databaseId); + static int getFolderItemCount(const double folderId); + static double getParentFolderId(const int databaseId); + static QList* getSubfolders(const double folderId); + static bool isBookmarked(const QString url); + static bool isFolder(const int databaseId); + static void updateBookmark(const BookmarkStruct *bookmarkStructPointer); + static void updateBookmarkName(const int databaseId, const QString &bookmarkName); + static void updateBookmarkUrl(const int databaseId, const QString &bookmarkUrl); + static void updateDisplayOrder(const int databaseId, const int displayOrder); + static void updateFolderContentsDisplayOrder(const double folderId); + static void updateParentFolderAndDisplayOrder(const int databaseId, const double parentFolderId, const int displayOrder); + + // The public constants. + static const QString CONNECTION_NAME; + static const QString BOOKMARK_NAME; + static const QString BOOKMARKS_TABLE; + static const QString BOOKMARK_URL; + static const QString DISPLAY_ORDER; + static const QString FAVORITE_ICON; + static const QString FOLDER_ID; + static const QString ID; + static const QString IS_FOLDER; + static const QString PARENT_FOLDER_ID; + +private: + // The private static constants. + static const int SCHEMA_VERSION; + + // The private functions. + static double generateFolderId(); + static QString getFavoriteIconBase64String(const QIcon &favoriteIcon); +}; +#endif diff --git a/src/databases/CMakeLists.txt b/src/databases/CMakeLists.txt index 0abb936..5f40f99 100644 --- a/src/databases/CMakeLists.txt +++ b/src/databases/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright © 2022 Soren Stoutner . +# Copyright 2022-2023 Soren Stoutner . # # This file is part of Privacy Browser PC . # @@ -17,7 +17,8 @@ # List the sources to include in the executable. -target_sources(privacy-browser PRIVATE +target_sources(privacybrowser PRIVATE + BookmarksDatabase.cpp CookiesDatabase.cpp DomainsDatabase.cpp ) diff --git a/src/databases/CookiesDatabase.cpp b/src/databases/CookiesDatabase.cpp index 207f45b..e8a870b 100644 --- a/src/databases/CookiesDatabase.cpp +++ b/src/databases/CookiesDatabase.cpp @@ -1,5 +1,5 @@ /* - * Copyright © 2022 Soren Stoutner . + * Copyright 2022-2023 Soren Stoutner . * * This file is part of Privacy Browser PC . * @@ -23,17 +23,15 @@ // Define the private static schema constants. const int CookiesDatabase::SCHEMA_VERSION = 0; -// Define the public static database constants. +// Define the public static constants. const QString CookiesDatabase::CONNECTION_NAME = "cookies_database"; const QString CookiesDatabase::COOKIES_TABLE = "cookies"; - -// Define the public static database field names. -const QString CookiesDatabase::_ID = "_id"; const QString CookiesDatabase::DOMAIN = "domain"; -const QString CookiesDatabase::NAME = "name"; -const QString CookiesDatabase::PATH = "path"; const QString CookiesDatabase::EXPIRATION_DATE = "expiration_date"; const QString CookiesDatabase::HTTP_ONLY = "http_only"; +const QString CookiesDatabase::ID = "_id"; +const QString CookiesDatabase::NAME = "name"; +const QString CookiesDatabase::PATH = "path"; const QString CookiesDatabase::SECURE = "secure"; const QString CookiesDatabase::VALUE = "value"; @@ -83,15 +81,14 @@ void CookiesDatabase::addDatabase() // Prepare the create table query. createTableQuery.prepare("CREATE TABLE " + COOKIES_TABLE + "(" + - _ID + " INTEGER PRIMARY KEY, " + - DOMAIN + " TEXT NOT NULL, " + - NAME + " TEXT NOT NULL, " + - PATH + " TEXT NOT NULL, " + - EXPIRATION_DATE + " TEXT, " + - HTTP_ONLY + " INTEGER NOT NULL DEFAULT 0, " + - SECURE + " INTEGER NOT NULL DEFAULT 0, " + - VALUE + " TEXT NOT NULL)" - ); + ID + " INTEGER PRIMARY KEY, " + + DOMAIN + " TEXT NOT NULL, " + + NAME + " TEXT NOT NULL, " + + PATH + " TEXT NOT NULL, " + + EXPIRATION_DATE + " TEXT, " + + HTTP_ONLY + " INTEGER NOT NULL DEFAULT 0, " + + SECURE + " INTEGER NOT NULL DEFAULT 0, " + + VALUE + " TEXT NOT NULL)"); // Execute the query. if (!createTableQuery.exec()) @@ -128,8 +125,15 @@ void CookiesDatabase::addCookie(const QNetworkCookie &cookie) QSqlQuery addCookieQuery(cookiesDatabase); // Prepare the add cookie query. - addCookieQuery.prepare("INSERT INTO " + COOKIES_TABLE + " (" + DOMAIN + ", " + NAME + ", " + PATH + ", " + EXPIRATION_DATE + ", " + HTTP_ONLY + ", " + SECURE + ", " + VALUE + ") " - "VALUES (:domain, :name, :path, :expiration_date, :http_only, :secure, :value)" + addCookieQuery.prepare("INSERT INTO " + COOKIES_TABLE + " (" + + DOMAIN + ", " + + NAME + ", " + + PATH + ", " + + EXPIRATION_DATE + ", " + + HTTP_ONLY + ", " + + SECURE + ", " + + VALUE + ") " + "VALUES (:domain, :name, :path, :expiration_date, :http_only, :secure, :value)" ); // Bind the values. @@ -158,7 +162,7 @@ int CookiesDatabase::cookieCount() countCookiesQuery.setForwardOnly(true); // Prepare the query. - countCookiesQuery.prepare("SELECT " + _ID + " FROM " + COOKIES_TABLE); + countCookiesQuery.prepare("SELECT " + ID + " FROM " + COOKIES_TABLE); // Execute the query. countCookiesQuery.exec(); @@ -204,7 +208,10 @@ void CookiesDatabase::deleteCookie(const QNetworkCookie &cookie) QSqlQuery deleteCookieQuery(cookiesDatabase); // Prepare the delete cookie query. - deleteCookieQuery.prepare("DELETE FROM " + COOKIES_TABLE + " WHERE " + DOMAIN + " = :domain AND " + NAME + " = :name AND " + PATH + " = :path"); + deleteCookieQuery.prepare("DELETE FROM " + COOKIES_TABLE + " WHERE " + + DOMAIN + " = :domain AND " + + NAME + " = :name AND " + + PATH + " = :path"); // Bind the values. deleteCookieQuery.bindValue(":domain", cookie.domain()); @@ -223,7 +230,7 @@ QList* CookiesDatabase::getCookies() // Instantiate a cookies query. QSqlQuery cookiesQuery(cookiesDatabase); - // Set the query to be forward only. + // Set the query to be forward only, which is more performant. cookiesQuery.setForwardOnly(true); // Prepare the cookies query. @@ -270,7 +277,7 @@ QNetworkCookie* CookiesDatabase::getCookieById(const int &id) cookieQuery.setForwardOnly(true); // Prepare the cookies query. - cookieQuery.prepare("SELECT * FROM " + COOKIES_TABLE + " WHERE " + _ID + " = :id"); + cookieQuery.prepare("SELECT * FROM " + COOKIES_TABLE + " WHERE " + ID + " = :id"); // Bind the values. cookieQuery.bindValue(":id", id); @@ -309,7 +316,10 @@ bool CookiesDatabase::isDurable(const QNetworkCookie &cookie) isDurableQuery.setForwardOnly(true); // Prepare the is durable query. - isDurableQuery.prepare("SELECT " + _ID + " FROM " + COOKIES_TABLE + " WHERE " + DOMAIN + " = :domain AND " + NAME + " = :name AND " + PATH + " = :path"); + isDurableQuery.prepare("SELECT " + ID + " FROM " + COOKIES_TABLE + " WHERE " + + DOMAIN + " = :domain AND " + + NAME + " = :name AND " + + PATH + " = :path"); // Bind the values. isDurableQuery.bindValue(":domain", cookie.domain()); @@ -335,8 +345,14 @@ bool CookiesDatabase::isUpdate(const QNetworkCookie &cookie) QSqlQuery isUpdateQuery(cookiesDatabase); // Prepare the is update query. - isUpdateQuery.prepare("SELECT " + EXPIRATION_DATE + " , " + HTTP_ONLY + " , " + SECURE + " , " + VALUE + " FROM " + COOKIES_TABLE + " WHERE " + DOMAIN + " = :domain AND " + - NAME + " = :name AND " + PATH + " = :path"); + isUpdateQuery.prepare("SELECT " + EXPIRATION_DATE + " , " + + HTTP_ONLY + " , " + + SECURE + " , " + + VALUE + + " FROM " + COOKIES_TABLE + + " WHERE " + DOMAIN + " = :domain AND " + + NAME + " = :name AND " + + PATH + " = :path"); // Bind the values. isUpdateQuery.bindValue(":domain", cookie.domain()); @@ -353,10 +369,10 @@ bool CookiesDatabase::isUpdate(const QNetworkCookie &cookie) if (isUpdateQuery.isValid()) // The cookie exists in the database. { // Check to see if the cookie data has changed. - if ((QDateTime::fromString(isUpdateQuery.value(0).toString(), Qt::ISODate) != cookie.expirationDate()) || - (isUpdateQuery.value(1).toBool() != cookie.isHttpOnly()) || - (isUpdateQuery.value(2).toBool() != cookie.isSecure()) || - (isUpdateQuery.value(3).toString().toUtf8() != cookie.value())) // The cookies data has changed. + if ((QDateTime::fromString(isUpdateQuery.value(EXPIRATION_DATE).toString(), Qt::ISODate) != cookie.expirationDate()) || + (isUpdateQuery.value(HTTP_ONLY).toBool() != cookie.isHttpOnly()) || + (isUpdateQuery.value(SECURE).toBool() != cookie.isSecure()) || + (isUpdateQuery.value(VALUE).toString().toUtf8() != cookie.value())) // The cookies data has changed. { //qDebug() << "The durable cookie data has changed."; @@ -386,9 +402,15 @@ void CookiesDatabase::updateCookie(const QNetworkCookie &cookie) // Create the update cookie query. QSqlQuery updateCookieQuery(cookiesDatabase); - // Prepare the edit cookie query. - updateCookieQuery.prepare("UPDATE " + COOKIES_TABLE + " SET " + EXPIRATION_DATE + " = :expiration_date , " + HTTP_ONLY + " = :http_only , " + SECURE + " = :secure , " + - VALUE + " = :value WHERE " + DOMAIN + " = :domain AND " + NAME + " = :name AND " + PATH + " = :path"); + // Prepare the update cookie query. + updateCookieQuery.prepare("UPDATE " + COOKIES_TABLE + + " SET " + EXPIRATION_DATE + " = :expiration_date , " + + HTTP_ONLY + " = :http_only , " + + SECURE + " = :secure , " + + VALUE + " = :value " + + "WHERE " + DOMAIN + " = :domain AND " + + NAME + " = :name AND " + + PATH + " = :path"); // Bind the values. updateCookieQuery.bindValue(":domain", cookie.domain()); @@ -415,7 +437,10 @@ void CookiesDatabase::updateCookie(const QNetworkCookie &oldCookie, const QNetwo oldCookieQuery.setForwardOnly(true); // Prepare the old cookie query. - oldCookieQuery.prepare("SELECT " + _ID + " FROM " + COOKIES_TABLE + " WHERE " + DOMAIN + " = :domain AND " + NAME + " = :name AND " + PATH + " = :path"); + oldCookieQuery.prepare("SELECT " + ID + " FROM " + COOKIES_TABLE + + " WHERE " + DOMAIN + " = :domain AND " + + NAME + " = :name AND " + + PATH + " = :path"); // Bind the values. oldCookieQuery.bindValue(":domain", oldCookie.domain()); @@ -432,8 +457,15 @@ void CookiesDatabase::updateCookie(const QNetworkCookie &oldCookie, const QNetwo QSqlQuery updateCookieQuery(cookiesDatabase); // Prepare the update cookie query. - updateCookieQuery.prepare("UPDATE " + COOKIES_TABLE + " SET " + DOMAIN + " = :domain , " + NAME + " = :name , " + PATH + " = :path , " + EXPIRATION_DATE + " = :expiration_date , " + - HTTP_ONLY + " = :http_only , " + SECURE + " = :secure , " + VALUE + " = :value WHERE " + _ID + " = :id"); + updateCookieQuery.prepare("UPDATE " + COOKIES_TABLE + + " SET " + DOMAIN + " = :domain , " + + NAME + " = :name , " + + PATH + " = :path , " + + EXPIRATION_DATE + " = :expiration_date , " + + HTTP_ONLY + " = :http_only , " + + SECURE + " = :secure , " + + VALUE + " = :value " + + "WHERE " + ID + " = :id"); // Bind the values. updateCookieQuery.bindValue(":id", oldCookieQuery.value(0).toLongLong()); diff --git a/src/databases/CookiesDatabase.h b/src/databases/CookiesDatabase.h index 76e788c..b81557b 100644 --- a/src/databases/CookiesDatabase.h +++ b/src/databases/CookiesDatabase.h @@ -1,5 +1,5 @@ /* - * Copyright © 2022 Soren Stoutner . + * Copyright 2022-2023 Soren Stoutner . * * This file is part of Privacy Browser PC . * @@ -44,12 +44,12 @@ public: static void updateCookie(const QNetworkCookie &oldCookie, const QNetworkCookie &newCookie); // The public constants. - static const QString _ID; static const QString CONNECTION_NAME; static const QString COOKIES_TABLE; static const QString DOMAIN; static const QString EXPIRATION_DATE; static const QString HTTP_ONLY; + static const QString ID; static const QString NAME; static const QString PATH; static const QString SECURE; diff --git a/src/databases/DomainsDatabase.cpp b/src/databases/DomainsDatabase.cpp index 6dfc1bd..d52647c 100644 --- a/src/databases/DomainsDatabase.cpp +++ b/src/databases/DomainsDatabase.cpp @@ -1,5 +1,5 @@ /* - * Copyright © 2022 Soren Stoutner . + * Copyright 2022-2024 Soren Stoutner . * * This file is part of Privacy Browser PC . * @@ -19,24 +19,23 @@ // Application headers. #include "DomainsDatabase.h" +#include "Settings.h" #include "helpers/UserAgentHelper.h" // Define the private static schema constants. -const int DomainsDatabase::SCHEMA_VERSION = 5; +const int DomainsDatabase::SCHEMA_VERSION = 6; -// Define the public static database constants. +// Define the public static constants. const QString DomainsDatabase::CONNECTION_NAME = "domains_database"; -const QString DomainsDatabase::DOMAINS_TABLE = "domains"; - -// Define the public static database field names. -const QString DomainsDatabase::_ID = "_id"; +const QString DomainsDatabase::CUSTOM_ZOOM_FACTOR = "custom_zoom_factor"; +const QString DomainsDatabase::DOM_STORAGE = "dom_storage"; const QString DomainsDatabase::DOMAIN_NAME = "domain_name"; +const QString DomainsDatabase::DOMAINS_TABLE = "domains"; +const QString DomainsDatabase::ID = "_id"; const QString DomainsDatabase::JAVASCRIPT = "javascript"; const QString DomainsDatabase::LOCAL_STORAGE = "local_storage"; -const QString DomainsDatabase::DOM_STORAGE = "dom_storage"; const QString DomainsDatabase::USER_AGENT = "user_agent"; const QString DomainsDatabase::ZOOM_FACTOR = "zoom_factor"; -const QString DomainsDatabase::CUSTOM_ZOOM_FACTOR = "custom_zoom_factor"; // Construct the class. DomainsDatabase::DomainsDatabase() {} @@ -76,7 +75,7 @@ void DomainsDatabase::addDatabase() // Add the JavaScript column. domainsDatabase.exec("ALTER TABLE " + DOMAINS_TABLE + " ADD COLUMN " + JAVASCRIPT + " INTEGER DEFAULT 0"); - // Fallthrough to the next case. + // Fall through to the next case. [[fallthrough]]; } @@ -86,7 +85,7 @@ void DomainsDatabase::addDatabase() // Add the User Agent column. domainsDatabase.exec("ALTER TABLE " + DOMAINS_TABLE + " ADD COLUMN " + USER_AGENT + " TEXT DEFAULT '" + UserAgentHelper::SYSTEM_DEFAULT_DATABASE + "'"); - // Fallthrough to the next case. + // Fall through to the next case. [[fallthrough]]; } @@ -97,7 +96,7 @@ void DomainsDatabase::addDatabase() domainsDatabase.exec("ALTER TABLE " + DOMAINS_TABLE + " ADD COLUMN " + ZOOM_FACTOR + " INTEGER DEFAULT 0"); domainsDatabase.exec("ALTER TABLE " + DOMAINS_TABLE + " ADD COLUMN " + CUSTOM_ZOOM_FACTOR + " REAL DEFAULT 1.0"); - // Fallthrough to the next case. + // Fall through to the next case. [[fallthrough]]; } @@ -107,7 +106,7 @@ void DomainsDatabase::addDatabase() // Add the DOM Storage column. domainsDatabase.exec("ALTER TABLE " + DOMAINS_TABLE + " ADD COLUMN " + DOM_STORAGE + " INTEGER DEFAULT 0"); - // Fallthrough to the next case. + // Fall through to the next case. [[fallthrough]]; } @@ -117,7 +116,120 @@ void DomainsDatabase::addDatabase() // Add the Local Storage column. domainsDatabase.exec("ALTER TABLE " + DOMAINS_TABLE + " ADD COLUMN " + LOCAL_STORAGE + " INTEGER DEFAULT 0"); - // Fallthrough to the next case. + // Fall through to the next case. + [[fallthrough]]; + } + + // Upgrade from schema version 5 to schema version 6. + case 5: + { + // Instantiate a spinner query. + QSqlQuery spinnerQuery(domainsDatabase); + + // Set the query to be forward only (increases performance while iterating over the query). + spinnerQuery.setForwardOnly(true); + + // Prepare the query. + spinnerQuery.prepare("SELECT " + ID + "," + JAVASCRIPT + "," + LOCAL_STORAGE + "," + DOM_STORAGE + " FROM " + DOMAINS_TABLE); + + // Execute the query. + spinnerQuery.exec(); + + // Update the spinner values so that enabled is 1 and disabled is 2. + while (spinnerQuery.next()) + { + // Initialize the new spinner values. + int newJavaScriptValue = SYSTEM_DEFAULT; + int newLocalStorageValue = SYSTEM_DEFAULT; + int newDomStorageValue = SYSTEM_DEFAULT; + + // Update the new JavaScript value if needed. + switch (spinnerQuery.value(JAVASCRIPT).toInt()) + { + // Disabled used to be 1. + case 1: + { + // Update the value to be 2. + newJavaScriptValue = DISABLED; + + break; + } + + // Enabled used to be 2. + case 2: + { + // Update the new value to be 1. + newJavaScriptValue = ENABLED; + + break; + } + } + + // Update the new local storage value if needed. + switch (spinnerQuery.value(LOCAL_STORAGE).toInt()) + { + // Disabled used to be 1. + case 1: + { + // Update the new value to be 2. + newLocalStorageValue = DISABLED; + + break; + } + + // Enabled used to be 2. + case 2: + { + // Update the new value to be 1. + newLocalStorageValue = ENABLED; + + break; + } + } + + // Update the new DOM storage value if needed. + switch (spinnerQuery.value(DOM_STORAGE).toInt()) + { + // Disabled used to be 1. + case 1: + { + // Update the new value to be 2. + newDomStorageValue = DISABLED; + + break; + } + + // Enabled used to be 2. + case 2: + { + // Update the new value to be 1. + newDomStorageValue = ENABLED; + + break; + } + } + + // Create an update spinner query. + QSqlQuery updateSpinnerQuery(domainsDatabase); + + // Prepare the update spinner query. + updateSpinnerQuery.prepare("UPDATE " + DOMAINS_TABLE + " SET " + + JAVASCRIPT + " = :javascript , " + + LOCAL_STORAGE + " = :local_storage , " + + DOM_STORAGE + " = :dom_storage " + + " WHERE " + ID + " = :id"); + + // Bind the values. + updateSpinnerQuery.bindValue(":javascript", newJavaScriptValue); + updateSpinnerQuery.bindValue(":local_storage", newLocalStorageValue); + updateSpinnerQuery.bindValue(":dom_storage", newDomStorageValue); + updateSpinnerQuery.bindValue(":id", spinnerQuery.value(ID)); + + // Execute the query. + updateSpinnerQuery.exec(); + } + + // Fall through to the next case. // [[fallthrough]]; } } @@ -133,15 +245,14 @@ void DomainsDatabase::addDatabase() // Prepare the create table query. createTableQuery.prepare("CREATE TABLE " + DOMAINS_TABLE + "(" + - _ID + " INTEGER PRIMARY KEY, " + - DOMAIN_NAME + " TEXT, " + - JAVASCRIPT + " INTEGER DEFAULT 0, " + - LOCAL_STORAGE + " INTEGER DEFAULT 0, " + - DOM_STORAGE + " INTEGER DEFAULT 0, " + - USER_AGENT + " TEXT DEFAULT '" + UserAgentHelper::SYSTEM_DEFAULT_DATABASE + "', " + - ZOOM_FACTOR + " INTEGER DEFAULT 0, " + - CUSTOM_ZOOM_FACTOR + " REAL DEFAULT 1.0)" - ); + ID + " INTEGER PRIMARY KEY, " + + DOMAIN_NAME + " TEXT, " + + JAVASCRIPT + " INTEGER DEFAULT 0, " + + LOCAL_STORAGE + " INTEGER DEFAULT 0, " + + DOM_STORAGE + " INTEGER DEFAULT 0, " + + USER_AGENT + " TEXT DEFAULT '" + UserAgentHelper::SYSTEM_DEFAULT_DATABASE + "', " + + ZOOM_FACTOR + " INTEGER DEFAULT 0, " + + CUSTOM_ZOOM_FACTOR + " REAL DEFAULT 1.0)"); // Execute the query. if (!createTableQuery.exec()) @@ -156,11 +267,51 @@ void DomainsDatabase::addDatabase() } else // Opening the database failed. { - // Write the last database error message to the debug output. + // Write the last database error message to the debug output.Settings::zoom qDebug().noquote().nospace() << "Error opening database: " << domainsDatabase.lastError(); } }; +void DomainsDatabase::addDomain(const QString &domainName) +{ + // Add the domain: + addDomain(domainName, SYSTEM_DEFAULT, SYSTEM_DEFAULT, SYSTEM_DEFAULT, UserAgentHelper::SYSTEM_DEFAULT_DATABASE, SYSTEM_DEFAULT, Settings::zoomFactor()); +} + +void DomainsDatabase::addDomain(const QString &domainName, const int javaScriptInt, const int localStorageInt, const int domStorageInt, const QString &userAgentDatabaseString, + const int zoomFactorInt, const double currentZoomFactorDouble) +{ + // Get a handle for the domains database. + QSqlDatabase domainsDatabase = QSqlDatabase::database(CONNECTION_NAME); + + // Instantiate an add domain settings query. + QSqlQuery addDomainSettingsQuery(domainsDatabase); + + // Prepare the query. + addDomainSettingsQuery.prepare("INSERT INTO " + DomainsDatabase::DOMAINS_TABLE + " (" + + DomainsDatabase::DOMAIN_NAME + ", " + + DomainsDatabase::JAVASCRIPT + ", " + + DomainsDatabase::LOCAL_STORAGE + ", " + + DomainsDatabase::DOM_STORAGE + ", " + + DomainsDatabase::USER_AGENT + ", " + + DomainsDatabase::ZOOM_FACTOR + ", " + + DomainsDatabase::CUSTOM_ZOOM_FACTOR + ") " + + "VALUES (:domain_name, :javascript, :local_storage, :dom_storage, :user_agent, :zoom_factor, :custom_zoom_factor)" + ); + + // Bind the query values. + addDomainSettingsQuery.bindValue(":domain_name", domainName); + addDomainSettingsQuery.bindValue(":javascript", javaScriptInt); + addDomainSettingsQuery.bindValue(":local_storage", localStorageInt); + addDomainSettingsQuery.bindValue(":dom_storage", domStorageInt); + addDomainSettingsQuery.bindValue(":user_agent", userAgentDatabaseString); + addDomainSettingsQuery.bindValue(":zoom_factor", zoomFactorInt); + addDomainSettingsQuery.bindValue(":custom_zoom_factor", currentZoomFactorDouble); + + // Execute the query. + addDomainSettingsQuery.exec(); +} + QSqlQuery DomainsDatabase::getDomainQuery(const QString &hostname) { // Get a handle for the domains database. @@ -173,7 +324,7 @@ QSqlQuery DomainsDatabase::getDomainQuery(const QString &hostname) allDomainNamesQuery.setForwardOnly(true); // Prepare the query. - allDomainNamesQuery.prepare("SELECT " + _ID + "," + DOMAIN_NAME + " FROM " + DOMAINS_TABLE); + allDomainNamesQuery.prepare("SELECT " + ID + "," + DOMAIN_NAME + " FROM " + DOMAINS_TABLE); // Execute the query. allDomainNamesQuery.exec(); @@ -185,7 +336,7 @@ QSqlQuery DomainsDatabase::getDomainQuery(const QString &hostname) while (allDomainNamesQuery.next()) { // Add the domain name and database ID to the map. - domainSettingsMap.insert(allDomainNamesQuery.record().field(DOMAIN_NAME).value().toString(), allDomainNamesQuery.record().field(_ID).value().toInt()); + domainSettingsMap.insert(allDomainNamesQuery.value(DOMAIN_NAME).toString(), allDomainNamesQuery.value(ID).toInt()); } // Initialize the database ID tracker. @@ -218,7 +369,7 @@ QSqlQuery DomainsDatabase::getDomainQuery(const QString &hostname) QSqlQuery domainLookupQuery(domainsDatabase); // Prepare the domain lookup query. - domainLookupQuery.prepare("SELECT * FROM " + DOMAINS_TABLE + " WHERE " + _ID + " = " + QString::number(databaseId)); + domainLookupQuery.prepare("SELECT * FROM " + DOMAINS_TABLE + " WHERE " + ID + " = " + QString::number(databaseId)); // Execute the query. domainLookupQuery.exec(); diff --git a/src/databases/DomainsDatabase.h b/src/databases/DomainsDatabase.h index a75b831..1e58d2e 100644 --- a/src/databases/DomainsDatabase.h +++ b/src/databases/DomainsDatabase.h @@ -1,5 +1,5 @@ /* - * Copyright © 2022 Soren Stoutner . + * Copyright 2022-2024 Soren Stoutner . * * This file is part of Privacy Browser PC . * @@ -31,21 +31,24 @@ public: // The public functions. static void addDatabase(); + static void addDomain(const QString &domainName); + static void addDomain(const QString &domainName, const int javaScriptInt, const int localStorageInt, const int domStorageInt, const QString &userAgentDatabaseString, const int zoomFactorInt, + const double currentZoomFactorDouble); static QSqlQuery getDomainQuery(const QString &hostname); // The public int constants. static const int SYSTEM_DEFAULT = 0; - static const int DISABLED = 1; - static const int ENABLED = 2; + static const int ENABLED = 1; + static const int DISABLED = 2; static const int CUSTOM = 1; // The public constants. - static const QString _ID; static const QString CONNECTION_NAME; static const QString CUSTOM_ZOOM_FACTOR; static const QString DOM_STORAGE; static const QString DOMAIN_NAME; static const QString DOMAINS_TABLE; + static const QString ID; static const QString JAVASCRIPT; static const QString LOCAL_STORAGE; static const QString USER_AGENT; diff --git a/src/delegates/CMakeLists.txt b/src/delegates/CMakeLists.txt index d56c050..d26efe7 100644 --- a/src/delegates/CMakeLists.txt +++ b/src/delegates/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright © 2022 Soren Stoutner . +# Copyright 2022-2023 Soren Stoutner . # # This file is part of Privacy Browser PC . # @@ -17,6 +17,6 @@ # List the sources to include in the executable. -target_sources(privacy-browser PRIVATE +target_sources(privacybrowser PRIVATE ViewOnlyDelegate.cpp ) diff --git a/src/dialogs/AddBookmarkDialog.cpp b/src/dialogs/AddBookmarkDialog.cpp new file mode 100644 index 0000000..041ca3d --- /dev/null +++ b/src/dialogs/AddBookmarkDialog.cpp @@ -0,0 +1,183 @@ +/* + * Copyright 2023-2024 Soren Stoutner . + * + * This file is part of Privacy Browser PC . + * + * Privacy Browser PC 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. + * + * Privacy Browser PC 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 Privacy Browser PC. If not, see . + */ + +// Application headers. +#include "AddBookmarkDialog.h" +#include "ui_AddBookmarkDialog.h" +#include "databases/BookmarksDatabase.h" + +// KDE Framework headers. +#include + +// Qt toolkit headers. +#include +#include + +// Construct the class. +AddBookmarkDialog::AddBookmarkDialog(QWidget *parentWidgetPointer, const QString &bookmarkName, const QString &bookmarkUrl, const QIcon &favoriteIcon, const double parentFolderId) : + QDialog(parentWidgetPointer) +{ + // Set the window title. + setWindowTitle(i18nc("The add bookmark dialog window title.", "Add Bookmark")); + + // Set the window modality. + setWindowModality(Qt::WindowModality::ApplicationModal); + + // Instantiate the add bookmark dialog UI. + Ui::AddBookmarkDialog addBookmarkDialogUi; + + // Setup the UI. + addBookmarkDialogUi.setupUi(this); + + // Get handles for the widgets. + websiteFavoriteIconRadioButtonPointer = addBookmarkDialogUi.websiteFavoriteIconRadioButton; + customFavoriteIconRadioButtonPointer = addBookmarkDialogUi.customFavoriteIconRadioButton; + parentFolderTreeWidgetPointer = addBookmarkDialogUi.parentFolderTreeWidget; + bookmarkNameLineEditPointer = addBookmarkDialogUi.bookmarkNameLineEdit; + bookmarkUrlLineEditPointer = addBookmarkDialogUi.bookmarkUrlLineEdit; + QPushButton *browseButtonPointer = addBookmarkDialogUi.browseButton; + QDialogButtonBox *dialogButtonBoxPointer = addBookmarkDialogUi.dialogButtonBox; + + // Set the icons. + websiteFavoriteIconRadioButtonPointer->setIcon(favoriteIcon); + customFavoriteIconRadioButtonPointer->setIcon(QIcon::fromTheme(QLatin1String("globe"), QIcon::fromTheme(QLatin1String("applications-internet")))); + + // Instantiate a folder helper. + folderHelperPointer = new FolderHelper(); + + // Set the parent folder tree widget column count. + parentFolderTreeWidgetPointer->setColumnCount(2); + + // Hide the second column. + parentFolderTreeWidgetPointer->hideColumn(folderHelperPointer->FOLDER_ID_COLUMN); + + // Set the column header. + parentFolderTreeWidgetPointer->setHeaderLabel(i18nc("The folder tree widget header", "Select Parent Folder")); + + // Create a bookmarks tree widget item. + QTreeWidgetItem *bookmarksTreeWidgetItemPointer = new QTreeWidgetItem(); + + // Populate the bookmarks tree widget item. + bookmarksTreeWidgetItemPointer->setText(folderHelperPointer->FOLDER_NAME_COLUMN, i18nc("The bookmarks root tree widget name", "Bookmarks")); + bookmarksTreeWidgetItemPointer->setIcon(folderHelperPointer->FOLDER_NAME_COLUMN, QIcon::fromTheme(QLatin1String("bookmarks"), QIcon::fromTheme(QLatin1String("bookmark-new")))); + bookmarksTreeWidgetItemPointer->setText(folderHelperPointer->FOLDER_ID_COLUMN, QLatin1String("0")); + + // Add the bookmarks tree widget item to the root of the tree. + parentFolderTreeWidgetPointer->addTopLevelItem(bookmarksTreeWidgetItemPointer); + + // Select the root bookmarks folder if it is the initial parent folder. + if (parentFolderId == 0) + bookmarksTreeWidgetItemPointer->setSelected(true); + + // Populate the subfolders. + folderHelperPointer->populateSubfolders(bookmarksTreeWidgetItemPointer, parentFolderId); + + // Open all the folders. + parentFolderTreeWidgetPointer->expandAll(); + + // Populate the line edits. + bookmarkNameLineEditPointer->setText(bookmarkName); + bookmarkUrlLineEditPointer->setText(bookmarkUrl); + + // Scroll to the beginning of the bookmark URL line edit. + bookmarkUrlLineEditPointer->setCursorPosition(0); + + // Focus the bookmark name line edit. + bookmarkNameLineEditPointer->setFocus(); + + // Add buttons to the dialog button box. + addButtonPointer = dialogButtonBoxPointer->addButton(i18nc("The add bookmark button", "Add"), QDialogButtonBox::AcceptRole); + + // Set the button icons. + addButtonPointer->setIcon(QIcon::fromTheme("list-add")); + + // Connect the buttons. + connect(browseButtonPointer, SIGNAL(clicked()), this, SLOT(browse())); + connect(dialogButtonBoxPointer, SIGNAL(accepted()), this, SLOT(addBookmark())); + connect(dialogButtonBoxPointer, SIGNAL(rejected()), this, SLOT(reject())); + + // Update the UI when the line edits change. + connect(bookmarkNameLineEditPointer, SIGNAL(textEdited(const QString&)), this, SLOT(updateUi())); + connect(bookmarkUrlLineEditPointer, SIGNAL(textEdited(const QString&)), this, SLOT(updateUi())); + + // Set the initial UI status. + updateUi(); +} + +void AddBookmarkDialog::addBookmark() +{ + // Get the selected folders list. + QList selectedFoldersList = parentFolderTreeWidgetPointer->selectedItems(); + + // Get the selected folder. + QTreeWidgetItem *selectedFolderPointer = selectedFoldersList.first(); + + // Get the favorite icon. + QIcon favoriteIcon = websiteFavoriteIconRadioButtonPointer->isChecked() ? websiteFavoriteIconRadioButtonPointer->icon() : customFavoriteIconRadioButtonPointer->icon(); + + // Create a bookmark struct. + BookmarkStruct *bookmarkStructPointer = new BookmarkStruct; + + // Populate the bookmark struct. + bookmarkStructPointer->name = bookmarkNameLineEditPointer->text(); + bookmarkStructPointer->url = bookmarkUrlLineEditPointer->text(); + bookmarkStructPointer->parentFolderId = selectedFolderPointer->text(folderHelperPointer->FOLDER_ID_COLUMN).toDouble(); + bookmarkStructPointer->favoriteIcon = favoriteIcon; + + // Add the bookmark. + BookmarksDatabase::addBookmark(bookmarkStructPointer); + + // Update the list of bookmarks in the menu and toolbar. + emit bookmarkAdded(); + + // Close the dialog. + close(); +} + +void AddBookmarkDialog::browse() +{ + // Get an image file string from the user. + QString imageFileString = QFileDialog::getOpenFileName(this, tr("Favorite Icon Image"), QDir::homePath(), + tr("Image Files — *.bmp, *.gif, *.jpg, *.jpeg, *.png, *.svg (*.bmp *.gif *.jpg *.jpeg *.png *.svg);;All Files (*)")); + + // Check to see if an image file string was returned. This will be empty if the user selected cancel. + if (!imageFileString.isEmpty()) + { + // Set the custom favorite icon. + customFavoriteIconRadioButtonPointer->setIcon(QIcon(imageFileString)); + + // Check the custom favorite icon radio button. + customFavoriteIconRadioButtonPointer->setChecked(true); + } +} + +void AddBookmarkDialog::updateUi() +{ + // Update the add button status + if (bookmarkNameLineEditPointer->text().isEmpty() || bookmarkUrlLineEditPointer->text().isEmpty()) // At least one of the line edits is empty. + { + // Disable the add button. + addButtonPointer->setEnabled(false); + } + else // Both of the line edits are populated. + { + // Enable the add button. + addButtonPointer->setEnabled(true); + } +} diff --git a/src/dialogs/AddBookmarkDialog.h b/src/dialogs/AddBookmarkDialog.h new file mode 100644 index 0000000..7d38628 --- /dev/null +++ b/src/dialogs/AddBookmarkDialog.h @@ -0,0 +1,63 @@ +/* + * Copyright 2023-2024 Soren Stoutner . + * + * This file is part of Privacy Browser PC . + * + * Privacy Browser PC 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. + * + * Privacy Browser PC 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 Privacy Browser PC. If not, see . + */ + +#ifndef ADDBOOKMARKDIALOG_H +#define ADDBOOKMARKDIALOG_H + +// Application headers. +#include "helpers/FolderHelper.h" + +// Qt toolkit headers. +#include +#include +#include +#include + +class AddBookmarkDialog : public QDialog +{ + // Include the Q_OBJECT macro. + Q_OBJECT + +public: + // The primary constructor. + explicit AddBookmarkDialog(QWidget *parentWidgetPointer, const QString &bookmarkName, const QString &bookmarkUrl, const QIcon &favoriteIcon, const double parentFolderId); + +signals: + // The signals. + void bookmarkAdded() const; + +private Q_SLOTS: + // The private slots. + void addBookmark(); + void browse(); + void updateUi(); + +private: + // The private variables. + FolderHelper *folderHelperPointer; + + // The private widgets. + QPushButton *addButtonPointer; + QLineEdit *bookmarkNameLineEditPointer; + QLineEdit *bookmarkUrlLineEditPointer; + QRadioButton *customFavoriteIconRadioButtonPointer; + QTreeWidget *parentFolderTreeWidgetPointer; + QRadioButton *websiteFavoriteIconRadioButtonPointer; +}; +#endif diff --git a/src/dialogs/AddFolderDialog.cpp b/src/dialogs/AddFolderDialog.cpp new file mode 100644 index 0000000..7462930 --- /dev/null +++ b/src/dialogs/AddFolderDialog.cpp @@ -0,0 +1,169 @@ +/* + * Copyright 2023-2024 Soren Stoutner . + * + * This file is part of Privacy Browser PC . + * + * Privacy Browser PC 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. + * + * Privacy Browser PC 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 Privacy Browser PC. If not, see . + */ + +// Application headers. +#include "AddFolderDialog.h" +#include "ui_AddFolderDialog.h" +#include "databases/BookmarksDatabase.h" +#include "helpers/FolderHelper.h" +#include "structs/BookmarkStruct.h" + +// Qt toolkit headers. +#include + +// Construct the class. +AddFolderDialog::AddFolderDialog(QWidget *parentWidgetPointer, const QIcon ¤tWebsiteFavoriteIcon, const double parentFolderId) : QDialog(parentWidgetPointer) +{ + // Set the window title. + setWindowTitle(i18nc("The add folder dialog window title.", "Add Folder")); + + // Set the window modality. + setWindowModality(Qt::WindowModality::ApplicationModal); + + // Instantiate the add folder dialog UI. + Ui::AddFolderDialog addFolderDialogUi; + + // Setup the UI. + addFolderDialogUi.setupUi(this); + + // Get handles for the widgets. + defaultFolderIconRadioButtonPointer = addFolderDialogUi.defaultFolderIconRadioButton; + currentWebsiteFavoriteIconRadioButtonPointer = addFolderDialogUi.currentWebsiteFavoriteIconRadioButton; + customFolderIconRadioButtonPointer = addFolderDialogUi.customFolderIconRadioButton; + parentFolderTreeWidgetPointer = addFolderDialogUi.parentFolderTreeWidget; + folderNameLineEditPointer = addFolderDialogUi.folderNameLineEdit; + QPushButton *browseButtonPointer = addFolderDialogUi.browseButton; + QDialogButtonBox *dialogButtonBoxPointer = addFolderDialogUi.dialogButtonBox; + + // Set the default favorite icon. + currentWebsiteFavoriteIconRadioButtonPointer->setIcon(currentWebsiteFavoriteIcon); + + // Instantiate a folder helper. + folderHelperPointer = new FolderHelper(); + + // Set the parent folder tree widget column count. + parentFolderTreeWidgetPointer->setColumnCount(2); + + // Hide the second column. + parentFolderTreeWidgetPointer->hideColumn(folderHelperPointer->FOLDER_ID_COLUMN); + + // Set the column header. + parentFolderTreeWidgetPointer->setHeaderLabel(i18nc("The folder tree widget header", "Select Parent Folder")); + + // Create a bookmarks tree widget item. + QTreeWidgetItem *bookmarksTreeWidgetItemPointer = new QTreeWidgetItem(); + + // Populate the bookmarks tree widget item. + bookmarksTreeWidgetItemPointer->setText(folderHelperPointer->FOLDER_NAME_COLUMN, i18nc("The bookmarks root tree widget name", "Bookmarks")); + bookmarksTreeWidgetItemPointer->setIcon(folderHelperPointer->FOLDER_NAME_COLUMN, QIcon::fromTheme(QLatin1String("bookmarks"), QIcon::fromTheme(QLatin1String("bookmark-new")))); + bookmarksTreeWidgetItemPointer->setText(folderHelperPointer->FOLDER_ID_COLUMN, QLatin1String("0")); + + // Add the bookmarks tree widget item to the root of the tree. + parentFolderTreeWidgetPointer->addTopLevelItem(bookmarksTreeWidgetItemPointer); + + // Select the root bookmarks folder if it is the initial parent folder. + if (parentFolderId == 0) + bookmarksTreeWidgetItemPointer->setSelected(true); + + // Populate the subfolders. + folderHelperPointer->populateSubfolders(bookmarksTreeWidgetItemPointer, parentFolderId); + + // Open all the folders. + parentFolderTreeWidgetPointer->expandAll(); + + // Focus the folder name line edit. + folderNameLineEditPointer->setFocus(); + + // Add buttons to the dialog button box. + addButtonPointer = dialogButtonBoxPointer->addButton(i18nc("The add folder button", "Add"), QDialogButtonBox::AcceptRole); + + // Set the button icons. + addButtonPointer->setIcon(QIcon::fromTheme("list-add")); + + // Connect the buttons. + connect(browseButtonPointer, SIGNAL(clicked()), this, SLOT(browse())); + connect(dialogButtonBoxPointer, SIGNAL(accepted()), this, SLOT(addFolder())); + connect(dialogButtonBoxPointer, SIGNAL(rejected()), this, SLOT(reject())); + + // Update the UI when the folder name changes. + connect(folderNameLineEditPointer, SIGNAL(textEdited(const QString&)), this, SLOT(updateUi(const QString&))); + + // Set the initial UI status. + updateUi(folderNameLineEditPointer->text()); +} + +void AddFolderDialog::addFolder() +{ + // Get the parent folder ID. + QList selectedFoldersList = parentFolderTreeWidgetPointer->selectedItems(); + + // Get the selected folder. + QTreeWidgetItem *selectedFolderPointer = selectedFoldersList.first(); + + // Create a favorite icon. + QIcon favoriteIcon; + + // Get the favorite icon. + if (defaultFolderIconRadioButtonPointer->isChecked()) // The default folder icon is checked. + favoriteIcon = defaultFolderIconRadioButtonPointer->icon(); + else if (currentWebsiteFavoriteIconRadioButtonPointer->isChecked()) // The current website favorite icon is checked. + favoriteIcon = currentWebsiteFavoriteIconRadioButtonPointer->icon(); + else // The custom folder icon is checked. + favoriteIcon = customFolderIconRadioButtonPointer->icon(); + + // Create a bookmark struct. + BookmarkStruct *bookmarkStructPointer = new BookmarkStruct; + + // Populate the bookmark struct. + bookmarkStructPointer->name = folderNameLineEditPointer->text(); + bookmarkStructPointer->parentFolderId = selectedFolderPointer->text(folderHelperPointer->FOLDER_ID_COLUMN).toDouble(); + bookmarkStructPointer->favoriteIcon = favoriteIcon; + + // Add the folder. + BookmarksDatabase::addFolder(bookmarkStructPointer); + + // Update the list of bookmarks in the menu and toolbar. + emit folderAdded(); + + // Close the dialog. + close(); +} + +void AddFolderDialog::browse() +{ + // Get an image file string from the user. + QString imageFileString = QFileDialog::getOpenFileName(this, tr("Favorite Icon Image"), QDir::homePath(), + tr("Image Files — *.bmp, *.gif, *.jpg, *.jpeg, *.png, *.svg (*.bmp *.gif *.jpg *.jpeg *.png *.svg);;All Files (*)")); + + // Check to see if an image file string was returned. This will be empty if the user selected cancel. + if (!imageFileString.isEmpty()) + { + // Set the custom favorite icon. + customFolderIconRadioButtonPointer->setIcon(QIcon(imageFileString)); + + // Check the custom favorite icon radio button. + customFolderIconRadioButtonPointer->setChecked(true); + } +} + +void AddFolderDialog::updateUi(const QString &newFolderName) +{ + // Set the status of the add button based on the + addButtonPointer->setEnabled(!newFolderName.isEmpty()); +} diff --git a/src/dialogs/AddFolderDialog.h b/src/dialogs/AddFolderDialog.h new file mode 100644 index 0000000..e0673a3 --- /dev/null +++ b/src/dialogs/AddFolderDialog.h @@ -0,0 +1,63 @@ +/* + * Copyright 2023-2024 Soren Stoutner . + * + * This file is part of Privacy Browser PC . + * + * Privacy Browser PC 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. + * + * Privacy Browser PC 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 Privacy Browser PC. If not, see . + */ + +#ifndef ADDFOLDERDIALOG_H +#define ADDFOLDERDIALOG_H + +// Application headers. +#include "helpers/FolderHelper.h" + +// Qt toolkit headers. +#include +#include +#include +#include + +class AddFolderDialog : public QDialog +{ + // Include the Q_OBJECT macro. + Q_OBJECT + +public: + // The primary constructor. + explicit AddFolderDialog(QWidget *parentWidgetPointer, const QIcon ¤tWebsiteFavoriteIcon, const double parentFolderId); + +signals: + // The signals. + void folderAdded() const; + +private Q_SLOTS: + // The private slots. + void addFolder(); + void browse(); + void updateUi(const QString &newFolderName); + +private: + // The private variables. + FolderHelper *folderHelperPointer; + + // The private widgets. + QPushButton *addButtonPointer; + QRadioButton *currentWebsiteFavoriteIconRadioButtonPointer; + QRadioButton *customFolderIconRadioButtonPointer; + QRadioButton *defaultFolderIconRadioButtonPointer; + QLineEdit *folderNameLineEditPointer; + QTreeWidget *parentFolderTreeWidgetPointer; +}; +#endif diff --git a/src/dialogs/AddOrEditCookieDialog.cpp b/src/dialogs/AddOrEditCookieDialog.cpp index e39303e..bb07c42 100644 --- a/src/dialogs/AddOrEditCookieDialog.cpp +++ b/src/dialogs/AddOrEditCookieDialog.cpp @@ -1,5 +1,5 @@ /* - * Copyright © 2022 Soren Stoutner . + * Copyright 2022-2024 Soren Stoutner . * * This file is part of Privacy Browser PC . * @@ -34,7 +34,7 @@ const int AddOrEditCookieDialog::AddCookie = 0; const int AddOrEditCookieDialog::EditCookie = 1; // Construct the class. -AddOrEditCookieDialog::AddOrEditCookieDialog(const int &dialogType, const QNetworkCookie *cookiePointer, const bool &isDurable) : QDialog(nullptr) +AddOrEditCookieDialog::AddOrEditCookieDialog(QWidget *parentWidgetPointer, const int dialogType, const QNetworkCookie *cookiePointer, const bool isDurable) : QDialog(parentWidgetPointer) { // Set the dialog window title according to the dialog type. if (dialogType == AddCookie) @@ -49,7 +49,7 @@ AddOrEditCookieDialog::AddOrEditCookieDialog(const int &dialogType, const QNetwo // Set the window modality. setWindowModality(Qt::WindowModality::ApplicationModal); - // Instantiate the cookie settings dialog UI. + // Instantiate the cookie dialog UI. Ui::AddOrEditCookieDialog addOrEditCookieDialogUi; // Setup the UI. @@ -83,6 +83,12 @@ AddOrEditCookieDialog::AddOrEditCookieDialog(const int &dialogType, const QNetwo secureCheckBoxPointer->setChecked(originalCookie.isSecure()); valueLineEditPointer->setText(originalCookie.value()); + // Scroll to the beginning of the line edits. + domainLineEditPointer->setCursorPosition(0); + nameLineEditPointer->setCursorPosition(0); + pathLineEditPointer->setCursorPosition(0); + valueLineEditPointer->setCursorPosition(0); + // Populate the expiration date if it exists. if (!originalCookie.isSessionCookie()) { @@ -167,7 +173,7 @@ void AddOrEditCookieDialog::saveCookie() emit addCookie(cookie, isDurable); // Close the dialog. - reject(); + close(); } void AddOrEditCookieDialog::updateExpirationDateTimeState(const int &newState) const diff --git a/src/dialogs/AddOrEditCookieDialog.h b/src/dialogs/AddOrEditCookieDialog.h index d00e3f7..cb641c9 100644 --- a/src/dialogs/AddOrEditCookieDialog.h +++ b/src/dialogs/AddOrEditCookieDialog.h @@ -1,5 +1,5 @@ /* - * Copyright © 2022 Soren Stoutner . + * Copyright 2022, 2024 Soren Stoutner . * * This file is part of Privacy Browser PC . * @@ -33,7 +33,7 @@ class AddOrEditCookieDialog : public QDialog public: // The primary constructor. - explicit AddOrEditCookieDialog(const int &dialogType, const QNetworkCookie *cookiePointer = nullptr, const bool &isDurable = false); + explicit AddOrEditCookieDialog(QWidget *parentWidgetPointer, const int dialogType, const QNetworkCookie *cookiePointer = nullptr, const bool isDurable = false); // The public static constants. static const int AddCookie; diff --git a/src/dialogs/BookmarksDialog.cpp b/src/dialogs/BookmarksDialog.cpp new file mode 100644 index 0000000..683d240 --- /dev/null +++ b/src/dialogs/BookmarksDialog.cpp @@ -0,0 +1,487 @@ +/* + * Copyright 2023-2024 Soren Stoutner . + * + * This file is part of Privacy Browser PC . + * + * Privacy Browser PC 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. + * + * Privacy Browser PC 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 Privacy Browser PC. If not, see . + */ + +// Application headers. +#include "BookmarksDialog.h" +#include "ui_BookmarksDialog.h" +#include "databases/BookmarksDatabase.h" +#include "dialogs/AddBookmarkDialog.h" +#include "dialogs/AddFolderDialog.h" +#include "dialogs/EditBookmarkDialog.h" +#include "dialogs/EditFolderDialog.h" + +// KDE Frameworks headers. +#include + +// Qt toolkit headers. +#include +#include + +// Construct the class. +BookmarksDialog::BookmarksDialog(QWidget *parentWidgetPointer, QIcon currentWebsiteFavorieIcon, QString currentWebsiteTitle, QString currentWebsiteUrl) : + QDialog(parentWidgetPointer), websiteFavoriteIcon(currentWebsiteFavorieIcon), websiteTitle(currentWebsiteTitle), websiteUrl(currentWebsiteUrl) +{ + // Set the dialog window title. + setWindowTitle(i18nc("The bookmarks dialog window title", "Bookmarks")); + + // Set the window modality. + setWindowModality(Qt::WindowModality::ApplicationModal); + + // Instantiate the bookmarks settings dialog UI. + Ui::BookmarksDialog bookmarksDialogUi; + + // Setup the UI. + bookmarksDialogUi.setupUi(this); + + // Get a handle for the draggable tree view. + draggableTreeViewPointer = bookmarksDialogUi.draggableTreeView; + + // Initialize the tree model. + treeModelPointer = new QStandardItemModel(); + + // Auto resize the headers. + draggableTreeViewPointer->header()->setSectionResizeMode(QHeaderView::ResizeToContents); + + // Indicate that all the rows are the same height, which improves performance. + draggableTreeViewPointer->setUniformRowHeights(true); + + // Set the selection mode to allow multiple rows to be selected at once. + draggableTreeViewPointer->setSelectionMode(QAbstractItemView::ExtendedSelection); + + // Allow dragging of bookmarks to reorder. + draggableTreeViewPointer->setDragDropMode(QAbstractItemView::InternalMove); + + // Set the tree model. + draggableTreeViewPointer->setModel(treeModelPointer); + + // Get a handle for the tree selection model. + treeSelectionModelPointer = draggableTreeViewPointer->selectionModel(); + + // Listen for selection changes. + connect(treeSelectionModelPointer, SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(updateSelection())); + + // Repopulate the bookmarks when they are moved. + connect(draggableTreeViewPointer, SIGNAL(bookmarksMoved()), this, SLOT(refreshBookmarks())); + + // Get handles for the buttons. + QPushButton *addBookmarkButtonPointer = bookmarksDialogUi.addBookmarkButton; + QPushButton *addFolderButtonPointer = bookmarksDialogUi.addFolderButton; + editButtonPointer = bookmarksDialogUi.editButton; + deleteItemsButtonPointer = bookmarksDialogUi.deleteItemsButton; + QDialogButtonBox *dialogButtonBoxPointer = bookmarksDialogUi.dialogButtonBox; + QPushButton *closeButtonPointer = dialogButtonBoxPointer->button(QDialogButtonBox::Close); + + // Connect the buttons. + connect(addBookmarkButtonPointer, SIGNAL(clicked()), this, SLOT(showAddBookmarkDialog())); + connect(addFolderButtonPointer, SIGNAL(clicked()), this, SLOT(showAddFolderDialog())); + connect(editButtonPointer, SIGNAL(clicked()), this, SLOT(showEditDialog())); + connect(deleteItemsButtonPointer, SIGNAL(clicked()), this, SLOT(deleteItems())); + connect(dialogButtonBoxPointer, SIGNAL(rejected()), this, SLOT(reject())); + + // Set the close button to be the default. + closeButtonPointer->setDefault(true); + + // Monitor editing of data in the tree model. + connect(treeModelPointer, SIGNAL(itemChanged(QStandardItem*)), this, SLOT(updateBookmarkFromTree(QStandardItem*))); + + // Populate the bookmarks. + populateBookmarks(); +} + +void BookmarksDialog::deleteItems() const +{ + // Create a parent folder ID standard C++ list (which can be sorted and from which duplicates can be removed). + std::list parentFolderIdList; + + // Get the list of selected model indexes. + QList selectedModelIndexList = treeSelectionModelPointer->selectedRows(DATABASE_ID_COLUMN); + + // Delete each of the bookmarks. + for (const QModelIndex &modelIndex : selectedModelIndexList) + { + // Add the parent folder ID to the list. + parentFolderIdList.push_back(modelIndex.parent().siblingAtColumn(FOLDER_ID_COLUMN).data().toDouble()); + + // Delete the bookmark. + BookmarksDatabase::deleteBookmark(modelIndex.data().toInt()); + } + + // Sort the parent folder ID. + parentFolderIdList.sort(); + + // Remove duplicates from the parent folder ID list. + parentFolderIdList.unique(); + + // Update the folder contents display order for each folder with a deleted bookmark. + for (const double parentFolderId : parentFolderIdList) + BookmarksDatabase::updateFolderContentsDisplayOrder(parentFolderId); + + // Repopulate the bookmarks in this dialog + populateBookmarks(); + + // Emit the bookmark updated signal to redraw the bookmarks in the menu and toolbar. + emit bookmarkUpdated(); +} + +void BookmarksDialog::populateBookmarks() const +{ + // Clear the current contents of the tree model. + treeModelPointer->clear(); + + // Set the column count. + treeModelPointer->setColumnCount(6); + + // Set the tree header data. + treeModelPointer->setHeaderData(NAME_COLUMN, Qt::Horizontal, i18nc("The bookmark Name header.", "Name")); + treeModelPointer->setHeaderData(URL_COLUMN, Qt::Horizontal, i18nc("The bookmark URL header.", "URL")); + + // Hide the backend columns. + draggableTreeViewPointer->setColumnHidden(DATABASE_ID_COLUMN, true); + draggableTreeViewPointer->setColumnHidden(DISPLAY_ORDER_COLUMN, true); + draggableTreeViewPointer->setColumnHidden(IS_FOLDER_COLUMN, true); + draggableTreeViewPointer->setColumnHidden(FOLDER_ID_COLUMN, true); + + // Create a bookmarks root item list. + QList bookmarksRootItemList; + + // Create the root items. + QStandardItem *rootItemNamePointer = new QStandardItem(QIcon::fromTheme(QLatin1String("bookmarks"), QIcon::fromTheme(QLatin1String("bookmark-new"))), + i18nc("The bookmarks root tree widget name", "Bookmarks")); + QStandardItem *rootItemUrlPointer = new QStandardItem(QLatin1String("")); + QStandardItem *rootItemDatabaseIdPonter = new QStandardItem(QLatin1String("-1")); // The root item doesn't have a database ID. + QStandardItem *rootItemDisplayOrderPointer = new QStandardItem(QLatin1String("-1")); // The root item doesn't have a display order. + QStandardItem *rootItemIsFolderPointer = new QStandardItem(QLatin1String("1")); + QStandardItem *rootItemFolderIdPointer = new QStandardItem(QLatin1String("0")); + + // Enable dropping on the root name column. + rootItemNamePointer->setDropEnabled(true); + + // Disable dropping on the URL. + rootItemUrlPointer->setDropEnabled(false); + + // Disable dragging of the bookmarks root item. + rootItemNamePointer->setDragEnabled(false); + rootItemUrlPointer->setDragEnabled(false); + + // Disable selecting the URL. + rootItemUrlPointer->setSelectable(false); + + // Populate the bookmarks root item list. + bookmarksRootItemList.append(rootItemNamePointer); + bookmarksRootItemList.append(rootItemUrlPointer); + bookmarksRootItemList.append(rootItemDatabaseIdPonter); + bookmarksRootItemList.append(rootItemDisplayOrderPointer); + bookmarksRootItemList.append(rootItemIsFolderPointer); + bookmarksRootItemList.append(rootItemFolderIdPointer); + + // Add the bookmarks root item to the tree. + treeModelPointer->appendRow(bookmarksRootItemList); + + // Populate the subfolders, starting with the root folder ID (`0`). + populateSubfolders(rootItemNamePointer, 0); + + // Expand all the folder. + draggableTreeViewPointer->expandAll(); + + // Update the UI. + updateUi(); +} + +void BookmarksDialog::populateSubfolders(QStandardItem *folderItemNamePointer, const double folderId) const +{ + // Get the folder contents. + QList *folderContentsListPointer = BookmarksDatabase::getFolderContents(folderId); + + // Populate the bookmarks tree view. + for (const BookmarkStruct &bookmarkStruct : *folderContentsListPointer) + { + // Create a bookmark item list. + QList bookmarkItemList; + + // Create the bookmark items. + QStandardItem *nameItemPointer = new QStandardItem(bookmarkStruct.favoriteIcon, bookmarkStruct.name); + QStandardItem *urlItemPointer = new QStandardItem(bookmarkStruct.url); + QStandardItem *databaseIdItemPointer = new QStandardItem(QString::number(bookmarkStruct.databaseId)); + QStandardItem *displayOrderItemPointer = new QStandardItem(QString::number(bookmarkStruct.displayOrder)); + QStandardItem *isFolderItemPointer = new QStandardItem(QString::number(bookmarkStruct.isFolder)); + QStandardItem *folderIdItemPointer = new QStandardItem(QString::number(bookmarkStruct.folderId, 'f', 0)); // Format the folder ID as a floating point with no trailing zeros. + + // Enable dragging and dropping of the name column. + nameItemPointer->setDragEnabled(true); + + // Only allow dropping on the name if this is a folder. + nameItemPointer->setDropEnabled(bookmarkStruct.isFolder); + + // Disable dragging and dropping on the URL. + urlItemPointer->setDragEnabled(false); + urlItemPointer->setDropEnabled(false); + + // Disable selecting the URL. + urlItemPointer->setSelectable(false); + + // Populate the bookmark item list. + bookmarkItemList.append(nameItemPointer); + bookmarkItemList.append(urlItemPointer); + bookmarkItemList.append(databaseIdItemPointer); + bookmarkItemList.append(displayOrderItemPointer); + bookmarkItemList.append(isFolderItemPointer); + bookmarkItemList.append(folderIdItemPointer); + + // Add the bookmark to the parent folder. + folderItemNamePointer->appendRow(bookmarkItemList); + + // Populate subfolders if this item is a folder. + if (bookmarkStruct.isFolder) + populateSubfolders(nameItemPointer, bookmarkStruct.folderId); + } +} + +void BookmarksDialog::refreshBookmarks() const +{ + // Repopulate the bookmarks in this dialog + populateBookmarks(); + + // Emit the bookmark updated signal to redraw the bookmarks in the menu and toolbar. + emit bookmarkUpdated(); +} + +void BookmarksDialog::selectSubfolderContents(const QModelIndex &parentModelIndex) const +{ + // Get the index model. + const QAbstractItemModel *modelIndexAbstractItemPointer = parentModelIndex.model(); + + // Get the number of items in the folder. + int numberOfChildrenInFolder = modelIndexAbstractItemPointer->rowCount(parentModelIndex); + + // Select any child items. + if (numberOfChildrenInFolder > 0) + { + // Select the contents of any subfolders. + for (int i = 0; i < numberOfChildrenInFolder; ++i) + { + // Get the child model index. + QModelIndex childModelIndex = modelIndexAbstractItemPointer->index(i, 0, parentModelIndex); + + // Select the subfolder contents if it is a folder. + if (childModelIndex.siblingAtColumn(IS_FOLDER_COLUMN).data().toBool()) + selectSubfolderContents(childModelIndex); + } + + // Get the first and last child model indexes. + QModelIndex firstChildModelIndex = modelIndexAbstractItemPointer->index(0, 0, parentModelIndex); + QModelIndex lastChildModelIndex = modelIndexAbstractItemPointer->index((numberOfChildrenInFolder - 1), 0, parentModelIndex); + + // Create an item selection that includes all the child items. + QItemSelection folderChildItemsSelection = QItemSelection(firstChildModelIndex, lastChildModelIndex); + + // Get the current selection. + QItemSelection currentSelection = treeSelectionModelPointer->selection(); + + // Combine the current selection and the folder child items selection. + currentSelection.merge(folderChildItemsSelection, QItemSelectionModel::SelectCurrent); + + // Selected the updated list of items. + treeSelectionModelPointer->select(currentSelection, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); + } +} + +void BookmarksDialog::showAddBookmarkDialog() +{ + // Return the most recently selected index. + QModelIndex currentIndex = treeSelectionModelPointer->currentIndex(); + + // Instantiate a parent folder ID. + double parentFolderId; + + // Get the parent folder ID. + if (currentIndex.siblingAtColumn(IS_FOLDER_COLUMN).data().toInt() == 1) // The current index is a folder. + { + // Store the parent folder ID. + parentFolderId = currentIndex.siblingAtColumn(FOLDER_ID_COLUMN).data().toDouble(); + } + else // The current index is not a folder. + { + // Store the parent folder ID of the folder that contains the bookmark. + parentFolderId = currentIndex.parent().siblingAtColumn(FOLDER_ID_COLUMN).data().toDouble(); + } + + // Instantiate an add bookmark dialog. + AddBookmarkDialog *addBookmarkDialogPointer = new AddBookmarkDialog(this, websiteTitle, websiteUrl, websiteFavoriteIcon, parentFolderId); + + // Update the displayed bookmarks when a new one is added. + connect(addBookmarkDialogPointer, SIGNAL(bookmarkAdded()), this, SLOT(refreshBookmarks())); + + // Show the dialog. + addBookmarkDialogPointer->show(); +} + +void BookmarksDialog::showAddFolderDialog() +{ + // Get the most recently selected index. + QModelIndex currentIndex = treeSelectionModelPointer->currentIndex(); + + // Instantiate a parent folder ID. + double parentFolderId; + + // Get the parent folder ID. + if (currentIndex.siblingAtColumn(IS_FOLDER_COLUMN).data().toInt() == 1) // The current index is a folder. + { + // Store the parent folder ID. + parentFolderId = currentIndex.siblingAtColumn(FOLDER_ID_COLUMN).data().toDouble(); + } + else // The current index is not a folder. + { + // Store the parent folder ID of the folder that contains the bookmark. + parentFolderId = currentIndex.parent().siblingAtColumn(FOLDER_ID_COLUMN).data().toDouble(); + } + + // Instantiate an add folder dialog. + AddFolderDialog *addFolderDialogPointer = new AddFolderDialog(this, websiteFavoriteIcon, parentFolderId); + + // Update the displayed bookmarks when a folder is added. + connect(addFolderDialogPointer, SIGNAL(folderAdded()), this, SLOT(refreshBookmarks())); + + // Show the dialog. + addFolderDialogPointer->show(); +} + +void BookmarksDialog::showEditDialog() +{ + // Get the current model index. + QModelIndex currentIndex = treeSelectionModelPointer->currentIndex(); + + // Check to see if the selected item is a folder. + if (currentIndex.siblingAtColumn(IS_FOLDER_COLUMN).data().toInt() == 1) // The selected item is a folder. + { + // Instantiate an edit folder dialog. + QDialog *editFolderDialogPointer = new EditFolderDialog(this, currentIndex.siblingAtColumn(DATABASE_ID_COLUMN).data().toInt(), websiteFavoriteIcon); + + // Show the dialog. + editFolderDialogPointer->show(); + + // Update the bookmarks UI. + connect(editFolderDialogPointer, SIGNAL(folderSaved()), this, SLOT(refreshBookmarks())); + } + else // The selected item is a bookmark. + { + // Instantiate an edit bookmark dialog. + QDialog *editBookmarkDialogPointer = new EditBookmarkDialog(this, currentIndex.siblingAtColumn(DATABASE_ID_COLUMN).data().toInt(), websiteFavoriteIcon); + + // Show the dialog. + editBookmarkDialogPointer->show(); + + // Update the bookmarks UI. + connect(editBookmarkDialogPointer, SIGNAL(bookmarkSaved()), this, SLOT(refreshBookmarks())); + } +} + +void BookmarksDialog::updateBookmarkFromTree(QStandardItem *modifiedStandardItem) +{ + // Get the model index of the modified item. + QModelIndex modifiedItemModelIndex = modifiedStandardItem->index(); + + // Get the model index of the database ID. + QModelIndex databaseIdModelIndex = modifiedItemModelIndex.siblingAtColumn(DATABASE_ID_COLUMN); + + // Get the database ID. + int databaseId = databaseIdModelIndex.data().toInt(); + + // Check to see if the bookmark name or the URL was edited. + if (modifiedStandardItem->column() == NAME_COLUMN) // The bookmark name was edited. + { + // Update the bookmark name. + BookmarksDatabase::updateBookmarkName(databaseId, modifiedStandardItem->text()); + } + else // The bookmark URL was edited. + { + // Update the bookmark URL. + BookmarksDatabase::updateBookmarkUrl(databaseId, modifiedStandardItem->text()); + } + + // Emit the bookmark updated signal. + emit bookmarkUpdated(); +} + +void BookmarksDialog::updateSelection() const +{ + // Set the status of the buttons. + if (treeSelectionModelPointer->hasSelection()) // A bookmark or folder is selected. + { + // Get the list of selected model indexes. + QModelIndexList selectedRowsModelIndexList = treeSelectionModelPointer->selectedRows(); + + // Check to see if each selected item is a folder. + for(QModelIndex modelIndex : selectedRowsModelIndexList) + { + // If it is a folder, select all the children bookmarks. + if (modelIndex.siblingAtColumn(IS_FOLDER_COLUMN).data().toBool()) + selectSubfolderContents(modelIndex); + } + } + + // Update the UI. + updateUi(); +} + +void BookmarksDialog::updateUi() const +{ + // Set the status of the buttons. + if (treeSelectionModelPointer->hasSelection()) // A bookmark or folder is selected. + { + // Get the currently selected index. + QModelIndex currentSelectedIndex = treeSelectionModelPointer->currentIndex(); + + // Get the list of selected model indexes. + QModelIndexList selectedRowsModelIndexList = treeSelectionModelPointer->selectedRows(); + + // Get the number of selected rows. + int numberOfSelectedRows = selectedRowsModelIndexList.count(); + + // Enable the edit button if a folder or only one bookmark is selected. + editButtonPointer->setEnabled(currentSelectedIndex.siblingAtColumn(IS_FOLDER_COLUMN).data().toBool() || (numberOfSelectedRows == 1)); + + // Check if the root folder is selected. + if (treeSelectionModelPointer->isRowSelected(0)) + { + // Disable the edit button. + editButtonPointer->setEnabled(false); + + // Decrease the number of selected rows by 1. + --numberOfSelectedRows; + } + + // Enabled the delete button if at least one real bookmark or folder is selected. + deleteItemsButtonPointer->setEnabled(numberOfSelectedRows > 0); + + // Update the delete items button text. + if (numberOfSelectedRows > 0) + deleteItemsButtonPointer->setText(i18ncp("Delete items button populated text.", "Delete %1 item", "Delete %1 items", numberOfSelectedRows)); + else + deleteItemsButtonPointer->setText(i18nc("Delete items button default text", "Delete items")); + } + else // Nothing is selected. + { + // Disable the buttons. + editButtonPointer->setEnabled(false); + deleteItemsButtonPointer->setEnabled(false); + + // Update the delete items button text. + deleteItemsButtonPointer->setText(i18nc("Delete items button default text", "Delete items")); + } +} diff --git a/src/dialogs/BookmarksDialog.h b/src/dialogs/BookmarksDialog.h new file mode 100644 index 0000000..0df0903 --- /dev/null +++ b/src/dialogs/BookmarksDialog.h @@ -0,0 +1,80 @@ +/* + * Copyright 2023-2024 Soren Stoutner . + * + * This file is part of Privacy Browser PC . + * + * Privacy Browser PC 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. + * + * Privacy Browser PC 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 Privacy Browser PC. If not, see . + */ + +#ifndef BOOKMARKSDIALOG_H +#define BOOKMARKSDIALOG_H + +// Application headers. +#include "structs/BookmarkStruct.h" +#include "widgets/DraggableTreeView.h" + +// Qt toolkit headers. +#include +#include +#include + +class BookmarksDialog : public QDialog +{ + // Include the Q_OBJECT macro. + Q_OBJECT + +public: + // The primary constructor. + BookmarksDialog(QWidget *parentWidgetPointer, QIcon currentWebsiteFavorieIcon, QString currentWebsiteTitle, QString currentWebsiteUrl); + + // The public constants. + static const int NAME_COLUMN = 0; + static const int URL_COLUMN = 1; + static const int DATABASE_ID_COLUMN = 2; + static const int DISPLAY_ORDER_COLUMN = 3; + static const int IS_FOLDER_COLUMN = 4; + static const int FOLDER_ID_COLUMN = 5; + +signals: + // The signals. + void bookmarkUpdated() const; + +private Q_SLOTS: + // The private slots. + void deleteItems() const; + void refreshBookmarks() const; + void showAddBookmarkDialog(); + void showAddFolderDialog(); + void showEditDialog(); + void updateBookmarkFromTree(QStandardItem *modifiedStandardItem); + void updateSelection() const; + +private: + // The private variables. + QPushButton *deleteItemsButtonPointer; + QPushButton *editButtonPointer; + QStandardItemModel *treeModelPointer; + QItemSelectionModel *treeSelectionModelPointer; + DraggableTreeView *draggableTreeViewPointer; + QIcon websiteFavoriteIcon; + QString websiteTitle; + QString websiteUrl; + + // The private functions. + void populateBookmarks() const; + void populateSubfolders(QStandardItem *folderItemNamePointer, const double folderId) const; + void selectSubfolderContents(const QModelIndex &parentModelIndex) const; + void updateUi() const; +}; +#endif diff --git a/src/dialogs/CMakeLists.txt b/src/dialogs/CMakeLists.txt index 7120f4d..94b84aa 100644 --- a/src/dialogs/CMakeLists.txt +++ b/src/dialogs/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright © 2022 Soren Stoutner . +# Copyright 2022-2024 Soren Stoutner . # # This file is part of Privacy Browser PC . # @@ -17,10 +17,17 @@ # List the sources to include in the executable. -target_sources(privacy-browser PRIVATE +target_sources(privacybrowser PRIVATE + AddBookmarkDialog.cpp + AddFolderDialog.cpp AddOrEditCookieDialog.cpp + BookmarksDialog.cpp CookiesDialog.cpp DomainSettingsDialog.cpp DurableCookiesDialog.cpp + EditBookmarkDialog.cpp + EditFolderDialog.cpp + HttpAuthenticationDialog.cpp SaveDialog.cpp + SettingsDialog.cpp ) diff --git a/src/dialogs/CookiesDialog.cpp b/src/dialogs/CookiesDialog.cpp index 4e85efe..57e5ed7 100644 --- a/src/dialogs/CookiesDialog.cpp +++ b/src/dialogs/CookiesDialog.cpp @@ -1,5 +1,5 @@ /* - * Copyright © 2022 Soren Stoutner . + * Copyright 2022-2024 Soren Stoutner . * * This file is part of Privacy Browser PC . * @@ -65,7 +65,7 @@ bool cookieSortPredicate(const QNetworkCookie &leftHandCookie, const QNetworkCoo QString leftHandThirdLevelDomain; QString rightHandThirdLevelDomain; - // Get the numer of dots in the strings. + // Get the number of dots in the strings. int leftHandDots = leftHandDomain.count(QLatin1Char('.')); int rightHandDots = rightHandDomain.count(QLatin1Char('.')); @@ -154,7 +154,7 @@ CookiesDialog::CookiesDialog(std::list *originalCookieListPointe treeModelPointer->horizontalHeaderItem(0)->setToolTip(i18nc("The cookie Name tool tip.", "The name identifies the cookie. Each cookie has a unique combination of domain, name, and path.")); treeModelPointer->horizontalHeaderItem(1)->setToolTip(i18nc("The cookie Durable tool tip", - "Durable cookies pursist across restarts, irrespective of the expiration date. All other cookies are deleted when Privacy Browser closes, irrespective of the expiration date.")); + "Durable cookies persist across restarts, irrespective of the expiration date. All other cookies are deleted when Privacy Browser closes, irrespective of the expiration date.")); treeModelPointer->horizontalHeaderItem(2)->setToolTip(i18nc("The cookie Path tool tip.", "Websites can restrict cookie access to subpath of their URL.")); treeModelPointer->horizontalHeaderItem(3)->setToolTip(i18nc("The cookie Expiration Date tool tip.", "Cookies without an expiration date are known as session cookies and are expected to be deleted every time the browser closes.")); @@ -202,11 +202,11 @@ CookiesDialog::CookiesDialog(std::list *originalCookieListPointe // Create the cookie items. QStandardItem *nameItemPointer = new QStandardItem(QString(cookie.name())); - QStandardItem *durableItemPointer = new QStandardItem(QString(isDurable ? i18n("yes") : i18n("no"))); - QStandardItem *pathItemPointer = new QStandardItem(QString(cookie.path())); - QStandardItem *expirationDateItemPointer = new QStandardItem(QString(cookie.expirationDate().toString())); - QStandardItem *isHttpOnlyItemPointer = new QStandardItem(QString(cookie.isHttpOnly() ? i18n("yes") : i18n("no"))); - QStandardItem *isSecureItemPointer = new QStandardItem(QString(cookie.isSecure() ? i18n("yes") : i18n("no"))); + QStandardItem *durableItemPointer = new QStandardItem(isDurable ? i18n("yes") : i18n("no")); + QStandardItem *pathItemPointer = new QStandardItem(cookie.path()); + QStandardItem *expirationDateItemPointer = new QStandardItem(cookie.expirationDate().toString()); + QStandardItem *isHttpOnlyItemPointer = new QStandardItem(cookie.isHttpOnly() ? i18n("yes") : i18n("no")); + QStandardItem *isSecureItemPointer = new QStandardItem(cookie.isSecure() ? i18n("yes") : i18n("no")); QStandardItem *valueItemPointer = new QStandardItem(QString(cookie.value())); // Populate the cookie standard item list. @@ -231,7 +231,7 @@ CookiesDialog::CookiesDialog(std::list *originalCookieListPointe // Don't elide the Value field (or any other field). treeViewPointer->setTextElideMode(Qt::ElideNone); - // Indicate that all the rows are the same height, wich improves performance. + // Indicate that all the rows are the same height, which improves performance. treeViewPointer->setUniformRowHeights(true); // Disable editing in the tree view. @@ -259,7 +259,7 @@ CookiesDialog::CookiesDialog(std::list *originalCookieListPointe QDialogButtonBox::ActionRole); // Set the button icons. - durableCookiesButtonPointer->setIcon(QIcon::fromTheme("view-visible")); + durableCookiesButtonPointer->setIcon(QIcon::fromTheme("view-visible", QIcon::fromTheme(QLatin1String("appointment-new")))); // Connect the buttons. connect(addCookieButtonPointer, SIGNAL(clicked()), this, SLOT(showAddCookieDialog())); @@ -594,10 +594,10 @@ void CookiesDialog::deleteCookieFromDialog(const QNetworkCookie &cookie) const emit deleteCookie(cookie); } -void CookiesDialog::showAddCookieDialog() const +void CookiesDialog::showAddCookieDialog() { // Instantiate an add cookie dialog. - QDialog *addCookieDialogPointer = new AddOrEditCookieDialog(AddOrEditCookieDialog::AddCookie); + QDialog *addCookieDialogPointer = new AddOrEditCookieDialog(this, AddOrEditCookieDialog::AddCookie); // Show the dialog. addCookieDialogPointer->show(); @@ -744,10 +744,10 @@ void CookiesDialog::showDeleteCookieMessageBox() const } } -void CookiesDialog::showDurableCookiesDialog() const +void CookiesDialog::showDurableCookiesDialog() { // Instantiate a durable cookies dialog. - QDialog *durableCookiesDialogPointer = new DurableCookiesDialog(); + QDialog *durableCookiesDialogPointer = new DurableCookiesDialog(this); // Show the dialog. durableCookiesDialogPointer->show(); @@ -758,7 +758,7 @@ void CookiesDialog::showDurableCookiesDialog() const connect(durableCookiesDialogPointer, SIGNAL(updateParentUi()), this, SLOT(updateUi())); } -void CookiesDialog::showEditCookieDialog() const +void CookiesDialog::showEditCookieDialog() { // Get the current model index. QModelIndex currentIndex = treeSelectionModelPointer->currentIndex(); @@ -783,7 +783,7 @@ void CookiesDialog::showEditCookieDialog() const } // Instantiate an edit cookie dialog. - QDialog *editCookieDialogPointer = new AddOrEditCookieDialog(AddOrEditCookieDialog::EditCookie, &cookieToEdit, currentIndex.siblingAtColumn(1).data().toString() == i18n("yes")); + QDialog *editCookieDialogPointer = new AddOrEditCookieDialog(this, AddOrEditCookieDialog::EditCookie, &cookieToEdit, currentIndex.siblingAtColumn(1).data().toString() == i18n("yes")); // Show the dialog. editCookieDialogPointer->show(); diff --git a/src/dialogs/CookiesDialog.h b/src/dialogs/CookiesDialog.h index 36a7d72..5319897 100644 --- a/src/dialogs/CookiesDialog.h +++ b/src/dialogs/CookiesDialog.h @@ -1,5 +1,5 @@ /* - * Copyright © 2022 Soren Stoutner . + * Copyright 2022-2024 Soren Stoutner . * * This file is part of Privacy Browser PC . * @@ -50,11 +50,11 @@ private Q_SLOTS: void addCookieFromDialog(const QNetworkCookie &cookie, const bool &isDurable) const; void deleteCookieFromDatabase(const QNetworkCookie &cookie) const; void deleteCookieFromDialog(const QNetworkCookie &cookie) const; - void showAddCookieDialog() const; + void showAddCookieDialog(); void showDeleteAllMessageBox() const; void showDeleteCookieMessageBox() const; - void showDurableCookiesDialog() const; - void showEditCookieDialog() const; + void showDurableCookiesDialog(); + void showEditCookieDialog(); void updateUi() const; private: diff --git a/src/dialogs/DomainSettingsDialog.cpp b/src/dialogs/DomainSettingsDialog.cpp index 34ff70e..0907a18 100644 --- a/src/dialogs/DomainSettingsDialog.cpp +++ b/src/dialogs/DomainSettingsDialog.cpp @@ -1,5 +1,5 @@ /* - * Copyright © 2022 Soren Stoutner . + * Copyright 2022-2024 Soren Stoutner . * * This file is part of Privacy Browser PC . * @@ -15,33 +15,35 @@ * * You should have received a copy of the GNU General Public License * along with Privacy Browser PC. If not, see . - */ + // */ // Application headers. #include "DomainSettingsDialog.h" #include "Settings.h" #include "ui_DomainSettingsDialog.h" #include "databases/DomainsDatabase.h" -#include "helpers/UserAgentHelper.h" // Qt toolkit headers. #include #include #include +#include // Define the public static int constants. const int DomainSettingsDialog::SHOW_ALL_DOMAINS = 0; -const int DomainSettingsDialog::ADD_DOMAIN = 1; -const int DomainSettingsDialog::EDIT_DOMAIN = 2; +const int DomainSettingsDialog::EDIT_DOMAIN = 1; // Construct the class. -DomainSettingsDialog::DomainSettingsDialog(const int &startType, const QString &domainName) : QDialog(nullptr) +DomainSettingsDialog::DomainSettingsDialog(QWidget *parentWidgetPointer, const int &startType, const QString &domainName) : QDialog(parentWidgetPointer) { // Set the window title. setWindowTitle(i18nc("The domain settings dialog window title", "Domain Settings")); // Set the window modality. - setWindowModality(Qt::WindowModality::ApplicationModal);; + setWindowModality(Qt::WindowModality::ApplicationModal); + + // Instantiate the user agent helper. + userAgentHelperPointer = new UserAgentHelper(); // Instantiate the domain settings dialog UI. Ui::DomainSettingsDialog domainSettingsDialogUi; @@ -124,17 +126,6 @@ DomainSettingsDialog::DomainSettingsDialog(const int &startType, const QString & // Select the first entry in the list view. domainsListViewPointer->setCurrentIndex(domainsTableModelPointer->index(0, domainsTableModelPointer->fieldIndex(DomainsDatabase::DOMAIN_NAME))); - // Populate the domain settings. - domainSelected(domainsSelectionModelPointer->currentIndex()); - - break; - } - - case ADD_DOMAIN: - { - // Add the new domain. - addDomain(domainName); - break; } @@ -147,13 +138,13 @@ DomainSettingsDialog::DomainSettingsDialog(const int &startType, const QString & // Move to the new domain. domainsListViewPointer->setCurrentIndex(newDomainIndex[0]); - // Populate the domain settings. - domainSelected(domainsSelectionModelPointer->currentIndex()); - break; } } + // Populate the domain settings. + domainSelected(domainsSelectionModelPointer->currentIndex()); + // Handle clicks on the domains. connect(domainsListViewPointer, SIGNAL(activated(QModelIndex)), this, SLOT(domainSelected(QModelIndex))); @@ -174,38 +165,8 @@ DomainSettingsDialog::DomainSettingsDialog(const int &startType, const QString & connect(applyButtonPointer, SIGNAL(clicked()), this, SLOT(apply())); connect(dialogButtonBoxPointer, SIGNAL(rejected()), this, SLOT(cancel())); - // Update the UI. - updateUi(); -} - -void DomainSettingsDialog::addDomain(const QString &domainName) const -{ - // Create a new domain record. - QSqlRecord newDomainRecord = QSqlDatabase::database(DomainsDatabase::CONNECTION_NAME).record(DomainsDatabase::DOMAINS_TABLE); - - // Set the values for the new domain. - newDomainRecord.setValue(domainsTableModelPointer->fieldIndex(DomainsDatabase::DOMAIN_NAME), domainName); - newDomainRecord.setValue(domainsTableModelPointer->fieldIndex(DomainsDatabase::JAVASCRIPT), DomainsDatabase::SYSTEM_DEFAULT); - newDomainRecord.setValue(domainsTableModelPointer->fieldIndex(DomainsDatabase::DOM_STORAGE), DomainsDatabase::SYSTEM_DEFAULT); - newDomainRecord.setValue(domainsTableModelPointer->fieldIndex(DomainsDatabase::USER_AGENT), UserAgentHelper::SYSTEM_DEFAULT_DATABASE); - newDomainRecord.setValue(domainsTableModelPointer->fieldIndex(DomainsDatabase::ZOOM_FACTOR), DomainsDatabase::SYSTEM_DEFAULT); - newDomainRecord.setValue(domainsTableModelPointer->fieldIndex(DomainsDatabase::CUSTOM_ZOOM_FACTOR), 1.0); - - // Insert the new domain. `-1` appends it to the end. - domainsTableModelPointer->insertRecord(-1, newDomainRecord); - - // Submit all pending changes. - domainsTableModelPointer->submitAll(); - - // Find the index for the new domain. `-1` allows for multiple entries to be returned. - QModelIndexList newDomainIndex = domainsTableModelPointer->match(domainsTableModelPointer->index(0, domainsTableModelPointer->fieldIndex(DomainsDatabase::DOMAIN_NAME)), - Qt::DisplayRole, domainName, -1, Qt::MatchWrap); - - // Move to the new domain. If there are multiple domains with the same name, the new one should be the last in the list. - domainsListViewPointer->setCurrentIndex(newDomainIndex[newDomainIndex.size() - 1]); - - // Populate the domain settings. - domainSelected(domainsSelectionModelPointer->currentIndex()); + // Update the DOM storage status. + updateDomStorageStatus(); // Update the UI. updateUi(); @@ -217,13 +178,13 @@ void DomainSettingsDialog::apply() const QModelIndex currentIndex = domainsListViewPointer->currentIndex(); // Get the ID of the current index row. - QVariant currentId = currentIndex.siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabase::_ID)).data(); + QVariant currentId = currentIndex.siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabase::ID)).data(); // Submit all pending changes. domainsTableModelPointer->submitAll(); // Find the new index for the selected id. The `1` keeps searching after the first match. - QModelIndexList newIndexList = domainsTableModelPointer->match(currentIndex.siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabase::_ID)), Qt::DisplayRole, currentId, + QModelIndexList newIndexList = domainsTableModelPointer->match(currentIndex.siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabase::ID)), Qt::DisplayRole, currentId, 1, Qt::MatchWrap); // Select the new index. @@ -312,11 +273,17 @@ void DomainSettingsDialog::domainSelected(const QModelIndex &modelIndex) const { // Display the default zoom factor. customZoomFactorSpinBoxPointer->setValue(Settings::zoomFactor()); + + // Use the default palette. + zoomFactorWidgetPointer->setPalette(defaultPalette); } else // Custom zoom factor is selected. { // Display the custom zoom factor from the domain settings. customZoomFactorSpinBoxPointer->setValue(modelIndex.siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabase::CUSTOM_ZOOM_FACTOR)).data().toDouble()); + + // Use the highlighted palette. + zoomFactorWidgetPointer->setPalette(highlightedPalette); } // Set the initial status of the custom zoom factor spin box. @@ -340,6 +307,9 @@ void DomainSettingsDialog::javaScriptChanged(const int &newIndex) const // Populate the JavaScript label. populateJavaScriptLabel(); + // Update the DOM storage status. + updateDomStorageStatus(); + // Update the UI. updateUi(); } @@ -349,9 +319,12 @@ void DomainSettingsDialog::localStorageChanged(const int &newIndex) const // Update the domains table model. domainsTableModelPointer->setData(domainsSelectionModelPointer->currentIndex().siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabase::LOCAL_STORAGE)), newIndex); - // Poplate the local storage label. + // Populate the local storage label. populateLocalStorageLabel(); + // Update the DOM storage status. + updateDomStorageStatus(); + // Update the UI. updateUi(); } @@ -362,7 +335,7 @@ void DomainSettingsDialog::ok() domainsTableModelPointer->submitAll(); // Emit the domain settings updated signal. - domainSettingsUpdated(); + emit domainSettingsUpdated(); // Close the dialog. accept(); @@ -387,10 +360,10 @@ void DomainSettingsDialog::populateDomStorageLabel() const break; } - case (DomainsDatabase::DISABLED): + case (DomainsDatabase::ENABLED): { - // Set the disabled text in bold. - domStorageLabelPointer->setText(i18nc("Domain settings DOM storage label. The tags should be retained.", "DOM storage disabled")); + // Set the enabled text in bold. + domStorageLabelPointer->setText(i18nc("Domain settings DOM storage label. The tags should be retained.", "DOM storage enabled")); // Set the palette. domStorageWidgetPointer->setPalette(highlightedPalette); @@ -398,10 +371,10 @@ void DomainSettingsDialog::populateDomStorageLabel() const break; } - case (DomainsDatabase::ENABLED): + case (DomainsDatabase::DISABLED): { - // Set the enabled text in bold. - domStorageLabelPointer->setText(i18nc("Domain settings DOM storage label. The tags should be retained.", "DOM storage enabled")); + // Set the disabled text in bold. + domStorageLabelPointer->setText(i18nc("Domain settings DOM storage label. The tags should be retained.", "DOM storage disabled")); // Set the palette. domStorageWidgetPointer->setPalette(highlightedPalette); @@ -430,10 +403,10 @@ void DomainSettingsDialog::populateJavaScriptLabel() const break; } - case (DomainsDatabase::DISABLED): + case (DomainsDatabase::ENABLED): { - // Set the disabled text in bold. - javaScriptLabelPointer->setText(i18nc("Domain settings JavaScript label. The tags should be retained.", "JavaScript disabled")); + // Set the enabled text in bold. + javaScriptLabelPointer->setText(i18nc("Domain settings JavaScript label. The tags should be retained.", "JavaScript enabled")); // Set the palette. javaScriptWidgetPointer->setPalette(highlightedPalette); @@ -441,10 +414,10 @@ void DomainSettingsDialog::populateJavaScriptLabel() const break; } - case (DomainsDatabase::ENABLED): + case (DomainsDatabase::DISABLED): { - // Set the enabled text in bold. - javaScriptLabelPointer->setText(i18nc("Domain settings JavaScript label. The tags should be retained.", "JavaScript enabled")); + // Set the disabled text in bold. + javaScriptLabelPointer->setText(i18nc("Domain settings JavaScript label. The tags should be retained.", "JavaScript disabled")); // Set the palette. javaScriptWidgetPointer->setPalette(highlightedPalette); @@ -473,10 +446,10 @@ void DomainSettingsDialog::populateLocalStorageLabel() const break; } - case (DomainsDatabase::DISABLED): + case (DomainsDatabase::ENABLED): { - // Set the disabled text in bold. - localStorageLabelPointer->setText(i18nc("Domain settings local storage label. The tags should be retained.", "Local storage disabled")); + // Set the enabled text in bold. + localStorageLabelPointer->setText(i18nc("Domain settings local storage label. The tabs should be retained.", "Local storage enabled")); // Set the palette. localStorageWidgetPointer->setPalette(highlightedPalette); @@ -484,10 +457,10 @@ void DomainSettingsDialog::populateLocalStorageLabel() const break; } - case (DomainsDatabase::ENABLED): + case (DomainsDatabase::DISABLED): { - // Set the enabled text in bold. - localStorageLabelPointer->setText(i18nc("Domain settings local storage label. The tabs should be retained.", "Local storage enabled")); + // Set the disabled text in bold. + localStorageLabelPointer->setText(i18nc("Domain settings local storage label. The tags should be retained.", "Local storage disabled")); // Set the palette. localStorageWidgetPointer->setPalette(highlightedPalette); @@ -501,10 +474,10 @@ void DomainSettingsDialog::populateLocalStorageLabel() const void DomainSettingsDialog::populateUserAgentLabel(const QString &userAgentName) const { // Populate the label according to the type. - if (userAgentName == UserAgentHelper::SYSTEM_DEFAULT_TRANSLATED) + if (userAgentName == userAgentHelperPointer->SYSTEM_DEFAULT_TRANSLATED) { // Display the system default user agent name. - userAgentLabelPointer->setText(UserAgentHelper::getTranslatedUserAgentNameFromDatabaseName(Settings::userAgent())); + userAgentLabelPointer->setText(userAgentHelperPointer->getTranslatedUserAgentNameFromDatabaseName(Settings::userAgent())); // Reset the palette. userAgentWidgetPointer->setPalette(defaultPalette); @@ -544,7 +517,31 @@ void DomainSettingsDialog::showAddMessageBox() QLineEdit::Normal, QString(), &okClicked); // Add the new domain if the user clicked OK. - if (okClicked) addDomain(newDomainName); + if (okClicked) + { + // Add the new domain. + DomainsDatabase::addDomain(newDomainName); + + // Submit all pending changes. This reloads the model from the database, getting the new domain added above. + domainsTableModelPointer->submitAll(); + + + // Find the index for the new domain. `-1` allows for multiple entries to be returned. + QModelIndexList newDomainIndex = domainsTableModelPointer->match(domainsTableModelPointer->index(0, domainsTableModelPointer->fieldIndex(DomainsDatabase::DOMAIN_NAME)), + Qt::DisplayRole, newDomainName, -1, Qt::MatchWrap); + + // Move to the new domain. If there are multiple domains with the same name, the new one should be the last in the list. + domainsListViewPointer->setCurrentIndex(newDomainIndex[newDomainIndex.size() - 1]); + + // Populate the domain settings. + domainSelected(domainsSelectionModelPointer->currentIndex()); + + // Update the UI. + updateUi(); + + // Emit the domain settings updated signal. + emit domainSettingsUpdated(); + } } void DomainSettingsDialog::showDeleteMessageBox() const @@ -606,9 +603,84 @@ void DomainSettingsDialog::showDeleteMessageBox() const // Update the Ui. updateUi(); + + // Emit the domain settings updated signal. + emit domainSettingsUpdated(); } } +void DomainSettingsDialog::updateDomStorageStatus() const +{ + // Instantiate tracking variables. + bool javaScriptEnabled; + bool localStorageEnabled; + + // Populate the JavaScript tracker. + switch (javaScriptComboBoxPointer->currentIndex()) + { + case (DomainsDatabase::SYSTEM_DEFAULT): + { + // Update the tracker according to the system default. + if (Settings::javaScriptEnabled()) + javaScriptEnabled = true; + else + javaScriptEnabled = false; + + break; + } + + case (DomainsDatabase::ENABLED): + { + // Update the tracker. + javaScriptEnabled = true; + + break; + } + + case (DomainsDatabase::DISABLED): + { + // Update the tracker. + javaScriptEnabled = false; + + break; + } + } + + // Populate the local storage tracker. + switch (localStorageComboBoxPointer->currentIndex()) + { + case (DomainsDatabase::SYSTEM_DEFAULT): + { + // Update the tracker according to the system default. + if (Settings::localStorageEnabled()) + localStorageEnabled = true; + else + localStorageEnabled = false; + + break; + } + + case (DomainsDatabase::ENABLED): + { + // Update the tracker. + localStorageEnabled = true; + + break; + } + + case (DomainsDatabase::DISABLED): + { + // Update the tracker. + localStorageEnabled = false; + + break; + } + } + + // Only enable DOM storage if both JavaScript and local storage are enabled. + domStorageComboBoxPointer->setEnabled(javaScriptEnabled && localStorageEnabled); +} + void DomainSettingsDialog::updateUi() const { // Update the delete button status. @@ -628,7 +700,7 @@ void DomainSettingsDialog::userAgentChanged(const QString &updatedUserAgent) con { // Update the domains table model. domainsTableModelPointer->setData(domainsSelectionModelPointer->currentIndex().siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabase::USER_AGENT)), - UserAgentHelper::getDatabaseUserAgentNameFromTranslatedName(updatedUserAgent)); + userAgentHelperPointer->getDatabaseUserAgentNameFromTranslatedName(updatedUserAgent)); // Populate the user agent label. populateUserAgentLabel(updatedUserAgent); @@ -659,7 +731,7 @@ void DomainSettingsDialog::zoomFactorComboBoxChanged(const int &newIndex) const // Display the custom zoom factor from the domain settings. customZoomFactorSpinBoxPointer->setValue(modelIndex.siblingAtColumn(domainsTableModelPointer->fieldIndex(DomainsDatabase::CUSTOM_ZOOM_FACTOR)).data().toDouble()); - // Set the palette. + // Use the highlighted palette. zoomFactorWidgetPointer->setPalette(highlightedPalette); } diff --git a/src/dialogs/DomainSettingsDialog.h b/src/dialogs/DomainSettingsDialog.h index 7680a03..a78d6e3 100644 --- a/src/dialogs/DomainSettingsDialog.h +++ b/src/dialogs/DomainSettingsDialog.h @@ -1,5 +1,5 @@ /* - * Copyright © 2022 Soren Stoutner . + * Copyright 2022,2024 Soren Stoutner . * * This file is part of Privacy Browser PC . * @@ -20,6 +20,9 @@ #ifndef DOMAINSETTINGSDIALOG_H #define DOMAINSETTINGSDIALOG_H +// Application headers. +#include "helpers/UserAgentHelper.h" + // KDE Frameworks headers. #include @@ -36,7 +39,7 @@ class DomainSettingsDialog : public QDialog public: // The primary constructor. - explicit DomainSettingsDialog(const int &startType = SHOW_ALL_DOMAINS, const QString &domainName = QStringLiteral("")); + explicit DomainSettingsDialog(QWidget *parentWidgetPointer, const int &startType = SHOW_ALL_DOMAINS, const QString &domainName = QStringLiteral("")); // The public static int constants. static const int SHOW_ALL_DOMAINS; @@ -87,16 +90,17 @@ private: QPushButton *resetButtonPointer; QWidget *userAgentWidgetPointer; QComboBox *userAgentComboBoxPointer; + UserAgentHelper *userAgentHelperPointer; QLabel *userAgentLabelPointer; QWidget *zoomFactorWidgetPointer; QComboBox *zoomFactorComboBoxPointer; // The private functions. - void addDomain(const QString &domainName) const; void populateDomStorageLabel() const; void populateJavaScriptLabel() const; void populateLocalStorageLabel() const; void populateUserAgentLabel(const QString &userAgentName) const; + void updateDomStorageStatus() const; void updateUi() const; }; #endif diff --git a/src/dialogs/DurableCookiesDialog.cpp b/src/dialogs/DurableCookiesDialog.cpp index d998c20..f076985 100644 --- a/src/dialogs/DurableCookiesDialog.cpp +++ b/src/dialogs/DurableCookiesDialog.cpp @@ -1,5 +1,5 @@ /* - * Copyright © 2022 Soren Stoutner . + * Copyright 2022-2024 Soren Stoutner . * * This file is part of Privacy Browser PC . * @@ -26,7 +26,7 @@ // KDE Frameworks headers. #include -DurableCookiesDialog::DurableCookiesDialog() : QDialog(nullptr) +DurableCookiesDialog::DurableCookiesDialog(QWidget *parentWidgetPointer) : QDialog(parentWidgetPointer) { // Set the dialog window title. setWindowTitle(i18nc("The durable cookies dialog window title", "Durable Cookies")); @@ -150,7 +150,7 @@ void DurableCookiesDialog::beforeUpdate(int row, QSqlRecord &sqlRecord) const if (sqlRecord.isGenerated(CookiesDatabase::DOMAIN) || sqlRecord.isGenerated(CookiesDatabase::NAME) || sqlRecord.isGenerated(CookiesDatabase::PATH)) { // Get the ID of the cookie - int id = sqlRecord.value(CookiesDatabase::_ID).toInt(); + int id = sqlRecord.value(CookiesDatabase::ID).toInt(); // Get the cookie. QNetworkCookie *cookiePointer = CookiesDatabase::getCookieById(id); diff --git a/src/dialogs/DurableCookiesDialog.h b/src/dialogs/DurableCookiesDialog.h index 61ffe5f..64294f8 100644 --- a/src/dialogs/DurableCookiesDialog.h +++ b/src/dialogs/DurableCookiesDialog.h @@ -1,5 +1,5 @@ /* - * Copyright © 2022 Soren Stoutner . + * Copyright 2022, 2024 Soren Stoutner . * * This file is part of Privacy Browser PC . * @@ -33,7 +33,7 @@ class DurableCookiesDialog : public QDialog public: // The default constructor. - explicit DurableCookiesDialog(); + explicit DurableCookiesDialog(QWidget *parentWidgetPointer); signals: // The signals. @@ -60,7 +60,7 @@ private: QPushButton *deleteAllCookiesButtonPointer; QPushButton *deleteCookieButtonPointer; QSqlTableModel *durableCookiesTableModelPointer; - QItemSelectionModel *tableSelectionModelPointer; QPushButton *resetButtonPointer; + QItemSelectionModel *tableSelectionModelPointer; }; #endif diff --git a/src/dialogs/EditBookmarkDialog.cpp b/src/dialogs/EditBookmarkDialog.cpp new file mode 100644 index 0000000..bad010e --- /dev/null +++ b/src/dialogs/EditBookmarkDialog.cpp @@ -0,0 +1,202 @@ +/* + * Copyright 2023-2024 Soren Stoutner . + * + * This file is part of Privacy Browser PC . + * + * Privacy Browser PC 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. + * + * Privacy Browser PC 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 Privacy Browser PC. If not, see . + */ + +// Application headers. +#include "EditBookmarkDialog.h" +#include "ui_EditBookmarkDialog.h" +#include "databases/BookmarksDatabase.h" + +// Qt toolkit headers. +#include + +// Construct the class. +EditBookmarkDialog::EditBookmarkDialog(QWidget *parentWidgetPointer, const int databaseId, QIcon ¤tWebsiteFavoriteIcon) : QDialog(parentWidgetPointer), bookmarkDatabaseId(databaseId) +{ + // Set the window title. + setWindowTitle(i18nc("The edit bookmark dialog window title.", "Edit Bookmark")); + + // Set the window modality. + setWindowModality(Qt::WindowModality::ApplicationModal); + + // Instantiate the edit bookmark dialog UI. + Ui::EditBookmarkDialog editBookmarkDialogUi; + + // Setup the UI. + editBookmarkDialogUi.setupUi(this); + + // Get handles for the widgets. + currentFavoriteIconRadioButtonPointer = editBookmarkDialogUi.currentFavoriteIconRadioButton; + currentWebsiteFavoriteIconRadioButtonPointer = editBookmarkDialogUi.currentWebsiteFavoriteIconRadioButton; + customFavoriteIconRadioButtonPointer = editBookmarkDialogUi.customFavoriteIconRadioButton; + parentFolderTreeWidgetPointer = editBookmarkDialogUi.parentFolderTreeWidget; + bookmarkNameLineEditPointer = editBookmarkDialogUi.bookmarkNameLineEdit; + bookmarkUrlLineEditPointer = editBookmarkDialogUi.bookmarkUrlLineEdit; + QPushButton *browseButtonPointer = editBookmarkDialogUi.browseButton; + QDialogButtonBox *dialogButtonBoxPointer = editBookmarkDialogUi.dialogButtonBox; + saveButtonPointer = dialogButtonBoxPointer->button(QDialogButtonBox::Save); + + // Get the bookmark struct. + bookmarkStructPointer = BookmarksDatabase::getBookmark(databaseId); + + // Set the favorite icons. + currentFavoriteIconRadioButtonPointer->setIcon(bookmarkStructPointer->favoriteIcon); + currentWebsiteFavoriteIconRadioButtonPointer->setIcon(currentWebsiteFavoriteIcon); + customFavoriteIconRadioButtonPointer->setIcon(QIcon::fromTheme(QLatin1String("globe"), QIcon::fromTheme(QLatin1String("applications-internet")))); + + // Instantiate a folder helper. + folderHelperPointer = new FolderHelper(); + + // Set the parent folder tree widget column count. + parentFolderTreeWidgetPointer->setColumnCount(2); + + // Hide the second column. + parentFolderTreeWidgetPointer->hideColumn(folderHelperPointer->FOLDER_ID_COLUMN); + + // Set the column header. + parentFolderTreeWidgetPointer->setHeaderLabel(i18nc("The folder tree widget header", "Select Parent Folder")); + + // Create a bookmarks tree widget item. + QTreeWidgetItem *bookmarksTreeWidgetItemPointer = new QTreeWidgetItem(); + + // Populate the bookmarks tree widget item. + bookmarksTreeWidgetItemPointer->setText(folderHelperPointer->FOLDER_NAME_COLUMN, i18nc("The bookmarks root tree widget name", "Bookmarks")); + bookmarksTreeWidgetItemPointer->setIcon(folderHelperPointer->FOLDER_NAME_COLUMN, QIcon::fromTheme(QLatin1String("bookmarks"), QIcon::fromTheme(QLatin1String("bookmark-new")))); + bookmarksTreeWidgetItemPointer->setText(folderHelperPointer->FOLDER_ID_COLUMN, QLatin1String("0")); + + // Add the bookmarks tree widget item to the root of the tree. + parentFolderTreeWidgetPointer->addTopLevelItem(bookmarksTreeWidgetItemPointer); + + // Select the root bookmarks folder if it is the initial parent folder. + if (bookmarkStructPointer->parentFolderId == 0) + bookmarksTreeWidgetItemPointer->setSelected(true); + + // Populate the subfolders. + folderHelperPointer->populateSubfolders(bookmarksTreeWidgetItemPointer, bookmarkStructPointer->parentFolderId); + + // Open all the folders. + parentFolderTreeWidgetPointer->expandAll(); + + // Populate the line edits. + bookmarkNameLineEditPointer->setText(bookmarkStructPointer->name); + bookmarkUrlLineEditPointer->setText(bookmarkStructPointer->url); + + // Scroll to the beginning of the bookmark URL line edit. + bookmarkUrlLineEditPointer->setCursorPosition(0); + + // Focus the bookmark name line edit. + bookmarkNameLineEditPointer->setFocus(); + + // Connect the buttons. + connect(browseButtonPointer, SIGNAL(clicked()), this, SLOT(browse())); + connect(dialogButtonBoxPointer, SIGNAL(accepted()), this, SLOT(save())); + connect(dialogButtonBoxPointer, SIGNAL(rejected()), this, SLOT(reject())); + + // Update the UI when the line edits change. + connect(bookmarkNameLineEditPointer, SIGNAL(textEdited(const QString&)), this, SLOT(updateUi())); + connect(bookmarkUrlLineEditPointer, SIGNAL(textEdited(const QString&)), this, SLOT(updateUi())); + + // Set the initial UI status. + updateUi(); +} + +void EditBookmarkDialog::browse() +{ + // Get an image file string from the user. + QString imageFileString = QFileDialog::getOpenFileName(this, i18nc("The browse for favorite icon dialog header", "Favorite Icon Image"), QDir::homePath(), + i18nc("The browse for image files filter", "Image Files — *.bmp, *.gif, *.jpg, *.jpeg, *.png, *.svg(*.bmp *.gif *.jpg *.jpeg *.png *.svg);;All Files(*)")); + + + // Check to see if an image file string was returned. This will be empty if the user selected cancel. + if (!imageFileString.isEmpty()) + { + // Set the custom favorite icon. + customFavoriteIconRadioButtonPointer->setIcon(QIcon(imageFileString)); + + // Check the custom favorite icon radio button. + customFavoriteIconRadioButtonPointer->setChecked(true); + } +} + +void EditBookmarkDialog::save() +{ + // Get the selected folders list. + QList selectedFoldersList = parentFolderTreeWidgetPointer->selectedItems(); + + // Get the selected folder. + QTreeWidgetItem *selectedFolderPointer = selectedFoldersList.first(); + + // Get the parent folder ID. + double parentFolderId = selectedFolderPointer->text(folderHelperPointer->FOLDER_ID_COLUMN).toDouble(); + + // Get the original display order. + int displayOrder = bookmarkStructPointer->displayOrder; + + // Get the new display order if the parent folder has changed. + if (parentFolderId != bookmarkStructPointer->parentFolderId) + displayOrder = BookmarksDatabase::getFolderItemCount(parentFolderId); + + // Create a favorite icon. + QIcon favoriteIcon; + + // Get the favorite icon. + if (currentFavoriteIconRadioButtonPointer->isChecked()) // The current favorite icon is checked. + favoriteIcon = currentFavoriteIconRadioButtonPointer->icon(); + else if (currentWebsiteFavoriteIconRadioButtonPointer->isChecked()) // The current website favorite icon is checked. + favoriteIcon = currentWebsiteFavoriteIconRadioButtonPointer->icon(); + else // The custom favorite icon is checked. + favoriteIcon = customFavoriteIconRadioButtonPointer->icon(); + + // Create a bookmark struct. + BookmarkStruct *updatedBookmarkStructPointer = new BookmarkStruct; + + // Populate the bookmark struct. + updatedBookmarkStructPointer->databaseId = bookmarkDatabaseId; + updatedBookmarkStructPointer->name = bookmarkNameLineEditPointer->text(); + updatedBookmarkStructPointer->url = bookmarkUrlLineEditPointer->text(); + updatedBookmarkStructPointer->parentFolderId = parentFolderId; + updatedBookmarkStructPointer->displayOrder = displayOrder; + updatedBookmarkStructPointer->favoriteIcon = favoriteIcon; + + // Update the bookmark. + BookmarksDatabase::updateBookmark(updatedBookmarkStructPointer); + + // Update the display order of all the items in the previous folder. + BookmarksDatabase::updateFolderContentsDisplayOrder(bookmarkStructPointer->parentFolderId); + + // Emit the bookmark saved signal. + emit bookmarkSaved(); + + // Close the dialog. + close(); +} + +void EditBookmarkDialog::updateUi() +{ + // Determine if both line edits are populated. + if (bookmarkNameLineEditPointer->text().isEmpty() || bookmarkUrlLineEditPointer->text().isEmpty()) // At least one of the line edits is empty. + { + // Disable the save button. + saveButtonPointer->setEnabled(false); + } + else // Both of the line edits are populated. + { + // Enable the save button. + saveButtonPointer->setEnabled(true); + } +} diff --git a/src/dialogs/EditBookmarkDialog.h b/src/dialogs/EditBookmarkDialog.h new file mode 100644 index 0000000..f862bc0 --- /dev/null +++ b/src/dialogs/EditBookmarkDialog.h @@ -0,0 +1,66 @@ +/* + * Copyright 2023-2024 Soren Stoutner . + * + * This file is part of Privacy Browser PC . + * + * Privacy Browser PC 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. + * + * Privacy Browser PC 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 Privacy Browser PC. If not, see . + */ + +#ifndef EDITBOOKMARKDIALOG_H +#define EDITBOOKMARKDIALOG_H + +// Application headers. +#include "helpers/FolderHelper.h" +#include "structs/BookmarkStruct.h" + +// Qt toolkit headers. +#include +#include +#include + +class EditBookmarkDialog : public QDialog +{ + // Include the Q_OBJECT macro. + Q_OBJECT + +public: + // The primary constructor. + explicit EditBookmarkDialog(QWidget *parentWidgetPointer, const int databaseId, QIcon ¤tWebsiteFavoriteIcon); + +signals: + // The signals. + void bookmarkSaved() const; + +private Q_SLOTS: + // The private slots. + void browse(); + void save(); + void updateUi(); + +private: + // The private variables. + FolderHelper *folderHelperPointer; + + // The private widgets. + int bookmarkDatabaseId; + QLineEdit *bookmarkNameLineEditPointer; + BookmarkStruct *bookmarkStructPointer; + QLineEdit *bookmarkUrlLineEditPointer; + QRadioButton *currentFavoriteIconRadioButtonPointer; + QRadioButton *currentWebsiteFavoriteIconRadioButtonPointer; + QRadioButton *customFavoriteIconRadioButtonPointer; + QTreeWidget *parentFolderTreeWidgetPointer; + QPushButton *saveButtonPointer; +}; +#endif diff --git a/src/dialogs/EditFolderDialog.cpp b/src/dialogs/EditFolderDialog.cpp new file mode 100644 index 0000000..bae0965 --- /dev/null +++ b/src/dialogs/EditFolderDialog.cpp @@ -0,0 +1,191 @@ +/* + * Copyright 2023-2024 Soren Stoutner . + * + * This file is part of Privacy Browser PC . + * + * Privacy Browser PC 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. + * + * Privacy Browser PC 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 Privacy Browser PC. If not, see . + */ + +// Application headers. +#include "EditFolderDialog.h" +#include "ui_EditFolderDialog.h" +#include "databases/BookmarksDatabase.h" + +// Qt toolkit headers. +#include + +// Construct the class. +EditFolderDialog::EditFolderDialog(QWidget *parentWidgetPointer, const int databaseId, QIcon ¤tWebsiteFavoriteIcon) : QDialog(parentWidgetPointer), folderDatabaseId(databaseId) +{ + // Set the window title. + setWindowTitle(i18nc("The edit folder dialog window title.", "Edit Folder")); + + // Set the window modality. + setWindowModality(Qt::WindowModality::ApplicationModal); + + // Instantiate the edit folder dialog UI. + Ui::EditFolderDialog editFolderDialogUi; + + // Setup the UI. + editFolderDialogUi.setupUi(this); + + // Get handles for the widgets. + currentFolderIconRadioButtonPointer = editFolderDialogUi.currentFolderIconRadioButton; + defaultFolderIconRadioButtonPointer = editFolderDialogUi.defaultFolderIconRadioButton; + currentWebsiteFavoriteIconRadioButtonPointer = editFolderDialogUi.currentWebsiteFavoriteIconRadioButton; + customFolderIconRadioButtonPointer = editFolderDialogUi.customFolderIconRadioButton; + parentFolderTreeWidgetPointer = editFolderDialogUi.parentFolderTreeWidget; + folderNameLineEditPointer = editFolderDialogUi.folderNameLineEdit; + QPushButton *browseButtonPointer = editFolderDialogUi.browseButton; + QDialogButtonBox *dialogButtonBoxPointer = editFolderDialogUi.dialogButtonBox; + saveButtonPointer = dialogButtonBoxPointer->button(QDialogButtonBox::Save); + + // Get the folder bookmark struct. + folderBookmarkStructPointer = BookmarksDatabase::getBookmark(databaseId); + + // Set the folder icons. + currentFolderIconRadioButtonPointer->setIcon(folderBookmarkStructPointer->favoriteIcon); + currentWebsiteFavoriteIconRadioButtonPointer->setIcon(currentWebsiteFavoriteIcon); + + // Instantiate a folder helper. + folderHelperPointer = new FolderHelper(); + + // Set the parent folder tree widget column count. + parentFolderTreeWidgetPointer->setColumnCount(2); + + // Hide the second column. + parentFolderTreeWidgetPointer->hideColumn(folderHelperPointer->FOLDER_ID_COLUMN); + + // Set the column header. + parentFolderTreeWidgetPointer->setHeaderLabel(i18nc("The folder tree widget header", "Select Parent Folder")); + + // Create a bookmarks tree widget item. + QTreeWidgetItem *bookmarksTreeWidgetItemPointer = new QTreeWidgetItem(); + + // Populate the bookmarks tree widget item. + bookmarksTreeWidgetItemPointer->setText(folderHelperPointer->FOLDER_NAME_COLUMN, i18nc("The bookmarks root tree widget name", "Bookmarks")); + bookmarksTreeWidgetItemPointer->setIcon(folderHelperPointer->FOLDER_NAME_COLUMN, QIcon::fromTheme(QLatin1String("bookmarks"), QIcon::fromTheme(QLatin1String("bookmark-new")))); + bookmarksTreeWidgetItemPointer->setText(folderHelperPointer->FOLDER_ID_COLUMN, QLatin1String("0")); + + // Add the bookmarks tree widget item to the root of the tree. + parentFolderTreeWidgetPointer->addTopLevelItem(bookmarksTreeWidgetItemPointer); + + // Select the root bookmarks folder if it is the initial parent folder. + if (folderBookmarkStructPointer->parentFolderId == 0) + bookmarksTreeWidgetItemPointer->setSelected(true); + + // Populate the subfolders, except for the one being edited. + folderHelperPointer->populateSubfoldersExcept(databaseId, bookmarksTreeWidgetItemPointer, folderBookmarkStructPointer->parentFolderId); + + // Open all the folders. + parentFolderTreeWidgetPointer->expandAll(); + + // Populate the line edits. + folderNameLineEditPointer->setText(folderBookmarkStructPointer->name); + + // Focus the folder name line edit. + folderNameLineEditPointer->setFocus(); + + // Connect the buttons. + connect(browseButtonPointer, SIGNAL(clicked()), this, SLOT(browse())); + connect(dialogButtonBoxPointer, SIGNAL(accepted()), this, SLOT(save())); + connect(dialogButtonBoxPointer, SIGNAL(rejected()), this, SLOT(reject())); + + // Update the UI when the line edit changes. + connect(folderNameLineEditPointer, SIGNAL(textEdited(const QString&)), this, SLOT(updateUi())); + + // Set the initial UI status. + updateUi(); +} + +void EditFolderDialog::browse() +{ + // Get an image file string from the user. + QString imageFileString = QFileDialog::getOpenFileName(this, i18nc("The browse for folder icon dialog header", "Folder Icon Image"), QDir::homePath(), + i18nc("The browse for image files filter", "Image Files — *.bmp, *.gif, *.jpg, *.jpeg, *.png, *.svg(*.bmp *.gif *.jpg *.jpeg *.png *.svg);;All Files(*)")); + + // Check to see if an image file string was returned. This will be empty if the user selected cancel. + if (!imageFileString.isEmpty()) + { + // Set the custom folder icon. + customFolderIconRadioButtonPointer->setIcon(QIcon(imageFileString)); + + // Check the custom folder icon radio button. + customFolderIconRadioButtonPointer->setChecked(true); + } +} + +void EditFolderDialog::save() +{ + // Get the selected folders list. + QList selectedFoldersList = parentFolderTreeWidgetPointer->selectedItems(); + + // Get the selected folder. + QTreeWidgetItem *selectedFolderPointer = selectedFoldersList.first(); + + // Get the parent folder ID. + double parentFolderId = selectedFolderPointer->text(folderHelperPointer->FOLDER_ID_COLUMN).toDouble(); + + // Determine if it has moved to a new folder. + bool movedToNewFolder = parentFolderId != folderBookmarkStructPointer->parentFolderId; + + // Get the original display order. + int displayOrder = folderBookmarkStructPointer->displayOrder; + + // Get the new display order if the parent folder has changed. + if (movedToNewFolder) + displayOrder = BookmarksDatabase::getFolderItemCount(parentFolderId); + + // Create a favorite icon. + QIcon favoriteIcon; + + // Get the favorite icon. + if (currentFolderIconRadioButtonPointer->isChecked()) // The current folder icon is checked. + favoriteIcon = currentFolderIconRadioButtonPointer->icon(); + else if (defaultFolderIconRadioButtonPointer->isChecked()) // The default folder icon is checked. + favoriteIcon = defaultFolderIconRadioButtonPointer->icon(); + else if (currentWebsiteFavoriteIconRadioButtonPointer->isChecked()) // The current website favorite icon is checked. + favoriteIcon = currentWebsiteFavoriteIconRadioButtonPointer->icon(); + else // The custom favorite icon is checked. + favoriteIcon = customFolderIconRadioButtonPointer->icon(); + + // Create a bookmark struct. + BookmarkStruct *updatedBookmarkStructPointer = new BookmarkStruct; + + // Populate the bookmark struct. + updatedBookmarkStructPointer->databaseId = folderDatabaseId; + updatedBookmarkStructPointer->name = folderNameLineEditPointer->text(); + updatedBookmarkStructPointer->parentFolderId = parentFolderId; + updatedBookmarkStructPointer->displayOrder = displayOrder; + updatedBookmarkStructPointer->favoriteIcon = favoriteIcon; + + // Update the folder. + BookmarksDatabase::updateBookmark(updatedBookmarkStructPointer); + + // Update the display order of all the items in the previous folder if it has moved to a new folder. + if (movedToNewFolder) + BookmarksDatabase::updateFolderContentsDisplayOrder(folderBookmarkStructPointer->parentFolderId); + + // Emit the folder saved signal. + emit folderSaved(); + + // Close the dialog. + close(); +} + +void EditFolderDialog::updateUi() +{ + // Set the status of the save button. + saveButtonPointer->setEnabled(!folderNameLineEditPointer->text().isEmpty()); +} diff --git a/src/dialogs/EditFolderDialog.h b/src/dialogs/EditFolderDialog.h new file mode 100644 index 0000000..899d5af --- /dev/null +++ b/src/dialogs/EditFolderDialog.h @@ -0,0 +1,65 @@ +/* + * Copyright 2023-2024 Soren Stoutner . + * + * This file is part of Privacy Browser PC . + * + * Privacy Browser PC 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. + * + * Privacy Browser PC 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 Privacy Browser PC. If not, see . + */ + +#ifndef EDITFOLDERDIALOG_H +#define EDITFOLDERDIALOG_H + +// Application headers. +#include "helpers/FolderHelper.h" +#include "structs/BookmarkStruct.h" + +// Qt toolkit headers. +#include +#include + +class EditFolderDialog : public QDialog +{ + // Include the Q_OBJECT macro. + Q_OBJECT + +public: + // The primary constructor. + explicit EditFolderDialog(QWidget *parentWidgetPointer, const int databaseId, QIcon ¤tWebsiteFavoriteIcon); + +signals: + // The signals. + void folderSaved() const; + +private Q_SLOTS: + // The private slots. + void browse(); + void save(); + void updateUi(); + +private: + // The private variables. + FolderHelper *folderHelperPointer; + + // The private widgets. + QRadioButton *currentFolderIconRadioButtonPointer; + QRadioButton *currentWebsiteFavoriteIconRadioButtonPointer; + QRadioButton *customFolderIconRadioButtonPointer; + QRadioButton *defaultFolderIconRadioButtonPointer; + BookmarkStruct *folderBookmarkStructPointer; + int folderDatabaseId; + QLineEdit *folderNameLineEditPointer; + QTreeWidget *parentFolderTreeWidgetPointer; + QPushButton *saveButtonPointer; +}; +#endif diff --git a/src/dialogs/HttpAuthenticationDialog.cpp b/src/dialogs/HttpAuthenticationDialog.cpp new file mode 100644 index 0000000..650ce99 --- /dev/null +++ b/src/dialogs/HttpAuthenticationDialog.cpp @@ -0,0 +1,91 @@ +/* + * Copyright 2024 Soren Stoutner . + * + * This file is part of Privacy Browser PC . + * + * Privacy Browser PC 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. + * + * Privacy Browser PC 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 Privacy Browser PC. If not, see . + */ + +// Application headers. +#include "HttpAuthenticationDialog.h" +#include "ui_HttpAuthenticationDialog.h" + +// Qt toolkit headers. +#include +#include + +// Construct the class. +HttpAuthenticationDialog::HttpAuthenticationDialog(QWidget *parentWidgetPointer, const QUrl &requestUrl, QAuthenticator *authenticatorPointer) : + QDialog(parentWidgetPointer), authenticatorPointer(authenticatorPointer) +{ + // Set the window title. + setWindowTitle(i18nc("The HTTP authentication dialog window title.", "HTTP Authentication")); + + // Set the window modality. + setWindowModality(Qt::WindowModality::ApplicationModal); + + // Instantiate the HTTP authentication dialog UI. + Ui::HttpAuthenticationDialog httpAuthenticationDialogUi; + + // Setup the UI. + httpAuthenticationDialogUi.setupUi(this); + + // Get handles for the widgets. + QLabel *realmLabelPointer = httpAuthenticationDialogUi.realmLabel; + QLabel *hostLabelPointer = httpAuthenticationDialogUi.hostLabel; + usernameLineEditPointer = httpAuthenticationDialogUi.usernameLineEdit; + passwordLineEditPointer = httpAuthenticationDialogUi.passwordLineEdit; + QDialogButtonBox *dialogButtonBoxPointer = httpAuthenticationDialogUi.dialogButtonBox; + okButtonPointer = dialogButtonBoxPointer->button(QDialogButtonBox::Ok); + + // Display the labels. + realmLabelPointer->setText(authenticatorPointer->realm()); + hostLabelPointer->setText(requestUrl.host()); + + // Connect the buttons. + connect(dialogButtonBoxPointer, SIGNAL(accepted()), this, SLOT(authenticate())); + connect(dialogButtonBoxPointer, SIGNAL(rejected()), this, SLOT(reject())); + + // Update the UI when the line edits change. + connect(usernameLineEditPointer, SIGNAL(textEdited(const QString&)), this, SLOT(updateUi())); + connect(passwordLineEditPointer, SIGNAL(passwordChanged(const QString&)), this, SLOT(updateUi())); + + // Initially disable the OK button. + okButtonPointer->setEnabled(false); +} + +void HttpAuthenticationDialog::authenticate() +{ + // Populate the authenticator. + authenticatorPointer->setUser(usernameLineEditPointer->text()); + authenticatorPointer->setPassword(passwordLineEditPointer->password()); + + // Close the dialog. + close(); +} + +void HttpAuthenticationDialog::updateUi() +{ + // Update the OK button status + if (usernameLineEditPointer->text().isEmpty() || passwordLineEditPointer->password().isEmpty()) // At least one of the line edits is empty. + { + // Disable the OK button. + okButtonPointer->setEnabled(false); + } + else // Both of the line edits are populated. + { + // Enable the OK button. + okButtonPointer->setEnabled(true); + } +} diff --git a/src/dialogs/HttpAuthenticationDialog.h b/src/dialogs/HttpAuthenticationDialog.h new file mode 100644 index 0000000..201067d --- /dev/null +++ b/src/dialogs/HttpAuthenticationDialog.h @@ -0,0 +1,54 @@ +/* + * Copyright 2024 Soren Stoutner . + * + * This file is part of Privacy Browser PC . + * + * Privacy Browser PC 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. + * + * Privacy Browser PC 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 Privacy Browser PC. If not, see . + */ + +#ifndef HTTPAUTHENTICATIONDIALOG_H +#define HTTPAUTHENTICATIONDIALOG_H + +// KDE framework headers. +#include +#include + +// Qt toolkit headers. +#include +#include + +class HttpAuthenticationDialog : public QDialog +{ + // Include the Q_OBJECT macro. + Q_OBJECT + +public: + // The primary constructor. + explicit HttpAuthenticationDialog(QWidget *parentWidgetPointer, const QUrl &requestUrl, QAuthenticator *authenticatorPointer); + +private Q_SLOTS: + // The private slots. + void authenticate(); + void updateUi(); + +private: + // The private variables. + QAuthenticator *authenticatorPointer; + + // The private widgets. + QPushButton *okButtonPointer; + KPasswordLineEdit *passwordLineEditPointer; + KLineEdit *usernameLineEditPointer; +}; +#endif diff --git a/src/dialogs/SaveDialog.cpp b/src/dialogs/SaveDialog.cpp index 281cc77..dac011c 100644 --- a/src/dialogs/SaveDialog.cpp +++ b/src/dialogs/SaveDialog.cpp @@ -1,5 +1,5 @@ /* - * Copyright © 2022 Soren Stoutner . + * Copyright 2022-2024 Soren Stoutner . * * This file is part of Privacy Browser PC . * @@ -30,7 +30,8 @@ #include #include -SaveDialog::SaveDialog(QWebEngineDownloadItem *downloadItemPointer) +SaveDialog::SaveDialog(QWidget *parentWidgetPointer, QUrl &url, QString &mimeTypeString, int totalBytes, QString fileName, bool nativeDownloader) : + QDialog(parentWidgetPointer), downloadUrl(url), suggestedFileName(fileName) { // Set the dialog window title. setWindowTitle(i18nc("The save dialog window title", "Save")); @@ -53,11 +54,6 @@ SaveDialog::SaveDialog(QWebEngineDownloadItem *downloadItemPointer) QDialogButtonBox *dialogButtonBoxPointer = saveDialogUi.dialogButtonBox; QPushButton *saveButtonPointer = dialogButtonBoxPointer->button(QDialogButtonBox::Save); - // Get the URL and the suggested file name. - downloadUrl = downloadItemPointer->url(); - suggestedFileName = downloadItemPointer->suggestedFileName(); - QString mimeTypeString = downloadItemPointer->mimeType(); - // Get a MIME type database. QMimeDatabase mimeDatabase; @@ -85,28 +81,40 @@ SaveDialog::SaveDialog(QWebEngineDownloadItem *downloadItemPointer) mimeTypeLabelPointer->setText("" + mimeTypeString + ""); // Populate the download size label. - if (downloadItemPointer->totalBytes() == -1) // The file size is unknown. + if (totalBytes == -1) // The file size is unknown. sizeLabelPointer->setText(i18nc("Unknown download file size. The bold style should be preserved.", "unknown")); else // The file size is known. Format it according to the locale. - sizeLabelPointer->setText(ki18nc("Download file size. The bold style should be preserved.", "%1 bytes").subs(downloadItemPointer->totalBytes()).toString()); - - // Connect the buttons. - connect(saveButtonPointer, SIGNAL(clicked()), this, SLOT(showFileDialog())); - connect(dialogButtonBoxPointer, SIGNAL(rejected()), this, SLOT(reject())); + sizeLabelPointer->setText(ki18nc("Download file size. The bold style should be preserved.", "%1 bytes").subs(totalBytes).toString()); // Create the keyboard shortcuts. QShortcut *sShortcutPointer = new QShortcut(QKeySequence(i18nc("The save key shortcut.", "s")), this); QShortcut *cShortcutPointer = new QShortcut(QKeySequence(i18nc("The close key shortcut.", "c")), this); - - // Connect the shortcuts. - connect(sShortcutPointer, SIGNAL(activated()), this, SLOT(showFileDialog())); + QShortcut *quitShortcutPointer = new QShortcut(QKeySequence::Quit, this); + + // Connect the save buttons. + if (nativeDownloader) + { + // Show the file picker for the native download. + connect(saveButtonPointer, SIGNAL(clicked()), this, SLOT(showFilePicker())); + connect(sShortcutPointer, SIGNAL(activated()), this, SLOT(showFilePicker())); + } + else + { + // Use WebEngine's downloader. + connect(saveButtonPointer, SIGNAL(clicked()), this, SLOT(accept())); + connect(sShortcutPointer, SIGNAL(activated()), this, SLOT(accept())); + } + + // Connect the cancel button. + connect(dialogButtonBoxPointer, SIGNAL(rejected()), this, SLOT(reject())); connect(cShortcutPointer, SIGNAL(activated()), this, SLOT(reject())); + connect(quitShortcutPointer, SIGNAL(activated()), this, SLOT(reject())); } -void SaveDialog::showFileDialog() +void SaveDialog::showFilePicker() { // Show the file picker dialog. - emit showSaveFilePickerDialog(downloadUrl, suggestedFileName); + emit useNativeKdeDownloader(downloadUrl, suggestedFileName); // Close the dialog. reject(); diff --git a/src/dialogs/SaveDialog.h b/src/dialogs/SaveDialog.h index 36354c1..a994d18 100644 --- a/src/dialogs/SaveDialog.h +++ b/src/dialogs/SaveDialog.h @@ -1,5 +1,5 @@ /* - * Copyright © 2022 Soren Stoutner . + * Copyright 2022, 2024 Soren Stoutner . * * This file is part of Privacy Browser PC . * @@ -32,15 +32,15 @@ class SaveDialog : public QDialog public: // The primary constructor. - explicit SaveDialog(QWebEngineDownloadItem *downloadItemPointer); + explicit SaveDialog(QWidget *parentWidgetPointer, QUrl &url, QString &mimeTypeString, int totalBytes, QString fileName = QString(), bool nativeDownloader = false); signals: // The signals. - void showSaveFilePickerDialog(QUrl &downloadUrl, QString &suggestedFileName); + void useNativeKdeDownloader(QUrl &downloadUrl, QString &suggestedFileName); private Q_SLOTS: // The private slots. - void showFileDialog(); + void showFilePicker(); private: // The private variables. diff --git a/src/dialogs/SettingsDialog.cpp b/src/dialogs/SettingsDialog.cpp new file mode 100644 index 0000000..1f3fb79 --- /dev/null +++ b/src/dialogs/SettingsDialog.cpp @@ -0,0 +1,231 @@ +/* + * Copyright 2024 Soren Stoutner . + * + * This file is part of Privacy Browser PC . + * + * Privacy Browser PC 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. + * + * Privacy Browser PC 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 Privacy Browser PC. If not, see . + */ + +// Application headers. +#include "Settings.h" +#include "SettingsDialog.h" +#include "helpers/SearchEngineHelper.h" +#include "helpers/UserAgentHelper.h" +#include "ui_SettingsGeneral.h" +#include "ui_SettingsPrivacy.h" +#include "ui_SettingsSpellCheck.h" + +// Qt toolkit headers. +#include +#include + +SettingsDialog::SettingsDialog(QWidget *parentWidgetPointer, KCoreConfigSkeleton *coreConfigSkeletonPointer) : + KConfigDialog(parentWidgetPointer, QLatin1String("settings"), coreConfigSkeletonPointer) +{ + // Instantiate the settings UI. + Ui::PrivacySettings privacySettingsUi; + Ui::GeneralSettings generalSettingsUi; + Ui::SpellCheckSettings spellCheckSettingsUi; + + // Create the settings widgets. + QWidget *privacySettingsWidgetPointer = new QWidget; + QWidget *generalSettingsWidgetPointer = new QWidget; + QWidget *spellCheckSettingsWidgetPointer = new QWidget; + + // Setup the UI to display the settings widgets. + privacySettingsUi.setupUi(privacySettingsWidgetPointer); + generalSettingsUi.setupUi(generalSettingsWidgetPointer); + spellCheckSettingsUi.setupUi(spellCheckSettingsWidgetPointer); + + // Get handles for the widgets. + QCheckBox *javaScriptCheckBoxPointer = privacySettingsUi.kcfg_javaScriptEnabled; + QCheckBox *localStorageCheckBoxPointer = privacySettingsUi.kcfg_localStorageEnabled; + QCheckBox *domStorageCheckBoxPointer = privacySettingsUi.kcfg_domStorageEnabled; + QComboBox *userAgentComboBoxPointer = privacySettingsUi.kcfg_userAgent; + userAgentLabelPointer = privacySettingsUi.userAgentLabel; + QComboBox *searchEngineComboBoxPointer = generalSettingsUi.kcfg_searchEngine; + searchEngineLabelPointer = generalSettingsUi.searchEngineLabel; + downloadDirectoryComboBoxPointer = generalSettingsUi.kcfg_downloadDirectory; + QPushButton *browseButtonPointer = generalSettingsUi.browseButton; + QListWidget *spellCheckListWidgetPointer = spellCheckSettingsUi.spellCheckListWidget; + + // Create a save spell check languages lambda. + auto updateCheckBoxes = [javaScriptCheckBoxPointer, localStorageCheckBoxPointer, domStorageCheckBoxPointer] () + { + // Only enable the DOM storage check box if both JavaScript and local storage are checked. + domStorageCheckBoxPointer->setEnabled(javaScriptCheckBoxPointer->isChecked() && localStorageCheckBoxPointer->isChecked()); + }; + + // Update the status of the DOM storage check box when either JavaScript or local storage are changed. + connect(javaScriptCheckBoxPointer, &QCheckBox::stateChanged, this, updateCheckBoxes); + connect(localStorageCheckBoxPointer, &QCheckBox::stateChanged, this, updateCheckBoxes); + + // Populate the combo box labels. + updateUserAgentLabel(userAgentComboBoxPointer->currentText()); + updateSearchEngineLabel(searchEngineComboBoxPointer->currentText()); + + // Update the labels when the combo boxes change. + connect(userAgentComboBoxPointer, SIGNAL(currentTextChanged(const QString)), this, SLOT(updateUserAgentLabel(const QString))); + connect(searchEngineComboBoxPointer, SIGNAL(currentTextChanged(const QString)), this, SLOT(updateSearchEngineLabel(const QString))); + + // Connect the download directory directory browse button. + connect(browseButtonPointer, SIGNAL(clicked()), this, SLOT(showDownloadDirectoryBrowseDialog())); + + // Create a dictionaries QDir from the `QTWEBENGINE_DICTIONARIES_PATH` environment variable. + QDir dictionariesDir = QDir(qEnvironmentVariable("QTWEBENGINE_DICTIONARIES_PATH")); + + // Get a dictionaries string list. + QStringList dictionariesStringList = dictionariesDir.entryList(QStringList(QLatin1String("*.bdic")), QDir::Files | QDir::NoSymLinks); + + // Remove the `.bdic` file extensions from the dictionaries list. + dictionariesStringList.replaceInStrings(QLatin1String(".bdic"), QLatin1String("")); + + // Get a list of the enabled spell check languages. + QStringList enabledSpellCheckLanguagesList = Settings::spellCheckLanguages(); + + // Add each dictionary to the spell check list widget. + foreach(QString dictionaryString, dictionariesStringList) + { + // Create a new list widget item pointer. + QListWidgetItem *listWidgetItemPointer = new QListWidgetItem(); + + // Create a dictionary check box widget with the name of the dictionary string. + QCheckBox *dictionaryCheckBoxWidget = new QCheckBox(dictionaryString); + + // Check the language if it is currently enabled. + if (enabledSpellCheckLanguagesList.contains(dictionaryString)) + dictionaryCheckBoxWidget->setCheckState(Qt::Checked); + else + dictionaryCheckBoxWidget->setCheckState(Qt::Unchecked); + + // Add the list widget item to the spell check list widget. + spellCheckListWidgetPointer->addItem(listWidgetItemPointer); + + // Set the list widget item check box widget. + spellCheckListWidgetPointer->setItemWidget(listWidgetItemPointer, dictionaryCheckBoxWidget); + } + + // Create a settings icon string. + QString settingsIconString; + + // Get a settings icon that matches the theme. + if (QIcon::hasThemeIcon("breeze-settings")) + { + // KDE uses breeze-settings. + settingsIconString = QLatin1String("breeze-settings"); + } + else + { + // Gnome uses preferences-desktop. + settingsIconString = QLatin1String("preferences-desktop"); + } + + // Add the settings widgets as config dialog pages. + addPage(privacySettingsWidgetPointer, i18nc("Settings tab title", "Privacy"), QLatin1String("privacybrowser")); + addPage(generalSettingsWidgetPointer, i18nc("Settings tab title", "General"), settingsIconString); + addPage(spellCheckSettingsWidgetPointer, i18nc("Settings tab title", "Spell Check"), QLatin1String("tools-check-spelling")); + + // Get handles for the buttons. + QPushButton *applyButtonPointer = button(QDialogButtonBox::Apply); + QPushButton *okButtonPointer = button(QDialogButtonBox::Ok); + + // Prevent interaction with the parent window while the dialog is open. + setWindowModality(Qt::WindowModal); + + // Create a save spell check languages lambda. + auto saveSpellCheckLanguages = [spellCheckListWidgetPointer, coreConfigSkeletonPointer, this] () + { + // Create a list of enabled languages. + QStringList newSpellCheckLanguages = QStringList(); + + // Get a count of all the languages. + int allLanguagesCount = spellCheckListWidgetPointer->count(); + + // Get a list of all the checked languages. + for (int i = 0; i < allLanguagesCount; ++i) { + // Get the language item. + QListWidgetItem *languageItemPointer = spellCheckListWidgetPointer->item(i); + + // Get the language check box. + QCheckBox *languageCheckBoxPointer = qobject_cast(spellCheckListWidgetPointer->itemWidget(languageItemPointer)); + + // Add the item to the enabled languages if it is checked. + if (languageCheckBoxPointer->checkState() == Qt::Checked) + { + // Get the text. + QString languageString = languageCheckBoxPointer->text(); + + // Remove all instances of `&`, which may have been added automatically when creating the check box text. + languageString.remove(QChar('&')); + + // Add the language string to the list. + newSpellCheckLanguages.append(languageString); + } + } + + // Update the spell check languages. + if (Settings::spellCheckLanguages() != newSpellCheckLanguages) + { + // Update the spell check languages. + Settings::setSpellCheckLanguages(newSpellCheckLanguages); + + // Write the settings to disk. + coreConfigSkeletonPointer->save(); + + // Emit the spell check languages updated signal. + emit spellCheckLanguagesUpdated(); + } + }; + + // Process clicks on the buttons. + connect(applyButtonPointer, &QPushButton::clicked, this, saveSpellCheckLanguages); + connect(okButtonPointer, &QPushButton::clicked, this, saveSpellCheckLanguages); +} + +void SettingsDialog::showDownloadDirectoryBrowseDialog() +{ + // Get the current download directory. + QString currentDownloadDirectory = downloadDirectoryComboBoxPointer->currentText(); + + // Resolve the system download directory if specified. + if (currentDownloadDirectory == QStringLiteral("System Download Directory")) + currentDownloadDirectory = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); + + // Get the new download directory. + QString newDownloadDirectory = QFileDialog::getExistingDirectory(this, i18nc("Select download directory dialog caption", "Select Download Directory"), currentDownloadDirectory); + + // Populate the download directory combo box according to the new download location. + if (newDownloadDirectory == QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)) // The default download location was selected. + { + // Populate the download location with the default text. + downloadDirectoryComboBoxPointer->setCurrentText("System Download Directory"); + } + else if (newDownloadDirectory != QStringLiteral("")) // A different directory was selected. + { + // Populate the download location. + downloadDirectoryComboBoxPointer->setCurrentText(newDownloadDirectory); + } +} + +void SettingsDialog::updateSearchEngineLabel(const QString &searchEngineString) const +{ + // Update the search engine label. + searchEngineLabelPointer->setText(SearchEngineHelper::getSearchUrl(searchEngineString)); +} + +void SettingsDialog::updateUserAgentLabel(const QString &userAgentDatabaseName) const +{ + // Update the user agent label. + userAgentLabelPointer->setText(UserAgentHelper::getUserAgentFromDatabaseName(userAgentDatabaseName)); +} diff --git a/src/dialogs/SettingsDialog.h b/src/dialogs/SettingsDialog.h new file mode 100644 index 0000000..bf56e18 --- /dev/null +++ b/src/dialogs/SettingsDialog.h @@ -0,0 +1,53 @@ +/* + * Copyright 2024 Soren Stoutner . + * + * This file is part of Privacy Browser PC . + * + * Privacy Browser PC 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. + * + * Privacy Browser PC 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 Privacy Browser PC. If not, see . + */ + +#ifndef SETTINGSDIALOG_H +#define SETTINGSDIALOG_H + +// KDE Framework headers. +#include + +// Qt toolkit headers. +#include +#include + +class SettingsDialog : public KConfigDialog +{ + // Include the Q_OBJECT macro. + Q_OBJECT + +public: + // The primary constructor. + explicit SettingsDialog(QWidget *parentWidgetPointer, KCoreConfigSkeleton *coreConfigSkeletonPointer); + +signals: + void spellCheckLanguagesUpdated() const; + +private Q_SLOTS: + void showDownloadDirectoryBrowseDialog(); + void updateSearchEngineLabel(const QString &searchEngineString) const; + void updateUserAgentLabel(const QString &userAgentDatabaseName) const; + +private: + // The private variables. + QComboBox *downloadDirectoryComboBoxPointer; + QLabel *searchEngineLabelPointer; + QLabel *userAgentLabelPointer; +}; +#endif diff --git a/src/filters/CMakeLists.txt b/src/filters/CMakeLists.txt index c57b6ee..c0d5651 100644 --- a/src/filters/CMakeLists.txt +++ b/src/filters/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright © 2022 Soren Stoutner . +# Copyright 2022-2023 Soren Stoutner . # # This file is part of Privacy Browser PC . # @@ -17,6 +17,6 @@ # List the sources to include in the executable. -target_sources(privacy-browser PRIVATE +target_sources(privacybrowser PRIVATE MouseEventFilter.cpp ) diff --git a/src/helpers/CMakeLists.txt b/src/helpers/CMakeLists.txt index 042c5c3..921e0c0 100644 --- a/src/helpers/CMakeLists.txt +++ b/src/helpers/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright © 2022 Soren Stoutner . +# Copyright 2022-2023 Soren Stoutner . # # This file is part of Privacy Browser PC . # @@ -17,7 +17,8 @@ # List the sources to include in the executable. -target_sources(privacy-browser PRIVATE +target_sources(privacybrowser PRIVATE + FolderHelper.cpp SearchEngineHelper.cpp UserAgentHelper.cpp ) diff --git a/src/helpers/FolderHelper.cpp b/src/helpers/FolderHelper.cpp new file mode 100644 index 0000000..01e1b22 --- /dev/null +++ b/src/helpers/FolderHelper.cpp @@ -0,0 +1,89 @@ +/* + * Copyright 2023-2024 Soren Stoutner . + * + * This file is part of Privacy Browser PC . + * + * Privacy Browser PC 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. + * + * Privacy Browser PC 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 Privacy Browser PC. If not, see . + */ + +// Application headers. +#include "FolderHelper.h" +#include "databases/BookmarksDatabase.h" +#include "structs/BookmarkStruct.h" + +// Construct the class. +FolderHelper::FolderHelper() {} + +void FolderHelper::populateSubfolders(QTreeWidgetItem *treeWidgetItemPointer, const double initialParentFolderId) +{ + // Get the list of subfolders. + QList *subfoldersList = BookmarksDatabase::getSubfolders(treeWidgetItemPointer->text(FOLDER_ID_COLUMN).toDouble()); + + // Populate each subfolder. + for (BookmarkStruct bookmarkStruct : *subfoldersList) + { + // Create a tree widget item. + QTreeWidgetItem *subfolderWidgetItemPointer = new QTreeWidgetItem(); + + // Populate the tree widget item. + subfolderWidgetItemPointer->setText(FOLDER_NAME_COLUMN, bookmarkStruct.name); + subfolderWidgetItemPointer->setIcon(FOLDER_NAME_COLUMN, bookmarkStruct.favoriteIcon); + subfolderWidgetItemPointer->setText(FOLDER_ID_COLUMN, QString::number(bookmarkStruct.folderId, 'f', 0)); // Format the folder ID as a floating point with no trailing zeros. + + // Add the subfolder to the tree widget item. + treeWidgetItemPointer->addChild(subfolderWidgetItemPointer); + + // Select the folder if it is the initial parent folder. + if (bookmarkStruct.folderId == initialParentFolderId) + subfolderWidgetItemPointer->setSelected(true); + + // Add any subfolders. + populateSubfolders(subfolderWidgetItemPointer, initialParentFolderId); + } +} + +void FolderHelper::populateSubfoldersExcept(const double exceptSubfolderDatabaseId, QTreeWidgetItem *treeWidgetItemPointer, const double initialParentFolderId) +{ + // Get the list of subfolders. + QList *subfoldersList = BookmarksDatabase::getSubfolders(treeWidgetItemPointer->text(FOLDER_ID_COLUMN).toDouble()); + + // Populate each subfolder. + for (BookmarkStruct bookmarkStruct : *subfoldersList) + { + // Determine if this is an excepted folder. + bool exceptedFolder = bookmarkStruct.databaseId == exceptSubfolderDatabaseId; + + // Create a tree widget item. + QTreeWidgetItem *subfolderWidgetItemPointer = new QTreeWidgetItem(); + + // Populate the tree widget item. + subfolderWidgetItemPointer->setText(FOLDER_NAME_COLUMN, bookmarkStruct.name); + subfolderWidgetItemPointer->setIcon(FOLDER_NAME_COLUMN, bookmarkStruct.favoriteIcon); + subfolderWidgetItemPointer->setText(FOLDER_ID_COLUMN, QString::number(bookmarkStruct.folderId, 'f', 0)); // Format the folder ID as a floating point with no trailing zeros. + + // Disable the folder widget if it is excepted. All subfolders will automatically be disabled. + if (exceptedFolder) + subfolderWidgetItemPointer->setDisabled(true); + + // Add the subfolder to the tree widget item. + treeWidgetItemPointer->addChild(subfolderWidgetItemPointer); + + // Select the folder if it is the initial parent folder. + if (bookmarkStruct.folderId == initialParentFolderId) + subfolderWidgetItemPointer->setSelected(true); + + // Add any subfolders. + populateSubfoldersExcept(exceptSubfolderDatabaseId, subfolderWidgetItemPointer, initialParentFolderId); + } +} diff --git a/src/helpers/FolderHelper.h b/src/helpers/FolderHelper.h new file mode 100644 index 0000000..9248576 --- /dev/null +++ b/src/helpers/FolderHelper.h @@ -0,0 +1,40 @@ +/* + * Copyright 2023 Soren Stoutner . + * + * This file is part of Privacy Browser PC . + * + * Privacy Browser PC 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. + * + * Privacy Browser PC 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 Privacy Browser PC. If not, see . + */ + +#ifndef FOLDERHELPER_H +#define FOLDERHELPER_H + +// Qt toolkit headers. +#include + +class FolderHelper +{ +public: + // The default constructor. + explicit FolderHelper(); + + // The public constants. + const int FOLDER_NAME_COLUMN = 0; + const int FOLDER_ID_COLUMN = 1; + + // The public functions. + void populateSubfolders(QTreeWidgetItem *treeWidgetItemPointer, const double initialParentFolderId); + void populateSubfoldersExcept(const double exceptSubfolderDatabaseId, QTreeWidgetItem *treeWidgetItemPointer, const double initialParentFolderId); +}; +#endif diff --git a/src/helpers/UserAgentHelper.cpp b/src/helpers/UserAgentHelper.cpp index 14a71e2..891702d 100644 --- a/src/helpers/UserAgentHelper.cpp +++ b/src/helpers/UserAgentHelper.cpp @@ -1,5 +1,5 @@ /* - * Copyright © 2022 Soren Stoutner . + * Copyright 2022-2024 Soren Stoutner . * * This file is part of Privacy Browser PC . * @@ -26,38 +26,38 @@ #include // Define the public database constants. -const QString UserAgentHelper::SYSTEM_DEFAULT_DATABASE = QStringLiteral("System Default"); -const QString UserAgentHelper::PRIVACY_BROWSER_DATABASE = QStringLiteral("Privacy Browser"); -const QString UserAgentHelper::WEB_ENGINE_DEFAULT_DATABASE = QStringLiteral("WebEngine Default"); -const QString UserAgentHelper::FIREFOX_LINUX_DATABASE = QStringLiteral("Firefox Linux"); -const QString UserAgentHelper::CHROMIUM_LINUX_DATABASE = QStringLiteral("Chromium Linux"); -const QString UserAgentHelper::FIREFOX_WINDOWS_DATABASE = QStringLiteral("Firefox Windows"); -const QString UserAgentHelper::CHROME_WINDOWS_DATABASE = QStringLiteral("Chrome Windows"); -const QString UserAgentHelper::EDGE_WINDOWS_DATABASE = QStringLiteral("Edge Windows"); -const QString UserAgentHelper::SAFARI_MACOS_DATABASE = QStringLiteral("Safari macOS"); - -// Define the public translated constants. -const QString UserAgentHelper::SYSTEM_DEFAULT_TRANSLATED = i18n("System default"); -const QString UserAgentHelper::PRIVACY_BROWSER_TRANSLATED = i18n("Privacy Browser"); -const QString UserAgentHelper::WEB_ENGINE_DEFAULT_TRANSLATED = i18n("WebEngine default"); -const QString UserAgentHelper::FIREFOX_LINUX_TRANSLATED = i18n("Firefox on Linux"); -const QString UserAgentHelper::CHROMIUM_LINUX_TRANSLATED = i18n("Chromium on Linux"); -const QString UserAgentHelper::FIREFOX_WINDOWS_TRANSLATED = i18n("Firefox on Windows"); -const QString UserAgentHelper::CHROME_WINDOWS_TRANSLATED = i18n("Chrome on Windows"); -const QString UserAgentHelper::EDGE_WINDOWS_TRANSLATED = i18n("Edge on Windows"); -const QString UserAgentHelper::SAFARI_MACOS_TRANSLATED = i18n("Safari on macOS"); +const QString UserAgentHelper::SYSTEM_DEFAULT_DATABASE = QLatin1String("System Default"); +const QString UserAgentHelper::PRIVACY_BROWSER_DATABASE = QLatin1String("Privacy Browser"); +const QString UserAgentHelper::WEB_ENGINE_DEFAULT_DATABASE = QLatin1String("WebEngine Default"); +const QString UserAgentHelper::FIREFOX_LINUX_DATABASE = QLatin1String("Firefox Linux"); +const QString UserAgentHelper::CHROMIUM_LINUX_DATABASE = QLatin1String("Chromium Linux"); +const QString UserAgentHelper::FIREFOX_WINDOWS_DATABASE = QLatin1String("Firefox Windows"); +const QString UserAgentHelper::CHROME_WINDOWS_DATABASE = QLatin1String("Chrome Windows"); +const QString UserAgentHelper::EDGE_WINDOWS_DATABASE = QLatin1String("Edge Windows"); +const QString UserAgentHelper::SAFARI_MACOS_DATABASE = QLatin1String("Safari macOS"); // Define the public user agent constants. -const QString UserAgentHelper::PRIVACY_BROWSER_USER_AGENT = QStringLiteral("PrivacyBrowser/1.0"); -const QString UserAgentHelper::FIREFOX_LINUX_USER_AGENT = QStringLiteral("Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0"); -const QString UserAgentHelper::CHROMIUM_LINUX_USER_AGENT = QStringLiteral("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.134 Safari/537.36"); -const QString UserAgentHelper::FIREFOX_WINDOWS_USER_AGENT = QStringLiteral("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:103.0) Gecko/20100101 Firefox/103.0"); -const QString UserAgentHelper::CHROME_WINDOWS_USER_AGENT = QStringLiteral("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.134 Safari/537.36"); -const QString UserAgentHelper::EDGE_WINDOWS_USER_AGENT = QStringLiteral("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.134 Safari/537.36 Edg/103.0.1264.71"); -const QString UserAgentHelper::SAFARI_MACOS_USER_AGENT = QStringLiteral("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.5 Safari/605.1.15"); +const QString UserAgentHelper::PRIVACY_BROWSER_USER_AGENT = QLatin1String("PrivacyBrowser/1.0"); +const QString UserAgentHelper::FIREFOX_LINUX_USER_AGENT = QLatin1String("Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0"); +const QString UserAgentHelper::CHROMIUM_LINUX_USER_AGENT = QLatin1String("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36"); +const QString UserAgentHelper::FIREFOX_WINDOWS_USER_AGENT = QLatin1String("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0"); +const QString UserAgentHelper::CHROME_WINDOWS_USER_AGENT = QLatin1String("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36"); +const QString UserAgentHelper::EDGE_WINDOWS_USER_AGENT = QLatin1String("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36 Edg/121.0.0.0"); +const QString UserAgentHelper::SAFARI_MACOS_USER_AGENT = QLatin1String("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15"); // Construct the class. -UserAgentHelper::UserAgentHelper() {}; +UserAgentHelper::UserAgentHelper() { + // Populate the translated user agents. Translated entries cannot be public static const. + SYSTEM_DEFAULT_TRANSLATED = i18nc("User agents", "System default"); + PRIVACY_BROWSER_TRANSLATED = i18nc("User agents", "Privacy Browser"); + WEB_ENGINE_DEFAULT_TRANSLATED = i18nc("User agents", "WebEngine default"); + FIREFOX_LINUX_TRANSLATED = i18nc("User agents", "Firefox on Linux"); + CHROMIUM_LINUX_TRANSLATED = i18nc("User agents", "Chromium on Linux"); + FIREFOX_WINDOWS_TRANSLATED = i18nc("User agents", "Firefox on Windows"); + CHROME_WINDOWS_TRANSLATED = i18nc("User agents", "Chrome on Windows"); + EDGE_WINDOWS_TRANSLATED = i18nc("User agents", "Edge on Windows"); + SAFARI_MACOS_TRANSLATED = i18nc("User agents", "Safari on macOS"); +}; QString UserAgentHelper::getDatabaseUserAgentNameFromTranslatedName(const QString &translatedUserAgentName) { @@ -74,6 +74,20 @@ QString UserAgentHelper::getDatabaseUserAgentNameFromTranslatedName(const QStrin else return translatedUserAgentName; // Return the custom user agent. } +QString UserAgentHelper::getDatabaseUserAgentNameFromUserAgent(const QString &userAgent) +{ + // Return the database user agent name. + if (userAgent == PRIVACY_BROWSER_USER_AGENT) return PRIVACY_BROWSER_DATABASE; // Privacy Browser. + else if (userAgent == TabWidget::webEngineDefaultUserAgent) return WEB_ENGINE_DEFAULT_DATABASE; // WebEngine default. + else if (userAgent == FIREFOX_LINUX_USER_AGENT) return FIREFOX_LINUX_DATABASE; // Firefox Linux. + else if (userAgent == CHROMIUM_LINUX_USER_AGENT) return CHROMIUM_LINUX_DATABASE; // Chromium Linux. + else if (userAgent == FIREFOX_WINDOWS_USER_AGENT) return FIREFOX_WINDOWS_DATABASE; // Firefox Windows. + else if (userAgent == CHROME_WINDOWS_USER_AGENT) return CHROME_WINDOWS_DATABASE; // Chrome Windows. + else if (userAgent == EDGE_WINDOWS_USER_AGENT) return EDGE_WINDOWS_DATABASE; // Edge Windows. + else if (userAgent == SAFARI_MACOS_USER_AGENT) return SAFARI_MACOS_DATABASE; // Safari macOS. + else return userAgent; // Return the custom user agent. +} + int UserAgentHelper::getDomainSettingsUserAgentIndex(const QString &userAgentName) { // Return the domain settings user agent index. diff --git a/src/helpers/UserAgentHelper.h b/src/helpers/UserAgentHelper.h index 5c8286a..60f9902 100644 --- a/src/helpers/UserAgentHelper.h +++ b/src/helpers/UserAgentHelper.h @@ -1,5 +1,5 @@ /* - * Copyright © 2022 Soren Stoutner . + * Copyright 2022,2024 Soren Stoutner . * * This file is part of Privacy Browser PC . * @@ -29,39 +29,47 @@ public: // The default constructor. UserAgentHelper(); + // The destructor. + ~UserAgentHelper(); + // The public static constants. static const QString CHROMIUM_LINUX_DATABASE; - static const QString CHROMIUM_LINUX_TRANSLATED; static const QString CHROMIUM_LINUX_USER_AGENT; static const QString CHROME_WINDOWS_DATABASE; - static const QString CHROME_WINDOWS_TRANSLATED; static const QString CHROME_WINDOWS_USER_AGENT; static const QString EDGE_WINDOWS_DATABASE; - static const QString EDGE_WINDOWS_TRANSLATED; static const QString EDGE_WINDOWS_USER_AGENT; static const QString FIREFOX_LINUX_DATABASE; - static const QString FIREFOX_LINUX_TRANSLATED; static const QString FIREFOX_LINUX_USER_AGENT; static const QString FIREFOX_WINDOWS_DATABASE; - static const QString FIREFOX_WINDOWS_TRANSLATED; static const QString FIREFOX_WINDOWS_USER_AGENT; static const QString PRIVACY_BROWSER_DATABASE; - static const QString PRIVACY_BROWSER_TRANSLATED; static const QString PRIVACY_BROWSER_USER_AGENT; static const QString SAFARI_MACOS_DATABASE; - static const QString SAFARI_MACOS_TRANSLATED; static const QString SAFARI_MACOS_USER_AGENT; static const QString SYSTEM_DEFAULT_DATABASE; - static const QString SYSTEM_DEFAULT_TRANSLATED; static const QString WEB_ENGINE_DEFAULT_DATABASE; - static const QString WEB_ENGINE_DEFAULT_TRANSLATED; // The public static functions. - static QString getDatabaseUserAgentNameFromTranslatedName(const QString &translatedUserAgentName); + static QString getDatabaseUserAgentNameFromUserAgent(const QString &userAgent); static int getDomainSettingsUserAgentIndex(const QString &userAgentName); static QString getUserAgentFromDatabaseName(const QString &userAgentDatabaseName); - static QString getUserAgentFromTranslatedName(const QString &userAgentTranslatedName); static QString getResultingDomainSettingsUserAgent(const QString &rawUserAgent); - static QString getTranslatedUserAgentNameFromDatabaseName(const QString &userAgentName); + + // The public variables. + QString SYSTEM_DEFAULT_TRANSLATED; + QString PRIVACY_BROWSER_TRANSLATED; + QString WEB_ENGINE_DEFAULT_TRANSLATED; + QString FIREFOX_LINUX_TRANSLATED; + QString CHROMIUM_LINUX_TRANSLATED; + QString FIREFOX_WINDOWS_TRANSLATED; + QString CHROME_WINDOWS_TRANSLATED; + QString EDGE_WINDOWS_TRANSLATED; + QString SAFARI_MACOS_TRANSLATED; + + // The public functions. Anything that accesses the translated user names must use an instantiated copy of the class. + QString getDatabaseUserAgentNameFromTranslatedName(const QString &translatedUserAgentName); + QString getUserAgentFromTranslatedName(const QString &userAgentTranslatedName); + QString getTranslatedUserAgentNameFromDatabaseName(const QString &userAgentName); }; #endif diff --git a/src/icons/1024-apps-privacybrowser.png b/src/icons/1024-apps-privacybrowser.png new file mode 100644 index 0000000..5744367 Binary files /dev/null and b/src/icons/1024-apps-privacybrowser.png differ diff --git a/src/icons/128-apps-privacybrowser.png b/src/icons/128-apps-privacybrowser.png new file mode 100644 index 0000000..718d27a Binary files /dev/null and b/src/icons/128-apps-privacybrowser.png differ diff --git a/src/icons/16-apps-privacybrowser.png b/src/icons/16-apps-privacybrowser.png new file mode 100644 index 0000000..108198e Binary files /dev/null and b/src/icons/16-apps-privacybrowser.png differ diff --git a/src/icons/22-apps-privacybrowser.png b/src/icons/22-apps-privacybrowser.png new file mode 100644 index 0000000..100693c Binary files /dev/null and b/src/icons/22-apps-privacybrowser.png differ diff --git a/src/icons/24-apps-privacybrowser.png b/src/icons/24-apps-privacybrowser.png new file mode 100644 index 0000000..7c5ffe0 Binary files /dev/null and b/src/icons/24-apps-privacybrowser.png differ diff --git a/src/icons/256-apps-privacybrowser.png b/src/icons/256-apps-privacybrowser.png new file mode 100644 index 0000000..cd7fca3 Binary files /dev/null and b/src/icons/256-apps-privacybrowser.png differ diff --git a/src/icons/32-apps-privacybrowser.png b/src/icons/32-apps-privacybrowser.png new file mode 100644 index 0000000..974f192 Binary files /dev/null and b/src/icons/32-apps-privacybrowser.png differ diff --git a/src/icons/48-apps-privacybrowser.png b/src/icons/48-apps-privacybrowser.png new file mode 100644 index 0000000..595c90e Binary files /dev/null and b/src/icons/48-apps-privacybrowser.png differ diff --git a/src/icons/512-apps-privacybrowser.png b/src/icons/512-apps-privacybrowser.png new file mode 100644 index 0000000..0c3c0d6 Binary files /dev/null and b/src/icons/512-apps-privacybrowser.png differ diff --git a/src/icons/64-apps-privacybrowser.png b/src/icons/64-apps-privacybrowser.png new file mode 100644 index 0000000..c9316c4 Binary files /dev/null and b/src/icons/64-apps-privacybrowser.png differ diff --git a/src/icons/javascript-warning.svg b/src/icons/javascript-warning.svg index 39245a7..c9e4542 100644 --- a/src/icons/javascript-warning.svg +++ b/src/icons/javascript-warning.svg @@ -5,7 +5,7 @@ This file is derived from `security` and `language`, which are part of the Android Material icon set. They are released under the Apache License 2.0. - This file is part of Privacy Browser PC . + This file is part of Privacy Browser PC . Privacy Browser PC is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/icons/loading.gif b/src/icons/loading.gif new file mode 100644 index 0000000..132f217 Binary files /dev/null and b/src/icons/loading.gif differ diff --git a/src/icons/privacy-mode.svg b/src/icons/privacy-mode.svg index 428d507..d799bd8 100644 --- a/src/icons/privacy-mode.svg +++ b/src/icons/privacy-mode.svg @@ -5,7 +5,7 @@ This file is derived from `security` and `language`, which are part of the Android Material icon set. They are released under the Apache License 2.0. - This file is part of Privacy Browser PC . + This file is part of Privacy Browser PC . Privacy Browser PC is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/icons/privacybrowser-symbolic.svg b/src/icons/privacybrowser-symbolic.svg new file mode 100644 index 0000000..9649f6f --- /dev/null +++ b/src/icons/privacybrowser-symbolic.svg @@ -0,0 +1,32 @@ + + + + + + + + diff --git a/src/icons/sc-apps-privacy-browser.svg b/src/icons/sc-apps-privacy-browser.svg deleted file mode 100644 index 428d507..0000000 --- a/src/icons/sc-apps-privacy-browser.svg +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/src/icons/sc-apps-privacybrowser.svg b/src/icons/sc-apps-privacybrowser.svg new file mode 100644 index 0000000..19bf69f --- /dev/null +++ b/src/icons/sc-apps-privacybrowser.svg @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/interceptors/CMakeLists.txt b/src/interceptors/CMakeLists.txt index 6e92b32..7f044ba 100644 --- a/src/interceptors/CMakeLists.txt +++ b/src/interceptors/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright © 2022 Soren Stoutner . +# Copyright 2022-2023 Soren Stoutner . # # This file is part of Privacy Browser PC . # @@ -17,6 +17,6 @@ # List the sources to include in the executable. -target_sources(privacy-browser PRIVATE +target_sources(privacybrowser PRIVATE UrlRequestInterceptor.cpp ) diff --git a/src/interceptors/UrlRequestInterceptor.cpp b/src/interceptors/UrlRequestInterceptor.cpp index 6348898..5f9a8cc 100644 --- a/src/interceptors/UrlRequestInterceptor.cpp +++ b/src/interceptors/UrlRequestInterceptor.cpp @@ -1,5 +1,5 @@ /* - * Copyright © 2022 Soren Stoutner . + * Copyright 2022-2023 Soren Stoutner . * * This file is part of Privacy Browser PC . * @@ -23,20 +23,40 @@ // KDE Framework headers. #include -// Qt framework headers. -#include - // Construct the class. UrlRequestInterceptor::UrlRequestInterceptor(QObject *parentObjectPointer) : QWebEngineUrlRequestInterceptor(parentObjectPointer) {} void UrlRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo &urlRequestInfo) { + // Handle the request according to the resource type. + switch (urlRequestInfo.resourceType()) + { + // A naughty HTTP ping request. + case QWebEngineUrlRequestInfo::ResourceTypePing: + { + // Block the HTTP ping request. + urlRequestInfo.block(true); + + // Display the HTTP Ping blocked dialog. + emit displayHttpPingDialog(urlRequestInfo.requestUrl().toString()); + + break; + } + + default: + { + // Do nothing. + break; + } + } + // Handle the request according to the navigation type. switch (urlRequestInfo.navigationType()) { case QWebEngineUrlRequestInfo::NavigationTypeLink: case QWebEngineUrlRequestInfo::NavigationTypeTyped: case QWebEngineUrlRequestInfo::NavigationTypeBackForward: + // case QWebEngineUrlRequestInfo::NavigationTypeReload: This can be uncommented once https://redmine.stoutner.com/issues/821 has been fixed. case QWebEngineUrlRequestInfo::NavigationTypeRedirect: { // Only check the hosts if the main URL is changing. @@ -58,42 +78,4 @@ void UrlRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo &urlReques // Do nothing. break; } - - // Handle the request according to the resource type. - switch (urlRequestInfo.resourceType()) - { - // A naughty HTTP ping request. - case QWebEngineUrlRequestInfo::ResourceTypePing: - { - // Block HTTP ping requests. - urlRequestInfo.block(true); - - // Instantiate an HTTP ping blocked message box. - QMessageBox httpPingBlockedMessageBox; - - // Set the icon. - httpPingBlockedMessageBox.setIcon(QMessageBox::Information); - - // Set the window title. - httpPingBlockedMessageBox.setWindowTitle(i18nc("HTTP Ping blocked dialog title", "HTTP Ping Blocked")); - - // Set the text. - httpPingBlockedMessageBox.setText(i18nc("HTTP Ping blocked dialog text", "This request has been blocked because it sends a naughty HTTP ping to %1.", - urlRequestInfo.requestUrl().toString())); - - // Set the standard button. - httpPingBlockedMessageBox.setStandardButtons(QMessageBox::Ok); - - // Display the message box. - httpPingBlockedMessageBox.exec(); - - break; - } - - default: - { - // Do nothing. - break; - } - } } diff --git a/src/interceptors/UrlRequestInterceptor.h b/src/interceptors/UrlRequestInterceptor.h index 721d523..5638768 100644 --- a/src/interceptors/UrlRequestInterceptor.h +++ b/src/interceptors/UrlRequestInterceptor.h @@ -1,5 +1,5 @@ /* - * Copyright © 2022 Soren Stoutner . + * Copyright 2022-2023 Soren Stoutner . * * This file is part of Privacy Browser PC . * @@ -37,6 +37,7 @@ public: signals: // The signals. - void applyDomainSettings(const QString hostname) const; + void applyDomainSettings(const QString &hostname) const; + void displayHttpPingDialog(const QString &httpPingUrl) const; }; #endif diff --git a/src/main.cpp b/src/main.cpp index 10ce579..0c499f2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,5 @@ /* - * Copyright © 2022 Soren Stoutner . + * Copyright 2022-2024 Soren Stoutner . * * This file is part of Privacy Browser PC . * @@ -18,6 +18,7 @@ */ // Application headers. +#include "databases/BookmarksDatabase.h" #include "databases/CookiesDatabase.h" #include "databases/DomainsDatabase.h" #include "windows/BrowserWindow.h" @@ -25,8 +26,8 @@ // KDE Frameworks headers. #include #include -#include #include +#include // Qt headers. #include @@ -45,26 +46,27 @@ int main(int argc, char *argv[]) KCrash::initialize(); // Instantiate about data, setting the component name, the display name, and the version. - KAboutData aboutData(QStringLiteral("privacybrowser"), i18n("Privacy Browser"), QStringLiteral("0.1")); + KAboutData aboutData(QStringLiteral("privacybrowser"), i18nc("Program Name", "Privacy Browser"), QStringLiteral("0.5")); // Add the author name, job description, email address, and website. - aboutData.addAuthor(i18n("Soren Stoutner"),i18n("Principal developer"), QStringLiteral("soren@stoutner.com"), QStringLiteral("https://www.stoutner.com/")); + aboutData.addAuthor(i18nc("Developer Information", "Soren Stoutner"),i18nc("Developer Information", "Principal developer"), QStringLiteral("soren@stoutner.com"), + QStringLiteral("https://www.stoutner.com/")); // Populate additional about data info. aboutData.setBugAddress("https://redmine.stoutner.com/projects/privacy-browser-pc/issues"); - aboutData.setCopyrightStatement(i18n("Copyright © 2016-2017,2021-2022 Soren Stoutner ")); + aboutData.setCopyrightStatement(i18nc("Copyright", "Copyright 2016-2017,2021-2024 Soren Stoutner ")); aboutData.setDesktopFileName(QStringLiteral("com.stoutner.privacybrowser")); aboutData.setHomepage(QStringLiteral("https://www.stoutner.com/privacy-browser-pc/")); //aboutData.setLicense(KAboutLicense::GPL_V3, KAboutLicense::OrLaterVersions); aboutData.setLicenseTextFile(QStringLiteral(":/licenses/GPLv3+.txt")); aboutData.setOrganizationDomain("stoutner.com"); - aboutData.setShortDescription(i18n("A web browser that respects your privacy.")); + aboutData.setShortDescription(i18nc("Tagline", "A web browser that respects your privacy.")); // Set the application data. KAboutData::setApplicationData(aboutData); // Set the window icon. - application.setWindowIcon(QIcon::fromTheme(QStringLiteral("privacy-browser"), QIcon(":/icons/sc-apps-privacy-browser.svg"))); + application.setWindowIcon(QIcon::fromTheme(QStringLiteral("privacy-browser"), QIcon(QStringLiteral(":/icons/sc-apps-privacybrowser.svg")))); // Create a command line parser. QCommandLineParser commandLineParser; @@ -81,9 +83,14 @@ int main(int argc, char *argv[]) // Register with D-Bus, allowing multiple instances and allowing the program to run if for some reason the registration fails. KDBusService appDBusService(KDBusService::Multiple | KDBusService::NoExitOnFailure); + // Create the app data location directory if it doesn't currently exist. This directory is used to store the databases in the subsequent commands. + // The first directory in the list should be the private, writable location, which on Linux should be `/home/user/.local/share/privacybrowser`. + QDir().mkdir(QStandardPaths::standardLocations(QStandardPaths::AppDataLocation).first()); + // Add the databases. - DomainsDatabase::addDatabase(); + BookmarksDatabase::addDatabase(); CookiesDatabase::addDatabase(); + DomainsDatabase::addDatabase(); // Create the main window. BrowserWindow *browserWindowPointer = new BrowserWindow(); diff --git a/src/privacybrowser.1 b/src/privacybrowser.1 new file mode 100644 index 0000000..828e6de --- /dev/null +++ b/src/privacybrowser.1 @@ -0,0 +1,95 @@ +.\" Copyright 2023 Soren Stoutner . +.\" +.\" This file is part of Privacy Browser PC . +.\" +.\" Privacy Browser PC 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. +.\" +.\" Privacy Browser PC 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 Privacy Browser PC. If not, see . + +.\" Title Header. +.TH "Privacy Browser" 1 "12 October 2023" "version 0.5" "Privacy Browser User Manual" + +.\" Section Header - Name. +.SH NAME +privacybrowser \- a web browser that respects your privacy + + +.\" Section Header - Synopsis. +.SH SYNOPSIS + +.\" Launching homepage. `.B` is "Bold". +.B privacybrowser +.br + +.\" Launching a URL. `.B` is "Bold". `.RI` is "Roman (not italic) alternating Italic (underlined)". +.B privacybrowser +.RI [ url ] +.br + +.\" Options. `.B` is "Bold". `.RI` is "Roman (not italic) alternating Italic (underlined)". +.B privacybrowser +.RI [ option ] + + +.\" Section Header - Description. +.SH DESCRIPTION +Privacy Browser is a web browser focused on user privacy. It uses Qt WebEngine to render websites, which is a modified version of Chromium’s rendering engine. + + +.\" Section Header - Options. +.SH OPTIONS + +.\" Help. `.TP` is "Tag Paragraph". `.BR` is "Bold alternating Roman (not bold)". +.TP +.BR -h ", " --help +Displays help for command line options. + +.\" Help all. `.TP` is "Tag Paragraph". `.B` is "Bold". +.TP +.B --help-all +Displays help, including Qt specific options. + +.\" Version. `.TP` is "Tag Paragraph". `.BR` is "Bold alternating Roman (not bold)". +.TP +.BR -v ", " --version +Displays version information. + +.\" Author. `.TP` is "Tag Paragraph". `.B` is "Bold". +.TP +.B --author +Show author information. + +.\" License. `.TP` is "Tag Paragraph". `.B` is "Bold". +.TP +.B --license +Show license information. + +.\" Desktop File Name. `.TP` is "Tag Paragraph". `.BI` is "Bold alternating Italic (underlined)". +.TP +.BI --desktopfile " file" +The base file name of the desktop entry for this application. + + +.\" Section Header - Bugs. `.TP` is "Tag Paragraph". +.SH BUGS +Privacy Browser is an early alpha release. As such, many features are not yet implemented. More information can be found at: +.TP +https://www.stoutner.com/privacy-browser-pc/ +.TP +Bugs in currently implemented features may be reported at: +.TP +https://redmine.stoutner.com/projects/privacy-browser-pc/issues + + +.\" Section Header - Author. +.SH AUTHOR +Soren Stoutner diff --git a/src/privacybrowser.notifyrc b/src/privacybrowser.notifyrc new file mode 100644 index 0000000..08915de --- /dev/null +++ b/src/privacybrowser.notifyrc @@ -0,0 +1,27 @@ +# Copyright 2022-2023 Soren Stoutner . +# +# This file is part of Privacy Browser PC . +# +# Privacy Browser PC 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. +# +# Privacy Browser PC 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 Privacy Browser PC. If not, see . + +[Global] +IconName=sc-apps-privacybrowser +Name=Privacy Browser +Comment=A browser that respects your privacy. +DesktopEntry=com.stoutner.privacybrowser.desktop + +[Event/FileDownload] +Name=File Download +Comment=The file has started downloading +Action=Popup diff --git a/src/resources.qrc b/src/resources.qrc index cfa6f79..c55f518 100644 --- a/src/resources.qrc +++ b/src/resources.qrc @@ -1,7 +1,7 @@ - + - + @@ -58,14 +60,22 @@ 1.00 - + System Download Directory + + true + + true + + true + + true @@ -82,4 +92,10 @@ true + + + + + + diff --git a/src/settings/Settings.kcfgc b/src/settings/Settings.kcfgc index 42940f7..3cb234c 100644 --- a/src/settings/Settings.kcfgc +++ b/src/settings/Settings.kcfgc @@ -1,4 +1,4 @@ -# Copyright © 2022 Soren Stoutner . +# Copyright 2022-2023 Soren Stoutner . # # This file is part of Privacy Browser PC . # @@ -15,6 +15,9 @@ # You should have received a copy of the GNU General Public License # along with Privacy Browser PC. If not, see . +# The options for this file are partially specified at the following URLs: +# +# # Specify the KConfig file. File=Settings.kcfg @@ -24,3 +27,6 @@ ClassName=Settings # Make the generated class a singleton. Singleton=true + +# List of variables that can be manually changed. +Mutators=spellCheckLanguages,downloadDirectory diff --git a/src/structs/BookmarkStruct.h b/src/structs/BookmarkStruct.h new file mode 100644 index 0000000..d7e0a86 --- /dev/null +++ b/src/structs/BookmarkStruct.h @@ -0,0 +1,38 @@ +/* + * Copyright 2023 Soren Stoutner . + * + * This file is part of Privacy Browser PC . + * + * Privacy Browser PC 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. + * + * Privacy Browser PC 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 Privacy Browser PC. If not, see . + */ + +#ifndef BOOKMARKSTRUCT_H +#define BOOKMARKSTRUCT_H + +// Qt framework headers. +#include +#include + +struct BookmarkStruct +{ + int databaseId; + QString name; + QString url; + double parentFolderId; + int displayOrder; + bool isFolder; + double folderId; + QIcon favoriteIcon; +}; +#endif diff --git a/src/ui.rcs/CMakeLists.txt b/src/ui.rcs/CMakeLists.txt index 2c8c30e..dc4dcc8 100644 --- a/src/ui.rcs/CMakeLists.txt +++ b/src/ui.rcs/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright © 2022 Soren Stoutner . +# Copyright 2022 Soren Stoutner . # # This file is part of Privacy Browser PC . # @@ -18,5 +18,5 @@ # Install Privacy Browser's RC (Runtime Configuration) files. install(FILES - browser_ui.rc + browserwindowui.rc DESTINATION ${KDE_INSTALL_KXMLGUI5DIR}/privacybrowser) diff --git a/src/ui.rcs/browser_ui.rc b/src/ui.rcs/browser_ui.rc deleted file mode 100644 index 0526147..0000000 --- a/src/ui.rcs/browser_ui.rc +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - - - - - - - On-The-Fly Settings - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Navigation Toolbar - - - - - - - - URL Toolbar - - - - - diff --git a/src/ui.rcs/browserwindowui.rc b/src/ui.rcs/browserwindowui.rc new file mode 100644 index 0000000..9953db6 --- /dev/null +++ b/src/ui.rcs/browserwindowui.rc @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + On-The-Fly Settings + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Navigation Toolbar + + + + + + + + + URL Toolbar + + + + + + + + + + + + diff --git a/src/uis/AddBookmarkDialog.ui b/src/uis/AddBookmarkDialog.ui new file mode 100644 index 0000000..d954863 --- /dev/null +++ b/src/uis/AddBookmarkDialog.ui @@ -0,0 +1,191 @@ + + + + + + AddBookmarkDialog + + + + + + + + + + + 10 + + + + + + + Website favorite icon + + + + true + + + + + 32 + 32 + + + + + + + + + + Custom favorite icon + + + + + 32 + 32 + + + + + + + + + + Qt::Vertical + + + + + + + + + + + + + + Qt::AlignLeft + + + + + + + + 1000 + 700 + + + + + + + + + + + + Qt::AlignLeft + + + + + + + The name of the bookmark. + + + + Bookmark name + + + + + + + + + + + + + + + Qt::AlignLeft + + + + + + + The URL of the bookmark. + + + + Bookmark URL + + + + + + + + + + + + + + + + + + + + + + Browse + + + + + + + + + + + + + QDialogButtonBox::Cancel + + + + + + + + diff --git a/src/uis/AddFolderDialog.ui b/src/uis/AddFolderDialog.ui new file mode 100644 index 0000000..752059e --- /dev/null +++ b/src/uis/AddFolderDialog.ui @@ -0,0 +1,189 @@ + + + + + + AddFolderDialog + + + + + + + + + + + 10 + + + + + + + Default folder icon + + + + true + + + + + + + + + 32 + 32 + + + + + + + + + + Current website favorite icon + + + + + 32 + 32 + + + + + + + + + + Custom folder icon + + + + + + + + + 32 + 32 + + + + + + + + + + Qt::Vertical + + + + + + + + + + + + + + Qt::AlignLeft + + + + + + + + 1000 + 700 + + + + + + + + + + + + Qt::AlignLeft + + + + + + + The folder name. + + + + Folder name + + + + + + + + + + + + + + + + + + + + + + Browse + + + + + + + + + + + + + QDialogButtonBox::Cancel + + + + + + + + diff --git a/src/uis/AddOrEditCookieDialog.ui b/src/uis/AddOrEditCookieDialog.ui index 3595e74..779b8fb 100644 --- a/src/uis/AddOrEditCookieDialog.ui +++ b/src/uis/AddOrEditCookieDialog.ui @@ -1,9 +1,9 @@ + + + BookmarksDialog + + + + DraggableTreeView + QTreeView +
widgets/DraggableTreeView.h
+
+
+ + + + + 0 + 0 + 1000 + 1500 + + + + + + + + + + + + + + + + + Add bookmark + + + + + + + + + + + + + Add folder + + + + + + + + + + + + + Edit + + + + + + + + + + + + + Delete items + + + + + + + + + + + + + QDialogButtonBox::Close + + + + + + + +
diff --git a/src/uis/CookiesDialog.ui b/src/uis/CookiesDialog.ui index bd2c24f..c5436b1 100644 --- a/src/uis/CookiesDialog.ui +++ b/src/uis/CookiesDialog.ui @@ -1,9 +1,9 @@ + + + EditBookmarkDialog + + + + + + + + + + + 10 + + + + + + + Current favorite icon + + + + true + + + + + 32 + 32 + + + + + + + + + + Current website favorite icon + + + + + 32 + 32 + + + + + + + + + + Custom favorite icon + + + + + 32 + 32 + + + + + + + + + + Qt::Vertical + + + + + + + + + + + + + + Qt::AlignLeft + + + + + + + + 1000 + 700 + + + + + + + + + + + + Qt::AlignLeft + + + + + + + The name of the bookmark. + + + + Bookmark name + + + + + + + + + + + + + + + Qt::AlignLeft + + + + + + + The URL of the bookmark. + + + + Bookmark URL + + + + + + + + + 0 + 0 + + + + + + 700 + 0 + + + + + + + + + + + + + + + + + + + Browse + + + + + + + + + + + + + QDialogButtonBox::Save | QDialogButtonBox::Cancel + + + + + + + + diff --git a/src/uis/EditFolderDialog.ui b/src/uis/EditFolderDialog.ui new file mode 100644 index 0000000..efebd2a --- /dev/null +++ b/src/uis/EditFolderDialog.ui @@ -0,0 +1,205 @@ + + + + + + EditFolderDialog + + + + + + + + + + + 10 + + + + + + + Current folder icon + + + + true + + + + + 32 + 32 + + + + + + + + + + Default folder icon + + + + + + + + + 32 + 32 + + + + + + + + + + Current website favorite icon + + + + + 32 + 32 + + + + + + + + + + Custom folder icon + + + + + + + + + 32 + 32 + + + + + + + + + + Qt::Vertical + + + + + + + + + + + + + + Qt::AlignLeft + + + + + + + + 1000 + 700 + + + + + + + + + + + + Qt::AlignLeft + + + + + + + The folder name. + + + + Folder name + + + + + + + + + + + + + + + + + + + + + + Browse + + + + + + + + + + + + + QDialogButtonBox::Save | QDialogButtonBox::Cancel + + + + + + + + diff --git a/src/uis/HttpAuthenticationDialog.ui b/src/uis/HttpAuthenticationDialog.ui new file mode 100644 index 0000000..d5d9de0 --- /dev/null +++ b/src/uis/HttpAuthenticationDialog.ui @@ -0,0 +1,119 @@ + + + + + + HttpAuthenticationDialog + + + + + + + + + 24 + true + + + + + Qt::AlignCenter + + + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + Host: + + + + + + + + + + + + + + + + + Username + + + + + + + + + 300 + 0 + + + + + + + + + + Password + + + + + + + + + + + + + + + Qt::Vertical + + + + + + + + + QDialogButtonBox::Ok | QDialogButtonBox::Cancel + + + + + + diff --git a/src/uis/SaveDialog.ui b/src/uis/SaveDialog.ui index b8e3dfa..d9d2cef 100644 --- a/src/uis/SaveDialog.ui +++ b/src/uis/SaveDialog.ui @@ -3,7 +3,7 @@ - - - - - - Download Location - - - - The default is System Download Directory. - - - - - - - - - 0 - 0 - - - - - true - - - - - System Download Directory - - - - - - - - - Browse - - - - - - @@ -205,6 +159,77 @@ + + + + + Spatial navigation + + + + Allow moving between links and input fields using the keyboard arrow keys. The default is enabled. + + + + + + + + + Download Location + + + + + + + + + + + 0 + 0 + + + + + true + + + + + System Download Directory + + + + + + + + + Browse + + + + + + + + + + + Auto update the download directory + + + + Automatically update the download directory to be whatever was used for the last download. The default is enabled. + + + + + + + diff --git a/src/uis/SettingsPrivacy.ui b/src/uis/SettingsPrivacy.ui index 0affb07..16c616e 100644 --- a/src/uis/SettingsPrivacy.ui +++ b/src/uis/SettingsPrivacy.ui @@ -1,9 +1,9 @@ + + false + diff --git a/src/uis/SettingsSpellCheck.ui b/src/uis/SettingsSpellCheck.ui new file mode 100644 index 0000000..9375f0b --- /dev/null +++ b/src/uis/SettingsSpellCheck.ui @@ -0,0 +1,53 @@ + + + + + + + SpellCheckSettings + + + + + + + + Spell checking languages can be added by installing the Hunspell language packages. One or more languages can be selected. All selected languages will be checked simultaneously. + + + + true + + + + + + + + + + 0 + 0 + + + + + + + diff --git a/src/uis/TabWidget.ui b/src/uis/TabWidget.ui index 2aad7ef..521526d 100644 --- a/src/uis/TabWidget.ui +++ b/src/uis/TabWidget.ui @@ -3,7 +3,7 @@