Logo Search packages:      
Sourcecode: nautilus version File versions

nautilus-mime-actions.c

/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */

/* nautilus-mime-actions.c - uri-specific versions of mime action functions

   Copyright (C) 2000, 2001 Eazel, Inc.

   The Gnome Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   The Gnome Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with the Gnome Library; see the file COPYING.LIB.  If not,
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.

   Authors: Maciej Stachowiak <mjs@eazel.com>
*/

#include <config.h>
#include "nautilus-mime-actions.h"

#include <eel/eel-glib-extensions.h>
#include <eel/eel-stock-dialogs.h>
#include <eel/eel-alert-dialog.h>
#include <eel/eel-string.h>
#include <glib/gi18n.h>
#include <glib/gstdio.h>
#include <string.h>
#include <dbus/dbus-glib.h>
#include <gdk/gdkx.h>

#include "nautilus-file-attributes.h"
#include "nautilus-file.h"
#include "nautilus-autorun.h"
#include "nautilus-file-operations.h"
#include "nautilus-metadata.h"
#include "nautilus-program-choosing.h"
#include "nautilus-desktop-icon-file.h"
#include "nautilus-global-preferences.h"
#include "nautilus-debug-log.h"

typedef enum {
      ACTIVATION_ACTION_LAUNCH_DESKTOP_FILE,
      ACTIVATION_ACTION_ASK,
      ACTIVATION_ACTION_LAUNCH,
      ACTIVATION_ACTION_LAUNCH_IN_TERMINAL,
      ACTIVATION_ACTION_OPEN_IN_VIEW,
      ACTIVATION_ACTION_OPEN_IN_APPLICATION,
      ACTIVATION_ACTION_DO_NOTHING,
} ActivationAction;

typedef struct {
      NautilusFile *file;
      char *uri;
} LaunchLocation;

typedef struct {
      GAppInfo *application;
      GList *uris;
} ApplicationLaunchParameters;

typedef struct {
      NautilusWindowSlotInfo *slot_info;
      gpointer window_info;
      GtkWindow *parent_window;
      GCancellable *cancellable;
      GList *locations;
      GList *mountables;
      GList *not_mounted;
      NautilusWindowOpenMode mode;
      NautilusWindowOpenFlags flags;
      char *timed_wait_prompt;
      gboolean timed_wait_active;
      NautilusFileListHandle *files_handle;
      gboolean tried_mounting;
      char *activation_directory;
      gboolean user_confirmation;
} ActivateParameters;

/* Number of seconds until cancel dialog shows up */
#define DELAY_UNTIL_CANCEL_MSECS 5000

#define RESPONSE_RUN 1000
#define RESPONSE_DISPLAY 1001
#define RESPONSE_RUN_IN_TERMINAL 1002
#define RESPONSE_MARK_TRUSTED 1003

#define SILENT_WINDOW_OPEN_LIMIT 5

/* This number controls a maximum character count for a URL that is
 * displayed as part of a dialog. It's fairly arbitrary -- big enough
 * to allow most "normal" URIs to display in full, but small enough to
 * prevent the dialog from getting insanely wide.
 */
#define MAX_URI_IN_DIALOG_LENGTH 60

static void cancel_activate_callback                (gpointer            callback_data);
static void activate_activation_uris_ready_callback (GList              *files,
                                         gpointer            callback_data);
static void activation_mount_mountables             (ActivateParameters *parameters);
static void activate_callback                       (GList              *files,
                                         gpointer            callback_data);
static void activation_mount_not_mounted            (ActivateParameters *parameters);


static void
launch_location_free (LaunchLocation *location)
{
      nautilus_file_unref (location->file);
      g_free (location->uri);
      g_free (location);
}

static void
launch_location_list_free (GList *list)
{
      g_list_foreach (list, (GFunc)launch_location_free, NULL);
      g_list_free (list);
}

static GList *
get_file_list_for_launch_locations (GList *locations)
{
      GList *files, *l;
      LaunchLocation *location;

      files = NULL;
      for (l = locations; l != NULL; l = l->next) {
            location = l->data;

            files = g_list_prepend (files,
                              nautilus_file_ref (location->file));
      }
      return g_list_reverse (files);
}


static LaunchLocation *
launch_location_from_file (NautilusFile *file)
{
      LaunchLocation *location;
      location = g_new (LaunchLocation, 1);
      location->file = nautilus_file_ref (file);
      location->uri = nautilus_file_get_uri (file);
      
      return location;
}

static void
launch_location_update_from_file (LaunchLocation *location,
                          NautilusFile *file)
{
      nautilus_file_unref (location->file);
      g_free (location->uri);
      location->file = nautilus_file_ref (file);
      location->uri = nautilus_file_get_uri (file);
}

static void
launch_location_update_from_uri (LaunchLocation *location,
                         const char *uri)
{
      nautilus_file_unref (location->file);
      g_free (location->uri);
      location->file = nautilus_file_get_by_uri (uri);
      location->uri = g_strdup (uri);
}

static LaunchLocation *
find_launch_location_for_file (GList *list,
                         NautilusFile *file)
{
      LaunchLocation *location;
      GList *l;

      for (l = list; l != NULL; l = l->next) {
            location = l->data;

            if (location->file == file) {
                  return location;
            }
      }
      return NULL;
}

static GList *
launch_locations_from_file_list (GList *list)
{
      GList *new;

      new = NULL;
      while (list) {
            new = g_list_prepend (new,
                              launch_location_from_file (list->data));
            list = list->next;
      }
      new = g_list_reverse (new);
      return new;
}

static ApplicationLaunchParameters *
application_launch_parameters_new (GAppInfo *application,
                                 GList *uris)
{
      ApplicationLaunchParameters *result;

      result = g_new0 (ApplicationLaunchParameters, 1);
      result->application = g_object_ref (application);
      result->uris = eel_g_str_list_copy (uris);

      return result;
}

static void
application_launch_parameters_free (ApplicationLaunchParameters *parameters)
{
      g_object_unref (parameters->application);
      eel_g_list_free_deep (parameters->uris);

      g_free (parameters);
}                       

static GList*
filter_nautilus_handler (GList *apps)
{
      GList *l, *next;
      GAppInfo *application;
      const char *id;

      l = apps;
      while (l != NULL) {
            application = (GAppInfo *) l->data;
            next = l->next;

            id = g_app_info_get_id (application);
            if (id != NULL &&
                strcmp (id,
                      "nautilus-folder-handler.desktop") == 0) {
                  g_object_unref (application);
                  apps = g_list_delete_link (apps, l); 
            }

            l = next;
      }

      return apps;
}

static GList*
filter_non_uri_apps (GList *apps)
{
      GList *l, *next;
      GAppInfo *app;

      for (l = apps; l != NULL; l = next) {
            app = l->data;
            next = l->next;
            
            if (!g_app_info_supports_uris (app)) {
                  apps = g_list_delete_link (apps, l);
                  g_object_unref (app);
            }
      }
      return apps;
}


static gboolean
nautilus_mime_actions_check_if_required_attributes_ready (NautilusFile *file)
{
      NautilusFileAttributes attributes;
      gboolean ready;

      attributes = nautilus_mime_actions_get_required_file_attributes ();
      ready = nautilus_file_check_if_ready (file, attributes);

      return ready;
}

NautilusFileAttributes 
nautilus_mime_actions_get_required_file_attributes (void)
{
      return NAUTILUS_FILE_ATTRIBUTE_INFO |
            NAUTILUS_FILE_ATTRIBUTE_LINK_INFO |
            NAUTILUS_FILE_ATTRIBUTE_METADATA;
}

static gboolean
file_has_local_path (NautilusFile *file)
{
      GFile *location;
      char *path;
      gboolean res;

      
      /* Don't only check _is_native, because we want to support
         using the fuse path */
      location = nautilus_file_get_location (file);
      if (g_file_is_native (location)) {
            res = TRUE;
      } else {
            path = g_file_get_path (location);
            
            res = path != NULL;
            
            g_free (path);
      }
      g_object_unref (location);
      
      return res;
}

GAppInfo *
nautilus_mime_get_default_application_for_file (NautilusFile *file)
{
      GAppInfo *app;
      char *mime_type;
      char *uri_scheme;

      if (!nautilus_mime_actions_check_if_required_attributes_ready (file)) {
            return NULL;
      }

      mime_type = nautilus_file_get_mime_type (file);
      app = g_app_info_get_default_for_type (mime_type, !file_has_local_path (file));
      g_free (mime_type);

      if (app == NULL) {
            uri_scheme = nautilus_file_get_uri_scheme (file);
            if (uri_scheme != NULL) {
                  app = g_app_info_get_default_for_uri_scheme (uri_scheme);
                  g_free (uri_scheme);
            }
      }
      
      return app;
}

