mirror of
https://github.com/processing/processing4.git
synced 2026-02-04 14:19:19 +01:00
2127 lines
72 KiB
Java
2127 lines
72 KiB
Java
/*
|
|
* Copyright (C) 2012-14 Martin Leopold <m@martinleopold.com> and Manindra Moharana <me@mkmoharana.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it under
|
|
* the terms of the GNU General Public License as published by the Free Software
|
|
* Foundation; either version 2 of the License, or (at your option) any later
|
|
* version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
* details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with
|
|
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
|
* Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
package processing.mode.experimental;
|
|
import static processing.mode.experimental.ExperimentalMode.log;
|
|
import static processing.mode.experimental.ExperimentalMode.logE;
|
|
import galsasson.mode.tweak.ColorControlBox;
|
|
import galsasson.mode.tweak.Handle;
|
|
import galsasson.mode.tweak.SketchParser;
|
|
import galsasson.mode.tweak.UDPTweakClient;
|
|
|
|
import java.awt.BorderLayout;
|
|
import java.awt.CardLayout;
|
|
import java.awt.Color;
|
|
import java.awt.Container;
|
|
import java.awt.EventQueue;
|
|
import java.awt.FlowLayout;
|
|
import java.awt.Font;
|
|
import java.awt.Frame;
|
|
import java.awt.event.ActionEvent;
|
|
import java.awt.event.ActionListener;
|
|
import java.awt.event.KeyEvent;
|
|
import java.awt.event.KeyListener;
|
|
import java.awt.event.WindowEvent;
|
|
import java.awt.event.WindowFocusListener;
|
|
import java.io.ByteArrayInputStream;
|
|
import java.io.File;
|
|
import java.io.IOException;
|
|
import java.nio.charset.Charset;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
import java.util.logging.Level;
|
|
import java.util.logging.Logger;
|
|
|
|
import javax.swing.BorderFactory;
|
|
import javax.swing.Box;
|
|
import javax.swing.BoxLayout;
|
|
import javax.swing.JButton;
|
|
import javax.swing.JCheckBox;
|
|
import javax.swing.JCheckBoxMenuItem;
|
|
import javax.swing.JDialog;
|
|
import javax.swing.JLabel;
|
|
import javax.swing.JMenu;
|
|
import javax.swing.JMenuItem;
|
|
import javax.swing.JOptionPane;
|
|
import javax.swing.JPanel;
|
|
import javax.swing.JScrollPane;
|
|
import javax.swing.SwingUtilities;
|
|
import javax.swing.SwingWorker;
|
|
import javax.swing.border.EtchedBorder;
|
|
import javax.swing.table.TableModel;
|
|
import javax.swing.text.Document;
|
|
|
|
import org.eclipse.jdt.core.compiler.IProblem;
|
|
|
|
import processing.app.Base;
|
|
import processing.app.EditorState;
|
|
import processing.app.EditorToolbar;
|
|
import processing.app.Language;
|
|
import processing.app.Mode;
|
|
import processing.app.Preferences;
|
|
import processing.app.Sketch;
|
|
import processing.app.SketchCode;
|
|
import processing.app.Toolkit;
|
|
import processing.app.syntax.JEditTextArea;
|
|
import processing.app.syntax.PdeTextAreaDefaults;
|
|
import processing.core.PApplet;
|
|
import processing.mode.java.JavaEditor;
|
|
|
|
/**
|
|
* Main View Class. Handles the editor window including tool bar and menu. Has
|
|
* access to the Sketch. Provides line highlighting (for breakpoints and the
|
|
* debuggers current line).
|
|
*
|
|
* @author Martin Leopold <m@martinleopold.com>
|
|
* @author Manindra Moharana <me@mkmoharana.com>
|
|
*
|
|
*
|
|
*/
|
|
public class DebugEditor extends JavaEditor implements ActionListener {
|
|
// important fields from superclass
|
|
//protected Sketch sketch;
|
|
//private JMenu fileMenu;
|
|
//protected EditorToolbar toolbar;
|
|
|
|
// highlighting
|
|
protected Color breakpointColor = new Color(240, 240, 240); // the background color for highlighting lines
|
|
protected Color currentLineColor = new Color(255, 255, 150); // the background color for highlighting lines
|
|
protected Color breakpointMarkerColor = new Color(74, 84, 94); // the color of breakpoint gutter markers
|
|
protected Color currentLineMarkerColor = new Color(226, 117, 0); // the color of current line gutter markers
|
|
protected List<LineHighlight> breakpointedLines = new ArrayList<LineHighlight>(); // breakpointed lines
|
|
protected LineHighlight currentLine; // line the debugger is currently suspended at
|
|
protected final String breakpointMarkerComment = " //<>//"; // breakpoint marker comment
|
|
// menus
|
|
protected JMenu debugMenu; // the debug menu
|
|
// debugger control
|
|
protected JMenuItem debugMenuItem;
|
|
protected JMenuItem continueMenuItem;
|
|
protected JMenuItem stopMenuItem;
|
|
// breakpoints
|
|
protected JMenuItem toggleBreakpointMenuItem;
|
|
protected JMenuItem listBreakpointsMenuItem;
|
|
// stepping
|
|
protected JMenuItem stepOverMenuItem;
|
|
protected JMenuItem stepIntoMenuItem;
|
|
protected JMenuItem stepOutMenuItem;
|
|
// info
|
|
protected JMenuItem printStackTraceMenuItem;
|
|
protected JMenuItem printLocalsMenuItem;
|
|
protected JMenuItem printThisMenuItem;
|
|
protected JMenuItem printSourceMenuItem;
|
|
protected JMenuItem printThreads;
|
|
// variable inspector
|
|
protected JMenuItem toggleVariableInspectorMenuItem;
|
|
// references
|
|
protected ExperimentalMode dmode; // the mode
|
|
protected Debugger dbg; // the debugger
|
|
protected VariableInspector vi; // the variable inspector frame
|
|
protected TextArea ta; // the text area
|
|
|
|
|
|
protected ErrorBar errorBar;
|
|
/**
|
|
* Show Console button
|
|
*/
|
|
protected XQConsoleToggle btnShowConsole;
|
|
|
|
/**
|
|
* Show Problems button
|
|
*/
|
|
protected XQConsoleToggle btnShowErrors;
|
|
|
|
/**
|
|
* Scroll pane for Error Table
|
|
*/
|
|
protected JScrollPane errorTableScrollPane;
|
|
|
|
/**
|
|
* Panel with card layout which contains the p5 console and Error Table
|
|
* panes
|
|
*/
|
|
protected JPanel consoleProblemsPane;
|
|
|
|
protected XQErrorTable errorTable;
|
|
|
|
/**
|
|
* Enable/Disable compilation checking
|
|
*/
|
|
protected boolean compilationCheckEnabled = true;
|
|
|
|
/**
|
|
* Show warnings menu item
|
|
*/
|
|
protected JCheckBoxMenuItem showWarnings;
|
|
|
|
/**
|
|
* Check box menu item for show/hide Problem Window
|
|
*/
|
|
public JCheckBoxMenuItem problemWindowMenuCB;
|
|
|
|
/**
|
|
* Enable/Disable debug ouput
|
|
*/
|
|
protected JCheckBoxMenuItem debugMessagesEnabled;
|
|
|
|
/**
|
|
* Show outline view
|
|
*/
|
|
protected JMenuItem showOutline, showTabOutline;
|
|
|
|
/**
|
|
* Enable/Disable error logging
|
|
*/
|
|
protected JCheckBoxMenuItem writeErrorLog;
|
|
|
|
/**
|
|
* Enable/Disable code completion
|
|
*/
|
|
protected JCheckBoxMenuItem completionsEnabled;
|
|
|
|
/**
|
|
* If sketch contains java tabs, some editor features are disabled
|
|
*/
|
|
protected boolean hasJavaTabs;
|
|
|
|
/**
|
|
* UNUSED. Disbaled for now.
|
|
*/
|
|
protected AutoSaveUtil autosaver;
|
|
|
|
public DebugEditor(Base base, String path, EditorState state, Mode mode) {
|
|
super(base, path, state, mode);
|
|
|
|
// get mode
|
|
dmode = (ExperimentalMode) mode;
|
|
|
|
// init controller class
|
|
dbg = new Debugger(this);
|
|
|
|
// variable inspector window
|
|
vi = new VariableInspector(this);
|
|
|
|
// access to customized (i.e. subclassed) text area
|
|
ta = (TextArea) textarea;
|
|
|
|
// Add show usage option
|
|
JMenuItem showUsageItem = new JMenuItem("Show Usage..");
|
|
showUsageItem.addActionListener(new ActionListener() {
|
|
public void actionPerformed(ActionEvent e) {
|
|
handleShowUsage();
|
|
}
|
|
});
|
|
ta.getRightClickPopup().add(showUsageItem);
|
|
|
|
// add refactor option
|
|
JMenuItem renameItem = new JMenuItem("Rename..");
|
|
renameItem.addActionListener(new ActionListener() {
|
|
public void actionPerformed(ActionEvent e) {
|
|
handleRefactor();
|
|
}
|
|
});
|
|
|
|
// TODO: Add support for word select on right click and rename.
|
|
// ta.customPainter.addMouseListener(new MouseAdapter() {
|
|
// public void mouseClicked(MouseEvent evt) {
|
|
// System.out.println(evt);
|
|
// }
|
|
// });
|
|
ta.getRightClickPopup().add(renameItem);
|
|
// set action on frame close
|
|
// addWindowListener(new WindowAdapter() {
|
|
// @Override
|
|
// public void windowClosing(WindowEvent e) {
|
|
// onWindowClosing(e);
|
|
// }
|
|
// });
|
|
|
|
Toolkit.setMenuMnemonics(ta.getRightClickPopup());
|
|
|
|
// load settings from theme.txt
|
|
ExperimentalMode theme = dmode;
|
|
breakpointColor = theme.getThemeColor("breakpoint.bgcolor", breakpointColor);
|
|
breakpointMarkerColor = theme.getThemeColor("breakpoint.marker.color", breakpointMarkerColor);
|
|
currentLineColor = theme.getThemeColor("currentline.bgcolor", currentLineColor);
|
|
currentLineMarkerColor = theme.getThemeColor("currentline.marker.color", currentLineMarkerColor);
|
|
|
|
// set breakpoints from marker comments
|
|
for (LineID lineID : stripBreakpointComments()) {
|
|
//System.out.println("setting: " + lineID);
|
|
dbg.setBreakpoint(lineID);
|
|
}
|
|
getSketch().setModified(false); // setting breakpoints will flag sketch as modified, so override this here
|
|
|
|
checkForJavaTabs();
|
|
initializeErrorChecker();
|
|
ta.setECSandThemeforTextArea(errorCheckerService, dmode);
|
|
addXQModeUI();
|
|
debugToolbarEnabled = new AtomicBoolean(false);
|
|
//log("Sketch Path: " + path);
|
|
}
|
|
|
|
private void addXQModeUI(){
|
|
|
|
// Adding ErrorBar
|
|
JPanel textAndError = new JPanel();
|
|
Box box = (Box) textarea.getParent();
|
|
box.remove(2); // Remove textArea from it's container, i.e Box
|
|
textAndError.setLayout(new BorderLayout());
|
|
errorBar = new ErrorBar(this, textarea.getMinimumSize().height, dmode);
|
|
textAndError.add(errorBar, BorderLayout.EAST);
|
|
textarea.setBounds(0, 0, errorBar.getX() - 1, textarea.getHeight());
|
|
textAndError.add(textarea);
|
|
box.add(textAndError);
|
|
|
|
// Adding Error Table in a scroll pane
|
|
errorTableScrollPane = new JScrollPane();
|
|
errorTable = new XQErrorTable(errorCheckerService);
|
|
// errorTableScrollPane.setBorder(new EmptyBorder(2, 2, 2, 2));
|
|
errorTableScrollPane.setBorder(new EtchedBorder());
|
|
errorTableScrollPane.setViewportView(errorTable);
|
|
|
|
// Adding toggle console button
|
|
consolePanel.remove(2);
|
|
JPanel lineStatusPanel = new JPanel();
|
|
lineStatusPanel.setLayout(new BorderLayout());
|
|
btnShowConsole = new XQConsoleToggle(this,
|
|
XQConsoleToggle.CONSOLE, lineStatus.getHeight());
|
|
btnShowErrors = new XQConsoleToggle(this,
|
|
XQConsoleToggle.ERRORSLIST, lineStatus.getHeight());
|
|
btnShowConsole.addMouseListener(btnShowConsole);
|
|
|
|
// lineStatusPanel.add(btnShowConsole, BorderLayout.EAST);
|
|
// lineStatusPanel.add(btnShowErrors);
|
|
btnShowErrors.addMouseListener(btnShowErrors);
|
|
|
|
JPanel toggleButtonPanel = new JPanel(new BorderLayout());
|
|
toggleButtonPanel.add(btnShowConsole, BorderLayout.EAST);
|
|
toggleButtonPanel.add(btnShowErrors, BorderLayout.WEST);
|
|
lineStatusPanel.add(toggleButtonPanel, BorderLayout.EAST);
|
|
lineStatus.setBounds(0, 0, toggleButtonPanel.getX() - 1,
|
|
toggleButtonPanel.getHeight());
|
|
lineStatusPanel.add(lineStatus);
|
|
consolePanel.add(lineStatusPanel, BorderLayout.SOUTH);
|
|
lineStatusPanel.repaint();
|
|
|
|
// Adding JPanel with CardLayout for Console/Problems Toggle
|
|
consolePanel.remove(1);
|
|
consoleProblemsPane = new JPanel(new CardLayout());
|
|
consoleProblemsPane.add(errorTableScrollPane, XQConsoleToggle.ERRORSLIST);
|
|
consoleProblemsPane.add(console, XQConsoleToggle.CONSOLE);
|
|
consolePanel.add(consoleProblemsPane, BorderLayout.CENTER);
|
|
|
|
// ensure completion gets hidden on editor losing focus
|
|
addWindowFocusListener(new WindowFocusListener() {
|
|
public void windowLostFocus(WindowEvent e) {
|
|
ta.hideSuggestion();
|
|
}
|
|
public void windowGainedFocus(WindowEvent e) {
|
|
|
|
}
|
|
});
|
|
}
|
|
|
|
// /**
|
|
// * Event handler called when closing the editor window. Kills the variable
|
|
// * inspector window.
|
|
// *
|
|
// * @param e the event object
|
|
// */
|
|
// protected void onWindowClosing(WindowEvent e) {
|
|
// // remove var.inspector
|
|
// vi.dispose();
|
|
// // quit running debug session
|
|
// dbg.stopDebug();
|
|
// }
|
|
/**
|
|
* Used instead of the windowClosing event handler, since it's not called on
|
|
* mode switch. Called when closing the editor window. Stops running debug
|
|
* sessions and kills the variable inspector window.
|
|
*/
|
|
@Override
|
|
public void dispose() {
|
|
//System.out.println("window dispose");
|
|
// quit running debug session
|
|
dbg.stopDebug();
|
|
// remove var.inspector
|
|
vi.dispose();
|
|
errorCheckerService.stopThread();
|
|
// original dispose
|
|
super.dispose();
|
|
}
|
|
|
|
// Added temporarily to dump error log. TODO: Remove this later
|
|
public void internalCloseRunner(){
|
|
if(ExperimentalMode.errorLogsEnabled) writeErrorsToFile();
|
|
// if(autosaver != null && !viewingAutosaveBackup) {
|
|
// log("stopping autosaver in internalCloseRunner");
|
|
// autosaver.stop();
|
|
// }
|
|
super.internalCloseRunner();
|
|
}
|
|
|
|
/**
|
|
* Writes all error messages to a csv file.
|
|
* For analytics purposes only.
|
|
*/
|
|
private void writeErrorsToFile() {
|
|
if (errorCheckerService.tempErrorLog.size() == 0) return;
|
|
|
|
try {
|
|
System.out.println("Writing errors");
|
|
StringBuilder sb = new StringBuilder();
|
|
sb.append("Sketch: " + getSketch().getFolder() + ", "
|
|
+ new java.sql.Timestamp(new java.util.Date().getTime())
|
|
+ "\nComma in error msg is substituted with ^ symbol\nFor separating arguments in error args | symbol is used\n");
|
|
sb.append("ERROR TYPE, ERROR ARGS, ERROR MSG\n");
|
|
|
|
for (String errMsg : errorCheckerService.tempErrorLog.keySet()) {
|
|
IProblem ip = errorCheckerService.tempErrorLog.get(errMsg);
|
|
if (ip != null) {
|
|
sb.append(ErrorMessageSimplifier.getIDName(ip.getID()));
|
|
sb.append(',');
|
|
sb.append("{");
|
|
for (int i = 0; i < ip.getArguments().length; i++) {
|
|
sb.append(ip.getArguments()[i]);
|
|
if (i < ip.getArguments().length-1)
|
|
sb.append("| ");
|
|
}
|
|
sb.append("}");
|
|
sb.append(',');
|
|
sb.append(ip.getMessage().replace(',', '^'));
|
|
sb.append("\n");
|
|
}
|
|
}
|
|
System.out.println(sb);
|
|
File opFile = new File(getSketch().getFolder(), "ErrorLogs"
|
|
+ File.separator + "ErrorLog_" + System.currentTimeMillis() + ".csv");
|
|
PApplet.saveStream(opFile, new ByteArrayInputStream(sb.toString()
|
|
.getBytes(Charset.defaultCharset())));
|
|
} catch (Exception e) {
|
|
System.err.println("Failed to save log file for sketch " + getSketch().getName());
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Overrides sketch menu creation to change keyboard shortcuts from "Run".
|
|
*
|
|
* @return the sketch menu
|
|
*/
|
|
/*@Override
|
|
public JMenu buildSketchMenu() {
|
|
JMenuItem runItem = Toolkit.newJMenuItemShift(DebugToolbar.getTitle(DebugToolbar.RUN, false), KeyEvent.VK_R);
|
|
runItem.addActionListener(new ActionListener() {
|
|
@Override
|
|
public void actionPerformed(ActionEvent e) {
|
|
handleRun();
|
|
}
|
|
});
|
|
|
|
JMenuItem presentItem = new JMenuItem(DebugToolbar.getTitle(DebugToolbar.RUN, true));
|
|
presentItem.addActionListener(new ActionListener() {
|
|
@Override
|
|
public void actionPerformed(ActionEvent e) {
|
|
handlePresent();
|
|
}
|
|
});
|
|
|
|
JMenuItem stopItem = new JMenuItem(DebugToolbar.getTitle(DebugToolbar.STOP, false));
|
|
stopItem.addActionListener(new ActionListener() {
|
|
@Override
|
|
public void actionPerformed(ActionEvent e) {
|
|
handleStop();
|
|
}
|
|
});
|
|
return buildSketchMenu(new JMenuItem[]{runItem, presentItem, stopItem});
|
|
}*/
|
|
|
|
/**
|
|
* Whether debug toolbar is enabled
|
|
*/
|
|
AtomicBoolean debugToolbarEnabled;
|
|
|
|
protected EditorToolbar javaToolbar, debugToolbar;
|
|
|
|
/**
|
|
* Toggles between java mode and debug mode toolbar
|
|
*/
|
|
protected void switchToolbars(){
|
|
final EditorToolbar nextToolbar;
|
|
if(debugToolbarEnabled.get()){
|
|
// switch to java
|
|
if(javaToolbar == null)
|
|
javaToolbar = createToolbar();
|
|
nextToolbar = javaToolbar;
|
|
debugToolbarEnabled.set(false);
|
|
log("Switching to Java Mode Toolbar");
|
|
}
|
|
else{
|
|
// switch to debug
|
|
if(debugToolbar == null)
|
|
debugToolbar = new DebugToolbar(this, getBase());
|
|
nextToolbar = debugToolbar;
|
|
debugToolbarEnabled.set(true);
|
|
log("Switching to Debugger Toolbar");
|
|
}
|
|
|
|
SwingUtilities.invokeLater(new Runnable() {
|
|
public void run() {
|
|
Box upper = (Box)splitPane.getComponent(0);
|
|
upper.remove(0);
|
|
upper.add(nextToolbar, 0);
|
|
upper.validate();
|
|
nextToolbar.repaint();
|
|
toolbar = nextToolbar;
|
|
// The toolbar responds to shift down/up events
|
|
// in order to show the alt version of toolbar buttons.
|
|
// With toolbar switch, KeyListener has to be changed as well
|
|
for (KeyListener kl : textarea.getKeyListeners()) {
|
|
if(kl instanceof EditorToolbar)
|
|
{
|
|
textarea.removeKeyListener(kl);
|
|
textarea.addKeyListener(toolbar);
|
|
break;
|
|
}
|
|
}
|
|
ta.repaint();
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Creates the debug menu. Includes ActionListeners for the menu items.
|
|
* Intended for adding to the menu bar.
|
|
*
|
|
* @return The debug menu
|
|
*/
|
|
protected JMenu buildDebugMenu() {
|
|
debugMenu = new JMenu(Language.text("menu.debug"));
|
|
//debugMenu = new JMenu("PDE X");
|
|
|
|
JCheckBoxMenuItem toggleDebugger = new JCheckBoxMenuItem(Language.text("menu.debug.show_debug_toolbar"));
|
|
toggleDebugger.setSelected(false);
|
|
toggleDebugger.addActionListener(new ActionListener() {
|
|
public void actionPerformed(ActionEvent e) {
|
|
switchToolbars();
|
|
}
|
|
});
|
|
debugMenu.add(toggleDebugger);
|
|
debugMenuItem = Toolkit.newJMenuItemAlt(Language.text("menu.debug.debug"), KeyEvent.VK_R);
|
|
debugMenuItem.addActionListener(this);
|
|
continueMenuItem = Toolkit.newJMenuItem(Language.text("menu.debug.continue"), KeyEvent.VK_U);
|
|
continueMenuItem.addActionListener(this);
|
|
stopMenuItem = new JMenuItem(Language.text("menu.debug.stop"));
|
|
stopMenuItem.addActionListener(this);
|
|
|
|
toggleBreakpointMenuItem = Toolkit.newJMenuItem(Language.text("menu.debug.toggle_breakpoint"), KeyEvent.VK_B);
|
|
toggleBreakpointMenuItem.addActionListener(this);
|
|
listBreakpointsMenuItem = new JMenuItem(Language.text("menu.debug.list_breakpoints"));
|
|
listBreakpointsMenuItem.addActionListener(this);
|
|
|
|
stepOverMenuItem = Toolkit.newJMenuItem(Language.text("menu.debug.step"), KeyEvent.VK_J);
|
|
stepOverMenuItem.addActionListener(this);
|
|
stepIntoMenuItem = Toolkit.newJMenuItemShift(Language.text("menu.debug.step_into"), KeyEvent.VK_J);
|
|
stepIntoMenuItem.addActionListener(this);
|
|
stepOutMenuItem = Toolkit.newJMenuItemAlt(Language.text("menu.debug.step_out"), KeyEvent.VK_J);
|
|
stepOutMenuItem.addActionListener(this);
|
|
|
|
printStackTraceMenuItem = new JMenuItem(Language.text("menu.debug.print_stack_trace"));
|
|
printStackTraceMenuItem.addActionListener(this);
|
|
printLocalsMenuItem = new JMenuItem(Language.text("menu.debug.print_locals"));
|
|
printLocalsMenuItem.addActionListener(this);
|
|
printThisMenuItem = new JMenuItem(Language.text("menu.debug.print_fields"));
|
|
printThisMenuItem.addActionListener(this);
|
|
printSourceMenuItem = new JMenuItem(Language.text("menu.debug.print_source_location"));
|
|
printSourceMenuItem.addActionListener(this);
|
|
printThreads = new JMenuItem(Language.text("menu.debug.print_threads"));
|
|
printThreads.addActionListener(this);
|
|
|
|
toggleVariableInspectorMenuItem = Toolkit.newJMenuItem(Language.text("menu.debug.toggle_variable_inspector"), KeyEvent.VK_I);
|
|
toggleVariableInspectorMenuItem.addActionListener(this);
|
|
|
|
debugMenu.add(debugMenuItem);
|
|
debugMenu.add(continueMenuItem);
|
|
debugMenu.add(stopMenuItem);
|
|
debugMenu.addSeparator();
|
|
debugMenu.add(toggleBreakpointMenuItem);
|
|
debugMenu.add(listBreakpointsMenuItem);
|
|
debugMenu.addSeparator();
|
|
debugMenu.add(stepOverMenuItem);
|
|
debugMenu.add(stepIntoMenuItem);
|
|
debugMenu.add(stepOutMenuItem);
|
|
debugMenu.addSeparator();
|
|
debugMenu.add(printStackTraceMenuItem);
|
|
debugMenu.add(printLocalsMenuItem);
|
|
debugMenu.add(printThisMenuItem);
|
|
debugMenu.add(printSourceMenuItem);
|
|
debugMenu.add(printThreads);
|
|
debugMenu.addSeparator();
|
|
debugMenu.add(toggleVariableInspectorMenuItem);
|
|
// debugMenu.addSeparator();
|
|
|
|
// XQMode menu items
|
|
/*
|
|
JCheckBoxMenuItem item;
|
|
item = new JCheckBoxMenuItem("Error Checker Enabled");
|
|
item.setSelected(ExperimentalMode.errorCheckEnabled);
|
|
item.addActionListener(new ActionListener() {
|
|
|
|
@Override
|
|
public void actionPerformed(ActionEvent e) {
|
|
ExperimentalMode.errorCheckEnabled = ((JCheckBoxMenuItem) e.getSource()).isSelected();
|
|
errorCheckerService.handleErrorCheckingToggle();
|
|
dmode.savePreferences();
|
|
}
|
|
});
|
|
debugMenu.add(item);
|
|
|
|
problemWindowMenuCB = new JCheckBoxMenuItem("Show Problem Window");
|
|
// problemWindowMenuCB.setSelected(true);
|
|
problemWindowMenuCB.addActionListener(new ActionListener() {
|
|
|
|
@Override
|
|
public void actionPerformed(ActionEvent e) {
|
|
if (errorCheckerService.errorWindow == null) {
|
|
return;
|
|
}
|
|
errorCheckerService.errorWindow
|
|
.setVisible(((JCheckBoxMenuItem) e.getSource())
|
|
.isSelected());
|
|
// switch to console, now that Error Window is open
|
|
showProblemListView(XQConsoleToggle.CONSOLE);
|
|
}
|
|
});
|
|
debugMenu.add(problemWindowMenuCB);
|
|
|
|
showWarnings = new JCheckBoxMenuItem("Warnings Enabled");
|
|
showWarnings.setSelected(ExperimentalMode.warningsEnabled);
|
|
showWarnings.addActionListener(new ActionListener() {
|
|
|
|
@Override
|
|
public void actionPerformed(ActionEvent e) {
|
|
ExperimentalMode.warningsEnabled = ((JCheckBoxMenuItem) e
|
|
.getSource()).isSelected();
|
|
errorCheckerService.runManualErrorCheck();
|
|
dmode.savePreferences();
|
|
}
|
|
});
|
|
debugMenu.add(showWarnings);
|
|
|
|
completionsEnabled = new JCheckBoxMenuItem("Code Completion Enabled");
|
|
completionsEnabled.setSelected(ExperimentalMode.codeCompletionsEnabled);
|
|
completionsEnabled.addActionListener(new ActionListener() {
|
|
@Override
|
|
public void actionPerformed(ActionEvent e) {
|
|
ExperimentalMode.codeCompletionsEnabled = (((JCheckBoxMenuItem) e
|
|
.getSource()).isSelected());
|
|
dmode.savePreferences();
|
|
}
|
|
});
|
|
debugMenu.add(completionsEnabled);
|
|
|
|
debugMessagesEnabled = new JCheckBoxMenuItem("Show Debug Messages");
|
|
debugMessagesEnabled.setSelected(ExperimentalMode.DEBUG);
|
|
debugMessagesEnabled.addActionListener(new ActionListener() {
|
|
@Override
|
|
public void actionPerformed(ActionEvent e) {
|
|
ExperimentalMode.DEBUG = ((JCheckBoxMenuItem) e
|
|
.getSource()).isSelected();
|
|
dmode.savePreferences();
|
|
}
|
|
});
|
|
debugMenu.add(debugMessagesEnabled);
|
|
|
|
|
|
writeErrorLog = new JCheckBoxMenuItem("Write Errors to Log");
|
|
writeErrorLog.setSelected(ExperimentalMode.errorLogsEnabled);
|
|
writeErrorLog.addActionListener(new ActionListener() {
|
|
@Override
|
|
public void actionPerformed(ActionEvent e) {
|
|
ExperimentalMode.errorLogsEnabled = ((JCheckBoxMenuItem) e
|
|
.getSource()).isSelected();
|
|
dmode.savePreferences();
|
|
}
|
|
});
|
|
debugMenu.add(writeErrorLog);
|
|
|
|
debugMenu.addSeparator();
|
|
JMenuItem jitem = new JMenuItem("PDE X on GitHub");
|
|
jitem.addActionListener(new ActionListener() {
|
|
@Override
|
|
public void actionPerformed(ActionEvent e) {
|
|
Base.openURL("https://github.com/processing/processing-experimental");
|
|
}
|
|
});
|
|
debugMenu.add(jitem);
|
|
*/
|
|
showOutline = Toolkit.newJMenuItem(Language.text("menu.debug.show_sketch_outline"), KeyEvent.VK_L);
|
|
showOutline.addActionListener(this);
|
|
debugMenu.add(showOutline);
|
|
|
|
showTabOutline = Toolkit.newJMenuItem(Language.text("menu.debug.show_tabs_list"), KeyEvent.VK_Y);
|
|
showTabOutline.addActionListener(this);
|
|
debugMenu.add(showTabOutline);
|
|
|
|
|
|
return debugMenu;
|
|
}
|
|
|
|
@Override
|
|
public JMenu buildModeMenu() {
|
|
return buildDebugMenu();
|
|
}
|
|
|
|
public JMenu buildSketchMenu() {
|
|
JMenuItem runItem = Toolkit.newJMenuItem(DebugToolbar
|
|
.getTitle(DebugToolbar.RUN, false), 'R');
|
|
runItem.addActionListener(new ActionListener() {
|
|
public void actionPerformed(ActionEvent e) {
|
|
handleRun();
|
|
}
|
|
});
|
|
|
|
JMenuItem presentItem = Toolkit.newJMenuItemShift(DebugToolbar
|
|
.getTitle(DebugToolbar.RUN, true), 'R');
|
|
presentItem.addActionListener(new ActionListener() {
|
|
public void actionPerformed(ActionEvent e) {
|
|
handlePresent();
|
|
}
|
|
});
|
|
|
|
JMenuItem stopItem = new JMenuItem(DebugToolbar.getTitle(DebugToolbar.STOP,
|
|
false));
|
|
stopItem.addActionListener(new ActionListener() {
|
|
public void actionPerformed(ActionEvent e) {
|
|
handleStop();
|
|
}
|
|
});
|
|
|
|
JMenuItem enableTweak = Toolkit.newJMenuItemShift(Language.text("menu.sketch.tweak"), 'T');
|
|
enableTweak.setSelected(ExperimentalMode.enableTweak);
|
|
enableTweak.addActionListener(new ActionListener() {
|
|
@Override
|
|
public void actionPerformed(ActionEvent e) {
|
|
ExperimentalMode.enableTweak = true;
|
|
handleRun();
|
|
}
|
|
});
|
|
|
|
return buildSketchMenu(new JMenuItem[] {
|
|
runItem, presentItem, enableTweak, stopItem });
|
|
}
|
|
|
|
/**
|
|
* Callback for menu items. Implementation of Swing ActionListener.
|
|
*
|
|
* @param ae Action event
|
|
*/
|
|
@Override
|
|
public void actionPerformed(ActionEvent ae) {
|
|
//System.out.println("ActionEvent: " + ae.toString());
|
|
|
|
JMenuItem source = (JMenuItem) ae.getSource();
|
|
if (source == debugMenuItem) {
|
|
Logger.getLogger(DebugEditor.class.getName()).log(Level.INFO, "Invoked 'Debug' menu item");
|
|
//dmode.handleDebug(sketch, this);
|
|
dbg.startDebug();
|
|
} else if (source == stopMenuItem) {
|
|
Logger.getLogger(DebugEditor.class.getName()).log(Level.INFO, "Invoked 'Stop' menu item");
|
|
//dmode.handleDebug(sketch, this);
|
|
dbg.stopDebug();
|
|
} else if (source == continueMenuItem) {
|
|
Logger.getLogger(DebugEditor.class.getName()).log(Level.INFO, "Invoked 'Continue' menu item");
|
|
//dmode.handleDebug(sketch, this);
|
|
dbg.continueDebug();
|
|
} else if (source == stepOverMenuItem) {
|
|
Logger.getLogger(DebugEditor.class.getName()).log(Level.INFO, "Invoked 'Step Over' menu item");
|
|
dbg.stepOver();
|
|
} else if (source == stepIntoMenuItem) {
|
|
Logger.getLogger(DebugEditor.class.getName()).log(Level.INFO, "Invoked 'Step Into' menu item");
|
|
dbg.stepInto();
|
|
} else if (source == stepOutMenuItem) {
|
|
Logger.getLogger(DebugEditor.class.getName()).log(Level.INFO, "Invoked 'Step Out' menu item");
|
|
dbg.stepOut();
|
|
} else if (source == printStackTraceMenuItem) {
|
|
Logger.getLogger(DebugEditor.class.getName()).log(Level.INFO, "Invoked 'Print Stack Trace' menu item");
|
|
dbg.printStackTrace();
|
|
} else if (source == printLocalsMenuItem) {
|
|
Logger.getLogger(DebugEditor.class.getName()).log(Level.INFO, "Invoked 'Print Locals' menu item");
|
|
dbg.printLocals();
|
|
} else if (source == printThisMenuItem) {
|
|
Logger.getLogger(DebugEditor.class.getName()).log(Level.INFO, "Invoked 'Print This' menu item");
|
|
dbg.printThis();
|
|
} else if (source == printSourceMenuItem) {
|
|
Logger.getLogger(DebugEditor.class.getName()).log(Level.INFO, "Invoked 'Print Source' menu item");
|
|
dbg.printSource();
|
|
} else if (source == printThreads) {
|
|
Logger.getLogger(DebugEditor.class.getName()).log(Level.INFO, "Invoked 'Print Threads' menu item");
|
|
dbg.printThreads();
|
|
} else if (source == toggleBreakpointMenuItem) {
|
|
Logger.getLogger(DebugEditor.class.getName()).log(Level.INFO, "Invoked 'Toggle Breakpoint' menu item");
|
|
dbg.toggleBreakpoint();
|
|
} else if (source == listBreakpointsMenuItem) {
|
|
Logger.getLogger(DebugEditor.class.getName()).log(Level.INFO, "Invoked 'List Breakpoints' menu item");
|
|
dbg.listBreakpoints();
|
|
} else if (source == toggleVariableInspectorMenuItem) {
|
|
Logger.getLogger(DebugEditor.class.getName()).log(Level.INFO, "Invoked 'Toggle Variable Inspector' menu item");
|
|
toggleVariableInspector();
|
|
} else if (source.equals(showOutline)){
|
|
log("Show Sketch Outline:");
|
|
errorCheckerService.getASTGenerator().showSketchOutline();
|
|
}
|
|
else if (source.equals(showTabOutline)){
|
|
log("Show Tab Outline:");
|
|
errorCheckerService.getASTGenerator().showTabOutline();
|
|
}
|
|
}
|
|
|
|
// @Override
|
|
// public void handleRun() {
|
|
// dbg.continueDebug();
|
|
// }
|
|
/**
|
|
* Event handler called when hitting the stop button. Stops a running debug
|
|
* session or performs standard stop action if not currently debugging.
|
|
*/
|
|
@Override
|
|
public void handleStop() {
|
|
if (dbg.isStarted()) {
|
|
dbg.stopDebug();
|
|
} else {
|
|
super.handleStop();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Event handler called when loading another sketch in this editor. Clears
|
|
* breakpoints of previous sketch.
|
|
*
|
|
* @param path
|
|
* @return true if a sketch was opened, false if aborted
|
|
*/
|
|
@Override
|
|
protected boolean handleOpenInternal(String path) {
|
|
// log("handleOpenInternal, path: " + path);
|
|
boolean didOpen = super.handleOpenInternal(path);
|
|
if (didOpen && dbg != null) {
|
|
// should already been stopped (open calls handleStop)
|
|
dbg.clearBreakpoints();
|
|
clearBreakpointedLines(); // force clear breakpoint highlights
|
|
variableInspector().reset(); // clear contents of variable inspector
|
|
}
|
|
//if(didOpen){
|
|
// autosaver = new AutoSaveUtil(this, ExperimentalMode.autoSaveInterval); // this is used instead of loadAutosaver(), temp measure
|
|
// loadAutoSaver();
|
|
// viewingAutosaveBackup = autosaver.isAutoSaveBackup();
|
|
// log("handleOpenInternal, viewing autosave? " + viewingAutosaveBackup);
|
|
//}
|
|
return didOpen;
|
|
}
|
|
|
|
/**
|
|
* Extract breakpointed lines from source code marker comments. This removes
|
|
* marker comments from the editor text. Intended to be called on loading a
|
|
* sketch, since re-setting the sketches contents after removing the markers
|
|
* will clear all breakpoints.
|
|
*
|
|
* @return the list of {@link LineID}s where breakpoint marker comments were
|
|
* removed from.
|
|
*/
|
|
protected List<LineID> stripBreakpointComments() {
|
|
List<LineID> bps = new ArrayList<LineID>();
|
|
// iterate over all tabs
|
|
Sketch sketch = getSketch();
|
|
for (int i = 0; i < sketch.getCodeCount(); i++) {
|
|
SketchCode tab = sketch.getCode(i);
|
|
String code = tab.getProgram();
|
|
String lines[] = code.split("\\r?\\n"); // newlines not included
|
|
//System.out.println(code);
|
|
|
|
// scan code for breakpoint comments
|
|
int lineIdx = 0;
|
|
for (String line : lines) {
|
|
//System.out.println(line);
|
|
if (line.endsWith(breakpointMarkerComment)) {
|
|
LineID lineID = new LineID(tab.getFileName(), lineIdx);
|
|
bps.add(lineID);
|
|
//System.out.println("found breakpoint: " + lineID);
|
|
// got a breakpoint
|
|
//dbg.setBreakpoint(lineID);
|
|
int index = line.lastIndexOf(breakpointMarkerComment);
|
|
lines[lineIdx] = line.substring(0, index);
|
|
}
|
|
lineIdx++;
|
|
}
|
|
//tab.setProgram(code);
|
|
code = PApplet.join(lines, "\n");
|
|
setTabContents(tab.getFileName(), code);
|
|
}
|
|
return bps;
|
|
}
|
|
|
|
/**
|
|
* Add breakpoint marker comments to the source file of a specific tab. This
|
|
* acts on the source file on disk, not the editor text. Intended to be
|
|
* called just after saving the sketch.
|
|
*
|
|
* @param tabFilename the tab file name
|
|
*/
|
|
protected void addBreakpointComments(String tabFilename) {
|
|
SketchCode tab = getTab(tabFilename);
|
|
if(tab == null) {
|
|
// this method gets called twice when saving sketch for the first time
|
|
// once with new name and another with old(causing NPE). Keep an eye out
|
|
// for potential issues. See #2675. TODO:
|
|
logE("Illegal tab name to addBreakpointComments() " + tabFilename);
|
|
return;
|
|
}
|
|
List<LineBreakpoint> bps = dbg.getBreakpoints(tab.getFileName());
|
|
|
|
// load the source file
|
|
File sourceFile = new File(sketch.getFolder(), tab.getFileName());
|
|
//System.out.println("file: " + sourceFile);
|
|
try {
|
|
String code = Base.loadFile(sourceFile);
|
|
//System.out.println("code: " + code);
|
|
String lines[] = code.split("\\r?\\n"); // newlines not included
|
|
for (LineBreakpoint bp : bps) {
|
|
//System.out.println("adding bp: " + bp.lineID());
|
|
lines[bp.lineID().lineIdx()] += breakpointMarkerComment;
|
|
}
|
|
code = PApplet.join(lines, "\n");
|
|
//System.out.println("new code: " + code);
|
|
Base.saveFile(code, sourceFile);
|
|
} catch (IOException ex) {
|
|
Logger.getLogger(DebugEditor.class.getName()).log(Level.SEVERE, null, ex);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean handleSave(boolean immediately) {
|
|
//System.out.println("handleSave " + immediately);
|
|
|
|
//log("handleSave, viewing autosave? " + viewingAutosaveBackup);
|
|
/* If user wants to save a backup, the backup sketch should get
|
|
* copied to the main sketch directory, simply reload the main sketch.
|
|
*/
|
|
if(viewingAutosaveBackup){
|
|
/*
|
|
File files[] = autosaver.getSketchBackupFolder().listFiles();
|
|
File src = autosaver.getSketchBackupFolder(), dst = autosaver
|
|
.getActualSketchFolder();
|
|
for (File f : files) {
|
|
log("Copying " + f.getAbsolutePath() + " to " + dst.getAbsolutePath());
|
|
try {
|
|
if (f.isFile()) {
|
|
f.delete();
|
|
Base.copyFile(f, new File(dst + File.separator + f.getName()));
|
|
} else {
|
|
Base.removeDir(f);
|
|
Base.copyDir(f, new File(dst + File.separator + f.getName()));
|
|
}
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
File sk = autosaver.getActualSketchFolder();
|
|
Base.removeDir(autosaver.getAutoSaveDir());
|
|
//handleOpenInternal(sk.getAbsolutePath() + File.separator + sk.getName() + ".pde");
|
|
getBase().handleOpen(sk.getAbsolutePath() + File.separator + sk.getName() + ".pde");
|
|
//viewingAutosaveBackup = false;
|
|
*/
|
|
}
|
|
|
|
// note modified tabs
|
|
final List<String> modified = new ArrayList<String>();
|
|
for (int i = 0; i < getSketch().getCodeCount(); i++) {
|
|
SketchCode tab = getSketch().getCode(i);
|
|
if (tab.isModified()) {
|
|
modified.add(tab.getFileName());
|
|
}
|
|
}
|
|
|
|
boolean saved = super.handleSave(immediately);
|
|
if (saved) {
|
|
if (immediately) {
|
|
for (String tabFilename : modified) {
|
|
addBreakpointComments(tabFilename);
|
|
}
|
|
} else {
|
|
EventQueue.invokeLater(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
for (String tabFilename : modified) {
|
|
addBreakpointComments(tabFilename);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
// if file location has changed, update autosaver
|
|
// autosaver.reloadAutosaveDir();
|
|
return saved;
|
|
}
|
|
|
|
@Override
|
|
public boolean handleSaveAs() {
|
|
//System.out.println("handleSaveAs");
|
|
String oldName = getSketch().getCode(0).getFileName();
|
|
//System.out.println("old name: " + oldName);
|
|
boolean saved = super.handleSaveAs();
|
|
if (saved) {
|
|
// re-set breakpoints in first tab (name has changed)
|
|
List<LineBreakpoint> bps = dbg.getBreakpoints(oldName);
|
|
dbg.clearBreakpoints(oldName);
|
|
String newName = getSketch().getCode(0).getFileName();
|
|
//System.out.println("new name: " + newName);
|
|
for (LineBreakpoint bp : bps) {
|
|
LineID line = new LineID(newName, bp.lineID().lineIdx());
|
|
//System.out.println("setting: " + line);
|
|
dbg.setBreakpoint(line);
|
|
}
|
|
// add breakpoint marker comments to source file
|
|
for (int i = 0; i < getSketch().getCodeCount(); i++) {
|
|
addBreakpointComments(getSketch().getCode(i).getFileName());
|
|
}
|
|
|
|
// set new name of variable inspector
|
|
vi.setTitle(getSketch().getName());
|
|
}
|
|
// if file location has changed, update autosaver
|
|
// autosaver.reloadAutosaveDir();
|
|
return saved;
|
|
}
|
|
|
|
|
|
private boolean viewingAutosaveBackup;
|
|
|
|
/**
|
|
* Loads and starts the auto save service
|
|
* Also handles the case where an auto save backup is found.
|
|
* The user is asked to save the sketch to a new location
|
|
*/
|
|
private void loadAutoSaver(){
|
|
log("Load Auto Saver()");
|
|
autosaver = new AutoSaveUtil(this, ExperimentalMode.autoSaveInterval);
|
|
if(!autosaver.checkForPastSave()) {
|
|
autosaver.init();
|
|
return;
|
|
}
|
|
File pastSave = autosaver.getPastSave();
|
|
int response = Base
|
|
.showYesNoQuestion(this,
|
|
"Unsaved backup found!",
|
|
"An automatic backup of \""
|
|
+ pastSave.getParentFile().getName()
|
|
+ "\" sketch has been found. This may mean Processing " +
|
|
"was closed unexpectedly last time.",
|
|
"Select YES to view it or NO to delete the backup.");
|
|
if(response == JOptionPane.YES_OPTION){
|
|
handleOpenInternal(pastSave.getAbsolutePath());
|
|
// Base.showMessage("Save it..", "Remember to save the backup sketch to a specific location if you want to.");
|
|
//log(getSketch().getMainFilePath());
|
|
log("loadAutoSaver, viewing autosave? " + viewingAutosaveBackup);
|
|
return;
|
|
}
|
|
else{
|
|
autosaver.init();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set text contents of a specific tab. Updates underlying document and text
|
|
* area. Clears Breakpoints.
|
|
*
|
|
* @param tabFilename the tab file name
|
|
* @param code the text to set
|
|
*/
|
|
protected void setTabContents(String tabFilename, String code) {
|
|
// remove all breakpoints of this tab
|
|
dbg.clearBreakpoints(tabFilename);
|
|
|
|
SketchCode currentTab = getCurrentTab();
|
|
|
|
// set code of tab
|
|
SketchCode tab = getTab(tabFilename);
|
|
if (tab != null) {
|
|
tab.setProgram(code);
|
|
// this updates document and text area
|
|
// TODO: does this have any negative effects? (setting the doc to null)
|
|
tab.setDocument(null);
|
|
setCode(tab);
|
|
|
|
// switch back to original tab
|
|
setCode(currentTab);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Clear the console.
|
|
*/
|
|
public void clearConsole() {
|
|
console.clear();
|
|
}
|
|
|
|
/**
|
|
* Clear current text selection.
|
|
*/
|
|
public void clearSelection() {
|
|
setSelection(getCaretOffset(), getCaretOffset());
|
|
}
|
|
|
|
/**
|
|
* Select a line in the current tab.
|
|
*
|
|
* @param lineIdx 0-based line number
|
|
*/
|
|
public void selectLine(int lineIdx) {
|
|
setSelection(getLineStartOffset(lineIdx), getLineStopOffset(lineIdx));
|
|
}
|
|
|
|
/**
|
|
* Set the cursor to the start of a line.
|
|
*
|
|
* @param lineIdx 0-based line number
|
|
*/
|
|
public void cursorToLineStart(int lineIdx) {
|
|
setSelection(getLineStartOffset(lineIdx), getLineStartOffset(lineIdx));
|
|
}
|
|
|
|
/**
|
|
* Set the cursor to the end of a line.
|
|
*
|
|
* @param lineIdx 0-based line number
|
|
*/
|
|
public void cursorToLineEnd(int lineIdx) {
|
|
setSelection(getLineStopOffset(lineIdx), getLineStopOffset(lineIdx));
|
|
}
|
|
|
|
/**
|
|
* Switch to a tab.
|
|
*
|
|
* @param tabFileName the file name identifying the tab. (as in
|
|
* {@link SketchCode#getFileName()})
|
|
*/
|
|
public void switchToTab(String tabFileName) {
|
|
Sketch s = getSketch();
|
|
for (int i = 0; i < s.getCodeCount(); i++) {
|
|
if (tabFileName.equals(s.getCode(i).getFileName())) {
|
|
s.setCurrentCode(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Access the debugger.
|
|
*
|
|
* @return the debugger controller object
|
|
*/
|
|
public Debugger dbg() {
|
|
return dbg;
|
|
}
|
|
|
|
/**
|
|
* Access the mode.
|
|
*
|
|
* @return the mode object
|
|
*/
|
|
public ExperimentalMode mode() {
|
|
return dmode;
|
|
}
|
|
|
|
/**
|
|
* Access the custom text area object.
|
|
*
|
|
* @return the text area object
|
|
*/
|
|
public TextArea textArea() {
|
|
return ta;
|
|
}
|
|
|
|
|
|
/**
|
|
* Grab current contents of the sketch window, advance the console, stop any
|
|
* other running sketches, auto-save the user's code... not in that order.
|
|
*/
|
|
@Override
|
|
public void prepareRun() {
|
|
autoSave();
|
|
super.prepareRun();
|
|
}
|
|
|
|
/**
|
|
* Displays a JDialog prompting the user to save when the user hits
|
|
* run/present/etc.
|
|
*/
|
|
protected void autoSave() {
|
|
if (!ExperimentalMode.autoSaveEnabled)
|
|
return;
|
|
|
|
try {
|
|
// if (sketch.isUntitled() &&
|
|
// ExperimentalMode.untitledAutoSaveEnabled) {
|
|
// if (handleSave(true))
|
|
// statusTimedNotice("Saved. Running...", 5);
|
|
// else
|
|
// statusTimedNotice("Save Canceled. Running anyway...", 5);
|
|
// }
|
|
// else
|
|
if (sketch.isModified() && !sketch.isUntitled()) {
|
|
if (ExperimentalMode.autoSavePromptEnabled) {
|
|
final JDialog autoSaveDialog = new JDialog(
|
|
base.getActiveEditor(), this.getSketch().getName(),
|
|
true);
|
|
Container container = autoSaveDialog.getContentPane();
|
|
|
|
JPanel panelMain = new JPanel();
|
|
panelMain.setBorder(BorderFactory.createEmptyBorder(4, 0,
|
|
2, 2));
|
|
panelMain.setLayout(new BoxLayout(panelMain,
|
|
BoxLayout.PAGE_AXIS));
|
|
|
|
JPanel panelLabel = new JPanel(new FlowLayout(
|
|
FlowLayout.LEFT));
|
|
JLabel label = new JLabel(
|
|
"<html><body> There are unsaved"
|
|
+ " changes in your sketch.<br />"
|
|
+ " Do you want to save it before"
|
|
+ " running? </body></html>");
|
|
label.setFont(new Font(label.getFont().getName(),
|
|
Font.PLAIN, label.getFont().getSize() + 1));
|
|
panelLabel.add(label);
|
|
panelMain.add(panelLabel);
|
|
final JCheckBox dontRedisplay = new JCheckBox(
|
|
"Remember this decision");
|
|
|
|
JPanel panelButtons = new JPanel(new FlowLayout(
|
|
FlowLayout.CENTER, 8, 2));
|
|
JButton btnRunSave = new JButton("Save and Run");
|
|
btnRunSave.addActionListener(new ActionListener() {
|
|
|
|
@Override
|
|
public void actionPerformed(ActionEvent e) {
|
|
handleSave(true);
|
|
if (dontRedisplay.isSelected()) {
|
|
ExperimentalMode.autoSavePromptEnabled = !dontRedisplay
|
|
.isSelected();
|
|
ExperimentalMode.defaultAutoSaveEnabled = true;
|
|
dmode.savePreferences();
|
|
}
|
|
autoSaveDialog.dispose();
|
|
}
|
|
});
|
|
panelButtons.add(btnRunSave);
|
|
JButton btnRunNoSave = new JButton("Run, Don't Save");
|
|
btnRunNoSave.addActionListener(new ActionListener() {
|
|
|
|
@Override
|
|
public void actionPerformed(ActionEvent e) {
|
|
if (dontRedisplay.isSelected()) {
|
|
ExperimentalMode.autoSavePromptEnabled = !dontRedisplay
|
|
.isSelected();
|
|
ExperimentalMode.defaultAutoSaveEnabled = false;
|
|
dmode.savePreferences();
|
|
}
|
|
autoSaveDialog.dispose();
|
|
}
|
|
});
|
|
panelButtons.add(btnRunNoSave);
|
|
panelMain.add(panelButtons);
|
|
|
|
JPanel panelCheck = new JPanel();
|
|
panelCheck
|
|
.setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0));
|
|
panelCheck.add(dontRedisplay);
|
|
panelMain.add(panelCheck);
|
|
|
|
container.add(panelMain);
|
|
|
|
autoSaveDialog.setResizable(false);
|
|
autoSaveDialog.pack();
|
|
autoSaveDialog
|
|
.setLocationRelativeTo(base.getActiveEditor());
|
|
autoSaveDialog.setVisible(true);
|
|
|
|
} else if (ExperimentalMode.defaultAutoSaveEnabled)
|
|
handleSave(true);
|
|
}
|
|
} catch (Exception e) {
|
|
statusError(e);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Access variable inspector window.
|
|
*
|
|
* @return the variable inspector object
|
|
*/
|
|
public VariableInspector variableInspector() {
|
|
return vi;
|
|
}
|
|
|
|
public DebugToolbar toolbar() {
|
|
if(toolbar instanceof DebugToolbar)
|
|
return (DebugToolbar) toolbar;
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Show the variable inspector window.
|
|
*/
|
|
public void showVariableInspector() {
|
|
vi.setVisible(true);
|
|
}
|
|
|
|
/**
|
|
* Set visibility of the variable inspector window.
|
|
*
|
|
* @param visible true to set the variable inspector visible, false for
|
|
* invisible.
|
|
*/
|
|
public void showVariableInspector(boolean visible) {
|
|
vi.setVisible(visible);
|
|
}
|
|
|
|
/**
|
|
* Hide the variable inspector window.
|
|
*/
|
|
public void hideVariableInspector() {
|
|
vi.setVisible(true);
|
|
}
|
|
|
|
/**
|
|
* Toggle visibility of the variable inspector window.
|
|
*/
|
|
public void toggleVariableInspector() {
|
|
vi.setFocusableWindowState(false); // to not get focus when set visible
|
|
vi.setVisible(!vi.isVisible());
|
|
vi.setFocusableWindowState(true); // allow to get focus again
|
|
}
|
|
|
|
/**
|
|
* Text area factory method. Instantiates the customized TextArea.
|
|
*
|
|
* @return the customized text area object
|
|
*/
|
|
@Override
|
|
protected JEditTextArea createTextArea() {
|
|
//System.out.println("overriding creation of text area");
|
|
return new TextArea(new PdeTextAreaDefaults(mode), this);
|
|
}
|
|
|
|
/**
|
|
* Set the line to highlight as currently suspended at. Will override the
|
|
* breakpoint color, if set. Switches to the appropriate tab and scroll to
|
|
* the line by placing the cursor there.
|
|
*
|
|
* @param line the line to highlight as current suspended line
|
|
*/
|
|
public void setCurrentLine(LineID line) {
|
|
clearCurrentLine();
|
|
if (line == null) {
|
|
return; // safety, e.g. when no line mapping is found and the null line is used.
|
|
}
|
|
switchToTab(line.fileName());
|
|
// scroll to line, by setting the cursor
|
|
cursorToLineStart(line.lineIdx());
|
|
// highlight line
|
|
currentLine = new LineHighlight(line.lineIdx(), currentLineColor, this);
|
|
currentLine.setMarker(ta.currentLineMarker, currentLineMarkerColor);
|
|
currentLine.setPriority(10); // fixes current line being hidden by the breakpoint when moved down
|
|
}
|
|
|
|
/**
|
|
* Clear the highlight for the debuggers current line.
|
|
*/
|
|
public void clearCurrentLine() {
|
|
if (currentLine != null) {
|
|
currentLine.clear();
|
|
currentLine.dispose();
|
|
|
|
// revert to breakpoint color if any is set on this line
|
|
for (LineHighlight hl : breakpointedLines) {
|
|
if (hl.lineID().equals(currentLine.lineID())) {
|
|
hl.paint();
|
|
break;
|
|
}
|
|
}
|
|
currentLine = null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add highlight for a breakpointed line.
|
|
*
|
|
* @param lineID the line id to highlight as breakpointed
|
|
*/
|
|
public void addBreakpointedLine(LineID lineID) {
|
|
LineHighlight hl = new LineHighlight(lineID, breakpointColor, this);
|
|
hl.setMarker(ta.breakpointMarker, breakpointMarkerColor);
|
|
breakpointedLines.add(hl);
|
|
// repaint current line if it's on this line
|
|
if (currentLine != null && currentLine.lineID().equals(lineID)) {
|
|
currentLine.paint();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add highlight for a breakpointed line on the current tab.
|
|
*
|
|
* @param lineIdx the line index on the current tab to highlight as
|
|
* breakpointed
|
|
*/
|
|
//TODO: remove and replace by {@link #addBreakpointedLine(LineID lineID)}
|
|
public void addBreakpointedLine(int lineIdx) {
|
|
addBreakpointedLine(getLineIDInCurrentTab(lineIdx));
|
|
}
|
|
|
|
/**
|
|
* Remove a highlight for a breakpointed line. Needs to be on the current
|
|
* tab.
|
|
*
|
|
* @param lineIdx the line index on the current tab to remove a breakpoint
|
|
* highlight from
|
|
*/
|
|
public void removeBreakpointedLine(int lineIdx) {
|
|
LineID line = getLineIDInCurrentTab(lineIdx);
|
|
//System.out.println("line id: " + line.fileName() + " " + line.lineIdx());
|
|
LineHighlight foundLine = null;
|
|
for (LineHighlight hl : breakpointedLines) {
|
|
if (hl.lineID.equals(line)) {
|
|
foundLine = hl;
|
|
break;
|
|
}
|
|
}
|
|
if (foundLine != null) {
|
|
foundLine.clear();
|
|
breakpointedLines.remove(foundLine);
|
|
foundLine.dispose();
|
|
// repaint current line if it's on this line
|
|
if (currentLine != null && currentLine.lineID().equals(line)) {
|
|
currentLine.paint();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Remove all highlights for breakpointed lines.
|
|
*/
|
|
public void clearBreakpointedLines() {
|
|
for (LineHighlight hl : breakpointedLines) {
|
|
hl.clear();
|
|
hl.dispose();
|
|
}
|
|
breakpointedLines.clear(); // remove all breakpoints
|
|
// fix highlights not being removed when tab names have changed due to opening a new sketch in same editor
|
|
ta.clearLineBgColors(); // force clear all highlights
|
|
ta.clearGutterText();
|
|
|
|
// repaint current line
|
|
if (currentLine != null) {
|
|
currentLine.paint();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retrieve a {@link LineID} object for a line on the current tab.
|
|
*
|
|
* @param lineIdx the line index on the current tab
|
|
* @return the {@link LineID} object representing a line index on the
|
|
* current tab
|
|
*/
|
|
public LineID getLineIDInCurrentTab(int lineIdx) {
|
|
return new LineID(getSketch().getCurrentCode().getFileName(), lineIdx);
|
|
}
|
|
|
|
/**
|
|
* Retrieve line of sketch where the cursor currently resides.
|
|
*
|
|
* @return the current {@link LineID}
|
|
*/
|
|
protected LineID getCurrentLineID() {
|
|
String tab = getSketch().getCurrentCode().getFileName();
|
|
int lineNo = getTextArea().getCaretLine();
|
|
return new LineID(tab, lineNo);
|
|
}
|
|
|
|
/**
|
|
* Check whether a {@link LineID} is on the current tab.
|
|
*
|
|
* @param line the {@link LineID}
|
|
* @return true, if the {@link LineID} is on the current tab.
|
|
*/
|
|
public boolean isInCurrentTab(LineID line) {
|
|
return line.fileName().equals(getSketch().getCurrentCode().getFileName());
|
|
}
|
|
|
|
/**
|
|
* Event handler called when switching between tabs. Loads all line
|
|
* background colors set for the tab.
|
|
*
|
|
* @param code tab to switch to
|
|
*/
|
|
@Override
|
|
protected void setCode(SketchCode code) {
|
|
//System.out.println("tab switch: " + code.getFileName());
|
|
super.setCode(code); // set the new document in the textarea, etc. need to do this first
|
|
|
|
// set line background colors for tab
|
|
if (ta != null) { // can be null when setCode is called the first time (in constructor)
|
|
// clear all line backgrounds
|
|
ta.clearLineBgColors();
|
|
// clear all gutter text
|
|
ta.clearGutterText();
|
|
// load appropriate line backgrounds for tab
|
|
// first paint breakpoints
|
|
for (LineHighlight hl : breakpointedLines) {
|
|
if (isInCurrentTab(hl.lineID())) {
|
|
hl.paint();
|
|
}
|
|
}
|
|
// now paint current line (if any)
|
|
if (currentLine != null) {
|
|
if (isInCurrentTab(currentLine.lineID())) {
|
|
currentLine.paint();
|
|
}
|
|
}
|
|
}
|
|
if (dbg() != null && dbg().isStarted()) {
|
|
dbg().startTrackingLineChanges();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get a tab by its file name.
|
|
*
|
|
* @param fileName the filename to search for.
|
|
* @return the {@link SketchCode} object representing the tab, or null if
|
|
* not found
|
|
*/
|
|
public SketchCode getTab(String fileName) {
|
|
Sketch s = getSketch();
|
|
for (SketchCode c : s.getCode()) {
|
|
if (c.getFileName().equals(fileName)) {
|
|
return c;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Retrieve the current tab.
|
|
*
|
|
* @return the {@link SketchCode} representing the current tab
|
|
*/
|
|
public SketchCode getCurrentTab() {
|
|
return getSketch().getCurrentCode();
|
|
}
|
|
|
|
/**
|
|
* Access the currently edited document.
|
|
*
|
|
* @return the document object
|
|
*/
|
|
public Document currentDocument() {
|
|
//return ta.getDocument();
|
|
return getCurrentTab().getDocument();
|
|
}
|
|
|
|
/**
|
|
* Factory method for the editor toolbar. Instantiates the customized
|
|
* toolbar.
|
|
*
|
|
* @return the toolbar
|
|
*/
|
|
/*@Override
|
|
public EditorToolbar createToolbar() {
|
|
return new DebugToolbar(this, base);
|
|
}*/
|
|
|
|
/**
|
|
* Event Handler for double clicking in the left hand gutter area.
|
|
*
|
|
* @param lineIdx the line (0-based) that was double clicked
|
|
*/
|
|
public void gutterDblClicked(int lineIdx) {
|
|
if (dbg != null) {
|
|
dbg.toggleBreakpoint(lineIdx);
|
|
}
|
|
}
|
|
|
|
public void statusBusy() {
|
|
statusNotice("Debugger busy...");
|
|
}
|
|
|
|
public void statusHalted() {
|
|
statusNotice("Debugger halted.");
|
|
}
|
|
|
|
public static final int STATUS_EMPTY = 100, STATUS_COMPILER_ERR = 200,
|
|
STATUS_WARNING = 300, STATUS_INFO = 400, STATUS_ERR = 500;
|
|
public int statusMessageType = STATUS_EMPTY;
|
|
public String statusMessage;
|
|
public void statusMessage(final String what, int type){
|
|
// Don't re-display the old message again
|
|
if(type != STATUS_EMPTY) {
|
|
if(what.equals(statusMessage) && type == statusMessageType) {
|
|
return;
|
|
}
|
|
}
|
|
statusMessage = new String(what);
|
|
statusMessageType = type;
|
|
switch (type) {
|
|
case STATUS_COMPILER_ERR:
|
|
case STATUS_ERR:
|
|
super.statusError(what);
|
|
break;
|
|
case STATUS_INFO:
|
|
case STATUS_WARNING:
|
|
statusNotice(what);
|
|
break;
|
|
}
|
|
// Don't need to clear compiler error messages
|
|
if(type == STATUS_COMPILER_ERR) return;
|
|
|
|
// Clear the message after a delay
|
|
SwingWorker<Object, Object> s = new SwingWorker<Object, Object>() {
|
|
@Override
|
|
protected Object doInBackground() throws Exception {
|
|
try {
|
|
Thread.sleep(2 * 1000);
|
|
} catch (InterruptedException e) {
|
|
e.printStackTrace();
|
|
}
|
|
statusEmpty();
|
|
return null;
|
|
}
|
|
};
|
|
s.execute();
|
|
}
|
|
|
|
public void statusEmpty(){
|
|
statusMessage = null;
|
|
statusMessageType = STATUS_EMPTY;
|
|
super.statusEmpty();
|
|
}
|
|
|
|
ErrorCheckerService errorCheckerService;
|
|
|
|
/**
|
|
* Initializes and starts Error Checker Service
|
|
*/
|
|
private void initializeErrorChecker() {
|
|
Thread errorCheckerThread = null;
|
|
|
|
if (errorCheckerThread == null) {
|
|
errorCheckerService = new ErrorCheckerService(this);
|
|
errorCheckerThread = new Thread(errorCheckerService);
|
|
try {
|
|
errorCheckerThread.start();
|
|
} catch (Exception e) {
|
|
System.err
|
|
.println("Error Checker Service not initialized [XQEditor]: "
|
|
+ e);
|
|
// e.printStackTrace();
|
|
}
|
|
// System.out.println("Error Checker Service initialized.");
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Updates the error bar
|
|
* @param problems
|
|
*/
|
|
public void updateErrorBar(ArrayList<Problem> problems) {
|
|
errorBar.updateErrorPoints(problems);
|
|
}
|
|
|
|
/**
|
|
* Toggle between Console and Errors List
|
|
*
|
|
* @param buttonName
|
|
* - Button Label
|
|
*/
|
|
public void showProblemListView(String buttonName) {
|
|
CardLayout cl = (CardLayout) consoleProblemsPane.getLayout();
|
|
cl.show(consoleProblemsPane, buttonName);
|
|
}
|
|
|
|
/**
|
|
* Updates the error table
|
|
* @param tableModel
|
|
* @return
|
|
*/
|
|
synchronized public boolean updateTable(final TableModel tableModel) {
|
|
return errorTable.updateTable(tableModel);
|
|
}
|
|
|
|
/**
|
|
* Handle whether the tiny red error indicator is shown near the error button
|
|
* at the bottom of the PDE
|
|
*/
|
|
public void updateErrorToggle(){
|
|
btnShowErrors.updateMarker(ExperimentalMode.errorCheckEnabled
|
|
&& errorCheckerService.hasErrors(), errorBar.errorColor);
|
|
}
|
|
|
|
/**
|
|
* Handle refactor operation
|
|
*/
|
|
private void handleRefactor() {
|
|
log("Caret at:");
|
|
log(ta.getLineText(ta.getCaretLine()));
|
|
errorCheckerService.getASTGenerator().handleRefactor();
|
|
}
|
|
|
|
/**
|
|
* Handle show usage operation
|
|
*/
|
|
private void handleShowUsage() {
|
|
log("Caret at:");
|
|
log(ta.getLineText(ta.getCaretLine()));
|
|
errorCheckerService.getASTGenerator().handleShowUsage();
|
|
}
|
|
|
|
/**
|
|
* Checks if the sketch contains java tabs. If it does, the editor ain't built
|
|
* for it, yet. Also, user should really start looking at more powerful IDEs
|
|
* likeEclipse. Disable compilation check and some more features.
|
|
*/
|
|
private void checkForJavaTabs() {
|
|
hasJavaTabs = false;
|
|
for (int i = 0; i < this.getSketch().getCodeCount(); i++) {
|
|
if (this.getSketch().getCode(i).getExtension().equals("java")) {
|
|
compilationCheckEnabled = false;
|
|
hasJavaTabs = true;
|
|
JOptionPane.showMessageDialog(new Frame(), this
|
|
.getSketch().getName()
|
|
+ " contains .java tabs. Some editor features are not supported " +
|
|
"for .java tabs and will be disabled.");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected void applyPreferences() {
|
|
super.applyPreferences();
|
|
if (dmode != null) {
|
|
dmode.loadPreferences();
|
|
log("Applying prefs");
|
|
// trigger it once to refresh UI
|
|
errorCheckerService.runManualErrorCheck();
|
|
}
|
|
}
|
|
|
|
// TweakMode code
|
|
/**
|
|
* Show warnings menu item
|
|
*/
|
|
//protected JCheckBoxMenuItem enableTweakCB;
|
|
|
|
public static final String prefTweakPort = "tweak.port";
|
|
public static final String prefTweakShowCode = "tweak.showcode";
|
|
|
|
String[] baseCode;
|
|
|
|
final static int SPACE_AMOUNT = 0;
|
|
|
|
UDPTweakClient tweakClient;
|
|
|
|
public void startInteractiveMode()
|
|
{
|
|
ta.startInteractiveMode();
|
|
}
|
|
|
|
//public void stopInteractiveMode(ArrayList<Handle> handles[]) {
|
|
public void stopInteractiveMode(List<List<Handle>> handles) {
|
|
tweakClient.shutdown();
|
|
ta.stopInteractiveMode();
|
|
|
|
// remove space from the code (before and after)
|
|
removeSpacesFromCode();
|
|
|
|
// check which tabs were modified
|
|
boolean modified = false;
|
|
boolean[] modifiedTabs = getModifiedTabs(handles);
|
|
for (boolean mod : modifiedTabs) {
|
|
if (mod) {
|
|
modified = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (modified) {
|
|
// ask to keep the values
|
|
int ret = Base.showYesNoQuestion(this, "Tweak Mode",
|
|
"Keep the changes?",
|
|
"You changed some values in your sketch. Would you like to keep the changes?");
|
|
if (ret == 1) {
|
|
// NO! don't keep changes
|
|
loadSavedCode();
|
|
// update the painter to draw the saved (old) code
|
|
ta.invalidate();
|
|
}
|
|
else {
|
|
// YES! keep changes
|
|
// the new values are already present, just make sure the user can save the modified tabs
|
|
for (int i=0; i<sketch.getCodeCount(); i++) {
|
|
if (modifiedTabs[i]) {
|
|
sketch.getCode(i).setModified(true);
|
|
}
|
|
else {
|
|
// load the saved code of tabs that didn't change
|
|
// (there might be formatting changes that should not be saved)
|
|
sketch.getCode(i).setProgram(sketch.getCode(i).getSavedProgram());
|
|
/* Wild Hack: set document to null so the text editor will refresh
|
|
the program contents when the document tab is being clicked */
|
|
sketch.getCode(i).setDocument(null);
|
|
|
|
if (i == sketch.getCurrentCodeIndex()) {
|
|
// this will update the current code
|
|
setCode(sketch.getCurrentCode());
|
|
}
|
|
}
|
|
}
|
|
|
|
// save the sketch
|
|
try {
|
|
sketch.save();
|
|
}
|
|
catch (IOException e) {
|
|
Base.showWarning("Tweak Mode", "Could not save the modified sketch!", e);
|
|
}
|
|
|
|
// repaint the editor header (show the modified tabs)
|
|
header.repaint();
|
|
ta.invalidate();
|
|
}
|
|
}
|
|
else {
|
|
// number values were not modified but we need to load the saved code
|
|
// because of some formatting changes
|
|
loadSavedCode();
|
|
ta.invalidate();
|
|
}
|
|
}
|
|
|
|
public void updateInterface(List<List<Handle>> handles, List<List<ColorControlBox>> colorBoxes) {
|
|
// set OSC port of handles
|
|
// for (int i=0; i<handles.length; i++) {
|
|
// for (Handle h : handles[i]) {
|
|
// h.setOscPort(oscPort);
|
|
// }
|
|
// }
|
|
|
|
ta.updateInterface(handles, colorBoxes);
|
|
}
|
|
|
|
|
|
/**
|
|
* Deactivate run button
|
|
* Do this because when Mode.handleRun returns null the play button stays on.
|
|
*/
|
|
public void deactivateRun() {
|
|
// toolbar.deactivate(TweakToolbar.RUN);
|
|
if(toolbar instanceof DebugToolbar){
|
|
toolbar.deactivate(DebugToolbar.RUN);
|
|
} else {
|
|
super.deactivateRun();
|
|
}
|
|
}
|
|
|
|
|
|
//private boolean[] getModifiedTabs(ArrayList<Handle> handles[]) {
|
|
private boolean[] getModifiedTabs(List<List<Handle>> handles) {
|
|
boolean[] modifiedTabs = new boolean[handles.size()];
|
|
|
|
for (int i = 0; i < handles.size(); i++) {
|
|
for (Handle h : handles.get(i)) {
|
|
if (h.valueChanged()) {
|
|
modifiedTabs[i] = true;
|
|
}
|
|
}
|
|
}
|
|
return modifiedTabs;
|
|
}
|
|
|
|
|
|
public void initBaseCode() {
|
|
SketchCode[] code = sketch.getCode();
|
|
|
|
String space = new String();
|
|
|
|
for (int i=0; i<SPACE_AMOUNT; i++) {
|
|
space += "\n";
|
|
}
|
|
|
|
baseCode = new String[code.length];
|
|
for (int i = 0; i < code.length; i++) {
|
|
baseCode[i] = new String(code[i].getSavedProgram());
|
|
baseCode[i] = space + baseCode[i] + space;
|
|
}
|
|
}
|
|
|
|
|
|
public void initEditorCode(List<List<Handle>> handles, boolean withSpaces)
|
|
{
|
|
SketchCode[] sketchCode = sketch.getCode();
|
|
for (int tab=0; tab<baseCode.length; tab++) {
|
|
// beautify the numbers
|
|
int charInc = 0;
|
|
String code = baseCode[tab];
|
|
|
|
for (Handle n : handles.get(tab)) {
|
|
int s = n.startChar + charInc;
|
|
int e = n.endChar + charInc;
|
|
String newStr = n.strNewValue;
|
|
if (withSpaces) {
|
|
newStr = " " + newStr + " ";
|
|
}
|
|
code = replaceString(code, s, e, newStr);
|
|
n.newStartChar = n.startChar + charInc;
|
|
charInc += n.strNewValue.length() - n.strValue.length();
|
|
if (withSpaces) {
|
|
charInc += 4;
|
|
}
|
|
n.newEndChar = n.endChar + charInc;
|
|
}
|
|
|
|
sketchCode[tab].setProgram(code);
|
|
/* Wild Hack: set document to null so the text editor will refresh
|
|
the program contents when the document tab is being clicked */
|
|
sketchCode[tab].setDocument(null);
|
|
}
|
|
|
|
// this will update the current code
|
|
setCode(sketch.getCurrentCode());
|
|
}
|
|
|
|
private void loadSavedCode()
|
|
{
|
|
SketchCode[] code = sketch.getCode();
|
|
for (int i=0; i<code.length; i++) {
|
|
if (!code[i].getProgram().equals(code[i].getSavedProgram())) {
|
|
code[i].setProgram(code[i].getSavedProgram());
|
|
/* Wild Hack: set document to null so the text editor will refresh
|
|
the program contents when the document tab is being clicked */
|
|
code[i].setDocument(null);
|
|
}
|
|
}
|
|
|
|
// this will update the current code
|
|
setCode(sketch.getCurrentCode());
|
|
}
|
|
|
|
private void removeSpacesFromCode()
|
|
{
|
|
SketchCode[] code = sketch.getCode();
|
|
for (int i=0; i<code.length; i++) {
|
|
String c = code[i].getProgram();
|
|
c = c.substring(SPACE_AMOUNT, c.length() - SPACE_AMOUNT);
|
|
code[i].setProgram(c);
|
|
/* Wild Hack: set document to null so the text editor will refresh
|
|
the program contents when the document tab is being clicked */
|
|
code[i].setDocument(null);
|
|
}
|
|
// this will update the current code
|
|
setCode(sketch.getCurrentCode());
|
|
}
|
|
|
|
/**
|
|
* Replace all numbers with variables and add code to initialize these variables and handle update messages.
|
|
* @param sketch
|
|
* the sketch to work on
|
|
* @param handles
|
|
* list of numbers to replace in this sketch
|
|
* @return
|
|
* true on success
|
|
*/
|
|
//public boolean automateSketch(Sketch sketch, ArrayList<Handle> handles[])
|
|
public boolean automateSketch(Sketch sketch, List<List<Handle>> handles) {
|
|
SketchCode[] code = sketch.getCode();
|
|
|
|
if (code.length<1)
|
|
return false;
|
|
|
|
if (handles.size() == 0)
|
|
return false;
|
|
|
|
int setupStartPos = SketchParser.getSetupStart(baseCode[0]);
|
|
if (setupStartPos < 0) {
|
|
return false;
|
|
}
|
|
|
|
// get port number from preferences.txt
|
|
int port;
|
|
String portStr = Preferences.get(prefTweakPort);
|
|
if (portStr == null) {
|
|
Preferences.set(prefTweakPort, "auto");
|
|
portStr = "auto";
|
|
}
|
|
|
|
if (portStr.equals("auto")) {
|
|
// random port for udp (0xc000 - 0xffff)
|
|
port = (int)(Math.random()*0x3fff) + 0xc000;
|
|
}
|
|
else {
|
|
port = Preferences.getInteger(prefTweakPort);
|
|
}
|
|
|
|
/* create the client that will send the new values to the sketch */
|
|
tweakClient = new UDPTweakClient(port);
|
|
// update handles with a reference to the client object
|
|
for (int tab=0; tab<code.length; tab++) {
|
|
for (Handle h : handles.get(tab)) {
|
|
h.setTweakClient(tweakClient);
|
|
}
|
|
}
|
|
|
|
|
|
// Copy current program to interactive program
|
|
|
|
/* modify the code below, replace all numbers with their variable names */
|
|
// loop through all tabs in the current sketch
|
|
for (int tab=0; tab<code.length; tab++)
|
|
{
|
|
int charInc = 0;
|
|
String c = baseCode[tab];
|
|
for (Handle n : handles.get(tab))
|
|
{
|
|
// replace number value with a variable
|
|
c = replaceString(c, n.startChar + charInc, n.endChar + charInc, n.name);
|
|
charInc += n.name.length() - n.strValue.length();
|
|
}
|
|
code[tab].setProgram(c);
|
|
}
|
|
|
|
/* add the main header to the code in the first tab */
|
|
String c = code[0].getProgram();
|
|
|
|
// header contains variable declaration, initialization, and OSC listener function
|
|
String header;
|
|
header = "\n\n" +
|
|
"/*************************/\n" +
|
|
"/* MODIFIED BY TWEAKMODE */\n" +
|
|
"/*************************/\n" +
|
|
"\n\n";
|
|
|
|
// add needed OSC imports and the global OSC object
|
|
header += "import java.net.*;\n";
|
|
header += "import java.io.*;\n";
|
|
header += "import java.nio.*;\n\n";
|
|
|
|
// write a declaration for int and float arrays
|
|
int numOfInts = howManyInts(handles);
|
|
int numOfFloats = howManyFloats(handles);
|
|
if (numOfInts > 0) {
|
|
header += "int[] tweakmode_int = new int["+numOfInts+"];\n";
|
|
}
|
|
if (numOfFloats > 0) {
|
|
header += "float[] tweakmode_float = new float["+numOfFloats+"];\n\n";
|
|
}
|
|
|
|
/* add the server code that will receive the value change messages */
|
|
header += UDPTweakClient.getServerCode(port, numOfInts>0, numOfFloats>0);
|
|
header += "TweakModeServer tweakmode_Server;\n";
|
|
|
|
|
|
header += "void tweakmode_initAllVars() {\n";
|
|
//for (int i=0; i<handles.length; i++) {
|
|
for (List<Handle> list : handles) {
|
|
//for (Handle n : handles[i]) {
|
|
for (Handle n : list) {
|
|
header += " " + n.name + " = " + n.strValue + ";\n";
|
|
}
|
|
}
|
|
header += "}\n\n";
|
|
header += "void tweakmode_initCommunication() {\n";
|
|
header += " tweakmode_Server = new TweakModeServer();\n";
|
|
header += " tweakmode_Server.setup();\n";
|
|
header += " tweakmode_Server.start();\n";
|
|
header += "}\n";
|
|
|
|
header += "\n\n\n\n\n";
|
|
|
|
// add call to our initAllVars and initOSC functions from the setup() function.
|
|
String addToSetup = "\n"+
|
|
" tweakmode_initAllVars();\n"+
|
|
" tweakmode_initCommunication();\n\n";
|
|
|
|
setupStartPos = SketchParser.getSetupStart(c);
|
|
c = replaceString(c, setupStartPos, setupStartPos, addToSetup);
|
|
|
|
code[0].setProgram(header + c);
|
|
|
|
/* print out modified code */
|
|
String showModCode = Preferences.get(prefTweakShowCode);
|
|
if (showModCode == null) {
|
|
Preferences.setBoolean(prefTweakShowCode, false);
|
|
}
|
|
|
|
if (Preferences.getBoolean(prefTweakShowCode)) {
|
|
System.out.println("\nTweakMode modified code:\n");
|
|
for (int i=0; i<code.length; i++)
|
|
{
|
|
System.out.println("tab " + i + "\n");
|
|
System.out.println("=======================================================\n");
|
|
System.out.println(code[i].getProgram());
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private String replaceString(String str, int start, int end, String put) {
|
|
return str.substring(0, start) + put + str.substring(end, str.length());
|
|
}
|
|
|
|
//private int howManyInts(ArrayList<Handle> handles[])
|
|
private int howManyInts(List<List<Handle>> handles) {
|
|
int count = 0;
|
|
//for (int i=0; i<handles.length; i++) {
|
|
for (List<Handle> list : handles) {
|
|
//for (Handle n : handles[i]) {
|
|
for (Handle n : list) {
|
|
if (n.type == "int" || n.type == "hex" || n.type == "webcolor") {
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
//private int howManyFloats(ArrayList<Handle> handles[])
|
|
private int howManyFloats(List<List<Handle>> handles) {
|
|
int count = 0;
|
|
//for (int i=0; i<handles.length; i++) {
|
|
for (List<Handle> list : handles) {
|
|
//for (Handle n : handles[i]) {
|
|
for (Handle n : list) {
|
|
if (n.type == "float") {
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
}
|