~ubuntu-branches/ubuntu/trusty/pcmanfm/trusty-proposed

« back to all changes in this revision

Viewing changes to src/ptk/ptk-file-properties.c

  • Committer: Bazaar Package Importer
  • Author(s): Andrew Lee
  • Date: 2008-09-26 10:19:20 UTC
  • mfrom: (4.1.5 intrepid)
  • Revision ID: james.westby@ubuntu.com-20080926101920-cfldybkmwgwrtv9u
Tags: 0.5-3
* Correct spellings,  03_correct_spelling.dpatch (Closes:498794) 
* Code in some files are taken from other projects, added these
  informations into copyright file. (Closes:499678)
* Applied 04_defaut_terminal.dpatch to support x-terminal-emulator
  alternative. (Closes:497494) 

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
*  C Implementation: file_properties
 
3
*
 
4
* Description:
 
5
*
 
6
*
 
7
* Author: Hong Jen Yee (PCMan) <pcman.tw (AT) gmail.com>, (C) 2006
 
8
*
 
9
* Copyright: See COPYING file that comes with this distribution
 
10
*
 
11
*/
 
12
 
 
13
#ifdef HAVE_CONFIG_H
 
14
#  include <config.h>
 
15
#endif
 
16
 
 
17
#include "private.h"
 
18
 
 
19
#include <gtk/gtk.h>
 
20
#include "glib-mem.h"
 
21
 
 
22
#include "ptk-file-properties.h"
 
23
#include "ptk-ui-xml.h"
 
24
 
 
25
#include "mime-type/mime-type.h"
 
26
 
 
27
#include <sys/types.h>
 
28
#include <pwd.h>
 
29
#include <grp.h>
 
30
#include <string.h>
 
31
 
 
32
#include "ptk-file-task.h"
 
33
#include "ptk-utils.h"
 
34
 
 
35
#include "vfs-file-info.h"
 
36
#include "vfs-app-desktop.h"
 
37
#include "ptk-app-chooser.h"
 
38
 
 
39
const char* chmod_names[] =
 
40
    {
 
41
        "owner_r", "owner_w", "owner_x",
 
42
        "group_r", "group_w", "group_x",
 
43
        "others_r", "others_w", "others_x",
 
44
        "set_uid", "set_gid", "sticky"
 
45
    };
 
46
 
 
47
typedef struct
 
48
{
 
49
    char* dir_path;
 
50
    GList* file_list;
 
51
    GtkWidget* dlg;
 
52
 
 
53
    GtkEntry* owner;
 
54
    GtkEntry* group;
 
55
    char* owner_name;
 
56
    char* group_name;
 
57
 
 
58
    GtkToggleButton* chmod_btns[ N_CHMOD_ACTIONS ];
 
59
    guchar chmod_states[ N_CHMOD_ACTIONS ];
 
60
 
 
61
    GtkLabel* total_size_label;
 
62
    GtkLabel* size_on_disk_label;
 
63
    off_t total_size;
 
64
    off_t size_on_disk;
 
65
    guint total_count;
 
66
    gboolean cancel;
 
67
    gboolean done;
 
68
    GThread* calc_size_thread;
 
69
    guint update_label_timer;
 
70
}
 
71
FilePropertiesDialogData;
 
72
 
 
73
static void
 
74
on_dlg_response ( GtkDialog *dialog,
 
75
                                gint response_id,
 
76
                                gpointer user_data );
 
77
 
 
78
/*
 
79
* void get_total_size_of_dir( const char* path, off_t* size )
 
80
* Recursively count total size of all files in the specified directory.
 
81
* If the path specified is a file, the size of the file is directly returned.
 
82
* cancel is used to cancel the operation. This function will check the value
 
83
* pointed by cancel in every iteration. If cancel is set to TRUE, the
 
84
* calculation is cancelled.
 
85
* NOTE: path is encoded in on-disk encoding and not necessarily UTF-8.
 
86
*/
 
87
static void calc_total_size_of_files( const char* path, FilePropertiesDialogData* data )
 
