Skip to content

Commit

Permalink
Clipboard: Added command line provider.
Browse files Browse the repository at this point in the history
  • Loading branch information
MeanEYE committed Jun 16, 2020
1 parent 76565a2 commit c40b085
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 18 deletions.
92 changes: 92 additions & 0 deletions sunflower/clipboard.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import sys

from gi.repository import Gtk, Gdk
from subprocess import check_output, run
from threading import Thread
from sunflower.common import executable_exists


class Clipboard:
Expand All @@ -12,6 +15,7 @@ def __init__(self):
self.data_support = []

self.add_provider(GtkProvider())
self.add_provider(CommandProvider())
self.add_provider(FakeProvider())

def add_provider(self, provider):
Expand Down Expand Up @@ -205,3 +209,91 @@ def data_available(self, mime_types):
"""Check if clipboard with specified mime types is available."""
targets_available = [target in self.data for target in mime_types]
return any(targets_available)


class CommandProvider(Provider):
"""Clipboard integration using command line application and piping."""

def __init__(self):
commands = ['xclip', 'wl-copy', 'wl-paste']
self.available_commands = tuple(filter(lambda command: executable_exists(command), commands))

def available(self):
"""Test environment and return tuple of boolean values indicating usability."""
result = len(self.available_commands) > 0
return result, result

def set_text(self, text):
"""Set text content."""
commands = (
('wl-copy',),
('xclip', '-selection', 'clipboard'),
)

for command in commands:
try:
run(command, input=text, text=True)
except:
pass
else:
break

def set_data(self, data, mime_types):
"""Set data as content with provided list of mime types."""
commands = (
('wl-copy', '-t', mime_types[0]),
('xclip', '-selection', 'clipboard', '-t', mime_types[0]),
)

for command in commands:
try:
run(command, input=data, text=True)
except:
pass
else:
break

def get_text(self):
"""Return text value stored in clipboard."""
result = None
commands = (
('wl-paste',),
('xclip', '-selection', 'clipboard', '-o'),
)

for command in commands:
try:
result = check_output(command).decode('unicode-escape')
except:
pass
else:
break

return result

def get_data(self, mime_types):
"""Return data stored for provided types in clipboard."""
result = None
commands = (
('wl-paste', '-t', mime_types[0]),
('xclip', '-selection', 'clipboard', '-o', '-t', mime_types[0]),
)

for command in commands:
try:
result = check_output(command, input=data, text=True).decode('unicode-escape')
except:
pass
else:
break

return result

def text_available(self):
"""Check if clipboard with text is available."""
return self.get_text() is not None

def data_available(self, mime_types):
"""Check if clipboard with specified mime types is available."""
return self.get_data(mime_types) is not None

42 changes: 28 additions & 14 deletions sunflower/gui/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -2368,15 +2368,10 @@ def set_clipboard_text(self, text):
self.clipboard.set_text(text)

def set_clipboard_item_list(self, operation, uri_list):
"""Set clipboard to contain list of items
operation - 'copy' or 'cut' string representing operation
uri_list - list of URIs
"""
targets = ['x-special/gnome-copied-files', 'text/uri-list']
raw_data = '{0}\n'.format(operation) + '\n'.join(uri_list)
return self.clipboard.set_data(raw_data, targets)
"""Set clipboard to contain list of items for either `copy` or `cut` operation."""
targets = ['x-special/nautilus-clipboard', 'x-special/gnome-copied-files', 'text/uri-list']
raw_data = '{}\n{}\n{}\n'.format(targets[0], operation, '\n'.join(uri_list))
return self.clipboard.set_text(raw_data)

def get_clipboard_text(self):
"""Get text from clipboard"""
Expand All @@ -2385,14 +2380,26 @@ def get_clipboard_text(self):
def get_clipboard_item_list(self):
"""Get item list from clipboard"""
result = None
targets = ['x-special/gnome-copied-files', 'text/uri-list']
selection = self.clipboard.get_data(targets)
targets = ['x-special/nautilus-clipboard', 'x-special/gnome-copied-files', 'text/uri-list']

# in case there is something to paste
# nautilus recently provides data through regular
# clipboard as plain text try getting data that way
selection = self.clipboard.get_text()
if selection is not None:
data = selection.splitlines(False)
data = list(filter(lambda x: len(x) > 0, data))
if data[0] in targets:
data.pop(0)
result = (data[0], data[1:])

# try getting data old way through mime type targets
else:
selection = self.clipboard.get_data(targets)
if selection is not None:
data = selection.splitlines(False)
data = list(filter(lambda x: len(x) > 0, data))
result = (data[0], data[1:])

return result

def is_clipboard_text(self):
Expand All @@ -2401,8 +2408,15 @@ def is_clipboard_text(self):

def is_clipboard_item_list(self):
"""Check if clipboard data is URI list"""
targets = ['x-special/gnome-copied-files', 'text/uri-list']
return self.clipboard.data_available(targets)
targets = ['x-special/nautilus-clipboard', 'x-special/gnome-copied-files', 'text/uri-list']
result = False

selection = self.clipboard.get_text()
if selection is not None:
data = selection.splitlines(False)
result = data[0] == targets[0] or data[0] in ('copy', 'cut')

return result

def is_archive_supported(self, mime_type):
"""Check if specified archive mime type is supported."""
Expand Down
8 changes: 4 additions & 4 deletions sunflower/plugin_base/item_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -888,14 +888,14 @@ def _paste_files_from_clipboard(self, widget=None, data=None):
# clipboard data contains URI list
if data is not None:
operation = data[0]
list_ = data[1]
protocol = list_[0].split('://')[0]
uri_list = data[1]
protocol = uri_list[0].split('://')[0]

# convert URI to normal path
list_ = [urllib.parse.unquote(item.split('://')[1]) for item in list_]
uri_list = [urllib.parse.unquote(item.split('://')[1]) for item in uri_list]

# call handler
self._handle_external_data(operation, protocol, list_, self.path)
self._handle_external_data(operation, protocol, uri_list, self.path)

return True

Expand Down

0 comments on commit c40b085

Please sign in to comment.