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"
25 #include "container_linux.h"
29 #define _USE_MATH_DEFINES
33 # define M_PI 3.14159265358979323846
36 container_linux::container_linux(void)
38 m_temp_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 2, 2);
39 m_temp_cr = cairo_create(m_temp_surface);
42 container_linux::~container_linux(void)
45 cairo_surface_destroy(m_temp_surface);
46 cairo_destroy(m_temp_cr);
49 litehtml::uint_ptr container_linux::create_font( const litehtml::tchar_t* faceName, int size, int weight, litehtml::font_style italic, unsigned int decoration, litehtml::font_metrics* fm )
51 litehtml::string_vector fonts;
52 litehtml::split_string(faceName, fonts, ",");
53 if (! fonts.empty()) {
54 litehtml::trim(fonts[0]);
57 cairo_font_face_t* fnt = 0;
59 FcPattern *pattern = FcPatternCreate();
61 for(litehtml::string_vector::iterator i = fonts.begin(); i != fonts.end(); i++)
63 if(FcPatternAddString(pattern, FC_FAMILY, (unsigned char *) i->c_str()))
71 if(italic == litehtml::fontStyleItalic )
73 FcPatternAddInteger (pattern, FC_SLANT, FC_SLANT_ITALIC);
76 FcPatternAddInteger (pattern, FC_SLANT, FC_SLANT_ROMAN);
79 int fc_weight = FC_WEIGHT_NORMAL;
80 if(weight >= 0 && weight < 150) fc_weight = FC_WEIGHT_THIN;
81 else if(weight >= 150 && weight < 250) fc_weight = FC_WEIGHT_EXTRALIGHT;
82 else if(weight >= 250 && weight < 350) fc_weight = FC_WEIGHT_LIGHT;
83 else if(weight >= 350 && weight < 450) fc_weight = FC_WEIGHT_NORMAL;
84 else if(weight >= 450 && weight < 550) fc_weight = FC_WEIGHT_MEDIUM;
85 else if(weight >= 550 && weight < 650) fc_weight = FC_WEIGHT_SEMIBOLD;
86 else if(weight >= 650 && weight < 750) fc_weight = FC_WEIGHT_BOLD;
87 else if(weight >= 750 && weight < 850) fc_weight = FC_WEIGHT_EXTRABOLD;
88 else if(weight >= 950) fc_weight = FC_WEIGHT_BLACK;
90 FcPatternAddInteger (pattern, FC_WEIGHT, fc_weight);
92 fnt = cairo_ft_font_face_create_for_pattern(pattern);
95 FcPatternDestroy(pattern);
101 cairo_save(m_temp_cr);
103 cairo_set_font_face(m_temp_cr, fnt);
104 cairo_set_font_size(m_temp_cr, size);
105 cairo_font_extents_t ext;
106 cairo_font_extents(m_temp_cr, &ext);
108 cairo_text_extents_t tex;
109 cairo_text_extents(m_temp_cr, "x", &tex);
111 fm->ascent = (int) ext.ascent;
112 fm->descent = (int) ext.descent;
113 fm->height = (int) (ext.ascent + ext.descent);
114 fm->x_height = (int) tex.height;
116 cairo_restore(m_temp_cr);
118 ret = new cairo_font;
121 ret->strikeout = (decoration & litehtml::font_decoration_linethrough) ? true : false;
122 ret->underline = (decoration & litehtml::font_decoration_underline) ? true : false;
126 return (litehtml::uint_ptr) ret;
129 void container_linux::delete_font( litehtml::uint_ptr hFont )
131 cairo_font* fnt = (cairo_font*) hFont;
134 cairo_font_face_destroy(fnt->font);
139 int container_linux::text_width( const litehtml::tchar_t* text, litehtml::uint_ptr hFont )
141 cairo_font* fnt = (cairo_font*) hFont;
143 cairo_save(m_temp_cr);
146 cairo_set_font_size(m_temp_cr, fnt->size);
147 cairo_set_font_face(m_temp_cr, fnt->font);
149 cairo_text_extents_t ext;
150 cairo_text_extents(m_temp_cr, text, &ext);
152 cairo_restore(m_temp_cr);
154 return (int) ext.x_advance;
157 void container_linux::draw_text( litehtml::uint_ptr hdc, const litehtml::tchar_t* text, litehtml::uint_ptr hFont, litehtml::web_color color, const litehtml::position& pos )
159 cairo_font* fnt = (cairo_font*) hFont;
160 cairo_t* cr = (cairo_t*) hdc;
166 cairo_set_font_face(cr, fnt->font);
167 cairo_set_font_size(cr, fnt->size);
169 cairo_font_extents_t ext;
170 cairo_font_extents(cr, &ext);
173 int y = pos.bottom() - ext.descent;
175 set_color(cr, color);
177 cairo_move_to(cr, x, y);
178 cairo_show_text(cr, text);
183 if(fnt->underline || fnt->strikeout)
185 tw = text_width(text, hFont);
190 cairo_set_line_width(cr, 1);
191 cairo_move_to(cr, x, y + 1.5);
192 cairo_line_to(cr, x + tw, y + 1.5);
197 cairo_text_extents_t tex;
198 cairo_text_extents(cr, "x", &tex);
200 int ln_y = y - tex.height / 2.0;
202 cairo_set_line_width(cr, 1);
203 cairo_move_to(cr, x, (double) ln_y - 0.5);
204 cairo_line_to(cr, x + tw, (double) ln_y - 0.5);
212 int container_linux::pt_to_px( int pt )
214 GdkScreen* screen = gdk_screen_get_default();
215 double dpi = gdk_screen_get_resolution(screen);
217 return (int) ((double) pt * dpi / 72.0);
220 int container_linux::get_default_font_size() const
225 void container_linux::draw_list_marker( litehtml::uint_ptr hdc, const litehtml::list_marker& marker )
227 if(!marker.image.empty())
229 /*litehtml::tstring url;
230 make_url(marker.image.c_str(), marker.baseurl, url);
233 images_map::iterator img_i = m_images.find(url.c_str());
234 if(img_i != m_images.end())
238 draw_txdib((cairo_t*) hdc, img_i->second, marker.pos.x, marker.pos.y, marker.pos.width, marker.pos.height);
241 unlock_images_cache();*/
244 switch(marker.marker_type)
246 case litehtml::list_style_type_circle:
248 draw_ellipse((cairo_t*) hdc, marker.pos.x, marker.pos.y, marker.pos.width, marker.pos.height, marker.color, 0.5);
251 case litehtml::list_style_type_disc:
253 fill_ellipse((cairo_t*) hdc, marker.pos.x, marker.pos.y, marker.pos.width, marker.pos.height, marker.color);
256 case litehtml::list_style_type_square:
259 cairo_t* cr = (cairo_t*) hdc;
263 cairo_rectangle(cr, marker.pos.x, marker.pos.y, marker.pos.width, marker.pos.height);
265 set_color(cr, marker.color);
277 void container_linux::load_image( const litehtml::tchar_t* src, const litehtml::tchar_t* baseurl, bool redraw_on_ready )
279 litehtml::tstring url;
280 make_url(src, baseurl, url);
283 for (auto ii = m_images.cbegin(); ii != m_images.cend(); ++ii) {
284 const image *i = &(*ii);
286 if (!strcmp(i->first.c_str(), url.c_str())) {
296 GdkPixbuf *img = get_image(url.c_str(), true);
299 m_images.push_back(std::make_pair(url, img));
309 void container_linux::get_image_size( const litehtml::tchar_t* src, const litehtml::tchar_t* baseurl, litehtml::size& sz )
311 litehtml::tstring url;
312 make_url(src, baseurl, url);
314 const image *img = NULL;
316 for (auto ii = m_images.cbegin(); ii != m_images.cend(); ++ii) {
317 const image *i = &(*ii);
318 if (i->first == url) {
327 sz.width = gdk_pixbuf_get_width(img->second);
328 sz.height = gdk_pixbuf_get_height(img->second);
336 void container_linux::draw_background( litehtml::uint_ptr hdc, const litehtml::background_paint& bg )
338 cairo_t* cr = (cairo_t*) hdc;
342 rounded_rectangle(cr, bg.border_box, bg.border_radius);
345 cairo_rectangle(cr, bg.clip_box.x, bg.clip_box.y, bg.clip_box.width, bg.clip_box.height);
350 set_color(cr, bg.color);
354 litehtml::tstring url;
355 make_url(bg.image.c_str(), bg.baseurl.c_str(), url);
357 //lock_images_cache();
359 const image *img_i = NULL;
361 for (auto ii = m_images.cbegin(); ii != m_images.cend(); ++ii) {
362 const image *i = &(*ii);
363 if (i->first == url) {
370 if(img_i != NULL && img_i->second)
372 GdkPixbuf *bgbmp = img_i->second;
375 if(bg.image_size.width != gdk_pixbuf_get_width(bgbmp) || bg.image_size.height != gdk_pixbuf_get_height(bgbmp))
377 new_img = gdk_pixbuf_scale_simple(bgbmp, bg.image_size.width, bg.image_size.height, GDK_INTERP_BILINEAR);
381 cairo_surface_t* img = surface_from_pixbuf(bgbmp);
382 cairo_pattern_t *pattern = cairo_pattern_create_for_surface(img);
383 cairo_matrix_t flib_m;
384 cairo_matrix_init_identity(&flib_m);
385 cairo_matrix_translate(&flib_m, -bg.position_x, -bg.position_y);
386 cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
387 cairo_pattern_set_matrix (pattern, &flib_m);
391 case litehtml::background_repeat_no_repeat:
392 draw_pixbuf(cr, bgbmp, bg.position_x, bg.position_y, gdk_pixbuf_get_width(bgbmp), gdk_pixbuf_get_height(bgbmp));
395 case litehtml::background_repeat_repeat_x:
396 cairo_set_source(cr, pattern);
397 cairo_rectangle(cr, bg.clip_box.left(), bg.position_y, bg.clip_box.width, gdk_pixbuf_get_height(bgbmp));
401 case litehtml::background_repeat_repeat_y:
402 cairo_set_source(cr, pattern);
403 cairo_rectangle(cr, bg.position_x, bg.clip_box.top(), gdk_pixbuf_get_width(bgbmp), bg.clip_box.height);
407 case litehtml::background_repeat_repeat:
408 cairo_set_source(cr, pattern);
409 cairo_rectangle(cr, bg.clip_box.left(), bg.clip_box.top(), bg.clip_box.width, bg.clip_box.height);
414 cairo_pattern_destroy(pattern);
415 cairo_surface_destroy(img);
418 // unlock_images_cache();
422 void container_linux::make_url(const litehtml::tchar_t* url, const litehtml::tchar_t* basepath, litehtml::tstring& out)
427 void container_linux::add_path_arc(cairo_t* cr, double x, double y, double rx, double ry, double a1, double a2, bool neg)
434 cairo_translate(cr, x, y);
435 cairo_scale(cr, 1, ry / rx);
436 cairo_translate(cr, -x, -y);
440 cairo_arc_negative(cr, x, y, rx, a1, a2);
443 cairo_arc(cr, x, y, rx, a1, a2);
449 cairo_move_to(cr, x, y);
453 void container_linux::draw_borders(litehtml::uint_ptr hdc, const litehtml::borders& borders, const litehtml::position& draw_pos, bool root)
455 cairo_t* cr = (cairo_t*) hdc;
466 if(borders.top.width != 0 && borders.top.style > litehtml::border_style_hidden)
468 bdr_top = (int) borders.top.width;
470 if(borders.bottom.width != 0 && borders.bottom.style > litehtml::border_style_hidden)
472 bdr_bottom = (int) borders.bottom.width;
474 if(borders.left.width != 0 && borders.left.style > litehtml::border_style_hidden)
476 bdr_left = (int) borders.left.width;
478 if(borders.right.width != 0 && borders.right.style > litehtml::border_style_hidden)
480 bdr_right = (int) borders.right.width;
486 set_color(cr, borders.right.color);
488 double r_top = borders.radius.top_right_x;
489 double r_bottom = borders.radius.bottom_right_x;
493 double end_angle = 2 * M_PI;
494 double start_angle = end_angle - M_PI / 2.0 / ((double) bdr_top / (double) bdr_right + 1);
497 draw_pos.right() - r_top,
498 draw_pos.top() + r_top,
500 r_top - bdr_right + (bdr_right - bdr_top),
505 draw_pos.right() - r_top,
506 draw_pos.top() + r_top,
513 cairo_move_to(cr, draw_pos.right() - bdr_right, draw_pos.top() + bdr_top);
514 cairo_line_to(cr, draw_pos.right(), draw_pos.top());
519 cairo_line_to(cr, draw_pos.right(), draw_pos.bottom() - r_bottom);
521 double start_angle = 0;
522 double end_angle = start_angle + M_PI / 2.0 / ((double) bdr_bottom / (double) bdr_right + 1);
525 draw_pos.right() - r_bottom,
526 draw_pos.bottom() - r_bottom,
533 draw_pos.right() - r_bottom,
534 draw_pos.bottom() - r_bottom,
535 r_bottom - bdr_right,
536 r_bottom - bdr_right + (bdr_right - bdr_bottom),
541 cairo_line_to(cr, draw_pos.right(), draw_pos.bottom());
542 cairo_line_to(cr, draw_pos.right() - bdr_right, draw_pos.bottom() - bdr_bottom);
548 // draw bottom border
551 set_color(cr, borders.bottom.color);
553 double r_left = borders.radius.bottom_left_x;
554 double r_right = borders.radius.bottom_right_x;
558 double start_angle = M_PI / 2.0;
559 double end_angle = start_angle + M_PI / 2.0 / ((double) bdr_left / (double) bdr_bottom + 1);
562 draw_pos.left() + r_left,
563 draw_pos.bottom() - r_left,
564 r_left - bdr_bottom + (bdr_bottom - bdr_left),
570 draw_pos.left() + r_left,
571 draw_pos.bottom() - r_left,
578 cairo_move_to(cr, draw_pos.left(), draw_pos.bottom());
579 cairo_line_to(cr, draw_pos.left() + bdr_left, draw_pos.bottom() - bdr_bottom);
584 cairo_line_to(cr, draw_pos.right() - r_right, draw_pos.bottom());
586 double end_angle = M_PI / 2.0;
587 double start_angle = end_angle - M_PI / 2.0 / ((double) bdr_right / (double) bdr_bottom + 1);
590 draw_pos.right() - r_right,
591 draw_pos.bottom() - r_right,
598 draw_pos.right() - r_right,
599 draw_pos.bottom() - r_right,
600 r_right - bdr_bottom + (bdr_bottom - bdr_right),
601 r_right - bdr_bottom,
606 cairo_line_to(cr, draw_pos.right() - bdr_right, draw_pos.bottom() - bdr_bottom);
607 cairo_line_to(cr, draw_pos.right(), draw_pos.bottom());
616 set_color(cr, borders.top.color);
618 double r_left = borders.radius.top_left_x;
619 double r_right = borders.radius.top_right_x;
623 double end_angle = M_PI * 3.0 / 2.0;
624 double start_angle = end_angle - M_PI / 2.0 / ((double) bdr_left / (double) bdr_top + 1);
627 draw_pos.left() + r_left,
628 draw_pos.top() + r_left,
635 draw_pos.left() + r_left,
636 draw_pos.top() + r_left,
637 r_left - bdr_top + (bdr_top - bdr_left),
643 cairo_move_to(cr, draw_pos.left(), draw_pos.top());
644 cairo_line_to(cr, draw_pos.left() + bdr_left, draw_pos.top() + bdr_top);
649 cairo_line_to(cr, draw_pos.right() - r_right, draw_pos.top() + bdr_top);
651 double start_angle = M_PI * 3.0 / 2.0;
652 double end_angle = start_angle + M_PI / 2.0 / ((double) bdr_right / (double) bdr_top + 1);
655 draw_pos.right() - r_right,
656 draw_pos.top() + r_right,
657 r_right - bdr_top + (bdr_top - bdr_right),
663 draw_pos.right() - r_right,
664 draw_pos.top() + r_right,
671 cairo_line_to(cr, draw_pos.right() - bdr_right, draw_pos.top() + bdr_top);
672 cairo_line_to(cr, draw_pos.right(), draw_pos.top());
681 set_color(cr, borders.left.color);
683 double r_top = borders.radius.top_left_x;
684 double r_bottom = borders.radius.bottom_left_x;
688 double start_angle = M_PI;
689 double end_angle = start_angle + M_PI / 2.0 / ((double) bdr_top / (double) bdr_left + 1);
692 draw_pos.left() + r_top,
693 draw_pos.top() + r_top,
695 r_top - bdr_left + (bdr_left - bdr_top),
700 draw_pos.left() + r_top,
701 draw_pos.top() + r_top,
708 cairo_move_to(cr, draw_pos.left() + bdr_left, draw_pos.top() + bdr_top);
709 cairo_line_to(cr, draw_pos.left(), draw_pos.top());
714 cairo_line_to(cr, draw_pos.left(), draw_pos.bottom() - r_bottom);
716 double end_angle = M_PI;
717 double start_angle = end_angle - M_PI / 2.0 / ((double) bdr_bottom / (double) bdr_left + 1);
720 draw_pos.left() + r_bottom,
721 draw_pos.bottom() - r_bottom,
728 draw_pos.left() + r_bottom,
729 draw_pos.bottom() - r_bottom,
731 r_bottom - bdr_left + (bdr_left - bdr_bottom),
736 cairo_line_to(cr, draw_pos.left(), draw_pos.bottom());
737 cairo_line_to(cr, draw_pos.left() + bdr_left, draw_pos.bottom() - bdr_bottom);
745 void container_linux::transform_text(litehtml::tstring& text, litehtml::text_transform tt)
750 void container_linux::set_clip( const litehtml::position& pos, const litehtml::border_radiuses& bdr_radius, bool valid_x, bool valid_y )
752 litehtml::position clip_pos = pos;
753 litehtml::position client_pos;
754 get_client_rect(client_pos);
757 clip_pos.x = client_pos.x;
758 clip_pos.width = client_pos.width;
762 clip_pos.y = client_pos.y;
763 clip_pos.height = client_pos.height;
765 m_clips.emplace_back(clip_pos, bdr_radius);
768 void container_linux::del_clip()
776 void container_linux::apply_clip( cairo_t* cr )
778 for(const auto& clip_box : m_clips)
780 rounded_rectangle(cr, clip_box.box, clip_box.radius);
785 void container_linux::draw_ellipse( cairo_t* cr, int x, int y, int width, int height, const litehtml::web_color& color, int line_width )
794 cairo_translate (cr, x + width / 2.0, y + height / 2.0);
795 cairo_scale (cr, width / 2.0, height / 2.0);
796 cairo_arc (cr, 0, 0, 1, 0, 2 * M_PI);
798 set_color(cr, color);
799 cairo_set_line_width(cr, line_width);
805 void container_linux::fill_ellipse( cairo_t* cr, int x, int y, int width, int height, const litehtml::web_color& color )
814 cairo_translate (cr, x + width / 2.0, y + height / 2.0);
815 cairo_scale (cr, width / 2.0, height / 2.0);
816 cairo_arc (cr, 0, 0, 1, 0, 2 * M_PI);
818 set_color(cr, color);
824 void container_linux::clear_images()
826 for(auto i = m_images.begin(); i != m_images.end(); ++i) {
830 g_object_unref(img->second);
837 void container_linux::clear_images(gint desired_size)
841 /* First, tally up size of all the stored GdkPixbufs and
842 * deallocate those which make the total size be above
843 * the desired_size limit. We will remove their list
845 for (auto i = m_images.rbegin(); i != m_images.rend(); ++i) {
849 if (img->second == NULL)
852 cursize = gdk_pixbuf_get_byte_length(img->second);
854 if (size + cursize > desired_size) {
855 g_object_unref(img->second);
862 /* Remove elements whose GdkPixbuf pointers point to NULL. */
863 m_images.remove_if([&](image _img) -> bool {
864 if (_img.second == NULL)
870 const litehtml::tchar_t* container_linux::get_default_font_name() const
872 return "Times New Roman";
875 std::shared_ptr<litehtml::element> container_linux::create_element(const litehtml::tchar_t *tag_name,
876 const litehtml::string_map &attributes,
877 const std::shared_ptr<litehtml::document> &doc)
882 void container_linux::rounded_rectangle( cairo_t* cr, const litehtml::position &pos, const litehtml::border_radiuses &radius )
885 if(radius.top_left_x)
887 cairo_arc(cr, pos.left() + radius.top_left_x, pos.top() + radius.top_left_x, radius.top_left_x, M_PI, M_PI * 3.0 / 2.0);
890 cairo_move_to(cr, pos.left(), pos.top());
893 cairo_line_to(cr, pos.right() - radius.top_right_x, pos.top());
895 if(radius.top_right_x)
897 cairo_arc(cr, pos.right() - radius.top_right_x, pos.top() + radius.top_right_x, radius.top_right_x, M_PI * 3.0 / 2.0, 2.0 * M_PI);
900 cairo_line_to(cr, pos.right(), pos.bottom() - radius.bottom_right_x);
902 if(radius.bottom_right_x)
904 cairo_arc(cr, pos.right() - radius.bottom_right_x, pos.bottom() - radius.bottom_right_x, radius.bottom_right_x, 0, M_PI / 2.0);
907 cairo_line_to(cr, pos.left() - radius.bottom_left_x, pos.bottom());
909 if(radius.bottom_left_x)
911 cairo_arc(cr, pos.left() + radius.bottom_left_x, pos.bottom() - radius.bottom_left_x, radius.bottom_left_x, M_PI / 2.0, M_PI);
915 void container_linux::draw_pixbuf(cairo_t* cr, const GdkPixbuf *bmp, int x, int y, int cx, int cy)
920 cairo_matrix_t flib_m;
921 cairo_matrix_init(&flib_m, 1, 0, 0, -1, 0, 0);
923 if(cx != gdk_pixbuf_get_width(bmp) || cy != gdk_pixbuf_get_height(bmp))
925 GdkPixbuf *new_img = gdk_pixbuf_scale_simple(bmp, cx, cy, GDK_INTERP_BILINEAR);
926 gdk_cairo_set_source_pixbuf(cr, new_img, x, y);
930 gdk_cairo_set_source_pixbuf(cr, bmp, x, y);
938 cairo_surface_t* container_linux::surface_from_pixbuf(const GdkPixbuf *bmp)
940 cairo_surface_t* ret = NULL;
942 if(gdk_pixbuf_get_has_alpha(bmp))
944 ret = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, gdk_pixbuf_get_width(bmp), gdk_pixbuf_get_height(bmp));
947 ret = cairo_image_surface_create(CAIRO_FORMAT_RGB24, gdk_pixbuf_get_width(bmp), gdk_pixbuf_get_height(bmp));
950 // Cairo::RefPtr<Cairo::Surface> surface(new Cairo::Surface(ret, false));
951 // Cairo::RefPtr<Cairo::Context> ctx = Cairo::Context::create(surface);
952 // Gdk::Cairo::set_source_pixbuf(ctx, bmp, 0.0, 0.0);
953 cairo_t *ctx = cairo_create(ret);
960 void container_linux::get_media_features(litehtml::media_features& media) const
962 litehtml::position client;
963 get_client_rect(client);
964 media.type = litehtml::media_type_screen;
965 media.width = client.width;
966 media.height = client.height;
967 media.device_width = gdk_screen_width();
968 media.device_height = gdk_screen_height();
970 media.monochrome = 0;
971 media.color_index = 256;
972 media.resolution = 96;
975 void container_linux::get_language(litehtml::tstring& language, litehtml::tstring& culture) const
981 void container_linux::link(const std::shared_ptr<litehtml::document> &ptr, const litehtml::element::ptr& el)