undo patch feature, can be enabled through compose.h by adding a #define USE_UNDO_PATCH
This commit is contained in:
parent
f362477839
commit
53c9af2bc2
8 changed files with 631 additions and 14 deletions
|
@ -1,3 +1,15 @@
|
|||
2001-11-11 [hoa] 0.6.5.claws6
|
||||
|
||||
* src/compose.[ch]
|
||||
Insert signature at the end of the text.
|
||||
Added undo patch but currently disabled
|
||||
|
||||
* src/procmsg.c
|
||||
Threading by subject improved.
|
||||
|
||||
* added src/undo.[ch]
|
||||
Undo module for compose dialog box.
|
||||
|
||||
2001-11-11 [alfons] 0.6.5.claws5
|
||||
|
||||
* src/procmsg.c
|
||||
|
|
|
@ -8,7 +8,7 @@ MINOR_VERSION=6
|
|||
MICRO_VERSION=5
|
||||
INTERFACE_AGE=0
|
||||
BINARY_AGE=0
|
||||
EXTRA_VERSION=claws4
|
||||
EXTRA_VERSION=claws6
|
||||
VERSION=$MAJOR_VERSION.$MINOR_VERSION.$MICRO_VERSION$EXTRA_VERSION
|
||||
|
||||
dnl
|
||||
|
|
|
@ -120,7 +120,8 @@ sylpheed_SOURCES = \
|
|||
quote_fmt_lex.l quote_fmt_lex.h \
|
||||
quote_fmt_parse.y quote_fmt.h \
|
||||
gtkspell.c gtkspell.h gtkxtext.h \
|
||||
template_select.c template_select.h
|
||||
template_select.c template_select.h \
|
||||
undo.c undo.h
|
||||
|
||||
BUILT_SOURCES = \
|
||||
quote_fmt_lex.c \
|
||||
|
|
|
@ -102,6 +102,12 @@
|
|||
#include "quote_fmt.h"
|
||||
#include "template.h"
|
||||
|
||||
/* DINH V. Hoa - waiting for definitive version
|
||||
of undo patch */
|
||||
#ifdef USE_UNDO_PATCH
|
||||
#include "undo.h"
|
||||
#endif
|
||||
|
||||
#if USE_GPGME
|
||||
# include "rfc2015.h"
|
||||
#endif
|
||||
|
@ -308,6 +314,10 @@ static gint compose_delete_cb (GtkWidget *widget,
|
|||
static void compose_destroy_cb (GtkWidget *widget,
|
||||
Compose *compose);
|
||||
|
||||
#ifdef USE_UNDO_PATCH /* DINH V. Hoa */
|
||||
static void compose_undo_cb (Compose *compose);
|
||||
static void compose_redo_cb (Compose *compose);
|
||||
#endif
|
||||
static void compose_cut_cb (Compose *compose);
|
||||
static void compose_copy_cb (Compose *compose);
|
||||
static void compose_paste_cb (Compose *compose);
|
||||
|
@ -316,6 +326,12 @@ static void compose_allsel_cb (Compose *compose);
|
|||
static void compose_grab_focus_cb (GtkWidget *widget,
|
||||
Compose *compose);
|
||||
|
||||
#ifdef USE_UNDO_PATCH /* DINH V. Hoa */
|
||||
static void compose_insert_text_cb(GtkEditable *editable, gchar *new_text,
|
||||
gint new_text_length, gint *position, Compose *compose);
|
||||
static void compose_delete_text_cb(GtkEditable *editable, gint start_pos,
|
||||
gint end_pos, Compose *compose);
|
||||
#endif
|
||||
static void compose_changed_cb (GtkEditable *editable,
|
||||
Compose *compose);
|
||||
static void compose_button_press_cb (GtkWidget *widget,
|
||||
|
@ -426,8 +442,13 @@ static GtkItemFactoryEntry compose_entries[] =
|
|||
{N_("/_File/_Close"), "<alt>W", compose_close_cb, 0, NULL},
|
||||
|
||||
{N_("/_Edit"), NULL, NULL, 0, "<Branch>"},
|
||||
{N_("/_Edit/_Undo"), "<control>Z", NULL, 0, NULL},
|
||||
{N_("/_Edit/_Redo"), "<control>Y", NULL, 0, NULL},
|
||||
#ifdef USE_UNDO_PATCH /* DINH V. Hoa */
|
||||
{N_("/_Edit/_Undo"), "<control>Z", compose_undo_cb, 0, NULL},
|
||||
{N_("/_Edit/_Redo"), "<control>Y", compose_redo_cb, 0, NULL},
|
||||
#else
|
||||
{N_("/_Edit/_Undo"), "<control>Z", NULL, 0, NULL},
|
||||
{N_("/_Edit/_Redo"), "<control>Y", NULL, 0, NULL},
|
||||
#endif
|
||||
{N_("/_Edit/---"), NULL, NULL, 0, "<Separator>"},
|
||||
{N_("/_Edit/Cu_t"), "<control>X", compose_cut_cb, 0, NULL},
|
||||
{N_("/_Edit/_Copy"), "<control>C", compose_copy_cb, 0, NULL},
|
||||
|
@ -1676,6 +1697,9 @@ static void compose_insert_sig(Compose *compose)
|
|||
return;
|
||||
}
|
||||
|
||||
gtk_stext_set_point(GTK_STEXT(compose->text),
|
||||
gtk_stext_get_length(GTK_STEXT(compose->text)));
|
||||
|
||||
gtk_stext_insert(GTK_STEXT(compose->text), NULL, NULL, NULL, "\n\n", 2);
|
||||
if (prefs_common.sig_sep) {
|
||||
gtk_stext_insert(GTK_STEXT(compose->text), NULL, NULL, NULL,
|
||||
|
@ -3739,6 +3763,12 @@ static Compose *compose_create(PrefsAccount *account, ComposeMode mode)
|
|||
|
||||
gtk_container_add(GTK_CONTAINER(scrolledwin), text);
|
||||
|
||||
#ifdef USE_UNDO_PATCH
|
||||
gtk_signal_connect(GTK_OBJECT(text), "insert-text",
|
||||
GTK_SIGNAL_FUNC(compose_insert_text_cb), compose);
|
||||
gtk_signal_connect(GTK_OBJECT(text), "delete-text",
|
||||
GTK_SIGNAL_FUNC(compose_delete_text_cb), compose);
|
||||
#endif
|
||||
gtk_signal_connect(GTK_OBJECT(text), "changed",
|
||||
GTK_SIGNAL_FUNC(compose_changed_cb), compose);
|
||||
gtk_signal_connect(GTK_OBJECT(text), "grab_focus",
|
||||
|
@ -4342,6 +4372,11 @@ static void compose_destroy(Compose *compose)
|
|||
|
||||
g_free(compose->exteditor_file);
|
||||
|
||||
#ifdef USE_UNDO_PATCH
|
||||
compose_undo_free_list(&compose->undo);
|
||||
compose_undo_free_list(&compose->redo);
|
||||
#endif
|
||||
|
||||
for (row = 0; (ainfo = gtk_clist_get_row_data(clist, row)) != NULL;
|
||||
row++)
|
||||
compose_attach_info_free(ainfo);
|
||||
|
@ -5325,6 +5360,22 @@ static void compose_destroy_cb(GtkWidget *widget, Compose *compose)
|
|||
compose_destroy(compose);
|
||||
}
|
||||
|
||||
#ifdef USE_UNDO_PATCH
|
||||
static void compose_undo_cb(Compose *compose)
|
||||
{
|
||||
if (compose->focused_editable &&
|
||||
GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
|
||||
compose_undo_undo(compose,NULL);
|
||||
}
|
||||
|
||||
static void compose_redo_cb(Compose *compose)
|
||||
{
|
||||
if (compose->focused_editable &&
|
||||
GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
|
||||
compose_undo_redo(compose,NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void compose_cut_cb(Compose *compose)
|
||||
{
|
||||
if (compose->focused_editable &&
|
||||
|
@ -5363,6 +5414,32 @@ static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
|
|||
compose->focused_editable = widget;
|
||||
}
|
||||
|
||||
#ifdef USE_UNDO_PATCH
|
||||
static void compose_insert_text_cb(GtkEditable *editable, gchar *new_text,
|
||||
gint new_text_length, gint *position, Compose *compose)
|
||||
{
|
||||
guchar *text_to_insert;
|
||||
|
||||
text_to_insert = g_strndup (new_text, new_text_length);
|
||||
compose_undo_add (text_to_insert, *position, (*position + new_text_length), UNDO_ACTION_INSERT, compose);
|
||||
|
||||
g_free (text_to_insert);
|
||||
}
|
||||
|
||||
static void compose_delete_text_cb(GtkEditable *editable, gint start_pos,
|
||||
gint end_pos, Compose *compose)
|
||||
{
|
||||
guchar *text_to_delete;
|
||||
|
||||
if (start_pos == end_pos )
|
||||
return;
|
||||
text_to_delete = gtk_editable_get_chars (GTK_EDITABLE(editable), start_pos, end_pos);
|
||||
compose_undo_add (text_to_delete, start_pos, end_pos, UNDO_ACTION_DELETE, compose);
|
||||
|
||||
g_free (text_to_delete);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void compose_changed_cb(GtkEditable *editable, Compose *compose)
|
||||
{
|
||||
if (compose->modified == FALSE) {
|
||||
|
|
|
@ -179,6 +179,15 @@ struct _Compose
|
|||
gint exteditor_readdes;
|
||||
gint exteditor_tag;
|
||||
|
||||
/* DINH V. Hoa - waiting for definitive version
|
||||
of undo patch */
|
||||
#ifdef USE_UNDO_PATCH
|
||||
GList *undo;
|
||||
GList *redo;
|
||||
gboolean undo_state;
|
||||
gboolean redo_state;
|
||||
#endif
|
||||
|
||||
#if USE_PSPELL
|
||||
/* Pspell spell checker */
|
||||
GtkPspell *gtkpspell;
|
||||
|
|
|
@ -568,17 +568,22 @@ GNode *procmsg_get_thread_tree(GSList *mlist)
|
|||
msginfo = (MsgInfo *) node->data;
|
||||
parent = NULL;
|
||||
if (subject_is_reply(msginfo->subject)) {
|
||||
if (NULL != (parent = subject_table_lookup(subject_table, msginfo->subject))) {
|
||||
/* the node may already be threaded by IN-REPLY-TO, so go up in the
|
||||
* tree to find the parent node */
|
||||
for (; parent->parent && parent->parent != root; parent = parent->parent)
|
||||
;
|
||||
}
|
||||
parent = subject_table_lookup(subject_table,
|
||||
msginfo->subject);
|
||||
/* the node may already be threaded by IN-REPLY-TO,
|
||||
so go up in the tree to find the parent node */
|
||||
if (parent != NULL) {
|
||||
if (g_node_is_ancestor(node, parent))
|
||||
parent = NULL;
|
||||
if (parent == node)
|
||||
parent = NULL;
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
g_node_unlink(node);
|
||||
g_node_append(parent, node);
|
||||
}
|
||||
}
|
||||
if (parent && parent != node) {
|
||||
g_node_unlink(node);
|
||||
g_node_append(parent, node);
|
||||
}
|
||||
node = next;
|
||||
}
|
||||
|
||||
|
|
476
src/undo.c
Normal file
476
src/undo.c
Normal file
|
@ -0,0 +1,476 @@
|
|||
/*
|
||||
* Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
|
||||
* Copyright (C) 1999-2001 Hiroyuki Yamamoto
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
/* code ported from gedit */
|
||||
|
||||
#include <config.h>
|
||||
#include <string.h> /* For strlen */
|
||||
|
||||
#include "prefs_common.h"
|
||||
#include "undo.h"
|
||||
#include "utils.h"
|
||||
#include "compose.h"
|
||||
#include "gtkstext.h"
|
||||
|
||||
typedef struct _UndoInfo UndoInfo;
|
||||
|
||||
struct _UndoInfo
|
||||
{
|
||||
UndoAction action;
|
||||
gchar *text;
|
||||
gint start_pos;
|
||||
gint end_pos;
|
||||
gfloat window_position;
|
||||
gint mergeable;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* compose_undo_free_list:
|
||||
* @list_pointer: list to be freed
|
||||
*
|
||||
* frees and undo structure list
|
||||
**/
|
||||
void compose_undo_free_list (GList ** list_pointer)
|
||||
{
|
||||
gint n;
|
||||
UndoInfo *nth_redo;
|
||||
GList *list = * list_pointer;
|
||||
|
||||
if (list==NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
debug_print("length of list: %d\n", g_list_length (list));
|
||||
|
||||
/* DINH V. Hoa */
|
||||
/* a better list traversing should be implemented, here
|
||||
it is O(n^2), it can be O(n) */
|
||||
for (n=0; n < g_list_length (list); n++)
|
||||
{
|
||||
nth_redo = g_list_nth_data (list, n);
|
||||
if (nth_redo==NULL){
|
||||
g_warning ("nth_redo==NULL");
|
||||
}
|
||||
g_free (nth_redo->text);
|
||||
g_free (nth_redo);
|
||||
}
|
||||
|
||||
g_list_free (list);
|
||||
*list_pointer = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* compose_set_undo:
|
||||
*
|
||||
* Change the sensivity of the menuentries undo and redo
|
||||
**/
|
||||
static /* DINH V.Hoa */
|
||||
void compose_set_undo (Compose *compose, gint undo_state, gint redo_state)
|
||||
{
|
||||
GtkItemFactory *ifactory;
|
||||
|
||||
debug_print ("\nSet_undo. view:0x%x UNDO:%i REDO:%i\n",
|
||||
(gint) compose,
|
||||
undo_state,
|
||||
redo_state);
|
||||
|
||||
g_return_if_fail (compose != NULL);
|
||||
|
||||
ifactory = gtk_item_factory_from_widget(compose->menubar);
|
||||
|
||||
/* Set undo */
|
||||
switch (undo_state)
|
||||
{
|
||||
case UNDO_STATE_TRUE:
|
||||
if (!compose->undo_state)
|
||||
{
|
||||
compose->undo_state = TRUE;
|
||||
menu_set_sensitive(ifactory, "/Edit/Undo", TRUE);
|
||||
}
|
||||
break;
|
||||
case UNDO_STATE_FALSE:
|
||||
if (compose->undo_state)
|
||||
{
|
||||
compose->undo_state = FALSE;
|
||||
menu_set_sensitive(ifactory, "/Edit/Undo", FALSE);
|
||||
}
|
||||
break;
|
||||
case UNDO_STATE_UNCHANGED:
|
||||
break;
|
||||
case UNDO_STATE_REFRESH:
|
||||
menu_set_sensitive(ifactory, "/Edit/Undo", compose->undo_state);
|
||||
break;
|
||||
default:
|
||||
g_warning ("Undo state not recognized");
|
||||
}
|
||||
|
||||
/* Set redo*/
|
||||
switch (redo_state)
|
||||
{
|
||||
case UNDO_STATE_TRUE:
|
||||
if (!compose->redo_state)
|
||||
{
|
||||
compose->redo_state = TRUE;
|
||||
menu_set_sensitive(ifactory, "/Edit/Redo", TRUE);
|
||||
}
|
||||
break;
|
||||
case UNDO_STATE_FALSE:
|
||||
if (compose->redo_state)
|
||||
{
|
||||
compose->redo_state = FALSE;
|
||||
menu_set_sensitive(ifactory, "/Edit/Redo", FALSE);
|
||||
}
|
||||
break;
|
||||
case UNDO_STATE_UNCHANGED:
|
||||
break;
|
||||
case UNDO_STATE_REFRESH:
|
||||
menu_set_sensitive(ifactory, "/Edit/Redo", compose->redo_state);
|
||||
break;
|
||||
default:
|
||||
g_warning ("Redo state not recognized");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* compose_undo_check_size:
|
||||
* @compose: document to check
|
||||
*
|
||||
* Checks that the size of compose->undo does not excede settings->undo_levels and
|
||||
* frees any undo level above sett->undo_level.
|
||||
*
|
||||
**/
|
||||
|
||||
/* DINH V. Hoa */
|
||||
/* remove only the first-inserted elements if necessary
|
||||
whenever we add a new element
|
||||
remove before inserting a new element */
|
||||
|
||||
static void compose_undo_check_size (Compose *compose)
|
||||
{
|
||||
gint n;
|
||||
UndoInfo *nth_undo;
|
||||
gint undo_levels = 100;
|
||||
|
||||
if (undo_levels < 1)
|
||||
return;
|
||||
|
||||
/* No need to check for the redo list size since the undo
|
||||
list gets freed on any call to compose_undo_add */
|
||||
if (g_list_length (compose->undo) > undo_levels && undo_levels > 0)
|
||||
{
|
||||
gint start;
|
||||
gint end;
|
||||
|
||||
start = g_list_length (compose->undo);
|
||||
end = undo_levels;
|
||||
for (n = start; n >= end; n--)
|
||||
{
|
||||
nth_undo = g_list_nth_data (compose->undo, n - 1);
|
||||
compose->undo = g_list_remove (compose->undo, nth_undo);
|
||||
g_free (nth_undo->text);
|
||||
g_free (nth_undo);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* compose_undo_merge:
|
||||
* @last_undo:
|
||||
* @start_pos:
|
||||
* @end_pos:
|
||||
* @action:
|
||||
*
|
||||
* This function tries to merge the undo object at the top of
|
||||
* the stack with a new set of data. So when we undo for example
|
||||
* typing, we can undo the whole word and not each letter by itself
|
||||
*
|
||||
* Return Value: TRUE is merge was sucessful, FALSE otherwise
|
||||
**/
|
||||
static gint compose_undo_merge (GList *list, guint start_pos, guint end_pos, gint action, const guchar* text)
|
||||
{
|
||||
guchar * temp_string;
|
||||
UndoInfo * last_undo;
|
||||
|
||||
/* This are the cases in which we will NOT merge :
|
||||
1. if (last_undo->mergeable == FALSE)
|
||||
[mergeable = FALSE when the size of the undo data was not 1.
|
||||
or if the data was size = 1 but = '\n' or if the undo object
|
||||
has been "undone" already ]
|
||||
2. The size of text is not 1
|
||||
3. If the new merging data is a '\n'
|
||||
4. If the last char of the undo_last data is a space/tab
|
||||
and the new char is not a space/tab ( so that we undo
|
||||
words and not chars )
|
||||
5. If the type (action) of undo is different from the last one
|
||||
Chema */
|
||||
|
||||
if (list==NULL)
|
||||
return FALSE;
|
||||
|
||||
/* DINH V. Hoa */
|
||||
/* g_list_first or just list->data */
|
||||
|
||||
last_undo = g_list_nth_data (list, 0);
|
||||
|
||||
if (!last_undo->mergeable)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (end_pos-start_pos != 1)
|
||||
{
|
||||
last_undo->mergeable = FALSE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* DINH V. Hoa */
|
||||
/* goto are bad coding */
|
||||
|
||||
if (text[0]=='\n')
|
||||
goto compose_undo_do_not_merge;
|
||||
|
||||
if (action != last_undo->action)
|
||||
goto compose_undo_do_not_merge;
|
||||
|
||||
if (action == UNDO_ACTION_DELETE)
|
||||
{
|
||||
if (last_undo->start_pos!=end_pos && last_undo->start_pos != start_pos)
|
||||
goto compose_undo_do_not_merge;
|
||||
|
||||
if (last_undo->start_pos == start_pos)
|
||||
{
|
||||
/* Deleted with the delete key */
|
||||
if ( text[0]!=' ' && text[0]!='\t' &&
|
||||
(last_undo->text [last_undo->end_pos-last_undo->start_pos - 1] ==' '
|
||||
|| last_undo->text [last_undo->end_pos-last_undo->start_pos - 1] == '\t'))
|
||||
goto compose_undo_do_not_merge;
|
||||
|
||||
temp_string = g_strdup_printf ("%s%s", last_undo->text, text);
|
||||
g_free (last_undo->text);
|
||||
last_undo->end_pos += 1;
|
||||
last_undo->text = temp_string;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Deleted with the backspace key */
|
||||
if ( text[0]!=' ' && text[0]!='\t' &&
|
||||
(last_undo->text [0] == ' '
|
||||
|| last_undo->text [0] == '\t'))
|
||||
goto compose_undo_do_not_merge;
|
||||
|
||||
temp_string = g_strdup_printf ("%s%s", text, last_undo->text);
|
||||
g_free (last_undo->text);
|
||||
last_undo->start_pos = start_pos;
|
||||
last_undo->text = temp_string;
|
||||
}
|
||||
}
|
||||
else if (action == UNDO_ACTION_INSERT)
|
||||
{
|
||||
if (last_undo->end_pos != start_pos)
|
||||
goto compose_undo_do_not_merge;
|
||||
|
||||
/* if ( text[0]!=' ' && text[0]!='\t' &&
|
||||
(last_undo->text [last_undo->end_pos-last_undo->start_pos - 1] ==' '
|
||||
|| last_undo->text [last_undo->end_pos-last_undo->start_pos - 1] == '\t'))
|
||||
goto compose_undo_do_not_merge;
|
||||
*/
|
||||
temp_string = g_strdup_printf ("%s%s", last_undo->text, text);
|
||||
g_free (last_undo->text);
|
||||
last_undo->end_pos = end_pos;
|
||||
last_undo->text = temp_string;
|
||||
}
|
||||
else
|
||||
debug_print ("Unknown action [%i] inside undo merge encountered", action);
|
||||
|
||||
debug_print ("Merged: %s\n", text);
|
||||
return TRUE;
|
||||
|
||||
compose_undo_do_not_merge:
|
||||
last_undo->mergeable = FALSE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* compose_undo_add:
|
||||
* @text:
|
||||
* @start_pos:
|
||||
* @end_pos:
|
||||
* @action: either UNDO_ACTION_INSERT or UNDO_ACTION_DELETE
|
||||
* @compose:
|
||||
* @view: The view so that we save the scroll bar position.
|
||||
*
|
||||
* Adds text to the undo stack. It also performs test to limit the number
|
||||
* of undo levels and deltes the redo list
|
||||
**/
|
||||
|
||||
void compose_undo_add (const gchar *text, gint start_pos, gint end_pos,
|
||||
UndoAction action, Compose *compose)
|
||||
{
|
||||
UndoInfo *undo;
|
||||
|
||||
debug_print ("undo_add(%i)*%s*\n", strlen (text), text);
|
||||
|
||||
g_return_if_fail (text != NULL);
|
||||
g_return_if_fail (end_pos >= start_pos);
|
||||
|
||||
compose_undo_free_list (&compose->redo);
|
||||
|
||||
/* Set the redo sensitivity */
|
||||
compose_set_undo (compose, UNDO_STATE_UNCHANGED, UNDO_STATE_FALSE);
|
||||
|
||||
if (compose_undo_merge (compose->undo, start_pos, end_pos, action, text))
|
||||
return;
|
||||
|
||||
debug_print ("New: %s\n", text);
|
||||
|
||||
/* DINH V. Hoa */
|
||||
/* build undo objects allocator and destructor functions */
|
||||
|
||||
undo = g_new (UndoInfo, 1);
|
||||
undo->text = g_strdup (text);
|
||||
undo->start_pos = start_pos;
|
||||
undo->end_pos = end_pos;
|
||||
undo->action = action;
|
||||
|
||||
undo->window_position = GTK_ADJUSTMENT(GTK_STEXT(compose->text)->vadj)->value;
|
||||
|
||||
if (end_pos-start_pos!=1 || text[0]=='\n')
|
||||
undo->mergeable = FALSE;
|
||||
else
|
||||
undo->mergeable = TRUE;
|
||||
|
||||
compose->undo = g_list_prepend (compose->undo, undo);
|
||||
|
||||
/* DINH V. Hoa */
|
||||
/* removal of elements should be done first */
|
||||
|
||||
compose_undo_check_size (compose);
|
||||
|
||||
compose_set_undo (compose, UNDO_STATE_TRUE, UNDO_STATE_UNCHANGED);
|
||||
}
|
||||
|
||||
/**
|
||||
* compose_undo_undo:
|
||||
* @w: not used
|
||||
* @data: not used
|
||||
*
|
||||
* Executes an undo request on the current document
|
||||
**/
|
||||
void compose_undo_undo (Compose *compose, gpointer data)
|
||||
{
|
||||
UndoInfo *undo;
|
||||
guint start_pos, end_pos;
|
||||
|
||||
if (compose->undo == NULL)
|
||||
return;
|
||||
|
||||
g_return_if_fail (compose!=NULL);
|
||||
|
||||
|
||||
/* The undo data we need is always at the top op the
|
||||
stack. So, therefore, the first one */
|
||||
undo = g_list_nth_data (compose->undo, 0);
|
||||
g_return_if_fail (undo!=NULL);
|
||||
undo->mergeable = FALSE;
|
||||
compose->redo = g_list_prepend (compose->redo, undo);
|
||||
compose->undo = g_list_remove (compose->undo, undo);
|
||||
|
||||
/* Check if there is a selection active */
|
||||
start_pos = GTK_EDITABLE(compose->text)->selection_start_pos;
|
||||
end_pos = GTK_EDITABLE(compose->text)->selection_end_pos;
|
||||
if ((start_pos > 0 || end_pos > 0) && (start_pos != end_pos))
|
||||
gtk_editable_select_region (GTK_EDITABLE(compose->text), 0, 0);
|
||||
|
||||
/* Move the view (scrollbars) to the correct position */
|
||||
gtk_adjustment_set_value (GTK_ADJUSTMENT(GTK_STEXT(compose->text)->vadj), undo->window_position);
|
||||
|
||||
switch (undo->action){
|
||||
case UNDO_ACTION_DELETE:
|
||||
gtk_stext_set_point(GTK_STEXT(compose->text), undo->start_pos);
|
||||
gtk_stext_insert (GTK_STEXT(compose->text), NULL, NULL, NULL, undo->text, -1);
|
||||
debug_print("UNDO_ACTION_DELETE %s\n",undo->text);
|
||||
break;
|
||||
case UNDO_ACTION_INSERT:
|
||||
gtk_stext_set_point(GTK_STEXT(compose->text), undo->end_pos);
|
||||
gtk_stext_backward_delete (GTK_STEXT(compose->text), undo->end_pos-undo->start_pos);
|
||||
debug_print("UNDO_ACTION_INSERT %d\n",undo->end_pos-undo->start_pos);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
compose_set_undo (compose, UNDO_STATE_UNCHANGED, UNDO_STATE_TRUE);
|
||||
if (g_list_length (compose->undo) == 0)
|
||||
compose_set_undo (compose, UNDO_STATE_FALSE, UNDO_STATE_UNCHANGED);
|
||||
}
|
||||
|
||||
/**
|
||||
* compose_undo_redo:
|
||||
* @w: not used
|
||||
* @data: not used
|
||||
*
|
||||
* executes a redo request on the current document
|
||||
**/
|
||||
void compose_undo_redo (Compose *compose, gpointer data)
|
||||
{
|
||||
UndoInfo *redo;
|
||||
guint start_pos, end_pos;
|
||||
|
||||
if (compose->redo == NULL)
|
||||
return;
|
||||
|
||||
if (compose==NULL)
|
||||
return;
|
||||
|
||||
redo = g_list_nth_data (compose->redo, 0);
|
||||
g_return_if_fail (redo!=NULL);
|
||||
compose->undo = g_list_prepend (compose->undo, redo);
|
||||
compose->redo = g_list_remove (compose->redo, redo);
|
||||
|
||||
/* Check if there is a selection active */
|
||||
start_pos = GTK_EDITABLE(compose->text)->selection_start_pos;
|
||||
end_pos = GTK_EDITABLE(compose->text)->selection_end_pos;
|
||||
if ((start_pos > 0 || end_pos > 0) && (start_pos != end_pos))
|
||||
gtk_editable_select_region (GTK_EDITABLE(compose->text), 0, 0);
|
||||
|
||||
/* Move the view to the right position. */
|
||||
gtk_adjustment_set_value (GTK_ADJUSTMENT(GTK_STEXT(compose->text)->vadj), redo->window_position);
|
||||
|
||||
switch (redo->action){
|
||||
case UNDO_ACTION_INSERT:
|
||||
gtk_stext_set_point(GTK_STEXT(compose->text), redo->start_pos);
|
||||
gtk_stext_insert (GTK_STEXT(compose->text), NULL, NULL, NULL, redo->text, -1);
|
||||
debug_print("UNDO_ACTION_DELETE %s\n",redo->text);
|
||||
break;
|
||||
case UNDO_ACTION_DELETE:
|
||||
gtk_stext_set_point(GTK_STEXT(compose->text), redo->end_pos);
|
||||
gtk_stext_backward_delete (GTK_STEXT(compose->text), redo->end_pos-redo->start_pos);
|
||||
debug_print("UNDO_ACTION_INSERT %d\n",redo->end_pos-redo->start_pos);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
compose_set_undo (compose, UNDO_STATE_TRUE, UNDO_STATE_UNCHANGED);
|
||||
if (g_list_length (compose->redo) == 0)
|
||||
compose_set_undo (compose, UNDO_STATE_UNCHANGED, UNDO_STATE_FALSE);
|
||||
}
|
37
src/undo.h
Normal file
37
src/undo.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
|
||||
* Copyright (C) 1999-2001 Hiroyuki Yamamoto
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
// code ported from gedit
|
||||
|
||||
#ifndef __UNDO_H__
|
||||
#define __UNDO_H__
|
||||
|
||||
typedef enum {
|
||||
UNDO_ACTION_INSERT,
|
||||
UNDO_ACTION_DELETE,
|
||||
} UndoAction;
|
||||
|
||||
typedef enum {
|
||||
UNDO_STATE_TRUE,
|
||||
UNDO_STATE_FALSE,
|
||||
UNDO_STATE_UNCHANGED,
|
||||
UNDO_STATE_REFRESH,
|
||||
} UndoState;
|
||||
|
||||
#endif /* __UNDO_H__ */
|
Loading…
Reference in a new issue