Updating branch refs/heads/master to d1075d569a475bc2e16dfaab0209e1e4d0c8dec0 (commit) from 1d7bef769029aae39ddcecb6c529177393363dcf (commit)
commit d1075d569a475bc2e16dfaab0209e1e4d0c8dec0 Author: Nick Schermer <n...@xfce.org> Date: Mon Dec 10 19:36:58 2007 +0000 * mousepad/mousepad-window.c: Use the switch-page signal instead of a notify on the page property. * mousepad/mousepad-*: Remove deprecated tooltip api when compiled with Gtk+ >= 2.12. * mousepad/mousepad-replace-dialog.c: Connect tab switch signal for updating the dialog status when switching tabs. * mousepad/mousepad-undo.c: Fix issues with the undo manager. It now works with a points system (chars: 1pt, space/tab: 10pts, new line: 25pts). A step contains 30pts, whole words and spaces are merged. This way the undo steps feel more consistent. Properly keep the number of visible undo steps < 100. Store document save point in the undo manager, when you undo to this points the document will not be modified, but the history is not erased either when saving. * mousepad/Makefile.am: Add DGTK_DISABLE_DEPRECATED and DGDK_DISABLE_DEPRECATED. (Old svn revision: 26454) ChangeLog | 20 ++ mousepad/Makefile.am | 2 + mousepad/mousepad-document.c | 8 +- mousepad/mousepad-private.h | 7 + mousepad/mousepad-replace-dialog.c | 30 ++- mousepad/mousepad-replace-dialog.h | 2 + mousepad/mousepad-statusbar.c | 2 +- mousepad/mousepad-undo.c | 621 +++++++++++++++++++++--------------- mousepad/mousepad-undo.h | 2 + mousepad/mousepad-util.c | 4 +- mousepad/mousepad-util.h | 2 + mousepad/mousepad-window.c | 110 ++++--- 12 files changed, 500 insertions(+), 310 deletions(-) diff --git a/ChangeLog b/ChangeLog index 2c1a270..dbc8aa4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,25 @@ 2007-12-08 Nick Schermer <n...@xfce.org> + * mousepad/mousepad-window.c: Use the switch-page signal instead + of a notify on the page property. + * mousepad/mousepad-*: Remove deprecated tooltip api when compiled + with Gtk+ >= 2.12. + * mousepad/mousepad-replace-dialog.c: Connect tab switch signal for + updating the dialog status when switching tabs. + * mousepad/mousepad-undo.c: Fix issues with the undo manager. It + now works with a points system (chars: 1pt, space/tab: 10pts, + new line: 25pts). A step contains 30pts, whole words and spaces + are merged. This way the undo steps feel more consistent. + Properly keep the number of visible undo steps < 100. + Store document save point in the undo manager, when you undo to + this points the document will not be modified, but the history + is not erased either when saving. + * mousepad/Makefile.am: Add DGTK_DISABLE_DEPRECATED and + DGDK_DISABLE_DEPRECATED. + + +2007-12-08 Nick Schermer <n...@xfce.org> + * mousepad/mousepad-{dialog,window}.c: Show save as button in question dialog for readonly documents. Also add the modified readonly documents to the save-as queue when running save all. diff --git a/mousepad/Makefile.am b/mousepad/Makefile.am index 7e97461..1d1b787 100644 --- a/mousepad/Makefile.am +++ b/mousepad/Makefile.am @@ -11,6 +11,8 @@ INCLUDES = \ -DLIBEXECDIR=\"$(libexecdir)\" \ -DPACKAGE_LOCALE_DIR=\"$(localedir)\" \ -DG_DISABLE_DEPRECATED \ + -DGTK_DISABLE_DEPRECATED \ + -DGDK_DISABLE_DEPRECATED \ $(PLATFORM_CPPFLAGS) bin_PROGRAMS = \ diff --git a/mousepad/mousepad-document.c b/mousepad/mousepad-document.c index 9dce17c..f2d2b68 100644 --- a/mousepad/mousepad-document.c +++ b/mousepad/mousepad-document.c @@ -422,7 +422,7 @@ mousepad_document_filename_changed (MousepadDocument *document, gtk_label_set_text (GTK_LABEL (document->priv->label), utf8_basename); /* set the tab tooltip */ - mousepad_util_set_tooltip (document->priv->ebox, utf8_filename); + mousepad_widget_set_tooltip_text (document->priv->ebox, utf8_filename); /* update label color */ mousepad_document_label_color (document); @@ -545,7 +545,7 @@ mousepad_document_get_tab_label (MousepadDocument *document) /* the ebox */ document->priv->ebox = g_object_new (GTK_TYPE_EVENT_BOX, "border-width", 2, "visible-window", FALSE, NULL); gtk_box_pack_start (GTK_BOX (hbox), document->priv->ebox, TRUE, TRUE, 0); - mousepad_util_set_tooltip (document->priv->ebox, document->priv->utf8_filename); + mousepad_widget_set_tooltip_text (document->priv->ebox, document->priv->utf8_filename); gtk_widget_show (document->priv->ebox); /* create the label */ @@ -566,10 +566,10 @@ mousepad_document_get_tab_label (MousepadDocument *document) style = gtk_rc_style_new (); style->xthickness = style->ythickness = 0; gtk_widget_modify_style (button, style); - gtk_rc_style_unref (style); + g_object_unref (G_OBJECT (style)); /* pack button, add signal and tooltip */ - mousepad_util_set_tooltip (button, _("Close this tab")); + mousepad_widget_set_tooltip_text (button, _("Close this tab")); gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0); g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (mousepad_document_tab_button_clicked), document); gtk_widget_show (button); diff --git a/mousepad/mousepad-private.h b/mousepad/mousepad-private.h index 8a9fa77..c6d6730 100644 --- a/mousepad/mousepad-private.h +++ b/mousepad/mousepad-private.h @@ -95,6 +95,13 @@ G_BEGIN_DECLS #define G_UNLIKELY(expr) (expr) #endif +/* tooltip api */ +#if GTK_CHECK_VERSION (2,12,0) +#define mousepad_widget_set_tooltip_text(widget,text) (gtk_widget_set_tooltip_text (widget, text)) +#else +#define mousepad_widget_set_tooltip_text(widget,text) (mousepad_util_set_tooltip (widget, text)) +#endif + G_END_DECLS #endif /* !__MOUSEPAD_PRIVATE_H__ */ diff --git a/mousepad/mousepad-replace-dialog.c b/mousepad/mousepad-replace-dialog.c index 9057766..b9827d5 100644 --- a/mousepad/mousepad-replace-dialog.c +++ b/mousepad/mousepad-replace-dialog.c @@ -355,7 +355,7 @@ mousepad_replace_dialog_response (GtkWidget *widget, MousepadReplaceDialog *dialog = MOUSEPAD_REPLACE_DIALOG (widget); gint matches; const gchar *search_str, *replace_str; - gchar *hits_str; + gchar *message; /* close dialog */ if (response_id == MOUSEPAD_RESPONSE_CLOSE) @@ -456,15 +456,19 @@ mousepad_replace_dialog_response (GtkWidget *widget, /* emit the signal */ g_signal_emit (G_OBJECT (dialog), dialog_signals[SEARCH], 0, flags, search_str, replace_str, &matches); - /* set search widget result */ + /* reset counter */ + if (response_id == MOUSEPAD_RESPONSE_REPLACE && dialog->replace_all) + matches = 0; + + /* update entry color */ mousepad_util_entry_error (dialog->search_entry, matches == 0); - /* update hits counter */ + /* update counter */ if (dialog->replace_all) { - hits_str = g_strdup_printf ("%d %s", matches, matches == 1 ? _("occurence") : _("occurences")); - gtk_label_set_markup (GTK_LABEL (dialog->hits_label), hits_str); - g_free (hits_str); + message = g_strdup_printf (ngettext ("%d occurence", "%d occurences", matches), matches); + gtk_label_set_markup (GTK_LABEL (dialog->hits_label), message); + g_free (message); } } @@ -634,6 +638,14 @@ mousepad_replace_dialog_history_insert_text (const gchar *text) +GtkWidget * +mousepad_replace_dialog_new (void) +{ + return g_object_new (MOUSEPAD_TYPE_REPLACE_DIALOG, NULL); +} + + + void mousepad_replace_dialog_history_clean (void) { @@ -658,8 +670,8 @@ mousepad_replace_dialog_history_clean (void) -GtkWidget * -mousepad_replace_dialog_new (void) +void +mousepad_replace_dialog_page_switched (MousepadReplaceDialog *dialog) { - return g_object_new (MOUSEPAD_TYPE_REPLACE_DIALOG, NULL); + mousepad_replace_dialog_changed (dialog); } diff --git a/mousepad/mousepad-replace-dialog.h b/mousepad/mousepad-replace-dialog.h index e66ad2d..b5cb629 100644 --- a/mousepad/mousepad-replace-dialog.h +++ b/mousepad/mousepad-replace-dialog.h @@ -36,6 +36,8 @@ GtkWidget *mousepad_replace_dialog_new (void); void mousepad_replace_dialog_history_clean (void); +void mousepad_replace_dialog_page_switched (MousepadReplaceDialog *dialog); + G_END_DECLS #endif /* !__MOUSEPAD_REPLACE_DIALOG_H__ */ diff --git a/mousepad/mousepad-statusbar.c b/mousepad/mousepad-statusbar.c index 613bc05..462c98a 100644 --- a/mousepad/mousepad-statusbar.c +++ b/mousepad/mousepad-statusbar.c @@ -149,7 +149,7 @@ mousepad_statusbar_init (MousepadStatusbar *statusbar) ebox = gtk_event_box_new (); gtk_box_pack_start (GTK_BOX (box), ebox, FALSE, TRUE, 0); gtk_event_box_set_visible_window (GTK_EVENT_BOX (ebox), FALSE); - mousepad_util_set_tooltip (ebox, _("Toggle the overwrite mode")); + mousepad_widget_set_tooltip_text (ebox, _("Toggle the overwrite mode")); g_signal_connect (G_OBJECT (ebox), "button-press-event", G_CALLBACK (mousepad_statusbar_overwrite_clicked), statusbar); gtk_widget_show (ebox); diff --git a/mousepad/mousepad-undo.c b/mousepad/mousepad-undo.c index 6d6b213..ab0adf0 100644 --- a/mousepad/mousepad-undo.c +++ b/mousepad/mousepad-undo.c @@ -23,7 +23,15 @@ #include <mousepad/mousepad-undo.h> -#define DEFAULT_CACHE_SIZE (50) +/* global */ +#define MOUSEPAD_UNDO_MAX_STEPS (100) /* maximum number of undo steps */ +#define MOUSEPAD_UNDO_BUFFER_SIZE (40) /* buffer size */ + +/* points system */ +#define MOUSEPAD_UNDO_POINTS (30) /* maximum points in a step */ +#define MOUSEPAD_UNDO_POINTS_CHAR (1) /* points for a character */ +#define MOUSEPAD_UNDO_POINTS_WORD_BREAK (10) /* points for a word break */ +#define MOUSEPAD_UNDO_POINTS_NEW_LINE (25) /* points for a new line */ @@ -33,31 +41,34 @@ typedef enum _MousepadUndoAction MousepadUndoAction; -static void mousepad_undo_class_init (MousepadUndoClass *klass); -static void mousepad_undo_init (MousepadUndo *undo); -static void mousepad_undo_finalize (GObject *object); -static void mousepad_undo_emit_signals (MousepadUndo *undo); -static void mousepad_undo_step_free (MousepadUndoStep *step, - MousepadUndo *undo); -static void mousepad_undo_step_perform (MousepadUndo *undo, - gboolean redo); -static void mousepad_undo_cache_to_step (MousepadUndo *undo); -static void mousepad_undo_cache_reset_needle (MousepadUndo *undo); -static void mousepad_undo_cache_update (MousepadUndo *undo, - MousepadUndoAction action, - gint start, - gint end, - const gchar *text); -static void mousepad_undo_buffer_insert (GtkTextBuffer *buffer, - GtkTextIter *pos, - const gchar *text, - gint length, - MousepadUndo *undo); -static void mousepad_undo_buffer_delete (GtkTextBuffer *buffer, - GtkTextIter *start_iter, - GtkTextIter *end_iter, - MousepadUndo *undo); - +static void mousepad_undo_class_init (MousepadUndoClass *klass); +static void mousepad_undo_init (MousepadUndo *undo); +static void mousepad_undo_finalize (GObject *object); +static void mousepad_undo_emit_signals (MousepadUndo *undo); +static void mousepad_undo_step_free (MousepadUndoStep *step); +static void mousepad_undo_step (MousepadUndo *undo, + gboolean redo); +static void mousepad_undo_cache_reset (MousepadUndo *undo); +static void mousepad_undo_clear_oldest_step (MousepadUndo *undo); +static void mousepad_undo_cache_to_step (MousepadUndo *undo); +static void mousepad_undo_needle_reset (MousepadUndo *undo); +static void mousepad_undo_buffer_changed (MousepadUndo *undo, + MousepadUndoAction action, + const gchar *text, + gint length, + gint start_offset, + gint end_offset); +static void mousepad_undo_buffer_insert (GtkTextBuffer *buffer, + GtkTextIter *pos, + const gchar *text, + gint length, + MousepadUndo *undo); +static void mousepad_undo_buffer_delete (GtkTextBuffer *buffer, + GtkTextIter *start_iter, + GtkTextIter *end_iter, + MousepadUndo *undo); +static void mousepad_undo_buffer_begin_user_action (GtkTextBuffer *buffer, + MousepadUndo *undo); enum @@ -74,27 +85,8 @@ struct _MousepadUndoClass enum _MousepadUndoAction { - INSERT, /* insert action */ - DELETE, /* delete action */ -}; - -struct _MousepadUndoCache -{ - /* string to cache inserted or deleted character */ - GString *string; - - /* current cached action */ - MousepadUndoAction action; - - /* cache start and end positions */ - gint start; - gint end; - - /* whether the last character was a word breaking character */ - guint is_space : 1; - - /* whether the changes in the cache are part of a group */ - guint in_group : 1; + INSERT, /* insert action */ + DELETE, /* delete action */ }; struct _MousepadUndo @@ -102,26 +94,48 @@ struct _MousepadUndo GObject __parent__; /* the text buffer we're monitoring */ - GtkTextBuffer *buffer; + GtkTextBuffer *buffer; /* whether the undo manager is locked */ - gint locked; + gint locked; - /* whether we should put multiple changes into one action */ - gint grouping; + /* whether multiple changes are merged */ + gint grouping; /* whether we can undo or redo */ - guint can_undo : 1; - guint can_redo : 1; + guint can_undo : 1; + guint can_redo : 1; /* list containing the steps */ - GList *steps; + GList *steps; + + /* number of steps */ + gint n_steps; /* steps list pointer when undoing */ - GList *needle; + GList *needle; + + /* element in the last when saving */ + GList *saved; + + /* string holding the deleted characters */ + GString *cache; - /* internal cache */ - MousepadUndoCache cache; + /* start and end positions of the cache */ + gint cache_start; + gint cache_end; + + /* if the last character in the cache is a space */ + guint cache_is_space : 1; + + /* if the changes in the cache are part of a group */ + guint cache_in_group : 1; + + /* current action in the cache */ + MousepadUndoAction cache_action; + + /* number of points assigned to the cache */ + gint cache_points; }; struct _MousepadUndoStep @@ -129,8 +143,8 @@ struct _MousepadUndoStep /* step action */ MousepadUndoAction action; - /* pointer to the string or another entry in the list */ - gpointer data; + /* deleted string */ + gchar *data; /* start and end positions */ gint start; @@ -203,17 +217,16 @@ mousepad_undo_init (MousepadUndo *undo) /* initialize */ undo->locked = 0; undo->grouping = 0; + undo->n_steps = 0; undo->can_undo = FALSE; undo->can_redo = FALSE; undo->steps = NULL; undo->needle = NULL; + undo->saved = NULL; /* initialize the cache */ - undo->cache.string = NULL; - undo->cache.start = -1; - undo->cache.end = -1; - undo->cache.is_space = FALSE; - undo->cache.in_group = FALSE; + undo->cache = NULL; + mousepad_undo_cache_reset (undo); } @@ -243,7 +256,7 @@ mousepad_undo_emit_signals (MousepadUndo *undo) gboolean can_undo, can_redo; /* detect if we can undo or redo */ - can_undo = (undo->needle != NULL); + can_undo = (undo->needle != NULL || undo->cache_start != undo->cache_end); can_redo = (undo->needle == NULL || g_list_previous (undo->needle) != NULL); /* emit signals if needed */ @@ -263,13 +276,8 @@ mousepad_undo_emit_signals (MousepadUndo *undo) static void -mousepad_undo_step_free (MousepadUndoStep *step, - MousepadUndo *undo) +mousepad_undo_step_free (MousepadUndoStep *step) { - /* remove from the list */ - if (G_LIKELY (undo)) - undo->steps = g_list_remove (undo->steps, step); - /* free the string */ g_free (step->data); @@ -280,15 +288,15 @@ mousepad_undo_step_free (MousepadUndoStep *step, static void -mousepad_undo_step_perform (MousepadUndo *undo, - gboolean redo) +mousepad_undo_step (MousepadUndo *undo, + gboolean redo) { MousepadUndoStep *step; MousepadUndoAction action; - GList *li; GtkTextIter start_iter, end_iter; + GList *li; - /* lock for updates */ + /* lock */ mousepad_undo_lock (undo); /* flush the cache */ @@ -306,63 +314,64 @@ mousepad_undo_step_perform (MousepadUndo *undo, /* freeze buffer notifications */ g_object_freeze_notify (G_OBJECT (undo->buffer)); - for (li = undo->needle; li != NULL; li = redo ? li->prev : li->next) + for (li = undo->needle; li != NULL; li = (redo ? li->prev : li->next)) { - /* get the step data */ + /* get the step */ step = li->data; - if (G_LIKELY (step)) - { - /* get the action */ - action = step->action; + /* get the action */ + action = step->action; - /* invert the action if we redo */ - if (redo) - action = (action == INSERT ? DELETE : INSERT); + /* invert the action if needed */ + if (redo) + action = (action == INSERT ? DELETE : INSERT); - /* get the start iter position */ - gtk_text_buffer_get_iter_at_offset (undo->buffer, &start_iter, step->start); + /* get the start iter */ + gtk_text_buffer_get_iter_at_offset (undo->buffer, &start_iter, step->start); - switch (action) - { - case INSERT: - /* get the end iter */ - gtk_text_buffer_get_iter_at_offset (undo->buffer, &end_iter, step->end); + switch (action) + { + case INSERT: + /* debug check */ + _mousepad_return_if_fail (step->data == NULL); - /* set the string we're going to remove for redo */ - if (step->data == NULL) - step->data = gtk_text_buffer_get_slice (undo->buffer, &start_iter, &end_iter, TRUE); + /* get the end iter */ + gtk_text_buffer_get_iter_at_offset (undo->buffer, &end_iter, step->end); - /* delete the inserted text */ - gtk_text_buffer_delete (undo->buffer, &start_iter, &end_iter); - break; + /* set the deleted for redo */ + step->data = gtk_text_buffer_get_slice (undo->buffer, &start_iter, &end_iter, TRUE); - case DELETE: - _mousepad_return_if_fail (step->data != NULL); + /* delete the inserted text */ + gtk_text_buffer_delete (undo->buffer, &start_iter, &end_iter); + break; - /* insert the deleted text */ - gtk_text_buffer_insert (undo->buffer, &start_iter, (gchar *)step->data, -1); - break; + case DELETE: + /* debug check */ + _mousepad_return_if_fail (step->data != NULL); - default: - _mousepad_assert_not_reached (); - break; - } + /* insert the deleted text */ + gtk_text_buffer_insert (undo->buffer, &start_iter, step->data, -1); - /* set the cursor, we scroll to the cursor in mousepad-document */ - gtk_text_buffer_place_cursor (undo->buffer, &start_iter); + /* and cleanup */ + g_free (step->data); + step->data = NULL; + break; + + default: + _mousepad_assert_not_reached (); + break; } + /* get the previous item when we redo */ if (redo) { - /* get the previous step to see if it's part of a group */ if (g_list_previous (li) != NULL) step = g_list_previous (li)->data; else step = NULL; } - /* as long as the step is part of a group, we continue */ + /* break when the step is not part of a group */ if (step == NULL || step->in_group == FALSE) break; } @@ -370,191 +379,267 @@ mousepad_undo_step_perform (MousepadUndo *undo, /* thawn buffer notifications */ g_object_thaw_notify (G_OBJECT (undo->buffer)); - /* set the needle element */ + /* set the needle */ if (redo) undo->needle = li; else undo->needle = g_list_next (li); + /* check if we've somehow reached the save point */ + gtk_text_buffer_set_modified (undo->buffer, undo->needle != undo->saved); + /* emit undo and redo signals */ mousepad_undo_emit_signals (undo); - /* release the lock */ + /* unlock */ mousepad_undo_unlock (undo); } static void -mousepad_undo_cache_to_step (MousepadUndo *undo) +mousepad_undo_clear_oldest_step (MousepadUndo *undo) { + GList *li, *lprev; MousepadUndoStep *step; + gint to_remove; - /* leave when the cache is empty */ - if (undo->cache.start == undo->cache.end) - return; + _mousepad_return_if_fail (undo->n_steps > MOUSEPAD_UNDO_MAX_STEPS); - /* allocate slice */ - step = g_slice_new0 (MousepadUndoStep); + /* number of steps to remove */ + to_remove = undo->n_steps - MOUSEPAD_UNDO_MAX_STEPS; - /* set step data */ - step->action = undo->cache.action; - step->start = undo->cache.start; - step->end = undo->cache.end; - step->in_group = undo->cache.in_group; - - if (step->action == DELETE) - { - /* set the step string and allocate a new one */ - step->data = g_string_free (undo->cache.string, FALSE); - undo->cache.string = NULL; - } - else + /* get end of steps list and remove the entire group */ + for (li = g_list_last (undo->steps); li != NULL; li = lprev) { - /* set the data to null on insert actions */ - step->data = NULL; + step = li->data; + + /* update counters */ + if (step->in_group == FALSE) + { + if (to_remove == 0) + break; + + /* update counter */ + to_remove--; + undo->n_steps--; + } + + /* cleanup */ + mousepad_undo_step_free (step); + + /* previous step */ + lprev = li->prev; + + /* remove from list */ + undo->steps = g_list_delete_link (undo->steps, li); } +} - /* prepend the new step */ - undo->steps = g_list_prepend (undo->steps, step); - /* reset the needle */ - undo->needle = undo->steps; - /* reset the cache */ - undo->cache.start = undo->cache.end = -1; - undo->cache.is_space = FALSE; - undo->cache.in_group = FALSE; +static void +mousepad_undo_cache_reset (MousepadUndo *undo) +{ + _mousepad_return_if_fail (undo->cache == NULL); + + /* reset variables */ + undo->cache_start = undo->cache_end = -1; + undo->cache_in_group = FALSE; + undo->cache_is_space = FALSE; + undo->cache_points = 0; } static void -mousepad_undo_cache_reset_needle (MousepadUndo *undo) +mousepad_undo_cache_to_step (MousepadUndo *undo) { - gint i; + MousepadUndoStep *step; - /* make sure the needle is the start of the list */ - if (undo->needle != undo->steps) + /* only add when the cache contains changes */ + if (G_LIKELY (undo->cache_start != undo->cache_end)) { - /* walk from the start to the needle and remove them */ - for (i = g_list_position (undo->steps, undo->needle); i > 0; i--) - mousepad_undo_step_free (g_list_first (undo->steps)->data, undo); + /* make sure the needle has been reset */ + _mousepad_return_if_fail (undo->needle == undo->steps); + + /* allocate slice */ + step = g_slice_new0 (MousepadUndoStep); + + /* set data */ + step->action = undo->cache_action; + step->start = undo->cache_start; + step->end = undo->cache_end; + step->in_group = undo->cache_in_group; + + /* increase real step counter */ + if (step->in_group == FALSE) + if (++undo->n_steps > MOUSEPAD_UNDO_MAX_STEPS) + mousepad_undo_clear_oldest_step (undo); + + if (step->action == DELETE) + { + /* free cache and set the data */ + step->data = g_string_free (undo->cache, FALSE); + + /* null the cache */ + undo->cache = NULL; + } + else + { + /* null the data */ + step->data = NULL; + } + + /* prepend the new step */ + undo->needle = undo->steps = g_list_prepend (undo->steps, step); - /* check needle */ - _mousepad_return_if_fail (undo->needle == undo->needle); + /* reset the cache */ + mousepad_undo_cache_reset (undo); } + + g_message ("%d steps", undo->n_steps); } static void -mousepad_undo_cache_update (MousepadUndo *undo, - MousepadUndoAction action, - gint start, - gint end, - const gchar *text) +mousepad_undo_needle_reset (MousepadUndo *undo) { - gint length; - guchar c; + MousepadUndoStep *step; + + /* remove steps from the list until we reach the needle */ + while (undo->steps != undo->needle) + { + step = undo->steps->data; - /* length of the text */ - length = ABS (end - start); + /* decrease real step counter */ + if (step->in_group == FALSE) + undo->n_steps--; - /* initialize the cache, if not already done */ - if (undo->cache.string == NULL && action == DELETE) - undo->cache.string = g_string_sized_new (DEFAULT_CACHE_SIZE); + /* free the step data */ + mousepad_undo_step_free (step); + + /* delete the element from the list */ + undo->steps = g_list_delete_link (undo->steps, undo->steps); + } + + /* debug check */ + _mousepad_return_if_fail (undo->needle == undo->steps); +} - /* check if we should start a new step before handling this one */ + + +static void +mousepad_undo_buffer_changed (MousepadUndo *undo, + MousepadUndoAction action, + const gchar *text, + gint length, + gint start_offset, + gint end_offset) +{ + gunichar c; + gboolean is_space, is_newline; + + /* when grouping is 0 we going to detect if it's needed to create a + * new step (existing data in buffer, points, etc). when grouping is + * > 0 it means we're already inside a group and thus merge as much + * as possible. */ if (undo->grouping == 0) { - if (undo->cache.in_group == TRUE) + if (length > 1 || undo->cache_in_group) { - /* force a new step if the new step is not part of a group and the - * content in the cache is */ - goto force_new_step; + /* the buffer contains still data from a grouped step or more then one + * character has been changed. in this case we always create a new step */ + goto create_new_step; } - else if (length == 1) + else /* single char changed */ { - /* get the character */ + /* get the changed character */ c = g_utf8_get_char (text); - /* detect if the character is a word breaking char */ - if (g_unichar_isspace (c)) - { - /* the char is a word breaker. we don't care if the previous - * one was one too, because we merge multiple spaces/tabs/newlines - * into one step */ - undo->cache.is_space = TRUE; - } - else if (undo->cache.is_space) + /* if the character is a space */ + is_space = g_unichar_isspace (c); + + /* when the maximum number of points has been passed and the + * last charater in the buffer differs from the new one, we + * force a new step */ + if (undo->cache_points > MOUSEPAD_UNDO_POINTS + && undo->cache_is_space != is_space) { - /* the new character is not a work breaking char, but the - * previous one was, force a new step */ - goto force_new_step; + goto create_new_step; } - } - else - { - /* grouping is not enabled and multiple chars are inseted or - * deleted, force a new step */ - goto force_new_step; + + /* set the new last character type */ + undo->cache_is_space = is_space; + + /* if the changed character is a new line */ + is_newline = (c == '\n' || c == '\r'); + + /* update the point statistics */ + if (is_newline) + undo->cache_points += MOUSEPAD_UNDO_POINTS_NEW_LINE; + else if (is_space) + undo->cache_points += MOUSEPAD_UNDO_POINTS_WORD_BREAK; + else + undo->cache_points += MOUSEPAD_UNDO_POINTS_CHAR; } } - /* handle the buffer change and try to append it to the cache */ - if (undo->cache.action == action + /* try to merge the new change with the buffer. if this is not possible + * new put the cache in a new step and insert the last change in the buffer */ + if (undo->cache_action == action && action == INSERT - && undo->cache.end == start) + && undo->cache_end == start_offset) { - /* we can append with the previous insert change, update end postion */ - undo->cache.end = end; + /* we can merge with the previous insert */ + undo->cache_end = end_offset; } - else if (undo->cache.action == action + else if (undo->cache_action == action && action == DELETE - && undo->cache.start == end) + && undo->cache_start == end_offset) { - /* we can append with the previous delete change, update */ - undo->cache.start = start; + /* we can merge with the previous delete */ + undo->cache_start = start_offset; + + /* label */ + prepend_deleted_text: + + /* create a new cache if needed */ + if (undo->cache == NULL) + undo->cache = g_string_sized_new (MOUSEPAD_UNDO_BUFFER_SIZE); /* prepend removed characters */ - undo->cache.string = g_string_prepend_len (undo->cache.string, text, length); + undo->cache = g_string_prepend_len (undo->cache, text, length); } else { /* label */ - force_new_step: + create_new_step: - /* reset the needle */ - mousepad_undo_cache_reset_needle (undo); + /* reset the needle of the steps list */ + mousepad_undo_needle_reset (undo); - /* we cannot cache with the previous change, put the cache into a step */ + /* put the cache in a new step */ mousepad_undo_cache_to_step (undo); - /* start a new cache */ - undo->cache.action = action; - undo->cache.start = start; - undo->cache.end = end; - undo->cache.in_group = (undo->grouping > 0); + /* set the new cache variables */ + undo->cache_start = start_offset; + undo->cache_end = end_offset; + undo->cache_action = action; + undo->cache_is_space = FALSE; + undo->cache_in_group = (undo->grouping > 0); + /* prepend deleted text */ if (action == DELETE) - { - undo->cache.string = g_string_sized_new (DEFAULT_CACHE_SIZE); - undo->cache.string = g_string_prepend_len (undo->cache.string, text, length); - } + goto prepend_deleted_text; } /* increase the grouping counter */ undo->grouping++; - /* stuff has been added to the cache, so we can undo now */ - if (undo->can_undo != TRUE) - { - undo->can_undo = TRUE; - - /* emit the can-undo signal */ - g_signal_emit (G_OBJECT (undo), undo_signals[CAN_UNDO], 0, undo->can_undo); - } + /* emit signals */ + mousepad_undo_emit_signals (undo); } @@ -566,21 +651,22 @@ mousepad_undo_buffer_insert (GtkTextBuffer *buffer, gint length, MousepadUndo *undo) { - gint start, end; + gint start_pos, end_pos; _mousepad_return_if_fail (GTK_IS_TEXT_BUFFER (buffer)); _mousepad_return_if_fail (buffer == undo->buffer); /* leave when locked */ - if (G_UNLIKELY (undo->locked > 0)) - return; - - /* buffer positions */ - start = gtk_text_iter_get_offset (pos); - end = start + length; + if (G_LIKELY (undo->locked == 0)) + { + /* buffer positions */ + start_pos = gtk_text_iter_get_offset (pos); + end_pos = start_pos + length; - /* update the cache */ - mousepad_undo_cache_update (undo, INSERT, start, end, text); + /* handle the change */ + mousepad_undo_buffer_changed (undo, INSERT, text, + length, start_pos, end_pos); + } } @@ -592,29 +678,33 @@ mousepad_undo_buffer_delete (GtkTextBuffer *buffer, MousepadUndo *undo) { gchar *text; - gint start, end; + gint start_pos, end_pos; _mousepad_return_if_fail (GTK_IS_TEXT_BUFFER (buffer)); _mousepad_return_if_fail (buffer == undo->buffer); - /* leave when locked */ - if (G_UNLIKELY (undo->locked > 0)) - return; - - /* get the removed string */ - text = gtk_text_buffer_get_slice (buffer, start_iter, end_iter, FALSE); + /* no nothing when locked */ + if (G_LIKELY (undo->locked == 0)) + { + /* get the removed string */ + text = gtk_text_buffer_get_slice (buffer, start_iter, end_iter, FALSE); - /* buffer positions */ - start = gtk_text_iter_get_offset (start_iter); - end = gtk_text_iter_get_offset (end_iter); + /* buffer positions */ + start_pos = gtk_text_iter_get_offset (start_iter); + end_pos = gtk_text_iter_get_offset (end_iter); - /* update the cache */ - mousepad_undo_cache_update (undo, DELETE, start, end, text); + /* handle the change */ + mousepad_undo_buffer_changed (undo, DELETE, text, + ABS (start_pos - end_pos), + start_pos, end_pos); - /* cleanup */ - g_free (text); + /* cleanup */ + g_free (text); + } } + + static void mousepad_undo_buffer_begin_user_action (GtkTextBuffer *buffer, MousepadUndo *undo) @@ -622,15 +712,16 @@ mousepad_undo_buffer_begin_user_action (GtkTextBuffer *buffer, _mousepad_return_if_fail (GTK_IS_TEXT_BUFFER (buffer)); _mousepad_return_if_fail (buffer == undo->buffer); - /* leave when locked */ - if (G_UNLIKELY (undo->locked > 0)) - return; - - /* reset the grouping couter */ - undo->grouping = 0; + /* only reset the group counter when not locked */ + if (G_LIKELY (undo->locked == 0)) + { + /* reset the grouping counter */ + undo->grouping = 0; + } } + MousepadUndo * mousepad_undo_new (GtkTextBuffer *buffer) { @@ -665,19 +756,21 @@ mousepad_undo_clear (MousepadUndo *undo) mousepad_undo_lock (undo); /* clear cache string */ - if (G_LIKELY (undo->cache.string)) - g_string_free (undo->cache.string, TRUE); + if (G_LIKELY (undo->cache)) + g_string_free (undo->cache, TRUE); /* cleanup the undo steps */ for (li = undo->steps; li != NULL; li = li->next) - mousepad_undo_step_free (li->data, undo); + mousepad_undo_step_free (li->data); /* free the list */ g_list_free (undo->steps); /* null */ - undo->steps = undo->needle = NULL; - undo->cache.string = NULL; + undo->steps = undo->needle = undo->saved = NULL; + undo->cache = NULL; + undo->n_steps = 0; + mousepad_undo_cache_reset (undo); /* release lock */ mousepad_undo_unlock (undo); @@ -708,6 +801,26 @@ mousepad_undo_unlock (MousepadUndo *undo) +void +mousepad_undo_save_point (MousepadUndo *undo) +{ + _mousepad_return_if_fail (MOUSEPAD_IS_UNDO (undo)); + + /* reset the needle */ + mousepad_undo_needle_reset (undo); + + /* make sure the buffer is flushed */ + mousepad_undo_cache_to_step (undo); + + /* store the current needle position */ + undo->saved = undo->needle; + + /* TODO remove */ + g_message ("store save point"); +} + + + gboolean mousepad_undo_can_undo (MousepadUndo *undo) { @@ -735,7 +848,7 @@ mousepad_undo_do_undo (MousepadUndo *undo) _mousepad_return_if_fail (mousepad_undo_can_undo (undo)); /* undo the last step */ - mousepad_undo_step_perform (undo, FALSE); + mousepad_undo_step (undo, FALSE); } @@ -747,5 +860,5 @@ mousepad_undo_do_redo (MousepadUndo *undo) _mousepad_return_if_fail (mousepad_undo_can_redo (undo)); /* redo the last undo-ed step */ - mousepad_undo_step_perform (undo, TRUE); + mousepad_undo_step (undo, TRUE); } diff --git a/mousepad/mousepad-undo.h b/mousepad/mousepad-undo.h index 2e37977..9a92d90 100644 --- a/mousepad/mousepad-undo.h +++ b/mousepad/mousepad-undo.h @@ -40,6 +40,8 @@ void mousepad_undo_lock (MousepadUndo *undo); void mousepad_undo_unlock (MousepadUndo *undo); +void mousepad_undo_save_point (MousepadUndo *undo); + gboolean mousepad_undo_can_undo (MousepadUndo *undo); gboolean mousepad_undo_can_redo (MousepadUndo *undo); diff --git a/mousepad/mousepad-util.c b/mousepad/mousepad-util.c index 947507e..fdb2281 100644 --- a/mousepad/mousepad-util.c +++ b/mousepad/mousepad-util.c @@ -473,7 +473,7 @@ mousepad_util_dialog_header (GtkDialog *dialog, } - +#if !GTK_CHECK_VERSION (2,12,0) void mousepad_util_set_tooltip (GtkWidget *widget, const gchar *string) @@ -490,7 +490,7 @@ mousepad_util_set_tooltip (GtkWidget *widget, /* setup the tooltip for the widget */ gtk_tooltips_set_tip (tooltips, widget, string, NULL); } - +#endif gint diff --git a/mousepad/mousepad-util.h b/mousepad/mousepad-util.h index 687b415..bd5d20a 100644 --- a/mousepad/mousepad-util.h +++ b/mousepad/mousepad-util.h @@ -91,8 +91,10 @@ void mousepad_util_dialog_header (GtkDialog *dialog, const gchar *subtitle, const gchar *icon); +#if !GTK_CHECK_VERSION (2,12,0) void mousepad_util_set_tooltip (GtkWidget *widget, const gchar *string); +#endif gint mousepad_util_get_real_line_offset (const GtkTextIter *iter, gint tab_size); diff --git a/mousepad/mousepad-window.c b/mousepad/mousepad-window.c index 34ba8a4..3fa577a 100644 --- a/mousepad/mousepad-window.c +++ b/mousepad/mousepad-window.c @@ -104,8 +104,9 @@ static gboolean mousepad_window_close_document (MousepadW static void mousepad_window_set_title (MousepadWindow *window); /* notebook signals */ -static void mousepad_window_notebook_notified (GtkNotebook *notebook, - GParamSpec *pspec, +static void mousepad_window_notebook_switch_page (GtkNotebook *notebook, + GtkNotebookPage *page, + guint page_num, MousepadWindow *window); static void mousepad_window_notebook_reordered (GtkNotebook *notebook, GtkWidget *page, @@ -689,7 +690,7 @@ mousepad_window_init (MousepadWindow *window) #endif /* connect signals to the notebooks */ - g_signal_connect (G_OBJECT (window->notebook), "notify::page", G_CALLBACK (mousepad_window_notebook_notified), window); + g_signal_connect (G_OBJECT (window->notebook), "switch-page", G_CALLBACK (mousepad_window_notebook_switch_page), window); g_signal_connect (G_OBJECT (window->notebook), "page-reordered", G_CALLBACK (mousepad_window_notebook_reordered), window); g_signal_connect (G_OBJECT (window->notebook), "page-added", G_CALLBACK (mousepad_window_notebook_added), window); g_signal_connect (G_OBJECT (window->notebook), "page-removed", G_CALLBACK (mousepad_window_notebook_removed), window); @@ -710,6 +711,7 @@ mousepad_window_init (MousepadWindow *window) /* allow drops in the window */ gtk_drag_dest_set (GTK_WIDGET (window), GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP, drop_targets, G_N_ELEMENTS (drop_targets), GDK_ACTION_COPY | GDK_ACTION_MOVE); g_signal_connect (G_OBJECT (window), "drag-data-received", G_CALLBACK (mousepad_window_drag_data_received), window); + } @@ -1136,6 +1138,9 @@ mousepad_window_add (MousepadWindow *window, _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document)); _mousepad_return_if_fail (GTK_IS_NOTEBOOK (window->notebook)); + /* get the active tab before we switch to the new one */ + prev_active = window->active; + /* create the tab label */ label = mousepad_document_get_tab_label (document); @@ -1152,21 +1157,21 @@ mousepad_window_add (MousepadWindow *window, /* show the document */ gtk_widget_show (GTK_WIDGET (document)); - /* get the active tab before we switch to the new one */ - prev_active = window->active; - - /* switch to the new tab */ - gtk_notebook_set_current_page (GTK_NOTEBOOK (window->notebook), page); + /* don't bother about this when there was no previous active page (startup) */ + if (G_LIKELY (prev_active != NULL)) + { + /* switch to the new tab */ + gtk_notebook_set_current_page (GTK_NOTEBOOK (window->notebook), page); + + /* destroy the previous tab if it was not modified, untitled and the new tab is not untitled */ + if (gtk_text_buffer_get_modified (prev_active->buffer) == FALSE + && mousepad_file_get_filename (prev_active->file) == NULL + && mousepad_file_get_filename (document->file) != NULL) + gtk_widget_destroy (GTK_WIDGET (prev_active)); + } /* make sure the textview is focused in the new document */ mousepad_document_focus_textview (document); - - /* destroy the previous tab if it was not modified, untitled and the new tab is not untitled */ - if (prev_active != NULL - && gtk_text_buffer_get_modified (prev_active->buffer) == FALSE - && mousepad_file_get_filename (prev_active->file) == NULL - && mousepad_file_get_filename (document->file) != NULL) - gtk_widget_destroy (GTK_WIDGET (prev_active)); } @@ -1265,31 +1270,34 @@ mousepad_window_set_title (MousepadWindow *window) * Notebook Signal Functions **/ static void -mousepad_window_notebook_notified (GtkNotebook *notebook, - GParamSpec *pspec, - MousepadWindow *window) +mousepad_window_notebook_switch_page (GtkNotebook *notebook, + GtkNotebookPage *page, + guint page_num, + MousepadWindow *window) { - gint page_num; + MousepadDocument *document; _mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window)); - - /* get the current page */ - page_num = gtk_notebook_get_current_page (notebook); + _mousepad_return_if_fail (GTK_IS_NOTEBOOK (notebook)); /* get the new active document */ - if (G_LIKELY (page_num != -1)) - window->active = MOUSEPAD_DOCUMENT (gtk_notebook_get_nth_page (notebook, page_num)); - else - g_assert_not_reached (); + document = MOUSEPAD_DOCUMENT (gtk_notebook_get_nth_page (notebook, page_num)); - /* set the window title */ - mousepad_window_set_title (window); + /* only update when really changed */ + if (G_LIKELY (window->active != document)) + { + /* set new active document */ + window->active = document; + + /* set the window title */ + mousepad_window_set_title (window); - /* update the menu actions */ - mousepad_window_update_actions (window); + /* update the menu actions */ + mousepad_window_update_actions (window); - /* update the statusbar */ - mousepad_document_send_signals (window->active); + /* update the statusbar */ + mousepad_document_send_signals (window->active); + } } @@ -3278,7 +3286,12 @@ mousepad_window_action_save (GtkAction *action, /* update the window title */ mousepad_window_set_title (window); - if (G_UNLIKELY (succeed == FALSE)) + if (G_LIKELY (succeed)) + { + /* store the save state in the undo manager */ + mousepad_undo_save_point (document->undo); + } + else { /* show the error */ mousepad_dialogs_show_error (GTK_WINDOW (window), error, _("Failed to save the document")); @@ -3390,9 +3403,16 @@ mousepad_window_action_save_all (GtkAction *action, /* try to save the file */ succeed = mousepad_file_save (MOUSEPAD_DOCUMENT (document)->file, &error); - /* break on problems */ - if (G_UNLIKELY (succeed == FALSE)) - break; + if (G_LIKELY (succeed)) + { + /* store save state */ + mousepad_undo_save_point (MOUSEPAD_DOCUMENT (document)->undo); + } + else + { + /* break on problems */ + break; + } } } @@ -3854,20 +3874,31 @@ mousepad_window_action_find_previous (GtkAction *action, } +static void +mousepad_window_action_replace_switch_page (MousepadWindow *window) +{ + _mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window)); + _mousepad_return_if_fail (MOUSEPAD_IS_REPLACE_DIALOG (window->replace_dialog)); + _mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active)); + + /* page switched */ + mousepad_replace_dialog_page_switched (MOUSEPAD_REPLACE_DIALOG (window->replace_dialog)); +} + static void mousepad_window_action_replace_destroy (MousepadWindow *window) { _mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window)); - /* TODO disconnect tab switch signal */ + /* disconnect tab switch signal */ + g_signal_handlers_disconnect_by_func (G_OBJECT (window->notebook), mousepad_window_action_replace_switch_page, window); /* reset the dialog variable */ window->replace_dialog = NULL; } - static void mousepad_window_action_replace (GtkAction *action, MousepadWindow *window) @@ -3888,13 +3919,12 @@ mousepad_window_action_replace (GtkAction *action, /* connect signals */ g_signal_connect_swapped (G_OBJECT (window->replace_dialog), "destroy", G_CALLBACK (mousepad_window_action_replace_destroy), window); g_signal_connect_swapped (G_OBJECT (window->replace_dialog), "search", G_CALLBACK (mousepad_window_search), window); - - /* TODO tab switch update signal */ + g_signal_connect_swapped (G_OBJECT (window->notebook), "switch-page", G_CALLBACK (mousepad_window_action_replace_switch_page), window); } else { /* focus the existing dialog */ - gtk_widget_grab_focus (window->replace_dialog); + gtk_window_present (GTK_WINDOW (window->replace_dialog)); } } _______________________________________________ Xfce4-commits mailing list Xfce4-commits@xfce.org https://mail.xfce.org/mailman/listinfo/xfce4-commits