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

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

The 'Command' part of the BuildAtoms Mode (BuildAtoms_Command and
BuildAtoms_basicGraphicsMode are the two split classes of the old
depositMode). It provides the command object for its GraphicsMode class.
The Command class defines anything related to the 'command half' of the mode --
For example:
- Anything related to its current Property Manager, its settings or state
- The model operations the command does (unless those are so simple
  that the mouse event bindings in the _GM half can do them directly
  and the code is still clean, *and* no command-half subclass needs
  to override them).

TODO: [as of 2008-09-25]
- Items mentioned in Build_GraphicsMode.py

History:

Originally as 'depositMode.py' by Josh Hall and then significantly modified by
several developers.
In January 2008, the old depositMode class was split into new Command and
GraphicsMode parts and the these classes were moved into their own module
[ See BuildAtoms_Command.py and BuildAtoms_GraphicsMode.py]
"""
from PyQt4.Qt import QSize


import foundation.env as env
import foundation.changes as changes
from utilities.debug import print_compact_traceback
from utilities.prefs_constants import buildModeHighlightingEnabled_prefs_key
from utilities.prefs_constants import buildModeWaterEnabled_prefs_key
from utilities.prefs_constants import keepBondsDuringTransmute_prefs_key

from commands.BuildAtoms.BuildAtomsPropertyManager import BuildAtomsPropertyManager
from commands.SelectAtoms.SelectAtoms_Command import SelectAtoms_Command

from commands.BuildAtoms.BuildAtoms_GraphicsMode import BuildAtoms_GraphicsMode
from ne1_ui.toolbars.Ui_BuildAtomsFlyout import BuildAtomsFlyout

_superclass = SelectAtoms_Command

class BuildAtoms_Command(SelectAtoms_Command):
    """
    As of 2008-09-16, the BuildAtoms_Command has two tools :
    Atoms tool and Bonds tool. At any point of time, user is using either
    of those tools. so, the user is never in the default
    'BuildAtoms_Command'. When user clicks on Build > Atoms button,
    he/she invokes BuildAtoms_Command. As soon as this command is entered,
    program invokes one of the two tools (subcommand). By default,
    we always invoke 'Atoms Tool'.
    @see: command_update_state()
    @see: B{AtomsTool_Command} , B{BondsTool_command}
    """
    #GraphicsMode
    GraphicsMode_class = BuildAtoms_GraphicsMode

    #The class constant PM_class defines the class for the Property Manager of
    #this command. See Command.py for more infor about this class constant
    PM_class = BuildAtomsPropertyManager

    #Flyout Toolbar
    FlyoutToolbar_class = BuildAtomsFlyout

    commandName = 'DEPOSIT'
    featurename = "Build Atoms Mode"
    from utilities.constants import CL_ENVIRONMENT_PROVIDING
    command_level = CL_ENVIRONMENT_PROVIDING

    highlight_singlets = True

    #graphicsMode will be set in BuildAtoms_Command.__init__ .
    graphicsMode = None

    flyoutToolbar = None

    _currentActiveTool = 'ATOMS_TOOL'

    def __init__(self, commandSequencer):
        _superclass.__init__(self, commandSequencer)

        #Initialize some more attributes.
        self._pastable_atomtype = None

        #The following flag, if set to True, doesn't allow converting
        #bonds between the selected atoms to the bondtyp specified in the
        #flyout toolbar. This is used only while activating the
        #bond tool. See self._convert_bonds_bet_selected_atoms() for details
        self._suppress_convert_bonds_bet_selected_atoms = False

    def command_enter_misc_actions(self):
        """
        Overrides superclass method.

        @see: baseCommand.command_enter_misc_actions()  for documentation
        """
        self.w.toolsDepositAtomAction.setChecked(True)

    def command_exit_misc_actions(self):
        """
        Overrides superclass method.

        @see: baseCommand.command_exit_misc_actions()  for documentation
        """
        self.w.toolsDepositAtomAction.setChecked(False)

    def command_update_state(self):
        """
        See superclass for documentation.
        Note that this method is called only when self is the currentcommand on
        the command stack.
        @see: BuildAtomsFlyout.resetStateOfActions()
        @see: self.activateAtomsTool()
        """
        _superclass.command_update_state(self)

        #Make sure that the command Name is DEPOSIT. (because subclasses
        #of BuildAtoms_Command might be using this method).
        #As of 2008-09-16, the BuildAtoms_Command has two tools :
        #Atoms tool and Bonds tool. At any point of time, user is using either
        #of those tools. so, the user is never in the default
        #'BuildAtoms_Command'. When user clicks on Build > Atoms button,
        #he/she invokes BuildAtoms_Command. As soon as this command is entered,
        #we need to invoke one of the two tools (subcommand).
        #By default, we always invoke 'Atoms Tool'.
        if self.commandName == 'DEPOSIT':
            ## print "**** in BuildAtoms.command_update_state"
            self.activateAtomsTool()

    def enterToolsCommand(self, commandName = ''): #REVIEW
        """
        Enter the given tools subcommand (e.g. Atoms tool or one of the bond
        tools)
        """
        if not commandName:
            return

        commandSequencer = self.win.commandSequencer
        commandSequencer.userEnterCommand( commandName)

    def getBondTypeString(self):
        """
        Overridden in subclasses.
        """
        return ''

    def viewing_main_part(self): #bruce 050416 ###e should refile into assy
        return self.o.assy.current_selgroup_iff_valid() is self.o.assy.tree

    def select_chunk_of_highlighted_atom(self):
        """Select the chunk containing the highlighted atom or singlet"""
        if self.o.selatom:
            self.o.assy.permit_pick_parts()
            # was unpickparts; I think this is the intent (and the effect,
            # before now) [bruce 060721]
            self.o.assy.unpickall_in_GLPane()
            self.o.selatom.molecule.pick()
            self.w.win_update()

    def toggleShowOverlayText(self):
        if (self.o.selatom):
            chunk = self.o.selatom.molecule
            chunk.showOverlayText = not chunk.showOverlayText
            self.w.win_update()

    def makeMenus(self): #bruce 050705 revised this to support bond menus
        """
        Create context menu for this command. (Build Atoms mode)
        """
        selatom, selobj = self.graphicsMode.update_selatom_and_selobj( None)
            # bruce 050612 added this [as an update_selatom call] --
            #not needed before since bareMotion did it (I guess).
            # [replaced with update_selatom_and_selobj, bruce 050705]

        self.Menu_spec = []
        ###e could include disabled chunk & selatom name at the top, whether
        ##selatom is singlet, hotspot, etc.

        # figure out which Set Hotspot menu item to include, and whether to
        #disable it or leave it out
        if self.viewing_main_part():
            text, meth = ('Set Hotspot and Copy',
                          self.graphicsMode.setHotSpot_mainPart)
                # bruce 050121 renamed this from "Set Hotspot" to "Set Hotspot
                # and Copy as Pastable".
                # bruce 050511 shortened that to "Set Hotspot and Copy".
                # If you want the name to be shorter, then change the method
                # to do something simpler! Note that the locally set hotspot
                # matters if we later drag this chunk to the clipboard.
                # IMHO, the complexity is a sign that the design
                # should not yet be considered finished!
        else:
            text, meth = ('Set Hotspot of clipboard item',
                          self.graphicsMode.setHotSpot_clipitem)
                ###e could check if it has a hotspot already, if that one is
                ##  different, etc
                ###e could include atom name in menu text... Set Hotspot to X13
        if selatom is not None and selatom.is_singlet():
            item = (text, meth)
        elif selatom is not None:
            item = (text, meth, 'disabled')
        else:
            item = None
        if item:
            self.Menu_spec.append(item)

        # Add the trans-deposit menu item.
        if selatom is not None and selatom.is_singlet():
            self.Menu_spec.append((
                'Trans-deposit previewed item',
                lambda dragatom=selatom: self.graphicsMode.transdepositPreviewedItem(dragatom) ))

        # figure out Select This Chunk item text and whether to include it
        ##e (should we include it for internal bonds, too? not for now, maybe
        ## not ever. [bruce 050705])
        if selatom is not None:
            name = selatom.molecule.name
            item = ('Select Chunk %r' % name,
                    self.select_chunk_of_highlighted_atom)
                #e maybe should disable this or change to checkmark item (with
                #unselect action) if it's already selected??
            self.Menu_spec.append(item)
            chunk = selatom.molecule
            if (chunk.chunkHasOverlayText):
                # note: this is only a hint, but since it's updated whenever
                # chunk is drawn, I suspect it will always be up to date at
                # this point. (If not, that's ok -- these commands will just
                # be noops.) [bruce 090112 comment]
                if (chunk.showOverlayText):
                    item = ('Hide overlay text on %r' % name, self.toggleShowOverlayText)
                else:
                    item = ('Show overlay text on %r' % name, self.toggleShowOverlayText)
                self.Menu_spec.append(item)

        ##e add something similar for bonds, displaying their atoms, and the
        ##bonded chunk or chunks?

        if selatom is not None:
            #k is 2nd cond redundant with is_singlet()?
            is_singlet = selatom.is_singlet() and len(selatom.bonds) == 1
        else:
            is_singlet = False

        # add submenu to change atom hybridization type [initial kluge]
        atomtypes = (selatom is None) and ['fake'] or selatom.element.atomtypes
            # kluge: ['fake'] is so the idiom "x and y or z" can pick y;
            # otherwise we'd use [] for 'y', but that doesn't work since it's
            #false.
##        if selatom is not None and not selatom.is_singlet():
##            self.Menu_spec.append(( '%s' % selatom.atomtype.fullname_for_msg(),
            ##noop, 'disabled' ))
        if len(atomtypes) > 1: # i.e. if elt has >1 atom type available!
            #(then it must not be Singlet, btw)
            # make a submenu for the available types, checkmarking the current
            #one, disabling if illegal to change, sbartext for why
            # (this code belongs in some more modular place... where exactly?
            #it's part of an atom-type-editor for use in a menu...
            #  put it with Atom, or with AtomType? ###e)
            submenu = []
            for atype in atomtypes:
                submenu.append((
                    atype.fullname_for_msg(),
                    lambda arg1=None,
                    arg2=None, atype=atype: atype.apply_to(selatom),
                    # Notes: the atype=atype is required
                    # -- otherwise each lambda refers to the same
                    # localvar 'atype' -- effectively by reference,
                    # not by value --
                    # even though it changes during this loop!
                    #   Also at least one of the arg1 and arg2 are required,
                    # otherwise atype ends up being an int,
                    # at least acc'd to exception we get here. Why is Qt passing
                    # this an int? Nevermind for now. ###k
                             (atype is selatom.atomtype) and 'checked' or None,
                             (not atype.ok_to_apply_to(selatom)) and 'disabled' or None
                           ))
            self.Menu_spec.append(( 'Atom Type: %s' % selatom.atomtype.fullname_for_msg(), submenu ))
##            self.Menu_spec.append(( 'Atom Type', submenu ))

        ###e offer to change element, too (or should the same submenu be used,
        ##with a separator? not sure)

        # for a highlighted bond, add submenu to change bond type, if atomtypes
        # would permit that;
        # or a menu item to just display the type, if not. Also add summary info
        # about the bond...
        # all this is returned (as a menu_spec sublist) by one external helper
        # method.

        if is_singlet:
            selbond = selatom.bonds[0]
        else:
            selbond = selobj # might not be a Bond (could be an Atom or None)

        try:
            method = selbond.bond_menu_section
        except AttributeError:
            # selbond is not a Bond
            pass
        else:
            glpane = self.o
            quat = glpane.quat
            try:
                menu_spec = method(quat = quat) #e pass some options??
            except:
                print_compact_traceback("exception in bond_menu_section for %r, ignored: " % (selobj,))
            else:
                if menu_spec:
                    self.Menu_spec.extend(menu_spec)
                pass
            pass

        # Local minimize [now called Adjust Atoms in history/Undo, Adjust <what>
        #here and in selectMode -- mark & bruce 060705]
        # WARNING: This code is duplicated in selectMode.makeMenus().
        # mark 060314.
        if selatom is not None and not selatom.is_singlet() and \
           self.w.simSetupAction.isEnabled():
            # if simSetupAction is not enabled, a sim process is running.
            #Fixes bug 1283. mark 060314.
            ## self.Menu_spec.append(( 'Adjust atom %s' % selatom,
            ##selatom.minimize_1_atom )) # older pseudocode
            # experimental. if we leave in these options, some of them might
            # want a submenu.
            # or maybe the layer depth is a dashboard control? or have buttons
            # instead of menu items?
            self.Menu_spec.append(( 'Adjust atom %s' % selatom,
                                    lambda e1=None,
                                    a = selatom: self.localmin(a,0) ))
            self.Menu_spec.append(( 'Adjust 1 layer',
                                    lambda e1=None,
                                    a = selatom: self.localmin(a,1) ))
            self.Menu_spec.append(( 'Adjust 2 layers',
                                    lambda e1=None,
                                    a = selatom: self.localmin(a,2) ))

        # offer to clean up singlet positions (not sure if this item should be
        # so prominent)
        if selatom is not None and not selatom.is_singlet():
            sings = selatom.singNeighbors() #e when possible, use
            #baggageNeighbors() here and remake_baggage below. [bruce 051209]
            if sings or selatom.bad():
                if sings:
                    text = 'Reposition bondpoints'
                        # - this might be offered even if they don't need
                        # repositioning;
                        # not easy to fix, but someday we'll always reposition
                        # them whenever needed
                        # and this menu command can be removed then.
                        # - ideally we'd reposition H's too (i.e.
                        #  call remake_baggage below)
                else:
                    text = 'Add bondpoints' # this text is only used if it
                                            #doesn't have enough
                cmd = (lambda a = selatom: self.RepositionBondpoints_command(a))
                self.Menu_spec.append(( text, cmd ))
                ##e should finish and use remake_baggage (and baggageNeighbors)

        # selobj-specific menu items.
        # This is duplicated in selectMode.makeMenus().
        # [bruce 060405 added try/except, and generalized this from Jig-specific
        # to selobj-specific items,
        #  by replacing isinstance(selobj, Jig) with hasattr(selobj,
        # 'make_selobj_cmenu_items'),
        #  so any kind of selobj can provide more menu items using this API.
        #  Note that the only way selobj could customize its menu items to the
        #  calling command or its graphicsMode
        #  would be by assuming that was the currentCommand or its graphicsMode.
        #  Someday we might extend the API
        #  to pass it glpane, so we can support multiple glpanes, each in a
        # different command and/or graphicsMode. #e]
        if selobj is not None and hasattr(selobj, 'make_selobj_cmenu_items'):
            try:
                selobj.make_selobj_cmenu_items(self.Menu_spec)
            except:
                print_compact_traceback("bug: exception (ignored) in make_selobj_cmenu_items for %r: " % selobj)

        # separator and other mode menu items.
        if self.Menu_spec:
            self.Menu_spec.append(None)

        # Enable/Disable Jig Selection.
        # This is duplicated in selectMode.makeMenus() and s
        # electMolsMode.makeMenus().
        if self.o.jigSelectionEnabled:
            self.Menu_spec.extend( [('Enable Jig Selection',
                                     self.graphicsMode.toggleJigSelection,
                                     'checked')])
        else:
            self.Menu_spec.extend( [('Enable Jig Selection',
                                     self.graphicsMode.toggleJigSelection,
                                     'unchecked')])

        self.Menu_spec.extend( [
            # mark 060303. added the following:
            None,
            ('Edit Color Scheme...', self.w.colorSchemeCommand),
            ])

        self.Menu_spec_shift = list(self.Menu_spec) #bruce 060721 experiment;
            # if it causes no harm, we can
            # replace the self.select item in the copy with one for
            #shift-selecting the chunk, to fix a bug/NFR 1833 ####@@@@
            # (we should also rename self.select)

        return # from makeMenus

    def RepositionBondpoints_command(self, atom):
        del self
        atom.remake_bondpoints()
        atom.molecule.assy.glpane.gl_update() #bruce 080216 bugfix
        return

    def isHighlightingEnabled(self):
        """
        overrides superclass method.
        @see: anyCommand.isHighlightingEnabled()
        """
        return env.prefs[buildModeHighlightingEnabled_prefs_key]

    def isWaterSurfaceEnabled(self):
        """
        overrides superclass method.
        @see: BuildAtoms_Command.isWaterSurfaceEnabled()

        """
        return env.prefs[buildModeWaterEnabled_prefs_key]

    def isAtomsToolActive(self):
        """
        Tells whether the Atoms Tool is active (boolean)
        @return: The checked state of B{self.depositAtomsAction}
        """
        #TODO: It relies on self.depositAtomsAction to see if the tool is active
        #There needs to be a better way to tell this. One idea is to
        #test which graphicsMode / Command is currently being used.
        #But for that, Bond Tools needs to be a separate command on the
        #command stack instead of a part of Build Atoms Command. So, in the
        #near future, we need to refactor Build Atoms command to separate out
        # Atoms and Bonds tools. -- Ninad 2008-01-03 [commented while splitting
        # legacy depositMode class into Command/GM classes]
        ##return self.depositAtomsAction.isChecked()
        return self._currentActiveTool == 'ATOMS_TOOL'

    def isBondsToolActive(self):
        """
        Tells whether the Bonds Tool is active (boolean)
        @return: The opposite of the checked state of B{self.depositAtomsAction}
        @see: comment in self.isAtomsToolActive()
        """
        # Note: the intent of self.bondclick_v6 was to be true only when this
        # should return true,
        # but the Atom tool does not yet conform to that,
        # and the existing code as of 060702 appears to use this condition,
        # so for now [bruce 060702] it's deemed the official condition.
        # But I can't tell for sure whether the other conditions (modkeys, or
        # commented out access to another button)
        # belong here, so I didn't copy them here but left the code in
        #bondLeftUp unchanged (at least for A8).

        ##return not self.depositAtomsAction.isChecked()
        return self._currentActiveTool == 'BONDS_TOOL'

    def isDeleteBondsToolActive(self):
        """
        Overridden in subclasses.
        Tells whether the Delete Bonds tool is active (boolean)
        @see: comment in self.isAtomsToolActive()
        """
        #Note: this method will be removed soon.
        return False


    def activateAtomsTool(self):
        """
        Activate the atoms tool of the Build Atoms mode
        hide only the Atoms Tools groupbox in the Build Atoms Property manager
        and show all others the others.
        @see: self.command_update_state()
        @see: BuildAtomsFlyout.resetStateOfActions()
        """
        self._currentActiveTool = 'ATOMS_TOOL'

        self.propMgr.bondToolsGroupBox.hide()

        for grpbox in self.propMgr.previewGroupBox, self.propMgr.elementChooser:
            grpbox.show()

        #Not sure if the following is needed. pw is None when this method is
        #called from self.command_update_state() .. cause unknown as
        #of 2008-09-16. Commenting out this line [-- Ninad]
        ##self.propMgr.pw.propertyManagerScrollArea.ensureWidgetVisible(
            ##self.propMgr.headerFrame)

        self.graphicsMode.update_cursor()

        self.w.depositState = 'Atoms'

        self.propMgr.updateMessage()

        self.enterToolsCommand('ATOMS_TOOL')

        self.win.win_update()



    def activateBondsTool(self):
        """
        Activate the bond tool of the Build Atoms mode
        Show only the Bond Tools groupbox in the Build Atoms Property manager
        and hide the others.
        @see:self._convert_bonds_bet_selected_atoms()
        """
        self._currentActiveTool = 'BONDS_TOOL'


        for widget in (self.propMgr.previewGroupBox,
                       self.propMgr.elementChooser,
                       self.propMgr.atomChooserComboBox):
            widget.hide()



        self.propMgr.bondToolsGroupBox.show()

        self.propMgr.pw.propertyManagerScrollArea.ensureWidgetVisible(
            self.propMgr.headerFrame)


        #note: its okay if the check_action is None
        checked_action = self.flyoutToolbar.getCheckedBondToolAction()
        self.changeBondTool(action = checked_action)

        bondToolActions =  self.flyoutToolbar.getBondToolActions()
        for btn in self.propMgr.bondToolButtonRow.buttonGroup.buttons():
            btnId = self.propMgr.bondToolButtonRow.buttonGroup.id(btn)
            action = bondToolActions[btnId]
            btn.setIconSize(QSize(24,24))
            btn.setDefaultAction(action)

        self.win.win_update()


    def changeBondTool(self, action):
        """
        Change the bond tool (e.g. single, double, triple, aromatic
        and graphitic) depending upon the checked action.
        @param: action is the checked bond tool action in the
        bondToolsActionGroup
        """
        bondTool_commandName = 'BOND_TOOL'

        if action is not None:
            objectName = action.objectName()
            prefix = 'ACTION_'
            #Note: objectName is a QString to convert it to a python string
            #first
            objectName = str(objectName)
            if objectName and objectName.startswith(prefix):
                objectName = ''.join(objectName)
                bondTool_commandName = objectName[len(prefix):]

        self.enterToolsCommand(bondTool_commandName)

    #=== Cursor id

    def get_cursor_id_for_active_tool(self):
        """
        Provides a cursor id (int) for updating cursor in graphics mode,
        based on the checked action in its flyout toolbar. (i.e. based on the
        active tool)
        @see: BuildAtoms_GraphicsMode.update_cursor_for_no_MB_selection_filter_disabled
        """
        if hasattr(self.flyoutToolbar, 'get_cursor_id_for_active_tool'):
            return self.flyoutToolbar.get_cursor_id_for_active_tool()

        return 0

    #== Transmute helper methods =======================

    def get_atomtype_from_MMKit(self):
        """
        Return the current atomtype selected in the MMKit.

        Note: This appears to be very similar (if not completely redundant) to
        pastable_atomtype() in this file.
        """
        elm = self.propMgr.elementChooser.getElement()
        atomtype = None
        if len(elm.atomtypes) > 1:
            try:
                # Obtain the hybrid index from the hybrid button group, not
                # the obsolete hybrid combobox. Fixes bug 2304. Mark 2007-06-20
                hybrid_name = self.propMgr.elementChooser.atomType
                atype = elm.find_atomtype(hybrid_name)
                if atype is not None:
                    atomtype = atype
            except:
                print_compact_traceback("exception (ignored): ")
            pass
        if atomtype is not None and atomtype.element is elm:
            return atomtype

        # For element that doesn't support hybridization
        return elm.atomtypes[0]

    def transmutePressed(self):
        """
        Slot for "Transmute" button.
        """
        forceToKeepBonds = env.prefs[keepBondsDuringTransmute_prefs_key]
        atomType = self.get_atomtype_from_MMKit()
        self.w.assy.modifyTransmute(
            self.propMgr.elementChooser.getElementNumber(),
            force = forceToKeepBonds,
            atomType=atomType)

    def isAutoBondingEnabled(self):
        if self.propMgr and hasattr(self.propMgr, 'autoBondCheckBox'):
            autoBondingEnabled = self.propMgr.autoBondCheckBox.isChecked()
        else:
            autoBondingEnabled = True

        return autoBondingEnabled

    def pastable_element(self):
        if self.propMgr and hasattr(self.propMgr, 'elementChooser'):
            return self.propMgr.elementChooser.getElement()
        else:
            # we're presumably a subclass with no propMgr or a different one
            from model.elements import Carbon
            return Carbon

    def pastable_atomtype(self):
        """
        Return the current pastable atomtype.

        [REVIEW: This appears to be very similar (if not completely redundant) to
        get_atomtype_from_MMKit() in this file. This is still used as of 071025;
        that one is called only by the slot transmutePressed -- can that still
        be called?]
        """
        #e we might extend this to remember a current atomtype per element
        #... not sure if useful
        current_element = self.pastable_element()
        if len(current_element.atomtypes) > 1:
            if self.propMgr and hasattr(self.propMgr, 'elementChooser'):
                try:
                    hybrid_name = self.propMgr.elementChooser.atomType
                    atype = current_element.find_atomtype(hybrid_name)
                    if atype is not None:
                        self._pastable_atomtype = atype
                except:
                    print_compact_traceback("exception (ignored): ")
                pass
            else:
                # we're presumably a subclass with no propMgr or a different one
                pass
        if self._pastable_atomtype is not None and self._pastable_atomtype.element is current_element:
            return self._pastable_atomtype
        self._pastable_atomtype = current_element.atomtypes[0]
        return self._pastable_atomtype

    def disable_selection_filter(self):
        """
        Disables the selection filter (if it is active)
        @see: The comment in BuildAtomsPropertyManager.set_selection_filter
              for things to be done when connectWithState API is functional
              This method is a temporary implementation
        @see: BuildAtoms_GraphicsMode.keyPress() which calls this method when
              Escape key is pressed.
        """
        if self.w.selection_filter_enabled:
            # Uncheck (disable) the Atom Selection Filter and activate the
            # Atom Tool.
            if self.propMgr.selectionFilterCheckBox:
                self.propMgr.selectionFilterCheckBox.setChecked(False)
            return

        return

    def setElement(self, elementNumber):
        """
        Set the current (active) element to I{elementNumber}.

        @param elementNumber: Element number. (i.e. 6 = Carbon)
        @type  elementNumber: int
        """
        self.propMgr.setElement(elementNumber)
        return