claws-mail/src/undo.c
Paul Mangan 283412fa01 2005-09-21 [paul] 1.9.14cvs47
* README
	* README.jp
	* configure.ac
	* config/config.rpath
	* doc/manual/en/sylpheed-20.html
	* doc/manual/es/sylpheed-20.html
	* po/bg.po
	* po/ca.po
	* po/de.po
	* po/es.po
	* po/fr.po
	* po/it.po
	* po/pt_BR.po
	* po/sk.po
	* po/zh_TW.po
	* src/account.c
	* src/account.h
	* src/action.c
	* src/action.h
	* src/adbookbase.h
	* src/addr_compl.c
	* src/addr_compl.h
	* src/addrbook.c
	* src/addrbook.h
	* src/addrcache.c
	* src/addrcache.h
	* src/addrcindex.c
	* src/addrcindex.h
	* src/addrclip.c
	* src/addrclip.h
	* src/addrdefs.h
	* src/addressadd.c
	* src/addressadd.h
	* src/addressbook.c
	* src/addressbook.h
	* src/addressitem.h
	* src/addrgather.c
	* src/addrgather.h
	* src/addrharvest.c
	* src/addrharvest.h
	* src/addrindex.c
	* src/addrindex.h
	* src/addritem.c
	* src/addritem.h
	* src/addrquery.c
	* src/addrquery.h
	* src/addrselect.c
	* src/addrselect.h
	* src/alertpanel.c
	* src/alertpanel.h
	* src/browseldap.c
	* src/browseldap.h
	* src/codeconv.c
	* src/codeconv.h
	* src/compose.c
	* src/compose.h
	* src/crash.c
	* src/crash.h
	* src/customheader.c
	* src/customheader.h
	* src/displayheader.c
	* src/displayheader.h
	* src/editaddress.c
	* src/editaddress.h
	* src/editbook.c
	* src/editbook.h
	* src/editgroup.c
	* src/editgroup.h
	* src/editjpilot.c
	* src/editjpilot.h
	* src/editldap.c
	* src/editldap.h
	* src/editldap_basedn.c
	* src/editldap_basedn.h
	* src/editvcard.c
	* src/editvcard.h
	* src/enriched.c
	* src/enriched.h
	* src/exphtmldlg.c
	* src/exphtmldlg.h
	* src/expldifdlg.c
	* src/expldifdlg.h
	* src/export.c
	* src/export.h
	* src/exporthtml.c
	* src/exporthtml.h
	* src/exportldif.c
	* src/exportldif.h
	* src/filtering.c
	* src/filtering.h
	* src/folder.c
	* src/folder.h
	* src/folder_item_prefs.c
	* src/folder_item_prefs.h
	* src/foldersel.c
	* src/foldersel.h
	* src/folderutils.c
	* src/folderutils.h
	* src/folderview.c
	* src/folderview.h
	* src/grouplistdialog.c
	* src/grouplistdialog.h
	* src/headerview.c
	* src/headerview.h
	* src/html.c
	* src/html.h
	* src/image_viewer.c
	* src/image_viewer.h
	* src/imap.c
	* src/imap.h
	* src/imap_gtk.c
	* src/imap_gtk.h
	* src/import.c
	* src/import.h
	* src/importldif.c
	* src/importldif.h
	* src/importmutt.c
	* src/importmutt.h
	* src/importpine.c
	* src/importpine.h
	* src/inc.c
	* src/inc.h
	* src/jpilot.c
	* src/jpilot.h
	* src/ldapctrl.c
	* src/ldapctrl.h
	* src/ldaplocate.c
	* src/ldaplocate.h
	* src/ldapquery.c
	* src/ldapquery.h
	* src/ldapserver.c
	* src/ldapserver.h
	* src/ldaputil.c
	* src/ldaputil.h
	* src/ldif.c
	* src/ldif.h
	* src/localfolder.c
	* src/localfolder.h
	* src/main.c
	* src/main.h
	* src/mainwindow.c
	* src/mainwindow.h
	* src/manual.c
	* src/manual.h
	* src/matcher.c
	* src/matcher.h
	* src/matcher_parser.h
	* src/matcher_parser_lex.l
	* src/matcher_parser_parse.y
	* src/mbox.c
	* src/mbox.h
	* src/message_search.c
	* src/message_search.h
	* src/messageview.c
	* src/messageview.h
	* src/mh.c
	* src/mh.h
	* src/mh_gtk.c
	* src/mh_gtk.h
	* src/mimeview.c
	* src/mimeview.h
	* src/msgcache.c
	* src/msgcache.h
	* src/mutt.c
	* src/mutt.h
	* src/news.c
	* src/news.h
	* src/news_gtk.c
	* src/news_gtk.h
	* src/noticeview.c
	* src/noticeview.h
	* src/partial_download.c
	* src/partial_download.h
	* src/pine.c
	* src/pine.h
	* src/pop.c
	* src/pop.h
	* src/prefs_account.c
	* src/prefs_account.h
	* src/prefs_actions.c
	* src/prefs_actions.h
	* src/prefs_common.c
	* src/prefs_common.h
	* src/prefs_compose_writing.c
	* src/prefs_compose_writing.h
	* src/prefs_customheader.c
	* src/prefs_customheader.h
	* src/prefs_display_header.c
	* src/prefs_display_header.h
	* src/prefs_ext_prog.c
	* src/prefs_ext_prog.h
	* src/prefs_filtering.c
	* src/prefs_filtering.h
	* src/prefs_filtering_action.c
	* src/prefs_filtering_action.h
	* src/prefs_folder_column.c
	* src/prefs_folder_column.h
	* src/prefs_folder_item.c
	* src/prefs_folder_item.h
	* src/prefs_fonts.c
	* src/prefs_fonts.h
	* src/prefs_gtk.c
	* src/prefs_gtk.h
	* src/prefs_image_viewer.c
	* src/prefs_image_viewer.h
	* src/prefs_matcher.c
	* src/prefs_matcher.h
	* src/prefs_message.c
	* src/prefs_message.h
	* src/prefs_msg_colors.c
	* src/prefs_msg_colors.h
	* src/prefs_other.c
	* src/prefs_other.h
	* src/prefs_quote.c
	* src/prefs_quote.h
	* src/prefs_receive.c
	* src/prefs_receive.h
	* src/prefs_send.c
	* src/prefs_send.h
	* src/prefs_spelling.c
	* src/prefs_spelling.h
	* src/prefs_summaries.c
	* src/prefs_summaries.h
	* src/prefs_summary_column.c
	* src/prefs_summary_column.h
	* src/prefs_template.c
	* src/prefs_template.h
	* src/prefs_themes.c
	* src/prefs_themes.h
	* src/prefs_toolbar.c
	* src/prefs_toolbar.h
	* src/prefs_wrapping.c
	* src/prefs_wrapping.h
	* src/privacy.c
	* src/privacy.h
	* src/procheader.c
	* src/procheader.h
	* src/procmime.c
	* src/procmime.h
	* src/procmsg.c
	* src/procmsg.h
	* src/quote_fmt.c
	* src/quote_fmt_parse.y
	* src/recv.c
	* src/recv.h
	* src/remotefolder.c
	* src/remotefolder.h
	* src/send_message.c
	* src/send_message.h
	* src/setup.c
	* src/setup.h
	* src/simple-gettext.c
	* src/sourcewindow.c
	* src/sourcewindow.h
	* src/ssl_manager.c
	* src/ssl_manager.h
	* src/statusbar.c
	* src/statusbar.h
	* src/stock_pixmap.c
	* src/stock_pixmap.h
	* src/summary_search.c
	* src/summary_search.h
	* src/summaryview.c
	* src/summaryview.h
	* src/syldap.c
	* src/syldap.h
	* src/textview.c
	* src/textview.h
	* src/toolbar.c
	* src/toolbar.h
	* src/undo.c
	* src/undo.h
	* src/unmime.c
	* src/unmime.h
	* src/vcard.c
	* src/vcard.h
	* src/wizard.c
	* src/wizard.h
	* src/common/base64.c
	* src/common/base64.h
	* src/common/defs.h
	* src/common/hooks.c
	* src/common/hooks.h
	* src/common/log.c
	* src/common/log.h
	* src/common/md5.c
	* src/common/md5.h
	* src/common/mgutils.c
	* src/common/mgutils.h
	* src/common/nntp.c
	* src/common/nntp.h
	* src/common/passcrypt.c
	* src/common/passcrypt.h.in
	* src/common/plugin.c
	* src/common/plugin.h
	* src/common/prefs.c
	* src/common/prefs.h
	* src/common/progressindicator.c
	* src/common/progressindicator.h
	* src/common/quoted-printable.c
	* src/common/quoted-printable.h
	* src/common/session.c
	* src/common/session.h
	* src/common/smtp.c
	* src/common/smtp.h
	* src/common/socket.c
	* src/common/socket.h
	* src/common/ssl.c
	* src/common/ssl.h
	* src/common/ssl_certificate.c
	* src/common/ssl_certificate.h
	* src/common/string_match.c
	* src/common/string_match.h
	* src/common/stringtable.c
	* src/common/sylpheed.c
	* src/common/sylpheed.h
	* src/common/template.c
	* src/common/template.h
	* src/common/timing.h
	* src/common/utils.c
	* src/common/utils.h
	* src/common/uuencode.c
	* src/common/uuencode.h
	* src/common/version.h.in
	* src/common/xml.c
	* src/common/xml.h
	* src/common/xmlprops.c
	* src/common/xmlprops.h
	* src/gtk/about.c
	* src/gtk/about.h
	* src/gtk/colorlabel.c
	* src/gtk/colorlabel.h
	* src/gtk/colorsel.c
	* src/gtk/colorsel.h
	* src/gtk/description_window.c
	* src/gtk/description_window.h
	* src/gtk/filesel.c
	* src/gtk/filesel.h
	* src/gtk/foldersort.c
	* src/gtk/foldersort.h
	* src/gtk/gtkutils.c
	* src/gtk/gtkutils.h
	* src/gtk/inputdialog.c
	* src/gtk/inputdialog.h
	* src/gtk/logwindow.c
	* src/gtk/logwindow.h
	* src/gtk/manage_window.c
	* src/gtk/manage_window.h
	* src/gtk/menu.c
	* src/gtk/menu.h
	* src/gtk/pluginwindow.c
	* src/gtk/pluginwindow.h
	* src/gtk/prefswindow.c
	* src/gtk/prefswindow.h
	* src/gtk/progressdialog.c
	* src/gtk/progressdialog.h
	* src/gtk/quicksearch.c
	* src/gtk/quicksearch.h
	* src/gtk/sslcertwindow.c
	* src/gtk/sslcertwindow.h
	* src/plugins/clamav/clamav_plugin.c
	* src/plugins/clamav/clamav_plugin.h
	* src/plugins/clamav/clamav_plugin_gtk.c
	* src/plugins/demo/demo.c
	* src/plugins/dillo_viewer/dillo_prefs.c
	* src/plugins/dillo_viewer/dillo_prefs.h
	* src/plugins/dillo_viewer/dillo_viewer.c
	* src/plugins/mathml_viewer/mathml_viewer.c
	* src/plugins/pgpcore/passphrase.c
	* src/plugins/pgpcore/passphrase.h
	* src/plugins/pgpcore/plugin.c
	* src/plugins/pgpcore/prefs_gpg.c
	* src/plugins/pgpcore/prefs_gpg.h
	* src/plugins/pgpcore/select-keys.c
	* src/plugins/pgpcore/select-keys.h
	* src/plugins/pgpcore/sgpgme.c
	* src/plugins/pgpcore/sgpgme.h
	* src/plugins/pgpinline/pgpinline.c
	* src/plugins/pgpinline/pgpinline.h
	* src/plugins/pgpinline/plugin.c
	* src/plugins/pgpmime/pgpmime.c
	* src/plugins/pgpmime/pgpmime.h
	* src/plugins/pgpmime/plugin.c
	* src/plugins/spamassassin/spamassassin.c
	* src/plugins/spamassassin/spamassassin.h
	* src/plugins/spamassassin/spamassassin_gtk.c
	* src/plugins/trayicon/trayicon.c
	* tools/OOo2sylpheed.pl
	* tools/acroread2sylpheed.pl
	* tools/asus_mailled.sh
	* tools/filter_conv.pl
	* tools/freshmeat_search.pl
	* tools/google_search.pl
	* tools/kmail2sylpheed.pl
	* tools/kmail2sylpheed_v2.pl
	* tools/maildir2sylpheed.pl
	* tools/multiwebsearch.pl
	* tools/newscache_clean.pl
	* tools/outlook2sylpheed.pl
	* tools/sylpheed-switcher
	* tools/update-po
	* tools/vcard2xml.py
	* tools/kdeservicemenu/sylpheed-kdeservicemenu.pl
		update FSF address