88
{
 
89
    GDir * dir;
 
90
    const char* name;
 
91
    char* full_path;
 
92
    struct stat file_stat;
 
93
 
 
94
    if ( data->cancel )
 
95
        return ;
 
96
 
 
97
    if ( lstat( path, &file_stat ) )
 
98
        return ;
 
99
 
 
100
    data->total_size += file_stat.st_size;
 
101
    data->size_on_disk += ( file_stat.st_blocks << 9 ); /* block x 512 */
 
102
    ++data->total_count;
 
103
 
 
104
    dir = g_dir_open( path, 0, NULL );
 
105
    if ( dir )
 
106
    {
 
107
        while ( !data->cancel && ( name = g_dir_read_name( dir ) ) )
 
108
        {
 
109
            full_path = g_build_filename( path, name, NULL );
 
110
            lstat( full_path, &file_stat );
 
111
            if ( S_ISDIR( file_stat.st_mode ) )
 
112
            {
 
113
                calc_total_size_of_files( full_path, data );
 
114
            }
 
115
            else
 
116
            {
 
117
                data->total_size += file_stat.st_size;
 
118
                data->size_on_disk += ( file_stat.st_blocks << 9 );
 
119
                ++data->total_count;
 
120
            }
 
121
            g_free( full_path );
 
122
        }
 
123
        g_dir_close( dir );
 
124
    }
 
125
}
 
126
 
 
127
static gpointer calc_size( gpointer user_data )
 
128
{
 
129
    FilePropertiesDialogData * data = ( FilePropertiesDialogData* ) user_data;
 
130
    GList* l;
 
131
    char* path;
 
132
    VFSFileInfo* file;
 
133
    for ( l = data->file_list; l; l = l->next )
 
134
    {
 
135
        if ( data->cancel )
 
136
            break;
 
137
        file = ( VFSFileInfo* ) l->data;
 
138
        path = g_build_filename( data->dir_path,
 
139
                                 vfs_file_info_get_name( file ), NULL );
 
140
        if ( path )
 
141
        {
 
142
            calc_total_size_of_files( path, data );
 
143
            g_free( path );
 
144
        }
 
145
    }
 
146
    data->done = TRUE;
 
147
    return NULL;
 
148
}
 
149
 
 
150
gboolean on_update_labels( FilePropertiesDialogData* data )
 
151
{
 
152
    char buf[ 64 ];
 
153
    char buf2[ 32 ];
 
154
 
 
155
    gdk_threads_enter();
 
156
 
 
157
    vfs_file_size_to_string( buf2, data->total_size );
 
158
    sprintf( buf, "%s  (%llu Bytes)", buf2, ( guint64 ) data->total_size );
 
159
    gtk_label_set_text( data->total_size_label, buf );
 
160
 
 
161
    vfs_file_size_to_string( buf2, data->size_on_disk );
 
162
    sprintf( buf, "%s  (%llu Bytes)", buf2, ( guint64 ) data->size_on_disk );
 
163
    gtk_label_set_text( data->size_on_disk_label, buf );
 
164
 
 
165
    gdk_threads_leave();
 
166
 
 
167
    return !data->done;
 
168
}
 
169
 
 
170
static void on_chmod_btn_toggled( GtkToggleButton* btn,
 
171
                                  FilePropertiesDialogData* data )
 
172
{
 
173
    /* Bypass the default handler */
 
174
    g_signal_stop_emission_by_name( btn, "toggled" );
 
175
    /* Block this handler while we are changing the state of buttons,
 
176
      or this handler will be called recursively. */
 
177
    g_signal_handlers_block_matched( btn, G_SIGNAL_MATCH_FUNC, 0,
 
178
                                     0, NULL, on_chmod_btn_toggled, NULL );
 
179
 
 
180
    if ( gtk_toggle_button_get_inconsistent( btn ) )
 
181
    {
 
182
        gtk_toggle_button_set_inconsistent( btn, FALSE );
 
183
        gtk_toggle_button_set_active( btn, FALSE );
 
184
    }
 
185
    else if ( ! gtk_toggle_button_get_active( btn ) )
 
186
    {
 
187
        gtk_toggle_button_set_inconsistent( btn, TRUE );
 
188
    }
 
189
 
 
190
    g_signal_handlers_unblock_matched( btn, G_SIGNAL_MATCH_FUNC, 0,
 
191
                                       0, NULL, on_chmod_btn_toggled, NULL );
 
192
}
 
193
 
 
194
static gboolean combo_sep( GtkTreeModel *model,
 
195
                           GtkTreeIter* it,
 
196
                           gpointer user_data )
 
197
{
 
198
    int i;
 
199
    for( i = 2; i > 0; --i )
 
200
    {
 
201
        char* tmp;
 
202
        gtk_tree_model_get( model, it, i, &tmp, -1 );
 
203
        if( tmp )
 
204
        {
 
205
            g_free( tmp );
 
206
            return FALSE;
 
207
        }
 
208
    }
 
209
    return TRUE;
 
210
}
 