static int
file_compare_by_mime_type (NautilusFile *file_a,
                     NautilusFile *file_b)
{
      char *mime_type_a, *mime_type_b;
      int ret;
      
      mime_type_a = nautilus_file_get_mime_type (file_a);
      mime_type_b = nautilus_file_get_mime_type (file_b);
      
      ret = strcmp (mime_type_a, mime_type_b);
      
      g_free (mime_type_a);
      g_free (mime_type_b);
      
      return ret;
}

static int
file_compare_by_parent_uri (NautilusFile *file_a,
                      NautilusFile *file_b) {
      char *parent_uri_a, *parent_uri_b;
      int ret;

      parent_uri_a = nautilus_file_get_parent_uri (file_a);
      parent_uri_b = nautilus_file_get_parent_uri (file_b);

      ret = strcmp (parent_uri_a, parent_uri_b);

      g_free (parent_uri_a);
      g_free (parent_uri_b);

      return ret;
}

static int
application_compare_by_name (const GAppInfo *app_a,
                       const GAppInfo *app_b)
{
      return g_utf8_collate (g_app_info_get_name ((GAppInfo *)app_a),
                         g_app_info_get_name ((GAppInfo *)app_b));
}

static int
application_compare_by_id (const GAppInfo *app_a,
                     const GAppInfo *app_b)
{
      const char *id_a, *id_b;

      id_a = g_app_info_get_id ((GAppInfo *)app_a);
      id_b = g_app_info_get_id ((GAppInfo *)app_b);

      if (id_a == NULL && id_b == NULL) {
            if (g_app_info_equal ((GAppInfo *)app_a, (GAppInfo *)app_b)) {
                  return 0;
            }
            if ((gsize)app_a < (gsize) app_b) {
                  return -1;
            }
            return 1;
      }

      if (id_a == NULL) {
            return -1;
      }
      
      if (id_b == NULL) {
            return 1;
      }
      
      
      return strcmp (id_a, id_b);
}

GList *
nautilus_mime_get_applications_for_file (NautilusFile *file)
{
      char *mime_type;
      char *uri_scheme;
      GList *result;
      GAppInfo *uri_handler;

      if (!nautilus_mime_actions_check_if_required_attributes_ready (file)) {
            return NULL;
      }
      mime_type = nautilus_file_get_mime_type (file);
      result = g_app_info_get_all_for_type (mime_type);

      uri_scheme = nautilus_file_get_uri_scheme (file);
      if (uri_scheme != NULL) {
            uri_handler = g_app_info_get_default_for_uri_scheme (uri_scheme);
            if (uri_handler) {
                  result = g_list_prepend (result, uri_handler);
            }
            g_free (uri_scheme);
      }
      
      if (!file_has_local_path (file)) {
            /* Filter out non-uri supporting apps */
            result = filter_non_uri_apps (result);
      }
      
      result = g_list_sort (result, (GCompareFunc) application_compare_by_name);
      g_free (mime_type);

      return filter_nautilus_handler (result);
}

gboolean
nautilus_mime_has_any_applications_for_file (NautilusFile *file)
{
      GList *apps;
      char *mime_type;
      gboolean result;
      char *uri_scheme;
      GAppInfo *uri_handler;

      mime_type = nautilus_file_get_mime_type (file);
      
      apps = g_app_info_get_all_for_type (mime_type);

      uri_scheme = nautilus_file_get_uri_scheme (file);
      if (uri_scheme != NULL) {
            uri_handler = g_app_info_get_default_for_uri_scheme (uri_scheme);
            if (uri_handler) {
                  apps = g_list_prepend (apps, uri_handler);
            }
            g_free (uri_scheme);
      }
      
      if (!file_has_local_path (file)) {
            /* Filter out non-uri supporting apps */
            apps = filter_non_uri_apps (apps);
      }
      apps = filter_nautilus_handler (apps);
            
      if (apps) {
            result = TRUE;
            eel_g_object_list_free (apps);
      } else {
            result = FALSE;
      }
      
      g_free (mime_type);

      return result;
}

GAppInfo *
nautilus_mime_get_default_application_for_files (GList *files)
{
      GList *l, *sorted_files;
      NautilusFile *file;
      GAppInfo *app, *one_app;

      g_assert (files != NULL);

      sorted_files = g_list_sort (g_list_copy (files), (GCompareFunc) file_compare_by_mime_type);

      app = NULL;
      for (l = sorted_files; l != NULL; l = l->next) {
            file = l->data;

            if (l->prev &&
                file_compare_by_mime_type (file, l->prev->data) == 0 &&
                file_compare_by_parent_uri (file, l->prev->data) == 0) {
                  continue;
            }

            one_app = nautilus_mime_get_default_application_for_file (file);
            if (one_app == NULL || (app != NULL && !g_app_info_equal (app, one_app))) {
                  if (app) {
                        g_object_unref (app);
                  }
                  if (one_app) {
                        g_object_unref (one_app);
                  }
                  app = NULL;
                  break;
            }

            if (app == NULL) {
                  app = one_app;
            } else {
                  g_object_unref (one_app);
            }
      }

      g_list_free (sorted_files);

      return app;
}

/* returns an intersection of two mime application lists,
 * and returns a new list, freeing a, b and all applications
 * that are not in the intersection set.
 * The lists are assumed to be pre-sorted by their IDs */
static GList *
intersect_application_lists (GList *a,
                       GList *b)
{
      GList *l, *m;
      GList *ret;
      GAppInfo *a_app, *b_app;
      int cmp;

      ret = NULL;

      l = a;
      m = b;

      while (l != NULL && m != NULL) {
            a_app = (GAppInfo *) l->data;
            b_app = (GAppInfo *) m->data;

            cmp = application_compare_by_id (a_app, b_app);
            if (cmp > 0) {
                  g_object_unref (b_app);
                  m = m->next;
            } else if (cmp < 0) {
                  g_object_unref (a_app);
                  l = l->next;
            } else {
                  g_object_unref (b_app);
                  ret = g_list_prepend (ret, a_app);
                  l = l->next;
                  m = m->next;
            }
      }

      g_list_foreach (l, (GFunc) g_object_unref, NULL);
      g_list_foreach (m, (GFunc) g_object_unref, NULL);

      g_list_free (a);
      g_list_free (b);

      return g_list_reverse (ret);
}

GList *
nautilus_mime_get_applications_for_files (GList *files)
{
      GList *l, *sorted_files;
      NautilusFile *file;
      GList *one_ret, *ret;

      g_assert (files != NULL);

      sorted_files = g_list_sort (g_list_copy (files), (GCompareFunc) file_compare_by_mime_type);

      ret = NULL;
      for (l = sorted_files; l != NULL; l = l->next) {
            file = l->data;

            if (l->prev &&
                file_compare_by_mime_type (file, l->prev->data) == 0 &&
                file_compare_by_parent_uri (file, l->prev->data) == 0) {
                  continue;
            }

            one_ret = nautilus_mime_get_applications_for_file (file);
            one_ret = g_list_sort (one_ret, (GCompareFunc) application_compare_by_id);
            if (ret != NULL) {
                  ret = intersect_application_lists (ret, one_ret);
            } else {
                  ret = one_ret;
            }

            if (ret == NULL) {
                  break;
            }
      }

      g_list_free (sorted_files);

      ret = g_list_sort (ret, (GCompareFunc) application_compare_by_name);
      
      return ret;
}

gboolean
nautilus_mime_has_any_applications_for_files (GList *files)
{
      GList *l, *sorted_files;
      NautilusFile *file;
      gboolean ret;

      g_assert (files != NULL);

      sorted_files = g_list_sort (g_list_copy (files), (GCompareFunc) file_compare_by_mime_type);

      ret = TRUE;
      for (l = sorted_files; l != NULL; l = l->next) {
            file = NAUTILUS_FILE (l->data);

            if (l->prev &&
                file_compare_by_mime_type (file, l->prev->data) == 0 &&
                file_compare_by_parent_uri (file, l->prev->data) == 0) {
                  continue;
            }

            if (!nautilus_mime_has_any_applications_for_file (file)) {
                  ret = FALSE;
                  break;
            }
      }

      g_list_free (sorted_files);

      return ret;
}



static void
trash_or_delete_files (GtkWindow *parent_window,
                   const GList *files,
                   gboolean delete_if_all_already_in_trash)
{
      GList *locations;
      const GList *node;
      
      locations = NULL;
      for (node = files; node != NULL; node = node->next) {
            locations = g_list_prepend (locations,
                                  nautilus_file_get_location ((NautilusFile *) node->data));
      }
      
      locations = g_list_reverse (locations);

      nautilus_file_operations_trash_or_delete (locations,
                                      parent_window,
                                      NULL, NULL);
      eel_g_object_list_free (locations);
}

