pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
Change the default irc server to libera.chat
release-2.x.y
2021-05-27, Gary Kramlich
c00b451a1dd8
Change the default irc server to libera.chat
This seems to make the most sense for users right now as many many channels have migrated away from freenode with many of them moving to libera.
Testing Done:
Compile only.
Reviewed at https://reviews.imfreedom.org/r/675/
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* gtksourceundomanager.c
* This file is part of GtkSourceView
*
* Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence
* Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi
* Copyright (C) 2002-2005 Paolo Maggi
*
* 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 02111-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include
<config.h>
#endif
#include
<glib.h>
#include
<stdlib.h>
#include
<string.h>
#include
"gtksourceundomanager.h"
#include
"gtksourceview-marshal.h"
#define DEFAULT_MAX_UNDO_LEVELS 25
typedef
struct
_GtkSourceUndoAction
GtkSourceUndoAction
;
typedef
struct
_GtkSourceUndoInsertAction
GtkSourceUndoInsertAction
;
typedef
struct
_GtkSourceUndoDeleteAction
GtkSourceUndoDeleteAction
;
typedef
struct
_GtkSourceUndoInsertAnchorAction
GtkSourceUndoInsertAnchorAction
;
typedef
enum
{
GTK_SOURCE_UNDO_ACTION_INSERT
,
GTK_SOURCE_UNDO_ACTION_DELETE
,
GTK_SOURCE_UNDO_ACTION_INSERT_ANCHOR
,
}
GtkSourceUndoActionType
;
/*
* We use offsets instead of GtkTextIters because the last ones
* require to much memory in this context without giving us any advantage.
*/
struct
_GtkSourceUndoInsertAction
{
gint
pos
;
gchar
*
text
;
gint
length
;
gint
chars
;
};
struct
_GtkSourceUndoDeleteAction
{
gint
start
;
gint
end
;
gchar
*
text
;
gboolean
forward
;
};
struct
_GtkSourceUndoInsertAnchorAction
{
gint
pos
;
GtkTextChildAnchor
*
anchor
;
};
struct
_GtkSourceUndoAction
{
GtkSourceUndoActionType
action_type
;
union
{
GtkSourceUndoInsertAction
insert
;
GtkSourceUndoDeleteAction
delete
;
GtkSourceUndoInsertAnchorAction
insert_anchor
;
}
action
;
gint
order_in_group
;
/* It is TRUE whether the action can be merged with the following action. */
guint
mergeable
:
1
;
/* It is TRUE whether the action is marked as "modified".
* An action is marked as "modified" if it changed the
* state of the buffer from "not modified" to "modified". Only the first
* action of a group can be marked as modified.
* There can be a single action marked as "modified" in the actions list.
*/
guint
modified
:
1
;
};
struct
_GtkSourceUndoManagerPrivate
{
GtkTextBuffer
*
document
;
GList
*
actions
;
gint
next_redo
;
gint
actions_in_current_group
;
gint
running_not_undoable_actions
;
gint
num_of_groups
;
gint
max_undo_levels
;
guint
can_undo
:
1
;
guint
can_redo
:
1
;
/* It is TRUE whether, while undoing an action of the current group (with order_in_group > 1),
* the state of the buffer changed from "not modified" to "modified".
*/
guint
modified_undoing_group
:
1
;
/* Pointer to the action (in the action list) marked as "modified".
* It is NULL when no action is marked as "modified". */
GtkSourceUndoAction
*
modified_action
;
};
enum
{
CAN_UNDO
,
CAN_REDO
,
LAST_SIGNAL
};
static
void
gtk_source_undo_manager_class_init
(
GtkSourceUndoManagerClass
*
klass
);
static
void
gtk_source_undo_manager_init
(
GtkSourceUndoManager
*
um
);
static
void
gtk_source_undo_manager_finalize
(
GObject
*
object
);
static
void
gtk_source_undo_manager_insert_text_handler
(
GtkTextBuffer
*
buffer
,
GtkTextIter
*
pos
,
const
gchar
*
text
,
gint
length
,
GtkSourceUndoManager
*
um
);
static
void
gtk_source_undo_manager_insert_anchor_handler
(
GtkTextBuffer
*
buffer
,
GtkTextIter
*
pos
,
GtkTextChildAnchor
*
anchor
,
GtkSourceUndoManager
*
um
);
static
void
gtk_source_undo_manager_delete_range_handler
(
GtkTextBuffer
*
buffer
,
GtkTextIter
*
start
,
GtkTextIter
*
end
,
GtkSourceUndoManager
*
um
);
static
void
gtk_source_undo_manager_begin_user_action_handler
(
GtkTextBuffer
*
buffer
,
GtkSourceUndoManager
*
um
);
static
void
gtk_source_undo_manager_modified_changed_handler
(
GtkTextBuffer
*
buffer
,
GtkSourceUndoManager
*
um
);
static
void
gtk_source_undo_manager_free_action_list
(
GtkSourceUndoManager
*
um
);
static
void
gtk_source_undo_manager_add_action
(
GtkSourceUndoManager
*
um
,
const
GtkSourceUndoAction
*
undo_action
);
static
void
gtk_source_undo_manager_free_first_n_actions
(
GtkSourceUndoManager
*
um
,
gint
n
);
static
void
gtk_source_undo_manager_check_list_size
(
GtkSourceUndoManager
*
um
);
static
gboolean
gtk_source_undo_manager_merge_action
(
GtkSourceUndoManager
*
um
,
const
GtkSourceUndoAction
*
undo_action
);
static
GObjectClass
*
parent_class
=
NULL
;
static
guint
undo_manager_signals
[
LAST_SIGNAL
]
=
{
0
};
GType
gtk_source_undo_manager_get_type
(
void
)
{
static
GType
undo_manager_type
=
0
;
if
(
undo_manager_type
==
0
)
{
static
const
GTypeInfo
our_info
=
{
sizeof
(
GtkSourceUndoManagerClass
),
NULL
,
/* base_init */
NULL
,
/* base_finalize */
(
GClassInitFunc
)
gtk_source_undo_manager_class_init
,
NULL
,
/* class_finalize */
NULL
,
/* class_data */
sizeof
(
GtkSourceUndoManager
),
0
,
/* n_preallocs */
(
GInstanceInitFunc
)
gtk_source_undo_manager_init
,
NULL
/* value_table */
};
undo_manager_type
=
g_type_register_static
(
G_TYPE_OBJECT
,
"GtkSourceUndoManager"
,
&
our_info
,
0
);
}
return
undo_manager_type
;
}
static
void
gtk_source_undo_manager_class_init
(
GtkSourceUndoManagerClass
*
klass
)
{
GObjectClass
*
object_class
=
G_OBJECT_CLASS
(
klass
);
parent_class
=
g_type_class_peek_parent
(
klass
);
object_class
->
finalize
=
gtk_source_undo_manager_finalize
;
klass
->
can_undo
=
NULL
;
klass
->
can_redo
=
NULL
;
undo_manager_signals
[
CAN_UNDO
]
=
g_signal_new
(
"can_undo"
,
G_OBJECT_CLASS_TYPE
(
object_class
),
G_SIGNAL_RUN_LAST
,
G_STRUCT_OFFSET
(
GtkSourceUndoManagerClass
,
can_undo
),
NULL
,
NULL
,
gtksourceview_marshal_VOID__BOOLEAN
,
G_TYPE_NONE
,
1
,
G_TYPE_BOOLEAN
);
undo_manager_signals
[
CAN_REDO
]
=
g_signal_new
(
"can_redo"
,
G_OBJECT_CLASS_TYPE
(
object_class
),
G_SIGNAL_RUN_LAST
,
G_STRUCT_OFFSET
(
GtkSourceUndoManagerClass
,
can_redo
),
NULL
,
NULL
,
gtksourceview_marshal_VOID__BOOLEAN
,
G_TYPE_NONE
,
1
,
G_TYPE_BOOLEAN
);
}
static
void
gtk_source_undo_manager_init
(
GtkSourceUndoManager
*
um
)
{
um
->
priv
=
g_new0
(
GtkSourceUndoManagerPrivate
,
1
);
um
->
priv
->
actions
=
NULL
;
um
->
priv
->
next_redo
=
0
;
um
->
priv
->
can_undo
=
FALSE
;
um
->
priv
->
can_redo
=
FALSE
;
um
->
priv
->
running_not_undoable_actions
=
0
;
um
->
priv
->
num_of_groups
=
0
;
um
->
priv
->
max_undo_levels
=
DEFAULT_MAX_UNDO_LEVELS
;
um
->
priv
->
modified_action
=
NULL
;
um
->
priv
->
modified_undoing_group
=
FALSE
;
}
static
void
gtk_source_undo_manager_finalize
(
GObject
*
object
)
{
GtkSourceUndoManager
*
um
;
g_return_if_fail
(
object
!=
NULL
);
g_return_if_fail
(
GTK_SOURCE_IS_UNDO_MANAGER
(
object
));
um
=
GTK_SOURCE_UNDO_MANAGER
(
object
);
g_return_if_fail
(
um
->
priv
!=
NULL
);
if
(
um
->
priv
->
actions
!=
NULL
)
{
gtk_source_undo_manager_free_action_list
(
um
);
}
g_signal_handlers_disconnect_by_func
(
G_OBJECT
(
um
->
priv
->
document
),
G_CALLBACK
(
gtk_source_undo_manager_delete_range_handler
),
um
);
g_signal_handlers_disconnect_by_func
(
G_OBJECT
(
um
->
priv
->
document
),
G_CALLBACK
(
gtk_source_undo_manager_insert_text_handler
),
um
);
g_signal_handlers_disconnect_by_func
(
G_OBJECT
(
um
->
priv
->
document
),
G_CALLBACK
(
gtk_source_undo_manager_insert_anchor_handler
),
um
);
g_signal_handlers_disconnect_by_func
(
G_OBJECT
(
um
->
priv
->
document
),
G_CALLBACK
(
gtk_source_undo_manager_begin_user_action_handler
),
um
);
g_free
(
um
->
priv
);
G_OBJECT_CLASS
(
parent_class
)
->
finalize
(
object
);
}
GtkSourceUndoManager
*
gtk_source_undo_manager_new
(
GtkTextBuffer
*
buffer
)
{
GtkSourceUndoManager
*
um
;
um
=
GTK_SOURCE_UNDO_MANAGER
(
g_object_new
(
GTK_SOURCE_TYPE_UNDO_MANAGER
,
NULL
));
g_return_val_if_fail
(
um
->
priv
!=
NULL
,
NULL
);
um
->
priv
->
document
=
buffer
;
g_signal_connect
(
G_OBJECT
(
buffer
),
"insert_text"
,
G_CALLBACK
(
gtk_source_undo_manager_insert_text_handler
),
um
);
g_signal_connect
(
G_OBJECT
(
buffer
),
"insert_child_anchor"
,
G_CALLBACK
(
gtk_source_undo_manager_insert_anchor_handler
),
um
);
g_signal_connect
(
G_OBJECT
(
buffer
),
"delete_range"
,
G_CALLBACK
(
gtk_source_undo_manager_delete_range_handler
),
um
);
g_signal_connect
(
G_OBJECT
(
buffer
),
"begin_user_action"
,
G_CALLBACK
(
gtk_source_undo_manager_begin_user_action_handler
),
um
);
g_signal_connect
(
G_OBJECT
(
buffer
),
"modified_changed"
,
G_CALLBACK
(
gtk_source_undo_manager_modified_changed_handler
),
um
);
return
um
;
}
void
gtk_source_undo_manager_begin_not_undoable_action
(
GtkSourceUndoManager
*
um
)
{
g_return_if_fail
(
GTK_SOURCE_IS_UNDO_MANAGER
(
um
));
g_return_if_fail
(
um
->
priv
!=
NULL
);
++
um
->
priv
->
running_not_undoable_actions
;
}
static
void
gtk_source_undo_manager_end_not_undoable_action_internal
(
GtkSourceUndoManager
*
um
)
{
g_return_if_fail
(
GTK_SOURCE_IS_UNDO_MANAGER
(
um
));
g_return_if_fail
(
um
->
priv
!=
NULL
);
g_return_if_fail
(
um
->
priv
->
running_not_undoable_actions
>
0
);
--
um
->
priv
->
running_not_undoable_actions
;
}
void
gtk_source_undo_manager_end_not_undoable_action
(
GtkSourceUndoManager
*
um
)
{
g_return_if_fail
(
GTK_SOURCE_IS_UNDO_MANAGER
(
um
));
g_return_if_fail
(
um
->
priv
!=
NULL
);
gtk_source_undo_manager_end_not_undoable_action_internal
(
um
);
if
(
um
->
priv
->
running_not_undoable_actions
==
0
)
{
gtk_source_undo_manager_free_action_list
(
um
);
um
->
priv
->
next_redo
=
-1
;
if
(
um
->
priv
->
can_undo
)
{
um
->
priv
->
can_undo
=
FALSE
;
g_signal_emit
(
G_OBJECT
(
um
),
undo_manager_signals
[
CAN_UNDO
],
0
,
FALSE
);
}
if
(
um
->
priv
->
can_redo
)
{
um
->
priv
->
can_redo
=
FALSE
;
g_signal_emit
(
G_OBJECT
(
um
),
undo_manager_signals
[
CAN_REDO
],
0
,
FALSE
);
}
}
}
gboolean
gtk_source_undo_manager_can_undo
(
const
GtkSourceUndoManager
*
um
)
{
g_return_val_if_fail
(
GTK_SOURCE_IS_UNDO_MANAGER
(
um
),
FALSE
);
g_return_val_if_fail
(
um
->
priv
!=
NULL
,
FALSE
);
return
um
->
priv
->
can_undo
;
}
gboolean
gtk_source_undo_manager_can_redo
(
const
GtkSourceUndoManager
*
um
)
{
g_return_val_if_fail
(
GTK_SOURCE_IS_UNDO_MANAGER
(
um
),
FALSE
);
g_return_val_if_fail
(
um
->
priv
!=
NULL
,
FALSE
);
return
um
->
priv
->
can_redo
;
}
static
void
set_cursor
(
GtkTextBuffer
*
buffer
,
gint
cursor
)
{
GtkTextIter
iter
;
/* Place the cursor at the requested position */
gtk_text_buffer_get_iter_at_offset
(
buffer
,
&
iter
,
cursor
);
gtk_text_buffer_place_cursor
(
buffer
,
&
iter
);
}
static
void
insert_text
(
GtkTextBuffer
*
buffer
,
gint
pos
,
const
gchar
*
text
,
gint
len
)
{
GtkTextIter
iter
;
gtk_text_buffer_get_iter_at_offset
(
buffer
,
&
iter
,
pos
);
gtk_text_buffer_insert
(
buffer
,
&
iter
,
text
,
len
);
}
static
void
insert_anchor
(
GtkTextBuffer
*
buffer
,
gint
pos
,
GtkTextChildAnchor
*
anchor
)
{
GtkTextIter
iter
;
gtk_text_buffer_get_iter_at_offset
(
buffer
,
&
iter
,
pos
);
gtk_text_buffer_insert_child_anchor
(
buffer
,
&
iter
,
anchor
);
}
static
void
delete_text
(
GtkTextBuffer
*
buffer
,
gint
start
,
gint
end
)
{
GtkTextIter
start_iter
;
GtkTextIter
end_iter
;
gtk_text_buffer_get_iter_at_offset
(
buffer
,
&
start_iter
,
start
);
if
(
end
<
0
)
gtk_text_buffer_get_end_iter
(
buffer
,
&
end_iter
);
else
gtk_text_buffer_get_iter_at_offset
(
buffer
,
&
end_iter
,
end
);
gtk_text_buffer_delete
(
buffer
,
&
start_iter
,
&
end_iter
);
}
static
gchar
*
get_chars
(
GtkTextBuffer
*
buffer
,
gint
start
,
gint
end
)
{
GtkTextIter
start_iter
;
GtkTextIter
end_iter
;
gtk_text_buffer_get_iter_at_offset
(
buffer
,
&
start_iter
,
start
);
if
(
end
<
0
)
gtk_text_buffer_get_end_iter
(
buffer
,
&
end_iter
);
else
gtk_text_buffer_get_iter_at_offset
(
buffer
,
&
end_iter
,
end
);
return
gtk_text_buffer_get_slice
(
buffer
,
&
start_iter
,
&
end_iter
,
TRUE
);
}
void
gtk_source_undo_manager_undo
(
GtkSourceUndoManager
*
um
)
{
GtkSourceUndoAction
*
undo_action
;
gboolean
modified
=
FALSE
;
g_return_if_fail
(
GTK_SOURCE_IS_UNDO_MANAGER
(
um
));
g_return_if_fail
(
um
->
priv
!=
NULL
);
g_return_if_fail
(
um
->
priv
->
can_undo
);
um
->
priv
->
modified_undoing_group
=
FALSE
;
gtk_source_undo_manager_begin_not_undoable_action
(
um
);
do
{
undo_action
=
g_list_nth_data
(
um
->
priv
->
actions
,
um
->
priv
->
next_redo
+
1
);
g_return_if_fail
(
undo_action
!=
NULL
);
/* undo_action->modified can be TRUE only if undo_action->order_in_group <= 1 */
g_return_if_fail
((
undo_action
->
order_in_group
<=
1
)
||
((
undo_action
->
order_in_group
>
1
)
&&
!
undo_action
->
modified
));
if
(
undo_action
->
order_in_group
<=
1
)
{
/* Set modified to TRUE only if the buffer did not change its state from
* "not modified" to "modified" undoing an action (with order_in_group > 1)
* in current group. */
modified
=
(
undo_action
->
modified
&&
!
um
->
priv
->
modified_undoing_group
);
}
switch
(
undo_action
->
action_type
)
{
case
GTK_SOURCE_UNDO_ACTION_DELETE
:
insert_text
(
um
->
priv
->
document
,
undo_action
->
action
.
delete
.
start
,
undo_action
->
action
.
delete
.
text
,
strlen
(
undo_action
->
action
.
delete
.
text
));
if
(
undo_action
->
action
.
delete
.
forward
)
set_cursor
(
um
->
priv
->
document
,
undo_action
->
action
.
delete
.
start
);
else
set_cursor
(
um
->
priv
->
document
,
undo_action
->
action
.
delete
.
end
);
break
;
case
GTK_SOURCE_UNDO_ACTION_INSERT
:
delete_text
(
um
->
priv
->
document
,
undo_action
->
action
.
insert
.
pos
,
undo_action
->
action
.
insert
.
pos
+
undo_action
->
action
.
insert
.
chars
);
set_cursor
(
um
->
priv
->
document
,
undo_action
->
action
.
insert
.
pos
);
break
;
case
GTK_SOURCE_UNDO_ACTION_INSERT_ANCHOR
:
delete_text
(
um
->
priv
->
document
,
undo_action
->
action
.
insert_anchor
.
pos
,
undo_action
->
action
.
insert_anchor
.
pos
+
1
);
undo_action
->
action
.
insert_anchor
.
anchor
->
segment
=
NULL
;
/* XXX: This may be a bug in GTK+ */
break
;
default
:
/* Unknown action type. */
g_return_if_reached
();
}
++
um
->
priv
->
next_redo
;
}
while
(
undo_action
->
order_in_group
>
1
);
if
(
modified
)
{
--
um
->
priv
->
next_redo
;
gtk_text_buffer_set_modified
(
um
->
priv
->
document
,
FALSE
);
++
um
->
priv
->
next_redo
;
}
gtk_source_undo_manager_end_not_undoable_action_internal
(
um
);
um
->
priv
->
modified_undoing_group
=
FALSE
;
if
(
!
um
->
priv
->
can_redo
)
{
um
->
priv
->
can_redo
=
TRUE
;
g_signal_emit
(
G_OBJECT
(
um
),
undo_manager_signals
[
CAN_REDO
],
0
,
TRUE
);
}
if
(
um
->
priv
->
next_redo
>=
(
gint
)(
g_list_length
(
um
->
priv
->
actions
)
-
1
))
{
um
->
priv
->
can_undo
=
FALSE
;
g_signal_emit
(
G_OBJECT
(
um
),
undo_manager_signals
[
CAN_UNDO
],
0
,
FALSE
);
}
}
void
gtk_source_undo_manager_redo
(
GtkSourceUndoManager
*
um
)
{
GtkSourceUndoAction
*
undo_action
;
gboolean
modified
=
FALSE
;
g_return_if_fail
(
GTK_SOURCE_IS_UNDO_MANAGER
(
um
));
g_return_if_fail
(
um
->
priv
!=
NULL
);
g_return_if_fail
(
um
->
priv
->
can_redo
);
undo_action
=
g_list_nth_data
(
um
->
priv
->
actions
,
um
->
priv
->
next_redo
);
g_return_if_fail
(
undo_action
!=
NULL
);
gtk_source_undo_manager_begin_not_undoable_action
(
um
);
do
{
if
(
undo_action
->
modified
)
{
g_return_if_fail
(
undo_action
->
order_in_group
<=
1
);
modified
=
TRUE
;
}
--
um
->
priv
->
next_redo
;
switch
(
undo_action
->
action_type
)
{
case
GTK_SOURCE_UNDO_ACTION_DELETE
:
delete_text
(
um
->
priv
->
document
,
undo_action
->
action
.
delete
.
start
,
undo_action
->
action
.
delete
.
end
);
set_cursor
(
um
->
priv
->
document
,
undo_action
->
action
.
delete
.
start
);
break
;
case
GTK_SOURCE_UNDO_ACTION_INSERT
:
set_cursor
(
um
->
priv
->
document
,
undo_action
->
action
.
insert
.
pos
);
insert_text
(
um
->
priv
->
document
,
undo_action
->
action
.
insert
.
pos
,
undo_action
->
action
.
insert
.
text
,
undo_action
->
action
.
insert
.
length
);
break
;
case
GTK_SOURCE_UNDO_ACTION_INSERT_ANCHOR
:
set_cursor
(
um
->
priv
->
document
,
undo_action
->
action
.
insert_anchor
.
pos
);
insert_anchor
(
um
->
priv
->
document
,
undo_action
->
action
.
insert_anchor
.
pos
,
undo_action
->
action
.
insert_anchor
.
anchor
);
break
;
default
:
/* Unknown action type */
++
um
->
priv
->
next_redo
;
g_return_if_reached
();
}
if
(
um
->
priv
->
next_redo
<
0
)
undo_action
=
NULL
;
else
undo_action
=
g_list_nth_data
(
um
->
priv
->
actions
,
um
->
priv
->
next_redo
);
}
while
((
undo_action
!=
NULL
)
&&
(
undo_action
->
order_in_group
>
1
));
if
(
modified
)
{
++
um
->
priv
->
next_redo
;
gtk_text_buffer_set_modified
(
um
->
priv
->
document
,
FALSE
);
--
um
->
priv
->
next_redo
;
}
gtk_source_undo_manager_end_not_undoable_action_internal
(
um
);
if
(
um
->
priv
->
next_redo
<
0
)
{
um
->
priv
->
can_redo
=
FALSE
;
g_signal_emit
(
G_OBJECT
(
um
),
undo_manager_signals
[
CAN_REDO
],
0
,
FALSE
);
}
if
(
!
um
->
priv
->
can_undo
)
{
um
->
priv
->
can_undo
=
TRUE
;
g_signal_emit
(
G_OBJECT
(
um
),
undo_manager_signals
[
CAN_UNDO
],
0
,
TRUE
);
}
}
static
void
gtk_source_undo_action_free
(
GtkSourceUndoAction
*
action
)
{
if
(
action
==
NULL
)
return
;
if
(
action
->
action_type
==
GTK_SOURCE_UNDO_ACTION_INSERT
)
g_free
(
action
->
action
.
insert
.
text
);
else
if
(
action
->
action_type
==
GTK_SOURCE_UNDO_ACTION_DELETE
)
g_free
(
action
->
action
.
delete
.
text
);
else
if
(
action
->
action_type
==
GTK_SOURCE_UNDO_ACTION_INSERT_ANCHOR
)
g_object_unref
(
action
->
action
.
insert_anchor
.
anchor
);
else
{
g_free
(
action
);
g_return_if_reached
();
}
g_free
(
action
);
}
static
void
gtk_source_undo_manager_free_action_list
(
GtkSourceUndoManager
*
um
)
{
GList
*
l
;
l
=
um
->
priv
->
actions
;
while
(
l
!=
NULL
)
{
GtkSourceUndoAction
*
action
=
l
->
data
;
if
(
action
->
order_in_group
==
1
)
--
um
->
priv
->
num_of_groups
;
if
(
action
->
modified
)
um
->
priv
->
modified_action
=
NULL
;
gtk_source_undo_action_free
(
action
);
l
=
g_list_next
(
l
);
}
g_list_free
(
um
->
priv
->
actions
);
um
->
priv
->
actions
=
NULL
;
}
static
void
gtk_source_undo_manager_insert_text_handler
(
GtkTextBuffer
*
buffer
,
GtkTextIter
*
pos
,
const
gchar
*
text
,
gint
length
,
GtkSourceUndoManager
*
um
)
{
GtkSourceUndoAction
undo_action
;
if
(
um
->
priv
->
running_not_undoable_actions
>
0
)
return
;
undo_action
.
action_type
=
GTK_SOURCE_UNDO_ACTION_INSERT
;
undo_action
.
action
.
insert
.
pos
=
gtk_text_iter_get_offset
(
pos
);
undo_action
.
action
.
insert
.
text
=
(
gchar
*
)
text
;
undo_action
.
action
.
insert
.
length
=
length
;
undo_action
.
action
.
insert
.
chars
=
g_utf8_strlen
(
text
,
length
);
if
((
undo_action
.
action
.
insert
.
chars
>
1
)
||
(
g_utf8_get_char
(
text
)
==
'\n'
))
undo_action
.
mergeable
=
FALSE
;
else
undo_action
.
mergeable
=
TRUE
;
undo_action
.
modified
=
FALSE
;
gtk_source_undo_manager_add_action
(
um
,
&
undo_action
);
}
static
void
gtk_source_undo_manager_insert_anchor_handler
(
GtkTextBuffer
*
buffer
,
GtkTextIter
*
pos
,
GtkTextChildAnchor
*
anchor
,
GtkSourceUndoManager
*
um
)
{
GtkSourceUndoAction
undo_action
;
if
(
um
->
priv
->
running_not_undoable_actions
>
0
)
return
;
undo_action
.
action_type
=
GTK_SOURCE_UNDO_ACTION_INSERT_ANCHOR
;
undo_action
.
action
.
insert_anchor
.
pos
=
gtk_text_iter_get_offset
(
pos
);
undo_action
.
action
.
insert_anchor
.
anchor
=
g_object_ref
(
anchor
);
undo_action
.
mergeable
=
FALSE
;
undo_action
.
modified
=
FALSE
;
gtk_source_undo_manager_add_action
(
um
,
&
undo_action
);
}
static
void
gtk_source_undo_manager_delete_range_handler
(
GtkTextBuffer
*
buffer
,
GtkTextIter
*
start
,
GtkTextIter
*
end
,
GtkSourceUndoManager
*
um
)
{
GtkSourceUndoAction
undo_action
;
GtkTextIter
insert_iter
;
if
(
um
->
priv
->
running_not_undoable_actions
>
0
)
return
;
undo_action
.
action_type
=
GTK_SOURCE_UNDO_ACTION_DELETE
;
gtk_text_iter_order
(
start
,
end
);
undo_action
.
action
.
delete
.
start
=
gtk_text_iter_get_offset
(
start
);
undo_action
.
action
.
delete
.
end
=
gtk_text_iter_get_offset
(
end
);
undo_action
.
action
.
delete
.
text
=
get_chars
(
buffer
,
undo_action
.
action
.
delete
.
start
,
undo_action
.
action
.
delete
.
end
);
/* figure out if the user used the Delete or the Backspace key */
gtk_text_buffer_get_iter_at_mark
(
buffer
,
&
insert_iter
,
gtk_text_buffer_get_insert
(
buffer
));
if
(
gtk_text_iter_get_offset
(
&
insert_iter
)
<=
undo_action
.
action
.
delete
.
start
)
undo_action
.
action
.
delete
.
forward
=
TRUE
;
else
undo_action
.
action
.
delete
.
forward
=
FALSE
;
if
(((
undo_action
.
action
.
delete
.
end
-
undo_action
.
action
.
delete
.
start
)
>
1
)
||
(
g_utf8_get_char
(
undo_action
.
action
.
delete
.
text
)
==
'\n'
))
undo_action
.
mergeable
=
FALSE
;
else
undo_action
.
mergeable
=
TRUE
;
undo_action
.
modified
=
FALSE
;
gtk_source_undo_manager_add_action
(
um
,
&
undo_action
);
g_free
(
undo_action
.
action
.
delete
.
text
);
}
static
void
gtk_source_undo_manager_begin_user_action_handler
(
GtkTextBuffer
*
buffer
,
GtkSourceUndoManager
*
um
)
{
g_return_if_fail
(
GTK_SOURCE_IS_UNDO_MANAGER
(
um
));
g_return_if_fail
(
um
->
priv
!=
NULL
);
if
(
um
->
priv
->
running_not_undoable_actions
>
0
)
return
;
um
->
priv
->
actions_in_current_group
=
0
;
}
static
void
gtk_source_undo_manager_add_action
(
GtkSourceUndoManager
*
um
,
const
GtkSourceUndoAction
*
undo_action
)
{
GtkSourceUndoAction
*
action
;
if
(
um
->
priv
->
next_redo
>=
0
)
{
gtk_source_undo_manager_free_first_n_actions
(
um
,
um
->
priv
->
next_redo
+
1
);
}
um
->
priv
->
next_redo
=
-1
;
if
(
!
gtk_source_undo_manager_merge_action
(
um
,
undo_action
))
{
action
=
g_new
(
GtkSourceUndoAction
,
1
);
*
action
=
*
undo_action
;
if
(
action
->
action_type
==
GTK_SOURCE_UNDO_ACTION_INSERT
)
action
->
action
.
insert
.
text
=
g_strndup
(
undo_action
->
action
.
insert
.
text
,
undo_action
->
action
.
insert
.
length
);
else
if
(
action
->
action_type
==
GTK_SOURCE_UNDO_ACTION_DELETE
)
action
->
action
.
delete
.
text
=
g_strdup
(
undo_action
->
action
.
delete
.
text
);
else
if
(
action
->
action_type
==
GTK_SOURCE_UNDO_ACTION_INSERT_ANCHOR
)
{
/* Nothing needs to be done */
}
else
{
g_free
(
action
);
g_return_if_reached
();
}
++
um
->
priv
->
actions_in_current_group
;
action
->
order_in_group
=
um
->
priv
->
actions_in_current_group
;
if
(
action
->
order_in_group
==
1
)
++
um
->
priv
->
num_of_groups
;
um
->
priv
->
actions
=
g_list_prepend
(
um
->
priv
->
actions
,
action
);
}
gtk_source_undo_manager_check_list_size
(
um
);
if
(
!
um
->
priv
->
can_undo
)
{
um
->
priv
->
can_undo
=
TRUE
;
g_signal_emit
(
G_OBJECT
(
um
),
undo_manager_signals
[
CAN_UNDO
],
0
,
TRUE
);
}
if
(
um
->
priv
->
can_redo
)
{
um
->
priv
->
can_redo
=
FALSE
;
g_signal_emit
(
G_OBJECT
(
um
),
undo_manager_signals
[
CAN_REDO
],
0
,
FALSE
);
}
}
static
void
gtk_source_undo_manager_free_first_n_actions
(
GtkSourceUndoManager
*
um
,
gint
n
)
{
gint
i
;
if
(
um
->
priv
->
actions
==
NULL
)
return
;
for
(
i
=
0
;
i
<
n
;
i
++
)
{
GtkSourceUndoAction
*
action
=
g_list_first
(
um
->
priv
->
actions
)
->
data
;
if
(
action
->
order_in_group
==
1
)
--
um
->
priv
->
num_of_groups
;
if
(
action
->
modified
)
um
->
priv
->
modified_action
=
NULL
;
gtk_source_undo_action_free
(
action
);
um
->
priv
->
actions
=
g_list_delete_link
(
um
->
priv
->
actions
,
um
->
priv
->
actions
);
if
(
um
->
priv
->
actions
==
NULL
)
return
;
}
}
static
void
gtk_source_undo_manager_check_list_size
(
GtkSourceUndoManager
*
um
)
{
gint
undo_levels
;
g_return_if_fail
(
GTK_SOURCE_IS_UNDO_MANAGER
(
um
));
g_return_if_fail
(
um
->
priv
!=
NULL
);
undo_levels
=
gtk_source_undo_manager_get_max_undo_levels
(
um
);
if
(
undo_levels
<
1
)
return
;
if
(
um
->
priv
->
num_of_groups
>
undo_levels
)
{
GtkSourceUndoAction
*
undo_action
;
GList
*
last
;
last
=
g_list_last
(
um
->
priv
->
actions
);
undo_action
=
(
GtkSourceUndoAction
*
)
last
->
data
;
do
{
GList
*
tmp
;
if
(
undo_action
->
order_in_group
==
1
)
--
um
->
priv
->
num_of_groups
;
if
(
undo_action
->
modified
)
um
->
priv
->
modified_action
=
NULL
;
gtk_source_undo_action_free
(
undo_action
);
tmp
=
g_list_previous
(
last
);
um
->
priv
->
actions
=
g_list_delete_link
(
um
->
priv
->
actions
,
last
);
last
=
tmp
;
g_return_if_fail
(
last
!=
NULL
);
undo_action
=
(
GtkSourceUndoAction
*
)
last
->
data
;
}
while
((
undo_action
->
order_in_group
>
1
)
||
(
um
->
priv
->
num_of_groups
>
undo_levels
));
}
}
/**
* gtk_source_undo_manager_merge_action:
* @um: a #GtkSourceUndoManager.
* @undo_action: a #GtkSourceUndoAction.
*
* This function tries to merge the undo action at the top of
* the stack with a new undo action. 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 successful, %FALSE otherwise.
**/
static
gboolean
gtk_source_undo_manager_merge_action
(
GtkSourceUndoManager
*
um
,
const
GtkSourceUndoAction
*
undo_action
)
{
GtkSourceUndoAction
*
last_action
;
g_return_val_if_fail
(
GTK_SOURCE_IS_UNDO_MANAGER
(
um
),
FALSE
);
g_return_val_if_fail
(
um
->
priv
!=
NULL
,
FALSE
);
if
(
um
->
priv
->
actions
==
NULL
)
return
FALSE
;
last_action
=
(
GtkSourceUndoAction
*
)
g_list_nth_data
(
um
->
priv
->
actions
,
0
);
if
(
!
last_action
->
mergeable
)
return
FALSE
;
if
((
!
undo_action
->
mergeable
)
||
(
undo_action
->
action_type
!=
last_action
->
action_type
))
{
last_action
->
mergeable
=
FALSE
;
return
FALSE
;
}
if
(
undo_action
->
action_type
==
GTK_SOURCE_UNDO_ACTION_DELETE
)
{
const
GtkSourceUndoDeleteAction
*
last_del
,
*
undo_del
;
last_del
=
&
last_action
->
action
.
delete
;
undo_del
=
&
undo_action
->
action
.
delete
;
if
(
last_del
->
forward
!=
undo_del
->
forward
||
(
last_del
->
start
!=
undo_del
->
start
&&
last_del
->
start
!=
undo_del
->
end
))
{
last_action
->
mergeable
=
FALSE
;
return
FALSE
;
}
if
(
last_action
->
action
.
delete
.
start
==
undo_action
->
action
.
delete
.
start
)
{
gchar
*
str
;
#define L (last_action->action.delete.end - last_action->action.delete.start - 1)
#define g_utf8_get_char_at(p,i) g_utf8_get_char(g_utf8_offset_to_pointer((p),(i)))
/* Deleted with the delete key */
if
((
g_utf8_get_char
(
undo_action
->
action
.
delete
.
text
)
!=
' '
)
&&
(
g_utf8_get_char
(
undo_action
->
action
.
delete
.
text
)
!=
'\t'
)
&&
((
g_utf8_get_char_at
(
last_action
->
action
.
delete
.
text
,
L
)
==
' '
)
||
(
g_utf8_get_char_at
(
last_action
->
action
.
delete
.
text
,
L
)
==
'\t'
)))
{
last_action
->
mergeable
=
FALSE
;
return
FALSE
;
}
str
=
g_strdup_printf
(
"%s%s"
,
last_action
->
action
.
delete
.
text
,
undo_action
->
action
.
delete
.
text
);
g_free
(
last_action
->
action
.
delete
.
text
);
last_action
->
action
.
delete
.
end
+=
(
undo_action
->
action
.
delete
.
end
-
undo_action
->
action
.
delete
.
start
);
last_action
->
action
.
delete
.
text
=
str
;
}
else
{
gchar
*
str
;
/* Deleted with the backspace key */
if
((
g_utf8_get_char
(
undo_action
->
action
.
delete
.
text
)
!=
' '
)
&&
(
g_utf8_get_char
(
undo_action
->
action
.
delete
.
text
)
!=
'\t'
)
&&
((
g_utf8_get_char
(
last_action
->
action
.
delete
.
text
)
==
' '
)
||
(
g_utf8_get_char
(
last_action
->
action
.
delete
.
text
)
==
'\t'
)))
{
last_action
->
mergeable
=
FALSE
;
return
FALSE
;
}
str
=
g_strdup_printf
(
"%s%s"
,
undo_action
->
action
.
delete
.
text
,
last_action
->
action
.
delete
.
text
);
g_free
(
last_action
->
action
.
delete
.
text
);
last_action
->
action
.
delete
.
start
=
undo_action
->
action
.
delete
.
start
;
last_action
->
action
.
delete
.
text
=
str
;
}
}
else
if
(
undo_action
->
action_type
==
GTK_SOURCE_UNDO_ACTION_INSERT
)
{
gchar
*
str
;
#define I (last_action->action.insert.chars - 1)
if
((
undo_action
->
action
.
insert
.
pos
!=
(
last_action
->
action
.
insert
.
pos
+
last_action
->
action
.
insert
.
chars
))
||
((
g_utf8_get_char
(
undo_action
->
action
.
insert
.
text
)
!=
' '
)
&&
(
g_utf8_get_char
(
undo_action
->
action
.
insert
.
text
)
!=
'\t'
)
&&
((
g_utf8_get_char_at
(
last_action
->
action
.
insert
.
text
,
I
)
==
' '
)
||
(
g_utf8_get_char_at
(
last_action
->
action
.
insert
.
text
,
I
)
==
'\t'
)))
)
{
last_action
->
mergeable
=
FALSE
;
return
FALSE
;
}
str
=
g_strdup_printf
(
"%s%s"
,
last_action
->
action
.
insert
.
text
,
undo_action
->
action
.
insert
.
text
);
g_free
(
last_action
->
action
.
insert
.
text
);
last_action
->
action
.
insert
.
length
+=
undo_action
->
action
.
insert
.
length
;
last_action
->
action
.
insert
.
text
=
str
;
last_action
->
action
.
insert
.
chars
+=
undo_action
->
action
.
insert
.
chars
;
}
else
if
(
undo_action
->
action_type
==
GTK_SOURCE_UNDO_ACTION_INSERT_ANCHOR
)
{
/* Nothing needs to be done */
}
else
/* Unknown action inside undo merge encountered */
g_return_val_if_reached
(
TRUE
);
return
TRUE
;
}
gint
gtk_source_undo_manager_get_max_undo_levels
(
GtkSourceUndoManager
*
um
)
{
g_return_val_if_fail
(
um
!=
NULL
,
0
);
g_return_val_if_fail
(
GTK_SOURCE_IS_UNDO_MANAGER
(
um
),
0
);
return
um
->
priv
->
max_undo_levels
;
}
void
gtk_source_undo_manager_set_max_undo_levels
(
GtkSourceUndoManager
*
um
,
gint
max_undo_levels
)
{
gint
old_levels
;
g_return_if_fail
(
um
!=
NULL
);
g_return_if_fail
(
GTK_SOURCE_IS_UNDO_MANAGER
(
um
));
old_levels
=
um
->
priv
->
max_undo_levels
;
um
->
priv
->
max_undo_levels
=
max_undo_levels
;
if
(
max_undo_levels
<
1
)
return
;
if
(
old_levels
>
max_undo_levels
)
{
/* strip redo actions first */
while
(
um
->
priv
->
next_redo
>=
0
&&
(
um
->
priv
->
num_of_groups
>
max_undo_levels
))
{
gtk_source_undo_manager_free_first_n_actions
(
um
,
1
);
um
->
priv
->
next_redo
--
;
}
/* now remove undo actions if necessary */
gtk_source_undo_manager_check_list_size
(
um
);
/* emit "can_undo" and/or "can_redo" if appropiate */
if
(
um
->
priv
->
next_redo
<
0
&&
um
->
priv
->
can_redo
)
{
um
->
priv
->
can_redo
=
FALSE
;
g_signal_emit
(
G_OBJECT
(
um
),
undo_manager_signals
[
CAN_REDO
],
0
,
FALSE
);
}
if
(
um
->
priv
->
can_undo
&&
um
->
priv
->
next_redo
>=
(
gint
)(
g_list_length
(
um
->
priv
->
actions
)
-
1
))
{
um
->
priv
->
can_undo
=
FALSE
;
g_signal_emit
(
G_OBJECT
(
um
),
undo_manager_signals
[
CAN_UNDO
],
0
,
FALSE
);
}
}
}
static
void
gtk_source_undo_manager_modified_changed_handler
(
GtkTextBuffer
*
buffer
,
GtkSourceUndoManager
*
um
)
{
GtkSourceUndoAction
*
action
;
GList
*
list
;
g_return_if_fail
(
GTK_SOURCE_IS_UNDO_MANAGER
(
um
));
g_return_if_fail
(
um
->
priv
!=
NULL
);
if
(
um
->
priv
->
actions
==
NULL
)
return
;
list
=
g_list_nth
(
um
->
priv
->
actions
,
um
->
priv
->
next_redo
+
1
);
if
(
list
!=
NULL
)
action
=
(
GtkSourceUndoAction
*
)
list
->
data
;
else
action
=
NULL
;
if
(
gtk_text_buffer_get_modified
(
buffer
)
==
FALSE
)
{
if
(
action
!=
NULL
)
action
->
mergeable
=
FALSE
;
if
(
um
->
priv
->
modified_action
!=
NULL
)
{
um
->
priv
->
modified_action
->
modified
=
FALSE
;
um
->
priv
->
modified_action
=
NULL
;
}
return
;
}
if
(
action
==
NULL
)
{
g_return_if_fail
(
um
->
priv
->
running_not_undoable_actions
>
0
);
return
;
}
/* gtk_text_buffer_get_modified (buffer) == TRUE */
g_return_if_fail
(
um
->
priv
->
modified_action
==
NULL
);
if
(
action
->
order_in_group
>
1
)
um
->
priv
->
modified_undoing_group
=
TRUE
;
while
(
action
->
order_in_group
>
1
)
{
list
=
g_list_next
(
list
);
g_return_if_fail
(
list
!=
NULL
);
action
=
(
GtkSourceUndoAction
*
)
list
->
data
;
g_return_if_fail
(
action
!=
NULL
);
}
action
->
modified
=
TRUE
;
um
->
priv
->
modified_action
=
action
;
}