Index: src/utils.c
===================================================================
--- src/utils.c	(Revision 3437)
+++ src/utils.c	(Arbeitskopie)
@@ -1354,6 +1354,41 @@
 }
 
 
+static guint utils_string_replace_helper(GString *haystack,
+											const gchar *needle,
+											const gchar *replace,
+											const guint max_replaces)
+{
+	const gchar *stack, *match;
+	guint ret = 0;
+	gssize pos;
+
+	if (haystack->len == 0)
+		return FALSE;
+	g_return_val_if_fail(NZV(needle), 0);
+
+	stack = haystack->str;
+	if (!(match = strstr(stack, needle)))
+		return 0;
+	do
+	{
+		pos = match - haystack->str;
+		g_string_erase(haystack, pos, strlen(needle));
+
+		/* make next search after removed matching text.
+		 * (we have to be careful to only use haystack->str as its address may change) */
+		stack = haystack->str + pos;
+
+		if (replace)
+		{
+			g_string_insert(haystack, pos, replace);
+			stack = haystack->str + pos + strlen(replace);	/* skip past replacement */
+		}
+	} while ( ++ret != max_replaces && (match = strstr(stack, needle)));
+	return ret;
+}
+
+
 /**
  * Replaces all occurrences of @c needle in @c haystack with @c replace.
  * As of Geany 0.16, @a replace can match @a needle, so the following will work:
@@ -1363,40 +1398,27 @@
  * @param needle The string which should be replaced.
  * @param replace The replacement for @c needle.
  *
- * @return @a TRUE if @c needle was found, else @a FALSE.
+ * @return @a amount of replacements done
  **/
-gboolean utils_string_replace_all(GString *haystack, const gchar *needle, const gchar *replace)
+guint utils_string_replace_all(GString *haystack, const gchar *needle, const gchar *replace)
 {
-	const gchar *stack, *match;
-	gssize pos = -1;
+	return utils_string_replace_helper(haystack, needle, replace, 0);
+}
 
-	if (haystack->len == 0)
-		return FALSE;
-	g_return_val_if_fail(NZV(needle), FALSE);
-
-	stack = haystack->str;
-	while (1)
-	{
-		match = strstr(stack, needle);
-		if (match == NULL)
-			break;
-		else
-		{
-			pos = match - haystack->str;
-			g_string_erase(haystack, pos, strlen(needle));
-
-			/* make next search after removed matching text.
-			 * (we have to be careful to only use haystack->str as its address may change) */
-			stack = haystack->str + pos;
-
-			if (replace)
-			{
-				g_string_insert(haystack, pos, replace);
-				stack = haystack->str + pos + strlen(replace);	/* skip past replacement */
-			}
-		}
-	}
-	return (pos != -1);
+/**
+ * Replaces the first occurrence of @c needle in @c haystack with @c replace.
+ * As of Geany 0.16, @a replace can match @a needle, so the following will work:
+ * @code utils_string_replace_all(text, "\n", "\r\n"); @endcode
+ *
+ * @param haystack The input string to operate on. This string is modified in place.
+ * @param needle The string which should be replaced.
+ * @param replace The replacement for @c needle.
+ *
+ * @return @a amount of replacements done
+ **/
+guint utils_string_replace_first(GString *haystack, const gchar *needle, const gchar *replace)
+{
+	return utils_string_replace_helper(haystack, needle, replace, 1);
 }
 
 
Index: src/utils.h
===================================================================
--- src/utils.h	(Revision 3437)
+++ src/utils.h	(Arbeitskopie)
@@ -82,8 +82,10 @@
 
 gchar *utils_get_hostname(void);
 
-gboolean utils_string_replace_all(GString *str, const gchar *needle, const gchar *replace);
+guint utils_string_replace_all(GString *haystack, const gchar *needle, const gchar *replace);
 
+guint utils_string_replace_first(GString *haystack, const gchar *needle, const gchar *replace);
+
 gchar *utils_str_replace(gchar *haystack, const gchar *needle, const gchar *replacement);
 
 gint utils_strpos(const gchar* haystack, const gchar *needle);