211
 
 
212
static void on_combo_change( GtkComboBox* combo, gpointer user_data )
 
213
{
 
214
    GtkTreeIter it;
 
215
    if( gtk_combo_box_get_active_iter(combo, &it) )
 
216
    {
 
217
        const char* action;
 
218
        GtkTreeModel* model = gtk_combo_box_get_model( combo );
 
219
        gtk_tree_model_get( model, &it, 2, &action, -1 );
 
220
        if( ! action )
 
221
        {
 
222
            char* action;
 
223
            GtkWidget* parent;
 
224
            VFSMimeType* mime = (VFSMimeType*)user_data;
 
225
            parent = gtk_widget_get_toplevel( GTK_WIDGET( combo ) );
 
226
            action = (char *) ptk_choose_app_for_mime_type( GTK_WINDOW(parent),
 
227
                                                   mime );
 
228
            if( action )
 
229
            {
 
230
                gboolean exist = FALSE;
 
231
                /* check if the action is already in the list */
 
232
                if( gtk_tree_model_get_iter_first( model, &it ) )
 
233
                {
 
234
                    do
 
235
                    {
 
236
                        char* tmp;
 
237
                        gtk_tree_model_get( model, &it, 2, &tmp, -1 );
 
238
                        if( !tmp )
 
239
                            continue;
 
240
                        if( 0 == strcmp( tmp, action ) )
 
241
                        {
 
242
                            exist = TRUE;
 
243
                            g_free( tmp );
 
244
                            break;
 
245
                        }
 
246
                        g_free( tmp );
 
247
                    } while( gtk_tree_model_iter_next( model, &it ) );
 
248
                }
 
249
 
 
250
                if( ! exist ) /* It didn't exist */
 
251
                {
 
252
                    VFSAppDesktop* app = vfs_app_desktop_new( action );
 
253
                    if( app )
 
254
                    {
 
255
                        GdkPixbuf* icon;
 
256
                        icon = vfs_app_desktop_get_icon( app, 20, TRUE );
 
257
                        gtk_list_store_insert_with_values(
 
258
                                            GTK_LIST_STORE( model ), &it, 0,
 
259
                                            0, icon,
 
260
                                            1, vfs_app_desktop_get_disp_name(app),
 
261
                                            2, action, -1 );
 
262
                        if( icon )
 
263
                            gdk_pixbuf_unref( icon );
 
264
                        vfs_app_desktop_unref( app );
 
265
                        exist = TRUE;
 
266
                    }
 
267
                }
 
268
 
 
269
                if( exist )
 
270
                    gtk_combo_box_set_active_iter( combo, &it );
 
271
                g_free( action );
 
272
            }
 
273
            else
 
274
            {
 
275
                int prev_sel;
 
276
                prev_sel = GPOINTER_TO_INT( g_object_get_data( G_OBJECT(combo), "prev_sel") );
 
277
                gtk_combo_box_set_active( combo, prev_sel );
 
278
            }
 
279
        }
 
280
        else
 
281
        {
 
282
            int prev_sel = gtk_combo_box_get_active( combo );
 
283
            g_object_set_data( G_OBJECT(combo), "prev_sel", GINT_TO_POINTER(prev_sel) );
 
284
        }
 
285
    }
 
286
    else
 
287
    {
 
288
        g_object_set_data( G_OBJECT(combo), "prev_sel", GINT_TO_POINTER(-1) );
 
289
    }
 
290
}
 
291
 
 
292
GtkWidget* file_properties_dlg_new( GtkWindow* parent,
 
293
                                    const char* dir_path,
 
294
                                    GList* sel_files )
 
