Index: gtksourceview/gtksourceview.c
===================================================================
RCS file: /cvs/gnome/gtksourceview/gtksourceview/gtksourceview.c,v
retrieving revision 1.48
diff -u -p -u -p -r1.48 gtksourceview.c
--- gtksourceview/gtksourceview.c	23 Jun 2006 08:21:56 -0000	1.48
+++ gtksourceview/gtksourceview.c	29 Jul 2006 14:56:37 -0000
@@ -1941,6 +1941,167 @@ compute_indentation (GtkSourceView *view
 	return gtk_text_iter_get_slice (&start, &end);
 }
 
+static void
+indent_lines (GtkSourceView *view, GtkTextIter *start, GtkTextIter *end)
+{
+	GtkTextBuffer *buf;
+	gint start_line, end_line;
+	gint i;
+	gchar *tab_buffer = NULL;
+
+	buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
+
+	start_line = gtk_text_iter_get_line (start);
+	end_line = gtk_text_iter_get_line (end);
+
+	if ((gtk_text_iter_get_visible_line_offset (end) == 0) &&
+	    (end_line > start_line))
+	{
+		end_line--;
+	}
+
+	if (gtk_source_view_get_insert_spaces_instead_of_tabs (view))
+	{
+		gint tabs_size;
+
+		tabs_size = view->priv->tabs_width;
+		tab_buffer = g_strnfill (tabs_size, ' ');
+	}
+	else
+	{
+		tab_buffer = g_strdup ("\t");
+	}
+
+	gtk_text_buffer_begin_user_action (buf);
+
+	for (i = start_line; i <= end_line; i++)
+	{
+		GtkTextIter iter;
+
+		gtk_text_buffer_get_iter_at_line (buf, &iter, i);
+
+		/* don't add indentation on empty lines */
+		if (gtk_text_iter_ends_line (&iter))
+			continue;
+
+		gtk_text_buffer_insert (buf, &iter, tab_buffer, -1);
+	}
+
+	gtk_text_buffer_end_user_action (buf);
+
+	g_free (tab_buffer);
+
+	gtk_text_view_scroll_mark_onscreen (GTK_TEXT_VIEW (view),
+					    gtk_text_buffer_get_insert (buf));
+}
+
+static void
+unindent_lines (GtkSourceView *view, GtkTextIter *start, GtkTextIter *end)
+{
+	GtkTextBuffer *buf;
+	gint start_line, end_line;
+	gint i;
+
+	buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
+
+	start_line = gtk_text_iter_get_line (start);
+	end_line = gtk_text_iter_get_line (end);
+
+	if ((gtk_text_iter_get_visible_line_offset (end) == 0) &&
+	    (end_line > start_line))
+	{
+		end_line--;
+	}
+
+	gtk_text_buffer_begin_user_action (buf);
+
+	for (i = start_line; i <= end_line; i++)
+	{
+		GtkTextIter iter, iter2;
+
+		gtk_text_buffer_get_iter_at_line (buf, &iter, i);
+
+		if (gtk_text_iter_get_char (&iter) == '\t')
+		{
+			iter2 = iter;
+			gtk_text_iter_forward_char (&iter2);
+			gtk_text_buffer_delete (buf, &iter, &iter2);
+		}
+		else if (gtk_text_iter_get_char (&iter) == ' ')
+		{
+			gint spaces = 0;
+
+			iter2 = iter;
+
+			while (!gtk_text_iter_ends_line (&iter2))
+			{
+				if (gtk_text_iter_get_char (&iter2) == ' ')
+					spaces++;
+				else
+					break;
+
+				gtk_text_iter_forward_char (&iter2);
+			}
+
+			if (spaces > 0)
+			{
+				gint tabs = 0;
+				gint tabs_size = view->priv->tabs_width;
+
+				tabs = spaces / tabs_size;
+				spaces = spaces - (tabs * tabs_size);
+
+				if (spaces == 0)
+					spaces = tabs_size;
+
+				iter2 = iter;
+
+				gtk_text_iter_forward_chars (&iter2, spaces);
+				gtk_text_buffer_delete (buf, &iter, &iter2);
+			}
+		}
+	}
+
+	gtk_text_buffer_end_user_action (buf);
+
+	gtk_text_view_scroll_mark_onscreen (GTK_TEXT_VIEW (view),
+					    gtk_text_buffer_get_insert (buf));
+}
+
+static void
+insert_tab_or_spaces (GtkSourceView *view, GtkTextIter *start, GtkTextIter *end)
+{
+	GtkTextBuffer *buf;
+	gchar *tab_buf;
+
+	if (view->priv->insert_spaces)
+	{
+		gint tabs_size;
+		gint pos;
+		gint num_of_equivalent_spaces;
+
+		tabs_size = view->priv->tabs_width; 
+
+		pos = gtk_text_iter_get_line_offset (start);
+		num_of_equivalent_spaces = tabs_size - (pos % tabs_size); 
+		tab_buf = g_strnfill (num_of_equivalent_spaces, ' ');
+	}
+	else
+	{
+		tab_buf = g_strdup ("\t");
+	}
+
+	buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
+
+	gtk_text_buffer_begin_user_action (buf);
+	gtk_text_buffer_delete (buf, start, end);
+	gtk_text_buffer_insert (buf, start, tab_buf, -1);
+	gtk_text_buffer_end_user_action (buf);
+
+	g_free (tab_buf);
+}
+
 static gboolean
 gtk_source_view_key_press_event (GtkWidget *widget, GdkEventKey *event)
 {
@@ -1996,30 +2157,37 @@ gtk_source_view_key_press_event (GtkWidg
 		}
 	}
 
