Port to the GtkSourceView search functionality

This also gives us support for regex search
This commit is contained in:
JosephMcc 2017-01-28 19:12:36 -08:00
parent 0a9588ef63
commit 511fc3dfec
19 changed files with 503 additions and 2216 deletions

View File

@ -64,7 +64,6 @@ IGNORE_HFILES= \
# Do not parse them to make the docs. # Do not parse them to make the docs.
IGNORE_HFILES += \ IGNORE_HFILES += \
bacon-message-connection.h \ bacon-message-connection.h \
xedtextregion.h \
sexy-icon-entry.h sexy-icon-entry.h
# Images to copy into HTML directory. # Images to copy into HTML directory.

View File

@ -36,7 +36,6 @@ xed_app_activatable_get_type
<SECTION> <SECTION>
<FILE>xed-document</FILE> <FILE>xed-document</FILE>
XedSearchFlags
XedDocumentPrivate XedDocumentPrivate
<TITLE>XedDocument</TITLE> <TITLE>XedDocument</TITLE>
XedDocument XedDocument
@ -58,21 +57,7 @@ xed_document_is_untouched
xed_document_is_untitled xed_document_is_untitled
xed_document_get_deleted xed_document_get_deleted
xed_document_goto_line xed_document_goto_line
xed_document_set_search_text
xed_document_get_search_text
xed_document_get_can_search_again
xed_document_search_forward
xed_document_search_backward
xed_document_replace_all
xed_document_set_language xed_document_set_language
xed_document_set_enable_search_highlighting
xed_document_get_enable_search_highlighting
XED_SEARCH_IS_DONT_SET_FLAGS
XED_SEARCH_SET_DONT_SET_FLAGS
XED_SEARCH_IS_ENTIRE_WORD
XED_SEARCH_SET_ENTIRE_WORD
XED_SEARCH_IS_CASE_SENSITIVE
XED_SEARCH_SET_CASE_SENSITIVE
<SUBSECTION Standard> <SUBSECTION Standard>
XED_DOCUMENT XED_DOCUMENT
XED_IS_DOCUMENT XED_IS_DOCUMENT
@ -517,8 +502,6 @@ g_utf8_caselessnmatch
xed_utils_set_atk_name_description xed_utils_set_atk_name_description
xed_utils_set_atk_relation xed_utils_set_atk_relation
xed_utils_uri_exists xed_utils_uri_exists
xed_utils_escape_search_text
xed_utils_unescape_search_text
xed_utils_get_stdin xed_utils_get_stdin
xed_warning xed_warning
xed_utils_make_valid_utf8 xed_utils_make_valid_utf8

View File

@ -35,6 +35,7 @@
#include <xed/xed-debug.h> #include <xed/xed-debug.h>
#include <xed/xed-statusbar.h> #include <xed/xed-statusbar.h>
#include <xed/xed-utils.h> #include <xed/xed-utils.h>
#include <gtksourceview/gtksource.h>
#include "xed-spell-checker.h" #include "xed-spell-checker.h"
#include "xed-spell-checker-dialog.h" #include "xed-spell-checker-dialog.h"
@ -704,7 +705,8 @@ change_all_cb (XedSpellCheckerDialog *dlg,
CheckRange *range; CheckRange *range;
gchar *w = NULL; gchar *w = NULL;
GtkTextIter start, end; GtkTextIter start, end;
gint flags = 0; GtkSourceSearchSettings *search_settings;
GtkSourceSearchContext *search_context;
xed_debug (DEBUG_PLUGINS); xed_debug (DEBUG_PLUGINS);
@ -739,16 +741,24 @@ change_all_cb (XedSpellCheckerDialog *dlg,
g_free (w); g_free (w);
XED_SEARCH_SET_CASE_SENSITIVE (flags, TRUE); search_settings = gtk_source_search_settings_new ();
XED_SEARCH_SET_ENTIRE_WORD (flags, TRUE); gtk_source_search_settings_set_case_sensitive (search_settings, TRUE);
gtk_source_search_settings_set_at_word_boundaries (search_settings, TRUE);
gtk_source_search_settings_set_search_text (search_settings, word);
/* CHECK: currently this function does escaping etc */ search_context = gtk_source_search_context_new (GTK_SOURCE_BUFFER (doc), search_settings);
xed_document_replace_all (doc, word, change, flags);
gtk_source_search_context_set_highlight (search_context, FALSE);
gtk_source_search_context_replace_all (search_context, change, -1, NULL);
update_current (doc, range->mw_start + g_utf8_strlen (change, -1)); update_current (doc, range->mw_start + g_utf8_strlen (change, -1));
/* go to next misspelled word */ /* go to next misspelled word */
ignore_cb (dlg, word, view); ignore_cb (dlg, word, view);
g_object_unref (search_settings);
g_object_unref (search_context);
} }
static void static void

View File

@ -23,7 +23,7 @@
#include <string.h> #include <string.h>
#include "xed-spell-utils.h" #include "xed-spell-utils.h"
#include <gtksourceview/gtksourcebuffer.h> #include <gtksourceview/gtksource.h>
gboolean gboolean
xed_spell_utils_is_digit (const char *text, gssize length) xed_spell_utils_is_digit (const char *text, gssize length)

View File

@ -60,7 +60,6 @@ NOINST_H_FILES = \
xed-settings.h \ xed-settings.h \
xed-status-combo-box.h \ xed-status-combo-box.h \
xed-tab-label.h \ xed-tab-label.h \
xedtextregion.h \
xed-ui.h \ xed-ui.h \
xed-utils.h \ xed-utils.h \
xed-view-frame.h \ xed-view-frame.h \
@ -146,8 +145,7 @@ libxed_c_files = \
xed-view-activatable.c \ xed-view-activatable.c \
xed-view-frame.c \ xed-view-frame.c \
xed-window.c \ xed-window.c \
xed-window-activatable.c \ xed-window-activatable.c
xedtextregion.c
libxed_la_SOURCES = \ libxed_la_SOURCES = \
$(BUILT_SOURCES) \ $(BUILT_SOURCES) \

View File

@ -14,18 +14,25 @@
#include "xed-searchbar.h" #include "xed-searchbar.h"
#include "xed-view-frame.h" #include "xed-view-frame.h"
// void
// _xed_cmd_search_find (GtkAction *action,
// XedWindow *window)
// {
// xed_searchbar_show (XED_SEARCHBAR (xed_window_get_searchbar (window)), FALSE);
// }
void void
_xed_cmd_search_find (GtkAction *action, _xed_cmd_search_find (GtkAction *action,
XedWindow *window) XedWindow *window)
{ {
xed_searchbar_show (XED_SEARCHBAR (xed_window_get_searchbar (window)), FALSE); xed_searchbar_show (XED_SEARCHBAR (xed_window_get_searchbar (window)), SEARCH_MODE_SEARCH);
} }
void void
_xed_cmd_search_replace (GtkAction *action, _xed_cmd_search_replace (GtkAction *action,
XedWindow *window) XedWindow *window)
{ {
xed_searchbar_show (XED_SEARCHBAR (xed_window_get_searchbar (window)), TRUE); xed_searchbar_show (XED_SEARCHBAR (xed_window_get_searchbar (window)), SEARCH_MODE_REPLACE);
} }
void void
@ -54,7 +61,7 @@ _xed_cmd_search_clear_highlight (XedWindow *window)
doc = xed_window_get_active_document (window); doc = xed_window_get_active_document (window);
if (doc != NULL) if (doc != NULL)
{ {
xed_document_set_search_text (XED_DOCUMENT(doc), "", XED_SEARCH_DONT_SET_FLAGS); _xed_document_set_search_context (doc, NULL);
} }
} }

View File