295
{
 
296
    GtkWidget * dlg = ptk_ui_xml_create_widget_from_file( PACKAGE_UI_DIR "/file_properties.glade" );
 
297
 
 
298
    FilePropertiesDialogData* data;
 
299
    gboolean need_calc_size = TRUE;
 
300
 
 
301
    VFSFileInfo *file, *file2;
 
302
    VFSMimeType* mime;
 
303
 
 
304
    const char* multiple_files = _( "Multiple files are selected" );
 
305
    const char* calculating;
 
306
    GtkWidget* name = ptk_ui_xml_get_widget( dlg, "file_name" );
 
307
    GtkWidget* location = ptk_ui_xml_get_widget( dlg, "location" );
 
308
    GtkWidget* mime_type = ptk_ui_xml_get_widget( dlg, "mime_type" );
 
309
    GtkWidget* open_with = ptk_ui_xml_get_widget( dlg, "open_with" );
 
310
 
 
311
    GtkWidget* mtime = ptk_ui_xml_get_widget( dlg, "mtime" );
 
312
    GtkWidget* atime = ptk_ui_xml_get_widget( dlg, "atime" );
 
313
 
 
314
    char buf[ 64 ];
 
315
    char buf2[ 32 ];
 
316
    const char* time_format = "%Y-%m-%d %H:%M";
 
317
 
 
318
    gchar* disp_path;
 
319
    gchar* file_type;
 
320
 
 
321
    int i;
 
322
    GList* l;
 
323
    gboolean same_type = TRUE;
 
324
    char *owner_group, *tmp;
 
325
 
 
326
    gtk_dialog_set_alternative_button_order( dlg, GTK_RESPONSE_OK, GTK_RESPONSE_CANCEL, -1 );
 
327
    gtk_window_set_transient_for( GTK_WINDOW( dlg ), parent );
 
328
 
 
329
    data = g_slice_new0( FilePropertiesDialogData );
 
330
    /* FIXME: When will the data be freed??? */
 
331
    g_object_set_data( G_OBJECT( dlg ), "DialogData", data );
 
332
    data->file_list = sel_files;
 
333
    data->dlg = dlg;
 
334
 
 
335
    data->dir_path = g_strdup( dir_path );
 
336
    disp_path = g_filename_display_name( dir_path );
 
337
    gtk_label_set_text( GTK_LABEL( location ), disp_path );
 
338
    g_free( disp_path );
 
339
 
 
340
    data->total_size_label = GTK_LABEL( ptk_ui_xml_get_widget( dlg, "total_size" ) );
 
341
    data->size_on_disk_label = GTK_LABEL( ptk_ui_xml_get_widget( dlg, "size_on_disk" ) );
 
342
    data->owner = GTK_ENTRY( ptk_ui_xml_get_widget( dlg, "owner" ) );
 
343
    data->group = GTK_ENTRY( ptk_ui_xml_get_widget( dlg, "group" ) );
 
344
 
 
345
    for ( i = 0; i < N_CHMOD_ACTIONS; ++i )
 
346
    {
 
347
        data->chmod_btns[ i ] = GTK_TOGGLE_BUTTON( ptk_ui_xml_get_widget( dlg, chmod_names[ i ] ) );
 
348
    }
 
349
 
 
350
    for ( l = sel_files; l && l->next; l = l->next )
 
351
    {
 
352
        VFSMimeType *type, *type2;
 
353
        file = ( VFSFileInfo* ) l->data;
 
354
        file2 = ( VFSFileInfo* ) l->next->data;
 
355
        type = vfs_file_info_get_mime_type( file );
 
356
        type2 = vfs_file_info_get_mime_type( file2 );
 
357
        if ( type != type2 )
 
358
        {
 
359
            vfs_mime_type_unref( type );
 
360
            vfs_mime_type_unref( type2 );
 
361
            same_type = FALSE;
 
362
            break;
 
363
        }
 
364
        vfs_mime_type_unref( type );
 
365
        vfs_mime_type_unref( type2 );
 
366
    }
 
367
 
 
368
    file = ( VFSFileInfo* ) sel_files->data;
 
369
    if ( same_type )
 
370
    {
 
371
        mime = vfs_file_info_get_mime_type( file );
 
372
        file_type = g_strdup_printf( "%s (%s)",
 
373
                                     vfs_mime_type_get_description( mime ),
 
374
                                     vfs_mime_type_get_type( mime ) );
 
375
        gtk_label_set_text( GTK_LABEL( mime_type ), file_type );
 
376
        g_free( file_type );
 
377
        vfs_mime_type_unref( mime );
 
378
    }
 
379
    else
 
380
    {
 
381
        gtk_label_set_text( GTK_LABEL( mime_type ), _( "Multiple files of different types" ) );
 
382
    }
 
383
 
 
384
    /* Open with...
 
385
     * Don't show this option menu if files of different types are selected,
 
386
     * ,the selected file is a folder, or its type is unknown.
 
387
     */
 
388
    if( ! same_type ||
 
389
          vfs_file_info_is_dir( file ) ||
 
390
          vfs_file_info_is_desktop_entry( file ) ||
 
391
          vfs_file_info_is_unknown_type( file ) ||
 
392
          vfs_file_info_is_executable( file, NULL ) )
 
393
    {
 
394
        /* if open with shouldn't show, destroy it. */
 
395
        gtk_widget_destroy( open_with );
 
396
        gtk_widget_destroy( ptk_ui_xml_get_widget( dlg, "open_with_label" ) );
 
397
    }
 
398
    else /* Add available actions to the option menu */
 
399
    {
 
400
        GtkTreeIter it;
 
401
        char **action, **actions;
 
402
 
 
403
        mime = vfs_file_info_get_mime_type( file );
 
404
        actions = vfs_mime_type_get_actions( mime );
 
405
        GtkCellRenderer* renderer;
 
406
        GtkListStore* model;
 
407
        gtk_cell_layout_clear( GTK_CELL_LAYOUT(open_with) );
 
408
        renderer = gtk_cell_renderer_pixbuf_new();
 
409
        gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(open_with), renderer, FALSE);
 
410
        gtk_cell_layout_set_attributes( GTK_CELL_LAYOUT(open_with), renderer,
 
411
                                        "pixbuf", 0, NULL );
 
412
        renderer = gtk_cell_renderer_text_new();
 
413
        gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(open_with), renderer, TRUE);
 
