[nautilus/wip/razvan/automatic-decompression: 2/2] Add extract operation



commit 76db3e2367503b82da3e17cbfff3f1ea25e2f161
Author: Razvan Chitu <razvan ch95 gmail com>
Date:   Mon Jun 27 11:43:22 2016 +0300

    Add extract operation

 configure.ac                                       |    2 +
 data/org.gnome.nautilus.gschema.xml                |    5 +
 src/nautilus-application.c                         |   19 +
 src/nautilus-application.h                         |    2 +
 src/nautilus-file-operations.c                     |  418 +++++++++++++++++++-
 src/nautilus-file-operations.h                     |    9 +
 src/nautilus-file.c                                |   39 +--
 src/nautilus-files-view.c                          |  134 ++++++-
 src/nautilus-global-preferences.h                  |    3 +
 src/nautilus-mime-actions.c                        |   38 ++
 src/nautilus-mime-actions.h                        |    1 +
 src/nautilus-operations-manager.c                  |  151 +++++++
 src/nautilus-operations-manager.h                  |   12 +
 src/nautilus-preferences-window.c                  |    5 +
 .../ui/nautilus-files-view-context-menus.ui        |   10 +
 src/resources/ui/nautilus-preferences-window.ui    |   46 +++
 16 files changed, 857 insertions(+), 37 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 493c5de..3d6b0a2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -11,6 +11,7 @@ m4_define(exif_minver,                 0.6.20)
 m4_define(exempi_minver,               2.1.0)
 m4_define(notify_minver,               0.7.0)
 m4_define(schemas_minver,              3.8.0)
+m4_define(autoar_minver,               0.1)
 
 dnl 1. If the library code has changed at all since last release, then increment revision.
 dnl 2. If any interfaces have been added, then increment current and set revision to 0.
@@ -270,6 +271,7 @@ dnl base libs
 PKG_CHECK_MODULES(BASE, [
        gtk+-3.0 >= gtk_minver
        glib-2.0 >= glib_minver
+    gnome-autoar >= autoar_minver
 ])
 
 dnl common libs (eel, nautilus)
diff --git a/data/org.gnome.nautilus.gschema.xml b/data/org.gnome.nautilus.gschema.xml
index 4be43d2..4128180 100644
--- a/data/org.gnome.nautilus.gschema.xml
+++ b/data/org.gnome.nautilus.gschema.xml
@@ -103,6 +103,11 @@
       <summary>Whether to ask for confirmation when deleting files, or emptying the Trash</summary>
       <description>If set to true, then Nautilus will ask for confirmation when you attempt to delete files, 
or empty the Trash.</description>
     </key>
+    <key type="b" name="automatic-decompression">
+      <default>true</default>
+      <summary>Whether to extract compressed files instead of opening them</summary>
+      <description>If set to true, then Nautilus will automatically extract compressed files instead of 
opening them in another application</description>
+    </key>
     <key name="show-directory-item-counts" enum="org.gnome.nautilus.SpeedTradeoff">
       <aliases><alias value='local_only' target='local-only'/></aliases>
       <default>'local-only'</default>
diff --git a/src/nautilus-application.c b/src/nautilus-application.c
index 989f31d..2e2b474 100644
--- a/src/nautilus-application.c
+++ b/src/nautilus-application.c
@@ -76,6 +76,8 @@ typedef struct {
        GList *windows;
 
         GHashTable *notifications;
+
+        AutoarPref *arpref;
 } NautilusApplicationPrivate;
 
 G_DEFINE_TYPE_WITH_PRIVATE (NautilusApplication, nautilus_application, GTK_TYPE_APPLICATION);
@@ -547,6 +549,8 @@ nautilus_application_finalize (GObject *object)
        g_clear_object (&priv->fdb_manager);
        g_clear_object (&priv->search_provider);
 
+        g_clear_object (&priv->arpref);
+
        g_list_free (priv->windows);
 
         g_hash_table_destroy (priv->notifications);
@@ -948,6 +952,7 @@ static void
 nautilus_application_init (NautilusApplication *self)
 {
         NautilusApplicationPrivate *priv;
+        g_autoptr (GSettings) archive_settings;
 
         priv = nautilus_application_get_instance_private (self);
 
@@ -960,6 +965,10 @@ nautilus_application_init (NautilusApplication *self)
 
         nautilus_ensure_extension_points ();
         nautilus_ensure_extension_builtins ();
+
+
+        archive_settings = g_settings_new (AUTOAR_PREF_DEFAULT_GSCHEMA_ID);
+        priv->arpref = autoar_pref_new_with_gsettings (archive_settings);
 }
 
 static void
@@ -1038,6 +1047,16 @@ nautilus_application_get_default (void)
         return self;
 }
 
