Reorder configure.ac a bit
[geany-mirror.git] / plugins / export.c
blob4655da8dbe54583340f600b3d964652c82d307fc
1 /*
2 * export.c - this file is part of Geany, a fast and lightweight IDE
4 * Copyright 2007-2011 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
5 * Copyright 2007-2011 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
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 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 * MA 02110-1301, USA.
22 * $Id$
25 /* Export plugin. */
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
31 #include <ctype.h>
32 #include <math.h>
34 #include "geanyplugin.h"
37 GeanyData *geany_data;
38 GeanyFunctions *geany_functions;
40 PLUGIN_VERSION_CHECK(GEANY_API_VERSION)
41 PLUGIN_SET_INFO(_("Export"), _("Exports the current file into different formats."), VERSION,
42 _("The Geany developer team"))
45 static GtkWidget *main_menu_item = NULL;
48 #define ROTATE_RGB(color) \
49 (((color) & 0xFF0000) >> 16) + ((color) & 0x00FF00) + (((color) & 0x0000FF) << 16)
50 #define TEMPLATE_HTML "\
51 <!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n\
52 \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n\
53 <html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n\
54 \n\
55 <head>\n\
56 <title>{export_filename}</title>\n\
57 <meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\" />\n\
58 <meta name=\"generator\" content=\"Geany " VERSION "\" />\n\
59 <meta name=\"date\" content=\"{export_date}\" />\n\
60 <style type=\"text/css\">\n\
61 {export_styles}\n\
62 </style>\n\
63 </head>\n\
64 \n\
65 <body>\n\
66 <p>\n\
67 {export_content}\n\
68 </p>\n\
69 </body>\n\
70 </html>\n"
72 #define TEMPLATE_LATEX "\
73 % {export_filename} (LaTeX code generated by Geany " VERSION " on {export_date})\n\
74 \\documentclass[a4paper]{article}\n\
75 \\usepackage[a4paper,margin=2cm]{geometry}\n\
76 \\usepackage[utf8]{inputenc}\n\
77 \\usepackage[T1]{fontenc}\n\
78 \\usepackage{color}\n\
79 \\setlength{\\parindent}{0em}\n\
80 \\setlength{\\parskip}{2ex plus1ex minus0.5ex}\n\
81 {export_styles}\n\
82 \\begin{document}\
83 \n\
84 \\ttfamily\n\
85 \\setlength{\\fboxrule}{0pt}\n\
86 \\setlength{\\fboxsep}{0pt}\n\
87 {export_content}\
88 \\end{document}\n"
91 enum
93 FORE = 0,
94 BACK,
95 BOLD,
96 ITALIC,
97 USED,
98 MAX_TYPES
101 enum
103 DATE_TYPE_DEFAULT,
104 DATE_TYPE_HTML
107 typedef void (*ExportFunc) (GeanyDocument *doc, const gchar *filename,
108 gboolean use_zoom, gboolean insert_line_numbers);
109 typedef struct
111 GeanyDocument *doc;
112 gboolean have_zoom_level_checkbox;
113 ExportFunc export_func;
114 } ExportInfo;
116 static void on_file_save_dialog_response(GtkDialog *dialog, gint response, gpointer user_data);
117 static void write_html_file(GeanyDocument *doc, const gchar *filename,
118 gboolean use_zoom, gboolean insert_line_numbers);
119 static void write_latex_file(GeanyDocument *doc, const gchar *filename,
120 gboolean use_zoom, gboolean insert_line_numbers);
123 /* converts a RGB colour into a LaTeX compatible representation, taken from SciTE */
124 static gchar* get_tex_rgb(gint rgb_colour)
126 /* texcolor[rgb]{0,0.5,0}{....} */
127 gdouble rf = (rgb_colour % 256) / 256.0;
128 gdouble gf = ((rgb_colour & - 16711936) / 256) / 256.0;
129 gdouble bf = ((rgb_colour & 0xff0000) / 65536) / 256.0;
130 gint r = (gint) (rf * 10 + 0.5);
131 gint g = (gint) (gf * 10 + 0.5);
132 gint b = (gint) (bf * 10 + 0.5);
134 return g_strdup_printf("%d.%d, %d.%d, %d.%d", r / 10, r % 10, g / 10, g % 10, b / 10, b % 10);
138 /* convert a style number (0..127) into a string representation (aa, ab, .., ba, bb, .., zy, zz) */
139 static gchar *get_tex_style(gint style)
141 static gchar buf[4];
142 int i = 0;
146 buf[i] = (style % 26) + 'a';
147 style /= 26;
148 i++;
149 } while (style > 0);
150 buf[i] = '\0';
152 return buf;
156 static void create_file_save_as_dialog(const gchar *extension, ExportFunc func,
157 gboolean show_zoom_level_checkbox)
159 GtkWidget *dialog, *vbox;
160 GeanyDocument *doc;
161 ExportInfo *exi;
163 g_return_if_fail(extension != NULL);
165 doc = document_get_current();
166 g_return_if_fail(doc != NULL);
168 exi = g_new(ExportInfo, 1);
169 exi->doc = doc;
170 exi->export_func = func;
171 exi->have_zoom_level_checkbox = FALSE;
173 dialog = gtk_file_chooser_dialog_new(_("Export File"), GTK_WINDOW(geany->main_widgets->window),
174 GTK_FILE_CHOOSER_ACTION_SAVE, NULL, NULL);
175 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
176 gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog), TRUE);
177 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(dialog), TRUE);
178 gtk_window_set_type_hint(GTK_WINDOW(dialog), GDK_WINDOW_TYPE_HINT_DIALOG);
179 gtk_widget_set_name(dialog, "GeanyExportDialog");
181 gtk_dialog_add_buttons(GTK_DIALOG(dialog),
182 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL);
183 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
185 /* file chooser extra widget */
186 vbox = gtk_vbox_new(FALSE, 0);
187 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(dialog), vbox);
189 GtkWidget *check_line_numbers;
191 check_line_numbers = gtk_check_button_new_with_mnemonic(_("_Insert line numbers"));
192 gtk_widget_set_tooltip_text(check_line_numbers,
193 _("Insert line numbers before each line in the exported document"));
194 gtk_box_pack_start(GTK_BOX(vbox), check_line_numbers, FALSE, FALSE, 0);
195 gtk_widget_show_all(vbox);
197 ui_hookup_widget(dialog, check_line_numbers, "check_line_numbers");
199 if (show_zoom_level_checkbox)
201 GtkWidget *check_zoom_level;
203 check_zoom_level = gtk_check_button_new_with_mnemonic(_("_Use current zoom level"));
204 gtk_widget_set_tooltip_text(check_zoom_level,
205 _("Renders the font size of the document together with the current zoom level"));
206 gtk_box_pack_start(GTK_BOX(vbox), check_zoom_level, FALSE, FALSE, 0);
207 gtk_widget_show_all(vbox);
209 ui_hookup_widget(dialog, check_zoom_level, "check_zoom_level");
210 exi->have_zoom_level_checkbox = TRUE;
213 g_signal_connect(dialog, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL);
214 g_signal_connect(dialog, "response", G_CALLBACK(on_file_save_dialog_response), exi);
216 gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(geany->main_widgets->window));
218 /* if the current document has a filename we use it as the default. */
219 gtk_file_chooser_unselect_all(GTK_FILE_CHOOSER(dialog));
220 if (doc->file_name != NULL)
222 gchar *base_name = g_path_get_basename(doc->file_name);
223 gchar *file_name;
224 gchar *locale_filename;
225 gchar *locale_dirname;
226 const gchar *suffix = "";
228 if (g_str_has_suffix(doc->file_name, extension))
229 suffix = "_export";
231 file_name = g_strconcat(base_name, suffix, extension, NULL);
232 locale_filename = utils_get_locale_from_utf8(doc->file_name);
233 locale_dirname = g_path_get_dirname(locale_filename);
234 /* set the current name to base_name.html which probably doesn't exist yet so
235 * gtk_file_chooser_set_filename() can't be used and we need
236 * gtk_file_chooser_set_current_folder() additionally */
237 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), locale_dirname);
238 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), file_name);
239 g_free(locale_dirname);
240 g_free(locale_filename);
241 g_free(file_name);
242 g_free(base_name);
244 else
246 const gchar *default_open_path = geany->prefs->default_open_path;
247 gchar *fname = g_strconcat(GEANY_STRING_UNTITLED, extension, NULL);
249 gtk_file_chooser_unselect_all(GTK_FILE_CHOOSER(dialog));
250 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), fname);
252 /* use default startup directory(if set) if no files are open */
253 if (NZV(default_open_path) && g_path_is_absolute(default_open_path))
255 gchar *locale_path = utils_get_locale_from_utf8(default_open_path);
256 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), locale_path);
257 g_free(locale_path);
259 g_free(fname);
261 gtk_dialog_run(GTK_DIALOG(dialog));
265 static void on_menu_create_latex_activate(GtkMenuItem *menuitem, gpointer user_data)
267 create_file_save_as_dialog(".tex", write_latex_file, FALSE);
271 static void on_menu_create_html_activate(GtkMenuItem *menuitem, gpointer user_data)
273 create_file_save_as_dialog(".html", write_html_file, TRUE);
277 static void write_data(const gchar *filename, const gchar *data)
279 gint error_nr = utils_write_file(filename, data);
280 gchar *utf8_filename = utils_get_utf8_from_locale(filename);
282 if (error_nr == 0)
283 ui_set_statusbar(TRUE, _("Document successfully exported as '%s'."), utf8_filename);
284 else
285 ui_set_statusbar(TRUE, _("File '%s' could not be written (%s)."),
286 utf8_filename, g_strerror(error_nr));
288 g_free(utf8_filename);
292 static gchar *get_date(gint type)
294 const gchar *format;
296 if (type == DATE_TYPE_HTML)
297 /* needs testing */
298 #ifdef _GNU_SOURCE
299 format = "%Y-%m-%dT%H:%M:%S%z";
300 #else
301 format = "%Y-%m-%dT%H:%M:%S";
302 #endif
303 else
304 format = "%c";
306 return utils_get_date_time(format, NULL);
310 static void on_file_save_dialog_response(GtkDialog *dialog, gint response, gpointer user_data)
312 ExportInfo *exi = user_data;
314 if (response == GTK_RESPONSE_ACCEPT && exi != NULL)
316 gchar *new_filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
317 gchar *utf8_filename;
318 gboolean insert_line_numbers;
319 gboolean use_zoom_level = FALSE;
321 if (exi->have_zoom_level_checkbox)
323 use_zoom_level = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
324 ui_lookup_widget(GTK_WIDGET(dialog), "check_zoom_level")));
326 insert_line_numbers = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
327 ui_lookup_widget(GTK_WIDGET(dialog), "check_line_numbers")));
329 utf8_filename = utils_get_utf8_from_locale(new_filename);
331 /* check if file exists and ask whether to overwrite or not */
332 if (g_file_test(new_filename, G_FILE_TEST_EXISTS))
334 if (dialogs_show_question(
335 _("The file '%s' already exists. Do you want to overwrite it?"),
336 utf8_filename) == FALSE)
337 return;
340 exi->export_func(exi->doc, new_filename, use_zoom_level, insert_line_numbers);
342 g_free(utf8_filename);
343 g_free(new_filename);
345 g_free(exi);
346 gtk_widget_destroy(GTK_WIDGET(dialog));
350 /* returns the "width" (count of needed characters) for the given number */
351 static gint get_line_numbers_arity(gint line_number)
353 gint a = 0;
354 while ((line_number /= 10) != 0)
355 a++;
356 return a;
360 static gint get_line_number_width(GeanyDocument *doc)
362 gint line_count = sci_get_line_count(doc->editor->sci);
363 return get_line_numbers_arity(line_count);
367 static void write_latex_file(GeanyDocument *doc, const gchar *filename,
368 gboolean use_zoom, gboolean insert_line_numbers)
370 GeanyEditor *editor = doc->editor;
371 ScintillaObject *sci = doc->editor->sci;
372 gint i, doc_len, style = -1, old_style = 0, column = 0;
373 gint k, line_number, line_number_width, line_number_max_width = 0, pad;
374 gchar c, c_next, *tmp, *date;
375 /* 0 - fore, 1 - back, 2 - bold, 3 - italic, 4 - font size, 5 - used(0/1) */
376 gint styles[STYLE_MAX + 1][MAX_TYPES];
377 gboolean block_open = FALSE;
378 GString *body;
379 GString *cmds;
380 GString *latex;
381 gint style_max = pow(2, scintilla_send_message(sci, SCI_GETSTYLEBITS, 0, 0));
383 /* first read all styles from Scintilla */
384 for (i = 0; i < style_max; i++)
386 styles[i][FORE] = scintilla_send_message(sci, SCI_STYLEGETFORE, i, 0);
387 styles[i][BACK] = scintilla_send_message(sci, SCI_STYLEGETBACK, i, 0);
388 styles[i][BOLD] = scintilla_send_message(sci, SCI_STYLEGETBOLD, i, 0);
389 styles[i][ITALIC] = scintilla_send_message(sci, SCI_STYLEGETITALIC, i, 0);
390 styles[i][USED] = 0;
393 if (insert_line_numbers)
394 line_number_max_width = get_line_number_width(doc);
396 /* read the document and write the LaTeX code */
397 body = g_string_new("");
398 doc_len = sci_get_length(sci);
399 for (i = 0; i <= doc_len; i++)
401 style = sci_get_style_at(sci, i);
402 c = sci_get_char_at(sci, i);
403 c_next = sci_get_char_at(sci, i + 1);
405 /* line numbers */
406 if (insert_line_numbers && column == 0)
408 line_number = sci_get_line_from_position(sci, i) + 1;
409 line_number_width = get_line_numbers_arity(line_number);
410 /* padding */
411 pad = line_number_max_width - line_number_width;
412 for (k = 0; k < pad; k++)
414 g_string_append(body, " ");
416 g_string_append_printf(body, "%d ", line_number);
419 if (style != old_style || ! block_open)
421 old_style = style;
422 styles[style][USED] = 1;
423 if (block_open)
425 g_string_append(body, "}\n");
426 block_open = FALSE;
428 if (i < doc_len)
430 g_string_append_printf(body, "\\style%s{", get_tex_style(style));
431 block_open = TRUE;
434 /* escape the current character if necessary else just add it */
435 switch (c)
437 case '\r':
438 case '\n':
440 if (c == '\r' && c_next == '\n')
441 continue; /* when using CR/LF skip CR and add the line break with LF */
443 if (block_open)
445 g_string_append(body, "}");
446 block_open = FALSE;
448 g_string_append(body, " \\\\\n");
449 column = -1;
450 break;
452 case '\t':
454 gint tab_width = sci_get_tab_width(editor->sci);
455 gint tab_stop = tab_width - (column % tab_width);
457 column += tab_stop - 1; /* -1 because we add 1 at the end of the loop */
458 g_string_append_printf(body, "\\hspace*{%dem}", tab_stop);
459 break;
461 case ' ':
463 if (c_next == ' ')
465 g_string_append(body, "{\\hspace*{1em}}");
466 i++; /* skip the next character */
468 else
469 g_string_append_c(body, ' ');
470 break;
472 case '{':
473 case '}':
474 case '_':
475 case '&':
476 case '$':
477 case '#':
478 case '%':
480 g_string_append_printf(body, "\\%c", c);
481 break;
483 case '\\':
485 g_string_append(body, "\\symbol{92}");
486 break;
488 case '~':
490 g_string_append(body, "\\symbol{126}");
491 break;
493 case '^':
495 g_string_append(body, "\\symbol{94}");
496 break;
498 /** TODO still don't work for "---" or "----" */
499 case '-': /* mask "--" */
501 if (c_next == '-')
503 g_string_append(body, "-\\/-");
504 i++; /* skip the next character */
506 else
507 g_string_append_c(body, '-');
509 break;
511 case '<': /* mask "<<" */
513 if (c_next == '<')
515 g_string_append(body, "<\\/<");
516 i++; /* skip the next character */
518 else
519 g_string_append_c(body, '<');
521 break;
523 case '>': /* mask ">>" */
525 if (c_next == '>')
527 g_string_append(body, ">\\/>");
528 i++; /* skip the next character */
530 else
531 g_string_append_c(body, '>');
533 break;
535 default: g_string_append_c(body, c);
537 column++;
539 if (block_open)
541 g_string_append(body, "}\n");
542 block_open = FALSE;
545 /* force writing of style 0 (used at least for line breaks) */
546 styles[0][USED] = 1;
548 /* write used styles in the header */
549 cmds = g_string_new("");
550 for (i = 0; i <= STYLE_MAX; i++)
552 if (styles[i][USED])
554 g_string_append_printf(cmds,
555 "\\newcommand{\\style%s}[1]{\\noindent{", get_tex_style(i));
556 if (styles[i][BOLD])
557 g_string_append(cmds, "\\textbf{");
558 if (styles[i][ITALIC])
559 g_string_append(cmds, "\\textit{");
561 tmp = get_tex_rgb(styles[i][FORE]);
562 g_string_append_printf(cmds, "\\textcolor[rgb]{%s}{", tmp);
563 g_free(tmp);
564 tmp = get_tex_rgb(styles[i][BACK]);
565 g_string_append_printf(cmds, "\\fcolorbox[rgb]{0, 0, 0}{%s}{", tmp);
566 g_string_append(cmds, "#1}}");
567 g_free(tmp);
569 if (styles[i][BOLD])
570 g_string_append_c(cmds, '}');
571 if (styles[i][ITALIC])
572 g_string_append_c(cmds, '}');
573 g_string_append(cmds, "}}\n");
577 date = get_date(DATE_TYPE_DEFAULT);
578 /* write all */
579 latex = g_string_new(TEMPLATE_LATEX);
580 utils_string_replace_all(latex, "{export_content}", body->str);
581 utils_string_replace_all(latex, "{export_styles}", cmds->str);
582 utils_string_replace_all(latex, "{export_date}", date);
583 if (doc->file_name == NULL)
584 utils_string_replace_all(latex, "{export_filename}", GEANY_STRING_UNTITLED);
585 else
586 utils_string_replace_all(latex, "{export_filename}", doc->file_name);
588 write_data(filename, latex->str);
590 g_string_free(body, TRUE);
591 g_string_free(cmds, TRUE);
592 g_string_free(latex, TRUE);
593 g_free(date);
597 static void write_html_file(GeanyDocument *doc, const gchar *filename,
598 gboolean use_zoom, gboolean insert_line_numbers)
600 GeanyEditor *editor = doc->editor;
601 ScintillaObject *sci = doc->editor->sci;
602 gint i, doc_len, style = -1, old_style = 0, column = 0;
603 gint k, line_number, line_number_width, line_number_max_width = 0, pad;
604 gchar c, c_next, *date;
605 /* 0 - fore, 1 - back, 2 - bold, 3 - italic, 4 - font size, 5 - used(0/1) */
606 gint styles[STYLE_MAX + 1][MAX_TYPES];
607 gboolean span_open = FALSE;
608 const gchar *font_name;
609 gint font_size;
610 PangoFontDescription *font_desc;
611 GString *body;
612 GString *css;
613 GString *html;
614 gint style_max = pow(2, scintilla_send_message(sci, SCI_GETSTYLEBITS, 0, 0));
616 /* first read all styles from Scintilla */
617 for (i = 0; i < style_max; i++)
619 styles[i][FORE] = ROTATE_RGB(scintilla_send_message(sci, SCI_STYLEGETFORE, i, 0));
620 styles[i][BACK] = ROTATE_RGB(scintilla_send_message(sci, SCI_STYLEGETBACK, i, 0));
621 styles[i][BOLD] = scintilla_send_message(sci, SCI_STYLEGETBOLD, i, 0);
622 styles[i][ITALIC] = scintilla_send_message(sci, SCI_STYLEGETITALIC, i, 0);
623 styles[i][USED] = 0;
626 /* read Geany's font and font size */
627 font_desc = pango_font_description_from_string(geany->interface_prefs->editor_font);
628 font_name = pango_font_description_get_family(font_desc);
629 /*font_size = pango_font_description_get_size(font_desc) / PANGO_SCALE;*/
630 /* take the zoom level also into account */
631 font_size = scintilla_send_message(sci, SCI_STYLEGETSIZE, 0, 0);
632 if (use_zoom)
633 font_size += scintilla_send_message(sci, SCI_GETZOOM, 0, 0);
635 if (insert_line_numbers)
636 line_number_max_width = get_line_number_width(doc);
638 /* read the document and write the HTML body */
639 body = g_string_new("");
640 doc_len = sci_get_length(sci);
641 for (i = 0; i <= doc_len; i++)
643 style = sci_get_style_at(sci, i);
644 c = sci_get_char_at(sci, i);
645 /* sci_get_char_at() takes care of index boundaries and return 0 if i is too high */
646 c_next = sci_get_char_at(sci, i + 1);
648 /* line numbers */
649 if (insert_line_numbers && column == 0)
651 line_number = sci_get_line_from_position(sci, i) + 1;
652 line_number_width = get_line_numbers_arity(line_number);
653 /* padding */
654 pad = line_number_max_width - line_number_width;
655 for (k = 0; k < pad; k++)
657 g_string_append(body, "&nbsp;");
659 g_string_append_printf(body, "%d&nbsp;", line_number);
662 if ((style != old_style || ! span_open) && ! isspace(c))
664 old_style = style;
665 styles[style][USED] = 1;
666 if (span_open)
668 g_string_append(body, "</span>");
670 if (i < doc_len)
672 g_string_append_printf(body, "<span class=\"style_%d\">", style);
673 span_open = TRUE;
676 /* escape the current character if necessary else just add it */
677 switch (c)
679 case '\r':
680 case '\n':
682 if (c == '\r' && c_next == '\n')
683 continue; /* when using CR/LF skip CR and add the line break with LF */
685 if (span_open)
687 g_string_append(body, "</span>");
688 span_open = FALSE;
690 g_string_append(body, "<br />\n");
691 column = -1;
692 break;
694 case '\t':
696 gint j;
697 gint tab_width = sci_get_tab_width(editor->sci);
698 gint tab_stop = tab_width - (column % tab_width);
700 column += tab_stop - 1; /* -1 because we add 1 at the end of the loop */
701 for (j = 0; j < tab_stop; j++)
703 g_string_append(body, "&nbsp;");
705 break;
707 case ' ':
709 g_string_append(body, "&nbsp;");
710 break;
712 case '<':
714 g_string_append(body, "&lt;");
715 break;
717 case '>':
719 g_string_append(body, "&gt;");
720 break;
722 case '&':
724 g_string_append(body, "&amp;");
725 break;
727 default: g_string_append_c(body, c);
729 column++;
731 if (span_open)
733 g_string_append(body, "</span>");
734 span_open = FALSE;
737 /* write used styles in the header */
738 css = g_string_new("");
739 g_string_append_printf(css,
740 "\tbody\n\t{\n\t\tfont-family: %s, monospace;\n\t\tfont-size: %dpt;\n\t}\n",
741 font_name, font_size);
743 for (i = 0; i <= STYLE_MAX; i++)
745 if (styles[i][USED])
747 g_string_append_printf(css,
748 "\t.style_%d\n\t{\n\t\tcolor: #%06x;\n\t\tbackground-color: #%06x;\n%s%s\t}\n",
749 i, styles[i][FORE], styles[i][BACK],
750 (styles[i][BOLD]) ? "\t\tfont-weight: bold;\n" : "",
751 (styles[i][ITALIC]) ? "\t\tfont-style: italic;\n" : "");
755 date = get_date(DATE_TYPE_HTML);
756 /* write all */
757 html = g_string_new(TEMPLATE_HTML);
758 utils_string_replace_all(html, "{export_date}", date);
759 utils_string_replace_all(html, "{export_content}", body->str);
760 utils_string_replace_all(html, "{export_styles}", css->str);
761 if (doc->file_name == NULL)
762 utils_string_replace_all(html, "{export_filename}", GEANY_STRING_UNTITLED);
763 else
764 utils_string_replace_all(html, "{export_filename}", doc->file_name);
766 write_data(filename, html->str);
768 pango_font_description_free(font_desc);
769 g_string_free(body, TRUE);
770 g_string_free(css, TRUE);
771 g_string_free(html, TRUE);
772 g_free(date);
776 void plugin_init(GeanyData *data)
778 GtkWidget *menu_export;
779 GtkWidget *menu_export_menu;
780 GtkWidget *menu_create_html;
781 GtkWidget *menu_create_latex;
783 menu_export = gtk_image_menu_item_new_with_mnemonic(_("_Export"));
784 gtk_container_add(GTK_CONTAINER(geany->main_widgets->tools_menu), menu_export);
786 menu_export_menu = gtk_menu_new ();
787 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_export), menu_export_menu);
789 /* HTML */
790 menu_create_html = gtk_menu_item_new_with_mnemonic(_("As _HTML"));
791 gtk_container_add(GTK_CONTAINER (menu_export_menu), menu_create_html);
793 g_signal_connect(menu_create_html, "activate", G_CALLBACK(on_menu_create_html_activate), NULL);
795 /* LaTeX */
796 menu_create_latex = gtk_menu_item_new_with_mnemonic(_("As _LaTeX"));
797 gtk_container_add(GTK_CONTAINER (menu_export_menu), menu_create_latex);
799 g_signal_connect(menu_create_latex, "activate",
800 G_CALLBACK(on_menu_create_latex_activate), NULL);
802 /* disable menu_item when there are no documents open */
803 ui_add_document_sensitive(menu_export);
804 main_menu_item = menu_export;
806 gtk_widget_show_all(menu_export);
810 void plugin_cleanup(void)
812 gtk_widget_destroy(main_menu_item);