nicer stuff ; cleanup of AI stuff

This commit is contained in:
2026-05-17 17:05:28 +02:00
parent ef40b733bd
commit 86799e4b19
24 changed files with 1661 additions and 33 deletions
+10
View File
@@ -0,0 +1,10 @@
# Default ignored files
/shelf/
/workspace.xml
# Ignored default folder with query files
/queries/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
# Editor-based HTTP Client requests
/httpRequests/
+14
View File
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DiscordProjectSettings">
<option name="show" value="PROJECT_FILES" />
<option name="description" value="" />
<option name="applicationTheme" value="default" />
<option name="iconsTheme" value="default" />
<option name="button1Title" value="" />
<option name="button1Url" value="" />
<option name="button2Title" value="" />
<option name="button2Url" value="" />
<option name="customApplicationId" value="" />
</component>
</project>
+7
View File
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
</component>
</project>
+14
View File
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
+124
View File
@@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Palette2">
<group name="Swing">
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
</item>
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
</item>
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.svg" removable="false" auto-create-binding="false" can-attach-label="true">
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
</item>
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
<initial-values>
<property name="text" value="Button" />
</initial-values>
</item>
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="RadioButton" />
</initial-values>
</item>
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="CheckBox" />
</initial-values>
</item>
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
<initial-values>
<property name="text" value="Label" />
</initial-values>
</item>
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
</item>
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
</item>
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
<preferred-size width="-1" height="20" />
</default-constraints>
</item>
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
</item>
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
</item>
</group>
</component>
</project>
Generated
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
+63
View File
@@ -14,6 +14,19 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>0.0.8</version>
<configuration>
<mainClass>com.rdesk.Main</mainClass>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<!-- Source: https://mvnrepository.com/artifact/com.google.code.gson/gson -->
<dependency>
@@ -29,6 +42,56 @@
<version>3.7.1</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>me.friwi</groupId>
<artifactId>jcefmaven</artifactId>
<version>146.0.10</version>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>5.14.0</version>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna-platform</artifactId>
<version>5.14.0</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>21.0.2</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-web</artifactId>
<version>21.0.2</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-swing</artifactId>
<version>21.0.2</version>
</dependency>
<!-- Source: https://mvnrepository.com/artifact/com.fifesoft/rsyntaxtextarea -->
<dependency>
<groupId>com.fifesoft</groupId>
<artifactId>rsyntaxtextarea</artifactId>
<version>3.6.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fifesoft</groupId>
<artifactId>autocomplete</artifactId>
<version>3.3.3</version>
</dependency>
<dependency>
<groupId>com.fifesoft</groupId>
<artifactId>languagesupport</artifactId>
<version>3.4.1</version>
</dependency>
</dependencies>
</project>
+4
View File
@@ -0,0 +1,4 @@
# rDesk
<img src="rev2.png">
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 328 KiB

