1 /*
2  * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package sun.print;
27 
28 import java.awt.BorderLayout;
29 import java.awt.Color;
30 import java.awt.Component;
31 import java.awt.Container;
32 import java.awt.Dialog;
33 import java.awt.FlowLayout;
34 import java.awt.Frame;
35 import java.awt.GraphicsConfiguration;
36 import java.awt.GridBagLayout;
37 import java.awt.GridBagConstraints;
38 import java.awt.GridLayout;
39 import java.awt.Insets;
40 import java.awt.Toolkit;
41 import java.awt.Window;
42 import java.awt.event.ActionEvent;
43 import java.awt.event.ActionListener;
44 import java.awt.event.FocusEvent;
45 import java.awt.event.FocusListener;
46 import java.awt.event.ItemEvent;
47 import java.awt.event.ItemListener;
48 import java.awt.event.WindowEvent;
49 import java.awt.event.WindowAdapter;
50 import java.awt.print.PrinterJob;
51 import java.io.File;
52 import java.io.FilePermission;
53 import java.io.IOException;
54 import java.net.URI;
55 import java.net.URL;
56 import java.text.DecimalFormat;
57 import java.util.Locale;
58 import java.util.ResourceBundle;
59 import java.util.Vector;
60 import javax.print.*;
61 import javax.print.attribute.*;
62 import javax.print.attribute.standard.*;
63 import javax.swing.*;
64 import javax.swing.border.Border;
65 import javax.swing.border.EmptyBorder;
66 import javax.swing.border.TitledBorder;
67 import javax.swing.event.ChangeEvent;
68 import javax.swing.event.ChangeListener;
69 import javax.swing.event.DocumentEvent;
70 import javax.swing.event.DocumentListener;
71 import javax.swing.event.PopupMenuEvent;
72 import javax.swing.event.PopupMenuListener;
73 import javax.swing.text.NumberFormatter;
74 import sun.print.SunPageSelection;
75 import java.awt.event.KeyEvent;
76 import java.net.URISyntaxException;
77 import java.lang.reflect.Field;
78 import java.net.MalformedURLException;
79 
80 /**
81  * A class which implements a cross-platform print dialog.
82  *
83  * @author  Chris Campbell
84  */
85 @SuppressWarnings("serial") // Superclass is not serializable across versions
86 public class ServiceDialog extends JDialog implements ActionListener {
87 
88     /**
89      * Waiting print status (user response pending).
90      */
91     public static final int WAITING = 0;
92 
93     /**
94      * Approve print status (user activated "Print" or "OK").
95      */
96     public static final int APPROVE = 1;
97 
98     /**
99      * Cancel print status (user activated "Cancel");
100      */
101     public static final int CANCEL = 2;
102 
103     private static final String strBundle = "sun.print.resources.serviceui";
104     private static final Insets panelInsets = new Insets(6, 6, 6, 6);
105     private static final Insets compInsets = new Insets(3, 6, 3, 6);
106 
107     private static ResourceBundle messageRB;
108     private JTabbedPane tpTabs;
109     private JButton btnCancel, btnApprove;
110     private PrintService[] services;
111     private int defaultServiceIndex;
112     private PrintRequestAttributeSet asOriginal;
113     private HashPrintRequestAttributeSet asCurrent;
114     private PrintService psCurrent;
115     private DocFlavor docFlavor;
116     private int status;
117 
118     private ValidatingFileChooser jfc;
119 
120     private GeneralPanel pnlGeneral;
121     private PageSetupPanel pnlPageSetup;
122     private AppearancePanel pnlAppearance;
123 
124     private boolean isAWT = false;
125     static {
initResource()126         initResource();
127     }
128 
129 
130     /**
131      * Constructor for the "standard" print dialog (containing all relevant
132      * tabs)
133      */
ServiceDialog(GraphicsConfiguration gc, int x, int y, PrintService[] services, int defaultServiceIndex, DocFlavor flavor, PrintRequestAttributeSet attributes, Window window)134     public ServiceDialog(GraphicsConfiguration gc,
135                          int x, int y,
136                          PrintService[] services,
137                          int defaultServiceIndex,
138                          DocFlavor flavor,
139                          PrintRequestAttributeSet attributes,
140                          Window window)
141     {
142         super(window, getMsg("dialog.printtitle"), Dialog.DEFAULT_MODALITY_TYPE, gc);
143         initPrintDialog(x, y, services, defaultServiceIndex,
144                         flavor, attributes);
145     }
146 
147     /**
148      * Initialize print dialog.
149      */
initPrintDialog(int x, int y, PrintService[] services, int defaultServiceIndex, DocFlavor flavor, PrintRequestAttributeSet attributes)150     void initPrintDialog(int x, int y,
151                          PrintService[] services,
152                          int defaultServiceIndex,
153                          DocFlavor flavor,
154                          PrintRequestAttributeSet attributes)
155     {
156         this.services = services;
157         this.defaultServiceIndex = defaultServiceIndex;
158         this.asOriginal = attributes;
159         this.asCurrent = new HashPrintRequestAttributeSet(attributes);
160         this.psCurrent = services[defaultServiceIndex];
161         this.docFlavor = flavor;
162         SunPageSelection pages =
163             (SunPageSelection)attributes.get(SunPageSelection.class);
164         if (pages != null) {
165             isAWT = true;
166         }
167 
168         if (attributes.get(DialogOwner.class) != null) {
169             DialogOwner owner = (DialogOwner)attributes.get(DialogOwner.class);
170             /* When the ServiceDialog is constructed the caller of the
171              * constructor checks for this attribute and if it specifies a
172              * window then it will use that in the constructor instead of
173              * inferring one from keyboard focus.
174              * In this case the owner of the dialog is the same as that
175              * specified in the attribute and we do not need to set the
176              * on top property
177              */
178             if ((getOwner() == null) || (owner.getOwner() != getOwner())) {
179                 try {
180                     setAlwaysOnTop(true);
181                 } catch (SecurityException e) {
182                 }
183             }
184         }
185         Container c = getContentPane();
186         c.setLayout(new BorderLayout());
187 
188         tpTabs = new JTabbedPane();
189         tpTabs.setBorder(new EmptyBorder(5, 5, 5, 5));
190 
191         String gkey = getMsg("tab.general");
192         int gmnemonic = getVKMnemonic("tab.general");
193         pnlGeneral = new GeneralPanel();
194         tpTabs.add(gkey, pnlGeneral);
195         tpTabs.setMnemonicAt(0, gmnemonic);
196 
197         String pkey = getMsg("tab.pagesetup");
198         int pmnemonic = getVKMnemonic("tab.pagesetup");
199         pnlPageSetup = new PageSetupPanel();
200         tpTabs.add(pkey, pnlPageSetup);
201         tpTabs.setMnemonicAt(1, pmnemonic);
202 
203         String akey = getMsg("tab.appearance");
204         int amnemonic = getVKMnemonic("tab.appearance");
205         pnlAppearance = new AppearancePanel();
206         tpTabs.add(akey, pnlAppearance);
207         tpTabs.setMnemonicAt(2, amnemonic);
208 
209         c.add(tpTabs, BorderLayout.CENTER);
210 
211         updatePanels();
212 
213         JPanel pnlSouth = new JPanel(new FlowLayout(FlowLayout.TRAILING));
214         btnApprove = createExitButton("button.print", this);
215         pnlSouth.add(btnApprove);
216         getRootPane().setDefaultButton(btnApprove);
217         btnCancel = createExitButton("button.cancel", this);
218         handleEscKey(btnCancel);
219         pnlSouth.add(btnCancel);
220         c.add(pnlSouth, BorderLayout.SOUTH);
221 
222         addWindowListener(new WindowAdapter() {
223             public void windowClosing(WindowEvent event) {
224                 dispose(CANCEL);
225             }
226         });
227 
228         getAccessibleContext().setAccessibleDescription(getMsg("dialog.printtitle"));
229         setResizable(false);
230         setLocation(x, y);
231         pack();
232     }
233 
234    /**
235      * Constructor for the solitary "page setup" dialog
236      */
ServiceDialog(GraphicsConfiguration gc, int x, int y, PrintService ps, DocFlavor flavor, PrintRequestAttributeSet attributes, Window window)237     public ServiceDialog(GraphicsConfiguration gc,
238                          int x, int y,
239                          PrintService ps,
240                          DocFlavor flavor,
241                          PrintRequestAttributeSet attributes,
242                          Window window)
243     {
244         super(window, getMsg("dialog.pstitle"), Dialog.DEFAULT_MODALITY_TYPE, gc);
245         initPageDialog(x, y, ps, flavor, attributes);
246     }
247 
248     /**
249      * Initialize "page setup" dialog
250      */
initPageDialog(int x, int y, PrintService ps, DocFlavor flavor, PrintRequestAttributeSet attributes)251     void initPageDialog(int x, int y,
252                          PrintService ps,
253                          DocFlavor flavor,
254                          PrintRequestAttributeSet attributes)
255     {
256         this.psCurrent = ps;
257         this.docFlavor = flavor;
258         this.asOriginal = attributes;
259         this.asCurrent = new HashPrintRequestAttributeSet(attributes);
260 
261         if (attributes.get(DialogOwner.class) != null) {
262             /* See comments in same block in initPrintDialog */
263             DialogOwner owner = (DialogOwner)attributes.get(DialogOwner.class);
264             if ((getOwner() == null) || (owner.getOwner() != getOwner())) {
265                 try {
266                     setAlwaysOnTop(true);
267                 } catch (SecurityException e) {
268                 }
269             }
270         }
271 
272         Container c = getContentPane();
273         c.setLayout(new BorderLayout());
274 
275         pnlPageSetup = new PageSetupPanel();
276         c.add(pnlPageSetup, BorderLayout.CENTER);
277 
278         pnlPageSetup.updateInfo();
279 
280         JPanel pnlSouth = new JPanel(new FlowLayout(FlowLayout.TRAILING));
281         btnApprove = createExitButton("button.ok", this);
282         pnlSouth.add(btnApprove);
283         getRootPane().setDefaultButton(btnApprove);
284         btnCancel = createExitButton("button.cancel", this);
285         handleEscKey(btnCancel);
286         pnlSouth.add(btnCancel);
287         c.add(pnlSouth, BorderLayout.SOUTH);
288 
289         addWindowListener(new WindowAdapter() {
290             public void windowClosing(WindowEvent event) {
291                 dispose(CANCEL);
292             }
293         });
294 
295         getAccessibleContext().setAccessibleDescription(getMsg("dialog.pstitle"));
296         setResizable(false);
297         setLocation(x, y);
298         pack();
299     }
300 
301     /**
302      * Performs Cancel when Esc key is pressed.
303      */
handleEscKey(JButton btnCancel)304     private void handleEscKey(JButton btnCancel) {
305         @SuppressWarnings("serial") // anonymous class
306         Action cancelKeyAction = new AbstractAction() {
307             public void actionPerformed(ActionEvent e) {
308                 dispose(CANCEL);
309             }
310         };
311         KeyStroke cancelKeyStroke =
312             KeyStroke.getKeyStroke((char)KeyEvent.VK_ESCAPE, 0);
313         InputMap inputMap =
314             btnCancel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
315         ActionMap actionMap = btnCancel.getActionMap();
316 
317         if (inputMap != null && actionMap != null) {
318             inputMap.put(cancelKeyStroke, "cancel");
319             actionMap.put("cancel", cancelKeyAction);
320         }
321     }
322 
323 
324     /**
325      * Returns the current status of the dialog (whether the user has selected
326      * the "Print" or "Cancel" button)
327      */
getStatus()328     public int getStatus() {
329         return status;
330     }
331 
332     /**
333      * Returns an AttributeSet based on whether or not the user cancelled the
334      * dialog.  If the user selected "Print" we return their new selections,
335      * otherwise we return the attributes that were passed in initially.
336      */
getAttributes()337     public PrintRequestAttributeSet getAttributes() {
338         if (status == APPROVE) {
339             return asCurrent;
340         } else {
341             return asOriginal;
342         }
343     }
344 
345     /**
346      * Returns a PrintService based on whether or not the user cancelled the
347      * dialog.  If the user selected "Print" we return the user's selection
348      * for the PrintService, otherwise we return null.
349      */
getPrintService()350     public PrintService getPrintService() {
351         if (status == APPROVE) {
352             return psCurrent;
353         } else {
354             return null;
355         }
356     }
357 
358     /**
359      * Sets the current status flag for the dialog and disposes it (thus
360      * returning control of the parent frame back to the user)
361      */
dispose(int status)362     public void dispose(int status) {
363         this.status = status;
364 
365         super.dispose();
366     }
367 
actionPerformed(ActionEvent e)368     public void actionPerformed(ActionEvent e) {
369         Object source = e.getSource();
370         boolean approved = false;
371 
372         if (source == btnApprove) {
373             approved = true;
374 
375             if (pnlGeneral != null) {
376                 if (pnlGeneral.isPrintToFileRequested()) {
377                     approved = showFileChooser();
378                 } else {
379                     asCurrent.remove(Destination.class);
380                 }
381             }
382         }
383 
384         dispose(approved ? APPROVE : CANCEL);
385     }
386 
387     /**
388      * Displays a JFileChooser that allows the user to select the destination
389      * for "Print To File"
390      */
showFileChooser()391     private boolean showFileChooser() {
392         Class<Destination> dstCategory = Destination.class;
393 
394         Destination dst = (Destination)asCurrent.get(dstCategory);
395         if (dst == null) {
396             dst = (Destination)asOriginal.get(dstCategory);
397             if (dst == null) {
398                 dst = (Destination)psCurrent.getDefaultAttributeValue(dstCategory);
399                 // "dst" should not be null. The following code
400                 // is only added to safeguard against a possible
401                 // buggy implementation of a PrintService having a
402                 // null default Destination.
403                 if (dst == null) {
404                     try {
405                         dst = new Destination(new URI("file:out.prn"));
406                     } catch (URISyntaxException e) {
407                     }
408                 }
409             }
410         }
411 
412         File fileDest;
413         if (dst != null) {
414             try {
415                 fileDest = new File(dst.getURI());
416             } catch (Exception e) {
417                 // all manner of runtime exceptions possible
418                 fileDest = new File("out.prn");
419             }
420         } else {
421             fileDest = new File("out.prn");
422         }
423 
424         ValidatingFileChooser jfc = new ValidatingFileChooser();
425         jfc.setApproveButtonText(getMsg("button.ok"));
426         jfc.setDialogTitle(getMsg("dialog.printtofile"));
427         jfc.setDialogType(JFileChooser.SAVE_DIALOG);
428         jfc.setSelectedFile(fileDest);
429 
430         int returnVal = jfc.showDialog(this, null);
431         if (returnVal == JFileChooser.APPROVE_OPTION) {
432             fileDest = jfc.getSelectedFile();
433 
434             try {
435                 asCurrent.add(new Destination(fileDest.toURI()));
436             } catch (Exception e) {
437                 asCurrent.remove(dstCategory);
438             }
439         } else {
440             asCurrent.remove(dstCategory);
441         }
442 
443         return (returnVal == JFileChooser.APPROVE_OPTION);
444     }
445 
446     /**
447      * Updates each of the top level panels
448      */
updatePanels()449     private void updatePanels() {
450         pnlGeneral.updateInfo();
451         pnlPageSetup.updateInfo();
452         pnlAppearance.updateInfo();
453     }
454 
455     /**
456      * Initialize ResourceBundle
457      */
458     @SuppressWarnings("removal")
initResource()459     public static void initResource() {
460         java.security.AccessController.doPrivileged(
461             new java.security.PrivilegedAction<Object>() {
462                 public Object run() {
463                     try {
464                         messageRB = ResourceBundle.getBundle(strBundle);
465                         return null;
466                     } catch (java.util.MissingResourceException e) {
467                         throw new Error("Fatal: Resource for ServiceUI " +
468                                         "is missing");
469                     }
470                 }
471             }
472         );
473     }
474 
475     /**
476      * Returns message string from resource
477      */
getMsg(String key)478     public static String getMsg(String key) {
479         try {
480             return removeMnemonics(messageRB.getString(key));
481         } catch (java.util.MissingResourceException e) {
482             throw new Error("Fatal: Resource for ServiceUI is broken; " +
483                             "there is no " + key + " key in resource");
484         }
485     }
486 
removeMnemonics(String s)487     private static String removeMnemonics(String s) {
488         int i = s.indexOf('&');
489         int len = s.length();
490         if (i < 0 || i == (len - 1)) {
491             return s;
492         }
493         int j = s.indexOf('&', i+1);
494         if (j == i+1) {
495             if (j+1 == len) {
496                 return s.substring(0, i+1);  // string ends with &&
497             } else {
498                 return s.substring(0, i+1) + removeMnemonics(s.substring(j+1));
499             }
500         }
501         // ok first & not double &&
502         if (i == 0) {
503             return removeMnemonics(s.substring(1));
504         } else {
505             return (s.substring(0, i) + removeMnemonics(s.substring(i+1)));
506         }
507     }
508 
509 
510     /**
511      * Returns mnemonic character from resource
512      */
getMnemonic(String key)513     private static char getMnemonic(String key) {
514         String str = messageRB.getString(key).replace("&&", "");
515         int index = str.indexOf('&');
516         if (0 <= index && index < str.length() - 1) {
517             char c = str.charAt(index + 1);
518             return Character.toUpperCase(c);
519         } else {
520             return (char)0;
521         }
522     }
523 
524     /**
525      * Returns the mnemonic as a KeyEvent.VK constant from the resource.
526      */
527     static Class<?> _keyEventClazz = null;
getVKMnemonic(String key)528     private static int getVKMnemonic(String key) {
529         String s = String.valueOf(getMnemonic(key));
530         if ( s == null || s.length() != 1) {
531             return 0;
532         }
533         String vkString = "VK_" + s.toUpperCase();
534 
535         try {
536             if (_keyEventClazz == null) {
537                 _keyEventClazz= Class.forName("java.awt.event.KeyEvent",
538                                  true, (ServiceDialog.class).getClassLoader());
539             }
540             Field field = _keyEventClazz.getDeclaredField(vkString);
541             int value = field.getInt(null);
542             return value;
543         } catch (Exception e) {
544         }
545         return 0;
546     }
547 
548     /**
549      * Returns URL for image resource
550      */
getImageResource(final String key)551     private static URL getImageResource(final String key) {
552         @SuppressWarnings("removal")
553         URL url = java.security.AccessController.doPrivileged(
554                        new java.security.PrivilegedAction<URL>() {
555                 public URL run() {
556                     URL url = ServiceDialog.class.getResource(
557                                                   "resources/" + key);
558                     return url;
559                 }
560         });
561 
562         if (url == null) {
563             throw new Error("Fatal: Resource for ServiceUI is broken; " +
564                             "there is no " + key + " key in resource");
565         }
566 
567         return url;
568     }
569 
570     /**
571      * Creates a new JButton and sets its text, mnemonic, and ActionListener
572      */
createButton(String key, ActionListener al)573     private static JButton createButton(String key, ActionListener al) {
574         JButton btn = new JButton(getMsg(key));
575         btn.setMnemonic(getMnemonic(key));
576         btn.addActionListener(al);
577 
578         return btn;
579     }
580 
581     /**
582      * Creates a new JButton and sets its text, and ActionListener
583      */
createExitButton(String key, ActionListener al)584     private static JButton createExitButton(String key, ActionListener al) {
585         String str = getMsg(key);
586         JButton btn = new JButton(str);
587         btn.addActionListener(al);
588         btn.getAccessibleContext().setAccessibleDescription(str);
589         return btn;
590     }
591 
592     /**
593      * Creates a new JCheckBox and sets its text, mnemonic, and ActionListener
594      */
createCheckBox(String key, ActionListener al)595     private static JCheckBox createCheckBox(String key, ActionListener al) {
596         JCheckBox cb = new JCheckBox(getMsg(key));
597         cb.setMnemonic(getMnemonic(key));
598         cb.addActionListener(al);
599 
600         return cb;
601     }
602 
603     /**
604      * Creates a new JRadioButton and sets its text, mnemonic,
605      * and ActionListener
606      */
createRadioButton(String key, ActionListener al)607     private static JRadioButton createRadioButton(String key,
608                                                   ActionListener al)
609     {
610         JRadioButton rb = new JRadioButton(getMsg(key));
611         rb.setMnemonic(getMnemonic(key));
612         rb.addActionListener(al);
613 
614         return rb;
615     }
616 
617   /**
618    * Creates a  pop-up dialog for "no print service"
619    */
showNoPrintService(GraphicsConfiguration gc)620     public static void showNoPrintService(GraphicsConfiguration gc)
621     {
622         Frame dlgFrame = new Frame(gc);
623         JOptionPane.showMessageDialog(dlgFrame,
624                                       getMsg("dialog.noprintermsg"));
625         dlgFrame.dispose();
626     }
627 
628     /**
629      * Sets the constraints for the GridBagLayout and adds the Component
630      * to the given Container
631      */
addToGB(Component comp, Container cont, GridBagLayout gridbag, GridBagConstraints constraints)632     private static void addToGB(Component comp, Container cont,
633                                 GridBagLayout gridbag,
634                                 GridBagConstraints constraints)
635     {
636         gridbag.setConstraints(comp, constraints);
637         cont.add(comp);
638     }
639 
640     /**
641      * Adds the AbstractButton to both the given ButtonGroup and Container
642      */
addToBG(AbstractButton button, Container cont, ButtonGroup bg)643     private static void addToBG(AbstractButton button, Container cont,
644                                 ButtonGroup bg)
645     {
646         bg.add(button);
647         cont.add(button);
648     }
649 
650 
651 
652 
653     /**
654      * The "General" tab.  Includes the controls for PrintService,
655      * PageRange, and Copies/Collate.
656      */
657     @SuppressWarnings("serial") // Superclass is not serializable across versions
658     private class GeneralPanel extends JPanel {
659 
660         private PrintServicePanel pnlPrintService;
661         private PrintRangePanel pnlPrintRange;
662         private CopiesPanel pnlCopies;
663 
GeneralPanel()664         public GeneralPanel() {
665             super();
666 
667             GridBagLayout gridbag = new GridBagLayout();
668             GridBagConstraints c = new GridBagConstraints();
669 
670             setLayout(gridbag);
671 
672             c.fill = GridBagConstraints.BOTH;
673             c.insets = panelInsets;
674             c.weightx = 1.0;
675             c.weighty = 1.0;
676 
677             c.gridwidth = GridBagConstraints.REMAINDER;
678             pnlPrintService = new PrintServicePanel();
679             addToGB(pnlPrintService, this, gridbag, c);
680 
681             c.gridwidth = GridBagConstraints.RELATIVE;
682             pnlPrintRange = new PrintRangePanel();
683             addToGB(pnlPrintRange, this, gridbag, c);
684 
685             c.gridwidth = GridBagConstraints.REMAINDER;
686             pnlCopies = new CopiesPanel();
687             addToGB(pnlCopies, this, gridbag, c);
688         }
689 
isPrintToFileRequested()690         public boolean isPrintToFileRequested() {
691             return (pnlPrintService.isPrintToFileSelected());
692         }
693 
updateInfo()694         public void updateInfo() {
695             pnlPrintService.updateInfo();
696             pnlPrintRange.updateInfo();
697             pnlCopies.updateInfo();
698         }
699     }
700 
701     @SuppressWarnings("serial") // Superclass is not serializable across versions
702     private class PrintServicePanel extends JPanel
703         implements ActionListener, ItemListener, PopupMenuListener
704     {
705         private final String strTitle = getMsg("border.printservice");
706         private FilePermission printToFilePermission;
707         private JButton btnProperties;
708         private JCheckBox cbPrintToFile;
709         private JComboBox<String> cbName;
710         private JLabel lblType, lblStatus, lblInfo;
711         private ServiceUIFactory uiFactory;
712         private boolean changedService = false;
713         private boolean filePermission;
714 
PrintServicePanel()715         public PrintServicePanel() {
716             super();
717 
718             uiFactory = psCurrent.getServiceUIFactory();
719 
720             GridBagLayout gridbag = new GridBagLayout();
721             GridBagConstraints c = new GridBagConstraints();
722 
723             setLayout(gridbag);
724             setBorder(BorderFactory.createTitledBorder(strTitle));
725 
726             String[] psnames = new String[services.length];
727             for (int i = 0; i < psnames.length; i++) {
728                 psnames[i] = services[i].getName();
729             }
730             cbName = new JComboBox<>(psnames);
731             cbName.setSelectedIndex(defaultServiceIndex);
732             cbName.addItemListener(this);
733             cbName.addPopupMenuListener(this);
734 
735             c.fill = GridBagConstraints.BOTH;
736             c.insets = compInsets;
737 
738             c.weightx = 0.0;
739             JLabel lblName = new JLabel(getMsg("label.psname"), JLabel.TRAILING);
740             lblName.setDisplayedMnemonic(getMnemonic("label.psname"));
741             lblName.setLabelFor(cbName);
742             addToGB(lblName, this, gridbag, c);
743             c.weightx = 1.0;
744             c.gridwidth = GridBagConstraints.RELATIVE;
745             addToGB(cbName, this, gridbag, c);
746             c.weightx = 0.0;
747             c.gridwidth = GridBagConstraints.REMAINDER;
748             btnProperties = createButton("button.properties", this);
749             addToGB(btnProperties, this, gridbag, c);
750 
751             c.weighty = 1.0;
752             lblStatus = addLabel(getMsg("label.status"), gridbag, c);
753             lblStatus.setLabelFor(null);
754 
755             lblType = addLabel(getMsg("label.pstype"), gridbag, c);
756             lblType.setLabelFor(null);
757 
758             c.gridwidth = 1;
759             addToGB(new JLabel(getMsg("label.info"), JLabel.TRAILING),
760                     this, gridbag, c);
761             c.gridwidth = GridBagConstraints.RELATIVE;
762             lblInfo = new JLabel();
763             lblInfo.setLabelFor(null);
764 
765             addToGB(lblInfo, this, gridbag, c);
766 
767             c.gridwidth = GridBagConstraints.REMAINDER;
768             cbPrintToFile = createCheckBox("checkbox.printtofile", this);
769             addToGB(cbPrintToFile, this, gridbag, c);
770 
771             filePermission = allowedToPrintToFile();
772         }
773 
isPrintToFileSelected()774         public boolean isPrintToFileSelected() {
775             return cbPrintToFile.isSelected();
776         }
777 
addLabel(String text, GridBagLayout gridbag, GridBagConstraints c)778         private JLabel addLabel(String text,
779                                 GridBagLayout gridbag, GridBagConstraints c)
780         {
781             c.gridwidth = 1;
782             addToGB(new JLabel(text, JLabel.TRAILING), this, gridbag, c);
783 
784             c.gridwidth = GridBagConstraints.REMAINDER;
785             JLabel label = new JLabel();
786             addToGB(label, this, gridbag, c);
787 
788             return label;
789         }
790 
791         @SuppressWarnings("deprecation")
actionPerformed(ActionEvent e)792         public void actionPerformed(ActionEvent e) {
793             Object source = e.getSource();
794 
795             if (source == btnProperties) {
796                 if (uiFactory != null) {
797                     JDialog dialog = (JDialog)uiFactory.getUI(
798                                                ServiceUIFactory.MAIN_UIROLE,
799                                                ServiceUIFactory.JDIALOG_UI);
800 
801                     if (dialog != null) {
802                         dialog.show();
803                     } else {
804                         DocumentPropertiesUI docPropertiesUI = null;
805                         try {
806                             docPropertiesUI =
807                                 (DocumentPropertiesUI)uiFactory.getUI
808                                 (DocumentPropertiesUI.DOCUMENTPROPERTIES_ROLE,
809                                  DocumentPropertiesUI.DOCPROPERTIESCLASSNAME);
810                         } catch (Exception ex) {
811                         }
812                         if (docPropertiesUI != null) {
813                             PrinterJobWrapper wrapper = (PrinterJobWrapper)
814                                 asCurrent.get(PrinterJobWrapper.class);
815                             if (wrapper == null) {
816                                 return; // should not happen, defensive only.
817                             }
818                             PrinterJob job = wrapper.getPrinterJob();
819                             if (job == null) {
820                                 return;  // should not happen, defensive only.
821                             }
822                             PrintRequestAttributeSet newAttrs =
823                                docPropertiesUI.showDocumentProperties
824                                (job, ServiceDialog.this, psCurrent, asCurrent);
825                             if (newAttrs != null) {
826                                 asCurrent.addAll(newAttrs);
827                                 updatePanels();
828                             }
829                         }
830                     }
831                 }
832             }
833         }
834 
itemStateChanged(ItemEvent e)835         public void itemStateChanged(ItemEvent e) {
836             if (e.getStateChange() == ItemEvent.SELECTED) {
837                 int index = cbName.getSelectedIndex();
838 
839                 if ((index >= 0) && (index < services.length)) {
840                     if (!services[index].equals(psCurrent)) {
841                         psCurrent = services[index];
842                         uiFactory = psCurrent.getServiceUIFactory();
843                         changedService = true;
844 
845                         Destination dest =
846                             (Destination)asOriginal.get(Destination.class);
847                         // to preserve the state of Print To File
848                         if ((dest != null || isPrintToFileSelected())
849                             && psCurrent.isAttributeCategorySupported(
850                                                         Destination.class)) {
851 
852                             if (dest != null) {
853                                 asCurrent.add(dest);
854                             } else {
855                                 dest = (Destination)psCurrent.
856                                     getDefaultAttributeValue(Destination.class);
857                                 // "dest" should not be null. The following code
858                                 // is only added to safeguard against a possible
859                                 // buggy implementation of a PrintService having a
860                                 // null default Destination.
861                                 if (dest == null) {
862                                     try {
863                                         dest =
864                                             new Destination(new URI("file:out.prn"));
865                                     } catch (URISyntaxException ue) {
866                                     }
867                                 }
868 
869                                 if (dest != null) {
870                                     asCurrent.add(dest);
871                                 }
872                             }
873                         } else {
874                             asCurrent.remove(Destination.class);
875                         }
876                     }
877                 }
878             }
879         }
880 
popupMenuWillBecomeVisible(PopupMenuEvent e)881         public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
882             changedService = false;
883         }
884 
popupMenuWillBecomeInvisible(PopupMenuEvent e)885         public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
886             if (changedService) {
887                 changedService = false;
888                 updatePanels();
889             }
890         }
891 
popupMenuCanceled(PopupMenuEvent e)892         public void popupMenuCanceled(PopupMenuEvent e) {
893         }
894 
895         /**
896          * We disable the "Print To File" checkbox if this returns false
897          */
allowedToPrintToFile()898         private boolean allowedToPrintToFile() {
899             try {
900                 throwPrintToFile();
901                 return true;
902             } catch (SecurityException e) {
903                 return false;
904             }
905         }
906 
907         /**
908          * Break this out as it may be useful when we allow API to
909          * specify printing to a file. In that case its probably right
910          * to throw a SecurityException if the permission is not granted.
911          */
throwPrintToFile()912         private void throwPrintToFile() {
913             @SuppressWarnings("removal")
914             SecurityManager security = System.getSecurityManager();
915             if (security != null) {
916                 if (printToFilePermission == null) {
917                     printToFilePermission =
918                         new FilePermission("<<ALL FILES>>", "read,write");
919                 }
920                 security.checkPermission(printToFilePermission);
921             }
922         }
923 
updateInfo()924         public void updateInfo() {
925             Class<Destination> dstCategory = Destination.class;
926             boolean dstSupported = false;
927             boolean dstSelected = false;
928             boolean dstAllowed = filePermission ?
929                 allowedToPrintToFile() : false;
930 
931             // setup Destination (print-to-file) widgets
932             Destination dst = (Destination)asCurrent.get(dstCategory);
933             if (dst != null) {
934                 try {
935                      dst.getURI().toURL();
936                      if (psCurrent.isAttributeValueSupported(dst, docFlavor,
937                                                              asCurrent)) {
938                          dstSupported = true;
939                          dstSelected = true;
940                      }
941                  } catch (MalformedURLException ex) {
942                      dstSupported = true;
943                  }
944             } else {
945                 if (psCurrent.isAttributeCategorySupported(dstCategory)) {
946                     dstSupported = true;
947                 }
948             }
949             cbPrintToFile.setEnabled(dstSupported && dstAllowed);
950             cbPrintToFile.setSelected(dstSelected && dstAllowed
951                                       && dstSupported);
952 
953             // setup PrintService information widgets
954             Attribute type = psCurrent.getAttribute(PrinterMakeAndModel.class);
955             if (type != null) {
956                 lblType.setText(type.toString());
957             }
958             Attribute status =
959                 psCurrent.getAttribute(PrinterIsAcceptingJobs.class);
960             if (status != null) {
961                 lblStatus.setText(getMsg(status.toString()));
962             }
963             Attribute info = psCurrent.getAttribute(PrinterInfo.class);
964             if (info != null) {
965                 lblInfo.setText(info.toString());
966             }
967             PrinterJob job = null;
968             PrinterJobWrapper wrapper = (PrinterJobWrapper)
969                                         asCurrent.get(PrinterJobWrapper.class);
970             if (wrapper != null) {
971                 job = wrapper.getPrinterJob();
972             }
973             btnProperties.setEnabled(uiFactory != null &&  job != null);
974         }
975     }
976 
977     @SuppressWarnings("serial") // Superclass is not serializable across versions
978     private class PrintRangePanel extends JPanel
979         implements ActionListener, FocusListener
980     {
981         private final String strTitle = getMsg("border.printrange");
982         private final PageRanges prAll = new PageRanges(1, Integer.MAX_VALUE);
983         private JRadioButton rbAll, rbPages, rbSelect;
984         private JFormattedTextField tfRangeFrom, tfRangeTo;
985         private JLabel lblRangeTo;
986         private boolean prSupported;
987         private boolean prPgRngSupported;
988 
PrintRangePanel()989         public PrintRangePanel() {
990             super();
991 
992             GridBagLayout gridbag = new GridBagLayout();
993             GridBagConstraints c = new GridBagConstraints();
994 
995             setLayout(gridbag);
996             setBorder(BorderFactory.createTitledBorder(strTitle));
997 
998             c.fill = GridBagConstraints.BOTH;
999             c.insets = compInsets;
1000             c.gridwidth = GridBagConstraints.REMAINDER;
1001 
1002             ButtonGroup bg = new ButtonGroup();
1003             JPanel pnlTop = new JPanel(new FlowLayout(FlowLayout.LEADING));
1004             rbAll = createRadioButton("radiobutton.rangeall", this);
1005             rbAll.setSelected(true);
1006             bg.add(rbAll);
1007             pnlTop.add(rbAll);
1008             addToGB(pnlTop, this, gridbag, c);
1009 
1010             // Selection never seemed to work so I'm commenting this part.
1011             /*
1012             if (isAWT) {
1013                 JPanel pnlMiddle  =
1014                     new JPanel(new FlowLayout(FlowLayout.LEADING));
1015                 rbSelect =
1016                     createRadioButton("radiobutton.selection", this);
1017                 bg.add(rbSelect);
1018                 pnlMiddle.add(rbSelect);
1019                 addToGB(pnlMiddle, this, gridbag, c);
1020             }
1021             */
1022 
1023             JPanel pnlBottom = new JPanel(new FlowLayout(FlowLayout.LEADING));
1024             rbPages = createRadioButton("radiobutton.rangepages", this);
1025             bg.add(rbPages);
1026             pnlBottom.add(rbPages);
1027             DecimalFormat format = new DecimalFormat("####0");
1028             format.setMinimumFractionDigits(0);
1029             format.setMaximumFractionDigits(0);
1030             format.setMinimumIntegerDigits(0);
1031             format.setMaximumIntegerDigits(5);
1032             format.setParseIntegerOnly(true);
1033             format.setDecimalSeparatorAlwaysShown(false);
1034             NumberFormatter nf = new NumberFormatter(format);
1035             nf.setMinimum(1);
1036             nf.setMaximum(Integer.MAX_VALUE);
1037             nf.setAllowsInvalid(true);
1038             nf.setCommitsOnValidEdit(true);
1039             tfRangeFrom = new JFormattedTextField(nf);
1040             tfRangeFrom.setColumns(4);
1041             tfRangeFrom.setEnabled(false);
1042             tfRangeFrom.addActionListener(this);
1043             tfRangeFrom.addFocusListener(this);
1044             tfRangeFrom.setFocusLostBehavior(
1045                 JFormattedTextField.PERSIST);
1046             tfRangeFrom.getAccessibleContext().setAccessibleName(
1047                                           getMsg("radiobutton.rangepages"));
1048             pnlBottom.add(tfRangeFrom);
1049             lblRangeTo = new JLabel(getMsg("label.rangeto"));
1050             lblRangeTo.setEnabled(false);
1051             pnlBottom.add(lblRangeTo);
1052             NumberFormatter nfto;
1053             try {
1054                 nfto = (NumberFormatter)nf.clone();
1055             } catch (CloneNotSupportedException e) {
1056                 nfto = new NumberFormatter();
1057             }
1058             tfRangeTo = new JFormattedTextField(nfto);
1059             tfRangeTo.setColumns(4);
1060             tfRangeTo.setEnabled(false);
1061             tfRangeTo.addFocusListener(this);
1062             tfRangeTo.getAccessibleContext().setAccessibleName(
1063                                           getMsg("label.rangeto"));
1064             pnlBottom.add(tfRangeTo);
1065             addToGB(pnlBottom, this, gridbag, c);
1066         }
1067 
actionPerformed(ActionEvent e)1068         public void actionPerformed(ActionEvent e) {
1069             Object source = e.getSource();
1070             SunPageSelection select = SunPageSelection.ALL;
1071 
1072             setupRangeWidgets();
1073 
1074             if (source == rbAll) {
1075                 asCurrent.add(prAll);
1076             } else if (source == rbSelect) {
1077                 select = SunPageSelection.SELECTION;
1078             } else if (source == rbPages ||
1079                        source == tfRangeFrom ||
1080                        source == tfRangeTo) {
1081                 updateRangeAttribute();
1082                 select = SunPageSelection.RANGE;
1083             }
1084 
1085             if (isAWT) {
1086                 asCurrent.add(select);
1087             }
1088         }
1089 
focusLost(FocusEvent e)1090         public void focusLost(FocusEvent e) {
1091             Object source = e.getSource();
1092 
1093             if ((source == tfRangeFrom) || (source == tfRangeTo)) {
1094                 updateRangeAttribute();
1095             }
1096         }
1097 
focusGained(FocusEvent e)1098         public void focusGained(FocusEvent e) {}
1099 
setupRangeWidgets()1100         private void setupRangeWidgets() {
1101             boolean rangeEnabled = (rbPages.isSelected() && prPgRngSupported);
1102             tfRangeFrom.setEnabled(rangeEnabled);
1103             tfRangeTo.setEnabled(rangeEnabled);
1104             lblRangeTo.setEnabled(rangeEnabled);
1105         }
1106 
updateRangeAttribute()1107         private void updateRangeAttribute() {
1108             String strFrom = tfRangeFrom.getText();
1109             String strTo = tfRangeTo.getText();
1110 
1111             int min;
1112             int max;
1113 
1114             try {
1115                 min = Integer.parseInt(strFrom);
1116             } catch (NumberFormatException e) {
1117                 min = 1;
1118             }
1119 
1120             try {
1121                 max = Integer.parseInt(strTo);
1122             } catch (NumberFormatException e) {
1123                 max = min;
1124             }
1125 
1126             if (min < 1) {
1127                 min = 1;
1128                 tfRangeFrom.setValue(1);
1129             }
1130 
1131             if (max < min) {
1132                 max = min;
1133                 tfRangeTo.setValue(min);
1134             }
1135 
1136             PageRanges pr = new PageRanges(min, max);
1137             asCurrent.add(pr);
1138         }
1139 
updateInfo()1140         public void updateInfo() {
1141             Class<PageRanges> prCategory = PageRanges.class;
1142             prSupported = false;
1143 
1144             if (psCurrent.isAttributeCategorySupported(prCategory) ||
1145                    isAWT) {
1146                 prSupported = true;
1147                 prPgRngSupported = psCurrent.isAttributeValueSupported(prAll,
1148                                                                      docFlavor,
1149                                                                      asCurrent);
1150             }
1151 
1152             SunPageSelection select = SunPageSelection.ALL;
1153             int min = 1;
1154             int max = 1;
1155 
1156             PageRanges pr = (PageRanges)asCurrent.get(prCategory);
1157             if (pr != null) {
1158                 if (!pr.equals(prAll)) {
1159                     select = SunPageSelection.RANGE;
1160 
1161                     int[][] members = pr.getMembers();
1162                     if ((members.length > 0) &&
1163                         (members[0].length > 1)) {
1164                         min = members[0][0];
1165                         max = members[0][1];
1166                     }
1167                 }
1168             }
1169 
1170             if (isAWT) {
1171                 select = (SunPageSelection)asCurrent.get(
1172                                                 SunPageSelection.class);
1173             }
1174 
1175             if (select == SunPageSelection.ALL) {
1176                 rbAll.setSelected(true);
1177             } else if (select == SunPageSelection.SELECTION) {
1178                 // Comment this for now -  rbSelect is not initialized
1179                 // because Selection button is not added.
1180                 // See PrintRangePanel above.
1181 
1182                 //rbSelect.setSelected(true);
1183             } else { // RANGE
1184                 rbPages.setSelected(true);
1185             }
1186             tfRangeFrom.setValue(min);
1187             tfRangeTo.setValue(max);
1188             rbAll.setEnabled(prSupported);
1189             rbPages.setEnabled(prPgRngSupported);
1190             setupRangeWidgets();
1191         }
1192     }
1193 
1194     @SuppressWarnings("serial") // Superclass is not serializable across versions
1195     private class CopiesPanel extends JPanel
1196         implements ActionListener, ChangeListener
1197     {
1198         private final String strTitle = getMsg("border.copies");
1199         private SpinnerNumberModel snModel;
1200         private JSpinner spinCopies;
1201         private JLabel lblCopies;
1202         private JCheckBox cbCollate;
1203         private boolean scSupported;
1204 
CopiesPanel()1205         public CopiesPanel() {
1206             super();
1207 
1208             GridBagLayout gridbag = new GridBagLayout();
1209             GridBagConstraints c = new GridBagConstraints();
1210 
1211             setLayout(gridbag);
1212             setBorder(BorderFactory.createTitledBorder(strTitle));
1213 
1214             c.fill = GridBagConstraints.HORIZONTAL;
1215             c.insets = compInsets;
1216 
1217             lblCopies = new JLabel(getMsg("label.numcopies"), JLabel.TRAILING);
1218             lblCopies.setDisplayedMnemonic(getMnemonic("label.numcopies"));
1219             lblCopies.getAccessibleContext().setAccessibleName(
1220                                              getMsg("label.numcopies"));
1221             addToGB(lblCopies, this, gridbag, c);
1222 
1223             snModel = new SpinnerNumberModel(1, 1, 999, 1);
1224             spinCopies = new JSpinner(snModel);
1225             lblCopies.setLabelFor(spinCopies);
1226             // REMIND
1227             ((JSpinner.NumberEditor)spinCopies.getEditor()).getTextField().setColumns(3);
1228             spinCopies.addChangeListener(this);
1229             c.gridwidth = GridBagConstraints.REMAINDER;
1230             addToGB(spinCopies, this, gridbag, c);
1231 
1232             cbCollate = createCheckBox("checkbox.collate", this);
1233             cbCollate.setEnabled(false);
1234             addToGB(cbCollate, this, gridbag, c);
1235         }
1236 
actionPerformed(ActionEvent e)1237         public void actionPerformed(ActionEvent e) {
1238             if (cbCollate.isSelected()) {
1239                 asCurrent.add(SheetCollate.COLLATED);
1240             } else {
1241                 asCurrent.add(SheetCollate.UNCOLLATED);
1242             }
1243         }
1244 
stateChanged(ChangeEvent e)1245         public void stateChanged(ChangeEvent e) {
1246             updateCollateCB();
1247 
1248             asCurrent.add(new Copies(snModel.getNumber().intValue()));
1249         }
1250 
updateCollateCB()1251         private void updateCollateCB() {
1252             int num = snModel.getNumber().intValue();
1253             if (isAWT) {
1254                 cbCollate.setEnabled(true);
1255             } else {
1256                 cbCollate.setEnabled((num > 1) && scSupported);
1257             }
1258         }
1259 
updateInfo()1260         public void updateInfo() {
1261             Class<Copies> cpCategory = Copies.class;
1262             Class<SheetCollate> scCategory = SheetCollate.class;
1263             boolean cpSupported = false;
1264             scSupported = false;
1265 
1266             // setup Copies spinner
1267             if (psCurrent.isAttributeCategorySupported(cpCategory)) {
1268                 cpSupported = true;
1269             }
1270             CopiesSupported cs =
1271                 (CopiesSupported)psCurrent.getSupportedAttributeValues(
1272                                                        cpCategory, null, null);
1273             if (cs == null) {
1274                 cs = new CopiesSupported(1, 999);
1275             }
1276             Copies cp = (Copies)asCurrent.get(cpCategory);
1277             if (cp == null) {
1278                 cp = (Copies)psCurrent.getDefaultAttributeValue(cpCategory);
1279                 if (cp == null) {
1280                     cp = new Copies(1);
1281                 }
1282             }
1283             spinCopies.setEnabled(cpSupported);
1284             lblCopies.setEnabled(cpSupported);
1285 
1286             int[][] members = cs.getMembers();
1287             int min, max;
1288             if ((members.length > 0) && (members[0].length > 0)) {
1289                 min = members[0][0];
1290                 max = members[0][1];
1291             } else {
1292                 min = 1;
1293                 max = Integer.MAX_VALUE;
1294             }
1295             snModel.setMinimum(min);
1296             snModel.setMaximum(max);
1297 
1298             int value = cp.getValue();
1299             if ((value < min) || (value > max)) {
1300                 value = min;
1301             }
1302             snModel.setValue(value);
1303 
1304             // setup Collate checkbox
1305             if (psCurrent.isAttributeCategorySupported(scCategory)) {
1306                 scSupported = true;
1307             }
1308             SheetCollate sc = (SheetCollate)asCurrent.get(scCategory);
1309             if (sc == null) {
1310                 sc = (SheetCollate)psCurrent.getDefaultAttributeValue(scCategory);
1311                 if (sc == null) {
1312                     sc = SheetCollate.UNCOLLATED;
1313                 }
1314                 if (sc != null &&
1315                     !psCurrent.isAttributeValueSupported(sc, docFlavor, asCurrent)) {
1316                     scSupported = false;
1317                 }
1318             } else {
1319                 if (!psCurrent.isAttributeValueSupported(sc, docFlavor, asCurrent)) {
1320                     scSupported = false;
1321                 }
1322             }
1323             cbCollate.setSelected(sc == SheetCollate.COLLATED && scSupported);
1324             updateCollateCB();
1325         }
1326     }
1327 
1328 
1329 
1330 
1331     /**
1332      * The "Page Setup" tab.  Includes the controls for MediaSource/MediaTray,
1333      * OrientationRequested, and Sides.
1334      */
1335     @SuppressWarnings("serial") // Superclass is not serializable across versions
1336     private class PageSetupPanel extends JPanel {
1337 
1338         private MediaPanel pnlMedia;
1339         private OrientationPanel pnlOrientation;
1340         private MarginsPanel pnlMargins;
1341 
PageSetupPanel()1342         public PageSetupPanel() {
1343             super();
1344 
1345             GridBagLayout gridbag = new GridBagLayout();
1346             GridBagConstraints c = new GridBagConstraints();
1347 
1348             setLayout(gridbag);
1349 
1350             c.fill = GridBagConstraints.BOTH;
1351             c.insets = panelInsets;
1352             c.weightx = 1.0;
1353             c.weighty = 1.0;
1354 
1355             c.gridwidth = GridBagConstraints.REMAINDER;
1356             pnlMedia = new MediaPanel();
1357             addToGB(pnlMedia, this, gridbag, c);
1358 
1359             pnlOrientation = new OrientationPanel();
1360             c.gridwidth = GridBagConstraints.RELATIVE;
1361             addToGB(pnlOrientation, this, gridbag, c);
1362 
1363             pnlMargins = new MarginsPanel();
1364             pnlOrientation.addOrientationListener(pnlMargins);
1365             pnlMedia.addMediaListener(pnlMargins);
1366             c.gridwidth = GridBagConstraints.REMAINDER;
1367             addToGB(pnlMargins, this, gridbag, c);
1368         }
1369 
updateInfo()1370         public void updateInfo() {
1371             pnlMedia.updateInfo();
1372             pnlOrientation.updateInfo();
1373             pnlMargins.updateInfo();
1374         }
1375     }
1376 
1377     @SuppressWarnings("serial") // Superclass is not serializable across versions
1378     private class MarginsPanel extends JPanel
1379                                implements ActionListener, FocusListener {
1380 
1381         private final String strTitle = getMsg("border.margins");
1382         private JFormattedTextField leftMargin, rightMargin,
1383                                     topMargin, bottomMargin;
1384         private JLabel lblLeft, lblRight, lblTop, lblBottom;
1385         private int units = MediaPrintableArea.MM;
1386         // storage for the last margin values calculated, -ve is uninitialised
1387         private float lmVal = -1f,rmVal = -1f, tmVal = -1f, bmVal = -1f;
1388         // storage for margins as objects mapped into orientation for display
1389         private Float lmObj,rmObj,tmObj,bmObj;
1390 
MarginsPanel()1391         public MarginsPanel() {
1392             super();
1393 
1394             GridBagLayout gridbag = new GridBagLayout();
1395             GridBagConstraints c = new GridBagConstraints();
1396             c.fill = GridBagConstraints.HORIZONTAL;
1397             c.weightx = 1.0;
1398             c.weighty = 0.0;
1399             c.insets = compInsets;
1400 
1401             setLayout(gridbag);
1402             setBorder(BorderFactory.createTitledBorder(strTitle));
1403 
1404             String unitsKey = "label.millimetres";
1405             String defaultCountry = Locale.getDefault().getCountry();
1406             if (defaultCountry != null &&
1407                 (defaultCountry.isEmpty() ||
1408                  defaultCountry.equals(Locale.US.getCountry()) ||
1409                  defaultCountry.equals(Locale.CANADA.getCountry()))) {
1410                 unitsKey = "label.inches";
1411                 units = MediaPrintableArea.INCH;
1412             }
1413             String unitsMsg = getMsg(unitsKey);
1414 
1415             DecimalFormat format;
1416             if (units == MediaPrintableArea.MM) {
1417                 format = new DecimalFormat("###.##");
1418                 format.setMaximumIntegerDigits(3);
1419             } else {
1420                 format = new DecimalFormat("##.##");
1421                 format.setMaximumIntegerDigits(2);
1422             }
1423 
1424             format.setMinimumFractionDigits(1);
1425             format.setMaximumFractionDigits(2);
1426             format.setMinimumIntegerDigits(1);
1427             format.setParseIntegerOnly(false);
1428             format.setDecimalSeparatorAlwaysShown(true);
1429             NumberFormatter nf = new NumberFormatter(format);
1430             nf.setMinimum(Float.valueOf(0.0f));
1431             nf.setMaximum(Float.valueOf(999.0f));
1432             nf.setAllowsInvalid(true);
1433             nf.setCommitsOnValidEdit(true);
1434 
1435             leftMargin = new JFormattedTextField(nf);
1436             leftMargin.addFocusListener(this);
1437             leftMargin.addActionListener(this);
1438             leftMargin.getAccessibleContext().setAccessibleName(
1439                                               getMsg("label.leftmargin"));
1440             rightMargin = new JFormattedTextField(nf);
1441             rightMargin.addFocusListener(this);
1442             rightMargin.addActionListener(this);
1443             rightMargin.getAccessibleContext().setAccessibleName(
1444                                               getMsg("label.rightmargin"));
1445             topMargin = new JFormattedTextField(nf);
1446             topMargin.addFocusListener(this);
1447             topMargin.addActionListener(this);
1448             topMargin.getAccessibleContext().setAccessibleName(
1449                                               getMsg("label.topmargin"));
1450 
1451             bottomMargin = new JFormattedTextField(nf);
1452             bottomMargin.addFocusListener(this);
1453             bottomMargin.addActionListener(this);
1454             bottomMargin.getAccessibleContext().setAccessibleName(
1455                                               getMsg("label.bottommargin"));
1456 
1457             c.gridwidth = GridBagConstraints.RELATIVE;
1458             lblLeft = new JLabel(getMsg("label.leftmargin") + " " + unitsMsg,
1459                                  JLabel.LEADING);
1460             lblLeft.setDisplayedMnemonic(getMnemonic("label.leftmargin"));
1461             lblLeft.setLabelFor(leftMargin);
1462             addToGB(lblLeft, this, gridbag, c);
1463 
1464             c.gridwidth = GridBagConstraints.REMAINDER;
1465             lblRight = new JLabel(getMsg("label.rightmargin") + " " + unitsMsg,
1466                                   JLabel.LEADING);
1467             lblRight.setDisplayedMnemonic(getMnemonic("label.rightmargin"));
1468             lblRight.setLabelFor(rightMargin);
1469             addToGB(lblRight, this, gridbag, c);
1470 
1471             c.gridwidth = GridBagConstraints.RELATIVE;
1472             addToGB(leftMargin, this, gridbag, c);
1473 
1474             c.gridwidth = GridBagConstraints.REMAINDER;
1475             addToGB(rightMargin, this, gridbag, c);
1476 
1477             // add an invisible spacing component.
1478             addToGB(new JPanel(), this, gridbag, c);
1479 
1480             c.gridwidth = GridBagConstraints.RELATIVE;
1481             lblTop = new JLabel(getMsg("label.topmargin") + " " + unitsMsg,
1482                                 JLabel.LEADING);
1483             lblTop.setDisplayedMnemonic(getMnemonic("label.topmargin"));
1484             lblTop.setLabelFor(topMargin);
1485             addToGB(lblTop, this, gridbag, c);
1486 
1487             c.gridwidth = GridBagConstraints.REMAINDER;
1488             lblBottom = new JLabel(getMsg("label.bottommargin") +
1489                                    " " + unitsMsg, JLabel.LEADING);
1490             lblBottom.setDisplayedMnemonic(getMnemonic("label.bottommargin"));
1491             lblBottom.setLabelFor(bottomMargin);
1492             addToGB(lblBottom, this, gridbag, c);
1493 
1494             c.gridwidth = GridBagConstraints.RELATIVE;
1495             addToGB(topMargin, this, gridbag, c);
1496 
1497             c.gridwidth = GridBagConstraints.REMAINDER;
1498             addToGB(bottomMargin, this, gridbag, c);
1499 
1500         }
1501 
actionPerformed(ActionEvent e)1502         public void actionPerformed(ActionEvent e) {
1503             Object source = e.getSource();
1504             updateMargins(source);
1505         }
1506 
focusLost(FocusEvent e)1507         public void focusLost(FocusEvent e) {
1508             Object source = e.getSource();
1509             updateMargins(source);
1510         }
1511 
focusGained(FocusEvent e)1512         public void focusGained(FocusEvent e) {}
1513 
1514         /* Get the numbers, use to create a MPA.
1515          * If its valid, accept it and update the attribute set.
1516          * If its not valid, then reject it and call updateInfo()
1517          * to re-establish the previous entries.
1518          */
updateMargins(Object source)1519         public void updateMargins(Object source) {
1520             if (!(source instanceof JFormattedTextField)) {
1521                 return;
1522             } else {
1523                 JFormattedTextField tf = (JFormattedTextField)source;
1524                 Float val = (Float)tf.getValue();
1525                 if (val == null) {
1526                     return;
1527                 }
1528                 if (tf == leftMargin && val.equals(lmObj)) {
1529                     return;
1530                 }
1531                 if (tf == rightMargin && val.equals(rmObj)) {
1532                     return;
1533                 }
1534                 if (tf == topMargin && val.equals(tmObj)) {
1535                     return;
1536                 }
1537                 if (tf == bottomMargin && val.equals(bmObj)) {
1538                     return;
1539                 }
1540             }
1541 
1542             Float lmTmpObj = (Float)leftMargin.getValue();
1543             Float rmTmpObj = (Float)rightMargin.getValue();
1544             Float tmTmpObj = (Float)topMargin.getValue();
1545             Float bmTmpObj = (Float)bottomMargin.getValue();
1546 
1547             float lm = lmTmpObj.floatValue();
1548             float rm = rmTmpObj.floatValue();
1549             float tm = tmTmpObj.floatValue();
1550             float bm = bmTmpObj.floatValue();
1551 
1552             /* adjust for orientation */
1553             Class<OrientationRequested> orCategory = OrientationRequested.class;
1554             OrientationRequested or =
1555                 (OrientationRequested)asCurrent.get(orCategory);
1556 
1557             if (or == null) {
1558                 or = (OrientationRequested)
1559                      psCurrent.getDefaultAttributeValue(orCategory);
1560             }
1561 
1562             float tmp;
1563             if (or == OrientationRequested.REVERSE_PORTRAIT) {
1564                 tmp = lm; lm = rm; rm = tmp;
1565                 tmp = tm; tm = bm; bm = tmp;
1566             } else if (or == OrientationRequested.LANDSCAPE) {
1567                 tmp = lm;
1568                 lm = tm;
1569                 tm = rm;
1570                 rm = bm;
1571                 bm = tmp;
1572             } else if (or == OrientationRequested.REVERSE_LANDSCAPE) {
1573                 tmp = lm;
1574                 lm = bm;
1575                 bm = rm;
1576                 rm = tm;
1577                 tm = tmp;
1578             }
1579             MediaPrintableArea mpa;
1580             if ((mpa = validateMargins(lm, rm, tm, bm)) != null) {
1581                 asCurrent.add(mpa);
1582                 lmVal = lm;
1583                 rmVal = rm;
1584                 tmVal = tm;
1585                 bmVal = bm;
1586                 lmObj = lmTmpObj;
1587                 rmObj = rmTmpObj;
1588                 tmObj = tmTmpObj;
1589                 bmObj = bmTmpObj;
1590             } else {
1591                 if (lmObj == null || rmObj == null ||
1592                     tmObj == null || bmObj == null) {
1593                     return;
1594                 } else {
1595                     leftMargin.setValue(lmObj);
1596                     rightMargin.setValue(rmObj);
1597                     topMargin.setValue(tmObj);
1598                     bottomMargin.setValue(bmObj);
1599 
1600                 }
1601             }
1602         }
1603 
1604         /*
1605          * This method either accepts the values and creates a new
1606          * MediaPrintableArea, or does nothing.
1607          * It should not attempt to create a printable area from anything
1608          * other than the exact values passed in.
1609          * But REMIND/TBD: it would be user friendly to replace margins the
1610          * user entered but are out of bounds with the minimum.
1611          * At that point this method will need to take responsibility for
1612          * updating the "stored" values and the UI.
1613          */
validateMargins(float lm, float rm, float tm, float bm)1614         private MediaPrintableArea validateMargins(float lm, float rm,
1615                                                    float tm, float bm) {
1616 
1617             Class<MediaPrintableArea> mpaCategory = MediaPrintableArea.class;
1618             MediaPrintableArea mpa;
1619             MediaPrintableArea mpaMax = null;
1620             MediaSize mediaSize = null;
1621 
1622             Media media = (Media)asCurrent.get(Media.class);
1623             if (media == null || !(media instanceof MediaSizeName)) {
1624                 media = (Media)psCurrent.getDefaultAttributeValue(Media.class);
1625             }
1626             if (media != null && (media instanceof MediaSizeName)) {
1627                 MediaSizeName msn = (MediaSizeName)media;
1628                 mediaSize = MediaSize.getMediaSizeForName(msn);
1629             }
1630             if (mediaSize == null) {
1631                 mediaSize = new MediaSize(8.5f, 11f, Size2DSyntax.INCH);
1632             }
1633 
1634             if (media != null) {
1635                 PrintRequestAttributeSet tmpASet =
1636                     new HashPrintRequestAttributeSet(asCurrent);
1637                 tmpASet.add(media);
1638 
1639                 Object values =
1640                     psCurrent.getSupportedAttributeValues(mpaCategory,
1641                                                           docFlavor,
1642                                                           tmpASet);
1643                 if (values instanceof MediaPrintableArea[] &&
1644                     ((MediaPrintableArea[])values).length > 0) {
1645                     mpaMax = ((MediaPrintableArea[])values)[0];
1646 
1647                 }
1648             }
1649             if (mpaMax == null) {
1650                 mpaMax = new MediaPrintableArea(0f, 0f,
1651                                                 mediaSize.getX(units),
1652                                                 mediaSize.getY(units),
1653                                                 units);
1654             }
1655 
1656             float wid = mediaSize.getX(units);
1657             float hgt = mediaSize.getY(units);
1658             float pax = lm;
1659             float pay = tm;
1660             float par = rm;
1661             float pab = bm;
1662             float paw = wid - lm - rm;
1663             float pah = hgt - tm - bm;
1664 
1665             if (paw <= 0f || pah <= 0f || pax < 0f || pay < 0f ||
1666                 par <= 0f || pab <= 0f ||
1667                 pax < mpaMax.getX(units) || paw > mpaMax.getWidth(units) ||
1668                 pay < mpaMax.getY(units) || pah > mpaMax.getHeight(units)) {
1669                 return null;
1670             } else {
1671                 return new MediaPrintableArea(lm, tm, paw, pah, units);
1672             }
1673         }
1674 
1675         /* This is complex as a MediaPrintableArea is valid only within
1676          * a particular context of media size.
1677          * So we need a MediaSize as well as a MediaPrintableArea.
1678          * MediaSize can be obtained from MediaSizeName.
1679          * If the application specifies a MediaPrintableArea, we accept it
1680          * to the extent its valid for the Media they specify. If they
1681          * don't specify a Media, then the default is assumed.
1682          *
1683          * If an application doesn't define a MediaPrintableArea, we need to
1684          * create a suitable one, this is created using the specified (or
1685          * default) Media and default 1 inch margins. This is validated
1686          * against the paper in case this is too large for tiny media.
1687          */
updateInfo()1688         public void updateInfo() {
1689 
1690             if (isAWT) {
1691                 leftMargin.setEnabled(false);
1692                 rightMargin.setEnabled(false);
1693                 topMargin.setEnabled(false);
1694                 bottomMargin.setEnabled(false);
1695                 lblLeft.setEnabled(false);
1696                 lblRight.setEnabled(false);
1697                 lblTop.setEnabled(false);
1698                 lblBottom.setEnabled(false);
1699                 return;
1700             }
1701 
1702             Class<MediaPrintableArea> mpaCategory = MediaPrintableArea.class;
1703             MediaPrintableArea mpa =
1704                  (MediaPrintableArea)asCurrent.get(mpaCategory);
1705             MediaPrintableArea mpaMax = null;
1706             MediaSize mediaSize = null;
1707 
1708             Media media = (Media)asCurrent.get(Media.class);
1709             if (media == null || !(media instanceof MediaSizeName)) {
1710                 media = (Media)psCurrent.getDefaultAttributeValue(Media.class);
1711             }
1712             if (media != null && (media instanceof MediaSizeName)) {
1713                 MediaSizeName msn = (MediaSizeName)media;
1714                 mediaSize = MediaSize.getMediaSizeForName(msn);
1715             }
1716             if (mediaSize == null) {
1717                 mediaSize = new MediaSize(8.5f, 11f, Size2DSyntax.INCH);
1718             }
1719 
1720             if (media != null) {
1721                 PrintRequestAttributeSet tmpASet =
1722                     new HashPrintRequestAttributeSet(asCurrent);
1723                 tmpASet.add(media);
1724 
1725                 Object values =
1726                     psCurrent.getSupportedAttributeValues(mpaCategory,
1727                                                           docFlavor,
1728                                                           tmpASet);
1729                 if (values instanceof MediaPrintableArea[] &&
1730                     ((MediaPrintableArea[])values).length > 0) {
1731                     mpaMax = ((MediaPrintableArea[])values)[0];
1732 
1733                 } else if (values instanceof MediaPrintableArea) {
1734                     mpaMax = (MediaPrintableArea)values;
1735                 }
1736             }
1737             if (mpaMax == null) {
1738                 mpaMax = new MediaPrintableArea(0f, 0f,
1739                                                 mediaSize.getX(units),
1740                                                 mediaSize.getY(units),
1741                                                 units);
1742             }
1743 
1744             /*
1745              * At this point we now know as best we can :-
1746              * - the media size
1747              * - the maximum corresponding printable area
1748              * - the media printable area specified by the client, if any.
1749              * The next step is to create a default MPA if none was specified.
1750              * 1" margins are used unless they are disproportionately
1751              * large compared to the size of the media.
1752              */
1753 
1754             float wid = mediaSize.getX(MediaPrintableArea.INCH);
1755             float hgt = mediaSize.getY(MediaPrintableArea.INCH);
1756             float maxMarginRatio = 5f;
1757             float xMgn, yMgn;
1758             if (wid > maxMarginRatio) {
1759                 xMgn = 1f;
1760             } else {
1761                 xMgn = wid / maxMarginRatio;
1762             }
1763             if (hgt > maxMarginRatio) {
1764                 yMgn = 1f;
1765             } else {
1766                 yMgn = hgt / maxMarginRatio;
1767             }
1768 
1769             if (mpa == null) {
1770                 mpa = new MediaPrintableArea(xMgn, yMgn,
1771                                              wid-(2*xMgn), hgt-(2*yMgn),
1772                                              MediaPrintableArea.INCH);
1773                 asCurrent.add(mpa);
1774             }
1775             float pax = mpa.getX(units);
1776             float pay = mpa.getY(units);
1777             float paw = mpa.getWidth(units);
1778             float pah = mpa.getHeight(units);
1779             float paxMax = mpaMax.getX(units);
1780             float payMax = mpaMax.getY(units);
1781             float pawMax = mpaMax.getWidth(units);
1782             float pahMax = mpaMax.getHeight(units);
1783 
1784 
1785             boolean invalid = false;
1786 
1787             // If the paper is set to something which is too small to
1788             // accommodate a specified printable area, perhaps carried
1789             // over from a larger paper, the adjustment that needs to be
1790             // performed should seem the most natural from a user's viewpoint.
1791             // Since the user is specifying margins, then we are biased
1792             // towards keeping the margins as close to what is specified as
1793             // possible, shrinking or growing the printable area.
1794             // But the API uses printable area, so you need to know the
1795             // media size in which the margins were previously interpreted,
1796             // or at least have a record of the margins.
1797             // In the case that this is the creation of this UI we do not
1798             // have this record, so we are somewhat reliant on the client
1799             // to supply a reasonable default
1800             wid = mediaSize.getX(units);
1801             hgt = mediaSize.getY(units);
1802             if (lmVal >= 0f) {
1803                 invalid = true;
1804 
1805                 if (lmVal + rmVal > wid) {
1806                     // margins impossible, but maintain P.A if can
1807                     if (paw > pawMax) {
1808                         paw = pawMax;
1809                     }
1810                     // try to centre the printable area.
1811                     pax = (wid - paw)/2f;
1812                 } else {
1813                     pax = (lmVal >= paxMax) ? lmVal : paxMax;
1814                     paw = wid - pax - rmVal;
1815                 }
1816                 if (tmVal + bmVal > hgt) {
1817                     if (pah > pahMax) {
1818                         pah = pahMax;
1819                     }
1820                     pay = (hgt - pah)/2f;
1821                 } else {
1822                     pay = (tmVal >= payMax) ? tmVal : payMax;
1823                     pah = hgt - pay - bmVal;
1824                 }
1825             }
1826             if (pax < paxMax) {
1827                 invalid = true;
1828                 pax = paxMax;
1829             }
1830             if (pay < payMax) {
1831                 invalid = true;
1832                 pay = payMax;
1833             }
1834             if (paw > pawMax) {
1835                 invalid = true;
1836                 paw = pawMax;
1837             }
1838             if (pah > pahMax) {
1839                 invalid = true;
1840                 pah = pahMax;
1841             }
1842 
1843             if ((pax + paw > paxMax + pawMax) || (paw <= 0f)) {
1844                 invalid = true;
1845                 pax = paxMax;
1846                 paw = pawMax;
1847             }
1848             if ((pay + pah > payMax + pahMax) || (pah <= 0f)) {
1849                 invalid = true;
1850                 pay = payMax;
1851                 pah = pahMax;
1852             }
1853 
1854             if (invalid) {
1855                 mpa = new MediaPrintableArea(pax, pay, paw, pah, units);
1856                 asCurrent.add(mpa);
1857             }
1858 
1859             /* We now have a valid printable area.
1860              * Turn it into margins, using the mediaSize
1861              */
1862             lmVal = pax;
1863             tmVal = pay;
1864             rmVal = mediaSize.getX(units) - pax - paw;
1865             bmVal = mediaSize.getY(units) - pay - pah;
1866 
1867             lmObj = lmVal;
1868             rmObj = rmVal;
1869             tmObj = tmVal;
1870             bmObj = bmVal;
1871 
1872             /* Now we know the values to use, we need to assign them
1873              * to the fields appropriate for the orientation.
1874              * Note: if orientation changes this method must be called.
1875              */
1876             Class<OrientationRequested> orCategory = OrientationRequested.class;
1877             OrientationRequested or =
1878                 (OrientationRequested)asCurrent.get(orCategory);
1879 
1880             if (or == null) {
1881                 or = (OrientationRequested)
1882                      psCurrent.getDefaultAttributeValue(orCategory);
1883             }
1884 
1885             Float tmp;
1886 
1887             if (or == OrientationRequested.REVERSE_PORTRAIT) {
1888                 tmp = lmObj; lmObj = rmObj; rmObj = tmp;
1889                 tmp = tmObj; tmObj = bmObj; bmObj = tmp;
1890             }  else if (or == OrientationRequested.LANDSCAPE) {
1891                 tmp = lmObj;
1892                 lmObj = bmObj;
1893                 bmObj = rmObj;
1894                 rmObj = tmObj;
1895                 tmObj = tmp;
1896             }  else if (or == OrientationRequested.REVERSE_LANDSCAPE) {
1897                 tmp = lmObj;
1898                 lmObj = tmObj;
1899                 tmObj = rmObj;
1900                 rmObj = bmObj;
1901                 bmObj = tmp;
1902             }
1903 
1904             leftMargin.setValue(lmObj);
1905             rightMargin.setValue(rmObj);
1906             topMargin.setValue(tmObj);
1907             bottomMargin.setValue(bmObj);
1908         }
1909     }
1910 
1911     @SuppressWarnings("serial") // Superclass is not serializable across versions
1912     private class MediaPanel extends JPanel implements ItemListener {
1913 
1914         private final String strTitle = getMsg("border.media");
1915         private JLabel lblSize, lblSource;
1916         private JComboBox<Object> cbSize, cbSource;
1917         private Vector<MediaSizeName> sizes = new Vector<>();
1918         private Vector<MediaTray> sources = new Vector<>();
1919         private MarginsPanel pnlMargins = null;
1920 
MediaPanel()1921         public MediaPanel() {
1922             super();
1923 
1924             GridBagLayout gridbag = new GridBagLayout();
1925             GridBagConstraints c = new GridBagConstraints();
1926 
1927             setLayout(gridbag);
1928             setBorder(BorderFactory.createTitledBorder(strTitle));
1929 
1930             cbSize = new JComboBox<>();
1931             cbSource = new JComboBox<>();
1932 
1933             c.fill = GridBagConstraints.BOTH;
1934             c.insets = compInsets;
1935             c.weighty = 1.0;
1936 
1937             c.weightx = 0.0;
1938             lblSize = new JLabel(getMsg("label.size"), JLabel.TRAILING);
1939             lblSize.setDisplayedMnemonic(getMnemonic("label.size"));
1940             lblSize.setLabelFor(cbSize);
1941             addToGB(lblSize, this, gridbag, c);
1942             c.weightx = 1.0;
1943             c.gridwidth = GridBagConstraints.REMAINDER;
1944             addToGB(cbSize, this, gridbag, c);
1945 
1946             c.weightx = 0.0;
1947             c.gridwidth = 1;
1948             lblSource = new JLabel(getMsg("label.source"), JLabel.TRAILING);
1949             lblSource.setDisplayedMnemonic(getMnemonic("label.source"));
1950             lblSource.setLabelFor(cbSource);
1951             addToGB(lblSource, this, gridbag, c);
1952             c.gridwidth = GridBagConstraints.REMAINDER;
1953             addToGB(cbSource, this, gridbag, c);
1954         }
1955 
getMediaName(String key)1956         private String getMediaName(String key) {
1957             try {
1958                 // replace characters that would be invalid in
1959                 // a resource key with valid characters
1960                 String newkey = key.replace(' ', '-');
1961                 newkey = newkey.replace('#', 'n');
1962 
1963                 return messageRB.getString(newkey);
1964             } catch (java.util.MissingResourceException e) {
1965                 return key;
1966             }
1967         }
1968 
itemStateChanged(ItemEvent e)1969         public void itemStateChanged(ItemEvent e) {
1970             Object source = e.getSource();
1971 
1972             if (e.getStateChange() == ItemEvent.SELECTED) {
1973                 if (source == cbSize) {
1974                     int index = cbSize.getSelectedIndex();
1975 
1976                     if ((index >= 0) && (index < sizes.size())) {
1977                         if ((cbSource.getItemCount() > 1) &&
1978                             (cbSource.getSelectedIndex() >= 1))
1979                         {
1980                             int src = cbSource.getSelectedIndex() - 1;
1981                             MediaTray mt = sources.get(src);
1982                             asCurrent.add(new SunAlternateMedia(mt));
1983                         }
1984                         asCurrent.add(sizes.get(index));
1985                     }
1986                 } else if (source == cbSource) {
1987                     int index = cbSource.getSelectedIndex();
1988 
1989                     if ((index >= 1) && (index < (sources.size() + 1))) {
1990                        asCurrent.remove(SunAlternateMedia.class);
1991                        MediaTray newTray = sources.get(index - 1);
1992                        Media m = (Media)asCurrent.get(Media.class);
1993                        if (m == null || m instanceof MediaTray) {
1994                            asCurrent.add(newTray);
1995                        } else if (m instanceof MediaSizeName) {
1996                            MediaSizeName msn = (MediaSizeName)m;
1997                            Media def = (Media)psCurrent.getDefaultAttributeValue(Media.class);
1998                            if (def instanceof MediaSizeName && def.equals(msn)) {
1999                                asCurrent.add(newTray);
2000                            } else {
2001                                /* Non-default paper size, so need to store tray
2002                                 * as SunAlternateMedia
2003                                 */
2004                                asCurrent.add(new SunAlternateMedia(newTray));
2005                            }
2006                        }
2007                     } else if (index == 0) {
2008                         asCurrent.remove(SunAlternateMedia.class);
2009                         if (cbSize.getItemCount() > 0) {
2010                             int size = cbSize.getSelectedIndex();
2011                             asCurrent.add(sizes.get(size));
2012                         }
2013                     }
2014                 }
2015             // orientation affects display of margins.
2016                 if (pnlMargins != null) {
2017                     pnlMargins.updateInfo();
2018                 }
2019             }
2020         }
2021 
2022 
2023         /* this is ad hoc to keep things simple */
addMediaListener(MarginsPanel pnl)2024         public void addMediaListener(MarginsPanel pnl) {
2025             pnlMargins = pnl;
2026         }
updateInfo()2027         public void updateInfo() {
2028             Class<Media> mdCategory = Media.class;
2029             Class<SunAlternateMedia> amCategory = SunAlternateMedia.class;
2030             boolean mediaSupported = false;
2031 
2032             cbSize.removeItemListener(this);
2033             cbSize.removeAllItems();
2034             cbSource.removeItemListener(this);
2035             cbSource.removeAllItems();
2036             cbSource.addItem(getMediaName("auto-select"));
2037 
2038             sizes.clear();
2039             sources.clear();
2040 
2041             if (psCurrent.isAttributeCategorySupported(mdCategory)) {
2042                 mediaSupported = true;
2043 
2044                 Object values =
2045                     psCurrent.getSupportedAttributeValues(mdCategory,
2046                                                           docFlavor,
2047                                                           asCurrent);
2048 
2049                 if (values instanceof Media[]) {
2050                     Media[] media = (Media[])values;
2051 
2052                     for (int i = 0; i < media.length; i++) {
2053                         Media medium = media[i];
2054 
2055                         if (medium instanceof MediaSizeName) {
2056                             sizes.add((MediaSizeName)medium);
2057                             cbSize.addItem(getMediaName(medium.toString()));
2058                         } else if (medium instanceof MediaTray) {
2059                             sources.add((MediaTray)medium);
2060                             cbSource.addItem(getMediaName(medium.toString()));
2061                         }
2062                     }
2063                 }
2064             }
2065 
2066             boolean msSupported = (mediaSupported && (sizes.size() > 0));
2067             lblSize.setEnabled(msSupported);
2068             cbSize.setEnabled(msSupported);
2069 
2070             if (isAWT) {
2071                 cbSource.setEnabled(false);
2072                 lblSource.setEnabled(false);
2073             } else {
2074                 cbSource.setEnabled(mediaSupported);
2075             }
2076 
2077             if (mediaSupported) {
2078 
2079                 Media medium = (Media)asCurrent.get(mdCategory);
2080 
2081                // initialize size selection to default
2082                 Media defMedia = (Media)psCurrent.getDefaultAttributeValue(mdCategory);
2083                 if (defMedia instanceof MediaSizeName) {
2084                     cbSize.setSelectedIndex(sizes.size() > 0 ? sizes.indexOf(defMedia) : -1);
2085                 }
2086 
2087                 if (medium == null ||
2088                     !psCurrent.isAttributeValueSupported(medium,
2089                                                          docFlavor, asCurrent)) {
2090 
2091                     medium = defMedia;
2092 
2093                     if (medium == null) {
2094                         if (sizes.size() > 0) {
2095                             medium = (Media)sizes.get(0);
2096                         }
2097                     }
2098                     if (medium != null) {
2099                         asCurrent.add(medium);
2100                     }
2101                 }
2102                 if (medium != null) {
2103                     if (medium instanceof MediaSizeName) {
2104                         MediaSizeName ms = (MediaSizeName)medium;
2105                         cbSize.setSelectedIndex(sizes.indexOf(ms));
2106                     } else if (medium instanceof MediaTray) {
2107                         MediaTray mt = (MediaTray)medium;
2108                         cbSource.setSelectedIndex(sources.indexOf(mt) + 1);
2109                     }
2110                 } else {
2111                     cbSize.setSelectedIndex(sizes.size() > 0 ? 0 : -1);
2112                     cbSource.setSelectedIndex(0);
2113                 }
2114 
2115                 SunAlternateMedia alt = (SunAlternateMedia)asCurrent.get(amCategory);
2116                 if (alt != null) {
2117                     Media md = alt.getMedia();
2118                     if (md instanceof MediaTray) {
2119                         MediaTray mt = (MediaTray)md;
2120                         cbSource.setSelectedIndex(sources.indexOf(mt) + 1);
2121                     }
2122                 }
2123 
2124                 int selIndex = cbSize.getSelectedIndex();
2125                 if ((selIndex >= 0) && (selIndex < sizes.size())) {
2126                   asCurrent.add(sizes.get(selIndex));
2127                 }
2128 
2129                 selIndex = cbSource.getSelectedIndex();
2130                 if ((selIndex >= 1) && (selIndex < (sources.size()+1))) {
2131                     MediaTray mt = sources.get(selIndex-1);
2132                     if (medium instanceof MediaTray) {
2133                         asCurrent.add(mt);
2134                     } else {
2135                         asCurrent.add(new SunAlternateMedia(mt));
2136                     }
2137                 }
2138 
2139 
2140             }
2141             cbSize.addItemListener(this);
2142             cbSource.addItemListener(this);
2143         }
2144     }
2145 
2146     @SuppressWarnings("serial") // Superclass is not serializable across versions
2147     private class OrientationPanel extends JPanel
2148         implements ActionListener
2149     {
2150         private final String strTitle = getMsg("border.orientation");
2151         private IconRadioButton rbPortrait, rbLandscape,
2152                                 rbRevPortrait, rbRevLandscape;
2153         private MarginsPanel pnlMargins = null;
2154 
OrientationPanel()2155         public OrientationPanel() {
2156             super();
2157 
2158             GridBagLayout gridbag = new GridBagLayout();
2159             GridBagConstraints c = new GridBagConstraints();
2160 
2161             setLayout(gridbag);
2162             setBorder(BorderFactory.createTitledBorder(strTitle));
2163 
2164             c.fill = GridBagConstraints.BOTH;
2165             c.insets = compInsets;
2166             c.weighty = 1.0;
2167             c.gridwidth = GridBagConstraints.REMAINDER;
2168 
2169             ButtonGroup bg = new ButtonGroup();
2170             rbPortrait = new IconRadioButton("radiobutton.portrait",
2171                                              "orientPortrait.png", true,
2172                                              bg, this);
2173             rbPortrait.addActionListener(this);
2174             addToGB(rbPortrait, this, gridbag, c);
2175             rbLandscape = new IconRadioButton("radiobutton.landscape",
2176                                               "orientLandscape.png", false,
2177                                               bg, this);
2178             rbLandscape.addActionListener(this);
2179             addToGB(rbLandscape, this, gridbag, c);
2180             rbRevPortrait = new IconRadioButton("radiobutton.revportrait",
2181                                                 "orientRevPortrait.png", false,
2182                                                 bg, this);
2183             rbRevPortrait.addActionListener(this);
2184             addToGB(rbRevPortrait, this, gridbag, c);
2185             rbRevLandscape = new IconRadioButton("radiobutton.revlandscape",
2186                                                  "orientRevLandscape.png", false,
2187                                                  bg, this);
2188             rbRevLandscape.addActionListener(this);
2189             addToGB(rbRevLandscape, this, gridbag, c);
2190         }
2191 
actionPerformed(ActionEvent e)2192         public void actionPerformed(ActionEvent e) {
2193             Object source = e.getSource();
2194 
2195             if (rbPortrait.isSameAs(source)) {
2196                 asCurrent.add(OrientationRequested.PORTRAIT);
2197             } else if (rbLandscape.isSameAs(source)) {
2198                 asCurrent.add(OrientationRequested.LANDSCAPE);
2199             } else if (rbRevPortrait.isSameAs(source)) {
2200                 asCurrent.add(OrientationRequested.REVERSE_PORTRAIT);
2201             } else if (rbRevLandscape.isSameAs(source)) {
2202                 asCurrent.add(OrientationRequested.REVERSE_LANDSCAPE);
2203             }
2204             // orientation affects display of margins.
2205             if (pnlMargins != null) {
2206                 pnlMargins.updateInfo();
2207             }
2208         }
2209 
2210         /* This is ad hoc to keep things simple */
addOrientationListener(MarginsPanel pnl)2211         void addOrientationListener(MarginsPanel pnl) {
2212             pnlMargins = pnl;
2213         }
2214 
updateInfo()2215         public void updateInfo() {
2216             Class<OrientationRequested> orCategory = OrientationRequested.class;
2217             boolean pSupported = false;
2218             boolean lSupported = false;
2219             boolean rpSupported = false;
2220             boolean rlSupported = false;
2221 
2222             if (isAWT) {
2223                 pSupported = true;
2224                 lSupported = true;
2225             } else
2226             if (psCurrent.isAttributeCategorySupported(orCategory)) {
2227                 Object values =
2228                     psCurrent.getSupportedAttributeValues(orCategory,
2229                                                           docFlavor,
2230                                                           asCurrent);
2231 
2232                 if (values instanceof OrientationRequested[]) {
2233                     OrientationRequested[] ovalues =
2234                         (OrientationRequested[])values;
2235 
2236                     for (int i = 0; i < ovalues.length; i++) {
2237                         OrientationRequested value = ovalues[i];
2238 
2239                         if (value == OrientationRequested.PORTRAIT) {
2240                             pSupported = true;
2241                         } else if (value == OrientationRequested.LANDSCAPE) {
2242                             lSupported = true;
2243                         } else if (value == OrientationRequested.REVERSE_PORTRAIT) {
2244                             rpSupported = true;
2245                         } else if (value == OrientationRequested.REVERSE_LANDSCAPE) {
2246                             rlSupported = true;
2247                         }
2248                     }
2249                 }
2250             }
2251 
2252 
2253             rbPortrait.setEnabled(pSupported);
2254             rbLandscape.setEnabled(lSupported);
2255             rbRevPortrait.setEnabled(rpSupported);
2256             rbRevLandscape.setEnabled(rlSupported);
2257 
2258             OrientationRequested or = (OrientationRequested)asCurrent.get(orCategory);
2259             if (or == null ||
2260                 !psCurrent.isAttributeValueSupported(or, docFlavor, asCurrent)) {
2261 
2262                 or = (OrientationRequested)psCurrent.getDefaultAttributeValue(orCategory);
2263                 // need to validate if default is not supported
2264                 if ((or != null) &&
2265                    !psCurrent.isAttributeValueSupported(or, docFlavor, asCurrent)) {
2266                     or = null;
2267                     Object values =
2268                         psCurrent.getSupportedAttributeValues(orCategory,
2269                                                               docFlavor,
2270                                                               asCurrent);
2271                     if (values instanceof OrientationRequested[]) {
2272                         OrientationRequested[] orValues =
2273                                             (OrientationRequested[])values;
2274                         if (orValues.length > 1) {
2275                             // get the first in the list
2276                             or = orValues[0];
2277                         }
2278                     }
2279                 }
2280 
2281                 if (or == null) {
2282                     or = OrientationRequested.PORTRAIT;
2283                 }
2284                 asCurrent.add(or);
2285             }
2286 
2287             if (or == OrientationRequested.PORTRAIT) {
2288                 rbPortrait.setSelected(true);
2289             } else if (or == OrientationRequested.LANDSCAPE) {
2290                 rbLandscape.setSelected(true);
2291             } else if (or == OrientationRequested.REVERSE_PORTRAIT) {
2292                 rbRevPortrait.setSelected(true);
2293             } else { // if (or == OrientationRequested.REVERSE_LANDSCAPE)
2294                 rbRevLandscape.setSelected(true);
2295             }
2296         }
2297     }
2298 
2299 
2300 
2301     /**
2302      * The "Appearance" tab.  Includes the controls for Chromaticity,
2303      * PrintQuality, JobPriority, JobName, and other related job attributes.
2304      */
2305     @SuppressWarnings("serial") // Superclass is not serializable across versions
2306     private class AppearancePanel extends JPanel {
2307 
2308         private ChromaticityPanel pnlChromaticity;
2309         private QualityPanel pnlQuality;
2310         private JobAttributesPanel pnlJobAttributes;
2311         private SidesPanel pnlSides;
2312 
AppearancePanel()2313         public AppearancePanel() {
2314             super();
2315 
2316             GridBagLayout gridbag = new GridBagLayout();
2317             GridBagConstraints c = new GridBagConstraints();
2318 
2319             setLayout(gridbag);
2320 
2321             c.fill = GridBagConstraints.BOTH;
2322             c.insets = panelInsets;
2323             c.weightx = 1.0;
2324             c.weighty = 1.0;
2325 
2326             c.gridwidth = GridBagConstraints.RELATIVE;
2327             pnlChromaticity = new ChromaticityPanel();
2328             addToGB(pnlChromaticity, this, gridbag, c);
2329 
2330             c.gridwidth = GridBagConstraints.REMAINDER;
2331             pnlQuality = new QualityPanel();
2332             addToGB(pnlQuality, this, gridbag, c);
2333 
2334             c.gridwidth = 1;
2335             pnlSides = new SidesPanel();
2336             addToGB(pnlSides, this, gridbag, c);
2337 
2338             c.gridwidth = GridBagConstraints.REMAINDER;
2339             pnlJobAttributes = new JobAttributesPanel();
2340             addToGB(pnlJobAttributes, this, gridbag, c);
2341 
2342         }
2343 
updateInfo()2344         public void updateInfo() {
2345             pnlChromaticity.updateInfo();
2346             pnlQuality.updateInfo();
2347             pnlSides.updateInfo();
2348             pnlJobAttributes.updateInfo();
2349         }
2350     }
2351 
2352     @SuppressWarnings("serial") // Superclass is not serializable across versions
2353     private class ChromaticityPanel extends JPanel
2354         implements ActionListener
2355     {
2356         private final String strTitle = getMsg("border.chromaticity");
2357         private JRadioButton rbMonochrome, rbColor;
2358 
ChromaticityPanel()2359         public ChromaticityPanel() {
2360             super();
2361 
2362             GridBagLayout gridbag = new GridBagLayout();
2363             GridBagConstraints c = new GridBagConstraints();
2364 
2365             setLayout(gridbag);
2366             setBorder(BorderFactory.createTitledBorder(strTitle));
2367 
2368             c.fill = GridBagConstraints.BOTH;
2369             c.gridwidth = GridBagConstraints.REMAINDER;
2370             c.weighty = 1.0;
2371 
2372             ButtonGroup bg = new ButtonGroup();
2373             rbMonochrome = createRadioButton("radiobutton.monochrome", this);
2374             rbMonochrome.setSelected(true);
2375             bg.add(rbMonochrome);
2376             addToGB(rbMonochrome, this, gridbag, c);
2377             rbColor = createRadioButton("radiobutton.color", this);
2378             bg.add(rbColor);
2379             addToGB(rbColor, this, gridbag, c);
2380         }
2381 
actionPerformed(ActionEvent e)2382         public void actionPerformed(ActionEvent e) {
2383             Object source = e.getSource();
2384 
2385             // REMIND: use isSameAs if we move to a IconRB in the future
2386             if (source == rbMonochrome) {
2387                 asCurrent.add(Chromaticity.MONOCHROME);
2388             } else if (source == rbColor) {
2389                 asCurrent.add(Chromaticity.COLOR);
2390             }
2391         }
2392 
updateInfo()2393         public void updateInfo() {
2394             Class<Chromaticity> chCategory = Chromaticity.class;
2395             boolean monoSupported = false;
2396             boolean colorSupported = false;
2397 
2398             if (isAWT) {
2399                 monoSupported = true;
2400                 colorSupported = true;
2401             } else
2402             if (psCurrent.isAttributeCategorySupported(chCategory)) {
2403                 Object values =
2404                     psCurrent.getSupportedAttributeValues(chCategory,
2405                                                           docFlavor,
2406                                                           asCurrent);
2407 
2408                 if (values instanceof Chromaticity[]) {
2409                     Chromaticity[] cvalues = (Chromaticity[])values;
2410 
2411                     for (int i = 0; i < cvalues.length; i++) {
2412                         Chromaticity value = cvalues[i];
2413 
2414                         if (value == Chromaticity.MONOCHROME) {
2415                             monoSupported = true;
2416                         } else if (value == Chromaticity.COLOR) {
2417                             colorSupported = true;
2418                         }
2419                     }
2420                 }
2421             }
2422 
2423 
2424             rbMonochrome.setEnabled(monoSupported);
2425             rbColor.setEnabled(colorSupported);
2426 
2427             Chromaticity ch = (Chromaticity)asCurrent.get(chCategory);
2428             if (ch == null) {
2429                 ch = (Chromaticity)psCurrent.getDefaultAttributeValue(chCategory);
2430                 if (ch == null) {
2431                     ch = Chromaticity.MONOCHROME;
2432                 }
2433             }
2434 
2435             if (ch == Chromaticity.MONOCHROME) {
2436                 rbMonochrome.setSelected(true);
2437             } else { // if (ch == Chromaticity.COLOR)
2438                 rbColor.setSelected(true);
2439             }
2440         }
2441     }
2442 
2443     @SuppressWarnings("serial") // Superclass is not serializable across versions
2444     private class QualityPanel extends JPanel
2445         implements ActionListener
2446     {
2447         private final String strTitle = getMsg("border.quality");
2448         private JRadioButton rbDraft, rbNormal, rbHigh;
2449 
QualityPanel()2450         public QualityPanel() {
2451             super();
2452 
2453             GridBagLayout gridbag = new GridBagLayout();
2454             GridBagConstraints c = new GridBagConstraints();
2455 
2456             setLayout(gridbag);
2457             setBorder(BorderFactory.createTitledBorder(strTitle));
2458 
2459             c.fill = GridBagConstraints.BOTH;
2460             c.gridwidth = GridBagConstraints.REMAINDER;
2461             c.weighty = 1.0;
2462 
2463             ButtonGroup bg = new ButtonGroup();
2464             rbDraft = createRadioButton("radiobutton.draftq", this);
2465             bg.add(rbDraft);
2466             addToGB(rbDraft, this, gridbag, c);
2467             rbNormal = createRadioButton("radiobutton.normalq", this);
2468             rbNormal.setSelected(true);
2469             bg.add(rbNormal);
2470             addToGB(rbNormal, this, gridbag, c);
2471             rbHigh = createRadioButton("radiobutton.highq", this);
2472             bg.add(rbHigh);
2473             addToGB(rbHigh, this, gridbag, c);
2474         }
2475 
actionPerformed(ActionEvent e)2476         public void actionPerformed(ActionEvent e) {
2477             Object source = e.getSource();
2478 
2479             if (source == rbDraft) {
2480                 asCurrent.add(PrintQuality.DRAFT);
2481             } else if (source == rbNormal) {
2482                 asCurrent.add(PrintQuality.NORMAL);
2483             } else if (source == rbHigh) {
2484                 asCurrent.add(PrintQuality.HIGH);
2485             }
2486         }
2487 
updateInfo()2488         public void updateInfo() {
2489             Class<PrintQuality> pqCategory = PrintQuality.class;
2490             boolean draftSupported = false;
2491             boolean normalSupported = false;
2492             boolean highSupported = false;
2493 
2494             if (isAWT) {
2495                 draftSupported = true;
2496                 normalSupported = true;
2497                 highSupported = true;
2498             } else
2499             if (psCurrent.isAttributeCategorySupported(pqCategory)) {
2500                 Object values =
2501                     psCurrent.getSupportedAttributeValues(pqCategory,
2502                                                           docFlavor,
2503                                                           asCurrent);
2504 
2505                 if (values instanceof PrintQuality[]) {
2506                     PrintQuality[] qvalues = (PrintQuality[])values;
2507 
2508                     for (int i = 0; i < qvalues.length; i++) {
2509                         PrintQuality value = qvalues[i];
2510 
2511                         if (value == PrintQuality.DRAFT) {
2512                             draftSupported = true;
2513                         } else if (value == PrintQuality.NORMAL) {
2514                             normalSupported = true;
2515                         } else if (value == PrintQuality.HIGH) {
2516                             highSupported = true;
2517                         }
2518                     }
2519                 }
2520             }
2521 
2522             rbDraft.setEnabled(draftSupported);
2523             rbNormal.setEnabled(normalSupported);
2524             rbHigh.setEnabled(highSupported);
2525 
2526             PrintQuality pq = (PrintQuality)asCurrent.get(pqCategory);
2527             if (pq == null) {
2528                 pq = (PrintQuality)psCurrent.getDefaultAttributeValue(pqCategory);
2529                 if (pq == null) {
2530                     pq = PrintQuality.NORMAL;
2531                 }
2532             }
2533 
2534             if (pq == PrintQuality.DRAFT) {
2535                 rbDraft.setSelected(true);
2536             } else if (pq == PrintQuality.NORMAL) {
2537                 rbNormal.setSelected(true);
2538             } else { // if (pq == PrintQuality.HIGH)
2539                 rbHigh.setSelected(true);
2540             }
2541         }
2542 
2543 
2544     }
2545 
2546     @SuppressWarnings("serial") // Superclass is not serializable across versions
2547     private class SidesPanel extends JPanel
2548         implements ActionListener
2549     {
2550         private final String strTitle = getMsg("border.sides");
2551         private IconRadioButton rbOneSide, rbTumble, rbDuplex;
2552 
SidesPanel()2553         public SidesPanel() {
2554             super();
2555 
2556             GridBagLayout gridbag = new GridBagLayout();
2557             GridBagConstraints c = new GridBagConstraints();
2558 
2559             setLayout(gridbag);
2560             setBorder(BorderFactory.createTitledBorder(strTitle));
2561 
2562             c.fill = GridBagConstraints.BOTH;
2563             c.insets = compInsets;
2564             c.weighty = 1.0;
2565             c.gridwidth = GridBagConstraints.REMAINDER;
2566 
2567             ButtonGroup bg = new ButtonGroup();
2568             rbOneSide = new IconRadioButton("radiobutton.oneside",
2569                                             "oneside.png", true,
2570                                             bg, this);
2571             rbOneSide.addActionListener(this);
2572             addToGB(rbOneSide, this, gridbag, c);
2573             rbTumble = new IconRadioButton("radiobutton.tumble",
2574                                            "tumble.png", false,
2575                                            bg, this);
2576             rbTumble.addActionListener(this);
2577             addToGB(rbTumble, this, gridbag, c);
2578             rbDuplex = new IconRadioButton("radiobutton.duplex",
2579                                            "duplex.png", false,
2580                                            bg, this);
2581             rbDuplex.addActionListener(this);
2582             c.gridwidth = GridBagConstraints.REMAINDER;
2583             addToGB(rbDuplex, this, gridbag, c);
2584         }
2585 
actionPerformed(ActionEvent e)2586         public void actionPerformed(ActionEvent e) {
2587             Object source = e.getSource();
2588 
2589             if (rbOneSide.isSameAs(source)) {
2590                 asCurrent.add(Sides.ONE_SIDED);
2591             } else if (rbTumble.isSameAs(source)) {
2592                 asCurrent.add(Sides.TUMBLE);
2593             } else if (rbDuplex.isSameAs(source)) {
2594                 asCurrent.add(Sides.DUPLEX);
2595             }
2596         }
2597 
updateInfo()2598         public void updateInfo() {
2599             Class<Sides> sdCategory = Sides.class;
2600             boolean osSupported = false;
2601             boolean tSupported = false;
2602             boolean dSupported = false;
2603 
2604             if (psCurrent.isAttributeCategorySupported(sdCategory)) {
2605                 Object values =
2606                     psCurrent.getSupportedAttributeValues(sdCategory,
2607                                                           docFlavor,
2608                                                           asCurrent);
2609 
2610                 if (values instanceof Sides[]) {
2611                     Sides[] svalues = (Sides[])values;
2612 
2613                     for (int i = 0; i < svalues.length; i++) {
2614                         Sides value = svalues[i];
2615 
2616                         if (value == Sides.ONE_SIDED) {
2617                             osSupported = true;
2618                         } else if (value == Sides.TUMBLE) {
2619                             tSupported = true;
2620                         } else if (value == Sides.DUPLEX) {
2621                             dSupported = true;
2622                         }
2623                     }
2624                 }
2625             }
2626             rbOneSide.setEnabled(osSupported);
2627             rbTumble.setEnabled(tSupported);
2628             rbDuplex.setEnabled(dSupported);
2629 
2630             Sides sd = (Sides)asCurrent.get(sdCategory);
2631             if (sd == null) {
2632                 sd = (Sides)psCurrent.getDefaultAttributeValue(sdCategory);
2633                 if (sd == null) {
2634                     sd = Sides.ONE_SIDED;
2635                 }
2636             }
2637 
2638             if (sd == Sides.ONE_SIDED) {
2639                 rbOneSide.setSelected(true);
2640             } else if (sd == Sides.TUMBLE) {
2641                 rbTumble.setSelected(true);
2642             } else { // if (sd == Sides.DUPLEX)
2643                 rbDuplex.setSelected(true);
2644             }
2645         }
2646     }
2647 
2648 
2649     @SuppressWarnings("serial") // Superclass is not serializable across versions
2650     private class JobAttributesPanel extends JPanel
2651         implements ActionListener, ChangeListener, FocusListener
2652     {
2653         private final String strTitle = getMsg("border.jobattributes");
2654         private JLabel lblPriority, lblJobName, lblUserName;
2655         private JSpinner spinPriority;
2656         private SpinnerNumberModel snModel;
2657         private JCheckBox cbJobSheets;
2658         private JTextField tfJobName, tfUserName;
2659 
JobAttributesPanel()2660         public JobAttributesPanel() {
2661             super();
2662 
2663             GridBagLayout gridbag = new GridBagLayout();
2664             GridBagConstraints c = new GridBagConstraints();
2665 
2666             setLayout(gridbag);
2667             setBorder(BorderFactory.createTitledBorder(strTitle));
2668 
2669             c.fill = GridBagConstraints.NONE;
2670             c.insets = compInsets;
2671             c.weighty = 1.0;
2672 
2673             cbJobSheets = createCheckBox("checkbox.jobsheets", this);
2674             c.anchor = GridBagConstraints.LINE_START;
2675             addToGB(cbJobSheets, this, gridbag, c);
2676 
2677             JPanel pnlTop = new JPanel();
2678             lblPriority = new JLabel(getMsg("label.priority"), JLabel.TRAILING);
2679             lblPriority.setDisplayedMnemonic(getMnemonic("label.priority"));
2680 
2681             pnlTop.add(lblPriority);
2682             snModel = new SpinnerNumberModel(1, 1, 100, 1);
2683             spinPriority = new JSpinner(snModel);
2684             lblPriority.setLabelFor(spinPriority);
2685             // REMIND
2686             ((JSpinner.NumberEditor)spinPriority.getEditor()).getTextField().setColumns(3);
2687             spinPriority.addChangeListener(this);
2688             pnlTop.add(spinPriority);
2689             c.anchor = GridBagConstraints.LINE_END;
2690             c.gridwidth = GridBagConstraints.REMAINDER;
2691             pnlTop.getAccessibleContext().setAccessibleName(
2692                                        getMsg("label.priority"));
2693             addToGB(pnlTop, this, gridbag, c);
2694 
2695             c.fill = GridBagConstraints.HORIZONTAL;
2696             c.anchor = GridBagConstraints.CENTER;
2697             c.weightx = 0.0;
2698             c.gridwidth = 1;
2699             char jmnemonic = getMnemonic("label.jobname");
2700             lblJobName = new JLabel(getMsg("label.jobname"), JLabel.TRAILING);
2701             lblJobName.setDisplayedMnemonic(jmnemonic);
2702             addToGB(lblJobName, this, gridbag, c);
2703             c.weightx = 1.0;
2704             c.gridwidth = GridBagConstraints.REMAINDER;
2705             tfJobName = new JTextField();
2706             lblJobName.setLabelFor(tfJobName);
2707             tfJobName.addFocusListener(this);
2708             tfJobName.setFocusAccelerator(jmnemonic);
2709             tfJobName.getAccessibleContext().setAccessibleName(
2710                                              getMsg("label.jobname"));
2711             addToGB(tfJobName, this, gridbag, c);
2712 
2713             c.weightx = 0.0;
2714             c.gridwidth = 1;
2715             char umnemonic = getMnemonic("label.username");
2716             lblUserName = new JLabel(getMsg("label.username"), JLabel.TRAILING);
2717             lblUserName.setDisplayedMnemonic(umnemonic);
2718             addToGB(lblUserName, this, gridbag, c);
2719             c.gridwidth = GridBagConstraints.REMAINDER;
2720             tfUserName = new JTextField();
2721             lblUserName.setLabelFor(tfUserName);
2722             tfUserName.addFocusListener(this);
2723             tfUserName.setFocusAccelerator(umnemonic);
2724             tfUserName.getAccessibleContext().setAccessibleName(
2725                                              getMsg("label.username"));
2726             addToGB(tfUserName, this, gridbag, c);
2727         }
2728 
actionPerformed(ActionEvent e)2729         public void actionPerformed(ActionEvent e) {
2730             if (cbJobSheets.isSelected()) {
2731                 asCurrent.add(JobSheets.STANDARD);
2732             } else {
2733                 asCurrent.add(JobSheets.NONE);
2734             }
2735         }
2736 
stateChanged(ChangeEvent e)2737         public void stateChanged(ChangeEvent e) {
2738             asCurrent.add(new JobPriority(snModel.getNumber().intValue()));
2739         }
2740 
focusLost(FocusEvent e)2741         public void focusLost(FocusEvent e) {
2742             Object source = e.getSource();
2743 
2744             if (source == tfJobName) {
2745                 asCurrent.add(new JobName(tfJobName.getText(),
2746                                           Locale.getDefault()));
2747             } else if (source == tfUserName) {
2748                 asCurrent.add(new RequestingUserName(tfUserName.getText(),
2749                                                      Locale.getDefault()));
2750             }
2751         }
2752 
focusGained(FocusEvent e)2753         public void focusGained(FocusEvent e) {}
2754 
updateInfo()2755         public void updateInfo() {
2756             Class<JobSheets>          jsCategory = JobSheets.class;
2757             Class<JobPriority>        jpCategory = JobPriority.class;
2758             Class<JobName>            jnCategory = JobName.class;
2759             Class<RequestingUserName> unCategory = RequestingUserName.class;
2760             boolean jsSupported = false;
2761             boolean jpSupported = false;
2762             boolean jnSupported = false;
2763             boolean unSupported = false;
2764 
2765             // setup JobSheets checkbox
2766             if (psCurrent.isAttributeCategorySupported(jsCategory)) {
2767                 jsSupported = true;
2768             }
2769             JobSheets js = (JobSheets)asCurrent.get(jsCategory);
2770             if (js == null) {
2771                 js = (JobSheets)psCurrent.getDefaultAttributeValue(jsCategory);
2772                 if (js == null) {
2773                     js = JobSheets.STANDARD;
2774                 }
2775             }
2776             cbJobSheets.setSelected(js != JobSheets.NONE && jsSupported);
2777             cbJobSheets.setEnabled(jsSupported);
2778 
2779             // setup JobPriority spinner
2780             if (!isAWT && psCurrent.isAttributeCategorySupported(jpCategory)) {
2781                 jpSupported = true;
2782             }
2783             JobPriority jp = (JobPriority)asCurrent.get(jpCategory);
2784             if (jp == null) {
2785                 jp = (JobPriority)psCurrent.getDefaultAttributeValue(jpCategory);
2786                 if (jp == null) {
2787                     jp = new JobPriority(1);
2788                 }
2789             }
2790             int value = jp.getValue();
2791             if ((value < 1) || (value > 100)) {
2792                 value = 1;
2793             }
2794             snModel.setValue(value);
2795             lblPriority.setEnabled(jpSupported);
2796             spinPriority.setEnabled(jpSupported);
2797 
2798             // setup JobName text field
2799             if (psCurrent.isAttributeCategorySupported(jnCategory)) {
2800                 jnSupported = true;
2801             }
2802             JobName jn = (JobName)asCurrent.get(jnCategory);
2803             if (jn == null) {
2804                 jn = (JobName)psCurrent.getDefaultAttributeValue(jnCategory);
2805                 if (jn == null) {
2806                     jn = new JobName("", Locale.getDefault());
2807                 }
2808             }
2809             tfJobName.setText(jn.getValue());
2810             tfJobName.setEnabled(jnSupported);
2811             lblJobName.setEnabled(jnSupported);
2812 
2813             // setup RequestingUserName text field
2814             if (!isAWT && psCurrent.isAttributeCategorySupported(unCategory)) {
2815                 unSupported = true;
2816             }
2817             RequestingUserName un = (RequestingUserName)asCurrent.get(unCategory);
2818             if (un == null) {
2819                 un = (RequestingUserName)psCurrent.getDefaultAttributeValue(unCategory);
2820                 if (un == null) {
2821                     un = new RequestingUserName("", Locale.getDefault());
2822                 }
2823             }
2824             tfUserName.setText(un.getValue());
2825             tfUserName.setEnabled(unSupported);
2826             lblUserName.setEnabled(unSupported);
2827         }
2828     }
2829 
2830 
2831 
2832 
2833     /**
2834      * A special widget that groups a JRadioButton with an associated icon,
2835      * placed to the left of the radio button.
2836      */
2837     @SuppressWarnings("serial") // Superclass is not serializable across versions
2838     private class IconRadioButton extends JPanel {
2839 
2840         private JRadioButton rb;
2841         private JLabel lbl;
2842 
IconRadioButton(String key, String img, boolean selected, ButtonGroup bg, ActionListener al)2843         public IconRadioButton(String key, String img, boolean selected,
2844                                ButtonGroup bg, ActionListener al)
2845         {
2846             super(new FlowLayout(FlowLayout.LEADING));
2847             final URL imgURL = getImageResource(img);
2848             @SuppressWarnings("removal")
2849             Icon icon = java.security.AccessController.doPrivileged(
2850                                  new java.security.PrivilegedAction<Icon>() {
2851                 public Icon run() {
2852                     Icon icon = new ImageIcon(imgURL);
2853                     return icon;
2854                 }
2855             });
2856             lbl = new JLabel(icon);
2857             add(lbl);
2858 
2859             rb = createRadioButton(key, al);
2860             rb.setSelected(selected);
2861             addToBG(rb, this, bg);
2862         }
2863 
addActionListener(ActionListener al)2864         public void addActionListener(ActionListener al) {
2865             rb.addActionListener(al);
2866         }
2867 
isSameAs(Object source)2868         public boolean isSameAs(Object source) {
2869             return (rb == source);
2870         }
2871 
setEnabled(boolean enabled)2872         public void setEnabled(boolean enabled) {
2873             rb.setEnabled(enabled);
2874             lbl.setEnabled(enabled);
2875         }
2876 
isSelected()2877         public boolean isSelected() {
2878             return rb.isSelected();
2879         }
2880 
setSelected(boolean selected)2881         public void setSelected(boolean selected) {
2882             rb.setSelected(selected);
2883         }
2884     }
2885 
2886     /**
2887      * Similar in functionality to the default JFileChooser, except this
2888      * chooser will pop up a "Do you want to overwrite..." dialog if the
2889      * user selects a file that already exists.
2890      */
2891     @SuppressWarnings("serial") // JDK implementation class
2892     private class ValidatingFileChooser extends JFileChooser {
approveSelection()2893         public void approveSelection() {
2894             File selected = getSelectedFile();
2895             boolean exists;
2896 
2897             try {
2898                 exists = selected.exists();
2899             } catch (SecurityException e) {
2900                 exists = false;
2901             }
2902 
2903             if (exists) {
2904                 int val;
2905                 val = JOptionPane.showConfirmDialog(this,
2906                                                     getMsg("dialog.overwrite"),
2907                                                     getMsg("dialog.owtitle"),
2908                                                     JOptionPane.YES_NO_OPTION);
2909                 if (val != JOptionPane.YES_OPTION) {
2910                     return;
2911                 }
2912             }
2913 
2914             try {
2915                 if (selected.createNewFile()) {
2916                     selected.delete();
2917                 }
2918             }  catch (IOException ioe) {
2919                 JOptionPane.showMessageDialog(this,
2920                                    getMsg("dialog.writeerror")+" "+selected,
2921                                    getMsg("dialog.owtitle"),
2922                                    JOptionPane.WARNING_MESSAGE);
2923                 return;
2924             } catch (SecurityException se) {
2925                 //There is already file read/write access so at this point
2926                 // only delete access is denied.  Just ignore it because in
2927                 // most cases the file created in createNewFile gets
2928                 // overwritten anyway.
2929             }
2930             File pFile = selected.getParentFile();
2931             if ((selected.exists() &&
2932                       (!selected.isFile() || !selected.canWrite())) ||
2933                      ((pFile != null) &&
2934                       (!pFile.exists() || (pFile.exists() && !pFile.canWrite())))) {
2935                 JOptionPane.showMessageDialog(this,
2936                                    getMsg("dialog.writeerror")+" "+selected,
2937                                    getMsg("dialog.owtitle"),
2938                                    JOptionPane.WARNING_MESSAGE);
2939                 return;
2940             }
2941 
2942             super.approveSelection();
2943         }
2944     }
2945 }
2946