r4fterman/pdf.forms

View on GitHub
src/main/java/org/pdf/forms/gui/designer/Designer.java

Summary

Maintainability
C
1 day
Test Coverage
F
16%
package org.pdf.forms.gui.designer;

import java.awt.*;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.swing.*;

import org.jpedal.PdfDecoder;
import org.pdf.forms.Configuration;
import org.pdf.forms.document.Page;
import org.pdf.forms.fonts.FontHandler;
import org.pdf.forms.gui.IMainFrame;
import org.pdf.forms.gui.designer.captionchanger.CaptionChanger;
import org.pdf.forms.gui.designer.gui.DesignerSelectionBox;
import org.pdf.forms.gui.designer.gui.Rule;
import org.pdf.forms.gui.designer.listeners.DesignerKeyListener;
import org.pdf.forms.gui.designer.listeners.DesignerMouseListener;
import org.pdf.forms.gui.designer.listeners.DesignerMouseMotionListener;
import org.pdf.forms.model.des.FieldProperties;
import org.pdf.forms.model.des.Version;
import org.pdf.forms.readers.properties.DesignerPropertiesFile;
import org.pdf.forms.widgets.IWidget;
import org.pdf.forms.widgets.utils.WidgetFactory;
import org.pdf.forms.widgets.utils.WidgetSelection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Designer extends PdfDecoder implements IDesigner {

    private final Logger logger = LoggerFactory.getLogger(Designer.class);

    private final WidgetSelection widgetSelection;
    private final IMainFrame mainFrame;
    private final DesignerSelectionBox selectionBox;
    private final Rule horizontalRuler;
    private final Rule verticalRuler;
    private final Point mouseLocation = new Point();
    private final CaptionChanger captionChanger = new CaptionChanger();
    private final Set<IWidget> selectedWidgets = new HashSet<>();

    private List<IWidget> pageWidgets;
    private int resizeType = DesignerMouseMotionListener.DEFAULT_CURSOR;
    private int widgetToAdd = IWidget.NONE;
    private Point dragBoxMouseLocation = null;

    private boolean isResizing;
    private int pageWidth;
    private int pageHeight;
    private final int inset;
    private boolean currentlyDragging;
    private boolean isResizingSplitComponent;
    private int drawingState;
    private Page currentPage;

    public Designer(
            final int inset,
            final Rule horizontalRuler,
            final Rule verticalRuler,
            final IMainFrame mainFrame,
            final Version version,
            final FontHandler fontHandler,
            final WidgetFactory widgetFactory,
            final Configuration configuration,
            final DesignerPropertiesFile designerPropertiesFile) {
        super();

        setBackground(BACKGROUND_COLOR);

        selectionBox = new DesignerSelectionBox(this);
        widgetSelection = new WidgetSelection(this,
                version,
                fontHandler,
                widgetFactory,
                configuration,
                designerPropertiesFile);

        this.horizontalRuler = horizontalRuler;
        this.verticalRuler = verticalRuler;
        this.inset = inset;
        this.mainFrame = mainFrame;

        addMouseListener(new DesignerMouseListener(this, widgetFactory));
        addMouseMotionListener(new DesignerMouseMotionListener(this));
        addKeyListener(new DesignerKeyListener(this));

        setLayout(null);
    }

    private void drawPage(final Graphics2D g2) {
        if (drawingState != IDesigner.CLOSED) {

            final Rectangle r = new Rectangle(inset, inset, currentPage.getWidth(), currentPage.getHeight());

            if (drawingState != IDesigner.PDFPAGE) {
                g2.setPaint(IDesigner.PAGE_COLOR);
                g2.fill(r);
            }

            g2.setColor(Color.black);
            g2.draw(r);
        }
    }

    private void drawWidget(
            final IWidget widget,
            final Graphics2D g2) {
        if (widget.getType() == IWidget.GROUP) {
            drawGroupWidget(widget, g2);
        } else {
            drawNormalWidget(widget, g2);
        }
    }

    private void drawNormalWidget(
            final IWidget widget,
            final Graphics2D g2) {
        final FieldProperties fieldProperties = widget.getWidgetModel().getProperties().getObject()
                .getField();

        fieldProperties.getPresence()
                .filter(visibility -> visibility.equals("Visible"))
                .ifPresent(visibility -> {
                    final int x = widget.getX();
                    final int y = widget.getY();

                    g2.translate(x, y);
                    widget.getComponent().paint(g2);
                    final boolean drawNodes = selectedWidgets.size() == 1 && selectedWidgets.contains(widget);
                    widgetSelection.drawSingleSectionBox(
                            g2,
                            widget,
                            drawNodes);
                    g2.translate(-x, -y);
                });
    }

    private void drawGroupWidget(
            final IWidget widget,
            final Graphics2D g2) {
        final List<IWidget> widgetsInGroup = widget.getWidgetsInGroup();
        widgetSelection.drawMulitipleSelectionBox(g2, Set.copyOf(widgetsInGroup), false);
        for (final IWidget drawWidget: widgetsInGroup) {
            drawWidget(drawWidget, g2);
        }
    }

    @Override
    public void paintComponent(final Graphics g) {
        super.paintComponent(g);

        final Graphics2D g2 = (Graphics2D) g;

        //  transform.scale(scale, scale); @scale
        g2.setTransform(g2.getTransform());
        // draw the basic blank page
        drawPage(g2);
        // draw each widget in turn onto the page
        pageWidgets.forEach(widget -> drawWidget(widget, g2));

        if (!selectedWidgets.isEmpty()) {
            drawSelection(g2);
        }
        selectionBox.paintBox(g2);
    }

    private void drawSelection(final Graphics2D g2) {
        final IWidget selectedWidget = selectedWidgets.iterator().next();

        // if we're dragging out a component from the library, dragBoxMouseLocation will
        // not be null, so we need to draw the special box, and display the tooltip.
        if (dragBoxMouseLocation != null) {
            final Rectangle boxSize = selectedWidget.getBounds();
            drawDragOutBoxAndDisplayTooltip(g2, boxSize);
        }

        // if we are dragging the widget, or resizing it, we need to display the
        // required tooltip which displays the components size/coordinates details
        final Rectangle boxSize = WidgetSelection.getMultipleWidgetBounds(selectedWidgets);
        if (currentlyDragging) {
            displayTooltip(g2, boxSize, false);
        } else if (isResizing) {
            displayTooltip(g2, boxSize, true);
        }

        if (selectedWidgets.size() > 1 || selectedWidget.getType() == IWidget.GROUP) {
            widgetSelection.drawMulitipleSelectionBox(g2, selectedWidgets, true);
        }
    }

    private void drawDragOutBoxAndDisplayTooltip(
            final Graphics2D g2,
            final Rectangle boxSize) {
        final int dragBoxX = dragBoxMouseLocation.x - (boxSize.width / 2);
        final int dragBoxY = dragBoxMouseLocation.y - (boxSize.height / 2);

        final float[] dashPattern = {1, 1};
        g2.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 1, dashPattern, 0));
        g2.setColor(Color.black);

        g2.drawRect(dragBoxX, dragBoxY, boxSize.width, boxSize.height);
        g2.setStroke(new BasicStroke());

        boxSize.setLocation(dragBoxX, dragBoxY);

        displayTooltip(g2, boxSize, false);
    }

    private void displayTooltip(
            final Graphics2D g2,
            final Rectangle boxSize,
            final boolean isResizing) {
        final int units = (int) (Rule.INCH / 2.54);
        final String text = getTooltipText(boxSize, isResizing, units);

        final JToolTip tooltip = new JToolTip();
        tooltip.setTipText(text);
        tooltip.setSize(tooltip.getPreferredSize());

        final int translateX = mouseLocation.x - (tooltip.getSize().width / 2);
        final int translateY = mouseLocation.y + 20;

        g2.translate(translateX, translateY);

        tooltip.paint(g2);

        g2.translate(-translateX, -translateY);
    }

    private String getTooltipText(
            final Rectangle boxSize,
            final boolean isResizing,
            final double units) {
        if (isResizing) {
            final double width = (double) boxSize.width / units;
            final double height = (double) boxSize.height / units;
            return round(width) + " cm x " + round(height) + " cm";
        }

        final double xLocation = (double) (boxSize.x - inset) / units;
        final double yLocation = (double) (boxSize.y - inset) / units;
        return round(xLocation) + " cm , " + round(yLocation) + " cm";
    }

    private double round(final double number) {
        final double exponential = Math.pow(10, 3);

        double value = number;
        value *= exponential;
        value = Math.round(value);
        value /= exponential;

        return value;
    }

    @Override
    public void close() {
        removeWidgetsFromHierarchy(pageWidgets);

        pageWidgets.clear();
        selectedWidgets.clear();

        drawingState = IDesigner.CLOSED;

        pageHeight = 0;
        pageWidth = 0;

        closePdfFile();

        updateUI();
    }

    @Override
    public Dimension getPreferredSize() {
        if (drawingState == IDesigner.PDFPAGE) {
            return getMaximumSize();
        } else {
            return new Dimension(inset * 2 + pageWidth, inset * 2 + pageHeight);
        }
    }

    @Override
    public void displayPage(final Page page) {
        this.currentPage = page;
        this.pageWidgets = page.getWidgets();
        selectedWidgets.clear();

        final String pdfFile = page.getPdfFileLocation();
        if (pdfFile == null) {
            this.drawingState = IDesigner.SIMPLEPAGE;

            closePdfFile();

            this.pageWidth = page.getWidth();
            this.pageHeight = page.getHeight();
        } else {
            this.drawingState = IDesigner.PDFPAGE;

            closePdfFile();
            setDisplayForms(false);

            try {
                final int pdfPageNumber = displayPdfFile(page, pdfFile);

                this.pageWidth = getPdfPageData().getCropBoxWidth(pdfPageNumber);
                this.pageHeight = getPdfPageData().getCropBoxHeight(pdfPageNumber);
            } catch (final Exception e) {
                logger.error("Error displaying page", e);
            }
        }

        updateUI();
    }

    private int displayPdfFile(
            final Page page,
            final String pdfFile) throws Exception {
        if (pdfFile.startsWith("http:") || pdfFile.startsWith("file:")) {
            openPdfFileFromURL(pdfFile);
        } else {
            openPdfFile(pdfFile);
        }

        setPDFBorder(BorderFactory.createLineBorder(Color.black, 1));
        setInset(inset, inset);

        final int pdfPageNumber = page.getPdfPageNumber();
        setPageParameters(1, pdfPageNumber);
        decodePage(pdfPageNumber);

        return pdfPageNumber;
    }

    @Override
    public IWidget getWidgetAt(
            final int x,
            final int y) {
        for (int i = pageWidgets.size() - 1; i > 0; i--) {
            final IWidget widget = pageWidgets.get(i);
            if (widget.getBounds().contains(x, y)) {
                return widget;
            }
        }
        return null;
    }

    @Override
    public void addWidget(final IWidget widget) {
        mainFrame.addWidgetToHierarchy(widget);
        mainFrame.addWidgetToPage(widget);

        final Set<IWidget> widgets = Set.of(widget);
        mainFrame.setPropertiesCompound(widgets);
        mainFrame.setPropertiesToolBar(widgets);
    }

    @Override
    public void addWidget(
            final int index,
            final IWidget widget) {
        pageWidgets.add(index, widget);
    }

    @Override
    public void removeSelectedWidgets() {
        pageWidgets.removeAll(selectedWidgets);

        removeWidgetsFromHierarchy(selectedWidgets);
    }

    private void removeWidgetsFromHierarchy(final Collection<IWidget> widgets) {
        widgets.forEach(mainFrame::removeWidgetFromHierarchy);
    }

    @Override
    public void removeWidget(
            final IWidget widgetToRemove,
            final List<IWidget> widgets) {
        for (final Iterator<IWidget> iterator = widgets.iterator(); iterator.hasNext();) {
            final IWidget widget = iterator.next();
            if (removeWidgetFromIteration(widgetToRemove, iterator, widget)) {
                break;
            }
        }

        mainFrame.updateHierarchy();
        repaint();
    }

    private boolean removeWidgetFromIteration(
            final IWidget widgetToRemove,
            final Iterator<IWidget> iterator,
            final IWidget widget) {
        if (widget.getType() == IWidget.GROUP) {
            return removeFromGroup(widgetToRemove, iterator, widget);
        }
        return removeWidget(widgetToRemove, iterator, widget);
    }

    private boolean removeFromGroup(
            final IWidget widgetToRemove,
            final Iterator<IWidget> iterator,
            final IWidget widget) {
        removeWidget(widgetToRemove, widget.getWidgetsInGroup());
        return removeWidget(widgetToRemove, iterator, widget);
    }

    private boolean removeWidget(
            final IWidget widgetToRemove,
            final Iterator<IWidget> iterator,
            final IWidget widget) {
        if (widget == widgetToRemove) {
            iterator.remove();
            return true;
        }
        return false;
    }

    @Override
    public Set<IWidget> getSelectedWidgets() {
        return selectedWidgets;
    }

    @Override
    public void setWidgetToAdd(final int widgetToAdd) {
        this.widgetToAdd = widgetToAdd;
    }

    @Override
    public int getWidgetToAdd() {
        return widgetToAdd;
    }

    @Override
    public void setSelectedWidgets(final Set<IWidget> selectedWidgets) {
        this.selectedWidgets.clear();
        this.selectedWidgets.addAll(selectedWidgets);

        if (this.selectedWidgets.isEmpty()) {
            widgetSelection.hideGroupingButtons();
        }
    }

    @Override
    public void setResizeType(final int resizeType) {
        this.resizeType = resizeType;
    }

    @Override
    public int getResizeType() {
        return resizeType;
    }

    @Override
    public void setIsResizing(final boolean isResizing) {
        this.isResizing = isResizing;
    }

    @Override
    public boolean isResizing() {
        return isResizing;
    }

    @Override
    public DesignerSelectionBox getSelectionBox() {
        return selectionBox;
    }

    @Override
    public void resetPaletteButtons() {
        mainFrame.resetPaletteButtons();
    }

    @Override
    public void setProperties(final Set<IWidget> widget) {
        mainFrame.setPropertiesCompound(widget);
        mainFrame.setPropertiesToolBar(widget);
    }

    @Override
    public void updateRulers(final Point point) {
        mouseLocation.setLocation(point);

        horizontalRuler.updateMarker(point.x);
        verticalRuler.updateMarker(point.y);
    }

    @Override
    public void setDragBoxLocation(final Point dragBoxLocation) {
        dragBoxMouseLocation = dragBoxLocation;
        repaint();
    }

    @Override
    public void setCurrentlyDragging(final boolean currentlyDragging) {
        this.currentlyDragging = currentlyDragging;
    }

    @Override
    public List<IWidget> getWidgets() {
        return pageWidgets;
    }

    @Override
    public void setIsResizingSplitComponent(final boolean isResizingSplitComponent) {
        this.isResizingSplitComponent = isResizingSplitComponent;
    }

    @Override
    public boolean isResizingSplitComponent() {
        return isResizingSplitComponent;
    }

    @Override
    public WidgetSelection getWidgetSelection() {
        return widgetSelection;
    }

    @Override
    public CaptionChanger getCaptionChanger() {
        return captionChanger;
    }

    @Override
    public IMainFrame getMainFrame() {
        return mainFrame;
    }

}