libyui-gtk  2.43.7
 All Classes
ygtkhtmlwrap.c
1 /********************************************************************
2  * YaST2-GTK - http://en.opensuse.org/YaST2-GTK *
3  ********************************************************************/
4 
5 /* YGtkHtmlWrap widget */
6 // check the header file for information about this widget
7 
8 #include <yui/Libyui_config.h>
9 #include <gtk/gtk.h>
10 #include <string.h>
11 #include "ygtkhtmlwrap.h"
12 
13 // ygutils
14 void ygutils_scrollAdj (GtkAdjustment *vadj, gboolean top);
15 
16 GtkWidget *ygtk_html_wrap_new (void)
17 {
18  GtkWidget *widget = g_object_new (ygtk_html_wrap_get_type(), NULL);
19  ygtk_html_wrap_init (widget);
20  return widget;
21 }
22 
23 //** WebKit
24 #ifdef USE_WEBKIT
25 #include <webkit/webkit.h>
26 
27 GType ygtk_html_wrap_get_type()
28 {
29  return WEBKIT_TYPE_WEB_VIEW;
30 }
31 
32 static void copy_activate_cb (GtkMenuItem *item, WebKitWebView *view)
33 { webkit_web_view_copy_clipboard (view); }
34 static void select_all_activate_cb (GtkMenuItem *item, WebKitWebView *view)
35 { webkit_web_view_select_all (view); }
36 
37 static gboolean button_press_event_cb (GtkWidget *widget, GdkEventButton *event,
38  WebKitWebView *view)
39 {
40  if (event && event->button != 3)
41  return FALSE;
42  // GtkMenu API is horrible for non-persistant menus. Make it persistant.
43  static GtkWidget *menu = 0;
44  if (menu)
45  gtk_widget_destroy (menu);
46  menu = gtk_menu_new();
47  // add a couple of items (based on GtkTextView)
48  GtkWidget *item;
49  item = gtk_image_menu_item_new_from_stock (GTK_STOCK_COPY, NULL);
50  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
51  if (webkit_web_view_can_copy_clipboard (view))
52  g_signal_connect (item, "activate", G_CALLBACK (copy_activate_cb), widget);
53  else
54  gtk_widget_set_sensitive (item, FALSE);
55  item = gtk_separator_menu_item_new();
56  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
57  item = gtk_image_menu_item_new_from_stock (GTK_STOCK_SELECT_ALL, NULL);
58  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
59  g_signal_connect (item, "activate", G_CALLBACK (select_all_activate_cb), widget);
60 
61  int button, event_time;
62  if (event) {
63  button = event->button;
64  event_time = event->time;
65  }
66  else {
67  button = 0;
68  event_time = gtk_get_current_event_time();
69  }
70  gtk_menu_attach_to_widget (GTK_MENU (menu), widget, NULL);
71  gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, event_time);
72  gtk_widget_show_all (menu);
73  return TRUE;
74 }
75 
76 static gboolean popup_menu_cb (GtkWidget *widget, WebKitWebView *view)
77 {
78  button_press_event_cb (widget, NULL, view);
79  return TRUE;
80 }
81 
82 static gboolean button_release_event_cb (GtkWidget *widget, GdkEventButton *event)
83 {
84  if (event->button != 3) return FALSE;
85  return TRUE;
86 }
87 
88 void ygtk_html_wrap_init (GtkWidget *widget)
89 {
90  /* WebKit popup menu has entries such as "reload", "go back" and "search web"
91  that we rather not show. "popup-menu" isn't working, use gtkwidget stuff... */
92  g_signal_connect (G_OBJECT (widget), "popup-menu",
93  G_CALLBACK (popup_menu_cb), widget);
94  g_signal_connect (G_OBJECT (widget), "button-press-event",
95  G_CALLBACK (button_press_event_cb), widget);
96  // webkit crashes if we don't overload the release event as well
97  g_signal_connect (G_OBJECT (widget), "button-release-event",
98  G_CALLBACK (button_release_event_cb), widget);
99 }
100 
101 // CSS roughly based on Yelp style
102 const char *CSS = "<style type=\"text/css\">"
103 "h1 { color: #5c5c5c; font-size: xx-large; font-weight: 900; border-bottom: thin solid cdcfd1; }"
104 "h2 { color: #5c5c5c; font-size: x-large; font-weight: 800; border-bottom: thin solid cdcfd1; }"
105 "h3 { color: #5c5c5c; font-size: large; font-weight: 700; border-bottom: thin solid cdcfd1; }"
106 "h4 { color: #5c5c5c; font-size: large; font-weight: 600; }"
107 "h5 { color: #5c5c5c; font-size: large; }"
108 "pre { background-color: #f0f0f0; border: thin solid #bfbfbf; padding: 2px; }"
109 "a { text-decoration: none; }"
110 "a:hover { text-decoration: underline; }"
111 "</style>";
112 
113 void ygtk_html_wrap_set_text (GtkWidget *widget, const gchar *text, gboolean plain_mode)
114 {
115  WebKitWebView *view = WEBKIT_WEB_VIEW (widget);
116  // webkit prepends base-uri to non-uri hrefs
117  if (plain_mode)
118  webkit_web_view_load_string (view, text, "text/plain", "UTF-8", "/");
119  else {
120  GString *str = NULL;
121  int i, last_i = 0;
122  for (i = 0; text[i]; i++) {
123  if (!strncmp (text+i, "href=", 5)) {
124  i += 5;
125  if (text[i] == '"')
126  i++;
127  int j;
128  for (j = i; text[j] && text[j] != ':'; j++)
129  if (text[j] == '"' || g_ascii_isspace (text[j])) {
130  if (!str)
131  str = g_string_new ("");
132  str = g_string_append_len (str, text+last_i, i-last_i);
133  last_i = i;
134  str = g_string_append (str, "label:/");
135  break;
136  }
137  }
138  }
139  if (str) {
140  str = g_string_append (str, text+last_i);
141  text = g_string_free (str, FALSE);
142  }
143 
144  const gchar *extra_css = g_object_get_data (G_OBJECT (widget), "extra-css");
145  if (!extra_css) extra_css = "";
146 
147  gchar *html = g_strdup_printf ("%s\n%s\n%s", CSS, extra_css, text);
148  if (str)
149  g_free ((gchar *) text);
150  webkit_web_view_load_string (view, html, "text/html", "UTF-8", "/");
151  g_free (html);
152  }
153 }
154 
155 void ygtk_html_wrap_scroll (GtkWidget *widget, gboolean top)
156 {
157  GtkWidget *scroll_win = gtk_widget_get_parent (widget);
158  ygutils_scrollAdj (gtk_scrolled_window_get_vadjustment (
159  GTK_SCROLLED_WINDOW (scroll_win)), top);
160 }
161 
162 gboolean ygtk_html_wrap_search (GtkWidget *widget, const gchar *text)
163 {
164  WebKitWebView *view = WEBKIT_WEB_VIEW (widget);
165  webkit_web_view_unmark_text_matches (view);
166  if (*text) {
167  gboolean found = webkit_web_view_mark_text_matches (view, text, FALSE, -1);
168  webkit_web_view_set_highlight_text_matches (view, TRUE);
169  if (found)
170  ygtk_html_wrap_search_next (widget, text);
171  return found;
172  }
173  // we want to un-select previous search (no such api though)
174  return TRUE;
175 }
176 
177 gboolean ygtk_html_wrap_search_next (GtkWidget *widget, const gchar *text)
178 {
179  WebKitWebView *view = WEBKIT_WEB_VIEW (widget);
180  return webkit_web_view_search_text (view, text, FALSE, TRUE, TRUE);
181 }
182 
183 static WebKitNavigationResponse ygtk_webkit_navigation_requested_cb (
184  WebKitWebView *view, WebKitWebFrame *frame, WebKitNetworkRequest *request, LinkClickedCb callback)
185 {
186  const gchar *uri = webkit_network_request_get_uri (request);
187  // look for set_text to see why we need to cut the uri in some cases
188  // (hint: not an uri)
189  if (!strncmp (uri, "label:/", sizeof ("label:/")-1))
190  uri = uri + sizeof ("label:/")-1;
191  gpointer data = g_object_get_data (G_OBJECT (view), "pointer");
192  (*callback) (GTK_WIDGET (view), uri, data);
193  return WEBKIT_NAVIGATION_RESPONSE_IGNORE;
194 }
195 
196 void ygtk_html_wrap_connect_link_clicked (GtkWidget *widget, LinkClickedCb callback, gpointer data)
197 {
198  g_object_set_data (G_OBJECT (widget), "pointer", data);
199  g_signal_connect (G_OBJECT (widget), "navigation-requested",
200  G_CALLBACK (ygtk_webkit_navigation_requested_cb), callback);
201 }
202 
203 void ygtk_html_wrap_set_background (GtkWidget *widget, GdkPixbuf *pixbuf, const gchar *filename)
204 {
205  int width = gdk_pixbuf_get_width (pixbuf);
206  gchar *bg_css = g_strdup_printf (
207  "<style type=\"text/css\">"
208  "body { background-image: url('%s'); background-repeat: no-repeat;"
209  "background-position: %dpx 100%%; background-attachment: fixed; }"
210  "</style>", filename, -width+40);
211  g_object_set_data_full (G_OBJECT (widget), "extra-css", bg_css, g_free);
212 }
213 
214 #else
215 
216 //** GtkHTML
217 #ifdef USE_GTKHTML
218 #include <gtkhtml/gtkhtml.h>
219 #include <gtkhtml/gtkhtml-stream.h>
220 #include <gtkhtml/gtkhtml-search.h>
221 
222 GType ygtk_html_wrap_get_type (void)
223 {
224  return GTK_TYPE_HTML;
225 }
226 
227 static void gtkhtml_url_requested_cb (GtkHTML *html, const gchar *url, GtkHTMLStream *stream)
228 { // to load images (and possibly other external embed files)
229  FILE *file = fopen (url, "rb");
230  if (!file) {
231  g_warning ("Error: couldn't open file '%s'\n", url);
232  return;
233  }
234 
235  fseek (file, 0, SEEK_END);
236  size_t file_size = ftell (file);
237  rewind (file);
238 
239  gboolean error;
240  gchar *data = g_new (gchar, file_size);
241  error = fread (data, 1, file_size, file) < file_size;
242  fclose (file);
243 
244  if (!error)
245  gtk_html_stream_write (stream, data, file_size);
246  g_free (data);
247 }
248 
249 void ygtk_html_wrap_init (GtkWidget *widget)
250 {
251  gtk_html_set_editable (GTK_HTML (widget), FALSE);
252  g_signal_connect (G_OBJECT (widget), "url-requested",
253  G_CALLBACK (gtkhtml_url_requested_cb), NULL);
254 }
255 
256 void ygtk_html_wrap_set_text (GtkWidget *widget, const gchar* text, gboolean plain_mode)
257 {
258  // TODO: implement plain_mode
259  GtkHTMLStream *stream = gtk_html_begin (GTK_HTML (widget));
260  gtk_html_write (GTK_HTML (widget), stream, text, strlen (text));
261  gtk_html_end (GTK_HTML (widget), stream, GTK_HTML_STREAM_OK);
262 }
263 
264 void ygtk_html_wrap_scroll (GtkWidget *widget, gboolean top)
265 {
266  ygutils_scrollAdj (GTK_LAYOUT (widget)->vadjustment, top);
267 }
268 
269 gboolean ygtk_html_wrap_search (GtkWidget *widget, const gchar *text)
270 {
271 /* if (*text == '\0')
272  return TRUE;*/
273  return gtk_html_engine_search (GTK_HTML (widget), text, FALSE, TRUE, FALSE);
274 }
275 
276 gboolean ygtk_html_wrap_search_next (GtkWidget *widget, const gchar *text)
277 {
278  return gtk_html_engine_search_next (GTK_HTML (widget));
279 }
280 
281 void ygtk_html_wrap_connect_link_clicked (GtkWidget *widget, LinkClickedCb callback, gpointer data)
282 {
283  g_signal_connect (G_OBJECT (widget), "link-clicked", G_CALLBACK (callback), data);
284 }
285 
286 void ygtk_html_wrap_set_background (GtkWidget *widget, GdkPixbuf *pixbuf, const gchar *filename)
287 {
288  // TODO
289 }
290 
291 #else
292 //** YGtkRichText (internal)
293 #include "ygtkrichtext.h"
294 
295 GType ygtk_html_wrap_get_type()
296 {
297  return YGTK_TYPE_RICH_TEXT;
298 }
299 
300 void ygtk_html_wrap_init (GtkWidget *widget)
301 {
302 }
303 
304 void ygtk_html_wrap_set_text (GtkWidget *widget, const gchar* text, gboolean plain_mode)
305 {
306  YGtkRichText *rtext = YGTK_RICH_TEXT (widget);
307  if (plain_mode)
308  ygtk_rich_text_set_plain_text (rtext, text);
309  else
310  ygtk_rich_text_set_text (rtext, text);
311 }
312 
313 void ygtk_html_wrap_scroll (GtkWidget *widget, gboolean top)
314 {
315  ygutils_scrollAdj (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (widget)), top);
316 }
317 
318 gboolean ygtk_html_wrap_search (GtkWidget *widget, const gchar *text)
319 {
320  gboolean ret = ygtk_rich_text_mark_text (YGTK_RICH_TEXT (widget), text);
321  ygtk_rich_text_forward_mark (YGTK_RICH_TEXT (widget), text);
322  return ret;
323 }
324 
325 gboolean ygtk_html_wrap_search_next (GtkWidget *widget, const gchar *text)
326 {
327  return ygtk_rich_text_forward_mark (YGTK_RICH_TEXT (widget), text);
328 }
329 
330 void ygtk_html_wrap_connect_link_clicked (GtkWidget *widget, LinkClickedCb callback, gpointer data)
331 {
332  g_signal_connect (G_OBJECT (widget), "link-clicked", G_CALLBACK (callback), data);
333 }
334 
335 void ygtk_html_wrap_set_background (GtkWidget *widget, GdkPixbuf *pixbuf, const gchar *filename)
336 {
337  ygtk_rich_text_set_background (YGTK_RICH_TEXT (widget), pixbuf);
338 }
339 
340 #endif
341 #endif
342