+AutoarPref *
+nautilus_application_get_arpref (NautilusApplication *self)
+{
+        NautilusApplicationPrivate *priv;
+
+        priv = nautilus_application_get_instance_private (self);
+
+        return priv->arpref;
+}
+
 void
 nautilus_application_send_notification (NautilusApplication *self,
                                         const gchar         *notification_id,
diff --git a/src/nautilus-application.h b/src/nautilus-application.h
index 197a276..6498816 100644
--- a/src/nautilus-application.h
+++ b/src/nautilus-application.h
@@ -24,6 +24,7 @@
 #include <gdk/gdk.h>
 #include <gio/gio.h>
 #include <gtk/gtk.h>
+#include <gnome-autoar/autoar.h>
 
 #include "nautilus-bookmark-list.h"
 #include "nautilus-window.h"
@@ -71,6 +72,7 @@ void nautilus_application_open_location_full (NautilusApplication     *applicati
                                               NautilusWindowSlot      *target_slot);
 
 NautilusApplication *nautilus_application_get_default (void);
+AutoarPref * nautilus_application_get_arpref (NautilusApplication *self);
 void nautilus_application_send_notification (NautilusApplication *self,
                                              const gchar         *notification_id,
                                              GNotification       *notification);
diff --git a/src/nautilus-file-operations.c b/src/nautilus-file-operations.c
index 7ea2ada..10487d5 100644
--- a/src/nautilus-file-operations.c
+++ b/src/nautilus-file-operations.c
@@ -51,6 +51,8 @@
 #include <gtk/gtk.h>
 #include <gio/gio.h>
 #include <glib.h>
+#include <gnome-autoar/autoar.h>
+
 #include "nautilus-operations-manager.h"
 #include "nautilus-file-changes-queue.h"
 #include "nautilus-file-private.h"
@@ -61,6 +63,7 @@
 #include "nautilus-file-conflict-dialog.h"
 #include "nautilus-file-undo-operations.h"
 #include "nautilus-file-undo-manager.h"
+#include "nautilus-application.h"
 
 /* TODO: TESTING!!! */
 
@@ -170,6 +173,23 @@ typedef struct {
        int last_reported_files_left;
 } TransferInfo;
 
+typedef struct {
+        CommonJob common;
+        GFile *source;
+        GFile *output;
+
+       NautilusExtractCallback done_callback;
+       gpointer done_callback_data;
+
+       AutoarExtract *arextract;
+
+       GFile *confirmed_destination;
+       gboolean success;
+
+        guint64 total_size;
+        guint total_files;
+} ExtractJob;
+
 #define SECONDS_NEEDED_FOR_RELIABLE_TRANSFER_RATE 8
 #define NSEC_PER_MICROSEC 1000
 
@@ -1838,7 +1858,7 @@ delete_files (CommonJob *job, GList *files, int *files_skipped)
        SourceInfo source_info;
        TransferInfo transfer_info;
        gboolean skipped_file;
-       
+
        if (job_aborted (job)) {
                return;
        }
@@ -5780,7 +5800,7 @@ link_file (CopyMoveJob *job,
                        g_object_unref (new_dest);
                }
        }