414
        gtk_cell_layout_set_attributes( GTK_CELL_LAYOUT(open_with),renderer,
 
415
                                        "text", 1, NULL );
 
416
        model = gtk_list_store_new( 3, GDK_TYPE_PIXBUF,
 
417
                                    G_TYPE_STRING,
 
418
                                    G_TYPE_STRING,
 
419
                                    G_TYPE_STRING );
 
420
        if( actions )
 
421
        {
 
422
            for( action = actions; *action; ++action )
 
423
            {
 
424
                VFSAppDesktop* desktop;
 
425
                GdkPixbuf* icon;
 
426
                desktop = vfs_app_desktop_new( *action );
 
427
                gtk_list_store_append( model, &it );
 
428
                icon = vfs_app_desktop_get_icon(desktop, 20, TRUE);
 
429
                gtk_list_store_set( model, &it,
 
430
                                    0, icon,
 
431
                                    1, vfs_app_desktop_get_disp_name(desktop),
 
432
                                    2, *action, -1 );
 
433
                if( icon )
 
434
                    gdk_pixbuf_unref( icon );
 
435
                vfs_app_desktop_unref( desktop );
 
436
            }
 
437
        }
 
438
        else
 
439
        {
 
440
            g_object_set_data( G_OBJECT(open_with), "prev_sel", GINT_TO_POINTER(-1) );
 
441
        }
 
442
 
 
443
        /* separator */
 
444
        gtk_list_store_append( model, &it );
 
445
 
 
446
        gtk_list_store_append( model, &it );
 
447
        gtk_list_store_set( model, &it,
 
448
                            0, NULL,
 
449
                            1, _("Other program..."), -1 );
 
450
        gtk_combo_box_set_model( GTK_COMBO_BOX(open_with),
 
451
                                 GTK_TREE_MODEL(model) );
 
452
        gtk_combo_box_set_row_separator_func(
 
453
                GTK_COMBO_BOX(open_with), combo_sep,
 
454
                NULL, NULL );
 
455
        gtk_combo_box_set_active(GTK_COMBO_BOX(open_with), 0);
 
456
        g_signal_connect( open_with, "changed",
 
457
                          G_CALLBACK(on_combo_change), mime );
 
458
 
 
459
        /* vfs_mime_type_unref( mime ); */
 
460
        /* We can unref mime when combo box gets destroyed */
 
461
        g_object_weak_ref( G_OBJECT(open_with),
 
462
                           (GWeakNotify)vfs_mime_type_unref, mime );
 
463
    }
 
464
 
 
465
    /* Multiple files are selected */
 
466
    if ( sel_files && sel_files->next )
 
467
    {
 
468
        gtk_widget_set_sensitive( name, FALSE );
 
469
        gtk_entry_set_text( GTK_ENTRY( name ), multiple_files );
 
470
 
 
471
        gtk_label_set_text( GTK_LABEL( mtime ), multiple_files );
 
472
        gtk_label_set_text( GTK_LABEL( atime ), multiple_files );
 
473
 
 
474
        for ( i = 0; i < N_CHMOD_ACTIONS; ++i )
 
475
        {
 
476
            gtk_toggle_button_set_inconsistent ( data->chmod_btns[ i ], TRUE );
 
477
            data->chmod_states[ i ] = 2; /* Don't touch this bit */
 
478
            g_signal_connect( G_OBJECT( data->chmod_btns[ i ] ), "toggled",
 
479
                              G_CALLBACK( on_chmod_btn_toggled ), data );
 
480
        }
 
481
    }
 
482
    else
 
