From 9494745d46fe20b0d3ca20e3d23a926b34ed7a08 Mon Sep 17 00:00:00 2001 From: Stefano Karapetsas Date: Thu, 17 Oct 2013 16:20:02 +0200 Subject: Add open-terminal extension --- open-terminal/caja-open-terminal.c | 577 +++++++++++++++++++++++++++++++++++++ 1 file changed, 577 insertions(+) create mode 100644 open-terminal/caja-open-terminal.c (limited to 'open-terminal/caja-open-terminal.c') diff --git a/open-terminal/caja-open-terminal.c b/open-terminal/caja-open-terminal.c new file mode 100644 index 0000000..48e8a58 --- /dev/null +++ b/open-terminal/caja-open-terminal.c @@ -0,0 +1,577 @@ +/* + * caja-open-terminal.c + * + * Copyright (C) 2004, 2005 Free Software Foundation, Inc. + * + * This library 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 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 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. + * + * Author: Christian Neumair + * + */ + +#ifdef HAVE_CONFIG_H + #include /* for GETTEXT_PACKAGE */ +#endif + +#include "caja-open-terminal.h" + +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include /* for strcmp, strdup, ... */ +#include /* for chdir */ +#include /* for atoi */ +#include + +#define SSH_DEFAULT_PORT 22 +#define COT_SCHEMA "org.mate.caja-open-terminal" +#define COT_DESKTOP_KEY "desktop-opens-home-dir" +#define CAJA_SCHEMA "org.mate.caja.preferences" +#define CAJA_DESKTOP_KEY "desktop-is-home-dir" +#define TERM_SCHEMA "org.mate.applications-terminal" +#define TERM_EXEC_KEY "exec" + +static void caja_open_terminal_instance_init (CajaOpenTerminal *cvs); +static void caja_open_terminal_class_init (CajaOpenTerminalClass *class); + +static GType terminal_type = 0; + +typedef enum { + FILE_INFO_LOCAL, + FILE_INFO_DESKTOP, + FILE_INFO_SFTP, + FILE_INFO_OTHER +} TerminalFileInfo; + +static TerminalFileInfo +get_terminal_file_info (CajaFileInfo *file_info) +{ + TerminalFileInfo ret; + char *uri_scheme, *p; + + g_assert (file_info); + + uri_scheme = caja_file_info_get_activation_uri (file_info); + p = strchr (uri_scheme, ':'); + if (p) { + *p = 0; + } + + if (strcmp (uri_scheme, "file") == 0) { + ret = FILE_INFO_LOCAL; + } else if (strcmp (uri_scheme, "x-caja-desktop") == 0) { + ret = FILE_INFO_DESKTOP; + } else if (strcmp (uri_scheme, "sftp") == 0 || + strcmp (uri_scheme, "ssh") == 0) { + ret = FILE_INFO_SFTP; + } else { + ret = FILE_INFO_OTHER; + } + + g_free (uri_scheme); + + return ret; +} + +char * +lookup_in_data_dir (const char *basename, + const char *data_dir) +{ + char *path; + + path = g_build_filename (data_dir, basename, NULL); + if (!g_file_test (path, G_FILE_TEST_EXISTS)) { + g_free (path); + return NULL; + } + + return path; +} + +static char * +lookup_in_data_dirs (const char *basename) +{ + const char * const *system_data_dirs; + const char *user_data_dir; + char *retval; + int i; + + user_data_dir = g_get_user_data_dir (); + system_data_dirs = g_get_system_data_dirs (); + + if ((retval = lookup_in_data_dir (basename, user_data_dir))) { + return retval; + } + + for (i = 0; system_data_dirs[i]; i++) { + if ((retval = lookup_in_data_dir (basename, system_data_dirs[i]))) + return retval; + } + + return NULL; +} + +static inline gboolean +desktop_opens_home_dir (void) +{ + gboolean result; + GSettings* settings; + + settings = g_settings_new (COT_SCHEMA); + result = g_settings_get_boolean (settings, COT_DESKTOP_KEY); + g_object_unref (settings); + return result; +} + +static inline gboolean +desktop_is_home_dir (void) +{ + gboolean result; + GSettings* settings; + + settings = g_settings_new (CAJA_SCHEMA); + result = g_settings_get_boolean (settings, CAJA_DESKTOP_KEY); + g_object_unref (settings); + return result; +} + +static inline gchar* +default_terminal_application (void) +{ + gchar *result; + GSettings* settings; + + settings = g_settings_new (TERM_SCHEMA); + result = g_settings_get_string (settings, TERM_EXEC_KEY); + g_object_unref (settings); + return result; +} + +#define get_desktop_dir() g_strdup (g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP)) + +static void +parse_sftp_uri (GFile *file, char **host, guint *port, char **user, + char **path) +{ + char *uri = g_file_get_uri (file); + char *u, *h, *s, *p; + char *h_end; + + g_assert (uri != NULL); + + u = strchr(uri, ':'); + g_assert (u != NULL); + u += 3; /* Skip over :// to userid */ + + p = strchr (u, '/'); + h = strchr(u, '@'); + + if (h && ((p == NULL) || (h < p))) { + *h='\0'; + h++; + } else { + h = u; + u = NULL; + } + + s = strchr(h, ':'); + + if (s && (p == NULL || s < p)) { + h_end = s-1; + *s = '\0'; + s++; + } else { + h_end = p; + s = NULL; + } + + if (h_end == NULL) { + h_end = h + strlen(h); + } + + *user = g_strdup(u); + *port = s == NULL ? 0 : atoi(s); /* FIXME: getservbyname ? */ + *path = g_uri_unescape_string (p, "/"); + *h_end = '\0'; + *host = g_strdup(h); + + g_free (uri); +} + +static void +append_sftp_info (char **terminal_exec, + CajaFileInfo *file_info) +{ + GFile *vfs_uri; + char *host_name, *path, *user_name; + char *user_host, *cmd, *quoted_cmd; + guint host_port; + + g_assert (terminal_exec != NULL); + g_assert (file_info != NULL); + + + vfs_uri = g_file_new_for_uri (caja_file_info_get_activation_uri (file_info)); + g_assert (vfs_uri != NULL); + + g_assert (g_file_has_uri_scheme(vfs_uri, "sftp")==TRUE || + g_file_has_uri_scheme(vfs_uri, "ssh")==TRUE); + + parse_sftp_uri (vfs_uri, &host_name, &host_port, &user_name, &path); + + if (host_port == 0) { + host_port = SSH_DEFAULT_PORT; + } + + if (user_name != NULL) { + user_host = g_strdup_printf ("%s@%s", user_name, host_name); + } else { + user_host = g_strdup (host_name); + } + + cmd = g_strdup_printf ("ssh %s -p %d -t \"cd \'%s\' && $SHELL -l\"", user_host, host_port, path); + quoted_cmd = g_shell_quote (cmd); + g_free (cmd); + + *terminal_exec = g_realloc (*terminal_exec, strlen (*terminal_exec) + strlen (quoted_cmd) + 4 + 1); + strcpy (*terminal_exec + strlen (*terminal_exec), " -e "); + strcpy (*terminal_exec + strlen (*terminal_exec), quoted_cmd); + + g_free (host_name); + g_free (user_name); + g_free (path); + + g_free (quoted_cmd); + g_free (user_host); + g_object_unref (vfs_uri); +} + +static void +open_terminal_callback (CajaMenuItem *item, + CajaFileInfo *file_info) +{ + gchar *display_str; + const gchar *old_display_str; + gchar *uri; + gchar **argv, *terminal_exec; + gchar *working_directory; + gchar *dfile; + MateDesktopItem *ditem; + GdkScreen *screen; + + terminal_exec = default_terminal_application(); + + if (terminal_exec == NULL || strlen (terminal_exec) == 0) { + g_free (terminal_exec); + terminal_exec = g_strdup ("mate-terminal"); + } + + switch (get_terminal_file_info (file_info)) { + case FILE_INFO_LOCAL: + uri = caja_file_info_get_activation_uri (file_info); + if (uri != NULL) { + working_directory = g_filename_from_uri (uri, NULL, NULL); + } else { + working_directory = g_strdup (g_get_home_dir ()); + } + g_free (uri); + break; + + case FILE_INFO_DESKTOP: + if (desktop_is_home_dir () || desktop_opens_home_dir ()) { + working_directory = g_strdup (g_get_home_dir ()); + } else { + working_directory = get_desktop_dir (); + } + break; + + case FILE_INFO_SFTP: + working_directory = NULL; + append_sftp_info (&terminal_exec, file_info); + break; + + case FILE_INFO_OTHER: + default: + g_assert_not_reached (); + } + + if (g_str_has_prefix (terminal_exec, "mate-terminal")) { + dfile = lookup_in_data_dirs ("applications/mate-terminal.desktop"); + } else { + dfile = NULL; + } + + g_shell_parse_argv (terminal_exec, NULL, &argv, NULL); + + display_str = NULL; + old_display_str = g_getenv ("DISPLAY"); + + screen = g_object_get_data (G_OBJECT (item), "CajaOpenTerminal::screen"); + if (screen != NULL) { + display_str = gdk_screen_make_display_name (screen); + g_setenv ("DISPLAY", display_str, TRUE); + } + + if (dfile != NULL) { + int orig_cwd = -1; + + do { + orig_cwd = open (".", O_RDONLY); + } while (orig_cwd == -1 && errno == EINTR); + + if (orig_cwd == -1) { + g_message ("CajaOpenTerminal: Failed to open current Caja working directory."); + } else if (working_directory != NULL) { + + if (chdir (working_directory) == -1) { + int ret; + + g_message ("CajaOpenTerminal: Failed to change Caja working directory to \"%s\".", + working_directory); + + do { + ret = close (orig_cwd); + } while (ret == -1 && errno == EINTR); + + if (ret == -1) { + g_message ("CajaOpenTerminal: Failed to close() current Caja working directory."); + } + + orig_cwd = -1; + } + } + + ditem = mate_desktop_item_new_from_file (dfile, 0, NULL); + + mate_desktop_item_set_string (ditem, "Exec", terminal_exec); + if (gtk_get_current_event_time () > 0) { + mate_desktop_item_set_launch_time (ditem, gtk_get_current_event_time ()); + } + mate_desktop_item_launch (ditem, NULL, MATE_DESKTOP_ITEM_LAUNCH_USE_CURRENT_DIR, NULL); + mate_desktop_item_unref (ditem); + g_free (dfile); + + if (orig_cwd != -1) { + int ret; + + ret = fchdir (orig_cwd); + if (ret == -1) { + g_message ("CajaOpenTerminal: Failed to change back Caja working directory to original location after changing it to \"%s\".", + working_directory); + } + + do { + ret = close (orig_cwd); + } while (ret == -1 && errno == EINTR); + + if (ret == -1) { + g_message ("CajaOpenTerminal: Failed to close Caja working directory."); + } + } + } else { + g_spawn_async (working_directory, + argv, + NULL, + G_SPAWN_SEARCH_PATH, + NULL, + NULL, + NULL, + NULL); + } + + g_setenv ("DISPLAY", old_display_str, TRUE); + + g_strfreev (argv); + g_free (terminal_exec); + g_free (working_directory); + g_free (display_str); +} + +static CajaMenuItem * +open_terminal_menu_item_new (TerminalFileInfo terminal_file_info, + GdkScreen *screen, + gboolean is_file_item) +{ + CajaMenuItem *ret; + const char *name; + const char *tooltip; + + switch (terminal_file_info) { + case FILE_INFO_LOCAL: + case FILE_INFO_SFTP: + name = _("Open In _Terminal"); + if (is_file_item) { + tooltip = _("Open the currently selected folder in a terminal"); + } else { + tooltip = _("Open the currently open folder in a terminal"); + } + break; + + case FILE_INFO_DESKTOP: + if (desktop_opens_home_dir ()) { + name = _("Open _Terminal"); + tooltip = _("Open a terminal"); + } else { + name = _("Open In _Terminal"); + tooltip = _("Open the currently open folder in a terminal"); + } + break; + + case FILE_INFO_OTHER: + default: + g_assert_not_reached (); + } + + ret = caja_menu_item_new ("CajaOpenTerminal::open_terminal", + name, tooltip, "terminal"); + g_object_set_data (G_OBJECT (ret), + "CajaOpenTerminal::screen", + screen); + + return ret; +} + +static GList * +caja_open_terminal_get_background_items (CajaMenuProvider *provider, + GtkWidget *window, + CajaFileInfo *file_info) +{ + CajaMenuItem *item; + TerminalFileInfo terminal_file_info; + + terminal_file_info = get_terminal_file_info (file_info); + switch (terminal_file_info) { + case FILE_INFO_LOCAL: + case FILE_INFO_DESKTOP: + case FILE_INFO_SFTP: + item = open_terminal_menu_item_new (terminal_file_info, gtk_widget_get_screen (window), FALSE); + g_object_set_data_full (G_OBJECT (item), "file-info", + g_object_ref (file_info), + (GDestroyNotify) g_object_unref); + g_signal_connect (item, "activate", + G_CALLBACK (open_terminal_callback), + file_info); + + return g_list_append (NULL, item); + + case FILE_INFO_OTHER: + return NULL; + + default: + g_assert_not_reached (); + } +} + +GList * +caja_open_terminal_get_file_items (CajaMenuProvider *provider, + GtkWidget *window, + GList *files) +{ + CajaMenuItem *item; + TerminalFileInfo terminal_file_info; + + if (g_list_length (files) != 1 || + (!caja_file_info_is_directory (files->data) && + caja_file_info_get_file_type (files->data) != G_FILE_TYPE_SHORTCUT && + caja_file_info_get_file_type (files->data) != G_FILE_TYPE_MOUNTABLE)) { + return NULL; + } + + terminal_file_info = get_terminal_file_info (files->data); + switch (terminal_file_info) { + case FILE_INFO_LOCAL: + case FILE_INFO_SFTP: + item = open_terminal_menu_item_new (terminal_file_info, gtk_widget_get_screen (window), TRUE); + g_object_set_data_full (G_OBJECT (item), "file-info", + g_object_ref (files->data), + (GDestroyNotify) g_object_unref); + g_signal_connect (item, "activate", + G_CALLBACK (open_terminal_callback), + files->data); + + return g_list_append (NULL, item); + + case FILE_INFO_DESKTOP: + case FILE_INFO_OTHER: + return NULL; + + default: + g_assert_not_reached (); + } +} + +static void +caja_open_terminal_menu_provider_iface_init (CajaMenuProviderIface *iface) +{ + iface->get_background_items = caja_open_terminal_get_background_items; + iface->get_file_items = caja_open_terminal_get_file_items; +} + +static void +caja_open_terminal_instance_init (CajaOpenTerminal *cvs) +{ +} + +static void +caja_open_terminal_class_init (CajaOpenTerminalClass *class) +{ +} + +GType +caja_open_terminal_get_type (void) +{ + return terminal_type; +} + +void +caja_open_terminal_register_type (GTypeModule *module) +{ + static const GTypeInfo info = { + sizeof (CajaOpenTerminalClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) caja_open_terminal_class_init, + NULL, + NULL, + sizeof (CajaOpenTerminal), + 0, + (GInstanceInitFunc) caja_open_terminal_instance_init, + }; + + static const GInterfaceInfo menu_provider_iface_info = { + (GInterfaceInitFunc) caja_open_terminal_menu_provider_iface_init, + NULL, + NULL + }; + + terminal_type = g_type_module_register_type (module, + G_TYPE_OBJECT, + "CajaOpenTerminal", + &info, 0); + + g_type_module_add_interface (module, + terminal_type, + CAJA_TYPE_MENU_PROVIDER, + &menu_provider_iface_info); +} -- cgit v1.2.1