Add {project}, {description} template wildcards (#2954737).
[geany-mirror.git] / src / templates.c
blobdd39b427e3358c0e5316e9f7f64cea2bbf7f6a76
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 void templates_init(void)
470 gchar *year = utils_get_date_time(template_prefs.year_format, NULL);
471 gchar *date = utils_get_date_time(template_prefs.date_format, NULL);
472 gchar *datetime = utils_get_date_time(template_prefs.datetime_format, NULL);
473 GtkWidget *item;
475 init_general_templates(year, date, datetime);
476 init_ft_templates(year, date, datetime);
478 g_free(date);
479 g_free(datetime);
480 g_free(year);
482 create_file_template_menu();
483 /* we hold our own ref for the menu as it has no parent whilst being moved */
484 g_object_ref(new_with_template_menu);
486 /* reparent the template menu as needed */
487 item = ui_lookup_widget(main_widgets.window, "file1");
488 item = gtk_menu_item_get_submenu(GTK_MENU_ITEM(item));
489 g_signal_connect(item, "show", G_CALLBACK(on_file_menu_show), NULL);
490 g_signal_connect(item, "hide", G_CALLBACK(on_file_menu_hide), NULL);
494 /* indent is used to make some whitespace between comment char and real start of the line
495 * e.g. indent = 8 prints " * here comes the text of the line"
496 * indent is meant to be the whole amount of characters before the real line content follows, i.e.
497 * 6 characters are filled with whitespace when the comment characters include " *" */
498 /* TODO make this function operating on a GString */
499 static gchar *make_comment_block(const gchar *comment_text, gint filetype_idx, guint indent)
501 gchar *frame_start; /* to add before comment_text */
502 gchar *frame_end; /* to add after comment_text */
503 const gchar *line_prefix; /* to add before every line in comment_text */
504 gchar *result;
505 gchar *tmp;
506 gchar *prefix;
507 gchar **lines;
508 guint i, len;
509 GeanyFiletype *ft = filetypes_index(filetype_idx);
511 g_return_val_if_fail(ft != NULL, NULL);
513 if (NZV(ft->comment_open))
515 if (NZV(ft->comment_close))
517 frame_start = g_strconcat(ft->comment_open, "\n", NULL);
518 frame_end = g_strconcat(ft->comment_close, "\n", NULL);
519 line_prefix = "";
521 else
523 frame_start = NULL;
524 frame_end = NULL;
525 line_prefix = ft->comment_open;
528 else
529 { /* use C-like multi-line comments as fallback */
530 frame_start = g_strdup("/*\n");
531 frame_end = g_strdup("*/\n");
532 line_prefix = "";
535 /* do some magic to nicely format C-like multi-line comments */
536 if (NZV(frame_start) && frame_start[1] == '*')
538 /* prefix the string with a space */
539 setptr(frame_end, g_strconcat(" ", frame_end, NULL));
540 line_prefix = " *";
543 /* construct the real prefix with given amount of whitespace */
544 i = (indent > strlen(line_prefix)) ? (indent - strlen(line_prefix)) : strlen(line_prefix);
545 tmp = g_strnfill(i, ' ');
546 prefix = g_strconcat(line_prefix, tmp, NULL);
547 g_free(tmp);
549 /* add line_prefix to every line of comment_text */
550 lines = g_strsplit(comment_text, "\n", -1);
551 len = g_strv_length(lines) - 1;
552 for (i = 0; i < len; i++)
554 tmp = lines[i];
555 lines[i] = g_strconcat(prefix, tmp, NULL);
556 g_free(tmp);
558 tmp = g_strjoinv("\n", lines);
560 /* add frame_start and frame_end */
561 if (frame_start != NULL)
562 result = g_strconcat(frame_start, tmp, frame_end, NULL);
563 else
564 result = g_strconcat(tmp, frame_end, NULL);
566 utils_free_pointers(4, prefix, tmp, frame_start, frame_end, NULL);
567 g_strfreev(lines);
568 return result;
572 gchar *templates_get_template_licence(GeanyDocument *doc, gint licence_type)
574 GString *template;
575 gchar *result = NULL;
577 g_return_val_if_fail(doc != NULL, NULL);
578 g_return_val_if_fail(licence_type == GEANY_TEMPLATE_GPL || licence_type == GEANY_TEMPLATE_BSD, NULL);
580 template = g_string_new(templates[licence_type]);
581 replace_static_values(template);
582 templates_replace_default_dates(template);
583 templates_replace_command(template, DOC_FILENAME(doc), doc->file_type->name, NULL);
585 result = make_comment_block(template->str, FILETYPE_ID(doc->file_type), 8);
587 g_string_free(template, TRUE);
589 return result;
593 static gchar *get_template_fileheader(GeanyFiletype *ft)
595 GString *template = g_string_new(templates[GEANY_TEMPLATE_FILEHEADER]);
596 gchar *result;
598 filetypes_load_config(ft->id, FALSE); /* load any user extension setting */
600 templates_replace_valist(template,
601 "{gpl}", templates[GEANY_TEMPLATE_GPL],
602 "{bsd}", templates[GEANY_TEMPLATE_BSD],
603 NULL);
605 /* we don't replace other wildcards here otherwise they would get done twice for files */
606 result = make_comment_block(template->str, ft->id, 8);
607 g_string_free(template, TRUE);
608 return result;
612 /* TODO change the signature to take a GeanyDocument? this would break plugin API/ABI */
613 gchar *templates_get_template_fileheader(gint filetype_idx, const gchar *fname)
615 GeanyFiletype *ft = filetypes[filetype_idx];
616 gchar *str = get_template_fileheader(ft);
617 GString *template = g_string_new(str);
619 g_free(str);
620 templates_replace_common(template, fname, ft, NULL);
621 return g_string_free(template, FALSE);
625 gchar *templates_get_template_new_file(GeanyFiletype *ft)
627 GString *ft_template;
628 gchar *file_header = NULL;
630 g_return_val_if_fail(ft != NULL, NULL);
631 g_return_val_if_fail(ft->id < GEANY_MAX_BUILT_IN_FILETYPES, NULL);
633 ft_template = g_string_new(ft_templates[ft->id]);
634 if (FILETYPE_ID(ft) == GEANY_FILETYPES_NONE)
636 replace_static_values(ft_template);
638 else
639 { /* file template only used for new files */
640 file_header = get_template_fileheader(ft);
641 templates_replace_valist(ft_template, "{fileheader}", file_header, NULL);
643 templates_replace_common(ft_template, NULL, ft, NULL);
645 g_free(file_header);
646 return g_string_free(ft_template, FALSE);
650 gchar *templates_get_template_function(GeanyDocument *doc, const gchar *func_name)
652 gchar *result;
653 GString *text;
655 func_name = (func_name != NULL) ? func_name : "";
656 text = g_string_new(templates[GEANY_TEMPLATE_FUNCTION]);
658 templates_replace_valist(text, "{functionname}", func_name, NULL);
659 templates_replace_default_dates(text);
660 templates_replace_command(text, DOC_FILENAME(doc), doc->file_type->name, func_name);
662 result = make_comment_block(text->str, doc->file_type->id, 3);
664 g_string_free(text, TRUE);
665 return result;
669 gchar *templates_get_template_changelog(GeanyDocument *doc)
671 GString *result = g_string_new(templates[GEANY_TEMPLATE_CHANGELOG]);
672 const gchar *file_type_name = (doc != NULL) ? doc->file_type->name : "";
674 replace_static_values(result);
675 templates_replace_default_dates(result);
676 templates_replace_command(result, DOC_FILENAME(doc), file_type_name, NULL);
678 return g_string_free(result, FALSE);
682 void templates_free_templates(void)
684 gint i;
685 GList *children, *item;
687 for (i = 0; i < GEANY_MAX_TEMPLATES; i++)
689 g_free(templates[i]);
691 for (i = 0; i < GEANY_MAX_BUILT_IN_FILETYPES; i++)
693 g_free(ft_templates[i]);
695 /* destroy "New with template" sub menu items (in case we want to reload the templates) */
696 children = gtk_container_get_children(GTK_CONTAINER(new_with_template_menu));
697 foreach_list(item, children)
699 gtk_widget_destroy(GTK_WIDGET(item->data));
701 g_list_free(children);
703 geany_menu_button_action_set_menu(
704 GEANY_MENU_BUTTON_ACTION(toolbar_get_action_by_name("New")), NULL);
705 g_object_unref(new_with_template_menu);
706 new_with_template_menu = NULL;
710 static void replace_static_values(GString *text)
712 utils_string_replace_all(text, "{version}", template_prefs.version);
713 utils_string_replace_all(text, "{initial}", template_prefs.initials);
714 utils_string_replace_all(text, "{developer}", template_prefs.developer);
715 utils_string_replace_all(text, "{mail}", template_prefs.mail);
716 utils_string_replace_all(text, "{company}", template_prefs.company);
717 utils_string_replace_all(text, "{untitled}", GEANY_STRING_UNTITLED);
718 utils_string_replace_all(text, "{geanyversion}", "Geany " VERSION);
722 /* Replaces all static template wildcards (version, mail, company, name, ...)
723 * plus those wildcard, value pairs which are passed, e.g.
725 * templates_replace_valist(text, "{some_wildcard}", "some value",
726 * "{another_wildcard}", "another value", NULL);
728 * The argument list must be terminated with NULL. */
729 void templates_replace_valist(GString *text, const gchar *first_wildcard, ...)
731 va_list args;
732 const gchar *key, *value;
734 g_return_if_fail(text != NULL);
736 va_start(args, first_wildcard);
738 key = first_wildcard;
739 value = va_arg(args, gchar*);
741 while (key != NULL)
743 utils_string_replace_all(text, key, value);
745 key = va_arg(args, gchar*);
746 if (key == NULL || text == NULL)
747 break;
748 value = va_arg(args, gchar*);
750 va_end(args);
752 replace_static_values(text);
756 static void templates_replace_default_dates(GString *text)
758 gchar *year = utils_get_date_time(template_prefs.year_format, NULL);
759 gchar *date = utils_get_date_time(template_prefs.date_format, NULL);
760 gchar *datetime = utils_get_date_time(template_prefs.datetime_format, NULL);
762 g_return_if_fail(text != NULL);
764 templates_replace_valist(text,
765 "{year}", year,
766 "{date}", date,
767 "{datetime}", datetime,
768 NULL);
770 utils_free_pointers(3, year, date, datetime, NULL);
774 static gchar *run_command(const gchar *command, const gchar *file_name,
775 const gchar *file_type, const gchar *func_name)
777 gchar *result = NULL;
778 gchar **argv;
780 if (g_shell_parse_argv(command, NULL, &argv, NULL))
782 GError *error = NULL;
783 gchar **env;
785 file_name = (file_name != NULL) ? file_name : "";
786 file_type = (file_type != NULL) ? file_type : "";
787 func_name = (func_name != NULL) ? func_name : "";
789 env = utils_copy_environment(NULL,
790 "GEANY_FILENAME", file_name,
791 "GEANY_FILETYPE", file_type,
792 "GEANY_FUNCNAME", func_name,
793 NULL);
794 if (! utils_spawn_sync(NULL, argv, env, G_SPAWN_SEARCH_PATH,
795 NULL, NULL, &result, NULL, NULL, &error))
797 g_warning("templates_replace_command: %s", error->message);
798 g_error_free(error);
799 return NULL;
801 g_strfreev(argv);
802 g_strfreev(env);
804 return result;
808 static void templates_replace_command(GString *text, const gchar *file_name,
809 const gchar *file_type, const gchar *func_name)
811 gchar *match = NULL;
812 gchar *wildcard = NULL;
813 gchar *cmd;
814 gchar *result;
816 g_return_if_fail(text != NULL);
818 while ((match = strstr(text->str, "{command:")) != NULL)
820 cmd = match;
821 while (*match != '}' && *match != '\0')
822 match++;
824 wildcard = g_strndup(cmd, match - cmd + 1);
825 cmd = g_strndup(wildcard + 9, strlen(wildcard) - 10);
827 result = run_command(cmd, file_name, file_type, func_name);
828 if (result != NULL)
830 utils_string_replace_first(text, wildcard, result);
831 g_free(result);
833 else
834 utils_string_replace_first(text, wildcard, "");
836 g_free(wildcard);
837 g_free(cmd);