blob: 4c446ec341a9bfbd4e6a0e6892a212213380a69a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.pivot.wtk.skin.terra;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.Path2D;
import org.apache.pivot.collections.ArrayList;
import org.apache.pivot.collections.Dictionary;
import org.apache.pivot.collections.Sequence;
import org.apache.pivot.util.Utils;
import org.apache.pivot.wtk.ApplicationContext;
import org.apache.pivot.wtk.Bounds;
import org.apache.pivot.wtk.Component;
import org.apache.pivot.wtk.ComponentMouseListener;
import org.apache.pivot.wtk.Dimensions;
import org.apache.pivot.wtk.Display;
import org.apache.pivot.wtk.Form;
import org.apache.pivot.wtk.FormAttributeListener;
import org.apache.pivot.wtk.FormListener;
import org.apache.pivot.wtk.GraphicsUtilities;
import org.apache.pivot.wtk.Insets;
import org.apache.pivot.wtk.Label;
import org.apache.pivot.wtk.MessageType;
import org.apache.pivot.wtk.Point;
import org.apache.pivot.wtk.Separator;
import org.apache.pivot.wtk.Style;
import org.apache.pivot.wtk.Theme;
import org.apache.pivot.wtk.Window;
import org.apache.pivot.wtk.WindowStateListener;
import org.apache.pivot.wtk.effects.Decorator;
import org.apache.pivot.wtk.effects.DropShadowDecorator;
import org.apache.pivot.wtk.media.Image;
import org.apache.pivot.wtk.skin.ContainerSkin;
/**
* Terra form skin. <p> TODO Animate preferred size calculations when flags
* change (make this configurable via a style flag)
*/
public class TerraFormSkin extends ContainerSkin implements FormListener, FormAttributeListener {
private class PopupFieldIndicatorDecorator implements Decorator {
private Graphics2D graphics = null;
@Override
public Graphics2D prepare(Component component, Graphics2D graphicsArgument) {
this.graphics = graphicsArgument;
return graphicsArgument;
}
@Override
public void update() {
GeneralPath arrow = new GeneralPath(Path2D.WIND_EVEN_ODD);
arrow.moveTo(POPUP_FIELD_INDICATOR_OFFSET, 0);
arrow.lineTo(POPUP_FIELD_INDICATOR_OFFSET + POPUP_FIELD_INDICATOR_WIDTH / 2,
-POPUP_FIELD_INDICATOR_HEIGHT);
arrow.lineTo(POPUP_FIELD_INDICATOR_OFFSET + POPUP_FIELD_INDICATOR_WIDTH, 0);
arrow.closePath();
GraphicsUtilities.setAntialiasingOn(graphics);
graphics.setStroke(new BasicStroke(0));
graphics.setColor(flagMessageWindow.getStyles().getColor(Style.backgroundColor));
graphics.draw(arrow);
graphics.fill(arrow);
graphics = null;
}
@Override
public Bounds getBounds(Component component) {
return new Bounds(POPUP_FIELD_INDICATOR_OFFSET, -POPUP_FIELD_INDICATOR_HEIGHT,
POPUP_FIELD_INDICATOR_WIDTH, POPUP_FIELD_INDICATOR_HEIGHT);
}
@Override
public AffineTransform getTransform(Component component) {
return new AffineTransform();
}
}
private class InlineFlagMessageDecorator implements Decorator {
private Graphics2D graphics = null;
@Override
public Graphics2D prepare(Component component, Graphics2D graphicsArgument) {
this.graphics = graphicsArgument;
return graphicsArgument;
}
@Override
public void update() {
if (showFlagMessagesInline) {
Form form = (Form) getComponent();
Form.SectionSequence sections = form.getSections();
for (int sectionIndex = 0, sectionCount = sections.getLength(); sectionIndex < sectionCount;
sectionIndex++) {
Form.Section section = sections.get(sectionIndex);
for (int fieldIndex = 0, fieldCount = section.getLength(); fieldIndex < fieldCount; fieldIndex++) {
Component field = section.get(fieldIndex);
if (field.isVisible()) {
Form.Flag flag = Form.getFlag(field);
if (flag != null) {
String message = flag.getMessage();
MessageType messageType = flag.getMessageType();
Color messageColor = null;
Color messageBackgroundColor = null;
switch (messageType) {
case ERROR:
messageColor = errorMessageColor;
messageBackgroundColor = errorMessageBackgroundColor;
break;
case WARNING:
messageColor = warningMessageColor;
messageBackgroundColor = warningMessageBackgroundColor;
break;
case QUESTION:
messageColor = questionMessageColor;
messageBackgroundColor = questionMessageBackgroundColor;
break;
case INFO:
messageColor = infoMessageColor;
messageBackgroundColor = infoMessageBackgroundColor;
break;
default:
break;
}
// Draw the label
flagMessageLabel.setText(message);
flagMessageLabel.setSize(flagMessageLabel.getPreferredSize());
flagMessageLabel.validate();
flagMessageLabel.getStyles().put(Style.color, messageColor);
flagMessageLabel.getStyles().put(Style.backgroundColor,
messageBackgroundColor);
int flagMessageX = field.getX() + field.getWidth()
+ INLINE_FIELD_INDICATOR_WIDTH - 2;
int flagMessageY = field.getY() - field.getBaseline()
+ flagMessageLabel.getBaseline();
graphics.translate(flagMessageX, flagMessageY);
flagMessageLabel.paint(graphics);
// Draw the arrow
GeneralPath arrow = new GeneralPath(Path2D.WIND_EVEN_ODD);
arrow.moveTo(0, 0);
arrow.lineTo(-INLINE_FIELD_INDICATOR_WIDTH,
(float) flagMessageLabel.getHeight() / 2);
arrow.lineTo(0, flagMessageLabel.getHeight());
arrow.closePath();
GraphicsUtilities.setAntialiasingOn(graphics);
graphics.setColor(messageBackgroundColor);
graphics.fill(arrow);
// Restore the graphics context
graphics.translate(-flagMessageX, -flagMessageY);
}
}
}
}
}
graphics = null;
}
@Override
public Bounds getBounds(Component component) {
return new Bounds(0, 0, component.getWidth(), component.getHeight());
}
@Override
public AffineTransform getTransform(Component component) {
return new AffineTransform();
}
}
private ArrayList<Separator> separators = new ArrayList<>();
private ArrayList<ArrayList<Label>> labels = new ArrayList<>();
private Label flagMessageLabel = new Label();
private Window flagMessageWindow = new Window(flagMessageLabel);
private Insets padding;
private int horizontalSpacing;
private int verticalSpacing;
private int flagIconOffset;
private boolean fill;
private boolean showFlagIcons;
private boolean showFlagHighlight;
private boolean showFlagMessagesInline;
private boolean leftAlignLabels;
private String delimiter;
private Image errorIcon = null;
private Color errorMessageColor = null;
private Color errorMessageBackgroundColor = null;
private Color errorHighlightColor = null;
private Image warningIcon = null;
private Color warningMessageColor = null;
private Color warningMessageBackgroundColor = null;
private Color warningHighlightColor = null;
private Image questionIcon = null;
private Color questionMessageColor = null;
private Color questionMessageBackgroundColor = null;
private Color questionHighlightColor = null;
private Image infoIcon = null;
private Color infoMessageColor = null;
private Color infoMessageBackgroundColor = null;
private Color infoHighlightColor = null;
private Color separatorColor = null;
private Color separatorHeadingColor = null;
private int maximumFlagImageWidth = 0;
private ComponentMouseListener fieldMouseListener = new ComponentMouseListener() {
@Override
public void mouseOver(Component component) {
if (!showFlagMessagesInline) {
Form.Flag flag = Form.getFlag(component);
if (flag != null) {
String message = flag.getMessage();
if (message != null) {
flagMessageLabel.setText(message);
MessageType messageType = flag.getMessageType();
Color color = null;
Color backgroundColor = null;
switch (messageType) {
case ERROR:
color = errorMessageColor;
backgroundColor = errorMessageBackgroundColor;
break;
case WARNING:
color = warningMessageColor;
backgroundColor = warningMessageBackgroundColor;
break;
case QUESTION:
color = questionMessageColor;
backgroundColor = questionMessageBackgroundColor;
break;
case INFO:
color = infoMessageColor;
backgroundColor = infoMessageBackgroundColor;
break;
default:
break;
}
flagMessageLabel.getStyles().put(Style.color, color);
flagMessageWindow.getStyles().put(Style.backgroundColor, backgroundColor);
// Open the window
Point location = component.mapPointToAncestor(component.getDisplay(), 0,
component.getHeight());
int y = location.y + POPUP_FIELD_INDICATOR_HEIGHT - 4;
if (showFlagHighlight) {
y += FLAG_HIGHLIGHT_PADDING;
}
flagMessageWindow.setLocation(location.x, y);
flagMessageWindow.open(component.getWindow());
}
}
}
}
@Override
public void mouseOut(Component component) {
flagMessageWindow.close();
}
};
private static final int FLAG_HIGHLIGHT_PADDING = 2;
private static final int POPUP_FIELD_INDICATOR_WIDTH = 13;
private static final int POPUP_FIELD_INDICATOR_HEIGHT = 6;
private static final int POPUP_FIELD_INDICATOR_OFFSET = 10;
private static final int HIDE_POPUP_MESSAGE_DELAY = 3500;
private static final int INLINE_FIELD_INDICATOR_WIDTH = 9;
private static final String DEFAULT_DELIMITER = ":";
public TerraFormSkin() {
padding = new Insets(4);
horizontalSpacing = 6;
verticalSpacing = 6;
flagIconOffset = 4;
fill = false;
showFlagIcons = true;
showFlagHighlight = true;
showFlagMessagesInline = false;
leftAlignLabels = false;
delimiter = DEFAULT_DELIMITER;
// Get theme icons/colors
TerraTheme theme = (TerraTheme) Theme.getTheme();
errorIcon = theme.getSmallMessageIcon(MessageType.ERROR);
errorMessageColor = theme.getColor(4);
errorMessageBackgroundColor = theme.getColor(22);
errorHighlightColor = theme.getColor(21);
warningIcon = theme.getSmallMessageIcon(MessageType.WARNING);
warningMessageColor = theme.getColor(1);
warningMessageBackgroundColor = theme.getColor(19);
warningHighlightColor = theme.getColor(18);
questionIcon = theme.getSmallMessageIcon(MessageType.QUESTION);
questionMessageColor = theme.getColor(4);
questionMessageBackgroundColor = theme.getColor(14);
questionHighlightColor = theme.getColor(13);
infoIcon = theme.getSmallMessageIcon(MessageType.INFO);
infoMessageColor = theme.getColor(1);
infoMessageBackgroundColor = theme.getColor(10);
infoHighlightColor = theme.getColor(9);
separatorColor = theme.getColor(7);
separatorHeadingColor = theme.getColor(12);
// Determine maximum icon size
maximumFlagImageWidth = Math.max(maximumFlagImageWidth, errorIcon.getWidth());
maximumFlagImageWidth = Math.max(maximumFlagImageWidth, warningIcon.getWidth());
maximumFlagImageWidth = Math.max(maximumFlagImageWidth, questionIcon.getWidth());
maximumFlagImageWidth = Math.max(maximumFlagImageWidth, infoIcon.getWidth());
// Create the flag message popup
flagMessageLabel.getStyles().put(Style.padding, new Insets(3, 4, 3, 4));
if (!themeIsFlat()) {
flagMessageWindow.getDecorators().add(new DropShadowDecorator());
}
flagMessageWindow.getDecorators().add(new PopupFieldIndicatorDecorator());
flagMessageWindow.getWindowStateListeners().add(new WindowStateListener() {
private ApplicationContext.ScheduledCallback scheduledHideFlagMessageCallback = null;
@Override
public void windowOpened(Window window) {
// Set a timer to hide the message
Runnable hideFlagMessageCallback = new Runnable() {
@Override
public void run() {
flagMessageWindow.close();
}
};
scheduledHideFlagMessageCallback = ApplicationContext.scheduleCallback(
hideFlagMessageCallback, HIDE_POPUP_MESSAGE_DELAY);
}
@Override
public void windowClosed(Window window, Display display, Window owner) {
scheduledHideFlagMessageCallback.cancel();
}
});
}
@Override
public void install(Component component) {
super.install(component);
Form form = (Form) component;
form.getFormListeners().add(this);
form.getFormAttributeListeners().add(this);
Form.SectionSequence sections = form.getSections();
for (int i = 0, n = sections.getLength(); i < n; i++) {
insertSection(sections.get(i), i);
}
form.getDecorators().add(new InlineFlagMessageDecorator());
}
@Override
public int getPreferredWidth(int height) {
int preferredWidth = 0;
int maximumLabelWidth = 0;
int maximumFieldWidth = 0;
int maximumSeparatorWidth = 0;
Form form = (Form) getComponent();
Form.SectionSequence sections = form.getSections();
for (int sectionIndex = 0, sectionCount = sections.getLength(); sectionIndex < sectionCount; sectionIndex++) {
Form.Section section = sections.get(sectionIndex);
if (sectionIndex > 0 || section.getHeading() != null) {
Separator separator = separators.get(sectionIndex);
maximumSeparatorWidth = Math.max(maximumSeparatorWidth,
separator.getPreferredWidth());
}
for (int fieldIndex = 0, fieldCount = section.getLength(); fieldIndex < fieldCount; fieldIndex++) {
Component field = section.get(fieldIndex);
if (field.isVisible()) {
Label label = labels.get(sectionIndex).get(fieldIndex);
maximumLabelWidth = Math.max(maximumLabelWidth, label.getPreferredWidth());
int fieldWidth = field.getPreferredWidth();
if (showFlagMessagesInline) {
// Calculate maximum flag message width
Form.Flag flag = Form.getFlag(field);
if (flag != null) {
String message = flag.getMessage();
if (message != null) {
flagMessageLabel.setText(message);
fieldWidth += (INLINE_FIELD_INDICATOR_WIDTH - 2)
+ flagMessageLabel.getPreferredWidth();
}
}
}
maximumFieldWidth = Math.max(maximumFieldWidth, fieldWidth);
}
}
}
preferredWidth = maximumLabelWidth + horizontalSpacing + maximumFieldWidth;
if (showFlagIcons) {
preferredWidth += maximumFlagImageWidth + flagIconOffset;
}
preferredWidth = Math.max(preferredWidth + padding.left + padding.right,
maximumSeparatorWidth);
return preferredWidth;
}
@Override
public int getPreferredHeight(int width) {
int preferredHeight = 0;
Form form = (Form) getComponent();
Form.SectionSequence sections = form.getSections();
// Determine the field width constraint
int fieldWidth = (fill && width != -1) ? getFieldWidth(width) : -1;
for (int sectionIndex = 0, sectionCount = sections.getLength(); sectionIndex < sectionCount; sectionIndex++) {
Form.Section section = sections.get(sectionIndex);
if (sectionIndex > 0 || section.getHeading() != null) {
Separator separator = separators.get(sectionIndex);
preferredHeight += separator.getPreferredHeight(width);
preferredHeight += verticalSpacing;
}
for (int fieldIndex = 0, fieldCount = section.getLength(); fieldIndex < fieldCount; fieldIndex++) {
Component field = section.get(fieldIndex);
if (field.isVisible()) {
// Determine the label size and baseline
Label label = labels.get(sectionIndex).get(fieldIndex);
Dimensions labelSize = label.getPreferredSize();
int labelAscent = label.getBaseline(labelSize.width, labelSize.height);
int labelDescent = labelSize.height - labelAscent;
// Determine the field size and baseline
Dimensions fieldSize;
if (fill && fieldWidth != -1) {
fieldSize = new Dimensions(fieldWidth, field.getPreferredHeight(fieldWidth));
} else {
fieldSize = field.getPreferredSize();
}
int fieldAscent = field.getBaseline(fieldSize.width, fieldSize.height);
if (fieldAscent == -1) {
fieldAscent = fieldSize.height;
}
int fieldDescent = fieldSize.height - fieldAscent;
// Determine the row height
int maximumAscent = Math.max(labelAscent, fieldAscent);
int maximumDescent = Math.max(labelDescent, fieldDescent);
int rowHeight = maximumAscent + maximumDescent;
preferredHeight += rowHeight;
if (fieldIndex > 0) {
preferredHeight += verticalSpacing;
}
}
}
}
preferredHeight += (padding.top + padding.bottom);
return preferredHeight;
}
@Override
public Dimensions getPreferredSize() {
// TODO Optimize
return new Dimensions(getPreferredWidth(-1), getPreferredHeight(-1));
}
@Override
public int getBaseline(int width, int height) {
int baseline = -1;
Form form = (Form) getComponent();
Form.SectionSequence sections = form.getSections();
// Determine the field width constraint
int fieldWidth = (fill) ? getFieldWidth(width) : -1;
int sectionCount = sections.getLength();
int sectionIndex = 0;
int rowY = 0;
while (sectionIndex < sectionCount && baseline == -1) {
Form.Section section = sections.get(sectionIndex);
if (sectionIndex > 0 || section.getHeading() != null) {
Separator separator = separators.get(sectionIndex);
rowY += separator.getPreferredHeight(width);
rowY += verticalSpacing;
}
int fieldCount = section.getLength();
int fieldIndex = 0;
while (fieldIndex < fieldCount && baseline == -1) {
Component field = section.get(fieldIndex);
if (field.isVisible()) {
// Determine the label size and baseline
Label label = labels.get(sectionIndex).get(fieldIndex);
Dimensions labelSize = label.getPreferredSize();
int labelAscent = label.getBaseline(labelSize.width, labelSize.height);
// Determine the field size and baseline
Dimensions fieldSize;
if (fill && fieldWidth != -1) {
fieldSize = new Dimensions(fieldWidth, field.getPreferredHeight(fieldWidth));
} else {
fieldSize = field.getPreferredSize();
}
int fieldAscent = field.getBaseline(fieldSize.width, fieldSize.height);
if (fieldAscent == -1) {
fieldAscent = labelAscent;
}
// Determine the baseline
int maximumAscent = Math.max(labelAscent, fieldAscent);
baseline = rowY + maximumAscent;
}
fieldIndex++;
}
sectionIndex++;
}
baseline += padding.top;
return baseline;
}
private int getFieldWidth(int width) {
int maximumLabelWidth = 0;
int maximumFlagMessageWidth = 0;
Form form = (Form) getComponent();
Form.SectionSequence sections = form.getSections();
for (int sectionIndex = 0, sectionCount = sections.getLength(); sectionIndex < sectionCount; sectionIndex++) {
Form.Section section = sections.get(sectionIndex);
for (int fieldIndex = 0, fieldCount = section.getLength(); fieldIndex < fieldCount; fieldIndex++) {
Component field = section.get(fieldIndex);
if (field.isVisible()) {
Label label = labels.get(sectionIndex).get(fieldIndex);
maximumLabelWidth = Math.max(maximumLabelWidth, label.getPreferredWidth());
if (showFlagMessagesInline) {
// Calculate maximum flag message width
Form.Flag flag = Form.getFlag(field);
if (flag != null) {
String message = flag.getMessage();
if (message != null) {
flagMessageLabel.setText(message);
maximumFlagMessageWidth = Math.max(maximumFlagMessageWidth,
flagMessageLabel.getPreferredWidth());
}
}
}
}
}
}
int fieldWidth = Math.max(0, width - (maximumLabelWidth + horizontalSpacing));
if (showFlagIcons) {
fieldWidth = Math.max(0, fieldWidth - (maximumFlagImageWidth + flagIconOffset));
}
if (showFlagMessagesInline) {
fieldWidth = Math.max(0, fieldWidth
- (maximumFlagMessageWidth + (INLINE_FIELD_INDICATOR_WIDTH - 2)));
}
fieldWidth = Math.max(0, fieldWidth - (padding.left + padding.right));
return fieldWidth;
}
@Override
public void layout() {
Form form = (Form) getComponent();
Form.SectionSequence sections = form.getSections();
// Determine the maximum label and flag message width
int maximumLabelWidth = 0;
int maximumFlagMessageWidth = 0;
for (int sectionIndex = 0, sectionCount = sections.getLength(); sectionIndex < sectionCount; sectionIndex++) {
Form.Section section = sections.get(sectionIndex);
for (int fieldIndex = 0, fieldCount = section.getLength(); fieldIndex < fieldCount; fieldIndex++) {
Component field = section.get(fieldIndex);
if (field.isVisible()) {
Label label = labels.get(sectionIndex).get(fieldIndex);
maximumLabelWidth = Math.max(maximumLabelWidth, label.getPreferredWidth());
if (showFlagMessagesInline) {
// Calculate maximum flag message width
Form.Flag flag = Form.getFlag(field);
if (flag != null) {
String message = flag.getMessage();
if (message != null) {
flagMessageLabel.setText(message);
maximumFlagMessageWidth = Math.max(maximumFlagMessageWidth,
flagMessageLabel.getPreferredWidth());
}
}
}
}
}
}
// Determine the field width
int width = getWidth();
int fieldWidth = Math.max(0, width - (maximumLabelWidth + horizontalSpacing));
if (showFlagIcons) {
fieldWidth = Math.max(0, fieldWidth - (maximumFlagImageWidth + flagIconOffset));
}
if (showFlagMessagesInline) {
fieldWidth = Math.max(0, fieldWidth
- (maximumFlagMessageWidth + (INLINE_FIELD_INDICATOR_WIDTH - 2)));
}
fieldWidth = Math.max(0, fieldWidth - (padding.left + padding.right));
// Lay out the components
int rowY = padding.top;
for (int sectionIndex = 0, sectionCount = sections.getLength(); sectionIndex < sectionCount; sectionIndex++) {
Form.Section section = sections.get(sectionIndex);
Separator separator = separators.get(sectionIndex);
if (sectionIndex > 0 || section.getHeading() != null) {
int separatorWidth = Math.max(width - (padding.left + padding.right), 0);
separator.setVisible(true);
separator.setSize(separatorWidth, separator.getPreferredHeight(separatorWidth));
separator.setLocation(padding.left, rowY);
rowY += separator.getHeight();
} else {
separator.setVisible(false);
}
for (int fieldIndex = 0, fieldCount = section.getLength(); fieldIndex < fieldCount; fieldIndex++) {
Label label = labels.get(sectionIndex).get(fieldIndex);
Component field = section.get(fieldIndex);
if (field.isVisible()) {
// Show the label
label.setVisible(true);
// Determine the label size and baseline
Dimensions labelSize = label.getPreferredSize();
label.setSize(labelSize);
int labelAscent = label.getBaseline(labelSize.width, labelSize.height);
int labelDescent = labelSize.height - labelAscent;
// Determine the field size and baseline
Dimensions fieldSize;
if (fill) {
fieldSize = new Dimensions(fieldWidth, field.getPreferredHeight(fieldWidth));
} else {
fieldSize = field.getPreferredSize();
}
field.setSize(fieldSize);
int fieldAscent = field.getBaseline(fieldSize.width, fieldSize.height);
if (fieldAscent == -1) {
fieldAscent = labelAscent;
}
int fieldDescent = fieldSize.height - fieldAscent;
// Determine the baseline and row height
int maximumAscent = Math.max(labelAscent, fieldAscent);
int maximumDescent = Math.max(labelDescent, fieldDescent);
int baseline = maximumAscent;
int rowHeight = maximumAscent + maximumDescent;
// Position the label
int labelX = padding.left;
if (!leftAlignLabels) {
labelX += maximumLabelWidth - label.getWidth();
}
if (showFlagIcons) {
labelX += (maximumFlagImageWidth + flagIconOffset);
}
int labelY = rowY + (baseline - labelAscent);
label.setLocation(labelX, labelY);
// Position the field
int fieldX = padding.left + maximumLabelWidth + horizontalSpacing;
if (showFlagIcons) {
fieldX += (maximumFlagImageWidth + flagIconOffset);
}
int fieldY = rowY + (baseline - fieldAscent);
field.setLocation(fieldX, fieldY);
// Update the row y-coordinate
rowY += rowHeight + verticalSpacing;
} else {
// Hide the label
label.setVisible(false);
}
}
}
}
@Override
public void paint(Graphics2D graphics) {
super.paint(graphics);
GraphicsUtilities.setAntialiasingOn(graphics);
Form form = (Form) getComponent();
Form.SectionSequence sections = form.getSections();
for (int sectionIndex = 0, sectionCount = sections.getLength(); sectionIndex < sectionCount; sectionIndex++) {
Form.Section section = sections.get(sectionIndex);
for (int fieldIndex = 0, fieldCount = section.getLength(); fieldIndex < fieldCount; fieldIndex++) {
Component field = section.get(fieldIndex);
if (field.isVisible()) {
Form.Flag flag = Form.getFlag(field);
if (flag != null) {
if (showFlagIcons) {
MessageType messageType = flag.getMessageType();
Image flagIcon = null;
switch (messageType) {
case ERROR:
flagIcon = errorIcon;
break;
case WARNING:
flagIcon = warningIcon;
break;
case QUESTION:
flagIcon = questionIcon;
break;
case INFO:
flagIcon = infoIcon;
break;
default:
flagIcon = infoIcon;
break;
}
Label label = labels.get(sectionIndex).get(fieldIndex);
int flagIconX = label.getX() - (flagIcon.getWidth() + flagIconOffset);
int flagIconY = label.getY()
+ (label.getHeight() - flagIcon.getHeight()) / 2;
graphics.translate(flagIconX, flagIconY);
flagIcon.paint(graphics);
graphics.translate(-flagIconX, -flagIconY);
}
if (showFlagHighlight) {
MessageType messageType = flag.getMessageType();
Color highlightColor = null;
switch (messageType) {
case ERROR:
highlightColor = errorHighlightColor;
break;
case WARNING:
highlightColor = warningHighlightColor;
break;
case QUESTION:
highlightColor = questionHighlightColor;
break;
case INFO:
highlightColor = infoHighlightColor;
break;
default:
break;
}
Bounds fieldBounds = field.getBounds();
graphics.setColor(highlightColor);
graphics.setStroke(new BasicStroke(1));
graphics.drawRect(fieldBounds.x - FLAG_HIGHLIGHT_PADDING, fieldBounds.y
- FLAG_HIGHLIGHT_PADDING, fieldBounds.width
+ FLAG_HIGHLIGHT_PADDING * 2 - 1, fieldBounds.height
+ FLAG_HIGHLIGHT_PADDING * 2 - 1);
}
}
}
}
}
}
public Insets getPadding() {
return padding;
}
public void setPadding(Insets padding) {
Utils.checkNull(padding, "padding");
this.padding = padding;
invalidateComponent();
}
public final void setPadding(Dictionary<String, ?> padding) {
setPadding(new Insets(padding));
}
public final void setPadding(Sequence<?> padding) {
setPadding(new Insets(padding));
}
public final void setPadding(int padding) {
setPadding(new Insets(padding));
}
public final void setPadding(Number padding) {
setPadding(new Insets(padding));
}
public final void setPadding(String padding) {
setPadding(Insets.decode(padding));
}
public int getHorizontalSpacing() {
return horizontalSpacing;
}
public void setHorizontalSpacing(int horizontalSpacing) {
Utils.checkNonNegative(horizontalSpacing, "horizontalSpacing");
this.horizontalSpacing = horizontalSpacing;
invalidateComponent();
}
public final void setHorizontalSpacing(Number horizontalSpacing) {
Utils.checkNull(horizontalSpacing, "horizontalSpacing");
setHorizontalSpacing(horizontalSpacing.intValue());
}
public int getVerticalSpacing() {
return verticalSpacing;
}
public void setVerticalSpacing(int verticalSpacing) {
Utils.checkNonNegative(verticalSpacing, "verticalSpacing");
this.verticalSpacing = verticalSpacing;
invalidateComponent();
}
public final void setVerticalSpacing(Number verticalSpacing) {
Utils.checkNull(verticalSpacing, "verticalSpacing");
setVerticalSpacing(verticalSpacing.intValue());
}
public int getFlagIconOffset() {
return flagIconOffset;
}
public void setFlagIconOffset(int flagIconOffset) {
Utils.checkNonNegative(flagIconOffset, "flagIconOffset");
this.flagIconOffset = flagIconOffset;
invalidateComponent();
}
public final void setFlagIconOffset(Number flagIconOffset) {
Utils.checkNull(flagIconOffset, "flagIconOffset");
setFlagIconOffset(flagIconOffset.intValue());
}
public boolean getFill() {
return fill;
}
public void setFill(boolean fill) {
this.fill = fill;
invalidateComponent();
}
public boolean getShowFlagIcons() {
return showFlagIcons;
}
public void setShowFlagIcons(boolean showFlagIcons) {
this.showFlagIcons = showFlagIcons;
invalidateComponent();
}
public boolean getShowFlagHighlight() {
return showFlagHighlight;
}
public void setShowFlagHighlight(boolean showFlagHighlight) {
this.showFlagHighlight = showFlagHighlight;
invalidateComponent();
}
public boolean getShowFlagMessagesInline() {
return showFlagMessagesInline;
}
public void setShowFlagMessagesInline(boolean showFlagMessagesInline) {
this.showFlagMessagesInline = showFlagMessagesInline;
invalidateComponent();
}
public boolean getLeftAlignLabels() {
return leftAlignLabels;
}
public void setLeftAlignLabels(boolean leftAlignLabels) {
this.leftAlignLabels = leftAlignLabels;
invalidateComponent();
}
public String getDelimiter() {
return delimiter;
}
public void setDelimiter(String delimiter) {
Utils.checkNull(delimiter, "delimiter");
this.delimiter = delimiter;
Form form = (Form) getComponent();
Form.SectionSequence sections = form.getSections();
for (int i = 0, n = sections.getLength(); i < n; i++) {
Form.Section section = sections.get(i);
for (int j = 0, m = section.getLength(); j < m; j++) {
updateFieldLabel(section, j);
}
}
invalidateComponent();
}
public Color getSeparatorColor() {
return separatorColor;
}
public void setSeparatorColor(Color separatorColor) {
this.separatorColor = separatorColor;
for (Separator separator : separators) {
separator.getStyles().put(Style.color, separatorColor);
}
}
public final void setSeparatorColor(String separatorColor) {
setSeparatorColor(GraphicsUtilities.decodeColor(separatorColor, "separatorColor"));
}
public Color getSeparatorHeadingColor() {
return separatorHeadingColor;
}
public void setSeparatorHeadingColor(Color separatorHeadingColor) {
this.separatorHeadingColor = separatorHeadingColor;
for (Separator separator : separators) {
separator.getStyles().put(Style.headingColor, separatorHeadingColor);
}
}
public final void setSeparatorHeadingColor(String separatorHeadingColor) {
setSeparatorHeadingColor(GraphicsUtilities.decodeColor(separatorHeadingColor, "separatorHeadingColor"));
}
// Form events
@Override
public void sectionInserted(Form form, int index) {
insertSection(form.getSections().get(index), index);
}
@Override
public void sectionsRemoved(Form form, int index, Sequence<Form.Section> removed) {
removeSections(index, removed);
}
@Override
public void sectionHeadingChanged(Form.Section section) {
updateSectionHeading(section);
}
@Override
public void fieldInserted(Form.Section section, int index) {
insertField(section, section.get(index), index);
}
@Override
public void fieldsRemoved(Form.Section section, int index, Sequence<Component> fields) {
Form form = (Form) getComponent();
removeFields(form.getSections().indexOf(section), index, fields);
}
// Form attribute events
@Override
public void labelChanged(Form form, Component field, String previousLabel) {
Form.Section section = Form.getSection(field);
updateFieldLabel(section, section.indexOf(field));
}
@Override
public void requiredChanged(Form form, Component field) {
// No-op
}
@Override
public void flagChanged(Form form, Component field, Form.Flag previousFlag) {
if (showFlagMessagesInline) {
invalidateComponent();
} else {
repaintComponent();
}
}
private void insertSection(Form.Section section, int index) {
Form form = (Form) getComponent();
// Insert separator
Separator separator = new Separator(section.getHeading());
separator.getStyles().put(Style.color, separatorColor);
separator.getStyles().put(Style.headingColor, separatorHeadingColor);
separators.insert(separator, index);
form.add(separator);
// Insert label list
ArrayList<Label> sectionLabels = new ArrayList<>();
labels.insert(sectionLabels, index);
// Insert fields
for (int i = 0, n = section.getLength(); i < n; i++) {
insertField(section, section.get(i), i);
}
invalidateComponent();
}
private void removeSections(int index, Sequence<Form.Section> removed) {
Form form = (Form) getComponent();
int count = removed.getLength();
// Remove fields
for (int i = 0; i < count; i++) {
removeFields(index + i, 0, removed.get(i));
}
// Remove labels
labels.remove(index, count);
// Remove separators
Sequence<Separator> removedSeparators = separators.remove(index, count);
for (int i = 0; i < count; i++) {
form.remove(removedSeparators.get(i));
}
invalidateComponent();
}
private void insertField(Form.Section section, Component field, int index) {
Form form = (Form) getComponent();
int sectionIndex = form.getSections().indexOf(section);
// Create the label
Label label = new Label();
labels.get(sectionIndex).insert(label, index);
form.add(label);
// Add mouse listener
field.getComponentMouseListeners().add(fieldMouseListener);
// Update the field label
updateFieldLabel(section, index);
invalidateComponent();
}
private void removeFields(int sectionIndex, int index, Sequence<Component> removed) {
Form form = (Form) getComponent();
int count = removed.getLength();
// Remove the labels
Sequence<Label> removedLabels = labels.get(sectionIndex).remove(index, count);
for (int i = 0; i < count; i++) {
form.remove(removedLabels.get(i));
// Remove mouse listener
Component field = removed.get(i);
field.getComponentMouseListeners().remove(fieldMouseListener);
}
invalidateComponent();
}
private void updateSectionHeading(Form.Section section) {
Form form = (Form) getComponent();
int sectionIndex = form.getSections().indexOf(section);
Separator separator = separators.get(sectionIndex);
separator.setHeading(section.getHeading());
}
private void updateFieldLabel(Form.Section section, int fieldIndex) {
Form form = (Form) getComponent();
Component field = section.get(fieldIndex);
int sectionIndex = form.getSections().indexOf(section);
Label label = labels.get(sectionIndex).get(fieldIndex);
String labelText = Form.getLabel(field);
label.setText((labelText == null) ? "" : labelText + delimiter);
}
}