483 lines
12 KiB
C
483 lines
12 KiB
C
/*
|
|
* mooedit-bookmarks.c
|
|
*
|
|
* Copyright (C) 2004-2006 by Yevgen Muntyan <muntyan@math.tamu.edu>
|
|
*
|
|
* 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.
|
|
*
|
|
* See COPYING file that comes with this distribution.
|
|
*/
|
|
|
|
#define MOOEDIT_COMPILATION
|
|
#include "mooedit/mooedit-bookmarks.h"
|
|
#include "mooedit/mooedit-private.h"
|
|
#include "mooedit/mootextbuffer.h"
|
|
#include "mooutils/moostock.h"
|
|
|
|
|
|
G_DEFINE_TYPE (MooEditBookmark, moo_edit_bookmark, MOO_TYPE_LINE_MARK)
|
|
|
|
|
|
static void disconnect_bookmark (MooEditBookmark *bk);
|
|
|
|
|
|
static void
|
|
moo_edit_bookmark_finalize (GObject *object)
|
|
{
|
|
// MooEditBookmark *bk = MOO_EDIT_BOOKMARK (object);
|
|
G_OBJECT_CLASS(moo_edit_bookmark_parent_class)->finalize (object);
|
|
}
|
|
|
|
|
|
static void
|
|
moo_edit_bookmark_class_init (MooEditBookmarkClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
object_class->finalize = moo_edit_bookmark_finalize;
|
|
}
|
|
|
|
|
|
static void
|
|
moo_edit_bookmark_init (MooEditBookmark *bk)
|
|
{
|
|
g_object_set (bk,
|
|
"visible", TRUE,
|
|
"background", "#E5E5FF",
|
|
NULL);
|
|
}
|
|
|
|
|
|
static void
|
|
bookmarks_changed (MooEdit *edit)
|
|
{
|
|
g_signal_emit_by_name (edit, "bookmarks-changed");
|
|
}
|
|
|
|
|
|
static MooTextBuffer *
|
|
get_moo_buffer (MooEdit *edit)
|
|
{
|
|
return MOO_TEXT_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (edit)));
|
|
}
|
|
|
|
static guint
|
|
get_line_count (MooEdit *edit)
|
|
{
|
|
return gtk_text_buffer_get_line_count (gtk_text_view_get_buffer (GTK_TEXT_VIEW (edit)));
|
|
}
|
|
|
|
|
|
void
|
|
moo_edit_set_enable_bookmarks (MooEdit *edit,
|
|
gboolean enable)
|
|
{
|
|
g_return_if_fail (MOO_IS_EDIT (edit));
|
|
|
|
enable = enable != 0;
|
|
|
|
if (enable != edit->priv->enable_bookmarks)
|
|
{
|
|
edit->priv->enable_bookmarks = enable;
|
|
|
|
if (!enable)
|
|
_moo_edit_delete_bookmarks (edit, FALSE);
|
|
|
|
g_object_notify (G_OBJECT (edit), "enable-bookmarks");
|
|
}
|
|
}
|
|
|
|
|
|
gboolean
|
|
moo_edit_get_enable_bookmarks (MooEdit *edit)
|
|
{
|
|
g_return_val_if_fail (MOO_IS_EDIT (edit), FALSE);
|
|
return edit->priv->enable_bookmarks;
|
|
}
|
|
|
|
|
|
static int
|
|
cmp_bookmarks (MooLineMark *a,
|
|
MooLineMark *b)
|
|
{
|
|
int line_a = moo_line_mark_get_line (a);
|
|
int line_b = moo_line_mark_get_line (b);
|
|
return line_a < line_b ? -1 : (line_a > line_b ? 1 : 0);
|
|
}
|
|
|
|
static gboolean
|
|
update_bookmarks (MooEdit *edit)
|
|
{
|
|
GSList *deleted, *dup, *old, *new, *l;
|
|
|
|
edit->priv->update_bookmarks_idle = 0;
|
|
old = edit->priv->bookmarks;
|
|
edit->priv->bookmarks = NULL;
|
|
|
|
for (deleted = NULL, new = NULL, l = old; l != NULL; l = l->next)
|
|
if (moo_line_mark_get_deleted (MOO_LINE_MARK (l->data)))
|
|
deleted = g_slist_prepend (deleted, l->data);
|
|
else
|
|
new = g_slist_prepend (new, l->data);
|
|
|
|
g_slist_foreach (deleted, (GFunc) disconnect_bookmark, NULL);
|
|
g_slist_foreach (deleted, (GFunc) g_object_unref, NULL);
|
|
g_slist_free (deleted);
|
|
|
|
new = g_slist_sort (new, (GCompareFunc) cmp_bookmarks);
|
|
old = new;
|
|
new = NULL;
|
|
dup = NULL;
|
|
|
|
for (l = old; l != NULL; l = l->next)
|
|
if (new && moo_line_mark_get_line (new->data) == moo_line_mark_get_line (l->data))
|
|
dup = g_slist_prepend (dup, l->data);
|
|
else
|
|
new = g_slist_prepend (new, l->data);
|
|
|
|
while (dup)
|
|
{
|
|
disconnect_bookmark (dup->data);
|
|
moo_text_buffer_delete_line_mark (get_moo_buffer (edit), dup->data);
|
|
g_object_unref (dup->data);
|
|
dup = g_slist_delete_link (dup, dup);
|
|
}
|
|
|
|
edit->priv->bookmarks = g_slist_reverse (new);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static void
|
|
update_bookmarks_now (MooEdit *edit)
|
|
{
|
|
if (edit->priv->update_bookmarks_idle)
|
|
{
|
|
g_source_remove (edit->priv->update_bookmarks_idle);
|
|
edit->priv->update_bookmarks_idle = 0;
|
|
update_bookmarks (edit);
|
|
}
|
|
}
|
|
|
|
|
|
const GSList *
|
|
moo_edit_list_bookmarks (MooEdit *edit)
|
|
{
|
|
g_return_val_if_fail (MOO_IS_EDIT (edit), NULL);
|
|
update_bookmarks_now (edit);
|
|
return edit->priv->bookmarks;
|
|
}
|
|
|
|
|
|
void
|
|
moo_edit_toggle_bookmark (MooEdit *edit,
|
|
guint line)
|
|
{
|
|
MooEditBookmark *bk;
|
|
|
|
g_return_if_fail (MOO_IS_EDIT (edit));
|
|
g_return_if_fail (line < get_line_count (edit));
|
|
|
|
bk = moo_edit_get_bookmark_at_line (edit, line);
|
|
|
|
if (bk)
|
|
moo_edit_remove_bookmark (edit, bk);
|
|
else
|
|
moo_edit_add_bookmark (edit, line, 0);
|
|
}
|
|
|
|
|
|
MooEditBookmark *
|
|
moo_edit_get_bookmark_at_line (MooEdit *edit,
|
|
guint line)
|
|
{
|
|
GSList *list, *l;
|
|
MooEditBookmark *bk;
|
|
|
|
g_return_val_if_fail (MOO_IS_EDIT (edit), NULL);
|
|
g_return_val_if_fail (line < get_line_count (edit), NULL);
|
|
|
|
bk = NULL;
|
|
list = moo_text_buffer_get_line_marks_at_line (get_moo_buffer (edit), line);
|
|
|
|
for (l = list; l != NULL; l = l->next)
|
|
{
|
|
if (MOO_IS_EDIT_BOOKMARK (l->data) && g_slist_find (edit->priv->bookmarks, l->data))
|
|
{
|
|
bk = l->data;
|
|
break;
|
|
}
|
|
}
|
|
|
|
g_slist_free (list);
|
|
return bk;
|
|
}
|
|
|
|
void
|
|
moo_edit_remove_bookmark (MooEdit *edit,
|
|
MooEditBookmark *bookmark)
|
|
{
|
|
g_return_if_fail (MOO_IS_EDIT (edit));
|
|
g_return_if_fail (MOO_IS_EDIT_BOOKMARK (bookmark));
|
|
g_return_if_fail (g_slist_find (edit->priv->bookmarks, bookmark));
|
|
|
|
disconnect_bookmark (bookmark);
|
|
edit->priv->bookmarks = g_slist_remove (edit->priv->bookmarks, bookmark);
|
|
moo_text_buffer_delete_line_mark (get_moo_buffer (edit), MOO_LINE_MARK (bookmark));
|
|
|
|
g_object_unref (bookmark);
|
|
bookmarks_changed (edit);
|
|
}
|
|
|
|
|
|
static guint
|
|
get_unused_bookmark_no (MooEdit *edit)
|
|
{
|
|
guint i;
|
|
const GSList *list;
|
|
char used[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
|
|
list = moo_edit_list_bookmarks (edit);
|
|
|
|
while (list)
|
|
{
|
|
MooEditBookmark *bk = list->data;
|
|
used[bk->no] = 1;
|
|
list = list->next;
|
|
}
|
|
|
|
for (i = 1; i < 10; ++i)
|
|
if (!used[i])
|
|
return i;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
moo_edit_add_bookmark (MooEdit *edit,
|
|
guint line,
|
|
guint no)
|
|
{
|
|
MooEditBookmark *bk;
|
|
|
|
g_return_if_fail (MOO_IS_EDIT (edit));
|
|
g_return_if_fail (line < get_line_count (edit));
|
|
g_return_if_fail (moo_edit_get_bookmark_at_line (edit, line) == NULL);
|
|
|
|
g_object_set (edit, "show-line-marks", TRUE, NULL);
|
|
|
|
bk = g_object_new (MOO_TYPE_EDIT_BOOKMARK, NULL);
|
|
moo_text_buffer_add_line_mark (get_moo_buffer (edit), MOO_LINE_MARK (bk), line);
|
|
g_object_set_data (G_OBJECT (bk), "moo-edit-bookmark", GINT_TO_POINTER (TRUE));
|
|
|
|
if (!no)
|
|
no = get_unused_bookmark_no (edit);
|
|
|
|
bk->no = no;
|
|
|
|
if (no)
|
|
{
|
|
char buf[32];
|
|
g_snprintf (buf, sizeof buf, "<b>%u</b>", no);
|
|
moo_line_mark_set_markup (MOO_LINE_MARK (bk), buf);
|
|
}
|
|
else
|
|
{
|
|
moo_line_mark_set_stock_id (MOO_LINE_MARK (bk), MOO_STOCK_EDIT_BOOKMARK);
|
|
}
|
|
|
|
if (!edit->priv->update_bookmarks_idle)
|
|
edit->priv->bookmarks =
|
|
g_slist_insert_sorted (edit->priv->bookmarks, bk,
|
|
(GCompareFunc) cmp_bookmarks);
|
|
else
|
|
edit->priv->bookmarks = g_slist_prepend (edit->priv->bookmarks, bk);
|
|
|
|
bookmarks_changed (edit);
|
|
}
|
|
|
|
|
|
static void
|
|
disconnect_bookmark (MooEditBookmark *bk)
|
|
{
|
|
g_object_set_data (G_OBJECT (bk), "moo-edit-bookmark", NULL);
|
|
}
|
|
|
|
|
|
void
|
|
_moo_edit_line_mark_moved (MooEdit *edit,
|
|
MooLineMark *mark)
|
|
{
|
|
if (MOO_IS_EDIT_BOOKMARK (mark) &&
|
|
g_object_get_data (G_OBJECT (mark), "moo-edit-bookmark") &&
|
|
!edit->priv->update_bookmarks_idle)
|
|
{
|
|
edit->priv->update_bookmarks_idle =
|
|
g_idle_add ((GSourceFunc) update_bookmarks, edit);
|
|
bookmarks_changed (edit);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
_moo_edit_line_mark_deleted (MooEdit *edit,
|
|
MooLineMark *mark)
|
|
{
|
|
if (MOO_IS_EDIT_BOOKMARK (mark) &&
|
|
g_object_get_data (G_OBJECT (mark), "moo-edit-bookmark") &&
|
|
g_slist_find (edit->priv->bookmarks, mark))
|
|
{
|
|
disconnect_bookmark (MOO_EDIT_BOOKMARK (mark));
|
|
edit->priv->bookmarks = g_slist_remove (edit->priv->bookmarks, mark);
|
|
g_object_unref (mark);
|
|
bookmarks_changed (edit);
|
|
}
|
|
}
|
|
|
|
|
|
gboolean
|
|
_moo_edit_line_mark_clicked (MooTextView *view,
|
|
int line)
|
|
{
|
|
moo_edit_toggle_bookmark (MOO_EDIT (view), line);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
GSList *
|
|
moo_edit_get_bookmarks_in_range (MooEdit *edit,
|
|
int first_line,
|
|
int last_line)
|
|
{
|
|
GSList *all, *range, *l;
|
|
|
|
g_return_val_if_fail (MOO_IS_EDIT (edit), NULL);
|
|
g_return_val_if_fail (first_line >= 0, NULL);
|
|
|
|
if (last_line < 0 || last_line >= (int) get_line_count (edit))
|
|
last_line = get_line_count (edit) - 1;
|
|
|
|
if (first_line > last_line)
|
|
return NULL;
|
|
|
|
all = (GSList*) moo_edit_list_bookmarks (edit);
|
|
|
|
for (l = all, range = NULL; l != NULL; l = l->next)
|
|
{
|
|
int line = moo_line_mark_get_line (l->data);
|
|
|
|
if (line < first_line)
|
|
continue;
|
|
else if (line > last_line)
|
|
break;
|
|
else
|
|
range = g_slist_prepend (range, l->data);
|
|
}
|
|
|
|
return g_slist_reverse (range);
|
|
}
|
|
|
|
|
|
void
|
|
_moo_edit_delete_bookmarks (MooEdit *edit,
|
|
gboolean in_destroy)
|
|
{
|
|
GSList *bookmarks;
|
|
|
|
bookmarks = edit->priv->bookmarks;
|
|
edit->priv->bookmarks = NULL;
|
|
|
|
if (bookmarks)
|
|
{
|
|
while (bookmarks)
|
|
{
|
|
disconnect_bookmark (bookmarks->data);
|
|
|
|
if (!in_destroy)
|
|
moo_text_buffer_delete_line_mark (get_moo_buffer (edit),
|
|
bookmarks->data);
|
|
|
|
g_object_unref (bookmarks->data);
|
|
bookmarks = g_slist_delete_link (bookmarks, bookmarks);
|
|
}
|
|
|
|
if (!in_destroy)
|
|
bookmarks_changed (edit);
|
|
}
|
|
}
|
|
|
|
|
|
MooEditBookmark *
|
|
moo_edit_get_bookmark (MooEdit *edit,
|
|
guint n)
|
|
{
|
|
const GSList *list;
|
|
|
|
g_return_val_if_fail (MOO_IS_EDIT (edit), NULL);
|
|
g_return_val_if_fail (n > 0 && n < 10, NULL);
|
|
|
|
list = moo_edit_list_bookmarks (edit);
|
|
|
|
while (list)
|
|
{
|
|
MooEditBookmark *bk = list->data;
|
|
|
|
if (bk->no == n)
|
|
return bk;
|
|
|
|
list = list->next;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void
|
|
moo_edit_goto_bookmark (MooEdit *edit,
|
|
MooEditBookmark *bk)
|
|
{
|
|
int cursor;
|
|
|
|
g_return_if_fail (MOO_IS_EDIT (edit));
|
|
g_return_if_fail (MOO_IS_EDIT_BOOKMARK (bk));
|
|
|
|
cursor = moo_line_mark_get_line (MOO_LINE_MARK (bk));
|
|
moo_text_view_move_cursor (MOO_TEXT_VIEW (edit), cursor, 0, FALSE, FALSE);
|
|
}
|
|
|
|
|
|
char *
|
|
_moo_edit_bookmark_get_text (MooEditBookmark *bk)
|
|
{
|
|
MooTextBuffer *buffer;
|
|
GtkTextIter start, end;
|
|
char *line;
|
|
|
|
g_return_val_if_fail (MOO_IS_EDIT_BOOKMARK (bk), NULL);
|
|
|
|
buffer = moo_line_mark_get_buffer (MOO_LINE_MARK (bk));
|
|
gtk_text_buffer_get_iter_at_line (GTK_TEXT_BUFFER (buffer), &start,
|
|
moo_line_mark_get_line (MOO_LINE_MARK (bk)));
|
|
end = start;
|
|
if (!gtk_text_iter_ends_line (&end))
|
|
gtk_text_iter_forward_to_line_end (&end);
|
|
|
|
line = g_strstrip (gtk_text_iter_get_slice (&start, &end));
|
|
|
|
#define MAXBOOKMARKCHARS 40
|
|
if (g_utf8_strlen (line, -1) > MAXBOOKMARKCHARS)
|
|
{
|
|
char *tmp;
|
|
* g_utf8_offset_to_pointer (line, MAXBOOKMARKCHARS - 3) = 0;
|
|
tmp = g_strdup_printf ("%s...", line);
|
|
g_free (line);
|
|
line = tmp;
|
|
}
|
|
#undef MAXBOOKMARKCHARS
|
|
|
|
return line;
|
|
}
|