+8 -2
View File
@@ -1,5 +1,8 @@
package com.rdesk;
import com.rdesk.applications.productivity.Calculator;
import com.rdesk.applications.productivity.browsing.TabbedBrowser;
import com.rdesk.applications.productivity.code.CodeDesk;
import com.rdesk.applications.system.Exit;
import com.rdesk.applications.productivity.Notes;
import com.rdesk.applications.system.TaskManager;
@@ -19,11 +22,10 @@ public class Main {
}
private static void boot() {
Kernel kernel = Kernel.get();
kernel.boot();
JFrame frame = new JFrame("rDesk");
JFrame frame = new JFrame("Java OS");
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.addWindowListener(new java.awt.event.WindowAdapter() {
@Override
@@ -80,7 +82,11 @@ public class Main {
}
private static void registerSystemApps(AppLauncher launcher) {
launcher.addApp(new Notes(), "Productivity");
launcher.addApp(new TabbedBrowser(), "Productivity");
launcher.addApp(new CodeDesk(), "Productivity");
launcher.addApp(new Calculator(), "Productivity");
launcher.addApp(new Exit(), "System");
launcher.addApp(new TaskManager(), "System");
launcher.addApp(new FileExplorerApp(), "System");
@@ -0,0 +1,457 @@
package com.rdesk.applications.productivity;
import com.rdesk.kernel.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.math.BigInteger;
public class Calculator implements DesktopApp {
@Override
public String getId() { return "calculator"; }
@Override
public String getName() { return "Calculator"; }
@Override
public JInternalFrame createWindow(Kernel kernel) {
JInternalFrame frame = new JInternalFrame(
"Calculator",
true, true, true, true
);
frame.setSize(420, 480);
frame.setLayout(new BorderLayout(6,6));
JPanel top = new JPanel(new BorderLayout(6,6));
String[] modes = {"Standard", "Scientific", "Programmer"};
JComboBox<String> modeBox = new JComboBox<>(modes);
top.add(modeBox, BorderLayout.WEST);
JTextField display = new JTextField();
display.setEditable(false);
display.setHorizontalAlignment(SwingConstants.RIGHT);
display.setFont(display.getFont().deriveFont(20f));
top.add(display, BorderLayout.CENTER);
frame.add(top, BorderLayout.NORTH);
JPanel cards = new JPanel(new CardLayout());
JPanel stdPanel = buildStandardPanel(display);
JPanel sciPanel = buildScientificPanel(display);
JPanel progPanel = buildProgrammerPanel(display);
cards.add(stdPanel, "Standard");
cards.add(sciPanel, "Scientific");
cards.add(progPanel, "Programmer");
frame.add(cards, BorderLayout.CENTER);
modeBox.addActionListener(e -> {
CardLayout cl = (CardLayout)(cards.getLayout());
cl.show(cards, (String)modeBox.getSelectedItem());
});
frame.setVisible(true);
return frame;
}
private JPanel buildStandardPanel(JTextField display) {
JPanel panel = new JPanel(new BorderLayout());
JPanel buttons = new JPanel(new GridLayout(5,4,5,5));
String[] labels = {
"C", "+-", "%", "/",
"7", "8", "9", "*",
"4", "5", "6", "-",
"1", "2", "3", "+",
"0", ".", "=", "OFF"
};
buttons.setBorder(BorderFactory.createEmptyBorder(8,8,8,8));
CalculatorState state = new CalculatorState(display);
addButtons(buttons, labels, state);
panel.add(buttons, BorderLayout.CENTER);
return panel;
}
private JPanel buildScientificPanel(JTextField display) {
JPanel panel = new JPanel(new BorderLayout());
JPanel topRow = new JPanel(new GridLayout(1,4,5,5));
topRow.setBorder(BorderFactory.createEmptyBorder(8,8,0,8));
JButton sin = new JButton("sin");
JButton cos = new JButton("cos");
JButton tan = new JButton("tan");
JButton sqrt = new JButton("");
topRow.add(sin); topRow.add(cos); topRow.add(tan); topRow.add(sqrt);
JPanel buttons = new JPanel(new GridLayout(5,4,5,5));
String[] labels = {
"C", "±", "%", "/",
"7", "8", "9", "*",
"4", "5", "6", "-",
"1", "2", "3", "+",
"0", ".", "=", "ln"
};
buttons.setBorder(BorderFactory.createEmptyBorder(0,8,8,8));
CalculatorState state = new CalculatorState(display);
addButtons(buttons, labels, state);
ActionListener sciListener = e -> {
String cmd = ((JButton)e.getSource()).getText();
try {
double v = state.currentAsDoubleOrLast();
double result;
switch (cmd) {
case "sin": result = Math.sin(Math.toRadians(v)); break;
case "cos": result = Math.cos(Math.toRadians(v)); break;
case "tan": result = Math.tan(Math.toRadians(v)); break;
case "sqrt": result = Math.sqrt(v); break;
case "ln": result = Math.log(v); break;
default: return;
}
state.setResult(result);
} catch (NumberFormatException ex) {
state.error();
}
};
sin.addActionListener(sciListener);
cos.addActionListener(sciListener);
tan.addActionListener(sciListener);
sqrt.addActionListener(sciListener);
panel.add(topRow, BorderLayout.NORTH);
panel.add(buttons, BorderLayout.CENTER);
return panel;
}
private JPanel buildProgrammerPanel(JTextField display) {
JPanel panel = new JPanel(new BorderLayout());
JPanel baseRow = new JPanel(new GridLayout(1,4,5,5));
baseRow.setBorder(BorderFactory.createEmptyBorder(8,8,0,8));
JButton binBtn = new JButton("BIN");
JButton octBtn = new JButton("OCT");
JButton decBtn = new JButton("DEC");
JButton hexBtn = new JButton("HEX");
baseRow.add(binBtn); baseRow.add(octBtn); baseRow.add(decBtn); baseRow.add(hexBtn);
JPanel buttons = new JPanel(new GridLayout(6,4,5,5));
buttons.setBorder(BorderFactory.createEmptyBorder(0,8,8,8));
String[] labels = {
"A","B","C","D",
"E","F","AND","OR",
"<<",">>","^","~",
"7","8","9","/",
"4","5","6","*",
"1","2","3","-",
"0","","=", "+"
};
CalculatorStateProg state = new CalculatorStateProg(display);
addButtonsProg(buttons, labels, state);
// base switching
binBtn.addActionListener(e -> state.setBase(2));
octBtn.addActionListener(e -> state.setBase(8));
decBtn.addActionListener(e -> state.setBase(10));
hexBtn.addActionListener(e -> state.setBase(16));
state.setBase(10);
panel.add(baseRow, BorderLayout.NORTH);
panel.add(buttons, BorderLayout.CENTER);
return panel;
}
private void addButtons(JPanel panel, String[] labels, CalculatorState state) {
for (String label : labels) {
if (label.isEmpty()) { panel.add(new JLabel()); continue; }
JButton b = new JButton(label);
b.setFont(b.getFont().deriveFont(14f));
b.addActionListener(e -> state.handle(((JButton)e.getSource()).getText()));
panel.add(b);
}
}
private void addButtonsProg(JPanel panel, String[] labels, CalculatorStateProg state) {
for (String label : labels) {
if (label.isEmpty()) { panel.add(new JLabel()); continue; }
JButton b = new JButton(label);
b.setFont(b.getFont().deriveFont(13f));
b.addActionListener(e -> state.handle(((JButton)e.getSource()).getText()));
panel.add(b);
}
}
private static class CalculatorState {
private final JTextField display;
private final StringBuilder current = new StringBuilder();
private double lastValue = 0;
private String op = null;
private boolean resetOnNext = false;
CalculatorState(JTextField display) { this.display = display; }
void handle(String cmd) {
try {
switch (cmd) {
case "C": clear(); break;
case "±": negate(); break;
case "%": percent(); break;
case "+": case "-": case "*": case "/": setOperator(cmd); break;
case "=": compute(); break;
case ".":
if (resetOnNext) { current.setLength(0); resetOnNext = false; }
if (current.indexOf(".") < 0) {
if (current.isEmpty()) current.append("0");
current.append(".");
display.setText(current.toString());
}
break;
default: // digits
if (resetOnNext) { current.setLength(0); resetOnNext = false; }
if (current.length() == 1 && current.charAt(0) == '0' && cmd.equals("0")) return;
current.append(cmd);
display.setText(current.toString());
break;
}
} catch (NumberFormatException ex) { error(); }
}
double currentAsDoubleOrLast() {
if (!current.isEmpty()) return Double.parseDouble(current.toString());
return lastValue;
}
void setOperatorInternal(String operator) {
if (!current.isEmpty()) {
double cur = Double.parseDouble(current.toString());
if (op != null) lastValue = applyOp(lastValue, cur, op);
else lastValue = cur;
display.setText(strip(lastValue));
current.setLength(0);
}
op = operator;
resetOnNext = false;
}
void setOperator(String operator) { setOperatorInternal(operator); }
void compute() {
if (op == null) {
if (!current.isEmpty()) display.setText(current.toString());
return;
}
double cur = !current.isEmpty() ? Double.parseDouble(current.toString()) : lastValue;
double result = applyOp(lastValue, cur, op);
display.setText(strip(result));
current.setLength(0);
current.append(strip(result));
lastValue = result;
op = null;
resetOnNext = true;
}
void clear() { current.setLength(0); lastValue = 0; op = null; display.setText(""); }
void negate() {
if (current.isEmpty()) return;
if (current.charAt(0)=='-') current.deleteCharAt(0); else current.insert(0,'-');
display.setText(current.toString());
}
void percent() {
if (current.isEmpty()) return;
double v = Double.parseDouble(current.toString())/100.0;
current.setLength(0); current.append(strip(v));
display.setText(current.toString());
}
void setResult(double v) {
display.setText(strip(v));
current.setLength(0); current.append(strip(v));
lastValue = v; op = null; resetOnNext = true;
}
void error() { display.setText("Error"); current.setLength(0); op = null; lastValue = 0; }
private double applyOp(double a, double b, String operator) {
return switch (operator) {
case "+" -> a + b;
case "-" -> a - b;
case "*" -> a * b;
case "/" -> b == 0 ? Double.NaN : a / b;
default -> b;
};
}
private String strip(double v) {
if (Double.isNaN(v)) return "Error";
if (v == (long)v) return String.format("%d", (long)v);
return String.format("%s", v).replaceAll("\\.?0+$", "");
}
}
/* -------------------------
Programmer state
- supports bases 2/8/10/16
- simple bitwise ops using BigInteger for safety
------------------------- */
private static class CalculatorStateProg {
private final JTextField display;
private int base = 10;
private BigInteger acc = BigInteger.ZERO;
private final StringBuilder current = new StringBuilder();
private String pendingOp = null;
CalculatorStateProg(JTextField display) { this.display = display; }
void setBase(int b) {
// convert displayed/current values to new base
String from = !current.isEmpty() ? current.toString() : acc.toString();
try {
BigInteger val = !current.isEmpty() ? new BigInteger(from, base) : acc;
base = b;
current.setLength(0);
current.append(val.toString(base).toUpperCase());
display.setText(current.toString());
} catch (Exception e) {
// if conversion fails, reset
base = b;
current.setLength(0);
acc = BigInteger.ZERO;
display.setText("");
}
}
void handle(String cmd) {
try {
switch (cmd) {
case "AND": commitBitwise("AND"); break;
case "OR": commitBitwise("OR"); break;
case "^": commitBitwise("XOR"); break;
case "~": applyNot(); break;
case "<<": commitShiftLeft(); break;
case ">>": commitShiftRight(); break;
case "C": clear(); break;
case "=": compute(); break;
default:
// digit or hex letter or operator symbols used as digits
if (isDigitForBase(cmd)) {
current.append(cmd);
display.setText(current.toString());
} else if (cmd.equals("+")||cmd.equals("-")||cmd.equals("*")||cmd.equals("/")) {
// allow arithmetic ops in programmer mode by converting to big integer math
commitArithmetic(cmd);
}
break;
}
} catch (Exception ex) { display.setText("Error"); current.setLength(0); acc = BigInteger.ZERO; pendingOp = null; }
}
private boolean isDigitForBase(String s) {
if (s.length()!=1) return false;
char c = s.charAt(0);
if (Character.isDigit(c)) {
int d = c - '0';
return d < base;
} else {
// hex letters
if (base<=10) return false;
return "ABCDEF".indexOf(Character.toUpperCase(c))>=0;
}
}
private BigInteger currentBI() {
if (current.isEmpty()) return acc;
return new BigInteger(current.toString(), base);
}
private void commitBitwise(String op) {
BigInteger cur = currentBI();
if (pendingOp == null) {
acc = cur;
} else {
acc = applyBitwise(acc, cur, pendingOp);
}
pendingOp = op;
current.setLength(0);
display.setText(acc.toString(base).toUpperCase());
}
private void applyNot() {
BigInteger cur = currentBI();
// bitwise not in two's complement: choose a width; using 32-bit mask for common programmer behavior
int width = Math.max(32, cur.bitLength()+1);
BigInteger mask = BigInteger.ONE.shiftLeft(width).subtract(BigInteger.ONE);
BigInteger result = cur.not().and(mask);
current.setLength(0);
current.append(result.toString(base).toUpperCase());
display.setText(current.toString());
}
private void commitShiftLeft() {
BigInteger cur = currentBI();
BigInteger res = cur.shiftLeft(1);
current.setLength(0);
current.append(res.toString(base).toUpperCase());
display.setText(current.toString());
}
private void commitShiftRight() {
BigInteger cur = currentBI();
BigInteger res = cur.shiftRight(1);
current.setLength(0);
current.append(res.toString(base).toUpperCase());
display.setText(current.toString());
}
private void commitArithmetic(String operator) {
BigInteger cur = currentBI();
if (pendingOp == null) {
acc = cur;
} else {
acc = applyArithmetic(acc, cur, pendingOp);
}
pendingOp = operator;
current.setLength(0);
display.setText(acc.toString(base).toUpperCase());
}
private void compute() {
BigInteger cur = currentBI();
if (pendingOp == null) {
acc = cur;
} else {
if (pendingOp.equals("AND")||pendingOp.equals("OR")||pendingOp.equals("XOR")) {
acc = applyBitwise(acc, cur, pendingOp);
} else {
acc = applyArithmetic(acc, cur, pendingOp);
}
}
current.setLength(0);
current.append(acc.toString(base).toUpperCase());
display.setText(current.toString());
pendingOp = null;
}
private void clear() { current.setLength(0); acc = BigInteger.ZERO; pendingOp = null; display.setText(""); }
private BigInteger applyBitwise(BigInteger a, BigInteger b, String op) {
return switch (op) {
case "AND" -> a.and(b);
case "OR" -> a.or(b);
case "XOR" -> a.xor(b);
default -> b;
};
}
private BigInteger applyArithmetic(BigInteger a, BigInteger b, String op) {
return switch (op) {
case "+" -> a.add(b);
case "-" -> a.subtract(b);
case "*" -> a.multiply(b);
case "/" -> b.equals(BigInteger.ZERO) ? BigInteger.ZERO : a.divide(b);
default -> b;
};
}
}
}
@@ -0,0 +1,295 @@
package com.rdesk.applications.productivity.browsing;
import javafx.application.Platform;
import javafx.concurrent.Worker;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebErrorEvent;
import javafx.scene.web.WebHistory;
import javafx.scene.web.WebView;
import javafx.stage.FileChooser;
import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
public class BrowserUI {
private final String homeUrl;
private final TabPane tabPane = new TabPane();
private final Map<Tab, WebEngine> engineMap = new HashMap<>();
private final TextField urlBar = new TextField();
private final ProgressIndicator globalProgress = new ProgressIndicator();
private final Button backBtn = new Button("<");
private final Button forwardBtn = new Button(">");
private final Button refreshBtn = new Button("Refresh");
private final Button homeBtn = new Button("Home");
private final Button newTabBtn = new Button("+");
private final Button openFileBtn = new Button("Open");
private Scene scene;
public BrowserUI(String homeUrl) {
this.homeUrl = homeUrl;
}
public Scene createScene() {
BorderPane root = new BorderPane();
root.setTop(createToolbar());
root.setCenter(createTabPane());
configureTabSelection();
this.scene = new Scene(root);
return scene;
}
private HBox createToolbar() {
HBox toolbar = new HBox(5);
toolbar.setPadding(new Insets(5));
toolbar.setAlignment(Pos.CENTER_LEFT);
urlBar.setPrefWidth(500);
globalProgress.setVisible(false);
globalProgress.setPrefSize(20, 20);
toolbar.getChildren().addAll(
backBtn, forwardBtn, refreshBtn, homeBtn, newTabBtn,
openFileBtn, urlBar, globalProgress);
attachToolbarActions();
return toolbar;
}
private TabPane createTabPane() {
tabPane.setTabClosingPolicy(TabPane.TabClosingPolicy.ALL_TABS);
addNewTab(homeUrl);
return tabPane;
}
private void attachToolbarActions() {
Consumer<Consumer<WebEngine>> onCurrentTab = operation -> {
try {
Tab current = tabPane.getSelectionModel().getSelectedItem();
if (current != null) {
WebEngine engine = engineMap.get(current);
if (engine != null) operation.accept(engine);
}
} catch (Exception ex) {
logError("Toolbar action failed", ex);
}
};
backBtn.setOnAction(e -> onCurrentTab.accept(engine -> {
WebHistory history = engine.getHistory();
if (history.getCurrentIndex() > 0) history.go(-1);
}));
forwardBtn.setOnAction(e -> onCurrentTab.accept(engine -> {
WebHistory history = engine.getHistory();
if (history.getCurrentIndex() < history.getEntries().size() - 1) history.go(1);
}));
refreshBtn.setOnAction(e -> onCurrentTab.accept(WebEngine::reload));
homeBtn.setOnAction(e -> onCurrentTab.accept(engine -> engine.load(homeUrl)));
newTabBtn.setOnAction(e -> addNewTab(homeUrl));
// --- Open local file button (fixed: no owner window cast) ---
openFileBtn.setOnAction(e -> {
try {
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Open HTML File");
fileChooser.getExtensionFilters().add(
new FileChooser.ExtensionFilter(
"Web Pages", "*.html", "*.htm", "*.xhtml", "*.svg", "*.xml", "*.txt"));
// Show without an owner window works inside an embedded JFXPanel
File selectedFile = fileChooser.showOpenDialog(null);
if (selectedFile != null) {
String fileUrl = selectedFile.toURI().toString();
onCurrentTab.accept(engine -> engine.load(fileUrl));
}
} catch (Exception ex) {
logError("Open file action failed", ex);
}
});
// --- URL bar ---
urlBar.setOnAction(e -> {
try {
String input = urlBar.getText().trim();
if (input.isEmpty()) return;
String url = input;
if (!url.contains("://")) {
if (isLikelyFilePath(url)) {
try {
File file = new File(url);
url = file.toURI().toString();
} catch (Exception ex) {
url = "file://" + url;
}
} else {
url = "https://" + url;
}
urlBar.setText(url);
}
final String finalUrl = url;
onCurrentTab.accept(engine -> engine.load(finalUrl));
} catch (Exception ex) {
logError("URL bar action failed", ex);
}
});
}
private boolean isLikelyFilePath(String input) {
if (input.startsWith("/") || input.startsWith("~")) {
return true;
}
if (input.length() >= 2 && Character.isLetter(input.charAt(0))
&& input.charAt(1) == ':') {
char third = input.length() > 2 ? input.charAt(2) : 0;
if (third == '\\' || third == '/') {
return true;
}
}
return false;
}
private void configureTabSelection() {
tabPane.getSelectionModel().selectedItemProperty().addListener((obs, oldTab, newTab) -> {
try {
if (newTab != null) {
WebEngine engine = engineMap.get(newTab);
if (engine != null) {
urlBar.setText(engine.getLocation());
}
}
} catch (Exception ex) {
logError("Tab selection change failed", ex);
}
});
}
private void addNewTab(String initialUrl) {
try {
WebView webView = new WebView();
WebEngine engine = webView.getEngine();
engine.getLoadWorker().exceptionProperty().addListener((obs, oldExc, newExc) -> {
if (newExc != null) {
logError("WebEngine load exception", newExc);
showErrorPage(engine, newExc);
}
});
engine.setOnError((WebErrorEvent event) -> {
logError("WebEngine onError: " + event.getMessage(), null);
showErrorPage(engine, new Exception(event.getMessage()));
});
engine.getLoadWorker().stateProperty().addListener((obs, oldState, newState) -> {
if (newState == Worker.State.FAILED) {
Throwable ex = engine.getLoadWorker().getException();
if (ex != null) {
showErrorPage(engine, ex);
}
}
});
engine.load(initialUrl);
ProgressIndicator tabProgress = new ProgressIndicator();
tabProgress.setPrefSize(16, 16);
Tab tab = new Tab("Loading...", webView);
tab.setGraphic(tabProgress);
tab.setClosable(true);
engineMap.put(tab, engine);
engine.titleProperty().addListener((obs, old, newTitle) -> {
try {
if (newTitle != null && !newTitle.isEmpty()) {
tab.setText(newTitle);
}
} catch (Exception ex) {
logError("Title listener error", ex);
}
});
engine.locationProperty().addListener((obs, old, newUrl) -> {
try {
if (newUrl != null && !newUrl.isEmpty()) {
Tab selected = tabPane.getSelectionModel().getSelectedItem();
if (selected == tab) {
urlBar.setText(newUrl);
}
if (engine.getTitle() == null || engine.getTitle().isEmpty()) {
String domain = newUrl.replaceFirst("^https?://", "")
.replaceFirst("/.*$", "");
tab.setText(domain);
}
}
} catch (Exception ex) {
logError("Location listener error", ex);
}
});
engine.getLoadWorker().stateProperty().addListener((obs, oldState, newState) -> {
try {
boolean running = newState == Worker.State.RUNNING;
tabProgress.setVisible(running);
if (!running && tab.getGraphic() == tabProgress) {
tab.setGraphic(null);
}
} catch (Exception ex) {
logError("State listener error", ex);
}
});
engine.getLoadWorker().progressProperty().addListener((obs, old, prog) -> {
try {
Tab selected = tabPane.getSelectionModel().getSelectedItem();
if (selected == tab) {
double value = prog.doubleValue();
globalProgress.setVisible(value > 0 && value < 1);
}
} catch (Exception ex) {
logError("Progress listener error", ex);
}
});
tabPane.getTabs().add(tab);
tabPane.getSelectionModel().select(tab);
tab.setOnClosed(event -> engineMap.remove(tab));
} catch (Exception ex) {
logError("Failed to create new tab", ex);
}
}
private void showErrorPage(WebEngine engine, Throwable exception) {
StringWriter sw = new StringWriter();
exception.printStackTrace(new PrintWriter(sw));
String errorHtml = "<html><body style='font-family:sans-serif;padding:20px;'>"
+ "<h2>Page could not be loaded</h2>"
+ "<p style='color:red;'>" + exception.getMessage() + "</p>"
+ "<pre style='font-size:smaller;white-space:pre-wrap;'>"
+ sw.toString().replace("&", "&amp;").replace("<", "&lt;")
+ "</pre>"
+ "</body></html>";
Platform.runLater(() -> engine.loadContent(errorHtml));
}
private static void logError(String message, Throwable t) {
System.err.println("[BrowserUI ERROR] " + message);
if (t != null) {
t.printStackTrace(System.err);
}
}
}
@@ -0,0 +1,61 @@
package com.rdesk.applications.productivity.browsing;
import com.rdesk.kernel.DesktopApp;
import com.rdesk.kernel.Kernel;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Scene;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
public class TabbedBrowser implements DesktopApp {
private static final String HOME_URL = "https://www.google.com";
@Override
public String getId() {
return "sharkbrowser";
}
@Override
public String getName() {
return "SharkBrowser";
}
@Override
public JInternalFrame createWindow(Kernel kernel) {
JInternalFrame frame = new JInternalFrame(
"SharkBrowser", true, true, true, false);
frame.setSize(1200, 800);
frame.setLayout(new BorderLayout());
JFXPanel fxPanel = new JFXPanel();
frame.add(fxPanel, BorderLayout.CENTER);
Platform.runLater(() -> {
BrowserUI ui = new BrowserUI(HOME_URL);
Scene scene = ui.createScene();
fxPanel.setScene(scene);
frame.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
Platform.runLater(() -> {
double w = fxPanel.getWidth();
double h = fxPanel.getHeight();
if (w > 0 && h > 0) {
scene.getWindow().setWidth(w);
scene.getWindow().setHeight(h);
}
});
}
});
});
frame.setVisible(true);
return frame;
}
}
@@ -0,0 +1,23 @@
package com.rdesk.applications.productivity.code;
import com.rdesk.kernel.*;
import javax.swing.*;
public class CodeDesk implements DesktopApp {
@Override
public String getId() {
return "CodeDesk";
}
@Override
public String getName() {
return "CodeDesk";
}
@Override
public JInternalFrame createWindow(Kernel kernel) {
EditorWindow window = new EditorWindow();
return window.getFrame();
}
}
@@ -0,0 +1,153 @@
package com.rdesk.applications.productivity.code;
import org.fife.rsta.ac.AbstractLanguageSupport;
import org.fife.ui.rsyntaxtextarea.*;
import org.fife.ui.rtextarea.*;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.io.*;
public class EditorTab {
private final TabManager tabManager;
private RSyntaxTextArea textArea;
private RTextScrollPane scrollPane;
private File file;
private boolean dirty;
private AbstractLanguageSupport langSupport;
private String title; // base title without dirty marker
private JPanel tabHeaderComponent;
public EditorTab(String title, TabManager tabManager) {
this.tabManager = tabManager;
this.title = title;
this.file = null;
this.dirty = false;
buildTextArea();
buildScrollPane();
installDocumentListener();
installLanguageSupport(null);
}
public RSyntaxTextArea getTextArea() { return textArea; }
public RTextScrollPane getScrollPane() { return scrollPane; }
public File getFile() { return file; }
public boolean isDirty() { return dirty; }
public String getTitle() { return title; }
public void setTitle(String title) {
this.title = title;
tabManager.updateTabTitle(this);
}
public void loadFile(File file) throws IOException {
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
textArea.read(reader, null);
}
this.file = file;
this.dirty = false;
setTitle(file.getName());
String style = LanguageSupport.syntaxStyleFor(file.getName());
textArea.setSyntaxEditingStyle(style);
installLanguageSupport(style);
tabManager.updateTabTitle(this);
}
public void saveToFile(File file) {
try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) {
textArea.write(writer);
this.file = file;
this.dirty = false;
setTitle(file.getName());
tabManager.updateTabTitle(this);
} catch (IOException ex) {
JOptionPane.showMessageDialog(tabManager.window.getFrame(),
"Error saving file: " + ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
}
}
public void performSaveAs(Component parent) {
JFileChooser fc = tabManager.window.buildFileChooser();
if (fc.showSaveDialog(parent) != JFileChooser.APPROVE_OPTION) return;
File chosen = fc.getSelectedFile();
saveToFile(chosen);
String style = LanguageSupport.syntaxStyleFor(chosen.getName());
textArea.setSyntaxEditingStyle(style);
installLanguageSupport(style);
}
public JPanel getTabHeaderComponent() {
if (tabHeaderComponent == null) {
tabHeaderComponent = buildTabHeader();
}
return tabHeaderComponent;
}
private JPanel buildTabHeader() {
JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
panel.setOpaque(false);
JLabel label = new JLabel(title);
JButton close = new JButton("");
close.setBorder(BorderFactory.createEmptyBorder(0, 4, 0, 0));
close.setContentAreaFilled(false);
close.setFocusable(false);
close.addActionListener(e -> tabManager.closeTab(this));
panel.add(label);
panel.add(close);
return panel;
}
public void dispose() {
if (langSupport != null) {
langSupport.uninstall(textArea);
langSupport = null;
}
file = null;
}
private void buildTextArea() {
textArea = new RSyntaxTextArea(25, 80);
textArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_NONE);
textArea.setCodeFoldingEnabled(true);
textArea.setAntiAliasingEnabled(true);
textArea.setBracketMatchingEnabled(true);
textArea.setPaintMatchedBracketPair(true);
textArea.setMarkOccurrences(true);
textArea.setAutoIndentEnabled(true);
textArea.setTabSize(4);
textArea.setTabsEmulated(true);
}
private void buildScrollPane() {
scrollPane = new RTextScrollPane(textArea);
scrollPane.setFoldIndicatorEnabled(true);
scrollPane.setLineNumbersEnabled(true);
}
private void installDocumentListener() {
textArea.getDocument().addDocumentListener(new DocumentListener() {
private void markDirty() {
if (!dirty) {
dirty = true;
tabManager.updateTabTitle(EditorTab.this);
}
}
@Override public void insertUpdate(DocumentEvent e) { markDirty(); }
@Override public void removeUpdate(DocumentEvent e) { markDirty(); }
@Override public void changedUpdate(DocumentEvent e) { markDirty(); }
});
}
private void installLanguageSupport(String style) {
if (langSupport != null) {
langSupport.uninstall(textArea);
langSupport = null;
}
langSupport = LanguageSupport.install(textArea, style);
}
}
@@ -0,0 +1,122 @@
package com.rdesk.applications.productivity.code;
import org.fife.ui.rtextarea.*;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
public class EditorWindow {
private JInternalFrame frame;
final TabManager tabManager;
private JToolBar toolbar;
public EditorWindow() {
frame = new JInternalFrame("CodeDesk", true, true, true, true);
frame.setSize(960, 680);
frame.setLayout(new BorderLayout());
tabManager = new TabManager(this);
toolbar = buildToolBar();
frame.add(toolbar, BorderLayout.NORTH);
frame.add(tabManager.getTabbedPane(), BorderLayout.CENTER);
installGlobalShortcuts();
tabManager.addTab("Untitled");
frame.setVisible(true);
}
public JInternalFrame getFrame() {
return frame;
}
private JToolBar buildToolBar() {
JToolBar bar = new JToolBar();
bar.setFloatable(false);
JButton newTabBtn = new JButton("New Tab");
JButton openBtn = new JButton("Open");
JButton saveBtn = new JButton("Save");
JButton saveAsBtn = new JButton("Save As");
newTabBtn.addActionListener(e -> tabManager.addTab("Untitled"));
openBtn.addActionListener(e -> actionOpen());
saveBtn.addActionListener(e -> actionSave());
saveAsBtn.addActionListener(e -> actionSaveAs());
bar.add(newTabBtn);
bar.add(openBtn);
bar.add(saveBtn);
bar.add(saveAsBtn);
return bar;
}
private void installGlobalShortcuts() {
InputMap im = frame.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
ActionMap am = frame.getRootPane().getActionMap();
bind(im, am, "open", KeyStroke.getKeyStroke(KeyEvent.VK_O, InputEvent.CTRL_DOWN_MASK),
e -> actionOpen());
bind(im, am, "save", KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK),
e -> actionSave());
bind(im, am, "saveAs", KeyStroke.getKeyStroke(KeyEvent.VK_S,
InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK),
e -> actionSaveAs());
bind(im, am, "closeTab", KeyStroke.getKeyStroke(KeyEvent.VK_W, InputEvent.CTRL_DOWN_MASK),
e -> tabManager.closeCurrentTab());
bind(im, am, "newTab", KeyStroke.getKeyStroke(KeyEvent.VK_T, InputEvent.CTRL_DOWN_MASK),
e -> tabManager.addTab("Untitled"));
}
private void bind(InputMap im, ActionMap am, String key, KeyStroke ks, ActionListener handler) {
im.put(ks, key);
am.put(key, new AbstractAction() {
@Override public void actionPerformed(ActionEvent e) {
handler.actionPerformed(e);
}
});
}
private void actionOpen() {
JFileChooser fc = buildFileChooser();
if (fc.showOpenDialog(frame) != JFileChooser.APPROVE_OPTION) return;
tabManager.openFile(fc.getSelectedFile());
}
private void actionSave() {
EditorTab tab = tabManager.getCurrentTab();
if (tab == null) return;
if (tab.getFile() != null) {
tab.saveToFile(tab.getFile());
} else {
tab.performSaveAs(frame);
}
}
private void actionSaveAs() {
EditorTab tab = tabManager.getCurrentTab();
if (tab != null) tab.performSaveAs(frame);
}
JFileChooser buildFileChooser() {
JFileChooser fc = new JFileChooser();
fc.setFileFilter(new javax.swing.filechooser.FileNameExtensionFilter(
"Source Files (java, js, ts, html, css, php, c, cpp, py, rb, pl, sh, sql, xml)",
"java", "js", "ts", "html", "htm", "css", "php",
"c", "h", "cpp", "cc", "cxx", "hpp",
"py", "rb", "pl", "sh", "bash", "sql", "xml"));
fc.setAcceptAllFileFilterUsed(true);
return fc;
}
void showError(String prefix, Exception ex) {
JOptionPane.showMessageDialog(frame,
prefix + ": " + ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
}
}
@@ -0,0 +1,86 @@
package com.rdesk.applications.productivity.code;
import org.fife.rsta.ac.AbstractLanguageSupport;
import org.fife.rsta.ac.css.CssLanguageSupport;
import org.fife.rsta.ac.html.HtmlLanguageSupport;
import org.fife.rsta.ac.java.JavaLanguageSupport;
import org.fife.rsta.ac.js.JavaScriptLanguageSupport;
import org.fife.ui.autocomplete.*;
import org.fife.ui.rsyntaxtextarea.*;
import javax.swing.*;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.io.IOException;
public class LanguageSupport {
public static String syntaxStyleFor(String fileName) {
if (fileName == null) return SyntaxConstants.SYNTAX_STYLE_NONE;
String n = fileName.toLowerCase();
if (n.endsWith(".java")) return SyntaxConstants.SYNTAX_STYLE_JAVA;
if (n.endsWith(".js") || n.endsWith(".ts")) return SyntaxConstants.SYNTAX_STYLE_JAVASCRIPT;
if (n.endsWith(".html") || n.endsWith(".htm")) return SyntaxConstants.SYNTAX_STYLE_HTML;
if (n.endsWith(".css")) return SyntaxConstants.SYNTAX_STYLE_CSS;
if (n.endsWith(".php")) return SyntaxConstants.SYNTAX_STYLE_PHP;
if (n.endsWith(".c") || n.endsWith(".h")) return SyntaxConstants.SYNTAX_STYLE_C;
if (n.endsWith(".cpp") || n.endsWith(".cc")
|| n.endsWith(".cxx") || n.endsWith(".hpp")) return SyntaxConstants.SYNTAX_STYLE_CPLUSPLUS;
if (n.endsWith(".py")) return SyntaxConstants.SYNTAX_STYLE_PYTHON;
if (n.endsWith(".rb")) return SyntaxConstants.SYNTAX_STYLE_RUBY;
if (n.endsWith(".pl")) return SyntaxConstants.SYNTAX_STYLE_PERL;
if (n.endsWith(".sh") || n.endsWith(".bash")) return SyntaxConstants.SYNTAX_STYLE_UNIX_SHELL;
if (n.endsWith(".sql")) return SyntaxConstants.SYNTAX_STYLE_SQL;
if (n.endsWith(".xml")) return SyntaxConstants.SYNTAX_STYLE_XML;
return SyntaxConstants.SYNTAX_STYLE_NONE;
}
public static AbstractLanguageSupport install(RSyntaxTextArea area, String style) {
if (style == null) style = area.getSyntaxEditingStyle();
AbstractLanguageSupport ls = buildLanguageSupport(style);
if (ls != null) {
try {
configureSupport(ls);
ls.install(area);
return ls;
} catch (Exception ex) {
System.err.println("CodeDesk: could not install language support for "
+ style + ": " + ex.getMessage());
installFallbackCompletion(area);
return null;
}
} else {
installFallbackCompletion(area);
return null;
}
}
private static AbstractLanguageSupport buildLanguageSupport(String style) {
return switch (style) {
case SyntaxConstants.SYNTAX_STYLE_JAVA -> new JavaLanguageSupport();
case SyntaxConstants.SYNTAX_STYLE_JAVASCRIPT -> new JavaScriptLanguageSupport();
case SyntaxConstants.SYNTAX_STYLE_HTML -> new HtmlLanguageSupport();
case SyntaxConstants.SYNTAX_STYLE_CSS -> new CssLanguageSupport();
default -> null;
};
}
private static void configureSupport(AbstractLanguageSupport ls) throws IOException {
ls.setAutoActivationEnabled(true);
ls.setAutoActivationDelay(400);
ls.setAutoCompleteEnabled(true);
ls.setParameterAssistanceEnabled(true);
ls.setShowDescWindow(true);
if (ls instanceof JavaLanguageSupport jls) {
jls.getJarManager().addCurrentJreClassFileSource();
}
}
private static void installFallbackCompletion(RSyntaxTextArea area) {
AutoCompletion ac = new AutoCompletion(new DefaultCompletionProvider());
ac.setAutoCompleteEnabled(true);
ac.setAutoActivationEnabled(false);
ac.setTriggerKey(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, InputEvent.CTRL_DOWN_MASK));
ac.install(area);
}
}
@@ -0,0 +1,135 @@
package com.rdesk.applications.productivity.code;
import org.fife.ui.rsyntaxtextarea.*;
import org.fife.ui.rtextarea.RTextScrollPane;
import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.util.*;
import java.util.List;
public class TabManager {
final EditorWindow window;
private final JTabbedPane tabbedPane;
private final List<EditorTab> tabs = new ArrayList<>();
public TabManager(EditorWindow window) {
this.window = window;
this.tabbedPane = new JTabbedPane();
tabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
}
public JTabbedPane getTabbedPane() {
return tabbedPane;
}
public void addTab(String title) {
EditorTab tab = new EditorTab(title, this);
addTabComponent(tab);
}
public void openFile(File file) {
for (EditorTab tab : tabs) {
if (file.equals(tab.getFile())) {
selectTabFor(tab);
return;
}
}
EditorTab current = getCurrentTab();
if (current != null
&& current.getTextArea().getText().isEmpty()
&& current.getFile() == null) {
loadFileInTab(current, file);
} else {
EditorTab newTab = new EditorTab(file.getName(), this);
addTabComponent(newTab);
loadFileInTab(newTab, file);
}
}
public void closeCurrentTab() {
int idx = tabbedPane.getSelectedIndex();
if (idx < 0) return;
closeTab(tabs.get(idx));
}
public void closeTab(EditorTab tab) {
if (!confirmDiscardOrSave(tab)) return;
int idx = tabs.indexOf(tab);
if (idx >= 0) {
tabbedPane.removeTabAt(idx);
tabs.remove(idx);
}
tab.dispose();
}
public void updateTabTitle(EditorTab tab) {
int idx = tabs.indexOf(tab);
if (idx < 0) return;
String title = (tab.isDirty() ? "*" : "") +
(tab.getFile() != null ? tab.getFile().getName() : "Untitled");
tabbedPane.setTitleAt(idx, title);
// Update the label inside the custom tab component
Component comp = tabbedPane.getTabComponentAt(idx);
if (comp instanceof JPanel p && p.getComponentCount() > 0
&& p.getComponent(0) instanceof JLabel lbl) {
lbl.setText(title);
}
}
public EditorTab getCurrentTab() {
int idx = tabbedPane.getSelectedIndex();
return (idx >= 0 && idx < tabs.size()) ? tabs.get(idx) : null;
}
private void addTabComponent(EditorTab tab) {
tabs.add(tab);
RTextScrollPane scroll = tab.getScrollPane();
tabbedPane.addTab(tab.getTitle(), scroll);
int idx = tabbedPane.indexOfComponent(scroll);
tabbedPane.setTabComponentAt(idx, tab.getTabHeaderComponent());
tabbedPane.setSelectedIndex(idx);
updateTabTitle(tab);
}
private void loadFileInTab(EditorTab tab, File file) {
try {
tab.loadFile(file);
} catch (IOException ex) {
window.showError("Error opening file", ex);
}
}
private void selectTabFor(EditorTab tab) {
int idx = tabs.indexOf(tab);
if (idx >= 0) tabbedPane.setSelectedIndex(idx);
}
private boolean confirmDiscardOrSave(EditorTab tab) {
if (!tab.isDirty()) return true;
String name = (tab.getFile() != null) ? tab.getFile().getName() : "Untitled";
int result = JOptionPane.showConfirmDialog(
window.getFrame(),
"Save changes to \"" + name + "\"?",
"Unsaved Changes",
JOptionPane.YES_NO_CANCEL_OPTION);
if (result == JOptionPane.YES_OPTION) {
if (tab.getFile() != null) {
tab.saveToFile(tab.getFile());
} else {
tab.performSaveAs(window.getFrame());
}
}
return result != JOptionPane.CANCEL_OPTION;
}
}
@@ -5,6 +5,7 @@ import com.rdesk.kernel.Kernel;
import javax.swing.*;
import java.awt.*;
import java.io.IOException;
public class Exit implements DesktopApp {
@@ -35,10 +36,20 @@ public class Exit implements DesktopApp {
label.setFont(label.getFont().deriveFont(Font.BOLD));
mainPanel.add(label, gbc);
JButton shutdownButton = new JButton("Shutdown OS");
JButton shutdownButton = new JButton("Shutdown rDesk");
JButton shutdownOS = new JButton("Shutdown Host OS");
shutdownButton.setFocusPainted(false);
shutdownButton.addActionListener(e -> confirmAndShutdown(kernel, frame));
shutdownOS.setFocusPainted(false);
shutdownOS.addActionListener(e -> {
try {
shutdownHost(kernel, frame);
} catch (IOException | InterruptedException ex) {
throw new RuntimeException(ex);
}
});
mainPanel.add(shutdownButton, gbc);
mainPanel.add(shutdownOS, gbc);
JButton cancelButton = new JButton("Cancel");
cancelButton.setFocusPainted(false);
@@ -50,10 +61,47 @@ public class Exit implements DesktopApp {
return frame;
}
public void performShutdownCmd() throws IOException, InterruptedException {
String os = System.getProperty("os.name").toLowerCase();
String[] cmd;
if (os.contains("win")) {
cmd = new String[] {"shutdown.exe", "-s", "-t", "2"};
} else if (os.contains("mac") || os.contains("darwin") || os.contains("nix") || os.contains("nux") || os.contains("aix")) {
cmd = new String[] {"shutdown", "-h", "now"};
} else {
throw new RuntimeException("Unknown OS: " + os);
}
ProcessBuilder pb = new ProcessBuilder(cmd);
pb.inheritIO();
Process p = pb.start();
int exit = p.waitFor();
if (exit != 0) {
throw new IOException("Shutdown command exited with code " + exit);
}
}
private void shutdownHost(Kernel kernel, JInternalFrame frame) throws IOException, InterruptedException {
int choice = JOptionPane.showConfirmDialog(
frame,
"Confirm Host Shutdown",
"Are you sure? This will shutdown the Host OS",
JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE
);
if (choice == JOptionPane.YES_OPTION) {
frame.dispose();
performShutdownCmd(); // cause else the call wouldnt be called
kernel.shutdown();
}
}
private void confirmAndShutdown(Kernel kernel, JInternalFrame frame) {
int choice = JOptionPane.showConfirmDialog(
frame,
"Are you sure you want to shut down the operating system?\nAll unsaved work will be lost.",
"Are you sure?",
"Confirm Shutdown",
JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE
@@ -4,6 +4,7 @@ import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;
@@ -144,7 +145,12 @@ public class AppLauncher extends JPanel {
} catch (Throwable ignored) {}
}
} catch (Exception e) {
e.printStackTrace();
JOptionPane.showMessageDialog(
null,
"Kernel Fault",
"The Kernel has encoutered a fault when trying to find DesktopAppClass" + e.getStackTrace(),
JOptionPane.WARNING_MESSAGE
);
}
return null;
}
@@ -198,7 +204,18 @@ public class AppLauncher extends JPanel {
appButton.setFocusPainted(false);
appButton.setBorder(BorderFactory.createEmptyBorder(4, 8, 4, 8));
appButton.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
appButton.addActionListener(e -> kernel.apps().launch(app.getId()));
appButton.addActionListener(e -> {
try {
kernel.apps().launch(app.getId());
} catch (IOException ex) {
JOptionPane.showMessageDialog(
appsContainer,
"Kernel Fault",
"The Kernel encoutered a critical error when launching an Application",
JOptionPane.ERROR_MESSAGE
);
}
});
// Make each app button fill the width of its container
appButton.setAlignmentX(LEFT_ALIGNMENT);
@@ -1,6 +1,7 @@
package com.rdesk.kernel;
import javax.swing.*;
import java.io.IOException;
import java.util.*;
public class AppManager {
@@ -22,7 +23,7 @@ public class AppManager {
return installedApps.containsKey(id);
}
public void launch(String id) {
public void launch(String id) throws IOException {
DesktopApp app = installedApps.get(id);
if (app == null) {
System.err.println("[AppManager] App not found: " + id);
@@ -1,6 +1,7 @@
package com.rdesk.kernel;
import javax.swing.*;
import java.io.IOException;
public interface DesktopApp {
@@ -12,7 +13,7 @@ public interface DesktopApp {
return null;
}
JInternalFrame createWindow(Kernel kernel);
JInternalFrame createWindow(Kernel kernel) throws IOException;
default void onStart(Kernel kernel) {}
@@ -3,6 +3,7 @@ package com.rdesk.kernel;
import javax.swing.*;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.jar.*;
@@ -171,7 +172,11 @@ public class DesktopIconManager {
if (!kernel.apps().isRegistered(app.getId())) {
kernel.apps().register(app);
}
kernel.apps().launch(app.getId());
try {
kernel.apps().launch(app.getId());
} catch (IOException ex) {
throw new RuntimeException(ex);
}
});
desktop.add(button);
@@ -23,7 +23,6 @@ public class Kernel {
this.windowManager = new WindowManager(this);
}
/** Thread-safe singleton accessor. */
public static Kernel get() {
if (instance == null) {
synchronized (Kernel.class) {
@@ -45,29 +44,10 @@ public class Kernel {
this.desktopFrame = frame;
}
/**
* Attach the icon manager so the kernel can delegate system-app
* registration to it. Call this after the desktop pane is ready.
*/
public void attachIconManager(DesktopIconManager iconManager) {
this.iconManager = iconManager;
}
// ------------------------------------------------------------------ //
// System-app API — call from main() before or after boot() //
// ------------------------------------------------------------------ //
/**
* Register a built-in / system app and place its icon on the desktop.
*
* <pre>{@code
* Kernel kernel = Kernel.get();
* kernel.registerSystemApp(new MyCalculatorApp());
* }</pre>
*
* @param app the {@link DesktopApp} implementation to register
* @throws IllegalStateException if {@link #attachIconManager} has not been called yet
*/
public void registerSystemApp(DesktopApp app) {
if (iconManager == null) {
throw new IllegalStateException(
@@ -96,10 +76,6 @@ public class Kernel {
System.exit(0);
}
// ------------------------------------------------------------------ //
// Accessors //
// ------------------------------------------------------------------ //
public AppManager apps() { return appManager; }
public WindowManager windows() { return windowManager; }
public EventBus events() { return eventBus; }