summaryrefslogtreecommitdiff
path: root/cad/src/command_support/EditCommand.py
blob: b69728c8a155b7fbb6fbabe61c380550ba8afb53 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
# Copyright 2007-2008 Nanorex, Inc.  See LICENSE file for details. 
"""
EditCommand.py

@author: Bruce Smith, Mark Sims, Ninad Sathaye, Will Ware
@version: $Id$
@copyright: 2007-2008 Nanorex, Inc.  See LICENSE file for details.

History:

- Originally created as 'GeometryGeneratorBaseClass'. It shared common code with 
GeneratorBaseClass but had some extra features to support creation and edition 
of a 'Plane' [June-Sept 2007]. It was split out of ReferenceGeometry.py
- Before 2007-12-28, Editcommand was known as 'EditController' 


Ninad 2007-09-17: Code cleanup to split ui part out of this class. 
Ninad 2007-10-05: Major changes. Refactored GeometryGeneratorBaseClass 
                  and surrounding code. Also renamed GeometryGeneratorBaseClass 
                  to EditCommand, similar changes in surrounding code
Ninad 2007-10-24: Changes to convert the old structure generators
                  such as DnaGenerator / DnaDuplexGenerator to use the 
                  EditCommand class (and their PMs to use EditCommand_PM)
Ninad 2007-12-26: Converted editControllers into Commands on commandSequencer

TODO:
- Need to cleanup docstrings. 
- In subclasses such as InsertDna_EditCommand, the method createStructure do 
  nothing (user is not immediately creating a structure) .
  Need to clean this up a bit in this class and in the  surrounding code
- New subclass InsertDna_EditCommand adds the structure as a node in the MT 
  in its _createStructure method. This should also be implemented for 
  following sublasses:  Plane_EditCommand, LineEditCommand, motor
  editcommand classes.
"""

import foundation.changes as changes
from foundation.FeatureDescriptor import register_abstract_feature_class

from utilities.Comparison import same_vals

from utilities.constants import permit_gensym_to_reuse_name
from utilities.exception_classes import AbstractMethod

from commands.Select.Select_Command import Select_Command


_superclass = Select_Command
class EditCommand(Select_Command):
    """
    EditCommand class that provides a editCommand object. 
    The client can call three public methods defined in this class to acheive 
    various things. 
    1. runCommand -- Used to run this editCommand . Depending upon the
       type of editCommand it is, it does various things. The most common 
       thing it does is to create and show  a property manager (PM) The PM 
       is used by the editCommand to define the UI for the model
       which this editCommand creates/edits. 
       See InsertDna_EditCommand.runCommand for an example 
    2. createStructure -- Used directly by the client when it already knows 
       input parameters for the structure being generated. This facilitates 
       imeediate preview of the model being created when you execute this 
       command. 
       See self.createStructure  which is the default implementation used
       by many subclasses such as RotaryMotor_EditCommand etc. 
    3. editStructure -- Used directly by the client when it needs to edit 
       an already created structure. 
       See self.editStructure for details.

    TODO: NEED TO IMPROVE DOCSTRING FURTHER
    """
    # see definition details in GeneratorBaseClass;
    # most of these should be overridden by each specific subclass
    cmd      =  "" 
    cmdname  =  "" 
    _gensym_data_for_reusing_name = None
    commandName = 'EditCommand'
    featurename = "Undocumented Edit Command" # default wiki help featurename
    from utilities.constants import CL_ABSTRACT
    command_level = CL_ABSTRACT
    __abstract_command_class = True
 
    flyoutToolbar = None
    
    def __init__(self, commandSequencer):
        """
        Constructor for the class EditCommand.        
        """
        self.win = commandSequencer.win
        self.previousParams       =  None
        self.old_props            =  None
        self.logMessage           = ''

        self.struct               =  None
        self.existingStructForEditing  =  False
        
        Select_Command.__init__(self, commandSequencer)
        return
         
    #=== START   NEW COMMAND API methods  ======================================
   
    
    
    def command_enter_misc_actions(self):
        pass
    
    def command_exit_misc_actions(self):
        pass     
    
    
    def command_will_exit(self):
        """
        Overrides superclass method. 
        @see: baseCommand.command_will_exit() for documentation
        """
        if self.commandSequencer.exit_is_forced:
            pass
        elif self.commandSequencer.exit_is_cancel:
            self.cancelStructure()
        else:
            self.preview_or_finalize_structure(previewing = False)
        
        _superclass.command_will_exit(self)
                    
    def runCommand(self):
        """        
        Used to run this editCommand . Depending upon the
        type of editCommand it is, it does various things. The most common 
        thing it does is to create and show  a property manager (PM) The PM 
        is used by the editCommand to define the UI for the model
        which this editCommand creates/edits. 
        See InsertDna_EditCommand.runCommand for an example
        Default implementation, subclasses should override this method.
        NEED TO DOCUMENT THIS FURTHER ?
        """
        self.existingStructForEditing = False        
        self.struct = None
        self.createStructure()

    def createStructure(self):
        """
        Default implementation of createStructure method. 
        Might be overridden in  subclasses. Creates an instance (object)
        of the structure this editCommand wants to generate. This implements
        a topLevel command that the client can execute to create an object it 
        wants.

        Example: If its a plane editCommand, this method will create an 
                object of class Plane. 

        @see: L{self.editStructure} (another top level command that facilitates
              editing an existing object (existing structure). 
        """
        
        assert not self.struct
        
        self.struct = self._createStructure()

        if not self.hasValidStructure():
            return                 
       
        if self.struct:
            #When a structure is created first, set the self.previousParams 
            #to the struture parameters. This makes sure that it doesn't 
            # unnecessarily do self._modifyStructure in 
            # self.preview_or_finalize_structure  -- Ninad 2007-10-11
            self.previousParams = self._gatherParameters()
            self.preview_or_finalize_structure(previewing = True)        
            

    def editStructure(self, struct = None):
        """
        Default implementation of editStructure method. Might be overridden in 
        subclasses. It facilitates editing an existing object 
        (existing structure). This implements a topLevel command that the client
        can execute to edit an existing object(i.e. self.struct) that it wants.

        Example: If its a plane edit controller, this method will be used to 
                edit an object of class Plane. 

        This method also creates a propMgr objects if it doesn't exist and 
        shows this property manager 

        @see: L{self.createStructure} (another top level command that 
              facilitates creation of a model object created by this 
              editCommand
        @see: L{Plane.edit} and L{Plane_EditCommand._createPropMgrObject} 
        """
        assert struct        
        self.struct = struct
        self.existingStructForEditing = True
        self.old_props = self.struct.getProps()
        
    def hasValidStructure(self):
        """
        Tells the caller if this edit command has a valid structure.

        This is the default implementation, overridden by the subclasses. 

        Default implementation:
        This method checks for a few common things and by default, in the end
        returns True. A subclass can first call this superclass method, 
        return False if superclass does so, if superclass returns True
        the subclass can then check the additional things

        @see: DnaSegment_EditCommand.hasValidStructure() 
        """ 
        if self.struct is None:
            return False  

        structType = self._getStructureType()

        if not isinstance(self.struct, structType): 
            return False   

        #The following can happen. 
        if hasattr(self.struct, 'killed') and self.struct.killed():
            return False           

        #Retrn True otherwise. Its now subclass's job to check additional things
        return True   

    def _getStructureType(self):
        """
        Subclasses must 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: self.hasValidStructure()
        """
        print "bug: EditCommand._getStructureType not overridden in a subclass"
        raise AbstractMethod()


    def _createStructure(self):
        """
        Create the model object which this edit controller  creates) 
        Abstract method.         
        @see: L{Plane_EditCommand._createStructure}
        """
        raise AbstractMethod()

   
    def _modifyStructure(self, params):
        """
        Abstract method that modifies the current object being edited.
        @param params: The parameters used as an input to modify the structure
                       (object created using this editCommand) 
        @type  params: tuple
        @see: L{Plane_EditCommand._modifyStructure}
        """
        raise AbstractMethod()

    def _gatherParameters(self):
        """
        Abstract method to be overridden by subclasses to return all 
        the parameters needed to modify or (re)create the current structure.
        """
        raise AbstractMethod()

    def preview_or_finalize_structure(self, previewing = False):
        """
        Preview or finalize the structure based on the the previewing flag
        @param previewing: If true, the structure will use different cosmetic 
                           properties. 
        @type  previewing: boolean
        """

        if previewing:
            self._previewStructure()
        else:
            self._finalizeStructure()

        return

    def _previewStructure(self):
        """
        Preview the structure and update the previous parameters attr 
        (self.previousParams)
        @see: self.preview_or_finalize_structure
        """
        
        #For certain edit commands, it is possible that self.struct is 
        #not created. If so simply return (don't use assert self.struct)
        ##This is a commented out stub code for the edit controllers 
        ##such as DNAEditCommand which take input from the user before 
        ##creating the struct. TO BE REVISED -- Ninad20071009
        #The following code is now used.  Need to improve comments and 
        # some refactoring -- Ninad 2007-10-24

        if not self.hasValidStructure():  
            self.struct = self._createStructure()
            self.previousParams = self._gatherParameters()
            self.win.assy.changed()
            self.win.win_update()
            return  

        self.win.assy.current_command_info(cmdname = self.cmdname) 

        params = self._gatherParameters()

        if not same_vals( params, self.previousParams):
            self._modifyStructure(params)    

        self.logMessage = str(self.cmd + "Previewing " + self.struct.name)
        self.previousParams = params      
        self.win.assy.changed()
        self.win.win_update()


    def _finalizeStructure(self):
        """
        Finalize the structure. This is a step just before calling Done method.
        to exit out of this command. Subclasses may overide this method
        @see: EditCommand_PM.ok_btn_clicked
        @see: DnaSegment_EditCommand where this method is overridden. 
        """
        if self.struct is None:
            return

        self.win.assy.current_command_info(cmdname = self.cmdname) 

        params = self._gatherParameters()

        if not same_vals( params, self.previousParams):
            self._modifyStructure(params)    

        if hasattr(self.struct, 'updateCosmeticProps'):
            self.struct.updateCosmeticProps()
            self.logMessage = str(self.cmd + "Created " + self.struct.name)

        #Do we need to set the self.previousParams even when the structure 
        #is finalized? I think this is unnecessary but harmless to do. 
        self.previousParams = params      

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


    def cancelStructure(self):
        """
        Delete the old structure generated during preview if user modifies
        some parameters or hits cancel. Subclasses can override this method. 
        @see: BuildDna_EditCommand.cancelStructure
        """
        if self.struct is None:
            return 
        
        self.win.assy.current_command_info(cmdname = self.cmdname + " (Cancel)")
        if self.existingStructForEditing: 
            if self.old_props:
                self.struct.setProps(self.old_props)
                if hasattr(self.struct, 'updateCosmeticProps'):
                    self.struct.updateCosmeticProps()
                self.win.glpane.gl_update()               
        else:
            self._removeStructure()

    def _removeStructure(self):
        """
        Remove this structure. 
        @see: L{self.cancelStructure}
        """
        if self.struct is not None:   
            self.struct.kill_with_contents()
            self.struct = None       
            self._revertNumber()
            self.win.win_update() 

    def _revertNumber(self):
        """
        Private method. Called internally when we discard the current structure
        and want to permit a number which was appended to its name to be reused.

        WARNING: the current implementation only works for classes which set
        self.create_name_from_prefix
        to cause our default _build_struct to set the private attr we use
        here, self._gensym_data_for_reusing_name, or which set it themselves
        in the same way (when they call gensym).

        This method is copied over from GeneratorBaseClass._revert_number
        """
        if self._gensym_data_for_reusing_name:
            prefix, name = self._gensym_data_for_reusing_name
                # this came from our own call of gensym, or from a caller's if
                # it decides to set that attr itself when it calls gensym
                # itself.
            permit_gensym_to_reuse_name(prefix, name)
        self._gensym_data_for_reusing_name = None
        return

    pass # end of class EditCommand

register_abstract_feature_class( EditCommand )
    # this is so "export command table" lists it as a separate kind of feature
    # [bruce 080905]

# end