/*
* Copyright 2017 Laszlo Balazs-Csiki
*
* This file is part of Pixelitor. Pixelitor is free software: you
* can redistribute it and/or modify it under the terms of the GNU
* General Public License, version 3 as published by the Free
* Software Foundation.
*
* Pixelitor 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 Pixelitor. If not, see <http://www.gnu.org/licenses/>.
*/
package pixelitor.filters.painters;
import org.jdesktop.swingx.painter.AbstractLayoutPainter.HorizontalAlignment;
import org.jdesktop.swingx.painter.AbstractLayoutPainter.VerticalAlignment;
import pixelitor.Composition;
import pixelitor.filters.gui.ColorParam;
import pixelitor.filters.gui.ColorParamGUI;
import pixelitor.filters.gui.FilterGUIPanel;
import pixelitor.filters.gui.ParamAdjustmentListener;
import pixelitor.filters.gui.RangeParam;
import pixelitor.gui.utils.GridBagHelper;
import pixelitor.gui.utils.SliderSpinner;
import pixelitor.layers.Drawable;
import pixelitor.layers.TextLayer;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import java.awt.Color;
import java.awt.Dialog;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GraphicsEnvironment;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.font.TextAttribute;
import java.util.HashMap;
import java.util.Map;
import static java.awt.Color.BLACK;
import static pixelitor.filters.gui.ColorParam.OpacitySetting.USER_ONLY_OPACITY;
import static pixelitor.gui.utils.SliderSpinner.TextPosition.NONE;
/**
* Customization panel for the text filters and text layers
*/
public class TextAdjustmentsPanel extends FilterGUIPanel implements ParamAdjustmentListener, ActionListener {
private TextLayer textLayer;
private JTextField textTF;
private JComboBox<String> fontFamilyChooserCB;
private SliderSpinner fontSizeSlider;
private JCheckBox boldCB;
private JCheckBox italicCB;
private ColorParam color;
private EffectsPanel effectsPanel;
private JComboBox<VerticalAlignment> verticalAlignmentCombo;
private JComboBox<HorizontalAlignment> horizontalAlignmentCombo;
private JCheckBox watermarkCB;
private static String lastText = "";
private Map<TextAttribute, Object> map;
private AdvancedTextSettingsDialog advancedSettingsDialog;
// called for image layers
public TextAdjustmentsPanel(TextFilter textFilter, Drawable dr) {
super(textFilter, dr);
createGUI(null);
if(!textTF.getText().isEmpty()) {
// a "last text" was set
paramAdjusted();
}
}
// called for text layers
public TextAdjustmentsPanel(TextLayer textLayer) {
super(null, null);
this.textLayer = textLayer;
createGUI(textLayer.getSettings());
// make sure that the text layer has a settings object
// even if the user presses OK without making any adjustments
paramAdjusted();
}
private void createGUI(TextSettings settings) {
Box verticalBox = Box.createVerticalBox();
verticalBox.add(createTextPanel(settings));
verticalBox.add(createFontPanel(settings));
AreaEffects areaEffects = null;
boolean hasWatermark = false;
if (settings != null) {
areaEffects = settings.getAreaEffects();
hasWatermark = settings.isWatermark();
}
effectsPanel = new EffectsPanel(this, areaEffects);
effectsPanel.setBorder(BorderFactory.createTitledBorder("Effects"));
verticalBox.add(effectsPanel);
watermarkCB = new JCheckBox("Use Text for Watermarking", hasWatermark);
watermarkCB.addActionListener(this);
JPanel bottomPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
bottomPanel.add(watermarkCB);
verticalBox.add(bottomPanel);
add(verticalBox);
}
private JPanel createTextPanel(TextSettings settings) {
JPanel textPanel = new JPanel();
textPanel.setLayout(new GridBagLayout());
GridBagHelper gbh = new GridBagHelper(textPanel);
gbh.addLabel("Text:", 0, 0);
createTextTF(settings);
gbh.addLastControl(textTF);
gbh.addLabel("Color", 0, 1);
Color defaultColor = settings == null ? BLACK : settings.getColor();
color = new ColorParam("Color", defaultColor, USER_ONLY_OPACITY);
ColorParamGUI colorParamGUI = new ColorParamGUI(color);
gbh.addLastControl(colorParamGUI);
color.setAdjustmentListener(this);
verticalAlignmentCombo = new JComboBox(VerticalAlignment.values());
horizontalAlignmentCombo = new JComboBox(HorizontalAlignment.values());
if (settings != null) {
verticalAlignmentCombo.setSelectedItem(settings.getVerticalAlignment());
horizontalAlignmentCombo.setSelectedItem(settings.getHorizontalAlignment());
}
gbh.addLabel("Vertical Alignment", 0, 2);
verticalAlignmentCombo.addActionListener(this);
gbh.addControl(verticalAlignmentCombo);
gbh.addLabel("Horizontal Alignment", 2, 2);
horizontalAlignmentCombo.addActionListener(this);
gbh.addControl(horizontalAlignmentCombo);
return textPanel;
}
private void createTextTF(TextSettings settings) {
String defaultText;
if (settings == null) {
if (filter == null) { // layer mode
defaultText = ""; // no last text remembering when creating new text layers
} else {
defaultText = lastText;
}
} else {
defaultText = settings.getText();
}
textTF = new JTextField(defaultText, 20);
textTF.setName("textTF");
textTF.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
paramAdjusted();
}
@Override
public void removeUpdate(DocumentEvent e) {
paramAdjusted();
}
@Override
public void changedUpdate(DocumentEvent e) {
paramAdjusted();
}
});
}
private JPanel createFontPanel(TextSettings settings) {
JPanel fontPanel = new JPanel();
fontPanel.setBorder(BorderFactory.createTitledBorder("Font"));
fontPanel.setLayout(new GridBagLayout());
GridBagHelper gbh = new GridBagHelper(fontPanel);
gbh.addLabel("Font Size:", 0, 0);
int defaultFontSize = settings == null ? 100 : settings.getFont().getSize();
RangeParam fontSizeParam = new RangeParam("", 1, defaultFontSize, 1000);
fontSizeSlider = new SliderSpinner(fontSizeParam, NONE, false);
fontSizeSlider.setSliderName("fontSize");
fontSizeParam.setAdjustmentListener(this);
gbh.addLastControl(fontSizeSlider);
gbh.addLabel("Font Type:", 0, 1);
GraphicsEnvironment localGE = GraphicsEnvironment.getLocalGraphicsEnvironment();
String[] availableFonts = localGE.getAvailableFontFamilyNames();
fontFamilyChooserCB = new JComboBox(availableFonts);
if (settings != null) {
// it is important to use Font.getName(), and not Font.getFontName(),
// otherwise it might not be in the combo box
String fontName = settings.getFont().getName();
fontFamilyChooserCB.setSelectedItem(fontName);
}
fontFamilyChooserCB.addActionListener(this);
gbh.addLastControl(fontFamilyChooserCB);
boolean defaultBold = false;
boolean defaultItalic = false;
if (settings != null) {
Font font = settings.getFont();
defaultBold = font.isBold();
defaultItalic = font.isItalic();
if (font.hasLayoutAttributes()) {
Map<TextAttribute, ?> attributes = font.getAttributes();
this.map = (Map<TextAttribute, Object>) attributes;
}
}
gbh.addLabel("Bold:", 0, 2);
boldCB = createAndAddEmphasisCheckBox("boldCB", gbh, defaultBold);
gbh.addLabel(" Italic:", 2, 2);
italicCB = createAndAddEmphasisCheckBox("italicCB", gbh, defaultItalic);
JButton showAdvancedSettingsButton = new JButton("Advanced...");
showAdvancedSettingsButton.addActionListener(e -> {
if (advancedSettingsDialog == null) {
Dialog parentDialog = (Dialog) SwingUtilities.getWindowAncestor(this);
advancedSettingsDialog = new AdvancedTextSettingsDialog(parentDialog, this, map);
}
advancedSettingsDialog.setVisible(true);
});
gbh.addLabel(" ", 4, 2);
gbh.addControl(showAdvancedSettingsButton);
return fontPanel;
}
private JCheckBox createAndAddEmphasisCheckBox(String name, GridBagHelper gbh, boolean selected) {
JCheckBox cb = new JCheckBox("", selected);
cb.setName(name);
cb.addActionListener(this);
gbh.addControl(cb);
return cb;
}
private Font getSelectedFont() {
String fontFamily = (String) fontFamilyChooserCB.getSelectedItem();
int style = Font.PLAIN;
if (boldCB.isSelected()) {
style |= Font.BOLD;
}
if (italicCB.isSelected()) {
style |= Font.ITALIC;
}
int size = fontSizeSlider.getCurrentValue();
Font font = new Font(fontFamily, style, size);
// It is important to create here a new Map, because
// the old one stores old values in TextAttribute.SIZE
// and other fields which would override the current ones.
// TODO there has to be a simpler way, for example overwriting
// however, it is not trivial, there is no single "style" TextAttribute
Map<TextAttribute, Object> oldMap = map;
map = new HashMap<>();
if (advancedSettingsDialog != null) {
advancedSettingsDialog.updateMap(map);
} else if (oldMap != null) {
// no dialog, copy manually the advanced settings
TextAttribute[] advancedSettings = {
TextAttribute.STRIKETHROUGH,
TextAttribute.UNDERLINE,
TextAttribute.KERNING,
TextAttribute.LIGATURES,
TextAttribute.TRACKING
};
for (TextAttribute setting : advancedSettings) {
map.put(setting, oldMap.get(setting));
}
}
return font.deriveFont(map);
}
@Override
public void actionPerformed(ActionEvent e) {
paramAdjusted();
}
@Override
public void paramAdjusted() {
TextFilter textFilter = (TextFilter) filter;
String text = textTF.getText();
lastText = text;
AreaEffects areaEffects = null;
if (effectsPanel != null) {
effectsPanel.updateEffectsFromGUI();
areaEffects = effectsPanel.getEffects();
}
Font selectedFont = getSelectedFont();
TextSettings settings = new TextSettings(
text, selectedFont, color.getColor(), areaEffects,
(HorizontalAlignment) horizontalAlignmentCombo.getSelectedItem(),
(VerticalAlignment) verticalAlignmentCombo.getSelectedItem(),
watermarkCB.isSelected());
if (textFilter != null) { // filter mode
textFilter.setSettings(settings);
super.executeFilterPreview();
} else {
assert textLayer != null;
textLayer.setSettings(settings);
textLayer.getComp().imageChanged(Composition.ImageChangeActions.FULL);
}
}
}