r4fterman/pdf.forms

View on GitHub
src/main/java/org/pdf/forms/gui/properties/font/FontPropertiesPanel.java

Summary

Maintainability
D
2 days
Test Coverage
F
42%
package org.pdf.forms.gui.properties.font;

import static java.util.stream.Collectors.toUnmodifiableList;
import static org.jdesktop.layout.GroupLayout.BASELINE;
import static org.jdesktop.layout.GroupLayout.DEFAULT_SIZE;
import static org.jdesktop.layout.GroupLayout.LEADING;
import static org.jdesktop.layout.GroupLayout.PREFERRED_SIZE;
import static org.jdesktop.layout.GroupLayout.TRAILING;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.Set;

import javax.swing.*;

import org.jdesktop.layout.GroupLayout;
import org.jdesktop.layout.LayoutStyle;
import org.pdf.forms.fonts.FontHandler;
import org.pdf.forms.gui.designer.IDesigner;
import org.pdf.forms.gui.properties.customcomponents.colorcombobox.ColorCellRenderer;
import org.pdf.forms.gui.properties.customcomponents.colorcombobox.ColorComboBoxEditor;
import org.pdf.forms.model.des.FontCaption;
import org.pdf.forms.model.des.FontValue;
import org.pdf.forms.widgets.IWidget;

public class FontPropertiesPanel extends JPanel {

    private static final String[] FONT_SIZES = {
            "6",
            "8",
            "10",
            "12",
            "14",
            "16",
            "18",
            "20",
            "24",
            "28",
            "36",
            "48",
            "72"
    };
    private static final String[] FONT_STYLES = {
            "Plain",
            "Bold",
            "Italic",
            "Bold Italic"};
    private static final String PROPERTY_UNDERLINE = "Underline";
    private static final String[] UNDERLINE_TYPES = {
            "No Underline",
            PROPERTY_UNDERLINE,
            "Double Underline",
            "Word Underline",
            "Word Double Underline"};

    private static final String[] STRIKETHROUGH_VALUES = {
            "Off",
            "On"};

    private static final String CUSTOM_COLOR = "Custom";
    private static final Object[] COLORS = {
            Color.black,
            Color.blue,
            Color.cyan,
            Color.green,
            Color.red,
            Color.white,
            Color.yellow,
            CUSTOM_COLOR};

    private static final String[] EDITING_VALUES = {
            "Caption and Value",
            "Caption properties",
            "Value properties"};

    private static final String ATTRIBUTE_VALUE = "value";

    private static final String PROPERTY_COLOR = "Color";
    private static final String PROPERTY_STRIKETHROUGH = "Strikethrough";
    private static final String PROPERTY_FONT_STYLE = "Font Style";
    private static final String PROPERTY_FONT_SIZE = "Font Size";
    private static final String PROPERTY_FONT_NAME = "Font Name";

    private final FontHandler fontHandler;

    private ColorComboBoxEditor editor;
    private IDesigner designerPanel;
    private Set<IWidget> widgets;

    private JComboBox<Object> colorBox;
    private JComboBox<String> currentlyEditingBox;
    private JComboBox<String> fontNameBox;
    private JComboBox<String> fontSizeBox;
    private JComboBox<String> fontStyleBox;
    private JComboBox<String> strikethroughBox;
    private JComboBox<String> underlineBox;

    FontPropertiesPanel(final FontHandler fontHandler) {
        this.fontHandler = fontHandler;

        initComponents();
    }

    public void setDesignerPanel(final IDesigner designerPanel) {
        this.designerPanel = designerPanel;
    }

