From 1c8d62ed5d747f65a1363041c3ed3417f830b0a0 Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Thu, 13 Jul 2006 14:30:44 +0000 Subject: [PATCH] Added basic Find in files search functionality. git-svn-id: https://geany.svn.sourceforge.net/svnroot/geany/trunk@551 ea778897-0a13-0410-b9d1-a72fbfd435f5 --- ChangeLog | 9 ++ geany.glade | 113 ++++++++++++++++++++++--- src/Makefile.am | 1 + src/build.c | 36 +------- src/callbacks.c | 83 +++++++++---------- src/callbacks.h | 10 ++- src/dialogs.c | 83 ++++++++++++++++++- src/dialogs.h | 2 + src/geany.h | 3 + src/interface.c | 139 ++++++++++++++++++++----------- src/keyfile.c | 5 ++ src/main.c | 1 + src/msgwindow.c | 144 ++++++++++++++++++++++++++++++++ src/msgwindow.h | 4 + src/prefs.c | 13 ++- src/search.c | 214 ++++++++++++++++++++++++++++++++++++++++++++++++ src/search.h | 25 ++++++ src/utils.c | 29 +++++++ src/utils.h | 2 + 19 files changed, 779 insertions(+), 137 deletions(-) create mode 100644 src/search.c create mode 100644 src/search.h diff --git a/ChangeLog b/ChangeLog index 096040da..bc8e3a4e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2006-07-13 Nick Treleaven + + * src/build.c, src/interface.c, src/utils.c, src/utils.h, src/geany.h, + src/msgwindow.c, src/msgwindow.h, src/callbacks.c, src/callbacks.h, + src/keyfile.c, src/search.c, src/search.h, src/prefs.c, + src/dialogs.c, src/main.c, src/dialogs.h, src/Makefile.am, + geany.glade: Added basic Find in files search functionality. + + 2006-07-11 Enrico Tröger * src/dialogs.c: Moved dialogs_show_prefs_dialog() to src/prefs.c. diff --git a/geany.glade b/geany.glade index e9037dc3..12fe3e8a 100644 --- a/geany.glade +++ b/geany.glade @@ -58,7 +58,7 @@ True - + True gtk-new 1 @@ -151,7 +151,7 @@ - + True gtk-save 1 @@ -228,7 +228,7 @@ - + True gtk-close 1 @@ -359,7 +359,7 @@ True - + True gtk-add 1 @@ -391,7 +391,7 @@ True - + True gtk-add 1 @@ -569,7 +569,7 @@ - + True gtk-find-and-replace 1 @@ -582,6 +582,21 @@ + + + True + + + + + + True + Find in f_iles + True + + + + True @@ -596,7 +611,7 @@ - + True gtk-jump-to 1 @@ -631,7 +646,7 @@ - + True gtk-select-font 1 @@ -653,7 +668,7 @@ - + True gtk-select-color 1 @@ -4703,7 +4718,7 @@ Bottom True - 4 + 5 3 False 7 @@ -5024,6 +5039,84 @@ Bottom + + + + True + Grep + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + 1 + 4 + 5 + fill + + + + + + + True + True + True + True + 0 + + True + * + False + + + 1 + 2 + 4 + 5 + + + + + + + True + True + GTK_RELIEF_NORMAL + True + + + + True + gtk-directory + 4 + 0.5 + 0.5 + 0 + 0 + + + + + 2 + 3 + 4 + 5 + fill + + + 0 diff --git a/src/Makefile.am b/src/Makefile.am index 64d8f322..7ff4895b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -14,6 +14,7 @@ bin_PROGRAMS = geany geany_SOURCES = \ main.c geany.h \ + search.c search.h \ notebook.c notebook.h \ keybindings.c keybindings.h \ templates.c templates.h \ diff --git a/src/build.c b/src/build.c index 72d8df15..e3b13731 100644 --- a/src/build.c +++ b/src/build.c @@ -41,7 +41,6 @@ #include "document.h" -static GIOChannel *build_set_up_io_channel (gint fd, GIOCondition cond, GIOFunc func, gpointer data); static gboolean build_iofunc(GIOChannel *ioc, GIOCondition cond, gpointer data); static gboolean build_create_shellscript(const gint idx, const gchar *fname, const gchar *cmd); static GPid build_spawn_cmd(gint idx, gchar **cmd); @@ -303,8 +302,10 @@ static GPid build_spawn_cmd(gint idx, gchar **cmd) } // use GIOChannels to monitor stdout and stderr - build_set_up_io_channel(stdout_fd, G_IO_IN|G_IO_PRI|G_IO_ERR|G_IO_HUP|G_IO_NVAL, build_iofunc, GINT_TO_POINTER(0)); - build_set_up_io_channel(stderr_fd, G_IO_IN|G_IO_PRI|G_IO_ERR|G_IO_HUP|G_IO_NVAL, build_iofunc, GINT_TO_POINTER(1)); + utils_set_up_io_channel(stdout_fd, G_IO_IN|G_IO_PRI|G_IO_ERR|G_IO_HUP|G_IO_NVAL, + build_iofunc, GINT_TO_POINTER(0)); + utils_set_up_io_channel(stderr_fd, G_IO_IN|G_IO_PRI|G_IO_ERR|G_IO_HUP|G_IO_NVAL, + build_iofunc, GINT_TO_POINTER(1)); g_strfreev(argv); g_free(utf8_working_dir); @@ -497,35 +498,6 @@ static gboolean build_iofunc(GIOChannel *ioc, GIOCondition cond, gpointer data) } -static GIOChannel *build_set_up_io_channel(gint fd, GIOCondition cond, GIOFunc func, gpointer data) -{ - GIOChannel *ioc; - GError *error = NULL; - const gchar *encoding; - - ioc = g_io_channel_unix_new(fd); - - g_io_channel_set_flags(ioc, G_IO_FLAG_NONBLOCK, NULL); - if (! g_get_charset(&encoding)) - { // hope this works reliably - g_io_channel_set_encoding(ioc, encoding, &error); - if (error) - { - geany_debug("compile: %s", error->message); - g_error_free(error); - return ioc; - } - } - // "auto-close" ;-) - g_io_channel_set_close_on_unref(ioc, TRUE); - - g_io_add_watch(ioc, cond, func, data); - g_io_channel_unref(ioc); - - return ioc; -} - - void build_exit_cb(GPid child_pid, gint status, gpointer user_data) { #ifdef G_OS_UNIX diff --git a/src/callbacks.c b/src/callbacks.c index 6225732c..67f462b7 100644 --- a/src/callbacks.c +++ b/src/callbacks.c @@ -48,6 +48,7 @@ #include "treeviews.h" #include "keybindings.h" #include "encodings.h" +#include "search.h" #ifdef GEANY_WIN32 @@ -1437,52 +1438,11 @@ on_tree_view_button_press_event (GtkWidget *widget, { if (GPOINTER_TO_INT(user_data) == 4) { // double click in the message treeview (results of 'Find usage') - GtkTreeIter iter; - GtkTreeModel *model; - GtkTreeSelection *selection; - gint idx; - gint line; - - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(msgwindow.tree_msg)); - if (gtk_tree_selection_get_selected(selection, &model, &iter)) - { - gtk_tree_model_get(model, &iter, 0, &line, 1, &idx, -1); - if (idx >= 0 && doc_list[idx].is_valid) - { - utils_goto_line(idx, line); - } - } + msgwin_goto_messages_file_line(); } else if (GPOINTER_TO_INT(user_data) == 5) { // double click in the compiler treeview - GtkTreeIter iter; - GtkTreeModel *model; - GtkTreeSelection *selection; - gchar *string; - - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(msgwindow.tree_compiler)); - if (gtk_tree_selection_get_selected(selection, &model, &iter)) - { - gtk_tree_model_get(model, &iter, 1, &string, -1); - if (string != NULL) - { - gint line; - gint idx; - gchar *filename; - utils_parse_compiler_error_line(string, &filename, &line); - if (filename != NULL && line > -1) - { - // use document_open_file to find an already open file, or open it in place - idx = document_open_file(-1, filename, 0, FALSE, NULL); - // document_set_indicator will check valid idx - document_set_indicator(idx, line - 1); - // utils_goto_file_line will check valid filename. - utils_goto_file_line(filename, FALSE, line); - } - g_free(filename); - } - g_free(string); - } + msgwin_goto_compiler_file_line(); } } @@ -1986,6 +1946,43 @@ on_replace_entry_activate (GtkEntry *entry, } +void +on_find_in_files1_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + dialogs_show_find_in_files(); +} + + +void +on_find_in_files_dialog_response (GtkDialog *dialog, + gint response, + gpointer user_data) +{ + if (response == GTK_RESPONSE_ACCEPT) + { + const gchar *entry_text = + gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(user_data)))); + gchar *search_text = g_strstrip(g_strdup(entry_text)); + + if (search_text && *search_text) + { + gchar *cur_dir = utils_get_current_file_dir(); + if (cur_dir) + search_find_in_files(search_text, cur_dir); + else + msgwin_status_add(_("Invalid directory for find in files.")); + g_free(cur_dir); + } + else + msgwin_status_add(_("No text to find.")); + + g_free(search_text); + } + gtk_widget_hide(app->find_in_files_dialog); +} + + void on_toolbutton_new_clicked (GtkToolButton *toolbutton, gpointer user_data) diff --git a/src/callbacks.h b/src/callbacks.h index 892634ad..2fd373ba 100644 --- a/src/callbacks.h +++ b/src/callbacks.h @@ -394,9 +394,17 @@ on_replace_entry_activate (GtkEntry *entry, gpointer user_data); void -on_new_with_template (GtkMenuItem *menuitem, +on_find_in_files1_activate (GtkMenuItem *menuitem, gpointer user_data); +void +on_find_in_files_dialog_response (GtkDialog *dialog, + gint response, + gpointer user_data); + +void +on_new_with_template (GtkMenuItem *menuitem, + gpointer user_data); void on_toolbutton_new_clicked (GtkToolButton *toolbutton, diff --git a/src/dialogs.c b/src/dialogs.c index 005304c6..a5ce5d4b 100644 --- a/src/dialogs.c +++ b/src/dialogs.c @@ -801,6 +801,8 @@ void dialogs_show_find(void) gint idx = document_get_cur_idx(); gchar *sel = NULL; + if (idx == -1 || ! doc_list[idx].is_valid) return; + if (sci_get_lines_selected(doc_list[idx].sci) == 1) { sel = g_malloc(sci_get_selected_text_length(doc_list[idx].sci)); @@ -817,7 +819,7 @@ void dialogs_show_find(void) gtk_dialog_add_button(GTK_DIALOG(app->find_dialog), "gtk-find", GTK_RESPONSE_ACCEPT); - label = gtk_label_new(_("Enter the search text here")); + label = gtk_label_new(_("Enter the search text here:")); gtk_misc_set_padding(GTK_MISC(label), 0, 6); gtk_misc_set_alignment(GTK_MISC(label), 0, 0); @@ -925,11 +927,11 @@ void dialogs_show_replace(void) gtk_dialog_add_action_widget(GTK_DIALOG(app->replace_dialog), button, GEANY_RESPONSE_REPLACE); - label_find = gtk_label_new(_("Enter the search text here")); + label_find = gtk_label_new(_("Enter the search text here:")); gtk_misc_set_padding(GTK_MISC(label_find), 0, 6); gtk_misc_set_alignment(GTK_MISC(label_find), 0, 0); - label_replace = gtk_label_new(_("Enter the replace text here")); + label_replace = gtk_label_new(_("Enter the replace text here:")); gtk_misc_set_padding(GTK_MISC(label_replace), 0, 6); gtk_misc_set_alignment(GTK_MISC(label_replace), 0, 0); @@ -1014,6 +1016,81 @@ void dialogs_show_replace(void) g_free(sel); } + +void dialogs_show_find_in_files(void) +{ + static GtkWidget *dirlabel = NULL, *combo = NULL; + GtkWidget *entry; //the child GtkEntry of combo + gint idx = document_get_cur_idx(); + gchar *sel = NULL; + gchar *cur_dir, *dirtext; + + if (idx == -1 || ! doc_list[idx].is_valid) return; + + if (app->find_in_files_dialog == NULL) + { + GtkWidget *label; + + app->find_in_files_dialog = gtk_dialog_new_with_buttons( + _("Find in files"), GTK_WINDOW(app->window), GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL); + + gtk_dialog_add_button(GTK_DIALOG(app->find_in_files_dialog), "gtk-find", GTK_RESPONSE_ACCEPT); + gtk_dialog_set_default_response(GTK_DIALOG(app->find_in_files_dialog), + GTK_RESPONSE_ACCEPT); + + dirlabel = gtk_label_new(""); + gtk_misc_set_alignment(GTK_MISC(dirlabel), 0, 0); + + label = gtk_label_new(_("Enter the search text here:")); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + + combo = gtk_combo_box_entry_new_text(); + entry = gtk_bin_get_child(GTK_BIN(combo)); + gtk_entry_set_max_length(GTK_ENTRY(entry), 248); + gtk_entry_set_width_chars(GTK_ENTRY(entry), 50); + gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE); + + g_signal_connect((gpointer) app->find_in_files_dialog, "response", + G_CALLBACK(on_find_in_files_dialog_response), combo); + g_signal_connect((gpointer) app->find_in_files_dialog, "delete_event", + G_CALLBACK(gtk_widget_hide), NULL); + + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(app->find_in_files_dialog)->vbox), + dirlabel, TRUE, TRUE, 6); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(app->find_in_files_dialog)->vbox), + label, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(app->find_in_files_dialog)->vbox), + combo, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(app->find_in_files_dialog)->vbox), + gtk_label_new(""), TRUE, TRUE, 0); + + gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(app->find_in_files_dialog)->vbox), 6); + + gtk_widget_show_all(app->find_in_files_dialog); + } + + if (sci_get_lines_selected(doc_list[idx].sci) == 1) + { + sel = g_malloc(sci_get_selected_text_length(doc_list[idx].sci)); + sci_get_selected_text(doc_list[idx].sci, sel); + } + + entry = GTK_BIN(combo)->child; + if (sel) gtk_entry_set_text(GTK_ENTRY(entry), sel); + g_free(sel); + gtk_widget_grab_focus(entry); + + cur_dir = utils_get_current_file_dir(); + dirtext = g_strdup_printf(_("Current directory: %s"), cur_dir); + g_free(cur_dir); + gtk_label_set_text(GTK_LABEL(dirlabel), dirtext); + g_free(dirtext); + + gtk_widget_show(app->find_in_files_dialog); +} + + void dialogs_show_includes_arguments_tex(void) { GtkWidget *dialog, *label, *entries[4]; diff --git a/src/dialogs.h b/src/dialogs.h index e4f56c60..613f4eea 100644 --- a/src/dialogs.h +++ b/src/dialogs.h @@ -61,6 +61,8 @@ void dialogs_show_find(void); void dialogs_show_replace(void); +void dialogs_show_find_in_files(void); + void dialogs_show_goto_line(void); void dialogs_show_includes_arguments_gen(void); diff --git a/src/geany.h b/src/geany.h index 5cd98eee..8a630365 100644 --- a/src/geany.h +++ b/src/geany.h @@ -78,6 +78,7 @@ #define GEANY_DEFAULT_TOOLS_TERMINAL "xterm" #define GEANY_DEFAULT_TOOLS_BROWSER "mozilla" #define GEANY_DEFAULT_TOOLS_PRINTCMD "lpr" +#define GEANY_DEFAULT_TOOLS_GREP "grep" #define GEANY_DEFAULT_MRU_LENGHTH 10 #define GEANY_DEFAULT_FONT_SYMBOL_LIST "Cursor 8" #define GEANY_DEFAULT_FONT_MSG_WINDOW "Cursor 8" @@ -202,6 +203,7 @@ typedef struct MyApp gchar *tools_make_cmd; gchar *tools_term_cmd; gchar *tools_print_cmd; + gchar *tools_grep_cmd; GtkIconSize toolbar_icon_size; GtkWidget *toolbar; GtkWidget *run_button; @@ -230,6 +232,7 @@ typedef struct MyApp GtkWidget *prefs_dialog; GtkWidget *find_dialog; GtkWidget *replace_dialog; + GtkWidget *find_in_files_dialog; GtkWidget *default_tag_tree; const TMWorkspace *tm_workspace; GQueue *recent_queue; diff --git a/src/interface.c b/src/interface.c index 61227bfc..f7cf4931 100644 --- a/src/interface.c +++ b/src/interface.c @@ -37,7 +37,7 @@ create_window1 (void) GtkWidget *menuitem1_menu; GtkWidget *menu_new1; GtkWidget *menu_new_with_template1; - GtkWidget *image791; + GtkWidget *image799; GtkWidget *menu_new_with_template1_menu; GtkWidget *invisible2; GtkWidget *separator12; @@ -49,7 +49,7 @@ create_window1 (void) GtkWidget *menu_save1; GtkWidget *menu_save_as1; GtkWidget *menu_save_all1; - GtkWidget *image792; + GtkWidget *image800; GtkWidget *revert1; GtkWidget *separator21; GtkWidget *preferences2; @@ -58,7 +58,7 @@ create_window1 (void) GtkWidget *separator14; GtkWidget *menu_close1; GtkWidget *menu_close_all1; - GtkWidget *image793; + GtkWidget *image801; GtkWidget *menu_separatormenuitem1; GtkWidget *menu_quit1; GtkWidget *edit1; @@ -74,11 +74,11 @@ create_window1 (void) GtkWidget *menu_select_all1; GtkWidget *separator25; GtkWidget *insert_include2; - GtkWidget *image794; + GtkWidget *image802; GtkWidget *insert_include2_menu; GtkWidget *invisible4; GtkWidget *add_comments1; - GtkWidget *image795; + GtkWidget *image803; GtkWidget *add_comments1_menu; GtkWidget *menu_add_changelog_entry1; GtkWidget *insert_file_header1; @@ -99,16 +99,18 @@ create_window1 (void) GtkWidget *find_next1; GtkWidget *find_previous1; GtkWidget *replace1; - GtkWidget *image796; + GtkWidget *image804; + GtkWidget *separator27; + GtkWidget *find_in_files1; GtkWidget *separator11; GtkWidget *go_to_line1; - GtkWidget *image797; + GtkWidget *image805; GtkWidget *menuitem3; GtkWidget *menuitem3_menu; GtkWidget *menu_change_font1; - GtkWidget *image798; + GtkWidget *image806; GtkWidget *menu_choose_color1; - GtkWidget *image799; + GtkWidget *image807; GtkWidget *menu_separator4; GtkWidget *menu_fullscreen1; GtkWidget *menu_show_messages_window1; @@ -243,9 +245,9 @@ create_window1 (void) gtk_widget_show (menu_new_with_template1); gtk_container_add (GTK_CONTAINER (menuitem1_menu), menu_new_with_template1); - image791 = gtk_image_new_from_stock ("gtk-new", GTK_ICON_SIZE_MENU); - gtk_widget_show (image791); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_new_with_template1), image791); + image799 = gtk_image_new_from_stock ("gtk-new", GTK_ICON_SIZE_MENU); + gtk_widget_show (image799); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_new_with_template1), image799); menu_new_with_template1_menu = gtk_menu_new (); gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_new_with_template1), menu_new_with_template1_menu); @@ -290,9 +292,9 @@ create_window1 (void) gtk_container_add (GTK_CONTAINER (menuitem1_menu), menu_save_all1); gtk_tooltips_set_tip (tooltips, menu_save_all1, _("Saves all open files"), NULL); - image792 = gtk_image_new_from_stock ("gtk-save", GTK_ICON_SIZE_MENU); - gtk_widget_show (image792); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_save_all1), image792); + image800 = gtk_image_new_from_stock ("gtk-save", GTK_ICON_SIZE_MENU); + gtk_widget_show (image800); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_save_all1), image800); revert1 = gtk_image_menu_item_new_from_stock ("gtk-revert-to-saved", accel_group); gtk_widget_show (revert1); @@ -331,9 +333,9 @@ create_window1 (void) gtk_container_add (GTK_CONTAINER (menuitem1_menu), menu_close_all1); gtk_tooltips_set_tip (tooltips, menu_close_all1, _("Closes all open files"), NULL); - image793 = gtk_image_new_from_stock ("gtk-close", GTK_ICON_SIZE_MENU); - gtk_widget_show (image793); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_close_all1), image793); + image801 = gtk_image_new_from_stock ("gtk-close", GTK_ICON_SIZE_MENU); + gtk_widget_show (image801); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_close_all1), image801); menu_separatormenuitem1 = gtk_separator_menu_item_new (); gtk_widget_show (menu_separatormenuitem1); @@ -399,9 +401,9 @@ create_window1 (void) gtk_widget_show (insert_include2); gtk_container_add (GTK_CONTAINER (edit1_menu), insert_include2); - image794 = gtk_image_new_from_stock ("gtk-add", GTK_ICON_SIZE_MENU); - gtk_widget_show (image794); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (insert_include2), image794); + image802 = gtk_image_new_from_stock ("gtk-add", GTK_ICON_SIZE_MENU); + gtk_widget_show (image802); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (insert_include2), image802); insert_include2_menu = gtk_menu_new (); gtk_menu_item_set_submenu (GTK_MENU_ITEM (insert_include2), insert_include2_menu); @@ -413,9 +415,9 @@ create_window1 (void) gtk_widget_show (add_comments1); gtk_container_add (GTK_CONTAINER (edit1_menu), add_comments1); - image795 = gtk_image_new_from_stock ("gtk-add", GTK_ICON_SIZE_MENU); - gtk_widget_show (image795); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (add_comments1), image795); + image803 = gtk_image_new_from_stock ("gtk-add", GTK_ICON_SIZE_MENU); + gtk_widget_show (image803); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (add_comments1), image803); add_comments1_menu = gtk_menu_new (); gtk_menu_item_set_submenu (GTK_MENU_ITEM (add_comments1), add_comments1_menu); @@ -502,9 +504,18 @@ create_window1 (void) gtk_widget_show (replace1); gtk_container_add (GTK_CONTAINER (search1_menu), replace1); - image796 = gtk_image_new_from_stock ("gtk-find-and-replace", GTK_ICON_SIZE_MENU); - gtk_widget_show (image796); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (replace1), image796); + image804 = gtk_image_new_from_stock ("gtk-find-and-replace", GTK_ICON_SIZE_MENU); + gtk_widget_show (image804); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (replace1), image804); + + separator27 = gtk_separator_menu_item_new (); + gtk_widget_show (separator27); + gtk_container_add (GTK_CONTAINER (search1_menu), separator27); + gtk_widget_set_sensitive (separator27, FALSE); + + find_in_files1 = gtk_menu_item_new_with_mnemonic (_("Find in f_iles")); + gtk_widget_show (find_in_files1); + gtk_container_add (GTK_CONTAINER (search1_menu), find_in_files1); separator11 = gtk_separator_menu_item_new (); gtk_widget_show (separator11); @@ -515,9 +526,9 @@ create_window1 (void) gtk_widget_show (go_to_line1); gtk_container_add (GTK_CONTAINER (search1_menu), go_to_line1); - image797 = gtk_image_new_from_stock ("gtk-jump-to", GTK_ICON_SIZE_MENU); - gtk_widget_show (image797); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (go_to_line1), image797); + image805 = gtk_image_new_from_stock ("gtk-jump-to", GTK_ICON_SIZE_MENU); + gtk_widget_show (image805); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (go_to_line1), image805); menuitem3 = gtk_menu_item_new_with_mnemonic (_("_View")); gtk_widget_show (menuitem3); @@ -531,18 +542,18 @@ create_window1 (void) gtk_container_add (GTK_CONTAINER (menuitem3_menu), menu_change_font1); gtk_tooltips_set_tip (tooltips, menu_change_font1, _("Change the default font"), NULL); - image798 = gtk_image_new_from_stock ("gtk-select-font", GTK_ICON_SIZE_MENU); - gtk_widget_show (image798); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_change_font1), image798); + image806 = gtk_image_new_from_stock ("gtk-select-font", GTK_ICON_SIZE_MENU); + gtk_widget_show (image806); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_change_font1), image806); menu_choose_color1 = gtk_image_menu_item_new_with_mnemonic (_("Show _Colour Chooser")); gtk_widget_show (menu_choose_color1); gtk_container_add (GTK_CONTAINER (menuitem3_menu), menu_choose_color1); gtk_tooltips_set_tip (tooltips, menu_choose_color1, _("Open a color chooser dialog, to interactively pick colors from a palette."), NULL); - image799 = gtk_image_new_from_stock ("gtk-select-color", GTK_ICON_SIZE_MENU); - gtk_widget_show (image799); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_choose_color1), image799); + image807 = gtk_image_new_from_stock ("gtk-select-color", GTK_ICON_SIZE_MENU); + gtk_widget_show (image807); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_choose_color1), image807); menu_separator4 = gtk_separator_menu_item_new (); gtk_widget_show (menu_separator4); @@ -1096,6 +1107,9 @@ create_window1 (void) g_signal_connect ((gpointer) replace1, "activate", G_CALLBACK (on_replace1_activate), NULL); + g_signal_connect ((gpointer) find_in_files1, "activate", + G_CALLBACK (on_find_in_files1_activate), + NULL); g_signal_connect ((gpointer) go_to_line1, "activate", G_CALLBACK (on_go_to_line1_activate), NULL); @@ -1250,7 +1264,7 @@ create_window1 (void) GLADE_HOOKUP_OBJECT (window1, menuitem1_menu, "menuitem1_menu"); GLADE_HOOKUP_OBJECT (window1, menu_new1, "menu_new1"); GLADE_HOOKUP_OBJECT (window1, menu_new_with_template1, "menu_new_with_template1"); - GLADE_HOOKUP_OBJECT (window1, image791, "image791"); + GLADE_HOOKUP_OBJECT (window1, image799, "image799"); GLADE_HOOKUP_OBJECT (window1, menu_new_with_template1_menu, "menu_new_with_template1_menu"); GLADE_HOOKUP_OBJECT (window1, invisible2, "invisible2"); GLADE_HOOKUP_OBJECT (window1, separator12, "separator12"); @@ -1262,7 +1276,7 @@ create_window1 (void) GLADE_HOOKUP_OBJECT (window1, menu_save1, "menu_save1"); GLADE_HOOKUP_OBJECT (window1, menu_save_as1, "menu_save_as1"); GLADE_HOOKUP_OBJECT (window1, menu_save_all1, "menu_save_all1"); - GLADE_HOOKUP_OBJECT (window1, image792, "image792"); + GLADE_HOOKUP_OBJECT (window1, image800, "image800"); GLADE_HOOKUP_OBJECT (window1, revert1, "revert1"); GLADE_HOOKUP_OBJECT (window1, separator21, "separator21"); GLADE_HOOKUP_OBJECT (window1, preferences2, "preferences2"); @@ -1271,7 +1285,7 @@ create_window1 (void) GLADE_HOOKUP_OBJECT (window1, separator14, "separator14"); GLADE_HOOKUP_OBJECT (window1, menu_close1, "menu_close1"); GLADE_HOOKUP_OBJECT (window1, menu_close_all1, "menu_close_all1"); - GLADE_HOOKUP_OBJECT (window1, image793, "image793"); + GLADE_HOOKUP_OBJECT (window1, image801, "image801"); GLADE_HOOKUP_OBJECT (window1, menu_separatormenuitem1, "menu_separatormenuitem1"); GLADE_HOOKUP_OBJECT (window1, menu_quit1, "menu_quit1"); GLADE_HOOKUP_OBJECT (window1, edit1, "edit1"); @@ -1287,11 +1301,11 @@ create_window1 (void) GLADE_HOOKUP_OBJECT (window1, menu_select_all1, "menu_select_all1"); GLADE_HOOKUP_OBJECT (window1, separator25, "separator25"); GLADE_HOOKUP_OBJECT (window1, insert_include2, "insert_include2"); - GLADE_HOOKUP_OBJECT (window1, image794, "image794"); + GLADE_HOOKUP_OBJECT (window1, image802, "image802"); GLADE_HOOKUP_OBJECT (window1, insert_include2_menu, "insert_include2_menu"); GLADE_HOOKUP_OBJECT (window1, invisible4, "invisible4"); GLADE_HOOKUP_OBJECT (window1, add_comments1, "add_comments1"); - GLADE_HOOKUP_OBJECT (window1, image795, "image795"); + GLADE_HOOKUP_OBJECT (window1, image803, "image803"); GLADE_HOOKUP_OBJECT (window1, add_comments1_menu, "add_comments1_menu"); GLADE_HOOKUP_OBJECT (window1, menu_add_changelog_entry1, "menu_add_changelog_entry1"); GLADE_HOOKUP_OBJECT (window1, insert_file_header1, "insert_file_header1"); @@ -1312,16 +1326,18 @@ create_window1 (void) GLADE_HOOKUP_OBJECT (window1, find_next1, "find_next1"); GLADE_HOOKUP_OBJECT (window1, find_previous1, "find_previous1"); GLADE_HOOKUP_OBJECT (window1, replace1, "replace1"); - GLADE_HOOKUP_OBJECT (window1, image796, "image796"); + GLADE_HOOKUP_OBJECT (window1, image804, "image804"); + GLADE_HOOKUP_OBJECT (window1, separator27, "separator27"); + GLADE_HOOKUP_OBJECT (window1, find_in_files1, "find_in_files1"); GLADE_HOOKUP_OBJECT (window1, separator11, "separator11"); GLADE_HOOKUP_OBJECT (window1, go_to_line1, "go_to_line1"); - GLADE_HOOKUP_OBJECT (window1, image797, "image797"); + GLADE_HOOKUP_OBJECT (window1, image805, "image805"); GLADE_HOOKUP_OBJECT (window1, menuitem3, "menuitem3"); GLADE_HOOKUP_OBJECT (window1, menuitem3_menu, "menuitem3_menu"); GLADE_HOOKUP_OBJECT (window1, menu_change_font1, "menu_change_font1"); - GLADE_HOOKUP_OBJECT (window1, image798, "image798"); + GLADE_HOOKUP_OBJECT (window1, image806, "image806"); GLADE_HOOKUP_OBJECT (window1, menu_choose_color1, "menu_choose_color1"); - GLADE_HOOKUP_OBJECT (window1, image799, "image799"); + GLADE_HOOKUP_OBJECT (window1, image807, "image807"); GLADE_HOOKUP_OBJECT (window1, menu_separator4, "menu_separator4"); GLADE_HOOKUP_OBJECT (window1, menu_fullscreen1, "menu_fullscreen1"); GLADE_HOOKUP_OBJECT (window1, menu_show_messages_window1, "menu_show_messages_window1"); @@ -2005,6 +2021,10 @@ create_prefs_dialog (void) GtkWidget *entry_print; GtkWidget *button_print; GtkWidget *image763; + GtkWidget *label171; + GtkWidget *entry_grep; + GtkWidget *button_grep; + GtkWidget *image808; GtkWidget *label96; GtkWidget *vbox9; GtkWidget *label120; @@ -2769,7 +2789,7 @@ create_prefs_dialog (void) gtk_box_pack_start (GTK_BOX (vbox2), label17, FALSE, FALSE, 0); gtk_misc_set_padding (GTK_MISC (label17), 0, 6); - table1 = gtk_table_new (4, 3, FALSE); + table1 = gtk_table_new (5, 3, FALSE); gtk_widget_show (table1); gtk_box_pack_start (GTK_BOX (vbox2), table1, TRUE, TRUE, 0); gtk_table_set_row_spacings (GTK_TABLE (table1), 7); @@ -2871,6 +2891,29 @@ create_prefs_dialog (void) gtk_widget_show (image763); gtk_container_add (GTK_CONTAINER (button_print), image763); + label171 = gtk_label_new (_("Grep")); + gtk_widget_show (label171); + gtk_table_attach (GTK_TABLE (table1), label171, 0, 1, 4, 5, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_misc_set_alignment (GTK_MISC (label171), 0, 0.5); + + entry_grep = gtk_entry_new (); + gtk_widget_show (entry_grep); + gtk_table_attach (GTK_TABLE (table1), entry_grep, 1, 2, 4, 5, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + button_grep = gtk_button_new (); + gtk_widget_show (button_grep); + gtk_table_attach (GTK_TABLE (table1), button_grep, 2, 3, 4, 5, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + image808 = gtk_image_new_from_stock ("gtk-directory", GTK_ICON_SIZE_BUTTON); + gtk_widget_show (image808); + gtk_container_add (GTK_CONTAINER (button_grep), image808); + label96 = gtk_label_new (_("Tools")); gtk_widget_show (label96); gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook2), gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook2), 4), label96); @@ -3159,6 +3202,10 @@ create_prefs_dialog (void) GLADE_HOOKUP_OBJECT (prefs_dialog, entry_print, "entry_print"); GLADE_HOOKUP_OBJECT (prefs_dialog, button_print, "button_print"); GLADE_HOOKUP_OBJECT (prefs_dialog, image763, "image763"); + GLADE_HOOKUP_OBJECT (prefs_dialog, label171, "label171"); + GLADE_HOOKUP_OBJECT (prefs_dialog, entry_grep, "entry_grep"); + GLADE_HOOKUP_OBJECT (prefs_dialog, button_grep, "button_grep"); + GLADE_HOOKUP_OBJECT (prefs_dialog, image808, "image808"); GLADE_HOOKUP_OBJECT (prefs_dialog, label96, "label96"); GLADE_HOOKUP_OBJECT (prefs_dialog, vbox9, "vbox9"); GLADE_HOOKUP_OBJECT (prefs_dialog, label120, "label120"); diff --git a/src/keyfile.c b/src/keyfile.c index 4cad0888..3b83aca3 100644 --- a/src/keyfile.c +++ b/src/keyfile.c @@ -145,6 +145,7 @@ void configuration_save(void) g_key_file_set_string(config, "tools", "term_cmd", app->tools_term_cmd ? app->tools_term_cmd : ""); g_key_file_set_string(config, "tools", "browser_cmd", app->tools_browser_cmd ? app->tools_browser_cmd : ""); g_key_file_set_string(config, "tools", "print_cmd", app->tools_print_cmd ? app->tools_print_cmd : ""); + g_key_file_set_string(config, "tools", "grep_cmd", app->tools_grep_cmd? app->tools_grep_cmd: ""); for (i = 0; i < app->mru_length; i++) { @@ -337,6 +338,10 @@ gboolean configuration_load(void) g_free(tmp_string); g_free(tmp_string2); + tmp_string = g_find_program_in_path(GEANY_DEFAULT_TOOLS_GREP); + app->tools_grep_cmd = utils_get_setting_string(config, "tools", "grep_cmd", tmp_string); + g_free(tmp_string); + recent_files = g_key_file_get_string_list(config, "files", "recent_files", &len, NULL); if (recent_files != NULL) { diff --git a/src/main.c b/src/main.c index 4811e1ee..efc56115 100644 --- a/src/main.c +++ b/src/main.c @@ -217,6 +217,7 @@ static void main_init(void) app->prefs_dialog = NULL; app->find_dialog = NULL; app->replace_dialog = NULL; + app->find_in_files_dialog = NULL; app->default_tag_tree = NULL; app->main_window_realized= FALSE; app->tab_order_ltr = FALSE; diff --git a/src/msgwindow.c b/src/msgwindow.c index 1b54acca..f01face6 100644 --- a/src/msgwindow.c +++ b/src/msgwindow.c @@ -29,10 +29,18 @@ #include "callbacks.h" #include "msgwindow.h" #include "utils.h" +#include "search.h" +#include "document.h" + +#include +#include + static GdkColor dark = {0, 58832, 58832, 58832}; static GdkColor white = {0, 65535, 65535, 65535}; +static void msgwin_parse_grep_line(const gchar *string, gchar **filename, gint *line); + /* does some preparing things to the status message list widget */ void msgwin_prepare_status_tree_view(void) @@ -235,3 +243,139 @@ GtkWidget *msgwin_create_message_popup_menu(gint type) return message_popup_menu; } + +gboolean msgwin_goto_compiler_file_line() +{ + GtkTreeIter iter; + GtkTreeModel *model; + GtkTreeSelection *selection; + gchar *string; + gboolean ret = FALSE; + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(msgwindow.tree_compiler)); + if (gtk_tree_selection_get_selected(selection, &model, &iter)) + { + gtk_tree_model_get(model, &iter, 1, &string, -1); + if (string != NULL) + { + gint line; + gint idx; + gchar *filename; + utils_parse_compiler_error_line(string, &filename, &line); + if (filename != NULL && line > -1) + { + // use document_open_file to find an already open file, or open it in place + idx = document_open_file(-1, filename, 0, FALSE, NULL); + // document_set_indicator will check valid idx + document_set_indicator(idx, line - 1); + // utils_goto_file_line will check valid filename. + ret = utils_goto_file_line(filename, FALSE, line); + } + g_free(filename); + } + g_free(string); + } + return ret; +} + + +gboolean msgwin_goto_messages_file_line() +{ + GtkTreeIter iter; + GtkTreeModel *model; + GtkTreeSelection *selection; + gboolean ret = FALSE; + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(msgwindow.tree_msg)); + if (gtk_tree_selection_get_selected(selection, &model, &iter)) + { + gint idx, line; + gchar *string; + + gtk_tree_model_get(model, &iter, 0, &line, 1, &idx, 3, &string, -1); + if (line >= 0 && idx >= 0) + utils_goto_line(idx, line); //checks valid idx + else if (line < 0 && string != NULL) + { + gchar *filename; + msgwin_parse_grep_line(string, &filename, &line); + if (filename != NULL && line > -1) + { + // use document_open_file to find an already open file, or open it in place + idx = document_open_file(-1, filename, 0, FALSE, NULL); + // utils_goto_file_line will check valid filename. + ret = utils_goto_file_line(filename, FALSE, line); + } + g_free(filename); + } + g_free(string); + } + return ret; +} + + +// Taken from utils_parse_compiler_error_line, could refactor both (keep get_cur_idx). +/* Try to parse the file and line number for string and when something useful is + * found, store the line number in *line and the relevant file with the error in + * *filename. + * *line will be -1 if no error was found in string. + * *filename must be freed unless NULL. */ +static void msgwin_parse_grep_line(const gchar *string, gchar **filename, gint *line) +{ + gchar *end = NULL; + gchar *path; + gchar **fields; + gchar *pattern; // pattern to split the error message into some fields + guint field_min_len; // used to detect errors after parsing + guint field_idx_line; // idx of the field where the line is + guint field_idx_file; // idx of the field where the filename is + guint skip_dot_slash = 0; // number of characters to skip at the beginning of the filename + gint cur_idx; + + *filename = NULL; + *line = -1; + + if (string == NULL) return; + + // conflict:3:conflicting types for `foo' + pattern = ":"; + field_min_len = 3; + field_idx_line = 1; + field_idx_file = 0; + + fields = g_strsplit_set(string, pattern, field_min_len); + + // parse the line + if (g_strv_length(fields) < field_min_len) + { + g_strfreev(fields); + return; + } + + *line = strtol(fields[field_idx_line], &end, 10); + + // if the line could not be read, line is 0 and an error occurred, so we leave + if (fields[field_idx_line] == end) + { + g_strfreev(fields); + return; + } + + // skip some characters at the beginning of the filename, at the moment only "./" + // can be extended if other "trash" is known + if (strncmp(fields[field_idx_file], "./", 2) == 0) skip_dot_slash = 2; + + // get the basename of the built file to get the path to look for other files + cur_idx = document_get_cur_idx(); + if (cur_idx >= 0 && doc_list[cur_idx].is_valid) + { + path = g_path_get_dirname(doc_list[cur_idx].file_name); + *filename = g_strconcat(path, G_DIR_SEPARATOR_S, + fields[field_idx_file] + skip_dot_slash, NULL); + g_free(path); + } + + g_strfreev(fields); +} + + diff --git a/src/msgwindow.h b/src/msgwindow.h index e453c2df..f2bbf4d4 100644 --- a/src/msgwindow.h +++ b/src/msgwindow.h @@ -73,4 +73,8 @@ void msgwin_status_add(gchar const *format, ...); GtkWidget *msgwin_create_message_popup_menu(gint type); +gboolean msgwin_goto_compiler_file_line(); + +gboolean msgwin_goto_messages_file_line(); + #endif diff --git a/src/prefs.c b/src/prefs.c index 9366a92e..184377da 100644 --- a/src/prefs.c +++ b/src/prefs.c @@ -227,7 +227,7 @@ void prefs_init_dialog(void) // Tools Settings #ifdef GEANY_WIN32 - // hide related Terminal path setting + // hide related Make path setting gtk_widget_set_sensitive(lookup_widget(app->prefs_dialog, "label11"), FALSE); gtk_widget_set_sensitive(lookup_widget(app->prefs_dialog, "entry_com_make"), FALSE); gtk_widget_set_sensitive(lookup_widget(app->prefs_dialog, "button_make"), FALSE); @@ -246,9 +246,12 @@ void prefs_init_dialog(void) if (app->tools_browser_cmd) gtk_entry_set_text(GTK_ENTRY(lookup_widget(app->prefs_dialog, "entry_browser")), app->tools_browser_cmd); - if (app->tools_browser_cmd) + if (app->tools_print_cmd) gtk_entry_set_text(GTK_ENTRY(lookup_widget(app->prefs_dialog, "entry_print")), app->tools_print_cmd); + if (app->tools_grep_cmd) + gtk_entry_set_text(GTK_ENTRY(lookup_widget(app->prefs_dialog, "entry_grep")), app->tools_grep_cmd); + // Template settings widget = lookup_widget(app->prefs_dialog, "entry_template_developer"); @@ -512,6 +515,10 @@ void on_prefs_button_clicked(GtkDialog *dialog, gint response, gpointer user_dat g_free(app->tools_print_cmd); app->tools_print_cmd = g_strdup(gtk_entry_get_text(GTK_ENTRY(widget))); + widget = lookup_widget(app->prefs_dialog, "entry_grep"); + g_free(app->tools_grep_cmd); + app->tools_grep_cmd = g_strdup(gtk_entry_get_text(GTK_ENTRY(widget))); + // Template settings widget = lookup_widget(app->prefs_dialog, "entry_template_developer"); @@ -1070,6 +1077,8 @@ void dialogs_show_prefs_dialog(void) "clicked", G_CALLBACK(on_pref_tools_button_clicked), lookup_widget(app->prefs_dialog, "entry_browser")); g_signal_connect((gpointer) lookup_widget(app->prefs_dialog, "button_print"), "clicked", G_CALLBACK(on_pref_tools_button_clicked), lookup_widget(app->prefs_dialog, "entry_print")); + g_signal_connect((gpointer) lookup_widget(app->prefs_dialog, "button_grep"), + "clicked", G_CALLBACK(on_pref_tools_button_clicked), lookup_widget(app->prefs_dialog, "entry_grep")); g_signal_connect((gpointer) lookup_widget(app->prefs_dialog, "check_toolbar_show"), "toggled", G_CALLBACK(on_pref_toolbar_show_toggled), NULL); diff --git a/src/search.c b/src/search.c new file mode 100644 index 00000000..65e9a916 --- /dev/null +++ b/src/search.c @@ -0,0 +1,214 @@ +/* + * search.c + * + * Copyright 2006 Nick Treleaven + * + * 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. + * + * $Id$ + */ + +#include "geany.h" +#include "search.h" +#include "support.h" +#include "utils.h" +#include "msgwindow.h" + +#include +#include + +#ifdef G_OS_UNIX +# include +# include +#endif + + +static gboolean search_read_io (GIOChannel *source, + GIOCondition condition, + gpointer data); + +static void search_close_pid(GPid child_pid, gint status, gpointer user_data); + +static gchar **search_get_argv(const gchar **argv_prefix, const gchar *dir); + +static GSList *search_get_file_list(const gchar *path, gint *length); + + +gboolean search_find_in_files(const gchar *search_text, const gchar *dir) +{ + const gchar *argv_prefix[] = {app->tools_grep_cmd, "-nHI", "--", search_text, NULL}; + gchar **argv; + GPid child_pid; + gint stdout_fd, stdin_fd; + GError *error = NULL; + gboolean ret = FALSE; + + if (! search_text || ! *search_text || ! dir) return TRUE; + if (! g_file_test(app->tools_grep_cmd, G_FILE_TEST_IS_EXECUTABLE)) + { + msgwin_status_add(_("Cannot execute grep tool '%s';" + " check the path setting in Preferences."), argv_prefix[0]); + return FALSE; + } + + gtk_list_store_clear(msgwindow.store_msg); + gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_MESSAGE); + + argv = search_get_argv(argv_prefix, dir); + if (argv == NULL) return FALSE; + + if (! g_spawn_async_with_pipes(dir, (gchar**)argv, NULL, + G_SPAWN_STDERR_TO_DEV_NULL | G_SPAWN_DO_NOT_REAP_CHILD, + NULL, NULL, &child_pid, + &stdin_fd, &stdout_fd, NULL, &error)) + { + geany_debug("%s: g_spawn_async_with_pipes() failed: %s", __func__, error->message); + msgwin_status_add(_("Process failed (%s)"), error->message); + g_error_free(error); + ret = FALSE; + } + else + { + g_child_watch_add(child_pid, search_close_pid, NULL); + utils_set_up_io_channel(stdout_fd, G_IO_IN|G_IO_PRI|G_IO_ERR|G_IO_HUP|G_IO_NVAL, + search_read_io, NULL); + ret = TRUE; + } + g_strfreev(argv); + return ret; +} + + +/* Creates an argument vector of strings, copying argv_prefix[] values for + * the first arguments, then followed by filenames found in dir. + * Returns NULL if no files were found, otherwise returned vector should be fully freed. */ +static gchar **search_get_argv(const gchar **argv_prefix, const gchar *dir) +{ + guint prefix_len, list_len, i, j; + gchar **argv; + GSList *list, *item; + g_return_val_if_fail(dir != NULL, NULL); + + prefix_len = g_strv_length((gchar**)argv_prefix); + list = search_get_file_list(dir, &list_len); + if (list == NULL) return NULL; + + argv = g_new(gchar*, prefix_len + list_len + 1); + + for (i = 0; i < prefix_len; i++) + argv[i] = g_strdup(argv_prefix[i]); + + item = list; + for (j = 0; j < list_len; j++) + { + argv[i++] = item->data; + item = g_slist_next(item); + } + argv[i] = NULL; + + g_slist_free(list); + return argv; +} + + +/* Gets a list of files in the current directory, or NULL if no files found. + * The list and the data in the list should be freed after use. + * *length is set to the number of non-NULL data items in the list. */ +static GSList *search_get_file_list(const gchar *path, gint *length) +{ + GError *error = NULL; + GSList *list = NULL; + gint len = 0; + const gchar *filename; + GDir *dir; + g_return_val_if_fail(path != NULL, NULL); + + dir = g_dir_open(path, 0, &error); + if (error) + { + geany_debug("Could not open directory (%s)", error->message); + g_error_free(error); + *length = 0; + return NULL; + } + do + { + filename = g_dir_read_name(dir); + list = g_slist_append(list, g_strdup(filename)); + len++; + } while (filename != NULL); + + g_dir_close(dir); + len--; //subtract 1 for the last null data element. + *length = len; + if (len == 0) + { + g_slist_free(list); + return NULL; + } + return list; +} + + +static gboolean search_read_io (GIOChannel *source, + GIOCondition condition, + gpointer data) +{ + if (condition & (G_IO_IN | G_IO_PRI)) + { + //GIOStatus s; + gchar *msg; + + while (g_io_channel_read_line(source, &msg, NULL, NULL, NULL) && msg) + { + //if (s != G_IO_STATUS_NORMAL && s != G_IO_STATUS_EOF) break; + msgwin_msg_add(-1, -1, g_strstrip(msg)); + g_free(msg); + } + } + if (condition & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) + return FALSE; + + return TRUE; +} + + +static void search_close_pid(GPid child_pid, gint status, gpointer user_data) +{ +#ifdef G_OS_UNIX + gboolean failure = FALSE; + + if (WIFEXITED(status)) + { + if (WEXITSTATUS(status) != EXIT_SUCCESS) + failure = TRUE; + } + else if (WIFSIGNALED(status)) + { + // the terminating signal: WTERMSIG (status)); + failure = TRUE; + } + else + { // any other failure occured + failure = TRUE; + } + + msgwin_msg_add(-1, -1, (failure) ? _("Search failed.") : _("Search completed.")); +#endif + utils_beep(); + g_spawn_close_pid(child_pid); +} + + diff --git a/src/search.h b/src/search.h new file mode 100644 index 00000000..47f1a849 --- /dev/null +++ b/src/search.h @@ -0,0 +1,25 @@ +/* + * search.h + * + * Copyright 2006 Nick Treleaven + * + * 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. + * + * $Id$ + */ + + +gboolean search_find_in_files(const gchar *search_text, const gchar *dir); + diff --git a/src/utils.c b/src/utils.c index 1779c4ac..9e1b3085 100644 --- a/src/utils.c +++ b/src/utils.c @@ -2520,6 +2520,35 @@ TMTag *utils_find_tm_tag(const GPtrArray *tags, const gchar *tag_name) } +GIOChannel *utils_set_up_io_channel(gint fd, GIOCondition cond, GIOFunc func, gpointer data) +{ + GIOChannel *ioc; + GError *error = NULL; + const gchar *encoding; + + ioc = g_io_channel_unix_new(fd); + + g_io_channel_set_flags(ioc, G_IO_FLAG_NONBLOCK, NULL); + if (! g_get_charset(&encoding)) + { // hope this works reliably + g_io_channel_set_encoding(ioc, encoding, &error); + if (error) + { + geany_debug("%s: %s", __func__, error->message); + g_error_free(error); + return ioc; + } + } + // "auto-close" ;-) + g_io_channel_set_close_on_unref(ioc, TRUE); + + g_io_add_watch(ioc, cond, func, data); + g_io_channel_unref(ioc); + + return ioc; +} + + void utils_update_toolbar_items(void) { // show toolbar diff --git a/src/utils.h b/src/utils.h index a31b1946..361d20d3 100644 --- a/src/utils.h +++ b/src/utils.h @@ -222,6 +222,8 @@ gchar *utils_get_current_time_string(); TMTag *utils_find_tm_tag(const GPtrArray *tags, const gchar *tag_name); +GIOChannel *utils_set_up_io_channel(gint fd, GIOCondition cond, GIOFunc func, gpointer data); + void utils_update_toolbar_items(void); #endif