Renamed XDSLoader to loading.
[rox-lib.git] / python / rox / loading.py
blob16d79f1dbb91779f0c4db2b966cd31272f15d890
1 """ROX applications should provide good drag-and-drop support. Use this module
2 to allow drops onto widgets in your application."""
4 import rox
5 from rox import g, alert, get_local_path, _
7 gdk = g.gdk
9 TARGET_URILIST = 0
10 TARGET_RAW = 1
12 def extract_uris(data):
13 """Convert a text/uri-list to a python list of URIs"""
14 lines = data.split('\r\n')
15 out = []
16 for l in lines:
17 if l == chr(0):
18 continue # (gmc adds a '\0' line)
19 if l and l[0] != '#':
20 out.append(l)
21 return out
23 def provides(context, type): return type in map(str, context.targets)
25 class RemoteFiles(Exception):
26 "Internal use"
27 def __init__(self):
28 Exception.__init__(self, _('Cannot load files from a remote machine '
29 '(multiple files, or target application/octet-stream not provided)'))
31 class XDSLoader:
32 """A mix-in class for widgets that can have files/data dropped on
33 them. Object should also be a GtkWidget."""
35 def __init__(self, types):
36 """Call this after initialising the widget.
37 Types is a list of MIME-types, or None to only accept files."""
39 targets = [('text/uri-list', 0, TARGET_URILIST)]
40 if types:
41 for type in types + ['application/octet-stream']:
42 targets.append((type, 0, TARGET_RAW))
44 self.targets = targets
45 self.xds_proxy_for(self)
47 def xds_proxy_for(self, widget):
48 "Handle drops on this widget as if they were to 'self'."
49 # (Konqueror requires ACTION_MOVE)
50 widget.drag_dest_set(g.DEST_DEFAULT_MOTION | g.DEST_DEFAULT_HIGHLIGHT,
51 self.targets,
52 gdk.ACTION_COPY | gdk.ACTION_MOVE | gdk.ACTION_PRIVATE)
54 widget.connect('drag_data_received', self.xds_data_received)
56 def xds_data_received(self, widget, context, x, y, selection, info, time):
57 "Called when we get some data. Internal."
58 if info == TARGET_RAW:
59 self.xds_load_from_selection(selection)
60 return 1
61 if info != TARGET_URILIST:
62 return 0
64 uris = extract_uris(selection.data)
65 if not uris:
66 alert("Nothing to load!")
67 return 1
69 try:
70 try:
71 self.xds_load_uris(uris)
72 except RemoteFiles:
73 if len(uris) != 1 or not provides(context, 'application/octet-stream'):
74 raise
75 widget.drag_get_data(context, 'application/octet-stream', time)
76 except:
77 rox.report_exception()
79 return 1
81 def xds_load_uris(self, uris):
82 """Try to load each URI in the list. Override this if you can handle URIs
83 directly. The default method passes each local path to xds_load_from_file()
84 and displays an error for anything else."""
85 paths = []
86 for uri in uris:
87 path = get_local_path(uri)
88 if path:
89 paths.append(path)
90 if len(paths) < len(uris):
91 raise RemoteFiles
92 for path in paths:
93 self.xds_load_from_file(path)
95 def xds_load_from_file(self, path):
96 """Try to load this local file. Override this if you have a better way
97 to load files. The default method loads the file and calls xds_load_from_stream()."""
98 try:
99 self.xds_load_from_stream(path, None, open(path, 'rb'))
100 except:
101 rox.report_exception()
103 def xds_load_from_selection(self, selection):
104 """Try to load this selection (data from another application). The default
105 puts the data in a cStringIO and calls xds_load_from_stream()."""
106 from cStringIO import StringIO
107 type = str(selection.type)
108 self.xds_load_from_stream(None, type, StringIO(selection.data))
110 def xds_load_from_stream(self, name, type, stream):
111 """Called when we get any data sent via drag-and-drop in any way (local
112 file or remote application transfer). You should override this and do
113 something with the data. 'name' may be None (if the data is unnamed),
114 a leafname, or a full path or URI. 'type' is the MIME type, or None if
115 unknown."""
116 alert('Got some data, but missing code to handle it!\n\n(name="%s";type="%s")'
117 % (name, type))