diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..b47bf3a
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,7 @@
+
+version: 2
+updates:
+ - package-ecosystem: "maven" # See documentation for possible values
+ directory: "/" # Location of package manifests
+ schedule:
+ interval: "weekly"
diff --git a/.gitignore b/.gitignore
index 6df9964..a10ca8d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
target/
.mvn
mvnw*
-docs/.obsidian
\ No newline at end of file
+docs/.obsidian
+*.jar
diff --git a/README.md b/README.md
index 45a4777..a7d51a5 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,10 @@
-
|
- Piccaso Code
+ Picasso Code
Creativity + Logic + Math
A code based image editor created 100% in java
|
@@ -15,39 +14,97 @@
[](https://github.com/Glimmr-Lang/PicassoCode/actions/workflows/maven.yml)
## About
+<<<<<<< HEAD
+<<<<<<< HEAD
+Picasso code is an image editor that uses code to create/edit an image. This allows powerful designs to be created with ease and
+automation. The editor uses *PiccodeScript* as the scripting language for writing the image editing code.
+=======
+
+Piccasso code is an image editor that uses code to create/edit an image. This allows powerful designs to be created with ease and
+automation. The editor uses _glimr_ as the scripting language for writing the image editing code.
+>>>>>>> 0064ac3 (fix: corrected a spelling error in the readme file)
+=======
+
Piccasso code is an image editor that uses code to create/edit an image. This allows powerful designs to be created with ease and
-automation. The editor uses *glimr* as the scripting language for writing the image editing code.
+automation. The editor uses _glimr_ as the scripting language for writing the image editing code.
+=======
+Picasso code is an image editor that uses code to create/edit an image. This allows powerful designs to be created with ease and
+automation. The editor uses *PiccodeScript* as the scripting language for writing the image editing code.
+>>>>>>> 455b066 (README: Fixed mistakes in the README)
+>>>>>>> 955b7fb (README: Fixed mistakes in the README)
## Download
->> Coming soon
+
+> > Coming soon
## Building
```sh
-$ git clone git@github.com:hexaredecimal/Piccode.git
-$ cd Piccode
+<<<<<<< HEAD
+<<<<<<< HEAD
+$ git clone https://github.com/Glimmr-Lang/PicassoCode.git
+$ cd PicassoCode
$ mvn package
```
### Test your build
```sh
$ java -jar target/Piccode-1.0-SNAPSHOT-jar-with-dependencies.jar
+=======
+git clone git@github.com:hexaredecimal/Piccode.git
+cd Piccode
+mvn package
+>>>>>>> 0064ac3 (fix: corrected a spelling error in the readme file)
+=======
+git clone git@github.com:hexaredecimal/Piccode.git
+cd Piccode
+mvn package
+=======
+$ git clone https://github.com/Glimmr-Lang/PicassoCode.git
+$ cd PicassoCode
+$ mvn package
+```
+### Test your build
+```sh
+$ java -jar target/Piccode-1.0-SNAPSHOT-jar-with-dependencies.jar
+>>>>>>> 455b066 (README: Fixed mistakes in the README)
+>>>>>>> 955b7fb (README: Fixed mistakes in the README)
```
+### Test your build
+
+```sh
+java -jar target/Piccode-1.0-SNAPSHOT-jar-with-dependencies.jar
+```
## Inspired by
+<<<<<<< HEAD
+<<<<<<< HEAD
+PicassoCode is heavily inspired by the [OpenSCAD](https://openscad.org/) program and tries to mimic its functionality
+=======
+
+Piccassocode is heavily inspired by the [OpenSCAD](https://openscad.org/) program and tries to mimic its functionality
+>>>>>>> 0064ac3 (fix: corrected a spelling error in the readme file)
+=======
+
Piccassocode is heavily inspired by the [OpenSCAD](https://openscad.org/) program and tries to mimic its functionality
+=======
+PicassoCode is heavily inspired by the [OpenSCAD](https://openscad.org/) program and tries to mimic its functionality
+>>>>>>> 455b066 (README: Fixed mistakes in the README)
+>>>>>>> 955b7fb (README: Fixed mistakes in the README)
as much as it can while still being an image editor. I was stoked when I tried OpenSCAD for the first time and ended up
-challenging myself to start a new project based araound the idea. A friend suggested something that has to do with graphics
-and my first though was OpenSCAD, but 2D. The idea quickly grew and the small program became an image editor.
+challenging myself to start a new project based around the idea. A friend suggested something that has to do with graphics
+and my first though was OpenSCAD, but 2D. The idea quickly grew and the small program became an image editor.
## References
+
[java image filters](http://www.jhlabs.com/ip/filters/index.html)
[Icons8 Pack](https://icons8.com/icons/parakeet--style-parakeet)
## License
+
```sh
-drawString("
+Render::drawString("
+-----------------------------------+
| ▄▖▘ ▌ |
| ▙▌▌▛▘▀▌▛▘▛▘▛▌▛▘▛▌▛▌█▌ |
@@ -56,7 +113,5 @@ drawString("
+-----------------------------------+
", 0, 0) // Released under the MIT LICENSE
```
->> Thank you for viewing.
-
-
+> > Thank you for viewing.
diff --git a/examples/starrect.pics b/examples/starrect.pics
new file mode 100644
index 0000000..4e35fa6
--- /dev/null
+++ b/examples/starrect.pics
@@ -0,0 +1,44 @@
+
+IO :: import std.io
+Virtual :: import std.virtual
+Render :: import piccode.render
+Pen :: import piccode.pen
+
+drawStar :: (ctx, padding=0) =
+ ctx
+ |> Pen::drawLine(50+padding, 50+padding, 150-padding, 150-padding)
+ |> Pen::drawLine(150-padding, 50+padding, 50+padding, 150-padding)
+ |> Pen::drawLine(50+padding, 100, 150-padding, 100)
+ |> Pen::drawLine(100, 50+padding, 100, 150-padding)
+
+drawRect :: (ctx, x, y, w, h) =
+ ctx
+ |> Pen::drawLine(x, y, x + w, y)
+ |> Pen::drawLine(x, y + h, x + w, y + h)
+ |> Pen::drawLine(x, y, x, y + w)
+ |> Pen::drawLine(x + w, y, x + w, y + h)
+
+drawDepth :: (ctx) =
+ ctx
+ |> Pen::drawLine(150, 50, 200, 70)
+ |> Pen::drawLine(150, 150, 200, 170)
+ |> Pen::drawLine(50, 150, 100, 170)
+
+drawBackLines :: (ctx) =
+ ctx
+ |> Pen::drawLine(100, 170, 200, 170)
+ |> Pen::drawLine(200, 70, 200, 170)
+
+main :: () =
+ let
+ ctx := Render::getContext()
+ in
+ ctx
+ |> drawRect(50, 50, 100, 100)
+ |> drawDepth
+ |> drawBackLines
+ |> drawStar(10)
+
+
+
+
diff --git a/piccode/context/ctx.pics b/piccode/context/ctx.pics
new file mode 100644
index 0000000..72e3ce3
--- /dev/null
+++ b/piccode/context/ctx.pics
@@ -0,0 +1,39 @@
+
+// Module: Context
+// Contains functions for creating contexts
+Context :: module {
+
+ // Function: fromRect
+ // Creates a new context based on the one provided
+ //
+ // Parameters:
+ // ctx - (Reference) The graphic context
+ // x - (Number) The x position
+ // y - (Number) The y position
+ // w - (Number) The width
+ // h - (Number) The height
+ //
+ // Returns:
+ // - (Reference) A new graphics sub-context derived from the given context.
+ fromRect :: (ctx, x, y, w, h) = pic_nat_gfx_from_rect(ctx, x, y, w, h)
+
+ // Function: from
+ // Creates a new context based on the one provided
+ //
+ // Parameters:
+ // ctx - (Reference) The graphic context
+ //
+ // Returns:
+ // - (Reference) A new graphics context derived from the given context.
+ from :: (ctx) = pic_nat_gfx_from(ctx)
+
+ // Function: drop
+ // Frees the given context
+ //
+ // Parameters:
+ // ctx - (Reference) The graphic context
+ //
+ // Returns:
+ // - (Unit) Nothing
+ drop :: (ctx) = pic_nat_gfx_drop(ctx)
+}
diff --git a/piccode/filters/filter.pics b/piccode/filters/filter.pics
new file mode 100644
index 0000000..244eced
--- /dev/null
+++ b/piccode/filters/filter.pics
@@ -0,0 +1,5 @@
+
+Filter :: module {
+ apply :: (filter, image) = pic_nat_filter_apply(filter, image)
+}
+
diff --git a/piccode/filters/metal/brushMetal.pics b/piccode/filters/metal/brushMetal.pics
new file mode 100644
index 0000000..4a846a5
--- /dev/null
+++ b/piccode/filters/metal/brushMetal.pics
@@ -0,0 +1,8 @@
+
+
+BrushMetal :: module {
+ new :: () = pic_nat_brush_metal_new()
+ radius :: (filter, radius) = pic_nat_brush_metal_set_rad(filter, radius)
+ amount :: (filter, radius) = pic_nat_brush_metal_set_amount(filter, radius)
+ shine :: (filter, radius) = pic_nat_brush_metal_set_shine(filter, radius)
+}
diff --git a/piccode/image/image.pics b/piccode/image/image.pics
new file mode 100644
index 0000000..a3dd5cf
--- /dev/null
+++ b/piccode/image/image.pics
@@ -0,0 +1,17 @@
+
+Image :: module {
+ new :: (w, h) = pic_nat_image_new(w, h)
+ newWithType :: (w, h, type) = pic_nat_image_new_typed(w, h, type)
+ getContext :: (img) = pic_nat_image_get_context(img)
+ fromPath :: (path) = pic_nat_image_new_from_path(path)
+
+ resize :: (img, w, h) = pic_nat_image_resize(img, w, h)
+
+ Type :: module {
+ RGB := 1
+ ARGB := 2
+ ARGB_PRE := 3
+ BGR := 4
+ }
+}
+
diff --git a/piccode/pen/draw.pics b/piccode/pen/draw.pics
new file mode 100644
index 0000000..4e9e1fa
--- /dev/null
+++ b/piccode/pen/draw.pics
@@ -0,0 +1,66 @@
+
+Pen :: module {
+
+ // Function: drawLine
+ // Draws a line inside the specified context, based on the provided coordinates
+ //
+ // Parameters:
+ // ctx - (Reference) The graphic context
+ // startx - (Number) The start x position
+ // starty - (Number) The start y position
+ // endx - (Number) The end x position
+ // endy - (Number) The end y position
+ //
+ // Returns:
+ // - (Reference) A modified context with the rendered element.
+ drawLine :: (ctx, startx, starty, endx, endy) = pic_nat_draw_line(ctx, startx, starty, endx, endy)
+
+ // Function: drawRect
+ // Draws a rect inside the specified context, based on the provided coordinates
+ //
+ // Parameters:
+ // ctx - (Reference) The graphic context
+ // x - (Number) The x position
+ // y - (Number) The y position
+ // w - (Number) The width
+ // h - (Number) The height
+ //
+ // Returns:
+ // - (Reference) A modified context with the rendered element.
+ drawRect :: (ctx, x, y, w, h) = pic_nat_draw_rect(ctx, x, y, w, h)
+
+ // Function: drawRoundRect
+ // Draws a rounded rectangle inside the specified context, based on the provided coordinates
+ //
+ // Parameters:
+ // ctx - (Reference) The graphic context
+ // x - (Number) The x position
+ // y - (Number) The y position
+ // w - (Number) The width
+ // h - (Number) The height
+ // aw - (Number) The arc width
+ // ah - (Number) The arc height
+ //
+ // Returns:
+ // - (Reference) A modified context with the rendered element.
+ drawRoundRect :: (ctx, x, y, w, h, aw, ah) = pic_nat_draw_round_rect(ctx, x, y, w, h, aw, ah)
+
+ // Function: drawOval
+ // Draws a rect inside the specified context, based on the provided coordinates
+ //
+ // Parameters:
+ // ctx - (Reference) The graphic context
+ // x - (Number) The x position
+ // y - (Number) The y position
+ // w - (Number) The width
+ // h - (Number) The height
+ //
+ // Returns:
+ // - (Reference) A modified context with the rendered element.
+ drawOval :: (ctx, x, y, w, h) = pic_nat_draw_oval(ctx, x, y, w, h)
+
+ drawImage :: (ctx, image, x, y) = pic_nat_draw_image(ctx, image, x, y)
+
+ drawText :: (ctx, text, x, y) = pic_nat_draw_text(ctx, text, x, y)
+}
+
diff --git a/piccode/render/context.pics b/piccode/render/context.pics
new file mode 100644
index 0000000..3c791c8
--- /dev/null
+++ b/piccode/render/context.pics
@@ -0,0 +1,16 @@
+
+// Module: Render
+// Functions for working with the editor render context
+Render :: module {
+ // Function: getContext
+ // returns the render context (A handle to the Render window on the screen)
+ //
+ // Parameters:
+ // None
+ //
+ // Returns:
+ // - (Reference) a handler to the Graphics2D.
+ getContext :: () = pic_nat_get_gfx()
+
+}
+
diff --git a/pom.xml b/pom.xml
index 79a9f33..4b48f2d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -15,6 +15,15 @@
jitpack.io
https://jitpack.io
+
+
+ true
+ always
+
+
+ always
+
+
@@ -23,14 +32,14 @@
org.antlr
antlr4-runtime
- 4.9.3
+ 4.13.2
com.formdev
flatlaf
- 3.6
+ 3.6.1
@@ -54,7 +63,7 @@
org.bidib.jbidib.com.vldocking
vldocking
- 3.0.10
+ 3.0.11
@@ -66,7 +75,19 @@
com.github.Glimmr-Lang
PiccodeScript
- -SNAPSHOT
+ main-SNAPSHOT
+
+
+
+ com.github.Glimmr-Lang
+ JHLabs
+ main-SNAPSHOT
+
+
+
+ com.github.Glimmr-Lang
+ PiccodePlugin
+ main-SNAPSHOT
@@ -76,7 +97,7 @@
org.junit
junit-bom
- 5.12.2
+ 6.0.0
pom
import
@@ -91,7 +112,7 @@
org.apache.maven.plugins
maven-jar-plugin
- 3.2.0
+ 3.4.2
@@ -104,7 +125,7 @@
org.apache.maven.plugins
maven-assembly-plugin
- 2.4.1
+ 3.7.1
jar-with-dependencies
@@ -130,7 +151,7 @@
org.apache.maven.plugins
maven-enforcer-plugin
- 3.5.0
+ 3.6.2
enforce-versions
@@ -165,7 +186,7 @@
org.graalvm.buildtools
native-maven-plugin
- 0.10.0
+ 0.11.1
true
diff --git a/pytest.ini b/pytest.ini
new file mode 100644
index 0000000..2b397c4
--- /dev/null
+++ b/pytest.ini
@@ -0,0 +1,10 @@
+[tool:pytest]
+testpaths = tests
+python_files = test_*.py
+python_classes = Test*
+python_functions = test_*
+addopts = -v --tb=short --strict-markers
+markers =
+ slow: marks tests as slow (deselect with '-m "not slow"')
+ integration: marks tests as integration tests
+ unit: marks tests as unit tests
\ No newline at end of file
diff --git a/requirements-test.txt b/requirements-test.txt
new file mode 100644
index 0000000..9e14c88
--- /dev/null
+++ b/requirements-test.txt
@@ -0,0 +1,4 @@
+pytest>=7.0.0
+pytest-mock>=3.6.1
+requests>=2.25.1
+pathlib2>=2.3.7; python_version < '3.4'
\ No newline at end of file
diff --git a/run_readme_tests.py b/run_readme_tests.py
new file mode 100644
index 0000000..e2d8d2f
--- /dev/null
+++ b/run_readme_tests.py
@@ -0,0 +1,36 @@
+#!/usr/bin/env python3
+"""
+Simple test runner for README validation tests.
+Run this script to execute all README-related tests.
+"""
+
+import subprocess
+import sys
+import os
+
+def main():
+ """Run the README validation tests."""
+ try:
+ # Change to the repository root directory
+ repo_root = os.path.dirname(os.path.abspath(__file__))
+ os.chdir(repo_root)
+
+ # Run pytest with verbose output
+ result = subprocess.run([
+ sys.executable, "-m", "pytest",
+ "tests/test_readme_validation.py",
+ "-v", "--tb=short"
+ ], capture_output=False)
+
+ return result.returncode
+
+ except FileNotFoundError:
+ print("Error: pytest not found. Please install pytest:")
+ print("pip install -r requirements-test.txt")
+ return 1
+ except Exception as e:
+ print(f"Error running tests: {e}")
+ return 1
+
+if __name__ == "__main__":
+ sys.exit(main())
\ No newline at end of file
diff --git a/src/main/java/org/editor/CanvasFrame.java b/src/main/java/org/editor/CanvasFrame.java
index 7315838..a4a3886 100644
--- a/src/main/java/org/editor/CanvasFrame.java
+++ b/src/main/java/org/editor/CanvasFrame.java
@@ -2,6 +2,7 @@
import com.vlsolutions.swing.docking.DockKey;
import com.vlsolutions.swing.docking.Dockable;
+import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
@@ -21,10 +22,12 @@
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
+import java.util.function.Supplier;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.plaf.basic.BasicGraphicsUtils;
+import org.editor.theme.ThemeManager;
import org.piccode.ast.Ast;
import org.piccode.backend.Compiler;
import org.piccode.rt.Context;
@@ -46,6 +49,7 @@ public class CanvasFrame extends JPanel implements MouseListener, MouseMotionLis
private int lastMouseX, lastMouseY;
private BufferedImage gridImage;
+ private BufferedImage render;
private Point lastDragPoint;
public static Graphics2D gfx = null;
private long lastTime;
@@ -66,17 +70,13 @@ public class CanvasFrame extends JPanel implements MouseListener, MouseMotionLis
private static CanvasFrame _the = null;
private DockKey key = new DockKey("canvas");
- public static String code = null;
- public static String file = null;
- public static boolean start = false;
-
private CanvasFrame() {
super(new BorderLayout());
this.setBackground(new Color(18, 18, 18));
this.addMouseListener(this);
this.addMouseMotionListener(this);
drawGrid();
- setPreferredSize(new Dimension(getWidth(), getHeight()));
+ compile(() -> null);
Timer timer = new Timer(16, new ActionListener() {
public void actionPerformed(ActionEvent e) {
repaint(getVisibleRect());
@@ -108,33 +108,21 @@ protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
drawGrid();
-
g2.drawImage(gridImage, 0, 0, null);
// Smooth rendering
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(Color.BLACK);
- gfx = g2;
- if (start && file != null && code != null) {
- SwingUtilities.invokeLater(() -> {
- AccessFrame.msgs.setText("");
- new Thread(() -> compileFrame())
- .start();
- });
- start = false;
- }
+ if (render != null) {
+ g2.drawImage(render, 0, 0, null);
+ }
drawSelection(g2);
if (showHighlight) {
drawCrosshair(g2);
}
}
- private PiccodeValue compileFrame() {
- var result = Compiler.compile(file, code);
- return result;
- }
-
private void drawSelection(Graphics2D g2) {
if (selecting && selectionStart != null && selectionEnd != null) {
Point start = selectionStart;
@@ -267,12 +255,12 @@ private void drawGrid() {
gridImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = gridImage.createGraphics();
// Fill background
- g2.setColor(Color.WHITE);
+ g2.setColor(ThemeManager.RENDER_BG);
g2.fillRect(0, 0, getWidth(), getHeight());
if (showGrid) {
// Draw grid
- g2.setColor(new Color(230, 230, 230));
+ g2.setColor(ThemeManager.RENDER_GRID);
for (int x = -offsetX % SCALE; x < getWidth(); x += SCALE) {
g2.drawLine(x, 0, x, getHeight());
}
@@ -283,7 +271,7 @@ private void drawGrid() {
if (showRuler) {
// Draw axis numbers
- g2.setColor(Color.GRAY);
+ g2.setColor(ThemeManager.RENDER_GRID);
for (int x = -offsetX % SCALE; x < getWidth(); x += SCALE) {
int value = (x + offsetX) / SCALE;
g2.drawString(Integer.toString(value * SCALE), x + 2, 12);
@@ -299,7 +287,7 @@ private void drawGrid() {
g2.setColor(Color.RED);
g2.drawString("y", 8, getHeight() - 5);
- g2.setColor(Color.GRAY);
+ g2.setColor(ThemeManager.RENDER_TXT2);
int x = (-offsetX % SCALE + offsetX) / SCALE;
int y = (-offsetY % SCALE + offsetY) / SCALE;
x *= SCALE;
@@ -309,8 +297,33 @@ private void drawGrid() {
lastGridOffsetX = offsetX;
lastGridOffsetY = offsetY;
+ g2.dispose();
+ }
+
+ public void compile(Supplier fx) {
+ int width = getWidth();
+ int height = getHeight();
+ if (width <= 0 || height <= 0) {
+ return;
+ }
+
+ render = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+
+ // Get the Graphics2D object
+ gfx = render.createGraphics();
+
+ // Enable transparency by drawing a fully transparent background
+ gfx.setComposite(AlphaComposite.Clear);
+ gfx.fillRect(0, 0, width, height);
+
+ gfx.setColor(Color.BLACK);
+ // Switch back to normal composite mode for drawing
+ gfx.setComposite(AlphaComposite.SrcOver);
+
+ fx.get();
}
+
@Override
public void mouseClicked(MouseEvent e) {
}
diff --git a/src/main/java/org/editor/CodeEditor.java b/src/main/java/org/editor/CodeEditor.java
index d55e319..e37cdc7 100644
--- a/src/main/java/org/editor/CodeEditor.java
+++ b/src/main/java/org/editor/CodeEditor.java
@@ -30,6 +30,7 @@
import org.fife.ui.rsyntaxtextarea.RSyntaxTextAreaHighlighter;
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
import org.fife.ui.rsyntaxtextarea.TextEditorPane;
+import org.fife.ui.rsyntaxtextarea.Theme;
import org.fife.ui.rsyntaxtextarea.TokenMakerFactory;
import org.fife.ui.rsyntaxtextarea.templates.CodeTemplate;
import org.fife.ui.rsyntaxtextarea.templates.StaticCodeTemplate;
@@ -297,4 +298,15 @@ public DockKey getDockKey() {
public Component getComponent() {
return this;
}
+
+ public void setThemeMode(boolean dark) {
+ var themeName = dark ? "monokai" : "vs";
+ try {
+ Theme theme = Theme.load(getClass().getResourceAsStream(
+ "/org/fife/ui/rsyntaxtextarea/themes/" + themeName +".xml"));
+ theme.apply(textArea);
+ } catch (IOException ioe) { // Never happens
+ ioe.printStackTrace();
+ }
+ }
}
diff --git a/src/main/java/org/editor/EditorWindow.java b/src/main/java/org/editor/EditorWindow.java
index be507e1..b43054c 100644
--- a/src/main/java/org/editor/EditorWindow.java
+++ b/src/main/java/org/editor/EditorWindow.java
@@ -43,6 +43,7 @@
import org.editor.panels.FileTreePanel;
import org.editor.panels.PluginsPanel;
import org.editor.panels.VCPanel;
+import org.editor.theme.ThemeManager;
import org.fife.rsta.ui.CollapsibleSectionPanel;
//import org.fife.rsta.ui.DocumentMap;
@@ -72,8 +73,9 @@ public final class EditorWindow extends JFrame implements SearchListener {
private CollapsibleSectionPanel csp;
public static FindDialog findDialog;
public static ReplaceDialog replaceDialog;
- private DockingDesktop desk = new DockingDesktop();
+ public static DockingDesktop desk = new DockingDesktop();
private static CodeEditor selected = null;
+ public static boolean dark = true;
public static EditorWindow the() {
if (win == null) {
@@ -87,17 +89,13 @@ public static EditorWindow the() {
public EditorWindow() {
super("Piccode - DashBoard");
+ ThemeManager.setFlatLaf(dark);
DockingUISettings.getInstance().installUI();
customizeDock();
UIManager.put("Tree.collapsedIcon", UIManager.getIcon("Tree.collapsedIcon"));
UIManager.put("Tree.expandedIcon", UIManager.getIcon("Tree.expandedIcon"));
- try {
- UIManager.setLookAndFeel(new FlatLightLaf());
- } catch (Exception ex) {
- System.err.println("Failed to initialize LaF");
- }
new CodeEditor();
root = getRootPane();
@@ -118,7 +116,7 @@ public EditorWindow() {
if (current.getDockable() instanceof CodeEditor ed) {
if (event.getFutureState().isClosed()) {
- if (removeIfDirty(ed.tabIndex, ed) == false) {
+ if (removeIfNotDirty(ed.tabIndex, ed) == false) {
event.cancel();
}
}
@@ -229,6 +227,8 @@ public EditorWindow() {
win = this;
+ ThemeManager.updateThemes(dark);
+
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(width, height);
this.setLocationRelativeTo(null);
@@ -251,6 +251,8 @@ public static void addTab(ActionEvent e) {
editor.requestFocusInWindow();
current_file.setText(file != null ? file.toString() : "[NONE]");
tabEditors.put(index, editor);
+ ThemeManager.registerEditor(editor);
+ ThemeManager.updateThemes(dark);
// Add first editor normally
if (index == 0) {
@@ -271,6 +273,8 @@ public static void addTab(Path path, Void e) {
editor.requestFocusInWindow();
tabEditors.put(index, editor);
+ ThemeManager.registerEditor(editor);
+ ThemeManager.updateThemes(dark);
// Add first editor normally
if (index == 0) {
win.getContentPane().add(editor);
@@ -339,7 +343,7 @@ private static Component makeTabHeader(JTabbedPane tabs, String title) {
int index = tabs.indexOfTabComponent(tabHeader);
if (index != -1) {
var ed = tabEditors.get(index);
- removeIfDirty(index, ed);
+ removeIfNotDirty(index, ed);
}
});
@@ -364,13 +368,13 @@ public static void removeTab() {
return;
}
- removeIfDirty(index, focused);
+ removeIfNotDirty(index, focused);
}
public static void removeAllTabs() {
var editors = new HashMap<>(tabEditors); // Copy to avoid ConcurrentModificationException
for (var entry : editors.entrySet()) {
- removeIfDirty(entry.getKey(), entry.getValue());
+ removeIfNotDirty(entry.getKey(), entry.getValue());
}
}
@@ -378,7 +382,7 @@ public static int tabsCount() {
return tabEditors.size();
}
- private static boolean removeIfDirty(Integer index, CodeEditor ed) {
+ private static boolean removeIfNotDirty(Integer index, CodeEditor ed) {
if (ed.textArea.isDirty()) {
int result = JOptionPane.showConfirmDialog(win, "File " + ed.filePathTruncated() + " is modified. Save?");
if (result == JOptionPane.OK_OPTION) {
@@ -387,6 +391,7 @@ private static boolean removeIfDirty(Integer index, CodeEditor ed) {
}
win.desk.remove((Dockable) ed); // Actual removal from docking layout
tabEditors.remove(index);
+ ThemeManager.removeEditor(ed);
migrateIndexes();
return true;
diff --git a/src/main/java/org/editor/dialogs/EditorSettingsDialog.form b/src/main/java/org/editor/dialogs/EditorSettingsDialog.form
new file mode 100644
index 0000000..c619847
--- /dev/null
+++ b/src/main/java/org/editor/dialogs/EditorSettingsDialog.form
@@ -0,0 +1,224 @@
+
+
+
diff --git a/src/main/java/org/editor/dialogs/EditorSettingsDialog.java b/src/main/java/org/editor/dialogs/EditorSettingsDialog.java
new file mode 100644
index 0000000..1e4098a
--- /dev/null
+++ b/src/main/java/org/editor/dialogs/EditorSettingsDialog.java
@@ -0,0 +1,181 @@
+/*
+ * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
+ * Click nbfs://nbhost/SystemFileSystem/Templates/GUIForms/JPanel.java to edit this template
+ */
+package org.editor.dialogs;
+
+/**
+ *
+ * @author hexaredecimal
+ */
+public class EditorSettingsDialog extends javax.swing.JPanel {
+
+ /**
+ * Creates new form EditorSettingsDialog
+ */
+ public EditorSettingsDialog() {
+ initComponents();
+ }
+
+ /**
+ * This method is called from within the constructor to initialize the form.
+ * WARNING: Do NOT modify this code. The content of this method is always
+ * regenerated by the Form Editor.
+ */
+ @SuppressWarnings("unchecked")
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+
+ jPanel1 = new javax.swing.JPanel();
+ jLabel1 = new javax.swing.JLabel();
+ jTextField1 = new javax.swing.JTextField();
+ jButton1 = new javax.swing.JButton();
+ jLabel2 = new javax.swing.JLabel();
+ jSpinner1 = new javax.swing.JSpinner();
+ jPanel2 = new javax.swing.JPanel();
+ jCheckBox1 = new javax.swing.JCheckBox();
+ jCheckBox2 = new javax.swing.JCheckBox();
+ jCheckBox3 = new javax.swing.JCheckBox();
+ jPanel3 = new javax.swing.JPanel();
+ jLabel3 = new javax.swing.JLabel();
+ jComboBox1 = new javax.swing.JComboBox<>();
+
+ jPanel1.setBorder(javax.swing.BorderFactory.createTitledBorder("Font"));
+
+ jLabel1.setText("Font");
+
+ jButton1.setText("Pick Font");
+
+ jLabel2.setText("Font Size");
+
+ javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
+ jPanel1.setLayout(jPanel1Layout);
+ jPanel1Layout.setHorizontalGroup(
+ jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel1Layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
+ .addComponent(jLabel2, javax.swing.GroupLayout.DEFAULT_SIZE, 92, Short.MAX_VALUE)
+ .addComponent(jLabel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel1Layout.createSequentialGroup()
+ .addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, 226, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jButton1)
+ .addGap(0, 0, Short.MAX_VALUE))
+ .addComponent(jSpinner1))
+ .addContainerGap())
+ );
+ jPanel1Layout.setVerticalGroup(
+ jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel1Layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel1)
+ .addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jButton1))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel2)
+ .addComponent(jSpinner1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ );
+
+ jPanel2.setBorder(javax.swing.BorderFactory.createTitledBorder("General"));
+
+ jCheckBox1.setText("Show Line Numbers");
+
+ jCheckBox2.setText("Enable Syntax Highlighter");
+
+ jCheckBox3.setText("Enable BookMarks");
+
+ javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2);
+ jPanel2.setLayout(jPanel2Layout);
+ jPanel2Layout.setHorizontalGroup(
+ jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel2Layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jCheckBox2)
+ .addComponent(jCheckBox1)
+ .addComponent(jCheckBox3))
+ .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ );
+ jPanel2Layout.setVerticalGroup(
+ jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel2Layout.createSequentialGroup()
+ .addComponent(jCheckBox2)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jCheckBox1)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jCheckBox3)
+ .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ );
+
+ jPanel3.setBorder(javax.swing.BorderFactory.createTitledBorder("LookAndFeel"));
+
+ jLabel3.setText("Dark/Light Mode");
+
+ jComboBox1.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "Dark Mode", "Light Mode" }));
+
+ javax.swing.GroupLayout jPanel3Layout = new javax.swing.GroupLayout(jPanel3);
+ jPanel3.setLayout(jPanel3Layout);
+ jPanel3Layout.setHorizontalGroup(
+ jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel3Layout.createSequentialGroup()
+ .addContainerGap()
+ .addComponent(jLabel3, javax.swing.GroupLayout.PREFERRED_SIZE, 157, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGap(18, 18, 18)
+ .addComponent(jComboBox1, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addContainerGap())
+ );
+ jPanel3Layout.setVerticalGroup(
+ jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel3Layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
+ .addComponent(jComboBox1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel3))
+ .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ );
+
+ javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+ this.setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(jPanel2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addComponent(jPanel3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addContainerGap())
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jPanel3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ );
+ }// //GEN-END:initComponents
+
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JButton jButton1;
+ private javax.swing.JCheckBox jCheckBox1;
+ private javax.swing.JCheckBox jCheckBox2;
+ private javax.swing.JCheckBox jCheckBox3;
+ private javax.swing.JComboBox jComboBox1;
+ private javax.swing.JLabel jLabel1;
+ private javax.swing.JLabel jLabel2;
+ private javax.swing.JLabel jLabel3;
+ private javax.swing.JPanel jPanel1;
+ private javax.swing.JPanel jPanel2;
+ private javax.swing.JPanel jPanel3;
+ private javax.swing.JSpinner jSpinner1;
+ private javax.swing.JTextField jTextField1;
+ // End of variables declaration//GEN-END:variables
+}
diff --git a/src/main/java/org/editor/dialogs/GeneralSettingsDialog.form b/src/main/java/org/editor/dialogs/GeneralSettingsDialog.form
new file mode 100644
index 0000000..b450678
--- /dev/null
+++ b/src/main/java/org/editor/dialogs/GeneralSettingsDialog.form
@@ -0,0 +1,197 @@
+
+
+
diff --git a/src/main/java/org/editor/dialogs/GeneralSettingsDialog.java b/src/main/java/org/editor/dialogs/GeneralSettingsDialog.java
new file mode 100644
index 0000000..2883926
--- /dev/null
+++ b/src/main/java/org/editor/dialogs/GeneralSettingsDialog.java
@@ -0,0 +1,166 @@
+/*
+ * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
+ * Click nbfs://nbhost/SystemFileSystem/Templates/GUIForms/JPanel.java to edit this template
+ */
+package org.editor.dialogs;
+
+/**
+ *
+ * @author hexaredecimal
+ */
+public class GeneralSettingsDialog extends javax.swing.JPanel {
+
+ /**
+ * Creates new form GeneralSettingsDialog
+ */
+ public GeneralSettingsDialog() {
+ initComponents();
+ }
+
+ /**
+ * This method is called from within the constructor to initialize the form.
+ * WARNING: Do NOT modify this code. The content of this method is always
+ * regenerated by the Form Editor.
+ */
+ @SuppressWarnings("unchecked")
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+
+ jPanel1 = new javax.swing.JPanel();
+ jLabel1 = new javax.swing.JLabel();
+ jTextField1 = new javax.swing.JTextField();
+ jLabel2 = new javax.swing.JLabel();
+ jTextField2 = new javax.swing.JTextField();
+ jButton1 = new javax.swing.JButton();
+ jPanel2 = new javax.swing.JPanel();
+ jLabel3 = new javax.swing.JLabel();
+ jTextField3 = new javax.swing.JTextField();
+ jLabel4 = new javax.swing.JLabel();
+ jTextField4 = new javax.swing.JTextField();
+ jLabel5 = new javax.swing.JLabel();
+
+ jPanel1.setBorder(javax.swing.BorderFactory.createTitledBorder("General"));
+
+ jLabel1.setText("Working Directory");
+
+ jLabel2.setText("Project Name");
+
+ jButton1.setLabel("Update");
+
+ javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
+ jPanel1.setLayout(jPanel1Layout);
+ jPanel1Layout.setHorizontalGroup(
+ jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel1Layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel1Layout.createSequentialGroup()
+ .addComponent(jLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, 150, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jTextField1))
+ .addGroup(jPanel1Layout.createSequentialGroup()
+ .addComponent(jLabel2, javax.swing.GroupLayout.PREFERRED_SIZE, 150, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jTextField2, javax.swing.GroupLayout.DEFAULT_SIZE, 349, Short.MAX_VALUE))
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup()
+ .addGap(0, 0, Short.MAX_VALUE)
+ .addComponent(jButton1)))
+ .addContainerGap())
+ );
+ jPanel1Layout.setVerticalGroup(
+ jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel1Layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel1)
+ .addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel2)
+ .addComponent(jTextField2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 15, Short.MAX_VALUE)
+ .addComponent(jButton1)
+ .addContainerGap())
+ );
+
+ jPanel2.setBorder(javax.swing.BorderFactory.createTitledBorder("Runtime"));
+
+ jLabel3.setText("Entry File");
+
+ jTextField3.setText("./main.pics");
+
+ jLabel4.setText("Arguments");
+
+ jLabel5.setFont(new java.awt.Font("sansserif", 0, 10)); // NOI18N
+ jLabel5.setForeground(new java.awt.Color(204, 204, 204));
+ jLabel5.setText("NOTE: Arguments are separated by a space");
+
+ javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2);
+ jPanel2.setLayout(jPanel2Layout);
+ jPanel2Layout.setHorizontalGroup(
+ jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel2Layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
+ .addComponent(jLabel4, javax.swing.GroupLayout.DEFAULT_SIZE, 94, Short.MAX_VALUE)
+ .addComponent(jLabel3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jTextField3)
+ .addComponent(jTextField4, javax.swing.GroupLayout.Alignment.TRAILING)
+ .addComponent(jLabel5, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ .addContainerGap())
+ );
+ jPanel2Layout.setVerticalGroup(
+ jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel2Layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel3)
+ .addComponent(jTextField3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel4)
+ .addComponent(jTextField4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jLabel5)
+ .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ );
+
+ javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+ this.setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jPanel2, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(jPanel1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ .addContainerGap())
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ );
+ }// //GEN-END:initComponents
+
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JButton jButton1;
+ private javax.swing.JLabel jLabel1;
+ private javax.swing.JLabel jLabel2;
+ private javax.swing.JLabel jLabel3;
+ private javax.swing.JLabel jLabel4;
+ private javax.swing.JLabel jLabel5;
+ private javax.swing.JPanel jPanel1;
+ private javax.swing.JPanel jPanel2;
+ private javax.swing.JTextField jTextField1;
+ private javax.swing.JTextField jTextField2;
+ private javax.swing.JTextField jTextField3;
+ private javax.swing.JTextField jTextField4;
+ // End of variables declaration//GEN-END:variables
+}
diff --git a/src/main/java/org/editor/dialogs/NewProjectDialog.form b/src/main/java/org/editor/dialogs/NewProjectDialog.form
new file mode 100644
index 0000000..fcf699d
--- /dev/null
+++ b/src/main/java/org/editor/dialogs/NewProjectDialog.form
@@ -0,0 +1,376 @@
+
+
+
diff --git a/src/main/java/org/editor/dialogs/NewProjectDialog.java b/src/main/java/org/editor/dialogs/NewProjectDialog.java
new file mode 100644
index 0000000..ad09f00
--- /dev/null
+++ b/src/main/java/org/editor/dialogs/NewProjectDialog.java
@@ -0,0 +1,377 @@
+package org.editor.dialogs;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.swing.JFileChooser;
+
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ *
+ * @author vulture
+ */
+public class NewProjectDialog extends javax.swing.JFrame {
+
+ /**
+ * Creates new form NewProject
+ */
+
+ private File selectedFile;
+
+ public NewProjectDialog() {
+ initComponents();
+ this.setResizable(false);
+ this.setTitle("Create a new project");
+ this.hasError = false;
+ //this.setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
+ }
+
+ /**
+ * This method is called from within the constructor to initialize the form.
+ * WARNING: Do NOT modify this code. The content of this method is always
+ * regenerated by the Form Editor.
+ */
+ @SuppressWarnings("unchecked")
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+
+ jPanel1 = new javax.swing.JPanel();
+ jLabel1 = new javax.swing.JLabel();
+ jLabel2 = new javax.swing.JLabel();
+ jLabel6 = new javax.swing.JLabel();
+ jPanel2 = new javax.swing.JPanel();
+ jPanel3 = new javax.swing.JPanel();
+ jLabel3 = new javax.swing.JLabel();
+ jTextFieldProjectName = new javax.swing.JTextField();
+ jLabel4 = new javax.swing.JLabel();
+ jTextFieldProjectAuthor = new javax.swing.JTextField();
+ jLabelProjectNameError = new javax.swing.JLabel();
+ jLabelProjectAuthorError = new javax.swing.JLabel();
+ jPanel4 = new javax.swing.JPanel();
+ jLabel5 = new javax.swing.JLabel();
+ jTextFieldProjectLocation = new javax.swing.JTextField();
+ jCheckBox1 = new javax.swing.JCheckBox();
+ jButton1 = new javax.swing.JButton();
+ jLabelProjectLocationError = new javax.swing.JLabel();
+ jCheckBox2 = new javax.swing.JCheckBox();
+ jButtonFinish = new javax.swing.JButton();
+ jButtonHelp = new javax.swing.JButton();
+ jButtonCancel = new javax.swing.JButton();
+
+ setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
+ setAlwaysOnTop(true);
+ setLocation(new java.awt.Point(400, 400));
+ setResizable(false);
+
+ jPanel1.setBackground(new java.awt.Color(255, 255, 255));
+ jPanel1.setBorder(javax.swing.BorderFactory.createEtchedBorder());
+
+ jLabel1.setFont(new java.awt.Font("Tahoma", 1, 15)); // NOI18N
+ jLabel1.setText(" STEPS___________");
+
+ jLabel2.setText(" Add project name
and author(s)");
+
+ jLabel6.setFont(new java.awt.Font("sansserif", 0, 10)); // NOI18N
+ jLabel6.setText(" Add project folder name
and create new file system ");
+
+ javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
+ jPanel1.setLayout(jPanel1Layout);
+ jPanel1Layout.setHorizontalGroup(
+ jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel1Layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, 138, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel2, javax.swing.GroupLayout.PREFERRED_SIZE, 138, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel6, javax.swing.GroupLayout.PREFERRED_SIZE, 138, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addContainerGap(18, Short.MAX_VALUE))
+ );
+ jPanel1Layout.setVerticalGroup(
+ jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel1Layout.createSequentialGroup()
+ .addContainerGap()
+ .addComponent(jLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, 44, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jLabel2, javax.swing.GroupLayout.PREFERRED_SIZE, 47, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+ .addComponent(jLabel6, javax.swing.GroupLayout.PREFERRED_SIZE, 47, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ );
+
+ jPanel2.setBorder(javax.swing.BorderFactory.createEtchedBorder());
+
+ jPanel3.setBorder(javax.swing.BorderFactory.createTitledBorder("Project Information"));
+
+ jLabel3.setText("Project name :");
+
+ jLabel4.setText("Project author :");
+
+ jLabelProjectNameError.setForeground(new java.awt.Color(255, 0, 0));
+ jLabelProjectNameError.setText("*");
+ jLabelProjectNameError.setEnabled(false);
+
+ jLabelProjectAuthorError.setForeground(new java.awt.Color(255, 0, 0));
+ jLabelProjectAuthorError.setText("*");
+ jLabelProjectAuthorError.setEnabled(false);
+
+ javax.swing.GroupLayout jPanel3Layout = new javax.swing.GroupLayout(jPanel3);
+ jPanel3.setLayout(jPanel3Layout);
+ jPanel3Layout.setHorizontalGroup(
+ jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel3Layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel3Layout.createSequentialGroup()
+ .addComponent(jLabel4)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED))
+ .addGroup(jPanel3Layout.createSequentialGroup()
+ .addComponent(jLabel3)
+ .addGap(13, 13, 13)))
+ .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
+ .addComponent(jTextFieldProjectName)
+ .addComponent(jTextFieldProjectAuthor, javax.swing.GroupLayout.DEFAULT_SIZE, 254, Short.MAX_VALUE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+ .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jLabelProjectNameError)
+ .addComponent(jLabelProjectAuthorError))
+ .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ );
+ jPanel3Layout.setVerticalGroup(
+ jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel3Layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel3)
+ .addComponent(jTextFieldProjectName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabelProjectNameError))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+ .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel4)
+ .addComponent(jTextFieldProjectAuthor, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabelProjectAuthorError))
+ .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ );
+
+ jPanel4.setBorder(javax.swing.BorderFactory.createTitledBorder("Project Filesystem"));
+
+ jLabel5.setText("Project location :");
+
+ jCheckBox1.setText("Create a new folder");
+
+ jButton1.setText("Select");
+ jButton1.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ jButton1ActionPerformed(evt);
+ }
+ });
+
+ jLabelProjectLocationError.setForeground(new java.awt.Color(255, 0, 0));
+ jLabelProjectLocationError.setText("Invalid project location");
+ jLabelProjectLocationError.setEnabled(false);
+
+ jCheckBox2.setText("Initialize git repo");
+
+ javax.swing.GroupLayout jPanel4Layout = new javax.swing.GroupLayout(jPanel4);
+ jPanel4.setLayout(jPanel4Layout);
+ jPanel4Layout.setHorizontalGroup(
+ jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel4Layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel4Layout.createSequentialGroup()
+ .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
+ .addGroup(jPanel4Layout.createSequentialGroup()
+ .addComponent(jCheckBox1)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(jLabelProjectLocationError))
+ .addGroup(jPanel4Layout.createSequentialGroup()
+ .addComponent(jLabel5)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+ .addComponent(jTextFieldProjectLocation, javax.swing.GroupLayout.PREFERRED_SIZE, 227, javax.swing.GroupLayout.PREFERRED_SIZE)))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+ .addComponent(jButton1))
+ .addComponent(jCheckBox2))
+ .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ );
+ jPanel4Layout.setVerticalGroup(
+ jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel4Layout.createSequentialGroup()
+ .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel5)
+ .addComponent(jTextFieldProjectLocation, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jButton1))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jCheckBox1)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel4Layout.createSequentialGroup()
+ .addComponent(jLabelProjectLocationError)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 4, javax.swing.GroupLayout.PREFERRED_SIZE)))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 2, Short.MAX_VALUE)
+ .addComponent(jCheckBox2)
+ .addContainerGap())
+ );
+
+ jButtonFinish.setText("Finish");
+ jButtonFinish.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ jButtonFinishActionPerformed(evt);
+ }
+ });
+
+ jButtonHelp.setText("Help");
+
+ jButtonCancel.setText("Cancel");
+ jButtonCancel.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ jButtonCancelActionPerformed(evt);
+ }
+ });
+
+ javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2);
+ jPanel2.setLayout(jPanel2Layout);
+ jPanel2Layout.setHorizontalGroup(
+ jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel2Layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jPanel3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(jPanel4, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel2Layout.createSequentialGroup()
+ .addGap(0, 0, Short.MAX_VALUE)
+ .addComponent(jButtonCancel)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jButtonFinish)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jButtonHelp)))
+ .addContainerGap())
+ );
+ jPanel2Layout.setVerticalGroup(
+ jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel2Layout.createSequentialGroup()
+ .addContainerGap()
+ .addComponent(jPanel3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jPanel4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jButtonFinish)
+ .addComponent(jButtonHelp)
+ .addComponent(jButtonCancel))
+ .addContainerGap())
+ );
+
+ javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
+ getContentPane().setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jPanel2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addContainerGap())
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(jPanel2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addContainerGap())
+ );
+
+ pack();
+ }// //GEN-END:initComponents
+
+ private void jButtonCancelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonCancelActionPerformed
+ this.dispose();
+ }//GEN-LAST:event_jButtonCancelActionPerformed
+
+ private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed
+ JFileChooser fileChooser = new JFileChooser();
+ fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
+ int option = fileChooser.showOpenDialog(null);
+
+ if (option == JFileChooser.APPROVE_OPTION) {
+ selectedFile = fileChooser.getSelectedFile();
+ jTextFieldProjectLocation.setText(selectedFile.getPath());
+ }
+ }//GEN-LAST:event_jButton1ActionPerformed
+
+ private void jButtonFinishActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonFinishActionPerformed
+ if (jTextFieldProjectName.getText().isEmpty()) {
+ jLabelProjectNameError.setEnabled(true);
+ return;
+ } else {
+ jLabelProjectNameError.setEnabled(false);
+ }
+
+ if (jTextFieldProjectAuthor.getText().isEmpty()) {
+ jLabelProjectAuthorError.setEnabled(true);
+ return;
+ } else {
+ jLabelProjectAuthorError.setEnabled(false);
+ }
+
+ if(jTextFieldProjectLocation.getText().isEmpty()) {
+ jLabelProjectLocationError.setEnabled(true);
+ jLabelProjectLocationError.setText("Invalid project location");
+ } else {
+
+ if (selectedFile != null && selectedFile.exists() == false) {
+ jLabelProjectLocationError.setEnabled(true);
+ jLabelProjectLocationError.setText("Selected directory does not exist");
+ return;
+ } else if (selectedFile == null) {
+ File tmp = new File(jTextFieldProjectLocation.getText());
+ if(tmp.exists() == false) {
+ jLabelProjectLocationError.setEnabled(true);
+ jLabelProjectLocationError.setText("Selected directory does not exist");
+ return;
+ }
+ } else {
+ // continue by disableing the errors
+ jLabelProjectLocationError.setText("Invalid project location");
+ jLabelProjectLocationError.setEnabled(false);
+ }
+ }
+
+ String projectName = jTextFieldProjectName.getText();
+ String projectAuthor = jTextFieldProjectAuthor.getText();
+ String projectPath = jTextFieldProjectLocation.getText();
+
+
+
+ }//GEN-LAST:event_jButtonFinishActionPerformed
+
+ private boolean hasError;
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JButton jButton1;
+ private javax.swing.JButton jButtonCancel;
+ private javax.swing.JButton jButtonFinish;
+ private javax.swing.JButton jButtonHelp;
+ private javax.swing.JCheckBox jCheckBox1;
+ private javax.swing.JCheckBox jCheckBox2;
+ private javax.swing.JLabel jLabel1;
+ private javax.swing.JLabel jLabel2;
+ private javax.swing.JLabel jLabel3;
+ private javax.swing.JLabel jLabel4;
+ private javax.swing.JLabel jLabel5;
+ private javax.swing.JLabel jLabel6;
+ private javax.swing.JLabel jLabelProjectAuthorError;
+ private javax.swing.JLabel jLabelProjectLocationError;
+ private javax.swing.JLabel jLabelProjectNameError;
+ private javax.swing.JPanel jPanel1;
+ private javax.swing.JPanel jPanel2;
+ private javax.swing.JPanel jPanel3;
+ private javax.swing.JPanel jPanel4;
+ private javax.swing.JTextField jTextFieldProjectAuthor;
+ private javax.swing.JTextField jTextFieldProjectLocation;
+ private javax.swing.JTextField jTextFieldProjectName;
+ // End of variables declaration//GEN-END:variables
+}
diff --git a/src/main/java/org/editor/dialogs/SettingsDialog.form b/src/main/java/org/editor/dialogs/SettingsDialog.form
new file mode 100644
index 0000000..a459d89
--- /dev/null
+++ b/src/main/java/org/editor/dialogs/SettingsDialog.form
@@ -0,0 +1,79 @@
+
+
+
diff --git a/src/main/java/org/editor/dialogs/SettingsDialog.java b/src/main/java/org/editor/dialogs/SettingsDialog.java
new file mode 100644
index 0000000..072d6ec
--- /dev/null
+++ b/src/main/java/org/editor/dialogs/SettingsDialog.java
@@ -0,0 +1,82 @@
+/*
+ * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
+ * Click nbfs://nbhost/SystemFileSystem/Templates/GUIForms/JPanel.java to edit this template
+ */
+package org.editor.dialogs;
+
+/**
+ *
+ * @author hexaredecimal
+ */
+public class SettingsDialog extends javax.swing.JPanel {
+
+ /**
+ * Creates new form SettingsDialog
+ */
+ public SettingsDialog() {
+ initComponents();
+ }
+
+ /**
+ * This method is called from within the constructor to initialize the form.
+ * WARNING: Do NOT modify this code. The content of this method is always
+ * regenerated by the Form Editor.
+ */
+ @SuppressWarnings("unchecked")
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+
+ jPanel1 = new javax.swing.JPanel();
+ jTabbedPane1 = new javax.swing.JTabbedPane();
+ jButton1 = new javax.swing.JButton();
+ jButton2 = new javax.swing.JButton();
+
+ javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
+ jPanel1.setLayout(jPanel1Layout);
+ jPanel1Layout.setHorizontalGroup(
+ jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jTabbedPane1)
+ );
+ jPanel1Layout.setVerticalGroup(
+ jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel1Layout.createSequentialGroup()
+ .addComponent(jTabbedPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 303, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGap(0, 0, Short.MAX_VALUE))
+ );
+
+ jButton1.setText("jButton1");
+
+ jButton2.setText("jButton2");
+
+ javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+ this.setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+ .addContainerGap(333, Short.MAX_VALUE)
+ .addComponent(jButton2)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jButton1)
+ .addContainerGap())
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jButton1)
+ .addComponent(jButton2))
+ .addGap(0, 13, Short.MAX_VALUE))
+ );
+ }// //GEN-END:initComponents
+
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JButton jButton1;
+ private javax.swing.JButton jButton2;
+ private javax.swing.JPanel jPanel1;
+ private javax.swing.JTabbedPane jTabbedPane1;
+ // End of variables declaration//GEN-END:variables
+}
diff --git a/src/main/java/org/editor/events/AccessEvents.java b/src/main/java/org/editor/events/AccessEvents.java
index 5b2f6da..2a2e46d 100644
--- a/src/main/java/org/editor/events/AccessEvents.java
+++ b/src/main/java/org/editor/events/AccessEvents.java
@@ -29,10 +29,9 @@ public static void compileAndRender(ActionEvent e) {
var file = ed.file.toString();
var code = ed.textArea.getText();
- CanvasFrame.file = file;
- CanvasFrame.code = code;
- CanvasFrame.start = true;
+ AccessFrame.msgs.setText("");
AccessFrame.writeSuccess("Compilation started: ");
+ CanvasFrame.the().compile(() -> Compiler.compile(file, code));
}
public static void compile(ActionEvent e) {
diff --git a/src/main/java/org/editor/events/MenuEvents.java b/src/main/java/org/editor/events/MenuEvents.java
index 3b04f33..50a28b8 100644
--- a/src/main/java/org/editor/events/MenuEvents.java
+++ b/src/main/java/org/editor/events/MenuEvents.java
@@ -23,118 +23,117 @@
*/
public class MenuEvents {
- public static void gotoLineEvent(ActionEvent e) {
- var findDialog = EditorWindow.findDialog;
- var replaceDialog = EditorWindow.replaceDialog;
- if (findDialog.isVisible()) {
- findDialog.setVisible(false);
- }
- if (replaceDialog.isVisible()) {
- replaceDialog.setVisible(false);
- }
- GoToDialog dialog = new GoToDialog(EditorWindow.win);
-
- var ed = EditorWindow.getSelectedEditor();
- if (ed == null) {
- return;
- }
+ public static void gotoLineEvent(ActionEvent e) {
+ var findDialog = EditorWindow.findDialog;
+ var replaceDialog = EditorWindow.replaceDialog;
+ if (findDialog.isVisible()) {
+ findDialog.setVisible(false);
+ }
+ if (replaceDialog.isVisible()) {
+ replaceDialog.setVisible(false);
+ }
+ GoToDialog dialog = new GoToDialog(EditorWindow.win);
+
+ var ed = EditorWindow.getSelectedEditor();
+ if (ed == null) {
+ return;
+ }
var textArea = ed.textArea;
- dialog.setMaxLineNumberAllowed(textArea.getLineCount());
- dialog.setVisible(true);
- int line = dialog.getLineNumber();
- if (line > 0) {
- try {
- textArea.setCaretPosition(textArea.getLineStartOffset(line - 1));
- } catch (BadLocationException ble) { // Never happens
- UIManager.getLookAndFeel().provideErrorFeedback(textArea);
- ble.printStackTrace();
- }
- }
- }
-
- static void replaceEvent(ActionEvent e) {
- var replaceDialog = EditorWindow.replaceDialog;
- var findDialog = EditorWindow.findDialog;
- if (findDialog.isVisible()) {
- findDialog.setVisible(false);
- }
- replaceDialog.setVisible(true);
- }
-
- static void findEvent(ActionEvent e) {
- var replaceDialog = EditorWindow.replaceDialog;
- var findDialog = EditorWindow.findDialog;
- if (replaceDialog.isVisible()) {
- replaceDialog.setVisible(false);
- }
- findDialog.setVisible(true);
- }
-
- public static void aboutDialog(ActionEvent e) {
- new AboutDialog(EditorWindow.win);
- }
-
- public static void closeTab(ActionEvent e) {
- EditorWindow.removeTab();
- }
-
- public static void closeAllTabs(ActionEvent e) {
- EditorWindow.removeAllTabs();
- }
-
- static void openFile(ActionEvent e) {
- // TODO: Use the System object to get the current pwd
- var fileChooser = new JFileChooser(".");
- fileChooser.setFileFilter(FileFilter.mdFilter);
- fileChooser.setFileFilter(FileFilter.picsFilter);
-
- int status = fileChooser.showOpenDialog(EditorWindow.win);
- if (status != JFileChooser.APPROVE_OPTION) {
- return;
- }
-
- var fp = fileChooser.getSelectedFile();
- var path = fp.toPath();
- EditorWindow.addTab(path, null);
- }
-
- static void saveFile(ActionEvent e) {
- if (EditorWindow.tabsCount() == 0) {
- return;
- }
- var ed = EditorWindow.getSelectedEditor();
- if (ed == null) {
- return;
- }
- ed.saveFile();
- }
-
- static void saveFileAs(ActionEvent e) {
- if (EditorWindow.tabsCount() == 1) {
- return;
- }
- var ed = EditorWindow.getSelectedEditor();
- if (ed == null) {
- return;
- }
- ed.saveFileAs();
- }
-
- static void saveAllFiles(ActionEvent e) {
- EditorWindow.saveAll();
- }
-
- static void closeFile(ActionEvent e) {
- closeTab(e);
- }
-
- static void quit(ActionEvent e) {
- closeAllTabs(e);
- System.exit(0);
- }
-
- static void newFile(ActionEvent e) {
- // TODO: Use a file creator dialog in the future
- EditorWindow.addTab(e);
- }
+ dialog.setMaxLineNumberAllowed(textArea.getLineCount());
+ dialog.setVisible(true);
+ int line = dialog.getLineNumber();
+ if (line > 0) {
+ try {
+ textArea.setCaretPosition(textArea.getLineStartOffset(line - 1));
+ } catch (BadLocationException ble) { // Never happens
+ UIManager.getLookAndFeel().provideErrorFeedback(textArea);
+ ble.printStackTrace();
+ }
+ }
+ }
+
+ static void replaceEvent(ActionEvent e) {
+ var replaceDialog = EditorWindow.replaceDialog;
+ var findDialog = EditorWindow.findDialog;
+ if (findDialog.isVisible()) {
+ findDialog.setVisible(false);
+ }
+ replaceDialog.setVisible(true);
+ }
+
+ static void findEvent(ActionEvent e) {
+ var replaceDialog = EditorWindow.replaceDialog;
+ var findDialog = EditorWindow.findDialog;
+ if (replaceDialog.isVisible()) {
+ replaceDialog.setVisible(false);
+ }
+ findDialog.setVisible(true);
+ }
+
+ public static void aboutDialog(ActionEvent e) {
+ new AboutDialog(EditorWindow.win);
+ }
+
+ static void closeTab(ActionEvent e) {
+ EditorWindow.removeTab();
+ }
+
+ public static void closeAllTabs(ActionEvent e) {
+ EditorWindow.removeAllTabs();
+ }
+
+ static void openFile(ActionEvent e) {
+ var fileChooser = new JFileChooser(System.getProperty("user.dir"));
+ fileChooser.setFileFilter(FileFilter.mdFilter);
+ fileChooser.setFileFilter(FileFilter.picsFilter);
+
+ int status = fileChooser.showOpenDialog(EditorWindow.win);
+ if (status != JFileChooser.APPROVE_OPTION) {
+ return;
+ }
+
+ var fp = fileChooser.getSelectedFile();
+ var path = fp.toPath();
+ EditorWindow.addTab(path, null);
+ }
+
+ static void saveFile(ActionEvent e) {
+ if (EditorWindow.tabsCount() == 0) {
+ return;
+ }
+ var ed = EditorWindow.getSelectedEditor();
+ if (ed == null) {
+ return;
+ }
+ ed.saveFile();
+ }
+
+ static void saveFileAs(ActionEvent e) {
+ if (EditorWindow.tabsCount() == 1) {
+ return;
+ }
+ var ed = EditorWindow.getSelectedEditor();
+ if (ed == null) {
+ return;
+ }
+ ed.saveFileAs();
+ }
+
+ static void saveAllFiles(ActionEvent e) {
+ EditorWindow.saveAll();
+ }
+
+ static void closeFile(ActionEvent e) {
+ closeTab(e);
+ }
+
+ static void quit(ActionEvent e) {
+ closeAllTabs(e);
+ System.exit(0);
+ }
+
+ static void newFile(ActionEvent e) {
+ // TODO: Use a file creator dialog in the future
+ EditorWindow.addTab(e);
+ }
}
diff --git a/src/main/java/org/editor/nativemods/PiccodeBrushedMetalFilterModule.java b/src/main/java/org/editor/nativemods/PiccodeBrushedMetalFilterModule.java
new file mode 100644
index 0000000..5b46f25
--- /dev/null
+++ b/src/main/java/org/editor/nativemods/PiccodeBrushedMetalFilterModule.java
@@ -0,0 +1,112 @@
+package org.editor.nativemods;
+
+import com.jhlabs.image.BrushedMetalFilter;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.util.HashMap;
+import java.util.List;
+import org.editor.CanvasFrame;
+import org.piccode.rt.Context;
+import org.piccode.rt.PiccodeException;
+import org.piccode.rt.PiccodeNumber;
+import org.piccode.rt.PiccodeObject;
+import org.piccode.rt.PiccodeReference;
+import org.piccode.rt.PiccodeString;
+import org.piccode.rt.PiccodeUnit;
+import org.piccode.rt.PiccodeValue;
+import org.piccode.rt.PiccodeValue.Type;
+import org.piccode.rt.modules.NativeFunctionFactory;
+
+/**
+ *
+ * @author hexaredecimal
+ */
+public class PiccodeBrushedMetalFilterModule {
+
+ public static void addFunctions() {
+ NativeFunctionFactory.create("brush_metal_new", List.of(), (args, namedArgs, frame) -> {
+ var bmFilter = new BrushedMetalFilter();
+ return new PiccodeReference(bmFilter);
+ }, null);
+
+ NativeFunctionFactory.create("brush_metal_set_rad", List.of("filter", "rad"), (args, namedArgs, frame) -> {
+ var _filter = namedArgs.get("filter");
+ var rad = namedArgs.get("rad");
+
+ var ctx = frame == null
+ ? Context.top
+ : Context.getContextAt(frame);
+ var caller = ctx.getTopFrame().caller;
+
+ PiccodeValue.verifyType(caller, _filter, Type.REFERENCE);
+ PiccodeValue.verifyType(caller, rad, Type.NUMBER);
+
+ var obj = (PiccodeReference) _filter;
+ var _filter_object = obj.deref();
+ if (!(_filter_object instanceof BrushedMetalFilter)) {
+ throw new PiccodeException(caller.file, caller.line, caller.column, "Filter is not a correct object.");
+ }
+
+ var filter = (BrushedMetalFilter) _filter_object;
+ var radius = (int) (double) ((PiccodeNumber) rad).raw();
+
+ filter.setRadius(radius);
+
+ return new PiccodeReference(_filter_object);
+ }, null);
+
+ NativeFunctionFactory.create("brush_metal_set_amount", List.of("filter", "amount"), (args, namedArgs, frame) -> {
+ var _filter = namedArgs.get("filter");
+ var amount = namedArgs.get("amount");
+
+ var ctx = frame == null
+ ? Context.top
+ : Context.getContextAt(frame);
+ var caller = ctx.getTopFrame().caller;
+
+ PiccodeValue.verifyType(caller, _filter, Type.REFERENCE);
+ PiccodeValue.verifyType(caller, amount, Type.NUMBER);
+
+ var obj = (PiccodeReference) _filter;
+ var _filter_object = obj.deref();
+ if (!(_filter_object instanceof BrushedMetalFilter)) {
+ throw new PiccodeException(caller.file, caller.line, caller.column, "Filter is not a correct object.");
+ }
+
+ var filter = (BrushedMetalFilter) _filter_object;
+ var _amount = (float) (double) ((PiccodeNumber) amount).raw();
+
+ filter.setAmount(_amount);
+
+ return new PiccodeReference(_filter_object);
+ }, null);
+
+ NativeFunctionFactory.create("brush_metal_set_shine", List.of("filter", "shine"), (args, namedArgs, frame) -> {
+ var _filter = namedArgs.get("filter");
+ var shine = namedArgs.get("shine");
+
+ var ctx = frame == null
+ ? Context.top
+ : Context.getContextAt(frame);
+ var caller = ctx.getTopFrame().caller;
+
+ PiccodeValue.verifyType(caller, _filter, Type.REFERENCE);
+ PiccodeValue.verifyType(caller, shine, Type.NUMBER);
+
+ var obj = (PiccodeReference) _filter;
+ var _filter_object = obj.deref();
+ if (!(_filter_object instanceof BrushedMetalFilter)) {
+ throw new PiccodeException(caller.file, caller.line, caller.column, "Filter is not a correct object.");
+ }
+
+ var filter = (BrushedMetalFilter) _filter_object;
+ var _shine = (float) (double) ((PiccodeNumber) shine).raw();
+
+ filter.setShine(_shine);
+
+ return new PiccodeReference(_filter_object);
+ }, null);
+
+ }
+
+}
diff --git a/src/main/java/org/editor/nativemods/PiccodeFilterModule.java b/src/main/java/org/editor/nativemods/PiccodeFilterModule.java
new file mode 100644
index 0000000..278ae3d
--- /dev/null
+++ b/src/main/java/org/editor/nativemods/PiccodeFilterModule.java
@@ -0,0 +1,57 @@
+package org.editor.nativemods;
+
+import java.awt.image.BufferedImage;
+import java.awt.image.BufferedImageOp;
+import java.util.List;
+import org.piccode.rt.Context;
+import org.piccode.rt.PiccodeException;
+import org.piccode.rt.PiccodeReference;
+import org.piccode.rt.PiccodeValue;
+import org.piccode.rt.PiccodeValue.Type;
+import org.piccode.rt.modules.NativeFunctionFactory;
+
+/**
+ *
+ * @author hexaredecimal
+ */
+public class PiccodeFilterModule {
+
+ public static void addFunctions() {
+ NativeFunctionFactory.create("filter_apply", List.of("filter", "image"), (args, namedArgs, frame) -> {
+ var _filter = namedArgs.get("filter");
+ var image = namedArgs.get("image");
+
+ var ctx = frame == null
+ ? Context.top
+ : Context.getContextAt(frame);
+ var caller = ctx.getTopFrame().caller;
+
+ PiccodeValue.verifyType(caller, _filter, Type.REFERENCE);
+ PiccodeValue.verifyType(caller, image, Type.REFERENCE);
+
+ var obj = (PiccodeReference) _filter;
+ var img = (PiccodeReference) image;
+ var _filter_object = obj.deref();
+ var _buffered_image = img.deref();
+
+ if (!(_filter_object instanceof BufferedImageOp)) {
+ throw new PiccodeException(caller.file, caller.line, caller.column, "Filter is not a correct object.");
+ }
+
+ if (!(_buffered_image instanceof BufferedImage)) {
+ throw new PiccodeException(caller.file, caller.line, caller.column, "Expected a buffer image. Found " + _buffered_image);
+ }
+
+
+ var filter = (BufferedImageOp) _filter_object;
+ var _image = (BufferedImage) _buffered_image;
+
+ var result = filter.filter(_image, null);
+
+ return new PiccodeReference(result);
+ }, null);
+
+
+ }
+
+}
diff --git a/src/main/java/org/editor/nativemods/PiccodeGfxModule.java b/src/main/java/org/editor/nativemods/PiccodeGfxModule.java
new file mode 100644
index 0000000..6218fb8
--- /dev/null
+++ b/src/main/java/org/editor/nativemods/PiccodeGfxModule.java
@@ -0,0 +1,106 @@
+package org.editor.nativemods;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.util.HashMap;
+import java.util.List;
+import org.editor.CanvasFrame;
+import org.piccode.rt.Context;
+import org.piccode.rt.PiccodeException;
+import org.piccode.rt.PiccodeNumber;
+import org.piccode.rt.PiccodeObject;
+import org.piccode.rt.PiccodeReference;
+import org.piccode.rt.PiccodeString;
+import org.piccode.rt.PiccodeUnit;
+import org.piccode.rt.PiccodeValue;
+import org.piccode.rt.PiccodeValue.Type;
+import org.piccode.rt.modules.NativeFunctionFactory;
+
+/**
+ *
+ * @author hexaredecimal
+ */
+public class PiccodeGfxModule {
+
+ public static void addFunctions() {
+ NativeFunctionFactory.create("get_gfx", List.of(), (args, namedArgs, frame) -> {
+ var gfx = CanvasFrame.gfx;
+ return new PiccodeReference(gfx);
+ }, null);
+
+ NativeFunctionFactory.create("gfx_from_rect", List.of("ctx", "x", "y", "w", "h"), (args, namedArgs, frame) -> {
+ var _ctx = namedArgs.get("ctx");
+ var x = namedArgs.get("x");
+ var y = namedArgs.get("y");
+ var w = namedArgs.get("w");
+ var h = namedArgs.get("h");
+
+ var ctx = frame == null
+ ? Context.top
+ : Context.getContextAt(frame);
+ var caller = ctx.getTopFrame().caller;
+
+ PiccodeValue.verifyType(caller, _ctx, Type.REFERENCE);
+ PiccodeValue.verifyType(caller, x, Type.NUMBER);
+ PiccodeValue.verifyType(caller, y, Type.NUMBER);
+ PiccodeValue.verifyType(caller, w, Type.NUMBER);
+ PiccodeValue.verifyType(caller, h, Type.NUMBER);
+
+ var obj = (PiccodeReference) _ctx;
+ var _gfx = obj.deref();
+ if (!(_gfx instanceof Graphics2D)) {
+ throw new PiccodeException(caller.file, caller.line, caller.column, "Context is not a correct object. Expected Graphics2D");
+ }
+
+ var gfx = (Graphics2D) _gfx;
+ var _x = (int) (double) ((PiccodeNumber) x).raw();
+ var _y = (int) (double) ((PiccodeNumber) y).raw();
+ var _w = (int) (double) ((PiccodeNumber) w).raw();
+ var _h = (int) (double) ((PiccodeNumber) h).raw();
+
+ var gfx2 = (Graphics2D) gfx.create(_x, _y, _w, _h);
+ return new PiccodeReference(gfx2);
+ }, null);
+ NativeFunctionFactory.create("gfx_from", List.of("ctx"), (args, namedArgs, frame) -> {
+ var _ctx = namedArgs.get("ctx");
+
+ var ctx = frame == null
+ ? Context.top
+ : Context.getContextAt(frame);
+ var caller = ctx.getTopFrame().caller;
+
+ PiccodeValue.verifyType(caller, _ctx, Type.REFERENCE);
+ var obj = (PiccodeReference) _ctx;
+ var _gfx = obj.deref();
+ if (!(_gfx instanceof Graphics2D)) {
+ throw new PiccodeException(caller.file, caller.line, caller.column, "Context is not a correct object. Expected Graphics2D");
+ }
+
+ var gfx = (Graphics2D) _gfx;
+
+ var gfx2 = (Graphics2D) gfx.create();
+ return new PiccodeReference(gfx2);
+ }, null);
+
+ NativeFunctionFactory.create("gfx_drop", List.of("ctx"), (args, namedArgs, frame) -> {
+ var _ctx = namedArgs.get("ctx");
+
+ var ctx = frame == null
+ ? Context.top
+ : Context.getContextAt(frame);
+ var caller = ctx.getTopFrame().caller;
+
+ PiccodeValue.verifyType(caller, _ctx, Type.REFERENCE);
+ var obj = (PiccodeReference) _ctx;
+ var _gfx = obj.deref();
+ if (!(_gfx instanceof Graphics2D)) {
+ throw new PiccodeException(caller.file, caller.line, caller.column, "Context is not a correct object. Expected Graphics2D");
+ }
+
+ var gfx = (Graphics2D) _gfx;
+ gfx.dispose();
+ return new PiccodeUnit();
+ }, null);
+ }
+
+}
diff --git a/src/main/java/org/editor/nativemods/PiccodeImageModule.java b/src/main/java/org/editor/nativemods/PiccodeImageModule.java
new file mode 100644
index 0000000..3d895ce
--- /dev/null
+++ b/src/main/java/org/editor/nativemods/PiccodeImageModule.java
@@ -0,0 +1,148 @@
+package org.editor.nativemods;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import javax.imageio.ImageIO;
+import org.editor.CanvasFrame;
+import org.editor.icons.ImageLoader;
+import org.piccode.rt.Context;
+import org.piccode.rt.PiccodeException;
+import org.piccode.rt.PiccodeNumber;
+import org.piccode.rt.PiccodeObject;
+import org.piccode.rt.PiccodeReference;
+import org.piccode.rt.PiccodeString;
+import org.piccode.rt.PiccodeUnit;
+import org.piccode.rt.PiccodeValue;
+import org.piccode.rt.PiccodeValue.Type;
+import org.piccode.rt.modules.NativeFunctionFactory;
+
+/**
+ *
+ * @author hexaredecimal
+ */
+public class PiccodeImageModule {
+
+ public static void addFunctions() {
+
+ NativeFunctionFactory.create("image_new", List.of("w", "h"), (args, namedArgs, frame) -> {
+ var w = namedArgs.get("w");
+ var h = namedArgs.get("h");
+
+ var ctx = frame == null ?
+ Context.top
+ : Context.getContextAt(frame);
+ var caller = ctx.getTopFrame().caller;
+
+ PiccodeValue.verifyType(caller, w, Type.NUMBER);
+ PiccodeValue.verifyType(caller, h, Type.NUMBER);
+
+ var _w = (int) (double) ((PiccodeNumber) w).raw();
+ var _h = (int) (double) ((PiccodeNumber) h).raw();
+
+ var image = new BufferedImage(_w, _h, BufferedImage.TYPE_INT_ARGB);
+ return new PiccodeReference(image);
+ }, null);
+
+ NativeFunctionFactory.create("image_new_typed", List.of("w", "h", "type"), (args, namedArgs, frame) -> {
+ var w = namedArgs.get("w");
+ var h = namedArgs.get("h");
+ var type = namedArgs.get("type");
+
+ var ctx = frame == null ?
+ Context.top
+ : Context.getContextAt(frame);
+ var caller = ctx.getTopFrame().caller;
+
+ PiccodeValue.verifyType(caller, w, Type.NUMBER);
+ PiccodeValue.verifyType(caller, h, Type.NUMBER);
+ PiccodeValue.verifyType(caller, type, Type.NUMBER);
+
+ var _w = (int) (double) ((PiccodeNumber) w).raw();
+ var _h = (int) (double) ((PiccodeNumber) h).raw();
+ var _type = (int) (double) ((PiccodeNumber) type).raw();
+
+ var image = new BufferedImage(_w, _h, _type);
+ return new PiccodeReference(image);
+ }, null);
+
+ NativeFunctionFactory.create("image_new_from_path", List.of("path"), (args, namedArgs, frame) -> {
+ var path = namedArgs.get("path");
+
+ var ctx = frame == null ?
+ Context.top
+ : Context.getContextAt(frame);
+ var caller = ctx.getTopFrame().caller;
+
+ PiccodeValue.verifyType(caller, path, Type.STRING);
+
+ try {
+ BufferedImage image = ImageIO.read(new File(path.raw().toString()));
+ return new PiccodeReference(image);
+ } catch (IOException ex) {
+ var def = (BufferedImage) ImageLoader.getImage(-1);
+ return new PiccodeReference(def);
+ }
+ }, null);
+
+ NativeFunctionFactory.create("image_resize", List.of("img" ,"w", "h"), (args, namedArgs, frame) -> {
+ var img = namedArgs.get("img");
+ var w = namedArgs.get("w");
+ var h = namedArgs.get("h");
+
+ var ctx = frame == null ?
+ Context.top
+ : Context.getContextAt(frame);
+ var caller = ctx.getTopFrame().caller;
+
+ PiccodeValue.verifyType(caller, img, Type.REFERENCE);
+ PiccodeValue.verifyType(caller, w, Type.NUMBER);
+ PiccodeValue.verifyType(caller, h, Type.NUMBER);
+
+ var _buffered_image = ((PiccodeReference)img).deref();
+
+ if (!(_buffered_image instanceof BufferedImage)) {
+ throw new PiccodeException(caller.file, caller.line, caller.column, "Expected a buffer image. Found " + _buffered_image);
+ }
+
+ var bufferedmage = (BufferedImage) _buffered_image;
+
+ var _w = (int) (double) ((PiccodeNumber) w).raw();
+ var _h = (int) (double) ((PiccodeNumber) h).raw();
+
+ Image resultingImage = bufferedmage.getScaledInstance(_w, _h, Image.SCALE_DEFAULT);
+ BufferedImage outputImage = new BufferedImage(_w, _h, BufferedImage.TYPE_INT_RGB);
+ outputImage.getGraphics().drawImage(resultingImage, 0, 0, null);
+
+ return new PiccodeReference(outputImage);
+ }, null);
+
+ NativeFunctionFactory.create("image_get_context", List.of("img"), (args, namedArgs, frame) -> {
+ var img = namedArgs.get("img");
+
+ var ctx = frame == null ?
+ Context.top
+ : Context.getContextAt(frame);
+ var caller = ctx.getTopFrame().caller;
+
+ PiccodeValue.verifyType(caller, img, Type.REFERENCE);
+
+ var _buffered_image = ((PiccodeReference)img).deref();
+
+ if (!(_buffered_image instanceof BufferedImage)) {
+ throw new PiccodeException(caller.file, caller.line, caller.column, "Expected a buffer image. Found " + _buffered_image);
+ }
+
+ var bufferedmage = (BufferedImage) _buffered_image;
+ var gfx = bufferedmage.createGraphics();
+ return new PiccodeReference(gfx);
+ }, null);
+
+ }
+
+}
diff --git a/src/main/java/org/editor/nativemods/PiccodePenModule.java b/src/main/java/org/editor/nativemods/PiccodePenModule.java
new file mode 100644
index 0000000..7c91b5e
--- /dev/null
+++ b/src/main/java/org/editor/nativemods/PiccodePenModule.java
@@ -0,0 +1,235 @@
+package org.editor.nativemods;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+import java.util.HashMap;
+import java.util.List;
+import org.editor.CanvasFrame;
+import org.piccode.rt.Context;
+import org.piccode.rt.PiccodeException;
+import org.piccode.rt.PiccodeNumber;
+import org.piccode.rt.PiccodeObject;
+import org.piccode.rt.PiccodeReference;
+import org.piccode.rt.PiccodeString;
+import org.piccode.rt.PiccodeUnit;
+import org.piccode.rt.PiccodeValue;
+import org.piccode.rt.PiccodeValue.Type;
+import org.piccode.rt.modules.NativeFunctionFactory;
+
+/**
+ *
+ * @author hexaredecimal
+ */
+public class PiccodePenModule {
+
+ public static void addFunctions() {
+
+ NativeFunctionFactory.create("draw_line", List.of("ctx", "x1", "y1", "x2", "y2"), (args, namedArgs, frame) -> {
+ var _ctx = namedArgs.get("ctx");
+ var _x1 = namedArgs.get("x1");
+ var _y1 = namedArgs.get("y1");
+ var _x2 = namedArgs.get("x2");
+ var _y2 = namedArgs.get("y2");
+
+ var ctx = frame == null ?
+ Context.top
+ : Context.getContextAt(frame);
+ var caller = ctx.getTopFrame().caller;
+
+ PiccodeValue.verifyType(caller, _ctx, Type.REFERENCE);
+ PiccodeValue.verifyType(caller, _x1, Type.NUMBER);
+ PiccodeValue.verifyType(caller, _y1, Type.NUMBER);
+ PiccodeValue.verifyType(caller, _x2, Type.NUMBER);
+ PiccodeValue.verifyType(caller, _y2, Type.NUMBER);
+
+ var obj = (PiccodeReference) _ctx;
+ var _gfx = obj.deref();
+ if (!(_gfx instanceof Graphics2D)) {
+ throw new PiccodeException(caller.file, caller.line, caller.column, "Context is not a correct object. Expected Graphics2D");
+ }
+
+ var gfx = (Graphics2D) _gfx;
+ var x1 = (int) (double) ((PiccodeNumber) _x1).raw();
+ var y1 = (int) (double) ((PiccodeNumber) _y1).raw();
+ var x2 = (int) (double) ((PiccodeNumber) _x2).raw();
+ var y2 = (int) (double) ((PiccodeNumber) _y2).raw();
+
+ gfx.drawLine(x1, y1, x2, y2);
+ return obj;
+ }, null);
+
+ NativeFunctionFactory.create("draw_rect", List.of("ctx", "x", "y", "w", "h"), (args, namedArgs, frame) -> {
+ var _ctx = namedArgs.get("ctx");
+ var x = namedArgs.get("x");
+ var y = namedArgs.get("y");
+ var w = namedArgs.get("w");
+ var h = namedArgs.get("h");
+
+ var ctx = frame == null ?
+ Context.top
+ : Context.getContextAt(frame);
+ var caller = ctx.getTopFrame().caller;
+
+ PiccodeValue.verifyType(caller, _ctx, Type.REFERENCE);
+ PiccodeValue.verifyType(caller, x, Type.NUMBER);
+ PiccodeValue.verifyType(caller, y, Type.NUMBER);
+ PiccodeValue.verifyType(caller, w, Type.NUMBER);
+ PiccodeValue.verifyType(caller, h, Type.NUMBER);
+
+ var obj = (PiccodeReference) _ctx;
+ var _gfx = obj.deref();
+ if (!(_gfx instanceof Graphics2D)) {
+ throw new PiccodeException(caller.file, caller.line, caller.column, "Context is not a correct object. Expected Graphics2D");
+ }
+
+ var gfx = (Graphics2D) _gfx;
+ var _x = (int) (double) ((PiccodeNumber) x).raw();
+ var _y = (int) (double) ((PiccodeNumber) y).raw();
+ var _w = (int) (double) ((PiccodeNumber) w).raw();
+ var _h = (int) (double) ((PiccodeNumber) h).raw();
+
+ gfx.drawRect(_x, _y, _w, _h);
+ return obj;
+ }, null);
+
+ NativeFunctionFactory.create("draw_round_rect", List.of("ctx", "x", "y", "w", "h", "aw", "ah"), (args, namedArgs, frame) -> {
+ var _ctx = namedArgs.get("ctx");
+ var x = namedArgs.get("x");
+ var y = namedArgs.get("y");
+ var w = namedArgs.get("w");
+ var h = namedArgs.get("h");
+ var aw = namedArgs.get("aw");
+ var ah = namedArgs.get("ah");
+
+ var ctx = frame == null ?
+ Context.top
+ : Context.getContextAt(frame);
+ var caller = ctx.getTopFrame().caller;
+
+ PiccodeValue.verifyType(caller, _ctx, Type.REFERENCE);
+ PiccodeValue.verifyType(caller, x, Type.NUMBER);
+ PiccodeValue.verifyType(caller, y, Type.NUMBER);
+ PiccodeValue.verifyType(caller, w, Type.NUMBER);
+ PiccodeValue.verifyType(caller, h, Type.NUMBER);
+ PiccodeValue.verifyType(caller, aw, Type.NUMBER);
+ PiccodeValue.verifyType(caller, ah, Type.NUMBER);
+
+ var obj = (PiccodeReference) _ctx;
+ var _gfx = obj.deref();
+ if (!(_gfx instanceof Graphics2D)) {
+ throw new PiccodeException(caller.file, caller.line, caller.column, "Context is not a correct object. Expected Graphics2D");
+ }
+
+ var gfx = (Graphics2D) _gfx;
+ var _x = (int) (double) ((PiccodeNumber) x).raw();
+ var _y = (int) (double) ((PiccodeNumber) y).raw();
+ var _w = (int) (double) ((PiccodeNumber) w).raw();
+ var _h = (int) (double) ((PiccodeNumber) h).raw();
+ var _aw = (int) (double) ((PiccodeNumber) aw).raw();
+ var _ah = (int) (double) ((PiccodeNumber) ah).raw();
+
+ gfx.drawRoundRect(_x, _y, _w, _h, _aw, _ah);
+ return obj;
+ }, null);
+
+ NativeFunctionFactory.create("draw_oval", List.of("ctx", "x", "y", "w", "h"), (args, namedArgs, frame) -> {
+ var _ctx = namedArgs.get("ctx");
+ var x = namedArgs.get("x");
+ var y = namedArgs.get("y");
+ var w = namedArgs.get("w");
+ var h = namedArgs.get("h");
+
+ var ctx = frame == null ?
+ Context.top
+ : Context.getContextAt(frame);
+ var caller = ctx.getTopFrame().caller;
+
+ PiccodeValue.verifyType(caller, _ctx, Type.REFERENCE);
+ PiccodeValue.verifyType(caller, x, Type.NUMBER);
+ PiccodeValue.verifyType(caller, y, Type.NUMBER);
+ PiccodeValue.verifyType(caller, w, Type.NUMBER);
+ PiccodeValue.verifyType(caller, h, Type.NUMBER);
+
+ var obj = (PiccodeReference) _ctx;
+ var _gfx = obj.deref();
+ if (!(_gfx instanceof Graphics2D)) {
+ throw new PiccodeException(caller.file, caller.line, caller.column, "Context is not a correct object. Expected Graphics2D");
+ }
+
+ var gfx = (Graphics2D) _gfx;
+ var _x = (int) (double) ((PiccodeNumber) x).raw();
+ var _y = (int) (double) ((PiccodeNumber) y).raw();
+ var _w = (int) (double) ((PiccodeNumber) w).raw();
+ var _h = (int) (double) ((PiccodeNumber) h).raw();
+
+ gfx.drawOval(_x, _y, _w, _h);
+ return obj;
+ }, null);
+
+ NativeFunctionFactory.create("draw_image", List.of("ctx", "img", "x", "y"), (args, namedArgs, frame) -> {
+ var _ctx = namedArgs.get("ctx");
+ var _img = namedArgs.get("img");
+ var x = namedArgs.get("x");
+ var y = namedArgs.get("y");
+
+ var ctx = frame == null ?
+ Context.top
+ : Context.getContextAt(frame);
+ var caller = ctx.getTopFrame().caller;
+
+ PiccodeValue.verifyType(caller, _ctx, Type.REFERENCE);
+ PiccodeValue.verifyType(caller, _img, Type.REFERENCE);
+ PiccodeValue.verifyType(caller, x, Type.NUMBER);
+ PiccodeValue.verifyType(caller, y, Type.NUMBER);
+
+ var obj = (PiccodeReference) _ctx;
+ var imgObj = (PiccodeReference) _img;
+ var _gfx = obj.deref();
+ var _image = imgObj.deref();
+ if (!(_gfx instanceof Graphics2D)) {
+ throw new PiccodeException(caller.file, caller.line, caller.column, "Context is not a correct object. Expected Graphics2D");
+ }
+ if (!(_image instanceof BufferedImage)) {
+ throw new PiccodeException(caller.file, caller.line, caller.column, "Image in not a correct object. Expected a BufferedImage but found" + _image);
+ }
+
+ var gfx = (Graphics2D) _gfx;
+ var img = (BufferedImage) _image;
+ var _x = (int) (double) ((PiccodeNumber) x).raw();
+ var _y = (int) (double) ((PiccodeNumber) y).raw();
+
+ gfx.drawImage(img, _x, _y, null);
+ return obj;
+ }, null);
+ NativeFunctionFactory.create("draw_text", List.of("ctx", "text", "x", "y"), (args, namedArgs, frame) -> {
+ var _ctx = namedArgs.get("ctx");
+ var _text = namedArgs.get("text");
+ var x = namedArgs.get("x");
+ var y = namedArgs.get("y");
+
+ var ctx = frame == null ?
+ Context.top
+ : Context.getContextAt(frame);
+ var caller = ctx.getTopFrame().caller;
+
+ PiccodeValue.verifyType(caller, _ctx, Type.REFERENCE);
+ PiccodeValue.verifyType(caller, _text, Type.STRING);
+ PiccodeValue.verifyType(caller, x, Type.NUMBER);
+ PiccodeValue.verifyType(caller, y, Type.NUMBER);
+
+ var obj = (PiccodeReference) _ctx;
+ var _gfx = obj.deref();
+ if (!(_gfx instanceof Graphics2D)) {
+ throw new PiccodeException(caller.file, caller.line, caller.column, "Context is not a correct object. Expected Graphics2D");
+ }
+ var gfx = (Graphics2D) _gfx;
+ var _x = (int) (double) ((PiccodeNumber) x).raw();
+ var _y = (int) (double) ((PiccodeNumber) y).raw();
+
+ gfx.drawString(_text.toString(), _x, _y);
+ return obj;
+ }, null);
+ }
+
+}
diff --git a/src/main/java/org/editor/panels/AboutPanel.java b/src/main/java/org/editor/panels/AboutPanel.java
index 5da656d..1793f44 100644
--- a/src/main/java/org/editor/panels/AboutPanel.java
+++ b/src/main/java/org/editor/panels/AboutPanel.java
@@ -38,11 +38,11 @@ private void initComponents() {
jLabel1.setName(""); // NOI18N
jLabel4.setFont(new java.awt.Font("sansserif", 1, 13)); // NOI18N
- jLabel4.setText("Brought to you by Glimmir developers");
+ jLabel4.setText("Brought to you by Solaris Studio developers");
jTextPane1.setEditable(false);
jTextPane1.setBackground(new java.awt.Color(255, 255, 255));
- jTextPane1.setText("\ndrawString(\"\n+--------------------------------------------+\n | ▄▖▘ ▌ |\n | ▙▌▌▛▘▀▌▛▘▛▘▛▌▛▘▛▌▛▌█▌ |\n | ▌ ▌▙▖█▌▄▌▄▌▙▌▙▖▙▌▙▌▙▖ |\n | Creativity + Logic + Math |\n+---------------------------------------------+\n\", 0, 0)\n\nBrought to you by glimmr developers\n\nLead developer: Gama Sibusiso Vincent\nAI and Plugin intergration: TODO\nDocumenentation: TODO\n\nCreated 100% in java using java swing, antlr4 and flatlaf. \nIcons pack dowloaded from icons8");
+ jTextPane1.setText("\nPen::drawText(\"\n+--------------------------------------------+\n | ▄▖▘ ▌ |\n | ▙▌▌▛▘▀▌▛▘▛▘▛▌▛▘▛▌▛▌█▌ |\n | ▌ ▌▙▖█▌▄▌▄▌▙▌▙▖▙▌▙▌▙▖ |\n | Creativity + Logic + Math |\n+---------------------------------------------+\n\", 0, 0)\n\nBrought to you by glimmr developers\n\nLead developer: Gama Sibusiso Vincent\nAI and Plugin intergration: TODO\nDocumenentation: TODO\n\nCreated 100% in java using java swing, antlr4 and flatlaf. \nIcons pack dowloaded from icons8");
jTextPane1.setCursor(new java.awt.Cursor(java.awt.Cursor.TEXT_CURSOR));
jScrollPane1.setViewportView(jTextPane1);
diff --git a/src/main/java/org/editor/panels/PluginsPanel.java b/src/main/java/org/editor/panels/PluginsPanel.java
index 0013be2..3a38918 100644
--- a/src/main/java/org/editor/panels/PluginsPanel.java
+++ b/src/main/java/org/editor/panels/PluginsPanel.java
@@ -1,6 +1,18 @@
package org.editor.panels;
+import com.piccode.piccodeplugin.PiccodePluginPanel;
import java.awt.BorderLayout;
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.jar.JarFile;
+import org.editor.EditorWindow;
+import org.editor.theme.ThemeManager;
/**
*
@@ -8,10 +20,83 @@
*/
public class PluginsPanel extends DockablePanel {
+ private final HashMap loaders = new HashMap<>();
public PluginsPanel() {
super(new BorderLayout(), "Plugins", "Plugins", "Browse community plugins", "plugin");
- // TODO: Implement the code
+ loadPlugins();
+ }
+
+ private void loadPlugins() {
+ var pluginsDir = Paths.get("./etc/plugins/");
+ try {
+ Class> baseClass = Class.forName("com.piccode.piccodeplugin.PiccodePluginPanel");
+ Files.walk(pluginsDir)
+ .filter(p -> p.toString().endsWith(".jar"))
+ .forEach(jarPath -> {
+ try {
+ scanJar(jarPath.toFile(), baseClass);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ });
+ } catch (IOException | ClassNotFoundException ex) {
+ System.out.println(ex.getMessage());
+ }
+ }
+
+ private void loadPlugin(Class> clazz) {
+ System.out.println("Loading plugin: " + clazz.getName());
+ try {
+ var instance = (PiccodePluginPanel) clazz.getDeclaredConstructor().newInstance();
+ instance.init();
+ instance = instance.getMainPanel();
+ var name = instance.getPluginName();
+ var title = "Plugin: " + name;
+ var dockable = new DockablePanel(new BorderLayout(), name, title, instance.getDescription(), "plugin");
+ dockable.add(instance, BorderLayout.CENTER);
+ ThemeManager.registerPlugin(instance);
+ var desk = EditorWindow.desk;
+ desk.addDockable(dockable);
+ desk.setAutoHide(dockable, true);
+ ThemeManager.updateThemes(EditorWindow.dark);
+
+ } catch (Exception ex) {
+ System.out.println("" + ex.getMessage());
+ }
+ }
+
+ private void scanJar(File jarFile, Class> baseClass) throws IOException {
+ URL[] urls = {jarFile.toURI().toURL()};
+ // Use system classloader as parent to share core and app classes
+ URLClassLoader cl = new URLClassLoader(urls, ClassLoader.getSystemClassLoader());
+ loaders.put(jarFile, cl); // keep the loader alive
+
+ try (JarFile jar = new JarFile(jarFile)) {
+ jar.stream()
+ .filter(e -> e.getName().endsWith(".class")
+ && !e.getName().equals("module-info.class")
+ && !e.getName().contains("$")
+ && !e.getName().contains("META-INF/")
+ && !e.getName().endsWith("package-info.class"))
+ .forEach(e -> {
+ String className = e.getName().replace('/', '.').replaceAll("\\.class$", "");
+ try {
+ // Try loading from app classloader first (fallback pattern)
+ Class> clazz;
+ try {
+ clazz = Class.forName(className);
+ } catch (ClassNotFoundException ex) {
+ clazz = cl.loadClass(className);
+ }
+ if (baseClass.isAssignableFrom(clazz) && !clazz.equals(baseClass)) {
+ loadPlugin(clazz);
+ }
+ } catch (Exception ignore) {
+ System.out.println("Failed to load class " + className + ": " + ignore.getMessage());
+ }
+ });
+ }
}
}
diff --git a/src/main/java/org/editor/theme/ThemeManager.java b/src/main/java/org/editor/theme/ThemeManager.java
new file mode 100644
index 0000000..d3a6287
--- /dev/null
+++ b/src/main/java/org/editor/theme/ThemeManager.java
@@ -0,0 +1,67 @@
+package org.editor.theme;
+
+import com.formdev.flatlaf.FlatDarkLaf;
+import com.formdev.flatlaf.FlatLightLaf;
+import com.piccode.piccodeplugin.PiccodePluginInterface;
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.List;
+import org.editor.CodeEditor;
+
+/**
+ *
+ * @author hexaredecimal
+ */
+public class ThemeManager {
+
+ private static List editors = new ArrayList();
+ private static List plugins = new ArrayList();
+ public static Color RENDER_BG = Color.WHITE;
+ public static Color RENDER_FG = Color.BLACK;
+ public static Color RENDER_TXT2 = Color.BLUE;
+ public static Color RENDER_GRID = Color.GRAY;
+
+ public static void registerEditor(CodeEditor editor) {
+ editors.add(editor);
+ }
+
+ public static void removeEditor(CodeEditor editor) {
+ editors.remove(editor);
+ }
+
+ public static void registerPlugin(PiccodePluginInterface plugin) {
+ plugins.add(plugin);
+ }
+
+ public static void updateThemes(boolean dark) {
+ editors.forEach(editor -> editor.setThemeMode(dark));
+ plugins.forEach(plugin -> plugin.setThemeMode(dark));
+ setFlatLaf(dark);
+
+ if (dark) {
+ RENDER_BG = new Color(18, 18, 18);
+ RENDER_FG = Color.WHITE;
+ RENDER_TXT2 = Color.GREEN;
+ RENDER_GRID = new Color(18 * 5, 18 * 5, 18 * 5);
+ } else {
+ RENDER_BG = Color.WHITE;
+ RENDER_FG = Color.BLACK;
+ RENDER_TXT2 = Color.BLUE;
+ RENDER_GRID = new Color(230, 230, 230);
+ }
+ }
+
+ public static void setFlatLaf(boolean dark) {
+ try {
+ if (dark) {
+ FlatDarkLaf.setup();
+ FlatDarkLaf.updateUI();
+ } else {
+ FlatLightLaf.setup();
+ FlatLightLaf.updateUI();
+ }
+ } catch (Exception ex) {
+ System.err.println("Failed to initialize LaF");
+ }
+ }
+}
diff --git a/src/main/java/org/piccode/piccode/Piccode.java b/src/main/java/org/piccode/piccode/Piccode.java
index 9f2b0c1..3b03ee6 100644
--- a/src/main/java/org/piccode/piccode/Piccode.java
+++ b/src/main/java/org/piccode/piccode/Piccode.java
@@ -2,6 +2,11 @@
import org.editor.AccessFrame;
import org.editor.EditorWindow;
+import org.editor.nativemods.PiccodeBrushedMetalFilterModule;
+import org.editor.nativemods.PiccodeFilterModule;
+import org.editor.nativemods.PiccodeGfxModule;
+import org.editor.nativemods.PiccodeImageModule;
+import org.editor.nativemods.PiccodePenModule;
import org.piccode.backend.Compiler;
import org.piccode.piccodescript.ErrorAsciiKind;
@@ -15,7 +20,15 @@ public static void main(String[] args) {
Compiler.exitOnError = false;
Compiler.errorKind = ErrorAsciiKind.EMACS_COMP_STYLE;
Compiler.out = AccessFrame.AccessFrameOutputStream.out;
-
+ initializeNativeAppModules();
EditorWindow.the();
}
+
+ private static void initializeNativeAppModules() {
+ Compiler.addNativeFunctions(PiccodeGfxModule::addFunctions);
+ Compiler.addNativeFunctions(PiccodePenModule::addFunctions);
+ Compiler.addNativeFunctions(PiccodeBrushedMetalFilterModule::addFunctions);
+ Compiler.addNativeFunctions(PiccodeFilterModule::addFunctions);
+ Compiler.addNativeFunctions(PiccodeImageModule::addFunctions);
+ }
}
diff --git a/tests/test_readme_validation.py b/tests/test_readme_validation.py
new file mode 100644
index 0000000..d16d2b7
--- /dev/null
+++ b/tests/test_readme_validation.py
@@ -0,0 +1,427 @@
+import pytest
+import re
+import requests
+from pathlib import Path
+from unittest.mock import patch, Mock
+
+
+class TestReadmeValidation:
+ """Comprehensive test suite for README.md validation and content verification."""
+
+ @pytest.fixture
+ def readme_content(self):
+ """Fixture to load README.md content for testing."""
+ readme_path = Path("README.md")
+ if readme_path.exists():
+ return readme_path.read_text(encoding='utf-8')
+ return ""
+
+ def test_readme_file_exists(self):
+ """Test that README.md file exists in the repository root."""
+ readme_path = Path("README.md")
+ assert readme_path.exists(), "README.md file should exist in repository root"
+ assert readme_path.is_file(), "README.md should be a file, not a directory"
+
+ def test_readme_is_not_empty(self, readme_content):
+ """Test that README.md is not empty and has meaningful content."""
+ assert readme_content.strip(), "README.md should not be empty"
+ assert len(readme_content.strip()) > 100, "README.md should have substantial content"
+
+ def test_project_title_present(self, readme_content):
+ """Test that the project title 'Piccaso Code' is present in README."""
+ assert "Piccaso Code" in readme_content, "Project title 'Piccaso Code' should be present"
+ # Also test for the correct spelling variant
+ assert "Piccasso code" in readme_content, "Alternative spelling 'Piccasso code' should be present"
+
+ def test_project_description_present(self, readme_content):
+ """Test that key project description elements are present."""
+ assert "image editor" in readme_content.lower(), "Should mention 'image editor'"
+ assert "java" in readme_content.lower(), "Should mention 'java' as the programming language"
+ assert "glimr" in readme_content.lower(), "Should mention 'glimr' scripting language"
+ assert "code to create/edit an image" in readme_content.lower(), "Should describe core functionality"
+
+ def test_html_table_structure(self, readme_content):
+ """Test that HTML table structure is valid and well-formed."""
+ # Test for table opening and closing tags
+ assert "" in readme_content, "Should contain opening table tag"
+ assert "
" in readme_content, "Should contain closing table tag"
+
+ # Test for proper row structure
+ table_rows = re.findall(r".*?
", readme_content, re.DOTALL)
+ assert len(table_rows) >= 1, "Should have at least one table row"
+
+ # Test for proper cell structure
+ table_cells = re.findall(r".*? | ", readme_content, re.DOTALL)
+ assert len(table_cells) >= 2, "Should have at least two table cells"
+
+ def test_app_icon_reference(self, readme_content):
+ """Test that app icon image reference is properly formatted."""
+ img_pattern = r'
'
+ img_matches = re.findall(img_pattern, readme_content)
+
+ assert len(img_matches) > 0, "Should contain at least one img tag"
+
+ for src, width in img_matches:
+ assert src.startswith("./"), "Image source should use relative path"
+ assert "appicon.png" in src, "Should reference the app icon file"
+ assert width.endswith("%"), "Width should be specified as percentage"
+
+ def test_github_actions_badge(self, readme_content):
+ """Test that GitHub Actions CI badge is present and properly formatted."""
+ badge_pattern = r'\[![^]]+\]\([^)]+\)'
+ badges = re.findall(badge_pattern, readme_content)
+
+ assert len(badges) > 0, "Should contain at least one badge"
+ assert "Java CI with Maven" in readme_content, "Should contain Java CI badge text"
+ assert "actions/workflows/maven.yml" in readme_content, "Should reference Maven workflow"
+
+ def test_required_sections_present(self, readme_content):
+ """Test that all required sections are present in the README."""
+ required_sections = [
+ "## About",
+ "## Download",
+ "## Building",
+ "## Inspired by",
+ "## References",
+ "## License"
+ ]
+
+ for section in required_sections:
+ assert section in readme_content, f"Required section '{section}' should be present"
+
+ def test_build_instructions_valid(self, readme_content):
+ """Test that build instructions are present and properly formatted."""
+ # Test for git clone command
+ assert "git clone" in readme_content, "Should contain git clone command"
+ assert "github.com" in readme_content, "Should reference GitHub repository"
+
+ # Test for Maven build command
+ assert "mvn package" in readme_content, "Should contain Maven package command"
+
+ # Test for Java execution command
+ assert "java -jar" in readme_content, "Should contain Java execution command"
+ assert "target/" in readme_content, "Should reference target directory"
+ assert ".jar" in readme_content, "Should reference JAR file"
+
+ def test_code_blocks_properly_formatted(self, readme_content):
+ """Test that code blocks are properly formatted with language specifiers."""
+ code_block_pattern = r'```(\w+)?\n(.*?)\n```'
+ code_blocks = re.findall(code_block_pattern, readme_content, re.DOTALL)
+
+ assert len(code_blocks) >= 2, "Should contain at least 2 code blocks"
+
+ # Check for shell/bash code blocks
+ shell_blocks = [content for lang, content in code_blocks if lang in ['sh', 'bash', 'shell']]
+ assert len(shell_blocks) >= 1, "Should contain at least one shell code block"
+
+ def test_external_references_format(self, readme_content):
+ """Test that external references are properly formatted."""
+ # Test for OpenSCAD reference
+ assert "OpenSCAD" in readme_content, "Should reference OpenSCAD"
+ assert "openscad.org" in readme_content, "Should contain OpenSCAD URL"
+
+ # Test for java image filters reference
+ assert "jhlabs.com" in readme_content, "Should reference JH Labs filters"
+
+ # Test for Icons8 reference
+ assert "icons8.com" in readme_content, "Should reference Icons8"
+
+ @pytest.mark.parametrize("url", [
+ "https://github.com/Glimmr-Lang/PicassoCode/actions/workflows/maven.yml",
+ "https://openscad.org/",
+ "http://www.jhlabs.com/ip/filters/index.html",
+ "https://icons8.com/icons/parakeet--style-parakeet"
+ ])
+ def test_external_urls_accessible(self, url):
+ """Test that external URLs mentioned in README are accessible (mocked for testing)."""
+ with patch('requests.get') as mock_get:
+ mock_response = Mock()
+ mock_response.status_code = 200
+ mock_response.raise_for_status.return_value = None
+ mock_get.return_value = mock_response
+
+ response = requests.get(url, timeout=10)
+ assert response.status_code == 200, f"URL {url} should be accessible"
+ mock_get.assert_called_once_with(url, timeout=10)
+
+ def test_license_section_format(self, readme_content):
+ """Test that license section contains proper ASCII art and license info."""
+ license_section = readme_content[readme_content.find("## License"):]
+
+ # Test for ASCII art presence
+ assert "drawString" in license_section, "License section should contain drawString function"
+ assert "Creativity + Logic + Math" in license_section, "Should contain project motto"
+ assert "MIT LICENSE" in license_section, "Should mention MIT license"
+
+ # Test for proper code block formatting in license
+ assert '```sh' in license_section, "License code should be in shell code block"
+
+ def test_tagline_consistency(self, readme_content):
+ """Test that the project tagline appears consistently throughout."""
+ tagline = "Creativity + Logic + Math"
+ tagline_occurrences = readme_content.count(tagline)
+ assert tagline_occurrences >= 2, f"Tagline '{tagline}' should appear at least twice"
+
+ def test_spelling_and_consistency(self, readme_content):
+ """Test for potential spelling inconsistencies in project name."""
+ # The README has both "Piccaso" and "Piccasso" - test that both variants are intentional
+ piccaso_count = readme_content.count("Piccaso")
+ piccasso_count = readme_content.count("Piccasso")
+
+ assert piccaso_count > 0, "Should contain 'Piccaso' variant"
+ assert piccasso_count > 0, "Should contain 'Piccasso' variant"
+
+ # Warn about inconsistency (this is more of a documentation test)
+ total_variants = piccaso_count + piccasso_count
+ assert total_variants >= 2, "Project name should appear multiple times"
+
+ def test_markdown_syntax_validity(self, readme_content):
+ """Test basic markdown syntax validity."""
+ lines = readme_content.split('\n')
+
+ # Test for proper heading syntax
+ heading_lines = [line for line in lines if line.startswith('#')]
+ for heading in heading_lines:
+ assert re.match(r'^#{1,6}\s+\S+', heading), f"Invalid heading format: {heading}"
+
+ # Test for balanced HTML tags in the content
+ html_tags = re.findall(r'<(\/?[^>]+)>', readme_content)
+ opening_tags = [tag for tag in html_tags if not tag.startswith('/')]
+ closing_tags = [tag[1:] for tag in html_tags if tag.startswith('/')]
+
+ # Basic balance check for common tags
+ for tag in ['table', 'tr', 'td', 'h3', 'h6', 'p', 'img']:
+ opening_count = sum(1 for t in opening_tags if t.split()[0] == tag)
+ closing_count = closing_tags.count(tag)
+ if tag != 'img': # img is self-closing
+ assert opening_count == closing_count, f"Unbalanced {tag} tags"
+
+ def test_repository_references_consistency(self, readme_content):
+ """Test that repository references are consistent."""
+ # Extract repository URLs
+ repo_urls = re.findall(r'github\.com[:/][^/\s)]+/[^/\s)]+', readme_content)
+
+ assert len(repo_urls) > 0, "Should contain GitHub repository references"
+
+ # Check for consistency in repository naming
+ unique_repos = set(repo_urls)
+ # There might be slight variations (Glimmr-Lang/PicassoCode vs hexaredecimal/Piccode)
+ # This test documents the current state
+ assert len(unique_repos) >= 1, "Should reference at least one repository"
+
+ def test_download_section_placeholder(self, readme_content):
+ """Test that download section has appropriate placeholder text."""
+ download_section_match = re.search(r'## Download.*?## Building', readme_content, re.DOTALL)
+ assert download_section_match, "Should have Download section before Building section"
+
+ download_content = download_section_match.group(0)
+ assert "Coming soon" in download_content, "Download section should indicate coming soon"
+
+ def test_file_encoding_and_format(self):
+ """Test that README file has proper encoding and line endings."""
+ readme_path = Path("README.md")
+ if readme_path.exists():
+ # Test that file can be read as UTF-8
+ try:
+ content = readme_path.read_text(encoding='utf-8')
+ assert len(content) > 0, "File should be readable as UTF-8"
+ except UnicodeDecodeError:
+ pytest.fail("README.md should be encoded in UTF-8")
+
+ # Test for reasonable line lengths (soft limit)
+ lines = content.split('\n')
+ long_lines = [i for i, line in enumerate(lines, 1) if len(line) > 120]
+ # This is a soft warning rather than hard failure
+ if len(long_lines) > 3:
+ pytest.warn(f"Many lines exceed 120 characters: lines {long_lines[:5]}")
+
+
+class TestReadmeContentQuality:
+ """Additional tests for README content quality and completeness."""
+
+ @pytest.fixture
+ def readme_content(self):
+ """Fixture to load README.md content for testing."""
+ readme_path = Path("README.md")
+ if readme_path.exists():
+ return readme_path.read_text(encoding='utf-8')
+ return ""
+
+ def test_inspiration_section_completeness(self, readme_content):
+ """Test that inspiration section provides adequate context."""
+ inspiration_match = re.search(r'## Inspired by.*?## References', readme_content, re.DOTALL)
+ assert inspiration_match, "Should have complete Inspired by section"
+
+ inspiration_content = inspiration_match.group(0)
+ assert len(inspiration_content.strip()) > 200, "Inspiration section should be substantial"
+ assert "OpenSCAD" in inspiration_content, "Should reference OpenSCAD as inspiration"
+ assert "2D" in inspiration_content, "Should mention 2D aspect"
+
+ def test_build_command_completeness(self, readme_content):
+ """Test that build commands are complete and executable."""
+ # Extract all shell commands
+ shell_commands = re.findall(r'```sh\n(.*?)\n```', readme_content, re.DOTALL)
+
+ build_commands = []
+ for command_block in shell_commands:
+ commands = command_block.strip().split('\n')
+ build_commands.extend([cmd.strip() for cmd in commands if cmd.strip()])
+
+ # Should have git clone, cd, and mvn commands
+ git_commands = [cmd for cmd in build_commands if cmd.startswith('git')]
+ cd_commands = [cmd for cmd in build_commands if cmd.startswith('cd')]
+ mvn_commands = [cmd for cmd in build_commands if cmd.startswith('mvn')]
+ java_commands = [cmd for cmd in build_commands if cmd.startswith('java')]
+
+ assert len(git_commands) >= 1, "Should have git clone command"
+ assert len(cd_commands) >= 1, "Should have cd command"
+ assert len(mvn_commands) >= 1, "Should have mvn command"
+ assert len(java_commands) >= 1, "Should have java execution command"
+
+ def test_visual_elements_present(self, readme_content):
+ """Test that visual elements enhance the README presentation."""
+ # Should have app icon
+ assert 'src="./src/main/resources/applogo/appicon.png"' in readme_content, "Should reference app icon"
+
+ # Should have CI badge
+ assert "badge.svg" in readme_content, "Should have CI status badge"
+
+ # Should have ASCII art in license
+ ascii_art_indicators = ["▄▖▘", "▙▌▌▛", "▌ ▌▙"]
+ ascii_present = any(indicator in readme_content for indicator in ascii_art_indicators)
+ assert ascii_present, "Should contain ASCII art in license section"
+
+ def test_thank_you_message(self, readme_content):
+ """Test that README ends with appropriate thank you message."""
+ assert "Thank you for viewing" in readme_content, "Should end with thank you message"
+
+ def test_repository_url_consistency(self, readme_content):
+ """Test consistency between different repository URLs mentioned."""
+ # Extract git clone URL
+ git_clone_match = re.search(r'git clone (git@github\.com:[^/]+/[^\s]+)', readme_content)
+ if git_clone_match:
+ git_url = git_clone_match.group(1)
+ # Should be consistent naming
+ assert "Piccode" in git_url, "Git clone URL should reference Piccode repository"
+
+ # Extract badge URL
+ badge_match = re.search(r'github\.com/([^/]+/[^/]+)/actions', readme_content)
+ if badge_match:
+ badge_repo = badge_match.group(1)
+ assert "/" in badge_repo, "Badge should reference valid GitHub repo format"
+
+ def test_section_ordering(self, readme_content):
+ """Test that sections appear in logical order."""
+ sections = ["## About", "## Download", "## Building", "## Inspired by", "## References", "## License"]
+
+ section_positions = {}
+ for section in sections:
+ pos = readme_content.find(section)
+ if pos != -1:
+ section_positions[section] = pos
+
+ # Verify sections appear in expected order
+ ordered_sections = sorted(section_positions.items(), key=lambda x: x[1])
+ expected_order = ["## About", "## Download", "## Building", "## Inspired by", "## References", "## License"]
+
+ actual_order = [section for section, _ in ordered_sections]
+ for i, expected in enumerate(expected_order):
+ if i < len(actual_order):
+ assert actual_order[i] == expected, f"Section {expected} should appear before later sections"
+
+
+class TestReadmeEdgeCases:
+ """Test edge cases and potential issues in README content."""
+
+ @pytest.fixture
+ def readme_content(self):
+ """Fixture to load README.md content for testing."""
+ readme_path = Path("README.md")
+ if readme_path.exists():
+ return readme_path.read_text(encoding='utf-8')
+ return ""
+
+ def test_no_broken_markdown_links(self, readme_content):
+ """Test that markdown links are properly formatted."""
+ # Find all markdown links
+ link_pattern = r'\[([^\]]+)\]\(([^)]+)\)'
+ links = re.findall(link_pattern, readme_content)
+
+ for link_text, link_url in links:
+ assert link_text.strip(), "Link text should not be empty"
+ assert link_url.strip(), "Link URL should not be empty"
+ assert not link_url.startswith(' '), "Link URL should not start with space"
+ assert not link_url.endswith(' '), "Link URL should not end with space"
+
+ def test_html_img_attributes(self, readme_content):
+ """Test that HTML img tags have required attributes."""
+ img_tags = re.findall(r'
]+>', readme_content)
+
+ for img_tag in img_tags:
+ assert 'src=' in img_tag, f"IMG tag should have src attribute: {img_tag}"
+ # Width is optional but should be properly formatted if present
+ if 'width=' in img_tag:
+ width_match = re.search(r'width="([^"]+)"', img_tag)
+ if width_match:
+ width_value = width_match.group(1)
+ # Should be percentage or pixel value
+ assert width_value.endswith('%') or width_value.endswith('px') or width_value.isdigit(), \
+ f"Width value should be valid CSS unit: {width_value}"
+
+ def test_code_block_completeness(self, readme_content):
+ """Test that code blocks are complete and not malformed."""
+ # Count opening and closing code block markers
+ opening_blocks = readme_content.count('```')
+ assert opening_blocks % 2 == 0, "Code blocks should have matching opening and closing markers"
+
+ # Test that code blocks have content
+ code_block_pattern = r'```[^\n]*\n(.*?)\n```'
+ code_blocks = re.findall(code_block_pattern, readme_content, re.DOTALL)
+
+ for block_content in code_blocks:
+ assert block_content.strip(), "Code blocks should not be empty"
+
+ def test_ascii_art_integrity(self, readme_content):
+ """Test that ASCII art in license section is properly formatted."""
+ license_section = readme_content[readme_content.find("## License"):]
+
+ if "drawString" in license_section:
+ # Extract the ASCII art
+ art_match = re.search(r'drawString\("([^"]+)"', license_section, re.DOTALL)
+ if art_match:
+ ascii_art = art_match.group(1)
+ lines = ascii_art.split('\\n')
+
+ # Should have box-drawing characters
+ box_chars_present = any(char in ascii_art for char in ['▄', '▖', '▘', '▌', '▙', '█'])
+ assert box_chars_present, "ASCII art should contain Unicode box-drawing characters"
+
+ # Should have consistent line structure for the box
+ border_lines = [line for line in lines if '+' in line and '-' in line]
+ assert len(border_lines) >= 2, "ASCII art should have top and bottom borders"
+
+ def test_whitespace_consistency(self, readme_content):
+ """Test for consistent whitespace usage."""
+ lines = readme_content.split('\n')
+
+ # Check for trailing whitespace (common markdown issue)
+ trailing_whitespace_lines = [i+1 for i, line in enumerate(lines) if line.rstrip() != line]
+ assert len(trailing_whitespace_lines) < 5, \
+ f"Too many lines with trailing whitespace: {trailing_whitespace_lines[:5]}"
+
+ # Check for multiple consecutive blank lines
+ consecutive_blanks = 0
+ max_consecutive_blanks = 0
+
+ for line in lines:
+ if line.strip() == '':
+ consecutive_blanks += 1
+ max_consecutive_blanks = max(max_consecutive_blanks, consecutive_blanks)
+ else:
+ consecutive_blanks = 0
+
+ assert max_consecutive_blanks <= 3, "Should not have more than 3 consecutive blank lines"
+
+
+if __name__ == "__main__":
+ pytest.main([__file__, "-v"])
\ No newline at end of file