medit/moo/mooedit/mooedit-script.cpp
2016-10-08 15:26:51 -07:00

850 lines
20 KiB
C++

#include "mooedit/mooedit-script.h"
#include "mooedit/mootextview.h"
#include "mooedit/mootextbuffer.h"
#include "mooutils/mooutils.h"
/**
* moo_edit_can_undo:
**/
gboolean
moo_edit_can_undo (MooEdit *doc)
{
g_return_val_if_fail (MOO_IS_EDIT (doc), FALSE);
return moo_text_buffer_can_undo (MOO_TEXT_BUFFER (moo_edit_get_buffer (doc)));
}
/**
* moo_edit_can_redo:
**/
gboolean
moo_edit_can_redo (MooEdit *doc)
{
g_return_val_if_fail (MOO_IS_EDIT (doc), FALSE);
return moo_text_buffer_can_redo (MOO_TEXT_BUFFER (moo_edit_get_buffer (doc)));
}
/**
* moo_edit_undo:
**/
gboolean
moo_edit_undo (MooEdit *doc)
{
g_return_val_if_fail (MOO_IS_EDIT (doc), FALSE);
return moo_text_view_undo (MOO_TEXT_VIEW (moo_edit_get_view (doc)));
}
/**
* moo_edit_redo:
**/
gboolean
moo_edit_redo (MooEdit *doc)
{
g_return_val_if_fail (MOO_IS_EDIT (doc), FALSE);
return moo_text_view_redo (MOO_TEXT_VIEW (moo_edit_get_view (doc)));
}
/**
* moo_edit_begin_non_undoable_action:
**/
void
moo_edit_begin_non_undoable_action (MooEdit *doc)
{
g_return_if_fail (MOO_IS_EDIT (doc));
moo_text_buffer_begin_non_undoable_action (MOO_TEXT_BUFFER (moo_edit_get_buffer (doc)));
}
/**
* moo_edit_begin_user_action:
**/
void
moo_edit_begin_user_action (MooEdit *doc)
{
g_return_if_fail (MOO_IS_EDIT (doc));
gtk_text_buffer_begin_user_action (moo_edit_get_buffer (doc));
}
/**
* moo_edit_end_user_action:
**/
void
moo_edit_end_user_action (MooEdit *doc)
{
g_return_if_fail (MOO_IS_EDIT (doc));
gtk_text_buffer_end_user_action (moo_edit_get_buffer (doc));
}
/**
* moo_edit_end_non_undoable_action:
**/
void
moo_edit_end_non_undoable_action (MooEdit *doc)
{
g_return_if_fail (MOO_IS_EDIT (doc));
moo_text_buffer_end_non_undoable_action (MOO_TEXT_BUFFER (moo_edit_get_buffer (doc)));
}
/**
* moo_edit_get_start_pos:
*
* Returns: (transfer full): Iterator which points to the beginning of the document.
**/
GtkTextIter *
moo_edit_get_start_pos (MooEdit *doc)
{
GtkTextIter iter;
g_return_val_if_fail (MOO_IS_EDIT (doc), NULL);
gtk_text_buffer_get_start_iter (moo_edit_get_buffer (doc), &iter);
return gtk_text_iter_copy (&iter);
}
/**
* moo_edit_get_end_pos:
*
* Returns: (transfer full): Iterator which points to the end of the document,
* i.e. the position past the last character in the document.
**/
GtkTextIter *
moo_edit_get_end_pos (MooEdit *doc)
{
GtkTextIter iter;
g_return_val_if_fail (MOO_IS_EDIT (doc), NULL);
gtk_text_buffer_get_end_iter (moo_edit_get_buffer (doc), &iter);
return gtk_text_iter_copy (&iter);
}
static void
get_iter_at_cursor (MooEdit *doc,
GtkTextIter *iter)
{
GtkTextBuffer *buffer = moo_edit_get_buffer (doc);
gtk_text_buffer_get_iter_at_mark (buffer, iter, gtk_text_buffer_get_insert (buffer));
}
/**
* moo_edit_get_cursor_pos:
*
* Returns: (transfer full): Iterator which points to the current cursor (insertion point) position.
* In the case when text selection is not empty, it points to one of the ends of the selection.
**/
GtkTextIter *
moo_edit_get_cursor_pos (MooEdit *doc)
{
GtkTextIter iter;
g_return_val_if_fail (MOO_IS_EDIT (doc), NULL);
get_iter_at_cursor (doc, &iter);
return gtk_text_iter_copy (&iter);
}
/**
* moo_edit_get_selection_start_pos:
*
* Returns: (transfer full): Iterator which points to the beginning of the current
* text selection. If the selection is empty, it returns the current cursor position.
**/
GtkTextIter *
moo_edit_get_selection_start_pos (MooEdit *doc)
{
GtkTextIter iter;
GtkTextBuffer *buffer;
g_return_val_if_fail (MOO_IS_EDIT (doc), NULL);
buffer = moo_edit_get_buffer (doc);
gtk_text_buffer_get_selection_bounds (buffer, &iter, NULL);
return gtk_text_iter_copy (&iter);
}
/**
* moo_edit_get_selection_end_pos:
*
* Returns: (transfer full): Iterator which points to the end of the current
* text selection. If the selection is empty, it returns the current cursor position.
**/
GtkTextIter *
moo_edit_get_selection_end_pos (MooEdit *doc)
{
GtkTextIter iter;
GtkTextBuffer *buffer;
g_return_val_if_fail (MOO_IS_EDIT (doc), NULL);
buffer = moo_edit_get_buffer (doc);
gtk_text_buffer_get_selection_bounds (buffer, NULL, &iter);
return gtk_text_iter_copy (&iter);
}
/**
* moo_edit_set_cursor_pos:
*
* Move the text cursor to the given position. No text is selected
* after this operation, use one of the select* methods if you need to select a range of text.
**/
void
moo_edit_set_cursor_pos (MooEdit *doc,
const GtkTextIter *pos)
{
g_return_if_fail (MOO_IS_EDIT (doc));
gtk_text_buffer_place_cursor (moo_edit_get_buffer (doc), pos);
}
/**
* moo_edit_get_char_count:
**/
int
moo_edit_get_char_count (MooEdit *doc)
{
g_return_val_if_fail (MOO_IS_EDIT (doc), 0);
return gtk_text_buffer_get_char_count (moo_edit_get_buffer (doc));
}
/**
* moo_edit_get_line_count:
**/
int
moo_edit_get_line_count (MooEdit *doc)
{
g_return_val_if_fail (MOO_IS_EDIT (doc), 0);
return gtk_text_buffer_get_line_count (moo_edit_get_buffer (doc));
}
/**
* moo_edit_get_line_at_cursor:
*
* Returns: (type index): %INDEXBASE-based number of the current cursor line.
**/
int
moo_edit_get_line_at_cursor (MooEdit *doc)
{
GtkTextIter iter;
g_return_val_if_fail (MOO_IS_EDIT (doc), 0);
get_iter_at_cursor (doc, &iter);
return gtk_text_iter_get_line (&iter);
}
/**
* moo_edit_get_line_at_pos:
*
* Returns: (type index): %INDEXBASE-based number of the line at the given position.
**/
int
moo_edit_get_line_at_pos (MooEdit *doc,
const GtkTextIter *pos)
{
g_return_val_if_fail (MOO_IS_EDIT (doc), 0);
return gtk_text_iter_get_line (pos);
}
/**
* moo_edit_get_pos_at_line:
*
* @doc:
* @line: (type index): %INDEXBASE-based line number.
*
* Returns: (transfer full): Iterator which points to the beginning of the given line.
**/
GtkTextIter *
moo_edit_get_pos_at_line (MooEdit *doc,
int line)
{
GtkTextIter iter;
GtkTextBuffer *buffer;
g_return_val_if_fail (MOO_IS_EDIT (doc), NULL);
buffer = moo_edit_get_buffer (doc);
g_return_val_if_fail (line >= 0 && line <= gtk_text_buffer_get_line_count (buffer), NULL);
gtk_text_buffer_get_iter_at_line (buffer, &iter, line);
return gtk_text_iter_copy (&iter);
}
/**
* moo_edit_get_pos_at_line_end:
*
* @doc:
* @line: (type index): %INDEXBASE-based line number.
*
* Returns: (transfer full): Iterator which points to the end of the given line (i.e. the position
* before the line end character(s)).
**/
GtkTextIter *
moo_edit_get_pos_at_line_end (MooEdit *doc,
int line)
{
GtkTextIter iter;
GtkTextBuffer *buffer;
g_return_val_if_fail (MOO_IS_EDIT (doc), NULL);
buffer = moo_edit_get_buffer (doc);
g_return_val_if_fail (line >= 0 && line <= gtk_text_buffer_get_line_count (buffer), NULL);
gtk_text_buffer_get_iter_at_line (buffer, &iter, line);
if (!gtk_text_iter_ends_line (&iter))
gtk_text_iter_forward_to_line_end (&iter);
return gtk_text_iter_copy (&iter);
}
/**
* moo_edit_get_char_at_pos:
**/
gunichar
moo_edit_get_char_at_pos (MooEdit *doc,
const GtkTextIter *pos)
{
g_return_val_if_fail (MOO_IS_EDIT (doc), 0);
return gtk_text_iter_get_char (pos);
}
/**
* moo_edit_get_text:
*
* @doc:
* @start: (allow-none) (default NULL)
* @end: (allow-none) (default NULL)
*
* Returns: (type utf8): text between @start and @end. If @end is
* missing then it returns text from @start to the end of document;
* and if both @start and @end are missing then it returns whole
* document content.
**/
char *
moo_edit_get_text (MooEdit *doc,
const GtkTextIter *start,
const GtkTextIter *end)
{
GtkTextBuffer *buffer;
GtkTextIter start_iter;
GtkTextIter end_iter;
g_return_val_if_fail (MOO_IS_EDIT (doc), NULL);
buffer = moo_edit_get_buffer (doc);
if (start)
start_iter = *start;
else
gtk_text_buffer_get_start_iter (buffer, &start_iter);
if (end)
end_iter = *end;
else
gtk_text_buffer_get_end_iter (buffer, &end_iter);
return gtk_text_buffer_get_slice (buffer, &start_iter, &end_iter, TRUE);
}
/**
* moo_edit_get_line_text:
*
* @doc:
* @line: (type index) (default -1): %INDEXBASE-based line number.
*
* Returns: (type utf8): text at line @line, not including line
* end characters. If @line is missing, returns text at cursor line.
**/
char *
moo_edit_get_line_text (MooEdit *doc,
int line)
{
GtkTextBuffer *buffer;
GtkTextIter start, end;
g_return_val_if_fail (MOO_IS_EDIT (doc), NULL);
buffer = moo_edit_get_buffer (doc);
if (line >= 0)
{
g_return_val_if_fail (line < gtk_text_buffer_get_line_count (buffer), NULL);
gtk_text_buffer_get_iter_at_line (buffer, &start, line);
}
else
{
get_iter_at_cursor (doc, &start);
gtk_text_iter_set_line_offset (&start, 0);
}
end = start;
if (!gtk_text_iter_ends_line (&end))
gtk_text_iter_forward_to_line_end (&end);
return moo_edit_get_text (doc, &start, &end);
}
/**
* moo_edit_get_line_text_at_pos:
*
* Returns: (type utf8): text at line which contains position @pos,
* not including the line end character(s).
**/
char *
moo_edit_get_line_text_at_pos (MooEdit *doc,
const GtkTextIter *pos)
{
return moo_edit_get_line_text (doc, gtk_text_iter_get_line (pos));
}
/**
* moo_edit_set_text:
*
* @doc:
* @text: (type const-utf8)
**/
void
moo_edit_set_text (MooEdit *doc,
const char *text)
{
GtkTextBuffer *buffer;
GtkTextIter start, end;
g_return_if_fail (MOO_IS_EDIT (doc));
g_return_if_fail (text != NULL);
buffer = moo_edit_get_buffer (doc);
gtk_text_buffer_get_bounds (buffer, &start, &end);
gtk_text_buffer_delete (buffer, &start, &end);
gtk_text_buffer_insert (buffer, &start, text, -1);
}
/**
* moo_edit_insert_text:
*
* @doc:
* @text: (type const-utf8)
* @where: (allow-none) (default NULL)
*
* Insert @text at position @where or at cursor position if @where is %NULL.
**/
void
moo_edit_insert_text (MooEdit *doc,
const char *text,
GtkTextIter *where)
{
GtkTextIter iter;
GtkTextBuffer *buffer;
g_return_if_fail (MOO_IS_EDIT (doc));
g_return_if_fail (text != NULL);
buffer = moo_edit_get_buffer (doc);
if (where)
iter = *where;
else
gtk_text_buffer_get_iter_at_mark (buffer, &iter, gtk_text_buffer_get_insert (buffer));
gtk_text_buffer_insert (buffer, &iter, text, -1);
if (where)
*where = iter;
}
/**
* moo_edit_replace_text:
*
* @doc:
* @start:
* @end:
* @text: (type const-utf8)
**/
void
moo_edit_replace_text (MooEdit *doc,
GtkTextIter *start,
GtkTextIter *end,
const char *text)
{
GtkTextBuffer *buffer;
g_return_if_fail (MOO_IS_EDIT (doc));
g_return_if_fail (start != NULL);
g_return_if_fail (end != NULL);
g_return_if_fail (text != NULL);
buffer = moo_edit_get_buffer (doc);
gtk_text_buffer_delete (buffer, start, end);
gtk_text_buffer_insert (buffer, start, text, -1);
*end = *start;
}
/**
* moo_edit_delete_text:
**/
void
moo_edit_delete_text (MooEdit *doc,
GtkTextIter *start,
GtkTextIter *end)
{
GtkTextBuffer *buffer;
g_return_if_fail (MOO_IS_EDIT (doc));
g_return_if_fail (start != NULL);
g_return_if_fail (end != NULL);
buffer = moo_edit_get_buffer (doc);
gtk_text_buffer_delete (buffer, start, end);
}
/**
* moo_edit_append_text:
*
* @doc:
* @text: (type const-utf8)
*
* Append text to the end of document.
**/
void
moo_edit_append_text (MooEdit *doc,
const char *text)
{
GtkTextBuffer *buffer;
GtkTextIter iter;
g_return_if_fail (MOO_IS_EDIT (doc));
g_return_if_fail (text != NULL);
buffer = moo_edit_get_buffer (doc);
gtk_text_buffer_get_end_iter (buffer, &iter);
gtk_text_buffer_insert (buffer, &iter, text, -1);
}
/**
* moo_edit_clear:
*
* Remove all text from document.
**/
void
moo_edit_clear (MooEdit *doc)
{
GtkTextBuffer *buffer;
GtkTextIter start, end;
g_return_if_fail (MOO_IS_EDIT (doc));
buffer = moo_edit_get_buffer (doc);
gtk_text_buffer_get_bounds (buffer, &start, &end);
gtk_text_buffer_delete (buffer, &start, &end);
}
/**
* moo_edit_cut:
*
* Cut selection to clipboard.
**/
void
moo_edit_cut (MooEdit *doc)
{
g_return_if_fail (MOO_IS_EDIT (doc));
g_signal_emit_by_name (moo_edit_get_view (doc), "cut-clipboard");
}
/**
* moo_edit_copy:
*
* Copy selection to clipboard.
**/
void
moo_edit_copy (MooEdit *doc)
{
g_return_if_fail (MOO_IS_EDIT (doc));
g_signal_emit_by_name (moo_edit_get_view (doc), "copy-clipboard");
}
/**
* moo_edit_paste:
*
* Paste clipboard contents.
**/
void
moo_edit_paste (MooEdit *doc)
{
g_return_if_fail (MOO_IS_EDIT (doc));
g_signal_emit_by_name (moo_edit_get_view (doc), "paste-clipboard");
}
/**
* moo_edit_select_range:
*
* Select text from @start to @end.
**/
void
moo_edit_select_range (MooEdit *doc,
const GtkTextIter *start,
const GtkTextIter *end)
{
g_return_if_fail (MOO_IS_EDIT (doc));
g_return_if_fail (start != NULL);
g_return_if_fail (end != NULL);
gtk_text_buffer_select_range (moo_edit_get_buffer (doc), start, end);
}
/**
* moo_edit_select_lines:
*
* @doc:
* @start: (type index)
* @end: (type index) (default -1)
**/
void
moo_edit_select_lines (MooEdit *doc,
int start,
int end)
{
GtkTextBuffer *buffer;
GtkTextIter start_iter, end_iter;
g_return_if_fail (MOO_IS_EDIT (doc));
buffer = moo_edit_get_buffer (doc);
if (end < 0)
end = start;
if (start > end)
{
int tmp = start;
start = end;
end = tmp;
}
g_return_if_fail (start >= 0 && start < gtk_text_buffer_get_line_count (buffer));
g_return_if_fail (end >= 0 && end < gtk_text_buffer_get_line_count (buffer));
gtk_text_buffer_get_iter_at_line (buffer, &start_iter, start);
gtk_text_buffer_get_iter_at_line (buffer, &end_iter, end);
gtk_text_iter_forward_line (&end_iter);
gtk_text_buffer_select_range (buffer, &start_iter, &end_iter);
}
/**
* moo_edit_select_lines_at_pos:
*
* @doc:
* @start:
* @end: (allow-none) (default NULL)
*
* Select lines which span the range from @start to @end (including @end position).
* If @end is %NULL, then it selects single line which contains position @start.
**/
void
moo_edit_select_lines_at_pos (MooEdit *doc,
const GtkTextIter *start,
const GtkTextIter *end)
{
GtkTextBuffer *buffer;
GtkTextIter start_iter, end_iter;
g_return_if_fail (MOO_IS_EDIT (doc));
g_return_if_fail (start != NULL);
buffer = moo_edit_get_buffer (doc);
start_iter = *start;
end_iter = end ? *end : *start;
gtk_text_iter_order (&start_iter, &end_iter);
gtk_text_iter_forward_line (&end_iter);
gtk_text_buffer_select_range (buffer, &start_iter, &end_iter);
}
/**
* moo_edit_select_all:
**/
void
moo_edit_select_all (MooEdit *doc)
{
GtkTextBuffer *buffer;
GtkTextIter start, end;
g_return_if_fail (MOO_IS_EDIT (doc));
buffer = moo_edit_get_buffer (doc);
gtk_text_buffer_get_bounds (buffer, &start, &end);
gtk_text_buffer_select_range (buffer, &start, &end);
}
static void
get_selected_lines_bounds (GtkTextBuffer *buf,
GtkTextIter *start,
GtkTextIter *end,
gboolean *cursor_at_next_line)
{
if (cursor_at_next_line)
*cursor_at_next_line = FALSE;
gtk_text_buffer_get_selection_bounds (buf, start, end);
gtk_text_iter_set_line_offset (start, 0);
if (gtk_text_iter_starts_line (end) && !gtk_text_iter_equal (start, end))
{
gtk_text_iter_backward_line (end);
if (cursor_at_next_line)
*cursor_at_next_line = TRUE;
}
if (!gtk_text_iter_ends_line (end))
gtk_text_iter_forward_to_line_end (end);
}
/**
* moo_edit_get_selected_lines:
*
* Returns selected lines as a list of strings, one string for each line,
* line terminator characters not included. If nothing is selected, then
* line at cursor is returned.
*
* Returns: (type strv)
**/
char **
moo_edit_get_selected_lines (MooEdit *doc)
{
GtkTextIter start, end;
GtkTextBuffer *buf;
char *text;
char **lines;
g_return_val_if_fail (MOO_IS_EDIT (doc), NULL);
buf = moo_edit_get_buffer (doc);
get_selected_lines_bounds (buf, &start, &end, NULL);
text = gtk_text_buffer_get_slice (buf, &start, &end, TRUE);
lines = moo_splitlines (text);
if (!lines)
{
lines = g_new (char*, 2);
lines[0] = g_strdup ("");
lines[1] = NULL;
}
g_free (text);
return lines;
}
static char *
join_lines (char **strv)
{
char **p;
GString *string = g_string_new (NULL);
for (p = strv; p && *p; ++p)
{
if (p != strv)
g_string_append_c (string, '\n');
g_string_append (string, *p);
}
return g_string_free (string, FALSE);
}
/**
* moo_edit_replace_selected_lines:
*
* @doc:
* @replacement: (type strv) (allow-none): list of lines to replace
* selected lines with, maybe empty
*
* replace selected lines with @replacement. Similar to
* moo_edit_replace_selected_text(), but selection is extended to include
* whole lines. If nothing is selected, then line at cursor is replaced.
**/
void
moo_edit_replace_selected_lines (MooEdit *doc,
char **replacement)
{
GtkTextBuffer *buf;
GtkTextIter start, end;
gboolean cursor_at_next_line;
g_return_if_fail (MOO_IS_EDIT (doc));
buf = moo_edit_get_buffer (doc);
get_selected_lines_bounds (buf, &start, &end, &cursor_at_next_line);
gtk_text_buffer_delete (buf, &start, &end);
if (replacement)
{
char *text = join_lines (replacement);
gtk_text_buffer_insert (buf, &start, text, -1);
g_free (text);
}
if (cursor_at_next_line)
{
gtk_text_iter_forward_line (&start);
gtk_text_buffer_place_cursor (buf, &start);
}
}
/**
* moo_edit_get_selected_text:
*
* Returns: (type utf8): selected text.
**/
char *
moo_edit_get_selected_text (MooEdit *doc)
{
GtkTextBuffer *buf;
GtkTextIter start, end;
g_return_val_if_fail (MOO_IS_EDIT (doc), NULL);
buf = moo_edit_get_buffer (doc);
gtk_text_buffer_get_selection_bounds(buf, &start, &end);
return gtk_text_buffer_get_slice(buf, &start, &end, TRUE);
}
/**
* moo_edit_delete_selected_text:
**/
void
moo_edit_delete_selected_text (MooEdit *doc)
{
moo_edit_replace_selected_text (doc, "");
}
/**
* moo_edit_delete_selected_lines:
*
* Delete selected lines. Similar to moo_edit_delete_selected_text() but
* selection is extended to include whole lines. If no text is selected then
* line at cursor is deleted.
**/
void
moo_edit_delete_selected_lines (MooEdit *doc)
{
moo_edit_replace_selected_lines (doc, NULL);
}
/**
* moo_edit_replace_selected_text:
*
* @doc:
* @replacement: (type const-utf8)
*
* Replace selected text with string @replacement. If nothing is selected,
* then @replacement is inserted at cursor.
**/
void
moo_edit_replace_selected_text (MooEdit *doc,
const char *replacement)
{
GtkTextBuffer *buf;
GtkTextIter start, end;
g_return_if_fail (MOO_IS_EDIT (doc));
g_return_if_fail (replacement != NULL);
buf = moo_edit_get_buffer (doc);
gtk_text_buffer_get_selection_bounds (buf, &start, &end);
gtk_text_buffer_delete (buf, &start, &end);
if (*replacement)
gtk_text_buffer_insert (buf, &start, replacement, -1);
gtk_text_buffer_place_cursor (buf, &start);
}
/**
* moo_edit_has_selection:
**/
gboolean
moo_edit_has_selection (MooEdit *doc)
{
return moo_text_buffer_has_selection (MOO_TEXT_BUFFER (moo_edit_get_buffer (doc)));
}