static void
report_broken_symbolic_link (GtkWindow *parent_window, NautilusFile *file)
{
      char *target_path;
      char *display_name;
      char *prompt;
      char *detail;
      GtkDialog *dialog;
      GList file_as_list;
      int response;
      
      g_assert (nautilus_file_is_broken_symbolic_link (file));

      display_name = nautilus_file_get_display_name (file);
      if (nautilus_file_is_in_trash (file)) {
            prompt = g_strdup_printf (_("The Link \"%s\" is Broken."), display_name);
      } else {
            prompt = g_strdup_printf (_("The Link \"%s\" is Broken. Move it to Trash?"), display_name);
      }
      g_free (display_name);

      target_path = nautilus_file_get_symbolic_link_target_path (file);
      if (target_path == NULL) {
            detail = g_strdup (_("This link cannot be used, because it has no target."));
      } else {
            detail = g_strdup_printf (_("This link cannot be used, because its target "
                                  "\"%s\" doesn't exist."), target_path);
      }
      
      if (nautilus_file_is_in_trash (file)) {
            eel_run_simple_dialog (GTK_WIDGET (parent_window), FALSE, GTK_MESSAGE_WARNING,
                               prompt, detail, GTK_STOCK_CANCEL, NULL);
            goto out;
      }

      dialog = eel_show_yes_no_dialog (prompt, detail, _("Mo_ve to Trash"), GTK_STOCK_CANCEL,
                               parent_window);

      gtk_dialog_set_default_response (dialog, GTK_RESPONSE_YES);

      /* Make this modal to avoid problems with reffing the view & file
       * to keep them around in case the view changes, which would then
       * cause the old view not to be destroyed, which would cause its
       * merged Bonobo items not to be un-merged. Maybe we need to unmerge
       * explicitly when disconnecting views instead of relying on the
       * unmerge in Destroy. But since BonoboUIHandler is probably going
       * to change wildly, I don't want to mess with this now.
       */

      response = gtk_dialog_run (dialog);
      gtk_object_destroy (GTK_OBJECT (dialog));

      if (response == GTK_RESPONSE_YES) {
            file_as_list.data = file;
            file_as_list.next = NULL;
            file_as_list.prev = NULL;
              trash_or_delete_files (parent_window, &file_as_list, TRUE);
      }

out:
      g_free (prompt);
      g_free (target_path);
      g_free (detail);
}