@ -49,7 +49,6 @@
#include "xed-document-saver.h" #include "xed-document-saver.h"
#include "xed-marshal.h" #include "xed-marshal.h"
#include "xed-enum-types.h" #include "xed-enum-types.h"
#include "xedtextregion.h"
#ifndef ENABLE_GVFS_METADATA #ifndef ENABLE_GVFS_METADATA
#include "xed-metadata-manager.h" #include "xed-metadata-manager.h"
@ -86,14 +85,6 @@ static void xed_document_save_real (XedDocument *doc,
GFile *location, GFile *location,
const XedEncoding *encoding, const XedEncoding *encoding,
XedDocumentSaveFlags flags); XedDocumentSaveFlags flags);
static void insert_text_cb (XedDocument *doc,
GtkTextIter *pos,
const gchar *text,
gint length);
static void delete_range_cb (XedDocument *doc,
GtkTextIter *start,
GtkTextIter *end);
struct _XedDocumentPrivate struct _XedDocumentPrivate
{ {
@ -113,9 +104,7 @@ struct _XedDocumentPrivate
GTimeVal mtime; GTimeVal mtime;
GTimeVal time_of_last_save_or_load; GTimeVal time_of_last_save_or_load;
guint search_flags; GtkSourceSearchContext *search_context;
gchar *search_text;
gint num_of_lines_search_text;
XedDocumentNewlineType newline_type; XedDocumentNewlineType newline_type;
@ -129,10 +118,6 @@ struct _XedDocumentPrivate
/* Saving stuff */ /* Saving stuff */
XedDocumentSaver *saver; XedDocumentSaver *saver;
/* Search highlighting support variables */
XedTextRegion *to_search_region;
GtkTextTag *found_tag;
GtkTextTag *error_tag; GtkTextTag *error_tag;
/* Mount operation factory */ /* Mount operation factory */
@ -158,8 +143,6 @@ enum
PROP_MIME_TYPE, PROP_MIME_TYPE,
PROP_READ_ONLY, PROP_READ_ONLY,
PROP_ENCODING, PROP_ENCODING,
PROP_CAN_SEARCH_AGAIN,
PROP_ENABLE_SEARCH_HIGHLIGHTING,
PROP_NEWLINE_TYPE PROP_NEWLINE_TYPE
}; };
@ -172,7 +155,6 @@ enum
SAVE, SAVE,
SAVING, SAVING,
SAVED, SAVED,
SEARCH_HIGHLIGHT_UPDATED,
LAST_SIGNAL LAST_SIGNAL
}; };
@ -284,6 +266,7 @@ xed_document_dispose (GObject *object)
g_clear_object (&doc->priv->editor_settings); g_clear_object (&doc->priv->editor_settings);
g_clear_object (&doc->priv->metadata_info); g_clear_object (&doc->priv->metadata_info);
g_clear_object (&doc->priv->location); g_clear_object (&doc->priv->location);
g_clear_object (&doc->priv->search_context);
doc->priv->dispose_has_run = TRUE; doc->priv->dispose_has_run = TRUE;
@ -303,13 +286,6 @@ xed_document_finalize (GObject *object)
} }
g_free (doc->priv->content_type); g_free (doc->priv->content_type);
g_free (doc->priv->search_text);
if (doc->priv->to_search_region != NULL)
{
/* we can't delete marks if we're finalizing the buffer */
xed_text_region_destroy (doc->priv->to_search_region, FALSE);
}
G_OBJECT_CLASS (xed_document_parent_class)->finalize (object); G_OBJECT_CLASS (xed_document_parent_class)->finalize (object);
} }
@ -342,12 +318,6 @@ xed_document_get_property (GObject *object,
case PROP_ENCODING: case PROP_ENCODING:
g_value_set_boxed (value, doc->priv->encoding); g_value_set_boxed (value, doc->priv->encoding);
break; break;
case PROP_CAN_SEARCH_AGAIN:
g_value_set_boolean (value, xed_document_get_can_search_again (doc));
break;
case PROP_ENABLE_SEARCH_HIGHLIGHTING:
g_value_set_boolean (value, xed_document_get_enable_search_highlighting (doc));
break;
case PROP_NEWLINE_TYPE: case PROP_NEWLINE_TYPE:
g_value_set_enum (value, doc->priv->newline_type); g_value_set_enum (value, doc->priv->newline_type);
break; break;
@ -386,9 +356,6 @@ xed_document_set_property (GObject *object,
case PROP_CONTENT_TYPE: case PROP_CONTENT_TYPE:
xed_document_set_content_type (doc, g_value_get_string (value)); xed_document_set_content_type (doc, g_value_get_string (value));
break; break;
case PROP_ENABLE_SEARCH_HIGHLIGHTING:
xed_document_set_enable_search_highlighting (doc, g_value_get_boolean (value));
break;
case PROP_NEWLINE_TYPE: case PROP_NEWLINE_TYPE:
xed_document_set_newline_type (doc, g_value_get_enum (value)); xed_document_set_newline_type (doc, g_value_get_enum (value));
break; break;
@ -498,22 +465,6 @@ xed_document_class_init (XedDocumentClass *klass)
G_PARAM_READABLE | G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS)); G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_CAN_SEARCH_AGAIN,
g_param_spec_boolean ("can-search-again",
"Can search again",
"Whether it's possible to search again in the document",
FALSE,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_ENABLE_SEARCH_HIGHLIGHTING,
g_param_spec_boolean ("enable-search-highlighting",
"Enable Search Highlighting",
"Whether all the occurrences of the searched string must be highlighted",
FALSE,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
/** /**
* XedDocument:newline-type: * XedDocument:newline-type:
* *
@ -643,18 +594,6 @@ xed_document_class_init (XedDocumentClass *klass)
1, 1,
G_TYPE_POINTER); G_TYPE_POINTER);
document_signals[SEARCH_HIGHLIGHT_UPDATED] =
g_signal_new ("search-highlight-updated",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (XedDocumentClass, search_highlight_updated),
NULL, NULL,
xed_marshal_VOID__BOXED_BOXED,
G_TYPE_NONE,
2,
GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE,
GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE);
g_type_class_add_private (object_class, sizeof (XedDocumentPrivate)); g_type_class_add_private (object_class, sizeof (XedDocumentPrivate));
} }
@ -925,20 +864,12 @@ xed_document_init (XedDocument *doc)
"highlight-matching-brackets", "highlight-matching-brackets",
G_SETTINGS_BIND_GET); G_SETTINGS_BIND_GET);
g_settings_bind (priv->editor_settings,
XED_SETTINGS_SEARCH_HIGHLIGHTING,
doc,
"enable-search-highlighting",
G_SETTINGS_BIND_GET);
style_scheme = get_default_style_scheme (priv->editor_settings); style_scheme = get_default_style_scheme (priv->editor_settings);
if (style_scheme != NULL) if (style_scheme != NULL)
{ {
gtk_source_buffer_set_style_scheme (GTK_SOURCE_BUFFER (doc), style_scheme); gtk_source_buffer_set_style_scheme (GTK_SOURCE_BUFFER (doc), style_scheme);
} }
g_signal_connect_after (doc, "insert-text", G_CALLBACK (insert_text_cb), NULL);
g_signal_connect_after (doc, "delete-range", G_CALLBACK (delete_range_cb), NULL);
g_signal_connect (doc, "notify::content-type", G_CALLBACK (on_content_type_changed), NULL); g_signal_connect (doc, "notify::content-type", G_CALLBACK (on_content_type_changed), NULL);
g_signal_connect (doc, "notify::location", G_CALLBACK (on_location_changed), NULL); g_signal_connect (doc, "notify::location", G_CALLBACK (on_location_changed), NULL);
} }
@ -1807,448 +1738,6 @@ xed_document_goto_line_offset (XedDocument *doc,
return ret; return ret;
} }
static gint
compute_num_of_lines (const gchar *text)
{
const gchar *p;
gint len;
gint n = 1;
g_return_val_if_fail (text != NULL, 0);
len = strlen (text);
p = text;
while (len > 0)
{
gint del, par;
pango_find_paragraph_boundary (p, len, &del, &par);
if (del == par) /* not found */
break;
p += par;
len -= par;
++n;
}
return n;
}
static void
to_search_region_range (XedDocument *doc,
GtkTextIter *start,
GtkTextIter *end)
{
xed_debug (DEBUG_DOCUMENT);
if (doc->priv->to_search_region == NULL)
{
return;
}
gtk_text_iter_set_line_offset (start, 0);
gtk_text_iter_forward_to_line_end (end);
/*
g_print ("+ [%u (%u), %u (%u)]\n", gtk_text_iter_get_line (start), gtk_text_iter_get_offset (start),
gtk_text_iter_get_line (end), gtk_text_iter_get_offset (end));
*/
/* Add the region to the refresh region */
xed_text_region_add (doc->priv->to_search_region, start, end);
/* Notify views of the updated highlight region */
gtk_text_iter_backward_lines (start, doc->priv->num_of_lines_search_text);
gtk_text_iter_forward_lines (end, doc->priv->num_of_lines_search_text);
g_signal_emit (doc, document_signals [SEARCH_HIGHLIGHT_UPDATED], 0, start, end);
}
/**
* xed_document_set_search_text:
* @doc:
* @text: (allow-none):
* @flags:
**/
void
xed_document_set_search_text (XedDocument *doc,
const gchar *text,
guint flags)
{
gchar *converted_text;
gboolean notify = FALSE;
gboolean update_to_search_region = FALSE;
g_return_if_fail (XED_IS_DOCUMENT (doc));
g_return_if_fail ((text == NULL) || (doc->priv->search_text != text));
g_return_if_fail ((text == NULL) || g_utf8_validate (text, -1, NULL));
xed_debug_message (DEBUG_DOCUMENT, "text = %s", text);
if (text != NULL)
{
if (*text != '\0')
{
converted_text = xed_utils_unescape_search_text (text);
notify = !xed_document_get_can_search_again (doc);
}
else
{
converted_text = g_strdup("");
notify = xed_document_get_can_search_again (doc);
}
g_free (doc->priv->search_text);
doc->priv->search_text = converted_text;
doc->priv->num_of_lines_search_text = compute_num_of_lines (doc->priv->search_text);
update_to_search_region = TRUE;
}
if (!XED_SEARCH_IS_DONT_SET_FLAGS (flags))
{
if (doc->priv->search_flags != flags)
{
update_to_search_region = TRUE;
}
doc->priv->search_flags = flags;
}
if (update_to_search_region)
{
GtkTextIter begin;
GtkTextIter end;
gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (doc), &begin, &end);
to_search_region_range (doc, &begin, &end);
}
if (notify)
{
g_object_notify (G_OBJECT (doc), "can-search-again");
}
}
/**
* xed_document_get_search_text:
* @doc:
* @flags: (allow-none):
*/
gchar *
xed_document_get_search_text (XedDocument *doc,
guint *flags)
{
g_return_val_if_fail (XED_IS_DOCUMENT (doc), NULL);
if (flags != NULL)
{
*flags = doc->priv->search_flags;
}
return xed_utils_escape_search_text (doc->priv->search_text);
}
gboolean
xed_document_get_can_search_again (XedDocument *doc)
{
g_return_val_if_fail (XED_IS_DOCUMENT (doc), FALSE);
return ((doc->priv->search_text != NULL) && (*doc->priv->search_text != '\0'));
}
/**
* xed_document_search_forward:
* @doc:
* @start: (allow-none):
* @end: (allow-none):
* @match_start: (allow-none):
* @match_end: (allow-none):
**/
gboolean
xed_document_search_forward (XedDocument *doc,
const GtkTextIter *start,
const GtkTextIter *end,
GtkTextIter *match_start,
GtkTextIter *match_end)
{
GtkTextIter iter;
GtkTextSearchFlags search_flags;
gboolean found = FALSE;
GtkTextIter m_start;
GtkTextIter m_end;
g_return_val_if_fail (XED_IS_DOCUMENT (doc), FALSE);
g_return_val_if_fail ((start == NULL) || (gtk_text_iter_get_buffer (start) == GTK_TEXT_BUFFER (doc)), FALSE);
g_return_val_if_fail ((end == NULL) || (gtk_text_iter_get_buffer (end) == GTK_TEXT_BUFFER (doc)), FALSE);
if (doc->priv->search_text == NULL)
{
xed_debug_message (DEBUG_DOCUMENT, "doc->priv->search_text == NULL\n");
return FALSE;
}
else
{
xed_debug_message (DEBUG_DOCUMENT, "doc->priv->search_text == \"%s\"\n", doc->priv->search_text);
}
if (start == NULL)
{
gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (doc), &iter);
}
else
{
iter = *start;
}
search_flags = GTK_TEXT_SEARCH_VISIBLE_ONLY | GTK_TEXT_SEARCH_TEXT_ONLY;
if (!XED_SEARCH_IS_CASE_SENSITIVE (doc->priv->search_flags))
{
search_flags = search_flags | GTK_TEXT_SEARCH_CASE_INSENSITIVE;
}
while (!found)
{
found = gtk_text_iter_forward_search (&iter,
doc->priv->search_text,
search_flags,
&m_start,
&m_end,
end);
if (found && XED_SEARCH_IS_ENTIRE_WORD (doc->priv->search_flags))
{
found = gtk_text_iter_starts_word (&m_start) && gtk_text_iter_ends_word (&m_end);
if (!found)
{
iter = m_end;
}
}
else
{
break;
}
}
if (found && (match_start != NULL))
{
*match_start = m_start;
}
if (found && (match_end != NULL))
{
*match_end = m_end;
}
return found;
}
/**
* xed_document_search_backward:
* @doc:
* @start: (allow-none):
* @end: (allow-none):
* @match_start: (allow-none):
* @match_end: (allow-none):
**/
gboolean
xed_document_search_backward (XedDocument *doc,
const GtkTextIter *start,
const GtkTextIter *end,
GtkTextIter *match_start,
GtkTextIter *match_end)
{
GtkTextIter iter;
GtkTextSearchFlags search_flags;
gboolean found = FALSE;
GtkTextIter m_start;
GtkTextIter m_end;
g_return_val_if_fail (XED_IS_DOCUMENT (doc), FALSE);
g_return_val_if_fail ((start == NULL) || (gtk_text_iter_get_buffer (start) == GTK_TEXT_BUFFER (doc)), FALSE);
g_return_val_if_fail ((end == NULL) || (gtk_text_iter_get_buffer (end) == GTK_TEXT_BUFFER (doc)), FALSE);
if (doc->priv->search_text == NULL)
{
xed_debug_message (DEBUG_DOCUMENT, "doc->priv->search_text == NULL\n");
return FALSE;
}
else
{
xed_debug_message (DEBUG_DOCUMENT, "doc->priv->search_text == \"%s\"\n", doc->priv->search_text);
}
if (end == NULL)
{
gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (doc), &iter);
}
else
{
iter = *end;
}
search_flags = GTK_TEXT_SEARCH_VISIBLE_ONLY | GTK_TEXT_SEARCH_TEXT_ONLY;
if (!XED_SEARCH_IS_CASE_SENSITIVE (doc->priv->search_flags))
{
search_flags = search_flags | GTK_TEXT_SEARCH_CASE_INSENSITIVE;
}
while (!found)
{
found = gtk_text_iter_backward_search (&iter,
doc->priv->search_text,
search_flags,
&m_start,
&m_end,
start);
if (found && XED_SEARCH_IS_ENTIRE_WORD (doc->priv->search_flags))
{
found = gtk_text_iter_starts_word (&m_start) && gtk_text_iter_ends_word (&m_end);
if (!found)
{
iter = m_start;
}
}
else
{
break;
}
}
if (found && (match_start != NULL))
{
*match_start = m_start;
}
if (found && (match_end != NULL))
{
*match_end = m_end;
}
return found;
}
/* FIXME this is an issue for introspection regardning @find */
gint
xed_document_replace_all (XedDocument *doc,
const gchar *find,
const gchar *replace,
guint flags)
{
GtkTextIter iter;
GtkTextIter m_start;
GtkTextIter m_end;
GtkTextSearchFlags search_flags = 0;
gboolean found = TRUE;
gint cont = 0;
gchar *search_text;
gchar *replace_text;
gint replace_text_len;
GtkTextBuffer *buffer;
gboolean brackets_highlighting;
gboolean search_highliting;
g_return_val_if_fail (XED_IS_DOCUMENT (doc), 0);
g_return_val_if_fail (replace != NULL, 0);
g_return_val_if_fail ((find != NULL) || (doc->priv->search_text != NULL), 0);
buffer = GTK_TEXT_BUFFER (doc);
if (find == NULL)
{
search_text = g_strdup (doc->priv->search_text);
}
else
{
search_text = xed_utils_unescape_search_text (find);
}
replace_text = xed_utils_unescape_search_text (replace);
gtk_text_buffer_get_start_iter (buffer, &iter);
search_flags = GTK_TEXT_SEARCH_VISIBLE_ONLY | GTK_TEXT_SEARCH_TEXT_ONLY;
if (!XED_SEARCH_IS_CASE_SENSITIVE (flags))
{
search_flags = search_flags | GTK_TEXT_SEARCH_CASE_INSENSITIVE;
}
replace_text_len = strlen (replace_text);
/* disable cursor_moved emission until the end of the
* replace_all so that we don't spend all the time
* updating the position in the statusbar
*/
doc->priv->stop_cursor_moved_emission = TRUE;
/* also avoid spending time matching brackets */
brackets_highlighting = gtk_source_buffer_get_highlight_matching_brackets (GTK_SOURCE_BUFFER (buffer));
gtk_source_buffer_set_highlight_matching_brackets (GTK_SOURCE_BUFFER (buffer), FALSE);
/* and do search highliting later */
search_highliting = xed_document_get_enable_search_highlighting (doc);
xed_document_set_enable_search_highlighting (doc, FALSE);
gtk_text_buffer_begin_user_action (buffer);
do
{
found = gtk_text_iter_forward_search (&iter,
search_text,
search_flags,
&m_start,
&m_end,
NULL);
if (found && XED_SEARCH_IS_ENTIRE_WORD (flags))
{
gboolean word;
word = gtk_text_iter_starts_word (&m_start) && gtk_text_iter_ends_word (&m_end);
if (!word)
{
iter = m_end;
continue;
}
}
if (found)
{
++cont;
gtk_text_buffer_delete (buffer, &m_start, &m_end);
gtk_text_buffer_insert (buffer, &m_start, replace_text, replace_text_len);
iter = m_start;
}
} while (found);
gtk_text_buffer_end_user_action (buffer);
/* re-enable cursor_moved emission and notify
* the current position
*/
doc->priv->stop_cursor_moved_emission = FALSE;
emit_cursor_moved (doc);
gtk_source_buffer_set_highlight_matching_brackets (GTK_SOURCE_BUFFER (buffer), brackets_highlighting);
xed_document_set_enable_search_highlighting (doc, search_highliting);
g_free (search_text);
g_free (replace_text);
return cont;
}
static void static void
get_style_colors (XedDocument *doc, get_style_colors (XedDocument *doc,
const gchar *style_name, const gchar *style_name,
@ -2393,14 +1882,6 @@ sync_tag_style (XedDocument *doc,
g_object_thaw_notify (G_OBJECT (tag)); g_object_thaw_notify (G_OBJECT (tag));
} }
static void
sync_found_tag (XedDocument *doc,
GParamSpec *pspec,
gpointer data)
{
sync_tag_style (doc, doc->priv->found_tag, "search-match");
}
static void static void
text_tag_set_highest_priority (GtkTextTag *tag, text_tag_set_highest_priority (GtkTextTag *tag,
GtkTextBuffer *buffer) GtkTextBuffer *buffer)
@ -2413,200 +1894,6 @@ text_tag_set_highest_priority (GtkTextTag *tag,
gtk_text_tag_set_priority (tag, n - 1); gtk_text_tag_set_priority (tag, n - 1);
} }
static void
search_region (XedDocument *doc,
GtkTextIter *start,
GtkTextIter *end)
{
GtkTextIter iter;
GtkTextIter m_start;
GtkTextIter m_end;
GtkTextSearchFlags search_flags = 0;
gboolean found = TRUE;
GtkTextBuffer *buffer;
xed_debug (DEBUG_DOCUMENT);
buffer = GTK_TEXT_BUFFER (doc);
if (doc->priv->found_tag == NULL)
{
doc->priv->found_tag = gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (doc), "found", NULL);
sync_found_tag (doc, NULL, NULL);
g_signal_connect (doc, "notify::style-scheme", G_CALLBACK (sync_found_tag), NULL);
}
/* make sure the 'found' tag has the priority over
* syntax highlighting tags */
text_tag_set_highest_priority (doc->priv->found_tag, GTK_TEXT_BUFFER (doc));
if (doc->priv->search_text == NULL)
{
return;
}
g_return_if_fail (doc->priv->num_of_lines_search_text > 0);
gtk_text_iter_backward_lines (start, doc->priv->num_of_lines_search_text);
gtk_text_iter_forward_lines (end, doc->priv->num_of_lines_search_text);
if (gtk_text_iter_has_tag (start, doc->priv->found_tag) &&
!gtk_text_iter_begins_tag (start, doc->priv->found_tag))
{
gtk_text_iter_backward_to_tag_toggle (start, doc->priv->found_tag);
}
if (gtk_text_iter_has_tag (end, doc->priv->found_tag) &&
!gtk_text_iter_ends_tag (end, doc->priv->found_tag))
{
gtk_text_iter_forward_to_tag_toggle (end, doc->priv->found_tag);
}
/*
g_print ("[%u (%u), %u (%u)]\n", gtk_text_iter_get_line (start), gtk_text_iter_get_offset (start),
gtk_text_iter_get_line (end), gtk_text_iter_get_offset (end));
*/
gtk_text_buffer_remove_tag (buffer, doc->priv->found_tag, start, end);
if (*doc->priv->search_text == '\0')
{
return;
}
iter = *start;
search_flags = GTK_TEXT_SEARCH_VISIBLE_ONLY | GTK_TEXT_SEARCH_TEXT_ONLY;
if (!XED_SEARCH_IS_CASE_SENSITIVE (doc->priv->search_flags))
{
search_flags = search_flags | GTK_TEXT_SEARCH_CASE_INSENSITIVE;
}
do
{
if ((end != NULL) && gtk_text_iter_is_end (end))
{
end = NULL;
}
found = gtk_text_iter_forward_search (&iter,
doc->priv->search_text,
search_flags,
&m_start,
&m_end,
end);
iter = m_end;
if (found && XED_SEARCH_IS_ENTIRE_WORD (doc->priv->search_flags))
{
gboolean word;
word = gtk_text_iter_starts_word (&m_start) && gtk_text_iter_ends_word (&m_end);
if (!word)
{
continue;
}
}
if (found)
{
gtk_text_buffer_apply_tag (buffer, doc->priv->found_tag, &m_start, &m_end);
}
} while (found);
}
void
_xed_document_search_region (XedDocument *doc,
const GtkTextIter *start,
const GtkTextIter *end)
{
XedTextRegion *region;
xed_debug (DEBUG_DOCUMENT);
g_return_if_fail (XED_IS_DOCUMENT (doc));
g_return_if_fail (start != NULL);
g_return_if_fail (end != NULL);
if (doc->priv->to_search_region == NULL)
{
return;
}
/*
g_print ("U [%u (%u), %u (%u)]\n", gtk_text_iter_get_line (start), gtk_text_iter_get_offset (start),
gtk_text_iter_get_line (end), gtk_text_iter_get_offset (end));
*/
/* get the subregions not yet highlighted */
region = xed_text_region_intersect (doc->priv->to_search_region, start, end);
if (region)
{
gint i;
GtkTextIter start_search;
GtkTextIter end_search;
i = xed_text_region_subregions (region);
xed_text_region_nth_subregion (region, 0, &start_search, NULL);
xed_text_region_nth_subregion (region, i - 1, NULL, &end_search);
xed_text_region_destroy (region, TRUE);
gtk_text_iter_order (&start_search, &end_search);
search_region (doc, &start_search, &end_search);
/* remove the just highlighted region */
xed_text_region_subtract (doc->priv->to_search_region, start, end);
}
}
static void
insert_text_cb (XedDocument *doc,
GtkTextIter *pos,
const gchar *text,
gint length)
{
GtkTextIter start;
GtkTextIter end;
xed_debug (DEBUG_DOCUMENT);
start = end = *pos;
/*
* pos is invalidated when
* insertion occurs (because the buffer contents change), but the
* default signal handler revalidates it to point to the end of the
* inserted text
*/
gtk_text_iter_backward_chars (&start, g_utf8_strlen (text, length));
to_search_region_range (doc, &start, &end);
}
static void
delete_range_cb (XedDocument *doc,
GtkTextIter *start,
GtkTextIter *end)
{
GtkTextIter d_start;
GtkTextIter d_end;
xed_debug (DEBUG_DOCUMENT);
d_start = *start;
d_end = *end;
to_search_region_range (doc, &d_start, &d_end);
}
/** /**
* xed_document_set_language: * xed_document_set_language:
* @doc: * @doc:
@ -2657,58 +1944,6 @@ _xed_document_get_seconds_since_last_save_or_load (XedDocument *doc)
return (current_time.tv_sec - doc->priv->time_of_last_save_or_load.tv_sec); return (current_time.tv_sec - doc->priv->time_of_last_save_or_load.tv_sec);
} }
void
xed_document_set_enable_search_highlighting (XedDocument *doc,
gboolean enable)
{
g_return_if_fail (XED_IS_DOCUMENT (doc));
enable = enable != FALSE;
if ((doc->priv->to_search_region != NULL) == enable)
{
return;
}
if (doc->priv->to_search_region != NULL)
{
/* Disable search highlighting */
if (doc->priv->found_tag != NULL)
{
/* If needed remove the found_tag */
GtkTextIter begin;
GtkTextIter end;
gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (doc), &begin, &end);
gtk_text_buffer_remove_tag (GTK_TEXT_BUFFER (doc), doc->priv->found_tag, &begin, &end);
}
xed_text_region_destroy (doc->priv->to_search_region, TRUE);
doc->priv->to_search_region = NULL;
}
else
{
doc->priv->to_search_region = xed_text_region_new (GTK_TEXT_BUFFER (doc));
if (xed_document_get_can_search_again (doc))
{
/* If search_text is not empty, highligth all its occurrences */
GtkTextIter begin;
GtkTextIter end;
gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (doc), &begin, &end);
to_search_region_range (doc, &begin, &end);
}
}
}
gboolean
xed_document_get_enable_search_highlighting (XedDocument *doc)
{
g_return_val_if_fail (XED_IS_DOCUMENT (doc), FALSE);
return (doc->priv->to_search_region != NULL);
}
void void
xed_document_set_newline_type (XedDocument *doc, xed_document_set_newline_type (XedDocument *doc,
XedDocumentNewlineType newline_type) XedDocumentNewlineType newline_type)
@ -2946,3 +2181,30 @@ _xed_document_apply_error_style (XedDocument *doc,
gtk_text_buffer_apply_tag (buffer, doc->priv->error_tag, start, end); gtk_text_buffer_apply_tag (buffer, doc->priv->error_tag, start, end);
} }
void
_xed_document_set_search_context (XedDocument *doc,
GtkSourceSearchContext *search_context)
{
g_return_if_fail (XED_IS_DOCUMENT (doc));
g_clear_object (&doc->priv->search_context);
doc->priv->search_context = search_context;
if (search_context != NULL)
{
gboolean highlight = g_settings_get_boolean (doc->priv->editor_settings, XED_SETTINGS_SEARCH_HIGHLIGHTING);
gtk_source_search_context_set_highlight (search_context, highlight);
g_object_ref (search_context);
}
}
GtkSourceSearchContext *
_xed_document_get_search_context (XedDocument *doc)
{
g_return_val_if_fail (XED_IS_DOCUMENT (doc), NULL);
return doc->priv->search_context;
}

