/* * Copyright (C) 2004-2005 Vadim Berezniker * http://www.kryptolus.com * * 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, 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 GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * http://www.gnu.org/copyleft/gpl.html * */ #include "stdafx.h" #include "common.h" #include "sabbu.h" #include "kryArray.h" #include "kryString.h" #include "krySSACommandParser.h" #include "krySSACommandHighlighter.h" #include "sound.h" #include "gui_main_event.h" #include "gui_text_editor.h" #include "gui_event_list.h" extern struct sabbu app; struct _gui_find_replace *lui_find_replace = NULL; struct _gui_find_replace_options *lopts_find_replace = &app.gui_find_replace_options; #define lui lui_find_replace #define lopts lopts_find_replace struct _search_indicator_info { int id; int column; int count; gboolean on; gboolean visible; } search_indicator_info = {-1, 0, 0, FALSE, FALSE}; GtkWidget *gui_find_replace_get_search_indicator_widget(int column) { if(search_indicator_info.column == STYLE_COLUMN) return app.ui.search_indicator_style; else if(search_indicator_info.column == NAME_COLUMN) return app.ui.search_indicator_name; else if(search_indicator_info.column == TEXT_COLUMN) return app.ui.search_indicator_text; else return NULL; } gboolean gui_find_replace_search_indicator_timeout_cb(gpointer data) { if(!search_indicator_info.on) return FALSE; GtkWidget *widget = gui_find_replace_get_search_indicator_widget(search_indicator_info.column); if(!widget) { search_indicator_info.on = FALSE; return FALSE; } if(search_indicator_info.visible) gtk_widget_hide(widget); else gtk_widget_show(widget); search_indicator_info.visible = !search_indicator_info.visible; search_indicator_info.count++; if(search_indicator_info.count == 6) { search_indicator_info.on = FALSE; return FALSE; } return TRUE; } void gui_find_replace_show_search_indicator(int column) { if(search_indicator_info.on) { gtk_widget_hide(gui_find_replace_get_search_indicator_widget(search_indicator_info.column)); } else { search_indicator_info.on = TRUE; search_indicator_info.id = g_timeout_add(250, gui_find_replace_search_indicator_timeout_cb, NULL); } search_indicator_info.on = TRUE; search_indicator_info.column = column; search_indicator_info.count = 0; search_indicator_info.visible = FALSE; } void gui_find_replace_destroy() { gtk_window_present(app.ui.window); gtk_widget_destroy(GTK_WIDGET(lui->window)); kry_free(app.gui_find_replace); app.gui_find_replace = NULL; } gboolean gui_find_replace_delete_cb(GtkWidget *widget, gpointer data) { gui_find_replace_destroy(); return FALSE; } void gui_find_replace_cancel_cb(GtkWidget *widget, GtkWindow *window) { gui_find_replace_destroy(); } void gui_find_replace_select_and_focus_row(GtkTreeIter *iter) { GtkTreeSelection *selection = gui_event_list_get_selection(app.ui.event_list); gtk_tree_selection_unselect_all(selection); gtk_tree_selection_select_iter(selection, iter); gui_event_list_scroll_if_necessary(app.ui.event_list, NULL, lopts->search_up); gtk_widget_grab_focus(GTK_WIDGET(gui_text_editor_get_view(app.ui.text_editor))); //gtk_window_present(app.ui.window); } char *gui_find_replace_try_match(char *str, char *target, int offset) { GtkTreeSelection *selection; char *str_orig; char *match = NULL, *match_last; int match_offset = 0; if(offset == -1) { if(lopts->search_up) offset = g_utf8_strlen(str, -1); else offset = 0; } if(!lopts->search_up) str = g_utf8_offset_to_pointer(str, offset); selection = gui_event_list_get_selection(app.ui.event_list); str_orig = str; if(!lopts->match_case) str = KRY_TS(g_utf8_strdown(str, -1)); do { match_last = match; match = strstr(str + match_offset, target); if(lopts->search_up) { if(match && g_utf8_pointer_to_offset(str, match) < offset) { match_last = match; match_offset = g_utf8_next_char(match) - str; } else { match = match_last; break; } } } while(lopts->search_up); if(lopts->match_wholeword && match && match != str) { gunichar chr = g_utf8_get_char(g_utf8_prev_char(match)); if(!g_unichar_isspace(chr) && !g_unichar_ispunct(chr)) match = NULL; } if(lopts->match_wholeword && match) { char *strend = g_utf8_offset_to_pointer(match, g_utf8_strlen(target, -1)); gunichar chr = g_utf8_get_char(strend); if(chr != 0 && !g_unichar_isspace(chr) && !g_unichar_ispunct(chr)) match = NULL; } if(!lopts->match_case) kry_free(str); return (match ? str_orig + (match - str) : NULL); } void gui_find_next_spelling_mistake() { } gboolean gui_find_replace_find() { if(!lopts->target) return FALSE; GtkTreeSelection *selection = gui_event_list_get_selection(app.ui.event_list); GtkTreeModel *model; GList *selitems = gtk_tree_selection_get_selected_rows(selection, &model); GtkTreeIter iter; GtkTextIter iter_start, iter_end; GtkTextBuffer *buffer = gui_text_editor_get_buffer(app.ui.text_editor); gboolean found = FALSE; if(selitems) { GtkTreePath *first_path = (GtkTreePath *) selitems->data; gtk_tree_model_get_iter(model, &iter, first_path); g_list_foreach(selitems, (GFunc) gtk_tree_path_free, NULL); g_list_free(selitems); } else { gtk_tree_model_get_iter_first(model, &iter); } char *target = kry_strdup(lopts->target); kryEvent *event; kryEvent *event_start = NULL; char *match; if(!lopts->match_case) { char *target_lower = KRY_TS(g_utf8_strdown(target, -1)); kry_free(target); target = target_lower; } for(int row = 0;;row++) { kryEventDetailed *event_detailed = NULL; gtk_tree_model_get(model, &iter, PTR_COLUMN, &event, -1); if(event->IsDetailed()) event_detailed = (kryEventDetailed *) event; if(event_start && event == event_start) { if(app.gui_find_replace) gtk_widget_set_sensitive(GTK_WIDGET(lui->window), FALSE); gui_error(app.gui_find_replace ? lui->window : app.ui.window, _("String Not Found")); if(app.gui_find_replace) gtk_widget_set_sensitive(GTK_WIDGET(lui->window), TRUE); break; } if(!event_start) event_start = event; gtk_text_buffer_get_start_iter(buffer, &iter_start); gtk_text_buffer_get_end_iter(buffer, &iter_end); int offset = 0; char *event_text = NULL; if(row == 0) { event_text = KRY_TS(gtk_text_buffer_get_slice(buffer, &iter_start, &iter_end, FALSE)); if(gtk_text_buffer_get_selection_bounds(buffer, &iter_start, &iter_end)) { char *sel = KRY_TS(gtk_text_buffer_get_slice(buffer, &iter_start, &iter_end, FALSE)); if(!lopts->match_case) { char *selnew = KRY_TS(g_utf8_strdown(sel, -1)); kry_free(sel); sel = selnew; } if(strcmp(target, sel) || lopts->search_up) offset = gtk_text_iter_get_offset(&iter_start); else if(!strcmp(target, sel)) offset = gtk_text_iter_get_offset(&iter_end); kry_free(sel); } else { offset = gtk_text_iter_get_offset(&iter_end); if(lopts->search_up && offset == 0) { gtk_text_buffer_get_end_iter(buffer, &iter_end); offset = gtk_text_iter_get_offset(&iter_end); } } } else { if(lopts->search_style && event_detailed && event_detailed->GetStyle() && gui_find_replace_try_match(event_detailed->GetStyle(), target, -1)) { gui_find_replace_select_and_focus_row(&iter); gui_find_replace_show_search_indicator(STYLE_COLUMN); found = TRUE; break; } if(lopts->search_name && event_detailed && event_detailed->GetName() && gui_find_replace_try_match(event_detailed->GetName(), target, -1)) { gui_find_replace_select_and_focus_row(&iter); gui_find_replace_show_search_indicator(NAME_COLUMN); found = TRUE; break; } if(event->GetText() && lopts->search_text && gui_find_replace_try_match(event->GetText(), target, -1)) { enum KryTextEditorSyntaxHighlightMode mode; mode = gui_text_editor_get_syntax_highlight_mode(app.ui.text_editor); krySSACommandHighlighter parser(event->GetText(), mode == SYNTAX_HIGHLIGHT_CODE); event_text = parser.GetStringFromPartList(); } else { event_text = kry_strdup(""); } if(lopts->search_up) offset = g_utf8_strlen(event_text, -1); } if(lopts->search_text && (match = gui_find_replace_try_match(event_text, target, offset))) { if(row != 0) gui_main_event_current_save_text(); gui_find_replace_select_and_focus_row(&iter); buffer = gui_text_editor_get_buffer(app.ui.text_editor); int sel_start = g_utf8_pointer_to_offset(event_text, match); int sel_end = sel_start + g_utf8_strlen(target, -1); gtk_text_buffer_get_iter_at_offset(buffer, &iter_start, sel_start); gtk_text_buffer_get_iter_at_offset(buffer, &iter_end, sel_end); GtkTextMark *mark_start = gtk_text_buffer_get_mark(buffer, "selection_bound"); GtkTextMark *mark_end = gtk_text_buffer_get_mark(buffer, "insert"); gtk_text_buffer_move_mark(buffer, mark_start, &iter_start); gtk_text_buffer_move_mark(buffer, mark_end, &iter_end); gui_find_replace_show_search_indicator(TEXT_COLUMN); kry_free(event_text); found = TRUE; break; } if(lopts->search_up) { GtkTreePath *path_cur = gtk_tree_model_get_path(model, &iter); if(gtk_tree_path_prev(path_cur)) { gtk_tree_model_get_iter(model, &iter, path_cur); } else { int last_row = gtk_tree_model_iter_n_children(model, NULL) - 1; char *pathstr = kry_strdup_printf(KRY_LOC "%d", last_row); GtkTreePath *path_end = gtk_tree_path_new_from_string(pathstr); gtk_tree_model_get_iter(model, &iter, path_end); gtk_tree_path_free(path_end); kry_free(pathstr); } gtk_tree_path_free(path_cur); } else { if(!gtk_tree_model_iter_next(model, &iter)) gtk_tree_model_get_iter_first(model, &iter); } kry_free(event_text); } kry_free(target); return found; } void gui_find_replace_find_cb(GtkWidget *widget, gpointer data) { char *target = KRY_TS(gtk_combo_box_get_active_text(GTK_COMBO_BOX(lui->combo_find))); if(!strcmp(target, "")) { kry_free(target); gui_error(_("Please enter a search string")); return; } lopts->match_case = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lui->check_match_case)); lopts->match_wholeword = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lui->check_match_wholeword)); lopts->search_up = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lui->check_search_up)); lopts->search_text = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lui->check_search_text)); lopts->search_style = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lui->check_search_style)); lopts->search_name = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lui->check_search_name)); if(lopts->target) kry_free(lopts->target); lopts->target = target; kry_recent_list_add(&lopts->target_history, lopts->target); gtk_list_store_clear(GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX((lui->combo_find))))); for(GList *ptr = lopts->target_history; ptr; ptr = ptr->next) gtk_combo_box_append_text(GTK_COMBO_BOX(lui->combo_find), (const gchar *) ptr->data); if(gui_find_replace_find()) gui_find_replace_destroy(); } gboolean gui_find_replace_find_combo_cb(GtkWidget *widget, GdkEventKey *event, GtkButton *button_find) { if(event->keyval == GDK_Return) { gtk_button_clicked(button_find); return TRUE; } return FALSE; } void gui_find_replace_show() { if(app.gui_find_replace) return; GtkWindow *window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); GtkHBox *hbox_main = GTK_HBOX(gtk_hbox_new(FALSE, 0)); GtkVBox *vbox_opts = GTK_VBOX(gtk_vbox_new(FALSE, 0)); GtkHBox *hbox_opts = GTK_HBOX(gtk_hbox_new(FALSE, 0)); GtkVBox *vbox_opts_left = GTK_VBOX(gtk_vbox_new(FALSE, 0)); GtkVBox *vbox_opts_right = GTK_VBOX(gtk_vbox_new(FALSE, 0)); GtkVBox *vbox_buttons = GTK_VBOX(gtk_vbox_new(FALSE, 0)); GtkTable *table_criteria = GTK_TABLE(gtk_table_new(2, 2, FALSE)); GtkLabel *label_find = GTK_LABEL(gtk_label_new(_("Find:"))); GtkLabel *label_find_spacer = GTK_LABEL(gtk_label_new(" ")); GtkLabel *label_replace = GTK_LABEL(gtk_label_new(_("Replace With:"))); GtkLabel *label_replace_spacer = GTK_LABEL(gtk_label_new(" ")); GtkComboBoxEntry *combo_find = GTK_COMBO_BOX_ENTRY(gtk_combo_box_entry_new_text()); GtkComboBoxEntry *combo_replace = GTK_COMBO_BOX_ENTRY(gtk_combo_box_entry_new_text()); GtkCheckButton *check_match_case = GTK_CHECK_BUTTON(gtk_check_button_new_with_label(_("Match Case"))); GtkCheckButton *check_match_wholeword = GTK_CHECK_BUTTON(gtk_check_button_new_with_label(_("Match Whole Word"))); GtkCheckButton *check_search_up = GTK_CHECK_BUTTON(gtk_check_button_new_with_label(_("Search Up"))); GtkCheckButton *check_search_text = GTK_CHECK_BUTTON(gtk_check_button_new_with_label(_("Search in Text"))); GtkCheckButton *check_search_name = GTK_CHECK_BUTTON(gtk_check_button_new_with_label(_("Search in Name"))); GtkCheckButton *check_search_style = GTK_CHECK_BUTTON(gtk_check_button_new_with_label(_("Search in Style"))); GtkButton *button_find = GTK_BUTTON(gtk_button_new_with_label(_("Find Next"))); GtkButton *button_replace = GTK_BUTTON(gtk_button_new_with_label(_("Replace"))); GtkButton *button_replaceall = GTK_BUTTON(gtk_button_new_with_label(_("Replace All"))); GtkButton *button_replacemode = GTK_BUTTON(gtk_button_new_with_label(_("Replace Mode"))); GtkButton *button_close = GTK_BUTTON(gtk_button_new_with_label(_("Close"))); gtk_misc_set_alignment(GTK_MISC(label_find), 1.0, 0.5); gtk_misc_set_alignment(GTK_MISC(label_replace), 1.0, 0.5); gtk_table_attach(table_criteria, GTK_WIDGET(label_find), 0, 1, 0, 1, (GtkAttachOptions) (GTK_SHRINK | GTK_FILL), GTK_SHRINK, 0, 0); gtk_table_attach(table_criteria, GTK_WIDGET(label_find_spacer), 1, 2, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0); gtk_table_attach_defaults(table_criteria, GTK_WIDGET(combo_find), 2, 3, 0, 1); gtk_table_attach(table_criteria, GTK_WIDGET(label_replace), 0, 1, 1, 2, (GtkAttachOptions) (GTK_SHRINK | GTK_FILL), GTK_SHRINK, 0, 0); gtk_table_attach(table_criteria, GTK_WIDGET(label_replace_spacer), 1, 2, 1, 2, GTK_SHRINK, GTK_SHRINK, 0, 0); gtk_table_attach_defaults(table_criteria, GTK_WIDGET(combo_replace), 2, 3, 1, 2); gtk_box_pack_start(GTK_BOX(vbox_opts_left), GTK_WIDGET(check_match_case), FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox_opts_left), GTK_WIDGET(check_match_wholeword), FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox_opts_left), GTK_WIDGET(check_search_up), FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox_opts_right), GTK_WIDGET(check_search_text), FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox_opts_right), GTK_WIDGET(check_search_name), FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox_opts_right), GTK_WIDGET(check_search_style), FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(hbox_opts), GTK_WIDGET(vbox_opts_left), FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(hbox_opts), gtk_label_new(""), FALSE, TRUE, 5); gtk_box_pack_start(GTK_BOX(hbox_opts), GTK_WIDGET(vbox_opts_right), FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox_opts), GTK_WIDGET(table_criteria), FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox_opts), GTK_WIDGET(hbox_opts), FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox_buttons), GTK_WIDGET(button_find), FALSE, TRUE, 2); gtk_box_pack_start(GTK_BOX(vbox_buttons), GTK_WIDGET(button_replace), FALSE, TRUE, 2); gtk_box_pack_start(GTK_BOX(vbox_buttons), GTK_WIDGET(button_replaceall), FALSE, TRUE, 2); gtk_box_pack_start(GTK_BOX(vbox_buttons), GTK_WIDGET(button_replacemode), FALSE, TRUE, 2); gtk_box_pack_start(GTK_BOX(vbox_buttons), GTK_WIDGET(button_close), FALSE, TRUE, 2); gtk_box_pack_start(GTK_BOX(hbox_main), GTK_WIDGET(vbox_opts), TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(hbox_main), gtk_label_new(""), FALSE, TRUE, 5); gtk_box_pack_start(GTK_BOX(hbox_main), GTK_WIDGET(vbox_buttons), FALSE, TRUE, 0); gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(hbox_main)); gtk_container_set_border_width(GTK_CONTAINER(window), 4); gtk_window_set_title(window, "Find"); gtk_window_set_transient_for(window, app.ui.window); gtk_window_set_position(window, GTK_WIN_POS_CENTER_ON_PARENT); g_signal_connect(window, "delete-event", G_CALLBACK(gui_find_replace_delete_cb), NULL); g_signal_connect(button_find, "clicked", G_CALLBACK(gui_find_replace_find_cb), NULL); g_signal_connect(button_close, "clicked", G_CALLBACK(gui_find_replace_cancel_cb), window); g_signal_connect(gtk_bin_get_child(GTK_BIN(combo_find)), "key-press-event", G_CALLBACK(gui_find_replace_find_combo_cb), button_find); app.gui_find_replace = kry_new0(struct _gui_find_replace); lui = app.gui_find_replace; lui->window = window; lui->combo_find = combo_find; lui->check_match_case = check_match_case; lui->check_match_wholeword = check_match_wholeword; lui->check_search_up = check_search_up; lui->check_search_text = check_search_text; lui->check_search_name = check_search_name; lui->check_search_style = check_search_style; if(lopts->match_case) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_match_case), TRUE); if(lopts->match_wholeword) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_match_wholeword), TRUE); if(lopts->search_up) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_search_up), TRUE); if(lopts->search_text) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_search_text), TRUE); if(lopts->search_name) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_search_name), TRUE); if(lopts->search_style) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_search_style), TRUE); char *first_string = NULL; for(GList *ptr = lopts->target_history; ptr; ptr = ptr->next) { gui_combo_box_add_text(GTK_COMBO_BOX(combo_find), (char *) ptr->data); if(!first_string) first_string = (char *) ptr->data; } if(lopts->target) gui_combo_box_set_text(GTK_COMBO_BOX(combo_find), lopts->target); else if(first_string) gui_combo_box_set_text(GTK_COMBO_BOX(combo_find), first_string); GtkTextBuffer *buffer = gui_text_editor_get_buffer(app.ui.text_editor); GtkTextIter iter_start, iter_end; if(gtk_text_buffer_get_selection_bounds(buffer, &iter_start, &iter_end)) { char *sel = gtk_text_buffer_get_slice(buffer, &iter_start, &iter_end, FALSE); gui_combo_box_add_text(GTK_COMBO_BOX(combo_find), sel); } gtk_widget_show_all(GTK_WIDGET(window)); gtk_widget_hide(GTK_WIDGET(label_replace)); gtk_widget_hide(GTK_WIDGET(label_replace_spacer)); gtk_widget_hide(GTK_WIDGET(combo_replace)); gtk_widget_hide(GTK_WIDGET(button_replace)); gtk_widget_hide(GTK_WIDGET(button_replaceall)); gtk_widget_hide(GTK_WIDGET(button_replacemode)); }