-       /* Conflict */
+       /* FileConflict */
        if (error != NULL && IS_IO_ERROR (error, EXISTS)) {
                g_object_unref (dest);
                dest = get_target_file_for_link (src, dest_dir, *dest_fs_type, count++);
@@ -7108,6 +7128,400 @@ nautilus_file_mark_desktop_file_trusted (GFile *file,
        g_object_unref (task);
 }
 
+static FileConflictResponse *
+signal_extract_conflict (CommonJob *job,
+                         GFile *src,
+                         GFile *dest,
+                         GFile *dest_dir)
+{
+        FileConflictResponse *response;
+
+        g_timer_stop (job->time);
+        nautilus_progress_info_pause (job->progress);
+
+        response = get_extract_file_conflict_response (job->parent_window,
+                                                       src,
+                                                       dest,
+                                                       dest_dir);
+
+        nautilus_progress_info_resume (job->progress);
+        g_timer_continue (job->time);
+
+        return response;
+}
+
+static gboolean
+remove_file (GFile *file,
+             GError **error)
+{
+        gboolean success = TRUE;
+        g_autoptr (GFileEnumerator) enumerator = NULL;
+
+        if (g_file_delete (file, NULL, error)) {
+                return TRUE;
+        }
+
+        if (g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_NOT_EMPTY)) {
+                g_clear_error (error);
+        } else {
+                return FALSE;
+        }
+
+        enumerator = g_file_enumerate_children (file,
+                                                G_FILE_ATTRIBUTE_STANDARD_NAME,
+                                                G_FILE_QUERY_INFO_NONE,
+                                                NULL, error);
+
+        if (enumerator) {
+                GFileInfo *info;
+
+                while ((info = g_file_enumerator_next_file (enumerator, NULL, error)) != NULL) {
+                        g_autoptr(GFile) child;
+
+                        child = g_file_get_child (file, g_file_info_get_name (info));
+
+                        success = remove_file (child, error);
+
+                        g_object_unref (info);
+
+                        if (!success) {
+                                return FALSE;
+                        }
+                }
+        } else {
+                return FALSE;
+        }
+
+        g_file_delete (file, NULL, error);
+
+        return success && *error == NULL;
+}
+
+static void
+extract_task_done (GObject *source_object,
+                   GAsyncResult *res,
+                   gpointer user_data)
+{
+        ExtractJob *extract_job = user_data;
+
+        if (extract_job->done_callback) {
+                extract_job->done_callback (extract_job->source,
+                                            extract_job->confirmed_destination,
+                                            extract_job->success,
+                                            extract_job->done_callback_data);
+        }
+
+        g_clear_object (&extract_job->source);
+        g_clear_object (&extract_job->output);
+        g_clear_object (&extract_job->arextract);
+        g_clear_object (&extract_job->confirmed_destination);
+
+        finalize_common ((CommonJob *)extract_job);
+}
+
+static void
+extract_scanned_handler (AutoarExtract *arextract,
+                         guint          files_count,
+                         gpointer       user_data)
+{
+        ExtractJob *extract_job = user_data;
+
+        extract_job->total_files = autoar_extract_get_files (arextract);
+        extract_job->total_size = autoar_extract_get_size (arextract);
+}
+
+static GFile*
+extract_confirm_dest_handler (AutoarExtract *arextract,
+                              GFile         *destination,
+                              GList         *files,
+                              gpointer       user_data)
+{
+        ExtractJob *extract_job = user_data;
+        GFileType file_type;
+        g_autoptr (GFile) destination_directory;
+        GFile *confirmed_destination = NULL;
+
+        nautilus_progress_info_set_details (extract_job->common.progress,
+                                            _("Checking destination"));
+
+        destination_directory = g_file_get_parent (destination);
+
+        confirmed_destination = g_object_ref (destination);
+        file_type = g_file_query_file_type (confirmed_destination,
+                                            G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                            NULL);
+
+        while (file_type != G_FILE_TYPE_UNKNOWN) {
+                FileConflictResponse *response;
+                GError *error = NULL;
+
+                response = signal_extract_conflict ((CommonJob *)extract_job,
+                                                    extract_job->source,
+                                                    confirmed_destination,
+                                                    destination_directory);
+
+                switch (response->id) {
+                        case GTK_RESPONSE_CANCEL:
+                        case GTK_RESPONSE_DELETE_EVENT:
+                                g_cancellable_cancel (extract_job->common.cancellable);
+
+                                conflict_response_data_free (response);
+
+                                goto out;
+                        case CONFLICT_RESPONSE_REPLACE:
+                                nautilus_progress_info_set_details (extract_job->common.progress,
+                                                                    _("Replacing existing destination"));
+
+                                if (!remove_file (confirmed_destination, &error)) {
+                                        run_error ((CommonJob *)extract_job,
+                                                   f (_("Error while extracting “%B”."),
+                                                      extract_job->source),
+                                                   f (_("The existing destination “%B” could not be 
replaced."),
+                                                      confirmed_destination),
+                                                   error->message,
+                                                   FALSE,
+                                                   CANCEL,
+                                                   NULL);
+
+                                       g_cancellable_cancel (extract_job->common.cancellable);
+
+                                        g_error_free (error);
+                                }
+
+                               conflict_response_data_free (response);
+
+                                goto out;
+                        case CONFLICT_RESPONSE_RENAME:
+                                g_clear_object (&confirmed_destination);
+
+                                confirmed_destination = get_target_file_for_display_name 
(destination_directory,
+                                                                                          
response->new_name);
+
+                                break;
+                        default:
+                                g_assert_not_reached ();
+                }
+
+                file_type = g_file_query_file_type (confirmed_destination,
+                                                    G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                                    NULL);
+
+                conflict_response_data_free (response);
+
+        }
+out:
+       extract_job->confirmed_destination = confirmed_destination;
+
+        if (confirmed_destination != NULL) {
+                g_object_ref (confirmed_destination);
+        }
+
+        return confirmed_destination;
+}
+
+static void
+extract_progress_handler (AutoarExtract *arextract,
+                          guint64        completed_size,
+                          guint          completed_files,
+                          gpointer       user_data)
+{
+        ExtractJob *extract_job = user_data;
+        CommonJob *common = user_data;
+        char *details;
+        int files_left;
+        double elapsed, transfer_rate;
+        int remaining_time;
+
+        files_left = extract_job->total_files - completed_files;
+
+        nautilus_progress_info_take_status (common->progress,
+                                            f (_("Extracting “%B”"), extract_job->source));
+
+        elapsed = g_timer_elapsed (common->time, NULL);
+        transfer_rate = 0;
+        remaining_time = INT_MAX;
+        if (elapsed > 0) {
+                transfer_rate = completed_size / elapsed;
+                if (transfer_rate > 0) {
+                        remaining_time = (extract_job->total_size - completed_size) / transfer_rate;
+                }
+        }
+
+        if (transfer_rate == 0) {
+                transfer_rate = completed_files / elapsed;
+                if (transfer_rate > 0) {
+                        remaining_time = (extract_job->total_files - completed_files) / transfer_rate;
+                }
+        }
+
+        if (elapsed < SECONDS_NEEDED_FOR_RELIABLE_TRANSFER_RATE &&
+            transfer_rate > 0) {
+                if (extract_job->total_files == 1) {
+                        /* To translators: %S will expand to a size like "2 bytes" or "3 MB", so something 
like "4 kb / 4 MB" */
+                        details = f (_("%S / %S"), completed_size, extract_job->total_size);
+                } else {
+                        details = f (_("%'d / %'d"),
+                                     files_left > 0 ? completed_files + 1 : completed_files,
+                                     extract_job->total_files);
+                }
+        } else {
+                if (extract_job->total_files == 1) {
+                        if (files_left > 0) {
+                                /* To translators: %S will expand to a size like "2 bytes" or "3 MB", %T to 
a time duration like
+                                 * "2 minutes". So the whole thing will be something like "2 kb / 4 MB -- 2 
hours left (4kb/sec)"
+                                 *
+                                 * The singular/plural form will be used depending on the remaining time 
(i.e. the %T argument).
+                                 */
+                                details = f (ngettext ("%S / %S \xE2\x80\x94 %T left (%S/sec)",
+                                                       "%S / %S \xE2\x80\x94 %T left (%S/sec)",
+                                                       seconds_count_format_time_units (remaining_time)),
+                                             completed_size, extract_job->total_size,
+                                             remaining_time,
+                                             (goffset)transfer_rate);
+                        } else {
+                                /* To translators: %S will expand to a size like "2 bytes" or "3 MB". */
+                                details = f (_("%S / %S"),
+                                             completed_size,
+                                             extract_job->total_size);
+                        }
+                } else {
+                        if (files_left > 0) {
+                                /* To translators: %T will expand to a time duration like "2 minutes".
+                                 * So the whole thing will be something like "1 / 5 -- 2 hours left 
(4kb/sec)"
+                                 *
+                                 * The singular/plural form will be used depending on the remaining time 
(i.e. the %T argument).
+                                 */
+                                details = f (ngettext ("%'d / %'d \xE2\x80\x94 %T left (%S/sec)",
+                                                       "%'d / %'d \xE2\x80\x94 %T left (%S/sec)",
+                                                       seconds_count_format_time_units (remaining_time)),
+                                             completed_files + 1, extract_job->total_files,
+                                             remaining_time,
+                                             (goffset)transfer_rate);
+                        } else {
+                                /* To translators: %'d is the number of files completed for the operation,
+                                 * so it will be something like 2/14. */
+                                details = f (_("%'d / %'d"),
+                                             completed_files,
+                                             extract_job->total_files);
+                        }
+                }
+        }
+
+        nautilus_progress_info_take_details (common->progress, details);
+
+        if (elapsed > SECONDS_NEEDED_FOR_APROXIMATE_TRANSFER_RATE) {
+                nautilus_progress_info_set_remaining_time (common->progress,
+                                                           remaining_time);
+                nautilus_progress_info_set_elapsed_time (common->progress,
+                                                         elapsed);
+        }
+
+        nautilus_progress_info_set_progress (common->progress,
+                                             completed_size,
+                                             extract_job->total_size);
+}
+
+static void
+extract_error_handler (AutoarExtract *arextract,
+                       GError *error,
+                       gpointer user_data)
+{
+        ExtractJob *extract_job = user_data;
+
+        nautilus_progress_info_take_status (extract_job->common.progress,
+                                            f (_("Error extracting “%B””"),
+                                               extract_job->source));
+
+        run_error ((CommonJob *)extract_job,
+                   f (_("There was an error while extracting “%B”."),
+                      extract_job->source),
+                   g_strdup (error->message),
+                   NULL,
+                   FALSE,
+                   CANCEL,
+                   NULL);
+}
+
+static void
+extract_completed_handler (AutoarExtract *arextract,
+                          gpointer user_data)
+{
+        ExtractJob *extract_job = user_data;
+
+        extract_job->success = TRUE;
+
+        nautilus_progress_info_take_status (extract_job->common.progress,
+                                            f (_("Finished extracting “%B” to “%B”"),
+                                               extract_job->source,
+                                               extract_job->confirmed_destination));
+
+        nautilus_progress_info_set_destination (extract_job->common.progress,
+                                               extract_job->confirmed_destination);
+}
+
+static void
+extract_task_thread_func (GTask        *task,
+                          gpointer      source_object,
+                          gpointer      task_data,
+                          GCancellable *cancellable)
+{
+        ExtractJob *extract_job = task_data;
+        AutoarPref *arpref;
+
+        arpref = nautilus_application_get_arpref (nautilus_application_get_default ());
+
+        extract_job->arextract = autoar_extract_new_file (extract_job->source,
+                                                          extract_job->output,
+                                                          arpref);
+
+        autoar_extract_set_notify_interval (extract_job->arextract, 100000);
+
+        g_signal_connect (extract_job->arextract, "scanned",
+                          G_CALLBACK (extract_scanned_handler), extract_job);
+        g_signal_connect (extract_job->arextract, "error",
+                          G_CALLBACK (extract_error_handler), extract_job);
+        g_signal_connect (extract_job->arextract, "confirm-dest",
+                          G_CALLBACK (extract_confirm_dest_handler), extract_job);
+        g_signal_connect (extract_job->arextract, "progress",
+                          G_CALLBACK (extract_progress_handler), extract_job);
+        g_signal_connect (extract_job->arextract, "completed",
+                          G_CALLBACK (extract_completed_handler), extract_job);
+
+        g_timer_start (extract_job->common.time);
+
+        nautilus_progress_info_start (extract_job->common.progress);
+
+        nautilus_progress_info_set_details (extract_job->common.progress,
+                                            _("Scanning archive contents"));
+
+        autoar_extract_start (extract_job->arextract, extract_job->common.cancellable);
+}
+
+void
+nautilus_file_operations_extract (GFile                   *source,
+                                  GFile                   *output,
+                                  GtkWindow               *parent_window,
+                                  NautilusExtractCallback  done_callback,
+                                  gpointer                done_callback_data)
+{
+        GTask *task;
+        ExtractJob *job;
+
+        job = op_job_new (ExtractJob, parent_window);
+        job->source = g_object_ref (source);
+        job->output = g_object_ref (output);
+        job->done_callback = done_callback;
+        job->done_callback = done_callback_data;
+        job->success = FALSE;
+
+        inhibit_power_manager ((CommonJob *)job, _("Extracting Files"));
+
+        task = g_task_new (NULL, job->common.cancellable, extract_task_done, job);
+        g_task_set_task_data (task, job, NULL);
+        g_task_run_in_thread (task, extract_task_thread_func);
+        g_object_unref (task);
+}
+
 #if !defined (NAUTILUS_OMIT_SELF_CHECK)
 
 void
diff --git a/src/nautilus-file-operations.h b/src/nautilus-file-operations.h
index 08b11ca..50ae56c 100644
--- a/src/nautilus-file-operations.h
+++ b/src/nautilus-file-operations.h
@@ -44,6 +44,10 @@ typedef void (* NautilusMountCallback)     (GVolume    *volume,
                                            gboolean    success,
                                            GObject    *callback_data_object);
 typedef void (* NautilusUnmountCallback)   (gpointer    callback_data);
+typedef void (* NautilusExtractCallback)   (GFile    *source,
+                                           GFile    *destination,
+                                           gboolean  success,
+                                           gpointer  callback_data);
 
 /* FIXME: int copy_action should be an enum */
 
@@ -147,6 +151,11 @@ void nautilus_file_mark_desktop_file_trusted (GFile           *file,
                                              gboolean          interactive,
                                              NautilusOpCallback done_callback,
                                              gpointer          done_callback_data);
+void nautilus_file_operations_extract (GFile                   *source,
+                                       GFile                   *output,
+                                       GtkWindow               *parent_window,
+                                       NautilusExtractCallback  done_callback,
+                                       gpointer                        done_callback_data);
 
 
 #endif /* NAUTILUS_FILE_OPERATIONS_H */
diff --git a/src/nautilus-file.c b/src/nautilus-file.c
index a238d69..db26ff8 100644
--- a/src/nautilus-file.c
+++ b/src/nautilus-file.c
@@ -22,6 +22,7 @@
 #include <config.h>
 #include "nautilus-file.h"
 
+#include "nautilus-application.h"
 #include "nautilus-directory-notify.h"
 #include "nautilus-directory-private.h"
 #include "nautilus-signaller.h"
@@ -51,6 +52,7 @@
 #include <glib/gstdio.h>
 #include <gio/gio.h>
 #include <glib.h>
+#include <gnome-autoar/autoar.h>
 #include <gdesktop-enums.h>
 #include <libnautilus-extension/nautilus-file-info.h>
 #include <libnautilus-extension/nautilus-extension-private.h>
@@ -7014,38 +7016,13 @@ real_is_special_link (NautilusFile *file)
 gboolean
 nautilus_file_is_archive (NautilusFile *file)
 {
-       char *mime_type;
-       int i;
-       static const char * archive_mime_types[] = { "application/x-gtar",
-                                                    "application/x-zip",
-                                                    "application/x-zip-compressed",
-                                                    "application/zip",
-                                                    "application/x-zip",
-                                                    "application/x-tar",
-                                                    "application/x-7z-compressed",
-                                                    "application/x-rar",
-                                                    "application/x-rar-compressed",
-                                                    "application/x-jar",
-                                                    "application/x-java-archive",
-                                                    "application/x-war",
-                                                    "application/x-ear",
-                                                    "application/x-arj",
-                                                    "application/x-gzip",
-                                                    "application/x-bzip-compressed-tar",
-                                                    "application/x-compressed-tar" };
-
-       g_return_val_if_fail (file != NULL, FALSE);
-
-       mime_type = nautilus_file_get_mime_type (file);
-       for (i = 0; i < G_N_ELEMENTS (archive_mime_types); i++) {
-               if (!strcmp (mime_type, archive_mime_types[i])) {
-                       g_free (mime_type);
-                       return TRUE;
-               }
-       }
-       g_free (mime_type);
+        g_autofree char *mime_type;
+        AutoarPref *arpref;
 
-       return FALSE;
+        arpref = nautilus_application_get_arpref (nautilus_application_get_default ());
+        mime_type = nautilus_file_get_mime_type (file);
+
+        return autoar_pref_check_mime_type_d (arpref, mime_type);
 }
 
 
diff --git a/src/nautilus-files-view.c b/src/nautilus-files-view.c
index 3c9a8ea..d83f081 100644
--- a/src/nautilus-files-view.c
+++ b/src/nautilus-files-view.c
@@ -5617,6 +5617,94 @@ action_rename (GSimpleAction *action,
         real_action_rename (NAUTILUS_FILES_VIEW (user_data), FALSE);
 }
 
+static void
+nautilus_files_view_extract_selection (NautilusFilesView *view,
+                                       GFile             *destination)
+{
+        GList *selection;
+        GList *l;
+
+        selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
+
+        for (l = selection; l != NULL; l = l->next) {
+                GFile *source;
+
+                source = nautilus_file_get_location (NAUTILUS_FILE (l->data));
+
+                nautilus_file_operations_extract (source,
+                                                  destination,
+                                                  nautilus_files_view_get_containing_window (view),
+                                                  NULL,
+                                                  NULL);
+        }
+
+        nautilus_file_list_free (selection);
+}
+
+static void
+action_extract_here (GSimpleAction *action,
+                     GVariant      *state,
+                     gpointer       user_data)
+{
+        NautilusFilesView *view = user_data;
+
+        nautilus_files_view_extract_selection (view,
+                                               nautilus_view_get_location (NAUTILUS_VIEW (view)));
+}
+
+static void
+on_extract_destination_dialog_response (GtkDialog *dialog,
+                                        gint       response_id,
+                                        gpointer   user_data)
+{
+        NautilusFilesView *view = user_data;
+
+        if (response_id == GTK_RESPONSE_OK) {
+                g_autoptr (GFile) target_file;
+
+                target_file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
+
+                nautilus_files_view_extract_selection (view,
+                                                       target_file);
+        }
+
+        gtk_widget_destroy (GTK_WIDGET (dialog));
+}
+
+static void
+action_extract_to (GSimpleAction *action,
+                   GVariant      *state,
+                   gpointer       user_data)
+{
+        NautilusFilesView *view = user_data;
+        GtkWidget *dialog;
+        g_autofree char *uri;
+
+        dialog = gtk_file_chooser_dialog_new (_("Select Extract Destination"),
+                                              GTK_WINDOW (nautilus_files_view_get_window (view)),
+                                              GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
+                                              _("_Cancel"), GTK_RESPONSE_CANCEL,
+                                              _("_Select"), GTK_RESPONSE_OK,
+                                              NULL);
+        gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (dialog), FALSE);
+
+        gtk_dialog_set_default_response (GTK_DIALOG (dialog),
+                                         GTK_RESPONSE_OK);
+
+        gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE);
+        gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
+
+        uri = nautilus_directory_get_uri (view->details->model);
+        gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dialog), uri);
+
+        g_signal_connect (dialog, "response",
+                          G_CALLBACK (on_extract_destination_dialog_response),
+                          view);
+
+        gtk_widget_show_all (dialog);
+}
+
+
 #define BG_KEY_PRIMARY_COLOR      "primary-color"
 #define BG_KEY_SECONDARY_COLOR    "secondary-color"
 #define BG_KEY_COLOR_TYPE         "color-shading-type"