static ActivationAction
get_executable_text_file_action (GtkWindow *parent_window, NautilusFile *file)
{
      GtkDialog *dialog;
      char *file_name;
      char *prompt;
      char *detail;
      int preferences_value;
      int response;

      g_assert (nautilus_file_contains_text (file));

      preferences_value = eel_preferences_get_enum 
            (NAUTILUS_PREFERENCES_EXECUTABLE_TEXT_ACTIVATION);
      switch (preferences_value) {
      case NAUTILUS_EXECUTABLE_TEXT_LAUNCH:
            return ACTIVATION_ACTION_LAUNCH;
      case NAUTILUS_EXECUTABLE_TEXT_DISPLAY:
            return ACTIVATION_ACTION_OPEN_IN_APPLICATION;
      case NAUTILUS_EXECUTABLE_TEXT_ASK:
            break;
      default:
            /* Complain non-fatally, since preference data can't be trusted */
            g_warning ("Unknown value %d for NAUTILUS_PREFERENCES_EXECUTABLE_TEXT_ACTIVATION",
                     preferences_value);
            
      }


      file_name = nautilus_file_get_display_name (file);
      prompt = g_strdup_printf (_("Do you want to run \"%s\", or display its contents?"), 
                                  file_name);
      detail = g_strdup_printf (_("\"%s\" is an executable text file."),
                            file_name);
      g_free (file_name);

      dialog = eel_create_question_dialog (prompt,
                                   detail,
                                   _("Run in _Terminal"), RESPONSE_RUN_IN_TERMINAL,
                                   _("_Display"), RESPONSE_DISPLAY,
                                   parent_window);
      gtk_dialog_add_button (dialog, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
      gtk_dialog_add_button (dialog, _("_Run"), RESPONSE_RUN);
      gtk_dialog_set_default_response (dialog, GTK_RESPONSE_CANCEL);
      gtk_widget_show (GTK_WIDGET (dialog));
      
      g_free (prompt);
      g_free (detail);

      response = gtk_dialog_run (dialog);
      gtk_object_destroy (GTK_OBJECT (dialog));
      
      switch (response) {
      case RESPONSE_RUN:
            return ACTIVATION_ACTION_LAUNCH;
      case RESPONSE_RUN_IN_TERMINAL:
            return ACTIVATION_ACTION_LAUNCH_IN_TERMINAL;
      case RESPONSE_DISPLAY:
            return ACTIVATION_ACTION_OPEN_IN_APPLICATION;
      default:
            return ACTIVATION_ACTION_DO_NOTHING;
      }
}

static ActivationAction
get_default_executable_text_file_action (void)
{
      int preferences_value;

      preferences_value = eel_preferences_get_enum 
            (NAUTILUS_PREFERENCES_EXECUTABLE_TEXT_ACTIVATION);
      switch (preferences_value) {
      case NAUTILUS_EXECUTABLE_TEXT_LAUNCH:
            return ACTIVATION_ACTION_LAUNCH;
      case NAUTILUS_EXECUTABLE_TEXT_DISPLAY:
            return ACTIVATION_ACTION_OPEN_IN_APPLICATION;
      case NAUTILUS_EXECUTABLE_TEXT_ASK:
      default:
            return ACTIVATION_ACTION_ASK;
      }
}

gboolean
nautilus_mime_file_opens_in_view (NautilusFile *file)
{
  return (nautilus_file_is_directory (file) ||
        NAUTILUS_IS_DESKTOP_ICON_FILE (file) ||
        nautilus_file_is_nautilus_link (file));
}

static ActivationAction
get_activation_action (NautilusFile *file)
{
      ActivationAction action;
      char *activation_uri;

      if (nautilus_file_is_launcher (file)) {
            return ACTIVATION_ACTION_LAUNCH_DESKTOP_FILE;
      }
      
      activation_uri = nautilus_file_get_activation_uri (file);
      if (activation_uri == NULL) {
            activation_uri = nautilus_file_get_uri (file);
      }

      action = ACTIVATION_ACTION_DO_NOTHING;
      if (nautilus_file_is_launchable (file)) {
            char *executable_path;
            
            action = ACTIVATION_ACTION_LAUNCH;
            
            executable_path = g_filename_from_uri (activation_uri, NULL, NULL);
            if (!executable_path) {
                  action = ACTIVATION_ACTION_DO_NOTHING;
            } else if (nautilus_file_contains_text (file)) {
                  action = get_default_executable_text_file_action ();
            }
            g_free (executable_path);
      } 

      if (action == ACTIVATION_ACTION_DO_NOTHING) {
            if (nautilus_mime_file_opens_in_view (file)) {
                  action = ACTIVATION_ACTION_OPEN_IN_VIEW;
            } else {
                  action = ACTIVATION_ACTION_OPEN_IN_APPLICATION;
            }
      }
      g_free (activation_uri);

      return action;
}

gboolean
nautilus_mime_file_opens_in_external_app (NautilusFile *file)
{
  ActivationAction activation_action;
  
  activation_action = get_activation_action (file);
  
  return (activation_action == ACTIVATION_ACTION_OPEN_IN_APPLICATION);
}


static unsigned int
mime_application_hash (GAppInfo *app)
{
      const char *id;

      id = g_app_info_get_id (app);

      if (id == NULL) {
            return GPOINTER_TO_UINT(app);
      }

      return g_str_hash (id);
}

static void
list_to_parameters_foreach (GAppInfo *application,
                      GList *uris,
                      GList **ret)
{
      ApplicationLaunchParameters *parameters;

      uris = g_list_reverse (uris);

      parameters = application_launch_parameters_new
            (application, uris);
      *ret = g_list_prepend (*ret, parameters);
}


/**
 * make_activation_parameters
 *
 * Construct a list of ApplicationLaunchParameters from a list of NautilusFiles,
 * where files that have the same default application are put into the same
 * launch parameter, and others are put into the unhandled_files list.
 *
 * @files: Files to use for construction.
 * @unhandled_files: Files without any default application will be put here.
 * 
 * Return value: Newly allocated list of ApplicationLaunchParameters.
 **/
static GList *
make_activation_parameters (GList *uris,
                      GList **unhandled_uris)
{
      GList *ret, *l, *app_uris;
      NautilusFile *file;
      GAppInfo *app, *old_app;
      GHashTable *app_table;
      char *uri;

      ret = NULL;
      *unhandled_uris = NULL;

      app_table = g_hash_table_new_full
            ((GHashFunc) mime_application_hash,
             (GEqualFunc) g_app_info_equal,
             (GDestroyNotify) g_object_unref,
             (GDestroyNotify) g_list_free);

      for (l = uris; l != NULL; l = l->next) {
            uri = l->data;
            file = nautilus_file_get_by_uri (uri);

            app = nautilus_mime_get_default_application_for_file (file);
            if (app != NULL) {
                  app_uris = NULL;

                  if (g_hash_table_lookup_extended (app_table, app,
                                            (gpointer *) &old_app,
                                            (gpointer *) &app_uris)) {
                        g_hash_table_steal (app_table, old_app);

                        app_uris = g_list_prepend (app_uris, uri);

                        g_object_unref (app);
                        app = old_app;
                  } else {
                        app_uris = g_list_prepend (NULL, uri);
                  }

                  g_hash_table_insert (app_table, app, app_uris);
            } else {
                  *unhandled_uris = g_list_prepend (*unhandled_uris, uri);
            }
            nautilus_file_unref (file);
      }

      g_hash_table_foreach (app_table,
                        (GHFunc) list_to_parameters_foreach,
                        &ret);

      g_hash_table_destroy (app_table);

      *unhandled_uris = g_list_reverse (*unhandled_uris);

      return g_list_reverse (ret);
}

static gboolean
file_was_cancelled (NautilusFile *file)
{
      GError *error;
      
      error = nautilus_file_get_file_info_error (file);
      return
            error != NULL &&
            error->domain == G_IO_ERROR &&
            error->code == G_IO_ERROR_CANCELLED;
}

static gboolean
file_was_not_mounted (NautilusFile *file)
{
      GError *error;
      
      error = nautilus_file_get_file_info_error (file);
      return
            error != NULL &&
            error->domain == G_IO_ERROR &&
            error->code == G_IO_ERROR_NOT_MOUNTED;
}

static void
activation_parameters_free (ActivateParameters *parameters)
{
      if (parameters->timed_wait_active) {
            eel_timed_wait_stop (cancel_activate_callback, parameters);
      }
      
      if (parameters->slot_info) {
            g_object_remove_weak_pointer (G_OBJECT (parameters->slot_info), (gpointer *)&parameters->slot_info);
      }
      if (parameters->parent_window) {
            g_object_remove_weak_pointer (G_OBJECT (parameters->parent_window), (gpointer *)&parameters->parent_window);
      }
      g_object_unref (parameters->cancellable);
      launch_location_list_free (parameters->locations);
      nautilus_file_list_free (parameters->mountables);
      nautilus_file_list_free (parameters->not_mounted);
      g_free (parameters->activation_directory);
      g_free (parameters->timed_wait_prompt);
      g_assert (parameters->files_handle == NULL);
      g_free (parameters);
}

static void
cancel_activate_callback (gpointer callback_data)
{
      ActivateParameters *parameters = callback_data;

      parameters->timed_wait_active = FALSE;
      
      g_cancellable_cancel (parameters->cancellable);

      if (parameters->files_handle) {
            nautilus_file_list_cancel_call_when_ready (parameters->files_handle);
            parameters->files_handle = NULL;
            activation_parameters_free (parameters);
      }
}

static void
activation_start_timed_cancel (ActivateParameters *parameters)
{
      parameters->timed_wait_active = TRUE;
      eel_timed_wait_start_with_duration
            (DELAY_UNTIL_CANCEL_MSECS,
             cancel_activate_callback,
             parameters,
             parameters->timed_wait_prompt,
             parameters->parent_window);
}

static void
pause_activation_timed_cancel (ActivateParameters *parameters)
{
      if (parameters->timed_wait_active) {
            eel_timed_wait_stop (cancel_activate_callback, parameters);
            parameters->timed_wait_active = FALSE;
      }
}

static void
unpause_activation_timed_cancel (ActivateParameters *parameters)
{
      if (!parameters->timed_wait_active) {
            activation_start_timed_cancel (parameters);
      }
}


static void
activate_mount_op_active (GtkMountOperation *operation,
                    GParamSpec *pspec,
                    ActivateParameters *parameters)
{
      gboolean is_active;

      g_object_get (operation, "is-showing", &is_active, NULL);

      if (is_active) {
            pause_activation_timed_cancel (parameters);
      } else {
            unpause_activation_timed_cancel (parameters);
      }
}

static gboolean
confirm_multiple_windows (GtkWindow *parent_window,
                    int count,
                    gboolean use_tabs)
{
      GtkDialog *dialog;
      char *prompt;
      char *detail;
      int response;

      if (count <= SILENT_WINDOW_OPEN_LIMIT) {
            return TRUE;
      }

      prompt = _("Are you sure you want to open all files?");
      if (use_tabs) {
            detail = g_strdup_printf (ngettext("This will open %d separate tab.",
                                       "This will open %d separate tabs.", count), count);
      } else {
            detail = g_strdup_printf (ngettext("This will open %d separate window.",
                                       "This will open %d separate windows.", count), count);
      }
      dialog = eel_show_yes_no_dialog (prompt, detail, 
                               GTK_STOCK_OK, GTK_STOCK_CANCEL,
                               parent_window);
      g_free (detail);

      response = gtk_dialog_run (dialog);
      gtk_object_destroy (GTK_OBJECT (dialog));

      return response == GTK_RESPONSE_YES;
}

typedef struct {
      NautilusWindowSlotInfo *slot_info;
      GtkWindow *parent_window;
      NautilusFile *file;
      GList *files;
      NautilusWindowOpenMode mode;
      NautilusWindowOpenFlags flags;
      char *activation_directory;
      gboolean user_confirmation;
      char *uri;
} ActivateParametersInstall;

static void
activate_parameters_install_free (ActivateParametersInstall *parameters_install)
{
      if (parameters_install->slot_info) {
            g_object_remove_weak_pointer (G_OBJECT (parameters_install->slot_info), (gpointer *)&parameters_install->slot_info);
      }
      if (parameters_install->parent_window) {
            g_object_remove_weak_pointer (G_OBJECT (parameters_install->parent_window), (gpointer *)&parameters_install->parent_window);
      }
      nautilus_file_unref (parameters_install->file);
      nautilus_file_list_free (parameters_install->files);
      g_free (parameters_install->activation_directory);
      g_free (parameters_install->uri);
      g_free (parameters_install);
}

static char *
get_application_no_mime_type_handler_message (NautilusFile *file, char *uri)
{
      char *uri_for_display;
      char *nice_uri;
      char *error_message;
      GFile *location;

      /* For local files, we want to use filename if possible */
      if (nautilus_file_is_local (file)) {
            location = nautilus_file_get_location (file);
            nice_uri = g_file_get_parse_name (location);
            g_object_unref (location);
      } else {
            nice_uri = g_strdup (uri);
      }

      /* Truncate the URI so it doesn't get insanely wide. Note that even
       * though the dialog uses wrapped text, if the URI doesn't contain
       * white space then the text-wrapping code is too stupid to wrap it.
       */
      uri_for_display = eel_str_middle_truncate (nice_uri, MAX_URI_IN_DIALOG_LENGTH);
      error_message = g_strdup_printf (_("Could not display \"%s\"."), uri_for_display);
      g_free (nice_uri);
      g_free (uri_for_display);
      return error_message;
}

static void
show_unhandled_type_error (ActivateParametersInstall *parameters)
{
      char *mime_type = nautilus_file_get_mime_type (parameters->file);
      char *error_message = get_application_no_mime_type_handler_message (parameters->file, parameters->uri);
      if (g_content_type_is_unknown (mime_type)) {
            eel_show_error_dialog (error_message,
                               _("The file is of an unknown type"),
                               parameters->parent_window);
      } else {
            char *text;
            text = g_strdup_printf (_("There is no application installed for %s files"), g_content_type_get_description (mime_type));
            eel_show_error_dialog (error_message,
                               text,
                               parameters->parent_window);
            g_free (text);
      }
      g_free (error_message);
      g_free (mime_type);
}

static void
search_for_application_dbus_call_notify_cb (DBusGProxy *proxy, DBusGProxyCall *call, ActivateParametersInstall *parameters_install)
{
      gboolean ret;
      GError *error = NULL;
      char *message;
      const char *remote = NULL;

      ret = dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID);
      if (!ret) {
            if (error->domain == DBUS_GERROR && error->code == DBUS_GERROR_REMOTE_EXCEPTION) {
                  remote = dbus_g_error_get_name (error);
            }
            /* we already show an error in the installer if not found, just catch generic failure */
            if (remote == NULL || strcmp (remote, "org.freedesktop.PackageKit.Modify.Failed") == 0) {
                  message = g_strdup_printf ("%s\n%s",
                                       _("There was an internal error trying to search for applications:"),
                                       error->message);
                  eel_show_error_dialog (_("Unable to search for application"), message,
                                     parameters_install->parent_window);
                  g_free (message);
            }
            g_error_free (error);
            return;
      }
      g_object_unref (proxy);

      /* activate the file again */
      nautilus_mime_activate_files (parameters_install->parent_window,
                              parameters_install->slot_info,
                              parameters_install->files,
                              parameters_install->activation_directory,
                              parameters_install->mode,
                              parameters_install->flags,
                              parameters_install->user_confirmation);

      /* parameters_install freed by destroy notify */
}

