Example #1
0
class MMKit(QDialog,
            Ui_MMKitDialog,
            PropertyManagerMixin,
            SponsorableMixin):
    """
    THIS CLASS HAS BEEN DEPRECATED.
    SEE NEW IMPLEMETATION IN--
    Ui_BuildAtomsPropertyManager.py,
    BuildAtomsPropertyManager.py,
    Ui_PartLibPropertyManager.py,
    PartLibPropertyManager.py,
    Ui_PastePropertyManager.py
    PastePropertyManager.py

    Provide the MMKit PM for Build Atoms mode.
    """
    # <title> - the title that appears in the property manager header.
    title = "Build Atoms"
    # <iconPath> - full path to PNG file that appears in the header.
    iconPath = "ui/actions/Tools/Build Structures/Build Atoms.png"

    bond_id2name =['sp3', 'sp2', 'sp', 'sp2(graphitic)']
    sponsor_keyword = 'Build'

    def __init__(self, parentMode, win):
        QDialog.__init__(self, win, Qt.Dialog)# Qt.WStyle_Customize | Qt.WStyle_Tool | Qt.WStyle_Title | Qt.WStyle_NoBorder)

        self.w = win
        self.o = self.w.glpane

        #@NOTE: As of 20070717, MMKit supports only depositMode as its parent
        #(and perhaps subclasses of depositMode ..but such a class that also
        #uses MMKit is NIY so it is unconfirmed)  -- ninad
        self.parentMode = parentMode

        self.setupUi(self)

        # setupUi() did not add the icon or title. We do that here.
        pmSetPropMgrIcon( self, self.iconPath )
        pmSetPropMgrTitle( self, self.title )

        #self.connect(self.hybrid_btngrp,SIGNAL("buttonClicked(int)"),self.set_hybrid_type)

        self.pw = None # pw = partwindow

        self.connect(self.mmkit_tab,
                     SIGNAL("currentChanged(int)"),
                     self.tabCurrentChanged)

        self.connect(self.chunkListBox,
                     SIGNAL("currentItemChanged(QListWidgetItem*,QListWidgetItem*)"),
                     self.chunkChanged)
        self.connect(self.browseButton,
                     SIGNAL("clicked(bool)"),
                     self.browseDirectories)
        self.connect(self.defaultPartLibButton,
                     SIGNAL("clicked(bool)"),
                     self.useDefaultPartLibDirectory)

        #self.connect(self.elementButtonGroup,SIGNAL("buttonClicked(int)"),self.setElementInfo)

        self.connect(self.thumbView_groupBoxButton, SIGNAL("clicked()"),
                     self.toggle_thumbView_groupBox)
        self.connect(self.bondTool_groupBoxButton , SIGNAL("clicked()"),
                     self.toggle_bondTool_groupBox)
        self.connect(self.MMKitGrpBox_TitleButton, SIGNAL("clicked()"),
                     self.toggle_MMKit_groupBox)
        self.connect(self.filterCB, SIGNAL("stateChanged(int)"),
                     self.toggle_selectionFilter_groupBox)
        self.connect(self.advancedOptions_groupBoxButton, SIGNAL("clicked()"),
                     self.toggle_advancedOptions_groupBox)

        # Make the elements act like a big exclusive radio button.
        self.theElements = QtGui.QButtonGroup()
        self.theElements.setExclusive(True)
        self.theElements.addButton(self.toolButton1, 1)
        self.theElements.addButton(self.toolButton2, 2)
        self.theElements.addButton(self.toolButton6, 6)
        self.theElements.addButton(self.toolButton7, 7)
        self.theElements.addButton(self.toolButton8, 8)
        self.theElements.addButton(self.toolButton10, 10)
        self.theElements.addButton(self.toolButton9, 9)
        self.theElements.addButton(self.toolButton13,13)
        self.theElements.addButton(self.toolButton17,17)
        self.theElements.addButton(self.toolButton5, 5)
        self.theElements.addButton(self.toolButton10_2, 18)
        self.theElements.addButton(self.toolButton15, 15)
        self.theElements.addButton(self.toolButton16, 16)
        self.theElements.addButton(self.toolButton14, 14)
        self.theElements.addButton(self.toolButton33, 33)
        self.theElements.addButton(self.toolButton34, 34)
        self.theElements.addButton(self.toolButton35, 35)
        self.theElements.addButton(self.toolButton32, 32)
        self.theElements.addButton(self.toolButton36, 36)
        self.connect(self.theElements, SIGNAL("buttonPressed(int)"), self.update_dialog)

        self.theHybridizations = QtGui.QButtonGroup()
        self.theHybridizations.setExclusive(True)
        self.theHybridizations.addButton(self.sp3_btn, 0)
        self.theHybridizations.addButton(self.sp2_btn, 1)
        self.theHybridizations.addButton(self.sp_btn, 2)
        self.theHybridizations.addButton(self.graphitic_btn, 3)
        self.connect(self.theHybridizations, SIGNAL("buttonClicked(int)"), self.update_hybrid_btngrp)

        self.connect(self.filterCB,
                        SIGNAL("toggled(bool)"),self.set_selection_filter)


        self.elemTable = PeriodicTable
        self.displayMode = diTUBES
        self.elm = None

        self.newModel = None  ## used to save the selected lib part

        self.flayout = None

        # It looks like we now have correct fixes for bugs 1659 and bug 1824. If so, it would be safe to simply
        # hardware self.icon_tabs to True and simplify code accordingly. But we're not 100% certain, so by leaving
        # it as a debug pref, we can help any users who see those bugs come up again.
        # wware 060420
        #
        # Update, bruce 070328: the False value of this debug_pref is known to fail in the Qt4 version (on Mac anyway),
        # due to AttributeError exceptions for setMargin and setTabLabel,
        # so I'm changing the prefs key for it in order to let Qt3 and Qt4 have independent debug_pref settings,
        # adding a warning in the menu text, and adding a try/except to help in debugging this if anyone ever wants to.
        # (If the bugs Will mentioned go away entirely, we can abandon support for the False value instead of fixing it,
        #  as Will suggested.)
        from utilities.debug_prefs import debug_pref, Choice_boolean_True
        self.icon_tabs = debug_pref("use icons in MMKit tabs? (only True works in Qt4)", Choice_boolean_True,
                                    prefs_key = "A7/mmkit tab icons/Qt4")
            #e Changes to this only take effect in the next session.
            # Ideally we'd add a history message about that, when this is changed.
            # (It's not yet easy to do that in a supported way in debug_pref.) [bruce 060313]

        if not self.icon_tabs:
            # This code is known to fail in Qt4 Mac version, as explained above. [bruce 061222 and 070328]
            try:
                self.mmkit_tab.setMargin ( 0 )
            except:
                print_compact_traceback("ignoring this Qt4-specific exception: ") #bruce 061222
                pass
            self.mmkit_tab.setTabLabel (self.atomsPage, 'Atoms')
            self.mmkit_tab.setTabLabel (self.clipboardPage, 'Clipbd')
            self.mmkit_tab.setTabLabel (self.libraryPage, 'Lib')
        else:
            # Add icons to MMKit's tabs. mark 060223.
            atoms_ic = imagename_to_icon("actions/Properties Manager/MMKit.png")
            self.mmkit_tab.setTabIcon(self.mmkit_tab.indexOf(self.atomsPage), QIcon(atoms_ic))

            self.update_clipboard_page_icon() # Loads proper icon for clibpoard tab. Mark 2007-06-01

            library_ic = imagename_to_icon("actions/Properties Manager/library.png")
            self.mmkit_tab.setTabIcon(self.mmkit_tab.indexOf(self.libraryPage), QIcon(library_ic))

        # Tab tooltips. mark 060326
        self.mmkit_tab.setTabToolTip(self.mmkit_tab.indexOf(self.atomsPage), 'Atoms')
        self.mmkit_tab.setTabToolTip(self.mmkit_tab.indexOf(self.clipboardPage), 'Clipboard')
        self.mmkit_tab.setTabToolTip(self.mmkit_tab.indexOf(self.libraryPage), 'Part Library')

        self._setNewView('MMKitView')

        # Set current element in element button group.


        self.theElements.button(self.w.Element).setChecked(True)

        #self.connect(self., SIGNAL("), )

        self.connect(self.w.hybridComboBox, SIGNAL("activated(int)"), self.hybridChangedOutside)

        self.connect(self.w.hybridComboBox, SIGNAL("activated(const QString&)"), self.change2AtomsPage)
        self.connect(self.w.elemChangeComboBox, SIGNAL("activated(const QString&)"), self.change2AtomsPage)
        self.connect(self.w.pasteComboBox, SIGNAL("activated(const QString&)"), self.change2ClipboardPage)

        #self.connect(self.w.depositAtomDashboard.pasteBtn, SIGNAL("pressed()"), self.change2ClipboardPage)
        self.connect(self.w.depositAtomDashboard.pasteBtn, SIGNAL("stateChanged(int)"), self.pasteBtnStateChanged)
        self.connect(self.w.depositAtomDashboard.depositBtn, SIGNAL("stateChanged(int)"), self.depositBtnStateChanged)

        self.connect(self.dirView, SIGNAL("selectionChanged(QItemSelection *, QItemSelection *)"), self.partChanged)

        self.add_whats_this_text()

        return # from __init__

    # ==

    #bruce 060412 added everything related to __needs_update_xxx, to fix bugs 1726, 1629 and mitigate bug 1677;
    # for more info see the comments where update_clipboard_items is called (in depositMode.py).

    __needs_update_clipboard_items = False
        # (there could be other flags like this for other kinds of updates we might need)


    def show_propMgr(self):
        """
        Show the Build Property Manager.
        """
        #@NOTE: The build property manager files are still refered as MMKit and
        #MMKitDialog. This will change in the near future. -- ninad 20070717

        self.update_dialog(self.parentMode.w.Element)
        self.parentMode.set_selection_filter(False) # disable selection filter
        self.openPropertyManager(self)

        #Following is an old comment, was originally in depositMode.init_gui:
        #Do these before connecting signals or we'll get history msgs.
        #Part of fix for bug 1620. mark 060322
        self.highlightingCB.setChecked(self.parentMode.hover_highlighting_enabled)
        self.waterCB.setChecked(self.parentMode.water_enabled)


    def update_dialog(self, elemNum):
        """Called when the current element has been changed.
           Update non user interactive controls display for current selected
           element: element label info and element graphics info """

        elm = self.elemTable.getElement(elemNum)

        currentIndex = self.mmkit_tab.currentIndex()
        atomPageIndex = self.mmkit_tab.indexOf(self.atomsPage)

        ##if elm == self.elm and self.currentPageOpen(AtomsPage): return
        if elm == self.elm and (currentIndex == atomPageIndex) : return

        ## The following statements are redundant in some situations.
        self.theElements.button(elemNum).setChecked(True)
        self.w.Element = elemNum

        self.color = self.elemTable.getElemColor(elemNum)
        self.elm = self.elemTable.getElement(elemNum)

        self.update_hybrid_btngrp()

        self.elemGLPane.resetView()
        self.elemGLPane.refreshDisplay(self.elm, self.displayMode)

        #update the selection filter
        self.update_selection_filter_list()

        # Fix for bug 353, to allow the dialog to be updated with the correct page.  For example,
        # when the user selects Paste from the Edit toolbar/menu, the MMKit should show
        # the Clipboard page and not the Atoms page. Mark 050808
        if self.w.depositState == 'Clipboard':
            self.change2ClipboardPage()
        else:
            self.change2AtomsPage()
        self.tabCurrentChanged()

        self.updateBuildAtomsMessage() # Mark 2007-06-01

    def updateBuildAtomsMessage(self):
        """Updates the message box with an informative message based on the current page
        and current selected atom type.
        """
        msg = ""

        if self.MMKit_groupBox.isVisible():
            pageIndex = self.mmkit_tab.currentIndex()
            page = None

            if pageIndex is 0: # atomsPage
                msg = "Double click in empty space to insert a single " + self.elm.name + " atom. "
                if not self.elm.symbol in noblegases:
                    msg += "Click on an atom's <i>red bondpoint</i> to attach a " + self.elm.name + " atom to it."

            elif pageIndex is 1: # clipboardPage
                pastableItems = self.w.assy.shelf.get_pastable_chunks()
                if pastableItems:
                    msg = "Double click in empty space to insert a copy of the selected clipboard item. \
                    Click on a <i>red bondpoint</i> to attach a copy of the selected clipboard item."
                else:
                    msg = "There are no items on the clipboard."

            elif pageIndex is 2: # libraryPage
                msg = "Double click in empty space to insert a copy of the selected part in the library."

        else: # Bonds Tool is selected (MMKit groupbox is hidden).
            if self.parentMode.cutBondsAction.isChecked():
                msg = "<b> Cut Bonds </b> tool is active. \
                Click on bonds in order to delete them."
                self.MessageGroupBox.insertHtmlMessage(msg)
                return

            if not hasattr(self, 'bondclick_v6'): # Mark 2007-06-01
                return
            if self.bondclick_v6:
                name = btype_from_v6(self.bondclick_v6)
                msg = "Click bonds or bondpoints to make them %s bonds." % name # name is 'single' etc

        # Post message.
        self.MessageGroupBox.insertHtmlMessage(msg)

    #== Atom Selection Filter helper methods

    def set_selection_filter(self, enabled):
        """Slot for Atom Selection Filter checkbox. Prints history message when selection filter is
        enabled/disabled and updates the cursor.
        """

        if enabled != self.w.selection_filter_enabled:
            if enabled:
                env.history.message("Atom Selection Filter enabled.")
            else:
                env.history.message("Atom Selection Filter disabled.")

        self.w.selection_filter_enabled = enabled

        #print "update_selection_filter_list(): self.w.filtered_elements=", self.w.filtered_elements

        ##self.update_selection_filter_list_widget()
        self.update_selection_filter_list()
        self.filterlistLE.setEnabled(enabled)
        self.filterCB.setChecked(enabled)
        self.o.graphicsMode.update_cursor()

    def update_selection_filter_list(self):
        """Adds/removes the element selected in the MMKit to/from Atom Selection Filter
        based on what modifier key is pressed (if any).
        """
        eltnum = self.w.Element

        if self.o.modkeys is None:
            self.w.filtered_elements = []
            self.w.filtered_elements.append(PeriodicTable.getElement(eltnum))
        if self.o.modkeys == 'Shift':
            if not PeriodicTable.getElement(eltnum) in self.w.filtered_elements[:]:
                self.w.filtered_elements.append(PeriodicTable.getElement(eltnum))
        elif self.o.modkeys == 'Control':
            if PeriodicTable.getElement(eltnum) in self.w.filtered_elements[:]:
                self.w.filtered_elements.remove(PeriodicTable.getElement(eltnum))

        self.update_selection_filter_list_widget()

    def update_selection_filter_list_widget(self):
        """Updates the list of elements displayed in the Atom Selection Filter List.
        """
        filtered_syms=''
        for e in self.w.filtered_elements[:]:
            if filtered_syms: filtered_syms += ", "
            filtered_syms += e.symbol
        #self.w.depositAtomDashboard.filterlistLE.setText(filtered_syms)
        self.filterlistLE.setText(filtered_syms)

    def toggle_bondTool_groupBox(self):
        self.toggle_groupbox(self.bondTool_groupBoxButton, self.bondToolWidget)

    def toggle_thumbView_groupBox(self):
        self.toggle_groupbox(self.thumbView_groupBoxButton, self.elementFrame)

    def toggle_MMKit_groupBox(self):
        self.toggle_groupbox(self.MMKitGrpBox_TitleButton, self.mmkit_tab, self.transmuteBtn, self.transmuteCB)

    def toggle_selectionFilter_groupBox(self, state):
        """ Toggles the groupbox item display depending on checked state of the selection filter checkbox """

        #Current state is 'off' or(Qt.Unchecked)

        if state is 0:
            styleSheet = self.getGroupBoxCheckBoxStyleSheet(bool_expand = False)
            self.filterCB.setStyleSheet(styleSheet)

            palette = self.getGroupBoxCheckBoxPalette()
            self.filterCB.setPalette(palette)

            #hide the following widgets when checkbox is unchecked --
            self.selectionFilter_label.hide()
            self.filterlistLE.hide()
        else:
            styleSheet = self.getGroupBoxCheckBoxStyleSheet(bool_expand = True)
            self.filterCB.setStyleSheet(styleSheet)

            palette = self.getGroupBoxCheckBoxPalette()
            self.filterCB.setPalette(palette)

            #hide the following widgets when checkbox is unchecked --
            self.selectionFilter_label.show()
            self.filterlistLE.show()


    def toggle_advancedOptions_groupBox(self):
        self.toggle_groupbox(self.advancedOptions_groupBoxButton, self.autobondCB, self.waterCB, self.highlightingCB)

    #bruce 070615 removed 'def toggle_groupbox', since our mixin superclass PropertyManagerMixin
    # provides an identical-enough version.

    def tabCurrentChanged(self):
        pageIndex = self.mmkit_tab.currentIndex()
        page = None
        if pageIndex is 0: page = self.atomsPage
        elif pageIndex is 1: page = self.clipboardPage
        elif pageIndex is 2: page = self.libraryPage
        self.setup_current_page(page)

    def update_clipboard_items(self):
        self.__needs_update_clipboard_items = True
        self.update() # this makes sure self.event will get called; it might be better to test the flag only in self.repaint, not sure
        return

    def event(self, event): #bruce 060412 debug code, but also checks all self.__needs_update_xxx flags (an essential bugfix)
        if debug_mmkit_events:
            print "debug: MMKit.event got %r, type %r" % (event, event.type())
                # Qt doc for QEvent lists 'enum type' codes; the subclass is also printed by %r

        if self.__needs_update_clipboard_items:
            self.__really_update_clipboard_items()
            self.__needs_update_clipboard_items = False

        res = QDialog.event(self, event)
        if debug_mmkit_events:
            if res is not None:
                print "debug: MMKit.event returns %r" % (res,) # usually True, sometimes False
        # if we return None we get TypeError: invalid result type from MMKit.event()
        return res

    # ==

    def pasteBtnStateChanged(self, state):
        """Slot method. Called when the state of the Paste button of deposit dashboard has been changed. """
        if state == QButton.On:
            self.change2ClipboardPage()


    def depositBtnStateChanged(self, state):
        """Slot method. Called when the state of the Deposit button of deposit dashboard has been changed. """
        if state == QButton.On:
           self.change2AtomsPage()


    def hybridChangedOutside(self, newId):
        """Slot method. Called when user changes element hybridization from the dashboard.
         This method achieves the same effect as user clicked one of the hybridization buttons."""
        self.theElements.button(newId).setChecked(True)
        self.set_hybrid_type(newId)
        self.w.Element = newId

        ## fix bug 868
        self.w.depositAtomDashboard.depositBtn.setChecked(True)


    def change2AtomsPage(self):
        """Slot method called when user changes element/hybrid combobox or
        presses Deposit button from Build mode dashboard.
        """
        if self.mmkit_tab.currentIndex() != AtomsPage:
            self.mmkit_tab.setCurrentIndex(AtomsPage) # Generates signal

    def change2ClipboardPage(self):
        """Slot method called when user changes pastable item combobox or
        presses the Paste button from the Build mode dashboard. """
        if self.mmkit_tab.currentIndex() != ClipboardPage:
            self.mmkit_tab.setCurrentIndex(ClipboardPage) # Generates signal??


    def setElementInfo(self,value):
        """Slot method called when an element button is pressed in the element ButtonGroup.
        """
        self.w.setElement(value)


    def update_hybrid_btngrp(self, buttonIndex = 0):
        """Update the buttons of the current element\'s hybridization types into hybrid_btngrp;
        select the specified one if provided"""
        elem = PeriodicTable.getElement(self.w.Element) # self.w.Element is atomic number

        atypes = elem.atomtypes

        if elem.name == 'Carbon':
            self.setup_C_hybrid_buttons()
        elif elem.name == 'Nitrogen':
            self.setup_N_hybrid_buttons()
        elif elem.name == 'Oxygen':
            self.setup_O_hybrid_buttons()
        elif elem.name == 'Sulfur':
            self.setup_S_hybrid_buttons()
        else:
            self.hide_hybrid_btngrp()
            self.elemGLPane.changeHybridType(None)
            return

        #if len(atypes) > 1:
        # Prequisite: w.hybridComboBox has been updated at this moment.
        b_name = self.bond_id2name[buttonIndex]
        self.elemGLPane.changeHybridType(b_name)
        self.elemGLPane.refreshDisplay(self.elm, self.displayMode)
        self.theHybridizations.button(buttonIndex).setChecked(True)
        self.set_hybrid_type(buttonIndex)
        # Added Atomic Hybrids label. Mark 2007-05-30
        self.atomic_hybrids_label.setText("Atomic Hybrids for " + elem.name + " :")
        self.show_hybrid_btngrp()

    def show_hybrid_btngrp(self): # Mark 2007-06-20
        """Show the hybrid button group and label above it.
        This is a companion method to hide_hybrid_btngrp().
        It includes workarounds for Qt layout issues that crop up
        when hiding/showing the hybrid button groupbox using Qt's
        hide() and show() methods. See bug 2407 for more information.
        """
        if 1:
            self.hybrid_btngrp.show()
        else:
            self.hybrid_btngrp.show()
            self.atomic_hybrids_label.show()

    def hide_hybrid_btngrp(self): # Mark 2007-06-20
        """Hide the hybrid button group and label above it.
        This is a companion method to show_hybrid_btngrp().
        It includes workarounds for Qt layout issues that crop up
        when hiding/showing the hybrid button groupbox using Qt's
        hide() and show() methods. See bug 2407 for more information.
        """
        if 1:
            # This way of hiding confuses the layout manager, so I had
            # do create special spacers and set sizepolicies just to make
            # this work.
            self.hybrid_btngrp.hide()
            # I had to do this instead of use hide() since hide()
            # confuses the layout manager in special situations, like
            # that described in bug 2407. Mark 2007-06-20
            self.atomic_hybrids_label.setText(" ")
        else:
            # Alternate way of hiding. Hide all contents and the QButtonGroupbox
            # border, but there is no way to hide the border.
            self.sp3_btn.hide()
            self.sp2_btn.hide()
            self.sp_btn.hide()
            self.graphitic_btn.hide()
            self.atomic_hybrids_label.hide()

    def setup_C_hybrid_buttons(self):
        """Displays the Carbon hybrid buttons.
        """
        self.theElements.button(self.w.Element).setChecked(True)
        self.sp3_btn.setIcon(imagename_to_icon('modeltree/C_sp3.png'))
        self.sp3_btn.show()
        self.sp2_btn.setIcon(imagename_to_icon('modeltree/C_sp2.png'))
        self.sp2_btn.show()
        self.sp_btn.setIcon(imagename_to_icon('modeltree/C_sp.png'))
        self.sp_btn.show()
        self.graphitic_btn.hide()

    def setup_N_hybrid_buttons(self):
        """Displays the Nitrogen hybrid buttons.
        """
        self.sp3_btn.setIcon(imagename_to_icon('modeltree/N_sp3.png'))
        self.sp3_btn.show()
        self.sp2_btn.setIcon(imagename_to_icon('modeltree/N_sp2.png'))
        self.sp2_btn.show()
        self.sp_btn.setIcon(imagename_to_icon('modeltree/N_sp.png'))
        self.sp_btn.show()
        self.graphitic_btn.setIcon(imagename_to_icon('modeltree/N_graphitic.png'))
        self.graphitic_btn.show()


    def setup_O_hybrid_buttons(self):
        """Displays the Oxygen hybrid buttons.
        """
        self.sp3_btn.setIcon(imagename_to_icon('modeltree/O_sp3.png'))
        self.sp3_btn.show()
        self.sp2_btn.setIcon(imagename_to_icon('modeltree/O_sp2.png'))
        self.sp2_btn.show()
        self.sp_btn.hide()
        self.graphitic_btn.hide()


    def setup_S_hybrid_buttons(self):
        """Displays the Sulfur hybrid buttons.
        """
        self.sp3_btn.setIcon(imagename_to_icon('modeltree/O_sp3.png')) # S and O are the same.
        self.sp3_btn.show()
        self.sp2_btn.setIcon(imagename_to_icon('modeltree/O_sp2.png'))
        self.sp2_btn.show()
        self.sp_btn.hide()
        self.graphitic_btn.hide()


    def set_hybrid_type(self, type_id):
        """Slot method. Called when any of the hybrid type buttons was clicked. """
        self.w.hybridComboBox.setCurrentIndex( type_id )

        b_name = self.bond_id2name[type_id]

        #This condition fixs bug 866, also saves time since no need to draw without MMKIt shown
        if self.isVisible():
            self.elemGLPane.changeHybridType(b_name)
            self.elemGLPane.refreshDisplay(self.elm, self.displayMode)


    def setup_current_page(self, page):
        """Slot method that is called whenever a user clicks on the
        'Atoms', 'Clipboard' or 'Library' tab to change to that page.
        """

        #print "setup_current_page: page=", page

        if page == self.atomsPage:  # Atoms page
            self.w.depositState = 'Atoms'
            self.w.update_depositState_buttons()
            self.elemGLPane.resetView()
            self.elemGLPane.refreshDisplay(self.elm, self.displayMode)
            self.browseButton.hide()
            self.defaultPartLibButton.hide()
            self.atomsPageSpacer.changeSize(0,5,
                QtGui.QSizePolicy.Expanding,
                QtGui.QSizePolicy.Minimum)

        elif page == self.clipboardPage: # Clipboard page
            self.w.depositState = 'Clipboard'
            self.w.update_depositState_buttons()
            self.elemGLPane.setDisplay(self.displayMode)
            self._clipboardPageView()
            self.browseButton.hide()
            self.defaultPartLibButton.hide()
            self.atomsPageSpacer.changeSize(0,5,
                QtGui.QSizePolicy.Expanding,
                QtGui.QSizePolicy.Minimum)

        elif page == self.libraryPage: # Library page
            if self.rootDir:
                self.elemGLPane.setDisplay(self.displayMode)
                self._libPageView()
            self.browseButton.show()
            self.defaultPartLibButton.show()
            self.atomsPageSpacer.changeSize(0,5,
                QtGui.QSizePolicy.Minimum,
                QtGui.QSizePolicy.Minimum)

            #Turn off both paste and deposit buttons, so when in library page and user choose 'set hotspot and copy'
            #it will change to paste page, also, when no chunk selected, a history message shows instead of depositing an atom.
            self.w.depositState = 'Library'
            self.w.update_depositState_buttons()
        else:
            print 'Error: MMKit page unknown: ', page

        self.elemGLPane.setFocus()
        self.updateBuildAtomsMessage()

    def chunkChanged(self, item, previous):
        """Slot method. Called when user changed the selected chunk. """

        itemId = self.chunkListBox.row(item)

        if itemId != -1:

            newChunk = self.pastableItems[itemId]

            #self.w.pasteComboBox.setCurrentIndex(itemId)
            #buildModeObj = self.w.commandSequencer._commandTable['DEPOSIT']
            #assert buildModeObj
            #buildModeObj.setPaste()

            ##Compared to the above way, I think this way is better. Modules are more uncoupled.
            self.w.pasteComboBox.setCurrentIndex(itemId) # Fixes bug 1754. mark 060325

            self.elemGLPane.updateModel(newChunk)


    def __really_update_clipboard_items(self): #bruce 060412 renamed this from update_clipboard_items to __really_update_clipboard_items
        """Updates the items in the clipboard\'s listview, if the clipboard is currently shown. """
        if self.currentPageOpen(ClipboardPage): #bruce 060313 added this condition to fix bugs 1631, 1669, and MMKit part of 1627
            self._clipboardPageView() # includes self.update_clipboard_page_icon()
        else:
            self.update_clipboard_page_icon() # do this part of it, even if page not shown
        return

    def partChanged(self, item):
        """Slot method, called when user changed the partlib brower tree"""
        if isinstance(item, self.FileItem):
           self._libPageView(True)
        else:
           self.newModel = None
           self.elemGLPane.updateModel(self.newModel)


    def getPastablePart(self):
        """Public method. Retrieve pastable part and hotspot if current tab page is in libary, otherwise, return None. """
        if self.currentPageOpen(LibraryPage):
            return self.newModel, self.elemGLPane.hotspotAtom
        return None, None

    def currentPageOpen(self, page_id):
        """Returns True if <page_id> is the current page open in the tab widget, where:
            0 = Atoms page
            1 = Clipboard page
            2 = Library page
        """
        pageIndex = self.mmkit_tab.currentIndex()

        if page_id == pageIndex:
            return True
        else:
            return False

    def _libPageView(self, isFile = False):
        item = self.dirView.selectedItem()
        if not isFile and not isinstance(item, self.FileItem):
            self.newModel = None
            self.elemGLPane.updateModel(self.newModel)
            return

        mmpfile = str(item.getFileObj())
        if os.path.isfile(mmpfile):
            #self.newModel = Assembly(self.w, "Assembly 1")
            self.newModel = Assembly(self.w, os.path.normpath(mmpfile)) #ninad060924 to fix bug 1164
            self.newModel.o = self.elemGLPane ## Make it looks "Assembly" used by glpane.
            readmmp(self.newModel, mmpfile)

