~ubuntu-branches/ubuntu/utopic/python-traitsui/utopic

« back to all changes in this revision

Viewing changes to traitsui/wx/dnd_editor.py

  • Committer: Bazaar Package Importer
  • Author(s): Varun Hiremath
  • Date: 2011-07-09 13:57:39 UTC
  • Revision ID: james.westby@ubuntu.com-20110709135739-x5u20q86huissmn1
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#------------------------------------------------------------------------------
 
2
#
 
3
#  Copyright (c) 2006, Enthought, Inc.
 
4
#  All rights reserved.
 
5
#
 
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
 
10
#
 
11
#  Thanks for using Enthought open source!
 
12
#
 
13
#  Author: David C. Morrill
 
14
#  Date:   06/25/2006
 
15
#
 
16
#------------------------------------------------------------------------------
 
17
 
 
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
 
22
    target.
 
23
"""
 
24
 
 
25
#-------------------------------------------------------------------------------
 
26
#  Imports:
 
27
#-------------------------------------------------------------------------------
 
28
 
 
29
import wx
 
30
import numpy
 
31
 
 
32
from cPickle \
 
33
    import load
 
34
 
 
35
from traits.api \
 
36
    import Bool
 
37
 
 
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
 
43
 
 
44
from pyface.wx.drag_and_drop \
 
45
    import PythonDropSource, PythonDropTarget, clipboard
 
46
 
 
47
try:
 
48
    from apptools.io import File
 
49
except ImportError:
 
50
    File = None
 
51
 
 
52
try:
 
53
    from apptools.naming.api import Binding
 
54
except ImportError:
 
55
    Binding = None
 
56
 
 
57
from pyface.image_resource \
 
58
    import ImageResource
 
59
 
 
60
from editor \
 
61
    import Editor
 
62
 
 
63
#-------------------------------------------------------------------------------
 
64
#  Constants:
 
65
#-------------------------------------------------------------------------------
 
66
 
 
67
# The image to use when the editor accepts files:
 
68
file_image = ImageResource( 'file' ).create_image()
 
69
 
 
70
# The image to use when the editor accepts objects:
 
71
object_image = ImageResource( 'object' ).create_image()
 
72
 
 
73
# The image to use when the editor is disabled:
 
74
inactive_image = ImageResource( 'inactive' ).create_image()
 
75
 
 
76
# String types:
 
77
string_type = ( str, unicode )
 
78
 
 
79
#-------------------------------------------------------------------------------
 
80
#  'SimpleEditor' class:
 
81
#-------------------------------------------------------------------------------
 
82
 
 
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.
 
86
    """
 
87
 
 
88
    #---------------------------------------------------------------------------
 
89
    #  Trait definitions:
 
90
    #---------------------------------------------------------------------------
 
91
 
 
92
    # Is the editor a drop target?
 
93
    drop_target = Bool( True )
 
94
 
 
95
    # Is the editor a drag source?
 
96
    drag_source = Bool( True )
 
97
 
 
98
    #---------------------------------------------------------------------------
 
99
    #  Finishes initializing the editor by creating the underlying toolkit
 
100
    #  widget:
 
101
    #---------------------------------------------------------------------------
 
102
 
 
103
    def init ( self, parent ):
 
104
        """ Finishes initializing the editor by creating the underlying toolkit
 
105
            widget.
 
106
        """
 
107
        # Determine the drag/drop type:
 
108
        value         = self.value
 
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 )))
 
113
 
 
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()
 
121
        else:
 
122
            disabled_image = inactive_image
 
123
            image          = object_image
 
124
            if self._is_file:
 
125
                image = file_image
 
126
 
 
127
        self._image = image.ConvertToBitmap()
 
128
        if disabled_image is not None:
 
129
            self._disabled_image = disabled_image.ConvertToBitmap()
 
130
        else:
 
131
            data = numpy.reshape( numpy.fromstring( image.GetData(),
 
132
                                                    numpy.uint8 ),
 
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()
 
139
 
 
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() ) )
 