    private void initComponents() {
        final JLabel currentlyEditingLabel = new JLabel("Currently Editing:");

        currentlyEditingBox = new JComboBox<>(EDITING_VALUES);
        currentlyEditingBox.addActionListener(this::updateCurrentlyEditingBox);

        final JLabel fontLabel = new JLabel("Font:");
        final JLabel fontSizeLabel = new JLabel("Font Size:");
        final JLabel fontStyleLabel = new JLabel("Font Style:");
        final JLabel underlineLabel = new JLabel("Underline:");
        underlineLabel.setEnabled(false);

        final JLabel strikethroughLabel = new JLabel("Strikethrough:");
        strikethroughLabel.setEnabled(false);

        final JLabel colorLabel = new JLabel("Color:");

        fontNameBox = new JComboBox<>(fontHandler.getFontFamilies());
        fontNameBox.addActionListener(this::updateFont);

        fontStyleBox = new JComboBox<>(FONT_STYLES);
        fontStyleBox.addActionListener(this::updateFont);

        underlineBox = new JComboBox<>(UNDERLINE_TYPES);
        underlineBox.setEnabled(false);
        underlineBox.addActionListener(this::updateFont);

        strikethroughBox = new JComboBox<>(STRIKETHROUGH_VALUES);
        strikethroughBox.setEnabled(false);
        strikethroughBox.addActionListener(this::updateFont);

        colorBox = new JComboBox<>(COLORS);
        colorBox.setEditable(true);
        colorBox.setMaximumRowCount(5);

        editor = new ColorComboBoxEditor((Color) colorBox.getSelectedItem(), colorBox);

        colorBox.setEditor(editor);
        colorBox.setRenderer(new ColorCellRenderer());
        colorBox.addActionListener(this::updateColor);

        fontSizeBox = new JComboBox<>(FONT_SIZES);
        fontSizeBox.setEditable(true);
        fontSizeBox.addActionListener(this::updateFont);

        final GroupLayout layout = new GroupLayout(this);
        setLayout(layout);

        final GroupLayout.SequentialGroup sequentialGroup = layout.createSequentialGroup()
                .add(fontStyleLabel)
                .addPreferredGap(LayoutStyle.RELATED, 35, Short.MAX_VALUE)
                .add(fontStyleBox, PREFERRED_SIZE, 162, PREFERRED_SIZE);

        final GroupLayout.SequentialGroup sequentialGroup1 = layout.createSequentialGroup()
                .add(currentlyEditingLabel)
                .addPreferredGap(LayoutStyle.RELATED)
                .add(currentlyEditingBox, PREFERRED_SIZE, 162, PREFERRED_SIZE);
        final GroupLayout.SequentialGroup sequentialGroup2 = layout.createSequentialGroup()
                .add(colorLabel)
                .addPreferredGap(LayoutStyle.RELATED, 59, Short.MAX_VALUE)
                .add(colorBox, PREFERRED_SIZE, 162, PREFERRED_SIZE);
        final GroupLayout.SequentialGroup sequentialGroup3 = layout.createSequentialGroup()
                .add(underlineLabel)
                .addPreferredGap(LayoutStyle.RELATED, 39, Short.MAX_VALUE)
                .add(underlineBox, PREFERRED_SIZE, 162, PREFERRED_SIZE);
        final GroupLayout.SequentialGroup sequentialGroup4 = layout.createSequentialGroup()
                .add(strikethroughLabel)
                .addPreferredGap(LayoutStyle.RELATED, 19, Short.MAX_VALUE)
                .add(strikethroughBox, PREFERRED_SIZE, 162, PREFERRED_SIZE);

        final GroupLayout.ParallelGroup parallelGroup1 = layout.createParallelGroup(TRAILING)
                .add(fontNameBox, PREFERRED_SIZE, 162, PREFERRED_SIZE)
                .add(fontSizeBox, PREFERRED_SIZE, 162, PREFERRED_SIZE);

        final GroupLayout.SequentialGroup sequentialGroup5 = layout.createSequentialGroup()
                .add(layout.createParallelGroup(LEADING).add(fontLabel).add(fontSizeLabel))
                .addPreferredGap(LayoutStyle.RELATED, 40, Short.MAX_VALUE)
                .add(parallelGroup1);

        final GroupLayout.ParallelGroup parallelGroup = layout.createParallelGroup(LEADING)
                .add(sequentialGroup)
                .add(sequentialGroup1)
                .add(sequentialGroup2)
                .add(sequentialGroup3)
                .add(sequentialGroup4)
                .add(TRAILING, sequentialGroup5);

        final GroupLayout.SequentialGroup sequentialGroup6 = layout.createSequentialGroup()
                .addContainerGap()
                .add(parallelGroup)
                .addContainerGap(140, Short.MAX_VALUE);

        layout.setHorizontalGroup(layout.createParallelGroup(LEADING).add(sequentialGroup6));

        final GroupLayout.SequentialGroup sequentialGroup7 = layout.createSequentialGroup()
                .addContainerGap()
                .add(layout.createParallelGroup(BASELINE)
                        .add(currentlyEditingLabel)
                        .add(currentlyEditingBox, PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE))
                .addPreferredGap(LayoutStyle.RELATED)
                .add(layout.createParallelGroup(BASELINE)
                        .add(fontLabel)
                        .add(fontNameBox, PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE))
                .addPreferredGap(LayoutStyle.RELATED)
                .add(layout.createParallelGroup(BASELINE)
                        .add(fontSizeLabel)
                        .add(fontSizeBox, PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE))
                .addPreferredGap(LayoutStyle.RELATED)
                .add(layout.createParallelGroup(BASELINE)
                        .add(fontStyleLabel)
                        .add(fontStyleBox, PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE))
                .addPreferredGap(LayoutStyle.RELATED)
                .add(layout.createParallelGroup(BASELINE)
                        .add(underlineLabel)
                        .add(underlineBox, PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE))
                .addPreferredGap(LayoutStyle.RELATED)
                .add(layout.createParallelGroup(BASELINE)
                        .add(strikethroughLabel)
                        .add(strikethroughBox, PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE))
                .addPreferredGap(LayoutStyle.RELATED)
                .add(layout.createParallelGroup(BASELINE)
                        .add(colorLabel)
                        .add(colorBox, PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE))
                .addContainerGap(113, Short.MAX_VALUE);
        layout.setVerticalGroup(layout.createParallelGroup(LEADING).add(sequentialGroup7));
    }