# What we did in Qt 3:
##         #self.newModel = Assembly(self.w, "Assembly 1")
##         self.newModel = Assembly(self.w, os.path.normpath(mmpfile)) #ninad060924 to fix bug 1164

##         self.newModel.o = self.elemGLPane ## Make it looks "Assembly" used by glpane.
##         readmmp(self.newModel, mmpfile)

##         # The following is absolute nonsense, and is part of what's breaking the fix of bug 2028,
##         # so it needs to be revised, to give this assy a standard structure.
##         # We'll have to find some other way to draw the hotspot singlet
##         # (e.g. a reasonable, straightforward way). So we did -- MMKitView.always_draw_hotspot is True.
##         # [bruce 060627]

## ##        # Move all stuff under assembly.tree into assy.shelf. This is needed to draw hotspot singlet
## ##        def addChild(child):
## ##            self.newModel.shelf.addchild(child)
## ##
## ##        # Remove existing clipboard items from the libary part before adopting childern from 'tree'.
## ##        self.newModel.shelf.members = []
## ##        self.newModel.tree.apply2all(addChild)
## ##
## ##        self.newModel.shelf.prior_part = None
## ##        self.newModel.part = Part(self.newModel, self.newModel.shelf)

##         if 1: #bruce 060627

            self.newModel.update_parts() #k not sure if needed after readmmp)
            self.newModel.checkparts()
            if self.newModel.shelf.members:
                if debug_flags.atom_debug:
                    print "debug warning: library part %r contains clipboard items" % mmpfile
                    # we'll see if this is common
                    # happens for e.g. nanokids/nanoKid-C39H42O2.mmp
                for m in self.newModel.shelf.members[:]:
                    m.kill() #k guess about a correct way to handle them
                self.newModel.update_parts() #k probably not needed
                self.newModel.checkparts() #k probably not needed
        else:
            self.newModel = None

        self.elemGLPane.updateModel(self.newModel)


    def _clipboardPageView(self):
        """Updates the clipboard page. """
        if not self.currentPageOpen(ClipboardPage):
            # (old code permitted this to be false below in 'if len(list):',
            #  but failed to check for it in the 'else' clause,
            #  thus causing bug 1627 [I think], now fixed in our caller)
            print "bug: _clipboardPageView called when not self.currentPageOpen(ClipboardPage)" #bruce 060313
            return

        self.pastableItems = self.w.assy.shelf.get_pastable_chunks()
        self.chunkListBox.clear()
        for item in self.pastableItems:
            self.chunkListBox.addItem(item.name)

        newModel = None
        if len(self.pastableItems):
            i = self.w.pasteComboBox.currentIndex()
            if i < 0:
                i = self.w.pasteComboBox.count() - 1
            # Make sure the clipboard page is open before calling selSelected(), because
            # setSelected() causes the clipboard page to be displayed when we don't want it to
            # be displayed (i.e. pressing Control+C to copy something to the clipboard).
            self.chunkListBox.setCurrentItem(self.chunkListBox.item(i))
            # bruce 060313 question: why don't we now have to pass the selected chunk to
            # self.elemGLPane.updateModel? ###@@@
            newModel = self.pastableItems[i]
        self.elemGLPane.updateModel(newModel)
        self.update_clipboard_page_icon()


    def update_clipboard_page_icon(self):
        """Updates the Clipboard page (tab) icon with a full or empty clipboard icon
        based on whether there is anything on the clipboard (pasteables).
        """
        if not self.icon_tabs:
            # Work around for bug 1659. mark 060310 [revised by bruce 060313]
            return

        if self.w.assy.shelf.get_pastable_chunks():
            clipboard_ic = imagename_to_icon("actions/Properties Manager/clipboard-full.png")
        else:
            clipboard_ic = imagename_to_icon("actions/Properties Manager/clipboard-empty.png")

        self.mmkit_tab.setTabIcon(self.mmkit_tab.indexOf(self.clipboardPage), QIcon(clipboard_ic))

    class DirView(QTreeView):
        def __init__(self, mmkit, parent):
            QTreeView.__init__(self, parent)

            self.setEnabled(True)
            self.model = QtGui.QDirModel(['*.mmp', '*.MMP'], # name filters
                                         QDir.AllEntries|QDir.AllDirs|QDir.NoDotAndDotDot, # filters
                                         QDir.Name # sort order
                                         )
                # explanation of filters (from Qt 4.2 doc for QDirModel):
                # - QDir.AllEntries = list files, dirs, drives, symlinks.
                # - QDir.AllDirs = include dirs regardless of other filters [guess: needed to ignore the name filters for dirs]
                # - QDir.NoDotAndDotDot = don't include '.' and '..' dirs
                #
                # about dirnames of "CVS":
                # The docs don't mention any way to filter the dirnames using a callback,
                # or any choices besides "filter them same as filenames" or "don't filter them".
                # So there is no documented way to filter out the "CVS" subdirectories like we did in Qt3
                # (short of subclassing this and overriding some methods,
                #  but the docs don't make it obvious how to do that correctly).
                # Fortunately, autoBuild.py removes them from the partlib copy in built releases.
                #
                # Other QDirModel methods we might want to use:
                # QDirModel.refresh, to update its state from the filesystem (but see the docs --
                #  they imply we might have to pass the model's root pathname to that method,
                #  even if it hasn't changed, but they're not entirely clear on that).
                #
                # [bruce 070502 comments]

            self.path = None
            self.mmkit = mmkit
            self.setModel(self.model)
            self.setWindowTitle(self.tr("Dir View"))

            self.setItemsExpandable(True)
            self.setAlternatingRowColors(True)
            self.setColumnWidth(0, 200)
            for i in range(2,4):
                self.setColumnWidth(i, 4)

            self.show()

        #Ninad 070326 reimplementing mouseReleaseEvent and resizeEvent
        #for DirView Class (which is a subclass of QTreeView)
        #The old code reimplemented 'event' class which handles *all* events.
        #There was a bad bug which didn't send an event when the widget is resized
        # and then the seletcion is changed. In NE1Qt3 this wasn't a problem because
        #it only had one column. Now that we have multiple columns
        #(which are needed to show the horizontal scrollbar.
        # While using Library page only resize event or mouse release events
        #by the user should update the thumbview.
        #The Qt documentation also suggests reimplementing subevents instead of the main
        #event handler method (event())
        def mouseReleaseEvent(self, evt):
            """ Reimplementation of mouseReleaseEvent method of QTreeView"""
            if self.selectedItem() is not None:
                    self.mmkit._libPageView()
            return QTreeView.mouseReleaseEvent(self, evt)

        def resizeEvent(self, evt):
            """ Reimplementation of resizeEvent method of QTreeView"""
            if self.selectedItem() is not None:
                    self.mmkit._libPageView()
            return QTreeView.resizeEvent(self, evt)


        #Following method (event() ) is not reimplemented anymore. Instead, the subevent handlers are
        #reimplemented (see above)  -- ninad 070326
        """
        def event(self, evt):
            if evt.type() == evt.Timer:
                # This is the event we get when the library selection changes, so if there has
                # been a library selection, update the library page's GLPane. But this can also
                # happen without a selection; in that case don't erase the atom page's display.
                if self.selectedItem() is not None:
                    self.mmkit._libPageView()
            return QTreeView.event(self, evt)"""

        def setRootPath(self, path):
            self.path = path
            self.setRootIndex(self.model.index(path))

        def selectedItem(self):
            indexes = self.selectedIndexes()
            if not indexes:
                return None
            index = indexes[0]
            if not index.isValid():
                return None

            return self.FileItem(str(self.model.filePath(index)))

    class FileItem:
        def __init__(self, path):
            self.path = path
            dummy, self.filename = os.path.split(path)
        def name(self):
            return self.filename
        def getFileObj(self):
            return self.path
    DirView.FileItem = FileItem

    def _setNewView(self, viewClassName):
        # Put the GL widget inside the frame
        if not self.flayout:
            self.flayout = QVBoxLayout(self.elementFrame)
            self.flayout.setMargin(1)
            self.flayout.setSpacing(1)
        else:
            if self.elemGLPane:
                self.flayout.removeChild(self.elemGLPane)
                self.elemGLPane = None

        if viewClassName == 'ChunkView':
            # We never come here! How odd.
            self.elemGLPane = ChunkView(self.elementFrame, "chunk glPane", self.w.glpane)
        elif viewClassName == 'MMKitView':
            self.elemGLPane = MMKitView(self.elementFrame, "MMKitView glPane", self.w.glpane)

        self.flayout.addWidget(self.elemGLPane,1)


        #ninad 070326. Note that self.DirView inherits QTreeView.
        #It has got nothing to do with the experimental class DirView in file Dirview.py
        self.dirView = self.DirView(self, self.libraryPage)
        self.dirView.setSortingEnabled(False) #bruce 070521 changed True to False -- fixes "reverse order" bug on my Mac
        ##self.dirView.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn)

        libraryPageLayout = QVBoxLayout(self.libraryPage)
        libraryPageLayout.setMargin(pmMMKitPageMargin) # Was 4. Mark 2007-05-30
        libraryPageLayout.setSpacing(2)
        libraryPageLayout.addWidget(self.dirView)


        filePath = os.path.dirname(os.path.abspath(sys.argv[0]))
        libDir = os.path.normpath(filePath + '/../partlib')

        self.libPathKey = '/nanorex/nE-1/libraryPath'
        libDir = env.prefs.get(self.libPathKey, libDir)

        if os.path.isdir(libDir):
            self.rootDir = libDir
            self.dirView.setRootPath(libDir)
        else:
            self.rootDir = None
            from history.HistoryWidget import redmsg
            env.history.message(redmsg("The part library directory: %s doesn't exist." %libDir))

    def browseDirectories(self):
       """Slot method for the browse button of library page. """
       # Determine what directory to open.
       if self.w.assy.filename:
           odir = os.path.dirname(self.w.assy.filename)
       else:
           from utilities.prefs_constants import workingDirectory_prefs_key
           odir = env.prefs[workingDirectory_prefs_key]

       fdir = QFileDialog.getExistingDirectory(self, "Choose library directory", odir)
       libDir = str(fdir)
       if libDir and os.path.isdir(libDir):
           env.prefs[self.libPathKey] = libDir
           self.dirView.setRootPath(libDir)
           #Refresh GL-thumbView display
           self.newModel = None
           self.elemGLPane.updateModel(self.newModel)

    def useDefaultPartLibDirectory(self):
        """ Slot method that to reset the part lib directory path to default"""
        from history.HistoryWidget import redmsg

        #ninad070503 : A future enhancement would be a preference to have
        # a user defined 'default dir path' for the
        #part lib location.

        # set libDir to the standard partlib path
        filePath = os.path.dirname(os.path.abspath(sys.argv[0]))
        libDir = os.path.normpath(filePath + '/../partlib')

        if libDir and os.path.isdir(libDir):
            if self.dirView.path == libDir:
                msg1 = "Current directory path for the partlib is the default path."
                msg2 = " Current path not changed"
                env.history.message(redmsg(msg1 + msg2))
                return
            env.prefs[self.libPathKey] = libDir
            self.dirView.setRootPath(libDir)
            #Refresh GL-thumbView display
            self.newModel = None
            self.elemGLPane.updateModel(self.newModel)
        else:
            msg1 = "The default patlib directory %s doesn't exist."%libDir
            msg2 = "Current path not changed"
            env.history.message(redmsg(msg1 + msg2))


    def closeEvent(self, e):
        """This event handler receives all MMKit close events.  In other words,
        when this dialog's close() slot gets called, this gets called with event 'e'.
        """
        self.hide()

        # If the 'Library' page is open, change it to 'Atoms'.  Mark 051212.
        if self.currentPageOpen(LibraryPage):
            self.setup_current_page(self.atomsPage)


