/* Glimmer - searches.c * * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "declarations.h" #include "searches.h" #include "dialogs.h" #include "findbar.h" #include "linebar.h" #include "main.h" #include "misc.h" #include "settings.h" #include "windows.h" #include "gtkextext/gtkextext.h" #include "widgets/gdsfile.h" static void add_manpage_string (GtkWidget * widget, gpointer data); gboolean case_sensitive = FALSE; gboolean search_style = FALSE; gboolean search_start = FALSE; gboolean search_multi = FALSE; gboolean keep_open = FALSE; static void toggle_button_switch (GtkWidget * widget, gint * value); /* * Find (F6) * If the file has text in the filename_entry, look for it. * Elsewise we need to popup the dialog. */ void find_cb (GtkWidget * widget, gpointer data) { gchar *text; g_return_if_fail (cur_file != NULL); text = gtk_entry_get_text (GTK_ENTRY (find_entry)); if (strlen (text) > 0) execute_find_cb (widget, data); else { widget = gnome_search_dialog_new (GNOME_SEARCH_FIND); gtk_signal_connect (GTK_OBJECT (GNOME_SEARCH_DIALOG (widget)->find_button), "clicked", GTK_SIGNAL_FUNC (find_text_cb), widget); gtk_signal_connect_object (GTK_OBJECT (GNOME_SEARCH_DIALOG (widget)-> cancel_button), "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy), GTK_OBJECT (widget)); gnome_search_dialog_set_max_entries (GNOME_SEARCH_DIALOG (widget), general_preferences.history); gnome_search_dialog_set_find_history (GNOME_SEARCH_DIALOG (widget), build_glist_from_file ("find.hist", general_preferences.history)); set_find_text_from_selection (GNOME_SEARCH_DIALOG (widget)); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (GNOME_SEARCH_DIALOG (widget)-> check_case_sensitive), case_sensitive); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (GNOME_SEARCH_DIALOG (widget)-> check_search_style), search_style); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (GNOME_SEARCH_DIALOG (widget)-> check_search_start), search_start); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (GNOME_SEARCH_DIALOG (widget)-> check_search_multi), search_multi); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (GNOME_SEARCH_DIALOG (widget)-> check_stay_open), keep_open); gtk_signal_connect (GTK_OBJECT (GNOME_SEARCH_DIALOG (widget)-> check_case_sensitive), "clicked", GTK_SIGNAL_FUNC (toggle_button_switch), &case_sensitive); gtk_signal_connect (GTK_OBJECT (GNOME_SEARCH_DIALOG (widget)->check_search_style), "clicked", GTK_SIGNAL_FUNC (toggle_button_switch), &search_style); gtk_signal_connect (GTK_OBJECT (GNOME_SEARCH_DIALOG (widget)->check_search_start), "clicked", GTK_SIGNAL_FUNC (toggle_button_switch), &search_start); gtk_signal_connect (GTK_OBJECT (GNOME_SEARCH_DIALOG (widget)->check_search_multi), "clicked", GTK_SIGNAL_FUNC (toggle_button_switch), &search_multi); gtk_signal_connect (GTK_OBJECT (GNOME_SEARCH_DIALOG (widget)->check_stay_open), "clicked", GTK_SIGNAL_FUNC (toggle_button_switch), &keep_open); gtk_window_set_transient_for (GTK_WINDOW (widget), GTK_WINDOW (window)); gtk_widget_show (widget); gtk_widget_grab_focus (GTK_COMBO (GNOME_SEARCH_DIALOG (widget)->find_combo)-> entry); } } /* * Find callback */ void find_text_cb (GtkWidget * widget, GnomeSearchDialog * search_dialog) { GtkExTextMatch match; gchar *search_string; gboolean found; gint pos; gint old_pos; gchar text[256]; g_return_if_fail (cur_file != NULL); old_pos = gtk_extext_get_position (GTK_EXTEXT (cur_file->text)); search_string = gnome_search_dialog_get_find_text (GNOME_SEARCH_DIALOG (search_dialog)); search_string = g_strdup (search_string); if (search_start == GNOME_SEARCH_START_TOP) pos = 0; else pos = old_pos; gnome_search_dialog_set_startpos (search_dialog, (guint) pos); if (!search_dialog->search_selection) gnome_search_dialog_set_endpos (search_dialog, (guint) gtk_extext_get_length (GTK_EXTEXT (cur_file-> text))); found = find_text (GTK_EXTEXT (cur_file->text), pos, search_dialog->endpos, search_string, case_sensitive, search_style, TRUE, &match); gnome_search_dialog_add_find_string (search_dialog, search_string); find_combo_add_string (search_string); gtk_entry_set_text (GTK_ENTRY (find_entry), ""); add_string_to_file ("find.hist", search_string); if (found) { if (gnome_search_dialog_get_dialog_type (search_dialog) == GNOME_SEARCH_FIND && !keep_open) { gtk_widget_destroy (GTK_WIDGET (search_dialog)); } } else if (search_multi) { GdsFile *file; gint index; gint total; total = g_list_length (GTK_NOTEBOOK (files_book)->children); index = get_nth_file_number (cur_file); if (index != total - 1) { index++; file = get_nth_file_proper (index); if (file) file_change (NULL, file); gtk_extext_set_position (GTK_EXTEXT (cur_file->text), 0); while (gtk_events_pending ()) gtk_main_iteration (); gtk_signal_emit_by_name (GTK_OBJECT (search_dialog->find_button), "clicked", NULL); } else { g_snprintf (text, sizeof (text), "%s was not found.", search_string); generic_dialog_with_text (text); } } else { g_snprintf (text, sizeof (text), "%s was not found.", search_string); generic_dialog_with_text (text); } g_free (search_string); } /* * Find some text in a GtkExText with the given settings */ gboolean find_text (GtkExText * text, gint start, gint end, gchar * search_string, gboolean case_sensitive, gboolean is_regex, gboolean select, GtkExTextMatch * match) { Regex regex; gint pos = 0; gboolean ret; g_return_val_if_fail (text != NULL, FALSE); if (is_regex) { if (gtk_extext_compile_regex (search_string, ®ex)) { pos = gtk_extext_regex_search (text, start, ®ex, TRUE, match); if (pos == -1) pos = 0; if (regex.buf.fastmap) g_free (regex.buf.fastmap); regex.buf.fastmap = NULL; regfree (®ex.buf); } } else { pos = gtk_extext_search (text, search_string, start, case_sensitive, TRUE, match); } ret = pos && match->endpos <= end ? TRUE : FALSE; if (ret && select) { gtk_extext_set_position (GTK_EXTEXT (text), match->endpos); gtk_extext_select_region (GTK_EXTEXT (text), match->startpos, match->endpos); } else if (ret) gtk_extext_set_position (GTK_EXTEXT (text), match->endpos); return (ret); } /* * Replace (F7) * If the file has a selection and text in the filename_entry, replace the text. * Elsewise we need to popup the dialog. */ void replace_cb (GtkWidget * widget, gpointer data) { g_return_if_fail (cur_file != NULL); widget = gnome_search_dialog_new (GNOME_SEARCH_REPLACE); gtk_signal_connect (GTK_OBJECT (GNOME_SEARCH_DIALOG (widget)->find_button), "clicked", GTK_SIGNAL_FUNC (find_text_cb), widget); gtk_signal_connect (GTK_OBJECT (GNOME_SEARCH_DIALOG (widget)->replace_button), "clicked", GTK_SIGNAL_FUNC (replace_text_cb), widget); gtk_signal_connect (GTK_OBJECT (GNOME_SEARCH_DIALOG (widget)->replace_all), "clicked", GTK_SIGNAL_FUNC (replace_all_text_cb), widget); gtk_signal_connect_object (GTK_OBJECT (GNOME_SEARCH_DIALOG (widget)->cancel_button), "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy), GTK_OBJECT (widget)); gnome_search_dialog_set_max_entries (GNOME_SEARCH_DIALOG (widget), general_preferences.history); gnome_search_dialog_set_find_history (GNOME_SEARCH_DIALOG (widget), build_glist_from_file ("find.hist", general_preferences. history)); gnome_search_dialog_set_replace_history (GNOME_SEARCH_DIALOG (widget), build_glist_from_file ("replace.hist", general_preferences.history)); set_find_text_from_selection (GNOME_SEARCH_DIALOG (widget)); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (GNOME_SEARCH_DIALOG (widget)-> check_case_sensitive), case_sensitive); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (GNOME_SEARCH_DIALOG (widget)-> check_search_style), search_style); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (GNOME_SEARCH_DIALOG (widget)-> check_search_start), search_start); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (GNOME_SEARCH_DIALOG (widget)-> check_search_multi), search_multi); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (GNOME_SEARCH_DIALOG (widget)-> check_stay_open), keep_open); gtk_signal_connect (GTK_OBJECT (GNOME_SEARCH_DIALOG (widget)->check_case_sensitive), "clicked", GTK_SIGNAL_FUNC (toggle_button_switch), &case_sensitive); gtk_signal_connect (GTK_OBJECT (GNOME_SEARCH_DIALOG (widget)->check_search_style), "clicked", GTK_SIGNAL_FUNC (toggle_button_switch), &search_style); gtk_signal_connect (GTK_OBJECT (GNOME_SEARCH_DIALOG (widget)->check_search_start), "clicked", GTK_SIGNAL_FUNC (toggle_button_switch), &search_start); gtk_signal_connect (GTK_OBJECT (GNOME_SEARCH_DIALOG (widget)->check_search_multi), "clicked", GTK_SIGNAL_FUNC (toggle_button_switch), &search_multi); gtk_signal_connect (GTK_OBJECT (GNOME_SEARCH_DIALOG (widget)->check_stay_open), "clicked", GTK_SIGNAL_FUNC (toggle_button_switch), &keep_open); gtk_window_set_transient_for (GTK_WINDOW (widget), GTK_WINDOW (window)); gtk_widget_show (widget); gtk_widget_grab_focus (GTK_COMBO (GNOME_SEARCH_DIALOG (widget)->find_combo)->entry); } /* * Replace callback */ void replace_text_cb (GtkWidget * widget, GnomeSearchDialog * search_dialog) { gchar *find_text; gchar *new_text; gboolean found = FALSE; g_return_if_fail (cur_file != NULL); find_text = gnome_search_dialog_get_find_text (search_dialog); new_text = gnome_search_dialog_get_replace_text (search_dialog); found = replace_text (GTK_EXTEXT (cur_file->text), gnome_search_dialog_get_replace_text (search_dialog)); search_dialog->endpos += (strlen (new_text) - strlen (find_text)); gnome_search_dialog_add_replace_string (search_dialog, gnome_search_dialog_get_replace_text (search_dialog)); add_string_to_file ("replace.hist", gnome_search_dialog_get_replace_text (search_dialog)); if (found && !gnome_search_dialog_keep_window_open (search_dialog)) gtk_widget_destroy (GTK_WIDGET (search_dialog)); else gtk_signal_emit_by_name (GTK_OBJECT (search_dialog->find_button), "clicked", NULL); } /* * Replace All callback */ void replace_all_text_cb (GtkWidget * widget, GnomeSearchDialog * search_dialog) { gint replacements; gchar *search_text; gchar *new_text; gchar text[256]; g_return_if_fail (cur_file != NULL); search_text = gnome_search_dialog_get_find_text (search_dialog); new_text = gnome_search_dialog_get_replace_text (search_dialog); if (search_dialog->search_selection) gtk_extext_set_position (GTK_EXTEXT (cur_file->text), search_dialog->startpos); else if (search_start == GNOME_SEARCH_START_TOP) gtk_extext_set_position (GTK_EXTEXT (cur_file->text), 0); gtk_extext_freeze (GTK_EXTEXT (cur_file->text)); replacements = replace_all_text (GTK_EXTEXT (cur_file->text), search_text, new_text, case_sensitive, search_style, search_dialog->search_selection ? search_dialog-> endpos : GTK_EXTEXT (cur_file->text)->length); gtk_extext_thaw (GTK_EXTEXT (cur_file->text)); if (replacements > 0) { gnome_search_dialog_add_find_string (search_dialog, gnome_search_dialog_get_find_text (search_dialog)); add_string_to_file ("find.hist", gnome_search_dialog_get_find_text (search_dialog)); gnome_search_dialog_add_replace_string (search_dialog, gnome_search_dialog_get_replace_text (search_dialog)); add_string_to_file ("replace.hist", gnome_search_dialog_get_replace_text (search_dialog)); if (!keep_open) gtk_widget_destroy (GTK_WIDGET (search_dialog)); g_snprintf (text, sizeof (text), "%d replacements made.", replacements); generic_dialog_with_text (text); } else { g_snprintf (text, sizeof (text), "%s was not found.", search_text); generic_dialog_with_text (text); } } gint replace_all_text (GtkExText * text, gchar * search_text, gchar * new_text, gboolean iscase, gboolean regex, gint end) { GtkExTextMatch match; gint replacements = 0; while (1) { if (find_text (text, text->current_pos, end, search_text, iscase, regex, TRUE, &match)) { replace_text (GTK_EXTEXT (cur_file->text), new_text); end += (strlen (new_text) - strlen (search_text)); replacements++; } else if (search_multi) { GdsFile *file; gint total; gint index; total = g_list_length (GTK_NOTEBOOK (files_book)->children); index = get_nth_file_number (cur_file); if (index != total - 1) { gtk_extext_thaw (text); index++; file = get_nth_file_proper (index); if (file) file_change (NULL, file); gtk_extext_set_position (GTK_EXTEXT (file->text), 0); while (gtk_events_pending ()) gtk_main_iteration (); text = GTK_EXTEXT (cur_file->text); end = gtk_extext_get_length (text); gtk_extext_freeze (text); } else break; } else break; } return (replacements); } /* * Replace the current selection in editable with replace_text */ gboolean replace_text (GtkExText * text, gchar * new_text) { gint start, end; if (text->has_selection) { start = text->selection_start_pos; end = text->selection_end_pos; text->has_selection = FALSE; text->selection_start_pos = -1; text->selection_end_pos = -1; gtk_extext_delete_text (text, start, end); gtk_extext_set_position (text, start); if (new_text && strlen (new_text) > 0) gtk_extext_insert_text (text, new_text, strlen (new_text), (gint *) & text->current_pos); return (TRUE); } return (FALSE); } /* * Lets hop down to some line in the file. */ void gotoline_cb (GtkWidget * widget, gpointer data) { gchar *line_text = NULL; gint line_num; GtkExText *text; g_return_if_fail (cur_file != NULL); text = GTK_EXTEXT (cur_file->text); line_text = gtk_entry_get_text (GTK_ENTRY (line_entry)); if (strlen (line_text) > 0) { if (sscanf (line_text, "%d", &line_num) && line_num >= 1 && line_num <= text->line_count) { add_string_to_file ("line.hist", line_text); line_combo_add_string (line_text); gtk_extext_set_line (text, line_num - 1); gtk_widget_grab_focus (GTK_WIDGET (text)); } else display_message ("Line not found.", FLASH); } else { widget = gnome_goto_line_dialog_new (); gtk_signal_connect (GTK_OBJECT (GNOME_GOTO_LINE_DIALOG (widget)->goto_button), "clicked", GTK_SIGNAL_FUNC (find_line_cb), widget); gtk_signal_connect_object (GTK_OBJECT (GNOME_GOTO_LINE_DIALOG (widget)-> cancel_button), "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy), GTK_OBJECT (widget)); gnome_goto_line_dialog_set_history (GNOME_GOTO_LINE_DIALOG (widget), build_glist_from_file ("line.hist", general_preferences. history)); gtk_window_set_transient_for (GTK_WINDOW (widget), GTK_WINDOW (window)); gtk_widget_show (widget); gtk_widget_grab_focus (GNOME_GOTO_LINE_DIALOG (widget)->line_entry); } } void find_line_cb (GtkWidget * widget, GnomeGotoLineDialog * gtl) { gchar *text = NULL; gint line_num; g_return_if_fail (cur_file != NULL); line_num = gnome_goto_line_dialog_get_line (gtl); if (line_num > -1 && line_num <= GTK_EXTEXT (cur_file->text)->line_count) { text = gtk_entry_get_text (GTK_ENTRY (gtl->line_entry)); gtk_extext_set_line (GTK_EXTEXT (cur_file->text), line_num); gnome_goto_line_dialog_add_search_string (gtl, text); line_combo_add_string (text); gtk_entry_set_text (GTK_ENTRY (line_entry), ""); add_string_to_file ("line.hist", text); if (!GTK_TOGGLE_BUTTON (gtl->check_stay_open)->active) gtk_widget_destroy (GTK_WIDGET (gtl)); } } static void add_manpage_string (GtkWidget * widget, gpointer data) { GdsManpage *gds_manpage; GtkEntry *entry; gds_manpage = (GdsManpage *) data; entry = (GtkEntry *) GTK_COMBO (gds_manpage->search_combo)->entry; if (gds_manpage_add_search_string (gds_manpage, gtk_entry_get_text (entry))) add_string_to_file ("man.hist", gtk_entry_get_text (entry)); } void manpage_cb (GtkWidget * widget, gpointer data) { GtkExText *extext; gchar *text = NULL; gint start, end; extext = GTK_EXTEXT (cur_file->text); if (extext->has_selection) { text = gtk_extext_get_chars (extext, extext->selection_start_pos, extext->selection_end_pos); } else { start = gtk_extext_get_position (extext); if (gtk_extext_get_current_word (extext, &start, &end)) { text = gtk_extext_get_chars (extext, start, end); } } if (GPOINTER_TO_INT (data) == 0) make_manpage_viewer (text); else make_gnome_manpage_viewer (text); g_free (text); } void make_manpage_viewer (gchar * text) { GtkWidget *gds_manpage; GtkEntry *entry; gds_manpage = gds_manpage_new (); gtk_signal_connect (GTK_OBJECT (GDS_MANPAGE (gds_manpage)->search_button), "clicked", GTK_SIGNAL_FUNC (add_manpage_string), gds_manpage); gds_manpage_set_history (GDS_MANPAGE (gds_manpage), build_glist_from_file ("man.hist", general_preferences. history)); gds_manpage_set_search_string (GDS_MANPAGE (gds_manpage), text); gtk_widget_show (gds_manpage); if (text) gtk_widget_activate (GDS_MANPAGE (gds_manpage)->search_button); else { entry = GTK_ENTRY (GTK_COMBO (GDS_MANPAGE (gds_manpage)->search_combo)-> entry); gtk_entry_set_text (entry, ""); gtk_widget_grab_focus (GTK_WIDGET (entry)); } } void make_gnome_manpage_viewer (gchar * text) { gchar *command; if (text) command = g_strdup_printf ("gnome-help-browser man:%s &", text); else command = g_strdup ("gnome-help-browser toc:man"); system (command); } /* Utility functions */ void set_find_text_from_selection (GnomeSearchDialog * sd) { guint sel_start; guint sel_end; gchar *sel_text; if (GTK_EXTEXT (cur_file->text)->has_selection) { sel_start = GTK_EXTEXT (cur_file->text)->selection_start_pos; sel_end = GTK_EXTEXT (cur_file->text)->selection_end_pos; if (sel_end - sel_start <= 48) { sel_text = gtk_extext_get_chars (GTK_EXTEXT (cur_file->text), sel_start, sel_end); gnome_search_dialog_set_find_text (sd, sel_text); gnome_search_dialog_search_selection (sd, FALSE); g_free (sel_text); } else { GTK_EXTEXT (cur_file->text)->current_pos = sel_start; gnome_search_dialog_set_startpos (sd, sel_start); gnome_search_dialog_set_endpos (sd, sel_end); gnome_search_dialog_search_selection (sd, TRUE); } } else { gnome_search_dialog_search_selection (sd, FALSE); } } void last_bookmark_cb (GtkWidget * widget, gpointer data) { GtkExText *text; GdkPixmap *pixmap; GdkBitmap *mask; gint line; gboolean found = FALSE; text = GTK_EXTEXT (cur_file->text); line = gtk_extext_get_line (text); while (line > 0) { line--; gtk_extext_get_line_pixmap (text, line, &pixmap, &mask); if (pixmap == bookmark_pixmap->pixmap) { found = TRUE; break; } } if (found) { gtk_extext_set_line (text, line); } else display_message ("No more bookmarks.", FLASH); } void next_bookmark_cb (GtkWidget * widget, gpointer data) { GtkExText *text; GdkPixmap *pixmap; GdkBitmap *mask; gint line; gboolean found = FALSE; text = GTK_EXTEXT (cur_file->text); line = gtk_extext_get_line (text); while (line < text->line_count) { line++; gtk_extext_get_line_pixmap (text, line, &pixmap, &mask); if (pixmap == bookmark_pixmap->pixmap) { found = TRUE; break; } } if (found) { gtk_extext_set_line (text, line); } else display_message ("No more bookmarks.", FLASH); } void match_bracket_cb (GtkWidget * widget, gpointer data) { GtkExText *text; gchar cur_char; gint start = 0; g_return_if_fail (cur_file != NULL); text = GTK_EXTEXT (cur_file->text); start = text->current_pos - 1; cur_char = GTK_EXTEXT_INDEX (text, start); if (!strchr ("<{[()]}>", cur_char)) return; if (find_bracket_match (cur_file->text, &start)) gtk_extext_set_position (text, start + 1); } void select_to_bracket_cb (GtkWidget * widget, gpointer data) { GtkExText *text; gchar cur_char; gint start = 0; gint end = 0; g_return_if_fail (cur_file != NULL); text = GTK_EXTEXT (cur_file->text); start = end = text->current_pos; cur_char = GTK_EXTEXT_INDEX (text, start); if (!strchr ("{[()]}", cur_char)) return; if (find_bracket_match (cur_file->text, &end)) { gtk_extext_select_region (text, start < end ? start : end, end > start ? end + 1 : start + 1); } } static void toggle_button_switch (GtkWidget * widget, gint * value) { *value = *value ? 0 : 1; }