    private void updateFont(final ActionEvent event) {
        widgets.forEach(widget -> {
            updateFontProperties(widget);
            // todo: set widget model font properties
            //widget.setFontProperties(value, currentlyEditingBox.getSelectedIndex());
        });

        designerPanel.getMainFrame().setPropertiesToolBar(widgets);
        designerPanel.repaint();
    }

    private void updateFontProperties(final IWidget widget) {
        widget.getWidgetModel().getProperties().getFont().ifPresent(fontProperties -> {
            final FontCaption fontCaption = fontProperties.getFontCaption();

            final Optional<FontValue> fontValue;
            if (widget.allowEditCaptionAndValue()) {
                fontValue = Optional.of(fontProperties.getFontValue());
            } else {
                fontValue = Optional.empty();
            }

            updateFontNameProperty(widget, fontCaption, fontValue);
            updateFontSizeProperty(widget, fontCaption, fontValue);
            updateFontStyleProperty(widget, fontCaption, fontValue);
            updateUnderlineProperty(widget, fontCaption, fontValue);
            updateStrikethroughProperty(widget, fontCaption, fontValue);
            updateColorProperty(widget, fontCaption, fontValue);
        });
    }

    private void updateFontNameProperty(
            final IWidget widget,
            final FontCaption fontCaption,
            final Optional<FontValue> fontValue) {
        final String value = Optional.ofNullable(fontNameBox.getSelectedItem()).map(Object::toString).orElse("");

        if ("Caption and Value".equals(currentlyEditingBox.getSelectedItem())) {
            fontCaption.setFontName(value);
            if (widget.allowEditCaptionAndValue()) {
                fontValue.ifPresent(font -> font.setFontName(value));
            }
        } else if ("Caption properties".equals(currentlyEditingBox.getSelectedItem())) {
            fontCaption.setFontName(value);
        } else if ("Value properties".equals(currentlyEditingBox.getSelectedItem())) {
            fontValue.ifPresent(font -> font.setFontName(value));
        }
    }

    private void updateFontSizeProperty(
            final IWidget widget,
            final FontCaption fontCaption,
            final Optional<FontValue> fontValue) {
        final String value = Optional.ofNullable(fontSizeBox.getSelectedItem()).map(Object::toString).orElse("");

        if ("Caption and Value".equals(currentlyEditingBox.getSelectedItem())) {
            fontCaption.setFontSize(value);
            if (widget.allowEditCaptionAndValue()) {
                fontValue.ifPresent(font -> font.setFontSize(value));
            }
        } else if ("Caption properties".equals(currentlyEditingBox.getSelectedItem())) {
            fontCaption.setFontSize(value);
        } else if ("Value properties".equals(currentlyEditingBox.getSelectedItem())) {
            fontValue.ifPresent(font -> font.setFontSize(value));
        }
    }

