Path: blob/master/src/java.desktop/share/classes/sun/print/ServiceDialog.java
41153 views
/*1* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.print;2627import java.awt.BorderLayout;28import java.awt.Color;29import java.awt.Component;30import java.awt.Container;31import java.awt.Dialog;32import java.awt.FlowLayout;33import java.awt.Frame;34import java.awt.GraphicsConfiguration;35import java.awt.GridBagLayout;36import java.awt.GridBagConstraints;37import java.awt.GridLayout;38import java.awt.Insets;39import java.awt.Toolkit;40import java.awt.Window;41import java.awt.event.ActionEvent;42import java.awt.event.ActionListener;43import java.awt.event.FocusEvent;44import java.awt.event.FocusListener;45import java.awt.event.ItemEvent;46import java.awt.event.ItemListener;47import java.awt.event.WindowEvent;48import java.awt.event.WindowAdapter;49import java.awt.print.PrinterJob;50import java.io.File;51import java.io.FilePermission;52import java.io.IOException;53import java.net.URI;54import java.net.URL;55import java.text.DecimalFormat;56import java.util.Locale;57import java.util.ResourceBundle;58import java.util.Vector;59import javax.print.*;60import javax.print.attribute.*;61import javax.print.attribute.standard.*;62import javax.swing.*;63import javax.swing.border.Border;64import javax.swing.border.EmptyBorder;65import javax.swing.border.TitledBorder;66import javax.swing.event.ChangeEvent;67import javax.swing.event.ChangeListener;68import javax.swing.event.DocumentEvent;69import javax.swing.event.DocumentListener;70import javax.swing.event.PopupMenuEvent;71import javax.swing.event.PopupMenuListener;72import javax.swing.text.NumberFormatter;73import sun.print.SunPageSelection;74import java.awt.event.KeyEvent;75import java.net.URISyntaxException;76import java.lang.reflect.Field;77import java.net.MalformedURLException;7879/**80* A class which implements a cross-platform print dialog.81*82* @author Chris Campbell83*/84@SuppressWarnings("serial") // Superclass is not serializable across versions85public class ServiceDialog extends JDialog implements ActionListener {8687/**88* Waiting print status (user response pending).89*/90public static final int WAITING = 0;9192/**93* Approve print status (user activated "Print" or "OK").94*/95public static final int APPROVE = 1;9697/**98* Cancel print status (user activated "Cancel");99*/100public static final int CANCEL = 2;101102private static final String strBundle = "sun.print.resources.serviceui";103private static final Insets panelInsets = new Insets(6, 6, 6, 6);104private static final Insets compInsets = new Insets(3, 6, 3, 6);105106private static ResourceBundle messageRB;107private JTabbedPane tpTabs;108private JButton btnCancel, btnApprove;109private PrintService[] services;110private int defaultServiceIndex;111private PrintRequestAttributeSet asOriginal;112private HashPrintRequestAttributeSet asCurrent;113private PrintService psCurrent;114private DocFlavor docFlavor;115private int status;116117private ValidatingFileChooser jfc;118119private GeneralPanel pnlGeneral;120private PageSetupPanel pnlPageSetup;121private AppearancePanel pnlAppearance;122123private boolean isAWT = false;124static {125initResource();126}127128129/**130* Constructor for the "standard" print dialog (containing all relevant131* tabs)132*/133public ServiceDialog(GraphicsConfiguration gc,134int x, int y,135PrintService[] services,136int defaultServiceIndex,137DocFlavor flavor,138PrintRequestAttributeSet attributes,139Window window)140{141super(window, getMsg("dialog.printtitle"), Dialog.DEFAULT_MODALITY_TYPE, gc);142initPrintDialog(x, y, services, defaultServiceIndex,143flavor, attributes);144}145146/**147* Initialize print dialog.148*/149void initPrintDialog(int x, int y,150PrintService[] services,151int defaultServiceIndex,152DocFlavor flavor,153PrintRequestAttributeSet attributes)154{155this.services = services;156this.defaultServiceIndex = defaultServiceIndex;157this.asOriginal = attributes;158this.asCurrent = new HashPrintRequestAttributeSet(attributes);159this.psCurrent = services[defaultServiceIndex];160this.docFlavor = flavor;161SunPageSelection pages =162(SunPageSelection)attributes.get(SunPageSelection.class);163if (pages != null) {164isAWT = true;165}166167if (attributes.get(DialogOwner.class) != null) {168DialogOwner owner = (DialogOwner)attributes.get(DialogOwner.class);169/* When the ServiceDialog is constructed the caller of the170* constructor checks for this attribute and if it specifies a171* window then it will use that in the constructor instead of172* inferring one from keyboard focus.173* In this case the owner of the dialog is the same as that174* specified in the attribute and we do not need to set the175* on top property176*/177if ((getOwner() == null) || (owner.getOwner() != getOwner())) {178try {179setAlwaysOnTop(true);180} catch (SecurityException e) {181}182}183}184Container c = getContentPane();185c.setLayout(new BorderLayout());186187tpTabs = new JTabbedPane();188tpTabs.setBorder(new EmptyBorder(5, 5, 5, 5));189190String gkey = getMsg("tab.general");191int gmnemonic = getVKMnemonic("tab.general");192pnlGeneral = new GeneralPanel();193tpTabs.add(gkey, pnlGeneral);194tpTabs.setMnemonicAt(0, gmnemonic);195196String pkey = getMsg("tab.pagesetup");197int pmnemonic = getVKMnemonic("tab.pagesetup");198pnlPageSetup = new PageSetupPanel();199tpTabs.add(pkey, pnlPageSetup);200tpTabs.setMnemonicAt(1, pmnemonic);201202String akey = getMsg("tab.appearance");203int amnemonic = getVKMnemonic("tab.appearance");204pnlAppearance = new AppearancePanel();205tpTabs.add(akey, pnlAppearance);206tpTabs.setMnemonicAt(2, amnemonic);207208c.add(tpTabs, BorderLayout.CENTER);209210updatePanels();211212JPanel pnlSouth = new JPanel(new FlowLayout(FlowLayout.TRAILING));213btnApprove = createExitButton("button.print", this);214pnlSouth.add(btnApprove);215getRootPane().setDefaultButton(btnApprove);216btnCancel = createExitButton("button.cancel", this);217handleEscKey(btnCancel);218pnlSouth.add(btnCancel);219c.add(pnlSouth, BorderLayout.SOUTH);220221addWindowListener(new WindowAdapter() {222public void windowClosing(WindowEvent event) {223dispose(CANCEL);224}225});226227getAccessibleContext().setAccessibleDescription(getMsg("dialog.printtitle"));228setResizable(false);229setLocation(x, y);230pack();231}232233/**234* Constructor for the solitary "page setup" dialog235*/236public ServiceDialog(GraphicsConfiguration gc,237int x, int y,238PrintService ps,239DocFlavor flavor,240PrintRequestAttributeSet attributes,241Window window)242{243super(window, getMsg("dialog.pstitle"), Dialog.DEFAULT_MODALITY_TYPE, gc);244initPageDialog(x, y, ps, flavor, attributes);245}246247/**248* Initialize "page setup" dialog249*/250void initPageDialog(int x, int y,251PrintService ps,252DocFlavor flavor,253PrintRequestAttributeSet attributes)254{255this.psCurrent = ps;256this.docFlavor = flavor;257this.asOriginal = attributes;258this.asCurrent = new HashPrintRequestAttributeSet(attributes);259260if (attributes.get(DialogOwner.class) != null) {261/* See comments in same block in initPrintDialog */262DialogOwner owner = (DialogOwner)attributes.get(DialogOwner.class);263if ((getOwner() == null) || (owner.getOwner() != getOwner())) {264try {265setAlwaysOnTop(true);266} catch (SecurityException e) {267}268}269}270271Container c = getContentPane();272c.setLayout(new BorderLayout());273274pnlPageSetup = new PageSetupPanel();275c.add(pnlPageSetup, BorderLayout.CENTER);276277pnlPageSetup.updateInfo();278279JPanel pnlSouth = new JPanel(new FlowLayout(FlowLayout.TRAILING));280btnApprove = createExitButton("button.ok", this);281pnlSouth.add(btnApprove);282getRootPane().setDefaultButton(btnApprove);283btnCancel = createExitButton("button.cancel", this);284handleEscKey(btnCancel);285pnlSouth.add(btnCancel);286c.add(pnlSouth, BorderLayout.SOUTH);287288addWindowListener(new WindowAdapter() {289public void windowClosing(WindowEvent event) {290dispose(CANCEL);291}292});293294getAccessibleContext().setAccessibleDescription(getMsg("dialog.pstitle"));295setResizable(false);296setLocation(x, y);297pack();298}299300/**301* Performs Cancel when Esc key is pressed.302*/303private void handleEscKey(JButton btnCancel) {304@SuppressWarnings("serial") // anonymous class305Action cancelKeyAction = new AbstractAction() {306public void actionPerformed(ActionEvent e) {307dispose(CANCEL);308}309};310KeyStroke cancelKeyStroke =311KeyStroke.getKeyStroke((char)KeyEvent.VK_ESCAPE, 0);312InputMap inputMap =313btnCancel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);314ActionMap actionMap = btnCancel.getActionMap();315316if (inputMap != null && actionMap != null) {317inputMap.put(cancelKeyStroke, "cancel");318actionMap.put("cancel", cancelKeyAction);319}320}321322323/**324* Returns the current status of the dialog (whether the user has selected325* the "Print" or "Cancel" button)326*/327public int getStatus() {328return status;329}330331/**332* Returns an AttributeSet based on whether or not the user cancelled the333* dialog. If the user selected "Print" we return their new selections,334* otherwise we return the attributes that were passed in initially.335*/336public PrintRequestAttributeSet getAttributes() {337if (status == APPROVE) {338return asCurrent;339} else {340return asOriginal;341}342}343344/**345* Returns a PrintService based on whether or not the user cancelled the346* dialog. If the user selected "Print" we return the user's selection347* for the PrintService, otherwise we return null.348*/349public PrintService getPrintService() {350if (status == APPROVE) {351return psCurrent;352} else {353return null;354}355}356357/**358* Sets the current status flag for the dialog and disposes it (thus359* returning control of the parent frame back to the user)360*/361public void dispose(int status) {362this.status = status;363364super.dispose();365}366367public void actionPerformed(ActionEvent e) {368Object source = e.getSource();369boolean approved = false;370371if (source == btnApprove) {372approved = true;373374if (pnlGeneral != null) {375if (pnlGeneral.isPrintToFileRequested()) {376approved = showFileChooser();377} else {378asCurrent.remove(Destination.class);379}380}381}382383dispose(approved ? APPROVE : CANCEL);384}385386/**387* Displays a JFileChooser that allows the user to select the destination388* for "Print To File"389*/390private boolean showFileChooser() {391Class<Destination> dstCategory = Destination.class;392393Destination dst = (Destination)asCurrent.get(dstCategory);394if (dst == null) {395dst = (Destination)asOriginal.get(dstCategory);396if (dst == null) {397dst = (Destination)psCurrent.getDefaultAttributeValue(dstCategory);398// "dst" should not be null. The following code399// is only added to safeguard against a possible400// buggy implementation of a PrintService having a401// null default Destination.402if (dst == null) {403try {404dst = new Destination(new URI("file:out.prn"));405} catch (URISyntaxException e) {406}407}408}409}410411File fileDest;412if (dst != null) {413try {414fileDest = new File(dst.getURI());415} catch (Exception e) {416// all manner of runtime exceptions possible417fileDest = new File("out.prn");418}419} else {420fileDest = new File("out.prn");421}422423ValidatingFileChooser jfc = new ValidatingFileChooser();424jfc.setApproveButtonText(getMsg("button.ok"));425jfc.setDialogTitle(getMsg("dialog.printtofile"));426jfc.setDialogType(JFileChooser.SAVE_DIALOG);427jfc.setSelectedFile(fileDest);428429int returnVal = jfc.showDialog(this, null);430if (returnVal == JFileChooser.APPROVE_OPTION) {431fileDest = jfc.getSelectedFile();432433try {434asCurrent.add(new Destination(fileDest.toURI()));435} catch (Exception e) {436asCurrent.remove(dstCategory);437}438} else {439asCurrent.remove(dstCategory);440}441442return (returnVal == JFileChooser.APPROVE_OPTION);443}444445/**446* Updates each of the top level panels447*/448private void updatePanels() {449pnlGeneral.updateInfo();450pnlPageSetup.updateInfo();451pnlAppearance.updateInfo();452}453454/**455* Initialize ResourceBundle456*/457@SuppressWarnings("removal")458public static void initResource() {459java.security.AccessController.doPrivileged(460new java.security.PrivilegedAction<Object>() {461public Object run() {462try {463messageRB = ResourceBundle.getBundle(strBundle);464return null;465} catch (java.util.MissingResourceException e) {466throw new Error("Fatal: Resource for ServiceUI " +467"is missing");468}469}470}471);472}473474/**475* Returns message string from resource476*/477public static String getMsg(String key) {478try {479return removeMnemonics(messageRB.getString(key));480} catch (java.util.MissingResourceException e) {481throw new Error("Fatal: Resource for ServiceUI is broken; " +482"there is no " + key + " key in resource");483}484}485486private static String removeMnemonics(String s) {487int i = s.indexOf('&');488int len = s.length();489if (i < 0 || i == (len - 1)) {490return s;491}492int j = s.indexOf('&', i+1);493if (j == i+1) {494if (j+1 == len) {495return s.substring(0, i+1); // string ends with &&496} else {497return s.substring(0, i+1) + removeMnemonics(s.substring(j+1));498}499}500// ok first & not double &&501if (i == 0) {502return removeMnemonics(s.substring(1));503} else {504return (s.substring(0, i) + removeMnemonics(s.substring(i+1)));505}506}507508509/**510* Returns mnemonic character from resource511*/512private static char getMnemonic(String key) {513String str = messageRB.getString(key).replace("&&", "");514int index = str.indexOf('&');515if (0 <= index && index < str.length() - 1) {516char c = str.charAt(index + 1);517return Character.toUpperCase(c);518} else {519return (char)0;520}521}522523/**524* Returns the mnemonic as a KeyEvent.VK constant from the resource.525*/526static Class<?> _keyEventClazz = null;527private static int getVKMnemonic(String key) {528String s = String.valueOf(getMnemonic(key));529if ( s == null || s.length() != 1) {530return 0;531}532String vkString = "VK_" + s.toUpperCase();533534try {535if (_keyEventClazz == null) {536_keyEventClazz= Class.forName("java.awt.event.KeyEvent",537true, (ServiceDialog.class).getClassLoader());538}539Field field = _keyEventClazz.getDeclaredField(vkString);540int value = field.getInt(null);541return value;542} catch (Exception e) {543}544return 0;545}546547/**548* Returns URL for image resource549*/550private static URL getImageResource(final String key) {551@SuppressWarnings("removal")552URL url = java.security.AccessController.doPrivileged(553new java.security.PrivilegedAction<URL>() {554public URL run() {555URL url = ServiceDialog.class.getResource(556"resources/" + key);557return url;558}559});560561if (url == null) {562throw new Error("Fatal: Resource for ServiceUI is broken; " +563"there is no " + key + " key in resource");564}565566return url;567}568569/**570* Creates a new JButton and sets its text, mnemonic, and ActionListener571*/572private static JButton createButton(String key, ActionListener al) {573JButton btn = new JButton(getMsg(key));574btn.setMnemonic(getMnemonic(key));575btn.addActionListener(al);576577return btn;578}579580/**581* Creates a new JButton and sets its text, and ActionListener582*/583private static JButton createExitButton(String key, ActionListener al) {584String str = getMsg(key);585JButton btn = new JButton(str);586btn.addActionListener(al);587btn.getAccessibleContext().setAccessibleDescription(str);588return btn;589}590591/**592* Creates a new JCheckBox and sets its text, mnemonic, and ActionListener593*/594private static JCheckBox createCheckBox(String key, ActionListener al) {595JCheckBox cb = new JCheckBox(getMsg(key));596cb.setMnemonic(getMnemonic(key));597cb.addActionListener(al);598599return cb;600}601602/**603* Creates a new JRadioButton and sets its text, mnemonic,604* and ActionListener605*/606private static JRadioButton createRadioButton(String key,607ActionListener al)608{609JRadioButton rb = new JRadioButton(getMsg(key));610rb.setMnemonic(getMnemonic(key));611rb.addActionListener(al);612613return rb;614}615616/**617* Creates a pop-up dialog for "no print service"618*/619public static void showNoPrintService(GraphicsConfiguration gc)620{621Frame dlgFrame = new Frame(gc);622JOptionPane.showMessageDialog(dlgFrame,623getMsg("dialog.noprintermsg"));624dlgFrame.dispose();625}626627/**628* Sets the constraints for the GridBagLayout and adds the Component629* to the given Container630*/631private static void addToGB(Component comp, Container cont,632GridBagLayout gridbag,633GridBagConstraints constraints)634{635gridbag.setConstraints(comp, constraints);636cont.add(comp);637}638639/**640* Adds the AbstractButton to both the given ButtonGroup and Container641*/642private static void addToBG(AbstractButton button, Container cont,643ButtonGroup bg)644{645bg.add(button);646cont.add(button);647}648649650651652/**653* The "General" tab. Includes the controls for PrintService,654* PageRange, and Copies/Collate.655*/656@SuppressWarnings("serial") // Superclass is not serializable across versions657private class GeneralPanel extends JPanel {658659private PrintServicePanel pnlPrintService;660private PrintRangePanel pnlPrintRange;661private CopiesPanel pnlCopies;662663public GeneralPanel() {664super();665666GridBagLayout gridbag = new GridBagLayout();667GridBagConstraints c = new GridBagConstraints();668669setLayout(gridbag);670671c.fill = GridBagConstraints.BOTH;672c.insets = panelInsets;673c.weightx = 1.0;674c.weighty = 1.0;675676c.gridwidth = GridBagConstraints.REMAINDER;677pnlPrintService = new PrintServicePanel();678addToGB(pnlPrintService, this, gridbag, c);679680c.gridwidth = GridBagConstraints.RELATIVE;681pnlPrintRange = new PrintRangePanel();682addToGB(pnlPrintRange, this, gridbag, c);683684c.gridwidth = GridBagConstraints.REMAINDER;685pnlCopies = new CopiesPanel();686addToGB(pnlCopies, this, gridbag, c);687}688689public boolean isPrintToFileRequested() {690return (pnlPrintService.isPrintToFileSelected());691}692693public void updateInfo() {694pnlPrintService.updateInfo();695pnlPrintRange.updateInfo();696pnlCopies.updateInfo();697}698}699700@SuppressWarnings("serial") // Superclass is not serializable across versions701private class PrintServicePanel extends JPanel702implements ActionListener, ItemListener, PopupMenuListener703{704private final String strTitle = getMsg("border.printservice");705private FilePermission printToFilePermission;706private JButton btnProperties;707private JCheckBox cbPrintToFile;708private JComboBox<String> cbName;709private JLabel lblType, lblStatus, lblInfo;710private ServiceUIFactory uiFactory;711private boolean changedService = false;712private boolean filePermission;713714public PrintServicePanel() {715super();716717uiFactory = psCurrent.getServiceUIFactory();718719GridBagLayout gridbag = new GridBagLayout();720GridBagConstraints c = new GridBagConstraints();721722setLayout(gridbag);723setBorder(BorderFactory.createTitledBorder(strTitle));724725String[] psnames = new String[services.length];726for (int i = 0; i < psnames.length; i++) {727psnames[i] = services[i].getName();728}729cbName = new JComboBox<>(psnames);730cbName.setSelectedIndex(defaultServiceIndex);731cbName.addItemListener(this);732cbName.addPopupMenuListener(this);733734c.fill = GridBagConstraints.BOTH;735c.insets = compInsets;736737c.weightx = 0.0;738JLabel lblName = new JLabel(getMsg("label.psname"), JLabel.TRAILING);739lblName.setDisplayedMnemonic(getMnemonic("label.psname"));740lblName.setLabelFor(cbName);741addToGB(lblName, this, gridbag, c);742c.weightx = 1.0;743c.gridwidth = GridBagConstraints.RELATIVE;744addToGB(cbName, this, gridbag, c);745c.weightx = 0.0;746c.gridwidth = GridBagConstraints.REMAINDER;747btnProperties = createButton("button.properties", this);748addToGB(btnProperties, this, gridbag, c);749750c.weighty = 1.0;751lblStatus = addLabel(getMsg("label.status"), gridbag, c);752lblStatus.setLabelFor(null);753754lblType = addLabel(getMsg("label.pstype"), gridbag, c);755lblType.setLabelFor(null);756757c.gridwidth = 1;758addToGB(new JLabel(getMsg("label.info"), JLabel.TRAILING),759this, gridbag, c);760c.gridwidth = GridBagConstraints.RELATIVE;761lblInfo = new JLabel();762lblInfo.setLabelFor(null);763764addToGB(lblInfo, this, gridbag, c);765766c.gridwidth = GridBagConstraints.REMAINDER;767cbPrintToFile = createCheckBox("checkbox.printtofile", this);768addToGB(cbPrintToFile, this, gridbag, c);769770filePermission = allowedToPrintToFile();771}772773public boolean isPrintToFileSelected() {774return cbPrintToFile.isSelected();775}776777private JLabel addLabel(String text,778GridBagLayout gridbag, GridBagConstraints c)779{780c.gridwidth = 1;781addToGB(new JLabel(text, JLabel.TRAILING), this, gridbag, c);782783c.gridwidth = GridBagConstraints.REMAINDER;784JLabel label = new JLabel();785addToGB(label, this, gridbag, c);786787return label;788}789790@SuppressWarnings("deprecation")791public void actionPerformed(ActionEvent e) {792Object source = e.getSource();793794if (source == btnProperties) {795if (uiFactory != null) {796JDialog dialog = (JDialog)uiFactory.getUI(797ServiceUIFactory.MAIN_UIROLE,798ServiceUIFactory.JDIALOG_UI);799800if (dialog != null) {801dialog.show();802} else {803DocumentPropertiesUI docPropertiesUI = null;804try {805docPropertiesUI =806(DocumentPropertiesUI)uiFactory.getUI807(DocumentPropertiesUI.DOCUMENTPROPERTIES_ROLE,808DocumentPropertiesUI.DOCPROPERTIESCLASSNAME);809} catch (Exception ex) {810}811if (docPropertiesUI != null) {812PrinterJobWrapper wrapper = (PrinterJobWrapper)813asCurrent.get(PrinterJobWrapper.class);814if (wrapper == null) {815return; // should not happen, defensive only.816}817PrinterJob job = wrapper.getPrinterJob();818if (job == null) {819return; // should not happen, defensive only.820}821PrintRequestAttributeSet newAttrs =822docPropertiesUI.showDocumentProperties823(job, ServiceDialog.this, psCurrent, asCurrent);824if (newAttrs != null) {825asCurrent.addAll(newAttrs);826updatePanels();827}828}829}830}831}832}833834public void itemStateChanged(ItemEvent e) {835if (e.getStateChange() == ItemEvent.SELECTED) {836int index = cbName.getSelectedIndex();837838if ((index >= 0) && (index < services.length)) {839if (!services[index].equals(psCurrent)) {840psCurrent = services[index];841uiFactory = psCurrent.getServiceUIFactory();842changedService = true;843844Destination dest =845(Destination)asOriginal.get(Destination.class);846// to preserve the state of Print To File847if ((dest != null || isPrintToFileSelected())848&& psCurrent.isAttributeCategorySupported(849Destination.class)) {850851if (dest != null) {852asCurrent.add(dest);853} else {854dest = (Destination)psCurrent.855getDefaultAttributeValue(Destination.class);856// "dest" should not be null. The following code857// is only added to safeguard against a possible858// buggy implementation of a PrintService having a859// null default Destination.860if (dest == null) {861try {862dest =863new Destination(new URI("file:out.prn"));864} catch (URISyntaxException ue) {865}866}867868if (dest != null) {869asCurrent.add(dest);870}871}872} else {873asCurrent.remove(Destination.class);874}875}876}877}878}879880public void popupMenuWillBecomeVisible(PopupMenuEvent e) {881changedService = false;882}883884public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {885if (changedService) {886changedService = false;887updatePanels();888}889}890891public void popupMenuCanceled(PopupMenuEvent e) {892}893894/**895* We disable the "Print To File" checkbox if this returns false896*/897private boolean allowedToPrintToFile() {898try {899throwPrintToFile();900return true;901} catch (SecurityException e) {902return false;903}904}905906/**907* Break this out as it may be useful when we allow API to908* specify printing to a file. In that case its probably right909* to throw a SecurityException if the permission is not granted.910*/911private void throwPrintToFile() {912@SuppressWarnings("removal")913SecurityManager security = System.getSecurityManager();914if (security != null) {915if (printToFilePermission == null) {916printToFilePermission =917new FilePermission("<<ALL FILES>>", "read,write");918}919security.checkPermission(printToFilePermission);920}921}922923public void updateInfo() {924Class<Destination> dstCategory = Destination.class;925boolean dstSupported = false;926boolean dstSelected = false;927boolean dstAllowed = filePermission ?928allowedToPrintToFile() : false;929930// setup Destination (print-to-file) widgets931Destination dst = (Destination)asCurrent.get(dstCategory);932if (dst != null) {933try {934dst.getURI().toURL();935if (psCurrent.isAttributeValueSupported(dst, docFlavor,936asCurrent)) {937dstSupported = true;938dstSelected = true;939}940} catch (MalformedURLException ex) {941dstSupported = true;942}943} else {944if (psCurrent.isAttributeCategorySupported(dstCategory)) {945dstSupported = true;946}947}948cbPrintToFile.setEnabled(dstSupported && dstAllowed);949cbPrintToFile.setSelected(dstSelected && dstAllowed950&& dstSupported);951952// setup PrintService information widgets953Attribute type = psCurrent.getAttribute(PrinterMakeAndModel.class);954if (type != null) {955lblType.setText(type.toString());956}957Attribute status =958psCurrent.getAttribute(PrinterIsAcceptingJobs.class);959if (status != null) {960lblStatus.setText(getMsg(status.toString()));961}962Attribute info = psCurrent.getAttribute(PrinterInfo.class);963if (info != null) {964lblInfo.setText(info.toString());965}966PrinterJob job = null;967PrinterJobWrapper wrapper = (PrinterJobWrapper)968asCurrent.get(PrinterJobWrapper.class);969if (wrapper != null) {970job = wrapper.getPrinterJob();971}972btnProperties.setEnabled(uiFactory != null && job != null);973}974}975976@SuppressWarnings("serial") // Superclass is not serializable across versions977private class PrintRangePanel extends JPanel978implements ActionListener, FocusListener979{980private final String strTitle = getMsg("border.printrange");981private final PageRanges prAll = new PageRanges(1, Integer.MAX_VALUE);982private JRadioButton rbAll, rbPages, rbSelect;983private JFormattedTextField tfRangeFrom, tfRangeTo;984private JLabel lblRangeTo;985private boolean prSupported;986private boolean prPgRngSupported;987988public PrintRangePanel() {989super();990991GridBagLayout gridbag = new GridBagLayout();992GridBagConstraints c = new GridBagConstraints();993994setLayout(gridbag);995setBorder(BorderFactory.createTitledBorder(strTitle));996997c.fill = GridBagConstraints.BOTH;998c.insets = compInsets;999c.gridwidth = GridBagConstraints.REMAINDER;10001001ButtonGroup bg = new ButtonGroup();1002JPanel pnlTop = new JPanel(new FlowLayout(FlowLayout.LEADING));1003rbAll = createRadioButton("radiobutton.rangeall", this);1004rbAll.setSelected(true);1005bg.add(rbAll);1006pnlTop.add(rbAll);1007addToGB(pnlTop, this, gridbag, c);10081009// Selection never seemed to work so I'm commenting this part.1010/*1011if (isAWT) {1012JPanel pnlMiddle =1013new JPanel(new FlowLayout(FlowLayout.LEADING));1014rbSelect =1015createRadioButton("radiobutton.selection", this);1016bg.add(rbSelect);1017pnlMiddle.add(rbSelect);1018addToGB(pnlMiddle, this, gridbag, c);1019}1020*/10211022JPanel pnlBottom = new JPanel(new FlowLayout(FlowLayout.LEADING));1023rbPages = createRadioButton("radiobutton.rangepages", this);1024bg.add(rbPages);1025pnlBottom.add(rbPages);1026DecimalFormat format = new DecimalFormat("####0");1027format.setMinimumFractionDigits(0);1028format.setMaximumFractionDigits(0);1029format.setMinimumIntegerDigits(0);1030format.setMaximumIntegerDigits(5);1031format.setParseIntegerOnly(true);1032format.setDecimalSeparatorAlwaysShown(false);1033NumberFormatter nf = new NumberFormatter(format);1034nf.setMinimum(1);1035nf.setMaximum(Integer.MAX_VALUE);1036nf.setAllowsInvalid(true);1037nf.setCommitsOnValidEdit(true);1038tfRangeFrom = new JFormattedTextField(nf);1039tfRangeFrom.setColumns(4);1040tfRangeFrom.setEnabled(false);1041tfRangeFrom.addActionListener(this);1042tfRangeFrom.addFocusListener(this);1043tfRangeFrom.setFocusLostBehavior(1044JFormattedTextField.PERSIST);1045tfRangeFrom.getAccessibleContext().setAccessibleName(1046getMsg("radiobutton.rangepages"));1047pnlBottom.add(tfRangeFrom);1048lblRangeTo = new JLabel(getMsg("label.rangeto"));1049lblRangeTo.setEnabled(false);1050pnlBottom.add(lblRangeTo);1051NumberFormatter nfto;1052try {1053nfto = (NumberFormatter)nf.clone();1054} catch (CloneNotSupportedException e) {1055nfto = new NumberFormatter();1056}1057tfRangeTo = new JFormattedTextField(nfto);1058tfRangeTo.setColumns(4);1059tfRangeTo.setEnabled(false);1060tfRangeTo.addFocusListener(this);1061tfRangeTo.getAccessibleContext().setAccessibleName(1062getMsg("label.rangeto"));1063pnlBottom.add(tfRangeTo);1064addToGB(pnlBottom, this, gridbag, c);1065}10661067public void actionPerformed(ActionEvent e) {1068Object source = e.getSource();1069SunPageSelection select = SunPageSelection.ALL;10701071setupRangeWidgets();10721073if (source == rbAll) {1074asCurrent.add(prAll);1075} else if (source == rbSelect) {1076select = SunPageSelection.SELECTION;1077} else if (source == rbPages ||1078source == tfRangeFrom ||1079source == tfRangeTo) {1080updateRangeAttribute();1081select = SunPageSelection.RANGE;1082}10831084if (isAWT) {1085asCurrent.add(select);1086}1087}10881089public void focusLost(FocusEvent e) {1090Object source = e.getSource();10911092if ((source == tfRangeFrom) || (source == tfRangeTo)) {1093updateRangeAttribute();1094}1095}10961097public void focusGained(FocusEvent e) {}10981099private void setupRangeWidgets() {1100boolean rangeEnabled = (rbPages.isSelected() && prPgRngSupported);1101tfRangeFrom.setEnabled(rangeEnabled);1102tfRangeTo.setEnabled(rangeEnabled);1103lblRangeTo.setEnabled(rangeEnabled);1104}11051106private void updateRangeAttribute() {1107String strFrom = tfRangeFrom.getText();1108String strTo = tfRangeTo.getText();11091110int min;1111int max;11121113try {1114min = Integer.parseInt(strFrom);1115} catch (NumberFormatException e) {1116min = 1;1117}11181119try {1120max = Integer.parseInt(strTo);1121} catch (NumberFormatException e) {1122max = min;1123}11241125if (min < 1) {1126min = 1;1127tfRangeFrom.setValue(1);1128}11291130if (max < min) {1131max = min;1132tfRangeTo.setValue(min);1133}11341135PageRanges pr = new PageRanges(min, max);1136asCurrent.add(pr);1137}11381139public void updateInfo() {1140Class<PageRanges> prCategory = PageRanges.class;1141prSupported = false;11421143if (psCurrent.isAttributeCategorySupported(prCategory) ||1144isAWT) {1145prSupported = true;1146prPgRngSupported = psCurrent.isAttributeValueSupported(prAll,1147docFlavor,1148asCurrent);1149}11501151SunPageSelection select = SunPageSelection.ALL;1152int min = 1;1153int max = 1;11541155PageRanges pr = (PageRanges)asCurrent.get(prCategory);1156if (pr != null) {1157if (!pr.equals(prAll)) {1158select = SunPageSelection.RANGE;11591160int[][] members = pr.getMembers();1161if ((members.length > 0) &&1162(members[0].length > 1)) {1163min = members[0][0];1164max = members[0][1];1165}1166}1167}11681169if (isAWT) {1170select = (SunPageSelection)asCurrent.get(1171SunPageSelection.class);1172}11731174if (select == SunPageSelection.ALL) {1175rbAll.setSelected(true);1176} else if (select == SunPageSelection.SELECTION) {1177// Comment this for now - rbSelect is not initialized1178// because Selection button is not added.1179// See PrintRangePanel above.11801181//rbSelect.setSelected(true);1182} else { // RANGE1183rbPages.setSelected(true);1184}1185tfRangeFrom.setValue(min);1186tfRangeTo.setValue(max);1187rbAll.setEnabled(prSupported);1188rbPages.setEnabled(prPgRngSupported);1189setupRangeWidgets();1190}1191}11921193@SuppressWarnings("serial") // Superclass is not serializable across versions1194private class CopiesPanel extends JPanel1195implements ActionListener, ChangeListener1196{1197private final String strTitle = getMsg("border.copies");1198private SpinnerNumberModel snModel;1199private JSpinner spinCopies;1200private JLabel lblCopies;1201private JCheckBox cbCollate;1202private boolean scSupported;12031204public CopiesPanel() {1205super();12061207GridBagLayout gridbag = new GridBagLayout();1208GridBagConstraints c = new GridBagConstraints();12091210setLayout(gridbag);1211setBorder(BorderFactory.createTitledBorder(strTitle));12121213c.fill = GridBagConstraints.HORIZONTAL;1214c.insets = compInsets;12151216lblCopies = new JLabel(getMsg("label.numcopies"), JLabel.TRAILING);1217lblCopies.setDisplayedMnemonic(getMnemonic("label.numcopies"));1218lblCopies.getAccessibleContext().setAccessibleName(1219getMsg("label.numcopies"));1220addToGB(lblCopies, this, gridbag, c);12211222snModel = new SpinnerNumberModel(1, 1, 999, 1);1223spinCopies = new JSpinner(snModel);1224lblCopies.setLabelFor(spinCopies);1225// REMIND1226((JSpinner.NumberEditor)spinCopies.getEditor()).getTextField().setColumns(3);1227spinCopies.addChangeListener(this);1228c.gridwidth = GridBagConstraints.REMAINDER;1229addToGB(spinCopies, this, gridbag, c);12301231cbCollate = createCheckBox("checkbox.collate", this);1232cbCollate.setEnabled(false);1233addToGB(cbCollate, this, gridbag, c);1234}12351236public void actionPerformed(ActionEvent e) {1237if (cbCollate.isSelected()) {1238asCurrent.add(SheetCollate.COLLATED);1239} else {1240asCurrent.add(SheetCollate.UNCOLLATED);1241}1242}12431244public void stateChanged(ChangeEvent e) {1245updateCollateCB();12461247asCurrent.add(new Copies(snModel.getNumber().intValue()));1248}12491250private void updateCollateCB() {1251int num = snModel.getNumber().intValue();1252if (isAWT) {1253cbCollate.setEnabled(true);1254} else {1255cbCollate.setEnabled((num > 1) && scSupported);1256}1257}12581259public void updateInfo() {1260Class<Copies> cpCategory = Copies.class;1261Class<SheetCollate> scCategory = SheetCollate.class;1262boolean cpSupported = false;1263scSupported = false;12641265// setup Copies spinner1266if (psCurrent.isAttributeCategorySupported(cpCategory)) {1267cpSupported = true;1268}1269CopiesSupported cs =1270(CopiesSupported)psCurrent.getSupportedAttributeValues(1271cpCategory, null, null);1272if (cs == null) {1273cs = new CopiesSupported(1, 999);1274}1275Copies cp = (Copies)asCurrent.get(cpCategory);1276if (cp == null) {1277cp = (Copies)psCurrent.getDefaultAttributeValue(cpCategory);1278if (cp == null) {1279cp = new Copies(1);1280}1281}1282spinCopies.setEnabled(cpSupported);1283lblCopies.setEnabled(cpSupported);12841285int[][] members = cs.getMembers();1286int min, max;1287if ((members.length > 0) && (members[0].length > 0)) {1288min = members[0][0];1289max = members[0][1];1290} else {1291min = 1;1292max = Integer.MAX_VALUE;1293}1294snModel.setMinimum(min);1295snModel.setMaximum(max);12961297int value = cp.getValue();1298if ((value < min) || (value > max)) {1299value = min;1300}1301snModel.setValue(value);13021303// setup Collate checkbox1304if (psCurrent.isAttributeCategorySupported(scCategory)) {1305scSupported = true;1306}1307SheetCollate sc = (SheetCollate)asCurrent.get(scCategory);1308if (sc == null) {1309sc = (SheetCollate)psCurrent.getDefaultAttributeValue(scCategory);1310if (sc == null) {1311sc = SheetCollate.UNCOLLATED;1312}1313if (sc != null &&1314!psCurrent.isAttributeValueSupported(sc, docFlavor, asCurrent)) {1315scSupported = false;1316}1317} else {1318if (!psCurrent.isAttributeValueSupported(sc, docFlavor, asCurrent)) {1319scSupported = false;1320}1321}1322cbCollate.setSelected(sc == SheetCollate.COLLATED && scSupported);1323updateCollateCB();1324}1325}13261327132813291330/**1331* The "Page Setup" tab. Includes the controls for MediaSource/MediaTray,1332* OrientationRequested, and Sides.1333*/1334@SuppressWarnings("serial") // Superclass is not serializable across versions1335private class PageSetupPanel extends JPanel {13361337private MediaPanel pnlMedia;1338private OrientationPanel pnlOrientation;1339private MarginsPanel pnlMargins;13401341public PageSetupPanel() {1342super();13431344GridBagLayout gridbag = new GridBagLayout();1345GridBagConstraints c = new GridBagConstraints();13461347setLayout(gridbag);13481349c.fill = GridBagConstraints.BOTH;1350c.insets = panelInsets;1351c.weightx = 1.0;1352c.weighty = 1.0;13531354c.gridwidth = GridBagConstraints.REMAINDER;1355pnlMedia = new MediaPanel();1356addToGB(pnlMedia, this, gridbag, c);13571358pnlOrientation = new OrientationPanel();1359c.gridwidth = GridBagConstraints.RELATIVE;1360addToGB(pnlOrientation, this, gridbag, c);13611362pnlMargins = new MarginsPanel();1363pnlOrientation.addOrientationListener(pnlMargins);1364pnlMedia.addMediaListener(pnlMargins);1365c.gridwidth = GridBagConstraints.REMAINDER;1366addToGB(pnlMargins, this, gridbag, c);1367}13681369public void updateInfo() {1370pnlMedia.updateInfo();1371pnlOrientation.updateInfo();1372pnlMargins.updateInfo();1373}1374}13751376@SuppressWarnings("serial") // Superclass is not serializable across versions1377private class MarginsPanel extends JPanel1378implements ActionListener, FocusListener {13791380private final String strTitle = getMsg("border.margins");1381private JFormattedTextField leftMargin, rightMargin,1382topMargin, bottomMargin;1383private JLabel lblLeft, lblRight, lblTop, lblBottom;1384private int units = MediaPrintableArea.MM;1385// storage for the last margin values calculated, -ve is uninitialised1386private float lmVal = -1f,rmVal = -1f, tmVal = -1f, bmVal = -1f;1387// storage for margins as objects mapped into orientation for display1388private Float lmObj,rmObj,tmObj,bmObj;13891390public MarginsPanel() {1391super();13921393GridBagLayout gridbag = new GridBagLayout();1394GridBagConstraints c = new GridBagConstraints();1395c.fill = GridBagConstraints.HORIZONTAL;1396c.weightx = 1.0;1397c.weighty = 0.0;1398c.insets = compInsets;13991400setLayout(gridbag);1401setBorder(BorderFactory.createTitledBorder(strTitle));14021403String unitsKey = "label.millimetres";1404String defaultCountry = Locale.getDefault().getCountry();1405if (defaultCountry != null &&1406(defaultCountry.isEmpty() ||1407defaultCountry.equals(Locale.US.getCountry()) ||1408defaultCountry.equals(Locale.CANADA.getCountry()))) {1409unitsKey = "label.inches";1410units = MediaPrintableArea.INCH;1411}1412String unitsMsg = getMsg(unitsKey);14131414DecimalFormat format;1415if (units == MediaPrintableArea.MM) {1416format = new DecimalFormat("###.##");1417format.setMaximumIntegerDigits(3);1418} else {1419format = new DecimalFormat("##.##");1420format.setMaximumIntegerDigits(2);1421}14221423format.setMinimumFractionDigits(1);1424format.setMaximumFractionDigits(2);1425format.setMinimumIntegerDigits(1);1426format.setParseIntegerOnly(false);1427format.setDecimalSeparatorAlwaysShown(true);1428NumberFormatter nf = new NumberFormatter(format);1429nf.setMinimum(Float.valueOf(0.0f));1430nf.setMaximum(Float.valueOf(999.0f));1431nf.setAllowsInvalid(true);1432nf.setCommitsOnValidEdit(true);14331434leftMargin = new JFormattedTextField(nf);1435leftMargin.addFocusListener(this);1436leftMargin.addActionListener(this);1437leftMargin.getAccessibleContext().setAccessibleName(1438getMsg("label.leftmargin"));1439rightMargin = new JFormattedTextField(nf);1440rightMargin.addFocusListener(this);1441rightMargin.addActionListener(this);1442rightMargin.getAccessibleContext().setAccessibleName(1443getMsg("label.rightmargin"));1444topMargin = new JFormattedTextField(nf);1445topMargin.addFocusListener(this);1446topMargin.addActionListener(this);1447topMargin.getAccessibleContext().setAccessibleName(1448getMsg("label.topmargin"));14491450bottomMargin = new JFormattedTextField(nf);1451bottomMargin.addFocusListener(this);1452bottomMargin.addActionListener(this);1453bottomMargin.getAccessibleContext().setAccessibleName(1454getMsg("label.bottommargin"));14551456c.gridwidth = GridBagConstraints.RELATIVE;1457lblLeft = new JLabel(getMsg("label.leftmargin") + " " + unitsMsg,1458JLabel.LEADING);1459lblLeft.setDisplayedMnemonic(getMnemonic("label.leftmargin"));1460lblLeft.setLabelFor(leftMargin);1461addToGB(lblLeft, this, gridbag, c);14621463c.gridwidth = GridBagConstraints.REMAINDER;1464lblRight = new JLabel(getMsg("label.rightmargin") + " " + unitsMsg,1465JLabel.LEADING);1466lblRight.setDisplayedMnemonic(getMnemonic("label.rightmargin"));1467lblRight.setLabelFor(rightMargin);1468addToGB(lblRight, this, gridbag, c);14691470c.gridwidth = GridBagConstraints.RELATIVE;1471addToGB(leftMargin, this, gridbag, c);14721473c.gridwidth = GridBagConstraints.REMAINDER;1474addToGB(rightMargin, this, gridbag, c);14751476// add an invisible spacing component.1477addToGB(new JPanel(), this, gridbag, c);14781479c.gridwidth = GridBagConstraints.RELATIVE;1480lblTop = new JLabel(getMsg("label.topmargin") + " " + unitsMsg,1481JLabel.LEADING);1482lblTop.setDisplayedMnemonic(getMnemonic("label.topmargin"));1483lblTop.setLabelFor(topMargin);1484addToGB(lblTop, this, gridbag, c);14851486c.gridwidth = GridBagConstraints.REMAINDER;1487lblBottom = new JLabel(getMsg("label.bottommargin") +1488" " + unitsMsg, JLabel.LEADING);1489lblBottom.setDisplayedMnemonic(getMnemonic("label.bottommargin"));1490lblBottom.setLabelFor(bottomMargin);1491addToGB(lblBottom, this, gridbag, c);14921493c.gridwidth = GridBagConstraints.RELATIVE;1494addToGB(topMargin, this, gridbag, c);14951496c.gridwidth = GridBagConstraints.REMAINDER;1497addToGB(bottomMargin, this, gridbag, c);14981499}15001501public void actionPerformed(ActionEvent e) {1502Object source = e.getSource();1503updateMargins(source);1504}15051506public void focusLost(FocusEvent e) {1507Object source = e.getSource();1508updateMargins(source);1509}15101511public void focusGained(FocusEvent e) {}15121513/* Get the numbers, use to create a MPA.1514* If its valid, accept it and update the attribute set.1515* If its not valid, then reject it and call updateInfo()1516* to re-establish the previous entries.1517*/1518public void updateMargins(Object source) {1519if (!(source instanceof JFormattedTextField)) {1520return;1521} else {1522JFormattedTextField tf = (JFormattedTextField)source;1523Float val = (Float)tf.getValue();1524if (val == null) {1525return;1526}1527if (tf == leftMargin && val.equals(lmObj)) {1528return;1529}1530if (tf == rightMargin && val.equals(rmObj)) {1531return;1532}1533if (tf == topMargin && val.equals(tmObj)) {1534return;1535}1536if (tf == bottomMargin && val.equals(bmObj)) {1537return;1538}1539}15401541Float lmTmpObj = (Float)leftMargin.getValue();1542Float rmTmpObj = (Float)rightMargin.getValue();1543Float tmTmpObj = (Float)topMargin.getValue();1544Float bmTmpObj = (Float)bottomMargin.getValue();15451546float lm = lmTmpObj.floatValue();1547float rm = rmTmpObj.floatValue();1548float tm = tmTmpObj.floatValue();1549float bm = bmTmpObj.floatValue();15501551/* adjust for orientation */1552Class<OrientationRequested> orCategory = OrientationRequested.class;1553OrientationRequested or =1554(OrientationRequested)asCurrent.get(orCategory);15551556if (or == null) {1557or = (OrientationRequested)1558psCurrent.getDefaultAttributeValue(orCategory);1559}15601561float tmp;1562if (or == OrientationRequested.REVERSE_PORTRAIT) {1563tmp = lm; lm = rm; rm = tmp;1564tmp = tm; tm = bm; bm = tmp;1565} else if (or == OrientationRequested.LANDSCAPE) {1566tmp = lm;1567lm = tm;1568tm = rm;1569rm = bm;1570bm = tmp;1571} else if (or == OrientationRequested.REVERSE_LANDSCAPE) {1572tmp = lm;1573lm = bm;1574bm = rm;1575rm = tm;1576tm = tmp;1577}1578MediaPrintableArea mpa;1579if ((mpa = validateMargins(lm, rm, tm, bm)) != null) {1580asCurrent.add(mpa);1581lmVal = lm;1582rmVal = rm;1583tmVal = tm;1584bmVal = bm;1585lmObj = lmTmpObj;1586rmObj = rmTmpObj;1587tmObj = tmTmpObj;1588bmObj = bmTmpObj;1589} else {1590if (lmObj == null || rmObj == null ||1591tmObj == null || bmObj == null) {1592return;1593} else {1594leftMargin.setValue(lmObj);1595rightMargin.setValue(rmObj);1596topMargin.setValue(tmObj);1597bottomMargin.setValue(bmObj);15981599}1600}1601}16021603/*1604* This method either accepts the values and creates a new1605* MediaPrintableArea, or does nothing.1606* It should not attempt to create a printable area from anything1607* other than the exact values passed in.1608* But REMIND/TBD: it would be user friendly to replace margins the1609* user entered but are out of bounds with the minimum.1610* At that point this method will need to take responsibility for1611* updating the "stored" values and the UI.1612*/1613private MediaPrintableArea validateMargins(float lm, float rm,1614float tm, float bm) {16151616Class<MediaPrintableArea> mpaCategory = MediaPrintableArea.class;1617MediaPrintableArea mpa;1618MediaPrintableArea mpaMax = null;1619MediaSize mediaSize = null;16201621Media media = (Media)asCurrent.get(Media.class);1622if (media == null || !(media instanceof MediaSizeName)) {1623media = (Media)psCurrent.getDefaultAttributeValue(Media.class);1624}1625if (media != null && (media instanceof MediaSizeName)) {1626MediaSizeName msn = (MediaSizeName)media;1627mediaSize = MediaSize.getMediaSizeForName(msn);1628}1629if (mediaSize == null) {1630mediaSize = new MediaSize(8.5f, 11f, Size2DSyntax.INCH);1631}16321633if (media != null) {1634PrintRequestAttributeSet tmpASet =1635new HashPrintRequestAttributeSet(asCurrent);1636tmpASet.add(media);16371638Object values =1639psCurrent.getSupportedAttributeValues(mpaCategory,1640docFlavor,1641tmpASet);1642if (values instanceof MediaPrintableArea[] &&1643((MediaPrintableArea[])values).length > 0) {1644mpaMax = ((MediaPrintableArea[])values)[0];16451646}1647}1648if (mpaMax == null) {1649mpaMax = new MediaPrintableArea(0f, 0f,1650mediaSize.getX(units),1651mediaSize.getY(units),1652units);1653}16541655float wid = mediaSize.getX(units);1656float hgt = mediaSize.getY(units);1657float pax = lm;1658float pay = tm;1659float par = rm;1660float pab = bm;1661float paw = wid - lm - rm;1662float pah = hgt - tm - bm;16631664if (paw <= 0f || pah <= 0f || pax < 0f || pay < 0f ||1665par <= 0f || pab <= 0f ||1666pax < mpaMax.getX(units) || paw > mpaMax.getWidth(units) ||1667pay < mpaMax.getY(units) || pah > mpaMax.getHeight(units)) {1668return null;1669} else {1670return new MediaPrintableArea(lm, tm, paw, pah, units);1671}1672}16731674/* This is complex as a MediaPrintableArea is valid only within1675* a particular context of media size.1676* So we need a MediaSize as well as a MediaPrintableArea.1677* MediaSize can be obtained from MediaSizeName.1678* If the application specifies a MediaPrintableArea, we accept it1679* to the extent its valid for the Media they specify. If they1680* don't specify a Media, then the default is assumed.1681*1682* If an application doesn't define a MediaPrintableArea, we need to1683* create a suitable one, this is created using the specified (or1684* default) Media and default 1 inch margins. This is validated1685* against the paper in case this is too large for tiny media.1686*/1687public void updateInfo() {16881689if (isAWT) {1690leftMargin.setEnabled(false);1691rightMargin.setEnabled(false);1692topMargin.setEnabled(false);1693bottomMargin.setEnabled(false);1694lblLeft.setEnabled(false);1695lblRight.setEnabled(false);1696lblTop.setEnabled(false);1697lblBottom.setEnabled(false);1698return;1699}17001701Class<MediaPrintableArea> mpaCategory = MediaPrintableArea.class;1702MediaPrintableArea mpa =1703(MediaPrintableArea)asCurrent.get(mpaCategory);1704MediaPrintableArea mpaMax = null;1705MediaSize mediaSize = null;17061707Media media = (Media)asCurrent.get(Media.class);1708if (media == null || !(media instanceof MediaSizeName)) {1709media = (Media)psCurrent.getDefaultAttributeValue(Media.class);1710}1711if (media != null && (media instanceof MediaSizeName)) {1712MediaSizeName msn = (MediaSizeName)media;1713mediaSize = MediaSize.getMediaSizeForName(msn);1714}1715if (mediaSize == null) {1716mediaSize = new MediaSize(8.5f, 11f, Size2DSyntax.INCH);1717}17181719if (media != null) {1720PrintRequestAttributeSet tmpASet =1721new HashPrintRequestAttributeSet(asCurrent);1722tmpASet.add(media);17231724Object values =1725psCurrent.getSupportedAttributeValues(mpaCategory,1726docFlavor,1727tmpASet);1728if (values instanceof MediaPrintableArea[] &&1729((MediaPrintableArea[])values).length > 0) {1730mpaMax = ((MediaPrintableArea[])values)[0];17311732} else if (values instanceof MediaPrintableArea) {1733mpaMax = (MediaPrintableArea)values;1734}1735}1736if (mpaMax == null) {1737mpaMax = new MediaPrintableArea(0f, 0f,1738mediaSize.getX(units),1739mediaSize.getY(units),1740units);1741}17421743/*1744* At this point we now know as best we can :-1745* - the media size1746* - the maximum corresponding printable area1747* - the media printable area specified by the client, if any.1748* The next step is to create a default MPA if none was specified.1749* 1" margins are used unless they are disproportionately1750* large compared to the size of the media.1751*/17521753float wid = mediaSize.getX(MediaPrintableArea.INCH);1754float hgt = mediaSize.getY(MediaPrintableArea.INCH);1755float maxMarginRatio = 5f;1756float xMgn, yMgn;1757if (wid > maxMarginRatio) {1758xMgn = 1f;1759} else {1760xMgn = wid / maxMarginRatio;1761}1762if (hgt > maxMarginRatio) {1763yMgn = 1f;1764} else {1765yMgn = hgt / maxMarginRatio;1766}17671768if (mpa == null) {1769mpa = new MediaPrintableArea(xMgn, yMgn,1770wid-(2*xMgn), hgt-(2*yMgn),1771MediaPrintableArea.INCH);1772asCurrent.add(mpa);1773}1774float pax = mpa.getX(units);1775float pay = mpa.getY(units);1776float paw = mpa.getWidth(units);1777float pah = mpa.getHeight(units);1778float paxMax = mpaMax.getX(units);1779float payMax = mpaMax.getY(units);1780float pawMax = mpaMax.getWidth(units);1781float pahMax = mpaMax.getHeight(units);178217831784boolean invalid = false;17851786// If the paper is set to something which is too small to1787// accommodate a specified printable area, perhaps carried1788// over from a larger paper, the adjustment that needs to be1789// performed should seem the most natural from a user's viewpoint.1790// Since the user is specifying margins, then we are biased1791// towards keeping the margins as close to what is specified as1792// possible, shrinking or growing the printable area.1793// But the API uses printable area, so you need to know the1794// media size in which the margins were previously interpreted,1795// or at least have a record of the margins.1796// In the case that this is the creation of this UI we do not1797// have this record, so we are somewhat reliant on the client1798// to supply a reasonable default1799wid = mediaSize.getX(units);1800hgt = mediaSize.getY(units);1801if (lmVal >= 0f) {1802invalid = true;18031804if (lmVal + rmVal > wid) {1805// margins impossible, but maintain P.A if can1806if (paw > pawMax) {1807paw = pawMax;1808}1809// try to centre the printable area.1810pax = (wid - paw)/2f;1811} else {1812pax = (lmVal >= paxMax) ? lmVal : paxMax;1813paw = wid - pax - rmVal;1814}1815if (tmVal + bmVal > hgt) {1816if (pah > pahMax) {1817pah = pahMax;1818}1819pay = (hgt - pah)/2f;1820} else {1821pay = (tmVal >= payMax) ? tmVal : payMax;1822pah = hgt - pay - bmVal;1823}1824}1825if (pax < paxMax) {1826invalid = true;1827pax = paxMax;1828}1829if (pay < payMax) {1830invalid = true;1831pay = payMax;1832}1833if (paw > pawMax) {1834invalid = true;1835paw = pawMax;1836}1837if (pah > pahMax) {1838invalid = true;1839pah = pahMax;1840}18411842if ((pax + paw > paxMax + pawMax) || (paw <= 0f)) {1843invalid = true;1844pax = paxMax;1845paw = pawMax;1846}1847if ((pay + pah > payMax + pahMax) || (pah <= 0f)) {1848invalid = true;1849pay = payMax;1850pah = pahMax;1851}18521853if (invalid) {1854mpa = new MediaPrintableArea(pax, pay, paw, pah, units);1855asCurrent.add(mpa);1856}18571858/* We now have a valid printable area.1859* Turn it into margins, using the mediaSize1860*/1861lmVal = pax;1862tmVal = pay;1863rmVal = mediaSize.getX(units) - pax - paw;1864bmVal = mediaSize.getY(units) - pay - pah;18651866lmObj = lmVal;1867rmObj = rmVal;1868tmObj = tmVal;1869bmObj = bmVal;18701871/* Now we know the values to use, we need to assign them1872* to the fields appropriate for the orientation.1873* Note: if orientation changes this method must be called.1874*/1875Class<OrientationRequested> orCategory = OrientationRequested.class;1876OrientationRequested or =1877(OrientationRequested)asCurrent.get(orCategory);18781879if (or == null) {1880or = (OrientationRequested)1881psCurrent.getDefaultAttributeValue(orCategory);1882}18831884Float tmp;18851886if (or == OrientationRequested.REVERSE_PORTRAIT) {1887tmp = lmObj; lmObj = rmObj; rmObj = tmp;1888tmp = tmObj; tmObj = bmObj; bmObj = tmp;1889} else if (or == OrientationRequested.LANDSCAPE) {1890tmp = lmObj;1891lmObj = bmObj;1892bmObj = rmObj;1893rmObj = tmObj;1894tmObj = tmp;1895} else if (or == OrientationRequested.REVERSE_LANDSCAPE) {1896tmp = lmObj;1897lmObj = tmObj;1898tmObj = rmObj;1899rmObj = bmObj;1900bmObj = tmp;1901}19021903leftMargin.setValue(lmObj);1904rightMargin.setValue(rmObj);1905topMargin.setValue(tmObj);1906bottomMargin.setValue(bmObj);1907}1908}19091910@SuppressWarnings("serial") // Superclass is not serializable across versions1911private class MediaPanel extends JPanel implements ItemListener {19121913private final String strTitle = getMsg("border.media");1914private JLabel lblSize, lblSource;1915private JComboBox<Object> cbSize, cbSource;1916private Vector<MediaSizeName> sizes = new Vector<>();1917private Vector<MediaTray> sources = new Vector<>();1918private MarginsPanel pnlMargins = null;19191920public MediaPanel() {1921super();19221923GridBagLayout gridbag = new GridBagLayout();1924GridBagConstraints c = new GridBagConstraints();19251926setLayout(gridbag);1927setBorder(BorderFactory.createTitledBorder(strTitle));19281929cbSize = new JComboBox<>();1930cbSource = new JComboBox<>();19311932c.fill = GridBagConstraints.BOTH;1933c.insets = compInsets;1934c.weighty = 1.0;19351936c.weightx = 0.0;1937lblSize = new JLabel(getMsg("label.size"), JLabel.TRAILING);1938lblSize.setDisplayedMnemonic(getMnemonic("label.size"));1939lblSize.setLabelFor(cbSize);1940addToGB(lblSize, this, gridbag, c);1941c.weightx = 1.0;1942c.gridwidth = GridBagConstraints.REMAINDER;1943addToGB(cbSize, this, gridbag, c);19441945c.weightx = 0.0;1946c.gridwidth = 1;1947lblSource = new JLabel(getMsg("label.source"), JLabel.TRAILING);1948lblSource.setDisplayedMnemonic(getMnemonic("label.source"));1949lblSource.setLabelFor(cbSource);1950addToGB(lblSource, this, gridbag, c);1951c.gridwidth = GridBagConstraints.REMAINDER;1952addToGB(cbSource, this, gridbag, c);1953}19541955private String getMediaName(String key) {1956try {1957// replace characters that would be invalid in1958// a resource key with valid characters1959String newkey = key.replace(' ', '-');1960newkey = newkey.replace('#', 'n');19611962return messageRB.getString(newkey);1963} catch (java.util.MissingResourceException e) {1964return key;1965}1966}19671968public void itemStateChanged(ItemEvent e) {1969Object source = e.getSource();19701971if (e.getStateChange() == ItemEvent.SELECTED) {1972if (source == cbSize) {1973int index = cbSize.getSelectedIndex();19741975if ((index >= 0) && (index < sizes.size())) {1976if ((cbSource.getItemCount() > 1) &&1977(cbSource.getSelectedIndex() >= 1))1978{1979int src = cbSource.getSelectedIndex() - 1;1980MediaTray mt = sources.get(src);1981asCurrent.add(new SunAlternateMedia(mt));1982}1983asCurrent.add(sizes.get(index));1984}1985} else if (source == cbSource) {1986int index = cbSource.getSelectedIndex();19871988if ((index >= 1) && (index < (sources.size() + 1))) {1989asCurrent.remove(SunAlternateMedia.class);1990MediaTray newTray = sources.get(index - 1);1991Media m = (Media)asCurrent.get(Media.class);1992if (m == null || m instanceof MediaTray) {1993asCurrent.add(newTray);1994} else if (m instanceof MediaSizeName) {1995MediaSizeName msn = (MediaSizeName)m;1996Media def = (Media)psCurrent.getDefaultAttributeValue(Media.class);1997if (def instanceof MediaSizeName && def.equals(msn)) {1998asCurrent.add(newTray);1999} else {2000/* Non-default paper size, so need to store tray2001* as SunAlternateMedia2002*/2003asCurrent.add(new SunAlternateMedia(newTray));2004}2005}2006} else if (index == 0) {2007asCurrent.remove(SunAlternateMedia.class);2008if (cbSize.getItemCount() > 0) {2009int size = cbSize.getSelectedIndex();2010asCurrent.add(sizes.get(size));2011}2012}2013}2014// orientation affects display of margins.2015if (pnlMargins != null) {2016pnlMargins.updateInfo();2017}2018}2019}202020212022/* this is ad hoc to keep things simple */2023public void addMediaListener(MarginsPanel pnl) {2024pnlMargins = pnl;2025}2026public void updateInfo() {2027Class<Media> mdCategory = Media.class;2028Class<SunAlternateMedia> amCategory = SunAlternateMedia.class;2029boolean mediaSupported = false;20302031cbSize.removeItemListener(this);2032cbSize.removeAllItems();2033cbSource.removeItemListener(this);2034cbSource.removeAllItems();2035cbSource.addItem(getMediaName("auto-select"));20362037sizes.clear();2038sources.clear();20392040if (psCurrent.isAttributeCategorySupported(mdCategory)) {2041mediaSupported = true;20422043Object values =2044psCurrent.getSupportedAttributeValues(mdCategory,2045docFlavor,2046asCurrent);20472048if (values instanceof Media[]) {2049Media[] media = (Media[])values;20502051for (int i = 0; i < media.length; i++) {2052Media medium = media[i];20532054if (medium instanceof MediaSizeName) {2055sizes.add((MediaSizeName)medium);2056cbSize.addItem(getMediaName(medium.toString()));2057} else if (medium instanceof MediaTray) {2058sources.add((MediaTray)medium);2059cbSource.addItem(getMediaName(medium.toString()));2060}2061}2062}2063}20642065boolean msSupported = (mediaSupported && (sizes.size() > 0));2066lblSize.setEnabled(msSupported);2067cbSize.setEnabled(msSupported);20682069if (isAWT) {2070cbSource.setEnabled(false);2071lblSource.setEnabled(false);2072} else {2073cbSource.setEnabled(mediaSupported);2074}20752076if (mediaSupported) {20772078Media medium = (Media)asCurrent.get(mdCategory);20792080// initialize size selection to default2081Media defMedia = (Media)psCurrent.getDefaultAttributeValue(mdCategory);2082if (defMedia instanceof MediaSizeName) {2083cbSize.setSelectedIndex(sizes.size() > 0 ? sizes.indexOf(defMedia) : -1);2084}20852086if (medium == null ||2087!psCurrent.isAttributeValueSupported(medium,2088docFlavor, asCurrent)) {20892090medium = defMedia;20912092if (medium == null) {2093if (sizes.size() > 0) {2094medium = (Media)sizes.get(0);2095}2096}2097if (medium != null) {2098asCurrent.add(medium);2099}2100}2101if (medium != null) {2102if (medium instanceof MediaSizeName) {2103MediaSizeName ms = (MediaSizeName)medium;2104cbSize.setSelectedIndex(sizes.indexOf(ms));2105} else if (medium instanceof MediaTray) {2106MediaTray mt = (MediaTray)medium;2107cbSource.setSelectedIndex(sources.indexOf(mt) + 1);2108}2109} else {2110cbSize.setSelectedIndex(sizes.size() > 0 ? 0 : -1);2111cbSource.setSelectedIndex(0);2112}21132114SunAlternateMedia alt = (SunAlternateMedia)asCurrent.get(amCategory);2115if (alt != null) {2116Media md = alt.getMedia();2117if (md instanceof MediaTray) {2118MediaTray mt = (MediaTray)md;2119cbSource.setSelectedIndex(sources.indexOf(mt) + 1);2120}2121}21222123int selIndex = cbSize.getSelectedIndex();2124if ((selIndex >= 0) && (selIndex < sizes.size())) {2125asCurrent.add(sizes.get(selIndex));2126}21272128selIndex = cbSource.getSelectedIndex();2129if ((selIndex >= 1) && (selIndex < (sources.size()+1))) {2130MediaTray mt = sources.get(selIndex-1);2131if (medium instanceof MediaTray) {2132asCurrent.add(mt);2133} else {2134asCurrent.add(new SunAlternateMedia(mt));2135}2136}213721382139}2140cbSize.addItemListener(this);2141cbSource.addItemListener(this);2142}2143}21442145@SuppressWarnings("serial") // Superclass is not serializable across versions2146private class OrientationPanel extends JPanel2147implements ActionListener2148{2149private final String strTitle = getMsg("border.orientation");2150private IconRadioButton rbPortrait, rbLandscape,2151rbRevPortrait, rbRevLandscape;2152private MarginsPanel pnlMargins = null;21532154public OrientationPanel() {2155super();21562157GridBagLayout gridbag = new GridBagLayout();2158GridBagConstraints c = new GridBagConstraints();21592160setLayout(gridbag);2161setBorder(BorderFactory.createTitledBorder(strTitle));21622163c.fill = GridBagConstraints.BOTH;2164c.insets = compInsets;2165c.weighty = 1.0;2166c.gridwidth = GridBagConstraints.REMAINDER;21672168ButtonGroup bg = new ButtonGroup();2169rbPortrait = new IconRadioButton("radiobutton.portrait",2170"orientPortrait.png", true,2171bg, this);2172rbPortrait.addActionListener(this);2173addToGB(rbPortrait, this, gridbag, c);2174rbLandscape = new IconRadioButton("radiobutton.landscape",2175"orientLandscape.png", false,2176bg, this);2177rbLandscape.addActionListener(this);2178addToGB(rbLandscape, this, gridbag, c);2179rbRevPortrait = new IconRadioButton("radiobutton.revportrait",2180"orientRevPortrait.png", false,2181bg, this);2182rbRevPortrait.addActionListener(this);2183addToGB(rbRevPortrait, this, gridbag, c);2184rbRevLandscape = new IconRadioButton("radiobutton.revlandscape",2185"orientRevLandscape.png", false,2186bg, this);2187rbRevLandscape.addActionListener(this);2188addToGB(rbRevLandscape, this, gridbag, c);2189}21902191public void actionPerformed(ActionEvent e) {2192Object source = e.getSource();21932194if (rbPortrait.isSameAs(source)) {2195asCurrent.add(OrientationRequested.PORTRAIT);2196} else if (rbLandscape.isSameAs(source)) {2197asCurrent.add(OrientationRequested.LANDSCAPE);2198} else if (rbRevPortrait.isSameAs(source)) {2199asCurrent.add(OrientationRequested.REVERSE_PORTRAIT);2200} else if (rbRevLandscape.isSameAs(source)) {2201asCurrent.add(OrientationRequested.REVERSE_LANDSCAPE);2202}2203// orientation affects display of margins.2204if (pnlMargins != null) {2205pnlMargins.updateInfo();2206}2207}22082209/* This is ad hoc to keep things simple */2210void addOrientationListener(MarginsPanel pnl) {2211pnlMargins = pnl;2212}22132214public void updateInfo() {2215Class<OrientationRequested> orCategory = OrientationRequested.class;2216boolean pSupported = false;2217boolean lSupported = false;2218boolean rpSupported = false;2219boolean rlSupported = false;22202221if (isAWT) {2222pSupported = true;2223lSupported = true;2224} else2225if (psCurrent.isAttributeCategorySupported(orCategory)) {2226Object values =2227psCurrent.getSupportedAttributeValues(orCategory,2228docFlavor,2229asCurrent);22302231if (values instanceof OrientationRequested[]) {2232OrientationRequested[] ovalues =2233(OrientationRequested[])values;22342235for (int i = 0; i < ovalues.length; i++) {2236OrientationRequested value = ovalues[i];22372238if (value == OrientationRequested.PORTRAIT) {2239pSupported = true;2240} else if (value == OrientationRequested.LANDSCAPE) {2241lSupported = true;2242} else if (value == OrientationRequested.REVERSE_PORTRAIT) {2243rpSupported = true;2244} else if (value == OrientationRequested.REVERSE_LANDSCAPE) {2245rlSupported = true;2246}2247}2248}2249}225022512252rbPortrait.setEnabled(pSupported);2253rbLandscape.setEnabled(lSupported);2254rbRevPortrait.setEnabled(rpSupported);2255rbRevLandscape.setEnabled(rlSupported);22562257OrientationRequested or = (OrientationRequested)asCurrent.get(orCategory);2258if (or == null ||2259!psCurrent.isAttributeValueSupported(or, docFlavor, asCurrent)) {22602261or = (OrientationRequested)psCurrent.getDefaultAttributeValue(orCategory);2262// need to validate if default is not supported2263if ((or != null) &&2264!psCurrent.isAttributeValueSupported(or, docFlavor, asCurrent)) {2265or = null;2266Object values =2267psCurrent.getSupportedAttributeValues(orCategory,2268docFlavor,2269asCurrent);2270if (values instanceof OrientationRequested[]) {2271OrientationRequested[] orValues =2272(OrientationRequested[])values;2273if (orValues.length > 1) {2274// get the first in the list2275or = orValues[0];2276}2277}2278}22792280if (or == null) {2281or = OrientationRequested.PORTRAIT;2282}2283asCurrent.add(or);2284}22852286if (or == OrientationRequested.PORTRAIT) {2287rbPortrait.setSelected(true);2288} else if (or == OrientationRequested.LANDSCAPE) {2289rbLandscape.setSelected(true);2290} else if (or == OrientationRequested.REVERSE_PORTRAIT) {2291rbRevPortrait.setSelected(true);2292} else { // if (or == OrientationRequested.REVERSE_LANDSCAPE)2293rbRevLandscape.setSelected(true);2294}2295}2296}2297229822992300/**2301* The "Appearance" tab. Includes the controls for Chromaticity,2302* PrintQuality, JobPriority, JobName, and other related job attributes.2303*/2304@SuppressWarnings("serial") // Superclass is not serializable across versions2305private class AppearancePanel extends JPanel {23062307private ChromaticityPanel pnlChromaticity;2308private QualityPanel pnlQuality;2309private JobAttributesPanel pnlJobAttributes;2310private SidesPanel pnlSides;23112312public AppearancePanel() {2313super();23142315GridBagLayout gridbag = new GridBagLayout();2316GridBagConstraints c = new GridBagConstraints();23172318setLayout(gridbag);23192320c.fill = GridBagConstraints.BOTH;2321c.insets = panelInsets;2322c.weightx = 1.0;2323c.weighty = 1.0;23242325c.gridwidth = GridBagConstraints.RELATIVE;2326pnlChromaticity = new ChromaticityPanel();2327addToGB(pnlChromaticity, this, gridbag, c);23282329c.gridwidth = GridBagConstraints.REMAINDER;2330pnlQuality = new QualityPanel();2331addToGB(pnlQuality, this, gridbag, c);23322333c.gridwidth = 1;2334pnlSides = new SidesPanel();2335addToGB(pnlSides, this, gridbag, c);23362337c.gridwidth = GridBagConstraints.REMAINDER;2338pnlJobAttributes = new JobAttributesPanel();2339addToGB(pnlJobAttributes, this, gridbag, c);23402341}23422343public void updateInfo() {2344pnlChromaticity.updateInfo();2345pnlQuality.updateInfo();2346pnlSides.updateInfo();2347pnlJobAttributes.updateInfo();2348}2349}23502351@SuppressWarnings("serial") // Superclass is not serializable across versions2352private class ChromaticityPanel extends JPanel2353implements ActionListener2354{2355private final String strTitle = getMsg("border.chromaticity");2356private JRadioButton rbMonochrome, rbColor;23572358public ChromaticityPanel() {2359super();23602361GridBagLayout gridbag = new GridBagLayout();2362GridBagConstraints c = new GridBagConstraints();23632364setLayout(gridbag);2365setBorder(BorderFactory.createTitledBorder(strTitle));23662367c.fill = GridBagConstraints.BOTH;2368c.gridwidth = GridBagConstraints.REMAINDER;2369c.weighty = 1.0;23702371ButtonGroup bg = new ButtonGroup();2372rbMonochrome = createRadioButton("radiobutton.monochrome", this);2373rbMonochrome.setSelected(true);2374bg.add(rbMonochrome);2375addToGB(rbMonochrome, this, gridbag, c);2376rbColor = createRadioButton("radiobutton.color", this);2377bg.add(rbColor);2378addToGB(rbColor, this, gridbag, c);2379}23802381public void actionPerformed(ActionEvent e) {2382Object source = e.getSource();23832384// REMIND: use isSameAs if we move to a IconRB in the future2385if (source == rbMonochrome) {2386asCurrent.add(Chromaticity.MONOCHROME);2387} else if (source == rbColor) {2388asCurrent.add(Chromaticity.COLOR);2389}2390}23912392public void updateInfo() {2393Class<Chromaticity> chCategory = Chromaticity.class;2394boolean monoSupported = false;2395boolean colorSupported = false;23962397if (isAWT) {2398monoSupported = true;2399colorSupported = true;2400} else2401if (psCurrent.isAttributeCategorySupported(chCategory)) {2402Object values =2403psCurrent.getSupportedAttributeValues(chCategory,2404docFlavor,2405asCurrent);24062407if (values instanceof Chromaticity[]) {2408Chromaticity[] cvalues = (Chromaticity[])values;24092410for (int i = 0; i < cvalues.length; i++) {2411Chromaticity value = cvalues[i];24122413if (value == Chromaticity.MONOCHROME) {2414monoSupported = true;2415} else if (value == Chromaticity.COLOR) {2416colorSupported = true;2417}2418}2419}2420}242124222423rbMonochrome.setEnabled(monoSupported);2424rbColor.setEnabled(colorSupported);24252426Chromaticity ch = (Chromaticity)asCurrent.get(chCategory);2427if (ch == null) {2428ch = (Chromaticity)psCurrent.getDefaultAttributeValue(chCategory);2429if (ch == null) {2430ch = Chromaticity.MONOCHROME;2431}2432}24332434if (ch == Chromaticity.MONOCHROME) {2435rbMonochrome.setSelected(true);2436} else { // if (ch == Chromaticity.COLOR)2437rbColor.setSelected(true);2438}2439}2440}24412442@SuppressWarnings("serial") // Superclass is not serializable across versions2443private class QualityPanel extends JPanel2444implements ActionListener2445{2446private final String strTitle = getMsg("border.quality");2447private JRadioButton rbDraft, rbNormal, rbHigh;24482449public QualityPanel() {2450super();24512452GridBagLayout gridbag = new GridBagLayout();2453GridBagConstraints c = new GridBagConstraints();24542455setLayout(gridbag);2456setBorder(BorderFactory.createTitledBorder(strTitle));24572458c.fill = GridBagConstraints.BOTH;2459c.gridwidth = GridBagConstraints.REMAINDER;2460c.weighty = 1.0;24612462ButtonGroup bg = new ButtonGroup();2463rbDraft = createRadioButton("radiobutton.draftq", this);2464bg.add(rbDraft);2465addToGB(rbDraft, this, gridbag, c);2466rbNormal = createRadioButton("radiobutton.normalq", this);2467rbNormal.setSelected(true);2468bg.add(rbNormal);2469addToGB(rbNormal, this, gridbag, c);2470rbHigh = createRadioButton("radiobutton.highq", this);2471bg.add(rbHigh);2472addToGB(rbHigh, this, gridbag, c);2473}24742475public void actionPerformed(ActionEvent e) {2476Object source = e.getSource();24772478if (source == rbDraft) {2479asCurrent.add(PrintQuality.DRAFT);2480} else if (source == rbNormal) {2481asCurrent.add(PrintQuality.NORMAL);2482} else if (source == rbHigh) {2483asCurrent.add(PrintQuality.HIGH);2484}2485}24862487public void updateInfo() {2488Class<PrintQuality> pqCategory = PrintQuality.class;2489boolean draftSupported = false;2490boolean normalSupported = false;2491boolean highSupported = false;24922493if (isAWT) {2494draftSupported = true;2495normalSupported = true;2496highSupported = true;2497} else2498if (psCurrent.isAttributeCategorySupported(pqCategory)) {2499Object values =2500psCurrent.getSupportedAttributeValues(pqCategory,2501docFlavor,2502asCurrent);25032504if (values instanceof PrintQuality[]) {2505PrintQuality[] qvalues = (PrintQuality[])values;25062507for (int i = 0; i < qvalues.length; i++) {2508PrintQuality value = qvalues[i];25092510if (value == PrintQuality.DRAFT) {2511draftSupported = true;2512} else if (value == PrintQuality.NORMAL) {2513normalSupported = true;2514} else if (value == PrintQuality.HIGH) {2515highSupported = true;2516}2517}2518}2519}25202521rbDraft.setEnabled(draftSupported);2522rbNormal.setEnabled(normalSupported);2523rbHigh.setEnabled(highSupported);25242525PrintQuality pq = (PrintQuality)asCurrent.get(pqCategory);2526if (pq == null) {2527pq = (PrintQuality)psCurrent.getDefaultAttributeValue(pqCategory);2528if (pq == null) {2529pq = PrintQuality.NORMAL;2530}2531}25322533if (pq == PrintQuality.DRAFT) {2534rbDraft.setSelected(true);2535} else if (pq == PrintQuality.NORMAL) {2536rbNormal.setSelected(true);2537} else { // if (pq == PrintQuality.HIGH)2538rbHigh.setSelected(true);2539}2540}254125422543}25442545@SuppressWarnings("serial") // Superclass is not serializable across versions2546private class SidesPanel extends JPanel2547implements ActionListener2548{2549private final String strTitle = getMsg("border.sides");2550private IconRadioButton rbOneSide, rbTumble, rbDuplex;25512552public SidesPanel() {2553super();25542555GridBagLayout gridbag = new GridBagLayout();2556GridBagConstraints c = new GridBagConstraints();25572558setLayout(gridbag);2559setBorder(BorderFactory.createTitledBorder(strTitle));25602561c.fill = GridBagConstraints.BOTH;2562c.insets = compInsets;2563c.weighty = 1.0;2564c.gridwidth = GridBagConstraints.REMAINDER;25652566ButtonGroup bg = new ButtonGroup();2567rbOneSide = new IconRadioButton("radiobutton.oneside",2568"oneside.png", true,2569bg, this);2570rbOneSide.addActionListener(this);2571addToGB(rbOneSide, this, gridbag, c);2572rbTumble = new IconRadioButton("radiobutton.tumble",2573"tumble.png", false,2574bg, this);2575rbTumble.addActionListener(this);2576addToGB(rbTumble, this, gridbag, c);2577rbDuplex = new IconRadioButton("radiobutton.duplex",2578"duplex.png", false,2579bg, this);2580rbDuplex.addActionListener(this);2581c.gridwidth = GridBagConstraints.REMAINDER;2582addToGB(rbDuplex, this, gridbag, c);2583}25842585public void actionPerformed(ActionEvent e) {2586Object source = e.getSource();25872588if (rbOneSide.isSameAs(source)) {2589asCurrent.add(Sides.ONE_SIDED);2590} else if (rbTumble.isSameAs(source)) {2591asCurrent.add(Sides.TUMBLE);2592} else if (rbDuplex.isSameAs(source)) {2593asCurrent.add(Sides.DUPLEX);2594}2595}25962597public void updateInfo() {2598Class<Sides> sdCategory = Sides.class;2599boolean osSupported = false;2600boolean tSupported = false;2601boolean dSupported = false;26022603if (psCurrent.isAttributeCategorySupported(sdCategory)) {2604Object values =2605psCurrent.getSupportedAttributeValues(sdCategory,2606docFlavor,2607asCurrent);26082609if (values instanceof Sides[]) {2610Sides[] svalues = (Sides[])values;26112612for (int i = 0; i < svalues.length; i++) {2613Sides value = svalues[i];26142615if (value == Sides.ONE_SIDED) {2616osSupported = true;2617} else if (value == Sides.TUMBLE) {2618tSupported = true;2619} else if (value == Sides.DUPLEX) {2620dSupported = true;2621}2622}2623}2624}2625rbOneSide.setEnabled(osSupported);2626rbTumble.setEnabled(tSupported);2627rbDuplex.setEnabled(dSupported);26282629Sides sd = (Sides)asCurrent.get(sdCategory);2630if (sd == null) {2631sd = (Sides)psCurrent.getDefaultAttributeValue(sdCategory);2632if (sd == null) {2633sd = Sides.ONE_SIDED;2634}2635}26362637if (sd == Sides.ONE_SIDED) {2638rbOneSide.setSelected(true);2639} else if (sd == Sides.TUMBLE) {2640rbTumble.setSelected(true);2641} else { // if (sd == Sides.DUPLEX)2642rbDuplex.setSelected(true);2643}2644}2645}264626472648@SuppressWarnings("serial") // Superclass is not serializable across versions2649private class JobAttributesPanel extends JPanel2650implements ActionListener, ChangeListener, FocusListener2651{2652private final String strTitle = getMsg("border.jobattributes");2653private JLabel lblPriority, lblJobName, lblUserName;2654private JSpinner spinPriority;2655private SpinnerNumberModel snModel;2656private JCheckBox cbJobSheets;2657private JTextField tfJobName, tfUserName;26582659public JobAttributesPanel() {2660super();26612662GridBagLayout gridbag = new GridBagLayout();2663GridBagConstraints c = new GridBagConstraints();26642665setLayout(gridbag);2666setBorder(BorderFactory.createTitledBorder(strTitle));26672668c.fill = GridBagConstraints.NONE;2669c.insets = compInsets;2670c.weighty = 1.0;26712672cbJobSheets = createCheckBox("checkbox.jobsheets", this);2673c.anchor = GridBagConstraints.LINE_START;2674addToGB(cbJobSheets, this, gridbag, c);26752676JPanel pnlTop = new JPanel();2677lblPriority = new JLabel(getMsg("label.priority"), JLabel.TRAILING);2678lblPriority.setDisplayedMnemonic(getMnemonic("label.priority"));26792680pnlTop.add(lblPriority);2681snModel = new SpinnerNumberModel(1, 1, 100, 1);2682spinPriority = new JSpinner(snModel);2683lblPriority.setLabelFor(spinPriority);2684// REMIND2685((JSpinner.NumberEditor)spinPriority.getEditor()).getTextField().setColumns(3);2686spinPriority.addChangeListener(this);2687pnlTop.add(spinPriority);2688c.anchor = GridBagConstraints.LINE_END;2689c.gridwidth = GridBagConstraints.REMAINDER;2690pnlTop.getAccessibleContext().setAccessibleName(2691getMsg("label.priority"));2692addToGB(pnlTop, this, gridbag, c);26932694c.fill = GridBagConstraints.HORIZONTAL;2695c.anchor = GridBagConstraints.CENTER;2696c.weightx = 0.0;2697c.gridwidth = 1;2698char jmnemonic = getMnemonic("label.jobname");2699lblJobName = new JLabel(getMsg("label.jobname"), JLabel.TRAILING);2700lblJobName.setDisplayedMnemonic(jmnemonic);2701addToGB(lblJobName, this, gridbag, c);2702c.weightx = 1.0;2703c.gridwidth = GridBagConstraints.REMAINDER;2704tfJobName = new JTextField();2705lblJobName.setLabelFor(tfJobName);2706tfJobName.addFocusListener(this);2707tfJobName.setFocusAccelerator(jmnemonic);2708tfJobName.getAccessibleContext().setAccessibleName(2709getMsg("label.jobname"));2710addToGB(tfJobName, this, gridbag, c);27112712c.weightx = 0.0;2713c.gridwidth = 1;2714char umnemonic = getMnemonic("label.username");2715lblUserName = new JLabel(getMsg("label.username"), JLabel.TRAILING);2716lblUserName.setDisplayedMnemonic(umnemonic);2717addToGB(lblUserName, this, gridbag, c);2718c.gridwidth = GridBagConstraints.REMAINDER;2719tfUserName = new JTextField();2720lblUserName.setLabelFor(tfUserName);2721tfUserName.addFocusListener(this);2722tfUserName.setFocusAccelerator(umnemonic);2723tfUserName.getAccessibleContext().setAccessibleName(2724getMsg("label.username"));2725addToGB(tfUserName, this, gridbag, c);2726}27272728public void actionPerformed(ActionEvent e) {2729if (cbJobSheets.isSelected()) {2730asCurrent.add(JobSheets.STANDARD);2731} else {2732asCurrent.add(JobSheets.NONE);2733}2734}27352736public void stateChanged(ChangeEvent e) {2737asCurrent.add(new JobPriority(snModel.getNumber().intValue()));2738}27392740public void focusLost(FocusEvent e) {2741Object source = e.getSource();27422743if (source == tfJobName) {2744asCurrent.add(new JobName(tfJobName.getText(),2745Locale.getDefault()));2746} else if (source == tfUserName) {2747asCurrent.add(new RequestingUserName(tfUserName.getText(),2748Locale.getDefault()));2749}2750}27512752public void focusGained(FocusEvent e) {}27532754public void updateInfo() {2755Class<JobSheets> jsCategory = JobSheets.class;2756Class<JobPriority> jpCategory = JobPriority.class;2757Class<JobName> jnCategory = JobName.class;2758Class<RequestingUserName> unCategory = RequestingUserName.class;2759boolean jsSupported = false;2760boolean jpSupported = false;2761boolean jnSupported = false;2762boolean unSupported = false;27632764// setup JobSheets checkbox2765if (psCurrent.isAttributeCategorySupported(jsCategory)) {2766jsSupported = true;2767}2768JobSheets js = (JobSheets)asCurrent.get(jsCategory);2769if (js == null) {2770js = (JobSheets)psCurrent.getDefaultAttributeValue(jsCategory);2771if (js == null) {2772js = JobSheets.STANDARD;2773}2774}2775cbJobSheets.setSelected(js != JobSheets.NONE && jsSupported);2776cbJobSheets.setEnabled(jsSupported);27772778// setup JobPriority spinner2779if (!isAWT && psCurrent.isAttributeCategorySupported(jpCategory)) {2780jpSupported = true;2781}2782JobPriority jp = (JobPriority)asCurrent.get(jpCategory);2783if (jp == null) {2784jp = (JobPriority)psCurrent.getDefaultAttributeValue(jpCategory);2785if (jp == null) {2786jp = new JobPriority(1);2787}2788}2789int value = jp.getValue();2790if ((value < 1) || (value > 100)) {2791value = 1;2792}2793snModel.setValue(value);2794lblPriority.setEnabled(jpSupported);2795spinPriority.setEnabled(jpSupported);27962797// setup JobName text field2798if (psCurrent.isAttributeCategorySupported(jnCategory)) {2799jnSupported = true;2800}2801JobName jn = (JobName)asCurrent.get(jnCategory);2802if (jn == null) {2803jn = (JobName)psCurrent.getDefaultAttributeValue(jnCategory);2804if (jn == null) {2805jn = new JobName("", Locale.getDefault());2806}2807}2808tfJobName.setText(jn.getValue());2809tfJobName.setEnabled(jnSupported);2810lblJobName.setEnabled(jnSupported);28112812// setup RequestingUserName text field2813if (!isAWT && psCurrent.isAttributeCategorySupported(unCategory)) {2814unSupported = true;2815}2816RequestingUserName un = (RequestingUserName)asCurrent.get(unCategory);2817if (un == null) {2818un = (RequestingUserName)psCurrent.getDefaultAttributeValue(unCategory);2819if (un == null) {2820un = new RequestingUserName("", Locale.getDefault());2821}2822}2823tfUserName.setText(un.getValue());2824tfUserName.setEnabled(unSupported);2825lblUserName.setEnabled(unSupported);2826}2827}28282829283028312832/**2833* A special widget that groups a JRadioButton with an associated icon,2834* placed to the left of the radio button.2835*/2836@SuppressWarnings("serial") // Superclass is not serializable across versions2837private class IconRadioButton extends JPanel {28382839private JRadioButton rb;2840private JLabel lbl;28412842public IconRadioButton(String key, String img, boolean selected,2843ButtonGroup bg, ActionListener al)2844{2845super(new FlowLayout(FlowLayout.LEADING));2846final URL imgURL = getImageResource(img);2847@SuppressWarnings("removal")2848Icon icon = java.security.AccessController.doPrivileged(2849new java.security.PrivilegedAction<Icon>() {2850public Icon run() {2851Icon icon = new ImageIcon(imgURL);2852return icon;2853}2854});2855lbl = new JLabel(icon);2856add(lbl);28572858rb = createRadioButton(key, al);2859rb.setSelected(selected);2860addToBG(rb, this, bg);2861}28622863public void addActionListener(ActionListener al) {2864rb.addActionListener(al);2865}28662867public boolean isSameAs(Object source) {2868return (rb == source);2869}28702871public void setEnabled(boolean enabled) {2872rb.setEnabled(enabled);2873lbl.setEnabled(enabled);2874}28752876public boolean isSelected() {2877return rb.isSelected();2878}28792880public void setSelected(boolean selected) {2881rb.setSelected(selected);2882}2883}28842885/**2886* Similar in functionality to the default JFileChooser, except this2887* chooser will pop up a "Do you want to overwrite..." dialog if the2888* user selects a file that already exists.2889*/2890@SuppressWarnings("serial") // JDK implementation class2891private class ValidatingFileChooser extends JFileChooser {2892public void approveSelection() {2893File selected = getSelectedFile();2894boolean exists;28952896try {2897exists = selected.exists();2898} catch (SecurityException e) {2899exists = false;2900}29012902if (exists) {2903int val;2904val = JOptionPane.showConfirmDialog(this,2905getMsg("dialog.overwrite"),2906getMsg("dialog.owtitle"),2907JOptionPane.YES_NO_OPTION);2908if (val != JOptionPane.YES_OPTION) {2909return;2910}2911}29122913try {2914if (selected.createNewFile()) {2915selected.delete();2916}2917} catch (IOException ioe) {2918JOptionPane.showMessageDialog(this,2919getMsg("dialog.writeerror")+" "+selected,2920getMsg("dialog.owtitle"),2921JOptionPane.WARNING_MESSAGE);2922return;2923} catch (SecurityException se) {2924//There is already file read/write access so at this point2925// only delete access is denied. Just ignore it because in2926// most cases the file created in createNewFile gets2927// overwritten anyway.2928}2929File pFile = selected.getParentFile();2930if ((selected.exists() &&2931(!selected.isFile() || !selected.canWrite())) ||2932((pFile != null) &&2933(!pFile.exists() || (pFile.exists() && !pFile.canWrite())))) {2934JOptionPane.showMessageDialog(this,2935getMsg("dialog.writeerror")+" "+selected,2936getMsg("dialog.owtitle"),2937JOptionPane.WARNING_MESSAGE);2938return;2939}29402941super.approveSelection();2942}2943}2944}294529462947