Intro: The demo below will create an entry level code editor for running Processing source code from the command line using the processing-java.exe file. It was developed and tested mainly on MacOS(M2); I was unable to get it to run on other platforms due to difficulty finding either processing-java or processing-cli for the other operating systems. There are five Tabs with additional code which are also shown below. This project was undertaken for proof of concept and for those users who, for one reason or another, do not want to use the official Processing IDE. My presumption is that processing-java.exe from the command line will function even if the official Processing IDE is not on the userâs system; this may or may not be correct (I have no way to test since I donât want to give up my official Processing IDE and do not have a new computer to experiment with). The exported app is smaller in size than the official Processing IDE (4.4.10) at 254.3 MB (including embedded Java) compared to 660.6 MB for the official editor. Without Java embedded this appâs size falls to 13.8 MB and still runs on my system.
Dependency: The editor uses RSyntaxTextArea for syntax highlighting which may be downloaded here: https://sourceforge.net/projects/rsyntaxtextarea/files%2Frsyntaxtextarea%2F1.5.1%2Frsyntaxtextarea_1.5.1.zip/download . To install on your system, first create a folder in your Processing/libraries folder entitled âRSyntaxtTextAreaâ and inside of that create another folder entitled âlibraryâ. Place the downloaded 'rsyntaxtextarea.jarâ file in the âlibraryâ folder and then you should be able to use syntax highlighting. If you want to use code folding you will additionally need ârsyntaxtextarea-3.6.0.jarâ downloaded to your Processing/libraries/RSyntaxTextArea/library folder, available at: https://repo1.maven.org/maven2/com/fifesoft/rsyntaxtextarea/3.6.0/ . You can get by with only the earlier version if you donât care about code folding. Source code line number 412 is REMmed out to avoid an error message from not having the latter jar file available. If you choose to use code folding (after downloading and installing rsyntaxtextarea-3.6.0.jar) then just unREM that line of code (the editor was initially setup to include code folding).
Setup: 1) The first thing to do after installing RSyntaxTextArea is set the path to the processing-java.exe file. I found its location on my system by entering 'whereis processing-javaâ on the Terminal command line. It was at /usr/local/bin; I presume that is where the Processing IDE installed it after selecting 'Tools/Install âprocessing-javaâ on the menubar. There may be other ways to obtain this .exe file; I didnât have much luck running another copy from my home directory.
- The second thing to do is set the folder which you want JFileChooser to open each time you select File/Open. This is handy to keep from having to migrate to the desired folder each time you want to open another file; the chooser will automatically take you to that folder once set. Both settings are stored in a separate file in the sketch folder.
Design Features: 1) Each time you open a new file a new tab is created using the fileâs name, and a new instance of RSyntaxTextArea is embedded into the JTabbedPane. There is a menubar item to remove unwanted tabs (except for the first one which may not be removed). This makes it possible to copy/paste code from one tab to another.
-
Note that the folder path is used in the âsketch= string (not the full path of the .pde file).
-
On MacOS the menubar is placed at the top of the screen after the project is exported.
-
This demo editor uses only the ârun option in ProcessBuilder but others, including âexport, may also be used but were not fully tested.
-
There is no sketch âStopâ button on this app. To stop a running sketch simply hit the sketchâs window close button or hit cmd-Q with the sketch in focus.
Command line edition for Processing 4.4.10 (Java Mode):
--help Show this help text. Congratulations.
--sketch=<name> Specify the sketch folder (required)
--output=<name> Specify the output folder (optional and
cannot be the same as the sketch folder.)
--force The sketch will not build if the output
folder already exists, because the contents
will be replaced. This option erases the
folder first. Use with extreme caution!
--build Preprocess and compile a sketch into .class files.
--run Preprocess, compile, and run a sketch.
--present Preprocess, compile, and run a sketch in presentation mode.
--export Export an application.
--variant Specify the platform and architecture (Export only).
--no-java Do not embed Java.
Starting with 4.0, the --platform option has been removed because of the variety of platforms and architectures now available.
Use the --variant option instead, for instance:
variant platform
------------- ---------------------------
macos-x86_64 macOS (Intel 64-bit)
macos-aarch64 macOS (Apple Silicon)
windows-amd64 Windows (Intel 64-bit)
linux-amd64 Linux (Intel 64-bit)
linux-arm Linux (Raspberry Pi 32-bit)
linux-aarch64 Linux (Raspberry Pi 64-bit)
The --build, --run, --present, or --export must be the final parameter
passed to Processing. Arguments passed following one of those four will
be passed through to the sketch itself, and therefore available to the
sketch via the 'args' field. To pass options understood by PApplet.main(),
write a custom main() method so that the preprocessor does not add one.
Source code:
import java.awt.*;
import java.awt.event.*;
import java.awt.print.*;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.io.*;
import java.io.File;
import java.nio.file.Path;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import javax.swing.text.BadLocationException;
import static java.awt.event.InputEvent.META_DOWN_MASK;
import org.fife.ui.rsyntaxtextarea.*;
import org.fife.ui.rtextarea.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseListener;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.List;
ArrayList<String> files = new ArrayList<String>();
ArrayList<RSyntaxTextArea> txtAreas = new ArrayList<RSyntaxTextArea>();
JFrame frame;
JScrollPane txtScrlPane;
RSyntaxTextArea txtArea;
JTextArea logArea;
JTabbedPane tabbedPane;
JSplitPane splitPane;
JScrollPane logScrlPane;
JSpinner fontSize;
JTextField findField;
JTextField replaceField;
JLabel folderLabel;
JLabel cmdStrLabel;
final int _wndW = 750;
final int _wndH = 700;
String codeDir = "";
String cmdStrExe = "";
Process process = null;
File file;
void newFileAction() {
String folderPath = "";
String folderName = "";
String fileName = "";
String newFilePath = "";
JFileChooser fileChooser = new JFileChooser(codeDir);
fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
fileChooser.setDialogTitle("Create sketch folder as:");
int result = fileChooser.showSaveDialog(frame);
if (result == JFileChooser.APPROVE_OPTION) {
File newDir = fileChooser.getSelectedFile();
folderName = newDir.getName();
fileName = folderName + ".pde";
boolean created = newDir.mkdir();
logArea.append("Dir created:" + created + "\n");
Path folder = newDir.toPath();
folderPath = folder.toString();
newFilePath = folderPath + "/" + fileName;
frame.setTitle(fileName);
} else {
logArea.append("New file cancelled.");
}
txtArea();
tabbedPane.addTab(fileName, txtScrlPane);
int tabIndex = tabbedPane.indexOfTab(fileName);
tabbedPane.setSelectedIndex(tabIndex);
files.add(tabIndex, newFilePath);
txtAreas.add(tabIndex, txtArea);
}
String fileExtension(String fileName) {
int lastDotIndex = fileName.lastIndexOf('.');
if (lastDotIndex == -1 || lastDotIndex == 0) {
return ""; // No extension
}
return fileName.substring(lastDotIndex + 1);
}
void openAction() {
JFileChooser fileChooser = new JFileChooser(codeDir);
int i = fileChooser.showOpenDialog(fileChooser);
if (i == JFileChooser.APPROVE_OPTION) {
file = fileChooser.getSelectedFile();
String filePath = file.getPath();
if (!fileExtension(filePath).equals("pde")) {
Object[] options = {"CANCEL"};
JOptionPane.showOptionDialog(frame, "Please make another selection. \n Editor requires .py extension.", "Extension Not Supported.",
JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, null, options, options[0]);
return;
}
frame.setTitle(filePath);
txtArea();
tabbedPane.addTab(file.getName(), txtScrlPane);
int tabIndex = tabbedPane.indexOfTab(file.getName());
tabbedPane.setSelectedIndex(tabIndex);
files.add(tabIndex, filePath);
txtAreas.add(tabIndex, txtArea);
try {
BufferedReader buffer = new BufferedReader(new FileReader(filePath));
String s = "", s1 = "";
while ((s = buffer.readLine())!= null) {
s1 += s + "\n";
}
txtArea.setText(s1);
buffer.close();
}
catch (Exception ex) {
logArea.append(ex + "\n");
}
} else {
logArea.append("Open cancelled.");
}
}
void saveAction() {
String saveFilePath = "";
int selectedIndex = tabbedPane.getSelectedIndex();
for (int i = 0; i < files.size(); i++) {
String path = files.get(i);
if (i == selectedIndex) {
saveFilePath = path;
}
}
for (int i = 0; i < txtAreas.size(); i++) {
RSyntaxTextArea textArea = txtAreas.get(i);
if (i == selectedIndex) {
txtArea = textArea;
}
}
if (saveFilePath != null) {
try {
String content = txtArea.getText();
FileWriter fw = new FileWriter(saveFilePath) ;
BufferedWriter bw = new BufferedWriter(fw);
bw.write(content);
bw.close();
}
catch (IOException e) {
logArea.append(e + "\n");
}
} else {
saveAsAction();
}
}
void saveAsAction() {
String folderPath = "";
String folderName = "";
String fileName = "";
String saveAsFilePath = "";
JFileChooser fileChooser = new JFileChooser(codeDir);
fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
fileChooser.setDialogTitle("Save sketch folder as:");
int result = fileChooser.showSaveDialog(frame);
if (result == JFileChooser.APPROVE_OPTION) {
File newDir = fileChooser.getSelectedFile();
folderName = newDir.getName();
fileName = folderName + ".pde";
boolean created = newDir.mkdir();
logArea.append("Dir created:" + created + "\n");
Path folder = newDir.toPath();
folderPath = folder.toString();
saveAsFilePath = folderPath + "/" + fileName;
frame.setTitle(fileName);
try {
// Make certain we have the selected text area
int selectedIndex = tabbedPane.getSelectedIndex();
for (int i = 0; i < txtAreas.size(); i++) {
RSyntaxTextArea textArea = txtAreas.get(i);
if (i == selectedIndex) {
txtArea = textArea;
}
}
String content = txtArea.getText();
txtArea(); // create a new textArea
tabbedPane.addTab(fileName, txtScrlPane);
int tabIndex = tabbedPane.indexOfTab(fileName);
tabbedPane.setSelectedIndex(tabIndex);
txtAreas.add(tabIndex, txtArea); // Add to ArrayList
files.add(tabIndex, saveAsFilePath); //Add to ArrayList
FileWriter fw = new FileWriter(saveAsFilePath) ;
BufferedWriter bw = new BufferedWriter(fw);
bw.write(content);
bw.close();
// Save it and then read it back into tab
BufferedReader buffer = new BufferedReader(new FileReader(saveAsFilePath));
String s = "", s1 = "";
while ((s = buffer.readLine())!= null) {
s1 += s + "\n";
}
txtArea.setText(s1);
buffer.close();
}
catch (IOException e) {
logArea.append("Error:" + e + "\n");
}
} else {
logArea.append("No directory selected.");
}
}
void printerAction() {
PrinterJob job = PrinterJob.getPrinterJob();
PageFormat orient = job.defaultPage();
orient.setOrientation(PageFormat.PORTRAIT);
job.setPrintable(new MyPrinter(), job.defaultPage());
if (job.printDialog()) {
try {
job.print();
}
catch (Exception e) {
println("Printer error: ",e);
}
}
}
void findAction() {
findDialog = new FindDialog();
}
void runAction() {
String runFilePath = "";
String folderPath = "";
int selectedIndex = tabbedPane.getSelectedIndex();
for (int i = 0; i < files.size(); i++) {
String path = files.get(i);
if (i == selectedIndex) {
runFilePath = path;
// logArea.append("runAction : runFilePath = " + runFilePath + "\n");
}
}
int lastSlashIndex = runFilePath.lastIndexOf('/');
if (lastSlashIndex != -1) {
folderPath = runFilePath.substring(0, lastSlashIndex);
// logArea.append("runAction : folderPath = " + folderPath + "\n");
} else {
logArea.append("No path separator found.");
}
String sketchStr = "--sketch=" + folderPath;
ProcessBuilder processBuilder = new ProcessBuilder(cmdStrExe, sketchStr, " --run");
try {
process = processBuilder.start();
BufferedReader stdIn = new BufferedReader(new InputStreamReader(process.getInputStream()));
BufferedReader stdErr = new BufferedReader(new InputStreamReader(process.getErrorStream()));
String outStr = "";
while ((outStr = stdIn.readLine()) != null) {
logArea.append(outStr + "\n");
}
String errStr = "";
while ((errStr = stdErr.readLine()) != null) {
logArea.append(errStr + "\n");
}
// Wait for the process to complete and get its exit value
int exitCode = process.waitFor();
logArea.append("\nProcess exited with code: " + exitCode + "\n");
}
catch (IOException | InterruptedException e) {
logArea.append(e + "\n");
}
}
void clearLogAction() {
logArea.setText("");
}
void runBtnAction() {
thread("runAction");
}
void removeTabProcedure() {
int selectedIndex = tabbedPane.getSelectedIndex();
if (selectedIndex > 0) {
tabbedPane.removeTabAt(selectedIndex);
files.remove(selectedIndex); // Also remove selectedIndex from ArrayLists
txtAreas.remove(selectedIndex);
} else {
JOptionPane.showMessageDialog(frame, "Unable to remove baseline tab.");
}
}
void quitBtnAction() {
exit();
}
void openExecutableChooser() {
JFileChooser fileChooser = new JFileChooser();
int result = fileChooser.showOpenDialog(frame); // Or showSaveDialog(frame)
if (result == JFileChooser.APPROVE_OPTION) {
File selectedDirectory = fileChooser.getSelectedFile();
cmdStrExe = selectedDirectory.getAbsolutePath();
cmdStrLabel.setText("cmdStr:"+cmdStrExe);
String[] settingsStr = {cmdStrExe, codeDir};
saveStrings(sketchPath()+"/settings.txt", settingsStr);
} else {
cmdStrLabel.setText("No file selected.");
}
}
void openFolderChooser() {
JFileChooser fileChooser = new JFileChooser();
fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); // Set to select directories only
int result = fileChooser.showOpenDialog(frame); // Or showSaveDialog(frame)
if (result == JFileChooser.APPROVE_OPTION) {
File selectedDirectory = fileChooser.getSelectedFile();
codeDir = selectedDirectory.getAbsolutePath();
folderLabel.setText("fldr:"+codeDir);
String[] settingsStr = {cmdStrExe, codeDir};
saveStrings(sketchPath()+"/settings.txt", settingsStr);
} else {
folderLabel.setText("No directory selected.");
}
}
void runBtn(int x, int y, int w, int h) {
runBtn = new RunButton();
runBtn.setBounds(x, y, w, h);
runBtn.setToolTipText("Run code.");
runBtn.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent me) {
runBtnAction();
}
}
);
frame.add(runBtn);
runBtn.repaint();
}
void quitBtn(int x, int y, int w, int h) {
quitBtn = new QuitButton();
quitBtn.setBounds(x, y, w, h);
quitBtn.setToolTipText("Quit editor.");
quitBtn.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent me) {
quitBtnAction();
}
}
);
frame.add(quitBtn);
quitBtn.repaint();
}
void fontSizeLabel(int x, int y, int w, int h) {
JLabel fontSizeLabel = new JLabel("Font size:");
fontSizeLabel.setHorizontalAlignment(JLabel.LEFT);
fontSizeLabel.setBounds(x, y, w, h);
frame.add(fontSizeLabel);
}
void cmdStrLabel(int x, int y, int w, int h) {
cmdStrLabel = new JLabel("cmdStr:"+cmdStrExe);
cmdStrLabel.setHorizontalAlignment(JLabel.LEFT);
cmdStrLabel.setForeground(Color.BLUE);
Font myFont = new Font("Menlo", Font.ITALIC, 12);
cmdStrLabel.setFont(myFont);
cmdStrLabel.setBounds(x, y, w, h);
frame.add(cmdStrLabel);
}
void folderLabel(int x, int y, int w, int h) {
folderLabel = new JLabel("fldr:"+codeDir);
folderLabel.setHorizontalAlignment(JLabel.LEFT);
folderLabel.setForeground(Color.RED);
Font myFont = new Font("Menlo", Font.ITALIC, 12);
folderLabel.setFont(myFont);
folderLabel.setBounds(x, y, w, h);
frame.add(folderLabel);
}
void fontSizeSpinner(int x, int y, int w, int h) {
//syntax: (init, min, max, step)
SpinnerModel value = new SpinnerNumberModel(13, 8, 30, 1);
fontSize = new JSpinner(value);
fontSize.setBounds(x, y, w, h);
fontSize.setToolTipText("<html>For current and <br>successive files.</html>");
((JSpinner.DefaultEditor) fontSize.getEditor()).getTextField().setEditable(false);
fontSize.addChangeListener(e -> {
int fntSizeValue = (Integer)fontSize.getValue();
Font currentFont = txtArea.getFont();
Font newFont = currentFont.deriveFont((float)fntSizeValue);
txtArea.setFont(newFont);
}
);
frame.add(fontSize);
}
void txtArea() {
txtArea = new RSyntaxTextArea();
txtArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA);
// txtArea.setCodeFoldingEnabled(true);
txtArea.setBracketMatchingEnabled(true);
txtArea.setFont(new Font("Menlo", Font.PLAIN, (Integer)fontSize.getValue()));
txtScrlPane = new RTextScrollPane(txtArea);
txtArea.setEditable(true);
txtArea.setLineWrap(false);
txtArea.setWrapStyleWord(true);
txtArea.repaint();
}
void logArea() {
logArea = new JTextArea();
logScrlPane = new JScrollPane(logArea);
logArea.setEditable(true);
logArea.setFont(new Font("Menlo", Font.PLAIN, 12));
logArea.setLineWrap(false);
logArea.setWrapStyleWord(true);
logArea.repaint();
}
void tabbedPane() {
tabbedPane = new JTabbedPane();
tabbedPane.addChangeListener(e -> {
int index = tabbedPane.getSelectedIndex();
String selectedFileStr = tabbedPane.getTitleAt(index);
frame.setTitle(selectedFileStr);
}
);
}
//Create a split pane with two scroll panes in it.
void splitPane() {
splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, tabbedPane, logScrlPane);
splitPane.setBounds(0, 50, _wndW, _wndH - 80);
splitPane.setOneTouchExpandable(true);
frame.add(splitPane);
splitPane.setDividerLocation(_wndH - 210);
splitPane.repaint();
//Provide minimum sizes for the two components in the split pane
Dimension minimumSize = new Dimension(_wndW, 50);
txtScrlPane.setMinimumSize(minimumSize);
logScrlPane.setMinimumSize(minimumSize);
}
void checkForSettings() {
String filePath = sketchPath()+"/settings.txt";
File file = new File(filePath);
if (file.exists()) {
String[] s = loadStrings(sketchPath()+"/settings.txt");
for (int i = 0; i < s.length; i++) {
if (i == 0) {
cmdStrExe = s[0];
}
if (i == 1) {
codeDir = s[1];
}
}
} else {
println("The file does not exist but will be created.");
String[] settingsStr = {"null", "null"};
saveStrings(sketchPath()+"/settings.txt", settingsStr);
}
}
void buildWnd() {
checkForSettings();
menu = new Menu();
fontSizeSpinner(90, 20, 50, 24);
tabbedPane();
txtArea(); // RSyntaxTextArea
logArea();
splitPane();
runBtn(30, 5, 34, 34);
fontSizeLabel(85, 0, 75, 24);
cmdStrLabel(170, 0, 510, 24);
folderLabel(170, 20, 510, 24);
quitBtn(_wndW - 50, 5, 34, 34);
frame.setVisible(true);
}
void setup() {
surface.setVisible(false);
frame = new JFrame();
frame.setBounds(100, 100, _wndW, _wndH);
frame.setTitle("Processing Editor");
frame.setLayout(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.addComponentListener(new java.awt.event.ComponentAdapter() {
void componentResized(ComponentEvent e) {
JFrame tmp = (JFrame)e.getSource(); // For window resizing
splitPane.setBounds(0, 50, tmp.getWidth(), tmp.getHeight() - 80);
}
}
);
SwingUtilities.invokeLater(() -> {
buildWnd(); // Build components on EventDispatchThread(EDT).
}
);
}
âMenuBarâ Tab:
class Menu implements ActionListener {
JMenu fileMenu, editMenu, cmdMenu, settingsMenu, clearMenu, tabMenu;
JMenuItem newMenuItem,openMenuItem, saveMenuItem, saveAsMenuItem, printMenuItem;
JMenuItem cut, copy, paste, selectAll, findMenuItem, runPython;
JMenuItem folderMenuItem, cmdStrMenuItem, clearLog, removeTabMenuItem;
JMenuBar menuBar = new JMenuBar();
Menu() {
fileMenu = new JMenu("File");
newMenuItem = new JMenuItem("New");
openMenuItem = new JMenuItem("Open...");
openMenuItem.setAccelerator(KeyStroke.getKeyStroke('O', META_DOWN_MASK));
saveMenuItem = new JMenuItem("Save");
saveMenuItem.setAccelerator(KeyStroke.getKeyStroke('S', META_DOWN_MASK));
saveAsMenuItem = new JMenuItem("SaveAs...");
printMenuItem = new JMenuItem("Print...");
printMenuItem.setAccelerator(KeyStroke.getKeyStroke('P', META_DOWN_MASK));
editMenu = new JMenu("Edit");
cut = new JMenuItem("Cut");
cut.setAccelerator(KeyStroke.getKeyStroke('X', META_DOWN_MASK));
copy = new JMenuItem("Copy");
copy.setAccelerator(KeyStroke.getKeyStroke('C', META_DOWN_MASK));
paste = new JMenuItem("Paste");
paste.setAccelerator(KeyStroke.getKeyStroke('V', META_DOWN_MASK));
selectAll = new JMenuItem("SelectAll");
selectAll.setAccelerator(KeyStroke.getKeyStroke('A', META_DOWN_MASK));
findMenuItem = new JMenuItem("Find");
findMenuItem.setAccelerator(KeyStroke.getKeyStroke('F', META_DOWN_MASK));
cmdMenu = new JMenu("Cmd");
runPython = new JMenuItem("Run");
runPython.setAccelerator(KeyStroke.getKeyStroke('R', META_DOWN_MASK));
clearMenu = new JMenu("Clear");
clearLog = new JMenuItem("Log");
settingsMenu = new JMenu("Settings");
cmdStrMenuItem = new JMenuItem("CmdStr exe...");
folderMenuItem = new JMenuItem("Code Folder...");
tabMenu = new JMenu("Tab");
removeTabMenuItem = new JMenuItem("Remove Selected Tab");
fileMenu.add(newMenuItem);
fileMenu.add(openMenuItem);
fileMenu.add(saveMenuItem);
fileMenu.add(saveAsMenuItem);
fileMenu.addSeparator();
fileMenu.add(printMenuItem);
editMenu.add(cut);
editMenu.add(copy);
editMenu.add(paste);
editMenu.add(selectAll);
editMenu.addSeparator();
editMenu.add(findMenuItem);
cmdMenu.add(runPython);
clearMenu.add(clearLog);
settingsMenu.add(cmdStrMenuItem);
settingsMenu.add(folderMenuItem);
tabMenu.add(removeTabMenuItem);
menuBar.add(fileMenu);
menuBar.add(editMenu);
menuBar.add(cmdMenu);
menuBar.add(clearMenu);
menuBar.add(settingsMenu);
menuBar.add(tabMenu);
newMenuItem.addActionListener(this);
openMenuItem.addActionListener(this);
saveMenuItem.addActionListener(this);
saveAsMenuItem.addActionListener(this);
printMenuItem.addActionListener(this);
cut.addActionListener(this);
copy.addActionListener(this);
paste.addActionListener(this);
selectAll.addActionListener(this);
findMenuItem.addActionListener(this);
runPython.addActionListener(this);
clearLog.addActionListener(this);
cmdStrMenuItem.addActionListener(this);
folderMenuItem.addActionListener(this);
removeTabMenuItem.addActionListener(this);
frame.setJMenuBar(menuBar);
}
public void actionPerformed(ActionEvent evnt) {
if (evnt.getSource() == newMenuItem)
newFileAction();
if (evnt.getSource() == openMenuItem)
openAction();
if (evnt.getSource() == saveMenuItem)
saveAction();
if (evnt.getSource() == saveAsMenuItem)
saveAsAction();
if (evnt.getSource() == printMenuItem)
printerAction();
if (evnt.getSource() == cut)
txtArea.cut();
if (evnt.getSource() == paste)
txtArea.paste();
if (evnt.getSource() == copy)
txtArea.copy();
if (evnt.getSource() == selectAll)
txtArea.selectAll();
if (evnt.getSource() == findMenuItem)
findAction();
if (evnt.getSource() == runPython)
runBtnAction();
if (evnt.getSource() == clearLog)
clearLogAction();
if (evnt.getSource() == cmdStrMenuItem)
openExecutableChooser();
if (evnt.getSource() == folderMenuItem)
openFolderChooser();
if (evnt.getSource() == removeTabMenuItem)
removeTabProcedure();
}
}
Menu menu;
âFindâ Tab:
class FindDialog {
JDialog dlg;
JTextField findFld;
JTextField replaceFld;
void cancelBtnAction() {
dlg.setVisible(false);
}
void replaceBtnAction() {
// Make certain we have the selected text area
int selectedIndex = tabbedPane.getSelectedIndex();
for (int i = 0; i < txtAreas.size(); i++) {
RSyntaxTextArea textArea = txtAreas.get(i);
if (i == selectedIndex) {
txtArea = textArea;
}
}
String textToFind = findFld.getText();
String textToReplace = replaceFld.getText();
String currentText = txtArea.getText();
String newText = currentText.replaceAll(textToFind, textToReplace);
txtArea.setText(newText);
}
FindDialog() {
dlg = new JDialog(frame, "Find:");
dlg.setLayout(null);
dlg.setBounds(50, 50, 400, 200);
JLabel findLabel = new JLabel("Find:");
findLabel.setBounds(20, 20, 40, 24);
dlg.add(findLabel);
findFld = new JTextField();
findFld.setBounds(60, 20, 230, 24);
dlg.add(findFld);
JLabel replaceLabel = new JLabel("Replace:");
replaceLabel.setBounds(20, 60, 65, 24);
dlg.add(replaceLabel);
replaceFld = new JTextField();
replaceFld.setBounds(85, 60, 240, 24);
dlg.add(replaceFld);
JButton replaceBtn = new JButton("Replace All");
replaceBtn.setBounds(30, 120, 130, 24);
dlg.add(replaceBtn);
replaceBtn.addActionListener(e -> {
replaceBtnAction();
}
);
JButton cancelBtn = new JButton("Cancel");
cancelBtn.setBounds(180, 120, 130, 24);
dlg.add(cancelBtn);
cancelBtn.addActionListener(e -> {
cancelBtnAction();
}
);
dlg.setVisible(true);
}
}
FindDialog findDialog;
âPrinterâ Tab:
class MyPrinter implements Printable {
String filePath;
int[] pageBreaks; // array of page break line positions.
ArrayList<String> txtLines = new ArrayList<String>();
void initTextLines() {
int selectedIndex = tabbedPane.getSelectedIndex();
for (int i = 0; i < files.size(); i++) {
String path = files.get(i);
if (i == selectedIndex) {
filePath = path;
}
}
try {
BufferedReader buffer = new BufferedReader(new FileReader(filePath));
String s1 = "", s2 = "";
while ((s1 = buffer.readLine())!= null) {
s2 = s1 + "\n";
txtLines.add(s2); // add to ArrayList
}
buffer.close();
}
catch (Exception ex) {
ex.printStackTrace();
}
}
public int print(Graphics g, PageFormat pf, int pageIndex) throws PrinterException {
Font font = new Font("Menlo", Font.PLAIN, 12);
FontMetrics metrics = g.getFontMetrics(font);
int lineHeight = metrics.getHeight();
if (pageBreaks == null) {
initTextLines();
int linesPerPage = 44; // Hard coded to make it fit on page better.
// int linesPerPage = (int)(pf.getImageableHeight()/lineHeight);
int numBreaks = (txtLines.size()-1)/linesPerPage;
pageBreaks = new int[numBreaks];
for (int b=0; b<numBreaks; b++) {
pageBreaks[b] = (b+1)*linesPerPage;
}
}
if (pageIndex > pageBreaks.length) {
return NO_SUCH_PAGE;
}
// User (0,0) is typically outside the imageable area.
// Translate by the X and Y values in the PageFormat to avoid clipping
Graphics2D g2d = (Graphics2D)g;
g2d.translate(pf.getImageableX(), pf.getImageableY());
// Increment 'y' position by lineHeight for each line.
int x = 40; // left margin
int y = 40; // top Margin
int start = (pageIndex == 0) ? 0 : pageBreaks[pageIndex-1];
int end = (pageIndex == pageBreaks.length)
? txtLines.size() : pageBreaks[pageIndex];
for (int line=start; line<end; line++) {
y += lineHeight;
g.drawString(txtLines.get(line), x, y);
}
// Tell caller that this page is part of the printed document
return PAGE_EXISTS;
}
}
âQuitâ Tab:
class QuitButton extends JPanel {
int radius = 13; // Size of Btn
QuitButton() {
setOpaque(false);
}
void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
super.paintComponent(g);
BasicStroke bs = new BasicStroke(6.0);
Font myFont = new Font("Menlo",Font.BOLD,22);
g2.setStroke(bs);
g2.setColor(Color.lightGray);
g2.drawOval(getWidth()/2 - radius, getHeight()/2 - radius, radius * 2, radius * 2);
// g2.setColor(new Color(209, 209, 209));
g2.setColor(new Color(66,66,66));
g2.fillOval(getWidth()/2 - radius, getHeight()/2 - radius, radius * 2, radius * 2);
g2.setColor(Color.white);
g2.setFont(myFont);
g2.drawString("Q",11,25);
}
}
QuitButton quitBtn;
âRunâ Tab:
class RunButton extends JPanel {
int radius = 13; // Size of Btn
// Coordinates for triangle
int x[] = {16, 16, 18};
int y[] = {16, 18, 17};
RunButton() {
setOpaque(false);
}
void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
super.paintComponent(g);
BasicStroke bs = new BasicStroke(6.0);
g2.setStroke(bs);
g2.setColor (Color.lightGray);
g2.drawOval(getWidth()/2 - radius, getHeight()/2 - radius, radius * 2, radius * 2);
g2.setColor (new Color(24, 70, 183));
g2.fillOval(getWidth()/2 - radius, getHeight()/2 - radius, radius * 2, radius * 2);
g2.setColor(Color.white);
g2.drawPolygon(x, y, 3);
}
}
RunButton runBtn;
processing-java.exe (MacOS)
Output:




