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