View File

@ -64,15 +64,6 @@ typedef enum
#define XED_DOCUMENT_NEWLINE_TYPE_DEFAULT XED_DOCUMENT_NEWLINE_TYPE_LF #define XED_DOCUMENT_NEWLINE_TYPE_DEFAULT XED_DOCUMENT_NEWLINE_TYPE_LF
typedef enum
{
XED_SEARCH_DONT_SET_FLAGS = 1 << 0,
XED_SEARCH_ENTIRE_WORD = 1 << 1,
XED_SEARCH_CASE_SENSITIVE = 1 << 2,
XED_SEARCH_PARSE_ESCAPES = 1 << 3
} XedSearchFlags;
/** /**
* XedDocumentSaveFlags: * XedDocumentSaveFlags:
* @XED_DOCUMENT_SAVE_IGNORE_MTIME: save file despite external modifications. * @XED_DOCUMENT_SAVE_IGNORE_MTIME: save file despite external modifications.
@ -142,11 +133,6 @@ struct _XedDocumentClass
void (* saved) (XedDocument *document, void (* saved) (XedDocument *document,
const GError *error); const GError *error);
void (* search_highlight_updated)
(XedDocument *document,
GtkTextIter *start,
GtkTextIter *end);
}; };
@ -223,33 +209,6 @@ gboolean xed_document_goto_line_offset(XedDocument *doc,
gint line, gint line,
gint line_offset); gint line_offset);
void xed_document_set_search_text (XedDocument *doc,
const gchar *text,
guint flags);
gchar *xed_document_get_search_text (XedDocument *doc,
guint *flags);
gboolean xed_document_get_can_search_again
(XedDocument *doc);
gboolean xed_document_search_forward (XedDocument *doc,
const GtkTextIter *start,
const GtkTextIter *end,
GtkTextIter *match_start,
GtkTextIter *match_end);
gboolean xed_document_search_backward (XedDocument *doc,
const GtkTextIter *start,
const GtkTextIter *end,
GtkTextIter *match_start,
GtkTextIter *match_end);
gint xed_document_replace_all (XedDocument *doc,
const gchar *find,
const gchar *replace,
guint flags);
void xed_document_set_language (XedDocument *doc, void xed_document_set_language (XedDocument *doc,
GtkSourceLanguage *lang); GtkSourceLanguage *lang);
GtkSourceLanguage GtkSourceLanguage
@ -258,13 +217,6 @@ GtkSourceLanguage
const XedEncoding const XedEncoding
*xed_document_get_encoding (XedDocument *doc); *xed_document_get_encoding (XedDocument *doc);
void xed_document_set_enable_search_highlighting
(XedDocument *doc,
gboolean enable);
gboolean xed_document_get_enable_search_highlighting
(XedDocument *doc);
void xed_document_set_newline_type (XedDocument *doc, void xed_document_set_newline_type (XedDocument *doc,
XedDocumentNewlineType newline_type); XedDocumentNewlineType newline_type);
@ -299,27 +251,6 @@ void _xed_document_apply_error_style (XedDocument *doc,
gboolean _xed_document_check_externally_modified gboolean _xed_document_check_externally_modified
(XedDocument *doc); (XedDocument *doc);
void _xed_document_search_region (XedDocument *doc,
const GtkTextIter *start,
const GtkTextIter *end);
/* Search macros */
#define XED_SEARCH_IS_DONT_SET_FLAGS(sflags) ((sflags & XED_SEARCH_DONT_SET_FLAGS) != 0)
#define XED_SEARCH_SET_DONT_SET_FLAGS(sflags,state) ((state == TRUE) ? \
(sflags |= XED_SEARCH_DONT_SET_FLAGS) : (sflags &= ~XED_SEARCH_DONT_SET_FLAGS))
#define XED_SEARCH_IS_ENTIRE_WORD(sflags) ((sflags & XED_SEARCH_ENTIRE_WORD) != 0)
#define XED_SEARCH_SET_ENTIRE_WORD(sflags,state) ((state == TRUE) ? \
(sflags |= XED_SEARCH_ENTIRE_WORD) : (sflags &= ~XED_SEARCH_ENTIRE_WORD))
#define XED_SEARCH_IS_CASE_SENSITIVE(sflags) ((sflags & XED_SEARCH_CASE_SENSITIVE) != 0)
#define XED_SEARCH_SET_CASE_SENSITIVE(sflags,state) ((state == TRUE) ? \
(sflags |= XED_SEARCH_CASE_SENSITIVE) : (sflags &= ~XED_SEARCH_CASE_SENSITIVE))
#define XED_SEARCH_IS_PARSE_ESCAPES(sflags) ((sflags & XED_SEARCH_PARSE_ESCAPES) != 0)
#define XED_SEARCH_SET_PARSE_ESCAPES(sflags,state) ((state == TRUE) ? \
(sflags |= XED_SEARCH_PARSE_ESCAPES) : (sflags &= ~XED_SEARCH_PARSE_ESCAPES))
typedef GMountOperation *(*XedMountOperationFactory)(XedDocument *doc, typedef GMountOperation *(*XedMountOperationFactory)(XedDocument *doc,
gpointer userdata); gpointer userdata);
@ -331,6 +262,11 @@ GMountOperation
*_xed_document_create_mount_operation *_xed_document_create_mount_operation
(XedDocument *doc); (XedDocument *doc);
void _xed_document_set_search_context (XedDocument *doc,
GtkSourceSearchContext *search_context);
GtkSourceSearchContext *_xed_document_get_search_context (XedDocument *doc);
G_END_DECLS G_END_DECLS
#endif /* __XED_DOCUMENT_H__ */ #endif /* __XED_DOCUMENT_H__ */

View File

@ -516,54 +516,3 @@ xed_history_entry_get_entry (XedHistoryEntry *entry)
return gtk_bin_get_child (GTK_BIN (entry)); return gtk_bin_get_child (GTK_BIN (entry));
} }
static void
escape_cell_data_func (GtkTreeViewColumn *col,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
XedHistoryEntryEscapeFunc escape_func)
{
gchar *str;
gchar *escaped;
gtk_tree_model_get (model, iter, 0, &str, -1);
escaped = escape_func (str);
g_object_set (renderer, "text", escaped, NULL);
g_free (str);
g_free (escaped);
}
void
xed_history_entry_set_escape_func (XedHistoryEntry *entry,
XedHistoryEntryEscapeFunc escape_func)
{
GList *cells;
g_return_if_fail (XED_IS_HISTORY_ENTRY (entry));
cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (entry));
/* We only have one cell renderer */
g_return_if_fail (cells->data != NULL && cells->next == NULL);
if (escape_func != NULL)
{
gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (entry),
GTK_CELL_RENDERER (cells->data),
(GtkCellLayoutDataFunc) escape_cell_data_func,
escape_func,
NULL);
}
else
{
gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (entry),
GTK_CELL_RENDERER (cells->data),
NULL,
NULL,
NULL);
}
g_list_free (cells);
}

View File

@ -80,10 +80,6 @@ gboolean xed_history_entry_get_enable_completion (XedHistoryEntry *entry);
GtkWidget *xed_history_entry_get_entry (XedHistoryEntry *entry); GtkWidget *xed_history_entry_get_entry (XedHistoryEntry *entry);
typedef gchar * (* XedHistoryEntryEscapeFunc) (const gchar *str);
void xed_history_entry_set_escape_func (XedHistoryEntry *entry,
XedHistoryEntryEscapeFunc escape_func);
G_END_DECLS G_END_DECLS
#endif /* __XED_HISTORY_ENTRY_H__ */ #endif /* __XED_HISTORY_ENTRY_H__ */

View File

