class KConfigTree(JTree, CellEditorListener): """Custom Swing JTree based tree that visualizes a KConfig configuration. The full KConfig menu structure is put into a shadow tree model. From the shadow model, a real model is built (updateModel), where hidden nodes are not included. This update model is what the tree uses to visualize the configuration menu. Both the shadow and the updated model has the same TreeNodeData with KConfig data. The expanded state and search result state is kept in the TreeNodeData. """ shadowModel = None isUpdating = False showAll = False isSearching = False def __init__(self, kconf): self.setCellRenderer(CustomCellRenderer()) self.setCellEditor(CustomCellEditor(self)) self.createKconfShadowModel(kconf) self.setModel(self.createUpdatedModel()) self.expandRow(0) self.setEditable(True) self.setRootVisible(False) self.setShowsRootHandles(True) self.setRowHeight(0) self.addTreeExpansionListener(KConfigTreeExpansionListener()) self.getCellEditor().addCellEditorListener(self) def editingCanceled(self, event): """From CellEditorListener """ # log.info("editingCanceled", self.cellEditor.getCellEditorValue()) pass def editingStopped(self, event): """From CellEditorListener.""" # log.info("editingStopped", self.cellEditor.getCellEditorValue()) self.stopEditing() def createKconfShadowModel(self, kconf): """Create the one and only shadow data model""" rootNode = DefaultMutableTreeNode(kconf.mainmenu_text) self.addNodes(rootNode, kconf.top_node.list) self.shadowModel = DefaultTreeModel(rootNode) def addNodes(self, parent, node): """Recursively traverse the KConfig structure and add to the shadow model""" while node: newUiNode = DefaultMutableTreeNode(TreeNodeData(node, self)) parent.add(newUiNode) if node.list: self.addNodes(newUiNode, node.list) node = node.next def createUpdatedModel(self): """When the user does any changes in the tree, the underlaying kconfig structure will change. Nodes may change visibility and value. The tree control cannot hide nodes, so a new datamodel must be generated that does not include invisible nodes.""" shadowTreeRoot = self.shadowModel.getRoot() rootNode = DefaultMutableTreeNode("Root") self.addVisibleNodes(rootNode, shadowTreeRoot) return DefaultTreeModel(rootNode) def addVisibleNodes(self, visibleParent, shadowParent): """Adds visible nodes from the shadow tree model to the update tree model. If there is an active search operation, only search matches will be added. If showAll is set, all nodes are added regardless of visibility.""" childrenEnum = shadowParent.children() while childrenEnum.hasMoreElements(): shadowChild = childrenEnum.nextElement() if shadowChild.getUserObject().getVisible() > 0 or self.showAll: if not self.isSearching or shadowChild.getUserObject( ).isSearchMatch(): visibleChild = DefaultMutableTreeNode( shadowChild.getUserObject()) visibleParent.add(visibleChild) if shadowChild.getChildCount() > 0: self.addVisibleNodes(visibleChild, shadowChild) def isPathEditable(self, path): comp = path.getLastPathComponent() if isinstance(comp, DefaultMutableTreeNode): nodeData = comp.getUserObject() if isinstance(nodeData, TreeNodeData): return True return False def updateTree(self): """Call to create a new updated tree model""" if not self.isUpdating: # log.info("updateTree()") self.isUpdating = True self.setModel(self.createUpdatedModel()) self.updateExpandedState(self.getModel().getRoot()) self.isUpdating = False def updateExpandedState(self, parent): """Scan through the whole tree and expand the tree node if the node data has the expanded field set to True.""" childrenEnum = parent.children() while childrenEnum.hasMoreElements(): child = childrenEnum.nextElement() if child.getUserObject().isExpanded(): self.expandPath(TreePath(child.getPath())) if child.getChildCount() > 0: self.updateExpandedState(child) def setShowAll(self, show): self.showAll = show self.updateTree() def doSearch(self, searchText): """Perform a search in the data model with the supplied text.""" if len(searchText) > 0: self.isSearching = True self.doSearchBranch(self.shadowModel.getRoot(), searchText) else: self.isSearching = False self.updateTree() def doSearchBranch(self, shadowParent, searchText): """Traverse the tree model searching for the search text""" match = False childrenEnum = shadowParent.children() while childrenEnum.hasMoreElements(): shadowChild = childrenEnum.nextElement() if shadowChild.getUserObject().search(searchText, self.showAll): match = True if shadowChild.getChildCount() > 0: if self.doSearchBranch(shadowChild, searchText): shadowChild.getUserObject().setSearchMatch(True) match = True return match