Index: src/keybindings.c
===================================================================
--- src/keybindings.c	(Revision 3437)
+++ src/keybindings.c	(Arbeitskopie)
@@ -239,6 +239,8 @@
 		GDK_Down, GDK_MOD1_MASK, "edit_scrolllinedown", _("Scroll down the view by one line"), NULL);
 	keybindings_set_item(group, GEANY_KEYS_EDITOR_COMPLETESNIPPET, NULL,	/* handled specially in check_snippet_completion() */
 		GDK_Tab, 0, "edit_completesnippet", _("Complete snippet"), NULL);
+	keybindings_set_item(group, GEANY_KEYS_EDITOR_SNIPPET_NEXT_CURSOR, cb_func_editor_action,
+		0, 0, "move_snippetnextcursor", _("Move cursor in snippet"), NULL);
 	keybindings_set_item(group, GEANY_KEYS_EDITOR_SUPPRESSSNIPPETCOMPLETION, cb_func_editor_action,
 		0, 0, "edit_suppresssnippetcompletion", _("Suppress snippet completion"), NULL);
 	keybindings_set_item(group, GEANY_KEYS_EDITOR_CONTEXTACTION, cb_func_editor_action,
@@ -1678,6 +1680,10 @@
 		case GEANY_KEYS_EDITOR_DUPLICATELINE:
 			duplicate_lines(doc->editor);
 			break;
+		case GEANY_KEYS_EDITOR_SNIPPET_NEXT_CURSOR:
+			snippet_goto_next_cursor(doc->editor->sci,
+					sci_get_current_position(doc->editor->sci));
+			break;
 		case GEANY_KEYS_EDITOR_DELETELINE:
 			delete_lines(doc->editor);
 			break;
Index: src/keybindings.h
===================================================================
--- src/keybindings.h	(Revision 3437)
+++ src/keybindings.h	(Arbeitskopie)
@@ -139,6 +139,7 @@
 	GEANY_KEYS_EDITOR_AUTOCOMPLETE,
 	GEANY_KEYS_EDITOR_CALLTIP,
 	GEANY_KEYS_EDITOR_MACROLIST,
+	GEANY_KEYS_EDITOR_SNIPPET_NEXT_CURSOR,
 	GEANY_KEYS_EDITOR_COUNT
 };
 
Index: src/makefile.win32
===================================================================
--- src/makefile.win32	(Revision 3437)
+++ src/makefile.win32	(Arbeitskopie)
@@ -64,7 +64,7 @@
 		geanyentryaction.o geanymenubuttonaction.o geanyobject.o geanywraplabel.o highlighting.o \
 		interface.o keybindings.o keyfile.o \
 		log.o main.o msgwindow.o navqueue.o notebook.o plugins.o prefs.o printing.o project.o \
-		sciwrappers.o search.o socket.o stash.o \
+		queue.o sciwrappers.o search.o socket.o stash.o \
 		symbols.o templates.o toolbar.o tools.o treeviews.o \
 		ui_utils.o utils.o win32.o
 