static void
search_for_application_mime_type (ActivateParametersInstall *parameters_install, const gchar *mime_type)
{
      DBusGConnection *connection;
      DBusGProxy *proxy = NULL;
      guint xid = 0;
      GError *error = NULL;
      DBusGProxyCall *call = NULL;
      GtkWidget *dialog;
      GdkWindow *window;
      const char *mime_types[2];

      /* get bus */
      connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
      if (connection == NULL) {
            g_error_free (error);
            goto out;
      }

      /* get proxy - native clients for for KDE and GNOME */
      proxy = dbus_g_proxy_new_for_name (connection,
                                 "org.freedesktop.PackageKit",
                                 "/org/freedesktop/PackageKit",
                                 "org.freedesktop.PackageKit.Modify");
      if (proxy == NULL) {
            goto out;
      }

      /* get XID from parent window */
      window = gtk_widget_get_window (GTK_WIDGET (parameters_install->parent_window));
      if (window != NULL) {
            xid = GDK_WINDOW_XID (window);
      }

      /* don't timeout, as dbus-glib sets the timeout ~25 seconds */
      dbus_g_proxy_set_default_timeout (proxy, INT_MAX);

      /* invoke the method */
      mime_types[0] = mime_type;
      mime_types[1] = NULL;
      call = dbus_g_proxy_begin_call (proxy, "InstallMimeTypes",
                              (DBusGProxyCallNotify) search_for_application_dbus_call_notify_cb,
                              parameters_install,
                              (GDestroyNotify) activate_parameters_install_free,
                                G_TYPE_UINT, xid,
                                G_TYPE_STRV, mime_types,
                                G_TYPE_STRING, "hide-confirm-search",
                                G_TYPE_INVALID);
      if (call == NULL) {
            dialog = gtk_message_dialog_new (NULL,
                                     GTK_DIALOG_MODAL,
                                     GTK_MESSAGE_ERROR,
                                     GTK_BUTTONS_OK,
                                     _("Could not use system package installer"));
            g_signal_connect (G_OBJECT (dialog), "response",
                          G_CALLBACK (gtk_widget_destroy), NULL);
            gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
            gtk_widget_show (dialog);
            goto out;
      }

      nautilus_debug_log (FALSE, NAUTILUS_DEBUG_LOG_DOMAIN_USER,
                      "InstallMimeType method invoked for %s", mime_type);
out:
      if (call == NULL) {
            /* dbus method was not called, so we're not going to get the async dbus callback */
            activate_parameters_install_free (parameters_install);
      }
}

static void
application_unhandled_file_install (GtkDialog *dialog, gint response_id, ActivateParametersInstall *parameters_install)
{
      char *mime_type;

      gtk_widget_destroy (GTK_WIDGET (dialog));

      if (response_id == GTK_RESPONSE_YES) {
            mime_type = nautilus_file_get_mime_type (parameters_install->file);
            search_for_application_mime_type (parameters_install, mime_type);
            g_free (mime_type);
      } else {
            /* free as we're not going to get the async dbus callback */
            activate_parameters_install_free (parameters_install);
      }
}

static void
packagekit_present_dbus_call_notify_cb (DBusGProxy *proxy, DBusGProxyCall *call, ActivateParametersInstall *parameters_install)
{
      gboolean ret;
      GError *error = NULL;
      char *mime_type;
      char *error_message;
      gboolean present;
      GtkWidget *dialog;


      ret = dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_BOOLEAN, &present, G_TYPE_INVALID);
      if (!ret) {
            g_error_free (error);
            show_unhandled_type_error (parameters_install);
            activate_parameters_install_free (parameters_install);
            present = FALSE;
            goto out;
      }
      if (!present) {
            show_unhandled_type_error (parameters_install);
            activate_parameters_install_free (parameters_install);
            goto out;
      }

      mime_type = nautilus_file_get_mime_type (parameters_install->file);
      error_message = get_application_no_mime_type_handler_message (parameters_install->file, parameters_install->uri);
      /* use a custom dialog to prompt the user to install new software */
      dialog = gtk_message_dialog_new (NULL, 0,
                               GTK_MESSAGE_ERROR,
                               GTK_BUTTONS_YES_NO,
                               "%s", error_message);
      gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
                                      _("There is no application installed for %s files.\nDo you want to search for an application to open this file?"),
                                      g_content_type_get_description (mime_type));
      gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);

      g_signal_connect (dialog, "response", G_CALLBACK (application_unhandled_file_install), parameters_install);
      gtk_widget_show_all (dialog);
      g_free (mime_type);

out:
      g_object_unref (proxy);
}

static void
application_unhandled_uri (ActivateParameters *parameters, char *uri)
{
      gboolean show_install_mime;
      char *mime_type;
      NautilusFile *file;
      ActivateParametersInstall *parameters_install;
      DBusGConnection *connection;
      DBusGProxy *proxy;
      DBusGProxyCall *call;
      GError *error = NULL;

      file = nautilus_file_get_by_uri (uri);

      mime_type = nautilus_file_get_mime_type (file);

      /* copy the parts of parameters we are interested in as the orignal will be unref'd */
      parameters_install = g_new0 (ActivateParametersInstall, 1);
      parameters_install->slot_info = parameters->slot_info;
      g_object_add_weak_pointer (G_OBJECT (parameters_install->slot_info), (gpointer *)&parameters_install->slot_info);
      if (parameters->parent_window) {
            parameters_install->parent_window = parameters->parent_window;
            g_object_add_weak_pointer (G_OBJECT (parameters_install->parent_window), (gpointer *)&parameters_install->parent_window);
      }
      parameters_install->activation_directory = g_strdup (parameters->activation_directory);
      parameters_install->file = nautilus_file_ref (file);
      parameters_install->files = get_file_list_for_launch_locations (parameters->locations);
      parameters_install->mode = parameters->mode;
      parameters_install->flags = parameters->flags;
      parameters_install->user_confirmation = parameters->user_confirmation;
      parameters_install->uri = g_strdup(uri);

#ifdef ENABLE_PACKAGEKIT
      /* allow an admin to disable the PackageKit search functionality */
      show_install_mime = eel_preferences_get_boolean (NAUTILUS_PREFERENCES_INSTALL_MIME_ACTIVATION);
#else
      /* we have no install functionality */
      show_install_mime = FALSE;
#endif
      /* There is no use trying to look for handlers of application/octet-stream */
      if (g_content_type_is_unknown (mime_type)) {
            show_install_mime = FALSE;
      }

      if (!show_install_mime) {
            goto out;
      }

      /* Check whether PackageKit can be spawned */
      connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
      if (connection == NULL) {
            g_error_free (error);
            show_install_mime = FALSE;
            goto out;
      }

      proxy = dbus_g_proxy_new_for_name (connection,
                                 DBUS_SERVICE_DBUS,
                                 DBUS_PATH_DBUS,
                                 DBUS_INTERFACE_DBUS);

      if (proxy == NULL) {
            show_install_mime = FALSE;
            goto out;
      }
      call = dbus_g_proxy_begin_call (proxy, "NameHasOwner",
                              (DBusGProxyCallNotify) packagekit_present_dbus_call_notify_cb,
                              parameters_install,
                              NULL, /* Don't want to free user_data as we need to pass it on */
                                G_TYPE_STRING, "org.freedesktop.PackageKit",
                                G_TYPE_INVALID);
      if (call == NULL) {
            show_install_mime = FALSE;
            goto out;
      }

out:
      if (!show_install_mime) {
            /* show an unhelpful dialog */
            show_unhandled_type_error (parameters_install);
            /* The callback wasn't started, so we have to free the parameters */
            activate_parameters_install_free (parameters_install);
      }

      nautilus_file_unref (file);
      g_free (mime_type);
}

