You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

443 lines
18 KiB
Java

/*
* Apocalypse Laboratories
* Open Source License
*
* Source code can be used for any purpose, as long as:
* - Compiled binaries are rebranded and trademarks are not
* visible by the end user at any time, except to give
* credit to Apocalypse Laboratories, such as by showing
* "Based on <product> by Apocalypse Laboratories" or a
* similar notice;
* - You do not use the code for evil;
* - Rebranded compiled applications have significant
* differences in functionality;
* - and you provide your modified source code for download,
* under the terms of the GNU LGPL v3 or a comparable
* license.
*
* Compiled binaries cannot be redistributed or mirrored,
* unless:
* - You have written permission from Apocalypse Laboratories;
* - Downloads are not available from Apocalypse Laboratories,
* not even behind a paywall or other blocking mechanism;
* - or you have received a multi-computer license, in which
* case you should take measures to prevent unauthorized
* downloads, such as preventing download access from the
* Internet.
*/
package net.apocalypselabs.symat;
import java.awt.Font;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.filechooser.FileFilter;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.text.AttributeSet;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.TabSet;
import javax.swing.text.TabStop;
/**
*
* @author Skylar
*/
public class CodeEditor extends javax.swing.JInternalFrame {
private final JFileChooser fc = new JFileChooser();
private boolean isSaved = false;
private final JTextPane codeBox = new JTextPane();
private final TextLineNumber tln;
private String lastSaved = "";
/**
* Creates new form CodeEditor
*/
public CodeEditor() {
initComponents();
FileFilter filter = new FileNameExtensionFilter("SyMAT script (.symt)", "symt");
fc.setFileFilter(filter);
fc.addChoosableFileFilter(filter);
filter = new FileNameExtensionFilter("JavaScript file (.js)", "js");
fc.addChoosableFileFilter(filter);
filter = new FileNameExtensionFilter("MeetLeeb script (.mls)", "mls");
fc.addChoosableFileFilter(filter);
int font_size = 12;
try {
font_size = Integer.valueOf(PrefStorage.getSetting("editor-fontsize"));
} catch (Exception ex) {
}
codeBox.setFont(new Font(Font.MONOSPACED, Font.PLAIN, font_size));
TabStop[] tabs = new TabStop[30];
for (int i = 0; i < tabs.length; i++) {
tabs[i] = new TabStop(15 * i, TabStop.ALIGN_RIGHT, TabStop.LEAD_NONE);
}
TabSet tabset = new TabSet(tabs);
StyleContext sc = StyleContext.getDefaultStyleContext();
AttributeSet aset = sc.addAttribute(SimpleAttributeSet.EMPTY,
StyleConstants.TabSet, tabset);
codeBox.setParagraphAttributes(aset, false);
tln = new TextLineNumber(codeBox);
scrollPane.setRowHeaderView(tln);
codeBox.setVisible(true);
tln.setVisible(true);
codeBox.requestFocus();
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
jMenuItem4 = new javax.swing.JMenuItem();
jMenuBar2 = new javax.swing.JMenuBar();
jMenu3 = new javax.swing.JMenu();
jMenu4 = new javax.swing.JMenu();
jPanel1 = new javax.swing.JPanel();
jSplitPane1 = new javax.swing.JSplitPane();
scrollPane = new JScrollPane(codeBox);
jPanel2 = new javax.swing.JPanel();
jLabel1 = new javax.swing.JLabel();
jScrollPane1 = new javax.swing.JScrollPane();
outputPane = new javax.swing.JTextArea();
jMenuBar1 = new javax.swing.JMenuBar();
jMenu1 = new javax.swing.JMenu();
jMenuItem1 = new javax.swing.JMenuItem();
jMenuItem2 = new javax.swing.JMenuItem();
jMenuItem3 = new javax.swing.JMenuItem();
jMenuItem8 = new javax.swing.JMenuItem();
jMenu5 = new javax.swing.JMenu();
jMenuItem6 = new javax.swing.JMenuItem();
jMenuItem7 = new javax.swing.JMenuItem();
jMenu2 = new javax.swing.JMenu();
jMenuItem5 = new javax.swing.JMenuItem();
jMenuItem4.setText("jMenuItem4");
jMenu3.setText("File");
jMenuBar2.add(jMenu3);
jMenu4.setText("Edit");
jMenuBar2.add(jMenu4);
setClosable(true);
setIconifiable(true);
setMaximizable(true);
setResizable(true);
setTitle("Editor");
setFrameIcon(new javax.swing.ImageIcon(getClass().getResource("/net/apocalypselabs/symat/icons/editor.png"))); // NOI18N
jSplitPane1.setDividerLocation(275);
jSplitPane1.setDividerSize(3);
jSplitPane1.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
jSplitPane1.setResizeWeight(0.7);
scrollPane.setRequestFocusEnabled(false);
jSplitPane1.setTopComponent(scrollPane);
jLabel1.setText("Output:");
outputPane.setEditable(false);
outputPane.setColumns(20);
outputPane.setRows(5);
jScrollPane1.setViewportView(outputPane);
javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2);
jPanel2.setLayout(jPanel2Layout);
jPanel2Layout.setHorizontalGroup(
jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel2Layout.createSequentialGroup()
.addComponent(jLabel1)
.addGap(0, 0, Short.MAX_VALUE))
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 524, Short.MAX_VALUE)
);
jPanel2Layout.setVerticalGroup(
jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel2Layout.createSequentialGroup()
.addComponent(jLabel1)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 72, Short.MAX_VALUE))
);
jSplitPane1.setRightComponent(jPanel2);
javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
jPanel1.setLayout(jPanel1Layout);
jPanel1Layout.setHorizontalGroup(
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jSplitPane1)
);
jPanel1Layout.setVerticalGroup(
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jSplitPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 359, Short.MAX_VALUE)
);
jMenu1.setText("File");
jMenuItem1.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_O, java.awt.event.InputEvent.CTRL_MASK));
jMenuItem1.setText("Open...");
jMenuItem1.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jMenuItem1ActionPerformed(evt);
}
});
jMenu1.add(jMenuItem1);
jMenuItem2.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_S, java.awt.event.InputEvent.CTRL_MASK));
jMenuItem2.setText("Save...");
jMenuItem2.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jMenuItem2ActionPerformed(evt);
}
});
jMenu1.add(jMenuItem2);
jMenuItem3.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_S, java.awt.event.InputEvent.SHIFT_MASK | java.awt.event.InputEvent.CTRL_MASK));
jMenuItem3.setText("Save as...");
jMenuItem3.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jMenuItem3ActionPerformed(evt);
}
});
jMenu1.add(jMenuItem3);
jMenuItem8.setText("Export...");
jMenuItem8.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jMenuItem8ActionPerformed(evt);
}
});
jMenu1.add(jMenuItem8);
jMenuBar1.add(jMenu1);
jMenu5.setText("Edit");
jMenuItem6.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_W, java.awt.event.InputEvent.CTRL_MASK));
jMenuItem6.setText("Clear output");
jMenuItem6.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jMenuItem6ActionPerformed(evt);
}
});
jMenu5.add(jMenuItem6);
jMenuItem7.setText("Font size...");
jMenuItem7.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jMenuItem7ActionPerformed(evt);
}
});
jMenu5.add(jMenuItem7);
jMenuBar1.add(jMenu5);
jMenu2.setText("Run");
jMenu2.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jMenu2ActionPerformed(evt);
}
});
jMenuItem5.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_F5, 0));
jMenuItem5.setText("Run code");
jMenuItem5.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jMenuItem5ActionPerformed(evt);
}
});
jMenu2.add(jMenuItem5);
jMenuBar1.add(jMenu2);
setJMenuBar(jMenuBar1);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
);
pack();
}// </editor-fold>//GEN-END:initComponents
private void jMenuItem1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jMenuItem1ActionPerformed
int r = fc.showOpenDialog(this);
if (r == JFileChooser.APPROVE_OPTION) {
try {
File f = fc.getSelectedFile();
codeBox.setText(readFile(f.toString(), StandardCharsets.UTF_8));
isSaved = true;
lastSaved = codeBox.getText();
setTitle("Editor - " + f.getName());
} catch (IOException ex) {
JOptionPane.showInternalMessageDialog(this,
"Error: Cannot load file: " + ex.getMessage());
}
}
codeBox.setCaretPosition(0);
}//GEN-LAST:event_jMenuItem1ActionPerformed
public void openFileFromString(String file) {
try {
File f = new File(file);
codeBox.setText(readFile(f.toString(), StandardCharsets.UTF_8));
isSaved = true;
lastSaved = codeBox.getText();
setTitle("Editor - " + f.getName());
} catch (IOException ex) {
JOptionPane.showInternalMessageDialog(this,
"Error: Cannot load file: " + ex.getMessage());
}
}
private void jMenuItem2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jMenuItem2ActionPerformed
if (!isSaved) {
int r = fc.showSaveDialog(this);
if (r == JFileChooser.APPROVE_OPTION) {
try {
saveFile(codeBox.getText(), addSaveExt(fc.getSelectedFile().toString()));
} catch (IOException ex) {
JOptionPane.showInternalMessageDialog(this, "Error: Cannot save file: " + ex.getMessage());
}
}
} else {
try {
saveFile(codeBox.getText(), addSaveExt(fc.getSelectedFile().toString()));
} catch (IOException ex) {
JOptionPane.showInternalMessageDialog(this, "Error: Cannot save file: " + ex.getMessage());
}
}
}//GEN-LAST:event_jMenuItem2ActionPerformed
private String addSaveExt(String path) {
if (!path.matches(".*\\.(js|mls|symt)")) {
path += ".symt";
}
return path;
}
private void jMenuItem3ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jMenuItem3ActionPerformed
isSaved = false; // Reset saved status, force dialog
jMenuItem2ActionPerformed(evt);
}//GEN-LAST:event_jMenuItem3ActionPerformed
private void jMenu2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jMenu2ActionPerformed
}//GEN-LAST:event_jMenu2ActionPerformed
private void jMenuItem5ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jMenuItem5ActionPerformed
CodeRunner cr = new CodeRunner();
Object result = cr.evalString(codeBox.getText());
try {
outputPane.append(result.toString() + "\n");
} catch (NullPointerException ex) {
}
}//GEN-LAST:event_jMenuItem5ActionPerformed
private void jMenuItem6ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jMenuItem6ActionPerformed
outputPane.setText("");
}//GEN-LAST:event_jMenuItem6ActionPerformed
private void jMenuItem7ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jMenuItem7ActionPerformed
try {
int size = Integer.parseInt(JOptionPane.showInternalInputDialog(this,
"New font size (8-36):",
"Font Size",
JOptionPane.QUESTION_MESSAGE));
if (size >= 8 && size <= 36) {
codeBox.setFont(new Font(Font.MONOSPACED, Font.PLAIN, size));
PrefStorage.saveSetting("editor-fontsize", size + "");
}
} catch (Exception ex) {
}
}//GEN-LAST:event_jMenuItem7ActionPerformed
private void jMenuItem8ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jMenuItem8ActionPerformed
CodeExport ce = new CodeExport(codeBox.getText());
MainGUI.loadFrame(ce);
}//GEN-LAST:event_jMenuItem8ActionPerformed
private void saveFile(String content, String path)
throws IOException {
try (PrintStream out = new PrintStream(new FileOutputStream(path))) {
out.print(content);
}
setTitle("Editor - " + (new File(path)).getName());
lastSaved = content;
isSaved = true;
}
@Override
public void doDefaultCloseAction() {
if (lastSaved.equals(codeBox.getText())) {
dispose();
} else {
int p = JOptionPane.showInternalConfirmDialog(this,
"Are you sure you want to exit without saving this file?",
"Unsaved Changes",
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE);
if (p == JOptionPane.YES_OPTION) {
dispose();
} else {
jMenuItem3ActionPerformed(null);
}
}
}
private static String readFile(String path, Charset encoding)
throws IOException {
byte[] encoded = Files.readAllBytes(Paths.get(path));
return new String(encoded, encoding);
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel jLabel1;
private javax.swing.JMenu jMenu1;
private javax.swing.JMenu jMenu2;
private javax.swing.JMenu jMenu3;
private javax.swing.JMenu jMenu4;
private javax.swing.JMenu jMenu5;
private javax.swing.JMenuBar jMenuBar1;
private javax.swing.JMenuBar jMenuBar2;
private javax.swing.JMenuItem jMenuItem1;
private javax.swing.JMenuItem jMenuItem2;
private javax.swing.JMenuItem jMenuItem3;
private javax.swing.JMenuItem jMenuItem4;
private javax.swing.JMenuItem jMenuItem5;
private javax.swing.JMenuItem jMenuItem6;
private javax.swing.JMenuItem jMenuItem7;
private javax.swing.JMenuItem jMenuItem8;
private javax.swing.JPanel jPanel1;
private javax.swing.JPanel jPanel2;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JSplitPane jSplitPane1;
private javax.swing.JTextArea outputPane;
private javax.swing.JScrollPane scrollPane;
// End of variables declaration//GEN-END:variables
}