CLG plotter cleanup
by Vincent Sanders
OK another small tidy along the road to making the plotter API less mad
This one removes the clg call altogether. It was used in four places,
all of which set the clip rectangle just before clg was called!
This patch replaces these four calls with their direct equivalent fill
calls and removes the clg call from the plotters. Removes another
hundred lines of redundant code and makes the plotter API simpler.
amiga/plotters.c | 1 -
beos/beos_plotters.cpp | 23 -----------------------
desktop/knockout.c | 16 +---------------
desktop/plotters.h | 2 --
desktop/save_pdf/pdf_plotters.c | 7 -------
framebuffer/framebuffer.c | 1 -
gtk/gtk_plotters.c | 7 -------
gtk/gtk_print.c | 7 -------
render/html_redraw.c | 2 +-
render/textplain.c | 2 +-
riscos/plotters.c | 20 --------------------
riscos/print.c | 9 +++------
riscos/save_draw.c | 11 -----------
riscos/window.c | 2 +-
14 files changed, 7 insertions(+), 103 deletions(-)
--
Regards Vincent
http://www.kyllikki.org/
14 years, 2 months
Review: Paul Blokus -- Core treeview
by John-Mark Bell
Precis:
This is Paul Blokus' modification of the treeview code to utilise the
plotter interface and thus be platform agnostic.
Added files
Index: gtk/gtk_treeview.h
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ gtk/gtk_treeview.h 2009-06-23 16:08:38.000000000 +0100
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2004 Richard Wilson <not_ginger_matt(a)users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Generic tree handling.
+ */
+
+#ifndef __NSGTK_TREEVIEW_H__
+#define __NSGTK_TREEVIEW_H__
+
+#include "desktop/browser.h"
+
+struct gtk_treeview_window {
+ GtkWindow *window;
+ GtkScrolledWindow *scrolled;
+ GtkDrawingArea *drawing_area;
+ int mouse_pressed_x;
+ int mouse_pressed_y;
+ browser_mouse_state mouse_state;
+};
+
+gboolean nsgtk_tree_window_expose_event(GtkWidget *, GdkEventExpose *,
+ gpointer);
+gboolean nsgtk_tree_window_button_press_event(GtkWidget *widget,
+ GdkEventButton *event, gpointer g);
+gboolean nsgtk_tree_window_button_release_event(GtkWidget *widget,
+ GdkEventButton *event, gpointer g);
+gboolean nsgtk_tree_window_motion_notify_event(GtkWidget *widget,
+ GdkEventButton *event, gpointer g);
+
+void tree_redraw_start_callback(uintptr_t data);
+void tree_redraw_end_callback(uintptr_t data);
+
+#endif /*__NSGTK_TREEVIEW_H__*/
Index: gtk/res/small_dir.bmp
===================================================================
Binary files /dev/null and gtk/res/small_dir.bmp differ
Index: gtk/res/icon.bmp
===================================================================
Binary files /dev/null and gtk/res/icon.bmp differ
Index: desktop/history_global_core.c
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ desktop/history_global_core.c 2009-06-23 16:08:42.000000000 +0100
@@ -0,0 +1,379 @@
+/*
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <stdlib.h>
+
+#include "content/urldb.h"
+#include "desktop/browser.h"
+#include "desktop/history_global_core.h"
+#include "desktop/options.h"
+#include "desktop/plotters.h"
+#include "desktop/tree.h"
+#include "utils/messages.h"
+#include "utils/utils.h"
+#include "utils/log.h"
+
+#define MAXIMUM_BASE_NODES 16
+#define GLOBAL_HISTORY_RECENT_URLS 16
+
+static struct node *global_history_base_node[MAXIMUM_BASE_NODES];
+static int global_history_base_node_time[MAXIMUM_BASE_NODES];
+static int global_history_base_node_count = 0;
+
+static char *global_history_recent_url[GLOBAL_HISTORY_RECENT_URLS];
+static int global_history_recent_count = 0;
+
+static bool global_history_init;
+
+struct tree *global_history_tree;
+
+
+static void history_global_initialise_nodes(void);
+static void history_global_initialise_node(const char *title,
+ time_t base, int days_back);
+static void history_global_save(void);
+static bool global_history_add_internal(const char *url,
+ const struct url_data *data);
+static struct node *history_global_find(const char *url);
+
+
+bool history_global_initialise(uintptr_t handle,
+ void (*start_redraw)(uintptr_t data),
+ void (*end_redraw)(uintptr_t data))
+{
+
+ char s[MAXIMUM_URL_LENGTH];
+ FILE *fp;
+
+ /* Create an empty tree */
+ global_history_tree = calloc(sizeof(struct tree), 1);
+ if (!global_history_tree) {
+ warn_user("NoMemory", 0);
+ return false;
+ }
+ global_history_tree->root = tree_create_folder_node(NULL, "Root");
+ if (!global_history_tree->root) {
+ warn_user("NoMemory", 0);
+ free(global_history_tree);
+ global_history_tree = NULL;
+ return false;
+ }
+ global_history_tree->root->expanded = true;
+ history_global_initialise_nodes();
+ global_history_tree->movable = false;
+ global_history_tree->redraw = false;
+ global_history_tree->handle = handle;
+ global_history_tree->start_redraw = start_redraw;
+ global_history_tree->end_redraw = end_redraw;
+
+ /* load recent URLs */
+ fp = fopen(option_recent_file, "r");
+ if (!fp)
+ LOG(("Failed to open file '%s' for reading",
+ option_recent_file));
+ else {
+ while (fgets(s, MAXIMUM_URL_LENGTH, fp)) {
+ if (s[strlen(s) - 1] == '\n')
+ s[strlen(s) - 1] = '\0';
+ global_history_add_recent(s);
+ }
+ fclose(fp);
+ }
+
+ global_history_init = true;
+ urldb_iterate_entries(global_history_add_internal);
+ global_history_init = false;
+ tree_initialise(global_history_tree);
+
+ return true;
+}
+
+void history_global_initialise_nodes(void)
+{
+ struct tm *full_time;
+ time_t t;
+ int weekday;
+ int i;
+
+ /* get the current time */
+ t = time(NULL);
+ if (t == -1)
+ return;
+
+ /* get the time at the start of today */
+ full_time = localtime(&t);
+ weekday = full_time->tm_wday;
+ full_time->tm_sec = 0;
+ full_time->tm_min = 0;
+ full_time->tm_hour = 0;
+ t = mktime(full_time);
+ if (t == -1)
+ return;
+
+ history_global_initialise_node(messages_get("DateToday"), t, 0);
+ if (weekday > 0)
+ history_global_initialise_node(
+ messages_get("DateYesterday"), t, -1);
+ for (i = 2; i <= weekday; i++)
+ history_global_initialise_node(NULL, t, -i);
+ history_global_initialise_node(messages_get("Date1Week"),
+ t, -weekday - 7);
+ history_global_initialise_node(messages_get("Date2Week"),
+ t, -weekday - 14);
+ history_global_initialise_node(messages_get("Date3Week"),
+ t, -weekday - 21);
+}
+
+void history_global_initialise_node(const char *title,
+ time_t base, int days_back)
+{
+ struct tm *full_time;
+ char buffer[64];
+ struct node *node;
+
+ base += days_back * 60 * 60 * 24;
+ if (!title) {
+ full_time = localtime(&base);
+ strftime((char *)&buffer, (size_t)64, "%A", full_time);
+ node = tree_create_folder_node(NULL, buffer);
+ } else
+ node = tree_create_folder_node(NULL, title);
+
+ if (!node)
+ return;
+
+ node->retain_in_memory = true;
+ node->deleted = true;
+ node->editable = false;
+ global_history_base_node[global_history_base_node_count] = node;
+ global_history_base_node_time[global_history_base_node_count] = base;
+ global_history_base_node_count++;
+}
+
+/**
+ * Saves the global history's recent URL data.
+ */
+void history_global_save(void)
+{
+ FILE *fp;
+ int i;
+
+ /* save recent URLs */
+ fp = fopen(option_recent_file, "w");
+ if (!fp)
+ LOG(("Failed to open file '%s' for writing",
+ option_recent_file));
+ else {
+ for (i = global_history_recent_count - 1; i >= 0; i--)
+ if (strlen(global_history_recent_url[i]) <
+ MAXIMUM_URL_LENGTH)
+ fprintf(fp, "%s\n",
+ global_history_recent_url[i]);
+ fclose(fp);
+ }
+}
+
+void global_history_add(const char *url)
+{
+ const struct url_data *data;
+
+ data = urldb_get_url_data(url);
+ if (!data)
+ return;
+
+ global_history_add_internal(url, data);
+}
+
+/**
+ * Internal routine to actually perform global history addition
+ *
+ * \param url The URL to add
+ * \param data URL data associated with URL
+ * \return true (for urldb_iterate_entries)
+ */
+bool global_history_add_internal(const char *url,
+ const struct url_data *data)
+{
+ int i, j;
+ struct node *parent = NULL;
+ struct node *link;
+ struct node *node;
+ bool before = false;
+ int visit_date;
+
+ assert(url && data);
+
+ visit_date = data->last_visit;
+
+ /* find parent node */
+ for (i = 0; i < global_history_base_node_count; i++) {
+ if (global_history_base_node_time[i] <= visit_date) {
+ parent = global_history_base_node[i];
+ break;
+ }
+ }
+
+ /* the entry is too old to care about */
+ if (!parent)
+ return true;
+
+ if (parent->deleted) {
+ /* parent was deleted, so find place to insert it */
+ link = global_history_tree->root;
+
+ for (j = global_history_base_node_count - 1; j >= 0; j--) {
+ if (!global_history_base_node[j]->deleted &&
+ global_history_base_node_time[j] >
+ global_history_base_node_time[i]) {
+ link = global_history_base_node[j];
+ before = true;
+ break;
+ }
+ }
+
+ tree_set_node_selected(global_history_tree,
+ parent, false);
+ tree_set_node_expanded(global_history_tree,
+ parent, false);
+ tree_link_node(link, parent, before);
+
+ if (!global_history_init) {
+ tree_recalculate_node(global_history_tree, parent, true);
+ tree_recalculate_node_positions(global_history_tree,
+ global_history_tree->root);
+ tree_draw(global_history_tree, 0, 0, 16384, 16384);
+ }
+ }
+
+ /* find any previous occurance */
+ if (!global_history_init) {
+ node = history_global_find(url);
+ if (node) {
+ /* \todo: calculate old/new positions and redraw
+ * only the relevant portion */
+ tree_draw(global_history_tree, 0, 0, 16384, 16384);
+ tree_update_URL_node(node, url, data);
+ tree_delink_node(node);
+ tree_link_node(parent, node, false);
+ tree_handle_node_changed(global_history_tree,
+ node, false, true);
+/* ro_gui_tree_scroll_visible(hotlist_tree,
+ &node->data);
+*/ return true;
+ }
+ }
+
+ /* Add the node at the bottom */
+ node = tree_create_URL_node_shared(parent, url, data);
+ if ((!global_history_init) && (node)) {
+ tree_draw(global_history_tree, node->box.x - NODE_INSTEP, 0,
+ NODE_INSTEP, 16384);
+ tree_handle_node_changed(global_history_tree, node,
+ true, false);
+ }
+
+ return true;
+}
+
+/**
+ * Find an entry in the global history
+ *
+ * \param url The URL to find
+ * \return Pointer to node, or NULL if not found
+ */
+struct node *history_global_find(const char *url)
+{
+ int i;
+ struct node *node;
+ struct node_element *element;
+
+ for (i = 0; i < global_history_base_node_count; i++) {
+ if (!global_history_base_node[i]->deleted) {
+ for (node = global_history_base_node[i]->child;
+ node; node = node->next) {
+ element = tree_find_element(node,
+ TREE_ELEMENT_URL);
+ if ((element) && !strcmp(url, element->text))
+ return node;
+ }
+ }
+ }
+ return NULL;
+}
+
+
+/**
+ * Adds an URL to the recently used list
+ *
+ * \param url the URL to add (copied)
+ */
+void global_history_add_recent(const char *url)
+{
+ int i;
+ int j = -1;
+ char *current;
+
+ /* try to find a string already there */
+ for (i = 0; i < global_history_recent_count; i++)
+ if (global_history_recent_url[i] &&
+ !strcmp(global_history_recent_url[i], url))
+ j = i;
+
+ /* already at head of list */
+ if (j == 0)
+ return;
+
+ if (j < 0) {
+ /* add to head of list */
+ free(global_history_recent_url[
+ GLOBAL_HISTORY_RECENT_URLS - 1]);
+ memmove(&global_history_recent_url[1],
+ &global_history_recent_url[0],
+ (GLOBAL_HISTORY_RECENT_URLS - 1) *
+ sizeof(char *));
+ global_history_recent_url[0] = strdup(url);
+ global_history_recent_count++;
+ if (global_history_recent_count > GLOBAL_HISTORY_RECENT_URLS)
+ global_history_recent_count =
+ GLOBAL_HISTORY_RECENT_URLS;
+ /* TODO: This would be best taken care of in the front end specific code as it
+ seems to be necessary on risc os only*/
+ /*if (global_history_recent_count == 1)
+ ro_gui_window_prepare_navigate_all();*/
+ } else {
+ /* move to head of list */
+ current = global_history_recent_url[j];
+ for (i = j; i > 0; i--)
+ global_history_recent_url[i] =
+ global_history_recent_url[i - 1];
+ global_history_recent_url[0] = current;
+ }
+}
+
+
+/**
+ * Gets details of the currently used URL list.
+ *
+ * \param count set to the current number of entries in the URL array on exit
+ * \return the current URL array
+ */
+char **global_history_get_recent(int *count)
+{
+ *count = global_history_recent_count;
+ return global_history_recent_url;
+}
Index: desktop/history_global_core.h
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ desktop/history_global_core.h 2009-06-23 16:08:40.000000000 +0100
@@ -0,0 +1,31 @@
+/*
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _NETSURF_DESKTOP_HISTORY_GLOBAL_H_
+#define _NETSURF_DESKTOP_HISTORY_GLOBAL_H_
+
+#include <stdbool.h>
+
+#include "desktop/browser.h"
+
+bool history_global_initialise(uintptr_t handle,
+ void (*start_redraw)(uintptr_t data),
+ void (*end_redraw)(uintptr_t data));
+
+extern struct tree *global_history_tree;
+
+#endif
Changed files
Makefile.sources | 2
desktop/browser.h | 17
desktop/options.c | 2
desktop/options.h | 1
desktop/tree.c | 948 ++++++++++++++++++++++++++++++++++++++++++++------
desktop/tree.h | 78 ++--
gtk/gtk_gui.c | 6
gtk/gtk_history.c | 612 --------------------------------
gtk/gtk_history.h | 19 -
gtk/gtk_plotters.c | 2
gtk/gtk_scaffolding.c | 74 +++
gtk/gtk_scaffolding.h | 7
gtk/gtk_treeview.c | 363 ++++++++++++++++---
gtk/options.h | 1
riscos/sslcert.c | 1
riscos/treeview.c | 9
16 files changed, 1304 insertions(+), 838 deletions(-)
Index: gtk/options.h
===================================================================
--- gtk/options.h (revision 7935)
+++ gtk/options.h (working copy)
@@ -64,5 +64,4 @@
{ "hover_urls", OPTION_BOOL, &option_hover_urls}, \
{ "focus_new", OPTION_BOOL, &option_focus_new}, \
{ "new_blank", OPTION_BOOL, &option_new_blank}
-
#endif
Index: gtk/gtk_history.c
===================================================================
--- gtk/gtk_history.c (revision 7935)
+++ gtk/gtk_history.c (working copy)
@@ -16,613 +16,19 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <string.h>
-#include <gtk/gtk.h>
-#include <glade/glade.h>
-
-#include "utils/log.h"
-#include "utils/utils.h"
-#include "utils/url.h"
-#include "utils/messages.h"
-#include "content/urldb.h"
+#include "desktop/history_global_core.h"
+#include "desktop/plotters.h"
+#include "desktop/tree.h"
#include "gtk/gtk_history.h"
-#include "gtk/gtk_gui.h"
-#include "gtk/gtk_window.h"
-#include "gtk/gtk_bitmap.h"
+#include "gtk/gtk_plotters.h"
+#include "gtk/gtk_scaffolding.h"
+#include "utils/log.h"
-#define GLADE_NAME "history.glade"
-enum
-{
- SITE_TITLE = 0,
- SITE_DOMAIN,
- SITE_ADDRESS,
- SITE_LASTVISIT,
- SITE_TOTALVISITS,
- SITE_THUMBNAIL,
- SITE_NCOLS
-};
+struct gtk_treeview_window global_history_window;
-enum
+void nsgtk_history_init()
{
- DOM_DOMAIN,
- DOM_LASTVISIT,
- DOM_TOTALVISITS,
- DOM_HAS_SITES,
- DOM_NCOLS
-};
-
-GtkWindow *wndHistory;
-static GladeXML *gladeFile;
-
-static const gchar* dateToday;
-static const gchar* dateYesterday;
-static const gchar* dateAt;
-static const gchar* domainAll;
-
-static struct history_model *history;
-
-static void nsgtk_history_init_model(void);
-static void nsgtk_history_init_filters(void);
-static void nsgtk_history_init_sort(void);
-static void nsgtk_history_init_treeviews(void);
-static void nsgtk_history_init_list(void);
-
-static bool nsgtk_history_add_internal(const char *, const struct url_data *);
-
-static void nsgtk_history_show_domain(GtkTreeSelection *treesel,
- GString *domain_filter);
-
-static void nsgtk_history_show_all(void);
-
-static gboolean nsgtk_history_filter_search(GtkTreeModel *model,
- GtkTreeIter *iter, GtkWidget *search_entry);
-static gboolean nsgtk_history_filter_sites(GtkTreeModel *model,
- GtkTreeIter *iter, GString *domain_filter);
-
-static gchar *nsgtk_history_parent_get(gchar *domain);
-static void nsgtk_history_parent_update(gchar *path, const struct url_data *data);
-
-static void nsgtk_history_domain_sort_changed(GtkComboBox *combo);
-static gint nsgtk_history_domain_sort_compare(GtkTreeModel *model, GtkTreeIter *a,
- GtkTreeIter *b, gint sort_column);
-static void nsgtk_history_domain_set_visible (GtkTreeModel *model,
- GtkTreePath *path, GtkTreeIter *iter, gboolean has_sites);
-
-static void nsgtk_history_search(void);
-static void nsgtk_history_search_clear (GtkEntry *entry);
-
-static gchar *nsgtk_history_date_parse(time_t visit_time);
-static void nsgtk_history_row_activated(GtkTreeView *, GtkTreePath *,
- GtkTreeViewColumn *);
-static void nsgtk_history_update_info(GtkTreeSelection *treesel,
- gboolean domain);
-static void nsgtk_history_scroll_top (GtkScrolledWindow *scrolled_window);
-
-void nsgtk_history_init(void)
-{
- dateToday = messages_get("DateToday");
- dateYesterday = messages_get("DateYesterday");
- dateAt = messages_get("DateAt");
- domainAll = messages_get("DomainAll");
-
- gchar *glade_location = g_strconcat(res_dir_location, GLADE_NAME, NULL);
- gladeFile = glade_xml_new(glade_location, NULL, NULL);
- g_free(glade_location);
-
- glade_xml_signal_autoconnect(gladeFile);
- wndHistory = GTK_WINDOW(glade_xml_get_widget(gladeFile,
- "wndHistory"));
-
- nsgtk_history_init_model();
- nsgtk_history_init_list();
- nsgtk_history_init_filters();
- nsgtk_history_init_sort();
- nsgtk_history_init_treeviews();
-
- nsgtk_history_show_all();
+ global_history_window.mouse_state = 0;
}
-
-void nsgtk_history_init_model(void)
-{
- history = malloc(sizeof(struct history_model));
-
- history->history_list = gtk_list_store_new(SITE_NCOLS,
- G_TYPE_STRING, /* title */
- G_TYPE_STRING, /* domain */
- G_TYPE_STRING, /* address */
- G_TYPE_INT, /* last visit */
- G_TYPE_INT, /* num visits */
- G_TYPE_POINTER); /* thumbnail */
- history->history_filter = gtk_tree_model_filter_new(
- GTK_TREE_MODEL(history->history_list), NULL);
-
- history->site_filter = gtk_tree_model_filter_new(
- history->history_filter,NULL);
- history->site_sort = gtk_tree_model_sort_new_with_model(history->site_filter);
- history->site_treeview = GTK_TREE_VIEW(glade_xml_get_widget(gladeFile,
- "treeHistory"));
- history->site_selection =
- gtk_tree_view_get_selection(history->site_treeview);
-
- history->domain_list = gtk_list_store_new(DOM_NCOLS,
- G_TYPE_STRING, /* domain */
- G_TYPE_INT, /* last visit */
- G_TYPE_INT, /* num visits */
- G_TYPE_BOOLEAN); /* has sites */
- history->domain_filter = gtk_tree_model_filter_new(
- GTK_TREE_MODEL(history->domain_list), NULL);
- history->domain_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
- g_free, g_free);
- history->domain_sort = gtk_tree_model_sort_new_with_model(
- history->domain_filter);
- history->domain_treeview = GTK_TREE_VIEW(glade_xml_get_widget(
- gladeFile,"treeDomain"));
- history->domain_selection =
- gtk_tree_view_get_selection(history->domain_treeview);
-}
-
-void nsgtk_history_init_list(void)
-{
- GtkTreeIter iter;
-
- gtk_list_store_clear(history->history_list);
- gtk_list_store_clear(history->domain_list);
-
- gtk_list_store_append(history->domain_list, &iter);
- gtk_list_store_set(history->domain_list, &iter,
- DOM_DOMAIN, domainAll,
- DOM_LASTVISIT, -2,
- DOM_TOTALVISITS, -2,
- DOM_HAS_SITES, TRUE,
- -1);
-
- urldb_iterate_entries(nsgtk_history_add_internal);
-}
-
-void nsgtk_history_init_filters(void)
-{
- GtkWidget *search_entry, *clear_button;
- GString *filter_string = g_string_new(NULL);
-
- search_entry = glade_xml_get_widget(gladeFile,"entrySearch");
- clear_button = glade_xml_get_widget(gladeFile,"buttonClearSearch");
-
- g_signal_connect(G_OBJECT(search_entry), "changed",
- G_CALLBACK(nsgtk_history_search), NULL);
- g_signal_connect_swapped(G_OBJECT(clear_button), "clicked",
- G_CALLBACK(nsgtk_history_search_clear),
- GTK_ENTRY(search_entry));
-
- gtk_tree_model_filter_set_visible_func(
- GTK_TREE_MODEL_FILTER(history->history_filter),
- (GtkTreeModelFilterVisibleFunc)
- nsgtk_history_filter_search, search_entry, NULL);
- gtk_tree_model_filter_set_visible_func(
- GTK_TREE_MODEL_FILTER(history->site_filter),
- (GtkTreeModelFilterVisibleFunc)
- nsgtk_history_filter_sites, filter_string, NULL);
- gtk_tree_model_filter_set_visible_column(
- GTK_TREE_MODEL_FILTER(history->domain_filter),
- DOM_HAS_SITES);
-
- g_signal_connect(G_OBJECT(history->site_selection), "changed",
- G_CALLBACK(nsgtk_history_update_info), FALSE);
- g_signal_connect(G_OBJECT(history->domain_selection), "changed",
- G_CALLBACK(nsgtk_history_show_domain), filter_string);
-}
-
-void nsgtk_history_init_sort(void)
-{
- GtkWidget *domain_window = glade_xml_get_widget(gladeFile,
- "windowDomain");
- GtkComboBox *sort_combo_box =
- GTK_COMBO_BOX(glade_xml_get_widget(
- gladeFile, "comboSort"));
- gtk_combo_box_set_active(sort_combo_box, 0);
-
- g_signal_connect(G_OBJECT(sort_combo_box), "changed",
- G_CALLBACK(nsgtk_history_domain_sort_changed), NULL);
- g_signal_connect_swapped(G_OBJECT(sort_combo_box), "changed",
- G_CALLBACK(nsgtk_history_scroll_top), domain_window);
-
- gtk_tree_sortable_set_sort_func(
- GTK_TREE_SORTABLE(history->domain_sort),
- DOM_LASTVISIT, (GtkTreeIterCompareFunc)
- nsgtk_history_domain_sort_compare,
- GUINT_TO_POINTER(DOM_LASTVISIT), NULL);
- gtk_tree_sortable_set_sort_func(
- GTK_TREE_SORTABLE(history->domain_sort),
- DOM_TOTALVISITS, (GtkTreeIterCompareFunc)
- nsgtk_history_domain_sort_compare,
- GUINT_TO_POINTER(DOM_TOTALVISITS), NULL);
- gtk_tree_sortable_set_sort_func(
- GTK_TREE_SORTABLE(history->site_sort),
- SITE_LASTVISIT, (GtkTreeIterCompareFunc)
- nsgtk_history_domain_sort_compare,
- GUINT_TO_POINTER(SITE_LASTVISIT), NULL);
- gtk_tree_sortable_set_sort_func(
- GTK_TREE_SORTABLE(history->site_sort),
- SITE_TOTALVISITS, (GtkTreeIterCompareFunc)
- nsgtk_history_domain_sort_compare,
- GUINT_TO_POINTER(SITE_TOTALVISITS), NULL);
-}
-
-void nsgtk_history_init_treeviews(void)
-{
- GtkCellRenderer *renderer;
-
- renderer = gtk_cell_renderer_text_new();
- gtk_tree_view_insert_column_with_attributes(history->site_treeview, -1,
- messages_get("Title"), renderer,
- "text", SITE_TITLE,
- NULL);
-
- renderer = gtk_cell_renderer_text_new();
- gtk_tree_view_insert_column_with_attributes(history->domain_treeview,
- -1, messages_get("Domain"), renderer,
- "markup", DOM_DOMAIN,
- NULL);
-
- gtk_tree_view_set_model(history->site_treeview, history->site_sort);
- gtk_tree_view_set_model(history->domain_treeview, history->domain_sort);
-
- g_signal_connect(history->site_treeview, "row-activated",
- G_CALLBACK(nsgtk_history_row_activated), NULL);
-}
-
-bool nsgtk_history_add_internal(const char *url, const struct url_data *data)
-{
- GtkTreeIter iter;
- gchar *domain, *path;
- if (url_host(url, &domain) != URL_FUNC_OK)
- strcpy(domain, messages_get("gtkUnknownHost"));
-
- if (data->visits > 0)
- {
- path = nsgtk_history_parent_get(domain);
- nsgtk_history_parent_update(path, data);
-
- gtk_list_store_append(history->history_list, &iter);
- gtk_list_store_set(history->history_list, &iter,
- SITE_TITLE, data->title ? data->title :
- url,
- SITE_DOMAIN, domain,
- SITE_ADDRESS, url,
- SITE_LASTVISIT, data->last_visit,
- SITE_TOTALVISITS, data->visits,
- SITE_THUMBNAIL,
- gtk_bitmap_get_primary(
- urldb_get_thumbnail(url)),
- -1);
- }
- return true;
-}
-
-gchar *nsgtk_history_parent_get(gchar *domain)
-{
- GtkTreeIter iter;
- gchar *path;
-
- /* Adds an extra entry in the list to act as the root domain
- * (which will keep track of things like visits to all sites
- * in the domain), This does not work as a tree because the
- * children cannot be displayed if the root is hidden
- * (which would conflict with the site view) */
- path = g_hash_table_lookup(history->domain_hash, domain);
-
- if (path == NULL){
- gtk_list_store_append(history->domain_list, &iter);
- gtk_list_store_set(history->domain_list, &iter,
- DOM_DOMAIN, domain,
- DOM_LASTVISIT, messages_get("gtkUnknownHost"),
- DOM_TOTALVISITS, 0,
- -1);
-
- path = gtk_tree_model_get_string_from_iter(
- GTK_TREE_MODEL(history->domain_list), &iter);
- g_hash_table_insert(history->domain_hash, domain,
- path);
- }
-
- return path;
-}
-
-void nsgtk_history_parent_update(gchar *path, const struct url_data *data)
-{
- GtkTreeIter iter;
- gint num_visits, last_visit;
-
- gtk_tree_model_get_iter_from_string(
- GTK_TREE_MODEL(history->domain_list), &iter, path);
- gtk_tree_model_get(GTK_TREE_MODEL(history->domain_list), &iter,
- DOM_TOTALVISITS, &num_visits,
- DOM_LASTVISIT, &last_visit,
- -1);
-
- gtk_list_store_set(history->domain_list, &iter,
- DOM_TOTALVISITS, num_visits + data->visits,
- DOM_LASTVISIT, max(last_visit,data->last_visit),
- -1);
-
- /* Handle "All" */
- gtk_tree_model_get_iter_from_string(
- GTK_TREE_MODEL(history->domain_list), &iter, "0");
- gtk_tree_model_get(GTK_TREE_MODEL(history->domain_list), &iter,
- DOM_TOTALVISITS, &num_visits,
- DOM_LASTVISIT, &last_visit,
- -1);
-
- gtk_list_store_set(history->domain_list, &iter,
- DOM_TOTALVISITS, num_visits + data->visits,
- DOM_LASTVISIT, max(last_visit,data->last_visit),
- -1);
-}
-
-void nsgtk_history_show_domain(GtkTreeSelection *treesel,
- GString *domain_filter)
-{
- GtkTreeIter iter;
- GtkTreeModel *model;
-
- if (gtk_tree_selection_get_selected(treesel, &model, &iter)) {
- gtk_tree_model_get(model, &iter, DOM_DOMAIN,
- &domain_filter->str, -1);
- gtk_tree_model_filter_refilter(
- GTK_TREE_MODEL_FILTER(history->site_filter));
- }
-
- nsgtk_history_update_info(treesel, TRUE);
-}
-
-static void nsgtk_history_show_all(void)
-{
- GtkTreePath *path = gtk_tree_path_new_from_string("0");
-
- gtk_tree_selection_select_path(history->domain_selection, path);
-
- gtk_tree_path_free(path);
-}
-
-gboolean nsgtk_history_filter_search(GtkTreeModel *model, GtkTreeIter *iter,
- GtkWidget *search_entry)
-{
- gchar *title, *address, *domain, *path;
- gint result;
- GtkTreeIter new_iter;
- const gchar *search = gtk_entry_get_text(GTK_ENTRY(search_entry));
-
- gtk_tree_model_get(model, iter, SITE_TITLE, &title,
- SITE_ADDRESS, &address,
- SITE_DOMAIN, &domain,
- -1);
-
- if (title)
- result = (strstr(title, search) || strstr(address, search));
- else
- result = FALSE;
-
- if (result) {
- path = g_hash_table_lookup(history->domain_hash, domain);
- gtk_tree_model_get_iter_from_string(
- GTK_TREE_MODEL(history->domain_list),&new_iter,
- path);
-
- nsgtk_history_domain_set_visible(
- GTK_TREE_MODEL(history->domain_list), NULL,
- &new_iter, result);
- }
-
- g_free(title);
- g_free(address);
- g_free(domain);
-
- return result;
-}
-
-gboolean nsgtk_history_filter_sites(GtkTreeModel *model, GtkTreeIter *iter,
- GString *domain_filter)
-{
- gchar *domain;
- gboolean domain_match;
-
- gtk_tree_model_get(model, iter, SITE_DOMAIN, &domain, -1);
-
- if (domain && domain_filter->str)
- domain_match = g_str_equal(domain, domain_filter->str) ||
- g_str_equal(domain_filter->str,
- domainAll);
- else
- domain_match = FALSE;
-
- g_free(domain);
- return domain_match;
-}
-
-void nsgtk_history_domain_sort_changed(GtkComboBox *combo)
-{
- gint domain_options[] = { DOM_DOMAIN, DOM_LASTVISIT, DOM_TOTALVISITS };
- gint site_options[] = { SITE_TITLE, SITE_LASTVISIT, SITE_TOTALVISITS };
- gint sort = gtk_combo_box_get_active(combo);
-
- gtk_tree_sortable_set_sort_column_id(
- GTK_TREE_SORTABLE(history->domain_sort),
- domain_options[sort], GTK_SORT_ASCENDING);
- gtk_tree_sortable_set_sort_column_id(
- GTK_TREE_SORTABLE(history->site_sort),
- site_options[sort], GTK_SORT_ASCENDING);
-}
-
-gint nsgtk_history_domain_sort_compare(GtkTreeModel *model, GtkTreeIter *a,
- GtkTreeIter *b, gint sort_column)
-{
- gint comparable_a;
- gint comparable_b;
-
- gtk_tree_model_get(model, a, sort_column, &comparable_a, -1);
- gtk_tree_model_get(model, b, sort_column, &comparable_b, -1);
-
- /* Make sure "All" stays at the top */
- if (comparable_a < 0 || comparable_b < 0)
- return comparable_a - comparable_b;
- else
- return comparable_b - comparable_a;
-}
-
-void nsgtk_history_domain_set_visible (GtkTreeModel *model, GtkTreePath *path,
- GtkTreeIter *iter, gboolean has_sites)
-{
- gchar *string = gtk_tree_model_get_string_from_iter(model, iter);
-
- if (!g_str_equal(string, "0")) /* "All" */
- gtk_list_store_set(GTK_LIST_STORE(model), iter,
- DOM_HAS_SITES, has_sites, -1);
-
- g_free(string);
-}
-
-void nsgtk_history_search()
-{
- gtk_tree_model_foreach(GTK_TREE_MODEL(history->domain_list),
- (GtkTreeModelForeachFunc)
- nsgtk_history_domain_set_visible, FALSE);
-
- nsgtk_history_show_all();
- gtk_tree_model_filter_refilter(
- GTK_TREE_MODEL_FILTER(history->history_filter));
-}
-
-void nsgtk_history_search_clear (GtkEntry *entry)
-{
- gtk_entry_set_text(entry, "");
-}
-
-gchar *nsgtk_history_date_parse(time_t visit_time)
-{
- gchar *date_string = malloc(30);
- gchar format[30];
- time_t current_time = time(NULL);
- gint current_day = localtime(¤t_time)->tm_yday;
- struct tm *visit_date = localtime(&visit_time);
-
- if (visit_date->tm_yday == current_day)
- g_snprintf(format, 30, "%s %s %%I:%%M %%p",
- dateToday, dateAt);
- else if (current_day - visit_date->tm_yday == 1)
- g_snprintf(format, 30, "%s %s %%I:%%M %%p",
- dateYesterday, dateAt);
- else if (current_day - visit_date->tm_yday < 7)
- g_snprintf(format, 30, "%%A %s %%I:%%M %%p",
- dateAt);
- else
- g_snprintf(format, 30, "%%B %%d, %%Y");
-
- strftime(date_string, 30, format, visit_date);
-
- return date_string;
-}
-
-
-void nsgtk_history_row_activated(GtkTreeView *tv, GtkTreePath *path,
- GtkTreeViewColumn *column)
-{
- GtkTreeModel *model;
- GtkTreeIter iter;
-
- model = gtk_tree_view_get_model(tv);
- if (gtk_tree_model_get_iter(model, &iter, path))
- {
- gchar *address;
-
- gtk_tree_model_get(model, &iter, SITE_ADDRESS, &address, -1);
-
- browser_window_create(address, NULL, NULL, true, false);
- }
-}
-
-void nsgtk_history_update_info(GtkTreeSelection *treesel, gboolean domain)
-{
- GtkTreeIter iter;
- GtkTreeModel *model;
- gboolean has_selection;
-
- has_selection = gtk_tree_selection_get_selected(treesel, &model, &iter);
-
- if (has_selection && domain) {
- gchar *b;
- gint i;
- char buf[20];
- gboolean all = g_str_equal(gtk_tree_model_get_string_from_iter(
- model, &iter), "0");
-
- /* Address */
- gtk_tree_model_get(model, &iter, DOM_DOMAIN, &b, -1);
- gtk_label_set_text(GTK_LABEL(glade_xml_get_widget(gladeFile,
- "labelHistoryAddress")),
- all ? "-" : b);
- g_free(b);
- /* Last Visit */
- gtk_tree_model_get(model, &iter, DOM_LASTVISIT, &i, -1);
- gtk_label_set_text(GTK_LABEL(glade_xml_get_widget(gladeFile,
- "labelHistoryLastVisit")),
- nsgtk_history_date_parse(i));
-
- /* Total Visits */
- gtk_tree_model_get(model, &iter, DOM_TOTALVISITS, &i, -1);
- snprintf(buf, 20, "%d", i);
- gtk_label_set_text(GTK_LABEL(glade_xml_get_widget(gladeFile,
- "labelHistoryVisits")),
- buf);
- } else if (has_selection){
- GdkPixbuf *thumb;
- gchar *b;
- gint i;
- char buf[20];
- /* Address */
- gtk_tree_model_get(model, &iter, SITE_ADDRESS, &b, -1);
- gtk_label_set_text(GTK_LABEL(glade_xml_get_widget(gladeFile,
- "labelHistoryAddress")), b);
- g_free(b);
- /* Last Visit */
- gtk_tree_model_get(model, &iter, SITE_LASTVISIT, &i, -1);
- gtk_label_set_text(GTK_LABEL(glade_xml_get_widget(gladeFile,
- "labelHistoryLastVisit")),
- nsgtk_history_date_parse(i));
-
- /* Total Visits */
- gtk_tree_model_get(model, &iter, SITE_TOTALVISITS, &i, -1);
- snprintf(buf, 20, "%d", i);
- gtk_label_set_text(GTK_LABEL(glade_xml_get_widget(gladeFile,
- "labelHistoryVisits")), buf);
-
- gtk_tree_model_get(model, &iter, SITE_THUMBNAIL, &thumb, -1);
- gtk_image_set_from_pixbuf(GTK_IMAGE(
- glade_xml_get_widget(gladeFile,
- "imageThumbnail")), thumb);
- g_object_set(G_OBJECT(glade_xml_get_widget(
- gladeFile, "imageFrame")),
- "visible", (bool)thumb, NULL);
- }
-}
-
-void nsgtk_history_scroll_top (GtkScrolledWindow *scrolled_window)
-{
- GtkAdjustment *adjustment =
- gtk_scrolled_window_get_vadjustment(scrolled_window);
-
- gtk_adjustment_set_value(adjustment, 0);
-
- gtk_scrolled_window_set_vadjustment(scrolled_window, adjustment);
-}
-
-void global_history_add(const char *url)
-{
- const struct url_data *data;
-
- data = urldb_get_url_data(url);
- if (!data)
- return;
-
- nsgtk_history_add_internal(url, data);
-}
Index: gtk/gtk_history.h
===================================================================
--- gtk/gtk_history.h (revision 7935)
+++ gtk/gtk_history.h (working copy)
@@ -20,25 +20,10 @@
#define __NSGTK_HISTORY_H__
#include <gtk/gtk.h>
+#include "gtk/gtk_treeview.h"
-extern GtkWindow *wndHistory;
+extern struct gtk_treeview_window global_history_window;
-
-struct history_model {
- GtkListStore *history_list;
- GtkTreeModel *history_filter;
- GtkTreeModel *site_filter;
- GtkTreeModel *site_sort;
- GtkTreeView *site_treeview;
- GtkTreeSelection *site_selection;
- GtkListStore *domain_list;
- GtkTreeModel *domain_filter;
- GHashTable *domain_hash;
- GtkTreeModel *domain_sort;
- GtkTreeView *domain_treeview;
- GtkTreeSelection *domain_selection;
-};
-
void nsgtk_history_init(void);
#endif /* __NSGTK_HISTORY_H__ */
Index: gtk/gtk_plotters.c
===================================================================
--- gtk/gtk_plotters.c (revision 7935)
+++ gtk/gtk_plotters.c (working copy)
@@ -119,7 +119,7 @@
line_width = 1;
cairo_set_line_width(current_cr, line_width);
- cairo_rectangle(current_cr, x0, y0, width, height);
+ cairo_rectangle(current_cr, x0 + 0.5, y0 + 0.5, width, height);
cairo_stroke(current_cr);
return true;
Index: gtk/gtk_scaffolding.c
===================================================================
--- gtk/gtk_scaffolding.c (revision 7935)
+++ gtk/gtk_scaffolding.c (working copy)
@@ -26,6 +26,7 @@
#include "content/content.h"
#include "desktop/browser.h"
#include "desktop/history_core.h"
+#include "desktop/history_global_core.h"
#include "desktop/gui.h"
#include "desktop/netsurf.h"
#include "desktop/options.h"
@@ -37,6 +38,7 @@
#endif
#include "desktop/selection.h"
#include "desktop/textinput.h"
+#include "desktop/tree.h"
#include "gtk/gtk_completion.h"
#include "gtk/dialogs/gtk_options.h"
#include "gtk/dialogs/gtk_about.h"
@@ -50,6 +52,7 @@
#include "gtk/gtk_schedule.h"
#include "gtk/gtk_tabs.h"
#include "gtk/gtk_throbber.h"
+#include "gtk/gtk_treeview.h"
#include "gtk/gtk_window.h"
#include "gtk/options.h"
#include "render/box.h"
@@ -62,15 +65,6 @@
#include "utils/log.h"
-struct gtk_history_window;
-
-struct gtk_history_window {
- struct gtk_scaffolding *g;
- GtkWindow *window;
- GtkScrolledWindow *scrolled;
- GtkDrawingArea *drawing_area;
-};
-
struct menu_events {
const char *widget;
GCallback handler;
@@ -276,6 +270,9 @@
if (g->history_window->window) {
gtk_widget_destroy(GTK_WIDGET(g->history_window->window));
}
+ if (global_history_window.window) {
+ gtk_widget_destroy(GTK_WIDGET(global_history_window.window));
+ }
gtk_widget_destroy(GTK_WIDGET(g->window));
if (--open_windows == 0)
@@ -1120,9 +1117,18 @@
MENUHANDLER(global_history)
{
- gtk_widget_show(GTK_WIDGET(wndHistory));
- gdk_window_raise(GTK_WIDGET(wndHistory)->window);
+ //gtk_widget_show(GTK_WIDGET(wndHistory));
+ //gdk_window_raise(GTK_WIDGET(wndHistory)->window);
+
+ struct gtk_scaffolding *gw = (struct gtk_scaffolding *) g;
+ gtk_window_set_default_size(global_history_window.window, 500, 400);
+ gtk_window_set_position(global_history_window.window, GTK_WIN_POS_MOUSE);
+ gtk_window_set_transient_for(global_history_window.window, gw->window);
+ gtk_window_set_opacity(global_history_window.window, 0.9);
+ gtk_widget_show(GTK_WIDGET(global_history_window.window));
+ gdk_window_raise(GTK_WIDGET(global_history_window.window)->window);
+
return TRUE;
}
@@ -1203,6 +1209,7 @@
return TRUE;
}
+
#define GET_WIDGET(x) glade_xml_get_widget(g->xml, (x))
nsgtk_scaffolding *nsgtk_new_scaffolding(struct gui_window *toplevel)
@@ -1351,7 +1358,38 @@
gtk_scrolled_window_add_with_viewport(g->history_window->scrolled,
GTK_WIDGET(g->history_window->drawing_area));
gtk_widget_show(GTK_WIDGET(g->history_window->drawing_area));
+
+
+ global_history_window.window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
+ gtk_window_set_default_size(global_history_window.window, 400, 400);
+ gtk_window_set_title(global_history_window.window, "NetSurf Global History");
+ gtk_window_set_type_hint(global_history_window.window,
+ GDK_WINDOW_TYPE_HINT_UTILITY);
+ global_history_window.scrolled =
+ GTK_SCROLLED_WINDOW(gtk_scrolled_window_new(0, 0));
+ gtk_container_add(GTK_CONTAINER(global_history_window.window),
+ GTK_WIDGET(global_history_window.scrolled));
+ gtk_widget_show(GTK_WIDGET(global_history_window.scrolled));
+ global_history_window.drawing_area =
+ GTK_DRAWING_AREA(gtk_drawing_area_new());
+
+ gtk_widget_set_events(GTK_WIDGET(global_history_window.drawing_area),
+ GDK_EXPOSURE_MASK |
+ GDK_POINTER_MOTION_MASK |
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK);
+ gtk_widget_modify_bg(GTK_WIDGET(global_history_window.drawing_area),
+ GTK_STATE_NORMAL,
+ &((GdkColor) { 0, 0xffff, 0xffff, 0xffff } ));
+ gtk_scrolled_window_add_with_viewport(global_history_window.scrolled,
+ GTK_WIDGET(global_history_window.drawing_area));
+ gtk_widget_show(GTK_WIDGET(global_history_window.drawing_area));
+
+ history_global_initialise((uintptr_t) &global_history_window,
+ tree_redraw_start_callback,
+ tree_redraw_end_callback);
+
/* set up URL bar completion */
g->url_bar_completion = gtk_entry_completion_new();
gtk_entry_set_completion(g->url_bar, g->url_bar_completion);
@@ -1383,6 +1421,20 @@
nsgtk_history_button_press_event, g->history_window);
CONNECT(g->history_window->window, "delete_event",
gtk_widget_hide_on_delete, NULL);
+
+ CONNECT(global_history_window.drawing_area, "expose_event",
+ nsgtk_tree_window_expose_event, global_history_tree);
+ CONNECT(global_history_window.drawing_area, "button_press_event",
+ nsgtk_tree_window_button_press_event,
+ global_history_tree);
+ CONNECT(global_history_window.drawing_area, "button_release_event",
+ nsgtk_tree_window_button_release_event,
+ global_history_tree);
+ CONNECT(global_history_window.drawing_area, "motion_notify_event",
+ nsgtk_tree_window_motion_notify_event,
+ global_history_tree);
+ CONNECT(global_history_window.window, "delete_event",
+ gtk_widget_hide_on_delete, NULL);
g_signal_connect_after(g->notebook, "page-added",
G_CALLBACK(nsgtk_window_tabs_num_changed), g);
Index: gtk/gtk_scaffolding.h
===================================================================
--- gtk/gtk_scaffolding.h (revision 7935)
+++ gtk/gtk_scaffolding.h (working copy)
@@ -26,6 +26,13 @@
typedef struct gtk_scaffolding nsgtk_scaffolding;
+struct gtk_history_window {
+ struct gtk_scaffolding *g;
+ GtkWindow *window;
+ GtkScrolledWindow *scrolled;
+ GtkDrawingArea *drawing_area;
+};
+
struct gtk_scaffolding {
GtkWindow *window;
GtkNotebook *notebook;
Index: gtk/gtk_treeview.c
===================================================================
--- gtk/gtk_treeview.c (revision 7935)
+++ gtk/gtk_treeview.c (working copy)
@@ -20,108 +20,365 @@
* Generic tree handling (implementation).
*/
+#include <assert.h>
+#include <gtk/gtk.h>
+#include <stdio.h>
+#include "content/fetchcache.h"
+#include "desktop/gui.h"
#include "desktop/tree.h"
+#include "desktop/plotters.h"
+#include "gtk/gtk_gui.h"
+#include "gtk/gtk_plotters.h"
+#include "gtk/gtk_treeview.h"
+static void tree_callback(content_msg msg, struct content *c,
+ intptr_t p1, intptr_t p2, union content_msg_data data);
+static bool set = false;
+
/**
* Sets the origin variables to the correct values for a specified tree
*
* \param tree the tree to set the origin for
*/
-void tree_initialise_redraw(struct tree *tree) {
+void tree_initialise_redraw(struct tree *tree)
+{
}
-
/**
- * Informs the current window manager that an area requires updating.
+ * Acquires a specific front end specified icon.
*
- * \param tree the tree that is requesting a redraw
- * \param x the x co-ordinate of the redraw area
- * \param y the y co-ordinate of the redraw area
- * \param width the width of the redraw area
- * \param height the height of the redraw area
+ * \param node The node for which the icon bitmap gets acquired. This
+ * is necessary at the moment as the pointer gets passed
+ * to tree_callback which actually sets the icon.
+ * \param bitmap the bitmap to use
+ * \param path If true 'bitmap' contains the full path under which the
+ * icon can be found
*/
-void tree_redraw_area(struct tree *tree, int x, int y, int width, int height) {
+struct content *tree_get_bitmap(struct node *node, const char *bitmap,
+ bool path)
+{
+ /* TODO: something like bitmap_from_disk is needed here*/
+
+ char bitmap_path[MAXIMUM_URL_LENGTH];
+
+ struct content *c;
+
+ if (path) {
+ sprintf(bitmap_path, bitmap);
+ }
+ else {
+ sprintf(bitmap_path, "%s%s%s%s", "file://", res_dir_location,
+ bitmap, ".bmp");
+ }
+ c = fetchcache(bitmap_path, tree_callback, (intptr_t) node, 0, 16, 16, true,
+ 0, 0, true, false);
+ fetchcache_go(c, 0, tree_callback, (intptr_t) node, 0, 16, 16, 0, 0,
+ true, 0);
+
+ return c;
}
-
/**
- * Draws a line.
+ * Updates the tree owner following a tree resize
*
- * \param x the x co-ordinate
- * \param x the y co-ordinate
- * \param x the width of the line
- * \param x the height of the line
+ * \param tree the tree to update the owner of
*/
-void tree_draw_line(int x, int y, int width, int height) {
+void tree_resized(struct tree *tree)
+{
+ struct gtk_treeview_window *tw =
+ (struct gtk_treeview_window *)tree->handle;
+
+ gtk_widget_set_size_request(GTK_WIDGET(tw->drawing_area),
+ tree->width, tree->height);
+ return;
}
-
/**
- * Draws an element, including any expansion icons
+ * Callback for fetchcache(). Should be removed once bitmaps get loaded directly
+ * from disc
*
- * \param tree the tree to draw an element for
- * \param element the element to draw
+ * \param tree the tree to update the owner of
*/
-void tree_draw_node_element(struct tree *tree, struct node_element *element) {
+static void tree_callback(content_msg msg, struct content *c,
+ intptr_t p1, intptr_t p2, union content_msg_data data)
+{
+ struct node *node = (struct node *) p1;
+
+ switch (msg) {
+ case CONTENT_MSG_READY:
+ node->data.bitmap = c;
+ break;
+ default:
+ break;
+ }
}
/**
- * Draws an elements expansion icon
+ * Translates a content_type to the name of a respective icon
*
- * \param tree the tree to draw the expansion for
- * \param element the element to draw the expansion for
+ * \param content_type content type
+ * \param buffer buffer for the icon name
*/
-void tree_draw_node_expansion(struct tree *tree, struct node *node) {
+void tree_icon_name_from_filetype(char *buffer, content_type type)
+{
+ // TODO: design/acquire icons
+ switch (type) {
+ case CONTENT_HTML:
+ case CONTENT_TEXTPLAIN:
+ case CONTENT_CSS:
+#if defined(WITH_MNG) || defined(WITH_PNG)
+ case CONTENT_PNG:
+#endif
+#ifdef WITH_MNG
+ case CONTENT_JNG:
+ case CONTENT_MNG:
+#endif
+#ifdef WITH_JPEG
+ case CONTENT_JPEG:
+#endif
+#ifdef WITH_GIF
+ case CONTENT_GIF:
+#endif
+#ifdef WITH_BMP
+ case CONTENT_BMP:
+ case CONTENT_ICO:
+#endif
+#ifdef WITH_SPRITE
+ case CONTENT_SPRITE:
+#endif
+#ifdef WITH_DRAW
+ case CONTENT_DRAW:
+#endif
+#ifdef WITH_ARTWORKS
+ case CONTENT_ARTWORKS:
+#endif
+#ifdef WITH_NS_SVG
+ case CONTENT_SVG:
+#endif
+ default:
+ sprintf(buffer, "icon");
+ break;
+ }
}
-
/**
- * Recalculates the dimensions of a node element.
+ * Scrolls the tree to make an element visible
*
- * \param element the element to recalculate
+ * \param tree the tree to scroll
+ * \param element the element to display
*/
-void tree_recalculate_node_element(struct node_element *element) {
+void tree_scroll_visible(struct tree *tree, struct node_element *element)
+{
+ int y0, y1;
+ gdouble page;
+ struct gtk_treeview_window *tw =
+ (struct gtk_treeview_window *)tree->handle;
+ GtkAdjustment *vadj = gtk_scrolled_window_get_vadjustment(tw->scrolled);
+
+ assert(vadj);
+
+ g_object_get(vadj, "page-size", &page, NULL);
+
+ y0 = (int)(gtk_adjustment_get_value(vadj));
+ y1 = y0 + page;
+
+ if ((element->box.y >= y0) &&
+ (element->box.y + element->box.height <= y1))
+ return;
+ if (element->box.y + element->box.height > y1)
+ y0 = y0 + (element->box.y + element->box.height - y1);
+ if (element->box.y < y0)
+ y0 = element->box.y;
+ gtk_adjustment_set_value(vadj, y0);
}
-/**
- * Sets a node element as having a specific sprite.
- *
- * \param node the node to update
- * \param sprite the sprite to use
- * \param selected the expanded sprite name to use
- */
-void tree_set_node_sprite(struct node *node, const char *sprite,
- const char *expanded) {
+/* signal handler functions for a tree window */
+gboolean nsgtk_tree_window_expose_event(GtkWidget *widget,
+ GdkEventExpose *event, gpointer g)
+{
+ struct tree *tree = (struct tree *) g;
+
+ /* The environment can be already set for drawing. In that case it gets
+ updated and won't be deleted at the end.
+ */
+
+ tree->redraw = true;
+ tree_draw(tree, 0, 0, tree->width, tree->height);
+ tree->redraw = false;
+
+ return FALSE;
}
-/**
- * Sets a node element as having a folder sprite
- *
- * \param node the node to update
- */
-void tree_set_node_sprite_folder(struct node *node) {
+gboolean nsgtk_tree_window_button_press_event(GtkWidget *widget,
+ GdkEventButton *event, gpointer g)
+{
+ struct tree *tree = (struct tree *) g;
+ struct gtk_treeview_window *tw =
+ (struct gtk_treeview_window *) tree->handle;
+
+ gtk_window_set_focus(tw->window, NULL);
+ tw->mouse_pressed_x = event->x;
+ tw->mouse_pressed_y = event->y;
+
+ if (event->type == GDK_2BUTTON_PRESS)
+ tw->mouse_state = BROWSER_MOUSE_DOUBLE_CLICK;
+
+ switch (event->button) {
+ case 1: tw->mouse_state |= BROWSER_MOUSE_PRESS_1; break;
+ case 3: tw->mouse_state |= BROWSER_MOUSE_PRESS_2; break;
+ }
+ /* Handle the modifiers too */
+ if (event->state & GDK_SHIFT_MASK)
+ tw->mouse_state |= BROWSER_MOUSE_MOD_1;
+ if (event->state & GDK_CONTROL_MASK)
+ tw->mouse_state |= BROWSER_MOUSE_MOD_2;
+ if (event->state & GDK_MOD1_MASK)
+ tw->mouse_state |= BROWSER_MOUSE_MOD_3;
+
+ return TRUE;
}
+gboolean nsgtk_tree_window_button_release_event(GtkWidget *widget,
+ GdkEventButton *event, gpointer g)
+{
+ bool shift = event->state & GDK_SHIFT_MASK;
+ bool ctrl = event->state & GDK_CONTROL_MASK;
+ bool alt = event->state & GDK_MOD1_MASK;
+ struct tree *tree = (struct tree *) g;
+ struct gtk_treeview_window *tw = (struct gtk_treeview_window *) tree->handle;
+
+ /* We consider only button 1 clicks as double clicks.
+ * If the mouse state is PRESS then we are waiting for a release to emit
+ * a click event, otherwise just reset the state to nothing*/
+ if (tw->mouse_state & BROWSER_MOUSE_DOUBLE_CLICK) {
+
+ if (tw->mouse_state & BROWSER_MOUSE_PRESS_1)
+ tw->mouse_state ^= BROWSER_MOUSE_PRESS_1;
+ else if (tw->mouse_state & BROWSER_MOUSE_PRESS_2)
+ tw->mouse_state ^= (BROWSER_MOUSE_PRESS_2 |
+ BROWSER_MOUSE_DOUBLE_CLICK);
+
+ } else if (tw->mouse_state & BROWSER_MOUSE_PRESS_1)
+ tw->mouse_state ^=
+ (BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_CLICK_1);
+ else if (tw->mouse_state & BROWSER_MOUSE_PRESS_2)
+ tw->mouse_state ^=
+ (BROWSER_MOUSE_PRESS_2 | BROWSER_MOUSE_CLICK_2);
+
+ /* Handle modifiers being removed */
+ if (tw->mouse_state & BROWSER_MOUSE_MOD_1 && !shift)
+ tw->mouse_state ^= BROWSER_MOUSE_MOD_1;
+ if (tw->mouse_state & BROWSER_MOUSE_MOD_2 && !ctrl)
+ tw->mouse_state ^= BROWSER_MOUSE_MOD_2;
+ if (tw->mouse_state & BROWSER_MOUSE_MOD_3 && !alt)
+ tw->mouse_state ^= BROWSER_MOUSE_MOD_3;
+
+ tree->redraw = true;
+
+ if (tw->mouse_state &
+ (BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_CLICK_2
+ | BROWSER_MOUSE_DOUBLE_CLICK))
+ tree_mouse_action(tree, tw->mouse_state,
+ event->x, event->y);
+ else
+ tree_drag_end(tree, tw->mouse_state, tw->mouse_pressed_x,
+ tw->mouse_pressed_y, event->x, event->y);
+
+ tree->redraw = false;
+
+ tw->mouse_state = 0;
+
+ return TRUE;
+}
+
+gboolean nsgtk_tree_window_motion_notify_event(GtkWidget *widget,
+ GdkEventButton *event, gpointer g)
+{
+ bool shift = event->state & GDK_SHIFT_MASK;
+ bool ctrl = event->state & GDK_CONTROL_MASK;
+ bool alt = event->state & GDK_MOD1_MASK;
+ struct tree *tree = (struct tree *) g;
+ struct gtk_treeview_window *tw =
+ (struct gtk_treeview_window *) tree->handle;
+
+ if (tw->mouse_state & BROWSER_MOUSE_PRESS_1) {
+ /* Start button 1 drag */
+ tree_mouse_action(tree, BROWSER_MOUSE_DRAG_1,
+ tw->mouse_pressed_x, tw->mouse_pressed_y);
+ /* Replace PRESS with HOLDING and declare drag in progress */
+ tw->mouse_state ^= (BROWSER_MOUSE_PRESS_1 |
+ BROWSER_MOUSE_HOLDING_1);
+ tw->mouse_state |= BROWSER_MOUSE_DRAG_ON;
+ }
+ else if (tw->mouse_state & BROWSER_MOUSE_PRESS_2){
+ /* Start button 2s drag */
+ tree_mouse_action(tree, BROWSER_MOUSE_DRAG_2,
+ tw->mouse_pressed_x, tw->mouse_pressed_y);
+ /* Replace PRESS with HOLDING and declare drag in progress */
+ tw->mouse_state ^= (BROWSER_MOUSE_PRESS_2 |
+ BROWSER_MOUSE_HOLDING_2);
+ tw->mouse_state |= BROWSER_MOUSE_DRAG_ON;
+ }
+
+ /* Handle modifiers being removed */
+ if (tw->mouse_state & BROWSER_MOUSE_MOD_1 && !shift)
+ tw->mouse_state ^= BROWSER_MOUSE_MOD_1;
+ if (tw->mouse_state & BROWSER_MOUSE_MOD_2 && !ctrl)
+ tw->mouse_state ^= BROWSER_MOUSE_MOD_2;
+ if (tw->mouse_state & BROWSER_MOUSE_MOD_3 && !alt)
+ tw->mouse_state ^= BROWSER_MOUSE_MOD_3;
+
+ return TRUE;
+}
+
+
/**
- * Updates the node details for a URL node.
- * The internal node dimensions are not updated.
+ * Called when a treeview window wants to start drawing
*
- * \param node the node to update
+ * \param data should be a gtk_treeview_window
*/
-void tree_update_URL_node(struct node *node, const char *url,
- const struct url_data *data) {
+void tree_redraw_start_callback(uintptr_t data)
+{
+ struct gtk_treeview_window *tw =
+ (struct gtk_treeview_window *)data;
+ GtkWidget *widget = (GtkWidget *)tw->drawing_area;
+
+ if (set && current_widget == widget) {
+ set = true;
+ g_object_unref(current_gc);
+#ifdef CAIRO_VERSION
+ cairo_destroy(current_cr);
+#endif
+ }
+ else set = false;
+
+ current_widget = widget;
+ current_drawable = widget->window;
+ current_gc = gdk_gc_new(current_drawable);
+#ifdef CAIRO_VERSION
+ current_cr = gdk_cairo_create(current_drawable);
+#endif
+ plot = nsgtk_plotters;
+ nsgtk_plot_set_scale(1.0);
}
-
/**
- * Updates the tree owner following a tree resize
+ * Called when a treeview window wants to end drawing
*
- * \param tree the tree to update the owner of
+ * \param data should be a gtk_treeview_window
*/
-void tree_resized(struct tree *tree) {
+void tree_redraw_end_callback(uintptr_t data)
+{
+ if (!set) {
+ current_widget = NULL;
+ g_object_unref(current_gc);
+#ifdef CAIRO_VERSION
+ cairo_destroy(current_cr);
+#endif
+ }
}
Index: gtk/gtk_gui.c
===================================================================
--- gtk/gtk_gui.c (revision 7935)
+++ gtk/gtk_gui.c (working copy)
@@ -294,6 +294,12 @@
option_downloads_directory = home;
}
+ if (!option_recent_file) {
+ find_resource(buf, "Recent", "~/.netsurf/Recent");
+ LOG(("Using '%s' as Recent file", buf));
+ option_recent_file = strdup(buf);
+ }
+
find_resource(buf, "messages", "./gtk/res/messages");
LOG(("Using '%s' as Messages file", buf));
messages_load(buf);
Index: Makefile.sources
===================================================================
--- Makefile.sources (revision 7935)
+++ Makefile.sources (working copy)
@@ -32,7 +32,7 @@
# S_BROWSER are sources related to full browsers but are common
# between RISC OS, GTK, BeOS and AmigaOS builds
-S_BROWSER := browser.c frames.c history_core.c netsurf.c save_text.c \
+S_BROWSER := browser.c frames.c history_core.c history_global_core.c netsurf.c save_text.c \
selection.c textinput.c
S_BROWSER := $(addprefix desktop/,$(S_BROWSER))
Index: riscos/sslcert.c
===================================================================
--- riscos/sslcert.c (revision 7935)
+++ riscos/sslcert.c (working copy)
@@ -219,6 +219,7 @@
node = tree_create_leaf_node(tree->root, certs[i].subject);
if (node) {
node->data.data = TREE_ELEMENT_SSL;
+ node->action = ro_gui_cert_open;
tree_set_node_sprite(node, "small_xxx", "small_xxx");
}
}
Index: riscos/treeview.c
===================================================================
--- riscos/treeview.c (revision 7935)
+++ riscos/treeview.c (working copy)
@@ -520,7 +520,7 @@
*
* \param node the node to update
* \param sprite the sprite to use
- * \param selected the expanded sprite name to use
+ * \param expanded the expanded sprite name to use
*/
void tree_set_node_sprite(struct node *node, const char *sprite,
const char *expanded)
@@ -1620,3 +1620,10 @@
xwimp_force_redraw((wimp_w)tree->handle, 0, -16384, 16384, 16384);
}
}
+
+void tree_icon_name_from_filetype(char *buffer, content_type type)
+{
+ sprintf(buffer, "small_%.3x", ro_content_filetype_from_type(data->type));
+ if (!ro_gui_wimp_sprite_exists(buffer))
+ sprintf(buffer, "%s", "small_xxx");
+}
\ No newline at end of file
Index: desktop/options.h
===================================================================
--- desktop/options.h (revision 7935)
+++ desktop/options.h (working copy)
@@ -83,6 +83,7 @@
extern int option_scale;
extern bool option_incremental_reflow;
extern unsigned int option_min_reflow_period;
+extern char *option_recent_file;
extern int option_margin_top;
extern int option_margin_bottom;
Index: desktop/browser.h
===================================================================
--- desktop/browser.h (revision 7935)
+++ desktop/browser.h (working copy)
@@ -191,21 +191,24 @@
* a drag. */
BROWSER_MOUSE_CLICK_1 = 4, /* button 1 clicked. */
BROWSER_MOUSE_CLICK_2 = 8, /* button 2 clicked. */
+ BROWSER_MOUSE_DOUBLE_CLICK = 16, /* button 1 double clicked */
- BROWSER_MOUSE_DRAG_1 = 16, /* start of button 1 drag operation */
- BROWSER_MOUSE_DRAG_2 = 32, /* start of button 2 drag operation */
+ BROWSER_MOUSE_DRAG_1 = 32, /* start of button 1 drag operation */
+ BROWSER_MOUSE_DRAG_2 = 64, /* start of button 2 drag operation */
- BROWSER_MOUSE_DRAG_ON = 64, /* a drag operation was started and
+ BROWSER_MOUSE_DRAG_ON = 128, /* a drag operation was started and
* a mouse button is still pressed */
- BROWSER_MOUSE_HOLDING_1 = 128, /* while button 1 drag is in progress */
- BROWSER_MOUSE_HOLDING_2 = 256, /* while button 2 drag is in progress */
+ BROWSER_MOUSE_HOLDING_1 = 256, /* while button 1 drag is in progress */
+ BROWSER_MOUSE_HOLDING_2 = 512, /* while button 2 drag is in progress */
- BROWSER_MOUSE_MOD_1 = 512, /* primary modifier key pressed
+ BROWSER_MOUSE_MOD_1 = 1024, /* primary modifier key pressed
* (eg. Shift) */
- BROWSER_MOUSE_MOD_2 = 1024 /* secondary modifier key pressed
+ BROWSER_MOUSE_MOD_2 = 2048, /* secondary modifier key pressed
* (eg. Ctrl) */
+ BROWSER_MOUSE_MOD_3 = 4096 /* secondary modifier key pressed
+ * (eg. Alt) */
} browser_mouse_state;
Index: desktop/tree.c
===================================================================
--- desktop/tree.c (revision 7935)
+++ desktop/tree.c (working copy)
@@ -26,17 +26,26 @@
#include <stdlib.h>
#include <string.h>
#include "content/urldb.h"
+#include "desktop/browser.h"
#include "desktop/tree.h"
#include "desktop/options.h"
+#include "desktop/plotters.h"
+#include "render/font.h"
#include "utils/log.h"
#include "utils/messages.h"
#include "utils/utils.h"
+
+static void tree_initialise_nodes(struct tree *tree, struct node *root);
+static void tree_draw_node_expansion(struct tree *tree, struct node *node);
+static void tree_draw_node_element(struct tree *tree, struct node_element *element);
+static void tree_draw_line(int x, int y, int width, int height);
static void tree_draw_node(struct tree *tree, struct node *node, int clip_x,
int clip_y, int clip_width, int clip_height);
static struct node_element *tree_create_node_element(struct node *parent,
node_element_data data);
-static void tree_delete_node_internal(struct tree *tree, struct node *node, bool siblings);
+static void tree_delete_node_internal(struct tree *tree, struct node *node,
+ bool siblings);
static int tree_get_node_width(struct node *node);
static int tree_get_node_height(struct node *node);
static void tree_handle_selection_area_node(struct tree *tree,
@@ -44,19 +53,36 @@
bool invert);
static void tree_selected_to_processing(struct node *node);
void tree_clear_processing(struct node *node);
-struct node *tree_move_processing_node(struct node *node, struct node *link,
- bool before, bool first);
-struct node *tree_create_leaf_node_shared(struct node *parent, const char *title);
-
+static struct node *tree_move_processing_node(struct node *node,
+ struct node *link, bool before, bool first);
+static struct node *tree_create_leaf_node_shared(struct node *parent,
+ const char *title);
+static void tree_handle_node_changed_callback(void *p);
+static void tree_sort_insert(struct node *parent, struct node *node);
+static bool tree_launch_node(struct tree *tree, struct node *node);
+static void tree_move_selected_nodes(struct tree *tree, struct node *destination,
+ bool before);
+static void tree_handle_selection_area(struct tree *tree, int x, int y, int width,
+ int height, bool invert);
+static void tree_recalculate_size(struct tree *tree);
+static void tree_recalculate_node_element(struct node_element *element);
+static struct node *tree_get_node_at(struct node *root, int x, int y,
+ bool *furniture);
+static struct node_element *tree_get_node_element_at(struct node *node,
+ int x, int y, bool *furniture);
+static void tree_handle_node_element_changed(struct tree *tree,
+ struct node_element *element);
+
+
static int tree_initialising = 0;
-
/**
* Initialises a user-created tree
*
* \param tree the tree to initialise
*/
-void tree_initialise(struct tree *tree) {
+void tree_initialise(struct tree *tree)
+{
assert(tree);
@@ -75,7 +101,8 @@
*
* \param root the root node to update from
*/
-void tree_initialise_nodes(struct tree *tree, struct node *root) {
+void tree_initialise_nodes(struct tree *tree, struct node *root)
+{
struct node *node;
assert(root);
@@ -103,7 +130,8 @@
* \param expansion the request is the result of a node expansion
*/
void tree_handle_node_changed(struct tree *tree, struct node *node,
- bool recalculate_sizes, bool expansion) {
+ bool recalculate_sizes, bool expansion)
+{
int width, height;
assert(node);
@@ -119,10 +147,11 @@
tree_recalculate_node(tree, node, true);
if ((node->box.height != height) || (expansion)) {
tree_recalculate_node_positions(tree, tree->root);
- tree_redraw_area(tree, 0, node->box.y, 16384, 16384);
+ tree_draw(tree, 0, node->box.y, 16384, 16384);
} else {
width = (width > node->box.width) ? width : node->box.width;
- tree_redraw_area(tree, node->box.x, node->box.y, width, node->box.height);
+ tree_draw(tree, node->box.x, node->box.y, width,
+ node->box.height);
}
if ((recalculate_sizes) || (expansion))
tree_recalculate_size(tree);
@@ -136,7 +165,9 @@
* \param tree the tree to redraw
* \param element the node element to update
*/
-void tree_handle_node_element_changed(struct tree *tree, struct node_element *element) {
+void tree_handle_node_element_changed(struct tree *tree,
+ struct node_element *element)
+{
int width, height;
assert(element);
@@ -147,13 +178,13 @@
if (element->box.height != height) {
tree_recalculate_node(tree, element->parent, false);
- tree_redraw_area(tree, 0, element->box.y, 16384, 16384);
+ tree_draw(tree, 0, element->box.y, 16384, 16384);
} else {
if (element->box.width != width)
tree_recalculate_node(tree, element->parent, false);
width = (width > element->box.width) ? width :
element->box.width;
- tree_redraw_area(tree, element->box.x, element->box.y, width, element->box.height);
+ tree_draw(tree, element->box.x, element->box.y, width, element->box.height);
}
}
@@ -164,7 +195,9 @@
* \param node the node to update
* \param recalculate_sizes whether the node elements have changed
*/
-void tree_recalculate_node(struct tree *tree, struct node *node, bool recalculate_sizes) {
+void tree_recalculate_node(struct tree *tree, struct node *node,
+ bool recalculate_sizes)
+{
struct node_element *element;
int height;
@@ -185,7 +218,8 @@
}
} else {
if (recalculate_sizes)
- for (element = &node->data; element; element = element->next)
+ for (element = &node->data; element;
+ element = element->next)
tree_recalculate_node_element(element);
else
tree_recalculate_node_element(&node->data);
@@ -206,7 +240,8 @@
*
* \param root the root node to update from
*/
-void tree_recalculate_node_positions(struct tree *tree, struct node *root) {
+void tree_recalculate_node_positions(struct tree *tree, struct node *root)
+{
struct node *parent;
struct node *node;
struct node *child;
@@ -227,7 +262,7 @@
node->box.y += child->box.height;
} else {
node->box.x = tree->no_furniture ? -NODE_INSTEP + 4 : 0;
- node->box.y = -40;
+ node->box.y = -20;
}
if (node->expanded) {
if (node->folder) {
@@ -238,10 +273,11 @@
y = node->box.y;
for (element = &node->data; element;
element = element->next) {
- if (element->type == NODE_ELEMENT_TEXT_PLUS_SPRITE) {
+ if (element->type == NODE_ELEMENT_TEXT_PLUS_BITMAP) {
element->box.x = node->box.x;
} else {
- element->box.x = node->box.x + NODE_INSTEP;
+ element->box.x = node->box.x +
+ NODE_INSTEP;
}
element->box.y = y;
y += element->box.height;
@@ -261,7 +297,8 @@
* \param node the node to calculate the height of
* \return the total width of the node and children
*/
-int tree_get_node_width(struct node *node) {
+int tree_get_node_width(struct node *node)
+{
int width = 0;
int child_width;
@@ -309,13 +346,14 @@
/**
- * Updates all siblinds and descendants of a node to an expansion state.
+ * Updates all siblings and descendants of a node to an expansion state.
* No update is performed for the tree changes.
*
* \param node the node to set all siblings and descendants of
* \param expanded the expansion state to set
*/
-void tree_set_node_expanded(struct tree *tree, struct node *node, bool expanded) {
+void tree_set_node_expanded(struct tree *tree, struct node *node, bool expanded)
+{
for (; node; node = node->next) {
if (node->expanded != expanded) {
node->expanded = expanded;
@@ -328,7 +366,7 @@
/**
- * Updates all siblinds and descendants of a node to an expansion state.
+ * Updates all siblings and descendants of a node to an expansion state.
*
* \param tree the tree to update
* \param node the node to set all siblings and descendants of
@@ -337,14 +375,16 @@
* \param leaf whether to update leaves
* \return whether any changes were made
*/
-bool tree_handle_expansion(struct tree *tree, struct node *node, bool expanded, bool folder,
- bool leaf) {
+bool tree_handle_expansion(struct tree *tree, struct node *node, bool expanded,
+ bool folder, bool leaf)
+{
struct node *entry = node;
bool redraw = false;
for (; node; node = node->next) {
if ((node->expanded != expanded) && (node != tree->root) &&
- ((folder && (node->folder)) || (leaf && (!node->folder)))) {
+ ((folder && (node->folder)) ||
+ (leaf && (!node->folder)))) {
node->expanded = expanded;
if (node->child)
tree_set_node_expanded(tree, node->child, false);
@@ -355,11 +395,12 @@
redraw = true;
}
if ((node->child) && (node->expanded))
- redraw |= tree_handle_expansion(tree, node->child, expanded, folder, leaf);
+ redraw |= tree_handle_expansion(tree, node->child,
+ expanded, folder, leaf);
}
if ((entry == tree->root) && (redraw)) {
tree_recalculate_node_positions(tree, tree->root);
- tree_redraw_area(tree, 0, 0, 16384, 16384);
+ tree_draw(tree, 0, 0, 16384, 16384);
tree_recalculate_size(tree);
}
return redraw;
@@ -367,19 +408,20 @@
/**
- * Updates all siblinds and descendants of a node to an selected state.
+ * Updates all siblings and descendants of a node to an selected state.
* The required areas of the tree are redrawn.
*
* \param tree the tree to update nodes for
* \param node the node to set all siblings and descendants of
* \param selected the selection state to set
*/
-void tree_set_node_selected(struct tree *tree, struct node *node, bool selected) {
+void tree_set_node_selected(struct tree *tree, struct node *node, bool selected)
+{
for (; node; node = node->next) {
if ((node->selected != selected) && (node != tree->root)) {
node->selected = selected;
- tree_redraw_area(tree, node->box.x, node->box.y, node->box.width,
- node->data.box.height);
+ tree_draw(tree, node->box.x, node->box.y,
+ node->box.width, node->data.box.height);
}
if ((node->child) && (node->expanded))
tree_set_node_selected(tree, node->child, selected);
@@ -396,7 +438,8 @@
* \param furniture whether the returned area was in an elements furniture
* \return the node at the specified position, or NULL for none
*/
-struct node *tree_get_node_at(struct node *root, int x, int y, bool *furniture) {
+struct node *tree_get_node_at(struct node *root, int x, int y, bool *furniture)
+{
struct node_element *result;
if ((result = tree_get_node_element_at(root, x, y, furniture)))
@@ -415,7 +458,8 @@
* \return the node at the specified position, or NULL for none
*/
struct node_element *tree_get_node_element_at(struct node *node, int x, int y,
- bool *furniture) {
+ bool *furniture)
+{
struct node_element *element;
*furniture = false;
@@ -427,7 +471,8 @@
if (node->expanded) {
for (element = &node->data; element;
element = element->next) {
- if ((element->box.x < x) && (element->box.y < y) &&
+ if ((element->box.x < x) &&
+ (element->box.y < y) &&
(element->box.x + element->box.width >= x) &&
(element->box.y + element->box.height >= y))
return element;
@@ -438,18 +483,18 @@
(node->data.box.y + node->data.box.height >= y))
return &node->data;
if (((node->child) || (node->data.next)) &&
- (node->data.box.x - NODE_INSTEP + 8 < x) &&
- (node->data.box.y + 8 < y) &&
+ (node->data.box.x - NODE_INSTEP + 4 < x) &&
+ (node->data.box.y + 4 < y) &&
(node->data.box.x > x) &&
- (node->data.box.y + 32 > y)) {
+ (node->data.box.y + 20 > y)) {
*furniture = true;
return &node->data;
}
}
if ((node->child) && (node->expanded) &&
- ((element = tree_get_node_element_at(node->child, x, y,
- furniture))))
+ ((element = tree_get_node_element_at(
+ node->child, x, y, furniture))))
return element;
}
return NULL;
@@ -463,7 +508,9 @@
* \param user_type the user_type to check for
* \return the corresponding element
*/
-struct node_element *tree_find_element(struct node *node, node_element_data data) {
+struct node_element *tree_find_element(struct node *node,
+ node_element_data data)
+{
struct node_element *element;
for (element = &node->data; element; element = element->next)
if (element->data == data) return element;
@@ -478,7 +525,9 @@
* \param link the node to link before/as a child (folders) or before/after (link)
* \param before whether to link siblings before or after the supplied node
*/
-void tree_move_selected_nodes(struct tree *tree, struct node *destination, bool before) {
+void tree_move_selected_nodes(struct tree *tree, struct node *destination,
+ bool before)
+{
struct node *link;
struct node *test;
bool error;
@@ -486,7 +535,8 @@
tree_clear_processing(tree->root);
tree_selected_to_processing(tree->root);
- /* the destination node cannot be a child of any node with the processing flag set */
+ /* the destination node cannot be a child of any node with
+ the processing flag set */
error = destination->processing;
for (test = destination; test; test = test->parent)
error |= test->processing;
@@ -504,7 +554,7 @@
tree_clear_processing(tree->root);
tree_recalculate_node_positions(tree, tree->root);
- tree_redraw_area(tree, 0, 0, 16384, 16384);
+ tree_draw(tree, 0, 0, 16384, 16384);
}
@@ -539,14 +589,15 @@
/**
* Moves the first node in a tree with the processing flag set.
*
- * \param tree the node to move siblings/children of
+ * \param node the node to move siblings/children of
* \param link the node to link before/as a child (folders) or before/after (link)
* \param before whether to link siblings before or after the supplied node
* \param first whether to always link after the supplied node (ie not inside of folders)
* \return the node moved
*/
-struct node *tree_move_processing_node(struct node *node, struct node *link, bool before,
- bool first) {
+struct node *tree_move_processing_node(struct node *node, struct node *link,
+ bool before, bool first)
+{
struct node *result;
bool folder = link->folder;
@@ -562,7 +613,8 @@
return node;
}
if (node->child) {
- result = tree_move_processing_node(node->child, link, before, first);
+ result = tree_move_processing_node(node->child, link,
+ before, first);
if (result)
return result;
}
@@ -575,7 +627,8 @@
*
* \param node the root node to check from
*/
-bool tree_has_selection(struct node *node) {
+bool tree_has_selection(struct node *node)
+{
for (; node; node = node->next) {
if (node->selected)
return true;
@@ -597,8 +650,9 @@
* \param height the height of the selection rectangle
* \param invert whether to invert the selected state
*/
-void tree_handle_selection_area(struct tree *tree, int x, int y, int width, int height,
- bool invert) {
+void tree_handle_selection_area(struct tree *tree, int x, int y, int width,
+ int height, bool invert)
+{
assert(tree);
assert(tree->root);
@@ -628,8 +682,9 @@
* \param height the height of the selection rectangle
* \param invert whether to invert the selected state
*/
-void tree_handle_selection_area_node(struct tree *tree, struct node *node, int x, int y,
- int width, int height, bool invert) {
+void tree_handle_selection_area_node(struct tree *tree, struct node *node,
+ int x, int y, int width, int height, bool invert)
+{
struct node_element *element;
struct node *update;
@@ -665,10 +720,12 @@
if ((update) && (node != tree->root)) {
if (invert) {
node->selected = !node->selected;
- tree_handle_node_element_changed(tree, &node->data);
+ tree_handle_node_element_changed(tree,
+ &node->data);
} else if (!node->selected) {
node->selected = true;
- tree_handle_node_element_changed(tree, &node->data);
+ tree_handle_node_element_changed(tree,
+ &node->data);
}
}
}
@@ -693,11 +750,14 @@
assert(tree);
assert(tree->root);
- if (!tree->root->child) return;
+ if (!tree->root->child || !tree->redraw) return;
- tree_initialise_redraw(tree);
+ tree->start_redraw(tree->handle);
+ plot.fill(clip_x, clip_y, clip_x + clip_width, clip_y + clip_height,
+ 0xFFFFFF);
tree_draw_node(tree, tree->root->child, clip_x,
clip_y, clip_width, clip_height);
+ tree->end_redraw(tree->handle);
}
@@ -711,8 +771,8 @@
* \param clip_width the width of the clipping rectangle
* \param clip_height the height of the clipping rectangle
*/
-void tree_draw_node(struct tree *tree, struct node *node, int clip_x, int clip_y,
- int clip_width, int clip_height) {
+void tree_draw_node(struct tree *tree, struct node *node, int clip_x,
+ int clip_y, int clip_width, int clip_height) {
struct node_element *element;
int x_max, y_max;
@@ -720,6 +780,7 @@
assert(tree);
assert(node);
+
x_max = clip_x + clip_width + NODE_INSTEP;
y_max = clip_y + clip_height;
@@ -730,7 +791,7 @@
if (node->box.y > y_max) return;
if ((node->next) && (!tree->no_furniture))
tree_draw_line(node->box.x - (NODE_INSTEP / 2),
- node->box.y + (40 / 2), 0,
+ node->box.y + (20 / 2), 0,
node->next->box.y - node->box.y);
if ((node->box.x < x_max) && (node->box.y < y_max) &&
(node->box.x + node->box.width + NODE_INSTEP >= clip_x) &&
@@ -739,17 +800,17 @@
if ((node->expanded) && (node->child))
tree_draw_line(node->box.x + (NODE_INSTEP / 2),
node->data.box.y + node->data.box.height, 0,
- (40 / 2));
+ (20 / 2));
if ((node->parent) && (node->parent != tree->root) &&
(node->parent->child == node))
tree_draw_line(node->parent->box.x + (NODE_INSTEP / 2),
node->parent->data.box.y +
node->parent->data.box.height, 0,
- (40 / 2));
+ (20 / 2));
tree_draw_line(node->box.x - (NODE_INSTEP / 2),
node->data.box.y +
- node->data.box.height - (40 / 2),
- (NODE_INSTEP / 2) - 4, 0);
+ node->data.box.height - (20 / 2),
+ (NODE_INSTEP / 2) - 2, 0);
tree_draw_node_expansion(tree, node);
}
if (node->expanded)
@@ -775,7 +836,9 @@
* \param before set to whether the node should be linked before on exit
* \return the node to link with
*/
-struct node *tree_get_link_details(struct tree *tree, int x, int y, bool *before) {
+struct node *tree_get_link_details(struct tree *tree, int x, int y,
+ bool *before)
+{
struct node *node = NULL;
bool furniture;
@@ -805,37 +868,55 @@
* \param node the node to link
* \param before whether to link siblings before or after the supplied node
*/
-void tree_link_node(struct node *link, struct node *node, bool before) {
+void tree_link_node(struct node *link, struct node *node, bool before)
+{
+
+ struct node *parent;
+ bool sort = false;
+
assert(link);
assert(node);
if ((!link->folder) || (before)) {
- node->parent = link->parent;
- if (before) {
- node->next = link;
- node->previous = link->previous;
- if (link->previous) link->previous->next = node;
- link->previous = node;
- if ((link->parent) && (link->parent->child == link))
- link->parent->child = node;
- } else {
- node->previous = link;
- node->next = link->next;
- if (link->next) link->next->previous = node;
- link->next = node;
+ parent = node->parent = link->parent;
+ if (parent->sort)
+ sort = true;
+ else {
+ if (before) {
+ node->next = link;
+ node->previous = link->previous;
+ if (link->previous) link->previous->next = node;
+ link->previous = node;
+ if ((link->parent) && (link->parent->child == link))
+ link->parent->child = node;
+ } else {
+ node->previous = link;
+ node->next = link->next;
+ if (link->next) link->next->previous = node;
+ link->next = node;
+ }
}
} else {
- if (!link->child) {
- link->child = link->last_child = node;
- node->previous = NULL;
- } else {
- link->last_child->next = node;
- node->previous = link->last_child;
- link->last_child = node;
+ parent = node->parent = link;
+ if (parent->sort)
+ sort = true;
+ else {
+ node->next = NULL;
+ if (!link->child) {
+ link->child = link->last_child = node;
+ node->previous = NULL;
+ } else {
+ link->last_child->next = node;
+ node->previous = link->last_child;
+ link->last_child = node;
+ }
}
- node->parent = link;
- node->next = NULL;
+
}
+
+ if (sort)
+ tree_sort_insert(parent, node);
+
node->deleted = false;
}
@@ -845,7 +926,8 @@
*
* \param node the node to delink
*/
-void tree_delink_node(struct node *node) {
+void tree_delink_node(struct node *node)
+{
assert(node);
if (node->parent) {
@@ -875,7 +957,8 @@
* \param tree the tree to delete from
* \param node the node to delete
*/
-void tree_delete_selected_nodes(struct tree *tree, struct node *node) {
+void tree_delete_selected_nodes(struct tree *tree, struct node *node)
+{
struct node *next;
while (node) {
@@ -896,11 +979,12 @@
* \param node the node to delete
* \param siblings whether to delete all siblings
*/
-void tree_delete_node(struct tree *tree, struct node *node, bool siblings) {
+void tree_delete_node(struct tree *tree, struct node *node, bool siblings)
+{
tree_delete_node_internal(tree, node, siblings);
if (tree->root)
tree_recalculate_node_positions(tree, tree->root);
- tree_redraw_area(tree, 0, 0, 16384, 16384); /* \todo correct area */
+ tree_draw(tree, 0, 0, 16384, 16384); /* \todo correct area */
tree_recalculate_size(tree);
}
@@ -912,7 +996,9 @@
* \param node the node to delete
* \param siblings whether to delete all siblings
*/
-void tree_delete_node_internal(struct tree *tree, struct node *node, bool siblings) {
+void tree_delete_node_internal(struct tree *tree, struct node *node,
+ bool siblings)
+{
struct node *next, *child;
struct node_element *e, *f, *domain, *path;
const char *domain_t, *path_t, *name_t;
@@ -980,10 +1066,10 @@
}
}
}
- if (e->sprite) {
+ if (e->bitmap) {
/* TODO the type of this field is platform dependent */
- free(e->sprite); /* \todo platform specific bits */
- e->sprite = NULL;
+ free(e->bitmap); /* \todo platform specific bits */
+ e->bitmap = NULL;
}
f = e->next;
if (e != &node->data)
@@ -1004,8 +1090,10 @@
* \param title the node title (copied)
* \return the newly created node.
*/
-struct node *tree_create_folder_node(struct node *parent, const char *title) {
+struct node *tree_create_folder_node(struct node *parent, const char *title)
+{
struct node *node;
+ struct content *bm;
assert(title);
@@ -1017,7 +1105,10 @@
node->data.type = NODE_ELEMENT_TEXT;
node->data.text = squash_whitespace(title);
node->data.data = TREE_ELEMENT_TITLE;
- tree_set_node_sprite_folder(node);
+ node->sort = NULL;
+ node->action = NULL;
+ bm = tree_get_bitmap(node, "small_dir", false);
+ tree_set_node_bitmap(node, bm);
if (parent)
tree_link_node(parent, node, false);
return node;
@@ -1031,7 +1122,8 @@
* \param title the node title (copied)
* \return the newly created node.
*/
-struct node *tree_create_leaf_node(struct node *parent, const char *title) {
+struct node *tree_create_leaf_node(struct node *parent, const char *title)
+{
struct node *node;
assert(title);
@@ -1044,6 +1136,7 @@
node->data.text = strdup(squash_whitespace(title));
node->data.data = TREE_ELEMENT_TITLE;
node->editable = true;
+ node->sort = NULL;
if (parent)
tree_link_node(parent, node, false);
return node;
@@ -1057,7 +1150,9 @@
* \param title the node title
* \return the newly created node.
*/
-struct node *tree_create_leaf_node_shared(struct node *parent, const char *title) {
+struct node *tree_create_leaf_node_shared(struct node *parent,
+ const char *title)
+{
struct node *node;
assert(title);
@@ -1070,6 +1165,7 @@
node->data.text = title;
node->data.data = TREE_ELEMENT_TITLE;
node->editable = false;
+ node->sort = NULL;
if (parent)
tree_link_node(parent, node, false);
return node;
@@ -1086,9 +1182,8 @@
* \param title the custom title to use
* \return the node created, or NULL for failure
*/
-struct node *tree_create_URL_node(struct node *parent,
- const char *url, const struct url_data *data,
- const char *title) {
+struct node *tree_create_URL_node(struct node *parent, const char *url,
+ const struct url_data *data, const char *title) {
struct node *node;
struct node_element *element;
@@ -1124,7 +1219,8 @@
* \return the node created, or NULL for failure
*/
struct node *tree_create_URL_node_shared(struct node *parent,
- const char *url, const struct url_data *data) {
+ const char *url, const struct url_data *data)
+{
struct node *node;
struct node_element *element;
const char *title;
@@ -1165,11 +1261,14 @@
* \return the node created, or NULL for failure
*/
struct node *tree_create_cookie_node(struct node *parent,
- const struct cookie_data *data) {
+ const struct cookie_data *data)
+{
+
struct node *node;
struct node_element *element;
char buffer[256];
char buffer2[16];
+ struct content *bm;
node = tree_create_leaf_node(parent, data->name);
if (!node)
@@ -1187,7 +1286,8 @@
element = tree_create_node_element(node, TREE_ELEMENT_VERSION);
if (element) {
snprintf(buffer2, 16, "TreeVersion%i", data->version);
- snprintf(buffer, 256, messages_get("TreeVersion"), messages_get(buffer2));
+ snprintf(buffer, 256, messages_get("TreeVersion"),
+ messages_get(buffer2));
element->text = strdup(buffer);
}
element = tree_create_node_element(node, TREE_ELEMENT_SECURE);
@@ -1243,7 +1343,8 @@
element->text = strdup(buffer);
}
- tree_set_node_sprite(node, "small_xxx", "small_xxx");
+ bm = tree_get_bitmap(node, "small_xxx", false);
+ tree_set_node_bitmap(node, bm);
return node;
}
@@ -1255,7 +1356,9 @@
* \param user_type the required user_type
* \return the newly created element.
*/
-struct node_element *tree_create_node_element(struct node *parent, node_element_data data) {
+struct node_element *tree_create_node_element(struct node *parent,
+ node_element_data data)
+{
struct node_element *element;
element = calloc(sizeof(struct node_element), 1);
@@ -1274,13 +1377,13 @@
*
* \param tree the tree to recalculate
*/
-void tree_recalculate_size(struct tree *tree) {
+void tree_recalculate_size(struct tree *tree)
+{
int width, height;
assert(tree);
- if (!tree->handle)
- return;
+
width = tree->width;
height = tree->height;
if (tree->root) {
@@ -1301,7 +1404,8 @@
* \param node the node to search sibling and children
* \return the selected node, or NULL if multiple nodes are selected
*/
-struct node *tree_get_selected_node(struct node *node) {
+struct node *tree_get_selected_node(struct node *node)
+{
struct node *result = NULL;
struct node *temp;
@@ -1323,3 +1427,621 @@
}
return result;
}
+
+/**
+ * Draws an elements expansion icon
+ *
+ * \param tree the tree to draw the expansion for
+ * \param element the element to draw the expansion for
+ */
+void tree_draw_node_expansion(struct tree *tree, struct node *node)
+{
+ int x, y;
+
+ assert(tree);
+ assert(node);
+
+ if ((node->child) || (node->data.next)) {
+ x = node->box.x - (NODE_INSTEP / 2) - 4;
+ y = node->box.y - (TREE_TEXT_HEIGHT / 2) + 16;
+ plot.fill(x, y, x + 9, y + 9, 0xFFFFFF);
+ plot.rectangle(x , y, 8, 8, 0, 0x000000, false, false);
+ plot.line(x + 2, y + 4, x + 6, y + 4, 1, 0x000000, false, false);
+ if (!node->expanded)
+ plot.line(x + 4, y + 2, x + 4, y + 6, 1, 0x000000,
+ false, false);
+
+ }
+
+}
+
+/**
+ * Draws a line.
+ *
+ * \param x the x co-ordinate
+ * \param y the y co-ordinate
+ * \param width the width of the line
+ * \param height the height of the line
+ */
+void tree_draw_line(int x, int y, int width, int height)
+{
+ if (width > height)
+ plot.line(x, y, x + width, y, 1, 0x888888, false, false);
+ else
+ plot.line(x, y, x, y + height, 1, 0x888888, false, false);
+}
+
+/**
+ * Sets a node element as having a specific bitmap.
+ *
+ * \param node the node to update
+ * \param bitmap the bitmap to use
+ */
+void tree_set_node_bitmap(struct node *node, struct content *bitmap)
+{
+
+ assert(node);
+ assert(bitmap);
+
+ // this is done in tree_callback at the moment
+ /*node->data.bitmap = bitmap;
+ if (!node->data.bitmap) return;*/
+
+ node->data.type = NODE_ELEMENT_TEXT_PLUS_BITMAP;
+}
+
+/**
+ * Updates the node details for a URL node.
+ * The internal node dimensions are not updated.
+ *
+ * \param node the node to update
+ */
+void tree_update_URL_node(struct node *node, const char *url,
+ const struct url_data *data)
+{
+ struct node_element *element;
+ char buffer[256];
+ struct content *bm;
+
+ assert(node);
+
+ element = tree_find_element(node, TREE_ELEMENT_URL);
+ if (!element)
+ return;
+ if (data) {
+ /* node is linked, update */
+ assert(!node->editable);
+ if (!data->title)
+ urldb_set_url_title(url, url);
+
+ if (!data->title)
+ return;
+
+ node->data.text = data->title;
+ } else {
+ /* node is not linked, find data */
+ assert(node->editable);
+ data = urldb_get_url_data(element->text);
+ if (!data)
+ return;
+ }
+
+ if (element) {
+ tree_icon_name_from_filetype(buffer, data->type);
+ bm = tree_get_bitmap(node, buffer, false);
+ tree_set_node_bitmap(node, bm);
+ }
+
+ element = tree_find_element(node, TREE_ELEMENT_LAST_VISIT);
+ if (element) {
+ snprintf(buffer, 256, messages_get("TreeLast"),
+ (data->last_visit > 0) ?
+ ctime((time_t *)&data->last_visit) :
+ messages_get("TreeUnknown"));
+ if (data->last_visit > 0)
+ buffer[strlen(buffer) - 1] = '\0';
+ free((void *)element->text);
+ element->text = strdup(buffer);
+ }
+
+ element = tree_find_element(node, TREE_ELEMENT_VISITS);
+ if (element) {
+ snprintf(buffer, 256, messages_get("TreeVisits"),
+ data->visits);
+ free((void *)element->text);
+ element->text = strdup(buffer);
+ }
+}
+
+/**
+ * Recalculates the dimensions of a node element.
+ *
+ * \param element the element to recalculate
+ */
+void tree_recalculate_node_element(struct node_element *element)
+{
+ const struct bitmap *bitmap = NULL;
+ struct node_element *url_element;
+
+ assert(element);
+
+ switch (element->type) {
+ case NODE_ELEMENT_TEXT_PLUS_BITMAP:
+ /*TODO: This assert can't be used as long as the bitmap
+ gets fetched as a content*/
+// assert(element->bitmap);
+ case NODE_ELEMENT_TEXT:
+ assert(element->text);
+ nsfont.font_width(&css_base_style, element->text,
+ strlen(element->text),
+ &element->box.width);
+ element->box.width += 8;
+ element->box.height = TREE_TEXT_HEIGHT;
+ if (element->type == NODE_ELEMENT_TEXT_PLUS_BITMAP)
+ element->box.width += NODE_INSTEP;
+ break;
+ case NODE_ELEMENT_THUMBNAIL:
+ url_element = tree_find_element(element->parent,
+ TREE_ELEMENT_URL);
+ if (url_element)
+ bitmap = urldb_get_thumbnail(url_element->text);
+ if (bitmap) {
+/* if ((bitmap->width == 0) && (bitmap->height == 0))
+ frame = bitmap_get_buffer(bitmap);
+ element->box.width = bitmap->width * 2 + 1;
+ element->box.height = bitmap->height * 2 + 2;
+*/ element->box.width = THUMBNAIL_WIDTH + 1;
+ element->box.height = THUMBNAIL_HEIGHT + 2;
+ } else {
+ element->box.width = 0;
+ element->box.height = 0;
+ }
+ break;
+ }
+}
+
+void tree_handle_node_changed_callback(void *p)
+{
+ struct node_update *update = p;
+
+ tree_handle_node_changed(update->tree, update->node, true, false);
+ free(update);
+}
+
+/**
+ * Draws an element, including any expansion icons
+ *
+ * \param tree the tree to draw an element for
+ * \param element the element to draw
+ */
+void tree_draw_node_element(struct tree *tree, struct node_element *element)
+{
+
+ struct node_element *url_element;
+ struct bitmap *bitmap = NULL;
+ struct node_update *update;
+ const uint8_t *frame;
+ int x0, y0, x1, y1;
+ bool selected = false;
+ colour bg, c;
+
+ assert(tree);
+ assert(element);
+ assert(element->parent);
+
+ x0 = element->box.x;
+ x1 = x0 + element->box.width;
+ y0 = element->box.y;
+ y1 = y0 + element->box.height;
+ if (&element->parent->data == element)
+ if (element->parent->selected)
+ selected = true;
+
+ switch (element->type) {
+ case NODE_ELEMENT_TEXT_PLUS_BITMAP:
+
+ /*TODO: the if is necessary as long as the bitmap gets fetched
+ as a content*/
+ //assert(element->bitmap);
+ if (element->bitmap)
+ content_redraw(element->bitmap, x0, y0 + 3, 16,
+ 16, x0, y0, x0 + 16, y0 + 16, 1,
+ 0);
+ x0 += NODE_INSTEP;
+
+ /* fall through */
+ case NODE_ELEMENT_TEXT:
+ assert(element->text);
+
+ if (element == tree->editing)
+ return;
+
+ if (selected) {
+ bg = 0x000000;
+ c = 0xEEEEEE;
+ } else {
+ bg = 0xFFFFFF;
+ c = 0x000000;
+ }
+ plot.text(x0 + 4, y0 + TREE_TEXT_HEIGHT * 0.75,
+ &css_base_style, element->text,
+ strlen(element->text), bg, c);
+ break;
+ case NODE_ELEMENT_THUMBNAIL:
+ url_element = tree_find_element(element->parent,
+ TREE_ELEMENT_URL);
+ if (url_element)
+ bitmap = urldb_get_thumbnail(url_element->text);
+ if (bitmap) {
+ frame = bitmap_get_buffer(
+ (struct bitmap *) bitmap);
+ if (!frame)
+ urldb_set_thumbnail(url_element->text,
+ NULL);
+ if ((!frame) || (element->box.width == 0)) {
+ update = calloc(sizeof(struct node_update), 1);
+ if (!update)
+ return;
+ update->tree = tree;
+ update->node = element->parent;
+ schedule(0, tree_handle_node_changed_callback,
+ update);
+ return;
+ }
+ plot.bitmap(element->box.x, element->box.y,
+ THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT,
+ bitmap, 0xFFFFFF, NULL);
+ if (!tree->no_furniture) {
+ tree_draw_line(element->box.x,
+ element->box.y,
+ element->box.width - 1,
+ 0);
+ tree_draw_line(element->box.x,
+ element->box.y,
+ 0,
+ element->box.height - 3);
+ tree_draw_line(element->box.x,
+ element->box.y +
+ element->box.height - 3,
+ element->box.width - 1,
+ 0);
+ tree_draw_line(element->box.x +
+ element->box.width - 1,
+ element->box.y,
+ 0,
+ element->box.height - 3);
+ }
+ }
+ break;
+ }
+
+}
+
+
+/**
+ * Sets the sort funstion for a node
+ *
+ * \param node the node to be inserted
+ * \param sort pointer to the sorting function
+ */
+void tree_set_node_sort_function(struct node *node,
+ int (*sort) (struct node *, struct node *))
+{
+ struct node *child;
+
+ node->sort = sort;
+
+ /* the node had already some children so they must get sorted */
+ if (node->child) {
+
+ child = node->child;
+ node->child = NULL;
+
+ while (child) {
+ tree_sort_insert(node, child);
+ child = child->next;
+ }
+
+ }
+}
+
+/**
+ * Inserts a node into the correct place according to the parents sort function
+ *
+ * \param parent the node whose child node 'node' becomes
+ * \param node the node to be inserted
+ */
+static void tree_sort_insert(struct node *parent, struct node *node)
+{
+ struct node *after;
+
+ assert(node);
+ assert(parent);
+ assert(parent->sort);
+
+ after = parent->last_child;
+ while (after && parent->sort(node, after) == -1)
+ after = after->previous;
+
+ if (after) {
+ if (after->next)
+ after->next->previous = node;
+ node->next = after->next;
+ node->previous = after;
+ after->next = node;
+ }
+ else {
+ node->previous = NULL;
+ node->next = parent->child;
+ if (parent->child)
+ parent->child->previous = node;
+ parent->child = node;
+ }
+
+ if (node->next == NULL)
+ parent->last_child = node;
+
+ node->parent = parent;
+
+}
+
+
+/**
+ * Alphabetical comparison function for nodes
+ *
+ * \param n1 first node to compare
+ * \param n2 first node to compare
+ * \return 0 if equal, greater then zero if n1 > n2,
+ * less then zero if n2 < n1
+ */
+int tree_alphabetical_sort(struct node *n1, struct node *n2)
+{
+ return strcmp(n1->data.text, n2->data.text);
+}
+
+/**
+ * Handles a mouse action for a tree
+ *
+ * \param mouse the mouse state
+ * \param tree the tree to handle a click for
+ * \return whether the click was handled
+ */
+bool tree_mouse_action(struct tree *tree, browser_mouse_state mouse, int x,
+ int y)
+{
+ bool furniture;
+ struct node *node;
+ struct node *last;
+ struct node_element *element;
+
+ assert(tree);
+ assert(tree->root);
+
+ if (!tree->root->child)
+ return true;
+
+ tree_initialise_redraw(tree);
+ element = tree_get_node_element_at(tree->root->child, x, y, &furniture);
+
+ /* stop editing for anything but a drag */
+// if ((tree->editing) && /*(pointer->i != tree->edit_handle) &&*/
+// !(mouse & BROWSER_MOUSE_DRAG_1))
+// ro_gui_tree_stop_edit(tree);
+
+ /* handle a menu click */
+// if (pointer->buttons == wimp_CLICK_MENU) {
+// if ((!element) || (!tree->root->child) ||
+// (tree_has_selection(tree->root->child)))
+// return true;
+//
+// node = element->parent;
+// tree->temp_selection = node;
+// node->selected = true;
+// tree_handle_node_element_changed(tree, &node->data);
+// return true;
+//
+// }
+
+ /* no item either means cancel selection on (select) click or a drag */
+ if (!element) {
+ if (tree->single_selection) {
+ tree_set_node_selected(tree, tree->root->child, false);
+ return true;
+ }
+ if (mouse & (BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_DRAG_1))
+ tree_set_node_selected(tree, tree->root->child, false);
+ if (mouse & (BROWSER_MOUSE_DRAG_1 | BROWSER_MOUSE_DRAG_2)) {
+
+ /* TODO: the tree window has to scroll the tree when mouse
+ reaches border while dragging this isn't solved for
+ the browser window too.
+ */
+ tree->drag = TREE_SELECT_DRAG;
+ }
+ return true;
+ }
+
+ node = element->parent;
+
+ /* click on furniture or double click on folder toggles node expansion */
+ if (((furniture) && (mouse & (BROWSER_MOUSE_CLICK_1 |
+ BROWSER_MOUSE_CLICK_2))) ||
+ ((!furniture) && (node->child) &&
+ (mouse & BROWSER_MOUSE_DOUBLE_CLICK))) {
+
+ node->expanded = !node->expanded;
+ if (!furniture)
+ node->selected = false;
+ tree_handle_node_changed(tree, node, false, true);
+
+ /* find the last child node if expanded */
+ last = node;
+ if ((last->child) && (last->expanded)) {
+ last = last->child;
+ while ((last->next) ||
+ ((last->child) && (last->expanded))) {
+ if (last->next)
+ last = last->next;
+ else
+ last = last->child;
+ }
+ }
+ /* scroll to the bottom element then back to the top */
+ element = &last->data;
+ if (last->expanded)
+ for (; element->next; element = element->next);
+ tree_scroll_visible(tree, element);
+ tree_scroll_visible(tree, &node->data);
+ return true;
+ }
+
+ /* no use for any other furniture click */
+ if (furniture)
+ return true;
+
+ /* single/double alt+click starts editing */
+ if ((node->editable) && (!tree->editing) &&
+ ((element->data == TREE_ELEMENT_URL) ||
+ (element->data == TREE_ELEMENT_TITLE)) &&
+ (mouse & (BROWSER_MOUSE_CLICK_1 |
+ BROWSER_MOUSE_DOUBLE_CLICK)) &&
+ mouse & BROWSER_MOUSE_MOD_3) {
+// ro_gui_tree_start_edit(tree, element, pointer);
+ return true;
+ }
+
+ /* double click starts launches the leaf */
+ if (mouse & BROWSER_MOUSE_DOUBLE_CLICK) {
+ if (!tree_launch_node(tree, node))
+ return false;
+ return true;
+ }
+
+ /* single click (select) cancels current selection and selects item */
+ if (mouse & BROWSER_MOUSE_CLICK_1 || (mouse & BROWSER_MOUSE_CLICK_2 &&
+ tree->single_selection)) {
+ if (!node->selected) {
+ tree_set_node_selected(tree, tree->root->child, false);
+ node->selected = true;
+ tree_handle_node_element_changed(tree, &node->data);
+ }
+ return true;
+ }
+
+ /* single click (adjust) toggles item selection */
+ if (mouse & BROWSER_MOUSE_CLICK_2) {
+ node->selected = !node->selected;
+ tree_handle_node_element_changed(tree, &node->data);
+ return true;
+ }
+
+ /* drag starts a drag operation */
+ if ((!tree->editing) && (mouse & (BROWSER_MOUSE_DRAG_1 |
+ BROWSER_MOUSE_DRAG_2))) {
+ if (tree->no_drag)
+ return true;
+
+ if (!node->selected) {
+ node->selected = true;
+ tree_handle_node_element_changed(tree, &node->data);
+ }
+
+ tree->drag = TREE_MOVE_DRAG;
+
+// node = tree_get_selected_node(tree->root);
+// if (node) {
+// if (node->folder) {
+// if ((node->expanded) &&
+// (ro_gui_wimp_sprite_exists("directoryo")))
+// sprintf(ro_gui_tree_drag_name, "directoryo");
+// else
+// sprintf(ro_gui_tree_drag_name, "directory");
+// } else {
+// /* small_xxx -> file_xxx */
+// sprintf(ro_gui_tree_drag_name, "file_%s",
+// node->data.sprite->name + 6);
+// if (!ro_gui_wimp_sprite_exists(ro_gui_tree_drag_name))
+// sprintf(ro_gui_tree_drag_name, "file_xxx");
+// }
+// } else {
+// sprintf(ro_gui_tree_drag_name, "package");
+// }
+//
+// error = xdragasprite_start(dragasprite_HPOS_CENTRE |
+// dragasprite_VPOS_CENTRE |
+// dragasprite_BOUND_POINTER |
+// dragasprite_DROP_SHADOW,
+// (osspriteop_area *) 1,
+// ro_gui_tree_drag_name, &box, 0);
+// if (error)
+// LOG(("xdragasprite_start: 0x%x: %s",
+// error->errnum, error->errmess));
+ return true;
+ }
+
+
+ return false;
+}
+
+/**
+ * Launches a node using all known methods.
+ *
+ * \param node the node to launch
+ * \return whether the node could be launched
+ */
+bool tree_launch_node(struct tree *tree, struct node *node)
+{
+ struct node_element *element;
+
+ assert(node);
+
+ if (node->action) {
+ node->action(tree, node);
+ return true;
+ }
+
+ element = tree_find_element(node, TREE_ELEMENT_URL);
+ if (element) {
+ browser_window_create(element->text, NULL, 0, true, false);
+ return true;
+ }
+
+ return false;
+}
+
+
+/**
+ * Handle the end of a drag operation
+ *
+ * \param tree the tree on which the drag was performed
+ * \param mouse mouse state during drag end
+ * \param x0 x coordinate of drag start
+ * \param y0 y coordinate of drag start
+ * \param x1 x coordinate of drag end
+ * \param y1 y coordinate of drag end
+ */
+void tree_drag_end(struct tree *tree, browser_mouse_state mouse, int x0, int y0,
+ int x1, int y1)
+{
+
+ bool before;
+ struct node *node;
+
+ switch (tree->drag) {
+ case TREE_NO_DRAG:
+ break;
+ case TREE_SELECT_DRAG:
+ tree_handle_selection_area(tree, x0, y0,
+ x1 - x0 , y1 - y0,
+ (mouse | BROWSER_MOUSE_HOLDING_2));
+ break;
+ case TREE_MOVE_DRAG:
+ if (!tree->movable)
+ return;
+ node = tree_get_link_details(tree, x1, y1, &before);
+ tree_move_selected_nodes(tree, node, before);
+ break;
+ }
+
+ tree->drag = TREE_NO_DRAG;
+}
Index: desktop/tree.h
===================================================================
--- desktop/tree.h (revision 7935)
+++ desktop/tree.h (working copy)
@@ -26,10 +26,19 @@
#include <stdbool.h>
#include <stdint.h>
+#include "desktop/browser.h"
+#include "image/bitmap.h"
+
struct url_data;
struct cookie_data;
typedef enum {
+ TREE_NO_DRAG = 0,
+ TREE_SELECT_DRAG,
+ TREE_MOVE_DRAG
+} tree_drag_type;
+
+typedef enum {
TREE_ELEMENT_URL,
TREE_ELEMENT_ADDED,
TREE_ELEMENT_LAST_VISIT,
@@ -50,14 +59,22 @@
TREE_ELEMENT_SSL
} node_element_data;
-#define NODE_INSTEP 40
+typedef void(*tree_start_radraw_callback)(uintptr_t data);
+typedef void(*tree_end_radraw_callback)(uintptr_t data);
-struct node_sprite;
+#define NODE_INSTEP 20
+#define TREE_TEXT_HEIGHT 20
+#define THUMBNAIL_WIDTH 100
+#define THUMBNAIL_HEIGHT 86
+#define MAXIMUM_URL_LENGTH 1024
+
struct toolbar;
+struct node;
+struct tree;
typedef enum {
NODE_ELEMENT_TEXT, /* <-- Text only */
- NODE_ELEMENT_TEXT_PLUS_SPRITE, /* <-- Text and sprite */
+ NODE_ELEMENT_TEXT_PLUS_BITMAP, /* <-- Text and bitmap */
NODE_ELEMENT_THUMBNAIL, /* <-- Bitmap only */
} node_element_type;
@@ -75,7 +92,7 @@
node_element_type type; /* <-- Element type */
struct node_element_box box; /* <-- Element bounding box */
const char *text; /* <-- Text for the element */
- struct node_sprite *sprite; /* <-- Sprite for the element */
+ struct content *bitmap; /* <-- Bitmap for the element */
struct node_element *next; /* <-- Next node element */
node_element_data data; /* <-- Data being represented */
};
@@ -96,11 +113,12 @@
struct node *last_child; /* <-- Last child */
struct node *previous; /* <-- Previous child of the parent */
struct node *next; /* <-- Next child of the parent */
-
+ int (*sort) (struct node *, struct node *); /* <-- Sorting function for the node (for folder nodes only)*/
+ void (*action) (struct tree *tree, struct node *node);
};
struct tree {
- unsigned int handle; /* <-- User assigned handle */
+ uintptr_t handle; /* <-- User assigned handle */
int offset_x; /* <-- User assigned tree x offset */
int offset_y; /* <-- User assigned tree y offset */
struct node *root; /* <-- Tree root element */
@@ -118,24 +136,25 @@
struct node_element *editing; /* <-- Node element being edited */
struct node *temp_selection; /* <-- Temporarily selected node */
struct toolbar *toolbar; /* <-- Tree toolbar */
+ bool redraw; /* <-- Flag indicating whether the tree should be redrawn on layout changes*/
+ tree_drag_type drag;
+ tree_start_radraw_callback start_redraw;
+ tree_end_radraw_callback end_redraw;
};
+/* callback update */
+struct node_update {
+ struct tree *tree;
+ struct node *node;
+};
/* Non-platform specific code */
void tree_initialise(struct tree *tree);
-void tree_initialise_nodes(struct tree *tree, struct node *root);
void tree_handle_node_changed(struct tree *tree, struct node *node,
bool recalculate_sizes, bool expansion);
-void tree_handle_node_element_changed(struct tree *tree,
- struct node_element *element);
void tree_recalculate_node(struct tree *tree, struct node *node, bool recalculate_sizes);
void tree_recalculate_node_positions(struct tree *tree, struct node *root);
-struct node *tree_get_node_at(struct node *root, int x, int y, bool *furniture);
-struct node_element *tree_get_node_element_at(struct node *node, int x, int y,
- bool *furniture);
struct node_element *tree_find_element(struct node *node, node_element_data data);
-void tree_move_selected_nodes(struct tree *tree, struct node *destination,
- bool before);
bool tree_has_selection(struct node *node);
void tree_draw(struct tree *tree, int clip_x, int clip_y, int clip_width,
int clip_height);
@@ -150,33 +169,32 @@
const char *url, const struct url_data *data);
struct node *tree_create_cookie_node(struct node *parent,
const struct cookie_data *data);
-void tree_set_node_sprite(struct node *node, const char *sprite,
- const char *expanded);
+void tree_set_node_bitmap(struct node *node, struct content *bitmap);
void tree_set_node_expanded(struct tree *tree, struct node *node, bool expanded);
void tree_set_node_selected(struct tree *tree, struct node *node,
bool selected);
-void tree_handle_selection_area(struct tree *tree, int x, int y, int width,
- int height, bool invert);
void tree_delete_selected_nodes(struct tree *tree, struct node *node);
void tree_delete_node(struct tree *tree, struct node *node, bool siblings);
-void tree_recalculate_size(struct tree *tree);
bool tree_handle_expansion(struct tree *tree, struct node *node, bool expanded,
bool folder, bool leaf);
struct node *tree_get_selected_node(struct node *node);
struct node *tree_get_link_details(struct tree *tree, int x, int y,
bool *before);
-
-
+void tree_update_URL_node(struct node *node, const char *url,
+ const struct url_data *data);
+void tree_set_node_sort_function(struct node *node,
+ int (*sort) (struct node *, struct node *));
+int tree_alphabetical_sort(struct node *, struct node *);
+bool tree_mouse_action(struct tree *tree, browser_mouse_state mouse,
+ int x, int y);
+void tree_drag_end(struct tree *tree, browser_mouse_state mouse, int x0, int y0,
+ int x1, int y1);
+
/* Platform specific code */
void tree_initialise_redraw(struct tree *tree);
-void tree_redraw_area(struct tree *tree, int x, int y, int width, int height);
-void tree_draw_line(int x, int y, int width, int height);
-void tree_draw_node_element(struct tree *tree, struct node_element *element);
-void tree_draw_node_expansion(struct tree *tree, struct node *node);
-void tree_recalculate_node_element(struct node_element *element);
-void tree_update_URL_node(struct node *node, const char *url,
- const struct url_data *data);
void tree_resized(struct tree *tree);
-void tree_set_node_sprite_folder(struct node *node);
-
+struct content *tree_get_bitmap(struct node *node, const char *bitmap,
+ bool path);
+void tree_icon_name_from_filetype(char *buffer, content_type type);
+void tree_scroll_visible(struct tree *tree, struct node_element *element);
#endif
Index: desktop/options.c
===================================================================
--- desktop/options.c (revision 7935)
+++ desktop/options.c (working copy)
@@ -144,6 +144,7 @@
#else
unsigned int option_min_reflow_period = 25; /* time in cs */
#endif
+char *option_recent_file = 0;
/** top margin of exported page*/
int option_margin_top = DEFAULT_MARGIN_TOP_MM;
/** bottom margin of exported page*/
@@ -243,6 +244,7 @@
{ "scale", OPTION_INTEGER, &option_scale },
{ "incremental_reflow", OPTION_BOOL, &option_incremental_reflow },
{ "min_reflow_period", OPTION_INTEGER, &option_min_reflow_period },
+ { "recent_file", OPTION_STRING, &option_recent_file },
/* Fetcher options */
{ "max_fetchers", OPTION_INTEGER, &option_max_fetchers },
{ "max_fetchers_per_host",
Conflicted files
Removed files
14 years, 2 months
Bitmap plotter API cleanup (V2)
by Vincent Sanders
Same deal as before but:
dont use named initialisers for beos plotters
fixup previously missed RISC OS plotters
fixup names to remove all reference to "tiled" as per jmb request
Its been compile tested as before
--
Regards Vincent
http://www.kyllikki.org/
14 years, 2 months
Re: Bitmap plotter API cleanup
by John-Mark Bell
On Tue, 2009-06-30 at 12:06 +0100, Vincent Sanders wrote:
> On Tue, Jun 30, 2009 at 11:50:24AM +0100, John-Mark Bell wrote:
> > On Tue, 2009-06-30 at 11:23 +0100, Vincent Sanders wrote:
> > > There is a single "regression" from this alteration which is that the
> > > pdf save exporter will not currently directly embed the original jpegs
> > > into pdf...has anyone actually tested this with a modern haru? I have,
> > > its broken anyway! I do intend to fix this but as a seperate piece of
> > > work to make our haru handling less explody.
> >
> > Wise. I've no strong opinion on this regression.
>
> Looking at it a bit more the haru lib apply *zero* sanity checking to
> teh jpegs/png etc. supplied and as we retrieve them raw from the
> internet...
Joy. I hadn't realised that haru was that lax.
> I am beginning to think our "safely" decoded bitmaps should just be
> used all the time.
Might be sensible. Of course, you pay the price with bloated PDF
files :)
> > s/bitmap_tile/bitmap/ (and rename the function, too!)
> > This applies to a number of other plotter implementations, too.
>
> I know...I was thinking I should change as little as possible and let
> the other frontend people alter them if they want...I am happy to
> rename if thats more asthetically pleasing?
I think it is, yes.
J.
14 years, 2 months
Re: Bitmap plotter API cleanup
by Vincent Sanders
On Tue, Jun 30, 2009 at 12:42:39PM +0200, François Revol wrote:
> > beos/beos_plotters.cpp | 53 +++++++-------------
>
> Don't have time to try, but gcc2 doesn't handle named member
> initialization in C++ code here, oddly:
> - nsbeos_plot_path,
> - false // option_knockout
> + .clg = nsbeos_plot_clg,
> + .rectangle = nsbeos_plot_rectangle,
>
> so B_DONT_DO_THAT.
dammit....sigh, yes, ok, undone
>
> François.
>
>
--
Regards Vincent
http://www.kyllikki.org/
14 years, 2 months
Bitmap plotter API cleanup
by Vincent Sanders
As most of you know I have been discussing cleaning up the plotter API
with respect to the bitmap operations with a view to adding a bitmap
cache in future.
The first stage of this is to rationalize the bitmap operation to a
*single* call with flags and to remove the passing around of content
objects into plotters.
This has the pleasant side effect of improving cache locality and
reducing the number of parameters to seven for the tiled case, which
means GCC stops spilling *all* the parameters to stack.
Performance wise this cleanup has pretty much zero cost. However my
updates to each frontends plotters are somewhat naive and using
direct bitops on the flags instead of converting to a bool (I wanted
least change) results in an improvement.
I have altered *all* frontend plotters but have only been able to
compile and run on framebuffer and gtk, can someone please at least
compile risc os, amiga and beos?
There is a single "regression" from this alteration which is that the
pdf save exporter will not currently directly embed the original jpegs
into pdf...has anyone actually tested this with a modern haru? I have,
its broken anyway! I do intend to fix this but as a seperate piece of
work to make our haru handling less explody.
patch attached, I will commit this later today unless there are strong
objections.
amiga/plotters.c | 37 ++++++--------
amiga/plotters.h | 4 -
beos/beos_plotters.cpp | 53 +++++++-------------
desktop/history_core.c | 2
desktop/knockout.c | 103 +++++++++-------------------------------
desktop/plotters.h | 9 +--
desktop/save_pdf/pdf_plotters.c | 73 ++++++++--------------------
framebuffer/framebuffer.c | 53 ++++++++++----------
framebuffer/gui.h | 11 ----
gtk/gtk_plotters.c | 49 +++++++------------
gtk/gtk_print.c | 48 +++++++-----------
image/bmp.c | 16 ++++--
image/gif.c | 14 ++++-
image/ico.c | 16 ++++--
image/jpeg.c | 13 +++--
image/mng.c | 12 +++-
image/png.c | 25 +++++----
image/rsvg.c | 2
riscos/plotters.c | 57 ++++++++--------------
19 files changed, 249 insertions(+), 348 deletions(-)
--
Regards Vincent
http://www.kyllikki.org/
14 years, 2 months
Progress Report N°5
by Mark
Hi All,
well rather than re-posting that table, let's simply say that the
gtkmain branch has now merged most of the sub-branches, so subject to
review should be in a fit state for merging to trunk;
as well as commenting the coding, I'd encourage people to compile it to
see what they think of the actual interface: should the throbber have
more padding in all cases, should the url bar have minimum length, etc
uploads should merge automatically;
so aside from that, I may try to really repair the car, enjoy the
weather occasionally :-) , try to learn how to cross-compile/install
libs, put in a few of the essential interface elements for a windows port
Best
Mark
http://www.halloit.com
Key ID 046B65CF
14 years, 2 months
Review: Paul Blokus -- core text widget (round 2)
by John-Mark Bell
Precis:
This is the second version of Paul Blokus' core text widget.
Added files
Index: desktop/textarea.c
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ desktop/textarea.c 2009-06-24 17:28:49.000000000 +0100
@@ -0,0 +1,1391 @@
+/*
+ * Copyright 2006 John-Mark Bell <jmb(a)netsurf-browser.org>
+ * Copyright 2009 Paul Blokus <paul_pl(a)users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Single/Multi-line UTF-8 text area (implementation)
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include "css/css.h"
+#include "desktop/textarea.h"
+#include "desktop/textinput.h"
+#include "desktop/plotters.h"
+#include "render/font.h"
+#include "utils/log.h"
+#include "utils/utf8.h"
+#include "utils/utils.h"
+
+#define MARGIN_LEFT 2
+#define MARGIN_RIGHT 2
+#define CARET_COLOR 0x000000
+/* background color for readonly textarea*/
+#define READONLY_BG 0xD9D9D9
+#define BACKGROUND_COL 0xFFFFFF
+#define BORDER_COLOR 0x000000
+#define SELECTION_COL 0xFFDDDD
+
+
+struct line_info {
+ unsigned int b_start; /**< Byte offset of line start */
+ unsigned int b_length; /**< Byte length of line */
+};
+
+struct text_area {
+
+ int x, y; /**< Coordinates of the widget
+ (top left corner) with respect to
+ canvas origin(these don't change
+ it is the canvas which gets
+ scrolled) */
+
+ int scroll_x, scroll_y; /**< scroll offsets of the textarea
+ content */
+
+ unsigned int flags; /**< Textarea flags */
+ int vis_width; /**< Visible width, in pixels */
+ int vis_height; /**< Visible height, in pixels */
+
+ char *text; /**< UTF-8 text */
+ unsigned int text_alloc; /**< Size of allocated text */
+ unsigned int text_len; /**< Length of text, in bytes */
+ unsigned int text_utf8_len; /**< Length of text, in characters
+ without the /0 character*/
+ struct {
+ int line; /**< Line caret is on */
+ int char_off; /**< Character index of caret */
+ } caret_pos;
+
+ int selection_start; /**< Character index of sel start(inclusive) */
+ int selection_end; /**< Character index of sel end(exclusive) */
+
+ struct css_style *style; /**< Text style */
+
+ int line_count; /**< Count of lines */
+#define LINE_CHUNK_SIZE 16
+ struct line_info *lines; /**< Line info array */
+ int line_height; /**< Line height obtained from style */
+
+ /** Callback functions for a redraw request*/
+ textarea_start_radraw_callback redraw_start_callback;
+ textarea_start_radraw_callback redraw_end_callback;
+
+ void *data; /** < Callback data for both callback functions*/
+
+ int drag_start_char;/**< Character index at which the drag was started*/
+};
+
+
+static bool textarea_insert_text(struct text_area *ta, unsigned int index,
+ const char *text);
+static bool textarea_replace_text(struct text_area *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,
+ bool *line_end);
+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,
+ bool line_end);
+static void textarea_normalise_text(struct text_area *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 css style (font style properties are used only)
+ * \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 text_area *textarea_create(int x, int y, int width, int height,
+ unsigned int flags, const struct css_style *style,
+ textarea_start_radraw_callback redraw_start_callback,
+ textarea_end_radraw_callback redraw_end_callback, void *data)
+{
+ struct text_area *ret;
+
+ if (redraw_start_callback == NULL || redraw_end_callback == NULL) {
+ LOG(("no callback provided"));
+ return NULL;
+ }
+
+ ret = malloc(sizeof(struct text_area));
+ if (ret == NULL) {
+ LOG(("malloc failed"));
+ return NULL;
+ }
+
+ ret->redraw_start_callback = redraw_start_callback;
+ ret->redraw_end_callback = redraw_end_callback;
+ ret->data = data;
+ ret->x = x;
+ ret->y = y;
+ 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 = 1;
+
+ ret->style = malloc(sizeof(struct css_style));
+ if (ret->style == NULL) {
+ LOG(("malloc failed"));
+ free(ret->text);
+ free(ret);
+ return NULL;
+ }
+ memcpy(ret->style, style, sizeof(struct css_style));
+ ret->line_height = css_len2px(&(style->line_height.value.length),
+ style);
+
+ ret->caret_pos.line = ret->caret_pos.char_off = 0;
+ ret->selection_start = -1;
+ ret->selection_end = -1;
+
+ ret->line_count = 0;
+ ret->lines = 0;
+
+ return ret;
+}
+
+
+/**
+ * Destroy a text area
+ *
+ * \param ta Text area to destroy
+ */
+void textarea_destroy(struct text_area *ta)
+{
+ free(ta->text);
+ free(ta->style);
+ free(ta->lines);
+ free(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 text_area *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 = len + 64;
+ }
+
+ memcpy(ta->text, text, len);
+ ta->text_len = len;
+ ta->text_utf8_len = utf8_length(ta->text);
+
+ textarea_normalise_text(ta, 0, len);
+
+ return textarea_reflow(ta, 0);
+}
+
+
+/**
+ * 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 text_area *ta, char *buf, unsigned int len)
+{
+ if (buf == NULL && len == 0) {
+ /* want length */
+ return ta->text_len;
+ }
+
+ if (len < ta->text_len) {
+ LOG(("buffer too small"));
+ return -1;
+ }
+
+ memcpy(buf, ta->text, ta->text_len);
+
+ return ta->text_len;
+}
+
+
+/**
+ * Insert text into the text area
+ *
+ * \param ta Text area
+ * \param index 0-based character index to insert at
+ * \param text UTF-8 text to insert
+ * \return true on success, false otherwise (memory exhaustion or
+ * readonly flag set)
+ */
+bool textarea_insert_text(struct text_area *ta, unsigned int index,
+ const char *text)
+{
+ unsigned int b_len = strlen(text);
+ size_t b_off;
+
+ if (ta->flags & TEXTAREA_READONLY)
+ return false;
+
+ /* Find insertion point */
+ if (index > ta->text_utf8_len)
+ index = ta->text_utf8_len;
+
+ LOG(("inserting at %i\n", index));
+
+ /*find byte offset of insertion point*/
+ for (b_off = 0; index-- > 0;
+ b_off = utf8_next(ta->text, ta->text_len, b_off))
+ ; /* do nothing */
+
+ if (b_len + ta->text_len >= ta->text_alloc) {
+ char *temp = realloc(ta->text, b_len + ta->text_len + 64);
+ if (temp == NULL) {
+ LOG(("realloc failed"));
+ return false;
+ }
+
+ ta->text = temp;
+ ta->text_alloc = b_len + ta->text_len + 64;
+ }
+
+ /* Shift text following up */
+ memmove(ta->text + b_off + b_len, ta->text + b_off,
+ ta->text_len - b_off);
+ /* Insert new text */
+ memcpy(ta->text + b_off, text, b_len);
+ ta->text_len += b_len;
+ ta->text_utf8_len += utf8_length(text);
+
+ textarea_normalise_text(ta, b_off, b_len);
+
+ /** \todo calculate line to reflow from */
+ return textarea_reflow(ta, 0);
+
+}
+
+
+/**
+ * Replace text in a text area
+ *
+ * \param ta Text area
+ * \param start Start character index of replaced section (inclusive)
+ * \param end End character index of replaced section (exclusive)
+ * \param text UTF-8 text to insert
+ * \return true on success, false otherwise (memory exhaustion or
+ * readonly flag set)
+ */
+bool textarea_replace_text(struct text_area *ta, unsigned int start,
+ unsigned int end, const char *text)
+{
+ unsigned int b_len = strlen(text);
+ size_t b_start, b_end, diff;
+
+ if (ta->flags & TEXTAREA_READONLY)
+ return false;
+
+ if (start > ta->text_utf8_len)
+ start = ta->text_utf8_len;
+ 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 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);
+}
+
+
+/**
+ * Set the caret's position
+ *
+ * \param ta Text area
+ * \param caret 0-based character index to place caret at
+ * \param line_end says whether caret should be positioned at the end of
+ * the previos line if it indices on the beginning of
+ * a line that was crated by a soft wrap (on a space)
+ * \return true on success false otherwise
+ */
+bool textarea_set_caret(struct text_area *ta, int caret, bool line_end)
+{
+ unsigned int c_len;
+ unsigned int b_off;
+ int i;
+ int index;
+ int x, y;
+ int x0, y0, x1, y1;
+ int height;
+
+
+ if (ta->flags & TEXTAREA_READONLY)
+ return false;
+
+ ta->redraw_start_callback(ta->data);
+
+ c_len = ta->text_utf8_len;
+
+ if (caret != -1 && (unsigned)caret > c_len)
+ caret = c_len;
+
+ height = css_len2px(&(ta->style->font_size.value.length),
+ ta->style);
+ /* Delete the old caret */
+ if (ta->caret_pos.char_off != -1) {
+ index = textarea_get_caret(ta);
+ if (index == -1)
+ return false;
+
+ /*the redraw might happen in response to a text-change and
+ the caret position might be beyond the current text */
+ if ((unsigned)index > c_len)
+ index = c_len;
+
+ /* 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->style,
+ ta->text +
+ ta->lines[ta->caret_pos.line].b_start,
+ b_off - ta->lines[ta->caret_pos.line].b_start,
+ &x);
+
+ x += ta->x + MARGIN_LEFT - ta->scroll_x;
+
+ y = ta->line_height * ta->caret_pos.line + ta->y - ta->scroll_y;
+
+ textarea_redraw(ta, x - 1, y - 1, x + 1, y + height + 1);
+ }
+
+ /*check if the caret has to be drawn at all*/
+ if (caret != -1) {
+ /* Find byte offset of caret position */
+ for (b_off = 0; caret > 0; caret--)
+ b_off = utf8_next(ta->text, ta->text_len, b_off);
+
+ /* Now find line in which byte offset appears */
+ for (i = 0; i < ta->line_count - 1; i++)
+ if (ta->lines[i + 1].b_start > b_off)
+ break;
+
+ if (line_end && i > 0 && b_off == ta->lines[i].b_start &&
+ ta->text[ta->lines[i].b_start - 1] == ' ') {
+ b_off--;
+ --i;
+ }
+
+ ta->caret_pos.line = i;
+
+ /* Now calculate the char. offset of the caret in this line */
+ for (c_len = 0, ta->caret_pos.char_off = 0;
+ c_len < b_off - ta->lines[i].b_start;
+ c_len = utf8_next(ta->text +
+ ta->lines[i].b_start,
+ ta->lines[i].b_length, c_len))
+ ta->caret_pos.char_off++;
+
+ if (textarea_scroll_visible(ta))
+ textarea_redraw(ta, ta->x, ta->y, ta->x + ta->vis_width,
+ ta->y + ta->vis_height);
+
+ /* Finally, redraw the caret */
+ index = textarea_get_caret(ta);
+ if (index == -1)
+ return false;
+
+ /* 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->style,
+ ta->text +
+ ta->lines[ta->caret_pos.line].b_start,
+ b_off - ta->lines[ta->caret_pos.line].b_start,
+ &x);
+
+ x += ta->x + MARGIN_LEFT - ta->scroll_x;
+
+ y = ta->line_height * ta->caret_pos.line + ta->y - ta->scroll_y;
+
+ x0 = max(x - 1, ta->x + MARGIN_LEFT);
+ y0 = max(y - 1, ta->y);
+ x1 = min(x + 1, ta->x + ta->vis_width - MARGIN_RIGHT);
+ y1 = min(y + height + 1, ta->y + ta->vis_height);
+
+ plot.clip(x0, y0, x1, y1);
+ plot.line(x, y, x, y + height, 1, CARET_COLOR, false, false);
+ }
+ ta->redraw_end_callback(ta->data);
+
+ 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
+ * \param line_end will be updated to true if the caret should be placed at
+ * the end of a soft wrapped line false otherwise
+ * \return character offset
+ */
+unsigned int textarea_get_xy_offset(struct text_area *ta, int x, int y,
+ bool *line_end)
+{
+ size_t b_off, temp;
+ unsigned int c_off;
+ int line;
+
+ if (!ta->line_count)
+ return 0;
+
+ x = x - ta->x - MARGIN_LEFT + ta->scroll_x;
+ y = y - ta->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->style,
+ ta->text + ta->lines[line].b_start,
+ ta->lines[line].b_length, x, &b_off, &x);
+
+ *line_end = false;
+ /* If the calculated byte offset corresponds with the number of bytes
+ * in the line, and the line has been soft-wrapped, then check line_end
+ * true to ensure the caret offset will be set 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] == ' ')
+ *line_end = true;
+
+ 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 text_area *ta, int x, int y)
+{
+ unsigned int c_off;
+ bool line_end;
+
+ if (ta->flags & TEXTAREA_READONLY)
+ return false;
+
+ c_off = textarea_get_xy_offset(ta, x, y, &line_end);
+ return textarea_set_caret(ta, c_off, line_end);
+}
+
+
+/**
+ * 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 text_area *ta)
+{
+ unsigned int c_off = 0, b_off;
+
+
+ /* if the text is a \0 only*/
+ if (ta->text_len == 1)
+ 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 text_area *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->style, 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;
+}
+
+/**
+ * Handle redraw requests for text areas
+ *
+ * \param redraw Redraw request block
+ */
+void textarea_redraw(struct text_area *ta, int x0, int y0, int x1, int y1)
+{
+ int line0, line1, line;
+ int chars, offset;
+ unsigned int c_pos, c_len, b_start, b_end, line_len;
+ char *line_text;
+
+
+ if (x1 < ta->x || x0 > ta->x + ta->vis_width || y1 < ta->y ||
+ y0 > ta->y + ta->vis_height)
+ /* Textarea outside the clipping rectangle */
+ return;
+
+ if (ta->lines == NULL)
+ /* Nothing to redraw */
+ return;
+
+ line0 = (y0 - ta->y + ta->scroll_y) / ta->line_height - 1;
+ line1 = (y1 - ta->y + ta->scroll_y) / ta->line_height + 1;
+
+ if (line0 < 0)
+ line0 = 0;
+ if (line1 < 0)
+ line1 = 0;
+ if (ta->line_count - 1 < line0)
+ line0 = ta->line_count - 1;
+ if (ta->line_count - 1 < line1)
+ line1 = ta->line_count - 1;
+ if (line1 < line0)
+ line1 = line0;
+
+ if (x0 < ta->x)
+ x0 = ta->x;
+ if (y0 < ta->y)
+ y0 = ta->y;
+ if (x1 > ta->x + ta->vis_width)
+ x1 = ta->x + ta->vis_width;
+ if (y1 > ta->y + ta->vis_height)
+ y1 = ta->y + ta->vis_height;
+
+ plot.clip(x0, y0, x1, y1);
+ plot.fill(x0, y0, x1, y1, (ta->flags & TEXTAREA_READONLY) ?
+ READONLY_BG : BACKGROUND_COL);
+ plot.rectangle(ta->x, ta->y, ta->vis_width - 1, ta->vis_height - 1, 1,
+ BORDER_COLOR, false, false);
+
+ if (x0 < ta->x + MARGIN_LEFT)
+ x0 = ta->x + MARGIN_LEFT;
+ if (x1 > ta->x + ta->vis_width - MARGIN_RIGHT)
+ x1 = ta->x + ta->vis_width - MARGIN_RIGHT;
+ plot.clip(x0, y0, x1, y1);
+
+ if (line0 > 0)
+ c_pos = utf8_bounded_length(ta->text,
+ ta->lines[line0].b_start - 1);
+ else
+ c_pos = 0;
+
+ for (line = line0; (line <= line1) &&
+ (ta->y + line * ta->line_height <= y1 + ta->scroll_y);
+ line++) {
+ if (ta->lines[line].b_length == 0)
+ continue;
+
+ c_len = utf8_bounded_length(
+ &(ta->text[ta->lines[line].b_start]),
+ ta->lines[line].b_length);
+
+ /*if there is a newline between the lines count it too*/
+ if (line < ta->line_count - 1 && ta->lines[line + 1].b_start !=
+ ta->lines[line].b_start +
+ ta->lines[line].b_length)
+ c_len++;
+
+ /*check if a part of the line is selected, won't happen if no
+ selection (ta->selection_end = -1)
+ */
+ if (ta->selection_end != -1 &&
+ c_pos < (unsigned)ta->selection_end &&
+ c_pos + c_len > (unsigned)ta->selection_start) {
+
+ /*offset from the beginning of the line*/
+ offset = ta->selection_start - c_pos;
+ chars = ta->selection_end - c_pos -
+ (offset > 0 ? offset:0);
+
+ line_text = &(ta->text[ta->lines[line].b_start]);
+ line_len = ta->lines[line].b_length;
+
+ if (offset > 0) {
+
+ /*find byte start of the selected part*/
+ for (b_start = 0; offset > 0; offset--)
+ b_start = utf8_next(line_text,
+ line_len,
+ b_start);
+ nsfont.font_width(ta->style, line_text,
+ b_start, &x0);
+ x0 += ta->x + MARGIN_LEFT;
+ }
+ else {
+ x0 = ta->x + MARGIN_LEFT;
+ b_start = 0;
+ }
+
+
+ if (chars >= 0) {
+
+ /*find byte end of the selected part*/
+ for (b_end = b_start; chars > 0 &&
+ b_end < line_len;
+ chars--) {
+ b_end = utf8_next(line_text, line_len,
+ b_end);
+ }
+ }
+ else
+ b_end = ta->lines[line].b_length;
+
+ b_end -= b_start;
+ nsfont.font_width(ta->style,
+ &(ta->text[ta->lines[line].b_start +
+ b_start]),
+ b_end, &x1);
+ x1 += x0;
+ plot.fill(x0 - ta->scroll_x, ta->y +
+ line * ta->line_height
+ + 1 - ta->scroll_y,
+ x1 - ta->scroll_x,
+ ta->y + (line + 1) * ta->line_height -
+ 1 - ta->scroll_y,
+ SELECTION_COL);
+
+ }
+
+ c_pos += c_len;
+
+ y0 = ta->y + line * ta->line_height + 0.75 * ta->line_height;
+
+ plot.text(ta->x + MARGIN_LEFT - ta->scroll_x, y0 - ta->scroll_y,
+ ta->style,
+ ta->text + ta->lines[line].b_start,
+ ta->lines[line].b_length,
+ (ta->flags & TEXTAREA_READONLY) ?
+ READONLY_BG : BACKGROUND_COL,
+ ta->style->color);
+ }
+}
+
+/**
+ * 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 text_area *ta, uint32_t key)
+{
+ char utf8[6];
+ unsigned int caret, caret_init, length, l_len, b_off, b_len;
+ int c_line, c_chars, line;
+ bool redraw = false;
+ bool readonly;
+ bool line_end = false;
+
+ caret_init = caret = textarea_get_caret(ta);
+ line = ta->caret_pos.line;
+ readonly = (ta->flags & TEXTAREA_READONLY ? true:false);
+
+
+ if (!(key <= 0x001F || (0x007F <= key && key <= 0x009F))) {
+ /* normal character insertion */
+ length = utf8_from_ucs4(key, utf8);
+ utf8[length] = '\0';
+
+ if (!textarea_insert_text(ta, caret, utf8))
+ return false;
+ caret++;
+ redraw = true;
+
+ } else switch (key) {
+ case KEY_SELECT_ALL:
+ caret = ta->text_utf8_len;
+
+ ta->selection_start = 0;
+ ta->selection_end = ta->text_utf8_len;
+ redraw = true;
+ break;
+ case KEY_COPY_SELECTION:
+ break;
+ case KEY_DELETE_LEFT:
+ if (readonly)
+ break;
+ if (ta->selection_start != -1) {
+ if (!textarea_replace_text(ta,
+ ta->selection_start,
+ ta->selection_end, ""))
+ return false;
+ ta->selection_start = ta->selection_end = -1;
+ redraw = true;
+ } else {
+ if (caret) {
+ if (!textarea_replace_text(ta,
+ caret - 1,
+ caret, ""))
+ return false;
+ caret--;
+ redraw = true;
+ }
+ }
+ break;
+ break;
+ case KEY_NL:
+ if (readonly)
+ break;
+ if(!textarea_insert_text(ta, caret, "\n"))
+ return false;
+ caret++;
+ ta->selection_start = ta->selection_end = -1;
+ redraw = true;
+ break;
+ case KEY_CUT_LINE:
+ case KEY_PASTE:
+ case KEY_CUT_SELECTION:
+ break;
+ case KEY_ESCAPE:
+ case KEY_CLEAR_SELECTION:
+ ta->selection_start = -1;
+ ta->selection_end = -1;
+ redraw = true;
+ break;
+ case KEY_LEFT:
+ if (readonly)
+ break;
+ if (caret)
+ caret--;
+ if (ta->selection_start != -1) {
+ ta->selection_start = ta->selection_end = -1;
+ redraw = true;
+ }
+ break;
+ case KEY_RIGHT:
+ if (readonly)
+ break;
+ if (caret < ta->text_utf8_len)
+ caret++;
+ if (ta->selection_start != -1) {
+ ta->selection_start = ta->selection_end = -1;
+ redraw = true;
+ }
+ break;
+ case KEY_PAGE_UP:
+ if (readonly)
+ break;
+ if (ta->flags & TEXTAREA_MULTILINE) {
+ /* +1 because one line is subtracted in KEY_UP*/
+ line = ta->caret_pos.line - (ta->vis_height +
+ ta->line_height - 1) /
+ ta->line_height
+ + 1;
+ }
+ /*fall through*/
+ case KEY_UP:
+ if (readonly)
+ break;
+ if (ta->selection_start != -1) {
+ ta->selection_start = ta->selection_end = -1;
+ redraw = true;
+ }
+ if (ta->flags & TEXTAREA_MULTILINE) {
+ line--;
+ if (line < 0)
+ line = 0;
+ if (line == ta->caret_pos.line)
+ break;
+ c_line = ta->caret_pos.line;
+ c_chars = ta->caret_pos.char_off;
+
+ if (c_chars != 0)
+ line_end = true;
+
+ ta->caret_pos.line = line;
+
+ b_off = ta->lines[ta->caret_pos.line].b_start;
+ b_len = ta->lines[ta->caret_pos.line].b_length;
+ l_len = utf8_bounded_length(&(ta->text[b_off]),
+ b_len);
+
+ ta->caret_pos.char_off = min(l_len,
+ (unsigned)
+ ta->caret_pos.char_off);
+ caret = textarea_get_caret(ta);
+
+ ta->caret_pos.line = c_line;
+ ta->caret_pos.char_off = c_chars;
+ }
+ break;
+ case KEY_PAGE_DOWN:
+ if (readonly)
+ break;
+ if (ta->flags & TEXTAREA_MULTILINE) {
+ /* -1 because one line is added in KEY_DOWN*/
+ line = ta->caret_pos.line + (ta->vis_height +
+ ta->line_height - 1) /
+ ta->line_height
+ - 1;
+ }
+ /* fall through*/
+ case KEY_DOWN:
+ if (readonly)
+ break;
+ if (ta->selection_start != -1) {
+ ta->selection_start = ta->selection_end = -1;
+ redraw = true;
+ }
+ if (ta->flags & TEXTAREA_MULTILINE) {
+ line++;
+ if (line > ta->line_count - 1)
+ line = ta->line_count - 1;
+ if (line == ta->caret_pos.line)
+ break;
+ c_line = ta->caret_pos.line;
+ c_chars = ta->caret_pos.char_off;
+
+ if (c_chars != 0)
+ line_end = true;
+
+ ta->caret_pos.line = line;
+ b_off = ta->lines[ta->caret_pos.line].b_start;
+ b_len = ta->lines[ta->caret_pos.line].b_length;
+ l_len = utf8_bounded_length(&(ta->text[b_off]),
+ b_len);
+
+ ta->caret_pos.char_off = min(l_len,
+ (unsigned)
+ ta->caret_pos.char_off);
+ caret = textarea_get_caret(ta);
+
+ ta->caret_pos.line = c_line;
+ ta->caret_pos.char_off = c_chars;
+ }
+ break;
+ case KEY_DELETE_RIGHT:
+ if (readonly)
+ break;
+ if (ta->selection_start != -1) {
+ if (!textarea_replace_text(ta,
+ ta->selection_start,
+ ta->selection_end, ""))
+ return false;
+
+ ta->selection_start = ta->selection_end = -1;
+ redraw = true;
+ } else {
+ if (caret < ta->text_utf8_len) {
+ if (!textarea_replace_text(ta, caret,
+ caret + 1, ""))
+ return false;
+ redraw = true;
+ }
+ }
+ break;
+ case KEY_LINE_START:
+ if (readonly)
+ break;
+ caret -= ta->caret_pos.char_off;
+ if (ta->selection_start != -1) {
+ ta->selection_start = ta->selection_end = -1;
+ redraw = true;
+ }
+ break;
+ case KEY_LINE_END:
+ if (readonly)
+ break;
+
+ line_end = true;
+
+ caret = utf8_bounded_length(ta->text,
+ ta->lines[ta->caret_pos.line].b_start +
+ ta->lines[ta->caret_pos.line].b_length);
+ if (ta->text[ta->lines[ta->caret_pos.line].b_start +
+ ta->lines[ta->caret_pos.line].b_length
+ - 1] == ' ')
+ caret--;
+ if (ta->selection_start != -1) {
+ ta->selection_start = ta->selection_end = -1;
+ redraw = true;
+ }
+ break;
+ case KEY_TEXT_START:
+ if (readonly)
+ break;
+ caret = 0;
+ if (ta->selection_start != -1) {
+ ta->selection_start = ta->selection_end = -1;
+ redraw = true;
+ }
+ break;
+ case KEY_TEXT_END:
+ if (readonly)
+ break;
+ caret = ta->text_utf8_len;
+ if (ta->selection_start != -1) {
+ ta->selection_start = ta->selection_end = -1;
+ redraw = true;
+ }
+ break;
+ case KEY_WORD_LEFT:
+ case KEY_WORD_RIGHT:
+ break;
+ case KEY_DELETE_LINE_END:
+ if (readonly)
+ break;
+ if (ta->selection_start != -1) {
+ if (!textarea_replace_text(ta,
+ ta->selection_start,
+ ta->selection_end, ""))
+ return false;
+ ta->selection_start = ta->selection_end = -1;
+ } else {
+ b_off = ta->lines[ta->caret_pos.line].b_start;
+ b_len = ta->lines[ta->caret_pos.line].b_length;
+ l_len = utf8_bounded_length(&(ta->text[b_off]),
+ b_len);
+ if (!textarea_replace_text(ta, caret,
+ caret + l_len, ""))
+ return false;
+ }
+ redraw = true;
+ break;
+ case KEY_DELETE_LINE_START:
+ if (readonly)
+ break;
+ if (ta->selection_start != -1) {
+ if (!textarea_replace_text(ta,
+ ta->selection_start,
+ ta->selection_end, ""))
+ return false;
+ ta->selection_start = ta->selection_end = -1;
+ } else {
+ if (textarea_replace_text(ta,
+ caret - ta->caret_pos.char_off,
+ caret,
+ ""))
+ return false;
+ caret -= ta->caret_pos.char_off;
+ }
+ redraw = true;
+ break;
+ default:
+ return false;
+ }
+
+ //TODO:redraw only the important part
+ if (redraw) {
+ ta->redraw_start_callback(ta->data);
+ textarea_redraw(ta, ta->x, ta->y, ta->x + ta->vis_width,
+ ta->y + ta->vis_height);
+ ta->redraw_end_callback(ta->data);
+ }
+
+ if (caret != caret_init || redraw)
+ return textarea_set_caret(ta, caret, line_end);
+
+ 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 text_area *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 = ta->x + MARGIN_LEFT;
+ x1 = ta->x + ta->vis_width - MARGIN_RIGHT;
+ y0 = ta->y;
+ y1 = ta->y + 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->style,
+ 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 += ta->x + MARGIN_LEFT - ta->scroll_x;
+ y = ta->line_height * ta->caret_pos.line + ta->y - 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;
+}
+
+
+/**
+ * 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 text_area *ta, browser_mouse_state mouse,
+ int x, int y)
+{
+ int c_start, c_end;
+ bool line_end;
+
+ /* mouse button pressed above the text area, move caret*/
+ if (mouse & BROWSER_MOUSE_PRESS_1) {
+ if (ta->selection_start != -1) {
+ ta->selection_start = ta->selection_end = -1;
+ ta->redraw_start_callback(ta->data);
+ textarea_redraw(ta, ta->x, ta->y, ta->x + ta->vis_width,
+ ta->y + ta->vis_height);
+ ta->redraw_end_callback(ta->data);
+ }
+ if (!(ta->flags & TEXTAREA_READONLY))
+ return textarea_set_caret_xy(ta, x, y);
+ }
+ else if (mouse & BROWSER_MOUSE_DRAG_1) {
+ ta->drag_start_char = textarea_get_xy_offset(ta, x, y,
+ &line_end);
+ if (!(ta->flags & TEXTAREA_READONLY))
+ return textarea_set_caret(ta, -1, line_end);
+ }
+ else if (mouse & BROWSER_MOUSE_HOLDING_1) {
+ c_start = ta->drag_start_char;
+ c_end = textarea_get_xy_offset(ta, x, y, &line_end);
+ return textarea_select(ta, c_start, c_end, line_end);
+
+ }
+
+ return true;
+}
+
+
+/**
+ * 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 text_area *ta, browser_mouse_state mouse,
+ int x, int y)
+{
+ int c_end;
+ bool line_end;
+
+ c_end = textarea_get_xy_offset(ta, x, y, &line_end);
+ return textarea_select(ta, ta->drag_start_char, c_end, line_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)
+ * \param line_end true if the selection ends at the end of a soft wrapped
+ * line
+ * \return true on success false otherwise
+ */
+bool textarea_select(struct text_area *ta, int c_start, int c_end,
+ bool line_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;
+
+ ta->redraw_start_callback(ta->data);
+ textarea_redraw(ta, ta->x, ta->y, ta->x + ta->vis_width,
+ ta->y + ta->vis_height);
+ ta->redraw_end_callback(ta->data);
+
+ if (!(ta->flags & TEXTAREA_READONLY)) {
+ if (swap == -1)
+ return textarea_set_caret(ta, c_end, line_end);
+ else
+ return textarea_set_caret(ta, c_start, line_end);
+ }
+
+ return true;
+}
+
+
+/**
+ * Removes any CR characters and changes newlines into spaces if it is a single
+ * line textarea.
+ *
+ * \param ta Text area
+ * \param b_start Byte offset to start at
+ * \param b_len Byte length to check
+ */
+void textarea_normalise_text(struct text_area *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 CRNL pair delete it ot replace it
+ * with NL 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] = ' ';
+ }
+
+}
Index: desktop/textarea.h
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ desktop/textarea.h 2009-06-24 17:28:49.000000000 +0100
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2006 John-Mark Bell <jmb(a)netsurf-browser.org>
+ * Copyright 2009 Paul Blokus <paul_pl(a)users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Single/Multi-line UTF-8 text area (interface)
+ */
+
+#ifndef _NETSURF_DESKTOP_TEXTAREA_H_
+#define _NETSURF_DESKTOP_TEXTAREA_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "css/css.h"
+#include "desktop/browser.h"
+
+/* Text area flags */
+#define TEXTAREA_MULTILINE 0x01 /**< Text area is multiline */
+#define TEXTAREA_READONLY 0x02 /**< Text area is read only */
+
+struct text_area;
+
+typedef void(*textarea_start_radraw_callback)(void *data);
+typedef void(*textarea_end_radraw_callback)(void *data);
+
+struct text_area *textarea_create(int x, int y, int width, int height,
+ unsigned int flags, const struct css_style *style,
+ textarea_start_radraw_callback redraw_start_callback,
+ textarea_end_radraw_callback redraw_end_callback, 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, bool line_end);
+int textarea_get_caret(struct text_area *ta);
+void textarea_redraw(struct text_area *ta, int x0, int y0, int x1, int y1);
+bool textarea_keypress(struct text_area *ta, uint32_t key);
+bool textarea_mouse_action(struct text_area *ta, browser_mouse_state mouse,
+ int x, int y);
+bool textarea_drag_end(struct text_area *ta, browser_mouse_state mouse,
+ int x, int y);
+
+#endif
+
Changed files
Makefile.sources | 2 -
desktop/browser.h | 17 +++++++-----
desktop/textinput.h | 1
gtk/font_pango.c | 1
gtk/gtk_gui.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++
gtk/gtk_gui.h | 3 ++
gtk/gtk_plotters.c | 2 -
gtk/gtk_window.c | 43 +------------------------------
8 files changed, 91 insertions(+), 50 deletions(-)
Index: gtk/gtk_gui.h
===================================================================
--- gtk/gtk_gui.h (revision 7935)
+++ gtk/gtk_gui.h (working copy)
@@ -19,6 +19,7 @@
#ifndef GTK_GUI_H
#define GTK_GUI_H
+#include <inttypes.h>
#include <stdbool.h>
#include <gtk/gtk.h>
#include <glade/glade.h>
@@ -37,5 +38,7 @@
extern GtkDialog *wndOpenFile;
+uint32_t gtk_gui_gdkkey_to_nskey(GdkEventKey *);
+
#endif /* GTK_GUI_H */
Index: gtk/gtk_window.c
===================================================================
--- gtk/gtk_window.c (revision 7935)
+++ gtk/gtk_window.c (working copy)
@@ -38,7 +38,7 @@
struct gui_window *window_list = 0; /**< first entry in win list*/
int temp_open_background = -1;
-static uint32_t gdkkey_to_nskey(GdkEventKey *);
+
static void nsgtk_gui_window_attach_child(struct gui_window *parent,
struct gui_window *child);
/* Methods which apply only to a gui_window */
@@ -449,50 +449,11 @@
return TRUE;
}
-uint32_t gdkkey_to_nskey(GdkEventKey *key)
-{
- /* this function will need to become much more complex to support
- * everything that the RISC OS version does. But this will do for
- * now. I hope.
- */
-
- switch (key->keyval)
- {
- case GDK_BackSpace: return KEY_DELETE_LEFT;
- case GDK_Delete: return KEY_DELETE_RIGHT;
- case GDK_Linefeed: return 13;
- case GDK_Return: return 10;
- case GDK_Left: return KEY_LEFT;
- case GDK_Right: return KEY_RIGHT;
- case GDK_Up: return KEY_UP;
- case GDK_Down: return KEY_DOWN;
-
- /* Modifiers - do nothing for now */
- case GDK_Shift_L:
- case GDK_Shift_R:
- case GDK_Control_L:
- case GDK_Control_R:
- case GDK_Caps_Lock:
- case GDK_Shift_Lock:
- case GDK_Meta_L:
- case GDK_Meta_R:
- case GDK_Alt_L:
- case GDK_Alt_R:
- case GDK_Super_L:
- case GDK_Super_R:
- case GDK_Hyper_L:
- case GDK_Hyper_R: return 0;
-
- default: return gdk_keyval_to_unicode(
- key->keyval);
- }
-}
-
gboolean nsgtk_window_keypress_event(GtkWidget *widget, GdkEventKey *event,
gpointer data)
{
struct gui_window *g = data;
- uint32_t nskey = gdkkey_to_nskey(event);
+ uint32_t nskey = gtk_gui_gdkkey_to_nskey(event);
if (browser_window_key_press(g->bw, nskey))
return TRUE;
Index: gtk/gtk_plotters.c
===================================================================
--- gtk/gtk_plotters.c (revision 7935)
+++ gtk/gtk_plotters.c (working copy)
@@ -119,7 +119,7 @@
line_width = 1;
cairo_set_line_width(current_cr, line_width);
- cairo_rectangle(current_cr, x0, y0, width, height);
+ cairo_rectangle(current_cr, x0 + 0.5, y0 + 0.5, width, height);
cairo_stroke(current_cr);
return true;
Index: gtk/font_pango.c
===================================================================
--- gtk/font_pango.c (revision 7935)
+++ gtk/font_pango.c (working copy)
@@ -183,6 +183,7 @@
pango_layout_set_width(layout, x * PANGO_SCALE);
pango_layout_set_wrap(layout, PANGO_WRAP_WORD);
+ pango_layout_set_single_paragraph_mode(layout, true);
line = pango_layout_get_line(layout, 1);
if (line)
index = line->start_index - 1;
Index: gtk/gtk_gui.c
===================================================================
--- gtk/gtk_gui.c (revision 7935)
+++ gtk/gtk_gui.c (working copy)
@@ -43,6 +43,7 @@
#include "desktop/netsurf.h"
#include "desktop/options.h"
#include "desktop/save_pdf/pdf_plotters.h"
+#include "desktop/textinput.h"
#include "gtk/gtk_gui.h"
#include "gtk/dialogs/gtk_options.h"
#include "gtk/gtk_completion.h"
@@ -734,3 +735,74 @@
}
#endif
+uint32_t gtk_gui_gdkkey_to_nskey(GdkEventKey *key)
+{
+ /* this function will need to become much more complex to support
+ * everything that the RISC OS version does. But this will do for
+ * now. I hope.
+ */
+ switch (key->keyval)
+ {
+ case GDK_BackSpace:
+ if (key->state & GDK_SHIFT_MASK)
+ return KEY_DELETE_LINE_START;
+ else
+ return KEY_DELETE_LEFT;
+ case GDK_Delete:
+ if (key->state & GDK_SHIFT_MASK)
+ return KEY_DELETE_LINE_END;
+ else
+ return KEY_DELETE_RIGHT;
+ case GDK_Linefeed: return 13;
+ case GDK_Return: return 10;
+ case GDK_Left: return KEY_LEFT;
+ case GDK_Right: return KEY_RIGHT;
+ case GDK_Up: return KEY_UP;
+ case GDK_Down: return KEY_DOWN;
+ case GDK_Home:
+ if (key->state & GDK_CONTROL_MASK)
+ return KEY_TEXT_START;
+ else
+ return KEY_LINE_START;
+ case GDK_End:
+ if (key->state & GDK_CONTROL_MASK)
+ return KEY_TEXT_END;
+ else
+ return KEY_LINE_END;
+ case GDK_Page_Up:
+ return KEY_PAGE_UP;
+ case GDK_Page_Down:
+ return KEY_PAGE_DOWN;
+ case 'a':
+ if (key->state & GDK_CONTROL_MASK)
+ return KEY_SELECT_ALL;
+ return gdk_keyval_to_unicode(
+ key->keyval);
+ case 'u':
+ if (key->state & GDK_CONTROL_MASK)
+ return KEY_CLEAR_SELECTION;
+ return gdk_keyval_to_unicode(
+ key->keyval);
+ case GDK_Escape:
+ return KEY_ESCAPE;
+
+ /* Modifiers - do nothing for now */
+ case GDK_Shift_L:
+ case GDK_Shift_R:
+ case GDK_Control_L:
+ case GDK_Control_R:
+ case GDK_Caps_Lock:
+ case GDK_Shift_Lock:
+ case GDK_Meta_L:
+ case GDK_Meta_R:
+ case GDK_Alt_L:
+ case GDK_Alt_R:
+ case GDK_Super_L:
+ case GDK_Super_R:
+ case GDK_Hyper_L:
+ case GDK_Hyper_R: return 0;
+
+ default: return gdk_keyval_to_unicode(
+ key->keyval);
+ }
+}
Index: Makefile.sources
===================================================================
--- Makefile.sources (revision 7935)
+++ Makefile.sources (working copy)
@@ -13,7 +13,7 @@
layout.c list.c loosen.c table.c textplain.c
S_UTILS := base64.c filename.c hashtable.c locale.c messages.c talloc.c \
url.c utf8.c utils.c useragent.c
-S_DESKTOP := knockout.c options.c print.c tree.c version.c
+S_DESKTOP := knockout.c options.c print.c tree.c version.c textarea.c
# S_COMMON are sources common to all builds
S_COMMON := $(addprefix content/,$(S_CONTENT)) \
Index: desktop/textinput.h
===================================================================
--- desktop/textinput.h (revision 7935)
+++ desktop/textinput.h (working copy)
@@ -28,6 +28,7 @@
#include <stdbool.h>
+
struct browser_window;
struct box;
Index: desktop/browser.h
===================================================================
--- desktop/browser.h (revision 7935)
+++ desktop/browser.h (working copy)
@@ -191,21 +191,24 @@
* a drag. */
BROWSER_MOUSE_CLICK_1 = 4, /* button 1 clicked. */
BROWSER_MOUSE_CLICK_2 = 8, /* button 2 clicked. */
+ BROWSER_MOUSE_DOUBLE_CLICK = 16, /* button 1 double clicked */
- BROWSER_MOUSE_DRAG_1 = 16, /* start of button 1 drag operation */
- BROWSER_MOUSE_DRAG_2 = 32, /* start of button 2 drag operation */
+ BROWSER_MOUSE_DRAG_1 = 32, /* start of button 1 drag operation */
+ BROWSER_MOUSE_DRAG_2 = 64, /* start of button 2 drag operation */
- BROWSER_MOUSE_DRAG_ON = 64, /* a drag operation was started and
+ BROWSER_MOUSE_DRAG_ON = 128, /* a drag operation was started and
* a mouse button is still pressed */
- BROWSER_MOUSE_HOLDING_1 = 128, /* while button 1 drag is in progress */
- BROWSER_MOUSE_HOLDING_2 = 256, /* while button 2 drag is in progress */
+ BROWSER_MOUSE_HOLDING_1 = 256, /* while button 1 drag is in progress */
+ BROWSER_MOUSE_HOLDING_2 = 512, /* while button 2 drag is in progress */
- BROWSER_MOUSE_MOD_1 = 512, /* primary modifier key pressed
+ BROWSER_MOUSE_MOD_1 = 1024, /* primary modifier key pressed
* (eg. Shift) */
- BROWSER_MOUSE_MOD_2 = 1024 /* secondary modifier key pressed
+ BROWSER_MOUSE_MOD_2 = 2048, /* secondary modifier key pressed
* (eg. Ctrl) */
+ BROWSER_MOUSE_MOD_3 = 4096 /* secondary modifier key pressed
+ * (eg. Alt) */
} browser_mouse_state;
Conflicted files
Removed files
14 years, 3 months
Review: Paul Blokus -- Core text area
by John-Mark Bell
Precis:
This is Paul Blokus' port of the RISC OS text area to the core codebase.
It provides single and multi-line text editing capabilities in a
platform-agnostic manner.
Added files
Index: desktop/textarea.c
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ desktop/textarea.c 2009-06-23 11:58:18.000000000 +0100
@@ -0,0 +1,1258 @@
+/*
+ * Copyright 2006 John-Mark Bell <jmb202(a)ecs.soton.ac.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Single/Multi-line UTF-8 text area (implementation)
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include "css/css.h"
+#include "desktop/textarea.h"
+#include "desktop/textinput.h"
+#include "desktop/plotters.h"
+#include "render/font.h"
+#include "utils/log.h"
+#include "utils/utf8.h"
+#include "utils/utils.h"
+
+#define MARGIN_LEFT 2
+#define MARGIN_RIGHT 2
+
+struct line_info {
+ int b_start; /**< Byte offset of line start */
+ int b_length; /**< Byte length of line */
+};
+
+struct text_area {
+
+ int x, y; /**< Coordinates of the widget
+ (top left corner) */
+
+ int scroll_x, scroll_y;
+
+ unsigned int flags; /**< Textarea flags */
+ int vis_width; /**< Visible width, in pixels */
+ int vis_height; /**< Visible height, in pixels */
+
+ char *text; /**< UTF-8 text */
+ unsigned int text_alloc; /**< Size of allocated text */
+ unsigned int text_len; /**< Length of text, in bytes */
+ struct {
+ int line; /**< Line caret is on */
+ int char_off; /**< Character index of caret */
+ } caret_pos;
+
+ int selection_start; /**< Character index of sel start(inclusive) */
+ int selection_end; /**< Character index of sel end(exclusive) */
+
+ struct css_style *style; /**< Text style */
+
+ int line_count; /**< Count of lines */
+#define LINE_CHUNK_SIZE 256
+ struct line_info *lines; /**< Line info array */
+
+ /** Callback functions for a redraw request*/
+ textarea_start_radraw_callback redraw_start_callback;
+ textarea_start_radraw_callback redraw_end_callback;
+
+ intptr_t data; /** < Callback data for both callback functions*/
+
+ int drag_start_char; /**< Character index at which the drag was started*/
+};
+
+
+static void textarea_insert_text(struct text_area *ta, unsigned int index,
+ const char *text);
+static void textarea_replace_text(struct text_area *ta, unsigned int start,
+ unsigned int end, const char *text);
+static void textarea_reflow(struct text_area *ta, unsigned int line);
+static int textarea_get_xy_offset(struct text_area *ta, int x, int y);
+static void textarea_set_caret_xy(struct text_area *ta, int x, int y);
+static bool textarea_scroll_visible(struct text_area *ta);
+static void textarea_select(struct text_area *ta, int c_start, int c_end);
+static void textarea_normalise_text(struct text_area *ta,
+ unsigned int b_start, unsigned int b_len);
+// static bool textarea_mouse_click(wimp_pointer *pointer);
+
+
+/**
+ * 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 font_style Font style to use, or 0 for default
+ * \return Opaque handle for textarea or 0 on error
+ */
+
+struct text_area *textarea_create(int x, int y, int width, int height,
+ unsigned int flags, const struct css_style *style,
+ textarea_start_radraw_callback redraw_start_callback,
+ textarea_end_radraw_callback redraw_end_callback, intptr_t data)
+{
+ struct text_area *ret;
+
+ if (!redraw_start_callback || !redraw_end_callback) {
+ LOG(("no callback provided"));
+ return 0;
+ }
+
+ ret = malloc(sizeof(struct text_area));
+ if (!ret) {
+ LOG(("malloc failed"));
+ return 0;
+ }
+
+ ret->redraw_start_callback = redraw_start_callback;
+ ret->redraw_end_callback = redraw_end_callback;
+ ret->data = data;
+ ret->x = x;
+ ret->y = y;
+ 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) {
+ LOG(("malloc failed"));
+ free(ret);
+ return 0;
+ }
+ ret->text[0] = '\0';
+ ret->text_alloc = 64;
+ ret->text_len = 1;
+
+ ret->style = malloc(sizeof(struct css_style));
+ if (!ret->style) {
+ LOG(("malloc failed"));
+ free(ret->text);
+ free(ret);
+ return 0;
+ }
+ memcpy(ret->style, style, sizeof(struct css_style));
+
+ ret->caret_pos.line = ret->caret_pos.char_off = 0;
+ ret->selection_start = -1;
+ ret->selection_end = -1;
+
+ ret->line_count = 0;
+ ret->lines = 0;
+
+ return (struct text_area *)ret;
+}
+
+
+/**
+ * Destroy a text area
+ *
+ * \param ta Text area to destroy
+ */
+void textarea_destroy(struct text_area *ta)
+{
+ free(ta->text);
+ free(ta->style);
+ free(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 text_area *ta, const char *text)
+{
+ unsigned int len = strlen(text) + 1;
+
+ if (len >= ta->text_alloc) {
+ char *temp = realloc(ta->text, len + 64);
+ if (!temp) {
+ LOG(("realloc failed"));
+ return false;
+ }
+ ta->text = temp;
+ ta->text_alloc = len + 64;
+ }
+
+ memcpy(ta->text, text, len);
+ ta->text_len = len;
+
+ textarea_normalise_text(ta, 0, len);
+
+ textarea_reflow(ta, 0);
+
+ return true;
+}
+
+
+/**
+ * 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 text_area *ta, char *buf, unsigned int len)
+{
+ if (buf == NULL && len == 0) {
+ /* want length */
+ return ta->text_len;
+ }
+
+ if (len < ta->text_len) {
+ LOG(("buffer too small"));
+ return -1;
+ }
+
+ memcpy(buf, ta->text, ta->text_len);
+
+ return ta->text_len;
+}
+
+
+/**
+ * Insert text into the text area
+ *
+ * \param ta Text area
+ * \param index 0-based character index to insert at
+ * \param text UTF-8 text to insert
+ */
+void textarea_insert_text(struct text_area *ta, unsigned int index,
+ const char *text)
+{
+ unsigned int b_len = strlen(text);
+ size_t b_off, c_len;
+
+ if (ta-> flags & TEXTAREA_READONLY)
+ return;
+
+ c_len = utf8_length(ta->text);
+
+ /* Find insertion point */
+ if (index > c_len)
+ index = c_len;
+
+ LOG(("inserting at %i\n", index));
+
+ for (b_off = 0; index-- > 0;
+ b_off = utf8_next(ta->text, ta->text_len, b_off))
+ ; /* do nothing */
+
+ if (b_len + ta->text_len >= ta->text_alloc) {
+ char *temp = realloc(ta->text, b_len + ta->text_len + 64);
+ if (!temp) {
+ LOG(("realloc failed"));
+ return;
+ }
+
+ ta->text = temp;
+ ta->text_alloc = b_len + ta->text_len + 64;
+ }
+
+ /* Shift text following up */
+ memmove(ta->text + b_off + b_len, ta->text + b_off,
+ ta->text_len - b_off);
+ /* Insert new text */
+ memcpy(ta->text + b_off, text, b_len);
+ ta->text_len += b_len;
+
+ textarea_normalise_text(ta, b_off, b_len);
+
+ /** \todo calculate line to reflow from */
+ textarea_reflow(ta, 0);
+}
+
+
+/**
+ * Replace text in a text area
+ *
+ * \param ta Text area
+ * \param start Start character index of replaced section (inclusive)
+ * \param end End character index of replaced section (exclusive)
+ * \param text UTF-8 text to insert
+ */
+void textarea_replace_text(struct text_area *ta, unsigned int start,
+ unsigned int end, const char *text)
+{
+ int b_len = strlen(text);
+ size_t b_start, b_end, c_len, diff;
+
+ if (ta-> flags & TEXTAREA_READONLY)
+ return;
+
+ c_len = utf8_length(ta->text);
+
+ if (start > c_len)
+ start = c_len;
+ if (end > c_len)
+ end = c_len;
+
+ if (start == end)
+ return textarea_insert_text(ta, start, text);
+
+ if (start > end) {
+ int temp = end;
+ end = start;
+ start = temp;
+ }
+
+ diff = end - start;
+
+ for (b_start = 0; start-- > 0;
+ b_start = utf8_next(ta->text, ta->text_len, b_start))
+ ; /* do nothing */
+
+ 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) {
+ LOG(("realloc failed"));
+ return;
+ }
+
+ 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);
+ textarea_normalise_text(ta, b_start, b_len);
+
+ /** \todo calculate line to reflow from */
+ textarea_reflow(ta, 0);
+}
+
+
+/**
+ * Set the caret's position
+ *
+ * \param ta Text area
+ * \param caret 0-based character index to place caret at
+ */
+void textarea_set_caret(struct text_area *ta, int caret)
+{
+ int c_len;
+ int b_off;
+ int i;
+ int index;
+ int x, y;
+ int x0, y0, x1, y1;
+ int height;
+
+
+ if (ta-> flags & TEXTAREA_READONLY)
+ return;
+
+ ta->redraw_start_callback(ta->data);
+
+ c_len = utf8_length(ta->text);
+
+ if (caret > c_len)
+ caret = c_len;
+
+ height = css_len2px(&(ta->style->font_size.value.length),
+ ta->style);
+ /* Delete the old caret */
+ if (ta->caret_pos.char_off != -1) {
+ index = textarea_get_caret(ta);
+
+ /*the redraw might happen in response to a text-change and
+ the caret position might be beyond the current text */
+ if (index > c_len)
+ index = c_len;
+
+ for (b_off = 0; index-- > 0;
+ b_off = utf8_next(ta->text, ta->text_len, b_off))
+ ; /* do nothing */
+
+ nsfont.font_width(ta->style,
+ ta->text + ta->lines[ta->caret_pos.line].b_start,
+ b_off - ta->lines[ta->caret_pos.line].b_start,
+ &x);
+
+ x += ta->x + MARGIN_LEFT - ta->scroll_x;
+
+ y = css_len2px(&(ta->style->line_height.value.length), ta->style) *
+ ta->caret_pos.line + ta->y - ta->scroll_y;
+
+ textarea_redraw(ta, x - 1, y - 1, x + 1, y + height + 1);
+ }
+
+ /*check if the caret has to be drawn at all*/
+ if (caret != -1) {
+ /* Find byte offset of caret position */
+ for (b_off = 0; caret > 0; caret--)
+ b_off = utf8_next(ta->text, ta->text_len, b_off);
+
+ /* Now find line in which byte offset appears */
+ for (i = 0; i < ta->line_count - 1; i++)
+ if (ta->lines[i + 1].b_start > b_off)
+ break;
+
+ ta->caret_pos.line = i;
+
+ /* Now calculate the char. offset of the caret in this line */
+ for (c_len = 0, ta->caret_pos.char_off = 0;
+ c_len < b_off - ta->lines[i].b_start;
+ c_len = utf8_next(ta->text + ta->lines[i].b_start,
+ ta->lines[i].b_length, c_len))
+ ta->caret_pos.char_off++;
+
+ if (textarea_scroll_visible(ta))
+ textarea_redraw(ta, ta->x, ta->y, ta->x + ta->vis_width,
+ ta->y + ta->vis_height);
+
+ /* Finally, redraw the caret */
+ index = textarea_get_caret(ta);
+
+ for (b_off = 0; index-- > 0;
+ b_off = utf8_next(ta->text, ta->text_len, b_off))
+ ; /* do nothing */
+
+ nsfont.font_width(ta->style,
+ ta->text + ta->lines[ta->caret_pos.line].b_start,
+ b_off - ta->lines[ta->caret_pos.line].b_start, &x);
+
+ x += ta->x + MARGIN_LEFT - ta->scroll_x;
+
+ y = css_len2px(&(ta->style->line_height.value.length), ta->style) *
+ ta->caret_pos.line + ta->y - ta->scroll_y;
+
+ x0 = max(x - 1, ta->x + MARGIN_LEFT);
+ y0 = max(y - 1, ta->y);
+ x1 = min(x + 1, ta->x + ta->vis_width - MARGIN_RIGHT);
+ y1 = min(y + height + 1, ta->y + ta->vis_height);
+
+ plot.clip(x0, y0, x1, y1);
+ plot.line(x, y, x, y + height, 1, 0x000000, false, false);
+ }
+ ta->redraw_end_callback(ta->data);
+}
+
+
+/**
+ * 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 int textarea_get_xy_offset(struct text_area *ta, int x, int y)
+{
+ size_t b_off, c_off, temp;
+ int line_height;
+ int line;
+
+ if (!ta->line_count)
+ return 0;
+
+ line_height = css_len2px(&(ta->style->line_height.value.length),
+ ta->style);
+
+ x = x - ta->x - MARGIN_LEFT + ta->scroll_x;
+ y = y - ta->y + ta->scroll_y;
+
+ if (x < 0)
+ x = 0;
+
+ line = y / line_height;
+
+ if (line < 0)
+ line = 0;
+ if (ta->line_count - 1 < line)
+ line = ta->line_count - 1;
+
+ nsfont.font_position_in_string(ta->style,
+ ta->text + ta->lines[line].b_start,
+ ta->lines[line].b_length, x, &b_off, &x);
+
+
+ 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++;
+
+ /* if the offset is a space at the end of the line set the caret before
+ it otherwise the caret will go on the beginning of the next line
+ */
+ if (b_off == (unsigned)ta->lines[line].b_length &&
+ ta->text[ta->lines[line].b_start +
+ ta->lines[line].b_length - 1] == ' ')
+ c_off--;
+
+ return c_off;
+}
+
+
+/**
+ * Set the caret's position
+ *
+ * \param ta Text area
+ * \param x X position of caret in a window
+ * \param y Y position of caret in a window
+ */
+void textarea_set_caret_xy(struct text_area *ta, int x, int y)
+{
+ int c_off;
+
+ if (ta->flags & TEXTAREA_READONLY)
+ return;
+
+ c_off = textarea_get_xy_offset(ta, x, y);
+ textarea_set_caret(ta, c_off);
+}
+
+
+/**
+ * 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 text_area *ta)
+{
+ int c_off = 0, b_off;
+
+ if (ta->text_len == 1)
+ 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
+ */
+void textarea_reflow(struct text_area *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) {
+ ta->lines =
+ malloc(LINE_CHUNK_SIZE * sizeof(struct line_info));
+ if (!ta->lines) {
+ LOG(("malloc failed"));
+ return;
+ }
+ }
+
+ 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;
+ }
+
+ for (len = ta->text_len - 1, text = ta->text; len > 0;
+ len -= b_off, text += b_off) {
+
+ nsfont.font_split(ta->style, 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) {
+ LOG(("realloc failed"));
+ return;
+ }
+
+ 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;
+}
+
+/**
+ * Handle redraw requests for text areas
+ *
+ * \param redraw Redraw request block
+ */
+void textarea_redraw(struct text_area *ta, int x0, int y0, int x1, int y1)
+{
+ int line0, line1, line;
+ int line_height, c_pos, c_len, b_start, b_end, chars, offset;
+
+
+ if (x1 < ta->x || x0 > ta->x + ta->vis_width || y1 < ta->y ||
+ y0 > ta->y + ta->vis_height)
+ /* Textarea outside the clipping rectangle */
+ return;
+
+ if (!ta->lines)
+ /* Nothing to redraw */
+ return;
+
+ line_height = css_len2px(&(ta->style->line_height.value.length),
+ ta->style);
+
+ line0 = (y0 - ta->y + ta->scroll_y) / line_height - 1;
+ line1 = (y1 - ta->y + ta->scroll_y) / line_height + 1;
+
+ if (line0 < 0)
+ line0 = 0;
+ if (line1 < 0)
+ line1 = 0;
+ if (ta->line_count - 1 < line0)
+ line0 = ta->line_count - 1;
+ if (ta->line_count - 1 < line1)
+ line1 = ta->line_count - 1;
+ if (line1 < line0)
+ line1 = line0;
+
+ if (x0 < ta->x)
+ x0 = ta->x;
+ if (y0 < ta->y)
+ y0 = ta->y;
+ if (x1 > ta->x + ta->vis_width)
+ x1 = ta->x + ta->vis_width;
+ if (y1 > ta->y + ta->vis_height)
+ y1 = ta->y + ta->vis_height;
+
+ plot.clip(x0, y0, x1, y1);
+ plot.fill(x0, y0, x1, y1, (ta->flags & TEXTAREA_READONLY) ?
+ 0xD9D9D9 : 0xFFFFFF);
+ plot.rectangle(ta->x, ta->y, ta->vis_width - 1, ta->vis_height - 1, 1,
+ 0x000000, false, false);
+
+ if (x0 < ta->x + MARGIN_LEFT)
+ x0 = ta->x + MARGIN_LEFT;
+ if (x1 > ta->x + ta->vis_width - MARGIN_RIGHT)
+ x1 = ta->x + ta->vis_width - MARGIN_RIGHT;
+ plot.clip(x0, y0, x1, y1);
+
+ if (line0 > 0)
+ c_pos = utf8_bounded_length(ta->text,
+ ta->lines[line0].b_start - 1);
+ else
+ c_pos = 0;
+
+ for (line = line0; (line <= line1) &&
+ (ta->y + line * line_height <= y1 + ta->scroll_y);
+ line++) {
+ if (ta->lines[line].b_length == 0)
+ continue;
+
+ c_len = utf8_bounded_length(
+ &(ta->text[ta->lines[line].b_start]),
+ ta->lines[line].b_length);
+
+ /*if there is a newline between the lines count it too*/
+ if (line < ta->line_count - 1 && ta->lines[line + 1].b_start !=
+ ta->lines[line].b_start + ta->lines[line].b_length)
+ c_len++;
+
+ /*check if a part of the line is selected, won't happen if no
+ selection (ta->selection_end = -1)
+ */
+ if (c_pos < ta->selection_end &&
+ c_pos + c_len > ta->selection_start) {
+
+ /*offset from the beginning of the line*/
+ offset = ta->selection_start - c_pos;
+ chars = ta->selection_end - c_pos -
+ (offset > 0 ? offset:0);
+
+ if (offset > 0) {
+
+ /*find byte start of the selected part*/
+ for (b_start = 0; offset > 0; offset--)
+ b_start = utf8_next(
+ &(ta->text[ta->lines[line].b_start]),
+ ta->lines[line].b_length,
+ b_start);
+ nsfont.font_width(ta->style,
+ &(ta->text[ta->lines[line].b_start]),
+ b_start, &x0);
+ x0 += ta->x + MARGIN_LEFT;
+ }
+ else {
+ x0 = ta->x + MARGIN_LEFT;
+ b_start = 0;
+ }
+
+
+ if (chars >= 0) {
+
+ /*find byte end of the selected part*/
+ for (b_end = b_start; chars > 0 &&
+ b_end < ta->lines[line].b_length;
+ chars--) {
+ b_end = utf8_next(
+ &(ta->text[ta->lines[line].b_start]),
+ ta->lines[line].b_length,
+ b_end);
+ }
+ }
+ else
+ b_end = ta->lines[line].b_length;
+
+ b_end -= b_start;
+ nsfont.font_width(ta->style,
+ &(ta->text[ta->lines[line].b_start + b_start]),
+ b_end, &x1);
+ x1 += x0;
+ plot.fill(x0 - ta->scroll_x, ta->y + line * line_height + 1 - ta->scroll_y,
+ x1 - ta->scroll_x, ta->y + (line + 1) * line_height - 1 - ta->scroll_y,
+ 0xFFDDDD);
+
+ }
+
+ c_pos += c_len;
+
+ y0 = ta->y + line * line_height + 0.75 * line_height;
+
+ plot.text(ta->x + MARGIN_LEFT - ta->scroll_x, y0 - ta->scroll_y, ta->style,
+ ta->text + ta->lines[line].b_start,
+ ta->lines[line].b_length,
+ (ta->flags & TEXTAREA_READONLY) ?
+ 0xD9D9D9 : 0xFFFFFF,
+ 0x000000);
+ }
+}
+
+/**
+ * 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 text_area *ta, uint32_t key)
+{
+ char utf8[6];
+ unsigned int caret, caret_init, length, c_len, l_len;
+ int c_line, c_chars, line, line_height;
+ bool redraw = false;
+
+ caret_init = caret = textarea_get_caret(ta);
+ line = ta->caret_pos.line;
+
+ if (!(key <= 0x001F || (0x007F <= key && key <= 0x009F))) {
+ /* normal character insertion */
+ length = utf8_from_ucs4(key, utf8);
+ utf8[length] = '\0';
+
+ textarea_insert_text(ta, caret, utf8);
+ caret++;
+ redraw = true;
+
+ } else switch (key) {
+ case KEY_SELECT_ALL:
+ c_len = utf8_length(ta->text);
+ caret = c_len;
+
+ ta->selection_start = 0;
+ ta->selection_end = c_len;
+ redraw = true;
+ break;
+ case KEY_COPY_SELECTION:
+ break;
+ case KEY_DELETE_LEFT:
+ if (ta->selection_start != -1) {
+ textarea_replace_text(ta, ta->selection_start,
+ ta->selection_end, "");
+ ta->selection_start = ta->selection_end = -1;
+ redraw = true;
+ } else {
+ if (caret) {
+ textarea_replace_text(ta, caret - 1,
+ caret, "");
+ caret--;
+ redraw = true;
+ }
+ }
+ break;
+ case KEY_TAB:
+ break;
+ case KEY_NL:
+ textarea_insert_text(ta, caret, "\n");
+ caret++;
+ ta->selection_start = ta->selection_end = -1;
+ redraw = true;
+ break;
+ case KEY_SHIFT_TAB:
+ case KEY_CR:
+ case KEY_CUT_LINE:
+ case KEY_PASTE:
+ case KEY_CUT_SELECTION:
+ break;
+ case KEY_CLEAR_SELECTION:
+ ta->selection_start = -1;
+ ta->selection_end = -1;
+ redraw = true;
+ break;
+ case KEY_ESCAPE:
+ break;
+ case KEY_LEFT:
+ if (caret)
+ caret--;
+ if (ta->selection_start != -1) {
+ ta->selection_start = ta->selection_end = -1;
+ redraw = true;
+ }
+ break;
+ case KEY_RIGHT:
+ c_len = utf8_length(ta->text);
+ if (caret < c_len)
+ caret++;
+ if (ta->selection_start != -1) {
+ ta->selection_start = ta->selection_end = -1;
+ redraw = true;
+ }
+ break;
+ case KEY_PAGE_UP:
+ if (ta->flags & TEXTAREA_MULTILINE) {
+ line_height = css_len2px(
+ &(ta->style->line_height.value.length),
+ ta->style);
+
+ /* +1 because one line is subtracted in KEY_UP*/
+ line = ta->caret_pos.line - (ta->vis_height
+ + line_height - 1) / line_height
+ + 1;
+ }
+ /*fall through*/
+ case KEY_UP:
+ if (ta->flags & TEXTAREA_MULTILINE) {
+ line--;
+ if (line < 0)
+ line = 0;
+ if (line != ta->caret_pos.line) {
+ c_line = ta->caret_pos.line;
+ c_chars = ta->caret_pos.char_off;
+
+ ta->caret_pos.line = line;
+ l_len = utf8_bounded_length(
+ &(ta->text[ta->lines[ta->caret_pos.line].b_start]),
+ ta->lines[ta->caret_pos.line].b_length);
+ ta->caret_pos.char_off = min(l_len,
+ (unsigned)ta->caret_pos.char_off);
+ caret = textarea_get_caret(ta);
+
+ ta->caret_pos.line = c_line;
+ ta->caret_pos.char_off = c_chars;
+ }
+ }
+ if (ta->selection_start != -1) {
+ ta->selection_start = ta->selection_end = -1;
+ redraw = true;
+ }
+ break;
+ case KEY_PAGE_DOWN:
+ if (ta->flags & TEXTAREA_MULTILINE) {
+ line_height = css_len2px(
+ &(ta->style->line_height.value.length),
+ ta->style);
+
+ /* -1 because one line is added in KEY_DOWN*/
+ line = ta->caret_pos.line + (ta->vis_height
+ + line_height - 1) / line_height
+ - 1;
+ }
+ /* fall through*/
+ case KEY_DOWN:
+ if (ta->flags & TEXTAREA_MULTILINE) {
+ line++;
+ if (line > ta->line_count - 1)
+ line = ta->line_count - 1;
+ if (line != ta->caret_pos.line) {
+ c_line = ta->caret_pos.line;
+ c_chars = ta->caret_pos.char_off;
+
+ ta->caret_pos.line = line;
+ l_len = utf8_bounded_length(
+ &(ta->text[ta->lines[ta->caret_pos.line].b_start]),
+ ta->lines[ta->caret_pos.line].b_length);
+ ta->caret_pos.char_off = min(l_len,
+ (unsigned)ta->caret_pos.char_off);
+ caret = textarea_get_caret(ta);
+
+ ta->caret_pos.line = c_line;
+ ta->caret_pos.char_off = c_chars;
+ }
+ }
+ if (ta->selection_start != -1) {
+ ta->selection_start = ta->selection_end = -1;
+ redraw = true;
+ }
+ break;
+ case KEY_DELETE_RIGHT:
+ if (ta->selection_start != -1) {
+ textarea_replace_text(ta, ta->selection_start,
+ ta->selection_end, "");
+ ta->selection_start = ta->selection_end = -1;
+ redraw = true;
+ } else {
+ c_len = utf8_length(ta->text);
+ if (caret < c_len) {
+ textarea_replace_text(ta, caret, caret + 1, "");
+ redraw = true;
+ }
+ }
+ break;
+ case KEY_LINE_START:
+ caret -= ta->caret_pos.char_off;
+ if (ta->selection_start != -1) {
+ ta->selection_start = ta->selection_end = -1;
+ redraw = true;
+ }
+ break;
+ case KEY_LINE_END:
+ caret = utf8_bounded_length(ta->text,
+ ta->lines[ta->caret_pos.line].b_start +
+ ta->lines[ta->caret_pos.line].b_length);
+ if (ta->text[ta->lines[ta->caret_pos.line].b_start +
+ ta->lines[ta->caret_pos.line].b_length
+ - 1] == ' ')
+ caret--;
+ if (ta->selection_start != -1) {
+ ta->selection_start = ta->selection_end = -1;
+ redraw = true;
+ }
+ break;
+ case KEY_TEXT_START:
+ caret = 0;
+ if (ta->selection_start != -1) {
+ ta->selection_start = ta->selection_end = -1;
+ redraw = true;
+ }
+ break;
+ case KEY_TEXT_END:
+ caret = utf8_length(ta->text);
+ if (ta->selection_start != -1) {
+ ta->selection_start = ta->selection_end = -1;
+ redraw = true;
+ }
+ break;
+ case KEY_WORD_LEFT:
+ case KEY_WORD_RIGHT:
+ break;
+ case KEY_DELETE_LINE_END:
+ if (ta->selection_start != -1) {
+ textarea_replace_text(ta, ta->selection_start,
+ ta->selection_end, "");
+ ta->selection_start = ta->selection_end = -1;
+ } else {
+ textarea_replace_text(ta, caret, caret + utf8_bounded_length(
+ &(ta->text[ta->lines[ta->caret_pos.line].b_start]),
+ ta->lines[ta->caret_pos.line].b_length),
+ "");
+ }
+ redraw = true;
+ break;
+ case KEY_DELETE_LINE_START:
+ if (ta->selection_start != -1) {
+ textarea_replace_text(ta, ta->selection_start,
+ ta->selection_end, "");
+ ta->selection_start = ta->selection_end = -1;
+ } else {
+ textarea_replace_text(ta,
+ caret - ta->caret_pos.char_off, caret,
+ "");
+ caret -= ta->caret_pos.char_off;
+ }
+ redraw = true;
+ break;
+ default:
+ return false;
+ }
+
+ //TODO:redraw only the important part
+ if (redraw) {
+ ta->redraw_start_callback(ta->data);
+ textarea_redraw(ta, ta->x, ta->y, ta->x + ta->vis_width,
+ ta->y + ta->vis_height);
+ ta->redraw_end_callback(ta->data);
+ }
+
+ if (caret != caret_init || redraw)
+ textarea_set_caret(ta, caret);
+
+ 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
+ */
+static bool textarea_scroll_visible(struct text_area *ta)
+{
+ int x0, x1, y0, y1, x, y;
+ int index, b_off, line_height;
+ bool scrolled = false;
+
+ if (ta->caret_pos.char_off == -1)
+ return false;
+
+ x0 = ta->x + MARGIN_LEFT;
+ x1 = ta->x + ta->vis_width - MARGIN_RIGHT;
+ y0 = ta->y;
+ y1 = ta->y + ta->vis_height;
+
+ index = textarea_get_caret(ta);
+
+ for (b_off = 0; index-- > 0;
+ b_off = utf8_next(ta->text, ta->text_len, b_off))
+ ; /* do nothing */
+
+ nsfont.font_width(ta->style,
+ ta->text + ta->lines[ta->caret_pos.line].b_start,
+ b_off - ta->lines[ta->caret_pos.line].b_start,
+ &x);
+
+ line_height = css_len2px(&(ta->style->line_height.value.length),
+ ta->style);
+
+ /* top-left of caret*/
+ x += ta->x + MARGIN_LEFT - ta->scroll_x;
+ y = line_height * ta->caret_pos.line + ta->y - ta->scroll_y;
+
+ /* check and change vertical scroll*/
+ if (y < y0) {
+ ta->scroll_y -= y0 - y;
+ scrolled = true;
+ } else if (y + line_height > y1) {
+ ta->scroll_y += y + 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;
+}
+
+
+/**
+ * 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
+ */
+void textarea_mouse_action(struct text_area *ta, browser_mouse_state mouse,
+ int x, int y)
+{
+ int c_start, c_end;
+
+ /* mouse button pressed above the text area, move caret*/
+ if (mouse & BROWSER_MOUSE_PRESS_1) {
+ if (ta->selection_start != -1) {
+ ta->selection_start = ta->selection_end = -1;
+ ta->redraw_start_callback(ta->data);
+ textarea_redraw(ta, ta->x, ta->y, ta->x + ta->vis_width,
+ ta->y + ta->vis_height);
+ ta->redraw_end_callback(ta->data);
+ }
+ textarea_set_caret_xy(ta, x, y);
+
+ return;
+ }
+
+ if (mouse & BROWSER_MOUSE_DRAG_1) {
+ ta->drag_start_char = textarea_get_xy_offset(ta, x, y);
+ textarea_set_caret(ta, -1);
+ return;
+ }
+
+ if (mouse & BROWSER_MOUSE_HOLDING_1) {
+ c_start = ta->drag_start_char;
+ c_end = textarea_get_xy_offset(ta, x, y);
+ textarea_select(ta, c_start, c_end);
+ return;
+ }
+}
+
+
+/**
+ * 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
+ */
+void textarea_drag_end(struct text_area *ta, browser_mouse_state mouse,
+ int x, int y)
+{
+ int c_end;
+
+ c_end = textarea_get_xy_offset(ta, x, y);
+ 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)
+ */
+void textarea_select(struct text_area *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;
+
+ ta->redraw_start_callback(ta->data);
+ textarea_redraw(ta, ta->x, ta->y, ta->x + ta->vis_width,
+ ta->y + ta->vis_height);
+ ta->redraw_end_callback(ta->data);
+
+ if (swap == -1)
+ textarea_set_caret(ta, c_end);
+ else
+ textarea_set_caret(ta, c_start);
+}
+
+
+/**
+ * Removes any CR characters and changes newlines into spaces if it is a single
+ * line textarea.
+ *
+ * \param ta Text area
+ * \param b_start Byte offset to start at
+ * \param b_len Byte length to check
+ */
+void textarea_normalise_text(struct text_area *ta,
+ unsigned int b_start, unsigned int b_len)
+{
+ bool multi = (ta->flags & TEXTAREA_MULTILINE) ? true:false;
+ unsigned int index;
+
+ /*remove CR characters*/
+ 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--;
+ 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';
+ }
+
+ if (!multi && (ta->text[b_start + index] == '\n'))
+ ta->text[b_start + index] = ' ';
+ }
+
+}
Index: desktop/textarea.h
===================================================================
--- /dev/null 2009-04-16 19:17:07.000000000 +0100
+++ desktop/textarea.h 2009-06-23 11:58:18.000000000 +0100
@@ -0,0 +1,61 @@
+/*
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Single/Multi-line UTF-8 text area (interface)
+ */
+
+#ifndef _NETSURF_DESKTOP_TEXTAREA_H_
+#define _NETSURF_DESKTOP_TEXTAREA_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "css/css.h"
+#include "desktop/browser.h"
+
+/* Text area flags */
+#define TEXTAREA_MULTILINE 0x01 /**< Text area is multiline */
+#define TEXTAREA_READONLY 0x02 /**< Text area is read only */
+
+struct text_area;
+
+/* this is temporary only*/
+browser_mouse_state textarea_mouse_state;
+int textarea_pressed_x;
+int textarea_pressed_y;
+
+typedef void(*textarea_start_radraw_callback)(intptr_t data);
+typedef void(*textarea_end_radraw_callback)(intptr_t data);
+
+struct text_area *textarea_create(int x, int y, int width, int height,
+ unsigned int flags, const struct css_style *style,
+ textarea_start_radraw_callback redraw_start_callback,
+ textarea_end_radraw_callback redraw_end_callback, intptr_t 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);
+void 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 x0, int y0, int x1, int y1);
+bool textarea_keypress(struct text_area *ta, uint32_t key);
+void textarea_mouse_action(struct text_area *ta, browser_mouse_state mouse,
+ int x, int y);
+void textarea_drag_end(struct text_area *ta, browser_mouse_state mouse,
+ int x, int y);
+
+#endif
+
Changed files
Makefile.sources | 2
desktop/browser.h | 17 +-
desktop/textinput.h | 3
gtk/font_pango.c | 1
gtk/gtk_plotters.c | 2
gtk/gtk_scaffolding.c | 289 +++++++++++++++++++++++++++++++++++++++++++++++++-
gtk/gtk_window.c | 41 ++++++-
gtk/gtk_window.h | 3
8 files changed, 340 insertions(+), 18 deletions(-)
Index: gtk/gtk_window.c
===================================================================
--- gtk/gtk_window.c (revision 7935)
+++ gtk/gtk_window.c (working copy)
@@ -38,7 +38,7 @@
struct gui_window *window_list = 0; /**< first entry in win list*/
int temp_open_background = -1;
-static uint32_t gdkkey_to_nskey(GdkEventKey *);
+
static void nsgtk_gui_window_attach_child(struct gui_window *parent,
struct gui_window *child);
/* Methods which apply only to a gui_window */
@@ -455,17 +455,48 @@
* everything that the RISC OS version does. But this will do for
* now. I hope.
*/
-
switch (key->keyval)
{
- case GDK_BackSpace: return KEY_DELETE_LEFT;
- case GDK_Delete: return KEY_DELETE_RIGHT;
+ case GDK_BackSpace:
+ if (key->state & GDK_SHIFT_MASK)
+ return KEY_DELETE_LINE_START;
+ else
+ return KEY_DELETE_LEFT;
+ case GDK_Delete:
+ if (key->state & GDK_SHIFT_MASK)
+ return KEY_DELETE_LINE_END;
+ else
+ return KEY_DELETE_RIGHT;
case GDK_Linefeed: return 13;
case GDK_Return: return 10;
case GDK_Left: return KEY_LEFT;
case GDK_Right: return KEY_RIGHT;
case GDK_Up: return KEY_UP;
case GDK_Down: return KEY_DOWN;
+ case GDK_Home:
+ if (key->state & GDK_CONTROL_MASK)
+ return KEY_TEXT_START;
+ else
+ return KEY_LINE_START;
+ case GDK_End:
+ if (key->state & GDK_CONTROL_MASK)
+ return KEY_TEXT_END;
+ else
+ return KEY_LINE_END;
+ case GDK_Page_Up:
+ return KEY_PAGE_UP;
+ case GDK_Page_Down:
+ return KEY_PAGE_DOWN;
+ case 'a':
+ if (key->state & GDK_CONTROL_MASK)
+ return KEY_SELECT_ALL;
+ return gdk_keyval_to_unicode(
+ key->keyval);
+ case 'u':
+ if (key->state & GDK_CONTROL_MASK)
+ return KEY_CLEAR_SELECTION;
+ return gdk_keyval_to_unicode(
+ key->keyval);
/* Modifiers - do nothing for now */
case GDK_Shift_L:
@@ -483,7 +514,7 @@
case GDK_Hyper_L:
case GDK_Hyper_R: return 0;
- default: return gdk_keyval_to_unicode(
+ default: return gdk_keyval_to_unicode(
key->keyval);
}
}
Index: gtk/gtk_window.h
===================================================================
--- gtk/gtk_window.h (revision 7935)
+++ gtk/gtk_window.h (working copy)
@@ -76,6 +76,9 @@
int nsgtk_gui_window_update_targets(struct gui_window *g);
void nsgtk_window_destroy_browser(struct gui_window *g);
+/*TODO: find a better place for this?*/
+uint32_t gdkkey_to_nskey(GdkEventKey *);
+
struct browser_window *nsgtk_get_browser_window(struct gui_window *g);
#endif /* NETSURF_GTK_WINDOW_H */
Index: gtk/gtk_plotters.c
===================================================================
--- gtk/gtk_plotters.c (revision 7935)
+++ gtk/gtk_plotters.c (working copy)
@@ -119,7 +119,7 @@
line_width = 1;
cairo_set_line_width(current_cr, line_width);
- cairo_rectangle(current_cr, x0, y0, width, height);
+ cairo_rectangle(current_cr, x0 + 0.5, y0 + 0.5, width, height);
cairo_stroke(current_cr);
return true;
Index: gtk/gtk_scaffolding.c
===================================================================
--- gtk/gtk_scaffolding.c (revision 7935)
+++ gtk/gtk_scaffolding.c (working copy)
@@ -37,6 +37,7 @@
#endif
#include "desktop/selection.h"
#include "desktop/textinput.h"
+#include "desktop/textarea.h"
#include "gtk/gtk_completion.h"
#include "gtk/dialogs/gtk_options.h"
#include "gtk/dialogs/gtk_about.h"
@@ -71,6 +72,15 @@
GtkDrawingArea *drawing_area;
};
+struct gtk_treeview_window {
+ GtkWindow *window;
+ GtkScrolledWindow *scrolled;
+ GtkDrawingArea *drawing_area;
+ int mouse_pressed_x;
+ int mouse_pressed_y;
+ browser_mouse_state mouse_state;
+};
+
struct menu_events {
const char *widget;
GCallback handler;
@@ -84,6 +94,8 @@
static gboolean nsgtk_window_delete_event(GtkWidget *, gpointer);
static void nsgtk_window_destroy_event(GtkWidget *, gpointer);
+static struct gtk_treeview_window global_history_window;
+
static void nsgtk_window_update_back_forward(struct gtk_scaffolding *);
static void nsgtk_throb(void *);
static gboolean nsgtk_window_edit_menu_clicked(GtkWidget *widget,
@@ -116,6 +128,20 @@
static gboolean nsgtk_history_button_press_event(GtkWidget *, GdkEventButton *,
gpointer);
+gboolean nsgtk_global_history_expose_event(GtkWidget *widget,
+ GdkEventExpose *event, gpointer g);
+static gboolean nsgtk_global_history_button_press_event(GtkWidget *, GdkEventButton *,
+ gpointer g);
+static gboolean nsgtk_global_history_button_release_event(GtkWidget *, GdkEventButton *,
+ gpointer g);
+gboolean nsgtk_global_history_motion_notify_event(GtkWidget *widget,
+ GdkEventButton *event, gpointer g);
+static gboolean nsgtk_global_history_keypress_event(GtkWidget *, GdkEventKey *,
+ gpointer g);
+
+static void redraw_start_callback(intptr_t data);
+static void redraw_end_callback(intptr_t data);
+
static void nsgtk_attach_menu_handlers(GladeXML *, gpointer);
static void nsgtk_window_tabs_num_changed(GtkNotebook *notebook,
GtkWidget *page, guint page_num, struct gtk_scaffolding *g);
@@ -1120,9 +1146,18 @@
MENUHANDLER(global_history)
{
- gtk_widget_show(GTK_WIDGET(wndHistory));
- gdk_window_raise(GTK_WIDGET(wndHistory)->window);
+ //gtk_widget_show(GTK_WIDGET(wndHistory));
+ //gdk_window_raise(GTK_WIDGET(wndHistory)->window);
+
+ struct gtk_scaffolding *gw = (struct gtk_scaffolding *) g;
+ gtk_window_set_default_size(global_history_window.window, 500, 400);
+ gtk_window_set_position(global_history_window.window, GTK_WIN_POS_MOUSE);
+ gtk_window_set_transient_for(global_history_window.window, gw->window);
+ gtk_window_set_opacity(global_history_window.window, 0.9);
+ gtk_widget_show(GTK_WIDGET(global_history_window.window));
+ gdk_window_raise(GTK_WIDGET(global_history_window.window)->window);
+
return TRUE;
}
@@ -1203,6 +1238,193 @@
return TRUE;
}
+/* signal handler functions for the global history window */
+gboolean nsgtk_global_history_expose_event(GtkWidget *widget,
+ GdkEventExpose *event, gpointer g)
+{
+// struct gtk_history_window *hw = (struct gtk_history_window *)g;
+// struct browser_window *bw =
+// nsgtk_get_browser_for_gui(hw->g->top_level);
+
+ struct text_area *ta = (struct text_area *) g;
+
+ current_widget = widget;
+ current_drawable = widget->window;
+ current_gc = gdk_gc_new(current_drawable);
+#ifdef CAIRO_VERSION
+ current_cr = gdk_cairo_create(current_drawable);
+#endif
+ plot = nsgtk_plotters;
+ nsgtk_plot_set_scale(1.0);
+
+ textarea_redraw(ta, 0, 0, 1000, 1000);
+
+ g_object_unref(current_gc);
+#ifdef CAIRO_VERSION
+ cairo_destroy(current_cr);
+#endif
+
+ return FALSE;
+}
+
+gboolean nsgtk_global_history_button_press_event(GtkWidget *widget,
+ GdkEventButton *event, gpointer g)
+{
+ LOG(("X=%g, Y=%g", event->x, event->y));
+
+ struct text_area *ta = (struct text_area *) g;
+
+ textarea_pressed_x = event->x;
+ textarea_pressed_y = event->y;
+
+ if (event->type == GDK_2BUTTON_PRESS)
+ textarea_mouse_state = BROWSER_MOUSE_DOUBLE_CLICK;
+
+ switch (event->button) {
+ case 1: textarea_mouse_state |= BROWSER_MOUSE_PRESS_1; break;
+ case 3: textarea_mouse_state |= BROWSER_MOUSE_PRESS_2; break;
+ }
+ /* Handle the modifiers too */
+ if (event->state & GDK_SHIFT_MASK)
+ textarea_mouse_state |= BROWSER_MOUSE_MOD_1;
+ if (event->state & GDK_CONTROL_MASK)
+ textarea_mouse_state |= BROWSER_MOUSE_MOD_2;
+ if (event->state & GDK_MOD1_MASK)
+ textarea_mouse_state |= BROWSER_MOUSE_MOD_3;
+
+ textarea_mouse_action(ta, textarea_mouse_state,
+ event->x, event->y);
+
+ return TRUE;
+}
+
+gboolean nsgtk_global_history_button_release_event(GtkWidget *widget,
+ GdkEventButton *event, gpointer g)
+{
+ bool shift = event->state & GDK_SHIFT_MASK;
+ bool ctrl = event->state & GDK_CONTROL_MASK;
+ bool alt = event->state & GDK_MOD1_MASK;
+ struct text_area *ta = (struct text_area *) g;
+
+ /* We consider only button 1 clicks as double clicks.
+ * If the mouse state is PRESS then we are waiting for a release to emit
+ * a click event, otherwise just reset the state to nothing*/
+ if (textarea_mouse_state & BROWSER_MOUSE_DOUBLE_CLICK) {
+
+ if (textarea_mouse_state & BROWSER_MOUSE_PRESS_1)
+ textarea_mouse_state ^= BROWSER_MOUSE_PRESS_1;
+ else if (textarea_mouse_state & BROWSER_MOUSE_PRESS_2)
+ textarea_mouse_state ^= (BROWSER_MOUSE_PRESS_2 |
+ BROWSER_MOUSE_DOUBLE_CLICK);
+
+ } else if (textarea_mouse_state & BROWSER_MOUSE_PRESS_1)
+ textarea_mouse_state ^=
+ (BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_CLICK_1);
+ else if (textarea_mouse_state & BROWSER_MOUSE_PRESS_2)
+ textarea_mouse_state ^=
+ (BROWSER_MOUSE_PRESS_2 | BROWSER_MOUSE_CLICK_2);
+
+ /* Handle modifiers being removed */
+ if (textarea_mouse_state & BROWSER_MOUSE_MOD_1 && !shift)
+ textarea_mouse_state ^= BROWSER_MOUSE_MOD_1;
+ if (textarea_mouse_state & BROWSER_MOUSE_MOD_2 && !ctrl)
+ textarea_mouse_state ^= BROWSER_MOUSE_MOD_2;
+ if (textarea_mouse_state & BROWSER_MOUSE_MOD_3 && !alt)
+ textarea_mouse_state ^= BROWSER_MOUSE_MOD_3;
+
+ if (textarea_mouse_state &
+ (BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_CLICK_2
+ | BROWSER_MOUSE_DOUBLE_CLICK))
+ textarea_mouse_action(ta, textarea_mouse_state,
+ event->x, event->y);
+ else
+ textarea_drag_end(ta, textarea_mouse_state, event->x, event->y);
+
+ textarea_mouse_state = 0;
+
+ return TRUE;
+}
+
+gboolean nsgtk_global_history_motion_notify_event(GtkWidget *widget,
+ GdkEventButton *event, gpointer g)
+{
+ bool shift = event->state & GDK_SHIFT_MASK;
+ bool ctrl = event->state & GDK_CONTROL_MASK;
+ bool alt = event->state & GDK_MOD1_MASK;
+ struct text_area *ta = (struct text_area *) g;
+
+
+ if (textarea_mouse_state & BROWSER_MOUSE_PRESS_1) {
+ /* Start button 1 drag */
+ textarea_mouse_action(ta, BROWSER_MOUSE_DRAG_1,
+ textarea_pressed_x, textarea_pressed_y);
+ /* Replace PRESS with HOLDING and declare drag in progress */
+ textarea_mouse_state ^= (BROWSER_MOUSE_PRESS_1 |
+ BROWSER_MOUSE_HOLDING_1);
+ textarea_mouse_state |= BROWSER_MOUSE_DRAG_ON;
+ }
+ else if (textarea_mouse_state & BROWSER_MOUSE_PRESS_2){
+ /* Start button 2s drag */
+ textarea_mouse_action(ta, BROWSER_MOUSE_DRAG_2,
+ textarea_pressed_x, textarea_pressed_y);
+ /* Replace PRESS with HOLDING and declare drag in progress */
+ textarea_mouse_state ^= (BROWSER_MOUSE_PRESS_2 |
+ BROWSER_MOUSE_HOLDING_2);
+ textarea_mouse_state |= BROWSER_MOUSE_DRAG_ON;
+ }
+
+ /* Handle modifiers being removed */
+ if (textarea_mouse_state & BROWSER_MOUSE_MOD_1 && !shift)
+ textarea_mouse_state ^= BROWSER_MOUSE_MOD_1;
+ if (textarea_mouse_state & BROWSER_MOUSE_MOD_2 && !ctrl)
+ textarea_mouse_state ^= BROWSER_MOUSE_MOD_2;
+ if (textarea_mouse_state & BROWSER_MOUSE_MOD_3 && !alt)
+ textarea_mouse_state ^= BROWSER_MOUSE_MOD_3;
+
+ textarea_mouse_action(ta, textarea_mouse_state,
+ event->x, event->y);
+
+ return TRUE;
+}
+
+gboolean nsgtk_global_history_keypress_event(GtkWidget *widget,
+ GdkEventKey *event, gpointer g)
+{
+ struct text_area *ta = g;
+ uint32_t nskey = gdkkey_to_nskey(event);
+
+ if (textarea_keypress(ta, nskey))
+ return TRUE;
+
+ /*the final user of textarea will probably want to do something here*/
+
+ return TRUE;
+}
+
+
+void redraw_start_callback(intptr_t data)
+{
+ current_widget = (GtkWidget *) data;
+ current_drawable = current_widget->window;
+ current_gc = gdk_gc_new(current_drawable);
+#ifdef CAIRO_VERSION
+ current_cr = gdk_cairo_create(current_drawable);
+#endif
+ plot = nsgtk_plotters;
+ nsgtk_plot_set_scale(1.0);
+}
+
+static void redraw_end_callback(intptr_t data)
+{
+ GtkWidget *widget = (GtkWidget *) data;
+ if (current_widget == widget) {
+ g_object_unref(current_gc);
+#ifdef CAIRO_VERSION
+ cairo_destroy(current_cr);
+#endif
+ }
+}
+
#define GET_WIDGET(x) glade_xml_get_widget(g->xml, (x))
nsgtk_scaffolding *nsgtk_new_scaffolding(struct gui_window *toplevel)
@@ -1352,6 +1574,39 @@
GTK_WIDGET(g->history_window->drawing_area));
gtk_widget_show(GTK_WIDGET(g->history_window->drawing_area));
+
+ global_history_window.window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
+ gtk_window_set_default_size(global_history_window.window, 400, 400);
+ gtk_window_set_title(global_history_window.window, "NetSurf Global History");
+ gtk_window_set_type_hint(global_history_window.window,
+ GDK_WINDOW_TYPE_HINT_UTILITY);
+ global_history_window.scrolled =
+ GTK_SCROLLED_WINDOW(gtk_scrolled_window_new(0, 0));
+ gtk_container_add(GTK_CONTAINER(global_history_window.window),
+ GTK_WIDGET(global_history_window.scrolled));
+
+ gtk_widget_show(GTK_WIDGET(global_history_window.scrolled));
+ global_history_window.drawing_area =
+ GTK_DRAWING_AREA(gtk_drawing_area_new());
+
+ gtk_widget_set_events(GTK_WIDGET(global_history_window.drawing_area),
+ GDK_EXPOSURE_MASK |
+ GDK_POINTER_MOTION_MASK |
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_KEY_PRESS_MASK |
+ GDK_KEY_RELEASE_MASK);
+ gtk_widget_modify_bg(GTK_WIDGET(global_history_window.drawing_area),
+ GTK_STATE_NORMAL,
+ &((GdkColor) { 0, 0xffff, 0xffff, 0xffff } ));
+ gtk_scrolled_window_add_with_viewport(global_history_window.scrolled,
+ GTK_WIDGET(global_history_window.drawing_area));
+ gtk_widget_show(GTK_WIDGET(global_history_window.drawing_area));
+ gtk_widget_set_size_request(GTK_WIDGET(global_history_window.drawing_area),
+ 1000, 1000);
+ GTK_WIDGET_SET_FLAGS(GTK_WIDGET(global_history_window.drawing_area),
+ GTK_CAN_FOCUS);
+
/* set up URL bar completion */
g->url_bar_completion = gtk_entry_completion_new();
gtk_entry_set_completion(g->url_bar, g->url_bar_completion);
@@ -1366,7 +1621,7 @@
"popup-set-width", TRUE,
"popup-single-match", TRUE,
NULL);
-
+
/* set up the throbber. */
gtk_image_set_from_pixbuf(g->throbber, nsgtk_throbber->framedata[0]);
g->throb_frame = 0;
@@ -1384,6 +1639,34 @@
CONNECT(g->history_window->window, "delete_event",
gtk_widget_hide_on_delete, NULL);
+ struct text_area *ta;
+
+ ta = textarea_create( 100, 100, 100, 45, TEXTAREA_READONLY,
+ &css_base_style, redraw_start_callback,
+ redraw_end_callback,
+ (intptr_t)global_history_window.drawing_area);
+ textarea_set_text(ta, "012345 67890123");
+ textarea_mouse_state = 0;
+ textarea_pressed_x = 0;
+ textarea_pressed_y = 0;
+
+
+ CONNECT(global_history_window.drawing_area, "expose_event",
+ nsgtk_global_history_expose_event, ta);
+ CONNECT(global_history_window.drawing_area, "button_press_event",
+ nsgtk_global_history_button_press_event,
+ ta);
+ CONNECT(global_history_window.drawing_area, "button_release_event",
+ nsgtk_global_history_button_release_event,
+ ta);
+ CONNECT(global_history_window.drawing_area, "motion_notify_event",
+ nsgtk_global_history_motion_notify_event,
+ ta);
+ CONNECT(global_history_window.drawing_area, "key_press_event",
+ nsgtk_global_history_keypress_event, ta);
+ CONNECT(global_history_window.window, "delete_event",
+ gtk_widget_hide_on_delete, NULL);
+
g_signal_connect_after(g->notebook, "page-added",
G_CALLBACK(nsgtk_window_tabs_num_changed), g);
g_signal_connect_after(g->notebook, "page-removed",
Index: gtk/font_pango.c
===================================================================
--- gtk/font_pango.c (revision 7935)
+++ gtk/font_pango.c (working copy)
@@ -183,6 +183,7 @@
pango_layout_set_width(layout, x * PANGO_SCALE);
pango_layout_set_wrap(layout, PANGO_WRAP_WORD);
+ pango_layout_set_single_paragraph_mode(layout, true);
line = pango_layout_get_line(layout, 1);
if (line)
index = line->start_index - 1;
Index: Makefile.sources
===================================================================
--- Makefile.sources (revision 7935)
+++ Makefile.sources (working copy)
@@ -13,7 +13,7 @@
layout.c list.c loosen.c table.c textplain.c
S_UTILS := base64.c filename.c hashtable.c locale.c messages.c talloc.c \
url.c utf8.c utils.c useragent.c
-S_DESKTOP := knockout.c options.c print.c tree.c version.c
+S_DESKTOP := knockout.c options.c print.c tree.c version.c textarea.c
# S_COMMON are sources common to all builds
S_COMMON := $(addprefix content/,$(S_CONTENT)) \
Index: desktop/textinput.h
===================================================================
--- desktop/textinput.h (revision 7935)
+++ desktop/textinput.h (working copy)
@@ -28,7 +28,8 @@
#include <stdbool.h>
-struct browser_window;
+#include "desktop/browser.h"
+
struct box;
Index: desktop/browser.h
===================================================================
--- desktop/browser.h (revision 7935)
+++ desktop/browser.h (working copy)
@@ -191,21 +191,24 @@
* a drag. */
BROWSER_MOUSE_CLICK_1 = 4, /* button 1 clicked. */
BROWSER_MOUSE_CLICK_2 = 8, /* button 2 clicked. */
+ BROWSER_MOUSE_DOUBLE_CLICK = 16, /* button 1 double clicked */
- BROWSER_MOUSE_DRAG_1 = 16, /* start of button 1 drag operation */
- BROWSER_MOUSE_DRAG_2 = 32, /* start of button 2 drag operation */
+ BROWSER_MOUSE_DRAG_1 = 32, /* start of button 1 drag operation */
+ BROWSER_MOUSE_DRAG_2 = 64, /* start of button 2 drag operation */
- BROWSER_MOUSE_DRAG_ON = 64, /* a drag operation was started and
+ BROWSER_MOUSE_DRAG_ON = 128, /* a drag operation was started and
* a mouse button is still pressed */
- BROWSER_MOUSE_HOLDING_1 = 128, /* while button 1 drag is in progress */
- BROWSER_MOUSE_HOLDING_2 = 256, /* while button 2 drag is in progress */
+ BROWSER_MOUSE_HOLDING_1 = 256, /* while button 1 drag is in progress */
+ BROWSER_MOUSE_HOLDING_2 = 512, /* while button 2 drag is in progress */
- BROWSER_MOUSE_MOD_1 = 512, /* primary modifier key pressed
+ BROWSER_MOUSE_MOD_1 = 1024, /* primary modifier key pressed
* (eg. Shift) */
- BROWSER_MOUSE_MOD_2 = 1024 /* secondary modifier key pressed
+ BROWSER_MOUSE_MOD_2 = 2048, /* secondary modifier key pressed
* (eg. Ctrl) */
+ BROWSER_MOUSE_MOD_3 = 4096 /* secondary modifier key pressed
+ * (eg. Alt) */
} browser_mouse_state;
Conflicted files
Removed files
14 years, 3 months