class DnaSegment_EditCommand(State_preMixin, EditCommand):
    """
    Command to edit a DnaSegment object. 
    To edit a segment, first enter BuildDna_EditCommand (accessed using Build> Dna) 
    then, select an axis chunk of an existing DnaSegment  within the DnaGroup you
    are editing. When you select the axis chunk, it enters DnaSegment_Editcommand
    and shows the property manager with its widgets showing the properties of 
    selected segment.
    """
    cmd = 'Dna Segment'
    sponsor_keyword = 'DNA'
    prefix = 'Segment '  # used for gensym
    cmdname = "DNA_SEGMENT"
    commandName = 'DNA_SEGMENT'
    featurename = 'Edit Dna Segment'

    command_should_resume_prevMode = True
    command_has_its_own_gui = True
    command_can_be_suspended = False

    # Generators for DNA, nanotubes and graphene have their MT name
    # generated (in GeneratorBaseClass) from the prefix.
    create_name_from_prefix = True

    call_makeMenus_for_each_event = True

    #Graphics Mode
    GraphicsMode_class = DnaSegment_GraphicsMode

    #This is set to BuildDna_EditCommand.flyoutToolbar (as of 2008-01-14,
    #it only uses
    flyoutToolbar = None

    handlePoint1 = State(Point, ORIGIN)
    handlePoint2 = State(Point, ORIGIN)
    #The minimum 'stopper'length used for resize handles
    #@see: self._update_resizeHandle_stopper_length for details.
    _resizeHandle_stopper_length = State(Width, -100000)

    rotationHandleBasePoint1 = State(Point, ORIGIN)
    rotationHandleBasePoint2 = State(Point, ORIGIN)

    #See self._update_resizeHandle_radius where this gets changed.
    #also see DnaSegment_ResizeHandle to see how its implemented.
    handleSphereRadius1 = State(Width, HANDLE_RADIUS_DEFAULT_VALUE)
    handleSphereRadius2 = State(Width, HANDLE_RADIUS_DEFAULT_VALUE)

    cylinderWidth = State(Width, CYLINDER_WIDTH_DEFAULT_VALUE)
    cylinderWidth2 = State(Width, CYLINDER_WIDTH_DEFAULT_VALUE)

    #@TODO: modify the 'State params for rotation_distance
    rotation_distance1 = State(Width, CYLINDER_WIDTH_DEFAULT_VALUE)
    rotation_distance2 = State(Width, CYLINDER_WIDTH_DEFAULT_VALUE)

    duplexRise = getDuplexRise('B-DNA')

    leftHandle = Instance(
        DnaSegment_ResizeHandle(
            command=_self,
            height_ref=call_Expr(ObjAttr_StateRef, _self, 'cylinderWidth'),
            origin=handlePoint1,
            fixedEndOfStructure=handlePoint2,
            direction=norm_Expr(handlePoint1 - handlePoint2),
            sphereRadius=handleSphereRadius1,
            discRadius=env.prefs[dnaSegmentResizeHandle_discRadius_prefs_key],
            discThickness=env.
            prefs[dnaSegmentResizeHandle_discThickness_prefs_key],
            range=(_resizeHandle_stopper_length, 10000)))

    rightHandle = Instance(
        DnaSegment_ResizeHandle(
            command=_self,
            height_ref=call_Expr(ObjAttr_StateRef, _self, 'cylinderWidth2'),
            origin=handlePoint2,
            fixedEndOfStructure=handlePoint1,
            direction=norm_Expr(handlePoint2 - handlePoint1),
            sphereRadius=handleSphereRadius2,
            discRadius=env.prefs[dnaSegmentResizeHandle_discRadius_prefs_key],
            discThickness=env.
            prefs[dnaSegmentResizeHandle_discThickness_prefs_key],
            range=(_resizeHandle_stopper_length, 10000)))

    rotationHandle1 = Instance(
        RotationHandle(command=_self,
                       rotationDistanceRef=call_Expr(ObjAttr_StateRef, _self,
                                                     'rotation_distance1'),
                       center=handlePoint1,
                       axis=norm_Expr(handlePoint1 - handlePoint2),
                       origin=rotationHandleBasePoint1,
                       radiusVector=norm_Expr(rotationHandleBasePoint1 -
                                              handlePoint1)))

    rotationHandle2 = Instance(
        RotationHandle(command=_self,
                       rotationDistanceRef=call_Expr(ObjAttr_StateRef, _self,
                                                     'rotation_distance2'),
                       center=handlePoint2,
                       axis=norm_Expr(handlePoint2 - handlePoint1),
                       origin=rotationHandleBasePoint2,
                       radiusVector=norm_Expr(rotationHandleBasePoint2 -
                                              handlePoint2)))

    def __init__(self, commandSequencer, struct=None):
        """
        Constructor for DnaDuplex_EditCommand
        """

        glpane = commandSequencer
        State_preMixin.__init__(self, glpane)
        EditCommand.__init__(self, commandSequencer)
        self.struct = struct

        #Graphics handles for editing the structure .
        self.handles = []
        self.grabbedHandle = None

    def init_gui(self):
        """
        Initialize gui. 
        """

        #Note that DnaSegment_EditCommand only act as an edit command for an
        #existing structure. The call to self.propMgr.show() is done only during
        #the call to self.editStructure ..i .e. only after self.struct is
        #updated. This is done because of the following reason:
        # - self.init_gui is called immediately after entering the command.
        # - self.init_gui in turn, initialized propMgr object and may also
        #  show the property manager. The self.propMgr.show routine calls
        #  an update widget method just before the show. This update method
        #  updates the widgets based on the parameters from the existing
        #  structure of the command (self.editCommand.struct)
        #  Although, it checks whether this structure exists, the editCommand
        #  could still have a self.struct attr from a previous run. (Note that
        #  EditCommand API was written before the command sequencer API and
        #  it has some loose ends like this. ) -- Ninad 2008-01-22
        self.create_and_or_show_PM_if_wanted(showPropMgr=False)

    def model_changed(self):
        #This MAY HAVE BUG. WHEN --
        #debug pref 'call model_changed only when needed' is ON
        #See related bug 2729 for details.

        #The following code that updates te handle positions and the strand
        #sequence fixes bugs like 2745 and updating the handle positions
        #updating handle positions in model_changed instead of in
        #self.graphicsMode._draw_handles() is also a minor optimization
        #This can be further optimized by debug pref
        #'call model_changed only when needed' but its NOT done because of an
        # issue menitoned in bug 2729   - Ninad 2008-04-07
        EditCommand.model_changed(self)  #This also calls the
        #propMgr.model_changed

        if self.grabbedHandle is not None:
            return

        #For Rattlesnake, PAM5 segment resizing  is not supported.
        #@see: self.hasResizableStructure()
        if self.hasValidStructure():
            isStructResizable, why_not = self.hasResizableStructure()
            if not isStructResizable:
                self.handles = []
                return
            elif len(self.handles) == 0:
                self._updateHandleList()

            self.updateHandlePositions()

            self._update_previousParams_in_model_changed()

    def _update_previousParams_in_model_changed(self):
        #The following fixes bug 2802. The bug comment has details of what
        #it does. Copying some portion of it below--
        #We have fixed similar problem for strand resizing, by updating the
        #self.previousParams attr in model_changed method (and also updating
        #the numberOfBasePairs spinbox in the PM. But here, user can even
        #change the number of basepairs from the PM. When he does that,
        #the model_changed is called and it resets the number of basepairs
        #spinbox value with  the ones currently on the structure! Thereby
        #making it impossible to upate structure using spinbox.  To fix this
        #we introduce a new parameter in propMgr.getParameters() which
        #reports the actual number of bases on the structure.
        #-- Ninad 2008-04-12
        if self.previousParams is not None:
            new_numberOfBasePairs = self.struct.getNumberOfBasePairs()
            if new_numberOfBasePairs != self.previousParams[0]:
                self.propMgr.numberOfBasePairsSpinBox.setValue(
                    new_numberOfBasePairs)
                self.previousParams = self.propMgr.getParameters()

    def editStructure(self, struct=None):
        EditCommand.editStructure(self, struct)
        if self.hasValidStructure():
            self._updatePropMgrParams()

            #Store the previous parameters. Important to set it after you
            #set duplexRise and basesPerTurn attrs in the propMgr.
            #self.previousParams is used in self._previewStructure and
            #self._finalizeStructure to check if self.struct changed.
            self.previousParams = self._gatherParameters()

            #For Rattlesnake, we do not support resizing of PAM5 model.
            #So don't append the exprs handles to the handle list (and thus
            #don't draw those handles. See self.model_changed()
            isStructResizable, why_not = self.hasResizableStructure()
            if not isStructResizable:
                self.handles = []
            else:
                self._updateHandleList()
                self.updateHandlePositions()

    def _updatePropMgrParams(self):
        """
        Subclasses may override this method. 
        Update some property manager parameters with the parameters of
        self.struct (which is being edited)
        @see: self.editStructure()
        """

        #Format in which params need to be provided to the Property manager
        #(i.e. in propMgr.setParameters():
        #numberOfBasePairs,
        #dnaForm,
        #dnaModel,
        #basesPerTurn,
        #duplexRise,
        #endPoint1,
        #endPoint2

        basesPerTurn, duplexRise = self.struct.getProps()
        endPoint1, endPoint2 = self.struct.getAxisEndPoints()
        numberOfBasePairs = self.struct.getNumberOfBasePairs()
        color = self.struct.getColor()

        params_for_propMgr = (numberOfBasePairs, None, None, basesPerTurn,
                              duplexRise, endPoint1, endPoint2, color)

        #TODO 2008-03-25: better to get all parameters from self.struct and
        #set it in propMgr?  This will mostly work except that reverse is
        #not true. i.e. we can not specify same set of params for
        #self.struct.setProps ...because endPoint1 and endPoint2 are derived.
        #by the structure when needed. Commenting out following line of code
        #UPDATE 2008-05-06 Fixes a bug due to which the parameters in propMGr
        #of DnaSegment_EditCommand are not same as the original structure
        #(e.g. bases per turn and duplexrise)
        self.propMgr.setParameters(params_for_propMgr)

    def keep_empty_group(self, group):
        """
        Returns True if the empty group should not be automatically deleted. 
        otherwise returns False. The default implementation always returns 
        False. Subclasses should override this method if it needs to keep the
        empty group for some reasons. Note that this method will only get called
        when a group has a class constant autdelete_when_empty set to True. 
        (and as of 2008-03-06, it is proposed that dna_updater calls this method
        when needed. 
        @see: Command.keep_empty_group() which is overridden here. 
        @see: BreakStrands_Command.keep_empty_group
        @see: Group.autodelete_when_empty.. a class constant used by the 
              dna_updater (the dna updater then decides whether to call this 
              method to see which empty groups need to be deleted)
        """

        bool_keep = EditCommand.keep_empty_group(self, group)

        if not bool_keep:
            if self.hasValidStructure():
                if group is self.struct:
                    bool_keep = True
                elif group is self.struct.parent_node_of_class(
                        self.assy.DnaGroup):
                    bool_keep = True
            #If this command doesn't have a valid structure, as a fall back,
            #lets instruct it to keep ALL the DnaGroup objects even when empty
            #Reason? ..see explanation in BreakStrands_Command.keep_empty_group
            elif isinstance(group, self.assy.DnaGroup):
                bool_keep = True

        return bool_keep

    def hasResizableStructure(self):
        """
        For Rattlesnake release, we dont support segment resizing for PAM5 
        models. If the structure is not resizable, the handles won't be drawn
        @see:self.model_changed()
        @see:DnaSegment_PropertyManager.model_changed()
        @see: self.editStructure()
        @see: DnaSegment.is_PAM3_DnaSegment()
        """
        #Note: This method fixes bugs similar to bug 2812 but the changes
        #didn't made it to Rattlesnake rc2 -- Ninad 2008-04-16
        isResizable = True
        why_not = ''

        if not self.hasValidStructure():
            isResizable = False
            why_not = 'It is invalid.'
            return isResizable, why_not

        isResizable = self.struct.is_PAM3_DnaSegment()
        if not isResizable:
            why_not = 'It needs to be converted to PAM3 model'
            return isResizable, why_not

        endAtom1, endAtom2 = self.struct.getAxisEndAtoms()

        if endAtom1 is None or endAtom2 is None:
            isResizable = False
            why_not = "Unable to determine one or both end atoms of the segment"
            return isResizable, why_not

        if endAtom1 is endAtom2:
            isResizable = False
            why_not = "Resizing a segment with single atom is unsupported"
            return isResizable, why_not

        return isResizable, why_not

    def hasValidStructure(self):
        """
        Tells the caller if this edit command has a valid structure. 
        Overrides EditCommand.hasValidStructure()
        """
        #(By Bruce 2008-02-13)

        isValid = EditCommand.hasValidStructure(self)

        if not isValid:
            return isValid

        # would like to check here whether it's empty of axis chunks;
        # instead, this will do for now (probably too slow, though):
        p1, p2 = self.struct.getAxisEndPoints()
        return (p1 is not None)

    def _getStructureType(self):
        """
        Subclasses override this method to define their own structure type. 
        Returns the type of the structure this editCommand supports. 
        This is used in isinstance test. 
        @see: EditCommand._getStructureType() (overridden here)
        """
        return self.win.assy.DnaSegment

    def _updateHandleList(self):
        """        
        Updates the list of handles (self.handles) 
        @see: self.editStructure
        @see: DnaSegment_GraphicsMode._drawHandles()
        """
        # note: if handlePoint1 and/or handlePoint2 can change more often than this
        # runs, we'll need to rerun the two assignments above whenever they
        # change and before the handle is drawn. An easy way would be to rerun
        # these assignments in the draw method of our GM. [bruce 080128]
        self.handles = []  # guess, but seems like a good idea [bruce 080128]
        self.handles.append(self.leftHandle)
        self.handles.append(self.rightHandle)
        if DEBUG_ROTATION_HANDLES:
            self.handles.append(self.rotationHandle1)
            self.handles.append(self.rotationHandle2)

    def updateHandlePositions(self):
        """
        Update handle positions and also update the resize handle radii and
        their 'stopper' lengths. 
        @see: self._update_resizeHandle_radius()
        @see: self._update_resizeHandle_stopper_length()     
        @see: DnaSegment_GraphicsMode._drawHandles()
        """

        if len(self.handles) == 0:
            #No handles are appended to self.handles list.
            #@See self.model_changed() and self._updateHandleList()
            return

        #TODO: Call this method less often by implementing model_changed
        #see bug 2729 for a planned optimization
        self.cylinderWidth = CYLINDER_WIDTH_DEFAULT_VALUE
        self.cylinderWidth2 = CYLINDER_WIDTH_DEFAULT_VALUE

        self._update_resizeHandle_radius()

        handlePoint1, handlePoint2 = self.struct.getAxisEndPoints()

        if handlePoint1 is not None and handlePoint2 is not None:
            # (that condition is bugfix for deleted axis segment, bruce 080213)

            self.handlePoint1, self.handlePoint2 = handlePoint1, handlePoint2

            #Update the 'stopper'  length where the resize handle being dragged
            #should stop. See self._update_resizeHandle_stopper_length()
            #for more details
            self._update_resizeHandle_stopper_length()

            if DEBUG_ROTATION_HANDLES:
                self.rotation_distance1 = CYLINDER_WIDTH_DEFAULT_VALUE
                self.rotation_distance2 = CYLINDER_WIDTH_DEFAULT_VALUE
                #Following computes the base points for rotation handles.
                #to be revised -- Ninad 2008-02-13
                unitVectorAlongAxis = norm(self.handlePoint1 -
                                           self.handlePoint2)

                v = cross(self.glpane.lineOfSight, unitVectorAlongAxis)

                self.rotationHandleBasePoint1 = self.handlePoint1 + norm(
                    v) * 4.0
                self.rotationHandleBasePoint2 = self.handlePoint2 + norm(
                    v) * 4.0

    def _update_resizeHandle_radius(self):
        """
        Finds out the sphere radius to use for the resize handles, based on 
        atom /chunk or glpane display (whichever decides the display of the end 
        atoms.  


        @see: self.updateHandlePositions()
        @see: B{Atom.drawing_radius()}
        """
        atm1, atm2 = self.struct.getAxisEndAtoms()
        if atm1 is not None:
            self.handleSphereRadius1 = max(1.25 * atm1.drawing_radius(),
                                           1.25 * HANDLE_RADIUS_DEFAULT_VALUE)
        if atm2 is not None:
            self.handleSphereRadius2 = max(1.25 * atm2.drawing_radius(),
                                           1.25 * HANDLE_RADIUS_DEFAULT_VALUE)

    def _update_resizeHandle_stopper_length(self):
        """
        Update the limiting length at which the resize handle being dragged
        should 'stop'  without proceeding further in the drag direction. 
        The segment resize handle stops when you are dragging it towards the 
        other resizeend and the distance between the two ends reaches two 
        duplexes. 

        The self._resizeHandle_stopper_length computed in this method is 
        used as a lower limit of the 'range' option provided in declaration
        of resize handle objects (see class definition for the details)
        @see: self.updateHandlePositions()
        """

        total_length = vlen(self.handlePoint1 - self.handlePoint2)
        duplexRise = self.struct.getDuplexRise()

        #Length of the duplex for 2 base pairs
        two_bases_length = getDuplexLength('B-DNA', 2, duplexRise=duplexRise)

        self._resizeHandle_stopper_length = -total_length + two_bases_length

    def _createPropMgrObject(self):
        """
        Creates a property manager object (that defines UI things) for this 
        editCommand. 
        """
        assert not self.propMgr
        propMgr = self.win.createDnaSegmentPropMgr_if_needed(self)
        return propMgr

    def _gatherParameters(self):
        """
        Return the parameters from the property manager UI.

        @return: All the parameters (get those from the property manager):
                 - numberOfBases
                 - dnaForm
                 - basesPerTurn
                 - endPoint1
                 - endPoint2
        @rtype:  tuple
        """
        return self.propMgr.getParameters()

    def _createStructure(self):
        """
        Creates and returns the structure (in this case a L{Group} object that 
        contains the DNA strand and axis chunks. 
        @return : group containing that contains the DNA strand and axis chunks.
        @rtype: L{Group}  
        @note: This needs to return a DNA object once that model is implemented        
        """

        params = self._gatherParameters()

        # No error checking in build_struct, do all your error
        # checking in gather_parameters
        number_of_basePairs_from_struct,\
                                       numberOfBases, \
                                       dnaForm, \
                                       dnaModel, \
                                       basesPerTurn, \
                                       duplexRise, \
                                       endPoint1, \
                                       endPoint2, \
                                       color_junk = params
        #Note: color_junk is not used. Ideally it should do struct.setColor(color)
        #but the color combobox in the PM directly sets the color of the
        #structure to the specified one when the current index in the combobx
        #changes

        #If user enters the number of basepairs and hits preview i.e. endPoint1
        #and endPoint2 are not entered by the user and thus have default value
        #of V(0, 0, 0), then enter the endPoint1 as V(0, 0, 0) and compute
        #endPoint2 using the duplex length.
        #Do not use '==' equality check on vectors! its a bug. Use same_vals
        # or Veq instead.
        if Veq(endPoint1, endPoint2) and Veq(endPoint1, V(0, 0, 0)):
            endPoint2 = endPoint1 + \
                      self.win.glpane.right*getDuplexLength('B-DNA',
                                                            numberOfBases)

        if numberOfBases < 1:
            msg = redmsg("Cannot preview/insert a DNA duplex with 0 bases.")
            self.propMgr.updateMessage(msg)
            self.dna = None  # Fixes bug 2530. Mark 2007-09-02
            return None

        if dnaForm == 'B-DNA':
            if dnaModel == 'PAM3':
                dna = B_Dna_PAM3()
            elif dnaModel == 'PAM5':
                dna = B_Dna_PAM5()
            else:
                print "bug: unknown dnaModel type: ", dnaModel
        else:
            raise PluginBug("Unsupported DNA Form: " + dnaForm)

        self.dna = dna  # needed for done msg

        # self.name needed for done message
        if self.create_name_from_prefix:
            # create a new name
            name = self.name = gensym(self.prefix,
                                      self.win.assy)  # (in _build_struct)
            self._gensym_data_for_reusing_name = (self.prefix, name)
        else:
            # use externally created name
            self._gensym_data_for_reusing_name = None
            # (can't reuse name in this case -- not sure what prefix it was
            #  made with)
            name = self.name

        # Create the model tree group node.
        # Make sure that the 'topnode'  of this part is a Group (under which the
        # DNa group will be placed), if the topnode is not a group, make it a
        # a 'Group' (applicable to Clipboard parts).See part.py
        # --Part.ensure_toplevel_group method. This is an important line
        # and it fixes bug 2585
        self.win.assy.part.ensure_toplevel_group()
        dnaSegment = DnaSegment(self.name,
                                self.win.assy,
                                self.win.assy.part.topnode,
                                editCommand=self)
        try:
            # Make the DNA duplex. <dnaGroup> will contain three chunks:
            #  - Strand1
            #  - Strand2
            #  - Axis

            dna.make(dnaSegment, numberOfBases, basesPerTurn, duplexRise,
                     endPoint1, endPoint2)

            #set some properties such as duplexRise and number of bases per turn
            #This information will be stored on the DnaSegment object so that
            #it can be retrieved while editing this object.
            #This works with or without dna_updater. Now the question is
            #should these props be assigned to the DnaSegment in
            #dnaDuplex.make() itself ? This needs to be answered while modifying
            #make() method to fit in the dna data model. --Ninad 2008-03-05

            #WARNING 2008-03-05: Since self._modifyStructure calls
            #self._createStructure()
            #If in the near future, we actually permit modifying a
            #structure (such as dna) without actually recreating the whole
            #structre, then the following properties must be set in
            #self._modifyStructure as well. Needs more thought.
            props = (duplexRise, basesPerTurn)
            dnaSegment.setProps(props)

            return dnaSegment

        except (PluginBug, UserError):
            # Why do we need UserError here? Mark 2007-08-28
            dnaSegment.kill()
            raise PluginBug(
                "Internal error while trying to create DNA duplex.")

    def _modifyStructure(self, params):
        """
        Modify the structure based on the parameters specified. 
        Overrides EditCommand._modifystructure. This method removes the old 
        structure and creates a new one using self._createStructure. This 
        was needed for the structures like this (Dna, Nanotube etc) . .
        See more comments in the method.
        """
        assert self.struct

        self.dna = B_Dna_PAM3()

        number_of_basePairs_from_struct,\
                                       numberOfBases, \
                                       dnaForm, \
                                       dnaModel, \
                                       basesPerTurn, \
                                       duplexRise, \
                                       endPoint1, \
                                       endPoint2 , \
                                       color = params

        #Delete unused parameters.
        del endPoint1
        del endPoint2
        del number_of_basePairs_from_struct

        numberOfBasePairsToAddOrRemove = self._determine_numberOfBasePairs_to_change(
        )

        ladderEndAxisAtom = self.get_axisEndAtom_at_resize_end()

        if numberOfBasePairsToAddOrRemove != 0:

            resizeEnd_final_position = self._get_resizeEnd_final_position(
                ladderEndAxisAtom, abs(numberOfBasePairsToAddOrRemove),
                duplexRise)

            self.dna.modify(self.struct, ladderEndAxisAtom,
                            numberOfBasePairsToAddOrRemove, basesPerTurn,
                            duplexRise, ladderEndAxisAtom.posn(),
                            resizeEnd_final_position)

        #Find new end points of structure parameters after modification
        #and set these values in the propMgr.
        new_end1, new_end2 = self.struct.getAxisEndPoints()

        params_to_set_in_propMgr = (numberOfBases, dnaForm, dnaModel,
                                    basesPerTurn, duplexRise, new_end1,
                                    new_end2, color)

        #TODO: Need to set these params in the PM
        #and then self.previousParams = params_to_set_in_propMgr

        self.previousParams = params

        return

    def _get_resizeEnd_final_position(self, ladderEndAxisAtom, numberOfBases,
                                      duplexRise):

        final_position = None
        if self.grabbedHandle:
            final_position = self.grabbedHandle.currentPosition
        else:
            other_axisEndAtom = self.struct.getOtherAxisEndAtom(
                ladderEndAxisAtom)
            axis_vector = ladderEndAxisAtom.posn() - other_axisEndAtom.posn()
            segment_length_to_add = getDuplexLength('B-DNA',
                                                    numberOfBases,
                                                    duplexRise=duplexRise)

            final_position = ladderEndAxisAtom.posn(
            ) + norm(axis_vector) * segment_length_to_add

        return final_position

    def getStructureName(self):
        """
        Returns the name string of self.struct if there is a valid structure. 
        Otherwise returns None. This information is used by the name edit field 
        of  this command's PM when we call self.propMgr.show()
        @see: DnaSegment_PropertyManager.show()
        @see: self.setStructureName
        """
        if self.hasValidStructure():
            return self.struct.name
        else:
            return None

    def setStructureName(self, name):
        """
        Sets the name of self.struct to param <name> (if there is a valid 
        structure. 
        The PM of this command callss this method while closing itself 
        @param name: name of the structure to be set.
        @type name: string
        @see: DnaSegment_PropertyManager.close()
        @see: self.getStructureName()

        """
        #@BUG: We call this method in self.propMgr.close(). But propMgr.close()
        #is called even when the command is 'cancelled'. That means the
        #structure will get changed even when user hits cancel button or
        #exits the command by clicking on empty space.
        #This should really be done in self._finalizeStructure but that
        #method doesn't get called when you click on empty space to exit
        #the command. See DnaSegment_GraphicsMode.leftUp for a detailed
        #comment.

        if self.hasValidStructure():
            self.struct.name = name

    def getCursorText(self):
        """
        This is used as a callback method in DnaLine mode 
        @see: DnaLineMode.setParams, DnaLineMode_GM.Draw
        """
        #@TODO: Refactor this. Similar code exists in
        #DnaStrand_EditCommand.getCursorText() -- Ninad 2008-04-12
        if self.grabbedHandle is None:
            return

        currentPosition = self.grabbedHandle.currentPosition
        fixedEndOfStructure = self.grabbedHandle.fixedEndOfStructure

        duplexRise = self.struct.getDuplexRise()

        #############

        raw_numberOfBasePairsToAddOrRemove = self._determine_numberOfBasePairs_to_change(
        )

        #Following fixes bugs like 2904 and 2906
        #Note that we are using numberOfBasePairsToAddOrRemove in self._modifyStructure()
        #if self._determine_numberOfBasePairs_to_change() returns the number of basepairs
        #to add, it returns 1 more than the actual number of basepairs. Because
        #while creating the dna, it removes the first base pair of the newly created
        #dna. So, for cursor text and for PM spinbox, we should make adjustments to the
        #raw_numberOfBasePairsToAddOrRemove so that it reflects the correct value
        #in the spinbox and in the PM

        if raw_numberOfBasePairsToAddOrRemove > 1:
            numberOfBasePairsToAddOrRemove = raw_numberOfBasePairsToAddOrRemove - 1

        else:
            numberOfBasePairsToAddOrRemove = raw_numberOfBasePairsToAddOrRemove

        ##############

        current_numberOfBasePairs = self.struct.getNumberOfBasePairs()

        numberOfBasePairs = current_numberOfBasePairs + numberOfBasePairsToAddOrRemove
        #@TODO: The following updates the PM as the cursor moves.
        #Need to rename this method so that you that it also does more things
        #than just to return a textString -- Ninad 2007-12-20
        self.propMgr.numberOfBasePairsSpinBox.setValue(numberOfBasePairs)

        #Note: for Rattlesnake rc2, the text color is green when bases are added
        #, red when subtracted black when no change. But this implementation is
        #changed based on Mark's user experience. The text is now always shown
        #in black color. -- Ninad 2008-04-17
        textColor = black

        text = ""

        if not env.prefs[
                dnaSegmentEditCommand_showCursorTextCheckBox_prefs_key]:
            return '', black

        #@@TODO: refactor.
        #this duplex length canculation fixes bug 2906
        duplexLength = getDuplexLength('B-DNA',
                                       numberOfBasePairs,
                                       duplexRise=duplexRise)

        #Cursor text strings --
        duplexLengthString = str(round(duplexLength, 3))

        numberOfBasePairsString = self._getCursorText_numberOfBasePairs(
            numberOfBasePairs)

        duplexLengthString = self._getCursorText_length(duplexLength)

        changedBasePairsString = self._getCursorText_changedBasePairs(
            numberOfBasePairs)

        #Add commas (to be refactored)
        commaString = ", "
        text = numberOfBasePairsString

        if text and changedBasePairsString:
            text += " "  # commaString not needed here. Mark 2008-07-03

        text += changedBasePairsString

        if text and duplexLengthString:
            text += commaString

        text += duplexLengthString

        return (text, textColor)

    def _getCursorText_numberOfBasePairs(self, numberOfBasePairs):
        """
        Return the cursor textstring that gives information about the number 
        of basepairs if the corresponding prefs_key returns True.
        """
        numberOfBasePairsString = ''

        if env.prefs[
                dnaSegmentEditCommand_cursorTextCheckBox_numberOfBasePairs_prefs_key]:
            numberOfBasePairsString = "%db" % numberOfBasePairs

        return numberOfBasePairsString

    def _getCursorText_length(self, duplexLength):
        """
        """
        duplexLengthString = ''
        if env.prefs[
                dnaSegmentEditCommand_cursorTextCheckBox_length_prefs_key]:
            lengthUnitString = 'A'
            #change the unit of length to nanometers if the length is > 10A
            #fixes part of bug 2856
            if duplexLength > 10.0:
                lengthUnitString = 'nm'
                duplexLength = duplexLength * 0.1
            duplexLengthString = "%5.3f%s" % (duplexLength, lengthUnitString)

        return duplexLengthString

    def _getCursorText_changedBasePairs(self, numberOfBasePairs):
        """
        """
        changedBasePairsString = ''

        if env.prefs[
                dnaSegmentEditCommand_cursorTextCheckBox_changedBasePairs_prefs_key]:

            original_numberOfBasePairs = self.struct.getNumberOfBasePairs()

            changed_basePairs = numberOfBasePairs - original_numberOfBasePairs

            if changed_basePairs > 0:
                changedBasePairsString = "(" + "+" + str(
                    changed_basePairs) + ")"
            else:
                changedBasePairsString = "(" + str(changed_basePairs) + ")"

        return changedBasePairsString

    def getDnaRibbonParams(self):
        """
        Returns parameters for drawing the dna ribbon. 

        If the dna rubberband line should NOT be drawn (example when you are 
        removing basepairs from the segment 
        So the caller should check if the method return value is not None. 
        @see: DnaSegment_GraphicsMode._draw_handles()
        """

        if self.grabbedHandle is None:
            return None

        if self.grabbedHandle.origin is None:
            return None

        direction_of_drag = norm(self.grabbedHandle.currentPosition - \
                                 self.grabbedHandle.origin)

        #If the segment is being shortened (determined by checking the
        #direction of drag) , no need to draw the rubberband line.
        if dot(self.grabbedHandle.direction, direction_of_drag) < 0:
            return None

        basesPerTurn = self.struct.getBasesPerTurn()
        duplexRise = self.struct.getDuplexRise()

        ladderEndAxisAtom = self.get_axisEndAtom_at_resize_end()
        ladder = ladderEndAxisAtom.molecule.ladder
        endBaseAtomList = ladder.get_endBaseAtoms_containing_atom(
            ladderEndAxisAtom)
        ribbon1_start_point = None
        ribbon2_start_point = None
        ribbon1_direction = None
        ribbon2_direction = None

        ribbon1Color = applegreen
        ribbon2Color = applegreen

        if endBaseAtomList and len(endBaseAtomList) > 2:
            strand_atom1 = endBaseAtomList[0]
            strand_atom2 = endBaseAtomList[2]

            if strand_atom1:
                ribbon1_start_point = strand_atom1.posn()
                for bond_direction, neighbor in strand_atom1.bond_directions_to_neighbors(
                ):
                    if neighbor and neighbor.is_singlet():
                        ribbon1_direction = bond_direction
                        break

                ribbon1Color = strand_atom1.molecule.color
                if not ribbon1Color:
                    ribbon1Color = strand_atom1.element.color

            if strand_atom2:
                ribbon2_start_point = strand_atom2.posn()
                for bond_direction, neighbor in strand_atom2.bond_directions_to_neighbors(
                ):
                    if neighbor and neighbor.is_singlet():
                        ribbon2_direction = bond_direction
                        break
                ribbon2Color = strand_atom2.molecule.color
                if not ribbon2Color:
                    ribbon2Color = strand_atom2.element.color

        return (self.grabbedHandle.origin, self.grabbedHandle.currentPosition,
                basesPerTurn, duplexRise, ribbon1_start_point,
                ribbon2_start_point, ribbon1_direction, ribbon2_direction,
                ribbon1Color, ribbon2Color)

    def modifyStructure(self):
        """

        Called when a resize handle is dragged to change the length of the 
        segment. (Called upon leftUp) . This method assigns the new parameters 
        for the segment after it is resized and calls 
        preview_or_finalize_structure which does the rest of the job. 
        Note that Client should call this public method and should never call
        the private method self._modifyStructure. self._modifyStructure is 
        called only by self.preview_or_finalize_structure

        @see: B{DnaSegment_ResizeHandle.on_release} (the caller)
        @see: B{SelectChunks_GraphicsMode.leftUp} (which calls the 
              the relevent method in DragHandler API. )
        @see: B{exprs.DraggableHandle_AlongLine}, B{exprs.DragBehavior}
        @see: B{self.preview_or_finalize_structure }
        @see: B{self._modifyStructure}        

        As of 2008-02-01 it recreates the structure
        @see: a note in self._createStructure() about use of dnaSegment.setProps 
        """
        #TODO: need to cleanup this and may be use use something like
        #self.previousParams = params in the end -- 2008-03-24 (midnight)

        if self.grabbedHandle is None:
            return

        ##self.propMgr.setParameters(params_to_set_in_propMgr)
        #TODO: Important note: How does NE1 know that structure is modified?
        #Because number of base pairs parameter in the PropMgr changes as you
        #drag the handle . This is done in self.getCursorText() ... not the
        #right place to do it. OR that method needs to be renamed to reflect
        #this as suggested in that method -- Ninad 2008-03-25

        self.preview_or_finalize_structure(previewing=True)

        ##self.previousParams = params_to_set_in_propMgr

        self.glpane.gl_update()

    def get_axisEndAtom_at_resize_end(self):
        ladderEndAxisAtom = None
        if self.grabbedHandle is not None:
            ladderEndAxisAtom = self.struct.getAxisEndAtomAtPosition(
                self.grabbedHandle.origin)
        else:
            endAtom1, endAtom2 = self.struct.getAxisEndAtoms()
            ladderEndAxisAtom = endAtom2

        return ladderEndAxisAtom

    def _determine_numberOfBasePairs_to_change(self):
        """
        """

        duplexRise = self.struct.getDuplexRise()
        numberOfBasesToAddOrRemove = 0
        #Following helps fixing bugs like 2904 and 2906 see also self.getCursorText()
        #and TODO items in that method. Also note that the grabbed handle case
        #is similar to the one in MultipleDnaSegmentResize_EditCommand.
        #needs refactoring and overall cleanup.
        if self.grabbedHandle is not None:

            currentPosition = self.grabbedHandle.currentPosition
            fixedEndOfStructure = self.grabbedHandle.fixedEndOfStructure

            changedLength = vlen(currentPosition - self.grabbedHandle.origin)

            direction_of_drag = norm(self.grabbedHandle.currentPosition - \
                                     self.grabbedHandle.origin)

            #Even when the direction of drag is negative (i.e. the basepairs being
            #removed), make sure not to remove base pairs for very small movement
            #of the grabbed handle
            if changedLength < 0.2 * duplexRise:
                return 0

            #This check quickly determines if the grabbed handle moved by a distance
            #more than the duplexRise and avoids further computations
            #This condition is applicable only when the direction of drag is
            #positive..i.e. bases bing added to the segment.
            if changedLength < duplexRise and \
               dot(self.grabbedHandle.direction, direction_of_drag) > 0:
                return 0

            #If the segment is being shortened (determined by checking the
            #direction of drag)

            numberOfBasesToAddOrRemove =  \
                                       getNumberOfBasePairsFromDuplexLength(
                                           'B-DNA',
                                           changedLength,
                                           duplexRise = duplexRise)

            if dot(self.grabbedHandle.direction, direction_of_drag) < 0:
                numberOfBasesToAddOrRemove = -numberOfBasesToAddOrRemove

            if numberOfBasesToAddOrRemove > 0:
                #dna.modify will remove the first base pair it creates
                #(that basepair will only be used for proper alignment of the
                #duplex with the existing structure) So we need to compensate for
                #this basepair by adding 1 to the new number of base pairs.

                #UPDATE 2008-05-14: The following commented out code
                #i.e. "##numberOfBasesToAddOrRemove += 1" is not required in this
                #class , because the way we compute the number of base pairs to
                #be added is different than than how its done at the moment in the
                #superclass. In this method, we compute bases to be added from
                #the resize end and that computation INCLUDES the resize end.
                #so the number that it returns is already one more than the actual
                #bases to be added. so commenting out the following line
                # -- Ninad 2008-05-14
                ##numberOfBasesToAddOrRemove += 1
                pass
        else:
            #The Property manager will be showing the current number
            #of base pairs (w. May be we can use that number directly here?
            #The following is  safer to do so lets just recompute the
            #number of base pairs. (if it turns out to be slow, we will consider
            #using the already computed calue from the property manager
            new_numberOfBasePairs = self.propMgr.numberOfBasePairsSpinBox.value(
            )

            endPoint1, endPoint2 = self.struct.getAxisEndPoints()
            if endPoint1 is None or endPoint2 is None:
                return 0

            original_duplex_length = vlen(endPoint1 - endPoint2)

            original_numberOfBasePairs = self.struct.getNumberOfBasePairs()

            numberOfBasesToAddOrRemove = new_numberOfBasePairs - original_numberOfBasePairs

            if numberOfBasesToAddOrRemove > 0:
                #dna.modify will remove the first base pair it creates
                #(that basepair will only be used for proper alignment of the
                #duplex with the existing structure) So we need to compensate for
                #this basepair by adding 1 to the new number of base pairs.
                numberOfBasesToAddOrRemove += 1

        return numberOfBasesToAddOrRemove

    def makeMenus(self):
        """
        Create context menu for this command. (Build Dna mode)
        """
        if not hasattr(self, 'graphicsMode'):
            return

        selobj = self.glpane.selobj

        if selobj is None:
            return

        self.Menu_spec = []

        highlightedChunk = None
        if isinstance(selobj, Chunk):
            highlightedChunk = selobj
        if isinstance(selobj, Atom):
            highlightedChunk = selobj.molecule
        elif isinstance(selobj, Bond):
            chunk1 = selobj.atom1.molecule
            chunk2 = selobj.atom2.molecule
            if chunk1 is chunk2 and chunk1 is not None:
                highlightedChunk = chunk1

        if highlightedChunk is None:
            return

        if self.hasValidStructure():

            dnaGroup = self.struct.parent_node_of_class(self.assy.DnaGroup)
            if dnaGroup is None:
                return
            #following should be self.struct.getDnaGroup or self.struct.getDnaGroup
            #need to formalize method name and then make change.
            if not dnaGroup is highlightedChunk.parent_node_of_class(
                    self.assy.DnaGroup):
                item = ("Edit unavailable: Member of a different DnaGroup",
                        noop, 'disabled')
                self.Menu_spec.append(item)
                return

        highlightedChunk.make_glpane_context_menu_items(self.Menu_spec,
                                                        command=self)
