PageRenderTime 45ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/libgebr/gui/gebr-gui-html-viewer-widget.c

https://code.google.com/p/gebr/
C | 490 lines | 353 code | 85 blank | 52 comment | 38 complexity | ce68c546d260e29ea6542f998fd6daa1 MD5 | raw file
Possible License(s): LGPL-2.1, GPL-2.0
  1. /* GeBR - An environment for seismic processing.
  2. * Copyright (C) 2007-2009 GeBR core team (http://sites.google.com/site/gebr_guiproject/)
  3. *
  4. * This program is free software: you can redistribute it and/or
  5. * modify it under the terms of the GNU General Public License as
  6. * published by the Free Software Foundation, either version 3 of
  7. * the License, or * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see
  16. * <http://www.gnu.org/licenses/>.
  17. */
  18. #ifdef HAVE_CONFIG_H
  19. # include <config.h>
  20. #endif
  21. #include "../libgebr-gettext.h"
  22. #include <glib.h>
  23. #include <glib/gi18n-lib.h>
  24. #include <glib/gstdio.h>
  25. #include <geoxml.h>
  26. #include <regex.h>
  27. #include <string.h>
  28. #include <webkit/webkit.h>
  29. #include "gebr-gui-html-viewer-widget.h"
  30. #include "../utils.h"
  31. #include "gebr-gui-utils.h"
  32. #include "gebr-gui-js.h"
  33. #define CSS_LINK "<link rel=\"stylesheet\" type=\"text/css\" href=\"file://"LIBGEBR_DATA_DIR"/gebr.css\" />"
  34. enum {
  35. TITLE_READY,
  36. PRINT_REQUESTED,
  37. LAST_SIGNAL
  38. };
  39. static guint signals[ LAST_SIGNAL ] = { 0 };
  40. typedef struct _GebrGuiHtmlViewerWidgetPrivate GebrGuiHtmlViewerWidgetPrivate;
  41. struct _GebrGuiHtmlViewerWidgetPrivate {
  42. GtkWidget * web_view;
  43. GebrGeoXmlObject *object;
  44. gchar *tmp_file;
  45. GebrGuiHtmlViewerCustomTab callback;
  46. gchar * label;
  47. GString *content;
  48. };
  49. #define GEBR_GUI_HTML_VIEWER_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), GEBR_GUI_TYPE_HTML_VIEWER_WIDGET, GebrGuiHtmlViewerWidgetPrivate))
  50. //==============================================================================
  51. // PROTOTYPES =
  52. //==============================================================================
  53. static void on_load_finished(WebKitWebView * web_view, WebKitWebFrame * frame, GebrGuiHtmlViewerWidget *self);
  54. static WebKitNavigationResponse on_navigation_requested(WebKitWebView * web_view, WebKitWebFrame *frame,
  55. WebKitNetworkRequest *request, GebrGuiHtmlViewerWidget *self);
  56. static void gebr_gui_html_viewer_widget_finalize (GObject *object);
  57. G_DEFINE_TYPE(GebrGuiHtmlViewerWidget, gebr_gui_html_viewer_widget, GTK_TYPE_VBOX);
  58. //==============================================================================
  59. // GOBJECT RELATED FUNCTIONS =
  60. //==============================================================================
  61. static void gebr_gui_html_viewer_widget_class_init(GebrGuiHtmlViewerWidgetClass * klass)
  62. {
  63. GObjectClass *gobject_class;
  64. gobject_class = G_OBJECT_CLASS (klass);
  65. gobject_class->finalize = gebr_gui_html_viewer_widget_finalize;
  66. /**
  67. * GebrGuiHtmlViewerWidget::title-ready:
  68. * @widget: This #GebrGuiHtmlViewerWidget
  69. * @title: The title.
  70. *
  71. * This signal is fired when the title is ready to be set. The title is passed by parameter @title, and its
  72. * value may depend on context. If this viewer is showing the help of a geoxml-menu, the title is the same as
  73. * the menu. Otherwise, the title is defined by the &lt;title&gt; tag inside the Html.
  74. */
  75. signals[ TITLE_READY ] =
  76. g_signal_new("title-ready",
  77. GEBR_GUI_TYPE_HTML_VIEWER_WIDGET,
  78. G_SIGNAL_RUN_LAST,
  79. G_STRUCT_OFFSET(GebrGuiHtmlViewerWidgetClass, title_ready),
  80. NULL, NULL,
  81. g_cclosure_marshal_VOID__STRING,
  82. G_TYPE_NONE,
  83. 1,
  84. G_TYPE_STRING);
  85. /**
  86. * GebrGuiHtmlViewerWidget::print-requested:
  87. * Emitted when the user requests a print job.
  88. */
  89. signals[ PRINT_REQUESTED ] =
  90. g_signal_new("print-requested",
  91. GEBR_GUI_TYPE_HTML_VIEWER_WIDGET,
  92. G_SIGNAL_RUN_LAST,
  93. G_STRUCT_OFFSET(GebrGuiHtmlViewerWidgetClass, print_requested),
  94. NULL, NULL,
  95. g_cclosure_marshal_VOID__VOID,
  96. G_TYPE_NONE, 0);
  97. g_type_class_add_private(klass, sizeof(GebrGuiHtmlViewerWidgetPrivate));
  98. }
  99. static void gebr_gui_html_viewer_widget_init(GebrGuiHtmlViewerWidget * self)
  100. {
  101. GString *tmp_file;
  102. GtkWidget * scrolled_window;
  103. GebrGuiHtmlViewerWidgetPrivate * priv;
  104. priv = GEBR_GUI_HTML_VIEWER_WIDGET_GET_PRIVATE(self);
  105. priv->object = NULL;
  106. priv->web_view = webkit_web_view_new();
  107. priv->callback = NULL;
  108. priv->content = g_string_new (NULL);
  109. tmp_file = gebr_make_temp_filename("XXXXXX.html");
  110. priv->tmp_file = g_string_free (tmp_file, FALSE);
  111. g_signal_connect(priv->web_view, "load-finished", G_CALLBACK(on_load_finished), self);
  112. scrolled_window = gtk_scrolled_window_new(NULL, NULL);
  113. gtk_container_add(GTK_CONTAINER(scrolled_window), priv->web_view);
  114. gtk_box_pack_start(GTK_BOX(self), scrolled_window, TRUE, TRUE, 0);
  115. gtk_widget_show_all(scrolled_window);
  116. }
  117. static void gebr_gui_html_viewer_widget_finalize(GObject *object)
  118. {
  119. GebrGuiHtmlViewerWidgetPrivate * priv;
  120. priv = GEBR_GUI_HTML_VIEWER_WIDGET_GET_PRIVATE(object);
  121. g_unlink (priv->tmp_file);
  122. g_free (priv->tmp_file);
  123. g_free(priv->label);
  124. g_string_free (priv->content, TRUE);
  125. G_OBJECT_CLASS (gebr_gui_html_viewer_widget_parent_class)->finalize (object);
  126. }
  127. //==============================================================================
  128. // PRIVATE FUNCTIONS =
  129. //==============================================================================
  130. static void create_generate_links_index_function(JSContextRef context)
  131. {
  132. static const gchar * script =
  133. "function GenerateLinksIndex(links, is_programs_help) {"
  134. "var navbar = document.getElementsByClassName('navigation')[0];"
  135. "if (!navbar) return;"
  136. "var linklist = navbar.getElementsByTagName('ul')[0];"
  137. "if (!linklist) return;"
  138. "if (!is_programs_help) {"
  139. "var session = document.createElement('li');"
  140. "session.innerHTML = '<h2>Programs</h2>';"
  141. "linklist.appendChild(session);"
  142. "}"
  143. "for (var i = 0; i < links.length; ++i) {"
  144. "var link = document.createElement('a');"
  145. "link.setAttribute('href', links[i][1]);"
  146. "link.innerHTML = links[i][0];"
  147. "var li = document.createElement('li');"
  148. "li.appendChild(link);"
  149. "linklist.appendChild(li);"
  150. "}"
  151. "}"
  152. "function generateNavigationIndex() {"
  153. "var navbar = document.getElementsByClassName('navigation')[0];"
  154. "var content = document.getElementsByClassName('content')[0];"
  155. "if (!navbar || !content) return;"
  156. "var headers = content.getElementsByTagName('h2');"
  157. "navbar.innerHTML = '<h2>Index</h2><ul></ul>';"
  158. "var navlist = navbar.getElementsByTagName('ul')[0];"
  159. "for (var i = 0; i < headers.length; i++) {"
  160. "var anchor = 'header_' + i;"
  161. "var link = document.createElement('a');"
  162. "link.setAttribute('href', '#' + anchor);"
  163. "for (var j = 0; j < headers[i].childNodes.length; j++) {"
  164. "var clone = headers[i].childNodes[j].cloneNode(true);"
  165. "link.appendChild(clone);"
  166. "}"
  167. "var li = document.createElement('li');"
  168. "li.appendChild(link);"
  169. "navlist.appendChild(li);"
  170. "headers[i].setAttribute('id', anchor);"
  171. "}"
  172. "}";
  173. gebr_js_evaluate(context, script);
  174. }
  175. static void generate_links_index(JSContextRef context, const gchar * links, gboolean is_programs_help)
  176. {
  177. gchar * script;
  178. const gchar * program;
  179. program = is_programs_help? "true":"false";
  180. script = g_strdup_printf("generateNavigationIndex(); GenerateLinksIndex(%s,%s);", links, program);
  181. gebr_js_evaluate(context, script);
  182. g_free(script);
  183. }
  184. static gchar * js_get_document_title(JSContextRef context)
  185. {
  186. JSValueRef value;
  187. GString * title;
  188. value = gebr_js_evaluate(context, "document.title");
  189. title = gebr_js_value_get_string(context, value);
  190. return g_string_free(title, FALSE);
  191. }
  192. static void on_load_finished(WebKitWebView * web_view, WebKitWebFrame * frame, GebrGuiHtmlViewerWidget *self)
  193. {
  194. GebrGuiHtmlViewerWidgetPrivate * priv;
  195. GebrGeoXmlObjectType type;
  196. JSContextRef context;
  197. gchar * title;
  198. priv = GEBR_GUI_HTML_VIEWER_WIDGET_GET_PRIVATE(self);
  199. context = webkit_web_frame_get_global_context(frame);
  200. if (priv->object) {
  201. gchar *help;
  202. type = gebr_geoxml_object_get_type(priv->object);
  203. create_generate_links_index_function(context);
  204. if (type == GEBR_GEOXML_OBJECT_TYPE_PROGRAM) {
  205. GebrGeoXmlDocument *menu = gebr_geoxml_object_get_owner_document(priv->object);
  206. help = gebr_geoxml_document_get_help (menu);
  207. if(strlen(help) > 0)
  208. generate_links_index(context, "[['<b>Menu</b>', 'gebr://menu']]", TRUE);
  209. g_free (help);
  210. } else if (type == GEBR_GEOXML_OBJECT_TYPE_FLOW) {
  211. GString * list;
  212. GebrGeoXmlSequence *program;
  213. list = g_string_new("[");
  214. gebr_geoxml_flow_get_program(GEBR_GEOXML_FLOW(priv->object), &program, 0);
  215. for (gint i = 0; program != NULL; gebr_geoxml_sequence_next(&program), ++i) {
  216. help = gebr_geoxml_program_get_help (GEBR_GEOXML_PROGRAM (program));
  217. if(strlen(help) > 0)
  218. g_string_append_printf(list, "%s['%s', 'gebr://prog%d']",
  219. i == 0? "":",",
  220. gebr_geoxml_program_get_title(GEBR_GEOXML_PROGRAM(program)), i);
  221. g_free (help);
  222. }
  223. g_string_append(list, "]");
  224. if(strcmp(list->str,"[]") != 0)
  225. generate_links_index(context, list->str, FALSE);
  226. g_string_free(list, TRUE);
  227. }
  228. }
  229. title = js_get_document_title(context);
  230. g_signal_connect(priv->web_view, "navigation-requested", G_CALLBACK(on_navigation_requested), self);
  231. g_signal_emit(self, signals[ TITLE_READY ], 0, title);
  232. g_free (title);
  233. }
  234. static WebKitNavigationResponse on_navigation_requested(WebKitWebView * web_view, WebKitWebFrame *frame,
  235. WebKitNetworkRequest *request, GebrGuiHtmlViewerWidget *self)
  236. {
  237. GebrGuiHtmlViewerWidgetPrivate * priv = GEBR_GUI_HTML_VIEWER_WIDGET_GET_PRIVATE(self);
  238. const gchar * uri = webkit_network_request_get_uri(request);
  239. if (priv->object && g_str_has_prefix(uri, "gebr://")) {
  240. GebrGeoXmlObject *object;
  241. if (!strcmp(uri, "gebr://menu")) {
  242. if (gebr_geoxml_object_get_type(priv->object) == GEBR_GEOXML_OBJECT_TYPE_FLOW) {
  243. gebr_gui_message_dialog(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
  244. _("Invalid link"), _("Sorry, couldn't reach link."));
  245. return WEBKIT_NAVIGATION_RESPONSE_IGNORE;
  246. }
  247. GebrGeoXmlDocument *menu = gebr_geoxml_object_get_owner_document(priv->object);
  248. object = GEBR_GEOXML_OBJECT(menu);
  249. } else {
  250. if (gebr_geoxml_object_get_type(priv->object) != GEBR_GEOXML_OBJECT_TYPE_FLOW) {
  251. gebr_gui_message_dialog(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
  252. _("Invalid link"), _("Sorry, couldn't reach link."));
  253. return WEBKIT_NAVIGATION_RESPONSE_IGNORE;
  254. }
  255. int program_index = -1;
  256. sscanf(strstr(uri, "prog"), "prog%d", &program_index);
  257. GebrGeoXmlSequence *program;
  258. gebr_geoxml_flow_get_program(GEBR_GEOXML_FLOW(priv->object), &program, program_index);
  259. if (program == NULL) {
  260. gebr_gui_message_dialog(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
  261. _("Invalid link"), _("Sorry, couldn't find program."));
  262. return WEBKIT_NAVIGATION_RESPONSE_IGNORE;
  263. }
  264. object = GEBR_GEOXML_OBJECT(program);
  265. }
  266. gchar *help;
  267. GebrGeoXmlObjectType type;
  268. type = gebr_geoxml_object_get_type(object);
  269. if (type == GEBR_GEOXML_OBJECT_TYPE_FLOW)
  270. help = gebr_geoxml_document_get_help (GEBR_GEOXML_DOCUMENT (object));
  271. else
  272. help = gebr_geoxml_program_get_help (GEBR_GEOXML_PROGRAM (object));
  273. if(strlen(help) == 0) {
  274. gebr_gui_message_dialog(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
  275. _("Invalid link"), _("Sorry, couldn't reach link."));
  276. g_free (help);
  277. return WEBKIT_NAVIGATION_RESPONSE_IGNORE;
  278. }
  279. gebr_gui_html_viewer_widget_generate_links(self, object);
  280. gebr_gui_html_viewer_widget_show_html(self, help);
  281. g_free (help);
  282. return WEBKIT_NAVIGATION_RESPONSE_IGNORE;
  283. }
  284. if (g_str_has_prefix(uri + strlen("file://"), priv->tmp_file) || g_str_has_prefix(uri, "about:"))
  285. return WEBKIT_NAVIGATION_RESPONSE_ACCEPT;
  286. gebr_gui_show_uri(uri);
  287. return WEBKIT_NAVIGATION_RESPONSE_IGNORE;
  288. }
  289. static void pre_process_html(GebrGuiHtmlViewerWidgetPrivate * priv)
  290. {
  291. regex_t regexp;
  292. regmatch_t matchptr;
  293. if (!priv->content->len)
  294. return;
  295. regcomp(&regexp, "<link[^<]*gebr.css[^<]*>", REG_NEWLINE | REG_ICASE);
  296. if (!regexec(&regexp, priv->content->str, 1, &matchptr, 0)) {
  297. /*
  298. * If we found a link for GeBR's style sheet, we must update its path.
  299. */
  300. gssize start = matchptr.rm_so;
  301. gssize length = matchptr.rm_eo - matchptr.rm_so;
  302. g_string_erase(priv->content, start, length);
  303. g_string_insert(priv->content, start, CSS_LINK);
  304. } else if (priv->object != NULL) {
  305. /*
  306. * Otherwise, we only include the CSS link if we have a
  307. * GeoXml object associated with this priv->content view, because
  308. * this mean we are viewing a Flow or Program.
  309. */
  310. regcomp(&regexp, "<head>", REG_NEWLINE | REG_ICASE);
  311. if (!regexec(&regexp, priv->content->str, 1, &matchptr, 0)) {
  312. gssize start = matchptr.rm_eo;
  313. g_string_insert(priv->content, start, CSS_LINK);
  314. }
  315. }
  316. }
  317. static GtkWidget * on_create_custom_widget(GtkPrintOperation * print_op, GebrGuiHtmlViewerWidget *self)
  318. {
  319. GebrGuiHtmlViewerWidgetPrivate * priv;
  320. priv = GEBR_GUI_HTML_VIEWER_WIDGET_GET_PRIVATE(self);
  321. if (priv->callback)
  322. return priv->callback(self);
  323. return NULL;
  324. }
  325. static void on_apply_custom_widget(GtkPrintOperation * print_op, GtkWidget * widget, GebrGuiHtmlViewerWidget * self)
  326. {
  327. g_signal_emit(self, signals[ PRINT_REQUESTED ], 0);
  328. }
  329. //==============================================================================
  330. // PUBLIC FUNCTIONS =
  331. //==============================================================================
  332. GtkWidget *gebr_gui_html_viewer_widget_new(void)
  333. {
  334. return g_object_new(GEBR_GUI_TYPE_HTML_VIEWER_WIDGET, NULL);
  335. }
  336. void gebr_gui_html_viewer_widget_print(GebrGuiHtmlViewerWidget * self)
  337. {
  338. GebrGuiHtmlViewerWidgetPrivate * priv;
  339. priv = GEBR_GUI_HTML_VIEWER_WIDGET_GET_PRIVATE(self);
  340. g_return_if_fail(GEBR_GUI_IS_HTML_VIEWER_WIDGET(self));
  341. #if WEBKIT_CHECK_VERSION(1,1,5)
  342. WebKitWebFrame * frame;
  343. GtkPrintOperation * print_op;
  344. GError * error = NULL;
  345. print_op = gtk_print_operation_new();
  346. g_signal_connect(print_op, "create-custom-widget", G_CALLBACK(on_create_custom_widget), self);
  347. g_signal_connect(print_op, "custom-widget-apply", G_CALLBACK(on_apply_custom_widget), self);
  348. gtk_print_operation_set_custom_tab_label(print_op, priv->label);
  349. frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(priv->web_view));
  350. webkit_web_frame_print_full(frame, print_op, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, &error);
  351. if (error) {
  352. g_warning("%s", error->message);
  353. g_clear_error(&error);
  354. }
  355. g_object_unref(print_op);
  356. #else
  357. gebr_gui_show_uri (priv->tmp_file);
  358. #endif
  359. }
  360. void gebr_gui_html_viewer_widget_show_html(GebrGuiHtmlViewerWidget * self, const gchar * content)
  361. {
  362. GebrGuiHtmlViewerWidgetPrivate * priv;
  363. g_return_if_fail(GEBR_GUI_IS_HTML_VIEWER_WIDGET(self));
  364. priv = GEBR_GUI_HTML_VIEWER_WIDGET_GET_PRIVATE(self);
  365. g_string_assign (priv->content, content);
  366. pre_process_html (priv);
  367. /* some webkit versions crash to open an empty file... */
  368. if (!priv->content->len)
  369. g_string_assign(priv->content, " ");
  370. /* write current priv->content to temporary file */
  371. FILE *fp;
  372. fp = fopen(priv->tmp_file, "w");
  373. fputs(priv->content->str, fp);
  374. fclose(fp);
  375. webkit_web_view_open(WEBKIT_WEB_VIEW(priv->web_view), priv->tmp_file);
  376. }
  377. void gebr_gui_html_viewer_widget_generate_links(GebrGuiHtmlViewerWidget *self, GebrGeoXmlObject * object)
  378. {
  379. g_return_if_fail(GEBR_GUI_IS_HTML_VIEWER_WIDGET(self));
  380. GebrGuiHtmlViewerWidgetPrivate * priv = GEBR_GUI_HTML_VIEWER_WIDGET_GET_PRIVATE(self);
  381. priv->object = object;
  382. }
  383. void gebr_gui_html_viewer_widget_set_custom_tab (GebrGuiHtmlViewerWidget *self,
  384. const gchar *label,
  385. GebrGuiHtmlViewerCustomTab callback)
  386. {
  387. g_return_if_fail(GEBR_GUI_IS_HTML_VIEWER_WIDGET(self));
  388. GebrGuiHtmlViewerWidgetPrivate * priv = GEBR_GUI_HTML_VIEWER_WIDGET_GET_PRIVATE(self);
  389. priv->callback = callback;
  390. priv->label = g_strdup(label);
  391. }
  392. const gchar * gebr_gui_html_viewer_widget_get_html (GebrGuiHtmlViewerWidget *self)
  393. {
  394. GebrGuiHtmlViewerWidgetPrivate *priv;
  395. g_return_val_if_fail (GEBR_GUI_IS_HTML_VIEWER_WIDGET (self), NULL);
  396. priv = GEBR_GUI_HTML_VIEWER_WIDGET_GET_PRIVATE (self);
  397. return priv->content->str;
  398. }
  399. GebrGeoXmlObject *
  400. gebr_gui_html_viewer_widget_get_related_object (GebrGuiHtmlViewerWidget *self)
  401. {
  402. g_return_val_if_fail (GEBR_GUI_IS_HTML_VIEWER_WIDGET (self), NULL);
  403. GebrGuiHtmlViewerWidgetPrivate * priv = GEBR_GUI_HTML_VIEWER_WIDGET_GET_PRIVATE (self);
  404. return priv->object;
  405. }