~jeremywootten/pantheon-files/fix-1086929-remake

779 by am.monkeyd at gmail
undo redo - first tests. let's see if it works as well as it did in nautilus-elementary
1
/* MarlinUndoManager - Manages undo of file operations (implementation)
2
 *
3
 * Copyright (C) 2007-2010 Amos Brocco
4
 *
5
 * Author: Amos Brocco <amos.brocco@unifr.ch>
6
 *
7
 * This library is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU General Public
9
 * License as published by the Free Software Foundation; either
10
 * version 2 of the License, or (at your option) any later version.
11
 *
12
 * This library is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public
18
 * License along with this library; if not, write to the
19
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20
 * Boston, MA 02111-1307, USA.
21
 */
22
23
#include "marlin-undostack-manager.h"
24
#include "marlin-file-operations.h"
25
#include "gof-file.h"
26
#include <gio/gio.h>
27
#include <glib/gprintf.h>
28
#include <glib-object.h>
29
#include <glib/gi18n.h>
30
#include <locale.h>
31
#include <gdk/gdk.h>
32
#include "eel-glib-extensions.h"
33
34
35
struct _MarlinUndoActionData
36
{
37
    /* Common stuff */
38
    MarlinUndoActionType type;
39
    gboolean is_valid;
40
    gboolean locked;              /* True if the action is being undone/redone */
41
    gboolean freed;               /* True if the action must be freed after undo/redo */
42
    guint count;                  /* Size of affected uris (count of items) */
43
    MarlinUndoManager *manager;    /* Pointer to the manager */
44
45
    /* Copy / Move stuff */
46
    GFile *src_dir;
47
    GFile *dest_dir;
48
    GList *sources;               /* Relative to src_dir */
49
    GList *destinations;          /* Relative to dest_dir */
50
51
    /* Cached labels/descriptions */
52
    char *undo_label;
53
    char *undo_description;
54
    char *redo_label;
55
    char *redo_description;
56
57
    /* Create new file/folder stuff/set permissions */
58
    char *template;
59
    char *target_uri;
60
61
    /* Rename stuff */
62
    char *old_uri;
63
    char *new_uri;
64
65
    /* Trash stuff */
66
    GHashTable *trashed;
67
68
    /* Recursive change permissions stuff */
69
    GHashTable *original_permissions;
70
    guint32 dir_mask;
71
    guint32 dir_permissions;
72
    guint32 file_mask;
73
    guint32 file_permissions;
74
75
    /* Single file change permissions stuff */
76
    guint32 current_permissions;
77
    guint32 new_permissions;
78
79
    /* Group */
80
    char *original_group_name_or_id;
81
    char *new_group_name_or_id;
82
83
    /* Owner */
84
    char *original_user_name_or_id;
85
    char *new_user_name_or_id;
86
87
};
88
89
struct _MarlinUndoManagerPrivate
90
{
91
    /* Private fields */
798.2.4 by am.monkeyd at gmail
updating deprecated mutex
92
    GQueue      *stack;
93
    guint       undo_levels;
94
    guint       index;
95
    GMutex      mutex;                /* Used to protect access to stack (because of async file ops) */
96
    gboolean    dispose_has_run;
97
    gboolean    undo_redo_flag;
98
    gboolean    confirm_delete;
779 by am.monkeyd at gmail
undo redo - first tests. let's see if it works as well as it did in nautilus-elementary
99
};
100
101
#define MARLIN_UNDO_MANAGER_GET_PRIVATE(o)  \
102
    (G_TYPE_INSTANCE_GET_PRIVATE ((o), TYPE_MARLIN_UNDO_MANAGER, MarlinUndoManagerPrivate))
103
104
enum {
105
    REQUEST_MENU_UPDATE,
106
    LAST_SIGNAL
107
};
108
109
static guint signals[LAST_SIGNAL];
110
111
/* *****************************************************************
112
   Properties management prototypes
113
***************************************************************** */
114
enum
115
{
116
    PROP_UNDO_MANAGER_0, 
117
    PROP_UNDO_LEVELS, 
118
    PROP_CONFIRM_DELETE
119
};
120
121
static void marlin_undo_manager_set_property (GObject *object,
122
                                              guint prop_id, const GValue * value, GParamSpec * pspec);
123
124
static void marlin_undo_manager_get_property (GObject *object,
125
                                              guint prop_id, GValue * value, GParamSpec * pspec);
126
127
/* *****************************************************************
128
   Destructors prototypes
129
***************************************************************** */
130
static void marlin_undo_manager_finalize (GObject *object);
131
132
static void marlin_undo_manager_dispose (GObject *object);
133
134
/* *****************************************************************
135
   Type definition
136
***************************************************************** */
137
G_DEFINE_TYPE (MarlinUndoManager, marlin_undo_manager, G_TYPE_OBJECT);
138
139
/* *****************************************************************
140
   Private methods prototypes
141
***************************************************************** */
142
143
static void stack_clear_n_oldest (GQueue *stack, guint n);
144
145
static void stack_fix_size (MarlinUndoManagerPrivate *priv);
146
147
static gboolean can_undo (MarlinUndoManagerPrivate *priv);
148
149
static gboolean can_redo (MarlinUndoManagerPrivate *priv);
150
151
static void stack_push_action (MarlinUndoManagerPrivate *priv,
152
                               MarlinUndoActionData *action);
153
154
static MarlinUndoActionData
155
* stack_scroll_left (MarlinUndoManagerPrivate *priv);
156
157
static MarlinUndoActionData
158
* stack_scroll_right (MarlinUndoManagerPrivate *priv);
159
160
static MarlinUndoActionData
161
* get_next_redo_action (MarlinUndoManagerPrivate *priv);
162
163
static MarlinUndoActionData
164
* get_next_undo_action (MarlinUndoManagerPrivate *priv);
165
166
static gchar *get_undo_label (MarlinUndoActionData *action);
167
168
static gchar *get_undo_description (MarlinUndoActionData *action);
169
170
static gchar *get_redo_label (MarlinUndoActionData *action);
171
172
static gchar *get_redo_description (MarlinUndoActionData *action);
173
174
static void do_menu_update (MarlinUndoManager *manager);
175
176
static void free_undo_action (gpointer data, gpointer user_data);
177
178
static void undostack_dispose_all (GQueue *queue);
179
180
static void undo_redo_done_transfer_callback (GHashTable *debuting_uris,
181
                                              gpointer data);
182
183
static void undo_redo_op_callback (gpointer callback_data);
184
185
static void undo_redo_done_rename_callback (GOFFile * file,
186
                                            GFile * result_location, GError * error, gpointer callback_data);
187
188
static void undo_redo_done_delete_callback (GHashTable *debuting_uris,
189
                                            gboolean user_cancel, gpointer callback_data);
190
191
static void undo_redo_done_create_callback (GFile * new_file,
192
                                            gpointer callback_data);