class NanotubeSegment_EditCommand(State_preMixin, EditCommand):
    """
    Command to edit a NanotubeSegment object. 
    To edit a segment, first enter BuildNanotube_EditCommand (accessed using 
    Build > Nanotube) then, select an existing NanotubeSegment within the 
    NanotubeGroup you are editing. When you select the NanotubeSegment, it 
    enters NanotubeSegment_Editcommand and shows the property manager with its
    widgets showing the properties of selected segment.
    """
    cmd              =  'Nanotube Segment'
    sponsor_keyword  =  'Nanotube'
    prefix           =  'NanotubeSegment' # used for gensym
    cmdname          = "NANOTUBE_SEGMENT"
    commandName      = 'NANOTUBE_SEGMENT'
    featurename      = 'Edit Nanotube Segment'

    command_should_resume_prevMode = True
    command_has_its_own_gui = True
    command_can_be_suspended = False

    create_name_from_prefix  =  True 

    call_makeMenus_for_each_event = True 

    #Graphics Mode 
    GraphicsMode_class = NanotubeSegment_GraphicsMode

    #This is set to BuildDna_EditCommand.flyoutToolbar (as of 2008-01-14, 
    #it only uses 
    flyoutToolbar = None

    _parentNanotubeGroup = None    

    handlePoint1 = State( Point, ORIGIN)
    handlePoint2 = State( Point, ORIGIN)
    #The minimum 'stopper'length used for resize handles
    #@see: self._update_resizeHandle_stopper_length for details. 
    _resizeHandle_stopper_length = State(Width, -100000)

    rotationHandleBasePoint1 = State( Point, ORIGIN)
    rotationHandleBasePoint2 = State( Point, ORIGIN)

    #See self._update_resizeHandle_radius where this gets changed. 
    #also see NanotubeSegment_ResizeHandle to see how its implemented. 
    handleSphereRadius1 = State(Width, HANDLE_RADIUS_DEFAULT_VALUE)
    handleSphereRadius2 = State(Width, HANDLE_RADIUS_DEFAULT_VALUE)

    cylinderWidth = State(Width, CYLINDER_WIDTH_DEFAULT_VALUE) 
    cylinderWidth2 = State(Width, CYLINDER_WIDTH_DEFAULT_VALUE) 


    #@TODO: modify the 'State params for rotation_distance 
    rotation_distance1 = State(Width, CYLINDER_WIDTH_DEFAULT_VALUE)
    rotation_distance2 = State(Width, CYLINDER_WIDTH_DEFAULT_VALUE)

    leftHandle = Instance(         
        NanotubeSegment_ResizeHandle(    
            command = _self,
            height_ref = call_Expr( ObjAttr_StateRef, _self, 'cylinderWidth'),
            origin = handlePoint1,
            fixedEndOfStructure = handlePoint2,
            direction = norm_Expr(handlePoint1 - handlePoint2),
            sphereRadius = handleSphereRadius1, 
            range = (_resizeHandle_stopper_length, 10000)                               
        ))

    rightHandle = Instance( 
        NanotubeSegment_ResizeHandle(
            command = _self,
            height_ref = call_Expr( ObjAttr_StateRef, _self, 'cylinderWidth2'),
            origin = handlePoint2,
            fixedEndOfStructure = handlePoint1,
            direction = norm_Expr(handlePoint2 - handlePoint1),
            sphereRadius = handleSphereRadius2,
            range = (_resizeHandle_stopper_length, 10000)
        ))

    rotationHandle1 = Instance(         
        RotationHandle(    
            command = _self,
            rotationDistanceRef = call_Expr( ObjAttr_StateRef,
                                             _self, 
                                             'rotation_distance1'),
                                             center = handlePoint1,
                                             axis = norm_Expr(handlePoint1 - handlePoint2),
                                             origin = rotationHandleBasePoint1,
                                             radiusVector = norm_Expr(rotationHandleBasePoint1 - handlePoint1)

                                         ))

    rotationHandle2 = Instance(         
        RotationHandle(    
            command = _self,
            rotationDistanceRef = call_Expr( ObjAttr_StateRef,
                                             _self, 
                                             'rotation_distance2'),
                                             center = handlePoint2,
                                             axis = norm_Expr(handlePoint2 - handlePoint1),
                                             origin = rotationHandleBasePoint2,
                                             radiusVector = norm_Expr(rotationHandleBasePoint2 - handlePoint2)

                                         ))

    def __init__(self, commandSequencer, struct = None):
        """
        Constructor for DnaDuplex_EditCommand
        """

        glpane = commandSequencer
        State_preMixin.__init__(self, glpane)        
        EditCommand.__init__(self, commandSequencer)
        self.struct = struct

        #Graphics handles for editing the structure . 
        self.handles = []        
        self.grabbedHandle = None

        #Initialize DEBUG preference
        pref_nt_segment_resize_by_recreating_nanotube()
        return

    def init_gui(self):
        """
        Initialize gui. 
        """

        #Note that NanotubeSegment_EditCommand only act as an edit command for an 
        #existing structure. The call to self.propMgr.show() is done only during
        #the call to self.editStructure ..i .e. only after self.struct is 
        #updated. This is done because of the following reason:
        # - self.init_gui is called immediately after entering the command. 
        # - self.init_gui in turn, initialized propMgr object and may also 
        #  show the property manager. The self.propMgr.show routine calls 
        #  an update widget method just before the show. This update method 
        #  updates the widgets based on the parameters from the existing 
        #  structure of the command (self.editCommand.struct)
        #  Although, it checks whether this structure exists, the editCommand
        #  could still have a self.struct attr from a previous run. (Note that 
        #  EditCommand API was written before the command sequencer API and 
        #  it has some loose ends like this. ) -- Ninad 2008-01-22
        self.create_and_or_show_PM_if_wanted(showPropMgr = False)
        return

    def editStructure(self, struct = None):
        EditCommand.editStructure(self, struct)        
        if self.hasValidStructure():         
            #When the structure (segment) is finalized (after the  modifications)
            #it will be added to the original NanotubeGroup to which it belonged 
            #before we began editing (modifying) it. 
            self._parentNanotubeGroup = self.struct.getNanotubeGroup() 
            #Set the endpoints
            #@ DOES THIS DO ANYTHING? I don't think so. --Mark 2008-04-01
            #@endPoint1, endPoint2 = self.struct.nanotube.getEndPoints()
            #@params_for_propMgr = (endPoint1, endPoint2)

            #TODO 2008-03-25: better to get all parameters from self.struct and
            #set it in propMgr?  This will mostly work except that reverse is 
            #not true. i.e. we can not specify same set of params for 
            #self.struct.setProps ...because endPoint1 and endPoint2 are derived.
            #by the structure when needed. Commenting out following line of code
            self.propMgr.setParameters(self.struct.getProps())

            #Store the previous parameters. Important to set it after you 
            #set nanotube attrs in the propMgr. 
            #self.previousParams is used in self._previewStructure and 
            #self._finalizeStructure to check if self.struct changed.
            self.previousParams = self._gatherParameters()
            self._updateHandleList()
            self.updateHandlePositions()
        return

    def keep_empty_group(self, group):
        """
        Returns True if the empty group should not be automatically deleted. 
        otherwise returns False. The default implementation always returns 
        False. Subclasses should override this method if it needs to keep the
        empty group for some reasons. Note that this method will only get called
        when a group has a class constant autdelete_when_empty set to True. 
        (and as of 2008-03-06, it is proposed that dna_updater calls this method
        when needed. 
        @see: Command.keep_empty_group() which is overridden here. 
        @see: BreakStrands_Command.keep_empty_group
        @see: Group.autodelete_when_empty.. a class constant used by the 
              dna_updater (the dna updater then decides whether to call this 
              method to see which empty groups need to be deleted)
        """

        bool_keep = EditCommand.keep_empty_group(self, group)

        if not bool_keep:     
            if self.hasValidStructure():                
                if group is self.struct:
                    bool_keep = True
                elif group is self.struct.parent_node_of_class(self.assy.NanotubeGroup):
                    bool_keep = True
            #If this command doesn't have a valid structure, as a fall back, 
            #lets instruct it to keep ALL the NanotubeGroup objects even when empty
            #Reason? ..see explanation in BreakStrands_Command.keep_empty_group
            elif isinstance(group, self.assy.NanotubeGroup):
                bool_keep = True

        return bool_keep

    def hasValidStructure(self):
        """
        Tells the caller if this edit command has a valid structure. 
        Overrides EditCommand.hasValidStructure()
        """
        #(By Bruce 2008-02-13)

        isValid = EditCommand.hasValidStructure(self)

        if not isValid:
            return isValid 

        # would like to check here whether it's empty of axis chunks;
        # instead, this will do for now (probably too slow, though):
        p1, p2 = self.struct.nanotube.getEndPoints()
        return (p1 is not None)

    def _getStructureType(self):
        """
        Subclasses override this method to define their own structure type. 
        Returns the type of the structure this editCommand supports. 
        This is used in isinstance test. 
        @see: EditCommand._getStructureType() (overridden here)
        """
        return self.win.assy.NanotubeSegment

    def _updateHandleList(self):
        """        
        Updates the list of handles (self.handles) 
        @see: self.editStructure
        @see: NanotubeSegment_GraphicsMode._drawHandles()
        """   
        # note: if handlePoint1 and/or handlePoint2 can change more often than this 
        # runs, we'll need to rerun the two assignments above whenever they 
        # change and before the handle is drawn. An easy way would be to rerun
        # these assignments in the draw method of our GM. [bruce 080128]
        self.handles = [] # guess, but seems like a good idea [bruce 080128]
        self.handles.append(self.leftHandle)
        self.handles.append(self.rightHandle)
        if DEBUG_ROTATION_HANDLES:
            self.handles.append(self.rotationHandle1)
            self.handles.append(self.rotationHandle2)
        return

    def updateHandlePositions(self):
        """
        Update handle positions and also update the resize handle radii and
        their 'stopper' lengths. 
        @see: self._update_resizeHandle_radius()
        @see: self._update_resizeHandle_stopper_length()
        @see: NanotubeSegment_GraphicsMode._drawHandles()
        """
        self.handlePoint1 = None # Needed!
        self.handlePoint2 = None

        #TODO: Call this method less often by implementing model_changed
        #see bug 2729 for a planned optimization
        self.cylinderWidth = CYLINDER_WIDTH_DEFAULT_VALUE
        self.cylinderWidth2 = CYLINDER_WIDTH_DEFAULT_VALUE      

        self._update_resizeHandle_radius()

        handlePoint1, handlePoint2 = self.struct.nanotube.getEndPoints()

        if 0: # Debug prints
            print "updateHandlePositions(): handlePoint1=", handlePoint1
            print "updateHandlePositions(): handlePoint2=", handlePoint2

        if handlePoint1 is not None and handlePoint2 is not None:
            # (that condition is bugfix for deleted axis segment, bruce 080213)

            self.handlePoint1, self.handlePoint2 = handlePoint1, handlePoint2            

            #Update the 'stopper'  length where the resize handle being dragged 
            #should stop. See self._update_resizeHandle_stopper_length()
            #for more details
            self._update_resizeHandle_stopper_length()            

            if DEBUG_ROTATION_HANDLES:
                self.rotation_distance1 = CYLINDER_WIDTH_DEFAULT_VALUE
                self.rotation_distance2 = CYLINDER_WIDTH_DEFAULT_VALUE
                #Following computes the base points for rotation handles. 
                #to be revised -- Ninad 2008-02-13
                unitVectorAlongAxis = norm(self.handlePoint1 - self.handlePoint2)

                v  = cross(self.glpane.lineOfSight, unitVectorAlongAxis)

                self.rotationHandleBasePoint1 = self.handlePoint1 + norm(v) * 4.0  
                self.rotationHandleBasePoint2 = self.handlePoint2 + norm(v) * 4.0
        return

    def _update_resizeHandle_radius(self):
        """
        Finds out the sphere radius to use for the resize handles, based on 
        atom /chunk or glpane display (whichever decides the display of the end 
        atoms. The default value is 1.2.

        @see: self.updateHandlePositions()
        """
        self.handleSphereRadius1 = HANDLE_RADIUS_DEFAULT_VALUE
        self.handleSphereRadius2 = HANDLE_RADIUS_DEFAULT_VALUE
        return

    def _update_resizeHandle_stopper_length(self):
        """
        Update the limiting length at which the resize handle being dragged
        should 'stop'  without proceeding further in the drag direction. 
        The segment resize handle stops when you are dragging it towards the 
        other resizeend and the distance between the two ends reaches two 
        duplexes. 

        The self._resizeHandle_stopper_length computed in this method is 
        used as a lower limit of the 'range' option provided in declaration
        of resize handle objects (see class definition for the details)
        @see: self.updateHandlePositions()
        """

        total_length = vlen(self.handlePoint1 - self.handlePoint2)        
        nanotubeRise = self.struct.nanotube.getRise()
        self._resizeHandle_stopper_length = - total_length + nanotubeRise
        return

    def _createPropMgrObject(self):
        """
        Creates a property manager object (that defines UI things) for this 
        editCommand. 
        """
        assert not self.propMgr
        propMgr = self.win.createNanotubeSegmentPropMgr_if_needed(self)
        return propMgr

    def _gatherParameters(self):
        """
        Return the parameters from the property manager UI.

        @return: The endpoints of the nanotube.
        @rtype:  tuple (endPoint1, endPoint2).
        """     
        return self.propMgr.getParameters()

    def _createStructure(self):
        """
        Creates and returns the structure (in this case a L{NanotubeSegment} 
        object. 
        @return : Nanotube segment that include the nanotube chunk.
        @rtype: L{NanotubeSegment}        
        """
        # self.name needed for done message
        if self.create_name_from_prefix:
            # create a new name
            name = self.name = gensym(self.prefix, self.win.assy) # (in _build_struct)
            self._gensym_data_for_reusing_name = (self.prefix, name)
        else:
            # use externally created name
            self._gensym_data_for_reusing_name = None
                # (can't reuse name in this case -- not sure what prefix it was
                #  made with)
            name = self.name


        # Create the model tree group node. 
        # Make sure that the 'topnode'  of this part is a Group (under which the
        # DNa group will be placed), if the topnode is not a group, make it a
        # a 'Group' (applicable to Clipboard parts).See part.py
        # --Part.ensure_toplevel_group method. This is an important line
        # and it fixes bug 2585
        self.win.assy.part.ensure_toplevel_group()
        ntSegment = NanotubeSegment(self.name, 
                                    self.win.assy,
                                    self.win.assy.part.topnode,
                                    editCommand = self  )
        try:
            # Make the NanotubeSegment.

            n, m, type, endings, endPoint1, endPoint2 = self._gatherParameters()

            from cnt.model.Nanotube import Nanotube
            self.nanotube = Nanotube()
            nanotube  =  self.nanotube
            nanotube.setChirality(n, m)
            nanotube.setType(type)
            nanotube.setEndings(endings)
            nanotube.setEndPoints(endPoint1, endPoint2)
            position = V(0.0, 0.0, 0.0)
            ntChunk = nanotube.build(self.name, self.win.assy, position)

            nanotube.computeEndPointsFromChunk(ntChunk)

            ntSegment.addchild(ntChunk)

            #set some properties such as nanotubeRise
            #This information will be stored on the NanotubeSegment object so that
            #it can be retrieved while editing this object. 
            #Should these props be assigned to the NanotubeSegment in 
            #Nanotube.build() itself? This needs to be answered while modifying
            #build() method to fit in the dna data model. --Ninad 2008-03-05

            #WARNING 2008-03-05: Since self._modifyStructure calls 
            #self._createStructure() 
            #If in the near future, we actually permit modifying a
            #structure (such as dna) without actually recreating the whole 
            #structure, then the following properties must be set in 
            #self._modifyStructure as well. Needs more thought.
            props =(nanotube.getChirality(),
                    nanotube.getType(),
                    nanotube.getEndings(),
                    nanotube.getEndPoints())

            ntSegment.setProps(props)

            return ntSegment

        except (PluginBug, UserError):
            # Why do we need UserError here? Mark 2007-08-28
            ntSegment.kill()
            raise PluginBug("Internal error while trying to create a NanotubeSegment.")
        return

    def _modifyStructure(self, params):
        """
        Modify the structure based on the parameters specified. 
        Overrides EditCommand._modifystructure. This method removes the old 
        structure and creates a new one using self._createStructure. This 
        was needed for the structures like this (Dna, Nanotube etc) . .
        See more comments in the method.
        """    
        if not pref_nt_segment_resize_by_recreating_nanotube():
            self._modifyStructure_NEW_SEGMENT_RESIZE(params)
            return

        assert self.struct
        # parameters have changed, update existing structure
        self._revertNumber()

        # self.name needed for done message
        if self.create_name_from_prefix:
            # create a new name
            name = self.name = gensym(self.prefix, self.win.assy) # (in _build_struct)
            self._gensym_data_for_reusing_name = (self.prefix, name)
        else:
            # use externally created name
            self._gensym_data_for_reusing_name = None
                # (can't reuse name in this case -- not sure what prefix it was
                #  made with)
            name = self.name

        #@NOTE: Unlike editcommands such as Plane_EditCommand, this 
        #editCommand actually removes the structure and creates a new one 
        #when its modified. -- Ninad 2007-10-24

        self._removeStructure()

        self.previousParams = params

        self.struct = self._createStructure()
        # Now append the new structure in self._segmentList (this list of 
        # segments will be provided to the previous command 
        # (BuildDna_EditCommand)
        # TODO: Should self._createStructure does the job of appending the 
        # structure to the list of segments? This fixes bug 2599 
        # (see also BuildDna_PropertyManager.Ok 

        if self._parentNanotubeGroup is not None:
            #Should this be an assertion? (assert self._parentNanotubeGroup is not 
            #None. For now lets just print a warning if parentNanotubeGroup is None 
            self._parentNanotubeGroup.addSegment(self.struct)
        return  


    def _modifyStructure_NEW_SEGMENT_RESIZE(self, params): #@ NOT FIXED
        """
        Modify the structure based on the parameters specified. 
        Overrides EditCommand._modifystructure. This method removes the old 
        structure and creates a new one using self._createStructure. This 
        was needed for the structures like this (Dna, Nanotube etc) . .
        See more comments in the method.

        @attention: is not implemented.
        """        

        #@TODO: - rename this method from _modifyStructure_NEW_SEGMENT_RESIZE
        #to self._modifyStructure, after more testing
        #This method is used for debug prefence: 
        #'Nanotube Segment: resize without recreating whole duplex'
        #see also self.modifyStructure_NEW_SEGMENT_RESIZE

        assert self.struct      

        from utilities.debug import print_compact_stack
        print_compact_stack("_modifyStructure_NEW_SEGMENT_RESIZE() not fixed!" )
        print "Params =", params

        self.nanotube = params #@

        length_diff =  self._determine_how_to_change_length() #@

        if length_diff == 0:
            print_compact_stack("BUG: length_diff is always ZERO." )
            return
        elif length_diff > 0:
            print "Nanotube longer by ", length_diff, ", angstroms."
        else:
            print "Nanotube shorter by ", length_diff, ", angstroms."

        return

        if numberOfBasePairsToAddOrRemove != 0:   #@@@@ Not reached.

            resizeEnd_final_position = self._get_resizeEnd_final_position(
                ladderEndAxisAtom, 
                abs(numberOfBasePairsToAddOrRemove),
                nanotubeRise )

            self.nanotube.modify(self.struct,
                                 length_diff,
                                 ladderEndAxisAtom.posn(),
                                 resizeEnd_final_position)

        #Find new end points of structure parameters after modification 
        #and set these values in the propMgr. 
        new_end1 , new_end2 = self.struct.nanotube.getEndPoints() #@

        params_to_set_in_propMgr = (new_end1,
                                    new_end2)

        #TODO: Need to set these params in the PM 
        #and then self.previousParams = params_to_set_in_propMgr

        self.previousParams = params
        return  

    def _get_resizeEnd_final_position(self, 
                                      ladderEndAxisAtom, 
                                      numberOfBases, 
                                      nanotubeRise):

        final_position = None   
        if self.grabbedHandle:
            final_position = self.grabbedHandle.currentPosition
        else:
            other_axisEndAtom = self.struct.getOtherAxisEndAtom(ladderEndAxisAtom)
            axis_vector = ladderEndAxisAtom.posn() - other_axisEndAtom.posn()
            segment_length_to_add = 0 #@
            final_position = ladderEndAxisAtom.posn() + norm(axis_vector)*segment_length_to_add

        return final_position

    def getStructureName(self):
        """
        Returns the name string of self.struct if there is a valid structure. 
        Otherwise returns None. This information is used by the name edit field 
        of  this command's PM when we call self.propMgr.show()
        @see: NanotubeSegment_PropertyManager.show()
        @see: self.setStructureName
        """
        if self.hasValidStructure():
            return self.struct.name
        return None

    def setStructureName(self, name):
        """
        Sets the name of self.struct to param <name> (if there is a valid 
        structure. 
        The PM of this command callss this method while closing itself 
        @param name: name of the structure to be set.
        @type name: string
        @see: NanotubeSegment_PropertyManager.close()
        @see: self.getStructureName()

        """
        #@BUG: We call this method in self.propMgr.close(). But propMgr.close() 
                #is called even when the command is 'cancelled'. That means the 
                #structure will get changed even when user hits cancel button or
                #exits the command by clicking on empty space. 
                #This should really be done in self._finalizeStructure but that 
                #method doesn't get called when you click on empty space to exit 
                #the command. See NanotubeSegment_GraphicsMode.leftUp for a detailed 
                #comment. 

        if self.hasValidStructure():
            self.struct.name = name
        return

    def getCursorText(self):
        """
        This is used as a callback method in NanotubeLine mode 
        @see: NanotubeLineMode.setParams, NanotubeLineMode_GM.Draw
        """
        if self.grabbedHandle is None:
            return
        
        if not env.prefs[nanotubeSegmentEditCommand_showCursorTextCheckBox_prefs_key]:
            return '', black

        text = ""
        textColor = black

        currentPosition = self.grabbedHandle.currentPosition
        fixedEndOfStructure = self.grabbedHandle.fixedEndOfStructure

        nanotubeLength = vlen( currentPosition - fixedEndOfStructure )

        nanotubeLengthString = self._getCursorText_length(nanotubeLength)
        
        text = nanotubeLengthString
   
        #@TODO: The following updates the PM as the cursor moves. 
        #Need to rename this method so that you that it also does more things 
        #than just to return a textString -- Ninad 2007-12-20
        self.propMgr.ntLengthLineEdit.setText(nanotubeLengthString)

        return text, textColor
    
    def _getCursorText_length(self, nanotubeLength):
        """
        Returns a string that gives the length of the Nanotube for the cursor 
        text
        """
        nanotubeLengthString = ''
        if env.prefs[nanotubeSegmentEditCommand_cursorTextCheckBox_length_prefs_key]:
            lengthUnitString = 'A'
            #change the unit of length to nanometers if the length is > 10A
            #fixes part of bug 2856
            if nanotubeLength > 10.0:
                lengthUnitString = 'nm'
                nanotubeLength = nanotubeLength * 0.1
                
            nanotubeLengthString = "%5.3f%s"%(nanotubeLength, lengthUnitString)
        
        return nanotubeLengthString
    
    def modifyStructure(self):
        """
        Called when a resize handle is dragged to change the length of the 
        segment. (Called upon leftUp) . This method assigns the new parameters 
        for the segment after it is resized and calls 
        preview_or_finalize_structure which does the rest of the job. 
        Note that Client should call this public method and should never call
        the private method self._modifyStructure. self._modifyStructure is 
        called only by self.preview_or_finalize_structure

        @see: B{NanotubeSegment_ResizeHandle.on_release} (the caller)
        @see: B{SelectChunks_GraphicsMode.leftUp} (which calls the 
              the relevent method in DragHandler API. )
        @see: B{exprs.DraggableHandle_AlongLine}, B{exprs.DragBehavior}
        @see: B{self.preview_or_finalize_structure }
        @see: B{self._modifyStructure}        

        As of 2008-02-01 it recreates the structure
        @see: a note in self._createStructure() about use of ntSegment.setProps 
        """

        if not pref_nt_segment_resize_by_recreating_nanotube():
            self.modifyStructure_NEW_SEGMENT_RESIZE()
            return

        if self.grabbedHandle is None:
            return        

        self.propMgr.endPoint1 = self.grabbedHandle.fixedEndOfStructure
        self.propMgr.endPoint2 = self.grabbedHandle.currentPosition
        #@length = vlen(self.propMgr.endPoint1 - self.propMgr.endPoint2 ) #@  

        self.preview_or_finalize_structure(previewing = True)  

        self.updateHandlePositions()
        self.glpane.gl_update()
        return

    def modifyStructure_NEW_SEGMENT_RESIZE(self): #@ NOT FIXED
        """
        Called when a resize handle is dragged to change the length of the 
        segment. (Called upon leftUp) . This method assigns the new parameters 
        for the segment after it is resized and calls 
        preview_or_finalize_structure which does the rest of the job. 
        Note that Client should call this public method and should never call
        the private method self._modifyStructure. self._modifyStructure is 
        called only by self.preview_or_finalize_structure

        @see: B{NanotubeSegment_ResizeHandle.on_release} (the caller)
        @see: B{SelectChunks_GraphicsMode.leftUp} (which calls the 
              the relevent method in DragHandler API. )
        @see: B{exprs.DraggableHandle_AlongLine}, B{exprs.DragBehavior}
        @see: B{self.preview_or_finalize_structure }
        @see: B{self._modifyStructure}        

        As of 2008-02-01 it recreates the structure
        @see: a note in self._createStructure() about use of ntSegment.setProps 
        """
        #TODO: need to cleanup this and may be use use something like
        #self.previousParams = params in the end -- 2008-03-24 (midnight)


        #@TODO: - rename this method from modifyStructure_NEW_SEGMENT_RESIZE
        #to self.modifyStructure, after more testing
        #This method is used for debug prefence: 
        #'Nanotube Segment: resize without recreating whole duplex'
        #see also self._modifyStructure_NEW_SEGMENT_RESIZE

        if self.grabbedHandle is None:
            return   

        self.propMgr.endPoint1 = self.grabbedHandle.fixedEndOfStructure
        self.propMgr.endPoint2 = self.grabbedHandle.currentPosition

        DEBUG_DO_EVERYTHING_INSIDE_MODIFYSTRUCTURE_METHOD = False

        if DEBUG_DO_EVERYTHING_INSIDE_MODIFYSTRUCTURE_METHOD:

            # TO DO: this entire block of code.  --Mark 2008-04-03
            print_compact_stack("modifyStructure_NEW_SEGMENT_RESIZE(): NOT FIXED")

            length = vlen(self.grabbedHandle.fixedEndOfStructure - \
                          self.grabbedHandle.currentPosition )

            endAtom1, endAtom2 = self.struct.getAxisEndAtoms() #@

            for atm in (endAtom1, endAtom2):
                if not same_vals(self.grabbedHandle.fixedEndOfStructure, atm.posn()):
                    ladderEndAxisAtom = atm
                    break

                endPoint1, endPoint2 = self.struct.nanotube.getEndPoints()
                old_dulex_length = vlen(endPoint1 - endPoint2)

                nanotubeRise = self.struct.getProps()      #@ 

                params_to_set_in_propMgr = (
                    self.grabbedHandle.origin,
                    self.grabbedHandle.currentPosition,
                )

                ##self._modifyStructure(params)
                ############################################

                self.nanotube = Nanotube() #@ Creates 5x5 CNT. Miisng PM params.

                length_diff =  self._determine_how_to_change_length()  
                ladderEndAxisAtom = self.get_axisEndAtom_at_resize_end() #@

                #@ Nanotube class needs modify() method.
                self.nanotube.modify(self.struct, 
                                     length_diff,
                                     ladderEndAxisAtom.posn(),
                                     self.grabbedHandle.currentPosition)

        #TODO: Important note: How does NE1 know that structure is modified? 
        #Because number of base pairs parameter in the PropMgr changes as you 
        #drag the handle . This is done in self.getCursorText() ... not the 
        #right place to do it. OR that method needs to be renamed to reflect
        #this as suggested in that method -- Ninad 2008-03-25

        self.preview_or_finalize_structure(previewing = True) 

        ##self.previousParams = params_to_set_in_propMgr

        self.glpane.gl_update()
        return

    def get_axisEndAtom_at_resize_end(self):
        ladderEndAxisAtom = None
        if self.grabbedHandle is not None:
            ladderEndAxisAtom = self.struct.getAxisEndAtomAtPosition(self.grabbedHandle.origin)
        else:
            endAtom1, endAtom2 = self.struct.getAxisEndAtoms()
            ladderEndAxisAtom = endAtom2

        return ladderEndAxisAtom

    def _determine_how_to_change_length(self): #@ NEEDS WORK
        """
        Returns the difference in length between the original nanotube and the
        modified nanotube, where:
           0 = no change in length
         > 0 = lengthen
         < 0 = trim
        """
        nanotubeRise = self.struct.nanotube.getRise()
        endPoint1, endPoint2 = self.struct.nanotube.getEndPoints() #@ 
        original_nanotube_length = vlen(endPoint1 - endPoint2)
        new_nanotube_length      = vlen(endPoint1 - endPoint2) #@
        return new_nanotube_length - original_nanotube_length #@ ALWAYS RETURNS ZERO

    def makeMenus(self): 
        """
        Create context menu for this command. (Build Dna mode)
        """
        if not hasattr(self, 'graphicsMode'):
            return

        selobj = self.glpane.selobj

        if selobj is None:
            return

        self.Menu_spec = []

        highlightedChunk = None
        if isinstance(selobj, Chunk):
            highlightedChunk = selobj
        if isinstance(selobj, Atom):
            highlightedChunk = selobj.molecule
        elif isinstance(selobj, Bond):
            chunk1 = selobj.atom1.molecule
            chunk2 = selobj.atom2.molecule
            if chunk1 is chunk2 and chunk1 is not None:
                highlightedChunk = chunk1

        if highlightedChunk is None:
            return

        if self.hasValidStructure():        

            nanotubeGroup = self.struct.parent_node_of_class(self.assy.NanotubeGroup)
            if nanotubeGroup is None:
                return
            #following should be self.struct.getNanotubeGroup or self.struct.getNanotubeGroup
            #need to formalize method name and then make change.
            if not nanotubeGroup is highlightedChunk.parent_node_of_class(self.assy.NanotubeGroup):
                item = ("Edit unavailable: Member of a different NanotubeGroup",
                        noop, 'disabled')
                self.Menu_spec.append(item)
                return

        highlightedChunk.make_glpane_context_menu_items(self.Menu_spec,
                                                        command = self)
        return
