From 33934e12713f2ba8f5369a27f6abf725862f1a18 Mon Sep 17 00:00:00 2001 From: Vincent Sanders Date: Sun, 30 Dec 2012 01:17:17 +0000 Subject: implement the GTK preferances dialog with signals --- desktop/options.h | 3 +- desktop/options_main.h | 4 + gtk/Makefile.target | 2 +- gtk/dialogs/options.c | 1184 --------------------------------------------- gtk/dialogs/options.h | 34 -- gtk/dialogs/preferences.c | 1082 +++++++++++++++++++++++++++++++++++++++++ gtk/dialogs/preferences.h | 32 ++ gtk/gui.c | 13 +- gtk/gui.h | 1 + gtk/res/options.gtk2.ui | 274 ++++++++--- gtk/scaffolding.c | 15 +- gtk/theme.c | 52 +- gtk/theme.h | 2 +- 13 files changed, 1380 insertions(+), 1318 deletions(-) delete mode 100644 gtk/dialogs/options.c delete mode 100644 gtk/dialogs/options.h create mode 100644 gtk/dialogs/preferences.c create mode 100644 gtk/dialogs/preferences.h diff --git a/desktop/options.h b/desktop/options.h index 53c025c18..17ba64f6c 100644 --- a/desktop/options.h +++ b/desktop/options.h @@ -108,7 +108,8 @@ extern struct ns_options nsoptions; free(nsoptions.OPTION); \ } \ nsoptions.OPTION = VALUE; \ - if (*nsoptions.OPTION == 0) { \ + if ((nsoptions.OPTION != NULL) && \ + (*nsoptions.OPTION == 0)) { \ free(nsoptions.OPTION); \ nsoptions.OPTION = NULL; \ } \ diff --git a/desktop/options_main.h b/desktop/options_main.h index 9ec4888bc..7b9e7314b 100644 --- a/desktop/options_main.h +++ b/desktop/options_main.h @@ -66,6 +66,8 @@ char *accept_charset; \ /** Preferred maximum size of memory cache / bytes. */ \ int memory_cache_size; \ + /** Preferred expiry size of disc cache / bytes. */ \ + int disc_cache_size; \ /** Preferred expiry age of disc cache / days. */ \ int disc_cache_age; \ /** Whether to block advertisements */ \ @@ -220,6 +222,7 @@ .accept_language = NULL, \ .accept_charset = NULL, \ .memory_cache_size = 12 * 1024 * 1024, \ + .disc_cache_size = 1024 * 1024 * 1024, \ .disc_cache_age = 28, \ .block_ads = false, \ .do_not_track = false, \ @@ -315,6 +318,7 @@ { "accept_language", OPTION_STRING, &nsoptions.accept_language }, \ { "accept_charset", OPTION_STRING, &nsoptions.accept_charset }, \ { "memory_cache_size", OPTION_INTEGER, &nsoptions.memory_cache_size }, \ + { "disc_cache_size", OPTION_INTEGER, &nsoptions.disc_cache_size }, \ { "disc_cache_age", OPTION_INTEGER, &nsoptions.disc_cache_age }, \ { "block_advertisements", OPTION_BOOL, &nsoptions.block_ads }, \ { "do_not_track", OPTION_BOOL, &nsoptions.do_not_track }, \ diff --git a/gtk/Makefile.target b/gtk/Makefile.target index 2d1eebf50..88f8eccde 100644 --- a/gtk/Makefile.target +++ b/gtk/Makefile.target @@ -112,7 +112,7 @@ S_GTK := font_pango.c bitmap.c gui.c schedule.c thumbnail.c plotters.c \ selection.c history.c window.c filetype.c download.c menu.c \ print.c search.c tabs.c theme.c toolbar.c \ compat.c cookies.c hotlist.c system_colour.c \ - $(addprefix dialogs/,options.c about.c source.c) + $(addprefix dialogs/,preferences.c about.c source.c) S_GTK := $(addprefix gtk/,$(S_GTK)) $(addprefix utils/,container.c) # code in utils/container.ch is non-universal it seems diff --git a/gtk/dialogs/options.c b/gtk/dialogs/options.c deleted file mode 100644 index 8bd5665a3..000000000 --- a/gtk/dialogs/options.c +++ /dev/null @@ -1,1184 +0,0 @@ -/* - * Copyright 2006 Rob Kendrick - * Copyright 2008 Mike Lester - * Copyright 2009 Daniel Silverstone - * Copyright 2009 Mark Benjamin - * - * 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 . - */ - -#include -#include -#include -#include -#include -#include - -#include "desktop/browser_private.h" -#include "desktop/options.h" -#include "desktop/print.h" -#include "desktop/searchweb.h" - -#include "gtk/compat.h" -#include "gtk/gui.h" -#include "gtk/scaffolding.h" -#include "gtk/theme.h" -#include "gtk/dialogs/options.h" -#include "gtk/window.h" -#include "utils/log.h" -#include "utils/utils.h" -#include "utils/messages.h" - -GtkDialog *wndPreferences = NULL; -static GtkBuilder *gladeFile; - -static struct browser_window *current_browser; - -static void dialog_response_handler (GtkDialog *dlg, gint res_id); -static gboolean on_dialog_close (GtkDialog *dlg, gboolean stay_alive); -static void nsgtk_options_theme_combo(void); - -/* Declares both widget and callback */ -#define DECLARE(x) \ - static GtkWidget *x; \ - static gboolean on_##x##_changed(GtkWidget *widget, gpointer data) - -DECLARE(entryHomePageURL); -DECLARE(setCurrentPage); -DECLARE(setDefaultPage); -DECLARE(checkHideAdverts); -DECLARE(checkDisablePopups); -DECLARE(checkDisablePlugins); -DECLARE(spinHistoryAge); -DECLARE(checkHoverURLs); -DECLARE(checkDisplayRecentURLs); -//DECLARE(comboboxLanguage); -static GtkWidget *comboboxLanguage; -static gboolean on_comboboxLanguage_changed(GtkComboBox *combo, gpointer data); -DECLARE(checkSendReferer); -DECLARE(checkSendDNT); - -DECLARE(comboProxyType); -DECLARE(entryProxyHost); -DECLARE(spinProxyPort); -DECLARE(entryProxyUser); -DECLARE(entryProxyPassword); -DECLARE(spinMaxFetchers); -DECLARE(spinFetchesPerHost); -DECLARE(spinCachedConnections); - -DECLARE(checkEnableJavascript); - -DECLARE(checkResampleImages); -DECLARE(spinAnimationSpeed); -DECLARE(checkEnableAnimations); - -//DECLARE(fontSansSerif); -//DECLARE(fontSerif); -//DECLARE(fontMonospace); -//DECLARE(fontCursive); -//DECLARE(fontFantasy); -DECLARE(comboDefault); -DECLARE(spinDefaultSize); -//DECLARE(spinMinimumSize); -DECLARE(fontPreview); - -DECLARE(comboButtonType); - -DECLARE(spinMemoryCacheSize); -DECLARE(spinDiscCacheAge); - -DECLARE(checkClearDownloads); -DECLARE(checkRequestOverwrite); -DECLARE(fileChooserDownloads); -/* Tabs */ -DECLARE(checkShowSingleTab); -DECLARE(checkFocusNew); -DECLARE(checkNewBlank); -DECLARE(comboTabPosition); - -DECLARE(checkUrlSearch); -DECLARE(comboSearch); -DECLARE(combotheme); -DECLARE(buttonaddtheme); -DECLARE(sourceButtonTab); -static GtkWidget *sourceButtonWindow; - -DECLARE(spinMarginTop); -DECLARE(spinMarginBottom); -DECLARE(spinMarginLeft); -DECLARE(spinMarginRight); -DECLARE(spinExportScale); -DECLARE(checkSuppressImages); -DECLARE(checkRemoveBackgrounds); -DECLARE(checkFitPage); -DECLARE(checkCompressPDF); -DECLARE(checkPasswordPDF); -//DECLARE(setDefaultExportOptions); - -/* Used when the feature is not implemented yet */ -#define FIND_WIDGET(wname) \ - do { \ - (wname) = GTK_WIDGET(gtk_builder_get_object(gladeFile, #wname)); \ - if ((wname) == NULL) \ - LOG(("Unable to find widget '%s'!", #wname)); \ - } while (0) - -/* Assigns widget and connects it to its callback function */ -#define CONNECT(wname, event) \ - do { \ - if ((wname) == NULL) \ - LOG(("Unable to find widget '%s'!", #wname)); \ - else \ - g_signal_connect(G_OBJECT(wname), event, \ - G_CALLBACK(on_##wname##_changed), NULL); \ - } while (0) - -/* exported interface documented in gtk/dialogs/options.h */ -GtkDialog* -nsgtk_options_init(struct browser_window *bw, GtkWindow *parent) -{ - GError *error = NULL; - GObject *dlgobject; - //GSList *group; - - gladeFile = gtk_builder_new(); - if (!gtk_builder_add_from_file(gladeFile, glade_file_location->options, &error)) { - g_warning("Couldn't load builder file: %s", error->message); - g_error_free(error); - return NULL; - } - - - dlgobject = gtk_builder_get_object(gladeFile, "dialogPreferences"); - if (dlgobject == NULL) { - LOG(("Unable to get object for preferences dialog")); - return NULL; - } - - current_browser = bw; - wndPreferences = GTK_DIALOG(dlgobject); - gtk_window_set_transient_for(GTK_WINDOW(wndPreferences), parent); - - /* set the widgets to reflect the current options */ - nsgtk_options_load(); - - FIND_WIDGET(sourceButtonTab); - FIND_WIDGET(sourceButtonWindow); - //group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(sourceButtonWindow)); - //gtk_radio_button_set_group(GTK_RADIO_BUTTON(sourceButtonTab), group); - - - /* Connect all widgets to their appropriate callbacks */ - CONNECT(entryHomePageURL, "focus-out-event"); - CONNECT(setCurrentPage, "clicked"); - CONNECT(setDefaultPage, "clicked"); - CONNECT(checkHideAdverts, "toggled"); - - CONNECT(checkDisablePopups, "toggled"); - CONNECT(checkDisablePlugins, "toggled"); - CONNECT(spinHistoryAge, "focus-out-event"); - CONNECT(checkHoverURLs, "toggled"); - - CONNECT(comboboxLanguage, "changed"); - - CONNECT(checkDisplayRecentURLs, "toggled"); - CONNECT(checkSendReferer, "toggled"); - CONNECT(checkSendDNT, "toggled"); - CONNECT(checkShowSingleTab, "toggled"); - - CONNECT(comboProxyType, "changed"); - CONNECT(entryProxyHost, "focus-out-event"); - CONNECT(spinProxyPort, "focus-out-event"); - CONNECT(entryProxyUser, "focus-out-event"); - CONNECT(entryProxyPassword, "focus-out-event"); - CONNECT(spinMaxFetchers, "value-changed"); - CONNECT(spinFetchesPerHost, "value-changed"); - CONNECT(spinCachedConnections, "value-changed"); - - CONNECT(checkEnableJavascript, "toggled"); - - CONNECT(checkResampleImages, "toggled"); - CONNECT(spinAnimationSpeed, "value-changed"); - CONNECT(checkEnableAnimations, "toggled"); - -/* CONNECT(fontSansSerif, "font-set"); - CONNECT(fontSerif, "font-set"); - CONNECT(fontMonospace, "font-set"); - CONNECT(fontCursive, "font-set"); - CONNECT(fontFantasy, "font-set"); - CONNECT(spinMinimumSize, "value-changed"); -*/ - CONNECT(comboDefault, "changed"); - CONNECT(spinDefaultSize, "value-changed"); - CONNECT(fontPreview, "clicked"); - - CONNECT(comboButtonType, "changed"); - - CONNECT(comboTabPosition, "changed"); - - CONNECT(spinMemoryCacheSize, "value-changed"); - CONNECT(spinDiscCacheAge, "value-changed"); - - CONNECT(checkClearDownloads, "toggled"); - CONNECT(checkRequestOverwrite, "toggled"); - CONNECT(fileChooserDownloads, "current-folder-changed"); - - CONNECT(checkFocusNew, "toggled"); - CONNECT(checkNewBlank, "toggled"); - CONNECT(checkUrlSearch, "toggled"); - CONNECT(comboSearch, "changed"); - - CONNECT(combotheme, "changed"); - CONNECT(buttonaddtheme, "clicked"); - CONNECT(sourceButtonTab, "toggled"); - - CONNECT(spinMarginTop, "value-changed"); - CONNECT(spinMarginBottom, "value-changed"); - CONNECT(spinMarginLeft, "value-changed"); - CONNECT(spinMarginRight, "value-changed"); - CONNECT(spinExportScale, "value-changed"); - CONNECT(checkSuppressImages, "toggled"); - CONNECT(checkRemoveBackgrounds, "toggled"); - CONNECT(checkFitPage, "toggled"); - CONNECT(checkCompressPDF, "toggled"); - CONNECT(checkPasswordPDF, "toggled"); -// CONNECT(setDefaultExportOptions, "clicked"); - - g_signal_connect(G_OBJECT(wndPreferences), "response", - G_CALLBACK (dialog_response_handler), NULL); - - g_signal_connect(G_OBJECT(wndPreferences), "delete-event", - G_CALLBACK (on_dialog_close), (gpointer)TRUE); - - g_signal_connect(G_OBJECT(wndPreferences), "destroy", - G_CALLBACK (on_dialog_close), (gpointer)FALSE); - - gtk_widget_show(GTK_WIDGET(wndPreferences)); - - return wndPreferences; -} - -#define SET_ENTRY(widget, value) \ - do { \ - (widget) = GTK_WIDGET(gtk_builder_get_object(gladeFile, #widget)); \ - gtk_entry_set_text(GTK_ENTRY((widget)), (value)); \ - } while (0) - -#define SET_SPIN(widget, value) \ - do { \ - (widget) = GTK_WIDGET(gtk_builder_get_object(gladeFile, #widget)); \ - gtk_spin_button_set_value(GTK_SPIN_BUTTON((widget)), (value)); \ - } while (0) - -#define SET_CHECK(widget, value) \ - do { \ - (widget) = GTK_WIDGET(gtk_builder_get_object(gladeFile, #widget)); \ - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON((widget)), \ - (value)); \ - } while (0) - -#define SET_COMBO(widget, value) \ - do { \ - (widget) = GTK_WIDGET(gtk_builder_get_object(gladeFile, #widget)); \ - gtk_combo_box_set_active(GTK_COMBO_BOX((widget)), (value)); \ - } while (0) - -#define SET_FONT(widget, value) \ - do { \ - (widget) = GTK_WIDGET(gtk_builder_get_object(gladeFile, #widget)); \ - gtk_font_button_set_font_name(GTK_FONT_BUTTON((widget)), \ - (value)); \ - } while (0) - -#define SET_FILE_CHOOSER(widget, value) \ - do { \ - (widget) = GTK_WIDGET(gtk_builder_get_object(gladeFile, #widget)); \ - gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(\ - (widget)), (value)); \ - } while (0) - -#define SET_BUTTON(widget) \ - do { \ - (widget) = GTK_WIDGET(gtk_builder_get_object(gladeFile, #widget)); \ - } while (0) - -static void set_proxy_widgets_sensitivity(int proxyval) -{ - switch (proxyval) { - case 0: /* no proxy */ - gtk_widget_set_sensitive(entryProxyHost, FALSE); - gtk_widget_set_sensitive(spinProxyPort, FALSE); - gtk_widget_set_sensitive(entryProxyUser, FALSE); - gtk_widget_set_sensitive(entryProxyPassword, FALSE); - break; - - case 1: /* proxy with no auth */ - gtk_widget_set_sensitive(entryProxyHost, TRUE); - gtk_widget_set_sensitive(spinProxyPort, TRUE); - gtk_widget_set_sensitive(entryProxyUser, FALSE); - gtk_widget_set_sensitive(entryProxyPassword, FALSE); - break; - - case 2: /* proxy with basic auth */ - gtk_widget_set_sensitive(entryProxyHost, TRUE); - gtk_widget_set_sensitive(spinProxyPort, TRUE); - gtk_widget_set_sensitive(entryProxyUser, TRUE); - gtk_widget_set_sensitive(entryProxyPassword, TRUE); - break; - - case 3: /* proxy with ntlm auth */ - gtk_widget_set_sensitive(entryProxyHost, TRUE); - gtk_widget_set_sensitive(spinProxyPort, TRUE); - gtk_widget_set_sensitive(entryProxyUser, TRUE); - gtk_widget_set_sensitive(entryProxyPassword, TRUE); - break; - - case 4: /* system proxy */ - gtk_widget_set_sensitive(entryProxyHost, FALSE); - gtk_widget_set_sensitive(spinProxyPort, FALSE); - gtk_widget_set_sensitive(entryProxyUser, FALSE); - gtk_widget_set_sensitive(entryProxyPassword, FALSE); - break; - - } -} - -void nsgtk_options_load(void) -{ - const char *default_accept_language = "en"; - const char *default_homepage_url = ""; - const char *default_http_proxy_host; - const char *default_http_proxy_auth_user; - const char *default_http_proxy_auth_pass; - - int active_language = 0; - GtkListStore *liststore; - GtkTreeIter iter; - - int proxytype = 0; - FILE *fp; - char buf[50]; - - /* Network - HTTP Proxy */ - default_http_proxy_host = nsoption_charp(http_proxy_host); - default_http_proxy_auth_user = nsoption_charp(http_proxy_auth_user); - default_http_proxy_auth_pass = nsoption_charp(http_proxy_auth_pass); - - if (nsoption_bool(http_proxy) == true) { - /* proxy type combo box starts with disabled, to allow - * for this the http_proxy option needs combining with - * the http_proxy_auth option - */ - proxytype = nsoption_int(http_proxy_auth) + 1; - if (default_http_proxy_host == NULL) { - /* set to use a proxy without a host, turn proxy off */ - proxytype = 0; - } else if (((proxytype == 2) || - (proxytype == 3)) && - ((default_http_proxy_auth_user == NULL) || - (default_http_proxy_auth_pass == NULL))) { - /* authentication selected with empty credentials, turn proxy off */ - proxytype = 0; - } - } - - if (default_http_proxy_host == NULL) { - default_http_proxy_host = ""; - } - - if (default_http_proxy_auth_user == NULL) { - default_http_proxy_auth_user = ""; - } - - if (default_http_proxy_auth_pass == NULL) { - default_http_proxy_auth_pass = ""; - } - - SET_COMBO(comboProxyType, proxytype); - SET_ENTRY(entryProxyHost, default_http_proxy_host); - SET_SPIN(spinProxyPort, nsoption_int(http_proxy_port)); - SET_ENTRY(entryProxyUser, default_http_proxy_auth_user); - SET_ENTRY(entryProxyPassword, default_http_proxy_auth_pass); - - set_proxy_widgets_sensitivity(proxytype); - - - /* accept language selection */ - if (nsoption_charp(accept_language) != NULL) { - default_accept_language = nsoption_charp(accept_language); - } - - /* Fill content language list store */ - liststore = GTK_LIST_STORE(gtk_builder_get_object(gladeFile, "liststore_content_language")); - if ((liststore != NULL) && - (languages_file_location != NULL) && - ((fp = fopen(languages_file_location, "r")) != NULL)) { - int combo_row_count = 0; - - gtk_list_store_clear(liststore); - active_language = -1; - - LOG(("Used %s for languages", languages_file_location)); - while (fgets(buf, sizeof(buf), fp)) { - /* Ignore blank lines */ - if (buf[0] == '\0') - continue; - - /* Remove trailing \n */ - buf[strlen(buf) - 1] = '\0'; - - gtk_list_store_append(liststore, &iter); - gtk_list_store_set(liststore, &iter, 0, buf, -1 ); - - if (strcmp(buf, default_accept_language) == 0) { - active_language = combo_row_count; - } - - combo_row_count++; - } - - if (active_language == -1) { - /* configured language was not in list, add it */ - gtk_list_store_append(liststore, &iter); - gtk_list_store_set(liststore, &iter, 0, default_accept_language, -1 ); - active_language = combo_row_count; - - } - - fclose(fp); - } else { - LOG(("Failed opening languages file")); - } - - SET_COMBO(comboboxLanguage, active_language); - - - /* Startup */ - if (nsoption_charp(homepage_url) != NULL) { - default_homepage_url = nsoption_charp(homepage_url); - } - - SET_ENTRY(entryHomePageURL, default_homepage_url); - SET_BUTTON(setCurrentPage); - SET_BUTTON(setDefaultPage); - - /* Theme */ - nsgtk_options_theme_combo(); - - SET_CHECK(checkHideAdverts, nsoption_bool(block_ads)); - - SET_CHECK(checkDisablePopups, nsoption_bool(disable_popups)); - SET_CHECK(checkDisablePlugins, nsoption_bool(disable_plugins)); - SET_SPIN(spinHistoryAge, nsoption_int(history_age)); - SET_CHECK(checkHoverURLs, nsoption_bool(hover_urls)); - - SET_CHECK(checkDisplayRecentURLs, nsoption_bool(url_suggestion)); - SET_CHECK(checkSendReferer, nsoption_bool(send_referer)); - SET_CHECK(checkSendDNT, nsoption_bool(do_not_track)); - SET_CHECK(checkShowSingleTab, nsoption_bool(show_single_tab)); - - SET_SPIN(spinMaxFetchers, nsoption_int(max_fetchers)); - SET_SPIN(spinFetchesPerHost, nsoption_int(max_fetchers_per_host)); - SET_SPIN(spinCachedConnections, nsoption_int(max_cached_fetch_handles)); - - SET_CHECK(checkEnableJavascript, nsoption_bool(enable_javascript)); - - SET_CHECK(checkResampleImages, nsoption_bool(render_resample)); - SET_SPIN(spinAnimationSpeed, nsoption_int(minimum_gif_delay) / 100.0); - SET_CHECK(checkEnableAnimations, nsoption_bool(animate_images)); - -/* SET_FONT(fontSansSerif, nsoption_charp(font_sans)); - SET_FONT(fontSerif, nsoption_charp(font_serif)); - SET_FONT(fontMonospace, nsoption_charp(font_mono)); - SET_FONT(fontCursive, nsoption_charp(font_cursive)); - SET_FONT(fontFantasy, nsoption_charp(font_fantasy)); - SET_SPIN(spinMinimumSize, nsoption_bool(font_min_size) / 10); -*/ - SET_COMBO(comboDefault, nsoption_int(font_default)); - SET_SPIN(spinDefaultSize, nsoption_int(font_size) / 10); - SET_BUTTON(fontPreview); - - SET_COMBO(comboButtonType, nsoption_int(button_type) -1); - - SET_COMBO(comboTabPosition, nsoption_int(position_tab)); - - SET_SPIN(spinMemoryCacheSize, nsoption_int(memory_cache_size) >> 20); - SET_SPIN(spinDiscCacheAge, nsoption_int(disc_cache_age)); - - SET_CHECK(checkClearDownloads, nsoption_bool(downloads_clear)); - SET_CHECK(checkRequestOverwrite, nsoption_bool(request_overwrite)); - SET_FILE_CHOOSER(fileChooserDownloads, nsoption_charp(downloads_directory)); - - SET_CHECK(checkFocusNew, nsoption_bool(focus_new)); - SET_CHECK(checkNewBlank, nsoption_bool(new_blank)); - SET_CHECK(checkUrlSearch, nsoption_bool(search_url_bar)); - SET_COMBO(comboSearch, nsoption_int(search_provider)); - - SET_BUTTON(buttonaddtheme); - SET_CHECK(sourceButtonTab, nsoption_bool(source_tab)); - - SET_SPIN(spinMarginTop, nsoption_int(margin_top)); - SET_SPIN(spinMarginBottom, nsoption_int(margin_bottom)); - SET_SPIN(spinMarginLeft, nsoption_int(margin_left)); - SET_SPIN(spinMarginRight, nsoption_int(margin_right)); - SET_SPIN(spinExportScale, nsoption_int(export_scale)); - SET_CHECK(checkSuppressImages, nsoption_bool(suppress_images)); - SET_CHECK(checkRemoveBackgrounds, nsoption_bool(remove_backgrounds)); - SET_CHECK(checkFitPage, nsoption_bool(enable_loosening)); - SET_CHECK(checkCompressPDF, nsoption_bool(enable_PDF_compression)); - SET_CHECK(checkPasswordPDF, nsoption_bool(enable_PDF_password)); -// SET_BUTTON(setDefaultExportOptions); -} - -static void dialog_response_handler(GtkDialog *dlg, gint res_id) -{ - switch (res_id) { - case GTK_RESPONSE_CLOSE: - on_dialog_close(dlg, TRUE); - } -} - -static gboolean on_dialog_close (GtkDialog *dlg, gboolean stay_alive) -{ - LOG(("Writing options to file")); - nsoption_write(options_file_location); - if ((stay_alive) && GTK_IS_WIDGET(dlg)) - gtk_widget_hide(GTK_WIDGET(dlg)); - else { - stay_alive = FALSE; - } - return stay_alive; -} - -static void nsgtk_options_theme_combo(void) { -/* populate theme combo from themelist file */ - GtkBox *box = GTK_BOX(gtk_builder_get_object(gladeFile, "themehbox")); - char buf[50]; - size_t len = SLEN("themelist") + strlen(res_dir_location) + 1; - char themefile[len]; - - combotheme = nsgtk_combo_box_text_new(); - - if ((combotheme == NULL) || (box == NULL)) { - warn_user(messages_get("NoMemory"), 0); - return; - } - snprintf(themefile, len, "%sthemelist", res_dir_location); - FILE *fp = fopen((const char *)themefile, "r"); - if (fp == NULL) { - LOG(("Failed opening themes file")); - warn_user("FileError", (const char *) themefile); - return; - } - while (fgets(buf, sizeof(buf), fp) != NULL) { - /* Ignore blank lines */ - if (buf[0] == '\0') - continue; - - /* Remove trailing \n */ - buf[strlen(buf) - 1] = '\0'; - - nsgtk_combo_box_text_append_text(combotheme, buf); - } - fclose(fp); - gtk_combo_box_set_active(GTK_COMBO_BOX(combotheme), - nsoption_int(current_theme)); - gtk_box_pack_start(box, combotheme, FALSE, TRUE, 0); - gtk_widget_show(combotheme); -} - -bool nsgtk_options_combo_theme_add(const char *themename) -{ - if (wndPreferences == NULL) - return false; - nsgtk_combo_box_text_append_text(combotheme, themename); - return true; -} - - -/* Defines the callback functions for all widgets and specifies - * nsgtk_reflow_all_windows only where necessary */ - -#define ENTRY_CHANGED(widget, option) \ -static gboolean on_##widget##_changed(GtkWidget *widget, gpointer data) \ -{ \ - if (!g_str_equal(gtk_entry_get_text(GTK_ENTRY((widget))), \ - nsoption_charp(option) ? nsoption_charp(option) : "")) { \ - LOG(("Signal emitted on '%s'", #widget)); \ - nsoption_set_charp(option, strdup(gtk_entry_get_text(GTK_ENTRY((widget))))); \ - } \ - return FALSE; \ -} - -#define CHECK_CHANGED(widget, option) \ - static gboolean on_##widget##_changed(GtkWidget *widget, gpointer data) { \ - LOG(("Signal emitted on '%s'", #widget)); \ - nsoption_set_bool(option, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON((widget)))); \ - do - -#define SPIN_CHANGED(widget, option) \ - static gboolean on_##widget##_changed(GtkWidget *widget, gpointer data) { \ - LOG(("Signal emitted on '%s'", #widget)); \ - nsoption_set_int(option, gtk_spin_button_get_value(GTK_SPIN_BUTTON((widget)))); \ - do - -#define COMBO_CHANGED(widget, option) \ - static gboolean on_##widget##_changed(GtkWidget *widget, gpointer data) { \ - LOG(("Signal emitted on '%s'", #widget)); \ - nsoption_set_int(option, gtk_combo_box_get_active(GTK_COMBO_BOX((widget)))); \ - do - -#define FONT_CHANGED(widget, option) \ - static gboolean on_##widget##_changed(GtkWidget *widget, gpointer data) { \ - LOG(("Signal emitted on '%s'", #widget)); \ - nsoption_set_charp(option, strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON((widget))))); \ - do - -#define BUTTON_CLICKED(widget) \ - static gboolean on_##widget##_changed(GtkWidget *widget, gpointer data) { \ - LOG(("Signal emitted on '%s'", #widget)); \ - do - -#define END_HANDLER \ - while (0); \ - return FALSE; \ - } - -static gboolean on_comboboxLanguage_changed(GtkComboBox *combo, gpointer data) -{ - gchar *lang = NULL; - GtkTreeIter iter; - GtkTreeModel *model; - - /* Obtain currently selected item from combo box. - * If nothing is selected, do nothing. - */ - if (gtk_combo_box_get_active_iter(combo, &iter)) { - /* Obtain data model from combo box. */ - model = gtk_combo_box_get_model(combo); - - /* Obtain string from model. */ - gtk_tree_model_get(model, &iter, 0, &lang, -1); - } - - if (lang != NULL) { - nsoption_set_charp(accept_language, strdup(lang)); - g_free(lang); - } - - return FALSE; -} - -ENTRY_CHANGED(entryHomePageURL, homepage_url) - -BUTTON_CLICKED(setCurrentPage) -{ - const gchar *url; - url = nsurl_access(hlcache_handle_get_url(current_browser->current_content)); - gtk_entry_set_text(GTK_ENTRY(entryHomePageURL), url); - nsoption_set_charp(homepage_url, - strdup(gtk_entry_get_text(GTK_ENTRY(entryHomePageURL)))); -} -END_HANDLER - -BUTTON_CLICKED(setDefaultPage) -{ - gtk_entry_set_text(GTK_ENTRY(entryHomePageURL), NETSURF_HOMEPAGE); - nsoption_set_charp(homepage_url, - strdup(gtk_entry_get_text(GTK_ENTRY(entryHomePageURL)))); -} -END_HANDLER - -CHECK_CHANGED(checkHideAdverts, block_ads) -{ -} -END_HANDLER - -CHECK_CHANGED(checkDisplayRecentURLs, url_suggestion) -{ -} -END_HANDLER - -CHECK_CHANGED(checkSendReferer, send_referer) -{ -} -END_HANDLER - -CHECK_CHANGED(checkSendDNT, do_not_track) -{ -} -END_HANDLER - -CHECK_CHANGED(checkShowSingleTab, show_single_tab) -{ - nsgtk_reflow_all_windows(); -} -END_HANDLER - - -COMBO_CHANGED(comboProxyType, http_proxy_auth) -{ - LOG(("proxy auth: %d", nsoption_int(http_proxy_auth))); - - set_proxy_widgets_sensitivity(nsoption_int(http_proxy_auth)); - switch (nsoption_int(http_proxy_auth)) { - case 0: /* no proxy */ - nsoption_set_bool(http_proxy, false); - nsoption_set_int(http_proxy_auth, OPTION_HTTP_PROXY_AUTH_NONE); - break; - - case 1: /* proxy with no auth */ - nsoption_set_bool(http_proxy, true); - nsoption_set_int(http_proxy_auth, OPTION_HTTP_PROXY_AUTH_NONE); - break; - - case 2: /* proxy with basic auth */ - nsoption_set_bool(http_proxy, true); - nsoption_set_int(http_proxy_auth, OPTION_HTTP_PROXY_AUTH_BASIC); - break; - - case 3: /* proxy with ntlm auth */ - nsoption_set_bool(http_proxy, true); - nsoption_set_int(http_proxy_auth, OPTION_HTTP_PROXY_AUTH_NTLM); - break; - - case 4: /* system proxy */ - nsoption_set_bool(http_proxy, true); - nsoption_set_int(http_proxy_auth, OPTION_HTTP_PROXY_AUTH_NONE); - break; - - } -} -END_HANDLER - -ENTRY_CHANGED(entryProxyHost, http_proxy_host) - -SPIN_CHANGED(spinProxyPort, http_proxy_port) -{ -} -END_HANDLER - -ENTRY_CHANGED(entryProxyUser, http_proxy_auth_user) - -ENTRY_CHANGED(entryProxyPassword, http_proxy_auth_pass) - -SPIN_CHANGED(spinMaxFetchers, max_fetchers) -{ -} -END_HANDLER - -SPIN_CHANGED(spinFetchesPerHost, max_fetchers_per_host) -{ -} -END_HANDLER - -SPIN_CHANGED(spinCachedConnections, max_cached_fetch_handles) -{ -} -END_HANDLER - -CHECK_CHANGED(checkResampleImages, render_resample) -{ -} -END_HANDLER - -static gboolean on_spinAnimationSpeed_changed(GtkWidget *widget, gpointer data) -{ - LOG(("Signal emitted on '%s'", "spinAnimationSpeed")); - nsoption_set_int(minimum_gif_delay, - round(gtk_spin_button_get_value(GTK_SPIN_BUTTON(widget)) * 100.0)); - return FALSE; -} - -CHECK_CHANGED(checkEnableAnimations, animate_images) -{ -} -END_HANDLER - -CHECK_CHANGED(checkEnableJavascript, enable_javascript) -{ -} -END_HANDLER - -CHECK_CHANGED(checkDisablePopups, disable_popups) -{ -} -END_HANDLER - -CHECK_CHANGED(checkDisablePlugins, disable_plugins) -{ -} -END_HANDLER - -SPIN_CHANGED(spinHistoryAge, history_age) -{ -} -END_HANDLER - -CHECK_CHANGED(checkHoverURLs, hover_urls) -{ -} -END_HANDLER -/* -FONT_CHANGED(fontSansSerif, font_sans) -{ -} -END_HANDLER - -FONT_CHANGED(fontSerif, font_serif) -{ -} -END_HANDLER - -FONT_CHANGED(fontMonospace, font_mono) -{ -} -END_HANDLER - -FONT_CHANGED(fontCursive, font_cursive) -{ -} -END_HANDLER - -FONT_CHANGED(fontFantasy, font_fantasy) -{ -} -END_HANDLER -*/ -COMBO_CHANGED(comboDefault, font_default) -{ -} -END_HANDLER - -SPIN_CHANGED(spinDefaultSize, font_size) -{ - nsoption_set_int(font_size, nsoption_int(font_size) * 10); -} -END_HANDLER - -/*SPIN_CHANGED(spinMinimumSize, font_min_size) -{ - nsoption_set_int(font_min_size, nsoption_int(font_min_size) * 10); -} -END_HANDLER -*/ -BUTTON_CLICKED(fontPreview) -{ - nsgtk_reflow_all_windows(); -} -END_HANDLER - -COMBO_CHANGED(comboButtonType, button_type) -{ - nsgtk_scaffolding *current = scaf_list; - nsoption_set_int(button_type, nsoption_int(button_type) + 1); - - /* value of 0 is reserved for 'unset' */ - while (current) { - nsgtk_scaffolding_reset_offset(current); - switch(nsoption_int(button_type)) { - case 1: - gtk_toolbar_set_style( - GTK_TOOLBAR(nsgtk_scaffolding_toolbar(current)), - GTK_TOOLBAR_ICONS); - gtk_toolbar_set_icon_size( - GTK_TOOLBAR(nsgtk_scaffolding_toolbar(current)), - GTK_ICON_SIZE_SMALL_TOOLBAR); - break; - case 2: - gtk_toolbar_set_style( - GTK_TOOLBAR(nsgtk_scaffolding_toolbar(current)), - GTK_TOOLBAR_ICONS); - gtk_toolbar_set_icon_size( - GTK_TOOLBAR(nsgtk_scaffolding_toolbar(current)), - GTK_ICON_SIZE_LARGE_TOOLBAR); - break; - case 3: - gtk_toolbar_set_style( - GTK_TOOLBAR(nsgtk_scaffolding_toolbar(current)), - GTK_TOOLBAR_BOTH); - gtk_toolbar_set_icon_size( - GTK_TOOLBAR(nsgtk_scaffolding_toolbar(current)), - GTK_ICON_SIZE_LARGE_TOOLBAR); - break; - case 4: - gtk_toolbar_set_style( - GTK_TOOLBAR(nsgtk_scaffolding_toolbar(current)), - GTK_TOOLBAR_TEXT); - default: - break; - } - current = nsgtk_scaffolding_iterate(current); - } -} -END_HANDLER - -COMBO_CHANGED(comboTabPosition, position_tab) -{ - nsgtk_scaffolding *current = scaf_list; - nsoption_set_int(button_type, nsoption_int(button_type) + 1); - - /* value of 0 is reserved for 'unset' */ - while (current) { - nsgtk_scaffolding_reset_offset(current); - - nsgtk_reflow_all_windows(); - - current = nsgtk_scaffolding_iterate(current); - } -} -END_HANDLER - -SPIN_CHANGED(spinMemoryCacheSize, memory_cache_size) -{ - nsoption_set_int(memory_cache_size, nsoption_int(memory_cache_size) << 20); -} -END_HANDLER - -SPIN_CHANGED(spinDiscCacheAge, disc_cache_age) -{ -} -END_HANDLER - -CHECK_CHANGED(checkClearDownloads, downloads_clear) -{ -} -END_HANDLER - -CHECK_CHANGED(checkRequestOverwrite, request_overwrite) -{ -} -END_HANDLER - -static gboolean on_fileChooserDownloads_changed(GtkWidget *widget, gpointer data) -{ - gchar *dir; - LOG(("Signal emitted on '%s'", "fileChooserDownloads")); - - dir = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER((widget))); - nsoption_set_charp(downloads_directory, strdup(dir)); - g_free(dir); - return FALSE; -} - -CHECK_CHANGED(checkFocusNew, focus_new) -{ -} -END_HANDLER - -CHECK_CHANGED(checkNewBlank, new_blank) -{ -} -END_HANDLER - -CHECK_CHANGED(checkUrlSearch, search_url_bar) -{ -} -END_HANDLER - -COMBO_CHANGED(comboSearch, search_provider) -{ - nsgtk_scaffolding *current = scaf_list; - char *name; - - /* refresh web search prefs from file */ - search_web_provider_details(nsoption_charp(search_provider)); - - /* retrieve ico */ - search_web_retrieve_ico(false); - - /* callback may handle changing gui */ - if (search_web_ico() != NULL) - gui_window_set_search_ico(search_web_ico()); - - /* set entry */ - name = search_web_provider_name(); - if (name == NULL) { - warn_user(messages_get("NoMemory"), 0); - continue; - } - char content[strlen(name) + SLEN("Search ") + 1]; - sprintf(content, "Search %s", name); - free(name); - while (current) { - nsgtk_scaffolding_set_websearch(current, content); - current = nsgtk_scaffolding_iterate(current); - } -} -END_HANDLER - -COMBO_CHANGED(combotheme, current_theme) -{ - nsgtk_scaffolding *current = scaf_list; - char *name; - if (nsoption_int(current_theme) != 0) { - if (nsgtk_theme_name() != NULL) - free(nsgtk_theme_name()); - name = nsgtk_combo_box_text_get_active_text(combotheme); - if (name != NULL) { - nsgtk_theme_set_name(name); - nsgtk_theme_prepare(); - /* possible name leak */ - } - } else if (nsgtk_theme_name() != NULL) { - free(nsgtk_theme_name()); - nsgtk_theme_set_name(NULL); - } - - while (current) { - nsgtk_theme_implement(current); - current = nsgtk_scaffolding_iterate(current); - } -} -END_HANDLER - -BUTTON_CLICKED(buttonaddtheme) -{ - char *filename, *directory; - size_t len; - GtkWidget *fc = gtk_file_chooser_dialog_new( - messages_get("gtkAddThemeTitle"), - GTK_WINDOW(wndPreferences), - GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, - GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL); - len = SLEN("themes") + strlen(res_dir_location) + 1; - char themesfolder[len]; - snprintf(themesfolder, len, "%sthemes", res_dir_location); - gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(fc), - themesfolder); - gint res = gtk_dialog_run(GTK_DIALOG(fc)); - if (res == GTK_RESPONSE_ACCEPT) { - filename = gtk_file_chooser_get_current_folder( - GTK_FILE_CHOOSER(fc)); - if (strcmp(filename, themesfolder) != 0) { - directory = strrchr(filename, '/'); - *directory = '\0'; - if (strcmp(filename, themesfolder) != 0) { - warn_user(messages_get( - "gtkThemeFolderInstructions"), - 0); - gtk_widget_destroy(GTK_WIDGET(fc)); - if (filename != NULL) - g_free(filename); - return FALSE; - } else { - directory++; - } - } else { - if (filename != NULL) - g_free(filename); - filename = gtk_file_chooser_get_filename( - GTK_FILE_CHOOSER(fc)); - if (strcmp(filename, themesfolder) == 0) { - warn_user(messages_get("gtkThemeFolderSub"), - 0); - gtk_widget_destroy(GTK_WIDGET(fc)); - g_free(filename); - return FALSE; - } - directory = strrchr(filename, '/') + 1; - } - gtk_widget_destroy(GTK_WIDGET(fc)); - nsgtk_theme_add(directory); - if (filename != NULL) - g_free(filename); - } -} -END_HANDLER - -CHECK_CHANGED(sourceButtonTab, source_tab) -{ -} -END_HANDLER - -SPIN_CHANGED(spinMarginTop, margin_top) -{ -} -END_HANDLER - -SPIN_CHANGED(spinMarginBottom, margin_bottom) -{ -} -END_HANDLER - -SPIN_CHANGED(spinMarginLeft, margin_left) -{ -} -END_HANDLER - -SPIN_CHANGED(spinMarginRight, margin_right) -{ -} -END_HANDLER - -SPIN_CHANGED(spinExportScale, export_scale) -{ -} -END_HANDLER - -CHECK_CHANGED(checkSuppressImages, suppress_images) -{ -} -END_HANDLER - -CHECK_CHANGED(checkRemoveBackgrounds, remove_backgrounds) -{ -} -END_HANDLER - -CHECK_CHANGED(checkFitPage, enable_loosening) -{ -} -END_HANDLER - -CHECK_CHANGED(checkCompressPDF, enable_PDF_compression) -{ -} -END_HANDLER - -CHECK_CHANGED(checkPasswordPDF, enable_PDF_password) -{ -} -END_HANDLER - -/* -BUTTON_CLICKED(setDefaultExportOptions) -{ - nsoption_set_int(margin_top, DEFAULT_MARGIN_TOP_MM); - nsoption_set_int(margin_bottom, DEFAULT_MARGIN_BOTTOM_MM); - nsoption_set_int(margin_left, DEFAULT_MARGIN_LEFT_MM); - nsoption_set_int(margin_right, DEFAULT_MARGIN_RIGHT_MM); - nsoption_set_int(export_scale, DEFAULT_EXPORT_SCALE * 100); - nsoption_set_bool(suppress_images, false); - nsoption_set_bool(remove_backgrounds, false); - nsoption_set_bool(enable_loosening, true); - nsoption_set_bool(enable_PDF_compression, true); - nsoption_set_bool(enable_PDF_password, false); - - SET_SPIN(spinMarginTop, nsoption_int(margin_top)); - SET_SPIN(spinMarginBottom, nsoption_int(margin_bottom)); - SET_SPIN(spinMarginLeft, nsoption_int(margin_left)); - SET_SPIN(spinMarginRight, nsoption_int(margin_right)); - SET_SPIN(spinExportScale, nsoption_int(export_scale)); - SET_CHECK(checkSuppressImages, nsoption_bool(suppress_images)); - SET_CHECK(checkRemoveBackgrounds, nsoption_bool(remove_backgrounds)); - SET_CHECK(checkCompressPDF, nsoption_bool(enable_PDF_compression)); - SET_CHECK(checkPasswordPDF, nsoption_bool(enable_PDF_password)); - SET_CHECK(checkFitPage, nsoption_bool(enable_loosening)); -} -END_HANDLER -*/ diff --git a/gtk/dialogs/options.h b/gtk/dialogs/options.h deleted file mode 100644 index 9f6602593..000000000 --- a/gtk/dialogs/options.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2006 Rob Kendrick - * Copyright 2009 Mark Benjamin - * - * 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 . - */ - -#ifndef NETSURF_GTK_OPTIONS_H -#define NETSURF_GTK_OPTIONS_H - -#include - -extern GtkDialog *wndPreferences; - -GtkDialog* nsgtk_options_init(struct browser_window *bw, GtkWindow *parent); - /** Init options and load window */ -void nsgtk_options_load(void); /** Load current options into window */ -void nsgtk_options_save(void); /** Save options from window */ -bool nsgtk_options_combo_theme_add(const char *themename); - /** add new theme name to combo */ - -#endif diff --git a/gtk/dialogs/preferences.c b/gtk/dialogs/preferences.c new file mode 100644 index 000000000..2d787e6dc --- /dev/null +++ b/gtk/dialogs/preferences.c @@ -0,0 +1,1082 @@ +/* + * Copyright 2012 Vincent Sanders + * + * 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 . + */ + +#include +#include + +#include "desktop/browser_private.h" +#include "desktop/options.h" +#include "desktop/searchweb.h" +#include "utils/log.h" +#include "utils/utils.h" +#include "utils/messages.h" + +#include "gtk/compat.h" +#include "gtk/window.h" +#include "gtk/gui.h" +#include "gtk/scaffolding.h" +#include "gtk/theme.h" +#include "gtk/dialogs/preferences.h" + +/* private prefs */ +struct ppref { + /** dialog handle created when window first accessed */ + GObject *dialog; + + struct browser_window *bw; + + /* widgets which are accessed from outside their own signal handlers */ + GtkEntry *entryHomePageURL; + GtkEntry *entryProxyHost; + GtkEntry *entryProxyUser; + GtkEntry *entryProxyPassword; + GtkSpinButton *spinProxyPort; + + /* dynamic list stores */ + GtkListStore *themes; + GtkListStore *content_language; +}; + +static struct ppref ppref; + + +/* Set netsurf option based on toggle button state + * + * This works for any widget which subclasses togglebutton (checkbox, + * radiobutton etc.) + */ +#define TOGGLEBUTTON_SIGNALS(WIDGET, OPTION) \ +G_MODULE_EXPORT void \ +nsgtk_preferences_##WIDGET##_toggled(GtkToggleButton *togglebutton, \ + struct ppref *priv); \ +G_MODULE_EXPORT void \ +nsgtk_preferences_##WIDGET##_toggled(GtkToggleButton *togglebutton, \ + struct ppref *priv) \ +{ \ + nsoption_set_bool(OPTION, \ + gtk_toggle_button_get_active(togglebutton)); \ +} \ + \ +G_MODULE_EXPORT void \ +nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, \ + struct ppref *priv); \ +G_MODULE_EXPORT void \ +nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, \ + struct ppref *priv) \ +{ \ + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), \ + nsoption_bool(OPTION)); \ +} + +#define SPINBUTTON_SIGNALS(WIDGET, OPTION, MULTIPLIER) \ +G_MODULE_EXPORT void \ +nsgtk_preferences_##WIDGET##_valuechanged(GtkSpinButton *spinbutton, \ + struct ppref *priv); \ +G_MODULE_EXPORT void \ +nsgtk_preferences_##WIDGET##_valuechanged(GtkSpinButton *spinbutton, \ + struct ppref *priv) \ +{ \ + nsoption_set_int(OPTION, \ + round(gtk_spin_button_get_value(spinbutton) * MULTIPLIER)); \ +} \ + \ +G_MODULE_EXPORT void \ +nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, struct ppref *priv); \ +G_MODULE_EXPORT void \ +nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, struct ppref *priv) \ +{ \ + gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget), \ + ((gdouble)nsoption_int(OPTION)) / MULTIPLIER); \ +} + +#define ENTRY_SIGNALS(WIDGET, OPTION) \ +G_MODULE_EXPORT void \ +nsgtk_preferences_##WIDGET##_changed(GtkEditable *editable, struct ppref *priv); \ +G_MODULE_EXPORT void \ +nsgtk_preferences_##WIDGET##_changed(GtkEditable *editable, struct ppref *priv)\ +{ \ + nsoption_set_charp(OPTION, \ + strdup(gtk_entry_get_text(GTK_ENTRY(editable)))); \ +} \ + \ +G_MODULE_EXPORT void \ +nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, struct ppref *priv); \ +G_MODULE_EXPORT void \ +nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, struct ppref *priv) \ +{ \ + const char *OPTION; \ + OPTION = nsoption_charp(OPTION); \ + if (OPTION != NULL) { \ + gtk_entry_set_text(GTK_ENTRY(widget), OPTION); \ + } \ +} + +/* GTK module requires these to be exported symbols so these all need + * forward declaring to avoid warnings + */ +G_MODULE_EXPORT void nsgtk_preferences_comboProxyType_changed(GtkComboBox *combo, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboProxyType_realize(GtkWidget *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboboxLoadImages_changed(GtkComboBox *combo, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboboxLoadImages_realize(GtkWidget *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboDefault_changed(GtkComboBox *combo, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboDefault_realize(GtkWidget *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_fontPreview_clicked(GtkButton *button, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboboxLanguage_changed(GtkComboBox *combo, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboboxLanguage_realize(GtkWidget *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboTheme_changed(GtkComboBox *combo, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboTheme_realize(GtkWidget *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_buttonAddTheme_clicked(GtkButton *button, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_checkShowSingleTab_toggled(GtkToggleButton *togglebutton, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_checkShowSingleTab_realize(GtkWidget *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboTabPosition_changed(GtkComboBox *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboTabPosition_realize(GtkWidget *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_sourceButtonWindow_toggled(GtkToggleButton *togglebutton, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_sourceButtonWindow_realize(GtkWidget *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboButtonType_changed(GtkComboBox *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboButtonType_realize(GtkWidget *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_setCurrentPage_clicked(GtkButton *button, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_setDefaultPage_clicked(GtkButton *button, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboSearch_changed(GtkComboBox *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboSearch_realize(GtkWidget *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_fileChooserDownloads_selectionchanged(GtkFileChooser *chooser, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_fileChooserDownloads_realize(GtkWidget *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_dialogPreferences_response(GtkDialog *dlg, gint resid); +G_MODULE_EXPORT gboolean nsgtk_preferences_dialogPreferences_deleteevent(GtkDialog *dlg, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_dialogPreferences_destroy(GtkDialog *dlg, struct ppref *priv); + + +/********* PDF **********/ + +/* Appearance */ + +/* no images in output */ +TOGGLEBUTTON_SIGNALS(checkSuppressImages, suppress_images) + +/* no background images */ +TOGGLEBUTTON_SIGNALS(checkRemoveBackgrounds, remove_backgrounds) + +/* scale to fit page */ +TOGGLEBUTTON_SIGNALS(checkFitPage, enable_loosening) + +/* port */ +SPINBUTTON_SIGNALS(spinExportScale, export_scale, 1.0) + +/* Margins */ +SPINBUTTON_SIGNALS(spinMarginTop, margin_top, 1.0) +SPINBUTTON_SIGNALS(spinMarginBottom, margin_bottom, 1.0) +SPINBUTTON_SIGNALS(spinMarginLeft, margin_left, 1.0) +SPINBUTTON_SIGNALS(spinMarginRight, margin_right, 1.0) + + +/* Generation */ + +/* output is compressed */ +TOGGLEBUTTON_SIGNALS(checkCompressPDF, enable_PDF_compression) + +/* output has a password */ +TOGGLEBUTTON_SIGNALS(checkPasswordPDF, enable_PDF_password) + +/********* Network **********/ + +/* HTTP proxy */ +static void set_proxy_widgets_sensitivity(int proxyval, struct ppref *priv) +{ + gboolean host; + gboolean port; + gboolean user; + gboolean pass; + + switch (proxyval) { + case 0: /* no proxy */ + host = FALSE; + port = FALSE; + user = FALSE; + pass = FALSE; + break; + + case 1: /* proxy with no auth */ + host = TRUE; + port = TRUE; + user = FALSE; + pass = FALSE; + break; + + case 2: /* proxy with basic auth */ + host = TRUE; + port = TRUE; + user = TRUE; + pass = TRUE; + break; + + case 3: /* proxy with ntlm auth */ + host = TRUE; + port = TRUE; + user = TRUE; + pass = TRUE; + break; + + case 4: /* system proxy */ + host = FALSE; + port = FALSE; + user = FALSE; + pass = FALSE; + break; + + default: + return; + } + + gtk_widget_set_sensitive(GTK_WIDGET(priv->entryProxyHost), host); + gtk_widget_set_sensitive(GTK_WIDGET(priv->spinProxyPort), port); + gtk_widget_set_sensitive(GTK_WIDGET(priv->entryProxyUser), user); + gtk_widget_set_sensitive(GTK_WIDGET(priv->entryProxyPassword), pass); + +} + +G_MODULE_EXPORT void +nsgtk_preferences_comboProxyType_changed(GtkComboBox *combo, struct ppref *priv) +{ + int proxy_sel; + proxy_sel = gtk_combo_box_get_active(combo); + + switch (proxy_sel) { + case 0: /* no proxy */ + nsoption_set_bool(http_proxy, false); + break; + + case 1: /* proxy with no auth */ + nsoption_set_bool(http_proxy, true); + nsoption_set_int(http_proxy_auth, OPTION_HTTP_PROXY_AUTH_NONE); + break; + + case 2: /* proxy with basic auth */ + nsoption_set_bool(http_proxy, true); + nsoption_set_int(http_proxy_auth, OPTION_HTTP_PROXY_AUTH_BASIC); + break; + + case 3: /* proxy with ntlm auth */ + nsoption_set_bool(http_proxy, true); + nsoption_set_int(http_proxy_auth, OPTION_HTTP_PROXY_AUTH_NTLM); + break; + + case 4: /* system proxy */ + nsoption_set_bool(http_proxy, true); + nsoption_set_int(http_proxy_auth, OPTION_HTTP_PROXY_AUTH_NONE); + break; + } + + set_proxy_widgets_sensitivity(proxy_sel, priv); +} + +G_MODULE_EXPORT void +nsgtk_preferences_comboProxyType_realize(GtkWidget *widget, struct ppref *priv) +{ + int proxytype = 0; /* no proxy by default */ + + if (nsoption_bool(http_proxy) == true) { + /* proxy type combo box starts with disabled, to allow + * for this the http_proxy option needs combining with + * the http_proxy_auth option + */ + proxytype = nsoption_int(http_proxy_auth) + 1; + if (nsoption_charp(http_proxy_host) == NULL) { + /* set to use a proxy without a host, turn proxy off */ + proxytype = 0; + } else if (((proxytype == 2) || + (proxytype == 3)) && + ((nsoption_charp(http_proxy_auth_user) == NULL) || + (nsoption_charp(http_proxy_auth_pass) == NULL))) { + /* authentication selected with empty credentials, turn proxy off */ + proxytype = 0; + } + } + gtk_combo_box_set_active(GTK_COMBO_BOX(widget), proxytype); + + set_proxy_widgets_sensitivity(proxytype, priv); +} + +/* host */ +ENTRY_SIGNALS(entryProxyHost, http_proxy_host) + +/* port */ +SPINBUTTON_SIGNALS(spinProxyPort, http_proxy_port, 1.0) + +/* user */ +ENTRY_SIGNALS(entryProxyUser, http_proxy_auth_user) + +/* password */ +ENTRY_SIGNALS(entryProxyPassword, http_proxy_auth_pass) + +/* Fetching */ + +/* maximum fetchers */ +SPINBUTTON_SIGNALS(spinMaxFetchers, max_fetchers, 1.0) + +/* fetches per host */ +SPINBUTTON_SIGNALS(spinFetchesPerHost, max_fetchers_per_host, 1.0) + +/* cached connections */ +SPINBUTTON_SIGNALS(spinCachedConnections, max_cached_fetch_handles, 1.0) + + +/********* Privacy **********/ + +/* General */ + +/* enable referral submission */ +TOGGLEBUTTON_SIGNALS(checkSendReferer, send_referer) + +/* send do not track */ +TOGGLEBUTTON_SIGNALS(checkSendDNT, do_not_track) + +/* History */ + +/* local history shows url tooltips */ +TOGGLEBUTTON_SIGNALS(checkHoverURLs, hover_urls) + +/* remember browsing history */ +SPINBUTTON_SIGNALS(spinHistoryAge, history_age, 1.0) + +/* Cache */ + +/* memory cache size */ +SPINBUTTON_SIGNALS(spinMemoryCacheSize, memory_cache_size, (1024*1024)) + +/* disc cache size */ +SPINBUTTON_SIGNALS(spinDiscCacheSize, disc_cache_size, (1024*1024)) + + +/* disc cache age */ +SPINBUTTON_SIGNALS(spinDiscCacheAge, disc_cache_age, 1.0) + + +/********* Content **********/ + +/* Control */ + + +/* prevent popups */ +TOGGLEBUTTON_SIGNALS(checkDisablePopups, disable_popups) + +/* hide adverts */ +TOGGLEBUTTON_SIGNALS(checkHideAdverts, block_ads) + +/* enable javascript */ +TOGGLEBUTTON_SIGNALS(checkEnableJavascript, enable_javascript) + +/* disable plugins */ +TOGGLEBUTTON_SIGNALS(checkDisablePlugins, disable_plugins) + +/* high quality image scaling */ +TOGGLEBUTTON_SIGNALS(checkResampleImages, render_resample) + +/* load and display of images */ +G_MODULE_EXPORT void +nsgtk_preferences_comboboxLoadImages_changed(GtkComboBox *combo, + struct ppref *priv) +{ + int img_sel; + /* get the row number for the selection */ + img_sel = gtk_combo_box_get_active(combo); + switch (img_sel) { + case 0: + /* background and foreground */ + nsoption_set_bool(foreground_images, true); + nsoption_set_bool(background_images, true); + break; + + case 1: + /* foreground only */ + nsoption_set_bool(foreground_images, true); + nsoption_set_bool(background_images, false); + break; + + case 2: + /* background only */ + nsoption_set_bool(foreground_images, false); + nsoption_set_bool(background_images, true); + break; + + case 3: + /* no images */ + nsoption_set_bool(foreground_images, false); + nsoption_set_bool(background_images, false); + break; + } +} + +G_MODULE_EXPORT void +nsgtk_preferences_comboboxLoadImages_realize(GtkWidget *widget, + struct ppref *priv) +{ + if (nsoption_bool(foreground_images)) { + if (nsoption_bool(background_images)) { + /* background and foreground */ + gtk_combo_box_set_active(GTK_COMBO_BOX(widget), 0); + } else { + /* foreground only */ + gtk_combo_box_set_active(GTK_COMBO_BOX(widget), 1); + } + } else { + if (nsoption_bool(background_images)) { + /* background only */ + gtk_combo_box_set_active(GTK_COMBO_BOX(widget), 2); + } else { + /* no images */ + gtk_combo_box_set_active(GTK_COMBO_BOX(widget), 3); + } + } +} + +/* Animation */ + +/* enable animation */ +TOGGLEBUTTON_SIGNALS(checkEnableAnimations, animate_images) + +/* frame time */ +SPINBUTTON_SIGNALS(spinAnimationSpeed, minimum_gif_delay, 100.0) + +/* Fonts */ + +/* default font */ +G_MODULE_EXPORT void +nsgtk_preferences_comboDefault_changed(GtkComboBox *combo, struct ppref *priv) +{ + int font_sel; + /* get the row number for the selection */ + font_sel = gtk_combo_box_get_active(combo); + if ((font_sel >= 0) && (font_sel <= 4)) { + nsoption_set_int(font_default, font_sel); + } +} + +G_MODULE_EXPORT void +nsgtk_preferences_comboDefault_realize(GtkWidget *widget, struct ppref *priv) +{ + gtk_combo_box_set_active(GTK_COMBO_BOX(widget), + nsoption_int(font_default)); +} + +/* default font size */ +SPINBUTTON_SIGNALS(spinDefaultSize, font_size, 10.0) + +/* preview - actually reflow all views */ +G_MODULE_EXPORT void +nsgtk_preferences_fontPreview_clicked(GtkButton *button, struct ppref *priv) +{ + nsgtk_reflow_all_windows(); +} + + +/* Language */ + +/* accept language */ +G_MODULE_EXPORT void +nsgtk_preferences_comboboxLanguage_changed(GtkComboBox *combo, + struct ppref *priv) +{ + gchar *lang = NULL; + GtkTreeIter iter; + GtkTreeModel *model; + + /* Obtain currently selected item from combo box. + * If nothing is selected, do nothing. + */ + if (gtk_combo_box_get_active_iter(combo, &iter)) { + /* Obtain data model from combo box. */ + model = gtk_combo_box_get_model(combo); + + /* Obtain string from model. */ + gtk_tree_model_get(model, &iter, 0, &lang, -1); + } + + if (lang != NULL) { + nsoption_set_charp(accept_language, strdup(lang)); + g_free(lang); + } +} + +G_MODULE_EXPORT void +nsgtk_preferences_comboboxLanguage_realize(GtkWidget *widget, + struct ppref *priv) +{ + /* Fill content language list store */ + int active_language = 0; + GtkTreeIter iter; + FILE *fp; + char buf[50]; + const char *default_accept_language = "en"; + + if ((priv->content_language != NULL) && + (languages_file_location != NULL) && + ((fp = fopen(languages_file_location, "r")) != NULL)) { + int combo_row_count = 0; + + gtk_list_store_clear(priv->content_language); + active_language = -1; + + LOG(("Used %s for languages", languages_file_location)); + while (fgets(buf, sizeof(buf), fp)) { + /* Ignore blank lines */ + if (buf[0] == '\0') + continue; + + /* Remove trailing \n */ + buf[strlen(buf) - 1] = '\0'; + + gtk_list_store_append(priv->content_language, &iter); + gtk_list_store_set(priv->content_language, + &iter, 0, buf, -1 ); + + if (strcmp(buf, default_accept_language) == 0) { + active_language = combo_row_count; + } + + combo_row_count++; + } + + if (active_language == -1) { + /* configured language was not in list, add it */ + gtk_list_store_append(priv->content_language, &iter); + gtk_list_store_set(priv->content_language, + &iter, + 0, default_accept_language, -1 ); + active_language = combo_row_count; + } + + fclose(fp); + } else { + LOG(("Failed opening languages file")); + } + + gtk_combo_box_set_active(GTK_COMBO_BOX(widget), active_language); +} + + +/********* Apperance **********/ + +/* Themes */ + +/* select theme */ +G_MODULE_EXPORT void +nsgtk_preferences_comboTheme_changed(GtkComboBox *combo, struct ppref *priv) +{ + nsgtk_scaffolding *current = scaf_list; + int theme = 0; + gchar *name; + GtkTreeIter iter; + GtkTreeModel *model; + + /* Obtain currently selected item from combo box. + * If nothing is selected, do nothing. + */ + if (gtk_combo_box_get_active_iter(combo, &iter)) { + /* get the row number for the config */ + theme = gtk_combo_box_get_active(combo); + + nsoption_set_int(current_theme, theme); + + /* retrive the theme name if it is not the default */ + if (theme != 0) { + /* Obtain data model from combo box. */ + model = gtk_combo_box_get_model(combo); + + /* Obtain string from model. */ + gtk_tree_model_get(model, &iter, 0, &name, -1); + } else { + name = NULL; + } + + nsgtk_theme_set_name(name); + + if (name != NULL) { + g_free(name); + } + + while (current) { + nsgtk_theme_implement(current); + current = nsgtk_scaffolding_iterate(current); + } + } +} + +G_MODULE_EXPORT void +nsgtk_preferences_comboTheme_realize(GtkWidget *widget, struct ppref *priv) +{ + /* Fill theme list store */ + FILE *fp; + GtkTreeIter iter; + char buf[50]; + int combo_row_count = 0; + int selected_theme = 0; + + if ((priv->themes != NULL) && + (themelist_file_location != NULL) && + ((fp = fopen(themelist_file_location, "r")) != NULL)) { + gtk_list_store_clear(priv->themes); + + LOG(("Used %s for themelist", themelist_file_location)); + + while (fgets(buf, sizeof(buf), fp)) { + /* Ignore blank lines */ + if (buf[0] == '\0') + continue; + + /* Remove trailing \n */ + buf[strlen(buf) - 1] = '\0'; + + gtk_list_store_append(priv->themes, &iter); + gtk_list_store_set(priv->themes, &iter, 0, buf, -1); + + combo_row_count++; + } + + fclose(fp); + } else { + LOG(("Failed opening themes file")); + } + + /* get configured theme and sanity check value */ + selected_theme = nsoption_int(current_theme); + if (selected_theme > combo_row_count) { + selected_theme = combo_row_count; + } + if (selected_theme < 0) { + selected_theme = 0; + } + + gtk_combo_box_set_active(GTK_COMBO_BOX(widget), selected_theme); +} + +/* add theme */ +G_MODULE_EXPORT void +nsgtk_preferences_buttonAddTheme_clicked(GtkButton *button, struct ppref *priv) +{ + char *filename, *directory; + size_t len; + GtkWidget *fc = gtk_file_chooser_dialog_new( + messages_get("gtkAddThemeTitle"), + GTK_WINDOW(priv->dialog), + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, + GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL); + len = SLEN("themes") + strlen(res_dir_location) + 1; + char themesfolder[len]; + snprintf(themesfolder, len, "%sthemes", res_dir_location); + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(fc), + themesfolder); + gint res = gtk_dialog_run(GTK_DIALOG(fc)); + if (res == GTK_RESPONSE_ACCEPT) { + filename = gtk_file_chooser_get_current_folder( + GTK_FILE_CHOOSER(fc)); + if (strcmp(filename, themesfolder) != 0) { + directory = strrchr(filename, '/'); + *directory = '\0'; + if (strcmp(filename, themesfolder) != 0) { + warn_user(messages_get( + "gtkThemeFolderInstructions"), + 0); + gtk_widget_destroy(GTK_WIDGET(fc)); + if (filename != NULL) + g_free(filename); + return; + } else { + directory++; + } + } else { + if (filename != NULL) + g_free(filename); + filename = gtk_file_chooser_get_filename( + GTK_FILE_CHOOSER(fc)); + if (strcmp(filename, themesfolder) == 0) { + warn_user(messages_get("gtkThemeFolderSub"), + 0); + gtk_widget_destroy(GTK_WIDGET(fc)); + g_free(filename); + return; + } + directory = strrchr(filename, '/') + 1; + } + gtk_widget_destroy(GTK_WIDGET(fc)); + nsgtk_theme_add(directory); + if (filename != NULL) + g_free(filename); + } +} + +/* Tabs */ + +/* always show tab bar */ +G_MODULE_EXPORT void +nsgtk_preferences_checkShowSingleTab_toggled(GtkToggleButton *togglebutton, + struct ppref *priv) +{ + nsoption_set_bool(show_single_tab, + gtk_toggle_button_get_active(togglebutton)); + nsgtk_reflow_all_windows(); +} + +G_MODULE_EXPORT void +nsgtk_preferences_checkShowSingleTab_realize(GtkWidget *widget, + struct ppref *priv) +{ + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), + nsoption_bool(show_single_tab)); +} + +/* switch to newly opened tabs immediately */ +TOGGLEBUTTON_SIGNALS(checkFocusNew, focus_new) + +/* newly opened tabs are blank */ +TOGGLEBUTTON_SIGNALS(checkNewBlank, new_blank) + +/* tab position */ +G_MODULE_EXPORT void +nsgtk_preferences_comboTabPosition_changed(GtkComboBox *widget, + struct ppref *priv) +{ + nsgtk_scaffolding *current = scaf_list; + + /* set the option */ + nsoption_set_int(position_tab, gtk_combo_box_get_active(widget)); + + /* update all notebooks in all scaffolds */ + while (current) { + nsgtk_scaffolding_reset_offset(current); + + nsgtk_reflow_all_windows(); + + current = nsgtk_scaffolding_iterate(current); + } +} + +G_MODULE_EXPORT void +nsgtk_preferences_comboTabPosition_realize(GtkWidget *widget, + struct ppref *priv) +{ + gtk_combo_box_set_active(GTK_COMBO_BOX(widget), + nsoption_int(position_tab)); +} + +/* Source */ + +/* source view opening */ +TOGGLEBUTTON_SIGNALS(sourceButtonTab, source_tab) + +G_MODULE_EXPORT void +nsgtk_preferences_sourceButtonWindow_toggled(GtkToggleButton *togglebutton, + struct ppref *priv) +{ + nsoption_set_bool(source_tab, + !gtk_toggle_button_get_active(togglebutton)); +} + +G_MODULE_EXPORT void +nsgtk_preferences_sourceButtonWindow_realize(GtkWidget *widget, + struct ppref *priv) +{ + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), + !nsoption_bool(source_tab)); +} + + +/* URLbar */ + +/* show recently visited urls as you type */ +TOGGLEBUTTON_SIGNALS(checkDisplayRecentURLs, url_suggestion) + +/* Toolbar */ + +/* button position */ +G_MODULE_EXPORT void +nsgtk_preferences_comboButtonType_changed(GtkComboBox *widget, + struct ppref *priv) +{ + nsgtk_scaffolding *current = scaf_list; + nsoption_set_int(button_type, gtk_combo_box_get_active(widget) + 1); + + /* value of 0 is reserved for 'unset' */ + while (current) { + nsgtk_scaffolding_reset_offset(current); + switch(nsoption_int(button_type)) { + case 1: + gtk_toolbar_set_style( + GTK_TOOLBAR(nsgtk_scaffolding_toolbar(current)), + GTK_TOOLBAR_ICONS); + gtk_toolbar_set_icon_size( + GTK_TOOLBAR(nsgtk_scaffolding_toolbar(current)), + GTK_ICON_SIZE_SMALL_TOOLBAR); + break; + case 2: + gtk_toolbar_set_style( + GTK_TOOLBAR(nsgtk_scaffolding_toolbar(current)), + GTK_TOOLBAR_ICONS); + gtk_toolbar_set_icon_size( + GTK_TOOLBAR(nsgtk_scaffolding_toolbar(current)), + GTK_ICON_SIZE_LARGE_TOOLBAR); + break; + case 3: + gtk_toolbar_set_style( + GTK_TOOLBAR(nsgtk_scaffolding_toolbar(current)), + GTK_TOOLBAR_BOTH); + gtk_toolbar_set_icon_size( + GTK_TOOLBAR(nsgtk_scaffolding_toolbar(current)), + GTK_ICON_SIZE_LARGE_TOOLBAR); + break; + case 4: + gtk_toolbar_set_style( + GTK_TOOLBAR(nsgtk_scaffolding_toolbar(current)), + GTK_TOOLBAR_TEXT); + default: + break; + } + current = nsgtk_scaffolding_iterate(current); + } +} + +G_MODULE_EXPORT void +nsgtk_preferences_comboButtonType_realize(GtkWidget *widget, + struct ppref *priv) +{ + gtk_combo_box_set_active(GTK_COMBO_BOX(widget), + nsoption_int(button_type) - 1); +} + + + +/************ Main ************/ + +/* Startup */ + +/* entry HomePageURL widget */ +ENTRY_SIGNALS(entryHomePageURL, homepage_url) + +/* put current page into homepage url */ +G_MODULE_EXPORT void +nsgtk_preferences_setCurrentPage_clicked(GtkButton *button, struct ppref *priv) +{ + const gchar *url; + + if (priv->bw != NULL) { + url = nsurl_access(hlcache_handle_get_url(priv->bw->current_content)); + } else { + url = "about:blank"; + } + + if (priv->entryHomePageURL != NULL) { + gtk_entry_set_text(GTK_ENTRY(priv->entryHomePageURL), url); + nsoption_set_charp(homepage_url, strdup(url)); + } +} + +/* put default page into homepage */ +G_MODULE_EXPORT void +nsgtk_preferences_setDefaultPage_clicked(GtkButton *button, struct ppref *priv) +{ + const gchar *url = NETSURF_HOMEPAGE; + + if (priv->entryHomePageURL != NULL) { + gtk_entry_set_text(GTK_ENTRY(priv->entryHomePageURL), url); + nsoption_set_charp(homepage_url, strdup(url)); + } +} + +/* Search */ + +/* Url Search widget */ +TOGGLEBUTTON_SIGNALS(checkUrlSearch, search_url_bar) + +/* provider combo */ +G_MODULE_EXPORT void +nsgtk_preferences_comboSearch_changed(GtkComboBox *widget, struct ppref *priv) +{ + nsgtk_scaffolding *current = scaf_list; + char *name; + int provider; + + provider = gtk_combo_box_get_active(widget); + + /* set the option */ + nsoption_set_int(search_provider, provider); + + /* refresh web search prefs from file */ + search_web_provider_details(provider); + + /* retrieve ico */ + search_web_retrieve_ico(false); + + /* callback may handle changing gui */ + if (search_web_ico() != NULL) { + gui_window_set_search_ico(search_web_ico()); + } + + /* set entry */ + name = search_web_provider_name(); + if (name != NULL) { + char content[strlen(name) + SLEN("Search ") + 1]; + + sprintf(content, "Search %s", name); + free(name); + while (current) { + nsgtk_scaffolding_set_websearch(current, content); + current = nsgtk_scaffolding_iterate(current); + } + } +} + +G_MODULE_EXPORT void +nsgtk_preferences_comboSearch_realize(GtkWidget *widget, struct ppref *priv) +{ + gtk_combo_box_set_active(GTK_COMBO_BOX(widget), + nsoption_int(search_provider)); +} + + +/* Downloads */ + +/* clear downloads */ +TOGGLEBUTTON_SIGNALS(checkClearDownloads, downloads_clear) + +/* request overwite */ +TOGGLEBUTTON_SIGNALS(checkRequestOverwrite, request_overwrite) + +/* download location + * + * note selection-changed is used instead of file-set as the returned + * filename when that signal are used is incorrect. Though this signal + * does update frequently often with the same data. + */ +G_MODULE_EXPORT void +nsgtk_preferences_fileChooserDownloads_selectionchanged(GtkFileChooser *chooser, + struct ppref *priv) +{ + gchar *dir; + dir = gtk_file_chooser_get_filename(chooser); + nsoption_set_charp(downloads_directory, strdup(dir)); + g_free(dir); +} + +G_MODULE_EXPORT void +nsgtk_preferences_fileChooserDownloads_realize(GtkWidget *widget, + struct ppref *priv) +{ + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(widget), + nsoption_charp(downloads_directory)); +} + + +/************* Dialog window ***********/ + +/* dialog close and destroy events */ +G_MODULE_EXPORT void +nsgtk_preferences_dialogPreferences_response(GtkDialog *dlg, gint resid) +{ + if (resid == GTK_RESPONSE_CLOSE) { + nsoption_write(options_file_location); + gtk_widget_hide(GTK_WIDGET(dlg)); + } +} + +G_MODULE_EXPORT gboolean +nsgtk_preferences_dialogPreferences_deleteevent(GtkDialog *dlg, + struct ppref *priv) +{ + nsoption_write(options_file_location); + gtk_widget_hide(GTK_WIDGET(dlg)); + + /* delt with it by hiding window, no need to destory widget by + * default */ + return TRUE; +} + +G_MODULE_EXPORT void +nsgtk_preferences_dialogPreferences_destroy(GtkDialog *dlg, struct ppref *priv) +{ + nsoption_write(options_file_location); +} + + +/* exported interface documented in gtk/dialogs/preferences.h */ +GtkWidget* nsgtk_preferences(struct browser_window *bw, GtkWindow *parent) +{ + GError *error = NULL; + GtkBuilder *preferences_builder; + struct ppref *priv = &ppref; + + priv->bw = bw; /* for setting "current" page */ + + /* memoised dialog creation */ + if (priv->dialog != NULL) { + gtk_window_set_transient_for(GTK_WINDOW(priv->dialog), parent); + return GTK_WIDGET(priv->dialog); + } + + /* populate builder object */ + preferences_builder = gtk_builder_new(); + if (!gtk_builder_add_from_file(preferences_builder, + glade_file_location->options, + &error)) { + g_warning("Couldn't load builder file: %s", error->message); + g_error_free(error); + return NULL; + } + + + priv->dialog = gtk_builder_get_object(preferences_builder, + "dialogPreferences"); + if (priv->dialog == NULL) { + LOG(("Unable to get object for preferences dialog")); + /* release builder as were done with it */ + g_object_unref(G_OBJECT(preferences_builder)); + return NULL; + } + + /* need to explicitly obtain handles for some widgets enabling + * updates by other widget events + */ +#define GB(TYPE, NAME) GTK_##TYPE(gtk_builder_get_object(preferences_builder, #NAME)) + priv->entryHomePageURL = GB(ENTRY, entryHomePageURL); + priv->themes = GB(LIST_STORE, liststore_themes); + priv->content_language = GB(LIST_STORE, liststore_content_language); + priv->entryProxyHost = GB(ENTRY, entryProxyHost); + priv->spinProxyPort = GB(SPIN_BUTTON, spinProxyPort); + priv->entryProxyUser = GB(ENTRY, entryProxyUser); + priv->entryProxyPassword = GB(ENTRY, entryProxyPassword); +#undef GB + + /* connect all signals ready to use */ + gtk_builder_connect_signals(preferences_builder, priv); + + /* release builder as were done with it */ + g_object_unref(G_OBJECT(preferences_builder)); + + /* mark dialog as transient on parent */ + gtk_window_set_transient_for(GTK_WINDOW(priv->dialog), parent); + + return GTK_WIDGET(priv->dialog); +} + +/* exported interface documented in gtk/dialogs/preferences.h */ +void nsgtk_preferences_theme_add(const char *themename) +{ + struct ppref *priv = &ppref; + GtkTreeIter iter; + + gtk_list_store_append(priv->themes, &iter); + gtk_list_store_set(priv->themes, &iter, 0, themename, -1 ); +} diff --git a/gtk/dialogs/preferences.h b/gtk/dialogs/preferences.h new file mode 100644 index 000000000..3ef33ca30 --- /dev/null +++ b/gtk/dialogs/preferences.h @@ -0,0 +1,32 @@ +/* + * Copyright 2012 Vincent Sanders + * + * 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 . + */ + +#ifndef NETSURF_GTK_PREFERENCES_H +#define NETSURF_GTK_PREFERENCES_H + +#include + +/** Initialise prefernces window + */ +GtkWidget* nsgtk_preferences(struct browser_window *bw, GtkWindow *parent); + +/** Theme added + */ +void nsgtk_preferences_theme_add(const char *themename); + +#endif diff --git a/gtk/gui.c b/gtk/gui.c index 1d15ef743..a120fdfb6 100644 --- a/gtk/gui.c +++ b/gtk/gui.c @@ -59,7 +59,6 @@ #include "desktop/tree.h" #include "css/utils.h" #include "gtk/compat.h" -#include "gtk/dialogs/options.h" #include "gtk/completion.h" #include "gtk/cookies.h" #include "gtk/download.h" @@ -85,6 +84,7 @@ char *toolbar_indices_file_location; char *res_dir_location; char *print_options_file_location; char *languages_file_location; +char *themelist_file_location; GdkPixbuf *favicon_pixbuf; /* favicon default pixbuf */ @@ -364,6 +364,17 @@ static void gui_init(int argc, char** argv, char **respath) die("Unable to find resources.\n"); } + /* find the theme list file */ + themelist_file_location = filepath_find(respath, "themelist"); + if ((themelist_file_location != NULL) && + (strlen(themelist_file_location) < 10)) { + free(themelist_file_location); + themelist_file_location = NULL; + } + if (themelist_file_location == NULL) { + LOG(("Unable to find themelist - disabling")); + } + /* Obtain resources path location. * * Uses the directory the languages file was found in, diff --git a/gtk/gui.h b/gtk/gui.h index b53de256a..72794b231 100644 --- a/gtk/gui.h +++ b/gtk/gui.h @@ -54,6 +54,7 @@ extern char *toolbar_indices_file_location; extern char *options_file_location; /**< location where user options are written */ extern char *res_dir_location; extern char *print_options_file_location; +extern char *themelist_file_location; extern GdkPixbuf *favicon_pixbuf; /* favicon default pixbuf */ diff --git a/gtk/res/options.gtk2.ui b/gtk/res/options.gtk2.ui index 5dd793062..85db837b8 100644 --- a/gtk/res/options.gtk2.ui +++ b/gtk/res/options.gtk2.ui @@ -9,6 +9,9 @@ True dialog False + + + True @@ -59,6 +62,8 @@ True True + + end @@ -83,11 +88,13 @@ True True True + False False - 1 + end + 2 @@ -96,17 +103,18 @@ True True True + False False - 2 + end + 1 False - end 1 @@ -151,6 +159,8 @@ True False True + + 0 @@ -175,6 +185,8 @@ True liststore_search_provider + + @@ -232,6 +244,8 @@ True False True + + False @@ -245,6 +259,8 @@ True False True + + False @@ -269,6 +285,8 @@ True select-folder + + 1 @@ -330,14 +348,29 @@ True 12 - + + True + liststore_themes + + + + + + 0 + + + + + 0 + - + Add Theme... True True True + False @@ -386,6 +419,8 @@ True False True + + 0 @@ -398,6 +433,8 @@ True False True + + 1 @@ -410,6 +447,8 @@ True False True + + 2 @@ -434,6 +473,8 @@ True liststore_tab_position + + @@ -503,10 +544,11 @@ True True False - 0.54000002145767212 True True sourceButtonTab + + 0 @@ -520,6 +562,8 @@ False True True + + 1 @@ -571,6 +615,8 @@ True False True + + 0 @@ -628,6 +674,8 @@ True liststore_toolbar_buttontype + + @@ -705,6 +753,8 @@ True False True + + False @@ -719,6 +769,8 @@ True False True + + False @@ -733,6 +785,8 @@ True False True + + False @@ -747,6 +801,8 @@ True False True + + False @@ -761,6 +817,8 @@ True False True + + 4 @@ -784,6 +842,8 @@ True liststore_image_loading + + @@ -841,6 +901,8 @@ True False True + + 0 @@ -872,6 +934,8 @@ 1 True if-valid + + False @@ -934,6 +998,8 @@ True liststore_defaultfont + + @@ -970,12 +1036,15 @@ True True The base-line font size to use. - 3 + 4 - 3 + 4 adjustment_font_default_size 1 + 1 True + + False @@ -996,6 +1065,7 @@ True image1 True + False @@ -1055,6 +1125,8 @@ True set preferred language for web pages liststore_content_language + + 0 @@ -1118,6 +1190,68 @@ True vertical 6 + + + True + 0 + none + + + True + 6 + 12 + 12 + + + True + vertical + 6 + + + Enable referral submission + True + True + False + True + + + + + 0 + + + + + Enable sending "Do Not Track" request + True + True + False + True + + + + + 1 + + + + + + + + + True + <b>General</b> + True + + + + + False + 6 + 0 + + True @@ -1141,6 +1275,8 @@ True False True + + 0 @@ -1170,6 +1306,9 @@ adjustment_history_age 1 True + if-valid + + False @@ -1206,7 +1345,7 @@ False 6 - 0 + 1 @@ -1275,6 +1414,8 @@ adjustment_cache_memory_size 1 True + + 1 @@ -1291,6 +1432,8 @@ adjustment_cache_disc_size 1 True + + 1 @@ -1309,6 +1452,8 @@ adjustment_disc_cache_age 1 True + + 1 @@ -1401,63 +1546,6 @@ False 6 - 1 - - - - - True - 0 - none - - - True - 6 - 12 - 12 - - - True - vertical - 6 - - - Enable referral submission - True - True - False - True - - - 0 - - - - - Enable sending "Do Not Track" request - True - True - False - True - - - 1 - - - - - - - - - True - <b>General</b> - True - - - - - False 2 @@ -1551,6 +1639,8 @@ True The type of HTTP proxy server. liststore_proxy_type + + @@ -1574,6 +1664,8 @@ True Host name of your proxy server. + + 0 @@ -1602,6 +1694,8 @@ 1 True if-valid + + False @@ -1623,6 +1717,8 @@ True Username to access the proxy + + 1 @@ -1637,7 +1733,10 @@ True True Password to access the proxy + False + + 1 @@ -1727,6 +1826,8 @@ adjustment_fetching_max 1 True + + 1 @@ -1745,6 +1846,8 @@ adjustment_fetching_perhost 1 True + + 1 @@ -1765,6 +1868,8 @@ adjustment_fetching_cached 1 True + + 1 @@ -1835,6 +1940,8 @@ True False True + + 0 @@ -1847,6 +1954,8 @@ True False True + + 1 @@ -1859,6 +1968,8 @@ True False True + + 2 @@ -1889,6 +2000,8 @@ adjustment_pdf_scale 1 True + + False @@ -1995,6 +2108,8 @@ 1 1 True + + False @@ -2034,6 +2149,8 @@ 1 1 True + + False @@ -2072,6 +2189,8 @@ 1 1 True + + False @@ -2112,6 +2231,8 @@ 1 1 True + + False @@ -2188,6 +2309,8 @@ True False True + + 0 @@ -2200,6 +2323,8 @@ True False True + + 1 @@ -2481,9 +2606,9 @@ 16 1 - 100 - 1 - 10 + 99.900000000000006 + 0.10000000000000001 + 2 28 @@ -2562,4 +2687,15 @@ + + + + + + + + Default + + + diff --git a/gtk/scaffolding.c b/gtk/scaffolding.c index 91288a22e..8b9b8f3b1 100644 --- a/gtk/scaffolding.c +++ b/gtk/scaffolding.c @@ -53,7 +53,7 @@ #include "desktop/tree.h" #include "gtk/cookies.h" #include "gtk/completion.h" -#include "gtk/dialogs/options.h" +#include "gtk/dialogs/preferences.h" #include "gtk/dialogs/about.h" #include "gtk/dialogs/source.h" #include "gtk/bitmap.h" @@ -138,7 +138,6 @@ struct gtk_scaffolding { GtkBuilder *xml; struct gtk_history_window *history_window; - GtkDialog *preferences_dialog; int throb_frame; struct gui_window *top_level; @@ -1027,10 +1026,12 @@ MULTIHANDLER(find) MULTIHANDLER(preferences) { struct browser_window *bw = nsgtk_get_browser_window(g->top_level); - if (g->preferences_dialog == NULL) - g->preferences_dialog = nsgtk_options_init(bw, g->window); - else - gtk_widget_show(GTK_WIDGET(g->preferences_dialog)); + GtkWidget* wndpreferences; + + wndpreferences = nsgtk_preferences(bw, g->window); + if (wndpreferences != NULL) { + gtk_widget_show(GTK_WIDGET(wndpreferences)); + } return TRUE; } @@ -1769,8 +1770,6 @@ nsgtk_scaffolding *nsgtk_new_scaffolding(struct gui_window *toplevel) g->menu_bar = nsgtk_menu_bar_create(GTK_MENU_SHELL(gtk_builder_get_object(g->xml, "menubar")), group); - g->preferences_dialog = NULL; - /* set this window's size and position to what's in the options, or * or some sensible default if they're not set yet. */ diff --git a/gtk/theme.c b/gtk/theme.c index f16bd2d11..402433c32 100644 --- a/gtk/theme.c +++ b/gtk/theme.c @@ -32,7 +32,7 @@ #include "gtk/theme.h" #include "gtk/window.h" #include "desktop/options.h" -#include "gtk/dialogs/options.h" +#include "gtk/dialogs/preferences.h" #include "utils/container.h" #include "utils/log.h" #include "utils/messages.h" @@ -157,28 +157,32 @@ static bool nsgtk_theme_verify(const char *themename) void nsgtk_theme_init(void) { - size_t len; - if (nsoption_int(current_theme) == 0) { + int theme; + nsgtk_scaffolding *list = scaf_list; + FILE *fp; + char buf[50]; + int row_count = 0; + + theme = nsoption_int(current_theme); + + /* check if default theme is selected */ + if (theme == 0) { return; } - len = SLEN("themelist") + strlen(res_dir_location) + 1; - char themefile[len]; - snprintf(themefile, len, "%s%s", res_dir_location, "themelist"); - nsgtk_scaffolding *list = scaf_list; nsgtk_theme_verify(NULL); - FILE *fp = fopen(themefile, "r"); + fp = fopen(themelist_file_location, "r"); if (fp == NULL) return; - char buf[50]; - int row_count = 0; + while (fgets(buf, sizeof(buf), fp) != NULL) { if (buf[0] == '\0') continue; - if (row_count++ == nsoption_int(current_theme)) { - if (current_theme_name != NULL) + if (row_count++ == theme) { + if (current_theme_name != NULL) { free(current_theme_name); + } /* clear the '\n' ["\n\0"->"\0\0"] */ buf[strlen(buf) - 1] = '\0'; current_theme_name = strdup(buf); @@ -204,13 +208,25 @@ char *nsgtk_theme_name(void) } /** - * set static global current_theme_name from param; caller is responsible - * for the integrity of the global reference + * set static global current_theme_name from param */ -void nsgtk_theme_set_name(char *name) +void nsgtk_theme_set_name(const char *name) { - current_theme_name = name; + if ((name == NULL) && (current_theme_name == NULL)) { + return; /* setting it to the same thing */ + } else if ((name == NULL) && (current_theme_name != NULL)) { + free(current_theme_name); + current_theme_name = NULL; + } else if ((name != NULL) && (current_theme_name == NULL)) { + current_theme_name = strdup(name); + nsgtk_theme_prepare(); + } else if (strcmp(name, current_theme_name) != 0) { + /* disimilar new name */ + free(current_theme_name); + current_theme_name = strdup(name); + nsgtk_theme_prepare(); + } } /** @@ -260,9 +276,7 @@ void nsgtk_theme_add(const char *themename) gtk_widget_show_all(notification); /* update combo */ - if (wndPreferences != NULL) { - nsgtk_options_combo_theme_add(themename); - } + nsgtk_preferences_theme_add(themename); } diff --git a/gtk/theme.h b/gtk/theme.h index 117207037..4aedad23f 100644 --- a/gtk/theme.h +++ b/gtk/theme.h @@ -41,6 +41,6 @@ void nsgtk_theme_init(void); void nsgtk_theme_prepare(void); void nsgtk_theme_implement(struct gtk_scaffolding *g); char *nsgtk_theme_name(void); -void nsgtk_theme_set_name(char *name); +void nsgtk_theme_set_name(const char *name); #endif -- cgit v1.2.3 From 8bf3346ce24cdc889b7b52858ee2724229cb6a7d Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Wed, 2 Jan 2013 12:46:17 +0000 Subject: Fix min_max line width calc to include box spaces. --- render/layout.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/render/layout.c b/render/layout.c index 702cc03c6..331e1efdb 100644 --- a/render/layout.c +++ b/render/layout.c @@ -3045,11 +3045,15 @@ struct box *layout_minmax_line(struct box *first, &fixed, &frac); if (0 < fixed) max += fixed; - if (b->next && b->space == UNKNOWN_WIDTH) { - font_func->font_width(&fstyle, " ", 1, - &b->space); + + if (b->next) { + if (b->space == UNKNOWN_WIDTH) { + font_func->font_width(&fstyle, " ", 1, + &b->space); + } max += b->space; } + *line_has_height = true; continue; } @@ -3095,9 +3099,11 @@ struct box *layout_minmax_line(struct box *first, } } max += b->width; - if (b->next && b->space == UNKNOWN_WIDTH) { - font_func->font_width(&fstyle, " ", 1, - &b->space); + if (b->next) { + if (b->space == UNKNOWN_WIDTH) { + font_func->font_width(&fstyle, " ", 1, + &b->space); + } max += b->space; } -- cgit v1.2.3 From d7e3ca2230578267c86817bca6f56892a3d483f6 Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Wed, 2 Jan 2013 13:56:00 +0000 Subject: Fix form input size attribute handling to be case insensitive. Fixes form input widths. --- css/select.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/css/select.c b/css/select.c index a52b8b144..6004c3e3a 100644 --- a/css/select.c +++ b/css/select.c @@ -2516,20 +2516,17 @@ node_presentational_hint_width(nscss_select_ctx *ctx, } if ((width == NULL) || - dom_string_lwc_isequal(width, + dom_string_caseless_lwc_isequal(width, corestring_lwc_text) || - dom_string_lwc_isequal(width, + dom_string_caseless_lwc_isequal(width, corestring_lwc_search) || - dom_string_lwc_isequal(width, + dom_string_caseless_lwc_isequal(width, corestring_lwc_file) || - dom_string_lwc_isequal(width, + dom_string_caseless_lwc_isequal(width, corestring_lwc_password)) { hint->data.length.unit = CSS_UNIT_EX; - } dom_string_unref(width); - - } return CSS_OK; -- cgit v1.2.3 From 700429897a435a063b5f727f91c4f8e0d57f3098 Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Wed, 2 Jan 2013 14:06:02 +0000 Subject: Fix #3584934. Case insensitive matching of align attribute value. --- css/select.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/css/select.c b/css/select.c index a52b8b144..c2bfc8bb1 100644 --- a/css/select.c +++ b/css/select.c @@ -2669,11 +2669,13 @@ node_presentational_hint_float(nscss_select_ctx *ctx, return CSS_PROPERTY_NOT_SET; } - if (dom_string_lwc_isequal(align, corestring_lwc_left)) { + if (dom_string_caseless_lwc_isequal(align, + corestring_lwc_left)) { hint->status = CSS_FLOAT_LEFT; dom_string_unref(align); return CSS_OK; - } else if (dom_string_lwc_isequal(align, corestring_lwc_right)) { + } else if (dom_string_caseless_lwc_isequal(align, + corestring_lwc_right)) { hint->status = CSS_FLOAT_RIGHT; dom_string_unref(align); return CSS_OK; -- cgit v1.2.3 From 96841ae2ff8d6a08a1ea453a7fcc14e9a8729401 Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Wed, 2 Jan 2013 18:02:15 +0000 Subject: Remove forward declaration. --- render/html_redraw.c | 4121 +++++++++++++++++++++++++------------------------- 1 file changed, 2042 insertions(+), 2079 deletions(-) diff --git a/render/html_redraw.c b/render/html_redraw.c index e7474f8c4..26bc46c98 100644 --- a/render/html_redraw.c +++ b/render/html_redraw.c @@ -53,125 +53,9 @@ #include "utils/utils.h" -static bool html_redraw_box(const html_content *html, struct box *box, - int x, int y, const struct rect *clip, float scale, - colour current_background_color, - const struct redraw_context *ctx); -static bool html_redraw_box_children(const html_content *html, struct box *box, - int x_parent, int y_parent, const struct rect *clip, - float scale, colour current_background_color, - const struct redraw_context *ctx); -static bool html_redraw_text_box(const html_content *html, struct box *box, - int x, int y, const struct rect *clip, float scale, - colour current_background_color, - const struct redraw_context *ctx); -static bool html_redraw_borders(struct box *box, int x_parent, int y_parent, - int p_width, int p_height, const struct rect *clip, - float scale, const struct redraw_context *ctx); -static bool html_redraw_inline_borders(struct box *box, struct rect b, - const struct rect *clip, float scale, bool first, bool last, - const struct redraw_context *ctx); -static bool html_redraw_border_plot(const int side, const int *p, colour c, - enum css_border_style_e style, int thickness, bool rectangular, - const struct rect *clip, const struct redraw_context *ctx); -static bool html_redraw_checkbox(int x, int y, int width, int height, - bool selected, const struct redraw_context *ctx); -static bool html_redraw_radio(int x, int y, int width, int height, - bool selected, const struct redraw_context *ctx); -static bool html_redraw_file(int x, int y, int width, int height, - struct box *box, float scale, colour background_colour, - const struct redraw_context *ctx); -static bool html_redraw_background(int x, int y, struct box *box, float scale, - const struct rect *clip, colour *background_colour, - struct box *background, const struct redraw_context *ctx); -static bool html_redraw_inline_background(int x, int y, struct box *box, - float scale, const struct rect *clip, struct rect b, - bool first, bool last, colour *background_colour, - const struct redraw_context *ctx); -static bool html_redraw_text_decoration(struct box *box, - int x_parent, int y_parent, float scale, - colour background_colour, const struct redraw_context *ctx); -static bool html_redraw_text_decoration_inline(struct box *box, int x, int y, - float scale, colour colour, float ratio, - const struct redraw_context *ctx); -static bool html_redraw_text_decoration_block(struct box *box, int x, int y, - float scale, colour colour, float ratio, - const struct redraw_context *ctx); - -bool html_redraw_debug = false; - -/** - * Draw a CONTENT_HTML using the current set of plotters (plot). - * - * \param c content of type CONTENT_HTML - * \param data redraw data for this content redraw - * \param clip current clip region - * \param ctx current redraw context - * \return true if successful, false otherwise - * - * x, y, clip_[xy][01] are in target coordinates. - */ - -bool html_redraw(struct content *c, struct content_redraw_data *data, - const struct rect *clip, const struct redraw_context *ctx) -{ - html_content *html = (html_content *) c; - struct box *box; - bool result = true; - bool select, select_only; - plot_style_t pstyle_fill_bg = { - .fill_type = PLOT_OP_TYPE_SOLID, - .fill_colour = data->background_colour, - }; - - box = html->layout; - assert(box); - - /* The select menu needs special treating because, when opened, it - * reaches beyond its layout box. - */ - select = false; - select_only = false; - if (ctx->interactive && html->visible_select_menu != NULL) { - struct form_control *control = html->visible_select_menu; - select = true; - /* check if the redraw rectangle is completely inside of the - select menu */ - select_only = form_clip_inside_select_menu(control, - data->scale, clip); - } - - if (!select_only) { - /* clear to background colour */ - result = ctx->plot->clip(clip); - - if (html->background_colour != NS_TRANSPARENT) - pstyle_fill_bg.fill_colour = html->background_colour; - - result &= ctx->plot->rectangle(clip->x0, clip->y0, - clip->x1, clip->y1, - &pstyle_fill_bg); - - result &= html_redraw_box(html, box, data->x, data->y, clip, - data->scale, pstyle_fill_bg.fill_colour, ctx); - } - - if (select) { - int menu_x, menu_y; - box = html->visible_select_menu->box; - box_coords(box, &menu_x, &menu_y); - - menu_x -= box->border[LEFT].width; - menu_y += box->height + box->border[BOTTOM].width + - box->padding[BOTTOM] + box->padding[TOP]; - result &= form_redraw_select_menu(html->visible_select_menu, - data->x + menu_x, data->y + menu_y, - data->scale, clip, ctx); - } - return result; -} +bool html_redraw_debug = false; /** * Determine if a box has a background that needs drawing @@ -239,895 +123,829 @@ static struct box *html_redraw_find_bg_box(struct box *box) } /** - * Recursively draw a box. - * - * \param html html content - * \param box box to draw - * \param x_parent coordinate of parent box - * \param y_parent coordinate of parent box - * \param clip clip rectangle - * \param scale scale for redraw - * \param current_background_color background colour under this box - * \param ctx current redraw context - * \return true if successful, false otherwise + * Redraw a short text string, complete with highlighting + * (for selection/search) * - * x, y, clip_[xy][01] are in target coordinates. + * \param utf8_text pointer to UTF-8 text string + * \param utf8_len length of string, in bytes + * \param offset byte offset within textual representation + * \param space width of space that follows string (0 = no space) + * \param fstyle text style to use (pass text size unscaled) + * \param x x ordinate at which to plot text + * \param y y ordinate at which to plot text + * \param clip pointer to current clip rectangle + * \param height height of text string + * \param scale current display scale (1.0 = 100%) + * \param excluded exclude this text string from the selection + * \param ctx current redraw context + * \return true iff successful and redraw should proceed */ -bool html_redraw_box(const html_content *html, struct box *box, - int x_parent, int y_parent, - const struct rect *clip, float scale, - colour current_background_color, +bool text_redraw(const char *utf8_text, size_t utf8_len, + size_t offset, int space, const plot_font_style_t *fstyle, + int x, int y, const struct rect *clip, int height, + float scale, bool excluded, struct content *c, + const struct selection *sel, struct search_context *search, const struct redraw_context *ctx) { const struct plotter_table *plot = ctx->plot; - int x, y; - int width, height; - int padding_left, padding_top, padding_width, padding_height; - int border_left, border_top, border_right, border_bottom; - struct rect r; - int x_scrolled, y_scrolled; - struct box *bg_box = NULL; - bool has_x_scroll, has_y_scroll; - css_computed_clip_rect css_rect; + bool highlighted = false; + plot_font_style_t plot_fstyle = *fstyle; - if (html_redraw_printing && (box->flags & PRINTED)) - return true; + /* Need scaled text size to pass to plotters */ + plot_fstyle.size *= scale; - /* avoid trivial FP maths */ - if (scale == 1.0) { - x = x_parent + box->x; - y = y_parent + box->y; - width = box->width; - height = box->height; - padding_left = box->padding[LEFT]; - padding_top = box->padding[TOP]; - padding_width = padding_left + box->width + box->padding[RIGHT]; - padding_height = padding_top + box->height + - box->padding[BOTTOM]; - border_left = box->border[LEFT].width; - border_top = box->border[TOP].width; - border_right = box->border[RIGHT].width; - border_bottom = box->border[BOTTOM].width; - } else { - x = (x_parent + box->x) * scale; - y = (y_parent + box->y) * scale; - width = box->width * scale; - height = box->height * scale; - /* left and top padding values are normally zero, - * so avoid trivial FP maths */ - padding_left = box->padding[LEFT] ? box->padding[LEFT] * scale - : 0; - padding_top = box->padding[TOP] ? box->padding[TOP] * scale - : 0; - padding_width = (box->padding[LEFT] + box->width + - box->padding[RIGHT]) * scale; - padding_height = (box->padding[TOP] + box->height + - box->padding[BOTTOM]) * scale; - border_left = box->border[LEFT].width * scale; - border_top = box->border[TOP].width * scale; - border_right = box->border[RIGHT].width * scale; - border_bottom = box->border[BOTTOM].width * scale; - } + /* is this box part of a selection? */ + if (!excluded && ctx->interactive == true) { + unsigned len = utf8_len + (space ? 1 : 0); + unsigned start_idx; + unsigned end_idx; - /* calculate rectangle covering this box and descendants */ - if (box->style && css_computed_overflow(box->style) != - CSS_OVERFLOW_VISIBLE) { - /* box contents clipped to box size */ - r.x0 = x - border_left; - r.y0 = y - border_top; - r.x1 = x + padding_width + border_right; - r.y1 = y + padding_height + border_bottom; - } else { - /* box contents can hang out of the box; use descendant box */ - if (scale == 1.0) { - r.x0 = x + box->descendant_x0; - r.y0 = y + box->descendant_y0; - r.x1 = x + box->descendant_x1 + 1; - r.y1 = y + box->descendant_y1 + 1; - } else { - r.x0 = x + box->descendant_x0 * scale; - r.y0 = y + box->descendant_y0 * scale; - r.x1 = x + box->descendant_x1 * scale + 1; - r.y1 = y + box->descendant_y1 * scale + 1; - } - if (!box->parent) { - /* root element */ - int margin_left, margin_right; - int margin_top, margin_bottom; - if (scale == 1.0) { - margin_left = box->margin[LEFT]; - margin_top = box->margin[TOP]; - margin_right = box->margin[RIGHT]; - margin_bottom = box->margin[BOTTOM]; - } else { - margin_left = box->margin[LEFT] * scale; - margin_top = box->margin[TOP] * scale; - margin_right = box->margin[RIGHT] * scale; - margin_bottom = box->margin[BOTTOM] * scale; - } - r.x0 = x - border_left - margin_left < r.x0 ? - x - border_left - margin_left : r.x0; - r.y0 = y - border_top - margin_top < r.y0 ? - y - border_top - margin_top : r.y0; - r.x1 = x + padding_width + border_right + - margin_right > r.x1 ? - x + padding_width + border_right + - margin_right : r.x1; - r.y1 = y + padding_height + border_bottom + - margin_bottom > r.y1 ? - y + padding_height + border_bottom + - margin_bottom : r.y1; + /* first try the browser window's current selection */ + if (selection_defined(sel) && selection_highlighted(sel, + offset, offset + len, + &start_idx, &end_idx)) { + highlighted = true; } - } - /* return if the rectangle is completely outside the clip rectangle */ - if (clip->y1 < r.y0 || r.y1 < clip->y0 || - clip->x1 < r.x0 || r.x1 < clip->x0) - return true; + /* what about the current search operation, if any? */ + if (!highlighted && (search != NULL) && + search_term_highlighted(c, + offset, offset + len, + &start_idx, &end_idx, + search)) { + highlighted = true; + } - /*if the rectangle is under the page bottom but it can fit in a page, - don't print it now*/ - if (html_redraw_printing) { - if (r.y1 > html_redraw_printing_border) { - if (r.y1 - r.y0 <= html_redraw_printing_border && - (box->type == BOX_TEXT || - box->type == BOX_TABLE_CELL - || box->object || box->gadget)) { - /*remember the highest of all points from the - not printed elements*/ - if (r.y0 < html_redraw_printing_top_cropped) - html_redraw_printing_top_cropped = r.y0; - return true; + /* \todo make search terms visible within selected text */ + if (highlighted) { + struct rect r; + unsigned endtxt_idx = end_idx; + bool clip_changed = false; + bool text_visible = true; + int startx, endx; + plot_style_t *pstyle_fill_hback = plot_style_fill_white; + plot_font_style_t fstyle_hback = plot_fstyle; + + if (end_idx > utf8_len) { + /* adjust for trailing space, not present in + * utf8_text */ + assert(end_idx == utf8_len + 1); + endtxt_idx = utf8_len; } - } - else box->flags |= PRINTED; /*it won't be printed anymore*/ - } - /* if visibility is hidden render children only */ - if (box->style && css_computed_visibility(box->style) == - CSS_VISIBILITY_HIDDEN) { - if ((plot->group_start) && (!plot->group_start("hidden box"))) - return false; - if (!html_redraw_box_children(html, box, x_parent, y_parent, - &r, scale, current_background_color, ctx)) - return false; - return ((!plot->group_end) || (plot->group_end())); - } + if (!nsfont.font_width(fstyle, utf8_text, start_idx, + &startx)) + startx = 0; - if ((plot->group_start) && (!plot->group_start("vis box"))) - return false; + if (!nsfont.font_width(fstyle, utf8_text, endtxt_idx, + &endx)) + endx = 0; + /* is there a trailing space that should be highlighted + * as well? */ + if (end_idx > utf8_len) { + endx += space; + } - if (box->style != NULL && - css_computed_position(box->style) == - CSS_POSITION_ABSOLUTE && - css_computed_clip(box->style, &css_rect) == - CSS_CLIP_RECT) { - /* We have an absolutly positioned box with a clip rect */ - if (css_rect.left_auto == false) - r.x0 = x - border_left + FIXTOINT(nscss_len2px( - css_rect.left, css_rect.lunit, - box->style)); + if (scale != 1.0) { + startx *= scale; + endx *= scale; + } - if (css_rect.top_auto == false) - r.y0 = y - border_top + FIXTOINT(nscss_len2px( - css_rect.top, css_rect.tunit, - box->style)); + /* draw any text preceding highlighted portion */ + if (start_idx > 0 && + !plot->text(x, y + (int)(height * 0.75 * scale), + utf8_text, start_idx, + &plot_fstyle)) + return false; - if (css_rect.right_auto == false) - r.x1 = x - border_left + FIXTOINT(nscss_len2px( - css_rect.right, css_rect.runit, - box->style)); + /* decide whether highlighted portion is to be + * white-on-black or black-on-white */ + if ((fstyle->background & 0x808080) == 0x808080) + pstyle_fill_hback = plot_style_fill_black; - if (css_rect.bottom_auto == false) - r.y1 = y - border_top + FIXTOINT(nscss_len2px( - css_rect.bottom, css_rect.bunit, - box->style)); + /* highlighted portion */ + if (!plot->rectangle(x + startx, y, x + endx, + y + height * scale, + pstyle_fill_hback)) + return false; - /* find intersection of clip rectangle and box */ - if (r.x0 < clip->x0) r.x0 = clip->x0; - if (r.y0 < clip->y0) r.y0 = clip->y0; - if (clip->x1 < r.x1) r.x1 = clip->x1; - if (clip->y1 < r.y1) r.y1 = clip->y1; - /* no point trying to draw 0-width/height boxes */ - if (r.x0 == r.x1 || r.y0 == r.y1) - /* not an error */ - return ((!plot->group_end) || (plot->group_end())); - /* clip to it */ - if (!plot->clip(&r)) - return false; + if (start_idx > 0) { + int px0 = max(x + startx, clip->x0); + int px1 = min(x + endx, clip->x1); - } else if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK || - box->type == BOX_TABLE_CELL || box->object) { - /* find intersection of clip rectangle and box */ - if (r.x0 < clip->x0) r.x0 = clip->x0; - if (r.y0 < clip->y0) r.y0 = clip->y0; - if (clip->x1 < r.x1) r.x1 = clip->x1; - if (clip->y1 < r.y1) r.y1 = clip->y1; - /* no point trying to draw 0-width/height boxes */ - if (r.x0 == r.x1 || r.y0 == r.y1) - /* not an error */ - return ((!plot->group_end) || (plot->group_end())); - /* clip to it */ - if (!plot->clip(&r)) - return false; - } else { - /* clip box is fine, clip to it */ - r = *clip; - if (!plot->clip(&r)) - return false; - } + if (px0 < px1) { + r.x0 = px0; + r.y0 = clip->y0; + r.x1 = px1; + r.y1 = clip->y1; + if (!plot->clip(&r)) + return false; + clip_changed = true; + } else { + text_visible = false; + } + } - /* background colour and image for block level content and replaced - * inlines */ + fstyle_hback.background = + pstyle_fill_hback->fill_colour; + fstyle_hback.foreground = + pstyle_fill_hback->fill_colour ^ 0xffffff; - bg_box = html_redraw_find_bg_box(box); + if (text_visible && + !plot->text(x, y + (int)(height * 0.75 * scale), + utf8_text, endtxt_idx, + &fstyle_hback)) + return false; - /* bg_box == NULL implies that this box should not have - * its background rendered. Otherwise filter out linebreaks, - * optimize away non-differing inlines, only plot background - * for BOX_TEXT it's in an inline */ - if (bg_box && bg_box->type != BOX_BR && - bg_box->type != BOX_TEXT && - bg_box->type != BOX_INLINE_END && - (bg_box->type != BOX_INLINE || bg_box->object || - bg_box->flags & IFRAME || box->flags & REPLACE_DIM)) { - /* find intersection of clip box and border edge */ - struct rect p; - p.x0 = x - border_left < r.x0 ? r.x0 : x - border_left; - p.y0 = y - border_top < r.y0 ? r.y0 : y - border_top; - p.x1 = x + padding_width + border_right < r.x1 ? - x + padding_width + border_right : r.x1; - p.y1 = y + padding_height + border_bottom < r.y1 ? - y + padding_height + border_bottom : r.y1; - if (!box->parent) { - /* Root element, special case: - * background covers margins too */ - int m_left, m_top, m_right, m_bottom; - if (scale == 1.0) { - m_left = box->margin[LEFT]; - m_top = box->margin[TOP]; - m_right = box->margin[RIGHT]; - m_bottom = box->margin[BOTTOM]; - } else { - m_left = box->margin[LEFT] * scale; - m_top = box->margin[TOP] * scale; - m_right = box->margin[RIGHT] * scale; - m_bottom = box->margin[BOTTOM] * scale; + /* draw any text succeeding highlighted portion */ + if (endtxt_idx < utf8_len) { + int px0 = max(x + endx, clip->x0); + if (px0 < clip->x1) { + + r.x0 = px0; + r.y0 = clip->y0; + r.x1 = clip->x1; + r.y1 = clip->y1; + if (!plot->clip(&r)) + return false; + + clip_changed = true; + + if (!plot->text(x, y + (int) + (height * 0.75 * scale), + utf8_text, utf8_len, + &plot_fstyle)) + return false; + } } - p.x0 = p.x0 - m_left < r.x0 ? r.x0 : p.x0 - m_left; - p.y0 = p.y0 - m_top < r.y0 ? r.y0 : p.y0 - m_top; - p.x1 = p.x1 + m_right < r.x1 ? p.x1 + m_right : r.x1; - p.y1 = p.y1 + m_bottom < r.y1 ? p.y1 + m_bottom : r.y1; - } - /* valid clipping rectangles only */ - if ((p.x0 < p.x1) && (p.y0 < p.y1)) { - /* plot background */ - if (!html_redraw_background(x, y, box, scale, &p, - ¤t_background_color, bg_box, ctx)) - return false; - /* restore previous graphics window */ - if (!plot->clip(&r)) + + if (clip_changed && + !plot->clip(clip)) return false; } } - /* borders for block level content and replaced inlines */ - if (box->style && box->type != BOX_TEXT && - box->type != BOX_INLINE_END && - (box->type != BOX_INLINE || box->object || - box->flags & IFRAME || box->flags & REPLACE_DIM) && - (border_top || border_right || - border_bottom || border_left)) { - if (!html_redraw_borders(box, x_parent, y_parent, - padding_width, padding_height, &r, - scale, ctx)) + if (!highlighted) { + if (!plot->text(x, y + (int) (height * 0.75 * scale), + utf8_text, utf8_len, + &plot_fstyle)) return false; } + return true; +} - /* backgrounds and borders for non-replaced inlines */ - if (box->style && box->type == BOX_INLINE && box->inline_end && - (html_redraw_box_has_background(box) || - border_top || border_right || - border_bottom || border_left)) { - /* inline backgrounds and borders span other boxes and may - * wrap onto separate lines */ - struct box *ib; - struct rect b; /* border edge rectangle */ - struct rect p; /* clipped rect */ - bool first = true; - int ib_x; - int ib_y = y; - int ib_p_width; - int ib_b_left, ib_b_right; - - b.x0 = x - border_left; - b.x1 = x + padding_width + border_right; - b.y0 = y - border_top; - b.y1 = y + padding_height + border_bottom; - - p.x0 = b.x0 < r.x0 ? r.x0 : b.x0; - p.x1 = b.x1 < r.x1 ? b.x1 : r.x1; - p.y0 = b.y0 < r.y0 ? r.y0 : b.y0; - p.y1 = b.y1 < r.y1 ? b.y1 : r.y1; - for (ib = box; ib; ib = ib->next) { - /* to get extents of rectangle(s) associated with - * inline, cycle though all boxes in inline, skipping - * over floats */ - if (ib->type == BOX_FLOAT_LEFT || - ib->type == BOX_FLOAT_RIGHT) - continue; - if (scale == 1.0) { - ib_x = x_parent + ib->x; - ib_y = y_parent + ib->y; - ib_p_width = ib->padding[LEFT] + ib->width + - ib->padding[RIGHT]; - ib_b_left = ib->border[LEFT].width; - ib_b_right = ib->border[RIGHT].width; - } else { - ib_x = (x_parent + ib->x) * scale; - ib_y = (y_parent + ib->y) * scale; - ib_p_width = (ib->padding[LEFT] + ib->width + - ib->padding[RIGHT]) * scale; - ib_b_left = ib->border[LEFT].width * scale; - ib_b_right = ib->border[RIGHT].width * scale; - } +static plot_style_t plot_style_bdr = { + .stroke_type = PLOT_OP_TYPE_DASH, +}; +static plot_style_t plot_style_fillbdr = { + .fill_type = PLOT_OP_TYPE_SOLID, +}; +static plot_style_t plot_style_fillbdr_dark = { + .fill_type = PLOT_OP_TYPE_SOLID, +}; +static plot_style_t plot_style_fillbdr_light = { + .fill_type = PLOT_OP_TYPE_SOLID, +}; +static plot_style_t plot_style_fillbdr_ddark = { + .fill_type = PLOT_OP_TYPE_SOLID, +}; +static plot_style_t plot_style_fillbdr_dlight = { + .fill_type = PLOT_OP_TYPE_SOLID, +}; - if ((ib->flags & NEW_LINE) && ib != box) { - /* inline element has wrapped, plot background - * and borders */ - if (!html_redraw_inline_background( - x, y, box, scale, &p, b, - first, false, - ¤t_background_color, ctx)) - return false; - /* restore previous graphics window */ - if (!plot->clip(&r)) - return false; - if (!html_redraw_inline_borders(box, b, &r, - scale, first, false, ctx)) - return false; - /* reset coords */ - b.x0 = ib_x - ib_b_left; - b.y0 = ib_y - border_top - padding_top; - b.y1 = ib_y + padding_height - padding_top + - border_bottom; +/** + * Draw one border. + * + * \param side index of border side (TOP, RIGHT, BOTTOM, LEFT) + * \param p array of precomputed border vertices + * \param c colour for border + * \param style border line style + * \param thickness border thickness + * \param rectangular whether border is rectangular + * \param ctx current redraw context + * \return true if successful, false otherwise + */ - p.x0 = b.x0 < r.x0 ? r.x0 : b.x0; - p.y0 = b.y0 < r.y0 ? r.y0 : b.y0; - p.y1 = b.y1 < r.y1 ? b.y1 : r.y1; +static bool html_redraw_border_plot(const int side, const int *p, colour c, + enum css_border_style_e style, int thickness, bool rectangular, + const struct rect *clip, const struct redraw_context *ctx) +{ + const struct plotter_table *plot = ctx->plot; + int z[8]; /* Vertices of border part */ + unsigned int light = side; + plot_style_t *plot_style_bdr_in; + plot_style_t *plot_style_bdr_out; - first = false; - } + if (c == NS_TRANSPARENT) + return true; - /* increase width for current box */ - b.x1 = ib_x + ib_p_width + ib_b_right; - p.x1 = b.x1 < r.x1 ? b.x1 : r.x1; + plot_style_bdr.stroke_type = PLOT_OP_TYPE_DASH; + plot_style_bdr.stroke_colour = c; + plot_style_bdr.stroke_width = thickness; + plot_style_fillbdr.fill_colour = c; + plot_style_fillbdr_dark.fill_colour = darken_colour(c); + plot_style_fillbdr_light.fill_colour = lighten_colour(c); + plot_style_fillbdr_ddark.fill_colour = double_darken_colour(c); + plot_style_fillbdr_dlight.fill_colour = double_lighten_colour(c); - if (ib == box->inline_end) - /* reached end of BOX_INLINE span */ - break; - } - /* plot background and borders for last rectangle of - * the inline */ - if (!html_redraw_inline_background(x, ib_y, box, scale, &p, b, - first, true, ¤t_background_color, ctx)) - return false; - /* restore previous graphics window */ - if (!plot->clip(&r)) - return false; - if (!html_redraw_inline_borders(box, b, &r, scale, first, true, - ctx)) + switch (style) { + case CSS_BORDER_STYLE_DOTTED: + plot_style_bdr.stroke_type = PLOT_OP_TYPE_DOT; + /* fall through */ + case CSS_BORDER_STYLE_DASHED: + if (!plot->line((p[0] + p[2]) / 2, + (p[1] + p[3]) / 2, + (p[4] + p[6]) / 2, + (p[5] + p[7]) / 2, + &plot_style_bdr)) return false; + break; - } - - /* Debug outlines */ - if (html_redraw_debug) { - int margin_left, margin_right; - int margin_top, margin_bottom; - if (scale == 1.0) { - /* avoid trivial fp maths */ - margin_left = box->margin[LEFT]; - margin_top = box->margin[TOP]; - margin_right = box->margin[RIGHT]; - margin_bottom = box->margin[BOTTOM]; + case CSS_BORDER_STYLE_SOLID: + /* fall through to default */ + default: + if (rectangular || thickness == 1) { + int x0, y0, x1, y1; + if (side == TOP || side == RIGHT) { + x0 = p[2]; y0 = p[3]; + x1 = p[6]; y1 = p[7]; + x1 = ((side == TOP) && (p[4] - p[6] != 0)) ? + x1 + p[4] - p[6] : x1; + } else { + x0 = p[6]; y0 = p[7]; + x1 = p[2]; y1 = p[3]; + y1 = ((side == LEFT) && (p[1] - p[3] != 0)) ? + y1 + p[1] - p[3] : y1; + } + /* find intersection of clip rectangle and border */ + x0 = (clip->x0 > x0) ? clip->x0 : x0; + y0 = (clip->y0 > y0) ? clip->y0 : y0; + x1 = (clip->x1 < x1) ? clip->x1 : x1; + y1 = (clip->y1 < y1) ? clip->y1 : y1; + if ((x0 < x1) && (y0 < y1)) { + /* valid clip rectangles only */ + if (!plot->rectangle(x0, y0, x1, y1, + &plot_style_fillbdr)) + return false; + } } else { - margin_left = box->margin[LEFT] * scale; - margin_top = box->margin[TOP] * scale; - margin_right = box->margin[RIGHT] * scale; - margin_bottom = box->margin[BOTTOM] * scale; - } - /* Content edge -- blue */ - if (!plot->rectangle(x + padding_left, - y + padding_top, - x + padding_left + width, - y + padding_top + height, - plot_style_content_edge)) - return false; - /* Padding edge -- red */ - if (!plot->rectangle(x, y, - x + padding_width, y + padding_height, - plot_style_padding_edge)) - return false; - /* Margin edge -- yellow */ - if (!plot->rectangle( - x - border_left - margin_left, - y - border_top - margin_top, - x + padding_width + border_right + - margin_right, - y + padding_height + border_bottom + - margin_bottom, - plot_style_margin_edge)) - return false; - } - - /* clip to the padding edge for objects, or boxes with overflow hidden - * or scroll */ - if ((box->style && css_computed_overflow(box->style) != - CSS_OVERFLOW_VISIBLE) || box->object || - box->flags & IFRAME) { - r.x0 = x; - r.y0 = y; - r.x1 = x + padding_width; - r.y1 = y + padding_height; - if (r.x0 < clip->x0) r.x0 = clip->x0; - if (r.y0 < clip->y0) r.y0 = clip->y0; - if (clip->x1 < r.x1) r.x1 = clip->x1; - if (clip->y1 < r.y1) r.y1 = clip->y1; - if (r.x1 <= r.x0 || r.y1 <= r.y0) - return ((!plot->group_end) || (plot->group_end())); - if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK || - box->type == BOX_TABLE_CELL || box->object) { - if (!plot->clip(&r)) + if (!plot->polygon(p, 4, &plot_style_fillbdr)) return false; } - } + break; - /* text decoration */ - if (box->type != BOX_TEXT && box->style && - css_computed_text_decoration(box->style) != - CSS_TEXT_DECORATION_NONE) - if (!html_redraw_text_decoration(box, x_parent, y_parent, - scale, current_background_color, ctx)) + case CSS_BORDER_STYLE_DOUBLE: + z[0] = p[0]; + z[1] = p[1]; + z[2] = (p[0] * 2 + p[2]) / 3; + z[3] = (p[1] * 2 + p[3]) / 3; + z[4] = (p[6] * 2 + p[4]) / 3; + z[5] = (p[7] * 2 + p[5]) / 3; + z[6] = p[6]; + z[7] = p[7]; + if (!plot->polygon(z, 4, &plot_style_fillbdr)) return false; + z[0] = p[2]; + z[1] = p[3]; + z[2] = (p[2] * 2 + p[0]) / 3; + z[3] = (p[3] * 2 + p[1]) / 3; + z[4] = (p[4] * 2 + p[6]) / 3; + z[5] = (p[5] * 2 + p[7]) / 3; + z[6] = p[4]; + z[7] = p[5]; + if (!plot->polygon(z, 4, &plot_style_fillbdr)) + return false; + break; - if (box->object && width != 0 && height != 0) { - struct content_redraw_data obj_data; - - x_scrolled = x - scrollbar_get_offset(box->scroll_x) * scale; - y_scrolled = y - scrollbar_get_offset(box->scroll_y) * scale; + case CSS_BORDER_STYLE_GROOVE: + light = 3 - light; + /* fall through */ + case CSS_BORDER_STYLE_RIDGE: + /* choose correct colours for each part of the border line */ + if (light <= 1) { + plot_style_bdr_in = &plot_style_fillbdr_dark; + plot_style_bdr_out = &plot_style_fillbdr_light; + } else { + plot_style_bdr_in = &plot_style_fillbdr_light; + plot_style_bdr_out = &plot_style_fillbdr_dark; + } - obj_data.x = x_scrolled + padding_left; - obj_data.y = y_scrolled + padding_top; - obj_data.width = width; - obj_data.height = height; - obj_data.background_colour = current_background_color; - obj_data.scale = scale; - obj_data.repeat_x = false; - obj_data.repeat_y = false; + /* Render border */ + if ((rectangular || thickness == 2) && thickness != 1) { + /* Border made up from two parts and can be plotted + * with rectangles */ + int x0, y0, x1, y1; - if (content_get_type(box->object) == CONTENT_HTML) { - obj_data.x /= scale; - obj_data.y /= scale; - } + /* First part */ + if (side == TOP || side == RIGHT) { + x0 = (p[0] + p[2]) / 2; y0 = (p[1] + p[3]) / 2; + x1 = p[6]; y1 = p[7]; + } else { + x0 = p[6]; y0 = p[7]; + x1 = (p[0] + p[2]) / 2; y1 = (p[1] + p[3]) / 2; + } + /* find intersection of clip rectangle and border */ + x0 = (clip->x0 > x0) ? clip->x0 : x0; + y0 = (clip->y0 > y0) ? clip->y0 : y0; + x1 = (clip->x1 < x1) ? clip->x1 : x1; + y1 = (clip->y1 < y1) ? clip->y1 : y1; + if ((x0 < x1) && (y0 < y1)) { + /* valid clip rectangles only */ + if (!plot->rectangle(x0, y0, x1, y1, + plot_style_bdr_in)) + return false; + } - if (!content_redraw(box->object, &obj_data, &r, ctx)) { - /* Show image fail */ - /* Unicode (U+FFFC) 'OBJECT REPLACEMENT CHARACTER' */ - const char *obj = "\xef\xbf\xbc"; - int obj_width; - int obj_x = x + padding_left; - if (!plot->rectangle(x + padding_left, - y + padding_top, - x + padding_left + width - 1, - y + padding_top + height - 1, - plot_style_broken_object)) + /* Second part */ + if (side == TOP || side == RIGHT) { + x0 = p[2]; y0 = p[3]; + x1 = (p[6] + p[4]) / 2; y1 = (p[7] + p[5]) / 2; + } else { + x0 = (p[6] + p[4]) / 2; y0 = (p[7] + p[5]) / 2; + x1 = p[2]; y1 = p[3]; + } + /* find intersection of clip rectangle and border */ + x0 = (clip->x0 > x0) ? clip->x0 : x0; + y0 = (clip->y0 > y0) ? clip->y0 : y0; + x1 = (clip->x1 < x1) ? clip->x1 : x1; + y1 = (clip->y1 < y1) ? clip->y1 : y1; + if ((x0 < x1) && (y0 < y1)) { + /* valid clip rectangles only */ + if (!plot->rectangle(x0, y0, x1, y1, + plot_style_bdr_out)) + return false; + } + } else if (thickness == 1) { + /* Border made up from one part which can be plotted + * as a rectangle */ + int x0, y0, x1, y1; + if (side == TOP || side == RIGHT) { + x0 = p[2]; y0 = p[3]; + x1 = p[6]; y1 = p[7]; + x1 = ((side == TOP) && (p[4] - p[6] != 0)) ? + x1 + p[4] - p[6] : x1; + /* find intersection of clip rectangle and + * border */ + x0 = (clip->x0 > x0) ? clip->x0 : x0; + y0 = (clip->y0 > y0) ? clip->y0 : y0; + x1 = (clip->x1 < x1) ? clip->x1 : x1; + y1 = (clip->y1 < y1) ? clip->y1 : y1; + if ((x0 < x1) && (y0 < y1)) { + /* valid clip rectangles only */ + if (!plot->rectangle(x0, y0, x1, y1, + plot_style_bdr_in)) + return false; + } + } else { + x0 = p[6]; y0 = p[7]; + x1 = p[2]; y1 = p[3]; + y1 = ((side == LEFT) && (p[1] - p[3] != 0)) ? + y1 + p[1] - p[3] : y1; + /* find intersection of clip rectangle and + * border */ + x0 = (clip->x0 > x0) ? clip->x0 : x0; + y0 = (clip->y0 > y0) ? clip->y0 : y0; + x1 = (clip->x1 < x1) ? clip->x1 : x1; + y1 = (clip->y1 < y1) ? clip->y1 : y1; + if ((x0 < x1) && (y0 < y1)) { + /* valid clip rectangles only */ + if (!plot->rectangle(x0, y0, x1, y1, + plot_style_bdr_out)) + return false; + } + } + } else { + /* Border made up from two parts and can't be plotted + * with rectangles */ + z[0] = p[0]; + z[1] = p[1]; + z[2] = (p[0] + p[2]) / 2; + z[3] = (p[1] + p[3]) / 2; + z[4] = (p[6] + p[4]) / 2; + z[5] = (p[7] + p[5]) / 2; + z[6] = p[6]; + z[7] = p[7]; + if (!plot->polygon(z, 4, plot_style_bdr_in)) return false; - if (!nsfont.font_width(plot_fstyle_broken_object, obj, - sizeof(obj) - 1, &obj_width)) - obj_x += 1; - else - obj_x += width / 2 - obj_width / 2; - - if (!plot->text(obj_x, y + padding_top + (int) - (height * 0.75), - obj, sizeof(obj) - 1, - plot_fstyle_broken_object)) + z[0] = p[2]; + z[1] = p[3]; + z[6] = p[4]; + z[7] = p[5]; + if (!plot->polygon(z, 4, plot_style_bdr_out)) return false; } - - - } else if (box->iframe) { - /* Offset is passed to browser window redraw unscaled */ - browser_window_redraw(box->iframe, - (x + padding_left) / scale, - (y + padding_top) / scale, &r, ctx); - - } else if (box->gadget && box->gadget->type == GADGET_CHECKBOX) { - if (!html_redraw_checkbox(x + padding_left, y + padding_top, - width, height, box->gadget->selected, ctx)) - return false; - - } else if (box->gadget && box->gadget->type == GADGET_RADIO) { - if (!html_redraw_radio(x + padding_left, y + padding_top, - width, height, box->gadget->selected, ctx)) - return false; - - } else if (box->gadget && box->gadget->type == GADGET_FILE) { - if (!html_redraw_file(x + padding_left, y + padding_top, - width, height, box, scale, - current_background_color, ctx)) - return false; - - } else if (box->text) { - if (!html_redraw_text_box(html, box, x, y, &r, scale, - current_background_color, ctx)) - return false; - - } else { - if (!html_redraw_box_children(html, box, x_parent, y_parent, &r, - scale, current_background_color, ctx)) - return false; - } - - if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK || - box->type == BOX_TABLE_CELL || box->type == BOX_INLINE) - if (!plot->clip(clip)) - return false; - - /* list marker */ - if (box->list_marker) - if (!html_redraw_box(html, box->list_marker, - x_parent + box->x - - scrollbar_get_offset(box->scroll_x), - y_parent + box->y - - scrollbar_get_offset(box->scroll_y), - clip, scale, current_background_color, ctx)) - return false; - - /* scrollbars */ - if (((box->style && box->type != BOX_BR && - box->type != BOX_TABLE && box->type != BOX_INLINE && - (css_computed_overflow(box->style) == - CSS_OVERFLOW_SCROLL || - css_computed_overflow(box->style) == - CSS_OVERFLOW_AUTO)) || (box->object && - content_get_type(box->object) == CONTENT_HTML)) && - box->parent != NULL) { - - has_x_scroll = box_hscrollbar_present(box); - has_y_scroll = box_vscrollbar_present(box); - - if (!box_handle_scrollbars((struct content *)html, - box, has_x_scroll, has_y_scroll)) - return false; - - if (box->scroll_x != NULL) - scrollbar_redraw(box->scroll_x, - x_parent + box->x, - y_parent + box->y + box->padding[TOP] + - box->height + box->padding[BOTTOM] - - SCROLLBAR_WIDTH, clip, scale, ctx); - if (box->scroll_y != NULL) - scrollbar_redraw(box->scroll_y, - x_parent + box->x + box->padding[LEFT] + - box->width + box->padding[RIGHT] - - SCROLLBAR_WIDTH, - y_parent + box->y, clip, scale, ctx); - } - - if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK || - box->type == BOX_TABLE_CELL || box->type == BOX_INLINE) - if (!plot->clip(clip)) - return false; - - return ((!plot->group_end) || (plot->group_end())); -} - + break; -/** - * Draw the various children of a box. - * - * \param html html content - * \param box box to draw children of - * \param x_parent coordinate of parent box - * \param y_parent coordinate of parent box - * \param clip clip rectangle - * \param scale scale for redraw - * \param current_background_color background colour under this box - * \param ctx current redraw context - * \return true if successful, false otherwise - */ + case CSS_BORDER_STYLE_INSET: + light = (light + 2) % 4; + /* fall through */ + case CSS_BORDER_STYLE_OUTSET: + /* choose correct colours for each part of the border line */ + switch (light) { + case 0: + plot_style_bdr_in = &plot_style_fillbdr_light; + plot_style_bdr_out = &plot_style_fillbdr_dlight; + break; + case 1: + plot_style_bdr_in = &plot_style_fillbdr_ddark; + plot_style_bdr_out = &plot_style_fillbdr_dark; + break; + case 2: + plot_style_bdr_in = &plot_style_fillbdr_dark; + plot_style_bdr_out = &plot_style_fillbdr_ddark; + break; + case 3: + plot_style_bdr_in = &plot_style_fillbdr_dlight; + plot_style_bdr_out = &plot_style_fillbdr_light; + break; + default: + plot_style_bdr_in = &plot_style_fillbdr; + plot_style_bdr_out = &plot_style_fillbdr; + break; + } -bool html_redraw_box_children(const html_content *html, struct box *box, - int x_parent, int y_parent, - const struct rect *clip, float scale, - colour current_background_color, - const struct redraw_context *ctx) -{ - struct box *c; + /* Render border */ + if ((rectangular || thickness == 2) && thickness != 1) { + /* Border made up from two parts and can be plotted + * with rectangles */ + int x0, y0, x1, y1; - for (c = box->children; c; c = c->next) { + /* First part */ + if (side == TOP || side == RIGHT) { + x0 = (p[0] + p[2]) / 2; y0 = (p[1] + p[3]) / 2; + x1 = p[6]; y1 = p[7]; + } else { + x0 = p[6]; y0 = p[7]; + x1 = (p[0] + p[2]) / 2; y1 = (p[1] + p[3]) / 2; + } + /* find intersection of clip rectangle and border */ + x0 = (clip->x0 > x0) ? clip->x0 : x0; + y0 = (clip->y0 > y0) ? clip->y0 : y0; + x1 = (clip->x1 < x1) ? clip->x1 : x1; + y1 = (clip->y1 < y1) ? clip->y1 : y1; + if ((x0 < x1) && (y0 < y1)) { + /* valid clip rectangles only */ + if (!plot->rectangle(x0, y0, x1, y1, + plot_style_bdr_in)) + return false; + } - if (c->type != BOX_FLOAT_LEFT && c->type != BOX_FLOAT_RIGHT) - if (!html_redraw_box(html, c, - x_parent + box->x - - scrollbar_get_offset(box->scroll_x), - y_parent + box->y - - scrollbar_get_offset(box->scroll_y), - clip, scale, current_background_color, - ctx)) + /* Second part */ + if (side == TOP || side == RIGHT) { + x0 = p[2]; y0 = p[3]; + x1 = (p[6] + p[4]) / 2; y1 = (p[7] + p[5]) / 2; + } else { + x0 = (p[6] + p[4]) / 2; y0 = (p[7] + p[5]) / 2; + x1 = p[2]; y1 = p[3]; + } + /* find intersection of clip rectangle and border */ + x0 = (clip->x0 > x0) ? clip->x0 : x0; + y0 = (clip->y0 > y0) ? clip->y0 : y0; + x1 = (clip->x1 < x1) ? clip->x1 : x1; + y1 = (clip->y1 < y1) ? clip->y1 : y1; + if ((x0 < x1) && (y0 < y1)) { + /* valid clip rectangles only */ + if (!plot->rectangle(x0, y0, x1, y1, + plot_style_bdr_out)) + return false; + } + } else if (thickness == 1) { + /* Border made up from one part which can be plotted + * as a rectangle */ + int x0, y0, x1, y1; + if (side == TOP || side == RIGHT) { + x0 = p[2]; y0 = p[3]; + x1 = p[6]; y1 = p[7]; + x1 = ((side == TOP) && (p[4] - p[6] != 0)) ? + x1 + p[4] - p[6] : x1; + /* find intersection of clip rectangle and + * border */ + x0 = (clip->x0 > x0) ? clip->x0 : x0; + y0 = (clip->y0 > y0) ? clip->y0 : y0; + x1 = (clip->x1 < x1) ? clip->x1 : x1; + y1 = (clip->y1 < y1) ? clip->y1 : y1; + if ((x0 < x1) && (y0 < y1)) { + /* valid clip rectangles only */ + if (!plot->rectangle(x0, y0, x1, y1, + plot_style_bdr_in)) + return false; + } + } else { + x0 = p[6]; y0 = p[7]; + x1 = p[2]; y1 = p[3]; + y1 = ((side == LEFT) && (p[1] - p[3] != 0)) ? + y1 + p[1] - p[3] : y1; + /* find intersection of clip rectangle and + * border */ + x0 = (clip->x0 > x0) ? clip->x0 : x0; + y0 = (clip->y0 > y0) ? clip->y0 : y0; + x1 = (clip->x1 < x1) ? clip->x1 : x1; + y1 = (clip->y1 < y1) ? clip->y1 : y1; + if ((x0 < x1) && (y0 < y1)) { + /* valid clip rectangles only */ + if (!plot->rectangle(x0, y0, x1, y1, + plot_style_bdr_out)) + return false; + } + } + } else { + /* Border made up from two parts and can't be plotted + * with rectangles */ + z[0] = p[0]; + z[1] = p[1]; + z[2] = (p[0] + p[2]) / 2; + z[3] = (p[1] + p[3]) / 2; + z[4] = (p[6] + p[4]) / 2; + z[5] = (p[7] + p[5]) / 2; + z[6] = p[6]; + z[7] = p[7]; + if (!plot->polygon(z, 4, plot_style_bdr_in)) + return false; + z[0] = p[2]; + z[1] = p[3]; + z[6] = p[4]; + z[7] = p[5]; + if (!plot->polygon(z, 4, plot_style_bdr_out)) return false; + } + break; } - for (c = box->float_children; c; c = c->next_float) - if (!html_redraw_box(html, c, - x_parent + box->x - - scrollbar_get_offset(box->scroll_x), - y_parent + box->y - - scrollbar_get_offset(box->scroll_y), - clip, scale, current_background_color, - ctx)) - return false; return true; } /** - * Redraw the text content of a box, possibly partially highlighted - * because the text has been selected, or matches a search operation. + * Draw borders for a box. * - * \param box box with text content - * \param x x co-ord of box - * \param y y co-ord of box - * \param clip current clip rectangle - * \param scale current scale setting (1.0 = 100%) - * \param current_background_color - * \param ctx current redraw context - * \return true iff successful and redraw should proceed + * \param box box to draw + * \param x_parent coordinate of left padding edge of parent of box + * \param y_parent coordinate of top padding edge of parent of box + * \param p_width width of padding box + * \param p_height height of padding box + * \param scale scale for redraw + * \param ctx current redraw context + * \return true if successful, false otherwise */ -bool html_redraw_text_box(const html_content *html, struct box *box, - int x, int y, const struct rect *clip, float scale, - colour current_background_color, +static bool html_redraw_borders(struct box *box, int x_parent, int y_parent, + int p_width, int p_height, const struct rect *clip, float scale, const struct redraw_context *ctx) { - bool excluded = (box->object != NULL); - plot_font_style_t fstyle; - - font_plot_style_from_css(box->style, &fstyle); - fstyle.background = current_background_color; - - if (!text_redraw(box->text, box->length, box->byte_offset, - box->space, &fstyle, x, y, - clip, box->height, scale, excluded, - (struct content *)html, &html->sel, - html->search, ctx)) - return false; - - return true; -} + unsigned int sides[] = { LEFT, RIGHT, TOP, BOTTOM }; + int top = box->border[TOP].width; + int right = box->border[RIGHT].width; + int bottom = box->border[BOTTOM].width; + int left = box->border[LEFT].width; + int x, y; + unsigned int i, side; + int p[8]; /* Box border vertices */ + int z[8]; /* Border vertices */ + bool square_end_1 = false; + bool square_end_2 = false; -/** - * Redraw a short text string, complete with highlighting - * (for selection/search) - * - * \param utf8_text pointer to UTF-8 text string - * \param utf8_len length of string, in bytes - * \param offset byte offset within textual representation - * \param space width of space that follows string (0 = no space) - * \param fstyle text style to use (pass text size unscaled) - * \param x x ordinate at which to plot text - * \param y y ordinate at which to plot text - * \param clip pointer to current clip rectangle - * \param height height of text string - * \param scale current display scale (1.0 = 100%) - * \param excluded exclude this text string from the selection - * \param ctx current redraw context - * \return true iff successful and redraw should proceed - */ + x = x_parent + box->x; + y = y_parent + box->y; -bool text_redraw(const char *utf8_text, size_t utf8_len, - size_t offset, int space, const plot_font_style_t *fstyle, - int x, int y, const struct rect *clip, int height, - float scale, bool excluded, struct content *c, - const struct selection *sel, struct search_context *search, - const struct redraw_context *ctx) -{ - const struct plotter_table *plot = ctx->plot; - bool highlighted = false; - plot_font_style_t plot_fstyle = *fstyle; + if (scale != 1.0) { + top *= scale; + right *= scale; + bottom *= scale; + left *= scale; + x *= scale; + y *= scale; + } - /* Need scaled text size to pass to plotters */ - plot_fstyle.size *= scale; + assert(box->style); - /* is this box part of a selection? */ - if (!excluded && ctx->interactive == true) { - unsigned len = utf8_len + (space ? 1 : 0); - unsigned start_idx; - unsigned end_idx; + /* Calculate border vertices + * + * A----------------------+ + * | \ / | + * | B--------------+ | + * | | | | + * | +--------------C | + * | / \ | + * +----------------------D + */ + p[0] = x - left; p[1] = y - top; /* A */ + p[2] = x; p[3] = y; /* B */ + p[4] = x + p_width; p[5] = y + p_height; /* C */ + p[6] = x + p_width + right; p[7] = y + p_height + bottom; /* D */ - /* first try the browser window's current selection */ - if (selection_defined(sel) && selection_highlighted(sel, - offset, offset + len, - &start_idx, &end_idx)) { - highlighted = true; - } + for (i = 0; i != 4; i++) { + colour col = 0; + side = sides[i]; /* plot order */ - /* what about the current search operation, if any? */ - if (!highlighted && (search != NULL) && - search_term_highlighted(c, - offset, offset + len, - &start_idx, &end_idx, - search)) { - highlighted = true; - } + if (box->border[side].width == 0 || + nscss_color_is_transparent(box->border[side].c)) + continue; - /* \todo make search terms visible within selected text */ - if (highlighted) { - struct rect r; - unsigned endtxt_idx = end_idx; - bool clip_changed = false; - bool text_visible = true; - int startx, endx; - plot_style_t *pstyle_fill_hback = plot_style_fill_white; - plot_font_style_t fstyle_hback = plot_fstyle; + switch (side) { + case LEFT: + square_end_1 = (top == 0); + square_end_2 = (bottom == 0); - if (end_idx > utf8_len) { - /* adjust for trailing space, not present in - * utf8_text */ - assert(end_idx == utf8_len + 1); - endtxt_idx = utf8_len; + z[0] = p[0]; z[1] = p[7]; + z[2] = p[2]; z[3] = p[5]; + z[4] = p[2]; z[5] = p[3]; + z[6] = p[0]; z[7] = p[1]; + + if (nscss_color_is_transparent(box->border[TOP].c) == + false && + box->border[TOP].style != + CSS_BORDER_STYLE_DOUBLE) { + /* make border overhang top corner fully, + * if top border is opaque */ + z[5] -= top; + square_end_1 = true; + } + if (nscss_color_is_transparent(box->border[BOTTOM].c) == + false && + box->border[BOTTOM].style != + CSS_BORDER_STYLE_DOUBLE) { + /* make border overhang bottom corner fully, + * if bottom border is opaque */ + z[3] += bottom; + square_end_2 = true; } - if (!nsfont.font_width(fstyle, utf8_text, start_idx, - &startx)) - startx = 0; + col = nscss_color_to_ns(box->border[side].c); - if (!nsfont.font_width(fstyle, utf8_text, endtxt_idx, - &endx)) - endx = 0; + if (!html_redraw_border_plot(side, z, col, + box->border[side].style, + box->border[side].width * scale, + square_end_1 && square_end_2, + clip, ctx)) + return false; + break; + case RIGHT: + square_end_1 = (top == 0); + square_end_2 = (bottom == 0); - /* is there a trailing space that should be highlighted - * as well? */ - if (end_idx > utf8_len) { - endx += space; - } + z[0] = p[6]; z[1] = p[1]; + z[2] = p[4]; z[3] = p[3]; + z[4] = p[4]; z[5] = p[5]; + z[6] = p[6]; z[7] = p[7]; - if (scale != 1.0) { - startx *= scale; - endx *= scale; + if (nscss_color_is_transparent(box->border[TOP].c) == + false && + box->border[TOP].style != + CSS_BORDER_STYLE_DOUBLE) { + /* make border overhang top corner fully, + * if top border is opaque */ + z[3] -= top; + square_end_1 = true; + } + if (nscss_color_is_transparent(box->border[BOTTOM].c) == + false && + box->border[BOTTOM].style != + CSS_BORDER_STYLE_DOUBLE) { + /* make border overhang bottom corner fully, + * if bottom border is opaque */ + z[5] += bottom; + square_end_2 = true; } - /* draw any text preceding highlighted portion */ - if (start_idx > 0 && - !plot->text(x, y + (int)(height * 0.75 * scale), - utf8_text, start_idx, - &plot_fstyle)) - return false; - - /* decide whether highlighted portion is to be - * white-on-black or black-on-white */ - if ((fstyle->background & 0x808080) == 0x808080) - pstyle_fill_hback = plot_style_fill_black; + col = nscss_color_to_ns(box->border[side].c); - /* highlighted portion */ - if (!plot->rectangle(x + startx, y, x + endx, - y + height * scale, - pstyle_fill_hback)) + if (!html_redraw_border_plot(side, z, col, + box->border[side].style, + box->border[side].width * scale, + square_end_1 && square_end_2, + clip, ctx)) return false; + break; + case TOP: + if (clip->y0 > p[3]) + /* clip rectangle is below border; nothing to + * plot */ + continue; - if (start_idx > 0) { - int px0 = max(x + startx, clip->x0); - int px1 = min(x + endx, clip->x1); + square_end_1 = (left == 0); + square_end_2 = (right == 0); - if (px0 < px1) { - r.x0 = px0; - r.y0 = clip->y0; - r.x1 = px1; - r.y1 = clip->y1; - if (!plot->clip(&r)) - return false; - clip_changed = true; - } else { - text_visible = false; - } + z[0] = p[2]; z[1] = p[3]; + z[2] = p[0]; z[3] = p[1]; + z[4] = p[6]; z[5] = p[1]; + z[6] = p[4]; z[7] = p[3]; + + if (box->border[TOP].style == + CSS_BORDER_STYLE_SOLID && + box->border[TOP].c == + box->border[LEFT].c) { + /* don't bother overlapping left corner if + * it's the same colour anyway */ + z[2] += left; + square_end_1 = true; + } + if (box->border[TOP].style == + CSS_BORDER_STYLE_SOLID && + box->border[TOP].c == + box->border[RIGHT].c) { + /* don't bother overlapping right corner if + * it's the same colour anyway */ + z[4] -= right; + square_end_2 = true; } - fstyle_hback.background = - pstyle_fill_hback->fill_colour; - fstyle_hback.foreground = - pstyle_fill_hback->fill_colour ^ 0xffffff; + col = nscss_color_to_ns(box->border[side].c); - if (text_visible && - !plot->text(x, y + (int)(height * 0.75 * scale), - utf8_text, endtxt_idx, - &fstyle_hback)) + if (!html_redraw_border_plot(side, z, col, + box->border[side].style, + box->border[side].width * scale, + square_end_1 && square_end_2, + clip, ctx)) return false; + break; + case BOTTOM: + if (clip->y1 < p[5]) + /* clip rectangle is above border; nothing to + * plot */ + continue; - /* draw any text succeeding highlighted portion */ - if (endtxt_idx < utf8_len) { - int px0 = max(x + endx, clip->x0); - if (px0 < clip->x1) { - - r.x0 = px0; - r.y0 = clip->y0; - r.x1 = clip->x1; - r.y1 = clip->y1; - if (!plot->clip(&r)) - return false; + square_end_1 = (left == 0); + square_end_2 = (right == 0); - clip_changed = true; + z[0] = p[4]; z[1] = p[5]; + z[2] = p[6]; z[3] = p[7]; + z[4] = p[0]; z[5] = p[7]; + z[6] = p[2]; z[7] = p[5]; - if (!plot->text(x, y + (int) - (height * 0.75 * scale), - utf8_text, utf8_len, - &plot_fstyle)) - return false; - } + if (box->border[BOTTOM].style == + CSS_BORDER_STYLE_SOLID && + box->border[BOTTOM].c == + box->border[LEFT].c) { + /* don't bother overlapping left corner if + * it's the same colour anyway */ + z[4] += left; + square_end_1 = true; + } + if (box->border[BOTTOM].style == + CSS_BORDER_STYLE_SOLID && + box->border[BOTTOM].c == + box->border[RIGHT].c) { + /* don't bother overlapping right corner if + * it's the same colour anyway */ + z[2] -= right; + square_end_2 = true; } - if (clip_changed && - !plot->clip(clip)) - return false; - } - } + col = nscss_color_to_ns(box->border[side].c); - if (!highlighted) { - if (!plot->text(x, y + (int) (height * 0.75 * scale), - utf8_text, utf8_len, - &plot_fstyle)) - return false; + if (!html_redraw_border_plot(side, z, col, + box->border[side].style, + box->border[side].width * scale, + square_end_1 && square_end_2, + clip, ctx)) + return false; + break; + default: + assert(side == TOP || side == BOTTOM || + side == LEFT || side == RIGHT); + break; + } } + return true; } /** - * Draw borders for a box. + * Draw an inline's borders. * - * \param box box to draw - * \param x_parent coordinate of left padding edge of parent of box - * \param y_parent coordinate of top padding edge of parent of box - * \param p_width width of padding box - * \param p_height height of padding box - * \param scale scale for redraw - * \param ctx current redraw context + * \param box BOX_INLINE which created the border + * \param b coordinates of border edge rectangle + * \param scale scale for redraw + * \param first true if this is the first rectangle associated with the inline + * \param last true if this is the last rectangle associated with the inline + * \param ctx current redraw context * \return true if successful, false otherwise */ -bool html_redraw_borders(struct box *box, int x_parent, int y_parent, - int p_width, int p_height, const struct rect *clip, float scale, +static bool html_redraw_inline_borders(struct box *box, struct rect b, + const struct rect *clip, float scale, bool first, bool last, const struct redraw_context *ctx) { - unsigned int sides[] = { LEFT, RIGHT, TOP, BOTTOM }; int top = box->border[TOP].width; int right = box->border[RIGHT].width; int bottom = box->border[BOTTOM].width; int left = box->border[LEFT].width; - int x, y; - unsigned int i, side; + colour col; int p[8]; /* Box border vertices */ int z[8]; /* Border vertices */ - bool square_end_1 = false; - bool square_end_2 = false; - - x = x_parent + box->x; - y = y_parent + box->y; + bool square_end_1; + bool square_end_2; if (scale != 1.0) { top *= scale; right *= scale; bottom *= scale; left *= scale; - x *= scale; - y *= scale; } - assert(box->style); - /* Calculate border vertices * * A----------------------+ @@ -1138,771 +956,689 @@ bool html_redraw_borders(struct box *box, int x_parent, int y_parent, * | / \ | * +----------------------D */ - p[0] = x - left; p[1] = y - top; /* A */ - p[2] = x; p[3] = y; /* B */ - p[4] = x + p_width; p[5] = y + p_height; /* C */ - p[6] = x + p_width + right; p[7] = y + p_height + bottom; /* D */ + p[0] = b.x0; p[1] = b.y0; /* A */ + p[2] = first ? b.x0 + left : b.x0; p[3] = b.y0 + top; /* B */ + p[4] = last ? b.x1 - right : b.x1; p[5] = b.y1 - bottom; /* C */ + p[6] = b.x1; p[7] = b.y1; /* D */ - for (i = 0; i != 4; i++) { - colour col = 0; - side = sides[i]; /* plot order */ + assert(box->style); - if (box->border[side].width == 0 || - nscss_color_is_transparent(box->border[side].c)) - continue; + /* Left */ + square_end_1 = (top == 0); + square_end_2 = (bottom == 0); + if (left != 0 && first && nscss_color_is_transparent( + box->border[LEFT].c) == false) { + col = nscss_color_to_ns(box->border[LEFT].c); - switch (side) { - case LEFT: - square_end_1 = (top == 0); - square_end_2 = (bottom == 0); + z[0] = p[0]; z[1] = p[7]; + z[2] = p[2]; z[3] = p[5]; + z[4] = p[2]; z[5] = p[3]; + z[6] = p[0]; z[7] = p[1]; - z[0] = p[0]; z[1] = p[7]; - z[2] = p[2]; z[3] = p[5]; - z[4] = p[2]; z[5] = p[3]; - z[6] = p[0]; z[7] = p[1]; + if (nscss_color_is_transparent(box->border[TOP].c) == false && + box->border[TOP].style != + CSS_BORDER_STYLE_DOUBLE) { + /* make border overhang top corner fully, + * if top border is opaque */ + z[5] -= top; + square_end_1 = true; + } - if (nscss_color_is_transparent(box->border[TOP].c) == - false && - box->border[TOP].style != - CSS_BORDER_STYLE_DOUBLE) { - /* make border overhang top corner fully, - * if top border is opaque */ - z[5] -= top; - square_end_1 = true; - } - if (nscss_color_is_transparent(box->border[BOTTOM].c) == - false && - box->border[BOTTOM].style != - CSS_BORDER_STYLE_DOUBLE) { - /* make border overhang bottom corner fully, - * if bottom border is opaque */ - z[3] += bottom; - square_end_2 = true; - } + if (nscss_color_is_transparent(box->border[BOTTOM].c) == + false && + box->border[BOTTOM].style != + CSS_BORDER_STYLE_DOUBLE) { + /* make border overhang bottom corner fully, + * if bottom border is opaque */ + z[3] += bottom; + square_end_2 = true; + } - col = nscss_color_to_ns(box->border[side].c); + if (!html_redraw_border_plot(LEFT, z, col, + box->border[LEFT].style, + left, square_end_1 && square_end_2, + clip, ctx)) + return false; + } - if (!html_redraw_border_plot(side, z, col, - box->border[side].style, - box->border[side].width * scale, - square_end_1 && square_end_2, - clip, ctx)) - return false; - break; - case RIGHT: - square_end_1 = (top == 0); - square_end_2 = (bottom == 0); + /* Right */ + square_end_1 = (top == 0); + square_end_2 = (bottom == 0); + if (right != 0 && last && nscss_color_is_transparent( + box->border[RIGHT].c) == false) { + col = nscss_color_to_ns(box->border[RIGHT].c); - z[0] = p[6]; z[1] = p[1]; - z[2] = p[4]; z[3] = p[3]; - z[4] = p[4]; z[5] = p[5]; - z[6] = p[6]; z[7] = p[7]; + z[0] = p[6]; z[1] = p[1]; + z[2] = p[4]; z[3] = p[3]; + z[4] = p[4]; z[5] = p[5]; + z[6] = p[6]; z[7] = p[7]; - if (nscss_color_is_transparent(box->border[TOP].c) == - false && - box->border[TOP].style != - CSS_BORDER_STYLE_DOUBLE) { - /* make border overhang top corner fully, - * if top border is opaque */ - z[3] -= top; - square_end_1 = true; - } - if (nscss_color_is_transparent(box->border[BOTTOM].c) == - false && - box->border[BOTTOM].style != - CSS_BORDER_STYLE_DOUBLE) { - /* make border overhang bottom corner fully, - * if bottom border is opaque */ - z[5] += bottom; - square_end_2 = true; - } + if (nscss_color_is_transparent(box->border[TOP].c) == false && + box->border[TOP].style != + CSS_BORDER_STYLE_DOUBLE) { + /* make border overhang top corner fully, + * if top border is opaque */ + z[3] -= top; + square_end_1 = true; + } - col = nscss_color_to_ns(box->border[side].c); + if (nscss_color_is_transparent(box->border[BOTTOM].c) == + false && + box->border[BOTTOM].style != + CSS_BORDER_STYLE_DOUBLE) { + /* make border overhang bottom corner fully, + * if bottom border is opaque */ + z[5] += bottom; + square_end_2 = true; + } - if (!html_redraw_border_plot(side, z, col, - box->border[side].style, - box->border[side].width * scale, - square_end_1 && square_end_2, - clip, ctx)) - return false; - break; - case TOP: - if (clip->y0 > p[3]) - /* clip rectangle is below border; nothing to - * plot */ - continue; + if (!html_redraw_border_plot(RIGHT, z, col, + box->border[RIGHT].style, + right, square_end_1 && square_end_2, + clip, ctx)) + return false; + } - square_end_1 = (left == 0); - square_end_2 = (right == 0); + /* Top */ + square_end_1 = (left == 0); + square_end_2 = (right == 0); + if (top != 0 && nscss_color_is_transparent( + box->border[TOP].c) == false) { + col = nscss_color_to_ns(box->border[TOP].c); - z[0] = p[2]; z[1] = p[3]; - z[2] = p[0]; z[3] = p[1]; - z[4] = p[6]; z[5] = p[1]; - z[6] = p[4]; z[7] = p[3]; + z[0] = p[2]; z[1] = p[3]; + z[2] = p[0]; z[3] = p[1]; + z[4] = p[6]; z[5] = p[1]; + z[6] = p[4]; z[7] = p[3]; + + if (first && box->border[TOP].style == + CSS_BORDER_STYLE_SOLID && + box->border[TOP].c == + box->border[LEFT].c) { + /* don't bother overlapping left corner if + * it's the same colour anyway */ + z[2] += left; + square_end_1 = true; + } + + if (last && box->border[TOP].style == + CSS_BORDER_STYLE_SOLID && + box->border[TOP].c == + box->border[RIGHT].c) { + /* don't bother overlapping right corner if + * it's the same colour anyway */ + z[4] -= right; + square_end_2 = true; + } + + if (!html_redraw_border_plot(TOP, z, col, + box->border[TOP].style, + top, square_end_1 && square_end_2, + clip, ctx)) + return false; + } + + /* Bottom */ + square_end_1 = (left == 0); + square_end_2 = (right == 0); + if (bottom != 0 && nscss_color_is_transparent( + box->border[BOTTOM].c) == false) { + col = nscss_color_to_ns(box->border[BOTTOM].c); + + z[0] = p[4]; z[1] = p[5]; + z[2] = p[6]; z[3] = p[7]; + z[4] = p[0]; z[5] = p[7]; + z[6] = p[2]; z[7] = p[5]; + + if (first && box->border[BOTTOM].style == + CSS_BORDER_STYLE_SOLID && + box->border[BOTTOM].c == + box->border[LEFT].c) { + /* don't bother overlapping left corner if + * it's the same colour anyway */ + z[4] += left; + square_end_1 = true; + } + + if (last && box->border[BOTTOM].style == + CSS_BORDER_STYLE_SOLID && + box->border[BOTTOM].c == + box->border[RIGHT].c) { + /* don't bother overlapping right corner if + * it's the same colour anyway */ + z[2] -= right; + square_end_2 = true; + } - if (box->border[TOP].style == - CSS_BORDER_STYLE_SOLID && - box->border[TOP].c == - box->border[LEFT].c) { - /* don't bother overlapping left corner if - * it's the same colour anyway */ - z[2] += left; - square_end_1 = true; - } - if (box->border[TOP].style == - CSS_BORDER_STYLE_SOLID && - box->border[TOP].c == - box->border[RIGHT].c) { - /* don't bother overlapping right corner if - * it's the same colour anyway */ - z[4] -= right; - square_end_2 = true; - } + if (!html_redraw_border_plot(BOTTOM, z, col, + box->border[BOTTOM].style, + bottom, square_end_1 && square_end_2, + clip, ctx)) + return false; + } - col = nscss_color_to_ns(box->border[side].c); + return true; +} - if (!html_redraw_border_plot(side, z, col, - box->border[side].style, - box->border[side].width * scale, - square_end_1 && square_end_2, - clip, ctx)) - return false; - break; - case BOTTOM: - if (clip->y1 < p[5]) - /* clip rectangle is above border; nothing to - * plot */ - continue; - square_end_1 = (left == 0); - square_end_2 = (right == 0); +/** + * Plot a checkbox. + * + * \param x left coordinate + * \param y top coordinate + * \param width dimensions of checkbox + * \param height dimensions of checkbox + * \param selected the checkbox is selected + * \param ctx current redraw context + * \return true if successful, false otherwise + */ - z[0] = p[4]; z[1] = p[5]; - z[2] = p[6]; z[3] = p[7]; - z[4] = p[0]; z[5] = p[7]; - z[6] = p[2]; z[7] = p[5]; +static bool html_redraw_checkbox(int x, int y, int width, int height, + bool selected, const struct redraw_context *ctx) +{ + const struct plotter_table *plot = ctx->plot; + double z = width * 0.15; + if (z == 0) + z = 1; - if (box->border[BOTTOM].style == - CSS_BORDER_STYLE_SOLID && - box->border[BOTTOM].c == - box->border[LEFT].c) { - /* don't bother overlapping left corner if - * it's the same colour anyway */ - z[4] += left; - square_end_1 = true; - } - if (box->border[BOTTOM].style == - CSS_BORDER_STYLE_SOLID && - box->border[BOTTOM].c == - box->border[RIGHT].c) { - /* don't bother overlapping right corner if - * it's the same colour anyway */ - z[2] -= right; - square_end_2 = true; - } + if (!(plot->rectangle(x, y, x + width, y + height, + plot_style_fill_wbasec) && + plot->line(x, y, x + width, y, plot_style_stroke_darkwbasec) && + plot->line(x, y, x, y + height, plot_style_stroke_darkwbasec) && + plot->line(x + width, y, x + width, y + height, + plot_style_stroke_lightwbasec) && + plot->line(x, y + height, x + width, y + height, + plot_style_stroke_lightwbasec))) + return false; - col = nscss_color_to_ns(box->border[side].c); + if (selected) { + if (width < 12 || height < 12) { + /* render a solid box instead of a tick */ + if (!plot->rectangle(x + z + z, y + z + z, + x + width - z, y + height - z, + plot_style_fill_wblobc)) + return false; + } else { + /* render a tick, as it'll fit comfortably */ + if (!(plot->line(x + width - z, + y + z, + x + (z * 3), + y + height - z, + plot_style_stroke_wblobc) && - if (!html_redraw_border_plot(side, z, col, - box->border[side].style, - box->border[side].width * scale, - square_end_1 && square_end_2, - clip, ctx)) + plot->line(x + (z * 3), + y + height - z, + x + z + z, + y + (height / 2), + plot_style_stroke_wblobc))) return false; - break; - default: - assert(side == TOP || side == BOTTOM || - side == LEFT || side == RIGHT); - break; } } - return true; } /** - * Draw an inline's borders. + * Plot a radio icon. * - * \param box BOX_INLINE which created the border - * \param b coordinates of border edge rectangle - * \param scale scale for redraw - * \param first true if this is the first rectangle associated with the inline - * \param last true if this is the last rectangle associated with the inline - * \param ctx current redraw context + * \param x left coordinate + * \param y top coordinate + * \param width dimensions of radio icon + * \param height dimensions of radio icon + * \param selected the radio icon is selected + * \param ctx current redraw context * \return true if successful, false otherwise */ - -bool html_redraw_inline_borders(struct box *box, struct rect b, - const struct rect *clip, float scale, bool first, bool last, - const struct redraw_context *ctx) +static bool html_redraw_radio(int x, int y, int width, int height, + bool selected, const struct redraw_context *ctx) { - int top = box->border[TOP].width; - int right = box->border[RIGHT].width; - int bottom = box->border[BOTTOM].width; - int left = box->border[LEFT].width; - colour col; - int p[8]; /* Box border vertices */ - int z[8]; /* Border vertices */ - bool square_end_1; - bool square_end_2; + const struct plotter_table *plot = ctx->plot; - if (scale != 1.0) { - top *= scale; - right *= scale; - bottom *= scale; - left *= scale; - } + /* plot background of radio button */ + if (!plot->disc(x + width * 0.5, + y + height * 0.5, + width * 0.5 - 1, + plot_style_fill_wbasec)) + return false; - /* Calculate border vertices - * - * A----------------------+ - * | \ / | - * | B--------------+ | - * | | | | - * | +--------------C | - * | / \ | - * +----------------------D - */ - p[0] = b.x0; p[1] = b.y0; /* A */ - p[2] = first ? b.x0 + left : b.x0; p[3] = b.y0 + top; /* B */ - p[4] = last ? b.x1 - right : b.x1; p[5] = b.y1 - bottom; /* C */ - p[6] = b.x1; p[7] = b.y1; /* D */ + /* plot dark arc */ + if (!plot->arc(x + width * 0.5, + y + height * 0.5, + width * 0.5 - 1, + 45, + 225, + plot_style_fill_darkwbasec)) + return false; - assert(box->style); + /* plot light arc */ + if (!plot->arc(x + width * 0.5, + y + height * 0.5, + width * 0.5 - 1, + 225, + 45, + plot_style_fill_lightwbasec)) + return false; - /* Left */ - square_end_1 = (top == 0); - square_end_2 = (bottom == 0); - if (left != 0 && first && nscss_color_is_transparent( - box->border[LEFT].c) == false) { - col = nscss_color_to_ns(box->border[LEFT].c); + if (selected) { + /* plot selection blob */ + if (!plot->disc(x + width * 0.5, + y + height * 0.5, + width * 0.3 - 1, + plot_style_fill_wblobc)) + return false; + } - z[0] = p[0]; z[1] = p[7]; - z[2] = p[2]; z[3] = p[5]; - z[4] = p[2]; z[5] = p[3]; - z[6] = p[0]; z[7] = p[1]; + return true; +} - if (nscss_color_is_transparent(box->border[TOP].c) == false && - box->border[TOP].style != - CSS_BORDER_STYLE_DOUBLE) { - /* make border overhang top corner fully, - * if top border is opaque */ - z[5] -= top; - square_end_1 = true; - } - if (nscss_color_is_transparent(box->border[BOTTOM].c) == - false && - box->border[BOTTOM].style != - CSS_BORDER_STYLE_DOUBLE) { - /* make border overhang bottom corner fully, - * if bottom border is opaque */ - z[3] += bottom; - square_end_2 = true; - } +/** + * Plot a file upload input. + * + * \param x left coordinate + * \param y top coordinate + * \param width dimensions of input + * \param height dimensions of input + * \param box box of input + * \param scale scale for redraw + * \param background_colour current background colour + * \param ctx current redraw context + * \return true if successful, false otherwise + */ + +static bool html_redraw_file(int x, int y, int width, int height, + struct box *box, float scale, colour background_colour, + const struct redraw_context *ctx) +{ + int text_width; + const char *text; + size_t length; + plot_font_style_t fstyle; - if (!html_redraw_border_plot(LEFT, z, col, - box->border[LEFT].style, - left, square_end_1 && square_end_2, - clip, ctx)) - return false; - } + font_plot_style_from_css(box->style, &fstyle); + fstyle.background = background_colour; - /* Right */ - square_end_1 = (top == 0); - square_end_2 = (bottom == 0); - if (right != 0 && last && nscss_color_is_transparent( - box->border[RIGHT].c) == false) { - col = nscss_color_to_ns(box->border[RIGHT].c); + if (box->gadget->value) + text = box->gadget->value; + else + text = messages_get("Form_Drop"); + length = strlen(text); - z[0] = p[6]; z[1] = p[1]; - z[2] = p[4]; z[3] = p[3]; - z[4] = p[4]; z[5] = p[5]; - z[6] = p[6]; z[7] = p[7]; + if (!nsfont.font_width(&fstyle, text, length, &text_width)) + return false; + text_width *= scale; + if (width < text_width + 8) + x = x + width - text_width - 4; + else + x = x + 4; - if (nscss_color_is_transparent(box->border[TOP].c) == false && - box->border[TOP].style != - CSS_BORDER_STYLE_DOUBLE) { - /* make border overhang top corner fully, - * if top border is opaque */ - z[3] -= top; - square_end_1 = true; - } + return ctx->plot->text(x, y + height * 0.75, text, length, &fstyle); +} - if (nscss_color_is_transparent(box->border[BOTTOM].c) == - false && - box->border[BOTTOM].style != - CSS_BORDER_STYLE_DOUBLE) { - /* make border overhang bottom corner fully, - * if bottom border is opaque */ - z[5] += bottom; - square_end_2 = true; - } - if (!html_redraw_border_plot(RIGHT, z, col, - box->border[RIGHT].style, - right, square_end_1 && square_end_2, - clip, ctx)) - return false; - } +/** + * Plot background images. + * + * \param x coordinate of box + * \param y coordinate of box + * \param box box to draw background image of + * \param scale scale for redraw + * \param clip current clip rectangle + * \param background_colour current background colour + * \param background box containing background details (usually ::box) + * \param ctx current redraw context + * \return true if successful, false otherwise + * + * The reason for the presence of ::background is the backwards compatibility + * mess that is backgrounds on . The background will be drawn relative + * to ::box, using the background information contained within ::background. + */ - /* Top */ - square_end_1 = (left == 0); - square_end_2 = (right == 0); - if (top != 0 && nscss_color_is_transparent( - box->border[TOP].c) == false) { - col = nscss_color_to_ns(box->border[TOP].c); +static bool html_redraw_background(int x, int y, struct box *box, float scale, + const struct rect *clip, colour *background_colour, + struct box *background, const struct redraw_context *ctx) +{ + const struct plotter_table *plot = ctx->plot; + bool repeat_x = false; + bool repeat_y = false; + bool plot_colour = true; + bool plot_content; + bool clip_to_children = false; + struct box *clip_box = box; + int ox = x, oy = y; + int width, height; + css_fixed hpos = 0, vpos = 0; + css_unit hunit = CSS_UNIT_PX, vunit = CSS_UNIT_PX; + struct box *parent; + struct rect r = *clip; + css_color bgcol; + plot_style_t pstyle_fill_bg = { + .fill_type = PLOT_OP_TYPE_SOLID, + .fill_colour = *background_colour, + }; - z[0] = p[2]; z[1] = p[3]; - z[2] = p[0]; z[3] = p[1]; - z[4] = p[6]; z[5] = p[1]; - z[6] = p[4]; z[7] = p[3]; + if (ctx->background_images == false) + return true; - if (first && box->border[TOP].style == - CSS_BORDER_STYLE_SOLID && - box->border[TOP].c == - box->border[LEFT].c) { - /* don't bother overlapping left corner if - * it's the same colour anyway */ - z[2] += left; - square_end_1 = true; - } + plot_content = (background->background != NULL); - if (last && box->border[TOP].style == - CSS_BORDER_STYLE_SOLID && - box->border[TOP].c == - box->border[RIGHT].c) { - /* don't bother overlapping right corner if - * it's the same colour anyway */ - z[4] -= right; - square_end_2 = true; + if (plot_content) { + if (!box->parent) { + /* Root element, special case: + * background origin calc. is based on margin box */ + x -= box->margin[LEFT] * scale; + y -= box->margin[TOP] * scale; + width = box->margin[LEFT] + box->padding[LEFT] + + box->width + box->padding[RIGHT] + + box->margin[RIGHT]; + height = box->margin[TOP] + box->padding[TOP] + + box->height + box->padding[BOTTOM] + + box->margin[BOTTOM]; + } else { + width = box->padding[LEFT] + box->width + + box->padding[RIGHT]; + height = box->padding[TOP] + box->height + + box->padding[BOTTOM]; } + /* handle background-repeat */ + switch (css_computed_background_repeat(background->style)) { + case CSS_BACKGROUND_REPEAT_REPEAT: + repeat_x = repeat_y = true; + /* optimisation: only plot the colour if + * bitmap is not opaque */ + plot_colour = !content_get_opaque(background->background); + break; - if (!html_redraw_border_plot(TOP, z, col, - box->border[TOP].style, - top, square_end_1 && square_end_2, - clip, ctx)) - return false; - } + case CSS_BACKGROUND_REPEAT_REPEAT_X: + repeat_x = true; + break; - /* Bottom */ - square_end_1 = (left == 0); - square_end_2 = (right == 0); - if (bottom != 0 && nscss_color_is_transparent( - box->border[BOTTOM].c) == false) { - col = nscss_color_to_ns(box->border[BOTTOM].c); + case CSS_BACKGROUND_REPEAT_REPEAT_Y: + repeat_y = true; + break; - z[0] = p[4]; z[1] = p[5]; - z[2] = p[6]; z[3] = p[7]; - z[4] = p[0]; z[5] = p[7]; - z[6] = p[2]; z[7] = p[5]; + case CSS_BACKGROUND_REPEAT_NO_REPEAT: + break; - if (first && box->border[BOTTOM].style == - CSS_BORDER_STYLE_SOLID && - box->border[BOTTOM].c == - box->border[LEFT].c) { - /* don't bother overlapping left corner if - * it's the same colour anyway */ - z[4] += left; - square_end_1 = true; + default: + break; } - if (last && box->border[BOTTOM].style == - CSS_BORDER_STYLE_SOLID && - box->border[BOTTOM].c == - box->border[RIGHT].c) { - /* don't bother overlapping right corner if - * it's the same colour anyway */ - z[2] -= right; - square_end_2 = true; + /* handle background-position */ + css_computed_background_position(background->style, + &hpos, &hunit, &vpos, &vunit); + if (hunit == CSS_UNIT_PCT) { + x += (width - + content_get_width(background->background)) * + scale * FIXTOFLT(hpos) / 100.; + } else { + x += (int) (FIXTOFLT(nscss_len2px(hpos, hunit, + background->style)) * scale); } - if (!html_redraw_border_plot(BOTTOM, z, col, - box->border[BOTTOM].style, - bottom, square_end_1 && square_end_2, - clip, ctx)) - return false; + if (vunit == CSS_UNIT_PCT) { + y += (height - + content_get_height(background->background)) * + scale * FIXTOFLT(vpos) / 100.; + } else { + y += (int) (FIXTOFLT(nscss_len2px(vpos, vunit, + background->style)) * scale); + } } - return true; -} + /* special case for table rows as their background needs + * to be clipped to all the cells */ + if (box->type == BOX_TABLE_ROW) { + css_fixed h = 0, v = 0; + css_unit hu = CSS_UNIT_PX, vu = CSS_UNIT_PX; -static plot_style_t plot_style_bdr = { - .stroke_type = PLOT_OP_TYPE_DASH, -}; -static plot_style_t plot_style_fillbdr = { - .fill_type = PLOT_OP_TYPE_SOLID, -}; -static plot_style_t plot_style_fillbdr_dark = { - .fill_type = PLOT_OP_TYPE_SOLID, -}; -static plot_style_t plot_style_fillbdr_light = { - .fill_type = PLOT_OP_TYPE_SOLID, -}; -static plot_style_t plot_style_fillbdr_ddark = { - .fill_type = PLOT_OP_TYPE_SOLID, -}; -static plot_style_t plot_style_fillbdr_dlight = { - .fill_type = PLOT_OP_TYPE_SOLID, -}; + for (parent = box->parent; + ((parent) && (parent->type != BOX_TABLE)); + parent = parent->parent); + assert(parent && (parent->style)); -/** - * Draw one border. - * - * \param side index of border side (TOP, RIGHT, BOTTOM, LEFT) - * \param p array of precomputed border vertices - * \param c colour for border - * \param style border line style - * \param thickness border thickness - * \param rectangular whether border is rectangular - * \param ctx current redraw context - * \return true if successful, false otherwise - */ + css_computed_border_spacing(parent->style, &h, &hu, &v, &vu); -bool html_redraw_border_plot(const int side, const int *p, colour c, - enum css_border_style_e style, int thickness, bool rectangular, - const struct rect *clip, const struct redraw_context *ctx) -{ - const struct plotter_table *plot = ctx->plot; - int z[8]; /* Vertices of border part */ - unsigned int light = side; - plot_style_t *plot_style_bdr_in; - plot_style_t *plot_style_bdr_out; + clip_to_children = (h > 0) || (v > 0); - if (c == NS_TRANSPARENT) - return true; + if (clip_to_children) + clip_box = box->children; + } - plot_style_bdr.stroke_type = PLOT_OP_TYPE_DASH; - plot_style_bdr.stroke_colour = c; - plot_style_bdr.stroke_width = thickness; - plot_style_fillbdr.fill_colour = c; - plot_style_fillbdr_dark.fill_colour = darken_colour(c); - plot_style_fillbdr_light.fill_colour = lighten_colour(c); - plot_style_fillbdr_ddark.fill_colour = double_darken_colour(c); - plot_style_fillbdr_dlight.fill_colour = double_lighten_colour(c); + for (; clip_box; clip_box = clip_box->next) { + /* clip to child boxes if needed */ + if (clip_to_children) { + assert(clip_box->type == BOX_TABLE_CELL); - switch (style) { - case CSS_BORDER_STYLE_DOTTED: - plot_style_bdr.stroke_type = PLOT_OP_TYPE_DOT; - /* fall through */ - case CSS_BORDER_STYLE_DASHED: - if (!plot->line((p[0] + p[2]) / 2, - (p[1] + p[3]) / 2, - (p[4] + p[6]) / 2, - (p[5] + p[7]) / 2, - &plot_style_bdr)) - return false; - break; + /* update clip.* to the child cell */ + r.x0 = ox + (clip_box->x * scale); + r.y0 = oy + (clip_box->y * scale); + r.x1 = r.x0 + (clip_box->padding[LEFT] + + clip_box->width + + clip_box->padding[RIGHT]) * scale; + r.y1 = r.y0 + (clip_box->padding[TOP] + + clip_box->height + + clip_box->padding[BOTTOM]) * scale; - case CSS_BORDER_STYLE_SOLID: - /* fall through to default */ - default: - if (rectangular || thickness == 1) { - int x0, y0, x1, y1; - if (side == TOP || side == RIGHT) { - x0 = p[2]; y0 = p[3]; - x1 = p[6]; y1 = p[7]; - x1 = ((side == TOP) && (p[4] - p[6] != 0)) ? - x1 + p[4] - p[6] : x1; - } else { - x0 = p[6]; y0 = p[7]; - x1 = p[2]; y1 = p[3]; - y1 = ((side == LEFT) && (p[1] - p[3] != 0)) ? - y1 + p[1] - p[3] : y1; - } - /* find intersection of clip rectangle and border */ - x0 = (clip->x0 > x0) ? clip->x0 : x0; - y0 = (clip->y0 > y0) ? clip->y0 : y0; - x1 = (clip->x1 < x1) ? clip->x1 : x1; - y1 = (clip->y1 < y1) ? clip->y1 : y1; - if ((x0 < x1) && (y0 < y1)) { - /* valid clip rectangles only */ - if (!plot->rectangle(x0, y0, x1, y1, - &plot_style_fillbdr)) - return false; - } - } else { - if (!plot->polygon(p, 4, &plot_style_fillbdr)) - return false; - } - break; + if (r.x0 < clip->x0) r.x0 = clip->x0; + if (r.y0 < clip->y0) r.y0 = clip->y0; + if (r.x1 > clip->x1) r.x1 = clip->x1; + if (r.y1 > clip->y1) r.y1 = clip->y1; - case CSS_BORDER_STYLE_DOUBLE: - z[0] = p[0]; - z[1] = p[1]; - z[2] = (p[0] * 2 + p[2]) / 3; - z[3] = (p[1] * 2 + p[3]) / 3; - z[4] = (p[6] * 2 + p[4]) / 3; - z[5] = (p[7] * 2 + p[5]) / 3; - z[6] = p[6]; - z[7] = p[7]; - if (!plot->polygon(z, 4, &plot_style_fillbdr)) - return false; - z[0] = p[2]; - z[1] = p[3]; - z[2] = (p[2] * 2 + p[0]) / 3; - z[3] = (p[3] * 2 + p[1]) / 3; - z[4] = (p[4] * 2 + p[6]) / 3; - z[5] = (p[5] * 2 + p[7]) / 3; - z[6] = p[4]; - z[7] = p[5]; - if (!plot->polygon(z, 4, &plot_style_fillbdr)) - return false; - break; + css_computed_background_color(clip_box->style, &bgcol); - case CSS_BORDER_STYLE_GROOVE: - light = 3 - light; - /* fall through */ - case CSS_BORDER_STYLE_RIDGE: - /* choose correct colours for each part of the border line */ - if (light <= 1) { - plot_style_bdr_in = &plot_style_fillbdr_dark; - plot_style_bdr_out = &plot_style_fillbdr_light; - } else { - plot_style_bdr_in = &plot_style_fillbdr_light; - plot_style_bdr_out = &plot_style_fillbdr_dark; + /* attributes override */ + /* if the background content is opaque there + * is no need to plot underneath it. + */ + if ((r.x0 >= r.x1) || + (r.y0 >= r.y1) || + (nscss_color_is_transparent(bgcol) == false) || + ((clip_box->background != NULL) && + content_get_opaque(clip_box->background))) + continue; } - /* Render border */ - if ((rectangular || thickness == 2) && thickness != 1) { - /* Border made up from two parts and can be plotted - * with rectangles */ - int x0, y0, x1, y1; + /* plot the background colour */ + css_computed_background_color(background->style, &bgcol); - /* First part */ - if (side == TOP || side == RIGHT) { - x0 = (p[0] + p[2]) / 2; y0 = (p[1] + p[3]) / 2; - x1 = p[6]; y1 = p[7]; - } else { - x0 = p[6]; y0 = p[7]; - x1 = (p[0] + p[2]) / 2; y1 = (p[1] + p[3]) / 2; - } - /* find intersection of clip rectangle and border */ - x0 = (clip->x0 > x0) ? clip->x0 : x0; - y0 = (clip->y0 > y0) ? clip->y0 : y0; - x1 = (clip->x1 < x1) ? clip->x1 : x1; - y1 = (clip->y1 < y1) ? clip->y1 : y1; - if ((x0 < x1) && (y0 < y1)) { - /* valid clip rectangles only */ - if (!plot->rectangle(x0, y0, x1, y1, - plot_style_bdr_in)) + if (nscss_color_is_transparent(bgcol) == false) { + *background_colour = nscss_color_to_ns(bgcol); + pstyle_fill_bg.fill_colour = *background_colour; + if (plot_colour) + if (!plot->rectangle(r.x0, r.y0, r.x1, r.y1, + &pstyle_fill_bg)) return false; - } + } + /* and plot the image */ + if (plot_content) { + width = content_get_width(background->background); + height = content_get_height(background->background); - /* Second part */ - if (side == TOP || side == RIGHT) { - x0 = p[2]; y0 = p[3]; - x1 = (p[6] + p[4]) / 2; y1 = (p[7] + p[5]) / 2; - } else { - x0 = (p[6] + p[4]) / 2; y0 = (p[7] + p[5]) / 2; - x1 = p[2]; y1 = p[3]; - } - /* find intersection of clip rectangle and border */ - x0 = (clip->x0 > x0) ? clip->x0 : x0; - y0 = (clip->y0 > y0) ? clip->y0 : y0; - x1 = (clip->x1 < x1) ? clip->x1 : x1; - y1 = (clip->y1 < y1) ? clip->y1 : y1; - if ((x0 < x1) && (y0 < y1)) { - /* valid clip rectangles only */ - if (!plot->rectangle(x0, y0, x1, y1, - plot_style_bdr_out)) - return false; + /* ensure clip area only as large as required */ + if (!repeat_x) { + if (r.x0 < x) + r.x0 = x; + if (r.x1 > x + width * scale) + r.x1 = x + width * scale; } - } else if (thickness == 1) { - /* Border made up from one part which can be plotted - * as a rectangle */ - int x0, y0, x1, y1; - if (side == TOP || side == RIGHT) { - x0 = p[2]; y0 = p[3]; - x1 = p[6]; y1 = p[7]; - x1 = ((side == TOP) && (p[4] - p[6] != 0)) ? - x1 + p[4] - p[6] : x1; - /* find intersection of clip rectangle and - * border */ - x0 = (clip->x0 > x0) ? clip->x0 : x0; - y0 = (clip->y0 > y0) ? clip->y0 : y0; - x1 = (clip->x1 < x1) ? clip->x1 : x1; - y1 = (clip->y1 < y1) ? clip->y1 : y1; - if ((x0 < x1) && (y0 < y1)) { - /* valid clip rectangles only */ - if (!plot->rectangle(x0, y0, x1, y1, - plot_style_bdr_in)) - return false; - } - } else { - x0 = p[6]; y0 = p[7]; - x1 = p[2]; y1 = p[3]; - y1 = ((side == LEFT) && (p[1] - p[3] != 0)) ? - y1 + p[1] - p[3] : y1; - /* find intersection of clip rectangle and - * border */ - x0 = (clip->x0 > x0) ? clip->x0 : x0; - y0 = (clip->y0 > y0) ? clip->y0 : y0; - x1 = (clip->x1 < x1) ? clip->x1 : x1; - y1 = (clip->y1 < y1) ? clip->y1 : y1; - if ((x0 < x1) && (y0 < y1)) { - /* valid clip rectangles only */ - if (!plot->rectangle(x0, y0, x1, y1, - plot_style_bdr_out)) - return false; - } + if (!repeat_y) { + if (r.y0 < y) + r.y0 = y; + if (r.y1 > y + height * scale) + r.y1 = y + height * scale; + } + /* valid clipping rectangles only */ + if ((r.x0 < r.x1) && (r.y0 < r.y1)) { + struct content_redraw_data bg_data; + + if (!plot->clip(&r)) + return false; + + bg_data.x = x; + bg_data.y = y; + bg_data.width = ceilf(width * scale); + bg_data.height = ceilf(height * scale); + bg_data.background_colour = *background_colour; + bg_data.scale = scale; + bg_data.repeat_x = repeat_x; + bg_data.repeat_y = repeat_y; + + if (!content_redraw(background->background, + &bg_data, &r, ctx)) + return false; } - } else { - /* Border made up from two parts and can't be plotted - * with rectangles */ - z[0] = p[0]; - z[1] = p[1]; - z[2] = (p[0] + p[2]) / 2; - z[3] = (p[1] + p[3]) / 2; - z[4] = (p[6] + p[4]) / 2; - z[5] = (p[7] + p[5]) / 2; - z[6] = p[6]; - z[7] = p[7]; - if (!plot->polygon(z, 4, plot_style_bdr_in)) - return false; - z[0] = p[2]; - z[1] = p[3]; - z[6] = p[4]; - z[7] = p[5]; - if (!plot->polygon(z, 4, plot_style_bdr_out)) - return false; } - break; - case CSS_BORDER_STYLE_INSET: - light = (light + 2) % 4; - /* fall through */ - case CSS_BORDER_STYLE_OUTSET: - /* choose correct colours for each part of the border line */ - switch (light) { - case 0: - plot_style_bdr_in = &plot_style_fillbdr_light; - plot_style_bdr_out = &plot_style_fillbdr_dlight; + /* only rows being clipped to child boxes loop */ + if (!clip_to_children) + return true; + } + return true; +} + + +/** + * Plot an inline's background and/or background image. + * + * \param x coordinate of box + * \param y coordinate of box + * \param box BOX_INLINE which created the background + * \param scale scale for redraw + * \param clip coordinates of clip rectangle + * \param b coordinates of border edge rectangle + * \param first true if this is the first rectangle associated with the inline + * \param last true if this is the last rectangle associated with the inline + * \param background_colour updated to current background colour if plotted + * \param ctx current redraw context + * \return true if successful, false otherwise + */ + +static bool html_redraw_inline_background(int x, int y, struct box *box, + float scale, const struct rect *clip, struct rect b, + bool first, bool last, colour *background_colour, + const struct redraw_context *ctx) +{ + const struct plotter_table *plot = ctx->plot; + struct rect r = *clip; + bool repeat_x = false; + bool repeat_y = false; + bool plot_colour = true; + bool plot_content; + css_fixed hpos = 0, vpos = 0; + css_unit hunit = CSS_UNIT_PX, vunit = CSS_UNIT_PX; + css_color bgcol; + plot_style_t pstyle_fill_bg = { + .fill_type = PLOT_OP_TYPE_SOLID, + .fill_colour = *background_colour, + }; + + plot_content = (box->background != NULL); + + if (html_redraw_printing && nsoption_bool(remove_backgrounds)) + return true; + + if (plot_content) { + /* handle background-repeat */ + switch (css_computed_background_repeat(box->style)) { + case CSS_BACKGROUND_REPEAT_REPEAT: + repeat_x = repeat_y = true; + /* optimisation: only plot the colour if + * bitmap is not opaque + */ + plot_colour = !content_get_opaque(box->background); break; - case 1: - plot_style_bdr_in = &plot_style_fillbdr_ddark; - plot_style_bdr_out = &plot_style_fillbdr_dark; + + case CSS_BACKGROUND_REPEAT_REPEAT_X: + repeat_x = true; break; - case 2: - plot_style_bdr_in = &plot_style_fillbdr_dark; - plot_style_bdr_out = &plot_style_fillbdr_ddark; + + case CSS_BACKGROUND_REPEAT_REPEAT_Y: + repeat_y = true; break; - case 3: - plot_style_bdr_in = &plot_style_fillbdr_dlight; - plot_style_bdr_out = &plot_style_fillbdr_light; + + case CSS_BACKGROUND_REPEAT_NO_REPEAT: break; + default: - plot_style_bdr_in = &plot_style_fillbdr; - plot_style_bdr_out = &plot_style_fillbdr; break; } - /* Render border */ - if ((rectangular || thickness == 2) && thickness != 1) { - /* Border made up from two parts and can be plotted - * with rectangles */ - int x0, y0, x1, y1; + /* handle background-position */ + css_computed_background_position(box->style, + &hpos, &hunit, &vpos, &vunit); + if (hunit == CSS_UNIT_PCT) { + x += (b.x1 - b.x0 - + content_get_width(box->background) * + scale) * FIXTOFLT(hpos) / 100.; - /* First part */ - if (side == TOP || side == RIGHT) { - x0 = (p[0] + p[2]) / 2; y0 = (p[1] + p[3]) / 2; - x1 = p[6]; y1 = p[7]; - } else { - x0 = p[6]; y0 = p[7]; - x1 = (p[0] + p[2]) / 2; y1 = (p[1] + p[3]) / 2; - } - /* find intersection of clip rectangle and border */ - x0 = (clip->x0 > x0) ? clip->x0 : x0; - y0 = (clip->y0 > y0) ? clip->y0 : y0; - x1 = (clip->x1 < x1) ? clip->x1 : x1; - y1 = (clip->y1 < y1) ? clip->y1 : y1; - if ((x0 < x1) && (y0 < y1)) { - /* valid clip rectangles only */ - if (!plot->rectangle(x0, y0, x1, y1, - plot_style_bdr_in)) - return false; + if (!repeat_x && ((hpos < 2 && !first) || + (hpos > 98 && !last))){ + plot_content = false; } + } else { + x += (int) (FIXTOFLT(nscss_len2px(hpos, hunit, + box->style)) * scale); + } - /* Second part */ - if (side == TOP || side == RIGHT) { - x0 = p[2]; y0 = p[3]; - x1 = (p[6] + p[4]) / 2; y1 = (p[7] + p[5]) / 2; - } else { - x0 = (p[6] + p[4]) / 2; y0 = (p[7] + p[5]) / 2; - x1 = p[2]; y1 = p[3]; - } - /* find intersection of clip rectangle and border */ - x0 = (clip->x0 > x0) ? clip->x0 : x0; - y0 = (clip->y0 > y0) ? clip->y0 : y0; - x1 = (clip->x1 < x1) ? clip->x1 : x1; - y1 = (clip->y1 < y1) ? clip->y1 : y1; - if ((x0 < x1) && (y0 < y1)) { - /* valid clip rectangles only */ - if (!plot->rectangle(x0, y0, x1, y1, - plot_style_bdr_out)) - return false; - } - } else if (thickness == 1) { - /* Border made up from one part which can be plotted - * as a rectangle */ - int x0, y0, x1, y1; - if (side == TOP || side == RIGHT) { - x0 = p[2]; y0 = p[3]; - x1 = p[6]; y1 = p[7]; - x1 = ((side == TOP) && (p[4] - p[6] != 0)) ? - x1 + p[4] - p[6] : x1; - /* find intersection of clip rectangle and - * border */ - x0 = (clip->x0 > x0) ? clip->x0 : x0; - y0 = (clip->y0 > y0) ? clip->y0 : y0; - x1 = (clip->x1 < x1) ? clip->x1 : x1; - y1 = (clip->y1 < y1) ? clip->y1 : y1; - if ((x0 < x1) && (y0 < y1)) { - /* valid clip rectangles only */ - if (!plot->rectangle(x0, y0, x1, y1, - plot_style_bdr_in)) - return false; - } - } else { - x0 = p[6]; y0 = p[7]; - x1 = p[2]; y1 = p[3]; - y1 = ((side == LEFT) && (p[1] - p[3] != 0)) ? - y1 + p[1] - p[3] : y1; - /* find intersection of clip rectangle and - * border */ - x0 = (clip->x0 > x0) ? clip->x0 : x0; - y0 = (clip->y0 > y0) ? clip->y0 : y0; - x1 = (clip->x1 < x1) ? clip->x1 : x1; - y1 = (clip->y1 < y1) ? clip->y1 : y1; - if ((x0 < x1) && (y0 < y1)) { - /* valid clip rectangles only */ - if (!plot->rectangle(x0, y0, x1, y1, - plot_style_bdr_out)) - return false; - } - } + if (vunit == CSS_UNIT_PCT) { + y += (b.y1 - b.y0 - + content_get_height(box->background) * + scale) * FIXTOFLT(vpos) / 100.; } else { - /* Border made up from two parts and can't be plotted - * with rectangles */ - z[0] = p[0]; - z[1] = p[1]; - z[2] = (p[0] + p[2]) / 2; - z[3] = (p[1] + p[3]) / 2; - z[4] = (p[6] + p[4]) / 2; - z[5] = (p[7] + p[5]) / 2; - z[6] = p[6]; - z[7] = p[7]; - if (!plot->polygon(z, 4, plot_style_bdr_in)) + y += (int) (FIXTOFLT(nscss_len2px(vpos, vunit, + box->style)) * scale); + } + } + + /* plot the background colour */ + css_computed_background_color(box->style, &bgcol); + + if (nscss_color_is_transparent(bgcol) == false) { + *background_colour = nscss_color_to_ns(bgcol); + pstyle_fill_bg.fill_colour = *background_colour; + + if (plot_colour) + if (!plot->rectangle(r.x0, r.y0, r.x1, r.y1, + &pstyle_fill_bg)) return false; - z[0] = p[2]; - z[1] = p[3]; - z[6] = p[4]; - z[7] = p[5]; - if (!plot->polygon(z, 4, plot_style_bdr_out)) + } + /* and plot the image */ + if (plot_content) { + int width = content_get_width(box->background); + int height = content_get_height(box->background); + + if (!repeat_x) { + if (r.x0 < x) + r.x0 = x; + if (r.x1 > x + width * scale) + r.x1 = x + width * scale; + } + if (!repeat_y) { + if (r.y0 < y) + r.y0 = y; + if (r.y1 > y + height * scale) + r.y1 = y + height * scale; + } + /* valid clipping rectangles only */ + if ((r.x0 < r.x1) && (r.y0 < r.y1)) { + struct content_redraw_data bg_data; + + if (!plot->clip(&r)) + return false; + + bg_data.x = x; + bg_data.y = y; + bg_data.width = ceilf(width * scale); + bg_data.height = ceilf(height * scale); + bg_data.background_colour = *background_colour; + bg_data.scale = scale; + bg_data.repeat_x = repeat_x; + bg_data.repeat_y = repeat_y; + + if (!content_redraw(box->background, &bg_data, &r, ctx)) return false; } - break; } return true; @@ -1910,55 +1646,83 @@ bool html_redraw_border_plot(const int side, const int *p, colour c, /** - * Plot a checkbox. + * Plot text decoration for an inline box. * - * \param x left coordinate - * \param y top coordinate - * \param width dimensions of checkbox - * \param height dimensions of checkbox - * \param selected the checkbox is selected - * \param ctx current redraw context + * \param box box to plot decorations for, of type BOX_INLINE + * \param x x coordinate of parent of box + * \param y y coordinate of parent of box + * \param scale scale for redraw + * \param colour colour for decorations + * \param ratio position of line as a ratio of line height + * \param ctx current redraw context * \return true if successful, false otherwise */ -bool html_redraw_checkbox(int x, int y, int width, int height, bool selected, +static bool html_redraw_text_decoration_inline(struct box *box, int x, int y, + float scale, colour colour, float ratio, const struct redraw_context *ctx) { const struct plotter_table *plot = ctx->plot; - double z = width * 0.15; - if (z == 0) - z = 1; + struct box *c; + plot_style_t plot_style_box = { + .stroke_type = PLOT_OP_TYPE_SOLID, + .stroke_colour = colour, + }; - if (!(plot->rectangle(x, y, x + width, y + height, - plot_style_fill_wbasec) && - plot->line(x, y, x + width, y, plot_style_stroke_darkwbasec) && - plot->line(x, y, x, y + height, plot_style_stroke_darkwbasec) && - plot->line(x + width, y, x + width, y + height, - plot_style_stroke_lightwbasec) && - plot->line(x, y + height, x + width, y + height, - plot_style_stroke_lightwbasec))) - return false; + for (c = box->next; + c && c != box->inline_end; + c = c->next) { + if (c->type != BOX_TEXT) + continue; + if (!plot->line((x + c->x) * scale, + (y + c->y + c->height * ratio) * scale, + (x + c->x + c->width) * scale, + (y + c->y + c->height * ratio) * scale, + &plot_style_box)) + return false; + } + return true; +} - if (selected) { - if (width < 12 || height < 12) { - /* render a solid box instead of a tick */ - if (!plot->rectangle(x + z + z, y + z + z, - x + width - z, y + height - z, - plot_style_fill_wblobc)) - return false; - } else { - /* render a tick, as it'll fit comfortably */ - if (!(plot->line(x + width - z, - y + z, - x + (z * 3), - y + height - z, - plot_style_stroke_wblobc) && - plot->line(x + (z * 3), - y + height - z, - x + z + z, - y + (height / 2), - plot_style_stroke_wblobc))) +/** + * Plot text decoration for an non-inline box. + * + * \param box box to plot decorations for, of type other than BOX_INLINE + * \param x x coordinate of box + * \param y y coordinate of box + * \param scale scale for redraw + * \param colour colour for decorations + * \param ratio position of line as a ratio of line height + * \param ctx current redraw context + * \return true if successful, false otherwise + */ + +static bool html_redraw_text_decoration_block(struct box *box, int x, int y, + float scale, colour colour, float ratio, + const struct redraw_context *ctx) +{ + const struct plotter_table *plot = ctx->plot; + struct box *c; + plot_style_t plot_style_box = { + .stroke_type = PLOT_OP_TYPE_SOLID, + .stroke_colour = colour, + }; + + /* draw through text descendants */ + for (c = box->children; c; c = c->next) { + if (c->type == BOX_TEXT) { + if (!plot->line((x + c->x) * scale, + (y + c->y + c->height * ratio) * scale, + (x + c->x + c->width) * scale, + (y + c->y + c->height * ratio) * scale, + &plot_style_box)) + return false; + } else if (c->type == BOX_INLINE_CONTAINER || + c->type == BOX_BLOCK) { + if (!html_redraw_text_decoration_block(c, + x + c->x, y + c->y, + scale, colour, ratio, ctx)) return false; } } @@ -1967,53 +1731,56 @@ bool html_redraw_checkbox(int x, int y, int width, int height, bool selected, /** - * Plot a radio icon. + * Plot text decoration for a box. * - * \param x left coordinate - * \param y top coordinate - * \param width dimensions of radio icon - * \param height dimensions of radio icon - * \param selected the radio icon is selected + * \param box box to plot decorations for + * \param x_parent x coordinate of parent of box + * \param y_parent y coordinate of parent of box + * \param scale scale for redraw + * \param background_colour current background colour * \param ctx current redraw context * \return true if successful, false otherwise */ -bool html_redraw_radio(int x, int y, int width, int height, bool selected, - const struct redraw_context *ctx) -{ - const struct plotter_table *plot = ctx->plot; - /* plot background of radio button */ - if (!plot->disc(x + width * 0.5, - y + height * 0.5, - width * 0.5 - 1, - plot_style_fill_wbasec)) - return false; +static bool html_redraw_text_decoration(struct box *box, + int x_parent, int y_parent, float scale, + colour background_colour, const struct redraw_context *ctx) +{ + static const enum css_text_decoration_e decoration[] = { + CSS_TEXT_DECORATION_UNDERLINE, CSS_TEXT_DECORATION_OVERLINE, + CSS_TEXT_DECORATION_LINE_THROUGH }; + static const float line_ratio[] = { 0.9, 0.1, 0.5 }; + colour fgcol; + unsigned int i; + css_color col; - /* plot dark arc */ - if (!plot->arc(x + width * 0.5, - y + height * 0.5, - width * 0.5 - 1, - 45, - 225, - plot_style_fill_darkwbasec)) - return false; + css_computed_color(box->style, &col); + fgcol = nscss_color_to_ns(col); - /* plot light arc */ - if (!plot->arc(x + width * 0.5, - y + height * 0.5, - width * 0.5 - 1, - 225, - 45, - plot_style_fill_lightwbasec)) - return false; + /* antialias colour for under/overline */ + if (html_redraw_printing == false) + fgcol = blend_colour(background_colour, fgcol); - if (selected) { - /* plot selection blob */ - if (!plot->disc(x + width * 0.5, - y + height * 0.5, - width * 0.3 - 1, - plot_style_fill_wblobc)) - return false; + if (box->type == BOX_INLINE) { + if (!box->inline_end) + return true; + for (i = 0; i != NOF_ELEMENTS(decoration); i++) + if (css_computed_text_decoration(box->style) & + decoration[i]) + if (!html_redraw_text_decoration_inline(box, + x_parent, y_parent, scale, + fgcol, line_ratio[i], ctx)) + return false; + } else { + for (i = 0; i != NOF_ELEMENTS(decoration); i++) + if (css_computed_text_decoration(box->style) & + decoration[i]) + if (!html_redraw_text_decoration_block(box, + x_parent + box->x, + y_parent + box->y, + scale, + fgcol, line_ratio[i], ctx)) + return false; } return true; @@ -2021,554 +1788,750 @@ bool html_redraw_radio(int x, int y, int width, int height, bool selected, /** - * Plot a file upload input. + * Redraw the text content of a box, possibly partially highlighted + * because the text has been selected, or matches a search operation. * - * \param x left coordinate - * \param y top coordinate - * \param width dimensions of input - * \param height dimensions of input - * \param box box of input + * \param box box with text content + * \param x x co-ord of box + * \param y y co-ord of box + * \param clip current clip rectangle + * \param scale current scale setting (1.0 = 100%) + * \param current_background_color + * \param ctx current redraw context + * \return true iff successful and redraw should proceed + */ + +static bool html_redraw_text_box(const html_content *html, struct box *box, + int x, int y, const struct rect *clip, float scale, + colour current_background_color, + const struct redraw_context *ctx) +{ + bool excluded = (box->object != NULL); + plot_font_style_t fstyle; + + font_plot_style_from_css(box->style, &fstyle); + fstyle.background = current_background_color; + + if (!text_redraw(box->text, box->length, box->byte_offset, + box->space, &fstyle, x, y, + clip, box->height, scale, excluded, + (struct content *)html, &html->sel, + html->search, ctx)) + return false; + + return true; +} + +bool html_redraw_box(const html_content *html, struct box *box, + int x_parent, int y_parent, + const struct rect *clip, float scale, + colour current_background_color, + const struct redraw_context *ctx); + +/** + * Draw the various children of a box. + * + * \param html html content + * \param box box to draw children of + * \param x_parent coordinate of parent box + * \param y_parent coordinate of parent box + * \param clip clip rectangle * \param scale scale for redraw - * \param background_colour current background colour + * \param current_background_color background colour under this box * \param ctx current redraw context * \return true if successful, false otherwise */ -bool html_redraw_file(int x, int y, int width, int height, - struct box *box, float scale, colour background_colour, +static bool html_redraw_box_children(const html_content *html, struct box *box, + int x_parent, int y_parent, + const struct rect *clip, float scale, + colour current_background_color, const struct redraw_context *ctx) { - int text_width; - const char *text; - size_t length; - plot_font_style_t fstyle; - - font_plot_style_from_css(box->style, &fstyle); - fstyle.background = background_colour; + struct box *c; - if (box->gadget->value) - text = box->gadget->value; - else - text = messages_get("Form_Drop"); - length = strlen(text); + for (c = box->children; c; c = c->next) { - if (!nsfont.font_width(&fstyle, text, length, &text_width)) - return false; - text_width *= scale; - if (width < text_width + 8) - x = x + width - text_width - 4; - else - x = x + 4; + if (c->type != BOX_FLOAT_LEFT && c->type != BOX_FLOAT_RIGHT) + if (!html_redraw_box(html, c, + x_parent + box->x - + scrollbar_get_offset(box->scroll_x), + y_parent + box->y - + scrollbar_get_offset(box->scroll_y), + clip, scale, current_background_color, + ctx)) + return false; + } + for (c = box->float_children; c; c = c->next_float) + if (!html_redraw_box(html, c, + x_parent + box->x - + scrollbar_get_offset(box->scroll_x), + y_parent + box->y - + scrollbar_get_offset(box->scroll_y), + clip, scale, current_background_color, + ctx)) + return false; - return ctx->plot->text(x, y + height * 0.75, text, length, &fstyle); + return true; } - /** - * Plot background images. + * Recursively draw a box. * - * \param x coordinate of box - * \param y coordinate of box - * \param box box to draw background image of - * \param scale scale for redraw - * \param clip current clip rectangle - * \param background_colour current background colour - * \param background box containing background details (usually ::box) - * \param ctx current redraw context + * \param html html content + * \param box box to draw + * \param x_parent coordinate of parent box + * \param y_parent coordinate of parent box + * \param clip clip rectangle + * \param scale scale for redraw + * \param current_background_color background colour under this box + * \param ctx current redraw context * \return true if successful, false otherwise * - * The reason for the presence of ::background is the backwards compatibility - * mess that is backgrounds on . The background will be drawn relative - * to ::box, using the background information contained within ::background. + * x, y, clip_[xy][01] are in target coordinates. */ -bool html_redraw_background(int x, int y, struct box *box, float scale, - const struct rect *clip, colour *background_colour, - struct box *background, const struct redraw_context *ctx) +bool html_redraw_box(const html_content *html, struct box *box, + int x_parent, int y_parent, + const struct rect *clip, float scale, + colour current_background_color, + const struct redraw_context *ctx) { const struct plotter_table *plot = ctx->plot; - bool repeat_x = false; - bool repeat_y = false; - bool plot_colour = true; - bool plot_content; - bool clip_to_children = false; - struct box *clip_box = box; - int ox = x, oy = y; + int x, y; int width, height; - css_fixed hpos = 0, vpos = 0; - css_unit hunit = CSS_UNIT_PX, vunit = CSS_UNIT_PX; - struct box *parent; - struct rect r = *clip; - css_color bgcol; - plot_style_t pstyle_fill_bg = { - .fill_type = PLOT_OP_TYPE_SOLID, - .fill_colour = *background_colour, - }; + int padding_left, padding_top, padding_width, padding_height; + int border_left, border_top, border_right, border_bottom; + struct rect r; + int x_scrolled, y_scrolled; + struct box *bg_box = NULL; + bool has_x_scroll, has_y_scroll; + css_computed_clip_rect css_rect; - if (ctx->background_images == false) + if (html_redraw_printing && (box->flags & PRINTED)) return true; - plot_content = (background->background != NULL); + /* avoid trivial FP maths */ + if (scale == 1.0) { + x = x_parent + box->x; + y = y_parent + box->y; + width = box->width; + height = box->height; + padding_left = box->padding[LEFT]; + padding_top = box->padding[TOP]; + padding_width = padding_left + box->width + box->padding[RIGHT]; + padding_height = padding_top + box->height + + box->padding[BOTTOM]; + border_left = box->border[LEFT].width; + border_top = box->border[TOP].width; + border_right = box->border[RIGHT].width; + border_bottom = box->border[BOTTOM].width; + } else { + x = (x_parent + box->x) * scale; + y = (y_parent + box->y) * scale; + width = box->width * scale; + height = box->height * scale; + /* left and top padding values are normally zero, + * so avoid trivial FP maths */ + padding_left = box->padding[LEFT] ? box->padding[LEFT] * scale + : 0; + padding_top = box->padding[TOP] ? box->padding[TOP] * scale + : 0; + padding_width = (box->padding[LEFT] + box->width + + box->padding[RIGHT]) * scale; + padding_height = (box->padding[TOP] + box->height + + box->padding[BOTTOM]) * scale; + border_left = box->border[LEFT].width * scale; + border_top = box->border[TOP].width * scale; + border_right = box->border[RIGHT].width * scale; + border_bottom = box->border[BOTTOM].width * scale; + } - if (plot_content) { - if (!box->parent) { - /* Root element, special case: - * background origin calc. is based on margin box */ - x -= box->margin[LEFT] * scale; - y -= box->margin[TOP] * scale; - width = box->margin[LEFT] + box->padding[LEFT] + - box->width + box->padding[RIGHT] + - box->margin[RIGHT]; - height = box->margin[TOP] + box->padding[TOP] + - box->height + box->padding[BOTTOM] + - box->margin[BOTTOM]; + /* calculate rectangle covering this box and descendants */ + if (box->style && css_computed_overflow(box->style) != + CSS_OVERFLOW_VISIBLE) { + /* box contents clipped to box size */ + r.x0 = x - border_left; + r.y0 = y - border_top; + r.x1 = x + padding_width + border_right; + r.y1 = y + padding_height + border_bottom; + } else { + /* box contents can hang out of the box; use descendant box */ + if (scale == 1.0) { + r.x0 = x + box->descendant_x0; + r.y0 = y + box->descendant_y0; + r.x1 = x + box->descendant_x1 + 1; + r.y1 = y + box->descendant_y1 + 1; } else { - width = box->padding[LEFT] + box->width + - box->padding[RIGHT]; - height = box->padding[TOP] + box->height + - box->padding[BOTTOM]; + r.x0 = x + box->descendant_x0 * scale; + r.y0 = y + box->descendant_y0 * scale; + r.x1 = x + box->descendant_x1 * scale + 1; + r.y1 = y + box->descendant_y1 * scale + 1; } - /* handle background-repeat */ - switch (css_computed_background_repeat(background->style)) { - case CSS_BACKGROUND_REPEAT_REPEAT: - repeat_x = repeat_y = true; - /* optimisation: only plot the colour if - * bitmap is not opaque */ - plot_colour = !content_get_opaque(background->background); - break; - - case CSS_BACKGROUND_REPEAT_REPEAT_X: - repeat_x = true; - break; - - case CSS_BACKGROUND_REPEAT_REPEAT_Y: - repeat_y = true; - break; - - case CSS_BACKGROUND_REPEAT_NO_REPEAT: - break; - - default: - break; + if (!box->parent) { + /* root element */ + int margin_left, margin_right; + int margin_top, margin_bottom; + if (scale == 1.0) { + margin_left = box->margin[LEFT]; + margin_top = box->margin[TOP]; + margin_right = box->margin[RIGHT]; + margin_bottom = box->margin[BOTTOM]; + } else { + margin_left = box->margin[LEFT] * scale; + margin_top = box->margin[TOP] * scale; + margin_right = box->margin[RIGHT] * scale; + margin_bottom = box->margin[BOTTOM] * scale; + } + r.x0 = x - border_left - margin_left < r.x0 ? + x - border_left - margin_left : r.x0; + r.y0 = y - border_top - margin_top < r.y0 ? + y - border_top - margin_top : r.y0; + r.x1 = x + padding_width + border_right + + margin_right > r.x1 ? + x + padding_width + border_right + + margin_right : r.x1; + r.y1 = y + padding_height + border_bottom + + margin_bottom > r.y1 ? + y + padding_height + border_bottom + + margin_bottom : r.y1; } + } - /* handle background-position */ - css_computed_background_position(background->style, - &hpos, &hunit, &vpos, &vunit); - if (hunit == CSS_UNIT_PCT) { - x += (width - - content_get_width(background->background)) * - scale * FIXTOFLT(hpos) / 100.; - } else { - x += (int) (FIXTOFLT(nscss_len2px(hpos, hunit, - background->style)) * scale); - } + /* return if the rectangle is completely outside the clip rectangle */ + if (clip->y1 < r.y0 || r.y1 < clip->y0 || + clip->x1 < r.x0 || r.x1 < clip->x0) + return true; - if (vunit == CSS_UNIT_PCT) { - y += (height - - content_get_height(background->background)) * - scale * FIXTOFLT(vpos) / 100.; - } else { - y += (int) (FIXTOFLT(nscss_len2px(vpos, vunit, - background->style)) * scale); + /*if the rectangle is under the page bottom but it can fit in a page, + don't print it now*/ + if (html_redraw_printing) { + if (r.y1 > html_redraw_printing_border) { + if (r.y1 - r.y0 <= html_redraw_printing_border && + (box->type == BOX_TEXT || + box->type == BOX_TABLE_CELL + || box->object || box->gadget)) { + /*remember the highest of all points from the + not printed elements*/ + if (r.y0 < html_redraw_printing_top_cropped) + html_redraw_printing_top_cropped = r.y0; + return true; + } } + else box->flags |= PRINTED; /*it won't be printed anymore*/ } - /* special case for table rows as their background needs - * to be clipped to all the cells */ - if (box->type == BOX_TABLE_ROW) { - css_fixed h = 0, v = 0; - css_unit hu = CSS_UNIT_PX, vu = CSS_UNIT_PX; - - for (parent = box->parent; - ((parent) && (parent->type != BOX_TABLE)); - parent = parent->parent); - assert(parent && (parent->style)); - - css_computed_border_spacing(parent->style, &h, &hu, &v, &vu); - - clip_to_children = (h > 0) || (v > 0); - - if (clip_to_children) - clip_box = box->children; + /* if visibility is hidden render children only */ + if (box->style && css_computed_visibility(box->style) == + CSS_VISIBILITY_HIDDEN) { + if ((plot->group_start) && (!plot->group_start("hidden box"))) + return false; + if (!html_redraw_box_children(html, box, x_parent, y_parent, + &r, scale, current_background_color, ctx)) + return false; + return ((!plot->group_end) || (plot->group_end())); } - for (; clip_box; clip_box = clip_box->next) { - /* clip to child boxes if needed */ - if (clip_to_children) { - assert(clip_box->type == BOX_TABLE_CELL); + if ((plot->group_start) && (!plot->group_start("vis box"))) + return false; - /* update clip.* to the child cell */ - r.x0 = ox + (clip_box->x * scale); - r.y0 = oy + (clip_box->y * scale); - r.x1 = r.x0 + (clip_box->padding[LEFT] + - clip_box->width + - clip_box->padding[RIGHT]) * scale; - r.y1 = r.y0 + (clip_box->padding[TOP] + - clip_box->height + - clip_box->padding[BOTTOM]) * scale; - if (r.x0 < clip->x0) r.x0 = clip->x0; - if (r.y0 < clip->y0) r.y0 = clip->y0; - if (r.x1 > clip->x1) r.x1 = clip->x1; - if (r.y1 > clip->y1) r.y1 = clip->y1; + if (box->style != NULL && + css_computed_position(box->style) == + CSS_POSITION_ABSOLUTE && + css_computed_clip(box->style, &css_rect) == + CSS_CLIP_RECT) { + /* We have an absolutly positioned box with a clip rect */ + if (css_rect.left_auto == false) + r.x0 = x - border_left + FIXTOINT(nscss_len2px( + css_rect.left, css_rect.lunit, + box->style)); - css_computed_background_color(clip_box->style, &bgcol); + if (css_rect.top_auto == false) + r.y0 = y - border_top + FIXTOINT(nscss_len2px( + css_rect.top, css_rect.tunit, + box->style)); - /* attributes override */ - /* if the background content is opaque there - * is no need to plot underneath it. - */ - if ((r.x0 >= r.x1) || - (r.y0 >= r.y1) || - (nscss_color_is_transparent(bgcol) == false) || - ((clip_box->background != NULL) && - content_get_opaque(clip_box->background))) - continue; - } + if (css_rect.right_auto == false) + r.x1 = x - border_left + FIXTOINT(nscss_len2px( + css_rect.right, css_rect.runit, + box->style)); - /* plot the background colour */ - css_computed_background_color(background->style, &bgcol); + if (css_rect.bottom_auto == false) + r.y1 = y - border_top + FIXTOINT(nscss_len2px( + css_rect.bottom, css_rect.bunit, + box->style)); - if (nscss_color_is_transparent(bgcol) == false) { - *background_colour = nscss_color_to_ns(bgcol); - pstyle_fill_bg.fill_colour = *background_colour; - if (plot_colour) - if (!plot->rectangle(r.x0, r.y0, r.x1, r.y1, - &pstyle_fill_bg)) - return false; - } - /* and plot the image */ - if (plot_content) { - width = content_get_width(background->background); - height = content_get_height(background->background); + /* find intersection of clip rectangle and box */ + if (r.x0 < clip->x0) r.x0 = clip->x0; + if (r.y0 < clip->y0) r.y0 = clip->y0; + if (clip->x1 < r.x1) r.x1 = clip->x1; + if (clip->y1 < r.y1) r.y1 = clip->y1; + /* no point trying to draw 0-width/height boxes */ + if (r.x0 == r.x1 || r.y0 == r.y1) + /* not an error */ + return ((!plot->group_end) || (plot->group_end())); + /* clip to it */ + if (!plot->clip(&r)) + return false; - /* ensure clip area only as large as required */ - if (!repeat_x) { - if (r.x0 < x) - r.x0 = x; - if (r.x1 > x + width * scale) - r.x1 = x + width * scale; - } - if (!repeat_y) { - if (r.y0 < y) - r.y0 = y; - if (r.y1 > y + height * scale) - r.y1 = y + height * scale; - } - /* valid clipping rectangles only */ - if ((r.x0 < r.x1) && (r.y0 < r.y1)) { - struct content_redraw_data bg_data; + } else if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK || + box->type == BOX_TABLE_CELL || box->object) { + /* find intersection of clip rectangle and box */ + if (r.x0 < clip->x0) r.x0 = clip->x0; + if (r.y0 < clip->y0) r.y0 = clip->y0; + if (clip->x1 < r.x1) r.x1 = clip->x1; + if (clip->y1 < r.y1) r.y1 = clip->y1; + /* no point trying to draw 0-width/height boxes */ + if (r.x0 == r.x1 || r.y0 == r.y1) + /* not an error */ + return ((!plot->group_end) || (plot->group_end())); + /* clip to it */ + if (!plot->clip(&r)) + return false; + } else { + /* clip box is fine, clip to it */ + r = *clip; + if (!plot->clip(&r)) + return false; + } - if (!plot->clip(&r)) - return false; + /* background colour and image for block level content and replaced + * inlines */ - bg_data.x = x; - bg_data.y = y; - bg_data.width = ceilf(width * scale); - bg_data.height = ceilf(height * scale); - bg_data.background_colour = *background_colour; - bg_data.scale = scale; - bg_data.repeat_x = repeat_x; - bg_data.repeat_y = repeat_y; + bg_box = html_redraw_find_bg_box(box); - if (!content_redraw(background->background, - &bg_data, &r, ctx)) - return false; + /* bg_box == NULL implies that this box should not have + * its background rendered. Otherwise filter out linebreaks, + * optimize away non-differing inlines, only plot background + * for BOX_TEXT it's in an inline */ + if (bg_box && bg_box->type != BOX_BR && + bg_box->type != BOX_TEXT && + bg_box->type != BOX_INLINE_END && + (bg_box->type != BOX_INLINE || bg_box->object || + bg_box->flags & IFRAME || box->flags & REPLACE_DIM)) { + /* find intersection of clip box and border edge */ + struct rect p; + p.x0 = x - border_left < r.x0 ? r.x0 : x - border_left; + p.y0 = y - border_top < r.y0 ? r.y0 : y - border_top; + p.x1 = x + padding_width + border_right < r.x1 ? + x + padding_width + border_right : r.x1; + p.y1 = y + padding_height + border_bottom < r.y1 ? + y + padding_height + border_bottom : r.y1; + if (!box->parent) { + /* Root element, special case: + * background covers margins too */ + int m_left, m_top, m_right, m_bottom; + if (scale == 1.0) { + m_left = box->margin[LEFT]; + m_top = box->margin[TOP]; + m_right = box->margin[RIGHT]; + m_bottom = box->margin[BOTTOM]; + } else { + m_left = box->margin[LEFT] * scale; + m_top = box->margin[TOP] * scale; + m_right = box->margin[RIGHT] * scale; + m_bottom = box->margin[BOTTOM] * scale; } + p.x0 = p.x0 - m_left < r.x0 ? r.x0 : p.x0 - m_left; + p.y0 = p.y0 - m_top < r.y0 ? r.y0 : p.y0 - m_top; + p.x1 = p.x1 + m_right < r.x1 ? p.x1 + m_right : r.x1; + p.y1 = p.y1 + m_bottom < r.y1 ? p.y1 + m_bottom : r.y1; + } + /* valid clipping rectangles only */ + if ((p.x0 < p.x1) && (p.y0 < p.y1)) { + /* plot background */ + if (!html_redraw_background(x, y, box, scale, &p, + ¤t_background_color, bg_box, ctx)) + return false; + /* restore previous graphics window */ + if (!plot->clip(&r)) + return false; } - - /* only rows being clipped to child boxes loop */ - if (!clip_to_children) - return true; } - return true; -} + /* borders for block level content and replaced inlines */ + if (box->style && box->type != BOX_TEXT && + box->type != BOX_INLINE_END && + (box->type != BOX_INLINE || box->object || + box->flags & IFRAME || box->flags & REPLACE_DIM) && + (border_top || border_right || + border_bottom || border_left)) { + if (!html_redraw_borders(box, x_parent, y_parent, + padding_width, padding_height, &r, + scale, ctx)) + return false; + } -/** - * Plot an inline's background and/or background image. - * - * \param x coordinate of box - * \param y coordinate of box - * \param box BOX_INLINE which created the background - * \param scale scale for redraw - * \param clip coordinates of clip rectangle - * \param b coordinates of border edge rectangle - * \param first true if this is the first rectangle associated with the inline - * \param last true if this is the last rectangle associated with the inline - * \param background_colour updated to current background colour if plotted - * \param ctx current redraw context - * \return true if successful, false otherwise - */ - -bool html_redraw_inline_background(int x, int y, struct box *box, float scale, - const struct rect *clip, struct rect b, bool first, bool last, - colour *background_colour, const struct redraw_context *ctx) -{ - const struct plotter_table *plot = ctx->plot; - struct rect r = *clip; - bool repeat_x = false; - bool repeat_y = false; - bool plot_colour = true; - bool plot_content; - css_fixed hpos = 0, vpos = 0; - css_unit hunit = CSS_UNIT_PX, vunit = CSS_UNIT_PX; - css_color bgcol; - plot_style_t pstyle_fill_bg = { - .fill_type = PLOT_OP_TYPE_SOLID, - .fill_colour = *background_colour, - }; + /* backgrounds and borders for non-replaced inlines */ + if (box->style && box->type == BOX_INLINE && box->inline_end && + (html_redraw_box_has_background(box) || + border_top || border_right || + border_bottom || border_left)) { + /* inline backgrounds and borders span other boxes and may + * wrap onto separate lines */ + struct box *ib; + struct rect b; /* border edge rectangle */ + struct rect p; /* clipped rect */ + bool first = true; + int ib_x; + int ib_y = y; + int ib_p_width; + int ib_b_left, ib_b_right; - plot_content = (box->background != NULL); + b.x0 = x - border_left; + b.x1 = x + padding_width + border_right; + b.y0 = y - border_top; + b.y1 = y + padding_height + border_bottom; - if (html_redraw_printing && nsoption_bool(remove_backgrounds)) - return true; + p.x0 = b.x0 < r.x0 ? r.x0 : b.x0; + p.x1 = b.x1 < r.x1 ? b.x1 : r.x1; + p.y0 = b.y0 < r.y0 ? r.y0 : b.y0; + p.y1 = b.y1 < r.y1 ? b.y1 : r.y1; + for (ib = box; ib; ib = ib->next) { + /* to get extents of rectangle(s) associated with + * inline, cycle though all boxes in inline, skipping + * over floats */ + if (ib->type == BOX_FLOAT_LEFT || + ib->type == BOX_FLOAT_RIGHT) + continue; + if (scale == 1.0) { + ib_x = x_parent + ib->x; + ib_y = y_parent + ib->y; + ib_p_width = ib->padding[LEFT] + ib->width + + ib->padding[RIGHT]; + ib_b_left = ib->border[LEFT].width; + ib_b_right = ib->border[RIGHT].width; + } else { + ib_x = (x_parent + ib->x) * scale; + ib_y = (y_parent + ib->y) * scale; + ib_p_width = (ib->padding[LEFT] + ib->width + + ib->padding[RIGHT]) * scale; + ib_b_left = ib->border[LEFT].width * scale; + ib_b_right = ib->border[RIGHT].width * scale; + } - if (plot_content) { - /* handle background-repeat */ - switch (css_computed_background_repeat(box->style)) { - case CSS_BACKGROUND_REPEAT_REPEAT: - repeat_x = repeat_y = true; - /* optimisation: only plot the colour if - * bitmap is not opaque - */ - plot_colour = !content_get_opaque(box->background); - break; + if ((ib->flags & NEW_LINE) && ib != box) { + /* inline element has wrapped, plot background + * and borders */ + if (!html_redraw_inline_background( + x, y, box, scale, &p, b, + first, false, + ¤t_background_color, ctx)) + return false; + /* restore previous graphics window */ + if (!plot->clip(&r)) + return false; + if (!html_redraw_inline_borders(box, b, &r, + scale, first, false, ctx)) + return false; + /* reset coords */ + b.x0 = ib_x - ib_b_left; + b.y0 = ib_y - border_top - padding_top; + b.y1 = ib_y + padding_height - padding_top + + border_bottom; - case CSS_BACKGROUND_REPEAT_REPEAT_X: - repeat_x = true; - break; + p.x0 = b.x0 < r.x0 ? r.x0 : b.x0; + p.y0 = b.y0 < r.y0 ? r.y0 : b.y0; + p.y1 = b.y1 < r.y1 ? b.y1 : r.y1; - case CSS_BACKGROUND_REPEAT_REPEAT_Y: - repeat_y = true; - break; + first = false; + } - case CSS_BACKGROUND_REPEAT_NO_REPEAT: - break; + /* increase width for current box */ + b.x1 = ib_x + ib_p_width + ib_b_right; + p.x1 = b.x1 < r.x1 ? b.x1 : r.x1; - default: - break; + if (ib == box->inline_end) + /* reached end of BOX_INLINE span */ + break; } + /* plot background and borders for last rectangle of + * the inline */ + if (!html_redraw_inline_background(x, ib_y, box, scale, &p, b, + first, true, ¤t_background_color, ctx)) + return false; + /* restore previous graphics window */ + if (!plot->clip(&r)) + return false; + if (!html_redraw_inline_borders(box, b, &r, scale, first, true, + ctx)) + return false; - /* handle background-position */ - css_computed_background_position(box->style, - &hpos, &hunit, &vpos, &vunit); - if (hunit == CSS_UNIT_PCT) { - x += (b.x1 - b.x0 - - content_get_width(box->background) * - scale) * FIXTOFLT(hpos) / 100.; + } - if (!repeat_x && ((hpos < 2 && !first) || - (hpos > 98 && !last))){ - plot_content = false; - } + /* Debug outlines */ + if (html_redraw_debug) { + int margin_left, margin_right; + int margin_top, margin_bottom; + if (scale == 1.0) { + /* avoid trivial fp maths */ + margin_left = box->margin[LEFT]; + margin_top = box->margin[TOP]; + margin_right = box->margin[RIGHT]; + margin_bottom = box->margin[BOTTOM]; } else { - x += (int) (FIXTOFLT(nscss_len2px(hpos, hunit, - box->style)) * scale); + margin_left = box->margin[LEFT] * scale; + margin_top = box->margin[TOP] * scale; + margin_right = box->margin[RIGHT] * scale; + margin_bottom = box->margin[BOTTOM] * scale; } + /* Content edge -- blue */ + if (!plot->rectangle(x + padding_left, + y + padding_top, + x + padding_left + width, + y + padding_top + height, + plot_style_content_edge)) + return false; + /* Padding edge -- red */ + if (!plot->rectangle(x, y, + x + padding_width, y + padding_height, + plot_style_padding_edge)) + return false; + /* Margin edge -- yellow */ + if (!plot->rectangle( + x - border_left - margin_left, + y - border_top - margin_top, + x + padding_width + border_right + + margin_right, + y + padding_height + border_bottom + + margin_bottom, + plot_style_margin_edge)) + return false; + } - if (vunit == CSS_UNIT_PCT) { - y += (b.y1 - b.y0 - - content_get_height(box->background) * - scale) * FIXTOFLT(vpos) / 100.; - } else { - y += (int) (FIXTOFLT(nscss_len2px(vpos, vunit, - box->style)) * scale); + /* clip to the padding edge for objects, or boxes with overflow hidden + * or scroll */ + if ((box->style && css_computed_overflow(box->style) != + CSS_OVERFLOW_VISIBLE) || box->object || + box->flags & IFRAME) { + r.x0 = x; + r.y0 = y; + r.x1 = x + padding_width; + r.y1 = y + padding_height; + if (r.x0 < clip->x0) r.x0 = clip->x0; + if (r.y0 < clip->y0) r.y0 = clip->y0; + if (clip->x1 < r.x1) r.x1 = clip->x1; + if (clip->y1 < r.y1) r.y1 = clip->y1; + if (r.x1 <= r.x0 || r.y1 <= r.y0) + return ((!plot->group_end) || (plot->group_end())); + if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK || + box->type == BOX_TABLE_CELL || box->object) { + if (!plot->clip(&r)) + return false; } } - /* plot the background colour */ - css_computed_background_color(box->style, &bgcol); + /* text decoration */ + if (box->type != BOX_TEXT && box->style && + css_computed_text_decoration(box->style) != + CSS_TEXT_DECORATION_NONE) + if (!html_redraw_text_decoration(box, x_parent, y_parent, + scale, current_background_color, ctx)) + return false; - if (nscss_color_is_transparent(bgcol) == false) { - *background_colour = nscss_color_to_ns(bgcol); - pstyle_fill_bg.fill_colour = *background_colour; + if (box->object && width != 0 && height != 0) { + struct content_redraw_data obj_data; - if (plot_colour) - if (!plot->rectangle(r.x0, r.y0, r.x1, r.y1, - &pstyle_fill_bg)) - return false; - } - /* and plot the image */ - if (plot_content) { - int width = content_get_width(box->background); - int height = content_get_height(box->background); + x_scrolled = x - scrollbar_get_offset(box->scroll_x) * scale; + y_scrolled = y - scrollbar_get_offset(box->scroll_y) * scale; - if (!repeat_x) { - if (r.x0 < x) - r.x0 = x; - if (r.x1 > x + width * scale) - r.x1 = x + width * scale; - } - if (!repeat_y) { - if (r.y0 < y) - r.y0 = y; - if (r.y1 > y + height * scale) - r.y1 = y + height * scale; + obj_data.x = x_scrolled + padding_left; + obj_data.y = y_scrolled + padding_top; + obj_data.width = width; + obj_data.height = height; + obj_data.background_colour = current_background_color; + obj_data.scale = scale; + obj_data.repeat_x = false; + obj_data.repeat_y = false; + + if (content_get_type(box->object) == CONTENT_HTML) { + obj_data.x /= scale; + obj_data.y /= scale; } - /* valid clipping rectangles only */ - if ((r.x0 < r.x1) && (r.y0 < r.y1)) { - struct content_redraw_data bg_data; - if (!plot->clip(&r)) + if (!content_redraw(box->object, &obj_data, &r, ctx)) { + /* Show image fail */ + /* Unicode (U+FFFC) 'OBJECT REPLACEMENT CHARACTER' */ + const char *obj = "\xef\xbf\xbc"; + int obj_width; + int obj_x = x + padding_left; + if (!plot->rectangle(x + padding_left, + y + padding_top, + x + padding_left + width - 1, + y + padding_top + height - 1, + plot_style_broken_object)) return false; + if (!nsfont.font_width(plot_fstyle_broken_object, obj, + sizeof(obj) - 1, &obj_width)) + obj_x += 1; + else + obj_x += width / 2 - obj_width / 2; - bg_data.x = x; - bg_data.y = y; - bg_data.width = ceilf(width * scale); - bg_data.height = ceilf(height * scale); - bg_data.background_colour = *background_colour; - bg_data.scale = scale; - bg_data.repeat_x = repeat_x; - bg_data.repeat_y = repeat_y; - - if (!content_redraw(box->background, &bg_data, &r, ctx)) + if (!plot->text(obj_x, y + padding_top + (int) + (height * 0.75), + obj, sizeof(obj) - 1, + plot_fstyle_broken_object)) return false; } - } - - return true; -} + + } else if (box->iframe) { + /* Offset is passed to browser window redraw unscaled */ + browser_window_redraw(box->iframe, + (x + padding_left) / scale, + (y + padding_top) / scale, &r, ctx); -/** - * Plot text decoration for a box. - * - * \param box box to plot decorations for - * \param x_parent x coordinate of parent of box - * \param y_parent y coordinate of parent of box - * \param scale scale for redraw - * \param background_colour current background colour - * \param ctx current redraw context - * \return true if successful, false otherwise - */ + } else if (box->gadget && box->gadget->type == GADGET_CHECKBOX) { + if (!html_redraw_checkbox(x + padding_left, y + padding_top, + width, height, box->gadget->selected, ctx)) + return false; -bool html_redraw_text_decoration(struct box *box, - int x_parent, int y_parent, float scale, - colour background_colour, const struct redraw_context *ctx) -{ - static const enum css_text_decoration_e decoration[] = { - CSS_TEXT_DECORATION_UNDERLINE, CSS_TEXT_DECORATION_OVERLINE, - CSS_TEXT_DECORATION_LINE_THROUGH }; - static const float line_ratio[] = { 0.9, 0.1, 0.5 }; - colour fgcol; - unsigned int i; - css_color col; + } else if (box->gadget && box->gadget->type == GADGET_RADIO) { + if (!html_redraw_radio(x + padding_left, y + padding_top, + width, height, box->gadget->selected, ctx)) + return false; - css_computed_color(box->style, &col); - fgcol = nscss_color_to_ns(col); + } else if (box->gadget && box->gadget->type == GADGET_FILE) { + if (!html_redraw_file(x + padding_left, y + padding_top, + width, height, box, scale, + current_background_color, ctx)) + return false; - /* antialias colour for under/overline */ - if (html_redraw_printing == false) - fgcol = blend_colour(background_colour, fgcol); + } else if (box->text) { + if (!html_redraw_text_box(html, box, x, y, &r, scale, + current_background_color, ctx)) + return false; - if (box->type == BOX_INLINE) { - if (!box->inline_end) - return true; - for (i = 0; i != NOF_ELEMENTS(decoration); i++) - if (css_computed_text_decoration(box->style) & - decoration[i]) - if (!html_redraw_text_decoration_inline(box, - x_parent, y_parent, scale, - fgcol, line_ratio[i], ctx)) - return false; } else { - for (i = 0; i != NOF_ELEMENTS(decoration); i++) - if (css_computed_text_decoration(box->style) & - decoration[i]) - if (!html_redraw_text_decoration_block(box, - x_parent + box->x, - y_parent + box->y, - scale, - fgcol, line_ratio[i], ctx)) - return false; + if (!html_redraw_box_children(html, box, x_parent, y_parent, &r, + scale, current_background_color, ctx)) + return false; } - return true; -} + if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK || + box->type == BOX_TABLE_CELL || box->type == BOX_INLINE) + if (!plot->clip(clip)) + return false; + /* list marker */ + if (box->list_marker) + if (!html_redraw_box(html, box->list_marker, + x_parent + box->x - + scrollbar_get_offset(box->scroll_x), + y_parent + box->y - + scrollbar_get_offset(box->scroll_y), + clip, scale, current_background_color, ctx)) + return false; -/** - * Plot text decoration for an inline box. - * - * \param box box to plot decorations for, of type BOX_INLINE - * \param x x coordinate of parent of box - * \param y y coordinate of parent of box - * \param scale scale for redraw - * \param colour colour for decorations - * \param ratio position of line as a ratio of line height - * \param ctx current redraw context - * \return true if successful, false otherwise - */ + /* scrollbars */ + if (((box->style && box->type != BOX_BR && + box->type != BOX_TABLE && box->type != BOX_INLINE && + (css_computed_overflow(box->style) == + CSS_OVERFLOW_SCROLL || + css_computed_overflow(box->style) == + CSS_OVERFLOW_AUTO)) || (box->object && + content_get_type(box->object) == CONTENT_HTML)) && + box->parent != NULL) { -bool html_redraw_text_decoration_inline(struct box *box, int x, int y, - float scale, colour colour, float ratio, - const struct redraw_context *ctx) -{ - const struct plotter_table *plot = ctx->plot; - struct box *c; - plot_style_t plot_style_box = { - .stroke_type = PLOT_OP_TYPE_SOLID, - .stroke_colour = colour, - }; + has_x_scroll = box_hscrollbar_present(box); + has_y_scroll = box_vscrollbar_present(box); - for (c = box->next; - c && c != box->inline_end; - c = c->next) { - if (c->type != BOX_TEXT) - continue; - if (!plot->line((x + c->x) * scale, - (y + c->y + c->height * ratio) * scale, - (x + c->x + c->width) * scale, - (y + c->y + c->height * ratio) * scale, - &plot_style_box)) + if (!box_handle_scrollbars((struct content *)html, + box, has_x_scroll, has_y_scroll)) return false; + + if (box->scroll_x != NULL) + scrollbar_redraw(box->scroll_x, + x_parent + box->x, + y_parent + box->y + box->padding[TOP] + + box->height + box->padding[BOTTOM] - + SCROLLBAR_WIDTH, clip, scale, ctx); + if (box->scroll_y != NULL) + scrollbar_redraw(box->scroll_y, + x_parent + box->x + box->padding[LEFT] + + box->width + box->padding[RIGHT] - + SCROLLBAR_WIDTH, + y_parent + box->y, clip, scale, ctx); } - return true; -} + if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK || + box->type == BOX_TABLE_CELL || box->type == BOX_INLINE) + if (!plot->clip(clip)) + return false; + + return ((!plot->group_end) || (plot->group_end())); +} /** - * Plot text decoration for an non-inline box. + * Draw a CONTENT_HTML using the current set of plotters (plot). * - * \param box box to plot decorations for, of type other than BOX_INLINE - * \param x x coordinate of box - * \param y y coordinate of box - * \param scale scale for redraw - * \param colour colour for decorations - * \param ratio position of line as a ratio of line height - * \param ctx current redraw context + * \param c content of type CONTENT_HTML + * \param data redraw data for this content redraw + * \param clip current clip region + * \param ctx current redraw context * \return true if successful, false otherwise + * + * x, y, clip_[xy][01] are in target coordinates. */ -bool html_redraw_text_decoration_block(struct box *box, int x, int y, - float scale, colour colour, float ratio, - const struct redraw_context *ctx) +bool html_redraw(struct content *c, struct content_redraw_data *data, + const struct rect *clip, const struct redraw_context *ctx) { - const struct plotter_table *plot = ctx->plot; - struct box *c; - plot_style_t plot_style_box = { - .stroke_type = PLOT_OP_TYPE_SOLID, - .stroke_colour = colour, + html_content *html = (html_content *) c; + struct box *box; + bool result = true; + bool select, select_only; + plot_style_t pstyle_fill_bg = { + .fill_type = PLOT_OP_TYPE_SOLID, + .fill_colour = data->background_colour, }; - /* draw through text descendants */ - for (c = box->children; c; c = c->next) { - if (c->type == BOX_TEXT) { - if (!plot->line((x + c->x) * scale, - (y + c->y + c->height * ratio) * scale, - (x + c->x + c->width) * scale, - (y + c->y + c->height * ratio) * scale, - &plot_style_box)) - return false; - } else if (c->type == BOX_INLINE_CONTAINER || - c->type == BOX_BLOCK) { - if (!html_redraw_text_decoration_block(c, - x + c->x, y + c->y, - scale, colour, ratio, ctx)) - return false; - } + box = html->layout; + assert(box); + + /* The select menu needs special treating because, when opened, it + * reaches beyond its layout box. + */ + select = false; + select_only = false; + if (ctx->interactive && html->visible_select_menu != NULL) { + struct form_control *control = html->visible_select_menu; + select = true; + /* check if the redraw rectangle is completely inside of the + select menu */ + select_only = form_clip_inside_select_menu(control, + data->scale, clip); } - return true; + + if (!select_only) { + /* clear to background colour */ + result = ctx->plot->clip(clip); + + if (html->background_colour != NS_TRANSPARENT) + pstyle_fill_bg.fill_colour = html->background_colour; + + result &= ctx->plot->rectangle(clip->x0, clip->y0, + clip->x1, clip->y1, + &pstyle_fill_bg); + + result &= html_redraw_box(html, box, data->x, data->y, clip, + data->scale, pstyle_fill_bg.fill_colour, ctx); + } + + if (select) { + int menu_x, menu_y; + box = html->visible_select_menu->box; + box_coords(box, &menu_x, &menu_y); + + menu_x -= box->border[LEFT].width; + menu_y += box->height + box->border[BOTTOM].width + + box->padding[BOTTOM] + box->padding[TOP]; + result &= form_redraw_select_menu(html->visible_select_menu, + data->x + menu_x, data->y + menu_y, + data->scale, clip, ctx); + } + + return result; + } -- cgit v1.2.3 From bda7bfa7396f8879ae848f311a740f0854677552 Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Wed, 2 Jan 2013 18:44:08 +0000 Subject: Make scale const. --- render/html_redraw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/render/html_redraw.c b/render/html_redraw.c index 26bc46c98..e305b7b08 100644 --- a/render/html_redraw.c +++ b/render/html_redraw.c @@ -1893,7 +1893,7 @@ static bool html_redraw_box_children(const html_content *html, struct box *box, bool html_redraw_box(const html_content *html, struct box *box, int x_parent, int y_parent, - const struct rect *clip, float scale, + const struct rect *clip, const float scale, colour current_background_color, const struct redraw_context *ctx) { -- cgit v1.2.3 From 1b8f9daa51c901119d4dc27f82fb993fc8378bd0 Mon Sep 17 00:00:00 2001 From: Vincent Sanders Date: Wed, 2 Jan 2013 17:19:32 +0000 Subject: Initial implementation of document.createComment Improve robustness of jsobject to libdom object conversion in appendChild --- Makefile.sources.javascript | 1 + javascript/jsapi/comment.bnd | 47 +++++++++++++ javascript/jsapi/dom.bnd | 31 ++++++--- javascript/jsapi/htmldocument.bnd | 143 +++++++++++++++++++++++--------------- javascript/jsapi/htmlelement.bnd | 4 +- javascript/jsapi/navigator.bnd | 1 - javascript/jsapi/text.bnd | 4 +- javascript/jsapi/window.bnd | 9 ++- 8 files changed, 171 insertions(+), 69 deletions(-) create mode 100644 javascript/jsapi/comment.bnd diff --git a/Makefile.sources.javascript b/Makefile.sources.javascript index 4633e9d0d..bcdd68501 100644 --- a/Makefile.sources.javascript +++ b/Makefile.sources.javascript @@ -20,6 +20,7 @@ JSAPI_BINDING_location := javascript/jsapi/location.bnd JSAPI_BINDING_htmlcollection := javascript/jsapi/htmlcollection.bnd JSAPI_BINDING_nodelist := javascript/jsapi/nodelist.bnd JSAPI_BINDING_text := javascript/jsapi/text.bnd +JSAPI_BINDING_comment := javascript/jsapi/comment.bnd JSAPI_BINDING_node := javascript/jsapi/node.bnd JSAPI_BINDING_event := javascript/jsapi/event.bnd diff --git a/javascript/jsapi/comment.bnd b/javascript/jsapi/comment.bnd new file mode 100644 index 000000000..580f5cbed --- /dev/null +++ b/javascript/jsapi/comment.bnd @@ -0,0 +1,47 @@ +/* Binding to generate Comment interface + * + * Copyright 2012 Vincent Sanders + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * Released under the terms of the MIT License, + * http://www.opensource.org/licenses/mit-license + */ + + +webidlfile "html.idl"; + +hdrcomment "Copyright 2012 Vincent Sanders "; +hdrcomment "This file is part of NetSurf, http://www.netsurf-browser.org/"; +hdrcomment "Released under the terms of the MIT License,"; +hdrcomment " http://www.opensource.org/licenses/mit-license"; + +preamble %{ + +#include + +#include "utils/config.h" +#include "utils/log.h" +#include "render/html_internal.h" +#include "javascript/jsapi.h" + +#include "comment.h" + +%} + +#include "dom.bnd" + +binding comment { + type js_libdom; /* the binding type */ + + interface Comment; /* Web IDL interface to generate */ + + private "dom_comment *" node; + private "struct html_content *" htmlc; +} + +api finalise %{ + if (private != NULL) { + dom_node_unref(private->node); + } +%} diff --git a/javascript/jsapi/dom.bnd b/javascript/jsapi/dom.bnd index e781b330c..b6f7cf440 100644 --- a/javascript/jsapi/dom.bnd +++ b/javascript/jsapi/dom.bnd @@ -10,6 +10,12 @@ webidlfile "dom.idl"; +preamble %{ +#include "comment.h" +#include "text.h" +#include "htmlelement.h" +%} + /* interface Node members */ getter nodeType %{ @@ -74,7 +80,7 @@ getter textContent %{ } %} - +/* interface Node { Node appendChild(Node node); } */ operation appendChild %{ struct dom_node *result = NULL; dom_exception exc; @@ -82,23 +88,32 @@ operation appendChild %{ struct jsclass_private *node_private; dom_node_type node_type; - JSLOG("appending %p", node); - + /* @todo: make this a distinct function jsapiobject_to_domnode() */ /* CAUTION this expects all Node objects private pointers to * have private->node in the same place */ - /* text */ - node_private = JS_GetInstancePrivate(cx, node, &JSClass_Text, NULL); - if (node_private == NULL) { + if (node == NULL) { + node_private = NULL; + } else { /* element */ node_private = JS_GetInstancePrivate(cx, node, &JSClass_HTMLElement, NULL); + if (node_private == NULL) { + /* text */ + node_private = JS_GetInstancePrivate(cx, node, &JSClass_Text, NULL); + if (node_private == NULL) { + /* comment */ + node_private = JS_GetInstancePrivate(cx, node, &JSClass_Comment, NULL); + } + } } - if (node_private == NULL) { - /* type error? */ + /* should cause Error: NOT_FOUND_ERR: DOM Exception 8 */ + JSLOG("Error: NOT_FOUND_ERR: DOM Exception 8"); return JS_FALSE; } + JSLOG("appending %p", node); + /* append the found element */ exc = dom_node_append_child(private->node, node_private->node, &result); if (exc != DOM_NO_ERR) { diff --git a/javascript/jsapi/htmldocument.bnd b/javascript/jsapi/htmldocument.bnd index ddf408a9f..021694e17 100644 --- a/javascript/jsapi/htmldocument.bnd +++ b/javascript/jsapi/htmldocument.bnd @@ -8,8 +8,6 @@ * http://www.opensource.org/licenses/mit-license */ -#include "dom.bnd" - webidlfile "html.idl"; hdrcomment "Copyright 2012 Vincent Sanders "; @@ -20,7 +18,7 @@ hdrcomment " http://www.opensource.org/licenses/mit-license"; preamble %{ #include - + #include "utils/config.h" #include "utils/log.h" #include "utils/corestrings.h" @@ -38,6 +36,8 @@ preamble %{ %} +#include "dom.bnd" + binding document { type js_libdom; /* the binding type */ @@ -47,10 +47,10 @@ binding document { * context structure. */ private "dom_document *" node; - private "struct html_content *" htmlc; + private "struct html_content *" htmlc; /** location instantiated on first use */ - property unshared location; + property unshared location; /* events through a single interface */ property unshared type EventHandler; @@ -70,9 +70,9 @@ getter location %{ /* already created - return it */ return JS_TRUE; } - jsret = jsapi_new_Location(cx, - NULL, - NULL, + jsret = jsapi_new_Location(cx, + NULL, + NULL, llcache_handle_get_url(private->htmlc->base.llcache), private->htmlc); %} @@ -110,7 +110,7 @@ getter documentElement %{ /* document (html) element */ exc = dom_document_get_document_element(private->node, (void *)&element); - if (exc != DOM_NO_ERR) { + if (exc != DOM_NO_ERR) { return JS_FALSE; } @@ -122,11 +122,11 @@ getter documentElement %{ getter head %{ dom_node *element; dom_node *head; - dom_exception exc; + dom_exception exc; /* document (html) element */ exc = dom_document_get_document_element(private->node, &element); - if (exc != DOM_NO_ERR) { + if (exc != DOM_NO_ERR) { return JS_FALSE; } @@ -142,13 +142,13 @@ getter head %{ getter body %{ dom_node *element; dom_node *body; - dom_exception exc; + dom_exception exc; JSLOG("Getting your body"); /* document (html) element */ exc = dom_document_get_document_element(private->node, &element); - if (exc != DOM_NO_ERR) { + if (exc != DOM_NO_ERR) { return JS_FALSE; } @@ -167,58 +167,58 @@ getter body %{ operation getElementById %{ dom_string *elementId_dom; dom_element *element; - dom_exception exc; + dom_exception exc; exc = dom_string_create((unsigned char*)elementId, elementId_len, &elementId_dom); - if (exc != DOM_NO_ERR) { - return JS_FALSE; - } + if (exc != DOM_NO_ERR) { + return JS_FALSE; + } exc = dom_document_get_element_by_id(private->node, elementId_dom, &element); - dom_string_unref(elementId_dom); - if (exc != DOM_NO_ERR) { - return JS_FALSE; - } - - if (element != NULL) { - jsret = jsapi_new_HTMLElement(cx, NULL, NULL, element, private->htmlc); - } + dom_string_unref(elementId_dom); + if (exc != DOM_NO_ERR) { + return JS_FALSE; + } + + if (element != NULL) { + jsret = jsapi_new_HTMLElement(cx, NULL, NULL, element, private->htmlc); + } %} -/* +/* * * Dom 4 says this should return a htmlcollection, libdom currently - * returns DOM 3 spec of a nodelist + * returns DOM 3 spec of a nodelist */ operation getElementsByTagName %{ dom_string *localName_dom; - /* dom_html_collection *collection;*/ - dom_nodelist *nodelist; - dom_exception exc; + /* dom_html_collection *collection;*/ + dom_nodelist *nodelist; + dom_exception exc; exc = dom_string_create((uint8_t *)localName, localName_len, &localName_dom); - if (exc != DOM_NO_ERR) { - return JS_FALSE; - } - - exc = dom_document_get_elements_by_tag_name(private->node, localName_dom, /*&collection*/&nodelist); - dom_string_unref(localName_dom); - if (exc != DOM_NO_ERR) { - return JS_FALSE; - } - - if (/*collection*/nodelist != NULL) { - /*jsret = jsapi_new_HTMLCollection(cx, - NULL, - NULL, - collection, - private->htmlc);*/ - jsret = jsapi_new_NodeList(cx, - NULL, - NULL, - nodelist, - private->htmlc); - } + if (exc != DOM_NO_ERR) { + return JS_FALSE; + } + + exc = dom_document_get_elements_by_tag_name(private->node, localName_dom, /*&collection*/&nodelist); + dom_string_unref(localName_dom); + if (exc != DOM_NO_ERR) { + return JS_FALSE; + } + + if (/*collection*/nodelist != NULL) { + /*jsret = jsapi_new_HTMLCollection(cx, + NULL, + NULL, + collection, + private->htmlc);*/ + jsret = jsapi_new_NodeList(cx, + NULL, + NULL, + nodelist, + private->htmlc); + } %} @@ -228,10 +228,10 @@ operation write %{ } %} -/* in dom Document */ +/* interface Document (dom) { Text createTextNode(DOMString data); } */ operation createTextNode %{ dom_string *data_dom; - dom_exception exc; + dom_exception exc; dom_text *text; if (data != NULL) { @@ -255,10 +255,43 @@ operation createTextNode %{ %} +/* interface Document (dom) { Comment createComment(DOMString data); } */ +operation createComment %{ + dom_string *data_dom; + dom_exception exc; + dom_comment *comment; + + if (data != NULL) { + + JSLOG("Creating string \"%s\"", data); + exc = dom_string_create((unsigned char*)data, + data_len, + &data_dom); + if (exc != DOM_NO_ERR) { + return JS_FALSE; + } + + JSLOG("Creating comment object for dom string \"%s\"", + dom_string_data(comment)); + exc = dom_document_create_comment(private->node, + data_dom, + &comment); + dom_string_unref(data_dom); + if (exc != DOM_NO_ERR) { + return JS_FALSE; + } + + jsret = jsapi_new_Comment(cx, NULL, NULL, comment, private->htmlc); + } + + JSLOG("returning jsobject %p", jsret); + +%} + /* in dom Document */ operation createElement %{ dom_string *localName_dom; - dom_exception exc; + dom_exception exc; dom_element *element; if (localName != NULL) { diff --git a/javascript/jsapi/htmlelement.bnd b/javascript/jsapi/htmlelement.bnd index 48ebbdb64..5e22f7e7d 100644 --- a/javascript/jsapi/htmlelement.bnd +++ b/javascript/jsapi/htmlelement.bnd @@ -8,8 +8,6 @@ * http://www.opensource.org/licenses/mit-license */ -#include "dom.bnd" - webidlfile "html.idl"; hdrcomment "Copyright 2012 Vincent Sanders "; @@ -34,6 +32,8 @@ preamble %{ %} +#include "dom.bnd" + binding htmlelement { type js_libdom; /* the binding type */ diff --git a/javascript/jsapi/navigator.bnd b/javascript/jsapi/navigator.bnd index d040edec2..2fb0c2d0a 100644 --- a/javascript/jsapi/navigator.bnd +++ b/javascript/jsapi/navigator.bnd @@ -8,7 +8,6 @@ * http://www.opensource.org/licenses/mit-license */ -#include "dom.bnd" webidlfile "html.idl"; diff --git a/javascript/jsapi/text.bnd b/javascript/jsapi/text.bnd index 6b4352116..eb17a943e 100644 --- a/javascript/jsapi/text.bnd +++ b/javascript/jsapi/text.bnd @@ -8,7 +8,6 @@ * http://www.opensource.org/licenses/mit-license */ -#include "dom.bnd" webidlfile "html.idl"; @@ -27,10 +26,11 @@ preamble %{ #include "javascript/jsapi.h" #include "text.h" -#include "htmlelement.h" %} +#include "dom.bnd" + binding text { type js_libdom; /* the binding type */ diff --git a/javascript/jsapi/window.bnd b/javascript/jsapi/window.bnd index 288b5b3d8..937c150db 100644 --- a/javascript/jsapi/window.bnd +++ b/javascript/jsapi/window.bnd @@ -8,7 +8,6 @@ * http://www.opensource.org/licenses/mit-license */ -#include "dom.bnd" webidlfile "html.idl"; @@ -35,12 +34,15 @@ preamble %{ #include "nodelist.h" #include "htmldocument.h" #include "text.h" +#include "comment.h" #include "htmlelement.h" #include "window.h" #include "location.h" %} +#include "dom.bnd" + binding window { type js_libdom; /* the binding type */ @@ -150,6 +152,11 @@ api init %{ return NULL; } + user_proto = jsapi_InitClass_Comment(cx, prototype); + if (user_proto == NULL) { + return NULL; + } + user_proto = jsapi_InitClass_Node(cx, prototype); if (user_proto == NULL) { return NULL; -- cgit v1.2.3 From bb10e7131f615b9266d7a1996f561637286e2839 Mon Sep 17 00:00:00 2001 From: Vincent Sanders Date: Wed, 2 Jan 2013 22:43:29 +0000 Subject: use a prologue section in the node binding to abstract out javascrip dom node to libdom node conversion --- javascript/jsapi/dom.bnd | 74 +++++++++++++++++++++++++++------------ javascript/jsapi/htmldocument.bnd | 2 +- javascript/jsapi/window.bnd | 35 +++++++++--------- 3 files changed, 69 insertions(+), 42 deletions(-) diff --git a/javascript/jsapi/dom.bnd b/javascript/jsapi/dom.bnd index b6f7cf440..3fc7f9ed1 100644 --- a/javascript/jsapi/dom.bnd +++ b/javascript/jsapi/dom.bnd @@ -16,6 +16,51 @@ preamble %{ #include "htmlelement.h" %} + +prologue %{ +/* CAUTION this expects all javascript Node objects private pointers + * to have private->node in the same place. + */ +static struct dom_node *jsnode_to_domnode(JSContext *cx, JSObject *jsnode) +{ + struct jsclass_private *jsnode_private; + + if (jsnode == NULL) { + return NULL; + } + + /* element */ + jsnode_private = JS_GetInstancePrivate(cx, + jsnode, + &JSClass_HTMLElement, + NULL); + if (jsnode_private != NULL) { + return (struct dom_node *)jsnode_private->node; + } + + /* text */ + jsnode_private = JS_GetInstancePrivate(cx, + jsnode, + &JSClass_Text, + NULL); + if (jsnode_private != NULL) { + return (struct dom_node *)jsnode_private->node; + } + + /* comment */ + jsnode_private = JS_GetInstancePrivate(cx, + jsnode, + &JSClass_Comment, + NULL); + if (jsnode_private != NULL) { + return (struct dom_node *)jsnode_private->node; + } + + return NULL; +} + +%} + /* interface Node members */ getter nodeType %{ @@ -82,41 +127,24 @@ getter textContent %{ /* interface Node { Node appendChild(Node node); } */ operation appendChild %{ + struct dom_node *domnode; /* dom node from js input node */ struct dom_node *result = NULL; dom_exception exc; - - struct jsclass_private *node_private; dom_node_type node_type; - /* @todo: make this a distinct function jsapiobject_to_domnode() */ - /* CAUTION this expects all Node objects private pointers to - * have private->node in the same place - */ - if (node == NULL) { - node_private = NULL; - } else { - /* element */ - node_private = JS_GetInstancePrivate(cx, node, &JSClass_HTMLElement, NULL); - if (node_private == NULL) { - /* text */ - node_private = JS_GetInstancePrivate(cx, node, &JSClass_Text, NULL); - if (node_private == NULL) { - /* comment */ - node_private = JS_GetInstancePrivate(cx, node, &JSClass_Comment, NULL); - } - } - } - if (node_private == NULL) { + domnode = jsnode_to_domnode(cx, node); + if (domnode == NULL) { /* should cause Error: NOT_FOUND_ERR: DOM Exception 8 */ JSLOG("Error: NOT_FOUND_ERR: DOM Exception 8"); return JS_FALSE; } - JSLOG("appending %p", node); + JSLOG("appending js node %p (dom %p)", node, domnode); /* append the found element */ - exc = dom_node_append_child(private->node, node_private->node, &result); + exc = dom_node_append_child(private->node, domnode, &result); if (exc != DOM_NO_ERR) { + JSLOG("Error: DOM Exception (libdom append child)"); return JS_FALSE; } diff --git a/javascript/jsapi/htmldocument.bnd b/javascript/jsapi/htmldocument.bnd index 021694e17..8d5c69eb5 100644 --- a/javascript/jsapi/htmldocument.bnd +++ b/javascript/jsapi/htmldocument.bnd @@ -272,7 +272,7 @@ operation createComment %{ } JSLOG("Creating comment object for dom string \"%s\"", - dom_string_data(comment)); + dom_string_data(data_dom)); exc = dom_document_create_comment(private->node, data_dom, &comment); diff --git a/javascript/jsapi/window.bnd b/javascript/jsapi/window.bnd index 937c150db..bba1eb7db 100644 --- a/javascript/jsapi/window.bnd +++ b/javascript/jsapi/window.bnd @@ -10,6 +10,7 @@ webidlfile "html.idl"; +webidlfile "dom.idl"; hdrcomment "Copyright 2012 Vincent Sanders "; hdrcomment "This file is part of NetSurf, http://www.netsurf-browser.org/"; @@ -19,7 +20,7 @@ hdrcomment " http://www.opensource.org/licenses/mit-license"; preamble %{ #include - + #include "utils/config.h" #include "utils/log.h" #include "utils/corestrings.h" @@ -41,8 +42,6 @@ preamble %{ %} -#include "dom.bnd" - binding window { type js_libdom; /* the binding type */ @@ -109,7 +108,7 @@ api init %{ return NULL; /* Initialises all the user javascript classes to make their - * prototypes available. + * prototypes available. */ /** @todo should we be managing these prototype objects ourselves */ user_proto = jsapi_InitClass_Document(cx, prototype); @@ -173,16 +172,16 @@ api new %{ /* @todo sort out windows that are not globals */ assert(parent == NULL); - /* the window object is the global so its prototype *is* the instance */ - newobject = prototype; + /* the window object is the global so its prototype *is* the instance */ + newobject = prototype; /* instantiate the subclasses off the window global */ private->document = jsapi_new_Document(cx, - NULL, - newobject, - (dom_document *)dom_node_ref(htmlc->document), - htmlc); - if (private->document == NULL) { + NULL, + newobject, + (dom_document *)dom_node_ref(htmlc->document), + htmlc); + if (private->document == NULL) { free(private); return NULL; } @@ -219,7 +218,7 @@ operation prompt %{ /* boolean dispatchEvent(Event event); */ operation dispatchEvent %{ /* this implementation is unique to the window object as it is - * not a "real" dom node. + * not a "real" dom node. */ /* caution, this must match the struct generated from event.bnd */ @@ -249,7 +248,7 @@ operation dispatchEvent %{ jsret = JS_CallFunctionValue(cx, NULL, eventval, 1, event_argv, &event_rval); } } - } + } %} getter location %{ @@ -268,18 +267,18 @@ getter self %{ getter EventHandler %{ /* this implementation is unique to the window object as it is - * not a dom node. + * not a dom node. */ - JSLOG("propname[%d]=\"%s\"", + JSLOG("propname[%d]=\"%s\"", tinyid, jsclass_properties[tinyid].name); %} setter EventHandler %{ /* this implementation is unique to the window object as it is - * not a dom node. + * not a dom node. */ - JSLOG("propname[%d]=\"%s\"", - tinyid, + JSLOG("propname[%d]=\"%s\"", + tinyid, jsclass_properties[tinyid].name); %} -- cgit v1.2.3 From beffd9e481e9192730266f09bd5c27bf386d0e52 Mon Sep 17 00:00:00 2001 From: Vincent Sanders Date: Wed, 2 Jan 2013 23:13:28 +0000 Subject: search for teh correct script type in the defer callback. Should fix SF bug #3599063 --- render/html_script.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/render/html_script.c b/render/html_script.c index 9edd08cf2..9c14e84ce 100644 --- a/render/html_script.c +++ b/render/html_script.c @@ -212,7 +212,7 @@ convert_script_defer_cb(hlcache_handle *script, /* Find script */ for (i = 0, s = parent->scripts; i != parent->scripts_count; i++, s++) { - if (s->type == HTML_SCRIPT_ASYNC && s->data.handle == script) + if (s->type == HTML_SCRIPT_DEFER && s->data.handle == script) break; } -- cgit v1.2.3 From 924f8844d4e94f56232d70b25a925731ab19a84c Mon Sep 17 00:00:00 2001 From: John-Mark Bell Date: Thu, 3 Jan 2013 00:28:51 +0000 Subject: Treat cookies from HTTP and HTTPS as identical. --- content/urldb.c | 47 +++++++++++++++++++++++++++-------------------- utils/corestrings.c | 3 +++ utils/corestrings.h | 1 + 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/content/urldb.c b/content/urldb.c index e3cc1d73d..050dbf650 100644 --- a/content/urldb.c +++ b/content/urldb.c @@ -2410,9 +2410,9 @@ char *urldb_get_cookie(nsurl *url, bool include_http_only) const char *path; char *ret; lwc_string *scheme; + bool target_is_secure; time_t now; int i; - bool match; assert(url != NULL); @@ -2425,7 +2425,15 @@ char *urldb_get_cookie(nsurl *url, bool include_http_only) if (!p) return NULL; - scheme = p->scheme; + scheme = nsurl_get_component(url, NSURL_SCHEME); + if (scheme == NULL) + scheme = lwc_string_ref(corestring_lwc_http); + + if (lwc_string_caseless_isequal(scheme, corestring_lwc_https, + &target_is_secure) != lwc_error_ok) + return NULL; + + lwc_string_unref(scheme); matched_cookies = malloc(matched_cookies_size * sizeof(struct cookie_internal_data *)); @@ -2484,11 +2492,7 @@ char *urldb_get_cookie(nsurl *url, bool include_http_only) /* cookie has expired => ignore */ continue; - if (c->secure && lwc_string_isequal( - q->scheme, - corestring_lwc_https, - &match) && - match == false) + if (c->secure && target_is_secure == false) /* secure cookie for insecure host. * ignore */ continue; @@ -2523,11 +2527,7 @@ char *urldb_get_cookie(nsurl *url, bool include_http_only) /* cookie has expired => ignore */ continue; - if (c->secure && lwc_string_isequal( - q->scheme, - corestring_lwc_https, - &match) && - match == false) + if (c->secure && target_is_secure == false) /* Secure cookie for insecure server * => ignore */ continue; @@ -2567,10 +2567,7 @@ char *urldb_get_cookie(nsurl *url, bool include_http_only) /* paths don't match => ignore */ continue; - if (c->secure && lwc_string_isequal(p->scheme, - corestring_lwc_https, - &match) && - match == false) + if (c->secure && target_is_secure == false) /* Secure cookie for insecure server * => ignore */ continue; @@ -2601,10 +2598,7 @@ char *urldb_get_cookie(nsurl *url, bool include_http_only) /* paths don't match => ignore */ continue; - if (c->secure && lwc_string_isequal(scheme, - corestring_lwc_https, - &match) && - match == false) + if (c->secure && target_is_secure == false) /* secure cookie for insecure host. ignore */ continue; @@ -2698,6 +2692,19 @@ bool urldb_set_cookie(const char *header, nsurl *url, nsurl *referer) return false; } + /* If HTTPS, store cookie using HTTP */ + if (lwc_string_caseless_isequal(scheme, corestring_lwc_https, + &match) != lwc_error_ok) { + lwc_string_unref(scheme); + nsurl_unref(urlt); + return false; + } + + if (match) { + lwc_string_unref(scheme); + scheme = lwc_string_ref(corestring_lwc_http); + } + path = nsurl_get_component(url, NSURL_PATH); if (path == NULL) { lwc_string_unref(scheme); diff --git a/utils/corestrings.c b/utils/corestrings.c index 9fee96d6e..19ffcd148 100644 --- a/utils/corestrings.c +++ b/utils/corestrings.c @@ -58,6 +58,7 @@ lwc_string *corestring_lwc_head; lwc_string *corestring_lwc_hidden; lwc_string *corestring_lwc_hr; lwc_string *corestring_lwc_html; +lwc_string *corestring_lwc_http; lwc_string *corestring_lwc_https; lwc_string *corestring_lwc_iframe; lwc_string *corestring_lwc_image; @@ -272,6 +273,7 @@ void corestrings_fini(void) CSS_LWC_STRING_UNREF(hidden); CSS_LWC_STRING_UNREF(hr); CSS_LWC_STRING_UNREF(html); + CSS_LWC_STRING_UNREF(http); CSS_LWC_STRING_UNREF(https); CSS_LWC_STRING_UNREF(iframe); CSS_LWC_STRING_UNREF(image); @@ -506,6 +508,7 @@ nserror corestrings_init(void) CSS_LWC_STRING_INTERN(hidden); CSS_LWC_STRING_INTERN(hr); CSS_LWC_STRING_INTERN(html); + CSS_LWC_STRING_INTERN(http); CSS_LWC_STRING_INTERN(https); CSS_LWC_STRING_INTERN(iframe); CSS_LWC_STRING_INTERN(image); diff --git a/utils/corestrings.h b/utils/corestrings.h index 08d254501..74c3faa82 100644 --- a/utils/corestrings.h +++ b/utils/corestrings.h @@ -62,6 +62,7 @@ extern lwc_string *corestring_lwc_head; extern lwc_string *corestring_lwc_hidden; extern lwc_string *corestring_lwc_hr; extern lwc_string *corestring_lwc_html; +extern lwc_string *corestring_lwc_http; extern lwc_string *corestring_lwc_https; extern lwc_string *corestring_lwc_iframe; extern lwc_string *corestring_lwc_image; -- cgit v1.2.3 From 568a9c2b8fdbaf92af6661b8eb4c0efef74a1179 Mon Sep 17 00:00:00 2001 From: John-Mark Bell Date: Thu, 3 Jan 2013 00:33:36 +0000 Subject: Remove junk "http_equiv" string. --- utils/corestrings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/corestrings.c b/utils/corestrings.c index 19ffcd148..4be9871ce 100644 --- a/utils/corestrings.c +++ b/utils/corestrings.c @@ -641,7 +641,7 @@ nserror corestrings_init(void) CSS_DOM_STRING_INTERN(href); CSS_DOM_STRING_INTERN(hreflang); CSS_DOM_STRING_INTERN(hspace); - CSS_DOM_STRING_INTERN(http_equiv); + /* http-equiv: see below */ CSS_DOM_STRING_INTERN(id); CSS_DOM_STRING_INTERN(input); CSS_DOM_STRING_INTERN(invalid); -- cgit v1.2.3 From 0c56340897f5c73b5b6952dd288cdd9839a2b0ac Mon Sep 17 00:00:00 2001 From: John-Mark Bell Date: Thu, 3 Jan 2013 00:41:11 +0000 Subject: Fix typo. --- riscos/scripts/Run | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/riscos/scripts/Run b/riscos/scripts/Run index 5f59b7ffa..d3b9988e8 100644 --- a/riscos/scripts/Run +++ b/riscos/scripts/Run @@ -93,7 +93,7 @@ RMEnsure Iconv 0.11 Error NetSurf requires Iconv 0.11 or later. This can be down | Ensure CryptRandom RMEnsure CryptRandom 0.12 NetSurfRMLoad System:Modules.CryptRand -RMEnsure CryptRandom 0.12 Error NetSurf requires CryptRandom 0.12 or later. This can be downloaded form http://www.riscos.info/index.php/CryptRandom +RMEnsure CryptRandom 0.12 Error NetSurf requires CryptRandom 0.12 or later. This can be downloaded from http://www.riscos.info/index.php/CryptRandom | Disable SpecialFX, if present Set NetSurf$SpecialFX 1 -- cgit v1.2.3 From 4aadb5237a27dee82c6660294d4e76aa0d17503e Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Thu, 3 Jan 2013 12:48:09 +0000 Subject: Ensure selection related keypresses go to whatever has claimed input. --- desktop/textinput.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/desktop/textinput.c b/desktop/textinput.c index 8efc71963..36daa374d 100644 --- a/desktop/textinput.c +++ b/desktop/textinput.c @@ -127,6 +127,12 @@ bool browser_window_key_press(struct browser_window *bw, uint32_t key) assert(bw->window != NULL); + if (focus->caret_callback) { + /* Pass keypress onto anything that has claimed input focus */ + return focus->caret_callback(focus, key, + focus->caret_p1, focus->caret_p2); + } + /* keys that take effect wherever the caret is positioned */ switch (key) { case KEY_SELECT_ALL: @@ -151,12 +157,7 @@ bool browser_window_key_press(struct browser_window *bw, uint32_t key) return false; } - /* pass on to the appropriate field */ - if (!focus->caret_callback) - return false; - - return focus->caret_callback(focus, key, - focus->caret_p1, focus->caret_p2); + return false; } -- cgit v1.2.3 From 131b4cdda14632f6c1c59d896582d509d1e04b09 Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Thu, 3 Jan 2013 15:25:59 +0000 Subject: For now selection clear and selection copy are handled by the bw. Select all is only handled by the bw if nothing has claimed input. This stops the crash when select all is used in textarea. TODO: The special keys should not be handled by the bw, they should be handled by the content with focus. --- desktop/textinput.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/desktop/textinput.c b/desktop/textinput.c index 36daa374d..4b3d0b6ac 100644 --- a/desktop/textinput.c +++ b/desktop/textinput.c @@ -127,18 +127,8 @@ bool browser_window_key_press(struct browser_window *bw, uint32_t key) assert(bw->window != NULL); - if (focus->caret_callback) { - /* Pass keypress onto anything that has claimed input focus */ - return focus->caret_callback(focus, key, - focus->caret_p1, focus->caret_p2); - } - - /* keys that take effect wherever the caret is positioned */ + /* safe keys that can be handled whether input claimed or not */ switch (key) { - case KEY_SELECT_ALL: - selection_select_all(bw->cur_sel); - return true; - case KEY_COPY_SELECTION: gui_copy_to_clipboard(bw->cur_sel); return true; @@ -157,6 +147,19 @@ bool browser_window_key_press(struct browser_window *bw, uint32_t key) return false; } + if (focus->caret_callback) { + /* Pass keypress onto anything that has claimed input focus */ + return focus->caret_callback(focus, key, + focus->caret_p1, focus->caret_p2); + } + + /* keys we can't handle here if cursor is in form */ + switch (key) { + case KEY_SELECT_ALL: + selection_select_all(bw->cur_sel); + return true; + } + return false; } -- cgit v1.2.3 From 625012a8692aef7fd6a6f7cd0b86680f31047a91 Mon Sep 17 00:00:00 2001 From: Chris Young Date: Thu, 3 Jan 2013 19:26:51 +0000 Subject: Set the busy pointer during redraws --- amiga/gui.c | 14 ++++++++++++++ amiga/menu.c | 1 - 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/amiga/gui.c b/amiga/gui.c index e1fc19d7f..b4dca1727 100755 --- a/amiga/gui.c +++ b/amiga/gui.c @@ -3600,6 +3600,11 @@ void ami_do_redraw_tiled(struct gui_window_2 *gwin, int tile_x_scale = (int)(nsoption_int(redraw_tile_size_x) / gwin->bw->scale); int tile_y_scale = (int)(nsoption_int(redraw_tile_size_y) / gwin->bw->scale); + SetWindowPointer(gwin->win, + WA_BusyPointer, TRUE, + WA_PointerDelay, TRUE, + TAG_DONE); + browserglob.shared_pens = &gwin->shared_pens; if(top < 0) { @@ -3667,6 +3672,8 @@ void ami_do_redraw_tiled(struct gui_window_2 *gwin, } } } + + SetWindowPointer(gwin->win, TAG_DONE); } @@ -3846,11 +3853,18 @@ void ami_do_redraw(struct gui_window_2 *g) clip.x1 = bbox->Left + bbox->Width; clip.y1 = bbox->Top + bbox->Height; + SetWindowPointer(g->win, + WA_BusyPointer, TRUE, + WA_PointerDelay, TRUE, + TAG_DONE); + if(browser_window_redraw(g->bw, clip.x0 - hcurrent, clip.y0 - vcurrent, &clip, &ctx)) { ami_clearclipreg(&browserglob); browserglob.rp = temprp; } + + SetWindowPointer(g->win, TAG_DONE); } } diff --git a/amiga/menu.c b/amiga/menu.c index 58a4d5115..3ecfbb16a 100755 --- a/amiga/menu.c +++ b/amiga/menu.c @@ -341,7 +341,6 @@ void ami_init_menulabs(struct gui_window_2 *gwin) gwin->menutype[41] = NM_ITEM; gwin->menulab[41] = ami_utf8_easy((char *)messages_get("EnableJS")); gwin->menu_hook[41].h_Entry = (HOOKFUNC)ami_menu_item_browser_enablejs; - gwin->menukey[41] = 'J'; gwin->menutype[42] = NM_ITEM; gwin->menulab[42] = NM_BARLABEL; -- cgit v1.2.3 From dfc2fe6f970b50fc55b00b41e4c178fad2544770 Mon Sep 17 00:00:00 2001 From: Chris Young Date: Thu, 3 Jan 2013 19:32:21 +0000 Subject: gui_window_2 var should be gwin for consistency --- amiga/gui.c | 94 ++++++++++++++++++++++++++++++------------------------------- 1 file changed, 46 insertions(+), 48 deletions(-) diff --git a/amiga/gui.c b/amiga/gui.c index b4dca1727..1221a2cc0 100755 --- a/amiga/gui.c +++ b/amiga/gui.c @@ -3745,87 +3745,85 @@ void gui_window_update_box(struct gui_window *g, const struct rect *rect) rect->x1, rect->y1); } -void ami_do_redraw(struct gui_window_2 *g) +void ami_do_redraw(struct gui_window_2 *gwin) { struct Region *reg = NULL; struct Rectangle rect; hlcache_handle *c; ULONG hcurrent,vcurrent,xoffset,yoffset,width=800,height=600,x0=0,y0=0; struct IBox *bbox; - ULONG oldh=g->oldh,oldv=g->oldv; + ULONG oldh = gwin->oldh, oldv=gwin->oldv; bool morescroll = false; struct RastPort *temprp; - if(browser_window_redraw_ready(g->bw) == false) return; + if(browser_window_redraw_ready(gwin->bw) == false) return; - GetAttr(SPACE_AreaBox, (Object *)g->objects[GID_BROWSER], (ULONG *)&bbox); - ami_get_hscroll_pos(g, (ULONG *)&hcurrent); - ami_get_vscroll_pos(g, (ULONG *)&vcurrent); + GetAttr(SPACE_AreaBox, (Object *)gwin->objects[GID_BROWSER], (ULONG *)&bbox); + ami_get_hscroll_pos(gwin, (ULONG *)&hcurrent); + ami_get_vscroll_pos(gwin, (ULONG *)&vcurrent); - g->bw->window->scrollx = hcurrent; - g->bw->window->scrolly = vcurrent; + gwin->bw->window->scrollx = hcurrent; + gwin->bw->window->scrolly = vcurrent; - c = g->bw->current_content; + c = gwin->bw->current_content; width=bbox->Width; height=bbox->Height; xoffset=bbox->Left; yoffset=bbox->Top; - if(g->bw->reformat_pending) + if(gwin->bw->reformat_pending) { - browser_window_reformat(g->bw,false,width,height); - g->bw->reformat_pending = false; - g->redraw_scroll = false; + browser_window_reformat(gwin->bw, false, width, height); + gwin->bw->reformat_pending = false; + gwin->redraw_scroll = false; } - if(g->redraw_scroll) + if(gwin->redraw_scroll) { if((abs(vcurrent-oldv) > height) || (abs(hcurrent-oldh) > width)) - g->redraw_scroll = false; + gwin->redraw_scroll = false; - if(g->new_content) g->redraw_scroll = false; - - //if(g->bw->scale != 1.0) g->redraw_scroll = false; + if(gwin->new_content) gwin->redraw_scroll = false; } - if(g->redraw_scroll) + if(gwin->redraw_scroll) { - g->bw->window->c_h_temp = g->bw->window->c_h; - gui_window_remove_caret(g->bw->window); + gwin->bw->window->c_h_temp = gwin->bw->window->c_h; + gui_window_remove_caret(gwin->bw->window); - ScrollWindowRaster(g->win,hcurrent-oldh,vcurrent-oldv, - xoffset,yoffset,xoffset+width-1,yoffset+height-1); + ScrollWindowRaster(gwin->win, hcurrent - oldh, vcurrent - oldv, + xoffset, yoffset, xoffset + width - 1, yoffset + height - 1); - g->bw->window->c_h = g->bw->window->c_h_temp; + gwin->bw->window->c_h = gwin->bw->window->c_h_temp; if(vcurrent>oldv) /* Going down */ { - ami_do_redraw_limits(g->bw->window, g->bw, - hcurrent, (height / g->bw->scale) + oldv - 1, - hcurrent + (width / g->bw->scale), - vcurrent + (height / g->bw->scale) + 1); + ami_do_redraw_limits(gwin->bw->window, gwin->bw, + hcurrent, (height / gwin->bw->scale) + oldv - 1, + hcurrent + (width / gwin->bw->scale), + vcurrent + (height / gwin->bw->scale) + 1); } else if(vcurrentbw->window, g->bw, + ami_do_redraw_limits(gwin->bw->window, gwin->bw, hcurrent, vcurrent, - hcurrent + (width / g->bw->scale), + hcurrent + (width / gwin->bw->scale), oldv); } if(hcurrent>oldh) /* Going right */ { - ami_do_redraw_limits(g->bw->window, g->bw, - (width / g->bw->scale) + oldh , vcurrent, - hcurrent + (width / g->bw->scale), - vcurrent + (height / g->bw->scale)); + ami_do_redraw_limits(gwin->bw->window, gwin->bw, + (width / gwin->bw->scale) + oldh , vcurrent, + hcurrent + (width / gwin->bw->scale), + vcurrent + (height / gwin->bw->scale)); } else if(hcurrentbw->window, g->bw, + ami_do_redraw_limits(gwin->bw->window, gwin->bw, hcurrent, vcurrent, - oldh, vcurrent + (height / g->bw->scale)); + oldh, vcurrent + (height / gwin->bw->scale)); } } else @@ -3841,41 +3839,41 @@ void ami_do_redraw(struct gui_window_2 *g) if(nsoption_bool(direct_render) == false) { - ami_do_redraw_tiled(g, hcurrent, vcurrent, width, height, hcurrent, vcurrent, bbox, &ctx); + ami_do_redraw_tiled(gwin, hcurrent, vcurrent, width, height, hcurrent, vcurrent, bbox, &ctx); } else { - browserglob.shared_pens = &g->shared_pens; + browserglob.shared_pens = &gwin->shared_pens; temprp = browserglob.rp; - browserglob.rp = g->win->RPort; + browserglob.rp = gwin->win->RPort; clip.x0 = bbox->Left; clip.y0 = bbox->Top; clip.x1 = bbox->Left + bbox->Width; clip.y1 = bbox->Top + bbox->Height; - SetWindowPointer(g->win, + SetWindowPointer(gwin->win, WA_BusyPointer, TRUE, WA_PointerDelay, TRUE, TAG_DONE); - if(browser_window_redraw(g->bw, clip.x0 - hcurrent, clip.y0 - vcurrent, &clip, &ctx)) + if(browser_window_redraw(gwin->bw, clip.x0 - hcurrent, clip.y0 - vcurrent, &clip, &ctx)) { ami_clearclipreg(&browserglob); browserglob.rp = temprp; } - SetWindowPointer(g->win, TAG_DONE); + SetWindowPointer(gwin->win, TAG_DONE); } } - ami_update_buttons(g); + ami_update_buttons(gwin); - g->oldh = hcurrent; - g->oldv = vcurrent; + gwin->oldh = hcurrent; + gwin->oldv = vcurrent; - g->redraw_scroll = false; - g->redraw_required = false; - g->new_content = false; + gwin->redraw_scroll = false; + gwin->redraw_required = false; + gwin->new_content = false; } void ami_refresh_window(struct gui_window_2 *gwin) -- cgit v1.2.3 From 07024b05c4be91498245a3066fc3365259acf8bd Mon Sep 17 00:00:00 2001 From: Chris Young Date: Thu, 3 Jan 2013 19:53:20 +0000 Subject: Change the mouse pointer back to what it was (and what NetSurf thinks it is) instead of to the default pointer. Additionally don't use ami_update_pointer for treeview windows as AmigaOS has a pointer setting per window, but NetSurf is only storing the current one as a single global variable. --- amiga/arexx.c | 9 +++++++-- amiga/download.c | 9 +++++++-- amiga/drag.c | 10 +++++----- amiga/file.c | 4 ++-- amiga/gui.c | 8 +++++--- amiga/menu.c | 8 ++++---- amiga/theme.c | 12 +++++++++--- amiga/theme.h | 3 ++- amiga/tree.c | 16 +++++++++++----- 9 files changed, 52 insertions(+), 27 deletions(-) diff --git a/amiga/arexx.c b/amiga/arexx.c index c3279ad3d..154f69703 100755 --- a/amiga/arexx.c +++ b/amiga/arexx.c @@ -275,7 +275,12 @@ STATIC VOID rx_save(struct ARexxCmd *cmd, struct RexxMsg *rxm __attribute__((unu if(!bw) return; - ami_update_pointer(bw->window->shared->win,GUI_POINTER_WAIT); + /* Set the busy pointer. We intentionally don't use ami_update_pointer here. */ + SetWindowPointer(bw->window->shared->win, + WA_BusyPointer, TRUE, + WA_PointerDelay, TRUE, + TAG_DONE); + if(fh = FOpen((char *)cmd->ac_ArgList[0], MODE_NEWFILE, 0)) { if(source_data = content_get_source_data(bw->current_content, &source_size)) @@ -285,7 +290,7 @@ STATIC VOID rx_save(struct ARexxCmd *cmd, struct RexxMsg *rxm __attribute__((unu SetComment((char *)cmd->ac_ArgList[0], nsurl_access(hlcache_handle_get_url(bw->current_content))); } - ami_update_pointer(bw->window->shared->win,GUI_POINTER_DEFAULT); + ami_reset_pointer(bw->window->shared->win); } STATIC VOID rx_quit(struct ARexxCmd *cmd, struct RexxMsg *rxm __attribute__((unused))) diff --git a/amiga/download.c b/amiga/download.c index c0c88bb0b..37fc79e28 100644 --- a/amiga/download.c +++ b/amiga/download.c @@ -356,7 +356,12 @@ gui_window_save_link(struct gui_window *g, const char *url, const char *title) { strlcpy(fname, savereq->fr_Drawer, 1024); AddPart(fname,savereq->fr_File,1024); - ami_update_pointer(g->shared->win,GUI_POINTER_WAIT); + + /* Set the busy pointer. We intentionally don't use ami_update_pointer here. */ + SetWindowPointer(g->shared->win, + WA_BusyPointer, TRUE, + WA_PointerDelay, TRUE, + TAG_DONE); if(ami_download_check_overwrite(fname, g->shared->win, 0)) { @@ -383,7 +388,7 @@ gui_window_save_link(struct gui_window *g, const char *url, const char *title) } FreeVec(linkname); } - ami_update_pointer(g->shared->win,GUI_POINTER_DEFAULT); + ami_reset_pointer(g->shared->win); } } diff --git a/amiga/drag.c b/amiga/drag.c index e19b27393..20dbc78e3 100644 --- a/amiga/drag.c +++ b/amiga/drag.c @@ -142,7 +142,7 @@ void ami_drag_save(struct Window *win) return; } - ami_update_pointer(win,GUI_POINTER_WAIT); + ami_update_pointer(win, GUI_POINTER_WAIT, false); switch(drag_save) { @@ -189,7 +189,7 @@ void ami_drag_save(struct Window *win) drag_save = 0; drag_save_data = NULL; - ami_update_pointer(win,GUI_POINTER_DEFAULT); + ami_update_pointer(win, GUI_POINTER_DEFAULT, false); } void ami_drag_icon_show(struct Window *win, const char *type) @@ -205,12 +205,12 @@ void ami_drag_icon_show(struct Window *win, const char *type) if(nsoption_bool(drag_save_icons) == false) { - ami_update_pointer(win, AMI_GUI_POINTER_DRAG); + ami_update_pointer(win, AMI_GUI_POINTER_DRAG, false); return; } else { - ami_update_pointer(win, GUI_POINTER_DEFAULT); + ami_update_pointer(win, GUI_POINTER_DEFAULT, false); } if(!strcmp(type, "drawer")) deftype = WBDRAWER; @@ -266,7 +266,7 @@ void ami_drag_icon_move(void) void ami_drag_icon_close(struct Window *win) { if(drag_icon) CloseWindow(drag_icon); - if(win) ami_update_pointer(win, GUI_POINTER_DEFAULT); + if(win) ami_update_pointer(win, GUI_POINTER_DEFAULT, false); drag_icon = NULL; drag_in_progress = FALSE; } diff --git a/amiga/file.c b/amiga/file.c index 30a63b615..161aabdf3 100644 --- a/amiga/file.c +++ b/amiga/file.c @@ -141,7 +141,7 @@ void ami_file_save(int type, char *fname, struct Window *win, struct bitmap *bm; BPTR fh=0; - ami_update_pointer(win, GUI_POINTER_WAIT); + ami_update_pointer(win, GUI_POINTER_WAIT, false); if(ami_download_check_overwrite(fname, win, 0)) { @@ -200,7 +200,7 @@ void ami_file_save(int type, char *fname, struct Window *win, if(object) SetComment(fname, nsurl_access(hlcache_handle_get_url(object))); } - ami_update_pointer(win, GUI_POINTER_DEFAULT); + ami_update_pointer(win, GUI_POINTER_DEFAULT, false); } void ami_file_save_req(int type, struct gui_window_2 *gwin, diff --git a/amiga/gui.c b/amiga/gui.c index 1221a2cc0..0c31c7f68 100755 --- a/amiga/gui.c +++ b/amiga/gui.c @@ -1435,7 +1435,7 @@ void ami_handle_msg(void) { ami_context_menu_mouse_trap(gwin, FALSE); - if(!gwin->mouse_state) ami_update_pointer(gwin->win,GUI_POINTER_DEFAULT); + if(!gwin->mouse_state) ami_update_pointer(gwin->win, GUI_POINTER_DEFAULT, false); } break; @@ -3600,6 +3600,7 @@ void ami_do_redraw_tiled(struct gui_window_2 *gwin, int tile_x_scale = (int)(nsoption_int(redraw_tile_size_x) / gwin->bw->scale); int tile_y_scale = (int)(nsoption_int(redraw_tile_size_y) / gwin->bw->scale); + /* Set the busy pointer. We intentionally don't use ami_update_pointer here. */ SetWindowPointer(gwin->win, WA_BusyPointer, TRUE, WA_PointerDelay, TRUE, @@ -3673,7 +3674,7 @@ void ami_do_redraw_tiled(struct gui_window_2 *gwin, } } - SetWindowPointer(gwin->win, TAG_DONE); + ami_reset_pointer(gwin->win); } @@ -3851,6 +3852,7 @@ void ami_do_redraw(struct gui_window_2 *gwin) clip.x1 = bbox->Left + bbox->Width; clip.y1 = bbox->Top + bbox->Height; + /* Set the busy pointer. We intentionally don't use ami_update_pointer here. */ SetWindowPointer(gwin->win, WA_BusyPointer, TRUE, WA_PointerDelay, TRUE, @@ -3862,7 +3864,7 @@ void ami_do_redraw(struct gui_window_2 *gwin) browserglob.rp = temprp; } - SetWindowPointer(gwin->win, TAG_DONE); + ami_reset_pointer(gwin->win); } } diff --git a/amiga/menu.c b/amiga/menu.c index 3ecfbb16a..c3b191e40 100755 --- a/amiga/menu.c +++ b/amiga/menu.c @@ -753,9 +753,9 @@ static void ami_menu_item_project_print(struct Hook *hook, APTR window, struct I struct gui_window_2 *gwin; GetAttr(WINDOW_UserData, (Object *)window, (ULONG *)&gwin); - ami_update_pointer(gwin->win,GUI_POINTER_WAIT); + ami_update_pointer(gwin->win, GUI_POINTER_WAIT, false); ami_print_ui(gwin->bw->current_content); - ami_update_pointer(gwin->win,GUI_POINTER_DEFAULT); + ami_update_pointer(gwin->win, GUI_POINTER_DEFAULT, false); } static void ami_menu_item_project_about(struct Hook *hook, APTR window, struct IntuiMessage *msg) @@ -766,7 +766,7 @@ static void ami_menu_item_project_about(struct Hook *hook, APTR window, struct I GetAttr(WINDOW_UserData, (Object *)window, (ULONG *)&gwin); - ami_update_pointer(gwin->win,GUI_POINTER_WAIT); + ami_update_pointer(gwin->win, GUI_POINTER_WAIT, false); temp = ASPrintf("%s|%s|%s", messages_get("OK"), messages_get("HelpCredits"), @@ -800,7 +800,7 @@ static void ami_menu_item_project_about(struct Hook *hook, APTR window, struct I else if(sel == 0) browser_window_create("about:licence", NULL, 0, true, false); - ami_update_pointer(gwin->win,GUI_POINTER_DEFAULT); + ami_update_pointer(gwin->win, GUI_POINTER_DEFAULT, false); } static void ami_menu_item_project_quit(struct Hook *hook, APTR window, struct IntuiMessage *msg) diff --git a/amiga/theme.c b/amiga/theme.c index fce69eda3..52462c03c 100644 --- a/amiga/theme.c +++ b/amiga/theme.c @@ -174,12 +174,18 @@ void ami_get_theme_filename(char *filename, char *themestring, bool protocol) void gui_window_set_pointer(struct gui_window *g, gui_pointer_shape shape) { - ami_update_pointer(g->shared->win,shape); + ami_update_pointer(g->shared->win, shape, false); } -void ami_update_pointer(struct Window *win, gui_pointer_shape shape) +/* reset the mouse pointer back to what NetSurf last set it as */ +void ami_reset_pointer(struct Window *win) { - if(mouseptrcurrent == shape) return; + ami_update_pointer(win, mouseptrcurrent, true); +} + +void ami_update_pointer(struct Window *win, gui_pointer_shape shape, bool reapply) +{ + if((mouseptrcurrent == shape) && (reapply == false)) return; if(drag_save_data) return; if(nsoption_bool(use_os_pointers)) diff --git a/amiga/theme.h b/amiga/theme.h index ba1295d61..52a21bb1a 100644 --- a/amiga/theme.h +++ b/amiga/theme.h @@ -35,5 +35,6 @@ void ami_update_throbber(struct gui_window_2 *g,bool redraw); void ami_init_mouse_pointers(void); void ami_mouse_pointers_free(void); -void ami_update_pointer(struct Window *win, gui_pointer_shape shape); +void ami_update_pointer(struct Window *win, gui_pointer_shape shape, bool reapply); +void ami_reset_pointer(struct Window *win); #endif diff --git a/amiga/tree.c b/amiga/tree.c index 95a68dc20..e3224ff53 100755 --- a/amiga/tree.c +++ b/amiga/tree.c @@ -991,12 +991,15 @@ BOOL ami_tree_event(struct treeview_window *twin) { strlcpy(fname,savereq->fr_Drawer,1024); AddPart(fname,savereq->fr_File,1024); - ami_update_pointer(twin->win,GUI_POINTER_WAIT); + SetWindowPointer(twin->win, + WA_BusyPointer, TRUE, + WA_PointerDelay, TRUE, + TAG_DONE); if(twin->type == AMI_TREE_HISTORY) history_global_export(fname); else if(twin->type == AMI_TREE_HOTLIST) hotlist_export(fname); - ami_update_pointer(twin->win,GUI_POINTER_DEFAULT); + SetWindowPointer(twin->win, TAG_DONE); } break; @@ -1237,9 +1240,12 @@ void ami_tree_redraw_request(int x, int y, int width, int height, void *data) }; if(!twin->win) return; -// if(tree_get_redraw(twin->tree) == false) return; - ami_update_pointer(twin->win, GUI_POINTER_WAIT); + SetWindowPointer(twin->win, + WA_BusyPointer, TRUE, + WA_PointerDelay, TRUE, + TAG_DONE); + glob = &twin->globals; GetAttr(SPACE_AreaBox,twin->objects[GID_BROWSER],(ULONG *)&bbox); @@ -1286,6 +1292,6 @@ void ami_tree_redraw_request(int x, int y, int width, int height, void *data) } } - ami_update_pointer(twin->win, GUI_POINTER_DEFAULT); + SetWindowPointer(twin->win, TAG_DONE); glob = &browserglob; } -- cgit v1.2.3 From d0d3d31e97c3d8e23be983243fb29e30accb4c86 Mon Sep 17 00:00:00 2001 From: John-Mark Bell Date: Fri, 4 Jan 2013 22:01:15 +0000 Subject: Revert "Treat cookies from HTTP and HTTPS as identical." Sadly, this breaks path cookies on HTTPS sites. The correct fix is to implement RFC6265 in full (probably replacing urldb with something less complex, too). This reverts commit 924f8844d4e94f56232d70b25a925731ab19a84c. --- content/urldb.c | 47 ++++++++++++++++++++--------------------------- utils/corestrings.c | 3 --- utils/corestrings.h | 1 - 3 files changed, 20 insertions(+), 31 deletions(-) diff --git a/content/urldb.c b/content/urldb.c index 050dbf650..e3cc1d73d 100644 --- a/content/urldb.c +++ b/content/urldb.c @@ -2410,9 +2410,9 @@ char *urldb_get_cookie(nsurl *url, bool include_http_only) const char *path; char *ret; lwc_string *scheme; - bool target_is_secure; time_t now; int i; + bool match; assert(url != NULL); @@ -2425,15 +2425,7 @@ char *urldb_get_cookie(nsurl *url, bool include_http_only) if (!p) return NULL; - scheme = nsurl_get_component(url, NSURL_SCHEME); - if (scheme == NULL) - scheme = lwc_string_ref(corestring_lwc_http); - - if (lwc_string_caseless_isequal(scheme, corestring_lwc_https, - &target_is_secure) != lwc_error_ok) - return NULL; - - lwc_string_unref(scheme); + scheme = p->scheme; matched_cookies = malloc(matched_cookies_size * sizeof(struct cookie_internal_data *)); @@ -2492,7 +2484,11 @@ char *urldb_get_cookie(nsurl *url, bool include_http_only) /* cookie has expired => ignore */ continue; - if (c->secure && target_is_secure == false) + if (c->secure && lwc_string_isequal( + q->scheme, + corestring_lwc_https, + &match) && + match == false) /* secure cookie for insecure host. * ignore */ continue; @@ -2527,7 +2523,11 @@ char *urldb_get_cookie(nsurl *url, bool include_http_only) /* cookie has expired => ignore */ continue; - if (c->secure && target_is_secure == false) + if (c->secure && lwc_string_isequal( + q->scheme, + corestring_lwc_https, + &match) && + match == false) /* Secure cookie for insecure server * => ignore */ continue; @@ -2567,7 +2567,10 @@ char *urldb_get_cookie(nsurl *url, bool include_http_only) /* paths don't match => ignore */ continue; - if (c->secure && target_is_secure == false) + if (c->secure && lwc_string_isequal(p->scheme, + corestring_lwc_https, + &match) && + match == false) /* Secure cookie for insecure server * => ignore */ continue; @@ -2598,7 +2601,10 @@ char *urldb_get_cookie(nsurl *url, bool include_http_only) /* paths don't match => ignore */ continue; - if (c->secure && target_is_secure == false) + if (c->secure && lwc_string_isequal(scheme, + corestring_lwc_https, + &match) && + match == false) /* secure cookie for insecure host. ignore */ continue; @@ -2692,19 +2698,6 @@ bool urldb_set_cookie(const char *header, nsurl *url, nsurl *referer) return false; } - /* If HTTPS, store cookie using HTTP */ - if (lwc_string_caseless_isequal(scheme, corestring_lwc_https, - &match) != lwc_error_ok) { - lwc_string_unref(scheme); - nsurl_unref(urlt); - return false; - } - - if (match) { - lwc_string_unref(scheme); - scheme = lwc_string_ref(corestring_lwc_http); - } - path = nsurl_get_component(url, NSURL_PATH); if (path == NULL) { lwc_string_unref(scheme); diff --git a/utils/corestrings.c b/utils/corestrings.c index 4be9871ce..65666df66 100644 --- a/utils/corestrings.c +++ b/utils/corestrings.c @@ -58,7 +58,6 @@ lwc_string *corestring_lwc_head; lwc_string *corestring_lwc_hidden; lwc_string *corestring_lwc_hr; lwc_string *corestring_lwc_html; -lwc_string *corestring_lwc_http; lwc_string *corestring_lwc_https; lwc_string *corestring_lwc_iframe; lwc_string *corestring_lwc_image; @@ -273,7 +272,6 @@ void corestrings_fini(void) CSS_LWC_STRING_UNREF(hidden); CSS_LWC_STRING_UNREF(hr); CSS_LWC_STRING_UNREF(html); - CSS_LWC_STRING_UNREF(http); CSS_LWC_STRING_UNREF(https); CSS_LWC_STRING_UNREF(iframe); CSS_LWC_STRING_UNREF(image); @@ -508,7 +506,6 @@ nserror corestrings_init(void) CSS_LWC_STRING_INTERN(hidden); CSS_LWC_STRING_INTERN(hr); CSS_LWC_STRING_INTERN(html); - CSS_LWC_STRING_INTERN(http); CSS_LWC_STRING_INTERN(https); CSS_LWC_STRING_INTERN(iframe); CSS_LWC_STRING_INTERN(image); diff --git a/utils/corestrings.h b/utils/corestrings.h index 74c3faa82..08d254501 100644 --- a/utils/corestrings.h +++ b/utils/corestrings.h @@ -62,7 +62,6 @@ extern lwc_string *corestring_lwc_head; extern lwc_string *corestring_lwc_hidden; extern lwc_string *corestring_lwc_hr; extern lwc_string *corestring_lwc_html; -extern lwc_string *corestring_lwc_http; extern lwc_string *corestring_lwc_https; extern lwc_string *corestring_lwc_iframe; extern lwc_string *corestring_lwc_image; -- cgit v1.2.3 From 3dff750ae22185c2ee7bb6f5bc7e06d84267b9b9 Mon Sep 17 00:00:00 2001 From: John-Mark Bell Date: Fri, 4 Jan 2013 23:13:23 +0000 Subject: Downgrade TLS version support if it turns out the server can't cope with TLSv1.1. --- beos/fetch_rsrc.cpp | 2 +- content/fetch.c | 8 ++++--- content/fetch.h | 27 ++++++++++++----------- content/fetchers/about.c | 1 + content/fetchers/curl.c | 37 +++++++++++++++++++++++-------- content/fetchers/data.c | 2 +- content/fetchers/file.c | 1 + content/fetchers/resource.c | 1 + content/llcache.c | 53 +++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 105 insertions(+), 27 deletions(-) diff --git a/beos/fetch_rsrc.cpp b/beos/fetch_rsrc.cpp index f7c99d7c4..6f78aafa5 100644 --- a/beos/fetch_rsrc.cpp +++ b/beos/fetch_rsrc.cpp @@ -85,7 +85,7 @@ static bool fetch_rsrc_can_fetch(const nsurl *url) } static void *fetch_rsrc_setup(struct fetch *parent_fetch, nsurl *url, - bool only_2xx, const char *post_urlenc, + bool only_2xx, bool downgrade_tls, const char *post_urlenc, const struct fetch_multipart_data *post_multipart, const char **headers) { diff --git a/content/fetch.c b/content/fetch.c index 028fe9ee4..bfe4458cf 100644 --- a/content/fetch.c +++ b/content/fetch.c @@ -235,7 +235,8 @@ struct fetch * fetch_start(nsurl *url, nsurl *referer, fetch_callback callback, void *p, bool only_2xx, const char *post_urlenc, const struct fetch_multipart_data *post_multipart, - bool verifiable, const char *headers[]) + bool verifiable, bool downgrade_tls, + const char *headers[]) { struct fetch *fetch; scheme_fetcher *fetcher = fetchers; @@ -321,8 +322,9 @@ struct fetch * fetch_start(nsurl *url, nsurl *referer, /* Got a scheme fetcher, try and set up the fetch */ fetch->fetcher_handle = fetch->ops->setup_fetch(fetch, url, - only_2xx, post_urlenc, - post_multipart, headers); + only_2xx, downgrade_tls, + post_urlenc, post_multipart, + headers); if (fetch->fetcher_handle == NULL) goto failed; diff --git a/content/fetch.h b/content/fetch.h index d7cdced1b..d23b3cd4b 100644 --- a/content/fetch.h +++ b/content/fetch.h @@ -43,7 +43,8 @@ typedef enum { FETCH_REDIRECT, FETCH_NOTMODIFIED, FETCH_AUTH, - FETCH_CERT_ERR + FETCH_CERT_ERR, + FETCH_SSL_ERR } fetch_msg_type; typedef struct fetch_msg { @@ -103,7 +104,7 @@ struct fetch * fetch_start(nsurl *url, nsurl *referer, fetch_callback callback, void *p, bool only_2xx, const char *post_urlenc, const struct fetch_multipart_data *post_multipart, - bool verifiable, + bool verifiable, bool downgrade_tls, const char *headers[]); void fetch_abort(struct fetch *f); void fetch_poll(void); @@ -123,17 +124,17 @@ struct fetch_multipart_data *fetch_multipart_data_clone( /* API for fetchers themselves */ -typedef bool (*fetcher_initialise)(lwc_string *); -typedef bool (*fetcher_can_fetch)(const nsurl *); -typedef void* (*fetcher_setup_fetch)(struct fetch *, nsurl *, - bool, const char *, - const struct fetch_multipart_data *, - const char **); -typedef bool (*fetcher_start_fetch)(void *); -typedef void (*fetcher_abort_fetch)(void *); -typedef void (*fetcher_free_fetch)(void *); -typedef void (*fetcher_poll_fetcher)(lwc_string *); -typedef void (*fetcher_finalise)(lwc_string *); +typedef bool (*fetcher_initialise)(lwc_string *scheme); +typedef bool (*fetcher_can_fetch)(const nsurl *url); +typedef void *(*fetcher_setup_fetch)(struct fetch *parent_fetch, nsurl *url, + bool only_2xx, bool downgrade_tls, const char *post_urlenc, + const struct fetch_multipart_data *post_multipart, + const char **headers); +typedef bool (*fetcher_start_fetch)(void *fetch); +typedef void (*fetcher_abort_fetch)(void *fetch); +typedef void (*fetcher_free_fetch)(void *fetch); +typedef void (*fetcher_poll_fetcher)(lwc_string *scheme); +typedef void (*fetcher_finalise)(lwc_string *scheme); /** Register a fetcher for a scheme * diff --git a/content/fetchers/about.c b/content/fetchers/about.c index 78f22df01..387e32a98 100644 --- a/content/fetchers/about.c +++ b/content/fetchers/about.c @@ -729,6 +729,7 @@ static void * fetch_about_setup(struct fetch *fetchh, nsurl *url, bool only_2xx, + bool downgrade_tls, const char *post_urlenc, const struct fetch_multipart_data *post_multipart, const char **headers) diff --git a/content/fetchers/curl.c b/content/fetchers/curl.c index ff7ccbe30..668e05a46 100644 --- a/content/fetchers/curl.c +++ b/content/fetchers/curl.c @@ -77,6 +77,7 @@ struct curl_fetch_info { bool abort; /**< Abort requested. */ bool stopped; /**< Download stopped on purpose. */ bool only_2xx; /**< Only HTTP 2xx responses acceptable. */ + bool downgrade_tls; /**< Downgrade to TLS <= 1.0 */ nsurl *url; /**< URL of this fetch. */ lwc_string *host; /**< The hostname of this fetch. */ struct curl_slist *headers; /**< List of request headers. */ @@ -114,7 +115,7 @@ static bool fetch_curl_initialise(lwc_string *scheme); static void fetch_curl_finalise(lwc_string *scheme); static bool fetch_curl_can_fetch(const nsurl *url); static void * fetch_curl_setup(struct fetch *parent_fetch, nsurl *url, - bool only_2xx, const char *post_urlenc, + bool only_2xx, bool downgrade_tls, const char *post_urlenc, const struct fetch_multipart_data *post_multipart, const char **headers); static bool fetch_curl_start(void *vfetch); @@ -348,7 +349,7 @@ bool fetch_curl_can_fetch(const nsurl *url) */ void * fetch_curl_setup(struct fetch *parent_fetch, nsurl *url, - bool only_2xx, const char *post_urlenc, + bool only_2xx, bool downgrade_tls, const char *post_urlenc, const struct fetch_multipart_data *post_multipart, const char **headers) { @@ -370,6 +371,7 @@ void * fetch_curl_setup(struct fetch *parent_fetch, nsurl *url, fetch->abort = false; fetch->stopped = false; fetch->only_2xx = only_2xx; + fetch->downgrade_tls = downgrade_tls; fetch->headers = NULL; fetch->url = nsurl_ref(url); fetch->host = nsurl_get_component(url, NSURL_HOST); @@ -665,14 +667,28 @@ fetch_curl_set_options(struct curl_fetch_info *f) CURLcode fetch_curl_sslctxfun(CURL *curl_handle, void *_sslctx, void *parm) { + struct curl_fetch_info *f = (struct curl_fetch_info *) parm; SSL_CTX *sslctx = _sslctx; + long options = SSL_OP_ALL; + SSL_CTX_set_verify(sslctx, SSL_VERIFY_PEER, fetch_curl_verify_callback); SSL_CTX_set_cert_verify_callback(sslctx, fetch_curl_cert_verify_callback, parm); + + if (f->downgrade_tls) { +#ifdef SSL_OP_NO_TLSv1_1 + /* Disable TLS1.1, if the server can't cope with it */ + options |= SSL_OP_NO_TLSv1_1; +#endif + } + #ifdef SSL_OP_NO_TLSv1_2 /* Disable TLS1.2, as it causes some servers to stall. */ - SSL_CTX_set_options(sslctx, SSL_OP_NO_TLSv1_2); + options |= SSL_OP_NO_TLSv1_2; #endif + + SSL_CTX_set_options(sslctx, options); + return CURLE_OK; } @@ -851,17 +867,16 @@ void fetch_curl_done(CURL *curl_handle, CURLcode result) else { finished = true; } - } else if (result == CURLE_WRITE_ERROR && f->stopped) + } else if (result == CURLE_WRITE_ERROR && f->stopped) { /* CURLE_WRITE_ERROR occurs when fetch_curl_data * returns 0, which we use to abort intentionally */ ; - else if (result == CURLE_SSL_PEER_CERTIFICATE || + } else if (result == CURLE_SSL_PEER_CERTIFICATE || result == CURLE_SSL_CACERT) { memcpy(certs, f->cert_data, sizeof(certs)); memset(f->cert_data, 0, sizeof(f->cert_data)); cert = true; - } - else { + } else { LOG(("Unknown cURL response code %d", result)); error = true; } @@ -955,8 +970,12 @@ void fetch_curl_done(CURL *curl_handle, CURLcode result) msg.data.cert_err.num_certs = i; fetch_send_callback(&msg, f->fetch_handle); } else if (error) { - msg.type = FETCH_ERROR; - msg.data.error = fetch_error_buffer; + if (result != CURLE_SSL_CONNECT_ERROR) { + msg.type = FETCH_ERROR; + msg.data.error = fetch_error_buffer; + } else { + msg.type = FETCH_SSL_ERR; + } fetch_send_callback(&msg, f->fetch_handle); } diff --git a/content/fetchers/data.c b/content/fetchers/data.c index 3f8989e8c..77d3c9f9c 100644 --- a/content/fetchers/data.c +++ b/content/fetchers/data.c @@ -81,7 +81,7 @@ static bool fetch_data_can_fetch(const nsurl *url) } static void *fetch_data_setup(struct fetch *parent_fetch, nsurl *url, - bool only_2xx, const char *post_urlenc, + bool only_2xx, bool downgrade_tls, const char *post_urlenc, const struct fetch_multipart_data *post_multipart, const char **headers) { diff --git a/content/fetchers/file.c b/content/fetchers/file.c index 4c02c0c60..b7b831d7b 100644 --- a/content/fetchers/file.c +++ b/content/fetchers/file.c @@ -128,6 +128,7 @@ static void * fetch_file_setup(struct fetch *fetchh, nsurl *url, bool only_2xx, + bool downgrade_tls, const char *post_urlenc, const struct fetch_multipart_data *post_multipart, const char **headers) diff --git a/content/fetchers/resource.c b/content/fetchers/resource.c index 86d5498d5..1fc33f882 100644 --- a/content/fetchers/resource.c +++ b/content/fetchers/resource.c @@ -231,6 +231,7 @@ static void * fetch_resource_setup(struct fetch *fetchh, nsurl *url, bool only_2xx, + bool downgrade_tls, const char *post_urlenc, const struct fetch_multipart_data *post_multipart, const char **headers) diff --git a/content/llcache.c b/content/llcache.c index ed5cc6eda..4bdebdbc2 100644 --- a/content/llcache.c +++ b/content/llcache.c @@ -84,6 +84,8 @@ typedef struct { bool tried_with_auth; /**< Whether we've tried with auth */ + bool tried_with_tls_downgrade; /**< Whether we've tried TLS <= 1.0 */ + bool outstanding_query; /**< Waiting for a query response */ } llcache_fetch_ctx; @@ -711,6 +713,7 @@ static nserror llcache_object_refetch(llcache_object *object) object->fetch.flags & LLCACHE_RETRIEVE_NO_ERROR_PAGES, urlenc, multipart, object->fetch.flags & LLCACHE_RETRIEVE_VERIFIABLE, + object->fetch.tried_with_tls_downgrade, (const char **) headers); /* Clean up cache-control headers */ @@ -1543,6 +1546,45 @@ static nserror llcache_fetch_cert_error(llcache_object *object, return error; } +/** + * Handle a TLS connection setup failure + * + * \param object Object being fetched + * \return NSERROR_OK on success, appropriate error otherwise + */ +static nserror llcache_fetch_ssl_error(llcache_object *object) +{ + nserror error = NSERROR_OK; + + /* Fetch has been stopped, and destroyed. Invalidate object's pointer */ + object->fetch.fetch = NULL; + + /* Invalidate cache-control data */ + llcache_invalidate_cache_control_data(object); + + if (object->fetch.tried_with_tls_downgrade == true) { + /* Have already tried to downgrade, so give up */ + llcache_event event; + + /* Mark object complete */ + object->fetch.state = LLCACHE_FETCH_COMPLETE; + + /* Inform client(s) that object fetch failed */ + event.type = LLCACHE_EVENT_ERROR; + /** \todo More appropriate error message */ + event.data.msg = messages_get("FetchFailed"); + + error = llcache_send_event_to_users(object, &event); + } else { + /* Flag that we've tried to downgrade, so that if the + * fetch fails again, we give up */ + object->fetch.tried_with_tls_downgrade = true; + error = llcache_object_refetch(object); + } + + return error; +} + /** * Handler for fetch events * @@ -1705,6 +1747,17 @@ static void llcache_fetch_callback(const fetch_msg *msg, void *p) msg->data.cert_err.certs, msg->data.cert_err.num_certs); break; + case FETCH_SSL_ERR: + /* TLS connection setup failed */ + + /* Release candidate, if any */ + if (object->candidate != NULL) { + object->candidate->candidate_count--; + object->candidate = NULL; + } + + error = llcache_fetch_ssl_error(object); + break; } /* Deal with any errors reported by event handlers */ -- cgit v1.2.3 From 46b76915229f9dd1a58aabcddf9270ef0745b1d2 Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Sat, 5 Jan 2013 14:52:02 +0000 Subject: Start rationalising textarea widget. --- desktop/textarea.c | 68 +++++++++++++++++++++++++++--------------------------- desktop/textarea.h | 36 ++++++++++++++++------------- desktop/tree.c | 4 ++-- 3 files changed, 56 insertions(+), 52 deletions(-) diff --git a/desktop/textarea.c b/desktop/textarea.c index 05e3aaae5..8743c85d7 100644 --- a/desktop/textarea.c +++ b/desktop/textarea.c @@ -65,7 +65,7 @@ struct line_info { unsigned int b_length; /**< Byte length of line */ }; -struct text_area { +struct textarea { int scroll_x, scroll_y; /**< scroll offsets of the textarea * content @@ -112,17 +112,17 @@ struct text_area { }; -static bool textarea_insert_text(struct text_area *ta, unsigned int index, +static bool textarea_insert_text(struct textarea *ta, unsigned int index, const char *text); -static bool textarea_replace_text(struct text_area *ta, unsigned int start, +static bool textarea_replace_text(struct textarea *ta, unsigned int start, unsigned int end, const char *text); -static bool textarea_reflow(struct text_area *ta, unsigned int line); -static unsigned int textarea_get_xy_offset(struct text_area *ta, int x, int y); -static bool textarea_set_caret_xy(struct text_area *ta, int x, int y); -static bool textarea_scroll_visible(struct text_area *ta); -static bool textarea_select(struct text_area *ta, int c_start, int c_end); -static bool textarea_select_fragment(struct text_area *ta); -static void textarea_normalise_text(struct text_area *ta, +static bool textarea_reflow(struct textarea *ta, unsigned int line); +static unsigned int textarea_get_xy_offset(struct textarea *ta, int x, int y); +static bool textarea_set_caret_xy(struct textarea *ta, int x, int y); +static bool textarea_scroll_visible(struct textarea *ta); +static bool textarea_select(struct textarea *ta, int c_start, int c_end); +static bool textarea_select_fragment(struct textarea *ta); +static void textarea_normalise_text(struct textarea *ta, unsigned int b_start, unsigned int b_len); /** @@ -139,18 +139,18 @@ static void textarea_normalise_text(struct text_area *ta, * \param data user specified data which will be passed to redraw callbacks * \return Opaque handle for textarea or 0 on error */ -struct text_area *textarea_create(int width, int height, - unsigned int flags, const plot_font_style_t *style, +struct textarea *textarea_create(int width, int height, + textarea_flags flags, const plot_font_style_t *style, textarea_redraw_request_callback redraw_request, void *data) { - struct text_area *ret; + struct textarea *ret; if (redraw_request == NULL) { LOG(("no callback provided")); return NULL; } - ret = malloc(sizeof(struct text_area)); + ret = malloc(sizeof(struct textarea)); if (ret == NULL) { LOG(("malloc failed")); return NULL; @@ -199,7 +199,7 @@ struct text_area *textarea_create(int width, int height, * * \param ta Text area to destroy */ -void textarea_destroy(struct text_area *ta) +void textarea_destroy(struct textarea *ta) { free(ta->text); free(ta->lines); @@ -214,7 +214,7 @@ void textarea_destroy(struct text_area *ta) * \param text UTF-8 text to set text area's contents to * \return true on success, false on memory exhaustion */ -bool textarea_set_text(struct text_area *ta, const char *text) +bool textarea_set_text(struct textarea *ta, const char *text) { unsigned int len = strlen(text) + 1; @@ -247,7 +247,7 @@ bool textarea_set_text(struct text_area *ta, const char *text) * \param len Length (bytes) of buffer pointed to by buf, or 0 to read length * \return Length (bytes) written/required or -1 on error */ -int textarea_get_text(struct text_area *ta, char *buf, unsigned int len) +int textarea_get_text(struct textarea *ta, char *buf, unsigned int len) { if (buf == NULL && len == 0) { /* want length */ @@ -276,7 +276,7 @@ int textarea_get_text(struct text_area *ta, char *buf, unsigned int len) * \param text UTF-8 text to insert * \return false on memory exhaustion, true otherwise */ -bool textarea_insert_text(struct text_area *ta, unsigned int index, +bool textarea_insert_text(struct textarea *ta, unsigned int index, const char *text) { unsigned int b_len = strlen(text); @@ -330,7 +330,7 @@ bool textarea_insert_text(struct text_area *ta, unsigned int index, * \param text UTF-8 text to insert * \return false on memory exhaustion, true otherwise */ -bool textarea_replace_text(struct text_area *ta, unsigned int start, +bool textarea_replace_text(struct textarea *ta, unsigned int start, unsigned int end, const char *text) { unsigned int b_len = strlen(text); @@ -399,7 +399,7 @@ bool textarea_replace_text(struct text_area *ta, unsigned int start, * the caret * \return true on success false otherwise */ -bool textarea_set_caret(struct text_area *ta, int caret) +bool textarea_set_caret(struct textarea *ta, int caret) { unsigned int c_len; unsigned int b_off; @@ -540,7 +540,7 @@ bool textarea_set_caret(struct text_area *ta, int caret) * \param y Y coordinate * \return character offset */ -unsigned int textarea_get_xy_offset(struct text_area *ta, int x, int y) +unsigned int textarea_get_xy_offset(struct textarea *ta, int x, int y) { size_t b_off, temp; unsigned int c_off; @@ -593,7 +593,7 @@ unsigned int textarea_get_xy_offset(struct text_area *ta, int x, int y) * \param y Y position of caret in a window relative to text area top left * \return true on success false otherwise */ -bool textarea_set_caret_xy(struct text_area *ta, int x, int y) +bool textarea_set_caret_xy(struct textarea *ta, int x, int y) { unsigned int c_off; @@ -611,7 +611,7 @@ bool textarea_set_caret_xy(struct text_area *ta, int x, int y) * \param ta Text area * \return 0-based character index of caret location, or -1 on error */ -int textarea_get_caret(struct text_area *ta) +int textarea_get_caret(struct textarea *ta) { unsigned int c_off = 0, b_off; @@ -635,7 +635,7 @@ int textarea_get_caret(struct text_area *ta) * \param line Line number to begin reflow on * \return true on success false otherwise */ -bool textarea_reflow(struct text_area *ta, unsigned int line) +bool textarea_reflow(struct textarea *ta, unsigned int line) { char *text; unsigned int len; @@ -740,7 +740,7 @@ bool textarea_reflow(struct text_area *ta, unsigned int line) * \param y1 bottom Y coordinate of redraw area * \param ctx current redraw context */ -void textarea_redraw(struct text_area *ta, int x, int y, +void textarea_redraw(struct textarea *ta, int x, int y, const struct rect *clip, const struct redraw_context *ctx) { const struct plotter_table *plot = ctx->plot; @@ -934,7 +934,7 @@ void textarea_redraw(struct text_area *ta, int x, int y, * \param key The ucs4 character codepoint * \return true if the keypress is dealt with, false otherwise. */ -bool textarea_keypress(struct text_area *ta, uint32_t key) +bool textarea_keypress(struct textarea *ta, uint32_t key) { char utf8[6]; unsigned int caret, caret_init, length, l_len, b_off, b_len; @@ -1258,7 +1258,7 @@ bool textarea_keypress(struct text_area *ta, uint32_t key) * \param ta The text area to be scrolled * \return true if textarea was scrolled false otherwise */ -bool textarea_scroll_visible(struct text_area *ta) +bool textarea_scroll_visible(struct textarea *ta) { int x0, x1, y0, y1, x, y; int index, b_off; @@ -1320,7 +1320,7 @@ bool textarea_scroll_visible(struct text_area *ta) * \param y Y coordinate * \return true if action was handled false otherwise */ -bool textarea_mouse_action(struct text_area *ta, browser_mouse_state mouse, +bool textarea_mouse_action(struct textarea *ta, browser_mouse_state mouse, int x, int y) { int c_start, c_end; @@ -1367,7 +1367,7 @@ bool textarea_mouse_action(struct text_area *ta, browser_mouse_state mouse, * \param y Y coordinate * \return true if drag end was handled false otherwise */ -bool textarea_drag_end(struct text_area *ta, browser_mouse_state mouse, +bool textarea_drag_end(struct textarea *ta, browser_mouse_state mouse, int x, int y) { int c_end; @@ -1384,7 +1384,7 @@ bool textarea_drag_end(struct text_area *ta, browser_mouse_state mouse, * \param c_end Last character (exclusive) * \return true on success false otherwise */ -bool textarea_select(struct text_area *ta, int c_start, int c_end) +bool textarea_select(struct textarea *ta, int c_start, int c_end) { int swap = -1; @@ -1418,7 +1418,7 @@ bool textarea_select(struct text_area *ta, int c_start, int c_end) * \param ta Text area * \return True on success, false otherwise */ -static bool textarea_select_fragment(struct text_area * ta) +static bool textarea_select_fragment(struct textarea * ta) { int caret_pos, sel_start = 0, sel_end = 0, index; size_t b_start, b_end; @@ -1472,7 +1472,7 @@ static bool textarea_select_fragment(struct text_area * ta) * \param b_start Byte offset to start at * \param b_len Byte length to check */ -void textarea_normalise_text(struct text_area *ta, +void textarea_normalise_text(struct textarea *ta, unsigned int b_start, unsigned int b_len) { bool multi = (ta->flags & TEXTAREA_MULTILINE) ? true:false; @@ -1510,7 +1510,7 @@ void textarea_normalise_text(struct text_area *ta, * \param width if not NULL, gets updated to the width of the textarea * \param height if not NULL, gets updated to the height of the textarea */ -void textarea_get_dimensions(struct text_area *ta, int *width, int *height) +void textarea_get_dimensions(struct textarea *ta, int *width, int *height) { if (width != NULL) *width = ta->vis_width; @@ -1525,7 +1525,7 @@ void textarea_get_dimensions(struct text_area *ta, int *width, int *height) * \param width the new width of the textarea * \param height the new height of the textarea */ -void textarea_set_dimensions(struct text_area *ta, int width, int height) +void textarea_set_dimensions(struct textarea *ta, int width, int height) { ta->vis_width = width; ta->vis_height = height; diff --git a/desktop/textarea.h b/desktop/textarea.h index 1cbe5fa43..275acdc4f 100644 --- a/desktop/textarea.h +++ b/desktop/textarea.h @@ -30,30 +30,34 @@ #include "desktop/plot_style.h" /* Text area flags */ -#define TEXTAREA_MULTILINE 0x01 /**< Text area is multiline */ -#define TEXTAREA_READONLY 0x02 /**< Text area is read only */ +typedef enum textarea_flags { + TEXTAREA_DEFAULT = (1 << 0), + TEXTAREA_MULTILINE = (1 << 1), + TEXTAREA_READONLY = (1 << 2) +} textarea_flags; -struct text_area; + +struct textarea; typedef void(*textarea_redraw_request_callback)(void *data, int x, int y, int width, int height); -struct text_area *textarea_create(int width, int height, - unsigned int flags, const plot_font_style_t *style, +struct textarea *textarea_create(int width, int height, + textarea_flags flags, const plot_font_style_t *style, textarea_redraw_request_callback redraw_request, void *data); -void textarea_destroy(struct text_area *ta); -bool textarea_set_text(struct text_area *ta, const char *text); -int textarea_get_text(struct text_area *ta, char *buf, unsigned int len); -bool textarea_set_caret(struct text_area *ta, int caret); -int textarea_get_caret(struct text_area *ta); -void textarea_redraw(struct text_area *ta, int x, int y, +void textarea_destroy(struct textarea *ta); +bool textarea_set_text(struct textarea *ta, const char *text); +int textarea_get_text(struct textarea *ta, char *buf, unsigned int len); +bool textarea_set_caret(struct textarea *ta, int caret); +int textarea_get_caret(struct textarea *ta); +void textarea_redraw(struct textarea *ta, int x, int y, const struct rect *clip, const struct redraw_context *ctx); -bool textarea_keypress(struct text_area *ta, uint32_t key); -bool textarea_mouse_action(struct text_area *ta, browser_mouse_state mouse, +bool textarea_keypress(struct textarea *ta, uint32_t key); +bool textarea_mouse_action(struct textarea *ta, browser_mouse_state mouse, int x, int y); -bool textarea_drag_end(struct text_area *ta, browser_mouse_state mouse, +bool textarea_drag_end(struct textarea *ta, browser_mouse_state mouse, int x, int y); -void textarea_get_dimensions(struct text_area *ta, int *width, int *height); -void textarea_set_dimensions(struct text_area *ta, int width, int height); +void textarea_get_dimensions(struct textarea *ta, int *width, int *height); +void textarea_set_dimensions(struct textarea *ta, int width, int height); #endif diff --git a/desktop/tree.c b/desktop/tree.c index 8517cc122..250bdd861 100644 --- a/desktop/tree.c +++ b/desktop/tree.c @@ -159,7 +159,7 @@ struct tree { int width; /* Tree width */ int height; /* Tree height */ unsigned int flags; /* Tree flags */ - struct text_area *textarea; /* Handle for UTF-8 textarea */ + struct textarea *textarea; /* Handle for UTF-8 textarea */ bool textarea_drag_start; /* whether the start of a mouse drag was in the textarea */ struct node_element *editing; /* Node element being edited */ @@ -2959,7 +2959,7 @@ void tree_start_edit(struct tree *tree, struct node_element *element) if (element->type == NODE_ELEMENT_TEXT_PLUS_ICON) width -= NODE_INSTEP; - tree->textarea = textarea_create(width, height, 0, + tree->textarea = textarea_create(width, height, TEXTAREA_DEFAULT, &plot_fstyle, tree_textarea_redraw_request, tree); if (tree->textarea == NULL) { tree_stop_edit(tree, false); -- cgit v1.2.3 From 912d3f7690525e4c2124f03c5f8d129d17c33001 Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Sat, 5 Jan 2013 15:15:23 +0000 Subject: Put exported function comments in header. --- desktop/textarea.c | 113 ++++++++--------------------------------------------- desktop/textarea.h | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+), 97 deletions(-) diff --git a/desktop/textarea.c b/desktop/textarea.c index 8743c85d7..92fb02c9e 100644 --- a/desktop/textarea.c +++ b/desktop/textarea.c @@ -125,20 +125,8 @@ static bool textarea_select_fragment(struct textarea *ta); static void textarea_normalise_text(struct textarea *ta, unsigned int b_start, unsigned int b_len); -/** - * Create a text area - * - * \param x X coordinate of left border - * \param y Y coordinate of top border - * \param width width of the text area - * \param height width of the text area - * \param flags text area flags - * \param style font style - * \param redraw_start_callback will be called when textarea wants to redraw - * \param redraw_end_callback will be called when textarea finisjes redrawing - * \param data user specified data which will be passed to redraw callbacks - * \return Opaque handle for textarea or 0 on error - */ + +/* exported interface, documented in textarea.h */ struct textarea *textarea_create(int width, int height, textarea_flags flags, const plot_font_style_t *style, textarea_redraw_request_callback redraw_request, void *data) @@ -194,11 +182,8 @@ struct textarea *textarea_create(int width, int height, } -/** - * Destroy a text area - * - * \param ta Text area to destroy - */ + +/* exported interface, documented in textarea.h */ void textarea_destroy(struct textarea *ta) { free(ta->text); @@ -207,13 +192,7 @@ void textarea_destroy(struct textarea *ta) } -/** - * Set the text in a text area, discarding any current text - * - * \param ta Text area - * \param text UTF-8 text to set text area's contents to - * \return true on success, false on memory exhaustion - */ +/* exported interface, documented in textarea.h */ bool textarea_set_text(struct textarea *ta, const char *text) { unsigned int len = strlen(text) + 1; @@ -238,15 +217,7 @@ bool textarea_set_text(struct textarea *ta, const char *text) } -/** - * Extract the text from a text area - * - * \param ta Text area - * \param buf Pointer to buffer to receive data, or NULL - * to read length required - * \param len Length (bytes) of buffer pointed to by buf, or 0 to read length - * \return Length (bytes) written/required or -1 on error - */ +/* exported interface, documented in textarea.h */ int textarea_get_text(struct textarea *ta, char *buf, unsigned int len) { if (buf == NULL && len == 0) { @@ -391,14 +362,7 @@ bool textarea_replace_text(struct textarea *ta, unsigned int start, } -/** - * Set the caret's position - * - * \param ta Text area - * \param caret 0-based character index to place caret at, -1 removes - * the caret - * \return true on success false otherwise - */ +/* exported interface, documented in textarea.h */ bool textarea_set_caret(struct textarea *ta, int caret) { unsigned int c_len; @@ -605,12 +569,7 @@ bool textarea_set_caret_xy(struct textarea *ta, int x, int y) } -/** - * Get the caret's position - * - * \param ta Text area - * \return 0-based character index of caret location, or -1 on error - */ +/* exported interface, documented in textarea.h */ int textarea_get_caret(struct textarea *ta) { unsigned int c_off = 0, b_off; @@ -730,16 +689,7 @@ bool textarea_reflow(struct textarea *ta, unsigned int line) } -/** - * Handle redraw requests for text areas - * - * \param redraw Redraw request block - * \param x0 left X coordinate of redraw area - * \param y0 top Y coordinate of redraw area - * \param x1 right X coordinate of redraw area - * \param y1 bottom Y coordinate of redraw area - * \param ctx current redraw context - */ +/* exported interface, documented in textarea.h */ void textarea_redraw(struct textarea *ta, int x, int y, const struct rect *clip, const struct redraw_context *ctx) { @@ -927,13 +877,8 @@ void textarea_redraw(struct textarea *ta, int x, int y, } } -/** - * Key press handling for text areas. - * - * \param ta The text area which got the keypress - * \param key The ucs4 character codepoint - * \return true if the keypress is dealt with, false otherwise. - */ + +/* exported interface, documented in textarea.h */ bool textarea_keypress(struct textarea *ta, uint32_t key) { char utf8[6]; @@ -1311,15 +1256,7 @@ bool textarea_scroll_visible(struct textarea *ta) } -/** - * Handles all kinds of mouse action - * - * \param ta Text area - * \param mouse the mouse state at action moment - * \param x X coordinate - * \param y Y coordinate - * \return true if action was handled false otherwise - */ +/* exported interface, documented in textarea.h */ bool textarea_mouse_action(struct textarea *ta, browser_mouse_state mouse, int x, int y) { @@ -1358,15 +1295,7 @@ bool textarea_mouse_action(struct textarea *ta, browser_mouse_state mouse, } -/** - * Handles the end of a drag operation - * - * \param ta Text area - * \param mouse the mouse state at drag end moment - * \param x X coordinate - * \param y Y coordinate - * \return true if drag end was handled false otherwise - */ +/* exported interface, documented in textarea.h */ bool textarea_drag_end(struct textarea *ta, browser_mouse_state mouse, int x, int y) { @@ -1504,12 +1433,7 @@ void textarea_normalise_text(struct textarea *ta, } -/** - * Gets the dimensions of a textarea - * - * \param width if not NULL, gets updated to the width of the textarea - * \param height if not NULL, gets updated to the height of the textarea - */ +/* exported interface, documented in textarea.h */ void textarea_get_dimensions(struct textarea *ta, int *width, int *height) { if (width != NULL) @@ -1518,13 +1442,8 @@ void textarea_get_dimensions(struct textarea *ta, int *width, int *height) *height = ta->vis_height; } -/** - * Set the dimensions of a textarea, causing a reflow and - * emitting a redraw request. - * - * \param width the new width of the textarea - * \param height the new height of the textarea - */ + +/* exported interface, documented in textarea.h */ void textarea_set_dimensions(struct textarea *ta, int width, int height) { ta->vis_width = width; diff --git a/desktop/textarea.h b/desktop/textarea.h index 275acdc4f..b24c20fb4 100644 --- a/desktop/textarea.h +++ b/desktop/textarea.h @@ -42,22 +42,130 @@ struct textarea; typedef void(*textarea_redraw_request_callback)(void *data, int x, int y, int width, int height); +/** + * Create a text area + * + * \param x X coordinate of left border + * \param y Y coordinate of top border + * \param width width of the text area + * \param height width of the text area + * \param flags text area flags + * \param style font style + * \param redraw_start_callback will be called when textarea wants to redraw + * \param redraw_end_callback will be called when textarea finisjes redrawing + * \param data user specified data which will be passed to redraw callbacks + * \return Opaque handle for textarea or 0 on error + */ struct textarea *textarea_create(int width, int height, textarea_flags flags, const plot_font_style_t *style, textarea_redraw_request_callback redraw_request, void *data); + +/** + * Destroy a text area + * + * \param ta Text area to destroy + */ void textarea_destroy(struct textarea *ta); + +/** + * Set the text in a text area, discarding any current text + * + * \param ta Text area + * \param text UTF-8 text to set text area's contents to + * \return true on success, false on memory exhaustion + */ bool textarea_set_text(struct textarea *ta, const char *text); + +/** + * Extract the text from a text area + * + * \param ta Text area + * \param buf Pointer to buffer to receive data, or NULL + * to read length required + * \param len Length (bytes) of buffer pointed to by buf, or 0 to read length + * \return Length (bytes) written/required or -1 on error + */ int textarea_get_text(struct textarea *ta, char *buf, unsigned int len); + +/** + * Set the caret's position + * + * \param ta Text area + * \param caret 0-based character index to place caret at, -1 removes + * the caret + * \return true on success false otherwise + */ bool textarea_set_caret(struct textarea *ta, int caret); + +/** + * Get the caret's position + * + * \param ta Text area + * \return 0-based character index of caret location, or -1 on error + */ int textarea_get_caret(struct textarea *ta); + +/** + * Handle redraw requests for text areas + * + * \param redraw Redraw request block + * \param x0 left X coordinate of redraw area + * \param y0 top Y coordinate of redraw area + * \param x1 right X coordinate of redraw area + * \param y1 bottom Y coordinate of redraw area + * \param ctx current redraw context + */ void textarea_redraw(struct textarea *ta, int x, int y, const struct rect *clip, const struct redraw_context *ctx); + +/** + * Key press handling for text areas. + * + * \param ta The text area which got the keypress + * \param key The ucs4 character codepoint + * \return true if the keypress is dealt with, false otherwise. + */ bool textarea_keypress(struct textarea *ta, uint32_t key); + +/** + * Handles all kinds of mouse action + * + * \param ta Text area + * \param mouse the mouse state at action moment + * \param x X coordinate + * \param y Y coordinate + * \return true if action was handled false otherwise + */ bool textarea_mouse_action(struct textarea *ta, browser_mouse_state mouse, int x, int y); + +/** + * Handles the end of a drag operation + * + * \param ta Text area + * \param mouse the mouse state at drag end moment + * \param x X coordinate + * \param y Y coordinate + * \return true if drag end was handled false otherwise + */ bool textarea_drag_end(struct textarea *ta, browser_mouse_state mouse, int x, int y); + +/** + * Gets the dimensions of a textarea + * + * \param width if not NULL, gets updated to the width of the textarea + * \param height if not NULL, gets updated to the height of the textarea + */ void textarea_get_dimensions(struct textarea *ta, int *width, int *height); + +/** + * Set the dimensions of a textarea, causing a reflow and + * emitting a redraw request. + * + * \param width the new width of the textarea + * \param height the new height of the textarea + */ void textarea_set_dimensions(struct textarea *ta, int width, int height); #endif -- cgit v1.2.3 From 1ac2e00134f6174708ce2cec21a96a0ae08f9ad6 Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Sat, 5 Jan 2013 15:27:53 +0000 Subject: Remove forward declaration. --- desktop/textarea.c | 1031 ++++++++++++++++++++++++++-------------------------- 1 file changed, 512 insertions(+), 519 deletions(-) diff --git a/desktop/textarea.c b/desktop/textarea.c index 92fb02c9e..5c2e26947 100644 --- a/desktop/textarea.c +++ b/desktop/textarea.c @@ -112,130 +112,366 @@ struct textarea { }; -static bool textarea_insert_text(struct textarea *ta, unsigned int index, - const char *text); -static bool textarea_replace_text(struct textarea *ta, unsigned int start, - unsigned int end, const char *text); -static bool textarea_reflow(struct textarea *ta, unsigned int line); -static unsigned int textarea_get_xy_offset(struct textarea *ta, int x, int y); -static bool textarea_set_caret_xy(struct textarea *ta, int x, int y); -static bool textarea_scroll_visible(struct textarea *ta); -static bool textarea_select(struct textarea *ta, int c_start, int c_end); -static bool textarea_select_fragment(struct textarea *ta); + +/** + * Normalises any line endings within the text, replacing CRLF or CR with + * LF as necessary. If the textarea is single line, then all linebreaks are + * converted into spaces. + * + * \param ta Text area + * \param b_start Byte offset to start at + * \param b_len Byte length to check + */ static void textarea_normalise_text(struct textarea *ta, - unsigned int b_start, unsigned int b_len); + unsigned int b_start, unsigned int b_len) +{ + bool multi = (ta->flags & TEXTAREA_MULTILINE) ? true:false; + unsigned int index; + + /* Remove CR characters. If it's a CRLF pair delete it ot replace it + * with LF otherwise. + */ + for (index = 0; index < b_len; index++) { + if (ta->text[b_start + index] == '\r') { + if (b_start + index + 1 <= ta->text_len && + ta->text[b_start + index + 1] == '\n') { + ta->text_len--; + ta->text_utf8_len--; + memmove(ta->text + b_start + index, + ta->text + b_start + index + 1, + ta->text_len - b_start - index); + } + else + ta->text[b_start + index] = '\n'; + } + /* Replace newlines with spaces if this is a single line + * textarea. + */ + if (!multi && (ta->text[b_start + index] == '\n')) + ta->text[b_start + index] = ' '; + } +} -/* exported interface, documented in textarea.h */ -struct textarea *textarea_create(int width, int height, - textarea_flags flags, const plot_font_style_t *style, - textarea_redraw_request_callback redraw_request, void *data) + +/** + * Selects a character range in the textarea and redraws it + * + * \param ta Text area + * \param c_start First character (inclusive) + * \param c_end Last character (exclusive) + * \return true on success false otherwise + */ +static bool textarea_select(struct textarea *ta, int c_start, int c_end) { - struct textarea *ret; + int swap = -1; - if (redraw_request == NULL) { - LOG(("no callback provided")); - return NULL; + /* if start is after end they get swapped, start won't and end will + be selected this way */ + if (c_start > c_end) { + swap = c_start; + c_start = c_end; + c_end = swap; } - ret = malloc(sizeof(struct textarea)); - if (ret == NULL) { - LOG(("malloc failed")); - return NULL; + ta->selection_start = c_start; + ta->selection_end = c_end; + + if (!(ta->flags & TEXTAREA_READONLY)) { + if (swap == -1) + return textarea_set_caret(ta, c_end); + else + return textarea_set_caret(ta, c_start); } - ret->redraw_request = redraw_request; - ret->data = data; - ret->vis_width = width; - ret->vis_height = height; - ret->scroll_x = 0; - ret->scroll_y = 0; - ret->drag_start_char = 0; + ta->redraw_request(ta->data, 0, 0, ta->vis_width, ta->vis_height); + return true; +} - ret->flags = flags; - ret->text = malloc(64); - if (ret->text == NULL) { - LOG(("malloc failed")); - free(ret); - return NULL; - } - ret->text[0] = '\0'; - ret->text_alloc = 64; - ret->text_len = 1; - ret->text_utf8_len = 0; - ret->fstyle = *style; +/** + * Selects a text fragment, relative to current caret position. + * + * \param ta Text area + * \return True on success, false otherwise + */ +static bool textarea_select_fragment(struct textarea * ta) +{ + int caret_pos, sel_start = 0, sel_end = 0, index; + size_t b_start, b_end; - ret->line_height = FIXTOINT(FDIV((FMUL(FLTTOFIX(1.2), FMUL(nscss_screen_dpi, INTTOFIX((style->size / FONT_SIZE_SCALE))))), F_72)); + /* Fragment separators must be suitable for URLs and ordinary text */ + static const char *sep = " /:.\r\n"; - ret->caret_pos.line = ret->caret_pos.char_off = 0; - ret->caret_x = MARGIN_LEFT; - ret->caret_y = 0; - ret->selection_start = -1; - ret->selection_end = -1; + caret_pos = textarea_get_caret(ta); + if (caret_pos < 0) { + return false; + } - ret->line_count = 0; - ret->lines = NULL; + /* Compute byte offset of caret position */ + for (b_start = 0, index = 0; index < caret_pos; + b_start = utf8_next(ta->text, ta->text_len, + b_start), + index++) { + /* Cache the character offset of the last separator */ + if (strchr(sep, ta->text[b_start]) != NULL) { + /* Add one to start to skip over separator */ + sel_start = index + 1; + } + } - return ret; -} + /* Search for next separator, if any */ + for (b_end = b_start; b_end < ta->text_len; + b_end = utf8_next(ta->text, ta->text_len, + b_end), + index++) { + if (strchr(sep, ta->text[b_end]) != NULL) { + sel_end = index; + break; + } + } + if (sel_start < sel_end) { + textarea_select(ta, sel_start, sel_end); + return true; + } + return false; +} -/* exported interface, documented in textarea.h */ -void textarea_destroy(struct textarea *ta) + +/** + * Scrolls a textarea to make the caret visible (doesn't perform a redraw) + * + * \param ta The text area to be scrolled + * \return true if textarea was scrolled false otherwise + */ +static bool textarea_scroll_visible(struct textarea *ta) { - free(ta->text); - free(ta->lines); - free(ta); + int x0, x1, y0, y1, x, y; + int index, b_off; + bool scrolled = false; + + if (ta->caret_pos.char_off == -1) + return false; + + x0 = MARGIN_LEFT; + x1 = ta->vis_width - MARGIN_RIGHT; + y0 = 0; + y1 = ta->vis_height; + + index = textarea_get_caret(ta); + + /* find byte offset of caret position */ + for (b_off = 0; index-- > 0; + b_off = utf8_next(ta->text, ta->text_len, b_off)) + ; /* do nothing */ + + nsfont.font_width(&ta->fstyle, + ta->text + ta->lines[ta->caret_pos.line].b_start, + b_off - ta->lines[ta->caret_pos.line].b_start, + &x); + + /* top-left of caret */ + x += MARGIN_LEFT - ta->scroll_x; + y = ta->line_height * ta->caret_pos.line - ta->scroll_y; + + /* check and change vertical scroll */ + if (y < y0) { + ta->scroll_y -= y0 - y; + scrolled = true; + } else if (y + ta->line_height > y1) { + ta->scroll_y += y + ta->line_height - y1; + scrolled = true; + } + + + /* check and change horizontal scroll */ + if (x < x0) { + ta->scroll_x -= x0 - x ; + scrolled = true; + } else if (x > x1 - 1) { + ta->scroll_x += x - (x1 - 1); + scrolled = true; + } + + return scrolled; } -/* exported interface, documented in textarea.h */ -bool textarea_set_text(struct textarea *ta, const char *text) +/** + * Reflow a text area from the given line onwards + * + * \param ta Text area to reflow + * \param line Line number to begin reflow on + * \return true on success false otherwise + */ +static bool textarea_reflow(struct textarea *ta, unsigned int line) { - unsigned int len = strlen(text) + 1; + char *text; + unsigned int len; + size_t b_off; + int x; + char *space; + unsigned int line_count = 0; - if (len >= ta->text_alloc) { - char *temp = realloc(ta->text, len + 64); - if (temp == NULL) { - LOG(("realloc failed")); + /** \todo pay attention to line parameter */ + /** \todo create horizontal scrollbar if needed */ + + ta->line_count = 0; + + if (ta->lines == NULL) { + ta->lines = + malloc(LINE_CHUNK_SIZE * sizeof(struct line_info)); + if (ta->lines == NULL) { + LOG(("malloc failed")); return false; } - ta->text = temp; - ta->text_alloc = len + 64; } - memcpy(ta->text, text, len); - ta->text_len = len; - ta->text_utf8_len = utf8_length(ta->text); + if (!(ta->flags & TEXTAREA_MULTILINE)) { + /* Single line */ + ta->lines[line_count].b_start = 0; + ta->lines[line_count++].b_length = ta->text_len - 1; - textarea_normalise_text(ta, 0, len); + ta->line_count = line_count; - return textarea_reflow(ta, 0); + return true; + } + + for (len = ta->text_len - 1, text = ta->text; len > 0; + len -= b_off, text += b_off) { + + nsfont.font_split(&ta->fstyle, text, len, + ta->vis_width - MARGIN_LEFT - MARGIN_RIGHT, + &b_off, &x); + + if (line_count > 0 && line_count % LINE_CHUNK_SIZE == 0) { + struct line_info *temp = realloc(ta->lines, + (line_count + LINE_CHUNK_SIZE) * + sizeof(struct line_info)); + if (temp == NULL) { + LOG(("realloc failed")); + return false; + } + + ta->lines = temp; + } + + /* handle LF */ + for (space = text; space <= text + b_off; space++) { + if (*space == '\n') + break; + } + + if (space <= text + b_off) { + /* Found newline; use it */ + ta->lines[line_count].b_start = text - ta->text; + ta->lines[line_count++].b_length = space - text; + + b_off = space + 1 - text; + + if (len - b_off == 0) { + /* reached end of input => add last line */ + ta->lines[line_count].b_start = + text + b_off - ta->text; + ta->lines[line_count++].b_length = 0; + } + + continue; + } + + if (len - b_off > 0) { + /* find last space (if any) */ + for (space = text + b_off; space > text; space--) + if (*space == ' ') + break; + + if (space != text) + b_off = space + 1 - text; + } + + ta->lines[line_count].b_start = text - ta->text; + ta->lines[line_count++].b_length = b_off; + } + + ta->line_count = line_count; + + return true; } -/* exported interface, documented in textarea.h */ -int textarea_get_text(struct textarea *ta, char *buf, unsigned int len) +/** + * get character offset from the beginning of the text for some coordinates + * + * \param ta Text area + * \param x X coordinate + * \param y Y coordinate + * \return character offset + */ +static unsigned int textarea_get_xy_offset(struct textarea *ta, int x, int y) { - if (buf == NULL && len == 0) { - /* want length */ - return ta->text_len; - } else if (buf == NULL) { - /* Can't write to NULL */ - return -1; - } + size_t b_off, temp; + unsigned int c_off; + int line; - if (len < ta->text_len) { - LOG(("buffer too small")); - return -1; - } + if (!ta->line_count) + return 0; - memcpy(buf, ta->text, ta->text_len); + x = x - MARGIN_LEFT + ta->scroll_x; + y = y + ta->scroll_y; - return ta->text_len; + if (x < 0) + x = 0; + + line = y / ta->line_height; + + if (line < 0) + line = 0; + if (ta->line_count - 1 < line) + line = ta->line_count - 1; + + nsfont.font_position_in_string(&ta->fstyle, + ta->text + ta->lines[line].b_start, + ta->lines[line].b_length, x, &b_off, &x); + + /* If the calculated byte offset corresponds with the number of bytes + * in the line, and the line has been soft-wrapped, then ensure the + * caret offset is before the trailing space character, rather than + * after it. Otherwise, the caret will be placed at the start of the + * following line, which is undesirable. + */ + if (b_off == (unsigned)ta->lines[line].b_length && + ta->text[ta->lines[line].b_start + + ta->lines[line].b_length - 1] == ' ') + b_off--; + + for (temp = 0, c_off = 0; temp < b_off + ta->lines[line].b_start; + temp = utf8_next(ta->text, ta->text_len, temp)) + c_off++; + + return c_off; +} + + +/** + * Set the caret's position + * + * \param ta Text area + * \param x X position of caret in a window relative to text area top left + * \param y Y position of caret in a window relative to text area top left + * \return true on success false otherwise + */ +static bool textarea_set_caret_xy(struct textarea *ta, int x, int y) +{ + unsigned int c_off; + + if (ta->flags & TEXTAREA_READONLY) + return true; + + c_off = textarea_get_xy_offset(ta, x, y); + return textarea_set_caret(ta, c_off); } @@ -247,7 +483,7 @@ int textarea_get_text(struct textarea *ta, char *buf, unsigned int len) * \param text UTF-8 text to insert * \return false on memory exhaustion, true otherwise */ -bool textarea_insert_text(struct textarea *ta, unsigned int index, +static bool textarea_insert_text(struct textarea *ta, unsigned int index, const char *text) { unsigned int b_len = strlen(text); @@ -301,7 +537,7 @@ bool textarea_insert_text(struct textarea *ta, unsigned int index, * \param text UTF-8 text to insert * \return false on memory exhaustion, true otherwise */ -bool textarea_replace_text(struct textarea *ta, unsigned int start, +static bool textarea_replace_text(struct textarea *ta, unsigned int start, unsigned int end, const char *text) { unsigned int b_len = strlen(text); @@ -315,53 +551,169 @@ bool textarea_replace_text(struct textarea *ta, unsigned int start, if (end > ta->text_utf8_len) end = ta->text_utf8_len; - if (start == end) - return textarea_insert_text(ta, start, text); + if (start == end) + return textarea_insert_text(ta, start, text); + + if (start > end) + return false; + + diff = end - start; + + /* find byte offset of replace start */ + for (b_start = 0; start-- > 0; + b_start = utf8_next(ta->text, ta->text_len, b_start)) + ; /* do nothing */ + + /* find byte length of replaced text */ + for (b_end = b_start; diff-- > 0; + b_end = utf8_next(ta->text, ta->text_len, b_end)) + ; /* do nothing */ + + if (b_len + ta->text_len - (b_end - b_start) >= ta->text_alloc) { + char *temp = realloc(ta->text, + b_len + ta->text_len - (b_end - b_start) + 64); + if (temp == NULL) { + LOG(("realloc failed")); + return false; + } + + ta->text = temp; + ta->text_alloc = + b_len + ta->text_len - (b_end - b_start) + 64; + } + + /* Shift text following to new position */ + memmove(ta->text + b_start + b_len, ta->text + b_end, + ta->text_len - b_end); + + /* Insert new text */ + memcpy(ta->text + b_start, text, b_len); + + ta->text_len += b_len - (b_end - b_start); + ta->text_utf8_len = utf8_length(ta->text); + textarea_normalise_text(ta, b_start, b_len); + + /** \todo calculate line to reflow from */ + return textarea_reflow(ta, 0); +} + + + + +/* exported interface, documented in textarea.h */ +struct textarea *textarea_create(int width, int height, + textarea_flags flags, const plot_font_style_t *style, + textarea_redraw_request_callback redraw_request, void *data) +{ + struct textarea *ret; + + if (redraw_request == NULL) { + LOG(("no callback provided")); + return NULL; + } + + ret = malloc(sizeof(struct textarea)); + if (ret == NULL) { + LOG(("malloc failed")); + return NULL; + } + + ret->redraw_request = redraw_request; + ret->data = data; + ret->vis_width = width; + ret->vis_height = height; + ret->scroll_x = 0; + ret->scroll_y = 0; + ret->drag_start_char = 0; + + + ret->flags = flags; + ret->text = malloc(64); + if (ret->text == NULL) { + LOG(("malloc failed")); + free(ret); + return NULL; + } + ret->text[0] = '\0'; + ret->text_alloc = 64; + ret->text_len = 1; + ret->text_utf8_len = 0; + + ret->fstyle = *style; + + ret->line_height = FIXTOINT(FDIV((FMUL(FLTTOFIX(1.2), + FMUL(nscss_screen_dpi, + INTTOFIX((style->size / FONT_SIZE_SCALE))))), F_72)); + + ret->caret_pos.line = ret->caret_pos.char_off = 0; + ret->caret_x = MARGIN_LEFT; + ret->caret_y = 0; + ret->selection_start = -1; + ret->selection_end = -1; + + ret->line_count = 0; + ret->lines = NULL; - if (start > end) - return false; + return ret; +} - diff = end - start; - /* find byte offset of replace start */ - for (b_start = 0; start-- > 0; - b_start = utf8_next(ta->text, ta->text_len, b_start)) - ; /* do nothing */ +/* exported interface, documented in textarea.h */ +void textarea_destroy(struct textarea *ta) +{ + free(ta->text); + free(ta->lines); + free(ta); +} - /* find byte length of replaced text */ - for (b_end = b_start; diff-- > 0; - b_end = utf8_next(ta->text, ta->text_len, b_end)) - ; /* do nothing */ - if (b_len + ta->text_len - (b_end - b_start) >= ta->text_alloc) { - char *temp = realloc(ta->text, - b_len + ta->text_len - (b_end - b_start) + 64); +/* exported interface, documented in textarea.h */ +bool textarea_set_text(struct textarea *ta, const char *text) +{ + unsigned int len = strlen(text) + 1; + + if (len >= ta->text_alloc) { + char *temp = realloc(ta->text, len + 64); if (temp == NULL) { LOG(("realloc failed")); return false; } - ta->text = temp; - ta->text_alloc = - b_len + ta->text_len - (b_end - b_start) + 64; + ta->text_alloc = len + 64; } - /* Shift text following to new position */ - memmove(ta->text + b_start + b_len, ta->text + b_end, - ta->text_len - b_end); - - /* Insert new text */ - memcpy(ta->text + b_start, text, b_len); - - ta->text_len += b_len - (b_end - b_start); + memcpy(ta->text, text, len); + ta->text_len = len; ta->text_utf8_len = utf8_length(ta->text); - textarea_normalise_text(ta, b_start, b_len); - /** \todo calculate line to reflow from */ + textarea_normalise_text(ta, 0, len); + return textarea_reflow(ta, 0); } +/* exported interface, documented in textarea.h */ +int textarea_get_text(struct textarea *ta, char *buf, unsigned int len) +{ + if (buf == NULL && len == 0) { + /* want length */ + return ta->text_len; + } else if (buf == NULL) { + /* Can't write to NULL */ + return -1; + } + + if (len < ta->text_len) { + LOG(("buffer too small")); + return -1; + } + + memcpy(buf, ta->text, ta->text_len); + + return ta->text_len; +} + + /* exported interface, documented in textarea.h */ bool textarea_set_caret(struct textarea *ta, int caret) { @@ -457,235 +809,61 @@ bool textarea_set_caret(struct textarea *ta, int caret) /* find byte offset of caret position */ for (b_off = 0; index-- > 0; b_off = utf8_next(ta->text, - ta->text_len, b_off)) - ; /* do nothing */ - - nsfont.font_width(&ta->fstyle, - ta->text + - ta->lines[ta->caret_pos.line].b_start, - b_off - ta->lines[ta->caret_pos.line].b_start, - &x); - - x += MARGIN_LEFT - ta->scroll_x; - ta->caret_x = x; - y = ta->line_height * ta->caret_pos.line - ta->scroll_y; - ta->caret_y = y; - - if (textarea_scroll_visible(ta)) { - ta->redraw_request(ta->data, 0, 0, - ta->vis_width, - ta->vis_height); - } else { - x0 = max(x - 1, MARGIN_LEFT); - y0 = max(y + text_y_offset, 0); - x1 = min(x + 1, ta->vis_width - MARGIN_RIGHT); - y1 = min(y + ta->line_height + text_y_offset, - ta->vis_height); - - width = x1 - x0; - height = y1 - y0; - - if (width > 0 && height > 0) { - ta->redraw_request(ta->data, x0, y0, - width, height); - } - } - } - - return true; -} - - -/** - * get character offset from the beginning of the text for some coordinates - * - * \param ta Text area - * \param x X coordinate - * \param y Y coordinate - * \return character offset - */ -unsigned int textarea_get_xy_offset(struct textarea *ta, int x, int y) -{ - size_t b_off, temp; - unsigned int c_off; - int line; - - if (!ta->line_count) - return 0; - - x = x - MARGIN_LEFT + ta->scroll_x; - y = y + ta->scroll_y; - - if (x < 0) - x = 0; - - line = y / ta->line_height; - - if (line < 0) - line = 0; - if (ta->line_count - 1 < line) - line = ta->line_count - 1; - - nsfont.font_position_in_string(&ta->fstyle, - ta->text + ta->lines[line].b_start, - ta->lines[line].b_length, x, &b_off, &x); - - /* If the calculated byte offset corresponds with the number of bytes - * in the line, and the line has been soft-wrapped, then ensure the - * caret offset is before the trailing space character, rather than - * after it. Otherwise, the caret will be placed at the start of the - * following line, which is undesirable. - */ - if (b_off == (unsigned)ta->lines[line].b_length && - ta->text[ta->lines[line].b_start + - ta->lines[line].b_length - 1] == ' ') - b_off--; - - for (temp = 0, c_off = 0; temp < b_off + ta->lines[line].b_start; - temp = utf8_next(ta->text, ta->text_len, temp)) - c_off++; - - return c_off; -} - - -/** - * Set the caret's position - * - * \param ta Text area - * \param x X position of caret in a window relative to text area top left - * \param y Y position of caret in a window relative to text area top left - * \return true on success false otherwise - */ -bool textarea_set_caret_xy(struct textarea *ta, int x, int y) -{ - unsigned int c_off; - - if (ta->flags & TEXTAREA_READONLY) - return true; - - c_off = textarea_get_xy_offset(ta, x, y); - return textarea_set_caret(ta, c_off); -} - - -/* exported interface, documented in textarea.h */ -int textarea_get_caret(struct textarea *ta) -{ - unsigned int c_off = 0, b_off; - - - /* if the text is a trailing NUL only */ - if (ta->text_utf8_len == 0) - return 0; - - /* Calculate character offset of this line's start */ - for (b_off = 0; b_off < ta->lines[ta->caret_pos.line].b_start; - b_off = utf8_next(ta->text, ta->text_len, b_off)) - c_off++; - - return c_off + ta->caret_pos.char_off; -} - -/** - * Reflow a text area from the given line onwards - * - * \param ta Text area to reflow - * \param line Line number to begin reflow on - * \return true on success false otherwise - */ -bool textarea_reflow(struct textarea *ta, unsigned int line) -{ - char *text; - unsigned int len; - size_t b_off; - int x; - char *space; - unsigned int line_count = 0; - - /** \todo pay attention to line parameter */ - /** \todo create horizontal scrollbar if needed */ - - ta->line_count = 0; - - if (ta->lines == NULL) { - ta->lines = - malloc(LINE_CHUNK_SIZE * sizeof(struct line_info)); - if (ta->lines == NULL) { - LOG(("malloc failed")); - return false; - } - } - - if (!(ta->flags & TEXTAREA_MULTILINE)) { - /* Single line */ - ta->lines[line_count].b_start = 0; - ta->lines[line_count++].b_length = ta->text_len - 1; - - ta->line_count = line_count; - - return true; - } - - for (len = ta->text_len - 1, text = ta->text; len > 0; - len -= b_off, text += b_off) { - - nsfont.font_split(&ta->fstyle, text, len, - ta->vis_width - MARGIN_LEFT - MARGIN_RIGHT, - &b_off, &x); - - if (line_count > 0 && line_count % LINE_CHUNK_SIZE == 0) { - struct line_info *temp = realloc(ta->lines, - (line_count + LINE_CHUNK_SIZE) * - sizeof(struct line_info)); - if (temp == NULL) { - LOG(("realloc failed")); - return false; - } + ta->text_len, b_off)) + ; /* do nothing */ - ta->lines = temp; - } + nsfont.font_width(&ta->fstyle, + ta->text + + ta->lines[ta->caret_pos.line].b_start, + b_off - ta->lines[ta->caret_pos.line].b_start, + &x); - /* handle LF */ - for (space = text; space <= text + b_off; space++) { - if (*space == '\n') - break; - } + x += MARGIN_LEFT - ta->scroll_x; + ta->caret_x = x; + y = ta->line_height * ta->caret_pos.line - ta->scroll_y; + ta->caret_y = y; - if (space <= text + b_off) { - /* Found newline; use it */ - ta->lines[line_count].b_start = text - ta->text; - ta->lines[line_count++].b_length = space - text; + if (textarea_scroll_visible(ta)) { + ta->redraw_request(ta->data, 0, 0, + ta->vis_width, + ta->vis_height); + } else { + x0 = max(x - 1, MARGIN_LEFT); + y0 = max(y + text_y_offset, 0); + x1 = min(x + 1, ta->vis_width - MARGIN_RIGHT); + y1 = min(y + ta->line_height + text_y_offset, + ta->vis_height); - b_off = space + 1 - text; + width = x1 - x0; + height = y1 - y0; - if (len - b_off == 0) { - /* reached end of input => add last line */ - ta->lines[line_count].b_start = - text + b_off - ta->text; - ta->lines[line_count++].b_length = 0; + if (width > 0 && height > 0) { + ta->redraw_request(ta->data, x0, y0, + width, height); } - - continue; } + } - if (len - b_off > 0) { - /* find last space (if any) */ - for (space = text + b_off; space > text; space--) - if (*space == ' ') - break; + return true; +} - if (space != text) - b_off = space + 1 - text; - } - ta->lines[line_count].b_start = text - ta->text; - ta->lines[line_count++].b_length = b_off; - } +/* exported interface, documented in textarea.h */ +int textarea_get_caret(struct textarea *ta) +{ + unsigned int c_off = 0, b_off; - ta->line_count = line_count; - return true; + /* if the text is a trailing NUL only */ + if (ta->text_utf8_len == 0) + return 0; + + /* Calculate character offset of this line's start */ + for (b_off = 0; b_off < ta->lines[ta->caret_pos.line].b_start; + b_off = utf8_next(ta->text, ta->text_len, b_off)) + c_off++; + + return c_off + ta->caret_pos.char_off; } @@ -1197,64 +1375,6 @@ bool textarea_keypress(struct textarea *ta, uint32_t key) return true; } -/** - * Scrolls a textarea to make the caret visible (doesn't perform a redraw) - * - * \param ta The text area to be scrolled - * \return true if textarea was scrolled false otherwise - */ -bool textarea_scroll_visible(struct textarea *ta) -{ - int x0, x1, y0, y1, x, y; - int index, b_off; - bool scrolled = false; - - if (ta->caret_pos.char_off == -1) - return false; - - x0 = MARGIN_LEFT; - x1 = ta->vis_width - MARGIN_RIGHT; - y0 = 0; - y1 = ta->vis_height; - - index = textarea_get_caret(ta); - - /* find byte offset of caret position */ - for (b_off = 0; index-- > 0; - b_off = utf8_next(ta->text, ta->text_len, b_off)) - ; /* do nothing */ - - nsfont.font_width(&ta->fstyle, - ta->text + ta->lines[ta->caret_pos.line].b_start, - b_off - ta->lines[ta->caret_pos.line].b_start, - &x); - - /* top-left of caret */ - x += MARGIN_LEFT - ta->scroll_x; - y = ta->line_height * ta->caret_pos.line - ta->scroll_y; - - /* check and change vertical scroll */ - if (y < y0) { - ta->scroll_y -= y0 - y; - scrolled = true; - } else if (y + ta->line_height > y1) { - ta->scroll_y += y + ta->line_height - y1; - scrolled = true; - } - - - /* check and change horizontal scroll */ - if (x < x0) { - ta->scroll_x -= x0 - x ; - scrolled = true; - } else if (x > x1 - 1) { - ta->scroll_x += x - (x1 - 1); - scrolled = true; - } - - return scrolled; -} - /* exported interface, documented in textarea.h */ bool textarea_mouse_action(struct textarea *ta, browser_mouse_state mouse, @@ -1305,133 +1425,6 @@ bool textarea_drag_end(struct textarea *ta, browser_mouse_state mouse, return textarea_select(ta, ta->drag_start_char, c_end); } -/** - * Selects a character range in the textarea and redraws it - * - * \param ta Text area - * \param c_start First character (inclusive) - * \param c_end Last character (exclusive) - * \return true on success false otherwise - */ -bool textarea_select(struct textarea *ta, int c_start, int c_end) -{ - int swap = -1; - - /* if start is after end they get swapped, start won't and end will - be selected this way */ - if (c_start > c_end) { - swap = c_start; - c_start = c_end; - c_end = swap; - } - - ta->selection_start = c_start; - ta->selection_end = c_end; - - if (!(ta->flags & TEXTAREA_READONLY)) { - if (swap == -1) - return textarea_set_caret(ta, c_end); - else - return textarea_set_caret(ta, c_start); - } - - ta->redraw_request(ta->data, 0, 0, ta->vis_width, ta->vis_height); - - return true; -} - - -/** - * Selects a text fragment, relative to current caret position. - * - * \param ta Text area - * \return True on success, false otherwise - */ -static bool textarea_select_fragment(struct textarea * ta) -{ - int caret_pos, sel_start = 0, sel_end = 0, index; - size_t b_start, b_end; - - /* Fragment separators must be suitable for URLs and ordinary text */ - static const char *sep = " /:.\r\n"; - - caret_pos = textarea_get_caret(ta); - if (caret_pos < 0) { - return false; - } - - /* Compute byte offset of caret position */ - for (b_start = 0, index = 0; index < caret_pos; - b_start = utf8_next(ta->text, ta->text_len, - b_start), - index++) { - /* Cache the character offset of the last separator */ - if (strchr(sep, ta->text[b_start]) != NULL) { - /* Add one to start to skip over separator */ - sel_start = index + 1; - } - } - - /* Search for next separator, if any */ - for (b_end = b_start; b_end < ta->text_len; - b_end = utf8_next(ta->text, ta->text_len, - b_end), - index++) { - if (strchr(sep, ta->text[b_end]) != NULL) { - sel_end = index; - break; - } - } - - if (sel_start < sel_end) { - textarea_select(ta, sel_start, sel_end); - return true; - } - - return false; -} - - -/** - * Normalises any line endings within the text, replacing CRLF or CR with - * LF as necessary. If the textarea is single line, then all linebreaks are - * converted into spaces. - * - * \param ta Text area - * \param b_start Byte offset to start at - * \param b_len Byte length to check - */ -void textarea_normalise_text(struct textarea *ta, - unsigned int b_start, unsigned int b_len) -{ - bool multi = (ta->flags & TEXTAREA_MULTILINE) ? true:false; - unsigned int index; - - /* Remove CR characters. If it's a CRLF pair delete it ot replace it - * with LF otherwise. - */ - for (index = 0; index < b_len; index++) { - if (ta->text[b_start + index] == '\r') { - if (b_start + index + 1 <= ta->text_len && - ta->text[b_start + index + 1] == '\n') { - ta->text_len--; - ta->text_utf8_len--; - memmove(ta->text + b_start + index, - ta->text + b_start + index + 1, - ta->text_len - b_start - index); - } - else - ta->text[b_start + index] = '\n'; - } - /* Replace newlines with spaces if this is a single line - * textarea. - */ - if (!multi && (ta->text[b_start + index] == '\n')) - ta->text[b_start + index] = ' '; - } - -} - /* exported interface, documented in textarea.h */ void textarea_get_dimensions(struct textarea *ta, int *width, int *height) -- cgit v1.2.3 From cc321d5f63a526aab5a93bc3269736ac14bd92f7 Mon Sep 17 00:00:00 2001 From: Chris Young Date: Sun, 6 Jan 2013 00:21:15 +0000 Subject: Track the mouse pointer on a per-window basis --- amiga/arexx.c | 8 ++------ amiga/download.c | 8 ++------ amiga/drag.c | 11 ++++++----- amiga/file.c | 4 ++-- amiga/gui.c | 20 ++++++-------------- amiga/gui.h | 1 + amiga/gui_options.c | 9 ++------- amiga/menu.c | 8 ++++---- amiga/search.c | 11 ++++++----- amiga/theme.c | 42 +++++++++++++++++++++--------------------- amiga/theme.h | 8 ++++++-- amiga/tree.c | 14 ++++---------- 12 files changed, 62 insertions(+), 82 deletions(-) diff --git a/amiga/arexx.c b/amiga/arexx.c index 154f69703..7048c0673 100755 --- a/amiga/arexx.c +++ b/amiga/arexx.c @@ -275,11 +275,7 @@ STATIC VOID rx_save(struct ARexxCmd *cmd, struct RexxMsg *rxm __attribute__((unu if(!bw) return; - /* Set the busy pointer. We intentionally don't use ami_update_pointer here. */ - SetWindowPointer(bw->window->shared->win, - WA_BusyPointer, TRUE, - WA_PointerDelay, TRUE, - TAG_DONE); + ami_set_pointer(bw->window->shared, GUI_POINTER_WAIT, false); if(fh = FOpen((char *)cmd->ac_ArgList[0], MODE_NEWFILE, 0)) { @@ -290,7 +286,7 @@ STATIC VOID rx_save(struct ARexxCmd *cmd, struct RexxMsg *rxm __attribute__((unu SetComment((char *)cmd->ac_ArgList[0], nsurl_access(hlcache_handle_get_url(bw->current_content))); } - ami_reset_pointer(bw->window->shared->win); + ami_reset_pointer(bw->window->shared); } STATIC VOID rx_quit(struct ARexxCmd *cmd, struct RexxMsg *rxm __attribute__((unused))) diff --git a/amiga/download.c b/amiga/download.c index 37fc79e28..c49bd8554 100644 --- a/amiga/download.c +++ b/amiga/download.c @@ -357,11 +357,7 @@ gui_window_save_link(struct gui_window *g, const char *url, const char *title) strlcpy(fname, savereq->fr_Drawer, 1024); AddPart(fname,savereq->fr_File,1024); - /* Set the busy pointer. We intentionally don't use ami_update_pointer here. */ - SetWindowPointer(g->shared->win, - WA_BusyPointer, TRUE, - WA_PointerDelay, TRUE, - TAG_DONE); + ami_set_pointer(g->shared, GUI_POINTER_WAIT, false); if(ami_download_check_overwrite(fname, g->shared->win, 0)) { @@ -388,7 +384,7 @@ gui_window_save_link(struct gui_window *g, const char *url, const char *title) } FreeVec(linkname); } - ami_reset_pointer(g->shared->win); + ami_reset_pointer(g->shared); } } diff --git a/amiga/drag.c b/amiga/drag.c index 20dbc78e3..addc4b3cf 100644 --- a/amiga/drag.c +++ b/amiga/drag.c @@ -142,7 +142,7 @@ void ami_drag_save(struct Window *win) return; } - ami_update_pointer(win, GUI_POINTER_WAIT, false); + ami_update_pointer(win, GUI_POINTER_WAIT); switch(drag_save) { @@ -189,7 +189,8 @@ void ami_drag_save(struct Window *win) drag_save = 0; drag_save_data = NULL; - ami_update_pointer(win, GUI_POINTER_DEFAULT, false); + + ami_update_pointer(win, GUI_POINTER_DEFAULT); } void ami_drag_icon_show(struct Window *win, const char *type) @@ -205,12 +206,12 @@ void ami_drag_icon_show(struct Window *win, const char *type) if(nsoption_bool(drag_save_icons) == false) { - ami_update_pointer(win, AMI_GUI_POINTER_DRAG, false); + ami_update_pointer(win, AMI_GUI_POINTER_DRAG); return; } else { - ami_update_pointer(win, GUI_POINTER_DEFAULT, false); + ami_update_pointer(win, GUI_POINTER_DEFAULT); } if(!strcmp(type, "drawer")) deftype = WBDRAWER; @@ -266,7 +267,7 @@ void ami_drag_icon_move(void) void ami_drag_icon_close(struct Window *win) { if(drag_icon) CloseWindow(drag_icon); - if(win) ami_update_pointer(win, GUI_POINTER_DEFAULT, false); + if(win) ami_update_pointer(win, GUI_POINTER_DEFAULT); drag_icon = NULL; drag_in_progress = FALSE; } diff --git a/amiga/file.c b/amiga/file.c index 161aabdf3..30a63b615 100644 --- a/amiga/file.c +++ b/amiga/file.c @@ -141,7 +141,7 @@ void ami_file_save(int type, char *fname, struct Window *win, struct bitmap *bm; BPTR fh=0; - ami_update_pointer(win, GUI_POINTER_WAIT, false); + ami_update_pointer(win, GUI_POINTER_WAIT); if(ami_download_check_overwrite(fname, win, 0)) { @@ -200,7 +200,7 @@ void ami_file_save(int type, char *fname, struct Window *win, if(object) SetComment(fname, nsurl_access(hlcache_handle_get_url(object))); } - ami_update_pointer(win, GUI_POINTER_DEFAULT, false); + ami_update_pointer(win, GUI_POINTER_DEFAULT); } void ami_file_save_req(int type, struct gui_window_2 *gwin, diff --git a/amiga/gui.c b/amiga/gui.c index 0c31c7f68..2bf9dde70 100755 --- a/amiga/gui.c +++ b/amiga/gui.c @@ -1435,7 +1435,7 @@ void ami_handle_msg(void) { ami_context_menu_mouse_trap(gwin, FALSE); - if(!gwin->mouse_state) ami_update_pointer(gwin->win, GUI_POINTER_DEFAULT, false); + if(!gwin->mouse_state) ami_set_pointer(gwin, GUI_POINTER_DEFAULT, true); } break; @@ -3600,11 +3600,7 @@ void ami_do_redraw_tiled(struct gui_window_2 *gwin, int tile_x_scale = (int)(nsoption_int(redraw_tile_size_x) / gwin->bw->scale); int tile_y_scale = (int)(nsoption_int(redraw_tile_size_y) / gwin->bw->scale); - /* Set the busy pointer. We intentionally don't use ami_update_pointer here. */ - SetWindowPointer(gwin->win, - WA_BusyPointer, TRUE, - WA_PointerDelay, TRUE, - TAG_DONE); + ami_set_pointer(gwin, GUI_POINTER_WAIT, false); browserglob.shared_pens = &gwin->shared_pens; @@ -3674,7 +3670,7 @@ void ami_do_redraw_tiled(struct gui_window_2 *gwin, } } - ami_reset_pointer(gwin->win); + ami_reset_pointer(gwin); } @@ -3852,19 +3848,15 @@ void ami_do_redraw(struct gui_window_2 *gwin) clip.x1 = bbox->Left + bbox->Width; clip.y1 = bbox->Top + bbox->Height; - /* Set the busy pointer. We intentionally don't use ami_update_pointer here. */ - SetWindowPointer(gwin->win, - WA_BusyPointer, TRUE, - WA_PointerDelay, TRUE, - TAG_DONE); - + ami_set_pointer(gwin, GUI_POINTER_WAIT, false); + if(browser_window_redraw(gwin->bw, clip.x0 - hcurrent, clip.y0 - vcurrent, &clip, &ctx)) { ami_clearclipreg(&browserglob); browserglob.rp = temprp; } - ami_reset_pointer(gwin->win); + ami_reset_pointer(gwin); } } diff --git a/amiga/gui.h b/amiga/gui.h index 1dff5d3c0..ff467977a 100755 --- a/amiga/gui.h +++ b/amiga/gui.h @@ -120,6 +120,7 @@ struct gui_window_2 { struct IBox *ptr_lock; struct AppWindow *appwin; struct MinList shared_pens; + gui_pointer_shape mouse_pointer; }; struct gui_window diff --git a/amiga/gui_options.c b/amiga/gui_options.c index 66e47d8ad..e54ad15a6 100755 --- a/amiga/gui_options.c +++ b/amiga/gui_options.c @@ -1502,10 +1502,7 @@ void ami_gui_opts_use(bool save) bool rescan_fonts = false; bool old_tab_always_show; - SetWindowPointer(gow->win, - WA_BusyPointer, TRUE, - WA_PointerDelay, TRUE, - TAG_DONE); + ami_update_pointer(gow->win, GUI_POINTER_WAIT); GetAttr(STRINGA_TextVal,gow->objects[GID_OPTS_HOMEPAGE],(ULONG *)&data); nsoption_set_charp(homepage_url, (char *)strdup((char *)data)); @@ -1863,9 +1860,7 @@ void ami_gui_opts_use(bool save) ami_menu_check_toggled = true; - SetWindowPointer(gow->win, - WA_Pointer, NULL, - TAG_DONE); + ami_update_pointer(gow->win, GUI_POINTER_DEFAULT); } void ami_gui_opts_close(void) diff --git a/amiga/menu.c b/amiga/menu.c index c3b191e40..496400dfb 100755 --- a/amiga/menu.c +++ b/amiga/menu.c @@ -753,9 +753,9 @@ static void ami_menu_item_project_print(struct Hook *hook, APTR window, struct I struct gui_window_2 *gwin; GetAttr(WINDOW_UserData, (Object *)window, (ULONG *)&gwin); - ami_update_pointer(gwin->win, GUI_POINTER_WAIT, false); + ami_set_pointer(gwin, GUI_POINTER_WAIT, false); ami_print_ui(gwin->bw->current_content); - ami_update_pointer(gwin->win, GUI_POINTER_DEFAULT, false); + ami_reset_pointer(gwin); } static void ami_menu_item_project_about(struct Hook *hook, APTR window, struct IntuiMessage *msg) @@ -766,7 +766,7 @@ static void ami_menu_item_project_about(struct Hook *hook, APTR window, struct I GetAttr(WINDOW_UserData, (Object *)window, (ULONG *)&gwin); - ami_update_pointer(gwin->win, GUI_POINTER_WAIT, false); + ami_set_pointer(gwin, GUI_POINTER_WAIT, false); temp = ASPrintf("%s|%s|%s", messages_get("OK"), messages_get("HelpCredits"), @@ -800,7 +800,7 @@ static void ami_menu_item_project_about(struct Hook *hook, APTR window, struct I else if(sel == 0) browser_window_create("about:licence", NULL, 0, true, false); - ami_update_pointer(gwin->win, GUI_POINTER_DEFAULT, false); + ami_reset_pointer(gwin); } static void ami_menu_item_project_quit(struct Hook *hook, APTR window, struct IntuiMessage *msg) diff --git a/amiga/search.c b/amiga/search.c index 6f8ebd414..1148f6d77 100755 --- a/amiga/search.c +++ b/amiga/search.c @@ -36,9 +36,10 @@ #include "amiga/os3support.h" #include "amiga/search.h" #include "amiga/object.h" +#include "amiga/theme.h" + #include #include - #include #include #include @@ -277,10 +278,10 @@ void ami_search_set_status(bool found, void *p) void ami_search_set_hourglass(bool active, void *p) { - SetWindowPointer(fwin->win, - WA_BusyPointer,active, - WA_PointerDelay,active, - TAG_DONE); + if(active) + ami_update_pointer(fwin->win, GUI_POINTER_WAIT); + else + ami_update_pointer(fwin->win, GUI_POINTER_DEFAULT); } /** diff --git a/amiga/theme.c b/amiga/theme.c index 52462c03c..6e3336746 100644 --- a/amiga/theme.c +++ b/amiga/theme.c @@ -174,18 +174,24 @@ void ami_get_theme_filename(char *filename, char *themestring, bool protocol) void gui_window_set_pointer(struct gui_window *g, gui_pointer_shape shape) { - ami_update_pointer(g->shared->win, shape, false); + ami_set_pointer(g->shared, shape, true); +} + +void ami_set_pointer(struct gui_window_2 *gwin, gui_pointer_shape shape, bool update) +{ + if(gwin->mouse_pointer == shape) return; + ami_update_pointer(gwin->win, shape); + if(update == true) gwin->mouse_pointer = shape; } /* reset the mouse pointer back to what NetSurf last set it as */ -void ami_reset_pointer(struct Window *win) +void ami_reset_pointer(struct gui_window_2 *gwin) { - ami_update_pointer(win, mouseptrcurrent, true); + ami_update_pointer(gwin->win, gwin->mouse_pointer); } -void ami_update_pointer(struct Window *win, gui_pointer_shape shape, bool reapply) +void ami_update_pointer(struct Window *win, gui_pointer_shape shape) { - if((mouseptrcurrent == shape) && (reapply == false)) return; if(drag_save_data) return; if(nsoption_bool(use_os_pointers)) @@ -193,24 +199,24 @@ void ami_update_pointer(struct Window *win, gui_pointer_shape shape, bool reappl switch(shape) { case GUI_POINTER_DEFAULT: - SetWindowPointer(win,TAG_DONE); + SetWindowPointer(win, TAG_DONE); break; case GUI_POINTER_WAIT: SetWindowPointer(win, - WA_BusyPointer,TRUE, - WA_PointerDelay,TRUE, + WA_BusyPointer, TRUE, + WA_PointerDelay, TRUE, TAG_DONE); break; default: if(mouseptrobj[shape]) { - SetWindowPointer(win,WA_Pointer,mouseptrobj[shape],TAG_DONE); + SetWindowPointer(win, WA_Pointer, mouseptrobj[shape], TAG_DONE); } else { - SetWindowPointer(win,TAG_DONE); + SetWindowPointer(win, TAG_DONE); } break; } @@ -219,34 +225,28 @@ void ami_update_pointer(struct Window *win, gui_pointer_shape shape, bool reappl { if(mouseptrobj[shape]) { - SetWindowPointer(win,WA_Pointer,mouseptrobj[shape],TAG_DONE); + SetWindowPointer(win, WA_Pointer, mouseptrobj[shape], TAG_DONE); } else { if(shape == GUI_POINTER_WAIT) { SetWindowPointer(win, - WA_BusyPointer,TRUE, - WA_PointerDelay,TRUE, + WA_BusyPointer, TRUE, + WA_PointerDelay, TRUE, TAG_DONE); } else { - SetWindowPointer(win,TAG_DONE); + SetWindowPointer(win, TAG_DONE); } } } - - mouseptrcurrent = shape; } void gui_window_hide_pointer(struct gui_window *g) { - if(mouseptrcurrent != AMI_GUI_POINTER_BLANK) - { - SetWindowPointer(g->shared->win,WA_Pointer,mouseptrobj[AMI_GUI_POINTER_BLANK],TAG_DONE); - mouseptrcurrent = AMI_GUI_POINTER_BLANK; - } + ami_set_pointer(g->shared, AMI_GUI_POINTER_BLANK, true); } void ami_init_mouse_pointers(void) diff --git a/amiga/theme.h b/amiga/theme.h index 52a21bb1a..3c3931c12 100644 --- a/amiga/theme.h +++ b/amiga/theme.h @@ -35,6 +35,10 @@ void ami_update_throbber(struct gui_window_2 *g,bool redraw); void ami_init_mouse_pointers(void); void ami_mouse_pointers_free(void); -void ami_update_pointer(struct Window *win, gui_pointer_shape shape, bool reapply); -void ami_reset_pointer(struct Window *win); +void ami_set_pointer(struct gui_window_2 *gwin, gui_pointer_shape shape, bool update); +void ami_reset_pointer(struct gui_window_2 *gwin); +/* Use the following ONLY if nothing other than the Intuition window pointer is available, + * and ALWAYS in preference to SetWindowPointer(), as it features more pointers and uses + * the correct ones specified in user preferences. */ +void ami_update_pointer(struct Window *win, gui_pointer_shape shape); #endif diff --git a/amiga/tree.c b/amiga/tree.c index e3224ff53..9f53d4be8 100755 --- a/amiga/tree.c +++ b/amiga/tree.c @@ -991,15 +991,12 @@ BOOL ami_tree_event(struct treeview_window *twin) { strlcpy(fname,savereq->fr_Drawer,1024); AddPart(fname,savereq->fr_File,1024); - SetWindowPointer(twin->win, - WA_BusyPointer, TRUE, - WA_PointerDelay, TRUE, - TAG_DONE); + ami_update_pointer(twin->win, GUI_POINTER_WAIT); if(twin->type == AMI_TREE_HISTORY) history_global_export(fname); else if(twin->type == AMI_TREE_HOTLIST) hotlist_export(fname); - SetWindowPointer(twin->win, TAG_DONE); + ami_update_pointer(twin->win, GUI_POINTER_DEFAULT); } break; @@ -1241,10 +1238,7 @@ void ami_tree_redraw_request(int x, int y, int width, int height, void *data) if(!twin->win) return; - SetWindowPointer(twin->win, - WA_BusyPointer, TRUE, - WA_PointerDelay, TRUE, - TAG_DONE); + ami_update_pointer(twin->win, GUI_POINTER_WAIT); glob = &twin->globals; @@ -1292,6 +1286,6 @@ void ami_tree_redraw_request(int x, int y, int width, int height, void *data) } } - SetWindowPointer(twin->win, TAG_DONE); + ami_update_pointer(twin->win, GUI_POINTER_WAIT); glob = &browserglob; } -- cgit v1.2.3 From 4011cc8671bc6eced8edd571fba66a123bc910a3 Mon Sep 17 00:00:00 2001 From: Chris Young Date: Sun, 6 Jan 2013 11:40:37 +0000 Subject: Only set the busy pointer after we've decided not to bail out early --- amiga/gui.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/amiga/gui.c b/amiga/gui.c index 2bf9dde70..43412704a 100755 --- a/amiga/gui.c +++ b/amiga/gui.c @@ -3599,8 +3599,6 @@ void ami_do_redraw_tiled(struct gui_window_2 *gwin, struct rect clip; int tile_x_scale = (int)(nsoption_int(redraw_tile_size_x) / gwin->bw->scale); int tile_y_scale = (int)(nsoption_int(redraw_tile_size_y) / gwin->bw->scale); - - ami_set_pointer(gwin, GUI_POINTER_WAIT, false); browserglob.shared_pens = &gwin->shared_pens; @@ -3634,6 +3632,8 @@ void ami_do_redraw_tiled(struct gui_window_2 *gwin, // printf("%ld %ld %ld %ld\n",left, top, width, height); + ami_set_pointer(gwin, GUI_POINTER_WAIT, false); + for(y = top; y < (top + height); y += tile_y_scale) { clip.y0 = 0; clip.y1 = nsoption_int(redraw_tile_size_y); -- cgit v1.2.3 From a6c6d476e2a80d3187101cee82cb792ecd940ce6 Mon Sep 17 00:00:00 2001 From: Chris Young Date: Sun, 6 Jan 2013 11:42:19 +0000 Subject: Set the default pointer, not the busy pointer, after completing a redraw --- amiga/tree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amiga/tree.c b/amiga/tree.c index 9f53d4be8..b19cd203e 100755 --- a/amiga/tree.c +++ b/amiga/tree.c @@ -1286,6 +1286,6 @@ void ami_tree_redraw_request(int x, int y, int width, int height, void *data) } } - ami_update_pointer(twin->win, GUI_POINTER_WAIT); + ami_update_pointer(twin->win, GUI_POINTER_DEFAULT); glob = &browserglob; } -- cgit v1.2.3 From 94118d22beb4dbe59fa9c281a064208b57f9f58b Mon Sep 17 00:00:00 2001 From: Chris Young Date: Sun, 6 Jan 2013 11:44:37 +0000 Subject: Remove unused global --- amiga/theme.c | 1 - 1 file changed, 1 deletion(-) diff --git a/amiga/theme.c b/amiga/theme.c index 6e3336746..65d2f51e1 100644 --- a/amiga/theme.c +++ b/amiga/theme.c @@ -48,7 +48,6 @@ struct bitmap *throbber_nsbm = NULL; ULONG throbber_frames,throbber_update_interval; static Object *mouseptrobj[AMI_LASTPOINTER+1]; static struct BitMap *mouseptrbm[AMI_LASTPOINTER+1]; -static int mouseptrcurrent=0; char *ptrs[AMI_LASTPOINTER+1] = { "ptr_default", -- cgit v1.2.3 From e414d545af15b907e887b440e1ecbb918c4376e9 Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Mon, 7 Jan 2013 12:22:51 +0000 Subject: Make comment concise. --- desktop/textarea.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/desktop/textarea.c b/desktop/textarea.c index 5c2e26947..406231814 100644 --- a/desktop/textarea.c +++ b/desktop/textarea.c @@ -106,9 +106,7 @@ struct textarea { void *data; /** < Callback data for both callback functions */ - int drag_start_char; /**< Character index at which the drag was - * started - */ + int drag_start_char; /**< Character index of drag start */ }; -- cgit v1.2.3 From 0c0f1e4b7a25ba4df1e005a37b1251a8f62ff337 Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Mon, 7 Jan 2013 12:59:06 +0000 Subject: Remove wrong comments. --- desktop/textarea.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/desktop/textarea.h b/desktop/textarea.h index b24c20fb4..e4fa2c7aa 100644 --- a/desktop/textarea.h +++ b/desktop/textarea.h @@ -45,8 +45,6 @@ typedef void(*textarea_redraw_request_callback)(void *data, int x, int y, /** * Create a text area * - * \param x X coordinate of left border - * \param y Y coordinate of top border * \param width width of the text area * \param height width of the text area * \param flags text area flags -- cgit v1.2.3 From f43f84f5ad324be693fdbe1fc493f418a2fa177d Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Mon, 7 Jan 2013 14:15:35 +0000 Subject: Fix redraw of selection hightlight during selection drags. --- desktop/textarea.c | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/desktop/textarea.c b/desktop/textarea.c index 406231814..35959c8ce 100644 --- a/desktop/textarea.c +++ b/desktop/textarea.c @@ -162,10 +162,9 @@ static void textarea_normalise_text(struct textarea *ta, */ static bool textarea_select(struct textarea *ta, int c_start, int c_end) { - int swap = -1; + int swap; - /* if start is after end they get swapped, start won't and end will - be selected this way */ + /* Ensure start is the beginning of the selection */ if (c_start > c_end) { swap = c_start; c_start = c_end; @@ -175,13 +174,6 @@ static bool textarea_select(struct textarea *ta, int c_start, int c_end) ta->selection_start = c_start; ta->selection_end = c_end; - if (!(ta->flags & TEXTAREA_READONLY)) { - if (swap == -1) - return textarea_set_caret(ta, c_end); - else - return textarea_set_caret(ta, c_start); - } - ta->redraw_request(ta->data, 0, 0, ta->vis_width, ta->vis_height); return true; @@ -1382,31 +1374,28 @@ bool textarea_mouse_action(struct textarea *ta, browser_mouse_state mouse, /* mouse button pressed above the text area, move caret */ if (mouse & BROWSER_MOUSE_PRESS_1) { - if (!(ta->flags & TEXTAREA_READONLY)) + if (!(ta->flags & TEXTAREA_READONLY)) { textarea_set_caret_xy(ta, x, y); + ta->drag_start_char = textarea_get_xy_offset(ta, x, y); + } if (ta->selection_start != -1) { + /* remove selection */ ta->selection_start = ta->selection_end = -1; ta->redraw_request(ta->data, 0, 0, ta->vis_width, ta->vis_height); } - } - else if (mouse & BROWSER_MOUSE_DOUBLE_CLICK) { + + } else if (mouse & BROWSER_MOUSE_DOUBLE_CLICK) { if (!(ta->flags & TEXTAREA_READONLY)) { textarea_set_caret_xy(ta, x, y); return textarea_select_fragment(ta); } - } - else if (mouse & BROWSER_MOUSE_DRAG_1) { - ta->drag_start_char = textarea_get_xy_offset(ta, x, y); - if (!(ta->flags & TEXTAREA_READONLY)) - return textarea_set_caret(ta, -1); - } - else if (mouse & BROWSER_MOUSE_HOLDING_1) { + + } else if (mouse & (BROWSER_MOUSE_DRAG_1 | BROWSER_MOUSE_HOLDING_1)) { c_start = ta->drag_start_char; c_end = textarea_get_xy_offset(ta, x, y); return textarea_select(ta, c_start, c_end); - } return true; -- cgit v1.2.3 From 87ee963ff2c6fe3e0bacbe899133e0932f396506 Mon Sep 17 00:00:00 2001 From: Vincent Sanders Date: Mon, 7 Jan 2013 18:45:29 +0000 Subject: Initial changes ready to improve caching decisions --- content/llcache.c | 91 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 63 insertions(+), 28 deletions(-) diff --git a/content/llcache.c b/content/llcache.c index 4bdebdbc2..8996acadd 100644 --- a/content/llcache.c +++ b/content/llcache.c @@ -846,14 +846,14 @@ static nserror llcache_object_add_to_list(llcache_object *object, } /** - * Determine if an object is still fresh + * Determine the remaining lifetime of a cache object using the * * \param object Object to consider * \return True if object is still fresh, false otherwise */ -static bool llcache_object_is_fresh(const llcache_object *object) +static int +llcache_object_rfc2616_remaining_lifetime(const llcache_cache_control *cd) { - const llcache_cache_control *cd = &object->cache; int current_age, freshness_lifetime; time_t now = time(NULL); @@ -873,24 +873,51 @@ static bool llcache_object_is_fresh(const llcache_object *object) freshness_lifetime = 0; #ifdef LLCACHE_TRACE - LOG(("%p: (%d > %d || %d != %d)", object, - freshness_lifetime, current_age, - object->fetch.state, LLCACHE_FETCH_COMPLETE)); + LOG(("%d:%d", freshness_lifetime, current_age)); +#endif + + if ((cd->no_cache == LLCACHE_VALIDATE_FRESH) && + (freshness_lifetime > current_age)) { + /* object was not forbidden from being returned from + * the cache unvalidated (i.e. the response contained + * a no-cache directive) + * + * The object current age is within the freshness lifetime. + */ + return freshness_lifetime - current_age; + } + + return 0; /* object has no remaining lifetime */ +} + +/** + * Determine if an object is still fresh + * + * \param object Object to consider + * \return True if object is still fresh, false otherwise + */ +static bool llcache_object_is_fresh(const llcache_object *object) +{ + int remaining_lifetime; + const llcache_cache_control *cd = &object->cache; + + remaining_lifetime = llcache_object_rfc2616_remaining_lifetime(cd); + +#ifdef LLCACHE_TRACE + LOG(("%p: (%d > 0 || %d != %d)", object, + remaining_lifetime, + object->fetch.state, LLCACHE_FETCH_COMPLETE)); #endif /* The object is fresh if: + * - it was not forbidden from being returned from the cache + * unvalidated. * - * it was not forbidden from being returned from the cache - * unvalidated (i.e. the response contained a no-cache directive) - * - * and: - * - * its current age is within the freshness lifetime - * or if we're still fetching the object + * - it has remaining lifetime or still being fetched. */ - return (cd->no_cache == LLCACHE_VALIDATE_FRESH && - (freshness_lifetime > current_age || - object->fetch.state != LLCACHE_FETCH_COMPLETE)); + return ((cd->no_cache == LLCACHE_VALIDATE_FRESH) && + ((remaining_lifetime > 0) || + (object->fetch.state != LLCACHE_FETCH_COMPLETE))); } /** @@ -1576,7 +1603,7 @@ static nserror llcache_fetch_ssl_error(llcache_object *object) error = llcache_send_event_to_users(object, &event); } else { - /* Flag that we've tried to downgrade, so that if the + /* Flag that we've tried to downgrade, so that if the * fetch fails again, we give up */ object->fetch.tried_with_tls_downgrade = true; error = llcache_object_refetch(object); @@ -2139,6 +2166,7 @@ void llcache_clean(void) { llcache_object *object, *next; uint32_t llcache_size = 0; + int remaining_lifetime; #ifdef LLCACHE_TRACE LOG(("Attempting cache clean")); @@ -2175,17 +2203,25 @@ void llcache_clean(void) for (object = llcache->cached_objects; object != NULL; object = next) { next = object->next; - if ((object->users == NULL) && + remaining_lifetime = llcache_object_rfc2616_remaining_lifetime(&object->cache); + + if ((object->users == NULL) && (object->candidate_count == 0) && - (llcache_object_is_fresh(object) == false) && (object->fetch.fetch == NULL) && (object->fetch.outstanding_query == false)) { + + if (remaining_lifetime > 0) { + /* object is fresh */ + llcache_size += object->source_len + sizeof(*object); + } else { + /* object is not fresh */ #ifdef LLCACHE_TRACE - LOG(("Found victim %p", object)); + LOG(("Found stale cacheable object (%p) with no users or pending fetches", object)); #endif - llcache_object_remove_from_list(object, - &llcache->cached_objects); - llcache_object_destroy(object); + llcache_object_remove_from_list(object, + &llcache->cached_objects); + llcache_object_destroy(object); + } } else { llcache_size += object->source_len + sizeof(*object); } @@ -2199,11 +2235,10 @@ void llcache_clean(void) object = next) { next = object->next; - if (object->users == NULL && - object->candidate_count == 0 && - object->fetch.fetch == NULL && - object->fetch.outstanding_query == - false) { + if ((object->users == NULL) && + (object->candidate_count == 0) && + (object->fetch.fetch == NULL) && + (object->fetch.outstanding_query == false)) { #ifdef LLCACHE_TRACE LOG(("Found victim %p", object)); #endif -- cgit v1.2.3 From 8314101ada7d800369e7343304ce313d0269f46f Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Tue, 8 Jan 2013 12:07:56 +0000 Subject: Pass various control keys to core for clipboard control, etc. --- gtk/gui.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/gtk/gui.c b/gtk/gui.c index a120fdfb6..faae172df 100644 --- a/gtk/gui.c +++ b/gtk/gui.c @@ -1028,7 +1028,19 @@ uint32_t gtk_gui_gdkkey_to_nskey(GdkEventKey *key) return gdk_keyval_to_unicode(key->keyval); case 'u': if (key->state & GDK_CONTROL_MASK) - return KEY_CLEAR_SELECTION; + return KEY_CUT_LINE; + return gdk_keyval_to_unicode(key->keyval); + case 'c': + if (key->state & GDK_CONTROL_MASK) + return KEY_COPY_SELECTION; + return gdk_keyval_to_unicode(key->keyval); + case 'v': + if (key->state & GDK_CONTROL_MASK) + return KEY_PASTE; + return gdk_keyval_to_unicode(key->keyval); + case 'x': + if (key->state & GDK_CONTROL_MASK) + return KEY_CUT_SELECTION; return gdk_keyval_to_unicode(key->keyval); case GDK_KEY(Escape): return KEY_ESCAPE; -- cgit v1.2.3 From e93fae7f2d9fdcc4ddf2a4db936cbd0dd33de20c Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Tue, 8 Jan 2013 16:44:45 +0000 Subject: Change clipboard api, so it does not assume either browser_window/gui_window, or html/text-plain content selection objects. This allows desktop/textarea to use clipboard. --- desktop/gui.h | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/desktop/gui.h b/desktop/gui.h index 48684c3c5..d04d7b405 100644 --- a/desktop/gui.h +++ b/desktop/gui.h @@ -119,12 +119,33 @@ void gui_drag_save_selection(struct selection *s, struct gui_window *g); void gui_start_selection(struct gui_window *g); void gui_clear_selection(struct gui_window *g); -void gui_paste_from_clipboard(struct gui_window *g, int x, int y); -bool gui_empty_clipboard(void); -bool gui_add_to_clipboard(const char *text, size_t length, bool space, - const plot_font_style_t *fstyle); -bool gui_commit_clipboard(void); -bool gui_copy_to_clipboard(struct selection *s); + + +/** + * Core asks front end for clipboard contents. + * + * \param buffer UTF-8 text, allocated by front end, ownership yeilded to core + * \param length Byte length of UTF-8 text in buffer + */ +void gui_get_clipboard(char **buffer, size_t *length); + +typedef struct nsnsclipboard_styles { + size_t start; /**< Start of run */ + + plot_font_style_t style; /**< Style to give text run */ +} nsclipboard_styles; +/** + * Core tells front end to put given text in clipboard + * + * \param buffer UTF-8 text, owned by core + * \param length Byte length of UTF-8 text in buffer + * \param styles Array of styles given to text runs, owned by core, or NULL + * \param n_styles Number of text run styles in array + */ +void gui_set_clipboard(const char *buffer, size_t length, + nsclipboard_styles styles[], int n_styles); + + void gui_create_form_select_menu(struct browser_window *bw, struct form_control *control); -- cgit v1.2.3 From 147f2bd08aa48ba6a4f945e28db05b378ab85a13 Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Tue, 8 Jan 2013 16:53:00 +0000 Subject: Remove content_msg for PASTE, since it doesn't need a gui_window to get the buffer from the front end now. --- content/content.h | 3 +-- desktop/browser.c | 6 ------ render/html.c | 1 - 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/content/content.h b/content/content.h index 649f54dfa..7781ba9b8 100644 --- a/content/content.h +++ b/content/content.h @@ -78,8 +78,7 @@ typedef enum { CONTENT_MSG_SCROLL, /**< Request to scroll content */ CONTENT_MSG_DRAGSAVE, /**< Allow drag saving of content */ CONTENT_MSG_SAVELINK, /**< Allow URL to be saved */ - CONTENT_MSG_POINTER, /**< Wants a specific mouse pointer set */ - CONTENT_MSG_PASTE /**< Inform that content wants clipboard paste */ + CONTENT_MSG_POINTER /**< Wants a specific mouse pointer set */ } content_msg; /** RFC5988 metadata link */ diff --git a/desktop/browser.c b/desktop/browser.c index 6262f2453..6a1688192 100644 --- a/desktop/browser.c +++ b/desktop/browser.c @@ -1545,12 +1545,6 @@ nserror browser_window_callback(hlcache_handle *c, browser_window_set_pointer(bw, event->data.pointer); break; - case CONTENT_MSG_PASTE: - /* Content wants a clipboard paste */ - gui_paste_from_clipboard(bw->window, - event->data.paste.x, event->data.paste.y); - break; - default: assert(0); } diff --git a/render/html.c b/render/html.c index 19ea5a9d0..3e26928fd 100644 --- a/render/html.c +++ b/render/html.c @@ -1302,7 +1302,6 @@ html_object_callback(hlcache_handle *object, case CONTENT_MSG_SAVELINK: case CONTENT_MSG_POINTER: - case CONTENT_MSG_PASTE: /* These messages are for browser window layer. * we're not interested, so pass them on. */ content_broadcast(&c->base, event->type, event->data); -- cgit v1.2.3 From de63a64b5034900c6673421d4f66e41d68834cb5 Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Tue, 8 Jan 2013 16:54:46 +0000 Subject: Update to use new front end clipboard functions. Now we build the buffer from each individual text box in the core, instead of in each front end. Styles for text runs are generated in the HTML case. --- desktop/selection.c | 159 +++++++++++++++++++++++++++++----------------------- 1 file changed, 88 insertions(+), 71 deletions(-) diff --git a/desktop/selection.c b/desktop/selection.c index f4f44e117..ae9df5ec6 100644 --- a/desktop/selection.c +++ b/desktop/selection.c @@ -77,6 +77,9 @@ struct selection_string { char *buffer; size_t buffer_len; size_t length; + + int n_styles; + nsclipboard_styles *styles; }; @@ -742,69 +745,6 @@ void selection_redraw(struct selection *s, unsigned start_idx, unsigned end_idx) } -/** - * Selection traversal routine for appending text to the current contents - * of the clipboard. - * - * \param text pointer to text being added, or NULL for newline - * \param length length of text to be appended (bytes) - * \param box pointer to text box, or NULL if from textplain - * \param handle unused handle, we don't need one - * \param whitespace_text whitespace to place before text for formatting - * may be NULL - * \param whitespace_length length of whitespace_text - * \return true iff successful and traversal should continue - */ - -static bool selection_copy_clip_handler(const char *text, size_t length, - struct box *box, void *handle, const char *whitespace_text, - size_t whitespace_length) -{ - bool add_space = false; - plot_font_style_t style = *plot_style_font; - - /* add any whitespace which precedes the text from this box */ - if (whitespace_text != NULL && whitespace_length > 0) { - if (!gui_add_to_clipboard(whitespace_text, - whitespace_length, false, &style)) { - return false; - } - } - - if (box != NULL) { - /* HTML */ - add_space = (box->space != 0); - - if (box->style != NULL) { - /* Override default font style */ - font_plot_style_from_css(box->style, &style); - } else { - /* If there's no style, there must be no text */ - assert(box->text == NULL); - } - } - - /* add the text from this box */ - if (!gui_add_to_clipboard(text, length, add_space, &style)) - return false; - - return true; -} - - -/** - * Copy the selected contents to the clipboard - * - * \param s selection - * \return true iff successful, ie. cut operation can proceed without losing data - */ - -bool selection_copy_to_clipboard(struct selection *s) -{ - return selection_traverse(s, selection_copy_clip_handler, NULL); -} - - /** * Append text to selection string. * @@ -816,11 +756,34 @@ bool selection_copy_to_clipboard(struct selection *s) */ static bool selection_string_append(const char *text, size_t length, bool space, - struct selection_string *sel_string) + plot_font_style_t *style, struct selection_string *sel_string) { size_t new_length = sel_string->length + length + (space ? 1 : 0) + 1; + if (style != NULL) { + /* Add text run style */ + nsclipboard_styles *new_styles; + + if (sel_string->n_styles == 0) + assert(sel_string->length == 0); + + new_styles = realloc(sel_string->styles, + (sel_string->n_styles + 1) * + sizeof(nsclipboard_styles)); + if (new_styles == NULL) + return false; + + sel_string->styles = new_styles; + + sel_string->styles[sel_string->n_styles].style = *style; + sel_string->styles[sel_string->n_styles].start = + sel_string->length; + + sel_string->n_styles++; + } + if (new_length > sel_string->buffer_len) { + /* Need to extend buffer */ size_t new_alloc = new_length + (new_length / 4); char *new_buff; @@ -832,12 +795,14 @@ static bool selection_string_append(const char *text, size_t length, bool space, sel_string->buffer_len = new_alloc; } + /* Copy text onto end of existing text in buffer */ memcpy(sel_string->buffer + sel_string->length, text, length); sel_string->length += length; if (space) sel_string->buffer[sel_string->length++] = ' '; + /* Ensure NULL termination */ sel_string->buffer[sel_string->length] = '\0'; return true; @@ -862,11 +827,13 @@ static bool selection_copy_handler(const char *text, size_t length, size_t whitespace_length) { bool add_space = false; + plot_font_style_t style; + plot_font_style_t *pstyle = NULL; /* add any whitespace which precedes the text from this box */ if (whitespace_text != NULL && whitespace_length > 0) { if (!selection_string_append(whitespace_text, - whitespace_length, false, handle)) { + whitespace_length, false, pstyle, handle)) { return false; } } @@ -874,10 +841,19 @@ static bool selection_copy_handler(const char *text, size_t length, if (box != NULL) { /* HTML */ add_space = (box->space != 0); + + if (box->style != NULL) { + /* Override default font style */ + font_plot_style_from_css(box->style, &style); + pstyle = &style; + } else { + /* If there's no style, there must be no text */ + assert(box->text == NULL); + } } /* add the text from this box */ - if (!selection_string_append(text, length, add_space, handle)) + if (!selection_string_append(text, length, add_space, pstyle, handle)) return false; return true; @@ -891,28 +867,69 @@ static bool selection_copy_handler(const char *text, size_t length, * \return string of selected text, or NULL. Ownership passed to caller. */ -char * selection_get_copy(struct selection *s) +char *selection_get_copy(struct selection *s) { struct selection_string sel_string = { .buffer = NULL, .buffer_len = 0, - .length = 0 + .length = 0, + + .n_styles = 0, + .styles = NULL }; if (s == NULL || !s->defined) return NULL; if (!selection_traverse(s, selection_copy_handler, &sel_string)) { - if (sel_string.buffer != NULL) { - free(sel_string.buffer); - } + free(sel_string.buffer); + free(sel_string.styles); return NULL; } + free(sel_string.styles); + return sel_string.buffer; } + +/** + * Copy the selected contents to the clipboard + * + * \param s selection + * \return true iff successful + */ +bool selection_copy_to_clipboard(struct selection *s) +{ + struct selection_string sel_string = { + .buffer = NULL, + .buffer_len = 0, + .length = 0, + + .n_styles = 0, + .styles = NULL + }; + + if (s == NULL || !s->defined) + return false; + + if (!selection_traverse(s, selection_copy_handler, &sel_string)) { + free(sel_string.buffer); + free(sel_string.styles); + return false; + } + + gui_set_clipboard(sel_string.buffer, sel_string.length, + sel_string.styles, sel_string.n_styles); + + free(sel_string.buffer); + free(sel_string.styles); + + return true; +} + + /** * Clears the current selection, optionally causing the screen to be updated. * -- cgit v1.2.3 From 8af3dd9a433fda37b27a66608d9d8b507c4bb2aa Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Tue, 8 Jan 2013 16:59:26 +0000 Subject: Redo html textinput for new front end clipboard API. This doesn't affect the textinput code's brokenness. --- desktop/textinput.c | 4 +- render/textinput.c | 129 +++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 106 insertions(+), 27 deletions(-) diff --git a/desktop/textinput.c b/desktop/textinput.c index 4b3d0b6ac..b4fda5eef 100644 --- a/desktop/textinput.c +++ b/desktop/textinput.c @@ -130,7 +130,7 @@ bool browser_window_key_press(struct browser_window *bw, uint32_t key) /* safe keys that can be handled whether input claimed or not */ switch (key) { case KEY_COPY_SELECTION: - gui_copy_to_clipboard(bw->cur_sel); + selection_copy_to_clipboard(bw->cur_sel); return true; case KEY_CLEAR_SELECTION: @@ -172,6 +172,8 @@ bool browser_window_key_press(struct browser_window *bw, uint32_t key) * \param utf8_len length (bytes) of text block * \param last true iff this is the last chunk (update screen too) * \return true iff successful + * + * TODO: Remove this function. */ bool browser_window_paste_text(struct browser_window *bw, const char *utf8, diff --git a/render/textinput.c b/render/textinput.c index 15e89f20d..71b4e53e0 100644 --- a/render/textinput.c +++ b/render/textinput.c @@ -76,6 +76,13 @@ static bool textinput_input_paste_text(struct browser_window *bw, #define SPACE_LEN(b) ((b->space == 0) ? 0 : 1) +static struct textinput_buffer { + char *buffer; + size_t buffer_len; + size_t length; +} textinput_buffer; + + /** * Given the x,y co-ordinates of a point within a textarea, return the @@ -524,6 +531,69 @@ static struct box *textinput_line_below(struct box *text_box) } +/** + * Add some text to the buffer, optionally appending a trailing space. + * + * \param text text to be added + * \param length length of text in bytes + * \param space indicates whether a trailing space should be appended + * \param fstyle The font style + * \return true if successful + */ + +static bool textinput_add_to_buffer(const char *text, size_t length, bool space, + const plot_font_style_t *fstyle) +{ + size_t new_length = textinput_buffer.length + length + (space ? 1 : 0) + 1; + + if (new_length > textinput_buffer.buffer_len) { + size_t new_alloc = new_length + (new_length / 4); + char *new_buff; + + new_buff = realloc(textinput_buffer.buffer, new_alloc); + if (new_buff == NULL) + return false; + + textinput_buffer.buffer = new_buff; + textinput_buffer.buffer_len = new_alloc; + } + + memcpy(textinput_buffer.buffer + textinput_buffer.length, text, length); + textinput_buffer.length += length; + + if (space) + textinput_buffer.buffer[textinput_buffer.length++] = ' '; + + textinput_buffer.buffer[textinput_buffer.length] = '\0'; + + return true; +} + + +/** + * Empty the buffer, called prior to textinput_add_to_buffer sequence + * + * \return true iff successful + */ + +static bool textinput_empty_buffer(void) +{ + const size_t init_size = 1024; + + if (textinput_buffer.buffer_len == 0) { + textinput_buffer.buffer = malloc(init_size); + if (textinput_buffer.buffer == NULL) + return false; + + textinput_buffer.buffer_len = init_size; + } + + textinput_buffer.length = 0; + + return true; +} + + /** * Cut a range of text from a text box, * possibly placing it on the global clipboard. @@ -546,27 +616,26 @@ static bool textinput_textarea_cut(struct content *c, bool success = true; bool del = false; /* caller expects start_box to persist */ - if (clipboard && !gui_empty_clipboard()) + if (textinput_empty_buffer() == false) { return false; + } while (box && box != end_box) { /* read before deletion, in case the whole box goes */ struct box *next = box->next; if (box->type == BOX_BR) { - if (clipboard && !gui_add_to_clipboard("\n", 1, false, - plot_style_font)) { - gui_commit_clipboard(); + if (clipboard && !textinput_add_to_buffer("\n", 1, + false, plot_style_font)) { return false; } box_unlink_and_free(box); } else { /* append box text to clipboard and then delete it */ if (clipboard && - !gui_add_to_clipboard(box->text + start_idx, + !textinput_add_to_buffer(box->text + start_idx, box->length - start_idx, SPACE_LEN(box), plot_style_font)) { - gui_commit_clipboard(); return false; } @@ -575,7 +644,6 @@ static bool textinput_textarea_cut(struct content *c, start_idx, (box->length + SPACE_LEN(box)) - start_idx) && clipboard) { - gui_commit_clipboard(); return false; } } else { @@ -592,7 +660,7 @@ static bool textinput_textarea_cut(struct content *c, /* and the last box */ if (box) { - if (clipboard && !gui_add_to_clipboard(box->text + start_idx, + if (clipboard && !textinput_add_to_buffer(box->text + start_idx, end_idx - start_idx, end_idx > box->length, plot_style_font)) { success = false; @@ -608,10 +676,12 @@ static bool textinput_textarea_cut(struct content *c, } } - if (clipboard && !gui_commit_clipboard()) - success = false; + if (clipboard) { + gui_set_clipboard(textinput_buffer.buffer, + textinput_buffer.length, NULL, 0); + } - return success; + return true; } @@ -1310,14 +1380,18 @@ bool textinput_textarea_callback(struct browser_window *bw, uint32_t key, case KEY_PASTE: { - union content_msg_data msg_data; - msg_data.paste.x = box_x + inline_container->x + - text_box->x + pixel_offset; - msg_data.paste.y = box_y + inline_container->y + text_box->y; - content_broadcast(c, CONTENT_MSG_PASTE, msg_data); + char *buff; + size_t buff_len; + bool success; - /* screen updated and caret repositioned already */ - return true; + gui_get_clipboard(&buff, &buff_len); + if (utf8 == NULL) + return false; + + success = browser_window_paste_text(bw, buff, buff_len, true); + free(buff); + + return success; } case KEY_CUT_SELECTION: @@ -1804,7 +1878,6 @@ bool textinput_input_callback(struct browser_window *bw, uint32_t key, struct box *text_box = input->children->children; size_t box_offset = input->gadget->caret_box_offset; size_t end_offset; - int pixel_offset = input->gadget->caret_pixel_offset; int box_x, box_y; struct form* form = input->gadget->form; bool changed = false; @@ -1954,14 +2027,18 @@ bool textinput_input_callback(struct browser_window *bw, uint32_t key, case KEY_PASTE: { - union content_msg_data msg_data; - msg_data.paste.x = box_x + input->children->x + text_box->x + - pixel_offset; - msg_data.paste.y = box_y + input->children->y + text_box->y; - content_broadcast(c, CONTENT_MSG_PASTE, msg_data); + char *buff; + size_t buff_len; + bool success; - /* screen updated and caret repositioned already */ - return true; + gui_get_clipboard(&buff, &buff_len); + if (utf8 == NULL) + return false; + + success = browser_window_paste_text(bw, buff, buff_len, true); + free(buff); + + return success; } case KEY_CUT_SELECTION: -- cgit v1.2.3 From e2e7aa135c5f9c5ce20ce6523dc8c248883b9e3b Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Tue, 8 Jan 2013 17:02:27 +0000 Subject: Update GTK front end to implement new clipboard API. --- gtk/scaffolding.c | 6 ++-- gtk/selection.c | 84 +++++++++++++++++++++++++++++-------------------------- 2 files changed, 48 insertions(+), 42 deletions(-) diff --git a/gtk/scaffolding.c b/gtk/scaffolding.c index 8b9b8f3b1..287fed12b 100644 --- a/gtk/scaffolding.c +++ b/gtk/scaffolding.c @@ -971,21 +971,21 @@ MULTIHANDLER(copy) if (GTK_IS_EDITABLE (focused)) gtk_editable_copy_clipboard(GTK_EDITABLE(g->url_bar)); else - gui_copy_to_clipboard(browser_window_get_selection(bw)); + browser_window_key_press(bw, KEY_COPY_SELECTION); return TRUE; } MULTIHANDLER(paste) { - struct gui_window *gui = g->top_level; + struct browser_window *bw = nsgtk_get_browser_window(g->top_level); GtkWidget *focused = gtk_window_get_focus(g->window); /* If the url bar has focus, let gtk handle it */ if (GTK_IS_EDITABLE (focused)) gtk_editable_paste_clipboard (GTK_EDITABLE (focused)); else - gui_paste_from_clipboard(gui, 0, 0); + browser_window_key_press(bw, KEY_PASTE); return TRUE; } diff --git a/gtk/selection.c b/gtk/selection.c index fd0b7c84b..7d516e944 100644 --- a/gtk/selection.c +++ b/gtk/selection.c @@ -31,31 +31,10 @@ static GString *current_selection = NULL; static GtkClipboard *clipboard; -bool gui_add_to_clipboard(const char *text, size_t length, bool space, - const plot_font_style_t *fstyle) -{ - /* add the text from this box */ - current_selection = g_string_append_len(current_selection, - text, length); - if (space) g_string_append(current_selection, " "); - return true; -} -bool gui_copy_to_clipboard(struct selection *s) -{ - clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD); - if (s->defined && selection_copy_to_clipboard(s)) - gui_commit_clipboard(); - return true; -} void gui_start_selection(struct gui_window *g) { - if (current_selection == NULL) - current_selection = g_string_new(NULL); - else - g_string_set_size(current_selection, 0); - gtk_widget_grab_focus(GTK_WIDGET(nsgtk_window_get_layout(g))); } @@ -63,34 +42,61 @@ void gui_clear_selection(struct gui_window *g) { } -void gui_paste_from_clipboard(struct gui_window *g, int x, int y) +/** + * Core asks front end for clipboard contents. + * + * \param buffer UTF-8 text, allocated by front end, ownership yeilded to core + * \param length Byte length of UTF-8 text in buffer + */ +void gui_get_clipboard(char **buffer, size_t *length) { - gchar *text; - clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD); - text = gtk_clipboard_wait_for_text (clipboard); - /* clipboard_wait... converts the string to utf8 for us */ - if (text != NULL) - browser_window_paste_text(nsgtk_get_browser_window(g), - text, strlen(text), true); - g_free(text); + gchar *gtext; + + *buffer = NULL; + *length = 0; + + /* get clipboard contents from gtk */ + clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); + gtext = gtk_clipboard_wait_for_text(clipboard); /* conv to utf-8 */ + + if (gtext == NULL) + return; + + *length = strlen(gtext); + *buffer = malloc(*length); + if (*buffer == NULL) { + *length = 0; + g_free(gtext); + return; + } + + memcpy(*buffer, gtext, *length); + + g_free(gtext); } -bool gui_empty_clipboard(void) + +/** + * Core tells front end to put given text in clipboard + * + * \param buffer UTF-8 text, owned by core + * \param length Byte length of UTF-8 text in buffer + * \param styles Array of styles given to text runs, owned by core, or NULL + * \param n_styles Number of text run styles in array + */ +void gui_set_clipboard(const char *buffer, size_t length, + nsclipboard_styles styles[], int n_styles) { + clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); + if (!current_selection) current_selection = g_string_new(NULL); else g_string_set_size(current_selection, 0); - return true; -} + current_selection = g_string_append_len(current_selection, + buffer, length); -bool gui_commit_clipboard(void) -{ - clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); gtk_clipboard_set_text(clipboard, current_selection->str, -1); - gui_empty_clipboard(); - - return true; } -- cgit v1.2.3 From fc9199f14eb9444453fe7f8f0bb2362687c3fee2 Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Tue, 8 Jan 2013 17:03:12 +0000 Subject: Update framebuffer front end to implement new clipboard API. --- framebuffer/clipboard.c | 123 +++++++++++++----------------------------------- 1 file changed, 34 insertions(+), 89 deletions(-) diff --git a/framebuffer/clipboard.c b/framebuffer/clipboard.c index 241e43415..bd9c89dca 100644 --- a/framebuffer/clipboard.c +++ b/framebuffer/clipboard.c @@ -37,117 +37,62 @@ static struct gui_clipboard { } gui_clipboard; + + /** - * Empty the clipboard, called prior to gui_add_to_clipboard and - * gui_commit_clipboard + * Core asks front end for clipboard contents. * - * \return true iff successful + * \param buffer UTF-8 text, allocated by front end, ownership yeilded to core + * \param length Byte length of UTF-8 text in buffer */ - -bool gui_empty_clipboard(void) +void gui_get_clipboard(char **buffer, size_t *length) { - const size_t init_size = 1024; + *buffer = NULL; + *length = 0; - if (gui_clipboard.buffer_len == 0) { - gui_clipboard.buffer = malloc(init_size); - if (gui_clipboard.buffer == NULL) - return false; - - gui_clipboard.buffer_len = init_size; - } + if (gui_clipboard.length > 0) { + assert(gui_clipboard.buffer != NULL); + LOG(("Pasting %i bytes: \"%s\"\n", gui_clipboard.length, + gui_clipboard.buffer)); - gui_clipboard.length = 0; + *buffer = malloc(gui_clipboard.length); - return true; + if (*buffer != NULL) { + memcpy(*buffer, gui_clipboard.buffer, + gui_clipboard.length); + *length = gui_clipboard.length; + } + } } /** - * Add some text to the clipboard, optionally appending a trailing space. + * Core tells front end to put given text in clipboard * - * \param text text to be added - * \param length length of text in bytes - * \param space indicates whether a trailing space should be appended - * \param fstyle The font style - * \return true if successful + * \param buffer UTF-8 text, owned by core + * \param length Byte length of UTF-8 text in buffer + * \param styles Array of styles given to text runs, owned by core, or NULL + * \param n_styles Number of text run styles in array */ - -bool gui_add_to_clipboard(const char *text, size_t length, bool space, - const plot_font_style_t *fstyle) +void gui_set_clipboard(const char *buffer, size_t length, + nsclipboard_styles styles[], int n_styles) { - size_t new_length = gui_clipboard.length + length + (space ? 1 : 0) + 1; - - if (new_length > gui_clipboard.buffer_len) { - size_t new_alloc = new_length + (new_length / 4); + if (gui_clipboard.buffer_len < length + 1) { + /* Make buffer big enough */ char *new_buff; - new_buff = realloc(gui_clipboard.buffer, new_alloc); + new_buff = realloc(gui_clipboard.buffer, length + 1); if (new_buff == NULL) - return false; + return; gui_clipboard.buffer = new_buff; - gui_clipboard.buffer_len = new_alloc; + gui_clipboard.buffer_len = length + 1; } - memcpy(gui_clipboard.buffer + gui_clipboard.length, text, length); - gui_clipboard.length += length; - - if (space) - gui_clipboard.buffer[gui_clipboard.length++] = ' '; + gui_clipboard.length = 0; + memcpy(gui_clipboard.buffer, buffer, length); + gui_clipboard.length = length; gui_clipboard.buffer[gui_clipboard.length] = '\0'; - - return true; -} - - -/** - * Commit the changes made by gui_empty_clipboard and gui_add_to_clipboard. - * - * \return true iff successful - */ - -bool gui_commit_clipboard(void) -{ - /* TODO: Stick the clipboard in some fbtk buffer? */ - return true; -} - - -/** - * Copy the selected contents to the clipboard - * - * \param s selection - * \return true iff successful, ie. cut operation can proceed without losing data - */ - -bool gui_copy_to_clipboard(struct selection *s) -{ - if (!gui_empty_clipboard()) - return false; - - selection_copy_to_clipboard(s); - - return gui_commit_clipboard(); -} - - -/** - * Request to paste the clipboard contents into a textarea/input field - * at a given position. - * - * \param g gui window - * \param x x ordinate at which to paste text - * \param y y ordinate at which to paste text - */ - -void gui_paste_from_clipboard(struct gui_window *g, int x, int y) -{ - if (gui_clipboard.length > 0) { - LOG(("Pasting %i chars: \"%s\"\n", gui_clipboard.length, - gui_clipboard.buffer)); - browser_window_paste_text(g->bw, gui_clipboard.buffer, - gui_clipboard.length, true); - } } -- cgit v1.2.3 From 00a08e7b94fa7323248b73a75d859fa8e15a44eb Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Tue, 8 Jan 2013 17:03:50 +0000 Subject: Update RISC OS front end to implement new clipboard API. TODO: Handle paste when NetSurf doesn't own clipboard. Might be able to do some setup before KEY_PASTE is passed to the core. --- riscos/textselection.c | 148 ++++++++++++------------------------------------- 1 file changed, 36 insertions(+), 112 deletions(-) diff --git a/riscos/textselection.c b/riscos/textselection.c index 96c3e2c90..d1d4340b7 100644 --- a/riscos/textselection.c +++ b/riscos/textselection.c @@ -183,33 +183,6 @@ void ro_gui_selection_drag_end(struct gui_window *g, wimp_dragged *drag) } -/** - * Empty the clipboard, called prior to gui_add_to_clipboard and - * gui_commit_clipboard - * - * \return true iff successful - */ - -bool gui_empty_clipboard(void) -{ - const int init_size = 1024; - - if (!clip_alloc) { - clipboard = malloc(init_size); - if (!clipboard) { - LOG(("out of memory")); - warn_user("NoMemory", 0); - return false; - } - clip_alloc = init_size; - } - - clip_length = 0; - - return true; -} - - /** * Perform tasks after a selection has been cleared. * @@ -224,63 +197,35 @@ void gui_clear_selection(struct gui_window *g) /** - * Add some text to the clipboard, optionally appending a trailing space. + * Core tells front end to put given text in clipboard * - * \param text text to be added - * \param length length of text in bytes - * \param space indicates whether a trailing space should be appended also - * \param fstyle font plot style for text - * \return true iff successful + * \param buffer UTF-8 text, owned by core + * \param length Byte length of UTF-8 text in buffer + * \param styles Array of styles given to text runs, owned by core, or NULL + * \param n_styles Number of text run styles in array */ - -bool gui_add_to_clipboard(const char *text, size_t length, bool space, - const plot_font_style_t *fstyle) +void gui_set_clipboard(const char *buffer, size_t length, + nsclipboard_styles styles[], int n_styles) { - size_t new_length = clip_length + length + (space ? 1 : 0); - - if (new_length > clip_alloc) { - size_t new_alloc = clip_alloc + (clip_alloc / 4); - char *new_cb; - - if (new_alloc < new_length) new_alloc = new_length; - - new_cb = realloc(clipboard, new_alloc); - if (!new_cb) return false; - - clipboard = new_cb; - clip_alloc = new_alloc; - } + utf8_convert_ret res; + char *new_cb; - memcpy(clipboard + clip_length, text, length); - clip_length += length; - if (space) clipboard[clip_length++] = ' '; - - return true; -} + if (length == 0) + return; + /* Convert to local encoding */ + res = utf8_to_local_encoding(buffer, length, &new_cb); -/** - * Commit the changes made by gui_empty_clipboard and gui_add_to_clipboard. - * - * \return true iff successful - */ + if (res != UTF8_CONVERT_OK || new_cb == NULL) + return; -bool gui_commit_clipboard(void) -{ - if (clip_length) { - utf8_convert_ret res; - char *new_cb; - - res = utf8_to_local_encoding(clipboard, clip_length, &new_cb); - if (res == UTF8_CONVERT_OK) { - free(clipboard); - clipboard = new_cb; -/* \todo utf8_to_local_encoding should return the length! */ - clip_alloc = clip_length = strlen(new_cb); - } - } + /* Replace existing clipboard contents with converted text */ + free(clipboard); + clipboard = new_cb; + clip_alloc = clip_length = strlen(new_cb); if (!owns_clipboard) { + /* Tell RO we now own clipboard */ wimp_full_message_claim_entity msg; os_error *error; @@ -302,44 +247,20 @@ bool gui_commit_clipboard(void) } LOG(("clipboard now holds %zd bytes", clip_length)); - - return true; } - /** - * Copy the selected contents to the global clipboard, - * and claim ownership of the clipboard from other apps. + * Core asks front end for clipboard contents. * - * \param s selection - * \return true iff successful, ie. cut operation can proceed without losing data + * \param buffer UTF-8 text, allocated by front end, ownership yeilded to core + * \param length Byte length of UTF-8 text in buffer */ - -bool gui_copy_to_clipboard(struct selection *s) +void gui_get_clipboard(char **buffer, size_t *length) { - if (!gui_empty_clipboard()) - return false; - - selection_copy_to_clipboard(s); - - return gui_commit_clipboard(); -} + *buffer = NULL; + *length = 0; - -/** - * Request to paste the clipboard contents into a textarea/input field - * at a given position. Note that the actual operation may take place - * straight away (local clipboard) or in a number of chunks at some - * later time (clipboard owned by another app). - * - * \param g gui window - * \param x x ordinate at which to paste text - * \param y y ordinate at which to paste text - */ - -void gui_paste_from_clipboard(struct gui_window *g, int x, int y) -{ if (owns_clipboard) { if (clip_length > 0) { char *utf8; @@ -349,14 +270,17 @@ void gui_paste_from_clipboard(struct gui_window *g, int x, int y) ret = utf8_from_local_encoding(clipboard, clip_length, &utf8); if (ret == UTF8_CONVERT_OK) { - browser_window_paste_text(g->bw, utf8, - strlen(utf8), true); - free(utf8); + *buffer = utf8; + *length = strlen(utf8); } } - } - else { - wimp_full_message_data_request msg; + } else { + /** TODO: Handle case when we don't own the clipboard */ + +/* http://www.starfighter.acornarcade.com/mysite/articles/SelectionModel.html + */ + +/* wimp_full_message_data_request msg; os_error *error; os_coord pos; @@ -381,7 +305,7 @@ void gui_paste_from_clipboard(struct gui_window *g, int x, int y) error->errnum, error->errmess)); warn_user("WimpError", error->errmess); } - } +*/ } } -- cgit v1.2.3 From 0bfc40618a7c9d40cacc5de3c34306c19ebaef1e Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Tue, 8 Jan 2013 17:06:01 +0000 Subject: Make Monkey front end build with new clipboard API. --- monkey/browser.c | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/monkey/browser.c b/monkey/browser.c index ddea08675..e6a57d00b 100644 --- a/monkey/browser.c +++ b/monkey/browser.c @@ -327,34 +327,28 @@ gui_clear_selection(struct gui_window *g) { } -void -gui_paste_from_clipboard(struct gui_window *g, int x, int y) -{ -} - -bool -gui_empty_clipboard(void) -{ - return true; -} - -bool -gui_add_to_clipboard(const char *text, size_t length, bool space, - const plot_font_style_t *fstyle) +/** + * Core asks front end for clipboard contents. + * + * \param buffer UTF-8 text, allocated by front end, ownership yeilded to core + * \param length Byte length of UTF-8 text in buffer + */ +void gui_get_clipboard(char **buffer, size_t *length) { - return true; } -bool -gui_commit_clipboard(void) -{ - return true; -} -bool -gui_copy_to_clipboard(struct selection *s) +/** + * Core tells front end to put given text in clipboard + * + * \param buffer UTF-8 text, owned by core + * \param length Byte length of UTF-8 text in buffer + * \param styles Array of styles given to text runs, owned by core, or NULL + * \param n_styles Number of text run styles in array + */ +void gui_set_clipboard(const char *buffer, size_t length, + nsclipboard_styles styles[], int n_styles) { - return true; } void -- cgit v1.2.3 From 94f13d85551f45d323ed8658f8e849214a5c60e6 Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Tue, 8 Jan 2013 17:06:37 +0000 Subject: Untested update for new clipboard API. --- cocoa/selection.m | 67 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 47 insertions(+), 20 deletions(-) diff --git a/cocoa/selection.m b/cocoa/selection.m index 6bba74d34..69707ba33 100644 --- a/cocoa/selection.m +++ b/cocoa/selection.m @@ -36,47 +36,74 @@ void gui_clear_selection(struct gui_window *g) } -void gui_paste_from_clipboard(struct gui_window *g, int x, int y) +/** + * Core asks front end for clipboard contents. + * + * \param buffer UTF-8 text, allocated by front end, ownership yeilded to core + * \param length Byte length of UTF-8 text in buffer + */ +void gui_get_clipboard(char **buffer, size_t *length) { NSPasteboard *pb = [NSPasteboard generalPasteboard]; NSString *string = [pb stringForType: NSStringPboardType]; + + *buffer = NULL; + *length = 0; + if (string) { const char *text = [string UTF8String]; - browser_window_paste_text( [(BrowserViewController *)g browser], text, strlen(text), true ); + *length = strlen(text); + + *buffer = malloc(gui_clipboard.length); + + if (*buffer != NULL) { + memcpy(*buffer, text, *length); + } else { + *length = 0; + } } } -bool gui_empty_clipboard(void) +/** + * Core tells front end to put given text in clipboard + * + * \param buffer UTF-8 text, owned by core + * \param length Byte length of UTF-8 text in buffer + * \param styles Array of styles given to text runs, owned by core, or NULL + * \param n_styles Number of text run styles in array + */ +void gui_set_clipboard(const char *buffer, size_t length, + nsclipboard_styles styles[], int n_styles) { + /* Empty clipboard string */ if (nil == cocoa_clipboard_string) { cocoa_clipboard_string = [[NSMutableString alloc] init]; } else { [cocoa_clipboard_string setString: @""]; } - return true; -} -bool gui_add_to_clipboard(const char *text, size_t length, bool space, - const plot_font_style_t *fstyle) -{ + /* Add text to clipboard string */ if (nil == cocoa_clipboard_string) return false; - [cocoa_clipboard_string appendString: [[[NSString alloc] initWithBytes: text - length: length - encoding: NSUTF8StringEncoding] - autorelease]]; - if (space) [cocoa_clipboard_string appendString: @" "]; + [cocoa_clipboard_string appendString: [[[NSString alloc] + initWithBytes: buffer + length: length + encoding: NSUTF8StringEncoding] + autorelease]]; - return true; -} - -bool gui_commit_clipboard(void) -{ + /* Stick it on the pasteboard */ NSPasteboard *pb = [NSPasteboard generalPasteboard]; [pb declareTypes: [NSArray arrayWithObject: NSStringPboardType] owner: nil]; bool result = [pb setString: cocoa_clipboard_string forType: NSStringPboardType]; - if (result) gui_empty_clipboard(); - return result; + + if (result) { + /* Empty clipboard string */ + if (nil == cocoa_clipboard_string) { + cocoa_clipboard_string = [[NSMutableString alloc] init]; + } else { + [cocoa_clipboard_string setString: @""]; + } + } } bool gui_copy_to_clipboard(struct selection *s) -- cgit v1.2.3 From 9dc965d51cc014e4ae3028afd655eb1f30ca4ad1 Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Tue, 8 Jan 2013 17:07:04 +0000 Subject: Untested update for new clipboard API. --- windows/gui.c | 52 ++++++++++++++++++++-------------------------------- 1 file changed, 20 insertions(+), 32 deletions(-) diff --git a/windows/gui.c b/windows/gui.c index 78c2d1b10..9f44ddfc6 100644 --- a/windows/gui.c +++ b/windows/gui.c @@ -1727,8 +1727,15 @@ void gui_clear_selection(struct gui_window *w) { } -void gui_paste_from_clipboard(struct gui_window *w, int x, int y) +/** + * Core asks front end for clipboard contents. + * + * \param buffer UTF-8 text, allocated by front end, ownership yeilded to core + * \param length Byte length of UTF-8 text in buffer + */ +void gui_get_clipboard(char **buffer, size_t *length) { + /* TODO: Implement this */ HANDLE clipboard_handle; char *content; @@ -1740,14 +1747,18 @@ void gui_paste_from_clipboard(struct gui_window *w, int x, int y) } } -bool gui_empty_clipboard(void) -{ - return false; -} - -bool gui_add_to_clipboard(const char *text, size_t length, bool space, - const plot_font_style_t *fstyle) +/** + * Core tells front end to put given text in clipboard + * + * \param buffer UTF-8 text, owned by core + * \param length Byte length of UTF-8 text in buffer + * \param styles Array of styles given to text runs, owned by core, or NULL + * \param n_styles Number of text run styles in array + */ +void gui_set_clipboard(const char *buffer, size_t length, + nsclipboard_styles styles[], int n_styles) { + /* TODO: Implement this */ HANDLE hnew; char *new, *original; HANDLE h = GetClipboardData(CF_TEXT); @@ -1759,7 +1770,7 @@ bool gui_add_to_clipboard(const char *text, size_t length, bool space, size_t len = strlen(original) + 1; hnew = GlobalAlloc(GHND, length + len); new = (char *)GlobalLock(hnew); - snprintf(new, length + len, "%s%s", original, text); + snprintf(new, length + len, "%s%s", original, buffer); if (h != NULL) { GlobalUnlock(h); @@ -1767,29 +1778,6 @@ bool gui_add_to_clipboard(const char *text, size_t length, bool space, } GlobalUnlock(hnew); SetClipboardData(CF_TEXT, hnew); - return true; -} - -bool gui_commit_clipboard(void) -{ - return false; -} - - -bool gui_copy_to_clipboard(struct selection *s) -{ - if (selection_defined(s)) { - /* TODO: Fix this, so it's not looking inside selection - * object */ - -/* OpenClipboard(s->bw->window->main); - EmptyClipboard(); - if (selection_copy_to_clipboard(s)) { - CloseClipboard(); - return true; - } -*/ } - return false; } -- cgit v1.2.3 From 9cb1da921bd94b4d74afba656c357d7c1e23ee78 Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Tue, 8 Jan 2013 17:47:04 +0000 Subject: Prompt core to deal with copy/paste correctly. --- windows/gui.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/windows/gui.c b/windows/gui.c index 9f44ddfc6..e2f8f9afa 100644 --- a/windows/gui.c +++ b/windows/gui.c @@ -774,8 +774,7 @@ nsws_window_command(HWND hwnd, if (GetFocus() == gw->urlbar) { SendMessage(gw->urlbar, WM_COPY, 0, 0); } else if (gw->bw != NULL) { - gui_copy_to_clipboard( - browser_window_get_selection(gw->bw)); + browser_window_key_press(gw->bw, KEY_COPY_SELECTION); } break; @@ -791,7 +790,7 @@ nsws_window_command(HWND hwnd, if (GetFocus() == gw->urlbar) SendMessage(gw->urlbar, WM_PASTE, 0, 0); else - gui_paste_from_clipboard(gw, 0, 0); + browser_window_key_press(gw->bw, KEY_PASTE); break; } -- cgit v1.2.3 From fe210c5d5895eb53fd2325dff1cf68e831d5c4ec Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Tue, 8 Jan 2013 17:52:12 +0000 Subject: Fixups. --- cocoa/selection.m | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cocoa/selection.m b/cocoa/selection.m index 69707ba33..0acbd3b76 100644 --- a/cocoa/selection.m +++ b/cocoa/selection.m @@ -28,7 +28,6 @@ static NSMutableString *cocoa_clipboard_string; void gui_start_selection(struct gui_window *g) { - gui_empty_clipboard(); } void gui_clear_selection(struct gui_window *g) @@ -54,7 +53,7 @@ void gui_get_clipboard(char **buffer, size_t *length) const char *text = [string UTF8String]; *length = strlen(text); - *buffer = malloc(gui_clipboard.length); + *buffer = malloc(*length); if (*buffer != NULL) { memcpy(*buffer, text, *length); -- cgit v1.2.3 From 0767e590fdb43cfcbb39c7a6795c0efe5c58c79e Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Tue, 8 Jan 2013 17:54:55 +0000 Subject: Remove unused function. --- cocoa/selection.m | 7 ------- 1 file changed, 7 deletions(-) diff --git a/cocoa/selection.m b/cocoa/selection.m index 0acbd3b76..ef34b4e94 100644 --- a/cocoa/selection.m +++ b/cocoa/selection.m @@ -105,10 +105,3 @@ void gui_set_clipboard(const char *buffer, size_t length, } } -bool gui_copy_to_clipboard(struct selection *s) -{ - if (selection_defined( s ) && selection_copy_to_clipboard( s )) - gui_commit_clipboard(); - return true; -} - -- cgit v1.2.3 From e408c9ea4345c25efe2dff02e6b10ee05a582560 Mon Sep 17 00:00:00 2001 From: Chris Young Date: Tue, 8 Jan 2013 19:58:44 +0000 Subject: Update copy to new API (untested), pasting needs work, ami_drag_selection needs thought. --- amiga/clipboard.c | 125 ++++++++++++++++++------------------------------------ 1 file changed, 41 insertions(+), 84 deletions(-) diff --git a/amiga/clipboard.c b/amiga/clipboard.c index b24c2da65..f934388b5 100755 --- a/amiga/clipboard.c +++ b/amiga/clipboard.c @@ -137,7 +137,7 @@ bool ami_clipboard_check_for_utf8(struct IFFHandle *iffh) { return utf8_chunk; } -void gui_paste_from_clipboard(struct gui_window *g, int x, int y) +void gui_get_clipboard(char **buffer, size_t *length) { /* This and the other clipboard code is heavily based on the RKRM examples */ struct ContextNode *cn; @@ -193,7 +193,7 @@ void gui_paste_from_clipboard(struct gui_window *g, int x, int y) rlen, &clip); } - browser_window_paste_text(g->shared->bw,clip,rlen,true); + //browser_window_paste_text(g->shared->bw,clip,rlen,true); } if(rlen < 0) error = rlen; } @@ -202,7 +202,7 @@ void gui_paste_from_clipboard(struct gui_window *g, int x, int y) { while((rlen = ReadChunkBytes(iffh, readbuf, 1024)) > 0) { - browser_window_paste_text(g->shared->bw, readbuf, rlen, true); + //browser_window_paste_text(g->shared->bw, readbuf, rlen, true); } if(rlen < 0) error = rlen; } @@ -210,22 +210,24 @@ void gui_paste_from_clipboard(struct gui_window *g, int x, int y) CloseIFF(iffh); } -bool gui_empty_clipboard(void) +void gui_set_clipboard(const char *buffer, size_t length, + nsclipboard_styles styles[], int n_styles) { - /* Put a half-completed FTXT on the clipboard and leave it open for more additions */ - + char *text; struct CSet cset = {0}; - if(!(OpenIFF(iffh,IFFF_WRITE))) + if(buffer == NULL) return; + + if(!(OpenIFF(iffh, IFFF_WRITE))) { - if(!(PushChunk(iffh,ID_FTXT,ID_FORM,IFFSIZE_UNKNOWN))) + if(!(PushChunk(iffh, ID_FTXT, ID_FORM, IFFSIZE_UNKNOWN))) { if(nsoption_bool(utf8_clipboard)) { - if(!(PushChunk(iffh,0,ID_CSET,32))) + if(!(PushChunk(iffh, 0, ID_CSET, 32))) { cset.CodeSet = 106; // UTF-8 - WriteChunkBytes(iffh,&cset,32); + WriteChunkBytes(iffh, &cset, 32); PopChunk(iffh); } } @@ -233,82 +235,39 @@ bool gui_empty_clipboard(void) else { PopChunk(iffh); - return false; } - return true; - } - return false; -} - -bool gui_add_to_clipboard(const char *text, size_t length, bool space, - const plot_font_style_t *fstyle) -{ - /* This might crash or at least not work if gui_empty_clipboard isn't called first, - and gui_commit_clipboard after. - These only seem to be called from desktop/textinput.c in this specific order, if they - are added elsewhere this might need a rewrite. */ - - char *buffer; - - if(text == NULL) return true; - - if(!(PushChunk(iffh,0,ID_CHRS,IFFSIZE_UNKNOWN))) { - if(nsoption_bool(utf8_clipboard)) { - WriteChunkBytes(iffh,text,length); - } else { - if(utf8_to_local_encoding(text, length, &buffer) == UTF8_CONVERT_OK) { - char *p; - - p = buffer; - while(*p != '\0') { - if(*p == 0xa0) *p = 0x20; - p++; + if(!(PushChunk(iffh, 0, ID_CHRS, IFFSIZE_UNKNOWN))) { + if(nsoption_bool(utf8_clipboard)) { + WriteChunkBytes(iffh, buffer, length); + } else { + if(utf8_to_local_encoding(buffer, length, &text) == UTF8_CONVERT_OK) { + char *p; + + p = text; + + while(*p != '\0') { + if(*p == 0xa0) *p = 0x20; + p++; + } + WriteChunkBytes(iffh, textr, strlen(text)); + ami_utf8_free(text); } - WriteChunkBytes(iffh, buffer, strlen(buffer)); - ami_utf8_free(buffer); } - } - if(space) WriteChunkBytes(iffh," ",1); - PopChunk(iffh); - } else { - PopChunk(iffh); - return false; - } + PopChunk(iffh); + } else { + PopChunk(iffh); + } - if(!(PushChunk(iffh, 0, ID_UTF8, IFFSIZE_UNKNOWN))) { - WriteChunkBytes(iffh, text, length); - if(space) WriteChunkBytes(iffh, " ", 1); - PopChunk(iffh); - } else { - PopChunk(iffh); - return false; + if(!(PushChunk(iffh, 0, ID_UTF8, IFFSIZE_UNKNOWN))) { + WriteChunkBytes(iffh, buffer, length); + PopChunk(iffh); + } else { + PopChunk(iffh); + } + CloseIFF(iffh); } - - return true; -} - -bool gui_commit_clipboard(void) -{ - if(iffh) CloseIFF(iffh); - - return true; -} - -bool gui_copy_to_clipboard(struct selection *s) -{ - bool success; - - if(s->defined == false) return false; - if(!gui_empty_clipboard()) return false; - - success = selection_copy_to_clipboard(s); - - /* commit regardless, otherwise we leave the clipboard in an unusable state */ - gui_commit_clipboard(); - - return success; } struct ami_text_selection *ami_selection_to_text(struct gui_window_2 *gwin) @@ -340,6 +299,7 @@ struct ami_text_selection *ami_selection_to_text(struct gui_window_2 *gwin) return sel; } +#if 0 void ami_drag_selection(struct selection *s) { int x; @@ -404,14 +364,11 @@ void ami_drag_selection(struct selection *s) } } } +#endif bool ami_easy_clipboard(char *text) { - if(!gui_empty_clipboard()) return false; - if(!gui_add_to_clipboard(text,strlen(text),false,plot_style_font)) - return false; - if(!gui_commit_clipboard()) return false; - + gui_set_clipboard(text, strlen(text), NULL, 0); return true; } -- cgit v1.2.3 From 5a43a5a1a9f057a41ede60a763a2361a850a132c Mon Sep 17 00:00:00 2001 From: Chris Young Date: Tue, 8 Jan 2013 20:07:46 +0000 Subject: fix typo --- amiga/clipboard.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amiga/clipboard.c b/amiga/clipboard.c index f934388b5..3759aaa27 100755 --- a/amiga/clipboard.c +++ b/amiga/clipboard.c @@ -250,7 +250,7 @@ void gui_set_clipboard(const char *buffer, size_t length, if(*p == 0xa0) *p = 0x20; p++; } - WriteChunkBytes(iffh, textr, strlen(text)); + WriteChunkBytes(iffh, text, strlen(text)); ami_utf8_free(text); } } -- cgit v1.2.3 From 78f3f53622669443f997f94b84f8d99f60b00ed0 Mon Sep 17 00:00:00 2001 From: Chris Young Date: Tue, 8 Jan 2013 20:15:27 +0000 Subject: re-enable ami_drag_selection --- amiga/clipboard.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/amiga/clipboard.c b/amiga/clipboard.c index 3759aaa27..87071f626 100755 --- a/amiga/clipboard.c +++ b/amiga/clipboard.c @@ -299,7 +299,6 @@ struct ami_text_selection *ami_selection_to_text(struct gui_window_2 *gwin) return sel; } -#if 0 void ami_drag_selection(struct selection *s) { int x; @@ -364,7 +363,6 @@ void ami_drag_selection(struct selection *s) } } } -#endif bool ami_easy_clipboard(char *text) { -- cgit v1.2.3 From d1dabbb0e28edc5614940c08d594957faa956daf Mon Sep 17 00:00:00 2001 From: Chris Young Date: Tue, 8 Jan 2013 20:21:16 +0000 Subject: remove gui_copy_to_clipbaord call - this needs fixing --- amiga/clipboard.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/amiga/clipboard.c b/amiga/clipboard.c index 87071f626..018ea2604 100755 --- a/amiga/clipboard.c +++ b/amiga/clipboard.c @@ -320,13 +320,14 @@ void ami_drag_selection(struct selection *s) if(ami_text_box_at_point(gwin, (ULONG *)&x, (ULONG *)&y)) { iffh = ami_clipboard_init_internal(1); - +#if 0 +/* TODO: fix this */ if(gui_copy_to_clipboard(s)) { browser_window_mouse_click(gwin->bw, BROWSER_MOUSE_PRESS_1, x, y); browser_window_key_press(gwin->bw, KEY_PASTE); } - +#endif ami_clipboard_free_internal(iffh); iffh = old_iffh; } -- cgit v1.2.3