diff --git a/src/net/apocalypselabs/symat/CodeRunner.java b/src/net/apocalypselabs/symat/CodeRunner.java index c5a6f02..2725811 100644 --- a/src/net/apocalypselabs/symat/CodeRunner.java +++ b/src/net/apocalypselabs/symat/CodeRunner.java @@ -61,6 +61,8 @@ import javax.swing.JOptionPane; public class CodeRunner { private ScriptEngine se; + private StringWriter sw = new StringWriter(); + private PrintWriter pw = new PrintWriter(sw); // If we need to wrap code around input to make everything nice. private boolean wrapRequired = false; @@ -82,12 +84,6 @@ public class CodeRunner { case "js": case "rhino": se = new ScriptEngineManager().getEngineByName("rhino"); -// Context ctx = Context.getCurrentContext(); -// if (ctx == null) { -// ctx = Context.enter(); -// } - //ctx.setWrapFactory(new JsWrapFactory()); - //ctx.getWrapFactory().setJavaPrimitiveWrap(false); wrapRequired = true; try { // Add custom functions. @@ -97,6 +93,7 @@ public class CodeRunner { + getFunctions("js")); // Allow engine access from scripts. se.put("engine", se); + attachWriters(); } catch (Exception ex) { initError(ex); } @@ -113,6 +110,7 @@ public class CodeRunner { + getFunctions("py")); // Allow engine access from scripts. se.put("engine", se); + attachWriters(); } catch (Exception ex) { initError(ex); } @@ -126,6 +124,12 @@ public class CodeRunner { public CodeRunner(String lang, boolean shell) { this(lang); } + + public void attachWriters() { + se.getContext().setWriter(pw); + se.getContext().setErrorWriter(pw); + Debug.println("Attached writers."); + } /** * Inits the Python engine on application start. @@ -137,6 +141,20 @@ public class CodeRunner { se = new ScriptEngineManager().getEngineByName("python"); } } + + public StringWriter getStringWriter() { + return sw; + } + + public PrintWriter getPrintWriter() { + return pw; + } + + public String getBufferDump() { + String dump = sw.toString(); + sw.getBuffer().setLength(0); + return dump; + } private void initError(Exception ex) { JOptionPane.showMessageDialog(null, "Error: " @@ -146,16 +164,13 @@ public class CodeRunner { } /** - * Parse a String of JavaScript. + * Parse a String of code. * - * @param eval A String of JavaScript to evaluate. + * @param eval A String of code to evaluate. * @return the result. */ public Object evalString(String eval) { try { - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - se.getContext().setWriter(pw); Object res = se.eval(wrapMath(eval)); if (res == null) { res = ""; @@ -166,6 +181,22 @@ public class CodeRunner { return formatEx(ex); } } + + /** + * Parse code and use the default output writers. + * + * @param eval A String of code to evaluate. + */ + public void evalCode(String eval) { + try { + Object res = se.eval(wrapMath(eval)); + if (res == null) { + res = ""; + } + } catch (ScriptException ex) { + sw.append(formatEx(ex)); + } + } private String formatEx(ScriptException ex) { String err = ex.getMessage(); diff --git a/src/net/apocalypselabs/symat/Editor.form b/src/net/apocalypselabs/symat/Editor.form index 403606e..4781cb4 100644 --- a/src/net/apocalypselabs/symat/Editor.form +++ b/src/net/apocalypselabs/symat/Editor.form @@ -231,6 +231,15 @@ + + + + + + + + + diff --git a/src/net/apocalypselabs/symat/Editor.java b/src/net/apocalypselabs/symat/Editor.java index 4919965..4c94b8a 100644 --- a/src/net/apocalypselabs/symat/Editor.java +++ b/src/net/apocalypselabs/symat/Editor.java @@ -71,6 +71,10 @@ import org.fife.ui.rtextarea.RTextScrollPane; */ public class Editor extends javax.swing.JInternalFrame { + private CodeRunner cr; + private RunThread rt; + private UpdateOutput uo; + private final JFileChooser fc = new JFileChooser(); private boolean isSaved = false; private RSyntaxTextArea codeBox = new RSyntaxTextArea(); @@ -93,13 +97,16 @@ public class Editor extends javax.swing.JInternalFrame { FileFilter filter = new FileNameExtensionFilter("JavaScript (syjs, js)", "syjs", "js"); fc.setFileFilter(filter); fc.addChoosableFileFilter(filter); - filter = new FileNameExtensionFilter("Python (sypy, py)", "sypy", "py"); - fc.addChoosableFileFilter(filter); + fc.addChoosableFileFilter(new FileNameExtensionFilter( + "Python (sypy, py)", "sypy", "py")); + fc.addChoosableFileFilter(new FileNameExtensionFilter( + "Plain Text (txt, text)", "txt", "text")); int font_size = 12; try { font_size = Integer.valueOf(PrefStorage.getSetting("editfont")); } catch (Exception ex) { + Debug.printerr("Font size error!"); } codeBox.setFont(new Font(Font.MONOSPACED, Font.PLAIN, font_size)); outputBox.setFont(new Font(Font.MONOSPACED, Font.PLAIN, font_size)); @@ -247,6 +254,7 @@ public class Editor extends javax.swing.JInternalFrame { pasteBtn = new javax.swing.JMenuItem(); runMenu = new javax.swing.JMenu(); runCodeBtn = new javax.swing.JMenuItem(); + killButton = new javax.swing.JMenuItem(); codeLangMenu = new javax.swing.JMenu(); javascriptOption = new javax.swing.JRadioButtonMenuItem(); pythonOption = new javax.swing.JRadioButtonMenuItem(); @@ -491,6 +499,15 @@ public class Editor extends javax.swing.JInternalFrame { }); runMenu.add(runCodeBtn); + killButton.setText("Kill script"); + killButton.setEnabled(false); + killButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + killButtonActionPerformed(evt); + } + }); + runMenu.add(killButton); + codeLangMenu.setText("Language"); javascriptOption.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_J, java.awt.event.InputEvent.CTRL_MASK)); @@ -636,9 +653,11 @@ public class Editor extends javax.swing.JInternalFrame { private void runCodeBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_runCodeBtnActionPerformed if (javascriptOption.isSelected()) { - new RunThread("javascript").start(); + rt = new RunThread("javascript"); + rt.start(); } else if (pythonOption.isSelected()) { - new RunThread("python").start(); + rt = new RunThread("python"); + rt.start(); } }//GEN-LAST:event_runCodeBtnActionPerformed @@ -666,45 +685,88 @@ public class Editor extends javax.swing.JInternalFrame { execCode(lang); setRunning(false); } + } - public void setRunning(boolean isRunning) { - final boolean running = isRunning; - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - if (running) { - runMenu.setText("Running..."); - codeBox.setEnabled(false); - for (Component mu : jMenuBar1.getComponents()) { - mu.setEnabled(false); - } - jMenuBar1.setEnabled(false); - } else { - runMenu.setText("Run"); - codeBox.setEnabled(true); - for (Component mu : jMenuBar1.getComponents()) { - mu.setEnabled(true); - } - jMenuBar1.setEnabled(true); + private void setRunning(boolean isRunning) { + final boolean running = isRunning; + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + if (running) { + runMenu.setText("Running..."); + codeBox.setEditable(false); + for (Component mu : jMenuBar1.getComponents()) { + mu.setEnabled(false); + } + runMenu.setEnabled(true); + runCodeBtn.setEnabled(false); + codeLangMenu.setEnabled(false); + killButton.setEnabled(true); + } else { + runMenu.setText("Run"); + codeBox.setEditable(true); + for (Component mu : jMenuBar1.getComponents()) { + mu.setEnabled(true); } + runCodeBtn.setEnabled(true); + codeLangMenu.setEnabled(true); + killButton.setEnabled(false); } - }); - } + } + }); } private void execCode(String lang) { if (!checkRequiredVersion(codeBox.getText(), lang)) { return; } - CodeRunner cr = new CodeRunner(lang); + cr = new CodeRunner(lang); + uo = new UpdateOutput(cr); String script = loadExternalScripts(codeBox.getText(), lang); Debug.println(lang); Debug.println(script); - Object result = cr.evalString(script); - try { - outputBox.append(result.toString() + "\n"); - } catch (NullPointerException ex) { + uo.start(); + cr.evalCode(script); + uo.kill(); + } + + private class UpdateOutput extends Thread { + + private final CodeRunner cr; + private boolean keepGoing = true; + public void kill() { + keepGoing = false; + flush(); + } + + @Override + public void run() { + while (keepGoing) { + try { + flush(); + Thread.sleep(100); + } catch (Exception ex) { + + } + } + } + + private void flush() { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + String d = cr.getBufferDump(); + if (!d.equals("")) { + outputBox.append(d); + Debug.println(d); + } + } + }); + } + + public UpdateOutput(CodeRunner c) { + cr = c; } } @@ -855,6 +917,16 @@ public class Editor extends javax.swing.JInternalFrame { )); }//GEN-LAST:event_packPluginMenuActionPerformed + private void killButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_killButtonActionPerformed + uo.kill(); + rt.stop(); + setRunning(false); + outputBox.append("" + + "\n=============" + + "\nScript killed" + + "\n=============\n"); + }//GEN-LAST:event_killButtonActionPerformed + private void createShared(String id) { try { String padid = Pads.genPad(id, codeBox.getText()); @@ -945,6 +1017,7 @@ public class Editor extends javax.swing.JInternalFrame { private javax.swing.JPopupMenu.Separator jSeparator1; private javax.swing.JSplitPane jSplitPane1; private javax.swing.JRadioButtonMenuItem javascriptOption; + private javax.swing.JMenuItem killButton; private javax.swing.ButtonGroup langBtnGroup; private javax.swing.JMenuItem openMenu; private javax.swing.JMenu openSampleBtn; diff --git a/src/net/apocalypselabs/symat/Settings.form b/src/net/apocalypselabs/symat/Settings.form index 803f5d4..f674e64 100644 --- a/src/net/apocalypselabs/symat/Settings.form +++ b/src/net/apocalypselabs/symat/Settings.form @@ -1,6 +1,6 @@ -
+ @@ -13,13 +13,13 @@ - + - + - + @@ -44,21 +44,21 @@ + + + + + + + - - - - - - - - - + - - + + + @@ -71,22 +71,24 @@ - + + - - - - - - - - + + + + + + + + + - + @@ -236,7 +238,6 @@ - @@ -276,7 +277,7 @@ - + @@ -287,7 +288,7 @@ - + @@ -302,5 +303,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/net/apocalypselabs/symat/Settings.java b/src/net/apocalypselabs/symat/Settings.java index e069381..c28f7fd 100644 --- a/src/net/apocalypselabs/symat/Settings.java +++ b/src/net/apocalypselabs/symat/Settings.java @@ -56,6 +56,7 @@ public class Settings extends javax.swing.JInternalFrame { */ public Settings() { initComponents(); + jPanel5.setVisible(false); setBackground(Theme.windowColor()); } @@ -83,14 +84,16 @@ public class Settings extends javax.swing.JInternalFrame { jPanel4 = new javax.swing.JPanel(); jLabel1 = new javax.swing.JLabel(); nameBox = new javax.swing.JTextField(); + jPanel5 = new javax.swing.JPanel(); + textSize = new javax.swing.JSpinner(); setClosable(true); setIconifiable(true); setTitle("Settings"); setFrameIcon(new javax.swing.ImageIcon(getClass().getResource("/net/apocalypselabs/symat/icons/settings.png"))); // NOI18N - setMaximumSize(new java.awt.Dimension(390, 293)); - setMinimumSize(new java.awt.Dimension(390, 293)); - setPreferredSize(new java.awt.Dimension(390, 293)); + setMaximumSize(new java.awt.Dimension(524, 274)); + setMinimumSize(new java.awt.Dimension(524, 274)); + setPreferredSize(new java.awt.Dimension(524, 274)); addComponentListener(new java.awt.event.ComponentAdapter() { public void componentShown(java.awt.event.ComponentEvent evt) { formComponentShown(evt); @@ -192,8 +195,7 @@ public class Settings extends javax.swing.JInternalFrame { .addContainerGap() .addComponent(quickStart) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(skipUpdates) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addComponent(skipUpdates)) ); applyBtn.setText("Apply"); @@ -213,7 +215,7 @@ public class Settings extends javax.swing.JInternalFrame { jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel4Layout.createSequentialGroup() .addComponent(jLabel1) - .addGap(0, 0, Short.MAX_VALUE)) + .addGap(0, 96, Short.MAX_VALUE)) .addComponent(nameBox) ); jPanel4Layout.setVerticalGroup( @@ -222,7 +224,28 @@ public class Settings extends javax.swing.JInternalFrame { .addComponent(jLabel1) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(nameBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(0, 0, Short.MAX_VALUE)) + .addContainerGap(13, Short.MAX_VALUE)) + ); + + jPanel5.setBorder(javax.swing.BorderFactory.createTitledBorder("Text size")); + + textSize.setModel(new javax.swing.SpinnerNumberModel(12, 8, 48, 2)); + + javax.swing.GroupLayout jPanel5Layout = new javax.swing.GroupLayout(jPanel5); + jPanel5.setLayout(jPanel5Layout); + jPanel5Layout.setHorizontalGroup( + jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel5Layout.createSequentialGroup() + .addContainerGap() + .addComponent(textSize) + .addContainerGap()) + ); + jPanel5Layout.setVerticalGroup( + jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel5Layout.createSequentialGroup() + .addContainerGap() + .addComponent(textSize, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); @@ -230,18 +253,19 @@ public class Settings extends javax.swing.JInternalFrame { layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(jPanel3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) - .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(jPanel3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addGap(0, 0, 0) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) - .addComponent(jPanel2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(jPanel4, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jPanel5, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addGroup(layout.createSequentialGroup() + .addComponent(jPanel4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(applyBtn) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(saveBtn))) @@ -252,17 +276,18 @@ public class Settings extends javax.swing.JInternalFrame { .addGroup(layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(jPanel5, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(jPanel2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) - .addComponent(jPanel3, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(jPanel4, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(applyBtn) - .addComponent(saveBtn)) - .addGap(40, 40, 40)) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(jPanel3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(jPanel4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(applyBtn) + .addComponent(saveBtn))) + .addGap(57, 57, 57)) ); pack(); @@ -288,6 +313,7 @@ public class Settings extends javax.swing.JInternalFrame { skipUpdates.setSelected( PrefStorage.getSetting("skipupdates", "").equals("yes")); nameBox.setText(PrefStorage.getSetting("author", "")); + textSize.setValue(Integer.valueOf(PrefStorage.getSetting("editfont"))); }//GEN-LAST:event_formComponentShown private void saveBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveBtnActionPerformed @@ -301,6 +327,7 @@ public class Settings extends javax.swing.JInternalFrame { PrefStorage.saveSetting("quickstart", quickStart.isSelected() ? "yes" : ""); PrefStorage.saveSetting("skipupdates", skipUpdates.isSelected() ? "yes" : ""); PrefStorage.saveSetting("author", nameBox.getText()); + PrefStorage.saveSetting("editfont", String.valueOf(textSize.getValue())); PrefStorage.save(); Main.updateDisplay(); } @@ -322,11 +349,13 @@ public class Settings extends javax.swing.JInternalFrame { private javax.swing.JPanel jPanel2; private javax.swing.JPanel jPanel3; private javax.swing.JPanel jPanel4; + private javax.swing.JPanel jPanel5; private javax.swing.JTextField nameBox; private javax.swing.JCheckBox quickStart; private javax.swing.JButton saveBtn; private javax.swing.JCheckBox showRecent; private javax.swing.JCheckBox skipUpdates; + private javax.swing.JSpinner textSize; private javax.swing.JRadioButton themeDark; private javax.swing.ButtonGroup themeGroup; private javax.swing.JRadioButton themeLight;