/* Screem: screem-search.c * * Copyright (C) 2003 David A Knight * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "config.h" #include #include #include #include #include #include #include "screemmarshal.h" #include "screem-page.h" #include "screem-search.h" #include "screem-window.h" #include "fileops.h" #include "support.h" #include "screem-application.h" #include "screem-icon-cache.h" extern ScreemApplication *app; struct ScreemSearchPrivate { gint found_at; /* this is a byte offset */ glong found_c; /* this is a utf8 char offset */ guint length; /* this is the byte length */ glong length_c; /* this is the utf8 char length */ gboolean matches; gboolean regexp; gchar *find_pattern; gchar *replace_pattern; size_t nmatch; regmatch_t *pmatch; ScreemSearchType search_type; ScreemSearchFrom search_from; GtkWidget *dialog; GtkListStore *match_model; ScreemWindow *window; ScreemView *editor; ScreemIconCache *cache; }; typedef enum { SCREEM_SEARCH_MATCH_PATH = 0, SCREEM_SEARCH_MATCH_PIXBUF, SCREEM_SEARCH_MATCH_OBJECT, SCREEM_SEARCH_MATCH_COLS } MatchCols; enum { FOUND, NOT_FOUND, LAST_SIGNAL }; enum { PROP_0, PROP_WINDOW, PROP_EDITOR }; static guint screem_search_signals[ LAST_SIGNAL ] = { 0 }; G_DEFINE_TYPE( ScreemSearch, screem_search, G_TYPE_OBJECT ) static void screem_search_finalize( GObject *object ); static void screem_search_set_prop( GObject *object, guint prop_id, const GValue *value, GParamSpec *spec ); static void screem_search_get_prop( GObject *object, guint prop_id, GValue *value, GParamSpec *spec ); static void screem_search_class_init( ScreemSearchClass *klass ) { GObjectClass *obj_class; GParamSpec *pspec; obj_class = G_OBJECT_CLASS( klass ); obj_class->finalize = screem_search_finalize; obj_class->get_property = screem_search_get_prop; obj_class->set_property = screem_search_set_prop; screem_search_signals[ FOUND ] = g_signal_new( "found", G_OBJECT_CLASS_TYPE( obj_class ), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET( ScreemSearchClass, found ), NULL, NULL, screem_marshal_VOID__OBJECT_UINT_UINT, G_TYPE_NONE, 3, G_TYPE_OBJECT, G_TYPE_UINT, G_TYPE_UINT ); screem_search_signals[ NOT_FOUND ] = g_signal_new( "notfound", G_OBJECT_CLASS_TYPE( obj_class ), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET( ScreemSearchClass, notfound ), NULL, NULL, screem_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT ); pspec = g_param_spec_pointer( "window", "window", "window", G_PARAM_READABLE | G_PARAM_WRITABLE ); g_object_class_install_property( G_OBJECT_CLASS( obj_class ), PROP_WINDOW, pspec ); pspec = g_param_spec_pointer( "editor", "editor", "editor", G_PARAM_READABLE | G_PARAM_WRITABLE ); g_object_class_install_property( G_OBJECT_CLASS( obj_class ), PROP_EDITOR, pspec ); } static void screem_search_init( ScreemSearch *search ) { ScreemSearchPrivate *priv; priv = search->priv = g_new0( ScreemSearchPrivate, 1 ); priv->found_at = -1; priv->match_model = gtk_list_store_new( SCREEM_SEARCH_MATCH_COLS, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_POINTER, NULL ); priv->cache = screem_application_get_icon_cache( app ); } static void screem_search_finalize( GObject *object ) { ScreemSearch *search; ScreemSearchPrivate *priv; g_return_if_fail( object != NULL ); g_return_if_fail( SCREEM_IS_SEARCH( object ) ); search = SCREEM_SEARCH( object ); priv = search->priv; if( priv->dialog ) { gtk_widget_destroy( priv->dialog ); } g_free( priv->find_pattern ); g_free( priv->replace_pattern ); if( priv->pmatch ) { g_free( priv->pmatch ); } if( priv->cache ) { g_object_unref( G_OBJECT( priv->cache ) ); } g_free( priv ); G_OBJECT_CLASS( screem_search_parent_class )->finalize( object ); } static void screem_search_set_prop( GObject *object, guint prop_id, const GValue *value, GParamSpec *spec ) { ScreemSearch *search; ScreemSearchPrivate *priv; search = SCREEM_SEARCH( object ); priv = search->priv; switch( prop_id ) { case PROP_WINDOW: priv->window = g_value_get_pointer( value ); break; case PROP_EDITOR: priv->editor = g_value_get_pointer( value ); break; default: break; } } static void screem_search_get_prop( GObject *object, guint prop_id, GValue *value, GParamSpec *spec ) { ScreemSearch *search; ScreemSearchPrivate *priv; search = SCREEM_SEARCH( object ); priv = search->priv; switch( prop_id ) { default: break; } } /* static stuff */ static void screem_search_change_file( GtkTreeView *view, GtkTreePath *path, GtkTreeViewColumn *col, gpointer data ) { ScreemSearch *search; GtkTreeModel *model; GtkTreeIter it; gchar *pathname; ScreemWindow *window; ScreemSite *site; ScreemPage *page; g_return_if_fail( SCREEM_IS_SEARCH( data ) ); search = SCREEM_SEARCH( data ); g_return_if_fail( SCREEM_IS_WINDOW( search->priv->window ) ); window = SCREEM_WINDOW( search->priv->window ); model = gtk_tree_view_get_model( view ); gtk_tree_model_get_iter( model, &it, path ); gtk_tree_model_get( model, &it, SCREEM_SEARCH_MATCH_PATH, &pathname, -1 ); site = screem_window_get_current( window ); page = screem_site_locate_page( site, pathname ); g_free( pathname ); if( page ) { screem_window_set_document( window, page ); search->priv->found_at = -1; screem_search_do_find( search ); } } static void screem_search_setup_match_view( ScreemSearch *search ) { GladeXML *xml; GtkWidget *widget; GtkCellRenderer *renderer; GtkTreeViewColumn *col; g_return_if_fail( SCREEM_IS_SEARCH( search ) ); g_return_if_fail( search->priv->dialog != NULL ); xml = glade_get_widget_tree( search->priv->dialog ); widget = glade_xml_get_widget( xml, "match_list" ); col = gtk_tree_view_column_new(); gtk_tree_view_column_set_title( col, "File" ); renderer = gtk_cell_renderer_pixbuf_new(); gtk_tree_view_column_pack_start( col, renderer, FALSE ); gtk_tree_view_column_set_attributes( col, renderer, "pixbuf", SCREEM_SEARCH_MATCH_PIXBUF, NULL ); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_column_pack_start( col, renderer, TRUE ); gtk_tree_view_column_set_resizable( col, TRUE ); gtk_tree_view_append_column( GTK_TREE_VIEW( widget ), col ); gtk_tree_view_column_set_attributes( col, renderer, "text", SCREEM_SEARCH_MATCH_PATH, NULL ); g_signal_connect( G_OBJECT( widget ), "row_activated", G_CALLBACK( screem_search_change_file ), search ); gtk_tree_view_set_model( GTK_TREE_VIEW( widget ), GTK_TREE_MODEL( search->priv->match_model ) ); } static void screem_search_add_match( ScreemSearch *search, ScreemPage *page ) { ScreemSearchPrivate *priv; GtkListStore *store; GtkTreeIter it; GdkPixbuf *pixbuf; gchar *temp; const gchar *path; g_return_if_fail( SCREEM_IS_SEARCH( search ) ); priv = search->priv; store = GTK_LIST_STORE( priv->match_model ); path = screem_page_get_pathname( page ); pixbuf = NULL; if( path ) { temp = gnome_vfs_unescape_string_for_display( path ); pixbuf = screem_icon_cache_get_pixbuf( priv->cache, path, NULL, 16 ); } else { temp = g_strdup( _( "Untitled" ) ); } /* use large pos value to ensure append */ gtk_list_store_insert_with_values( GTK_LIST_STORE( store ), &it, G_MAXINT32, SCREEM_SEARCH_MATCH_PATH, temp, SCREEM_SEARCH_MATCH_PIXBUF, pixbuf, SCREEM_SEARCH_MATCH_OBJECT, search, -1 ); g_free( temp ); if( pixbuf ) { g_object_unref( pixbuf ); } priv->matches = TRUE; } static void screem_search_reset( ScreemSearch *search ) { ScreemSearchPrivate *priv; g_return_if_fail( SCREEM_IS_SEARCH( search ) ); priv = search->priv; priv->found_at = -1; priv->length = 0; } static void screem_search_has_match( gpointer data, ScreemSearch *search ) { ScreemSearchPrivate *priv; ScreemWindow *window; ScreemSite *site; ScreemPage *page; const gchar *path; gchar *text; GError *error; gchar *temp; gchar *primary; gboolean loaded; g_return_if_fail( SCREEM_IS_SEARCH( search ) ); g_return_if_fail( SCREEM_IS_PAGE( data ) ); g_return_if_fail( SCREEM_IS_WINDOW( search->priv->window ) ); priv = search->priv; window = priv->window; site = screem_window_get_current( window ); page = SCREEM_PAGE( data ); path = screem_page_get_pathname( page ); /* is path excluded? */ error = NULL; loaded = screem_page_is_loaded( page ) || screem_page_is_open( page ); if( ( ! screem_site_is_excluded( site, path ) ) ) { if( loaded ) { text = screem_page_get_data( page ); } else { text = load_file( path, NULL, NULL, NULL ); } /* we don't care about submatches here, we only want to know if there is a match */ if( screem_search_static( text, priv->find_pattern, priv->regexp, 0, &priv->length, NULL, NULL ) != -1 ) { screem_search_add_match( search, page ); } g_free( text ); } else if( error ) { temp = gnome_vfs_unescape_string_for_display( path ); primary = g_strdup_printf( _( "Failed to load: %s" ), temp ); screem_hig_alert( GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, primary, error->message, GTK_WIDGET( window ) ); g_free( primary ); g_free( temp ); g_error_free( error ); } } static gboolean screem_search_find_change( GtkWidget *widget, GdkEventFocus *event, ScreemSearch *search ) { const gchar *pattern; g_return_val_if_fail( SCREEM_IS_SEARCH( search ), FALSE ); pattern = gtk_entry_get_text( GTK_ENTRY( widget ) ); screem_search_set_find_pattern( search, pattern ); return FALSE; } static void screem_search_find_activate( GtkWidget *widget, ScreemSearch *search ) { g_return_if_fail( SCREEM_IS_SEARCH( search ) ); screem_search_find_change( widget, NULL, search ); } static gboolean screem_search_replace_change( GtkWidget *widget, GdkEventFocus *event, ScreemSearch *search ) { const gchar *pattern; g_return_val_if_fail( SCREEM_IS_SEARCH( search ), FALSE ); pattern = gtk_entry_get_text( GTK_ENTRY( widget ) ); screem_search_set_replace_pattern( search, pattern ); return FALSE; } static void screem_search_regexp_toggle( GtkToggleButton *widget, ScreemSearch *search ) { gboolean flag; g_return_if_fail( SCREEM_IS_SEARCH( search ) ); flag = gtk_toggle_button_get_active( widget ); screem_search_set_regexp( search, flag ); } static void screem_search_all_toggle( GtkToggleButton *widget, ScreemSearch *search ) { GladeXML *xml; g_return_if_fail( SCREEM_IS_SEARCH( search ) ); xml = glade_get_widget_tree( GTK_WIDGET( widget ) ); widget = GTK_TOGGLE_BUTTON( glade_xml_get_widget( xml, "check_all" ) ); if( ! gtk_toggle_button_get_active( widget ) ) { screem_search_set_search_type( search, SCREEM_SEARCH_SINGLE ); } else { widget = GTK_TOGGLE_BUTTON( glade_xml_get_widget( xml, "all_current" ) ); if( gtk_toggle_button_get_active( widget ) ) { screem_search_set_search_type( search, SCREEM_SEARCH_SITE ); } else { screem_search_set_search_type( search, SCREEM_SEARCH_OPEN ); } } } static void screem_search_pos_toggle( GtkToggleButton *widget, ScreemSearch *search ) { GladeXML *xml; g_return_if_fail( SCREEM_IS_SEARCH( search ) ); xml = glade_get_widget_tree( GTK_WIDGET( widget ) ); widget = GTK_TOGGLE_BUTTON( glade_xml_get_widget( xml, "pos_start" ) ); if( gtk_toggle_button_get_active( widget ) ) { screem_search_set_from( search, SCREEM_SEARCH_START, TRUE ); } else { screem_search_set_from( search, SCREEM_SEARCH_CURRENT, TRUE ); } } static void screem_search_do_replace_all( ScreemSearch *search ) { ScreemSearchPrivate *priv; g_return_if_fail( SCREEM_IS_SEARCH( search ) ); screem_search_set_from( search, SCREEM_SEARCH_START, TRUE ); priv = search->priv; for( screem_search_do_find( search ); priv->found_at != -1; ) { screem_search_do_replace( search ); } } /* public stuff */ /** * find_text: * * @string: the string to search * @find: the string to search for * @replace: the string to replace find with, NULL if not replacing * @length: a pointer to the place to store the length of the match, * NULL if not wanted. * * Find the string find in string, and replace with replace if given, * storing the length of the match in length, if given * * return: a pointer to the found position, or the new text if a replace was * performed */ gchar *find_text( const gchar *string, const gchar *find, const gchar *replace, guint *length ) { gchar *chunk1; gchar *chunk2; gchar *newChunk; gchar *text = NULL; gint found_at; guint find_length; /* find_text is called from code rather user based search, no need for submatches yet */ found_at = screem_search_static( string, find, TRUE, 0, &find_length, NULL, NULL ); if( found_at == -1 ) { return NULL; } /* replace the text if needed */ if( replace ) { chunk1 = g_strndup( string, found_at ); chunk2 = g_strdup( string + found_at + find_length ); newChunk = g_strconcat( chunk1, replace, chunk2, NULL ); g_free( chunk1 ); g_free( chunk2 ); text = newChunk; } else { text = (gchar*)string + found_at; } if( length ) { *length = find_length; } return text; } /** * screem_search_static: * * @text: the text to search * @find: the string to find * @start: offset in string to start searching at * @use_regex: use regexp * @length: a pointer to an integer to store the length of the match in, * can be NULL if the value isn't interested in * @nmatch: out value, number of submatches in the regexp * @pmatch: out value, positions of matches * * finds a string inside a piece of text * * returns: the position in text that find was found */ gint screem_search_static( const gchar *text, const gchar *find, gboolean use_regexp, gint start, guint *length, size_t *nmatch, regmatch_t **pmatch ) { gint retval = start; g_return_val_if_fail( text != NULL, -1 ); g_return_val_if_fail( find != NULL, -1 ); g_assert( length != NULL ); if( use_regexp ) { int failed; regex_t *expressions; size_t junk; regmatch_t *pjunk; if( ! nmatch ) { nmatch = &junk; } if( ! pmatch ) { pmatch = &pjunk; } expressions = g_new0( regex_t, 1 ); failed = ( regcomp( expressions, find, REG_EXTENDED | REG_NEWLINE ) ); *pmatch = NULL; if( ! failed ) { *nmatch = (expressions)->re_nsub + 1; *pmatch = g_new0( regmatch_t, *nmatch ); failed = regexec( expressions, text + start, *nmatch, *pmatch, 0 ); } if( failed ) { retval = -1; *length = 0; g_free( *pmatch ); *pmatch = NULL; } else { retval += (*pmatch)[ 0 ].rm_so; *length = (*pmatch)[0].rm_eo - (*pmatch)[0].rm_so; if( pmatch == &pjunk ) { g_free( *pmatch ); } } regfree( expressions ); g_free( expressions ); } else { /* not using regular expressions */ gchar *pos; pos = strstr( text + start, find ); if( ! pos ) { retval = -1; } else { retval = pos - text; } *length = strlen( find ); } return retval; } ScreemSearch *screem_search_new( void ) { ScreemSearch *search; search = g_object_new( SCREEM_TYPE_SEARCH, NULL ); return search; } void screem_search_set_search_type( ScreemSearch *search, ScreemSearchType type ) { g_return_if_fail( SCREEM_IS_SEARCH( search ) ); g_return_if_fail( type >= SCREEM_SEARCH_SINGLE && type <= SCREEM_SEARCH_OPEN ); search->priv->search_type = type; } ScreemSearchType screem_search_get_search_type( const ScreemSearch *search ) { g_return_val_if_fail( SCREEM_IS_SEARCH( search ), SCREEM_SEARCH_SINGLE ); return search->priv->search_type; } void screem_search_set_from( ScreemSearch *search, ScreemSearchFrom from, gboolean reset ) { GladeXML *xml; GtkWidget *widget; g_return_if_fail( SCREEM_IS_SEARCH( search ) ); g_return_if_fail( from >= SCREEM_SEARCH_START && from <= SCREEM_SEARCH_CURRENT ); search->priv->search_from = from; if( reset ) { search->priv->found_at = -1; if( search->priv->dialog ) { xml = glade_get_widget_tree( search->priv->dialog ); widget = glade_xml_get_widget( xml, "replace" ); gtk_widget_set_sensitive( widget, FALSE ); } } } ScreemSearchFrom screem_search_get_from( const ScreemSearch *search ) { g_return_val_if_fail( SCREEM_IS_SEARCH( search ), SCREEM_SEARCH_START ); return search->priv->search_from; } void screem_search_set_regexp( ScreemSearch *search, gboolean flag ) { g_return_if_fail( SCREEM_IS_SEARCH( search ) ); search->priv->regexp = flag; } gboolean screem_search_get_regexp( const ScreemSearch *search ) { g_return_val_if_fail( SCREEM_IS_SEARCH( search ), FALSE ); return search->priv->regexp; } void screem_search_set_find_pattern( ScreemSearch *search, const gchar *pattern ) { g_return_if_fail( SCREEM_IS_SEARCH( search ) ); g_free( search->priv->find_pattern ); if( pattern ) { search->priv->find_pattern = g_strdup( pattern ); } else { search->priv->find_pattern = NULL; } } gchar *screem_search_get_find_pattern( const ScreemSearch *search ) { gchar *ret; g_return_val_if_fail( SCREEM_IS_SEARCH( search ), NULL ); ret = NULL; if( search->priv->find_pattern ) { ret = g_strdup( search->priv->find_pattern ); } return ret; } void screem_search_set_replace_pattern( ScreemSearch *search, const gchar *pattern ) { g_return_if_fail( SCREEM_IS_SEARCH( search ) ); g_free( search->priv->replace_pattern ); if( pattern ) { search->priv->replace_pattern = g_strdup( pattern ); } else { search->priv->replace_pattern = NULL; } } gchar *screem_search_get_replace_pattern( const ScreemSearch *search ) { gchar *ret; g_return_val_if_fail( SCREEM_IS_SEARCH( search ), NULL ); ret = NULL; if( search->priv->replace_pattern ) { ret = g_strdup( search->priv->replace_pattern ); } return ret; } void screem_search_show_dialog( ScreemSearch *search, gboolean doreplace ) { GladeXML *xml; GtkWidget *widget; gchar *pattern; guint start; guint end; GtkTextIter it; GtkTextIter eit; ScreemPage *page; g_return_if_fail( SCREEM_IS_SEARCH( search ) ); g_return_if_fail( SCREEM_IS_WINDOW( search->priv->window ) ); if( ! search->priv->dialog ) { xml = glade_xml_new( GLADE_PATH"/screem.glade", "find_dialog", NULL ); search->priv->dialog = glade_xml_get_widget( xml, "find_dialog" ); gtk_window_set_transient_for( GTK_WINDOW( search->priv->dialog ), GTK_WINDOW( search->priv->window ) ); g_signal_connect( G_OBJECT( search->priv->dialog ), "delete_event", G_CALLBACK( gtk_widget_destroy ), NULL ); g_signal_connect_swapped( G_OBJECT( search->priv->dialog ), "delete_event", G_CALLBACK( g_nullify_pointer ), &search->priv->dialog ); g_signal_connect_swapped( G_OBJECT( search->priv->dialog ), "destroy", G_CALLBACK( g_nullify_pointer ), &search->priv->dialog ); widget = glade_xml_get_widget( xml, "find" ); g_signal_connect_swapped( G_OBJECT( widget ), "clicked", G_CALLBACK( screem_search_do_find ), search ); widget = glade_xml_get_widget( xml, "replace" ); g_signal_connect_swapped( G_OBJECT( widget ), "clicked", G_CALLBACK( screem_search_do_replace ), search ); widget = glade_xml_get_widget( xml, "replace_all" ); g_signal_connect_swapped( G_OBJECT( widget ), "clicked", G_CALLBACK( screem_search_do_replace_all ), search ); /* setup field change handlers */ widget = glade_xml_get_widget( xml, "find_entry" ); screem_gtk_add_history( widget ); widget = GTK_BIN( widget )->child; g_signal_connect( G_OBJECT( widget ), "activate", G_CALLBACK( screem_search_find_activate ), search ); gtk_entry_set_activates_default( GTK_ENTRY( widget ), TRUE ); g_signal_connect( G_OBJECT( widget ), "focus_out_event", G_CALLBACK( screem_search_find_change ), search ); widget = glade_xml_get_widget( xml, "replace_entry" ); screem_gtk_add_history( widget ); widget = GTK_BIN( widget )->child; g_signal_connect( G_OBJECT( widget ), "focus_out_event", G_CALLBACK( screem_search_replace_change ), search ); widget = glade_xml_get_widget( xml, "regex" ); g_signal_connect( G_OBJECT( widget ), "toggled", G_CALLBACK( screem_search_regexp_toggle ), search ); widget = glade_xml_get_widget( xml, "pos_start" ); g_signal_connect( G_OBJECT( widget ), "toggled", G_CALLBACK( screem_search_pos_toggle ), search ); widget = glade_xml_get_widget( xml, "pos_current" ); g_signal_connect( G_OBJECT( widget ), "toggled", G_CALLBACK( screem_search_pos_toggle ), search ); widget = glade_xml_get_widget( xml, "check_all" ); g_signal_connect( G_OBJECT( widget ), "toggled", G_CALLBACK( screem_search_all_toggle ), search ); widget = glade_xml_get_widget( xml, "all_current" ); g_signal_connect( G_OBJECT( widget ), "toggled", G_CALLBACK( screem_search_all_toggle ), search ); widget = glade_xml_get_widget( xml, "all_open" ); g_signal_connect( G_OBJECT( widget ), "toggled", G_CALLBACK( screem_search_all_toggle ), search ); screem_search_set_search_type( search, SCREEM_SEARCH_SINGLE ); screem_search_setup_match_view( search ); glade_xml_signal_autoconnect( xml ); } else { xml = glade_get_widget_tree( search->priv->dialog ); } /* reset previous search data */ screem_search_reset( search ); /* setup dialog fields */ /* find pattern */ widget = glade_xml_get_widget( xml, "find_entry" ); widget = GTK_BIN( widget )->child; if( screem_view_has_selection( SCREEM_VIEW( search->priv->editor ), &start, &end ) ) { page = screem_window_get_document( search->priv->window ); gtk_text_buffer_get_iter_at_offset( GTK_TEXT_BUFFER( page ), &it, start ); gtk_text_buffer_get_iter_at_offset( GTK_TEXT_BUFFER( page ), &eit, end ); pattern = gtk_text_buffer_get_text( GTK_TEXT_BUFFER( page ), &it, &eit, FALSE ); } else { pattern = screem_search_get_find_pattern( search ); } if( pattern ) { gtk_entry_set_text( GTK_ENTRY( widget ), pattern ); g_free( pattern ); } else { gtk_entry_set_text( GTK_ENTRY( widget), "" ); } /* pattern overide if we have selected text */ /* replace pattern */ if( doreplace ) { pattern = screem_search_get_replace_pattern( search ); } else { pattern = NULL; } widget = glade_xml_get_widget( xml, "replace_check" ); gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( widget ), ( pattern != NULL ) || doreplace ); widget = glade_xml_get_widget( xml, "replace_entry" ); widget = GTK_BIN( widget )->child; if( pattern ) { gtk_entry_set_text( GTK_ENTRY( widget ), pattern ); g_free( pattern ); } else { gtk_entry_set_text( GTK_ENTRY( widget), "" ); } /* regexp flag */ widget = glade_xml_get_widget( xml, "regex" ); gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( widget ), screem_search_get_regexp( search ) ); /* start pos */ switch( screem_search_get_from( search ) ) { case SCREEM_SEARCH_START: widget = glade_xml_get_widget( xml, "pos_start" ); break; case SCREEM_SEARCH_CURRENT: widget = glade_xml_get_widget( xml, "pos_current" ); break; default: g_assert( FALSE ); break; } gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( widget ), TRUE ); /* search scope */ switch( screem_search_get_search_type( search ) ) { case SCREEM_SEARCH_SINGLE: widget = glade_xml_get_widget( xml, "check_all" ); gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( widget ), FALSE ); break; case SCREEM_SEARCH_SITE: widget = glade_xml_get_widget( xml, "all_current" ); gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( widget ), TRUE ); break; case SCREEM_SEARCH_OPEN: widget = glade_xml_get_widget( xml, "all_open" ); gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( widget ), TRUE ); break; default: g_assert( FALSE ); break; } gtk_list_store_clear( GTK_LIST_STORE( search->priv->match_model ) ); search->priv->matches = FALSE; widget = glade_xml_get_widget( xml, "replace" ); gtk_widget_set_sensitive( widget, FALSE ); gtk_widget_show( search->priv->dialog ); widget = glade_xml_get_widget( xml, "find_entry" ); widget = GTK_BIN( widget )->child; gtk_widget_grab_focus( widget ); } void screem_search_do_find( ScreemSearch *search ) { ScreemSearchPrivate *priv; ScreemWindow *window; ScreemApplication *app; ScreemView *editor; ScreemSite *site; const GList *pages; guint pos; ScreemPage *page; gchar *text; GladeXML *xml; GtkWidget *widget; g_return_if_fail( SCREEM_IS_SEARCH( search ) ); g_return_if_fail( SCREEM_IS_WINDOW( search->priv->window ) ); g_return_if_fail( SCREEM_IS_EDITOR( search->priv->editor ) ); priv = search->priv; window = priv->window; app = window->application; editor = priv->editor; site = screem_window_get_current( window ); switch( screem_search_get_search_type( search ) ) { case SCREEM_SEARCH_SINGLE: pages = NULL; break; case SCREEM_SEARCH_SITE: pages = screem_site_get_pages( site, TRUE, TRUE ); break; case SCREEM_SEARCH_OPEN: pages = screem_window_get_documents( window ); break; default: pages = NULL; g_assert( FALSE ); break; } if( pages && ! priv->matches ) { /* if we don't have a list of matches, * then we need to search the list of files */ screem_application_set_cursor( app, GDK_WATCH ); g_list_foreach( (GList *)pages, (GFunc)screem_search_has_match, search ); screem_application_set_cursor( app, GDK_LAST_CURSOR ); } /* FIXME: we leak pages if SCREEM_SEARCH_SITE */ switch( screem_search_get_from( search ) ) { case SCREEM_SEARCH_START: pos = 0; break; case SCREEM_SEARCH_CURRENT: pos = screem_view_get_pos( SCREEM_VIEW( editor ) ); break; default: pos = 0; g_assert( FALSE ); break; } if( priv->found_at != -1 ) { pos = priv->found_at + 1; } page = screem_window_get_document( window ); text = screem_page_get_data( page ); if( ! priv->find_pattern ) { screem_search_set_find_pattern( search, "" ); } screem_application_set_cursor( app, GDK_WATCH ); /* we may want submatches here */ if( priv->pmatch ) { g_free( priv->pmatch ); priv->pmatch = NULL; } priv->found_at = screem_search_static( text, priv->find_pattern, priv->regexp, pos, &priv->length, &priv->nmatch, &priv->pmatch ); priv->found_c = g_utf8_pointer_to_offset( text, text + priv->found_at ); priv->length_c = g_utf8_pointer_to_offset( text, text + priv->found_at + priv->length ) - priv->found_c; screem_application_set_cursor( app, GDK_LAST_CURSOR ); widget = NULL; if( priv->dialog ) { xml = glade_get_widget_tree( priv->dialog ); widget = glade_xml_get_widget( xml, "replace" ); } if( priv->found_at != -1 ) { g_signal_emit( G_OBJECT( search ), screem_search_signals[ FOUND ], 0, page, priv->found_c, priv->length_c ); if( widget ) { gtk_widget_set_sensitive( widget, TRUE ); } } else { g_signal_emit( G_OBJECT( search ), screem_search_signals[ NOT_FOUND ], 0, page ); if( widget ) { gtk_widget_set_sensitive( widget, FALSE ); } } g_free( text ); } void screem_search_do_replace( ScreemSearch *search ) { ScreemSearchPrivate *priv; ScreemView *editor; gchar *match_text; GString *replace; const gchar *rstring; gunichar c; regmatch_t *pmatch; guint sub; guint offset; guint len; g_return_if_fail( SCREEM_IS_SEARCH( search ) ); g_return_if_fail( search->priv->found_at != -1 ); g_return_if_fail( search->priv->editor != NULL ); priv = search->priv; editor = priv->editor; if( ! priv->replace_pattern ) { screem_search_set_replace_pattern( search, "" ); } g_return_if_fail( ( ! priv->regexp ) || ( priv->nmatch > 0 ) ); pmatch = priv->pmatch; match_text = screem_view_get_text( SCREEM_VIEW( editor ), priv->found_c, priv->length_c ); replace = g_string_new( "" ); rstring = priv->replace_pattern; while( *rstring != '\0' ) { c = g_utf8_get_char( rstring ); if( c != '\\' ) { g_string_append_unichar( replace, c ); } else { rstring = g_utf8_next_char( rstring ); c = g_utf8_get_char( rstring ); if( c == 'n' ) { g_string_append_c( replace, '\n' ); } else if( c == 'r' ) { g_string_append_c( replace, '\r' ); } else if( c == 't' ) { g_string_append_c( replace, '\t' ); } else if( c == '\0' ) { break; } else if( priv->regexp && c >= '1' && c <= '9' ) { /* insert a submatch if it exists */ sub = ( c - '0' ); if( sub < priv->nmatch ) { offset = pmatch[ sub ].rm_so - pmatch[ 0 ].rm_so; len = pmatch[ sub ].rm_eo - pmatch[ sub ].rm_so; g_string_append_len( replace, match_text + offset, len ); } } else { g_string_append_unichar( replace, c ); } } rstring = g_utf8_next_char( rstring ); } screem_view_delete_forward( SCREEM_VIEW( editor ), priv->found_c, priv->length_c ); screem_view_insert( SCREEM_VIEW( editor ), priv->found_c, replace->str ); priv->found_at += replace->len; priv->found_c += g_utf8_strlen( replace->str, -1 ); while( gtk_events_pending() ) { gtk_main_iteration(); } g_string_free( replace, TRUE ); g_free( match_text ); screem_view_set_pos( SCREEM_VIEW( editor ), priv->found_c ); screem_search_do_find( search ); }