/* * Copyright 2015-2016 Igor Maznitsa. * * Licensed 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 com.igormaznitsa.mindmap.print; import com.igormaznitsa.mindmap.model.logger.Logger; import com.igormaznitsa.mindmap.model.logger.LoggerFactory; import com.igormaznitsa.mindmap.swing.panel.HasPreferredFocusComponent; import com.igormaznitsa.mindmap.swing.panel.MindMapPanel; import com.igormaznitsa.mindmap.swing.services.UIComponentFactory; import com.igormaznitsa.mindmap.swing.services.UIComponentFactoryProvider; import java.awt.BasicStroke; import javax.swing.JButton; import javax.swing.JToolBar; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Stroke; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.geom.Rectangle2D; import java.awt.print.PageFormat; import java.awt.print.Pageable; import java.awt.print.Printable; import java.awt.print.PrinterException; import java.awt.print.PrinterJob; import java.util.ArrayList; import java.util.List; import java.util.ResourceBundle; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.swing.DefaultComboBoxModel; import javax.swing.Icon; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JPanel; import javax.swing.JScrollPane; import com.igormaznitsa.meta.annotation.MustNotContainNull; import com.igormaznitsa.mindmap.swing.services.IconID; import com.igormaznitsa.mindmap.swing.services.ImageIconServiceProvider; public class MMDPrintPanel extends JPanel implements HasPreferredFocusComponent { static final Color BORDER_COLOR = Color.GRAY; static final Stroke BORDER_STYLE = new BasicStroke(1f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 1f, new float[]{1f, 3f}, 0f); private static final Icon ICO_PRINTER = ImageIconServiceProvider.findInstance().getIconForId(IconID.ICON_PRINTER); private static final Icon ICO_PAGE = ImageIconServiceProvider.findInstance().getIconForId(IconID.ICON_PAGE); public enum IconId { PRINTER, PAGE } public interface Adaptor { void startBackgroundTask (@Nonnull MMDPrintPanel source, @Nonnull String name, @Nonnull Runnable task); boolean isDarkTheme (@Nonnull MMDPrintPanel source); void onPrintTaskStarted (@Nonnull MMDPrintPanel source); @Nonnull Dimension getPreferredSizeOfPanel(@Nonnull MMDPrintPanel source); } private static final long serialVersionUID = -2588424836865316862L; protected static final ResourceBundle BUNDLE = java.util.ResourceBundle.getBundle("com/igormaznitsa/mindmap/swing/panel/Bundle"); private static final Logger LOGGER = LoggerFactory.getLogger(MMDPrintPanel.class); private static final UIComponentFactory UI_COMPO_FACTORY = UIComponentFactoryProvider.findInstance(); private PageFormat pageFormat; private final Pages previewContainer; private double pageZoomFactor; private final JComponent preferredFocusedComponent; private final MindMapPanel mmdPanel; private final Adaptor theAdaptor; private final JCheckBox checkBoxDrawBorder; private PrintPage[][] pages; public MMDPrintPanel (@Nullable final Adaptor adaptor, @Nonnull final MindMapPanel mindMapPanel) { super(new BorderLayout()); this.theAdaptor = adaptor == null ? new DefaultMMDPrintPanelAdaptor() : adaptor; this.mmdPanel = mindMapPanel; super.setPreferredSize(this.theAdaptor.getPreferredSizeOfPanel(this)); final JScrollPane scrollPane = UI_COMPO_FACTORY.makeScrollPane(); final PrinterJob printerJob = PrinterJob.getPrinterJob(); printerJob.setJobName("Mind map print job"); final JToolBar toolBar = UI_COMPO_FACTORY.makeToolBar(); toolBar.setOrientation(JToolBar.HORIZONTAL); toolBar.setFloatable(false); final JButton buttonPrint = UI_COMPO_FACTORY.makeButton(); buttonPrint.setText(BUNDLE.getString("MMDPrintPanel.PrintPages")); buttonPrint.setIcon(ICO_PRINTER); final MMDPrintPanel theInstance = this; buttonPrint.addActionListener(new ActionListener() { @Override public void actionPerformed (@Nonnull final ActionEvent e) { splitToPagesForCurrentFormat(); final PageFormat format = pageFormat; final int numOfPages = countPages(); final boolean drawBorder = checkBoxDrawBorder.isSelected(); printerJob.setPageable(new Pageable() { @Override public int getNumberOfPages () { return numOfPages; } @Override @Nonnull public PageFormat getPageFormat (final int pageIndex) throws IndexOutOfBoundsException { final PrintPage thepage = findPageForIndex(pageIndex); if (thepage == null) throw new IndexOutOfBoundsException(); return format; } @SuppressWarnings("unchecked") @Override @Nonnull public Printable getPrintable (final int pageIndex) throws IndexOutOfBoundsException { final PrintPage thePage = findPageForIndex(pageIndex); if (thePage == null) throw new IndexOutOfBoundsException(); return new Printable() { @SuppressWarnings("unchecked") @Override public int print (@Nonnull final Graphics graphics, @Nonnull final PageFormat format, final int pageIndex) throws PrinterException { final Graphics2D gfx = (Graphics2D) graphics; if (thePage == null) { return Printable.NO_SUCH_PAGE; } else { gfx.translate((int) format.getImageableX(), (int) format.getImageableY()); thePage.print(gfx); if (drawBorder) { final Stroke stroke = gfx.getStroke(); gfx.setStroke(BORDER_STYLE); gfx.draw(new Rectangle2D.Double(0d, 0d, format.getImageableWidth(), format.getImageableHeight())); gfx.setColor(BORDER_COLOR); gfx.setStroke(stroke); } gfx.translate(-(int) format.getImageableX(), -(int) format.getImageableY()); return Printable.PAGE_EXISTS; } } }; } }); if (printerJob.printDialog()) { theAdaptor.startBackgroundTask(theInstance, BUNDLE.getString("MMDPrintPanel.JobTitle"), new Runnable() { @Override public void run () { try { LOGGER.info("Start print job"); printerJob.print(); } catch (PrinterException ex) { LOGGER.error("Print error", ex); throw new RuntimeException("Error during print job", ex); } } }); theAdaptor.onPrintTaskStarted(theInstance); } } }); toolBar.add(buttonPrint); final JButton buttonPrintOptions = UI_COMPO_FACTORY.makeButton(); buttonPrintOptions.setText(BUNDLE.getString("MMDPrintPanel.PageSetup")); buttonPrintOptions.setIcon(ICO_PAGE); buttonPrintOptions.addActionListener(new ActionListener() { @Override public void actionPerformed (@Nonnull final ActionEvent e) { pageFormat = printerJob.pageDialog(pageFormat); splitToPagesForCurrentFormat(); scrollPane.revalidate(); scrollPane.repaint(); } }); toolBar.add(buttonPrintOptions); final List<String> scalesList = new ArrayList<String>(); scalesList.add("10 %"); for (int i = 25; i < 225; i += 25) { scalesList.add(Integer.toString(i) + " %"); } final String[] scales = scalesList.toArray(new String[scalesList.size()]); this.pageZoomFactor = 0.1d; final JComboBox comboBoxScale = UI_COMPO_FACTORY.makeComboBox(); comboBoxScale.setModel(new DefaultComboBoxModel(scales)); comboBoxScale.setEditable(false); comboBoxScale.addActionListener(new ActionListener() { @Override public void actionPerformed (@Nonnull final ActionEvent e) { final int percent = Integer.parseInt(comboBoxScale.getSelectedItem().toString().split("\\s")[0]); pageZoomFactor = (double) percent / 100d; splitToPagesForCurrentFormat(); scrollPane.revalidate(); scrollPane.getViewport().revalidate(); scrollPane.repaint(); } }); comboBoxScale.setMaximumSize(comboBoxScale.getPreferredSize()); toolBar.addSeparator(); toolBar.add(comboBoxScale); toolBar.addSeparator(); this.checkBoxDrawBorder = UI_COMPO_FACTORY.makeCheckBox(); this.checkBoxDrawBorder.setSelected(true); this.checkBoxDrawBorder.setText(BUNDLE.getString("MMDPrintPanel.DrawBorder")); this.checkBoxDrawBorder.addActionListener(new ActionListener() { @Override public void actionPerformed (@Nonnull ActionEvent e) { scrollPane.repaint(); } }); toolBar.add(this.checkBoxDrawBorder); this.add(toolBar, BorderLayout.NORTH); this.pageFormat = printerJob.defaultPage(); this.splitToPagesForCurrentFormat(); this.previewContainer = new Pages(this); scrollPane.getViewport().setView(this.previewContainer); this.preferredFocusedComponent = scrollPane; this.add(scrollPane, BorderLayout.CENTER); } @Override @Nonnull public JComponent getComponentPreferredForFocus () { return this.preferredFocusedComponent; } @Nonnull PageFormat getPageFormat () { return this.pageFormat; } int countPages () { int result = 0; for (final PrintPage[] p : this.pages) { result += p.length; } return result; } @Nonnull @MustNotContainNull PrintPage[][] getPages () { return this.pages; } @Nullable PrintPage findPageForIndex (final int value) { int i = 0; for (final PrintPage[] row : this.pages) { for (final PrintPage page : row) { if (i == value) { return page; } i++; } } return null; } double getScale () { return this.pageZoomFactor; } private void splitToPagesForCurrentFormat () { final MMDPrint printer = new MMDPrint(this.mmdPanel, (int) this.pageFormat.getImageableWidth(), (int) this.pageFormat.getImageableHeight(), 1.0d); this.pages = printer.getPages(); } boolean isDrawBorder () { return this.checkBoxDrawBorder.isSelected(); } boolean isDarkTheme () { return this.theAdaptor.isDarkTheme(this); } }