1 """ROX applications should provide good drag-and-drop support. Use this module
2 to allow drops onto widgets in your application."""
5 from rox
import g
, alert
, get_local_path
, _
13 if '%' not in uri
: return uri
15 return re
.sub('%[0-9a-fA-F][0-9a-fA-F]',
16 lambda match
: chr(int(match
.group(0)[1:], 16)),
19 def extract_uris(data
):
20 """Convert a text/uri-list to a python list of (unescaped) URIs"""
21 lines
= data
.split('\r\n')
25 continue # (gmc adds a '\0' line)
27 out
.append(unescape(l
))
30 def provides(context
, type): return type in map(str, context
.targets
)
32 class RemoteFiles(Exception):
35 Exception.__init
__(self
, _('Cannot load files from a remote machine '
36 '(multiple files, or target application/octet-stream not provided)'))
39 """A mix-in class for widgets that can have files/data dropped on
40 them. Object should also be a GtkWidget."""
42 def __init__(self
, types
):
43 """Call this after initialising the widget.
44 Types is a list of MIME-types, or None to only accept files."""
46 targets
= [('text/uri-list', 0, TARGET_URILIST
)]
48 for type in types
+ ['application/octet-stream']:
49 targets
.append((type, 0, TARGET_RAW
))
51 self
.targets
= targets
52 self
.xds_proxy_for(self
)
54 def xds_proxy_for(self
, widget
):
55 "Handle drops on this widget as if they were to 'self'."
56 # (Konqueror requires ACTION_MOVE)
57 widget
.drag_dest_set(g
.DEST_DEFAULT_MOTION | g
.DEST_DEFAULT_HIGHLIGHT
,
59 gdk
.ACTION_COPY | gdk
.ACTION_MOVE | gdk
.ACTION_PRIVATE
)
61 widget
.connect('drag-data-received', self
.xds_data_received
)
62 widget
.connect('drag-drop', self
.xds_drag_drop
)
64 def xds_drag_drop(self
, widget
, context
, data
, info
, time
):
65 """Called when something is dropped on us. Decide which of the
66 offered targets to request and ask for it. xds_data_received will
67 be called when it finally arrives."""
68 target
= widget
.drag_dest_find_target(context
, self
.targets
)
69 context
.rox_leafname
= None
72 context
.drop_finish(False, time
)
74 if provides(context
, 'XdndDirectSave0'):
76 context
.rox_leafname
= saving
._read
_xds
_property
(context
, False)
77 widget
.drag_get_data(context
, target
, time
)
80 def xds_data_received(self
, widget
, context
, x
, y
, selection
, info
, time
):
81 "Called when we get some data. Internal."
83 if info
== TARGET_RAW
:
85 self
.xds_load_from_selection(selection
, context
.rox_leafname
)
87 context
.drop_finish(False, time
)
89 context
.drop_finish(True, time
)
91 if info
!= TARGET_URILIST
:
94 uris
= extract_uris(selection
.data
)
96 alert("Nothing to load!")
97 context
.drop_finish(False, time
)
102 self
.xds_load_uris(uris
)
104 if len(uris
) != 1 or not provides(context
, 'application/octet-stream'):
106 widget
.drag_get_data(context
, 'application/octet-stream', time
)
107 return 1 # Don't do drag_finish
109 context
.drop_finish(False, time
)
110 rox
.report_exception()
112 context
.drop_finish(True, time
)
116 def xds_load_uris(self
, uris
):
117 """Try to load each URI in the list. Override this if you can handle URIs
118 directly. The default method passes each local path to xds_load_from_file()
119 and displays an error for anything else."""
122 path
= get_local_path(uri
)
125 if len(paths
) < len(uris
):
128 self
.xds_load_from_file(path
)
130 def xds_load_from_file(self
, path
):
131 """Try to load this local file. Override this if you have a better way
132 to load files. The default method opens the file and calls xds_load_from_stream()."""
134 self
.xds_load_from_stream(path
, None, open(path
, 'rb'))
136 rox
.report_exception()
138 def xds_load_from_selection(self
, selection
, leafname
= None):
139 """Try to load this selection (data from another application). The default
140 puts the data in a cStringIO and calls xds_load_from_stream()."""
141 if selection
.data
is None:
142 g
.gdk
.beep() # Load aborted
144 from cStringIO
import StringIO
145 type = str(selection
.type)
146 self
.xds_load_from_stream(leafname
, type, StringIO(selection
.data
))
148 def xds_load_from_stream(self
, name
, type, stream
):
149 """Called when we get any data sent via drag-and-drop in any way (local
150 file or remote application transfer). You should override this and do
151 something with the data. 'name' may be None (if the data is unnamed),
152 a leafname, or a full path or URI. 'type' is the MIME type, or None if
154 alert('Got some data, but missing code to handle it!\n\n(name="%s";type="%s")'