@@ -6053,6 +6141,8 @@ const GActionEntry view_entries[] = {
         { "restore-from-trash", action_restore_from_trash},
         { "paste-into", action_paste_files_into },
         { "rename", action_rename},
+        { "extract-here", action_extract_here},
+        { "extract-to", action_extract_to},
         { "properties", action_properties},
         { "set-as-wallpaper", action_set_as_wallpaper },
         { "mount-volume", action_mount_volume },
@@ -6290,6 +6380,21 @@ all_in_trash (GList *files)
         return TRUE;
 }
 
+static gboolean
+can_extract_all (GList *files)
+{
+        NautilusFile *file;
+        GList *l;
+
+        for (l = files; l != NULL; l = l->next) {
+                file = l->data;
+                if (!nautilus_file_is_archive (file)) {
+                        return FALSE;
+                }
+        }
+        return TRUE;
+}
+
 GActionGroup *
 nautilus_files_view_get_action_group (NautilusFilesView *view)
 {
@@ -6315,6 +6420,7 @@ real_update_actions_state (NautilusFilesView *view)
         gboolean can_move_files;
         gboolean can_trash_files;
         gboolean can_copy_files;
+        gboolean can_extract_files;
         gboolean can_link_from_copied_files;
         gboolean can_paste_files_into;
         gboolean item_opens_in_view;
@@ -6329,6 +6435,7 @@ real_update_actions_state (NautilusFilesView *view)
         gboolean show_detect_media;
         gboolean settings_show_delete_permanently;
         gboolean settings_show_create_link;
+        gboolean settings_automatic_decompression;
         GDriveStartStopType start_stop_type;
 
         view_action_group = view->details->view_action_group;
@@ -6358,6 +6465,8 @@ real_update_actions_state (NautilusFilesView *view)
                 !selection_contains_desktop_or_home_dir;
         can_copy_files = selection_count != 0
                 && !selection_contains_special_link;
+        can_extract_files = selection_count != 0 &&
+                            can_extract_all (selection);
         can_link_from_copied_files = !nautilus_clipboard_monitor_is_cut (nautilus_clipboard_monitor_get ()) 
&&
                                      !selection_contains_recent && !is_read_only;
         can_move_files = can_delete_files && !selection_contains_recent;
@@ -6368,6 +6477,8 @@ real_update_actions_state (NautilusFilesView *view)
                                                                     
NAUTILUS_PREFERENCES_SHOW_DELETE_PERMANENTLY);
          settings_show_create_link = g_settings_get_boolean (nautilus_preferences,
                                                              NAUTILUS_PREFERENCES_SHOW_CREATE_LINK);
