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.
netsurf/frontends/qt/actions.cpp

523 lines
13 KiB

/*
* Copyright 2023 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
* Implementations of netsurf qt actions.
*/
#include <cfloat>
#include <QWidget>
#include <QStyle>
#include <QPainter>
#include <QLabel>
#include <QHBoxLayout>
extern "C" {
#include "utils/messages.h"
#include "utils/nsoption.h"
#include "utils/nsurl.h"
#include "utils/log.h"
#include "desktop/browser_history.h"
#include "desktop/hotlist.h"
}
#include "qt/application.cls.h"
#include "qt/urlbar.cls.h"
#include "qt/actions.cls.h"
NS_Actions::NS_Actions(QWidget* parent, struct browser_window *bw) :
QObject(parent),
m_back(new QAction(parent->style()->standardIcon(QStyle::SP_ArrowLeft),
messages_get("Back"),
parent)),
m_forward(new QAction(parent->style()->standardIcon(QStyle::SP_ArrowRight),
messages_get("Forward"),
parent)),
m_stop_reload(new QAction(parent)),
m_settings(new QAction(messages_get("Settings"), parent)),
m_bookmarks(new QAction(messages_get("ManageBookmarks"), parent)),
m_add_edit_bookmark(new QAction(QIcon(":/icons/hotlist-add.png"),
messages_get("AddBookmark"),
parent)),
m_local_history(new QAction(QIcon(":/local-history.png"),
messages_get("HistLocalNS"),
parent)),
m_global_history(new QAction(messages_get("HistGlobalNS"), parent)),
m_cookies(new QAction(messages_get("ShowCookiesNS"), parent)),
m_page_info(new QAction(QIcon(":/icons/page-info-internal.svg"),
messages_get("PageInfo"),
parent)),
m_page_scale(new QAction(messages_get("PageScale"), parent)),
m_reset_page_scale(new QAction(messages_get("PageScaleReset"), parent)),
m_reduce_page_scale(new QAction(messages_get("PageScaleReduce"), parent)),
m_increase_page_scale(new QAction(messages_get("PageScaleIncrease"), parent)),
m_newtab(new QAction(messages_get("NewTab"), parent)),
m_newwindow(new QAction(messages_get("NewWindowNS"), parent)),
m_quit(new QAction(messages_get("Quit"), parent)),
m_page_source(new QAction(messages_get("PageSource"), parent)),
m_debug_render(new QAction(messages_get("DebugRender"), parent)),
m_debug_box_tree(new QAction(messages_get("DebugBoxTree"), parent)),
m_debug_dom_tree(new QAction(messages_get("DebugDomTree"), parent)),
m_about_netsurf(new QAction(messages_get("About"), parent)),
m_bw(bw),
m_marked(false),
m_pistate(PAGE_STATE_INTERNAL)
{
/* shortcuts */
m_back->setShortcut(QKeySequence::Back);
m_forward->setShortcut(QKeySequence::Forward);
m_bookmarks->setShortcut(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_O));
m_global_history->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_H));
m_newtab->setShortcut(QKeySequence::AddTab);
m_newwindow->setShortcut(QKeySequence::New);
m_quit->setShortcut(QKeySequence::Quit);
m_page_source->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_U));
//m_quit->setShortcutContext(Qt::WidgetWithChildrenShortcut);
//m_newtab->setShortcutContext(Qt::WidgetWithChildrenShortcut);
//m_newwindow->setShortcutContext(Qt::WidgetWithChildrenShortcut);
/* icon texts */
m_reduce_page_scale->setIconText(messages_get("PageScaleReduceShort"));
m_increase_page_scale->setIconText(messages_get("PageScaleIncreaseShort"));
/* hook up the signals */
connect(m_back, &QAction::triggered,
this, &NS_Actions::back_slot);
connect(m_forward, &QAction::triggered,
this, &NS_Actions::forward_slot);
connect(m_stop_reload, &QAction::triggered,
this, &NS_Actions::stop_reload_slot);
connect(m_settings, &QAction::triggered,
this, &NS_Actions::settings_slot);
connect(m_bookmarks, &QAction::triggered,
this, &NS_Actions::bookmarks_slot);
connect(m_add_edit_bookmark, &QAction::triggered,
this, &NS_Actions::add_edit_bookmark_slot);
connect(m_local_history, &QAction::triggered,
this, &NS_Actions::local_history_slot);
connect(m_global_history, &QAction::triggered,
this, &NS_Actions::global_history_slot);
connect(m_cookies, &QAction::triggered,
this, &NS_Actions::cookies_slot);
connect(m_page_info, &QAction::triggered,
this, &NS_Actions::page_info_slot);
connect(m_page_scale, &QAction::triggered,
this, &NS_Actions::reset_page_scale_slot);
connect(m_reset_page_scale, &QAction::triggered,
this, &NS_Actions::reset_page_scale_slot);
connect(m_reduce_page_scale, &QAction::triggered,
this, &NS_Actions::reduce_page_scale_slot);
connect(m_increase_page_scale, &QAction::triggered,
this, &NS_Actions::increase_page_scale_slot);
connect(m_newtab, &QAction::triggered,
this, &NS_Actions::newtab_slot);
connect(m_newwindow, &QAction::triggered,
this, &NS_Actions::newwindow_slot);
connect(m_quit, &QAction::triggered,
this, &NS_Actions::quit_slot);
update_navigation(NS_Actions::UpdateInactive);
update_page_info();
update_page_scale();
update_bookmarks();
}
NS_Actions::~NS_Actions()
{
delete m_back;
delete m_forward;
}
/*
* get tool button associated with action
*/
QToolButton *NS_Actions::QToolButtonFromQAction(QAction *action)
{
QList <QObject *> widget_list = action->associatedObjects();
/* iterate objects associated with action */
for (int idx = 0; idx < widget_list.count(); idx++) {
#ifdef BUTTON_QOBJECT_CAST
// @todo find out why qobject_cast fails (always null) here
QToolButton *button = qobject_cast<QToolButton *>(widget_list.at(idx));
#else
const char *clsname = widget_list.at(idx)->metaObject()->className();
//NSLOG(netsurf, WARN, "%s",clsname);
if ((strcmp(clsname, "QToolButton") == 0) ||
(strcmp(clsname, "QLineEditIconButton") == 0)) {
return (QToolButton *)(widget_list.at(idx));
}
#endif
}
return nullptr;
}
/*
* obtain the bottom left corner global location of an action attached widget
*/
QPoint NS_Actions::actionGlobal(QAction *action)
{
QToolButton *button = QToolButtonFromQAction(action);
if (button == nullptr) {
/* no associated button default to cursor position */
return QCursor::pos();
}
return button->mapToGlobal(QPoint(0, button->height()));
}
/* Change action state appropriate for flag */
void NS_Actions::update(NS_Actions::Update update)
{
switch (update) {
case UpdateInactive:
case UpdateActive:
case UpdateUnchanged:
update_navigation(update);
update_bookmarks();
update_page_scale();
break;
case UpdatePageInfo:
update_page_info();
break;
case UpdateBookmarks:
update_bookmarks();
break;
case UpdatePageScale:
update_page_scale();
break;
}
}
/*
* page scale action widget for menu
*/
QWidgetAction *NS_Actions::page_scale_widget_action(QWidget* parent)
{
QLabel *scalelabel = new QLabel("Page Scale");
QToolButton *scaleminus = new QToolButton();
scaleminus->setDefaultAction(m_reduce_page_scale);
scaleminus->setStyleSheet("QToolButton {border: none; }");
QToolButton *scalevalue = new QToolButton();
scalevalue->setDefaultAction(m_page_scale);
scalevalue->setStyleSheet("QToolButton {border: none; }");
QToolButton *scaleplus = new QToolButton();
scaleplus->setDefaultAction(m_increase_page_scale);
scaleplus->setStyleSheet("QToolButton {border: none; }");
QHBoxLayout *scalelayout = new QHBoxLayout();
scalelayout->addSpacing(20);
scalelayout->addWidget(scalelabel);
scalelayout->addStretch();
scalelayout->addWidget(scaleminus);
scalelayout->addWidget(scalevalue);
scalelayout->addWidget(scaleplus);
QWidget *scalewidget = new QWidget();
scalewidget->setLayout(scalelayout);
QWidgetAction *scaleaction = new QWidgetAction(parent);
scaleaction->setDefaultWidget(scalewidget);
return scaleaction;
}
QIcon NS_Actions::QIconFromText(QString text)
{
QPixmap pixmap(64, 64);
QPainter painter(&pixmap);
painter.fillRect(QRect(0, 0, 64, 64), QColor(230,230,230));
QFont font = painter.font();
font.setFamily("Helvetica");
font.setStretch(75);
font.setPixelSize(34);
painter.setFont(font);
painter.setPen(QColor(Qt::black));
painter.drawText(0, 45, text);
return QIcon(pixmap);
}
void NS_Actions::update_page_scale()
{
double scale = browser_window_get_scale(m_bw) * 100.0;
QString scaletext = QString::number(scale)+"%";
m_reset_page_scale->setVisible(scale != nsoption_int(scale));
#ifdef USE_ICON_FOR_SCALE
m_reset_page_scale->setIcon(QIconFromText(scaletext));
#endif
m_reset_page_scale->setIconText(scaletext);
m_page_scale->setIconText(scaletext);
}
void NS_Actions::update_page_info()
{
/* manage page information state */
browser_window_page_info_state pistate;
pistate = browser_window_get_page_info_state(m_bw);
if (pistate != m_pistate) {
m_pistate = pistate;
QString fname(":/icons/page-info-internal.png");
switch (pistate) {
case PAGE_STATE_LOCAL:
fname=":/icons/page-info-local.png";
break;
case PAGE_STATE_INSECURE:
fname=":/icons/page-info-insecure.png";
break;
case PAGE_STATE_SECURE_OVERRIDE:
fname=":/icons/page-info-warning.png";
break;
case PAGE_STATE_SECURE_ISSUES:
fname=":/icons/page-info-warning.png";
break;
case PAGE_STATE_SECURE:
fname=":/icons/page-info-secure.png";
break;
default:
break;
}
m_page_info->setIcon(QIcon(fname));
}
}
void NS_Actions::update_bookmarks()
{
/* manage bookmark state */
struct nsurl *url;
if (browser_window_get_url(m_bw, true, &url) != NSERROR_OK) {
return;
}
bool marked = hotlist_has_url(url);
nsurl_unref(url);
if (marked != m_marked) {
m_marked = marked;
if (m_marked) {
m_add_edit_bookmark->setIcon(QIcon(":/icons/hotlist-rmv.png"));
m_add_edit_bookmark->setText(messages_get("EditBookmark"));
} else {
m_add_edit_bookmark->setIcon(QIcon(":/icons/hotlist-add.png"));
m_add_edit_bookmark->setText(messages_get("AddBookmark"));
}
}
}
void NS_Actions::update_navigation(NS_Actions::Update update)
{
/* manage stop/reload state */
QWidget* parentw = qobject_cast<QWidget*>(parent());
switch (update) {
case UpdateInactive:
m_active = false;
m_stop_reload->setIcon(parentw->style()->standardIcon(QStyle::SP_BrowserReload));
m_stop_reload->setText(messages_get("Reload"));
m_stop_reload->setShortcut(QKeySequence::Refresh);
break;
case UpdateActive:
m_active = true;
m_stop_reload->setIcon(parentw->style()->standardIcon(QStyle::SP_BrowserStop));
m_stop_reload->setText(messages_get("Stop"));
m_stop_reload->setShortcut(QKeySequence::Cancel);
break;
default:
break;
}
/* manage history navigation state */
m_back->setEnabled(browser_window_history_back_available(m_bw));
m_forward->setEnabled(browser_window_history_forward_available(m_bw));
}
#define PAGE_SCALE_COUNT 17
static const float page_scales[PAGE_SCALE_COUNT]={ 0.33, 0.50, 0.67, 0.75, 0.80, 0.90, 1.00, 1.10, 1.20, 1.33, 1.50, 1.70, 2.00, 2.40, 3.00, 4.00, 5.00 };
void NS_Actions::change_page_scale(int step)
{
float scale = browser_window_get_scale(m_bw);
float max_delta = FLT_MAX;
int sel_idx = 0;
for (int idx = 0; idx < PAGE_SCALE_COUNT; idx++) {
float delta = fabs(page_scales[idx] - scale);
if (delta < max_delta) {
max_delta = delta;
sel_idx = idx;
}
}
sel_idx += step;
if (sel_idx < 0) {
sel_idx = 0;
} else if (sel_idx >= PAGE_SCALE_COUNT) {
sel_idx = PAGE_SCALE_COUNT - 1;
}
browser_window_set_scale(m_bw, page_scales[sel_idx], true);
update_page_scale();
}
/* slots */
void NS_Actions::back_slot(bool checked)
{
browser_window_history_back(m_bw, false);
}
void NS_Actions::forward_slot(bool checked)
{
browser_window_history_forward(m_bw, false);
}
void NS_Actions::stop_reload_slot(bool checked)
{
if (m_active) {
browser_window_stop(m_bw);
} else {
browser_window_reload(m_bw, true);
}
}
void NS_Actions::settings_slot(bool checked)
{
NS_Application::instance()->settings_show();
}
void NS_Actions::bookmarks_slot(bool checked)
{
NS_Application::instance()->bookmarks_show();
}
void NS_Actions::add_edit_bookmark_slot(bool checked)
{
struct nsurl *url;
if (browser_window_get_url(m_bw, true, &url) == NSERROR_OK) {
bool marked = hotlist_has_url(url);
if (marked) {
hotlist_remove_url(url);
} else {
hotlist_add_url(url);
}
update_bookmarks();
nsurl_unref(url);
}
}
void NS_Actions::local_history_slot(bool checked)
{
NS_Application::instance()->local_history_show(m_bw, actionGlobal(m_local_history));
}
void NS_Actions::global_history_slot(bool checked)
{
NS_Application::instance()->global_history_show();
}
void NS_Actions::cookies_slot(bool checked)
{
NS_Application::instance()->cookies_show();
}
void NS_Actions::page_info_slot(bool checked)
{
NS_Application::instance()->page_info_show(m_bw, actionGlobal(m_page_info));
}
void NS_Actions::reset_page_scale_slot(bool checked)
{
browser_window_set_scale(m_bw, (float)nsoption_int(scale)/100.0, true);
update_page_scale();
}
void NS_Actions::reduce_page_scale_slot(bool checked)
{
change_page_scale(-1);
}
void NS_Actions::increase_page_scale_slot(bool checked)
{
change_page_scale(1);
}
void NS_Actions::newtab_slot(bool checked)
{
NS_Application::create_browser_widget(NULL, m_bw, true);
}
void NS_Actions::newwindow_slot(bool checked)
{
NS_Application::create_browser_widget(NULL, m_bw, false);
}
void NS_Actions::quit_slot(bool checked)
{
NS_Application::instance()->quit();
}