Author: erikharrison
Date: 2007-01-15 21:47:38 +0000 (Mon, 15 Jan 2007)
New Revision: 24498
Modified:
mousepad/trunk/src/callback.c
mousepad/trunk/src/undo.c
mousepad/trunk/src/undo.h
Log:
Moving undo system changes from local tree to Xfce svn, and resuming work there
Modified: mousepad/trunk/src/callback.c
===================================================================
--- mousepad/trunk/src/callback.c 2007-01-15 21:38:34 UTC (rev 24497)
+++ mousepad/trunk/src/callback.c 2007-01-15 21:47:38 UTC (rev 24498)
@@ -264,14 +264,12 @@
void cb_edit_undo(StructData *sd)
{
- while (undo_undo(sd->mainwin->textbuffer)) {}
+ undo_undo(sd->mainwin->textbuffer);
}
void cb_edit_redo(StructData *sd)
{
- while (undo_redo(sd->mainwin->textbuffer)) {
- undo_set_sequency(TRUE);
- }
+ undo_redo(sd->mainwin->textbuffer);
}
void cb_edit_cut(StructData *sd)
Modified: mousepad/trunk/src/undo.c
===================================================================
--- mousepad/trunk/src/undo.c 2007-01-15 21:38:34 UTC (rev 24497)
+++ mousepad/trunk/src/undo.c 2007-01-15 21:47:38 UTC (rev 24498)
@@ -26,14 +26,32 @@
#define DV(x)
+/* This struct is used to carry info about a undo/redo steps.
+ * ->next holds a pointer to the next element in the stack.
+ See the GTrashStack docs for more (vague) info.
+ * ->command holds the type of action, backspace, delete, or inset.
+ * ->start is the _character_ offset of the beginning of the step
+ * ->end is offset for the end of the step, mostly for deletions
+ * ->seq is a flag for cases (like overwriting text with a paste)
+ * where one user level undo involves multiple steps. For
+ * example, overwriting text via a paste involves an deletion
+ * and an insertion. The insertion will be at the top of the
+ * stack with the seq flag set, the deletion below it, with
+ * the flag unset. If a step with the flag set is undone or
+ * redone, we perform the next step as well, until we undo
+ * or redo a step without the flag set
+ * ->sty, the text of the change
+ */
typedef struct {
+ GTrashStack *next;
gint command;
gint start;
gint end;
- gboolean seq; /* sequency flag */
+ gboolean seq;
gchar *str;
} UndoInfo;
+/* For the command field in UndoInfo*/
enum {
BS = 0,
DEL,
@@ -43,14 +61,26 @@
static GtkWidget *undo_menu_item = NULL;
static GtkWidget *redo_menu_item = NULL;
-static GList *undo_list = NULL;
-static GList *redo_list = NULL;
+static GTrashStack *undo_stack = NULL;
+static GTrashStack *redo_stack = NULL;
+/*undo_gstr acts like a temp for UndoInfo->str, growing or shrinking
+ with insertions until a word boundry is set, and it's copied to
+ ui->str */
static GString *undo_gstr;
+/*we keep one of these around to "build up" before copying it
+into the undo list when a word boundry is met*/
static UndoInfo *ui_tmp;
+/*doesn't seem to do anything but store 0 so that we can know when we're
+out of data in the undo_list EXPME*/
static gint step_modif;
static GtkWidget *view;
+/*I dunno why we track whether or not overwrite is on. I mean, it
+seems like a good idea, I guess EXPME*/
static gboolean overwrite_mode = FALSE;
+/*instead of passing stuff in through arguments, we use locals. Fun*/
static guint keyval;
+/*on occasion we need the previous keyval to determine whether or not we've
+hit a word boundry*/
static guint prev_keyval;/*, keyevent_setval(0); */
static void cb_toggle_overwrite(void)
@@ -61,22 +91,31 @@
static void undo_clear_undo_info(void)
{
- while (g_list_length(undo_list)) {
- g_free(((UndoInfo *)undo_list->data)->str);
- g_free(undo_list->data);
- undo_list = g_list_delete_link(undo_list, undo_list);
+ UndoInfo *ui;
+
+ /*Peeking is O(1) while checking the height is O(N)*/
+ while (g_trash_stack_peek(&undo_stack)) {
+ ui = (UndoInfo *)g_trash_stack_pop(&undo_stack);
+ g_free(ui->str);
+ g_free(ui);
}
}
static void undo_clear_redo_info(void)
{
- while (g_list_length(redo_list)) {
- g_free(((UndoInfo *)redo_list->data)->str);
- g_free(redo_list->data);
- redo_list = g_list_delete_link(redo_list, redo_list);
+ UndoInfo *ri;
+
+ while (g_trash_stack_peek(&redo_stack)) {
+ ri = (UndoInfo *)g_trash_stack_pop(&redo_stack);
+ g_free(ri->str);
+ g_free(ri);
}
+
}
+/*takes command, offsets and a string, and builds a UndoInfo object, which
+we stuff into the undo_list. Note that you can't set ->seq this way.
+Also (FIXME) we have no use for the buffer passed to us*/
static void undo_append_undo_info(GtkTextBuffer *buffer, gint command, gint
start, gint end, gchar *str)
{
UndoInfo *ui = g_malloc(sizeof(UndoInfo));
@@ -86,10 +125,25 @@
ui->end = end;
ui->seq = FALSE;
ui->str = str;
- undo_list = g_list_append(undo_list, ui);
-DV(g_print("undo_cb: %d %s (%d-%d)\n", command, str, start, end));
+ g_trash_stack_push(&undo_stack, ui);
+DV(g_print("undo_append_undo_info: command: %d string: %s range: (%d-%d)\n",
command, str, start, end));
}
+/* Here it is. The monster that drives everything around here and does
+ * most of the work. The various callbacks hooked into the buffer pass
+ * the buck here. Since this function will be called for every little
+ * change to the buffer it attempts to determine if the current change
+ * is part of an ongoing action the user will want to undo/redo in one
+ * go (confusingly, also called a sequence) or the start of a new
+ * sequence. If it's part of an ongoing sequence, it builds up data in
+ * the undo_gstr or ui_tmp variables. If it's the start of a new
+ * sequence, it copies the ui_tmp and undo_gstr data into the
+ * undo_info list (via undo_append_undo_info), and then clears ui_tmp
+ * and undo_gstr for the next run.
+ *
+ * Note that a sequence is not just a series of insertions and a word
+ * boundry, but also a linefeed, a deletion or a backspace
+ */
static void undo_create_undo_info(GtkTextBuffer *buffer, gint command, gint
start, gint end)
{
GtkTextIter start_iter, end_iter;
@@ -98,6 +152,9 @@
gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
+
+ /*The callbacks have the text - why do we get it from the buffer?
+ For redo, maybe? EXPME*/
str = gtk_text_buffer_get_text(buffer, &start_iter, &end_iter, FALSE);
if (undo_gstr->len) {
@@ -168,7 +225,7 @@
g_string_append(undo_gstr, str);
} else
undo_append_undo_info(buffer, command, start, end,
g_strdup(str));
-
+DV(g_print("undo_create_undo_info: ui_tmp->command: %d", ui_tmp->command));
undo_clear_redo_info();
prev_keyval = keyval;
keyevent_setval(0);
@@ -186,9 +243,13 @@
end = gtk_text_iter_get_offset(end_iter);
DV(g_print("delete-range: keyval = 0x%x\n", keyval));
+
+ /*Overwriting text from the menubar or context menu. I have
+ no idea why ovewriting from the context menu generates a keyevent
+ but inserting does not (see comments in the inset-text callback*/
+ if (!keyval && prev_keyval)
+ undo_set_sequency(TRUE);
- if (!keyval && prev_keyval)
- undo_set_sequency(TRUE);
if (keyval == 0x10000 && prev_keyval > 0x10000) /* for Ctrl+V overwrite
*/
undo_set_sequency(TRUE);
if (keyval == GDK_BackSpace)
@@ -206,14 +267,6 @@
end = gtk_text_iter_get_offset(iter);
start = end - g_utf8_strlen(str, -1);
-DV(g_print("insert-text: keyval = 0x%x\n", keyevent_getval()));
-
- if (!keyval && prev_keyval)
- undo_set_sequency(TRUE);
-#if 0
- if (keyval == 0x10000 && prev_keyval > 0x10000) /* don't need probably
*/
- undo_set_sequency(TRUE);
-#endif
undo_create_undo_info(buffer, INS, start, end);
}
@@ -239,15 +292,19 @@
set_main_window_title_with_asterisk(TRUE);
}
+/*I'm this is only called by init, and when we save. We call it from
+the save call back, so that we can properly handle the asterisk in the
+titlebar and warn that the document is altered after a save, but
+retain the undo history. The leads to (FIXME) bug 2730*/
void undo_reset_step_modif(void)
{
- step_modif = g_list_length(undo_list);
+ step_modif = g_trash_stack_height(&undo_stack);
DV(g_print("undo_reset_step_modif: Reseted step_modif by %d\n", step_modif));
}
static void undo_check_step_modif(GtkTextBuffer *buffer)
{
- if (g_list_length(undo_list) == step_modif) {
+ if (g_trash_stack_height(&undo_stack) == step_modif) {
g_signal_handlers_block_by_func(G_OBJECT(buffer),
G_CALLBACK(cb_modified_changed), NULL);
gtk_text_buffer_set_modified(buffer, FALSE);
g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
G_CALLBACK(cb_modified_changed), NULL);
@@ -255,6 +312,11 @@
}
}
+/*used by undo_init to attach the various callbacks we need
+ to generate the undo history. Of course, it is a bit of
+ a misnomer, as it only connects tp signals from the buffer,
+ undo_init connects to signals emitted by the textview
+ */
static gint undo_connect_signal(GtkTextBuffer *buffer)
{
g_signal_connect(G_OBJECT(buffer), "delete-range",
@@ -266,18 +328,22 @@
G_CALLBACK(cb_modified_changed), NULL);
}
+/*Sets up this party. It also tracks if it's been called multiple times,
+ and cleans up after itself (sorta) if it has. Which is odd, as only
+ we only call it during startup.*/
void undo_init(GtkWidget *textview, GtkTextBuffer *buffer, GtkWidget *menubar)
{
GtkItemFactory *ifactory;
static guint init_flag = 0; /* TODO: divide to undo_clear() */
- if (undo_list)
+ if (undo_stack != NULL)
undo_clear_undo_info();
- if (redo_list)
+ if (redo_stack != NULL)
undo_clear_redo_info();
undo_reset_step_modif();
DV(g_print("undo_init: list reseted\n"));
+ /*This is retarded*/
if (!undo_menu_item) {
ifactory = gtk_item_factory_from_widget(menubar);
undo_menu_item = gtk_item_factory_get_widget(ifactory,
"/Edit/Undo");
@@ -288,7 +354,7 @@
if (!init_flag) {
g_signal_connect(G_OBJECT(textview), "toggle-overwrite",
- G_CALLBACK(cb_toggle_overwrite), NULL);
+ G_CALLBACK(cb_toggle_overwrite), NULL); /*I dunno why
we care*/
view = textview;
init_flag = undo_connect_signal(buffer);
keyevent_setval(0);
@@ -296,10 +362,15 @@
/* ui_tmp->str = g_strdup(""); */
undo_gstr = g_string_new("");
}
+ /*Not sure why we do this here, and not in the insert callback*/
ui_tmp->command = INS;
undo_gstr = g_string_erase(undo_gstr, 0, -1);
}
+/*Straight forward. When performing the undo/redo, we don't want our
+ deletion and insertion callbacks getting called and going all wonky
+ so we provide a couple of functions to block and unblock. This
+ constitutes all the code in this file which doesn't suck*/
gint undo_block_signal(GtkTextBuffer *buffer)
{
return
@@ -316,37 +387,58 @@
g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
G_CALLBACK(cb_insert_text), buffer);
}
-gint undo_disconnect_signal(GtkTextBuffer *buffer) /*must be not needed */
-{
- return
- g_signal_handlers_disconnect_by_func(G_OBJECT(buffer),
G_CALLBACK(cb_delete_range), buffer) +
-/* g_signal_handlers_disconnect_by_func(G_OBJECT(buffer),
G_CALLBACK(cb_modified_changed), buffer) + */
- g_signal_handlers_disconnect_by_func(G_OBJECT(buffer),
G_CALLBACK(cb_insert_text), buffer);
-}
+/* More in the world of the bizarre sequency flag. If we ever need to set the
+ flag, by definition the UndoInfo struct for the undo step is already on
+ the stack, and a new temp object already being filled up - probably
+ getting ready to be saved itself. So we need a way to set the flag
+ on the UndoInfo struct at the top of the stack.
+ TaaDaa!
+ */
void undo_set_sequency(gboolean seq)
{
UndoInfo *ui;
-
- if (g_list_length(undo_list)) {
- ui = g_list_last(undo_list)->data;
+
+ ui = g_trash_stack_peek(&undo_stack);
+ if (ui != NULL) {
ui->seq = seq;
}
DV(g_print("<undo_set_sequency: %d>\n", seq));
}
-gboolean undo_undo(GtkTextBuffer *buffer)
+/* Pretty straightforward, believe it or not. First thing it does is
+ * put any data from undo_gstr in a UndoInfo struct on the stack.
+ * Once that's done, it sees if there is anything on the stack,
+ * pops it, blocks the undo callbacks on the buffer, and inspects
+ * the command on the UndoInfo struct. If were undoing an insertion
+ * then we need to delete text. Grab iterators from the offsets
+ * in the UndoInfo object, and delete that sucker. If it is a
+ * deletion were undoing, then insert the text back in. REMEMBER:
+ * overwritten text winds up being handled via a two part process
+ * with the sequency flag. If the sequency flag is set for the
+ * NEXT step in the stack, then (Lord Help Us and also FIXME)
+ * we return true, so we tell the caller to call us again so
+ * we can do it all over again. If, on the other hand, the
+ * sequency flag is not set, then we tidy up after ourselves,
+ * putting the curser in the right place, and scrolling the textview,
+ * setting the redo menu item sensitive, fixing the title bar if
+ * necessary and finally set the undo item insensitive if we've
+ * emptied the stack. Whew!
+ */
+void undo_undo(GtkTextBuffer *buffer)
{
GtkTextIter start_iter, end_iter;
UndoInfo *ui;
if (undo_gstr->len) {
+DV(g_print("undo_undo_real: ui_tmp->command %d", ui_tmp->command));
undo_append_undo_info(buffer, ui_tmp->command, ui_tmp->start,
ui_tmp->end, g_strdup(undo_gstr->str));
undo_gstr = g_string_erase(undo_gstr, 0, -1);
}
- if (g_list_length(undo_list)) {
+ if ((g_trash_stack_peek(&undo_stack)) != NULL) {
undo_block_signal(buffer);
- ui = g_list_last(undo_list)->data;
+ ui = (UndoInfo *)(g_trash_stack_pop(&undo_stack));
+DV(g_print("undo_undo_real: ui->command %d", ui->command));
gtk_text_buffer_get_iter_at_offset(buffer, &start_iter,
ui->start);
switch (ui->command) {
case INS:
@@ -356,14 +448,15 @@
default:
gtk_text_buffer_insert(buffer, &start_iter, ui->str,
-1);
}
- redo_list = g_list_append(redo_list, ui);
- undo_list = g_list_delete_link(undo_list,
g_list_last(undo_list));
-DV(g_print("cb_edit_undo: undo list left %d\n", g_list_length(undo_list)));
+ g_trash_stack_push(&redo_stack, ui);
+DV(g_print("cb_edit_undo: undo list left %d\n",
g_trash_stack_height(&undo_stack)));
undo_unblock_signal(buffer);
undo_check_step_modif(buffer);
- if (g_list_length(undo_list)) {
- if (((UndoInfo *)g_list_last(undo_list)->data)->seq)
- return TRUE;
+ if (undo_stack != NULL) {
+ if (((UndoInfo *)g_trash_stack_peek(&undo_stack))->seq)
{
+ undo_undo(buffer);
+ return;
+ }
} else
gtk_widget_set_sensitive(undo_menu_item, FALSE);
gtk_widget_set_sensitive(redo_menu_item, TRUE);
@@ -373,17 +466,18 @@
gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(view), &start_iter,
0.1, FALSE, 0.5, 0.5);
}
- return FALSE;
+ return;
}
-gboolean undo_redo(GtkTextBuffer *buffer)
+/*yeah, so, basically this is exactly like undo_undo, except simpler*/
+void undo_redo(GtkTextBuffer *buffer)
{
GtkTextIter start_iter, end_iter;
UndoInfo *ri;
- if (g_list_length(redo_list)) {
+ if (redo_stack != NULL) {
undo_block_signal(buffer);
- ri = g_list_last(redo_list)->data;
+ ri = (UndoInfo *)(g_trash_stack_pop(&redo_stack));
gtk_text_buffer_get_iter_at_offset(buffer, &start_iter,
ri->start);
switch (ri->command) {
case INS:
@@ -393,19 +487,20 @@
gtk_text_buffer_get_iter_at_offset(buffer, &end_iter,
ri->end);
gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
}
- undo_list = g_list_append(undo_list, ri);
- redo_list = g_list_delete_link(redo_list,
g_list_last(redo_list));
-DV(g_print("cb_edit_redo: redo list left %d\n", g_list_length(redo_list)));
+ g_trash_stack_push(&undo_stack, ri);
+DV(g_print("cb_edit_redo: redo list left %d\n",
g_trash_stack_height(&redo_stack)));
undo_unblock_signal(buffer);
undo_check_step_modif(buffer);
- if (ri->seq)
- return TRUE;
- if (!g_list_length(redo_list))
+ if (ri->seq) {
+ undo_redo(buffer);
+ return;
+ }
+ if (redo_stack == NULL)
gtk_widget_set_sensitive(redo_menu_item, FALSE);
gtk_widget_set_sensitive(undo_menu_item, TRUE);
gtk_text_buffer_place_cursor(buffer, &start_iter);
gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(view), &start_iter,
0.1, FALSE, 0.5, 0.5);
}
- return FALSE;
+ return;
}
Modified: mousepad/trunk/src/undo.h
===================================================================
--- mousepad/trunk/src/undo.h 2007-01-15 21:38:34 UTC (rev 24497)
+++ mousepad/trunk/src/undo.h 2007-01-15 21:47:38 UTC (rev 24498)
@@ -28,7 +28,7 @@
gint undo_unblock_signal(GtkTextBuffer *buffer);
gint undo_disconnect_signal(GtkTextBuffer *buffer);
void undo_set_sequency(gboolean seq);
-gboolean undo_undo(GtkTextBuffer *buffer);
-gboolean undo_redo(GtkTextBuffer *buffer);
+void undo_undo(GtkTextBuffer *buffer);
+void undo_redo(GtkTextBuffer *buffer);
#endif /* _UNDO_H */
_______________________________________________
Xfce4-commits mailing list
[email protected]
http://foo-projects.org/mailman/listinfo/xfce4-commits