typedef struct {
      GtkWindow *parent_window;
      NautilusFile *file;
} ActivateParametersDesktop;

static void
activate_parameters_desktop_free (ActivateParametersDesktop *parameters_desktop)
{
      if (parameters_desktop->parent_window) {
            g_object_remove_weak_pointer (G_OBJECT (parameters_desktop->parent_window), (gpointer *)&parameters_desktop->parent_window);
      }
      nautilus_file_unref (parameters_desktop->file);
      g_free (parameters_desktop);
}

static void
untrusted_launcher_response_callback (GtkDialog *dialog,
                              int response_id,
                              ActivateParametersDesktop *parameters)
{
      GdkScreen *screen;
      char *uri;
      GFile *file;
      
      switch (response_id) {
      case RESPONSE_RUN:
            screen = gtk_widget_get_screen (GTK_WIDGET (parameters->parent_window));
            uri = nautilus_file_get_uri (parameters->file);
            nautilus_debug_log (FALSE, NAUTILUS_DEBUG_LOG_DOMAIN_USER,
                            "directory view activate_callback launch_desktop_file window=%p: %s",
                            parameters->parent_window, uri);
            nautilus_launch_desktop_file (screen, uri, NULL,
                                    parameters->parent_window);
            g_free (uri);
            break;
      case RESPONSE_MARK_TRUSTED:
            file = nautilus_file_get_location (parameters->file);
            nautilus_file_mark_desktop_file_trusted (file,
                                           parameters->parent_window,
                                           TRUE, 
                                           NULL, NULL);
            g_object_unref (file);
            break;
      default:
            /* Just destroy dialog */
            break;
      }
      
      gtk_widget_destroy (GTK_WIDGET (dialog));
      activate_parameters_desktop_free (parameters);
}

static void
activate_desktop_file (ActivateParameters *parameters,
                   NautilusFile *file)
{
      ActivateParametersDesktop *parameters_desktop;
      char *primary, *secondary, *display_name;
      GtkWidget *dialog;
      GdkScreen *screen;
      char *uri;
      
      screen = gtk_widget_get_screen (GTK_WIDGET (parameters->parent_window));

      if (!nautilus_file_is_trusted_link (file)) {
            /* copy the parts of parameters we are interested in as the orignal will be freed */
            parameters_desktop = g_new0 (ActivateParametersDesktop, 1);
            if (parameters->parent_window) {
                  parameters_desktop->parent_window = parameters->parent_window;
                  g_object_add_weak_pointer (G_OBJECT (parameters_desktop->parent_window), (gpointer *)&parameters_desktop->parent_window);
            }
            parameters_desktop->file = nautilus_file_ref (file);

            primary = _("Untrusted application launcher");
            display_name = nautilus_file_get_display_name (file);
            secondary =
                  g_strdup_printf (_("The application launcher \"%s\" has not been marked as trusted. "
                                 "If you do not know the source of this file, launching it may be unsafe."
                                 ),
                               display_name);
            
            dialog = eel_alert_dialog_new (parameters->parent_window,
                                     0,
                                     GTK_MESSAGE_WARNING,
                                     GTK_BUTTONS_NONE,
                                     primary,
                                     secondary);
            gtk_dialog_add_button (GTK_DIALOG (dialog),
                               _("_Launch Anyway"), RESPONSE_RUN);
            if (nautilus_file_can_set_permissions (file)) {
                  gtk_dialog_add_button (GTK_DIALOG (dialog),
                                     _("Mark as _Trusted"), RESPONSE_MARK_TRUSTED);
            }
            gtk_dialog_add_button (GTK_DIALOG (dialog),
                               GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
            gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL);

            g_signal_connect (dialog, "response",
                          G_CALLBACK (untrusted_launcher_response_callback),
                          parameters_desktop);
            gtk_widget_show (dialog);
            
            g_free (display_name);
            g_free (secondary);
            return;
      }
      
      uri = nautilus_file_get_uri (file);
      nautilus_debug_log (FALSE, NAUTILUS_DEBUG_LOG_DOMAIN_USER,
                      "directory view activate_callback launch_desktop_file window=%p: %s",
                      parameters->parent_window, uri);
      nautilus_launch_desktop_file (screen, uri, NULL,
                              parameters->parent_window);
      g_free (uri);
}

static void
activate_files (ActivateParameters *parameters)
{
      NautilusWindowInfo *window_info;
      NautilusWindowOpenFlags flags;
      NautilusFile *file;
      GList *launch_desktop_files;
      GList *launch_files;
      GList *launch_in_terminal_files;
      GList *open_in_app_uris;
      GList *open_in_app_parameters;
      GList *unhandled_open_in_app_uris;
      ApplicationLaunchParameters *one_parameters;
      GList *open_in_view_files;
      GList *l;
      int count;
      char *uri;
      char *executable_path, *quoted_path, *name;
      char *old_working_dir;
      ActivationAction action;
      GdkScreen *screen;
      LaunchLocation *location;
      
      screen = gtk_widget_get_screen (GTK_WIDGET (parameters->parent_window));

      launch_desktop_files = NULL;
      launch_files = NULL;
      launch_in_terminal_files = NULL;
      open_in_app_uris = NULL;
      open_in_view_files = NULL;

      for (l = parameters->locations; l != NULL; l = l->next) {
            location = l->data;
            file = location->file;

            if (file_was_cancelled (file)) {
                  continue;
            }

            action = get_activation_action (file);
            if (action == ACTIVATION_ACTION_ASK) {
                  /* Special case for executable text files, since it might be
                   * dangerous & unexpected to launch these.
                   */
                  pause_activation_timed_cancel (parameters);
                  action = get_executable_text_file_action (parameters->parent_window, file);
                  unpause_activation_timed_cancel (parameters);
            }

            switch (action) {
            case ACTIVATION_ACTION_LAUNCH_DESKTOP_FILE :
                  launch_desktop_files = g_list_prepend (launch_desktop_files, file);
                  break;
            case ACTIVATION_ACTION_LAUNCH :
                  launch_files = g_list_prepend (launch_files, file);
                  break;
            case ACTIVATION_ACTION_LAUNCH_IN_TERMINAL :
                  launch_in_terminal_files = g_list_prepend (launch_in_terminal_files, file);
                  break;
            case ACTIVATION_ACTION_OPEN_IN_VIEW :
                  open_in_view_files = g_list_prepend (open_in_view_files, file);
                  break;
            case ACTIVATION_ACTION_OPEN_IN_APPLICATION :
                  open_in_app_uris = g_list_prepend (open_in_app_uris, location->uri);
                  break;
            case ACTIVATION_ACTION_DO_NOTHING :
                  break;
            case ACTIVATION_ACTION_ASK :
                  g_assert_not_reached ();
                  break;
            }
      }

      launch_desktop_files = g_list_reverse (launch_desktop_files);
      for (l = launch_desktop_files; l != NULL; l = l->next) {
            file = NAUTILUS_FILE (l->data);

            activate_desktop_file (parameters, file);
      }

      old_working_dir = NULL;
      if (parameters->activation_directory &&
          (launch_files != NULL || launch_in_terminal_files != NULL)) {
            old_working_dir = g_get_current_dir ();
            g_chdir (parameters->activation_directory);
            
      }

      launch_files = g_list_reverse (launch_files);
      for (l = launch_files; l != NULL; l = l->next) {
            file = NAUTILUS_FILE (l->data);

            uri = nautilus_file_get_activation_uri (file);
            executable_path = g_filename_from_uri (uri, NULL, NULL);
            quoted_path = g_shell_quote (executable_path);
            name = nautilus_file_get_name (file);

            nautilus_debug_log (FALSE, NAUTILUS_DEBUG_LOG_DOMAIN_USER,
                            "directory view activate_callback launch_file window=%p: %s",
                            parameters->parent_window, quoted_path);

            nautilus_launch_application_from_command (screen, name, quoted_path, FALSE, NULL);
            g_free (name);
            g_free (quoted_path);
            g_free (executable_path);
            g_free (uri);
                  
      }

      launch_in_terminal_files = g_list_reverse (launch_in_terminal_files);
      for (l = launch_in_terminal_files; l != NULL; l = l->next) {
            file = NAUTILUS_FILE (l->data);

            uri = nautilus_file_get_activation_uri (file);
            executable_path = g_filename_from_uri (uri, NULL, NULL);
            quoted_path = g_shell_quote (executable_path);
            name = nautilus_file_get_name (file);

            nautilus_debug_log (FALSE, NAUTILUS_DEBUG_LOG_DOMAIN_USER,
                            "directory view activate_callback launch_in_terminal window=%p: %s",
                            parameters->parent_window, quoted_path);

            nautilus_launch_application_from_command (screen, name, quoted_path, TRUE, NULL);
            g_free (name);
            g_free (quoted_path);
            g_free (executable_path);
            g_free (uri);
      }

      if (old_working_dir != NULL) {
            g_chdir (old_working_dir);
            g_free (old_working_dir);
      }

      open_in_view_files = g_list_reverse (open_in_view_files);
      count = g_list_length (open_in_view_files);

      flags = parameters->flags;
      if (count > 1) {
            if (eel_preferences_get_boolean (NAUTILUS_PREFERENCES_ENABLE_TABS) &&
                (parameters->flags & NAUTILUS_WINDOW_OPEN_FLAG_NEW_WINDOW) == 0) {
                  flags |= NAUTILUS_WINDOW_OPEN_FLAG_NEW_TAB;
            } else {
                  flags |= NAUTILUS_WINDOW_OPEN_FLAG_NEW_WINDOW;
            }
      }

      if (parameters->slot_info != NULL &&
          (!parameters->user_confirmation ||
           confirm_multiple_windows (parameters->parent_window, count,
                               (flags & NAUTILUS_WINDOW_OPEN_FLAG_NEW_TAB) != 0))) {

            if ((flags & NAUTILUS_WINDOW_OPEN_FLAG_NEW_TAB) != 0 &&
                eel_preferences_get_enum (NAUTILUS_PREFERENCES_NEW_TAB_POSITION) ==
                NAUTILUS_NEW_TAB_POSITION_AFTER_CURRENT_TAB) {
                  /* When inserting N tabs after the current one,
                   * we first open tab N, then tab N-1, ..., then tab 0.
                   * Each of them is appended to the current tab, i.e.
                   * prepended to the list of tabs to open.
                   */
                  open_in_view_files = g_list_reverse (open_in_view_files);
            }


            for (l = open_in_view_files; l != NULL; l = l->next) {
                  GFile *f;
                  /* The ui should ask for navigation or object windows
                   * depending on what the current one is */
                  file = NAUTILUS_FILE (l->data);

                  uri = nautilus_file_get_activation_uri (file);
                  f = g_file_new_for_uri (uri);
                  nautilus_window_slot_info_open_location (parameters->slot_info,
                                                 f, parameters->mode, flags, NULL);
                  g_object_unref (f);
                  g_free (uri);
            }
      }

      open_in_app_parameters = NULL;
      unhandled_open_in_app_uris = NULL;

      if (open_in_app_uris != NULL) {
            open_in_app_uris = g_list_reverse (open_in_app_uris);

            open_in_app_parameters = make_activation_parameters
                  (open_in_app_uris, &unhandled_open_in_app_uris);
      }

      for (l = open_in_app_parameters; l != NULL; l = l->next) {
            one_parameters = l->data;

            nautilus_launch_application_by_uri (one_parameters->application,
                                        one_parameters->uris,
                                        parameters->parent_window);
            application_launch_parameters_free (one_parameters);
      }

      for (l = unhandled_open_in_app_uris; l != NULL; l = l->next) {
            uri = l->data;

            /* this does not block */
            application_unhandled_uri (parameters, uri);
      }

      window_info = NULL;
      if (parameters->slot_info != NULL) {
            window_info = nautilus_window_slot_info_get_window (parameters->slot_info);
      }

      if (open_in_app_parameters != NULL ||
          unhandled_open_in_app_uris != NULL) {
            if ((parameters->flags & NAUTILUS_WINDOW_OPEN_FLAG_CLOSE_BEHIND) != 0 &&
                window_info != NULL && 
                 nautilus_window_info_get_window_type (window_info) == NAUTILUS_WINDOW_SPATIAL) {
                  nautilus_window_info_close (window_info);
            }
      }

      g_list_free (launch_desktop_files);
      g_list_free (launch_files);
      g_list_free (launch_in_terminal_files);
      g_list_free (open_in_view_files);
      g_list_free (open_in_app_uris);
      g_list_free (open_in_app_parameters);
      g_list_free (unhandled_open_in_app_uris);
      
      activation_parameters_free (parameters);
}

