[Xfce4-commits] <mousepad:master> * 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.

Nick Schermer noreply at xfce.org
Sat May 5 21:31:07 CEST 2012


Updating branch refs/heads/master
         to d1075d569a475bc2e16dfaab0209e1e4d0c8dec0 (commit)
       from 1d7bef769029aae39ddcecb6c529177393363dcf (commit)

commit d1075d569a475bc2e16dfaab0209e1e4d0c8dec0
Author: Nick Schermer <nick at 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 <nick at 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 <nick at 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));
     }
 }
 


More information about the Xfce4-commits mailing list