    private void updateFontStyleProperty(
            final IWidget widget,
            final FontCaption fontCaption,
            final Optional<FontValue> fontValue) {
        final String value = getComboBoxSelectedValueAsString(fontStyleBox);

        if ("Caption and Value".equals(currentlyEditingBox.getSelectedItem())) {
            fontCaption.setFontStyle(value);
            if (widget.allowEditCaptionAndValue()) {
                fontValue.ifPresent(font -> font.setFontStyle(value));
            }
        } else if ("Caption properties".equals(currentlyEditingBox.getSelectedItem())) {
            fontCaption.setFontStyle(value);
        } else if ("Value properties".equals(currentlyEditingBox.getSelectedItem())) {
            fontValue.ifPresent(font -> font.setFontStyle(value));
        }
    }

    private void updateUnderlineProperty(
            final IWidget widget,
            final FontCaption fontCaption,
            final Optional<FontValue> fontValue) {
        final String value = getComboBoxSelectedValueAsString(underlineBox);

        if ("Caption and Value".equals(currentlyEditingBox.getSelectedItem())) {
            fontCaption.setUnderline(value);
            if (widget.allowEditCaptionAndValue()) {
                fontValue.ifPresent(font -> font.setUnderline(value));
            }
        } else if ("Caption properties".equals(currentlyEditingBox.getSelectedItem())) {
            fontCaption.setUnderline(value);
        } else if ("Value properties".equals(currentlyEditingBox.getSelectedItem())) {
            fontValue.ifPresent(font -> font.setUnderline(value));
        }
    }

    private void updateStrikethroughProperty(
            final IWidget widget,
            final FontCaption fontCaption,
            final Optional<FontValue> fontValue) {
        final String value = getComboBoxSelectedValueAsString(strikethroughBox);

        if ("Caption and Value".equals(currentlyEditingBox.getSelectedItem())) {
            fontCaption.setStrikeThrough(value);
            if (widget.allowEditCaptionAndValue()) {
                fontValue.ifPresent(font -> font.setStrikeThrough(value));
            }
        } else if ("Caption properties".equals(currentlyEditingBox.getSelectedItem())) {
            fontCaption.setStrikeThrough(value);
        } else if ("Value properties".equals(currentlyEditingBox.getSelectedItem())) {
            fontValue.ifPresent(font -> font.setStrikeThrough(value));
        }
    }

    private void updateColorProperty(
            final IWidget widget,
            final FontCaption fontCaption,
            final Optional<FontValue> fontValue) {
        final String value = getSelectedColorValueAsString();

        if ("Caption and Value".equals(currentlyEditingBox.getSelectedItem())) {
            fontCaption.setColor(value);
            if (widget.allowEditCaptionAndValue()) {
                fontValue.ifPresent(font -> font.setColor(value));
            }
        } else if ("Caption properties".equals(currentlyEditingBox.getSelectedItem())) {
            fontCaption.setColor(value);
        } else if ("Value properties".equals(currentlyEditingBox.getSelectedItem())) {
            fontValue.ifPresent(font -> font.setColor(value));
        }
    }

    private void updateColor(final ActionEvent event) {
        if (CUSTOM_COLOR.equals(colorBox.getSelectedItem())) {
            final Color currentBackground = (Color) editor.getItem();
            final Color color = JColorChooser.showDialog(null, "Color Chooser", currentBackground);
            if ((color != null) && (currentBackground != color)) {
                colorBox.setSelectedItem(color);
            }
        }

        updateFont(null);
    }

    private void updateCurrentlyEditingBox(final ActionEvent event) {
        setProperties(widgets, currentlyEditingBox.getSelectedIndex());
    }

    public void updateAvailableFonts() {
        final String[] fonts = fontHandler.getFontFamilies();
        fontNameBox.setModel(new DefaultComboBoxModel<>(fonts));
    }