static void 
activation_mount_not_mounted_callback (GObject *source_object,
                               GAsyncResult *res,
                               gpointer user_data)
{
      ActivateParameters *parameters = user_data;
      GError *error;
      NautilusFile *file;
      LaunchLocation *loc;

      file = parameters->not_mounted->data;
            
      error = NULL;
      if (!g_file_mount_enclosing_volume_finish (G_FILE (source_object), res, &error)) {
            if (error->domain != G_IO_ERROR ||
                (error->code != G_IO_ERROR_CANCELLED &&
                 error->code != G_IO_ERROR_FAILED_HANDLED &&
                 error->code != G_IO_ERROR_ALREADY_MOUNTED)) {
                  eel_show_error_dialog (_("Unable to mount location"),
                                     error->message, NULL);
            }

            if (error->domain != G_IO_ERROR ||
                error->code != G_IO_ERROR_ALREADY_MOUNTED) {
                  loc = find_launch_location_for_file (parameters->locations,
                                               file);
                  if (loc) {
                        parameters->locations =
                              g_list_remove (parameters->locations, loc); 
                        launch_location_free (loc);
                  }
            }

            g_error_free (error);
      } 
      
      parameters->not_mounted = g_list_delete_link (parameters->not_mounted,
                                          parameters->not_mounted);
      nautilus_file_unref (file);

      activation_mount_not_mounted (parameters);
}

static void
activation_mount_not_mounted (ActivateParameters *parameters)
{
      NautilusFile *file;
      GFile *location;
      LaunchLocation *loc;
      GMountOperation *mount_op;
      GList *l, *next, *files;

      if (parameters->not_mounted != NULL) {
            file = parameters->not_mounted->data;
            mount_op = gtk_mount_operation_new (parameters->parent_window);
            g_signal_connect (mount_op, "notify::is-showing",
                          G_CALLBACK (activate_mount_op_active), parameters);
            location = nautilus_file_get_location (file);
            g_file_mount_enclosing_volume (location, 0, mount_op, parameters->cancellable,
                                     activation_mount_not_mounted_callback, parameters);
            g_object_unref (location);
            /* unref mount_op here - g_file_mount_enclosing_volume() does ref for itself */
            g_object_unref (mount_op);
            return;
      }

      parameters->tried_mounting = TRUE;

      if (parameters->locations == NULL) {
            activation_parameters_free (parameters);
            return;
      }
      
      /*  once the mount is finished, refresh all attributes        */
      /*  - fixes new windows not appearing after successful mount  */
      for (l = parameters->locations; l != NULL; l = next) {
            loc = l->data;
            next = l->next;
            nautilus_file_invalidate_all_attributes (loc->file);
      }
      
      files = get_file_list_for_launch_locations (parameters->locations);
      nautilus_file_list_call_when_ready
            (files,
             nautilus_mime_actions_get_required_file_attributes () | NAUTILUS_FILE_ATTRIBUTE_LINK_INFO,
             &parameters->files_handle,
             activate_callback, parameters);
      nautilus_file_list_free (files);
}


static void
activate_callback (GList *files, gpointer callback_data)
{
      ActivateParameters *parameters = callback_data;
      GList *l, *next;
      NautilusFile *file;
      LaunchLocation *location;

      parameters->files_handle = NULL;

      for (l = parameters->locations; l != NULL; l = next) {
            location = l->data;
            file = location->file;
            next = l->next;

            if (file_was_cancelled (file)) {
                  launch_location_free (location);
                  parameters->locations = g_list_delete_link (parameters->locations, l);
                  continue;
            }

            if (file_was_not_mounted (file)) {
                  if (parameters->tried_mounting) {
                        launch_location_free (location);
                        parameters->locations = g_list_delete_link (parameters->locations, l);
                  } else {
                        parameters->not_mounted = g_list_prepend (parameters->not_mounted,
                                                        nautilus_file_ref (file));
                  }
                  continue;
            }
      }


      if (parameters->not_mounted != NULL) {
            activation_mount_not_mounted (parameters);
      } else {
            activate_files (parameters);
      }
}

