Skip to content

Commit

Permalink
Add Ctrl-C shortcut and context menu item to copy file paths
Browse files Browse the repository at this point in the history
Fixes #919
  • Loading branch information
mathiascode committed Jan 3, 2021
1 parent 631255d commit 7d104e0
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 48 deletions.
1 change: 1 addition & 0 deletions pynicotine/config.py
Expand Up @@ -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":
Expand Down
24 changes: 13 additions & 11 deletions pynicotine/gtkgui/downloads.py
Expand Up @@ -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),
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand All @@ -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)
Expand Down
36 changes: 29 additions & 7 deletions pynicotine/gtkgui/search.py
Expand Up @@ -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()

Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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:
Expand All @@ -1087,23 +1103,21 @@ 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('\\'):
# At least one selected result is a file, activate file-related items
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)
Expand Down Expand Up @@ -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)
Expand Down
10 changes: 10 additions & 0 deletions pynicotine/gtkgui/transferlist.py
Expand Up @@ -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)
Expand Down
22 changes: 12 additions & 10 deletions pynicotine/gtkgui/uploads.py
Expand Up @@ -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),
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand All @@ -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)
Expand Down
70 changes: 50 additions & 20 deletions pynicotine/gtkgui/userbrowse.py
Expand Up @@ -150,15 +150,18 @@ 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(
("USERMENU", _("User"), self.popup_menu_users, self.on_popup_menu_folder_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)
)

Expand Down Expand Up @@ -198,24 +201,28 @@ 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(
("USERMENU", "User", self.popup_menu_users2, self.on_popup_menu_file_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()

Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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))

Expand Down Expand Up @@ -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:
Expand Down
1 change: 1 addition & 0 deletions pynicotine/pynicotine.py
Expand Up @@ -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)
Expand Down

0 comments on commit 7d104e0

Please sign in to comment.