feat: F4.6f3 the file explorer and the image viewer
Oracle FileExplorerWindowRun uses Java's explorer and viewer windows as a person does (the right-click menu, Java's questions answered, the import window, the viewer's open). Both windows work on the document's files through the core's operations: the tree of folders and files, the chosen folder's entries, Java's menu (also as buttons, no right click needed), copy that stays and cut that goes once pasted, the import; the viewer's picture and title. Not taken from Java: a file chosen in the tree taken for a folder, pictures laid over each other, a text file as an empty picture. writeFiles numbers the entries in the file system's order again when a rename made a new key. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1427,3 +1427,36 @@ Gemessen (03.10.2026):
|
||||
spätere „Speichern“ geht verloren. Ebenso nach dem Löschen der offenen
|
||||
Datei. „Speichern unter“ mit `ordner/d.txt` legt in der Wurzel eine Datei
|
||||
mit `/` im Namen an, ohne Namen eine namenlose.
|
||||
|
||||
### Datei-Explorer und Bildbetrachter (F4.6f3)
|
||||
|
||||
`FileExplorerWindowRun` öffnet Javas `GUIApplicationFileExplorerWindow` und
|
||||
`GUIApplicationImageViewerWindow` auf einem `GUIDesktopPanel` (erster
|
||||
Rechner von mail-lan, mit Befehlszeile und Webserver) und benutzt sie wie
|
||||
eine Person: den Baum, die Liste mit dem Menü eines Rechtsklicks (auf einem
|
||||
Eintrag, auf der freien Fläche; ein gezeigtes Menü liegt in der Ebene des
|
||||
Rahmens, nicht im Fenster), Javas Fragen — ein Wächter tippt in die
|
||||
Eingabedialoge und sagt ja oder nein —, „Aktualisieren“, das Importfenster
|
||||
(die Felder, wie „Datei wählen“ sie füllt); im Bildbetrachter „Öffnen“ über
|
||||
den Dateidialog. Je Schritt: Zeilen des Baums, Liste, Menü, Texte der
|
||||
Dialoge, Titel, Bilder des Betrachters, alle Dateien
|
||||
(`packages/sim/test/fileExplorerWindow.java.json`).
|
||||
|
||||
Gemessen (03.10.2026):
|
||||
|
||||
* Der Baum zeigt Ordner und Dateien in Entstehungsreihenfolge, die Wurzel
|
||||
als „root“; die Liste den gewählten Ordner, ohne „Aktualisieren“ nicht,
|
||||
was die Befehlszeile anlegte.
|
||||
* Das Menü auf einem Eintrag: Löschen, Ausschneiden, Kopieren, Umbenennen;
|
||||
auf der freien Fläche: Neuer Ordner, mit etwas zum Einfügen auch
|
||||
Einfügen. Kopiertes bleibt zum nächsten Einfügen, Ausgeschnittenes ist
|
||||
nach dem Einfügen weg.
|
||||
* Neuer Ordner fragt ohne Text, Umbenennen mit „Neuer Name:“ (ein
|
||||
vorhandener oder leerer Name ändert still nichts), Löschen mit „Sicher?“.
|
||||
* Das Importfenster: „Lokale Datei:“ (nur lesbar) mit „Datei wählen“, „Neuer
|
||||
Name:“, „Datei importieren“, die Meldung darunter, „Schließen“.
|
||||
* Javas Fehler: Eine im Baum gewählte Datei gilt als Ordner — die Liste ist
|
||||
leer, ein neuer Ordner landet unter der Datei.
|
||||
* Der Bildbetrachter heißt nach dem Öffnen wie die Datei; jedes geöffnete
|
||||
Bild legt Java über das vorige, eine Textdatei zeigt es als leeres Bild,
|
||||
ohne Fund bleibt alles.
|
||||
|
||||
@@ -21,7 +21,7 @@ if [ ! -f "$upstream/target/filius.jar" ]; then
|
||||
fi
|
||||
classpath="$upstream/target/filius.jar:$upstream/target/lib/*"
|
||||
mkdir -p "$here/.build"
|
||||
javac -encoding UTF-8 -d "$here/.build" -cp "$classpath" "$here/src/FlsOracle.java" "$here/src/GetterDefaults.java" "$here/src/IpCheckCases.java" "$here/src/DnsHostsCases.java" "$here/src/EmailFileCases.java" "$here/src/BeansDump.java" "$here/src/Base64Cases.java" "$here/src/ScenarioNetwork.java" "$here/src/DhcpRun.java" "$here/src/TcpRun.java" "$here/src/DnsRun.java" "$here/src/ArpRun.java" "$here/src/RipRun.java" "$here/src/NatRun.java" "$here/src/WlanRun.java" "$here/src/ExampleRun.java" "$here/src/EchoRun.java" "$here/src/WebRun.java" "$here/src/RouterWebRun.java" "$here/src/KonfigCases.java" "$here/src/EmailRun.java" "$here/src/MailCases.java" "$here/src/EmailClientRun.java" "$here/src/TerminalCases.java" "$here/src/TerminalRun.java" "$here/src/FileCases.java" "$here/src/GnutellaRun.java" "$here/src/NewDeviceRun.java" "$here/src/SheetRun.java" "$here/src/TerminalWindowRun.java" "$here/src/InstallRun.java" "$here/src/BrowserWindowRun.java" "$here/src/MailWindowRun.java" "$here/src/SaveAfterRunRun.java" "$here/src/TextEditorWindowRun.java"
|
||||
javac -encoding UTF-8 -d "$here/.build" -cp "$classpath" "$here/src/FlsOracle.java" "$here/src/GetterDefaults.java" "$here/src/IpCheckCases.java" "$here/src/DnsHostsCases.java" "$here/src/EmailFileCases.java" "$here/src/BeansDump.java" "$here/src/Base64Cases.java" "$here/src/ScenarioNetwork.java" "$here/src/DhcpRun.java" "$here/src/TcpRun.java" "$here/src/DnsRun.java" "$here/src/ArpRun.java" "$here/src/RipRun.java" "$here/src/NatRun.java" "$here/src/WlanRun.java" "$here/src/ExampleRun.java" "$here/src/EchoRun.java" "$here/src/WebRun.java" "$here/src/RouterWebRun.java" "$here/src/KonfigCases.java" "$here/src/EmailRun.java" "$here/src/MailCases.java" "$here/src/EmailClientRun.java" "$here/src/TerminalCases.java" "$here/src/TerminalRun.java" "$here/src/FileCases.java" "$here/src/GnutellaRun.java" "$here/src/NewDeviceRun.java" "$here/src/SheetRun.java" "$here/src/TerminalWindowRun.java" "$here/src/InstallRun.java" "$here/src/BrowserWindowRun.java" "$here/src/MailWindowRun.java" "$here/src/SaveAfterRunRun.java" "$here/src/TextEditorWindowRun.java" "$here/src/FileExplorerWindowRun.java"
|
||||
# FILIUS writes its settings below $HOME; keep them out of the real one.
|
||||
mkdir -p "$here/.build/home"
|
||||
# Removed first: a crashed run must not leave the previous result standing.
|
||||
@@ -411,6 +411,16 @@ HOME="$here/.build/home" xvfb-run -a java -Duser.language=de -Duser.country=DE -
|
||||
"$root/packages/core/test/scenarios/mail-lan.scenario.json" "$output" 2>&1 | grep -v 'logback\|^$\|^\s*at ' || true
|
||||
test -s "$output"
|
||||
|
||||
# FILIUS' file explorer and image viewer windows used as a person does —
|
||||
# the tree, the right-click menu, Java's questions answered, the import
|
||||
# window, the viewer's "Öffnen" (FileExplorerWindowRun): the editor's
|
||||
# explorer and viewer (packages/editor/test/fileExplorer.test.ts).
|
||||
output="$root/packages/sim/test/fileExplorerWindow.java.json"
|
||||
rm -f "$output"
|
||||
HOME="$here/.build/home" xvfb-run -a java -Duser.language=de -Duser.country=DE -cp "$classpath:$here/.build" FileExplorerWindowRun \
|
||||
"$root/packages/core/test/scenarios/mail-lan.scenario.json" "$output" 2>&1 | grep -v 'logback\|^$\|^\s*at ' || true
|
||||
test -s "$output"
|
||||
|
||||
# FILIUS' own examples as Java FILIUS runs them, opened and started as FILIUS
|
||||
# does (ExampleRun): the acceptance of F2 (packages/fls/test/examplesJava.test.ts)
|
||||
# and the classics of F3 (web, email, gnutella; packages/fls/test/classicsJava.test.ts).
|
||||
|
||||
@@ -0,0 +1,490 @@
|
||||
// Uses FILIUS' own file explorer and image viewer windows
|
||||
// (GUIApplicationFileExplorerWindow, GUIApplicationImageViewerWindow on a
|
||||
// GUIDesktopPanel) on mail-lan's first computer as a person does: the
|
||||
// explorer's tree, its list with the menu of a right click (on an entry, on
|
||||
// the free space), Java's questions answered as given (a watcher types into
|
||||
// the input dialogs, says yes or no), "Aktualisieren", the import window;
|
||||
// the image viewer's "Öffnen" over the file dialog. The command line beside
|
||||
// them changes the files. After each step it writes what the windows show
|
||||
// (the tree's rows, the list, the menu, the dialogs' texts, the title, the
|
||||
// viewer's pictures) and the computer's files. The reference for the
|
||||
// editor's explorer and viewer (packages/editor/test/fileExplorer.test.ts).
|
||||
//
|
||||
// Usage: java -cp filius.jar:lib/*:classes FileExplorerWindowRun <scenario.json> <output json>
|
||||
|
||||
import filius.gui.anwendungssicht.GUIApplicationFileExplorerWindow;
|
||||
import filius.gui.anwendungssicht.GUIApplicationImageViewerWindow;
|
||||
import filius.gui.anwendungssicht.GUIApplicationWindow;
|
||||
import filius.gui.anwendungssicht.GUIDesktopPanel;
|
||||
import filius.software.lokal.Terminal;
|
||||
import filius.software.system.Betriebssystem;
|
||||
import filius.software.system.Datei;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Container;
|
||||
import java.awt.Point;
|
||||
import java.awt.Window;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseListener;
|
||||
import java.io.FileOutputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Deque;
|
||||
import java.util.Enumeration;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.StringTokenizer;
|
||||
import javax.swing.AbstractButton;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.JTree;
|
||||
import javax.swing.ListModel;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import javax.swing.tree.TreePath;
|
||||
|
||||
public class FileExplorerWindowRun {
|
||||
static final String CLIENT = "00000000-0000-0000-0000-000000000201";
|
||||
|
||||
static final List<Object> steps = new ArrayList<>();
|
||||
/** The answers to Java's next dialogs: a text typed into an input dialog, or "yes"/"no". */
|
||||
static final Deque<String> answers = new ArrayDeque<>();
|
||||
/** The texts of the dialogs since the last step. */
|
||||
static final List<String> dialogs = new ArrayList<>();
|
||||
static GUIApplicationFileExplorerWindow explorer;
|
||||
static GUIApplicationImageViewerWindow viewer;
|
||||
static GUIDesktopPanel desktop;
|
||||
static Betriebssystem system;
|
||||
static Terminal terminal;
|
||||
static List<String> menu = null;
|
||||
static List<Object> shown = new ArrayList<>();
|
||||
|
||||
static Object field(Class<?> owner, Object object, String name) throws Exception {
|
||||
Field field = owner.getDeclaredField(name);
|
||||
field.setAccessible(true);
|
||||
return field.get(object);
|
||||
}
|
||||
|
||||
static void setField(Class<?> owner, Object object, String name, Object value) throws Exception {
|
||||
Field field = owner.getDeclaredField(name);
|
||||
field.setAccessible(true);
|
||||
field.set(object, value);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static <T> T find(Container container, Class<T> kind) {
|
||||
for (Component component : container.getComponents()) {
|
||||
if (kind.isInstance(component)) return (T) component;
|
||||
if (component instanceof Container) {
|
||||
T found = find((Container) component, kind);
|
||||
if (found != null) return found;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static <T> List<T> all(Container container, Class<T> kind, List<T> found) {
|
||||
for (Component component : container.getComponents()) {
|
||||
if (kind.isInstance(component)) found.add(kind.cast(component));
|
||||
if (component instanceof Container) all((Container) component, kind, found);
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
static AbstractButton button(Container container, String text) {
|
||||
for (AbstractButton each : all(container, AbstractButton.class, new ArrayList<>())) if (text.equals(each.getText())) return each;
|
||||
throw new IllegalStateException("no button " + text);
|
||||
}
|
||||
|
||||
static void onEdt(Runnable action) throws Exception {
|
||||
SwingUtilities.invokeLater(action);
|
||||
Thread.sleep(400);
|
||||
}
|
||||
|
||||
/** Answers each of Java's option dialogs from the answers given, writes down its text. */
|
||||
static void watchDialogs() {
|
||||
Thread watcher = new Thread(() -> {
|
||||
while (true) {
|
||||
for (Window window : Window.getWindows()) {
|
||||
if (!(window instanceof JDialog) || !window.isVisible()) continue;
|
||||
JOptionPane pane = find((Container) window, JOptionPane.class);
|
||||
if (pane == null) continue;
|
||||
String answer;
|
||||
synchronized (dialogs) {
|
||||
dialogs.add(String.valueOf(pane.getMessage()));
|
||||
answer = answers.isEmpty() ? "yes" : answers.pop();
|
||||
}
|
||||
if (pane.getWantsInput()) {
|
||||
pane.setInputValue(answer);
|
||||
pane.setValue(JOptionPane.OK_OPTION);
|
||||
} else {
|
||||
pane.setValue(answer.equals("no") ? JOptionPane.NO_OPTION : pane.getOptionType() == JOptionPane.DEFAULT_OPTION ? JOptionPane.OK_OPTION : JOptionPane.YES_OPTION);
|
||||
}
|
||||
window.setVisible(false);
|
||||
}
|
||||
try {
|
||||
Thread.sleep(50);
|
||||
} catch (InterruptedException error) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
watcher.setDaemon(true);
|
||||
watcher.start();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static JList<String> list() throws Exception {
|
||||
return (JList<String>) field(GUIApplicationFileExplorerWindow.class, explorer, "dateiListe");
|
||||
}
|
||||
|
||||
static JTree tree() throws Exception {
|
||||
return (JTree) field(GUIApplicationFileExplorerWindow.class, explorer, "tv");
|
||||
}
|
||||
|
||||
/** A right click on the list: on the entry's row, or below the entries; the menu Java shows. */
|
||||
static void rightClick(String entry) throws Exception {
|
||||
JList<String> list = list();
|
||||
int index = -1;
|
||||
for (int each = 0; each < list.getModel().getSize(); each++) if (list.getModel().getElementAt(each).endsWith(";" + entry)) index = each;
|
||||
if (entry != null && index < 0) throw new IllegalStateException("no entry " + entry);
|
||||
int at = index;
|
||||
List<JPopupMenu> before = popups();
|
||||
onEdt(() -> {
|
||||
Point point = at >= 0 ? list.indexToLocation(at) : new Point(5, list.getModel().getSize() * 16 + 40);
|
||||
MouseEvent event = new MouseEvent(list, MouseEvent.MOUSE_CLICKED, System.currentTimeMillis(), 0, point.x + 2, point.y + 2, 1, true, MouseEvent.BUTTON3);
|
||||
for (MouseListener listener : list.getMouseListeners()) listener.mouseClicked(event);
|
||||
});
|
||||
JPopupMenu popup = null;
|
||||
for (JPopupMenu each : popups()) if (!before.contains(each) && each.isVisible()) popup = each;
|
||||
if (popup == null) throw new IllegalStateException("no menu");
|
||||
menu = new ArrayList<>();
|
||||
for (Component item : popup.getComponents()) if (item instanceof JMenuItem) menu.add(((JMenuItem) item).getText());
|
||||
lastMenu = popup;
|
||||
}
|
||||
|
||||
static JPopupMenu lastMenu;
|
||||
|
||||
/** The menus Java shows (a shown menu lies in its frame's layer, not in the window that made it). */
|
||||
static List<JPopupMenu> popups() {
|
||||
List<JPopupMenu> found = new ArrayList<>();
|
||||
for (Window window : Window.getWindows()) {
|
||||
all(window, JPopupMenu.class, found);
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
/** A right click and the item chosen in its menu. */
|
||||
static void choose(String entry, String item, String... typed) throws Exception {
|
||||
rightClick(entry);
|
||||
synchronized (dialogs) {
|
||||
answers.addAll(Arrays.asList(typed));
|
||||
}
|
||||
JMenuItem chosen = null;
|
||||
for (Component each : lastMenu.getComponents()) if (each instanceof JMenuItem && item.equals(((JMenuItem) each).getText())) chosen = (JMenuItem) each;
|
||||
if (chosen == null) throw new IllegalStateException("no item " + item + " in " + menu);
|
||||
JMenuItem pressed = chosen;
|
||||
onEdt(() -> {
|
||||
lastMenu.setVisible(false);
|
||||
pressed.doClick();
|
||||
});
|
||||
Thread.sleep(400);
|
||||
}
|
||||
|
||||
/** Chooses the tree's row of the path (its folders' names from the root), as a click on it does. */
|
||||
static void select(String... names) throws Exception {
|
||||
JTree tree = tree();
|
||||
onEdt(() -> {
|
||||
DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getModel().getRoot();
|
||||
TreePath path = new TreePath(node);
|
||||
for (String name : names) {
|
||||
DefaultMutableTreeNode next = null;
|
||||
for (Enumeration<?> each = node.children(); each.hasMoreElements();) {
|
||||
DefaultMutableTreeNode child = (DefaultMutableTreeNode) each.nextElement();
|
||||
if (name.equals(child.getUserObject() instanceof Datei ? ((Datei) child.getUserObject()).getName() : child.toString())) next = child;
|
||||
}
|
||||
if (next == null) throw new IllegalStateException("no tree node " + name);
|
||||
node = next;
|
||||
path = path.pathByAddingChild(node);
|
||||
}
|
||||
tree.setSelectionPath(path);
|
||||
});
|
||||
}
|
||||
|
||||
static void command(String line) throws Exception {
|
||||
StringTokenizer tokens = new StringTokenizer(line, " ");
|
||||
String name = tokens.nextToken();
|
||||
String[] parameters = new String[3 + tokens.countTokens()];
|
||||
Arrays.fill(parameters, "");
|
||||
for (int index = 0; tokens.hasMoreTokens(); index++) parameters[index] = tokens.nextToken();
|
||||
terminal.terminalEingabeAuswerten(name, parameters);
|
||||
Thread.sleep(500);
|
||||
}
|
||||
|
||||
static List<List<String>> files() {
|
||||
List<List<String>> files = new ArrayList<>();
|
||||
walk(system.getDateisystem().getRoot(), "", files);
|
||||
return files;
|
||||
}
|
||||
|
||||
static void walk(DefaultMutableTreeNode folder, String path, List<List<String>> files) {
|
||||
for (Enumeration<?> each = folder.children(); each.hasMoreElements();) {
|
||||
DefaultMutableTreeNode node = (DefaultMutableTreeNode) each.nextElement();
|
||||
if (node.getUserObject() instanceof Datei) {
|
||||
Datei datei = (Datei) node.getUserObject();
|
||||
String content = datei.getDateiInhalt();
|
||||
boolean text = String.valueOf(datei.getDateiTyp()).startsWith("text");
|
||||
files.add(Arrays.asList(path + "/" + datei.getName(), String.valueOf(datei.getDateiTyp()), text ? content : "(" + (content == null ? 0 : content.length()) + " characters)"));
|
||||
// A node below a file (Java can make one): written as it is.
|
||||
if (node.getChildCount() > 0) walk(node, path + "/" + datei.getName(), files);
|
||||
} else {
|
||||
files.add(Arrays.asList(path + "/" + node + "/", "folder", ""));
|
||||
walk(node, path + "/" + node, files);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static String title(GUIApplicationWindow window) throws Exception {
|
||||
return ((JLabel) field(GUIApplicationWindow.class, window, "titleLabel")).getText();
|
||||
}
|
||||
|
||||
static void step(String name) throws Exception {
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("step", name);
|
||||
JTree tree = tree();
|
||||
List<String> rows = new ArrayList<>();
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
for (int row = 0; row < tree.getRowCount(); row++) tree.expandRow(row);
|
||||
for (int row = 0; row < tree.getRowCount(); row++) {
|
||||
TreePath path = tree.getPathForRow(row);
|
||||
rows.add(path.getPathCount() - 1 + ":" + path.getLastPathComponent());
|
||||
}
|
||||
});
|
||||
result.put("tree", rows);
|
||||
List<String> entries = new ArrayList<>();
|
||||
ListModel<String> model = list().getModel();
|
||||
for (int index = 0; index < model.getSize(); index++) entries.add(model.getElementAt(index));
|
||||
result.put("list", entries);
|
||||
if (menu != null) result.put("menu", menu);
|
||||
menu = null;
|
||||
synchronized (dialogs) {
|
||||
result.put("dialogs", new ArrayList<>(dialogs));
|
||||
dialogs.clear();
|
||||
}
|
||||
if (!shown.isEmpty()) result.put("shown", shown);
|
||||
shown = new ArrayList<>();
|
||||
result.put("files", files());
|
||||
steps.add(result);
|
||||
}
|
||||
|
||||
/** What the image viewer shows: its title and its pictures (their sizes; -1 when none could be read). */
|
||||
static void viewerStep(String name) throws Exception {
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("step", name);
|
||||
result.put("title", title(viewer));
|
||||
List<String> pictures = new ArrayList<>();
|
||||
for (JLabel label : all((JPanel) field(GUIApplicationImageViewerWindow.class, viewer, "backPanel"), JLabel.class, new ArrayList<>())) {
|
||||
ImageIcon icon = (ImageIcon) label.getIcon();
|
||||
pictures.add(icon == null ? "none" : icon.getIconWidth() + "x" + icon.getIconHeight() + (label.isShowing() ? "" : " hidden") + " at " + label.getX() + "," + label.getY());
|
||||
}
|
||||
result.put("pictures", pictures);
|
||||
if (!shown.isEmpty()) result.put("shown", shown);
|
||||
shown = new ArrayList<>();
|
||||
steps.add(result);
|
||||
}
|
||||
|
||||
static JDialog fileDialog() throws Exception {
|
||||
long until = System.currentTimeMillis() + 5000;
|
||||
while (System.currentTimeMillis() < until) {
|
||||
for (Window each : Window.getWindows()) {
|
||||
if (each instanceof JDialog && each.isVisible() && find((Container) each, JList.class) != null) return (JDialog) each;
|
||||
}
|
||||
Thread.sleep(100);
|
||||
}
|
||||
throw new IllegalStateException("no file dialog");
|
||||
}
|
||||
|
||||
/** The viewer's "Öffnen": the folders double clicked, the file's name typed, "Öffnen". */
|
||||
static void viewerOpen(String folder, String name) throws Exception {
|
||||
onEdt(button(viewer, "Öffnen")::doClick);
|
||||
JDialog dialog = fileDialog();
|
||||
if (folder != null) {
|
||||
JList<?> list = find(dialog.getContentPane(), JList.class);
|
||||
int index = -1;
|
||||
for (int each = 0; each < list.getModel().getSize(); each++) if (("Ordner;" + folder).equals(String.valueOf(list.getModel().getElementAt(each)))) index = each;
|
||||
int found = index;
|
||||
onEdt(() -> {
|
||||
Point at = list.indexToLocation(found);
|
||||
MouseEvent event = new MouseEvent(list, MouseEvent.MOUSE_CLICKED, System.currentTimeMillis(), 0, at.x + 2, at.y + 2, 2, false, MouseEvent.BUTTON1);
|
||||
for (MouseListener listener : list.getMouseListeners()) listener.mouseClicked(event);
|
||||
});
|
||||
}
|
||||
JTextField field = find(dialog.getContentPane(), JTextField.class);
|
||||
onEdt(() -> field.setText(name));
|
||||
onEdt(button(dialog.getContentPane(), "Öffnen")::doClick);
|
||||
Thread.sleep(500);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
try {
|
||||
run(args);
|
||||
} catch (Throwable error) {
|
||||
error.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
static void run(String[] args) throws Exception {
|
||||
ScenarioNetwork network = ScenarioNetwork.read(args[0]);
|
||||
system = (Betriebssystem) network.nodes.get(CLIENT).getSystemSoftware();
|
||||
system.installApp("filius.software.lokal.FileExplorer", false);
|
||||
system.installApp("filius.software.lokal.ImageViewer", false);
|
||||
system.installApp("filius.software.lokal.Terminal", false);
|
||||
system.installApp("filius.software.www.WebServer", false);
|
||||
network.start();
|
||||
Thread.sleep(1000);
|
||||
terminal = (Terminal) system.holeSoftware("filius.software.lokal.Terminal");
|
||||
watchDialogs();
|
||||
|
||||
JFrame frame = new JFrame();
|
||||
desktop = new GUIDesktopPanel(system);
|
||||
frame.getContentPane().add(desktop);
|
||||
explorer = (GUIApplicationFileExplorerWindow) desktop.starteAnwendung("filius.software.lokal.FileExplorer");
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
frame.pack();
|
||||
frame.setVisible(true);
|
||||
});
|
||||
Thread.sleep(500);
|
||||
Map<String, Object> open = new LinkedHashMap<>();
|
||||
open.put("step", "explorer title");
|
||||
open.put("title", title(explorer));
|
||||
open.put("buttons", Arrays.asList(button(explorer, "Aktualisieren").getText(), button(explorer, "Importieren").getText()));
|
||||
steps.add(open);
|
||||
step("open");
|
||||
|
||||
// The menu below the entries: a new folder, named in Java's input dialog.
|
||||
choose(null, "Neuer Ordner", "ordner");
|
||||
step("new folder ordner");
|
||||
// The command line changes the files: does the list follow by itself?
|
||||
command("mkdir extern");
|
||||
command("touch notiz.txt");
|
||||
step("command line made extern and notiz.txt");
|
||||
onEdt(button(explorer, "Aktualisieren")::doClick);
|
||||
step("Aktualisieren");
|
||||
|
||||
// An entry's menu; copy, then the free space's menu has "Einfügen", once and again.
|
||||
rightClick("notiz.txt");
|
||||
step("menu of an entry");
|
||||
choose("notiz.txt", "Kopieren");
|
||||
rightClick(null);
|
||||
step("menu after copy");
|
||||
select("ordner");
|
||||
choose(null, "Einfügen");
|
||||
step("paste the copy into ordner");
|
||||
select("extern");
|
||||
rightClick(null);
|
||||
step("menu after the paste of a copy");
|
||||
choose(null, "Einfügen");
|
||||
step("paste the copy again, into extern");
|
||||
|
||||
// Cut and paste: the free space's menu has no "Einfügen" afterwards.
|
||||
select();
|
||||
choose("notiz.txt", "Ausschneiden");
|
||||
select("webserver");
|
||||
choose(null, "Einfügen");
|
||||
rightClick(null);
|
||||
step("cut notiz.txt and paste it into webserver, then the menu");
|
||||
|
||||
// Rename: Java's question, a name there already, a blank name, a new one.
|
||||
select();
|
||||
choose("ordner", "Umbenennen", "extern");
|
||||
step("rename ordner to extern");
|
||||
choose("ordner", "Umbenennen", " ");
|
||||
step("rename ordner to blanks");
|
||||
choose("ordner", "Umbenennen", "Ordner Neu");
|
||||
step("rename ordner to Ordner Neu");
|
||||
|
||||
// Delete: Java asks; no, then yes.
|
||||
choose("extern", "Löschen", "no");
|
||||
step("delete extern, no");
|
||||
choose("extern", "Löschen", "yes");
|
||||
step("delete extern, yes");
|
||||
|
||||
// A file chosen in the tree: the list is empty; a new folder there.
|
||||
select("webserver", "index.html");
|
||||
step("a file chosen in the tree");
|
||||
choose(null, "Neuer Ordner", "unter");
|
||||
step("new folder in a file chosen in the tree");
|
||||
|
||||
// The import window: its texts, an import without a file, one with the fields as "Datei wählen" fills them.
|
||||
select();
|
||||
onEdt(button(explorer, "Importieren")::doClick);
|
||||
List<String> texts = new ArrayList<>();
|
||||
JPanel layer = (JPanel) field(GUIDesktopPanel.class, desktop, "dialogLayer");
|
||||
for (JLabel label : all(layer, JLabel.class, new ArrayList<>())) texts.add("label:" + label.getText());
|
||||
for (AbstractButton each : all(layer, AbstractButton.class, new ArrayList<>())) texts.add("button:" + each.getText());
|
||||
List<JTextField> fields = all(layer, JTextField.class, new ArrayList<>());
|
||||
for (JTextField each : fields) texts.add("field:" + each.getText() + (each.isEditable() ? "" : " (read only)"));
|
||||
shown.add(texts);
|
||||
JTextArea output = find(layer, JTextArea.class);
|
||||
press(button(layer, "Datei importieren"));
|
||||
shown.add("output:" + output.getText());
|
||||
java.io.File local = java.io.File.createTempFile("import", ".txt");
|
||||
java.nio.file.Files.write(local.toPath(), "Zeile 1\nZeile 2".getBytes(StandardCharsets.UTF_8));
|
||||
setField(GUIApplicationFileExplorerWindow.class, explorer, "datei", local.getName());
|
||||
setField(GUIApplicationFileExplorerWindow.class, explorer, "pfad", local.getParent() + java.io.File.separator);
|
||||
onEdt(() -> {
|
||||
fields.get(0).setText(local.getPath());
|
||||
fields.get(1).setText("importiert.txt");
|
||||
});
|
||||
press(button(layer, "Datei importieren"));
|
||||
shown.add("output:" + output.getText());
|
||||
press(button(layer, "Schließen"));
|
||||
shown.add("layer visible:" + layer.isVisible());
|
||||
step("import window");
|
||||
|
||||
// The image viewer.
|
||||
viewer = (GUIApplicationImageViewerWindow) desktop.starteAnwendung("filius.software.lokal.ImageViewer");
|
||||
Thread.sleep(500);
|
||||
viewerStep("viewer open");
|
||||
viewerOpen("webserver", "splashscreen-mini.png");
|
||||
viewerStep("viewer: the web server's picture");
|
||||
viewerOpen("webserver", "splashscreen-mini.png");
|
||||
viewerStep("viewer: the picture again");
|
||||
viewerOpen(null, "importiert.txt");
|
||||
viewerStep("viewer: a text file");
|
||||
viewerOpen(null, "fehlt.png");
|
||||
viewerStep("viewer: no such file");
|
||||
|
||||
Map<String, Object> output2 = new LinkedHashMap<>();
|
||||
output2.put("steps", steps);
|
||||
try (FileOutputStream out = new FileOutputStream(args[1])) {
|
||||
out.write((FlsOracle.Json.write(output2, "") + "\n").getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
network.stop();
|
||||
System.out.println("file explorer window: " + steps.size() + " steps");
|
||||
}
|
||||
|
||||
/** A press with the mouse, as Java's import window listens (mousePressed). */
|
||||
static void press(AbstractButton button) throws Exception {
|
||||
onEdt(() -> {
|
||||
MouseEvent event = new MouseEvent(button, MouseEvent.MOUSE_PRESSED, System.currentTimeMillis(), 0, 5, 5, 1, false);
|
||||
for (MouseListener listener : button.getMouseListeners()) listener.mousePressed(event);
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user