diff options
Diffstat (limited to 'gsearchtool/src/gsearchtool-support.c')
-rw-r--r-- | gsearchtool/src/gsearchtool-support.c | 1588 |
1 files changed, 1588 insertions, 0 deletions
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 <[email protected]> + * 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 <config.h> +#endif + +#include <string.h> +#include <glib/gi18n.h> +#include <glib.h> +#include <regex.h> +#include <stdlib.h> +#include <gdk/gdkx.h> +#include <gio/gio.h> +#include <gio/gdesktopappinfo.h> + +#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; +} |