diff --git a/ChangeLog b/ChangeLog
index f66ec60..3aa7e91 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2010-11-21  Eugene Arshinov  <earshinov(at)gmail(dot)com>
+
+ * src/editor.c, src/templates.c, src/templates.h:
+   Fix a bug in editor.c:snippets_make_replacements() with handling of
+   {ob} and {cb}. Function template_replace_common() now takes an
+   argument that controls if {ob} and {cb} should be replaced.
+
+
 2010-11-18  Nick Treleaven  <nick(dot)treleaven(at)btinternet(dot)com>
 
  * src/build.c, src/dialogs.c, src/dialogs.h, src/callbacks.c:
diff --git a/src/editor.c b/src/editor.c
index 46a0666..bbb73f7 100644
--- a/src/editor.c
+++ b/src/editor.c
@@ -2402,12 +2402,13 @@ static gssize snippets_make_replacements(GeanyEditor *editor, GString *pattern,
 		gsize indent_size)
 {
 	gssize cur_index = -1;
-	gchar *whitespace;
-	gint i, tmp_pos, whitespace_len, nl_count = 0;
+	gchar *whitespace, *ptr;
+	gint i, pos, whitespace_len, nl_count = 0, offset;
 	GHashTable *specials;
-	GList *temp_list = NULL;
+	GList *temp_list = NULL, *node;
 	const GeanyIndentPrefs *iprefs = editor_get_indent_prefs(editor);
 	gint cursor_steps, old_cursor = 0;
+	gboolean cur_index_handled;
 
 	/* replace 'special' completions */
 	specials = g_hash_table_lookup(snippet_hash, "Special");
@@ -2418,8 +2419,8 @@ static gssize snippets_make_replacements(GeanyEditor *editor, GString *pattern,
 		g_hash_table_foreach(specials, snippets_replace_specials, NULL);
 	}
 
-	/* replace any template {foo} wildcards */
-	templates_replace_common(pattern, editor->document->file_name, editor->document->file_type, NULL);
+	/* replace any template {foo} wildcards except braces {ob} and {cb}  */
+	templates_replace_common(pattern, editor->document->file_name, editor->document->file_type, NULL, FALSE);
 
 	/* transform other wildcards */
 	/* convert to %newlines%, else we get endless loops */
@@ -2442,7 +2443,7 @@ static gssize snippets_make_replacements(GeanyEditor *editor, GString *pattern,
 	{
 		/* replace every %newline% (up to next %cursor%) with EOL,
 		 * and update cursor_steps after */
-		while ((tmp_pos = utils_strpos(pattern->str, "%newline%")) < cursor_steps && tmp_pos != -1)
+		while ((pos = utils_strpos(pattern->str, "%newline%")) < cursor_steps && pos != -1)
 		{
 			nl_count++;
 			utils_string_replace_first(pattern, "%newline%", editor_get_eol_char(editor));
@@ -2450,7 +2451,7 @@ static gssize snippets_make_replacements(GeanyEditor *editor, GString *pattern,
 		}
 		/* replace every %ws% (up to next %cursor%) with whitespaces,
 		 * and update cursor_steps after */
-		while ((tmp_pos = utils_strpos(pattern->str, "%ws%")) < cursor_steps && tmp_pos != -1)
+		while ((pos = utils_strpos(pattern->str, "%ws%")) < cursor_steps && pos != -1)
 		{
 			utils_string_replace_first(pattern, "%ws%", whitespace);
 			cursor_steps = utils_strpos(pattern->str, "%cursor%");
@@ -2480,16 +2481,61 @@ static gssize snippets_make_replacements(GeanyEditor *editor, GString *pattern,
 	g_free(whitespace);
 
 	/* escape % last */
-	/* Bug: {ob}pc{cb} will be replaced by % too */
-	templates_replace_valist(pattern, "{pc}", "%", NULL);
+	pos = 0;
+	offset = 0;
+	node = temp_list;
+	cur_index_handled = FALSE;
+	while ((ptr = strchr(pattern->str + pos, '{')) != NULL)
+	{
+		pos = ptr - pattern->str;
+
+		/* synchronously iterate the list of cursor positions, applying current offset
+		 * remember to handle cur_index separately */
+		if (!cur_index_handled && cur_index + offset <= pos)
+		{
+			cur_index += offset;
+			cur_index_handled = TRUE;
+		}
+		while (node && GPOINTER_TO_INT(node->data) + offset <= pos)
+		{
+			node->data += offset;
+			node = node->next;
+		}
+
+		if (strncmp(pattern->str + pos + 1, "pc}", 3) == 0)
+		{
+			g_string_erase(pattern, pos + 1, 3);
+			pattern->str[pos] = '%';
+			pos++; /* one char inserted */
+			offset -= 3; /* pattern became 3 chars shorter */
+		}
+		else if (strncmp(pattern->str + pos + 1, "ob}", 3) == 0)
+		{
+			g_string_erase(pattern, pos + 1, 3);
+			pattern->str[pos] = '{';
+			pos++; /* one char inserted */
+			offset -= 3; /* pattern became 3 chars shorter */
+		}
+		else if (strncmp(pattern->str + pos + 1, "cb}", 3) == 0)
+		{
+			g_string_erase(pattern, pos + 1, 3);
+			pattern->str[pos] = '}';
+			pos++; /* one char inserted */
+			offset -= 3; /* pattern became 3 chars shorter */
+		}
+		else
+			pos++; /* skip the brace */
+	}
+	if (!cur_index_handled)
+		cur_index += offset;
+	for (; node; node = node->next)
+		node->data += offset;
 
 	/* We put the cursor positions for the most recent
 	 * parsed snippet first, followed by any remaining positions */
 	i = 0;
 	if (temp_list)
 	{
-		GList *node;
-
 		foreach_list(node, temp_list)
 			g_queue_push_nth(snippet_offsets, node->data, i++);
 
diff --git a/src/templates.c b/src/templates.c
index ad37b2c..eda66c8 100644
--- a/src/templates.c
+++ b/src/templates.c
@@ -220,7 +220,7 @@ static gboolean create_new_filetype_items(void)
 
 
 void templates_replace_common(GString *template, const gchar *fname,
-							  GeanyFiletype *ft, const gchar *func_name)
+							  GeanyFiletype *ft, const gchar *func_name, gboolean replace_braces)
 {
 	gchar *shortname;
 
@@ -244,11 +244,15 @@ void templates_replace_common(GString *template, const gchar *fname,
 	templates_replace_default_dates(template);
 	templates_replace_command(template, fname, ft->name, func_name);
 	/* Bug: command results could have {ob} {cb} strings in! */
-	/* replace braces last */
-	templates_replace_valist(template,
-		"{ob}", "{",
-		"{cb}", "}",
-		NULL);
+
+	if (replace_braces)
+	{
+		/* replace braces last */
+		templates_replace_valist(template,
+			"{ob}", "{",
+			"{cb}", "}",
+			NULL);
+	}
 }
 
 
@@ -270,7 +274,7 @@ static gchar *get_template_from_file(const gchar *locale_fname, const gchar *doc
 		templates_replace_valist(template,
 			"{fileheader}", file_header,
 			NULL);
-		templates_replace_common(template, doc_filename, ft, NULL);
+		templates_replace_common(template, doc_filename, ft, NULL, TRUE);
 
 		utils_free_pointers(2, file_header, content, NULL);
 		return g_string_free(template, FALSE);
@@ -578,7 +582,7 @@ gchar *templates_get_template_fileheader(gint filetype_idx, const gchar *fname)
 	GString *template = g_string_new(str);
 
 	g_free(str);
-	templates_replace_common(template, fname, ft, NULL);
+	templates_replace_common(template, fname, ft, NULL, TRUE);
 	convert_eol_characters(template, NULL);
 	return g_string_free(template, FALSE);
 }
@@ -603,7 +607,7 @@ gchar *templates_get_template_new_file(GeanyFiletype *ft)
 		file_header = get_template_fileheader(ft);
 		templates_replace_valist(ft_template, "{fileheader}", file_header, NULL);
 	}
-	templates_replace_common(ft_template, NULL, ft, NULL);
+	templates_replace_common(ft_template, NULL, ft, NULL, TRUE);
 	convert_eol_characters(ft_template, NULL);
 
 	g_free(file_header);
diff --git a/src/templates.h b/src/templates.h
index 74a1555..069069c 100644
--- a/src/templates.h
+++ b/src/templates.h
@@ -73,7 +73,7 @@ gchar *templates_get_template_function(GeanyDocument *doc, const gchar *func_nam
 gchar *templates_get_template_licence(GeanyDocument *doc, gint licence_type);
 
 void templates_replace_common(GString *template, const gchar *fname,
-	GeanyFiletype *ft, const gchar *func_name);
+	GeanyFiletype *ft, const gchar *func_name, gboolean replace_braces);
 
 void templates_replace_valist(GString *text,
 	const gchar *first_wildcard, ...) G_GNUC_NULL_TERMINATED;
