2 * Claws Mail -- A GTK+ based, lightweight, and fast e-mail client
3 * Copyright(C) 1999-2015 the Claws Mail Team
5 * This file Copyright (C) 2009-2015 Salvatore De Paolis
6 * <iwkse@claws-mail.org> and the Claws Mail Team
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write tothe Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 #include "claws-features.h"
26 #include <glib/gstdio.h>
28 #include <sys/types.h>
30 #include <curl/curl.h>
31 #include "lh_widget.h"
32 #include "lh_widget_wrapped.h"
41 static char* response_mime = NULL; /* response content-type. ex: "text/html" */
42 static char* response_data = NULL; /* response data from server. */
43 static size_t response_size = 0; /* response size of data */
45 static gboolean expose_event_cb(GtkWidget *widget, GdkEvent *event,
47 static void size_allocate_cb(GtkWidget *widget, GdkRectangle *allocation,
49 static size_t handle_returned_data(char* ptr, size_t size, size_t nmemb, void* stream);
50 static size_t handle_returned_header(void* ptr, size_t size, size_t nmemb, void* stream);
52 lh_widget::lh_widget()
55 m_scrolled_window = gtk_scrolled_window_new(NULL, NULL);
56 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(m_scrolled_window),
57 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
58 g_signal_connect(m_scrolled_window, "size-allocate",
59 G_CALLBACK(size_allocate_cb), this);
62 GtkScrolledWindow *scw = GTK_SCROLLED_WINDOW(m_scrolled_window);
63 m_viewport = gtk_viewport_new(
64 gtk_scrolled_window_get_hadjustment(scw),
65 gtk_scrolled_window_get_vadjustment(scw));
66 gtk_container_add(GTK_CONTAINER(m_scrolled_window), m_viewport);
69 m_drawing_area = gtk_drawing_area_new();
70 gtk_container_add(GTK_CONTAINER(m_viewport), m_drawing_area);
71 g_signal_connect(m_drawing_area, "expose-event",
72 G_CALLBACK(expose_event_cb), this);
74 gtk_widget_show_all(m_scrolled_window);
78 m_context.load_master_stylesheet(master_css);
82 lh_widget::~lh_widget()
84 g_object_unref(m_drawing_area);
85 m_drawing_area = NULL;
86 g_object_unref(m_scrolled_window);
87 m_scrolled_window = NULL;
90 g_input_stream_close(stream, NULL, NULL);
95 GtkWidget *lh_widget::get_widget() const
97 return m_scrolled_window;
100 void lh_widget::set_caption(const litehtml::tchar_t* caption)
102 g_log(NULL, G_LOG_LEVEL_MESSAGE, "lh_widget set_caption");
106 void lh_widget::set_base_url(const litehtml::tchar_t* base_url)
108 g_log(NULL, G_LOG_LEVEL_MESSAGE, "lh_widget set_base_url");
112 void lh_widget::on_anchor_click(const litehtml::tchar_t* url, const litehtml::element::ptr& el)
114 g_log(NULL, G_LOG_LEVEL_MESSAGE, "lh_widget on_anchor_click");
118 void lh_widget::set_cursor(const litehtml::tchar_t* cursor)
120 g_log(NULL, G_LOG_LEVEL_MESSAGE, "lh_widget set_cursor");
125 void lh_widget::import_css(litehtml::tstring& text, const litehtml::tstring& url, litehtml::tstring& baseurl)
127 g_log(NULL, G_LOG_LEVEL_MESSAGE, "lh_widget import_css");
128 baseurl = master_css;
131 void lh_widget::get_client_rect(litehtml::position& client) const
133 if (m_drawing_area == NULL)
136 client.width = m_rendered_width;
137 client.height = m_height;
141 // g_log(NULL, G_LOG_LEVEL_MESSAGE, "lh_widget::get_client_rect: %dx%d",
142 // client.width, client.height);
145 GdkPixbuf *lh_widget::get_image(const litehtml::tchar_t* url, bool redraw_on_ready)
147 GError *error = NULL;
148 GdkPixbuf *pixbuf = NULL;
150 g_log(NULL, G_LOG_LEVEL_MESSAGE, "Loading... %s", url);
152 GInputStream *image = load_url(url, &error);
154 g_log(NULL, G_LOG_LEVEL_MESSAGE, "Error: %s", error->message);
159 GdkPixbufLoader* loader = gdk_pixbuf_loader_new();
160 if (gdk_pixbuf_loader_write(loader, (const guchar*)response_data, response_size, &error)) {
161 pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
163 g_log(NULL, G_LOG_LEVEL_ERROR, "lh_widget::get_image: Could not create pixbuf");
165 gdk_pixbuf_loader_close(loader, NULL);
167 /* cleanup callback data */
168 if (response_mime) g_free(response_mime);
169 if (response_data) g_free(response_data);
170 response_data = NULL;
171 response_mime = NULL;
177 void lh_widget::open_html(const gchar *contents)
179 m_html = litehtml::document::createFromString(contents, this, &m_context);
180 m_rendered_width = 0;
181 if (m_html != NULL) {
182 g_log(NULL, G_LOG_LEVEL_MESSAGE, "lh_widget::open_html created document");
187 void lh_widget::draw(cairo_t *cr)
189 double x1, x2, y1, y2;
190 double width, height;
195 cairo_clip_extents(cr, &x1, &y1, &x2, &y2);
200 litehtml::position pos;
201 pos.width = (int)width;
202 pos.height = (int)height;
206 m_html->draw((litehtml::uint_ptr)cr, 0, 0, &pos);
209 void lh_widget::redraw()
216 if (m_html == NULL) {
217 g_log(NULL, G_LOG_LEVEL_WARNING, "lh_widget::redraw: No document!");
221 /* Get width of the viewport. */
222 gdkwin = gtk_viewport_get_view_window(GTK_VIEWPORT(m_viewport));
223 gdk_drawable_get_size(gdkwin, &width, NULL);
225 /* If the available width has changed, rerender the HTML content. */
226 if (m_rendered_width != width) {
227 g_log(NULL, G_LOG_LEVEL_MESSAGE,
228 "lh_widget::redraw: width changed: %d != %d",
229 m_rendered_width, width);
231 /* Update our internally stored width, mainly so that
232 * lh_widget::get_client_rect() gives correct width during the
234 m_rendered_width = width;
236 /* Re-render HTML for this width. */
237 m_html->media_changed();
238 m_html->render(m_rendered_width);
239 g_log(NULL, G_LOG_LEVEL_MESSAGE, "render is %dx%d",
240 m_html->width(), m_html->height());
242 /* Change drawing area's size to match what was rendered. */
243 gtk_widget_set_size_request(m_drawing_area,
244 m_html->width(), m_html->height());
249 /* Paint the rendered HTML. */
250 gdkwin = gtk_widget_get_window(m_drawing_area);
251 if (gdkwin == NULL) {
252 g_log(NULL, G_LOG_LEVEL_WARNING, "lh_widget::redraw: No GdkWindow to draw on!");
255 cr = gdk_cairo_create(GDK_DRAWABLE(gdkwin));
261 void lh_widget::paint_white()
263 GdkWindow *gdkwin = gtk_widget_get_window(m_drawing_area);
264 if (gdkwin == NULL) {
265 g_log(NULL, G_LOG_LEVEL_WARNING, "lh_widget::clear: No GdkWindow to draw on!");
268 cairo_t *cr = gdk_cairo_create(GDK_DRAWABLE(gdkwin));
270 /* Paint white background. */
272 gdk_drawable_get_size(gdkwin, &width, &height);
273 cairo_rectangle(cr, 0, 0, width, height);
274 cairo_set_source_rgb(cr, 255, 255, 255);
279 void lh_widget::clear()
282 m_rendered_width = 0;
285 GInputStream *lh_widget::load_url(const gchar *url, GError **error)
287 GError* _error = NULL;
289 CURLcode res = CURLE_OK;
293 /* initialize callback data */
294 response_mime = NULL;
295 response_data = NULL;
298 g_input_stream_close(stream, NULL, &_error);
300 if (error) *error = _error;
307 if (!strncmp(url, "file:///", 8) || g_file_test(url, G_FILE_TEST_EXISTS)) {
308 gchar* newurl = g_filename_from_uri(url, NULL, NULL);
309 if (g_file_get_contents(newurl ? newurl : url, &content, &len, &_error)) {
310 stream = g_memory_input_stream_new_from_data(content, len, g_free);
312 g_log(NULL, G_LOG_LEVEL_MESSAGE, "%s", _error->message);
316 curl = curl_easy_init();
317 if (!curl) return NULL;
318 curl_easy_setopt(curl, CURLOPT_URL, url);
319 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, handle_returned_data);
320 curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, handle_returned_header);
321 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
322 curl_easy_setopt(curl, CURLOPT_TIMEOUT, HTTP_GET_TIMEOUT);
323 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
324 res = curl_easy_perform(curl);
325 curl_easy_cleanup(curl);
326 if (res == CURLE_OK) {
327 stream = g_memory_input_stream_new_from_data(content, response_size, g_free);
329 _error = g_error_new_literal(G_FILE_ERROR, res, curl_easy_strerror(res));
332 if (error && _error) *error = _error;
337 static gboolean expose_event_cb(GtkWidget *widget, GdkEvent *event,
340 lh_widget *w = (lh_widget *)user_data;
345 static void size_allocate_cb(GtkWidget *widget, GdkRectangle *allocation,
348 lh_widget *w = (lh_widget *)user_data;
350 g_log(NULL, G_LOG_LEVEL_MESSAGE, "size_allocate_cb: %dx%d",
351 allocation->width, allocation->height);
353 w->setHeight(allocation->height);
357 static size_t handle_returned_data(char* ptr, size_t size, size_t nmemb, void* stream) {
359 response_data = (char*)malloc(size*nmemb);
361 response_data = (char*)realloc(response_data, response_size+size*nmemb);
363 memcpy(response_data+response_size, ptr, size*nmemb);
364 response_size += size*nmemb;
369 static size_t handle_returned_header(void* ptr, size_t size, size_t nmemb, void* stream) {
372 header = (char*) malloc(size*nmemb + 1);
373 memcpy(header, ptr, size*nmemb);
374 header[size*nmemb] = 0;
375 if (strncmp(header, "Content-Type: ", 14) == 0) {
376 char* stop = header + 14;
377 stop = strpbrk(header + 14, "\r\n;");
379 response_mime = strdup(header + 14);
385 ///////////////////////////////////////////////////////////
388 lh_widget_wrapped *lh_widget_new()
390 return new lh_widget;
393 GtkWidget *lh_widget_get_widget(lh_widget_wrapped *w)
395 return w->get_widget();
398 void lh_widget_open_html(lh_widget_wrapped *w, const gchar *path)
403 void lh_widget_clear(lh_widget_wrapped *w)
408 void lh_widget_destroy(lh_widget_wrapped *w)