-	if ((key == GDK_Tab) && view->priv->insert_spaces)
+	/* if tab or shift+tab:
+	 * with shift+tab key is GDK_ISO_Left_Tab (depends on X?)
+	 */
+	if ((key == GDK_Tab || key == GDK_KP_Tab || key == GDK_ISO_Left_Tab) &&
+	    (event->state == 0 || event->state == GDK_SHIFT_MASK))
 	{
-		gint cur_pos;
-		gint num_of_equivalent_spaces;
-		gint tabs_size;
-		gchar *spaces;
+		gboolean has_selection;
+		GtkTextIter s, e;
 
-		tabs_size = view->priv->tabs_width; 
-		
-		cur_pos = gtk_text_iter_get_line_offset (&cur);
+		has_selection = gtk_text_buffer_get_selection_bounds (buf, &s, &e);
 
-		num_of_equivalent_spaces = tabs_size - (cur_pos % tabs_size); 
+		/* shift+tab: always unindent */
+		if (event->state & GDK_SHIFT_MASK)
+		{
+			unindent_lines (view, &s, &e);
+			return TRUE;
+		}
 
-		spaces = g_strnfill (num_of_equivalent_spaces, ' ');
-		
-		gtk_text_buffer_begin_user_action (buf);
-		gtk_text_buffer_insert (buf,  &cur, spaces, num_of_equivalent_spaces);
-		gtk_text_buffer_end_user_action (buf);
-
-		gtk_text_view_scroll_mark_onscreen (GTK_TEXT_VIEW (widget),
-						    gtk_text_buffer_get_insert (buf));
-		
-		g_free (spaces);
+		/* tab: if we have a selection which spans one whole line
+		 * or more, we mass indent, if the selection spans less then
+		 * the full line just replace the text with \t
+		 */
+		if (has_selection &&
+		    ((gtk_text_iter_starts_line (&s) && gtk_text_iter_starts_line (&e)) ||
+		     (gtk_text_iter_get_line (&s) != gtk_text_iter_get_line (&e))))
+		{
+			indent_lines (view, &s, &e);
+			return TRUE;
+		}
 
+		insert_tab_or_spaces (view, &s, &e);
 		return TRUE;
 	}
 
