You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
532 lines
12 KiB
532 lines
12 KiB
/*
|
|
* Copyright 2024 Vincent Sanders <vince@netsurf-browser.org>
|
|
*
|
|
* This file is part of NetSurf, http://www.netsurf-browser.org/
|
|
*
|
|
* NetSurf 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; version 2 of the License.
|
|
*
|
|
* NetSurf is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/**
|
|
* \file
|
|
* Implementation of netsurf application for qt frontend.
|
|
*/
|
|
|
|
#include <QSettings>
|
|
#include <QStandardPaths>
|
|
#include <QResource>
|
|
#include <QDir>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
extern "C" {
|
|
#include "utils/utils.h"
|
|
#include "utils/errors.h"
|
|
#include "utils/log.h"
|
|
#include "utils/nsoption.h"
|
|
#include "utils/nsurl.h"
|
|
#include "utils/file.h"
|
|
#include "utils/messages.h"
|
|
|
|
#include "netsurf/bitmap.h"
|
|
#include "netsurf/netsurf.h"
|
|
#include "netsurf/content.h"
|
|
#include "netsurf/browser_window.h"
|
|
#include "netsurf/cookie_db.h"
|
|
#include "netsurf/url_db.h"
|
|
|
|
#include "desktop/hotlist.h"
|
|
}
|
|
|
|
#include "qt/resources.h"
|
|
#include "qt/misc.h"
|
|
|
|
#include "qt/application.cls.h"
|
|
#include "qt/settings.cls.h"
|
|
#include "qt/bookmarks.cls.h"
|
|
#include "qt/global_history.cls.h"
|
|
#include "qt/cookies.cls.h"
|
|
#include "qt/page_info.cls.h"
|
|
|
|
//QTimer
|
|
//QSocketNotifier
|
|
|
|
static NS_Application *nsqtapp;
|
|
|
|
NS_Application* NS_Application::instance()
|
|
{
|
|
return nsqtapp;
|
|
}
|
|
|
|
/**
|
|
* Ensures output logging stream is correctly configured
|
|
*/
|
|
bool NS_Application::nslog_stream_configure(FILE *fptr)
|
|
{
|
|
/* set log stream to be non-buffering */
|
|
setbuf(fptr, NULL);
|
|
|
|
return true;
|
|
}
|
|
|
|
static char *accept_language_from_qlocale(QLocale &loc)
|
|
{
|
|
QString alang;
|
|
QStringList llist = loc.uiLanguages();
|
|
int lidx;
|
|
float quality = 1.0;
|
|
|
|
for (lidx = 0;lidx < llist.size(); lidx++) {
|
|
QStringList lparts = llist.at(lidx).split('-', Qt::SkipEmptyParts);
|
|
if (lparts.size() == 0 || lparts.size() > 2) {
|
|
continue;
|
|
}
|
|
quality -= 0.1;
|
|
if (quality <= 0.2) {
|
|
quality = 0.2;
|
|
}
|
|
alang += lparts.at(0);
|
|
if (lparts.size() == 2) {
|
|
alang += "-" + lparts.at(1);
|
|
}
|
|
alang += ";q=" + QString::number(quality) + ", ";
|
|
}
|
|
if (alang.size() < 3) {
|
|
return NULL;
|
|
}
|
|
return strdup(alang.left(alang.size() - 2).toUtf8().data());
|
|
}
|
|
|
|
|
|
/**
|
|
* Set option defaults for qt frontend
|
|
*
|
|
* @param defaults The option table to update.
|
|
* @return error status.
|
|
*/
|
|
nserror NS_Application::set_option_defaults(struct nsoption_s *defaults)
|
|
{
|
|
QDir config_dir(QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation));
|
|
|
|
/* ensure all elements of path exist */
|
|
if (!config_dir.exists()) {
|
|
config_dir.mkpath(config_dir.absoluteFilePath(""));
|
|
}
|
|
|
|
/* cookies database default read and write paths */
|
|
nsoption_setnull_charp(cookie_file,
|
|
strdup(config_dir.absoluteFilePath("Cookies").toUtf8()));
|
|
|
|
nsoption_setnull_charp(cookie_jar,
|
|
strdup(config_dir.absoluteFilePath("Cookies").toUtf8()));
|
|
|
|
/* url database default path */
|
|
nsoption_setnull_charp(url_file,
|
|
strdup(config_dir.absoluteFilePath("URLs").toUtf8()));
|
|
|
|
/* bookmark database default path */
|
|
nsoption_setnull_charp(hotlist_path,
|
|
strdup(config_dir.absoluteFilePath("Hotlist").toUtf8()));
|
|
|
|
if (nsoption_charp(hotlist_path) == NULL) {
|
|
NSLOG(netsurf, ERROR, "Failed initialising bookmarks resource path");
|
|
return NSERROR_BAD_PARAMETER;
|
|
}
|
|
|
|
/* set default font names */
|
|
nsoption_set_charp(font_sans, strdup("Sans"));
|
|
nsoption_set_charp(font_serif, strdup("Serif"));
|
|
nsoption_set_charp(font_mono, strdup("Monospace"));
|
|
nsoption_set_charp(font_cursive, strdup("Serif"));
|
|
nsoption_set_charp(font_fantasy, strdup("Serif"));
|
|
|
|
/* use qt locale to generate a default accept language configuration */
|
|
QLocale loc;
|
|
char *alang;
|
|
alang = accept_language_from_qlocale(loc);
|
|
if (alang != NULL) {
|
|
NSLOG(netsurf, DEBUG, "accept_language \"%s\"", alang);
|
|
nsoption_set_charp(accept_language, alang);
|
|
}
|
|
|
|
return NSERROR_OK;
|
|
}
|
|
|
|
void NS_Application::nsOptionLoad()
|
|
{
|
|
QSettings settings;
|
|
unsigned int entry; /* index to option being output */
|
|
for (entry = 0; entry < NSOPTION_LISTEND; entry++) {
|
|
struct nsoption_s *option = nsoptions + entry;
|
|
if (settings.contains(option->key)) {
|
|
switch (option->type) {
|
|
case OPTION_BOOL:
|
|
option->value.b = settings.value(option->key).toBool();
|
|
break;
|
|
|
|
case OPTION_INTEGER:
|
|
option->value.i = settings.value(option->key).toInt();
|
|
break;
|
|
|
|
case OPTION_UINT:
|
|
option->value.u = settings.value(option->key).toUInt();
|
|
break;
|
|
|
|
case OPTION_COLOUR:
|
|
option->value.c = settings.value(option->key).toUInt();
|
|
break;
|
|
|
|
case OPTION_STRING:
|
|
nsoption_set_tbl_charp(nsoptions,
|
|
(enum nsoption_e)entry,
|
|
strdup(settings.value(option->key).toString().toUtf8()));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static size_t set_qtsetting(struct nsoption_s *option, void *ctx)
|
|
{
|
|
QSettings *settings = (QSettings *)ctx;
|
|
|
|
switch (option->type) {
|
|
case OPTION_BOOL:
|
|
settings->setValue(option->key, option->value.b);
|
|
break;
|
|
|
|
case OPTION_INTEGER:
|
|
settings->setValue(option->key, option->value.i);
|
|
break;
|
|
|
|
case OPTION_UINT:
|
|
settings->setValue(option->key, option->value.u);
|
|
break;
|
|
|
|
case OPTION_COLOUR:
|
|
settings->setValue(option->key, option->value.c);
|
|
break;
|
|
|
|
case OPTION_STRING:
|
|
settings->setValue(option->key,
|
|
QString(((option->value.s == NULL) ||
|
|
(*option->value.s == 0)) ?
|
|
"" : option->value.s));
|
|
break;
|
|
}
|
|
|
|
return NSERROR_OK;
|
|
}
|
|
|
|
void NS_Application::nsOptionPersist()
|
|
{
|
|
QSettings settings;
|
|
settings.clear();
|
|
nsoption_generate(set_qtsetting,
|
|
&settings,
|
|
NSOPTION_GENERATE_CHANGED,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
|
|
|
|
NS_Application::NS_Application(int &argc, char **argv, struct netsurf_table *nsqt_table)
|
|
:QApplication(argc, argv),
|
|
m_settings_window(nullptr),
|
|
m_bookmarks_window(nullptr),
|
|
m_local_history_window(nullptr),
|
|
m_global_history_window(nullptr),
|
|
m_cookies_window(nullptr)
|
|
{
|
|
nserror res;
|
|
|
|
nsqtapp = this;
|
|
|
|
/* register operation tables */
|
|
res = netsurf_register(nsqt_table);
|
|
if (res != NSERROR_OK) {
|
|
throw NS_Exception("NetSurf operation table failed registration", res);
|
|
}
|
|
|
|
/* organization setup for settings */
|
|
setOrganizationName("NetSurf");
|
|
setOrganizationDomain("netsurf-browser.org");
|
|
setApplicationName("NetSurf");
|
|
|
|
// set up scheduler timer
|
|
m_schedule_timer = new QTimer(this);
|
|
m_schedule_timer->setSingleShot(true);
|
|
connect(m_schedule_timer, &QTimer::timeout,
|
|
this, &NS_Application::schedule_run);
|
|
|
|
/* Prep the resource search paths */
|
|
res = nsqt_init_resource_path("${HOME}/.netsurf/:${NETSURFRES}:" QT_RESPATH);
|
|
if (res != NSERROR_OK) {
|
|
throw NS_Exception("Resources failed to initialise",res);
|
|
}
|
|
|
|
/* Initialise logging. Not fatal if it fails but not much we
|
|
* can do about it either.
|
|
*/
|
|
nslog_init(nslog_stream_configure, &argc, argv);
|
|
|
|
/* Initialise user options */
|
|
res = nsoption_init(set_option_defaults, &nsoptions, &nsoptions_default);
|
|
if (res != NSERROR_OK) {
|
|
throw NS_Exception("Options failed to initialise", res);
|
|
}
|
|
|
|
/* load user options */
|
|
nsOptionLoad();
|
|
|
|
/* override loaded options with those from commandline */
|
|
nsoption_commandline(&argc, argv, nsoptions);
|
|
|
|
/* setup bitmap format */
|
|
bitmap_fmt_t qtfmt = {
|
|
.layout = BITMAP_LAYOUT_ARGB8888,
|
|
.pma = false,
|
|
};
|
|
bitmap_set_format(&qtfmt);
|
|
|
|
QResource messages_res("Messages");
|
|
QByteArray messages_data = messages_res.uncompressedData();
|
|
res = messages_add_from_inline((uint8_t*)messages_data.data(),
|
|
messages_data.size());
|
|
|
|
char *addr = NULL;
|
|
nsurl *url = NULL;
|
|
|
|
/* netsurf initialisation */
|
|
res = netsurf_init(NULL);
|
|
if (res != NSERROR_OK) {
|
|
throw NS_Exception("Netsurf core initialisation failed", res);
|
|
}
|
|
|
|
/* initialise url database from user data */
|
|
urldb_load(nsoption_charp(url_file));
|
|
|
|
/* initialise cookies database from user data */
|
|
urldb_load_cookies(nsoption_charp(cookie_file));
|
|
|
|
/* initialise the bookmarks from user data */
|
|
hotlist_init(nsoption_charp(hotlist_path),
|
|
nsoption_charp(hotlist_path));
|
|
|
|
/* If there is a url specified on the command line use it */
|
|
if (argc > 1) {
|
|
struct stat fs;
|
|
if (stat(argv[1], &fs) == 0) {
|
|
size_t addrlen;
|
|
char *rp = realpath(argv[1], NULL);
|
|
assert(rp != NULL);
|
|
|
|
/* calculate file url length including terminator */
|
|
addrlen = SLEN("file://") + strlen(rp) + 1;
|
|
addr = (char *)malloc(addrlen);
|
|
assert(addr != NULL);
|
|
snprintf(addr, addrlen, "file://%s", rp);
|
|
free(rp);
|
|
} else {
|
|
addr = strdup(argv[1]);
|
|
}
|
|
|
|
/* convert initial target to url */
|
|
res = nsurl_create(addr, &url);
|
|
if (res != NSERROR_OK) {
|
|
throw NS_Exception("failed converting initial url", res);
|
|
}
|
|
free(addr);
|
|
}
|
|
|
|
res = create_browser_widget(url, NULL, false);
|
|
if (res != NSERROR_OK) {
|
|
throw NS_Exception("Opening initial url failed", res);
|
|
}
|
|
}
|
|
|
|
NS_Application::~NS_Application()
|
|
{
|
|
nserror res;
|
|
|
|
delete m_cookies_window;
|
|
delete m_global_history_window;
|
|
delete m_local_history_window;
|
|
delete m_bookmarks_window;
|
|
delete m_settings_window;
|
|
|
|
/* finalise cookie database */
|
|
urldb_save_cookies(nsoption_charp(cookie_jar));
|
|
|
|
/* finalise url database */
|
|
urldb_save(nsoption_charp(url_file));
|
|
|
|
res = hotlist_fini();
|
|
if (res != NSERROR_OK) {
|
|
NSLOG(netsurf, INFO, "Error finalising hotlist: %s",
|
|
messages_get_errorcode(res));
|
|
}
|
|
|
|
/* common finalisation */
|
|
netsurf_exit();
|
|
|
|
/* finalise options */
|
|
nsoption_finalise(nsoptions, nsoptions_default);
|
|
|
|
/* finalise logging */
|
|
nslog_finalise();
|
|
|
|
delete m_schedule_timer;
|
|
}
|
|
|
|
/**
|
|
* scheduled timer slot
|
|
*/
|
|
void NS_Application::schedule_run()
|
|
{
|
|
int ms;
|
|
ms = nsqt_schedule_run();
|
|
if (ms >= 0) {
|
|
m_schedule_timer->start(ms);
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
void NS_Application::next_schedule(int ms)
|
|
{
|
|
if ((m_schedule_timer->isActive()==false) ||
|
|
(m_schedule_timer->remainingTime() > ms)) {
|
|
m_schedule_timer->start(ms);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* show settings window
|
|
*/
|
|
void NS_Application::settings_show()
|
|
{
|
|
if (m_settings_window == nullptr) {
|
|
m_settings_window = new NS_Settings(nullptr);
|
|
}
|
|
m_settings_window->show();
|
|
m_settings_window->raise();
|
|
}
|
|
|
|
|
|
/**
|
|
* show bookmarks window
|
|
*/
|
|
void NS_Application::bookmarks_show()
|
|
{
|
|
if (m_bookmarks_window == nullptr) {
|
|
m_bookmarks_window = new NS_Bookmarks(nullptr);
|
|
}
|
|
m_bookmarks_window->show();
|
|
m_bookmarks_window->raise();
|
|
}
|
|
|
|
/**
|
|
* show local history window
|
|
*/
|
|
void NS_Application::local_history_show(struct browser_window *bw, const QPoint &pos)
|
|
{
|
|
if (m_local_history_window == nullptr) {
|
|
m_local_history_window = new NS_Local_history(nullptr, bw);
|
|
} else {
|
|
m_local_history_window->setbw(bw);
|
|
}
|
|
m_local_history_window->move(pos);
|
|
m_local_history_window->show();
|
|
m_local_history_window->raise();
|
|
}
|
|
|
|
|
|
/**
|
|
* show page info window
|
|
*/
|
|
void NS_Application::page_info_show(struct browser_window *bw, const QPoint &pos)
|
|
{
|
|
NS_Page_info *page_info = new NS_Page_info(nullptr, bw);
|
|
page_info->move(pos);
|
|
page_info->show();
|
|
page_info->raise();
|
|
}
|
|
|
|
/**
|
|
* show global history window
|
|
*/
|
|
void NS_Application::global_history_show()
|
|
{
|
|
if (m_global_history_window == nullptr) {
|
|
m_global_history_window = new NS_Global_history(nullptr);
|
|
}
|
|
m_global_history_window->show();
|
|
m_global_history_window->raise();
|
|
}
|
|
|
|
/**
|
|
* show cookies window
|
|
*/
|
|
nserror NS_Application::cookies_show(const char *search_term)
|
|
{
|
|
nserror res;
|
|
if (m_cookies_window == nullptr) {
|
|
m_cookies_window = new NS_Cookies(nullptr);
|
|
}
|
|
res = m_cookies_window->setSearch(search_term);
|
|
m_cookies_window->show();
|
|
m_cookies_window->raise();
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
/* create a new browsing context in a tab or window */
|
|
nserror NS_Application::create_browser_widget(nsurl *url,
|
|
struct browser_window *existing,
|
|
bool intab)
|
|
{
|
|
nserror res = NSERROR_OK;
|
|
int flags = BW_CREATE_HISTORY | BW_CREATE_FOCUS_LOCATION | BW_CREATE_FOREGROUND;
|
|
|
|
if (intab) {
|
|
flags |= BW_CREATE_TAB;
|
|
}
|
|
|
|
if ((url == NULL) && (!nsoption_bool(new_blank))) {
|
|
const char *addr;
|
|
if (nsoption_charp(homepage_url) != NULL) {
|
|
addr = nsoption_charp(homepage_url);
|
|
} else {
|
|
addr = NETSURF_HOMEPAGE;
|
|
}
|
|
res = nsurl_create(addr, &url);
|
|
}
|
|
|
|
if (res == NSERROR_OK) {
|
|
res = browser_window_create((enum browser_window_create_flags)flags,
|
|
url,
|
|
NULL,
|
|
existing,
|
|
NULL);
|
|
}
|
|
|
|
if (url != NULL) {
|
|
nsurl_unref(url);
|
|
}
|
|
|
|
return res;
|
|
}
|