Esempio n. 3
0
class MultipleDnaSegmentResize_EditCommand(DnaSegment_EditCommand):

    #Graphics Mode
    GraphicsMode_class = MultipleDnaSegmentResize_GraphicsMode

    #Property Manager
    PM_class = MultipleDnaSegmentResize_PropertyManager

    cmdname = 'MULTIPLE_DNA_SEGMENT_RESIZE'  # REVIEW: needed? correct?

    commandName = 'MULTIPLE_DNA_SEGMENT_RESIZE'
    featurename = "Edit Multiple Dna Segments"
    from utilities.constants import CL_SUBCOMMAND
    command_level = CL_SUBCOMMAND
    command_parent = 'BUILD_DNA'

    #This command operates on (resizes) multiple DnaSegments at once.
    #It does that by  looping through the DnaSegments and resizing them
    #individually. self.currentStruct is set to the 'current' DnaSegment
    #in that loop and then it is used in the resizing code.
    #@see: DnaSegmentList class
    #@see: self.modifyStructure()
    currentStruct = None

    handlePoint1 = State(Point, ORIGIN)
    handlePoint2 = State(Point, ORIGIN)
    #The minimum 'stopper'length used for resize handles
    #@see: self._update_resizeHandle_stopper_length for details.
    _resizeHandle_stopper_length = State(Width, -100000)

    rotationHandleBasePoint1 = State(Point, ORIGIN)
    rotationHandleBasePoint2 = State(Point, ORIGIN)

    #See self._update_resizeHandle_radius where this gets changed.
    #also see DnaSegment_ResizeHandle to see how its implemented.
    handleSphereRadius1 = State(Width, HANDLE_RADIUS_DEFAULT_VALUE)
    handleSphereRadius2 = State(Width, HANDLE_RADIUS_DEFAULT_VALUE)

    cylinderWidth = State(Width, CYLINDER_WIDTH_DEFAULT_VALUE)
    cylinderWidth2 = State(Width, CYLINDER_WIDTH_DEFAULT_VALUE)

    #@TODO: modify the 'State params for rotation_distance
    rotation_distance1 = State(Width, CYLINDER_WIDTH_DEFAULT_VALUE)
    rotation_distance2 = State(Width, CYLINDER_WIDTH_DEFAULT_VALUE)

    leftHandle = Instance(
        DnaSegment_ResizeHandle(
            command=_self,
            height_ref=call_Expr(ObjAttr_StateRef, _self, 'cylinderWidth'),
            origin=handlePoint1,
            fixedEndOfStructure=handlePoint2,
            direction=norm_Expr(handlePoint1 - handlePoint2),
            sphereRadius=handleSphereRadius1,
            range=(_resizeHandle_stopper_length, 10000)))

    rightHandle = Instance(
        DnaSegment_ResizeHandle(
            command=_self,
            height_ref=call_Expr(ObjAttr_StateRef, _self, 'cylinderWidth2'),
            origin=handlePoint2,
            fixedEndOfStructure=handlePoint1,
            direction=norm_Expr(handlePoint2 - handlePoint1),
            sphereRadius=handleSphereRadius2,
            range=(_resizeHandle_stopper_length, 10000)))

    def _update_previousParams_in_model_changed(self):
        """
        Overrides superclass method. Does nothing in this class.
        @see: self.model_changed() which calls this method.
        """
        pass

    def isAddSegmentsToolActive(self):
        """
        Returns True if the Add segments tool in the PM, that allows adding
        dna segments from the resize segment list,  is active.
        @see: MultipleDnaSegmentResize_GraphicsMode.chunkLeftUp()
        @see: MultipleDnaSegmentResize_GraphicsMode.end_selection_from_GLPane()
        @see: self.isRemoveSegmentsToolActive()
        @see: self.addSegmentToResizeSegmentList()
        """
        if self.propMgr is None:
            return False

        return self.propMgr.isAddSegmentsToolActive()

    def isRemoveSegmentsToolActive(self):
        """
        Returns True if the Remove Segments tool in the PM, that allows removing
        dna segments from the resize segment list, is active.
        @see: MultipleDnaSegmentResize_GraphicsMode.chunkLeftUp()
        @see: MultipleDnaSegmentResize_GraphicsMode.end_selection_from_GLPane()
        @see: self.isAddSegmentsToolActive()
        @see: self.removeSegmentFromResizeSegmentList()
        """
        if self.propMgr is None:
            return False

        return self.propMgr.isRemoveSegmentsToolActive()

    def addSegmentToResizeSegmentList(self, segment):
        """
        Adds the given segment to the resize segment list
        @param segment: The DnaSegment to be added to the resize segment list.
        Also does other things such as updating resize handles etc.
        @type sement: B{DnaSegment}
        @see: self.isAddSegmentsToolActive()
        """
        assert isinstance(segment, self.win.assy.DnaSegment)
        self.struct.addToStructList(segment)

    def removeSegmentFromResizeSegmentList(self, segment):
        """
        Removes the given segment from the resize segment list
        @param segment: The DnaSegment to be removed from the resize segment
        list. Also does other things such as updating resize handles etc.
        @type sement: B{DnaSegment}
        @see: self.isRemoveSegmentsToolActive()
        """
        assert isinstance(segment, self.win.assy.DnaSegment)
        self.struct.removeFromStructList(segment)

    def setResizeStructList(self, structList):
        """
        Replaces the list of segments to be resized with the
        given segmentList. Calls the related method of self.struct.
        @param structList: New list of segments to be resized.
        @type  structList: list
        @see: DnaSegmentList.setResizeStructList()
        """
        if self.hasValidStructure():
            self.struct.setStructList(structList)
            self.updateHandlePositions()

    def getResizeSegmentList(self):
        """
        Returns a list of segments to be resized at once.
        @see: DnaSegmentList.getStructList()
        """
        if self.hasValidStructure():
            return self.struct.getStructList()
        return ()

    def updateResizeSegmentList(self):
        """
        Update the list of resize segments (maintained by self.struct)
        """
        if self.hasValidStructure():
            self.struct.updateStructList()

    def editStructure(self, struct=None):
        """
        Overrides superclass method. It first creates a B{DnaSegmentList} object
        using the provided list (struct argument) The DnaSegmentList object
        acts as self.struct.
        @param struct: The caller of this method should provide a list of
               containing all the structurs that need to be edited (resized) at
               once. Caller also needs to make sure that this list contains
               objects of class DnaSegment.
        @type  struct: list
        @see: B{DnaSegmentList}
        """
        if isinstance(struct, list):
            dnaSegmentListObject = DnaSegmentList(self, structList=struct)

        _superclass.editStructure(self, struct=dnaSegmentListObject)

    def _updatePropMgrParams(self):
        """
        Subclasses may override this method.
        Update some property manager parameters with the parameters of
        self.struct (which is being edited)
        @see: self.editStructure()
        @TODO: For multiple Dna segment resizing, this method does nothing as
           of 2008-05-14. Need to be revised.
        """

        #Commented out code as of 2008-05-14 ===========

        #endPoint1, endPoint2 = self.struct.getAxisEndPoints()
        #params_for_propMgr = (None,
        #endPoint1,
        #endPoint2)

        ##TODO 2008-03-25: better to get all parameters from self.struct and
        ##set it in propMgr?  This will mostly work except that reverse is
        ##not true. i.e. we can not specify same set of params for
        ##self.struct.setProps ...because endPoint1 and endPoint2 are derived.
        ##by the structure when needed. Commenting out following line of code
        ##UPDATE 2008-05-06 Fixes a bug due to which the parameters in propMGr
        ##of DnaSegment_EditCommand are not same as the original structure
        ##(e.g. bases per turn and duplexrise)
        ###self.propMgr.setParameters(params_for_propMgr)

        pass

    def _update_resizeHandle_radius(self):
        """
        Finds out the sphere radius to use for the resize handles, based on
        atom /chunk or glpane display (whichever decides the display of the end
        atoms.  The default  value is 1.2.


        @see: self.updateHandlePositions()
        @see: B{Atom.drawing_radius()}
        @TODO: Refactor this. It does essentially same thing as the
        superclass method. The problem is due to the use of the global constant
        HANDLE_RADIUS_DEFAULT_VALUE which needs to be different (bigger) in this
        class. So better to make it a class constant (need similar changes
        in several commands. To be done in a refactoring project.
        """
        atm1, atm2 = self.struct.getAxisEndAtoms()
        if atm1 is not None:
            self.handleSphereRadius1 = max(1.005 * atm1.drawing_radius(),
                                           1.005 * HANDLE_RADIUS_DEFAULT_VALUE)
        if atm2 is not None:
            self.handleSphereRadius2 = max(1.005 * atm2.drawing_radius(),
                                           1.005 * HANDLE_RADIUS_DEFAULT_VALUE)

    def _modifyStructure(self, params):
        """
        Modify the structure based on the parameters specified.
        Overrides EditCommand._modifystructure. This method removes the old
        structure and creates a new one using self._createStructure. This
        was needed for the structures like this (Dna, Nanotube etc) . .
        See more comments in the method.
        """

        if self.currentStruct is None:
            print_compact_stack("bug: self.currentStruct doesn't exist"\
                                "exiting self._modifyStructure")
            return

        assert isinstance(self.currentStruct, self.win.assy.DnaSegment)

        self.dna = B_Dna_PAM3_Generator()

        duplexRise = self.currentStruct.getDuplexRise()

        basesPerTurn = self.currentStruct.getBasesPerTurn()

        numberOfBasePairsToAddOrRemove = self._determine_numberOfBasePairs_to_change(
        )

        ladderEndAxisAtom = self.get_axisEndAtom_at_resize_end()

        if ladderEndAxisAtom is None:
            print_compact_stack("bug: unable to resize the DnaSegment"\
                                "%r, end axis ato not found"%self.currentStruct)
            return

        if numberOfBasePairsToAddOrRemove != 0:

            resizeEnd_final_position = self._get_resizeEnd_final_position(
                ladderEndAxisAtom, abs(numberOfBasePairsToAddOrRemove),
                duplexRise)

            self.dna.modify(self.currentStruct, ladderEndAxisAtom,
                            numberOfBasePairsToAddOrRemove, basesPerTurn,
                            duplexRise, ladderEndAxisAtom.posn(),
                            resizeEnd_final_position)

        self.previousParams = params

        return

    def modifyStructure(self):
        """

        Called when a resize handle is dragged to change the length of one
        or more DnaSegments (to be resized together)  Called upon leftUp .

        To acheive this resizing, it loops through the DnaSegments to be resized
        and calls self._modifyStructure() for individual DnaSegments.

        Note that Client should call this public method and should never call
        the private method self._modifyStructure.

        @see: B{DnaSegment_ResizeHandle.on_release} (the caller)
        @see: B{SelectChunks_GraphicsMode.leftUp} (which calls the
              the relevent method in DragHandler API. )
        @see: B{exprs.DraggableHandle_AlongLine}, B{exprs.DragBehavior}

        @see: B{self._modifyStructure}
        @see: B{DnaSegmentList.updateAverageEndPoints()}

        """

        if self.grabbedHandle is None:
            return

        for segment in self.getResizeSegmentList():
            #TODO 2008-05-14: DnaSegmentList class relies on self.currentStruct.
            #See if there is a better way. Ok for now
            self.currentStruct = segment
            #self._modifyStructure needs 'param' argument. As of 2008-05-14
            #it is not supported/needed for multiple dna segment resizing.
            #So just pass an empty tuple.
            params = ()
            self._modifyStructure(params)
            #Reset the self.currentStruct after use
            self.currentStruct = None

        #Update the average end points of the self.struct so that resize handles
        #are placed at proper positions.
        self.struct.updateAverageEndPoints()

        self.win.assy.changed()
        self.win.win_update()

    def _get_resizeEnd_final_position(self, ladderEndAxisAtom, numberOfBases,
                                      duplexRise):
        """
        Returns the final position of the resize end.
        Note that we can not use the grabbedHandle's currentPosition as the
        final position because resize handle is placed at an 'average' position.
        So we must compute the correct vector using the 'self.currentStruct'
        """

        final_position = None

        other_axisEndAtom = self.struct.getOtherAxisEndAtom(ladderEndAxisAtom)

        if other_axisEndAtom is None:
            return None

        axis_vector = ladderEndAxisAtom.posn() - other_axisEndAtom.posn()
        segment_length_to_add = getDuplexLength('B-DNA',
                                                numberOfBases,
                                                duplexRise=duplexRise)

        signFactor = +1

        ##if self.grabbedHandle is not None:
        ##vec = self.grabbedHandle.currentPosition - \
        ##       self.grabbedHandle.fixedEndOfStructure

        ##if dot(vec, axis_vector) < 0:
        ##signFactor = -1
        ##else:
        ##signFactor = +1

        axis_vector = axis_vector * signFactor


        final_position = ladderEndAxisAtom.posn() + \
                       norm(axis_vector)*segment_length_to_add

        return final_position

    def hasResizableStructure(self):
        """
        """
        if len(self.struct.getStructList()) == 0:
            why_not = 'Segment list is empty'
            isResizable = False
            return isResizable, why_not

        return _superclass.hasResizableStructure(self)

    def _getStructureType(self):
        """
        Returns the type of the structure this command supports.
        This is used in isinstance test.
        @see: EditCommand._getStructureType()
        """
        return DnaSegmentList

    def getDnaRibbonParams(self):
        """
        Overrides superclass method.
        @TODO:[2008-05-14]  This could SLOW things up! Its called multiple times
        from the graphics mode (for all the DnaSegments to be resized at once).
        There is no apparent solution to this, but we could try optimizing the
        following code. Also needs some refactoring.
        @see: MultipleDnaSegmentResize_GraphicsMode._drawHandles()
        @see: self._determine_numberOfBasePairs_to_change()
        """

        #Optimization: First test a few basic things to see if the dna ribbon
        #should be drawn, before doing more computations -- Ninad 2008-05-14

        params_when_adding_bases = None
        params_when_removing_bases = None

        if self.grabbedHandle is None:
            return None, None

        if self.grabbedHandle.origin is None:
            return None, None

        ladderEndAxisAtom = self.get_axisEndAtom_at_resize_end()

        if ladderEndAxisAtom is None:
            return None, None

        numberOfBasePairsToAddOrRemove = self._determine_numberOfBasePairs_to_change(
        )

        if numberOfBasePairsToAddOrRemove == 0:
            return None, None


        direction_of_drag = norm(self.grabbedHandle.currentPosition - \
                                 self.grabbedHandle.origin)

        #@TODO: BUG: DO NOT use self._get_resizeEnd_final_position to compute
        #the resizeEnd_final_position. It computes the segment length to add
        #using the number of base pairs to add ... and it uses
        #getDuplexLength method which in turn rounds off the length as an integer
        #multiple of duplexRise. Although this is what we want in self._modify()
        #the drawDnaRibbon draws the base pair only if step size is > 3.18!
        #so no basepair is drawn at exact val of 3.18. This is BUG, harder to
        #fix. for drawing dna ribbon, lets justcompute the resize end final
        #position using the following code. -- Ninad 2008-05-14
        other_axisEndAtom = self.struct.getOtherAxisEndAtom(ladderEndAxisAtom)

        if other_axisEndAtom is None:
            return None, None

        axis_vector = ladderEndAxisAtom.posn() - other_axisEndAtom.posn()
        currentPosition = self.grabbedHandle.currentPosition
        changedLength = vlen(currentPosition - self.grabbedHandle.origin)

        #NOTE: (TODO) we call self._determine_numberOfBasePairs_to_change() at the
        #beginning of this method which checks various things such as
        #total distance moved by the handle etc to determine whether to draw
        #the ribbon. So those checks are not done here. If that call is removed
        #then we need to do those checks.

        if dot(self.grabbedHandle.direction, direction_of_drag) < 0:
            signFactor = -1.0
        else:
            signFactor = 1.0

        resizeEnd_final_position = ladderEndAxisAtom.posn() + \
                                 norm(axis_vector)*changedLength*signFactor

        #If the segment is being shortened (determined by checking the
        #direction of drag) , no need to draw the rubberband line.
        if dot(self.grabbedHandle.direction, direction_of_drag) < 0:
            params_when_adding_bases = None
            params_when_removing_bases = (resizeEnd_final_position)

            return params_when_adding_bases, \
                   params_when_removing_bases

        basesPerTurn = self.struct.getBasesPerTurn()
        duplexRise = self.struct.getDuplexRise()

        ladder = ladderEndAxisAtom.molecule.ladder
        endBaseAtomList = ladder.get_endBaseAtoms_containing_atom(
            ladderEndAxisAtom)

        ribbon1_start_point = None
        ribbon2_start_point = None
        ribbon1_direction = None
        ribbon2_direction = None

        ribbon1Color = applegreen
        ribbon2Color = applegreen

        if endBaseAtomList and len(endBaseAtomList) > 2:
            strand_atom1 = endBaseAtomList[0]
            strand_atom2 = endBaseAtomList[2]

            if strand_atom1:
                ribbon1_start_point = strand_atom1.posn()
                for bond_direction, neighbor in strand_atom1.bond_directions_to_neighbors(
                ):
                    if neighbor and neighbor.is_singlet():
                        ribbon1_direction = bond_direction
                        break

                ribbon1Color = strand_atom1.molecule.color
                if not ribbon1Color:
                    ribbon1Color = strand_atom1.element.color

            if strand_atom2:
                ribbon2_start_point = strand_atom2.posn()
                for bond_direction, neighbor in strand_atom2.bond_directions_to_neighbors(
                ):
                    if neighbor and neighbor.is_singlet():
                        ribbon2_direction = bond_direction
                        break
                ribbon2Color = strand_atom2.molecule.color
                if not ribbon2Color:
                    ribbon2Color = strand_atom2.element.color

        params_when_adding_bases = (ladderEndAxisAtom.posn(),
                                    resizeEnd_final_position, basesPerTurn,
                                    duplexRise, ribbon1_start_point,
                                    ribbon2_start_point, ribbon1_direction,
                                    ribbon2_direction, ribbon1Color,
                                    ribbon2Color)

        params_when_removing_bases = None

        return params_when_adding_bases, params_when_removing_bases

    def _determine_numberOfBasePairs_to_change(self):
        """
        Overrides superclass method
        @TODO: This is significantly different (and perhaps better)
               than the superclass method. See how to incorporate changes in
               this method in superclass.

        @see: self.getDnaRibbonParams()
        """

        currentPosition = self.grabbedHandle.currentPosition
        fixedEndOfStructure = self.grabbedHandle.fixedEndOfStructure
        duplexRise = self.struct.getDuplexRise()

        changedLength = vlen(currentPosition - self.grabbedHandle.origin)

        direction_of_drag = norm(self.grabbedHandle.currentPosition - \
                                 self.grabbedHandle.origin)

        #Even when the direction of drag is negative (i.e. the basepairs being
        #removed), make sure not to remove base pairs for very small movement
        #of the grabbed handle
        if changedLength < 0.2 * duplexRise:
            return 0

        #This check quickly determines if the grabbed handle moved by a distance
        #more than the duplexRise and avoids further computations
        #This condition is applicable only when the direction of drag is
        #positive..i.e. bases bing added to the segment.
        if changedLength < duplexRise and \
           dot(self.grabbedHandle.direction, direction_of_drag) > 0:
            return 0

        #If the segment is being shortened (determined by checking the
        #direction of drag)

        numberOfBasesToAddOrRemove =  \
                                   getNumberOfBasePairsFromDuplexLength(
                                       'B-DNA',
                                       changedLength,
                                       duplexRise = duplexRise)

        if dot(self.grabbedHandle.direction, direction_of_drag) < 0:
            numberOfBasesToAddOrRemove = -numberOfBasesToAddOrRemove

        if numberOfBasesToAddOrRemove > 0:
            #dna.modify will remove the first base pair it creates
            #(that basepair will only be used for proper alignment of the
            #duplex with the existing structure) So we need to compensate for
            #this basepair by adding 1 to the new number of base pairs.

            #UPDATE 2008-05-14: The following commented out code
            #i.e. "##numberOfBasesToAddOrRemove += 1" is not required in this
            #class , because the way we compute the number of base pairs to
            #be added is different than than how its done at the moment in the
            #superclass. In this method, we compute bases to be added from
            #the resize end and that computation INCLUDES the resize end.
            #so the number that it returns is already one more than the actual
            #bases to be added. so commenting out the following line
            # -- Ninad 2008-05-14
            ##numberOfBasesToAddOrRemove += 1
            ##print "*** numberOfBasesToAddOrRemove = ", numberOfBasesToAddOrRemove
            ##print "**** changedLength =", changedLength
            pass
            ###UPDATE 2008-06-26: for some reason, when the number of base pairs
            ###to be added (i.e. value of numberOfBasesToAddOrRemove) is  1 more
            ###than the actual number of base pairs to be added. So subtract 1
            ###from this number. Cause not debugged. -- Ninad
            ##if numberOfBasesToAddOrRemove > 1:
            ##numberOfBasesToAddOrRemove -= 1
            #UPDATE 2008-08-20: Note that DnaSegment_EditCommand.getCursorText()
            #does the job of removing the extra basepair from numberOfBasesToAddOrRemove
            #It(subtracting 1 basePair) is not done here as
            #self._modifyStructure() needs it without the subtraction. This is
            #prone to bugs and need to be cleaned up. -- Ninad

        return numberOfBasesToAddOrRemove
