Skip to content

Commit c40b085

Browse files
committed
Clipboard: Added command line provider.
1 parent 76565a2 commit c40b085

File tree

3 files changed

+124
-18
lines changed

3 files changed

+124
-18
lines changed

sunflower/clipboard.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import sys
22

33
from gi.repository import Gtk, Gdk
4+
from subprocess import check_output, run
5+
from threading import Thread
6+
from sunflower.common import executable_exists
47

58

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

1417
self.add_provider(GtkProvider())
18+
self.add_provider(CommandProvider())
1519
self.add_provider(FakeProvider())
1620

1721
def add_provider(self, provider):
@@ -205,3 +209,91 @@ def data_available(self, mime_types):
205209
"""Check if clipboard with specified mime types is available."""
206210
targets_available = [target in self.data for target in mime_types]
207211
return any(targets_available)
212+
213+
214+
class CommandProvider(Provider):
215+
"""Clipboard integration using command line application and piping."""
216+
217+
def __init__(self):
218+
commands = ['xclip', 'wl-copy', 'wl-paste']
219+
self.available_commands = tuple(filter(lambda command: executable_exists(command), commands))
220+
221+
def available(self):
222+
"""Test environment and return tuple of boolean values indicating usability."""
223+
result = len(self.available_commands) > 0
224+
return result, result
225+
226+
def set_text(self, text):
227+
"""Set text content."""
228+
commands = (
229+
('wl-copy',),
230+
('xclip', '-selection', 'clipboard'),
231+
)
232+
233+
for command in commands:
234+
try:
235+
run(command, input=text, text=True)
236+
except:
237+
pass
238+
else:
239+
break
240+
241+
def set_data(self, data, mime_types):
242+
"""Set data as content with provided list of mime types."""
243+
commands = (
244+
('wl-copy', '-t', mime_types[0]),
245+
('xclip', '-selection', 'clipboard', '-t', mime_types[0]),
246+
)
247+
248+
for command in commands:
249+
try:
250+
run(command, input=data, text=True)
251+
except:
252+
pass
253+
else:
254+
break
255+
256+
def get_text(self):
257+
"""Return text value stored in clipboard."""
258+
result = None
259+
commands = (
260+
('wl-paste',),
261+
('xclip', '-selection', 'clipboard', '-o'),
262+
)
263+
264+
for command in commands:
265+
try:
266+
result = check_output(command).decode('unicode-escape')
267+
except:
268+
pass
269+
else:
270+
break
271+
272+
return result
273+
274+
def get_data(self, mime_types):
275+
"""Return data stored for provided types in clipboard."""
276+
result = None
277+
commands = (
278+
('wl-paste', '-t', mime_types[0]),
279+
('xclip', '-selection', 'clipboard', '-o', '-t', mime_types[0]),
280+
)
281+
282+
for command in commands:
283+
try:
284+
result = check_output(command, input=data, text=True).decode('unicode-escape')
285+
except:
286+
pass
287+
else:
288+
break
289+
290+
return result
291+
292+
def text_available(self):
293+
"""Check if clipboard with text is available."""
294+
return self.get_text() is not None
295+
296+
def data_available(self, mime_types):
297+
"""Check if clipboard with specified mime types is available."""
298+
return self.get_data(mime_types) is not None
299+

sunflower/gui/main_window.py

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2368,15 +2368,10 @@ def set_clipboard_text(self, text):
23682368
self.clipboard.set_text(text)
23692369

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

23812376
def get_clipboard_text(self):
23822377
"""Get text from clipboard"""
@@ -2385,14 +2380,26 @@ def get_clipboard_text(self):
23852380
def get_clipboard_item_list(self):
23862381
"""Get item list from clipboard"""
23872382
result = None
2388-
targets = ['x-special/gnome-copied-files', 'text/uri-list']
2389-
selection = self.clipboard.get_data(targets)
2383+
targets = ['x-special/nautilus-clipboard', 'x-special/gnome-copied-files', 'text/uri-list']
23902384

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

2395+
# try getting data old way through mime type targets
2396+
else:
2397+
selection = self.clipboard.get_data(targets)
2398+
if selection is not None:
2399+
data = selection.splitlines(False)
2400+
data = list(filter(lambda x: len(x) > 0, data))
2401+
result = (data[0], data[1:])
2402+
23962403
return result
23972404

23982405
def is_clipboard_text(self):
@@ -2401,8 +2408,15 @@ def is_clipboard_text(self):
24012408

24022409
def is_clipboard_item_list(self):
24032410
"""Check if clipboard data is URI list"""
2404-
targets = ['x-special/gnome-copied-files', 'text/uri-list']
2405-
return self.clipboard.data_available(targets)
2411+
targets = ['x-special/nautilus-clipboard', 'x-special/gnome-copied-files', 'text/uri-list']
2412+
result = False
2413+
2414+
selection = self.clipboard.get_text()
2415+
if selection is not None:
2416+
data = selection.splitlines(False)
2417+
result = data[0] == targets[0] or data[0] in ('copy', 'cut')
2418+
2419+
return result
24062420

24072421
def is_archive_supported(self, mime_type):
24082422
"""Check if specified archive mime type is supported."""

sunflower/plugin_base/item_list.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -888,14 +888,14 @@ def _paste_files_from_clipboard(self, widget=None, data=None):
888888
# clipboard data contains URI list
889889
if data is not None:
890890
operation = data[0]
891-
list_ = data[1]
892-
protocol = list_[0].split('://')[0]
891+
uri_list = data[1]
892+
protocol = uri_list[0].split('://')[0]
893893

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

897897
# call handler
898-
self._handle_external_data(operation, protocol, list_, self.path)
898+
self._handle_external_data(operation, protocol, uri_list, self.path)
899899

900900
return True
901901

0 commit comments

Comments
 (0)