diff --git a/moo/TODO b/moo/TODO index 703db8d3..789d079c 100644 --- a/moo/TODO +++ b/moo/TODO @@ -12,6 +12,7 @@ Editor Terminal ======== +0. FIX IT! 1. Think about profiles, can they help? 2. Terminal settings. diff --git a/moo/mooedit/Makefile.incl b/moo/mooedit/Makefile.incl index 43cd2a27..b7a7f2b2 100644 --- a/moo/mooedit/Makefile.incl +++ b/moo/mooedit/Makefile.incl @@ -11,8 +11,8 @@ mooedit_srcdir = $(srcdir)/$(mooedit) mooedit_glade = $(mooedit)/glade moo_extra_dist += \ - $(mooedit_glade)/mooeditgotoline.glade \ - $(mooedit_glade)/mooeditfind.glade \ + $(mooedit_glade)/mootextgotoline.glade \ + $(mooedit_glade)/mootextfind.glade \ $(mooedit_glade)/mooeditprefs.glade \ $(mooedit_glade)/mooeditcolorsprefs.glade \ $(mooedit_glade)/moopluginprefs.glade @@ -86,7 +86,7 @@ mooedit_include_headers = \ $(mooedit)/mooedit.h \ $(mooedit)/mooeditor.h \ $(mooedit)/mooeditprefs.h \ - $(mooedit)/mooeditsearch.h \ + $(mooedit)/mootextsearch.h \ $(mooedit)/mooeditwindow.h \ $(mooedit)/mooindenter.h \ $(mooedit)/moolang.h \ @@ -106,8 +106,6 @@ mooedit_noinst_headers = \ $(mooedit)/mooeditcolorsprefs-glade.h \ $(mooedit)/mooeditdialogs.h \ $(mooedit)/mooeditfileops.h \ - $(mooedit)/mooeditfind-glade.h \ - $(mooedit)/mooeditgotoline-glade.h \ $(mooedit)/mooeditprefs-glade.h \ $(mooedit)/moohighlighter.h \ $(mooedit)/moolang-aux.h \ @@ -116,6 +114,9 @@ mooedit_noinst_headers = \ $(mooedit)/moolang-strings.h \ $(mooedit)/moolinebuffer.h \ $(mooedit)/moopluginprefs-glade.h \ + $(mooedit)/mootextfind-glade.h \ + $(mooedit)/mootextfind.h \ + $(mooedit)/mootextgotoline-glade.h \ $(mooedit)/mootextbtree.h \ $(mooedit)/mootextview-private.h @@ -128,12 +129,12 @@ mooedit_sources = \ $(mooedit)/mooedit.c \ $(mooedit)/mooeditdialogs.c \ $(mooedit)/mooeditfileops.c \ - $(mooedit)/mooeditfind.c \ + $(mooedit)/mootextfind.c \ $(mooedit)/mooeditinput.c \ $(mooedit)/mooeditor.c \ $(mooedit)/mooeditprefs.c \ $(mooedit)/mooeditprefspage.c \ - $(mooedit)/mooeditsearch.c \ + $(mooedit)/mootextsearch.c \ $(mooedit)/mooeditwindow.c \ $(mooedit)/moohighlighter.c \ $(mooedit)/mooindenter.c \ @@ -151,20 +152,20 @@ mooedit_sources = \ $(mooedit)/mootextview.c mooedit_built_sources = \ - $(mooedit)/mooeditgotoline-glade.h \ - $(mooedit)/mooeditfind-glade.h \ + $(mooedit)/mootextgotoline-glade.h \ + $(mooedit)/mootextfind-glade.h \ $(mooedit)/moopluginprefs-glade.h \ $(mooedit)/mooeditprefs-glade.h \ $(mooedit)/mooeditcolorsprefs-glade.h -$(mooedit)/mooeditgotoline-glade.h: $(mooedit_srcdir)/glade/mooeditgotoline.glade $(XML2H) - sh $(XML2H) MOO_EDIT_GOTO_LINE_GLADE_UI $(mooedit_srcdir)/glade/mooeditgotoline.glade \ - > $(mooedit)/mooeditgotoline-glade.h +$(mooedit)/mootextgotoline-glade.h: $(mooedit_srcdir)/glade/mootextgotoline.glade $(XML2H) + sh $(XML2H) MOO_TEXT_GOTO_LINE_GLADE_UI $(mooedit_srcdir)/glade/mootextgotoline.glade \ + > $(mooedit)/mootextgotoline-glade.h -$(mooedit)/mooeditfind-glade.h: $(mooedit_srcdir)/glade/mooeditfind.glade $(XML2H) - sh $(XML2H) MOO_EDIT_FIND_GLADE_UI $(mooedit_srcdir)/glade/mooeditfind.glade \ - > $(mooedit)/mooeditfind-glade.h +$(mooedit)/mootextfind-glade.h: $(mooedit_srcdir)/glade/mootextfind.glade $(XML2H) + sh $(XML2H) MOO_TEXT_FIND_GLADE_UI $(mooedit_srcdir)/glade/mootextfind.glade \ + > $(mooedit)/mootextfind-glade.h $(mooedit)/mooeditprefs-glade.h: $(mooedit_srcdir)/glade/mooeditprefs.glade $(XML2H) sh $(XML2H) MOO_EDIT_PREFS_GLADE_UI $(mooedit_srcdir)/glade/mooeditprefs.glade \ diff --git a/moo/mooedit/glade/mooeditfind.glade b/moo/mooedit/glade/mootextfind.glade similarity index 96% rename from moo/mooedit/glade/mooeditfind.glade rename to moo/mooedit/glade/mootextfind.glade index 337949da..d1074021 100644 --- a/moo/mooedit/glade/mooeditfind.glade +++ b/moo/mooedit/glade/mootextfind.glade @@ -64,14 +64,14 @@ - + 3 True False 3 - + True 0 0.5 @@ -117,7 +117,7 @@ - + True True True @@ -141,7 +141,7 @@ 0 - + True True Regular expression @@ -258,7 +258,7 @@ - + True True True @@ -282,10 +282,10 @@ 0 - + True True - Use placeholders + Literal replacement True GTK_RELIEF_NORMAL False @@ -338,7 +338,7 @@ - + True 0 0.5 @@ -366,7 +366,7 @@ 3 - + True True Whole words only @@ -410,7 +410,7 @@ - + True True Selected text @@ -453,7 +453,7 @@ - + True True Find backwards @@ -474,7 +474,7 @@ - + True True Don't prompt on replace @@ -570,7 +570,7 @@ True GTK_RELIEF_NORMAL False - 3 + 2 @@ -583,7 +583,7 @@ True GTK_RELIEF_NORMAL False - 4 + 3 @@ -596,7 +596,7 @@ True GTK_RELIEF_NORMAL False - 2 + 1 @@ -609,7 +609,7 @@ True GTK_RELIEF_NORMAL False - -6 + 0 diff --git a/moo/mooedit/glade/mooeditgotoline.glade b/moo/mooedit/glade/mootextgotoline.glade similarity index 100% rename from moo/mooedit/glade/mooeditgotoline.glade rename to moo/mooedit/glade/mootextgotoline.glade diff --git a/moo/mooedit/mooedit-private.h b/moo/mooedit/mooedit-private.h index 23e5fee8..ec71b7cd 100644 --- a/moo/mooedit/mooedit-private.h +++ b/moo/mooedit/mooedit-private.h @@ -20,7 +20,6 @@ #define __MOO_EDIT_PRIVATE_H__ #include "mooedit/mooeditor.h" -#include "mooedit/mooeditsearch.h" #include "mooedit/mootextview.h" G_BEGIN_DECLS diff --git a/moo/mooedit/mooeditdialogs.c b/moo/mooedit/mooeditdialogs.c index 77a17783..a0981e8f 100644 --- a/moo/mooedit/mooeditdialogs.c +++ b/moo/mooedit/mooeditdialogs.c @@ -16,10 +16,11 @@ #include "mooedit/mooeditdialogs.h" #include "mooedit/mooedit-private.h" #include "mooedit/mooeditprefs.h" -#include "mooedit/mooeditfind-glade.h" +#include "mooedit/mootextfind-glade.h" #include "mooutils/moodialogs.h" #include "mooutils/moostock.h" #include "mooutils/mooglade.h" +#include "mooutils/eggregex.h" #include @@ -576,24 +577,38 @@ moo_edit_file_modified_on_disk_dialog (MooEdit *edit) /* Search dialogs */ +static GtkWindow * +get_parent_window (GtkWidget *widget) +{ + if (widget) + { + widget = gtk_widget_get_toplevel (widget); + + if (GTK_WIDGET_TOPLEVEL (widget)) + return GTK_WINDOW (widget); + } + + return NULL; +} + + void -moo_text_nothing_found_dialog (MooTextView *view, +moo_text_nothing_found_dialog (GtkWidget *parent, const char *text, gboolean regex) { - GtkWindow *parent_window; GtkWidget *dialog; char *msg_text; - g_return_if_fail (MOO_IS_TEXT_VIEW (view) && text != NULL); + g_return_if_fail (text != NULL); if (regex) msg_text = g_strdup_printf ("Search pattern '%s' not found!", text); else msg_text = g_strdup_printf ("Search string '%s' not found!", text); - parent_window = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view))); - dialog = gtk_message_dialog_new (parent_window, GTK_DIALOG_MODAL, + dialog = gtk_message_dialog_new (get_parent_window (parent), + GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_NONE, msg_text); gtk_dialog_add_buttons (GTK_DIALOG (dialog), GTK_STOCK_CLOSE, @@ -608,16 +623,13 @@ moo_text_nothing_found_dialog (MooTextView *view, gboolean -moo_text_search_from_beginning_dialog (MooTextView *view, - gboolean backwards) +moo_text_search_from_start_dialog (GtkWidget *widget, + gboolean backwards) { - GtkWindow *parent_window; GtkWidget *dialog; int response; const char *msg; - g_return_val_if_fail (MOO_IS_TEXT_VIEW (view), FALSE); - if (backwards) msg = "Beginning of document reached.\n" "Continue from the end?"; @@ -625,8 +637,7 @@ moo_text_search_from_beginning_dialog (MooTextView *view, msg = "End of document reached.\n" "Continue from the beginning?"; - parent_window = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view))); - dialog = gtk_message_dialog_new (parent_window, GTK_DIALOG_MODAL, + dialog = gtk_message_dialog_new (get_parent_window (widget), GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, msg); gtk_dialog_add_buttons (GTK_DIALOG (dialog), @@ -649,31 +660,34 @@ moo_text_search_from_beginning_dialog (MooTextView *view, void -moo_text_regex_error_dialog (MooTextView *view, - GError *err) +moo_text_regex_error_dialog (GtkWidget *parent, + GError *error) { - GtkWindow *parent_window; GtkWidget *dialog; char *msg_text = NULL; - g_return_if_fail (MOO_IS_TEXT_VIEW (view)); - - if (err) { - if (err->domain != EGG_REGEX_ERROR) + if (error) + { + if (error->domain != EGG_REGEX_ERROR) + { g_warning ("%s: unknown error domain", G_STRLOC); - else if (err->code != EGG_REGEX_ERROR_COMPILE && - err->code != EGG_REGEX_ERROR_OPTIMIZE && - err->code != EGG_REGEX_ERROR_REPLACE) - g_warning ("%s: unknown error code", G_STRLOC); + } + else if (error->code != EGG_REGEX_ERROR_COMPILE && + error->code != EGG_REGEX_ERROR_OPTIMIZE && + error->code != EGG_REGEX_ERROR_REPLACE) + { + g_warning ("%s: unknown error code", G_STRLOC); + } - msg_text = g_strdup (err->message); + msg_text = g_strdup (error->message); + } + else + { + msg_text = g_strdup_printf ("Invalid regular expression"); } - if (!msg_text) - msg_text = g_strdup_printf ("Invalid regular expression"); - - parent_window = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view))); - dialog = gtk_message_dialog_new (parent_window, GTK_DIALOG_MODAL, + dialog = gtk_message_dialog_new (get_parent_window (parent), + GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_NONE, msg_text); gtk_dialog_add_buttons (GTK_DIALOG (dialog), GTK_STOCK_CLOSE, @@ -688,15 +702,12 @@ moo_text_regex_error_dialog (MooTextView *view, void -moo_text_replaced_n_dialog (MooTextView *view, - guint n) +moo_text_replaced_n_dialog (GtkWidget *parent, + guint n) { - GtkWindow *parent_window; GtkWidget *dialog; char *msg_text; - g_return_if_fail (MOO_IS_TEXT_VIEW (view)); - if (!n) msg_text = g_strdup_printf ("No replacement made"); else if (n == 1) @@ -704,8 +715,8 @@ moo_text_replaced_n_dialog (MooTextView *view, else msg_text = g_strdup_printf ("%d replacements made", n); - parent_window = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view))); - dialog = gtk_message_dialog_new (parent_window, GTK_DIALOG_MODAL, + dialog = gtk_message_dialog_new (get_parent_window (parent), + GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_NONE, msg_text); gtk_dialog_add_buttons (GTK_DIALOG (dialog), GTK_STOCK_CLOSE, @@ -720,22 +731,17 @@ moo_text_replaced_n_dialog (MooTextView *view, GtkWidget* -moo_text_prompt_on_replace_dialog (MooTextView *view) +moo_text_prompt_on_replace_dialog (GtkWidget *parent) { GtkWidget *dialog; - GtkWindow *parent_window; MooGladeXML *xml; - g_return_val_if_fail (MOO_IS_TEXT_VIEW (view), NULL); - - xml = moo_glade_xml_new_from_buf (MOO_EDIT_FIND_GLADE_UI, -1, + xml = moo_glade_xml_new_from_buf (MOO_TEXT_FIND_GLADE_UI, -1, "prompt_on_replace_dialog", NULL); dialog = moo_glade_xml_get_widget (xml, "prompt_on_replace_dialog"); moo_glade_xml_unref (xml); - parent_window = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view))); - - gtk_window_set_transient_for (GTK_WINDOW (dialog), parent_window); + gtk_window_set_transient_for (GTK_WINDOW (dialog), get_parent_window (parent)); return dialog; } diff --git a/moo/mooedit/mooeditdialogs.h b/moo/mooedit/mooeditdialogs.h index 1bd8aca0..c932f7ab 100644 --- a/moo/mooedit/mooeditdialogs.h +++ b/moo/mooedit/mooeditdialogs.h @@ -54,17 +54,19 @@ void moo_edit_reload_error_dialog (GtkWidget *widget, const char *err_msg); -void moo_text_nothing_found_dialog (MooTextView *view, +void moo_text_nothing_found_dialog (GtkWidget *parent, const char *text, gboolean regex); -gboolean moo_text_search_from_beginning_dialog - (MooTextView *view, +gboolean moo_text_search_from_start_dialog (GtkWidget *parent, gboolean backwards); -void moo_text_regex_error_dialog (MooTextView *view, +void moo_text_regex_error_dialog (GtkWidget *parent, GError *error); -void moo_text_replaced_n_dialog (MooTextView *view, + +gboolean moo_text_replace_from_start_dialog (GtkWidget *parent, + int replaced); +void moo_text_replaced_n_dialog (GtkWidget *parent, guint n); -GtkWidget *moo_text_prompt_on_replace_dialog (MooTextView *view); +GtkWidget *moo_text_prompt_on_replace_dialog (GtkWidget *parent); G_END_DECLS diff --git a/moo/mooedit/mooeditfind.c b/moo/mooedit/mooeditfind.c deleted file mode 100644 index 8dacf827..00000000 --- a/moo/mooedit/mooeditfind.c +++ /dev/null @@ -1,923 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4; coding: utf-8 -*- - * - * mooeditfind.c - * - * Copyright (C) 2004-2005 by Yevgen Muntyan - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * See COPYING file that comes with this distribution. - */ - -#define MOOEDIT_COMPILATION -#include "mooedit/mootextview-private.h" -#include "mooedit/mooeditsearch.h" -#include "mooedit/mooeditprefs.h" -#include "mooedit/mooeditdialogs.h" -#include "mooedit/mooeditgotoline-glade.h" -#include "mooedit/mooeditfind-glade.h" -#include "mooutils/moohistoryentry.h" -#include "mooutils/moocompat.h" -#include "mooutils/mooglade.h" - - -static void -scroll_to_mark (MooTextView *view, - GtkTextMark *mark) -{ - gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (view), mark, 0.2, FALSE, 0, 0); -} - - -/****************************************************************************/ -/* Go to line - */ - -static void update_spin_value (GtkRange *scale, - GtkSpinButton *spin); -static gboolean update_scale_value (GtkSpinButton *spin, - GtkRange *scale); - -static void update_spin_value (GtkRange *scale, - GtkSpinButton *spin) -{ - double value = gtk_range_get_value (scale); - g_signal_handlers_block_matched (spin, G_SIGNAL_MATCH_FUNC, 0, 0, 0, - (gpointer)update_scale_value, 0); - gtk_spin_button_set_value (spin, value); - g_signal_handlers_unblock_matched (spin, G_SIGNAL_MATCH_FUNC, 0, 0, 0, - (gpointer)update_scale_value, 0); -} - -static gboolean update_scale_value (GtkSpinButton *spin, - GtkRange *scale) -{ - double value = gtk_spin_button_get_value (spin); - g_signal_handlers_block_matched (scale, G_SIGNAL_MATCH_FUNC, 0, 0, 0, - (gpointer)update_spin_value, 0); - gtk_range_set_value (scale, value); - g_signal_handlers_unblock_matched (scale, G_SIGNAL_MATCH_FUNC, 0, 0, 0, - (gpointer)update_spin_value, 0); - return FALSE; -} - - -void -moo_text_view_goto_line (MooTextView *view, - int line) -{ - GtkWidget *dialog; - GtkTextBuffer *buffer; - int line_count; - GtkTextIter iter; - GtkRange *scale; - GtkSpinButton *spin; - - g_return_if_fail (MOO_IS_TEXT_VIEW (view)); - - buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); - line_count = gtk_text_buffer_get_line_count (buffer); - - if (line < 0 || line >= line_count) - { - MooGladeXML *xml; - - xml = moo_glade_xml_new_from_buf (MOO_EDIT_GOTO_LINE_GLADE_UI, - -1, NULL, NULL); - g_return_if_fail (xml != NULL); - - dialog = moo_glade_xml_get_widget (xml, "dialog"); - -#if GTK_CHECK_VERSION(2,6,0) - gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), - GTK_RESPONSE_OK, - GTK_RESPONSE_CANCEL, - -1); -#endif /* GTK_CHECK_VERSION(2,6,0) */ - - gtk_text_buffer_get_iter_at_mark (buffer, &iter, gtk_text_buffer_get_insert (buffer)); - line = gtk_text_iter_get_line (&iter); - - scale = moo_glade_xml_get_widget (xml, "scale"); - gtk_range_set_range (scale, 1, line_count + 1); - gtk_range_set_value (scale, line + 1); - - spin = moo_glade_xml_get_widget (xml, "spin"); - gtk_entry_set_activates_default (GTK_ENTRY (spin), TRUE); - gtk_spin_button_set_range (spin, 1, line_count); - gtk_spin_button_set_value (spin, line + 1); - gtk_editable_select_region (GTK_EDITABLE (spin), 0, -1); - - g_signal_connect (scale, "value-changed", G_CALLBACK (update_spin_value), spin); - g_signal_connect (spin, "value-changed", G_CALLBACK (update_scale_value), scale); - - gtk_window_set_transient_for (GTK_WINDOW (dialog), - GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view)))); - - moo_glade_xml_unref (xml); - - if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_OK) - { - gtk_widget_destroy (dialog); - return; - } - - line = (int)gtk_spin_button_get_value (spin) - 1; - gtk_widget_destroy (dialog); - } - - gtk_text_buffer_get_iter_at_line (buffer, &iter, line); - gtk_text_buffer_place_cursor (buffer, &iter); - scroll_to_mark (view, gtk_text_buffer_get_insert (buffer)); -} - - -/****************************************************************************/ -/* Search and replace - */ - -static void set (GtkWidget *dialog, - gboolean regex, - gboolean case_sensitive, - gboolean whole_words, - gboolean from_cursor, - gboolean backwards, - gboolean selected, - gboolean dont_prompt_on_replace); -static void get (GtkWidget *dialog, - gboolean *regex, - gboolean *case_sensitive, - gboolean *whole_words, - gboolean *from_cursor, - gboolean *backwards, - gboolean *selected, - gboolean *dont_prompt_on_replace); -static const char *get_text (GtkWidget *dialog); -static const char *get_replace_with (GtkWidget *dialog); -static void set_text (GtkWidget *dialog, - const char *text); -static void set_replace_with (GtkWidget *dialog, - const char *text); - -static GtkWidget *create_find_dialog (gboolean replace); - - -typedef struct { - MooTextView *view; - GtkWidget *dialog; - MooTextReplaceResponseType response; -} PromptFuncData; - -static MooTextReplaceResponseType prompt_on_replace_func - (const char *text, - EggRegex *regex, - const char *replacement, - GtkTextIter *to_replace_start, - GtkTextIter *to_replace_end, - gpointer data); - -static GtkWidget* -create_find_dialog (gboolean replace) -{ - MooGladeXML *xml; - GtkWidget *dialog, *replace_frame, *dont_prompt_on_replace; - GtkButton *ok_btn; - MooHistoryEntry *text_to_find, *replacement_text; - - xml = moo_glade_xml_new_empty (); - moo_glade_xml_map_id (xml, "text_to_find", MOO_TYPE_HISTORY_ENTRY); - moo_glade_xml_map_id (xml, "replacement_text", MOO_TYPE_HISTORY_ENTRY); - moo_glade_xml_parse_memory (xml, MOO_EDIT_FIND_GLADE_UI, -1, "dialog"); - - dialog = moo_glade_xml_get_widget (xml, "dialog"); - g_return_val_if_fail (dialog != NULL, NULL); - - g_object_set_data_full (G_OBJECT (dialog), "moo-dialog-xml", - xml, (GDestroyNotify) moo_glade_xml_unref); - - replace_frame = moo_glade_xml_get_widget (xml, "replace_frame"); - dont_prompt_on_replace = moo_glade_xml_get_widget (xml, "dont_prompt_on_replace"); - ok_btn = moo_glade_xml_get_widget (xml, "ok_btn"); - - text_to_find = moo_glade_xml_get_widget (xml, "text_to_find"); - replacement_text = moo_glade_xml_get_widget (xml, "replacement_text"); - moo_history_entry_set_list (text_to_find, _moo_text_search_params->text_to_find_history); - moo_history_entry_set_list (replacement_text, _moo_text_search_params->replacement_history); - - if (replace) - { - gtk_window_set_title (GTK_WINDOW (dialog), "Replace"); - gtk_widget_show (replace_frame); - gtk_widget_show (dont_prompt_on_replace); - gtk_button_set_label (ok_btn, GTK_STOCK_FIND_AND_REPLACE); - } - else - { - gtk_window_set_title (GTK_WINDOW (dialog), "Find"); - gtk_widget_hide (replace_frame); - gtk_widget_hide (dont_prompt_on_replace); - gtk_button_set_label (ok_btn, GTK_STOCK_FIND); - } - - return dialog; -} - - -void -_moo_text_view_find (MooTextView *view) -{ - GtkWidget *dialog; - gboolean regex, case_sensitive, whole_words, from_cursor, backwards, selected; - GtkTextIter sel_start, sel_end; - int response; - MooTextSearchOptions options; - const char *text; - GtkTextMark *insert; - GtkTextBuffer *buffer; - - g_return_if_fail (MOO_IS_TEXT_VIEW (view)); - - buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); - - regex = _moo_text_search_params->regex; - case_sensitive = _moo_text_search_params->case_sensitive; - backwards = _moo_text_search_params->backwards; - whole_words = _moo_text_search_params->whole_words; - from_cursor = _moo_text_search_params->from_cursor; - - selected = FALSE; - if (moo_prefs_get_bool (moo_edit_setting (MOO_EDIT_PREFS_SEARCH_SELECTED)) && - gtk_text_buffer_get_selection_bounds (buffer, &sel_start, &sel_end) && - ABS (gtk_text_iter_get_line (&sel_start) - gtk_text_iter_get_line (&sel_end) > 1)) - selected = TRUE; - - dialog = create_find_dialog (FALSE); - set (dialog, regex, case_sensitive, whole_words, - from_cursor, backwards, selected, FALSE); - - if (gtk_text_buffer_get_selection_bounds (buffer, &sel_start, &sel_end) && - gtk_text_iter_get_line (&sel_start) == gtk_text_iter_get_line (&sel_end)) - { - char *selection = gtk_text_buffer_get_text (buffer, &sel_start, &sel_end, TRUE); - set_text (dialog, selection); - g_free (selection); - } - else if (_moo_text_search_params->text) - { - set_text (dialog, _moo_text_search_params->text); - } - - gtk_window_set_transient_for (GTK_WINDOW (dialog), - GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view)))); - response = gtk_dialog_run (GTK_DIALOG (dialog)); - - if (response != GTK_RESPONSE_OK) - { - gtk_widget_destroy (dialog); - return; - } - - get (dialog, ®ex, &case_sensitive, &whole_words, - &from_cursor, &backwards, &selected, NULL); - - if (selected) - { - g_warning ("%s: searching in selected not imlemented\n", G_STRLOC); - gtk_widget_destroy (dialog); - return; - } - - _moo_text_search_params->regex = regex; - _moo_text_search_params->case_sensitive = case_sensitive; - _moo_text_search_params->backwards = backwards; - _moo_text_search_params->whole_words = whole_words; - _moo_text_search_params->from_cursor = from_cursor; - - _moo_text_search_params->last_search_stamp++; - view->priv->last_search_stamp = _moo_text_search_params->last_search_stamp; - - g_free (_moo_text_search_params->text); - _moo_text_search_params->text = g_strdup (get_text (dialog)); - text = _moo_text_search_params->text; - gtk_widget_destroy (dialog); - - if (text && text[0]) - moo_history_list_add (_moo_text_search_params->text_to_find_history, text); - - options = 0; - - if (regex) - options |= MOO_TEXT_SEARCH_REGEX; - if (backwards) - options |= MOO_TEXT_SEARCH_BACKWARDS; - if (!case_sensitive) - options |= MOO_TEXT_SEARCH_CASE_INSENSITIVE; - - insert = gtk_text_buffer_get_insert (buffer); - - { - GtkTextIter start, search_start, limit, match_start, match_end; - gboolean result; - GError *err = NULL; - - if (from_cursor) { - gtk_text_buffer_get_iter_at_mark (buffer, &start, insert); - } - else { - if (backwards) - gtk_text_buffer_get_end_iter (buffer, &start); - else - gtk_text_buffer_get_start_iter (buffer, &start); - } - - search_start = start; - - if (backwards) - gtk_text_buffer_get_start_iter (buffer, &limit); - else - gtk_text_buffer_get_end_iter (buffer, &limit); - - result = moo_text_search (&start, &limit, text, - &match_start, &match_end, options, - &err); - if (!result) - { - if (err) { - moo_text_regex_error_dialog (view, err); - g_error_free (err); - return; - } - - if (!from_cursor) { - moo_text_nothing_found_dialog (view, text, regex); - return; - } - if ((backwards && gtk_text_iter_is_end (&start)) || - (!backwards && gtk_text_iter_is_start (&start))) - { - moo_text_nothing_found_dialog (view, text, regex); - return; - } - - if (!moo_text_search_from_beginning_dialog (view, backwards)) - { - return; - } - - if (backwards) - gtk_text_buffer_get_end_iter (buffer, &start); - else - gtk_text_buffer_get_start_iter (buffer, &start); - - limit = search_start; - - result = moo_text_search (&start, &limit, text, - &match_start, &match_end, options, - NULL); - - if (!result) { - moo_text_nothing_found_dialog (view, text, regex); - return; - } - } - - if (backwards) - gtk_text_iter_order (&match_end, &match_start); - - if (!view->priv->last_found_start) { - view->priv->last_found_start = - gtk_text_buffer_create_mark (buffer, NULL, &match_start, TRUE); - view->priv->last_found_end = - gtk_text_buffer_create_mark (buffer, NULL, &match_end, TRUE); - } - else { - gtk_text_buffer_move_mark (buffer, view->priv->last_found_start, - &match_start); - gtk_text_buffer_move_mark (buffer, view->priv->last_found_end, - &match_end); - } - - gtk_text_buffer_select_range (buffer, &match_end, &match_start); - scroll_to_mark (view, insert); - } -} - - -void -_moo_text_view_find_next (MooTextView *view) -{ - gboolean regex, case_sensitive, whole_words, backwards; - MooTextSearchOptions options; - const char *text; - GtkTextMark *insert; - GtkTextBuffer *buffer; - - g_return_if_fail (MOO_IS_TEXT_VIEW (view)); - - buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); - - if (_moo_text_search_params->last_search_stamp < 0 || - !_moo_text_search_params->text) - return moo_text_view_find_interactive (view); - - regex = _moo_text_search_params->regex; - case_sensitive = _moo_text_search_params->case_sensitive; - backwards = _moo_text_search_params->backwards; - whole_words = _moo_text_search_params->whole_words; - - view->priv->last_search_stamp = _moo_text_search_params->last_search_stamp; - - text = _moo_text_search_params->text; - - options = 0; - if (regex) options |= MOO_TEXT_SEARCH_REGEX; - if (backwards) options |= MOO_TEXT_SEARCH_BACKWARDS; - if (!case_sensitive) options |= MOO_TEXT_SEARCH_CASE_INSENSITIVE; - - insert = gtk_text_buffer_get_insert (buffer); - - { - GtkTextIter start, search_start, match_start, match_end, limit; - gboolean result; - - gtk_text_buffer_get_iter_at_mark (buffer, &start, insert); - search_start = start; - - if (backwards) - gtk_text_buffer_get_start_iter (buffer, &limit); - else - gtk_text_buffer_get_end_iter (buffer, &limit); - - result = moo_text_search (&start, &limit, text, - &match_start, &match_end, options, - NULL); - if (!result) - { - if ((backwards && gtk_text_iter_is_end (&start)) || - (!backwards && gtk_text_iter_is_start (&start))) - { - moo_text_nothing_found_dialog (view, text, regex); - return; - } - - if (!moo_text_search_from_beginning_dialog (view, backwards)) - { - return; - } - - if (backwards) - gtk_text_buffer_get_end_iter (buffer, &start); - else - gtk_text_buffer_get_start_iter (buffer, &start); - - limit = search_start; - - result = moo_text_search (&start, &search_start, text, - &match_start, &match_end, options, - NULL); - - if (!result) { - moo_text_nothing_found_dialog (view, text, regex); - return; - } - } - - if (backwards) - gtk_text_iter_order (&match_end, &match_start); - - if (!view->priv->last_found_start) { - view->priv->last_found_start = - gtk_text_buffer_create_mark (buffer, NULL, &match_start, TRUE); - view->priv->last_found_end = - gtk_text_buffer_create_mark (buffer, NULL, &match_end, TRUE); - } - else { - gtk_text_buffer_move_mark (buffer, view->priv->last_found_start, - &match_start); - gtk_text_buffer_move_mark (buffer, view->priv->last_found_end, - &match_end); - } - - gtk_text_buffer_select_range (buffer, &match_end, &match_start); - scroll_to_mark (view, insert); - } -} - - -void -_moo_text_view_find_previous (MooTextView *view) -{ - gboolean regex, case_sensitive, whole_words, backwards; - MooTextSearchOptions options; - const char *text; - GtkTextMark *insert; - GtkTextBuffer *buffer; - - g_return_if_fail (MOO_IS_TEXT_VIEW (view)); - - buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); - - if (_moo_text_search_params->last_search_stamp < 0 || - !_moo_text_search_params->text) - return moo_text_view_find_interactive (view); - - regex = _moo_text_search_params->regex; - case_sensitive = _moo_text_search_params->case_sensitive; - backwards = _moo_text_search_params->backwards; - whole_words = _moo_text_search_params->whole_words; - - view->priv->last_search_stamp = _moo_text_search_params->last_search_stamp; - - text = _moo_text_search_params->text; - - options = 0; - if (regex) options |= MOO_TEXT_SEARCH_REGEX; - if (!backwards) options |= MOO_TEXT_SEARCH_BACKWARDS; - if (!case_sensitive) options |= MOO_TEXT_SEARCH_CASE_INSENSITIVE; - - insert = gtk_text_buffer_get_insert (buffer); - - { - GtkTextIter start, search_start, match_start, match_end, limit; - gboolean result; - - gtk_text_buffer_get_iter_at_mark (buffer, &start, insert); - search_start = start; - - if (!backwards) - gtk_text_buffer_get_start_iter (buffer, &limit); - else - gtk_text_buffer_get_end_iter (buffer, &limit); - - result = moo_text_search (&start, &limit, text, - &match_start, &match_end, options, - NULL); - - if (!result) - { - if ((!backwards && gtk_text_iter_is_end (&start)) || - (backwards && gtk_text_iter_is_start (&start))) - { - moo_text_nothing_found_dialog (view, text, regex); - return; - } - - if (!moo_text_search_from_beginning_dialog (view, !backwards)) - { - return; - } - - if (!backwards) - gtk_text_buffer_get_end_iter (buffer, &start); - else - gtk_text_buffer_get_start_iter (buffer, &start); - - limit = search_start; - - result = moo_text_search (&start, &search_start, text, - &match_start, &match_end, options, - NULL); - - if (!result) { - moo_text_nothing_found_dialog (view, text, regex); - return; - } - } - - if (!backwards) - gtk_text_iter_order (&match_end, &match_start); - - if (!view->priv->last_found_start) { - view->priv->last_found_start = - gtk_text_buffer_create_mark (buffer, NULL, &match_start, TRUE); - view->priv->last_found_end = - gtk_text_buffer_create_mark (buffer, NULL, &match_end, TRUE); - } - else { - gtk_text_buffer_move_mark (buffer, view->priv->last_found_start, - &match_start); - gtk_text_buffer_move_mark (buffer, view->priv->last_found_end, - &match_end); - } - - gtk_text_buffer_select_range (buffer, &match_end, &match_start); - scroll_to_mark (view, insert); - } -} - - -void -_moo_text_view_replace (MooTextView *view) -{ - GtkWidget *dialog; - gboolean regex, case_sensitive, whole_words, from_cursor, - backwards, selected, dont_prompt_on_replace; - GtkTextIter sel_start, sel_end; - MooTextSearchOptions options; - const char *text, *replace_with; - GtkTextMark *insert; - GtkTextBuffer *buffer; - - g_return_if_fail (MOO_IS_TEXT_VIEW (view)); - - buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); - - regex = _moo_text_search_params->regex; - case_sensitive = _moo_text_search_params->case_sensitive; - backwards = _moo_text_search_params->backwards; - whole_words = _moo_text_search_params->whole_words; - from_cursor = _moo_text_search_params->from_cursor; - dont_prompt_on_replace = _moo_text_search_params->dont_prompt_on_replace; - - selected = FALSE; - if (moo_prefs_get_bool (moo_edit_setting (MOO_EDIT_PREFS_SEARCH_SELECTED)) && - gtk_text_buffer_get_selection_bounds (buffer, &sel_start, &sel_end) && - ABS (gtk_text_iter_get_line (&sel_start) - gtk_text_iter_get_line (&sel_end) > 1)) - selected = TRUE; - - dialog = create_find_dialog (TRUE); - set (dialog, regex, case_sensitive, whole_words, - from_cursor, backwards, selected, dont_prompt_on_replace); - - if (gtk_text_buffer_get_selection_bounds (buffer, &sel_start, &sel_end) && - gtk_text_iter_get_line (&sel_start) == gtk_text_iter_get_line (&sel_end)) - { - char *selection = gtk_text_buffer_get_text (buffer, &sel_start, &sel_end, TRUE); - set_text (dialog, selection); - g_free (selection); - } - else if (_moo_text_search_params->text) - { - set_text (dialog, _moo_text_search_params->text); - } - - if (_moo_text_search_params->replace_with) - set_replace_with (dialog, _moo_text_search_params->replace_with); - - gtk_window_set_transient_for (GTK_WINDOW (dialog), - GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view)))); - - if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_OK) - { - gtk_widget_destroy (dialog); - return; - } - - get (dialog, ®ex, &case_sensitive, &whole_words, - &from_cursor, &backwards, &selected, &dont_prompt_on_replace); - - if (selected) - { - g_warning ("%s: searching in selected not imlemented\n", G_STRLOC); - return; - } - - _moo_text_search_params->regex = regex; - _moo_text_search_params->case_sensitive = case_sensitive; - _moo_text_search_params->backwards = backwards; - _moo_text_search_params->whole_words = whole_words; - _moo_text_search_params->from_cursor = from_cursor; - _moo_text_search_params->dont_prompt_on_replace = dont_prompt_on_replace; - - _moo_text_search_params->last_search_stamp++; - view->priv->last_search_stamp = _moo_text_search_params->last_search_stamp; - - g_free (_moo_text_search_params->text); - _moo_text_search_params->text = g_strdup (get_text (dialog)); - text = _moo_text_search_params->text; - g_free (_moo_text_search_params->replace_with); - _moo_text_search_params->replace_with = g_strdup (get_replace_with (dialog)); - replace_with = _moo_text_search_params->replace_with; - gtk_widget_destroy (dialog); - - if (text && text[0]) - moo_history_list_add (_moo_text_search_params->text_to_find_history, text); - if (replace_with && replace_with[0]) - moo_history_list_add (_moo_text_search_params->replacement_history, replace_with); - - options = 0; - if (regex) options |= MOO_TEXT_SEARCH_REGEX; - if (backwards) options |= MOO_TEXT_SEARCH_BACKWARDS; - if (!case_sensitive) options |= MOO_TEXT_SEARCH_CASE_INSENSITIVE; - - insert = gtk_text_buffer_get_insert (buffer); - - G_STMT_START { - GtkTextIter start, limit; - gboolean result; - GError *err = NULL; - PromptFuncData data = {view, NULL, MOO_TEXT_REPLACE}; - - if (from_cursor) { - gtk_text_buffer_get_iter_at_mark (buffer, &start, insert); - } - else { - if (backwards) - gtk_text_buffer_get_end_iter (buffer, &start); - else - gtk_text_buffer_get_start_iter (buffer, &start); - } - - if (backwards) - gtk_text_buffer_get_start_iter (buffer, &limit); - else - gtk_text_buffer_get_end_iter (buffer, &limit); - - if (!dont_prompt_on_replace) { - result = moo_text_replace_all_interactive (&start, &limit, text, - replace_with, - options, &err, - prompt_on_replace_func, - &data); - } - else - result = moo_text_replace_all_interactive (&start, &limit, text, - replace_with, options, - &err, moo_text_replace_func_replace_all, - NULL); - - g_return_if_fail (result != MOO_TEXT_REPLACE_INVALID_ARGS); - - if (result == MOO_TEXT_REPLACE_REGEX_ERROR || err) { - moo_text_regex_error_dialog (view, err); - if (err) g_error_free (err); - return; - } - - if (!from_cursor || data.response == MOO_TEXT_REPLACE_STOP) { - if (data.dialog) gtk_widget_destroy (data.dialog); - moo_text_replaced_n_dialog (view, result); - return; - } - - if ((backwards && gtk_text_iter_is_end (&start)) || - (!backwards && gtk_text_iter_is_start (&start))) - { - if (data.dialog) gtk_widget_destroy (data.dialog); - moo_text_replaced_n_dialog (view, result); - return; - } - - if (!moo_text_search_from_beginning_dialog (view, backwards)) - { - if (data.dialog) gtk_widget_destroy (data.dialog); - moo_text_replaced_n_dialog (view, result); - return; - } - - { - int result2; - - if (backwards) - gtk_text_buffer_get_end_iter (buffer, &start); - else - gtk_text_buffer_get_start_iter (buffer, &start); - - gtk_text_buffer_get_iter_at_mark (buffer, &limit, insert); - - if (!dont_prompt_on_replace) - result2 = moo_text_replace_all_interactive (&start, &limit, text, - replace_with, - options, &err, - prompt_on_replace_func, - &data); - else - result2 = moo_text_replace_all_interactive (&start, &limit, text, - replace_with, options, &err, - moo_text_replace_func_replace_all, - NULL); - - g_return_if_fail (result2 >= 0); - - if (data.dialog) gtk_widget_destroy (data.dialog); - moo_text_replaced_n_dialog (view, result + result2); - } - } G_STMT_END; -} - - -static MooTextReplaceResponseType prompt_on_replace_func - (G_GNUC_UNUSED const char *text, - G_GNUC_UNUSED EggRegex *regex, - G_GNUC_UNUSED const char *replacement, - GtkTextIter *to_replace_start, - GtkTextIter *to_replace_end, - gpointer d) -{ - PromptFuncData *data = (PromptFuncData*) d; - GtkTextBuffer *buffer; - int response; - - buffer = gtk_text_iter_get_buffer (to_replace_end); - gtk_text_buffer_select_range (buffer, to_replace_end, to_replace_start); - scroll_to_mark (data->view, gtk_text_buffer_get_insert (buffer)); - - if (!data->dialog) - data->dialog = moo_text_prompt_on_replace_dialog (data->view); - - response = gtk_dialog_run (GTK_DIALOG (data->dialog)); - - if (response == GTK_RESPONSE_DELETE_EVENT || - response == GTK_RESPONSE_CANCEL) - data->response = MOO_TEXT_REPLACE_STOP; - else - data->response = (MooTextReplaceResponseType)response; - - return data->response; -} - - -#define GET_WIDGET(name) \ - GtkWidget *name = moo_glade_xml_get_widget (xml, #name); -#define GET_TOGGLE_BUTTON(name) \ - GtkToggleButton *name = moo_glade_xml_get_widget (xml, #name); - -static void set (GtkWidget *dialog, - gboolean regex, - gboolean casesensitive, - gboolean whole_words, - gboolean fromcursor, - gboolean backwards, - gboolean selected, - gboolean dontpromptonreplace) -{ - MooGladeXML *xml = g_object_get_data (G_OBJECT (dialog), "moo-dialog-xml"); - - GET_TOGGLE_BUTTON (regular_expression); - GET_TOGGLE_BUTTON (case_sensitive); - GET_TOGGLE_BUTTON (whole_words_only); - GET_TOGGLE_BUTTON (from_cursor); - GET_TOGGLE_BUTTON (find_backwards); - GET_TOGGLE_BUTTON (selected_text); - GET_TOGGLE_BUTTON (dont_prompt_on_replace); - - gtk_toggle_button_set_active (regular_expression, regex); - gtk_toggle_button_set_active (case_sensitive, casesensitive); - gtk_toggle_button_set_active (whole_words_only, whole_words); - gtk_toggle_button_set_active (from_cursor, fromcursor); - gtk_toggle_button_set_active (find_backwards, backwards); - gtk_toggle_button_set_active (selected_text, selected); - gtk_toggle_button_set_active (dont_prompt_on_replace, dontpromptonreplace); -} - -static void get (GtkWidget *dialog, - gboolean *regex, - gboolean *casesensitive, - gboolean *whole_words, - gboolean *fromcursor, - gboolean *backwards, - gboolean *selected, - gboolean *dontpromptonreplace) -{ - MooGladeXML *xml = g_object_get_data (G_OBJECT (dialog), "moo-dialog-xml"); - - GET_TOGGLE_BUTTON (regular_expression); - GET_TOGGLE_BUTTON (case_sensitive); - GET_TOGGLE_BUTTON (whole_words_only); - GET_TOGGLE_BUTTON (from_cursor); - GET_TOGGLE_BUTTON (find_backwards); - GET_TOGGLE_BUTTON (selected_text); - GET_TOGGLE_BUTTON (dont_prompt_on_replace); - - if (regex) *regex = gtk_toggle_button_get_active (regular_expression); - if (casesensitive) *casesensitive = gtk_toggle_button_get_active (case_sensitive); - if (whole_words) *whole_words = gtk_toggle_button_get_active (whole_words_only); - if (fromcursor) *fromcursor = gtk_toggle_button_get_active (from_cursor); - if (backwards) *backwards = gtk_toggle_button_get_active (find_backwards); - if (selected) *selected = gtk_toggle_button_get_active (selected_text); - if (dontpromptonreplace) *dontpromptonreplace = gtk_toggle_button_get_active (dont_prompt_on_replace); -} - -static void set_text (GtkWidget *dialog, - const char *text) -{ - MooGladeXML *xml = g_object_get_data (G_OBJECT (dialog), "moo-dialog-xml"); - GET_WIDGET (text_to_find); - moo_combo_entry_set_text (MOO_COMBO (text_to_find), text); - moo_combo_select_region (MOO_COMBO (text_to_find), 0, -1); -} - -static void set_replace_with (GtkWidget *dialog, - const char *text) -{ - MooGladeXML *xml = g_object_get_data (G_OBJECT (dialog), "moo-dialog-xml"); - GET_WIDGET (replacement_text); - moo_combo_entry_set_text (MOO_COMBO (replacement_text), text); -} - -static const char *get_text (GtkWidget *dialog) -{ - MooGladeXML *xml = g_object_get_data (G_OBJECT (dialog), "moo-dialog-xml"); - GET_WIDGET (text_to_find); - return moo_combo_entry_get_text (MOO_COMBO (text_to_find)); -} - -static const char *get_replace_with (GtkWidget *dialog) -{ - MooGladeXML *xml = g_object_get_data (G_OBJECT (dialog), "moo-dialog-xml"); - GET_WIDGET (replacement_text); - return moo_combo_entry_get_text (MOO_COMBO (replacement_text)); -} diff --git a/moo/mooedit/mooeditsearch.c b/moo/mooedit/mooeditsearch.c deleted file mode 100644 index d7cf87b8..00000000 --- a/moo/mooedit/mooeditsearch.c +++ /dev/null @@ -1,1124 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4; coding: utf-8 -*- - * - * mooeditsearch.c - * - * Copyright (C) 2004-2005 by Yevgen Muntyan - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * See COPYING file that comes with this distribution. - */ - -#define MOOEDIT_COMPILATION -#include "mooedit/mooedit-private.h" -#include "mooedit/mootextiter.h" -#include "mooedit/gtksourceiter.h" -#include - - -GType -moo_text_search_options_get_type (void) -{ - static GType type = 0; - - if (!type) - { - static const GFlagsValue values[] = { - { MOO_TEXT_SEARCH_BACKWARDS, (char*)"MOO_TEXT_SEARCH_BACKWARDS", (char*)"backwards" }, - { MOO_TEXT_SEARCH_CASE_INSENSITIVE, (char*)"MOO_TEXT_SEARCH_CASE_INSENSITIVE", (char*)"case-insensitive" }, - { MOO_TEXT_SEARCH_REGEX, (char*)"MOO_TEXT_SEARCH_REGEX", (char*)"regex" }, - { 0, NULL, NULL } - }; - - type = g_flags_register_static ("MooTextSearchOptions", values); - } - - return type; -} - -GType -moo_text_replace_response_type_get_type (void) -{ - static GType type = 0; - - if (!type) - { - static const GEnumValue values[] = { - { MOO_TEXT_REPLACE_STOP, (char*)"MOO_TEXT_REPLACE_STOP", (char*)"stop" }, - { MOO_TEXT_REPLACE_AND_STOP, (char*)"MOO_TEXT_REPLACE_AND_STOP", (char*)"replace-and-stop" }, - { MOO_TEXT_REPLACE_SKIP, (char*)"MOO_TEXT_REPLACE_SKIP", (char*)"skip" }, - { MOO_TEXT_REPLACE, (char*)"MOO_TEXT_REPLACE", (char*)"replace" }, - { MOO_TEXT_REPLACE_ALL, (char*)"MOO_TEXT_REPLACE_ALL", (char*)"all" }, - { 0, NULL, NULL } - }; - type = g_enum_register_static ("MooTextReplaceResponseType", values); - } - - return type; -} - - -#define NUM_LINES_FOR_REGEX_SEARCH 1 - -static EggRegex *last_regex = NULL; -static MooTextSearchOptions last_options = 0; - - -static gboolean moo_text_search_regex_forward (const GtkTextIter *start, - const GtkTextIter *end, - EggRegex *regex, - GtkTextIter *match_start, - GtkTextIter *match_end); -/* assumes start < end */ -static gboolean moo_text_search_regex_backward (const GtkTextIter *start, - const GtkTextIter *end, - EggRegex *regex, - GtkTextIter *match_start, - GtkTextIter *match_end); -static gboolean moo_text_match_regex_forward (const GtkTextIter *start, - const GtkTextIter *end, - EggRegex *regex, - GtkTextIter *match_start, - GtkTextIter *match_end); -/* assumes start < end */ -static gboolean moo_text_match_regex_backward (const GtkTextIter *start, - const GtkTextIter *end, - EggRegex *regex, - GtkTextIter *match_start, - GtkTextIter *match_end); - - -gboolean moo_text_search (const GtkTextIter *start, - const GtkTextIter *limit, - const char *text, - GtkTextIter *match_start, - GtkTextIter *match_end, - MooTextSearchOptions options, - GError **error) -{ - GtkSourceSearchFlags flags; - gboolean backwards = MOO_TEXT_SEARCH_BACKWARDS & options; - - g_return_val_if_fail (text != NULL, FALSE); - g_return_val_if_fail (start != NULL, FALSE); - - if (error) *error = NULL; - - if (MOO_TEXT_SEARCH_REGEX & options) - { - EggRegex *regex = last_regex; - - if (!last_regex || last_options != options || - strcmp (egg_regex_get_pattern (last_regex), text)) - { - GError *err = NULL; - EggRegexCompileFlags flags; - - if (last_regex) egg_regex_unref (last_regex); - last_regex = NULL; - last_options = 0; - - flags = EGG_REGEX_MULTILINE; - if (options & MOO_TEXT_SEARCH_CASE_INSENSITIVE) - flags |= EGG_REGEX_CASELESS; - - last_regex = egg_regex_new (text, flags, 0, &err); - if (err) - { - if (error) *error = err; - else g_error_free (err); - egg_regex_free (last_regex); - last_regex = NULL; - return FALSE; - } - - egg_regex_optimize (last_regex, &err); - if (err) - { - g_warning ("%s: %s", G_STRLOC, err->message); - g_error_free (err); - } - - last_options = options; - - regex = last_regex; - } - - return moo_text_search_regex (start, limit, regex, - match_start, match_end, - options & MOO_TEXT_SEARCH_BACKWARDS); - } - - flags = GTK_SOURCE_SEARCH_VISIBLE_ONLY | GTK_SOURCE_SEARCH_TEXT_ONLY; - if (MOO_TEXT_SEARCH_CASE_INSENSITIVE & options) - flags |= GTK_SOURCE_SEARCH_CASE_INSENSITIVE; - - if (backwards) - return gtk_source_iter_backward_search (start, text, flags, - match_start, match_end, limit); - else - return gtk_source_iter_forward_search (start, text, flags, - match_start, match_end, limit); -} - - -gboolean moo_text_search_regex (const GtkTextIter *start, - const GtkTextIter *limit, - EggRegex *regex, - GtkTextIter *match_start, - GtkTextIter *match_end, - gboolean backwards) -{ - g_return_val_if_fail (start != NULL && limit != NULL && - regex != NULL, FALSE); - - egg_regex_clear (regex); - - if (backwards) - { - GtkTextIter real_start = *limit; - GtkTextIter real_end = *start; - return moo_text_search_regex_backward (&real_start, &real_end, regex, - match_start, match_end); - } - else - { - return moo_text_search_regex_forward (start, limit, regex, - match_start, match_end); - } -} - - -static gboolean moo_text_search_regex_forward (const GtkTextIter *start, - const GtkTextIter *end, - EggRegex *regex, - GtkTextIter *match_start, - GtkTextIter *match_end) -{ - GtkTextIter current_start, current_end; - int start_line, end_line; - GtkTextBuffer *buffer; - int i; - - g_return_val_if_fail (gtk_text_iter_compare (start, end) <= 0, FALSE); - - buffer = gtk_text_iter_get_buffer (start); - - start_line = gtk_text_iter_get_line (start); - end_line = gtk_text_iter_get_line (end); - - if (end_line - start_line < NUM_LINES_FOR_REGEX_SEARCH) - return moo_text_match_regex_forward (start, end, regex, - match_start, match_end); - - gtk_text_buffer_get_iter_at_line (buffer, ¤t_end, - start_line + NUM_LINES_FOR_REGEX_SEARCH - 1); - if (!gtk_text_iter_ends_line (¤t_end)) - gtk_text_iter_forward_to_line_end (¤t_end); - if (moo_text_match_regex_forward (start, ¤t_end, regex, - match_start, match_end)) - return TRUE; - - for (i = start_line + 1; end_line - i > NUM_LINES_FOR_REGEX_SEARCH; ++i) - { - egg_regex_clear (regex); - gtk_text_buffer_get_iter_at_line (buffer, ¤t_start, i); - gtk_text_buffer_get_iter_at_line (buffer, ¤t_end, - i + NUM_LINES_FOR_REGEX_SEARCH - 1); - if (!gtk_text_iter_ends_line (¤t_end)) - gtk_text_iter_forward_to_line_end (¤t_end); - if (moo_text_match_regex_forward (¤t_start, ¤t_end, regex, - match_start, match_end)) - return TRUE; - } - - egg_regex_clear (regex); - gtk_text_buffer_get_iter_at_line (buffer, ¤t_start, - end_line - NUM_LINES_FOR_REGEX_SEARCH + 1); - return moo_text_match_regex_forward (¤t_start, end, regex, - match_start, match_end); -} - - -static gboolean moo_text_match_regex_forward (const GtkTextIter *start, - const GtkTextIter *end, - EggRegex *regex, - GtkTextIter *match_start, - GtkTextIter *match_end) -{ - GtkTextBuffer *buffer; - char *text; - int result; - int start_pos, end_pos; - long start_offset, end_offset; - EggRegexMatchFlags options = 0; - gunichar c; - - buffer = gtk_text_iter_get_buffer (start); - text = gtk_text_buffer_get_slice (buffer, start, end, TRUE); - - if (!gtk_text_iter_ends_line (end)) - { - options = EGG_REGEX_MATCH_NOTEOL; - } - if (!gtk_text_iter_starts_line (start)) - { - options |= EGG_REGEX_MATCH_NOTBOL; - } - - result = egg_regex_match (regex, text, -1, options); - - if (result <= 0) - { - g_free (text); - return FALSE; - } - - egg_regex_fetch_pos (regex, text, 0, &start_pos, &end_pos); - - c = g_utf8_get_char_validated (text + start_pos, -1); - if (c == ((gunichar)-2) || c == ((gunichar)-1)) - { - g_critical ("%s: egg_regex_fetch_pos() returned invalid " - "match start position", G_STRLOC); - g_free (text); - return FALSE; - } - c = g_utf8_get_char_validated (text + end_pos, -1); - if (c == ((gunichar)-2) || c == ((gunichar)-1)) - { - g_critical ("%s: egg_regex_fetch_pos() returned invalid " - "match end position", G_STRLOC); - g_free (text); - return FALSE; - } - - start_offset = g_utf8_pointer_to_offset (text, text + start_pos); - end_offset = g_utf8_pointer_to_offset (text + start_pos, text + end_pos); - end_offset += start_offset; - - /* TODO: why end and start are swapped? */ - - if (match_start) - { - *match_start = *start; - gtk_text_iter_forward_chars (match_start, start_offset); - if (match_end) - { - *match_end = *match_start; - gtk_text_iter_forward_chars (match_start, end_offset - start_offset); - } - - gtk_text_iter_order (match_start, match_end); - } - else - { - *match_end = *start; - gtk_text_iter_forward_chars (match_end, start_offset); /* TODO: <- swapping */ - } - - g_free (text); - g_assert (gtk_text_iter_compare (match_start, match_end) <= 0); - return TRUE; -} - - -static gboolean moo_text_search_regex_backward (const GtkTextIter *start, - const GtkTextIter *end, - EggRegex *regex, - GtkTextIter *match_start, - GtkTextIter *match_end) -{ - GtkTextIter current_start, current_end; - int start_line, end_line; - GtkTextBuffer *buffer; - int i; - - g_return_val_if_fail (gtk_text_iter_compare (start, end) <= 0, FALSE); - - buffer = gtk_text_iter_get_buffer (start); - - start_line = gtk_text_iter_get_line (start); - end_line = gtk_text_iter_get_line (end); - - if (end_line - start_line < NUM_LINES_FOR_REGEX_SEARCH) - return moo_text_match_regex_backward (start, end, regex, - match_start, match_end); - - gtk_text_buffer_get_iter_at_line (buffer, ¤t_start, - end_line - NUM_LINES_FOR_REGEX_SEARCH + 1); - if (moo_text_match_regex_backward (¤t_start, end, regex, - match_start, match_end)) - return TRUE; - - for (i = end_line - 1; i > start_line + NUM_LINES_FOR_REGEX_SEARCH - 1; --i) - { - egg_regex_clear (regex); - gtk_text_buffer_get_iter_at_line (buffer, ¤t_start, - i - NUM_LINES_FOR_REGEX_SEARCH + 1); - gtk_text_buffer_get_iter_at_line (buffer, ¤t_end, i); - if (!gtk_text_iter_ends_line (¤t_end)) - gtk_text_iter_forward_to_line_end (¤t_end); - if (moo_text_match_regex_forward (¤t_start, ¤t_end, regex, - match_start, match_end)) - return TRUE; - } - - egg_regex_clear (regex); - gtk_text_buffer_get_iter_at_line (buffer, ¤t_end, - start_line + NUM_LINES_FOR_REGEX_SEARCH - 1); - if (!gtk_text_iter_ends_line (¤t_end)) - gtk_text_iter_forward_to_line_end (¤t_end); - return moo_text_match_regex_forward (start, ¤t_end, regex, - match_start, match_end); -} - - -/* assumes start < end */ -static gboolean moo_text_match_regex_backward (const GtkTextIter *start, - const GtkTextIter *end, - EggRegex *regex, - GtkTextIter *match_start, - GtkTextIter *match_end) -{ - GtkTextBuffer *buffer; - char *text; - int start_pos = -1, end_pos = -1; - long start_offset, end_offset; - EggRegexMatchFlags options = 0; - - buffer = gtk_text_iter_get_buffer (start); - text = gtk_text_buffer_get_slice (buffer, start, end, TRUE); - - if (!gtk_text_iter_ends_line (end)) - options = EGG_REGEX_MATCH_NOTEOL; - if (!gtk_text_iter_starts_line (start)) - options |= EGG_REGEX_MATCH_NOTBOL; - - while (TRUE) - { - if (egg_regex_match_next (regex, text, -1, 0) <= 0) - { - if (start_pos >= 0) - { - break; - } - else - { - g_free (text); - return FALSE; - } - } - else - egg_regex_fetch_pos (regex, text, 0, &start_pos, &end_pos); - } - - start_offset = g_utf8_pointer_to_offset (text, text + start_pos); - end_offset = g_utf8_pointer_to_offset (text + start_pos, text + end_pos); - end_offset += start_offset; - - /* TODO: why end and start are swapped? */ - - if (match_start) - { - *match_start = *start; - gtk_text_iter_forward_chars (match_start, start_offset); - if (match_end) - { - *match_end = *match_start; - gtk_text_iter_forward_chars (match_start, end_offset - start_offset); - } - } - else - { - *match_end = *start; - gtk_text_iter_forward_chars (match_end, end_offset); /* TODO: <- swapping */ - } - - g_free (text); - g_assert (gtk_text_iter_compare (match_start, match_end) >= 0); - return TRUE; -} - - -/****************************************************************************/ -/* Replace - */ - -MooTextReplaceResponseType moo_text_replace_func_replace_all - (G_GNUC_UNUSED const char *text, - G_GNUC_UNUSED EggRegex *regex, - G_GNUC_UNUSED const char *replacement, - G_GNUC_UNUSED GtkTextIter *to_replace_start, - G_GNUC_UNUSED GtkTextIter *to_replace_end, - G_GNUC_UNUSED gpointer data) -{ - return MOO_TEXT_REPLACE_ALL; -} - -static int moo_text_replace_all_interactive_forward - (GtkTextIter *start, - GtkTextIter *end, - const char *text, - const char *replacement, - MooTextSearchOptions options, - MooTextReplaceFunc func, - gpointer data); -static int moo_text_replace_all_interactive_backward - (GtkTextIter *start, - GtkTextIter *end, - const char *text, - const char *replacement, - MooTextSearchOptions options, - MooTextReplaceFunc func, - gpointer data); -static int moo_text_replace_regex_all_interactive_forward - (GtkTextIter *start, - GtkTextIter *end, - EggRegex *regex, - const char *replacement, - GError **error, - MooTextReplaceFunc func, - gpointer data); -static int moo_text_replace_regex_all_interactive_backward - (GtkTextIter *start, - GtkTextIter *end, - EggRegex *regex, - const char *replacement, - GError **error, - MooTextReplaceFunc func, - gpointer data); - -static gboolean moo_text_search_regex_replace_forward - (const GtkTextIter *start, - const GtkTextIter *end, - EggRegex *regex, - const char *replacement, - char **replacement_text, - GtkTextIter *match_start, - GtkTextIter *match_end, - GError **error); -static gboolean moo_text_search_regex_replace_backward - (const GtkTextIter *start, - const GtkTextIter *end, - EggRegex *regex, - const char *replacement, - char **replacement_text, - GtkTextIter *match_start, - GtkTextIter *match_end, - GError **error); -static gboolean moo_text_match_regex_replace_forward - (const GtkTextIter *start, - const GtkTextIter *end, - EggRegex *regex, - const char *replacement, - char **replacement_text, - GtkTextIter *match_start, - GtkTextIter *match_end, - GError **error); -#if 0 -static gboolean moo_text_match_regex_replace_backward - (const GtkTextIter *start, - const GtkTextIter *end, - EggRegex *regex, - const char *replacement, - char **replacement_text, - GtkTextIter *match_start, - GtkTextIter *match_end, - GError **error); -#endif - - -int moo_text_replace_all_interactive(GtkTextIter *start, - GtkTextIter *end, - const char *text, - const char *replacement, - MooTextSearchOptions options, - GError **error, - MooTextReplaceFunc func, - gpointer data) -{ - gboolean backwards = MOO_TEXT_SEARCH_BACKWARDS & options; - - g_return_val_if_fail (start != NULL && end != NULL, MOO_TEXT_REPLACE_INVALID_ARGS); - g_return_val_if_fail (text != NULL && replacement != NULL, MOO_TEXT_REPLACE_INVALID_ARGS); - g_return_val_if_fail (func != NULL, MOO_TEXT_REPLACE_INVALID_ARGS); - - if (backwards) - g_return_val_if_fail (gtk_text_iter_compare (start, end) == 1, - MOO_TEXT_REPLACE_INVALID_ARGS); - else - g_return_val_if_fail (gtk_text_iter_compare (start, end) == -1, - MOO_TEXT_REPLACE_INVALID_ARGS); - - if (error) *error = NULL; - - if (MOO_TEXT_SEARCH_REGEX & options) - { - EggRegex *regex = last_regex; - - if (!last_regex || last_options != options || - strcmp (egg_regex_get_pattern (last_regex), text)) - { - GError *err = NULL; - EggRegexCompileFlags flags; - - if (last_regex) egg_regex_unref (last_regex); - last_regex = NULL; - last_options = 0; - - flags = EGG_REGEX_MULTILINE; - if (options & MOO_TEXT_SEARCH_CASE_INSENSITIVE) - flags |= EGG_REGEX_CASELESS; - - last_regex = egg_regex_new (text, flags, 0, &err); - if (err) - { - if (error) *error = err; - else g_error_free (err); - egg_regex_free (last_regex); - last_regex = NULL; - return MOO_TEXT_REPLACE_REGEX_ERROR; - } - - egg_regex_optimize (last_regex, &err); - if (err) - { - g_warning ("%s: %s", G_STRLOC, err->message); - g_error_free (err); - } - - last_options = options; - - regex = last_regex; - } - - if (backwards) - return moo_text_replace_regex_all_interactive_backward ( - start, end, regex, replacement, - error, func, data); - else - return moo_text_replace_regex_all_interactive_forward ( - start, end, regex, replacement, - error, func, data); - } - - if (backwards) - return moo_text_replace_all_interactive_backward (start, end, - text, replacement, - options, func, data); - else - return moo_text_replace_all_interactive_forward (start, end, - text, replacement, - options, func, data); -} - - -int moo_text_replace_regex_all_interactive - (GtkTextIter *start, - GtkTextIter *end, - EggRegex *regex, - const char *replacement, - gboolean backwards, - GError **error, - MooTextReplaceFunc func, - gpointer data) -{ - g_return_val_if_fail (start != NULL && end != NULL, MOO_TEXT_REPLACE_INVALID_ARGS); - g_return_val_if_fail (regex != NULL && replacement != NULL, MOO_TEXT_REPLACE_INVALID_ARGS); - g_return_val_if_fail (func != NULL, MOO_TEXT_REPLACE_INVALID_ARGS); - - if (backwards) - g_return_val_if_fail (gtk_text_iter_compare (start, end) == 1, - MOO_TEXT_REPLACE_INVALID_ARGS); - else - g_return_val_if_fail (gtk_text_iter_compare (start, end) == -1, - MOO_TEXT_REPLACE_INVALID_ARGS); - - if (error) *error = NULL; - - if (backwards) - return moo_text_replace_regex_all_interactive_backward ( - start, end, regex, replacement, - error, func, data); - else - return moo_text_replace_regex_all_interactive_forward ( - start, end, regex, replacement, - error, func, data); -} - - -static int moo_text_replace_all_interactive_forward - (GtkTextIter *start, - GtkTextIter *end, - const char *text, - const char *replacement, - MooTextSearchOptions options, - MooTextReplaceFunc func, - gpointer data) -{ - GtkTextMark *start_mark, *end_mark; - GtkTextBuffer *buffer; - int count = 0; - GtkSourceSearchFlags flags; - gsize replacement_len = strlen (replacement); - MooTextReplaceResponseType response = MOO_TEXT_REPLACE; - - buffer = gtk_text_iter_get_buffer (start); - start_mark = gtk_text_buffer_create_mark (buffer, NULL, start, FALSE); - end_mark = gtk_text_buffer_create_mark (buffer, NULL, end, FALSE); - - flags = GTK_SOURCE_SEARCH_TEXT_ONLY; - if (options & MOO_TEXT_SEARCH_CASE_INSENSITIVE) - flags |= GTK_SOURCE_SEARCH_CASE_INSENSITIVE; - - gtk_text_buffer_begin_user_action (buffer); - - while (TRUE) - { - GtkTextIter match_start, match_end; - - if (!gtk_source_iter_forward_search (start, text, flags, - &match_start, &match_end, - end)) - break; - - if (response != MOO_TEXT_REPLACE_ALL) - { - response = func (text, NULL, replacement, &match_start, &match_end, data); - - if (response == MOO_TEXT_REPLACE_STOP) - break; - } - - if (response != MOO_TEXT_REPLACE_SKIP) - { - gtk_text_buffer_delete (buffer, &match_start, &match_end); - if (replacement_len) - gtk_text_buffer_insert (buffer, &match_end, - replacement, replacement_len); - ++count; - } - - *start = match_end; - gtk_text_buffer_get_iter_at_mark (buffer, end, end_mark); - - if (response == MOO_TEXT_REPLACE_AND_STOP) - break; - - if (gtk_text_iter_compare (start, end) >= 0) - break; - } - - gtk_text_buffer_end_user_action (buffer); - - gtk_text_buffer_get_iter_at_mark (buffer, start, start_mark); - gtk_text_buffer_delete_mark (buffer, start_mark); - gtk_text_buffer_delete_mark (buffer, end_mark); - - return count; -} - - -static int moo_text_replace_all_interactive_backward - (GtkTextIter *start, - GtkTextIter *end, - const char *text, - const char *replacement, - MooTextSearchOptions options, - MooTextReplaceFunc func, - gpointer data) -{ - GtkTextMark *start_mark, *end_mark, *insert; - GtkTextBuffer *buffer; - int count = 0; - GtkSourceSearchFlags flags; - gsize replacement_len; - MooTextReplaceResponseType response = MOO_TEXT_REPLACE; - - buffer = gtk_text_iter_get_buffer (start); - start_mark = gtk_text_buffer_create_mark (buffer, NULL, start, FALSE); - end_mark = gtk_text_buffer_create_mark (buffer, NULL, end, TRUE); - insert = gtk_text_buffer_create_mark (buffer, NULL, start, TRUE); - - flags = GTK_SOURCE_SEARCH_TEXT_ONLY; - if (options & MOO_TEXT_SEARCH_CASE_INSENSITIVE) - flags |= GTK_SOURCE_SEARCH_CASE_INSENSITIVE; - - gtk_text_buffer_begin_user_action (buffer); - - replacement_len = strlen (replacement); - - while (TRUE) - { - GtkTextIter match_start, match_end; - - if (!gtk_source_iter_backward_search (start, text, flags, - &match_start, &match_end, - end)) - break; - - if (response != MOO_TEXT_REPLACE_ALL) - { - response = func (text, NULL, replacement, &match_start, &match_end, data); - - if (response == MOO_TEXT_REPLACE_STOP) - break; - } - - gtk_text_buffer_move_mark (buffer, insert, &match_start); - if (response != MOO_TEXT_REPLACE_SKIP) - { - gtk_text_buffer_delete (buffer, &match_start, &match_end); - if (replacement_len) - gtk_text_buffer_insert (buffer, &match_start, - replacement, replacement_len); - ++count; - } - - gtk_text_buffer_get_iter_at_mark (buffer, end, end_mark); - gtk_text_buffer_get_iter_at_mark (buffer, start, insert); - - if (response == MOO_TEXT_REPLACE_AND_STOP) - break; - - if (gtk_text_iter_compare (start, end) <= 0) - break; - } - - gtk_text_buffer_end_user_action (buffer); - - gtk_text_buffer_get_iter_at_mark (buffer, start, start_mark); - gtk_text_buffer_delete_mark (buffer, start_mark); - gtk_text_buffer_delete_mark (buffer, end_mark); - - return count; -} - - -static int moo_text_replace_regex_all_interactive_forward - (GtkTextIter *start, - GtkTextIter *end, - EggRegex *regex, - const char *replacement, - GError **error, - MooTextReplaceFunc func, - gpointer data) -{ - GtkTextMark *start_mark, *end_mark; - GtkTextBuffer *buffer; - int count = 0; - MooTextReplaceResponseType response = MOO_TEXT_REPLACE; - - buffer = gtk_text_iter_get_buffer (start); - start_mark = gtk_text_buffer_create_mark (buffer, NULL, start, FALSE); - end_mark = gtk_text_buffer_create_mark (buffer, NULL, end, FALSE); - - gtk_text_buffer_begin_user_action (buffer); - - while (TRUE) - { - GtkTextIter match_start, match_end; - char *replacement_text = NULL; - - if (!moo_text_search_regex_replace_forward (start, end, - regex, replacement, - &replacement_text, - &match_start, &match_end, - error)) - break; - - if (response != MOO_TEXT_REPLACE_ALL) - { - response = func (egg_regex_get_pattern (regex), regex, - replacement_text, &match_start, &match_end, data); - - if (response == MOO_TEXT_REPLACE_STOP) - { - g_free (replacement_text); - break; - } - } - - if (response != MOO_TEXT_REPLACE_SKIP) - { - gtk_text_buffer_delete (buffer, &match_start, &match_end); - gtk_text_buffer_insert (buffer, &match_end, replacement_text, -1); - g_free (replacement_text); - ++count; - } - - *start = match_end; - gtk_text_buffer_get_iter_at_mark (buffer, end, end_mark); - - if (response == MOO_TEXT_REPLACE_AND_STOP) - break; - - if (gtk_text_iter_compare (start, end) >= 0) - break; - } - - gtk_text_buffer_end_user_action (buffer); - - gtk_text_buffer_get_iter_at_mark (buffer, start, start_mark); - gtk_text_buffer_delete_mark (buffer, start_mark); - gtk_text_buffer_delete_mark (buffer, end_mark); - - return count; -} - - -static int moo_text_replace_regex_all_interactive_backward - (GtkTextIter *start, - GtkTextIter *end, - EggRegex *regex, - const char *replacement, - GError **error, - MooTextReplaceFunc func, - gpointer data) -{ - GtkTextMark *start_mark, *end_mark, *insert; - GtkTextBuffer *buffer; - int count = 0; - MooTextReplaceResponseType response = MOO_TEXT_REPLACE; - - buffer = gtk_text_iter_get_buffer (start); - start_mark = gtk_text_buffer_create_mark (buffer, NULL, start, FALSE); - end_mark = gtk_text_buffer_create_mark (buffer, NULL, end, TRUE); - insert = gtk_text_buffer_create_mark (buffer, NULL, start, TRUE); - - gtk_text_buffer_begin_user_action (buffer); - - while (TRUE) - { - GtkTextIter match_start, match_end; - char *replacement_text = NULL; - - if (!moo_text_search_regex_replace_backward (start, end, regex, - replacement, &replacement_text, - &match_start, &match_end, - error)) - break; - - if (response != MOO_TEXT_REPLACE_ALL) - { - response = func (egg_regex_get_pattern (regex), - regex, replacement_text, &match_start, &match_end, - data); - - if (response == MOO_TEXT_REPLACE_STOP) - { - g_free (replacement_text); - break; - } - } - - gtk_text_buffer_move_mark (buffer, insert, &match_start); - if (response != MOO_TEXT_REPLACE_SKIP) - { - gtk_text_buffer_delete (buffer, &match_start, &match_end); - gtk_text_buffer_insert (buffer, &match_start, replacement_text, -1); - g_free (replacement_text); - ++count; - } - - gtk_text_buffer_get_iter_at_mark (buffer, end, end_mark); - gtk_text_buffer_get_iter_at_mark (buffer, start, insert); - - if (response == MOO_TEXT_REPLACE_AND_STOP) - break; - - if (gtk_text_iter_compare (start, end) <= 0) - break; - } - - gtk_text_buffer_end_user_action (buffer); - - gtk_text_buffer_get_iter_at_mark (buffer, start, start_mark); - gtk_text_buffer_delete_mark (buffer, start_mark); - gtk_text_buffer_delete_mark (buffer, end_mark); - - return count; -} - - -static gboolean moo_text_search_regex_replace_forward - (const GtkTextIter *start, - const GtkTextIter *end, - EggRegex *regex, - const char *replacement, - char **replacement_text, - GtkTextIter *match_start, - GtkTextIter *match_end, - GError **error) -{ - GtkTextIter current_start, current_end; - int start_line, end_line; - GtkTextBuffer *buffer; - int i; - - g_return_val_if_fail (gtk_text_iter_compare (start, end) <= 0, FALSE); - - start_line = gtk_text_iter_get_line (start); - end_line = gtk_text_iter_get_line (end); - - if (end_line - start_line < NUM_LINES_FOR_REGEX_SEARCH) - return moo_text_match_regex_replace_forward (start, end, regex, - replacement, replacement_text, - match_start, match_end, - error); - - buffer = gtk_text_iter_get_buffer (start); - gtk_text_buffer_get_iter_at_line (buffer, ¤t_end, - start_line + NUM_LINES_FOR_REGEX_SEARCH - 1); - if (!gtk_text_iter_ends_line (¤t_end)) - gtk_text_iter_forward_to_line_end (¤t_end); - if (moo_text_match_regex_replace_forward (start, ¤t_end, regex, - replacement, replacement_text, - match_start, match_end, - error)) - return TRUE; - - for (i = start_line + 1; end_line - i > NUM_LINES_FOR_REGEX_SEARCH; ++i) - { - egg_regex_clear (regex); - gtk_text_buffer_get_iter_at_line (buffer, ¤t_start, i); - gtk_text_buffer_get_iter_at_line (buffer, ¤t_end, - i + NUM_LINES_FOR_REGEX_SEARCH - 1); - if (!gtk_text_iter_ends_line (¤t_end)) - gtk_text_iter_forward_to_line_end (¤t_end); - if (moo_text_match_regex_replace_forward (¤t_start, ¤t_end, regex, - replacement, replacement_text, - match_start, match_end, - error)) - return TRUE; - } - - egg_regex_clear (regex); - gtk_text_buffer_get_iter_at_line (buffer, ¤t_start, - end_line - NUM_LINES_FOR_REGEX_SEARCH + 1); - return moo_text_match_regex_replace_forward (¤t_start, end, regex, - replacement, replacement_text, - match_start, match_end, - error); -} - - -static gboolean moo_text_match_regex_replace_forward - (const GtkTextIter *start, - const GtkTextIter *end, - EggRegex *regex, - const char *replacement, - char **replacement_text, - GtkTextIter *match_start, - GtkTextIter *match_end, - GError **error) -{ - GtkTextBuffer *buffer; - char *text; - int result; - int start_pos, end_pos; - long start_offset, end_offset; - gunichar c; - - buffer = gtk_text_iter_get_buffer (start); - text = gtk_text_buffer_get_slice (buffer, start, end, TRUE); - result = egg_regex_match (regex, text, -1, 0); - - if (result <= 0) { - g_free (text); - return FALSE; - } - - egg_regex_fetch_pos (regex, text, 0, &start_pos, &end_pos); - - c = g_utf8_get_char_validated (text + start_pos, -1); - if (c == ((gunichar)-2) || c == ((gunichar)-1)) { - g_critical ("%s: egg_regex_fetch_pos() returned invalid " - "match start position", G_STRLOC); - g_free (text); - return FALSE; - } - c = g_utf8_get_char_validated (text + end_pos, -1); - if (c == ((gunichar)-2) || c == ((gunichar)-1)) { - g_critical ("%s: egg_regex_fetch_pos() returned invalid " - "match end position", G_STRLOC); - g_free (text); - return FALSE; - } - - start_offset = g_utf8_pointer_to_offset (text, text + start_pos); - end_offset = g_utf8_pointer_to_offset (text + start_pos, text + end_pos); - end_offset += start_offset; - - /* TODO: why end and start are swapped? */ - - if (match_start) - { - *match_start = *start; - gtk_text_iter_forward_chars (match_start, start_offset); - if (match_end) { - *match_end = *match_start; - gtk_text_iter_forward_chars (match_start, end_offset - start_offset); - } - - gtk_text_iter_order (match_start, match_end); - } - else - { - *match_end = *start; - gtk_text_iter_forward_chars (match_end, start_offset); /* TODO: <- swapping */ - } - - { - gchar *rtext = egg_regex_eval_replacement (regex, text, replacement, error); - if (rtext) - { - if (replacement_text) *replacement_text = rtext; - else g_free (rtext); - g_free (text); - return TRUE; - } - else - { - if (replacement_text) *replacement_text = NULL; - g_free (text); - return FALSE; - } - } -} - - -static gboolean moo_text_search_regex_replace_backward - (G_GNUC_UNUSED const GtkTextIter *start, - G_GNUC_UNUSED const GtkTextIter *end, - G_GNUC_UNUSED EggRegex *regex, - G_GNUC_UNUSED const char *replacement, - G_GNUC_UNUSED char **replacement_text, - G_GNUC_UNUSED GtkTextIter *match_start, - G_GNUC_UNUSED GtkTextIter *match_end, - G_GNUC_UNUSED GError **error) -{ - g_warning ("%s: implement me", G_STRLOC); - return FALSE; -} - - -#if 0 -static gboolean moo_text_match_regex_replace_backward - (G_GNUC_UNUSED const GtkTextIter *start, - G_GNUC_UNUSED const GtkTextIter *end, - G_GNUC_UNUSED EggRegex *regex, - G_GNUC_UNUSED const char *replacement, - G_GNUC_UNUSED char **replacement_text, - G_GNUC_UNUSED GtkTextIter *match_start, - G_GNUC_UNUSED GtkTextIter *match_end, - G_GNUC_UNUSED GError **error) -{ - g_warning ("%s: implement me", G_STRLOC); - return FALSE; -} -#endif diff --git a/moo/mooedit/mooeditsearch.h b/moo/mooedit/mooeditsearch.h deleted file mode 100644 index 0df04257..00000000 --- a/moo/mooedit/mooeditsearch.h +++ /dev/null @@ -1,110 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4; coding: utf-8 -*- - * - * mooeditsearch.h - * - * Copyright (C) 2004-2005 by Yevgen Muntyan - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * See COPYING file that comes with this distribution. - */ - -#ifndef __MOO_EDIT_SEARCH_H__ -#define __MOO_EDIT_SEARCH_H__ - -#include -#include -#include - -G_BEGIN_DECLS - - -#define MOO_TYPE_TEXT_SEARCH_OPTIONS (moo_text_search_options_get_type ()) -#define MOO_TYPE_TEXT_REPLACE_RESPONSE_TYPE (moo_text_replace_response_type_get_type ()) -GType moo_text_search_options_get_type (void) G_GNUC_UNUSED; -GType moo_text_replace_response_type_get_type (void) G_GNUC_UNUSED; - - -typedef enum { - MOO_TEXT_SEARCH_BACKWARDS = 1 << 0, - MOO_TEXT_SEARCH_CASE_INSENSITIVE = 1 << 1, - MOO_TEXT_SEARCH_REGEX = 1 << 2 -} MooTextSearchOptions; - - -gboolean moo_text_search (const GtkTextIter *start, - const GtkTextIter *limit, - const char *text, - GtkTextIter *match_start, - GtkTextIter *match_end, - MooTextSearchOptions options, - GError **error); - -gboolean moo_text_search_regex (const GtkTextIter *start, - const GtkTextIter *limit, - EggRegex *regex, - GtkTextIter *match_start, - GtkTextIter *match_end, - gboolean backwards); - - -#define MOO_TEXT_REPLACE_INVALID_ARGS ((int)-1) -#define MOO_TEXT_REPLACE_REGEX_ERROR ((int)-2) - -/* Do not change numerical values!!! */ -typedef enum { - MOO_TEXT_REPLACE_STOP = 0, - MOO_TEXT_REPLACE_AND_STOP = 1, - MOO_TEXT_REPLACE_SKIP = 2, - MOO_TEXT_REPLACE = 3, - MOO_TEXT_REPLACE_ALL = 4 -} MooTextReplaceResponseType; - -/* replacement is real replacement text when searching for regex */ -typedef MooTextReplaceResponseType (* MooTextReplaceFunc) (const char *text, - EggRegex *regex, - const char *replacement, - GtkTextIter *to_replace_start, - GtkTextIter *to_replace_end, - gpointer data); - -MooTextReplaceResponseType moo_text_replace_func_replace_all - (const char *text, - EggRegex *regex, - const char *replacement, - GtkTextIter *to_replace_start, - GtkTextIter *to_replace_end, - gpointer data); - -int moo_text_replace_all_interactive(GtkTextIter *start, - GtkTextIter *end, - const char *text, - const char *replacement, - MooTextSearchOptions options, - GError **error, - MooTextReplaceFunc func, - gpointer data); - -int moo_text_replace_regex_all_interactive - (GtkTextIter *start, - GtkTextIter *end, - EggRegex *regex, - const char *replacement, - gboolean backwards, - GError **error, - MooTextReplaceFunc func, - gpointer data); - - -void _moo_text_view_find (MooTextView *edit); -void _moo_text_view_replace (MooTextView *edit); -void _moo_text_view_find_next (MooTextView *edit); -void _moo_text_view_find_previous (MooTextView *edit); - - -G_END_DECLS - -#endif /* __MOO_EDIT_SEARCH_H__ */ diff --git a/moo/mooedit/moolang-rules.c b/moo/mooedit/moolang-rules.c index d24f7555..8d007b58 100644 --- a/moo/mooedit/moolang-rules.c +++ b/moo/mooedit/moolang-rules.c @@ -483,11 +483,12 @@ moo_rule_regex_new (const char *pattern, regex = egg_regex_new (pattern, regex_compile_options, regex_match_options, &error); - if (!regex) + if (error) { g_warning ("could not compile pattern '%s': %s", pattern, error->message); g_error_free (error); + egg_regex_unref (regex); return NULL; } diff --git a/moo/mooedit/mootextbuffer.c b/moo/mooedit/mootextbuffer.c index d573abf1..ad534dc1 100644 --- a/moo/mooedit/mootextbuffer.c +++ b/moo/mooedit/mootextbuffer.c @@ -37,6 +37,9 @@ struct _MooTextBufferPrivate { MooBracketMatchType bracket_found; LineBuffer *line_buf; + + int cursor_moved_frozen; + gboolean cursor_moved; }; @@ -67,9 +70,15 @@ static void moo_text_buffer_apply_tag (GtkTextBuffer *buffer, static void moo_text_buffer_delete_range (GtkTextBuffer *buffer, GtkTextIter *start, GtkTextIter *end); +static void moo_text_buffer_begin_user_action (GtkTextBuffer *buffer); +static void moo_text_buffer_end_user_action (GtkTextBuffer *buffer); static void moo_text_buffer_queue_highlight (MooTextBuffer *buffer); +static void cursor_moved (MooTextBuffer *buffer, + const GtkTextIter *iter); +static void freeze_cursor_moved (MooTextBuffer *buffer); +static void thaw_cursor_moved (MooTextBuffer *buffer); enum { CURSOR_MOVED, @@ -108,6 +117,8 @@ moo_text_buffer_class_init (MooTextBufferClass *klass) buffer_class->insert_text = moo_text_buffer_insert_text; buffer_class->delete_range = moo_text_buffer_delete_range; buffer_class->apply_tag = moo_text_buffer_apply_tag; + buffer_class->begin_user_action = moo_text_buffer_begin_user_action; + buffer_class->end_user_action = moo_text_buffer_end_user_action; klass->cursor_moved = moo_text_buffer_cursor_moved; @@ -162,7 +173,7 @@ moo_text_buffer_class_init (MooTextBufferClass *klass) signals[CURSOR_MOVED] = g_signal_new ("cursor-moved", G_OBJECT_CLASS_TYPE (klass), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MooTextBufferClass, cursor_moved), NULL, NULL, _moo_marshal_VOID__BOXED, @@ -209,6 +220,20 @@ moo_text_buffer_new (GtkTextTagTable *table) } +static void +moo_text_buffer_begin_user_action (GtkTextBuffer *buffer) +{ + moo_text_buffer_freeze (MOO_TEXT_BUFFER (buffer)); +} + + +static void +moo_text_buffer_end_user_action (GtkTextBuffer *buffer) +{ + moo_text_buffer_thaw (MOO_TEXT_BUFFER (buffer)); +} + + static void moo_text_buffer_mark_set (GtkTextBuffer *text_buffer, const GtkTextIter *iter, @@ -242,7 +267,7 @@ moo_text_buffer_mark_set (GtkTextBuffer *text_buffer, } if (mark == insert) - g_signal_emit (buffer, signals[CURSOR_MOVED], 0, iter); + cursor_moved (buffer, iter); } @@ -282,7 +307,7 @@ moo_text_buffer_insert_text (GtkTextBuffer *text_buffer, moo_text_buffer_queue_highlight (buffer); - g_signal_emit (buffer, signals[CURSOR_MOVED], 0, pos); + cursor_moved (buffer, pos); if (!buffer->priv->has_text) { @@ -300,11 +325,17 @@ moo_text_buffer_delete_range (GtkTextBuffer *text_buffer, MooTextBuffer *buffer = MOO_TEXT_BUFFER (text_buffer); int first_line, last_line; - gtk_text_buffer_remove_all_tags (text_buffer, start, end); - first_line = gtk_text_iter_get_line (start); last_line = gtk_text_iter_get_line (end); +#define MANY_LINES 1000 + if (buffer->priv->lang && buffer->priv->do_highlight && + last_line - first_line > MANY_LINES) + { + gtk_text_buffer_remove_all_tags (text_buffer, start, end); + } +#undef MANY_LINES + GTK_TEXT_BUFFER_CLASS(moo_text_buffer_parent_class)->delete_range (text_buffer, start, end); if (first_line < last_line) @@ -315,7 +346,7 @@ moo_text_buffer_delete_range (GtkTextBuffer *text_buffer, moo_line_buffer_invalidate (buffer->priv->line_buf, first_line); moo_text_buffer_queue_highlight (buffer); - g_signal_emit (buffer, signals[CURSOR_MOVED], 0, start); + cursor_moved (buffer, start); if (buffer->priv->has_text) { @@ -551,12 +582,60 @@ moo_text_buffer_apply_scheme (MooTextBuffer *buffer, } +void +moo_text_buffer_freeze (MooTextBuffer *buffer) +{ + g_return_if_fail (MOO_IS_TEXT_BUFFER (buffer)); + freeze_cursor_moved (buffer); +} + + +void +moo_text_buffer_thaw (MooTextBuffer *buffer) +{ + g_return_if_fail (MOO_IS_TEXT_BUFFER (buffer)); + thaw_cursor_moved (buffer); +} + + /*****************************************************************************/ /* Matching brackets */ #define FIND_BRACKETS_LIMIT 10000 +static void +cursor_moved (MooTextBuffer *buffer, + const GtkTextIter *where) +{ + if (!buffer->priv->cursor_moved_frozen) + g_signal_emit (buffer, signals[CURSOR_MOVED], 0, where); + else + buffer->priv->cursor_moved = TRUE; +} + +static void +freeze_cursor_moved (MooTextBuffer *buffer) +{ + buffer->priv->cursor_moved_frozen++; +} + +static void +thaw_cursor_moved (MooTextBuffer *buffer) +{ + g_return_if_fail (buffer->priv->cursor_moved_frozen > 0); + + if (!--buffer->priv->cursor_moved_frozen && buffer->priv->cursor_moved) + { + GtkTextIter iter; + GtkTextMark *insert = gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (buffer)); + gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer), &iter, insert); + cursor_moved (buffer, &iter); + buffer->priv->cursor_moved = FALSE; + } +} + + static void moo_text_buffer_cursor_moved (MooTextBuffer *buffer, const GtkTextIter *where) diff --git a/moo/mooedit/mootextbuffer.h b/moo/mooedit/mootextbuffer.h index c1ab3b9e..03e8d1c4 100644 --- a/moo/mooedit/mootextbuffer.h +++ b/moo/mooedit/mootextbuffer.h @@ -70,6 +70,9 @@ void moo_text_buffer_set_brackets (MooTextBuffer *buf void moo_text_buffer_set_check_brackets (MooTextBuffer *buffer, gboolean check); +void moo_text_buffer_freeze (MooTextBuffer *buffer); +void moo_text_buffer_thaw (MooTextBuffer *buffer); + gboolean moo_text_buffer_has_text (MooTextBuffer *buffer); gboolean moo_text_buffer_has_selection (MooTextBuffer *buffer); diff --git a/moo/mooedit/mootextfind.c b/moo/mooedit/mootextfind.c new file mode 100644 index 00000000..12de78ba --- /dev/null +++ b/moo/mooedit/mootextfind.c @@ -0,0 +1,1000 @@ +/* + * mootextfind.c + * + * Copyright (C) 2004-2005 by Yevgen Muntyan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * See COPYING file that comes with this distribution. + */ + +#include "mooedit/mootextfind.h" +#include "mooedit/mootextfind-glade.h" +#include "mooedit/mootextgotoline-glade.h" +#include "mooedit/mootextview.h" +#include "mooedit/mooeditdialogs.h" +#include "mooedit/mootextsearch.h" +#include "mooutils/moohistoryentry.h" +#include + + +static char *last_search; +static EggRegex *last_regex; +static MooFindFlags last_search_flags; +static MooHistoryList *search_history; +static MooHistoryList *replace_history; + + +static GObject *moo_find_constructor (GType type, + guint n_props, + GObjectConstructParam *props); +static void moo_find_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void moo_find_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static void moo_find_finalize (GObject *object); + + +G_DEFINE_TYPE(MooFind, moo_find, GTK_TYPE_DIALOG) + + +enum { + PROP_0, + PROP_REPLACE +}; + + +static void +moo_find_class_init (MooFindClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->constructor = moo_find_constructor; + gobject_class->set_property = moo_find_set_property; + gobject_class->get_property = moo_find_get_property; + gobject_class->finalize = moo_find_finalize; + + search_history = moo_history_list_new ("MooFind"); + replace_history = moo_history_list_new ("MooReplace"); + last_search_flags = MOO_FIND_CASELESS; + + g_object_class_install_property (gobject_class, + PROP_REPLACE, + g_param_spec_boolean ("replace", + "replace", + "replace", + TRUE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); +} + + +static void +moo_find_init (MooFind *find) +{ + GtkWidget *vbox; + + find->xml = moo_glade_xml_new_empty (); + moo_glade_xml_map_id (find->xml, "search_entry", MOO_TYPE_HISTORY_ENTRY); + moo_glade_xml_map_id (find->xml, "replace_entry", MOO_TYPE_HISTORY_ENTRY); + + if (!moo_glade_xml_parse_memory (find->xml, MOO_TEXT_FIND_GLADE_UI, -1, "vbox")) + { + moo_glade_xml_unref (find->xml); + find->xml = NULL; + g_return_if_reached (); + } + + vbox = moo_glade_xml_get_widget (find->xml, "vbox"); + + gtk_container_add (GTK_CONTAINER (GTK_DIALOG(find)->vbox), vbox); + gtk_dialog_set_has_separator (GTK_DIALOG (find), FALSE); + + moo_history_entry_set_list (moo_glade_xml_get_widget (find->xml, "search_entry"), search_history); + moo_history_entry_set_list (moo_glade_xml_get_widget (find->xml, "replace_entry"), replace_history); +} + + +static GObject* +moo_find_constructor (GType type, + guint n_props, + GObjectConstructParam *props) +{ + GObject *object; + MooFind *find; + gboolean use_replace; + const char *stock_id, *title; + + object = G_OBJECT_CLASS (moo_find_parent_class)->constructor (type, n_props, props); + find = MOO_FIND (object); + + if (find->replace) + { + use_replace = TRUE; + stock_id = GTK_STOCK_FIND_AND_REPLACE; + title = "Replace"; + } + else + { + use_replace = FALSE; + stock_id = GTK_STOCK_FIND; + title = "Find"; + } + + gtk_widget_set_sensitive (moo_glade_xml_get_widget (find->xml, "backwards"), !use_replace); + g_object_set (moo_glade_xml_get_widget (find->xml, "replace_frame"), + "visible", use_replace, NULL); + g_object_set (moo_glade_xml_get_widget (find->xml, "dont_prompt"), + "visible", use_replace, NULL); + + gtk_window_set_title (GTK_WINDOW (find), title); + gtk_dialog_add_buttons (GTK_DIALOG (find), + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + stock_id, GTK_RESPONSE_OK, + NULL); + gtk_dialog_set_alternative_button_order (GTK_DIALOG (find), GTK_RESPONSE_OK, + GTK_STOCK_CANCEL, -1); + gtk_dialog_set_default_response (GTK_DIALOG (find), GTK_RESPONSE_OK); + + return object; +} + + +static void +moo_find_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + MooFind *find = MOO_FIND (object); + + switch (prop_id) + { + case PROP_REPLACE: + find->replace = g_value_get_boolean (value) ? TRUE : FALSE; + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static void +moo_find_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + MooFind *find = MOO_FIND (object); + + switch (prop_id) + { + case PROP_REPLACE: + g_value_set_boolean (value, find->replace ? TRUE : FALSE); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static void +moo_find_finalize (GObject *object) +{ + MooFind *find = MOO_FIND (object); + + moo_glade_xml_unref (find->xml); + egg_regex_unref (find->regex); + + G_OBJECT_CLASS(moo_find_parent_class)->finalize (object); +} + + +GtkWidget* +moo_find_new (gboolean replace) +{ + return g_object_new (MOO_TYPE_FIND, "replace", replace, NULL); +} + + +void +moo_find_setup (MooFind *find, + GtkTextView *view) +{ + GtkWidget *parent; + GtkTextBuffer *buffer; + GtkTextIter sel_start, sel_end; + MooCombo *search_entry; + + g_return_if_fail (MOO_IS_FIND (find)); + g_return_if_fail (GTK_IS_TEXT_VIEW (view)); + + parent = gtk_widget_get_toplevel (GTK_WIDGET (view)); + + if (GTK_WIDGET_TOPLEVEL (parent)) + gtk_window_set_transient_for (GTK_WINDOW (find), GTK_WINDOW (parent)); + + buffer = gtk_text_view_get_buffer (view); + search_entry = moo_glade_xml_get_widget (find->xml, "search_entry"); + + if (gtk_text_buffer_get_selection_bounds (buffer, &sel_start, &sel_end) && + gtk_text_iter_get_line (&sel_start) == gtk_text_iter_get_line (&sel_end)) + { + char *selection = gtk_text_buffer_get_text (buffer, &sel_start, &sel_end, TRUE); + moo_combo_entry_set_text (search_entry, selection); + g_free (selection); + } + else if (last_search) + { + moo_combo_entry_set_text (search_entry, last_search); + } + + moo_find_set_flags (find, last_search_flags); +} + + +gboolean +moo_find_run (MooFind *find) +{ + MooCombo *search_entry, *replace_entry; + g_return_val_if_fail (MOO_IS_FIND (find), FALSE); + + search_entry = moo_glade_xml_get_widget (find->xml, "search_entry"); + replace_entry = moo_glade_xml_get_widget (find->xml, "replace_entry"); + + egg_regex_unref (find->regex); + find->regex = NULL; + + while (TRUE) + { + const char *search_for, *replace_with; + MooFindFlags flags; + + if (gtk_dialog_run (GTK_DIALOG (find)) != GTK_RESPONSE_OK) + return FALSE; + + search_for = moo_combo_entry_get_text (search_entry); + replace_with = moo_combo_entry_get_text (replace_entry); + + if (!search_for[0]) + return FALSE; + + flags = moo_find_get_flags (find); + + if (flags & MOO_FIND_REGEX) + { + EggRegex *regex; + EggRegexCompileFlags re_flags = 0; + GError *error = NULL; + + if (flags & MOO_FIND_CASELESS) + re_flags |= EGG_REGEX_CASELESS; + + regex = egg_regex_new (search_for, re_flags, 0, &error); + + if (error) + { + moo_text_regex_error_dialog (GTK_WIDGET (find), error); + g_error_free (error); + egg_regex_unref (regex); + continue; + } + + find->regex = regex; + + egg_regex_optimize (regex, &error); + + if (error) + { + g_warning ("%s", error->message); + g_error_free (error); + } + } + + if (find->replace && !(flags & MOO_FIND_REPL_LITERAL)) + { + GError *error = NULL; + + if (!egg_regex_check_replacement (replace_with, NULL, &error)) + { + moo_text_regex_error_dialog (GTK_WIDGET (find), error); + g_error_free (error); + egg_regex_unref (find->regex); + find->regex = NULL; + continue; + } + } + + last_search_flags = flags; + g_free (last_search); + last_search = g_strdup (search_for); + egg_regex_unref (last_regex); + last_regex = egg_regex_ref (find->regex); + + moo_history_list_add (search_history, search_for); + + if (find->replace && replace_with[0]) + moo_history_list_add (replace_history, replace_with); + + return TRUE; + } +} + + +MooFindFlags +moo_find_get_flags (MooFind *find) +{ + MooFindFlags flags = 0; + + g_return_val_if_fail (MOO_IS_FIND (find), 0); + + if (gtk_toggle_button_get_active (moo_glade_xml_get_widget (find->xml, "regex"))) + flags |= MOO_FIND_REGEX; + if (gtk_toggle_button_get_active (moo_glade_xml_get_widget (find->xml, "repl_literal"))) + flags |= MOO_FIND_REPL_LITERAL; + if (gtk_toggle_button_get_active (moo_glade_xml_get_widget (find->xml, "whole_words"))) + flags |= MOO_FIND_WHOLE_WORDS; + if (gtk_toggle_button_get_active (moo_glade_xml_get_widget (find->xml, "from_cursor"))) + flags |= MOO_FIND_FROM_CURSOR; + if (gtk_toggle_button_get_active (moo_glade_xml_get_widget (find->xml, "backwards"))) + flags |= MOO_FIND_BACKWARDS; + if (gtk_toggle_button_get_active (moo_glade_xml_get_widget (find->xml, "selected"))) + flags |= MOO_FIND_IN_SELECTED; + if (gtk_toggle_button_get_active (moo_glade_xml_get_widget (find->xml, "dont_prompt"))) + flags |= MOO_FIND_DONT_PROMPT; + + if (!gtk_toggle_button_get_active (moo_glade_xml_get_widget (find->xml, "case_sensitive"))) + flags |= MOO_FIND_CASELESS; + + return flags; +} + + +void +moo_find_set_flags (MooFind *find, + MooFindFlags flags) +{ + g_return_if_fail (MOO_IS_FIND (find)); + + gtk_toggle_button_set_active (moo_glade_xml_get_widget (find->xml, "regex"), + (flags & MOO_FIND_REGEX) ? TRUE : FALSE); + gtk_toggle_button_set_active (moo_glade_xml_get_widget (find->xml, "repl_literal"), + (flags & MOO_FIND_REPL_LITERAL) ? TRUE : FALSE); + gtk_toggle_button_set_active (moo_glade_xml_get_widget (find->xml, "whole_words"), + (flags & MOO_FIND_WHOLE_WORDS) ? TRUE : FALSE); + gtk_toggle_button_set_active (moo_glade_xml_get_widget (find->xml, "from_cursor"), + (flags & MOO_FIND_FROM_CURSOR) ? TRUE : FALSE); + gtk_toggle_button_set_active (moo_glade_xml_get_widget (find->xml, "backwards"), + (flags & MOO_FIND_BACKWARDS) ? TRUE : FALSE); + gtk_toggle_button_set_active (moo_glade_xml_get_widget (find->xml, "selected"), + (flags & MOO_FIND_IN_SELECTED) ? TRUE : FALSE); + gtk_toggle_button_set_active (moo_glade_xml_get_widget (find->xml, "dont_prompt"), + (flags & MOO_FIND_DONT_PROMPT) ? TRUE : FALSE); + + gtk_toggle_button_set_active (moo_glade_xml_get_widget (find->xml, "case_sensitive"), + (flags & MOO_FIND_CASELESS) ? FALSE : TRUE); +} + + +char* +moo_find_get_text (MooFind *find) +{ + MooCombo *entry; + g_return_val_if_fail (MOO_IS_FIND (find), NULL); + entry = moo_glade_xml_get_widget (find->xml, "search_entry"); + return g_strdup (moo_combo_entry_get_text (entry)); +} + + +EggRegex* +moo_find_get_regex (MooFind *find) +{ + g_return_val_if_fail (MOO_IS_FIND (find), NULL); + return find->regex ? egg_regex_ref (find->regex) : NULL; +} + + +char* +moo_find_get_replacement (MooFind *find) +{ + MooCombo *entry; + g_return_val_if_fail (MOO_IS_FIND (find), NULL); + entry = moo_glade_xml_get_widget (find->xml, "replace_entry"); + return g_strdup (moo_combo_entry_get_text (entry)); +} + + +static gboolean +do_find (const GtkTextIter *start, + const GtkTextIter *end, + MooFindFlags flags, + EggRegex *regex, + const char *text, + GtkTextIter *match_start, + GtkTextIter *match_end) +{ + if (regex) + { + if (flags & MOO_FIND_BACKWARDS) + return moo_text_search_regex_backward (start, end, regex, match_start, + match_end, NULL, NULL, NULL); + else + return moo_text_search_regex_forward (start, end, regex, match_start, + match_end, NULL, NULL, NULL); + } + else + { + MooTextSearchFlags search_flags = 0; + + if (flags & MOO_FIND_CASELESS) + search_flags |= MOO_TEXT_SEARCH_CASELESS; + if (flags & MOO_FIND_WHOLE_WORDS) + search_flags |= MOO_TEXT_SEARCH_WHOLE_WORDS; + + if (flags & MOO_FIND_BACKWARDS) + return moo_text_search_backward (start, text, search_flags, + match_start, match_end, end); + else + return moo_text_search_forward (start, text, search_flags, + match_start, match_end, end); + } +} + + +static void +get_search_bounds (GtkTextBuffer *buffer, + MooFindFlags flags, + GtkTextIter *start, + GtkTextIter *end) +{ + if (flags & MOO_FIND_FROM_CURSOR) + { + gtk_text_buffer_get_iter_at_mark (buffer, start, + gtk_text_buffer_get_insert (buffer)); + if (flags & MOO_FIND_BACKWARDS) + gtk_text_buffer_get_start_iter (buffer, end); + else + gtk_text_buffer_get_end_iter (buffer, end); + } + else if (flags & MOO_FIND_BACKWARDS) + { + gtk_text_buffer_get_end_iter (buffer, start); + gtk_text_buffer_get_start_iter (buffer, end); + } + else + { + gtk_text_buffer_get_start_iter (buffer, start); + gtk_text_buffer_get_end_iter (buffer, end); + } +} + +static gboolean +get_search_bounds2 (GtkTextBuffer *buffer, + MooFindFlags flags, + GtkTextIter *start, + GtkTextIter *end) +{ + if (flags & MOO_FIND_FROM_CURSOR) + { + gtk_text_buffer_get_iter_at_mark (buffer, end, + gtk_text_buffer_get_insert (buffer)); + + if (flags & MOO_FIND_BACKWARDS) + gtk_text_buffer_get_end_iter (buffer, start); + else + gtk_text_buffer_get_start_iter (buffer, start); + + return TRUE; + } + else + { + return FALSE; + } +} + + +void +moo_text_view_run_find (GtkTextView *view) +{ + GtkWidget *find; + MooFindFlags flags; + char *text; + EggRegex *regex; + GtkTextIter start, end; + GtkTextIter match_start, match_end; + GtkTextBuffer *buffer; + gboolean found; + + g_return_if_fail (GTK_IS_TEXT_VIEW (view)); + + find = moo_find_new (FALSE); + moo_find_setup (MOO_FIND (find), view); + + if (!moo_find_run (MOO_FIND (find))) + { + gtk_widget_destroy (find); + return; + } + + flags = moo_find_get_flags (MOO_FIND (find)); + text = moo_find_get_text (MOO_FIND (find)); + regex = moo_find_get_regex (MOO_FIND (find)); + + gtk_widget_destroy (find); + buffer = gtk_text_view_get_buffer (view); + + get_search_bounds (buffer, flags, &start, &end); + + found = do_find (&start, &end, flags, regex, text, &match_start, &match_end); + + if (!found && get_search_bounds2 (buffer, flags, &start, &end)) + found = do_find (&start, &end, flags, regex, text, &match_start, &match_end); + + if (found) + { + gtk_text_buffer_select_range (buffer, &match_end, &match_start); + gtk_text_view_scroll_to_mark (view, gtk_text_buffer_get_insert (buffer), + 0.2, FALSE, 0, 0); + } + else + { + GtkTextIter insert; + gtk_text_buffer_get_iter_at_mark (buffer, &insert, + gtk_text_buffer_get_insert (buffer)); + gtk_text_buffer_place_cursor (buffer, &insert); + } + + g_free (text); + egg_regex_unref (regex); +} + + +void +moo_text_view_run_find_next (GtkTextView *view) +{ + GtkTextBuffer *buffer; + GtkTextIter sel_start, sel_end; + GtkTextIter start, end; + GtkTextIter match_start, match_end; + gboolean found; + gboolean has_selection; + + g_return_if_fail (GTK_IS_TEXT_VIEW (view)); + + if (!last_search) + return moo_text_view_run_find (view); + + buffer = gtk_text_view_get_buffer (view); + + has_selection = gtk_text_buffer_get_selection_bounds (buffer, &sel_start, &sel_end); + + start = sel_end; + gtk_text_buffer_get_end_iter (buffer, &end); + + found = do_find (&start, &end, last_search_flags & ~MOO_FIND_BACKWARDS, + last_regex, last_search, &match_start, &match_end); + + if (found && !has_selection && + gtk_text_iter_equal (&match_start, &match_end) && + gtk_text_iter_equal (&match_start, &start)) + { + if (!gtk_text_iter_forward_char (&start)) + { + found = FALSE; + } + else + { + found = do_find (&start, &end, last_search_flags & ~MOO_FIND_BACKWARDS, + last_regex, last_search, &match_start, &match_end);; + } + } + + if (!found && !gtk_text_iter_is_start (&sel_start)) + { + gtk_text_buffer_get_start_iter (buffer, &start); + end = sel_start; + found = do_find (&start, &end, last_search_flags & ~MOO_FIND_BACKWARDS, + last_regex, last_search, &match_start, &match_end); + } + + if (found) + { + gtk_text_buffer_select_range (buffer, &match_end, &match_start); + gtk_text_view_scroll_to_mark (view, gtk_text_buffer_get_insert (buffer), + 0.2, FALSE, 0, 0); + } + else + { + GtkTextIter insert; + gtk_text_buffer_get_iter_at_mark (buffer, &insert, + gtk_text_buffer_get_insert (buffer)); + gtk_text_buffer_place_cursor (buffer, &insert); + } +} + + +void +moo_text_view_run_find_prev (GtkTextView *view) +{ + GtkTextBuffer *buffer; + GtkTextIter sel_start, sel_end; + GtkTextIter start, end; + GtkTextIter match_start, match_end; + gboolean found; + gboolean has_selection; + + g_return_if_fail (GTK_IS_TEXT_VIEW (view)); + + if (!last_search) + return moo_text_view_run_find (view); + + buffer = gtk_text_view_get_buffer (view); + + has_selection = gtk_text_buffer_get_selection_bounds (buffer, &sel_start, &sel_end); + + start = sel_start; + gtk_text_buffer_get_start_iter (buffer, &end); + + found = do_find (&start, &end, last_search_flags | MOO_FIND_BACKWARDS, + last_regex, last_search, &match_start, &match_end); + + if (found && !has_selection && + gtk_text_iter_equal (&match_start, &match_end) && + gtk_text_iter_equal (&match_start, &start)) + { + if (!gtk_text_iter_backward_char (&start)) + { + found = FALSE; + } + else + { + found = do_find (&start, &end, last_search_flags | MOO_FIND_BACKWARDS, + last_regex, last_search, &match_start, &match_end);; + } + } + + if (!found && !gtk_text_iter_is_end (&sel_end)) + { + gtk_text_buffer_get_end_iter (buffer, &start); + end = sel_end; + found = do_find (&start, &end, last_search_flags | MOO_FIND_BACKWARDS, + last_regex, last_search, &match_start, &match_end); + } + + if (found) + { + gtk_text_buffer_select_range (buffer, &match_start, &match_end); + gtk_text_view_scroll_to_mark (view, gtk_text_buffer_get_insert (buffer), + 0.2, FALSE, 0, 0); + } + else + { + GtkTextIter insert; + gtk_text_buffer_get_iter_at_mark (buffer, &insert, + gtk_text_buffer_get_insert (buffer)); + gtk_text_buffer_place_cursor (buffer, &insert); + } +} + + +static int +do_replace_silent (GtkTextIter *start, + GtkTextIter *end, + MooFindFlags flags, + const char *text, + EggRegex *regex, + const char *replacement) +{ + MooTextSearchFlags search_flags = 0; + + if (flags & MOO_FIND_CASELESS) + search_flags |= MOO_TEXT_SEARCH_CASELESS; + if (flags & MOO_FIND_WHOLE_WORDS) + search_flags |= MOO_TEXT_SEARCH_WHOLE_WORDS; + + if (regex) + return moo_text_replace_regex_all (start, end, regex, replacement, + flags & MOO_FIND_REPL_LITERAL); + else + return moo_text_replace_all (start, end, text, replacement, search_flags); +} + + +static void +run_replace_silent (GtkTextView *view, + const char *text, + EggRegex *regex, + const char *replacement, + MooFindFlags flags) +{ + GtkTextIter start, end; + GtkTextBuffer *buffer; + int replaced; + + buffer = gtk_text_view_get_buffer (view); + get_search_bounds (buffer, flags, &start, &end); + + replaced = do_replace_silent (&start, &end, flags, text, regex, replacement); + + if (get_search_bounds2 (buffer, flags, &start, &end)) + { + int replaced2; + + if (moo_text_search_from_start_dialog (GTK_WIDGET (view), replaced)) + replaced2 = do_replace_silent (&start, &end, flags, text, regex, replacement); + + moo_text_replaced_n_dialog (GTK_WIDGET (view), replaced2); + } + else + { + moo_text_replaced_n_dialog (GTK_WIDGET (view), replaced); + } +} + + +static MooTextReplaceResponse +replace_func (G_GNUC_UNUSED const char *text, + G_GNUC_UNUSED EggRegex *regex, + G_GNUC_UNUSED const char *replacement, + const GtkTextIter *to_replace_start, + const GtkTextIter *to_replace_end, + gpointer user_data) +{ + GtkTextBuffer *buffer; + int response; + + struct { + GtkTextView *view; + GtkWidget *dialog; + MooTextReplaceResponse response; + } *data = user_data; + + buffer = gtk_text_view_get_buffer (data->view); + gtk_text_buffer_select_range (buffer, to_replace_end, to_replace_start); + gtk_text_view_scroll_to_mark (data->view, gtk_text_buffer_get_insert (buffer), + 0.2, FALSE, 0, 0); + + if (!data->dialog) + data->dialog = moo_text_prompt_on_replace_dialog (GTK_WIDGET (data->view)); + + response = gtk_dialog_run (GTK_DIALOG (data->dialog)); + + switch (response) + { + case MOO_TEXT_REPLACE_SKIP: + case MOO_TEXT_REPLACE_DO_REPLACE: + case MOO_TEXT_REPLACE_ALL: + data->response = response; + break; + + default: + data->response = MOO_TEXT_REPLACE_STOP; + break; + } + + return data->response; +} + + +static MooTextReplaceResponse +do_replace_interactive (GtkTextView *view, + GtkTextIter *start, + GtkTextIter *end, + MooFindFlags flags, + const char *text, + EggRegex *regex, + const char *replacement, + int *replaced) +{ + MooTextSearchFlags search_flags = 0; + + struct { + GtkTextView *view; + GtkWidget *dialog; + MooTextReplaceResponse response; + } data = {view, NULL, MOO_TEXT_REPLACE_DO_REPLACE}; + + if (flags & MOO_FIND_CASELESS) + search_flags |= MOO_TEXT_SEARCH_CASELESS; + if (flags & MOO_FIND_WHOLE_WORDS) + search_flags |= MOO_TEXT_SEARCH_WHOLE_WORDS; + + if (regex) + *replaced = moo_text_replace_regex_all_interactive (start, end, regex, replacement, + flags & MOO_FIND_REPL_LITERAL, + replace_func, &data); + else + *replaced = moo_text_replace_all_interactive (start, end, text, replacement, + search_flags, replace_func, &data); + + if (data.dialog) + gtk_widget_destroy (data.dialog); + + return data.response; +} + + +static void +run_replace_interactive (GtkTextView *view, + const char *text, + EggRegex *regex, + const char *replacement, + MooFindFlags flags) +{ + GtkTextIter start, end; + GtkTextBuffer *buffer; + MooTextReplaceResponse result; + int replaced; + + buffer = gtk_text_view_get_buffer (view); + get_search_bounds (buffer, flags, &start, &end); + + result = do_replace_interactive (view, &start, &end, flags, text, regex, replacement, &replaced); + + if (result != MOO_TEXT_REPLACE_STOP && get_search_bounds2 (buffer, flags, &start, &end)) + { + int replaced2; + + if (moo_text_search_from_start_dialog (GTK_WIDGET (view), replaced)) + do_replace_interactive (view, &start, &end, flags, text, regex, replacement, &replaced2); + + moo_text_replaced_n_dialog (GTK_WIDGET (view), replaced2); + } + else + { + moo_text_replaced_n_dialog (GTK_WIDGET (view), replaced); + } +} + + +void +moo_text_view_run_replace (GtkTextView *view) +{ + GtkWidget *find; + MooFindFlags flags; + char *text, *replacement; + EggRegex *regex; + + g_return_if_fail (GTK_IS_TEXT_VIEW (view)); + + find = moo_find_new (TRUE); + moo_find_setup (MOO_FIND (find), view); + + if (!moo_find_run (MOO_FIND (find))) + { + gtk_widget_destroy (find); + return; + } + + flags = moo_find_get_flags (MOO_FIND (find)); + text = moo_find_get_text (MOO_FIND (find)); + regex = moo_find_get_regex (MOO_FIND (find)); + replacement = moo_find_get_replacement (MOO_FIND (find)); + gtk_widget_destroy (find); + + if (flags & MOO_FIND_DONT_PROMPT) + run_replace_silent (view, text, regex, replacement, flags); + else + run_replace_interactive (view, text, regex, replacement, flags); + + g_free (text); + g_free (replacement); + egg_regex_unref (regex); +} + + +/****************************************************************************/ +/* goto line + */ + +static void update_spin_value (GtkRange *scale, + GtkSpinButton *spin); +static gboolean update_scale_value (GtkSpinButton *spin, + GtkRange *scale); + +static void +update_spin_value (GtkRange *scale, + GtkSpinButton *spin) +{ + double value = gtk_range_get_value (scale); + g_signal_handlers_block_matched (spin, G_SIGNAL_MATCH_FUNC, 0, 0, 0, + (gpointer)update_scale_value, 0); + gtk_spin_button_set_value (spin, value); + g_signal_handlers_unblock_matched (spin, G_SIGNAL_MATCH_FUNC, 0, 0, 0, + (gpointer)update_scale_value, 0); +} + +static gboolean +update_scale_value (GtkSpinButton *spin, + GtkRange *scale) +{ + double value = gtk_spin_button_get_value (spin); + g_signal_handlers_block_matched (scale, G_SIGNAL_MATCH_FUNC, 0, 0, 0, + (gpointer)update_spin_value, 0); + gtk_range_set_value (scale, value); + g_signal_handlers_unblock_matched (scale, G_SIGNAL_MATCH_FUNC, 0, 0, 0, + (gpointer)update_spin_value, 0); + return FALSE; +} + + +void +moo_text_view_run_goto_line (GtkTextView *view) +{ + GtkWidget *dialog; + GtkTextBuffer *buffer; + int line_count, line; + GtkTextIter iter; + GtkRange *scale; + GtkSpinButton *spin; + MooGladeXML *xml; + + g_return_if_fail (GTK_IS_TEXT_VIEW (view)); + + buffer = gtk_text_view_get_buffer (view); + line_count = gtk_text_buffer_get_line_count (buffer); + + xml = moo_glade_xml_new_from_buf (MOO_TEXT_GOTO_LINE_GLADE_UI, + -1, NULL, NULL); + g_return_if_fail (xml != NULL); + + dialog = moo_glade_xml_get_widget (xml, "dialog"); + + gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), + GTK_RESPONSE_OK, + GTK_RESPONSE_CANCEL, + -1); + + gtk_text_buffer_get_iter_at_mark (buffer, &iter, + gtk_text_buffer_get_insert (buffer)); + line = gtk_text_iter_get_line (&iter); + + scale = moo_glade_xml_get_widget (xml, "scale"); + gtk_range_set_range (scale, 1, line_count + 1); + gtk_range_set_value (scale, line + 1); + + spin = moo_glade_xml_get_widget (xml, "spin"); + gtk_entry_set_activates_default (GTK_ENTRY (spin), TRUE); + gtk_spin_button_set_range (spin, 1, line_count); + gtk_spin_button_set_value (spin, line + 1); + gtk_editable_select_region (GTK_EDITABLE (spin), 0, -1); + + g_signal_connect (scale, "value-changed", G_CALLBACK (update_spin_value), spin); + g_signal_connect (spin, "value-changed", G_CALLBACK (update_scale_value), scale); + + gtk_window_set_transient_for (GTK_WINDOW (dialog), + GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view)))); + + moo_glade_xml_unref (xml); + + if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_OK) + { + gtk_widget_destroy (dialog); + return; + } + + line = gtk_spin_button_get_value (spin) - 1; + gtk_widget_destroy (dialog); + + if (MOO_IS_TEXT_VIEW (view)) + { + moo_text_view_move_cursor (MOO_TEXT_VIEW (view), line, 0, FALSE); + } + else + { + gtk_text_buffer_get_iter_at_line (buffer, &iter, line); + gtk_text_buffer_place_cursor (buffer, &iter); + gtk_text_view_scroll_to_mark (view, gtk_text_buffer_get_insert (buffer), + 0.2, FALSE, 0, 0); + } +} diff --git a/moo/mooedit/mootextfind.h b/moo/mooedit/mootextfind.h new file mode 100644 index 00000000..43be5003 --- /dev/null +++ b/moo/mooedit/mootextfind.h @@ -0,0 +1,87 @@ +/* + * mootextfind.h + * + * Copyright (C) 2004-2005 by Yevgen Muntyan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * See COPYING file that comes with this distribution. + */ + +#ifndef __MOO_TEXT_FIND_H__ +#define __MOO_TEXT_FIND_H__ + +#include +#include +#include "mooutils/moohistorylist.h" +#include "mooutils/mooglade.h" +#include "mooutils/eggregex.h" + +G_BEGIN_DECLS + + +#define MOO_TYPE_FIND (moo_find_get_type ()) +#define MOO_FIND(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), MOO_TYPE_FIND, MooFind)) +#define MOO_FIND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MOO_TYPE_FIND, MooFindClass)) +#define MOO_IS_FIND(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), MOO_TYPE_FIND)) +#define MOO_IS_FIND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MOO_TYPE_FIND)) +#define MOO_FIND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MOO_TYPE_FIND, MooFindClass)) + + +typedef struct _MooFind MooFind; +typedef struct _MooFindClass MooFindClass; + +typedef enum { + MOO_FIND_REGEX = 1 << 0, + MOO_FIND_CASELESS = 1 << 1, + MOO_FIND_IN_SELECTED = 1 << 2, + MOO_FIND_BACKWARDS = 1 << 3, + MOO_FIND_WHOLE_WORDS = 1 << 4, + MOO_FIND_FROM_CURSOR = 1 << 5, + MOO_FIND_DONT_PROMPT = 1 << 6, + MOO_FIND_REPL_LITERAL = 1 << 7 +} MooFindFlags; + +struct _MooFind +{ + GtkDialog base; + MooGladeXML *xml; + EggRegex *regex; + guint replace : 1; +}; + +struct _MooFindClass +{ + GtkDialogClass base_class; +}; + + +GType moo_find_get_type (void) G_GNUC_CONST; + +GtkWidget *moo_find_new (gboolean replace); + +void moo_find_setup (MooFind *find, + GtkTextView *view); +gboolean moo_find_run (MooFind *find); + +void moo_find_set_flags (MooFind *find, + MooFindFlags flags); +MooFindFlags moo_find_get_flags (MooFind *find); +/* returned string/regex must be freed/unrefed */ +char *moo_find_get_text (MooFind *find); +EggRegex *moo_find_get_regex (MooFind *find); +char *moo_find_get_replacement (MooFind *find); + +void moo_text_view_run_find (GtkTextView *view); +void moo_text_view_run_replace (GtkTextView *view); +void moo_text_view_run_find_next (GtkTextView *view); +void moo_text_view_run_find_prev (GtkTextView *view); +void moo_text_view_run_goto_line (GtkTextView *view); + + +G_END_DECLS + +#endif /* __MOO_TEXT_FIND_H__ */ diff --git a/moo/mooedit/mootextsearch.c b/moo/mooedit/mootextsearch.c new file mode 100644 index 00000000..cf57e553 --- /dev/null +++ b/moo/mooedit/mootextsearch.c @@ -0,0 +1,842 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4; coding: utf-8 -*- + * + * mootextsearch.c + * + * Copyright (C) 2004-2005 by Yevgen Muntyan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * See COPYING file that comes with this distribution. + */ + +#include "mooedit/mootextsearch.h" +#include "mooedit/gtksourceiter.h" +#include + + +gboolean +moo_text_search_regex_forward (const GtkTextIter *search_start, + const GtkTextIter *search_end, + EggRegex *regex, + GtkTextIter *match_start, + GtkTextIter *match_end, + char **string, + int *match_offset, + int *match_len) +{ + GtkTextIter start, end; + GtkTextBuffer *buffer; + int start_offset; + char *text, *text_start; + + g_return_val_if_fail (search_start != NULL, FALSE); + g_return_val_if_fail (match_start != NULL && match_end != NULL, FALSE); + g_return_val_if_fail (regex != NULL, FALSE); + + buffer = gtk_text_iter_get_buffer (search_start); + + start = *search_start; + start_offset = gtk_text_iter_get_line_offset (&start); + if (start_offset) + gtk_text_iter_set_line_offset (&start, 0); + + end = *search_start; + if (!gtk_text_iter_ends_line (&end)) + gtk_text_iter_forward_to_line_end (&end); + + while (TRUE) + { + text = gtk_text_buffer_get_slice (buffer, &start, &end, TRUE); + text_start = g_utf8_offset_to_pointer (text, start_offset); + + egg_regex_clear (regex); + + if (egg_regex_match_extended (regex, text, -1, text_start - text, 0) > 0) + { + int start_pos, end_pos; + egg_regex_fetch_pos (regex, text, 0, &start_pos, &end_pos); + + *match_start = start; + gtk_text_iter_forward_chars (match_start, g_utf8_pointer_to_offset (text, text + start_pos)); + + if (search_end && gtk_text_iter_compare (match_start, search_end) > 0) + { + g_free (text); + return FALSE; + } + + *match_end = *match_start; + gtk_text_iter_forward_chars (match_end, g_utf8_pointer_to_offset (text + start_pos, text + end_pos)); + + if (string) + *string = text; + else + g_free (text); + + if (match_offset) + *match_offset = start_pos; + if (match_len) + *match_len = end_pos - start_pos; + + return TRUE; + } + + start = end; + start_offset = 0; + + if (!gtk_text_iter_forward_line (&start)) + break; + + if (search_end && gtk_text_iter_compare (&start, search_end) > 0) + break; + + end = start; + + if (!gtk_text_iter_ends_line (&end)) + gtk_text_iter_forward_to_line_end (&end); + } + + return FALSE; +} + + +static gboolean +find_last_match (EggRegex *regex, + const char *text, + EggRegexMatchFlags flags, + int *start_pos, + int *end_pos) +{ + int len, start; + + *start_pos = -1; + egg_regex_clear (regex); + len = strlen (text); + start = 0; + + while (egg_regex_match_extended (regex, text, len, start, flags) > 0) + { + egg_regex_fetch_pos (regex, text, 0, start_pos, end_pos); + start = *start_pos + 1; + if (start >= len) + break; + } + + return *start_pos >= 0; +} + + +gboolean +moo_text_search_regex_backward (const GtkTextIter *search_start, + const GtkTextIter *search_end, + EggRegex *regex, + GtkTextIter *match_start, + GtkTextIter *match_end, + char **string, + int *match_offset, + int *match_len) +{ + GtkTextIter slice_start, slice_end; + GtkTextBuffer *buffer; + char *text; + EggRegexMatchFlags flags; + + g_return_val_if_fail (search_start != NULL, FALSE); + g_return_val_if_fail (match_start != NULL && match_end != NULL, FALSE); + g_return_val_if_fail (regex != NULL, FALSE); + + buffer = gtk_text_iter_get_buffer (search_start); + slice_start = *search_start; + slice_end = slice_start; + gtk_text_iter_backward_line (&slice_start); + flags = 0; + + if (!gtk_text_iter_ends_line (&slice_end)) + flags |= EGG_REGEX_MATCH_NOTEOL; + + while (TRUE) + { + int start_pos, end_pos; + + text = gtk_text_buffer_get_slice (buffer, &slice_start, &slice_end, TRUE); + + if (find_last_match (regex, text, flags, &start_pos, &end_pos)) + { + *match_start = slice_start; + gtk_text_iter_forward_chars (match_start, g_utf8_pointer_to_offset (text, text + start_pos)); + + if (search_end && gtk_text_iter_compare (match_start, search_end) < 0) + { + g_free (text); + return FALSE; + } + + *match_end = *match_start; + gtk_text_iter_forward_chars (match_end, g_utf8_pointer_to_offset (text + start_pos, text + end_pos)); + + if (string) + *string = text; + else + g_free (text); + + if (match_offset) + *match_offset = start_pos; + if (match_len) + *match_len = end_pos - start_pos; + + return TRUE; + } + + slice_end = slice_start; + flags = 0; + + if (gtk_text_iter_is_start (&slice_end)) + break; + + if (search_end && gtk_text_iter_compare (&slice_end, search_end) < 0) + break; + + gtk_text_iter_backward_line (&slice_start); + } + + return FALSE; +} + + +static EggRegex * +get_regex (const char *pattern, + MooTextSearchFlags flags, + GError **error) +{ + static EggRegex *saved_regex; + static char *saved_pattern; + static MooTextSearchFlags saved_flags; + GError *tmp_error = NULL; + + if (!saved_pattern || strcmp (saved_pattern, pattern) || saved_flags != flags) + { + EggRegexCompileFlags re_flags = 0; + + egg_regex_unref (saved_regex); + g_free (saved_pattern); + + saved_pattern = g_strdup (pattern); + saved_flags = flags; + + if (flags & MOO_TEXT_SEARCH_CASELESS) + re_flags |= EGG_REGEX_CASELESS; + + saved_regex = egg_regex_new (saved_pattern, re_flags, 0, &tmp_error); + + if (tmp_error) + { + g_free (saved_pattern); + saved_pattern = NULL; + g_propagate_error (error, tmp_error); + egg_regex_unref (saved_regex); + saved_regex = NULL; + return NULL; + } + + egg_regex_optimize (saved_regex, error); + } + + return saved_regex; +} + + +static gboolean +is_whole_word (const GtkTextIter *start, + const GtkTextIter *end) +{ + GtkTextIter s = *start; + GtkTextIter e = *end; + + gtk_text_iter_order (&s, &e); + + if (!gtk_text_iter_starts_line (&s)) + { + gunichar c; + gtk_text_iter_backward_char (&s); + c = gtk_text_iter_get_char (&s); + if (g_unichar_isalnum (c)) + return FALSE; + } + + if (!gtk_text_iter_ends_line (&e)) + { + gunichar c = gtk_text_iter_get_char (&e); + if (g_unichar_isalnum (c)) + return FALSE; + } + + return TRUE; +} + + +gboolean +moo_text_search_forward (const GtkTextIter *start, + const char *str, + MooTextSearchFlags flags, + GtkTextIter *match_start, + GtkTextIter *match_end, + const GtkTextIter *end) +{ + EggRegex *regex; + GError *error = NULL; + + g_return_val_if_fail (start != NULL, FALSE); + g_return_val_if_fail (str != NULL, FALSE); + g_return_val_if_fail (match_start != NULL && match_end != NULL, FALSE); + + if (!(flags & MOO_TEXT_SEARCH_REGEX)) + { + GtkSourceSearchFlags gs_flags = 0; + GtkTextIter real_end, real_start; + + if (flags & MOO_TEXT_SEARCH_CASELESS) + gs_flags |= GTK_SOURCE_SEARCH_CASE_INSENSITIVE; + + /* http://bugzilla.gnome.org/show_bug.cgi?id=321299 */ + if (!end || gtk_text_iter_is_end (end)) + { + end = NULL; + } + else + { + real_end = *end; + gtk_text_iter_forward_char (&real_end); + end = &real_end; + } + + if (!(flags & MOO_TEXT_SEARCH_WHOLE_WORDS)) + return gtk_source_iter_forward_search (start, str, gs_flags, + match_start, match_end, end); + + real_start = *start; + + while (gtk_source_iter_forward_search (&real_start, str, gs_flags, + match_start, match_end, end)) + { + if (is_whole_word (match_start, match_end)) + return TRUE; + real_start = *match_end; + } + + return FALSE; + } + + regex = get_regex (str, flags, &error); + + if (error) + { + g_warning ("%s: %s", G_STRLOC, error->message); + g_error_free (error); + } + + if (!regex) + return FALSE; + + return moo_text_search_regex_forward (start, end, regex, + match_start, match_end, + NULL, NULL, NULL); +} + + +gboolean +moo_text_search_backward (const GtkTextIter *start, + const char *str, + MooTextSearchFlags flags, + GtkTextIter *match_start, + GtkTextIter *match_end, + const GtkTextIter *end) +{ + EggRegex *regex; + GError *error = NULL; + + g_return_val_if_fail (start != NULL, FALSE); + g_return_val_if_fail (str != NULL, FALSE); + g_return_val_if_fail (match_start != NULL && match_end != NULL, FALSE); + + if (!(flags & MOO_TEXT_SEARCH_REGEX)) + { + GtkSourceSearchFlags gs_flags = 0; + GtkTextIter real_start; + + if (flags & MOO_TEXT_SEARCH_CASELESS) + gs_flags |= GTK_SOURCE_SEARCH_CASE_INSENSITIVE; + + if (!(flags & MOO_TEXT_SEARCH_WHOLE_WORDS)) + return gtk_source_iter_backward_search (start, str, gs_flags, + match_start, match_end, end); + + real_start = *start; + + while (gtk_source_iter_backward_search (&real_start, str, gs_flags, + match_start, match_end, end)) + { + if (is_whole_word (match_start, match_end)) + return TRUE; + real_start = *match_start; + } + + return FALSE; + } + + regex = get_regex (str, flags, &error); + + if (error) + { + g_warning ("%s: %s", G_STRLOC, error->message); + g_error_free (error); + } + + if (!regex) + return FALSE; + + return moo_text_search_regex_backward (start, end, regex, + match_start, match_end, + NULL, NULL, NULL); +} + + +static int +moo_text_replace_regex_all_real (GtkTextIter *start, + GtkTextIter *end, + EggRegex *regex, + const char *replacement, + gboolean replacement_literal, + MooTextReplaceFunc func, + gpointer func_data) +{ + int count = 0; + GtkTextMark *end_mark; + GtkTextBuffer *buffer; + MooTextReplaceResponse response; + gboolean need_end_user_action = FALSE; + char *freeme = NULL; + const char *const_replacement = NULL; + GError *error = NULL; + gboolean was_zero_match = FALSE; + + g_return_val_if_fail (start != NULL, 0); + g_return_val_if_fail (regex != NULL, 0); + g_return_val_if_fail (replacement != NULL, 0); + + if (replacement_literal) + { + const_replacement = replacement; + } + else + { + freeme = egg_regex_try_eval_replacement (regex, replacement, &error); + + if (error) + { + g_warning ("%s: %s", G_STRLOC, error->message); + g_error_free (error); + return 0; + } + + const_replacement = freeme; + } + + buffer = gtk_text_iter_get_buffer (start); + + if (end && !gtk_text_iter_is_end (end)) + { + end_mark = gtk_text_buffer_create_mark (buffer, NULL, end, TRUE); + } + else + { + end = NULL; + end_mark = NULL; + } + + if (func) + { + response = MOO_TEXT_REPLACE_DO_REPLACE; + } + else + { + gtk_text_buffer_begin_user_action (buffer); + need_end_user_action = TRUE; + response = MOO_TEXT_REPLACE_ALL; + } + + while (TRUE) + { + GtkTextIter match_start, match_end; + char *freeme_here = NULL; + const char *real_replacement; + char *string; + GError *error = NULL; + int match_len; + + if (!moo_text_search_regex_forward (start, end, regex, &match_start, &match_end, + &string, NULL, &match_len)) + goto out; + + if (!match_len) + { + if (was_zero_match && gtk_text_iter_equal (&match_start, start)) + { + was_zero_match = FALSE; + g_free (string); + + if (!gtk_text_iter_forward_char (start)) + goto out; + + continue; + } + + was_zero_match = TRUE; + } + else + { + was_zero_match = FALSE; + } + + if (const_replacement) + { + real_replacement = const_replacement; + g_free (string); + } + else + { + freeme_here = egg_regex_eval_replacement (regex, string, replacement, &error); + g_free (string); + + if (!freeme_here) + { + g_warning ("%s: %s", G_STRLOC, error->message); + g_error_free (error); + goto out; + } + + real_replacement = freeme_here; + } + + if (response != MOO_TEXT_REPLACE_ALL) + { + response = func (NULL, regex, real_replacement, &match_start, &match_end, func_data); + + if (!response) + { + g_free (freeme_here); + goto out; + } + } + + if (response != MOO_TEXT_REPLACE_SKIP && (match_len || *real_replacement)) + { + count++; + + if (response == MOO_TEXT_REPLACE_ALL) + { + if (!need_end_user_action) + { + gtk_text_buffer_begin_user_action (buffer); + need_end_user_action = TRUE; + } + } + else + { + gtk_text_buffer_begin_user_action (buffer); + } + + gtk_text_buffer_delete (buffer, &match_start, &match_end); + gtk_text_buffer_insert (buffer, &match_end, real_replacement, -1); + + if (response != MOO_TEXT_REPLACE_ALL) + gtk_text_buffer_end_user_action (buffer); + } + + *start = match_end; + + if (was_zero_match && !*real_replacement) + { + if (gtk_text_iter_is_end (start)) + { + g_free (freeme_here); + goto out; + } + + gtk_text_iter_forward_char (start); + was_zero_match = FALSE; + } + + if (end) + gtk_text_buffer_get_iter_at_mark (buffer, end, end_mark); + + g_free (freeme_here); + } + +out: + if (end_mark) + gtk_text_buffer_delete_mark (buffer, end_mark); + if (need_end_user_action) + gtk_text_buffer_end_user_action (buffer); + g_free (freeme); + return count; +} + + +int +moo_text_replace_regex_all (GtkTextIter *start, + GtkTextIter *end, + EggRegex *regex, + const char *replacement, + gboolean replacement_literal) +{ + g_return_val_if_fail (start != NULL, 0); + g_return_val_if_fail (regex != NULL, 0); + g_return_val_if_fail (replacement != NULL, 0); + + return moo_text_replace_regex_all_real (start, end, regex, replacement, + replacement_literal, NULL, NULL); +} + + +int +moo_text_replace_regex_all_interactive (GtkTextIter *start, + GtkTextIter *end, + EggRegex *regex, + const char *replacement, + gboolean replacement_literal, + MooTextReplaceFunc func, + gpointer func_data) +{ + g_return_val_if_fail (start != NULL, 0); + g_return_val_if_fail (regex != NULL, 0); + g_return_val_if_fail (replacement != NULL, 0); + g_return_val_if_fail (func != NULL, 0); + + return moo_text_replace_regex_all_real (start, end, regex, + replacement, replacement_literal, + func, func_data); +} + + +int +moo_text_replace_all (GtkTextIter *start, + GtkTextIter *end, + const char *text, + const char *replacement, + MooTextSearchFlags flags) +{ + int count = 0; + GtkTextMark *end_mark; + GtkTextBuffer *buffer; + + g_return_val_if_fail (start != NULL, 0); + g_return_val_if_fail (text != NULL, 0); + g_return_val_if_fail (text[0] != 0, 0); + g_return_val_if_fail (replacement != NULL, 0); + + if (flags & MOO_TEXT_SEARCH_REGEX) + { + GError *error = NULL; + EggRegex *regex = get_regex (text, flags, &error); + + if (error) + { + g_warning ("%s: %s", G_STRLOC, error->message); + g_error_free (error); + } + + if (!regex) + return 0; + + return moo_text_replace_regex_all (start, end, regex, replacement, + flags & MOO_TEXT_SEARCH_REPL_LITERAL); + } + + buffer = gtk_text_iter_get_buffer (start); + gtk_text_buffer_begin_user_action (buffer); + + if (!end || gtk_text_iter_is_end (end)) + end = NULL; + else + gtk_text_iter_forward_char (end); + + if (end) + end_mark = gtk_text_buffer_create_mark (buffer, NULL, end, TRUE); + else + end_mark = NULL; + + while (TRUE) + { + GtkTextIter match_start, match_end; + + if (!moo_text_search_forward (start, text, flags, &match_start, &match_end, end)) + goto out; + + count++; + gtk_text_buffer_delete (buffer, &match_start, &match_end); + gtk_text_buffer_insert (buffer, &match_end, replacement, -1); + + *start = match_end; + + if (end) + gtk_text_buffer_get_iter_at_mark (buffer, end, end_mark); + } + +out: + if (end_mark) + gtk_text_buffer_delete_mark (buffer, end_mark); + + gtk_text_buffer_end_user_action (buffer); + return count; +} + + +int +moo_text_replace_all_interactive (GtkTextIter *start, + GtkTextIter *end, + const char *text, + const char *replacement, + MooTextSearchFlags flags, + MooTextReplaceFunc func, + gpointer func_data) +{ + int count = 0; + GtkTextMark *end_mark; + GtkTextBuffer *buffer; + MooTextReplaceResponse response = MOO_TEXT_REPLACE_DO_REPLACE; + gboolean need_end_user_action = FALSE; + + g_return_val_if_fail (start != NULL, 0); + g_return_val_if_fail (text != NULL, 0); + g_return_val_if_fail (text[0] != 0, 0); + g_return_val_if_fail (replacement != NULL, 0); + g_return_val_if_fail (func != NULL, 0); + + if (flags & MOO_TEXT_SEARCH_REGEX) + { + GError *error = NULL; + EggRegex *regex = get_regex (text, flags, &error); + + if (error) + { + g_warning ("%s: %s", G_STRLOC, error->message); + g_error_free (error); + } + + if (!regex) + return 0; + + return moo_text_replace_regex_all_interactive (start, end, regex, replacement, + flags & MOO_TEXT_SEARCH_REPL_LITERAL, + func, func_data); + } + + buffer = gtk_text_iter_get_buffer (start); + + if (!end || gtk_text_iter_is_end (end)) + end = NULL; + else + gtk_text_iter_forward_char (end); + + if (end) + end_mark = gtk_text_buffer_create_mark (buffer, NULL, end, TRUE); + else + end_mark = NULL; + + while (TRUE) + { + GtkTextIter match_start, match_end; + + if (!moo_text_search_forward (start, text, flags, &match_start, &match_end, end)) + goto out; + + if (response != MOO_TEXT_REPLACE_ALL) + { + response = func (text, NULL, replacement, &match_start, &match_end, func_data); + + if (!response) + goto out; + } + + if (response != MOO_TEXT_REPLACE_SKIP) + { + count++; + + if (response == MOO_TEXT_REPLACE_ALL) + { + if (!need_end_user_action) + { + gtk_text_buffer_begin_user_action (buffer); + need_end_user_action = TRUE; + } + } + else + { + gtk_text_buffer_begin_user_action (buffer); + } + + gtk_text_buffer_delete (buffer, &match_start, &match_end); + gtk_text_buffer_insert (buffer, &match_end, replacement, -1); + + if (response != MOO_TEXT_REPLACE_ALL) + gtk_text_buffer_end_user_action (buffer); + } + + *start = match_end; + + if (end) + gtk_text_buffer_get_iter_at_mark (buffer, end, end_mark); + } + +out: + if (end_mark) + gtk_text_buffer_delete_mark (buffer, end_mark); + if (need_end_user_action) + gtk_text_buffer_end_user_action (buffer); + return count; +} + + +GType +moo_text_search_flags_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GFlagsValue values[] = { + { MOO_TEXT_SEARCH_CASELESS, (char*)"MOO_TEXT_SEARCH_CASELESS", (char*)"caseless" }, + { MOO_TEXT_SEARCH_REGEX, (char*)"MOO_TEXT_SEARCH_REGEX", (char*)"regex" }, + { 0, NULL, NULL } + }; + + type = g_flags_register_static ("MooTextSearchFlags", values); + } + + return type; +} + + +GType +moo_text_replace_response_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GFlagsValue values[] = { + { MOO_TEXT_REPLACE_STOP, (char*)"MOO_TEXT_REPLACE_STOP", (char*)"stop" }, + { MOO_TEXT_REPLACE_SKIP, (char*)"MOO_TEXT_REPLACE_SKIP", (char*)"skip" }, + { MOO_TEXT_REPLACE_DO_REPLACE, (char*)"MOO_TEXT_REPLACE_DO_REPLACE", (char*)"do-replace" }, + { MOO_TEXT_REPLACE_ALL, (char*)"MOO_TEXT_REPLACE_ALL", (char*)"all" }, + { 0, NULL, NULL } + }; + + type = g_flags_register_static ("MooTextReplaceResponse", values); + } + + return type; +} diff --git a/moo/mooedit/mootextsearch.h b/moo/mooedit/mootextsearch.h new file mode 100644 index 00000000..7f91191d --- /dev/null +++ b/moo/mooedit/mootextsearch.h @@ -0,0 +1,113 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4; coding: utf-8 -*- + * + * mootextsearch.h + * + * Copyright (C) 2004-2005 by Yevgen Muntyan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * See COPYING file that comes with this distribution. + */ + +#ifndef __MOO_TEXT_SEARCH_H__ +#define __MOO_TEXT_SEARCH_H__ + +#include +#include + +G_BEGIN_DECLS + + +#define MOO_TYPE_TEXT_SEARCH_FLAGS (moo_text_search_flags_get_type ()) +#define MOO_TYPE_TEXT_REPLACE_RESPONSE (moo_text_replace_response_get_type ()) +GType moo_text_search_flags_get_type (void) G_GNUC_CONST; +GType moo_text_replace_response_get_type (void) G_GNUC_CONST; + + +typedef enum { + MOO_TEXT_SEARCH_CASELESS = 1 << 0, + MOO_TEXT_SEARCH_REGEX = 1 << 1, + MOO_TEXT_SEARCH_WHOLE_WORDS = 1 << 2, + MOO_TEXT_SEARCH_REPL_LITERAL = 1 << 3 +} MooTextSearchFlags; + +typedef enum { + MOO_TEXT_REPLACE_STOP = 0, + MOO_TEXT_REPLACE_SKIP = 1, + MOO_TEXT_REPLACE_DO_REPLACE = 2, + MOO_TEXT_REPLACE_ALL = 3 +} MooTextReplaceResponse; + +/* replacement is evaluated in case of regex */ +typedef MooTextReplaceResponse (*MooTextReplaceFunc) (const char *text, + EggRegex *regex, + const char *replacement, + const GtkTextIter *to_replace_start, + const GtkTextIter *to_replace_end, + gpointer user_data); + + +gboolean moo_text_search_forward (const GtkTextIter *start, + const char *str, + MooTextSearchFlags flags, + GtkTextIter *match_start, + GtkTextIter *match_end, + const GtkTextIter *end); +gboolean moo_text_search_backward (const GtkTextIter *start, + const char *str, + MooTextSearchFlags flags, + GtkTextIter *match_start, + GtkTextIter *match_end, + const GtkTextIter *end); + +gboolean moo_text_search_regex_forward (const GtkTextIter *start, + const GtkTextIter *end, + EggRegex *regex, + GtkTextIter *match_start, + GtkTextIter *match_end, + char **string, + int *match_offset, + int *match_len); +gboolean moo_text_search_regex_backward (const GtkTextIter *start, + const GtkTextIter *end, + EggRegex *regex, + GtkTextIter *match_start, + GtkTextIter *match_end, + char **string, + int *match_offset, + int *match_len); + +int moo_text_replace_regex_all (GtkTextIter *start, + GtkTextIter *end, + EggRegex *regex, + const char *replacement, + gboolean replacement_literal); +int moo_text_replace_all (GtkTextIter *start, + GtkTextIter *end, + const char *text, + const char *replacement, + MooTextSearchFlags flags); + +int moo_text_replace_regex_all_interactive + (GtkTextIter *start, + GtkTextIter *end, + EggRegex *regex, + const char *replacement, + gboolean replacement_literal, + MooTextReplaceFunc func, + gpointer func_data); +int moo_text_replace_all_interactive (GtkTextIter *start, + GtkTextIter *end, + const char *text, + const char *replacement, + MooTextSearchFlags flags, + MooTextReplaceFunc func, + gpointer func_data); + + +G_END_DECLS + +#endif /* __MOO_TEXT_SEARCH_H__ */ diff --git a/moo/mooedit/mootextview-private.h b/moo/mooedit/mootextview-private.h index 31fdc982..3f2cb233 100644 --- a/moo/mooedit/mootextview-private.h +++ b/moo/mooedit/mootextview-private.h @@ -19,7 +19,6 @@ #ifndef __MOO_TEXT_VIEW_PRIVATE_H__ #define __MOO_TEXT_VIEW_PRIVATE_H__ -#include "mooedit/mooeditsearch.h" #include "mooedit/mootextview.h" #include "mooutils/gtksourceundomanager.h" #include "mooutils/moohistorylist.h" @@ -62,23 +61,6 @@ typedef enum { } MooTextViewDragType; -typedef struct { - int last_search_stamp; - char *text; - char *replace_with; - gboolean regex; - gboolean case_sensitive; - gboolean backwards; - gboolean whole_words; - gboolean from_cursor; - gboolean dont_prompt_on_replace; - MooHistoryList *text_to_find_history; - MooHistoryList *replacement_history; -} MooTextSearchParams; - -extern MooTextSearchParams *_moo_text_search_params; - - struct _MooTextViewPrivate { gboolean constructed; diff --git a/moo/mooedit/mootextview.c b/moo/mooedit/mootextview.c index 29bcc26b..399bb4ed 100644 --- a/moo/mooedit/mootextview.c +++ b/moo/mooedit/mootextview.c @@ -16,6 +16,7 @@ #include "mooedit/mootextview-private.h" #include "mooedit/mootextview.h" #include "mooedit/mootextbuffer.h" +#include "mooedit/mootextfind.h" #include "mooutils/moomarshals.h" #include "mooutils/mooutils-gobject.h" #include @@ -23,10 +24,6 @@ #define LIGHT_BLUE "#EEF6FF" -MooTextSearchParams *_moo_text_search_params; -static MooTextSearchParams search_params; - - static GObject *moo_text_view_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_param); @@ -60,7 +57,11 @@ static void cursor_moved (MooTextView *view, static void proxy_prop_notify (MooTextView *view, GParamSpec *pspec); -static void goto_line (MooTextView *view); +static void find_interactive (MooTextView *view); +static void replace_interactive (MooTextView *view); +static void find_next_interactive (MooTextView *view); +static void find_prev_interactive (MooTextView *view); +static void goto_line_interactive (MooTextView *view); static void insert_text_cb (MooTextView *view, GtkTextIter *iter, @@ -114,11 +115,6 @@ static void moo_text_view_class_init (MooTextViewClass *klass) GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); GtkTextViewClass *text_view_class = GTK_TEXT_VIEW_CLASS (klass); - _moo_text_search_params = &search_params; - search_params.last_search_stamp = -1; - search_params.text_to_find_history = moo_history_list_new (NULL); - search_params.replacement_history = moo_history_list_new (NULL); - gobject_class->set_property = moo_text_view_set_property; gobject_class->get_property = moo_text_view_get_property; gobject_class->constructor = moo_text_view_constructor; @@ -137,11 +133,11 @@ static void moo_text_view_class_init (MooTextViewClass *klass) klass->delete_selection = moo_text_view_delete_selection; klass->extend_selection = _moo_text_view_extend_selection; - klass->find_interactive = _moo_text_view_find; - klass->find_next_interactive = _moo_text_view_find_next; - klass->find_prev_interactive = _moo_text_view_find_previous; - klass->replace_interactive = _moo_text_view_replace; - klass->goto_line_interactive = goto_line; + klass->find_interactive = find_interactive; + klass->find_next_interactive = find_next_interactive; + klass->find_prev_interactive = find_prev_interactive; + klass->replace_interactive = replace_interactive; + klass->goto_line_interactive = goto_line_interactive; klass->undo = moo_text_view_undo; klass->redo = moo_text_view_redo; klass->char_inserted = moo_text_view_char_inserted; @@ -434,38 +430,34 @@ moo_text_view_delete_selection (MooTextView *view) } -void -moo_text_view_find_interactive (MooTextView *view) +static void +find_interactive (MooTextView *view) { - g_return_if_fail (MOO_IS_TEXT_VIEW (view)); - g_signal_emit (view, signals[FIND_INTERACTIVE], 0, NULL); -} - -void -moo_text_view_replace_interactive (MooTextView *view) -{ - g_return_if_fail (MOO_IS_TEXT_VIEW (view)); - g_signal_emit (view, signals[REPLACE_INTERACTIVE], 0, NULL); -} - -void -moo_text_view_find_next_interactive (MooTextView *view) -{ - g_return_if_fail (MOO_IS_TEXT_VIEW (view)); - g_signal_emit (view, signals[FIND_NEXT_INTERACTIVE], 0, NULL); -} - -void -moo_text_view_find_prev_interactive (MooTextView *view) -{ - g_return_if_fail (MOO_IS_TEXT_VIEW (view)); - g_signal_emit (view, signals[FIND_PREV_INTERACTIVE], 0, NULL); + moo_text_view_run_find (GTK_TEXT_VIEW (view)); } static void -goto_line (MooTextView *view) +replace_interactive (MooTextView *view) { - moo_text_view_goto_line (view, -1); + moo_text_view_run_replace (GTK_TEXT_VIEW (view)); +} + +static void +find_next_interactive (MooTextView *view) +{ + moo_text_view_run_find_next (GTK_TEXT_VIEW (view)); +} + +static void +find_prev_interactive (MooTextView *view) +{ + moo_text_view_run_find_prev (GTK_TEXT_VIEW (view)); +} + +static void +goto_line_interactive (MooTextView *view) +{ + moo_text_view_run_goto_line (GTK_TEXT_VIEW (view)); } @@ -539,12 +531,12 @@ void moo_text_view_redo (MooTextView *view) { g_return_if_fail (MOO_IS_TEXT_VIEW (view)); -// _moo_text_buffer_freeze_highlight (get_moo_buffer (view)); + moo_text_buffer_freeze (get_moo_buffer (view)); gtk_source_undo_manager_redo (view->priv->undo_mgr); gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (view), get_insert (view), 0, FALSE, 0, 0); -// _moo_text_buffer_thaw_highlight (get_moo_buffer (view)); + moo_text_buffer_thaw (get_moo_buffer (view)); } @@ -552,12 +544,12 @@ void moo_text_view_undo (MooTextView *view) { g_return_if_fail (MOO_IS_TEXT_VIEW (view)); -// _moo_text_buffer_freeze_highlight (get_moo_buffer (view)); + moo_text_buffer_freeze (get_moo_buffer (view)); gtk_source_undo_manager_undo (view->priv->undo_mgr); gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (view), get_insert (view), 0, FALSE, 0, 0); -// _moo_text_buffer_thaw_highlight (get_moo_buffer (view)); + moo_text_buffer_thaw (get_moo_buffer (view)); } @@ -1436,29 +1428,28 @@ moo_text_view_strip_whitespace (MooTextView *view) gtk_text_iter_forward_line (&iter)) { GtkTextIter end; + char *slice, *p; + int len; if (gtk_text_iter_ends_line (&iter)) continue; - gtk_text_iter_forward_to_line_end (&iter); end = iter; + gtk_text_iter_forward_to_line_end (&end); - do + slice = gtk_text_buffer_get_slice (buffer, &iter, &end, TRUE); + len = strlen (slice); + g_assert (len > 0); + + for (p = slice + len; p > slice && (p[-1] == ' ' || p[-1] == '\t'); --p) ; + + if (*p) { - gunichar c; - - gtk_text_iter_backward_char (&iter); - c = gtk_text_iter_get_char (&iter); - - if (!g_unichar_isspace (c)) - { - gtk_text_iter_forward_char (&iter); - break; - } + gtk_text_iter_forward_chars (&iter, g_utf8_pointer_to_offset (slice, p)); + gtk_text_buffer_delete (buffer, &iter, &end); } - while (!gtk_text_iter_starts_line (&iter)); - gtk_text_buffer_delete (buffer, &iter, &end); + g_free (slice); } gtk_text_buffer_end_user_action (buffer); diff --git a/moo/mooedit/mootextview.h b/moo/mooedit/mootextview.h index 24926126..ab81a5a9 100644 --- a/moo/mooedit/mootextview.h +++ b/moo/mooedit/mootextview.h @@ -100,10 +100,6 @@ void moo_text_view_undo (MooTextView *view); void moo_text_view_start_not_undoable_action(MooTextView *view); void moo_text_view_end_not_undoable_action (MooTextView *view); -void moo_text_view_find_interactive (MooTextView *view); -void moo_text_view_replace_interactive (MooTextView *view); -void moo_text_view_find_next_interactive (MooTextView *view); -void moo_text_view_find_prev_interactive (MooTextView *view); void moo_text_view_goto_line (MooTextView *view, int line); diff --git a/moo/mooedit/syntax/c.lang b/moo/mooedit/syntax/c.lang index a9c8d96c..07afac96 100644 --- a/moo/mooedit/syntax/c.lang +++ b/moo/mooedit/syntax/c.lang @@ -88,11 +88,11 @@ - + - + @@ -111,7 +111,7 @@ - + @@ -137,7 +137,6 @@