/*
This file belongs to the Servoy development and deployment environment, Copyright (C) 1997-2010 Servoy BV
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License as published by the Free
Software Foundation; either version 3 of the License, or (at your option) any
later version.
This program 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along
with this program; if not, see http://www.gnu.org/licenses or write to the Free
Software Foundation,Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
*/
package com.servoy.j2db.printing;
import java.awt.Adjustable;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GraphicsConfiguration;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.awt.print.PageFormat;
import java.awt.print.Pageable;
import java.awt.print.PrinterJob;
import javax.print.Doc;
import javax.print.DocFlavor;
import javax.print.DocPrintJob;
import javax.print.PrintService;
import javax.print.PrintServiceLookup;
import javax.print.ServiceUI;
import javax.print.SimpleDoc;
import javax.print.attribute.DocAttributeSet;
import javax.print.attribute.HashDocAttributeSet;
import javax.print.attribute.HashPrintRequestAttributeSet;
import javax.print.attribute.PrintRequestAttributeSet;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.RepaintManager;
import javax.swing.plaf.basic.BasicComboBoxEditor;
import com.servoy.j2db.FormController;
import com.servoy.j2db.IApplication;
import com.servoy.j2db.ISmartClientApplication;
import com.servoy.j2db.cmd.ICmd;
import com.servoy.j2db.cmd.ICmdManager;
import com.servoy.j2db.dataprocessing.IFoundSetInternal;
import com.servoy.j2db.smart.J2DBClient;
import com.servoy.j2db.smart.cmd.CmdPageSetup;
import com.servoy.j2db.ui.IComponent;
import com.servoy.j2db.util.Debug;
import com.servoy.j2db.util.ObjectWrapper;
import com.servoy.j2db.util.SwingHelper;
import com.servoy.j2db.util.Utils;
import com.servoy.j2db.util.toolbar.Toolbar;
import com.servoy.j2db.util.toolbar.ToolbarButton;
/**
* This is the preview shower class with all the controls arround it
*
* @author jblok
*/
public class PrintPreview extends JPanel implements ActionListener, ItemListener, IComponent
{
private final JPanel desk;
private final FormPreviewPanel fpp;
private final String preferredPrinterName;
private PrinterJob printerJob;
private final int currentShowingPage;
private final ISmartClientApplication application;
private final JComboBox zoom;
private final JComboBox pages;
private final JButton previous_page;
private final JButton next_page;
private static final float MIN_ZOOM_VALUE = 0.1f;
private static final float MAX_ZOOM_VALUE = 4.0f;
public PrintPreview(ISmartClientApplication app, FormController formPanel, IFoundSetInternal fs, int zoomFactor, PrinterJob printerJob) throws Exception
{
this(app, formPanel, fs, printerJob);
float z = getValidZoom(zoomFactor + "");
fpp.zoom(z);
updateEditor(z);
}
public PrintPreview(ISmartClientApplication app, FormController formPanel, IFoundSetInternal fs, PrinterJob printerJob) throws Exception
{
application = app;
application.getScriptEngine().getJSApplication().setDidLastPrintPreviewPrint(false);
preferredPrinterName = formPanel.getPreferredPrinterName();
this.printerJob = printerJob;
setLayout(new BorderLayout());
Toolbar buttonPane = new Toolbar("print", application.getI18NMessage("servoy.toolbar.print.title"), false); //$NON-NLS-1$ //$NON-NLS-2$
buttonPane.setFloatable(false);
// buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.X_AXIS));
// buttonPane.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 0));
//
// buttonPane.add(Box.createRigidArea(new
// Dimension(J2DBClient.BUTTON_SPACING, 0)));
buttonPane.addSeparator(new Dimension(J2DBClient.BUTTON_SPACING, 0));
JButton print = new ToolbarButton(application.getI18NMessage("servoy.toolbar.print.button.print"), application.loadImage("print.gif")); //$NON-NLS-1$//$NON-NLS-2$
print.setActionCommand("print"); //$NON-NLS-1$
print.addActionListener(this);
buttonPane.add(print);
buttonPane.addSeparator(new Dimension(J2DBClient.BUTTON_SPACING, 0));
// buttonPane.add(Box.createRigidArea(new
// Dimension(J2DBClient.BUTTON_SPACING, 0)));
JButton pagesetup = new ToolbarButton(application.getI18NMessage("servoy.toolbar.print.button.pageSetup"), application.loadImage("page_setup.gif")); //$NON-NLS-1$//$NON-NLS-2$
pagesetup.setActionCommand("pagesetup"); //$NON-NLS-1$
pagesetup.addActionListener(this);
buttonPane.add(pagesetup);
// buttonPane.add(Box.createRigidArea(new
// Dimension(J2DBClient.BUTTON_SPACING, 0)));
buttonPane.addSeparator(new Dimension(30, 0));
// buttonPane.add(new JLabel("Page: "));
previous_page = new ToolbarButton(application.loadImage("page_previous.gif")); //$NON-NLS-1$
// previous_page.setPreferredSize(new Dimension(20,print.getHeight()));
previous_page.setToolTipText(application.getI18NMessage("servoy.toolbar.print.button.prevPage.tooltip")); //$NON-NLS-1$
previous_page.setActionCommand("pageprevious"); //$NON-NLS-1$
previous_page.addActionListener(this);
previous_page.setFocusPainted(false);
buttonPane.add(previous_page);
pages = new JComboBox();
pages.setPreferredSize(new Dimension(70, ToolbarButton.PREF_HEIGHT));
pages.setMaximumSize(new Dimension(70, ToolbarButton.PREF_HEIGHT));
buttonPane.add(pages);
next_page = new ToolbarButton(application.loadImage("page_next.gif")); //$NON-NLS-1$
// next_page.setPreferredSize(new Dimension(20,print.getHeight()));
next_page.setToolTipText(application.getI18NMessage("servoy.toolbar.print.button.nextPage.tooltip")); //$NON-NLS-1$
next_page.setActionCommand("pagenext"); //$NON-NLS-1$
next_page.addActionListener(this);
next_page.setFocusPainted(false);
buttonPane.add(next_page);
buttonPane.addSeparator(new Dimension(30, 0));
// buttonPane.add(Box.createRigidArea(new
// Dimension(J2DBClient.BUTTON_SPACING, 0)));
// buttonPane.add(new JLabel("Zoom: "));
zoom = new JComboBox();
zoom.setEditable(true);
zoom.setEditor(new BasicComboBoxEditor());
zoom.setPreferredSize(new Dimension(100, ToolbarButton.PREF_HEIGHT));
zoom.setMaximumSize(new Dimension(100, ToolbarButton.PREF_HEIGHT));
zoom.addItem(new ObjectWrapper(application.getI18NMessage("servoy.print.zoom"), new Float(0.99f)));//to be enable //$NON-NLS-1$
zoom.addItem(new ObjectWrapper(new Float(MIN_ZOOM_VALUE * 100.0f).intValue() + "%", new Float(MIN_ZOOM_VALUE))); //$NON-NLS-1$
zoom.addItem(new ObjectWrapper("25%", new Float(0.25f))); //$NON-NLS-1$
zoom.addItem(new ObjectWrapper("50%", new Float(0.5f))); //$NON-NLS-1$
zoom.addItem(new ObjectWrapper("80%", new Float(0.8f))); //$NON-NLS-1$
zoom.addItem(new ObjectWrapper("100%", new Float(1f))); //$NON-NLS-1$
zoom.addItem(new ObjectWrapper("125%", new Float(1.25f))); //$NON-NLS-1$
zoom.addItem(new ObjectWrapper("150%", new Float(1.5f))); //$NON-NLS-1$
zoom.addItem(new ObjectWrapper("200%", new Float(2f))); //$NON-NLS-1$
zoom.addItem(new ObjectWrapper("300%", new Float(3f))); //$NON-NLS-1$
zoom.addItem(new ObjectWrapper(new Float(MAX_ZOOM_VALUE * 100.0f).intValue() + "%", new Float(MAX_ZOOM_VALUE))); //$NON-NLS-1$
// zoom.setSelectedIndex(3);
zoom.addItemListener(this);
buttonPane.add(zoom);
buttonPane.addSeparator(new Dimension(J2DBClient.BUTTON_SPACING, 0));
// buttonPane.add(Box.createRigidArea(new
// Dimension(J2DBClient.BUTTON_SPACING, 0)));
JButton close = new ToolbarButton(application.getI18NMessage("servoy.button.close")); //$NON-NLS-1$
close.setActionCommand("close"); //$NON-NLS-1$
close.addActionListener(this);
// close.setPreferredSize(print.getPreferredSize());
buttonPane.add(close);
// buttonPane.add(Box.createHorizontalGlue());
// buttonPane.setBorder(BorderFactory.createEtchedBorder());//
// mptyBorder(10, 10, 10, 10));
add(buttonPane, BorderLayout.NORTH);
desk = new JPanel();
desk.setBackground(new Color(141, 141, 141));
desk.setBorder(BorderFactory.createEmptyBorder(25, 25, 25, 25));
fpp = new FormPreviewPanel(application, formPanel, fs);
desk.add(fpp, BorderLayout.CENTER);
JScrollPane deskScroller = new ReleaseScrollPane(desk);
add(deskScroller);
currentShowingPage = 0;
}
private static class ReleaseScrollPane extends JScrollPane
{
public ReleaseScrollPane(Component view)
{
super(view);
}
/**
* @see JScrollPane#createHorizontalScrollBar()
*/
@Override
public JScrollBar createHorizontalScrollBar()
{
return new EditListScrollBar(Adjustable.HORIZONTAL);
}
/**
* @see JScrollPane#createVerticalScrollBar()
*/
@Override
public JScrollBar createVerticalScrollBar()
{
return new EditListScrollBar(Adjustable.VERTICAL);
}
protected class EditListScrollBar extends ScrollBar
{
protected int iValue = -1;
public EditListScrollBar(int orientation)
{
super(orientation);
setUnitIncrement(25);
}
// public void setUI(ComponentUI ui)
// {
// super.setUI(ui);
// }
//
/**
* @see Adjustable#setValue(int)
*/
@Override
public void setValue(int v)
{
if (iValue == -1)
{
super.setValue(v);
}
else
{
iValue = v;
}
}
/**
* @see Component#processMouseMotionEvent(MouseEvent)
*/
@Override
protected void processMouseMotionEvent(MouseEvent e)
{
if (iValue == -1 && e.getID() == MouseEvent.MOUSE_DRAGGED)
{
iValue = getValue();
}
super.processMouseMotionEvent(e);
}
/**
* @see Component#processMouseEvent(MouseEvent)
*/
@Override
protected void processMouseEvent(MouseEvent e)
{
// if(e.getID() == MouseEvent.MOUSE_PRESSED)
// {
// if(getViewport().getView() instanceof CellEditorListener)
// {
// ((CellEditorListener)getViewport().getView()).editingStopped(null);
// requestFocus();
// }
// }
if (iValue != -1 && e.getID() == MouseEvent.MOUSE_RELEASED)
{
super.setValue(iValue);
iValue = -1;
}
super.processMouseEvent(e);
}
}
}
public void showPages()
{
application.getScheduledExecutor().execute(new Runnable()
{
public void run()
{
application.blockGUI(application.getI18NMessage("servoy.print.status.generatePages")); //$NON-NLS-1$
try
{
int pageCount = fpp.process();
fillPageComboModel(pageCount);
application.invokeLater(new Runnable()
{
public void run()
{
showPage(currentShowingPage);//show again in new Format
}
});
}
catch (Exception ex)
{
application.reportError(application.getI18NMessage("servoy.print.error.retrievingAllData"), ex); //$NON-NLS-1$
}
finally
{
application.releaseGUI();
}
}
});
}
private void fillPageComboModel(int pageCount)
{
DefaultComboBoxModel dml = new DefaultComboBoxModel();
for (int i = 0; i < pageCount; i++)
{
dml.addElement(new Integer(i + 1));
}
if (dml.getSize() != 0)
{
pages.setModel(dml);
pages.setSelectedIndex(0);
pages.addItemListener(this);
}
previous_page.setEnabled(false);
next_page.setEnabled(dml.getSize() > 1);
}
//shows dialog if argument is null
private void setPageFormat()//boolean showDialog)
{
try
{
PageFormat pf = CmdPageSetup.getPageFormat(fpp.getPageFormat(), application.getSettings(), application.getMainApplicationFrame());
if (pf != null)
{
fpp.setPageFormat(pf);
}
showPages();
}
catch (Exception ex)
{
application.reportError(application.getI18NMessage("servoy.print.error.cannotCreatePreview"), ex); //$NON-NLS-1$
}
showPage(currentShowingPage);//show again in new Format
}
public static PrintService[] capablePrintServices;
public static void startPrinting(IApplication application, Pageable pageable, PrinterJob a_printerJob, String a_preferredPrinterName,
boolean showPrinterSelectDialog, boolean avoidDialogs)
{
RepaintManager currentManager = RepaintManager.currentManager(application.getPrintingRendererParent().getParent());
boolean isDoubleBufferingEnabled = false;
try
{
if (currentManager != null)
{
isDoubleBufferingEnabled = currentManager.isDoubleBufferingEnabled();
}
if (a_printerJob != null)
{
//for plugin printing
a_printerJob.setPageable(pageable);
a_printerJob.setJobName("Servoy Print");//$NON-NLS-1$
if (showPrinterSelectDialog)
{
if (!a_printerJob.printDialog())
{
return;
}
SwingHelper.dispatchEvents(100);//hide dialog
}
if (currentManager != null)
{
currentManager.setDoubleBufferingEnabled(false);
}
a_printerJob.print();
}
else
{
//by default we use old system for mac, new is not always working
boolean useSystemPrintDialog = Utils.getAsBoolean(application.getSettings().getProperty("useSystemPrintDialog", "" + Utils.isAppleMacOS())); //$NON-NLS-1$//$NON-NLS-2$
DocFlavor flavor = DocFlavor.SERVICE_FORMATTED.PAGEABLE;
PrintRequestAttributeSet pras = new HashPrintRequestAttributeSet();
if (capablePrintServices == null)
{
capablePrintServices = PrintServiceLookup.lookupPrintServices(flavor, pras);
}
PrintService service = null;
if (capablePrintServices == null || capablePrintServices.length == 0)
{
if (avoidDialogs)
{
Debug.warn("Cannot find capable print services. Print aborted.");
return;
}
else
{
JOptionPane.showConfirmDialog(((ISmartClientApplication)application).getMainApplicationFrame(),
application.getI18NMessage("servoy.print.msg.noPrintersFound"), application.getI18NMessage("servoy.print.printing.title"), //$NON-NLS-1$ //$NON-NLS-2$
JOptionPane.OK_OPTION, JOptionPane.ERROR_MESSAGE);
capablePrintServices = new PrintService[0];
showPrinterSelectDialog = true;//must show select printer and if none found show this
useSystemPrintDialog = true;//we leave to system to show no printers are found, important for apple mac
}
}
else
{
service = capablePrintServices[0];//default select
}
PrintService systemDefaultPrinter = PrintServiceLookup.lookupDefaultPrintService();
if (systemDefaultPrinter != null)
{
//check if system default printer is in capable list
for (PrintService ps : capablePrintServices)
{
if (ps.getName().equalsIgnoreCase(systemDefaultPrinter.getName()))
{
service = systemDefaultPrinter;
break;
}
}
}
boolean didFindPrinter = true; //want custom preferred printer
if (a_preferredPrinterName != null)
{
didFindPrinter = false;
for (PrintService ps : capablePrintServices)
{
if (ps.getName().equalsIgnoreCase(a_preferredPrinterName))
{
didFindPrinter = true;
service = ps;
break;
}
}
}
if (!didFindPrinter)
{
if (avoidDialogs)
{
Debug.warn("Cannot find capable printer for preferred form printer name '" + a_preferredPrinterName +
"'. Trying to use default/any capable printer.");
}
else
{
showPrinterSelectDialog = true;//did not found the prefered , do show
}
}
if (!useSystemPrintDialog)
{
if (showPrinterSelectDialog)
{
JFrame frame = ((ISmartClientApplication)application).getMainApplicationFrame();
GraphicsConfiguration gc = frame.getGraphicsConfiguration();
Point loc = frame.getLocation();
service = ServiceUI.printDialog(gc, loc.x + 50, loc.y + 50, capablePrintServices, service, flavor, pras);
}
if (service != null)
{
if (currentManager != null)
{
currentManager.setDoubleBufferingEnabled(false);
}
DocPrintJob job = service.createPrintJob();
DocAttributeSet das = new HashDocAttributeSet();
Doc doc = new SimpleDoc(pageable, flavor, das);
if (job != null)
{
job.print(doc, pras);
}
else
{
// for example if the print service cancels (e.g. print to pdf and then user cancel when choosing save location)
application.reportWarning(application.getI18NMessage("servoy.print.error.cannotPrintDocument")); //$NON-NLS-1$
}
}
}
else
{
a_printerJob = PrinterJob.getPrinterJob();
a_printerJob.setPageable(pageable);
a_printerJob.setJobName("Servoy Print");//$NON-NLS-1$
if (service != null)
{
a_printerJob.setPrintService(service);
}
if (showPrinterSelectDialog)
{
if (!a_printerJob.printDialog())
{
return;
}
SwingHelper.dispatchEvents(100);//hide dialog
}
if (currentManager != null)
{
currentManager.setDoubleBufferingEnabled(false);
}
a_printerJob.print();
}
}
}
catch (Exception ex)
{
application.reportError(application.getI18NMessage("servoy.print.error.cannotPrintDocument"), ex); //$NON-NLS-1$
}
finally
{
if (currentManager != null)
{
currentManager.setDoubleBufferingEnabled(isDoubleBufferingEnabled);
}
}
}
public void destroy()
{
if (fpp != null)
{
fpp.destroy();
fpp.setVisible(false);//no more paints
}
printerJob = null;
}
public void showPage(int pageNumber)
{
try
{
fpp.showPage(pageNumber);
}
catch (Exception ex)
{
Debug.error(ex);
}
}
private void show(int diff)
{
int idx = pages.getSelectedIndex();
if (idx != -1)
{
int newIndex = idx + diff;
if (newIndex < pages.getModel().getSize())
{
pages.setSelectedIndex(newIndex);
}
}
}
public void actionPerformed(ActionEvent ae)
{
String command = ae.getActionCommand();
if (command.equals("print")) //$NON-NLS-1$
{
startPrinting(application, fpp.getPageable(), printerJob, preferredPrinterName, true, false);
}
else if (command.equals("pagesetup")) setPageFormat();//true); //$NON-NLS-1$
else if (command.equals("pageprevious")) show(-1); //$NON-NLS-1$
else if (command.equals("pagenext")) show(+1); //$NON-NLS-1$
else if (command.equals("close")) //$NON-NLS-1$
{
ICmdManager cm = application.getCmdManager();
Action a = cm.getRegisteredAction("cmdbrowsemode"); //$NON-NLS-1$
ICmd cmd = (ICmd)a;
cmd.doIt(ae);
}
}
public void itemStateChanged(ItemEvent ie)
{
if (ie.getStateChange() == ItemEvent.SELECTED)
{
int idx = pages.getSelectedIndex();
previous_page.setEnabled(idx != 0);
next_page.setEnabled(idx < pages.getModel().getSize() - 1);
if (ie.getSource() == pages)
{
showPage(idx);
}
else
{
//the comboBox editor also triggers an unnecessary ItemEvent(.SELECTED)
if (zoom.getSelectedItem() instanceof String) return;
ObjectWrapper tw = (ObjectWrapper)zoom.getSelectedItem();
Float z = (Float)tw.getType();
fpp.zoom(z.floatValue());
if (getParent() != null)
{
invalidate();
getParent().validate();
}
}
}
if (ie.getStateChange() == ItemEvent.DESELECTED)
{
int idx = pages.getSelectedIndex();
previous_page.setEnabled(idx != 0);
next_page.setEnabled(idx < pages.getModel().getSize() - 1);
String zoomString = "";
try
{
zoomString = (String)zoom.getEditor().getItem();
}
catch (ClassCastException e)
{
return;
}
if (zoomString.indexOf("%") == zoomString.length() - 1) zoomString = zoomString.substring(0, zoomString.length() - 1);
float z = getValidZoom(zoomString);
fpp.zoom(z);
updateEditor(z);
if (getParent() != null)
{
invalidate();
getParent().validate();
}
}
}
/**
* sets the zoom factor to a value between MIN_ZOOM_VALUE and MAX_ZOOM_VALUE, validating the input
*
* @param zoomFactor
* @return the zoom factor for the print preview
*/
private float getValidZoom(Object zoomFactor)
{
float z = Utils.getAsFloat(zoomFactor);
if (z == 0) return 1.0f;
z /= 100.0f;
if (z < MIN_ZOOM_VALUE) z = MIN_ZOOM_VALUE;
if (z > MAX_ZOOM_VALUE) z = MAX_ZOOM_VALUE;
return z;
}
/**
* always display the zoom factor in the comboBox (when setting it manually or programatically)
*
* @param zoomFactor
*/
private void updateEditor(float zoomFactor)
{
zoom.getEditor().setItem((new Float(zoomFactor * 100.0f)).intValue() + "%");
}
public void setComponentVisible(boolean visible)
{
setVisible(visible);
}
public void setComponentEnabled(boolean enabled)
{
setEnabled(enabled);
}
public String getId()
{
return (String)getClientProperty("Id"); //$NON-NLS-1$
}
}