483
    {
 
484
        /* special processing for files with special display names */
 
485
        if( vfs_file_info_is_desktop_entry( file ) )
 
486
        {
 
487
            char* disp_name = g_filename_display_name( file->name );
 
488
            gtk_entry_set_text( GTK_ENTRY( name ),
 
489
                                disp_name );
 
490
            g_free( disp_name );
 
491
        }
 
492
        else
 
493
            gtk_entry_set_text( GTK_ENTRY( name ),
 
494
                                vfs_file_info_get_disp_name( file ) );
 
495
 
 
496
        gtk_editable_set_editable ( GTK_EDITABLE( name ), FALSE );
 
497
 
 
498
        if ( ! vfs_file_info_is_dir( file ) )
 
499
        {
 
500
            /* Only single "file" is selected, so we don't need to
 
501
                caculate total file size */
 
502
            need_calc_size = FALSE;
 
503
 
 
504
            sprintf( buf, "%s  (%llu Bytes)",
 
505
                     vfs_file_info_get_disp_size( file ),
 
506
                     ( guint64 ) vfs_file_info_get_size( file ) );
 
507
            gtk_label_set_text( data->total_size_label, buf );
 
508
 
 
509
            vfs_file_size_to_string( buf2,
 
510
                                 vfs_file_info_get_blocks( file ) * 512 );
 
511
            sprintf( buf, "%s  (%llu Bytes)", buf2,
 
512
                     ( guint64 ) vfs_file_info_get_blocks( file ) * 512 );
 
513
            gtk_label_set_text( data->size_on_disk_label, buf );
 
514
        }
 
515
        gtk_label_set_text( GTK_LABEL( mtime ),
 
516
                            vfs_file_info_get_disp_mtime( file ) );
 
517
 
 
518
        strftime( buf, sizeof( buf ),
 
519
                  time_format, localtime( vfs_file_info_get_atime( file ) ) );
 
520
        gtk_label_set_text( GTK_LABEL( atime ), buf );
 
521
 
 
522
        owner_group = (char *) vfs_file_info_get_disp_owner( file );
 
523
        tmp = strchr( owner_group, ':' );
 
524
        data->owner_name = g_strndup( owner_group, tmp - owner_group );
 
525
        gtk_entry_set_text( GTK_ENTRY( data->owner ), data->owner_name );
 
526
        data->group_name = g_strdup( tmp + 1 );
 
527
        gtk_entry_set_text( GTK_ENTRY( data->group ), data->group_name );
 
528
 
 
529
        for ( i = 0; i < N_CHMOD_ACTIONS; ++i )
 
530
        {
 
531
            if ( data->chmod_states[ i ] != 2 ) /* allow to touch this bit */
 
532
            {
 
533
                data->chmod_states[ i ] = ( vfs_file_info_get_mode( file ) & chmod_flags[ i ] ? 1 : 0 );
 
534
                gtk_toggle_button_set_active( data->chmod_btns[ i ], data->chmod_states[ i ] );
 
535
            }
 
536
        }
 
537
    }
 
538
 
 
539
    if ( need_calc_size )
 
540
    {
 
541
        /* The total file size displayed in "File Properties" is not
 
542
           completely calculated yet. So "Calculating..." is displayed. */
 
543
        calculating = _( "Calculating..." );
 
544
        gtk_label_set_text( data->total_size_label, calculating );
 
545
        gtk_label_set_text( data->size_on_disk_label, calculating );
 
546
 
 
547
        g_object_set_data( G_OBJECT( dlg ), "calc_size", data );
 
548
        data->calc_size_thread = g_thread_create ( ( GThreadFunc ) calc_size,
 
549
                                                   data, TRUE, NULL );
 
550
        data->update_label_timer = g_timeout_add( 250,
 
551
                                                  ( GSourceFunc ) on_update_labels,
 
552
                                                  data );
 
553
    }
 
554
 
 
555
    g_signal_connect( dlg, "response",
 
556
                        G_CALLBACK(on_dlg_response), dlg );
 
557
    g_signal_connect_swapped( ptk_ui_xml_get_widget(dlg, "ok_button"),
 
558
                        "clicked",
 
559
                        G_CALLBACK(gtk_widget_destroy), dlg );
 
560
    g_signal_connect_swapped( ptk_ui_xml_get_widget(dlg, "cancel_button"),
 
561
                        "clicked",
 
562
                        G_CALLBACK(gtk_widget_destroy), dlg );
 
563
 
 
564
    ptk_dialog_fit_small_screen( dlg );
 
565
    return dlg;
 
566
}
 
