summaryrefslogtreecommitdiff
path: root/cad/src/dna/commands/BuildDna/BuildDna_GraphicsMode.py
blob: 27f2b8961aae63fb5d062d67f66bcf2222ce02ec (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
# Copyright 2008-2009 Nanorex, Inc.  See LICENSE file for details.
"""

@author: Ninad
@version: $Id$
@copyright: 2008-2009 Nanorex, Inc.  See LICENSE file for details.

History:

TODO:
"""

from commands.SelectChunks.SelectChunks_GraphicsMode import SelectChunks_GraphicsMode
from model.chem import Atom
from model.bonds import Bond
from Numeric import dot
from PyQt4.Qt import QMouseEvent
from geometry.VQT import V, Q, A, norm, vlen
from commands.Select.Select_GraphicsMode import DRAG_STICKINESS_LIMIT
from utilities.debug import print_compact_traceback
import math
import foundation.env as env

from utilities.prefs_constants import cursorTextFontSize_prefs_key

from graphics.drawing.drawDnaLabels import draw_dnaBaseNumberLabels

DEBUG_CLICK_ON_OBJECT_ENTERS_ITS_EDIT_COMMAND = True

_superclass = SelectChunks_GraphicsMode
class BuildDna_GraphicsMode( SelectChunks_GraphicsMode):
    """
    #doc

    @note: this has subclasses, including DnaSegment_GraphicsMode.
    """

    #The flag that decides whether to draw the handles. This flag is
    #set during left dragging, when no handle is 'grabbed'. This optimizes the
    #drawing code as it skips handle drawing code and also the computation
    #of handle positions each time the mouse moves.
    #@see self.leftUp , self.leftDrag, and subclass Draw_other methods
    # for more details.
    _handleDrawingRequested = True

    #Some left drag variables used to drag the whole segment along axis or
    #rotate the segment around its own axis of for free drag translation
    _movablesForLeftDrag = []

    #Subclasses such as MakeCrossovers_GraphicsMode need this attr.
    #needs refactoring. See self.leftADown()
    _movable_dnaSegment_for_leftDrag = None

    #The common center is the center about which the list of movables (the segment
    #contents are rotated.
    #@see: self.leftADown where this is set.
    #@see: self.leftDrag where it is used.
    _commonCenterForRotation = None
    _axis_for_constrained_motion = None

    #Flags that decide the type of left drag.
    #@see: self.leftADown where it is set and self.leftDrag where these are used
    _translateAlongAxis = False
    _rotateAboutAxis = False
    _freeDragWholeStructure = False



    cursor_over_when_LMB_pressed = ''

    def Enter_GraphicsMode(self):
        _superclass.Enter_GraphicsMode(self)
        #Precaution
        self.clear_leftA_variables()

    def update_cursor_for_no_MB(self):
        """
        Update the cursor for Select mode (Default implementation).
        """
        _superclass.update_cursor_for_no_MB(self)

        #minor optimization -- don't go further into the method if
        #nothing is highlighted i.e. self.o.selobj is None.
        if self.o.selobj is None:
            return

        if self.o.modkeys is None:
            if isinstance(self.o.selobj, Atom):
                if self.o.selobj.element.role == 'strand':
                    self.o.setCursor(self.win.rotateAboutCentralAxisCursor)
                elif self.o.selobj.element.role == 'axis':
                    self.o.setCursor(self.win.translateAlongCentralAxisCursor)



    def bareMotion(self, event):
        """
        @see: self.update_cursor_for_no_MB
        """
        value = _superclass.bareMotion(self, event)

        #When the cursor is over a specific atom, we need to display
        #a different icon. (e.g. when over a strand atom, it should display
        # rotate cursor)
        self.update_cursor()

        return value

    def leftDown(self, event):
        """
        """
        self.reset_drag_vars()

        self.clear_leftA_variables()

        _superclass.leftDown(self, event)

        self.LMB_press_event = QMouseEvent(event) # Make a copy of this event
        #and save it.
        # We will need it later if we change our mind and start selecting a 2D
        # region in leftDrag().Copying the event in this way is necessary
        #because Qt will overwrite  <event> later (in
        # leftDrag) if we simply set self.LMB_press_event = event.  mark 060220

        self.LMB_press_pt_xy = (event.pos().x(), event.pos().y())
        # <LMB_press_pt_xy> is the position of the mouse in window coordinates
        #when the LMB was pressed.
        #Used in mouse_within_stickiness_limit (called by leftDrag() and other
        #methods).
        # We don't bother to vertically flip y using self.height
        #(as mousepoints does),
        # since this is only used for drag distance within single drags.
        #Subclasses should override one of the following method if they need
        #to do additional things to prepare for dragging.
        ##self._leftDown_preparation_for_dragging(event)

    def clear_leftA_variables(self):
        self._movablesForLeftDrag = []
        #Subclasses such as MakeCrossovers_GraphicsMode need this attr.
        #(self._movable_dnaSegment_for_leftDrag) needs refactoring.
        self._movable_dnaSegment_for_leftDrag = None
        self._commonCenterForRotation = None
        self._axis_for_constrained_motion = None
        self._translateAlongAxis = False
        self._rotateAboutAxis = False
        self._freeDragWholeStructure = False

    def _leftDown_preparation_for_dragging(self, objectUnderMouse, event):
        """
        Handle left down event. Preparation for rotation and/or selection
        This method is called inside of self.leftDown.
        @param event: The mouse left down event.
        @type  event: QMouseEvent instance
        @see: self.leftDown
        @see: self.leftDragRotation
        Overrides _superclass._leftDown_preparation_for_dragging
        """
        self.o.SaveMouse(event)
        self.picking = True
        self.dragdist = 0.0
        farQ_junk, self.movingPoint = self.dragstart_using_GL_DEPTH( event)
        self.leftADown(objectUnderMouse, event)



    def singletLeftDown(self, s, event):
        """
        Handles SingletLeftDown (left down on a bond point) event.
        @see: JoinStrands_GraphicsMode.leftUp()
        """
        #@see: class JoinStrands_GraphicsMode for a detailed explanation.
        #copying some portion in that comment below --
        #Example: In this BuildDna_EditCommand (graphicsMode), if you want to
        #join  two strands, upon 'singletLeftDown'  it enters
        #JoinStrands_By_DND_Command , also calling leftDown method of its graphicsmode.
        #Now, when user releases theLMB, it calls
        #JoinStrands_GraphicsMode.leftUp()  which in turn exits that command
        #if the flag 'exit_command_on_leftUp' is set to True(to go back to the
        #previous command user was in) .
        #A lot of code that does bondpoint dragging is available in
        #BuildAtoms_GraphicsMode, but it isn't in BuildDna_GraphicsMode
        #(as it is a  SelectChunks_GraphicsMode superclass for lots of reasons)
        #So, for some significant amount of time, we may continue to use
        #this flag to temporarily enter/exit this command.
        #@see: self.leftUp(), BuildDna_GraphicsMode.singletLeftDown()

        commandSequencer = self.win.commandSequencer

        commandSequencer.userEnterCommand('JoinStrands_By_DND')

        assert commandSequencer.currentCommand.commandName == 'JoinStrands_By_DND'

        #Make sure that the glpane selobj is set to 's' (this bondpoint)
        #when we enter a different command, all that information is probably
        #lost , so its important to set it explicitly here.
        self.glpane.set_selobj(s)

        #'gm' is the graphics mode of JoinStrands_Command
        gm = commandSequencer.currentCommand.graphicsMode
        #Set the graphics mode flag so that it knows to return to
        #BuildDna_EditCommand upon leftUp()
        gm.exit_command_on_leftUp = True
        gm.leftDown(event)
        return


    def chunkLeftDown(self, a_chunk, event):
        """
        Depending on the modifier key(s) pressed, it does various operations on
        chunk..typically pick or unpick the chunk(s) or do nothing.

        If an object left down happens, the left down method of that object
        calls this method (chunkLeftDown) as it is the 'SelectChunks_GraphicsMode' which
        is supposed to select Chunk of the object clicked
        @param a_chunk: The chunk of the object clicked (example, if the  object
                      is an atom, then it is atom.molecule
        @type a_chunk: B{Chunk}
        @param event: MouseLeftDown event
        @see: self.atomLeftDown
        @see: self.chunkLeftDown
        @see:self.objectSetUp()
        """

        strandOrSegment = a_chunk.parent_node_of_class(
            self.win.assy.DnaStrandOrSegment)

        if strandOrSegment is not None:
            #Make sure to call chunkSetUp if you are not calling the
            #chunkLeftDown method of superclass. This in turn,
            #calls self.objectSetUp(). Fixes a problem in selecting a
            #DnaClynder chunk (selobj = Chunk)
            self.chunkSetUp(a_chunk, event)
            return

        _superclass.chunkLeftDown(self, a_chunk, event)


    def chunkLeftUp(self, aChunk, event):
        """
        Upon chunkLeftUp, it enters the strand or segment edit command
        This is an alternative implementation. As of 2008-03-03,
        we have decided to change this implementation. Keeping the
        related methods alive if, in future, we want to switch to this
        implementation and/or add a user preference to do this.
        """

        _superclass.chunkLeftUp(self, aChunk, event)

        if self.glpane.modkeys is not None:
            #Don't go further if some modkey is pressed. We will call the
            #edit method of the Dna components only if no modifier key is
            #pressed

            return

        if not self.mouse_within_stickiness_limit(event, DRAG_STICKINESS_LIMIT):
            return

        #@TODO: If the object under mouse was double clicked, don't enter the
        #edit mode, instead do appropriate operation such as expand selection or
        #contract selection (done in superclass)
        #Note: when the object is just single clicked, it becomes editable).

        if self.editObjectOnSingleClick():
            if aChunk.picked:
                if aChunk.isAxisChunk() or aChunk.isStrandChunk():
                    strandOrSegmentGroup = aChunk.parent_node_of_class(
                        self.win.assy.DnaStrandOrSegment)
                    if strandOrSegmentGroup is not None:
                        strandOrSegmentGroup.edit()
                        strandOrSegmentGroup.pick()

    def _do_leftShiftCntlUp_delete_operations(self,
                                              event,
                                              objUnderMouse,
                                              parentNodesOfObjUnderMouse = ()):
        """
        Overrides superclass method

        @param parentNodesOfObjUnderMouse: Tuple containing the
                parent chunk(s), of which, the object
                under mouse  is a part of,  or, some other node such as a
                DnaStrand Or DnaSegment etc which the user probably wants to
                operate on.
        @type: Tuple

        @see: self.chunkLeftUp()
        @see: self.leftShiftCntlUp() which calls this method.
        @see: SelectChunks_GraphicsMode._do_leftShiftCntlUp_delete_operations()
        """
        obj = objUnderMouse
        if obj is self.o.selobj:
            if isinstance(obj, Bond):
                self.bondDelete(event)
                return


        _superclass._do_leftShiftCntlUp_delete_operations(
            self,
            event,
            objUnderMouse,
            parentNodesOfObjUnderMouse = parentNodesOfObjUnderMouse)


    def leftADown(self, objectUnderMouse, event):
        """
        Method called during mouse left down . It sets some parameters
        necessary for rotating the structure around its own axis (during
        a left drag to follow) In graphics modes such as
        RotateChunks_GraphicsMode, rotating entities around their own axis is
        acheived by holding down 'A' key and then left dragging , thats why the
        method is named as 'leftADrag'  (A= Axis)
        """
        obj = objectUnderMouse

        if obj is None: # Cursor over empty space.
            self.emptySpaceLeftDown(event)
            #Left A drag is not possible unless the cursor is over a
            #selected object. So make sure to let self.leftAError method sets
            #proper flag so that left-A drag won't be done in this case.
            return

        ma = V(0, 0, 0)
        chunk = None

        if isinstance(obj, Atom):
            chunk = obj.molecule
        elif isinstance(obj, self.win.assy.Chunk):
            chunk = obj
        elif isinstance(obj, Bond):
            chunk1 = obj.atom1.molecule
            chunk2 = obj.atom2.molecule
            #@@ what should we do if its a interchunk bond?? lets just select
            #one chunk
            chunk = chunk1

        if chunk is None:
            return

        ma, segment = chunk.getAxis_of_self_or_eligible_parent_node()

        self._axis_for_constrained_motion = ma

        #@see: DnaSegment.get_all_content_chunks() for details about
        #what it returns. See also DnaSegment.isAncestorOf() which
        #is called in self.leftDown to determine whether the DnaSegment
        #user is editing is an ancestor of the selobj. (it also considers
        #'logical contents' while determining whether it is an ancestor.
        #-- Ninad 2008-03-11

        if isinstance(segment, self.win.assy.DnaSegment):
            self._movablesForLeftDrag = segment.get_all_content_chunks()
            self._movable_dnaSegment_for_leftDrag = segment
        else:
            self._movablesForLeftDrag = self.win.assy.getSelectedMovables()

        ma = norm(V(dot(ma,self.o.right),dot(ma,self.o.up)))

        self.Zmat = A([ma,[-ma[1],ma[0]]])


        if isinstance(obj, Atom) and \
           obj.element.role == 'axis':
            self._translateAlongAxis = True
            self._rotateAboutAxis = False
            self._freeDragWholeStructure = False
        elif isinstance(obj, Atom) and \
             obj.element.role == 'strand':
            self._translateAlongAxis = False
            self._rotateAboutAxis = True
            self._freeDragWholeStructure = False
            #The self._commonCenterForrotation is a point on segment axis
            #about which the whole segment will be rotated. Specifying this
            #as a common center  for rotation fixes bug 2578. We determine this
            #by selecting the center of the axis atom that is connected
            #(bonded) to the strand atom being left dragged. Using this as a
            #common center instead of the avaraging the center of the segment
            #axis atoms has an advantage. We compute the rotation offset 'dy'
            #with reference to the strand atom being dragged, so it seems more
            #appropriate to use the nearest axis center for segment rotation
            #about axis. But what happens if the axis is not straigt but is
            #curved? Should we select the averaged center of all axis atoms?
            #..that may not be right. Or should we take _average center_ of
            #a the following axis atoms --strand atoms axis_neighbors and
            #axis atom centers directly connected to this axis atom.
            #  -- Ninad 2008-03-25
            axis_neighbor = obj.axis_neighbor()
            if axis_neighbor is not None:
                self._commonCenterForRotation = axis_neighbor.posn()
        else:
            self._translateAlongAxis = False
            self._rotateAboutAxis = False
            self._freeDragWholeStructure = True


        self.o.SaveMouse(event)
        self.dragdist = 0.0


    def leftUp(self, event):
        """
        Method called during Left up event.
        """

        _superclass.leftUp(self, event)
        self.update_selobj(event)
        self.update_cursor()
        self.o.gl_update()

        #Reset the flag that decides whether to draw the handles. This flag is
        #set during left dragging, when no handle is 'grabbed'. See the
        #class definition for more details about this flag.
        if hasattr(self.command, 'handles') and self.command.handles:
            self.command.updateHandlePositions()
            if not self._handleDrawingRequested:
                self._handleDrawingRequested = True

        #IMPLEMENTATION CHANGE 2008-03-05.
        #Due to an implementation change, user is not allowed to
        #exit this command by simply clicking onto empty space. So following
        #is commented out. (Keeping it for now just in case we change our mind
        #soon. If you see this code being commented out even after 1 or 2 months
        #from the original comment date, please just delete it.
        #--Ninad 2008-03-05
        ##if self.cursor_over_when_LMB_pressed == 'Empty Space':
            ###Exit this command by directly calling command.Done.
            ###This skips call of command.preview_or_finalize_structure
            ###Not calling 'preview_or_finialize_structure before calling
            ###command.command_Done(), has an advantage. As of 2008-02-20, we
            ###remove the structure (segment) and recreate it upon done.
            ###This also removes, for instance, any cross overs you created
            ###earlier. although same thing happens when you hit 'Done button',
            ###it is likely to happen by accident while you are in segment edit
            ###mode and just click on empty space, Therefore, we simply call
            ###Command.command_Done(). See a related bug mentioned in
            ###DnaSegment_EditCommand.setStructureName
            ##self.command.command_Done()


    def leftDrag(self, event):
        """
        Method called during Left drag event.
        """
        if self.mouse_within_stickiness_limit(event, DRAG_STICKINESS_LIMIT):
            # [let this happen even for drag_handlers -- bruce 060728]
            return

        self.current_obj_clicked = False

        #If there is a drag handler (e.g. a segment resize handle is being
        #dragged, call its drag method and don't proceed further.

        #NOTE:
        #In SelectChunks_GraphicsMode.leftDrag, there is a condition statement
        #which checks if self.drag_handler is in assy.getSelectedMovables
        #I don't know why it does that... I think it always assums that the
        #drag handler is officially a node in the MT? In our case,
        #the drag handler is a 'Highlightable' object (actually
        #an instance of 'DnaSegment_ResizeHandle' (has superclass from
        #exprs module ..which implements API for a highlightable object
        #So it doesn't get registered in the selectMovables list. Thats why
        #we are not calling _superclass.leftDrag. The above mentioned
        #method in the superclass needs to be revised -- Ninad 2008-02-01

        if self.drag_handler is not None:
            self.dragHandlerDrag(self.drag_handler, event)
            return

        #Duplicates some code from SelectChunks_GraphicsMode.leftDrag
        #see a to do comment below in this method
        if self.cursor_over_when_LMB_pressed == 'Empty Space':
            self.emptySpaceLeftDrag(event)
            return

        if self.o.modkeys is not None:
            # If a drag event has happened after the cursor was over an atom
            # and a modkey is pressed, do a 2D region selection as if the
            # atom were absent.
            self.emptySpaceLeftDown(self.LMB_press_event)
            #bruce 060721 question: why don't we also do emptySpaceLeftDrag
            # at this point?
            return

        #TODO: This duplicates some code from SelectChunks_GraphicsMode.leftDrag

        #Following code will never be called if a handle is grabbed.
        #Thus, it instructs what to do for other cases (when user is not moving
        #the draggable handles)

        #First, don't draw handles (set the flag here so that self.Draw_other knows
        #not to draw handles). This skips unnecessary computation of new handle
        #position during left dragging. The flag is reset to True in leftUp

        if hasattr(self.command, 'handles') and self.command.handles:
            if self.command.grabbedHandle is None:
                self._handleDrawingRequested = False

        #Copies AND modifies some code from Move_GraphicsMode for doing
        #leftDrag translation or rotation.

        w = self.o.width + 0.0
        h = self.o.height + 0.0
        deltaMouse = V(event.pos().x() - self.o.MousePos[0],
                       self.o.MousePos[1] - event.pos().y())

        a =  dot(self.Zmat, deltaMouse)
        dx,dy =  a * V(self.o.scale/(h*0.5), 2*math.pi/w)

        if self._translateAlongAxis:
            for mol in self._movablesForLeftDrag:
                mol.move(dx*self._axis_for_constrained_motion)

        if self._rotateAboutAxis:
            rotation_quat = Q(self._axis_for_constrained_motion, -dy)
            self.o.assy.rotateSpecifiedMovables(
                rotation_quat,
                movables = self._movablesForLeftDrag,
                commonCenter = self._commonCenterForRotation )

        if self._freeDragWholeStructure:
            try:
                point = self.dragto( self.movingPoint, event)
                offset = point - self.movingPoint
                self.o.assy.translateSpecifiedMovables(offset,
                                                       movables = self._movablesForLeftDrag)
                self.movingPoint = point
            except:
                #may be self.movingPoint is not defined in leftDown?
                #(e.g. _superclass.leftDown doesn't get called or as a bug? )
                print_compact_traceback("bug:unable to free drag the whole segment")


        self.dragdist += vlen(deltaMouse) #k needed?? [bruce 070605 comment]

        self.o.SaveMouse(event)
        self.o.assy.changed() #ninad060924 fixed bug 2278
        self.o.gl_update()
        return


    def editObjectOnSingleClick(self):
        """
        Subclasses can override this method. If this method returns True,
        when you left click on a DnaSegment or a DnaStrand, it becomes editable
        (i.e. program enters the edit command of that particular object if
        that object is editable)
        @see: MakeCrossover_GraphicsMode.editObjectOnSingleClick()
        """
        if DEBUG_CLICK_ON_OBJECT_ENTERS_ITS_EDIT_COMMAND:
            return True


    def drawHighlightedChunk(self, glpane, selobj, hicolor, hicolor2):
        """
        Overrides SelectChunks_basicGraphicsMode method.
        """
        chunk = None

        if isinstance(selobj, self.win.assy.Chunk):
            chunk = selobj
        elif isinstance(selobj, Atom):## and not selobj.is_singlet():
            chunk = selobj.molecule
        elif isinstance(selobj, Bond):
            #@@@ what if its a interchunk bond -- case not supported as of
            #2008-04-30
            chunk = selobj.atom1.molecule


        if chunk is not None:
            dnaStrandOrSegment = chunk.parent_node_of_class(
                self.win.assy.DnaStrandOrSegment)
            if dnaStrandOrSegment is not None:
                if self.glpane.modkeys == 'Shift+Control':
                    #Don't highlight the whole chunk if object under cursor
                    #is a bond and user has pressed the Shift+Control key
                    #It means we will allow breaking of that bond upon leftClick
                    if isinstance(selobj, Bond):
                        return False
                else:
                    #If the object under cursor is not a bond,
                    #AND the modifier key is not "Shift+Control', only highlight
                    #that object (which will most likely be the Atom or a
                    #singlet. This special highlighting will be used to do
                    #various stuff like rotating the Dna duplex around its axis
                    #etc.
                    if isinstance(selobj, Atom):
                        return False

        #For all other cases , do what superclass does for highlighting
        # a chunk
        return _superclass.drawHighlightedChunk(self,
                                                glpane,
                                                selobj,
                                                hicolor,
                                                hicolor2)


    def _drawCursorText(self, position = None):
        """
        Draw the text near the cursor. It gives information about number of
        basepairs/bases being added or removed, length of the segment (if self.struct
        is a strand etc.
        @param position: Optional argument. If position (a vector) is specified,
                         instead of drawing the text at the cursor position,
                         it is drawn at the specified position.
        @type position: B{V} or None

        @see: DnaSegment_GraphicsMode,  DnaStrand_GraphicsMode  (subclasses of
        this class where this is being used.
        """
        if hasattr(self.command, 'grabbedHandle') and hasattr(self.command,
                                                              'getCursorText'):
            if self.command.grabbedHandle is not None:
                #Draw the text next to the cursor that gives info about
                #number of base pairs etc

                text , textColor = self.command.getCursorText()

                if position is None:
                    self.glpane.renderTextNearCursor(text,
                                                     offset = 30,
                                                     textColor = textColor,
                                                     fontSize = env.prefs[cursorTextFontSize_prefs_key])
                else:
                    self.glpane.renderTextAtPosition(position,
                                                     text,
                                                     textColor = textColor,
                                                     fontSize = env.prefs[cursorTextFontSize_prefs_key])


    def _drawLabels(self):
        """
        Overrides superclass method
        @see: self.Draw_other()
        @see: drawDnaLabels.py
        """
        _superclass._drawLabels(self)
        #Draw the Dna base number labels.
        draw_dnaBaseNumberLabels(self.glpane)