[Xfce4-commits] <mousepad:master> * mousepad/mousepad-view.c: Reimplemented multi-selection. It is also possible to do multi editing (typing in all the selections), select random words in the document (they will be copied to the clipboard as a list of words) and multi insert (create a drag without content or drag at the end of lines). Multiple selection will also be merged when possible. This proably has some rough edges, but it works pretty good. Keyboard multi drags will be added later. * docs/manual/C/Mousepad.xml.in: Add a bit of info about multiple- and column-selections, but I'm not very good at this. * mousepad/mousepad-{window.c, window-ui.xml}: Use our edit menu on right click, so copy/paste/delete works with multi selections. Also saves a bit of code since it integrates with the ui-manager.
Nick Schermer
noreply at xfce.org
Sat May 5 21:30:54 CEST 2012
Updating branch refs/heads/master
to 24c163061c3e40620db488f98c51f1edd8d20f62 (commit)
from 2d4c6469fc7680fe83c02af5ecfd4791f96de79f (commit)
commit 24c163061c3e40620db488f98c51f1edd8d20f62
Author: Nick Schermer <nick at xfce.org>
Date: Thu Oct 25 17:51:07 2007 +0000
* mousepad/mousepad-view.c: Reimplemented multi-selection. It is also
possible to do multi editing (typing in all the selections), select
random words in the document (they will be copied to the clipboard as
a list of words) and multi insert (create a drag without content or
drag at the end of lines). Multiple selection will also be merged
when possible. This proably has some rough edges, but it works pretty
good. Keyboard multi drags will be added later.
* docs/manual/C/Mousepad.xml.in: Add a bit of info about multiple- and
column-selections, but I'm not very good at this.
* mousepad/mousepad-{window.c,window-ui.xml}: Use our edit menu on right
click, so copy/paste/delete works with multi selections. Also saves a
bit of code since it integrates with the ui-manager.
(Old svn revision: 26195)
ChangeLog | 15 +
docs/manual/C/Mousepad.xml.in | 26 +-
mousepad/mousepad-document.c | 1 -
mousepad/mousepad-private.h | 5 +
mousepad/mousepad-undo.c | 37 --
mousepad/mousepad-undo.h | 4 -
mousepad/mousepad-view.c | 1243 +++++++++++++++++++++++++--------------
mousepad/mousepad-view.h | 1 +
mousepad/mousepad-window-ui.xml | 13 +
mousepad/mousepad-window.c | 53 ++
10 files changed, 917 insertions(+), 481 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index f06185a..5e9a0f3 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,18 @@
+2007-10-25 Nick Schermer <nick at xfce.org>
+ * mousepad/mousepad-view.c: Reimplemented multi-selection. It is also
+ possible to do multi editing (typing in all the selections), select
+ random words in the document (they will be copied to the clipboard as
+ a list of words) and multi insert (create a drag without content or
+ drag at the end of lines). Multiple selection will also be merged
+ when possible. This proably has some rough edges, but it works pretty
+ good. Keyboard multi drags will be added later.
+ * docs/manual/C/Mousepad.xml.in: Add a bit of info about multiple- and
+ column-selections, but I'm not very good at this.
+ * mousepad/mousepad-{window.c,window-ui.xml}: Use our edit menu on right
+ click, so copy/paste/delete works with multi selections. Also saves a
+ bit of code since it integrates with the ui-manager.
+
+
2007-10-23 Nick Schermer <nick at xfce.org>
* configure.in.in, po/POTFILES.in: Fix dist-check. Thank to Brian
for the hint.
diff --git a/docs/manual/C/Mousepad.xml.in b/docs/manual/C/Mousepad.xml.in
index b2fac62..48cf014 100644
--- a/docs/manual/C/Mousepad.xml.in
+++ b/docs/manual/C/Mousepad.xml.in
@@ -63,7 +63,7 @@
</para>
</sect1>
- <sect1 id="the-mousepad-window">
+ <sect1 id="mousepad-window">
<title>The Mousepad Window</title>
<para>
@@ -86,6 +86,30 @@
</para>
</sect2>
</sect1>
+
+ <sect1 id="multi-selection">
+ <title>Multi Editing</title>
+
+ <para>
+ Mousepad supports multiple- and column-selections. You can start a column selection by pressing the <keycap>Ctrl</keycap>
+ key and starting a drag with your mouse. To add single words to the selection press <keycap>Ctrl</keycap> and double-click
+ a word with the mouse pointer. As long as you press <keycap>Ctrl</keycap> you can add content to the selection.
+ </para>
+
+ <para>
+ When you've created a multi-selection, it is possible to cut or copy this to the clipboard. When you start typing after a
+ multi-selection, the selected content will be removed and the typed characters will appear in each selected part.
+ </para>
+
+ <para>
+ It is also possible to use this for multi typing. When you press <keycap>Ctrl</keycap> and drag at the end of a couple of lines
+ you will see blue retangles appear at the end of the lines. When you start typing the characters will be added to each selected
+ line. You can add as much of these insert points as you want as long you don't select a whole character.
+
+ Another useful key combination with multi typing is <keycombo><keycap>Ctrl</keycap><keycap>Backspace</keycap></keycombo>, this will
+ also remove character left or the insert point, while <keycap>Backspace</keycap> only removes characters inside the selection.
+ </para>
+ </sect1>
</article>
<!--
vim:set ts=2 sw=2 et ai encoding=UTF-8:
diff --git a/mousepad/mousepad-document.c b/mousepad/mousepad-document.c
index 856e262..7a55dac 100644
--- a/mousepad/mousepad-document.c
+++ b/mousepad/mousepad-document.c
@@ -260,7 +260,6 @@ mousepad_document_init (MousepadDocument *document)
g_signal_connect (G_OBJECT (document->buffer), "notify::has-selection", G_CALLBACK (mousepad_document_notify_has_selection), document);
g_signal_connect (G_OBJECT (document->buffer), "notify::cursor-position", G_CALLBACK (mousepad_document_notify_cursor_position), document);
g_signal_connect (G_OBJECT (document->textview), "notify::overwrite", G_CALLBACK (mousepad_document_toggle_overwrite), document);
- g_signal_connect (G_OBJECT (document->textview), "populate-popup", G_CALLBACK (mousepad_undo_populate_popup), document->undo);
g_signal_connect (G_OBJECT (document->textview), "drag-data-received", G_CALLBACK (mousepad_document_drag_data_received), document);
}
diff --git a/mousepad/mousepad-private.h b/mousepad/mousepad-private.h
index 20d9eb8..dec76ca 100644
--- a/mousepad/mousepad-private.h
+++ b/mousepad/mousepad-private.h
@@ -25,6 +25,11 @@
G_BEGIN_DECLS
+/* handling flags */
+#define MOUSEPAD_SET_FLAG(flags,flag) G_STMT_START{ ((flags) |= (flag)); }G_STMT_END
+#define MOUSEPAD_UNSET_FLAG(flags,flag) G_STMT_START{ ((flags) &= ~(flag)); }G_STMT_END
+#define MOUSEPAD_HAS_FLAG(flags,flag) (((flags) & (flag)) != 0)
+
/* for personal testing */
#define TIMER_START GTimer *__FUNCTION__timer = g_timer_new();
#define TIMER_SPLIT g_print ("%s (%s:%d): %.2f ms\n", __FUNCTION__, __FILE__, __LINE__, g_timer_elapsed (__FUNCTION__timer, NULL) * 1000);
diff --git a/mousepad/mousepad-undo.c b/mousepad/mousepad-undo.c
index 284f7a3..6d6b213 100644
--- a/mousepad/mousepad-undo.c
+++ b/mousepad/mousepad-undo.c
@@ -749,40 +749,3 @@ mousepad_undo_do_redo (MousepadUndo *undo)
/* redo the last undo-ed step */
mousepad_undo_step_perform (undo, TRUE);
}
-
-
-
-void
-mousepad_undo_populate_popup (GtkTextView *textview,
- GtkMenu *menu,
- MousepadUndo *undo)
-{
- GtkWidget *item;
- gboolean editable;
-
- _mousepad_return_if_fail (MOUSEPAD_IS_UNDO (undo));
- _mousepad_return_if_fail (GTK_IS_TEXT_VIEW (textview));
- _mousepad_return_if_fail (GTK_IS_MENU (menu));
-
- /* whether the textview is editable */
- editable = gtk_text_view_get_editable (textview);
-
- /* separator */
- item = gtk_separator_menu_item_new ();
- gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
- gtk_widget_show (item);
-
- /* redo item */
- item = gtk_image_menu_item_new_from_stock (GTK_STOCK_REDO, NULL);
- gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
- gtk_widget_set_sensitive (item, editable && mousepad_undo_can_redo (undo));
- g_signal_connect_swapped (G_OBJECT (item), "activate", G_CALLBACK (mousepad_undo_do_redo), undo);
- gtk_widget_show (item);
-
- /* undo item */
- item = gtk_image_menu_item_new_from_stock (GTK_STOCK_UNDO, NULL);
- gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
- gtk_widget_set_sensitive (item, editable && mousepad_undo_can_undo (undo));
- g_signal_connect_swapped (G_OBJECT (item), "activate", G_CALLBACK (mousepad_undo_do_undo), undo);
- gtk_widget_show (item);
-}
diff --git a/mousepad/mousepad-undo.h b/mousepad/mousepad-undo.h
index 15cb44e..2e37977 100644
--- a/mousepad/mousepad-undo.h
+++ b/mousepad/mousepad-undo.h
@@ -48,10 +48,6 @@ void mousepad_undo_do_undo (MousepadUndo *undo);
void mousepad_undo_do_redo (MousepadUndo *undo);
-void mousepad_undo_populate_popup (GtkTextView *textview,
- GtkMenu *menu,
- MousepadUndo *undo);
-
G_END_DECLS
#endif /* !__MOUSEPAD_UNDO_H__ */
diff --git a/mousepad/mousepad-view.c b/mousepad/mousepad-view.c
index b979690..088f44d 100644
--- a/mousepad/mousepad-view.c
+++ b/mousepad/mousepad-view.c
@@ -31,51 +31,62 @@
-/* we can assume there is a buffer for this view */
-#define mousepad_view_get_buffer(view) (GTK_TEXT_VIEW (view)->buffer)
-
-
-
-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);
-static void mousepad_view_vertical_selection_handle (MousepadView *view,
- gboolean prepend_marks);
-static gboolean mousepad_view_vertical_selection_finish (MousepadView *view);
-static void mousepad_view_vertical_selection_free_marks (MousepadView *view);
-static void mousepad_view_vertical_selection_clear (MousepadView *view);
-static gboolean mousepad_view_vertical_selection_timeout (gpointer user_data);
-static void mousepad_view_vertical_selection_timeout_destroy (gpointer user_data);
-static void mousepad_view_increase_indent_iter (MousepadView *view,
- GtkTextIter *iter,
- gboolean tab);
-static void mousepad_view_decrease_indent_iter (MousepadView *view,
- GtkTextIter *iter,
- gboolean tab);
-static void mousepad_view_indent_selection (MousepadView *view,
- gboolean increase,
- gboolean tab);
-
-static gchar *mousepad_view_indentation_string (GtkTextBuffer *buffer,
- const GtkTextIter *iter);
-static gint mousepad_view_calculate_layout_width (GtkWidget *widget,
- gsize length,
- gchar fill_char);
-static void mousepad_view_clipboard (MousepadView *view,
- GtkClipboard *clipboard,
- gboolean remove);
-
-
+#define mousepad_view_get_buffer(view) (GTK_TEXT_VIEW (view)->buffer)
+#define MOUSEPAD_VIEW_RECT_WIDTH (3)
+
+
+
+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);
+static void mousepad_view_selection_key_press_event (MousepadView *view,
+ GdkEventKey *event,
+ guint modifiers);
+static void mousepad_view_selection_delete_content (MousepadView *view);
+static void mousepad_view_selection_destroy (MousepadView *view);
+static void mousepad_view_selection_add_word (MousepadView *view);
+static void mousepad_view_selection_cleanup_equal_marks (MousepadView *view);
+static void mousepad_view_selection_add_marks (MousepadView *view,
+ GtkTextIter *start_iter,
+ GtkTextIter *end_iter);
+static void mousepad_view_selection_draw (MousepadView *view,
+ gboolean draw_rectangles,
+ gboolean add_marks);
+static void mousepad_view_selection_emit_has_selection (MousepadView *view,
+ gboolean has_content);
+static gboolean mousepad_view_selection_timeout (gpointer user_data);
+static void mousepad_view_selection_timeout_destroy (gpointer user_data);
+static void mousepad_view_indent_increase (MousepadView *view,
+ GtkTextIter *iter);
+static void mousepad_view_indent_decrease (MousepadView *view,
+ GtkTextIter *iter);
+static void mousepad_view_indent_selection (MousepadView *view,
+ gboolean increase);
+static gchar *mousepad_view_indent_string (GtkTextBuffer *buffer,
+ const GtkTextIter *iter);
+static gint mousepad_view_calculate_layout_width (GtkWidget *widget,
+ gsize length,
+ gchar fill_char);
+
+
+enum _MousepadViewFlags
+{
+ HAS_SELECTION = 1 << 0, /* if there are marks in the selection list */
+ EDITING_MODE = 1 << 1, /* users can type in the selection */
+ HAS_CONTENT = 1 << 2, /* selection contains content */
+ DRAGGING = 1 << 3, /* still dragging */
+ MULTIPLE = 1 << 4 /* whether this is an multiple selection */
+};
struct _MousepadViewClass
{
@@ -86,24 +97,29 @@ struct _MousepadView
{
GtkTextView __parent__;
- /* coordinates for the vertical selection */
- gint vertical_start_x, vertical_start_y;
- gint vertical_end_x, vertical_end_y;
+ /* current status */
+ MousepadViewFlags flags;
+
+ /* the selection style tag */
+ GtkTextTag *selection_tag;
- /* pointer array with all the vertical selection marks */
- GSList *marks;
+ /* list with all the selection marks */
+ GSList *marks;
- /* vertical selection timeout id */
- guint vertical_timeout_id;
+ /* selection timeout id */
+ guint selection_timeout_id;
- /* the selection tag */
- GtkTextTag *tag;
+ /* coordinates for the selection */
+ gint selection_start_x;
+ gint selection_start_y;
+ gint selection_end_x;
+ gint selection_end_y;
/* settings */
- guint auto_indent : 1;
- guint line_numbers : 1;
- guint insert_spaces : 1;
- guint tab_size;
+ guint auto_indent : 1;
+ guint line_numbers : 1;
+ guint insert_spaces : 1;
+ guint tab_size;
};
@@ -163,14 +179,11 @@ mousepad_view_init (MousepadView *view)
view->insert_spaces = FALSE;
view->tab_size = 8;
- /* initialize vertical selection */
+ /* initialize selection variables */
+ view->selection_timeout_id = 0;
+ view->flags = 0;
+ view->selection_tag = NULL;
view->marks = NULL;
- view->tag = NULL;
- view->vertical_start_x = -1;
- view->vertical_start_y = -1;
- view->vertical_end_x = -1;
- view->vertical_end_y = -1;
- view->vertical_timeout_id = 0;
}
@@ -180,11 +193,11 @@ mousepad_view_finalize (GObject *object)
{
MousepadView *view = MOUSEPAD_VIEW (object);
- /* stop a pending vertical selection timeout */
- if (G_UNLIKELY (view->vertical_timeout_id != 0))
- g_source_remove (view->vertical_timeout_id);
+ /* stop a running selection timeout */
+ if (G_UNLIKELY (view->selection_timeout_id != 0))
+ g_source_remove (view->selection_timeout_id);
- /* free the marks list (we're not owning a ref on the marks) */
+ /* free the selection marks list */
if (G_UNLIKELY (view->marks != NULL))
g_slist_free (view->marks);
@@ -197,24 +210,27 @@ 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)
+ GtkTextView *textview = GTK_TEXT_VIEW (widget);
+ MousepadView *view = MOUSEPAD_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 */
+
+ if (view->flags != 0
+ && MOUSEPAD_HAS_FLAG (view->flags, HAS_CONTENT) == FALSE
+ && event->window == gtk_text_view_get_window (textview, GTK_TEXT_WINDOW_TEXT))
+ {
+ /* redraw the retangles for the vertical selection */
+ mousepad_view_selection_draw (view, TRUE, FALSE);
+ }
+ else if (event->window == gtk_text_view_get_window (textview, GTK_TEXT_WINDOW_LEFT))
{
/* get the real start position */
- gtk_text_view_window_to_buffer_coords (text_view, GTK_TEXT_WINDOW_LEFT,
+ gtk_text_view_window_to_buffer_coords (textview, GTK_TEXT_WINDOW_LEFT,
0, event->area.y, NULL, &y_top);
/* get the bottom position */
@@ -224,10 +240,10 @@ mousepad_view_expose (GtkWidget *widget,
y_offset = event->area.y - y_top;
/* get the start iter */
- gtk_text_view_get_line_at_y (text_view, &iter, y_top, NULL);
+ gtk_text_view_get_line_at_y (textview, &iter, y_top, NULL);
/* get the number of lines in the buffer */
- line_count = gtk_text_buffer_get_line_count (text_view->buffer);
+ line_count = gtk_text_buffer_get_line_count (textview->buffer);
/* string with the 'last' line number */
g_snprintf (str, sizeof (str), "%d", MAX (99, line_count));
@@ -239,10 +255,10 @@ mousepad_view_expose (GtkWidget *widget,
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);
+ gtk_text_view_set_border_window_size (textview, GTK_TEXT_WINDOW_LEFT, width + 8);
/* draw a vertical line to separate the numbers and text */
- gtk_paint_vline (widget->style, window,
+ gtk_paint_vline (widget->style, event->window,
GTK_WIDGET_STATE (widget),
NULL, widget, NULL,
event->area.y,
@@ -253,7 +269,7 @@ mousepad_view_expose (GtkWidget *widget,
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);
+ gtk_text_view_get_line_yrange (textview, &iter, &y, &height);
/* include the buffer offset */
y += y_offset;
@@ -268,7 +284,7 @@ mousepad_view_expose (GtkWidget *widget,
pango_layout_set_text (layout, str, strlen (str));
/* draw the layout on the left window */
- gtk_paint_layout (widget->style, window,
+ gtk_paint_layout (widget->style, event->window,
GTK_WIDGET_STATE (widget),
FALSE, NULL, widget, NULL,
width + 2, y, layout);
@@ -313,19 +329,19 @@ mousepad_view_style_set (GtkWidget *widget,
buffer = mousepad_view_get_buffer (view);
/* remove previous selection tag */
- if (G_UNLIKELY (view->tag))
+ if (G_UNLIKELY (view->selection_tag))
{
gtk_text_buffer_get_bounds (buffer, &start, &end);
- gtk_text_buffer_remove_tag (buffer, view->tag, &start, &end);
+ gtk_text_buffer_remove_tag (buffer, view->selection_tag, &start, &end);
}
/* create the new selection tag */
- view->tag = gtk_text_buffer_create_tag (buffer, NULL,
- "background-gdk", &style->base[GTK_STATE_SELECTED],
- "foreground-gdk", &style->text[GTK_STATE_SELECTED],
- NULL);
+ view->selection_tag = gtk_text_buffer_create_tag (buffer, NULL,
+ "background-gdk", &style->base[GTK_STATE_SELECTED],
+ "foreground-gdk", &style->text[GTK_STATE_SELECTED],
+ NULL);
- /* update the tab stops */
+ /* update the tab size */
mousepad_view_set_tab_size (view, view->tab_size);
}
}
@@ -366,7 +382,7 @@ mousepad_view_key_press_event (GtkWidget *widget,
gtk_text_buffer_get_iter_at_mark (buffer, &iter, cursor);
/* get the string of tabs and spaces we're going to indent */
- string = mousepad_view_indentation_string (buffer, &iter);
+ string = mousepad_view_indent_string (buffer, &iter);
if (string != NULL)
{
@@ -437,7 +453,7 @@ mousepad_view_key_press_event (GtkWidget *widget,
/* label for the ctrl home/end events */
move_cursor:
- /* move the cursor */
+ /* move (select) or set (jump) cursor */
if (modifiers & GDK_SHIFT_MASK)
gtk_text_buffer_move_mark (buffer, cursor, &iter);
else
@@ -450,44 +466,68 @@ mousepad_view_key_press_event (GtkWidget *widget,
}
break;
+ case GDK_Delete:
+ case GDK_KP_Delete:
+ case GDK_BackSpace:
+ if (view->flags != 0)
+ {
+ goto selection_key_press;
+ }
+ break;
+
case GDK_Tab:
case GDK_KP_Tab:
case GDK_ISO_Left_Tab:
- if (mousepad_view_get_has_selection (view) && is_editable)
+ if (G_LIKELY (is_editable))
{
- /* indent the selection */
- mousepad_view_indent_selection (view, (modifiers != GDK_SHIFT_MASK), TRUE);
+ if (view->flags != 0)
+ {
+ goto selection_key_press;
+ }
+ else if (mousepad_view_get_has_selection (view))
+ {
+ /* indent the selection */
+ mousepad_view_indent_selection (view, (modifiers & GDK_SHIFT_MASK));
- return TRUE;
- }
- else if (view->insert_spaces && is_editable)
- {
- /* get the iter position of the cursor */
- cursor = gtk_text_buffer_get_insert (buffer);
- gtk_text_buffer_get_iter_at_mark (buffer, &iter, cursor);
+ return TRUE;
+ }
+ else if (view->insert_spaces)
+ {
+ /* get the iter position of the cursor */
+ cursor = gtk_text_buffer_get_insert (buffer);
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter, cursor);
- /* insert spaces */
- mousepad_view_increase_indent_iter (view, &iter, TRUE);
+ /* begin user action */
+ gtk_text_buffer_begin_user_action (buffer);
- return TRUE;
+ /* insert spaces */
+ mousepad_view_indent_increase (view, &iter);
+
+ /* end user action */
+ gtk_text_buffer_end_user_action (buffer);
+
+ return TRUE;
+ }
}
+ break;
- case GDK_BackSpace:
- case GDK_space:
- /* indent on Space unindent on Backspace or Tab */
- if (modifiers == GDK_SHIFT_MASK && mousepad_view_get_has_selection (view) && is_editable)
+ default:
+ if (view->flags != 0 && event->length > 0)
{
- /* indent the selection */
- mousepad_view_indent_selection (view, (event->keyval == GDK_space), FALSE);
+ /* label for tab and backspace */
+ selection_key_press:
+
+ /* handle a key press event for a selection */
+ mousepad_view_selection_key_press_event (view, event, modifiers);
return TRUE;
}
break;
}
- /* reset the vertical selection */
- if (G_UNLIKELY (view->marks != NULL && !event->is_modifier))
- mousepad_view_vertical_selection_clear (view);
+ /* remove the selection when no valid key combination has been pressed */
+ if (G_UNLIKELY (view->flags != 0 && event->is_modifier == FALSE))
+ mousepad_view_selection_destroy (view);
return (*GTK_WIDGET_CLASS (mousepad_view_parent_class)->key_press_event) (widget, event);
}
@@ -499,33 +539,48 @@ mousepad_view_button_press_event (GtkWidget *widget,
GdkEventButton *event)
{
MousepadView *view = MOUSEPAD_VIEW (widget);
- GtkTextView *textview;
+ GtkTextView *textview = GTK_TEXT_VIEW (widget);;
- /* cleanup vertical selection */
- if (G_UNLIKELY (view->marks != NULL))
- mousepad_view_vertical_selection_clear (view);
-
- /* 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))
+ /* work with vertical selection while ctrl is pressed */
+ if (event->state & GDK_CONTROL_MASK
+ && event->x >= 0
+ && event->y >= 0
+ && event->button == 1)
{
- /* get the textview */
- textview = GTK_TEXT_VIEW (widget);
-
/* set the vertical selection start position, inclusing textview offset */
- view->vertical_start_x = event->x + textview->xoffset;
- view->vertical_start_y = event->y + textview->yoffset;
+ view->selection_start_x = event->x + textview->xoffset;
+ view->selection_start_y = event->y + textview->yoffset;
- /* start a vertical selection timeout */
- view->vertical_timeout_id = g_timeout_add_full (G_PRIORITY_LOW, 50, mousepad_view_vertical_selection_timeout,
- view, mousepad_view_vertical_selection_timeout_destroy);
+ if (event->type == GDK_BUTTON_PRESS)
+ {
+ /* whether this is going to be a multiple selection */
+ if (view->flags != 0)
+ MOUSEPAD_SET_FLAG (view->flags, MULTIPLE);
+
+ /* enable dragging */
+ MOUSEPAD_SET_FLAG (view->flags, DRAGGING);
+
+ /* start a vertical selection timeout */
+ view->selection_timeout_id = g_timeout_add_full (G_PRIORITY_LOW, 50, mousepad_view_selection_timeout,
+ view, mousepad_view_selection_timeout_destroy);
+ }
+ else if (event->type == GDK_2BUTTON_PRESS)
+ {
+ /* whether this is going to be a multiple selection */
+ if (view->flags != 0)
+ MOUSEPAD_SET_FLAG (view->flags, MULTIPLE);
+
+ /* add the word under the cursor to the selection */
+ mousepad_view_selection_add_word (view);
+ }
return TRUE;
}
+ else if (view->flags != 0 && event->button != 3)
+ {
+ /* no drag started or continued */
+ mousepad_view_selection_destroy (view);
+ }
return (*GTK_WIDGET_CLASS (mousepad_view_parent_class)->button_press_event) (widget, event);
}
@@ -537,18 +592,22 @@ mousepad_view_button_release_event (GtkWidget *widget,
GdkEventButton *event)
{
MousepadView *view = MOUSEPAD_VIEW (widget);
-
- _mousepad_return_val_if_fail (view->marks == NULL, TRUE);
+ gboolean has_content;
/* end of a vertical selection */
- if (G_UNLIKELY (view->vertical_start_x != -1 && event->button == 1))
+ if (G_UNLIKELY (view->selection_timeout_id != 0))
{
/* stop the timeout */
- g_source_remove (view->vertical_timeout_id);
+ g_source_remove (view->selection_timeout_id);
+
+ /* check if the selection has any content */
+ has_content = MOUSEPAD_HAS_FLAG (view->flags, HAS_CONTENT);
+
+ /* prepend the selected marks */
+ mousepad_view_selection_draw (view, !has_content, TRUE);
- /* create the marks list and emit has-selection signal */
- if (G_LIKELY (mousepad_view_vertical_selection_finish (view)))
- return TRUE;
+ /* remove the dragging flag */
+ MOUSEPAD_UNSET_FLAG (view->flags, DRAGGING);
}
return (*GTK_WIDGET_CLASS (mousepad_view_parent_class)->button_release_event) (widget, event);
@@ -557,201 +616,583 @@ mousepad_view_button_release_event (GtkWidget *widget,
/**
- * Vertical Selection Functions
+ * Selection Functions
**/
static void
-mousepad_view_vertical_selection_handle (MousepadView *view,
- gboolean prepend_marks)
+mousepad_view_selection_key_press_event (MousepadView *view,
+ GdkEventKey *event,
+ guint modifiers)
+{
+ GtkTextIter start, end;
+ GSList *li;
+ GtkTextBuffer *buffer;
+ gboolean has_content = FALSE;
+
+ _mousepad_return_if_fail (view->marks != NULL);
+
+ /* get buffer */
+ buffer = mousepad_view_get_buffer (view);
+
+ /* begin user action */
+ gtk_text_buffer_begin_user_action (buffer);
+
+ /* delete the content when the selection has content, we're not
+ * in editing more or the delete key is pressed */
+ if (MOUSEPAD_HAS_FLAG (view->flags, HAS_CONTENT))
+ {
+ if (!MOUSEPAD_HAS_FLAG (view->flags, EDITING_MODE)
+ || (event->keyval == GDK_Delete || event->keyval == GDK_KP_Delete))
+ {
+ mousepad_view_selection_delete_content (view);
+ }
+ }
+
+ /* enter editing mode */
+ MOUSEPAD_SET_FLAG (view->flags, EDITING_MODE);
+
+ for (li = view->marks; li != NULL; li = li->next)
+ {
+ /* get end iter */
+ gtk_text_buffer_get_iter_at_mark (buffer, &end, li->next->data);
+
+ /* handle the events */
+ switch (event->keyval)
+ {
+ case GDK_Tab:
+ case GDK_KP_Tab:
+ case GDK_ISO_Left_Tab:
+ /* insert a (soft) tab */
+ mousepad_view_indent_increase (view, &end);
+ break;
+
+ case GDK_BackSpace:
+ /* get start iter */
+ gtk_text_buffer_get_iter_at_mark (buffer, &start, li->data);
+
+ /* only backspace when there is text inside the selection or ctrl is pressed */
+ if (!gtk_text_iter_equal (&start, &end) || modifiers == GDK_CONTROL_MASK)
+ {
+ gtk_text_buffer_backspace (buffer, &end, FALSE, TRUE);
+ }
+ break;
+
+ default:
+ /* insert the typed string */
+ gtk_text_buffer_insert (buffer, &end, event->string, event->length);
+ break;
+ }
+
+ /* get the start iter */
+ gtk_text_buffer_get_iter_at_mark (buffer, &start, li->data);
+
+ if (!gtk_text_iter_equal (&start, &end))
+ {
+ /* select the text */
+ gtk_text_buffer_apply_tag (buffer, view->selection_tag, &start, &end);
+
+ /* we have some selected text */
+ has_content = TRUE;
+ }
+
+ /* go to next element in the list */
+ li = li->next;
+ }
+
+ /* end user action */
+ gtk_text_buffer_end_user_action (buffer);
+
+ /* update selection status */
+ mousepad_view_selection_emit_has_selection (view, has_content);
+}
+
+
+
+static void
+mousepad_view_selection_delete_content (MousepadView *view)
{
GtkTextBuffer *buffer;
- GtkTextView *textview = GTK_TEXT_VIEW (view);
GtkTextIter start_iter, end_iter;
- gint y, y_begin, y_height, y_end;
- GtkTextMark *start_mark, *end_mark;
+ GSList *li;
+
+ _mousepad_return_if_fail (view->marks != NULL);
+ _mousepad_return_if_fail (MOUSEPAD_HAS_FLAG (view->flags, HAS_CONTENT));
+ _mousepad_return_if_fail (view->marks == NULL || g_slist_length (view->marks) % 2 == 0);
+
+ /* get the buffer */
+ buffer = mousepad_view_get_buffer (view);
+
+ /* begin user action */
+ gtk_text_buffer_begin_user_action (buffer);
+
+ /* remove everything between the marks */
+ for (li = view->marks; li != NULL; li = li->next)
+ {
+ /* get end iter */
+ gtk_text_buffer_get_iter_at_mark (buffer, &start_iter, li->data);
+
+ /* next element */
+ li = li->next;
+
+ /* get start iter */
+ gtk_text_buffer_get_iter_at_mark (buffer, &end_iter, li->data);
+
+ /* delete */
+ gtk_text_buffer_delete (buffer, &start_iter, &end_iter);
+ }
+
+ /* end user action */
+ gtk_text_buffer_end_user_action (buffer);
+
+ /* update selection status */
+ mousepad_view_selection_emit_has_selection (view, FALSE);
+}
+
+
+
+static void
+mousepad_view_selection_destroy (MousepadView *view)
+{
+ GtkTextBuffer *buffer;
+ GSList *li;
+ GtkTextIter start_iter, end_iter;
+
+ _mousepad_return_if_fail (view->marks != NULL);
+ _mousepad_return_if_fail (view->flags != 0);
/* get the buffer */
buffer = mousepad_view_get_buffer (view);
- /* remove the old _selection_ tags */
+ /* remove the selection tags */
gtk_text_buffer_get_bounds (buffer, &start_iter, &end_iter);
- gtk_text_buffer_remove_tag (buffer, view->tag, &start_iter, &end_iter);
+ gtk_text_buffer_remove_tag (buffer, view->selection_tag, &start_iter, &end_iter);
+
+ /* remove all the selection marks */
+ if (G_LIKELY (view->marks))
+ {
+ /* delete marks from the buffer */
+ for (li = view->marks; li != NULL; li = li->next)
+ gtk_text_buffer_delete_mark (buffer, li->data);
+
+ /* free the list */
+ g_slist_free (view->marks);
+
+ /* null */
+ view->marks = NULL;
+ }
+
+ /* reset status */
+ view->flags = 0;
+
+ /* update selection status */
+ mousepad_view_selection_emit_has_selection (view, FALSE);
+}
+
- /* get the start and end iter */
- gtk_text_view_get_iter_at_location (textview, &start_iter, 0, view->vertical_start_y);
- gtk_text_view_get_iter_at_location (textview, &end_iter, 0, view->vertical_end_y);
- /* make sure the start iter is before the end iter */
- gtk_text_iter_order (&start_iter, &end_iter);
+static void
+mousepad_view_selection_add_word (MousepadView *view)
+{
+ GtkTextBuffer *buffer;
+ GtkTextIter start_iter, end_iter;
+
+ /* get buffer */
+ buffer = mousepad_view_get_buffer (view);
+
+ /* get the iter at the start coordinate */
+ gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (view), &start_iter, view->selection_start_x, view->selection_start_y);
+
+ /* move the iter to the start of the word */
+ while (!gtk_text_iter_starts_word (&start_iter))
+ if (!gtk_text_iter_backward_char (&start_iter))
+ break;
- /* get the y coordinates we're going to walk */
- gtk_text_view_get_line_yrange (textview, &start_iter, &y_begin, &y_height);
- gtk_text_view_get_line_yrange (textview, &end_iter, &y_end, NULL);
+ /* set end iter */
+ end_iter = start_iter;
- /* make sure atleast one line is selected */
- y_end += y_height;
+ /* walk forward to end of the word */
+ while (!gtk_text_iter_ends_word (&end_iter))
+ if (!gtk_text_iter_forward_char (&end_iter))
+ break;
- /* walk */
- for (y = y_begin; y < y_end; y += y_height)
+ /* add the section when the iters are not equal */
+ if (!gtk_text_iter_equal (&start_iter, &end_iter))
{
- /* get the start and end iter in the line */
- gtk_text_view_get_iter_at_location (textview, &start_iter, view->vertical_start_x, y);
- gtk_text_view_get_iter_at_location (textview, &end_iter, view->vertical_end_x, y);
+ /* add to the list */
+ mousepad_view_selection_add_marks (view, &start_iter, &end_iter);
- /* continue when the iters are the same */
- if (G_UNLIKELY (gtk_text_iter_equal (&start_iter, &end_iter)))
- continue;
+ /* redraw */
+ mousepad_view_selection_draw (view, FALSE, FALSE);
+ }
+}
- /* apply tag */
- gtk_text_buffer_apply_tag (buffer, view->tag, &start_iter, &end_iter);
- /* prepend to list */
- if (G_UNLIKELY (prepend_marks))
- {
- /* make sure the iters are correctly sorted */
- gtk_text_iter_order (&start_iter, &end_iter);
- /* append start mark to the list */
- start_mark = gtk_text_buffer_create_mark (buffer, NULL, &start_iter, FALSE);
- view->marks = g_slist_prepend (view->marks, start_mark);
+static void
+mousepad_view_selection_cleanup_equal_marks (MousepadView *view)
+{
+ GtkTextBuffer *buffer;
+ GtkTextIter start_iter, end_iter;
+ GSList *li, *lnext;
+
+ _mousepad_return_if_fail (view->marks == NULL || g_slist_length (view->marks) % 2 == 0);
+
+ /* get buffer */
+ buffer = mousepad_view_get_buffer (view);
+
+ for (li = view->marks; li != NULL; li = lnext)
+ {
+ /* get iters */
+ gtk_text_buffer_get_iter_at_mark (buffer, &start_iter, li->data);
+ gtk_text_buffer_get_iter_at_mark (buffer, &end_iter, li->next->data);
+
+ /* get next element */
+ lnext = li->next->next;
+
+ if (gtk_text_iter_equal (&start_iter, &end_iter))
+ {
+ gtk_text_buffer_delete_mark (buffer, li->next->data);
+ view->marks = g_slist_delete_link (view->marks, li->next);
- /* append end mark to the list */
- end_mark = gtk_text_buffer_create_mark (buffer, NULL, &end_iter, FALSE);
- view->marks = g_slist_prepend (view->marks, end_mark);
+ gtk_text_buffer_delete_mark (buffer, li->data);
+ view->marks = g_slist_delete_link (view->marks, li);
}
}
}
-static gboolean
-mousepad_view_vertical_selection_finish (MousepadView *view)
+static void
+mousepad_view_selection_add_marks (MousepadView *view,
+ GtkTextIter *start_iter,
+ GtkTextIter *end_iter)
{
GtkTextBuffer *buffer;
+ GtkTextIter iter;
+ GtkTextMark *start_mark, *end_mark;
+ GSList *li, *lnext;
+ gboolean is_start = FALSE;
+ gboolean handled_start = FALSE;
+ gint compare;
+
+ _mousepad_return_if_fail (view->marks == NULL || g_slist_length (view->marks) % 2 == 0);
- /* cleanup the marks list */
- if (view->marks)
- mousepad_view_vertical_selection_free_marks (view);
+ /* make sure the iters are correctly sorted */
+ gtk_text_iter_order (start_iter, end_iter);
- /* update the highlight an prepend the marks */
- mousepad_view_vertical_selection_handle (view, TRUE);
+ /* get buffer */
+ buffer = mousepad_view_get_buffer (view);
+
+ /* create marks */
+ start_mark = gtk_text_buffer_create_mark (buffer, NULL, start_iter, TRUE);
+ end_mark = gtk_text_buffer_create_mark (buffer, NULL, end_iter, FALSE);
- if (G_LIKELY (view->marks != NULL))
+ for (li = view->marks; li != NULL; li = lnext)
{
- /* reverse the list after rebuilding */
- view->marks = g_slist_reverse (view->marks);
+ /* type of mark */
+ is_start = !is_start;
- /* get the buffer */
- buffer = mousepad_view_get_buffer (view);
+ /* determine next element in the list */
+ lnext = li->next;
- /* notify the has-selection signal */
- buffer->has_selection = TRUE;
- g_object_notify (G_OBJECT (buffer), "has-selection");
+ /* get the iter of a mark in the list */
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter, li->data);
- return TRUE;
+ /* search the insert position of the start mark */
+ if (!handled_start)
+ {
+ /* compare iters */
+ compare = gtk_text_iter_compare (&iter, start_iter);
+
+ if (compare >= 0)
+ {
+ if (compare == 0 && gtk_text_iter_equal (start_iter, end_iter))
+ {
+ /* zerro width insert on top of an existing mark, skip those */
+ gtk_text_buffer_delete_mark (buffer, start_mark);
+ gtk_text_buffer_delete_mark (buffer, end_mark);
+
+ break;
+ }
+ else if (is_start)
+ {
+ /* inserted before a start mark */
+ view->marks = g_slist_insert_before (view->marks, li, start_mark);
+ }
+ else
+ {
+ /* inserted after a start mark, remove the new one */
+ gtk_text_buffer_delete_mark (buffer, start_mark);
+ }
+
+ /* we've handled the start mark */
+ handled_start = TRUE;
+ }
+ }
+
+ /* handle the end mark */
+ if (handled_start)
+ {
+ /* check if the iter can be merged inside our selection */
+ if (gtk_text_iter_compare (&iter, end_iter) < 0)
+ {
+ /* delete the existing mark from the buffer */
+ gtk_text_buffer_delete_mark (buffer, li->data);
+
+ /* delete item from the list */
+ view->marks = g_slist_delete_link (view->marks, li);
+ }
+ else
+ {
+ /* handle the end mark */
+ if (is_start)
+ view->marks = g_slist_insert_before (view->marks, li, end_mark);
+ else
+ gtk_text_buffer_delete_mark (buffer, end_mark);
+
+ break;
+ }
+ }
}
- return FALSE;
+ /* reached end of list */
+ if (li == NULL)
+ {
+ /* still not start mark added, append it to the list */
+ if (!handled_start)
+ view->marks = g_slist_append (view->marks, start_mark);
+
+ /* append end mark */
+ view->marks = g_slist_append (view->marks, end_mark);
+ }
}
static void
-mousepad_view_vertical_selection_free_marks (MousepadView *view)
+mousepad_view_selection_draw (MousepadView *view,
+ gboolean draw_rectangles,
+ gboolean add_marks)
{
- GSList *li;
GtkTextBuffer *buffer;
+ GtkTextView *textview = GTK_TEXT_VIEW (view);
+ GtkTextIter start_iter, end_iter;
+ gint y, y_begin, y_height, y_end;
+ gboolean has_content = FALSE;
+ GdkRectangle rect;
+ GdkWindow *window;
+ GSList *li;
+
+ _mousepad_return_if_fail (view->marks == NULL || g_slist_length (view->marks) % 2 == 0);
/* get the buffer */
buffer = mousepad_view_get_buffer (view);
- /* remove all the marks */
+ /* get the drawable window */
+ window = gtk_text_view_get_window (textview, GTK_TEXT_WINDOW_TEXT);
+
+ if (MOUSEPAD_HAS_FLAG (view->flags, HAS_CONTENT))
+ {
+ /* remove the old tags */
+ gtk_text_buffer_get_bounds (buffer, &start_iter, &end_iter);
+ gtk_text_buffer_remove_tag (buffer, view->selection_tag, &start_iter, &end_iter);
+ }
+ else
+ {
+ /* invalidate the window so no retangles are left */
+ gtk_widget_queue_draw (GTK_WIDGET (view));
+ }
+
+ /* if the selection already contains marks, show them first */
for (li = view->marks; li != NULL; li = li->next)
- gtk_text_buffer_delete_mark (buffer, li->data);
+ {
+ /* get the end iter */
+ gtk_text_buffer_get_iter_at_mark (buffer, &start_iter, li->data);
- /* free the list */
- g_slist_free (view->marks);
+ /* next element in the list */
+ li = li->next;
- /* null */
- view->marks = NULL;
+ if (draw_rectangles)
+ {
+ /* get the iter location and size */
+ gtk_text_view_get_iter_location (textview, &start_iter, &rect);
+
+ /* draw a thin retangle in front of the iter */
+ gdk_draw_rectangle (GDK_DRAWABLE (window),
+ GTK_WIDGET (view)->style->base_gc[GTK_STATE_SELECTED],
+ TRUE, rect.x - textview->xoffset, rect.y - textview->yoffset,
+ MOUSEPAD_VIEW_RECT_WIDTH, rect.height);
+ }
+ else
+ {
+ /* get the start iter */
+ gtk_text_buffer_get_iter_at_mark (buffer, &end_iter, li->data);
+
+ /* check if the iters are not equal */
+ if (!gtk_text_iter_equal (&start_iter, &end_iter))
+ {
+ /* apply tag */
+ gtk_text_buffer_apply_tag (buffer, view->selection_tag, &start_iter, &end_iter);
+
+ /* this selection has atleast some content */
+ has_content = TRUE;
+ }
+ }
+ }
+
+ /* when we're still dragging, show the selection in the area */
+ if (MOUSEPAD_HAS_FLAG (view->flags, DRAGGING))
+ {
+ /* get the start and end iter */
+ gtk_text_view_get_iter_at_location (textview, &start_iter, 0, view->selection_start_y);
+ gtk_text_view_get_iter_at_location (textview, &end_iter, 0, view->selection_end_y);
+
+ /* make sure the start iter is before the end iter */
+ gtk_text_iter_order (&start_iter, &end_iter);
+
+ /* get the y coordinates we're going to walk */
+ gtk_text_view_get_line_yrange (textview, &start_iter, &y_begin, &y_height);
+ gtk_text_view_get_line_yrange (textview, &end_iter, &y_end, NULL);
+
+ /* make sure atleast one line is selected */
+ y_end += y_height;
+
+ /* walk the lines inside the selection area */
+ for (y = y_begin; y < y_end; y += y_height)
+ {
+ /* get the start and end iter in the line */
+ gtk_text_view_get_iter_at_location (textview, &start_iter, view->selection_start_x, y);
+ gtk_text_view_get_iter_at_location (textview, &end_iter, view->selection_end_x, y);
+
+ if (draw_rectangles)
+ {
+ /* get the iter location and size */
+ gtk_text_view_get_iter_location (textview, &end_iter, &rect);
+
+ /* draw a thin retangle in front of the iter */
+ gdk_draw_rectangle (GDK_DRAWABLE (window),
+ GTK_WIDGET (view)->style->base_gc[GTK_STATE_SELECTED],
+ TRUE, rect.x - textview->xoffset, rect.y - textview->yoffset,
+ MOUSEPAD_VIEW_RECT_WIDTH, rect.height);
+ }
+ else if (!gtk_text_iter_equal (&start_iter, &end_iter))
+ {
+ /* apply tag */
+ gtk_text_buffer_apply_tag (buffer, view->selection_tag, &start_iter, &end_iter);
+
+ /* this selection has atleast some content */
+ has_content = TRUE;
+ }
+ else
+ {
+ /* when both of the conditions above were not met, we'll
+ * never added them to the marks list */
+ continue;
+ }
+
+ /* prepend to list */
+ if (add_marks)
+ {
+ /* add the marks */
+ mousepad_view_selection_add_marks (view, &start_iter, &end_iter);
+ }
+ }
+ }
+
+ /* update status when marks have been added */
+ if (add_marks && has_content && MOUSEPAD_HAS_FLAG (view->flags, MULTIPLE))
+ mousepad_view_selection_cleanup_equal_marks (view);
+
+ /* there is something in the list, prevent 0 flag */
+ if (view->marks != NULL)
+ MOUSEPAD_SET_FLAG (view->flags, HAS_SELECTION);
+
+ /* update selection status */
+ mousepad_view_selection_emit_has_selection (view, has_content);
}
+
static void
-mousepad_view_vertical_selection_clear (MousepadView *view)
+mousepad_view_selection_emit_has_selection (MousepadView *view,
+ gboolean has_content)
{
GtkTextBuffer *buffer;
- GtkTextIter start, end;
-
- _mousepad_return_if_fail (view->marks != NULL);
- /* get the buffer */
- buffer = mousepad_view_get_buffer (view);
+ /* jump when there is not selection */
+ if (view->flags == 0)
+ goto emit_has_selection;
- /* no selection */
- buffer->has_selection = FALSE;
- g_object_notify (G_OBJECT (buffer), "has-selection");
+ /* check if we need to emit */
+ if (has_content != MOUSEPAD_HAS_FLAG (view->flags, HAS_CONTENT))
+ {
+ /* update flag */
+ if (has_content)
+ MOUSEPAD_SET_FLAG (view->flags, HAS_CONTENT);
+ else
+ MOUSEPAD_UNSET_FLAG (view->flags, HAS_CONTENT);
- /* remove the selections */
- gtk_text_buffer_get_bounds (buffer, &start, &end);
- gtk_text_buffer_remove_tag (buffer, view->tag, &start, &end);
+ /* label */
+ emit_has_selection:
- /* reset the coordinates */
- view->vertical_start_x = view->vertical_start_y =
- view->vertical_end_x = view->vertical_end_y = -1;
+ /* get the buffer */
+ buffer = mousepad_view_get_buffer (view);
- /* cleanup marks */
- mousepad_view_vertical_selection_free_marks (view);
+ buffer->has_selection = has_content;
+ g_object_notify (G_OBJECT (buffer), "has-selection");
+ }
}
static gboolean
-mousepad_view_vertical_selection_timeout (gpointer user_data)
+mousepad_view_selection_timeout (gpointer user_data)
{
MousepadView *view = MOUSEPAD_VIEW (user_data);
GtkTextView *textview = GTK_TEXT_VIEW (view);
- gint vertical_start_x, vertical_start_y;
- gint vertical_end_x, vertical_end_y;
+ gint pointer_x, pointer_y;
+ gint selection_start_x, selection_start_y;
GtkTextBuffer *buffer;
GtkTextIter iter;
+ _mousepad_return_val_if_fail (MOUSEPAD_HAS_FLAG (view->flags, DRAGGING), FALSE);
+
GDK_THREADS_ENTER ();
/* get the cursor coordinate */
gdk_window_get_pointer (gtk_text_view_get_window (textview, GTK_TEXT_WINDOW_TEXT),
- &vertical_end_x, &vertical_end_y, NULL);
+ &pointer_x, &pointer_y, NULL);
- /* convert to positive values and buffer coordinates */
- vertical_end_x = MAX (vertical_end_x, 0) + textview->xoffset;
- vertical_end_y = MAX (vertical_end_y, 0) + textview->yoffset;
+ /* convert to positive values and add buffer offset */
+ pointer_x = MAX (pointer_x, 0) + textview->xoffset;
+ pointer_y = MAX (pointer_y, 0) + textview->yoffset;
/* only update the selection when the cursor moved */
- if (view->vertical_end_x != vertical_end_x || view->vertical_end_y != vertical_end_y)
+ if (view->selection_end_x != pointer_x || view->selection_end_y != pointer_y)
{
/* update the end coordinates */
- view->vertical_end_x = vertical_end_x;
- view->vertical_end_y = vertical_end_y;
+ view->selection_end_x = pointer_x;
+ view->selection_end_y = pointer_y;
/* backup start coordinates */
- vertical_start_x = view->vertical_start_x;
- vertical_start_y = view->vertical_start_y;
+ selection_start_x = view->selection_start_x;
+ selection_start_y = view->selection_start_y;
- /* during the timeout, we're only interested in the visible selection */
- view->vertical_start_x = MAX (view->vertical_start_x, textview->xoffset);
- view->vertical_start_y = MAX (view->vertical_start_y, textview->yoffset);
+ /* only draw the visible selection during timeout */
+ view->selection_start_x = MAX (view->selection_start_x, textview->xoffset);
+ view->selection_start_y = MAX (view->selection_start_y, textview->yoffset);
- /* update the selection */
- mousepad_view_vertical_selection_handle (view, FALSE);
+ /* show the selection */
+ mousepad_view_selection_draw (view, FALSE, FALSE);
/* restore the start coordinates */
- view->vertical_start_x = vertical_start_x;
- view->vertical_start_y = vertical_start_y;
+ view->selection_start_x = selection_start_x;
+ view->selection_start_y = selection_start_y;
/* get the text buffer */
buffer = mousepad_view_get_buffer (view);
/* put the cursor under the mouse pointer */
- gtk_text_view_get_iter_at_location (textview, &iter, vertical_end_x, vertical_end_y);
+ gtk_text_view_get_iter_at_location (textview, &iter, pointer_x, pointer_y);
gtk_text_buffer_place_cursor (buffer, &iter);
/* put cursor on screen */
@@ -767,19 +1208,76 @@ mousepad_view_vertical_selection_timeout (gpointer user_data)
static void
-mousepad_view_vertical_selection_timeout_destroy (gpointer user_data)
+mousepad_view_selection_timeout_destroy (gpointer user_data)
{
- MOUSEPAD_VIEW (user_data)->vertical_timeout_id = 0;
+ MOUSEPAD_VIEW (user_data)->selection_timeout_id = 0;
}
+
+static void
+mousepad_view_selection_clipboard (MousepadView *view,
+ GtkClipboard *clipboard)
+{
+ GString *string;
+ GtkTextBuffer *buffer;
+ gint line, previous_line = -1;
+ gchar *slice;
+ GtkTextIter start_iter, end_iter;
+ GSList *li;
+
+ _mousepad_return_if_fail (view->marks == NULL || g_slist_length (view->marks) % 2 == 0);
+
+ /* create string with some size so we don't have to realloc a zillon times */
+ string = g_string_sized_new (1024);
+
+ /* get the buffer */
+ buffer = mousepad_view_get_buffer (view);
+
+ for (li = view->marks; li != NULL; li = li->next)
+ {
+ /* get the iters */
+ gtk_text_buffer_get_iter_at_mark (buffer, &start_iter, li->data);
+ li = li->next;
+ gtk_text_buffer_get_iter_at_mark (buffer, &end_iter, li->data);
+
+ /* get the line number */
+ line = gtk_text_iter_get_line (&start_iter);
+
+ /* fix the number of new lines between the parts */
+ if (previous_line == -1)
+ previous_line = line;
+ else if (!MOUSEPAD_HAS_FLAG (view->flags, MULTIPLE))
+ for (; previous_line < line; previous_line++)
+ string = g_string_append_c (string, '\n');
+ else
+ string = g_string_append_c (string, '\n');
+
+ /* get the text slice */
+ slice = gtk_text_buffer_get_slice (buffer, &start_iter, &end_iter, TRUE);
+
+ /* append the slice to the string */
+ string = g_string_append (string, slice);
+
+ /* cleanup */
+ g_free (slice);
+ }
+
+ /* set the clipboard text */
+ gtk_clipboard_set_text (clipboard, string->str, string->len);
+
+ /* free the string */
+ g_string_free (string, TRUE);
+}
+
+
+
/**
* Indentation Functions
**/
static void
-mousepad_view_increase_indent_iter (MousepadView *view,
- GtkTextIter *iter,
- gboolean tab)
+mousepad_view_indent_increase (MousepadView *view,
+ GtkTextIter *iter)
{
gchar *string;
gint offset, length, inline_len;
@@ -788,7 +1286,7 @@ mousepad_view_increase_indent_iter (MousepadView *view,
/* get the buffer */
buffer = mousepad_view_get_buffer (view);
- if (view->insert_spaces && tab)
+ if (view->insert_spaces)
{
/* get the offset */
offset = mousepad_util_get_real_line_offset (iter, view->tab_size);
@@ -813,67 +1311,39 @@ mousepad_view_increase_indent_iter (MousepadView *view,
else
{
/* insert a tab or a space */
- gtk_text_buffer_insert (buffer, iter, tab ? "\t" : " ", -1);
+ gtk_text_buffer_insert (buffer, iter, "\t", -1);
}
}
static void
-mousepad_view_decrease_indent_iter (MousepadView *view,
- GtkTextIter *iter,
- gboolean tab)
+mousepad_view_indent_decrease (MousepadView *view,
+ GtkTextIter *iter)
{
GtkTextBuffer *buffer;
GtkTextIter start, end;
- gint columns = 1;
+ gint columns = view->tab_size;
gunichar c;
/* set iters */
start = end = *iter;
- if (gtk_text_iter_starts_line (iter))
+ /* walk until we've removed enough columns */
+ while (columns > 0)
{
- /* set number of columns */
- columns = tab ? view->tab_size : 1;
-
- /* walk until we've removed enough columns */
- while (columns > 0)
- {
- /* get the character */
- c = gtk_text_iter_get_char (&end);
-
- if (c == '\t')
- columns -= view->tab_size;
- else if (c == ' ')
- columns--;
- else
- break;
-
- /* go to the next character */
- gtk_text_iter_forward_char (&end);
- }
- }
- else
- {
- /* walk backwards until we've removed enough columns */
- while (columns > 0
- && !gtk_text_iter_starts_line (iter)
- && gtk_text_iter_backward_char (iter))
- {
- /* get character */
- c = gtk_text_iter_get_char (iter);
-
- if (c == '\t')
- columns -= view->tab_size;
- else if (c == ' ')
- columns--;
- else
- break;
-
- /* update the start iter */
- start = *iter;
- }
+ /* get the character */
+ c = gtk_text_iter_get_char (&end);
+
+ if (c == '\t')
+ columns -= view->tab_size;
+ else if (c == ' ')
+ columns--;
+ else
+ break;
+
+ /* go to the next character */
+ gtk_text_iter_forward_char (&end);
}
/* unindent the selection when the iters are not equal */
@@ -891,52 +1361,28 @@ mousepad_view_decrease_indent_iter (MousepadView *view,
static void
mousepad_view_indent_selection (MousepadView *view,
- gboolean increase,
- gboolean tab)
+ gboolean increase)
{
GtkTextBuffer *buffer;
GtkTextIter start_iter, end_iter;
- GSList *li;
gint start_line, end_line;
gint i;
/* get the textview buffer */
buffer = mousepad_view_get_buffer (view);
- /* begin a user action */
- gtk_text_buffer_begin_user_action (buffer);
-
- if (view->marks != NULL)
+ if (gtk_text_buffer_get_selection_bounds (buffer, &start_iter, &end_iter))
{
- for (li = view->marks; li != NULL; li = li->next)
- {
- /* get the iter */
- gtk_text_buffer_get_iter_at_mark (buffer, &start_iter, li->data);
-
- /* only indent when the iter does not start a line */
- if (!gtk_text_iter_starts_line (&start_iter))
- {
- /* increase or decrease the indentation */
- if (increase)
- mousepad_view_increase_indent_iter (view, &start_iter, tab);
- else
- mousepad_view_decrease_indent_iter (view, &start_iter, tab);
- }
+ /* begin a user action */
+ gtk_text_buffer_begin_user_action (buffer);
- /* skip end mark in the list (always pairs of two) */
- li = li->next;
- }
- }
- else if (gtk_text_buffer_get_selection_bounds (buffer, &start_iter, &end_iter))
- {
/* get start and end line */
start_line = gtk_text_iter_get_line (&start_iter);
end_line = gtk_text_iter_get_line (&end_iter);
/* only change indentation when an entire line is selected or multiple lines */
if (start_line != end_line
- || (gtk_text_iter_starts_line (&start_iter)
- && gtk_text_iter_ends_line (&end_iter)))
+ || (gtk_text_iter_starts_line (&start_iter) && gtk_text_iter_ends_line (&end_iter)))
{
/* change indentation of each line */
for (i = start_line; i <= end_line; i++)
@@ -950,25 +1396,25 @@ mousepad_view_indent_selection (MousepadView *view,
/* increase or decrease the indentation */
if (increase)
- mousepad_view_increase_indent_iter (view, &start_iter, tab);
+ mousepad_view_indent_increase (view, &start_iter);
else
- mousepad_view_decrease_indent_iter (view, &start_iter, tab);
+ mousepad_view_indent_decrease (view, &start_iter);
}
}
- }
- /* end user action */
- gtk_text_buffer_end_user_action (buffer);
+ /* end user action */
+ gtk_text_buffer_end_user_action (buffer);
- /* put cursor on screen */
- mousepad_view_put_cursor_on_screen (view);
+ /* put cursor on screen */
+ mousepad_view_put_cursor_on_screen (view);
+ }
}
static gchar *
-mousepad_view_indentation_string (GtkTextBuffer *buffer,
- const GtkTextIter *iter)
+mousepad_view_indent_string (GtkTextBuffer *buffer,
+ const GtkTextIter *iter)
{
GtkTextIter start, end;
gint line;
@@ -1027,89 +1473,6 @@ mousepad_view_calculate_layout_width (GtkWidget *widget,
-static void
-mousepad_view_clipboard (MousepadView *view,
- GtkClipboard *clipboard,
- gboolean remove)
-{
- GString *string = NULL;
- GSList *li;
- gint ln, previous_ln = -1;
- gchar *slice;
- GtkTextBuffer *buffer;
- GtkTextMark *mark_start, *mark_end;
- GtkTextIter iter_start, iter_end;
-
- /* get the buffer */
- buffer = mousepad_view_get_buffer (view);
-
- /* create a new string */
- if (clipboard)
- string = g_string_new (NULL);
-
- /* begin user action */
- gtk_text_buffer_begin_user_action (buffer);
-
- /* walk through the marks */
- for (li = view->marks; li != NULL; li = li->next)
- {
- /* get the start mark */
- mark_start = li->data;
-
- /* jump to next item in the list */
- li = li->next;
-
- /* get end mark */
- mark_end = li->data;
-
- /* 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);
-
- /* only handle the selection when there is clipbaord */
- if (clipboard)
- {
- /* the line number of the iter */
- ln = gtk_text_iter_get_line (&iter_start);
-
- /* fix the number of new lines between the parts */
- if (previous_ln == -1)
- previous_ln = ln;
- else
- for (; previous_ln < ln; previous_ln++)
- string = g_string_append_c (string, '\n');
-
- /* get the text slice */
- slice = gtk_text_buffer_get_slice (buffer, &iter_start, &iter_end, TRUE);
-
- /* append the slice to the string */
- string = g_string_append (string, slice);
-
- /* cleanup */
- g_free (slice);
- }
-
- /* delete the text between the iters if requested */
- if (remove)
- gtk_text_buffer_delete (buffer, &iter_start, &iter_end);
- }
-
- /* end user action */
- gtk_text_buffer_end_user_action (buffer);
-
- /* set the clipboard text */
- if (G_LIKELY (clipboard))
- {
- /* set the clipboard text */
- gtk_clipboard_set_text (clipboard, string->str, string->len);
-
- /* free the string */
- g_string_free (string, TRUE);
- }
-}
-
-
-
void
mousepad_view_put_cursor_on_screen (MousepadView *view)
{
@@ -1140,13 +1503,16 @@ mousepad_view_clipboard_cut (MousepadView *view)
/* get the clipboard */
clipboard = gtk_widget_get_clipboard (GTK_WIDGET (view), GDK_SELECTION_CLIPBOARD);
- if (view->marks)
+ if (view->flags != 0)
{
- /* call the clipboard function with remove bool */
- mousepad_view_clipboard (view, clipboard, TRUE);
+ /* copy to clipboard */
+ mousepad_view_selection_clipboard (view, clipboard);
/* cleanup the vertical selection */
- mousepad_view_vertical_selection_clear (view);
+ mousepad_view_selection_delete_content (view);
+
+ /* destroy selection */
+ mousepad_view_selection_destroy (view);
}
else
{
@@ -1175,10 +1541,11 @@ mousepad_view_clipboard_copy (MousepadView *view)
/* get the clipboard */
clipboard = gtk_widget_get_clipboard (GTK_WIDGET (view), GDK_SELECTION_CLIPBOARD);
- if (view->marks)
+
+ if (view->flags != 0)
{
- /* call the clipboard function */
- mousepad_view_clipboard (view, clipboard, FALSE);
+ /* copy selection to clipboard */
+ mousepad_view_selection_clipboard (view, clipboard);
}
else
{
@@ -1294,13 +1661,13 @@ mousepad_view_delete_selection (MousepadView *view)
_mousepad_return_if_fail (MOUSEPAD_IS_VIEW (view));
_mousepad_return_if_fail (mousepad_view_get_has_selection (view));
- if (view->marks)
+ if (view->flags != 0)
{
- /* remove the text in the vertical selection */
- mousepad_view_clipboard (view, NULL, TRUE);
+ /* remove the text in our selection */
+ mousepad_view_selection_delete_content (view);
/* cleanup the vertical selection */
- mousepad_view_vertical_selection_clear (view);
+ mousepad_view_selection_destroy (view);
}
else
{
@@ -1325,9 +1692,9 @@ mousepad_view_select_all (MousepadView *view)
_mousepad_return_if_fail (MOUSEPAD_IS_VIEW (view));
- /* cleanup vertical selection */
- if (view->marks)
- mousepad_view_vertical_selection_clear (view);
+ /* cleanup our selection */
+ if (view->flags != 0)
+ mousepad_view_selection_destroy (view);
/* get buffer */
buffer = mousepad_view_get_buffer (view);
@@ -1428,7 +1795,7 @@ mousepad_view_get_has_selection (MousepadView *view)
_mousepad_return_val_if_fail (MOUSEPAD_IS_VIEW (view), FALSE);
/* we have a vertical selection */
- if (view->marks != NULL)
+ if (view->flags != 0)
return TRUE;
/* get the text buffer */
diff --git a/mousepad/mousepad-view.h b/mousepad/mousepad-view.h
index 8ddff04..f29a671 100644
--- a/mousepad/mousepad-view.h
+++ b/mousepad/mousepad-view.h
@@ -29,6 +29,7 @@ G_BEGIN_DECLS
typedef struct _MousepadViewClass MousepadViewClass;
typedef struct _MousepadView MousepadView;
+typedef enum _MousepadViewFlags MousepadViewFlags;
GType mousepad_view_get_type (void) G_GNUC_CONST;
diff --git a/mousepad/mousepad-window-ui.xml b/mousepad/mousepad-window-ui.xml
index 6c4e6b8..efbf935 100644
--- a/mousepad/mousepad-window-ui.xml
+++ b/mousepad/mousepad-window-ui.xml
@@ -108,4 +108,17 @@
<menuitem action="detach-tab" />
<menuitem action="close-tab" />
</popup>
+
+ <popup action="textview-menu">
+ <menuitem action="undo" />
+ <menuitem action="redo" />
+ <separator />
+ <menuitem action="cut" />
+ <menuitem action="copy" />
+ <menuitem action="paste" />
+ <menuitem action="paste-column" />
+ <menuitem action="delete" />
+ <separator />
+ <menuitem action="select-all" />
+ </popup>
</ui>
diff --git a/mousepad/mousepad-window.c b/mousepad/mousepad-window.c
index 88d985c..3bab8a5 100644
--- a/mousepad/mousepad-window.c
+++ b/mousepad/mousepad-window.c
@@ -156,6 +156,11 @@ static void mousepad_window_can_redo (MousepadW
/* menu functions */
static void mousepad_window_menu_tab_sizes (MousepadWindow *window);
static void mousepad_window_menu_tab_sizes_update (MousepadWindow *window);
+static void mousepad_window_menu_textview_deactivate (GtkWidget *menu,
+ GtkTextView *textview);
+static void mousepad_window_menu_textview_popup (GtkTextView *textview,
+ GtkMenu *old_menu,
+ MousepadWindow *window);
static void mousepad_window_update_actions (MousepadWindow *window);
static gboolean mousepad_window_update_gomenu_idle (gpointer user_data);
static void mousepad_window_update_gomenu_idle_destroy (gpointer user_data);
@@ -1226,6 +1231,7 @@ mousepad_window_notebook_added (GtkNotebook *notebook,
g_signal_connect_swapped (G_OBJECT (document->undo), "can-undo", G_CALLBACK (mousepad_window_can_undo), window);
g_signal_connect_swapped (G_OBJECT (document->undo), "can-redo", G_CALLBACK (mousepad_window_can_redo), window);
g_signal_connect_swapped (G_OBJECT (document->buffer), "modified-changed", G_CALLBACK (mousepad_window_modified_changed), window);
+ g_signal_connect (G_OBJECT (document->textview), "populate-popup", G_CALLBACK (mousepad_window_menu_textview_popup), window);
/* get the number of pages */
npages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (window->notebook));
@@ -1271,6 +1277,7 @@ mousepad_window_notebook_removed (GtkNotebook *notebook,
g_signal_handlers_disconnect_by_func (G_OBJECT (document->undo), mousepad_window_can_undo, window);
g_signal_handlers_disconnect_by_func (G_OBJECT (document->undo), mousepad_window_can_redo, window);
g_signal_handlers_disconnect_by_func (G_OBJECT (document->buffer), mousepad_window_modified_changed, window);
+ g_signal_handlers_disconnect_by_func (G_OBJECT (document->textview), mousepad_window_menu_textview_popup, window);
/* unset the go menu item (part of the old window) */
g_object_set_data (G_OBJECT (page), I_("navigation-menu-action"), NULL);
@@ -1642,6 +1649,52 @@ mousepad_window_menu_tab_sizes_update (MousepadWindow *window)
+static void
+mousepad_window_menu_textview_deactivate (GtkWidget *menu,
+ GtkTextView *textview)
+{
+ _mousepad_return_if_fail (GTK_IS_TEXT_VIEW (textview));
+ _mousepad_return_if_fail (textview->popup_menu == menu);
+
+ /* disconnect this signal */
+ g_signal_handlers_disconnect_by_func (G_OBJECT (menu), mousepad_window_menu_textview_deactivate, textview);
+
+ /* unset the popup menu since your menu is owned by the ui manager */
+ GTK_TEXT_VIEW (textview)->popup_menu = NULL;
+}
+
+
+
+static void
+mousepad_window_menu_textview_popup (GtkTextView *textview,
+ GtkMenu *old_menu,
+ MousepadWindow *window)
+{
+ GtkWidget *menu;
+
+ _mousepad_return_if_fail (GTK_WIDGET (old_menu) == textview->popup_menu);
+ _mousepad_return_if_fail (GTK_IS_TEXT_VIEW (textview));
+ _mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+ _mousepad_return_if_fail (GTK_IS_MENU (textview->popup_menu));
+
+ /* destroy origional menu */
+ gtk_widget_destroy (textview->popup_menu);
+
+ /* get the textview menu */
+ menu = gtk_ui_manager_get_widget (window->ui_manager, "/textview-menu");
+
+ /* connect signal */
+ g_signal_connect (G_OBJECT (menu), "deactivate", G_CALLBACK (mousepad_window_menu_textview_deactivate), textview);
+
+ /* set screen */
+ gtk_menu_set_screen (GTK_MENU (menu), gtk_widget_get_screen (GTK_WIDGET (textview)));
+
+ /* set ours */
+ textview->popup_menu = menu;
+}
+
+
static void
mousepad_window_update_actions (MousepadWindow *window)
More information about the Xfce4-commits
mailing list