+        settings_automatic_decompression = g_settings_get_boolean (nautilus_preferences,
+                                                                   
NAUTILUS_PREFERENCES_AUTOMATIC_DECOMPRESSION);
 
         /* Right click actions */
         /* Selection menu actions */
@@ -6388,6 +6499,15 @@ real_update_actions_state (NautilusFilesView *view)
         }
 
         action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
+                                             "extract-here");
+        g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
+                                     can_extract_files && !settings_automatic_decompression);
+
+        action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
+                                             "extract-to");
+        g_simple_action_set_enabled (G_SIMPLE_ACTION (action), can_extract_files);
+
+        action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
                                              "open-item-location");
 
         g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
@@ -6644,7 +6764,7 @@ update_selection_menu (NautilusFilesView *view)
         GList *selection, *l;
         NautilusFile *file;
         gint selection_count;
-        gboolean show_app, show_run;
+        gboolean show_extract, show_app, show_run;
         gboolean item_opens_in_view;
         gchar *item_label;
         GAppInfo *app;
@@ -6682,11 +6802,15 @@ update_selection_menu (NautilusFilesView *view)
         g_free (item_label);
 
         /* Open With <App> menu item */
-        show_app = show_run = item_opens_in_view = selection_count != 0;
+        show_extract = show_app = show_run = item_opens_in_view = selection_count != 0;
         for (l = selection; l != NULL; l = l->next) {
                 NautilusFile *file;
 
-                file = NAUTILUS_FILE (selection->data);
+                file = NAUTILUS_FILE (l->data);
+
+                if (!nautilus_mime_file_extracts (file)) {
+                        show_extract = FALSE;
+                }
 
                 if (!nautilus_mime_file_opens_in_external_app (file)) {
                         show_app = FALSE;
@@ -6700,7 +6824,7 @@ update_selection_menu (NautilusFilesView *view)
                         item_opens_in_view = FALSE;
                 }
 
-                if (!show_app && !show_run && !item_opens_in_view) {
+                if (!show_extract && !show_app && !show_run && !item_opens_in_view) {
                         break;
                 }
         }
@@ -6726,6 +6850,8 @@ update_selection_menu (NautilusFilesView *view)
                 g_object_unref (app);
         } else if (show_run) {
                 item_label = g_strdup (_("Run"));
+        } else if (show_extract) {
+                item_label = g_strdup (_("Extract Here"));
         } else {
                 item_label = g_strdup (_("Open"));
         }
