/*
- * Claws Mail -- A GTK+ based, lightweight, and fast e-mail client
- * Copyright(C) 1999-2015 the Claws Mail Team
- * == Fancy Plugin ==
- * This file Copyright (C) 2009-2015 Salvatore De Paolis
- * <iwkse@claws-mail.org> and the Claws Mail Team
+ * Claws Mail -- A GTK based, lightweight, and fast e-mail client
+ * Copyright(C) 2019 the Claws Mail Team
+ *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
#endif
#include <glib.h>
+#include <glib/gi18n.h>
#include <glib/gstdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <curl/curl.h>
+#include <gdk/gdk.h>
+
+#include "utils.h"
+
+#include "litehtml/litehtml.h"
+
+#include "lh_prefs.h"
#include "lh_widget.h"
#include "lh_widget_wrapped.h"
-char master_css[] = {
-#include "css.inc"
-};
-
-/**
- * curl callback
- */
-static char* response_mime = NULL; /* response content-type. ex: "text/html" */
-static char* response_data = NULL; /* response data from server. */
-static size_t response_size = 0; /* response size of data */
+extern "C" {
+const gchar *prefs_common_get_uri_cmd(void);
+}
-static gboolean expose_event_cb(GtkWidget *widget, GdkEvent *event,
+static gboolean draw_cb(GtkWidget *widget, cairo_t *cr,
gpointer user_data);
-static void size_allocate_cb(GtkWidget *widget, GdkRectangle *allocation,
+static gboolean button_press_event(GtkWidget *widget, GdkEventButton *event,
gpointer user_data);
-static size_t handle_returned_data(char* ptr, size_t size, size_t nmemb, void* stream);
-static size_t handle_returned_header(void* ptr, size_t size, size_t nmemb, void* stream);
+static gboolean motion_notify_event(GtkWidget *widget, GdkEventButton *event,
+ gpointer user_data);
+static gboolean button_release_event(GtkWidget *widget, GdkEventButton *event,
+ gpointer user_data);
+static void open_link_cb(GtkMenuItem *item, gpointer user_data);
+static void copy_link_cb(GtkMenuItem *item, gpointer user_data);
lh_widget::lh_widget()
{
+ GtkWidget *item;
+
+ m_force_render = false;
+ m_blank = false;
+
/* scrolled window */
m_scrolled_window = gtk_scrolled_window_new(NULL, NULL);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(m_scrolled_window),
GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
- g_signal_connect(m_scrolled_window, "size-allocate",
- G_CALLBACK(size_allocate_cb), this);
/* viewport */
GtkScrolledWindow *scw = GTK_SCROLLED_WINDOW(m_scrolled_window);
/* drawing area */
m_drawing_area = gtk_drawing_area_new();
gtk_container_add(GTK_CONTAINER(m_viewport), m_drawing_area);
- g_signal_connect(m_drawing_area, "expose-event",
- G_CALLBACK(expose_event_cb), this);
+ g_signal_connect(m_drawing_area, "draw",
+ G_CALLBACK(draw_cb), this);
+ g_signal_connect(m_drawing_area, "motion_notify_event",
+ G_CALLBACK(motion_notify_event), this);
+ g_signal_connect(m_drawing_area, "button_press_event",
+ G_CALLBACK(button_press_event), this);
+ g_signal_connect(m_drawing_area, "button_release_event",
+ G_CALLBACK(button_release_event), this);
gtk_widget_show_all(m_scrolled_window);
+ /* context menu */
+ m_context_menu = gtk_menu_new();
+
+ item = gtk_menu_item_new_with_label(_("Open Link"));
+ g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(open_link_cb), this);
+ gtk_menu_shell_append(GTK_MENU_SHELL(m_context_menu), item);
+
+ item = gtk_menu_item_new_with_label(_("Copy Link Location"));
+ g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(copy_link_cb), this);
+ gtk_menu_shell_append(GTK_MENU_SHELL(m_context_menu), item);
+
m_html = NULL;
m_rendered_width = 0;
- m_context.load_master_stylesheet(master_css);
- stream = NULL;
+
+ m_font_name = NULL;
+ m_font_size = 0;
+
+ m_partinfo = NULL;
+
+ m_showing_url = FALSE;
+
+ m_cairo_context = NULL;
+
+ gtk_widget_set_events(m_drawing_area,
+ GDK_BUTTON_RELEASE_MASK
+ | GDK_BUTTON_PRESS_MASK
+ | GDK_POINTER_MOTION_MASK);
}
lh_widget::~lh_widget()
g_object_unref(m_scrolled_window);
m_scrolled_window = NULL;
m_html = NULL;
- if (stream) {
- g_input_stream_close(stream, NULL, NULL);
- stream = NULL;
- }
+ g_free(m_font_name);
}
GtkWidget *lh_widget::get_widget() const
return m_scrolled_window;
}
-void lh_widget::set_caption(const litehtml::tchar_t* caption)
+void lh_widget::set_caption(const char *caption)
{
- g_log(NULL, G_LOG_LEVEL_MESSAGE, "lh_widget set_caption");
+ debug_print("lh_widget set_caption\n");
return;
}
-void lh_widget::set_base_url(const litehtml::tchar_t* base_url)
+void lh_widget::set_base_url(const char *base_url)
{
- g_log(NULL, G_LOG_LEVEL_MESSAGE, "lh_widget set_base_url");
- return;
-}
+ debug_print("lh_widget set_base_url '%s'\n",
+ (base_url ? base_url : "(null)"));
+ if (base_url)
+ m_base_url = base_url;
+ else
+ m_base_url.clear();
-void lh_widget::on_anchor_click(const litehtml::tchar_t* url, const litehtml::element::ptr& el)
-{
- g_log(NULL, G_LOG_LEVEL_MESSAGE, "lh_widget on_anchor_click");
return;
}
-void lh_widget::set_cursor(const litehtml::tchar_t* cursor)
+void lh_widget::on_anchor_click(const char *url, const litehtml::element::ptr& el)
{
- g_log(NULL, G_LOG_LEVEL_MESSAGE, "lh_widget set_cursor");
- if (cursor == NULL)
- return;
+ debug_print("lh_widget on_anchor_click. url -> %s\n", url);
+
+ m_clicked_url = fullurl(url);
+ return;
}
-void lh_widget::import_css(litehtml::tstring& text, const litehtml::tstring& url, litehtml::tstring& baseurl)
+void lh_widget::import_css(litehtml::string& text, const litehtml::string& url, litehtml::string& baseurl)
{
- g_log(NULL, G_LOG_LEVEL_MESSAGE, "lh_widget import_css");
- baseurl = master_css;
+ debug_print("lh_widget import_css. url=\"%s\" baseurl=\"%s\"\n", url.c_str(), baseurl.c_str());
}
void lh_widget::get_client_rect(litehtml::position& client) const
client.x = 0;
client.y = 0;
-// g_log(NULL, G_LOG_LEVEL_MESSAGE, "lh_widget::get_client_rect: %dx%d",
+// debug_print("lh_widget::get_client_rect: %dx%d\n",
// client.width, client.height);
}
-GdkPixbuf *lh_widget::get_image(const litehtml::tchar_t* url, bool redraw_on_ready)
+void lh_widget::open_html(const gchar *contents)
{
- GError *error = NULL;
- GdkPixbuf *pixbuf = NULL;
+ gint num = clear_images(lh_prefs_get()->image_cache_size * 1024 * 1000);
+ GtkAdjustment *adj;
- g_log(NULL, G_LOG_LEVEL_MESSAGE, "Loading... %s", url);
+ debug_print("LH: cleared %d images from image cache\n", num);
- GInputStream *image = load_url(url, &error);
- if (error) {
- g_log(NULL, G_LOG_LEVEL_MESSAGE, "Error: %s", error->message);
- g_error_free(error);
- return NULL;
- }
+ update_font();
- GdkPixbufLoader* loader = gdk_pixbuf_loader_new();
- if (gdk_pixbuf_loader_write(loader, (const guchar*)response_data, response_size, &error)) {
- pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
- } else {
- g_log(NULL, G_LOG_LEVEL_ERROR, "lh_widget::get_image: Could not create pixbuf");
+ lh_widget_statusbar_push("Loading HTML part ...");
+ m_html = litehtml::document::createFromString(contents, this);
+ m_rendered_width = 0;
+ if (m_html != NULL) {
+ debug_print("lh_widget::open_html created document\n");
+ adj = gtk_scrolled_window_get_hadjustment(
+ GTK_SCROLLED_WINDOW(m_scrolled_window));
+ gtk_adjustment_set_value(adj, 0.0);
+ adj = gtk_scrolled_window_get_vadjustment(
+ GTK_SCROLLED_WINDOW(m_scrolled_window));
+ gtk_adjustment_set_value(adj, 0.0);
+ m_blank = false;
}
- gdk_pixbuf_loader_close(loader, NULL);
-
- /* cleanup callback data */
- if (response_mime) g_free(response_mime);
- if (response_data) g_free(response_data);
- response_data = NULL;
- response_mime = NULL;
- response_size = 0;
-
- return pixbuf;
+ lh_widget_statusbar_pop();
}
-void lh_widget::open_html(const gchar *contents)
+void lh_widget::rerender()
{
- m_html = litehtml::document::createFromString(contents, this, &m_context);
- m_rendered_width = 0;
- if (m_html != NULL) {
- g_log(NULL, G_LOG_LEVEL_MESSAGE, "lh_widget::open_html created document");
- redraw();
- }
+ m_force_render = true;
+ gtk_widget_queue_draw(m_drawing_area);
}
void lh_widget::draw(cairo_t *cr)
void lh_widget::redraw()
{
GtkAllocation rect;
- gint width, height;
+ gint width;
GdkWindow *gdkwin;
cairo_t *cr;
+ cairo_region_t *creg;
+ GdkDrawingContext *gdkctx;
+ gboolean destroy = FALSE;
- if (m_html == NULL) {
- g_log(NULL, G_LOG_LEVEL_WARNING, "lh_widget::redraw: No document!");
+ if (m_html == NULL)
return;
- }
/* Get width of the viewport. */
- gdkwin = gtk_viewport_get_view_window(GTK_VIEWPORT(m_viewport));
- gdk_drawable_get_size(gdkwin, &width, NULL);
+ gtk_widget_get_allocation(GTK_WIDGET(m_viewport), &rect);
+ width = rect.width;
+ m_height = rect.height;
/* If the available width has changed, rerender the HTML content. */
- if (m_rendered_width != width) {
- g_log(NULL, G_LOG_LEVEL_MESSAGE,
- "lh_widget::redraw: width changed: %d != %d",
+ if (m_rendered_width != width || std::atomic_exchange(&m_force_render, false)) {
+ debug_print("lh_widget::redraw: width changed: %d != %d\n",
m_rendered_width, width);
/* Update our internally stored width, mainly so that
/* Re-render HTML for this width. */
m_html->media_changed();
m_html->render(m_rendered_width);
- g_log(NULL, G_LOG_LEVEL_MESSAGE, "render is %dx%d",
- m_html->width(), m_html->height());
+ debug_print("render is %dx%d\n", m_html->width(), m_html->height());
/* Change drawing area's size to match what was rendered. */
gtk_widget_set_size_request(m_drawing_area,
m_html->width(), m_html->height());
}
- paint_white();
+ /* Use provided cairo context, if any. Otherwise create our own. */
+ if (m_cairo_context != NULL) {
+ cr = m_cairo_context;
+ } else {
+ gdkwin = gtk_widget_get_window(m_drawing_area);
+ if (gdkwin == NULL) {
+ g_warning("lh_widget::redraw: No GdkWindow to draw on!");
+ return;
+ }
+ creg = cairo_region_create_rectangle(&rect);
+ gdkctx = gdk_window_begin_draw_frame(gdkwin, creg);
+ cr = gdk_drawing_context_get_cairo_context(gdkctx);
+ destroy = TRUE;
+ }
- /* Paint the rendered HTML. */
- gdkwin = gtk_widget_get_window(m_drawing_area);
- if (gdkwin == NULL) {
- g_log(NULL, G_LOG_LEVEL_WARNING, "lh_widget::redraw: No GdkWindow to draw on!");
- return;
+ if(!std::atomic_exchange(&m_blank, false)) {
+ draw(cr);
+ } else {
+ cairo_rectangle(cr, rect.x, rect.y, rect.width, rect.height);
+ cairo_set_source_rgb(cr, 255, 255, 255);
+ cairo_fill(cr);
+ }
+
+ /* Only destroy the used cairo context if we created it earlier. */
+ if (destroy) {
+ gdk_window_end_draw_frame(gdkwin, gdkctx);
+ cairo_region_destroy(creg);
}
- cr = gdk_cairo_create(GDK_DRAWABLE(gdkwin));
- draw(cr);
+}
- cairo_destroy(cr);
+void lh_widget::clear()
+{
+ m_html = nullptr;
+ m_blank = true;
+ m_rendered_width = 0;
+ m_base_url.clear();
+ m_clicked_url.clear();
}
-void lh_widget::paint_white()
+void lh_widget::set_cursor(const char *cursor)
{
- GdkWindow *gdkwin = gtk_widget_get_window(m_drawing_area);
- if (gdkwin == NULL) {
- g_log(NULL, G_LOG_LEVEL_WARNING, "lh_widget::clear: No GdkWindow to draw on!");
- return;
- }
- cairo_t *cr = gdk_cairo_create(GDK_DRAWABLE(gdkwin));
+ litehtml::element::const_ptr over_el = m_html->get_over_element();
- /* Paint white background. */
- gint width, height;
- gdk_drawable_get_size(gdkwin, &width, &height);
- cairo_rectangle(cr, 0, 0, width, height);
- cairo_set_source_rgb(cr, 255, 255, 255);
- cairo_fill(cr);
+ if (m_showing_url &&
+ (over_el == NULL || over_el != m_over_element)) {
+ lh_widget_statusbar_pop();
+ m_showing_url = FALSE;
+ }
- cairo_destroy(cr);
+ if (over_el != m_over_element) {
+ m_over_element = over_el;
+ update_cursor(cursor);
+ }
}
-void lh_widget::clear()
+
+void lh_widget::update_cursor(const char *cursor)
{
- paint_white();
- m_rendered_width = 0;
+ GdkCursorType cursType = GDK_ARROW;
+ const char *href = get_href_at(m_over_element);
+
+ /* If there is a href, and litehtml is okay with showing a pointer
+ * cursor ("pointer" or "auto"), set it, otherwise keep the
+ * default arrow cursor */
+ if ((!strcmp(cursor, "pointer") || !strcmp(cursor, "auto")) &&
+ href != NULL) {
+ cursType = GDK_HAND2;
+ }
+
+ if (cursType == GDK_ARROW) {
+ gdk_window_set_cursor(gtk_widget_get_window(m_drawing_area), NULL);
+ } else {
+ gdk_window_set_cursor(gtk_widget_get_window(m_drawing_area),
+ gdk_cursor_new_for_display(gtk_widget_get_display(m_drawing_area),
+ cursType));
+ }
+
+ /* If there is a href, show it in statusbar */
+ if (href != NULL) {
+ lh_widget_statusbar_push(fullurl(href).c_str());
+ m_showing_url = TRUE;
+ }
}
-GInputStream *lh_widget::load_url(const gchar *url, GError **error)
+const char *lh_widget::get_href_at(litehtml::element::const_ptr element)
{
- GError* _error = NULL;
- CURL* curl = NULL;
- CURLcode res = CURLE_OK;
- gsize len;
- gchar* content;
+ litehtml::element::const_ptr el;
- /* initialize callback data */
- response_mime = NULL;
- response_data = NULL;
- response_size = 0;
- if (stream) {
- g_input_stream_close(stream, NULL, &_error);
- if (_error) {
- if (error) *error = _error;
- return NULL;
+ if (element == NULL)
+ return NULL;
+
+ /* If it's not an anchor, check if it has a parent anchor
+ * (e.g. it's an image within an anchor) and grab a pointer
+ * to that. */
+ if (strcmp(element->get_tagName(), "a") && element->parent()) {
+ el = element->parent();
+ while (el && el != m_html->root() && strcmp(el->get_tagName(), "a")) {
+ el = el->parent();
}
+
+ if (!el || el == m_html->root())
+ return NULL;
+ } else {
+ el = element;
}
-
- stream = NULL;
-
- if (!strncmp(url, "file:///", 8) || g_file_test(url, G_FILE_TEST_EXISTS)) {
- gchar* newurl = g_filename_from_uri(url, NULL, NULL);
- if (g_file_get_contents(newurl ? newurl : url, &content, &len, &_error)) {
- stream = g_memory_input_stream_new_from_data(content, len, g_free);
- } else {
- g_log(NULL, G_LOG_LEVEL_MESSAGE, "%s", _error->message);
+
+ /* At this point, over_el is pointing at an anchor tag, so let's
+ * grab its href attribute. */
+ return el->get_attr("href");
+}
+
+void lh_widget::print()
+{
+ debug_print("lh_widget print\n");
+ gtk_widget_realize(GTK_WIDGET(m_drawing_area));
+}
+
+void lh_widget::popup_context_menu(const char *url,
+ GdkEventButton *event)
+{
+ cm_return_if_fail(url != NULL);
+ cm_return_if_fail(event != NULL);
+
+ debug_print("lh_widget showing context menu for '%s'\n", url);
+
+ m_clicked_url = url;
+ gtk_widget_show_all(m_context_menu);
+ gtk_menu_popup_at_pointer(GTK_MENU(m_context_menu), (GdkEvent *)event);
+}
+
+void lh_widget::update_font()
+{
+ PangoFontDescription *pd =
+ pango_font_description_from_string(lh_prefs_get()->default_font);
+ gboolean absolute = pango_font_description_get_size_is_absolute(pd);
+
+ g_free(m_font_name);
+ m_font_name = g_strdup(pango_font_description_get_family(pd));
+ m_font_size = pango_font_description_get_size(pd);
+
+ pango_font_description_free(pd);
+
+ if (!absolute)
+ m_font_size /= PANGO_SCALE;
+
+ debug_print("Font set to '%s', size %d\n", m_font_name, m_font_size);
+}
+
+const litehtml::string lh_widget::fullurl(const char *url) const
+{
+ if (*url == '#' && !m_base_url.empty())
+ return m_base_url + url;
+
+ return url;
+}
+
+void lh_widget::set_partinfo(MimeInfo *partinfo)
+{
+ m_partinfo = partinfo;
+}
+
+GdkPixbuf *lh_widget::get_local_image(const litehtml::string url) const
+{
+ GdkPixbuf *pixbuf;
+ const gchar *name;
+ MimeInfo *p = m_partinfo;
+
+ if (strncmp(url.c_str(), "cid:", 4) != 0) {
+ debug_print("lh_widget::get_local_image: '%s' is not a local URI, ignoring\n", url.c_str());
+ return NULL;
+ }
+
+ name = url.c_str() + 4;
+ debug_print("getting message part '%s'\n", name);
+
+ while ((p = procmime_mimeinfo_next(p)) != NULL) {
+ size_t len = strlen(name);
+
+ /* p->id is in format "<partname>" */
+ if (p->id != NULL &&
+ strlen(p->id) >= len + 2 &&
+ !strncasecmp(name, p->id + 1, len) &&
+ *(p->id + len + 1) == '>') {
+ GError *error = NULL;
+
+ pixbuf = procmime_get_part_as_pixbuf(p, &error);
+ if (error != NULL) {
+ g_warning("couldn't load image: %s", error->message);
+ g_error_free(error);
+ return NULL;
+ }
+
+ return pixbuf;
}
- g_free(newurl);
- } else {
- curl = curl_easy_init();
- if (!curl) return NULL;
- curl_easy_setopt(curl, CURLOPT_URL, url);
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, handle_returned_data);
- curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, handle_returned_header);
- curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
- curl_easy_setopt(curl, CURLOPT_TIMEOUT, HTTP_GET_TIMEOUT);
- curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
- res = curl_easy_perform(curl);
- curl_easy_cleanup(curl);
- if (res == CURLE_OK) {
- stream = g_memory_input_stream_new_from_data(content, response_size, g_free);
- } else
- _error = g_error_new_literal(G_FILE_ERROR, res, curl_easy_strerror(res));
}
- if (error && _error) *error = _error;
+ /* MIME part with requested name was not found */
+ return NULL;
+}
- return stream;
+void lh_widget::set_cairo_context(cairo_t *cr)
+{
+ m_cairo_context = cr;
}
-static gboolean expose_event_cb(GtkWidget *widget, GdkEvent *event,
+
+////////////////////////////////////////////////
+static gboolean draw_cb(GtkWidget *widget, cairo_t *cr,
gpointer user_data)
{
lh_widget *w = (lh_widget *)user_data;
+ w->set_cairo_context(cr);
w->redraw();
+ w->set_cairo_context(NULL);
return FALSE;
}
-static void size_allocate_cb(GtkWidget *widget, GdkRectangle *allocation,
+static gboolean button_press_event(GtkWidget *widget, GdkEventButton *event,
gpointer user_data)
{
+ litehtml::position::vector redraw_boxes;
lh_widget *w = (lh_widget *)user_data;
- g_log(NULL, G_LOG_LEVEL_MESSAGE, "size_allocate_cb: %dx%d",
- allocation->width, allocation->height);
+ if (w->m_html == NULL)
+ return FALSE;
- w->setHeight(allocation->height);
- w->redraw();
+ //debug_print("lh_widget on_button_press_event\n");
+
+ if (event->type == GDK_2BUTTON_PRESS ||
+ event->type == GDK_3BUTTON_PRESS)
+ return TRUE;
+
+ /* Right-click */
+ if (event->button == 3) {
+ const char *url = w->get_href_at(w->m_html->get_over_element());
+
+ if (url != NULL)
+ w->popup_context_menu(url, event);
+
+ return TRUE;
+ }
+
+ if(w->m_html->on_lbutton_down((int) event->x, (int) event->y,
+ (int) event->x, (int) event->y, redraw_boxes)) {
+ for(auto& pos : redraw_boxes) {
+ debug_print("x: %d y:%d w: %d h: %d\n", pos.x, pos.y, pos.width, pos.height);
+ gtk_widget_queue_draw_area(widget, pos.x, pos.y, pos.width, pos.height);
+ }
+ }
+
+ return TRUE;
}
-static size_t handle_returned_data(char* ptr, size_t size, size_t nmemb, void* stream) {
- if (!response_data)
- response_data = (char*)malloc(size*nmemb);
- else
- response_data = (char*)realloc(response_data, response_size+size*nmemb);
- if (response_data) {
- memcpy(response_data+response_size, ptr, size*nmemb);
- response_size += size*nmemb;
+static gboolean motion_notify_event(GtkWidget *widget, GdkEventButton *event,
+ gpointer user_data)
+{
+ litehtml::position::vector redraw_boxes;
+ lh_widget *w = (lh_widget *)user_data;
+
+ //debug_print("lh_widget on_motion_notify_event\n");
+
+ if(w->m_html)
+ {
+ if(w->m_html->on_mouse_over((int) event->x, (int) event->y, (int) event->x, (int) event->y, redraw_boxes))
+ {
+ for (auto& pos : redraw_boxes)
+ {
+ debug_print("x: %d y:%d w: %d h: %d\n", pos.x, pos.y, pos.width, pos.height);
+ gtk_widget_queue_draw_area(widget, pos.x, pos.y, pos.width, pos.height);
+ }
+ }
}
- return size*nmemb;
+
+ return TRUE;
}
-static size_t handle_returned_header(void* ptr, size_t size, size_t nmemb, void* stream) {
- char* header = NULL;
+static gboolean button_release_event(GtkWidget *widget, GdkEventButton *event,
+ gpointer user_data)
+{
+ litehtml::position::vector redraw_boxes;
+ lh_widget *w = (lh_widget *)user_data;
- header = (char*) malloc(size*nmemb + 1);
- memcpy(header, ptr, size*nmemb);
- header[size*nmemb] = 0;
- if (strncmp(header, "Content-Type: ", 14) == 0) {
- char* stop = header + 14;
- stop = strpbrk(header + 14, "\r\n;");
- if (stop) *stop = 0;
- response_mime = strdup(header + 14);
- }
- free(header);
- return size*nmemb;
+ if (w->m_html == NULL)
+ return FALSE;
+
+ //debug_print("lh_widget on_button_release_event\n");
+
+ if (event->type == GDK_2BUTTON_PRESS ||
+ event->type == GDK_3BUTTON_PRESS)
+ return TRUE;
+
+ /* Right-click */
+ if (event->button == 3)
+ return TRUE;
+
+ w->m_clicked_url.clear();
+
+ if(w->m_html->on_lbutton_up((int) event->x, (int) event->y, (int) event->x, (int) event->y, redraw_boxes))
+ {
+ for (auto& pos : redraw_boxes)
+ {
+ debug_print("x: %d y:%d w: %d h: %d\n", pos.x, pos.y, pos.width, pos.height);
+ gtk_widget_queue_draw_area(widget, pos.x, pos.y, pos.width, pos.height);
+ }
+ }
+
+ if (!w->m_clicked_url.empty())
+ {
+ debug_print("Open in browser: %s\n", w->m_clicked_url.c_str());
+ open_uri(w->m_clicked_url.c_str(), prefs_common_get_uri_cmd());
+ }
+
+ return TRUE;
+}
+
+static void open_link_cb(GtkMenuItem *item, gpointer user_data)
+{
+ lh_widget_wrapped *w = (lh_widget_wrapped *)user_data;
+
+ open_uri(w->m_clicked_url.c_str(), prefs_common_get_uri_cmd());
+}
+
+static void copy_link_cb(GtkMenuItem *item, gpointer user_data)
+{
+ lh_widget_wrapped *w = (lh_widget_wrapped *)user_data;
+
+ gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_PRIMARY),
+ w->m_clicked_url.c_str(), -1);
+ gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
+ w->m_clicked_url.c_str(), -1);
}
///////////////////////////////////////////////////////////
delete w;
}
+void lh_widget_print(lh_widget_wrapped *w) {
+ w->print();
+}
+
+void lh_widget_set_partinfo(lh_widget_wrapped *w, MimeInfo *partinfo)
+{
+ w->set_partinfo(partinfo);
+}
+
} /* extern "C" */