Finished new Undo system. Now some more actions can be undone and also redone.
git-svn-id: https://geany.svn.sourceforge.net/svnroot/geany/trunk@878 ea778897-0a13-0410-b9d1-a72fbfd435f5
This commit is contained in:
parent
5815ae99a9
commit
028fe98373
@ -1,6 +1,13 @@
|
||||
2006-10-10 Enrico Tröger <enrico.troeger@uvena.de>
|
||||
|
||||
* src/document.c: Connect only once to the "sci-notify" signal.
|
||||
* scintilla/CellBuffer.cxx, scintilla/Document.cxx,
|
||||
scintilla/include/Scintilla.h:
|
||||
Applied patch from Armel Asselin (thanks). It adds SC_START_ACTION
|
||||
notification which is required for Geany's new Undo system.
|
||||
* src/document.c, src/sci_cb.c, src/callbacks.c:
|
||||
Finished new Undo system. Now some more actions can be undone and
|
||||
also redone.
|
||||
|
||||
|
||||
2006-10-09 Nick Treleaven <nick.treleaven@btinternet.com>
|
||||
|
@ -2432,15 +2432,12 @@ on_encoding_change (GtkMenuItem *menuitem,
|
||||
gint idx = document_get_cur_idx();
|
||||
guint i = GPOINTER_TO_INT(user_data);
|
||||
|
||||
if (app->ignore_callback || idx < 0 || encodings[i].charset == NULL ||
|
||||
if (app->ignore_callback || ! DOC_IDX_VALID(idx) || encodings[i].charset == NULL ||
|
||||
utils_strcmp(encodings[i].charset, doc_list[idx].encoding)) return;
|
||||
|
||||
// old charset string will be freed with the undo buffer
|
||||
document_undo_add(idx, UNDO_ENCODING, doc_list[idx].encoding);
|
||||
doc_list[idx].encoding = g_strdup(encodings[i].charset);
|
||||
ui_update_statusbar(idx, -1);
|
||||
gtk_widget_set_sensitive(lookup_widget(app->window, "menu_write_unicode_bom1"),
|
||||
utils_is_unicode_charset(doc_list[idx].encoding));
|
||||
document_undo_add(idx, UNDO_ENCODING, g_strdup(doc_list[idx].encoding));
|
||||
|
||||
document_set_encoding(idx, encodings[i].charset);
|
||||
}
|
||||
|
||||
|
||||
@ -2492,9 +2489,10 @@ on_menu_write_unicode_bom1_toggled (GtkCheckMenuItem *checkmenuitem,
|
||||
|
||||
if (idx == -1 || ! doc_list[idx].is_valid) return;
|
||||
|
||||
document_undo_add(idx, UNDO_BOM, GINT_TO_POINTER(doc_list[idx].has_bom));
|
||||
|
||||
doc_list[idx].has_bom = ! doc_list[idx].has_bom;
|
||||
|
||||
document_undo_add(idx, UNDO_BOM, GINT_TO_POINTER(! doc_list[idx].has_bom));
|
||||
ui_update_statusbar(idx, -1);
|
||||
}
|
||||
}
|
||||
|
217
src/document.c
217
src/document.c
@ -68,6 +68,10 @@ static gint
|
||||
document_replace_range(gint idx, const gchar *find_text, const gchar *replace_text,
|
||||
gint flags, gint start, gint end, gboolean escaped_chars);
|
||||
|
||||
static void document_undo_clear(gint idx);
|
||||
static void document_redo_add(gint idx, guint type, gpointer data);
|
||||
|
||||
|
||||
|
||||
/* returns the index of the notebook page which has the given filename
|
||||
* is_tm_filename is needed when passing TagManager filenames because they are
|
||||
@ -362,7 +366,6 @@ gboolean document_remove(guint page_num)
|
||||
doc_list[idx].has_bom = FALSE;
|
||||
doc_list[idx].tm_file = NULL;
|
||||
document_undo_clear(idx);
|
||||
document_redo_clear(idx);
|
||||
if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(app->notebook)) == 0)
|
||||
{
|
||||
ui_update_tag_list(-1, FALSE);
|
||||
@ -388,10 +391,19 @@ void document_new_file(filetype *ft)
|
||||
|
||||
g_assert(idx != -1);
|
||||
|
||||
sci_set_undo_collection(doc_list[idx].sci, FALSE); // avoid creation of an undo action
|
||||
sci_clear_all(doc_list[idx].sci);
|
||||
sci_set_text(doc_list[idx].sci, template);
|
||||
g_free(template);
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
sci_set_eol_mode(doc_list[idx].sci, SC_EOL_CRLF);
|
||||
#else
|
||||
sci_set_eol_mode(doc_list[idx].sci, SC_EOL_LF);
|
||||
#endif
|
||||
sci_set_undo_collection(doc_list[idx].sci, TRUE);
|
||||
sci_empty_undo_buffer(doc_list[idx].sci);
|
||||
|
||||
doc_list[idx].encoding = g_strdup(encodings[app->pref_editor_default_encoding].charset);
|
||||
//document_set_filetype(idx, (ft == NULL) ? filetypes[GEANY_FILETYPES_ALL] : ft);
|
||||
document_set_filetype(idx, ft); // also clears taglist
|
||||
@ -402,13 +414,8 @@ void document_new_file(filetype *ft)
|
||||
doc_list[idx].changed = FALSE;
|
||||
document_set_text_changed(idx);
|
||||
ui_document_show_hide(idx); //update the document menu
|
||||
#ifdef G_OS_WIN32
|
||||
sci_set_eol_mode(doc_list[idx].sci, SC_EOL_CRLF);
|
||||
#else
|
||||
sci_set_eol_mode(doc_list[idx].sci, SC_EOL_LF);
|
||||
#endif
|
||||
|
||||
sci_set_line_numbers(doc_list[idx].sci, app->show_linenumber_margin, 0);
|
||||
sci_empty_undo_buffer(doc_list[idx].sci);
|
||||
sci_goto_pos(doc_list[idx].sci, 0, TRUE);
|
||||
|
||||
// "the" SCI signal (connect after initial setup(i.e. adding text))
|
||||
@ -637,15 +644,17 @@ int document_open_file(gint idx, const gchar *filename, gint pos, gboolean reado
|
||||
if (idx == -1) return -1; // really should not happen
|
||||
|
||||
// set editor mode and add the text to the ScintillaObject
|
||||
sci_set_text(doc_list[idx].sci, data); // NULL terminated data; avoids modifying sci
|
||||
sci_set_undo_collection(doc_list[idx].sci, FALSE); // avoid creation of an undo action
|
||||
sci_empty_undo_buffer(doc_list[idx].sci);
|
||||
sci_set_text(doc_list[idx].sci, data); // NULL terminated data
|
||||
|
||||
editor_mode = utils_get_line_endings(data, size);
|
||||
sci_set_eol_mode(doc_list[idx].sci, editor_mode);
|
||||
|
||||
sci_set_line_numbers(doc_list[idx].sci, app->show_linenumber_margin, 0);
|
||||
sci_set_savepoint(doc_list[idx].sci);
|
||||
sci_empty_undo_buffer(doc_list[idx].sci);
|
||||
// get the modification time from file and keep it
|
||||
doc_list[idx].mtime = st.st_mtime;
|
||||
|
||||
sci_set_undo_collection(doc_list[idx].sci, TRUE);
|
||||
|
||||
doc_list[idx].mtime = st.st_mtime; // get the modification time from file and keep it
|
||||
doc_list[idx].changed = FALSE;
|
||||
doc_list[idx].file_name = g_strdup(utf8_filename);
|
||||
doc_list[idx].encoding = enc;
|
||||
@ -676,6 +685,7 @@ int document_open_file(gint idx, const gchar *filename, gint pos, gboolean reado
|
||||
else
|
||||
{ // reloading
|
||||
document_update_tag_list(idx, TRUE);
|
||||
document_undo_clear(idx);
|
||||
}
|
||||
|
||||
document_set_text_changed(idx);
|
||||
@ -1494,11 +1504,26 @@ void document_ensure_final_newline(gint idx)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void document_set_encoding(gint idx, const gchar *new_encoding)
|
||||
{
|
||||
if (! DOC_IDX_VALID(idx) || new_encoding == NULL ||
|
||||
utils_strcmp(new_encoding, doc_list[idx].encoding)) return;
|
||||
|
||||
g_free(doc_list[idx].encoding);
|
||||
doc_list[idx].encoding = g_strdup(new_encoding);
|
||||
|
||||
ui_update_statusbar(idx, -1);
|
||||
gtk_widget_set_sensitive(lookup_widget(app->window, "menu_write_unicode_bom1"),
|
||||
utils_is_unicode_charset(doc_list[idx].encoding));
|
||||
}
|
||||
|
||||
|
||||
/* own Undo / Redo implementation to be able to undo / redo changes
|
||||
* to the encoding or the Unicode BOM (which are Scintilla independet).
|
||||
* All Scintilla events are stored in the undo / redo buffer and are passed through. */
|
||||
|
||||
/* Clears the Undo buffer (to be called after saving a file or when closing the document) */
|
||||
/* Clears the Undo and Redo buffer (to be called when reloading or closing the document) */
|
||||
void document_undo_clear(gint idx)
|
||||
{
|
||||
undo_action *a;
|
||||
@ -1519,17 +1544,6 @@ void document_undo_clear(gint idx)
|
||||
}
|
||||
doc_list[idx].undo_actions = NULL;
|
||||
|
||||
doc_list[idx].changed = FALSE;
|
||||
if (! app->quitting) document_set_text_changed(idx);
|
||||
}
|
||||
|
||||
|
||||
/* Clears the Redo buffer (to be called after saving a file or when closing the document) */
|
||||
void document_redo_clear(gint idx)
|
||||
{
|
||||
/*
|
||||
undo_action *a;
|
||||
|
||||
while (g_trash_stack_height(&doc_list[idx].redo_actions) > 0)
|
||||
{
|
||||
a = g_trash_stack_pop(&doc_list[idx].redo_actions);
|
||||
@ -1548,7 +1562,9 @@ void document_redo_clear(gint idx)
|
||||
|
||||
doc_list[idx].changed = FALSE;
|
||||
if (! app->quitting) document_set_text_changed(idx);
|
||||
*/
|
||||
|
||||
//geany_debug("%s: new undo stack height: %d, new redo stack height: %d", __func__,
|
||||
//g_trash_stack_height(&doc_list[idx].undo_actions), g_trash_stack_height(&doc_list[idx].redo_actions));
|
||||
}
|
||||
|
||||
|
||||
@ -1556,7 +1572,7 @@ void document_undo_add(gint idx, guint type, gpointer data)
|
||||
{
|
||||
undo_action *action;
|
||||
|
||||
if (idx == -1 || ! doc_list[idx].is_valid) return;
|
||||
if (! DOC_IDX_VALID(idx)) return;
|
||||
|
||||
action = g_new0(undo_action, 1);
|
||||
action->type = type;
|
||||
@ -1568,18 +1584,14 @@ void document_undo_add(gint idx, guint type, gpointer data)
|
||||
document_set_text_changed(idx);
|
||||
ui_update_popup_reundo_items(idx);
|
||||
|
||||
{
|
||||
geany_debug("%s: new stack height: %d, added type: %d", __func__,
|
||||
g_trash_stack_height(&doc_list[idx].undo_actions), action->type);
|
||||
}
|
||||
//geany_debug("%s: new stack height: %d, added type: %d", __func__,
|
||||
//g_trash_stack_height(&doc_list[idx].undo_actions), action->type);
|
||||
}
|
||||
|
||||
|
||||
gboolean document_can_undo(gint idx)
|
||||
{
|
||||
return sci_can_undo(doc_list[idx].sci);
|
||||
|
||||
if (idx == -1 || ! doc_list[idx].is_valid) return FALSE;
|
||||
if (! DOC_IDX_VALID(idx)) return FALSE;
|
||||
|
||||
if (g_trash_stack_height(&doc_list[idx].undo_actions) > 0 || sci_can_undo(doc_list[idx].sci))
|
||||
return TRUE;
|
||||
@ -1588,30 +1600,18 @@ gboolean document_can_undo(gint idx)
|
||||
}
|
||||
|
||||
|
||||
gboolean document_can_redo(gint idx)
|
||||
{
|
||||
if (idx == -1 || ! doc_list[idx].is_valid) return FALSE;
|
||||
|
||||
return sci_can_redo(doc_list[idx].sci);
|
||||
}
|
||||
|
||||
|
||||
void document_undo(gint idx)
|
||||
{
|
||||
undo_action *action;
|
||||
|
||||
#if 1
|
||||
sci_undo(doc_list[idx].sci);
|
||||
return;
|
||||
#endif
|
||||
|
||||
if (idx == -1 || ! doc_list[idx].is_valid) return;
|
||||
if (! DOC_IDX_VALID(idx)) return;
|
||||
|
||||
action = g_trash_stack_pop(&doc_list[idx].undo_actions);
|
||||
|
||||
if (action == NULL)
|
||||
{
|
||||
// fallback, should not be necessary
|
||||
geany_debug("%s: fallback used", __func__);
|
||||
sci_undo(doc_list[idx].sci);
|
||||
}
|
||||
else
|
||||
@ -1620,13 +1620,15 @@ void document_undo(gint idx)
|
||||
{
|
||||
case UNDO_SCINTILLA:
|
||||
{
|
||||
geany_debug("undo: Scintilla");
|
||||
document_redo_add(idx, UNDO_SCINTILLA, NULL);
|
||||
|
||||
sci_undo(doc_list[idx].sci);
|
||||
break;
|
||||
}
|
||||
case UNDO_BOM:
|
||||
{
|
||||
geany_debug("undo: BOM");
|
||||
document_redo_add(idx, UNDO_BOM, GINT_TO_POINTER(doc_list[idx].has_bom));
|
||||
|
||||
doc_list[idx].has_bom = GPOINTER_TO_INT(action->data);
|
||||
ui_update_statusbar(idx, -1);
|
||||
ui_document_show_hide(idx);
|
||||
@ -1634,28 +1636,123 @@ void document_undo(gint idx)
|
||||
}
|
||||
case UNDO_ENCODING:
|
||||
{
|
||||
geany_debug("undo: Encoding");
|
||||
doc_list[idx].encoding = (gchar*) action->data;
|
||||
ui_update_statusbar(idx, -1);
|
||||
encodings_select_radio_item(doc_list[idx].encoding);
|
||||
gtk_widget_set_sensitive(lookup_widget(app->window, "menu_write_unicode_bom1"),
|
||||
utils_is_unicode_charset(doc_list[idx].encoding));
|
||||
// use the "old" encoding
|
||||
document_redo_add(idx, UNDO_ENCODING, g_strdup(doc_list[idx].encoding));
|
||||
|
||||
document_set_encoding(idx, (const gchar*)action->data);
|
||||
|
||||
app->ignore_callback = TRUE;
|
||||
encodings_select_radio_item((const gchar*)action->data);
|
||||
app->ignore_callback = FALSE;
|
||||
|
||||
g_free(action->data);
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
g_free(action); // free the action which was taken from the stack
|
||||
|
||||
if (g_trash_stack_height(&doc_list[idx].undo_actions) == 0)
|
||||
{
|
||||
doc_list[idx].changed = FALSE;
|
||||
document_set_text_changed(idx);
|
||||
}
|
||||
else
|
||||
doc_list[idx].changed = TRUE;
|
||||
|
||||
if (g_trash_stack_height(&doc_list[idx].undo_actions) == 0) doc_list[idx].changed = FALSE;
|
||||
ui_update_popup_reundo_items(idx);
|
||||
geany_debug("%s: new stack height: %d", __func__, g_trash_stack_height(&doc_list[idx].undo_actions));
|
||||
//geany_debug("%s: new stack height: %d", __func__, g_trash_stack_height(&doc_list[idx].undo_actions));
|
||||
}
|
||||
|
||||
|
||||
gboolean document_can_redo(gint idx)
|
||||
{
|
||||
if (! DOC_IDX_VALID(idx)) return FALSE;
|
||||
|
||||
if (g_trash_stack_height(&doc_list[idx].redo_actions) > 0 || sci_can_redo(doc_list[idx].sci))
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
void document_redo(gint idx)
|
||||
{
|
||||
if (idx == -1 || ! doc_list[idx].is_valid) return;
|
||||
undo_action *action;
|
||||
|
||||
sci_redo(doc_list[idx].sci);
|
||||
if (! DOC_IDX_VALID(idx)) return;
|
||||
|
||||
action = g_trash_stack_pop(&doc_list[idx].redo_actions);
|
||||
|
||||
if (action == NULL)
|
||||
{
|
||||
// fallback, should not be necessary
|
||||
geany_debug("%s: fallback used", __func__);
|
||||
sci_redo(doc_list[idx].sci);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (action->type)
|
||||
{
|
||||
case UNDO_SCINTILLA:
|
||||
{
|
||||
document_undo_add(idx, UNDO_SCINTILLA, NULL);
|
||||
|
||||
sci_redo(doc_list[idx].sci);
|
||||
break;
|
||||
}
|
||||
case UNDO_BOM:
|
||||
{
|
||||
document_undo_add(idx, UNDO_BOM, GINT_TO_POINTER(doc_list[idx].has_bom));
|
||||
|
||||
doc_list[idx].has_bom = GPOINTER_TO_INT(action->data);
|
||||
ui_update_statusbar(idx, -1);
|
||||
ui_document_show_hide(idx);
|
||||
break;
|
||||
}
|
||||
case UNDO_ENCODING:
|
||||
{
|
||||
document_undo_add(idx, UNDO_ENCODING, g_strdup(doc_list[idx].encoding));
|
||||
|
||||
document_set_encoding(idx, (const gchar*)action->data);
|
||||
|
||||
app->ignore_callback = TRUE;
|
||||
encodings_select_radio_item((const gchar*)action->data);
|
||||
app->ignore_callback = FALSE;
|
||||
|
||||
g_free(action->data);
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
g_free(action); // free the action which was taken from the stack
|
||||
|
||||
ui_update_popup_reundo_items(idx);
|
||||
//geany_debug("%s: new stack height: %d", __func__, g_trash_stack_height(&doc_list[idx].redo_actions));
|
||||
}
|
||||
|
||||
|
||||
static void document_redo_add(gint idx, guint type, gpointer data)
|
||||
{
|
||||
undo_action *action;
|
||||
|
||||
if (! DOC_IDX_VALID(idx)) return;
|
||||
|
||||
action = g_new0(undo_action, 1);
|
||||
action->type = type;
|
||||
action->data = data;
|
||||
|
||||
g_trash_stack_push(&doc_list[idx].redo_actions, action);
|
||||
|
||||
doc_list[idx].changed = TRUE;
|
||||
document_set_text_changed(idx);
|
||||
ui_update_popup_reundo_items(idx);
|
||||
|
||||
//geany_debug("%s: new stack height: %d, added type: %d", __func__,
|
||||
//g_trash_stack_height(&doc_list[idx].redo_actions), action->type);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -175,11 +175,14 @@ void document_strip_trailing_spaces(gint idx);
|
||||
|
||||
void document_ensure_final_newline(gint idx);
|
||||
|
||||
void document_set_encoding(gint idx, const gchar *new_encoding);
|
||||
|
||||
|
||||
/* own Undo / Redo implementation to be able to undo / redo changes
|
||||
* to the encoding or the Unicode BOM (which are Scintilla independet).
|
||||
* All Scintilla events are stored in the undo / redo buffer and are passed through. */
|
||||
|
||||
// available UNDO actions, UNDO_SCINTILLA is a pseudo action to trigger Scintilla's undo management
|
||||
enum
|
||||
{
|
||||
UNDO_SCINTILLA = 0,
|
||||
@ -188,11 +191,13 @@ enum
|
||||
UNDO_ACTIONS_MAX
|
||||
};
|
||||
|
||||
// an undo action, also used for redo actions
|
||||
typedef struct
|
||||
{
|
||||
GTrashStack *next;
|
||||
guint type; // to identify the action
|
||||
gpointer *data; // the old value (before the change)
|
||||
GTrashStack *next; // pointer to the next stack element(required for the GTrashStack)
|
||||
guint type; // to identify the action
|
||||
gpointer *data; // the old value (before the change), in case of a redo action it contains
|
||||
// the new value
|
||||
} undo_action;
|
||||
|
||||
gboolean document_can_undo(gint idx);
|
||||
@ -205,8 +210,4 @@ void document_redo(gint idx);
|
||||
|
||||
void document_undo_add(gint idx, guint type, gpointer data);
|
||||
|
||||
void document_undo_clear(gint idx);
|
||||
|
||||
void document_redo_clear(gint idx);
|
||||
|
||||
#endif
|
||||
|
18
src/sci_cb.c
18
src/sci_cb.c
@ -153,26 +153,16 @@ void on_editor_notification(GtkWidget *editor, gint scn, gpointer lscn, gpointer
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 2023:
|
||||
case SCN_MODIFIED:
|
||||
{
|
||||
geany_debug("Undo notification");
|
||||
break;
|
||||
}
|
||||
/* case SCN_KEY:
|
||||
{
|
||||
//geany_debug("key notification triggered with %c", nt->ch);
|
||||
break;
|
||||
}
|
||||
case SCN_MODIFIED:
|
||||
{
|
||||
if (nt->modificationType & SC_MOD_INSERTTEXT ||
|
||||
nt->modificationType & SC_MOD_DELETETEXT)
|
||||
if (nt->modificationType & SC_START_ACTION && ! app->ignore_callback)
|
||||
{
|
||||
// get notified about undo changes
|
||||
document_undo_add(idx, UNDO_SCINTILLA, NULL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
*/ case SCN_CHARADDED:
|
||||
case SCN_CHARADDED:
|
||||
{
|
||||
gint pos = sci_get_current_position(sci);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user