#bruce 070615 commented out the following since I think it's obsolete:
# (evidence: no other mentions of 'polish' or 'move_to_best_location' in our code,
#  and MMKit is no longer a movable dialog)
##    num_polish = 0
##
##    def polish(self):
##        """This slot is called after a widget has been fully created and before it is shown the very first time.
##        Polishing is useful for final initialization which depends on having an instantiated widget.
##        This is something a constructor cannot guarantee since the initialization of the subclasses might not be finished.
##        After this function, the widget has a proper font and palette and QApplication.polish() has been called.
##        Remember to call QDialog's implementation when reimplementing this function.
##        """
##        QDialog.polish(self) # call QDialog's polish() implementation
##        self.num_polish += 1
##        #&print "num_polish =", self.num_polish
##        if self.num_polish < 2:
##            # polish() is called twice; not sure why.
##            # Call move_to_best_location() only after the second polish signal since
##            # get_location() can only get self.frameGeometry() after that.
##            return
##        self.move_to_best_location(True)


    def show(self):
        """MMKit\'s show slot.
        """
        #QDialog.show(self)
        #&print "MMKit.move: setting mainwindow to active window"
        #Show it in Property Manager Tab ninad061207
        if not self.pw or self:
            self.pw = self.w.activePartWindow()       #@@@ ninad061206
            self.pw.updatePropertyManagerTab(self)
            self.setSponsor()

        else:
            if not self.pw:
                self.pw = self.w.activePartWindow()
            self.pw.updatePropertyManagerTab(self)


        self.w.activateWindow() # Fixes bug 1503.  mark 060216.
            # Required to give the keyboard input focus back to self (MainWindow).

    def add_whats_this_text(self):
        """What's This text for some of the widgets in the Build > Atoms Property Manager.
        Many are still missing.
        """

        self.elementFrame.setWhatsThis("""<b>Preview Window</b>
        <p>This displays the active object. It can be inserted by double
        clicking in the 3D graphics area.</p>
        <p>Left click on a red bondpoint to make it a green <i>hotspot</i>
        (clipboard and library parts only). The hotspot indicates which
        bondpoint should be used to bond the current object when selecting
        a bondpoint in the model.</p>
        <p><u>Mouse Commands Supported</u><br>
        Left - Select hotspot (clipboard and library parts only)<br>
        Middle - Rotate view<br> \
        Wheel - Zoom in/out \
        </p>""")

        self.MMKit_groupBox.setWhatsThis("""<b>Molecular Modeling Kit</b>
        <p>A tabular widget for selecting atom types or other structures to insert
        into the model.</p>
        <p><b>Atoms Tab</b> - A select group of atom types options from the periodic
        table of elements.</p>
        <p><b>Clipboard Tab</b> - the list of objects copied on the clipboard (using
        the Copy command).</p>
        <p><b>Library Tab</b> - Accesses the NE1 Part Library. There are hundreds of
        structures to choose from.</p>""")

        self.transmuteBtn.setWhatsThis("""<b>Transmute Atoms</b>
        <p>When clicked, transmutes selected atoms to the current type displayed
        in the Preview window.</p>""")

        self.transmuteCB.setWhatsThis("""<b>Force to keep bonds</b>
        <p>When checked, all bonds remain in place when atoms are transmuted,
        even if this will result in unrealistic (chemical) bonds.</p>""")

        self.selectionFilter_groupBox.setWhatsThis("""<b>Atoms Selection Filter</b>
        <p>When activated, only atoms listed can be selected.</p>""")

        self.advancedOptions_groupBox.setWhatsThis("""<b>Advanced Options</b>
        <p><b>Autobond</b> - when checked, the atom being inserted or attached to another
        atom will autobond to other bondpoints of other candidate atoms automatically.</p>
        <p><b>Highlighting</b> - Turns on/off hover highlighting.</p>
        <p><b>Water</b>- Adds a tranparent plane normal to the screen through 0,0,0. Anything
        behind the water plane can not be selected.</p>""")

        self.transmuteAtomsAction.setWhatsThis("""<b>Transmute Atoms</b>
        <p>When clicked, transmutes selected atoms to the current type displayed
        in the Preview window.</p>""")

    pass # end of class MMKit