From 0d36d61761a1d68839d61f521889dba3db7f514f Mon Sep 17 00:00:00 2001 From: monsta Date: Wed, 9 Sep 2015 12:09:52 +0300 Subject: gsearchtool: move smclient stuff there, move sources to src/ subdir --- gsearchtool/src/Makefile.am | 36 + gsearchtool/src/gsearchtool-callbacks.c | 1953 ++++++++++++++++++++ gsearchtool/src/gsearchtool-callbacks.h | 180 ++ gsearchtool/src/gsearchtool-support.c | 1588 ++++++++++++++++ gsearchtool/src/gsearchtool-support.h | 114 ++ gsearchtool/src/gsearchtool.c | 3070 +++++++++++++++++++++++++++++++ gsearchtool/src/gsearchtool.h | 243 +++ 7 files changed, 7184 insertions(+) create mode 100644 gsearchtool/src/Makefile.am create mode 100644 gsearchtool/src/gsearchtool-callbacks.c create mode 100644 gsearchtool/src/gsearchtool-callbacks.h create mode 100644 gsearchtool/src/gsearchtool-support.c create mode 100644 gsearchtool/src/gsearchtool-support.h create mode 100644 gsearchtool/src/gsearchtool.c create mode 100644 gsearchtool/src/gsearchtool.h (limited to 'gsearchtool/src') diff --git a/gsearchtool/src/Makefile.am b/gsearchtool/src/Makefile.am new file mode 100644 index 00000000..58a042ec --- /dev/null +++ b/gsearchtool/src/Makefile.am @@ -0,0 +1,36 @@ +AM_CPPFLAGS = \ + -I$(top_srcdir)/gsearchtool/libeggsmclient \ + -I$(top_srcdir)/gsearchtool/libmateui-deprecated \ + $(DISABLE_DEPRECATED) \ + -DMATELOCALEDIR=\""$(prefix)/$(DATADIRNAME)/locale"\" \ + -DLOCALEDIR=\"$(datadir)/locale\" \ + -DDATADIR=\""$(datadir)"\" \ + -DGREP_COMMAND=\""$(GREP_COMMAND)"\" + +libeggsmclient_LIB = $(top_builddir)/gsearchtool/libeggsmclient/libeggsmclient.la +libmateui_deprecated_LIB = $(top_builddir)/gsearchtool/libmateui-deprecated/libmateui-deprecated.la + +bin_PROGRAMS = mate-search-tool + +mate_search_tool_SOURCES = \ + gsearchtool-support.c \ + gsearchtool-support.h \ + gsearchtool-callbacks.c \ + gsearchtool-callbacks.h \ + gsearchtool.c \ + gsearchtool.h + +mate_search_tool_CFLAGS = \ + $(GLIB_CFLAGS) \ + $(GIO_CFLAGS) \ + $(GIO_UNIX_CFLAGS) \ + $(GTK_CFLAGS) + +mate_search_tool_LDADD = \ + $(GLIB_LIBS) \ + $(GIO_LIBS) \ + $(GIO_UNIX_LIBS) \ + $(GTK_LIBS) \ + $(libeggsmclient_LIB) \ + $(libmateui_deprecated_LIB) + diff --git a/gsearchtool/src/gsearchtool-callbacks.c b/gsearchtool/src/gsearchtool-callbacks.c new file mode 100644 index 00000000..0c040867 --- /dev/null +++ b/gsearchtool/src/gsearchtool-callbacks.c @@ -0,0 +1,1953 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * MATE Search Tool + * + * File: gsearchtool-callbacks.c + * + * (C) 2002 the Free Software Foundation + * + * Authors: Dennis Cranston + * George Lebl + * + * 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 St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "gsearchtool.h" +#include "gsearchtool-callbacks.h" +#include "gsearchtool-support.h" + +#define SILENT_WINDOW_OPEN_LIMIT 5 + +#ifdef HAVE_GETPGID +extern pid_t getpgid (pid_t); +#endif + +gboolean row_selected_by_button_press_event; + +static void +store_window_state_and_geometry (GSearchWindow *gsearch) +{ + gsearch->window_width = MAX (gsearch->window_width, MINIMUM_WINDOW_WIDTH); + gsearch->window_height = MAX (gsearch->window_height, MINIMUM_WINDOW_HEIGHT); + + g_settings_set_int (gsearch->mate_search_tool_settings, + "default-window-width", + gsearch->window_width); + g_settings_set_int (gsearch->mate_search_tool_settings, + "default-window-height", + gsearch->window_height); + g_settings_set_boolean (gsearch->mate_search_tool_settings, + "default-window-maximized", + gsearch->is_window_maximized); +} + +static void +quit_application (GSearchWindow * gsearch) +{ + GSearchCommandDetails * command_details = gsearch->command_details; + + if (command_details->command_status == RUNNING) { +#ifdef HAVE_GETPGID + pid_t pgid; +#endif + command_details->command_status = MAKE_IT_QUIT; +#ifdef HAVE_GETPGID + pgid = getpgid (command_details->command_pid); + + if ((pgid > 1) && (pgid != getpid ())) { + kill (-(getpgid (command_details->command_pid)), SIGKILL); + } + else { + kill (command_details->command_pid, SIGKILL); + } +#else + kill (command_details->command_pid, SIGKILL); +#endif + wait (NULL); + } + store_window_state_and_geometry (gsearch); + gtk_main_quit (); +} + +void +version_cb (const gchar * option_name, + const gchar * value, + gpointer data, + GError ** error) +{ + g_print ("%s %s\n", g_get_application_name (), VERSION); + exit (0); +} + +void +quit_session_cb (EggSMClient * client, + gpointer data) +{ + quit_application ((GSearchWindow *) data); +} + +void +quit_cb (GtkWidget * widget, + GdkEvent * event, + gpointer data) +{ + quit_application ((GSearchWindow *) data); +} + +void +click_close_cb (GtkWidget * widget, + gpointer data) +{ + quit_application ((GSearchWindow *) data); +} + +void +click_find_cb (GtkWidget * widget, + gpointer data) +{ + GSearchWindow * gsearch = data; + gchar * command; + + if (gsearch->command_details->is_command_timeout_enabled == TRUE) { + return; + } + + if ((gsearch->command_details->command_status == STOPPED) || + (gsearch->command_details->command_status == ABORTED)) { + command = build_search_command (gsearch, TRUE); + if (command != NULL) { + spawn_search_command (gsearch, command); + g_free (command); + } + } +} + +void +click_stop_cb (GtkWidget * widget, + gpointer data) +{ + GSearchWindow * gsearch = data; + + if (gsearch->command_details->command_status == RUNNING) { +#ifdef HAVE_GETPGID + pid_t pgid; +#endif + gtk_widget_set_sensitive (gsearch->stop_button, FALSE); + gsearch->command_details->command_status = MAKE_IT_STOP; +#ifdef HAVE_GETPGID + pgid = getpgid (gsearch->command_details->command_pid); + + if ((pgid > 1) && (pgid != getpid ())) { + kill (-(getpgid (gsearch->command_details->command_pid)), SIGKILL); + } + else { + kill (gsearch->command_details->command_pid, SIGKILL); + } +#else + kill (gsearch->command_details->command_pid, SIGKILL); +#endif + wait (NULL); + } +} + +void +click_help_cb (GtkWidget * widget, + gpointer data) +{ + GtkWidget * window = data; + GError * error = NULL; + + gtk_show_uri (gtk_widget_get_screen (widget), "help:mate-search-tool", + gtk_get_current_event_time (), &error); + if (error) { + GtkWidget * dialog; + + dialog = gtk_message_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _("Could not open help document.")); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + error->message, NULL); + + gtk_window_set_title (GTK_WINDOW (dialog), ""); + gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); + gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 14); + + g_signal_connect (G_OBJECT (dialog), + "response", + G_CALLBACK (gtk_widget_destroy), NULL); + + gtk_widget_show (dialog); + g_error_free (error); + } +} + +void +click_expander_cb (GObject * object, + GParamSpec * param_spec, + gpointer data) +{ + GSearchWindow * gsearch = data; + + if (gtk_expander_get_expanded (GTK_EXPANDER (object)) == TRUE) { + gtk_widget_show (gsearch->available_options_vbox); + gtk_window_set_geometry_hints (GTK_WINDOW (gsearch->window), + GTK_WIDGET (gsearch->window), + &gsearch->window_geometry, + GDK_HINT_MIN_SIZE); + } + else { + GdkGeometry default_geometry = {MINIMUM_WINDOW_WIDTH, MINIMUM_WINDOW_HEIGHT}; + + gtk_widget_hide (gsearch->available_options_vbox); + gtk_window_set_geometry_hints (GTK_WINDOW (gsearch->window), + GTK_WIDGET (gsearch->window), + &default_geometry, + GDK_HINT_MIN_SIZE); + } +} + +void +size_allocate_cb (GtkWidget * widget, + GtkAllocation * allocation, + gpointer data) +{ + GtkWidget * button = data; + + gtk_widget_set_size_request (button, allocation->width, -1); +} + +void +add_constraint_cb (GtkWidget * widget, + gpointer data) +{ + GSearchWindow * gsearch = data; + gint idx; + + idx = gtk_combo_box_get_active (GTK_COMBO_BOX (gsearch->available_options_combo_box)); + add_constraint (gsearch, idx, NULL, FALSE); +} + +void +remove_constraint_cb (GtkWidget * widget, + gpointer data) +{ + GList * list = data; + + GSearchWindow * gsearch = g_list_first (list)->data; + GSearchConstraint * constraint = g_list_last (list)->data; + + gsearch->window_geometry.min_height -= WINDOW_HEIGHT_STEP; + + gtk_window_set_geometry_hints (GTK_WINDOW (gsearch->window), + GTK_WIDGET (gsearch->window), + &gsearch->window_geometry, + GDK_HINT_MIN_SIZE); + + gtk_container_remove (GTK_CONTAINER (gsearch->available_options_vbox), gtk_widget_get_parent (widget)); + + gsearch->available_options_selected_list = + g_list_remove (gsearch->available_options_selected_list, constraint); + + set_constraint_selected_state (gsearch, constraint->constraint_id, FALSE); + set_constraint_gsettings_boolean (constraint->constraint_id, FALSE); + g_slice_free (GSearchConstraint, constraint); + g_list_free (list); +} + +void +constraint_activate_cb (GtkWidget * widget, + gpointer data) +{ + GSearchWindow * gsearch = data; + + if ((gtk_widget_get_visible (gsearch->find_button)) && + (gtk_widget_get_sensitive (gsearch->find_button))) { + click_find_cb (gsearch->find_button, data); + } +} + +void +constraint_update_info_cb (GtkWidget * widget, + gpointer data) +{ + static gchar * string; + GSearchConstraint * opt = data; + + string = (gchar *) gtk_entry_get_text (GTK_ENTRY (widget)); + update_constraint_info (opt, string); +} + +void +name_contains_activate_cb (GtkWidget * widget, + gpointer data) +{ + GSearchWindow * gsearch = data; + + if ((gtk_widget_get_visible (gsearch->find_button)) && + (gtk_widget_get_sensitive (gsearch->find_button))) { + click_find_cb (gsearch->find_button, data); + } +} + +void +look_in_folder_changed_cb (GtkWidget * widget, + gpointer data) +{ + GSearchWindow * gsearch = data; + gchar * value; + + value = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (gsearch->look_in_folder_button)); + + if (value != NULL) { + g_settings_set_string (gsearch->mate_search_tool_settings, "look-in-folder", value); + } + g_free (value); +} + + +static gint +display_dialog_file_open_limit (GtkWidget * window, + gint count) +{ + GtkWidget * dialog; + GtkWidget * button; + gchar * primary; + gchar * secondary; + gint response; + + primary = g_strdup_printf (ngettext ("Are you sure you want to open %d document?", + "Are you sure you want to open %d documents?", + count), + count); + + secondary = g_strdup_printf (ngettext ("This will open %d separate window.", + "This will open %d separate windows.", + count), + count); + + dialog = gtk_message_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_CANCEL, + primary, NULL); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + secondary, NULL); + + gtk_window_set_title (GTK_WINDOW (dialog), ""); + gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); + gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 14); + + button = gtk_button_new_from_stock ("gtk-open"); + gtk_widget_set_can_default (button, TRUE); + gtk_widget_show (button); + + gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button, GTK_RESPONSE_OK); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); + + response = gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + g_free (primary); + g_free (secondary); + + return response; +} + +static void +display_dialog_could_not_open_file (GtkWidget * window, + const gchar * file, + const gchar * message) +{ + GtkWidget * dialog; + gchar * primary; + + primary = g_strdup_printf (_("Could not open document \"%s\"."), file); + + dialog = gtk_message_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + primary, NULL); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + message, NULL); + + gtk_window_set_title (GTK_WINDOW (dialog), ""); + gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); + gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 14); + + g_signal_connect (G_OBJECT (dialog), + "response", + G_CALLBACK (gtk_widget_destroy), NULL); + + gtk_widget_show (dialog); + g_free (primary); +} + +static void +display_dialog_could_not_open_folder (GtkWidget * window, + const gchar * folder) +{ + GtkWidget * dialog; + gchar * primary; + + primary = g_strdup_printf (_("Could not open folder \"%s\"."), folder); + + dialog = gtk_message_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + primary, NULL); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + _("The caja file manager is not running.")); + + gtk_window_set_title (GTK_WINDOW (dialog), ""); + gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); + gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 14); + + g_signal_connect (G_OBJECT (dialog), + "response", + G_CALLBACK (gtk_widget_destroy), NULL); + + gtk_widget_show (dialog); + g_free (primary); +} + +void +open_file_event_cb (GtkWidget * widget, + GdkEventButton * event, + gpointer data) +{ + open_file_cb ((GtkMenuItem *) widget, data); +} + +void +open_file_cb (GtkMenuItem * action, + gpointer data) +{ + GSearchWindow * gsearch = data; + GtkTreeModel * model; + GList * list; + guint idx; + + if (gtk_tree_selection_count_selected_rows (GTK_TREE_SELECTION (gsearch->search_results_selection)) == 0) { + return; + } + + list = gtk_tree_selection_get_selected_rows (GTK_TREE_SELECTION (gsearch->search_results_selection), + &model); + + if (g_list_length (list) > SILENT_WINDOW_OPEN_LIMIT) { + gint response; + + response = display_dialog_file_open_limit (gsearch->window, g_list_length (list)); + + if (response == GTK_RESPONSE_CANCEL) { + g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL); + g_list_free (list); + return; + } + } + + for (idx = 0; idx < g_list_length (list); idx++) { + + gboolean no_files_found = FALSE; + gchar * utf8_name; + gchar * locale_file; + GtkTreeIter iter; + + gtk_tree_model_get_iter (GTK_TREE_MODEL (gsearch->search_results_list_store), &iter, + g_list_nth_data (list, idx)); + + gtk_tree_model_get (GTK_TREE_MODEL (gsearch->search_results_list_store), &iter, + COLUMN_NAME, &utf8_name, + COLUMN_LOCALE_FILE, &locale_file, + COLUMN_NO_FILES_FOUND, &no_files_found, + -1); + + if (!no_files_found) { + GAppInfo * app = NULL; + + if (GTK_IS_MENU_ITEM (action)) { + app = g_object_get_data (G_OBJECT (action), "app"); + } + + if (!g_file_test (locale_file, G_FILE_TEST_EXISTS)) { + gtk_tree_selection_unselect_iter (GTK_TREE_SELECTION (gsearch->search_results_selection), + &iter); + display_dialog_could_not_open_file (gsearch->window, utf8_name, + _("The document does not exist.")); + + } + else if (open_file_with_application (gsearch->window, locale_file, app) == FALSE) { + + if (launch_file (locale_file) == FALSE) { + + if (g_file_test (locale_file, G_FILE_TEST_IS_DIR)) { + + if (open_file_with_filemanager (gsearch->window, locale_file) == FALSE) { + display_dialog_could_not_open_folder (gsearch->window, utf8_name); + } + } + else { + display_dialog_could_not_open_file (gsearch->window, utf8_name, + _("There is no installed viewer capable " + "of displaying the document.")); + } + } + } + } + g_free (utf8_name); + g_free (locale_file); + } + g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL); + g_list_free (list); +} + +static gint +display_dialog_folder_open_limit (GtkWidget * window, + gint count) +{ + GtkWidget * dialog; + GtkWidget * button; + gchar * primary; + gchar * secondary; + gint response; + + primary = g_strdup_printf (ngettext ("Are you sure you want to open %d folder?", + "Are you sure you want to open %d folders?", + count), + count); + + secondary = g_strdup_printf (ngettext ("This will open %d separate window.", + "This will open %d separate windows.", + count), + count); + + dialog = gtk_message_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_CANCEL, + primary, NULL); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + secondary, NULL); + + gtk_window_set_title (GTK_WINDOW (dialog), ""); + gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); + gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 14); + + button = gtk_button_new_from_stock ("gtk-open"); + gtk_widget_set_can_default (button, TRUE); + gtk_widget_show (button); + + gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button, GTK_RESPONSE_OK); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); + + response = gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + g_free (primary); + g_free (secondary); + + return response; +} + +void +open_folder_cb (GtkAction * action, + gpointer data) +{ + GSearchWindow * gsearch = data; + GtkTreeModel * model; + GFile * g_file = NULL; + GFileInfo * g_file_info = NULL; + GAppInfo * g_app_info = NULL; + GList * list; + guint idx; + + if (gtk_tree_selection_count_selected_rows (GTK_TREE_SELECTION (gsearch->search_results_selection)) == 0) { + return; + } + + list = gtk_tree_selection_get_selected_rows (GTK_TREE_SELECTION (gsearch->search_results_selection), + &model); + + if (g_list_length (list) > SILENT_WINDOW_OPEN_LIMIT) { + gint response; + + response = display_dialog_folder_open_limit (gsearch->window, g_list_length (list)); + + if (response == GTK_RESPONSE_CANCEL) { + g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL); + g_list_free (list); + return; + } + } + + for (idx = 0; idx < g_list_length (list); idx++) { + + gchar * locale_folder; + gchar * utf8_folder; + gchar * locale_file; + GtkTreeIter iter; + + gtk_tree_model_get_iter (GTK_TREE_MODEL (gsearch->search_results_list_store), &iter, + g_list_nth_data (list, idx)); + + gtk_tree_model_get (GTK_TREE_MODEL (gsearch->search_results_list_store), &iter, + COLUMN_RELATIVE_PATH, &utf8_folder, + COLUMN_LOCALE_FILE, &locale_file, + -1); + + locale_folder = g_path_get_dirname (locale_file); + + if (idx == 0) { + g_file = g_file_new_for_path (locale_folder); + g_file_info = g_file_query_info (g_file, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, G_FILE_QUERY_INFO_NONE, NULL, NULL); + g_app_info = g_app_info_get_default_for_type (g_file_info_get_content_type (g_file_info), FALSE); + } + + if (open_file_with_application (gsearch->window, locale_folder, g_app_info) == FALSE) { + + if (open_file_with_filemanager (gsearch->window, locale_folder) == FALSE) { + + display_dialog_could_not_open_folder (gsearch->window, utf8_folder); + + g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL); + g_list_free (list); + g_free (locale_folder); + g_free (utf8_folder); + g_object_unref (g_file); + g_object_unref (g_file_info); + g_object_unref (g_app_info); + return; + } + } + g_free (locale_folder); + g_free (locale_file); + g_free (utf8_folder); + } + g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL); + g_list_free (list); + g_object_unref (g_file); + g_object_unref (g_file_info); + g_object_unref (g_app_info); +} + +void +file_changed_cb (GFileMonitor * handle, + const gchar * monitor_uri, + const gchar * info_uri, + GFileMonitorEvent event_type, + gpointer data) +{ + GSearchMonitor * monitor = data; + GSearchWindow * gsearch = monitor->gsearch; + GtkTreeModel * model; + GtkTreePath * path; + GtkTreeIter iter; + + switch (event_type) { + case G_FILE_MONITOR_EVENT_DELETED: + path = gtk_tree_row_reference_get_path (monitor->reference); + model = gtk_tree_row_reference_get_model (monitor->reference); + gtk_tree_model_get_iter (model, &iter, path); + tree_model_iter_free_monitor (model, NULL, &iter, NULL); + gtk_list_store_remove (GTK_LIST_STORE (model), &iter); + update_search_counts (gsearch); + break; + default: + break; + } +} + +static void +display_dialog_could_not_move_to_trash (GtkWidget * window, + const gchar * file, + const gchar * message) +{ + GtkWidget * dialog; + gchar * primary; + + primary = g_strdup_printf (_("Could not move \"%s\" to trash."), file); + + dialog = gtk_message_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + primary, NULL); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + message, NULL); + + gtk_window_set_title (GTK_WINDOW (dialog), ""); + gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); + gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 14); + + g_signal_connect (G_OBJECT (dialog), + "response", + G_CALLBACK (gtk_widget_destroy), NULL); + gtk_widget_show (dialog); + g_free (primary); +} + +static gint +display_dialog_delete_permanently (GtkWidget * window, + const gchar * file) +{ + GtkWidget * dialog; + GtkWidget * button; + gchar * primary; + gchar * secondary; + gint response; + + primary = g_strdup_printf (_("Do you want to delete \"%s\" permanently?"), + g_path_get_basename (file)); + + secondary = g_strdup_printf (_("Trash is unavailable. Could not move \"%s\" to the trash."), + file); + + dialog = gtk_message_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_CANCEL, + primary, NULL); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + secondary, NULL); + + gtk_window_set_title (GTK_WINDOW (dialog), ""); + gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); + gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 14); + + button = gtk_button_new_from_stock ("gtk-delete"); + gtk_widget_set_can_default (button, TRUE); + gtk_widget_show (button); + + gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button, GTK_RESPONSE_OK); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); + + response = gtk_dialog_run (GTK_DIALOG (dialog)); + + gtk_widget_destroy (GTK_WIDGET(dialog)); + g_free (primary); + g_free (secondary); + + return response; +} + +static void +display_dialog_could_not_delete (GtkWidget * window, + const gchar * file, + const gchar * message) +{ + GtkWidget * dialog; + gchar * primary; + + primary = g_strdup_printf (_("Could not delete \"%s\"."), file); + + dialog = gtk_message_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + primary, NULL); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + message, NULL); + + gtk_window_set_title (GTK_WINDOW (dialog), ""); + gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); + gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 14); + + g_signal_connect (G_OBJECT (dialog), + "response", + G_CALLBACK (gtk_widget_destroy), NULL); + gtk_widget_show (dialog); + g_free (primary); +} + +void +move_to_trash_cb (GtkAction * action, + gpointer data) +{ + GSearchWindow * gsearch = data; + GtkTreePath * last_selected_path = NULL; + gint total; + gint idx; + + if (gtk_tree_selection_count_selected_rows (GTK_TREE_SELECTION (gsearch->search_results_selection)) == 0) { + return; + } + + total = gtk_tree_selection_count_selected_rows (GTK_TREE_SELECTION (gsearch->search_results_selection)); + + for (idx = 0; idx < total; idx++) { + gboolean no_files_found = FALSE; + GtkTreeModel * model; + GtkTreeIter iter; + GList * list; + GFile * g_file; + GError * error = NULL; + gchar * utf8_basename; + gchar * utf8_filename; + gchar * locale_filename; + gboolean result; + + list = gtk_tree_selection_get_selected_rows (GTK_TREE_SELECTION (gsearch->search_results_selection), + &model); + + gtk_tree_model_get_iter (GTK_TREE_MODEL (gsearch->search_results_list_store), &iter, + g_list_nth_data (list, 0)); + + gtk_tree_model_get (GTK_TREE_MODEL (gsearch->search_results_list_store), &iter, + COLUMN_NAME, &utf8_basename, + COLUMN_LOCALE_FILE, &locale_filename, + COLUMN_NO_FILES_FOUND, &no_files_found, + -1); + + if (no_files_found) { + g_free (utf8_basename); + g_free (locale_filename); + return; + } + + utf8_filename = g_filename_display_name (locale_filename); + + if (idx + 1 == total) { + last_selected_path = gtk_tree_model_get_path (GTK_TREE_MODEL (gsearch->search_results_list_store), &iter); + } + + if ((!g_file_test (locale_filename, G_FILE_TEST_EXISTS)) && + (!g_file_test (locale_filename, G_FILE_TEST_IS_SYMLINK))) { + gtk_tree_selection_unselect_iter (GTK_TREE_SELECTION (gsearch->search_results_selection), &iter); + display_dialog_could_not_move_to_trash (gsearch->window, utf8_basename, + _("The document does not exist.")); + } + + g_file = g_file_new_for_path (locale_filename); + result = g_file_trash (g_file, NULL, &error); + + gtk_tree_selection_unselect_iter (GTK_TREE_SELECTION (gsearch->search_results_selection), &iter); + g_object_unref (g_file); + + if (result == TRUE) { + tree_model_iter_free_monitor (GTK_TREE_MODEL (gsearch->search_results_list_store), + NULL, &iter, NULL); + gtk_list_store_remove (GTK_LIST_STORE (gsearch->search_results_list_store), &iter); + } + else { + gint response; + + gtk_tree_selection_unselect_iter (GTK_TREE_SELECTION (gsearch->search_results_selection), &iter); + response = display_dialog_delete_permanently (gsearch->window, utf8_filename); + + if (response == GTK_RESPONSE_OK) { + GFile * g_file_tmp; + GError * error_tmp = NULL; + + g_file_tmp = g_file_new_for_path (locale_filename); + result = g_file_delete (g_file_tmp, NULL, &error_tmp); + g_object_unref (g_file_tmp); + + if (result == TRUE) { + tree_model_iter_free_monitor (GTK_TREE_MODEL (gsearch->search_results_list_store), + NULL, &iter, NULL); + gtk_list_store_remove (GTK_LIST_STORE (gsearch->search_results_list_store), &iter); + } + else { + gchar * message; + + message = g_strdup_printf (_("Deleting \"%s\" failed: %s."), + utf8_filename, error_tmp->message); + + display_dialog_could_not_delete (gsearch->window, utf8_basename, message); + + g_error_free (error_tmp); + g_free (message); + } + } + else { + gchar * message; + + message = g_strdup_printf (_("Moving \"%s\" failed: %s."), + utf8_filename, + error->message); + display_dialog_could_not_move_to_trash (gsearch->window, utf8_basename, + message); + g_error_free (error); + g_free (message); + } + } + g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL); + g_list_free (list); + g_free (locale_filename); + g_free (utf8_filename); + g_free (utf8_basename); + } + + /* Bugzilla #397945: Select next row in the search results list */ + if (last_selected_path != NULL) { + if (gtk_tree_selection_count_selected_rows (GTK_TREE_SELECTION (gsearch->search_results_selection)) == 0) { + gtk_tree_selection_select_path (GTK_TREE_SELECTION (gsearch->search_results_selection), + last_selected_path); + if (gtk_tree_selection_count_selected_rows (GTK_TREE_SELECTION (gsearch->search_results_selection)) == 0) { + gtk_tree_path_prev (last_selected_path); + gtk_tree_selection_select_path (GTK_TREE_SELECTION (gsearch->search_results_selection), + last_selected_path); + } + } + gtk_tree_path_free (last_selected_path); + } + + if (gsearch->command_details->command_status != RUNNING) { + update_search_counts (gsearch); + } +} + +gboolean +file_button_press_event_cb (GtkWidget * widget, + GdkEventButton * event, + gpointer data) +{ + GtkTreeView * tree = data; + GtkTreePath * path; + + row_selected_by_button_press_event = TRUE; + + if (event->window != gtk_tree_view_get_bin_window (tree)) { + return FALSE; + } + + if (gtk_tree_view_get_path_at_pos (tree, event->x, event->y, + &path, NULL, NULL, NULL)) { + + if ((event->button == 1 || event->button == 2 || event->button == 3) + && gtk_tree_selection_path_is_selected (gtk_tree_view_get_selection (tree), path)) { + row_selected_by_button_press_event = FALSE; + } + gtk_tree_path_free (path); + } + else { + gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (tree)); + } + + return !(row_selected_by_button_press_event); +} + +gboolean +file_key_press_event_cb (GtkWidget * widget, + GdkEventKey * event, + gpointer data) +{ + if (event->keyval == GDK_KEY_space || + event->keyval == GDK_KEY_Return || + event->keyval == GDK_KEY_KP_Enter) { + if (event->state != GDK_CONTROL_MASK) { + open_file_cb ((GtkMenuItem *) NULL, data); + return TRUE; + } + } + else if (event->keyval == GDK_KEY_Delete) { + move_to_trash_cb ((GtkAction *) NULL, data); + return TRUE; + } + return FALSE; +} + +static gint +open_with_list_sort (gconstpointer a, + gconstpointer b) +{ + const gchar * a_app_name = g_app_info_get_name ((GAppInfo *) a); + const gchar * b_app_name = g_app_info_get_name ((GAppInfo *) b); + gchar * a_utf8; + gchar * b_utf8; + gint result; + + a_utf8 = g_utf8_casefold (a_app_name, -1); + b_utf8 = g_utf8_casefold (b_app_name, -1); + + result = g_utf8_collate (a_utf8, b_utf8); + + g_free (a_utf8); + g_free (b_utf8); + + return result; +} + +static void +build_popup_menu_for_file (GSearchWindow * gsearch, + gchar * file) +{ + GtkWidget * new1, * image1, * separatormenuitem1; + GtkWidget * new2; + gint i; + + if (GTK_IS_MENU (gsearch->search_results_popup_menu) == TRUE) { + g_object_ref_sink (gsearch->search_results_popup_menu); + g_object_unref (gsearch->search_results_popup_menu); + } + + if (GTK_IS_MENU (gsearch->search_results_popup_submenu) == TRUE) { + g_object_ref_sink (gsearch->search_results_popup_submenu); + g_object_unref (gsearch->search_results_popup_submenu); + } + + gsearch->search_results_popup_menu = gtk_menu_new (); + + if (file == NULL || g_file_test (file, G_FILE_TEST_IS_DIR) == TRUE) { + /* Popup menu item: Open */ + new1 = gtk_image_menu_item_new_with_mnemonic (_("_Open")); + gtk_container_add (GTK_CONTAINER (gsearch->search_results_popup_menu), new1); + gtk_widget_show (new1); + + image1 = gtk_image_new_from_stock ("gtk-open", GTK_ICON_SIZE_MENU); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (new1), image1); + gtk_widget_show (image1); + + g_signal_connect (G_OBJECT (new1), + "activate", + G_CALLBACK (open_file_cb), + (gpointer) gsearch); + } + else { + GFile * g_file; + GFileInfo * file_info; + GIcon * file_icon; + GList * list; + gchar * str; + gint list_length; + + g_file = g_file_new_for_path (file); + file_info = g_file_query_info (g_file, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, G_FILE_QUERY_INFO_NONE, NULL, NULL); + list = g_app_info_get_all_for_type (g_file_info_get_content_type (file_info)); + + list_length = g_list_length (list); + + if (list_length <= 0) { + + /* Popup menu item: Open */ + new1 = gtk_image_menu_item_new_with_mnemonic (_("_Open")); + gtk_container_add (GTK_CONTAINER (gsearch->search_results_popup_menu), new1); + gtk_widget_show (new1); + + image1 = gtk_image_new_from_stock ("gtk-open", GTK_ICON_SIZE_MENU); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (new1), image1); + gtk_widget_show (image1); + + g_signal_connect (G_OBJECT (new1), + "activate", + G_CALLBACK (open_file_cb), + (gpointer) gsearch); + } + else { + if (list_length >= 3) { /* Sort all except first application by name */ + GList * tmp; + + tmp = g_list_first (list); + list = g_list_remove_link (list, tmp); + list = g_list_sort (list, open_with_list_sort); + list = g_list_prepend (list, tmp->data); + g_list_free (tmp); + } + + /* Popup menu item: Open with (default) */ + str = g_strdup_printf (_("_Open with %s"), g_app_info_get_name (list->data)); + new1 = gtk_image_menu_item_new_with_mnemonic (str); + g_free (str); + gtk_widget_show (new1); + + g_object_set_data_full (G_OBJECT (new1), "app", (GAppInfo *)list->data, + (GDestroyNotify) g_object_unref); + + gtk_container_add (GTK_CONTAINER (gsearch->search_results_popup_menu), new1); + g_signal_connect ((gpointer) new1, "activate", G_CALLBACK (open_file_cb), + (gpointer) gsearch); + + if (g_app_info_get_icon ((GAppInfo *)list->data) != NULL) { + file_icon = g_object_ref (g_app_info_get_icon ((GAppInfo *)list->data)); + gtk_image_menu_item_set_always_show_image (GTK_IMAGE_MENU_ITEM (new1), file_icon != NULL); + + if (file_icon == NULL) { + file_icon = g_themed_icon_new (GTK_STOCK_OPEN); + } + + image1 = gtk_image_new_from_gicon (file_icon, GTK_ICON_SIZE_MENU); + g_object_unref (file_icon); + gtk_widget_show (image1); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (new1), image1); + } + + separatormenuitem1 = gtk_separator_menu_item_new (); + gtk_widget_show (separatormenuitem1); + gtk_container_add (GTK_CONTAINER (gsearch->search_results_popup_menu), separatormenuitem1); + gtk_widget_set_sensitive (separatormenuitem1, FALSE); + + for (list = g_list_next (list), i = 0; list != NULL; list = g_list_next (list), i++) { + + /* Popup menu item: Open with (others) */ + if (list_length < 4) { + str = g_strdup_printf (_("Open with %s"), g_app_info_get_name (list->data)); + } + else { + str = g_strdup_printf ("%s", g_app_info_get_name (list->data)); + } + + new1 = gtk_image_menu_item_new_with_mnemonic (str); + g_free (str); + gtk_widget_show (new1); + + g_object_set_data_full (G_OBJECT (new1), "app", (GAppInfo *)list->data, + (GDestroyNotify) g_object_unref); + + if (list_length >= 4) { + + if (g_app_info_get_icon ((GAppInfo *)list->data) != NULL) { + file_icon = g_object_ref (g_app_info_get_icon ((GAppInfo *)list->data)); + gtk_image_menu_item_set_always_show_image (GTK_IMAGE_MENU_ITEM (new1), file_icon != NULL); + + if (file_icon == NULL) { + file_icon = g_themed_icon_new (GTK_STOCK_OPEN); + } + + image1 = gtk_image_new_from_gicon (file_icon, GTK_ICON_SIZE_MENU); + g_object_unref (file_icon); + gtk_widget_show (image1); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (new1), image1); + } + + if (i == 0) { + gsearch->search_results_popup_submenu = gtk_menu_new (); + + /* Popup menu item: Open With */ + new2 = gtk_menu_item_new_with_mnemonic (_("Open Wit_h")); + gtk_widget_show (new2); + gtk_container_add (GTK_CONTAINER (gsearch->search_results_popup_menu), new2); + + gtk_menu_item_set_submenu (GTK_MENU_ITEM (new2), gsearch->search_results_popup_submenu); + } + gtk_container_add (GTK_CONTAINER (gsearch->search_results_popup_submenu), new1); + + /* For submenu items, the "activate" signal is only emitted if the user first clicks + on the parent menu item. Since submenus in gtk+ are automatically displayed when + the user hovers over them, most will never click on the parent menu item. + The work-around is to connect to "button-press-event". */ + g_signal_connect (G_OBJECT(new1), "button-press-event", G_CALLBACK (open_file_event_cb), + (gpointer) gsearch); + } + else { + if (g_app_info_get_icon ((GAppInfo *)list->data) != NULL) { + + file_icon = g_object_ref (g_app_info_get_icon ((GAppInfo *)list->data)); + gtk_image_menu_item_set_always_show_image (GTK_IMAGE_MENU_ITEM (new1), file_icon != NULL); + + if (file_icon == NULL) { + file_icon = g_themed_icon_new (GTK_STOCK_OPEN); + } + + image1 = gtk_image_new_from_gicon (file_icon, GTK_ICON_SIZE_MENU); + g_object_unref (file_icon); + gtk_widget_show (image1); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (new1), image1); + } + gtk_container_add (GTK_CONTAINER (gsearch->search_results_popup_menu), new1); + g_signal_connect ((gpointer) new1, "activate", G_CALLBACK (open_file_cb), + (gpointer) gsearch); + } + } + + if (list_length >= 2) { + separatormenuitem1 = gtk_separator_menu_item_new (); + gtk_container_add (GTK_CONTAINER (gsearch->search_results_popup_menu), separatormenuitem1); + gtk_widget_show (separatormenuitem1); + } + } + } + + /* Popup menu item: Open Containing Folder */ + new1 = gtk_image_menu_item_new_with_mnemonic (_("Open Containing _Folder")); + gtk_container_add (GTK_CONTAINER (gsearch->search_results_popup_menu), new1); + gtk_widget_show (new1); + + image1 = gtk_image_new_from_stock ("gtk-open", GTK_ICON_SIZE_MENU); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (new1), image1); + gtk_widget_show (image1); + + g_signal_connect (G_OBJECT (new1), + "activate", + G_CALLBACK (open_folder_cb), + (gpointer) gsearch); + + /* Popup menu item: Move to Trash */ + separatormenuitem1 = gtk_separator_menu_item_new (); + gtk_container_add (GTK_CONTAINER (gsearch->search_results_popup_menu), separatormenuitem1); + gtk_widget_show (separatormenuitem1); + + new1 = gtk_image_menu_item_new_with_mnemonic (_("Mo_ve to Trash")); + gtk_container_add (GTK_CONTAINER (gsearch->search_results_popup_menu), new1); + gtk_widget_show (new1); + + GtkIconTheme *icon_theme; + GdkPixbuf *pixbuf; + icon_theme = gtk_icon_theme_get_default (); + pixbuf = gtk_icon_theme_load_icon (icon_theme, "user-trash", GTK_ICON_SIZE_MENU, 0, NULL); + image1 = gtk_image_new_from_pixbuf (pixbuf); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (new1), image1); + gtk_widget_show (image1); + + g_signal_connect (G_OBJECT (new1), + "activate", + G_CALLBACK (move_to_trash_cb), + (gpointer) gsearch); + + /* Popup menu item: Save Results As... */ + separatormenuitem1 = gtk_separator_menu_item_new (); + gtk_container_add (GTK_CONTAINER (gsearch->search_results_popup_menu), separatormenuitem1); + gtk_widget_show (separatormenuitem1); + + gsearch->search_results_save_results_as_item = gtk_image_menu_item_new_with_mnemonic (_("_Save Results As...")); + gtk_container_add (GTK_CONTAINER (gsearch->search_results_popup_menu), gsearch->search_results_save_results_as_item); + gtk_widget_show (gsearch->search_results_save_results_as_item); + + if (gsearch->command_details->command_status == RUNNING) { + gtk_widget_set_sensitive (gsearch->search_results_save_results_as_item, FALSE); + } + + image1 = gtk_image_new_from_stock ("gtk-save", GTK_ICON_SIZE_MENU); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (gsearch->search_results_save_results_as_item), image1); + gtk_widget_show (image1); + + g_signal_connect (G_OBJECT (gsearch->search_results_save_results_as_item), + "activate", + G_CALLBACK (show_file_selector_cb), + (gpointer) gsearch); +} + +gboolean +file_button_release_event_cb (GtkWidget * widget, + GdkEventButton * event, + gpointer data) +{ + GSearchWindow * gsearch = data; + + if (event->window != gtk_tree_view_get_bin_window (GTK_TREE_VIEW (gsearch->search_results_tree_view))) { + return FALSE; + } + + if (event->button == 1 || event->button == 2) { + GtkTreePath *path; + + if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (gsearch->search_results_tree_view), event->x, event->y, + &path, NULL, NULL, NULL)) { + if ((event->state & GDK_SHIFT_MASK) || (event->state & GDK_CONTROL_MASK)) { + if (row_selected_by_button_press_event) { + gtk_tree_selection_select_path (gtk_tree_view_get_selection (GTK_TREE_VIEW(gsearch->search_results_tree_view)), path); + } + else { + gtk_tree_selection_unselect_path (gtk_tree_view_get_selection (GTK_TREE_VIEW(gsearch->search_results_tree_view)), path); + } + } + else { + if (gsearch->is_search_results_single_click_to_activate == FALSE) { + gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW(gsearch->search_results_tree_view))); + } + gtk_tree_selection_select_path (gtk_tree_view_get_selection (GTK_TREE_VIEW(gsearch->search_results_tree_view)), path); + } + } + gtk_tree_path_free (path); + } + + if (gtk_tree_selection_count_selected_rows (GTK_TREE_SELECTION (gsearch->search_results_selection)) == 0) { + return FALSE; + } + + if (event->button == 3) { + gboolean no_files_found = FALSE; + GtkTreeModel * model; + GtkTreeIter iter; + GList * list; + gchar * utf8_name_first; + gchar * locale_file_first; + + list = gtk_tree_selection_get_selected_rows (GTK_TREE_SELECTION (gsearch->search_results_selection), + &model); + + gtk_tree_model_get_iter (GTK_TREE_MODEL (gsearch->search_results_list_store), &iter, + g_list_first (list)->data); + + gtk_tree_model_get (GTK_TREE_MODEL (gsearch->search_results_list_store), &iter, + COLUMN_NAME, &utf8_name_first, + COLUMN_LOCALE_FILE, &locale_file_first, + COLUMN_NO_FILES_FOUND, &no_files_found, + -1); + + if (!no_files_found) { + + gboolean show_app_list = TRUE; + GAppInfo * first_app_info = NULL; + GTimer * timer; + GList * tmp; + gchar * locale_file_tmp; + gchar * file = NULL; + gint idx; + + timer = g_timer_new (); + g_timer_start (timer); + + if (g_list_length (list) >= 2) { + + /* Verify the selected files each have the same default handler. */ + for (tmp = g_list_first (list), idx = 0; tmp != NULL; tmp = g_list_next (tmp), idx++) { + + GFile * g_file; + GAppInfo * app_info; + + gtk_tree_model_get_iter (GTK_TREE_MODEL (gsearch->search_results_list_store), &iter, + tmp->data); + + gtk_tree_model_get (GTK_TREE_MODEL (gsearch->search_results_list_store), &iter, + COLUMN_LOCALE_FILE, &locale_file_tmp, + -1); + + g_file = g_file_new_for_path (locale_file_tmp); + app_info = g_file_query_default_handler (g_file, NULL, NULL); + + if (G_IS_APP_INFO (app_info) == FALSE) { + show_app_list = FALSE; + } + else { + if (idx == 0) { + first_app_info = g_app_info_dup (app_info); + g_object_unref (app_info); + continue; + } + + show_app_list = g_app_info_equal (app_info, first_app_info); + g_object_unref (app_info); + + /* Break out, if more that 1.5 seconds have passed */ + if (g_timer_elapsed (timer, NULL) > 1.50) { + show_app_list = FALSE; + } + } + g_object_unref (g_file); + g_free (locale_file_tmp); + + if (show_app_list == FALSE) { + break; + } + } + g_timer_destroy (timer); + if (first_app_info != NULL) { + g_object_unref (first_app_info); + } + } + + file = g_strdup (((show_app_list == TRUE) ? locale_file_first : NULL)); + + build_popup_menu_for_file (gsearch, file); + gtk_menu_popup (GTK_MENU (gsearch->search_results_popup_menu), NULL, NULL, NULL, NULL, + event->button, event->time); + g_free (file); + + } + g_free (locale_file_first); + g_free (utf8_name_first); + g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL); + g_list_free (list); + } + else if (event->button == 1 || event->button == 2) { + if (gsearch->is_search_results_single_click_to_activate == TRUE) { + if (!(event->state & GDK_CONTROL_MASK) && !(event->state & GDK_SHIFT_MASK)) { + open_file_cb ((GtkMenuItem *) NULL, data); + } + } + } + return FALSE; +} + +gboolean +file_event_after_cb (GtkWidget * widget, + GdkEventButton * event, + gpointer data) +{ + GSearchWindow * gsearch = data; + + if (event->window != gtk_tree_view_get_bin_window (GTK_TREE_VIEW (gsearch->search_results_tree_view))) { + return FALSE; + } + + if (gtk_tree_selection_count_selected_rows (GTK_TREE_SELECTION (gsearch->search_results_selection)) == 0) { + return FALSE; + } + + if (!(event->state & GDK_CONTROL_MASK) && !(event->state & GDK_SHIFT_MASK)) { + if (gsearch->is_search_results_single_click_to_activate == FALSE) { + if (event->type == GDK_2BUTTON_PRESS) { + open_file_cb ((GtkMenuItem *) NULL, data); + return TRUE; + } + } + } + return FALSE; +} + +gboolean +file_motion_notify_cb (GtkWidget *widget, + GdkEventMotion *event, + gpointer user_data) +{ + GSearchWindow * gsearch = user_data; + GdkCursor * cursor; + GtkTreePath * last_hover_path; + GtkTreeIter iter; + + if (gsearch->is_search_results_single_click_to_activate == FALSE) { + return FALSE; + } + + if (event->window != gtk_tree_view_get_bin_window (GTK_TREE_VIEW (gsearch->search_results_tree_view))) { + return FALSE; + } + + last_hover_path = gsearch->search_results_hover_path; + + gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget), + event->x, event->y, + &gsearch->search_results_hover_path, + NULL, NULL, NULL); + + if (gsearch->search_results_hover_path != NULL) { + cursor = gdk_cursor_new (GDK_HAND2); + } + else { + cursor = NULL; + } + + gdk_window_set_cursor (event->window, cursor); + + /* Redraw if the hover row has changed */ + if (!(last_hover_path == NULL && gsearch->search_results_hover_path == NULL) && + (!(last_hover_path != NULL && gsearch->search_results_hover_path != NULL) || + gtk_tree_path_compare (last_hover_path, gsearch->search_results_hover_path))) { + if (last_hover_path) { + gtk_tree_model_get_iter (GTK_TREE_MODEL (gsearch->search_results_list_store), + &iter, last_hover_path); + gtk_tree_model_row_changed (GTK_TREE_MODEL (gsearch->search_results_list_store), + last_hover_path, &iter); + } + + if (gsearch->search_results_hover_path) { + gtk_tree_model_get_iter (GTK_TREE_MODEL (gsearch->search_results_list_store), + &iter, gsearch->search_results_hover_path); + gtk_tree_model_row_changed (GTK_TREE_MODEL (gsearch->search_results_list_store), + gsearch->search_results_hover_path, &iter); + } + } + + gtk_tree_path_free (last_hover_path); + + return FALSE; +} + +gboolean +file_leave_notify_cb (GtkWidget *widget, + GdkEventCrossing *event, + gpointer user_data) +{ + GSearchWindow * gsearch = user_data; + GtkTreeIter iter; + + if (gsearch->is_search_results_single_click_to_activate && (gsearch->search_results_hover_path != NULL)) { + gtk_tree_model_get_iter (GTK_TREE_MODEL (gsearch->search_results_list_store), + &iter, + gsearch->search_results_hover_path); + gtk_tree_model_row_changed (GTK_TREE_MODEL (gsearch->search_results_list_store), + gsearch->search_results_hover_path, + &iter); + + gtk_tree_path_free (gsearch->search_results_hover_path); + gsearch->search_results_hover_path = NULL; + + return TRUE; + } + + return FALSE; +} + +void +drag_begin_file_cb (GtkWidget * widget, + GdkDragContext * context, + gpointer data) +{ + GSearchWindow * gsearch = data; + gint number_of_selected_rows; + + number_of_selected_rows = gtk_tree_selection_count_selected_rows (GTK_TREE_SELECTION (gsearch->search_results_selection)); + + if (number_of_selected_rows > 1) { + gtk_drag_set_icon_stock (context, GTK_STOCK_DND_MULTIPLE, 0, 0); + } + else if (number_of_selected_rows == 1) { + GdkPixbuf * pixbuf; + GtkTreeModel * model; + GtkTreeIter iter; + GList * list; + + list = gtk_tree_selection_get_selected_rows (GTK_TREE_SELECTION (gsearch->search_results_selection), + &model); + + gtk_tree_model_get_iter (GTK_TREE_MODEL (gsearch->search_results_list_store), &iter, + g_list_first (list)->data); + + gtk_tree_model_get (GTK_TREE_MODEL (gsearch->search_results_list_store), &iter, + COLUMN_ICON, &pixbuf, + -1); + g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL); + g_list_free (list); + + if (pixbuf) { + gtk_drag_set_icon_pixbuf (context, pixbuf, 0, 0); + } + else { + gtk_drag_set_icon_stock (context, GTK_STOCK_DND, 0, 0); + } + } +} + +void +drag_file_cb (GtkWidget * widget, + GdkDragContext * context, + GtkSelectionData * selection_data, + guint info, + guint drag_time, + gpointer data) +{ + GSearchWindow * gsearch = data; + gchar * uri_list = NULL; + GList * list; + GtkTreeModel * model; + GtkTreeIter iter; + guint idx; + + if (gtk_tree_selection_count_selected_rows (GTK_TREE_SELECTION (gsearch->search_results_selection)) == 0) { + return; + } + + list = gtk_tree_selection_get_selected_rows (GTK_TREE_SELECTION (gsearch->search_results_selection), + &model); + + for (idx = 0; idx < g_list_length (list); idx++) { + + gboolean no_files_found = FALSE; + gchar * utf8_name; + gchar * locale_file; + + gtk_tree_model_get_iter (GTK_TREE_MODEL (gsearch->search_results_list_store), &iter, + g_list_nth_data (list, idx)); + + gtk_tree_model_get (GTK_TREE_MODEL (gsearch->search_results_list_store), &iter, + COLUMN_NAME, &utf8_name, + COLUMN_LOCALE_FILE, &locale_file, + COLUMN_NO_FILES_FOUND, &no_files_found, + -1); + + if (!no_files_found) { + gchar * tmp_uri = g_filename_to_uri (locale_file, NULL, NULL); + + if (uri_list == NULL) { + uri_list = g_strdup (tmp_uri); + } + else { + uri_list = g_strconcat (uri_list, "\n", tmp_uri, NULL); + } + gtk_selection_data_set (selection_data, + gtk_selection_data_get_target (selection_data), + 8, + (guchar *) uri_list, + strlen (uri_list)); + g_free (tmp_uri); + } + else { + gtk_selection_data_set_text (selection_data, utf8_name, -1); + } + g_free (utf8_name); + g_free (locale_file); + } + g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL); + g_list_free (list); + g_free (uri_list); +} + + +void +show_file_selector_cb (GtkAction * action, + gpointer data) +{ + GSearchWindow * gsearch = data; + GtkWidget * file_chooser; + + file_chooser = gtk_file_chooser_dialog_new (_("Save Search Results As..."), + GTK_WINDOW (gsearch->window), + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, GTK_RESPONSE_OK, + NULL); + + gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (file_chooser), TRUE); + if (gsearch->save_results_as_default_filename != NULL) { + gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (file_chooser), + gsearch->save_results_as_default_filename); + } + + g_signal_connect (G_OBJECT (file_chooser), "response", + G_CALLBACK (save_results_cb), gsearch); + + gtk_window_set_modal (GTK_WINDOW (file_chooser), TRUE); + gtk_window_set_position (GTK_WINDOW (file_chooser), GTK_WIN_POS_CENTER_ON_PARENT); + + gtk_widget_show (GTK_WIDGET (file_chooser)); +} + +static void +display_dialog_could_not_save_no_name (GtkWidget * window) +{ + GtkWidget * dialog; + gchar * primary; + gchar * secondary; + + primary = g_strdup (_("Could not save document.")); + secondary = g_strdup (_("You did not select a document name.")); + + dialog = gtk_message_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + primary, NULL); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + secondary, NULL); + + gtk_window_set_title (GTK_WINDOW (dialog), ""); + gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); + gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 14); + + g_signal_connect (G_OBJECT (dialog), + "response", + G_CALLBACK (gtk_widget_destroy), NULL); + gtk_widget_show (dialog); + g_free (primary); + g_free (secondary); +} + +static void +display_dialog_could_not_save_to (GtkWidget * window, + const gchar * file, + const gchar * message) +{ + GtkWidget * dialog; + gchar * primary; + + primary = g_strdup_printf (_("Could not save \"%s\" document to \"%s\"."), + g_path_get_basename (file), + g_path_get_dirname (file)); + + dialog = gtk_message_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + primary, NULL); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + message, NULL); + + gtk_window_set_title (GTK_WINDOW (dialog), ""); + gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); + gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 14); + + g_signal_connect (G_OBJECT (dialog), + "response", + G_CALLBACK (gtk_widget_destroy), NULL); + gtk_widget_show (dialog); + g_free (primary); +} + +static gint +display_dialog_could_not_save_exists (GtkWidget * window, + const gchar * file) +{ + GtkWidget * dialog; + GtkWidget * button; + gchar * primary; + gchar * secondary; + gint response; + + primary = g_strdup_printf (_("The document \"%s\" already exists. " + "Would you like to replace it?"), + g_path_get_basename (file)); + + secondary = g_strdup (_("If you replace an existing file, " + "its contents will be overwritten.")); + + dialog = gtk_message_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_CANCEL, + "%s", primary); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + "%s", secondary); + + gtk_window_set_title (GTK_WINDOW (dialog), ""); + gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); + gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 14); + + button = gsearchtool_button_new_with_stock_icon (_("_Replace"), GTK_STOCK_OK); + gtk_widget_set_can_default (button, TRUE); + gtk_widget_show (button); + + gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button, GTK_RESPONSE_OK); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); + + response = gtk_dialog_run (GTK_DIALOG (dialog)); + + gtk_widget_destroy (GTK_WIDGET(dialog)); + g_free (primary); + g_free (secondary); + + return response; +} + +void +save_results_cb (GtkWidget * chooser, + gint response, + gpointer data) +{ + GSearchWindow * gsearch = data; + GtkListStore * store; + GtkTreeIter iter; + FILE * fp; + gchar * utf8 = NULL; + + if (response != GTK_RESPONSE_OK) { + gtk_widget_destroy (GTK_WIDGET (chooser)); + return; + } + + store = gsearch->search_results_list_store; + g_free (gsearch->save_results_as_default_filename); + + gsearch->save_results_as_default_filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (chooser)); + gtk_widget_destroy (chooser); + + if (gsearch->save_results_as_default_filename != NULL) { + utf8 = g_filename_to_utf8 (gsearch->save_results_as_default_filename, -1, NULL, NULL, NULL); + } + + if (utf8 == NULL) { + display_dialog_could_not_save_no_name (gsearch->window); + return; + } + + if (g_file_test (gsearch->save_results_as_default_filename, G_FILE_TEST_IS_DIR)) { + display_dialog_could_not_save_to (gsearch->window, utf8, + _("The document name you selected is a folder.")); + g_free (utf8); + return; + } + + if (g_file_test (gsearch->save_results_as_default_filename, G_FILE_TEST_EXISTS)) { + + gint response; + + response = display_dialog_could_not_save_exists (gsearch->window, utf8); + + if (response != GTK_RESPONSE_OK) { + g_free (utf8); + return; + } + } + + if ((fp = fopen (gsearch->save_results_as_default_filename, "w")) != NULL) { + + gint idx; + + for (idx = 0; idx < gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), NULL); idx++) + { + if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (store), &iter, NULL, idx) == TRUE) { + + gchar * locale_file; + + gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, COLUMN_LOCALE_FILE, &locale_file, -1); + fprintf (fp, "%s\n", locale_file); + g_free (locale_file); + } + } + fclose (fp); + } + else { + display_dialog_could_not_save_to (gsearch->window, utf8, + _("You may not have write permissions to the document.")); + } + g_free (utf8); +} + +void +save_session_cb (EggSMClient * client, + GKeyFile * state_file, + gpointer client_data) +{ + GSearchWindow * gsearch = client_data; + char ** argv; + int argc; + + set_clone_command (gsearch, &argc, &argv, "mate-search-tool", FALSE); + egg_sm_client_set_restart_command (client, argc, (const char **) argv); +} + +gboolean +key_press_cb (GtkWidget * widget, + GdkEventKey * event, + gpointer data) +{ + GSearchWindow * gsearch = data; + + g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + + if (event->keyval == GDK_KEY_Escape) { + if (gsearch->command_details->command_status == RUNNING) { + click_stop_cb (widget, data); + } + else if (gsearch->command_details->is_command_timeout_enabled == FALSE) { + quit_cb (widget, (GdkEvent *) NULL, data); + } + } + else if (event->keyval == GDK_KEY_F10) { + if (event->state & GDK_SHIFT_MASK) { + gboolean no_files_found = FALSE; + GtkTreeModel * model; + GtkTreeIter iter; + GList * list; + + if (gtk_tree_selection_count_selected_rows (GTK_TREE_SELECTION (gsearch->search_results_selection)) == 0) { + return FALSE; + } + + list = gtk_tree_selection_get_selected_rows (GTK_TREE_SELECTION (gsearch->search_results_selection), + &model); + + gtk_tree_model_get_iter (GTK_TREE_MODEL (gsearch->search_results_list_store), &iter, + g_list_first (list)->data); + + gtk_tree_model_get (GTK_TREE_MODEL (gsearch->search_results_list_store), &iter, + COLUMN_NO_FILES_FOUND, &no_files_found, -1); + + g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL); + g_list_free (list); + + if (!no_files_found) { + gtk_menu_popup (GTK_MENU (gsearch->search_results_popup_menu), NULL, NULL, NULL, NULL, + event->keyval, event->time); + return TRUE; + } + } + } + return FALSE; +} + +gboolean +not_running_timeout_cb (gpointer data) +{ + GSearchWindow * gsearch = data; + + gsearch->command_details->is_command_timeout_enabled = FALSE; + return FALSE; +} + +void +disable_quick_search_cb (GtkWidget * dialog, + gint response, + gpointer data) +{ + GSearchWindow * gsearch = data; + + gtk_widget_destroy (GTK_WIDGET (dialog)); + + if (response == GTK_RESPONSE_OK) { + g_settings_set_boolean (gsearch->mate_search_tool_settings, "disable-quick-search", TRUE); + } +} + +void +single_click_to_activate_key_changed_cb (GSettings * settings, + gchar * key, + gpointer user_data) +{ + GSearchWindow * gsearch = user_data; + gchar * value; + + value = g_settings_get_string (settings, key); + + gsearch->is_search_results_single_click_to_activate = + (strncmp (value, "single", 6) == 0) ? TRUE : FALSE; + + g_free (value); +} + +void +columns_changed_cb (GtkTreeView * treeview, + gpointer user_data) +{ + GVariantBuilder array_builder; + GSearchWindow * gsearch = user_data; + GSList * order; + GSList * iter; + + order = gsearchtool_get_columns_order (treeview); + + g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("ai")); + for (iter = order; iter; iter = iter->next) + g_variant_builder_add (&array_builder, "i", GPOINTER_TO_INT (iter->data)); + + if (g_slist_length (order) == NUM_VISIBLE_COLUMNS) { + g_settings_set_value (gsearch->mate_search_tool_settings, "columns-order", g_variant_new ("ai", &array_builder)); + } + g_slist_free (order); +} + +gboolean +window_state_event_cb (GtkWidget * widget, + GdkEventWindowState * event, + gpointer data) +{ + GSearchWindow * gsearch = data; + + if (event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) { + gsearch->is_window_maximized = TRUE; + } + else { + gsearch->is_window_maximized = FALSE; + } + return FALSE; +} diff --git a/gsearchtool/src/gsearchtool-callbacks.h b/gsearchtool/src/gsearchtool-callbacks.h new file mode 100644 index 00000000..b9db03a2 --- /dev/null +++ b/gsearchtool/src/gsearchtool-callbacks.h @@ -0,0 +1,180 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * MATE Search Tool + * + * File: gsearchtool-callbacks.h + * + * (C) 2002 the Free Software Foundation + * + * Authors: Dennis Cranston + * George Lebl + * + * 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 St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef _GSEARCHTOOL_CALLBACKS_H_ +#define _GSEARCHTOOL_CALLBACKS_H_ + +#ifdef __cplusplus +extern "C" { +#pragma } +#endif + +#include "eggsmclient.h" + +void +version_cb (const gchar * option_name, + const gchar * value, + gpointer data, + GError ** error); +void +quit_session_cb (EggSMClient * client, + gpointer data); +void +quit_cb (GtkWidget * widget, + GdkEvent * event, + gpointer data); +void +click_close_cb (GtkWidget * widget, + gpointer data); +void +click_find_cb (GtkWidget * widget, + gpointer data); +void +click_stop_cb (GtkWidget * widget, + gpointer data); +void +click_help_cb (GtkWidget * widget, + gpointer data); +void +click_expander_cb (GObject * object, + GParamSpec * param_spec, + gpointer data); +void +size_allocate_cb (GtkWidget * widget, + GtkAllocation * allocation, + gpointer data); +void +add_constraint_cb (GtkWidget * widget, + gpointer data); +void +remove_constraint_cb (GtkWidget * widget, + gpointer data); +void +constraint_activate_cb (GtkWidget * widget, + gpointer data); +void +constraint_update_info_cb (GtkWidget * widget, + gpointer data); +void +name_contains_activate_cb (GtkWidget * widget, + gpointer data); +void +look_in_folder_changed_cb (GtkWidget * widget, + gpointer data); +void +open_file_cb (GtkMenuItem * action, + gpointer data); +void +open_file_event_cb (GtkWidget * widget, + GdkEventButton * event, + gpointer data); +void +open_folder_cb (GtkAction * action, + gpointer data); +void +file_changed_cb (GFileMonitor * handle, + const gchar * monitor_uri, + const gchar * info_uri, + GFileMonitorEvent event_type, + gpointer data); +void +move_to_trash_cb (GtkAction * action, + gpointer data); +void +drag_begin_file_cb (GtkWidget * widget, + GdkDragContext * context, + gpointer data); +void +drag_file_cb (GtkWidget * widget, + GdkDragContext * context, + GtkSelectionData * selection_data, + guint info, + guint time, + gpointer data); +void +show_file_selector_cb (GtkAction * action, + gpointer data); +void +save_results_cb (GtkWidget * chooser, + gint response, + gpointer data); +void +save_session_cb (EggSMClient * client, + GKeyFile * state_file, + gpointer client_data); +gboolean +key_press_cb (GtkWidget * widget, + GdkEventKey * event, + gpointer data); +gboolean +file_button_release_event_cb (GtkWidget * widget, + GdkEventButton * event, + gpointer data); +gboolean +file_event_after_cb (GtkWidget * widget, + GdkEventButton * event, + gpointer data); +gboolean +file_button_press_event_cb (GtkWidget * widget, + GdkEventButton * event, + gpointer data); +gboolean +file_key_press_event_cb (GtkWidget * widget, + GdkEventKey * event, + gpointer data); +gboolean +file_motion_notify_cb (GtkWidget *widget, + GdkEventMotion *event, + gpointer user_data); +gboolean +file_leave_notify_cb (GtkWidget *widget, + GdkEventCrossing *event, + gpointer user_data); +gboolean +not_running_timeout_cb (gpointer data); + +void +disable_quick_search_cb (GtkWidget * dialog, + gint response, + gpointer data); +void +single_click_to_activate_key_changed_cb (GSettings * settings, + gchar * key, + gpointer user_data); +void +columns_changed_cb (GtkTreeView * treeview, + gpointer user_data); +gboolean +window_state_event_cb (GtkWidget * widget, + GdkEventWindowState * event, + gpointer data); + +#ifdef __cplusplus +} +#endif + +#endif /* _GSEARCHTOOL_CALLBACKS_H_ */ diff --git a/gsearchtool/src/gsearchtool-support.c b/gsearchtool/src/gsearchtool-support.c new file mode 100644 index 00000000..a120967b --- /dev/null +++ b/gsearchtool/src/gsearchtool-support.c @@ -0,0 +1,1588 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * MATE Search Tool + * + * File: gsearchtool-support.c + * + * (C) 2002 the Free Software Foundation + * + * Authors: Dennis Cranston + * George Lebl + * + * 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 St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gsearchtool.h" +#include "gsearchtool-callbacks.h" +#include "gsearchtool-support.h" + +#define C_STANDARD_STRFTIME_CHARACTERS "aAbBcdHIjmMpSUwWxXyYZ" +#define C_STANDARD_NUMERIC_STRFTIME_CHARACTERS "dHIjmMSUwWyY" +#define SUS_EXTENDED_STRFTIME_MODIFIERS "EO" +#define BINARY_EXEC_MIME_TYPE "application/x-executable" + +GtkTreeViewColumn * +gsearchtool_gtk_tree_view_get_column_with_sort_column_id (GtkTreeView * treeview, + gint id); + +/* START OF GENERIC MATE-SEARCH-TOOL FUNCTIONS */ + +gboolean +is_path_hidden (const gchar * path) +{ + gint results = FALSE; + gchar * sub_str; + gchar * hidden_path_substr = g_strconcat (G_DIR_SEPARATOR_S, ".", NULL); + + sub_str = g_strstr_len (path, strlen (path), hidden_path_substr); + + if (sub_str != NULL) { + gchar * mate_desktop_str; + + mate_desktop_str = g_strconcat (G_DIR_SEPARATOR_S, ".mate-desktop", G_DIR_SEPARATOR_S, NULL); + + /* exclude the .mate-desktop folder */ + if (strncmp (sub_str, mate_desktop_str, strlen (mate_desktop_str)) == 0) { + sub_str++; + results = (g_strstr_len (sub_str, strlen (sub_str), hidden_path_substr) != NULL); + } + else { + results = TRUE; + } + + g_free (mate_desktop_str); + } + + g_free (hidden_path_substr); + return results; +} + +gboolean +is_quick_search_excluded_path (const gchar * path) +{ + GSettings * settings; + gchar ** exclude_path_list; + gchar * dir; + gboolean results = FALSE; + gint i; + + dir = g_strdup (path); + + /* Remove trailing G_DIR_SEPARATOR. */ + if ((strlen (dir) > 1) && (g_str_has_suffix (dir, G_DIR_SEPARATOR_S) == TRUE)) { + dir[strlen (dir) - 1] = '\0'; + } + + /* Always exclude a path that is symbolic link. */ + if (g_file_test (dir, G_FILE_TEST_IS_SYMLINK)) { + g_free (dir); + + return TRUE; + } + g_free (dir); + + settings = g_settings_new ("org.mate.search-tool"); + + /* Check path against the Quick-Search-Excluded-Paths list. */ + exclude_path_list = g_settings_get_strv (settings, "quick-search-excluded-paths"); + + if (exclude_path_list) { + for (i = 0; exclude_path_list[i]; i++) { + + /* Skip empty or null values. */ + if (strlen (exclude_path_list[i]) == 0) { + continue; + } + + dir = g_strdup (exclude_path_list[i]); + + /* Wild-card comparisons. */ + if (g_strstr_len (dir, strlen (dir), "*") != NULL) { + + if (g_pattern_match_simple (dir, path) == TRUE) { + + results = TRUE; + g_free (dir); + break; + } + } + /* Non-wild-card comparisons. */ + else { + /* Add a trailing G_DIR_SEPARATOR. */ + if (g_str_has_suffix (dir, G_DIR_SEPARATOR_S) == FALSE) { + + gchar *tmp; + + tmp = dir; + dir = g_strconcat (dir, G_DIR_SEPARATOR_S, NULL); + g_free (tmp); + } + + if (strcmp (path, dir) == 0) { + + results = TRUE; + g_free (dir); + break; + } + } + g_free (dir); + } + g_strfreev (exclude_path_list); + } + + g_object_unref (settings); + return results; +} + +gboolean +is_second_scan_excluded_path (const gchar * path) +{ + GSettings * settings; + gchar ** exclude_path_list; + gchar * dir; + gboolean results = FALSE; + gint i; + + dir = g_strdup (path); + + /* Remove trailing G_DIR_SEPARATOR. */ + if ((strlen (dir) > 1) && (g_str_has_suffix (dir, G_DIR_SEPARATOR_S) == TRUE)) { + dir[strlen (dir) - 1] = '\0'; + } + + /* Always exclude a path that is symbolic link. */ + if (g_file_test (dir, G_FILE_TEST_IS_SYMLINK)) { + g_free (dir); + + return TRUE; + } + g_free (dir); + + settings = g_settings_new ("org.mate.search-tool"); + + /* Check path against the Quick-Search-Excluded-Paths list. */ + exclude_path_list = g_settings_get_strv (settings, "quick-search-second-scan-excluded-paths"); + + if (exclude_path_list) { + for (i = 0; exclude_path_list[i]; i++) { + + /* Skip empty or null values. */ + if (strlen (exclude_path_list[i]) == 0) { + continue; + } + + dir = g_strdup (exclude_path_list[i]); + + /* Wild-card comparisons. */ + if (g_strstr_len (dir, strlen (dir), "*") != NULL) { + + if (g_pattern_match_simple (dir, path) == TRUE) { + + results = TRUE; + g_free (dir); + break; + } + } + /* Non-wild-card comparisons. */ + else { + /* Add a trailing G_DIR_SEPARATOR. */ + if (g_str_has_suffix (dir, G_DIR_SEPARATOR_S) == FALSE) { + + gchar *tmp; + + tmp = dir; + dir = g_strconcat (dir, G_DIR_SEPARATOR_S, NULL); + g_free (tmp); + } + + if (strcmp (path, dir) == 0) { + + results = TRUE; + g_free (dir); + break; + } + } + g_free (dir); + } + g_strfreev (exclude_path_list); + } + + g_object_unref (settings); + return results; +} + +gboolean +compare_regex (const gchar * regex, + const gchar * string) +{ + regex_t regexec_pattern; + + if (regex == NULL) { + return TRUE; + } + + if (!regcomp (®exec_pattern, regex, REG_EXTENDED|REG_NOSUB)) { + if (regexec (®exec_pattern, string, 0, 0, 0) != REG_NOMATCH) { + regfree (®exec_pattern); + return TRUE; + } + regfree (®exec_pattern); + } + return FALSE; +} + +gboolean +limit_string_to_x_lines (GString * string, + gint x) +{ + int i; + int count = 0; + for (i = 0; string->str[i] != '\0'; i++) { + if (string->str[i] == '\n') { + count++; + if (count == x) { + g_string_truncate (string, i); + return TRUE; + } + } + } + return FALSE; +} + +static gint +count_of_char_in_string (const gchar * string, + const gchar c) +{ + int cnt = 0; + for(; *string; string++) { + if (*string == c) cnt++; + } + return cnt; +} + +gchar * +escape_single_quotes (const gchar * string) +{ + GString * gs; + + if (string == NULL) { + return NULL; + } + + if (count_of_char_in_string (string, '\'') == 0) { + return g_strdup(string); + } + gs = g_string_new (""); + for(; *string; string++) { + if (*string == '\'') { + g_string_append(gs, "'\\''"); + } + else { + g_string_append_c(gs, *string); + } + } + return g_string_free (gs, FALSE); +} + +gchar * +escape_double_quotes (const gchar * string) +{ + GString * gs; + + if (string == NULL) { + return NULL; + } + + if (count_of_char_in_string (string, '\"') == 0) { + return g_strdup(string); + } + gs = g_string_new (""); + for(; *string; string++) { + if (*string == '\"') { + g_string_append(gs, "\\\""); + } + else { + g_string_append_c(gs, *string); + } + } + return g_string_free (gs, FALSE); +} + +gchar * +backslash_backslash_characters (const gchar * string) +{ + GString * gs; + + if (string == NULL) { + return NULL; + } + + if (count_of_char_in_string (string, '\\') == 0){ + return g_strdup(string); + } + gs = g_string_new (""); + for(; *string; string++) { + if (*string == '\\') { + g_string_append(gs, "\\\\"); + } + else { + g_string_append_c(gs, *string); + } + } + return g_string_free (gs, FALSE); +} + +gchar * +backslash_special_characters (const gchar * string) +{ + GString * gs; + + if (string == NULL) { + return NULL; + } + + if ((count_of_char_in_string (string, '\\') == 0) && + (count_of_char_in_string (string, '-') == 0)) { + return g_strdup(string); + } + gs = g_string_new (""); + for(; *string; string++) { + if (*string == '\\') { + g_string_append(gs, "\\\\"); + } + else if (*string == '-') { + g_string_append(gs, "\\-"); + } + else { + g_string_append_c(gs, *string); + } + } + return g_string_free (gs, FALSE); +} + +gchar * +remove_mnemonic_character (const gchar * string) +{ + GString * gs; + gboolean first_mnemonic = TRUE; + + if (string == NULL) { + return NULL; + } + + gs = g_string_new (""); + for(; *string; string++) { + if ((first_mnemonic) && (*string == '_')) { + first_mnemonic = FALSE; + continue; + } + g_string_append_c(gs, *string); + } + return g_string_free (gs, FALSE); +} + +gchar * +get_readable_date (const CajaDateFormat date_format_enum, + const time_t file_time_raw) +{ + struct tm * file_time; + gchar * format; + GDate * today; + GDate * file_date; + guint32 file_date_age; + gchar * readable_date; + + file_time = localtime (&file_time_raw); + + /* Base format of date column on caja date-format key */ + if (date_format_enum == CAJA_DATE_FORMAT_LOCALE) { + return gsearchtool_strdup_strftime ("%c", file_time); + } else if (date_format_enum == CAJA_DATE_FORMAT_ISO) { + return gsearchtool_strdup_strftime ("%Y-%m-%d %H:%M:%S", file_time); + } + + file_date = g_date_new_dmy (file_time->tm_mday, + file_time->tm_mon + 1, + file_time->tm_year + 1900); + + today = g_date_new (); + g_date_set_time_t (today, time (NULL)); + + file_date_age = g_date_get_julian (today) - g_date_get_julian (file_date); + + g_date_free (today); + g_date_free (file_date); + + if (file_date_age == 0) { + /* Translators: Below are the strings displayed in the 'Date Modified' + column of the list view. The format of this string can vary depending + on age of a file. Please modify the format of the timestamp to match + your locale. For example, to display 24 hour time replace the '%-I' + with '%-H' and remove the '%p'. (See bugzilla report #120434.) */ + format = g_strdup(_("today at %-I:%M %p")); + } else if (file_date_age == 1) { + format = g_strdup(_("yesterday at %-I:%M %p")); + } else if (file_date_age < 7) { + format = g_strdup(_("%A, %B %-d %Y at %-I:%M:%S %p")); + } else { + format = g_strdup(_("%A, %B %-d %Y at %-I:%M:%S %p")); + } + + readable_date = gsearchtool_strdup_strftime (format, file_time); + g_free (format); + + return readable_date; +} + +gchar * +gsearchtool_strdup_strftime (const gchar * format, + struct tm * time_pieces) +{ + /* This function is borrowed from eel's eel_strdup_strftime() */ + GString * string; + const char * remainder, * percent; + char code[4], buffer[512]; + char * piece, * result, * converted; + size_t string_length; + gboolean strip_leading_zeros, turn_leading_zeros_to_spaces; + char modifier; + int i; + + /* Format could be translated, and contain UTF-8 chars, + * so convert to locale encoding which strftime uses */ + converted = g_locale_from_utf8 (format, -1, NULL, NULL, NULL); + g_return_val_if_fail (converted != NULL, NULL); + + string = g_string_new (""); + remainder = converted; + + /* Walk from % character to % character. */ + for (;;) { + percent = strchr (remainder, '%'); + if (percent == NULL) { + g_string_append (string, remainder); + break; + } + g_string_append_len (string, remainder, + percent - remainder); + + /* Handle the "%" character. */ + remainder = percent + 1; + switch (*remainder) { + case '-': + strip_leading_zeros = TRUE; + turn_leading_zeros_to_spaces = FALSE; + remainder++; + break; + case '_': + strip_leading_zeros = FALSE; + turn_leading_zeros_to_spaces = TRUE; + remainder++; + break; + case '%': + g_string_append_c (string, '%'); + remainder++; + continue; + case '\0': + g_warning ("Trailing %% passed to gsearchtool_strdup_strftime"); + g_string_append_c (string, '%'); + continue; + default: + strip_leading_zeros = FALSE; + turn_leading_zeros_to_spaces = FALSE; + break; + } + + modifier = 0; + if (strchr (SUS_EXTENDED_STRFTIME_MODIFIERS, *remainder) != NULL) { + modifier = *remainder; + remainder++; + + if (*remainder == 0) { + g_warning ("Unfinished %%%c modifier passed to gsearchtool_strdup_strftime", modifier); + break; + } + } + + if (strchr (C_STANDARD_STRFTIME_CHARACTERS, *remainder) == NULL) { + g_warning ("gsearchtool_strdup_strftime does not support " + "non-standard escape code %%%c", + *remainder); + } + + /* Convert code to strftime format. We have a fixed + * limit here that each code can expand to a maximum + * of 512 bytes, which is probably OK. There's no + * limit on the total size of the result string. + */ + i = 0; + code[i++] = '%'; + if (modifier != 0) { +#ifdef HAVE_STRFTIME_EXTENSION + code[i++] = modifier; +#endif + } + code[i++] = *remainder; + code[i++] = '\0'; + string_length = strftime (buffer, sizeof (buffer), + code, time_pieces); + if (string_length == 0) { + /* We could put a warning here, but there's no + * way to tell a successful conversion to + * empty string from a failure. + */ + buffer[0] = '\0'; + } + + /* Strip leading zeros if requested. */ + piece = buffer; + if (strip_leading_zeros || turn_leading_zeros_to_spaces) { + if (strchr (C_STANDARD_NUMERIC_STRFTIME_CHARACTERS, *remainder) == NULL) { + g_warning ("gsearchtool_strdup_strftime does not support " + "modifier for non-numeric escape code %%%c%c", + remainder[-1], + *remainder); + } + if (*piece == '0') { + do { + piece++; + } while (*piece == '0'); + if (!g_ascii_isdigit (*piece)) { + piece--; + } + } + if (turn_leading_zeros_to_spaces) { + memset (buffer, ' ', piece - buffer); + piece = buffer; + } + } + remainder++; + + /* Add this piece. */ + g_string_append (string, piece); + } + + /* Convert the string back into utf-8. */ + result = g_locale_to_utf8 (string->str, -1, NULL, NULL, NULL); + + g_string_free (string, TRUE); + g_free (converted); + + return result; +} + +gchar * +get_file_type_description (const gchar * file, + GFileInfo * file_info) +{ + const char * content_type = NULL; + gchar * desc; + + if (file != NULL) { + content_type = g_file_info_get_content_type (file_info); + } + + if (content_type == NULL || g_content_type_is_unknown (content_type) == TRUE) { + return g_strdup (g_content_type_get_description ("application/octet-stream")); + } + + desc = g_strdup (g_content_type_get_description (content_type)); + + if (g_file_info_get_is_symlink (file_info) == TRUE) { + + const gchar * symlink_target; + gchar * absolute_symlink = NULL; + gchar * str = NULL; + + symlink_target = g_file_info_get_symlink_target (file_info); + + if (g_path_is_absolute (symlink_target) != TRUE) { + gchar *dirname; + + dirname = g_path_get_dirname (file); + absolute_symlink = g_strconcat (dirname, G_DIR_SEPARATOR_S, symlink_target, NULL); + g_free (dirname); + } + else { + absolute_symlink = g_strdup (symlink_target); + } + + if (g_file_test (absolute_symlink, G_FILE_TEST_EXISTS) != TRUE) { + if ((g_ascii_strcasecmp (content_type, "x-special/socket") != 0) && + (g_ascii_strcasecmp (content_type, "x-special/fifo") != 0)) { + g_free (absolute_symlink); + g_free (desc); + return g_strdup (_("link (broken)")); + } + } + + str = g_strdup_printf (_("link to %s"), (desc != NULL) ? desc : content_type); + g_free (absolute_symlink); + g_free (desc); + return str; + } + return desc; +} + +static gchar * +gsearchtool_pixmap_file (const gchar * partial_path) +{ + gchar * path; + + path = g_build_filename(DATADIR "/pixmaps/mate-search-tool", partial_path, NULL); + if (g_file_test(path, G_FILE_TEST_EXISTS)){ + return path; + } + g_free (path); + return NULL; +} + +static GdkPixbuf * +gsearchtool_load_thumbnail_frame (void) +{ + GdkPixbuf * pixbuf = NULL; + gchar * image_path; + + image_path = gsearchtool_pixmap_file("thumbnail_frame.png"); + + if (image_path != NULL){ + pixbuf = gdk_pixbuf_new_from_file(image_path, NULL); + } + g_free(image_path); + return pixbuf; +} + +static void +gsearchtool_draw_frame_row (GdkPixbuf * frame_image, + gint target_width, + gint source_width, + gint source_v_position, + gint dest_v_position, + GdkPixbuf * result_pixbuf, + gint left_offset, + gint height) +{ + gint remaining_width; + gint h_offset; + gint slab_width; + + remaining_width = target_width; + h_offset = 0; + while (remaining_width > 0) { + slab_width = remaining_width > source_width ? source_width : remaining_width; + gdk_pixbuf_copy_area (frame_image, left_offset, source_v_position, slab_width, + height, result_pixbuf, left_offset + h_offset, dest_v_position); + remaining_width -= slab_width; + h_offset += slab_width; + } +} + +static void +gsearchtool_draw_frame_column (GdkPixbuf * frame_image, + gint target_height, + gint source_height, + gint source_h_position, + gint dest_h_position, + GdkPixbuf * result_pixbuf, + gint top_offset, + gint width) +{ + gint remaining_height; + gint v_offset; + gint slab_height; + + remaining_height = target_height; + v_offset = 0; + while (remaining_height > 0) { + slab_height = remaining_height > source_height ? source_height : remaining_height; + gdk_pixbuf_copy_area (frame_image, source_h_position, top_offset, width, slab_height, + result_pixbuf, dest_h_position, top_offset + v_offset); + remaining_height -= slab_height; + v_offset += slab_height; + } +} + +static GdkPixbuf * +gsearchtool_stretch_frame_image (GdkPixbuf *frame_image, + gint left_offset, + gint top_offset, + gint right_offset, + gint bottom_offset, + gint dest_width, + gint dest_height, + gboolean fill_flag) +{ + GdkPixbuf * result_pixbuf; + gint frame_width, frame_height; + gint target_width, target_frame_width; + gint target_height, target_frame_height; + + frame_width = gdk_pixbuf_get_width (frame_image); + frame_height = gdk_pixbuf_get_height (frame_image); + + if (fill_flag) { + result_pixbuf = gdk_pixbuf_scale_simple (frame_image, dest_width, dest_height, GDK_INTERP_NEAREST); + } else { + result_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, dest_width, dest_height); + } + + /* clear the new pixbuf */ + if (fill_flag == FALSE) { + gdk_pixbuf_fill (result_pixbuf, 0xffffffff); + } + + target_width = dest_width - left_offset - right_offset; + target_frame_width = frame_width - left_offset - right_offset; + + target_height = dest_height - top_offset - bottom_offset; + target_frame_height = frame_height - top_offset - bottom_offset; + + /* Draw the left top corner and top row */ + gdk_pixbuf_copy_area (frame_image, 0, 0, left_offset, top_offset, result_pixbuf, 0, 0); + gsearchtool_draw_frame_row (frame_image, target_width, target_frame_width, 0, 0, + result_pixbuf, left_offset, top_offset); + + /* Draw the right top corner and left column */ + gdk_pixbuf_copy_area (frame_image, frame_width - right_offset, 0, right_offset, top_offset, + result_pixbuf, dest_width - right_offset, 0); + gsearchtool_draw_frame_column (frame_image, target_height, target_frame_height, 0, 0, + result_pixbuf, top_offset, left_offset); + + /* Draw the bottom right corner and bottom row */ + gdk_pixbuf_copy_area (frame_image, frame_width - right_offset, frame_height - bottom_offset, + right_offset, bottom_offset, result_pixbuf, dest_width - right_offset, + dest_height - bottom_offset); + gsearchtool_draw_frame_row (frame_image, target_width, target_frame_width, + frame_height - bottom_offset, dest_height - bottom_offset, + result_pixbuf, left_offset, bottom_offset); + + /* Draw the bottom left corner and the right column */ + gdk_pixbuf_copy_area (frame_image, 0, frame_height - bottom_offset, left_offset, bottom_offset, + result_pixbuf, 0, dest_height - bottom_offset); + gsearchtool_draw_frame_column (frame_image, target_height, target_frame_height, + frame_width - right_offset, dest_width - right_offset, + result_pixbuf, top_offset, right_offset); + return result_pixbuf; +} + +static GdkPixbuf * +gsearchtool_embed_image_in_frame (GdkPixbuf * source_image, + GdkPixbuf * frame_image, + gint left_offset, + gint top_offset, + gint right_offset, + gint bottom_offset) +{ + GdkPixbuf * result_pixbuf; + gint source_width, source_height; + gint dest_width, dest_height; + + source_width = gdk_pixbuf_get_width (source_image); + source_height = gdk_pixbuf_get_height (source_image); + + dest_width = source_width + left_offset + right_offset; + dest_height = source_height + top_offset + bottom_offset; + + result_pixbuf = gsearchtool_stretch_frame_image (frame_image, left_offset, top_offset, right_offset, bottom_offset, + dest_width, dest_height, FALSE); + + gdk_pixbuf_copy_area (source_image, 0, 0, source_width, source_height, result_pixbuf, left_offset, top_offset); + + return result_pixbuf; +} + +static void +gsearchtool_thumbnail_frame_image (GdkPixbuf ** pixbuf) +{ + GdkPixbuf * pixbuf_with_frame; + GdkPixbuf * frame; + + frame = gsearchtool_load_thumbnail_frame (); + if (frame == NULL) { + return; + } + + pixbuf_with_frame = gsearchtool_embed_image_in_frame (*pixbuf, frame, 3, 3, 6, 6); + g_object_unref (*pixbuf); + g_object_unref (frame); + + *pixbuf = pixbuf_with_frame; +} + +static GdkPixbuf * +gsearchtool_get_thumbnail_image (const gchar * thumbnail) +{ + GdkPixbuf * pixbuf = NULL; + + if (thumbnail != NULL) { + if (g_file_test (thumbnail, G_FILE_TEST_EXISTS)) { + + GdkPixbuf * thumbnail_pixbuf = NULL; + gfloat scale_factor_x = 1.0; + gfloat scale_factor_y = 1.0; + gint scale_x; + gint scale_y; + + thumbnail_pixbuf = gdk_pixbuf_new_from_file (thumbnail, NULL); + gsearchtool_thumbnail_frame_image (&thumbnail_pixbuf); + + if (gdk_pixbuf_get_width (thumbnail_pixbuf) > ICON_SIZE) { + scale_factor_x = (gfloat) ICON_SIZE / (gfloat) gdk_pixbuf_get_width (thumbnail_pixbuf); + } + if (gdk_pixbuf_get_height (thumbnail_pixbuf) > ICON_SIZE) { + scale_factor_y = (gfloat) ICON_SIZE / (gfloat) gdk_pixbuf_get_height (thumbnail_pixbuf); + } + + if (gdk_pixbuf_get_width (thumbnail_pixbuf) > gdk_pixbuf_get_height (thumbnail_pixbuf)) { + scale_x = ICON_SIZE; + scale_y = (gint) (gdk_pixbuf_get_height (thumbnail_pixbuf) * scale_factor_x); + } + else { + scale_x = (gint) (gdk_pixbuf_get_width (thumbnail_pixbuf) * scale_factor_y); + scale_y = ICON_SIZE; + } + + pixbuf = gdk_pixbuf_scale_simple (thumbnail_pixbuf, scale_x, scale_y, GDK_INTERP_BILINEAR); + g_object_unref (thumbnail_pixbuf); + } + } + return pixbuf; +} + +static GdkPixbuf * +get_themed_icon_pixbuf (GThemedIcon * icon, + int size, + GtkIconTheme * icon_theme) +{ + char ** icon_names; + GtkIconInfo * icon_info; + GdkPixbuf * pixbuf; + GError * error = NULL; + + g_object_get (icon, "names", &icon_names, NULL); + + icon_info = gtk_icon_theme_choose_icon (icon_theme, (const char **)icon_names, size, 0); + if (icon_info == NULL) { + icon_info = gtk_icon_theme_lookup_icon (icon_theme, "text-x-generic", size, GTK_ICON_LOOKUP_USE_BUILTIN); + } + pixbuf = gtk_icon_info_load_icon (icon_info, &error); + if (pixbuf == NULL) { + g_warning ("Could not load icon pixbuf: %s\n", error->message); + g_clear_error (&error); + } + + gtk_icon_info_free (icon_info); + g_strfreev (icon_names); + + return pixbuf; +} + + + +GdkPixbuf * +get_file_pixbuf (GSearchWindow * gsearch, + GFileInfo * file_info) +{ + GdkPixbuf * pixbuf; + GIcon * icon = NULL; + const gchar * thumbnail_path = NULL; + + if (file_info == NULL) { + return NULL; + } + + icon = g_file_info_get_icon (file_info); + + if (gsearch->show_thumbnails == TRUE) { + thumbnail_path = g_file_info_get_attribute_byte_string (file_info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH); + } + + if (thumbnail_path != NULL) { + pixbuf = gsearchtool_get_thumbnail_image (thumbnail_path); + } + else { + gchar * icon_string; + + icon_string = g_icon_to_string (icon); + pixbuf = (GdkPixbuf *) g_hash_table_lookup (gsearch->search_results_filename_hash_table, icon_string); + + if (pixbuf == NULL) { + pixbuf = get_themed_icon_pixbuf (G_THEMED_ICON (icon), ICON_SIZE, gtk_icon_theme_get_default ()); + g_hash_table_insert (gsearch->search_results_filename_hash_table, g_strdup (icon_string), pixbuf); + } + g_free (icon_string); + } + return pixbuf; +} + +gboolean +open_file_with_filemanager (GtkWidget * window, + const gchar * file) +{ + GDesktopAppInfo * d_app_info; + GKeyFile * key_file; + GdkAppLaunchContext * ctx = NULL; + GList * list = NULL; + GAppInfo * g_app_info; + GFile * g_file; + gchar * command; + gchar * contents; + gchar * uri; + gboolean result = TRUE; + + uri = g_filename_to_uri (file, NULL, NULL); + list = g_list_prepend (list, uri); + + g_file = g_file_new_for_path (file); + g_app_info = g_file_query_default_handler (g_file, NULL, NULL); + + if (strcmp (g_app_info_get_executable (g_app_info), "caja") == 0) { + command = g_strconcat ("caja ", + "--sm-disable ", + "--no-desktop ", + "--no-default-window ", + NULL); + } + else { + command = g_strconcat (g_app_info_get_executable (g_app_info), + " ", NULL); + } + + contents = g_strdup_printf ("[Desktop Entry]\n" + "Name=Caja\n" + "Icon=file-manager\n" + "Exec=%s\n" + "Terminal=false\n" + "StartupNotify=true\n" + "Type=Application\n", + command); + key_file = g_key_file_new (); + g_key_file_load_from_data (key_file, contents, strlen(contents), G_KEY_FILE_NONE, NULL); + d_app_info = g_desktop_app_info_new_from_keyfile (key_file); + + if (d_app_info != NULL) { + ctx = gdk_app_launch_context_new (); + gdk_app_launch_context_set_screen (ctx, gtk_widget_get_screen (window)); + + result = g_app_info_launch_uris (G_APP_INFO (d_app_info), list, G_APP_LAUNCH_CONTEXT (ctx), NULL); + } + else { + result = FALSE; + } + + g_object_unref (g_app_info); + g_object_unref (d_app_info); + g_object_unref (g_file); + g_object_unref (ctx); + g_key_file_free (key_file); + g_list_free (list); + g_free (contents); + g_free (command); + g_free (uri); + + return result; +} + +gboolean +open_file_with_application (GtkWidget * window, + const gchar * file, + GAppInfo * app) +{ + GdkAppLaunchContext * context; + GdkScreen * screen; + gboolean result; + + if (g_file_test (file, G_FILE_TEST_IS_DIR) == TRUE) { + return FALSE; + } + + context = gdk_app_launch_context_new (); + screen = gtk_widget_get_screen (window); + gdk_app_launch_context_set_screen (context, screen); + + if (app == NULL) { + gchar * uri; + + uri = g_filename_to_uri (file, NULL, NULL); + result = g_app_info_launch_default_for_uri (uri, (GAppLaunchContext *) context, NULL); + g_free (uri); + } + else { + GList * g_file_list = NULL; + GFile * g_file = NULL; + + g_file = g_file_new_for_path (file); + + if (g_file == NULL) { + result = FALSE; + } + else { + g_file_list = g_list_prepend (g_file_list, g_file); + + result = g_app_info_launch (app, g_file_list, (GAppLaunchContext *) context, NULL); + g_list_free (g_file_list); + g_object_unref (g_file); + } + } + return result; +} + +gboolean +launch_file (const gchar * file) +{ + const char * content_type = g_content_type_guess (file, NULL, 0, NULL); + gboolean result = FALSE; + + if ((g_file_test (file, G_FILE_TEST_IS_EXECUTABLE)) && + (g_ascii_strcasecmp (content_type, BINARY_EXEC_MIME_TYPE) == 0)) { + result = g_spawn_command_line_async (file, NULL); + } + + return result; +} + +gchar * +gsearchtool_get_unique_filename (const gchar * path, + const gchar * suffix) +{ + const gint num_of_words = 12; + gchar * words[] = { + "foo", + "bar", + "blah", + "cranston", + "frobate", + "hadjaha", + "greasy", + "hammer", + "eek", + "larry", + "curly", + "moe", + NULL}; + gchar * retval = NULL; + gboolean exists = TRUE; + + while (exists) { + gchar * file; + gint rnd; + gint word; + + rnd = rand (); + word = rand () % num_of_words; + + file = g_strdup_printf ("%s-%010x%s", + words [word], + (guint) rnd, + suffix); + + g_free (retval); + retval = g_strconcat (path, G_DIR_SEPARATOR_S, file, NULL); + exists = g_file_test (retval, G_FILE_TEST_EXISTS); + g_free (file); + } + return retval; +} + +GtkWidget * +gsearchtool_button_new_with_stock_icon (const gchar * string, + const gchar * stock_id) +{ + GtkWidget * align; + GtkWidget * button; + GtkWidget * hbox; + GtkWidget * image; + GtkWidget * label; + + button = gtk_button_new (); + label = gtk_label_new_with_mnemonic (string); + gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (button)); + image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON); + hbox = gtk_hbox_new (FALSE, 2); + align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0); + gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0); + gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_container_add (GTK_CONTAINER (button), align); + gtk_container_add (GTK_CONTAINER (align), hbox); + gtk_widget_show_all (align); + + return button; +} + +GSList * +gsearchtool_get_columns_order (GtkTreeView * treeview) +{ + GSList *order = NULL; + GList * columns; + GList * col; + + columns = gtk_tree_view_get_columns (treeview); + + for (col = columns; col; col = col->next) { + gint id; + + id = gtk_tree_view_column_get_sort_column_id (col->data); + order = g_slist_prepend (order, GINT_TO_POINTER (id)); + } + g_list_free (columns); + + order = g_slist_reverse (order); + return order; +} + +GtkTreeViewColumn * +gsearchtool_gtk_tree_view_get_column_with_sort_column_id (GtkTreeView * treeview, + gint id) +{ + GtkTreeViewColumn * col = NULL; + GList * columns; + GList * it; + + columns = gtk_tree_view_get_columns (treeview); + + for (it = columns; it; it = it->next) { + if (gtk_tree_view_column_get_sort_column_id (it->data) == id) { + col = it->data; + break; + } + } + g_list_free (columns); + return col; +} + +void +gsearchtool_set_columns_order (GtkTreeView * treeview) +{ + GtkTreeViewColumn * last = NULL; + GSettings * settings; + GVariant * value; + + settings = g_settings_new ("org.mate.search-tool"); + + value = g_settings_get_value (settings, "columns-order"); + + if (value) { + GVariantIter *iter; + GVariant *item; + + g_variant_get (value, "ai", &iter); + + while ((item = g_variant_iter_next_value (iter))) { + GtkTreeViewColumn * cur; + gint id; + + g_variant_get (item, "i", &id); + + if (id >= 0 && id < NUM_COLUMNS) { + + cur = gsearchtool_gtk_tree_view_get_column_with_sort_column_id (treeview, id); + + if (cur && cur != last) { + gtk_tree_view_move_column_after (treeview, cur, last); + last = cur; + } + } + g_variant_unref (item); + } + g_variant_iter_free (iter); + g_variant_unref (value); + } + g_object_unref (settings); +} + +void +gsearchtool_get_stored_window_geometry (gint * width, + gint * height) +{ + GSettings * settings; + gint saved_width; + gint saved_height; + + if (width == NULL || height == NULL) { + return; + } + + settings = g_settings_new ("org.mate.search-tool"); + + saved_width = g_settings_get_int (settings, "default-window-width"); + saved_height = g_settings_get_int (settings, "default-window-height"); + + if (saved_width == -1) { + saved_width = DEFAULT_WINDOW_WIDTH; + } + + if (saved_height == -1) { + saved_height = DEFAULT_WINDOW_HEIGHT; + } + + *width = MAX (saved_width, MINIMUM_WINDOW_WIDTH); + *height = MAX (saved_height, MINIMUM_WINDOW_HEIGHT); + g_object_unref (settings); +} + +/* START OF CAJA/EEL FUNCTIONS: USED FOR HANDLING OF DUPLICATE FILENAMES */ + +/* Localizers: + * Feel free to leave out the st, nd, rd and th suffix or + * make some or all of them match. + */ + +/* localizers: tag used to detect the first copy of a file */ +static const char untranslated_copy_duplicate_tag[] = N_(" (copy)"); +/* localizers: tag used to detect the second copy of a file */ +static const char untranslated_another_copy_duplicate_tag[] = N_(" (another copy)"); + +/* localizers: tag used to detect the x11th copy of a file */ +static const char untranslated_x11th_copy_duplicate_tag[] = N_("th copy)"); +/* localizers: tag used to detect the x12th copy of a file */ +static const char untranslated_x12th_copy_duplicate_tag[] = N_("th copy)"); +/* localizers: tag used to detect the x13th copy of a file */ +static const char untranslated_x13th_copy_duplicate_tag[] = N_("th copy)"); + +/* localizers: tag used to detect the x1st copy of a file */ +static const char untranslated_st_copy_duplicate_tag[] = N_("st copy)"); +/* localizers: tag used to detect the x2nd copy of a file */ +static const char untranslated_nd_copy_duplicate_tag[] = N_("nd copy)"); +/* localizers: tag used to detect the x3rd copy of a file */ +static const char untranslated_rd_copy_duplicate_tag[] = N_("rd copy)"); + +/* localizers: tag used to detect the xxth copy of a file */ +static const char untranslated_th_copy_duplicate_tag[] = N_("th copy)"); + +#define COPY_DUPLICATE_TAG _(untranslated_copy_duplicate_tag) +#define ANOTHER_COPY_DUPLICATE_TAG _(untranslated_another_copy_duplicate_tag) +#define X11TH_COPY_DUPLICATE_TAG _(untranslated_x11th_copy_duplicate_tag) +#define X12TH_COPY_DUPLICATE_TAG _(untranslated_x12th_copy_duplicate_tag) +#define X13TH_COPY_DUPLICATE_TAG _(untranslated_x13th_copy_duplicate_tag) + +#define ST_COPY_DUPLICATE_TAG _(untranslated_st_copy_duplicate_tag) +#define ND_COPY_DUPLICATE_TAG _(untranslated_nd_copy_duplicate_tag) +#define RD_COPY_DUPLICATE_TAG _(untranslated_rd_copy_duplicate_tag) +#define TH_COPY_DUPLICATE_TAG _(untranslated_th_copy_duplicate_tag) + +/* localizers: appended to first file copy */ +static const char untranslated_first_copy_duplicate_format[] = N_("%s (copy)%s"); +/* localizers: appended to second file copy */ +static const char untranslated_second_copy_duplicate_format[] = N_("%s (another copy)%s"); + +/* localizers: appended to x11th file copy */ +static const char untranslated_x11th_copy_duplicate_format[] = N_("%s (%dth copy)%s"); +/* localizers: appended to x12th file copy */ +static const char untranslated_x12th_copy_duplicate_format[] = N_("%s (%dth copy)%s"); +/* localizers: appended to x13th file copy */ +static const char untranslated_x13th_copy_duplicate_format[] = N_("%s (%dth copy)%s"); + +/* localizers: appended to x1st file copy */ +static const char untranslated_st_copy_duplicate_format[] = N_("%s (%dst copy)%s"); +/* localizers: appended to x2nd file copy */ +static const char untranslated_nd_copy_duplicate_format[] = N_("%s (%dnd copy)%s"); +/* localizers: appended to x3rd file copy */ +static const char untranslated_rd_copy_duplicate_format[] = N_("%s (%drd copy)%s"); +/* localizers: appended to xxth file copy */ +static const char untranslated_th_copy_duplicate_format[] = N_("%s (%dth copy)%s"); + +#define FIRST_COPY_DUPLICATE_FORMAT _(untranslated_first_copy_duplicate_format) +#define SECOND_COPY_DUPLICATE_FORMAT _(untranslated_second_copy_duplicate_format) +#define X11TH_COPY_DUPLICATE_FORMAT _(untranslated_x11th_copy_duplicate_format) +#define X12TH_COPY_DUPLICATE_FORMAT _(untranslated_x12th_copy_duplicate_format) +#define X13TH_COPY_DUPLICATE_FORMAT _(untranslated_x13th_copy_duplicate_format) + +#define ST_COPY_DUPLICATE_FORMAT _(untranslated_st_copy_duplicate_format) +#define ND_COPY_DUPLICATE_FORMAT _(untranslated_nd_copy_duplicate_format) +#define RD_COPY_DUPLICATE_FORMAT _(untranslated_rd_copy_duplicate_format) +#define TH_COPY_DUPLICATE_FORMAT _(untranslated_th_copy_duplicate_format) + +static gchar * +make_valid_utf8 (const gchar * name) +{ + GString *string; + const char *remainder, *invalid; + int remaining_bytes, valid_bytes; + + string = NULL; + remainder = name; + remaining_bytes = strlen (name); + + while (remaining_bytes != 0) { + if (g_utf8_validate (remainder, remaining_bytes, &invalid)) { + break; + } + valid_bytes = invalid - remainder; + + if (string == NULL) { + string = g_string_sized_new (remaining_bytes); + } + g_string_append_len (string, remainder, valid_bytes); + g_string_append_c (string, '?'); + + remaining_bytes -= valid_bytes + 1; + remainder = invalid + 1; + } + + if (string == NULL) { + return g_strdup (name); + } + + g_string_append (string, remainder); + g_string_append (string, _(" (invalid Unicode)")); + g_assert (g_utf8_validate (string->str, -1, NULL)); + + return g_string_free (string, FALSE); +} + +static gchar * +extract_string_until (const gchar * original, + const gchar * until_substring) +{ + gchar * result; + + g_assert ((gint) strlen (original) >= until_substring - original); + g_assert (until_substring - original >= 0); + + result = g_malloc (until_substring - original + 1); + strncpy (result, original, until_substring - original); + result[until_substring - original] = '\0'; + + return result; +} + +/* Dismantle a file name, separating the base name, the file suffix and removing any + * (xxxcopy), etc. string. Figure out the count that corresponds to the given + * (xxxcopy) substring. + */ +static void +parse_previous_duplicate_name (const gchar * name, + gchar ** name_base, + const gchar ** suffix, + gint * count) +{ + const gchar * tag; + + g_assert (name[0] != '\0'); + + *suffix = strchr (name + 1, '.'); + if (*suffix == NULL || (*suffix)[1] == '\0') { + /* no suffix */ + *suffix = ""; + } + + tag = strstr (name, COPY_DUPLICATE_TAG); + if (tag != NULL) { + if (tag > *suffix) { + /* handle case "foo. (copy)" */ + *suffix = ""; + } + *name_base = extract_string_until (name, tag); + *count = 1; + return; + } + + tag = strstr (name, ANOTHER_COPY_DUPLICATE_TAG); + if (tag != NULL) { + if (tag > *suffix) { + /* handle case "foo. (another copy)" */ + *suffix = ""; + } + *name_base = extract_string_until (name, tag); + *count = 2; + return; + } + + /* Check to see if we got one of st, nd, rd, th. */ + tag = strstr (name, X11TH_COPY_DUPLICATE_TAG); + + if (tag == NULL) { + tag = strstr (name, X12TH_COPY_DUPLICATE_TAG); + } + if (tag == NULL) { + tag = strstr (name, X13TH_COPY_DUPLICATE_TAG); + } + if (tag == NULL) { + tag = strstr (name, ST_COPY_DUPLICATE_TAG); + } + if (tag == NULL) { + tag = strstr (name, ND_COPY_DUPLICATE_TAG); + } + if (tag == NULL) { + tag = strstr (name, RD_COPY_DUPLICATE_TAG); + } + if (tag == NULL) { + tag = strstr (name, TH_COPY_DUPLICATE_TAG); + } + + /* If we got one of st, nd, rd, th, fish out the duplicate number. */ + if (tag != NULL) { + /* localizers: opening parentheses to match the "th copy)" string */ + tag = strstr (name, _(" (")); + if (tag != NULL) { + if (tag > *suffix) { + /* handle case "foo. (22nd copy)" */ + *suffix = ""; + } + *name_base = extract_string_until (name, tag); + /* localizers: opening parentheses of the "th copy)" string */ + if (sscanf (tag, _(" (%d"), count) == 1) { + if (*count < 1 || *count > 1000000) { + /* keep the count within a reasonable range */ + *count = 0; + } + return; + } + *count = 0; + return; + } + } + + *count = 0; + if (**suffix != '\0') { + *name_base = extract_string_until (name, *suffix); + } else { + *name_base = g_strdup (name); + } +} + +static gchar * +make_next_duplicate_name (const gchar *base, + const gchar *suffix, + gint count) +{ + const gchar * format; + gchar * result; + + if (count < 1) { + g_warning ("bad count %d in make_next_duplicate_name()", count); + count = 1; + } + + if (count <= 2) { + + /* Handle special cases for low numbers. + * Perhaps for some locales we will need to add more. + */ + switch (count) { + default: + g_assert_not_reached (); + /* fall through */ + case 1: + format = FIRST_COPY_DUPLICATE_FORMAT; + break; + case 2: + format = SECOND_COPY_DUPLICATE_FORMAT; + break; + + } + result = g_strdup_printf (format, base, suffix); + } else { + + /* Handle special cases for the first few numbers of each ten. + * For locales where getting this exactly right is difficult, + * these can just be made all the same as the general case below. + */ + + /* Handle special cases for x11th - x20th. + */ + switch (count % 100) { + case 11: + format = X11TH_COPY_DUPLICATE_FORMAT; + break; + case 12: + format = X12TH_COPY_DUPLICATE_FORMAT; + break; + case 13: + format = X13TH_COPY_DUPLICATE_FORMAT; + break; + default: + format = NULL; + break; + } + + if (format == NULL) { + switch (count % 10) { + case 1: + format = ST_COPY_DUPLICATE_FORMAT; + break; + case 2: + format = ND_COPY_DUPLICATE_FORMAT; + break; + case 3: + format = RD_COPY_DUPLICATE_FORMAT; + break; + default: + /* The general case. */ + format = TH_COPY_DUPLICATE_FORMAT; + break; + } + } + result = g_strdup_printf (format, base, count, suffix); + } + return result; +} + +static gchar * +get_duplicate_name (const gchar *name) +{ + const gchar * suffix; + gchar * name_base; + gchar * result; + gint count; + + parse_previous_duplicate_name (name, &name_base, &suffix, &count); + result = make_next_duplicate_name (name_base, suffix, count + 1); + g_free (name_base); + + return result; +} + +gchar * +gsearchtool_get_next_duplicate_name (const gchar * basename) +{ + gchar * utf8_name; + gchar * utf8_result; + gchar * result; + + utf8_name = g_filename_to_utf8 (basename, -1, NULL, NULL, NULL); + + if (utf8_name == NULL) { + /* Couldn't convert to utf8 - probably + * G_BROKEN_FILENAMES not set when it should be. + * Try converting from the locale */ + utf8_name = g_locale_to_utf8 (basename, -1, NULL, NULL, NULL); + + if (utf8_name == NULL) { + utf8_name = make_valid_utf8 (basename); + } + } + + utf8_result = get_duplicate_name (utf8_name); + g_free (utf8_name); + + result = g_filename_from_utf8 (utf8_result, -1, NULL, NULL, NULL); + g_free (utf8_result); + return result; +} diff --git a/gsearchtool/src/gsearchtool-support.h b/gsearchtool/src/gsearchtool-support.h new file mode 100644 index 00000000..15585d5d --- /dev/null +++ b/gsearchtool/src/gsearchtool-support.h @@ -0,0 +1,114 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * MATE Search Tool + * + * File: gsearchtool-support.h + * + * (C) 2002 the Free Software Foundation + * + * Authors: Dennis Cranston + * George Lebl + * + * 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 St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef _GSEARCHTOOL_SUPPORT_H_ +#define _GSEARCHTOOL_SUPPORT_H_ + +#ifdef __cplusplus +extern "C" { +#pragma } +#endif + +#include "gsearchtool.h" + +#define ICON_SIZE 24 + +gboolean +is_path_hidden (const gchar * path); + +gboolean +is_quick_search_excluded_path (const gchar * path); + +gboolean +is_second_scan_excluded_path (const gchar * path); + +gboolean +compare_regex (const gchar * regex, + const gchar * string); +gboolean +limit_string_to_x_lines (GString * string, + gint x); +gchar * +escape_single_quotes (const gchar * string); + +gchar * +escape_double_quotes (const gchar * string); + +gchar * +backslash_backslash_characters (const gchar * string); + +gchar * +backslash_special_characters (const gchar * string); + +gchar * +remove_mnemonic_character (const gchar * string); + +gchar * +get_readable_date (const CajaDateFormat date_format_enum, + const time_t file_time_raw); +gchar * +gsearchtool_strdup_strftime (const gchar * format, + struct tm * time_pieces); +gchar * +get_file_type_description (const gchar * file, + GFileInfo * file_info); +GdkPixbuf * +get_file_pixbuf (GSearchWindow * gsearch, + GFileInfo * file_info); +gboolean +open_file_with_filemanager (GtkWidget * window, + const gchar * file); +gboolean +open_file_with_application (GtkWidget * window, + const gchar * file, + GAppInfo * app); +gboolean +launch_file (const gchar * file); + +gchar * +gsearchtool_get_unique_filename (const gchar * path, + const gchar * suffix); +GtkWidget * +gsearchtool_button_new_with_stock_icon (const gchar * string, + const gchar * stock_id); +GSList * +gsearchtool_get_columns_order (GtkTreeView * treeview); + +void +gsearchtool_set_columns_order (GtkTreeView * treeview); + +void +gsearchtool_get_stored_window_geometry (gint * width, + gint * height); +gchar * +gsearchtool_get_next_duplicate_name (const gchar * basname); + +#ifdef __cplusplus +} +#endif + +#endif /* _GSEARCHTOOL_SUPPORT_H */ diff --git a/gsearchtool/src/gsearchtool.c b/gsearchtool/src/gsearchtool.c new file mode 100644 index 00000000..757e8ca2 --- /dev/null +++ b/gsearchtool/src/gsearchtool.c @@ -0,0 +1,3070 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * MATE Search Tool + * + * File: gsearchtool.c + * + * (C) 1998,2002 the Free Software Foundation + * + * Authors: Dennis Cranston + * George Lebl + * + * 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 St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#ifndef FNM_CASEFOLD +# define FNM_CASEFOLD 0 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gsearchtool.h" +#include "gsearchtool-callbacks.h" +#include "gsearchtool-support.h" +#include "gsearchtool-entry.h" + +#define MATE_SEARCH_TOOL_DEFAULT_ICON_SIZE 16 +#define MATE_SEARCH_TOOL_STOCK "panel-searchtool" +#define MATE_SEARCH_TOOL_REFRESH_DURATION 50000 +#define LEFT_LABEL_SPACING " " + +static GObjectClass * parent_class; + +typedef enum { + SEARCH_CONSTRAINT_TYPE_BOOLEAN, + SEARCH_CONSTRAINT_TYPE_NUMERIC, + SEARCH_CONSTRAINT_TYPE_TEXT, + SEARCH_CONSTRAINT_TYPE_DATE_BEFORE, + SEARCH_CONSTRAINT_TYPE_DATE_AFTER, + SEARCH_CONSTRAINT_TYPE_SEPARATOR, + SEARCH_CONSTRAINT_TYPE_NONE +} GSearchConstraintType; + +typedef struct _GSearchOptionTemplate GSearchOptionTemplate; + +struct _GSearchOptionTemplate { + GSearchConstraintType type; /* The available option type */ + gchar * option; /* An option string to pass to the command */ + gchar * desc; /* The description for display */ + gchar * units; /* Optional units for display */ + gboolean is_selected; +}; + +static GSearchOptionTemplate GSearchOptionTemplates[] = { + { SEARCH_CONSTRAINT_TYPE_TEXT, NULL, N_("Contains the _text"), NULL, FALSE }, + { SEARCH_CONSTRAINT_TYPE_SEPARATOR, NULL, NULL, NULL, TRUE }, + { SEARCH_CONSTRAINT_TYPE_DATE_BEFORE, "-mtime -%d", N_("_Date modified less than"), N_("days"), FALSE }, + { SEARCH_CONSTRAINT_TYPE_DATE_AFTER, "\\( -mtime +%d -o -mtime %d \\)", N_("Date modified more than"), N_("days"), FALSE }, + { SEARCH_CONSTRAINT_TYPE_SEPARATOR, NULL, NULL, NULL, TRUE }, + { SEARCH_CONSTRAINT_TYPE_NUMERIC, "\\( -size %uc -o -size +%uc \\)", N_("S_ize at least"), N_("kilobytes"), FALSE }, + { SEARCH_CONSTRAINT_TYPE_NUMERIC, "\\( -size %uc -o -size -%uc \\)", N_("Si_ze at most"), N_("kilobytes"), FALSE }, + { SEARCH_CONSTRAINT_TYPE_BOOLEAN, "-size 0c \\( -type f -o -type d \\)", N_("File is empty"), NULL, FALSE }, + { SEARCH_CONSTRAINT_TYPE_SEPARATOR, NULL, NULL, NULL, TRUE }, + { SEARCH_CONSTRAINT_TYPE_TEXT, "-user '%s'", N_("Owned by _user"), NULL, FALSE }, + { SEARCH_CONSTRAINT_TYPE_TEXT, "-group '%s'", N_("Owned by _group"), NULL, FALSE }, + { SEARCH_CONSTRAINT_TYPE_BOOLEAN, "\\( -nouser -o -nogroup \\)", N_("Owner is unrecognized"), NULL, FALSE }, + { SEARCH_CONSTRAINT_TYPE_SEPARATOR, NULL, NULL, NULL, TRUE }, + { SEARCH_CONSTRAINT_TYPE_TEXT, "'!' -name '*%s*'", N_("Na_me does not contain"), NULL, FALSE }, + { SEARCH_CONSTRAINT_TYPE_TEXT, "-regex '%s'", N_("Name matches regular e_xpression"), NULL, FALSE }, + { SEARCH_CONSTRAINT_TYPE_SEPARATOR, NULL, NULL, NULL, TRUE }, + { SEARCH_CONSTRAINT_TYPE_BOOLEAN, "SHOW_HIDDEN_FILES", N_("Show hidden and backup files"), NULL, FALSE }, + { SEARCH_CONSTRAINT_TYPE_BOOLEAN, "-follow", N_("Follow symbolic links"), NULL, FALSE }, + { SEARCH_CONSTRAINT_TYPE_BOOLEAN, "EXCLUDE_OTHER_FILESYSTEMS", N_("Exclude other filesystems"), NULL, FALSE }, + { SEARCH_CONSTRAINT_TYPE_NONE, NULL, NULL, NULL, FALSE} +}; + +enum { + SEARCH_CONSTRAINT_CONTAINS_THE_TEXT, + SEARCH_CONSTRAINT_TYPE_SEPARATOR_00, + SEARCH_CONSTRAINT_DATE_MODIFIED_BEFORE, + SEARCH_CONSTRAINT_DATE_MODIFIED_AFTER, + SEARCH_CONSTRAINT_TYPE_SEPARATOR_01, + SEARCH_CONSTRAINT_SIZE_IS_MORE_THAN, + SEARCH_CONSTRAINT_SIZE_IS_LESS_THAN, + SEARCH_CONSTRAINT_FILE_IS_EMPTY, + SEARCH_CONSTRAINT_TYPE_SEPARATOR_02, + SEARCH_CONSTRAINT_OWNED_BY_USER, + SEARCH_CONSTRAINT_OWNED_BY_GROUP, + SEARCH_CONSTRAINT_OWNER_IS_UNRECOGNIZED, + SEARCH_CONSTRAINT_TYPE_SEPARATOR_03, + SEARCH_CONSTRAINT_FILE_IS_NOT_NAMED, + SEARCH_CONSTRAINT_FILE_MATCHES_REGULAR_EXPRESSION, + SEARCH_CONSTRAINT_TYPE_SEPARATOR_04, + SEARCH_CONSTRAINT_SHOW_HIDDEN_FILES_AND_FOLDERS, + SEARCH_CONSTRAINT_FOLLOW_SYMBOLIC_LINKS, + SEARCH_CONSTRAINT_SEARCH_OTHER_FILESYSTEMS, + SEARCH_CONSTRAINT_MAXIMUM_POSSIBLE +}; + +static GtkTargetEntry GSearchDndTable[] = { + { "text/uri-list", 0, 1 }, + { "text/plain", 0, 0 }, + { "STRING", 0, 0 } +}; + +static guint GSearchTotalDnds = sizeof (GSearchDndTable) / sizeof (GSearchDndTable[0]); + +struct _GSearchGOptionArguments { + gchar * name; + gchar * path; + gchar * contains; + gchar * user; + gchar * group; + gboolean nouser; + gchar * mtimeless; + gchar * mtimemore; + gchar * sizeless; + gchar * sizemore; + gboolean empty; + gchar * notnamed; + gchar * regex; + gboolean hidden; + gboolean follow; + gboolean mounts; + gchar * sortby; + gboolean descending; + gboolean start; +} GSearchGOptionArguments; + +static GOptionEntry GSearchGOptionEntries[] = { + { "version", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, version_cb, N_("Show version of the application"), NULL}, + { "named", 0, 0, G_OPTION_ARG_STRING, &GSearchGOptionArguments.name, NULL, N_("STRING") }, + { "path", 0, 0, G_OPTION_ARG_STRING, &GSearchGOptionArguments.path, NULL, N_("PATH") }, + { "sortby", 0, 0, G_OPTION_ARG_STRING, &GSearchGOptionArguments.sortby, NULL, N_("VALUE") }, + { "descending", 0, 0, G_OPTION_ARG_NONE, &GSearchGOptionArguments.descending, NULL, NULL }, + { "start", 0, 0, G_OPTION_ARG_NONE, &GSearchGOptionArguments.start, NULL, NULL }, + { "contains", 0, 0, G_OPTION_ARG_STRING, &GSearchGOptionArguments.contains, NULL, N_("STRING") }, + { "mtimeless", 0, 0, G_OPTION_ARG_STRING, &GSearchGOptionArguments.mtimeless, NULL, N_("DAYS") }, + { "mtimemore", 0, 0, G_OPTION_ARG_STRING, &GSearchGOptionArguments.mtimemore, NULL, N_("DAYS") }, + { "sizemore", 0, 0, G_OPTION_ARG_STRING, &GSearchGOptionArguments.sizemore, NULL, N_("KILOBYTES") }, + { "sizeless", 0, 0, G_OPTION_ARG_STRING, &GSearchGOptionArguments.sizeless, NULL, N_("KILOBYTES") }, + { "empty", 0, 0, G_OPTION_ARG_NONE, &GSearchGOptionArguments.empty, NULL, NULL }, + { "user", 0, 0, G_OPTION_ARG_STRING, &GSearchGOptionArguments.user, NULL, N_("USER") }, + { "group", 0, 0, G_OPTION_ARG_STRING, &GSearchGOptionArguments.group, NULL, N_("GROUP") }, + { "nouser", 0, 0, G_OPTION_ARG_NONE, &GSearchGOptionArguments.nouser, NULL, NULL }, + { "notnamed", 0, 0, G_OPTION_ARG_STRING, &GSearchGOptionArguments.notnamed, NULL, N_("STRING") }, + { "regex", 0, 0, G_OPTION_ARG_STRING, &GSearchGOptionArguments.regex, NULL, N_("PATTERN") }, + { "hidden", 0, 0, G_OPTION_ARG_NONE, &GSearchGOptionArguments.hidden, NULL, NULL }, + { "follow", 0, 0, G_OPTION_ARG_NONE, &GSearchGOptionArguments.follow, NULL, NULL }, + { "mounts", 0, 0, G_OPTION_ARG_NONE, &GSearchGOptionArguments.mounts, NULL, NULL }, + { NULL } +}; + +static gchar * find_command_default_name_argument; +static gchar * locate_command_default_options; +pid_t locate_database_check_command_pid; + +static gboolean +handle_locate_command_stdout_io (GIOChannel * ioc, + GIOCondition condition, + gpointer data) +{ + GSearchWindow * gsearch = data; + gboolean broken_pipe = FALSE; + + if (condition & G_IO_IN) { + + GError * error = NULL; + GString * string; + + string = g_string_new (NULL); + + while (ioc->is_readable != TRUE); + + do { + gint status; + + do { + status = g_io_channel_read_line_string (ioc, string, NULL, &error); + + if (status == G_IO_STATUS_EOF) { + broken_pipe = TRUE; + } + else if (status == G_IO_STATUS_AGAIN) { + if (gtk_events_pending ()) { + while (gtk_events_pending ()) { + gtk_main_iteration (); + } + } + } + else if ((string->len != 0) && (strncmp (string->str, "/", 1) == 0)) { + gsearch->is_locate_database_available = TRUE; + broken_pipe = TRUE; + } + + } while (status == G_IO_STATUS_AGAIN && broken_pipe == FALSE); + + if (broken_pipe == TRUE) { + break; + } + + if (status != G_IO_STATUS_NORMAL) { + if (error != NULL) { + g_warning ("handle_locate_command_stdout_io(): %s", error->message); + g_error_free (error); + } + } + + } while (g_io_channel_get_buffer_condition (ioc) & G_IO_IN); + + waitpid (locate_database_check_command_pid, NULL, 0); + g_string_free (string, TRUE); + } + + if (!(condition & G_IO_IN) || broken_pipe == TRUE) { + gsearch->is_locate_database_check_finished = TRUE; + g_io_channel_shutdown (ioc, TRUE, NULL); + return FALSE; + } + return TRUE; +} + +static void +setup_case_insensitive_arguments (GSearchWindow * gsearch) +{ + static gboolean case_insensitive_arguments_initialized = FALSE; + gchar * cmd_stderr; + gchar * grep_cmd; + gchar * locate; + + if (case_insensitive_arguments_initialized == TRUE) { + return; + } + case_insensitive_arguments_initialized = TRUE; + + /* check find command for -iname argument compatibility */ + g_spawn_command_line_sync ("find /dev/null -iname 'string'", NULL, &cmd_stderr, NULL, NULL); + + if ((cmd_stderr != NULL) && (strlen (cmd_stderr) == 0)) { + find_command_default_name_argument = g_strdup ("-iname"); + GSearchOptionTemplates[SEARCH_CONSTRAINT_FILE_IS_NOT_NAMED].option = g_strdup ("'!' -iname '*%s*'"); + } + else { + find_command_default_name_argument = g_strdup ("-name"); + } + g_free (cmd_stderr); + + /* check grep command for -i argument compatibility */ + grep_cmd = g_strdup_printf ("%s -i 'string' /dev/null", GREP_COMMAND); + g_spawn_command_line_sync (grep_cmd, NULL, &cmd_stderr, NULL, NULL); + + if ((cmd_stderr != NULL) && (strlen (cmd_stderr) == 0)) { + g_free (cmd_stderr); + g_free (grep_cmd); + + /* check grep command for -I argument compatibility, bug 568840 */ + grep_cmd = g_strdup_printf ("%s -i -I 'string' /dev/null", GREP_COMMAND); + g_spawn_command_line_sync (grep_cmd, NULL, &cmd_stderr, NULL, NULL); + + if ((cmd_stderr != NULL) && (strlen (cmd_stderr) == 0)) { + GSearchOptionTemplates[SEARCH_CONSTRAINT_CONTAINS_THE_TEXT].option = + g_strdup_printf ("'!' -type p -exec %s -i -I -c '%%s' {} \\;", GREP_COMMAND); + } + else { + GSearchOptionTemplates[SEARCH_CONSTRAINT_CONTAINS_THE_TEXT].option = + g_strdup_printf ("'!' -type p -exec %s -i -c '%%s' {} \\;", GREP_COMMAND); + } + } + else { + GSearchOptionTemplates[SEARCH_CONSTRAINT_CONTAINS_THE_TEXT].option = + g_strdup_printf ("'!' -type p -exec %s -c '%%s' {} \\;", GREP_COMMAND); + } + g_free (cmd_stderr); + + locate = g_find_program_in_path ("locate"); + + if (locate != NULL) { + GIOChannel * ioc_stdout; + gchar ** argv = NULL; + gchar *command = NULL; + gint child_stdout; + + /* check locate command for -i argument compatibility */ + command = g_strconcat (locate, " -i /", NULL); + g_shell_parse_argv (command, NULL, &argv, NULL); + g_free (command); + + gsearch->is_locate_database_check_finished = FALSE; + gsearch->is_locate_database_available = FALSE; + + /* run locate command asynchronously because on some systems it can be slow */ + if (g_spawn_async_with_pipes (g_get_home_dir (), argv, NULL, + G_SPAWN_SEARCH_PATH, NULL, NULL, + &locate_database_check_command_pid, NULL, &child_stdout, + NULL, NULL)) { + + ioc_stdout = g_io_channel_unix_new (child_stdout); + g_io_channel_set_encoding (ioc_stdout, NULL, NULL); + g_io_channel_set_flags (ioc_stdout, G_IO_FLAG_NONBLOCK, NULL); + g_io_add_watch (ioc_stdout, G_IO_IN | G_IO_HUP, + handle_locate_command_stdout_io, gsearch); + g_io_channel_unref (ioc_stdout); + } + else { + gsearch->is_locate_database_check_finished = TRUE; + } + + g_strfreev (argv); + + while (gsearch->is_locate_database_check_finished == FALSE) { + if (gtk_events_pending ()) { + while (gtk_events_pending ()) { + gtk_main_iteration (); + } + } + } + + if (gsearch->is_locate_database_available == TRUE) { + locate_command_default_options = g_strdup ("-i"); + } + else { + /* run locate again to check if it can find anything */ + command = g_strconcat (locate, " /", NULL); + g_shell_parse_argv (command, NULL, &argv, NULL); + g_free (command); + + gsearch->is_locate_database_check_finished = FALSE; + locate_command_default_options = g_strdup (""); + + /* run locate command asynchronously because on some systems it can be slow */ + if (g_spawn_async_with_pipes (g_get_home_dir (), argv, NULL, + G_SPAWN_SEARCH_PATH, NULL, NULL, + &locate_database_check_command_pid, NULL, &child_stdout, + NULL, NULL)) { + + ioc_stdout = g_io_channel_unix_new (child_stdout); + g_io_channel_set_encoding (ioc_stdout, NULL, NULL); + g_io_channel_set_flags (ioc_stdout, G_IO_FLAG_NONBLOCK, NULL); + g_io_add_watch (ioc_stdout, G_IO_IN | G_IO_HUP, + handle_locate_command_stdout_io, gsearch); + g_io_channel_unref (ioc_stdout); + } + else { + gsearch->is_locate_database_check_finished = TRUE; + } + + g_strfreev (argv); + + while (gsearch->is_locate_database_check_finished == FALSE) { + if (gtk_events_pending ()) { + while (gtk_events_pending ()) { + gtk_main_iteration (); + } + } + } + + if (gsearch->is_locate_database_available == FALSE) { + g_warning (_("A locate database has probably not been created.")); + } + } + } + else { + /* locate is not installed */ + locate_command_default_options = g_strdup (""); + gsearch->is_locate_database_available = FALSE; + } + g_free (grep_cmd); + g_free (locate); +} + +static gchar * +setup_find_name_options (gchar * file) +{ + /* This function builds the name options for the find command. This in + done to insure that the find command returns hidden files and folders. */ + + GString * command; + command = g_string_new (""); + + if (strstr (file, "*") == NULL) { + + if ((strlen (file) == 0) || (file[0] != '.')) { + g_string_append_printf (command, "\\( %s \"*%s*\" -o %s \".*%s*\" \\) ", + find_command_default_name_argument, file, + find_command_default_name_argument, file); + } + else { + g_string_append_printf (command, "\\( %s \"*%s*\" -o %s \".*%s*\" -o %s \"%s*\" \\) ", + find_command_default_name_argument, file, + find_command_default_name_argument, file, + find_command_default_name_argument, file); + } + } + else { + if (file[0] == '.') { + g_string_append_printf (command, "\\( %s \"%s\" -o %s \".*%s\" \\) ", + find_command_default_name_argument, file, + find_command_default_name_argument, file); + } + else if (file[0] != '*') { + g_string_append_printf (command, "%s \"%s\" ", + find_command_default_name_argument, file); + } + else { + if ((strlen (file) >= 1) && (file[1] == '.')) { + g_string_append_printf (command, "\\( %s \"%s\" -o %s \"%s\" \\) ", + find_command_default_name_argument, file, + find_command_default_name_argument, &file[1]); + } + else { + g_string_append_printf (command, "\\( %s \"%s\" -o %s \".%s\" \\) ", + find_command_default_name_argument, file, + find_command_default_name_argument, file); + } + } + } + return g_string_free (command, FALSE); +} + +static gboolean +has_additional_constraints (GSearchWindow * gsearch) +{ + GList * list; + + if (gsearch->available_options_selected_list != NULL) { + + for (list = gsearch->available_options_selected_list; list != NULL; list = g_list_next (list)) { + + GSearchConstraint * constraint = list->data; + + switch (GSearchOptionTemplates[constraint->constraint_id].type) { + case SEARCH_CONSTRAINT_TYPE_BOOLEAN: + case SEARCH_CONSTRAINT_TYPE_NUMERIC: + case SEARCH_CONSTRAINT_TYPE_DATE_BEFORE: + case SEARCH_CONSTRAINT_TYPE_DATE_AFTER: + return TRUE; + case SEARCH_CONSTRAINT_TYPE_TEXT: + if (strlen (constraint->data.text) > 0) { + return TRUE; + } + default: + break; + } + } + } + return FALSE; +} + +static void +display_dialog_character_set_conversion_error (GtkWidget * window, + gchar * string, + GError * error) +{ + GtkWidget * dialog; + + dialog = gtk_message_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _("Character set conversion failed for \"%s\""), + string); + + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + (error == NULL) ? " " : error->message, NULL); + + gtk_window_set_title (GTK_WINDOW (dialog), ""); + gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); + gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 14); + + g_signal_connect (G_OBJECT (dialog), + "response", + G_CALLBACK (gtk_widget_destroy), NULL); + + gtk_widget_show (dialog); +} + +static void +start_animation (GSearchWindow * gsearch, gboolean first_pass) +{ + if (first_pass == TRUE) { + + gchar *title = NULL; + + title = g_strconcat (_("Searching..."), " - ", _("Search for Files"), NULL); + gtk_window_set_title (GTK_WINDOW (gsearch->window), title); + + gtk_label_set_text (GTK_LABEL (gsearch->files_found_label), ""); + if (g_settings_get_boolean (gsearch->mate_desktop_interface_settings, "enable-animations")) { + gtk_spinner_start (GTK_SPINNER (gsearch->progress_spinner)); + gtk_widget_show (gsearch->progress_spinner); + } + g_free (title); + + gsearch->focus = gtk_window_get_focus (GTK_WINDOW (gsearch->window)); + + gtk_window_set_default (GTK_WINDOW (gsearch->window), gsearch->stop_button); + gtk_widget_show (gsearch->stop_button); + gtk_widget_set_sensitive (gsearch->stop_button, TRUE); + gtk_widget_hide (gsearch->find_button); + gtk_widget_set_sensitive (gsearch->find_button, FALSE); + gtk_widget_set_sensitive (gsearch->search_results_vbox, TRUE); + gtk_widget_set_sensitive (GTK_WIDGET (gsearch->search_results_tree_view), TRUE); + gtk_widget_set_sensitive (gsearch->available_options_vbox, FALSE); + gtk_widget_set_sensitive (gsearch->show_more_options_expander, FALSE); + gtk_widget_set_sensitive (gsearch->name_and_folder_table, FALSE); + } +} + +static void +stop_animation (GSearchWindow * gsearch) +{ + gtk_spinner_stop (GTK_SPINNER (gsearch->progress_spinner)); + + gtk_window_set_default (GTK_WINDOW (gsearch->window), gsearch->find_button); + gtk_widget_set_sensitive (gsearch->available_options_vbox, TRUE); + gtk_widget_set_sensitive (gsearch->show_more_options_expander, TRUE); + gtk_widget_set_sensitive (gsearch->name_and_folder_table, TRUE); + gtk_widget_set_sensitive (gsearch->find_button, TRUE); + gtk_widget_hide (gsearch->progress_spinner); + gtk_widget_hide (gsearch->stop_button); + gtk_widget_show (gsearch->find_button); + + if (GTK_IS_MENU_ITEM (gsearch->search_results_save_results_as_item) == TRUE) { + gtk_widget_set_sensitive (gsearch->search_results_save_results_as_item, TRUE); + } + + if (gtk_window_get_focus (GTK_WINDOW (gsearch->window)) == NULL) { + gtk_window_set_focus (GTK_WINDOW (gsearch->window), gsearch->focus); + } +} + +gchar * +build_search_command (GSearchWindow * gsearch, + gboolean first_pass) +{ + GString * command; + GError * error = NULL; + gchar * file_is_named_utf8; + gchar * file_is_named_locale; + gchar * file_is_named_escaped; + gchar * file_is_named_backslashed; + gchar * look_in_folder_locale; + gchar * look_in_folder_escaped; + gchar * look_in_folder_backslashed; + + start_animation (gsearch, first_pass); + setup_case_insensitive_arguments (gsearch); + + file_is_named_utf8 = g_strdup ((gchar *) gtk_entry_get_text (GTK_ENTRY (gsearch_history_entry_get_entry + (GSEARCH_HISTORY_ENTRY (gsearch->name_contains_entry))))); + + if (!file_is_named_utf8 || !*file_is_named_utf8) { + g_free (file_is_named_utf8); + file_is_named_utf8 = g_strdup ("*"); + } + else { + gchar * locale; + + locale = g_locale_from_utf8 (file_is_named_utf8, -1, NULL, NULL, &error); + if (locale == NULL) { + stop_animation (gsearch); + display_dialog_character_set_conversion_error (gsearch->window, file_is_named_utf8, error); + g_free (file_is_named_utf8); + g_error_free (error); + return NULL; + } + gsearch_history_entry_prepend_text (GSEARCH_HISTORY_ENTRY (gsearch->name_contains_entry), file_is_named_utf8); + + if ((strstr (locale, "*") == NULL) && (strstr (locale, "?") == NULL)) { + gchar *tmp; + + tmp = file_is_named_utf8; + file_is_named_utf8 = g_strconcat ("*", file_is_named_utf8, "*", NULL); + g_free (tmp); + } + + g_free (locale); + } + + file_is_named_locale = g_locale_from_utf8 (file_is_named_utf8, -1, NULL, NULL, &error); + if (file_is_named_locale == NULL) { + stop_animation (gsearch); + display_dialog_character_set_conversion_error (gsearch->window, file_is_named_utf8, error); + g_free (file_is_named_utf8); + g_error_free (error); + return NULL; + } + + look_in_folder_locale = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (gsearch->look_in_folder_button)); + + if (look_in_folder_locale == NULL) { + /* If for some reason a path was not returned fallback to the user's home directory. */ + look_in_folder_locale = g_strdup (g_get_home_dir ()); + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (gsearch->look_in_folder_button), look_in_folder_locale); + } + + if (!g_str_has_suffix (look_in_folder_locale, G_DIR_SEPARATOR_S)) { + gchar *tmp; + + tmp = look_in_folder_locale; + look_in_folder_locale = g_strconcat (look_in_folder_locale, G_DIR_SEPARATOR_S, NULL); + g_free (tmp); + } + g_free (gsearch->command_details->look_in_folder_string); + + look_in_folder_backslashed = backslash_backslash_characters (look_in_folder_locale); + look_in_folder_escaped = escape_double_quotes (look_in_folder_backslashed); + gsearch->command_details->look_in_folder_string = g_strdup (look_in_folder_locale); + + command = g_string_new (""); + gsearch->command_details->is_command_show_hidden_files_enabled = FALSE; + gsearch->command_details->name_contains_regex_string = NULL; + gsearch->command_details->name_contains_pattern_string = NULL; + + gsearch->command_details->is_command_first_pass = first_pass; + if (gsearch->command_details->is_command_first_pass == TRUE) { + gsearch->command_details->is_command_using_quick_mode = FALSE; + } + + if ((gtk_widget_get_visible (gsearch->available_options_vbox) == FALSE) || + (has_additional_constraints (gsearch) == FALSE)) { + + file_is_named_backslashed = backslash_backslash_characters (file_is_named_locale); + file_is_named_escaped = escape_double_quotes (file_is_named_backslashed); + gsearch->command_details->name_contains_pattern_string = g_strdup (file_is_named_utf8); + + if (gsearch->command_details->is_command_first_pass == TRUE) { + + gchar * locate; + CajaSpeedTradeoff show_thumbnails_enum; + gboolean disable_quick_search; + + locate = g_find_program_in_path ("locate"); + disable_quick_search = g_settings_get_boolean (gsearch->mate_search_tool_settings, "disable-quick-search"); + gsearch->command_details->is_command_second_pass_enabled = !g_settings_get_boolean (gsearch->mate_search_tool_settings, "disable-quick-search-second-scan"); + + /* Use caja settings for thumbnails if caja is installed, else fall back to the caja default */ + if (gsearch->caja_schema_exists) { + show_thumbnails_enum = g_settings_get_enum (gsearch->caja_settings, "show-image-thumbnails"); + } else { + show_thumbnails_enum = SPEED_TRADEOFF_LOCAL_ONLY; + } + + if (show_thumbnails_enum == SPEED_TRADEOFF_ALWAYS || + show_thumbnails_enum == SPEED_TRADEOFF_LOCAL_ONLY) { + GVariant * value; + guint64 size_limit = 10485760; + + if (gsearch->caja_schema_exists) { + value = g_settings_get_value (gsearch->caja_settings, "thumbnail-limit"); + if (value) { + size_limit = g_variant_get_uint64 (value); + g_variant_unref (value); + } + } + + gsearch->show_thumbnails = TRUE; + gsearch->show_thumbnails_file_size_limit = size_limit; + } + else { + gsearch->show_thumbnails = FALSE; + gsearch->show_thumbnails_file_size_limit = 0; + } + + if ((disable_quick_search == FALSE) + && (gsearch->is_locate_database_available == TRUE) + && (locate != NULL) + && (is_quick_search_excluded_path (look_in_folder_locale) == FALSE)) { + + g_string_append_printf (command, "%s %s \"%s*%s\"", + locate, + locate_command_default_options, + look_in_folder_escaped, + file_is_named_escaped); + gsearch->command_details->is_command_using_quick_mode = TRUE; + } + else { + g_string_append_printf (command, "find \"%s\" %s \"%s\" -print", + look_in_folder_escaped, + find_command_default_name_argument, + file_is_named_escaped); + } + g_free (locate); + } + else { + g_string_append_printf (command, "find \"%s\" %s \"%s\" -print", + look_in_folder_escaped, + find_command_default_name_argument, + file_is_named_escaped); + } + } + else { + GList * list; + gboolean disable_mount_argument = TRUE; + + gsearch->command_details->is_command_regex_matching_enabled = FALSE; + file_is_named_backslashed = backslash_backslash_characters (file_is_named_locale); + file_is_named_escaped = escape_double_quotes (file_is_named_backslashed); + + g_string_append_printf (command, "find \"%s\" %s", + look_in_folder_escaped, + setup_find_name_options (file_is_named_escaped)); + + for (list = gsearch->available_options_selected_list; list != NULL; list = g_list_next (list)) { + + GSearchConstraint * constraint = list->data; + + switch (GSearchOptionTemplates[constraint->constraint_id].type) { + case SEARCH_CONSTRAINT_TYPE_BOOLEAN: + if (strcmp (GSearchOptionTemplates[constraint->constraint_id].option, "EXCLUDE_OTHER_FILESYSTEMS") == 0) { + disable_mount_argument = FALSE; + } + else if (strcmp (GSearchOptionTemplates[constraint->constraint_id].option, "SHOW_HIDDEN_FILES") == 0) { + gsearch->command_details->is_command_show_hidden_files_enabled = TRUE; + } + else { + g_string_append_printf (command, "%s ", + GSearchOptionTemplates[constraint->constraint_id].option); + } + break; + case SEARCH_CONSTRAINT_TYPE_TEXT: + if (strcmp (GSearchOptionTemplates[constraint->constraint_id].option, "-regex '%s'") == 0) { + + gchar * escaped; + gchar * regex; + + escaped = backslash_special_characters (constraint->data.text); + regex = escape_single_quotes (escaped); + + if (regex != NULL) { + gsearch->command_details->is_command_regex_matching_enabled = TRUE; + gsearch->command_details->name_contains_regex_string = g_locale_from_utf8 (regex, -1, NULL, NULL, NULL); + } + + g_free (escaped); + g_free (regex); + } + else { + gchar * escaped; + gchar * backslashed; + gchar * locale; + + backslashed = backslash_special_characters (constraint->data.text); + escaped = escape_single_quotes (backslashed); + + locale = g_locale_from_utf8 (escaped, -1, NULL, NULL, NULL); + + if (strlen (locale) != 0) { + g_string_append_printf (command, + GSearchOptionTemplates[constraint->constraint_id].option, + locale); + + g_string_append_c (command, ' '); + } + + g_free (escaped); + g_free (backslashed); + g_free (locale); + } + break; + case SEARCH_CONSTRAINT_TYPE_NUMERIC: + g_string_append_printf (command, + GSearchOptionTemplates[constraint->constraint_id].option, + (constraint->data.number * 1024), + (constraint->data.number * 1024)); + g_string_append_c (command, ' '); + break; + case SEARCH_CONSTRAINT_TYPE_DATE_BEFORE: + g_string_append_printf (command, + GSearchOptionTemplates[constraint->constraint_id].option, + constraint->data.time); + g_string_append_c (command, ' '); + break; + case SEARCH_CONSTRAINT_TYPE_DATE_AFTER: + g_string_append_printf (command, + GSearchOptionTemplates[constraint->constraint_id].option, + constraint->data.time, + constraint->data.time); + g_string_append_c (command, ' '); + break; + default: + break; + } + } + gsearch->command_details->name_contains_pattern_string = g_strdup ("*"); + + if (disable_mount_argument != TRUE) { + g_string_append (command, "-xdev "); + } + + g_string_append (command, "-print "); + } + g_free (file_is_named_locale); + g_free (file_is_named_utf8); + g_free (file_is_named_backslashed); + g_free (file_is_named_escaped); + g_free (look_in_folder_locale); + g_free (look_in_folder_backslashed); + g_free (look_in_folder_escaped); + + return g_string_free (command, FALSE); +} + +static void +add_file_to_search_results (const gchar * file, + GtkListStore * store, + GtkTreeIter * iter, + GSearchWindow * gsearch) +{ + GdkPixbuf * pixbuf; + GSearchMonitor * monitor; + GFileMonitor * handle; + GFileInfo * file_info; + GFile * g_file; + GError * error = NULL; + GTimeVal time_val; + GtkTreePath * path; + GtkTreeRowReference * reference; + gchar * description; + gchar * readable_size; + gchar * readable_date; + gchar * utf8_base_name; + gchar * utf8_relative_dir_name; + gchar * base_name; + gchar * dir_name; + gchar * relative_dir_name; + gchar * look_in_folder; + + if (g_hash_table_lookup_extended (gsearch->search_results_filename_hash_table, file, NULL, NULL) == TRUE) { + return; + } + + if ((g_file_test (file, G_FILE_TEST_EXISTS) != TRUE) && + (g_file_test (file, G_FILE_TEST_IS_SYMLINK) != TRUE)) { + return; + } + + g_hash_table_insert (gsearch->search_results_filename_hash_table, g_strdup (file), NULL); + + if (gtk_tree_view_get_headers_visible (GTK_TREE_VIEW (gsearch->search_results_tree_view)) == FALSE) { + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (gsearch->search_results_tree_view), TRUE); + } + + g_file = g_file_new_for_path (file); + file_info = g_file_query_info (g_file, "standard::*,time::modified,thumbnail::path", 0, NULL, NULL); + + pixbuf = get_file_pixbuf (gsearch, file_info); + description = get_file_type_description (file, file_info); + readable_size = g_format_size (g_file_info_get_size (file_info)); + + g_file_info_get_modification_time (file_info, &time_val); + readable_date = get_readable_date (gsearch->search_results_date_format, time_val.tv_sec); + + base_name = g_path_get_basename (file); + dir_name = g_path_get_dirname (file); + + look_in_folder = g_strdup (gsearch->command_details->look_in_folder_string); + if (strlen (look_in_folder) > 1) { + gchar * path_str; + + if (g_str_has_suffix (look_in_folder, G_DIR_SEPARATOR_S) == TRUE) { + look_in_folder[strlen (look_in_folder) - 1] = '\0'; + } + path_str = g_path_get_dirname (look_in_folder); + if (strcmp (path_str, G_DIR_SEPARATOR_S) == 0) { + relative_dir_name = g_strconcat (&dir_name[strlen (path_str)], NULL); + } + else { + relative_dir_name = g_strconcat (&dir_name[strlen (path_str) + 1], NULL); + } + g_free (path_str); + } + else { + relative_dir_name = g_strdup (dir_name); + } + + utf8_base_name = g_filename_display_basename (file); + utf8_relative_dir_name = g_filename_display_name (relative_dir_name); + + gtk_list_store_append (GTK_LIST_STORE (store), iter); + gtk_list_store_set (GTK_LIST_STORE (store), iter, + COLUMN_ICON, pixbuf, + COLUMN_NAME, utf8_base_name, + COLUMN_RELATIVE_PATH, utf8_relative_dir_name, + COLUMN_LOCALE_FILE, file, + COLUMN_READABLE_SIZE, readable_size, + COLUMN_SIZE, (-1) * (gdouble) g_file_info_get_size(file_info), + COLUMN_TYPE, (description != NULL) ? description : g_strdup (g_file_info_get_content_type (file_info)), + COLUMN_READABLE_DATE, readable_date, + COLUMN_DATE, (-1) * (gdouble) time_val.tv_sec, + COLUMN_NO_FILES_FOUND, FALSE, + -1); + + monitor = g_slice_new0 (GSearchMonitor); + if (monitor) { + path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), iter); + reference = gtk_tree_row_reference_new (GTK_TREE_MODEL (store), path); + gtk_tree_path_free (path); + + handle = g_file_monitor_file (g_file, G_FILE_MONITOR_EVENT_DELETED, NULL, &error); + + if (error == NULL) { + monitor->gsearch = gsearch; + monitor->reference = reference; + monitor->handle = handle; + gtk_list_store_set (GTK_LIST_STORE (store), iter, + COLUMN_MONITOR, monitor, -1); + + g_signal_connect (handle, "changed", + G_CALLBACK (file_changed_cb), monitor); + } + else { + gtk_tree_row_reference_free (reference); + g_slice_free (GSearchMonitor, monitor); + g_clear_error (&error); + } + } + + g_object_unref (g_file); + g_object_unref (file_info); + g_free (base_name); + g_free (dir_name); + g_free (relative_dir_name); + g_free (utf8_base_name); + g_free (utf8_relative_dir_name); + g_free (look_in_folder); + g_free (description); + g_free (readable_size); + g_free (readable_date); +} + +static void +add_no_files_found_message (GSearchWindow * gsearch) +{ + /* When the list is empty append a 'No Files Found.' message. */ + gtk_widget_set_sensitive (GTK_WIDGET (gsearch->search_results_tree_view), FALSE); + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (gsearch->search_results_tree_view), FALSE); + gtk_tree_view_column_set_visible (gsearch->search_results_folder_column, FALSE); + gtk_tree_view_column_set_visible (gsearch->search_results_size_column, FALSE); + gtk_tree_view_column_set_visible (gsearch->search_results_type_column, FALSE); + gtk_tree_view_column_set_visible (gsearch->search_results_date_column, FALSE); + gtk_tree_view_columns_autosize (GTK_TREE_VIEW (gsearch->search_results_tree_view)); + g_object_set (gsearch->search_results_name_cell_renderer, + "underline", PANGO_UNDERLINE_NONE, + "underline-set", FALSE, + NULL); + gtk_list_store_append (GTK_LIST_STORE (gsearch->search_results_list_store), &gsearch->search_results_iter); + gtk_list_store_set (GTK_LIST_STORE (gsearch->search_results_list_store), &gsearch->search_results_iter, + COLUMN_ICON, NULL, + COLUMN_NAME, _("No files found"), + COLUMN_RELATIVE_PATH, "", + COLUMN_LOCALE_FILE, "", + COLUMN_READABLE_SIZE, "", + COLUMN_SIZE, (gdouble) 0, + COLUMN_TYPE, "", + COLUMN_READABLE_DATE, "", + COLUMN_DATE, (gdouble) 0, + COLUMN_NO_FILES_FOUND, TRUE, + -1); +} + +void +update_search_counts (GSearchWindow * gsearch) +{ + gchar * title_bar_string = NULL; + gchar * message_string = NULL; + gchar * stopped_string = NULL; + gchar * tmp; + gint total_files; + + if (gsearch->command_details->command_status == ABORTED) { + stopped_string = g_strdup (_("(stopped)")); + } + + total_files = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (gsearch->search_results_list_store), NULL); + + if (total_files == 0) { + title_bar_string = g_strdup (_("No Files Found")); + message_string = g_strdup (_("No files found")); + add_no_files_found_message (gsearch); + } + else { + title_bar_string = g_strdup_printf (ngettext ("%'d File Found", + "%'d Files Found", + total_files), + total_files); + message_string = g_strdup_printf (ngettext ("%'d file found", + "%'d files found", + total_files), + total_files); + } + + if (stopped_string != NULL) { + tmp = message_string; + message_string = g_strconcat (message_string, " ", stopped_string, NULL); + g_free (tmp); + + tmp = title_bar_string; + title_bar_string = g_strconcat (title_bar_string, " ", stopped_string, NULL); + g_free (tmp); + } + + tmp = title_bar_string; + title_bar_string = g_strconcat (title_bar_string, " - ", _("Search for Files"), NULL); + gtk_window_set_title (GTK_WINDOW (gsearch->window), title_bar_string); + g_free (tmp); + + gtk_label_set_text (GTK_LABEL (gsearch->files_found_label), message_string); + + g_free (title_bar_string); + g_free (message_string); + g_free (stopped_string); +} + +static void +intermediate_file_count_update (GSearchWindow * gsearch) +{ + gchar * string; + gint count; + + count = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (gsearch->search_results_list_store), NULL); + + if (count > 0) { + + string = g_strdup_printf (ngettext ("%'d file found", + "%'d files found", + count), + count); + + gtk_label_set_text (GTK_LABEL (gsearch->files_found_label), string); + g_free (string); + } +} + +gboolean +tree_model_iter_free_monitor (GtkTreeModel * model, + GtkTreePath * path, + GtkTreeIter * iter, + gpointer data) +{ + GSearchMonitor * monitor; + + g_return_val_if_fail (GTK_IS_TREE_MODEL (model), FALSE); + + gtk_tree_model_get (model, iter, COLUMN_MONITOR, &monitor, -1); + if (monitor) { + g_file_monitor_cancel (monitor->handle); + gtk_tree_row_reference_free (monitor->reference); + g_slice_free (GSearchMonitor, monitor); + } + return FALSE; +} + +static GtkTreeModel * +gsearch_create_list_of_templates (void) +{ + GtkListStore * store; + GtkTreeIter iter; + gint idx; + + store = gtk_list_store_new (1, G_TYPE_STRING); + + for (idx = 0; GSearchOptionTemplates[idx].type != SEARCH_CONSTRAINT_TYPE_NONE; idx++) { + + if (GSearchOptionTemplates[idx].type == SEARCH_CONSTRAINT_TYPE_SEPARATOR) { + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, 0, "separator", -1); + } + else { + gchar * text = remove_mnemonic_character (_(GSearchOptionTemplates[idx].desc)); + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, 0, text, -1); + g_free (text); + } + } + return GTK_TREE_MODEL (store); +} + +static void +set_constraint_info_defaults (GSearchConstraint * opt) +{ + switch (GSearchOptionTemplates[opt->constraint_id].type) { + case SEARCH_CONSTRAINT_TYPE_BOOLEAN: + break; + case SEARCH_CONSTRAINT_TYPE_TEXT: + opt->data.text = ""; + break; + case SEARCH_CONSTRAINT_TYPE_NUMERIC: + opt->data.number = 0; + break; + case SEARCH_CONSTRAINT_TYPE_DATE_BEFORE: + case SEARCH_CONSTRAINT_TYPE_DATE_AFTER: + opt->data.time = 0; + break; + default: + break; + } +} + +void +update_constraint_info (GSearchConstraint * constraint, + gchar * info) +{ + switch (GSearchOptionTemplates[constraint->constraint_id].type) { + case SEARCH_CONSTRAINT_TYPE_TEXT: + constraint->data.text = info; + break; + case SEARCH_CONSTRAINT_TYPE_NUMERIC: + sscanf (info, "%d", &constraint->data.number); + break; + case SEARCH_CONSTRAINT_TYPE_DATE_BEFORE: + case SEARCH_CONSTRAINT_TYPE_DATE_AFTER: + sscanf (info, "%d", &constraint->data.time); + break; + default: + g_warning (_("Entry changed called for a non entry option!")); + break; + } +} + +void +set_constraint_selected_state (GSearchWindow * gsearch, + gint constraint_id, + gboolean state) +{ + gint idx; + + GSearchOptionTemplates[constraint_id].is_selected = state; + + for (idx = 0; GSearchOptionTemplates[idx].type != SEARCH_CONSTRAINT_TYPE_NONE; idx++) { + if (GSearchOptionTemplates[idx].is_selected == FALSE) { + gtk_combo_box_set_active (GTK_COMBO_BOX (gsearch->available_options_combo_box), idx); + gtk_widget_set_sensitive (gsearch->available_options_add_button, TRUE); + gtk_widget_set_sensitive (gsearch->available_options_combo_box, TRUE); + gtk_widget_set_sensitive (gsearch->available_options_label, TRUE); + return; + } + } + gtk_widget_set_sensitive (gsearch->available_options_add_button, FALSE); + gtk_widget_set_sensitive (gsearch->available_options_combo_box, FALSE); + gtk_widget_set_sensitive (gsearch->available_options_label, FALSE); +} + +void +set_constraint_gsettings_boolean (gint constraint_id, + gboolean flag) +{ + GSettings * select_settings; + + select_settings = g_settings_new ("org.mate.search-tool.select"); + + switch (constraint_id) { + + case SEARCH_CONSTRAINT_CONTAINS_THE_TEXT: + g_settings_set_boolean (select_settings, "contains-the-text", + flag); + break; + case SEARCH_CONSTRAINT_DATE_MODIFIED_BEFORE: + g_settings_set_boolean (select_settings, "date-modified-less-than", + flag); + break; + case SEARCH_CONSTRAINT_DATE_MODIFIED_AFTER: + g_settings_set_boolean (select_settings, "date-modified-more-than", + flag); + break; + case SEARCH_CONSTRAINT_SIZE_IS_MORE_THAN: + g_settings_set_boolean (select_settings, "size-at-least", + flag); + break; + case SEARCH_CONSTRAINT_SIZE_IS_LESS_THAN: + g_settings_set_boolean (select_settings, "size-at-most", + flag); + break; + case SEARCH_CONSTRAINT_FILE_IS_EMPTY: + g_settings_set_boolean (select_settings, "file-is-empty", + flag); + break; + case SEARCH_CONSTRAINT_OWNED_BY_USER: + g_settings_set_boolean (select_settings, "owned-by-user", + flag); + break; + case SEARCH_CONSTRAINT_OWNED_BY_GROUP: + g_settings_set_boolean (select_settings, "owned-by-group", + flag); + break; + case SEARCH_CONSTRAINT_OWNER_IS_UNRECOGNIZED: + g_settings_set_boolean (select_settings, "owner-is-unrecognized", + flag); + break; + case SEARCH_CONSTRAINT_FILE_IS_NOT_NAMED: + g_settings_set_boolean (select_settings, "name-does-not-contain", + flag); + break; + case SEARCH_CONSTRAINT_FILE_MATCHES_REGULAR_EXPRESSION: + g_settings_set_boolean (select_settings, "name-matches-regular-expression", + flag); + break; + case SEARCH_CONSTRAINT_SHOW_HIDDEN_FILES_AND_FOLDERS: + g_settings_set_boolean (select_settings, "show-hidden-files-and-folders", + flag); + break; + case SEARCH_CONSTRAINT_FOLLOW_SYMBOLIC_LINKS: + g_settings_set_boolean (select_settings, "follow-symbolic-links", + flag); + break; + case SEARCH_CONSTRAINT_SEARCH_OTHER_FILESYSTEMS: + g_settings_set_boolean (select_settings, "exclude-other-filesystems", + flag); + break; + + default: + break; + } + g_object_unref (select_settings); +} + +/* + * add_atk_namedesc + * @widget : The Gtk Widget for which @name and @desc are added. + * @name : Accessible Name + * @desc : Accessible Description + * Description: This function adds accessible name and description to a + * Gtk widget. + */ + +static void +add_atk_namedesc (GtkWidget * widget, + const gchar * name, + const gchar * desc) +{ + AtkObject * atk_widget; + + g_assert (GTK_IS_WIDGET (widget)); + + atk_widget = gtk_widget_get_accessible (widget); + + if (name != NULL) + atk_object_set_name (atk_widget, name); + if (desc !=NULL) + atk_object_set_description (atk_widget, desc); +} + +/* + * add_atk_relation + * @obj1 : The first widget in the relation @rel_type + * @obj2 : The second widget in the relation @rel_type. + * @rel_type : Relation type which relates @obj1 and @obj2 + * Description: This function establishes Atk Relation between two given + * objects. + */ + +static void +add_atk_relation (GtkWidget * obj1, + GtkWidget * obj2, + AtkRelationType rel_type) +{ + AtkObject * atk_obj1, * atk_obj2; + AtkRelationSet * relation_set; + AtkRelation * relation; + + g_assert (GTK_IS_WIDGET (obj1)); + g_assert (GTK_IS_WIDGET (obj2)); + + atk_obj1 = gtk_widget_get_accessible (obj1); + + atk_obj2 = gtk_widget_get_accessible (obj2); + + relation_set = atk_object_ref_relation_set (atk_obj1); + relation = atk_relation_new (&atk_obj2, 1, rel_type); + atk_relation_set_add (relation_set, relation); + g_object_unref (G_OBJECT (relation)); + +} + +static void +gsearch_setup_goption_descriptions (void) +{ + gint i = 1; + gint j; + + GSearchGOptionEntries[i++].description = g_strdup (_("Set the text of \"Name contains\" search option")); + GSearchGOptionEntries[i++].description = g_strdup (_("Set the text of \"Look in folder\" search option")); + GSearchGOptionEntries[i++].description = g_strdup (_("Sort files by one of the following: name, folder, size, type, or date")); + GSearchGOptionEntries[i++].description = g_strdup (_("Set sort order to descending, the default is ascending")); + GSearchGOptionEntries[i++].description = g_strdup (_("Automatically start a search")); + + for (j = 0; GSearchOptionTemplates[j].type != SEARCH_CONSTRAINT_TYPE_NONE; j++) { + if (GSearchOptionTemplates[j].type != SEARCH_CONSTRAINT_TYPE_SEPARATOR) { + gchar *text = remove_mnemonic_character (_(GSearchOptionTemplates[j].desc)); + if (GSearchOptionTemplates[j].type == SEARCH_CONSTRAINT_TYPE_BOOLEAN) { + GSearchGOptionEntries[i++].description = g_strdup_printf (_("Select the \"%s\" search option"), text); + } + else { + GSearchGOptionEntries[i++].description = g_strdup_printf (_("Select and set the \"%s\" search option"), text); + } + g_free (text); + } + } +} + +static gboolean +handle_goption_args (GSearchWindow * gsearch) +{ + gboolean goption_args_found = FALSE; + gint sort_by; + + if (GSearchGOptionArguments.name != NULL) { + goption_args_found = TRUE; + gtk_entry_set_text (GTK_ENTRY (gsearch_history_entry_get_entry (GSEARCH_HISTORY_ENTRY (gsearch->name_contains_entry))), + g_locale_to_utf8 (GSearchGOptionArguments.name, -1, NULL, NULL, NULL)); + } + if (GSearchGOptionArguments.path != NULL) { + goption_args_found = TRUE; + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (gsearch->look_in_folder_button), + g_locale_to_utf8 (GSearchGOptionArguments.path, -1, NULL, NULL, NULL)); + } + if (GSearchGOptionArguments.contains != NULL) { + goption_args_found = TRUE; + add_constraint (gsearch, SEARCH_CONSTRAINT_CONTAINS_THE_TEXT, + GSearchGOptionArguments.contains, TRUE); + } + if (GSearchGOptionArguments.mtimeless != NULL) { + goption_args_found = TRUE; + add_constraint (gsearch, SEARCH_CONSTRAINT_DATE_MODIFIED_BEFORE, + GSearchGOptionArguments.mtimeless, TRUE); + } + if (GSearchGOptionArguments.mtimemore != NULL) { + goption_args_found = TRUE; + add_constraint (gsearch, SEARCH_CONSTRAINT_DATE_MODIFIED_AFTER, + GSearchGOptionArguments.mtimemore, TRUE); + } + if (GSearchGOptionArguments.sizemore != NULL) { + goption_args_found = TRUE; + add_constraint (gsearch, SEARCH_CONSTRAINT_SIZE_IS_MORE_THAN, + GSearchGOptionArguments.sizemore, TRUE); + } + if (GSearchGOptionArguments.sizeless != NULL) { + goption_args_found = TRUE; + add_constraint (gsearch, SEARCH_CONSTRAINT_SIZE_IS_LESS_THAN, + GSearchGOptionArguments.sizeless, TRUE); + } + if (GSearchGOptionArguments.empty) { + goption_args_found = TRUE; + add_constraint (gsearch, SEARCH_CONSTRAINT_FILE_IS_EMPTY, NULL, TRUE); + } + if (GSearchGOptionArguments.user != NULL) { + goption_args_found = TRUE; + add_constraint (gsearch, SEARCH_CONSTRAINT_OWNED_BY_USER, + GSearchGOptionArguments.user, TRUE); + } + if (GSearchGOptionArguments.group != NULL) { + goption_args_found = TRUE; + add_constraint (gsearch, SEARCH_CONSTRAINT_OWNED_BY_GROUP, + GSearchGOptionArguments.group, TRUE); + } + if (GSearchGOptionArguments.nouser) { + goption_args_found = TRUE; + add_constraint (gsearch, SEARCH_CONSTRAINT_OWNER_IS_UNRECOGNIZED, NULL, TRUE); + } + if (GSearchGOptionArguments.notnamed != NULL) { + goption_args_found = TRUE; + add_constraint (gsearch, SEARCH_CONSTRAINT_FILE_IS_NOT_NAMED, + GSearchGOptionArguments.notnamed, TRUE); + } + if (GSearchGOptionArguments.regex != NULL) { + goption_args_found = TRUE; + add_constraint (gsearch, SEARCH_CONSTRAINT_FILE_MATCHES_REGULAR_EXPRESSION, + GSearchGOptionArguments.regex, TRUE); + } + if (GSearchGOptionArguments.hidden) { + goption_args_found = TRUE; + add_constraint (gsearch, SEARCH_CONSTRAINT_SHOW_HIDDEN_FILES_AND_FOLDERS, NULL, TRUE); + } + if (GSearchGOptionArguments.follow) { + goption_args_found = TRUE; + add_constraint (gsearch, SEARCH_CONSTRAINT_FOLLOW_SYMBOLIC_LINKS, NULL, TRUE); + } + if (GSearchGOptionArguments.mounts) { + goption_args_found = TRUE; + add_constraint (gsearch, SEARCH_CONSTRAINT_SEARCH_OTHER_FILESYSTEMS, NULL, TRUE); + } + if (GSearchGOptionArguments.sortby != NULL) { + + goption_args_found = TRUE; + if (strcmp (GSearchGOptionArguments.sortby, "name") == 0) { + sort_by = COLUMN_NAME; + } + else if (strcmp (GSearchGOptionArguments.sortby, "folder") == 0) { + sort_by = COLUMN_RELATIVE_PATH; + } + else if (strcmp (GSearchGOptionArguments.sortby, "size") == 0) { + sort_by = COLUMN_SIZE; + } + else if (strcmp (GSearchGOptionArguments.sortby, "type") == 0) { + sort_by = COLUMN_TYPE; + } + else if (strcmp (GSearchGOptionArguments.sortby, "date") == 0) { + sort_by = COLUMN_DATE; + } + else { + g_warning (_("Invalid option passed to sortby command line argument.")); + sort_by = COLUMN_NAME; + } + + if (GSearchGOptionArguments.descending) { + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (gsearch->search_results_list_store), sort_by, + GTK_SORT_DESCENDING); + } + else { + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (gsearch->search_results_list_store), sort_by, + GTK_SORT_ASCENDING); + } + } + if (GSearchGOptionArguments.start) { + goption_args_found = TRUE; + click_find_cb (gsearch->find_button, (gpointer) gsearch); + } + return goption_args_found; +} + +static gboolean +handle_search_command_stdout_io (GIOChannel * ioc, + GIOCondition condition, + gpointer data) +{ + GSearchWindow * gsearch = data; + gboolean broken_pipe = FALSE; + + if (condition & G_IO_IN) { + + GError * error = NULL; + GTimer * timer; + GString * string; + GdkRectangle prior_rect; + GdkRectangle after_rect; + gulong duration; + gint look_in_folder_string_length; + + string = g_string_new (NULL); + look_in_folder_string_length = strlen (gsearch->command_details->look_in_folder_string); + + timer = g_timer_new (); + g_timer_start (timer); + + while (ioc->is_readable != TRUE); + + do { + gchar * utf8 = NULL; + gchar * filename = NULL; + gint status; + + if (gsearch->command_details->command_status == MAKE_IT_STOP) { + broken_pipe = TRUE; + break; + } + else if (gsearch->command_details->command_status != RUNNING) { + break; + } + + do { + status = g_io_channel_read_line_string (ioc, string, NULL, &error); + + if (status == G_IO_STATUS_EOF) { + broken_pipe = TRUE; + } + else if (status == G_IO_STATUS_AGAIN) { + if (gtk_events_pending ()) { + intermediate_file_count_update (gsearch); + while (gtk_events_pending ()) { + if (gsearch->command_details->command_status == MAKE_IT_QUIT) { + return FALSE; + } + gtk_main_iteration (); + } + + } + } + + } while (status == G_IO_STATUS_AGAIN && broken_pipe == FALSE); + + if (broken_pipe == TRUE) { + break; + } + + if (status != G_IO_STATUS_NORMAL) { + if (error != NULL) { + g_warning ("handle_search_command_stdout_io(): %s", error->message); + g_error_free (error); + } + continue; + } + + string = g_string_truncate (string, string->len - 1); + if (string->len <= 1) { + continue; + } + + utf8 = g_filename_display_name (string->str); + if (utf8 == NULL) { + continue; + } + + if (strncmp (string->str, gsearch->command_details->look_in_folder_string, look_in_folder_string_length) == 0) { + + if (strlen (string->str) != look_in_folder_string_length) { + + filename = g_path_get_basename (utf8); + + if (fnmatch (gsearch->command_details->name_contains_pattern_string, filename, FNM_NOESCAPE | FNM_CASEFOLD ) != FNM_NOMATCH) { + if (gsearch->command_details->is_command_show_hidden_files_enabled) { + if (gsearch->command_details->is_command_regex_matching_enabled == FALSE) { + add_file_to_search_results (string->str, gsearch->search_results_list_store, &gsearch->search_results_iter, gsearch); + } + else if (compare_regex (gsearch->command_details->name_contains_regex_string, filename)) { + add_file_to_search_results (string->str, gsearch->search_results_list_store, &gsearch->search_results_iter, gsearch); + } + } + else if ((is_path_hidden (string->str) == FALSE || + is_path_hidden (gsearch->command_details->look_in_folder_string) == TRUE) && + (!g_str_has_suffix (string->str, "~"))) { + if (gsearch->command_details->is_command_regex_matching_enabled == FALSE) { + add_file_to_search_results (string->str, gsearch->search_results_list_store, &gsearch->search_results_iter, gsearch); + } + else if (compare_regex (gsearch->command_details->name_contains_regex_string, filename)) { + add_file_to_search_results (string->str, gsearch->search_results_list_store, &gsearch->search_results_iter, gsearch); + } + } + } + } + } + g_free (utf8); + g_free (filename); + + gtk_tree_view_get_visible_rect (GTK_TREE_VIEW (gsearch->search_results_tree_view), &prior_rect); + + if (prior_rect.y == 0) { + gtk_tree_view_get_visible_rect (GTK_TREE_VIEW (gsearch->search_results_tree_view), &after_rect); + if (after_rect.y <= 40) { /* limit this hack to the first few pixels */ + gtk_tree_view_scroll_to_point (GTK_TREE_VIEW (gsearch->search_results_tree_view), -1, 0); + } + } + + g_timer_elapsed (timer, &duration); + + if (duration > MATE_SEARCH_TOOL_REFRESH_DURATION) { + if (gtk_events_pending ()) { + intermediate_file_count_update (gsearch); + while (gtk_events_pending ()) { + if (gsearch->command_details->command_status == MAKE_IT_QUIT) { + return FALSE; + } + gtk_main_iteration (); + } + } + g_timer_reset (timer); + } + + } while (g_io_channel_get_buffer_condition (ioc) & G_IO_IN); + + g_string_free (string, TRUE); + g_timer_destroy (timer); + } + + if (!(condition & G_IO_IN) || broken_pipe == TRUE) { + + g_io_channel_shutdown (ioc, TRUE, NULL); + + if ((gsearch->command_details->command_status != MAKE_IT_STOP) + && (gsearch->command_details->is_command_using_quick_mode == TRUE) + && (gsearch->command_details->is_command_first_pass == TRUE) + && (gsearch->command_details->is_command_second_pass_enabled == TRUE) + && (is_second_scan_excluded_path (gsearch->command_details->look_in_folder_string) == FALSE)) { + + gchar * command; + + /* Free these strings now because they are reassign values during the second pass. */ + g_free (gsearch->command_details->name_contains_pattern_string); + g_free (gsearch->command_details->name_contains_regex_string); + + command = build_search_command (gsearch, FALSE); + if (command != NULL) { + spawn_search_command (gsearch, command); + g_free (command); + } + } + else { + gsearch->command_details->command_status = (gsearch->command_details->command_status == MAKE_IT_STOP) ? ABORTED : STOPPED; + gsearch->command_details->is_command_timeout_enabled = TRUE; + g_hash_table_destroy (gsearch->search_results_pixbuf_hash_table); + g_hash_table_destroy (gsearch->search_results_filename_hash_table); + g_timeout_add (500, not_running_timeout_cb, (gpointer) gsearch); + + update_search_counts (gsearch); + stop_animation (gsearch); + + /* Free the gchar fields of search_command structure. */ + g_free (gsearch->command_details->name_contains_pattern_string); + g_free (gsearch->command_details->name_contains_regex_string); + + } + return FALSE; + } + return TRUE; +} + +static gboolean +handle_search_command_stderr_io (GIOChannel * ioc, + GIOCondition condition, + gpointer data) +{ + GSearchWindow * gsearch = data; + static GString * error_msgs = NULL; + static gboolean truncate_error_msgs = FALSE; + gboolean broken_pipe = FALSE; + + if (condition & G_IO_IN) { + + GString * string; + GError * error = NULL; + gchar * utf8 = NULL; + + string = g_string_new (NULL); + + if (error_msgs == NULL) { + error_msgs = g_string_new (NULL); + } + + while (ioc->is_readable != TRUE); + + do { + gint status; + + do { + status = g_io_channel_read_line_string (ioc, string, NULL, &error); + + if (status == G_IO_STATUS_EOF) { + broken_pipe = TRUE; + } + else if (status == G_IO_STATUS_AGAIN) { + if (gtk_events_pending ()) { + intermediate_file_count_update (gsearch); + while (gtk_events_pending ()) { + if (gsearch->command_details->command_status == MAKE_IT_QUIT) { + break; + } + gtk_main_iteration (); + + } + } + } + + } while (status == G_IO_STATUS_AGAIN && broken_pipe == FALSE); + + if (broken_pipe == TRUE) { + break; + } + + if (status != G_IO_STATUS_NORMAL) { + if (error != NULL) { + g_warning ("handle_search_command_stderr_io(): %s", error->message); + g_error_free (error); + } + continue; + } + + if (truncate_error_msgs == FALSE) { + if ((strstr (string->str, "ermission denied") == NULL) && + (strstr (string->str, "No such file or directory") == NULL) && + (strncmp (string->str, "grep: ", 6) != 0) && + (strcmp (string->str, "find: ") != 0)) { + utf8 = g_locale_to_utf8 (string->str, -1, NULL, NULL, NULL); + error_msgs = g_string_append (error_msgs, utf8); + truncate_error_msgs = limit_string_to_x_lines (error_msgs, 20); + } + } + + } while (g_io_channel_get_buffer_condition (ioc) & G_IO_IN); + + g_string_free (string, TRUE); + g_free (utf8); + } + + if (!(condition & G_IO_IN) || broken_pipe == TRUE) { + + if (error_msgs != NULL) { + + if (error_msgs->len > 0) { + + GtkWidget * dialog; + + if (truncate_error_msgs) { + error_msgs = g_string_append (error_msgs, + _("\n... Too many errors to display ...")); + } + + if (gsearch->command_details->is_command_using_quick_mode != TRUE) { + + GtkWidget * hbox; + GtkWidget * spacer; + GtkWidget * expander; + GtkWidget * label; + + dialog = gtk_message_dialog_new (GTK_WINDOW (gsearch->window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _("The search results may be invalid." + " There were errors while performing this search.")); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), " "); + + gtk_window_set_title (GTK_WINDOW (dialog), ""); + gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); + + hbox = gtk_hbox_new (0, FALSE); + + spacer = gtk_label_new (" "); + gtk_box_pack_start (GTK_BOX (hbox), spacer, FALSE, FALSE, 0); + + expander = gtk_expander_new_with_mnemonic (_("Show more _details")); + gtk_container_set_border_width (GTK_CONTAINER (expander), 6); + gtk_expander_set_spacing (GTK_EXPANDER (expander), 6); + gtk_box_pack_start (GTK_BOX (hbox), expander, TRUE, TRUE, 0); + + label = gtk_label_new (error_msgs->str); + gtk_container_add (GTK_CONTAINER (expander), label); + + gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), hbox, FALSE, FALSE, 0); + gtk_widget_show_all (hbox); + + g_signal_connect (G_OBJECT (dialog), + "response", + G_CALLBACK (gtk_widget_destroy), NULL); + + gtk_widget_show (dialog); + } + else if ((gsearch->command_details->is_command_second_pass_enabled == FALSE) || + (is_second_scan_excluded_path (gsearch->command_details->look_in_folder_string) == TRUE)) { + + GtkWidget * button; + GtkWidget * hbox; + GtkWidget * spacer; + GtkWidget * expander; + GtkWidget * label; + + dialog = gtk_message_dialog_new (GTK_WINDOW (gsearch->window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CANCEL, + _("The search results may be out of date or invalid." + " Do you want to disable the quick search feature?")); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + "Please reference the help documentation for instructions " + "on how to configure and enable quick searches."); + + gtk_window_set_title (GTK_WINDOW (dialog), ""); + gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); + + hbox = gtk_hbox_new (0, FALSE); + + spacer = gtk_label_new (" "); + gtk_box_pack_start (GTK_BOX (hbox), spacer, FALSE, FALSE, 0); + + expander = gtk_expander_new_with_mnemonic (_("Show more _details")); + gtk_container_set_border_width (GTK_CONTAINER (expander), 6); + gtk_expander_set_spacing (GTK_EXPANDER (expander), 6); + gtk_box_pack_start (GTK_BOX (hbox), expander, TRUE, TRUE, 0); + + label = gtk_label_new (error_msgs->str); + gtk_container_add (GTK_CONTAINER (expander), label); + + gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), hbox, FALSE, FALSE, 0); + gtk_widget_show_all (hbox); + + button = gsearchtool_button_new_with_stock_icon (_("Disable _Quick Search"), GTK_STOCK_OK); + gtk_widget_set_can_default (button, TRUE); + gtk_widget_show (button); + + gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button, GTK_RESPONSE_OK); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); + + g_signal_connect (G_OBJECT (dialog), + "response", + G_CALLBACK (disable_quick_search_cb), (gpointer) gsearch); + + gtk_widget_show (dialog); + } + } + truncate_error_msgs = FALSE; + g_string_truncate (error_msgs, 0); + } + g_io_channel_shutdown (ioc, TRUE, NULL); + return FALSE; + } + return TRUE; +} + +static void +child_command_set_pgid_cb (gpointer data) +{ + if (setpgid (0, 0) < 0) { + g_print (_("Failed to set process group id of child %d: %s.\n"), + getpid (), g_strerror (errno)); + } +} + +void +spawn_search_command (GSearchWindow * gsearch, + gchar * command) +{ + GIOChannel * ioc_stdout; + GIOChannel * ioc_stderr; + GError * error = NULL; + gchar ** argv = NULL; + gint child_stdout; + gint child_stderr; + + if (!g_shell_parse_argv (command, NULL, &argv, &error)) { + GtkWidget * dialog; + + stop_animation (gsearch); + + dialog = gtk_message_dialog_new (GTK_WINDOW (gsearch->window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _("Error parsing the search command.")); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + (error == NULL) ? " " : error->message, NULL); + + gtk_window_set_title (GTK_WINDOW (dialog), ""); + gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); + gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 14); + + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + g_error_free (error); + g_strfreev (argv); + + /* Free the gchar fields of search_command structure. */ + g_free (gsearch->command_details->look_in_folder_string); + g_free (gsearch->command_details->name_contains_pattern_string); + g_free (gsearch->command_details->name_contains_regex_string); + return; + } + + if (!g_spawn_async_with_pipes (g_get_home_dir (), argv, NULL, + G_SPAWN_SEARCH_PATH, + child_command_set_pgid_cb, NULL, &gsearch->command_details->command_pid, NULL, &child_stdout, + &child_stderr, &error)) { + GtkWidget * dialog; + + stop_animation (gsearch); + + dialog = gtk_message_dialog_new (GTK_WINDOW (gsearch->window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _("Error running the search command.")); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + (error == NULL) ? " " : error->message, NULL); + + gtk_window_set_title (GTK_WINDOW (dialog), ""); + gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); + gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 14); + + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + g_error_free (error); + g_strfreev (argv); + + /* Free the gchar fields of search_command structure. */ + g_free (gsearch->command_details->look_in_folder_string); + g_free (gsearch->command_details->name_contains_pattern_string); + g_free (gsearch->command_details->name_contains_regex_string); + return; + } + + if (gsearch->command_details->is_command_first_pass == TRUE) { + + gsearch->command_details->command_status = RUNNING; + gsearch->search_results_pixbuf_hash_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); + gsearch->search_results_filename_hash_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + + /* Get the value of the caja date-format key if available. */ + if (gsearch->caja_schema_exists) { + gsearch->search_results_date_format = g_settings_get_enum (gsearch->caja_settings, "date-format"); + } else { + gsearch->search_results_date_format = CAJA_DATE_FORMAT_LOCALE; + } + + gtk_tree_view_scroll_to_point (GTK_TREE_VIEW (gsearch->search_results_tree_view), 0, 0); + gtk_tree_model_foreach (GTK_TREE_MODEL (gsearch->search_results_list_store), + (GtkTreeModelForeachFunc) tree_model_iter_free_monitor, gsearch); + gtk_list_store_clear (GTK_LIST_STORE (gsearch->search_results_list_store)); + + gtk_tree_view_column_set_visible (gsearch->search_results_folder_column, TRUE); + gtk_tree_view_column_set_visible (gsearch->search_results_size_column, TRUE); + gtk_tree_view_column_set_visible (gsearch->search_results_type_column, TRUE); + gtk_tree_view_column_set_visible (gsearch->search_results_date_column, TRUE); + } + + ioc_stdout = g_io_channel_unix_new (child_stdout); + ioc_stderr = g_io_channel_unix_new (child_stderr); + + g_io_channel_set_encoding (ioc_stdout, NULL, NULL); + g_io_channel_set_encoding (ioc_stderr, NULL, NULL); + + g_io_channel_set_flags (ioc_stdout, G_IO_FLAG_NONBLOCK, NULL); + g_io_channel_set_flags (ioc_stderr, G_IO_FLAG_NONBLOCK, NULL); + + g_io_add_watch (ioc_stdout, G_IO_IN | G_IO_HUP, + handle_search_command_stdout_io, gsearch); + g_io_add_watch (ioc_stderr, G_IO_IN | G_IO_HUP, + handle_search_command_stderr_io, gsearch); + + g_io_channel_unref (ioc_stdout); + g_io_channel_unref (ioc_stderr); + g_strfreev (argv); +} + +static GtkWidget * +create_constraint_box (GSearchWindow * gsearch, + GSearchConstraint * opt, + gchar * value) +{ + GtkWidget * hbox; + GtkWidget * label; + GtkWidget * entry; + GtkWidget * entry_hbox; + GtkWidget * button; + + hbox = gtk_hbox_new (FALSE, 12); + + switch (GSearchOptionTemplates[opt->constraint_id].type) { + case SEARCH_CONSTRAINT_TYPE_BOOLEAN: + { + gchar * text = remove_mnemonic_character (GSearchOptionTemplates[opt->constraint_id].desc); + gchar * desc = g_strconcat (LEFT_LABEL_SPACING, _(text), ".", NULL); + label = gtk_label_new (desc); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + g_free (desc); + g_free (text); + } + break; + case SEARCH_CONSTRAINT_TYPE_TEXT: + case SEARCH_CONSTRAINT_TYPE_NUMERIC: + case SEARCH_CONSTRAINT_TYPE_DATE_BEFORE: + case SEARCH_CONSTRAINT_TYPE_DATE_AFTER: + { + gchar * desc = g_strconcat (LEFT_LABEL_SPACING, _(GSearchOptionTemplates[opt->constraint_id].desc), ":", NULL); + label = gtk_label_new_with_mnemonic (desc); + g_free (desc); + } + + /* add description label */ + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + if (GSearchOptionTemplates[opt->constraint_id].type == SEARCH_CONSTRAINT_TYPE_TEXT) { + entry = gtk_entry_new (); + if (value != NULL) { + gtk_entry_set_text (GTK_ENTRY (entry), value); + opt->data.text = value; + } + } + else { + entry = gtk_spin_button_new_with_range (0, 999999999, 1); + if (value != NULL) { + gtk_spin_button_set_value (GTK_SPIN_BUTTON (entry), atoi (value)); + opt->data.time = atoi (value); + opt->data.number = atoi (value); + } + } + + if (gsearch->is_window_accessible) { + gchar * text = remove_mnemonic_character (GSearchOptionTemplates[opt->constraint_id].desc); + gchar * name; + gchar * desc; + + if (GSearchOptionTemplates[opt->constraint_id].units == NULL) { + name = g_strdup (_(text)); + desc = g_strdup_printf (_("Enter a text value for the \"%s\" search option."), _(text)); + } + else { + /* Translators: Below is a string displaying the search options name + and unit value. For example, "\"Date modified less than\" in days". */ + name = g_strdup_printf (_("\"%s\" in %s"), _(text), + _(GSearchOptionTemplates[opt->constraint_id].units)); + desc = g_strdup_printf (_("Enter a value in %s for the \"%s\" search option."), + _(GSearchOptionTemplates[opt->constraint_id].units), + _(text)); + } + add_atk_namedesc (GTK_WIDGET (entry), name, desc); + g_free (name); + g_free (desc); + g_free (text); + } + + gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (entry)); + + g_signal_connect (G_OBJECT (entry), "changed", + G_CALLBACK (constraint_update_info_cb), opt); + + g_signal_connect (G_OBJECT (entry), "activate", + G_CALLBACK (constraint_activate_cb), + (gpointer) gsearch); + + /* add text field */ + entry_hbox = gtk_hbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (hbox), entry_hbox, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (entry_hbox), entry, TRUE, TRUE, 0); + + /* add units label */ + if (GSearchOptionTemplates[opt->constraint_id].units != NULL) + { + label = gtk_label_new_with_mnemonic (_(GSearchOptionTemplates[opt->constraint_id].units)); + gtk_box_pack_start (GTK_BOX (entry_hbox), label, FALSE, FALSE, 0); + } + + break; + default: + /* This should never happen. If it does, there is a bug */ + label = gtk_label_new ("???"); + gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0); + break; + } + + button = gtk_button_new_from_stock (GTK_STOCK_REMOVE); + gtk_widget_set_can_default (button, FALSE); + + { + GList * list = NULL; + + list = g_list_append (list, (gpointer) gsearch); + list = g_list_append (list, (gpointer) opt); + + g_signal_connect (G_OBJECT (button), "clicked", + G_CALLBACK (remove_constraint_cb), + (gpointer) list); + + } + gtk_size_group_add_widget (gsearch->available_options_button_size_group, button); + gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0); + + if (gsearch->is_window_accessible) { + gchar * text = remove_mnemonic_character (GSearchOptionTemplates[opt->constraint_id].desc); + gchar * name = g_strdup_printf (_("Remove \"%s\""), _(text)); + gchar * desc = g_strdup_printf (_("Click to remove the \"%s\" search option."), _(text)); + add_atk_namedesc (GTK_WIDGET (button), name, desc); + g_free (name); + g_free (desc); + g_free (text); + } + return hbox; +} + +void +add_constraint (GSearchWindow * gsearch, + gint constraint_id, + gchar * value, + gboolean show_constraint) +{ + GSearchConstraint * constraint = g_slice_new (GSearchConstraint); + GtkWidget * widget; + + if (show_constraint) { + if (gtk_widget_get_visible (gsearch->available_options_vbox) == FALSE) { + gtk_expander_set_expanded (GTK_EXPANDER (gsearch->show_more_options_expander), TRUE); + gtk_widget_show (gsearch->available_options_vbox); + } + } + + gsearch->window_geometry.min_height += WINDOW_HEIGHT_STEP; + + if (gtk_widget_get_visible (gsearch->available_options_vbox)) { + gtk_window_set_geometry_hints (GTK_WINDOW (gsearch->window), + GTK_WIDGET (gsearch->window), + &gsearch->window_geometry, + GDK_HINT_MIN_SIZE); + } + + constraint->constraint_id = constraint_id; + set_constraint_info_defaults (constraint); + set_constraint_gsettings_boolean (constraint->constraint_id, TRUE); + + widget = create_constraint_box (gsearch, constraint, value); + gtk_box_pack_start (GTK_BOX (gsearch->available_options_vbox), widget, FALSE, FALSE, 0); + gtk_widget_show_all (widget); + + gsearch->available_options_selected_list = + g_list_append (gsearch->available_options_selected_list, constraint); + + set_constraint_selected_state (gsearch, constraint->constraint_id, TRUE); +} + +static void +set_sensitive (GtkCellLayout * cell_layout, + GtkCellRenderer * cell, + GtkTreeModel * tree_model, + GtkTreeIter * iter, + gpointer data) +{ + GtkTreePath * path; + gint idx; + + path = gtk_tree_model_get_path (tree_model, iter); + idx = gtk_tree_path_get_indices (path)[0]; + gtk_tree_path_free (path); + + g_object_set (cell, "sensitive", !(GSearchOptionTemplates[idx].is_selected), NULL); +} + +static gboolean +is_separator (GtkTreeModel * model, + GtkTreeIter * iter, + gpointer data) +{ + GtkTreePath * path; + gint idx; + + path = gtk_tree_model_get_path (model, iter); + idx = gtk_tree_path_get_indices (path)[0]; + gtk_tree_path_free (path); + + return (GSearchOptionTemplates[idx].type == SEARCH_CONSTRAINT_TYPE_SEPARATOR); +} + +static void +create_additional_constraint_section (GSearchWindow * gsearch) +{ + GtkCellRenderer * renderer; + GtkTreeModel * model; + GtkWidget * hbox; + gchar * desc; + + gsearch->available_options_vbox = gtk_vbox_new (FALSE, 6); + + hbox = gtk_hbox_new (FALSE, 12); + gtk_box_pack_end (GTK_BOX (gsearch->available_options_vbox), hbox, FALSE, FALSE, 0); + + desc = g_strconcat (LEFT_LABEL_SPACING, _("A_vailable options:"), NULL); + gsearch->available_options_label = gtk_label_new_with_mnemonic (desc); + g_free (desc); + + gtk_box_pack_start (GTK_BOX (hbox), gsearch->available_options_label, FALSE, FALSE, 0); + + model = gsearch_create_list_of_templates (); + gsearch->available_options_combo_box = gtk_combo_box_new_with_model (model); + g_object_unref (model); + + gtk_label_set_mnemonic_widget (GTK_LABEL (gsearch->available_options_label), GTK_WIDGET (gsearch->available_options_combo_box)); + gtk_combo_box_set_active (GTK_COMBO_BOX (gsearch->available_options_combo_box), 0); + gtk_box_pack_start (GTK_BOX (hbox), gsearch->available_options_combo_box, TRUE, TRUE, 0); + + renderer = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (gsearch->available_options_combo_box), + renderer, + TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (gsearch->available_options_combo_box), renderer, + "text", 0, + NULL); + gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (gsearch->available_options_combo_box), + renderer, + set_sensitive, + NULL, NULL); + gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (gsearch->available_options_combo_box), + is_separator, NULL, NULL); + + if (gsearch->is_window_accessible) { + add_atk_namedesc (GTK_WIDGET (gsearch->available_options_combo_box), _("Available options"), + _("Select a search option from the drop-down list.")); + } + + gsearch->available_options_add_button = gtk_button_new_from_stock (GTK_STOCK_ADD); + gtk_widget_set_can_default (gsearch->available_options_add_button, FALSE); + gsearch->available_options_button_size_group = gtk_size_group_new (GTK_SIZE_GROUP_BOTH); + gtk_size_group_add_widget (gsearch->available_options_button_size_group, gsearch->available_options_add_button); + + g_signal_connect (G_OBJECT (gsearch->available_options_add_button),"clicked", + G_CALLBACK (add_constraint_cb), (gpointer) gsearch); + + if (gsearch->is_window_accessible) { + add_atk_namedesc (GTK_WIDGET (gsearch->available_options_add_button), _("Add search option"), + _("Click to add the selected available search option.")); + } + + gtk_box_pack_end (GTK_BOX (hbox), gsearch->available_options_add_button, FALSE, FALSE, 0); +} + +static void +filename_cell_data_func (GtkTreeViewColumn * column, + GtkCellRenderer * renderer, + GtkTreeModel * model, + GtkTreeIter * iter, + GSearchWindow * gsearch) +{ + GtkTreePath * path; + PangoUnderline underline; + gboolean underline_set; + + if (gsearch->is_search_results_single_click_to_activate == TRUE) { + + path = gtk_tree_model_get_path (model, iter); + + if ((gsearch->search_results_hover_path == NULL) || + (gtk_tree_path_compare (path, gsearch->search_results_hover_path) != 0)) { + underline = PANGO_UNDERLINE_NONE; + underline_set = FALSE; + } + else { + underline = PANGO_UNDERLINE_SINGLE; + underline_set = TRUE; + } + gtk_tree_path_free (path); + } + else { + underline = PANGO_UNDERLINE_NONE; + underline_set = FALSE; + } + + g_object_set (gsearch->search_results_name_cell_renderer, + "underline", underline, + "underline-set", underline_set, + NULL); +} + +static gboolean +gsearch_equal_func (GtkTreeModel * model, + gint column, + const gchar * key, + GtkTreeIter * iter, + gpointer search_data) +{ + gboolean results = TRUE; + gchar * name; + + gtk_tree_model_get (model, iter, COLUMN_NAME, &name, -1); + + if (name != NULL) { + gchar * casefold_key; + gchar * casefold_name; + + casefold_key = g_utf8_casefold (key, -1); + casefold_name = g_utf8_casefold (name, -1); + + if ((casefold_key != NULL) && + (casefold_name != NULL) && + (strstr (casefold_name, casefold_key) != NULL)) { + results = FALSE; + } + g_free (casefold_key); + g_free (casefold_name); + g_free (name); + } + return results; +} + +static GtkWidget * +create_search_results_section (GSearchWindow * gsearch) +{ + GtkWidget * label; + GtkWidget * vbox; + GtkWidget * hbox; + GtkWidget * window; + GtkTreeViewColumn * column; + GtkCellRenderer * renderer; + + vbox = gtk_vbox_new (FALSE, 6); + + hbox = gtk_hbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + + label = gtk_label_new_with_mnemonic (_("S_earch results:")); + g_object_set (G_OBJECT (label), "xalign", 0.0, NULL); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0); + + gsearch->progress_spinner = gtk_spinner_new (); + gtk_widget_set_size_request (gsearch->progress_spinner, + GTK_ICON_SIZE_MENU, GTK_ICON_SIZE_MENU); + gtk_box_pack_start (GTK_BOX (hbox), gsearch->progress_spinner, FALSE, FALSE, 0); + + gsearch->files_found_label = gtk_label_new (NULL); + gtk_label_set_selectable (GTK_LABEL (gsearch->files_found_label), TRUE); + g_object_set (G_OBJECT (gsearch->files_found_label), "xalign", 1.0, NULL); + gtk_box_pack_start (GTK_BOX (hbox), gsearch->files_found_label, TRUE, TRUE, 0); + + window = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (window), GTK_SHADOW_IN); + gtk_container_set_border_width (GTK_CONTAINER (window), 0); + gtk_widget_set_size_request (window, 530, 160); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (window), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + + gsearch->search_results_list_store = gtk_list_store_new (NUM_COLUMNS, + GDK_TYPE_PIXBUF, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_DOUBLE, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_DOUBLE, + G_TYPE_POINTER, + G_TYPE_BOOLEAN); + + gsearch->search_results_tree_view = GTK_TREE_VIEW (gtk_tree_view_new_with_model (GTK_TREE_MODEL (gsearch->search_results_list_store))); + + gtk_tree_view_set_headers_visible (gsearch->search_results_tree_view, FALSE); + gtk_tree_view_set_search_equal_func (gsearch->search_results_tree_view, + gsearch_equal_func, NULL, NULL); + gtk_tree_view_set_rules_hint (gsearch->search_results_tree_view, TRUE); + g_object_unref (G_OBJECT (gsearch->search_results_list_store)); + + if (gsearch->is_window_accessible) { + add_atk_namedesc (GTK_WIDGET (gsearch->search_results_tree_view), _("List View"), NULL); + } + + gsearch->search_results_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (gsearch->search_results_tree_view)); + + gtk_tree_selection_set_mode (GTK_TREE_SELECTION (gsearch->search_results_selection), + GTK_SELECTION_MULTIPLE); + + gtk_drag_source_set (GTK_WIDGET (gsearch->search_results_tree_view), + GDK_BUTTON1_MASK | GDK_BUTTON2_MASK, + GSearchDndTable, GSearchTotalDnds, + GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_ASK); + + g_signal_connect (G_OBJECT (gsearch->search_results_tree_view), + "drag_data_get", + G_CALLBACK (drag_file_cb), + (gpointer) gsearch); + + g_signal_connect (G_OBJECT (gsearch->search_results_tree_view), + "drag_begin", + G_CALLBACK (drag_begin_file_cb), + (gpointer) gsearch); + + g_signal_connect (G_OBJECT (gsearch->search_results_tree_view), + "event_after", + G_CALLBACK (file_event_after_cb), + (gpointer) gsearch); + + g_signal_connect (G_OBJECT (gsearch->search_results_tree_view), + "button_release_event", + G_CALLBACK (file_button_release_event_cb), + (gpointer) gsearch); + + g_signal_connect (G_OBJECT (gsearch->search_results_tree_view), + "button_press_event", + G_CALLBACK (file_button_press_event_cb), + (gpointer) gsearch->search_results_tree_view); + + g_signal_connect (G_OBJECT (gsearch->search_results_tree_view), + "key_press_event", + G_CALLBACK (file_key_press_event_cb), + (gpointer) gsearch); + + g_signal_connect (G_OBJECT (gsearch->search_results_tree_view), + "motion_notify_event", + G_CALLBACK (file_motion_notify_cb), + (gpointer) gsearch); + + g_signal_connect (G_OBJECT (gsearch->search_results_tree_view), + "leave_notify_event", + G_CALLBACK (file_leave_notify_cb), + (gpointer) gsearch); + + gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (gsearch->search_results_tree_view)); + + gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (gsearch->search_results_tree_view)); + gtk_box_pack_end (GTK_BOX (vbox), window, TRUE, TRUE, 0); + + /* create the name column */ + column = gtk_tree_view_column_new (); + gtk_tree_view_column_set_title (column, _("Name")); + + renderer = gtk_cell_renderer_pixbuf_new (); + gtk_tree_view_column_pack_start (column, renderer, FALSE); + gtk_tree_view_column_set_attributes (column, renderer, + "pixbuf", COLUMN_ICON, + NULL); + + gsearch->search_results_name_cell_renderer = gtk_cell_renderer_text_new (); + gtk_tree_view_column_pack_start (column, gsearch->search_results_name_cell_renderer, TRUE); + gtk_tree_view_column_set_attributes (column, gsearch->search_results_name_cell_renderer, + "text", COLUMN_NAME, + NULL); + gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); + gtk_tree_view_column_set_resizable (column, TRUE); + gtk_tree_view_column_set_sort_column_id (column, COLUMN_NAME); + gtk_tree_view_column_set_reorderable (column, TRUE); + gtk_tree_view_column_set_cell_data_func (column, renderer, + (GtkTreeCellDataFunc) filename_cell_data_func, + gsearch, NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (gsearch->search_results_tree_view), column); + + /* create the folder column */ + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("Folder"), renderer, + "text", COLUMN_RELATIVE_PATH, + NULL); + gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); + gtk_tree_view_column_set_resizable (column, TRUE); + gtk_tree_view_column_set_sort_column_id (column, COLUMN_RELATIVE_PATH); + gtk_tree_view_column_set_reorderable (column, TRUE); + gtk_tree_view_append_column (GTK_TREE_VIEW (gsearch->search_results_tree_view), column); + gsearch->search_results_folder_column = column; + + /* create the size column */ + renderer = gtk_cell_renderer_text_new (); + g_object_set (renderer, "xalign", 1.0, NULL); + column = gtk_tree_view_column_new_with_attributes (_("Size"), renderer, + "text", COLUMN_READABLE_SIZE, + NULL); + gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); + gtk_tree_view_column_set_resizable (column, TRUE); + gtk_tree_view_column_set_sort_column_id (column, COLUMN_SIZE); + gtk_tree_view_column_set_reorderable (column, TRUE); + gtk_tree_view_append_column (GTK_TREE_VIEW (gsearch->search_results_tree_view), column); + gsearch->search_results_size_column = column; + + /* create the type column */ + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("Type"), renderer, + "text", COLUMN_TYPE, + NULL); + gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); + gtk_tree_view_column_set_resizable (column, TRUE); + gtk_tree_view_column_set_sort_column_id (column, COLUMN_TYPE); + gtk_tree_view_column_set_reorderable (column, TRUE); + gtk_tree_view_append_column (GTK_TREE_VIEW (gsearch->search_results_tree_view), column); + gsearch->search_results_type_column = column; + + /* create the date modified column */ + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("Date Modified"), renderer, + "text", COLUMN_READABLE_DATE, + NULL); + gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); + gtk_tree_view_column_set_resizable (column, TRUE); + gtk_tree_view_column_set_sort_column_id (column, COLUMN_DATE); + gtk_tree_view_column_set_reorderable (column, TRUE); + gtk_tree_view_append_column (GTK_TREE_VIEW (gsearch->search_results_tree_view), column); + gsearch->search_results_date_column = column; + + gsearchtool_set_columns_order (gsearch->search_results_tree_view); + + g_signal_connect (G_OBJECT (gsearch->search_results_tree_view), + "columns-changed", + G_CALLBACK (columns_changed_cb), + (gpointer) gsearch); + return vbox; +} + +static void +register_gsearchtool_icon (GtkIconFactory * factory) +{ + GtkIconSource * source; + GtkIconSet * icon_set; + + source = gtk_icon_source_new (); + + gtk_icon_source_set_icon_name (source, MATE_SEARCH_TOOL_ICON); + + icon_set = gtk_icon_set_new (); + gtk_icon_set_add_source (icon_set, source); + + gtk_icon_factory_add (factory, MATE_SEARCH_TOOL_STOCK, icon_set); + + gtk_icon_set_unref (icon_set); + + gtk_icon_source_free (source); +} + +static void +gsearchtool_init_stock_icons (void) +{ + GtkIconFactory * factory; + GtkIconSize gsearchtool_icon_size; + + gsearchtool_icon_size = gtk_icon_size_register ("panel-menu", + MATE_SEARCH_TOOL_DEFAULT_ICON_SIZE, + MATE_SEARCH_TOOL_DEFAULT_ICON_SIZE); + + factory = gtk_icon_factory_new (); + gtk_icon_factory_add_default (factory); + + register_gsearchtool_icon (factory); + + g_object_unref (factory); +} + +void +set_clone_command (GSearchWindow * gsearch, + gint * argcp, + gchar *** argvp, + gpointer client_data, + gboolean escape_values) +{ + gchar ** argv; + gchar * file_is_named_utf8; + gchar * file_is_named_locale; + gchar * look_in_folder_locale; + gchar * tmp; + GList * list; + gint i = 0; + + argv = g_new0 (gchar*, SEARCH_CONSTRAINT_MAXIMUM_POSSIBLE); + + argv[i++] = (gchar *) client_data; + + file_is_named_utf8 = (gchar *) gtk_entry_get_text (GTK_ENTRY (gsearch_history_entry_get_entry (GSEARCH_HISTORY_ENTRY (gsearch->name_contains_entry)))); + file_is_named_locale = g_locale_from_utf8 (file_is_named_utf8 != NULL ? file_is_named_utf8 : "" , + -1, NULL, NULL, NULL); + if (escape_values) + tmp = g_shell_quote (file_is_named_locale); + else + tmp = g_strdup (file_is_named_locale); + argv[i++] = g_strdup_printf ("--named=%s", tmp); + g_free (tmp); + g_free (file_is_named_locale); + + look_in_folder_locale = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (gsearch->look_in_folder_button)); + + if (look_in_folder_locale == NULL) { + look_in_folder_locale = g_strdup (""); + } + + if (escape_values) + tmp = g_shell_quote (look_in_folder_locale); + else + tmp = g_strdup (look_in_folder_locale); + argv[i++] = g_strdup_printf ("--path=%s", tmp); + g_free (tmp); + g_free (look_in_folder_locale); + + if (gtk_widget_get_visible (gsearch->available_options_vbox)) { + for (list = gsearch->available_options_selected_list; list != NULL; list = g_list_next (list)) { + GSearchConstraint * constraint = list->data; + gchar * locale = NULL; + + switch (constraint->constraint_id) { + case SEARCH_CONSTRAINT_CONTAINS_THE_TEXT: + locale = g_locale_from_utf8 (constraint->data.text, -1, NULL, NULL, NULL); + if (escape_values) + tmp = g_shell_quote (locale); + else + tmp = g_strdup (locale); + argv[i++] = g_strdup_printf ("--contains=%s", tmp); + g_free (tmp); + break; + case SEARCH_CONSTRAINT_DATE_MODIFIED_BEFORE: + argv[i++] = g_strdup_printf ("--mtimeless=%d", constraint->data.time); + break; + case SEARCH_CONSTRAINT_DATE_MODIFIED_AFTER: + argv[i++] = g_strdup_printf ("--mtimemore=%d", constraint->data.time); + break; + case SEARCH_CONSTRAINT_SIZE_IS_MORE_THAN: + argv[i++] = g_strdup_printf ("--sizemore=%u", constraint->data.number); + break; + case SEARCH_CONSTRAINT_SIZE_IS_LESS_THAN: + argv[i++] = g_strdup_printf ("--sizeless=%u", constraint->data.number); + break; + case SEARCH_CONSTRAINT_FILE_IS_EMPTY: + argv[i++] = g_strdup ("--empty"); + break; + case SEARCH_CONSTRAINT_OWNED_BY_USER: + locale = g_locale_from_utf8 (constraint->data.text, -1, NULL, NULL, NULL); + if (escape_values) + tmp = g_shell_quote (locale); + else + tmp = g_strdup (locale); + argv[i++] = g_strdup_printf ("--user=%s", tmp); + g_free (tmp); + break; + case SEARCH_CONSTRAINT_OWNED_BY_GROUP: + locale = g_locale_from_utf8 (constraint->data.text, -1, NULL, NULL, NULL); + if (escape_values) + tmp = g_shell_quote (locale); + else + tmp = g_strdup (locale); + argv[i++] = g_strdup_printf ("--group=%s", tmp); + g_free (tmp); + break; + case SEARCH_CONSTRAINT_OWNER_IS_UNRECOGNIZED: + argv[i++] = g_strdup ("--nouser"); + break; + case SEARCH_CONSTRAINT_FILE_IS_NOT_NAMED: + locale = g_locale_from_utf8 (constraint->data.text, -1, NULL, NULL, NULL); + if (escape_values) + tmp = g_shell_quote (locale); + else + tmp = g_strdup (locale); + argv[i++] = g_strdup_printf ("--notnamed=%s", tmp); + g_free (tmp); + break; + case SEARCH_CONSTRAINT_FILE_MATCHES_REGULAR_EXPRESSION: + locale = g_locale_from_utf8 (constraint->data.text, -1, NULL, NULL, NULL); + if (escape_values) + tmp = g_shell_quote (locale); + else + tmp = g_strdup (locale); + argv[i++] = g_strdup_printf ("--regex=%s", tmp); + g_free (tmp); + break; + case SEARCH_CONSTRAINT_SHOW_HIDDEN_FILES_AND_FOLDERS: + argv[i++] = g_strdup ("--hidden"); + break; + case SEARCH_CONSTRAINT_FOLLOW_SYMBOLIC_LINKS: + argv[i++] = g_strdup ("--follow"); + break; + case SEARCH_CONSTRAINT_SEARCH_OTHER_FILESYSTEMS: + argv[i++] = g_strdup ("--mounts"); + break; + default: + break; + } + g_free (locale); + } + } + *argvp = argv; + *argcp = i; +} + +static void +handle_gsettings_settings (GSearchWindow * gsearch) +{ + if (g_settings_get_boolean (gsearch->mate_search_tool_settings, "show-additional-options")) { + if (gtk_widget_get_visible (gsearch->available_options_vbox) == FALSE) { + gtk_expander_set_expanded (GTK_EXPANDER (gsearch->show_more_options_expander), TRUE); + gtk_widget_show (gsearch->available_options_vbox); + } + } + + if (g_settings_get_boolean (gsearch->mate_search_tool_select_settings, "contains-the-text")) { + add_constraint (gsearch, SEARCH_CONSTRAINT_CONTAINS_THE_TEXT, "", FALSE); + } + + if (g_settings_get_boolean (gsearch->mate_search_tool_select_settings, "date-modified-less-than")) { + add_constraint (gsearch, SEARCH_CONSTRAINT_DATE_MODIFIED_BEFORE, "", FALSE); + } + + if (g_settings_get_boolean (gsearch->mate_search_tool_select_settings, "date-modified-more-than")) { + add_constraint (gsearch, SEARCH_CONSTRAINT_DATE_MODIFIED_AFTER, "", FALSE); + } + + if (g_settings_get_boolean (gsearch->mate_search_tool_select_settings, "size-at-least")) { + add_constraint (gsearch, SEARCH_CONSTRAINT_SIZE_IS_MORE_THAN, "", FALSE); + } + + if (g_settings_get_boolean (gsearch->mate_search_tool_select_settings, "size-at-most")) { + add_constraint (gsearch, SEARCH_CONSTRAINT_SIZE_IS_LESS_THAN, "", FALSE); + } + + if (g_settings_get_boolean (gsearch->mate_search_tool_select_settings, "file-is-empty")) { + add_constraint (gsearch, SEARCH_CONSTRAINT_FILE_IS_EMPTY, NULL, FALSE); + } + + if (g_settings_get_boolean (gsearch->mate_search_tool_select_settings, "owned-by-user")) { + add_constraint (gsearch, SEARCH_CONSTRAINT_OWNED_BY_USER, "", FALSE); + } + + if (g_settings_get_boolean (gsearch->mate_search_tool_select_settings, "owned-by-group")) { + add_constraint (gsearch, SEARCH_CONSTRAINT_OWNED_BY_GROUP, "", FALSE); + } + + if (g_settings_get_boolean (gsearch->mate_search_tool_select_settings, "owner-is-unrecognized")) { + add_constraint (gsearch, SEARCH_CONSTRAINT_OWNER_IS_UNRECOGNIZED, NULL, FALSE); + } + + if (g_settings_get_boolean (gsearch->mate_search_tool_select_settings, "name-does-not-contain")) { + add_constraint (gsearch, SEARCH_CONSTRAINT_FILE_IS_NOT_NAMED, "", FALSE); + } + + if (g_settings_get_boolean (gsearch->mate_search_tool_select_settings, "name-matches-regular-expression")) { + add_constraint (gsearch, SEARCH_CONSTRAINT_FILE_MATCHES_REGULAR_EXPRESSION, "", FALSE); + } + + if (g_settings_get_boolean (gsearch->mate_search_tool_select_settings, "show-hidden-files-and-folders")) { + add_constraint (gsearch, SEARCH_CONSTRAINT_SHOW_HIDDEN_FILES_AND_FOLDERS, NULL, FALSE); + } + + if (g_settings_get_boolean (gsearch->mate_search_tool_select_settings, "follow-symbolic-links")) { + add_constraint (gsearch, SEARCH_CONSTRAINT_FOLLOW_SYMBOLIC_LINKS, NULL, FALSE); + } + + if (g_settings_get_boolean (gsearch->mate_search_tool_select_settings, "exclude-other-filesystems")) { + add_constraint (gsearch, SEARCH_CONSTRAINT_SEARCH_OTHER_FILESYSTEMS, NULL, FALSE); + } +} + +static void +gsearch_window_size_allocate (GtkWidget * widget, + GtkAllocation * allocation, + GSearchWindow * gsearch) +{ + if (gsearch->is_window_maximized == FALSE) { + gsearch->window_width = allocation->width; + gsearch->window_height = allocation->height; + } +} + +static GtkWidget * +gsearch_app_create (GSearchWindow * gsearch) +{ + gchar * locale_string; + gchar * utf8_string; + GtkWidget * hbox; + GtkWidget * vbox; + GtkWidget * entry; + GtkWidget * label; + GtkWidget * button; + GtkWidget * container; + + gsearch->mate_search_tool_settings = g_settings_new ("org.mate.search-tool"); + gsearch->mate_search_tool_select_settings = g_settings_new ("org.mate.search-tool.select"); + gsearch->mate_desktop_interface_settings = g_settings_new ("org.mate.interface"); + + /* Check if caja schema is installed before trying to read caja settings */ + gsearch->caja_schema_exists = FALSE; + GSettingsSchema *schema = g_settings_schema_source_lookup (g_settings_schema_source_get_default (), + CAJA_PREFERENCES_SCHEMA, + FALSE); + + if (schema != NULL) { + gsearch->caja_schema_exists = TRUE; + g_settings_schema_unref (schema); + } + + if (gsearch->caja_schema_exists) { + gsearch->caja_settings = g_settings_new (CAJA_PREFERENCES_SCHEMA); + } else { + gsearch->caja_settings = NULL; + } + + gsearch->window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gsearch->is_window_maximized = g_settings_get_boolean (gsearch->mate_search_tool_settings, "default-window-maximized"); + g_signal_connect (G_OBJECT (gsearch->window), "size-allocate", + G_CALLBACK (gsearch_window_size_allocate), + gsearch); + gsearch->command_details = g_slice_new0 (GSearchCommandDetails); + gsearch->window_geometry.min_height = MINIMUM_WINDOW_HEIGHT; + gsearch->window_geometry.min_width = MINIMUM_WINDOW_WIDTH; + + gtk_window_set_position (GTK_WINDOW (gsearch->window), GTK_WIN_POS_CENTER); + gtk_window_set_geometry_hints (GTK_WINDOW (gsearch->window), GTK_WIDGET (gsearch->window), + &gsearch->window_geometry, GDK_HINT_MIN_SIZE); + + gsearchtool_get_stored_window_geometry (&gsearch->window_width, + &gsearch->window_height); + gtk_window_set_default_size (GTK_WINDOW (gsearch->window), + gsearch->window_width, + gsearch->window_height); + + if (gsearch->is_window_maximized == TRUE) { + gtk_window_maximize (GTK_WINDOW (gsearch->window)); + } + + container = gtk_vbox_new (FALSE, 6); + gtk_container_add (GTK_CONTAINER (gsearch->window), container); + gtk_container_set_border_width (GTK_CONTAINER (container), 12); + + hbox = gtk_hbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (container), hbox, FALSE, FALSE, 0); + + gsearch->name_and_folder_table = gtk_table_new (2, 2, FALSE); + gtk_table_set_row_spacings (GTK_TABLE (gsearch->name_and_folder_table), 6); + gtk_table_set_col_spacings (GTK_TABLE (gsearch->name_and_folder_table), 12); + gtk_container_add (GTK_CONTAINER (hbox), gsearch->name_and_folder_table); + + label = gtk_label_new_with_mnemonic (_("_Name contains:")); + gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT); + g_object_set (G_OBJECT (label), "xalign", 0.0, NULL); + + gtk_table_attach (GTK_TABLE (gsearch->name_and_folder_table), label, 0, 1, 0, 1, GTK_FILL, 0, 0, 1); + + gsearch->name_contains_entry = gsearch_history_entry_new ("gsearchtool-file-entry", FALSE); + gtk_label_set_mnemonic_widget (GTK_LABEL (label), gsearch->name_contains_entry); + gsearch_history_entry_set_history_length (GSEARCH_HISTORY_ENTRY (gsearch->name_contains_entry), 10); + gtk_table_attach (GTK_TABLE (gsearch->name_and_folder_table), gsearch->name_contains_entry, 1, 2, 0, 1, GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, 0, 0); + entry = gsearch_history_entry_get_entry (GSEARCH_HISTORY_ENTRY (gsearch->name_contains_entry)); + + if (GTK_IS_ACCESSIBLE (gtk_widget_get_accessible (gsearch->name_contains_entry))) { + gsearch->is_window_accessible = TRUE; + add_atk_namedesc (gsearch->name_contains_entry, NULL, _("Enter a filename or partial filename with or without wildcards.")); + add_atk_namedesc (entry, _("Name contains"), _("Enter a filename or partial filename with or without wildcards.")); + } + g_signal_connect (G_OBJECT (gsearch_history_entry_get_entry (GSEARCH_HISTORY_ENTRY (gsearch->name_contains_entry))), "activate", + G_CALLBACK (name_contains_activate_cb), + (gpointer) gsearch); + + label = gtk_label_new_with_mnemonic (_("_Look in folder:")); + gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT); + g_object_set (G_OBJECT (label), "xalign", 0.0, NULL); + + gtk_table_attach (GTK_TABLE (gsearch->name_and_folder_table), label, 0, 1, 1, 2, GTK_FILL, 0, 0, 0); + + gsearch->look_in_folder_button = gtk_file_chooser_button_new (_("Browse"), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER); + gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (gsearch->look_in_folder_button)); + gtk_table_attach (GTK_TABLE (gsearch->name_and_folder_table), gsearch->look_in_folder_button, 1, 2, 1, 2, GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, 0, 0); + + g_signal_connect (G_OBJECT (gsearch->look_in_folder_button), "current-folder-changed", + G_CALLBACK (look_in_folder_changed_cb), + (gpointer) gsearch); + + if (gsearch->is_window_accessible) { + add_atk_namedesc (GTK_WIDGET (gsearch->look_in_folder_button), _("Look in folder"), _("Select the folder or device from which you want to begin the search.")); + } + + locale_string = g_settings_get_string (gsearch->mate_search_tool_settings, "look-in-folder"); + + if ((g_file_test (locale_string, G_FILE_TEST_EXISTS) == FALSE) || + (g_file_test (locale_string, G_FILE_TEST_IS_DIR) == FALSE)) { + g_free (locale_string); + locale_string = g_get_current_dir (); + } + + utf8_string = g_filename_to_utf8 (locale_string, -1, NULL, NULL, NULL); + + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (gsearch->look_in_folder_button), utf8_string); + + g_free (locale_string); + g_free (utf8_string); + + gsearch->show_more_options_expander = gtk_expander_new_with_mnemonic (_("Select more _options")); + gtk_box_pack_start (GTK_BOX (container), gsearch->show_more_options_expander, FALSE, FALSE, 0); + g_signal_connect (G_OBJECT (gsearch->show_more_options_expander), "notify::expanded", + G_CALLBACK (click_expander_cb), (gpointer) gsearch); + + create_additional_constraint_section (gsearch); + gtk_box_pack_start (GTK_BOX (container), GTK_WIDGET (gsearch->available_options_vbox), FALSE, FALSE, 0); + + if (gsearch->is_window_accessible) { + add_atk_namedesc (GTK_WIDGET (gsearch->show_more_options_expander), _("Select more options"), _("Click to expand or collapse the list of available options.")); + add_atk_relation (GTK_WIDGET (gsearch->available_options_vbox), GTK_WIDGET (gsearch->show_more_options_expander), ATK_RELATION_CONTROLLED_BY); + add_atk_relation (GTK_WIDGET (gsearch->show_more_options_expander), GTK_WIDGET (gsearch->available_options_vbox), ATK_RELATION_CONTROLLER_FOR); + } + + vbox = gtk_vbox_new (FALSE, 12); + gtk_box_pack_start (GTK_BOX (container), vbox, TRUE, TRUE, 0); + + gsearch->search_results_vbox = create_search_results_section (gsearch); + gtk_widget_set_sensitive (GTK_WIDGET (gsearch->search_results_vbox), FALSE); + gtk_box_pack_start (GTK_BOX (vbox), gsearch->search_results_vbox, TRUE, TRUE, 0); + + hbox = gtk_hbutton_box_new (); + gtk_button_box_set_layout (GTK_BUTTON_BOX (hbox), GTK_BUTTONBOX_END); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + + gtk_box_set_spacing (GTK_BOX (hbox), 6); + button = gtk_button_new_from_stock (GTK_STOCK_HELP); + gtk_widget_set_can_default (button, TRUE); + gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0); + gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (hbox), button, TRUE); + g_signal_connect (G_OBJECT (button), "clicked", + G_CALLBACK (click_help_cb), (gpointer) gsearch->window); + if (gsearch->is_window_accessible) { + add_atk_namedesc (GTK_WIDGET (button), NULL, _("Click to display the help manual.")); + } + + button = gtk_button_new_from_stock (GTK_STOCK_CLOSE); + gtk_widget_set_can_default (button, TRUE); + g_signal_connect (G_OBJECT (button), "clicked", + G_CALLBACK (click_close_cb), (gpointer) gsearch); + if (gsearch->is_window_accessible) { + add_atk_namedesc (GTK_WIDGET (button), NULL, _("Click to close \"Search for Files\".")); + } + + gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0); + + /* Find and Stop buttons... */ + gsearch->find_button = gtk_button_new_from_stock (GTK_STOCK_FIND); + gsearch->stop_button = gtk_button_new_from_stock (GTK_STOCK_STOP); + + gtk_widget_set_can_default (gsearch->find_button, TRUE); + gtk_widget_set_can_default (gsearch->stop_button, TRUE); + + gtk_box_pack_end (GTK_BOX (hbox), gsearch->stop_button, FALSE, FALSE, 0); + gtk_box_pack_end (GTK_BOX (hbox), gsearch->find_button, FALSE, FALSE, 0); + + gtk_widget_set_sensitive (gsearch->stop_button, FALSE); + gtk_widget_set_sensitive (gsearch->find_button, TRUE); + + g_signal_connect (G_OBJECT (gsearch->find_button), "clicked", + G_CALLBACK (click_find_cb), (gpointer) gsearch); + g_signal_connect (G_OBJECT (gsearch->find_button), "size_allocate", + G_CALLBACK (size_allocate_cb), (gpointer) gsearch->available_options_add_button); + g_signal_connect (G_OBJECT (gsearch->stop_button), "clicked", + G_CALLBACK (click_stop_cb), (gpointer) gsearch); + + if (gsearch->is_window_accessible) { + add_atk_namedesc (GTK_WIDGET (gsearch->find_button), NULL, _("Click to perform a search.")); + add_atk_namedesc (GTK_WIDGET (gsearch->stop_button), NULL, _("Click to stop a search.")); + } + + gtk_widget_show_all (container); + gtk_widget_hide (gsearch->available_options_vbox); + gtk_widget_hide (gsearch->progress_spinner); + gtk_widget_hide (gsearch->stop_button); + + gtk_window_set_focus (GTK_WINDOW (gsearch->window), + GTK_WIDGET (gsearch_history_entry_get_entry (GSEARCH_HISTORY_ENTRY (gsearch->name_contains_entry)))); + + gtk_window_set_default (GTK_WINDOW (gsearch->window), gsearch->find_button); + + return gsearch->window; +} + +static void +gsearch_window_finalize (GObject * object) +{ + parent_class->finalize (object); +} + +static void +gsearch_window_class_init (GSearchWindowClass * klass) +{ + GObjectClass * object_class = (GObjectClass *) klass; + + object_class->finalize = gsearch_window_finalize; + parent_class = g_type_class_peek_parent (klass); +} + +GType +gsearch_window_get_type (void) +{ + static GType object_type = 0; + + if (!object_type) { + static const GTypeInfo object_info = { + sizeof (GSearchWindowClass), + NULL, + NULL, + (GClassInitFunc) gsearch_window_class_init, + NULL, + NULL, + sizeof (GSearchWindow), + 0, + (GInstanceInitFunc) gsearch_app_create + }; + object_type = g_type_register_static (GTK_TYPE_WINDOW, "GSearchWindow", &object_info, 0); + } + return object_type; +} + +static void +gsearchtool_setup_gsettings_notifications (GSearchWindow * gsearch) + +{ + gchar * click_to_activate_pref; + + /* Use the default double click behavior if caja isn't installed */ + if (gsearch->caja_schema_exists == FALSE) { + gsearch->is_search_results_single_click_to_activate = FALSE; + return; + } + + g_signal_connect (gsearch->caja_settings, + "changed::click-policy", + G_CALLBACK (single_click_to_activate_key_changed_cb), + gsearch); + + /* Get value of caja click behavior (single or double click to activate items) */ + click_to_activate_pref = g_settings_get_string (gsearch->caja_settings, "click-policy"); + + gsearch->is_search_results_single_click_to_activate = + (strncmp (click_to_activate_pref, "single", 6) == 0) ? TRUE : FALSE; + + g_free (click_to_activate_pref); +} + +int +main (int argc, + char * argv[]) +{ + GSearchWindow * gsearch; + GOptionContext * context; + GtkWidget * window; + GError * error = NULL; + EggSMClient * client; + + setlocale (LC_ALL, ""); + bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + context = g_option_context_new (N_("- the MATE Search Tool")); + g_option_context_set_translation_domain(context, GETTEXT_PACKAGE); + gsearch_setup_goption_descriptions (); + g_option_context_add_main_entries (context, GSearchGOptionEntries, GETTEXT_PACKAGE); + g_option_context_add_group (context, gtk_get_option_group (TRUE)); + g_option_context_add_group (context, egg_sm_client_get_option_group ()); + g_option_context_parse (context, &argc, &argv, &error); + + if (error) { + g_printerr (_("Failed to parse command line arguments: %s\n"), error->message); + return (-1); + } + + g_option_context_free (context); + + g_set_application_name (_("Search for Files")); + gtk_window_set_default_icon_name (MATE_SEARCH_TOOL_ICON); + + gsearchtool_init_stock_icons (); + + window = g_object_new (GSEARCH_TYPE_WINDOW, NULL); + gsearch = GSEARCH_WINDOW (window); + + gtk_window_set_wmclass (GTK_WINDOW (gsearch->window), "mate-search-tool", "mate-search-tool"); + gtk_window_set_resizable (GTK_WINDOW (gsearch->window), TRUE); + + g_signal_connect (G_OBJECT (gsearch->window), "delete_event", + G_CALLBACK (quit_cb), + (gpointer) gsearch); + g_signal_connect (G_OBJECT (gsearch->window), "key_press_event", + G_CALLBACK (key_press_cb), + (gpointer) gsearch); + g_signal_connect (G_OBJECT (gsearch->window), "window_state_event", + G_CALLBACK (window_state_event_cb), + (gpointer) gsearch); + + if ((client = egg_sm_client_get ()) != NULL) { + g_signal_connect (client, "save_state", + G_CALLBACK (save_session_cb), + (gpointer) gsearch); + g_signal_connect (client, "quit", + G_CALLBACK (quit_session_cb), + (gpointer) gsearch); + } + + gtk_widget_show (gsearch->window); + + gsearchtool_setup_gsettings_notifications (gsearch); + + if (handle_goption_args (gsearch) == FALSE) { + handle_gsettings_settings (gsearch); + } + + gtk_main (); + return 0; +} diff --git a/gsearchtool/src/gsearchtool.h b/gsearchtool/src/gsearchtool.h new file mode 100644 index 00000000..f48c13f2 --- /dev/null +++ b/gsearchtool/src/gsearchtool.h @@ -0,0 +1,243 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * MATE Search Tool + * + * File: gsearchtool.h + * + * (C) 1998,2002 the Free Software Foundation + * + * Authors: Dennis Cranston + * George Lebl + * + * 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 St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef _GSEARCHTOOL_H_ +#define _GSEARCHTOOL_H_ + +#ifdef __cplusplus +extern "C" { +#pragma } +#endif + +#include + +#define GSEARCH_TYPE_WINDOW gsearch_window_get_type() +#define GSEARCH_WINDOW(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSEARCH_TYPE_WINDOW, GSearchWindow)) +#define GSEARCH_WINDOW_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), GSEARCH_TYPE_WINDOW, GSearchWindowClass)) +#define GSEARCH_IS_WINDOW(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSEARCH_TYPE_WINDOW)) +#define GSEARCH_IS_WINDOW_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), GSEARCH_TYPE_WINDOW)) +#define GSEARCH_WINDOW_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), GSEARCH_TYPE_WINDOW, GSearchWindowClass)) + +#define MATE_SEARCH_TOOL_ICON "system-search" +#define MINIMUM_WINDOW_WIDTH 420 +#define MINIMUM_WINDOW_HEIGHT 310 +#define DEFAULT_WINDOW_WIDTH 554 +#define DEFAULT_WINDOW_HEIGHT 350 +#define WINDOW_HEIGHT_STEP 35 +#define NUM_VISIBLE_COLUMNS 5 +#define CAJA_PREFERENCES_SCHEMA "org.mate.caja.preferences" + +typedef enum { + STOPPED, + ABORTED, + RUNNING, + MAKE_IT_STOP, + MAKE_IT_QUIT +} GSearchCommandStatus; + +typedef enum { + SPEED_TRADEOFF_ALWAYS = 0, + SPEED_TRADEOFF_LOCAL_ONLY, + SPEED_TRADEOFF_NEVER +} CajaSpeedTradeoff; + +typedef enum { + CAJA_DATE_FORMAT_LOCALE = 0, + CAJA_DATE_FORMAT_ISO, + CAJA_DATE_FORMAT_INFORMAL +} CajaDateFormat; + +typedef enum { + COLUMN_ICON, + COLUMN_NAME, + COLUMN_RELATIVE_PATH, + COLUMN_LOCALE_FILE, + COLUMN_READABLE_SIZE, + COLUMN_SIZE, + COLUMN_TYPE, + COLUMN_READABLE_DATE, + COLUMN_DATE, + COLUMN_MONITOR, + COLUMN_NO_FILES_FOUND, + NUM_COLUMNS +} GSearchResultColumns; + +typedef struct _GSearchWindow GSearchWindow; +typedef struct _GSearchWindowClass GSearchWindowClass; +typedef struct _GSearchCommandDetails GSearchCommandDetails; +typedef struct _GSearchConstraint GSearchConstraint; +typedef struct _GSearchMonitor GSearchMonitor; + +struct _GSearchWindow { + GtkWindow parent_instance; + + GtkWidget * window; + GtkUIManager * window_ui_manager; + GdkGeometry window_geometry; + gint window_width; + gint window_height; + gboolean is_window_maximized; + gboolean is_window_accessible; + + GtkWidget * name_contains_entry; + GtkWidget * look_in_folder_button; + GtkWidget * name_and_folder_table; + GtkWidget * progress_spinner; + GtkWidget * find_button; + GtkWidget * stop_button; + GtkWidget * focus; + + GtkWidget * show_more_options_expander; + GtkWidget * available_options_vbox; + GtkWidget * available_options_label; + GtkWidget * available_options_combo_box; + GtkWidget * available_options_add_button; + GtkSizeGroup * available_options_button_size_group; + GList * available_options_selected_list; + + GtkWidget * files_found_label; + GtkWidget * search_results_vbox; + GtkWidget * search_results_popup_menu; + GtkWidget * search_results_popup_submenu; + GtkWidget * search_results_save_results_as_item; + GtkTreeView * search_results_tree_view; + GtkTreeViewColumn * search_results_folder_column; + GtkTreeViewColumn * search_results_size_column; + GtkTreeViewColumn * search_results_type_column; + GtkTreeViewColumn * search_results_date_column; + GtkListStore * search_results_list_store; + GtkCellRenderer * search_results_name_cell_renderer; + GtkTreeSelection * search_results_selection; + GtkTreeIter search_results_iter; + GtkTreePath * search_results_hover_path; + GHashTable * search_results_filename_hash_table; + GHashTable * search_results_pixbuf_hash_table; + CajaDateFormat search_results_date_format; + gint show_thumbnails_file_size_limit; + gboolean show_thumbnails; + gboolean is_search_results_single_click_to_activate; + gboolean is_locate_database_check_finished; + gboolean is_locate_database_available; + + gchar * save_results_as_default_filename; + + GSettings * mate_search_tool_settings; + GSettings * mate_search_tool_select_settings; + GSettings * mate_desktop_interface_settings; + GSettings * caja_settings; + gboolean caja_schema_exists; + + GSearchCommandDetails * command_details; +}; + +struct _GSearchCommandDetails { + pid_t command_pid; + GSearchCommandStatus command_status; + + gchar * name_contains_pattern_string; + gchar * name_contains_regex_string; + gchar * look_in_folder_string; + + gboolean is_command_first_pass; + gboolean is_command_using_quick_mode; + gboolean is_command_second_pass_enabled; + gboolean is_command_show_hidden_files_enabled; + gboolean is_command_regex_matching_enabled; + gboolean is_command_timeout_enabled; +}; + +struct _GSearchConstraint { + gint constraint_id; + union { + gchar * text; + gint time; + gint number; + } data; +}; + +struct _GSearchWindowClass { + GtkWindowClass parent_class; +}; + +struct _GSearchMonitor { + GSearchWindow * gsearch; + GtkTreeRowReference * reference; + GFileMonitor * handle; +}; + +GType +gsearch_window_get_type (void); + +gchar * +build_search_command (GSearchWindow * gsearch, + gboolean first_pass); +void +spawn_search_command (GSearchWindow * gsearch, + gchar * command); +void +add_constraint (GSearchWindow * gsearch, + gint constraint_id, + gchar * value, + gboolean show_constraint); +void +update_constraint_info (GSearchConstraint * constraint, + gchar * info); +void +remove_constraint (gint constraint_id); + +void +set_constraint_gsettings_boolean (gint constraint_id, + gboolean flag); +void +set_constraint_selected_state (GSearchWindow * gsearch, + gint constraint_id, + gboolean state); +void +set_clone_command (GSearchWindow * gsearch, + gint * argcp, + gchar *** argvp, + gpointer client_data, + gboolean escape_values); +void +update_search_counts (GSearchWindow * gsearch); + +gboolean +tree_model_iter_free_monitor (GtkTreeModel * model, + GtkTreePath * path, + GtkTreeIter * iter, + gpointer data); + +#ifdef __cplusplus +} +#endif + +#endif /* _GSEARCHTOOL_H_ */ -- cgit v1.2.1