315 lines
7.6 KiB
C
315 lines
7.6 KiB
C
#include "gm-searchable.h"
|
|
|
|
GType
|
|
gm_searchable_get_type() {
|
|
static GType searchable_type = 0;
|
|
|
|
if (searchable_type == 0) {
|
|
static const GTypeInfo searchable_info = {
|
|
sizeof (GmSearchableInterface),
|
|
NULL, /* base_init */
|
|
NULL, /* base_finalize */
|
|
NULL, /* class_init */
|
|
NULL, /* class_finalize */
|
|
NULL, /* class_data */
|
|
0,
|
|
0, /* n_preallocs */
|
|
NULL /* instance_init */
|
|
};
|
|
|
|
searchable_type = g_type_register_static(G_TYPE_INTERFACE,
|
|
"GmSearchable", &searchable_info, 0);
|
|
}
|
|
|
|
return searchable_type;
|
|
}
|
|
|
|
/* Searching implementations */
|
|
gboolean
|
|
gm_searchable_text_view_find_next(GtkTextView *text_view, const gchar *str,
|
|
GmSearchableSearchFlags flags) {
|
|
GtkTextBuffer *buffer = NULL;
|
|
GtkTextIter end, start, match_start, match_end;
|
|
gboolean found = FALSE;
|
|
|
|
g_return_val_if_fail(text_view != NULL, FALSE);
|
|
|
|
if (str == NULL || *str == '\0') {
|
|
return FALSE;
|
|
}
|
|
|
|
buffer = gtk_text_view_get_buffer(text_view);
|
|
|
|
if (buffer) {
|
|
if (!gtk_text_buffer_get_selection_bounds(buffer, &start, &end)) {
|
|
// For editables, search from the insertion position
|
|
if (gtk_text_view_get_editable(text_view)) {
|
|
gtk_text_buffer_get_iter_at_mark(buffer, &end,
|
|
gtk_text_buffer_get_insert(buffer));
|
|
} else {
|
|
// For non editables, search from start (or end, depending on
|
|
// the search direction
|
|
if (flags & GM_SEARCHABLE_SEARCH_FORWARDS) {
|
|
gtk_text_buffer_get_start_iter(buffer, &end);
|
|
} else {
|
|
gtk_text_buffer_get_end_iter(buffer, &end);
|
|
}
|
|
}
|
|
|
|
start = end;
|
|
}
|
|
|
|
if (flags & GM_SEARCHABLE_SEARCH_FORWARDS) {
|
|
found = gtk_text_iter_forward_search(&end, str,
|
|
GTK_TEXT_SEARCH_VISIBLE_ONLY | GTK_TEXT_SEARCH_TEXT_ONLY,
|
|
&match_start, &match_end, NULL);
|
|
} else {
|
|
found = gtk_text_iter_backward_search(&start, str,
|
|
GTK_TEXT_SEARCH_VISIBLE_ONLY | GTK_TEXT_SEARCH_TEXT_ONLY,
|
|
&match_start, &match_end, NULL);
|
|
}
|
|
|
|
if (found) {
|
|
gtk_text_buffer_place_cursor(buffer, &match_start);
|
|
gtk_text_buffer_move_mark_by_name(buffer, "selection_bound",
|
|
&match_end);
|
|
gtk_text_view_scroll_to_iter(text_view, &match_start,
|
|
0.0, FALSE, 0.0, 0.0);
|
|
}
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
gboolean
|
|
gm_searchable_text_view_find_first(GtkTextView *text_view,
|
|
const gchar *str, GmSearchableSearchFlags flags) {
|
|
GtkTextIter iter;
|
|
GtkTextBuffer *buffer;
|
|
|
|
g_return_val_if_fail(text_view != NULL, FALSE);
|
|
|
|
buffer = gtk_text_view_get_buffer(text_view);
|
|
|
|
if (buffer) {
|
|
if (flags & GM_SEARCHABLE_SEARCH_BACKWARDS) {
|
|
gtk_text_buffer_get_end_iter(buffer, &iter);
|
|
} else {
|
|
gtk_text_buffer_get_start_iter(buffer, &iter);
|
|
}
|
|
|
|
gtk_text_buffer_place_cursor(buffer, &iter);
|
|
|
|
return gm_searchable_text_view_find_next(text_view, str, flags);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
gm_searchable_text_view_replace(GtkTextView *text_view, gchar const *replace) {
|
|
GtkTextBuffer *buffer = NULL;
|
|
GtkTextIter end, start;
|
|
|
|
buffer = gtk_text_view_get_buffer(text_view);
|
|
|
|
if (buffer) {
|
|
if (!gtk_text_buffer_get_selection_bounds(buffer, &start, &end)) {
|
|
return FALSE;
|
|
} else {
|
|
gtk_text_buffer_begin_user_action(buffer);
|
|
gtk_text_buffer_delete(buffer, &start, &end);
|
|
gtk_text_buffer_insert(buffer, &start, replace, -1);
|
|
gtk_text_buffer_end_user_action(buffer);
|
|
|
|
return TRUE;
|
|
}
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
gm_searchable_text_view_replace_all(GtkTextView *text_view, gchar const *str,
|
|
gchar const *replace, GmSearchableSearchFlags flags) {
|
|
gboolean found = gm_searchable_text_view_find_first(text_view, str, flags);
|
|
|
|
if (found) {
|
|
while (found) {
|
|
gm_searchable_text_view_replace(text_view, replace);
|
|
found = gm_searchable_text_view_find_next(text_view, str, flags);
|
|
}
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
GtkTextView *
|
|
gm_searchable_interface_get_text_view(GmSearchable *self,
|
|
GmSearchableInterface *iface) {
|
|
g_return_val_if_fail(iface != NULL, FALSE);
|
|
|
|
if (iface->get_text_view == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
return (* iface->get_text_view) (self);
|
|
}
|
|
|
|
GtkTextView *
|
|
gm_searchable_get_text_view(GmSearchable *self) {
|
|
GmSearchableInterface *iface;
|
|
|
|
g_return_val_if_fail(GM_IS_SEARCHABLE(self), FALSE);
|
|
|
|
iface = GM_SEARCHABLE_GET_INTERFACE(self);
|
|
|
|
return gm_searchable_interface_get_text_view(self, iface);
|
|
}
|
|
|
|
gboolean
|
|
gm_searchable_can_find(GmSearchable *self) {
|
|
GmSearchableInterface *iface;
|
|
|
|
g_return_val_if_fail(GM_IS_SEARCHABLE(self), FALSE);
|
|
|
|
iface = GM_SEARCHABLE_GET_INTERFACE(self);
|
|
|
|
g_return_val_if_fail(iface != NULL, FALSE);
|
|
|
|
if (iface->find_first == NULL || iface->find_next == NULL) {
|
|
if (gm_searchable_interface_get_text_view(self, iface) != NULL) {
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
gm_searchable_find_first(GmSearchable *self, const gchar *str,
|
|
GmSearchableSearchFlags flags) {
|
|
GmSearchableInterface *iface;
|
|
GtkTextView *text_view;
|
|
|
|
g_return_val_if_fail(GM_IS_SEARCHABLE(self), FALSE);
|
|
|
|
iface = GM_SEARCHABLE_GET_INTERFACE(self);
|
|
|
|
g_return_val_if_fail(iface != NULL, FALSE);
|
|
|
|
if (iface->find_first == NULL) {
|
|
text_view = gm_searchable_interface_get_text_view(self, iface);
|
|
|
|
if (text_view) {
|
|
return gm_searchable_text_view_find_first(text_view, str, flags);
|
|
} else {
|
|
g_return_val_if_reached(FALSE);
|
|
}
|
|
}
|
|
|
|
return (* iface->find_first) (self, str, flags);
|
|
}
|
|
|
|
gboolean
|
|
gm_searchable_find_next(GmSearchable *self, const gchar *str,
|
|
GmSearchableSearchFlags flags) {
|
|
GmSearchableInterface *iface;
|
|
GtkTextView *text_view;
|
|
|
|
g_return_val_if_fail(GM_IS_SEARCHABLE(self), FALSE);
|
|
|
|
iface = GM_SEARCHABLE_GET_INTERFACE(self);
|
|
|
|
g_return_val_if_fail(iface != NULL, FALSE);
|
|
|
|
if (iface->find_next == NULL) {
|
|
text_view = gm_searchable_interface_get_text_view(self, iface);
|
|
|
|
if (text_view) {
|
|
return gm_searchable_text_view_find_next(text_view, str, flags);
|
|
} else {
|
|
g_return_val_if_reached(FALSE);
|
|
}
|
|
}
|
|
|
|
return (* iface->find_next) (self, str, flags);
|
|
}
|
|
|
|
gboolean
|
|
gm_searchable_can_replace(GmSearchable *self) {
|
|
GmSearchableInterface *iface;
|
|
GtkTextView *text_view;
|
|
|
|
g_return_val_if_fail(GM_IS_SEARCHABLE(self), FALSE);
|
|
|
|
iface = GM_SEARCHABLE_GET_INTERFACE(self);
|
|
|
|
g_return_val_if_fail(iface != NULL, FALSE);
|
|
|
|
if (iface->replace == NULL || iface->replace_all == NULL) {
|
|
text_view = gm_searchable_interface_get_text_view(self, iface);
|
|
if (text_view != NULL && gtk_text_view_get_editable(text_view)) {
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
gm_searchable_replace(GmSearchable *self, const gchar *replace) {
|
|
GmSearchableInterface *iface;
|
|
GtkTextView *text_view;
|
|
|
|
g_return_val_if_fail(GM_IS_SEARCHABLE(self), FALSE);
|
|
|
|
iface = GM_SEARCHABLE_GET_INTERFACE(self);
|
|
|
|
g_return_val_if_fail(iface != NULL, FALSE);
|
|
|
|
if (iface->replace == NULL) {
|
|
text_view = gm_searchable_interface_get_text_view(self, iface);
|
|
|
|
if (text_view && gtk_text_view_get_editable(text_view)) {
|
|
return gm_searchable_text_view_replace(text_view, replace, flags);
|
|
} else {
|
|
g_return_val_if_reached(FALSE);
|
|
}
|
|
}
|
|
|
|
return (* iface->replace) (self, replace, flags);
|
|
}
|
|
|
|
gboolean
|
|
gm_searchable_replace_all(GmSearchable *self, gchar const *str,
|
|
gchar const *replace, GmSearchableSearchFlags flags) {
|
|
GmSearchableInterface *iface;
|
|
GtkTextView *text_view;
|
|
|
|
g_return_val_if_fail(GM_IS_SEARCHABLE(self), FALSE);
|
|
|
|
iface = GM_SEARCHABLE_GET_INTERFACE(self);
|
|
|
|
g_return_val_if_fail(iface != NULL, FALSE);
|
|
|
|
if (iface->replace_all == NULL) {
|
|
text_view = gm_searchable_interface_get_text_view(self, iface);
|
|
|
|
if (text_view && gtk_text_view_get_editable(text_view)) {
|
|
return gm_searchable_text_view_replace_all(text_view, str, replace,
|
|
flags);
|
|
} else {
|
|
g_return_val_if_reached(FALSE);
|
|
}
|
|
}
|
|
|
|
return (* iface->replace_all) (self, str, replace, flags);
|
|
}
|