@ -18,7 +18,13 @@
#define XED_SEARCHBAR_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), XED_TYPE_SEARCHBAR, XedSearchbarPrivate)) #define XED_SEARCHBAR_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), XED_TYPE_SEARCHBAR, XedSearchbarPrivate))
/* Signals */ // typedef enum
// {
// SEARCH,
// REPLACE
// } SearchMode;
enum enum
{ {
SHOW_REPLACE, SHOW_REPLACE,
@ -37,6 +43,7 @@ struct _XedSearchbarPrivate
GtkWidget *replace_label; GtkWidget *replace_label;
GtkWidget *replace_entry; GtkWidget *replace_entry;
GtkWidget *replace_text_entry; GtkWidget *replace_text_entry;
GtkWidget *regex_checkbutton;
GtkWidget *match_case_checkbutton; GtkWidget *match_case_checkbutton;
GtkWidget *entire_word_checkbutton; GtkWidget *entire_word_checkbutton;
GtkWidget *wrap_around_checkbutton; GtkWidget *wrap_around_checkbutton;
@ -45,15 +52,38 @@ struct _XedSearchbarPrivate
GtkWidget *replace_button; GtkWidget *replace_button;
GtkWidget *replace_all_button; GtkWidget *replace_all_button;
GtkWidget *close_button; GtkWidget *close_button;
GtkSourceSearchSettings *search_settings;
SearchMode search_mode;
guint update_occurrence_count_id;
}; };
G_DEFINE_TYPE(XedSearchbar, xed_searchbar, GTK_TYPE_BOX) G_DEFINE_TYPE(XedSearchbar, xed_searchbar, GTK_TYPE_BOX)
static void
xed_searchbar_dispose (GObject *object)
{
XedSearchbar *searchbar = XED_SEARCHBAR (object);
if (searchbar->priv->update_occurrence_count_id != 0)
{
g_source_remove (searchbar->priv->update_occurrence_count_id);
searchbar->priv->update_occurrence_count_id = 0;
}
g_clear_object (&searchbar->priv->search_settings);
G_OBJECT_CLASS (xed_searchbar_parent_class)->dispose (object);
}
static void static void
xed_searchbar_class_init (XedSearchbarClass *klass) xed_searchbar_class_init (XedSearchbarClass *klass)
{ {
GObjectClass *object_class = G_OBJECT_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = xed_searchbar_dispose;
g_type_class_add_private (object_class, sizeof(XedSearchbarPrivate)); g_type_class_add_private (object_class, sizeof(XedSearchbarPrivate));
} }
@ -91,130 +121,285 @@ text_found (XedWindow *window,
} }
static void static void
text_not_found (XedWindow *window, text_not_found (XedSearchbar *searchbar)
const gchar *text)
{ {
gchar *searched; const gchar *search_text;
gchar *truncated_text;
searched = xed_utils_str_end_truncate (text, MAX_MSG_LENGTH); search_text = xed_searchbar_get_search_text (searchbar);
xed_statusbar_flash_message (XED_STATUSBAR (window->priv->statusbar), truncated_text = xed_utils_str_end_truncate (search_text, MAX_MSG_LENGTH);
window->priv->generic_message_cid,
_("\"%s\" not found"), searched); xed_statusbar_flash_message (XED_STATUSBAR (searchbar->window->priv->statusbar),
g_free (searched); searchbar->window->priv->generic_message_cid,
_("\"%s\" not found"), truncated_text);
g_free (truncated_text);
} }
static gboolean static gboolean
run_search (XedView *view, forward_search_finished (GtkSourceSearchContext *search_context,
gboolean wrap_around, GAsyncResult *result,
gboolean search_backwards, XedView *view)
gboolean jump_to_next_result)
{ {
XedDocument *doc; gboolean found;
GtkTextIter start_iter; GtkSourceBuffer *buffer;
GtkTextIter end_iter;
GtkTextIter match_start; GtkTextIter match_start;
GtkTextIter match_end; GtkTextIter match_end;
gboolean found = FALSE;
doc = XED_DOCUMENT(gtk_text_view_get_buffer (GTK_TEXT_VIEW (view))); found = gtk_source_search_context_forward_finish (search_context, result, &match_start, &match_end, NULL);
gtk_text_buffer_get_selection_bounds (GTK_TEXT_BUFFER (doc), &start_iter, &end_iter); buffer = gtk_source_search_context_get_buffer (search_context);
if (!search_backwards)
{
if (jump_to_next_result)
{
found = xed_document_search_forward (doc, &end_iter, NULL, &match_start, &match_end);
}
else
{
found = xed_document_search_forward (doc, &start_iter, NULL, &match_start, &match_end);
}
}
else
{
found = xed_document_search_backward (doc, NULL, &start_iter, &match_start, &match_end);
}
if (!found && wrap_around)
{
if (!search_backwards)
{
/* FIXME: set the end_inter */
found = xed_document_search_forward (doc, NULL, NULL, &match_start, &match_end);
}
else
{
/* FIXME: set the start_inter */
found = xed_document_search_backward (doc, NULL, NULL, &match_start, &match_end);
}
}
if (found) if (found)
{ {
gtk_text_buffer_place_cursor (GTK_TEXT_BUFFER (doc), &match_start); gtk_text_buffer_select_range (GTK_TEXT_BUFFER (buffer), &match_start, &match_end);
gtk_text_buffer_move_mark_by_name (GTK_TEXT_BUFFER (doc), "selection_bound", &match_end);
xed_view_scroll_to_cursor (view); xed_view_scroll_to_cursor (view);
} }
else else
{ {
gtk_text_buffer_place_cursor (GTK_TEXT_BUFFER (doc), &start_iter); GtkTextIter end_selection;
gtk_text_buffer_get_selection_bounds (GTK_TEXT_BUFFER (buffer), NULL, &end_selection);
gtk_text_buffer_select_range (GTK_TEXT_BUFFER (buffer), &end_selection, &end_selection);
} }
return found; return found;
} }
static void
run_forward_search (XedWindow *window,
gboolean jump_to_next_result)
{
XedView *view;
GtkTextBuffer *buffer;
GtkTextIter start_at;
GtkTextIter end_at;
GtkSourceSearchContext *search_context;
view = xed_window_get_active_view (window);
if (view == NULL)
{
return;
}
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
search_context = _xed_document_get_search_context (XED_DOCUMENT (buffer));
if (search_context == NULL)
{
return;
}
gtk_text_buffer_get_selection_bounds (buffer, &start_at, &end_at);
if (jump_to_next_result)
{
gtk_source_search_context_forward_async (search_context,
&end_at,
NULL,
(GAsyncReadyCallback)forward_search_finished,
view);
}
else
{
gtk_source_search_context_forward_async (search_context,
&start_at,
NULL,
(GAsyncReadyCallback)forward_search_finished,
view);
}
}
static gboolean
backward_search_finished (GtkSourceSearchContext *search_context,
GAsyncResult *result,
XedView *view)
{
gboolean found;
GtkTextIter match_start;
GtkTextIter match_end;
GtkSourceBuffer *buffer;
found = gtk_source_search_context_backward_finish (search_context, result, &match_start, &match_end, NULL);
buffer = gtk_source_search_context_get_buffer (search_context);
if (found)
{
gtk_text_buffer_select_range (GTK_TEXT_BUFFER (buffer), &match_start, &match_end);
xed_view_scroll_to_cursor (view);
}
else
{
GtkTextIter start_selection;
gtk_text_buffer_get_selection_bounds (GTK_TEXT_BUFFER (buffer), &start_selection, NULL);
gtk_text_buffer_select_range (GTK_TEXT_BUFFER (buffer), &start_selection, &start_selection);
}
return found;
}
static void
run_backward_search (XedWindow *window)
{
XedView *view;
GtkTextBuffer *buffer;
GtkTextIter start_at;
GtkSourceSearchContext *search_context;
view = xed_window_get_active_view (window);
if (view == NULL)
{
return;
}
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
search_context = _xed_document_get_search_context (XED_DOCUMENT (buffer));
if (search_context == NULL)
{
return;
}
gtk_text_buffer_get_selection_bounds (buffer, &start_at, NULL);
gtk_source_search_context_backward_async (search_context,
&start_at,
NULL,
(GAsyncReadyCallback)backward_search_finished,
view);
}
static void
update_occurrence_count (XedSearchbar *searchbar)
{
XedDocument *doc;
GtkSourceSearchContext *search_context;
GtkTextIter match_start;
GtkTextIter match_end;
gint count;
gint pos;
if (searchbar->priv->search_mode == SEARCH_MODE_REPLACE)
{
return;
}
searchbar->priv->update_occurrence_count_id = 0;
doc = xed_window_get_active_document (searchbar->window);
search_context = _xed_document_get_search_context (doc);
if (search_context == NULL)
{
return;
}
count = gtk_source_search_context_get_occurrences_count (search_context);
gtk_text_buffer_get_selection_bounds (GTK_TEXT_BUFFER (doc), &match_start, &match_end);
pos = gtk_source_search_context_get_occurrence_position (search_context, &match_start, &match_end);
if (count == -1 || pos == -1)
{
/* Wait for the buffer to be fully scanned */
return;
}
if (count == 0)
{
xed_statusbar_flash_message (XED_STATUSBAR (searchbar->window->priv->statusbar),
searchbar->window->priv->generic_message_cid,
_("No matches found"));
return;
}
if (pos == 0)
{
xed_statusbar_flash_message (XED_STATUSBAR (searchbar->window->priv->statusbar),
searchbar->window->priv->generic_message_cid,
ngettext ("%d match", "%d matches", count), count);
return;
}
xed_statusbar_flash_message (XED_STATUSBAR (searchbar->window->priv->statusbar),
searchbar->window->priv->generic_message_cid,
ngettext ("%d of %d match", "%d of %d matches",
pos),
pos, count);
}
static gboolean
update_occurrence_count_id_cb (XedSearchbar *searchbar)
{
searchbar->priv->update_occurrence_count_id = 0;
update_occurrence_count (searchbar);
return G_SOURCE_REMOVE;
}
static void
install_occurrence_count_idle (XedSearchbar *searchbar)
{
if (searchbar->priv->update_occurrence_count_id == 0)
{
searchbar->priv->update_occurrence_count_id = g_idle_add ((GSourceFunc)update_occurrence_count_id_cb, searchbar);
}
}
static void
mark_set_cb (GtkTextBuffer *buffer,
GtkTextIter *location,
GtkTextMark *mark,
XedSearchbar *searchbar)
{
GtkTextMark *insert;
GtkTextMark *selection_bound;
insert = gtk_text_buffer_get_insert (buffer);
selection_bound = gtk_text_buffer_get_selection_bound (buffer);
if (mark == insert || mark == selection_bound)
{
install_occurrence_count_idle (searchbar);
}
}
static void static void
do_find (XedSearchbar *searchbar, do_find (XedSearchbar *searchbar,
gboolean search_backwards, gboolean search_backwards,
gboolean jump_to_next_result) gboolean jump_to_next_result)
{ {
XedView *active_view;
XedDocument *doc; XedDocument *doc;
gchar *search_text; GtkSourceSearchContext *search_context;
const gchar *entry_text; GtkSourceSearchSettings *search_settings;
gboolean match_case;
gboolean entire_word;
gboolean wrap_around;
guint flags = 0;
guint old_flags = 0;
gboolean found;
/* TODO: make the searchbar insensitive when all the tabs are closed search_settings = xed_searchbar_get_search_settings (searchbar);
* and assert here that the view is not NULL */ doc = xed_window_get_active_document (searchbar->window);
active_view = xed_window_get_active_view (searchbar->window); search_context = _xed_document_get_search_context (doc);
if (active_view == NULL) searchbar->priv->search_mode = SEARCH_MODE_SEARCH;
if (search_context == NULL || search_settings != gtk_source_search_context_get_settings (search_context))
{ {
return; search_context = gtk_source_search_context_new (GTK_SOURCE_BUFFER (doc), search_settings);
_xed_document_set_search_context (doc, search_context);
g_signal_connect (GTK_TEXT_BUFFER (doc), "mark-set",
G_CALLBACK (mark_set_cb), searchbar);
g_signal_connect_swapped (search_context, "notify::occurrences-count",
G_CALLBACK (install_occurrence_count_idle), searchbar);
g_object_unref (search_context);
} }
doc = XED_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (active_view))); if (search_backwards)
match_case = xed_searchbar_get_match_case (searchbar);
entire_word = xed_searchbar_get_entire_word (searchbar);
wrap_around = xed_searchbar_get_wrap_around (searchbar);
entry_text = xed_searchbar_get_search_text (searchbar);
XED_SEARCH_SET_CASE_SENSITIVE (flags, match_case);
XED_SEARCH_SET_ENTIRE_WORD (flags, entire_word);
search_text = xed_document_get_search_text (doc, &old_flags);
if ((search_text == NULL) || (strcmp (search_text, entry_text) != 0) || (flags != old_flags))
{ {
xed_document_set_search_text (doc, entry_text, flags); run_backward_search (searchbar->window);
}
g_free (search_text);
found = run_search (active_view, wrap_around, search_backwards, jump_to_next_result);
if (found)
{
text_found (searchbar->window, 0);
} }
else else
{ {
text_not_found (searchbar->window, entry_text); run_forward_search (searchbar->window, jump_to_next_result);
} }
} }
@ -222,21 +407,14 @@ void
xed_searchbar_find_again (XedSearchbar *searchbar, xed_searchbar_find_again (XedSearchbar *searchbar,
gboolean backward) gboolean backward)
{ {
XedView *active_view; if (backward)
gboolean wrap_around = TRUE;
gpointer data;
active_view = xed_window_get_active_view (searchbar->window);
g_return_if_fail (active_view != NULL);
data = g_object_get_data (G_OBJECT (searchbar->window), XED_SEARCHBAR_KEY);
if (data != NULL)
{ {
wrap_around = xed_searchbar_get_wrap_around (XED_SEARCHBAR (data)); do_find (searchbar, TRUE, TRUE);
}
else
{
do_find (searchbar, FALSE, TRUE);
} }
run_search (active_view, wrap_around, backward, TRUE);
} }
static void static void
@ -279,69 +457,45 @@ get_selected_text (GtkTextBuffer *doc,
return TRUE; return TRUE;
} }
static void
replace_selected_text (GtkTextBuffer *buffer,
const gchar *replace)
{
g_return_if_fail (gtk_text_buffer_get_selection_bounds (buffer, NULL, NULL));
g_return_if_fail (replace != NULL);
gtk_text_buffer_begin_user_action (buffer);
gtk_text_buffer_delete_selection (buffer, FALSE, TRUE);
gtk_text_buffer_insert_at_cursor (buffer, replace, strlen (replace));
gtk_text_buffer_end_user_action (buffer);
}
static void static void
do_replace (XedSearchbar *searchbar) do_replace (XedSearchbar *searchbar)
{ {
XedDocument *doc; XedDocument *doc;
const gchar *search_entry_text; GtkSourceSearchContext *search_context;
const gchar *replace_entry_text; const gchar *replace_entry_text;
gchar *unescaped_search_text;
gchar *unescaped_replace_text; gchar *unescaped_replace_text;
gchar *selected_text = NULL; GtkTextIter start;
gboolean match_case; GtkTextIter end;
doc = xed_window_get_active_document (searchbar->window); doc = xed_window_get_active_document (searchbar->window);
if (doc == NULL) if (doc == NULL)
{ {
return; return;
} }
search_entry_text = xed_searchbar_get_search_text (searchbar); search_context = _xed_document_get_search_context (doc);
g_return_if_fail ((search_entry_text) != NULL);
g_return_if_fail ((*search_entry_text) != '\0'); if (search_context == NULL)
{
return;
}
/* replace text may be "", we just delete */ /* replace text may be "", we just delete */
replace_entry_text = xed_searchbar_get_replace_text (searchbar); replace_entry_text = xed_searchbar_get_replace_text (searchbar);
g_return_if_fail ((replace_entry_text) != NULL); g_return_if_fail ((replace_entry_text) != NULL);
unescaped_search_text = xed_utils_unescape_search_text (search_entry_text); unescaped_replace_text = gtk_source_utils_unescape_search_text (replace_entry_text);
gtk_text_buffer_get_selection_bounds (GTK_TEXT_BUFFER (doc), &start, &end);
searchbar->priv->search_mode = SEARCH_MODE_REPLACE;
get_selected_text (GTK_TEXT_BUFFER (doc), &selected_text, NULL); gtk_source_search_context_replace (search_context,
&start,
&end,
unescaped_replace_text,
-1,
NULL);
match_case = xed_searchbar_get_match_case (searchbar);
if ((selected_text == NULL) ||
(match_case && (strcmp (selected_text, unescaped_search_text) != 0)) ||
(!match_case && !g_utf8_caselessnmatch (selected_text,
unescaped_search_text,
strlen (selected_text),
strlen (unescaped_search_text)) != 0))
{
do_find (searchbar, FALSE, TRUE );
g_free (unescaped_search_text);
g_free (selected_text);
return;
}
unescaped_replace_text = xed_utils_unescape_search_text (replace_entry_text);
replace_selected_text (GTK_TEXT_BUFFER (doc), unescaped_replace_text);
g_free (unescaped_search_text);
g_free (selected_text);
g_free (unescaped_replace_text); g_free (unescaped_replace_text);
do_find (searchbar, FALSE, TRUE); do_find (searchbar, FALSE, TRUE);
@ -350,38 +504,35 @@ do_replace (XedSearchbar *searchbar)
static void static void
do_replace_all (XedSearchbar *searchbar) do_replace_all (XedSearchbar *searchbar)
{ {
XedView *active_view;
XedDocument *doc; XedDocument *doc;
const gchar *search_entry_text; GtkSourceSearchContext *search_context;
const gchar *replace_entry_text; const gchar *replace_entry_text;
gboolean match_case; gchar *unescaped_replace_text;
gboolean entire_word;
guint flags = 0;
gint count; gint count;
active_view = xed_window_get_active_view (searchbar->window); doc = xed_window_get_active_document (searchbar->window);
if (active_view == NULL)
if (doc == NULL)
{ {
return; return;
} }
doc = XED_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (active_view))); search_context = _xed_document_get_search_context (doc);
search_entry_text = xed_searchbar_get_search_text (searchbar); if (search_context == NULL)
g_return_if_fail ((search_entry_text) != NULL); {
g_return_if_fail ((*search_entry_text) != '\0'); return;
}
/* replace text may be "", we just delete all occurrences */ /* replace text may be "", we just delete all occurrences */
replace_entry_text = xed_searchbar_get_replace_text (searchbar); replace_entry_text = xed_searchbar_get_replace_text (searchbar);
g_return_if_fail ((replace_entry_text) != NULL); g_return_if_fail ((replace_entry_text) != NULL);
match_case = xed_searchbar_get_match_case (searchbar); unescaped_replace_text = gtk_source_utils_unescape_search_text (replace_entry_text);
entire_word = xed_searchbar_get_entire_word (searchbar); count = gtk_source_search_context_replace_all (search_context, unescaped_replace_text, -1, NULL);
searchbar->priv->search_mode = SEARCH_MODE_REPLACE;
XED_SEARCH_SET_CASE_SENSITIVE (flags, match_case); g_free (unescaped_replace_text);
XED_SEARCH_SET_ENTIRE_WORD (flags, entire_word);
count = xed_document_replace_all (doc, search_entry_text, replace_entry_text, flags);
if (count > 0) if (count > 0)
{ {
@ -389,49 +540,11 @@ do_replace_all (XedSearchbar *searchbar)
} }
else else
{ {
text_not_found (searchbar->window, search_entry_text); text_not_found (searchbar);
} }
} }
static void
insert_text_handler (GtkEditable *editable,
const gchar *text,
gint length,
gint *position,
gpointer data)
{
static gboolean insert_text = FALSE;
gchar *escaped_text;
gint new_len;
/* To avoid recursive behavior */
if (insert_text)
{
return;
}
escaped_text = xed_utils_escape_search_text (text);
new_len = strlen (escaped_text);
if (new_len == length)
{
g_free (escaped_text);
return;
}
insert_text = TRUE;
g_signal_stop_emission_by_name (editable, "insert_text");
gtk_editable_insert_text (editable, escaped_text, new_len, position);
insert_text = FALSE;
g_free (escaped_text);
}
static void static void
search_text_entry_changed (GtkEditable *editable, search_text_entry_changed (GtkEditable *editable,
XedSearchbar *searchbar) XedSearchbar *searchbar)
@ -444,26 +557,38 @@ search_text_entry_changed (GtkEditable *editable,
if (*search_string != '\0') if (*search_string != '\0')
{ {
search_buttons_set_sensitive (searchbar, TRUE); search_buttons_set_sensitive (searchbar, TRUE);
do_find (searchbar, FALSE, FALSE);
} }
else else
{ {
search_buttons_set_sensitive (searchbar, FALSE); search_buttons_set_sensitive (searchbar, FALSE);
do_find (searchbar, FALSE, FALSE);
} }
if (gtk_source_search_settings_get_regex_enabled (searchbar->priv->search_settings))
{
gtk_source_search_settings_set_search_text (searchbar->priv->search_settings, search_string);
}
else
{
gchar *unescaped_search_string;
unescaped_search_string = gtk_source_utils_unescape_search_text (search_string);
gtk_source_search_settings_set_search_text (searchbar->priv->search_settings, unescaped_search_string);
g_free (unescaped_search_string);
}
do_find (searchbar, FALSE, FALSE);
} }
static void static void
remember_search_entry (XedSearchbar *searchbar) remember_search_entry (XedSearchbar *searchbar)
{ {
const gchar *str; const gchar *str;
str = gtk_entry_get_text (GTK_ENTRY(searchbar->priv->search_text_entry)); str = gtk_entry_get_text (GTK_ENTRY(searchbar->priv->search_text_entry));
if (*str != '\0') if (*str != '\0')
{ {
gchar *text; xed_history_entry_prepend_text (XED_HISTORY_ENTRY (searchbar->priv->search_entry), str);
text = xed_utils_unescape_search_text (str);
xed_history_entry_prepend_text (XED_HISTORY_ENTRY(searchbar->priv->search_entry), text);
g_free (text);
} }
} }
@ -471,13 +596,11 @@ static void
remember_replace_entry (XedSearchbar *searchbar) remember_replace_entry (XedSearchbar *searchbar)
{ {
const gchar *str; const gchar *str;
str = gtk_entry_get_text (GTK_ENTRY(searchbar->priv->replace_text_entry)); str = gtk_entry_get_text (GTK_ENTRY(searchbar->priv->replace_text_entry));
if (*str != '\0') if (*str != '\0')
{ {
gchar *text; xed_history_entry_prepend_text (XED_HISTORY_ENTRY(searchbar->priv->replace_entry), str);
text = xed_utils_unescape_search_text (str);
xed_history_entry_prepend_text (XED_HISTORY_ENTRY(searchbar->priv->replace_entry), text);
g_free (text);
} }
} }
@ -561,6 +684,7 @@ xed_searchbar_init (XedSearchbar *searchbar)
"grid", &searchbar->priv->grid, "grid", &searchbar->priv->grid,
"search_label", &searchbar->priv->search_label, "search_label", &searchbar->priv->search_label,
"replace_with_label", &searchbar->priv->replace_label, "replace_with_label", &searchbar->priv->replace_label,
"regex_checkbutton", &searchbar->priv->regex_checkbutton,
"match_case_checkbutton", &searchbar->priv->match_case_checkbutton, "match_case_checkbutton", &searchbar->priv->match_case_checkbutton,
"entire_word_checkbutton", &searchbar->priv->entire_word_checkbutton, "entire_word_checkbutton", &searchbar->priv->entire_word_checkbutton,
"wrap_around_checkbutton", &searchbar->priv->wrap_around_checkbutton, "wrap_around_checkbutton", &searchbar->priv->wrap_around_checkbutton,
@ -574,10 +698,8 @@ xed_searchbar_init (XedSearchbar *searchbar)
gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (searchbar)), "xed-searchbar"); gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (searchbar)), "xed-searchbar");
searchbar->priv->search_entry = xed_history_entry_new ("history-search-for", TRUE); searchbar->priv->search_entry = xed_history_entry_new ("history-search-for", FALSE);
gtk_widget_set_hexpand (searchbar->priv->search_entry, TRUE); gtk_widget_set_hexpand (searchbar->priv->search_entry, TRUE);
xed_history_entry_set_escape_func (XED_HISTORY_ENTRY (searchbar->priv->search_entry),
(XedHistoryEntryEscapeFunc) xed_utils_escape_search_text);
searchbar->priv->search_text_entry = xed_history_entry_get_entry (XED_HISTORY_ENTRY (searchbar->priv->search_entry)); searchbar->priv->search_text_entry = xed_history_entry_get_entry (XED_HISTORY_ENTRY (searchbar->priv->search_entry));
gtk_entry_set_activates_default (GTK_ENTRY (searchbar->priv->search_text_entry), TRUE); gtk_entry_set_activates_default (GTK_ENTRY (searchbar->priv->search_text_entry), TRUE);
@ -585,9 +707,7 @@ xed_searchbar_init (XedSearchbar *searchbar)
gtk_widget_show (searchbar->priv->search_entry); gtk_widget_show (searchbar->priv->search_entry);
gtk_grid_attach (GTK_GRID (searchbar->priv->grid), searchbar->priv->search_entry, 2, 0, 1, 1); gtk_grid_attach (GTK_GRID (searchbar->priv->grid), searchbar->priv->search_entry, 2, 0, 1, 1);
searchbar->priv->replace_entry = xed_history_entry_new ("history-replace-with", TRUE); searchbar->priv->replace_entry = xed_history_entry_new ("history-replace-with", FALSE);
xed_history_entry_set_escape_func (XED_HISTORY_ENTRY (searchbar->priv->replace_entry),
(XedHistoryEntryEscapeFunc) xed_utils_escape_search_text);
searchbar->priv->replace_text_entry = xed_history_entry_get_entry ( searchbar->priv->replace_text_entry = xed_history_entry_get_entry (
XED_HISTORY_ENTRY (searchbar->priv->replace_entry)); XED_HISTORY_ENTRY (searchbar->priv->replace_entry));
@ -620,12 +740,6 @@ xed_searchbar_init (XedSearchbar *searchbar)
g_object_unref (content); g_object_unref (content);
g_signal_connect (searchbar->priv->search_text_entry, "insert_text",
G_CALLBACK (insert_text_handler), NULL);
g_signal_connect (searchbar->priv->replace_text_entry, "insert_text",
G_CALLBACK (insert_text_handler), NULL);
g_signal_connect (searchbar->priv->search_text_entry, "changed", g_signal_connect (searchbar->priv->search_text_entry, "changed",
G_CALLBACK (search_text_entry_changed), searchbar); G_CALLBACK (search_text_entry_changed), searchbar);
@ -653,27 +767,45 @@ xed_searchbar_init (XedSearchbar *searchbar)
g_signal_connect (searchbar->priv->match_case_checkbutton, "clicked", g_signal_connect (searchbar->priv->match_case_checkbutton, "clicked",
G_CALLBACK (toggle_button_clicked_callback), searchbar); G_CALLBACK (toggle_button_clicked_callback), searchbar);
searchbar->priv->search_settings = gtk_source_search_settings_new ();
g_object_bind_property (searchbar->priv->regex_checkbutton, "active",
searchbar->priv->search_settings, "regex-enabled",
G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
g_object_bind_property (searchbar->priv->match_case_checkbutton, "active",
searchbar->priv->search_settings, "case-sensitive",
G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
g_object_bind_property (searchbar->priv->entire_word_checkbutton, "active",
searchbar->priv->search_settings, "at-word-boundaries",
G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
g_object_bind_property (searchbar->priv->wrap_around_checkbutton, "active",
searchbar->priv->search_settings, "wrap-around",
G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
} }
GtkWidget * GtkWidget *
xed_searchbar_new (GtkWindow *parent, xed_searchbar_new (GtkWindow *parent)
gboolean show_replace)
{ {
XedSearchbar *searchbar; XedSearchbar *searchbar;
searchbar = g_object_new (XED_TYPE_SEARCHBAR, NULL); searchbar = g_object_new (XED_TYPE_SEARCHBAR, NULL);
searchbar->window = XED_WINDOW (parent); searchbar->window = XED_WINDOW (parent);
return GTK_WIDGET (searchbar); return GTK_WIDGET (searchbar);
} }
void void
xed_searchbar_show (XedSearchbar *searchbar, xed_searchbar_show (XedSearchbar *searchbar,
gboolean show_replace) SearchMode search_mode)
{ {
XedDocument *doc; XedDocument *doc;
gboolean selection_exists; gboolean selection_exists;
gchar *find_text = NULL; gchar *find_text = NULL;
const gchar *search_text = NULL; gint sel_len = 0;
gint sel_len;
doc = xed_window_get_active_document (searchbar->window); doc = xed_window_get_active_document (searchbar->window);
g_return_if_fail (doc != NULL); g_return_if_fail (doc != NULL);
@ -682,28 +814,28 @@ xed_searchbar_show (XedSearchbar *searchbar,
if (selection_exists && find_text != NULL && sel_len < 80) if (selection_exists && find_text != NULL && sel_len < 80)
{ {
/* gchar *escaped_find_text;
* Special case: if the currently selected text
* is the same as the unescaped search text, use the if (gtk_source_search_settings_get_regex_enabled (searchbar->priv->search_settings))
* same old search text. (Without this, if you e.g.
* search for '\n' and then open the search searchbar again,
* you'll get an unprintable single-character literal '\n' in the "search for" box).
*/
search_text = xed_searchbar_get_search_text (XED_SEARCHBAR (searchbar));
if (!(search_text != NULL && !strcmp (xed_utils_unescape_search_text (search_text), find_text)))
{ {
/* General case */ escaped_find_text = g_regex_escape_string (find_text, -1);
xed_searchbar_set_search_text (XED_SEARCHBAR (searchbar), find_text);
} }
g_free (find_text); else
} {
else escaped_find_text = gtk_source_utils_escape_search_text (find_text);
{ }
g_free (find_text);
xed_searchbar_set_search_text (XED_SEARCHBAR (searchbar), escaped_find_text);
g_free (escaped_find_text);
} }
g_free (find_text);
gtk_revealer_set_transition_type (GTK_REVEALER (searchbar->priv->revealer), GTK_REVEALER_TRANSITION_TYPE_SLIDE_UP); gtk_revealer_set_transition_type (GTK_REVEALER (searchbar->priv->revealer), GTK_REVEALER_TRANSITION_TYPE_SLIDE_UP);
gtk_revealer_set_reveal_child (GTK_REVEALER (searchbar->priv->revealer), TRUE); gtk_revealer_set_reveal_child (GTK_REVEALER (searchbar->priv->revealer), TRUE);
if (show_replace)
if (search_mode == SEARCH_MODE_REPLACE)
{ {
gtk_widget_show (searchbar->priv->replace_label); gtk_widget_show (searchbar->priv->replace_label);
gtk_widget_show (searchbar->priv->replace_entry); gtk_widget_show (searchbar->priv->replace_entry);
@ -734,6 +866,7 @@ xed_searchbar_hide (XedSearchbar *searchbar)
// focus document // focus document
active_view = xed_window_get_active_view (searchbar->window); active_view = xed_window_get_active_view (searchbar->window);
if (active_view != NULL) if (active_view != NULL)
{ {
gtk_widget_grab_focus (GTK_WIDGET (active_view)); gtk_widget_grab_focus (GTK_WIDGET (active_view));
@ -743,84 +876,35 @@ xed_searchbar_hide (XedSearchbar *searchbar)
_xed_cmd_search_clear_highlight (searchbar->window); _xed_cmd_search_clear_highlight (searchbar->window);
} }
void
xed_searchbar_set_search_text (XedSearchbar *searchbar,
const gchar *text)
{
g_return_if_fail (XED_IS_SEARCHBAR (searchbar));
g_return_if_fail (text != NULL);
gtk_entry_set_text (GTK_ENTRY (searchbar->priv->search_text_entry), text);
search_buttons_set_sensitive (searchbar, (text != '\0'));
}
/*
* The text must be unescaped before searching.
*/
const gchar *
xed_searchbar_get_search_text (XedSearchbar *searchbar)
{
g_return_val_if_fail (XED_IS_SEARCHBAR (searchbar), NULL);
return gtk_entry_get_text (GTK_ENTRY (searchbar->priv->search_text_entry));
}
void
xed_searchbar_set_replace_text (XedSearchbar *searchbar,
const gchar *text)
{
g_return_if_fail (XED_IS_SEARCHBAR (searchbar));
g_return_if_fail (text != NULL);
gtk_entry_set_text (GTK_ENTRY (searchbar->priv->replace_text_entry), text);
}
const gchar * const gchar *
xed_searchbar_get_replace_text (XedSearchbar *searchbar) xed_searchbar_get_replace_text (XedSearchbar *searchbar)
{ {
g_return_val_if_fail (XED_IS_SEARCHBAR (searchbar), NULL); g_return_val_if_fail (XED_IS_SEARCHBAR (searchbar), NULL);
return gtk_entry_get_text (GTK_ENTRY (searchbar->priv->replace_text_entry)); return gtk_entry_get_text (GTK_ENTRY (searchbar->priv->replace_text_entry));
} }
void GtkSourceSearchSettings *
xed_searchbar_set_match_case (XedSearchbar *searchbar, xed_searchbar_get_search_settings (XedSearchbar *searchbar)
gboolean match_case)
{ {
g_return_if_fail (XED_IS_SEARCHBAR (searchbar)); g_return_val_if_fail (XED_IS_SEARCHBAR (searchbar), NULL);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (searchbar->priv->match_case_checkbutton), match_case);
return searchbar->priv->search_settings;
} }
gboolean const gchar *
xed_searchbar_get_match_case (XedSearchbar *searchbar) xed_searchbar_get_search_text (XedSearchbar *searchbar)
{ {
g_return_val_if_fail (XED_IS_SEARCHBAR (searchbar), FALSE); g_return_val_if_fail (XED_IS_SEARCHBAR (searchbar), NULL);
return gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (searchbar->priv->match_case_checkbutton));
return gtk_entry_get_text (GTK_ENTRY (searchbar->priv->search_text_entry));
} }
void void
xed_searchbar_set_entire_word (XedSearchbar *searchbar, xed_searchbar_set_search_text (XedSearchbar *searchbar,
gboolean entire_word) const gchar *search_text)
{ {
g_return_if_fail (XED_IS_SEARCHBAR (searchbar)); g_return_if_fail (XED_IS_SEARCHBAR (searchbar));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (searchbar->priv->entire_word_checkbutton), entire_word);
}
gboolean gtk_entry_set_text (GTK_ENTRY (searchbar->priv->search_text_entry), search_text);
xed_searchbar_get_entire_word (XedSearchbar *searchbar)
{
g_return_val_if_fail (XED_IS_SEARCHBAR (searchbar), FALSE);
return gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (searchbar->priv->entire_word_checkbutton));
}
void
xed_searchbar_set_wrap_around (XedSearchbar *searchbar,
gboolean wrap_around)
{
g_return_if_fail (XED_IS_SEARCHBAR (searchbar));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (searchbar->priv->wrap_around_checkbutton), wrap_around);
}
gboolean
xed_searchbar_get_wrap_around (XedSearchbar *searchbar)
{
g_return_val_if_fail (XED_IS_SEARCHBAR (searchbar), FALSE);
return gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (searchbar->priv->wrap_around_checkbutton));
} }

View File

@ -3,6 +3,7 @@
#define __XED_SEARCHBAR_H__ #define __XED_SEARCHBAR_H__
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include <gtksourceview/gtksource.h>
#include "xed-window.h" #include "xed-window.h"
G_BEGIN_DECLS G_BEGIN_DECLS
@ -36,38 +37,29 @@ struct _XedSearchbarClass
gboolean (* show_replace) (XedSearchbar *dlg); gboolean (* show_replace) (XedSearchbar *dlg);
}; };
enum typedef enum
{ {
XED_SEARCHBAR_FIND_RESPONSE = 100, SEARCH_MODE_SEARCH,
XED_SEARCHBAR_REPLACE_RESPONSE, SEARCH_MODE_REPLACE
XED_SEARCHBAR_REPLACE_ALL_RESPONSE } SearchMode;
};
GType xed_searchbar_get_type (void) G_GNUC_CONST; GType xed_searchbar_get_type (void) G_GNUC_CONST;
GtkWidget *xed_searchbar_new (GtkWindow *parent, gboolean show_replace); GtkWidget *xed_searchbar_new (GtkWindow *parent);
void xed_searchbar_hide (XedSearchbar *searchbar); void xed_searchbar_hide (XedSearchbar *searchbar);
void xed_searchbar_show (XedSearchbar *searchbar, gboolean show_replace); void xed_searchbar_show (XedSearchbar *searchbar, SearchMode search_mode);
void xed_searchbar_find_again (XedSearchbar *searchbar, gboolean backward); void xed_searchbar_find_again (XedSearchbar *searchbar, gboolean backward);
void xed_searchbar_set_search_text (XedSearchbar *searchbar, const gchar *text);
const gchar *xed_searchbar_get_search_text (XedSearchbar *searchbar);
void xed_searchbar_set_replace_text (XedSearchbar *searchbar, const gchar *text);
const gchar *xed_searchbar_get_replace_text (XedSearchbar *searchbar); const gchar *xed_searchbar_get_replace_text (XedSearchbar *searchbar);
const gchar *xed_searchbar_get_search_text (XedSearchbar *searchbar);
void xed_searchbar_set_match_case (XedSearchbar *searchbar, gboolean match_case);
gboolean xed_searchbar_get_match_case (XedSearchbar *searchbar);
void xed_searchbar_set_entire_word (XedSearchbar *searchbar, gboolean entire_word);
gboolean xed_searchbar_get_entire_word (XedSearchbar *searchbar);
void xed_searchbar_set_backwards (XedSearchbar *searchbar, gboolean backwards);
gboolean xed_searchbar_get_backwards (XedSearchbar *searchbar); gboolean xed_searchbar_get_backwards (XedSearchbar *searchbar);
void xed_searchbar_set_wrap_around (XedSearchbar *searchbar, gboolean wrap_around); GtkSourceSearchSettings *xed_searchbar_get_search_settings (XedSearchbar *searchbar);
gboolean xed_searchbar_get_wrap_around (XedSearchbar *searchbar);
void xed_searchbar_set_search_text (XedSearchbar *searchbar,
const gchar *search_text);
void xed_searchbar_set_parse_escapes (XedSearchbar *searchbar, gboolean parse_escapes); void xed_searchbar_set_parse_escapes (XedSearchbar *searchbar, gboolean parse_escapes);
gboolean xed_searchbar_get_parse_escapes (XedSearchbar *searchbar); gboolean xed_searchbar_get_parse_escapes (XedSearchbar *searchbar);