    public void setProperties(
            final Set<IWidget> widgets,
            final int currentlyEditing) {
        this.widgets = widgets;

        final boolean allowEditCaptionAndValue = widgets.stream()
                .anyMatch(IWidget::allowEditCaptionAndValue);

        final int currentlyEditingIdx;
        if (!allowEditCaptionAndValue) {
            currentlyEditingIdx = 1;
        } else {
            currentlyEditingIdx = currentlyEditing;
        }

        currentlyEditingBox.setSelectedIndex(currentlyEditingIdx);
        currentlyEditingBox.setEnabled(allowEditCaptionAndValue);

        final FontPropertiesList fontPropertiesList = getFontPropertiesList(
                widgets,
                currentlyEditing
        );

        setComboValue(fontNameBox, fontPropertiesList.getFontName());
        setComboValue(fontSizeBox, fontPropertiesList.getFontSize());
        setComboValue(fontStyleBox, fontPropertiesList.getFontStyle());
        setComboValue(underlineBox, fontPropertiesList.getUnderline());
        setComboValue(strikethroughBox, fontPropertiesList.getStrikethrough());
        setComboValue(colorBox, fontPropertiesList.getColor());
    }

    private FontPropertiesList getFontPropertiesList(
            final Set<IWidget> widgets,
            final int currentlyEditing) {
        final List<FontProperties> fontProperties = widgets.stream()
                .map(widget -> convertToFontProperties(widget, currentlyEditing))
                .filter(Optional::isPresent)
                .map(Optional::get)
                .collect(toUnmodifiableList());

        return new FontPropertiesList(fontProperties);
    }

    private Optional<FontProperties> convertToFontProperties(
            final IWidget widget,
            final int currentlyEditing) {
        return widget.getWidgetModel().getProperties().getFont()
                .map(font -> {
                    final FontCaption fontCaption = font.getFontCaption();
                    final String captionFontName = fontCaption.getFontName().orElse("Arial");
                    final String captionFontSize = fontCaption.getFontSize().orElse("10");
                    final String captionFontStyle = fontCaption.getFontStyle().orElse("1");
                    final String captionUnderline = fontCaption.getUnderline().orElse("1");
                    final String captionStrikethrough = fontCaption.getStrikeThrough().orElse("1");
                    final String captionColor = fontCaption.getColor().orElse(String.valueOf(Color.BLACK.getRGB()));

                    final FontValue fontValue = font.getFontValue();
                    final String valueFontName = fontValue.getFontName().orElse("Arial");
                    final String valueFontSize = fontValue.getFontSize().orElse("10");
                    final String valueFontStyle = fontValue.getFontStyle().orElse("1");
                    final String valueUnderline = fontValue.getUnderline().orElse("1");
                    final String valueStrikethrough = fontValue.getStrikeThrough().orElse("1");
                    final String valueColor = fontValue.getColor().orElse(String.valueOf(Color.BLACK.getRGB()));

                    return new FontProperties(
                            getProperty(currentlyEditing, captionFontName, valueFontName),
                            getProperty(currentlyEditing, captionFontSize, valueFontSize),
                            getProperty(currentlyEditing, captionFontStyle, valueFontStyle),
                            getProperty(currentlyEditing, captionUnderline, valueUnderline),
                            getProperty(currentlyEditing, captionStrikethrough, valueStrikethrough),
                            getProperty(currentlyEditing, captionColor, valueColor)
                    );
                });
    }

    private void setComboValue(
            final JComboBox<?> comboBox,
            final Object value) {
        final ActionListener[] listeners = comboBox.getActionListeners();
        Arrays.stream(listeners).forEach(comboBox::removeActionListener);

        if (value instanceof Integer) {
            comboBox.setSelectedIndex((Integer) value);
        } else {
            comboBox.setSelectedItem(value);
        }

        Arrays.stream(listeners).forEach(comboBox::addActionListener);
    }

    private String getProperty(
            final int currentlyEditing,
            final String captionProperty,
            final String valueProperty) {
        if (currentlyEditing == IWidget.COMPONENT_BOTH) {
            if (captionProperty.equals(valueProperty)) {
                // both are the same
                return captionProperty;
            }
            // properties are different
            return "mixed";
        } else if (currentlyEditing == IWidget.COMPONENT_CAPTION) {
            // just set the caption properties
            return captionProperty;
        } else if (currentlyEditing == IWidget.COMPONENT_VALUE) {
            // just set the value properties
            return valueProperty;
        }

        return null;
    }

    private String getComboBoxSelectedValueAsString(final JComboBox<?> comboBox) {
        final int index = comboBox.getSelectedIndex();
        if (index == -1) {
            return null;
        }
        return String.valueOf(index);
    }

    private String getSelectedColorValueAsString() {
        final Color color = ((Color) colorBox.getSelectedItem());
        if (color == null) {
            return null;
        }
        return String.valueOf(color.getRGB());
    }
}