143
        self.set_tooltip()
 
144
 
 
145
        if self.drop_target:
 
146
            control.SetDropTarget( PythonDropTarget( self ) )
 
147
 
 
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 )
 
152
 
 
153
    #---------------------------------------------------------------------------
 
154
    #  Disposes of the contents of an editor:
 
155
    #---------------------------------------------------------------------------
 
156
 
 
157
    def dispose ( self ):
 
158
        """ Disposes of the contents of an editor.
 
159
        """
 
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 )
 
165
 
 
166
        super( SimpleEditor, self ).dispose()
 
167
 
 
168
    #---------------------------------------------------------------------------
 
169
    #  Updates the editor when the object trait changes external to the editor:
 
170
    #---------------------------------------------------------------------------
 
171
 
 
172
    def update_editor ( self ):
 
173
        """ Updates the editor when the object trait changes externally to the
 
174
            editor.
 
175
        """
 
176
        return
 
177
 
 
178
#-- Private Methods ------------------------------------------------------------
 
179
 
 
180
    #---------------------------------------------------------------------------
 
181
    #  Returns the processed version of a drag request's data:
 
182
    #---------------------------------------------------------------------------
 
183
 
 
184
    def _get_drag_data ( self, data ):
 
185
        """ Returns the processed version of a drag request's data.
 
186
        """
 
187
        if isinstance( data, list ):
 
188
 
 
189
            if Binding is not None and isinstance( data[0], Binding ):
 
190
                data = [ item.obj for item in data ]
 
191
 
 
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:
 
195
                    result = []
 
196
                    for file in data:
 
197
                        item = self._unpickle( file )
 
198
                        if item is not None:
 
199
                            result.append( item )
 
200
                    data = result
 
201
 
 
202
        else:
 
203
            if Binding is not None and isinstance( data, Binding ):
 
204
                data = data.obj
 
205
 
 
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:
 
211
                        data = object
 
212
 
 
213
        return data
 
214
 
 
215
    #---------------------------------------------------------------------------
 
216
    #  Returns the unpickled version of a specified file (if possible):
 
217
    #---------------------------------------------------------------------------
 
218
 
 
219
    def _unpickle ( self, file_name ):
 
220
        """ Returns the unpickled version of a specified file (if possible).
 
221
        """
 
222
        fh = None
 
223
        try:
 
224
            fh     = file( file_name, 'rb' )
 
225
            object = load( fh )
 
226
        except:
 
227
            object = None
 
228
 
 
229
        if fh is not None:
 
230
            fh.close()
 
231
 
 
232
        return object
 
233
 
 
234
#-- wxPython Event Handlers ----------------------------------------------------
 
235
 
 
236
    def _on_paint ( self, event ):
 
237
        """ Called when the control needs repainting.
 
238
        """
 
239
        image   = self._image
 
240
        control = self.control
 
241
        if not control.IsEnabled():
 
242
            image = self._disabled_image
 
243
 
 
244
        wdx, wdy = control.GetClientSizeTuple()
 
245
        wx.PaintDC( control ).DrawBitmap( image,
 
246
            (wdx - image.GetWidth())  / 2, (wdy - image.GetHeight()) / 2, True )
 
247
 
 
248
    def _left_down ( self, event ):
 
249
        """ Handles the left mouse button being pressed.
 
250
        """
 
251
        if self.control.IsEnabled() and self.drag_source:
 
252
            self._x, self._y = event.GetX(), event.GetY()
 
253
            self.control.CaptureMouse()
 
254
 
 
255
        event.Skip()
 
256
 
 
257
    def _left_up ( self, event ):
 
258
        """ Handles the left mouse button being released.
 
259
        """
 
260
        if self._x is not None:
 
261
            self._x = None
 
262
            self.control.ReleaseMouse()
 
263
 
 
264
        event.Skip()
 
265
 
 
266
    def _mouse_move ( self, event ):
 
267
        """ Handles the mouse being moved.
 
268
        """
 
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()
 