View File

@ -112,6 +112,26 @@
<object class="GtkBox" id="box1"> <object class="GtkBox" id="box1">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<child>
<object class="GtkToggleButton" id="regex_checkbutton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Regular expression</property>
<child>
<object class="GtkImage" id="image4">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">xapp-use-regex-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child> <child>
<object class="GtkToggleButton" id="match_case_checkbutton"> <object class="GtkToggleButton" id="match_case_checkbutton">
<property name="visible">True</property> <property name="visible">True</property>
@ -130,7 +150,7 @@
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="fill">False</property> <property name="fill">False</property>
<property name="position">0</property> <property name="position">1</property>
</packing> </packing>
</child> </child>
<child> <child>
@ -151,7 +171,7 @@
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="fill">False</property> <property name="fill">False</property>
<property name="position">1</property> <property name="position">2</property>
</packing> </packing>
</child> </child>
<child> <child>
@ -172,7 +192,7 @@
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="fill">True</property> <property name="fill">True</property>
<property name="position">2</property> <property name="position">3</property>
</packing> </packing>
</child> </child>
<style> <style>

View File

@ -44,6 +44,7 @@
#include <glib.h> #include <glib.h>
#include <glib/gi18n.h> #include <glib/gi18n.h>
#include <gio/gio.h> #include <gio/gio.h>
#include <gtksourceview/gtksource.h>
#include "xed-utils.h" #include "xed-utils.h"
@ -276,142 +277,6 @@ xed_utils_set_atk_relation (GtkWidget *obj1,
g_object_unref (G_OBJECT (relation)); g_object_unref (G_OBJECT (relation));
} }
gchar *
xed_utils_escape_search_text (const gchar* text)
{
GString *str;
gint length;
const gchar *p;
const gchar *end;
if (text == NULL)
{
return NULL;
}
xed_debug_message (DEBUG_SEARCH, "Text: %s", text);
length = strlen (text);
/* no escape when typing.
* The short circuit works only for ascii, but we only
* care about not escaping a single '\' */
if (length == 1)
{
return g_strdup (text);
}
str = g_string_new ("");
p = text;
end = text + length;
while (p != end)
{
const gchar *next;
next = g_utf8_next_char (p);
switch (*p)
{
case '\n':
g_string_append (str, "\\n");
break;
case '\r':
g_string_append (str, "\\r");
break;
case '\t':
g_string_append (str, "\\t");
break;
case '\\':
g_string_append (str, "\\\\");
break;
default:
g_string_append_len (str, p, next - p);
break;
}
p = next;
}
return g_string_free (str, FALSE);
}
gchar *
xed_utils_unescape_search_text (const gchar *text)
{
GString *str;
gint length;
gboolean drop_prev = FALSE;
const gchar *cur;
const gchar *end;
const gchar *prev;
if (text == NULL)
{
return NULL;
}
length = strlen (text);
str = g_string_new ("");
cur = text;
end = text + length;
prev = NULL;
while (cur != end)
{
const gchar *next;
next = g_utf8_next_char (cur);
if (prev && (*prev == '\\'))
{
switch (*cur)
{
case 'n':
str = g_string_append (str, "\n");
break;
case 'r':
str = g_string_append (str, "\r");
break;
case 't':
str = g_string_append (str, "\t");
break;
case '\\':
str = g_string_append (str, "\\");
drop_prev = TRUE;
break;
default:
str = g_string_append (str, "\\");
str = g_string_append_len (str, cur, next - cur);
break;
}
}
else if (*cur != '\\')
{
str = g_string_append_len (str, cur, next - cur);
}
else if ((next == end) && (*cur == '\\'))
{
str = g_string_append (str, "\\");
}
if (!drop_prev)
{
prev = cur;
}
else
{
prev = NULL;
drop_prev = FALSE;
}
cur = next;
}
return g_string_free (str, FALSE);
}
void void
xed_warning (GtkWindow *parent, xed_warning (GtkWindow *parent,
const gchar *format, const gchar *format,

View File

@ -85,10 +85,6 @@ void xed_utils_set_atk_relation (GtkWidget *obj1,
GtkWidget *obj2, GtkWidget *obj2,
AtkRelationType rel_type); AtkRelationType rel_type);
gchar *xed_utils_escape_search_text (const gchar *text);
gchar *xed_utils_unescape_search_text (const gchar *text);
void xed_warning (GtkWindow *parent, void xed_warning (GtkWindow *parent,
const gchar *format, const gchar *format,
...) G_GNUC_PRINTF(2, 3); ...) G_GNUC_PRINTF(2, 3);

View File

@ -55,56 +55,12 @@ document_read_only_notify_handler (XedDocument *document,
gtk_text_view_set_editable (GTK_TEXT_VIEW (view), !xed_document_get_readonly (document)); gtk_text_view_set_editable (GTK_TEXT_VIEW (view), !xed_document_get_readonly (document));
} }
static void
search_highlight_updated_cb (XedDocument *doc,
GtkTextIter *start,
GtkTextIter *end,
XedView *view)
{
GdkRectangle visible_rect;
GdkRectangle updated_rect;
GdkRectangle redraw_rect;
gint y;
gint height;
GtkTextView *text_view;
text_view = GTK_TEXT_VIEW(view);
g_return_if_fail(xed_document_get_enable_search_highlighting (XED_DOCUMENT (gtk_text_view_get_buffer (text_view))));
/* get visible area */
gtk_text_view_get_visible_rect (text_view, &visible_rect);
/* get updated rectangle */
gtk_text_view_get_line_yrange (text_view, start, &y, &height);
updated_rect.y = y;
gtk_text_view_get_line_yrange (text_view, end, &y, &height);
updated_rect.height = y + height - updated_rect.y;
updated_rect.x = visible_rect.x;
updated_rect.width = visible_rect.width;
/* intersect both rectangles to see whether we need to queue a redraw */
if (gdk_rectangle_intersect (&updated_rect, &visible_rect, &redraw_rect))
{
GdkRectangle widget_rect;
gtk_text_view_buffer_to_window_coords (text_view, GTK_TEXT_WINDOW_WIDGET, redraw_rect.x, redraw_rect.y,
&widget_rect.x, &widget_rect.y);
widget_rect.width = redraw_rect.width;
widget_rect.height = redraw_rect.height;
gtk_widget_queue_draw_area (GTK_WIDGET(text_view), widget_rect.x, widget_rect.y, widget_rect.width,
widget_rect.height);
}
}
static void static void
current_buffer_removed (XedView *view) current_buffer_removed (XedView *view)
{ {
if (view->priv->current_buffer != NULL) if (view->priv->current_buffer != NULL)
{ {
g_signal_handlers_disconnect_by_func(view->priv->current_buffer, document_read_only_notify_handler, view); g_signal_handlers_disconnect_by_func(view->priv->current_buffer, document_read_only_notify_handler, view);
g_signal_handlers_disconnect_by_func(view->priv->current_buffer, search_highlight_updated_cb, view);
g_object_unref (view->priv->current_buffer); g_object_unref (view->priv->current_buffer);
view->priv->current_buffer = NULL; view->priv->current_buffer = NULL;
} }
@ -136,9 +92,9 @@ on_notify_buffer_cb (XedView *view,
GtkTextBuffer *buffer; GtkTextBuffer *buffer;
current_buffer_removed (view); current_buffer_removed (view);
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(view)); buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
if (buffer == NULL || !XED_IS_DOCUMENT(buffer)) if (buffer == NULL || !XED_IS_DOCUMENT (buffer))
{ {
return; return;
} }
@ -146,9 +102,7 @@ on_notify_buffer_cb (XedView *view,
view->priv->current_buffer = g_object_ref (buffer); view->priv->current_buffer = g_object_ref (buffer);
g_signal_connect(buffer, "notify::read-only", G_CALLBACK (document_read_only_notify_handler), view); g_signal_connect(buffer, "notify::read-only", G_CALLBACK (document_read_only_notify_handler), view);
gtk_text_view_set_editable (GTK_TEXT_VIEW(view), !xed_document_get_readonly (XED_DOCUMENT(buffer))); gtk_text_view_set_editable (GTK_TEXT_VIEW (view), !xed_document_get_readonly (XED_DOCUMENT(buffer)));
g_signal_connect(buffer, "search_highlight_updated", G_CALLBACK (search_highlight_updated_cb), view);
} }
static void static void
@ -306,35 +260,6 @@ xed_view_focus_out (GtkWidget *widget,
return FALSE; return FALSE;
} }
static gboolean
xed_view_draw (GtkWidget *widget,
cairo_t *cr)
{
GtkTextView *text_view;
XedDocument *doc;
GdkWindow *window;
text_view = GTK_TEXT_VIEW(widget);
doc = XED_DOCUMENT(gtk_text_view_get_buffer (text_view));
window = gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT);
if (gtk_cairo_should_draw_window (cr, window) && xed_document_get_enable_search_highlighting (doc))
{
GdkRectangle visible_rect;
GtkTextIter iter1, iter2;
gtk_text_view_get_visible_rect (text_view, &visible_rect);
gtk_text_view_get_line_at_y (text_view, &iter1, visible_rect.y, NULL);
gtk_text_view_get_line_at_y (text_view, &iter2, visible_rect.y + visible_rect.height, NULL);
gtk_text_iter_forward_line (&iter2);
_xed_document_search_region (doc, &iter1, &iter2);
}
return GTK_WIDGET_CLASS (xed_view_parent_class)->draw (widget, cr);
}
static GdkAtom static GdkAtom
drag_get_uri_target (GtkWidget *widget, drag_get_uri_target (GtkWidget *widget,
GdkDragContext *context) GdkDragContext *context)
@ -615,7 +540,6 @@ xed_view_class_init (XedViewClass *klass)
object_class->constructed = xed_view_constructed; object_class->constructed = xed_view_constructed;
widget_class->focus_out_event = xed_view_focus_out; widget_class->focus_out_event = xed_view_focus_out;
widget_class->draw = xed_view_draw;
/* /*
* Override the gtk_text_view_drag_motion and drag_drop * Override the gtk_text_view_drag_motion and drag_drop

View File

@ -660,7 +660,7 @@ set_sensitivity_according_to_tab (XedWindow *window,
action = gtk_action_group_get_action (window->priv->action_group, "SearchReplace"); action = gtk_action_group_get_action (window->priv->action_group, "SearchReplace");
gtk_action_set_sensitive (action, state_normal && editable); gtk_action_set_sensitive (action, state_normal && editable);
b = xed_document_get_can_search_again (doc); b = TRUE;
action = gtk_action_group_get_action (window->priv->action_group, "SearchFindNext"); action = gtk_action_group_get_action (window->priv->action_group, "SearchFindNext");
gtk_action_set_sensitive (action, (state_normal || state == XED_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION) && b); gtk_action_set_sensitive (action, (state_normal || state == XED_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION) && b);
@ -1715,7 +1715,7 @@ create_statusbar (XedWindow *window,
xed_debug (DEBUG_WINDOW); xed_debug (DEBUG_WINDOW);
window->priv->statusbar = xed_statusbar_new (); window->priv->statusbar = xed_statusbar_new ();
window->priv->searchbar = xed_searchbar_new (GTK_WINDOW (window), TRUE); window->priv->searchbar = xed_searchbar_new (GTK_WINDOW (window));
window->priv->generic_message_cid = gtk_statusbar_get_context_id (GTK_STATUSBAR (window->priv->statusbar), window->priv->generic_message_cid = gtk_statusbar_get_context_id (GTK_STATUSBAR (window->priv->statusbar),
"generic_message"); "generic_message");
@ -2781,9 +2781,9 @@ fullscreen_controls_build (XedWindow *window)
} }
static void static void
can_search_again (XedDocument *doc, search_text_notify_cb (XedDocument *doc,
GParamSpec *pspec, GParamSpec *pspec,
XedWindow *window) XedWindow *window)
{ {
gboolean sensitive; gboolean sensitive;
GtkAction *action; GtkAction *action;
@ -2793,7 +2793,7 @@ can_search_again (XedDocument *doc,
return; return;
} }
sensitive = xed_document_get_can_search_again (doc); sensitive = TRUE;
action = gtk_action_group_get_action (window->priv->action_group, "SearchFindNext"); action = gtk_action_group_get_action (window->priv->action_group, "SearchFindNext");
gtk_action_set_sensitive (action, sensitive); gtk_action_set_sensitive (action, sensitive);
@ -2948,7 +2948,7 @@ notebook_tab_added (XedNotebook *notebook,
g_signal_connect(tab, "notify::state", G_CALLBACK (sync_state), window); g_signal_connect(tab, "notify::state", G_CALLBACK (sync_state), window);
g_signal_connect(doc, "cursor-moved", G_CALLBACK (update_cursor_position_statusbar), window); g_signal_connect(doc, "cursor-moved", G_CALLBACK (update_cursor_position_statusbar), window);
g_signal_connect(doc, "notify::can-search-again", G_CALLBACK (can_search_again), window); g_signal_connect(doc, "notify::search-text", G_CALLBACK (search_text_notify_cb), window);
g_signal_connect(doc, "notify::can-undo", G_CALLBACK (can_undo), window); g_signal_connect(doc, "notify::can-undo", G_CALLBACK (can_undo), window);
g_signal_connect(doc, "notify::can-redo", G_CALLBACK (can_redo), window); g_signal_connect(doc, "notify::can-redo", G_CALLBACK (can_redo), window);
g_signal_connect(doc, "notify::has-selection", G_CALLBACK (selection_changed), window); g_signal_connect(doc, "notify::has-selection", G_CALLBACK (selection_changed), window);
@ -2986,7 +2986,7 @@ notebook_tab_removed (XedNotebook *notebook,
g_signal_handlers_disconnect_by_func(tab, G_CALLBACK (sync_name), window); g_signal_handlers_disconnect_by_func(tab, G_CALLBACK (sync_name), window);
g_signal_handlers_disconnect_by_func(tab, G_CALLBACK (sync_state), window); g_signal_handlers_disconnect_by_func(tab, G_CALLBACK (sync_state), window);
g_signal_handlers_disconnect_by_func(doc, G_CALLBACK (update_cursor_position_statusbar), window); g_signal_handlers_disconnect_by_func(doc, G_CALLBACK (update_cursor_position_statusbar), window);
g_signal_handlers_disconnect_by_func(doc, G_CALLBACK (can_search_again), window); g_signal_handlers_disconnect_by_func(doc, G_CALLBACK (search_text_notify_cb), window);
g_signal_handlers_disconnect_by_func(doc, G_CALLBACK (can_undo), window); g_signal_handlers_disconnect_by_func(doc, G_CALLBACK (can_undo), window);
g_signal_handlers_disconnect_by_func(doc, G_CALLBACK (can_redo), window); g_signal_handlers_disconnect_by_func(doc, G_CALLBACK (can_redo), window);
g_signal_handlers_disconnect_by_func(doc, G_CALLBACK (selection_changed), window); g_signal_handlers_disconnect_by_func(doc, G_CALLBACK (selection_changed), window);

View File

@ -1,646 +0,0 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
*
* xedtextregion.h - GtkTextMark based region utility functions
*
* This file is part of the GtkSourceView widget
*
* Copyright (C) 2002 Gustavo Giráldez <gustavo.giraldez@gmx.net>
*
* 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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <glib.h>
#include "xedtextregion.h"
#undef ENABLE_DEBUG
/*
#define ENABLE_DEBUG
*/
#ifdef ENABLE_DEBUG
#define DEBUG(x) (x)
#else
#define DEBUG(x)
#endif
typedef struct _Subregion {
GtkTextMark *start;
GtkTextMark *end;
} Subregion;
struct _XedTextRegion {
GtkTextBuffer *buffer;
GList *subregions;
guint32 time_stamp;
};
typedef struct _XedTextRegionIteratorReal XedTextRegionIteratorReal;
struct _XedTextRegionIteratorReal {
XedTextRegion *region;
guint32 region_time_stamp;
GList *subregions;
};
/* ----------------------------------------------------------------------
Private interface
---------------------------------------------------------------------- */
/* Find and return a subregion node which contains the given text
iter. If left_side is TRUE, return the subregion which contains
the text iter or which is the leftmost; else return the rightmost
subregion */
static GList *
find_nearest_subregion (XedTextRegion *region,
const GtkTextIter *iter,
GList *begin,
gboolean leftmost,
gboolean include_edges)
{
GList *l, *retval;
g_return_val_if_fail (region != NULL && iter != NULL, NULL);
if (!begin)
begin = region->subregions;
if (begin)
retval = begin->prev;
else
retval = NULL;
for (l = begin; l; l = l->next) {
GtkTextIter sr_iter;
Subregion *sr = l->data;
gint cmp;
if (!leftmost) {
gtk_text_buffer_get_iter_at_mark (region->buffer, &sr_iter, sr->end);
cmp = gtk_text_iter_compare (iter, &sr_iter);
if (cmp < 0 || (cmp == 0 && include_edges)) {
retval = l;
break;
}
} else {
gtk_text_buffer_get_iter_at_mark (region->buffer, &sr_iter, sr->start);
cmp = gtk_text_iter_compare (iter, &sr_iter);
if (cmp > 0 || (cmp == 0 && include_edges))
retval = l;
else
break;
}
}
return retval;
}
/* ----------------------------------------------------------------------
Public interface
---------------------------------------------------------------------- */
XedTextRegion *
xed_text_region_new (GtkTextBuffer *buffer)
{
XedTextRegion *region;
g_return_val_if_fail (buffer != NULL, NULL);
region = g_new (XedTextRegion, 1);
region->buffer = buffer;
region->subregions = NULL;
region->time_stamp = 0;
return region;
}
void
xed_text_region_destroy (XedTextRegion *region, gboolean delete_marks)
{
g_return_if_fail (region != NULL);
while (region->subregions) {
Subregion *sr = region->subregions->data;
if (delete_marks) {
gtk_text_buffer_delete_mark (region->buffer, sr->start);
gtk_text_buffer_delete_mark (region->buffer, sr->end);
}
g_free (sr);
region->subregions = g_list_delete_link (region->subregions,
region->subregions);
}
region->buffer = NULL;
region->time_stamp = 0;
g_free (region);
}
GtkTextBuffer *
xed_text_region_get_buffer (XedTextRegion *region)
{
g_return_val_if_fail (region != NULL, NULL);
return region->buffer;
}
static void
xed_text_region_clear_zero_length_subregions (XedTextRegion *region)
{
GtkTextIter start, end;
GList *node;
g_return_if_fail (region != NULL);
for (node = region->subregions; node; ) {
Subregion *sr = node->data;
gtk_text_buffer_get_iter_at_mark (region->buffer, &start, sr->start);
gtk_text_buffer_get_iter_at_mark (region->buffer, &end, sr->end);
if (gtk_text_iter_equal (&start, &end)) {
gtk_text_buffer_delete_mark (region->buffer, sr->start);
gtk_text_buffer_delete_mark (region->buffer, sr->end);
g_free (sr);
if (node == region->subregions)
region->subregions = node = g_list_delete_link (node, node);
else
node = g_list_delete_link (node, node);
++region->time_stamp;
} else {
node = node->next;
}
}
}
void
xed_text_region_add (XedTextRegion *region,
const GtkTextIter *_start,
const GtkTextIter *_end)
{
GList *start_node, *end_node;
GtkTextIter start, end;
g_return_if_fail (region != NULL && _start != NULL && _end != NULL);
start = *_start;
end = *_end;
DEBUG (g_print ("---\n"));
DEBUG (xed_text_region_debug_print (region));
DEBUG (g_message ("region_add (%d, %d)",
gtk_text_iter_get_offset (&start),
gtk_text_iter_get_offset (&end)));
gtk_text_iter_order (&start, &end);
/* don't add zero-length regions */
if (gtk_text_iter_equal (&start, &end))
return;
/* find bounding subregions */
start_node = find_nearest_subregion (region, &start, NULL, FALSE, TRUE);
end_node = find_nearest_subregion (region, &end, start_node, TRUE, TRUE);
if (start_node == NULL || end_node == NULL || end_node == start_node->prev) {
/* create the new subregion */
Subregion *sr = g_new0 (Subregion, 1);
sr->start = gtk_text_buffer_create_mark (region->buffer, NULL, &start, TRUE);
sr->end = gtk_text_buffer_create_mark (region->buffer, NULL, &end, FALSE);
if (start_node == NULL) {
/* append the new region */
region->subregions = g_list_append (region->subregions, sr);
} else if (end_node == NULL) {
/* prepend the new region */
region->subregions = g_list_prepend (region->subregions, sr);
} else {
/* we are in the middle of two subregions */
region->subregions = g_list_insert_before (region->subregions,
start_node, sr);
}
}
else {
GtkTextIter iter;
Subregion *sr = start_node->data;
if (start_node != end_node) {
/* we need to merge some subregions */
GList *l = start_node->next;
Subregion *q;
gtk_text_buffer_delete_mark (region->buffer, sr->end);
while (l != end_node) {
q = l->data;
gtk_text_buffer_delete_mark (region->buffer, q->start);
gtk_text_buffer_delete_mark (region->buffer, q->end);
g_free (q);
l = g_list_delete_link (l, l);
}
q = l->data;
gtk_text_buffer_delete_mark (region->buffer, q->start);
sr->end = q->end;
g_free (q);
l = g_list_delete_link (l, l);
}
/* now move marks if that action expands the region */
gtk_text_buffer_get_iter_at_mark (region->buffer, &iter, sr->start);
if (gtk_text_iter_compare (&iter, &start) > 0)
gtk_text_buffer_move_mark (region->buffer, sr->start, &start);
gtk_text_buffer_get_iter_at_mark (region->buffer, &iter, sr->end);
if (gtk_text_iter_compare (&iter, &end) < 0)
gtk_text_buffer_move_mark (region->buffer, sr->end, &end);
}
++region->time_stamp;
DEBUG (xed_text_region_debug_print (region));
}
void
xed_text_region_subtract (XedTextRegion *region,
const GtkTextIter *_start,
const GtkTextIter *_end)
{
GList *start_node, *end_node, *node;
GtkTextIter sr_start_iter, sr_end_iter;
gboolean done;
gboolean start_is_outside, end_is_outside;
Subregion *sr;
GtkTextIter start, end;
g_return_if_fail (region != NULL && _start != NULL && _end != NULL);
start = *_start;
end = *_end;
DEBUG (g_print ("---\n"));
DEBUG (xed_text_region_debug_print (region));
DEBUG (g_message ("region_substract (%d, %d)",
gtk_text_iter_get_offset (&start),
gtk_text_iter_get_offset (&end)));
gtk_text_iter_order (&start, &end);
/* find bounding subregions */
start_node = find_nearest_subregion (region, &start, NULL, FALSE, FALSE);
end_node = find_nearest_subregion (region, &end, start_node, TRUE, FALSE);
/* easy case first */
if (start_node == NULL || end_node == NULL || end_node == start_node->prev)
return;
/* deal with the start point */
start_is_outside = end_is_outside = FALSE;
sr = start_node->data;
gtk_text_buffer_get_iter_at_mark (region->buffer, &sr_start_iter, sr->start);
gtk_text_buffer_get_iter_at_mark (region->buffer, &sr_end_iter, sr->end);
if (gtk_text_iter_in_range (&start, &sr_start_iter, &sr_end_iter) &&
!gtk_text_iter_equal (&start, &sr_start_iter)) {
/* the starting point is inside the first subregion */
if (gtk_text_iter_in_range (&end, &sr_start_iter, &sr_end_iter) &&
!gtk_text_iter_equal (&end, &sr_end_iter)) {
/* the ending point is also inside the first
subregion: we need to split */
Subregion *new_sr = g_new0 (Subregion, 1);
new_sr->end = sr->end;
new_sr->start = gtk_text_buffer_create_mark (region->buffer,
NULL, &end, TRUE);
start_node = g_list_insert_before (start_node, start_node->next, new_sr);
sr->end = gtk_text_buffer_create_mark (region->buffer,
NULL, &start, FALSE);
/* no further processing needed */
DEBUG (g_message ("subregion splitted"));
return;
} else {
/* the ending point is outside, so just move
the end of the subregion to the starting point */
gtk_text_buffer_move_mark (region->buffer, sr->end, &start);
}
} else {
/* the starting point is outside (and so to the left)
of the first subregion */
DEBUG (g_message ("start is outside"));
start_is_outside = TRUE;
}
/* deal with the end point */
if (start_node != end_node) {
sr = end_node->data;
gtk_text_buffer_get_iter_at_mark (region->buffer, &sr_start_iter, sr->start);
gtk_text_buffer_get_iter_at_mark (region->buffer, &sr_end_iter, sr->end);
}
if (gtk_text_iter_in_range (&end, &sr_start_iter, &sr_end_iter) &&
!gtk_text_iter_equal (&end, &sr_end_iter)) {
/* ending point is inside, move the start mark */
gtk_text_buffer_move_mark (region->buffer, sr->start, &end);
} else {
end_is_outside = TRUE;
DEBUG (g_message ("end is outside"));
}
/* finally remove any intermediate subregions */
done = FALSE;
node = start_node;
while (!done) {
if (node == end_node)
/* we are done, exit in the next iteration */
done = TRUE;
if ((node == start_node && !start_is_outside) ||
(node == end_node && !end_is_outside)) {
/* skip starting or ending node */
node = node->next;
} else {
GList *l = node->next;
sr = node->data;
gtk_text_buffer_delete_mark (region->buffer, sr->start);
gtk_text_buffer_delete_mark (region->buffer, sr->end);
g_free (sr);
region->subregions = g_list_delete_link (region->subregions,
node);
node = l;
}
}
++region->time_stamp;
DEBUG (xed_text_region_debug_print (region));
/* now get rid of empty subregions */
xed_text_region_clear_zero_length_subregions (region);
DEBUG (xed_text_region_debug_print (region));
}
gint
xed_text_region_subregions (XedTextRegion *region)
{
g_return_val_if_fail (region != NULL, 0);
return g_list_length (region->subregions);
}
gboolean
xed_text_region_nth_subregion (XedTextRegion *region,
guint subregion,
GtkTextIter *start,
GtkTextIter *end)
{
Subregion *sr;
g_return_val_if_fail (region != NULL, FALSE);
sr = g_list_nth_data (region->subregions, subregion);
if (sr == NULL)
return FALSE;
if (start)
gtk_text_buffer_get_iter_at_mark (region->buffer, start, sr->start);
if (end)
gtk_text_buffer_get_iter_at_mark (region->buffer, end, sr->end);
return TRUE;
}
XedTextRegion *
xed_text_region_intersect (XedTextRegion *region,
const GtkTextIter *_start,
const GtkTextIter *_end)
{
GList *start_node, *end_node, *node;
GtkTextIter sr_start_iter, sr_end_iter;
Subregion *sr, *new_sr;
gboolean done;
XedTextRegion *new_region;
GtkTextIter start, end;
g_return_val_if_fail (region != NULL && _start != NULL && _end != NULL, NULL);
start = *_start;
end = *_end;
gtk_text_iter_order (&start, &end);
/* find bounding subregions */
start_node = find_nearest_subregion (region, &start, NULL, FALSE, FALSE);
end_node = find_nearest_subregion (region, &end, start_node, TRUE, FALSE);
/* easy case first */
if (start_node == NULL || end_node == NULL || end_node == start_node->prev)
return NULL;
new_region = xed_text_region_new (region->buffer);
done = FALSE;
sr = start_node->data;
gtk_text_buffer_get_iter_at_mark (region->buffer, &sr_start_iter, sr->start);
gtk_text_buffer_get_iter_at_mark (region->buffer, &sr_end_iter, sr->end);
/* starting node */
if (gtk_text_iter_in_range (&start, &sr_start_iter, &sr_end_iter)) {
new_sr = g_new0 (Subregion, 1);
new_region->subregions = g_list_prepend (new_region->subregions, new_sr);
new_sr->start = gtk_text_buffer_create_mark (new_region->buffer, NULL,
&start, TRUE);
if (start_node == end_node) {
/* things will finish shortly */
done = TRUE;
if (gtk_text_iter_in_range (&end, &sr_start_iter, &sr_end_iter))
new_sr->end = gtk_text_buffer_create_mark (new_region->buffer,
NULL, &end, FALSE);
else
new_sr->end = gtk_text_buffer_create_mark (new_region->buffer,
NULL, &sr_end_iter,
FALSE);
} else {
new_sr->end = gtk_text_buffer_create_mark (new_region->buffer, NULL,
&sr_end_iter, FALSE);
}
node = start_node->next;
} else {
/* start should be the same as the subregion, so copy it in the loop */
node = start_node;
}
if (!done) {
while (node != end_node) {
/* copy intermediate subregions verbatim */
sr = node->data;
gtk_text_buffer_get_iter_at_mark (region->buffer, &sr_start_iter,
sr->start);
gtk_text_buffer_get_iter_at_mark (region->buffer, &sr_end_iter, sr->end);
new_sr = g_new0 (Subregion, 1);
new_region->subregions = g_list_prepend (new_region->subregions, new_sr);
new_sr->start = gtk_text_buffer_create_mark (new_region->buffer, NULL,
&sr_start_iter, TRUE);
new_sr->end = gtk_text_buffer_create_mark (new_region->buffer, NULL,
&sr_end_iter, FALSE);
/* next node */
node = node->next;
}
/* ending node */
sr = node->data;
gtk_text_buffer_get_iter_at_mark (region->buffer, &sr_start_iter, sr->start);
gtk_text_buffer_get_iter_at_mark (region->buffer, &sr_end_iter, sr->end);
new_sr = g_new0 (Subregion, 1);
new_region->subregions = g_list_prepend (new_region->subregions, new_sr);
new_sr->start = gtk_text_buffer_create_mark (new_region->buffer, NULL,
&sr_start_iter, TRUE);
if (gtk_text_iter_in_range (&end, &sr_start_iter, &sr_end_iter))
new_sr->end = gtk_text_buffer_create_mark (new_region->buffer, NULL,
&end, FALSE);
else
new_sr->end = gtk_text_buffer_create_mark (new_region->buffer, NULL,
&sr_end_iter, FALSE);
}
new_region->subregions = g_list_reverse (new_region->subregions);
return new_region;
}
static gboolean
check_iterator (XedTextRegionIteratorReal *real)
{
if ((real->region == NULL) ||
(real->region_time_stamp != real->region->time_stamp))
{
g_warning("Invalid iterator: either the iterator "
"is uninitialized, or the region "
"has been modified since the iterator "
"was created.");
return FALSE;
}
return TRUE;
}
void
xed_text_region_get_iterator (XedTextRegion *region,
XedTextRegionIterator *iter,
guint start)
{
XedTextRegionIteratorReal *real;
g_return_if_fail (region != NULL);
g_return_if_fail (iter != NULL);
real = (XedTextRegionIteratorReal *)iter;
/* region->subregions may be NULL, -> end iter */
real->region = region;
real->subregions = g_list_nth (region->subregions, start);
real->region_time_stamp = region->time_stamp;
}
gboolean
xed_text_region_iterator_is_end (XedTextRegionIterator *iter)
{
XedTextRegionIteratorReal *real;
g_return_val_if_fail (iter != NULL, FALSE);
real = (XedTextRegionIteratorReal *)iter;
g_return_val_if_fail (check_iterator (real), FALSE);
return (real->subregions == NULL);
}
gboolean
xed_text_region_iterator_next (XedTextRegionIterator *iter)
{
XedTextRegionIteratorReal *real;
g_return_val_if_fail (iter != NULL, FALSE);
real = (XedTextRegionIteratorReal *)iter;
g_return_val_if_fail (check_iterator (real), FALSE);
if (real->subregions != NULL) {
real->subregions = g_list_next (real->subregions);
return TRUE;
}
else
return FALSE;
}
void
xed_text_region_iterator_get_subregion (XedTextRegionIterator *iter,
GtkTextIter *start,
GtkTextIter *end)
{
XedTextRegionIteratorReal *real;
Subregion *sr;
g_return_if_fail (iter != NULL);
real = (XedTextRegionIteratorReal *)iter;
g_return_if_fail (check_iterator (real));
g_return_if_fail (real->subregions != NULL);
sr = (Subregion*)real->subregions->data;
g_return_if_fail (sr != NULL);
if (start)
gtk_text_buffer_get_iter_at_mark (real->region->buffer, start, sr->start);
if (end)
gtk_text_buffer_get_iter_at_mark (real->region->buffer, end, sr->end);
}
void
xed_text_region_debug_print (XedTextRegion *region)
{
GList *l;
g_return_if_fail (region != NULL);
g_print ("Subregions: ");
l = region->subregions;
while (l) {
Subregion *sr = l->data;
GtkTextIter iter1, iter2;
gtk_text_buffer_get_iter_at_mark (region->buffer, &iter1, sr->start);
gtk_text_buffer_get_iter_at_mark (region->buffer, &iter2, sr->end);
g_print ("%d-%d ", gtk_text_iter_get_offset (&iter1),
gtk_text_iter_get_offset (&iter2));
l = l->next;
}
g_print ("\n");
}

