void on_mount_added(GVolumeMonitor* vm, GMount* mount, gpointer user_data) { FmPlacesModel* model = FM_PLACES_MODEL(user_data); GVolume* vol = g_mount_get_volume(mount); if(vol) { FmPlaceItem *item; GtkTreeIter it; item = find_vol(model, vol, &it); if(item && item->type == FM_PLACES_ITEM_VOL && !item->fi->path) { GtkTreePath* tp; GFile* gf = g_mount_get_root(mount); FmPath* path = fm_path_new_for_gfile(gf); g_debug("mount path: %s", path->name); g_object_unref(gf); fm_file_info_set_path(item->fi, path); if(path) fm_path_unref(path); item->vol_mounted = TRUE; /* inform the view to update mount indicator */ tp = gtk_tree_model_get_path(GTK_TREE_MODEL(model), &it); gtk_tree_model_row_changed(GTK_TREE_MODEL(model), tp, &it); gtk_tree_path_free(tp); } g_object_unref(vol); } }
FmFolder* fm_folder_get_for_gfile (GFile* gf) { FmPath* path = fm_path_new_for_gfile(gf); FmFolder* folder = fm_folder_new_internal(path, gf); fm_path_unref(path); return folder; }
/** * fm_file_info_job_add_gfile * @job: a job to add file * @gf: a file descriptor to add to query list * * Adds a path @gf to query list for the @job. * * This API may only be called before starting the @job. * * Since: 0.1.0 */ void fm_file_info_job_add_gfile(FmFileInfoJob* job, GFile* gf) { FmPath* path = fm_path_new_for_gfile(gf); FmFileInfo* fi = fm_file_info_new(); fm_file_info_set_path(fi, path); fm_path_unref(path); fm_file_info_list_push_tail_noref(job->file_infos, fi); }
/** * fm_dir_list_job_new_for_gfile * @gf: descriptor of directory to get listing * * Creates a new #FmDirListJob for listing of directory @gf. * * Returns: (transfer full): a new #FmDirListJob object. * * Since: 0.1.0 */ FmDirListJob* fm_dir_list_job_new_for_gfile(GFile* gf) { /* FIXME: should we cache this with hash table? Or, the cache * should be done at the level of FmFolder instead? */ FmDirListJob* job = (FmDirListJob*)g_object_new(FM_TYPE_DIR_LIST_JOB, NULL); job->dir_path = fm_path_new_for_gfile(gf); return job; }
static void update_vol(FmPlacesModel* model, FmPlaceItem* item, GtkTreeIter* it, FmFileInfoJob* job) { FmIcon* icon; GIcon* gicon; char* name; GdkPixbuf* pix; GMount* mount; FmPath* path; name = g_volume_get_name(item->vol); if(item->fi->icon) fm_icon_unref(item->fi->icon); gicon = g_volume_get_icon(item->vol); icon = fm_icon_from_gicon(gicon); item->fi->icon = icon; g_object_unref(gicon); mount = g_volume_get_mount(item->vol); if(mount) { GFile* gf = g_mount_get_root(mount); path = fm_path_new_for_gfile(gf); g_object_unref(gf); g_object_unref(mount); item->vol_mounted = TRUE; } else { path = NULL; item->vol_mounted = FALSE; } if(!fm_path_equal(item->fi->path, path)) { fm_file_info_set_path(item->fi, path); if(path) { if(job) fm_file_info_job_add(job, path); else { job = fm_file_info_job_new(NULL, FM_FILE_INFO_JOB_FOLLOW_SYMLINK); model->jobs = g_slist_prepend(model->jobs, job); g_signal_connect(job, "finished", G_CALLBACK(on_file_info_job_finished), model); fm_job_run_async(FM_JOB(job)); } fm_path_unref(path); } } pix = fm_icon_get_pixbuf(item->fi->icon, fm_config->pane_icon_size); gtk_list_store_set(GTK_LIST_STORE(model), it, FM_PLACES_MODEL_COL_ICON, pix, FM_PLACES_MODEL_COL_LABEL, name, -1); g_object_unref(pix); g_free(name); }
void activate_row(FmPlacesView* view, guint button, GtkTreePath* tree_path) { GtkTreeIter it; if(gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &it, tree_path)) { FmPlaceItem* item; FmPath* path; gtk_tree_model_get(GTK_TREE_MODEL(model), &it, FM_PLACES_MODEL_COL_INFO, &item, -1); if(!item) return; switch(item->type) { case FM_PLACES_ITEM_PATH: path = fm_path_ref(item->fi->path); break; case FM_PLACES_ITEM_VOL: { GFile* gf; GMount* mnt = g_volume_get_mount(item->vol); if(!mnt) { GtkWindow* parent = GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(view))); if(!fm_mount_volume(parent, item->vol, TRUE)) return; mnt = g_volume_get_mount(item->vol); if(!mnt) { g_debug("GMount is invalid after successful g_volume_mount().\nThis is quite possibly a gvfs bug.\nSee https://bugzilla.gnome.org/show_bug.cgi?id=552168"); return; } } gf = g_mount_get_root(mnt); g_object_unref(mnt); if(gf) { path = fm_path_new_for_gfile(gf); g_object_unref(gf); } else path = NULL; break; } default: return; } if(path) { g_signal_emit(view, signals[CHDIR], 0, button, path); fm_path_unref(path); } } }
/* TODO: support selecting multiple files */ FmPath* fm_select_file(GtkWindow* parent, const char* title, const char* default_folder, gboolean local_only, gboolean show_preview, /* filter1, filter2, ..., NULL */ ...) { FmPath* path; GtkFileChooser* chooser; GtkFileFilter* filter; gulong handler_id = 0; va_list args; chooser = (GtkFileChooser*)gtk_file_chooser_dialog_new( title, parent, GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); gtk_dialog_set_alternative_button_order(GTK_DIALOG(chooser), GTK_RESPONSE_CANCEL, GTK_RESPONSE_OK, NULL); if(local_only) gtk_file_chooser_set_local_only(chooser, TRUE); if(default_folder) gtk_file_chooser_set_current_folder(chooser, default_folder); va_start(args, show_preview); while((filter = va_arg(args, GtkFileFilter*))) { gtk_file_chooser_add_filter(chooser, filter); } va_end (args); if(show_preview) handler_id = fm_add_image_preview_to_file_chooser(chooser); if(gtk_dialog_run(GTK_DIALOG(chooser)) == GTK_RESPONSE_OK) { GFile* file = gtk_file_chooser_get_file(chooser); path = fm_path_new_for_gfile(file); g_object_unref(file); } else path = NULL; if(handler_id > 0) g_signal_handler_disconnect(chooser, handler_id); gtk_widget_destroy(GTK_WIDGET(chooser)); return path; }
static void on_dlg_response(GtkDialog* dlg, int res, gpointer user_data) { AutoRun* data = (AutoRun*)user_data; /* stop the detection */ g_cancellable_cancel(data->cancel); if(res == GTK_RESPONSE_OK) { GtkTreeModel* model; GtkTreeSelection* sel = gtk_tree_view_get_selection(data->view); GtkTreeIter it; if( gtk_tree_selection_get_selected(sel, &model, &it) ) { GAppInfo* app; gtk_tree_model_get(model, &it, 2, &app, -1); if(app) { GFile* gf = g_mount_get_root(data->mount); GList* filelist = g_list_prepend(NULL, gf); g_app_info_launch(app, filelist, NULL, NULL); g_list_free(filelist); g_object_unref(gf); g_object_unref(app); } else { GFile* gf = g_mount_get_root(data->mount); FmPath* path = fm_path_new_for_gfile(gf); fm_main_win_add_win(NULL, path); fm_path_unref(path); g_object_unref(gf); } } } g_signal_handlers_disconnect_by_func(dlg, on_dlg_response, data); g_signal_handlers_disconnect_by_func(data->view, on_row_activated, data); g_signal_handlers_disconnect_by_func(data->mount, on_unmount, data); gtk_widget_destroy(GTK_WIDGET(dlg)); g_object_unref(data->cancel); g_object_unref(data->store); g_object_unref(data->mount); g_slice_free(AutoRun, data); pcmanfm_unref(); }
void on_row_activated(GtkTreeView* view, GtkTreePath* tree_path, GtkTreeViewColumn *col) { GtkTreeIter it; if(gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &it, tree_path)) { PlaceItem* item; FmPath* path; gtk_tree_model_get(GTK_TREE_MODEL(model), &it, COL_INFO, &item, -1); if(!item) return; switch(item->type) { case PLACE_PATH: path = fm_path_ref(item->path); break; case PLACE_VOL: { GFile* gf; GMount* mnt = g_volume_get_mount(item->vol); if(!mnt) { if(!fm_mount_volume(NULL, item->vol)) return; mnt = g_volume_get_mount(item->vol); } gf = g_mount_get_root(mnt); g_object_unref(mnt); path = fm_path_new_for_gfile(gf); g_object_unref(gf); break; } default: return; } g_signal_emit(view, signals[CHDIR], 0, path); fm_path_unref(path); } }
/** * fm_select_folder * @parent: a window to place dialog over it * @title: title for dialog window * * Presents the message to user and lets him/her to select a folder. * Returned data should be freed with fm_path_unref() after usage. * * Before 0.1.16 this call had different arguments. * * Returns: (transfer full): selected folder path or %NULL if dialog was closed. * * Since: 0.1.0 */ FmPath* fm_select_folder(GtkWindow* parent, const char* title) { FmPath* path; GtkFileChooser* chooser; chooser = (GtkFileChooser*)gtk_file_chooser_dialog_new( title ? title : _("Select Folder"), parent, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); gtk_dialog_set_alternative_button_order(GTK_DIALOG(chooser), GTK_RESPONSE_CANCEL, GTK_RESPONSE_OK, NULL); if( gtk_dialog_run(GTK_DIALOG(chooser)) == GTK_RESPONSE_OK ) { GFile* file = gtk_file_chooser_get_file(chooser); path = fm_path_new_for_gfile(file); g_object_unref(file); } else path = NULL; gtk_widget_destroy(GTK_WIDGET(chooser)); return path; }
static gboolean fm_dir_list_job_run_gio(FmDirListJob* job) { GFileEnumerator *enu; GFileInfo *inf; FmFileInfo* fi; GError *err = NULL; FmJob* fmjob = FM_JOB(job); GFile* gf; const char* query; gf = fm_path_to_gfile(job->dir_path); _retry: inf = g_file_query_info(gf, gfile_info_query_attribs, 0, fm_job_get_cancellable(fmjob), &err); if(!inf ) { FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE); g_error_free(err); if( act == FM_JOB_RETRY ) { err = NULL; goto _retry; } else { g_object_unref(gf); return FALSE; } } if( g_file_info_get_file_type(inf) != G_FILE_TYPE_DIRECTORY) { char *path_str = fm_path_to_str(job->dir_path); err = g_error_new(G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY, _("The specified directory '%s' is not valid"), path_str); fm_job_emit_error(fmjob, err, FM_JOB_ERROR_CRITICAL); g_free(path_str); g_error_free(err); g_object_unref(gf); g_object_unref(inf); return FALSE; } /* check if FS is R/O and set attr. into inf */ _fm_file_info_job_update_fs_readonly(gf, inf, NULL, NULL); job->dir_fi = fm_file_info_new_from_g_file_data(gf, inf, job->dir_path); g_object_unref(inf); if(G_UNLIKELY(job->flags & FM_DIR_LIST_JOB_DIR_ONLY)) { query = G_FILE_ATTRIBUTE_STANDARD_TYPE","G_FILE_ATTRIBUTE_STANDARD_NAME"," G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN","G_FILE_ATTRIBUTE_STANDARD_IS_BACKUP"," G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK","G_FILE_ATTRIBUTE_STANDARD_IS_VIRTUAL"," G_FILE_ATTRIBUTE_STANDARD_EDIT_NAME"," G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME","G_FILE_ATTRIBUTE_STANDARD_ICON"," G_FILE_ATTRIBUTE_STANDARD_SIZE","G_FILE_ATTRIBUTE_STANDARD_TARGET_URI"," "unix::*,time::*,access::*,id::filesystem"; } else query = gfile_info_query_attribs; enu = g_file_enumerate_children (gf, query, 0, fm_job_get_cancellable(fmjob), &err); g_object_unref(gf); if(enu) { while( ! fm_job_is_cancelled(fmjob) ) { inf = g_file_enumerator_next_file(enu, fm_job_get_cancellable(fmjob), &err); if(inf) { FmPath *dir, *sub; GFile *child; if(G_UNLIKELY(job->flags & FM_DIR_LIST_JOB_DIR_ONLY)) { /* FIXME: handle symlinks */ if(g_file_info_get_file_type(inf) != G_FILE_TYPE_DIRECTORY) { g_object_unref(inf); continue; } } /* virtual folders may return children not within them */ dir = fm_path_new_for_gfile(g_file_enumerator_get_container(enu)); if (fm_path_equal(job->dir_path, dir)) sub = fm_path_new_child(job->dir_path, g_file_info_get_name(inf)); else sub = fm_path_new_child(dir, g_file_info_get_name(inf)); child = g_file_get_child(g_file_enumerator_get_container(enu), g_file_info_get_name(inf)); if (g_file_info_get_file_type(inf) == G_FILE_TYPE_DIRECTORY) /* for dir: check if its FS is R/O and set attr. into inf */ _fm_file_info_job_update_fs_readonly(child, inf, NULL, NULL); fi = fm_file_info_new_from_g_file_data(child, inf, sub); fm_path_unref(sub); fm_path_unref(dir); g_object_unref(child); fm_dir_list_job_add_found_file(job, fi); fm_file_info_unref(fi); } else { if(err) { FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MILD); g_error_free(err); /* FM_JOB_RETRY is not supported. */ if(act == FM_JOB_ABORT) fm_job_cancel(fmjob); } /* otherwise it's EOL */ break; } g_object_unref(inf); } g_file_enumerator_close(enu, NULL, &err); g_object_unref(enu); } else { fm_job_emit_error(fmjob, err, FM_JOB_ERROR_CRITICAL); g_error_free(err); return FALSE; } return TRUE; }
static gboolean deep_count_gio(FmDeepCountJob* job, GFileInfo* inf, GFile* gf) { FmJob* fmjob = FM_JOB(job); GError* err = NULL; GFileType type; const char* fs_id; gboolean descend; if(inf) g_object_ref(inf); else { _retry_query_info: inf = g_file_query_info(gf, query_str, (job->flags & FM_DC_JOB_FOLLOW_LINKS) ? 0 : G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, fm_job_get_cancellable(fmjob), &err); if(!inf) { FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MILD); g_error_free(err); err = NULL; if(act == FM_JOB_RETRY) goto _retry_query_info; return FALSE; } } if(fm_job_is_cancelled(fmjob)) { g_object_unref(inf); return FALSE; } type = g_file_info_get_file_type(inf); descend = TRUE; ++job->count; job->total_size += g_file_info_get_size(inf); job->total_ondisk_size += g_file_info_get_attribute_uint64(inf, G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE); /* prepare for moving across different devices */ if( job->flags & FM_DC_JOB_PREPARE_MOVE ) { fs_id = g_file_info_get_attribute_string(inf, G_FILE_ATTRIBUTE_ID_FILESYSTEM); if( g_strcmp0(fs_id, job->dest_fs_id) != 0 ) { /* files on different device requires an additional 'delete' for the source file. */ ++job->total_size; /* this is for the additional delete */ ++job->total_ondisk_size; ++job->count; } else descend = FALSE; } if( type == G_FILE_TYPE_DIRECTORY ) { FmPath* fm_path = fm_path_new_for_gfile(gf); /* check if we need to decends into the dir. */ /* trash:/// doesn't support deleting files recursively */ if(job->flags & FM_DC_JOB_PREPARE_DELETE && fm_path_is_trash(fm_path) && ! fm_path_is_trash_root(fm_path)) descend = FALSE; else { /* only descends into files on the same filesystem */ if( job->flags & FM_DC_JOB_SAME_FS ) { fs_id = g_file_info_get_attribute_string(inf, G_FILE_ATTRIBUTE_ID_FILESYSTEM); descend = (g_strcmp0(fs_id, job->dest_fs_id) == 0); } } fm_path_unref(fm_path); g_object_unref(inf); inf = NULL; if(descend) { GFileEnumerator* enu; _retry_enum_children: enu = g_file_enumerate_children(gf, query_str, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, fm_job_get_cancellable(fmjob), &err); if(enu) { while( !fm_job_is_cancelled(fmjob) ) { inf = g_file_enumerator_next_file(enu, fm_job_get_cancellable(fmjob), &err); if(inf) { GFile* child = g_file_get_child(gf, g_file_info_get_name(inf)); deep_count_gio(job, inf, child); g_object_unref(child); g_object_unref(inf); inf = NULL; } else { if(err) /* error! */ { /* FM_JOB_RETRY is not supported */ /*FmJobErrorAction act = */ fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MILD); g_error_free(err); err = NULL; } else { /* EOF is reached, do nothing. */ break; } } } g_file_enumerator_close(enu, NULL, NULL); g_object_unref(enu); } else { FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MILD); g_error_free(err); err = NULL; if(act == FM_JOB_RETRY) goto _retry_enum_children; } } } else g_object_unref(inf); return TRUE; }
static gboolean _fm_file_ops_job_copy_file(FmFileOpsJob* job, GFile* src, GFileInfo* inf, GFile* dest, FmFolder *src_folder, /* if move */ FmFolder *dest_folder) { gboolean ret = FALSE; gboolean delete_src = FALSE; GError* err = NULL; GFileType type; guint64 size; GFile* new_dest = NULL; GFileCopyFlags flags; FmJob* fmjob = FM_JOB(job); FmPath *fm_dest; guint32 mode; gboolean skip_dir_content = FALSE; /* FIXME: g_file_get_child() failed? generate error! */ g_return_val_if_fail(dest != NULL, FALSE); job->supported_options = FM_FILE_OP_RENAME | FM_FILE_OP_SKIP | FM_FILE_OP_OVERWRITE; if( G_LIKELY(inf) ) g_object_ref(inf); else { _retry_query_src_info: inf = g_file_query_info(src, query, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, fm_job_get_cancellable(fmjob), &err); if( !inf ) { FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE); g_error_free(err); err = NULL; if(act == FM_JOB_RETRY) goto _retry_query_src_info; return FALSE; } } if(!_fm_file_ops_job_check_paths(job, src, inf, dest)) { g_object_unref(inf); return FALSE; } /* if this is a cross-device move operation, delete source files. */ if( job->type == FM_FILE_OP_MOVE ) delete_src = TRUE; /* showing currently processed file. */ fm_file_ops_job_emit_cur_file(job, g_file_info_get_display_name(inf)); type = g_file_info_get_file_type(inf); size = g_file_info_get_size(inf); mode = g_file_info_get_attribute_uint32(inf, G_FILE_ATTRIBUTE_UNIX_MODE); g_object_unref(inf); inf = NULL; switch(type) { case G_FILE_TYPE_DIRECTORY: { GFileEnumerator* enu; gboolean dir_created = FALSE; _retry_mkdir: if( !fm_job_is_cancelled(fmjob) && !job->skip_dir_content && !g_file_make_directory(dest, fm_job_get_cancellable(fmjob), &err) ) { if(err->domain == G_IO_ERROR && (err->code == G_IO_ERROR_EXISTS || err->code == G_IO_ERROR_INVALID_FILENAME || err->code == G_IO_ERROR_FILENAME_TOO_LONG)) { GFile* dest_cp = new_dest; gboolean dest_exists = (err->code == G_IO_ERROR_EXISTS); FmFileOpOption opt = 0; g_error_free(err); err = NULL; new_dest = NULL; opt = _fm_file_ops_job_ask_new_name(job, src, dest, &new_dest, dest_exists); if(!new_dest) /* restoring status quo */ new_dest = dest_cp; else if(dest_cp) /* we got new new_dest, forget old one */ g_object_unref(dest_cp); switch(opt) { case FM_FILE_OP_RENAME: dest = new_dest; goto _retry_mkdir; break; case FM_FILE_OP_SKIP: /* when a dir is skipped, we need to know its total size to calculate correct progress */ job->finished += size; fm_file_ops_job_emit_percent(job); job->skip_dir_content = skip_dir_content = TRUE; dir_created = TRUE; /* pretend that dir creation succeeded */ break; case FM_FILE_OP_OVERWRITE: dir_created = TRUE; /* pretend that dir creation succeeded */ break; case FM_FILE_OP_CANCEL: fm_job_cancel(fmjob); break; case FM_FILE_OP_SKIP_ERROR: ; /* FIXME */ } } else if(!fm_job_is_cancelled(fmjob)) { FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE); g_error_free(err); err = NULL; if(act == FM_JOB_RETRY) goto _retry_mkdir; } job->finished += size; fm_file_ops_job_emit_percent(job); } else { /* chmod the newly created dir properly */ if(!fm_job_is_cancelled(fmjob) && !job->skip_dir_content) { if(mode) { _retry_chmod_for_dir: mode |= (S_IRUSR|S_IWUSR); /* ensure we have rw permission to this file. */ if( !g_file_set_attribute_uint32(dest, G_FILE_ATTRIBUTE_UNIX_MODE, mode, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, fm_job_get_cancellable(fmjob), &err) ) { FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE); g_error_free(err); err = NULL; if(act == FM_JOB_RETRY) goto _retry_chmod_for_dir; /* FIXME: some filesystems may not support this. */ } } dir_created = TRUE; } job->finished += size; fm_file_ops_job_emit_percent(job); } if(!dir_created) /* if target dir is not created, don't copy dir content */ { if(!job->skip_dir_content) job->skip_dir_content = skip_dir_content = TRUE; } /* the dest dir is created. let's copy its content. */ /* FIXME: handle the case when the dir cannot be created. */ else if(!fm_job_is_cancelled(fmjob)) { FmFolder *sub_folder; FmFolder *sub_src = NULL; if (delete_src) { FmPath *src_path = fm_path_new_for_gfile(src); sub_src = fm_folder_find_by_path(src_path); fm_path_unref(src_path); } fm_dest = fm_path_new_for_gfile(dest); sub_folder = fm_folder_find_by_path(fm_dest); /* inform folder we created directory */ if (!dest_folder || !_fm_folder_event_file_added(dest_folder, fm_dest)) fm_path_unref(fm_dest); _retry_enum_children: enu = g_file_enumerate_children(src, query, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, fm_job_get_cancellable(fmjob), &err); if(enu) { int n_children = 0; int n_copied = 0; ret = TRUE; while( !fm_job_is_cancelled(fmjob) ) { inf = g_file_enumerator_next_file(enu, fm_job_get_cancellable(fmjob), &err); if( inf ) { ++n_children; /* don't overwrite dir content, only calculate progress. */ if(G_UNLIKELY(job->skip_dir_content)) { /* FIXME: this is incorrect as we don't do the calculation recursively. */ job->finished += g_file_info_get_size(inf); fm_file_ops_job_emit_percent(job); } else { gboolean ret2; GFile* sub = g_file_get_child(src, g_file_info_get_name(inf)); GFile* sub_dest; char* tmp_basename; if(g_file_is_native(src) == g_file_is_native(dest)) /* both are native or both are virtual */ tmp_basename = NULL; else if(g_file_is_native(src)) /* copy from native to virtual */ tmp_basename = g_filename_to_utf8(g_file_info_get_name(inf), -1, NULL, NULL, NULL); /* gvfs escapes it itself */ else /* copy from virtual to native */ tmp_basename = fm_uri_subpath_to_native_subpath(g_file_info_get_name(inf), NULL); sub_dest = g_file_get_child(dest, tmp_basename ? tmp_basename : g_file_info_get_name(inf)); g_free(tmp_basename); ret2 = _fm_file_ops_job_copy_file(job, sub, inf, sub_dest, sub_src, sub_folder); g_object_unref(sub); g_object_unref(sub_dest); if(ret2) ++n_copied; else ret = FALSE; } g_object_unref(inf); } else { if(err) { fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE); g_error_free(err); err = NULL; /* FM_JOB_RETRY is not supported here */ ret = FALSE; } else /* EOF is reached */ { /* all files are successfully copied. */ if(fm_job_is_cancelled(fmjob)) ret = FALSE; else { if(dir_created) /* target dir is created */ { /* some files are not copied */ if(n_children != n_copied) { /* if the copy actions are skipped deliberately, it's ok */ if(!job->skip_dir_content) ret = FALSE; } } /* else job->skip_dir_content is TRUE */ } break; } } } g_file_enumerator_close(enu, NULL, &err); g_object_unref(enu); } else { FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE); g_error_free(err); err = NULL; if(act == FM_JOB_RETRY) goto _retry_enum_children; } if (sub_src) g_object_unref(sub_src); if (sub_folder) g_object_unref(sub_folder); } if(job->skip_dir_content) delete_src = FALSE; if(skip_dir_content) job->skip_dir_content = FALSE; } break; case G_FILE_TYPE_SPECIAL: /* only handle FIFO for local files */ if(g_file_is_native(src) && g_file_is_native(dest)) { char* src_path = g_file_get_path(src); struct stat src_st; int r; r = lstat(src_path, &src_st); g_free(src_path); if(r == 0) { /* Handle FIFO on native file systems. */ if(S_ISFIFO(src_st.st_mode)) { char* dest_path = g_file_get_path(dest); int r = mkfifo(dest_path, src_st.st_mode); g_free(dest_path); if( r == 0) ret = TRUE; } /* FIXME: how about block device, char device, and socket? */ } } if (!ret) { g_set_error(&err, G_IO_ERROR, G_IO_ERROR_FAILED, _("Cannot copy file '%s': not supported"), g_file_info_get_display_name(inf)); fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE); g_clear_error(&err); } goto _file_copied; default: flags = G_FILE_COPY_ALL_METADATA|G_FILE_COPY_NOFOLLOW_SYMLINKS; _retry_copy: if( !g_file_copy(src, dest, flags, fm_job_get_cancellable(fmjob), progress_cb, fmjob, &err) ) { flags &= ~G_FILE_COPY_OVERWRITE; /* handle existing files or file name conflict */ if(err->domain == G_IO_ERROR && (err->code == G_IO_ERROR_EXISTS || err->code == G_IO_ERROR_INVALID_FILENAME || err->code == G_IO_ERROR_FILENAME_TOO_LONG)) { GFile* dest_cp = new_dest; gboolean dest_exists = (err->code == G_IO_ERROR_EXISTS); FmFileOpOption opt = 0; g_error_free(err); err = NULL; new_dest = NULL; opt = _fm_file_ops_job_ask_new_name(job, src, dest, &new_dest, dest_exists); if(!new_dest) /* restoring status quo */ new_dest = dest_cp; else if(dest_cp) /* we got new new_dest, forget old one */ g_object_unref(dest_cp); switch(opt) { case FM_FILE_OP_RENAME: dest = new_dest; goto _retry_copy; break; case FM_FILE_OP_OVERWRITE: flags |= G_FILE_COPY_OVERWRITE; goto _retry_copy; break; case FM_FILE_OP_CANCEL: fm_job_cancel(fmjob); break; case FM_FILE_OP_SKIP: ret = TRUE; delete_src = FALSE; /* don't delete source file. */ break; case FM_FILE_OP_SKIP_ERROR: ; /* FIXME */ } } else { gboolean is_no_space = (err->domain == G_IO_ERROR && err->code == G_IO_ERROR_NO_SPACE); FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE); g_error_free(err); err = NULL; if(act == FM_JOB_RETRY) { job->current_file_finished = 0; goto _retry_copy; } /* FIXME: ask to leave partial content? */ if(is_no_space) g_file_delete(dest, fm_job_get_cancellable(fmjob), NULL); ret = FALSE; delete_src = FALSE; } } else ret = TRUE; _file_copied: job->finished += size; job->current_file_finished = 0; if(ret && dest_folder) { fm_dest = fm_path_new_for_gfile(dest); if(!_fm_folder_event_file_added(dest_folder, fm_dest)) fm_path_unref(fm_dest); } /* update progress */ fm_file_ops_job_emit_percent(job); break; } /* if this is a cross-device move operation, delete source files. */ /* ret == TRUE means the copy is successful. */ if( !fm_job_is_cancelled(fmjob) && ret && delete_src ) ret = _fm_file_ops_job_delete_file(fmjob, src, inf, src_folder); /* delete the source file. */ if(new_dest) g_object_unref(new_dest); return ret; }
gboolean _fm_file_ops_job_move_file(FmFileOpsJob* job, GFile* src, GFileInfo* inf, GFile* dest, FmPath *src_path, FmFolder *src_folder, FmFolder *dest_folder) { GError* err = NULL; FmJob* fmjob = FM_JOB(job); const char* src_fs_id; gboolean ret = TRUE; GFile* new_dest = NULL; job->supported_options = FM_FILE_OP_RENAME | FM_FILE_OP_SKIP | FM_FILE_OP_OVERWRITE; if( G_LIKELY(inf) ) g_object_ref(inf); else { _retry_query_src_info: inf = g_file_query_info(src, query, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, fm_job_get_cancellable(fmjob), &err); if( !inf ) { FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE); g_error_free(err); err = NULL; if(act == FM_JOB_RETRY) goto _retry_query_src_info; return FALSE; } } if(!_fm_file_ops_job_check_paths(job, src, inf, dest)) { g_object_unref(inf); return FALSE; } src_fs_id = g_file_info_get_attribute_string(inf, G_FILE_ATTRIBUTE_ID_FILESYSTEM); /* Check if source and destination are on the same device */ if( job->type == FM_FILE_OP_UNTRASH || g_strcmp0(src_fs_id, job->dest_fs_id) == 0 ) /* same device */ { guint64 size; GFileCopyFlags flags = G_FILE_COPY_ALL_METADATA|G_FILE_COPY_NOFOLLOW_SYMLINKS; FmPath *fm_dest; fm_dest = fm_path_new_for_gfile(dest); /* showing currently processed file. */ fm_file_ops_job_emit_cur_file(job, g_file_info_get_display_name(inf)); _retry_move: if( !g_file_move(src, dest, flags, fm_job_get_cancellable(fmjob), progress_cb, job, &err)) { flags &= ~G_FILE_COPY_OVERWRITE; if(err->domain == G_IO_ERROR && err->code == G_IO_ERROR_EXISTS) { GFile* dest_cp = new_dest; FmFileOpOption opt = 0; new_dest = NULL; opt = _fm_file_ops_job_ask_new_name(job, src, dest, &new_dest, TRUE); if(!new_dest) /* restoring status quo */ new_dest = dest_cp; else if(dest_cp) /* we got new new_dest, forget old one */ g_object_unref(dest_cp); g_error_free(err); err = NULL; switch(opt) { case FM_FILE_OP_RENAME: dest = new_dest; goto _retry_move; break; case FM_FILE_OP_OVERWRITE: flags |= G_FILE_COPY_OVERWRITE; if(g_file_info_get_file_type(inf) == G_FILE_TYPE_DIRECTORY) /* merge dirs */ { GFileEnumerator* enu = g_file_enumerate_children(src, query, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, fm_job_get_cancellable(fmjob), &err); if(enu) { GFileInfo* child_inf; FmFolder *sub_src_folder = fm_folder_find_by_path(src_path); FmFolder *sub_dest_folder = fm_folder_find_by_path(fm_dest); while(!fm_job_is_cancelled(fmjob)) { child_inf = g_file_enumerator_next_file(enu, fm_job_get_cancellable(fmjob), &err); if(child_inf) { GFile* child = g_file_get_child(src, g_file_info_get_name(child_inf)); GFile* child_dest = g_file_get_child(dest, g_file_info_get_name(child_inf)); FmPath *child_path = fm_path_new_for_gfile(child); _fm_file_ops_job_move_file(job, child, child_inf, child_dest, child_path, sub_src_folder, sub_dest_folder); g_object_unref(child); g_object_unref(child_dest); g_object_unref(child_inf); fm_path_unref(child_path); } else { if(err) /* error */ { fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE); g_error_free(err); err = NULL; } else /* EOF */ { break; } } } if (sub_src_folder) g_object_unref(sub_src_folder); if (sub_dest_folder) g_object_unref(sub_dest_folder); g_object_unref(enu); } else { /*FmJobErrorAction act = */ fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE); g_error_free(err); err = NULL; /* if(act == FM_JOB_RETRY) goto _retry_move; */ } /* remove source dir after its content is merged with destination dir */ if(!g_file_delete(src, fm_job_get_cancellable(fmjob), &err)) { fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE); g_error_free(err); err = NULL; /* FIXME: should this be recoverable? */ } } else /* the destination is a file, just overwrite it. */ goto _retry_move; break; case FM_FILE_OP_CANCEL: fm_job_cancel(fmjob); ret = FALSE; break; case FM_FILE_OP_SKIP: ret = TRUE; break; case FM_FILE_OP_SKIP_ERROR: ; /* FIXME */ } } if(err) { FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE); g_error_free(err); err = NULL; if(act == FM_JOB_RETRY) goto _retry_move; } fm_path_unref(fm_dest); } else { if (src_folder) _fm_folder_event_file_deleted(src_folder, src_path); if (!dest_folder || !_fm_folder_event_file_added(dest_folder, fm_dest)) fm_path_unref(fm_dest); } /* size = g_file_info_get_attribute_uint64(inf, G_FILE_ATTRIBUTE_UNIX_BLOCKS); size *= g_file_info_get_attribute_uint32(inf, G_FILE_ATTRIBUTE_UNIX_BLOCK_SIZE); */ size = g_file_info_get_size(inf); job->finished += size; fm_file_ops_job_emit_percent(job); } else /* use copy if they are on different devices */ { /* use copy & delete */ /* source file will be deleted in _fm_file_ops_job_copy_file() */ ret = _fm_file_ops_job_copy_file(job, src, inf, dest, src_folder, dest_folder); } if(new_dest) g_object_unref(new_dest); g_object_unref(inf); return ret; }