Fix indenting a snippet when there is whitespace after the
[geany-mirror.git] / src / templates.c
blob12db3182393a51657aac29dcfeda73ac128e3ead
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"
44 GeanyTemplatePrefs template_prefs;
46 static GtkWidget *new_with_template_menu = NULL; /* submenu used for both file menu and toolbar */
49 /* TODO: implement custom insertion templates, put these into files in data/templates */
51 /* default templates, only for initial tempate file creation on first start of Geany */
52 static const gchar templates_gpl_notice[] = "\
53 This program is free software; you can redistribute it and/or modify\n\
54 it under the terms of the GNU General Public License as published by\n\
55 the Free Software Foundation; either version 2 of the License, or\n\
56 (at your option) any later version.\n\
57 \n\
58 This program is distributed in the hope that it will be useful,\n\
59 but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
60 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\
61 GNU General Public License for more details.\n\
62 \n\
63 You should have received a copy of the GNU General Public License\n\
64 along with this program; if not, write to the Free Software\n\
65 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,\n\
66 MA 02110-1301, USA.\n\
69 static const gchar templates_bsd_notice[] = "\
70 Redistribution and use in source and binary forms, with or without\n\
71 modification, are permitted provided that the following conditions are\n\
72 met:\n\
73 \n\
74 * Redistributions of source code must retain the above copyright\n\
75 notice, this list of conditions and the following disclaimer.\n\
76 * Redistributions in binary form must reproduce the above\n\
77 copyright notice, this list of conditions and the following disclaimer\n\
78 in the documentation and/or other materials provided with the\n\
79 distribution.\n\
80 * Neither the name of the {company} nor the names of its\n\
81 contributors may be used to endorse or promote products derived from\n\
82 this software without specific prior written permission.\n\
83 \n\
84 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\
85 \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n\
86 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n\
87 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n\
88 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n\
89 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n\
90 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n\
91 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n\
92 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n\
93 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n\
94 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\
97 static const gchar templates_function_description[] = "\
98 \n\
99 name: {functionname}\n\
100 @param\n\
101 @return\n\
104 static const gchar templates_multiline[] = "\
109 static const gchar templates_fileheader[] = "\
110 {filename}\n\
112 Copyright {year} {developer} <{mail}>\n\
114 {gpl}\
117 static const gchar templates_changelog[] = "\
118 {date} {developer} <{mail}>\n\
120 * \n\n\n";
122 static gchar *templates[GEANY_MAX_TEMPLATES];
124 /* We should probably remove filetype templates support soon - users can use custom
125 * file templates instead. */
126 static gchar *ft_templates[GEANY_MAX_BUILT_IN_FILETYPES] = {NULL};
129 static void replace_static_values(GString *text);
132 /* some simple macros to reduce code size and make the code readable */
133 #define TEMPLATES_GET_FILENAME(shortname) \
134 g_strconcat(app->configdir, \
135 G_DIR_SEPARATOR_S GEANY_TEMPLATES_SUBDIR G_DIR_SEPARATOR_S, shortname, NULL)
137 #define TEMPLATES_READ_FILE(fname, contents_ptr) \
138 g_file_get_contents(fname, contents_ptr, NULL, NULL);
141 static void create_template_file_if_necessary(const gchar *filename, const gchar *content)
143 if (! g_file_test(filename, G_FILE_TEST_EXISTS))
145 if (file_prefs.default_eol_character != SC_EOL_LF)
147 /* Replace the \n characters in the default template text by the proper
148 * platform-specific line ending characters. */
149 GString *tmp = g_string_new(content);
150 const gchar *eol_str = (file_prefs.default_eol_character == SC_EOL_CR) ? "\r" : "\r\n";
152 utils_string_replace_all(tmp, "\n", eol_str);
153 utils_write_file(filename, tmp->str);
154 g_string_free(tmp, TRUE);
156 else
157 utils_write_file(filename, content);
162 /* FIXME the callers should use GStrings instead of char arrays */
163 static gchar *replace_all(gchar *text, const gchar *year, const gchar *date, const gchar *datetime)
165 GString *str;
167 if (text == NULL)
168 return NULL;
170 str = g_string_new(text);
172 g_free(text);
173 templates_replace_valist(str,
174 "{year}", year,
175 "{date}", date,
176 "{datetime}", datetime,
177 NULL);
179 return g_string_free(str, FALSE);
183 static void init_general_templates(const gchar *year, const gchar *date, const gchar *datetime)
185 gchar *template_filename_fileheader = TEMPLATES_GET_FILENAME("fileheader");
186 gchar *template_filename_gpl = TEMPLATES_GET_FILENAME("gpl");
187 gchar *template_filename_bsd = TEMPLATES_GET_FILENAME("bsd");
188 gchar *template_filename_function = TEMPLATES_GET_FILENAME("function");
189 gchar *template_filename_changelog = TEMPLATES_GET_FILENAME("changelog");
191 /* create the template files in the configuration directory, if they don't exist */
192 create_template_file_if_necessary(template_filename_fileheader, templates_fileheader);
193 create_template_file_if_necessary(template_filename_gpl, templates_gpl_notice);
194 create_template_file_if_necessary(template_filename_bsd, templates_bsd_notice);
195 create_template_file_if_necessary(template_filename_function, templates_function_description);
196 create_template_file_if_necessary(template_filename_changelog, templates_changelog);
198 /* read the contents */
199 TEMPLATES_READ_FILE(template_filename_fileheader, &templates[GEANY_TEMPLATE_FILEHEADER]);
200 templates[GEANY_TEMPLATE_FILEHEADER] = replace_all(templates[GEANY_TEMPLATE_FILEHEADER], year, date, datetime);
202 TEMPLATES_READ_FILE(template_filename_gpl, &templates[GEANY_TEMPLATE_GPL]);
203 templates[GEANY_TEMPLATE_GPL] = replace_all(templates[GEANY_TEMPLATE_GPL], year, date, datetime);
205 TEMPLATES_READ_FILE(template_filename_bsd, &templates[GEANY_TEMPLATE_BSD]);
206 templates[GEANY_TEMPLATE_BSD] = replace_all(templates[GEANY_TEMPLATE_BSD], year, date, datetime);
208 TEMPLATES_READ_FILE(template_filename_function, &templates[GEANY_TEMPLATE_FUNCTION]);
209 templates[GEANY_TEMPLATE_FUNCTION] = replace_all(templates[GEANY_TEMPLATE_FUNCTION], year, date, datetime);
211 TEMPLATES_READ_FILE(template_filename_changelog, &templates[GEANY_TEMPLATE_CHANGELOG]);
212 templates[GEANY_TEMPLATE_CHANGELOG] = replace_all(templates[GEANY_TEMPLATE_CHANGELOG], year, date, datetime);
214 /* free the whole stuff */
215 g_free(template_filename_fileheader);
216 g_free(template_filename_gpl);
217 g_free(template_filename_bsd);
218 g_free(template_filename_function);
219 g_free(template_filename_changelog);
223 static void init_ft_templates(const gchar *year, const gchar *date, const gchar *datetime)
225 filetype_id ft_id;
227 for (ft_id = 0; ft_id < GEANY_MAX_BUILT_IN_FILETYPES; ft_id++)
229 gchar *ext = filetypes_get_conf_extension(ft_id);
230 gchar *shortname = g_strconcat("filetype.", ext, NULL);
231 gchar *fname = TEMPLATES_GET_FILENAME(shortname);
233 TEMPLATES_READ_FILE(fname, &ft_templates[ft_id]);
234 ft_templates[ft_id] = replace_all(ft_templates[ft_id], year, date, datetime);
236 g_free(fname);
237 g_free(shortname);
238 g_free(ext);
243 static void
244 on_new_with_filetype_template(GtkMenuItem *menuitem, gpointer user_data)
246 GeanyFiletype *ft = user_data;
247 gchar *template = templates_get_template_new_file(ft);
249 document_new_file(NULL, ft, template);
250 g_free(template);
254 /* TODO: remove filetype template support after 0.19 */
255 static gboolean create_new_filetype_items(void)
257 GSList *node;
258 gboolean ret = FALSE;
259 GtkWidget *menu = NULL;
261 foreach_slist(node, filetypes_by_title)
263 GeanyFiletype *ft = node->data;
264 GtkWidget *item;
266 if (ft->id >= GEANY_MAX_BUILT_IN_FILETYPES || ft_templates[ft->id] == NULL)
267 continue;
269 if (!menu)
271 item = gtk_menu_item_new_with_label(_("Old"));
272 menu = gtk_menu_new();
273 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), menu);
274 gtk_widget_show_all(item);
275 gtk_container_add(GTK_CONTAINER(new_with_template_menu), item);
277 item = gtk_menu_item_new_with_label(ft->title);
278 gtk_widget_show(item);
279 gtk_container_add(GTK_CONTAINER(menu), item);
280 g_signal_connect(item, "activate", G_CALLBACK(on_new_with_filetype_template), ft);
281 ret = TRUE;
283 return ret;
287 static gchar *get_template_from_file(const gchar *locale_fname, const gchar *doc_filename,
288 GeanyFiletype *ft)
290 gchar *content;
291 GString *template = NULL;
293 g_file_get_contents(locale_fname, &content, NULL, NULL);
295 if (content != NULL)
297 gchar *file_header;
299 template = g_string_new(content);
301 file_header = templates_get_template_fileheader(FILETYPE_ID(ft), doc_filename);
302 templates_replace_valist(template,
303 "{filename}", doc_filename,
304 "{fileheader}", file_header,
305 NULL);
306 templates_replace_default_dates(template);
307 templates_replace_command(template, doc_filename, ft->name, NULL);
309 utils_free_pointers(2, file_header, content, NULL);
310 return g_string_free(template, FALSE);
312 return NULL;
316 static void
317 on_new_with_file_template(GtkMenuItem *menuitem, G_GNUC_UNUSED gpointer user_data)
319 gchar *fname = ui_menu_item_get_text(menuitem);
320 GeanyFiletype *ft;
321 gchar *template;
322 const gchar *extension = strrchr(fname, '.'); /* easy way to get the file extension */
323 gchar *new_filename = g_strconcat(GEANY_STRING_UNTITLED, extension, NULL);
324 gchar *path;
326 ft = filetypes_detect_from_extension(fname);
327 setptr(fname, utils_get_locale_from_utf8(fname));
329 /* fname is just the basename from the menu item, so prepend the custom files path */
330 path = g_build_path(G_DIR_SEPARATOR_S, app->configdir, GEANY_TEMPLATES_SUBDIR,
331 "files", fname, NULL);
332 template = get_template_from_file(path, new_filename, ft);
333 if (!template)
335 /* try the system path */
336 g_free(path);
337 path = g_build_path(G_DIR_SEPARATOR_S, app->datadir, GEANY_TEMPLATES_SUBDIR,
338 "files", fname, NULL);
339 template = get_template_from_file(path, new_filename, ft);
341 if (template)
342 document_new_file(new_filename, ft, template);
343 else
345 setptr(fname, utils_get_utf8_from_locale(fname));
346 ui_set_statusbar(TRUE, _("Could not find file '%s'."), fname);
348 g_free(template);
349 g_free(path);
350 g_free(new_filename);
351 g_free(fname);
355 static void add_file_item(const gchar *fname, GtkWidget *menu)
357 GtkWidget *tmp_button;
358 gchar *label;
360 g_return_if_fail(fname);
361 g_return_if_fail(menu);
363 label = utils_get_utf8_from_locale(fname);
365 tmp_button = gtk_menu_item_new_with_label(label);
366 gtk_widget_show(tmp_button);
367 gtk_container_add(GTK_CONTAINER(menu), tmp_button);
368 g_signal_connect(tmp_button, "activate", G_CALLBACK(on_new_with_file_template), NULL);
370 g_free(label);
374 static gboolean add_custom_template_items(void)
376 GSList *list = utils_get_config_files(GEANY_TEMPLATES_SUBDIR G_DIR_SEPARATOR_S "files");
377 GSList *node;
379 foreach_slist(node, list)
381 gchar *fname = node->data;
383 add_file_item(fname, new_with_template_menu);
384 g_free(fname);
386 g_slist_free(list);
387 return list != NULL;
391 static void create_file_template_menu(void)
393 GtkWidget *sep = NULL;
395 new_with_template_menu = gtk_menu_new();
397 if (add_custom_template_items())
399 sep = gtk_separator_menu_item_new();
400 gtk_container_add(GTK_CONTAINER(new_with_template_menu), sep);
402 if (create_new_filetype_items() && sep)
404 gtk_widget_show(sep);
406 /* unless the file menu is showing, menu should be in the toolbar widget */
407 geany_menu_button_action_set_menu(GEANY_MENU_BUTTON_ACTION(
408 toolbar_get_action_by_name("New")), new_with_template_menu);
412 static void on_file_menu_show(GtkWidget *item)
414 geany_menu_button_action_set_menu(
415 GEANY_MENU_BUTTON_ACTION(toolbar_get_action_by_name("New")), NULL);
416 item = ui_lookup_widget(main_widgets.window, "menu_new_with_template1");
417 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_with_template_menu);
421 static void on_file_menu_hide(GtkWidget *item)
423 item = ui_lookup_widget(main_widgets.window, "menu_new_with_template1");
424 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), NULL);
425 geany_menu_button_action_set_menu(
426 GEANY_MENU_BUTTON_ACTION(toolbar_get_action_by_name("New")), new_with_template_menu);
430 void templates_init(void)
432 gchar *year = utils_get_date_time(template_prefs.year_format, NULL);
433 gchar *date = utils_get_date_time(template_prefs.date_format, NULL);
434 gchar *datetime = utils_get_date_time(template_prefs.datetime_format, NULL);
435 GtkWidget *item;
437 init_general_templates(year, date, datetime);
438 init_ft_templates(year, date, datetime);
440 g_free(date);
441 g_free(datetime);
442 g_free(year);
444 create_file_template_menu();
445 /* we hold our own ref for the menu as it has no parent whilst being moved */
446 g_object_ref(new_with_template_menu);
448 /* reparent the template menu as needed */
449 item = ui_lookup_widget(main_widgets.window, "file1");
450 item = gtk_menu_item_get_submenu(GTK_MENU_ITEM(item));
451 g_signal_connect(item, "show", G_CALLBACK(on_file_menu_show), NULL);
452 g_signal_connect(item, "hide", G_CALLBACK(on_file_menu_hide), NULL);
456 /* indent is used to make some whitespace between comment char and real start of the line
457 * e.g. indent = 8 prints " * here comes the text of the line"
458 * indent is meant to be the whole amount of characters before the real line content follows, i.e.
459 * 6 characters are filled with whitespace when the comment characters include " *" */
460 /* TODO make this function operating on a GString */
461 static gchar *make_comment_block(const gchar *comment_text, gint filetype_idx, guint indent)
463 gchar *frame_start; /* to add before comment_text */
464 gchar *frame_end; /* to add after comment_text */
465 gchar *line_prefix; /* to add before every line in comment_text */
466 gchar *result;
467 gchar *tmp;
468 gchar *prefix;
469 gchar **lines;
470 guint i, len;
471 GeanyFiletype *ft = filetypes_index(filetype_idx);
473 g_return_val_if_fail(ft != NULL, NULL);
475 if (NZV(ft->comment_open))
477 if (NZV(ft->comment_close))
479 frame_start = g_strconcat(ft->comment_open, "\n", NULL);
480 frame_end = g_strconcat(ft->comment_close, "\n", NULL);
481 line_prefix = "";
483 else
485 frame_start = NULL;
486 frame_end = NULL;
487 line_prefix = ft->comment_open;
490 else
491 { /* use C-like multi-line comments as fallback */
492 frame_start = g_strdup("/*\n");
493 frame_end = g_strdup("*/\n");
494 line_prefix = "";
497 /* do some magic to nicely format C-like multi-line comments */
498 if (NZV(frame_start) && frame_start[1] == '*')
500 /* prefix the string with a space */
501 setptr(frame_end, g_strconcat(" ", frame_end, NULL));
502 line_prefix = " *";
505 /* construct the real prefix with given amount of whitespace */
506 i = (indent > strlen(line_prefix)) ? (indent - strlen(line_prefix)) : strlen(line_prefix);
507 tmp = g_strnfill(i, ' ');
508 prefix = g_strconcat(line_prefix, tmp, NULL);
509 g_free(tmp);
511 /* add line_prefix to every line of comment_text */
512 lines = g_strsplit(comment_text, "\n", -1);
513 len = g_strv_length(lines) - 1;
514 for (i = 0; i < len; i++)
516 tmp = lines[i];
517 lines[i] = g_strconcat(prefix, tmp, NULL);
518 g_free(tmp);
520 tmp = g_strjoinv("\n", lines);
522 /* add frame_start and frame_end */
523 if (frame_start != NULL)
524 result = g_strconcat(frame_start, tmp, frame_end, NULL);
525 else
526 result = g_strconcat(tmp, frame_end, NULL);
528 utils_free_pointers(4, prefix, tmp, frame_start, frame_end, NULL);
529 g_strfreev(lines);
530 return result;
534 gchar *templates_get_template_licence(GeanyDocument *doc, gint licence_type)
536 GString *template;
537 gchar *result = NULL;
539 g_return_val_if_fail(doc != NULL, NULL);
540 g_return_val_if_fail(licence_type == GEANY_TEMPLATE_GPL || licence_type == GEANY_TEMPLATE_BSD, NULL);
542 template = g_string_new(templates[licence_type]);
543 replace_static_values(template);
544 templates_replace_default_dates(template);
545 templates_replace_command(template, DOC_FILENAME(doc), doc->file_type->name, NULL);
547 result = make_comment_block(template->str, FILETYPE_ID(doc->file_type), 8);
549 g_string_free(template, TRUE);
551 return result;
555 /* TODO change the signature to take a GeanyDocument although this would break plugin API/ABI */
556 gchar *templates_get_template_fileheader(gint filetype_idx, const gchar *fname)
558 GString *template = g_string_new(templates[GEANY_TEMPLATE_FILEHEADER]);
559 gchar *shortname;
560 gchar *result;
561 filetype_id ft_id = filetype_idx;
562 GeanyFiletype *ft = filetypes[ft_id];
564 filetypes_load_config(ft_id, FALSE); /* load any user extension setting */
566 if (fname == NULL)
568 if (!ft->extension)
569 shortname = g_strdup(GEANY_STRING_UNTITLED);
570 else
571 shortname = g_strconcat(GEANY_STRING_UNTITLED, ".", ft->extension, NULL);
573 else
574 shortname = g_path_get_basename(fname);
576 templates_replace_valist(template,
577 "{filename}", shortname,
578 "{gpl}", templates[GEANY_TEMPLATE_GPL],
579 "{bsd}", templates[GEANY_TEMPLATE_BSD],
580 NULL);
581 templates_replace_default_dates(template);
582 templates_replace_command(template, fname, ft->name, NULL);
584 result = make_comment_block(template->str, ft_id, 8);
586 g_string_free(template, TRUE);
587 g_free(shortname);
588 return result;
592 gchar *templates_get_template_new_file(GeanyFiletype *ft)
594 GString *ft_template;
595 gchar *file_header = NULL;
597 g_return_val_if_fail(ft != NULL, NULL);
598 g_return_val_if_fail(ft->id < GEANY_MAX_BUILT_IN_FILETYPES, NULL);
600 ft_template = g_string_new(ft_templates[ft->id]);
601 if (FILETYPE_ID(ft) == GEANY_FILETYPES_NONE)
603 replace_static_values(ft_template);
605 else
606 { /* file template only used for new files */
607 file_header = templates_get_template_fileheader(ft->id, NULL);
608 templates_replace_valist(ft_template, "{fileheader}", file_header, NULL);
610 templates_replace_default_dates(ft_template);
611 templates_replace_command(ft_template, NULL, ft->name, NULL);
613 g_free(file_header);
614 return g_string_free(ft_template, FALSE);
618 gchar *templates_get_template_function(GeanyDocument *doc, const gchar *func_name)
620 gchar *result;
621 GString *text;
623 func_name = (func_name != NULL) ? func_name : "";
624 text = g_string_new(templates[GEANY_TEMPLATE_FUNCTION]);
626 templates_replace_valist(text, "{functionname}", func_name, NULL);
627 templates_replace_default_dates(text);
628 templates_replace_command(text, DOC_FILENAME(doc), doc->file_type->name, func_name);
630 result = make_comment_block(text->str, doc->file_type->id, 3);
632 g_string_free(text, TRUE);
633 return result;
637 gchar *templates_get_template_changelog(GeanyDocument *doc)
639 GString *result = g_string_new(templates[GEANY_TEMPLATE_CHANGELOG]);
640 gchar *file_type_name = (doc != NULL) ? doc->file_type->name : "";
642 replace_static_values(result);
643 templates_replace_default_dates(result);
644 templates_replace_command(result, DOC_FILENAME(doc), file_type_name, NULL);
646 return g_string_free(result, FALSE);
650 void templates_free_templates(void)
652 gint i;
653 GList *children, *item;
655 for (i = 0; i < GEANY_MAX_TEMPLATES; i++)
657 g_free(templates[i]);
659 for (i = 0; i < GEANY_MAX_BUILT_IN_FILETYPES; i++)
661 g_free(ft_templates[i]);
663 /* destroy "New with template" sub menu items (in case we want to reload the templates) */
664 children = gtk_container_get_children(GTK_CONTAINER(new_with_template_menu));
665 foreach_list(item, children)
667 gtk_widget_destroy(GTK_WIDGET(item->data));
669 g_list_free(children);
671 geany_menu_button_action_set_menu(
672 GEANY_MENU_BUTTON_ACTION(toolbar_get_action_by_name("New")), NULL);
673 g_object_unref(new_with_template_menu);
674 new_with_template_menu = NULL;
678 static void replace_static_values(GString *text)
680 utils_string_replace_all(text, "{version}", template_prefs.version);
681 utils_string_replace_all(text, "{initial}", template_prefs.initials);
682 utils_string_replace_all(text, "{developer}", template_prefs.developer);
683 utils_string_replace_all(text, "{mail}", template_prefs.mail);
684 utils_string_replace_all(text, "{company}", template_prefs.company);
685 utils_string_replace_all(text, "{untitled}", GEANY_STRING_UNTITLED);
686 utils_string_replace_all(text, "{geanyversion}", "Geany " VERSION);
690 /* Replaces all static template wildcards (version, mail, company, name, ...)
691 * plus those wildcard, value pairs which are passed, e.g.
693 * templates_replace_valist(text, "{some_wildcard}", "some value",
694 * "{another_wildcard}", "another value", NULL);
696 * The argument list must be terminated with NULL. */
697 void templates_replace_valist(GString *text, const gchar *first_wildcard, ...)
699 va_list args;
700 const gchar *key, *value;
702 g_return_if_fail(text != NULL);
704 va_start(args, first_wildcard);
706 key = first_wildcard;
707 value = va_arg(args, gchar*);
709 while (key != NULL)
711 utils_string_replace_all(text, key, value);
713 key = va_arg(args, gchar*);
714 if (key == NULL || text == NULL)
715 break;
716 value = va_arg(args, gchar*);
718 va_end(args);
720 replace_static_values(text);
724 void templates_replace_default_dates(GString *text)
726 gchar *year = utils_get_date_time(template_prefs.year_format, NULL);
727 gchar *date = utils_get_date_time(template_prefs.date_format, NULL);
728 gchar *datetime = utils_get_date_time(template_prefs.datetime_format, NULL);
730 g_return_if_fail(text != NULL);
732 templates_replace_valist(text,
733 "{year}", year,
734 "{date}", date,
735 "{datetime}", datetime,
736 NULL);
738 utils_free_pointers(3, year, date, datetime, NULL);
742 static gchar *run_command(const gchar *command, const gchar *file_name,
743 const gchar *file_type, const gchar *func_name)
745 gchar *result = NULL;
746 gchar **argv;
748 if (g_shell_parse_argv(command, NULL, &argv, NULL))
750 GError *error = NULL;
751 gchar **env;
753 file_name = (file_name != NULL) ? file_name : "";
754 file_type = (file_type != NULL) ? file_type : "";
755 func_name = (func_name != NULL) ? func_name : "";
757 env = utils_copy_environment(NULL,
758 "GEANY_FILENAME", file_name,
759 "GEANY_FILETYPE", file_type,
760 "GEANY_FUNCNAME", func_name,
761 NULL);
762 if (! utils_spawn_sync(NULL, argv, env, G_SPAWN_SEARCH_PATH,
763 NULL, NULL, &result, NULL, NULL, &error))
765 g_warning("templates_replace_command: %s", error->message);
766 g_error_free(error);
767 return NULL;
769 g_strfreev(argv);
770 g_strfreev(env);
772 return result;
776 void templates_replace_command(GString *text, const gchar *file_name,
777 const gchar *file_type, const gchar *func_name)
779 gchar *match = NULL;
780 gchar *wildcard = NULL;
781 gchar *cmd;
782 gchar *result;
784 g_return_if_fail(text != NULL);
786 while ((match = strstr(text->str, "{command:")) != NULL)
788 cmd = match;
789 while (*match != '}' && *match != '\0')
790 match++;
792 wildcard = g_strndup(cmd, match - cmd + 1);
793 cmd = g_strndup(wildcard + 9, strlen(wildcard) - 10);
795 result = run_command(cmd, file_name, file_type, func_name);
796 if (result != NULL)
798 utils_string_replace_first(text, wildcard, result);
799 g_free(result);
801 else
802 utils_string_replace_first(text, wildcard, "");
804 g_free(wildcard);
805 g_free(cmd);