193
194
static void clear_redo_actions (MarlinUndoManagerPrivate *priv);
195
196
static gchar *get_first_target_short_name (MarlinUndoActionData *action);
197
198
static GList *construct_gfile_list (const GList *urilist, GFile *parent);
199
200
static GList *construct_gfile_list_from_uri (char *uri);
201
202
static GList *uri_list_to_gfile_list (GList *urilist);
203
204
static char *get_uri_basename (char *uri);
205
206
static char *get_uri_parent (char *uri);
207
208
static char *get_uri_parent_path (char *uri);
209
210
static GFile *get_file_parent_from_uri (char *uri);
211
212
static GHashTable *retrieve_files_to_restore (GHashTable *trashed);
213
214
/* *****************************************************************
215
   Base functions
216
***************************************************************** */
217
static void
218
marlin_undo_manager_class_init (MarlinUndoManagerClass *klass)
219
{
220
    GParamSpec *undo_levels;
221
    GParamSpec *confirm_delete;
222
    GObjectClass *g_object_class;
223
224
    /* Add private structure */
225
    g_type_class_add_private (klass, sizeof (MarlinUndoManagerPrivate));
226
227
    /* Create properties */
228
    undo_levels = g_param_spec_uint ("undo-levels", "undo levels",
229
                                     "Number of undo levels to be stored",
230
                                     1, UINT_MAX, 30, G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
231
232
    confirm_delete =
233
        g_param_spec_boolean ("confirm-delete", "confirm delete",
234
                              "Always confirm file deletion", FALSE,
235
                              G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
236
237
    /* Set properties get/set methods */
238
    g_object_class = G_OBJECT_CLASS (klass);
239
240
    g_object_class->set_property = marlin_undo_manager_set_property;
241
    g_object_class->get_property = marlin_undo_manager_get_property;
242
243
    /* Install properties */
244
    g_object_class_install_property (g_object_class, PROP_UNDO_LEVELS,
245
                                     undo_levels);
246
247
    g_object_class_install_property (g_object_class, PROP_CONFIRM_DELETE,
248
                                     confirm_delete);
249
250
    /* The UI menu needs to update its status */
251
    signals[REQUEST_MENU_UPDATE] = g_signal_new ("request-menu-update",
252
                                                 G_TYPE_FROM_CLASS (klass),
253
                                                 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 
254
                                                 0, NULL, NULL,
255
                                                 g_cclosure_marshal_VOID__POINTER, 
256
                                                 G_TYPE_NONE, 1, G_TYPE_POINTER);
257
258
    /* Hook deconstructors */
259
    g_object_class->dispose = marlin_undo_manager_dispose;
260
    g_object_class->finalize = marlin_undo_manager_finalize;
261
}
262
263
static void
264
marlin_undo_manager_init (MarlinUndoManager *self)
265
{
266
    MarlinUndoManagerPrivate *priv;
267
268
    priv = MARLIN_UNDO_MANAGER_GET_PRIVATE (self);
269
270
    self->priv = priv;
271
272
    /* Initialize private fields */
273
    priv->stack = g_queue_new ();
798.2.4 by am.monkeyd at gmail
updating deprecated mutex
274
    g_mutex_init (&priv->mutex);
779 by am.monkeyd at gmail
undo redo - first tests. let's see if it works as well as it did in nautilus-elementary
275
    priv->index = 0;
276
    priv->dispose_has_run = FALSE;
277
    priv->undo_redo_flag = FALSE;
278
    priv->confirm_delete = FALSE;
279
}
280
281
static void
282
marlin_undo_manager_dispose (GObject *object)
283
{
284
    MarlinUndoManager *self = MARLIN_UNDO_MANAGER (object);
285
    MarlinUndoManagerPrivate *priv = self->priv;
286
287
    if (priv->dispose_has_run)
288
        return;
289
798.2.4 by am.monkeyd at gmail
updating deprecated mutex
290
    g_mutex_lock (&priv->mutex);
779 by am.monkeyd at gmail
undo redo - first tests. let's see if it works as well as it did in nautilus-elementary
291
292
    /* Free each undoable action in the stack and the stack itself */
293
    undostack_dispose_all (priv->stack);
294
    g_queue_free (priv->stack);
798.2.4 by am.monkeyd at gmail
updating deprecated mutex
295
    g_mutex_unlock (&priv->mutex);
779 by am.monkeyd at gmail
undo redo - first tests. let's see if it works as well as it did in nautilus-elementary
296
798.2.4 by am.monkeyd at gmail
updating deprecated mutex
297
    g_mutex_clear (&priv->mutex);
779 by am.monkeyd at gmail
undo redo - first tests. let's see if it works as well as it did in nautilus-elementary
298
299
    priv->dispose_has_run = TRUE;
300
301
    G_OBJECT_CLASS (marlin_undo_manager_parent_class)->dispose (object);
302
}
303
304
static void
305
marlin_undo_manager_finalize (GObject * object)
306
{
307
    G_OBJECT_CLASS (marlin_undo_manager_parent_class)->finalize (object);
308
}
309
310
/* *****************************************************************
311
   Property management
312
***************************************************************** */
313
static void
314
marlin_undo_manager_set_property (GObject *object, guint prop_id,
315
                                  const GValue *value, GParamSpec *pspec)
316
{
317
    g_return_if_fail (IS_MARLIN_UNDO_MANAGER (object));
318
319
    MarlinUndoManager *manager = MARLIN_UNDO_MANAGER (object);
320
    MarlinUndoManagerPrivate *priv = manager->priv;
321
    guint new_undo_levels;
322
323
    switch (prop_id) {
324
    case PROP_UNDO_LEVELS:
325
        new_undo_levels = g_value_get_uint (value);
326
        if (new_undo_levels > 0 && (priv->undo_levels != new_undo_levels)) {
327
            priv->undo_levels = new_undo_levels;
798.2.4 by am.monkeyd at gmail
updating deprecated mutex
328
            g_mutex_lock (&priv->mutex);
779 by am.monkeyd at gmail
undo redo - first tests. let's see if it works as well as it did in nautilus-elementary
329
            stack_fix_size (priv);
798.2.4 by am.monkeyd at gmail
updating deprecated mutex
330
            g_mutex_unlock (&priv->mutex);
779 by am.monkeyd at gmail
undo redo - first tests. let's see if it works as well as it did in nautilus-elementary
331
            do_menu_update (manager);
332
        }
333
        break;
334
    case PROP_CONFIRM_DELETE:
335
        priv->confirm_delete = g_value_get_boolean (value);
336
        break;
337
    default:
338
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
339
        break;
340
    }
341
}
342
343
static void
344
marlin_undo_manager_get_property (GObject *object, guint prop_id,
345
                                  GValue *value, GParamSpec *pspec)
346
{
347
    g_return_if_fail (IS_MARLIN_UNDO_MANAGER (object));
348
349
    MarlinUndoManager *manager = MARLIN_UNDO_MANAGER (object);
350
    MarlinUndoManagerPrivate *priv = manager->priv;
351
352
    switch (prop_id) {
353
    case PROP_UNDO_LEVELS:
354
        g_value_set_uint (value, priv->undo_levels);
355
        break;
356
357
    default:
358
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
359
        break;
360
    }
361
}
362
363
/* *****************************************************************
364
   Public methods
365
***************************************************************** */
366
367
/** ****************************************************************
368
 * Returns the undo stack manager instance (singleton pattern)
369
** ****************************************************************/
370
MarlinUndoManager *
371
marlin_undo_manager_instance (void)
372
{
373
    static MarlinUndoManager *manager = NULL;
374
375
    if (manager == NULL)
376
        manager = g_object_new (TYPE_MARLIN_UNDO_MANAGER, "undo-levels", 10, NULL);
377
378
    return manager;
379
}
380
381
/** ****************************************************************
382
 * True if undoing / redoing
383
** ****************************************************************/
384
gboolean
385
marlin_undo_manager_is_undo_redo (MarlinUndoManager *manager)
386
{
387
    if (manager->priv->undo_redo_flag)
388
        return TRUE;
389
390
    return FALSE;
391
}
392
393
/*void
394
  marlin_undo_manager_request_menu_update (MarlinUndoManager *manager)
395
  {
396
  do_menu_update (manager);
397
  }*/
398
399
/** ****************************************************************
400
 * Redoes the last file operation
401
** ****************************************************************/
402
void
403
marlin_undo_manager_redo (MarlinUndoManager *manager,
404
                          GtkWidget *parent_view, 
405
                          MarlinUndoFinishCallback cb)
406
{
407
    GList *uris;
408
    GOFFile *file;
409
    char *new_name;
410
    char *puri;
411
    GFile *fparent;
412
    MarlinUndoManagerPrivate *priv = manager->priv;
413
798.2.4 by am.monkeyd at gmail
updating deprecated mutex
414
    g_mutex_lock (&priv->mutex);
779 by am.monkeyd at gmail
undo redo - first tests. let's see if it works as well as it did in nautilus-elementary
415
416
    MarlinUndoActionData *action = stack_scroll_left (priv);
417
418
    /* Action will be NULL if redo is not possible */
419
    if (action != NULL) {
420
        action->locked = TRUE;
421
    }
422
798.2.4 by am.monkeyd at gmail
updating deprecated mutex
423
    g_mutex_unlock (&priv->mutex);
779 by am.monkeyd at gmail
undo redo - first tests. let's see if it works as well as it did in nautilus-elementary
424
425
    do_menu_update (manager);
426
427
    if (action != NULL) {
428
        action->locked = TRUE;      /* Remember to unlock when redo is finished */
429
        priv->undo_redo_flag = TRUE;
430
        switch (action->type) {
431
        case MARLIN_UNDO_COPY:
432
            uris = construct_gfile_list (action->sources, action->src_dir);
433
            marlin_file_operations_copy (uris, NULL,
434
                                         action->dest_dir, NULL, undo_redo_done_transfer_callback, action);
435
            g_list_free_full (uris, g_object_unref);
436
            break;
437
#if 0
438
        case MARLIN_UNDO_CREATEFILEFROMTEMPLATE:
439
            puri = get_uri_parent (action->target_uri);
440
            new_name = get_uri_basename (action->target_uri);
441
            marlin_file_operations_new_file_from_template (NULL,
442
                                                           NULL,
443
                                                           puri,
444
                                                           new_name, action->template, undo_redo_done_create_callback, action);
445
            g_free (puri);
446
            g_free (new_name);
447
            break;
448
#endif
449
        case MARLIN_UNDO_DUPLICATE:
450
            uris = construct_gfile_list (action->sources, action->src_dir);
451
            marlin_file_operations_duplicate (uris, NULL, NULL,
452
                                              undo_redo_done_transfer_callback, action);
453
            g_list_free_full (uris, g_object_unref);
454
            break;
455
        case MARLIN_UNDO_RESTOREFROMTRASH:
456
        case MARLIN_UNDO_MOVE:
457
            uris = construct_gfile_list (action->sources, action->src_dir);
458
            marlin_file_operations_move (uris, NULL,
459
                                         action->dest_dir, NULL, undo_redo_done_transfer_callback, action);
460
            g_list_free_full (uris, g_object_unref);
461
            break;
462
#if 0
463
        case MARLIN_UNDO_RENAME:
464
            new_name = get_uri_basename (action->new_uri);
465
            file = gof_file_get_by_uri (action->old_uri);
466
            marlin_file_rename (file, new_name,
467
                                undo_redo_done_rename_callback, action);
468
            g_object_unref (file);
469
            g_free (new_name);
470
            break;
798.3.1 by am.monkeyd at gmail
new empty file & templates - draft
471
#endif
779 by am.monkeyd at gmail
undo redo - first tests. let's see if it works as well as it did in nautilus-elementary
472
        case MARLIN_UNDO_CREATEEMPTYFILE:
473
            puri = get_uri_parent (action->target_uri);
474
            new_name = get_uri_basename (action->target_uri);
475
            marlin_file_operations_new_file (NULL, NULL, puri,
476
                                             new_name,
477
                                             action->template,
478
                                             0, undo_redo_done_create_callback, action);
479
            g_free (puri);
480
            g_free (new_name);
481
            break;
482
        case MARLIN_UNDO_CREATEFOLDER:
483
            fparent = get_file_parent_from_uri (action->target_uri);
484
            marlin_file_operations_new_folder (NULL, NULL, fparent,
485
                                               undo_redo_done_create_callback, action);
486
            g_object_unref (fparent);
487
            break;
488
        case MARLIN_UNDO_MOVETOTRASH:
489
            //amtest
490
            //printf ("MARLIN_UNDO_MOVETRASH\n");
491
            if (g_hash_table_size (action->trashed) > 0) {
492
                GList *uri_to_trash = g_hash_table_get_keys (action->trashed);
493
                uris = uri_list_to_gfile_list (uri_to_trash);
494
                priv->undo_redo_flag = TRUE;
495
                marlin_file_operations_trash_or_delete
496
                    (uris, NULL, undo_redo_done_delete_callback, action);
497
                g_list_free (uri_to_trash);
498
                g_list_free_full (uris, g_object_unref);
499
            }
500
            break;
501
        case MARLIN_UNDO_CREATELINK:
502
            uris = construct_gfile_list (action->sources, action->src_dir);
503
            marlin_file_operations_link (uris, NULL,
504
                                         action->dest_dir, NULL, undo_redo_done_transfer_callback, action);
505
            g_list_free_full (uris, g_object_unref);
506
            break;
507
#if 0
508
        case MARLIN_UNDO_SETPERMISSIONS:
509
            file = gof_file_get_by_uri (action->target_uri);
510
            marlin_file_set_permissions (file,
511
                                         action->new_permissions, undo_redo_done_rename_callback, action);
512
            g_object_unref (file);
513
            break;
514
        case MARLIN_UNDO_RECURSIVESETPERMISSIONS:
515
            puri = g_file_get_uri (action->dest_dir);
516
            marlin_file_set_permissions_recursive (puri,
517
                                                   action->file_permissions,
518
                                                   action->file_mask,
519
                                                   action->dir_permissions,
520
                                                   action->dir_mask, undo_redo_op_callback, action);
521
            g_free (puri);
522
            break;
523
        case MARLIN_UNDO_CHANGEGROUP:
524
            file = gof_file_get_by_uri (action->target_uri);
525
            marlin_file_set_group (file,
526
                                   action->new_group_name_or_id,
527
                                   undo_redo_done_rename_callback, action);
528
            g_object_unref (file);
529
            break;
530
        case MARLIN_UNDO_CHANGEOWNER:
531
            file = gof_file_get_by_uri (action->target_uri);
532
            marlin_file_set_owner (file,
533
                                   action->new_user_name_or_id,
534
                                   undo_redo_done_rename_callback, action);
535
            g_object_unref (file);
536
            break;
537
#endif
538
539
        case MARLIN_UNDO_DELETE:
540
        default:
541
            priv->undo_redo_flag = FALSE;
542
            break;                  /* We shouldn't be here */
543
        }
544
    }
545
546
    if (cb != NULL)
547
        (*cb) ((gpointer) parent_view);
548
}
549
550
/** ****************************************************************
551
 * Undoes the last file operation
552
** ****************************************************************/
553
void
554
marlin_undo_manager_undo (MarlinUndoManager *manager,
555
                          GtkWidget *parent_view, 
556
                          MarlinUndoFinishCallback cb)
557
{
558
    GList *uris = NULL;
559
    GHashTable *files_to_restore;
560
    GOFFile *file;
561
    char *new_name;
562
    MarlinUndoManagerPrivate *priv = manager->priv;
563
798.2.4 by am.monkeyd at gmail
updating deprecated mutex
564
    g_mutex_lock (&priv->mutex);
779 by am.monkeyd at gmail
undo redo - first tests. let's see if it works as well as it did in nautilus-elementary
565
566
    MarlinUndoActionData *action = stack_scroll_right (priv);
567
568
    if (action != NULL) {
569
        action->locked = TRUE;
570
    }
571
798.2.4 by am.monkeyd at gmail
updating deprecated mutex
572
    g_mutex_unlock (&priv->mutex);
779 by am.monkeyd at gmail
undo redo - first tests. let's see if it works as well as it did in nautilus-elementary
573
574
    do_menu_update (manager);
575
576
    if (action != NULL) {
577
        priv->undo_redo_flag = TRUE;
578
        switch (action->type) {
798.3.1 by am.monkeyd at gmail
new empty file & templates - draft
579
        case MARLIN_UNDO_CREATEEMPTYFILE:
580
        /*case MARLIN_UNDO_CREATEFILEFROMTEMPLATE:*/
779 by am.monkeyd at gmail
undo redo - first tests. let's see if it works as well as it did in nautilus-elementary
581
        case MARLIN_UNDO_CREATEFOLDER:
582
            uris = construct_gfile_list_from_uri (action->target_uri);
583
        case MARLIN_UNDO_COPY:
584
        case MARLIN_UNDO_DUPLICATE:
585
        case MARLIN_UNDO_CREATELINK:
586
            if (!uris) {
587
                uris = construct_gfile_list (action->destinations, action->dest_dir);
588
                uris = g_list_reverse (uris); // Deleting must be done in reverse
589
            }
590
            if (priv->confirm_delete) {
591
                marlin_file_operations_delete (uris, NULL,
592
                                               undo_redo_done_delete_callback, action);
593
                g_list_free_full (uris, g_object_unref);
594
            } else {
595
                /* We skip the confirmation message
596
                */
597
                GList *f;
598
                for (f = uris; f != NULL; f = f->next) {
599
                    char *name;
600
                    name = g_file_get_uri (f->data);
601
                    g_free (name);
602
                    g_file_delete (f->data, NULL, NULL);
603
                    g_object_unref (f->data);
604
                }
605
                g_list_free (uris);
606
607
                /* Here we must do what's necessary for the callback */
608
                undo_redo_done_transfer_callback (NULL, action);
609
            }
610
            break;
611
        case MARLIN_UNDO_RESTOREFROMTRASH:
612
            uris = construct_gfile_list (action->destinations, action->dest_dir);
613
            marlin_file_operations_trash_or_delete (uris, NULL,
614
                                                    undo_redo_done_delete_callback, action);
615
            g_list_free_full (uris, g_object_unref);
616
            break;
617
        case MARLIN_UNDO_MOVETOTRASH:
618
            files_to_restore = retrieve_files_to_restore (action->trashed);
619
            if (g_hash_table_size (files_to_restore) > 0) {
620
                GList *l;
621
                GList *gfiles_in_trash = g_hash_table_get_keys (files_to_restore);
622
                GFile *item;
623
                GFile *dest;
624
                char *value;
625
626
                for (l = gfiles_in_trash; l != NULL; l = l->next) {
627
                    item = l->data;
628
                    value = g_hash_table_lookup (files_to_restore, item);
629
                    dest = g_file_new_for_uri (value);
630
                    g_file_move (item, dest,
631
                                 G_FILE_COPY_NOFOLLOW_SYMLINKS, NULL, NULL, NULL, NULL);
632
                    g_object_unref (dest);
633
                }
634
635
                g_list_free (gfiles_in_trash);
636
            }
637
            g_hash_table_destroy (files_to_restore);
638
639
            /* Here we must do what's necessary for the callback */
640
            undo_redo_done_transfer_callback (NULL, action);
641
            break;
642
        case MARLIN_UNDO_MOVE:
643
            uris = construct_gfile_list (action->destinations, action->dest_dir);
644
            marlin_file_operations_move (uris, NULL,
645
                                         action->src_dir, NULL, undo_redo_done_transfer_callback, action);
646
            g_list_free_full (uris, g_object_unref);
647
            break;
648
#if 0
649
        case MARLIN_UNDO_RENAME:
650
            new_name = get_uri_basename (action->old_uri);
651
            file = gof_file_get_by_uri (action->new_uri);
652
            marlin_file_rename (file, new_name,
653
                                undo_redo_done_rename_callback, action);
654
            g_object_unref (file);
655
            g_free (new_name);
656
            break;
657
        case MARLIN_UNDO_SETPERMISSIONS:
658
            file = gof_file_get_by_uri (action->target_uri);
659
            marlin_file_set_permissions (file,
660
                                         action->current_permissions,
661
                                         undo_redo_done_rename_callback, action);
662
            g_object_unref (file);
663
            break;
664
        case MARLIN_UNDO_RECURSIVESETPERMISSIONS:
665
            if (g_hash_table_size (action->original_permissions) > 0) {
666
                GList *gfiles_list =
667
                    g_hash_table_get_keys (action->original_permissions);
668
                guint32 *perm;
669
                GList *l;
670
                GFile *dest;
671
                char *item;
672
673
                for (l = gfiles_list; l != NULL; l = l->next) {
674
                    item = l->data;
675
                    perm = g_hash_table_lookup (action->original_permissions, item);
676
                    dest = g_file_new_for_uri (item);
677
                    g_file_set_attribute_uint32 (dest,
678
                                                 G_FILE_ATTRIBUTE_UNIX_MODE,
679
                                                 *perm, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, NULL);
680
                    g_object_unref (dest);
681
                }
682
683
                g_list_free (gfiles_list);
684
                /* Here we must do what's necessary for the callback */
685
                undo_redo_done_transfer_callback (NULL, action);
686
            }
687
            break;
688
        case MARLIN_UNDO_CHANGEGROUP:
689
            file = gof_file_get_by_uri (action->target_uri);
690
            marlin_file_set_group (file,
691
                                   action->original_group_name_or_id,
692
                                   undo_redo_done_rename_callback, action);
693
            g_object_unref (file);
694
            break;
695
        case MARLIN_UNDO_CHANGEOWNER:
696
            file = gof_file_get_by_uri (action->target_uri);
697
            marlin_file_set_owner (file,
698
                                   action->original_user_name_or_id,
699
                                   undo_redo_done_rename_callback, action);
700
            g_object_unref (file);
701
            break;
702
#endif
703
        case MARLIN_UNDO_DELETE:
704
        default:
705
            priv->undo_redo_flag = FALSE;
706
            break;                  /* We shouldn't be here */
707
        }
708
    }
709
710
    if (cb != NULL)
711
        (*cb) ((gpointer) parent_view);
712
}
713
714
/** ****************************************************************
715
 * Adds an operation to the stack
716
** ****************************************************************/
717
void
718
marlin_undo_manager_add_action (MarlinUndoManager *manager,
719
                                MarlinUndoActionData *action)
720
{
721
    MarlinUndoManagerPrivate *priv = manager->priv;
722
723
    if (!action)
724
        return;
725
726
    if (!(action && action->is_valid)) {
727
        free_undo_action ((gpointer) action, NULL);
728
        return;
729
    }
730
731
    action->manager = manager;
732
798.2.4 by am.monkeyd at gmail
updating deprecated mutex
733
    g_mutex_lock (&priv->mutex);
779 by am.monkeyd at gmail
undo redo - first tests. let's see if it works as well as it did in nautilus-elementary
734
    stack_push_action (priv, action);
798.2.4 by am.monkeyd at gmail
updating deprecated mutex
735
    g_mutex_unlock (&priv->mutex);
779 by am.monkeyd at gmail
undo redo - first tests. let's see if it works as well as it did in nautilus-elementary
736
737
    do_menu_update (manager);
738
739
}
740
741
static GList *
742
get_all_trashed_items (GQueue *stack)
743
{
744
    MarlinUndoActionData *action = NULL;
745
    GList *trash = NULL;
746
    GList *l;
747
    GQueue *tmp_stack = g_queue_copy(stack);
748
749
    while ((action = (MarlinUndoActionData *) g_queue_pop_tail (tmp_stack)) != NULL)
750
        if (action->trashed)
751
            for (l = g_hash_table_get_keys (action->trashed); l != NULL; l=l->next) { 
752
                trash = g_list_append(trash, l->data);
753
            }
754
755
    g_queue_free (tmp_stack);
756
    return (trash);
757
}
758
759
static gboolean 
760
is_destination_uri_action_partof_trashed(GList *trash, GList *g)
761
{
762
    GList *l;
763
    char *uri;
764
765
    for (l = trash; l != NULL; l=l->next) { 
766
        for (; g != NULL; g=g->next) { 
767
            //printf ("destinations: %s\n", g_file_get_uri(l->data));
768
            uri = g_file_get_uri(g->data);
769
            if (!strcmp (uri, l->data)) {
770
                //printf ("GG %s\nZZ %s\n", uri, l->data);
771
                g_free (uri);
772
                return TRUE;
773
            }
774
            g_free (uri);
775
        }
776
    }
777
778
    return FALSE;
779
}
780
/** ****************************************************************
781
 * Callback after emptying the trash
782
** ****************************************************************/
783
void
784
marlin_undo_manager_trash_has_emptied (MarlinUndoManager *manager)
785
{
786
    //amtest
787
    //printf("%s\n", G_STRFUNC);
788
    MarlinUndoManagerPrivate *priv = manager->priv;
789
790
    /* Clear actions from the oldest to the newest move to trash */
798.2.4 by am.monkeyd at gmail
updating deprecated mutex
791
    g_mutex_lock (&priv->mutex);
779 by am.monkeyd at gmail
undo redo - first tests. let's see if it works as well as it did in nautilus-elementary
792
    clear_redo_actions (priv);
793
    MarlinUndoActionData *action = NULL;
794
795
    GList *g;
796
    GQueue *tmp_stack = g_queue_copy(priv->stack);
797
    GList *trash = get_all_trashed_items (tmp_stack);
798
    while ((action = (MarlinUndoActionData *) g_queue_pop_tail (tmp_stack)) != NULL)
799
    {
800
        if (action->destinations && action->dest_dir) {
801
            /* what a pain rebuild again and again an uri
802
            ** TODO change the struct add uri elements */
803
            g = construct_gfile_list (action->destinations, action->dest_dir);
804
            /* remove action for trashed item uris == destination action */
805
            if (is_destination_uri_action_partof_trashed(trash, g)) {
806
                g_queue_remove (priv->stack, action);
807
                continue;
808
            }
809
        }
810
        if (action->type == MARLIN_UNDO_MOVETOTRASH) {
811
            //printf ("detected MARLIN_UNDO_MOVETOTRASH\n");
812
            g_queue_remove (priv->stack, action);
813
        }
814
    }
815
816
    g_queue_free (tmp_stack);
798.2.4 by am.monkeyd at gmail
updating deprecated mutex
817
    g_mutex_unlock (&priv->mutex);
779 by am.monkeyd at gmail
undo redo - first tests. let's see if it works as well as it did in nautilus-elementary
818
    do_menu_update (manager);
819
}
820
821
/** ****************************************************************
822
 * Returns the modification time for the given file (used for undo trash)
823
** ****************************************************************/
824
//amtest
825
/* TODO use GOFFile we shouldn't have to query_info we already know all of this */
826
guint64
827
marlin_undo_manager_get_file_modification_time (GFile *file)
828
{
829
    GFileInfo *info;
830
    guint64 mtime;
831
832
    info = g_file_query_info (file, G_FILE_ATTRIBUTE_TIME_MODIFIED,
833
                              G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, FALSE, NULL);
834
    if (info == NULL) {
835
        return -1;
836
    }
837
838
    mtime = g_file_info_get_attribute_uint64 (info,
839
                                              G_FILE_ATTRIBUTE_TIME_MODIFIED);
840
841
    g_object_unref (info);
842
843
    return mtime;
844
}
845
846
/** ****************************************************************
847
 * Returns a new undo data container
848
** ****************************************************************/
849
MarlinUndoActionData *
850
marlin_undo_manager_data_new (MarlinUndoActionType type, gint items_count)
851
{
852
    //amtest
853
    //printf("%s\n", G_STRFUNC);
854
    MarlinUndoActionData *data = g_slice_new0 (MarlinUndoActionData);
855
    data->type = type;
856
    data->count = items_count;
857
858
    if (type == MARLIN_UNDO_MOVETOTRASH) {
859
        data->trashed = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
860
    } 
861
    //undotest
862
    /*else if (type == MARLIN_UNDO_RECURSIVESETPERMISSIONS) {
863
      data->original_permissions = 
864
      g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
865
      }*/
866
867
    return data;
868
}
869
870
/** ****************************************************************
871
 * Sets the source directory
872
** ****************************************************************/
873
void
874
marlin_undo_manager_data_set_src_dir (MarlinUndoActionData *data, GFile *src)
875
{
876
    if (!data)
877
        return;
878
879
    data->src_dir = src;
880
}
881
882
/** ****************************************************************
883
 * Sets the destination directory
884
** ****************************************************************/
885
void
886
marlin_undo_manager_data_set_dest_dir (MarlinUndoActionData *data, GFile *dest)
887
{
888
    if (!data)
889
        return;
890
891
    data->dest_dir = dest;
892
}
893
894
/** ****************************************************************
895
 * Pushes an origin, target pair in an existing undo data container
896
** ****************************************************************/
897
void 
898
marlin_undo_manager_data_add_origin_target_pair (MarlinUndoActionData *data, 
899
                                                 GFile *origin, 
900
                                                 GFile *target)
901
{
902
903
    if (!data)
904
        return;
905
906
    char *src_relative = g_file_get_relative_path (data->src_dir, origin);
907
    data->sources = g_list_append (data->sources, src_relative);
908
    char *dest_relative = g_file_get_relative_path (data->dest_dir, target);
909
    data->destinations = g_list_append (data->destinations, dest_relative);
910
911
    data->is_valid = TRUE;
912
}
913
914
/** ****************************************************************
915
 * Pushes an trashed file with modification time in an existing undo data container
916
** ****************************************************************/
917
void
918
marlin_undo_manager_data_add_trashed_file (MarlinUndoActionData *data, 
919
                                           GFile *file, 
920
                                           guint64 mtime)
921
{
922
    if (!data) 
923
        return;
924
925
    guint64 *modification_time = g_new (guint64, 1);
926
    *modification_time = mtime;
927
928
    char *original_uri = g_file_get_uri (file);
929
    //amtest
930
    //printf ("[trash] orig uri %s\n", originalURI);
931
932
    g_hash_table_insert (data->trashed, original_uri, modification_time);
933
934
    data->is_valid = TRUE;
935
}
936
937
/** ****************************************************************
938
 * Pushes a recursive permission change data in an existing undo data container
939
** ****************************************************************/
940
void 
941
marlin_undo_manager_data_add_file_permissions (MarlinUndoActionData *data, 
942
                                               GFile *file, 
943
                                               guint32 permission)
944
{
945
    if (!data)
946
        return;
947
948
    guint32 *currentPermission = g_new (guint32, 1);
949
    *currentPermission = permission;
950
951
    char *originalURI = g_file_get_uri (file);
952
953
    g_hash_table_insert (data->original_permissions, originalURI,
954
                         currentPermission);
955
956
    data->is_valid = TRUE;
957
}
958
959
/** ****************************************************************
960
 * Sets the original file permission in an existing undo data container
961
** ****************************************************************/
962
void 
963
marlin_undo_manager_data_set_file_permissions (MarlinUndoActionData *data, 
964
                                               char *uri,
965
                                               guint32 current_permissions, 
966
                                               guint32 new_permissions)
967
{
968
    if (!data)
969
        return;
970
971
    data->target_uri = uri;
972
973
    data->current_permissions = current_permissions;
974
    data->new_permissions = new_permissions;
975
976
    data->is_valid = TRUE;
977
}
978
979
/** ****************************************************************
980
 * Sets the change owner information in an existing undo data container
981
** ****************************************************************/
982
void 
983
marlin_undo_manager_data_set_owner_change_information (MarlinUndoActionData *data, 
984
                                                       char *uri,
985
                                                       const char *current_user,
986
                                                       const char *new_user)
987
{
988
    if (!data)
989
        return;
990
991
    data->target_uri = uri;
992
993
    data->original_user_name_or_id = g_strdup (current_user);
994
    data->new_user_name_or_id = g_strdup (new_user);
995
996
    data->is_valid = TRUE;
997
}
998
999
/** ****************************************************************
1000
 * Sets the change group information in an existing undo data container
1001
** ****************************************************************/
1002
void 
1003
marlin_undo_manager_data_set_group_change_information (MarlinUndoActionData *data, 
1004
                                                       char *uri,
1005
                                                       const char *current_group,
1006
                                                       const char *new_group)
1007
{
1008
    if (!data)
1009
        return;
1010
1011
    data->target_uri = uri;
1012
1013
    data->original_group_name_or_id = g_strdup (current_group);
1014
    data->new_group_name_or_id = g_strdup (new_group);
1015
1016
    data->is_valid = TRUE;
1017
}
1018
1019
/** ****************************************************************
1020
 * Sets the permission change mask
1021
** ****************************************************************/
1022
void 
1023
marlin_undo_manager_data_set_recursive_permissions (MarlinUndoActionData *data, 
1024
                                                    guint32 file_permissions,
1025
                                                    guint32 file_mask,
1026
                                                    guint32 dir_permissions,
1027
                                                    guint32 dir_mask)
1028
{
1029
    if (!data)
1030
        return;
1031
1032
    data->file_permissions = file_permissions;
1033
    data->file_mask = file_mask;
1034
    data->dir_permissions = dir_permissions;
1035
    data->dir_mask = dir_mask;
1036
1037
    data->is_valid = TRUE;
1038
}
1039
1040
/** ****************************************************************
1041
 * Sets create file information
1042
** ****************************************************************/
1043
void
1044
marlin_undo_manager_data_set_create_data (MarlinUndoActionData *data, 
1045
                                          char *target_uri, 
1046
                                          char *template)
1047
{
1048
    if (!data)
1049
        return;
1050
1051
    data->template = g_strdup (template);
1052
    data->target_uri = g_strdup (target_uri);
1053
1054
    data->is_valid = TRUE;
1055
}
1056
1057
/** ****************************************************************
1058
 * Sets rename information
1059
** ****************************************************************/
1060
void 
1061
marlin_undo_manager_data_set_rename_information (MarlinUndoActionData *data, 
1062
                                                 GFile *old_file, 
1063
                                                 GFile *new_file)
1064
{
1065
    if (!data)
1066
        return;
1067
1068
    data->old_uri = g_file_get_uri (old_file);
1069
    data->new_uri = g_file_get_uri (new_file);
1070
1071
    data->is_valid = TRUE;
1072
}
1073
1074
/* *****************************************************************
1075
   Private methods (nothing to see here, move along)
1076
***************************************************************** */
1077
1078
static MarlinUndoActionData *
1079
stack_scroll_right (MarlinUndoManagerPrivate *priv)
1080
{
1081
    gpointer data = NULL;
1082
1083
    if (!can_undo (priv))
1084
        return NULL;
1085
1086
    data = g_queue_peek_nth (priv->stack, priv->index);
1087
    if (priv->index < g_queue_get_length (priv->stack)) {
1088
        priv->index++;
1089
    }
1090
1091
    return data;
1092
}
1093
1094
/** ---------------------------------------------------------------- */
1095
static MarlinUndoActionData *
1096
stack_scroll_left (MarlinUndoManagerPrivate *priv)
1097
{
1098
    gpointer data = NULL;
1099
1100
    if (!can_redo (priv))
1101
        return NULL;
1102
1103
    priv->index--;
1104
    data = g_queue_peek_nth (priv->stack, priv->index);
1105
1106
    return data;
1107
}
1108
1109
/** ---------------------------------------------------------------- */
1110
static void
1111
stack_clear_n_oldest (GQueue *stack, guint n)
1112
{
1113
    MarlinUndoActionData *action;
1114
    guint i;
1115
1116
    for (i = 0; i < n; i++) {
1117
        if ((action = (MarlinUndoActionData *) g_queue_pop_tail (stack)) == NULL)
1118
            break;
1119
        if (action->locked) {
1120
            action->freed = TRUE;
1121
        } else {
1122
            free_undo_action (action, NULL);
1123
        }
1124
    }
1125
}
1126
1127
/** ---------------------------------------------------------------- */
1128
static void
1129
stack_fix_size (MarlinUndoManagerPrivate *priv)
1130
{
1131
    guint length = g_queue_get_length (priv->stack);
1132
1133
    if (length > priv->undo_levels) {
1134
        if (priv->index > (priv->undo_levels + 1)) {
1135
            /* If the index will fall off the stack
1136
             * move it back to the maximum position */
1137
            priv->index = priv->undo_levels + 1;
1138
        }
1139
        stack_clear_n_oldest (priv->stack, length - (priv->undo_levels));
1140
    }
1141
}
1142
1143
/** ---------------------------------------------------------------- */
1144
static void
1145
clear_redo_actions (MarlinUndoManagerPrivate *priv)
1146
{
1147
    while (priv->index > 0) {
1148
        MarlinUndoActionData *head = (MarlinUndoActionData *)
1149
            g_queue_pop_head (priv->stack);
1150
        free_undo_action (head, NULL);
1151
        priv->index--;
1152
    }
1153
}
1154
1155
/** ---------------------------------------------------------------- */
1156
static void
1157
stack_push_action (MarlinUndoManagerPrivate *priv,
1158
                   MarlinUndoActionData *action)
1159
{
1160
    guint length;
1161
1162
    clear_redo_actions (priv);
1163
1164
    g_queue_push_head (priv->stack, (gpointer) action);
1165
    length = g_queue_get_length (priv->stack);
1166
1167
    if (length > priv->undo_levels) {
1168
        stack_fix_size (priv);
1169
    }
1170
}
1171
1172
/** ---------------------------------------------------------------- */
1173
static gchar *
1174
get_first_target_short_name (MarlinUndoActionData *action)
1175
{
1176
    GList *targets_first;
1177
    gchar *file_name;
1178
1179
    targets_first = g_list_first (action->destinations);
1180
    file_name = (gchar *) g_strdup (targets_first->data);
1181
1182
    return file_name;
1183
}
1184
1185
/** ---------------------------------------------------------------- */
1186
static gchar *
1187
get_undo_description (MarlinUndoActionData *action)
1188
{
1189
    gchar *description = NULL;
1190
    gchar *source = NULL;
1191
    guint count;
1192
1193
    if (action != NULL) {
1194
        if (action->undo_description == NULL) {
1195
            if (action->src_dir) {
1196
                source = g_file_get_path (action->src_dir);
1197
            }
1198
            count = action->count;
1199
            switch (action->type) {
1200
            case MARLIN_UNDO_COPY:
1201
                if (count != 1) {
1202
                    description = g_strdup_printf (_("Delete %d copied items"), count);
1203
                } else {
1204
                    gchar *name = get_first_target_short_name (action);
1205
                    description = g_strdup_printf (_("Delete '%s'"), name);
1206
                    g_free (name);
1207
                }
1208
                break;
1209
            case MARLIN_UNDO_DUPLICATE:
1210
                if (count != 1) {
1211
                    description =
1212
                        g_strdup_printf (_("Delete %d duplicated items"), count);
1213
                } else {
1214
                    gchar *name = get_first_target_short_name (action);
1215
                    description = g_strdup_printf (_("Delete '%s'"), name);
1216
                    g_free (name);
1217
                }
1218
                break;
1219
            case MARLIN_UNDO_MOVE:
1220
                if (count != 1) {
1221
                    description =
1222
                        g_strdup_printf (_
1223
                                         ("Move %d items back to '%s'"), count, source);
1224
                } else {
1225
                    gchar *name = get_first_target_short_name (action);
1226
                    description =
1227
                        g_strdup_printf (_("Move '%s' back to '%s'"), name, source);
1228
                    g_free (name);
1229
                }
1230
                break;
1231
            case MARLIN_UNDO_RENAME:
1232
                {
1233
                    char *from_name = get_uri_basename (action->new_uri);
1234
                    char *to_name = get_uri_basename (action->old_uri);
1235
                    description =
1236
                        g_strdup_printf (_("Rename '%s' as '%s'"), from_name, to_name);
1237
                    g_free (from_name);
1238
                    g_free (to_name);
1239
                }
1240
                break;
798.3.1 by am.monkeyd at gmail
new empty file & templates - draft
1241
            /*case MARLIN_UNDO_CREATEFILEFROMTEMPLATE:*/
779 by am.monkeyd at gmail
undo redo - first tests. let's see if it works as well as it did in nautilus-elementary
1242
            case MARLIN_UNDO_CREATEEMPTYFILE:
1243
            case MARLIN_UNDO_CREATEFOLDER:
1244
                {
1245
                    char *name = get_uri_basename (action->target_uri);
1246
                    description = g_strdup_printf (_("Delete '%s'"), name);
1247
                    g_free (name);
1248
                }
1249
                break;
1250
            case MARLIN_UNDO_MOVETOTRASH:
1251
                {
1252
                    count = g_hash_table_size (action->trashed);
1253
                    if (count != 1) {
1254
                        description =
1255
                            g_strdup_printf (_("Restore %d items from trash"), count);
1256
                    } else {
1257
                        GList *keys = g_hash_table_get_keys (action->trashed);
1258
                        GList *first = g_list_first (keys);
1259
                        char *item = (char *) first->data;
1260
                        char *name = get_uri_basename (item);
1261
                        char *orig_path = get_uri_parent_path (item);
1262
                        description =
1263
                            g_strdup_printf (_("Restore '%s' to '%s'"), name, orig_path);
1264
                        g_free (name);
1265
                        g_free (orig_path);
1266
                        g_list_free (keys);
1267
                    }
1268
                }
1269
                break;
1270
            case MARLIN_UNDO_RESTOREFROMTRASH:
1271
                {
1272
                    if (count != 1) {
1273
                        description =
1274
                            g_strdup_printf (_("Move %d items back to trash"), count);
1275
                    } else {
1276
                        gchar *name = get_first_target_short_name (action);
1277
                        description = g_strdup_printf (_("Move '%s' back to trash"), name);
1278
                        g_free (name);
1279
                    }
1280
                }
1281
                break;
1282
            case MARLIN_UNDO_CREATELINK:
1283
                {
1284
                    if (count != 1) {
1285
                        description =
1286
                            g_strdup_printf (_("Delete links to %d items"), count);
1287
                    } else {
1288
                        gchar *name = get_first_target_short_name (action);
1289
                        description = g_strdup_printf (_("Delete link to '%s'"), name);
1290
                        g_free (name);
1291
                    }
1292
                }
1293
                break;
1294
            case MARLIN_UNDO_RECURSIVESETPERMISSIONS:
1295
                {
1296
                    char *name = g_file_get_path (action->dest_dir);
1297
                    description =
1298
                        g_strdup_printf (_
1299
                                         ("Restore original permissions of items enclosed in '%s'"), name);
1300
                    g_free (name);
1301
                }
1302
                break;
1303
            case MARLIN_UNDO_SETPERMISSIONS:
1304
                {
1305
                    char *name = get_uri_basename (action->target_uri);
1306
                    description =
1307
                        g_strdup_printf (_("Restore original permissions of '%s'"), name);
1308
                    g_free (name);
1309
                }
1310
                break;
1311
            case MARLIN_UNDO_CHANGEGROUP:
1312
                {
1313
                    char *name = get_uri_basename (action->target_uri);
1314
                    description =
1315
                        g_strdup_printf (_
1316
                                         ("Restore group of '%s' to '%s'"),
1317
                                         name, action->original_group_name_or_id);
1318
                    g_free (name);
1319
                }
1320
                break;
1321
            case MARLIN_UNDO_CHANGEOWNER:
1322
                {
1323
                    char *name = get_uri_basename (action->target_uri);
1324
                    description =
1325
                        g_strdup_printf (_
1326
                                         ("Restore owner of '%s' to '%s'"),
1327
                                         name, action->original_user_name_or_id);
1328
                    g_free (name);
1329
                }
1330
                break;
1331
            default:
1332
                break;
1333
            }
1334
            if (source) {
1335
                g_free (source);
1336
            }
1337
            action->undo_description = description;
1338
        } else {
1339
            return action->undo_description;
1340
        }
1341
    }
1342
1343
    return description;
1344
}
1345
1346
/** ---------------------------------------------------------------- */
1347
static gchar *
1348
get_redo_description (MarlinUndoActionData * action)
1349
{
1350
    gchar *description = NULL;
1351
    gchar *destination = NULL;
1352
    guint count;
1353
1354
    if (action != NULL) {
1355
        if (action->redo_description == NULL) {
1356
            if (action->dest_dir) {
1357
                destination = g_file_get_path (action->dest_dir);
1358
            }
1359
            count = action->count;
1360
            switch (action->type) {
1361
            case MARLIN_UNDO_COPY:
1362
                if (count != 1) {
1363
                    description =
1364
                        g_strdup_printf (_
1365
                                         ("Copy %d items to '%s'"), count, destination);
1366
                } else {
1367
                    gchar *name = get_first_target_short_name (action);
1368
                    description =
1369
                        g_strdup_printf (_("Copy '%s' to '%s'"), name, destination);
1370
                    g_free (name);
1371
                }
1372
                break;
1373
            case MARLIN_UNDO_DUPLICATE:
1374
                if (count != 1) {
1375
                    description =
1376
                        g_strdup_printf (_
1377
                                         ("Duplicate of %d items in '%s'"), count, destination);
1378
                } else {
1379
                    gchar *name = get_first_target_short_name (action);
1380
                    description =
1381
                        g_strdup_printf (_
1382
                                         ("Duplicate '%s' in '%s'"), name, destination);
1383
                    g_free (name);
1384
                }
1385
                break;
1386
            case MARLIN_UNDO_MOVE:
1387
                if (count != 1) {
1388
                    description =
1389
                        g_strdup_printf (_
1390
                                         ("Move %d items to '%s'"), count, destination);
1391
                } else {
1392
                    gchar *name = get_first_target_short_name (action);
1393
                    description =
1394
                        g_strdup_printf (_("Move '%s' to '%s'"), name, destination);
1395
                    g_free (name);
1396
                }
1397
                break;
1398
            case MARLIN_UNDO_RENAME:
1399
                {
1400
                    char *from_name = get_uri_basename (action->old_uri);
1401
                    char *to_name = get_uri_basename (action->new_uri);
1402
                    description =
1403
                        g_strdup_printf (_("Rename '%s' as '%s'"), from_name, to_name);
1404
                    g_free (from_name);
1405
                    g_free (to_name);
1406
                }
1407
                break;
1408
            case MARLIN_UNDO_CREATEFILEFROMTEMPLATE:
1409
                {
1410
                    char *name = get_uri_basename (action->target_uri);
1411
                    description =
1412
                        g_strdup_printf (_("Create new file '%s' from template "), name);
1413
                    g_free (name);
1414
                }
1415
                break;
1416
            case MARLIN_UNDO_CREATEEMPTYFILE:
1417
                {
1418
                    char *name = get_uri_basename (action->target_uri);
1419
                    description = g_strdup_printf (_("Create an empty file '%s'"), name);
1420
                    g_free (name);
1421
                }
1422
                break;
1423
            case MARLIN_UNDO_CREATEFOLDER:
1424
                {
1425
                    char *name = get_uri_basename (action->target_uri);
1426
                    description = g_strdup_printf (_("Create a new folder '%s'"), name);
1427
                    g_free (name);
1428
                }
1429
                break;
1430
            case MARLIN_UNDO_MOVETOTRASH:
1431
                {
1432
                    count = g_hash_table_size (action->trashed);
1433
                    if (count != 1) {
1434
                        description = g_strdup_printf (_("Move %d items to trash"), count);
1435
                    } else {
1436
                        GList *keys = g_hash_table_get_keys (action->trashed);
1437
                        GList *first = g_list_first (keys);
1438
                        char *item = (char *) first->data;
1439
                        char *name = get_uri_basename (item);
1440
                        description = g_strdup_printf (_("Move '%s' to trash"), name);
1441
                        g_free (name);
1442
                        g_list_free (keys);
1443
                    }
1444
                }
1445
                break;
1446
            case MARLIN_UNDO_RESTOREFROMTRASH:
1447
                {
1448
                    if (count != 1) {
1449
                        description =
1450
                            g_strdup_printf (_("Restore %d items from trash"), count);
1451
                    } else {
1452
                        gchar *name = get_first_target_short_name (action);
1453
                        description = g_strdup_printf (_("Restore '%s' from trash"), name);
1454
                        g_free (name);
1455
                    }
1456
                }
1457
                break;
1458
            case MARLIN_UNDO_CREATELINK:
1459
                {
1460
                    if (count != 1) {
1461
                        description =
1462
                            g_strdup_printf (_("Create links to %d items"), count);
1463
                    } else {
1464
                        gchar *name = get_first_target_short_name (action);
1465
                        description = g_strdup_printf (_("Create link to '%s'"), name);
1466
                        g_free (name);
1467
                    }
1468
                }
1469
                break;
1470
            case MARLIN_UNDO_RECURSIVESETPERMISSIONS:
1471
                {
1472
                    char *name = g_file_get_path (action->dest_dir);
1473
                    description =
1474
                        g_strdup_printf (_("Set permissions of items enclosed in '%s'"),
1475
                                         name);
1476
                    g_free (name);
1477
                }
1478
                break;
1479
            case MARLIN_UNDO_SETPERMISSIONS:
1480
                {
1481
                    char *name = get_uri_basename (action->target_uri);
1482
                    description = g_strdup_printf (_("Set permissions of '%s'"), name);
1483
                    g_free (name);
1484
                }
1485
                break;
1486
            case MARLIN_UNDO_CHANGEGROUP:
1487
                {
1488
                    char *name = get_uri_basename (action->target_uri);
1489
                    description =
1490
                        g_strdup_printf (_
1491
                                         ("Set group of '%s' to '%s'"),
1492
                                         name, action->new_group_name_or_id);
1493
                    g_free (name);
1494
                }
1495
                break;
1496
            case MARLIN_UNDO_CHANGEOWNER:
1497
                {
1498
                    char *name = get_uri_basename (action->target_uri);
1499
                    description =
1500
                        g_strdup_printf (_
1501
                                         ("Set owner of '%s' to '%s'"), name, action->new_user_name_or_id);
1502
                    g_free (name);
1503
                }
1504
                break;
1505
            default:
1506
                break;
1507
            }
1508
            if (destination) {
1509
                g_free (destination);
1510
            }
1511
            action->redo_description = description;
1512
        } else {
1513
            return action->redo_description;
1514
        }
1515
    }
1516
1517
    return description;
1518
}
1519
1520
/** ---------------------------------------------------------------- */
1521
static gchar *
1522
get_undo_label (MarlinUndoActionData * action)
1523
{
1524
    gchar *label = NULL;
1525
    guint count;
1526
1527
    if (action != NULL) {
1528
        if (action->undo_label == NULL) {
1529
            count = action->count;
1530
            switch (action->type) {
1531
            case MARLIN_UNDO_COPY:
1532
                label = g_strdup_printf (ngettext
1533
                                         ("_Undo copy of %d item",
1534
                                          "_Undo copy of %d items", count), count);
1535
                break;
1536
            case MARLIN_UNDO_DUPLICATE:
1537
                label = g_strdup_printf (ngettext
1538
                                         ("_Undo duplicate of %d item",
1539
                                          "_Undo duplicate of %d items", count), count);
1540
                break;
1541
            case MARLIN_UNDO_MOVE:
1542
                label = g_strdup_printf (ngettext
1543
                                         ("_Undo move of %d item",
1544
                                          "_Undo move of %d items", count), count);
1545
                break;
1546
            case MARLIN_UNDO_RENAME:
1547
                label = g_strdup_printf (ngettext
1548
                                         ("_Undo rename of %d item",
1549
                                          "_Undo rename of %d items", count), count);
1550
                break;
1551
            case MARLIN_UNDO_CREATEEMPTYFILE:
1552
                label = g_strdup_printf (_("_Undo creation of an empty file"));
1553
                break;
1554
            case MARLIN_UNDO_CREATEFILEFROMTEMPLATE:
1555
                label = g_strdup_printf (_("_Undo creation of a file from template"));
1556
                break;
1557
            case MARLIN_UNDO_CREATEFOLDER:
1558
                label = g_strdup_printf (ngettext
1559
                                         ("_Undo creation of %d folder",
1560
                                          "_Undo creation of %d folders", count), count);
1561
                break;
1562
            case MARLIN_UNDO_MOVETOTRASH:
1563
                label = g_strdup_printf (ngettext
1564
                                         ("_Undo move to trash of %d item",
1565
                                          "_Undo move to trash of %d items", count), count);
1566
                break;
1567
            case MARLIN_UNDO_RESTOREFROMTRASH:
1568
                label = g_strdup_printf (ngettext
1569
                                         ("_Undo restore from trash of %d item",
1570
                                          "_Undo restore from trash of %d items", count), count);
1571
                break;
1572
            case MARLIN_UNDO_CREATELINK:
1573
                label = g_strdup_printf (ngettext
1574
                                         ("_Undo create link to %d item",
1575
                                          "_Undo create link to %d items", count), count);
1576
                break;
1577
            case MARLIN_UNDO_DELETE:
1578
                label = g_strdup_printf (ngettext
1579
                                         ("_Undo delete of %d item",
1580
                                          "_Undo delete of %d items", count), count);
1581
                break;
1582
            case MARLIN_UNDO_RECURSIVESETPERMISSIONS:
1583
                label = g_strdup_printf (ngettext
1584
                                         ("Undo recursive change permissions of %d item",
1585
                                          "Undo recursive change permissions of %d items",
1586
                                          count), count);
1587
                break;
1588
            case MARLIN_UNDO_SETPERMISSIONS:
1589
                label = g_strdup_printf (ngettext
1590
                                         ("Undo change permissions of %d item",
1591
                                          "Undo change permissions of %d items", count), count);
1592
                break;
1593
            case MARLIN_UNDO_CHANGEGROUP:
1594
                label = g_strdup_printf (ngettext
1595
                                         ("Undo change group of %d item",
1596
                                          "Undo change group of %d items", count), count);
1597
                break;
1598
            case MARLIN_UNDO_CHANGEOWNER:
1599
                label = g_strdup_printf (ngettext
1600
                                         ("Undo change owner of %d item",
1601
                                          "Undo change owner of %d items", count), count);
1602
                break;
1603
            default:
1604
                break;
1605
            }
1606
            action->undo_label = label;
1607
        } else {
1608
            return action->undo_label;
1609
        }
1610
    }
1611
1612
    return label;
1613
}
1614
1615
/** ---------------------------------------------------------------- */
1616
static gchar *
1617
get_redo_label (MarlinUndoActionData * action)
1618
{
1619
    gchar *label = NULL;
1620
    guint count;
1621
1622
    if (action != NULL) {
1623
        if (action->redo_label == NULL) {
1624
            count = action->count;
1625
            switch (action->type) {
1626
            case MARLIN_UNDO_COPY:
1627
                label = g_strdup_printf (ngettext
1628
                                         ("_Redo copy of %d item",
1629
                                          "_Redo copy of %d items", count), count);
1630
                break;
1631
            case MARLIN_UNDO_DUPLICATE:
1632
                label = g_strdup_printf (ngettext
1633
                                         ("_Redo duplicate of %d item",
1634
                                          "_Redo duplicate of %d items", count), count);
1635
                break;
1636
            case MARLIN_UNDO_MOVE:
1637
                label = g_strdup_printf (ngettext
1638
                                         ("_Redo move of %d item",
1639
                                          "_Redo move of %d items", count), count);
1640
                break;
1641
            case MARLIN_UNDO_RENAME:
1642
                label = g_strdup_printf (ngettext
1643
                                         ("_Redo rename of %d item",
1644
                                          "_Redo rename of %d items", count), count);
1645
                break;
1646
            case MARLIN_UNDO_CREATEEMPTYFILE:
1647
                label = g_strdup_printf (_("_Redo creation of an empty file"));
1648
                break;
1649
            case MARLIN_UNDO_CREATEFILEFROMTEMPLATE:
1650
                label = g_strdup_printf (_("_Redo creation of a file from template"));
1651
                break;
1652
            case MARLIN_UNDO_CREATEFOLDER:
1653
                label = g_strdup_printf (ngettext
1654
                                         ("_Redo creation of %d folder",
1655
                                          "_Redo creation of %d folders", count), count);
1656
                break;
1657
            case MARLIN_UNDO_MOVETOTRASH:
1658
                label = g_strdup_printf (ngettext
1659
                                         ("_Redo move to trash of %d item",
1660
                                          "_Redo move to trash of %d items", count), count);
1661
                break;
1662
            case MARLIN_UNDO_RESTOREFROMTRASH:
1663
                label = g_strdup_printf (ngettext
1664
                                         ("_Redo restore from trash of %d item",
1665
                                          "_Redo restore from trash of %d items", count), count);
1666
                break;
1667
            case MARLIN_UNDO_CREATELINK:
1668
                label = g_strdup_printf (ngettext
1669
                                         ("_Redo create link to %d item",
1670
                                          "_Redo create link to %d items", count), count);
1671
                break;
1672
            case MARLIN_UNDO_DELETE:
1673
                label = g_strdup_printf (ngettext
1674
                                         ("_Redo delete of %d item",
1675
                                          "_Redo delete of %d items", count), count);
1676
                break;
1677
            case MARLIN_UNDO_RECURSIVESETPERMISSIONS:
1678
                label = g_strdup_printf (ngettext
1679
                                         ("Redo recursive change permissions of %d item",
1680
                                          "Redo recursive change permissions of %d items",
1681
                                          count), count);
1682
                break;
1683
            case MARLIN_UNDO_SETPERMISSIONS:
1684
                label = g_strdup_printf (ngettext
1685
                                         ("Redo change permissions of %d item",
1686
                                          "Redo change permissions of %d items", count), count);
1687
                break;
1688
            case MARLIN_UNDO_CHANGEGROUP:
1689
                label = g_strdup_printf (ngettext
1690
                                         ("Redo change group of %d item",
1691
                                          "Redo change group of %d items", count), count);
1692
                break;
1693
            case MARLIN_UNDO_CHANGEOWNER:
1694
                label = g_strdup_printf (ngettext
1695
                                         ("Redo change owner of %d item",
1696
                                          "Redo change owner of %d items", count), count);
1697
                break;
1698
            default:
1699
                break;
1700
            }
1701
            action->redo_label = label;
1702
        } else {
1703
            return action->redo_label;
1704
        }
1705
    }
1706
1707
    return label;
1708
}
1709
1710
/** ---------------------------------------------------------------- */
1711
static void
1712
undo_redo_done_transfer_callback (GHashTable * debuting_uris, gpointer data)
1713
{
1714
    MarlinUndoActionData *action;
1715
1716
    action = (MarlinUndoActionData *) data;
1717
1718
    /* If the action needed to be freed but was locked, free now */
1719
    if (action->freed) {
1720
        free_undo_action (action, NULL);
1721
    } else {
1722
        action->locked = FALSE;
1723
    }
1724
1725
    MarlinUndoManager *manager = action->manager;
1726
    manager->priv->undo_redo_flag = FALSE;
1727
1728
    /* Update menus */
1729
    do_menu_update (action->manager);
1730
}
1731
1732
/** ---------------------------------------------------------------- */
1733
static void
1734
undo_redo_done_delete_callback (GHashTable *
1735
                                debuting_uris, gboolean user_cancel, gpointer callback_data)
1736
{
1737
    undo_redo_done_transfer_callback (debuting_uris, callback_data);
1738
}
1739
1740
/** ---------------------------------------------------------------- */
1741
static void
1742
undo_redo_done_create_callback (GFile * new_file, gpointer callback_data)
1743
{
1744
    undo_redo_done_transfer_callback (NULL, callback_data);
1745
}
1746
1747
/** ---------------------------------------------------------------- */
1748
static void
1749
undo_redo_op_callback (gpointer callback_data)
1750
{
1751
    undo_redo_done_transfer_callback (NULL, callback_data);
1752
}
1753
1754
/** ---------------------------------------------------------------- */
1755
static void
1756
undo_redo_done_rename_callback (GOFFile * file,
1757
                                GFile * result_location, GError * error, gpointer callback_data)
1758
{
1759
    undo_redo_done_transfer_callback (NULL, callback_data);
1760
}
1761
1762
/** ---------------------------------------------------------------- */
1763
static void
1764
free_undo_action (gpointer data, gpointer user_data)
1765
{
1766
    MarlinUndoActionData *action = (MarlinUndoActionData *) data;
1767
1768
    if (!action)
1769
        return;
1770
1771
    g_free (action->template);
1772
    g_free (action->target_uri);
1773
    g_free (action->old_uri);
1774
    g_free (action->new_uri);
1775
1776
    g_free (action->undo_label);
1777
    g_free (action->undo_description);
1778
    g_free (action->redo_label);
1779
    g_free (action->redo_description);
1780
1781
    g_free (action->original_group_name_or_id);
1782
    g_free (action->original_user_name_or_id);
1783
    g_free (action->new_group_name_or_id);
1784
    g_free (action->new_user_name_or_id);
1785
1786
    if (action->sources) {
1787
        g_list_foreach (action->sources, (GFunc) g_free, NULL);
1788
        g_list_free (action->sources);
1789
    }
1790
    if (action->destinations) {
1791
        g_list_foreach (action->destinations, (GFunc) g_free, NULL);
1792
        g_list_free (action->destinations);
1793
    }
1794
1795
    if (action->trashed) {
1796
        g_hash_table_destroy (action->trashed);
1797
    }
1798
1799
    if (action->original_permissions) {
1800
        g_hash_table_destroy (action->original_permissions);
1801
    }
1802
1803
    if (action->src_dir)
1804
        g_object_unref (action->src_dir);
1805
    if (action->dest_dir)
1806
        g_object_unref (action->dest_dir);
1807
1808
    if (action)
1809
        g_slice_free (MarlinUndoActionData, action);
1810
}
1811
1812
/** ---------------------------------------------------------------- */
1813
static void
1814
undostack_dispose_all (GQueue * queue)
1815
{
1816
    g_queue_foreach (queue, free_undo_action, NULL);
1817
}
1818
1819
/** ---------------------------------------------------------------- */
1820
static gboolean
1821
can_undo (MarlinUndoManagerPrivate * priv)
1822
{
1823
    return (get_next_undo_action (priv) != NULL);
1824
}
1825
1826
/** ---------------------------------------------------------------- */
1827
static gboolean
1828
can_redo (MarlinUndoManagerPrivate * priv)
1829
{
1830
    return (get_next_redo_action (priv) != NULL);
1831
}
1832
1833
/** ---------------------------------------------------------------- */
1834
static MarlinUndoActionData *
1835
get_next_redo_action (MarlinUndoManagerPrivate * priv)
1836
{
1837
    if (g_queue_is_empty (priv->stack)) {
1838
        return NULL;
1839
    }
1840
1841
    if (priv->index == 0) {
1842
        /* ... no redo actions */
1843
        return NULL;
1844
    }
1845
1846
    MarlinUndoActionData *action = g_queue_peek_nth (priv->stack,
1847
                                                     priv->index - 1);
1848
1849
    if (action->locked) {
1850
        return NULL;
1851
    } else {
1852
        return action;
1853
    }
1854
}
1855
1856
/** ---------------------------------------------------------------- */
1857
static MarlinUndoActionData *
1858
get_next_undo_action (MarlinUndoManagerPrivate * priv)
1859
{
1860
    if (g_queue_is_empty (priv->stack)) {
1861
        return NULL;
1862
    }
1863
1864
    guint stack_size = g_queue_get_length (priv->stack);
1865
1866
    if (priv->index == stack_size) {
1867
        return NULL;
1868
    }
1869
1870
    MarlinUndoActionData *action = g_queue_peek_nth (priv->stack,
1871
                                                     priv->index);
1872
1873
    if (action->locked) {
1874
        return NULL;
1875
    } else {
1876
        return action;
1877
    }
1878
}
1879
1880
/** ---------------------------------------------------------------- */
1881
static void
1882
do_menu_update (MarlinUndoManager *manager)
1883
{
1884
    g_return_if_fail (manager);
1885
1886
    MarlinUndoActionData *action;
1887
    MarlinUndoManagerPrivate *priv = manager->priv;
1888
    MarlinUndoMenuData *data = g_slice_new0 (MarlinUndoMenuData);
1889
798.2.4 by am.monkeyd at gmail
updating deprecated mutex
1890
    g_mutex_lock (&priv->mutex);
779 by am.monkeyd at gmail
undo redo - first tests. let's see if it works as well as it did in nautilus-elementary
1891
1892
    action = get_next_undo_action (priv);
1893
    if (action != NULL) {
1894
        data->undo_label = get_undo_label (action);
1895
        data->undo_description = get_undo_description (action);
1896
    }
1897
1898
    action = get_next_redo_action (priv);
1899
    if (action != NULL) {
1900
        data->redo_label = get_redo_label (action);
1901
        data->redo_description = get_redo_description (action);
1902
    }
1903
798.2.4 by am.monkeyd at gmail
updating deprecated mutex
1904
    g_mutex_unlock (&priv->mutex);
779 by am.monkeyd at gmail
undo redo - first tests. let's see if it works as well as it did in nautilus-elementary
1905
1906
    /* Update menus */
1907
    g_signal_emit (manager, signals[REQUEST_MENU_UPDATE], 0, data);
1908
1909
    /* Free the signal data */
1910
    // Note: we do not own labels and descriptions, they are part of the action.
1911
    g_slice_free (MarlinUndoMenuData, data);
1912
}
1913
1914
/** ---------------------------------------------------------------- */
1915
static GList *
1916
construct_gfile_list (const GList * urilist, GFile * parent)
1917
{
1918
    const GList *l;
1919
    GList *file_list = NULL;
1920
    GFile *file;
1921
1922
    for (l = urilist; l != NULL; l = l->next) {
1923
        file = g_file_get_child (parent, l->data);
1924
        file_list = g_list_append (file_list, file);
1925
    }
1926
1927
    return file_list;
1928
}
1929
1930
/** ---------------------------------------------------------------- */
1931
static GList *
1932
construct_gfile_list_from_uri (char *uri)
1933
{
1934
    GList *file_list = NULL;
1935
    GFile *file;
1936
1937
    file = g_file_new_for_uri (uri);
1938
    file_list = g_list_append (file_list, file);
1939
1940
    return file_list;
1941
}
1942
1943
/** ---------------------------------------------------------------- */
1944
static GList *
1945
uri_list_to_gfile_list (GList * urilist)
1946
{
1947
    const GList *l;
1948
    GList *file_list = NULL;
1949
    GFile *file;
1950
1951
    for (l = urilist; l != NULL; l = l->next) {
1952
        file = g_file_new_for_uri (l->data);
1953
        file_list = g_list_append (file_list, file);
1954
    }
1955
1956
    return file_list;
1957
}
1958
1959
/** ---------------------------------------------------------------- */
1960
static char *
1961
get_uri_basename (char *uri)
1962
{
1963
    GFile *f = g_file_new_for_uri (uri);
1964
    char *basename = g_file_get_basename (f);
1965
    g_object_unref (f);
1966
    return basename;
1967
}
1968
1969
/** ---------------------------------------------------------------- */
1970
static char *
1971
get_uri_parent (char *uri)
1972
{
1973
    GFile *f = g_file_new_for_uri (uri);
1974
    GFile *p = g_file_get_parent (f);
1975
    char *parent = g_file_get_uri (p);
1976
    g_object_unref (f);
1977
    g_object_unref (p);
1978
    return parent;
1979
}
1980
1981
/** ---------------------------------------------------------------- */
1982
static char *
1983
get_uri_parent_path (char *uri)
1984
{
1985
    GFile *f = g_file_new_for_uri (uri);
1986
    GFile *p = g_file_get_parent (f);
1987
    char *parent = g_file_get_path (p);
1988
    g_object_unref (f);
1989
    g_object_unref (p);
1990
    return parent;
1991
}
1992
1993
/** ---------------------------------------------------------------- */
1994
static GFile *
1995
get_file_parent_from_uri (char *uri)
1996
{
1997
    GFile *f = g_file_new_for_uri (uri);
1998
    GFile *p = g_file_get_parent (f);
1999
    g_object_unref (f);
2000
    return p;
2001
}
2002
2003
/** ---------------------------------------------------------------- */
2004
static GHashTable *
2005
retrieve_files_to_restore (GHashTable * trashed)
2006
{
2007
    GFileEnumerator *enumerator;
2008
    GFileInfo *info;
2009
    GFile *trash;
2010
    GFile *item;
2011
    guint64 mtime_item;
2012
    guint64 *mtime;
2013
    const char *origpath;
2014
    GFile *origfile;
2015
    char *origuri;
2016
    gpointer lookupvalue;
2017
    GHashTable *to_restore;
2018
2019
    to_restore =
2020
        g_hash_table_new_full (g_direct_hash,
2021
                               g_direct_equal, g_object_unref, g_free);
2022
2023
    trash = g_file_new_for_uri ("trash:");
2024
2025
    enumerator = g_file_enumerate_children (trash,
2026
                                            G_FILE_ATTRIBUTE_STANDARD_NAME
2027
                                            ","
2028
                                            G_FILE_ATTRIBUTE_TIME_MODIFIED
2029
                                            ",trash::orig-path", G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, FALSE, NULL);
2030
2031
    mtime = 0;
2032
2033
2034
    //amtest
2035
    /*guint nb;
2036
      GList *l;*/
2037
    if (!(g_hash_table_size (trashed)) > 0)
2038
        return NULL;
2039
2040
    if (enumerator) {
2041
        while ((info =
2042
                g_file_enumerator_next_file (enumerator, NULL, NULL)) != NULL) {
2043
            /* Retrieve the original file uri */
2044
            origpath = g_file_info_get_attribute_byte_string (info, "trash::orig-path");
2045
            origfile = g_file_new_for_path (origpath);
2046
            origuri = g_file_get_uri (origfile);
2047
            g_object_unref (origfile);
2048
2049
            lookupvalue = g_hash_table_lookup (trashed, origuri);
2050
2051
            if (lookupvalue) {
2052
                //printf ("we got a MATCH\n");
2053
                mtime = (guint64 *)
2054
                    lookupvalue;
2055
                mtime_item =
2056
                    g_file_info_get_attribute_uint64
2057
                    (info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
2058
                if (*mtime == mtime_item) {
2059
                    item = g_file_get_child (trash, g_file_info_get_name (info)); /* File in the trash */
2060
                    g_hash_table_insert (to_restore, item, origuri);
2061
                }
2062
            } else {
2063
                g_free (origuri);
2064
            }
2065
2066
        }
2067
        g_file_enumerator_close (enumerator, FALSE, NULL);
2068
        g_object_unref (enumerator);
2069
    }
2070
    g_object_unref (trash);
2071
2072
    return to_restore;
2073
}
2074
2075
/** ---------------------------------------------------------------- */