ExperimentDocumentPreviewDialog.java

package org.hammer;

import java.awt.BorderLayout;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.nio.file.Path;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingWorker;
import org.hammer.audio.experiment.document.ExperimentDocumentPreview;
import org.hammer.audio.experiment.document.ExperimentDocumentService;
import org.hammer.audio.workflow.Workflow;

/** Modeless, copyable preview and explicit Save-As surface for one experiment document. */
final class ExperimentDocumentPreviewDialog {

  private static final String TITLE = "Portable experiment document";

  private final JFrame owner;
  private final ExperimentDocumentService documentService;

  ExperimentDocumentPreviewDialog(JFrame owner, ExperimentDocumentService documentService) {
    this.owner = java.util.Objects.requireNonNull(owner, "owner");
    this.documentService = java.util.Objects.requireNonNull(documentService, "documentService");
  }

  /** Display a modeless preview; no workflow state is applied or executed. */
  void show(Path source, ExperimentDocumentPreview preview) {
    Workflow workflow = documentService.workflow(preview);
    JDialog dialog = new JDialog(owner, TITLE, false);
    dialog.setLayout(new BorderLayout(8, 8));

    JTextArea summary =
        new JTextArea(ExperimentDocumentPreviewFormatter.format(source, preview, workflow));
    summary.setEditable(false);
    summary.setLineWrap(true);
    summary.setWrapStyleWord(true);
    summary.setCaretPosition(0);
    summary.getAccessibleContext().setAccessibleName("Experiment document preview");
    JScrollPane scrollPane = new JScrollPane(summary);
    scrollPane.setPreferredSize(new Dimension(760, 480));
    dialog.add(scrollPane, BorderLayout.CENTER);

    JPanel actions = new JPanel(new FlowLayout(FlowLayout.RIGHT));
    JButton saveAs = new JButton("Save normalized copy...");
    saveAs.setToolTipText(
        "Write a canonical copy to a distinct path without modifying the imported source.");
    saveAs.addActionListener(event -> chooseNormalizeTarget(dialog, source));
    actions.add(saveAs);
    JButton close = new JButton("Close");
    close.addActionListener(event -> dialog.dispose());
    actions.add(close);
    dialog.add(actions, BorderLayout.SOUTH);

    dialog.pack();
    dialog.setLocationRelativeTo(owner);
    dialog.setVisible(true);
  }

  private void chooseNormalizeTarget(JDialog dialog, Path source) {
    Path target = ExperimentDocumentFileChooser.chooseSave(dialog);
    if (target == null) {
      return;
    }
    dialog.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
    new SwingWorker<ExperimentDocumentPreview, Void>() {
      @Override
      protected ExperimentDocumentPreview doInBackground() throws Exception {
        return documentService.normalize(source, target);
      }

      @Override
      protected void done() {
        dialog.setCursor(Cursor.getDefaultCursor());
        try {
          get();
          JOptionPane.showMessageDialog(
              dialog,
              "Normalized experiment document saved to:\n" + target,
              TITLE,
              JOptionPane.INFORMATION_MESSAGE);
        } catch (InterruptedException exception) {
          Thread.currentThread().interrupt();
          showFailure("Normalization was interrupted.", exception);
        } catch (java.util.concurrent.ExecutionException exception) {
          showFailure("Could not save normalized experiment document.", exception.getCause());
        }
      }
    }.execute();
  }

  private void showFailure(String message, Throwable failure) {
    String detail = failure == null || failure.getMessage() == null ? "" : failure.getMessage();
    JOptionPane.showMessageDialog(
        owner,
        detail.isBlank() ? message : message + "\n" + detail,
        TITLE,
        JOptionPane.ERROR_MESSAGE);
  }
}