Index: src/utils.c
===================================================================
--- src/utils.c	(Revision 3437)
+++ src/utils.c	(Arbeitskopie)
@@ -1355,20 +1355,25 @@
 
 
 /**
- * Replaces all occurrences of @c needle in @c haystack with @c replace.
+ * Replaces 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:
  * @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.
+ * @param max_replaces Limits the amount of replaces to be done, 0 is unlimited.
  *
- * @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)
+static guint utils_string_replace__helper(GString *haystack,
+											const gchar *needle,
+											const gchar *replace,
+											const guint max_replaces)
 {
 	const gchar *stack, *match;
 	gssize pos = -1;
+	guint replace_count = 0;
 
 	if (haystack->len == 0)
 		return FALSE;
@@ -1378,7 +1383,7 @@
 	while (1)
 	{
 		match = strstr(stack, needle);
-		if (match == NULL)
+		if (match == NULL || (max_replaces && replace_count++ >= max_replaces))
 			break;
 		else
 		{
@@ -1396,10 +1401,19 @@
 			}
 		}
 	}
-	return (pos != -1);
+	return replace_count;
 }
 
+guint utils_string_replace_all(GString *haystack, const gchar *needle, const gchar *replace)
+{
+	return utils_string_replace__helper(haystack, needle, replace, 0);
+}
 
+guint utils_string_replace_first(GString *haystack, const gchar *needle, const gchar *replace)
+{
+	return utils_string_replace__helper(haystack, needle, replace, 1);
+}
+
 /* Get project or default startup directory (if set), or NULL. */
 const gchar *utils_get_default_dir_utf8(void)
 {
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,
+		GDK_Tab, GDK_CONTROL_MASK, "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,
@@ -418,7 +420,7 @@
 	keybindings_set_item(group, GEANY_KEYS_NOTEBOOK_SWITCHTABRIGHT, cb_func_switch_tabright,
 		GDK_Page_Down, GDK_CONTROL_MASK, "switch_tabright", _("Switch to right document"), NULL);
 	keybindings_set_item(group, GEANY_KEYS_NOTEBOOK_SWITCHTABLASTUSED, cb_func_switch_tablastused,
-		GDK_Tab, GDK_CONTROL_MASK, "switch_tablastused", _("Switch to last used document"), NULL);
+		0, 0, "switch_tablastused", _("Switch to last used document"), NULL);
 	keybindings_set_item(group, GEANY_KEYS_NOTEBOOK_MOVETABLEFT, cb_func_move_tab,
 		GDK_Page_Up, GDK_MOD1_MASK, "move_tableft", _("Move document left"), NULL);
 	keybindings_set_item(group, GEANY_KEYS_NOTEBOOK_MOVETABRIGHT, cb_func_move_tab,
@@ -862,7 +864,6 @@
 	return FALSE;
 }
 
-
 #ifdef HAVE_VTE
 static gboolean on_menu_expose_event(GtkWidget *widget, GdkEventExpose *event,
 		gpointer user_data)
@@ -1678,6 +1679,9 @@
 		case GEANY_KEYS_EDITOR_DUPLICATELINE:
 			duplicate_lines(doc->editor);
 			break;
+		case GEANY_KEYS_EDITOR_SNIPPET_NEXT_CURSOR:
+			editor_snippet_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)
@@ -134,6 +134,7 @@
 	GEANY_KEYS_EDITOR_SCROLLLINEUP,
 	GEANY_KEYS_EDITOR_SCROLLLINEDOWN,
 	GEANY_KEYS_EDITOR_COMPLETESNIPPET,
+	GEANY_KEYS_EDITOR_SNIPPET_NEXT_CURSOR,
 	GEANY_KEYS_EDITOR_SUPPRESSSNIPPETCOMPLETION,
 	GEANY_KEYS_EDITOR_CONTEXTACTION,
 	GEANY_KEYS_EDITOR_AUTOCOMPLETE,
Index: src/editor.c
===================================================================
--- src/editor.c	(Revision 3437)
+++ src/editor.c	(Arbeitskopie)
@@ -61,6 +61,16 @@
 #include "projectprivate.h"
 
 
+#define MAX_CURSOR_IN_SNIPPET 10
+#define MAX_INDENT_COUNT 10
+struct snippet_cursor_info {
+	gint cur_idx;
+	gint current;
+	gint positions[MAX_CURSOR_IN_SNIPPET];
+	gboolean enabled;
+};
+
+static struct snippet_cursor_info snippet_cursor;
 /* 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)
 
@@ -1835,6 +1845,8 @@
 		g_free(whitespace);
 	}
 
+	/* the next 2 replacements have no effect to snippets, since they replace
+	 *  seperately (because those must be done before %cursor% replacement */
 	/* transform line endings */
 	utils_string_replace_all(buf, "\n", editor_get_eol_char(editor));
 
