1
#------------------------------------------------------------------------------
3
# Copyright (c) 2006, Enthought, Inc.
6
# This software is provided without warranty under the terms of the BSD
7
# license included in enthought/LICENSE.txt and may be redistributed only
8
# under the conditions described in the aforementioned license. The license
9
# is also available online at http://www.enthought.com/licenses/BSD.txt
11
# Thanks for using Enthought open source!
13
# Author: David C. Morrill
16
#------------------------------------------------------------------------------
18
""" Defines the various editors for a drag-and-drop editor,
19
for the wxPython user interface toolkit. A drag-and-drop editor represents
20
its value as a simple image which, depending upon the editor style, can be
21
a drag source only, a drop target only, or both a drag source and a drop
25
#-------------------------------------------------------------------------------
27
#-------------------------------------------------------------------------------
38
# FIXME: ToolkitEditorFactory is a proxy class defined here just for backward
39
# compatibility. The class has been moved to the
40
# traitsui.editors.dnd_editor file.
41
from traitsui.editors.dnd_editor \
42
import ToolkitEditorFactory
44
from pyface.wx.drag_and_drop \
45
import PythonDropSource, PythonDropTarget, clipboard
48
from apptools.io import File
53
from apptools.naming.api import Binding
57
from pyface.image_resource \
63
#-------------------------------------------------------------------------------
65
#-------------------------------------------------------------------------------
67
# The image to use when the editor accepts files:
68
file_image = ImageResource( 'file' ).create_image()
70
# The image to use when the editor accepts objects:
71
object_image = ImageResource( 'object' ).create_image()
73
# The image to use when the editor is disabled:
74
inactive_image = ImageResource( 'inactive' ).create_image()
77
string_type = ( str, unicode )
79
#-------------------------------------------------------------------------------
80
# 'SimpleEditor' class:
81
#-------------------------------------------------------------------------------
83
class SimpleEditor ( Editor ):
84
""" Simply style of editor for a drag-and-drop editor, which is both a drag
85
source and a drop target.
88
#---------------------------------------------------------------------------
90
#---------------------------------------------------------------------------
92
# Is the editor a drop target?
93
drop_target = Bool( True )
95
# Is the editor a drag source?
96
drag_source = Bool( True )
98
#---------------------------------------------------------------------------
99
# Finishes initializing the editor by creating the underlying toolkit
101
#---------------------------------------------------------------------------
103
def init ( self, parent ):
104
""" Finishes initializing the editor by creating the underlying toolkit
107
# Determine the drag/drop type:
109
self._is_list = isinstance( value, list )
110
self._is_file = (isinstance( value, string_type ) or
111
(self._is_list and (len( value ) > 0) and
112
isinstance( value[0], string_type )))
114
# Get the right image to use:
115
image = self.factory.image
116
if image is not None:
117
image = image.create_image()
118
disabled_image = self.factory.disabled_image
119
if disabled_image is not None:
120
disabled_image = disabled_image.create_image()
122
disabled_image = inactive_image
127
self._image = image.ConvertToBitmap()
128
if disabled_image is not None:
129
self._disabled_image = disabled_image.ConvertToBitmap()
131
data = numpy.reshape( numpy.fromstring( image.GetData(),
133
( -1, 3 ) ) * numpy.array( [ [ 0.297, 0.589, 0.114 ] ] )
134
g = data[ :, 0 ] + data[ :, 1 ] + data[ :, 2 ]
135
data[ :, 0 ] = data[ :, 1 ] = data[ :, 2 ] = g
136
image.SetData( numpy.ravel( data.astype(numpy.uint8) ).tostring() )
137
image.SetMaskColour( 0, 0, 0 )
138
self._disabled_image = image.ConvertToBitmap()
140
# Create the control and set up the event handlers:
141
self.control = control = wx.Window( parent, -1,
142
size = wx.Size( image.GetWidth(), image.GetHeight() ) )
146
control.SetDropTarget( PythonDropTarget( self ) )
148
wx.EVT_LEFT_DOWN( control, self._left_down )
149
wx.EVT_LEFT_UP( control, self._left_up )
150
wx.EVT_MOTION( control, self._mouse_move )
151
wx.EVT_PAINT( control, self._on_paint )
153
#---------------------------------------------------------------------------
154
# Disposes of the contents of an editor:
155
#---------------------------------------------------------------------------
157
def dispose ( self ):
158
""" Disposes of the contents of an editor.
160
control = self.control
161
wx.EVT_LEFT_DOWN( control, None )
162
wx.EVT_LEFT_UP( control, None )
163
wx.EVT_MOTION( control, None )
164
wx.EVT_PAINT( control, None )
166
super( SimpleEditor, self ).dispose()
168
#---------------------------------------------------------------------------
169
# Updates the editor when the object trait changes external to the editor:
170
#---------------------------------------------------------------------------
172
def update_editor ( self ):
173
""" Updates the editor when the object trait changes externally to the
178
#-- Private Methods ------------------------------------------------------------
180
#---------------------------------------------------------------------------
181
# Returns the processed version of a drag request's data:
182
#---------------------------------------------------------------------------
184
def _get_drag_data ( self, data ):
185
""" Returns the processed version of a drag request's data.
187
if isinstance( data, list ):
189
if Binding is not None and isinstance( data[0], Binding ):
190
data = [ item.obj for item in data ]
192
if File is not None and isinstance( data[0], File ):
193
data = [ item.absolute_path for item in data ]
194
if not self._is_file:
197
item = self._unpickle( file )
199
result.append( item )
203
if Binding is not None and isinstance( data, Binding ):
206
if File is not None and isinstance( data, File ):
207
data = data.absolute_path
208
if not self._is_file:
209
object = self._unpickle( data )
210
if object is not None:
215
#---------------------------------------------------------------------------
216
# Returns the unpickled version of a specified file (if possible):
217
#---------------------------------------------------------------------------
219
def _unpickle ( self, file_name ):
220
""" Returns the unpickled version of a specified file (if possible).
224
fh = file( file_name, 'rb' )
234
#-- wxPython Event Handlers ----------------------------------------------------
236
def _on_paint ( self, event ):
237
""" Called when the control needs repainting.
240
control = self.control
241
if not control.IsEnabled():
242
image = self._disabled_image
244
wdx, wdy = control.GetClientSizeTuple()
245
wx.PaintDC( control ).DrawBitmap( image,
246
(wdx - image.GetWidth()) / 2, (wdy - image.GetHeight()) / 2, True )
248
def _left_down ( self, event ):
249
""" Handles the left mouse button being pressed.
251
if self.control.IsEnabled() and self.drag_source:
252
self._x, self._y = event.GetX(), event.GetY()
253
self.control.CaptureMouse()
257
def _left_up ( self, event ):
258
""" Handles the left mouse button being released.
260
if self._x is not None:
262
self.control.ReleaseMouse()
266
def _mouse_move ( self, event ):
267
""" Handles the mouse being moved.
269
if self._x is not None:
270
if ((abs( self._x - event.GetX() ) +
271
abs( self._y - event.GetY() )) >= 3):
272
self.control.ReleaseMouse()
275
FileDropSource( self.control, self.value )
277
PythonDropSource( self.control, self.value )
281
#----- Drag and drop event handlers: -------------------------------------------
283
#---------------------------------------------------------------------------
284
# Handles a Python object being dropped on the control:
285
#---------------------------------------------------------------------------
287
def wx_dropped_on ( self, x, y, data, drag_result ):
288
""" Handles a Python object being dropped on the tree.
291
self.value = self._get_drag_data( data )
296
#---------------------------------------------------------------------------
297
# Handles a Python object being dragged over the control:
298
#---------------------------------------------------------------------------
300
def wx_drag_over ( self, x, y, data, drag_result ):
301
""" Handles a Python object being dragged over the tree.
304
self.object.base_trait( self.name ).validate( self.object,
305
self.name, self._get_drag_data( data ) )
310
#-------------------------------------------------------------------------------
311
# 'CustomEditor' class:
312
#-------------------------------------------------------------------------------
314
class CustomEditor ( SimpleEditor ):
315
""" Custom style of drag-and-drop editor, which is not a drag source.
317
#---------------------------------------------------------------------------
319
#---------------------------------------------------------------------------
321
# Is the editor a drag source? This value overrides the default.
324
#-------------------------------------------------------------------------------
325
# 'ReadonlyEditor' class:
326
#-------------------------------------------------------------------------------
328
class ReadonlyEditor ( SimpleEditor ):
329
""" Read-only style of drag-and-drop editor, which is not a drop target.
331
#---------------------------------------------------------------------------
333
#---------------------------------------------------------------------------
335
# Is the editor a drop target? This value overrides the default.
338
#-------------------------------------------------------------------------------
339
# 'FileDropSource' class:
340
#-------------------------------------------------------------------------------
342
class FileDropSource ( wx.DropSource ):
343
""" Represents a draggable file.
345
#---------------------------------------------------------------------------
346
# Initializes the object:
347
#---------------------------------------------------------------------------
349
def __init__ ( self, source, files ):
350
""" Initializes the object.
353
self.allow_move = True
355
# Put the data to be dragged on the clipboard:
356
clipboard.data = files
357
clipboard.source = source
358
clipboard.drop_source = self
360
data_object = wx.FileDataObject()
361
if isinstance( files, string_type ):
365
data_object.AddFile( file )
367
# Create the drop source and begin the drag and drop operation:
368
super( FileDropSource, self ).__init__( source )
369
self.SetData( data_object )
370
self.result = self.DoDragDrop( True )
372
#---------------------------------------------------------------------------
373
# Called when the data has been dropped:
374
#---------------------------------------------------------------------------
376
def on_dropped ( self, drag_result ):
377
""" Called when the data has been dropped. """
380
## EOF ########################################################################