diff --git a/src/nautilus-global-preferences.h b/src/nautilus-global-preferences.h
index ef1f8aa..1d5ed4f 100644
--- a/src/nautilus-global-preferences.h
+++ b/src/nautilus-global-preferences.h
@@ -32,6 +32,9 @@ G_BEGIN_DECLS
 /* Trash options */
 #define NAUTILUS_PREFERENCES_CONFIRM_TRASH                     "confirm-trash"
 
+/* Compressed files options */
+#define NAUTILUS_PREFERENCES_AUTOMATIC_DECOMPRESSION            "automatic-decompression"
+
 /* Display  */
 #define NAUTILUS_PREFERENCES_SHOW_HIDDEN_FILES                 "show-hidden"
 
diff --git a/src/nautilus-mime-actions.c b/src/nautilus-mime-actions.c
index e196dce..68d6270 100644
--- a/src/nautilus-mime-actions.c
+++ b/src/nautilus-mime-actions.c
@@ -43,6 +43,7 @@
 #include "nautilus-program-choosing.h"
 #include "nautilus-global-preferences.h"
 #include "nautilus-signaller.h"
+#include "nautilus-application.h"
 
 #define DEBUG_FLAG NAUTILUS_DEBUG_MIME
 #include "nautilus-debug.h"
@@ -54,6 +55,7 @@ typedef enum {
        ACTIVATION_ACTION_LAUNCH_IN_TERMINAL,
        ACTIVATION_ACTION_OPEN_IN_VIEW,
        ACTIVATION_ACTION_OPEN_IN_APPLICATION,
+        ACTIVATION_ACTION_EXTRACT,
        ACTIVATION_ACTION_DO_NOTHING,
 } ActivationAction;
 
