diff --git a/pynicotine/config.py b/pynicotine/config.py index 978b84b6831f..248885f1c80c 100644 --- a/pynicotine/config.py +++ b/pynicotine/config.py @@ -384,6 +384,7 @@ def __init__(self, filename, data_dir): # Windows specific stuff if sys.platform == "win32": self.defaults['ui']['filemanager'] = 'explorer $' + self.defaults['players']['npplayer'] = 'other' # Non-functional tray icon is disabled on macOS if sys.platform == "darwin": diff --git a/pynicotine/gtkgui/downloads.py b/pynicotine/gtkgui/downloads.py index 1f8bf67024ca..f0acb4f3de60 100644 --- a/pynicotine/gtkgui/downloads.py +++ b/pynicotine/gtkgui/downloads.py @@ -60,12 +60,15 @@ def __init__(self, frame, tab_label): self.popup_menu = popup = PopupMenu(frame) popup.setup( + ("#" + _("Send to _Player"), self.on_play_files), + ("#" + _("_Open Folder"), self.on_open_directory), + ("#" + _("File P_roperties"), self.on_file_properties), + ("", None), + ("#" + _("Copy _File Path"), self.on_copy_file_path), ("#" + _("Copy _URL"), self.on_copy_url), ("#" + _("Copy Folder URL"), self.on_copy_dir_url), - ("#" + _("Send to _player"), self.on_play_files), - ("#" + _("File Properties"), self.on_file_properties), - ("#" + _("Open Folder"), self.on_open_directory), - ("#" + _("Search"), self.on_file_search), + ("", None), + ("#" + _("_Search"), self.on_file_search), (1, _("User(s)"), self.popup_menu_users, self.on_popup_menu_users), ("", None), ("#" + _("_Retry"), self.on_retry_transfer), @@ -238,6 +241,8 @@ def on_key_press_event(self, widget, event): self.on_abort_transfer(widget) elif key in ("R", "r"): self.on_retry_transfer(widget) + elif key in ("C", "c") and event.state in (Gdk.ModifierType.CONTROL_MASK, Gdk.ModifierType.LOCK_MASK | Gdk.ModifierType.CONTROL_MASK): + self.on_copy_file_path(widget) elif key == "Delete": self.on_abort_transfer(widget, clear=True) else: @@ -314,19 +319,16 @@ def on_popup_menu(self, widget, event, kind): files = len(self.selected_transfers) > 0 items = self.popup_menu.get_children() - if users: - items[6].set_sensitive(True) # Users Menu - else: - items[6].set_sensitive(False) # Users Menu + items[9].set_sensitive(users) # Users Menu if files: act = True else: # Disable options - # Copy URL, Copy Folder URL, Send to player, File Properties, File manager, Search filename + # Send to player, File manager, file properties, Copy File Path, Copy URL, Copy Folder URL, Search filename act = False - for i in range(0, 6): + for i in range(0, 7): items[i].set_sensitive(act) if not users or not files: @@ -336,7 +338,7 @@ def on_popup_menu(self, widget, event, kind): else: act = True - for i in range(8, 11): + for i in range(11, 14): items[i].set_sensitive(act) self.popup_menu.popup(None, None, None, None, 3, event.time) diff --git a/pynicotine/gtkgui/search.py b/pynicotine/gtkgui/search.py index beafc2f98fba..97cb5c14c183 100644 --- a/pynicotine/gtkgui/search.py +++ b/pynicotine/gtkgui/search.py @@ -516,6 +516,7 @@ def __init__(self, searches, text, id, mode, remember, showtab): self.ResultsList.set_model(self.resultsmodel) self.ResultsList.connect("button_press_event", self.on_list_clicked) + self.ResultsList.connect("key-press-event", self.on_key_press_event) self.update_visuals() @@ -578,6 +579,7 @@ def __init__(self, searches, text, id, mode, remember, showtab): ("#" + _("Download F_older(s) To..."), self.on_download_folders_to), ("#" + _("File _Properties"), self.on_file_properties), ("", None), + ("#" + _("Copy _File Path"), self.on_copy_file_path), ("#" + _("Copy _URL"), self.on_copy_url), ("#" + _("Copy Folder U_RL"), self.on_copy_dir_url), ("", None), @@ -1075,6 +1077,20 @@ def on_list_clicked(self, widget, event): return False + def on_key_press_event(self, widget, event): + + key = Gdk.keyval_name(event.keyval) + self.select_results() + + if key in ("C", "c") and event.state in (Gdk.ModifierType.CONTROL_MASK, Gdk.ModifierType.LOCK_MASK | Gdk.ModifierType.CONTROL_MASK): + self.on_copy_file_path(widget) + else: + # No key match, continue event + return False + + widget.stop_emission_by_name("key_press_event") + return True + def on_popup_menu(self, widget, event): if event.button != 3: @@ -1087,15 +1103,13 @@ def on_popup_menu(self, widget, event): users = len(self.selected_users) > 0 files = len(self.selected_results) > 0 - for i in range(0, 5): - items[i].set_sensitive(files) - items[0].set_sensitive(False) items[1].set_sensitive(False) items[4].set_sensitive(False) - items[6].set_sensitive(False) - items[7].set_sensitive(files) - items[8].set_sensitive(users) + items[6].set_sensitive(files) + items[7].set_sensitive(False) + items[8].set_sensitive(files) + items[10].set_sensitive(users) for result in self.selected_results: if not result[1].endswith('\\'): @@ -1103,7 +1117,7 @@ def on_popup_menu(self, widget, event): items[0].set_sensitive(True) items[1].set_sensitive(True) items[4].set_sensitive(True) - items[6].set_sensitive(True) + items[7].set_sensitive(True) break self.popup_menu.popup(None, None, None, None, event.button, event.time) @@ -1232,6 +1246,14 @@ def on_download_folders_to(self, widget): self.frame.np.requested_folders[user][folder] = destination self.frame.np.send_message_to_peer(user, slskmessages.FolderContentsRequest(None, folder)) + def on_copy_file_path(self, widget): + + if not self.selected_results: + return + + user, path = next(iter(self.selected_results))[:2] + self.frame.clip.set_text(path, -1) + def on_copy_url(self, widget): user, path = next(iter(self.selected_results))[:2] self.frame.set_clipboard_url(user, path) diff --git a/pynicotine/gtkgui/transferlist.py b/pynicotine/gtkgui/transferlist.py index 3f13e6ca7e3e..68fe27381958 100644 --- a/pynicotine/gtkgui/transferlist.py +++ b/pynicotine/gtkgui/transferlist.py @@ -639,6 +639,16 @@ def on_select_user_transfers(self, widget): self.select_transfers() + def on_copy_file_path(self, widget): + + if not self.selected_transfers: + return + + i = next(iter(self.selected_transfers)) + text = self.transfersmodel.get_value(i.iter, 10) + + self.frame.clip.set_text(text, -1) + def on_copy_url(self, widget): i = next(iter(self.selected_transfers)) self.frame.set_clipboard_url(i.user, i.filename) diff --git a/pynicotine/gtkgui/uploads.py b/pynicotine/gtkgui/uploads.py index bb5e2283a7bf..65cfdf41e6c2 100644 --- a/pynicotine/gtkgui/uploads.py +++ b/pynicotine/gtkgui/uploads.py @@ -57,11 +57,14 @@ def __init__(self, frame, tab_label): self.popup_menu = popup = PopupMenu(frame) popup.setup( + ("#" + _("Send to _Player"), self.on_play_files), + ("#" + _("_Open Folder"), self.on_open_directory), + ("", None), + ("#" + _("Copy _File Path"), self.on_copy_file_path), ("#" + _("Copy _URL"), self.on_copy_url), ("#" + _("Copy Folder URL"), self.on_copy_dir_url), - ("#" + _("Send to _Player"), self.on_play_files), - ("#" + _("Open Folder"), self.on_open_directory), - ("#" + _("Search"), self.on_file_search), + ("", None), + ("#" + _("_Search"), self.on_file_search), (1, _("User(s)"), self.popup_menu_users, self.on_popup_menu_users), ("", None), ("#" + _("_Retry"), self.on_upload_transfer), @@ -158,6 +161,8 @@ def on_key_press_event(self, widget, event): if key in ("T", "t"): self.on_abort_transfer(widget) + elif key in ("C", "c") and event.state in (Gdk.ModifierType.CONTROL_MASK, Gdk.ModifierType.LOCK_MASK | Gdk.ModifierType.CONTROL_MASK): + self.on_copy_file_path(widget) elif key == "Delete": self.on_abort_transfer(widget, clear=True) else: @@ -202,19 +207,16 @@ def on_popup_menu(self, widget, event, kind): files = len(self.selected_transfers) > 0 items = self.popup_menu.get_children() - if users: - items[5].set_sensitive(True) # Users Menu - else: - items[5].set_sensitive(False) # Users Menu + items[8].set_sensitive(users) # Users Menu if files: act = True else: # Disable options - # Copy URL, Copy Folder URL, Send to player, File manager, Search filename + # Send to player, File manager, Copy File Path, Copy URL, Copy Folder URL, Search filename act = False - for i in range(0, 5): + for i in range(0, 6): items[i].set_sensitive(act) if users and files: @@ -224,7 +226,7 @@ def on_popup_menu(self, widget, event, kind): # Retry, Abort, Clear act = False - for i in range(7, 10): + for i in range(10, 13): items[i].set_sensitive(act) self.popup_menu.popup(None, None, None, None, 3, event.time) diff --git a/pynicotine/gtkgui/userbrowse.py b/pynicotine/gtkgui/userbrowse.py index 70ba74ba0bf1..0999addf1aaf 100644 --- a/pynicotine/gtkgui/userbrowse.py +++ b/pynicotine/gtkgui/userbrowse.py @@ -150,8 +150,10 @@ def __init__(self, userbrowses, user): (1, _("Download"), self.popup_menu_downloads_folders, None), (1, _("Upload"), self.popup_menu_uploads_folders, None), ("", None), + ("#" + _("Open in File _Manager"), self.on_file_manager), + ("", None), + ("#" + _("Copy _Folder Path"), self.on_copy_file_path, False), ("#" + _("Copy _URL"), self.on_copy_dir_url), - ("#" + _("Open in File Manager"), self.on_file_manager) ) else: self.folder_popup_menu.setup( @@ -159,6 +161,7 @@ def __init__(self, userbrowses, user): ("", None), (1, _("Download"), self.popup_menu_downloads_folders, None), ("", None), + ("#" + _("Copy _Folder Path"), self.on_copy_file_path, False), ("#" + _("Copy _URL"), self.on_copy_dir_url) ) @@ -198,11 +201,12 @@ def __init__(self, userbrowses, user): (1, _("Download"), self.popup_menu_downloads_files, None), (1, _("Upload"), self.popup_menu_uploads_files, None), ("", None), - ("#" + _("Copy _URL"), self.on_copy_url), ("#" + _("Send to _Player"), self.on_play_files), - ("#" + _("Open in File Manager"), self.on_file_manager), + ("#" + _("Open in File _Manager"), self.on_file_manager), + ("#" + _("File _Properties"), self.on_file_properties), ("", None), - ("#" + _("File Properties"), self.on_file_properties) + ("#" + _("Copy _File Path"), self.on_copy_file_path, True), + ("#" + _("Copy _URL"), self.on_copy_url) ) else: self.file_popup_menu.setup( @@ -210,12 +214,15 @@ def __init__(self, userbrowses, user): ("", None), (1, _("Download"), self.popup_menu_downloads_files, None), ("", None), - ("#" + _("Copy _URL"), self.on_copy_url), + ("#" + _("File _Properties"), self.on_file_properties), ("", None), - ("#" + _("File Properties"), self.on_file_properties) + ("#" + _("Copy _File Path"), self.on_copy_file_path, True), + ("#" + _("Copy _URL"), self.on_copy_url) ) + self.FolderTreeView.connect("key-press-event", self.on_key_press_event) self.FileTreeView.connect("button_press_event", self.on_file_clicked) + self.FileTreeView.connect("key-press-event", self.on_key_press_event) self.update_visuals() @@ -287,6 +294,10 @@ def on_folder_popup_menu(self, widget, event): self.folder_popup_menu.popup(None, None, None, None, event.button, event.time) + def select_files(self): + self.selected_files = [] + self.FileTreeView.get_selection().selected_foreach(self.selected_files_callback) + def selected_files_callback(self, model, path, iterator): rawfilename = self.file_store.get_value(iterator, 6) self.selected_files.append(rawfilename) @@ -303,8 +314,7 @@ def on_file_clicked(self, widget, event): widget.get_selection().unselect_all() elif event.button == 1 and event.type == Gdk.EventType._2BUTTON_PRESS: - self.selected_files = [] - self.FileTreeView.get_selection().selected_foreach(self.selected_files_callback) + self.select_files() self.on_download_files(widget) self.FileTreeView.get_selection().unselect_all() return True @@ -314,15 +324,7 @@ def on_file_clicked(self, widget, event): def on_file_popup_menu(self, widget, event): set_treeview_selected_row(widget, event) - - self.selected_files = [] - self.FileTreeView.get_selection().selected_foreach(self.selected_files_callback) - - files = True - multiple = False - - if len(self.selected_files) > 1: - multiple = True + self.select_files() if len(self.selected_files) >= 1: files = True @@ -334,11 +336,15 @@ def on_file_popup_menu(self, widget, event): if self.user == self.frame.np.config.sections["server"]["login"]: items[2].set_sensitive(files) # Downloads items[3].set_sensitive(files) # Uploads - items[5].set_sensitive(not multiple and files) # Copy URL - items[6].set_sensitive(files) # Send to player + items[5].set_sensitive(files) # Send to player + items[7].set_sensitive(files) # File Properties + items[9].set_sensitive(files) # Copy File Path + items[10].set_sensitive(files) # Copy URL else: items[2].set_sensitive(files) # Downloads - items[4].set_sensitive(not multiple and files) # Copy URL + items[4].set_sensitive(files) # File Properties + items[6].set_sensitive(files) # Copy File Path + items[7].set_sensitive(files) # Copy URL self.FileTreeView.stop_emission_by_name("button_press_event") self.file_popup_menu.popup(None, None, None, None, event.button, event.time) @@ -915,6 +921,21 @@ def on_upload_files(self, widget, prefix=""): self.frame.np.transfers.push_file(user, "\\".join([folder, fn]), "\\".join([realpath, fn]), prefix) self.frame.np.transfers.check_upload_queue() + def on_key_press_event(self, widget, event): + + key = Gdk.keyval_name(event.keyval) + self.select_files() + + if key in ("C", "c") and event.state in (Gdk.ModifierType.CONTROL_MASK, Gdk.ModifierType.LOCK_MASK | Gdk.ModifierType.CONTROL_MASK): + files = (widget == self.FileTreeView) + self.on_copy_file_path(widget, files) + else: + # No key match, continue event + return False + + widget.stop_emission_by_name("key_press_event") + return True + def on_play_files(self, widget, prefix=""): start_new_thread(self._on_play_files, (widget, prefix)) @@ -1006,6 +1027,15 @@ def on_refresh(self, widget): self.FileTreeView.set_sensitive(False) self.frame.browse_user(self.user) + def on_copy_file_path(self, widget, files=False): + + text = self.selected_folder + + if files and self.selected_files: + text = "\\".join([self.selected_folder, self.selected_files[0]]) + + self.frame.clip.set_text(text, -1) + def on_copy_url(self, widget): if self.selected_files != [] and self.selected_files is not None: diff --git a/pynicotine/pynicotine.py b/pynicotine/pynicotine.py index a36be3c462ff..63e8043fa3b2 100644 --- a/pynicotine/pynicotine.py +++ b/pynicotine/pynicotine.py @@ -1312,6 +1312,7 @@ def say_chat_room(self, msg): if self.chatrooms is not None: event = self.pluginhandler.incoming_public_chat_event(msg.room, msg.user, msg.msg) + if event is not None: (r, n, msg.msg) = event self.chatrooms.roomsctrl.say_chat_room(msg, msg.msg)