#include #include #include #include #include #include #include #include "gm-external-view.h" #include "gm-debug.h" #include "gm-app.h" #include "gm-options.h" void on_gm_external_view_exited(GPid pid, gint status, GmExternalView *view); void on_gm_external_view_file_changed(GnomeVFSMonitorHandle *handle, const gchar *monitor_uri, const gchar *info_uri, GnomeVFSMonitorEventType event_type, GmExternalView *view); void gm_external_view_destroy(GmExternalView *view) { struct stat buf; if (view->monitor) { gnome_vfs_monitor_cancel(view->monitor); } g_source_remove(view->child_watch); g_spawn_close_pid(view->pid); if (GM_IS_EDITOR(view->editor)) { if (stat(view->filename, &buf) != -1) { if (buf.st_mtime > view->last_modified) { gm_editor_set_lines_from_file(view->editor, view->filename); gm_editor_save(view->editor); } } } if (view->filename) { unlink(view->filename); g_free(view->filename); } } void gm_external_view_update_last_modified(GmExternalView *view) { struct stat buf; if (stat(view->filename, &buf) != -1) { view->last_modified = buf.st_mtime; } } gboolean gm_external_view_spawn(GmExternalView *view, gchar **argv) { GError *err = NULL; g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &(view->pid), &err); if (err) { gm_debug_msg(DEBUG_DEFAULT, "GmExternalView.Spawn: couldn't spawn " "editor: %s", err->message); g_error_free(err); return FALSE; } else { return TRUE; } } typedef struct { gchar const *terminal; gchar const *arguments; } Terminal; static Terminal terminals[] = { {"gnome-terminal", "--disable-factory"}, {"gnome-terminal.wrapper", "--disable-factory"}, {NULL, NULL} }; static gchar * resolve_file(gchar const *file) { GSList *paths = NULL; GSList *item; gchar *resolve; gchar *prog; prog = g_find_program_in_path(file); while (prog && g_file_test(prog, G_FILE_TEST_IS_SYMLINK)) { paths = g_slist_prepend(paths, prog); resolve = g_file_read_link(prog, NULL); if (g_slist_find_custom(paths, resolve, (GCompareFunc)strcmp)) { prog = NULL; g_free(resolve); break; } prog = resolve; } for (item = paths; item; item = item->next) if (item->data != prog) g_free(item->data); g_slist_free(paths); return prog; } static Terminal * find_terminal(gchar const *term) { gchar *prog; gchar *basename; Terminal *terminal; prog = resolve_file(term); if (prog) { basename = g_path_get_basename(prog); g_message("%s", basename); for (terminal = terminals; terminal->terminal != NULL; ++terminal) if (strcmp(terminal->terminal, basename) == 0) break; g_free(basename); g_free(prog); if (terminal->terminal) return terminal; } return NULL; } static gchar ** build_terminal_command(gchar const *editor_command) { GConfClient *client; gchar *term; gchar *term_extra_args = NULL; gchar *term_args = NULL; gchar **spawn_command = NULL; Terminal *terminal; gchar **argv; gint argc; gint i; client = gconf_client_get_default(); term = gconf_client_get_string(client, "/desktop/gnome/applications/terminal/exec", NULL); if (term != NULL) term_extra_args = gconf_client_get_string(client, "/desktop/gnome/applications/terminal/exec_arg", NULL); else { term = g_strdup(getenv("TERM")); if (term == NULL) term = g_strdup("xterm"); term_extra_args = g_strdup("-e"); } terminal = find_terminal(term); if (terminal) term_args = (gchar *)(terminal->arguments); if (term_args || (term_extra_args && *term_extra_args != '\0')) { term_args = g_strdup_printf("%s %s %s", term_args ? term_args : "", term_extra_args ? term_extra_args : "", editor_command); if (g_shell_parse_argv(term_args, &argc, &argv, NULL)) { spawn_command = g_new0(gchar *, argc + 2); spawn_command[0] = term; for (i = 0; i < argc; ++i) spawn_command[i + 1] = argv[i]; g_free(argv); } g_free(term_args); } g_free(term_extra_args); g_object_unref(client); for (i = 0; spawn_command[i]; ++i) printf("'%s' ", spawn_command[i]); printf("\n"); return spawn_command; } GmExternalView * gm_external_view_new(GmWorld *world, GmEditor *editor) { GmExternalView *obj = g_new0(GmExternalView, 1); gchar **spawn_command = NULL; gchar *filename; gchar *command; gint argc; GmOptions *options = gm_app_options(gm_app_instance()); obj->world = world; obj->editor = editor; obj->filename = gm_editor_write_lines(editor); gm_external_view_update_last_modified(obj); gnome_vfs_monitor_add(&(obj->monitor), obj->filename, GNOME_VFS_MONITOR_FILE, (GnomeVFSMonitorCallback)on_gm_external_view_file_changed, obj); filename = g_shell_quote(obj->filename); command = g_strconcat(gm_options_get(options, "editor_alternative"), " ", filename, NULL); g_free(filename); if (gm_options_get_int(options, "editor_needs_terminal")) { spawn_command = build_terminal_command(command); } else { g_shell_parse_argv(command, &argc, &spawn_command, NULL); } gm_external_view_spawn(obj, spawn_command); g_strfreev(spawn_command); g_free(command); obj->child_watch = g_child_watch_add(obj->pid, (GChildWatchFunc)on_gm_external_view_exited, obj); return obj; } /* Callbacks */ void on_gm_external_view_exited(GPid pid, gint status, GmExternalView *view) { gm_editor_close(view->editor); } void on_gm_external_view_file_changed(GnomeVFSMonitorHandle *handle, const gchar *monitor_uri, const gchar *info_uri, GnomeVFSMonitorEventType event_type, GmExternalView *view) { switch (event_type) { case GNOME_VFS_MONITOR_EVENT_CHANGED: case GNOME_VFS_MONITOR_EVENT_CREATED: gm_debug_msg(DEBUG_DEFAULT, "GmExternalView.OnFileChanged: " "change event detected!"); gm_editor_set_lines_from_file(view->editor, view->filename); gm_editor_save(view->editor); gm_external_view_update_last_modified(view); break; default: break; } }