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>
32 #include "lh_widget.h"
33 #include "lh_widget_wrapped.h"
40 static gboolean expose_event_cb(GtkWidget *widget, GdkEvent *event,
42 static void size_allocate_cb(GtkWidget *widget, GdkRectangle *allocation,
44 static gboolean button_press_event(GtkWidget *widget, GdkEventButton *event,
46 static gboolean motion_notify_event(GtkWidget *widget, GdkEventButton *event,
48 static gboolean button_release_event(GtkWidget *widget, GdkEventButton *event,
51 lh_widget::lh_widget()
54 m_scrolled_window = gtk_scrolled_window_new(NULL, NULL);
55 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(m_scrolled_window),
56 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
57 g_signal_connect(m_scrolled_window, "size-allocate",
58 G_CALLBACK(size_allocate_cb), this);
61 GtkScrolledWindow *scw = GTK_SCROLLED_WINDOW(m_scrolled_window);
62 m_viewport = gtk_viewport_new(
63 gtk_scrolled_window_get_hadjustment(scw),
64 gtk_scrolled_window_get_vadjustment(scw));
65 gtk_container_add(GTK_CONTAINER(m_scrolled_window), m_viewport);
68 m_drawing_area = gtk_drawing_area_new();
69 gtk_container_add(GTK_CONTAINER(m_viewport), m_drawing_area);
70 g_signal_connect(m_drawing_area, "expose-event",
71 G_CALLBACK(expose_event_cb), this);
72 g_signal_connect(m_drawing_area, "motion_notify_event",
73 G_CALLBACK(motion_notify_event), this);
74 g_signal_connect(m_drawing_area, "button_press_event",
75 G_CALLBACK(button_press_event), this);
76 g_signal_connect(m_drawing_area, "button_release_event",
77 G_CALLBACK(button_release_event), this);
79 gtk_widget_show_all(m_scrolled_window);
83 m_context.load_master_stylesheet(master_css);
85 gtk_widget_set_events(m_drawing_area,
86 GDK_BUTTON_RELEASE_MASK
87 | GDK_BUTTON_PRESS_MASK
88 | GDK_POINTER_MOTION_MASK);
92 lh_widget::~lh_widget()
94 g_object_unref(m_drawing_area);
95 m_drawing_area = NULL;
96 g_object_unref(m_scrolled_window);
97 m_scrolled_window = NULL;
101 GtkWidget *lh_widget::get_widget() const
103 return m_scrolled_window;
106 void lh_widget::set_caption(const litehtml::tchar_t* caption)
108 g_log(NULL, G_LOG_LEVEL_MESSAGE, "lh_widget set_caption");
112 void lh_widget::set_base_url(const litehtml::tchar_t* base_url)
114 g_log(NULL, G_LOG_LEVEL_MESSAGE, "lh_widget set_base_url");
118 void lh_widget::on_anchor_click(const litehtml::tchar_t* url, const litehtml::element::ptr& el)
120 g_log(NULL, G_LOG_LEVEL_MESSAGE, "lh_widget on_anchor_click. url -> %s", url);
126 void lh_widget::import_css(litehtml::tstring& text, const litehtml::tstring& url, litehtml::tstring& baseurl)
128 g_log(NULL, G_LOG_LEVEL_MESSAGE, "lh_widget import_css");
129 baseurl = master_css;
132 void lh_widget::get_client_rect(litehtml::position& client) const
134 if (m_drawing_area == NULL)
137 client.width = m_rendered_width;
138 client.height = m_height;
142 // g_log(NULL, G_LOG_LEVEL_MESSAGE, "lh_widget::get_client_rect: %dx%d",
143 // client.width, client.height);
146 GdkPixbuf *lh_widget::get_image(const litehtml::tchar_t* url, bool redraw_on_ready)
148 GError *error = NULL;
149 GdkPixbuf *pixbuf = NULL;
151 g_log(NULL, G_LOG_LEVEL_MESSAGE, "Loading... %s", url);
152 gchar *msg = g_strdup_printf("Loading %s ...", url);
153 lh_widget_statusbar_push(msg);
157 GInputStream *image = http_loader.load_url(url, &error);
159 if (error || !image) {
161 g_log(NULL, G_LOG_LEVEL_WARNING, "lh_widget::get_image: Could not create pixbuf %s", error->message);
162 g_clear_error(&error);
167 pixbuf = gdk_pixbuf_new_from_stream(image, NULL, &error);
169 g_log(NULL, G_LOG_LEVEL_WARNING, "lh_widget::get_image: Could not create pixbuf %s", error->message);
170 //g_object_unref(pixbuf);
172 g_clear_error(&error);
174 g_input_stream_close(image, NULL, NULL);
176 /* if (redraw_on_ready) {
181 lh_widget_statusbar_pop();
186 void lh_widget::open_html(const gchar *contents)
188 lh_widget_statusbar_push("Loading HTML part ...");
189 m_html = litehtml::document::createFromString(contents, this, &m_context);
190 m_rendered_width = 0;
191 if (m_html != NULL) {
192 g_log(NULL, G_LOG_LEVEL_MESSAGE, "lh_widget::open_html created document");
195 lh_widget_statusbar_pop();
198 void lh_widget::draw(cairo_t *cr)
200 double x1, x2, y1, y2;
201 double width, height;
206 cairo_clip_extents(cr, &x1, &y1, &x2, &y2);
211 litehtml::position pos;
212 pos.width = (int)width;
213 pos.height = (int)height;
217 m_html->draw((litehtml::uint_ptr)cr, 0, 0, &pos);
220 void lh_widget::redraw()
227 if (m_html == NULL) {
228 g_log(NULL, G_LOG_LEVEL_WARNING, "lh_widget::redraw: No document!");
232 /* Get width of the viewport. */
233 gdkwin = gtk_viewport_get_view_window(GTK_VIEWPORT(m_viewport));
234 gdk_drawable_get_size(gdkwin, &width, NULL);
236 /* If the available width has changed, rerender the HTML content. */
237 if (m_rendered_width != width) {
238 g_log(NULL, G_LOG_LEVEL_MESSAGE,
239 "lh_widget::redraw: width changed: %d != %d",
240 m_rendered_width, width);
242 /* Update our internally stored width, mainly so that
243 * lh_widget::get_client_rect() gives correct width during the
245 m_rendered_width = width;
247 /* Re-render HTML for this width. */
248 m_html->media_changed();
249 m_html->render(m_rendered_width);
250 g_log(NULL, G_LOG_LEVEL_MESSAGE, "render is %dx%d",
251 m_html->width(), m_html->height());
253 /* Change drawing area's size to match what was rendered. */
254 gtk_widget_set_size_request(m_drawing_area,
255 m_html->width(), m_html->height());
260 /* Paint the rendered HTML. */
261 gdkwin = gtk_widget_get_window(m_drawing_area);
262 if (gdkwin == NULL) {
263 g_log(NULL, G_LOG_LEVEL_WARNING, "lh_widget::redraw: No GdkWindow to draw on!");
266 cr = gdk_cairo_create(GDK_DRAWABLE(gdkwin));
272 void lh_widget::paint_white()
274 GdkWindow *gdkwin = gtk_widget_get_window(m_drawing_area);
275 if (gdkwin == NULL) {
276 g_log(NULL, G_LOG_LEVEL_WARNING, "lh_widget::clear: No GdkWindow to draw on!");
279 cairo_t *cr = gdk_cairo_create(GDK_DRAWABLE(gdkwin));
281 /* Paint white background. */
283 gdk_drawable_get_size(gdkwin, &width, &height);
284 cairo_rectangle(cr, 0, 0, width, height);
285 cairo_set_source_rgb(cr, 255, 255, 255);
290 void lh_widget::clear()
293 m_rendered_width = 0;
296 void lh_widget::set_cursor(const litehtml::tchar_t* cursor)
298 //g_log(NULL, G_LOG_LEVEL_MESSAGE, "lh_widget set_cursor %s:%s", m_cursor, cursor);
301 if (m_cursor != cursor)
309 void lh_widget::update_cursor()
311 //g_log(NULL, G_LOG_LEVEL_MESSAGE, "lh_widget update_cursor %s", m_cursor);
312 GdkCursorType cursType = GDK_ARROW;
313 if(m_cursor == _t("pointer"))
315 cursType = GDK_HAND2;
317 if(cursType == GDK_ARROW)
319 lh_widget_statusbar_pop();
320 gdk_window_set_cursor(gtk_widget_get_window(m_drawing_area), NULL);
323 if (!m_clicked_url.empty()) {
324 lh_widget_statusbar_push(m_clicked_url.c_str());
326 gdk_window_set_cursor(gtk_widget_get_window(m_drawing_area), gdk_cursor_new(cursType));
330 void lh_widget::print()
332 g_log(NULL, G_LOG_LEVEL_MESSAGE, "lh_widget print");
333 gtk_widget_realize(GTK_WIDGET(m_drawing_area));
336 static gboolean expose_event_cb(GtkWidget *widget, GdkEvent *event,
339 lh_widget *w = (lh_widget *)user_data;
344 static void size_allocate_cb(GtkWidget *widget, GdkRectangle *allocation,
347 lh_widget *w = (lh_widget *)user_data;
349 g_log(NULL, G_LOG_LEVEL_MESSAGE, "size_allocate_cb: %dx%d",
350 allocation->width, allocation->height);
352 w->setHeight(allocation->height);
356 static gboolean button_press_event(GtkWidget *widget, GdkEventButton *event,
359 litehtml::position::vector redraw_boxes;
360 lh_widget *w = (lh_widget *)user_data;
362 g_log(NULL, G_LOG_LEVEL_MESSAGE, "lh_widget on_button_press_event");
366 if(w->m_html->on_lbutton_down((int) event->x, (int) event->y, (int) event->x, (int) event->y, redraw_boxes))
368 for(auto& pos : redraw_boxes)
370 g_log(NULL, G_LOG_LEVEL_MESSAGE, "x: %d y:%d w: %d h: %d", pos.x, pos.y, pos.width, pos.height);
371 gtk_widget_queue_draw_area(widget, pos.x, pos.y, pos.width, pos.height);
379 static gboolean motion_notify_event(GtkWidget *widget, GdkEventButton *event,
382 litehtml::position::vector redraw_boxes;
383 lh_widget *w = (lh_widget *)user_data;
385 //g_log(NULL, G_LOG_LEVEL_MESSAGE, "lh_widget on_motion_notify_event");
389 //if(m_cursor == _t("pointer"))
390 if(w->m_html->on_mouse_over((int) event->x, (int) event->y, (int) event->x, (int) event->y, redraw_boxes))
392 for (auto& pos : redraw_boxes)
394 g_log(NULL, G_LOG_LEVEL_MESSAGE, "x: %d y:%d w: %d h: %d", pos.x, pos.y, pos.width, pos.height);
395 gtk_widget_queue_draw_area(widget, pos.x, pos.y, pos.width, pos.height);
403 static gboolean button_release_event(GtkWidget *widget, GdkEventButton *event,
406 litehtml::position::vector redraw_boxes;
407 lh_widget *w = (lh_widget *)user_data;
408 GError* error = NULL;
410 g_log(NULL, G_LOG_LEVEL_MESSAGE, "lh_widget on_button_release_event");
414 w->m_clicked_url.clear();
415 if(w->m_html->on_lbutton_up((int) event->x, (int) event->y, (int) event->x, (int) event->y, redraw_boxes))
417 for (auto& pos : redraw_boxes)
419 g_log(NULL, G_LOG_LEVEL_MESSAGE, "x: %d y:%d w: %d h: %d", pos.x, pos.y, pos.width, pos.height);
420 gtk_widget_queue_draw_area(widget, pos.x, pos.y, pos.width, pos.height);
424 if (!w->m_clicked_url.empty())
426 g_log(NULL, G_LOG_LEVEL_MESSAGE, "Open in browser: %s", w->m_clicked_url.c_str());
427 gtk_show_uri(gdk_screen_get_default(),
428 w->m_clicked_url.c_str(),
429 GDK_CURRENT_TIME, &error);
431 g_log(NULL, G_LOG_LEVEL_WARNING, "Failed opening url(%s): %s", w->m_clicked_url, error->message);
432 g_clear_error(&error);
440 ///////////////////////////////////////////////////////////
443 lh_widget_wrapped *lh_widget_new()
445 return new lh_widget;
448 GtkWidget *lh_widget_get_widget(lh_widget_wrapped *w)
450 return w->get_widget();
453 void lh_widget_open_html(lh_widget_wrapped *w, const gchar *path)
458 void lh_widget_clear(lh_widget_wrapped *w)
463 void lh_widget_destroy(lh_widget_wrapped *w)
468 void lh_widget_print(lh_widget_wrapped *w) {