diff --git a/pdex/src/processing/mode/experimental/ASTGenerator.java b/pdex/src/processing/mode/experimental/ASTGenerator.java index 614cd2b9e..fe249c059 100644 --- a/pdex/src/processing/mode/experimental/ASTGenerator.java +++ b/pdex/src/processing/mode/experimental/ASTGenerator.java @@ -2327,8 +2327,9 @@ public class ASTGenerator { protected SketchOutline sketchOutline; protected void showSketchOutline(){ - sketchOutline = new SketchOutline(codeTree, errorCheckerService); - sketchOutline.show(); + //sketchOutline = new SketchOutline(codeTree, errorCheckerService); + //sketchOutline.show(); + new TabOutline(errorCheckerService).show(); } public int javaCodeOffsetToLineStartOffset(int line, int jOffset){ diff --git a/pdex/src/processing/mode/experimental/TabOutline.java b/pdex/src/processing/mode/experimental/TabOutline.java new file mode 100644 index 000000000..7a866f149 --- /dev/null +++ b/pdex/src/processing/mode/experimental/TabOutline.java @@ -0,0 +1,302 @@ +package processing.mode.experimental; + +import java.awt.Dimension; +import java.awt.Point; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.WindowEvent; +import java.awt.event.WindowFocusListener; + +import javax.swing.BoxLayout; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextField; +import javax.swing.JTree; +import javax.swing.ScrollPaneConstants; +import javax.swing.SwingWorker; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.TreeSelectionListener; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeSelectionModel; + +import processing.app.SketchCode; +import static processing.mode.experimental.ExperimentalMode.log; + +public class TabOutline { + protected JFrame frmOutlineView; + + protected JScrollPane jsp; + + protected DefaultMutableTreeNode tabNode, tempNode; + + protected JTree tabTree; + + protected JTextField searchField; + + protected DebugEditor editor; + + protected ErrorCheckerService errorCheckerService; + + protected boolean internalSelection = false; + + public TabOutline(ErrorCheckerService ecs) { + errorCheckerService = ecs; + editor = ecs.getEditor(); + createAndShowGUI(); + } + + private void createAndShowGUI() { + frmOutlineView = new JFrame(); + frmOutlineView.setAlwaysOnTop(true); + frmOutlineView.setUndecorated(true); + Point tp = errorCheckerService.getEditor().ta.getLocationOnScreen(); + + int minWidth = (int) (editor.getMinimumSize().width * 0.7f), maxWidth = (int) (editor + .getMinimumSize().width * 0.9f); + frmOutlineView.setLayout(new BoxLayout(frmOutlineView.getContentPane(), + BoxLayout.Y_AXIS)); + JPanel panelTop = new JPanel(), panelBottom = new JPanel(); + panelTop.setLayout(new BoxLayout(panelTop, BoxLayout.Y_AXIS)); + panelBottom.setLayout(new BoxLayout(panelBottom, BoxLayout.Y_AXIS)); + searchField = new JTextField(); + searchField.setMinimumSize(new Dimension(minWidth, 25)); + panelTop.add(searchField); + + jsp = new JScrollPane(); + populateTabTree(); + jsp.setViewportView(tabTree); + jsp.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); + jsp.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); + jsp.setMinimumSize(new Dimension(minWidth, editor.ta.getHeight() - 10)); + jsp.setMaximumSize(new Dimension(maxWidth, editor.ta.getHeight() - 10)); + + panelBottom.add(jsp); + frmOutlineView.add(panelTop); + frmOutlineView.add(panelBottom); + frmOutlineView.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + frmOutlineView.pack(); + frmOutlineView.setBounds(tp.x + + errorCheckerService.getEditor().ta + .getWidth() - minWidth, + tp.y, + minWidth, + estimateFrameHeight()); + frmOutlineView.setMinimumSize(new Dimension(minWidth, Math + .min(errorCheckerService.getEditor().ta.getHeight(), + frmOutlineView.getHeight()))); + frmOutlineView.setLocation(tp.x + + errorCheckerService.getEditor().ta + .getWidth() - frmOutlineView.getWidth(), + frmOutlineView.getY() + + (editor.ta.getHeight() - frmOutlineView + .getHeight()) / 2); + addListeners(); + } + + private void addListeners() { + searchField.addKeyListener(new KeyAdapter() { + public void keyPressed(KeyEvent evt) { + if (tabTree.getRowCount() == 0) + return; + + internalSelection = true; + + if (evt.getKeyCode() == KeyEvent.VK_ESCAPE) { + close(); + } else if (evt.getKeyCode() == KeyEvent.VK_ENTER) { + if (tabTree.getLastSelectedPathComponent() != null) { + DefaultMutableTreeNode tnode = (DefaultMutableTreeNode) tabTree + .getLastSelectedPathComponent(); + log("Enter Key, Tab: " + tnode); + switchToTab(tnode.toString()); + close(); + } + } else if (evt.getKeyCode() == KeyEvent.VK_UP) { + if (tabTree.getLastSelectedPathComponent() == null) { + tabTree.setSelectionRow(0); + return; + } + + int x = tabTree.getLeadSelectionRow() - 1; + int step = jsp.getVerticalScrollBar().getMaximum() + / tabTree.getRowCount(); + if (x == -1) { + x = tabTree.getRowCount() - 1; + jsp.getVerticalScrollBar().setValue(jsp.getVerticalScrollBar() + .getMaximum()); + } else { + jsp.getVerticalScrollBar().setValue((jsp.getVerticalScrollBar() + .getValue() - step)); + } + tabTree.setSelectionRow(x); + } else if (evt.getKeyCode() == KeyEvent.VK_DOWN) { + if (tabTree.getLastSelectedPathComponent() == null) { + tabTree.setSelectionRow(0); + return; + } + int x = tabTree.getLeadSelectionRow() + 1; + + int step = jsp.getVerticalScrollBar().getMaximum() + / tabTree.getRowCount(); + if (x == tabTree.getRowCount()) { + x = 0; + jsp.getVerticalScrollBar().setValue(jsp.getVerticalScrollBar() + .getMinimum()); + } else { + jsp.getVerticalScrollBar().setValue((jsp.getVerticalScrollBar() + .getValue() + step)); + } + tabTree.setSelectionRow(x); + } + } + }); + + searchField.getDocument().addDocumentListener(new DocumentListener() { + + public void insertUpdate(DocumentEvent e) { + updateSelection(); + } + + public void removeUpdate(DocumentEvent e) { + updateSelection(); + } + + public void changedUpdate(DocumentEvent e) { + updateSelection(); + } + + private void updateSelection() { + SwingWorker worker = new SwingWorker() { + protected Object doInBackground() throws Exception { + String text = searchField.getText().toLowerCase(); + tempNode = new DefaultMutableTreeNode(); + filterTree(text, tempNode, tabNode); + return null; + } + + protected void done() { + tabTree.setModel(new DefaultTreeModel(tempNode)); + ((DefaultTreeModel) tabTree.getModel()).reload(); +// for (int i = 0; i < tabTree.getRowCount(); i++) { +// tabTree.expandRow(i); +// } + internalSelection = true; + tabTree.setSelectionRow(0); + } + }; + worker.execute(); + } + }); + + tabTree.addTreeSelectionListener(new TreeSelectionListener() { + + public void valueChanged(TreeSelectionEvent e) { + + if (internalSelection) { + log("Internal selection"); + internalSelection = (false); + return; + } + // log(e); + SwingWorker worker = new SwingWorker() { + + protected Object doInBackground() throws Exception { + return null; + } + + protected void done() { + if (tabTree.getLastSelectedPathComponent() == null) { + return; + } + DefaultMutableTreeNode tnode = (DefaultMutableTreeNode) tabTree + .getLastSelectedPathComponent(); + log("Clicked " + tnode); + switchToTab(tnode.toString()); + close(); + } + }; + worker.execute(); + } + }); + + frmOutlineView.addWindowFocusListener(new WindowFocusListener() { + public void windowLostFocus(WindowEvent e) { + close(); + } + + public void windowGainedFocus(WindowEvent e) { + } + }); + } + + private void switchToTab(String tabName) { + for (SketchCode sc : editor.getSketch().getCode()) { + if (sc.getPrettyName().equals(tabName)) { + editor.getSketch().setCurrentCode(editor.getSketch().getCodeIndex(sc)); + } + } + } + + private void populateTabTree() { + tabNode = new DefaultMutableTreeNode("Tabs"); + for (SketchCode sc : editor.getSketch().getCode()) { + DefaultMutableTreeNode tab = new DefaultMutableTreeNode( + sc.getPrettyName()); + tabNode.add(tab); + } + tempNode = tabNode; + tabTree = new JTree(tabNode); + tabTree.getSelectionModel() + .setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); + tabTree.setRootVisible(false); + tabTree.setSelectionRow(editor.getSketch().getCurrentCodeIndex()); + } + + protected boolean filterTree(String prefix, DefaultMutableTreeNode tree, + DefaultMutableTreeNode mainTree) { + if (mainTree.isLeaf()) { + return (mainTree.getUserObject().toString().toLowerCase() + .startsWith(prefix)); + } + + boolean found = false; + for (int i = 0; i < mainTree.getChildCount(); i++) { + DefaultMutableTreeNode tNode = new DefaultMutableTreeNode( + ((DefaultMutableTreeNode) mainTree + .getChildAt(i)) + .getUserObject()); + if (filterTree(prefix, tNode, + (DefaultMutableTreeNode) mainTree.getChildAt(i))) { + found = true; + tree.add(tNode); + } + } + return found; + } + + private int estimateFrameHeight(){ + // Assuming each tree node height to be 25 pixels + return Math.min(20 * (editor.getSketch().getCodeCount() + 1), + frmOutlineView.getHeight()); +// return Math.min(editor.ta.getHeight(), +// frmOutlineView.getHeight()); + } + + public void show() { + frmOutlineView.setVisible(true); + } + + public void close() { + frmOutlineView.setVisible(false); + frmOutlineView.dispose(); + } + + public boolean isVisible() { + return frmOutlineView.isVisible(); + } + +}