· 8 years ago · May 27, 2018, 05:20 PM
1/*
2* C Implementation: ptk-file-task
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#include <glib.h>
14#include <glib/gi18n.h>
15#include "glib-mem.h"
16#include <sys/types.h>
17#include <sys/stat.h>
18#include <string.h>
19
20#include "ptk-file-task.h"
21#include "ptk-utils.h"
22
23static gboolean open_up_progress_dlg( PtkFileTask* task );
24
25static void on_vfs_file_task_progress_cb( VFSFileTask* task,
26 int percent,
27 const char* src_file,
28 const char* dest_file,
29 gpointer user_data );
30
31static gboolean on_vfs_file_task_state_cb( VFSFileTask* task,
32 VFSFileTaskState state,
33 gpointer state_data,
34 gpointer user_data );
35
36static gboolean query_overwrite( PtkFileTask* task, char** new_dest );
37
38PtkFileTask* ptk_file_task_new( VFSFileTaskType type,
39 GList* src_files,
40 const char* dest_dir,
41 GtkWindow* parent_window )
42{
43 PtkFileTask * task = g_slice_new0( PtkFileTask );
44 task->task = vfs_task_new( type, src_files, dest_dir );
45 vfs_file_task_set_progress_callback( task->task,
46 on_vfs_file_task_progress_cb,
47 task );
48 vfs_file_task_set_state_callback( task->task,
49 on_vfs_file_task_state_cb, task );
50 task->parent_window = parent_window;
51 return task;
52}
53
54void ptk_file_task_destroy( PtkFileTask* task )
55{
56 if ( task->task )
57 vfs_file_task_free( task->task );
58 if ( task->progress_dlg )
59 gtk_widget_destroy( task->progress_dlg );
60 if ( task->timeout )
61 g_source_remove( task->timeout );
62 g_slice_free( PtkFileTask, task );
63}
64
65void ptk_file_task_set_complete_notify( PtkFileTask* task,
66 GFunc callback,
67 gpointer user_data )
68{
69 task->complete_notify = callback;
70 task->user_data = user_data;
71}
72
73void ptk_file_task_run( PtkFileTask* task )
74{
75 task->timeout = g_timeout_add( 500,
76 ( GSourceFunc ) open_up_progress_dlg, task );
77 vfs_file_task_run( task->task );
78}
79
80void ptk_file_task_cancel( PtkFileTask* task )
81{
82 vfs_file_task_abort( task->task );
83}
84
85void on_progress_dlg_response( GtkDialog* dlg, int response, PtkFileTask* task )
86{
87 switch ( response )
88 {
89 case GTK_RESPONSE_CANCEL:
90 case GTK_RESPONSE_NONE:
91 vfs_file_task_try_abort( task->task );
92 break;
93 }
94}
95
96gboolean open_up_progress_dlg( PtkFileTask* task )
97{
98 GtkTable* table;
99 GtkLabel* label;
100
101 const char * actions[] =
102 {
103 N_( "Move: " ),
104 N_( "Copy: " ),
105 N_( "Move to Trash: " ),
106 N_( "Delete: " ),
107 N_( "Link: " ),
108 N_( "Change Properties: " )
109 };
110 const char* titles[] =
111 {
112 N_( "Moving..." ),
113 N_( "Copying..." ),
114 N_( "Moving to Trash..." ),
115 N_( "Deleting..." ),
116 N_( "Linking..." ),
117 N_( "Changing Properties" )
118 };
119
120 gdk_threads_enter();
121
122 g_source_remove( task->timeout );
123 task->timeout = 0;
124
125 task->progress_dlg = gtk_dialog_new_with_buttons(
126 _( titles[ task->task->type ] ),
127 task->parent_window,
128 0, GTK_STOCK_CANCEL,
129 GTK_RESPONSE_CANCEL, NULL );
130
131 table = GTK_TABLE(gtk_table_new( 4, 2, FALSE ));
132 gtk_container_set_border_width( GTK_CONTAINER ( table ), 4 );
133 gtk_table_set_row_spacings( table, 4 );
134 gtk_table_set_col_spacings( table, 4 );
135
136 /* From: */
137 label = GTK_LABEL(gtk_label_new( _( actions[ task->task->type ] ) ));
138 gtk_misc_set_alignment( GTK_MISC ( label ), 0, 0.5 );
139 gtk_table_attach( table,
140 GTK_WIDGET(label),
141 0, 1, 0, 1, GTK_FILL, 0, 0, 0 );
142 task->from = GTK_LABEL(gtk_label_new( task->task->current_file ));
143 gtk_misc_set_alignment( GTK_MISC ( task->from ), 0, 0.5 );
144 gtk_label_set_ellipsize( task->from, PANGO_ELLIPSIZE_MIDDLE );
145 gtk_table_attach( table,
146 GTK_WIDGET( task->from ),
147 1, 2, 0, 1, GTK_FILL, 0, 0, 0 );
148
149 /* To: */
150 if ( task->task->dest_dir )
151 {
152 /* To: <Destination folder>
153 ex. Copy file to..., Move file to...etc. */
154 label = GTK_LABEL(gtk_label_new( _( "To:" ) ));
155 gtk_misc_set_alignment( GTK_MISC ( label ), 0, 0.5 );
156 gtk_table_attach( table,
157 GTK_WIDGET(label),
158 0, 1, 1, 2, GTK_FILL, 0, 0, 0 );
159 task->to = GTK_LABEL(gtk_label_new( task->task->dest_dir ));
160 gtk_misc_set_alignment( GTK_MISC ( task->to ), 0, 0.5 );
161 gtk_label_set_ellipsize( task->to, PANGO_ELLIPSIZE_MIDDLE );
162 gtk_table_attach( table,
163 GTK_WIDGET( task->to ),
164 1, 2, 1, 2, GTK_FILL, 0, 0, 0 );
165 }
166
167 /* Processing: */
168 /* Processing: <Name of currently proccesed file> */
169 label = GTK_LABEL(gtk_label_new( _( "Processing:" ) ));
170 gtk_misc_set_alignment( GTK_MISC ( label ), 0, 0.5 );
171 gtk_table_attach( table,
172 GTK_WIDGET(label),
173 0, 1, 2, 3, GTK_FILL, 0, 0, 0 );
174
175 /* Preparing to do some file operation (Copy, Move, Delete...) */
176 task->current = GTK_LABEL(gtk_label_new( _( "Preparing..." ) ));
177 gtk_label_set_ellipsize( task->current, PANGO_ELLIPSIZE_MIDDLE );
178 gtk_misc_set_alignment( GTK_MISC ( task->current ), 0, 0.5 );
179 gtk_table_attach( table,
180 GTK_WIDGET( task->current ),
181 1, 2, 2, 3, GTK_FILL, 0, 0, 0 );
182
183 /* Progress: */
184 label = GTK_LABEL(gtk_label_new( _( "Progress:" ) ));
185 gtk_misc_set_alignment( GTK_MISC ( label ), 0, 0.5 );
186 gtk_table_attach( table,
187 GTK_WIDGET(label),
188 0, 1, 3, 4, GTK_FILL, 0, 0, 0 );
189 task->progress = GTK_PROGRESS_BAR(gtk_progress_bar_new());
190 gtk_table_attach( table,
191 GTK_WIDGET( task->progress ),
192 1, 2, 3, 4, GTK_FILL | GTK_EXPAND, 0, 0, 0 );
193
194 gtk_box_pack_start( GTK_BOX( GTK_DIALOG( task->progress_dlg ) ->vbox ),
195 GTK_WIDGET( table ),
196 TRUE, TRUE, 0 );
197
198 gtk_window_set_default_size( GTK_WINDOW( task->progress_dlg ),
199 400, -1 );
200 gtk_button_box_set_layout ( GTK_BUTTON_BOX ( GTK_DIALOG( task->progress_dlg ) ->action_area ),
201 GTK_BUTTONBOX_END );
202 gtk_window_set_type_hint ( GTK_WINDOW ( task->progress_dlg ),
203 GDK_WINDOW_TYPE_HINT_DIALOG );
204 gtk_window_set_gravity ( GTK_WINDOW ( task->progress_dlg ),
205 GDK_GRAVITY_NORTH_EAST );
206
207 g_signal_connect( task->progress_dlg, "response",
208 G_CALLBACK( on_progress_dlg_response ), task );
209 gtk_widget_show_all( task->progress_dlg );
210
211 gdk_threads_leave();
212
213 return TRUE;
214}
215
216void ptk_file_task_set_chmod( PtkFileTask* task,
217 guchar* chmod_actions )
218{
219 vfs_file_task_set_chmod( task->task, chmod_actions );
220}
221
222void ptk_file_task_set_chown( PtkFileTask* task,
223 uid_t uid, gid_t gid )
224{
225 vfs_file_task_set_chown( task->task, uid, gid );
226}
227
228void ptk_file_task_set_recursive( PtkFileTask* task, gboolean recursive )
229{
230 vfs_file_task_set_recursive( task->task, recursive );
231}
232
233void on_vfs_file_task_progress_cb( VFSFileTask* task,
234 int percent,
235 const char* src_file,
236 const char* dest_file,
237 gpointer user_data )
238{
239 PtkFileTask * data = ( PtkFileTask* ) user_data;
240 char* ufile_path;
241 char percent_str[ 16 ];
242
243 if ( ! data->progress_dlg )
244 return ;
245
246 gdk_threads_enter();
247
248 /* update current src file */
249 if ( data->old_src_file != src_file )
250 {
251 data->old_src_file = src_file;
252 ufile_path = g_filename_display_name( src_file );
253 gtk_label_set_text( data->current, ufile_path );
254 g_free( ufile_path );
255 }
256
257 /* update current dest file */
258 if ( data->old_dest_file != dest_file )
259 {
260 data->old_dest_file = dest_file;
261 ufile_path = g_filename_display_name( dest_file );
262 gtk_label_set_text( data->to, ufile_path );
263 g_free( ufile_path );
264 }
265
266 /* update progress */
267 if ( data->old_percent != percent )
268 {
269 data->old_percent = percent;
270 gtk_progress_bar_set_fraction( data->progress,
271 ( ( gdouble ) percent ) / 100 );
272 g_snprintf( percent_str, 16, "%d %%", percent );
273 gtk_progress_bar_set_text( data->progress, percent_str );
274 }
275
276 gdk_threads_leave();
277}
278
279gboolean on_vfs_file_task_state_cb( VFSFileTask* task,
280 VFSFileTaskState state,
281 gpointer state_data,
282 gpointer user_data )
283{
284 PtkFileTask* data = ( PtkFileTask* ) user_data;
285 GtkWidget* dlg;
286 int response;
287 gboolean ret = TRUE;
288 char** new_dest;
289
290 gdk_threads_enter();
291
292 switch ( state )
293 {
294 case VFS_FILE_TASK_FINISH:
295 if ( data->timeout )
296 {
297 g_source_remove( data->timeout );
298 data->timeout = 0;
299 }
300 if ( data->progress_dlg )
301 {
302 gtk_widget_destroy( data->progress_dlg );
303 data->progress_dlg = NULL;
304 }
305 if ( data->complete_notify )
306 data->complete_notify( task, data->user_data );
307 vfs_file_task_free( data->task );
308 data->task = NULL;
309 ptk_file_task_destroy( data );
310 break;
311 case VFS_FILE_TASK_QUERY_OVERWRITE:
312 new_dest = ( char** ) state_data;
313 ret = query_overwrite( data, new_dest );
314 break;
315 case VFS_FILE_TASK_QUERY_ABORT:
316 dlg = gtk_message_dialog_new( GTK_WINDOW( data->progress_dlg ),
317 GTK_DIALOG_MODAL,
318 GTK_MESSAGE_QUESTION,
319 GTK_BUTTONS_YES_NO,
320 _( "Cancel the operation?" ) );
321 response = gtk_dialog_run( GTK_DIALOG( dlg ) );
322 gtk_widget_destroy( dlg );
323 ret = ( response != GTK_RESPONSE_YES );
324 break;
325 case VFS_FILE_TASK_ERROR:
326 if( data->timeout )
327 {
328 g_source_remove( data->timeout );
329 data->timeout = 0;
330 }
331 ptk_show_error( data->progress_dlg ? GTK_WINDOW(data->progress_dlg) : data->parent_window,
332 _("Error"),
333 state_data ? (char*)state_data : g_strerror( task->error ) );
334 ret = FALSE; /* abort */
335 break;
336 /* FIXME:
337 VFS_FILE_TASK_RUNNING, VFS_FILE_TASK_ABORTED not handled */
338 default:
339 break;
340 }
341
342 gdk_threads_leave();
343
344 return ret; /* return TRUE to continue */
345}
346
347
348enum{
349 RESPONSE_OVERWRITE = 1 << 0,
350 RESPONSE_OVERWRITEALL = 1 << 1,
351 RESPONSE_RENAME = 1 << 2,
352 RESPONSE_SKIP = 1 << 3,
353 RESPONSE_SKIPALL = 1 << 4
354};
355
356static void on_file_name_entry_changed( GtkEntry* entry, GtkDialog* dlg )
357{
358 const char * old_name;
359 gboolean can_rename;
360 const char* new_name = gtk_entry_get_text( entry );
361
362 old_name = ( const char* ) g_object_get_data( G_OBJECT( entry ), "old_name" );
363 can_rename = new_name && ( 0 != strcmp( new_name, old_name ) );
364
365 gtk_dialog_set_response_sensitive ( dlg, RESPONSE_RENAME, can_rename );
366 gtk_dialog_set_response_sensitive ( dlg, RESPONSE_OVERWRITE, !can_rename );
367 gtk_dialog_set_response_sensitive ( dlg, RESPONSE_OVERWRITEALL, !can_rename );
368}
369
370gboolean query_overwrite( PtkFileTask* task, char** new_dest )
371{
372 const char * message;
373 const char* question;
374 GtkWidget* dlg;
375 GtkWidget* parent_win;
376 GtkEntry* entry;
377
378 int response;
379 gboolean has_overwrite_btn = TRUE;
380 char* udest_file;
381 char* file_name;
382 char* ufile_name;
383 char* dir_name;
384 gboolean different_files, is_src_dir, is_dest_dir;
385 struct stat src_stat, dest_stat;
386 gboolean restart_timer = FALSE;
387
388 different_files = ( 0 != strcmp( task->task->current_file,
389 task->task->current_dest ) );
390
391 lstat( task->task->current_file, &src_stat );
392 lstat( task->task->current_dest, &dest_stat );
393
394 is_src_dir = !!S_ISDIR( dest_stat.st_mode );
395 is_dest_dir = !!S_ISDIR( src_stat.st_mode );
396
397 if ( task->timeout )
398 {
399 g_source_remove( task->timeout );
400 task->timeout = 0;
401 restart_timer = TRUE;
402 }
403
404 if ( different_files && is_dest_dir == is_src_dir )
405 {
406 if ( is_dest_dir )
407 {
408 /* Ask the user whether to overwrite dir content or not */
409 question = _( "Do you want to overwrite the folder and its content?" );
410 }
411 else
412 {
413 /* Ask the user whether to overwrite the file or not */
414 question = _( "Do you want to overwrite this file?" );
415 }
416 }
417 else
418 { /* Rename is required */
419 question = _( "Please choose a new file name." );
420 has_overwrite_btn = FALSE;
421 }
422
423 if ( different_files )
424 {
425 /* Ths first %s is a file name, and the second one represents followed message.
426 ex: "/home/pcman/some_file" has existed.\n\nDo you want to overwrite existing file?"
427 --should not be said in past tense */
428 message = _( "\"%s\" already exists.\n\n%s" );
429 }
430 else
431 {
432 /* Ths first %s is a file name, and the second one represents followed message. */
433 message = _( "\"%s\"\n\nDestination and source are the same file.\n\n%s" );
434 }
435
436 udest_file = g_filename_display_name( task->task->current_dest );
437 parent_win = task->progress_dlg ? task->progress_dlg : GTK_WIDGET(task->parent_window);
438 dlg = gtk_message_dialog_new( GTK_WINDOW( parent_win ),
439 GTK_DIALOG_MODAL,
440 GTK_MESSAGE_QUESTION,
441 GTK_BUTTONS_NONE,
442 message,
443 udest_file,
444 question );
445 g_free( udest_file );
446
447 if ( has_overwrite_btn )
448 {
449 gtk_dialog_add_buttons ( GTK_DIALOG( dlg ),
450 _( "Overwrite" ), RESPONSE_OVERWRITE,
451 _( "Overwrite All" ), RESPONSE_OVERWRITEALL,
452 NULL );
453 }
454
455 gtk_dialog_add_buttons ( GTK_DIALOG( dlg ),
456 _( "Rename" ), RESPONSE_RENAME,
457 _( "Skip" ), RESPONSE_SKIP,
458 _( "Skip All" ), RESPONSE_SKIPALL,
459 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
460 NULL );
461
462 file_name = g_path_get_basename( task->task->current_dest );
463 ufile_name = g_filename_display_name( file_name );
464 g_free( file_name );
465
466 entry = ( GtkEntry* ) gtk_entry_new();
467 g_object_set_data_full( G_OBJECT( entry ), "old_name",
468 ufile_name, g_free );
469 g_signal_connect( G_OBJECT( entry ), "changed",
470 G_CALLBACK( on_file_name_entry_changed ), dlg );
471
472 gtk_entry_set_text( entry, ufile_name );
473
474 gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dlg ) ->vbox ), GTK_WIDGET( entry ),
475 FALSE, TRUE, 4 );
476
477 gtk_widget_show_all( dlg );
478 response = gtk_dialog_run( GTK_DIALOG( dlg ) );
479
480 switch ( response )
481 {
482 case RESPONSE_OVERWRITEALL:
483 vfs_file_task_set_overwrite_mode( task->task, VFS_FILE_TASK_OVERWRITE_ALL );
484 break;
485 case RESPONSE_OVERWRITE:
486 vfs_file_task_set_overwrite_mode( task->task, VFS_FILE_TASK_OVERWRITE );
487 break;
488 case RESPONSE_SKIPALL:
489 vfs_file_task_set_overwrite_mode( task->task, VFS_FILE_TASK_SKIP_ALL );
490 break;
491 case RESPONSE_SKIP:
492 vfs_file_task_set_overwrite_mode( task->task, VFS_FILE_TASK_SKIP );
493 break;
494 case RESPONSE_RENAME:
495 vfs_file_task_set_overwrite_mode( task->task, VFS_FILE_TASK_RENAME );
496 file_name = g_filename_from_utf8( gtk_entry_get_text( entry ),
497 - 1, NULL, NULL, NULL );
498 if ( file_name )
499 {
500 dir_name = g_path_get_dirname( task->task->current_dest );
501 *new_dest = g_build_filename( dir_name, file_name, NULL );
502 g_free( file_name );
503 g_free( dir_name );
504 }
505 break;
506 case GTK_RESPONSE_CANCEL:
507 vfs_file_task_abort( task->task );
508 break;
509 }
510 gtk_widget_destroy( dlg );
511 if( restart_timer )
512 {
513 task->timeout = g_timeout_add( 500,
514 ( GSourceFunc ) open_up_progress_dlg,
515 ( gpointer ) task );
516 }
517 return response != GTK_RESPONSE_CANCEL;
518}