Index: src/plugindata.h
===================================================================
--- src/plugindata.h	(Revision 3437)
+++ src/plugindata.h	(Arbeitskopie)
@@ -329,7 +329,7 @@
 typedef struct UtilsFuncs
 {
 	gboolean	(*str_equal) (const gchar *a, const gchar *b);
-	gboolean	(*string_replace_all) (GString *haystack, const gchar *needle,
+	guint		(*string_replace_all) (GString *haystack, const gchar *needle,
 				 const gchar *replacement);
 	GSList*		(*get_file_list) (const gchar *path, guint *length, GError **error);
 	gint		(*write_file) (const gchar *filename, const gchar *text);
Index: src/queue.c
===================================================================
--- src/queue.c	(Revision 0)
+++ src/queue.c	(Revision 0)
@@ -0,0 +1,192 @@
+/*
+ *      queue.c
+ *      
+ *      Copyright 2008 kugel. aka Thomas Martitz <thomas47@arcor.de>
+ *      
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *      
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *      
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *      MA 02110-1301, USA.
+ *
+ *      This provides a simple single linked list, with some functons to modify
+ *      it. Being a queue, you can append data only to the end of the list,
+ *      end retrieve data from the beginning. Only the first node is directly
+ *      visible, but with the several foreach functions you can iterate through
+ *      the entire list.
+ */
+
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include "queue.h"
+
+/* Allocate memory for a note */
+inline struct queue_node *queue_malloc(void)
+{
+    return malloc(sizeof(struct queue_node));
+}
+
+/* Returns true if q_node is the last node in a queue */
+inline bool queue_is_last_node(struct queue_node *q_node)
+{
+    return (q_node->next == NULL);
+}
+
+/* Returns true if q_node is an empty queue */
+inline bool queue_is_empty(struct queue_node *q_node)
+{
+    return q_node->data == NULL;
+}
+
+/* Appends an data in a new node at the end of the lst */
+void queue_append(struct queue_node *queue_start, intptr_t *data)
+{
+    if (!queue_start)
+        return;
+    if (!queue_start->data)
+    {
+        queue_start->data = data;
+        return;
+    }
+    struct queue_node *temp = queue_malloc();
+    temp->data = data;
+    temp->next = NULL;
+    struct queue_node *next = queue_start;
+    while(!queue_is_last_node(next))
+        next = next->next;
+    next->next = temp;
+}
+
+/* Removes and frees the first node in queue_start, and writes the data of the
+ * removed node into data.
+ * Returns a pointer to the new first item */
+struct queue_node *queue_delete(struct queue_node *queue_start, intptr_t **data)
+{
+    struct queue_node *ret;
+    if(data)
+        *data = queue_start->data;
+    if (queue_is_last_node(queue_start))
+        ret = NULL;
+    else
+        ret = queue_start->next;
+    free(queue_start);
+    return ret;
+}
+
+/* Removes and frees the entire queue staring at queue_start */
+void queue_destroy(struct queue_node *queue_start)
+{
+    if(!queue_start)
+        return;
+    while(!queue_is_last_node(queue_start))
+        queue_start = queue_delete(queue_start, NULL);
+    free(queue_start);
+/*
+    queue_start = NULL;
+*/
+}
+
+/* Iterates through param, and calls func with the node queue_start and each node in param */
+static void queue_foreach_data_2(struct queue_node *queue_start, void* func(), struct queue_node *param)
+{
+    if (!(queue_start && param))
+        return;
+
+    struct queue_node *temp = param;
+    while(1)
+    {
+        (*func)(queue_start, (temp->data));
+        if (!queue_is_last_node(temp))
+            temp = temp->next;
+        else
+            break;
+    }
+}
+
+/* Copies the data of each node in q1, then the data of each node in q2 to a newly
+ * created queue, using queue_append. Frees q1 and q2.
+ * Returns a ponter to the created queue. */
+struct queue_node *queue_concat_copy(struct queue_node *q1, struct queue_node *q2)
+{
+    /* ->1->2 + ->4->5->6 = 4->5->6->1->2 */
+    struct queue_node *ret = NULL;
+    queue_init(ret);
+    queue_foreach_data_2(ret, (void*)queue_append, q1);
+    queue_foreach_data_2(ret, (void*)queue_append, q2);
+    queue_destroy(q1);
+    queue_destroy(q2);
+    return ret;
+}
+
+#ifdef QUEUE_USE_ALL_FUNCS
+/* Iterates through the list, calling func with each node and param (if given) */
+void queue_foreach(struct queue_node *queue_start, void* func(), void *param)
+{
+    struct queue_node *temp = queue_start;
+    while(1)
+    {
+        if(param)
+            (*func)(temp, param);
+        else
+            (*func)(temp);
+        if (!queue_is_last_node(temp))
+            temp = temp->next;
+        else
+            break;
+    }
+}
+
+/* Iterates through the list, calling func with the data of each node, and param (if given) */
+void queue_foreach_data(struct queue_node *queue_start, void* func(), void *param)
+{
+    struct queue_node *temp = queue_start;
+    while(1)
+    {
+        if(param)
+            (*func)(temp->data, param);
+        else
+            (*func)(temp->data);
+        if (!queue_is_last_node(temp))
+            temp = temp->next;
+        else
+            break;
+    }
+}
+
+/* Appends donator to dest, directly, without copying data */
+struct queue_node *queue_concat(struct queue_node *dest, struct queue_node *donator)
+{
+    struct queue_node *temp = dest;
+
+    while(!queue_is_last_node(temp))
+        temp = temp->next;
+    temp->next = donator;
+    return dest;
+}
+static int printf_wrapper(void* data, const char* format)
+{
+    return printf(format, (int)data);
+}
+
+/* Prints the data of each node according to the given format string. Format
+ * must not contain more than 1 format specfier */
+void queue_print(struct queue_node *queue_start, char *format)
+{
+    if (queue_is_empty(queue_start))
+        return;
+    printf("Queue: ");
+    queue_foreach_data(queue_start, (void*)printf_wrapper, format);
+    printf(" #Stop. \n");
+}
+#endif
Index: src/editor.c
===================================================================
--- src/editor.c	(Revision 3437)
+++ src/editor.c	(Arbeitskopie)
@@ -59,12 +59,16 @@
 #include "keybindings.h"
 #include "project.h"
 #include "projectprivate.h"
+#include "queue.h"
 
 
 /* Note: Avoid using SSM in files not related to scintilla, use sciwrappers.h instead. */
 #define SSM(s, m, w, l) scintilla_send_message(s, m, w, l)
 
 
+static struct queue_node *snip_list;
+static gint snip_entry_pos;
+
 /* holds word under the mouse or keyboard cursor */
 static gchar current_word[GEANY_MAX_WORD_LENGTH];
 
@@ -100,6 +104,7 @@
 void editor_snippets_free()
 {
 	g_hash_table_destroy(editor_prefs.snippets);
+	queue_destroy(snip_list);
 }
 
 
@@ -114,6 +119,9 @@
 	GKeyFile *userconfig = g_key_file_new();
 	GHashTable *tmp;
 
+	snip_list = NULL;
+	queue_init(snip_list);
+
 	sysconfigfile = g_strconcat(app->datadir, G_DIR_SEPARATOR_S, "snippets.conf", NULL);
 	userconfigfile = g_strconcat(app->configdir, G_DIR_SEPARATOR_S, "snippets.conf", NULL);
 
@@ -1858,24 +1866,47 @@
 	/* fixup indentation (very useful for Tabs & Spaces indent type) */
 	line_end = sci_get_line_from_position(sci, insert_pos + buf->len);
 	fix_line_indents(editor, line_start, line_end);
+	snip_entry_pos = sci_get_current_position(sci);
 
 	g_string_free(buf, TRUE);
 }
 
 
+/* Move the cursor to the next specified cursor position in an inserted snippet. */
+void snippet_goto_next_cursor(ScintillaObject *sci, gint current_pos)
+{
+	if (snip_list)
+	{
+		gintptr offset;
+		/* cast to void to shut the compiler up */
+		snip_list = queue_delete(snip_list, ((void*)&offset));
+		printf("sizeof (void*): %d\n", sizeof(void*));
+		if (current_pos > snip_entry_pos)
+			snip_entry_pos = (gint)offset + current_pos;
+		else
+			snip_entry_pos += (gint)offset;
+		sci_set_current_position(sci, snip_entry_pos, FALSE);
+	}
+}
+
 static gboolean snippets_complete_constructs(GeanyEditor *editor, gint pos, const gchar *word)
 {
+	struct queue_node *temp_list = NULL;
+	ScintillaObject *sci = editor->sci;
+	gint line = sci_get_line_from_position(sci, pos);
 	gchar *str, *whitespace;
 	GString *pattern;
-	gint step, str_len;
+	gint i, str_len, tmp_pos, whitespace_len, nl_count = 0;
+	gintptr cursor_steps, old_cursor = 0;
 	gsize cur_index;
 	gint ft_id = FILETYPE_ID(editor->document->file_type);
 	GHashTable *specials;
-	ScintillaObject *sci = editor->sci;
-
+	const GeanyIndentPrefs *iprefs = editor_get_indent_prefs(editor);
+	read_indent(editor, pos);
+	gint indent_size = strlen(indent);
 	str = g_strdup(word);
 	g_strstrip(str);
-
+	queue_init(temp_list);
 	pattern = g_string_new(snippets_find_completion_by_name(filetypes[ft_id]->name, str));
 	if (pattern == NULL || pattern->len == 0)
 	{
@@ -1906,24 +1937,71 @@
 	snippets_replace_wildcards(editor, pattern);
 
 	/* transform other wildcards */
+	/* convert to %newlines%, else we get endless loops */
+	utils_string_replace_all(pattern, "\n", "%newline%");
+
+	/* if spaces are used, replaces all tabs with %ws%, which is later replaced
+	 * by real whitespace characters
+	 * otherwise replace all %ws% by \t, which will be replaced later by tab
+	 * characters,
+	 * this makes seperating between tab and spaces intentation pretty easy */
+	if (iprefs->type == GEANY_INDENT_TYPE_SPACES)
+		utils_string_replace_all(pattern, "\t", "%ws%");
+	else
+		utils_string_replace_all(pattern, "%ws%", "\t");
+	
+	whitespace = g_strnfill(iprefs->width, ' '); /* use spaces for indentation, will be fixed up */
+	whitespace_len = strlen(whitespace);
+	for (i = 0;;i++)
+	{
+		cursor_steps = utils_strpos(pattern->str, "%cursor%");
+		if (cursor_steps == -1)
+			break;
+		/* replace every %newline% (upto next %cursor%) with EOL,
+		 * and update cursor_steps after */
+		while ((tmp_pos = utils_strpos(pattern->str, "%newline%")) < cursor_steps && tmp_pos != -1)
+		{
+			nl_count++;
+			utils_string_replace_first(pattern, "%newline%", editor_get_eol_char(editor));
+			cursor_steps = utils_strpos(pattern->str, "%cursor%");
+		}
+		/* replace every %ws% (upto next %cursor%) with whitespaces,
+		 * and update cursor_steps after */
+		while ((tmp_pos = utils_strpos(pattern->str, "%ws%")) < cursor_steps && tmp_pos != -1)
+		{
+			utils_string_replace_first(pattern, "%ws%", whitespace);
+			cursor_steps = utils_strpos(pattern->str, "%cursor%");
+		}
+		/* finally replace the next %cursor% */
+		utils_string_replace_first(pattern, "%cursor%", "");
+		/* modify cursor_steps to take indentation count and type into account */
+
+		/* We're saving the relative offset to each cursor position in a simple
+		 * linked list, including intendations between them */
+		if (i > 0)
+		{
+			cursor_steps += (nl_count * indent_size);
+			queue_append(temp_list, (gintptr*)(cursor_steps - old_cursor));
+		}
+		else
+		{
+			nl_count = 0;
+			cur_index = cursor_steps;
+		}
+		old_cursor = cursor_steps;	
+	}
+	/* replace remaining %ws% and %newline% which may occur after the last %cursor% */
+	utils_string_replace_all(pattern, "%ws", whitespace);
 	utils_string_replace_all(pattern, "%newline%", editor_get_eol_char(editor));
-
-	/* use spaces for indentation, will be fixed up */
-	whitespace = g_strnfill(editor_get_indent_prefs(editor)->width, ' ');
-	utils_string_replace_all(pattern, "%ws%", whitespace);
 	g_free(whitespace);
-
-	/* find the %cursor% pos (has to be done after all other operations) */
-	step = utils_strpos(pattern->str, "%cursor%");
-	if (step != -1)
-		utils_string_replace_all(pattern, "%cursor%", "");
-
+	queue_print(temp_list, "->%d");
+	/* We create a new list, where the cursor positions for the most recent
+	 * parsed snipped come first, followed by the remaining positions */
+	if (!queue_is_empty(temp_list))
+		snip_list = queue_concat_copy(temp_list, snip_list);
+	if (cur_index < 0)
+		cur_index = pattern->len;
 	/* finally insert the text and set the cursor */
-	if (step != -1)
-		cur_index = step;
-	else
-		cur_index = pattern->len;
-
 	editor_insert_text_block(editor, pattern->str, pos, cur_index, -1);
 	sci_scroll_caret(sci);
 
Index: src/Makefile.am
===================================================================
--- src/Makefile.am	(Revision 3437)
+++ src/Makefile.am	(Arbeitskopie)
@@ -34,6 +34,7 @@
 	prefs.c prefs.h \
 	printing.c printing.h \
 	project.c project.h \
+	queue.c queue.h \
 	sciwrappers.c sciwrappers.h \
 	search.c search.h \
 	socket.c socket.h \
Index: src/queue.h
===================================================================
--- src/queue.h	(Revision 0)
+++ src/queue.h	(Revision 0)
@@ -0,0 +1,57 @@
+/*
+ *      queue.h
+ *      
+ *      Copyright 2009 kugel. aka Thomas Martitz <thomas47@arcor.de>
+ *      
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *      
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *      
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *      MA 02110-1301, USA.
+ */
+
+#ifndef __QUEUE_H__
+#define __QUEUE_H__
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+/* define this to have all functions available, if not defined only those Geany
+ * needs are avalable */
+#define QUEUE_USE_ALL_FUNCS
+struct queue_node
+{
+    uintptr_t *data;
+    struct queue_node *next;
+};
+
+struct queue_node *queue_malloc(void);
+
+#define queue_init(x)   {                                   \
+                            if(!(x))                        \
+                            x=queue_malloc();               \
+                            x->data = NULL;x->next = NULL;  \
+                        }
+
+bool queue_is_last_node(struct queue_node *q_node);
+bool queue_is_empty(struct queue_node *queue_start);
+void queue_append(struct queue_node *queue_start, intptr_t *data);
+struct queue_node *queue_delete(struct queue_node *queue_start, intptr_t **data);
+struct queue_node *queue_concat_copy(struct queue_node *q1, struct queue_node *q2);
+void queue_destroy(struct queue_node *queue_start);
+#ifdef QUEUE_USE_ALL_FUNCS
+void queue_foreach(struct queue_node *queue_start, void* func(), void *param);
+void queue_foreach_data(struct queue_node *queue_start, void* func(), void *param);
+struct queue_node *queue_concat(struct queue_node *donator, struct queue_node *dest);
+void queue_print(struct queue_node *queue_start, char *format);
+#endif /* QUEUE_USE_ALL_FUNCS */
+#endif /* __QUEUE_H__ */
Index: src/editor.h
===================================================================
--- src/editor.h	(Revision 3437)
+++ src/editor.h	(Arbeitskopie)
@@ -167,6 +167,8 @@
 
 gboolean editor_start_auto_complete(GeanyEditor *editor, gint pos, gboolean force);
 
+void snippet_goto_next_cursor(ScintillaObject *sci, gint current_pos);
+
 gboolean editor_complete_snippet(GeanyEditor *editor, gint pos);
 
 void editor_auto_latex(GeanyEditor *editor, gint pos);
Index: doc/geany.txt
===================================================================
--- doc/geany.txt	(Revision 3437)
+++ doc/geany.txt	(Arbeitskopie)
@@ -806,7 +806,9 @@
                   ('key= value' won't work, use 'key=\\svalue')
 
 %cursor%          Place the cursor at this position after completion has
-                  been done.
+                  been done. You can define multiple %cursor% wildcards
+                  and use the ``Move cursor in snippet`` to jump to the next
+                  defined cursor position in the completed snippet.
 
 %...%             "..." means the name of a key in the "Special" section.
                   If you have defined a key "brace_open" in the "Special"
@@ -826,7 +828,8 @@
 
 Every time you write ``myname`` <TAB> in Geany, it will replace "myname"
 with "Enrico Tr��ger". The key to start auto completion can be changed
-in the preferences dialog, by default it is TAB.
+in the preferences dialog, by default it is TAB. The corresponding keybinding
+is called ``Complete snippet``.
 
 Since Geany 0.15 you can also use most of the available templates wildcards
 listed in `Template wildcards`_. All wildcards which are listed as
@@ -2354,6 +2357,10 @@
                                                           argument. See the section called `Context
                                                           actions`_.
 
+Move cursor in snippet                                    Jumps to the next defined cursor positions in a
+                                                          completed snippets if multiple cursor positions
+                                                          where defined.
+
 **Clipboard**
 
 Cut                             Ctrl-X                    Cut the current selection to the clipboard.
Index: data/snippets.conf
===================================================================
--- data/snippets.conf	(Revision 3437)
+++ data/snippets.conf	(Arbeitskopie)
@@ -3,9 +3,11 @@
 # use \n or %newline% for a new line (it will be replaced by the used EOL char(s) - LF, CR/LF, CR).
 # use \t or %ws% for an indentation step, it will be replaced according to the current document's indent mode.
 # use \s to force whitespace at beginning or end of a value ('key= value' won't work, use 'key=\svalue').
-# use %cursor% to define where the cursor should be placed after completion.
 # use %key% for all keys defined in the [Special] section.
-# you can define a section for each supported filetype to overwrite default settings, the section
+# use %cursor% to define where the cursor should be placed after completion. You can define multiple
+#     %cursor% wildcards and use the "Move cursor in snippet" to jump to the next defined cursor
+#     position in the completed snippet.
+# You can define a section for each supported filetype to overwrite default settings, the section
 # name must match exactly the internal filetype name, run 'geany --ft-names' for a full list.
 #
 # Additionally, you can use most of the template wildcards like {developer} or {date} in the snippets.
@@ -18,13 +20,13 @@
 
 # Default is used for all filetypes and keys can be overwritten by [filetype] sections
 [Default]
-if=if (%cursor%)%brace_open%\n%brace_close%
-else=else%brace_open%%cursor%\n%brace_close%
-for=for (i = 0; i < %cursor%; i++)%brace_open%\n%brace_close%
-while=while (%cursor%)%brace_open%\n%brace_close%
-do=do%brace_open%%cursor%\n%brace_close% while ()
-switch=switch (%cursor%)%brace_open%case : break;\n%ws%default: \n%brace_close%
-try=try%block_cursor%catch ()%block%
+if=if (%cursor%)%block_cursor%
+else=else%block_cursor%
+for=for (i = 0; i < %cursor%; i++)%block_cursor%
+while=while (%cursor%)%block_cursor%
+do=do\n{%n%%cursor%\n} while(%cursor%)\n%cursor%
+switch=switch (%cursor%)%brace_open%case %cursor%: %n%\t%cursor%%n%break;\n%ws%default: %n%\t%cursor%\n%brace_close%%cursor%
+try=try%block%\ncatch (%cursor%)%block_cursor%
 
 # special keys to be used in other snippets, cannot be used "standalone"
 # can be used by %key%, e.g. %brace_open%
@@ -34,15 +36,27 @@
 [Special]
 brace_open=\n{\n\t
 brace_close=}\n
-block=\n{\n\t\n}\n
-block_cursor=\n{\n\t%cursor%\n}\n
+n=\n\t
+block=\n{\n\t%cursor%\n}
+block_cursor=\n{\n\t%cursor%\n}\n%cursor%
+_do=do\n\t%cursor%\nend\n%cursor%
 #wordchars=_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
 
-[C++]
-for=for (int i = 0; i < %cursor%; i++)%brace_open%\n%brace_close%
+[C]
+un=unsigned %cursor%
+vo=volatile %cursor%
 
-[Java]
-for=for (int i = 0; i < %cursor%; i++)%brace_open%\n%brace_close%
+[Ruby]
+do=%_do%
+def=def %cursor%%n%%cursor%\nend\n%cursor%
+class=class %cursor%%n%def initialize%cursor%%n%\t%cursor%%n%end\n%n%%cursor%\nend\n%cursor%
+for=for i in 0..%cursor% %_do%
+while=while (%cursor%) %_do%
+until=until (%cursor%) %_do%
+if=if %cursor% then%n%%cursor%\nend\n%cursor%
+elsif=elsif %cursor%%n%%cursor%
+else=else%n%%cursor%\nend\n%cursor%
+times=times { %cursor% %n%%cursor%\n}\n%cursor%
 
 [PHP]
 for=for ($i = 0; $i < %cursor%; $i++)%brace_open%\n%brace_close%
@@ -61,4 +75,3 @@
 [Ferite]
 iferr=iferr%block_cursor%fix%block%
 monitor=monitor%block_cursor%handle%block%
-

