[Xfce4-commits] <mousepad:master> * mousepad/mousepad-view.c: Cleanup the line number code. This version is a bit faster and removed a bunch of code. * mousepad/mousepad-view.c: Cleanup the indentation code. You can also increase the (vertical) selected text with Shift + Space and decrease with Shift + Backspace. I though this might be useful for developers. * mousepad/mousepad-view.c: Vertical selection using the mouse and Ctrl + Shift. Keyboard vertical selection is not possible because that adds too much code for (hardly) nothing. You can (un-)indent the block with Tab, Shift + Tab and the 2 new space (in/de)crease commands above. * mousepad/mousepad-view.c: Draw a vertical line to separate the line numbers from the text, this looks beter with light themes (IMHO). * mousepad/mousepad-window.c: Add 'paste column' option to paste the clipboard text in a column under the cursor. * mousepad/mousepad-window.c: Set stock menu names to NULL so G tk fills the default (translated) name, makes your binary smaller and translators happy. * mousepad/mousepad-{search-bar, document}.c: Fix segfault with empty string in the search bar and highlighting enabled. * TODO: Add items.

Nick Schermer noreply at xfce.org
Sat May 5 21:30:35 CEST 2012


Updating branch refs/heads/master
         to 924ec4cfdac8ef8ad541d643f9789836bb3bd86c (commit)
       from 4abd78f00bd8306fd7a8028875cb9050b62df53e (commit)

commit 924ec4cfdac8ef8ad541d643f9789836bb3bd86c
Author: Nick Schermer <nick at xfce.org>
Date:   Thu May 17 20:00:53 2007 +0000

    	* mousepad/mousepad-view.c: Cleanup the line number code.
    	  This version is a bit faster and removed a bunch of code.
    	* mousepad/mousepad-view.c: Cleanup the indentation code. You
    	  can also increase the (vertical) selected text with Shift +
    	  Space and decrease with Shift + Backspace. I though this
    	  might be useful for developers.
    	* mousepad/mousepad-view.c: Vertical selection using the mouse
    	  and Ctrl + Shift. Keyboard vertical selection is not possible
    	  because that adds too much code for (hardly) nothing. You can
    	  (un-)indent the block with Tab, Shift + Tab and the 2 new
    	  space (in/de)crease commands above.
    	* mousepad/mousepad-view.c: Draw a vertical line to separate
    	  the line numbers from the text, this looks beter with light
    	  themes (IMHO).
    	* mousepad/mousepad-window.c: Add 'paste column' option to
    	  paste the clipboard text in a column under the cursor.
    	* mousepad/mousepad-window.c: Set stock menu names to NULL so
    	  Gtk fills the default (translated) name,  makes your binary
    	  smaller and translators happy.
    	* mousepad/mousepad-{search-bar,document}.c: Fix segfault with
    	  empty string in the search bar and highlighting enabled.
    	* TODO: Add items.
    
    (Old svn revision: 25720)

 ChangeLog                       |   25 +
 TODO                            |    9 +-
 mousepad/mousepad-document.c    |   41 ++-
 mousepad/mousepad-document.h    |    2 +
 mousepad/mousepad-private.h     |    4 +-
 mousepad/mousepad-search-bar.c  |   11 +-
 mousepad/mousepad-view.c        | 1156 +++++++++++++++++++++++++++------------
 mousepad/mousepad-view.h        |   13 +
 mousepad/mousepad-window-ui.xml |    1 +
 mousepad/mousepad-window.c      |   60 ++-
 10 files changed, 945 insertions(+), 377 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index c5c1855..326ead6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,28 @@
+2007-05-xx	Nick Schermer <nick at xfce.org>
+	* mousepad/mousepad-view.c: Cleanup the line number code.
+	  This version is a bit faster and removed a bunch of code.
+	* mousepad/mousepad-view.c: Cleanup the indentation code. You
+	  can also increase the (vertical) selected text with Shift +
+	  Space and decrease with Shift + Backspace. I though this
+	  might be useful for developers.
+	* mousepad/mousepad-view.c: Vertical selection using the mouse
+	  and Ctrl + Shift. Keyboard vertical selection is not possible
+	  because that adds too much code for (hardly) nothing. You can
+	  (un-)indent the block with Tab, Shift + Tab and the 2 new
+	  space (in/de)crease commands above.
+	* mousepad/mousepad-view.c: Draw a vertical line to separate
+	  the line numbers from the text, this looks beter with light
+	  themes (IMHO).
+	* mousepad/mousepad-window.c: Add 'paste column' option to
+	  paste the clipboard text in a column under the cursor.
+	* mousepad/mousepad-window.c: Set stock menu names to NULL so
+	  Gtk fills the default (translated) name,  makes your binary
+	  smaller and translators happy.
+	* mousepad/mousepad-{search-bar,document}.c: Fix segfault with
+	  empty string in the search bar and highlighting enabled.
+	* TODO: Add items.
+
+
 2007-05-12	Nick Schermer <nick at xfce.org>
 	* mousepad/mousepad-window.c: Add extra tests if the file
 	  really exists, because Gtk file dialogs hang if the
diff --git a/TODO b/TODO
index 8f30a10..b475bf5 100644
--- a/TODO
+++ b/TODO
@@ -19,6 +19,9 @@ Interface
 - You can't use the Ctrl - v/c/x buttons in the type-ahead bar
   because they are 'registered' by the textview/ui.
 - When hitting the enter button in the jump dialog, we should jump.
+- Replace dialog. This dialog should also provide a find button so
+  there is an alternative for the type-ahead feature.
+- Maybe a match whole word option.
 
 
 Code
@@ -30,9 +33,7 @@ Code
 
 Text View
 =========
-- Replace dialog. This dialog should also provide a find button so
-  there is an alternative for the type-ahead feature.
-- Maybe a match whole word option.
+- Tabs as spaces option.
 
 
 Undo Manager
@@ -42,6 +43,8 @@ Undo Manager
 - We erase the GString in the undo manager, but this buffer will be
   (very) large when a large bunch of text is deleted. Maybe not a big
   problem, but we could shrink it after erasing.
+- Let the undo manager handle multiple steps (mostly for vertical
+  selection cut/delete).
 
 
 Saving and loading