567
 
 
568
static uid_t uid_from_name( const char* user_name )
 
569
{
 
570
    struct passwd * pw;
 
571
    uid_t uid = -1;
 
572
    const char* p;
 
573
 
 
574
    pw = getpwnam( user_name );
 
575
    if ( pw )
 
576
    {
 
577
        uid = pw->pw_uid;
 
578
    }
 
579
    else
 
580
    {
 
581
        uid = 0;
 
582
        for ( p = user_name; *p; ++p )
 
583
        {
 
584
            if ( !g_ascii_isdigit( *p ) )
 
585
                return -1;
 
586
            uid *= 10;
 
587
            uid += ( *p - '0' );
 
588
        }
 
589
#if 0 /* This is not needed */
 
590
        /* Check the existance */
 
591
        pw = getpwuid( uid );
 
592
        if ( !pw )     /* Invalid uid */
 
593
            return -1;
 
594
#endif
 
595
 
 
596
    }
 
597
    return uid;
 
598
}
 
599
 
 
600
gid_t gid_from_name( const char* group_name )
 
601
{
 
602
    struct group * grp;
 
603
    gid_t gid = -1;
 
604
    const char* p;
 
605
 
 
606
    grp = getgrnam( group_name );
 
607
    if ( grp )
 
608
    {
 
609
        gid = grp->gr_gid;
 
610
    }
 
611
    else
 
612
    {
 
613
        gid = 0;
 
614
        for ( p = group_name; *p; ++p )
 
615
        {
 
616
            if ( !g_ascii_isdigit( *p ) )
 
617
                return -1;
 
618
            gid *= 10;
 
619
            gid += ( *p - '0' );
 
620
        }
 
621
#if 0 /* This is not needed */
 
622
        /* Check the existance */
 
623
        grp = getgrgid( gid );
 
624
        if ( !grp )     /* Invalid gid */
 
625
            return -1;
 
626
#endif
 
627
 
 
628
    }
 
629
    return gid;
 
630
}
 
631
 
 
632
void
 
633
on_dlg_response ( GtkDialog *dialog,
 
634
                                gint response_id,
 
635
                                gpointer user_data )
 
