diff --git a/deluge/core/torrent.py b/deluge/core/torrent.py index 832ef9806..e846c5835 100644 --- a/deluge/core/torrent.py +++ b/deluge/core/torrent.py @@ -119,8 +119,10 @@ class Torrent: self.waiting_on_resume_data = False # Keep a list of file indexes we're waiting for file_rename alerts on + # This also includes the old_folder and new_folder to know what signal to send # This is so we can send one folder_renamed signal instead of multiple # file_renamed signals. + # [(old_folder, new_folder, [*indexes]), ...] self.waiting_on_folder_rename = [] # We store the filename just in case we need to make a copy of the torrentfile @@ -824,8 +826,10 @@ class Torrent: if new_folder[-1:] != "/": new_folder += "/" + wait_on_folder = (folder, new_folder, []) for f in self.get_files(): if f["path"].startswith(folder): # Keep a list of filerenames we're waiting on - self.waiting_on_folder_rename.append(f["index"]) + wait_on_folder[2].append(f["index"]) self.handle.rename_file(f["index"], f["path"].replace(folder, new_folder, 1)) + self.waiting_on_folder_rename.append(wait_on_folder) diff --git a/deluge/core/torrentmanager.py b/deluge/core/torrentmanager.py index 318fc7098..e2ad3234b 100644 --- a/deluge/core/torrentmanager.py +++ b/deluge/core/torrentmanager.py @@ -757,16 +757,23 @@ class TorrentManager(component.Component): log.debug("index: %s name: %s", alert.index, alert.name) torrent_id = str(alert.handle.info_hash()) torrent = self.torrents[torrent_id] - old_folder = os.path.split(torrent.files[alert.index]["path"])[0] - new_folder = os.path.split(alert.name)[0] torrent.files[alert.index]["path"] = alert.name - if alert.index in torrent.waiting_on_folder_rename: - if torrent.waiting_on_folder_rename == [alert.index]: - # This is the last alert we were waiting for, time to send signal - component.get("SignalManager").emit("torrent_folder_renamed", torrent_id, old_folder, new_folder) - torrent.waiting_on_folder_rename.remove(alert.index) - else: + # We need to see if this file index is in a waiting_on_folder list + folder_rename = False + for i, wait_on_folder in enumerate(torrent.waiting_on_folder_rename): + if alert.index in wait_on_folder[2]: + folder_rename = True + if len(wait_on_folder[2]) == 1: + # This is the last alert we were waiting for, time to send signal + component.get("SignalManager").emit("torrent_folder_renamed", torrent_id, wait_on_folder[0], wait_on_folder[1]) + del torrent.waiting_on_folder_rename[i] + break + # This isn't the last file to be renamed in this folder, so just + # remove the index and continue + torrent.waiting_on_folder_rename[i][2].remove(alert.index) + + if not folder_rename: # This is just a regular file rename so send the signal component.get("SignalManager").emit("torrent_file_renamed", torrent_id, alert.index, alert.name) diff --git a/deluge/ui/gtkui/files_tab.py b/deluge/ui/gtkui/files_tab.py index 868a9c00d..270d18755 100644 --- a/deluge/ui/gtkui/files_tab.py +++ b/deluge/ui/gtkui/files_tab.py @@ -484,6 +484,7 @@ class FilesTab(Tab): def _on_filename_edited(self, renderer, path, new_text): index = self.treestore[path][5] + log.debug("new_text: %s", new_text) # Don't do anything if the text hasn't changed if new_text == self.treestore[path][0]: @@ -494,17 +495,17 @@ class FilesTab(Tab): # We are renaming a file itr = self.treestore.get_iter(path) # Recurse through the treestore to get the actual path of the file - def get_filepath(i, fp): - if self.treestore.iter_parent(i): - fp = get_filepath(self.treestore.iter_parent(i), fp) - else: - fp += self.treestore[i][0] - return fp + def get_filepath(i): + ip = self.treestore.iter_parent(i) + fp = "" + while ip: + fp = self.treestore[ip][0] + fp + ip = self.treestore.iter_parent(ip) return fp # Only recurse if file is in a folder.. if self.treestore.iter_parent(itr): - filepath = get_filepath(itr, str()) + new_text + filepath = get_filepath(itr) + new_text else: filepath = new_text @@ -579,7 +580,9 @@ class FilesTab(Tab): parent_iter, self.treestore.get(itr, *xrange(self.treestore.get_n_columns()))) + itr_parent = self.treestore.iter_parent(itr) self.treestore.remove(itr) + self.remove_childless_folders(itr_parent) return True self.treestore.foreach(get_file_iter, None) @@ -598,7 +601,73 @@ class FilesTab(Tab): return True self.treestore.foreach(set_file_name, None) + def get_iter_at_path(self, filepath): + """ + Returns the gtkTreeIter for filepath + """ + log.debug("get_iter_at_path: %s", filepath) + is_dir = False + if filepath[-1] == "/": + is_dir = True + + filepath = filepath.split("/") + if "" in filepath: + filepath.remove("") + + path_iter = None + itr = self.treestore.iter_children(None) + level = 0 + while itr: + ipath = self.treestore[itr][0] + if (level + 1) != len(filepath) and ipath == filepath[level] + "/": + # We're not at the last index, but we do have a match + itr = self.treestore.iter_children(itr) + level += 1 + continue + elif (level + 1) == len(filepath) and ipath == filepath[level] + "/" if is_dir else filepath[level]: + # This is the iter we've been searching for + path_iter = itr + break + else: + itr = self.treestore.iter_next(itr) + continue + + return path_iter + + def reparent_iter(self, itr, parent, move_siblings=False): + """ + This effectively moves itr plus it's children to be a child of parent + + If move_siblings is True, it will move all itr's siblings to parent + """ + src = itr + def move_children(i, dest): + while i: + n = self.treestore.append(dest, self.treestore.get(i, *xrange(self.treestore.get_n_columns()))) + to_remove = i + if self.treestore.iter_children(i): + move_children(self.treestore.iter_children(i), n) + if i != src: + i = self.treestore.iter_next(i) + else: + # This is the source iter, we don't want other iters in it's level + if not move_siblings: + i = None + self.treestore.remove(to_remove) + + move_children(itr, parent) + + def remove_childless_folders(self, itr): + """ + Goes up the tree removing childless itrs starting at itr + """ + while not self.treestore.iter_children(itr): + parent = self.treestore.iter_parent(itr) + self.treestore.remove(itr) + itr = parent + def _on_torrent_folder_renamed_signal(self, torrent_id, old_folder, new_folder): + log.debug("on_torrent_folder_renamed_signal") log.debug("old_folder: %s new_folder: %s", old_folder, new_folder) if old_folder[-1] != "/": @@ -624,27 +693,32 @@ class FilesTab(Tab): except: pass - itr = None - for i, old in enumerate(old_split): - itr = self.treestore.iter_children(itr) - while itr: - if self.treestore[itr][0] == old + "/": - if old != new_split[i]: - # we need to change this one - self.treestore[itr][0] = new_split[i] + "/" - break - itr = self.treestore.iter_next(itr) + old_folder_iter = self.get_iter_at_path(old_folder) + old_folder_iter_parent = self.treestore.iter_parent(old_folder_iter) + + new_folder_iter = self.get_iter_at_path(new_folder) + if new_folder_iter: + # This means that a folder by this name already exists + self.reparent_iter(self.treestore.iter_children(old_folder_iter), new_folder_iter) + else: + new_folder_iter = self.get_iter_at_path("/".join(new_split[:-1]) + "/") + self.reparent_iter(old_folder_iter, new_folder_iter) + + # We need to check if the old_folder_iter_parent no longer has children + # and if so, we delete it + self.remove_childless_folders(old_folder_iter_parent) def _on_torrent_removed_signal(self, torrent_id): if torrent_id in self.files_list: del self.files_list[torrent_id] def _on_drag_data_get_data(self, treeview, context, selection, target_id, etime): - indexes = self.get_selected_files() - selection.set_text(",".join([str(x) for x in indexes])) + paths = self.listview.get_selection().get_selected_rows()[1] + selection.set_text(cPickle.dumps(paths)) def _on_drag_data_received_data(self, treeview, context, x, y, selection, info, etime): - log.debug("selection.data: %s", selection.data) + selected = cPickle.loads(selection.data) + log.debug("selection.data: %s", selected) drop_info = treeview.get_dest_row_at_pos(x, y) model = treeview.get_model() if drop_info: @@ -658,16 +732,20 @@ class FilesTab(Tab): parent_path = model[parent_iter][0] + parent_path parent_iter = model.iter_parent(parent_iter) - #[(index, filepath), ...] - to_rename = [] - - selected = [int(x) for x in selection.data.split(",")] - def find_file(model, path, itr, user_data): - if model[itr][5] in selected: - to_rename.append((model[itr][5], parent_path + model[itr][0])) - if len(to_rename) == len(selected): - return True - - model.foreach(find_file, None) - log.debug("to_rename: %s", to_rename) - client.rename_files(self.torrent_id, to_rename) + if model[selected[0]][5] == -1: + log.debug("parent_path: %s", parent_path) + log.debug("rename_to: %s", parent_path + model[selected[0]][0]) + # Get the full path of the folder we want to rename + pp = "" + itr = self.treestore.iter_parent(self.treestore.get_iter(selected[0])) + while itr: + pp = self.treestore[itr][0] + pp + itr = self.treestore.iter_parent(itr) + client.rename_folder(self.torrent_id, pp + model[selected[0]][0], parent_path + model[selected[0]][0]) + else: + #[(index, filepath), ...] + to_rename = [] + for s in selected: + to_rename.append((model[s][5], parent_path + model[s][0])) + log.debug("to_rename: %s", to_rename) + client.rename_files(self.torrent_id, to_rename)