/*
GeoGebra - Dynamic Mathematics for Everyone
http://www.geogebra.org
This file is part of GeoGebra.
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation.
*/
package org.geogebra.desktop.export;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.awt.print.Book;
import java.awt.print.PageFormat;
import java.awt.print.Pageable;
import java.awt.print.Paper;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.util.ArrayList;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import org.geogebra.common.main.App;
import org.geogebra.common.main.Localization;
import org.geogebra.common.util.debug.Log;
import org.geogebra.desktop.euclidian.EuclidianViewD;
import org.geogebra.desktop.gui.GuiManagerD;
import org.geogebra.desktop.gui.TitlePanel;
import org.geogebra.desktop.gui.layout.DockManagerD;
import org.geogebra.desktop.gui.layout.DockPanelD;
import org.geogebra.desktop.gui.view.Gridable;
import org.geogebra.desktop.gui.view.consprotocol.ConstructionProtocolViewD;
import org.geogebra.desktop.main.AppD;
import org.geogebra.desktop.main.GeoGebraPreferencesD;
import org.geogebra.desktop.util.GuiResourcesD;
public class PrintPreviewD extends JDialog {
private static final long serialVersionUID = 1L;
protected int m_wPage;
protected int m_hPage;
protected int m_orientation;
protected int m_scale;
protected List<Printable> m_target;
@SuppressWarnings("rawtypes")
protected JComboBox m_cbScale, m_cbOrientation, m_cbView;
// protected JCheckBox cbEVscalePanel;
protected JScrollPane ps;
protected PreviewContainer m_preview;
protected final AppD app;
protected JPanel tempPanel, panelForTitleAndScaling; // used for title and
// scaling of
// graphics view's
// print preview
protected transient ActionListener lst;
protected boolean kernelChanged = false;
private boolean justPreview = true;
private int[] targetPages;
private Book book;
static Graphics tempGraphics;
static {
BufferedImage img = new BufferedImage(5, 5, BufferedImage.TYPE_INT_RGB);
tempGraphics = img.getGraphics();
}
public static PrintPreviewD get(AppD app, int viewID, int orientation) {
PrintPreviewD ret = new PrintPreviewD(app);
ret.m_target = getPrintables(viewID, app);
ret.m_orientation = orientation;
ret.initPrintPreview();
return ret;
}
static List<Printable> getPrintables(int viewID, AppD app) {
GuiManagerD gui = (GuiManagerD) app.getGuiManager();
if (viewID == App.VIEW_CAS) {
return wrap(gui.getCasView());
} else if (viewID == App.VIEW_CONSTRUCTION_PROTOCOL) {
return (wrap((ConstructionProtocolViewD) app.getGuiManager()
.getConstructionProtocolView()));
} else if (viewID == App.VIEW_SPREADSHEET) {
return wrap(gui.getSpreadsheetView());
} else if (viewID == App.VIEW_EUCLIDIAN2) {
return wrap(app.getEuclidianView2(1));
} else if (viewID == App.VIEW_ALGEBRA) {
return wrap(gui.getAlgebraView());
} else if (viewID == App.VIEW_DATA_ANALYSIS) {
return wrap(gui.getDataAnalysisView());
}
// if there is no view in focus (e.g. just closed the
// focused view),
// it prints the GeoGebra main window
else {
return wrap(app.getEuclidianView1());
}
}
public PrintPreviewD(AppD app) {
// modal=true: user shouldn't be able to
// change anything before actual print
// happened.
super(app.getFrame(), true);
this.app = app;
app.setPrintPreview(this);
}
private static List<Printable> wrap(Gridable target) {
List<Printable> list = new ArrayList<Printable>();
list.add(new PrintGridable(target));
return list;
}
private static List<Printable> wrap(Printable mainComponent) {
ArrayList<Printable> list = new ArrayList<Printable>();
list.add(mainComponent);
return list;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private void initPrintPreview() {
final Localization loc = app.getLocalization();
m_scale = 75; // init scale to 75%
loadPreferences();
setTitle(loc.getMenu("PrintPreview"));
Cursor oldCursor = app.getMainComponent().getCursor();
app.getMainComponent()
.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
getContentPane().setLayout(new BorderLayout());
// print button
JButton btnPrint = new JButton(loc.getMenu("Print"),
app.getScaledIcon(GuiResourcesD.DOCUMENT_PRINT));
lst = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Thread runner = new Thread() {
@Override
public void run() {
try {
PrinterJob prnJob = PrinterJob.getPrinterJob();
prnJob.setPageable(book);
if (!prnJob.printDialog()) {
return;
}
setCursor(Cursor
.getPredefinedCursor(Cursor.WAIT_CURSOR));
justPreview = false;
prnJob.print();
justPreview = true;
setCursor(Cursor.getPredefinedCursor(
Cursor.DEFAULT_CURSOR));
setVisible(false);
} catch (PrinterException ex) {
ex.printStackTrace();
Log.debug("Printing error: " + ex.toString());
}
}
};
runner.start();
}
};
btnPrint.addActionListener(lst);
btnPrint.setAlignmentY(0.5f);
// btnPrint.setMargin(new Insets(2, 2, 2, 2));
// scale comboBox
String[] scales = { "10%", "25%", "50%", "75%", "100%", "150%",
"200%" };
m_cbScale = new JComboBox(scales);
m_cbScale.setSelectedItem(m_scale + "%");
lst = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Thread runner = new Thread() {
@Override
public void run() {
setCursor(
Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
String str = m_cbScale.getSelectedItem().toString();
if (str.endsWith("%")) {
str = str.substring(0, str.length() - 1);
}
str = str.trim();
int scale = 0;
try {
scale = Integer.parseInt(str);
} catch (NumberFormatException ex) {
return;
}
setScale(scale);
setCursor(Cursor.getDefaultCursor());
}
};
runner.start();
}
};
m_cbScale.addActionListener(lst);
m_cbScale.setMaximumSize(m_cbScale.getPreferredSize());
m_cbScale.setEditable(false); // can be set true
// ORIENTATION combo box
String[] orients = { loc.getMenu("Portrait"),
loc.getMenu("Landscape") };
m_cbOrientation = new JComboBox(orients);
m_cbOrientation.setSelectedIndex(
(m_orientation == PageFormat.PORTRAIT) ? 0 : 1);
lst = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Thread runner = new Thread() {
@Override
public void run() {
setCursor(
Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
int pageOrientation = (m_cbOrientation
.getSelectedIndex() == 0) ? PageFormat.PORTRAIT
: PageFormat.LANDSCAPE;
setOrientation(pageOrientation);
PrintPreviewD prev = PrintPreviewD.this;
int width = prev.getPreferredSize().width;
if (width > prev.getWidth()) {
setSize(width, prev.getHeight());
}
setCursor(Cursor.getDefaultCursor());
}
};
runner.start();
}
};
m_cbOrientation.addActionListener(lst);
m_cbOrientation.setMaximumSize(m_cbOrientation.getPreferredSize());
m_cbOrientation.setEditable(false);
// VIEW combo box
m_cbView = new JComboBox(getAvailableViews());
DockPanelD focusedPanel = ((GuiManagerD) app.getGuiManager())
.getLayout().getDockManager().getFocusedPanel();
if (focusedPanel == null) {
m_cbView.setSelectedItem(loc.getMenu("AllViews"));
} else {
m_cbView.setSelectedItem(loc.getMenu(focusedPanel.getViewTitle()));
}
ActionListener lst_view = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Thread runner = new Thread() {
@Override
public void run() {
setCursor(
Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
m_preview.removeAll();
final String selItem = m_cbView.getSelectedItem()
.toString();
// change view
if (selItem.equals(loc.getMenu("AllViews"))) {
final List<Printable> l = new ArrayList<Printable>();
app.forEachView(new App.ViewCallback() {
@Override
public void run(int viewID, String viewName) {
l.addAll(getPrintables(viewID, app));// TODO
}
});
m_target = l;
} else {
m_target = new ArrayList<Printable>();
app.forEachView(new App.ViewCallback() {
@Override
public void run(int viewID, String viewName) {
if (selItem.equals(loc.getMenu(viewName))) {
m_target.addAll(
getPrintables(viewID, app));
}
}
});
}
tempPanel.removeAll();
if (selItem.equals(loc.getMenu("DrawingPad"))
|| selItem.equals(loc.getMenu("AllViews"))) {
tempPanel.add(createPanelForScaling(
app.getEuclidianView1()));
}
if (selItem.equals(loc.getMenu("DrawingPad2"))
|| (selItem.equals(loc.getMenu("AllViews"))
&& app.hasEuclidianView2(1))) {
tempPanel.add(createPanelForScaling(
app.getEuclidianView2(1)));
}
panelForTitleAndScaling.revalidate();
initPages();
updateFormat();
m_preview.doLayout();
m_preview.getParent().getParent().validate();
setCursor(Cursor.getDefaultCursor());
}
};
runner.start();
}
};
m_cbView.addActionListener(lst_view);
m_cbView.setMaximumSize(m_cbView.getPreferredSize());
m_cbView.setEditable(false);
// BUTTON PANEL
JPanel westPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
westPanel.add(btnPrint);
westPanel.add(Box.createHorizontalStrut(30));
westPanel.add(m_cbView);
westPanel.add(Box.createHorizontalStrut(30));
westPanel.add(m_cbScale);
westPanel.add(m_cbOrientation);
JPanel buttonPanel = new JPanel(new BorderLayout(2, 2));
buttonPanel.setBorder(BorderFactory.createEmptyBorder(2, 5, 2, 5));
buttonPanel.add(westPanel, app.getLocalization().borderWest());
// title
TitlePanel titlePanel = new TitlePanel(app);
lst = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
kernelChanged = true;
Thread runner = new Thread() {
@Override
public void run() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
setCursor(Cursor.getPredefinedCursor(
Cursor.WAIT_CURSOR));
updatePages();
setCursor(Cursor.getDefaultCursor());
}
});
}
};
runner.start();
}
};
titlePanel.addActionListener(lst);
m_preview = new PreviewContainer();
ps = new JScrollPane(m_preview);
JPanel centerPanel = new JPanel(new BorderLayout());
panelForTitleAndScaling = new JPanel(new BorderLayout());
// show scale panel for euclidian view
EuclidianViewD ev = app.getEuclidianView1();
EuclidianViewD ev2 = app.getEuclidianView2(1);
// CASView cas = app.getca
app.getSelectionManager().clearSelectedGeos();
tempPanel = new JPanel(new GridLayout(0, 1));
if (m_target.contains(ev)) {
tempPanel.add(createPanelForScaling(ev));
}
if (m_target.contains(ev2)) {
tempPanel.add(createPanelForScaling(ev2));
}
// HACK: m_target gives no information about the current view
panelForTitleAndScaling.add(tempPanel, BorderLayout.SOUTH);
panelForTitleAndScaling.add(titlePanel, BorderLayout.CENTER);
centerPanel.add(panelForTitleAndScaling, BorderLayout.NORTH);
// preview in center
centerPanel.add(ps, BorderLayout.CENTER);
// toolbar north
getContentPane().add(buttonPanel, BorderLayout.NORTH);
// title and preview center
getContentPane().add(centerPanel, BorderLayout.CENTER);
// setDefaultCloseOperation(DISPOSE_ON_CLOSE);
// init the preview
initPages();
updateFormat();
centerOnScreen();
app.getMainComponent().setCursor(oldCursor);
}
private String[] getAvailableViews() {
final ArrayList<String> list = new ArrayList<String>();
final Localization loc = app.getLocalization();
app.forEachView(new App.ViewCallback() {
@Override
public void run(int viewID, String viewName) {
list.add(loc.getPlain(viewName));
}
});
list.add(loc.getPlain("AllViews"));
String[] s = new String[list.size()];
list.toArray(s);
return s;
}
public JPanel createPanelForScaling(final EuclidianViewD view) {
// checkbox to turn on/off printing of scale string
final JCheckBox cbEVscalePanel = new JCheckBox();
cbEVscalePanel.setSelected(view.isPrintScaleString());
cbEVscalePanel.addActionListener(lst);
cbEVscalePanel.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
view.setPrintScaleString(cbEVscalePanel.isSelected());
}
});
// scale panel to set scale of x-axis in cm
PrintScalePanel scalePanel = new PrintScalePanel(app, view);
scalePanel.enableAbsoluteSize(false);
scalePanel.addActionListener(lst);
JPanel retPanel = new JPanel();
DockPanelD dock;
dock = ((DockManagerD) app.getGuiManager().getLayout().getDockManager())
.getPanel(view.getViewID());
retPanel.add(Box.createHorizontalStrut(10));
retPanel.add(new JLabel(dock.getIcon()));
// retPanel.add(new JLabel(loc.getMenu(dock.getViewTitle())));
retPanel.setLayout(new BoxLayout(retPanel, BoxLayout.X_AXIS));
retPanel.setBorder(BorderFactory.createEtchedBorder());
retPanel.add(Box.createHorizontalStrut(10));
retPanel.add(cbEVscalePanel);
retPanel.add(scalePanel);
return retPanel;
}
private void loadPreferences() {
try {
// orientation
String strOrientation = GeoGebraPreferencesD.getPref()
.loadPreference(GeoGebraPreferencesD.PRINT_ORIENTATION,
"landscape");
m_orientation = "portrait".equals(strOrientation)
? PageFormat.PORTRAIT : PageFormat.LANDSCAPE;
// show printing scale in cm
app.getEuclidianView1().setPrintScaleString(Boolean
.valueOf(GeoGebraPreferencesD.getPref().loadPreference(
GeoGebraPreferencesD.PRINT_SHOW_SCALE, "false"))
.booleanValue());
if (app.hasEuclidianView2EitherShowingOrNot(1)) {
app.getEuclidianView2(1)
.setPrintScaleString(Boolean
.valueOf(GeoGebraPreferencesD.getPref()
.loadPreference(
GeoGebraPreferencesD.PRINT_SHOW_SCALE2,
"false"))
.booleanValue());
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void savePreferences() {
// orientation
String strOrientation;
switch (m_orientation) {
case PageFormat.LANDSCAPE:
strOrientation = "landscape";
break;
default:
strOrientation = "portrait";
}
GeoGebraPreferencesD pref = GeoGebraPreferencesD.getPref();
pref.savePreference(GeoGebraPreferencesD.PRINT_ORIENTATION,
strOrientation);
// show printing scale in cm
pref.savePreference(GeoGebraPreferencesD.PRINT_SHOW_SCALE,
Boolean.toString(app.getEuclidianView1().isPrintScaleString()));
if (app.hasEuclidianView2EitherShowingOrNot(1)) {
pref.savePreference(GeoGebraPreferencesD.PRINT_SHOW_SCALE2, Boolean
.toString(app.getEuclidianView2(1).isPrintScaleString()));
}
}
@Override
public void setVisible(boolean flag) {
if (flag) {
// note: preferences loaded in initPreview
super.setVisible(true);
} else {
// store undo info
if (kernelChanged) {
app.storeUndoInfo();
}
// save preferences
savePreferences();
super.setVisible(false);
}
}
private void centerOnScreen() {
// center on screen
pack();
Dimension size = getPreferredSize();
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
int w = Math.min(size.width, dim.width);
int h = Math.min(size.height, (int) (dim.height * 0.9));
setLocation((dim.width - w) / 2, (dim.height - h) / 2);
setSize(w, h);
}
/**
* Sets the orientation of preview and applies current scale.
*/
void initPages() {
PageFormat pageFormat = getDefaultPageFormat();
pageFormat.setOrientation(m_orientation);
if (pageFormat.getWidth() == 0 || pageFormat.getHeight() == 0) {
Log.debug("Unable to determine default page size");
return;
}
int pageIndex = 0;
int targetIndex = 0;
book = new Book();
targetPages = new int[m_target.size()];
while (true) {
if (pageExists(targetIndex, pageIndex)) {
PagePreview pp = new PagePreview(m_target.get(targetIndex),
pageFormat, pageIndex, targetIndex, app);
pp.setScale(m_scale);
m_preview.add(pp);
// book.append(m_target.get(targetIndex), pageFormat);
} else {
book.append(m_target.get(targetIndex), pageFormat, pageIndex);
targetPages[targetIndex] = pageIndex;
targetIndex++;
pageIndex = -1;
}
if (targetIndex >= m_target.size()) {
break;
}
pageIndex++;
}
}
public int computePageIndex(int pageIndex0) {
int pageIndex = pageIndex0;
for (int i = 0; i < targetPages.length
&& targetPages[i] <= pageIndex; i++) {
pageIndex -= targetPages[i];
}
return pageIndex;
}
private static PageFormat getDefaultPageFormat() {
PrinterJob prnJob = PrinterJob.getPrinterJob();
PageFormat pageFormat = prnJob.defaultPage();
Paper paper = pageFormat.getPaper();
double width = paper.getWidth();
double height = paper.getHeight();
if (width > 0 && height > 0) {
// set margins
paper.setImageableArea(AppD.PAGE_MARGIN_X, AppD.PAGE_MARGIN_Y,
width - 2 * AppD.PAGE_MARGIN_X,
height - 2 * AppD.PAGE_MARGIN_Y);
pageFormat.setPaper(paper);
}
return pageFormat;
}
// update Pages, add or remove last page if necessary
void updatePages() {
// update existing pages
Component[] comps = m_preview.getComponents();
int[] lengths = new int[m_target.size()];
for (int i = 0; i < lengths.length; i++) {
lengths[i] = 0;
}
for (int k = 0; k < comps.length; k++) {
if (!(comps[k] instanceof PagePreview)) {
continue;
}
PagePreview pp = (PagePreview) comps[k];
lengths[pp.getTarget()]++;
pp.update();
}
for (int i = 0; i < m_target.size(); i++) {
// add or remove last page if necessary
// last page gone?
if (!pageExists(i, lengths[i] - 1)) {
m_preview.remove(lengths[i] - 1);
m_preview.doLayout();
m_preview.getParent().getParent().validate();
}
// new page?
else if (pageExists(i, lengths[i])) {
PageFormat pageFormat = getDefaultPageFormat();
pageFormat.setOrientation(m_orientation);
if (pageFormat.getHeight() == 0 || pageFormat.getWidth() == 0) {
Log.debug("Unable to determine default page size");
return;
}
PagePreview pp = new PagePreview(m_target.get(i), pageFormat,
lengths[i], i, app);
pp.setScale(m_scale);
m_preview.add(pp);
m_preview.doLayout();
m_preview.getParent().getParent().validate();
}
}
}
public boolean pageExists(int targetIndex, int pageIndex) {
try {
PageFormat pageFormat = getDefaultPageFormat();
pageFormat.setOrientation(m_orientation);
return (m_target.get(targetIndex).print(tempGraphics, pageFormat,
pageIndex) == Printable.PAGE_EXISTS);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
void setOrientation(int orientation) {
m_orientation = orientation;
m_preview.removeAll();
initPages();
updateFormat();
m_preview.doLayout();
m_preview.getParent().getParent().validate();
}
void updateFormat() {
PageFormat pageFormat = getDefaultPageFormat();
pageFormat.setOrientation(m_orientation);
Component[] comps = m_preview.getComponents();
for (int k = 0; k < comps.length; k++) {
if (!(comps[k] instanceof PagePreview)) {
continue;
}
PagePreview pp = (PagePreview) comps[k];
pp.setPageFormat(pageFormat);
}
}
void setScale(int scale) {
m_scale = scale;
Component[] comps = m_preview.getComponents();
for (int k = 0; k < comps.length; k++) {
if (!(comps[k] instanceof PagePreview)) {
continue;
}
PagePreview pp = (PagePreview) comps[k];
pp.setScale(scale);
}
m_preview.doLayout();
m_preview.getParent().getParent().validate();
}
class PreviewContainer extends JPanel implements Pageable {
private static final long serialVersionUID = 1L;
protected int H_GAP = 16;
protected int V_GAP = 10;
@Override
public Dimension getPreferredSize() {
int n = getComponentCount();
if (n == 0) {
return new Dimension(H_GAP, V_GAP);
}
Component comp = getComponent(0);
Dimension dc = comp.getPreferredSize();
int w = dc.width;
int h = dc.height;
Dimension dp = getParent().getSize();
int nCol = Math.max((dp.width - H_GAP) / (w + H_GAP), 1);
int nRow = n / nCol;
if (nRow * nCol < n) {
nRow++;
}
int ww = nCol * (w + H_GAP) + H_GAP;
int hh = nRow * (h + V_GAP) + V_GAP;
Insets ins = getInsets();
return new Dimension(ww + ins.left + ins.right,
hh + ins.top + ins.bottom);
}
@Override
public Dimension getMaximumSize() {
return getPreferredSize();
}
@Override
public Dimension getMinimumSize() {
return getPreferredSize();
}
@Override
public void doLayout() {
Insets ins = getInsets();
int x = ins.left + H_GAP;
int y = ins.top + V_GAP;
int n = getComponentCount();
if (n == 0) {
return;
}
Component comp = getComponent(0);
Dimension dc = comp.getPreferredSize();
int w = dc.width;
int h = dc.height;
Dimension dp = getParent().getSize();
int nCol = Math.max((dp.width - H_GAP) / (w + H_GAP), 1);
int nRow = n / nCol;
if (nRow * nCol < n) {
nRow++;
}
int index = 0;
for (int k = 0; k < nRow; k++) {
for (int m = 0; m < nCol; m++) {
if (index >= n) {
return;
}
comp = getComponent(index++);
comp.setBounds(x, y, w, h);
x += w + H_GAP;
}
y += h + V_GAP;
x = ins.left + H_GAP;
}
}
/*
* **************** Pageable interface ***************
*/
@Override
public int getNumberOfPages() {
return getComponentCount();
}
@Override
public PageFormat getPageFormat(int pageIndex)
throws IndexOutOfBoundsException {
try {
return ((PagePreview) getComponent(pageIndex)).getPageFormat();
} catch (Exception e) {
throw new IndexOutOfBoundsException();
}
}
@Override
public Printable getPrintable(int pageIndex)
throws IndexOutOfBoundsException {
return m_target.get(0);
}
}
public int adjustIndex(int pageIndex0) {
if (!justPreview) {
return computePageIndex(pageIndex0);
}
return pageIndex0;
}
}