636
{
 
637
    FilePropertiesDialogData * data;
 
638
    PtkFileTask* task;
 
639
    gboolean mod_change;
 
640
    uid_t uid = -1;
 
641
    gid_t gid = -1;
 
642
    const char* owner_name;
 
643
    const char* group_name;
 
644
    int i;
 
645
    GList* l;
 
646
    GList* file_list;
 
647
    char* file_path;
 
648
    GtkWidget* ask_recursive;
 
649
    VFSFileInfo* file;
 
650
 
 
651
    data = ( FilePropertiesDialogData* ) g_object_get_data( G_OBJECT( dialog ),
 
652
                                                            "DialogData" );
 
653
    if ( data )
 
654
    {
 
655
        if ( data->update_label_timer )
 
656
            g_source_remove( data->update_label_timer );
 
657
        data->cancel = TRUE;
 
658
 
 
659
        if ( data->calc_size_thread )
 
660
            g_thread_join( data->calc_size_thread );
 
661
 
 
662
        if ( response_id == GTK_RESPONSE_OK )
 
663
        {
 
664
            GtkWidget* open_with;
 
665
 
 
666
            /* Set default action for mimetype */
 
667
            if( ( open_with = ptk_ui_xml_get_widget( GTK_WIDGET(dialog), "open_with" ) ) )
 
668
            {
 
669
                GtkTreeModel* model = gtk_combo_box_get_model( GTK_COMBO_BOX(open_with) );
 
670
                GtkTreeIter it;
 
671
 
 
672
                if( model && gtk_combo_box_get_active_iter( GTK_COMBO_BOX(open_with), &it ) )
 
673
                {
 
674
                    char* action;
 
675
                    gtk_tree_model_get( model, &it, 2, &action, -1 );
 
676
                    if( action )
 
677
                    {
 
678
                        file = ( VFSFileInfo* ) data->file_list->data;
 
679
                        VFSMimeType* mime = vfs_file_info_get_mime_type( file );
 
680
                        vfs_mime_type_set_default_action( mime, action );
 
681
                        vfs_mime_type_unref( mime );
 
682
                        g_free( action );
 
683
                    }
 
684
                }
 
685
            }
 
686
 
 
687
            /* Check if we need chown */
 
688
            owner_name = gtk_entry_get_text( data->owner );
 
689
            if ( owner_name && *owner_name &&
 
690
                 (!data->owner_name || strcmp( owner_name, data->owner_name )) )
 
691
            {
 
692
                uid = uid_from_name( owner_name );
 
693
                if ( uid == -1 )
 
694
                {
 
695
                    ptk_show_error( GTK_WINDOW( dialog ), _("Error"), _( "Invalid User" ) );
 
696
                    return ;
 
697
                }
 
698
            }
 
699
            group_name = gtk_entry_get_text( data->group );
 
700
            if ( group_name && *group_name &&
 
701
                 (!data->group_name || strcmp( group_name, data->group_name )) )
 
702
            {
 
703
                gid = gid_from_name( group_name );
 
704
                if ( gid == -1 )
 
705
                {
 
706
                    ptk_show_error( GTK_WINDOW( dialog ), _("Error"), _( "Invalid Group" ) );
 
707
                    return ;
 
708
                }
 
709
            }
 
710
 
 
711
            for ( i = 0; i < N_CHMOD_ACTIONS; ++i )
 
712
            {
 
713
                if ( gtk_toggle_button_get_inconsistent( data->chmod_btns[ i ] ) )
 
714
                {
 
715
                    data->chmod_states[ i ] = 2;  /* Don't touch this bit */
 
716
                }
 
717
                else if ( data->chmod_states[ i ] != gtk_toggle_button_get_active( data->chmod_btns[ i ] ) )
 
718
                {
 
719
                    mod_change = TRUE;
 
720
                    data->chmod_states[ i ] = gtk_toggle_button_get_active( data->chmod_btns[ i ] );
 
721
                }
 
722
                else /* Don't change this bit */
 
723
                {
 
724
                    data->chmod_states[ i ] = 2;
 
725
                }
 
726
            }
 
727
 
 
728
            if ( uid != -1 || gid != -1 || mod_change )
 
729
            {
 
730
                file_list = NULL;
 
731
                for ( l = data->file_list; l; l = l->next )
 
732
                {
 
733
                    file = ( VFSFileInfo* ) l->data;
 
734
                    file_path = g_build_filename( data->dir_path,
 
735
                            vfs_file_info_get_name( file ), NULL );
 
736
                    file_list = g_list_prepend( file_list, file_path );
 
737
                }
 
738
 
 
739
                task = ptk_file_task_new( VFS_FILE_TASK_CHMOD_CHOWN,
 
740
                                          file_list,
 
741
                                          NULL,
 
742
                                          GTK_WINDOW(gtk_widget_get_parent( GTK_WIDGET( dialog ) )) );
 
743
 
 
744
                for ( l = data->file_list; l; l = l->next )
 
745
                {
 
746
                    file = ( VFSFileInfo* ) l->data;
 
747
                    if ( vfs_file_info_is_dir( file ) )
 
748
                    {
 
749
                        ask_recursive = gtk_message_dialog_new(
 
750
                                            GTK_WINDOW( data->dlg ),
 
751
                                            GTK_DIALOG_MODAL,
 
752
                                            GTK_MESSAGE_QUESTION,
 
753
                                            GTK_BUTTONS_YES_NO,
 
754
                                            _( "Do you want to recursively apply these changes to all files and sub-folders?" ) );
 
755
                        ptk_file_task_set_recursive( task,
 
756
                                ( GTK_RESPONSE_YES == gtk_dialog_run( GTK_DIALOG( ask_recursive ) ) ) );
 
757
                        gtk_widget_destroy( ask_recursive );
 
758
                        break;
 
759
                    }
 
760
                }
 
761
 
 
762
                if ( mod_change )
 
763
                {
 
764
                     /* If the permissions of file has been changed by the user */
 
765
                    ptk_file_task_set_chmod( task, data->chmod_states );
 
766
                }
 
767
                /* For chown */
 
768
                ptk_file_task_set_chown( task, uid, gid );
 
769
                ptk_file_task_run( task );
 
770
 
 
771
                /*
 
772
                * This file list will be freed by file operation, so we don't
 
773
                * need to do this. Just set the pointer to NULL.
 
774
                */
 
775
                data->file_list = NULL;
 
776
            }
 
777
        }
 
778
 
 
779
        g_free( data->owner_name );
 
780
        g_free( data->group_name );
 
781
        /*
 
782
         *NOTE: File operation chmod/chown will free the list when it's done,
 
783
         *and we only need to free it when there is no file operation applyed.
 
784
        */
 
785
        g_slice_free( FilePropertiesDialogData, data );
 
786
    }
 
787
 
 
788
    gtk_widget_destroy( GTK_WIDGET( dialog ) );
 
789
}
 
790