273
                self._x = None
 
274
                if self._is_file:
 
275
                    FileDropSource(   self.control, self.value )
 
276
                else:
 
277
                    PythonDropSource( self.control, self.value )
 
278
 
 
279
        event.Skip()
 
280
 
 
281
#----- Drag and drop event handlers: -------------------------------------------
 
282
 
 
283
    #---------------------------------------------------------------------------
 
284
    #  Handles a Python object being dropped on the control:
 
285
    #---------------------------------------------------------------------------
 
286
 
 
287
    def wx_dropped_on ( self, x, y, data, drag_result ):
 
288
        """ Handles a Python object being dropped on the tree.
 
289
        """
 
290
        try:
 
291
            self.value = self._get_drag_data( data )
 
292
            return drag_result
 
293
        except:
 
294
            return wx.DragNone
 
295
 
 
296
    #---------------------------------------------------------------------------
 
297
    #  Handles a Python object being dragged over the control:
 
298
    #---------------------------------------------------------------------------
 
299
 
 
300
    def wx_drag_over ( self, x, y, data, drag_result ):
 
301
        """ Handles a Python object being dragged over the tree.
 
302
        """
 
303
        try:
 
304
            self.object.base_trait( self.name ).validate( self.object,
 
305
                                        self.name, self._get_drag_data( data ) )
 
306
            return drag_result
 
307
        except:
 
308
            return wx.DragNone
 
309
 
 
310
#-------------------------------------------------------------------------------
 
311
#  'CustomEditor' class:
 
312
#-------------------------------------------------------------------------------
 
313
 
 
314
class CustomEditor ( SimpleEditor ):
 
315
    """ Custom style of drag-and-drop editor, which is not a drag source.
 
316
    """
 
317
    #---------------------------------------------------------------------------
 
318
    #  Trait definitions:
 
319
    #---------------------------------------------------------------------------
 
320
 
 
321
    # Is the editor a drag source? This value overrides the default.
 
322
    drag_source = False
 
323
 
 
324
#-------------------------------------------------------------------------------
 
325
#  'ReadonlyEditor' class:
 
326
#-------------------------------------------------------------------------------
 
327
 
 
328
class ReadonlyEditor ( SimpleEditor ):
 
329
    """ Read-only style of drag-and-drop editor, which is not a drop target.
 
330
    """
 
331
    #---------------------------------------------------------------------------
 
332
    #  Trait definitions:
 
333
    #---------------------------------------------------------------------------
 
334
 
 
335
    # Is the editor a drop target? This value overrides the default.
 
336
    drop_target = False
 
337
 
 
338
#-------------------------------------------------------------------------------
 
339
#  'FileDropSource' class:
 
340
#-------------------------------------------------------------------------------
 
341
 
 
342
class FileDropSource ( wx.DropSource ):
 
343
    """ Represents a draggable file.
 
344
    """
 
345
    #---------------------------------------------------------------------------
 
346
    #  Initializes the object:
 
347
    #---------------------------------------------------------------------------
 
348
 
 
349
    def __init__ ( self, source, files ):
 
350
        """ Initializes the object.
 
351
        """
 
352
        self.handler    = None
 
353
        self.allow_move = True
 
354
 
 
355
        # Put the data to be dragged on the clipboard:
 
356
        clipboard.data        = files
 
357
        clipboard.source      = source
 
358
        clipboard.drop_source = self
 
359
 
 
360
        data_object = wx.FileDataObject()
 
361
        if isinstance( files, string_type ):
 
362
            files = [ files ]
 
363
 
 
364
        for file in files:
 
365
            data_object.AddFile( file )
 
366
 
 
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 )
 
371
 
 
372
    #---------------------------------------------------------------------------
 
373
    #  Called when the data has been dropped:
 
374
    #---------------------------------------------------------------------------
 
375
 
 
376
    def on_dropped ( self, drag_result ):
 
377
        """ Called when the data has been dropped. """
 
378
        return
 
379
 
 
380
## EOF ########################################################################