View File

@ -1,88 +0,0 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
*
* xedtextregion.h - GtkTextMark based region utility functions
*
* This file is part of the GtkSourceView widget
*
* Copyright (C) 2002 Gustavo Giráldez <gustavo.giraldez@gmx.net>
*
* 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 St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __XED_TEXT_REGION_H__
#define __XED_TEXT_REGION_H__
#include <gtk/gtk.h>
G_BEGIN_DECLS
typedef struct _XedTextRegion XedTextRegion;
typedef struct _XedTextRegionIterator XedTextRegionIterator;
struct _XedTextRegionIterator {
/* XedTextRegionIterator is an opaque datatype; ignore all these fields.
* Initialize the iter with xed_text_region_get_iterator
* function
*/
/*< private >*/
gpointer dummy1;
guint32 dummy2;
gpointer dummy3;
};
XedTextRegion *xed_text_region_new (GtkTextBuffer *buffer);
void xed_text_region_destroy (XedTextRegion *region,
gboolean delete_marks);
GtkTextBuffer *xed_text_region_get_buffer (XedTextRegion *region);
void xed_text_region_add (XedTextRegion *region,
const GtkTextIter *_start,
const GtkTextIter *_end);
void xed_text_region_subtract (XedTextRegion *region,
const GtkTextIter *_start,
const GtkTextIter *_end);
gint xed_text_region_subregions (XedTextRegion *region);
gboolean xed_text_region_nth_subregion (XedTextRegion *region,
guint subregion,
GtkTextIter *start,
GtkTextIter *end);
XedTextRegion *xed_text_region_intersect (XedTextRegion *region,
const GtkTextIter *_start,
const GtkTextIter *_end);
void xed_text_region_get_iterator (XedTextRegion *region,
XedTextRegionIterator *iter,
guint start);
gboolean xed_text_region_iterator_is_end (XedTextRegionIterator *iter);
/* Returns FALSE if iterator is the end iterator */
gboolean xed_text_region_iterator_next (XedTextRegionIterator *iter);
void xed_text_region_iterator_get_subregion (XedTextRegionIterator *iter,
GtkTextIter *start,
GtkTextIter *end);
void xed_text_region_debug_print (XedTextRegion *region);
G_END_DECLS
#endif /* __XED_TEXT_REGION_H__ */