@@ -677,6 +679,13 @@ get_activation_action (NautilusFile *file)
 {
        ActivationAction action;
        char *activation_uri;
+        gboolean can_extract;
+        can_extract =  g_settings_get_boolean (nautilus_preferences,
+                                               NAUTILUS_PREFERENCES_AUTOMATIC_DECOMPRESSION);
+
+        if (can_extract && nautilus_file_is_archive (file)) {
+                return ACTIVATION_ACTION_EXTRACT;
+        }
 
        if (nautilus_file_is_nautilus_link (file)) {
                return ACTIVATION_ACTION_LAUNCH_DESKTOP_FILE;
@@ -715,6 +724,12 @@ get_activation_action (NautilusFile *file)
 }
 
 gboolean
+nautilus_mime_file_extracts (NautilusFile *file)
+{
+        return get_activation_action (file) == ACTIVATION_ACTION_EXTRACT;
+}
+
+gboolean
 nautilus_mime_file_launches (NautilusFile *file)
 {
        ActivationAction activation_action;
@@ -1506,6 +1521,7 @@ activate_files (ActivateParameters *parameters)
        GList *open_in_app_uris;
        GList *open_in_app_parameters;
        GList *unhandled_open_in_app_uris;
+        GList *extract_files;
        ApplicationLaunchParameters *one_parameters;
        GList *open_in_view_files;
        GList *l;
@@ -1528,6 +1544,7 @@ activate_files (ActivateParameters *parameters)
        launch_in_terminal_files = NULL;
        open_in_app_uris = NULL;
        open_in_view_files = NULL;
+        extract_files = NULL;
 
        for (l = parameters->locations; l != NULL; l = l->next) {
                location = l->data;
@@ -1560,6 +1577,9 @@ activate_files (ActivateParameters *parameters)
                case ACTIVATION_ACTION_OPEN_IN_VIEW :
                        open_in_view_files = g_list_prepend (open_in_view_files, file);
                        break;
+                case ACTIVATION_ACTION_EXTRACT :
+                        extract_files = g_list_prepend (extract_files, file);
+                        break;
                case ACTIVATION_ACTION_OPEN_IN_APPLICATION :
                        open_in_app_uris = g_list_prepend (open_in_app_uris, location->uri);
                        break;
@@ -1674,6 +1694,24 @@ activate_files (ActivateParameters *parameters)
                }
        }
 
+        extract_files = g_list_reverse (extract_files);
+        if (extract_files) {
+                for (l = extract_files; l != NULL; l = l->next) {
+                        file = NAUTILUS_FILE (l->data);
+                        g_autoptr (GFile) source;
+                        g_autoptr (GFile) output;
+
+                        source = nautilus_file_get_location (file);
+                        output = nautilus_file_get_parent_location (file);
+
+                        nautilus_file_operations_extract (source,
+                                                          output,
+                                                          parameters->parent_window,
+                                                          NULL,
+                                                          NULL);
+                }
+        }
+
        open_in_app_parameters = NULL;
        unhandled_open_in_app_uris = NULL;
 
diff --git a/src/nautilus-mime-actions.h b/src/nautilus-mime-actions.h
index f96ca66..c3f9f87 100644
--- a/src/nautilus-mime-actions.h
+++ b/src/nautilus-mime-actions.h
@@ -38,6 +38,7 @@ GList *                nautilus_mime_get_applications_for_file            (Nauti
 GAppInfo *             nautilus_mime_get_default_application_for_files    (GList                   *files);
 
 gboolean               nautilus_mime_file_opens_in_external_app           (NautilusFile            *file);
+gboolean               nautilus_mime_file_extracts                        (NautilusFile            *file);
 gboolean               nautilus_mime_file_launches                        (NautilusFile            *file);
 void                   nautilus_mime_activate_files                       (GtkWindow               
*parent_window,
                                                                           NautilusWindowSlot      *slot,
diff --git a/src/nautilus-operations-manager.c b/src/nautilus-operations-manager.c
index f0bebe9..763d368 100644
--- a/src/nautilus-operations-manager.c
+++ b/src/nautilus-operations-manager.c
@@ -120,6 +120,42 @@ set_copy_move_dialog_text (FileConflictDialogData *data)
 }
 
 static void
+set_extract_dialog_text (FileConflictDialogData *data)
+{
+
+        g_autofree gchar *primary_text = NULL;
+        g_autofree gchar *secondary_text = NULL;
+        const gchar *message_extra;
+        g_autofree gchar *message = NULL;
+        g_autofree gchar *dest_name;
+        g_autofree gchar *dest_dir_name;
+
+        dest_name = nautilus_file_get_display_name (data->destination);
+        dest_dir_name = nautilus_file_get_display_name (data->dest_dir);
+
+        if (nautilus_file_is_directory (data->destination)) {
+                primary_text = g_strdup_printf (_("Replace folder “%s”?"), dest_name);
+                message = g_strdup_printf
+                        (_("A folder with the same name as the extract destination already exists in “%s”."),
+                         dest_dir_name);
+                message_extra =
+                        _("Replacing it will remove all files in the folder.");
+        } else {
+                primary_text = g_strdup_printf (_("Replace file “%s”?"), dest_name);
+                message = g_strdup_printf
+                        (_("A file with the same name as the extract destination already exists in “%s”."),
+                         dest_dir_name);
+                message_extra = _("Replacing it will overwrite its content.");
+        }
+
+        secondary_text = g_strdup_printf ("%s\n%s", message, message_extra);
+
+        nautilus_file_conflict_dialog_set_text (data->dialog,
+                                                primary_text,
+                                                secondary_text);
+}
+
+static void
 set_images (FileConflictDialogData *data)
 {
         GdkPixbuf *source_pixbuf;
@@ -298,6 +334,36 @@ copy_move_file_list_ready_cb (GList    *files,
 }
 
 static void
+extract_file_list_ready_cb (GList    *files,
+                            gpointer  user_data)
+{
+        FileConflictDialogData *data = user_data;
+
+        data->handle = NULL;
+
+        gtk_window_set_title (GTK_WINDOW (data->dialog), _("Extract conflict"));
+
+        set_extract_dialog_text (data);
+
+        set_images (data);
+
+        set_file_labels (data);
+
+        set_conflict_name (data);
+
+        nautilus_file_conflict_dialog_disable_skip (data->dialog);
+        nautilus_file_conflict_dialog_disable_apply_to_all (data->dialog);
+
+        nautilus_file_monitor_add (data->source, data, NAUTILUS_FILE_ATTRIBUTES_FOR_ICON);
+        nautilus_file_monitor_add (data->destination, data, NAUTILUS_FILE_ATTRIBUTES_FOR_ICON);
+
+        data->src_handler_id = g_signal_connect (data->source, "changed",
+                          G_CALLBACK (file_icons_changed), data);
+        data->dest_handler_id = g_signal_connect (data->destination, "changed",
+                          G_CALLBACK (file_icons_changed), data);
+}
+
+static void
 build_dialog_appearance (FileConflictDialogData *data)
 {
         GList *files = NULL;
@@ -418,3 +484,88 @@ get_copy_move_file_conflict_response (GtkWindow *parent_window,
 
         return get_file_conflict_response (data);
 }
+
+FileConflictResponse *
+get_extract_file_conflict_response (GtkWindow *parent_window,
+                                    GFile     *src_name,
+                                    GFile     *dest_name,
+                                    GFile     *dest_dir_name)
+{
+        FileConflictDialogData *data;
+
+        data = g_slice_new0 (FileConflictDialogData);
+        data->parent = parent_window;
+        data->src_name = src_name;
+        data->dest_name = dest_name;
+        data->dest_dir_name = dest_dir_name;
+
+        data->files_list_ready_cb = extract_file_list_ready_cb;
+
+        return get_file_conflict_response (data);
+}
+
+typedef struct {
+        GFile *source;
+
+        GtkWindow *parent;
+
+        NameConflictResponse *response;
+
+        GtkWidget *dialog;
+        /* Dialogs are ran from operation threads, which need to be blocked until
+         * the user gives a valid response
+         */
+        gboolean completed;
+        GMutex mutex;
+        GCond cond;
+} NameConflictData;
+
+static gboolean
+run_name_conflict_dialog (gpointer _data)
+{
+        NameConflictData *data = _data;
+
+
+
+
+        return FALSE;
+}
+
+
+NameConflictResponse *
+get_extract_name_conflict_response (GtkWindow *parent_window,
+                                    GFile     *source)
+{
+        NameConflictData *data;
+        NameConflictResponse *response;
+
+        data = g_slice_new0 (NameConflictData);
+        data->parent = parent_window;
+        data->source = source;
+
+        response = g_slice_new0 (NameConflictResponse);
+        response->new_name = NULL;
+        data->response = response;
+
+        data->completed = FALSE;
+        g_mutex_init (&data->mutex);
+        g_cond_init (&data->cond);
+
+        g_mutex_lock (&data->mutex);
+
+        g_main_context_invoke (NULL,
+                               run_name_conflict_dialog,
+                               data);
+
+        while (!data->completed) {
+                g_cond_wait (&data->cond, &data->mutex);
+        }
+
+        g_mutex_unlock (&data->mutex);
+        g_mutex_clear (&data->mutex);
+        g_cond_clear (&data->cond);
+
+        g_slice_free (NameConflictData, data);
+
+        return response;
+}
diff --git a/src/nautilus-operations-manager.h b/src/nautilus-operations-manager.h
index 84d15d0..c91bafd 100644
--- a/src/nautilus-operations-manager.h
+++ b/src/nautilus-operations-manager.h
@@ -13,5 +13,17 @@ FileConflictResponse * get_copy_move_file_conflict_response (GtkWindow *parent_w
                                                              GFile     *src,
                                                              GFile     *dest,
                                                              GFile     *dest_dir);
+FileConflictResponse * get_extract_file_conflict_response (GtkWindow *parent_window,
+                                                           GFile     *src,
+                                                           GFile     *dest,
+                                                           GFile     *dest_dir);
+
+typedef struct {
+    int id;
+    char *new_name;
+} NameConflictResponse;
+
+NameConflictResponse * get_extract_name_conflict_response (GtkWindow *parent_window,
+                                                           GFile     *source);
 
 #endif
diff --git a/src/nautilus-preferences-window.c b/src/nautilus-preferences-window.c
index 5fe783b..875b233 100644
--- a/src/nautilus-preferences-window.c
+++ b/src/nautilus-preferences-window.c
@@ -58,6 +58,8 @@
   "use_tree_view_checkbutton"
 #define NAUTILUS_PREFERENCES_DIALOG_TRASH_CONFIRM_WIDGET                       \
   "trash_confirm_checkbutton"
+#define NAUTILUS_PREFERENCES_DIALOG_AUTOMATIC_DECOMPRESSION_WIDGET             \
+  "automatic_decompression_checkbutton"
 
 /* int enums */
 #define NAUTILUS_PREFERENCES_DIALOG_THUMBNAIL_LIMIT_WIDGET                     \
@@ -432,6 +434,9 @@ static void nautilus_preferences_window_setup(GtkBuilder *builder,
   bind_builder_bool(builder, nautilus_preferences,
                     NAUTILUS_PREFERENCES_DIALOG_TRASH_CONFIRM_WIDGET,
                     NAUTILUS_PREFERENCES_CONFIRM_TRASH);
+  bind_builder_bool (builder, nautilus_preferences,
+                     NAUTILUS_PREFERENCES_DIALOG_AUTOMATIC_DECOMPRESSION_WIDGET,
+                     NAUTILUS_PREFERENCES_AUTOMATIC_DECOMPRESSION);
   bind_builder_bool(builder, nautilus_list_view_preferences,
                     NAUTILUS_PREFERENCES_DIALOG_LIST_VIEW_USE_TREE_WIDGET,
                     NAUTILUS_PREFERENCES_LIST_VIEW_USE_TREE);
diff --git a/src/resources/ui/nautilus-files-view-context-menus.ui 
b/src/resources/ui/nautilus-files-view-context-menus.ui
index b40d242..9d4ac30 100644
--- a/src/resources/ui/nautilus-files-view-context-menus.ui
+++ b/src/resources/ui/nautilus-files-view-context-menus.ui
@@ -235,6 +235,16 @@
     </section>
     <section>
       <attribute name="id">extensions</attribute>
+      <item>
+        <attribute name="label" translatable="yes">Extract Here</attribute>
+        <attribute name="action">view.extract-here</attribute>
+        <attribute name="hidden-when">action-disabled</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">Extract to…</attribute>
+        <attribute name="action">view.extract-to</attribute>
+        <attribute name="hidden-when">action-disabled</attribute>
+      </item>
     </section>
     <section>
       <item>
diff --git a/src/resources/ui/nautilus-preferences-window.ui b/src/resources/ui/nautilus-preferences-window.ui
index 17c32b2..90ae4d7 100644
--- a/src/resources/ui/nautilus-preferences-window.ui
+++ b/src/resources/ui/nautilus-preferences-window.ui
@@ -756,6 +756,52 @@ More information will appear when zooming closer.</property>
                 <property name="position">3</property>
               </packing>
             </child>
+            <child>
+              <object class="GtkBox" id="vbox10">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="orientation">vertical</property>
+                <property name="spacing">6</property>
+                <child>
+                  <object class="GtkLabel" id="label17">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">Compressed Files</property>
+                    <property name="xalign">0</property>
+                    <attributes>
+                      <attribute name="weight" value="bold"/>
+                    </attributes>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkCheckButton" id="automatic_decompression_checkbutton">
+                    <property name="label" translatable="yes">E_xtract compressed files instead of opening 
them</property>
+                    <property name="use_action_appearance">False</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="use_underline">True</property>
+                    <property name="xalign">0</property>
+                    <property name="draw_indicator">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">4</property>
+              </packing>
+            </child>
           </object>
           <packing>
             <property name="position">1</property>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]