class EditNanotube_EditCommand(State_preMixin, EditCommand):
    """
    Command to edit a NanotubeSegment (nanotube).
    """
    # class constants
    GraphicsMode_class = EditNanotube_GraphicsMode
    PM_class = EditNanotube_PropertyManager
    
    commandName      = 'EDIT_NANOTUBE'
    featurename      = "Edit Nanotube"
    from utilities.constants import CL_SUBCOMMAND
    command_level = CL_SUBCOMMAND
    command_parent = 'BUILD_NANOTUBE'
    
    command_should_resume_prevMode = True
    command_has_its_own_PM = True
    
    flyoutToolbar = None

    call_makeMenus_for_each_event = True 

    handlePoint1 = State( Point, ORIGIN)
    handlePoint2 = State( Point, ORIGIN)
    #The minimum 'stopper'length used for resize handles
    #@see: self._update_resizeHandle_stopper_length for details. 
    _resizeHandle_stopper_length = State(Width, -100000)

    rotationHandleBasePoint1 = State( Point, ORIGIN)
    rotationHandleBasePoint2 = State( Point, ORIGIN)

    #See self._update_resizeHandle_radius where this gets changed. 
    #also see EditNanotube_ResizeHandle to see how its implemented. 
    handleSphereRadius1 = State(Width, HANDLE_RADIUS_DEFAULT_VALUE)
    handleSphereRadius2 = State(Width, HANDLE_RADIUS_DEFAULT_VALUE)

    cylinderWidth = State(Width, CYLINDER_WIDTH_DEFAULT_VALUE) 
    cylinderWidth2 = State(Width, CYLINDER_WIDTH_DEFAULT_VALUE) 

    #@TODO: modify the 'State params for rotation_distance 
    rotation_distance1 = State(Width, CYLINDER_WIDTH_DEFAULT_VALUE)
    rotation_distance2 = State(Width, CYLINDER_WIDTH_DEFAULT_VALUE)

    leftHandle = Instance(         
        EditNanotube_ResizeHandle(    
            command = _self,
            height_ref = call_Expr( ObjAttr_StateRef, _self, 'cylinderWidth'),
            origin = handlePoint1,
            fixedEndOfStructure = handlePoint2,
            direction = norm_Expr(handlePoint1 - handlePoint2),
            sphereRadius = handleSphereRadius1, 
            range = (_resizeHandle_stopper_length, 10000)                               
        ))

    rightHandle = Instance( 
        EditNanotube_ResizeHandle(
            command = _self,
            height_ref = call_Expr( ObjAttr_StateRef, _self, 'cylinderWidth2'),
            origin = handlePoint2,
            fixedEndOfStructure = handlePoint1,
            direction = norm_Expr(handlePoint2 - handlePoint1),
            sphereRadius = handleSphereRadius2,
            range = (_resizeHandle_stopper_length, 10000)
        ))

    rotationHandle1 = Instance(         
        RotationHandle(    
            command = _self,
            rotationDistanceRef = call_Expr( ObjAttr_StateRef,
                                             _self, 
                                             'rotation_distance1'),
                                             center = handlePoint1,
                                             axis = norm_Expr(handlePoint1 - handlePoint2),
                                             origin = rotationHandleBasePoint1,
                                             radiusVector = norm_Expr(rotationHandleBasePoint1 - handlePoint1)

                                         ))

    rotationHandle2 = Instance(         
        RotationHandle(    
            command = _self,
            rotationDistanceRef = call_Expr( ObjAttr_StateRef,
                                             _self, 
                                             'rotation_distance2'),
                                             center = handlePoint2,
                                             axis = norm_Expr(handlePoint2 - handlePoint1),
                                             origin = rotationHandleBasePoint2,
                                             radiusVector = norm_Expr(rotationHandleBasePoint2 - handlePoint2)

                                         ))

    def __init__(self, commandSequencer):
        """
        Constructor for InsertDna_EditCommand
        """
        glpane = commandSequencer.assy.glpane
        State_preMixin.__init__(self, glpane)        
        EditCommand.__init__(self, commandSequencer)
        
        #Graphics handles for editing the structure . 
        self.handles = []        
        self.grabbedHandle = None

        #Initialize DEBUG preference
        pref_nt_segment_resize_by_recreating_nanotube()
        return
    
    def editStructure(self, struct = None):
        EditCommand.editStructure(self, struct)        
        if self.hasValidStructure():

            #TODO 2008-03-25: better to get all parameters from self.struct and
            #set it in propMgr?  This will mostly work except that reverse is 
            #not true. i.e. we can not specify same set of params for 
            #self.struct.setProps ...because endPoint1 and endPoint2 are derived.
            #by the structure when needed.
            self.propMgr.setParameters(self.struct.getProps())

            #Store the previous parameters. Important to set it after you 
            #set nanotube attrs in the propMgr. 
            #self.previousParams is used in self._previewStructure and 
            #self._finalizeStructure to check if self.struct changed.
            self.previousParams = self._gatherParameters()
            self._updateHandleList()
            self.updateHandlePositions()
        return

    def hasValidStructure(self):
        """
        Tells the caller if this edit command has a valid structure. 
        Overrides EditCommand.hasValidStructure()
        """
        #(By Bruce 2008-02-13)

        isValid = EditCommand.hasValidStructure(self)

        if not isValid:
            return isValid 

        # would like to check here whether it's empty of axis chunks;
        # instead, this will do for now (probably too slow, though):
        p1, p2 = self.struct.nanotube.getEndPoints()
        return (p1 is not None)

    def _getStructureType(self):
        """
        Subclasses override this method to define their own structure type. 
        Returns the type of the structure this editCommand supports. 
        This is used in isinstance test. 
        @see: EditCommand._getStructureType() (overridden here)
        """
        return self.win.assy.NanotubeSegment

    def _updateHandleList(self):
        """        
        Updates the list of handles (self.handles) 
        @see: self.editStructure
        @see: EditNanotube_GraphicsMode._drawHandles()
        """   
        # note: if handlePoint1 and/or handlePoint2 can change more often than this 
        # runs, we'll need to rerun the two assignments above whenever they 
        # change and before the handle is drawn. An easy way would be to rerun
        # these assignments in the draw method of our GM. [bruce 080128]
        self.handles = [] # guess, but seems like a good idea [bruce 080128]
        self.handles.append(self.leftHandle)
        self.handles.append(self.rightHandle)
        if DEBUG_ROTATION_HANDLES:
            self.handles.append(self.rotationHandle1)
            self.handles.append(self.rotationHandle2)
        return

    def updateHandlePositions(self):
        """
        Update handle positions and also update the resize handle radii and
        their 'stopper' lengths. 
        @see: self._update_resizeHandle_radius()
        @see: self._update_resizeHandle_stopper_length()
        @see: EditNanotube_GraphicsMode._drawHandles()
        """
        self.handlePoint1 = None # Needed!
        self.handlePoint2 = None

        #TODO: Call this method less often by implementing model_changed
        #see bug 2729 for a planned optimization
        self.cylinderWidth = CYLINDER_WIDTH_DEFAULT_VALUE
        self.cylinderWidth2 = CYLINDER_WIDTH_DEFAULT_VALUE      

        self._update_resizeHandle_radius()

        handlePoint1, handlePoint2 = self.struct.nanotube.getEndPoints()

        if 0: # Debug prints
            print "updateHandlePositions(): handlePoint1=", handlePoint1
            print "updateHandlePositions(): handlePoint2=", handlePoint2

        if handlePoint1 is not None and handlePoint2 is not None:
            # (that condition is bugfix for deleted axis segment, bruce 080213)

            self.handlePoint1, self.handlePoint2 = handlePoint1, handlePoint2            

            #Update the 'stopper'  length where the resize handle being dragged 
            #should stop. See self._update_resizeHandle_stopper_length()
            #for more details
            self._update_resizeHandle_stopper_length()            

            if DEBUG_ROTATION_HANDLES:
                self.rotation_distance1 = CYLINDER_WIDTH_DEFAULT_VALUE
                self.rotation_distance2 = CYLINDER_WIDTH_DEFAULT_VALUE
                #Following computes the base points for rotation handles. 
                #to be revised -- Ninad 2008-02-13
                unitVectorAlongAxis = norm(self.handlePoint1 - self.handlePoint2)

                v  = cross(self.glpane.lineOfSight, unitVectorAlongAxis)

                self.rotationHandleBasePoint1 = self.handlePoint1 + norm(v) * 4.0  
                self.rotationHandleBasePoint2 = self.handlePoint2 + norm(v) * 4.0
        return

    def _update_resizeHandle_radius(self):
        """
        Finds out the sphere radius to use for the resize handles, based on 
        atom /chunk or glpane display (whichever decides the display of the end 
        atoms. The default value is 1.2.

        @see: self.updateHandlePositions()
        """
        self.handleSphereRadius1 = HANDLE_RADIUS_DEFAULT_VALUE
        self.handleSphereRadius2 = HANDLE_RADIUS_DEFAULT_VALUE
        return

    def _update_resizeHandle_stopper_length(self):
        """
        Update the limiting length at which the resize handle being dragged
        should 'stop'  without proceeding further in the drag direction. 
        The segment resize handle stops when you are dragging it towards the 
        other resizeend and the distance between the two ends reaches two 
        duplexes. 

        The self._resizeHandle_stopper_length computed in this method is 
        used as a lower limit of the 'range' option provided in declaration
        of resize handle objects (see class definition for the details)
        @see: self.updateHandlePositions()
        """

        total_length = vlen(self.handlePoint1 - self.handlePoint2)        
        nanotubeRise = self.struct.nanotube.getRise()
        self._resizeHandle_stopper_length = - total_length + nanotubeRise
        return

    
    def _gatherParameters(self):
        """
        Return the parameters from the property manager UI.

        @return: The endpoints of the nanotube.
        @rtype:  tuple (endPoint1, endPoint2).
        """     
        return self.propMgr.getParameters()

    def _createStructure(self):
        """
        Returns the current NanotubeSegment being edited with a new nanotube 
        chunk.
        @return : Nanotube segment that include the new nanotube chunk.
        @rtype: L{NanotubeSegment}        
        """
        
        try:
            # Create a new nanotube chunk using new params.
            n, m, type, endings, endPoint1, endPoint2 = self._gatherParameters()
            from cnt.model.NanotubeParameters import NanotubeParameters
            self.nanotube = NanotubeParameters()
            nanotube  =  self.nanotube
            nanotube.setChirality(n, m)
            nanotube.setType(type)
            nanotube.setEndings(endings)
            nanotube.setEndPoints(endPoint1, endPoint2)
            position = V(0.0, 0.0, 0.0)
            ntChunk = nanotube.build(self.struct.name, self.win.assy, position)
            nanotube.computeEndPointsFromChunk(ntChunk) # Needed.
            self.struct.addchild(ntChunk)
            
            #WARNING 2008-03-05:
            #When we actually permit modifying a nanotube without recreating it,
            #then the following properties must be set in self._modifyStructure 
            #as well. Needs more thought.
            props =(nanotube.getChirality(),
                    nanotube.getType(),
                    nanotube.getEndings(),
                    nanotube.getEndPoints())

            self.struct.setProps(props)

            return self.struct

        except (PluginBug, UserError):
            self.struct.kill()
            raise PluginBug("Internal error while trying to recreate a NanotubeSegment.")
        return None

    def _modifyStructure(self, params):
        """
        Modify the structure based on the parameters specified. 
        Overrides EditCommand._modifystructure. This method removes the old 
        nanotube and replaces it with a new one using self._createStructure.
        """    
        if not pref_nt_segment_resize_by_recreating_nanotube():
            self._modifyStructure_NEW_SEGMENT_RESIZE(params)
            return

        # To modify the structure, we need to:
        # 1. Delete the current nanotube chunk inside the NanotubeSegment group.
        # 2. Create a new new nanotube chunk using nanotube.build()
        # 3. Add the newly generated nanotube to this NanotubeSegment.
        # 4. Update all the nanotube parameters.
        # Note: Steps 2-4 are done in self._createStructure()
        assert self.struct
        self.struct.members[0].kill() # Delete the current nanotube chunk.
        self.previousParams = params
        self.struct = self._createStructure()
        return

    def _modifyStructure_NEW_SEGMENT_RESIZE(self, params): #@ NOT FIXED
        """
        This resizes without recreating whole nanotube 
        Overrides EditCommand._modifystructure.
        @attention: is not implemented.
        """        

        #@TODO: - rename this method from _modifyStructure_NEW_SEGMENT_RESIZE
        #to self._modifyStructure, after more testing
        #This method is used for debug prefence: 
        #'Nanotube Segment: resize without recreating whole nanotube'
        #see also self.modifyStructure_NEW_SEGMENT_RESIZE

        assert self.struct      

        from utilities.debug import print_compact_stack
        print_compact_stack("_modifyStructure_NEW_SEGMENT_RESIZE() not fixed!" )
        print "Params =", params

        self.nanotube = params #@

        length_diff =  self._determine_how_to_change_length() #@

        if length_diff == 0:
            print_compact_stack("BUG: length_diff is always ZERO." )
            return
        elif length_diff > 0:
            print "Nanotube longer by ", length_diff, ", angstroms."
        else:
            print "Nanotube shorter by ", length_diff, ", angstroms."

        return

        if numberOfBasePairsToAddOrRemove != 0:   #@@@@ Not reached.

            resizeEnd_final_position = self._get_resizeEnd_final_position(
                ladderEndAxisAtom, 
                abs(numberOfBasePairsToAddOrRemove),
                nanotubeRise )

            self.nanotube.modify(self.struct,
                                 length_diff,
                                 ladderEndAxisAtom.posn(),
                                 resizeEnd_final_position)

        #Find new end points of structure parameters after modification 
        #and set these values in the propMgr. 
        new_end1 , new_end2 = self.struct.nanotube.getEndPoints() #@

        params_to_set_in_propMgr = (new_end1,
                                    new_end2)

        #TODO: Need to set these params in the PM 
        #and then self.previousParams = params_to_set_in_propMgr

        self.previousParams = params
        return  

    def _get_resizeEnd_final_position(self, 
                                      ladderEndAxisAtom, 
                                      numberOfBases, 
                                      nanotubeRise):

        final_position = None   
        if self.grabbedHandle:
            final_position = self.grabbedHandle.currentPosition
        else:
            other_axisEndAtom = self.struct.getOtherAxisEndAtom(ladderEndAxisAtom)
            axis_vector = ladderEndAxisAtom.posn() - other_axisEndAtom.posn()
            segment_length_to_add = 0 #@
            final_position = ladderEndAxisAtom.posn() + norm(axis_vector)*segment_length_to_add

        return final_position

    def getStructureName(self):
        """
        Returns the name string of self.struct if there is a valid structure. 
        Otherwise returns None. This information is used by the name edit field 
        of this command's PM when we call self.propMgr.show()
        @see: EditNanotube_PropertyManager.show()
        @see: self.setStructureName
        """
        if self.hasValidStructure():
            return self.struct.name
        return None

    def setStructureName(self, name):
        """
        Sets the name of self.struct to param <name> (if there is a valid 
        structure. 
        The PM of this command callss this method while closing itself 
        @param name: name of the structure to be set.
        @type name: string
        @see: EditNanotube_PropertyManager.close()
        @see: self.getStructureName()

        """
        #@BUG: We call this method in self.propMgr.close(). But propMgr.close() 
                #is called even when the command is 'cancelled'. That means the 
                #structure will get changed even when user hits cancel button or
                #exits the command by clicking on empty space. 
                #This should really be done in self._finalizeStructure but that 
                #method doesn't get called when you click on empty space to exit 
                #the command. See EditNanotube_GraphicsMode.leftUp for a detailed 
                #comment. 

        if self.hasValidStructure():
            self.struct.name = name
        return

    def getCursorText(self):
        """
        This is used as a callback method in NanotubeLine mode 
        @see: NanotubeLineMode.setParams, NanotubeLineMode_GM.Draw
        """
        if self.grabbedHandle is None:
            return
        
        text = ''
        textColor = env.prefs[cursorTextColor_prefs_key]
        
        if not env.prefs[editNanotubeEditCommand_showCursorTextCheckBox_prefs_key]:
            return text, textColor

        currentPosition = self.grabbedHandle.currentPosition
        fixedEndOfStructure = self.grabbedHandle.fixedEndOfStructure

        nanotubeLength = vlen( currentPosition - fixedEndOfStructure )

        nanotubeLengthString = self._getCursorText_length(nanotubeLength)
        
        text = nanotubeLengthString
   
        #@TODO: The following updates the PM as the cursor moves. 
        #Need to rename this method so that you that it also does more things 
        #than just to return a textString -- Ninad 2007-12-20
        self.propMgr.ntLengthLineEdit.setText(nanotubeLengthString)

        return text, textColor
    
    def _getCursorText_length(self, nanotubeLength):
        """
        Returns a string that gives the length of the Nanotube for the cursor 
        text
        """
        nanotubeLengthString = ''
        if env.prefs[editNanotubeEditCommand_cursorTextCheckBox_length_prefs_key]:
            lengthUnitString = 'A'
            #change the unit of length to nanometers if the length is > 10A
            #fixes part of bug 2856
            if nanotubeLength > 10.0:
                lengthUnitString = 'nm'
                nanotubeLength = nanotubeLength * 0.1
                
            nanotubeLengthString = "%5.3f%s"%(nanotubeLength, lengthUnitString)
        
        return nanotubeLengthString
    
    def modifyStructure(self):
        """
        Called when a resize handle is dragged to change the length of the 
        segment. (Called upon leftUp) . This method assigns the new parameters 
        for the segment after it is resized and calls 
        preview_or_finalize_structure which does the rest of the job. 
        Note that Client should call this public method and should never call
        the private method self._modifyStructure. self._modifyStructure is 
        called only by self.preview_or_finalize_structure

        @see: B{EditNanotube_ResizeHandle.on_release} (the caller)
        @see: B{SelectChunks_GraphicsMode.leftUp} (which calls the 
              the relevent method in DragHandler API. )
        @see: B{exprs.DraggableHandle_AlongLine}, B{exprs.DragBehavior}
        @see: B{self.preview_or_finalize_structure }
        @see: B{self._modifyStructure}        

        As of 2008-02-01 it recreates the structure
        @see: a note in self._createStructure() about use of ntSegment.setProps 
        """

        if not pref_nt_segment_resize_by_recreating_nanotube():
            self.modifyStructure_NEW_SEGMENT_RESIZE()
            return

        if self.grabbedHandle is None:
            return        

        self.propMgr.endPoint1 = self.grabbedHandle.fixedEndOfStructure
        self.propMgr.endPoint2 = self.grabbedHandle.currentPosition
        #@length = vlen(self.propMgr.endPoint1 - self.propMgr.endPoint2 ) #@  

        self.preview_or_finalize_structure(previewing = True)  

        self.updateHandlePositions()
        self.glpane.gl_update()
        return

    def modifyStructure_NEW_SEGMENT_RESIZE(self): #@ NOT FIXED
        """
        Called when a resize handle is dragged to change the length of the 
        segment. (Called upon leftUp) . This method assigns the new parameters 
        for the segment after it is resized and calls 
        preview_or_finalize_structure which does the rest of the job. 
        Note that Client should call this public method and should never call
        the private method self._modifyStructure. self._modifyStructure is 
        called only by self.preview_or_finalize_structure

        @see: B{EditNanotube_ResizeHandle.on_release} (the caller)
        @see: B{SelectChunks_GraphicsMode.leftUp} (which calls the 
              the relevent method in DragHandler API. )
        @see: B{exprs.DraggableHandle_AlongLine}, B{exprs.DragBehavior}
        @see: B{self.preview_or_finalize_structure }
        @see: B{self._modifyStructure}        

        As of 2008-02-01 it recreates the structure
        @see: a note in self._createStructure() about use of ntSegment.setProps 
        """
        #TODO: need to cleanup this and may be use use something like
        #self.previousParams = params in the end -- 2008-03-24 (midnight)


        #@TODO: - rename this method from modifyStructure_NEW_SEGMENT_RESIZE
        #to self.modifyStructure, after more testing
        #This method is used for debug prefence: 
        #'Nanotube Segment: resize without recreating whole duplex'
        #see also self._modifyStructure_NEW_SEGMENT_RESIZE

        if self.grabbedHandle is None:
            return   

        self.propMgr.endPoint1 = self.grabbedHandle.fixedEndOfStructure
        self.propMgr.endPoint2 = self.grabbedHandle.currentPosition

        DEBUG_DO_EVERYTHING_INSIDE_MODIFYSTRUCTURE_METHOD = False

        if DEBUG_DO_EVERYTHING_INSIDE_MODIFYSTRUCTURE_METHOD:

            # TO DO: this entire block of code.  --Mark 2008-04-03
            print_compact_stack("modifyStructure_NEW_SEGMENT_RESIZE(): NOT FIXED")

            length = vlen(self.grabbedHandle.fixedEndOfStructure - \
                          self.grabbedHandle.currentPosition )

            endAtom1, endAtom2 = self.struct.getAxisEndAtoms() #@

            for atm in (endAtom1, endAtom2):
                if not same_vals(self.grabbedHandle.fixedEndOfStructure, atm.posn()):
                    ladderEndAxisAtom = atm
                    break

                endPoint1, endPoint2 = self.struct.nanotube.getEndPoints()
                old_dulex_length = vlen(endPoint1 - endPoint2)

                nanotubeRise = self.struct.getProps()      #@ 

                params_to_set_in_propMgr = (
                    self.grabbedHandle.origin,
                    self.grabbedHandle.currentPosition,
                )

                ##self._modifyStructure(params)
                ############################################

                self.nanotube = NanotubeParameters() #@ Creates 5x5 CNT. Missing PM params.

                length_diff =  self._determine_how_to_change_length()  
                ladderEndAxisAtom = self.get_axisEndAtom_at_resize_end() #@

                #@ Nanotube class needs modify() method.
                self.nanotube.modify(self.struct, 
                                     length_diff,
                                     ladderEndAxisAtom.posn(),
                                     self.grabbedHandle.currentPosition)

        #TODO: Important note: How does NE1 know that structure is modified? 
        #Because number of base pairs parameter in the PropMgr changes as you 
        #drag the handle . This is done in self.getCursorText() ... not the 
        #right place to do it. OR that method needs to be renamed to reflect
        #this as suggested in that method -- Ninad 2008-03-25

        self.preview_or_finalize_structure(previewing = True) 

        ##self.previousParams = params_to_set_in_propMgr

        self.glpane.gl_update()
        return

    def get_axisEndAtom_at_resize_end(self):
        ladderEndAxisAtom = None
        if self.grabbedHandle is not None:
            ladderEndAxisAtom = self.struct.getAxisEndAtomAtPosition(self.grabbedHandle.origin)
        else:
            endAtom1, endAtom2 = self.struct.getAxisEndAtoms()
            ladderEndAxisAtom = endAtom2

        return ladderEndAxisAtom

    def _determine_how_to_change_length(self): #@ NEEDS WORK
        """
        Returns the difference in length between the original nanotube and the
        modified nanotube, where:
           0 = no change in length
         > 0 = lengthen
         < 0 = trim
        """
        nanotubeRise = self.struct.nanotube.getRise()
        endPoint1, endPoint2 = self.struct.nanotube.getEndPoints() #@ 
        original_nanotube_length = vlen(endPoint1 - endPoint2)
        new_nanotube_length      = vlen(endPoint1 - endPoint2) #@
        return new_nanotube_length - original_nanotube_length #@ ALWAYS RETURNS ZERO

    def makeMenus(self): 
        """
        Create context menu for this command.
        """
        if not hasattr(self, 'graphicsMode'):
            return

        selobj = self.glpane.selobj

        if selobj is None:
            return

        self.Menu_spec = []

        highlightedChunk = None
        if isinstance(selobj, Chunk):
            highlightedChunk = selobj
        if isinstance(selobj, Atom):
            highlightedChunk = selobj.molecule
        elif isinstance(selobj, Bond):
            chunk1 = selobj.atom1.molecule
            chunk2 = selobj.atom2.molecule
            if chunk1 is chunk2 and chunk1 is not None:
                highlightedChunk = chunk1

        if highlightedChunk is None:
            return

        highlightedChunk.make_glpane_cmenu_items(self.Menu_spec, self)
        return