Skip to content

Commit 41850da

Browse files
committed
Release 1.1.1: add print support and UI fixes
Adds the 1.1.1 patch release updates for printing, the trash icon, JS preview opening, and drag-and-drop insertion behavior. Made-with: Cursor
1 parent 42e377c commit 41850da

14 files changed

Lines changed: 142 additions & 21 deletions

File tree

README.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ git clone https://github.com/code4teaching/VisuStruct.git
2222

2323
---
2424

25+
## Changes in 1.1.1 (summary)
26+
27+
- **About dialog:** clearer links for website, GitHub repository, and developer site.
28+
- **Palette:** Lucide trash icon for the delete area, shown larger.
29+
- **File menu:** added system **Print...** support for printing or saving via the OS dialog.
30+
2531
## Changes in 1.1 (summary)
2632

2733
- **Element editor:** first step toward the Swift-style bottom editor panel.
@@ -97,7 +103,7 @@ This release aligns **branding and technical identity** with **VisuStruct**. **`
97103
- **Java 17** target (build and run with JDK 17+).
98104
- **FlatLaf** light/dark themes; **JDOM2**; no legacy AppleJavaExtensions.
99105
- **Motif** look-and-feel removed; **Metal** and FlatLaf available.
100-
- Current fat JAR name follows **`pom.xml`** **`version`** (e.g. **`visustruct-1.1.jar`**).
106+
- Current fat JAR name follows **`pom.xml`** **`version`** (e.g. **`visustruct-1.1.1.jar`**).
101107

102108
---
103109

@@ -121,7 +127,7 @@ On **Windows**, double-click often works if `.jar` is associated with Java. If a
121127

122128
All releases: [github.com/code4teaching/VisuStruct/releases](https://github.com/code4teaching/VisuStruct/releases)
123129

124-
**Maintainers:** To ship a new version, create a GitHub **Release** with a tag such as **`v1.1`**. Workflow [`.github/workflows/release-assets.yml`](.github/workflows/release-assets.yml) builds the JARs and attaches **`visustruct.jar`** (stable download URL above).
130+
**Maintainers:** To ship a new version, create a GitHub **Release** with a tag such as **`v1.1.1`**. Workflow [`.github/workflows/release-assets.yml`](.github/workflows/release-assets.yml) builds the JARs and attaches **`visustruct.jar`** (stable download URL above).
125131

126132
---
127133

@@ -172,13 +178,13 @@ chmod +x mvnw
172178
Output (version from `pom.xml`):
173179

174180
```text
175-
target/visustruct-1.1.jar
181+
target/visustruct-1.1.1.jar
176182
```
177183

178184
## Run
179185

180186
```bash
181-
java -jar target/visustruct-1.1.jar
187+
java -jar target/visustruct-1.1.1.jar
182188
```
183189

184190
Double-click may work if `.jar` is associated with Java.

packaging/macos/build-lightweight-app.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
set -euo pipefail
55
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
66
ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
7-
VER="${PROJECT_VERSION:-1.1}"
7+
VER="${PROJECT_VERSION:-1.1.1}"
88
JAR_NAME="visustruct-${VER}.jar"
99
OUT="${ROOT}/dist/macos-app-light"
1010
APP="${OUT}/VisuStruct.app"

packaging/macos/build-macos-app.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# Voraussetzung: JDK 17+ mit jpackage (im PATH oder JAVA_HOME).
55
set -euo pipefail
66
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
7-
VER="${PROJECT_VERSION:-1.1}"
7+
VER="${PROJECT_VERSION:-1.1.1}"
88
JAR="visustruct-${VER}.jar"
99
INPUT="${ROOT}/target"
1010
OUT="${ROOT}/dist/macos-app"

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<modelVersion>4.0.0</modelVersion>
55
<groupId>de.visustruct</groupId>
66
<artifactId>visustruct</artifactId>
7-
<version>1.1</version>
7+
<version>1.1.1</version>
88
<name>VisuStruct</name>
99

1010
<properties>

src/main/java/de/visustruct/control/Controlling.java

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import java.awt.Desktop;
44
import java.awt.Dimension;
5+
import java.awt.Graphics2D;
56
import java.awt.Toolkit;
67
import java.awt.datatransfer.Clipboard;
78
import java.awt.datatransfer.ClipboardOwner;
@@ -17,6 +18,10 @@
1718
import java.awt.image.BufferedImage;
1819
import java.io.File;
1920
import java.io.IOException;
21+
import java.awt.print.PageFormat;
22+
import java.awt.print.Printable;
23+
import java.awt.print.PrinterException;
24+
import java.awt.print.PrinterJob;
2025

2126
import javax.swing.JCheckBoxMenuItem;
2227
import javax.swing.JEditorPane;
@@ -255,6 +260,46 @@ public void bildSpeichernNurPng(){
255260
}
256261
}
257262

263+
public void bildDrucken(){
264+
Struktogramm str = gibAktuellesStruktogramm();
265+
if (str == null) {
266+
return;
267+
}
268+
269+
BufferedImage image = str.generateImage(false);
270+
PrinterJob job = PrinterJob.getPrinterJob();
271+
job.setJobName(GlobalSettings.APP_DISPLAY_NAME);
272+
job.setPrintable((graphics, pageFormat, pageIndex) -> {
273+
if (pageIndex > 0) {
274+
return Printable.NO_SUCH_PAGE;
275+
}
276+
Graphics2D g2 = (Graphics2D) graphics.create();
277+
try {
278+
double scale = Math.min(
279+
pageFormat.getImageableWidth() / image.getWidth(),
280+
pageFormat.getImageableHeight() / image.getHeight());
281+
scale = Math.min(scale, 1.0d);
282+
double x = pageFormat.getImageableX() + (pageFormat.getImageableWidth() - image.getWidth() * scale) / 2.0d;
283+
double y = pageFormat.getImageableY() + (pageFormat.getImageableHeight() - image.getHeight() * scale) / 2.0d;
284+
g2.translate(x, y);
285+
g2.scale(scale, scale);
286+
g2.drawImage(image, 0, 0, null);
287+
} finally {
288+
g2.dispose();
289+
}
290+
return Printable.PAGE_EXISTS;
291+
});
292+
293+
if (!job.printDialog()) {
294+
return;
295+
}
296+
try {
297+
job.print();
298+
} catch (PrinterException ex) {
299+
JOptionPane.showMessageDialog(gui, ex.getMessage(), I18n.tr("menu.file.print"), JOptionPane.ERROR_MESSAGE);
300+
}
301+
}
302+
258303
public void titelleisteAktualisieren(){
259304
String pfad = "";
260305

@@ -295,6 +340,10 @@ public void actionPerformed(ActionEvent e) {
295340
bildSpeichern();
296341
break;
297342

343+
case bildDrucken:
344+
bildDrucken();
345+
break;
346+
298347
case bildInZwischenAblage:
299348
copyImagetoClipBoard(gibAktuellesStruktogramm().generateImage(false));
300349
break;

src/main/java/de/visustruct/other/XActionCommands.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package de.visustruct.other;
22

33
public enum XActionCommands {
4-
neu, oeffnen, speichern, speicherUnter, bildSpeichern, bildInZwischenAblage, quellcodeErzeugen,
4+
neu, oeffnen, speichern, speicherUnter, bildSpeichern, bildDrucken, bildInZwischenAblage, quellcodeErzeugen,
55
struktogrammSchliessen, programmBeenden,
66

77
rueckgaengig, widerrufen, ganzesStruktogrammKopieren,

src/main/java/de/visustruct/view/AuswahlPanel.java

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737

3838
import org.jdom2.Document;
3939

40+
import com.formdev.flatlaf.extras.FlatSVGIcon;
41+
4042
import de.visustruct.control.Controlling;
4143
import de.visustruct.control.Struktogramm;
4244
import de.visustruct.i18n.I18n;
@@ -54,6 +56,8 @@ public class AuswahlPanel extends JPanel implements DropTargetListener, DragGest
5456
private boolean muelleimerIstAuf;
5557
private Controlling controlling;
5658
private Document kopiertesStrElement;
59+
private boolean paletteDragAktiv;
60+
private long letzterPaletteDragEndeZeitpunkt;
5761

5862
public AuswahlPanel(Controlling controlling){
5963

@@ -91,7 +95,7 @@ public AuswahlPanel(Controlling controlling){
9195
for (int i = 0; i < panelElemente.length; i++) {
9296
int typ = StruktogrammPalette.TYPEN_REIHENFOLGE[i];
9397
panelElemente[i] = new AuswahlPanelElement(typ);
94-
panelElemente[i].addActionListener(e -> controlling.paletteElementEinfuegen(typ));
98+
panelElemente[i].addActionListener(e -> paletteElementGeklickt(typ));
9599
add(panelElemente[i], c);
96100
dragSource.createDefaultDragGestureRecognizer(panelElemente[i], DnDConstants.ACTION_COPY_OR_MOVE, this);
97101
c.gridy++;
@@ -101,8 +105,8 @@ public AuswahlPanel(Controlling controlling){
101105
muelleimer = new JLabel();
102106
muelleimerIstAuf = true;
103107
muelleimerAuf(!muelleimerIstAuf);
104-
muelleimer.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 11));
105108
muelleimer.setHorizontalAlignment(SwingConstants.CENTER);
109+
muelleimer.setBorder(BorderFactory.createEmptyBorder(6, 0, 6, 0));
106110
muelleimer.setToolTipText(I18n.tr("palette.trashDrop"));
107111
add(muelleimer, c);
108112

@@ -123,6 +127,8 @@ public AuswahlPanel(Controlling controlling){
123127
c.gridy++;
124128

125129
kopiertesStrElement = null;
130+
paletteDragAktiv = false;
131+
letzterPaletteDragEndeZeitpunkt = 0;
126132

127133
c.weighty = 1000;
128134
c.fill = GridBagConstraints.VERTICAL;
@@ -132,6 +138,13 @@ public AuswahlPanel(Controlling controlling){
132138
new DropTarget(this, DnDConstants.ACTION_COPY_OR_MOVE,this, true, null);
133139
}
134140

141+
private void paletteElementGeklickt(int typ) {
142+
if (paletteDragAktiv || System.currentTimeMillis() - letzterPaletteDragEndeZeitpunkt < 500) {
143+
return;
144+
}
145+
controlling.paletteElementEinfuegen(typ);
146+
}
147+
135148
private static JButton paletteAktionsButton(String text) {
136149
JButton b = new JButton(text);
137150
b.setFocusable(false);
@@ -198,13 +211,20 @@ public Document gibKopiertesStrElement(){
198211

199212
private void muelleimerAuf(boolean oeffnen){
200213
if (muelleimerIstAuf != oeffnen){
201-
if (oeffnen){
202-
muelleimerIstAuf = true;
203-
}else{
204-
muelleimerIstAuf = false;
214+
muelleimerIstAuf = oeffnen;
215+
muelleimer.setIcon(erzeugeMuelleimerIcon(oeffnen));
216+
}
217+
}
218+
219+
private static FlatSVGIcon erzeugeMuelleimerIcon(boolean aktiv) {
220+
FlatSVGIcon icon = new FlatSVGIcon("icons/lucide/trash-2.svg", 32, 32);
221+
if (aktiv) {
222+
java.awt.Color accent = UIManager.getColor("Component.accentColor");
223+
if (accent != null) {
224+
icon.setColorFilter(new FlatSVGIcon.ColorFilter(c -> accent));
205225
}
206-
muelleimer.setIcon(new ModernTrashIcon(oeffnen));
207226
}
227+
return icon;
208228
}
209229

210230

@@ -228,6 +248,7 @@ public void dragGestureRecognized(DragGestureEvent evt){//User hat angefangen ei
228248

229249
Transferable t = new StringSelection("n"+typ);
230250

251+
paletteDragAktiv = true;
231252
dragSource.startDrag(evt, DragSource.DefaultCopyDrop, t, this);
232253
}
233254
}
@@ -249,6 +270,8 @@ public void dropActionChanged(DragSourceDragEvent evt){
249270
}
250271

251272
public void dragDropEnd(DragSourceDropEvent evt){
273+
paletteDragAktiv = false;
274+
letzterPaletteDragEndeZeitpunkt = System.currentTimeMillis();
252275
PaletteButtonStyle.clearPressedArmedState(evt.getDragSourceContext().getComponent());
253276
}
254277

src/main/java/de/visustruct/view/CodeErzeuger.java

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -278,12 +278,7 @@ private void openJsPreviewInBrowser() {
278278
Files.writeString(temp, html, StandardCharsets.UTF_8);
279279
java.io.File file = temp.toFile();
280280
file.deleteOnExit();
281-
if (!Desktop.isDesktopSupported() || !Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
282-
JOptionPane.showMessageDialog(this, I18n.tr("dialog.codeGen.jsBrowserNoDesktop.message"),
283-
I18n.tr("dialog.codeGen.jsBrowserNoDesktop.title"), JOptionPane.ERROR_MESSAGE);
284-
return;
285-
}
286-
Desktop.getDesktop().browse(file.toURI());
281+
openPreviewFile(file);
287282
} catch (Exception ex) {
288283
String detail = ex.getMessage();
289284
if (detail == null || detail.isBlank()) {
@@ -294,6 +289,42 @@ private void openJsPreviewInBrowser() {
294289
}
295290
}
296291

292+
private void openPreviewFile(File file) throws Exception {
293+
Exception letzterFehler = null;
294+
if (Desktop.isDesktopSupported()) {
295+
Desktop desktop = Desktop.getDesktop();
296+
if (desktop.isSupported(Desktop.Action.OPEN)) {
297+
try {
298+
desktop.open(file);
299+
return;
300+
} catch (Exception ex) {
301+
letzterFehler = ex;
302+
}
303+
}
304+
if (desktop.isSupported(Desktop.Action.BROWSE)) {
305+
try {
306+
desktop.browse(file.toURI());
307+
return;
308+
} catch (Exception ex) {
309+
letzterFehler = ex;
310+
}
311+
}
312+
}
313+
if (System.getProperty("os.name", "").toLowerCase().contains("mac")) {
314+
try {
315+
new ProcessBuilder("open", file.getAbsolutePath()).start();
316+
return;
317+
} catch (Exception ex) {
318+
letzterFehler = ex;
319+
}
320+
}
321+
if (letzterFehler != null) {
322+
throw letzterFehler;
323+
}
324+
JOptionPane.showMessageDialog(this, I18n.tr("dialog.codeGen.jsBrowserNoDesktop.message"),
325+
I18n.tr("dialog.codeGen.jsBrowserNoDesktop.title"), JOptionPane.ERROR_MESSAGE);
326+
}
327+
297328
private static String escapeForHtmlText(String raw) {
298329
if (raw == null) {
299330
return "";

src/main/java/de/visustruct/view/GUI.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ private void buildMenuBar() {
129129
menu.add(createMenuItem(I18n.tr("menu.file.saveAs"), XActionCommands.speicherUnter, KeyEvent.VK_A));
130130
menu.add(new JSeparator());
131131
menu.add(createMenuItem(I18n.tr("menu.file.saveImage"), XActionCommands.bildSpeichern, KeyEvent.VK_I));
132+
menu.add(createMenuItem(I18n.tr("menu.file.print"), XActionCommands.bildDrucken, KeyEvent.VK_P, KeyEvent.VK_P));
132133
menu.add(createMenuItem(I18n.tr("menu.file.copyImage"), XActionCommands.bildInZwischenAblage, KeyEvent.VK_B, KeyEvent.VK_K));
133134
menu.add(new JSeparator());
134135
menu.add(createMenuItem(I18n.tr("menu.file.generateCode"), XActionCommands.quellcodeErzeugen, KeyEvent.VK_G));

src/main/resources/de/visustruct/i18n/Messages.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ menu.file.open=Open...
77
menu.file.save=Save
88
menu.file.saveAs=Save As...
99
menu.file.saveImage=Save as Image...
10+
menu.file.print=Print...
1011
menu.file.copyImage=Copy Image to Clipboard
1112
menu.file.generateCode=Generate Source Code...
1213
menu.file.closeDiagram=Close Diagram

0 commit comments

Comments
 (0)