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