2005-09-21 18:22:51 +00:00

626 lines
18 KiB
C

/*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/* code ported from gedit */
/* This is for my patient girlfirend Regina */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <glib.h>
#include <string.h> /* for strlen */
#include <stdlib.h> /* for mbstowcs */
#include "undo.h"
#include "utils.h"
#include "prefs_common.h"
typedef struct _UndoInfo UndoInfo;
struct _UndoInfo
{
UndoAction action;
gchar *text;
gint start_pos;
gint end_pos;
gfloat window_position;
gint mergeable;
};
static void undo_free_list (GList **list_pointer);
static void undo_check_size (UndoMain *undostruct);
static gint undo_merge (GList *list,
guint start_pos,
guint end_pos,
gint action,
const guchar *text);
static void undo_add (const gchar *text,
gint start_pos,
gint end_pos,
UndoAction action,
UndoMain *undostruct);
static gint undo_get_selection (GtkTextView *textview,
guint *start,
guint *end);
static void undo_insert_text_cb (GtkTextBuffer *textbuf,
GtkTextIter *iter,
gchar *new_text,
gint new_text_length,
UndoMain *undostruct);
static void undo_delete_text_cb (GtkTextBuffer *textbuf,
GtkTextIter *start,
GtkTextIter *end,
UndoMain *undostruct);
static void undo_paste_clipboard_cb (GtkTextView *textview,
UndoMain *undostruct);
void undo_undo (UndoMain *undostruct);
void undo_redo (UndoMain *undostruct);
UndoMain *undo_init(GtkWidget *text)
{
UndoMain *undostruct;
GtkTextView *textview = GTK_TEXT_VIEW(text);
GtkTextBuffer *textbuf = gtk_text_view_get_buffer(textview);
g_return_val_if_fail(text != NULL, NULL);
undostruct = g_new(UndoMain, 1);
undostruct->textview = textview;
undostruct->undo = NULL;
undostruct->redo = NULL;
undostruct->paste = 0;
undostruct->undo_state = FALSE;
undostruct->redo_state = FALSE;
g_signal_connect(G_OBJECT(textbuf), "insert-text",
G_CALLBACK(undo_insert_text_cb), undostruct);
g_signal_connect(G_OBJECT(textbuf), "delete-range",
G_CALLBACK(undo_delete_text_cb), undostruct);
g_signal_connect(G_OBJECT(textview), "paste-clipboard",
G_CALLBACK(undo_paste_clipboard_cb), undostruct);
return undostruct;
}
void undo_destroy (UndoMain *undostruct)
{
undo_free_list(&undostruct->undo);
undo_free_list(&undostruct->redo);
g_free(undostruct);
}
static UndoInfo *undo_object_new(gchar *text, gint start_pos, gint end_pos,
UndoAction action, gfloat window_position)
{
UndoInfo *undoinfo;
undoinfo = g_new (UndoInfo, 1);
undoinfo->text = text;
undoinfo->start_pos = start_pos;
undoinfo->end_pos = end_pos;
undoinfo->action = action;
undoinfo->window_position = window_position;
return undoinfo;
}
static void undo_object_free(UndoInfo *undo)
{
g_free (undo->text);
g_free (undo);
}
/**
* undo_free_list:
* @list_pointer: list to be freed
*
* frees and undo structure list
**/
static void undo_free_list(GList **list_pointer)
{
UndoInfo *undo;
GList *cur, *list = *list_pointer;
if (list == NULL) return;
for (cur = list; cur != NULL; cur = cur->next) {
undo = (UndoInfo *)cur->data;
undo_object_free(undo);
}
g_list_free(list);
*list_pointer = NULL;
}
void undo_set_change_state_func(UndoMain *undostruct, UndoChangeStateFunc func,
gpointer data)
{
g_return_if_fail(undostruct != NULL);
undostruct->change_state_func = func;
undostruct->change_state_data = data;
}
/**
* 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.
*
**/
static void undo_check_size(UndoMain *undostruct)
{
UndoInfo *last_undo;
guint length;
if (prefs_common.undolevels < 1) return;
/* No need to check for the redo list size since the undo
list gets freed on any call to compose_undo_add */
length = g_list_length(undostruct->undo);
if (length >= prefs_common.undolevels && prefs_common.undolevels > 0) {
last_undo = (UndoInfo *)g_list_last(undostruct->undo)->data;
undostruct->undo = g_list_remove(undostruct->undo, last_undo);
undo_object_free(last_undo);
}
}
/**
* 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 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;
last_undo = list->data;
if (!last_undo->mergeable) return FALSE;
if (end_pos - start_pos != 1 ||
text[0] == '\n' ||
action != last_undo->action ||
action == UNDO_ACTION_REPLACE_INSERT ||
action == UNDO_ACTION_REPLACE_DELETE) {
last_undo->mergeable = FALSE;
return FALSE;
}
if (action == UNDO_ACTION_DELETE) {
gboolean checkit = TRUE;
if (last_undo->start_pos != end_pos &&
last_undo->start_pos != start_pos) {
last_undo->mergeable = FALSE;
return FALSE;
} else 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'))
checkit = FALSE;
temp_string = g_strdup_printf("%s%s", last_undo->text, text);
last_undo->end_pos++;
g_free(last_undo->text);
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'))
checkit = FALSE;
temp_string = g_strdup_printf("%s%s", text, last_undo->text);
last_undo->start_pos = start_pos;
g_free(last_undo->text);
last_undo->text = temp_string;
}
if (!checkit) {
last_undo->mergeable = FALSE;
return FALSE;
}
} else if (action == UNDO_ACTION_INSERT) {
if (last_undo->end_pos != start_pos) {
last_undo->mergeable = FALSE;
return FALSE;
} else {
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);
return TRUE;
}
/**
* 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
**/
static void undo_add(const gchar *text,
gint start_pos, gint end_pos,
UndoAction action, UndoMain *undostruct)
{
UndoInfo *undoinfo;
GtkAdjustment *vadj;
g_return_if_fail(text != NULL);
g_return_if_fail(end_pos >= start_pos);
undo_free_list(&undostruct->redo);
/* Set the redo sensitivity */
undostruct->change_state_func(undostruct,
UNDO_STATE_UNCHANGED, UNDO_STATE_FALSE,
undostruct->change_state_data);
if (undostruct->paste != 0) {
if (action == UNDO_ACTION_INSERT)
action = UNDO_ACTION_REPLACE_INSERT;
else
action = UNDO_ACTION_REPLACE_DELETE;
undostruct->paste = undostruct->paste + 1;
if (undostruct->paste == 3)
undostruct->paste = 0;
}
if (undo_merge(undostruct->undo, start_pos, end_pos, action, text))
return;
undo_check_size(undostruct);
vadj = GTK_ADJUSTMENT(GTK_TEXT_VIEW(undostruct->textview)->vadjustment);
undoinfo = undo_object_new(g_strdup(text), start_pos, end_pos, action,
vadj->value);
if (end_pos - start_pos != 1 || text[0] == '\n')
undoinfo->mergeable = FALSE;
else
undoinfo->mergeable = TRUE;
undostruct->undo = g_list_prepend(undostruct->undo, undoinfo);
undostruct->change_state_func(undostruct,
UNDO_STATE_TRUE, UNDO_STATE_UNCHANGED,
undostruct->change_state_data);
}
/**
* undo_undo:
* @w: not used
* @data: not used
*
* Executes an undo request on the current document
**/
void undo_undo(UndoMain *undostruct)
{
UndoInfo *undoinfo;
GtkTextView *textview;
GtkTextBuffer *buffer;
GtkTextIter iter, start_iter, end_iter;
GtkTextMark *mark;
g_return_if_fail(undostruct != NULL);
if (undostruct->undo == NULL) return;
/* The undo data we need is always at the top op the
stack. So, therefore, the first one */
undoinfo = (UndoInfo *)undostruct->undo->data;
g_return_if_fail(undoinfo != NULL);
undoinfo->mergeable = FALSE;
undostruct->redo = g_list_prepend(undostruct->redo, undoinfo);
undostruct->undo = g_list_remove(undostruct->undo, undoinfo);
textview = undostruct->textview;
buffer = gtk_text_view_get_buffer(textview);
undo_block(undostruct);
/* Check if there is a selection active */
mark = gtk_text_buffer_get_insert(buffer);
gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
gtk_text_buffer_place_cursor(buffer, &iter);
/* Move the view (scrollbars) to the correct position */
gtk_adjustment_set_value
(GTK_ADJUSTMENT(textview->vadjustment),
undoinfo->window_position);
switch (undoinfo->action) {
case UNDO_ACTION_DELETE:
gtk_text_buffer_get_iter_at_offset(buffer, &iter, undoinfo->start_pos);
gtk_text_buffer_insert(buffer, &iter, undoinfo->text, -1);
break;
case UNDO_ACTION_INSERT:
gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, undoinfo->start_pos);
gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, undoinfo->end_pos);
gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
break;
case UNDO_ACTION_REPLACE_INSERT:
gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, undoinfo->start_pos);
gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, undoinfo->end_pos);
gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
/* "pull" another data structure from the list */
undoinfo = (UndoInfo *)undostruct->undo->data;
g_return_if_fail(undoinfo != NULL);
undostruct->redo = g_list_prepend(undostruct->redo, undoinfo);
undostruct->undo = g_list_remove(undostruct->undo, undoinfo);
g_return_if_fail(undoinfo->action == UNDO_ACTION_REPLACE_DELETE);
gtk_text_buffer_insert(buffer, &start_iter, undoinfo->text, -1);
break;
case UNDO_ACTION_REPLACE_DELETE:
g_warning("This should not happen. UNDO_REPLACE_DELETE");
break;
default:
g_assert_not_reached();
break;
}
undostruct->change_state_func(undostruct,
UNDO_STATE_UNCHANGED, UNDO_STATE_TRUE,
undostruct->change_state_data);
if (undostruct->undo == NULL)
undostruct->change_state_func(undostruct,
UNDO_STATE_FALSE,
UNDO_STATE_UNCHANGED,
undostruct->change_state_data);
undo_unblock(undostruct);
}
/**
* undo_redo:
* @w: not used
* @data: not used
*
* executes a redo request on the current document
**/
void undo_redo(UndoMain *undostruct)
{
UndoInfo *redoinfo;
GtkTextView *textview;
GtkTextBuffer *buffer;
GtkTextIter iter, start_iter, end_iter;
GtkTextMark *mark;
g_return_if_fail(undostruct != NULL);
if (undostruct->redo == NULL) return;
redoinfo = (UndoInfo *)undostruct->redo->data;
g_return_if_fail (redoinfo != NULL);
undostruct->undo = g_list_prepend(undostruct->undo, redoinfo);
undostruct->redo = g_list_remove(undostruct->redo, redoinfo);
textview = undostruct->textview;
buffer = gtk_text_view_get_buffer(textview);
undo_block(undostruct);
/* Check if there is a selection active */
mark = gtk_text_buffer_get_insert(buffer);
gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
gtk_text_buffer_place_cursor(buffer, &iter);
/* Move the view to the right position. */
gtk_adjustment_set_value(textview->vadjustment,
redoinfo->window_position);
switch (redoinfo->action) {
case UNDO_ACTION_INSERT:
gtk_text_buffer_get_iter_at_offset(buffer, &iter, redoinfo->start_pos);
gtk_text_buffer_insert(buffer, &iter, redoinfo->text, -1);
break;
case UNDO_ACTION_DELETE:
gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, redoinfo->start_pos);
gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, redoinfo->end_pos);
gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
break;
case UNDO_ACTION_REPLACE_DELETE:
gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, redoinfo->start_pos);
gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, redoinfo->end_pos);
gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
debug_print("UNDO_ACTION_REPLACE %s\n", redoinfo->text);
/* "pull" another data structure from the list */
redoinfo = (UndoInfo *)undostruct->redo->data;
g_return_if_fail(redoinfo != NULL);
undostruct->undo = g_list_prepend(undostruct->redo, redoinfo);
undostruct->redo = g_list_remove(undostruct->undo, redoinfo);
g_return_if_fail(redoinfo->action == UNDO_ACTION_REPLACE_DELETE);
gtk_text_buffer_insert(buffer, &start_iter, redoinfo->text, -1);
break;
case UNDO_ACTION_REPLACE_INSERT:
g_warning("This should not happen. Redo: UNDO_REPLACE_INSERT");
break;
default:
g_assert_not_reached();
break;
}
undostruct->change_state_func(undostruct,
UNDO_STATE_TRUE, UNDO_STATE_UNCHANGED,
undostruct->change_state_data);
if (undostruct->redo == NULL)
undostruct->change_state_func(undostruct,
UNDO_STATE_UNCHANGED,
UNDO_STATE_FALSE,
undostruct->change_state_data);
undo_unblock(undostruct);
}
void undo_block(UndoMain *undostruct)
{
GtkTextBuffer *buffer;
g_return_if_fail(GTK_IS_TEXT_VIEW(undostruct->textview));
buffer = gtk_text_view_get_buffer(undostruct->textview);
g_signal_handlers_block_by_func(buffer, undo_insert_text_cb, undostruct);
g_signal_handlers_block_by_func(buffer, undo_delete_text_cb, undostruct);
g_signal_handlers_block_by_func(buffer, undo_paste_clipboard_cb,
undostruct);
}
void undo_unblock(UndoMain *undostruct)
{
GtkTextBuffer *buffer;
g_return_if_fail(GTK_IS_TEXT_VIEW(undostruct->textview));
buffer = gtk_text_view_get_buffer(undostruct->textview);
g_signal_handlers_unblock_by_func(buffer, undo_insert_text_cb, undostruct);
g_signal_handlers_unblock_by_func(buffer, undo_delete_text_cb, undostruct);
g_signal_handlers_unblock_by_func(buffer, undo_paste_clipboard_cb,
undostruct);
}
void undo_insert_text_cb(GtkTextBuffer *textbuf, GtkTextIter *iter,
gchar *new_text, gint new_text_length,
UndoMain *undostruct)
{
gchar *text_to_insert;
gint pos;
if (prefs_common.undolevels <= 0) return;
pos = gtk_text_iter_get_offset(iter);
Xstrndup_a(text_to_insert, new_text, new_text_length, return);
undo_add(text_to_insert, pos, pos + g_utf8_strlen(text_to_insert, -1),
UNDO_ACTION_INSERT, undostruct);
}
void undo_delete_text_cb(GtkTextBuffer *textbuf, GtkTextIter *start,
GtkTextIter *end, UndoMain *undostruct)
{
gchar *text_to_delete;
gint start_pos, end_pos;
if (prefs_common.undolevels <= 0) return;
text_to_delete = gtk_text_buffer_get_text(textbuf, start, end, FALSE);
if (!text_to_delete || !*text_to_delete) return;
start_pos = gtk_text_iter_get_offset(start);
end_pos = gtk_text_iter_get_offset(end);
undo_add(text_to_delete, start_pos, end_pos, UNDO_ACTION_DELETE,
undostruct);
g_free(text_to_delete);
}
void undo_paste_clipboard_cb(GtkTextView *textview, UndoMain *undostruct)
{
#if 0
if (editable->clipboard_text == NULL) return;
#endif
if (prefs_common.undolevels > 0)
if (undo_get_selection(textview, NULL, NULL))
undostruct->paste = TRUE;
}
/**
* undo_get_selection:
* @text: Text to get the selection from
* @start: return here the start position of the selection
* @end: return here the end position of the selection
*
* Gets the current selection for View
*
* Return Value: TRUE if there is a selection active, FALSE if not
**/
static gint undo_get_selection(GtkTextView *textview, guint *start, guint *end)
{
GtkTextBuffer *buffer;
GtkTextIter start_iter, end_iter;
guint start_pos, end_pos;
buffer = gtk_text_view_get_buffer(textview);
gtk_text_buffer_get_selection_bounds(buffer, &start_iter, &end_iter);
start_pos = gtk_text_iter_get_offset(&start_iter);
end_pos = gtk_text_iter_get_offset(&end_iter);
/* The user can select from end to start too. If so, swap it*/
if (end_pos < start_pos) {
guint swap_pos;
swap_pos = end_pos;
end_pos = start_pos;
start_pos = swap_pos;
}
if (start != NULL)
*start = start_pos;
if (end != NULL)
*end = end_pos;
if ((start_pos > 0 || end_pos > 0) && (start_pos != end_pos))
return TRUE;
else
return FALSE;
}