/* -*- 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); } #if GTK_CHECK_VERSION (3, 8, 0) g_object_unref (icon_info); #else gtk_icon_info_free (icon_info); #endif 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; #if GTK_CHECK_VERSION (3, 0, 0) GdkScreen *screen; GdkDisplay *display; #endif 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) { #if GTK_CHECK_VERSION (3, 0, 0) screen = gtk_widget_get_screen (GTK_WIDGET (window)); display = gdk_screen_get_display (screen); ctx = gdk_display_get_app_launch_context (display); #else ctx = gdk_app_launch_context_new (); #endif 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; #if GTK_CHECK_VERSION (3, 0, 0) GdkDisplay *display; #endif GdkScreen * screen; gboolean result; if (g_file_test (file, G_FILE_TEST_IS_DIR) == TRUE) { return FALSE; } screen = gtk_widget_get_screen (window); #if GTK_CHECK_VERSION (3, 0, 0) display = gdk_screen_get_display (screen); context = gdk_display_get_app_launch_context (display); #else context = gdk_app_launch_context_new (); #endif 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); #if GTK_CHECK_VERSION (3, 0, 0) hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2); #else hbox = gtk_hbox_new (FALSE, 2); #endif 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; }