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/Makefile.am | 70 +- gsearchtool/data/Makefile.am | 32 +- gsearchtool/data/mate-search-tool.1 | 110 + gsearchtool/data/mate-search-tool.convert | 27 + gsearchtool/data/mate-search-tool.desktop.in | 15 + .../data/org.mate.search-tool.gschema.xml.in | 117 + gsearchtool/gsearchtool-callbacks.c | 1953 ------------- gsearchtool/gsearchtool-callbacks.h | 180 -- gsearchtool/gsearchtool-support.c | 1588 ---------- gsearchtool/gsearchtool-support.h | 114 - gsearchtool/gsearchtool.c | 3070 -------------------- gsearchtool/gsearchtool.h | 243 -- gsearchtool/libeggsmclient/Makefile.am | 16 + gsearchtool/libeggsmclient/eggdesktopfile.c | 1469 ++++++++++ gsearchtool/libeggsmclient/eggdesktopfile.h | 159 + gsearchtool/libeggsmclient/eggsmclient-private.h | 56 + gsearchtool/libeggsmclient/eggsmclient-xsmp.c | 1372 +++++++++ gsearchtool/libeggsmclient/eggsmclient.c | 589 ++++ gsearchtool/libeggsmclient/eggsmclient.h | 117 + gsearchtool/mate-search-tool.1 | 110 - gsearchtool/mate-search-tool.convert | 27 - gsearchtool/mate-search-tool.desktop.in | 15 - gsearchtool/org.mate.search-tool.gschema.xml.in | 117 - 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 ++ 30 files changed, 11262 insertions(+), 7488 deletions(-) create mode 100644 gsearchtool/data/mate-search-tool.1 create mode 100644 gsearchtool/data/mate-search-tool.convert create mode 100644 gsearchtool/data/mate-search-tool.desktop.in create mode 100644 gsearchtool/data/org.mate.search-tool.gschema.xml.in delete mode 100644 gsearchtool/gsearchtool-callbacks.c delete mode 100644 gsearchtool/gsearchtool-callbacks.h delete mode 100644 gsearchtool/gsearchtool-support.c delete mode 100644 gsearchtool/gsearchtool-support.h delete mode 100644 gsearchtool/gsearchtool.c delete mode 100644 gsearchtool/gsearchtool.h create mode 100644 gsearchtool/libeggsmclient/Makefile.am create mode 100644 gsearchtool/libeggsmclient/eggdesktopfile.c create mode 100644 gsearchtool/libeggsmclient/eggdesktopfile.h create mode 100644 gsearchtool/libeggsmclient/eggsmclient-private.h create mode 100644 gsearchtool/libeggsmclient/eggsmclient-xsmp.c create mode 100644 gsearchtool/libeggsmclient/eggsmclient.c create mode 100644 gsearchtool/libeggsmclient/eggsmclient.h delete mode 100644 gsearchtool/mate-search-tool.1 delete mode 100644 gsearchtool/mate-search-tool.convert delete mode 100644 gsearchtool/mate-search-tool.desktop.in delete mode 100644 gsearchtool/org.mate.search-tool.gschema.xml.in 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') diff --git a/gsearchtool/Makefile.am b/gsearchtool/Makefile.am index acdf8748..145b98b5 100644 --- a/gsearchtool/Makefile.am +++ b/gsearchtool/Makefile.am @@ -1,70 +1,2 @@ -Utilitiesdir = $(datadir)/applications +SUBDIRS = data help libeggsmclient libmateui-deprecated src -SUBDIRS = data help libmateui-deprecated - -AM_CPPFLAGS = \ - -I$(srcdir)/libmateui-deprecated \ - -I$(top_srcdir)/libeggsmclient \ - $(DISABLE_DEPRECATED) \ - -DMATELOCALEDIR=\""$(prefix)/$(DATADIRNAME)/locale"\" \ - -DLOCALEDIR=\"$(datadir)/locale\" \ - -DDATADIR=\""$(datadir)"\" \ - -DGREP_COMMAND=\""$(GREP_COMMAND)"\" - -libmateui_deprecated_LIB = $(builddir)/libmateui-deprecated/libmateui-deprecated.la -libeggsmclient_LIB = $(top_builddir)/libeggsmclient/libeggsmclient.la - -bin_PROGRAMS = mate-search-tool - -gsettingsschema_in_files = org.mate.search-tool.gschema.xml.in -gsettings_SCHEMAS = $(gsettingsschema_in_files:.xml.in=.xml) -.PRECIOUS: $(gsettings_SCHEMAS) - -@INTLTOOL_XML_NOMERGE_RULE@ - -@GSETTINGS_RULES@ - -convertdir = $(datadir)/MateConf/gsettings -convert_DATA = mate-search-tool.convert - -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) \ - $(libmateui_deprecated_LIB) \ - $(libeggsmclient_LIB) - -man_MANS = mate-search-tool.1 - -Utilities_in_files = mate-search-tool.desktop.in -Utilities_DATA = $(Utilities_in_files:.desktop.in=.desktop) -@INTLTOOL_DESKTOP_RULE@ - -EXTRA_DIST = \ - $(Utilities_in_files) \ - $(Utilities_DATA) \ - $(convert_DATA) \ - $(gsettingsschema_in_files) \ - $(man_MANS) - -CLEANFILES = \ - mate-search-tool.desktop \ - $(gsettings_SCHEMAS) - -disthook: - cd $(distdir) ; rm -f $(CLEANFILES) diff --git a/gsearchtool/data/Makefile.am b/gsearchtool/data/Makefile.am index d5a3e9d3..725e18de 100644 --- a/gsearchtool/data/Makefile.am +++ b/gsearchtool/data/Makefile.am @@ -1,13 +1,41 @@ icondir = $(datadir)/pixmaps/mate-search-tool icon_DATA = thumbnail_frame.png +gsettingsschema_in_files = org.mate.search-tool.gschema.xml.in +gsettings_SCHEMAS = $(gsettingsschema_in_files:.xml.in=.xml) +.PRECIOUS: $(gsettings_SCHEMAS) + +@INTLTOOL_DESKTOP_RULE@ + +@GSETTINGS_RULES@ + +convertdir = $(datadir)/MateConf/gsettings +convert_DATA = mate-search-tool.convert + @INTLTOOL_XML_RULE@ appdatadir = $(datadir)/appdata appdata_in_files = mate-search-tool.appdata.xml.in appdata_DATA = $(appdata_in_files:.xml.in=.xml) -CLEANFILES = mate-search-tool.appdata.xml +man_MANS = mate-search-tool.1 + +Utilitiesdir = $(datadir)/applications +Utilities_in_files = mate-search-tool.desktop.in +Utilities_DATA = $(Utilities_in_files:.desktop.in=.desktop) EXTRA_DIST = \ $(icon_DATA) \ - $(appdata_in_files) + $(appdata_in_files) \ + $(Utilities_in_files) \ + $(Utilities_DATA) \ + $(convert_DATA) \ + $(gsettingsschema_in_files) \ + $(man_MANS) + +CLEANFILES = \ + mate-search-tool.appdata.xml \ + mate-search-tool.desktop \ + $(gsettings_SCHEMAS) + +disthook: + cd $(distdir) ; rm -f $(CLEANFILES) diff --git a/gsearchtool/data/mate-search-tool.1 b/gsearchtool/data/mate-search-tool.1 new file mode 100644 index 00000000..686a8cc9 --- /dev/null +++ b/gsearchtool/data/mate-search-tool.1 @@ -0,0 +1,110 @@ +.\" mate-search-tool.1 - MATE Search Tool +.\" Copyright 2001 Jochen Voss +.TH mate-search-tool 1 "March 16 2009" "mate-utils 2.27.1" +.SH NAME +mate-search-tool \- the MATE Search Tool +.SH SYNOPSIS +.B mate-search-tool +.RI [ options ] +.sp +or select +.B Search for Files... +from a +.B Main Menu +or from the +.B Places +menu in a +.B Menu Bar +.SH DESCRIPTION +.B MATE Search Tool +is a utility for finding files on your system. To perform a +basic search, you can type a filename or a partial filename, +with or without wildcards. To refine your search, you can +apply additional search options. + +.B MATE Search Tool +uses the find, grep, and locate UNIX commands. The case +sensitivity of the search depends on your operating +system. For example, on Linux, the find, grep, and locate +commands support the \-i option, so all searches are +case-insensitive. + +For full documentation see the +.B MATE Search Tool +online help. + +.SH OPTIONS +.TP +.BR \-\-help +Show help message +.TP +.BI "\-\-named=" STRING +Set the text of "Name contains" search option +.TP +.BI "\-\-path=" PATH +Set the text of "Look in folder" search option +.TP +.BI "\-\-sortby=" VALUE +Sort files by one of the following: name, folder, size, type, or date +.TP +.BR \-\-descending +Set sort order to descending, the default is ascending +.TP +.BR \-\-start +Automatically start a search +.TP +.BI "\-\-contains=" STRING +Select and set the "Contains the text" search option +.TP +.BI "\-\-mtimeless=" DAYS +Select and set the "Date modified less than" search option +.TP +.BI "\-\-mtimemore=" DAYS +Select and set the "Date modified more than" search option +.TP +.BI "\-\-sizemore=" KILOBYTES +Select and set the "Size at least" search option +.TP +.BI "\-\-sizeless=" KILOBYTES +Select and set the "Size at most" search option +.TP +.BR \-\-empty +Select the "File is empty" search option +.TP +.BI "\-\-user=" USER +Select and set the "Owned by user" search option +.TP +.BI "\-\-group=" GROUP +Select and set the "Owned by group" search option +.TP +.BR \-\-nouser +Select the "Owner is unrecognized" search option +.TP +.BI "\-\-notnamed=" STRING +Select and set the "Name does not contain" search option +.TP +.BI "\-\-regex=" PATTERN +Select and set the "Name matches regular expression" search option +.TP +.BR \-\-hidden +Select the "Show hidden and backup files" search option +.TP +.BR \-\-follow +Select the "Follow symbolic links" search option +.TP +.BR \-\-mounts +Select the "Exclude other filesystems" search option +.SH AUTHOR +.B MATE Search Tool +was originally written by George Lebl (). +Version 2 was written by Dennis M. Cranston +(). + +This manual page was originally written by Jochen Voss +. Version 2 was written by +Dennis M. Cranston (). + +.SH SEE ALSO +.BR find (1), +.BR locate (1), +.BR grep (1) diff --git a/gsearchtool/data/mate-search-tool.convert b/gsearchtool/data/mate-search-tool.convert new file mode 100644 index 00000000..44156425 --- /dev/null +++ b/gsearchtool/data/mate-search-tool.convert @@ -0,0 +1,27 @@ +[org.mate.search-tool] +show-additional-options=/apps/mate-search-tool/show_additional_options +disable-quick-search=/apps/mate-search-tool/disable_quick_search +quick-search-excluded-paths=/apps/mate-search-tool/quick_search_excluded_paths +disable-quick-search-second-scan=/apps/mate-search-tool/disable_quick_search_second-scan +quick-search-second-scan-excluded-paths=/apps/mate-search-tool/quick_search_second_scan_excluded_paths +columns-order=/apps/mate-search-tool/columns_order +default-window-width=/apps/mate-search-tool/default_window_width +default-window-height=/apps/mate-search-tool/default_window_height +default-window-maximized=/apps/mate-search-tool/default_window_maximized +look-in-folder=/apps/mate-search-tool/look_in_folder + +[org.mate.search-tool.select] +contains-the-text=/apps/mate-search-tool/select/contains_the_text +date-modified-less-than=/apps/mate-search-tool/select/date_modified_less_than +date-modified-more-than=/apps/mate-search-tool/select/date_modified_more_than +size-at-least=/apps/mate-search-tool/select/size_at_least +size-at-most=/apps/mate-search-tool/select/size_at_most +file-is-empty=/apps/mate-search-tool/select/file_is_empty +owned-by-user=/apps/mate-search-tool/select/owned_by_user +owned-by-group=/apps/mate-search-tool/select/owned_by_group +owner-is-unrecognized=/apps/mate-search-tool/select/owner_is_unrecognized +name-does-not-contain=/apps/mate-search-tool/select/name_does_not_contain +name-matches-regular-expression=/apps/mate-search-tool/select/name_matches_regular_expression +show-hidden-files-and-folders=/apps/mate-search-tool/select/show_hidden_files_and_folders +follow-symbolic-links=/apps/mate-search-tool/select/follow_symbolic_links +exclude-other-filesystems=/apps/mate-search-tool/select/exclude_other_filesystems diff --git a/gsearchtool/data/mate-search-tool.desktop.in b/gsearchtool/data/mate-search-tool.desktop.in new file mode 100644 index 00000000..56645ceb --- /dev/null +++ b/gsearchtool/data/mate-search-tool.desktop.in @@ -0,0 +1,15 @@ +[Desktop Entry] +_Name=MATE Search Tool +_Comment=Locate documents and folders on this computer by name or content +Exec=mate-search-tool +Icon=system-search +Terminal=false +Type=Application +StartupNotify=true +Categories=GTK;Utility;Core; +Keywords=MATE;search;files;locate;documents;folders;computer;name;content;find;tool; +OnlyShowIn=MATE; +X-MATE-DocPath=mate-search-tool/mate-search-tool.xml +X-MATE-Bugzilla-Bugzilla=MATE +X-MATE-Bugzilla-Product=mate-utils +X-MATE-Bugzilla-Component=gsearchtool diff --git a/gsearchtool/data/org.mate.search-tool.gschema.xml.in b/gsearchtool/data/org.mate.search-tool.gschema.xml.in new file mode 100644 index 00000000..b6a532c4 --- /dev/null +++ b/gsearchtool/data/org.mate.search-tool.gschema.xml.in @@ -0,0 +1,117 @@ + + + + [ ] + <_summary>Search history + <_description>This key defines the items which were searched for in the past. + + + false + <_summary>Show Additional Options + + + false + <_summary>Disable Quick Search + <_description>This key determines if the search tool disables the use of the locate command when performing simple file name searches. + + + [ '/mnt/*', '/media/*', '/dev/*', '/tmp/*', '/proc/*', '/var/*' ] + <_summary>Quick Search Excluded Paths + <_description>This key defines the paths the search tool will exclude from a quick search. The wildcards '*' and '?' are supported. The default values are /mnt/*, /media/*, /dev/*, /tmp/*, /proc/*, and /var/*. + + + false + <_summary>Disable Quick Search Second Scan + <_description>This key determines if the search tool disables the use of the find command after performing a quick search. + + + [ '/' ] + <_summary>Quick Search Second Scan Excluded Paths + <_description>This key defines the paths the search tool will exclude from a second scan when performing a quick search. The second scan uses the find command to search for files. The purpose of the second scan is to find files that have not been indexed. The wildcards '*' and '?' are supported. The default value is /. + + + [ 0 ] + <_summary>Search Result Columns Order + <_description>This key defines the order of the columns in the search results. This key should not be modified by the user. + + + -1 + <_summary>Default Window Width + <_description>This key defines the window width, and it's used to remember the size of the search tool between sessions. Setting it to -1 will make the search tool use the default width. + + + -1 + <_summary>Default Window Height + <_description>This key defines the window height, and it's used to remember the size of the search tool between sessions. Setting it to -1 will make the search tool use the default height. + + + false + <_summary>Default Window Maximized + <_description>This key determines if the search tool window starts in a maximized state. + + + '' + <_summary>Look in Folder + <_description>This key defines the default value of the "Look in Folder" widget. + + + + + + true + <_description>This key determines if the "Contains the text" search option is selected when the search tool is started. + + + false + <_description>This key determines if the "Date modified less than" search option is selected when the search tool is started. + + + false + <_description>This key determines if the "Date modified more than" search option is selected when the search tool is started. + + + false + <_description>This key determines if the "Size at least" search option is selected when the search tool is started. + + + false + <_description>This key determines if the "Size at most" search option is selected when the search tool is started. + + + false + <_description>This key determines if the "File is empty" search option is selected when the search tool is started. + + + false + <_description>This key determines if the "Owned by user" search option is selected when the search tool is started. + + + false + <_description>This key determines if the "Owned by group" search option is selected when the search tool is started. + + + false + <_description>This key determines if the "Owner is unrecognized" search option is selected when the search tool is started. + + + false + <_description>This key determines if the "Name does not contain" search option is selected when the search tool is started. + + + false + <_description>This key determines if the "Name matches regular expression" search option is selected when the search tool is started. + + + false + <_description>This key determines if the "Show hidden files and folders" search option is selected when the search tool is started. + + + false + <_description>This key determines if the "Follow symbolic links" search option is selected when the search tool is started. + + + false + <_description>This key determines if the "Exclude other filesystems" search option is selected when the search tool is started. + + + diff --git a/gsearchtool/gsearchtool-callbacks.c b/gsearchtool/gsearchtool-callbacks.c deleted file mode 100644 index 0c040867..00000000 --- a/gsearchtool/gsearchtool-callbacks.c +++ /dev/null @@ -1,1953 +0,0 @@ -/* -*- 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/gsearchtool-callbacks.h b/gsearchtool/gsearchtool-callbacks.h deleted file mode 100644 index b9db03a2..00000000 --- a/gsearchtool/gsearchtool-callbacks.h +++ /dev/null @@ -1,180 +0,0 @@ -/* -*- 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/gsearchtool-support.c b/gsearchtool/gsearchtool-support.c deleted file mode 100644 index a120967b..00000000 --- a/gsearchtool/gsearchtool-support.c +++ /dev/null @@ -1,1588 +0,0 @@ -/* -*- 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/gsearchtool-support.h b/gsearchtool/gsearchtool-support.h deleted file mode 100644 index 15585d5d..00000000 --- a/gsearchtool/gsearchtool-support.h +++ /dev/null @@ -1,114 +0,0 @@ -/* -*- 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/gsearchtool.c b/gsearchtool/gsearchtool.c deleted file mode 100644 index 757e8ca2..00000000 --- a/gsearchtool/gsearchtool.c +++ /dev/null @@ -1,3070 +0,0 @@ -/* -*- 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/gsearchtool.h b/gsearchtool/gsearchtool.h deleted file mode 100644 index f48c13f2..00000000 --- a/gsearchtool/gsearchtool.h +++ /dev/null @@ -1,243 +0,0 @@ -/* -*- 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_ */ diff --git a/gsearchtool/libeggsmclient/Makefile.am b/gsearchtool/libeggsmclient/Makefile.am new file mode 100644 index 00000000..cc64b8cb --- /dev/null +++ b/gsearchtool/libeggsmclient/Makefile.am @@ -0,0 +1,16 @@ +AM_CPPFLAGS = $(DISABLE_DEPRECATED) + +noinst_LTLIBRARIES = libeggsmclient.la + +libeggsmclient_la_LIBADD = $(LIBEGGSMCLIENT_LIBS) \ + -lSM -lICE +libeggsmclient_la_CFLAGS = $(LIBEGGSMCLIENT_CFLAGS) \ + -DEGG_SM_CLIENT_BACKEND_XSMP \ + -DG_LOG_DOMAIN=\""EggSMClient"\" \ + $(WARN_CFLAGS) +libeggsmclient_la_SOURCES = eggdesktopfile.h \ + eggdesktopfile.c \ + eggsmclient.h \ + eggsmclient.c \ + eggsmclient-private.h \ + eggsmclient-xsmp.c diff --git a/gsearchtool/libeggsmclient/eggdesktopfile.c b/gsearchtool/libeggsmclient/eggdesktopfile.c new file mode 100644 index 00000000..a96e5bd7 --- /dev/null +++ b/gsearchtool/libeggsmclient/eggdesktopfile.c @@ -0,0 +1,1469 @@ +/* eggdesktopfile.c - Freedesktop.Org Desktop Files + * Copyright (C) 2007 Novell, Inc. + * + * Based on mate-desktop-item.c + * Copyright (C) 1999, 2000 Red Hat Inc. + * Copyright (C) 2001 George Lebl + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING.LIB. If not, + * write to the Free Software Foundation, Inc., 51 Franklin St, + * Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "eggdesktopfile.h" + +#include +#include + +#include +#include +#include + +struct EggDesktopFile { + GKeyFile *key_file; + char *source; + + char *name, *icon; + EggDesktopFileType type; + char document_code; +}; + +/** + * egg_desktop_file_new: + * @desktop_file_path: path to a Freedesktop-style Desktop file + * @error: error pointer + * + * Creates a new #EggDesktopFile for @desktop_file. + * + * Return value: the new #EggDesktopFile, or %NULL on error. + **/ +EggDesktopFile * +egg_desktop_file_new (const char *desktop_file_path, GError **error) +{ + GKeyFile *key_file; + + key_file = g_key_file_new (); + if (!g_key_file_load_from_file (key_file, desktop_file_path, 0, error)) + { + g_key_file_free (key_file); + return NULL; + } + + return egg_desktop_file_new_from_key_file (key_file, desktop_file_path, + error); +} + +/** + * egg_desktop_file_new_from_data_dirs: + * @desktop_file_path: relative path to a Freedesktop-style Desktop file + * @error: error pointer + * + * Looks for @desktop_file_path in the paths returned from + * g_get_user_data_dir() and g_get_system_data_dirs(), and creates + * a new #EggDesktopFile from it. + * + * Return value: the new #EggDesktopFile, or %NULL on error. + **/ +EggDesktopFile * +egg_desktop_file_new_from_data_dirs (const char *desktop_file_path, + GError **error) +{ + EggDesktopFile *desktop_file; + GKeyFile *key_file; + char *full_path; + + key_file = g_key_file_new (); + if (!g_key_file_load_from_data_dirs (key_file, desktop_file_path, + &full_path, 0, error)) + { + g_key_file_free (key_file); + return NULL; + } + + desktop_file = egg_desktop_file_new_from_key_file (key_file, + full_path, + error); + g_free (full_path); + return desktop_file; +} + +/** + * egg_desktop_file_new_from_dirs: + * @desktop_file_path: relative path to a Freedesktop-style Desktop file + * @search_dirs: NULL-terminated array of directories to search + * @error: error pointer + * + * Looks for @desktop_file_path in the paths returned from + * g_get_user_data_dir() and g_get_system_data_dirs(), and creates + * a new #EggDesktopFile from it. + * + * Return value: the new #EggDesktopFile, or %NULL on error. + **/ +EggDesktopFile * +egg_desktop_file_new_from_dirs (const char *desktop_file_path, + const char **search_dirs, + GError **error) +{ + EggDesktopFile *desktop_file; + GKeyFile *key_file; + char *full_path; + + key_file = g_key_file_new (); + if (!g_key_file_load_from_dirs (key_file, desktop_file_path, search_dirs, + &full_path, 0, error)) + { + g_key_file_free (key_file); + return NULL; + } + + desktop_file = egg_desktop_file_new_from_key_file (key_file, + full_path, + error); + g_free (full_path); + return desktop_file; +} + +/** + * egg_desktop_file_new_from_key_file: + * @key_file: a #GKeyFile representing a desktop file + * @source: the path or URI that @key_file was loaded from, or %NULL + * @error: error pointer + * + * Creates a new #EggDesktopFile for @key_file. Assumes ownership of + * @key_file (on success or failure); you should consider @key_file to + * be freed after calling this function. + * + * Return value: the new #EggDesktopFile, or %NULL on error. + **/ +EggDesktopFile * +egg_desktop_file_new_from_key_file (GKeyFile *key_file, + const char *source, + GError **error) +{ + EggDesktopFile *desktop_file; + char *version, *type; + + if (!g_key_file_has_group (key_file, EGG_DESKTOP_FILE_GROUP)) + { + g_set_error (error, EGG_DESKTOP_FILE_ERROR, + EGG_DESKTOP_FILE_ERROR_INVALID, + _("File is not a valid .desktop file")); + g_key_file_free (key_file); + return NULL; + } + + version = g_key_file_get_value (key_file, EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_VERSION, + NULL); + if (version) + { + double version_num; + char *end; + + version_num = g_ascii_strtod (version, &end); + if (*end) + { + g_warning ("Invalid Version string '%s' in %s", + version, source ? source : "(unknown)"); + } + else if (version_num > 1.0) + { + g_set_error (error, EGG_DESKTOP_FILE_ERROR, + EGG_DESKTOP_FILE_ERROR_INVALID, + _("Unrecognized desktop file Version '%s'"), version); + g_free (version); + g_key_file_free (key_file); + return NULL; + } + g_free (version); + } + + desktop_file = g_new0 (EggDesktopFile, 1); + desktop_file->key_file = key_file; + + if (g_path_is_absolute (source)) + desktop_file->source = g_filename_to_uri (source, NULL, NULL); + else + desktop_file->source = g_strdup (source); + + desktop_file->name = g_key_file_get_string (key_file, EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_NAME, error); + if (!desktop_file->name) + { + egg_desktop_file_free (desktop_file); + return NULL; + } + + type = g_key_file_get_string (key_file, EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_TYPE, error); + if (!type) + { + egg_desktop_file_free (desktop_file); + return NULL; + } + + if (!strcmp (type, "Application")) + { + char *exec, *p; + + desktop_file->type = EGG_DESKTOP_FILE_TYPE_APPLICATION; + + exec = g_key_file_get_string (key_file, + EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_EXEC, + error); + if (!exec) + { + egg_desktop_file_free (desktop_file); + g_free (type); + return NULL; + } + + /* See if it takes paths or URIs or neither */ + for (p = exec; *p; p++) + { + if (*p == '%') + { + if (p[1] == '\0' || strchr ("FfUu", p[1])) + { + desktop_file->document_code = p[1]; + break; + } + p++; + } + } + + g_free (exec); + } + else if (!strcmp (type, "Link")) + { + char *url; + + desktop_file->type = EGG_DESKTOP_FILE_TYPE_LINK; + + url = g_key_file_get_string (key_file, + EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_URL, + error); + if (!url) + { + egg_desktop_file_free (desktop_file); + g_free (type); + return NULL; + } + g_free (url); + } + else if (!strcmp (type, "Directory")) + desktop_file->type = EGG_DESKTOP_FILE_TYPE_DIRECTORY; + else + desktop_file->type = EGG_DESKTOP_FILE_TYPE_UNRECOGNIZED; + + g_free (type); + + /* Check the Icon key */ + desktop_file->icon = g_key_file_get_string (key_file, + EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_ICON, + NULL); + if (desktop_file->icon && !g_path_is_absolute (desktop_file->icon)) + { + char *ext; + + /* Lots of .desktop files still get this wrong */ + ext = strrchr (desktop_file->icon, '.'); + if (ext && (!strcmp (ext, ".png") || + !strcmp (ext, ".xpm") || + !strcmp (ext, ".svg"))) + { + g_warning ("Desktop file '%s' has malformed Icon key '%s'" + "(should not include extension)", + source ? source : "(unknown)", + desktop_file->icon); + *ext = '\0'; + } + } + + return desktop_file; +} + +/** + * egg_desktop_file_free: + * @desktop_file: an #EggDesktopFile + * + * Frees @desktop_file. + **/ +void +egg_desktop_file_free (EggDesktopFile *desktop_file) +{ + g_key_file_free (desktop_file->key_file); + g_free (desktop_file->source); + g_free (desktop_file->name); + g_free (desktop_file->icon); + g_free (desktop_file); +} + +/** + * egg_desktop_file_get_source: + * @desktop_file: an #EggDesktopFile + * + * Gets the URI that @desktop_file was loaded from. + * + * Return value: @desktop_file's source URI + **/ +const char * +egg_desktop_file_get_source (EggDesktopFile *desktop_file) +{ + return desktop_file->source; +} + +/** + * egg_desktop_file_get_desktop_file_type: + * @desktop_file: an #EggDesktopFile + * + * Gets the desktop file type of @desktop_file. + * + * Return value: @desktop_file's type + **/ +EggDesktopFileType +egg_desktop_file_get_desktop_file_type (EggDesktopFile *desktop_file) +{ + return desktop_file->type; +} + +/** + * egg_desktop_file_get_name: + * @desktop_file: an #EggDesktopFile + * + * Gets the (localized) value of @desktop_file's "Name" key. + * + * Return value: the application/link name + **/ +const char * +egg_desktop_file_get_name (EggDesktopFile *desktop_file) +{ + return desktop_file->name; +} + +/** + * egg_desktop_file_get_icon: + * @desktop_file: an #EggDesktopFile + * + * Gets the value of @desktop_file's "Icon" key. + * + * If the icon string is a full path (that is, if g_path_is_absolute() + * returns %TRUE when called on it), it points to a file containing an + * unthemed icon. If the icon string is not a full path, it is the + * name of a themed icon, which can be looked up with %GtkIconTheme, + * or passed directly to a theme-aware widget like %GtkImage or + * %GtkCellRendererPixbuf. + * + * Return value: the icon path or name + **/ +const char * +egg_desktop_file_get_icon (EggDesktopFile *desktop_file) +{ + return desktop_file->icon; +} + +gboolean +egg_desktop_file_has_key (EggDesktopFile *desktop_file, + const char *key, + GError **error) +{ + return g_key_file_has_key (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, key, + error); +} + +char * +egg_desktop_file_get_string (EggDesktopFile *desktop_file, + const char *key, + GError **error) +{ + return g_key_file_get_string (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, key, + error); +} + +char * +egg_desktop_file_get_locale_string (EggDesktopFile *desktop_file, + const char *key, + const char *locale, + GError **error) +{ + return g_key_file_get_locale_string (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, key, locale, + error); +} + +gboolean +egg_desktop_file_get_boolean (EggDesktopFile *desktop_file, + const char *key, + GError **error) +{ + return g_key_file_get_boolean (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, key, + error); +} + +double +egg_desktop_file_get_numeric (EggDesktopFile *desktop_file, + const char *key, + GError **error) +{ + return g_key_file_get_double (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, key, + error); +} + +char ** +egg_desktop_file_get_string_list (EggDesktopFile *desktop_file, + const char *key, + gsize *length, + GError **error) +{ + return g_key_file_get_string_list (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, key, length, + error); +} + +char ** +egg_desktop_file_get_locale_string_list (EggDesktopFile *desktop_file, + const char *key, + const char *locale, + gsize *length, + GError **error) +{ + return g_key_file_get_locale_string_list (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, key, + locale, length, + error); +} + +/** + * egg_desktop_file_can_launch: + * @desktop_file: an #EggDesktopFile + * @desktop_environment: the name of the running desktop environment, + * or %NULL + * + * Tests if @desktop_file can/should be launched in the current + * environment. If @desktop_environment is non-%NULL, @desktop_file's + * "OnlyShowIn" and "NotShowIn" keys are checked to make sure that + * this desktop_file is appropriate for the named environment. + * + * Furthermore, if @desktop_file has type + * %EGG_DESKTOP_FILE_TYPE_APPLICATION, its "TryExec" key (if any) is + * also checked, to make sure the binary it points to exists. + * + * egg_desktop_file_can_launch() does NOT check the value of the + * "Hidden" key. + * + * Return value: %TRUE if @desktop_file can be launched + **/ +gboolean +egg_desktop_file_can_launch (EggDesktopFile *desktop_file, + const char *desktop_environment) +{ + char *try_exec, *found_program; + char **only_show_in, **not_show_in; + gboolean found; + int i; + + if (desktop_file->type != EGG_DESKTOP_FILE_TYPE_APPLICATION && + desktop_file->type != EGG_DESKTOP_FILE_TYPE_LINK) + return FALSE; + + if (desktop_environment) + { + only_show_in = g_key_file_get_string_list (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_ONLY_SHOW_IN, + NULL, NULL); + if (only_show_in) + { + for (i = 0, found = FALSE; only_show_in[i] && !found; i++) + { + if (!strcmp (only_show_in[i], desktop_environment)) + found = TRUE; + } + + g_strfreev (only_show_in); + + if (!found) + return FALSE; + } + + not_show_in = g_key_file_get_string_list (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_NOT_SHOW_IN, + NULL, NULL); + if (not_show_in) + { + for (i = 0, found = FALSE; not_show_in[i] && !found; i++) + { + if (!strcmp (not_show_in[i], desktop_environment)) + found = TRUE; + } + + g_strfreev (not_show_in); + + if (found) + return FALSE; + } + } + + if (desktop_file->type == EGG_DESKTOP_FILE_TYPE_APPLICATION) + { + try_exec = g_key_file_get_string (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_TRY_EXEC, + NULL); + if (try_exec) + { + found_program = g_find_program_in_path (try_exec); + g_free (try_exec); + + if (!found_program) + return FALSE; + g_free (found_program); + } + } + + return TRUE; +} + +/** + * egg_desktop_file_accepts_documents: + * @desktop_file: an #EggDesktopFile + * + * Tests if @desktop_file represents an application that can accept + * documents on the command line. + * + * Return value: %TRUE or %FALSE + **/ +gboolean +egg_desktop_file_accepts_documents (EggDesktopFile *desktop_file) +{ + return desktop_file->document_code != 0; +} + +/** + * egg_desktop_file_accepts_multiple: + * @desktop_file: an #EggDesktopFile + * + * Tests if @desktop_file can accept multiple documents at once. + * + * If this returns %FALSE, you can still pass multiple documents to + * egg_desktop_file_launch(), but that will result in multiple copies + * of the application being launched. See egg_desktop_file_launch() + * for more details. + * + * Return value: %TRUE or %FALSE + **/ +gboolean +egg_desktop_file_accepts_multiple (EggDesktopFile *desktop_file) +{ + return (desktop_file->document_code == 'F' || + desktop_file->document_code == 'U'); +} + +/** + * egg_desktop_file_accepts_uris: + * @desktop_file: an #EggDesktopFile + * + * Tests if @desktop_file can accept (non-"file:") URIs as documents to + * open. + * + * Return value: %TRUE or %FALSE + **/ +gboolean +egg_desktop_file_accepts_uris (EggDesktopFile *desktop_file) +{ + return (desktop_file->document_code == 'U' || + desktop_file->document_code == 'u'); +} + +static void +append_quoted_word (GString *str, + const char *s, + gboolean in_single_quotes, + gboolean in_double_quotes) +{ + const char *p; + + if (!in_single_quotes && !in_double_quotes) + g_string_append_c (str, '\''); + else if (!in_single_quotes && in_double_quotes) + g_string_append (str, "\"'"); + + if (!strchr (s, '\'')) + g_string_append (str, s); + else + { + for (p = s; *p != '\0'; p++) + { + if (*p == '\'') + g_string_append (str, "'\\''"); + else + g_string_append_c (str, *p); + } + } + + if (!in_single_quotes && !in_double_quotes) + g_string_append_c (str, '\''); + else if (!in_single_quotes && in_double_quotes) + g_string_append (str, "'\""); +} + +static void +do_percent_subst (EggDesktopFile *desktop_file, + char code, + GString *str, + GSList **documents, + gboolean in_single_quotes, + gboolean in_double_quotes) +{ + GSList *d; + char *doc; + + switch (code) + { + case '%': + g_string_append_c (str, '%'); + break; + + case 'F': + case 'U': + for (d = *documents; d; d = d->next) + { + doc = d->data; + g_string_append (str, " "); + append_quoted_word (str, doc, in_single_quotes, in_double_quotes); + } + *documents = NULL; + break; + + case 'f': + case 'u': + if (*documents) + { + doc = (*documents)->data; + g_string_append (str, " "); + append_quoted_word (str, doc, in_single_quotes, in_double_quotes); + *documents = (*documents)->next; + } + break; + + case 'i': + if (desktop_file->icon) + { + g_string_append (str, "--icon "); + append_quoted_word (str, desktop_file->icon, + in_single_quotes, in_double_quotes); + } + break; + + case 'c': + if (desktop_file->name) + { + append_quoted_word (str, desktop_file->name, + in_single_quotes, in_double_quotes); + } + break; + + case 'k': + if (desktop_file->source) + { + append_quoted_word (str, desktop_file->source, + in_single_quotes, in_double_quotes); + } + break; + + case 'D': + case 'N': + case 'd': + case 'n': + case 'v': + case 'm': + /* Deprecated; skip */ + break; + + default: + g_warning ("Unrecognized %%-code '%%%c' in Exec", code); + break; + } +} + +static char * +parse_exec (EggDesktopFile *desktop_file, + GSList **documents, + GError **error) +{ + char *exec, *p, *command; + gboolean escape, single_quot, double_quot; + GString *gs; + + exec = g_key_file_get_string (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_EXEC, + error); + if (!exec) + return NULL; + + /* Build the command */ + gs = g_string_new (NULL); + escape = single_quot = double_quot = FALSE; + + for (p = exec; *p != '\0'; p++) + { + if (escape) + { + escape = FALSE; + g_string_append_c (gs, *p); + } + else if (*p == '\\') + { + if (!single_quot) + escape = TRUE; + g_string_append_c (gs, *p); + } + else if (*p == '\'') + { + g_string_append_c (gs, *p); + if (!single_quot && !double_quot) + single_quot = TRUE; + else if (single_quot) + single_quot = FALSE; + } + else if (*p == '"') + { + g_string_append_c (gs, *p); + if (!single_quot && !double_quot) + double_quot = TRUE; + else if (double_quot) + double_quot = FALSE; + } + else if (*p == '%' && p[1]) + { + do_percent_subst (desktop_file, p[1], gs, documents, + single_quot, double_quot); + p++; + } + else + g_string_append_c (gs, *p); + } + + g_free (exec); + command = g_string_free (gs, FALSE); + + /* Prepend "xdg-terminal " if needed (FIXME: use gvfs) */ + if (g_key_file_has_key (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_TERMINAL, + NULL)) + { + GError *terminal_error = NULL; + gboolean use_terminal = + g_key_file_get_boolean (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_TERMINAL, + &terminal_error); + if (terminal_error) + { + g_free (command); + g_propagate_error (error, terminal_error); + return NULL; + } + + if (use_terminal) + { + gs = g_string_new ("xdg-terminal "); + append_quoted_word (gs, command, FALSE, FALSE); + g_free (command); + command = g_string_free (gs, FALSE); + } + } + + return command; +} + +static GSList * +translate_document_list (EggDesktopFile *desktop_file, GSList *documents) +{ + gboolean accepts_uris = egg_desktop_file_accepts_uris (desktop_file); + GSList *ret, *d; + + for (d = documents, ret = NULL; d; d = d->next) + { + const char *document = d->data; + gboolean is_uri = !g_path_is_absolute (document); + char *translated; + + if (accepts_uris) + { + if (is_uri) + translated = g_strdup (document); + else + translated = g_filename_to_uri (document, NULL, NULL); + } + else + { + if (is_uri) + translated = g_filename_from_uri (document, NULL, NULL); + else + translated = g_strdup (document); + } + + if (translated) + ret = g_slist_prepend (ret, translated); + } + + return g_slist_reverse (ret); +} + +static void +free_document_list (GSList *documents) +{ + GSList *d; + + for (d = documents; d; d = d->next) + g_free (d->data); + g_slist_free (documents); +} + +/** + * egg_desktop_file_parse_exec: + * @desktop_file: a #EggDesktopFile + * @documents: a list of document paths or URIs + * @error: error pointer + * + * Parses @desktop_file's Exec key, inserting @documents into it, and + * returns the result. + * + * If @documents contains non-file: URIs and @desktop_file does not + * accept URIs, those URIs will be ignored. Likewise, if @documents + * contains more elements than @desktop_file accepts, the extra + * documents will be ignored. + * + * Return value: the parsed Exec string + **/ +char * +egg_desktop_file_parse_exec (EggDesktopFile *desktop_file, + GSList *documents, + GError **error) +{ + GSList *translated, *docs; + char *command; + + docs = translated = translate_document_list (desktop_file, documents); + command = parse_exec (desktop_file, &docs, error); + free_document_list (translated); + + return command; +} + +static gboolean +parse_link (EggDesktopFile *desktop_file, + EggDesktopFile **app_desktop_file, + GSList **documents, + GError **error) +{ + char *url; + GKeyFile *key_file; + + url = g_key_file_get_string (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_URL, + error); + if (!url) + return FALSE; + *documents = g_slist_prepend (NULL, url); + + /* FIXME: use gvfs */ + key_file = g_key_file_new (); + g_key_file_set_string (key_file, EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_NAME, + "xdg-open"); + g_key_file_set_string (key_file, EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_TYPE, + "Application"); + g_key_file_set_string (key_file, EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_EXEC, + "xdg-open %u"); + *app_desktop_file = egg_desktop_file_new_from_key_file (key_file, NULL, NULL); + return TRUE; +} + +static char * +start_startup_notification (GdkDisplay *display, + EggDesktopFile *desktop_file, + const char *argv0, + int screen, + int workspace, + guint32 launch_time) +{ + static int sequence = 0; + char *startup_id; + char *description, *wmclass; + char *screen_str, *workspace_str; + + if (g_key_file_has_key (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_STARTUP_NOTIFY, + NULL)) + { + if (!g_key_file_get_boolean (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_STARTUP_NOTIFY, + NULL)) + return NULL; + wmclass = NULL; + } + else + { + wmclass = g_key_file_get_string (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_STARTUP_WM_CLASS, + NULL); + if (!wmclass) + return NULL; + } + + if (launch_time == (guint32)-1) + launch_time = gdk_x11_display_get_user_time (display); + startup_id = g_strdup_printf ("%s-%lu-%s-%s-%d_TIME%lu", + g_get_prgname (), + (unsigned long)getpid (), + g_get_host_name (), + argv0, + sequence++, + (unsigned long)launch_time); + + description = g_strdup_printf (_("Starting %s"), desktop_file->name); + screen_str = g_strdup_printf ("%d", screen); + workspace_str = workspace == -1 ? NULL : g_strdup_printf ("%d", workspace); + + gdk_x11_display_broadcast_startup_message (display, "new", + "ID", startup_id, + "NAME", desktop_file->name, + "SCREEN", screen_str, + "BIN", argv0, + "ICON", desktop_file->icon, + "DESKTOP", workspace_str, + "DESCRIPTION", description, + "WMCLASS", wmclass, + NULL); + + g_free (description); + g_free (wmclass); + g_free (screen_str); + g_free (workspace_str); + + return startup_id; +} + +static void +end_startup_notification (GdkDisplay *display, + const char *startup_id) +{ + gdk_x11_display_broadcast_startup_message (display, "remove", + "ID", startup_id, + NULL); +} + +#define EGG_DESKTOP_FILE_SN_TIMEOUT_LENGTH (30 /* seconds */) + +typedef struct { + GdkDisplay *display; + char *startup_id; +} StartupNotificationData; + +static gboolean +startup_notification_timeout (gpointer data) +{ + StartupNotificationData *sn_data = data; + + end_startup_notification (sn_data->display, sn_data->startup_id); + g_object_unref (sn_data->display); + g_free (sn_data->startup_id); + g_free (sn_data); + + return FALSE; +} + +static void +set_startup_notification_timeout (GdkDisplay *display, + const char *startup_id) +{ + StartupNotificationData *sn_data; + + sn_data = g_new (StartupNotificationData, 1); + sn_data->display = g_object_ref (display); + sn_data->startup_id = g_strdup (startup_id); + + g_timeout_add_seconds (EGG_DESKTOP_FILE_SN_TIMEOUT_LENGTH, + startup_notification_timeout, sn_data); +} + +static GPtrArray * +array_putenv (GPtrArray *env, char *variable) +{ + guint i, keylen; + + if (!env) + { + char **envp; + + env = g_ptr_array_new (); + + envp = g_listenv (); + for (i = 0; envp[i]; i++) + { + const char *value; + + value = g_getenv (envp[i]); + g_ptr_array_add (env, g_strdup_printf ("%s=%s", envp[i], + value ? value : "")); + } + g_strfreev (envp); + } + + keylen = strcspn (variable, "="); + + /* Remove old value of key */ + for (i = 0; i < env->len; i++) + { + char *envvar = env->pdata[i]; + + if (!strncmp (envvar, variable, keylen) && envvar[keylen] == '=') + { + g_free (envvar); + g_ptr_array_remove_index_fast (env, i); + break; + } + } + + /* Add new value */ + g_ptr_array_add (env, g_strdup (variable)); + + return env; +} + +static gboolean +egg_desktop_file_launchv (EggDesktopFile *desktop_file, + GSList *documents, va_list args, + GError **error) +{ + EggDesktopFileLaunchOption option; + GSList *translated_documents = NULL, *docs = NULL; + char *command, **argv; + int argc, i, screen_num; + gboolean success, current_success; + GdkDisplay *display; + char *startup_id; + + GPtrArray *env = NULL; + char **variables = NULL; + GdkScreen *screen = NULL; + int workspace = -1; + const char *directory = NULL; + guint32 launch_time = (guint32)-1; + GSpawnFlags flags = G_SPAWN_SEARCH_PATH; + GSpawnChildSetupFunc setup_func = NULL; + gpointer setup_data = NULL; + + GPid *ret_pid = NULL; + int *ret_stdin = NULL, *ret_stdout = NULL, *ret_stderr = NULL; + char **ret_startup_id = NULL; + + if (documents && desktop_file->document_code == 0) + { + g_set_error (error, EGG_DESKTOP_FILE_ERROR, + EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE, + _("Application does not accept documents on command line")); + return FALSE; + } + + /* Read the options: technically it's incorrect for the caller to + * NULL-terminate the list of options (rather than 0-terminating + * it), but NULL-terminating lets us use G_GNUC_NULL_TERMINATED, + * it's more consistent with other glib/gtk methods, and it will + * work as long as sizeof (int) <= sizeof (NULL), and NULL is + * represented as 0. (Which is true everywhere we care about.) + */ + while ((option = va_arg (args, EggDesktopFileLaunchOption))) + { + switch (option) + { + case EGG_DESKTOP_FILE_LAUNCH_CLEARENV: + if (env) + g_ptr_array_free (env, TRUE); + env = g_ptr_array_new (); + break; + case EGG_DESKTOP_FILE_LAUNCH_PUTENV: + variables = va_arg (args, char **); + for (i = 0; variables[i]; i++) + env = array_putenv (env, variables[i]); + break; + + case EGG_DESKTOP_FILE_LAUNCH_SCREEN: + screen = va_arg (args, GdkScreen *); + break; + case EGG_DESKTOP_FILE_LAUNCH_WORKSPACE: + workspace = va_arg (args, int); + break; + + case EGG_DESKTOP_FILE_LAUNCH_DIRECTORY: + directory = va_arg (args, const char *); + break; + case EGG_DESKTOP_FILE_LAUNCH_TIME: + launch_time = va_arg (args, guint32); + break; + case EGG_DESKTOP_FILE_LAUNCH_FLAGS: + flags |= va_arg (args, GSpawnFlags); + /* Make sure they didn't set any flags that don't make sense. */ + flags &= ~G_SPAWN_FILE_AND_ARGV_ZERO; + break; + case EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC: + setup_func = va_arg (args, GSpawnChildSetupFunc); + setup_data = va_arg (args, gpointer); + break; + + case EGG_DESKTOP_FILE_LAUNCH_RETURN_PID: + ret_pid = va_arg (args, GPid *); + break; + case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDIN_PIPE: + ret_stdin = va_arg (args, int *); + break; + case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDOUT_PIPE: + ret_stdout = va_arg (args, int *); + break; + case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDERR_PIPE: + ret_stderr = va_arg (args, int *); + break; + case EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID: + ret_startup_id = va_arg (args, char **); + break; + + default: + g_set_error (error, EGG_DESKTOP_FILE_ERROR, + EGG_DESKTOP_FILE_ERROR_UNRECOGNIZED_OPTION, + _("Unrecognized launch option: %d"), + GPOINTER_TO_INT (option)); + success = FALSE; + goto out; + } + } + + if (screen) + { + char *display_name = gdk_screen_make_display_name (screen); + char *display_env = g_strdup_printf ("DISPLAY=%s", display_name); + env = array_putenv (env, display_env); + g_free (display_name); + g_free (display_env); + + display = gdk_screen_get_display (screen); + } + else + { + display = gdk_display_get_default (); + screen = gdk_display_get_default_screen (display); + } + screen_num = gdk_screen_get_number (screen); + + translated_documents = translate_document_list (desktop_file, documents); + docs = translated_documents; + + success = FALSE; + + do + { + command = parse_exec (desktop_file, &docs, error); + if (!command) + goto out; + + if (!g_shell_parse_argv (command, &argc, &argv, error)) + { + g_free (command); + goto out; + } + g_free (command); + + startup_id = start_startup_notification (display, desktop_file, + argv[0], screen_num, + workspace, launch_time); + if (startup_id) + { + char *startup_id_env = g_strdup_printf ("DESKTOP_STARTUP_ID=%s", + startup_id); + env = array_putenv (env, startup_id_env); + g_free (startup_id_env); + } + + if (env != NULL) + g_ptr_array_add (env, NULL); + + current_success = + g_spawn_async_with_pipes (directory, + argv, + env ? (char **)(env->pdata) : NULL, + flags, + setup_func, setup_data, + ret_pid, + ret_stdin, ret_stdout, ret_stderr, + error); + g_strfreev (argv); + + if (startup_id) + { + if (current_success) + { + set_startup_notification_timeout (display, startup_id); + + if (ret_startup_id) + *ret_startup_id = startup_id; + else + g_free (startup_id); + } + else + g_free (startup_id); + } + else if (ret_startup_id) + *ret_startup_id = NULL; + + if (current_success) + { + /* If we successfully launch any instances of the app, make + * sure we return TRUE and don't set @error. + */ + success = TRUE; + error = NULL; + + /* Also, only set the output params on the first one */ + ret_pid = NULL; + ret_stdin = ret_stdout = ret_stderr = NULL; + ret_startup_id = NULL; + } + } + while (docs && current_success); + + out: + if (env) + { + g_strfreev ((char **)env->pdata); + g_ptr_array_free (env, FALSE); + } + free_document_list (translated_documents); + + return success; +} + +/** + * egg_desktop_file_launch: + * @desktop_file: an #EggDesktopFile + * @documents: a list of URIs or paths to documents to open + * @error: error pointer + * @...: additional options + * + * Launches @desktop_file with the given arguments. Additional options + * can be specified as follows: + * + * %EGG_DESKTOP_FILE_LAUNCH_CLEARENV: (no arguments) + * clears the environment in the child process + * %EGG_DESKTOP_FILE_LAUNCH_PUTENV: (char **variables) + * adds the NAME=VALUE strings in the given %NULL-terminated + * array to the child process's environment + * %EGG_DESKTOP_FILE_LAUNCH_SCREEN: (GdkScreen *screen) + * causes the application to be launched on the given screen + * %EGG_DESKTOP_FILE_LAUNCH_WORKSPACE: (int workspace) + * causes the application to be launched on the given workspace + * %EGG_DESKTOP_FILE_LAUNCH_DIRECTORY: (char *dir) + * causes the application to be launched in the given directory + * %EGG_DESKTOP_FILE_LAUNCH_TIME: (guint32 launch_time) + * sets the "launch time" for the application. If the user + * interacts with another window after @launch_time but before + * the launched application creates its first window, the window + * manager may choose to not give focus to the new application. + * Passing 0 for @launch_time will explicitly request that the + * application not receive focus. + * %EGG_DESKTOP_FILE_LAUNCH_FLAGS (GSpawnFlags flags) + * Sets additional #GSpawnFlags to use. See g_spawn_async() for + * more details. + * %EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC (GSpawnChildSetupFunc, gpointer) + * Sets the child setup callback and the data to pass to it. + * (See g_spawn_async() for more details.) + * + * %EGG_DESKTOP_FILE_LAUNCH_RETURN_PID (GPid **pid) + * On a successful launch, sets *@pid to the PID of the launched + * application. + * %EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID (char **startup_id) + * On a successful launch, sets *@startup_id to the Startup + * Notification "startup id" of the launched application. + * %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDIN_PIPE (int *fd) + * On a successful launch, sets *@fd to the file descriptor of + * a pipe connected to the application's stdin. + * %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDOUT_PIPE (int *fd) + * On a successful launch, sets *@fd to the file descriptor of + * a pipe connected to the application's stdout. + * %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDERR_PIPE (int *fd) + * On a successful launch, sets *@fd to the file descriptor of + * a pipe connected to the application's stderr. + * + * The options should be terminated with a single %NULL. + * + * If @documents contains multiple documents, but + * egg_desktop_file_accepts_multiple() returns %FALSE for + * @desktop_file, then egg_desktop_file_launch() will actually launch + * multiple instances of the application. In that case, the return + * value (as well as any values passed via + * %EGG_DESKTOP_FILE_LAUNCH_RETURN_PID, etc) will only reflect the + * first instance of the application that was launched (but the + * %EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC will be called for each + * instance). + * + * Return value: %TRUE if the application was successfully launched. + **/ +gboolean +egg_desktop_file_launch (EggDesktopFile *desktop_file, + GSList *documents, GError **error, + ...) +{ + va_list args; + gboolean success; + EggDesktopFile *app_desktop_file; + + switch (desktop_file->type) + { + case EGG_DESKTOP_FILE_TYPE_APPLICATION: + va_start (args, error); + success = egg_desktop_file_launchv (desktop_file, documents, + args, error); + va_end (args); + break; + + case EGG_DESKTOP_FILE_TYPE_LINK: + if (documents) + { + g_set_error (error, EGG_DESKTOP_FILE_ERROR, + EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE, + _("Can't pass document URIs to a 'Type=Link' desktop entry")); + return FALSE; + } + + if (!parse_link (desktop_file, &app_desktop_file, &documents, error)) + return FALSE; + + va_start (args, error); + success = egg_desktop_file_launchv (app_desktop_file, documents, + args, error); + va_end (args); + + egg_desktop_file_free (app_desktop_file); + free_document_list (documents); + break; + + default: + g_set_error (error, EGG_DESKTOP_FILE_ERROR, + EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE, + _("Not a launchable item")); + success = FALSE; + break; + } + + return success; +} + + +GQuark +egg_desktop_file_error_quark (void) +{ + return g_quark_from_static_string ("egg-desktop_file-error-quark"); +} + + +G_LOCK_DEFINE_STATIC (egg_desktop_file); +static EggDesktopFile *egg_desktop_file; + +/** + * egg_set_desktop_file: + * @desktop_file_path: path to the application's desktop file + * + * Creates an #EggDesktopFile for the application from the data at + * @desktop_file_path. This will also call g_set_application_name() + * with the localized application name from the desktop file, and + * gtk_window_set_default_icon_name() or + * gtk_window_set_default_icon_from_file() with the application's + * icon. Other code may use additional information from the desktop + * file. + * + * Note that for thread safety reasons, this function can only + * be called once. + **/ +void +egg_set_desktop_file (const char *desktop_file_path) +{ + GError *error = NULL; + + G_LOCK (egg_desktop_file); + if (egg_desktop_file) + egg_desktop_file_free (egg_desktop_file); + + egg_desktop_file = egg_desktop_file_new (desktop_file_path, &error); + if (error) + { + g_warning ("Could not load desktop file '%s': %s", + desktop_file_path, error->message); + g_error_free (error); + } + + if (egg_desktop_file) { + /* Set localized application name and default window icon */ + if (egg_desktop_file->name) + g_set_application_name (egg_desktop_file->name); + if (egg_desktop_file->icon) + { + if (g_path_is_absolute (egg_desktop_file->icon)) + gtk_window_set_default_icon_from_file (egg_desktop_file->icon, NULL); + else + gtk_window_set_default_icon_name (egg_desktop_file->icon); + } + } + + G_UNLOCK (egg_desktop_file); +} + +/** + * egg_get_desktop_file: + * + * Gets the application's #EggDesktopFile, as set by + * egg_set_desktop_file(). + * + * Return value: the #EggDesktopFile, or %NULL if it hasn't been set. + **/ +EggDesktopFile * +egg_get_desktop_file (void) +{ + EggDesktopFile *retval; + + G_LOCK (egg_desktop_file); + retval = egg_desktop_file; + G_UNLOCK (egg_desktop_file); + + return retval; +} diff --git a/gsearchtool/libeggsmclient/eggdesktopfile.h b/gsearchtool/libeggsmclient/eggdesktopfile.h new file mode 100644 index 00000000..9a5bd2e7 --- /dev/null +++ b/gsearchtool/libeggsmclient/eggdesktopfile.h @@ -0,0 +1,159 @@ +/* eggdesktopfile.h - Freedesktop.Org Desktop Files + * Copyright (C) 2007 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING.LIB. If not, + * write to the Free Software Foundation, Inc., 51 Franklin St, + * Fifth Floor, Suite 330, Boston, MA 02110-1301, USA. + */ + +#ifndef __EGG_DESKTOP_FILE_H__ +#define __EGG_DESKTOP_FILE_H__ + +#include + +G_BEGIN_DECLS + +typedef struct EggDesktopFile EggDesktopFile; + +typedef enum { + EGG_DESKTOP_FILE_TYPE_UNRECOGNIZED, + + EGG_DESKTOP_FILE_TYPE_APPLICATION, + EGG_DESKTOP_FILE_TYPE_LINK, + EGG_DESKTOP_FILE_TYPE_DIRECTORY +} EggDesktopFileType; + +EggDesktopFile *egg_desktop_file_new (const char *desktop_file_path, + GError **error); + +EggDesktopFile *egg_desktop_file_new_from_data_dirs (const char *desktop_file_path, + GError **error); +EggDesktopFile *egg_desktop_file_new_from_dirs (const char *desktop_file_path, + const char **search_dirs, + GError **error); +EggDesktopFile *egg_desktop_file_new_from_key_file (GKeyFile *key_file, + const char *source, + GError **error); + +void egg_desktop_file_free (EggDesktopFile *desktop_file); + +const char *egg_desktop_file_get_source (EggDesktopFile *desktop_file); + +EggDesktopFileType egg_desktop_file_get_desktop_file_type (EggDesktopFile *desktop_file); + +const char *egg_desktop_file_get_name (EggDesktopFile *desktop_file); +const char *egg_desktop_file_get_icon (EggDesktopFile *desktop_file); + +gboolean egg_desktop_file_can_launch (EggDesktopFile *desktop_file, + const char *desktop_environment); + +gboolean egg_desktop_file_accepts_documents (EggDesktopFile *desktop_file); +gboolean egg_desktop_file_accepts_multiple (EggDesktopFile *desktop_file); +gboolean egg_desktop_file_accepts_uris (EggDesktopFile *desktop_file); + +char *egg_desktop_file_parse_exec (EggDesktopFile *desktop_file, + GSList *documents, + GError **error); + +gboolean egg_desktop_file_launch (EggDesktopFile *desktop_file, + GSList *documents, + GError **error, + ...) G_GNUC_NULL_TERMINATED; + +typedef enum { + EGG_DESKTOP_FILE_LAUNCH_CLEARENV = 1, + EGG_DESKTOP_FILE_LAUNCH_PUTENV, + EGG_DESKTOP_FILE_LAUNCH_SCREEN, + EGG_DESKTOP_FILE_LAUNCH_WORKSPACE, + EGG_DESKTOP_FILE_LAUNCH_DIRECTORY, + EGG_DESKTOP_FILE_LAUNCH_TIME, + EGG_DESKTOP_FILE_LAUNCH_FLAGS, + EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC, + EGG_DESKTOP_FILE_LAUNCH_RETURN_PID, + EGG_DESKTOP_FILE_LAUNCH_RETURN_STDIN_PIPE, + EGG_DESKTOP_FILE_LAUNCH_RETURN_STDOUT_PIPE, + EGG_DESKTOP_FILE_LAUNCH_RETURN_STDERR_PIPE, + EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID +} EggDesktopFileLaunchOption; + +/* Standard Keys */ +#define EGG_DESKTOP_FILE_GROUP "Desktop Entry" + +#define EGG_DESKTOP_FILE_KEY_TYPE "Type" +#define EGG_DESKTOP_FILE_KEY_VERSION "Version" +#define EGG_DESKTOP_FILE_KEY_NAME "Name" +#define EGG_DESKTOP_FILE_KEY_GENERIC_NAME "GenericName" +#define EGG_DESKTOP_FILE_KEY_NO_DISPLAY "NoDisplay" +#define EGG_DESKTOP_FILE_KEY_COMMENT "Comment" +#define EGG_DESKTOP_FILE_KEY_ICON "Icon" +#define EGG_DESKTOP_FILE_KEY_HIDDEN "Hidden" +#define EGG_DESKTOP_FILE_KEY_ONLY_SHOW_IN "OnlyShowIn" +#define EGG_DESKTOP_FILE_KEY_NOT_SHOW_IN "NotShowIn" +#define EGG_DESKTOP_FILE_KEY_TRY_EXEC "TryExec" +#define EGG_DESKTOP_FILE_KEY_EXEC "Exec" +#define EGG_DESKTOP_FILE_KEY_PATH "Path" +#define EGG_DESKTOP_FILE_KEY_TERMINAL "Terminal" +#define EGG_DESKTOP_FILE_KEY_MIME_TYPE "MimeType" +#define EGG_DESKTOP_FILE_KEY_CATEGORIES "Categories" +#define EGG_DESKTOP_FILE_KEY_STARTUP_NOTIFY "StartupNotify" +#define EGG_DESKTOP_FILE_KEY_STARTUP_WM_CLASS "StartupWMClass" +#define EGG_DESKTOP_FILE_KEY_URL "URL" + +/* Accessors */ +gboolean egg_desktop_file_has_key (EggDesktopFile *desktop_file, + const char *key, + GError **error); +char *egg_desktop_file_get_string (EggDesktopFile *desktop_file, + const char *key, + GError **error) G_GNUC_MALLOC; +char *egg_desktop_file_get_locale_string (EggDesktopFile *desktop_file, + const char *key, + const char *locale, + GError **error) G_GNUC_MALLOC; +gboolean egg_desktop_file_get_boolean (EggDesktopFile *desktop_file, + const char *key, + GError **error); +double egg_desktop_file_get_numeric (EggDesktopFile *desktop_file, + const char *key, + GError **error); +char **egg_desktop_file_get_string_list (EggDesktopFile *desktop_file, + const char *key, + gsize *length, + GError **error) G_GNUC_MALLOC; +char **egg_desktop_file_get_locale_string_list (EggDesktopFile *desktop_file, + const char *key, + const char *locale, + gsize *length, + GError **error) G_GNUC_MALLOC; + + +/* Errors */ +#define EGG_DESKTOP_FILE_ERROR egg_desktop_file_error_quark() + +GQuark egg_desktop_file_error_quark (void); + +typedef enum { + EGG_DESKTOP_FILE_ERROR_INVALID, + EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE, + EGG_DESKTOP_FILE_ERROR_UNRECOGNIZED_OPTION +} EggDesktopFileError; + +/* Global application desktop file */ +void egg_set_desktop_file (const char *desktop_file_path); +EggDesktopFile *egg_get_desktop_file (void); + + +G_END_DECLS + +#endif /* __EGG_DESKTOP_FILE_H__ */ diff --git a/gsearchtool/libeggsmclient/eggsmclient-private.h b/gsearchtool/libeggsmclient/eggsmclient-private.h new file mode 100644 index 00000000..d13035bf --- /dev/null +++ b/gsearchtool/libeggsmclient/eggsmclient-private.h @@ -0,0 +1,56 @@ +/* eggsmclient-private.h + * Copyright (C) 2007 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __EGG_SM_CLIENT_PRIVATE_H__ +#define __EGG_SM_CLIENT_PRIVATE_H__ + +#include +#if !GTK_CHECK_VERSION (3, 0, 0) +#include +#endif +#include "eggsmclient.h" + +G_BEGIN_DECLS + +GKeyFile *egg_sm_client_save_state (EggSMClient *client); +void egg_sm_client_quit_requested (EggSMClient *client); +void egg_sm_client_quit_cancelled (EggSMClient *client); +void egg_sm_client_quit (EggSMClient *client); + +#if defined (GDK_WINDOWING_X11) +# ifdef EGG_SM_CLIENT_BACKEND_XSMP +GType egg_sm_client_xsmp_get_type (void); +EggSMClient *egg_sm_client_xsmp_new (void); +# endif +# ifdef EGG_SM_CLIENT_BACKEND_DBUS +GType egg_sm_client_dbus_get_type (void); +EggSMClient *egg_sm_client_dbus_new (void); +# endif +#elif defined (GDK_WINDOWING_WIN32) +GType egg_sm_client_win32_get_type (void); +EggSMClient *egg_sm_client_win32_new (void); +#elif defined (GDK_WINDOWING_QUARTZ) +GType egg_sm_client_osx_get_type (void); +EggSMClient *egg_sm_client_osx_new (void); +#endif + +G_END_DECLS + + +#endif /* __EGG_SM_CLIENT_PRIVATE_H__ */ diff --git a/gsearchtool/libeggsmclient/eggsmclient-xsmp.c b/gsearchtool/libeggsmclient/eggsmclient-xsmp.c new file mode 100644 index 00000000..0601cd98 --- /dev/null +++ b/gsearchtool/libeggsmclient/eggsmclient-xsmp.c @@ -0,0 +1,1372 @@ +/* + * Copyright (C) 2007 Novell, Inc. + * + * Inspired by various other pieces of code including GsmClient (C) + * 2001 Havoc Pennington, MateClient (C) 1998 Carsten Schaar, and twm + * session code (C) 1998 The Open Group. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#include "eggsmclient.h" +#include "eggsmclient-private.h" + +#include "eggdesktopfile.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#define EGG_TYPE_SM_CLIENT_XSMP (egg_sm_client_xsmp_get_type ()) +#define EGG_SM_CLIENT_XSMP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SM_CLIENT_XSMP, EggSMClientXSMP)) +#define EGG_SM_CLIENT_XSMP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_SM_CLIENT_XSMP, EggSMClientXSMPClass)) +#define EGG_IS_SM_CLIENT_XSMP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_SM_CLIENT_XSMP)) +#define EGG_IS_SM_CLIENT_XSMP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_SM_CLIENT_XSMP)) +#define EGG_SM_CLIENT_XSMP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_SM_CLIENT_XSMP, EggSMClientXSMPClass)) + +typedef struct _EggSMClientXSMP EggSMClientXSMP; +typedef struct _EggSMClientXSMPClass EggSMClientXSMPClass; + +/* These mostly correspond to the similarly-named states in section + * 9.1 of the XSMP spec. Some of the states there aren't represented + * here, because we don't need them. SHUTDOWN_CANCELLED is slightly + * different from the spec; we use it when the client is IDLE after a + * ShutdownCancelled message, but the application is still interacting + * and doesn't know the shutdown has been cancelled yet. + */ +typedef enum +{ + XSMP_STATE_IDLE, + XSMP_STATE_SAVE_YOURSELF, + XSMP_STATE_INTERACT_REQUEST, + XSMP_STATE_INTERACT, + XSMP_STATE_SAVE_YOURSELF_DONE, + XSMP_STATE_SHUTDOWN_CANCELLED, + XSMP_STATE_CONNECTION_CLOSED +} EggSMClientXSMPState; + +static const char *state_names[] = { + "idle", + "save-yourself", + "interact-request", + "interact", + "save-yourself-done", + "shutdown-cancelled", + "connection-closed" +}; + +#define EGG_SM_CLIENT_XSMP_STATE(xsmp) (state_names[(xsmp)->state]) + +struct _EggSMClientXSMP +{ + EggSMClient parent; + + SmcConn connection; + char *client_id; + + EggSMClientXSMPState state; + char **restart_command; + gboolean set_restart_command; + int restart_style; + + guint idle; + + /* Current SaveYourself state */ + guint expecting_initial_save_yourself : 1; + guint need_save_state : 1; + guint need_quit_requested : 1; + guint interact_errors : 1; + guint shutting_down : 1; + + /* Todo list */ + guint waiting_to_set_initial_properties : 1; + guint waiting_to_emit_quit : 1; + guint waiting_to_emit_quit_cancelled : 1; + guint waiting_to_save_myself : 1; + +}; + +struct _EggSMClientXSMPClass +{ + EggSMClientClass parent_class; + +}; + +static void sm_client_xsmp_startup (EggSMClient *client, + const char *client_id); +static void sm_client_xsmp_set_restart_command (EggSMClient *client, + int argc, + const char **argv); +static void sm_client_xsmp_will_quit (EggSMClient *client, + gboolean will_quit); +static gboolean sm_client_xsmp_end_session (EggSMClient *client, + EggSMClientEndStyle style, + gboolean request_confirmation); + +static void xsmp_save_yourself (SmcConn smc_conn, + SmPointer client_data, + int save_style, + Bool shutdown, + int interact_style, + Bool fast); +static void xsmp_die (SmcConn smc_conn, + SmPointer client_data); +static void xsmp_save_complete (SmcConn smc_conn, + SmPointer client_data); +static void xsmp_shutdown_cancelled (SmcConn smc_conn, + SmPointer client_data); +static void xsmp_interact (SmcConn smc_conn, + SmPointer client_data); + +static SmProp *array_prop (const char *name, + ...); +static SmProp *ptrarray_prop (const char *name, + GPtrArray *values); +static SmProp *string_prop (const char *name, + const char *value); +static SmProp *card8_prop (const char *name, + unsigned char value); + +static void set_properties (EggSMClientXSMP *xsmp, ...); +static void delete_properties (EggSMClientXSMP *xsmp, ...); + +static GPtrArray *generate_command (char **restart_command, + const char *client_id, + const char *state_file); + +static void save_state (EggSMClientXSMP *xsmp); +static void do_save_yourself (EggSMClientXSMP *xsmp); +static void update_pending_events (EggSMClientXSMP *xsmp); + +static void ice_init (void); +static gboolean process_ice_messages (IceConn ice_conn); +static void smc_error_handler (SmcConn smc_conn, + Bool swap, + int offending_minor_opcode, + unsigned long offending_sequence, + int error_class, + int severity, + SmPointer values); + +G_DEFINE_TYPE (EggSMClientXSMP, egg_sm_client_xsmp, EGG_TYPE_SM_CLIENT) + +static void +egg_sm_client_xsmp_init (EggSMClientXSMP *xsmp) +{ + xsmp->state = XSMP_STATE_CONNECTION_CLOSED; + xsmp->connection = NULL; + xsmp->restart_style = SmRestartIfRunning; +} + +static void +egg_sm_client_xsmp_class_init (EggSMClientXSMPClass *klass) +{ + EggSMClientClass *sm_client_class = EGG_SM_CLIENT_CLASS (klass); + + sm_client_class->startup = sm_client_xsmp_startup; + sm_client_class->set_restart_command = sm_client_xsmp_set_restart_command; + sm_client_class->will_quit = sm_client_xsmp_will_quit; + sm_client_class->end_session = sm_client_xsmp_end_session; +} + +EggSMClient * +egg_sm_client_xsmp_new (void) +{ + if (!g_getenv ("SESSION_MANAGER")) + return NULL; + + return g_object_new (EGG_TYPE_SM_CLIENT_XSMP, NULL); +} + +static gboolean +sm_client_xsmp_set_initial_properties (gpointer user_data) +{ + EggSMClientXSMP *xsmp = user_data; + EggDesktopFile *desktop_file; + GPtrArray *clone, *restart; + char pid_str[64]; + + if (xsmp->idle) + { + g_source_remove (xsmp->idle); + xsmp->idle = 0; + } + xsmp->waiting_to_set_initial_properties = FALSE; + + if (egg_sm_client_get_mode () == EGG_SM_CLIENT_MODE_NO_RESTART) + xsmp->restart_style = SmRestartNever; + + /* Parse info out of desktop file */ + desktop_file = egg_get_desktop_file (); + if (desktop_file) + { + GError *err = NULL; + char *cmdline, **argv; + int argc; + + if (xsmp->restart_style == SmRestartIfRunning) + { + if (egg_desktop_file_get_boolean (desktop_file, + "X-MATE-AutoRestart", NULL)) + xsmp->restart_style = SmRestartImmediately; + } + + if (!xsmp->set_restart_command) + { + cmdline = egg_desktop_file_parse_exec (desktop_file, NULL, &err); + if (cmdline && g_shell_parse_argv (cmdline, &argc, &argv, &err)) + { + egg_sm_client_set_restart_command (EGG_SM_CLIENT (xsmp), + argc, (const char **)argv); + g_strfreev (argv); + } + else + { + g_warning ("Could not parse Exec line in desktop file: %s", + err->message); + g_error_free (err); + } + g_free (cmdline); + } + } + + if (!xsmp->set_restart_command) + xsmp->restart_command = g_strsplit (g_get_prgname (), " ", -1); + + clone = generate_command (xsmp->restart_command, NULL, NULL); + restart = generate_command (xsmp->restart_command, xsmp->client_id, NULL); + + g_debug ("Setting initial properties"); + + /* Program, CloneCommand, RestartCommand, and UserID are required. + * ProcessID isn't required, but the SM may be able to do something + * useful with it. + */ + g_snprintf (pid_str, sizeof (pid_str), "%lu", (gulong) getpid ()); + set_properties (xsmp, + string_prop (SmProgram, g_get_prgname ()), + ptrarray_prop (SmCloneCommand, clone), + ptrarray_prop (SmRestartCommand, restart), + string_prop (SmUserID, g_get_user_name ()), + string_prop (SmProcessID, pid_str), + card8_prop (SmRestartStyleHint, xsmp->restart_style), + NULL); + g_ptr_array_free (clone, TRUE); + g_ptr_array_free (restart, TRUE); + + if (desktop_file) + { + set_properties (xsmp, + string_prop ("_GSM_DesktopFile", egg_desktop_file_get_source (desktop_file)), + NULL); + } + + update_pending_events (xsmp); + return FALSE; +} + +/* This gets called from two different places: xsmp_die() (when the + * server asks us to disconnect) and process_ice_messages() (when the + * server disconnects unexpectedly). + */ +static void +sm_client_xsmp_disconnect (EggSMClientXSMP *xsmp) +{ + SmcConn connection; + + if (!xsmp->connection) + return; + + g_debug ("Disconnecting"); + + connection = xsmp->connection; + xsmp->connection = NULL; + SmcCloseConnection (connection, 0, NULL); + xsmp->state = XSMP_STATE_CONNECTION_CLOSED; + + xsmp->waiting_to_save_myself = FALSE; + update_pending_events (xsmp); +} + +static void +sm_client_xsmp_startup (EggSMClient *client, + const char *client_id) +{ + EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client; + SmcCallbacks callbacks; + char *ret_client_id; + char error_string_ret[256]; + + xsmp->client_id = g_strdup (client_id); + + ice_init (); + SmcSetErrorHandler (smc_error_handler); + + callbacks.save_yourself.callback = xsmp_save_yourself; + callbacks.die.callback = xsmp_die; + callbacks.save_complete.callback = xsmp_save_complete; + callbacks.shutdown_cancelled.callback = xsmp_shutdown_cancelled; + + callbacks.save_yourself.client_data = xsmp; + callbacks.die.client_data = xsmp; + callbacks.save_complete.client_data = xsmp; + callbacks.shutdown_cancelled.client_data = xsmp; + + client_id = NULL; + error_string_ret[0] = '\0'; + xsmp->connection = + SmcOpenConnection (NULL, xsmp, SmProtoMajor, SmProtoMinor, + SmcSaveYourselfProcMask | SmcDieProcMask | + SmcSaveCompleteProcMask | + SmcShutdownCancelledProcMask, + &callbacks, + xsmp->client_id, &ret_client_id, + sizeof (error_string_ret), error_string_ret); + + if (!xsmp->connection) + { + g_warning ("Failed to connect to the session manager: %s\n", + error_string_ret[0] ? + error_string_ret : "no error message given"); + xsmp->state = XSMP_STATE_CONNECTION_CLOSED; + return; + } + + /* We expect a pointless initial SaveYourself if either (a) we + * didn't have an initial client ID, or (b) we DID have an initial + * client ID, but the server rejected it and gave us a new one. + */ + if (!xsmp->client_id || + (ret_client_id && strcmp (xsmp->client_id, ret_client_id) != 0)) + xsmp->expecting_initial_save_yourself = TRUE; + + if (ret_client_id) + { + g_free (xsmp->client_id); + xsmp->client_id = g_strdup (ret_client_id); + free (ret_client_id); + + gdk_threads_enter (); + gdk_x11_set_sm_client_id (xsmp->client_id); + gdk_threads_leave (); + + g_debug ("Got client ID \"%s\"", xsmp->client_id); + } + + xsmp->state = XSMP_STATE_IDLE; + + /* Do not set the initial properties until we reach the main loop, + * so that the application has a chance to call + * egg_set_desktop_file(). (This may also help the session manager + * have a better idea of when the application is fully up and + * running.) + */ + xsmp->waiting_to_set_initial_properties = TRUE; + xsmp->idle = g_idle_add (sm_client_xsmp_set_initial_properties, client); +} + +static void +sm_client_xsmp_set_restart_command (EggSMClient *client, + int argc, + const char **argv) +{ + EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client; + int i; + + g_strfreev (xsmp->restart_command); + + xsmp->restart_command = g_new (char *, argc + 1); + for (i = 0; i < argc; i++) + xsmp->restart_command[i] = g_strdup (argv[i]); + xsmp->restart_command[i] = NULL; + + xsmp->set_restart_command = TRUE; +} + +static void +sm_client_xsmp_will_quit (EggSMClient *client, + gboolean will_quit) +{ + EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client; + + if (xsmp->state == XSMP_STATE_CONNECTION_CLOSED) + { + /* The session manager has already exited! Schedule a quit + * signal. + */ + xsmp->waiting_to_emit_quit = TRUE; + update_pending_events (xsmp); + return; + } + else if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED) + { + /* We received a ShutdownCancelled message while the application + * was interacting; Schedule a quit_cancelled signal. + */ + xsmp->waiting_to_emit_quit_cancelled = TRUE; + update_pending_events (xsmp); + return; + } + + g_return_if_fail (xsmp->state == XSMP_STATE_INTERACT); + + g_debug ("Sending InteractDone(%s)", will_quit ? "False" : "True"); + SmcInteractDone (xsmp->connection, !will_quit); + + if (will_quit && xsmp->need_save_state) + save_state (xsmp); + + g_debug ("Sending SaveYourselfDone(%s)", will_quit ? "True" : "False"); + SmcSaveYourselfDone (xsmp->connection, will_quit); + xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE; +} + +static gboolean +sm_client_xsmp_end_session (EggSMClient *client, + EggSMClientEndStyle style, + gboolean request_confirmation) +{ + EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client; + int save_type; + + /* To end the session via XSMP, we have to send a + * SaveYourselfRequest. We aren't allowed to do that if anything + * else is going on, but we don't want to expose this fact to the + * application. So we do our best to patch things up here... + * + * In the worst case, this method might block for some length of + * time in process_ice_messages, but the only time that code path is + * honestly likely to get hit is if the application tries to end the + * session as the very first thing it does, in which case it + * probably won't actually block anyway. It's not worth gunking up + * the API to try to deal nicely with the other 0.01% of cases where + * this happens. + */ + + while (xsmp->state != XSMP_STATE_IDLE || + xsmp->expecting_initial_save_yourself) + { + /* If we're already shutting down, we don't need to do anything. */ + if (xsmp->shutting_down) + return TRUE; + + switch (xsmp->state) + { + case XSMP_STATE_CONNECTION_CLOSED: + return FALSE; + + case XSMP_STATE_SAVE_YOURSELF: + /* Trying to log out from the save_state callback? Whatever. + * Abort the save_state. + */ + SmcSaveYourselfDone (xsmp->connection, FALSE); + xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE; + break; + + case XSMP_STATE_INTERACT_REQUEST: + case XSMP_STATE_INTERACT: + case XSMP_STATE_SHUTDOWN_CANCELLED: + /* Already in a shutdown-related state, just ignore + * the new shutdown request... + */ + return TRUE; + + case XSMP_STATE_IDLE: + if (xsmp->waiting_to_set_initial_properties) + sm_client_xsmp_set_initial_properties (xsmp); + + if (!xsmp->expecting_initial_save_yourself) + break; + /* else fall through */ + + case XSMP_STATE_SAVE_YOURSELF_DONE: + /* We need to wait for some response from the server.*/ + process_ice_messages (SmcGetIceConnection (xsmp->connection)); + break; + + default: + /* Hm... shouldn't happen */ + return FALSE; + } + } + + /* xfce4-session will do the wrong thing if we pass SmSaveGlobal and + * the user chooses to save the session. But mate-session will do + * the wrong thing if we pass SmSaveBoth and the user chooses NOT to + * save the session... Sigh. + */ + if (!strcmp (SmcVendor (xsmp->connection), "xfce4-session")) + save_type = SmSaveBoth; + else + save_type = SmSaveGlobal; + + g_debug ("Sending SaveYourselfRequest(SmSaveGlobal, Shutdown, SmInteractStyleAny, %sFast)", request_confirmation ? "!" : ""); + SmcRequestSaveYourself (xsmp->connection, + save_type, + True, /* shutdown */ + SmInteractStyleAny, + !request_confirmation, /* fast */ + True /* global */); + return TRUE; +} + +static gboolean +idle_do_pending_events (gpointer data) +{ + EggSMClientXSMP *xsmp = data; + EggSMClient *client = data; + + gdk_threads_enter (); + + xsmp->idle = 0; + + if (xsmp->waiting_to_emit_quit) + { + xsmp->waiting_to_emit_quit = FALSE; + egg_sm_client_quit (client); + goto out; + } + + if (xsmp->waiting_to_emit_quit_cancelled) + { + xsmp->waiting_to_emit_quit_cancelled = FALSE; + egg_sm_client_quit_cancelled (client); + xsmp->state = XSMP_STATE_IDLE; + } + + if (xsmp->waiting_to_save_myself) + { + xsmp->waiting_to_save_myself = FALSE; + do_save_yourself (xsmp); + } + + out: + gdk_threads_leave (); + return FALSE; +} + +static void +update_pending_events (EggSMClientXSMP *xsmp) +{ + gboolean want_idle = + xsmp->waiting_to_emit_quit || + xsmp->waiting_to_emit_quit_cancelled || + xsmp->waiting_to_save_myself; + + if (want_idle) + { + if (xsmp->idle == 0) + xsmp->idle = g_idle_add (idle_do_pending_events, xsmp); + } + else + { + if (xsmp->idle != 0) + g_source_remove (xsmp->idle); + xsmp->idle = 0; + } +} + +static void +fix_broken_state (EggSMClientXSMP *xsmp, const char *message, + gboolean send_interact_done, + gboolean send_save_yourself_done) +{ + g_warning ("Received XSMP %s message in state %s: client or server error", + message, EGG_SM_CLIENT_XSMP_STATE (xsmp)); + + /* Forget any pending SaveYourself plans we had */ + xsmp->waiting_to_save_myself = FALSE; + update_pending_events (xsmp); + + if (send_interact_done) + SmcInteractDone (xsmp->connection, False); + if (send_save_yourself_done) + SmcSaveYourselfDone (xsmp->connection, True); + + xsmp->state = send_save_yourself_done ? XSMP_STATE_SAVE_YOURSELF_DONE : XSMP_STATE_IDLE; +} + +/* SM callbacks */ + +static void +xsmp_save_yourself (SmcConn smc_conn, + SmPointer client_data, + int save_type, + Bool shutdown, + int interact_style, + Bool fast) +{ + EggSMClientXSMP *xsmp = client_data; + gboolean wants_quit_requested; + + g_debug ("Received SaveYourself(%s, %s, %s, %s) in state %s", + save_type == SmSaveLocal ? "SmSaveLocal" : + save_type == SmSaveGlobal ? "SmSaveGlobal" : "SmSaveBoth", + shutdown ? "Shutdown" : "!Shutdown", + interact_style == SmInteractStyleAny ? "SmInteractStyleAny" : + interact_style == SmInteractStyleErrors ? "SmInteractStyleErrors" : + "SmInteractStyleNone", fast ? "Fast" : "!Fast", + EGG_SM_CLIENT_XSMP_STATE (xsmp)); + + if (xsmp->state != XSMP_STATE_IDLE && + xsmp->state != XSMP_STATE_SHUTDOWN_CANCELLED) + { + fix_broken_state (xsmp, "SaveYourself", FALSE, TRUE); + return; + } + + if (xsmp->waiting_to_set_initial_properties) + sm_client_xsmp_set_initial_properties (xsmp); + + /* If this is the initial SaveYourself, ignore it; we've already set + * properties and there's no reason to actually save state too. + */ + if (xsmp->expecting_initial_save_yourself) + { + xsmp->expecting_initial_save_yourself = FALSE; + + if (save_type == SmSaveLocal && + interact_style == SmInteractStyleNone && + !shutdown && !fast) + { + g_debug ("Sending SaveYourselfDone(True) for initial SaveYourself"); + SmcSaveYourselfDone (xsmp->connection, True); + /* As explained in the comment at the end of + * do_save_yourself(), SAVE_YOURSELF_DONE is the correct + * state here, not IDLE. + */ + xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE; + return; + } + else + g_warning ("First SaveYourself was not the expected one!"); + } + + /* Even ignoring the "fast" flag completely, there are still 18 + * different combinations of save_type, shutdown and interact_style. + * We interpret them as follows: + * + * Type Shutdown Interact Interpretation + * G F A/E/N do nothing (1) + * G T N do nothing (1)* + * G T A/E quit_requested (2) + * L/B F A/E/N save_state (3) + * L/B T N save_state (3)* + * L/B T A/E quit_requested, then save_state (4) + * + * 1. Do nothing, because the SM asked us to do something + * uninteresting (save open files, but then don't quit + * afterward) or rude (save open files without asking the user + * for confirmation). + * + * 2. Request interaction and then emit ::quit_requested. This + * perhaps isn't quite correct for the SmInteractStyleErrors + * case, but we don't care. + * + * 3. Emit ::save_state. The SmSaveBoth SaveYourselfs in these + * rows essentially get demoted to SmSaveLocal, because their + * Global halves correspond to "do nothing". + * + * 4. Request interaction, emit ::quit_requested, and then emit + * ::save_state after interacting. This is the SmSaveBoth + * equivalent of #2, but we also promote SmSaveLocal shutdown + * SaveYourselfs to SmSaveBoth here, because we want to give + * the user a chance to save open files before quitting. + * + * (* It would be nice if we could do something useful when the + * session manager sends a SaveYourself with shutdown True and + * SmInteractStyleNone. But we can't, so we just pretend it didn't + * even tell us it was shutting down. The docs for ::quit mention + * that it might not always be preceded by ::quit_requested.) + */ + + /* As an optimization, we don't actually request interaction and + * emit ::quit_requested if the application isn't listening to the + * signal. + */ + wants_quit_requested = g_signal_has_handler_pending (xsmp, g_signal_lookup ("quit_requested", EGG_TYPE_SM_CLIENT), 0, FALSE); + + xsmp->need_save_state = (save_type != SmSaveGlobal); + xsmp->need_quit_requested = (shutdown && wants_quit_requested && + interact_style != SmInteractStyleNone); + xsmp->interact_errors = (interact_style == SmInteractStyleErrors); + + xsmp->shutting_down = shutdown; + + do_save_yourself (xsmp); +} + +static void +do_save_yourself (EggSMClientXSMP *xsmp) +{ + if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED) + { + /* The SM cancelled a previous SaveYourself, but we haven't yet + * had a chance to tell the application, so we can't start + * processing this SaveYourself yet. + */ + xsmp->waiting_to_save_myself = TRUE; + update_pending_events (xsmp); + return; + } + + if (xsmp->need_quit_requested) + { + xsmp->state = XSMP_STATE_INTERACT_REQUEST; + + g_debug ("Sending InteractRequest(%s)", + xsmp->interact_errors ? "Error" : "Normal"); + SmcInteractRequest (xsmp->connection, + xsmp->interact_errors ? SmDialogError : SmDialogNormal, + xsmp_interact, + xsmp); + return; + } + + if (xsmp->need_save_state) + { + save_state (xsmp); + + /* Though unlikely, the client could have been disconnected + * while the application was saving its state. + */ + if (!xsmp->connection) + return; + } + + g_debug ("Sending SaveYourselfDone(True)"); + SmcSaveYourselfDone (xsmp->connection, True); + + /* The client state diagram in the XSMP spec says that after a + * non-shutdown SaveYourself, we go directly back to "idle". But + * everything else in both the XSMP spec and the libSM docs + * disagrees. + */ + xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE; +} + +static void +save_state (EggSMClientXSMP *xsmp) +{ + GKeyFile *state_file; + char *state_file_path, *data; + EggDesktopFile *desktop_file; + GPtrArray *restart; + int offset, fd; + + /* We set xsmp->state before emitting save_state, but our caller is + * responsible for setting it back afterward. + */ + xsmp->state = XSMP_STATE_SAVE_YOURSELF; + + state_file = egg_sm_client_save_state ((EggSMClient *)xsmp); + if (!state_file) + { + restart = generate_command (xsmp->restart_command, xsmp->client_id, NULL); + set_properties (xsmp, + ptrarray_prop (SmRestartCommand, restart), + NULL); + g_ptr_array_free (restart, TRUE); + delete_properties (xsmp, SmDiscardCommand, NULL); + return; + } + + desktop_file = egg_get_desktop_file (); + if (desktop_file) + { + GKeyFile *merged_file; + char *desktop_file_path; + + merged_file = g_key_file_new (); + desktop_file_path = + g_filename_from_uri (egg_desktop_file_get_source (desktop_file), + NULL, NULL); + if (desktop_file_path && + g_key_file_load_from_file (merged_file, desktop_file_path, + G_KEY_FILE_KEEP_COMMENTS | + G_KEY_FILE_KEEP_TRANSLATIONS, NULL)) + { + guint g, k, i; + char **groups, **keys, *value, *exec; + + groups = g_key_file_get_groups (state_file, NULL); + for (g = 0; groups[g]; g++) + { + keys = g_key_file_get_keys (state_file, groups[g], NULL, NULL); + for (k = 0; keys[k]; k++) + { + value = g_key_file_get_value (state_file, groups[g], + keys[k], NULL); + if (value) + { + g_key_file_set_value (merged_file, groups[g], + keys[k], value); + g_free (value); + } + } + g_strfreev (keys); + } + g_strfreev (groups); + + g_key_file_free (state_file); + state_file = merged_file; + + /* Update Exec key using "--sm-client-state-file %k" */ + restart = generate_command (xsmp->restart_command, + NULL, "%k"); + for (i = 0; i < restart->len; i++) + restart->pdata[i] = g_shell_quote (restart->pdata[i]); + g_ptr_array_add (restart, NULL); + exec = g_strjoinv (" ", (char **)restart->pdata); + g_strfreev ((char **)restart->pdata); + g_ptr_array_free (restart, FALSE); + + g_key_file_set_string (state_file, EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_EXEC, + exec); + g_free (exec); + } + else + desktop_file = NULL; + + g_free (desktop_file_path); + } + + /* Now write state_file to disk. (We can't use mktemp(), because + * that requires the filename to end with "XXXXXX", and we want + * it to end with ".desktop".) + */ + + data = g_key_file_to_data (state_file, NULL, NULL); + g_key_file_free (state_file); + + offset = 0; + while (1) + { + state_file_path = g_strdup_printf ("%s%csession-state%c%s-%ld.%s", + g_get_user_config_dir (), + G_DIR_SEPARATOR, G_DIR_SEPARATOR, + g_get_prgname (), + (long)time (NULL) + offset, + desktop_file ? "desktop" : "state"); + + fd = open (state_file_path, O_WRONLY | O_CREAT | O_EXCL, 0644); + if (fd == -1) + { + if (errno == EEXIST) + { + offset++; + g_free (state_file_path); + continue; + } + else if (errno == ENOTDIR || errno == ENOENT) + { + char *sep = strrchr (state_file_path, G_DIR_SEPARATOR); + + *sep = '\0'; + if (g_mkdir_with_parents (state_file_path, 0755) != 0) + { + g_warning ("Could not create directory '%s'", + state_file_path); + g_free (state_file_path); + state_file_path = NULL; + break; + } + + continue; + } + + g_warning ("Could not create file '%s': %s", + state_file_path, g_strerror (errno)); + g_free (state_file_path); + state_file_path = NULL; + break; + } + + close (fd); + g_file_set_contents (state_file_path, data, -1, NULL); + break; + } + g_free (data); + + restart = generate_command (xsmp->restart_command, xsmp->client_id, + state_file_path); + set_properties (xsmp, + ptrarray_prop (SmRestartCommand, restart), + NULL); + g_ptr_array_free (restart, TRUE); + + if (state_file_path) + { + set_properties (xsmp, + array_prop (SmDiscardCommand, + "/bin/rm", "-rf", state_file_path, + NULL), + NULL); + g_free (state_file_path); + } +} + +static void +xsmp_interact (SmcConn smc_conn, + SmPointer client_data) +{ + EggSMClientXSMP *xsmp = client_data; + EggSMClient *client = client_data; + + g_debug ("Received Interact message in state %s", + EGG_SM_CLIENT_XSMP_STATE (xsmp)); + + if (xsmp->state != XSMP_STATE_INTERACT_REQUEST) + { + fix_broken_state (xsmp, "Interact", TRUE, TRUE); + return; + } + + xsmp->state = XSMP_STATE_INTERACT; + egg_sm_client_quit_requested (client); +} + +static void +xsmp_die (SmcConn smc_conn, + SmPointer client_data) +{ + EggSMClientXSMP *xsmp = client_data; + EggSMClient *client = client_data; + + g_debug ("Received Die message in state %s", + EGG_SM_CLIENT_XSMP_STATE (xsmp)); + + sm_client_xsmp_disconnect (xsmp); + egg_sm_client_quit (client); +} + +static void +xsmp_save_complete (SmcConn smc_conn, + SmPointer client_data) +{ + EggSMClientXSMP *xsmp = client_data; + + g_debug ("Received SaveComplete message in state %s", + EGG_SM_CLIENT_XSMP_STATE (xsmp)); + + if (xsmp->state == XSMP_STATE_SAVE_YOURSELF_DONE) + xsmp->state = XSMP_STATE_IDLE; + else + fix_broken_state (xsmp, "SaveComplete", FALSE, FALSE); +} + +static void +xsmp_shutdown_cancelled (SmcConn smc_conn, + SmPointer client_data) +{ + EggSMClientXSMP *xsmp = client_data; + EggSMClient *client = client_data; + + g_debug ("Received ShutdownCancelled message in state %s", + EGG_SM_CLIENT_XSMP_STATE (xsmp)); + + xsmp->shutting_down = FALSE; + + if (xsmp->state == XSMP_STATE_SAVE_YOURSELF_DONE) + { + /* We've finished interacting and now the SM has agreed to + * cancel the shutdown. + */ + xsmp->state = XSMP_STATE_IDLE; + egg_sm_client_quit_cancelled (client); + } + else if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED) + { + /* Hm... ok, so we got a shutdown SaveYourself, which got + * cancelled, but the application was still interacting, so we + * didn't tell it yet, and then *another* SaveYourself arrived, + * which we must still be waiting to tell the app about, except + * that now that SaveYourself has been cancelled too! Dizzy yet? + */ + xsmp->waiting_to_save_myself = FALSE; + update_pending_events (xsmp); + } + else + { + g_debug ("Sending SaveYourselfDone(False)"); + SmcSaveYourselfDone (xsmp->connection, False); + + if (xsmp->state == XSMP_STATE_INTERACT) + { + /* The application is currently interacting, so we can't + * tell it about the cancellation yet; we will wait until + * after it calls egg_sm_client_will_quit(). + */ + xsmp->state = XSMP_STATE_SHUTDOWN_CANCELLED; + } + else + { + /* The shutdown was cancelled before the application got a + * chance to interact. + */ + xsmp->state = XSMP_STATE_IDLE; + } + } +} + +/* Utilities */ + +/* Create a restart/clone/Exec command based on @restart_command. + * If @client_id is non-%NULL, add "--sm-client-id @client_id". + * If @state_file is non-%NULL, add "--sm-client-state-file @state_file". + * + * None of the input strings are g_strdup()ed; the caller must keep + * them around until it is done with the returned GPtrArray, and must + * then free the array, but not its contents. + */ +static GPtrArray * +generate_command (char **restart_command, const char *client_id, + const char *state_file) +{ + GPtrArray *cmd; + int i; + + cmd = g_ptr_array_new (); + g_ptr_array_add (cmd, restart_command[0]); + + if (client_id) + { + g_ptr_array_add (cmd, "--sm-client-id"); + g_ptr_array_add (cmd, (char *)client_id); + } + + if (state_file) + { + g_ptr_array_add (cmd, "--sm-client-state-file"); + g_ptr_array_add (cmd, (char *)state_file); + } + + for (i = 1; restart_command[i]; i++) + g_ptr_array_add (cmd, restart_command[i]); + + return cmd; +} + +/* Takes a NULL-terminated list of SmProp * values, created by + * array_prop, ptrarray_prop, string_prop, card8_prop, sets them, and + * frees them. + */ +static void +set_properties (EggSMClientXSMP *xsmp, ...) +{ + GPtrArray *props; + SmProp *prop; + va_list ap; + guint i; + + props = g_ptr_array_new (); + + va_start (ap, xsmp); + while ((prop = va_arg (ap, SmProp *))) + g_ptr_array_add (props, prop); + va_end (ap); + + if (xsmp->connection) + { + SmcSetProperties (xsmp->connection, props->len, + (SmProp **)props->pdata); + } + + for (i = 0; i < props->len; i++) + { + prop = props->pdata[i]; + g_free (prop->vals); + g_free (prop); + } + g_ptr_array_free (props, TRUE); +} + +/* Takes a NULL-terminated list of property names and deletes them. */ +static void +delete_properties (EggSMClientXSMP *xsmp, ...) +{ + GPtrArray *props; + char *prop; + va_list ap; + + if (!xsmp->connection) + return; + + props = g_ptr_array_new (); + + va_start (ap, xsmp); + while ((prop = va_arg (ap, char *))) + g_ptr_array_add (props, prop); + va_end (ap); + + SmcDeleteProperties (xsmp->connection, props->len, + (char **)props->pdata); + + g_ptr_array_free (props, TRUE); +} + +/* Takes an array of strings and creates a LISTofARRAY8 property. The + * strings are neither dupped nor freed; they need to remain valid + * until you're done with the SmProp. + */ +static SmProp * +array_prop (const char *name, ...) +{ + SmProp *prop; + SmPropValue pv; + GArray *vals; + char *value; + va_list ap; + + prop = g_new (SmProp, 1); + prop->name = (char *)name; + prop->type = SmLISTofARRAY8; + + vals = g_array_new (FALSE, FALSE, sizeof (SmPropValue)); + + va_start (ap, name); + while ((value = va_arg (ap, char *))) + { + pv.length = strlen (value); + pv.value = value; + g_array_append_val (vals, pv); + } + va_end (ap); + + prop->num_vals = vals->len; + prop->vals = (SmPropValue *)vals->data; + + g_array_free (vals, FALSE); + + return prop; +} + +/* Takes a GPtrArray of strings and creates a LISTofARRAY8 property. + * The array contents are neither dupped nor freed; they need to + * remain valid until you're done with the SmProp. + */ +static SmProp * +ptrarray_prop (const char *name, GPtrArray *values) +{ + SmProp *prop; + SmPropValue pv; + GArray *vals; + guint i; + + prop = g_new (SmProp, 1); + prop->name = (char *)name; + prop->type = SmLISTofARRAY8; + + vals = g_array_new (FALSE, FALSE, sizeof (SmPropValue)); + + for (i = 0; i < values->len; i++) + { + pv.length = strlen (values->pdata[i]); + pv.value = values->pdata[i]; + g_array_append_val (vals, pv); + } + + prop->num_vals = vals->len; + prop->vals = (SmPropValue *)vals->data; + + g_array_free (vals, FALSE); + + return prop; +} + +/* Takes a string and creates an ARRAY8 property. The string is + * neither dupped nor freed; it needs to remain valid until you're + * done with the SmProp. + */ +static SmProp * +string_prop (const char *name, const char *value) +{ + SmProp *prop; + + prop = g_new (SmProp, 1); + prop->name = (char *)name; + prop->type = SmARRAY8; + + prop->num_vals = 1; + prop->vals = g_new (SmPropValue, 1); + + prop->vals[0].length = strlen (value); + prop->vals[0].value = (char *)value; + + return prop; +} + +/* Takes a char and creates a CARD8 property. */ +static SmProp * +card8_prop (const char *name, unsigned char value) +{ + SmProp *prop; + char *card8val; + + /* To avoid having to allocate and free prop->vals[0], we cheat and + * make vals a 2-element-long array and then use the second element + * to store value. + */ + + prop = g_new (SmProp, 1); + prop->name = (char *)name; + prop->type = SmCARD8; + + prop->num_vals = 1; + prop->vals = g_new (SmPropValue, 2); + card8val = (char *)(&prop->vals[1]); + card8val[0] = value; + + prop->vals[0].length = 1; + prop->vals[0].value = card8val; + + return prop; +} + +/* ICE code. This makes no effort to play nice with anyone else trying + * to use libICE. Fortunately, no one uses libICE for anything other + * than SM. (DCOP uses ICE, but it has its own private copy of + * libICE.) + * + * When this moves to gtk, it will need to be cleverer, to avoid + * tripping over old apps that use MateClient or that use libSM + * directly. + */ + +#include +#include + +static void ice_error_handler (IceConn ice_conn, + Bool swap, + int offending_minor_opcode, + unsigned long offending_sequence, + int error_class, + int severity, + IcePointer values); +static void ice_io_error_handler (IceConn ice_conn); +static void ice_connection_watch (IceConn ice_conn, + IcePointer client_data, + Bool opening, + IcePointer *watch_data); + +static void +ice_init (void) +{ + IceSetIOErrorHandler (ice_io_error_handler); + IceSetErrorHandler (ice_error_handler); + IceAddConnectionWatch (ice_connection_watch, NULL); +} + +static gboolean +process_ice_messages (IceConn ice_conn) +{ + IceProcessMessagesStatus status; + + gdk_threads_enter (); + status = IceProcessMessages (ice_conn, NULL, NULL); + gdk_threads_leave (); + + switch (status) + { + case IceProcessMessagesSuccess: + return TRUE; + + case IceProcessMessagesIOError: + sm_client_xsmp_disconnect (IceGetConnectionContext (ice_conn)); + return FALSE; + + case IceProcessMessagesConnectionClosed: + return FALSE; + + default: + g_assert_not_reached (); + } +} + +static gboolean +ice_iochannel_watch (GIOChannel *channel, + GIOCondition condition, + gpointer client_data) +{ + return process_ice_messages (client_data); +} + +static void +ice_connection_watch (IceConn ice_conn, + IcePointer client_data, + Bool opening, + IcePointer *watch_data) +{ + guint watch_id; + + if (opening) + { + GIOChannel *channel; + int fd = IceConnectionNumber (ice_conn); + + fcntl (fd, F_SETFD, fcntl (fd, F_GETFD, 0) | FD_CLOEXEC); + channel = g_io_channel_unix_new (fd); + watch_id = g_io_add_watch (channel, G_IO_IN | G_IO_ERR, + ice_iochannel_watch, ice_conn); + g_io_channel_unref (channel); + + *watch_data = GUINT_TO_POINTER (watch_id); + } + else + { + watch_id = GPOINTER_TO_UINT (*watch_data); + g_source_remove (watch_id); + } +} + +static void +ice_error_handler (IceConn ice_conn, + Bool swap, + int offending_minor_opcode, + unsigned long offending_sequence, + int error_class, + int severity, + IcePointer values) +{ + /* Do nothing */ +} + +static void +ice_io_error_handler (IceConn ice_conn) +{ + /* Do nothing */ +} + +static void +smc_error_handler (SmcConn smc_conn, + Bool swap, + int offending_minor_opcode, + unsigned long offending_sequence, + int error_class, + int severity, + SmPointer values) +{ + /* Do nothing */ +} diff --git a/gsearchtool/libeggsmclient/eggsmclient.c b/gsearchtool/libeggsmclient/eggsmclient.c new file mode 100644 index 00000000..0cc3818a --- /dev/null +++ b/gsearchtool/libeggsmclient/eggsmclient.c @@ -0,0 +1,589 @@ +/* + * Copyright (C) 2007 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#include +#include + +#include "eggsmclient.h" +#include "eggsmclient-private.h" + +static void egg_sm_client_debug_handler (const char *log_domain, + GLogLevelFlags log_level, + const char *message, + gpointer user_data); + +enum { + SAVE_STATE, + QUIT_REQUESTED, + QUIT_CANCELLED, + QUIT, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +struct _EggSMClientPrivate { + GKeyFile *state_file; +}; + +#define EGG_SM_CLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), EGG_TYPE_SM_CLIENT, EggSMClientPrivate)) + +G_DEFINE_TYPE (EggSMClient, egg_sm_client, G_TYPE_OBJECT) + +static EggSMClient *global_client; +static EggSMClientMode global_client_mode = EGG_SM_CLIENT_MODE_NORMAL; + +static void +egg_sm_client_init (EggSMClient *client) +{ + ; +} + +static void +egg_sm_client_class_init (EggSMClientClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (EggSMClientPrivate)); + + /** + * EggSMClient::save_state: + * @client: the client + * @state_file: a #GKeyFile to save state information into + * + * Emitted when the session manager has requested that the + * application save information about its current state. The + * application should save its state into @state_file, and then the + * session manager may then restart the application in a future + * session and tell it to initialize itself from that state. + * + * You should not save any data into @state_file's "start group" + * (ie, the %NULL group). Instead, applications should save their + * data into groups with names that start with the application name, + * and libraries that connect to this signal should save their data + * into groups with names that start with the library name. + * + * Alternatively, rather than (or in addition to) using @state_file, + * the application can save its state by calling + * egg_sm_client_set_restart_command() during the processing of this + * signal (eg, to include a list of files to open). + **/ + signals[SAVE_STATE] = + g_signal_new ("save_state", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EggSMClientClass, save_state), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, G_TYPE_POINTER); + + /** + * EggSMClient::quit_requested: + * @client: the client + * + * Emitted when the session manager requests that the application + * exit (generally because the user is logging out). The application + * should decide whether or not it is willing to quit (perhaps after + * asking the user what to do with documents that have unsaved + * changes) and then call egg_sm_client_will_quit(), passing %TRUE + * or %FALSE to give its answer to the session manager. (It does not + * need to give an answer before returning from the signal handler; + * it can interact with the user asynchronously and then give its + * answer later on.) If the application does not connect to this + * signal, then #EggSMClient will automatically return %TRUE on its + * behalf. + * + * The application should not save its session state as part of + * handling this signal; if the user has requested that the session + * be saved when logging out, then ::save_state will be emitted + * separately. + * + * If the application agrees to quit, it should then wait for either + * the ::quit_cancelled or ::quit signals to be emitted. + **/ + signals[QUIT_REQUESTED] = + g_signal_new ("quit_requested", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EggSMClientClass, quit_requested), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + /** + * EggSMClient::quit_cancelled: + * @client: the client + * + * Emitted when the session manager decides to cancel a logout after + * the application has already agreed to quit. After receiving this + * signal, the application can go back to what it was doing before + * receiving the ::quit_requested signal. + **/ + signals[QUIT_CANCELLED] = + g_signal_new ("quit_cancelled", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EggSMClientClass, quit_cancelled), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + /** + * EggSMClient::quit: + * @client: the client + * + * Emitted when the session manager wants the application to quit + * (generally because the user is logging out). The application + * should exit as soon as possible after receiving this signal; if + * it does not, the session manager may choose to forcibly kill it. + * + * Normally a GUI application would only be sent a ::quit if it + * agreed to quit in response to a ::quit_requested signal. However, + * this is not guaranteed; in some situations the session manager + * may decide to end the session without giving applications a + * chance to object. + **/ + signals[QUIT] = + g_signal_new ("quit", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EggSMClientClass, quit), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); +} + +static gboolean sm_client_disable = FALSE; +static char *sm_client_state_file = NULL; +static char *sm_client_id = NULL; +static char *sm_config_prefix = NULL; + +static gboolean +sm_client_post_parse_func (GOptionContext *context, + GOptionGroup *group, + gpointer data, + GError **error) +{ + EggSMClient *client = egg_sm_client_get (); + + if (sm_client_id == NULL) + { + const gchar *desktop_autostart_id; + + desktop_autostart_id = g_getenv ("DESKTOP_AUTOSTART_ID"); + + if (desktop_autostart_id != NULL) + sm_client_id = g_strdup (desktop_autostart_id); + } + + /* Unset DESKTOP_AUTOSTART_ID in order to avoid child processes to + * use the same client id. */ + g_unsetenv ("DESKTOP_AUTOSTART_ID"); + + if (EGG_SM_CLIENT_GET_CLASS (client)->startup) + EGG_SM_CLIENT_GET_CLASS (client)->startup (client, sm_client_id); + return TRUE; +} + +/** + * egg_sm_client_get_option_group: + * + * Creates a %GOptionGroup containing the session-management-related + * options. You should add this group to the application's + * %GOptionContext if you want to use #EggSMClient. + * + * Return value: the %GOptionGroup + **/ +GOptionGroup * +egg_sm_client_get_option_group (void) +{ + const GOptionEntry entries[] = { + { "sm-client-disable", 0, 0, + G_OPTION_ARG_NONE, &sm_client_disable, + N_("Disable connection to session manager"), NULL }, + { "sm-client-state-file", 0, 0, + G_OPTION_ARG_FILENAME, &sm_client_state_file, + N_("Specify file containing saved configuration"), N_("FILE") }, + { "sm-client-id", 0, 0, + G_OPTION_ARG_STRING, &sm_client_id, + N_("Specify session management ID"), N_("ID") }, + /* MateClient compatibility option */ + { "sm-disable", 0, G_OPTION_FLAG_HIDDEN, + G_OPTION_ARG_NONE, &sm_client_disable, + NULL, NULL }, + /* MateClient compatibility option. This is a dummy option that only + * exists so that sessions saved by apps with MateClient can be restored + * later when they've switched to EggSMClient. See bug #575308. + */ + { "sm-config-prefix", 0, G_OPTION_FLAG_HIDDEN, + G_OPTION_ARG_STRING, &sm_config_prefix, + NULL, NULL }, + { NULL } + }; + GOptionGroup *group; + + /* Use our own debug handler for the "EggSMClient" domain. */ + g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, + egg_sm_client_debug_handler, NULL); + + group = g_option_group_new ("sm-client", + _("Session management options:"), + _("Show session management options"), + NULL, NULL); + g_option_group_add_entries (group, entries); + g_option_group_set_parse_hooks (group, NULL, sm_client_post_parse_func); + + return group; +} + +/** + * egg_sm_client_set_mode: + * @mode: an #EggSMClient mode + * + * Sets the "mode" of #EggSMClient as follows: + * + * %EGG_SM_CLIENT_MODE_DISABLED: Session management is completely + * disabled. The application will not even connect to the session + * manager. (egg_sm_client_get() will still return an #EggSMClient, + * but it will just be a dummy object.) + * + * %EGG_SM_CLIENT_MODE_NO_RESTART: The application will connect to + * the session manager (and thus will receive notification when the + * user is logging out, etc), but will request to not be + * automatically restarted with saved state in future sessions. + * + * %EGG_SM_CLIENT_MODE_NORMAL: The default. #EggSMCLient will + * function normally. + * + * This must be called before the application's main loop begins. + **/ +void +egg_sm_client_set_mode (EggSMClientMode mode) +{ + global_client_mode = mode; +} + +/** + * egg_sm_client_get_mode: + * + * Gets the global #EggSMClientMode. See egg_sm_client_set_mode() + * for details. + * + * Return value: the global #EggSMClientMode + **/ +EggSMClientMode +egg_sm_client_get_mode (void) +{ + return global_client_mode; +} + +/** + * egg_sm_client_get: + * + * Returns the master #EggSMClient for the application. + * + * On platforms that support saved sessions (ie, POSIX/X11), the + * application will only request to be restarted by the session + * manager if you call egg_set_desktop_file() to set an application + * desktop file. In particular, if the desktop file contains the key + * "X + * + * Return value: the master #EggSMClient. + **/ +EggSMClient * +egg_sm_client_get (void) +{ + if (!global_client) + { + if (global_client_mode != EGG_SM_CLIENT_MODE_DISABLED && + !sm_client_disable) + { +#if defined (GDK_WINDOWING_WIN32) + global_client = egg_sm_client_win32_new (); +#elif defined (GDK_WINDOWING_QUARTZ) + global_client = egg_sm_client_osx_new (); +#else + /* If both D-Bus and XSMP are compiled in, try XSMP first + * (since it supports state saving) and fall back to D-Bus + * if XSMP isn't available. + */ +# ifdef EGG_SM_CLIENT_BACKEND_XSMP + global_client = egg_sm_client_xsmp_new (); +# endif +# ifdef EGG_SM_CLIENT_BACKEND_DBUS + if (!global_client) + global_client = egg_sm_client_dbus_new (); +# endif +#endif + } + + /* Fallback: create a dummy client, so that callers don't have + * to worry about a %NULL return value. + */ + if (!global_client) + global_client = g_object_new (EGG_TYPE_SM_CLIENT, NULL); + } + + return global_client; +} + +/** + * egg_sm_client_is_resumed: + * @client: the client + * + * Checks whether or not the current session has been resumed from + * a previous saved session. If so, the application should call + * egg_sm_client_get_state_file() and restore its state from the + * returned #GKeyFile. + * + * Return value: %TRUE if the session has been resumed + **/ +gboolean +egg_sm_client_is_resumed (EggSMClient *client) +{ + g_return_val_if_fail (client == global_client, FALSE); + + return sm_client_state_file != NULL; +} + +/** + * egg_sm_client_get_state_file: + * @client: the client + * + * If the application was resumed by the session manager, this will + * return the #GKeyFile containing its state from the previous + * session. + * + * Note that other libraries and #EggSMClient itself may also store + * state in the key file, so if you call egg_sm_client_get_groups(), + * on it, the return value will likely include groups that you did not + * put there yourself. (It is also not guaranteed that the first + * group created by the application will still be the "start group" + * when it is resumed.) + * + * Return value: the #GKeyFile containing the application's earlier + * state, or %NULL on error. You should not free this key file; it + * is owned by @client. + **/ +GKeyFile * +egg_sm_client_get_state_file (EggSMClient *client) +{ + EggSMClientPrivate *priv = EGG_SM_CLIENT_GET_PRIVATE (client); + char *state_file_path; + GError *err = NULL; + + g_return_val_if_fail (client == global_client, NULL); + + if (!sm_client_state_file) + return NULL; + if (priv->state_file) + return priv->state_file; + + if (!strncmp (sm_client_state_file, "file://", 7)) + state_file_path = g_filename_from_uri (sm_client_state_file, NULL, NULL); + else + state_file_path = g_strdup (sm_client_state_file); + + priv->state_file = g_key_file_new (); + if (!g_key_file_load_from_file (priv->state_file, state_file_path, 0, &err)) + { + g_warning ("Could not load SM state file '%s': %s", + sm_client_state_file, err->message); + g_clear_error (&err); + g_key_file_free (priv->state_file); + priv->state_file = NULL; + } + + g_free (state_file_path); + return priv->state_file; +} + +/** + * egg_sm_client_set_restart_command: + * @client: the client + * @argc: the length of @argv + * @argv: argument vector + * + * Sets the command used to restart @client if it does not have a + * .desktop file that can be used to find its restart command. + * + * This can also be used when handling the ::save_state signal, to + * save the current state via an updated command line. (Eg, providing + * a list of filenames to open when the application is resumed.) + **/ +void +egg_sm_client_set_restart_command (EggSMClient *client, + int argc, + const char **argv) +{ + g_return_if_fail (EGG_IS_SM_CLIENT (client)); + + if (EGG_SM_CLIENT_GET_CLASS (client)->set_restart_command) + EGG_SM_CLIENT_GET_CLASS (client)->set_restart_command (client, argc, argv); +} + +/** + * egg_sm_client_will_quit: + * @client: the client + * @will_quit: whether or not the application is willing to quit + * + * This MUST be called in response to the ::quit_requested signal, to + * indicate whether or not the application is willing to quit. The + * application may call it either directly from the signal handler, or + * at some later point (eg, after asynchronously interacting with the + * user). + * + * If the application does not connect to ::quit_requested, + * #EggSMClient will call this method on its behalf (passing %TRUE + * for @will_quit). + * + * After calling this method, the application should wait to receive + * either ::quit_cancelled or ::quit. + **/ +void +egg_sm_client_will_quit (EggSMClient *client, + gboolean will_quit) +{ + g_return_if_fail (EGG_IS_SM_CLIENT (client)); + + if (EGG_SM_CLIENT_GET_CLASS (client)->will_quit) + EGG_SM_CLIENT_GET_CLASS (client)->will_quit (client, will_quit); +} + +/** + * egg_sm_client_end_session: + * @style: a hint at how to end the session + * @request_confirmation: whether or not the user should get a chance + * to confirm the action + * + * Requests that the session manager end the current session. @style + * indicates how the session should be ended, and + * @request_confirmation indicates whether or not the user should be + * given a chance to confirm the logout/reboot/shutdown. Both of these + * flags are merely hints though; the session manager may choose to + * ignore them. + * + * Return value: %TRUE if the request was sent; %FALSE if it could not + * be (eg, because it could not connect to the session manager). + **/ +gboolean +egg_sm_client_end_session (EggSMClientEndStyle style, + gboolean request_confirmation) +{ + EggSMClient *client = egg_sm_client_get (); + + g_return_val_if_fail (EGG_IS_SM_CLIENT (client), FALSE); + + if (EGG_SM_CLIENT_GET_CLASS (client)->end_session) + { + return EGG_SM_CLIENT_GET_CLASS (client)->end_session (client, style, + request_confirmation); + } + else + return FALSE; +} + +/* Signal-emitting callbacks from platform-specific code */ + +GKeyFile * +egg_sm_client_save_state (EggSMClient *client) +{ + GKeyFile *state_file; + char *group; + + g_return_val_if_fail (client == global_client, NULL); + + state_file = g_key_file_new (); + + g_debug ("Emitting save_state"); + g_signal_emit (client, signals[SAVE_STATE], 0, state_file); + g_debug ("Done emitting save_state"); + + group = g_key_file_get_start_group (state_file); + if (group) + { + g_free (group); + return state_file; + } + else + { + g_key_file_free (state_file); + return NULL; + } +} + +void +egg_sm_client_quit_requested (EggSMClient *client) +{ + g_return_if_fail (client == global_client); + + if (!g_signal_has_handler_pending (client, signals[QUIT_REQUESTED], 0, FALSE)) + { + g_debug ("Not emitting quit_requested because no one is listening"); + egg_sm_client_will_quit (client, TRUE); + return; + } + + g_debug ("Emitting quit_requested"); + g_signal_emit (client, signals[QUIT_REQUESTED], 0); + g_debug ("Done emitting quit_requested"); +} + +void +egg_sm_client_quit_cancelled (EggSMClient *client) +{ + g_return_if_fail (client == global_client); + + g_debug ("Emitting quit_cancelled"); + g_signal_emit (client, signals[QUIT_CANCELLED], 0); + g_debug ("Done emitting quit_cancelled"); +} + +void +egg_sm_client_quit (EggSMClient *client) +{ + g_return_if_fail (client == global_client); + + g_debug ("Emitting quit"); + g_signal_emit (client, signals[QUIT], 0); + g_debug ("Done emitting quit"); + + /* FIXME: should we just call gtk_main_quit() here? */ +} + +static void +egg_sm_client_debug_handler (const char *log_domain, + GLogLevelFlags log_level, + const char *message, + gpointer user_data) +{ + static int debug = -1; + + if (debug < 0) + debug = (g_getenv ("EGG_SM_CLIENT_DEBUG") != NULL); + + if (debug) + g_log_default_handler (log_domain, log_level, message, NULL); +} diff --git a/gsearchtool/libeggsmclient/eggsmclient.h b/gsearchtool/libeggsmclient/eggsmclient.h new file mode 100644 index 00000000..d87f670a --- /dev/null +++ b/gsearchtool/libeggsmclient/eggsmclient.h @@ -0,0 +1,117 @@ +/* eggsmclient.h + * Copyright (C) 2007 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __EGG_SM_CLIENT_H__ +#define __EGG_SM_CLIENT_H__ + +#include + +G_BEGIN_DECLS + +#define EGG_TYPE_SM_CLIENT (egg_sm_client_get_type ()) +#define EGG_SM_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SM_CLIENT, EggSMClient)) +#define EGG_SM_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_SM_CLIENT, EggSMClientClass)) +#define EGG_IS_SM_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_SM_CLIENT)) +#define EGG_IS_SM_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_SM_CLIENT)) +#define EGG_SM_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_SM_CLIENT, EggSMClientClass)) + +typedef struct _EggSMClient EggSMClient; +typedef struct _EggSMClientClass EggSMClientClass; +typedef struct _EggSMClientPrivate EggSMClientPrivate; + +typedef enum { + EGG_SM_CLIENT_END_SESSION_DEFAULT, + EGG_SM_CLIENT_LOGOUT, + EGG_SM_CLIENT_REBOOT, + EGG_SM_CLIENT_SHUTDOWN +} EggSMClientEndStyle; + +typedef enum { + EGG_SM_CLIENT_MODE_DISABLED, + EGG_SM_CLIENT_MODE_NO_RESTART, + EGG_SM_CLIENT_MODE_NORMAL +} EggSMClientMode; + +struct _EggSMClient +{ + GObject parent; + +}; + +struct _EggSMClientClass +{ + GObjectClass parent_class; + + /* signals */ + void (*save_state) (EggSMClient *client, + GKeyFile *state_file); + + void (*quit_requested) (EggSMClient *client); + void (*quit_cancelled) (EggSMClient *client); + void (*quit) (EggSMClient *client); + + /* virtual methods */ + void (*startup) (EggSMClient *client, + const char *client_id); + void (*set_restart_command) (EggSMClient *client, + int argc, + const char **argv); + void (*will_quit) (EggSMClient *client, + gboolean will_quit); + gboolean (*end_session) (EggSMClient *client, + EggSMClientEndStyle style, + gboolean request_confirmation); + + /* Padding for future expansion */ + void (*_egg_reserved1) (void); + void (*_egg_reserved2) (void); + void (*_egg_reserved3) (void); + void (*_egg_reserved4) (void); +}; + +GType egg_sm_client_get_type (void) G_GNUC_CONST; + +GOptionGroup *egg_sm_client_get_option_group (void); + +/* Initialization */ +void egg_sm_client_set_mode (EggSMClientMode mode); +EggSMClientMode egg_sm_client_get_mode (void); +EggSMClient *egg_sm_client_get (void); + +/* Resuming a saved session */ +gboolean egg_sm_client_is_resumed (EggSMClient *client); +GKeyFile *egg_sm_client_get_state_file (EggSMClient *client); + +/* Alternate means of saving state */ +void egg_sm_client_set_restart_command (EggSMClient *client, + int argc, + const char **argv); + +/* Handling "quit_requested" signal */ +void egg_sm_client_will_quit (EggSMClient *client, + gboolean will_quit); + +/* Initiate a logout/reboot/shutdown */ +gboolean egg_sm_client_end_session (EggSMClientEndStyle style, + gboolean request_confirmation); + +G_END_DECLS + + +#endif /* __EGG_SM_CLIENT_H__ */ diff --git a/gsearchtool/mate-search-tool.1 b/gsearchtool/mate-search-tool.1 deleted file mode 100644 index 686a8cc9..00000000 --- a/gsearchtool/mate-search-tool.1 +++ /dev/null @@ -1,110 +0,0 @@ -.\" mate-search-tool.1 - MATE Search Tool -.\" Copyright 2001 Jochen Voss -.TH mate-search-tool 1 "March 16 2009" "mate-utils 2.27.1" -.SH NAME -mate-search-tool \- the MATE Search Tool -.SH SYNOPSIS -.B mate-search-tool -.RI [ options ] -.sp -or select -.B Search for Files... -from a -.B Main Menu -or from the -.B Places -menu in a -.B Menu Bar -.SH DESCRIPTION -.B MATE Search Tool -is a utility for finding files on your system. To perform a -basic search, you can type a filename or a partial filename, -with or without wildcards. To refine your search, you can -apply additional search options. - -.B MATE Search Tool -uses the find, grep, and locate UNIX commands. The case -sensitivity of the search depends on your operating -system. For example, on Linux, the find, grep, and locate -commands support the \-i option, so all searches are -case-insensitive. - -For full documentation see the -.B MATE Search Tool -online help. - -.SH OPTIONS -.TP -.BR \-\-help -Show help message -.TP -.BI "\-\-named=" STRING -Set the text of "Name contains" search option -.TP -.BI "\-\-path=" PATH -Set the text of "Look in folder" search option -.TP -.BI "\-\-sortby=" VALUE -Sort files by one of the following: name, folder, size, type, or date -.TP -.BR \-\-descending -Set sort order to descending, the default is ascending -.TP -.BR \-\-start -Automatically start a search -.TP -.BI "\-\-contains=" STRING -Select and set the "Contains the text" search option -.TP -.BI "\-\-mtimeless=" DAYS -Select and set the "Date modified less than" search option -.TP -.BI "\-\-mtimemore=" DAYS -Select and set the "Date modified more than" search option -.TP -.BI "\-\-sizemore=" KILOBYTES -Select and set the "Size at least" search option -.TP -.BI "\-\-sizeless=" KILOBYTES -Select and set the "Size at most" search option -.TP -.BR \-\-empty -Select the "File is empty" search option -.TP -.BI "\-\-user=" USER -Select and set the "Owned by user" search option -.TP -.BI "\-\-group=" GROUP -Select and set the "Owned by group" search option -.TP -.BR \-\-nouser -Select the "Owner is unrecognized" search option -.TP -.BI "\-\-notnamed=" STRING -Select and set the "Name does not contain" search option -.TP -.BI "\-\-regex=" PATTERN -Select and set the "Name matches regular expression" search option -.TP -.BR \-\-hidden -Select the "Show hidden and backup files" search option -.TP -.BR \-\-follow -Select the "Follow symbolic links" search option -.TP -.BR \-\-mounts -Select the "Exclude other filesystems" search option -.SH AUTHOR -.B MATE Search Tool -was originally written by George Lebl (). -Version 2 was written by Dennis M. Cranston -(). - -This manual page was originally written by Jochen Voss -. Version 2 was written by -Dennis M. Cranston (). - -.SH SEE ALSO -.BR find (1), -.BR locate (1), -.BR grep (1) diff --git a/gsearchtool/mate-search-tool.convert b/gsearchtool/mate-search-tool.convert deleted file mode 100644 index 44156425..00000000 --- a/gsearchtool/mate-search-tool.convert +++ /dev/null @@ -1,27 +0,0 @@ -[org.mate.search-tool] -show-additional-options=/apps/mate-search-tool/show_additional_options -disable-quick-search=/apps/mate-search-tool/disable_quick_search -quick-search-excluded-paths=/apps/mate-search-tool/quick_search_excluded_paths -disable-quick-search-second-scan=/apps/mate-search-tool/disable_quick_search_second-scan -quick-search-second-scan-excluded-paths=/apps/mate-search-tool/quick_search_second_scan_excluded_paths -columns-order=/apps/mate-search-tool/columns_order -default-window-width=/apps/mate-search-tool/default_window_width -default-window-height=/apps/mate-search-tool/default_window_height -default-window-maximized=/apps/mate-search-tool/default_window_maximized -look-in-folder=/apps/mate-search-tool/look_in_folder - -[org.mate.search-tool.select] -contains-the-text=/apps/mate-search-tool/select/contains_the_text -date-modified-less-than=/apps/mate-search-tool/select/date_modified_less_than -date-modified-more-than=/apps/mate-search-tool/select/date_modified_more_than -size-at-least=/apps/mate-search-tool/select/size_at_least -size-at-most=/apps/mate-search-tool/select/size_at_most -file-is-empty=/apps/mate-search-tool/select/file_is_empty -owned-by-user=/apps/mate-search-tool/select/owned_by_user -owned-by-group=/apps/mate-search-tool/select/owned_by_group -owner-is-unrecognized=/apps/mate-search-tool/select/owner_is_unrecognized -name-does-not-contain=/apps/mate-search-tool/select/name_does_not_contain -name-matches-regular-expression=/apps/mate-search-tool/select/name_matches_regular_expression -show-hidden-files-and-folders=/apps/mate-search-tool/select/show_hidden_files_and_folders -follow-symbolic-links=/apps/mate-search-tool/select/follow_symbolic_links -exclude-other-filesystems=/apps/mate-search-tool/select/exclude_other_filesystems diff --git a/gsearchtool/mate-search-tool.desktop.in b/gsearchtool/mate-search-tool.desktop.in deleted file mode 100644 index 56645ceb..00000000 --- a/gsearchtool/mate-search-tool.desktop.in +++ /dev/null @@ -1,15 +0,0 @@ -[Desktop Entry] -_Name=MATE Search Tool -_Comment=Locate documents and folders on this computer by name or content -Exec=mate-search-tool -Icon=system-search -Terminal=false -Type=Application -StartupNotify=true -Categories=GTK;Utility;Core; -Keywords=MATE;search;files;locate;documents;folders;computer;name;content;find;tool; -OnlyShowIn=MATE; -X-MATE-DocPath=mate-search-tool/mate-search-tool.xml -X-MATE-Bugzilla-Bugzilla=MATE -X-MATE-Bugzilla-Product=mate-utils -X-MATE-Bugzilla-Component=gsearchtool diff --git a/gsearchtool/org.mate.search-tool.gschema.xml.in b/gsearchtool/org.mate.search-tool.gschema.xml.in deleted file mode 100644 index b6a532c4..00000000 --- a/gsearchtool/org.mate.search-tool.gschema.xml.in +++ /dev/null @@ -1,117 +0,0 @@ - - - - [ ] - <_summary>Search history - <_description>This key defines the items which were searched for in the past. - - - false - <_summary>Show Additional Options - - - false - <_summary>Disable Quick Search - <_description>This key determines if the search tool disables the use of the locate command when performing simple file name searches. - - - [ '/mnt/*', '/media/*', '/dev/*', '/tmp/*', '/proc/*', '/var/*' ] - <_summary>Quick Search Excluded Paths - <_description>This key defines the paths the search tool will exclude from a quick search. The wildcards '*' and '?' are supported. The default values are /mnt/*, /media/*, /dev/*, /tmp/*, /proc/*, and /var/*. - - - false - <_summary>Disable Quick Search Second Scan - <_description>This key determines if the search tool disables the use of the find command after performing a quick search. - - - [ '/' ] - <_summary>Quick Search Second Scan Excluded Paths - <_description>This key defines the paths the search tool will exclude from a second scan when performing a quick search. The second scan uses the find command to search for files. The purpose of the second scan is to find files that have not been indexed. The wildcards '*' and '?' are supported. The default value is /. - - - [ 0 ] - <_summary>Search Result Columns Order - <_description>This key defines the order of the columns in the search results. This key should not be modified by the user. - - - -1 - <_summary>Default Window Width - <_description>This key defines the window width, and it's used to remember the size of the search tool between sessions. Setting it to -1 will make the search tool use the default width. - - - -1 - <_summary>Default Window Height - <_description>This key defines the window height, and it's used to remember the size of the search tool between sessions. Setting it to -1 will make the search tool use the default height. - - - false - <_summary>Default Window Maximized - <_description>This key determines if the search tool window starts in a maximized state. - - - '' - <_summary>Look in Folder - <_description>This key defines the default value of the "Look in Folder" widget. - - - - - - true - <_description>This key determines if the "Contains the text" search option is selected when the search tool is started. - - - false - <_description>This key determines if the "Date modified less than" search option is selected when the search tool is started. - - - false - <_description>This key determines if the "Date modified more than" search option is selected when the search tool is started. - - - false - <_description>This key determines if the "Size at least" search option is selected when the search tool is started. - - - false - <_description>This key determines if the "Size at most" search option is selected when the search tool is started. - - - false - <_description>This key determines if the "File is empty" search option is selected when the search tool is started. - - - false - <_description>This key determines if the "Owned by user" search option is selected when the search tool is started. - - - false - <_description>This key determines if the "Owned by group" search option is selected when the search tool is started. - - - false - <_description>This key determines if the "Owner is unrecognized" search option is selected when the search tool is started. - - - false - <_description>This key determines if the "Name does not contain" search option is selected when the search tool is started. - - - false - <_description>This key determines if the "Name matches regular expression" search option is selected when the search tool is started. - - - false - <_description>This key determines if the "Show hidden files and folders" search option is selected when the search tool is started. - - - false - <_description>This key determines if the "Follow symbolic links" search option is selected when the search tool is started. - - - false - <_description>This key determines if the "Exclude other filesystems" search option is selected when the search tool is started. - - - 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