diff --git a/mousepad/mousepad-document.c b/mousepad/mousepad-document.c
index de38641..6c92296 100644
--- a/mousepad/mousepad-document.c
+++ b/mousepad/mousepad-document.c
@@ -907,7 +907,7 @@ mousepad_document_highlight_all (MousepadDocument    *document,
   gtk_text_buffer_remove_tag (document->buffer, document->tag, &doc_start, &doc_end);
 
   /* highlight the new string */
-  if (G_LIKELY (string != NULL))
+  if (G_LIKELY (string != NULL && *string != '\0'))
     {
       /* set the iter to the beginning of the document */
       iter = doc_start;
@@ -937,12 +937,16 @@ void
 mousepad_document_cut_selection (MousepadDocument *document)
 {
   GtkClipboard *clipboard;
+  MousepadView *view = MOUSEPAD_VIEW (document->textview);
 
   /* get the clipboard */
   clipboard = gtk_widget_get_clipboard (GTK_WIDGET (document->textview), GDK_SELECTION_CLIPBOARD);
 
   /* cut the text */
-  gtk_text_buffer_cut_clipboard (document->buffer, clipboard, gtk_text_view_get_editable (document->textview));
+  if (mousepad_view_get_vertical_selection (view))
+    mousepad_view_cut_clipboard (view, clipboard);
+  else
+    gtk_text_buffer_cut_clipboard (document->buffer, clipboard, gtk_text_view_get_editable (document->textview));
 
   /* make sure the cursor is in the visible area */
   mousepad_document_scroll_to_visible_area (document);
@@ -950,18 +954,20 @@ mousepad_document_cut_selection (MousepadDocument *document)
 
 
 
-
-
 void
 mousepad_document_copy_selection (MousepadDocument *document)
 {
   GtkClipboard *clipboard;
+  MousepadView *view = MOUSEPAD_VIEW (document->textview);
 
   /* get the clipboard */
   clipboard = gtk_widget_get_clipboard (GTK_WIDGET (document->textview), GDK_SELECTION_CLIPBOARD);
 
   /* copy the selected text */
-  gtk_text_buffer_copy_clipboard (document->buffer, clipboard);
+  if (mousepad_view_get_vertical_selection (view))
+    mousepad_view_copy_clipboard (view, clipboard);
+  else
+    gtk_text_buffer_copy_clipboard (document->buffer, clipboard);
 }
 
 
@@ -984,10 +990,33 @@ mousepad_document_paste_clipboard (MousepadDocument *document)
 
 
 void
+mousepad_document_paste_column_clipboard (MousepadDocument *document)
+{
+  GtkClipboard *clipboard;
+  MousepadView *view = MOUSEPAD_VIEW (document->textview);
+
+  /* get the clipboard */
+  clipboard = gtk_widget_get_clipboard (GTK_WIDGET (document->textview), GDK_SELECTION_CLIPBOARD);
+
+  /* past the clipboard text in a column */
+  mousepad_view_paste_column_clipboard (view, clipboard);
+
+  /* make sure the cursor is in the visible area */
+  mousepad_document_scroll_to_visible_area (document);
+}
+
+
+
+void
 mousepad_document_delete_selection (MousepadDocument *document)
 {
+  MousepadView *view = MOUSEPAD_VIEW (document->textview);
+
   /* delete the selected text */
-  gtk_text_buffer_delete_selection (document->buffer, TRUE, gtk_text_view_get_editable (document->textview));
+  if (mousepad_view_get_vertical_selection (view))
+    mousepad_view_delete_selection (view);
+  else
+    gtk_text_buffer_delete_selection (document->buffer, TRUE, gtk_text_view_get_editable (document->textview));
 
   /* make sure the cursor is in the visible area */
   mousepad_document_scroll_to_visible_area (document);
diff --git a/mousepad/mousepad-document.h b/mousepad/mousepad-document.h
index 28e01fa..019471e 100644
--- a/mousepad/mousepad-document.h
+++ b/mousepad/mousepad-document.h
@@ -85,6 +85,8 @@ void            mousepad_document_copy_selection           (MousepadDocument
 
 void            mousepad_document_paste_clipboard          (MousepadDocument    *document);
 
+void            mousepad_document_paste_column_clipboard   (MousepadDocument    *document);
+
 void            mousepad_document_delete_selection         (MousepadDocument    *document);
 
 void            mousepad_document_select_all               (MousepadDocument    *document);
diff --git a/mousepad/mousepad-private.h b/mousepad/mousepad-private.h
index ce7fafe..3bb9a96 100644
--- a/mousepad/mousepad-private.h
+++ b/mousepad/mousepad-private.h
@@ -33,10 +33,10 @@ G_BEGIN_DECLS
   GTimer *__FUNCTION__timer = g_timer_new();
 
 #define TIMER_SPLIT \
-  g_print ("%s (%d): %f\n", __FUNCTION__, __LINE__, g_timer_elapsed (__FUNCTION__timer, NULL));
+  g_print ("%s (%s:%d): %.2f ms\n", __FUNCTION__, __FILE__, __LINE__, g_timer_elapsed (__FUNCTION__timer, NULL) * 1000);
 
 #define TIMER_STOP \
-  g_print ("%s (%d): %f\n", __FUNCTION__, __LINE__, g_timer_elapsed (__FUNCTION__timer, NULL)); \
+  TIMER_SPLIT \
   g_timer_destroy (__FUNCTION__timer);
 
 #define PRINT_LINE g_print ("%d\n", __LINE__);
diff --git a/mousepad/mousepad-search-bar.c b/mousepad/mousepad-search-bar.c
index f9c3e05..2804684 100644
--- a/mousepad/mousepad-search-bar.c
+++ b/mousepad/mousepad-search-bar.c
@@ -518,14 +518,11 @@ mousepad_search_bar_highlight_timeout (gpointer user_data)
   if (!search_bar->match_case)
     flags |= MOUSEPAD_SEARCH_CASE_INSENSITIVE;
 
-  /* get the string if highlighting is enabled */
+  /* set the entry string or a 0 string to remove the highlight */
   if (search_bar->highlight_all)
-    {
-      /* get the entry string */
-      string = gtk_entry_get_text (GTK_ENTRY (search_bar->entry));
-      if (string == NULL || *string == '\0')
-        string = NULL;
-    }
+    string = gtk_entry_get_text (GTK_ENTRY (search_bar->entry));
+  else
+    string = "\0";
 
   /* send the signal and wait for the result */
   g_signal_emit (G_OBJECT (search_bar), search_bar_signals[HIGHLIGHT_ALL], 0,
diff --git a/mousepad/mousepad-view.c b/mousepad/mousepad-view.c
index 3ca086e..a00d8e6 100644
--- a/mousepad/mousepad-view.c
+++ b/mousepad/mousepad-view.c
@@ -32,24 +32,47 @@
 
 
 
-static void      mousepad_view_class_init           (MousepadViewClass  *klass);
-static void      mousepad_view_finalize             (GObject            *object);
-static gboolean  mousepad_view_key_press_event      (GtkWidget          *widget,
-                                                     GdkEventKey        *event);
-static void      mousepad_view_indent_lines         (MousepadView       *view,
-                                                     GtkTextIter        *start,
-                                                     GtkTextIter        *end,
-                                                     gboolean            indent);
-static gchar    *mousepad_view_compute_indentation  (MousepadView       *view,
-                                                     GtkTextIter        *iter);
-static gint      mousepad_view_expose               (GtkWidget          *widget,
-                                                     GdkEventExpose     *event);
-static void      mousepad_view_get_lines            (GtkTextView        *text_view,
-                                                     gint                first_y,
-                                                     gint                last_y,
-                                                     GArray            **pixels,
-                                                     GArray            **numbers,
-                                                     gint               *countp);
+/* we can assume there is a buffer for this view */
+#define mousepad_view_get_buffer(view) (GTK_TEXT_VIEW(view)->buffer)
+
+
+
+/* class functions */
+static void      mousepad_view_class_init                    (MousepadViewClass  *klass);
+static void      mousepad_view_init                          (MousepadView       *view);
+static void      mousepad_view_finalize                      (GObject            *object);
+static void      mousepad_view_style_set                     (GtkWidget          *widget,
+                                                              GtkStyle           *previous_style);
+static gint      mousepad_view_expose                        (GtkWidget          *widget,
+                                                              GdkEventExpose     *event);
+static gboolean  mousepad_view_key_press_event               (GtkWidget          *widget,
+                                                              GdkEventKey        *event);
+static gboolean  mousepad_view_button_press_event            (GtkWidget          *widget,
+                                                              GdkEventButton     *event);
+static gboolean  mousepad_view_button_release_event          (GtkWidget          *widget,
+                                                              GdkEventButton     *event);
+
+/* vertical selection functions */
+static GArray   *mousepad_view_vertical_selection_array      (MousepadView       *view,
+                                                              gboolean            forced);
+static gboolean  mousepad_view_vertical_selection_timeout    (gpointer            user_data);
+static void      mousepad_view_vertical_selection_reset      (MousepadView       *view);
+
+/* indentation functions */
+static void      mousepad_view_indent_line                   (GtkTextBuffer      *buffer,
+                                                              GtkTextIter        *iter,
+                                                              gboolean            increase,
+                                                              gboolean            tab);
+static gboolean  mousepad_view_indent_lines                  (MousepadView       *view,
+                                                              gboolean            indent,
+                                                              gboolean            tab);
+static gchar    *mousepad_view_compute_indentation           (GtkTextBuffer      *buffer,
+                                                              const GtkTextIter  *iter);
+
+/* vertical selection edit handler */
+static void      mousepad_view_handle_clipboard                 (MousepadView       *view,
+                                                              GtkClipboard       *clipboard,
+                                                              gboolean            remove);
 
 
 
@@ -62,6 +85,19 @@ struct _MousepadView
 {
   GtkTextView  __parent__;
 
+  /* coordinates for the vertical selection */
+  gint         vertical_start_x, vertical_start_y;
+  gint         vertical_end_x, vertical_end_y;
+
+  /* pointer array with all the vertical selection marks */
+  GPtrArray   *marks;
+
+  /* vertical selection timeout id */
+  guint        vertical_selection_id;
+
+  /* the selection tag */
+  GtkTextTag  *tag;
+
   /* settings */
   guint        auto_indent : 1;
   guint        line_numbers : 1;
@@ -85,7 +121,7 @@ mousepad_view_get_type (void)
                                             sizeof (MousepadViewClass),
                                             (GClassInitFunc) mousepad_view_class_init,
                                             sizeof (MousepadView),
-                                            NULL,
+                                            (GInstanceInitFunc) mousepad_view_init,
                                             0);
     }
 
@@ -106,8 +142,29 @@ mousepad_view_class_init (MousepadViewClass *klass)
   gobject_class->finalize = mousepad_view_finalize;
 
   widget_class = GTK_WIDGET_CLASS (klass);
-  widget_class->key_press_event = mousepad_view_key_press_event;
-  widget_class->expose_event = mousepad_view_expose;
+  widget_class->expose_event         = mousepad_view_expose;
+  widget_class->style_set            = mousepad_view_style_set;
+  widget_class->key_press_event      = mousepad_view_key_press_event;
+  widget_class->button_press_event   = mousepad_view_button_press_event;
+  widget_class->button_release_event = mousepad_view_button_release_event;
+}
+
+
+
+static void
+mousepad_view_init (MousepadView *view)
+{
+  /* initialize */
+  view->auto_indent = view->line_numbers = FALSE;
+
+  /* initialize */
+  view->marks = NULL;
+  view->tag = NULL;
+
+  /* initialize */
+  view->vertical_start_x = view->vertical_start_y =
+  view->vertical_end_x = view->vertical_end_y = -1;
+  view->vertical_selection_id = 0;
 }
 
 
@@ -115,240 +172,668 @@ mousepad_view_class_init (MousepadViewClass *klass)
 static void
 mousepad_view_finalize (GObject *object)
 {
+  MousepadView *view = MOUSEPAD_VIEW (object);
+
+  /* stop a pending vertical selection timeout */
+  if (G_UNLIKELY (view->vertical_selection_id != 0))
+    g_source_remove (view->vertical_selection_id);
+
+  /* free the marks array (we're not owning a ref on the marks) */
+  if (G_UNLIKELY (view->marks != NULL))
+    g_ptr_array_free (view->marks, TRUE);
+
   (*G_OBJECT_CLASS (mousepad_view_parent_class)->finalize) (object);
 }
 
 
 
-/**
- * mousepad_view_key_press_event:
- * @widget : A #GtkWidget.
- * @event  : A #GdkEventKey
- *
- * This function is triggered when the user pressed a key in the
- * #MousepadView. It checks if we need to auto indent the new line
- * or when multiple lines are selected we (un)indent all of them.
- *
- * Return value: %TRUE if we handled the event, else we trigger the
- *               parent class so Gtk can handle the event.
- **/
+static gboolean
+mousepad_view_expose (GtkWidget      *widget,
+                      GdkEventExpose *event)
+{
+  GdkWindow   *window;
+  GtkTextView *text_view = GTK_TEXT_VIEW (widget);
+  gint         y_top, y_offset, y_bottom;
+  gint         y, height;
+  gint         line_number = 0, line_count;
+  GtkTextIter  iter;
+  gint         width;
+  PangoLayout *layout;
+  gchar        str[8]; /* maximum of 10e6 lines */
+
+  /* get the left window */
+  window = gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_LEFT);
+
+  /* check if the event if for the left window */
+  if (event->window == window)
+    {
+      /* get the real start position */
+      gtk_text_view_window_to_buffer_coords (text_view, GTK_TEXT_WINDOW_LEFT,
+                                             0, event->area.y, NULL, &y_top);
+
+      /* get the bottom position */
+      y_bottom = y_top + event->area.height;
+
+      /* get the buffer offset (negative value or 0) */
+      y_offset = event->area.y - y_top;
+
+      /* get the start iter */
+      gtk_text_view_get_line_at_y (text_view, &iter, y_top, NULL);
+
+      /* get the number of lines in the buffer */
+      line_count = gtk_text_buffer_get_line_count (text_view->buffer);
+
+      /* string with the 'last' line number */
+      g_snprintf (str, sizeof (str), "%d", MAX (99, line_count));
+
+      /* create the pango layout */
+      layout = gtk_widget_create_pango_layout (widget, str);
+      pango_layout_get_pixel_size (layout, &width, NULL);
+      pango_layout_set_width (layout, width);
+      pango_layout_set_alignment (layout, PANGO_ALIGN_RIGHT);
+
+      /* set the width of the left window border */
+      gtk_text_view_set_border_window_size (text_view, GTK_TEXT_WINDOW_LEFT, width + 8);
+
+      /* draw a vertical line to separate the numbers and text */
+      gtk_paint_vline (widget->style, window,
+                       GTK_WIDGET_STATE (widget),
+                       NULL, widget, NULL,
+                       event->area.y,
+                       event->area.y + event->area.height,
+                       width + 6);
+
+      /* walk through the lines until we hit the last line */
+      while (line_number < line_count)
+        {
+          /* get the y position and the height of the iter */
+          gtk_text_view_get_line_yrange (text_view, &iter, &y, &height);
+
+          /* include the buffer offset */
+          y += y_offset;
+
+          /* get the number of the line */
+          line_number = gtk_text_iter_get_line (&iter) + 1;
+
+          /* create the number */
+          g_snprintf (str, sizeof (str), "%d", line_number);
+
+          /* create the pange layout */
+          pango_layout_set_text (layout, str, strlen (str));
+
+          /* draw the layout on the left window */
+          gtk_paint_layout (widget->style, window,
+                            GTK_WIDGET_STATE (widget),
+                            FALSE, NULL, widget, NULL,
+                            width + 2, y, layout);
+
+          /* stop when we reached the end of the expose area */
+          if ((y + height) >= y_bottom)
+            break;
+
+          /* jump to the next line */
+          gtk_text_iter_forward_line (&iter);
+        }
+
+      /* release the pango layout */
+      g_object_unref (G_OBJECT (layout));
+    }
+
+  /* Gtk can draw the text now */
+  return (*GTK_WIDGET_CLASS (mousepad_view_parent_class)->expose_event) (widget, event);
+}
+
+
+
+static void
+mousepad_view_style_set (GtkWidget *widget,
+                         GtkStyle  *previous_style)
+{
+  GtkStyle     *style;
+  MousepadView *view = MOUSEPAD_VIEW (widget);
+
+  /* only set the style once, because we don't care to restart
+   * after a theme change */
+  if (view->tag == NULL && GTK_WIDGET_VISIBLE (widget))
+    {
+      /* get the textview style */
+      style = gtk_widget_get_style (widget);
+
+      /* create the selection tag */
+      view->tag = gtk_text_buffer_create_tag (mousepad_view_get_buffer (view), NULL,
+                                              "background-gdk", &style->base[GTK_STATE_SELECTED],
+                                              "foreground-gdk", &style->text[GTK_STATE_SELECTED],
+                                              NULL);
+    }
+
+  (*GTK_WIDGET_CLASS (mousepad_view_parent_class)->style_set) (widget, previous_style);
+}
+
+
+
 static gboolean
 mousepad_view_key_press_event (GtkWidget   *widget,
                                GdkEventKey *event)
 {
   MousepadView  *view = MOUSEPAD_VIEW (widget);
   GtkTextBuffer *buffer;
-  GtkTextIter    start, end;
+  GtkTextIter    start;
   GtkTextMark   *mark;
   guint          modifiers;
-  guint          key = event->keyval;
   gchar         *string;
-  gboolean       has_selection;
+  gboolean       key_is_tab = FALSE;
 
-  /* get the textview buffer */
-  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
+  /* get the modifiers state */
+  modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
 
-  /* check if we have to indent when the user pressed enter */
-  if ((key == GDK_Return || key == GDK_KP_Enter)
-      && !(event->state & GDK_SHIFT_MASK) && view->auto_indent)
+  /* handle the key event */
+  switch (event->keyval)
     {
-      /* get the iter */
-      mark = gtk_text_buffer_get_insert (buffer);
-      gtk_text_buffer_get_iter_at_mark (buffer, &start, mark);
+      case GDK_Return:
+      case GDK_KP_Enter:
+        if (!(event->state & GDK_SHIFT_MASK) && view->auto_indent)
+          {
+            /* get the textview buffer */
+            buffer = mousepad_view_get_buffer (view);
+
+            /* get the iter position of the cursor */
+            mark = gtk_text_buffer_get_insert (buffer);
+            gtk_text_buffer_get_iter_at_mark (buffer, &start, mark);
+
+            /* get the string of tabs and spaces we're going to indent */
+            string = mousepad_view_compute_indentation (buffer, &start);
+
+            if (string != NULL)
+              {
+                /* insert the indent characters */
+                gtk_text_buffer_insert (buffer, &start, "\n", 1);
+                gtk_text_buffer_insert (buffer, &start, string, strlen (string));
+
+                /* cleanup */
+                g_free (string);
+
+                /* make sure the new string is visible for the user */
+                gtk_text_view_scroll_mark_onscreen (GTK_TEXT_VIEW (widget), mark);
+
+                /* we inserted the new line, nothing to do for gtk */
+                return TRUE;
+              }
+          }
+        break;
 
-      /* get the string of tabs and spaces we're going to indent */
-      string = mousepad_view_compute_indentation (view, &start);
+      case GDK_Tab:
+      case GDK_KP_Tab:
+      case GDK_ISO_Left_Tab:
+        /* indent a tab when no modifiers are pressed, else fall-through */
+        if (modifiers == 0 &&
+            mousepad_view_indent_lines (view, TRUE, TRUE))
+              return TRUE;
 
-      if (string != NULL)
-        {
-          /* tell others we're going to change the buffer */
-          gtk_text_buffer_begin_user_action (buffer);
+        /* a tab was pressed */
+        key_is_tab = TRUE;
 
-          /* insert the indent characters */
-          gtk_text_buffer_insert (buffer, &start, "\n", 1);
-          gtk_text_buffer_insert (buffer, &start, string, strlen (string));
-          g_free (string);
+      case GDK_BackSpace:
+      case GDK_space:
+        /* indent on Space unindent on Backspace or Tab */
+        if (modifiers == GDK_SHIFT_MASK &&
+            mousepad_view_indent_lines (view, (event->keyval == GDK_space), key_is_tab))
+              return TRUE;
+        break;
+    }
 
-          /* we're finished */
-          gtk_text_buffer_end_user_action (buffer);
+  /* reset the vertical selection */
+  if (G_UNLIKELY (view->marks != NULL && !event->is_modifier))
+    mousepad_view_vertical_selection_reset (view);
 
-          /* make sure the new string is visible for the user */
-          gtk_text_view_scroll_mark_onscreen (GTK_TEXT_VIEW (widget), mark);
+  return (*GTK_WIDGET_CLASS (mousepad_view_parent_class)->key_press_event) (widget, event);
+}
 
-          return TRUE;
-        }
+
+
+static gboolean
+mousepad_view_button_press_event (GtkWidget      *widget,
+                                  GdkEventButton *event)
+{
+  MousepadView *view = MOUSEPAD_VIEW (widget);
+  GtkTextView  *text_view;
+
+  /* cleanup the marks if needed */
+  if (G_UNLIKELY (view->marks != NULL))
+    {
+      /* reset the vertical selection */
+      mousepad_view_vertical_selection_reset (view);
     }
 
-  /* get the modifiers state */
-  modifiers = gtk_accelerator_get_default_mod_mask ();
+  /* Shift + Ctrl + Button 1 are pressed */
+  if (G_UNLIKELY (event->type == GDK_BUTTON_PRESS
+                  && event->button == 1
+                  && (event->state & GDK_CONTROL_MASK)
+                  && (event->state & GDK_SHIFT_MASK)
+                  && event->x >= 0
+                  && event->y >= 0))
+    {
+      /* get the textview */
+      text_view = GTK_TEXT_VIEW (widget);
+
+      /* set the vertical selection start position, inclusing textview offset */
+      view->vertical_start_x = event->x + text_view->xoffset;
+      view->vertical_start_y = event->y + text_view->yoffset;
+
+      /* start a vertical selection timeout */
+      view->vertical_selection_id = g_timeout_add (75, mousepad_view_vertical_selection_timeout, view);
+
+      return TRUE;
+    }
+
+  return (*GTK_WIDGET_CLASS (mousepad_view_parent_class)->button_press_event) (widget, event);
+}
+
+
 
-  /* check if the user pressed the tab button for indenting lines */
-  if ((key == GDK_Tab || key == GDK_KP_Tab || key == GDK_ISO_Left_Tab)
-      && ((event->state & modifiers) == 0 || (event->state & modifiers) == GDK_SHIFT_MASK))
+static gboolean
+mousepad_view_button_release_event (GtkWidget      *widget,
+                                    GdkEventButton *event)
+{
+  MousepadView  *view = MOUSEPAD_VIEW (widget);
+  GArray        *array;
+  gint           i;
+  GtkTextBuffer *buffer;
+  GtkTextIter    iter;
+  GtkTextMark   *mark;
+
+  _mousepad_return_val_if_fail (view->marks == NULL, TRUE);
+
+  /* end of a vertical selection */
+  if (G_UNLIKELY (view->vertical_start_x != -1
+                  && event->button == 1))
+    {
+      /* stop the timeout */
+      g_source_remove (view->vertical_selection_id);
+      view->vertical_selection_id = 0;
+
+      /* get the array */
+      array = mousepad_view_vertical_selection_array (view, TRUE);
+
+      if (G_LIKELY (array != NULL))
         {
-          /* get the selected text */
-          has_selection = gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
+          /* get the buffer */
+          buffer = mousepad_view_get_buffer (view);
+
+          /* allocate the array */
+          view->marks = g_ptr_array_sized_new (array->len);
 
-          /* shift + tab */
-          if (event->state & GDK_SHIFT_MASK)
+          /* walk though the iters in the array */
+          for (i = 0; i < array->len; i++)
             {
-              /* unindent */
-              mousepad_view_indent_lines (view, &start, &end, FALSE);
+              /* get the iter from the array */
+              iter = g_array_index (array, GtkTextIter, i);
 
-              return TRUE;
+              /* create the mark */
+              mark = gtk_text_buffer_create_mark (buffer, NULL, &iter, FALSE);
+
+              /* append the mark to the array */
+              g_ptr_array_add (view->marks, mark);
             }
 
-          if (has_selection &&
-              ((gtk_text_iter_starts_line (&start) && gtk_text_iter_ends_line (&end)) ||
-               (gtk_text_iter_get_line (&start) != gtk_text_iter_get_line (&end))))
-            {
-              /* indent */
-              mousepad_view_indent_lines (view, &start, &end, TRUE);
+          /* free the array */
+          g_array_free (array, TRUE);
 
-              return TRUE;
-            }
+          /* notify the has-selection signal */
+          buffer->has_selection = TRUE;
+          g_object_notify (G_OBJECT (buffer), "has-selection");
         }
 
-  return (*GTK_WIDGET_CLASS (mousepad_view_parent_class)->key_press_event) (widget, event);
+      return TRUE;
+    }
+
+  return (*GTK_WIDGET_CLASS (mousepad_view_parent_class)->button_release_event) (widget, event);
 }
 
 
 
 /**
- * mousepad_view_indent_lines:
- * @view   : A #MousepadView
- * @start  : The start #GtkTextIter of the selection.
- * @end    : The end #GtkTextIter of the selection
- * @indent : Whether we need to indent or unindent the
- *           selected lines.
- *
- * This function (un)indents the selected lines. It simply walks
- * from the start line number to the end line number and prepends
- * or removed a tab (or the tab width in spaces).
+ * Vertical Selection Functions
  **/
+static GArray *
+mousepad_view_vertical_selection_array (MousepadView *view,
+                                        gboolean      forced)
+{
+  gint           x, y, i;
+  gint           start_y, end_y, height;
+  GArray        *array = NULL;
+  GtkTextIter    start, end;
+  GtkTextView   *text_view = GTK_TEXT_VIEW (view);
+
+  /* leave when there is no start coordinate */
+  if (G_UNLIKELY (view->vertical_start_x == -1))
+    return NULL;
+
+  /* get the cursor position */
+  gdk_window_get_pointer (gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT),
+                          &x, &y, NULL);
+
+  /* no negative values, that sucks */
+  if (G_LIKELY (x >= 0 && y >= 0))
+    {
+      /* get the buffer coordinates */
+      x += text_view->xoffset;
+      y += text_view->yoffset;
+
+      /* quick check if we need to update the selection */
+      if (G_LIKELY (forced || view->vertical_end_x != x || view->vertical_end_y != y))
+        {
+          /* update the end coordinates */
+          view->vertical_end_x = x;
+          view->vertical_end_y = y;
+
+          /* get the start and end iter */
+          gtk_text_view_get_iter_at_location (text_view, &start, 0, view->vertical_start_y);
+          gtk_text_view_get_iter_at_location (text_view, &end, 0, view->vertical_end_y);
+
+          /* make sure the start iter is before the end iter */
+          gtk_text_iter_order (&start, &end);
+
+          /* get the start and end location in pixels */
+          gtk_text_view_get_line_yrange (text_view, &start, &start_y, &height);
+          gtk_text_view_get_line_yrange (text_view, &end, &end_y, NULL);
+
+          /* allocate a sized array to avoid reallocation (array might be too big if there are equal iters) */
+          array = g_array_sized_new (FALSE, FALSE, sizeof (GtkTextIter), (end_y - start_y) / height * 2);
+
+          /* make sure atleast one line is selected */
+          end_y += height;
+
+          /* walk through the selected lines */
+          for (i = start_y; i < end_y; i += height)
+            {
+              /* get the begin and end iter */
+              gtk_text_view_get_iter_at_location (text_view, &start, view->vertical_start_x, i);
+              gtk_text_view_get_iter_at_location (text_view, &end, view->vertical_end_x, i);
+
+              /* continue when the iters are the same */
+              if (gtk_text_iter_equal (&start, &end))
+                continue;
+
+              /* make sure the iters are in the correct order */
+              gtk_text_iter_order (&start, &end);
+
+              /* append the iters to the array */
+              g_array_append_val (array, start);
+              g_array_append_val (array, end);
+            }
+        }
+    }
+
+  return array;
+}
+
+
+static gboolean
+mousepad_view_vertical_selection_timeout (gpointer user_data)
+{
+  gint           i;
+  GArray        *array;
+  GtkTextIter    start, end;
+  GtkTextBuffer *buffer;
+  MousepadView  *view = MOUSEPAD_VIEW (user_data);
+
+  GDK_THREADS_ENTER ();
+
+  /* get the iters array */
+  array = mousepad_view_vertical_selection_array (view, FALSE);
+
+  /* update the selection highlight */
+  if (array != NULL)
+    {
+      /* get the buffer */
+      buffer = mousepad_view_get_buffer (user_data);
+
+      /* remove the old highlight */
+      gtk_text_buffer_get_bounds (buffer, &start, &end);
+      gtk_text_buffer_remove_tag (buffer, view->tag, &start, &end);
+
+      /* walk though the iters in the array */
+      for (i = 0; i < array->len; i += 2)
+        {
+          /* get the iters from the array */
+          start = g_array_index (array, GtkTextIter, i);
+          end   = g_array_index (array, GtkTextIter, i + 1);
+
+          /* highlight the text between the iters */
+          gtk_text_buffer_apply_tag (buffer, view->tag, &start, &end);
+        }
+
+      /* free the array */
+      g_array_free (array, TRUE);
+
+      /* set the cursor below the pointer */
+      gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (view), &end, view->vertical_end_x, view->vertical_end_y);
+      gtk_text_buffer_place_cursor (buffer, &end);
+    }
+
+  GDK_THREADS_LEAVE ();
+
+  /* keep the timeout running */
+  return TRUE;
+}
+
+
+
 static void
-mousepad_view_indent_lines (MousepadView *view,
-                            GtkTextIter  *start,
-                            GtkTextIter  *end,
-                            gboolean      indent)
+mousepad_view_vertical_selection_reset (MousepadView *view)
 {
+  GtkTextIter    start, end;
   GtkTextBuffer *buffer;
-  GtkTextIter    iter, iter2;
-  gint           start_line, end_line;
-  gint           i, spaces, tabs;
+  gint           i;
 
-  /* get the textview buffer */
-  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
+  _mousepad_return_if_fail (view->marks != NULL);
 
-  /* get the line numbers for the start and end iter */
-  start_line = gtk_text_iter_get_line (start);
-  end_line = gtk_text_iter_get_line (end);
+  /* get the buffer */
+  buffer = mousepad_view_get_buffer (view);
 
-  /* check if the last line does not exist of 'invisible' characters */
-  if ((gtk_text_iter_get_visible_line_offset (end) == 0) && (end_line > start_line))
-    end_line--;
+  buffer->has_selection = FALSE;
+  g_object_notify (G_OBJECT (buffer), "has-selection");
 
-  /* tell everybody we're going to change the buffer */
-  gtk_text_buffer_begin_user_action (buffer);
+  /* remove the selections */
+  gtk_text_buffer_get_bounds (buffer, &start, &end);
+  gtk_text_buffer_remove_tag (buffer, view->tag, &start, &end);
 
-  /* insert a tab in front of each line */
-  for (i = start_line; i <= end_line; i++)
-    {
-      /* get the iter of the line we're going to indent */
-      gtk_text_buffer_get_iter_at_line (buffer, &iter, i);
+  /* reset the coordinates */
+  view->vertical_start_x = view->vertical_start_y =
+  view->vertical_end_x = view->vertical_end_y = -1;
+
+  /* delete all the marks in the buffer */
+  for (i = 0; i < view->marks->len; i++)
+    gtk_text_buffer_delete_mark (buffer, g_ptr_array_index (view->marks, i));
+
+  /* cleanup */
+  g_ptr_array_free (view->marks, TRUE);
+  view->marks = NULL;
+}
+
+
+/**
+ * Indentation Functions
+ **/
+static void
+mousepad_view_indent_line (GtkTextBuffer *buffer,
+                           GtkTextIter   *iter,
+                           gboolean       increase,
+                           gboolean       tab)
+{
+  GtkTextIter end;
+  gint        spaces;
 
-      /* don't (un)indent empty lines */
-      if (gtk_text_iter_ends_line (&iter))
-        continue;
+  /* increase the indentation */
+  if (increase)
+    {
+      /* insert the tab or a space */
+      gtk_text_buffer_insert (buffer, iter, tab ? "\t" : " ", -1);
+    }
+  else /* decrease the indentation */
+    {
+      /* set the end iter */
+      end = *iter;
 
-      if (indent)
+      /* remove a tab */
+      if (tab && gtk_text_iter_get_char (iter) == '\t')
         {
-          /* insert the tab */
-          gtk_text_buffer_insert (buffer, &iter, "\t", -1);
+          /* get the iter after the tab */
+          gtk_text_iter_forward_char (&end);
+
+          /* remove the tab */
+          gtk_text_buffer_delete (buffer, iter, &end);
         }
-      else /* unindent */
+      /* remove a space */
+      else if (gtk_text_iter_get_char (iter) == ' ')
         {
-          if (gtk_text_iter_get_char (&iter) == '\t')
+          /* (re)set the number of spaces */
+          spaces = tab ? 0 : 1;
+
+          /* count the number of spaces before the text (when unindenting tabs) */
+          while (tab && !gtk_text_iter_ends_line (&end))
             {
-              /* remove the tab */
-              iter2 = iter;
-              gtk_text_iter_forward_char (&iter2);
-              gtk_text_buffer_delete (buffer, &iter, &iter2);
+              if (gtk_text_iter_get_char (&end) == ' ')
+                spaces++;
+              else
+                break;
+
+              gtk_text_iter_forward_char (&end);
             }
-          else if (gtk_text_iter_get_char (&iter) == ' ')
-            {
-              spaces = 0;
 
-              iter2 = iter;
+          /* delete the spaces */
+          if (spaces > 0)
+            {
+              /* reset the end iter */
+              end = *iter;
 
-              /* count the number of spaces before the text */
-              while (!gtk_text_iter_ends_line (&iter2))
+              /* correct the number of spaces for matching tabs */
+              if (tab)
                 {
-                  if (gtk_text_iter_get_char (&iter2) == ' ')
-                    spaces++;
-                  else
-                    break;
+                  /* the number of spaces we need to remove to be inlign with the tabs */
+                  spaces = spaces - (spaces / 8) * 8;
 
-                  gtk_text_iter_forward_char (&iter2);
+                  /* we're inlign with the tabs, remove 8 spaces */
+                  if (spaces == 0)
+                    spaces = 8;
                 }
 
-              if (spaces > 0)
-                {
-                  /* the number of tabs in the spaces */
-                  tabs = spaces / 8;
+              /* delete the spaces */
+              gtk_text_iter_forward_chars (&end, spaces);
+              gtk_text_buffer_delete (buffer, iter, &end);
+            }
+        }
+    }
+}
 
-                  /* the number of spaces after the 'tabs' */
-                  spaces = spaces - (tabs * 8);
 
-                  /* we're going to remove the number of spaces after the 'tabs'
-                   * or 1 'tab' */
-                  if (spaces == 0)
-                    spaces = 8;
 
-                  iter2 = iter;
+static gboolean
+mousepad_view_indent_lines (MousepadView *view,
+                            gboolean      increase,
+                            gboolean      tab)
+{
+  GtkTextBuffer *buffer;
+  gint           start_line, end_line;
+  gint           i;
+  GtkTextIter    start, end, needle;
+  GtkTextMark   *mark;
+  gboolean       succeed = FALSE;
+
+  /* get the textview buffer */
+  buffer = mousepad_view_get_buffer (view);
 
-                  /* delete the spaces */
-                  gtk_text_iter_forward_chars (&iter2, spaces);
-                  gtk_text_buffer_delete (buffer, &iter, &iter2);
+  /* if we have a vertical selection, we're not handling that here */
+  if (view->vertical_start_x != -1)
+    {
+      /* walk through the start marks (hence the +2) */
+      for (i = 0; i < view->marks->len; i += 2)
+        {
+          /* get the mark */
+          mark = g_ptr_array_index (view->marks, i);
+
+          /* get the iter at this mark */
+          gtk_text_buffer_get_iter_at_mark (buffer, &start, mark);
+
+          /* first move the iter when we're going to indent */
+          if (!increase)
+            {
+              /* sync the iters */
+              needle = end = start;
+
+              /* walk backwards till we hit text */
+              while (gtk_text_iter_backward_char (&needle)
+                     && !gtk_text_iter_starts_line (&needle))
+                {
+                  if ((gtk_text_iter_get_char (&needle) == ' ' && !tab)
+                      || (gtk_text_iter_get_char (&needle) == '\t' && tab))
+                    end = needle;
+                  else
+                    break;
                 }
+
+              /* continue if the old and new iter are the same, this makes sure
+               * we don't indent spaces in the block */
+              if (gtk_text_iter_equal (&start, &end))
+                continue;
+
+              /* set the new start iter */
+              start = end;
             }
+
+          /* (de/in)crease the line the line with a tab or a space */
+          mousepad_view_indent_line (buffer, &start, increase, tab);
         }
+
+      succeed = TRUE;
     }
 
-  /* we're done */
-  gtk_text_buffer_end_user_action (buffer);
+  if (gtk_text_buffer_get_selection_bounds (buffer, &start, &end))
+    {
+      /* only indent when the entire line or multiple lines are selected */
+      if (increase
+          && !(gtk_text_iter_starts_line (&start) && gtk_text_iter_ends_line (&end))
+          && (gtk_text_iter_get_line (&start) == gtk_text_iter_get_line (&end)))
+        goto failed;
+
+      /* get the line numbers for the start and end iter */
+      start_line = gtk_text_iter_get_line (&start);
+      end_line = gtk_text_iter_get_line (&end);
+
+      /* insert a tab in front of each line */
+      for (i = start_line; i <= end_line; i++)
+        {
+          /* get the iter of the line we're going to indent */
+          gtk_text_buffer_get_iter_at_line (buffer, &start, i);
+
+          /* don't (un)indent empty lines */
+          if (gtk_text_iter_ends_line (&start))
+            continue;
+
+          /* change the indentation of this line */
+          mousepad_view_indent_line (buffer, &start, increase, tab);
+        }
+
+      succeed = TRUE;
+    }
 
+failed:
   /* scroll */
   gtk_text_view_scroll_mark_onscreen (GTK_TEXT_VIEW (view),
                                       gtk_text_buffer_get_insert (buffer));
+
+  return succeed;
 }
 
 
 
-/**
- * mousepad_view_compute_indentation:
- * @view : A #MousepadView.
- * @iter : The #GtkTextIter of the previous line.
- *
- * This function is used to get the tabs and spaces we need to
- * append when auto indentation is enabled. If scans the line of
- * @iter and returns a slice of the tabs and spaces before the
- * first character or line end.
- *
- * Return value: A string of tabs and spaces or %NULL when the
- *               previous line has no tabs or spaces before the text.
- **/
 static gchar *
-mousepad_view_compute_indentation (MousepadView *view,
-                                   GtkTextIter  *iter)
+mousepad_view_compute_indentation (GtkTextBuffer     *buffer,
+                                   const GtkTextIter *iter)
 {
-  GtkTextBuffer *buffer;
-  GtkTextIter    start, end;
-  gint           line;
-  gunichar       ch;
-
-  /* get the buffer */
-  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
+  GtkTextIter start, end;
+  gint        line;
+  gunichar    ch;
 
   /* get the line of the iter */
   line = gtk_text_iter_get_line (iter);
@@ -356,16 +841,17 @@ mousepad_view_compute_indentation (MousepadView *view,
   /* get the iter of the beginning of this line */
   gtk_text_buffer_get_iter_at_line (buffer, &start, line);
 
-  /* clone baby */
+  /* set the end iter */
   end = start;
 
   /* get the first character */
   ch = gtk_text_iter_get_char (&end);
 
   /* keep walking till we hit text or a new line */
-  while (g_unichar_isspace (ch) &&
-         (ch != '\n') && (ch != '\r') &&
-         (gtk_text_iter_compare (&end, iter) < 0))
+  while (g_unichar_isspace (ch)
+         && ch != '\n'
+         && ch != '\r'
+         && gtk_text_iter_compare (&end, iter) < 0)
     {
       /* break if we can't step forward */
       if (!gtk_text_iter_forward_char (&end))
@@ -385,204 +871,202 @@ mousepad_view_compute_indentation (MousepadView *view,
 
 
 
-/**
- * mousepad_view_get_lines:
- * @text_view : A #GtkTextView
- * @first_y   : The first y location of the visible #GtkTextView window.
- * @last_y    : The last y location of the visible #GtkTextView window.
- * @pixels    : Return location for the #GArray with the y location
- *              in pixels we need to draw line numbers.
- * @numbers   : Return location for the #GArray with all the visible
- *              line numbers.
- * @countp    : Return location for the number of lines visible between
- *              @first_y and @last_y.
- *
- * This function returns two arrays with the lines number and there draw
- * location of the visible text view window.
- **/
 static void
-mousepad_view_get_lines (GtkTextView  *text_view,
-                         gint          first_y,
-                         gint          last_y,
-                         GArray      **pixels,
-                         GArray      **numbers,
-                         gint         *countp)
+mousepad_view_handle_clipboard (MousepadView *view,
+                                GtkClipboard *clipboard,
+                                gboolean      remove)
 {
-  GtkTextIter iter;
-  gint        count = 0;
-  gint        y, height;
-  gint        line_number = 0, last_line_number;
+  GString       *string;
+  gint           i;
+  gint           ln, previous_ln;
+  gchar         *slice;
+  GtkTextBuffer *buffer;
+  GtkTextMark   *mark_start, *mark_end;
+  GtkTextIter    iter_start, iter_end;
 
-  g_array_set_size (*pixels, 0);
-  g_array_set_size (*numbers, 0);
+  /* get the buffer */
+  buffer = mousepad_view_get_buffer (view);
 
-  /* get iter at first y */
-  gtk_text_view_get_line_at_y (text_view, &iter, first_y, NULL);
+  /* create a new string */
+  if (G_LIKELY (clipboard))
+    string = g_string_new (NULL);
 
-  /* for each iter, get its location and add it to the arrays.
-   * stop when we pass last_y */
-  while (!gtk_text_iter_is_end (&iter))
+  /* walk through the marks */
+  for (i = 0; i < view->marks->len; i += 2)
     {
-      /* get the y position of the iter */
-      gtk_text_view_get_line_yrange (text_view, &iter, &y, &height);
+      /* get the marks */
+      mark_start = g_ptr_array_index (view->marks, i);
+      mark_end   = g_ptr_array_index (view->marks, i + 1);
 
-      /* get the number of the line */
-      line_number = gtk_text_iter_get_line (&iter);
+      /* and their iter positions */
+      gtk_text_buffer_get_iter_at_mark (buffer, &iter_start, mark_start);
+      gtk_text_buffer_get_iter_at_mark (buffer, &iter_end, mark_end);
 
-      /* append both values to the array */
-      g_array_append_val (*pixels, y);
-      g_array_append_val (*numbers, line_number);
+      /* only handle the selection when there is clipbaord */
+      if (G_LIKELY (clipboard))
+        {
+          /* the line number of the iter */
+          ln = gtk_text_iter_get_line (&iter_start);
 
-      ++count;
+          /* fix the number of new lines between the parts */
+          if (i == 0)
+            previous_ln = ln;
+          else
+            for (; previous_ln < ln; previous_ln++)
+              string = g_string_append_c (string, '\n');
 
-      /* stop when we reached last_y */
-      if ((y + height) >= last_y)
-        break;
+          /* get the text slice */
+          slice = gtk_text_buffer_get_slice (buffer, &iter_start, &iter_end, TRUE);
 
-      /* jump to the next line */
-      gtk_text_iter_forward_line (&iter);
-    }
+          /* append the slice to the string */
+          string = g_string_append (string, slice);
 
-  if (gtk_text_iter_is_end (&iter))
-    {
-      gtk_text_view_get_line_yrange (text_view, &iter, &y, &height);
-
-      last_line_number = gtk_text_iter_get_line (&iter);
-
-      if (last_line_number != line_number)
-        {
-          g_array_append_val (*pixels, y);
-          g_array_append_val (*numbers, last_line_number);
-          ++count;
+          /* cleanup */
+          g_free (slice);
         }
+
+      /* delete the text between the iters if requested */
+      if (remove)
+        gtk_text_buffer_delete (buffer, &iter_start, &iter_end);
     }
 
-  /* not lines in the buffer? weird, there is atleast one... */
-  if (G_UNLIKELY (count == 0))
+  /* set the clipboard text */
+  if (G_LIKELY (clipboard))
     {
-      y = 0;
-      g_array_append_val (*pixels, y);
-      g_array_append_val (*numbers, y);
+      /* set the clipboard text */
+      gtk_clipboard_set_text (clipboard, string->str, string->len);
 
-      ++count;
+      /* free the string */
+      g_string_free (string, TRUE);
     }
+}
 
-  *countp = count;
+
+
+gboolean
+mousepad_view_get_vertical_selection (MousepadView *view)
+{
+  _mousepad_return_val_if_fail (MOUSEPAD_IS_VIEW (view), FALSE);
+
+  return (view->marks != NULL);
 }
 
 
 
-/**
- * mousepad_view_expose:
- * @widget : A textview widget.
- * @event  : A #GdkEventExpose event.
- *
- * This function draws the line numbers in the GTK_TEXT_WINDOW_LEFT window
- * when it receives an expose event. At the end of the function we always
- * return the expose_event of the parent class so Gtk can draw the text.
- *
- * Return value: The return value of the parent_class.
- **/
-static gboolean
-mousepad_view_expose (GtkWidget      *widget,
-                      GdkEventExpose *event)
+void
+mousepad_view_cut_clipboard (MousepadView *view,
+                             GtkClipboard *clipboard)
 {
-  GdkWindow   *window;
-  GtkTextView *view = GTK_TEXT_VIEW (widget);
-  gint         y1, y2;
-  GArray      *numbers, *pixels;
-  gint         count, i;
-  PangoLayout *layout;
-  gchar        str[8]; /* maximum of 10e6 lines */
-  gint         width;
-  gint         position;
-  gint         line_number;
+  _mousepad_return_if_fail (MOUSEPAD_IS_VIEW (view));
+  _mousepad_return_if_fail (view->marks != NULL);
+  _mousepad_return_if_fail (GTK_IS_CLIPBOARD (clipboard));
 
-  /* get the left window */
-  window = gtk_text_view_get_window (view, GTK_TEXT_WINDOW_LEFT);
+  /* call the clipboard function with remove bool */
+  mousepad_view_handle_clipboard (view, clipboard, TRUE);
 
-  /* check if the event if for the left window */
-  if (event->window == window)
+  /* cleanup the vertical selection */
+  mousepad_view_vertical_selection_reset (view);
+}
+
+
+
+void
+mousepad_view_copy_clipboard (MousepadView *view,
+                              GtkClipboard *clipboard)
+{
+  _mousepad_return_if_fail (MOUSEPAD_IS_VIEW (view));
+  _mousepad_return_if_fail (view->marks != NULL);
+  _mousepad_return_if_fail (GTK_IS_CLIPBOARD (clipboard));
+
+  /* call the clipboard function without remove bool */
+  mousepad_view_handle_clipboard (view, clipboard, FALSE);
+}
+
+
+
+void
+mousepad_view_paste_column_clipboard (MousepadView *view,
+                                      GtkClipboard *clipboard)
+{
+  _mousepad_return_if_fail (MOUSEPAD_IS_VIEW (view));
+  _mousepad_return_if_fail (GTK_IS_CLIPBOARD (clipboard));
+
+  GtkTextMark   *mark;
+  GtkTextIter    iter;
+  GtkTextBuffer *buffer;
+  GtkTextView   *text_view;
+  gchar         *string, **pieces;
+  gint           i, y;
+  GdkRectangle   rect;
+
+  /* get the text buffer */
+  buffer = mousepad_view_get_buffer (view);
+  text_view = GTK_TEXT_VIEW (view);
+
+  /* get the clipboard text */
+  string = gtk_clipboard_wait_for_text (clipboard);
+
+  if (G_LIKELY (string != NULL))
     {
-      /* get the expose event area */
-      y1 = event->area.y;
-      y2 = y1 + event->area.height;
-
-      /* correct then to the size of the textview window */
-      gtk_text_view_window_to_buffer_coords (view, GTK_TEXT_WINDOW_LEFT,
-                                             0, y1, NULL, &y1);
-      gtk_text_view_window_to_buffer_coords (view, GTK_TEXT_WINDOW_LEFT,
-                                             0, y2, NULL, &y2);
-
-      /* create new arrays */
-      pixels  = g_array_new (FALSE, FALSE, sizeof (gint));
-      numbers = g_array_new (FALSE, FALSE, sizeof (gint));
-
-      /* get the line numbers and y coordinates. */
-      mousepad_view_get_lines (view,
-                               y1, y2,
-                               &pixels,
-                               &numbers,
-                               &count);
+      /* chop the string into pieces */
+      pieces = g_strsplit (string, "\n", -1);
 
-      /* create the pango layout */
-      g_snprintf (str, sizeof (str), "%d",
-                  MAX (99, gtk_text_buffer_get_line_count (view->buffer)));
-      layout = gtk_widget_create_pango_layout (widget, str);
-      pango_layout_get_pixel_size (layout, &width, NULL);
-      pango_layout_set_width (layout, width);
-      pango_layout_set_alignment (layout, PANGO_ALIGN_RIGHT);
+      /* cleanup */
+      g_free (string);
 
-      /* set the width of the left window border */
-      gtk_text_view_set_border_window_size (view, GTK_TEXT_WINDOW_LEFT,
-                                            width + 6);
+      /* get the iter are the cursor position */
+      mark = gtk_text_buffer_get_insert (buffer);
+      gtk_text_buffer_get_iter_at_mark (buffer, &iter, mark);
 
-      for (i = 0; i < count; i++)
+      /* get the buffer location */
+      gtk_text_view_get_iter_location (text_view, &iter, &rect);
+
+      /* insert the pieces in the buffer */
+      for (i = 0; pieces[i] != NULL; i++)
         {
-          gtk_text_view_buffer_to_window_coords (view,
-                                                 GTK_TEXT_WINDOW_LEFT, 0,
-                                                 g_array_index (pixels, gint, i),
-                                                 NULL,
-                                                 &position);
+          /* insert the text in the buffer */
+          gtk_text_buffer_insert (buffer, &iter, pieces[i], -1);
 
-          /* get the line number */
-          line_number = g_array_index (numbers, gint, i) + 1;
+          /* don't try to add a new line if we reached the end of the array */
+          if (G_UNLIKELY (pieces[i+1] == NULL))
+            break;
 
-          /* create the pango layout */
-          g_snprintf (str, sizeof (str), "%d", line_number);
-          pango_layout_set_markup (layout, str, strlen (str));
+          /* move the iter to the next line and add a new line if needed */
+          if (!gtk_text_iter_forward_line (&iter))
+             gtk_text_buffer_insert (buffer, &iter, "\n", 1);
 
-          /* time to draw the layout in the window */
-          gtk_paint_layout (widget->style, window,
-                            GTK_WIDGET_STATE (widget),
-                            FALSE, NULL,
-                            widget, NULL,
-                            width + 2,
-                            position,
-                            layout);
+          /* get the y coordinate for this line */
+          gtk_text_view_get_line_yrange (text_view, &iter, &y, NULL);
+
+          /* get the iter at the y location of this line and the same x as the cursor */
+          gtk_text_view_get_iter_at_location (text_view, &iter, rect.x, y);
         }
 
       /* cleanup */
-      g_array_free (pixels, TRUE);
-      g_array_free (numbers, TRUE);
+      g_strfreev (pieces);
 
-      g_object_unref (G_OBJECT (layout));
+      /* set the cursor at the last iter position */
+      gtk_text_buffer_place_cursor (buffer, &iter);
     }
+}
 
-  /* Gtk can draw the text now */
-  return (*GTK_WIDGET_CLASS (mousepad_view_parent_class)->expose_event) (widget, event);
+
+
+void
+mousepad_view_delete_selection (MousepadView *view)
+{
+  _mousepad_return_if_fail (MOUSEPAD_IS_VIEW (view));
+  _mousepad_return_if_fail (view->marks != NULL);
+
+  /* set the clipbaord to NULL to remove the selection */
+  mousepad_view_handle_clipboard (view, NULL, TRUE);
+
+  /* cleanup the vertical selection */
+  mousepad_view_vertical_selection_reset (view);
 }
 
 
 
-/**
- * mousepad_view_set_show_line_numbers:
- * @view    : A #MousepadView
- * @visible : Boolean whether we show or hide the line
- *            numbers.
- *
- * Whether line numbers are visible in the @view.
- **/
 void
 mousepad_view_set_show_line_numbers (MousepadView *view,
                                      gboolean      visible)
@@ -606,14 +1090,6 @@ mousepad_view_set_show_line_numbers (MousepadView *view,
 
 
 
-/**
- * mousepad_view_set_auto_indent:
- * @view    : A #MousepadView
- * @visible : Boolean whether we enable or disable
- *            auto indentation.
- *
- * Whether we enable auto indentation.
- **/
 void
 mousepad_view_set_auto_indent (MousepadView *view,
                                gboolean      enable)
diff --git a/mousepad/mousepad-view.h b/mousepad/mousepad-view.h
index f18da6e..3eb1406 100644
--- a/mousepad/mousepad-view.h
+++ b/mousepad/mousepad-view.h
@@ -34,6 +34,19 @@ typedef struct _MousepadView      MousepadView;
 
 GType           mousepad_view_get_type                  (void) G_GNUC_CONST;
 
+gboolean        mousepad_view_get_vertical_selection    (MousepadView *view);
+
+void            mousepad_view_cut_clipboard             (MousepadView *view,
+                                                         GtkClipboard *clipboard);
+
+void            mousepad_view_copy_clipboard            (MousepadView *view,
+                                                         GtkClipboard *clipboard);
+
+void            mousepad_view_paste_column_clipboard    (MousepadView *view,
+                                                         GtkClipboard *clipboard);
+
+void            mousepad_view_delete_selection          (MousepadView *view);
+
 void            mousepad_view_set_show_line_numbers     (MousepadView *view,
                                                          gboolean      visible);
 
diff --git a/mousepad/mousepad-window-ui.xml b/mousepad/mousepad-window-ui.xml
index e69829f..a3d6483 100644
--- a/mousepad/mousepad-window-ui.xml
+++ b/mousepad/mousepad-window-ui.xml
@@ -40,6 +40,7 @@
       <menuitem action="cut" />
       <menuitem action="copy" />
       <menuitem action="paste" />
+      <menuitem action="paste-column" />
       <menuitem action="delete" />
       <separator />
       <menuitem action="select-all" />
diff --git a/mousepad/mousepad-window.c b/mousepad/mousepad-window.c
index 5b21c0a..76e6b1b 100644
--- a/mousepad/mousepad-window.c
+++ b/mousepad/mousepad-window.c
@@ -213,6 +213,8 @@ static void              mousepad_window_action_copy                  (GtkAction
                                                                        MousepadWindow         *window);
 static void              mousepad_window_action_paste                 (GtkAction              *action,
                                                                        MousepadWindow         *window);
+static void              mousepad_window_action_paste_column          (GtkAction              *action,
+                                                                       MousepadWindow         *window);
 static void              mousepad_window_action_delete                (GtkAction              *action,
                                                                        MousepadWindow         *window);
 static void              mousepad_window_action_select_all            (GtkAction              *action,
@@ -313,41 +315,42 @@ static const GtkActionEntry action_entries[] =
     { "recent-menu", NULL, N_("Open _Recent"), NULL, NULL, NULL, },
       { "no-recent-items", NULL, N_("No items found"), NULL, NULL, NULL, },
       { "clear-recent", GTK_STOCK_CLEAR, N_("Clear _History"), NULL, N_("Clear the recently used files history"), G_CALLBACK (mousepad_window_action_clear_recent), },
-    { "save-file", GTK_STOCK_SAVE, N_("_Save"), NULL, N_("Save the current file"), G_CALLBACK (mousepad_window_action_save_file), },
-    { "save-file-as", GTK_STOCK_SAVE_AS, N_("Save _As"), NULL, N_("Save current document as another file"), G_CALLBACK (mousepad_window_action_save_file_as), },
+    { "save-file", GTK_STOCK_SAVE, NULL, NULL, N_("Save the current file"), G_CALLBACK (mousepad_window_action_save_file), },
+    { "save-file-as", GTK_STOCK_SAVE_AS, NULL, NULL, N_("Save current document as another file"), G_CALLBACK (mousepad_window_action_save_file_as), },
     { "reload", GTK_STOCK_REFRESH, N_("Re_load"), NULL, N_("Reload this document."), G_CALLBACK (mousepad_window_action_reload), },
-    { "print-document", GTK_STOCK_PRINT, N_("_Print"), "<control>P", N_("Prin the current page"), G_CALLBACK (mousepad_window_action_print), },
+    { "print-document", GTK_STOCK_PRINT, NULL, "<control>P", N_("Prin the current page"), G_CALLBACK (mousepad_window_action_print), },
     { "detach-tab", NULL, N_("_Detach Tab"), "<control>D", N_("Move the current document to a new window"), G_CALLBACK (mousepad_window_action_detach), },
     { "close-tab", GTK_STOCK_CLOSE, N_("C_lose Tab"), "<control>W", N_("Close the current file"), G_CALLBACK (mousepad_window_action_close_tab), },
     { "close-window", GTK_STOCK_QUIT, N_("_Close Window"), "<control>Q", N_("Quit the program"), G_CALLBACK (mousepad_window_action_close), },
 
   { "edit-menu", NULL, N_("_Edit"), NULL, NULL, NULL, },
-    { "undo", GTK_STOCK_UNDO, N_("_Undo"), "<control>Z", N_("Undo the last action"), G_CALLBACK (mousepad_window_action_undo), },
-    { "redo", GTK_STOCK_REDO, N_("_Redo"), "<control>Y", N_("Redo the last undone action"), G_CALLBACK (mousepad_window_action_redo), },
-    { "cut", GTK_STOCK_CUT, N_("Cu_t"), NULL, N_("Cut the selection"), G_CALLBACK (mousepad_window_action_cut), },
-    { "copy", GTK_STOCK_COPY, N_("_Copy"), NULL, N_("Copy the selection"), G_CALLBACK (mousepad_window_action_copy), },
-    { "paste", GTK_STOCK_PASTE, N_("_Paste"), NULL, N_("Paste the clipboard"), G_CALLBACK (mousepad_window_action_paste), },
-    { "delete", GTK_STOCK_DELETE, N_("_Delete"), NULL, N_("Delete the selected text"), G_CALLBACK (mousepad_window_action_delete), },
-    { "select-all", GTK_STOCK_SELECT_ALL, N_("Select _All"), NULL, N_("Select the entire document"), G_CALLBACK (mousepad_window_action_select_all), },
+    { "undo", GTK_STOCK_UNDO, NULL, "<control>Z", N_("Undo the last action"), G_CALLBACK (mousepad_window_action_undo), },
+    { "redo", GTK_STOCK_REDO, NULL, "<control>Y", N_("Redo the last undone action"), G_CALLBACK (mousepad_window_action_redo), },
+    { "cut", GTK_STOCK_CUT, NULL, NULL, N_("Cut the selection"), G_CALLBACK (mousepad_window_action_cut), },
+    { "copy", GTK_STOCK_COPY, NULL, NULL, N_("Copy the selection"), G_CALLBACK (mousepad_window_action_copy), },
+    { "paste", GTK_STOCK_PASTE, NULL, NULL, N_("Paste the clipboard"), G_CALLBACK (mousepad_window_action_paste), },
+    { "paste-column", NULL, N_("Paste _Column"), NULL, N_("Paste the clipboard text in a clumn"), G_CALLBACK (mousepad_window_action_paste_column), },
+    { "delete", GTK_STOCK_DELETE, NULL, NULL, N_("Delete the selected text"), G_CALLBACK (mousepad_window_action_delete), },
+    { "select-all", GTK_STOCK_SELECT_ALL, NULL, NULL, N_("Select the entire document"), G_CALLBACK (mousepad_window_action_select_all), },
 
   { "search-menu", NULL, N_("_Search"), NULL, NULL, NULL, },
-    { "find", GTK_STOCK_FIND, N_("_Find"), NULL, N_("Search for text"), G_CALLBACK (mousepad_window_action_find), },
+    { "find", GTK_STOCK_FIND, NULL, NULL, N_("Search for text"), G_CALLBACK (mousepad_window_action_find), },
     { "find-next", NULL, N_("Find _Next"), NULL, N_("Search forwards for the same text"), G_CALLBACK (mousepad_window_action_find_next), },
     { "find-previous", NULL, N_("Find _Previous"), NULL, N_("Search backwards for the same text"), G_CALLBACK (mousepad_window_action_find_previous), },
-    { "replace", GTK_STOCK_FIND_AND_REPLACE, N_("_Replace"), NULL, N_("Search for and replace text"), G_CALLBACK (mousepad_window_action_replace), },
-    { "jump-to", GTK_STOCK_JUMP_TO, N_("_Jump To"), NULL, N_("Go to a specific line"), G_CALLBACK (mousepad_window_action_jump_to), },
+    { "replace", GTK_STOCK_FIND_AND_REPLACE, NULL, NULL, N_("Search for and replace text"), G_CALLBACK (mousepad_window_action_replace), },
+    { "jump-to", GTK_STOCK_JUMP_TO, NULL, NULL, N_("Go to a specific line"), G_CALLBACK (mousepad_window_action_jump_to), },
 
   { "view-menu", NULL, N_("_View"), NULL, NULL, NULL, },
-    { "font", GTK_STOCK_SELECT_FONT, N_("_Font"), NULL, N_("Change the editor font"), G_CALLBACK (mousepad_window_action_select_font), },
+    { "font", GTK_STOCK_SELECT_FONT, NULL, NULL, N_("Change the editor font"), G_CALLBACK (mousepad_window_action_select_font), },
 
   { "document-menu", NULL, N_("_Document"), NULL, NULL, NULL, },
 
   { "go-menu", NULL, N_("_Go"), NULL, },
-    { "back", GTK_STOCK_GO_BACK, N_("Back"), NULL, N_("Select the previous tab"), G_CALLBACK (mousepad_window_action_prev_tab), },
-    { "forward", GTK_STOCK_GO_FORWARD, N_("Forward"), NULL, N_("Select the next tab"), G_CALLBACK (mousepad_window_action_next_tab), },
+    { "back", GTK_STOCK_GO_BACK, NULL, NULL, N_("Select the previous tab"), G_CALLBACK (mousepad_window_action_prev_tab), },
+    { "forward", GTK_STOCK_GO_FORWARD, NULL, NULL, N_("Select the next tab"), G_CALLBACK (mousepad_window_action_next_tab), },
 
   { "help-menu", NULL, N_("_Help"), NULL, },
-    { "about", GTK_STOCK_ABOUT, N_("_About"), NULL, N_("About this application"), G_CALLBACK (mousepad_window_action_about), },
+    { "about", GTK_STOCK_ABOUT, NULL, NULL, N_("About this application"), G_CALLBACK (mousepad_window_action_about), },
 };
 
 static const GtkToggleActionEntry toggle_action_entries[] =
@@ -929,7 +932,7 @@ mousepad_window_save (MousepadWindow   *window,
 
   /* get the current filename */
   filename = mousepad_document_get_filename (document);
-  
+
   /* check if the file really exists */
   if (g_file_test (filename, G_FILE_TEST_EXISTS) == FALSE)
     filename = NULL;
@@ -1612,7 +1615,7 @@ static gboolean
 mousepad_window_update_gomenu_idle (gpointer user_data)
 {
   GtkWidget      *document;
-  MousepadWindow *window = MOUSEPAD_WINDOW (user_data);
+  MousepadWindow *window;
   gint            npages;
   gint            n;
   gchar           name[15];
@@ -1623,8 +1626,13 @@ mousepad_window_update_gomenu_idle (gpointer user_data)
   GSList         *group = NULL;
   GList          *actions, *li;
 
+  _mousepad_return_val_if_fail (MOUSEPAD_IS_WINDOW (user_data), FALSE);
+
   GDK_THREADS_ENTER ();
 
+  /* get the window */
+  window = MOUSEPAD_WINDOW (user_data);
+
   /* prevent menu updates */
   lock_menu_updates++;
 
@@ -1704,6 +1712,8 @@ mousepad_window_update_gomenu_idle (gpointer user_data)
 static void
 mousepad_window_update_gomenu_idle_destroy (gpointer user_data)
 {
+  _mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (user_data));
+
   MOUSEPAD_WINDOW (user_data)->update_go_menu_id = 0;
 }
 
@@ -1712,6 +1722,8 @@ mousepad_window_update_gomenu_idle_destroy (gpointer user_data)
 static void
 mousepad_window_update_gomenu (MousepadWindow *window)
 {
+  _mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+
   /* leave when we're updating multiple files or there is this an idle function pending */
   if (lock_menu_updates && window->update_go_menu_id != 0)
     return;
@@ -2518,6 +2530,16 @@ mousepad_window_action_paste (GtkAction      *action,
 
 
 static void
+mousepad_window_action_paste_column (GtkAction      *action,
+                                     MousepadWindow *window)
+{
+  if (G_LIKELY (window->active != NULL))
+    mousepad_document_paste_column_clipboard (window->active);
+}
+
+
+
+static void
 mousepad_window_action_delete (GtkAction      *action,
                                MousepadWindow *window)
 {


More information about the Xfce4-commits mailing list