static void
activate_activation_uris_ready_callback (GList *files_ignore,
                               gpointer callback_data)
{
      ActivateParameters *parameters = callback_data;
      GList *l, *next, *files;
      NautilusFile *file;
      LaunchLocation *location;

      parameters->files_handle = NULL;
      
      for (l = parameters->locations; l != NULL; l = next) {
            location = l->data;
            file = location->file;
            next = l->next;

            if (file_was_cancelled (file)) {
                  launch_location_free (location);
                  parameters->locations = g_list_delete_link (parameters->locations, l);
                  continue;
            }

            if (nautilus_file_is_broken_symbolic_link (file)) {
                  launch_location_free (location);
                  parameters->locations = g_list_delete_link (parameters->locations, l);
                  report_broken_symbolic_link (parameters->parent_window, file);
                  continue;
            }

            if (nautilus_file_get_file_type (file) == G_FILE_TYPE_MOUNTABLE &&
                !nautilus_file_has_activation_uri (file)) {
                  /* Don't launch these... There is nothing we
                     can do */
                  launch_location_free (location);
                  parameters->locations = g_list_delete_link (parameters->locations, l);
                  continue;
            }
            
      }

      if (parameters->locations == NULL) {
            activation_parameters_free (parameters);
            return;
      }

      /* Convert the files to the actual activation uri files */
      for (l = parameters->locations; l != NULL; l = l->next) {
            char *uri;
            location = l->data;

            /* We want the file for the activation URI since we care
             * about the attributes for that, not for the original file.
             */
            uri = nautilus_file_get_activation_uri (location->file);
            if (uri != NULL) {
                  launch_location_update_from_uri (location, uri);
            }
            g_free (uri);
      }


      /* get the parameters for the actual files */   
      files = get_file_list_for_launch_locations (parameters->locations);
      nautilus_file_list_call_when_ready
            (files,
             nautilus_mime_actions_get_required_file_attributes () | NAUTILUS_FILE_ATTRIBUTE_LINK_INFO,
             &parameters->files_handle,
             activate_callback, parameters);
      nautilus_file_list_free (files);
}

static void
activation_get_activation_uris (ActivateParameters *parameters)
{
      GList *l, *files;
      NautilusFile *file;
      LaunchLocation *location;

      /* link target info might be stale, re-read it */
      for (l = parameters->locations; l != NULL; l = l->next) {
            location = l->data;
            file = location->file;

            if (file_was_cancelled (file)) {
                  launch_location_free (location);
                  parameters->locations = g_list_delete_link (parameters->locations, l);
                  continue;
            }
            
            if (nautilus_file_is_symbolic_link (file)) {
                  nautilus_file_invalidate_attributes 
                        (file,
                         NAUTILUS_FILE_ATTRIBUTE_INFO |
                         NAUTILUS_FILE_ATTRIBUTE_LINK_INFO);
            }
      }

      if (parameters->locations == NULL) {
            activation_parameters_free (parameters);
            return;
      }

      files = get_file_list_for_launch_locations (parameters->locations);
      nautilus_file_list_call_when_ready
            (files,
             NAUTILUS_FILE_ATTRIBUTE_INFO |
             NAUTILUS_FILE_ATTRIBUTE_LINK_INFO,
             &parameters->files_handle,
             activate_activation_uris_ready_callback, parameters);
      nautilus_file_list_free (files);
}

static void
activation_mountable_mounted (NautilusFile  *file,
                        GFile         *result_location,
                        GError        *error,
                        gpointer       callback_data)
{
      ActivateParameters *parameters = callback_data;
      NautilusFile *target_file;
      LaunchLocation *location;

      /* Remove from list of files that have to be mounted */
      parameters->mountables = g_list_remove (parameters->mountables, file); 
      nautilus_file_unref (file);

      
      if (error == NULL) {
            /* Replace file with the result of the mount */
            target_file = nautilus_file_get (result_location);

            location = find_launch_location_for_file (parameters->locations,
                                            file);
            if (location) {
                  launch_location_update_from_file (location, target_file);
            }
            nautilus_file_unref (target_file);
      } else {
            /* Remove failed file */
            
            if (error->domain != G_IO_ERROR ||
                (error->code != G_IO_ERROR_FAILED_HANDLED &&
                 error->code != G_IO_ERROR_ALREADY_MOUNTED)) {
                  location = find_launch_location_for_file (parameters->locations,
                                                  file);
                  if (location) {
                        parameters->locations =
                              g_list_remove (parameters->locations,
                                           location); 
                        launch_location_free (location);
                  }
            }
            
            if (error->domain != G_IO_ERROR ||
                (error->code != G_IO_ERROR_CANCELLED &&
                 error->code != G_IO_ERROR_FAILED_HANDLED &&
                 error->code != G_IO_ERROR_ALREADY_MOUNTED)) {
                  eel_show_error_dialog (_("Unable to mount location"),
                                     error->message, NULL);
            }

            if (error->code == G_IO_ERROR_CANCELLED) {
                  activation_parameters_free (parameters);
                  return;
            }
      }

      /* Mount more mountables */
      activation_mount_mountables (parameters);
}


static void
activation_mount_mountables (ActivateParameters *parameters)
{
      NautilusFile *file;
      GMountOperation *mount_op;

      if (parameters->mountables != NULL) {
            file = parameters->mountables->data;
            mount_op = gtk_mount_operation_new (parameters->parent_window);
            g_signal_connect (mount_op, "notify::is-showing",
                          G_CALLBACK (activate_mount_op_active), parameters);
            nautilus_file_mount (file,
                             mount_op,
                             parameters->cancellable,
                             activation_mountable_mounted,
                             parameters);
            g_object_unref (mount_op);
            return;
      }

      activation_get_activation_uris (parameters);
}

/**
 * nautilus_mime_activate_files:
 * 
 * Activate a list of files. Each one might launch with an application or
 * with a component. This is normally called only by subclasses.
 * @view: FMDirectoryView in question.
 * @files: A GList of NautilusFiles to activate.
 * 
 **/
void
nautilus_mime_activate_files (GtkWindow *parent_window,
                        NautilusWindowSlotInfo *slot_info,
                        GList *files,
                        const char *launch_directory,
                        NautilusWindowOpenMode mode,
                        NautilusWindowOpenFlags flags,
                        gboolean user_confirmation)
{
      ActivateParameters *parameters;
      char *file_name;
      int file_count;
      GList *l, *next;
      NautilusFile *file;
      LaunchLocation *location;

      if (files == NULL) {
            return;
      }

      nautilus_debug_log_with_file_list (FALSE, NAUTILUS_DEBUG_LOG_DOMAIN_USER, files,
                                 "nautilus_mime_activate_files window=%p",
                                 parent_window);

      parameters = g_new0 (ActivateParameters, 1);
      parameters->slot_info = slot_info;
      g_object_add_weak_pointer (G_OBJECT (parameters->slot_info), (gpointer *)&parameters->slot_info);
      if (parent_window) {
            parameters->parent_window = parent_window;
            g_object_add_weak_pointer (G_OBJECT (parameters->parent_window), (gpointer *)&parameters->parent_window);
      }
      parameters->cancellable = g_cancellable_new ();
      parameters->activation_directory = g_strdup (launch_directory);
      parameters->locations = launch_locations_from_file_list (files);
      parameters->mode = mode;
      parameters->flags = flags;
      parameters->user_confirmation = user_confirmation;

      file_count = g_list_length (files);
      if (file_count == 1) {
            file_name = nautilus_file_get_display_name (files->data);
            parameters->timed_wait_prompt = g_strdup_printf (_("Opening \"%s\"."), file_name);
            g_free (file_name);
      } else {
            parameters->timed_wait_prompt = g_strdup_printf (ngettext ("Opening %d item.",
                                                         "Opening %d items.",
                                                         file_count),
                                                 file_count);
      }

      
      for (l = parameters->locations; l != NULL; l = next) {
            location = l->data;
            file = location->file;
            next = l->next;
            
            if (nautilus_file_can_mount (file)) {
                  parameters->mountables = g_list_prepend (parameters->mountables,
                                                 nautilus_file_ref (file));
            }
      }
      
      activation_start_timed_cancel (parameters);
      activation_mount_mountables (parameters);
}

/**
 * nautilus_mime_activate_file:
 * 
 * Activate a file in this view. This might involve switching the displayed
 * location for the current window, or launching an application.
 * @view: FMDirectoryView in question.
 * @file: A NautilusFile representing the file in this view to activate.
 * @use_new_window: Should this item be opened in a new window?
 * 
 **/

void
nautilus_mime_activate_file (GtkWindow *parent_window,
                       NautilusWindowSlotInfo *slot_info,
                       NautilusFile *file,
                       const char *launch_directory,
                       NautilusWindowOpenMode mode,
                       NautilusWindowOpenFlags flags)
{
      GList *files;

      g_return_if_fail (NAUTILUS_IS_FILE (file));

      files = g_list_prepend (NULL, file);
      nautilus_mime_activate_files (parent_window, slot_info, files, launch_directory, mode, flags, FALSE);
      g_list_free (files);
}

Generated by  Doxygen 1.6.0   Back to index