/*
* Copyright 2015 Igor Maznitsa.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not usne 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 com.igormaznitsa.mindmap.plugins.exporters;
import com.igormaznitsa.mindmap.plugins.api.AbstractExporter;
import com.igormaznitsa.mindmap.model.logger.Logger;
import com.igormaznitsa.mindmap.model.logger.LoggerFactory;
import com.igormaznitsa.mindmap.swing.panel.MindMapPanel;
import com.igormaznitsa.mindmap.swing.panel.MindMapPanelConfig;
import com.igormaznitsa.mindmap.swing.services.UIComponentFactory;
import com.igormaznitsa.mindmap.swing.services.UIComponentFactoryProvider;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.RenderedImage;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.Icon;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JPanel;
import org.apache.commons.io.IOUtils;
import com.igormaznitsa.meta.annotation.MustNotContainNull;
import com.igormaznitsa.meta.common.utils.Assertions;
import com.igormaznitsa.mindmap.model.Topic;
import com.igormaznitsa.mindmap.plugins.api.HasOptions;
import com.igormaznitsa.mindmap.swing.panel.Texts;
import com.igormaznitsa.mindmap.swing.panel.utils.MindMapUtils;
import com.igormaznitsa.mindmap.swing.services.IconID;
import com.igormaznitsa.mindmap.swing.services.ImageIconServiceProvider;
public final class PNGImageExporter extends AbstractExporter {
private static final Logger LOGGER = LoggerFactory.getLogger(PNGImageExporter.class);
private static final UIComponentFactory UI_FACTORY = UIComponentFactoryProvider.findInstance();
private boolean flagExpandAllNodes = false;
private boolean flagDrawBackground = true;
private static final Icon ICO = ImageIconServiceProvider.findInstance().getIconForId(IconID.POPUP_EXPORT_PNG);
public PNGImageExporter() {
super();
}
private static class Options implements HasOptions {
private static final String KEY_EXPAND_ALL = "expand.all";
private static final String KEY_DRAW_BACK = "draw.back";
private boolean expandAll;
private boolean drawBack;
private Options(final boolean expandAllNodes, final boolean drawBackground) {
this.expandAll = expandAllNodes;
this.drawBack = drawBackground;
}
@Override
public boolean doesSupportKey(@Nonnull final String key) {
return KEY_DRAW_BACK.equals(key) || KEY_EXPAND_ALL.equals(key);
}
@Override
@Nonnull
@MustNotContainNull
public String[] getOptionKeys() {
return new String[]{KEY_EXPAND_ALL, KEY_DRAW_BACK};
}
@Override
@Nonnull
public String getOptionKeyDescription(@Nonnull final String key) {
if (KEY_DRAW_BACK.equals(key)) {
return "Draw background";
}
if (KEY_EXPAND_ALL.equals(key)) {
return "Unfold all topics";
}
return "";
}
@Override
public void setOption(@Nonnull final String key, @Nullable final String value) {
if (KEY_DRAW_BACK.equals(key)) {
this.drawBack = Boolean.parseBoolean(value);
} else if (KEY_EXPAND_ALL.equals(key)) {
this.expandAll = Boolean.parseBoolean(value);
}
}
@Override
@Nullable
public String getOption(@Nonnull final String key) {
if (KEY_DRAW_BACK.equals(key)) {
return Boolean.toString(this.drawBack);
}
if (KEY_EXPAND_ALL.equals(key)) {
return Boolean.toString(this.expandAll);
}
return null;
}
}
@Override
@Nullable
public JComponent makeOptions() {
final Options options = new Options(flagExpandAllNodes, flagDrawBackground);
final JPanel panel = UI_FACTORY.makePanelWithOptions(options);
final JCheckBox checkBoxExpandAll = UI_FACTORY.makeCheckBox();
checkBoxExpandAll.setSelected(flagExpandAllNodes);
checkBoxExpandAll.setText(Texts.getString("PNGImageExporter.optionUnfoldAll"));
checkBoxExpandAll.setActionCommand("unfold");
final JCheckBox checkBoxDrawBackground = UI_FACTORY.makeCheckBox();
checkBoxDrawBackground.setSelected(flagDrawBackground);
checkBoxDrawBackground.setText(Texts.getString("PNGImageExporter.optionDrawBackground"));
checkBoxDrawBackground.setActionCommand("back");
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.add(checkBoxExpandAll);
panel.add(checkBoxDrawBackground);
panel.setBorder(BorderFactory.createEmptyBorder(16, 32, 16, 32));
final ActionListener actionListener = new ActionListener() {
@Override
public void actionPerformed(@Nonnull final ActionEvent e) {
if (e.getSource() == checkBoxExpandAll) {
options.setOption(Options.KEY_EXPAND_ALL, Boolean.toString(checkBoxExpandAll.isSelected()));
}
if (e.getSource() == checkBoxDrawBackground) {
options.setOption(Options.KEY_DRAW_BACK, Boolean.toString(checkBoxDrawBackground.isSelected()));
}
}
};
checkBoxExpandAll.addActionListener(actionListener);
checkBoxDrawBackground.addActionListener(actionListener);
return panel;
}
@Override
public void doExport(@Nonnull final MindMapPanel panel, @Nullable final JComponent options, @Nullable final OutputStream out) throws IOException {
if (options instanceof HasOptions) {
final HasOptions opts = (HasOptions) options;
this.flagExpandAllNodes = Boolean.parseBoolean(opts.getOption(Options.KEY_EXPAND_ALL));
this.flagDrawBackground = Boolean.parseBoolean(opts.getOption(Options.KEY_DRAW_BACK));
} else {
for (final Component compo : Assertions.assertNotNull(options).getComponents()) {
if (compo instanceof JCheckBox) {
final JCheckBox cb = (JCheckBox) compo;
if ("unfold".equalsIgnoreCase(cb.getActionCommand())) {
this.flagExpandAllNodes = cb.isSelected();
} else if ("back".equalsIgnoreCase(cb.getActionCommand())) {
this.flagDrawBackground = cb.isSelected();
}
}
}
}
final MindMapPanelConfig newConfig = new MindMapPanelConfig(panel.getConfiguration(), false);
newConfig.setDrawBackground(this.flagDrawBackground);
newConfig.setScale(1.0f);
final RenderedImage image = MindMapPanel.renderMindMapAsImage(panel.getModel(), newConfig, flagExpandAllNodes);
if (image == null) {
if (out == null) {
LOGGER.error("Can't render map as image");
panel.getController().getDialogProvider(panel).msgError(Texts.getString("PNGImageExporter.msgErrorDuringRendering"));
return;
} else {
throw new IOException("Can't render image");
}
}
final ByteArrayOutputStream buff = new ByteArrayOutputStream(128000);
ImageIO.write(image, "png", buff);//NOI18N
final byte[] imageData = buff.toByteArray();
File fileToSaveMap = null;
OutputStream theOut = out;
if (theOut == null) {
fileToSaveMap = MindMapUtils.selectFileToSaveForFileFilter(panel, Texts.getString("PNGImageExporter.saveDialogTitle"), ".png", Texts.getString("PNGImageExporter.filterDescription"), Texts.getString("PNGImageExporter.approveButtonText"));
fileToSaveMap = MindMapUtils.checkFileAndExtension(panel, fileToSaveMap, ".png");//NOI18N
theOut = fileToSaveMap == null ? null : new BufferedOutputStream(new FileOutputStream(fileToSaveMap, false));
}
if (theOut != null) {
try {
IOUtils.write(imageData, theOut);
}
finally {
if (fileToSaveMap != null) {
IOUtils.closeQuietly(theOut);
}
}
}
}
@Override
@Nullable
public String getMnemonic() {
return "png";
}
@Override
@Nonnull
public String getName(@Nonnull final MindMapPanel panel, @Nullable Topic actionTopic, @Nonnull @MustNotContainNull Topic[] selectedTopics) {
return Texts.getString("PNGImageExporter.exporterName");
}
@Override
@Nonnull
public String getReference(@Nonnull final MindMapPanel panel, @Nullable Topic actionTopic, @Nonnull @MustNotContainNull Topic[] selectedTopics) {
return Texts.getString("PNGImageExporter.exporterReference");
}
@Override
@Nonnull
public Icon getIcon(@Nonnull final MindMapPanel panel, @Nullable Topic actionTopic, @Nonnull @MustNotContainNull Topic[] selectedTopics) {
return ICO;
}
@Override
public int getOrder() {
return 4;
}
}