/*
* Copyright (C) 2024 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package com.jpexs.decompiler.flash.easygui.properties;
import java.awt.AWTEvent;
import java.awt.BasicStroke;
import java.awt.CardLayout;
import java.awt.Cursor;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Stroke;
import java.awt.Toolkit;
import java.awt.event.AWTEventListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
/**
*
* @author JPEXS
*/
public abstract class AbstractPropertyField extends JPanel {
private static final String CARD_READ = "Read";
private static final String CARD_WRITE = "Write";
protected JLabel readLabel;
protected JTextField writeField;
private final List> validations = new ArrayList<>();
private final List changeListeners = new ArrayList<>();
private AWTEventListener aeListener;
private boolean undetermined = false;
public void addValidation(PropertyValidationInteface validation) {
validations.add(validation);
}
public void removeValidation(PropertyValidationInteface validation) {
validations.remove(validation);
}
public void addChangeListener(ChangeListener changeListener) {
changeListeners.add(changeListener);
}
public void removeChangeListener(ChangeListener changeListener) {
changeListeners.remove(changeListener);
}
private void fireChange() {
for (ChangeListener listener : changeListeners) {
listener.stateChanged(new ChangeEvent(this));
}
}
@SuppressWarnings("unchecked")
public AbstractPropertyField(String text) {
setLayout(new CardLayout());
readLabel = new DottedUnderlineLabel(text);
readLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
writeField = new JTextField(text);
add(readLabel, CARD_READ);
add(writeField, CARD_WRITE);
readLabel.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 1) {
((CardLayout)AbstractPropertyField.this.getLayout()).show(AbstractPropertyField.this, CARD_WRITE);
writeField.requestFocus();
writeField.selectAll();
Toolkit.getDefaultToolkit().addAWTEventListener(aeListener, AWTEvent.MOUSE_EVENT_MASK);
}
}
});
writeField.addFocusListener(new FocusAdapter() {
@Override
public void focusLost(FocusEvent e) {
finishEdit();
}
});
writeField.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
e.consume();
finishEdit();
}
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
e.consume();
cancelEdit();
}
}
});
aeListener = new AWTEventListener() {
@Override
public void eventDispatched(AWTEvent event) {
if (event instanceof MouseEvent) {
MouseEvent me = (MouseEvent) event;
if (!SwingUtilities.isDescendingFrom(me.getComponent(), writeField)) {
if (me.getClickCount() > 0) {
finishEdit();
}
}
}
}
};
}
protected abstract E textToValue(String text);
protected abstract String valueToText(E value);
private void finishEdit() {
String textBefore = readLabel.getText();
String textAfter = writeField.getText();
if (textBefore.equals(textAfter)) {
cancelEdit();
return;
}
Toolkit.getDefaultToolkit().removeAWTEventListener(aeListener);
boolean ok = true;
E value = textToValue(textAfter);
if (value == null) {
ok = false;
} else {
for (PropertyValidationInteface validation : validations) {
if (!validation.validate(value)) {
ok = false;
break;
}
}
}
if (!ok) {
cancelEdit();
return;
}
undetermined = false;
readLabel.setText(valueToText(value));
((CardLayout)AbstractPropertyField.this.getLayout()).show(AbstractPropertyField.this, CARD_READ);
fireChange();
}
private void cancelEdit() {
Toolkit.getDefaultToolkit().removeAWTEventListener(aeListener);
if (undetermined) {
writeField.setText("");
} else {
writeField.setText(readLabel.getText());
}
((CardLayout)AbstractPropertyField.this.getLayout()).show(AbstractPropertyField.this, CARD_READ);
}
public E getValue() {
if (undetermined) {
return null;
}
return textToValue(writeField.getText());
}
public void setValue(Set value) {
if (value.size() != 1) {
setValue((E) null);
} else {
setValue(value.iterator().next());
}
}
public void setValue(E value) {
if (value == null) {
readLabel.setText("-");
writeField.setText("");
undetermined = true;
fireChange();
return;
}
String text = valueToText(value);
readLabel.setText(text);
writeField.setText(text);
fireChange();
}
}
class DottedUnderlineLabel extends JLabel {
public DottedUnderlineLabel(String text) {
super(text);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
Font font = getFont();
FontMetrics metrics = getFontMetrics(font);
String text = getText();
int x = 0;
int y = metrics.getAscent();
g2d.setPaint(getForeground());
int textWidth = metrics.stringWidth(text);
int underlineY = y + 6;
float[] dash = {1f, 1f};
Stroke dotted = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 1f, dash, 0f);
g2d.setStroke(dotted);
g2d.draw(new Line2D.Float(x, underlineY, x + textWidth, underlineY));
}
}