Avoid connecting signals more than once.
[geany-mirror.git] / src / templates.c
blobc100df0003bf01ca5b00665dec9c800cfa349bfa
1 /*
2 * templates.c - this file is part of Geany, a fast and lightweight IDE
4 * Copyright 2005-2010 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
5 * Copyright 2006-2010 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, MA 02110-1301, USA.
21 * $Id$
25 * Templates to insert into the current document, or filetype templates to create a new
26 * document from.
29 #include <time.h>
30 #include <string.h>
32 #include "geany.h"
34 #include "templates.h"
35 #include "support.h"
36 #include "utils.h"
37 #include "document.h"
38 #include "filetypes.h"
39 #include "ui_utils.h"
40 #include "toolbar.h"
41 #include "geanymenubuttonaction.h"
42 #include "project.h"
45 GeanyTemplatePrefs template_prefs;
47 static GtkWidget *new_with_template_menu = NULL; /* submenu used for both file menu and toolbar */
50 /* TODO: implement custom insertion templates, put these into files in data/templates */
52 /* default templates, only for initial tempate file creation on first start of Geany */
53 static const gchar templates_gpl_notice[] = "\
54 This program is free software; you can redistribute it and/or modify\n\
55 it under the terms of the GNU General Public License as published by\n\
56 the Free Software Foundation; either version 2 of the License, or\n\
57 (at your option) any later version.\n\
58 \n\
59 This program is distributed in the hope that it will be useful,\n\
60 but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
61 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\
62 GNU General Public License for more details.\n\
63 \n\
64 You should have received a copy of the GNU General Public License\n\
65 along with this program; if not, write to the Free Software\n\
66 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,\n\
67 MA 02110-1301, USA.\n\
70 static const gchar templates_bsd_notice[] = "\
71 Redistribution and use in source and binary forms, with or without\n\
72 modification, are permitted provided that the following conditions are\n\
73 met:\n\
74 \n\
75 * Redistributions of source code must retain the above copyright\n\
76 notice, this list of conditions and the following disclaimer.\n\
77 * Redistributions in binary form must reproduce the above\n\
78 copyright notice, this list of conditions and the following disclaimer\n\
79 in the documentation and/or other materials provided with the\n\
80 distribution.\n\
81 * Neither the name of the {company} nor the names of its\n\
82 contributors may be used to endorse or promote products derived from\n\
83 this software without specific prior written permission.\n\
84 \n\
85 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\
86 \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n\
87 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n\
88 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n\
89 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n\
90 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n\
91 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n\
92 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n\
93 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n\
94 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n\
95 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\
98 static const gchar templates_function_description[] = "\
99 \n\
100 name: {functionname}\n\
101 @param\n\
102 @return\n\
105 static const gchar templates_multiline[] = "\
110 static const gchar templates_fileheader[] = "\
111 {filename}\n\
113 Copyright {year} {developer} <{mail}>\n\
115 {gpl}\
118 static const gchar templates_changelog[] = "\
119 {date} {developer} <{mail}>\n\
121 * \n\n\n";
123 static gchar *templates[GEANY_MAX_TEMPLATES];
125 /* We should probably remove filetype templates support soon - users can use custom
126 * file templates instead. */
127 static gchar *ft_templates[GEANY_MAX_BUILT_IN_FILETYPES] = {NULL};
130 static void replace_static_values(GString *text);
131 static gchar *get_template_fileheader(GeanyFiletype *ft);
133 /* called by templates_replace_common */
134 static void templates_replace_default_dates(GString *text);
135 static void templates_replace_command(GString *text, const gchar *file_name,
136 const gchar *file_type, const gchar *func_name);
139 /* some simple macros to reduce code size and make the code readable */
140 #define TEMPLATES_GET_FILENAME(shortname) \
141 g_strconcat(app->configdir, \
142 G_DIR_SEPARATOR_S GEANY_TEMPLATES_SUBDIR G_DIR_SEPARATOR_S, shortname, NULL)
144 #define TEMPLATES_READ_FILE(fname, contents_ptr) \
145 g_file_get_contents(fname, contents_ptr, NULL, NULL);
148 static void create_template_file_if_necessary(const gchar *filename, const gchar *content)
150 if (! g_file_test(filename, G_FILE_TEST_EXISTS))
152 if (file_prefs.default_eol_character != SC_EOL_LF)
154 /* Replace the \n characters in the default template text by the proper
155 * platform-specific line ending characters. */
156 GString *tmp = g_string_new(content);
157 const gchar *eol_str = (file_prefs.default_eol_character == SC_EOL_CR) ? "\r" : "\r\n";
159 utils_string_replace_all(tmp, "\n", eol_str);
160 utils_write_file(filename, tmp->str);
161 g_string_free(tmp, TRUE);
163 else
164 utils_write_file(filename, content);
169 /* FIXME the callers should use GStrings instead of char arrays */
170 static gchar *replace_all(gchar *text, const gchar *year, const gchar *date, const gchar *datetime)
172 GString *str;
174 if (text == NULL)
175 return NULL;
177 str = g_string_new(text);
179 g_free(text);
180 templates_replace_valist(str,
181 "{year}", year,
182 "{date}", date,
183 "{datetime}", datetime,
184 NULL);
186 return g_string_free(str, FALSE);
190 static void init_general_templates(const gchar *year, const gchar *date, const gchar *datetime)
192 gchar *template_filename_fileheader = TEMPLATES_GET_FILENAME("fileheader");
193 gchar *template_filename_gpl = TEMPLATES_GET_FILENAME("gpl");
194 gchar *template_filename_bsd = TEMPLATES_GET_FILENAME("bsd");
195 gchar *template_filename_function = TEMPLATES_GET_FILENAME("function");
196 gchar *template_filename_changelog = TEMPLATES_GET_FILENAME("changelog");
198 /* create the template files in the configuration directory, if they don't exist */
199 create_template_file_if_necessary(template_filename_fileheader, templates_fileheader);
200 create_template_file_if_necessary(template_filename_gpl, templates_gpl_notice);
201 create_template_file_if_necessary(template_filename_bsd, templates_bsd_notice);
202 create_template_file_if_necessary(template_filename_function, templates_function_description);
203 create_template_file_if_necessary(template_filename_changelog, templates_changelog);
205 /* read the contents */
206 TEMPLATES_READ_FILE(template_filename_fileheader, &templates[GEANY_TEMPLATE_FILEHEADER]);
207 templates[GEANY_TEMPLATE_FILEHEADER] = replace_all(templates[GEANY_TEMPLATE_FILEHEADER], year, date, datetime);
209 TEMPLATES_READ_FILE(template_filename_gpl, &templates[GEANY_TEMPLATE_GPL]);
210 templates[GEANY_TEMPLATE_GPL] = replace_all(templates[GEANY_TEMPLATE_GPL], year, date, datetime);
212 TEMPLATES_READ_FILE(template_filename_bsd, &templates[GEANY_TEMPLATE_BSD]);
213 templates[GEANY_TEMPLATE_BSD] = replace_all(templates[GEANY_TEMPLATE_BSD], year, date, datetime);
215 TEMPLATES_READ_FILE(template_filename_function, &templates[GEANY_TEMPLATE_FUNCTION]);
216 templates[GEANY_TEMPLATE_FUNCTION] = replace_all(templates[GEANY_TEMPLATE_FUNCTION], year, date, datetime);
218 TEMPLATES_READ_FILE(template_filename_changelog, &templates[GEANY_TEMPLATE_CHANGELOG]);
219 templates[GEANY_TEMPLATE_CHANGELOG] = replace_all(templates[GEANY_TEMPLATE_CHANGELOG], year, date, datetime);
221 /* free the whole stuff */
222 g_free(template_filename_fileheader);
223 g_free(template_filename_gpl);
224 g_free(template_filename_bsd);
225 g_free(template_filename_function);
226 g_free(template_filename_changelog);
230 static void init_ft_templates(const gchar *year, const gchar *date, const gchar *datetime)
232 filetype_id ft_id;
234 for (ft_id = 0; ft_id < GEANY_MAX_BUILT_IN_FILETYPES; ft_id++)
236 gchar *ext = filetypes_get_conf_extension(ft_id);
237 gchar *shortname = g_strconcat("filetype.", ext, NULL);
238 gchar *fname = TEMPLATES_GET_FILENAME(shortname);
240 TEMPLATES_READ_FILE(fname, &ft_templates[ft_id]);
241 ft_templates[ft_id] = replace_all(ft_templates[ft_id], year, date, datetime);
243 g_free(fname);
244 g_free(shortname);
245 g_free(ext);
250 static void
251 on_new_with_filetype_template(GtkMenuItem *menuitem, gpointer user_data)
253 GeanyFiletype *ft = user_data;
254 gchar *template = templates_get_template_new_file(ft);
256 document_new_file(NULL, ft, template);
257 g_free(template);
261 /* TODO: remove filetype template support after 0.19 */
262 static gboolean create_new_filetype_items(void)
264 GSList *node;
265 gboolean ret = FALSE;
266 GtkWidget *menu = NULL;
268 foreach_slist(node, filetypes_by_title)
270 GeanyFiletype *ft = node->data;
271 GtkWidget *item;
273 if (ft->id >= GEANY_MAX_BUILT_IN_FILETYPES || ft_templates[ft->id] == NULL)
274 continue;
276 if (!menu)
278 item = gtk_menu_item_new_with_label(_("Old"));
279 menu = gtk_menu_new();
280 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), menu);
281 gtk_widget_show_all(item);
282 gtk_container_add(GTK_CONTAINER(new_with_template_menu), item);
284 item = gtk_menu_item_new_with_label(ft->title);
285 gtk_widget_show(item);
286 gtk_container_add(GTK_CONTAINER(menu), item);
287 g_signal_connect(item, "activate", G_CALLBACK(on_new_with_filetype_template), ft);
288 ret = TRUE;
290 return ret;
294 void templates_replace_common(GString *template, const gchar *fname,
295 GeanyFiletype *ft, const gchar *func_name)
297 gchar *shortname;
299 if (fname == NULL)
301 if (!ft->extension)
302 shortname = g_strdup(GEANY_STRING_UNTITLED);
303 else
304 shortname = g_strconcat(GEANY_STRING_UNTITLED, ".", ft->extension, NULL);
306 else
307 shortname = g_path_get_basename(fname);
309 templates_replace_valist(template,
310 "{filename}", shortname,
311 "{project}", app->project ? app->project->name : "",
312 "{description}", app->project ? app->project->description : "",
313 NULL);
314 g_free(shortname);
316 templates_replace_default_dates(template);
317 templates_replace_command(template, fname, ft->name, func_name);
318 /* Bug: command results could have {ob} {cb} strings in! */
319 /* replace braces last */
320 templates_replace_valist(template,
321 "{ob}", "{",
322 "{cb}", "}",
323 NULL);
327 static gchar *get_template_from_file(const gchar *locale_fname, const gchar *doc_filename,
328 GeanyFiletype *ft)
330 gchar *content;
331 GString *template = NULL;
333 g_file_get_contents(locale_fname, &content, NULL, NULL);
335 if (content != NULL)
337 gchar *file_header;
339 template = g_string_new(content);
341 file_header = get_template_fileheader(ft);
342 templates_replace_valist(template,
343 "{fileheader}", file_header,
344 NULL);
345 templates_replace_common(template, doc_filename, ft, NULL);
347 utils_free_pointers(2, file_header, content, NULL);
348 return g_string_free(template, FALSE);
350 return NULL;
354 static void
355 on_new_with_file_template(GtkMenuItem *menuitem, G_GNUC_UNUSED gpointer user_data)
357 gchar *fname = ui_menu_item_get_text(menuitem);
358 GeanyFiletype *ft;
359 gchar *template;
360 const gchar *extension = strrchr(fname, '.'); /* easy way to get the file extension */
361 gchar *new_filename = g_strconcat(GEANY_STRING_UNTITLED, extension, NULL);
362 gchar *path;
364 ft = filetypes_detect_from_extension(fname);
365 setptr(fname, utils_get_locale_from_utf8(fname));
367 /* fname is just the basename from the menu item, so prepend the custom files path */
368 path = g_build_path(G_DIR_SEPARATOR_S, app->configdir, GEANY_TEMPLATES_SUBDIR,
369 "files", fname, NULL);
370 template = get_template_from_file(path, new_filename, ft);
371 if (!template)
373 /* try the system path */
374 g_free(path);
375 path = g_build_path(G_DIR_SEPARATOR_S, app->datadir, GEANY_TEMPLATES_SUBDIR,
376 "files", fname, NULL);
377 template = get_template_from_file(path, new_filename, ft);
379 if (template)
380 document_new_file(new_filename, ft, template);
381 else
383 setptr(fname, utils_get_utf8_from_locale(fname));
384 ui_set_statusbar(TRUE, _("Could not find file '%s'."), fname);
386 g_free(template);
387 g_free(path);
388 g_free(new_filename);
389 g_free(fname);
393 static void add_file_item(const gchar *fname, GtkWidget *menu)
395 GtkWidget *tmp_button;
396 gchar *label;
398 g_return_if_fail(fname);
399 g_return_if_fail(menu);
401 label = utils_get_utf8_from_locale(fname);
403 tmp_button = gtk_menu_item_new_with_label(label);
404 gtk_widget_show(tmp_button);
405 gtk_container_add(GTK_CONTAINER(menu), tmp_button);
406 g_signal_connect(tmp_button, "activate", G_CALLBACK(on_new_with_file_template), NULL);
408 g_free(label);
412 static gboolean add_custom_template_items(void)
414 GSList *list = utils_get_config_files(GEANY_TEMPLATES_SUBDIR G_DIR_SEPARATOR_S "files");
415 GSList *node;
417 foreach_slist(node, list)
419 gchar *fname = node->data;
421 add_file_item(fname, new_with_template_menu);
422 g_free(fname);
424 g_slist_free(list);
425 return list != NULL;
429 static void create_file_template_menu(void)
431 GtkWidget *sep = NULL;
433 new_with_template_menu = gtk_menu_new();
435 if (add_custom_template_items())
437 sep = gtk_separator_menu_item_new();
438 gtk_container_add(GTK_CONTAINER(new_with_template_menu), sep);
440 if (create_new_filetype_items() && sep)
442 gtk_widget_show(sep);
444 /* unless the file menu is showing, menu should be in the toolbar widget */
445 geany_menu_button_action_set_menu(GEANY_MENU_BUTTON_ACTION(
446 toolbar_get_action_by_name("New")), new_with_template_menu);
450 static void on_file_menu_show(GtkWidget *item)
452 geany_menu_button_action_set_menu(
453 GEANY_MENU_BUTTON_ACTION(toolbar_get_action_by_name("New")), NULL);
454 item = ui_lookup_widget(main_widgets.window, "menu_new_with_template1");
455 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_with_template_menu);
459 static void on_file_menu_hide(GtkWidget *item)
461 item = ui_lookup_widget(main_widgets.window, "menu_new_with_template1");
462 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), NULL);
463 geany_menu_button_action_set_menu(
464 GEANY_MENU_BUTTON_ACTION(toolbar_get_action_by_name("New")), new_with_template_menu);
468 /* reload templates if any file in the templates path is saved */
469 static void on_document_save(G_GNUC_UNUSED GObject *object, GeanyDocument *doc)
471 const gchar *path = utils_build_path(app->configdir, GEANY_TEMPLATES_SUBDIR, NULL);
473 g_return_if_fail(NZV(doc->real_path));
475 if (strncmp(doc->real_path, path, strlen(path)) == 0)
477 /* reload templates */
478 templates_free_templates();
479 templates_init();
484 /* warning: also called when reloading template settings */
485 void templates_init(void)
487 gchar *year = utils_get_date_time(template_prefs.year_format, NULL);
488 gchar *date = utils_get_date_time(template_prefs.date_format, NULL);
489 gchar *datetime = utils_get_date_time(template_prefs.datetime_format, NULL);
490 static gboolean init_done = FALSE;
492 init_general_templates(year, date, datetime);
493 init_ft_templates(year, date, datetime);
495 g_free(date);
496 g_free(datetime);
497 g_free(year);
499 create_file_template_menu();
500 /* we hold our own ref for the menu as it has no parent whilst being moved */
501 g_object_ref(new_with_template_menu);
503 /* only connect signals to persistent objects once */
504 if (!init_done)
506 GtkWidget *item;
507 /* reparent the template menu as needed */
508 item = ui_lookup_widget(main_widgets.window, "file1");
509 item = gtk_menu_item_get_submenu(GTK_MENU_ITEM(item));
510 g_signal_connect(item, "show", G_CALLBACK(on_file_menu_show), NULL);
511 g_signal_connect(item, "hide", G_CALLBACK(on_file_menu_hide), NULL);
513 g_signal_connect(geany_object, "document-save", G_CALLBACK(on_document_save), NULL);
515 init_done = TRUE;
519 /* indent is used to make some whitespace between comment char and real start of the line
520 * e.g. indent = 8 prints " * here comes the text of the line"
521 * indent is meant to be the whole amount of characters before the real line content follows, i.e.
522 * 6 characters are filled with whitespace when the comment characters include " *" */
523 /* TODO make this function operating on a GString */
524 static gchar *make_comment_block(const gchar *comment_text, gint filetype_idx, guint indent)
526 gchar *frame_start; /* to add before comment_text */
527 gchar *frame_end; /* to add after comment_text */
528 const gchar *line_prefix; /* to add before every line in comment_text */
529 gchar *result;
530 gchar *tmp;
531 gchar *prefix;
532 gchar **lines;
533 guint i, len;
534 GeanyFiletype *ft = filetypes_index(filetype_idx);
536 g_return_val_if_fail(ft != NULL, NULL);
538 if (NZV(ft->comment_open))
540 if (NZV(ft->comment_close))
542 frame_start = g_strconcat(ft->comment_open, "\n", NULL);
543 frame_end = g_strconcat(ft->comment_close, "\n", NULL);
544 line_prefix = "";
546 else
548 frame_start = NULL;
549 frame_end = NULL;
550 line_prefix = ft->comment_open;
553 else
554 { /* use C-like multi-line comments as fallback */
555 frame_start = g_strdup("/*\n");
556 frame_end = g_strdup("*/\n");
557 line_prefix = "";
560 /* do some magic to nicely format C-like multi-line comments */
561 if (NZV(frame_start) && frame_start[1] == '*')
563 /* prefix the string with a space */
564 setptr(frame_end, g_strconcat(" ", frame_end, NULL));
565 line_prefix = " *";
568 /* construct the real prefix with given amount of whitespace */
569 i = (indent > strlen(line_prefix)) ? (indent - strlen(line_prefix)) : strlen(line_prefix);
570 tmp = g_strnfill(i, ' ');
571 prefix = g_strconcat(line_prefix, tmp, NULL);
572 g_free(tmp);
574 /* add line_prefix to every line of comment_text */
575 lines = g_strsplit(comment_text, "\n", -1);
576 len = g_strv_length(lines) - 1;
577 for (i = 0; i < len; i++)
579 tmp = lines[i];
580 lines[i] = g_strconcat(prefix, tmp, NULL);
581 g_free(tmp);
583 tmp = g_strjoinv("\n", lines);
585 /* add frame_start and frame_end */
586 if (frame_start != NULL)
587 result = g_strconcat(frame_start, tmp, frame_end, NULL);
588 else
589 result = g_strconcat(tmp, frame_end, NULL);
591 utils_free_pointers(4, prefix, tmp, frame_start, frame_end, NULL);
592 g_strfreev(lines);
593 return result;
597 gchar *templates_get_template_licence(GeanyDocument *doc, gint licence_type)
599 GString *template;
600 gchar *result = NULL;
602 g_return_val_if_fail(doc != NULL, NULL);
603 g_return_val_if_fail(licence_type == GEANY_TEMPLATE_GPL || licence_type == GEANY_TEMPLATE_BSD, NULL);
605 template = g_string_new(templates[licence_type]);
606 replace_static_values(template);
607 templates_replace_default_dates(template);
608 templates_replace_command(template, DOC_FILENAME(doc), doc->file_type->name, NULL);
610 result = make_comment_block(template->str, FILETYPE_ID(doc->file_type), 8);
612 g_string_free(template, TRUE);
614 return result;
618 static gchar *get_template_fileheader(GeanyFiletype *ft)
620 GString *template = g_string_new(templates[GEANY_TEMPLATE_FILEHEADER]);
621 gchar *result;
623 filetypes_load_config(ft->id, FALSE); /* load any user extension setting */
625 templates_replace_valist(template,
626 "{gpl}", templates[GEANY_TEMPLATE_GPL],
627 "{bsd}", templates[GEANY_TEMPLATE_BSD],
628 NULL);
630 /* we don't replace other wildcards here otherwise they would get done twice for files */
631 result = make_comment_block(template->str, ft->id, 8);
632 g_string_free(template, TRUE);
633 return result;
637 /* TODO change the signature to take a GeanyDocument? this would break plugin API/ABI */
638 gchar *templates_get_template_fileheader(gint filetype_idx, const gchar *fname)
640 GeanyFiletype *ft = filetypes[filetype_idx];
641 gchar *str = get_template_fileheader(ft);
642 GString *template = g_string_new(str);
644 g_free(str);
645 templates_replace_common(template, fname, ft, NULL);
646 return g_string_free(template, FALSE);
650 gchar *templates_get_template_new_file(GeanyFiletype *ft)
652 GString *ft_template;
653 gchar *file_header = NULL;
655 g_return_val_if_fail(ft != NULL, NULL);
656 g_return_val_if_fail(ft->id < GEANY_MAX_BUILT_IN_FILETYPES, NULL);
658 ft_template = g_string_new(ft_templates[ft->id]);
659 if (FILETYPE_ID(ft) == GEANY_FILETYPES_NONE)
661 replace_static_values(ft_template);
663 else
664 { /* file template only used for new files */
665 file_header = get_template_fileheader(ft);
666 templates_replace_valist(ft_template, "{fileheader}", file_header, NULL);
668 templates_replace_common(ft_template, NULL, ft, NULL);
670 g_free(file_header);
671 return g_string_free(ft_template, FALSE);
675 gchar *templates_get_template_function(GeanyDocument *doc, const gchar *func_name)
677 gchar *result;
678 GString *text;
680 func_name = (func_name != NULL) ? func_name : "";
681 text = g_string_new(templates[GEANY_TEMPLATE_FUNCTION]);
683 templates_replace_valist(text, "{functionname}", func_name, NULL);
684 templates_replace_default_dates(text);
685 templates_replace_command(text, DOC_FILENAME(doc), doc->file_type->name, func_name);
687 result = make_comment_block(text->str, doc->file_type->id, 3);
689 g_string_free(text, TRUE);
690 return result;
694 gchar *templates_get_template_changelog(GeanyDocument *doc)
696 GString *result = g_string_new(templates[GEANY_TEMPLATE_CHANGELOG]);
697 const gchar *file_type_name = (doc != NULL) ? doc->file_type->name : "";
699 replace_static_values(result);
700 templates_replace_default_dates(result);
701 templates_replace_command(result, DOC_FILENAME(doc), file_type_name, NULL);
703 return g_string_free(result, FALSE);
707 void templates_free_templates(void)
709 gint i;
710 GList *children, *item;
712 for (i = 0; i < GEANY_MAX_TEMPLATES; i++)
714 g_free(templates[i]);
716 for (i = 0; i < GEANY_MAX_BUILT_IN_FILETYPES; i++)
718 g_free(ft_templates[i]);
720 /* destroy "New with template" sub menu items (in case we want to reload the templates) */
721 children = gtk_container_get_children(GTK_CONTAINER(new_with_template_menu));
722 foreach_list(item, children)
724 gtk_widget_destroy(GTK_WIDGET(item->data));
726 g_list_free(children);
728 geany_menu_button_action_set_menu(
729 GEANY_MENU_BUTTON_ACTION(toolbar_get_action_by_name("New")), NULL);
730 g_object_unref(new_with_template_menu);
731 new_with_template_menu = NULL;
735 static void replace_static_values(GString *text)
737 utils_string_replace_all(text, "{version}", template_prefs.version);
738 utils_string_replace_all(text, "{initial}", template_prefs.initials);
739 utils_string_replace_all(text, "{developer}", template_prefs.developer);
740 utils_string_replace_all(text, "{mail}", template_prefs.mail);
741 utils_string_replace_all(text, "{company}", template_prefs.company);
742 utils_string_replace_all(text, "{untitled}", GEANY_STRING_UNTITLED);
743 utils_string_replace_all(text, "{geanyversion}", "Geany " VERSION);
747 /* Replaces all static template wildcards (version, mail, company, name, ...)
748 * plus those wildcard, value pairs which are passed, e.g.
750 * templates_replace_valist(text, "{some_wildcard}", "some value",
751 * "{another_wildcard}", "another value", NULL);
753 * The argument list must be terminated with NULL. */
754 void templates_replace_valist(GString *text, const gchar *first_wildcard, ...)
756 va_list args;
757 const gchar *key, *value;
759 g_return_if_fail(text != NULL);
761 va_start(args, first_wildcard);
763 key = first_wildcard;
764 value = va_arg(args, gchar*);
766 while (key != NULL)
768 utils_string_replace_all(text, key, value);
770 key = va_arg(args, gchar*);
771 if (key == NULL || text == NULL)
772 break;
773 value = va_arg(args, gchar*);
775 va_end(args);
777 replace_static_values(text);
781 static void templates_replace_default_dates(GString *text)
783 gchar *year = utils_get_date_time(template_prefs.year_format, NULL);
784 gchar *date = utils_get_date_time(template_prefs.date_format, NULL);
785 gchar *datetime = utils_get_date_time(template_prefs.datetime_format, NULL);
787 g_return_if_fail(text != NULL);
789 templates_replace_valist(text,
790 "{year}", year,
791 "{date}", date,
792 "{datetime}", datetime,
793 NULL);
795 utils_free_pointers(3, year, date, datetime, NULL);
799 static gchar *run_command(const gchar *command, const gchar *file_name,
800 const gchar *file_type, const gchar *func_name)
802 gchar *result = NULL;
803 gchar **argv;
805 if (g_shell_parse_argv(command, NULL, &argv, NULL))
807 GError *error = NULL;
808 gchar **env;
810 file_name = (file_name != NULL) ? file_name : "";
811 file_type = (file_type != NULL) ? file_type : "";
812 func_name = (func_name != NULL) ? func_name : "";
814 env = utils_copy_environment(NULL,
815 "GEANY_FILENAME", file_name,
816 "GEANY_FILETYPE", file_type,
817 "GEANY_FUNCNAME", func_name,
818 NULL);
819 if (! utils_spawn_sync(NULL, argv, env, G_SPAWN_SEARCH_PATH,
820 NULL, NULL, &result, NULL, NULL, &error))
822 g_warning("templates_replace_command: %s", error->message);
823 g_error_free(error);
824 return NULL;
826 g_strfreev(argv);
827 g_strfreev(env);
829 return result;
833 static void templates_replace_command(GString *text, const gchar *file_name,
834 const gchar *file_type, const gchar *func_name)
836 gchar *match = NULL;
837 gchar *wildcard = NULL;
838 gchar *cmd;
839 gchar *result;
841 g_return_if_fail(text != NULL);
843 while ((match = strstr(text->str, "{command:")) != NULL)
845 cmd = match;
846 while (*match != '}' && *match != '\0')
847 match++;
849 wildcard = g_strndup(cmd, match - cmd + 1);
850 cmd = g_strndup(wildcard + 9, strlen(wildcard) - 10);
852 result = run_command(cmd, file_name, file_type, func_name);
853 if (result != NULL)
855 utils_string_replace_first(text, wildcard, result);
856 g_free(result);
858 else
859 utils_string_replace_first(text, wildcard, "");
861 g_free(wildcard);
862 g_free(cmd);