@@ -1862,19 +1874,33 @@
 	g_string_free(buf, TRUE);
 }
 
+/* Move the cursor to the next specified cursor position in a inserted snipped */
+void editor_snippet_next_cursor(ScintillaObject *sci, gint current_pos)
+{
+	snippet_cursor.cur_idx++;
+	if (!snippet_cursor.enabled)
+		return;
+	if (snippet_cursor.cur_idx > 0)
+		snippet_cursor.current = snippet_cursor.positions[snippet_cursor.cur_idx]
+				- snippet_cursor.positions[snippet_cursor.cur_idx-1];
+	if (snippet_cursor.current > 0)
+		sci_set_current_position(sci, snippet_cursor.current+current_pos, FALSE);
+	else
+		snippet_cursor.enabled = FALSE;
+}
 
 static gboolean snippets_complete_constructs(GeanyEditor *editor, gint pos, const gchar *word)
 {
 	gchar *str, *whitespace;
 	GString *pattern;
-	gint step, str_len;
+	gint str_len, i, indent_count = 0;;
 	gsize cur_index;
 	gint ft_id = FILETYPE_ID(editor->document->file_type);
 	GHashTable *specials;
 	ScintillaObject *sci = editor->sci;
-
 	str = g_strdup(word);
 	g_strstrip(str);
+	snippet_cursor.enabled = FALSE;
 
 	pattern = g_string_new(snippets_find_completion_by_name(filetypes[ft_id]->name, str));
 	if (pattern == NULL || pattern->len == 0)
@@ -1906,23 +1932,70 @@
 	snippets_replace_wildcards(editor, pattern);
 
 	/* transform other wildcards */
+	/* transform line endings */
 	utils_string_replace_all(pattern, "%newline%", editor_get_eol_char(editor));
+	utils_string_replace_all(pattern, "\n", 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);
+	/* find the %cursor% positions
+	 * (has to be done after all other operations) */
+	/* This looks dirty, but is needed, since we want to have multiple
+	 * %cursor% insertions working for any indent type. Spaces and Tabs need
+	 * different steps for the cursor.
+	 * So, before replacing %cursor% one by one, we replace every %ws%,\t up
+	 * to the next cursor and count them. Later we modify the step size, depending
+	 * on the configured indent type and counted indents */
+	for (i = 0; i < MAX_CURSOR_IN_SNIPPET; i++)
+	{
+		snippet_cursor.positions[i] = 0;
+		gint cursor_steps;
+		cursor_steps = utils_strpos(pattern->str, "%cursor%");
+		if (temp != -1)
+		{
+			/* count indentions upto the next %cursor% and fix step number
+			 * since it was counted with %ws% and \t in the pattern, and not the
+			 * actual whitespace number which is going to be */
+			while (utils_strpos(pattern->str, "%ws%") < cursor_steps
+					&& utils_strpos(pattern->str, "%ws%") != -1)
+			{
+				indent_count++;
+				utils_string_replace_first(pattern, "%ws%", whitespace);
+				temp -= strlen("%ws%") - strlen(whitespace);
+			}
+			while (utils_strpos(pattern->str, "\t") < cursor_steps
+					&& utils_strpos(pattern->str, "\t") != -1)
+			{
+				indent_count++;
+				utils_string_replace_first(pattern, "\t", whitespace);
+				temp -= strlen("\t") - strlen(whitespace);
+			}
+			/* finally replace the next %cursor% */
+			utils_string_replace_first(pattern, "%cursor%", "");
+			/* modify temp to take indent count and type into account */
+			if (editor_get_indent_prefs(editor)->type & GEANY_INDENT_TYPE_TABS)
+				cursor_steps -= indent_count * (editor_get_indent_prefs(editor)->width -1);
+		}
+		else /* no more found */
+			break;
+		snippet_cursor.positions[i] = cursor_steps;
+	}
+	/* replace remaining %ws%,\t which may occur after the last %cursor% */
+	utils_string_replace_all(pattern, "\t", whitespace);
+	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%", "");
-
-	/* finally insert the text and set the cursor */
-	if (step != -1)
-		cur_index = step;
+	snippet_cursor.current = snippet_cursor.positions[0];
+	snippet_cursor.cur_idx = 0;
+	if (snippet_cursor.current < 1)
+	{
+		cur_index = pattern->len;
+		snippet_cursor.enabled = FALSE;
+	}
 	else
-		cur_index = pattern->len;
+	{
+		cur_index = snippet_cursor.current;
+		snippet_cursor.enabled = TRUE;
+	}
 
 	editor_insert_text_block(editor, pattern->str, pos, cur_index, -1);
 	sci_scroll_caret(sci);
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 editor_snippet_next_cursor(ScintillaObject *sci, gint current_pos);
+
 gboolean editor_complete_snippet(GeanyEditor *editor, gint pos);
 
 void editor_auto_latex(GeanyEditor *editor, gint pos);
