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