nicer stuff ; cleanup of AI stuff
This commit is contained in:
Generated
+10
@@ -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/
|
||||||
Generated
+14
@@ -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>
|
||||||
Generated
+7
@@ -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>
|
||||||
Generated
+14
@@ -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>
|
||||||
Generated
+124
@@ -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
@@ -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>
|
||||||
@@ -14,6 +14,19 @@
|
|||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
</properties>
|
</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>
|
<dependencies>
|
||||||
<!-- Source: https://mvnrepository.com/artifact/com.google.code.gson/gson -->
|
<!-- Source: https://mvnrepository.com/artifact/com.google.code.gson/gson -->
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -29,6 +42,56 @@
|
|||||||
<version>3.7.1</version>
|
<version>3.7.1</version>
|
||||||
<scope>runtime</scope>
|
<scope>runtime</scope>
|
||||||
</dependency>
|
</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>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
package com.rdesk;
|
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.system.Exit;
|
||||||
import com.rdesk.applications.productivity.Notes;
|
import com.rdesk.applications.productivity.Notes;
|
||||||
import com.rdesk.applications.system.TaskManager;
|
import com.rdesk.applications.system.TaskManager;
|
||||||
@@ -19,11 +22,10 @@ public class Main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void boot() {
|
private static void boot() {
|
||||||
|
|
||||||
Kernel kernel = Kernel.get();
|
Kernel kernel = Kernel.get();
|
||||||
kernel.boot();
|
kernel.boot();
|
||||||
|
JFrame frame = new JFrame("rDesk");
|
||||||
|
|
||||||
JFrame frame = new JFrame("Java OS");
|
|
||||||
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
|
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
|
||||||
frame.addWindowListener(new java.awt.event.WindowAdapter() {
|
frame.addWindowListener(new java.awt.event.WindowAdapter() {
|
||||||
@Override
|
@Override
|
||||||
@@ -80,7 +82,11 @@ public class Main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void registerSystemApps(AppLauncher launcher) {
|
private static void registerSystemApps(AppLauncher launcher) {
|
||||||
|
|
||||||
launcher.addApp(new Notes(), "Productivity");
|
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 Exit(), "System");
|
||||||
launcher.addApp(new TaskManager(), "System");
|
launcher.addApp(new TaskManager(), "System");
|
||||||
launcher.addApp(new FileExplorerApp(), "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("&", "&").replace("<", "<")
|
||||||
|
+ "</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 javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
public class Exit implements DesktopApp {
|
public class Exit implements DesktopApp {
|
||||||
|
|
||||||
@@ -35,10 +36,20 @@ public class Exit implements DesktopApp {
|
|||||||
label.setFont(label.getFont().deriveFont(Font.BOLD));
|
label.setFont(label.getFont().deriveFont(Font.BOLD));
|
||||||
mainPanel.add(label, gbc);
|
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.setFocusPainted(false);
|
||||||
shutdownButton.addActionListener(e -> confirmAndShutdown(kernel, frame));
|
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(shutdownButton, gbc);
|
||||||
|
mainPanel.add(shutdownOS, gbc);
|
||||||
|
|
||||||
JButton cancelButton = new JButton("Cancel");
|
JButton cancelButton = new JButton("Cancel");
|
||||||
cancelButton.setFocusPainted(false);
|
cancelButton.setFocusPainted(false);
|
||||||
@@ -50,10 +61,47 @@ public class Exit implements DesktopApp {
|
|||||||
return frame;
|
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) {
|
private void confirmAndShutdown(Kernel kernel, JInternalFrame frame) {
|
||||||
int choice = JOptionPane.showConfirmDialog(
|
int choice = JOptionPane.showConfirmDialog(
|
||||||
frame,
|
frame,
|
||||||
"Are you sure you want to shut down the operating system?\nAll unsaved work will be lost.",
|
"Are you sure?",
|
||||||
"Confirm Shutdown",
|
"Confirm Shutdown",
|
||||||
JOptionPane.YES_NO_OPTION,
|
JOptionPane.YES_NO_OPTION,
|
||||||
JOptionPane.WARNING_MESSAGE
|
JOptionPane.WARNING_MESSAGE
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import javax.swing.*;
|
|||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLClassLoader;
|
import java.net.URLClassLoader;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -144,7 +145,12 @@ public class AppLauncher extends JPanel {
|
|||||||
} catch (Throwable ignored) {}
|
} catch (Throwable ignored) {}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} 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;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -198,7 +204,18 @@ public class AppLauncher extends JPanel {
|
|||||||
appButton.setFocusPainted(false);
|
appButton.setFocusPainted(false);
|
||||||
appButton.setBorder(BorderFactory.createEmptyBorder(4, 8, 4, 8));
|
appButton.setBorder(BorderFactory.createEmptyBorder(4, 8, 4, 8));
|
||||||
appButton.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
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
|
// Make each app button fill the width of its container
|
||||||
appButton.setAlignmentX(LEFT_ALIGNMENT);
|
appButton.setAlignmentX(LEFT_ALIGNMENT);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.rdesk.kernel;
|
package com.rdesk.kernel;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public class AppManager {
|
public class AppManager {
|
||||||
@@ -22,7 +23,7 @@ public class AppManager {
|
|||||||
return installedApps.containsKey(id);
|
return installedApps.containsKey(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void launch(String id) {
|
public void launch(String id) throws IOException {
|
||||||
DesktopApp app = installedApps.get(id);
|
DesktopApp app = installedApps.get(id);
|
||||||
if (app == null) {
|
if (app == null) {
|
||||||
System.err.println("[AppManager] App not found: " + id);
|
System.err.println("[AppManager] App not found: " + id);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.rdesk.kernel;
|
package com.rdesk.kernel;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
public interface DesktopApp {
|
public interface DesktopApp {
|
||||||
|
|
||||||
@@ -12,7 +13,7 @@ public interface DesktopApp {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
JInternalFrame createWindow(Kernel kernel);
|
JInternalFrame createWindow(Kernel kernel) throws IOException;
|
||||||
|
|
||||||
default void onStart(Kernel kernel) {}
|
default void onStart(Kernel kernel) {}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.rdesk.kernel;
|
|||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLClassLoader;
|
import java.net.URLClassLoader;
|
||||||
import java.util.jar.*;
|
import java.util.jar.*;
|
||||||
@@ -171,7 +172,11 @@ public class DesktopIconManager {
|
|||||||
if (!kernel.apps().isRegistered(app.getId())) {
|
if (!kernel.apps().isRegistered(app.getId())) {
|
||||||
kernel.apps().register(app);
|
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);
|
desktop.add(button);
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ public class Kernel {
|
|||||||
this.windowManager = new WindowManager(this);
|
this.windowManager = new WindowManager(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Thread-safe singleton accessor. */
|
|
||||||
public static Kernel get() {
|
public static Kernel get() {
|
||||||
if (instance == null) {
|
if (instance == null) {
|
||||||
synchronized (Kernel.class) {
|
synchronized (Kernel.class) {
|
||||||
@@ -45,29 +44,10 @@ public class Kernel {
|
|||||||
this.desktopFrame = frame;
|
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) {
|
public void attachIconManager(DesktopIconManager iconManager) {
|
||||||
this.iconManager = 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) {
|
public void registerSystemApp(DesktopApp app) {
|
||||||
if (iconManager == null) {
|
if (iconManager == null) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
@@ -96,10 +76,6 @@ public class Kernel {
|
|||||||
System.exit(0);
|
System.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------ //
|
|
||||||
// Accessors //
|
|
||||||
// ------------------------------------------------------------------ //
|
|
||||||
|
|
||||||
public AppManager apps() { return appManager; }
|
public AppManager apps() { return appManager; }
|
||||||
public WindowManager windows() { return windowManager; }
|
public WindowManager windows() { return windowManager; }
|
||||||
public EventBus events() { return eventBus; }
|
public EventBus events() { return eventBus; }
|
||||||
|
|||||||
Reference in New Issue
Block a user