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 import java.net.MalformedURLException;
  78 
  79 /**
  80  * A class which implements a cross-platform print dialog.
  81  *
  82  * @author  Chris Campbell
  83  */
  84 @SuppressWarnings("serial") // Superclass is not serializable across versions
  85 public class ServiceDialog extends JDialog implements ActionListener {
  86 
  87     /**
  88      * Waiting print status (user response pending).
  89      */
  90     public static final int WAITING = 0;
  91 
  92     /**
  93      * Approve print status (user activated "Print" or "OK").
  94      */
  95     public static final int APPROVE = 1;
  96 
  97     /**
  98      * Cancel print status (user activated "Cancel");
  99      */
 100     public static final int CANCEL = 2;
 101 
 102     private static final String strBundle = "sun.print.resources.serviceui";
 103     private static final Insets panelInsets = new Insets(6, 6, 6, 6);
 104     private static final Insets compInsets = new Insets(3, 6, 3, 6);
 105 
 106     private static ResourceBundle messageRB;
 107     private JTabbedPane tpTabs;
 108     private JButton btnCancel, btnApprove;
 109     private PrintService[] services;
 110     private int defaultServiceIndex;
 111     private PrintRequestAttributeSet asOriginal;
 112     private HashPrintRequestAttributeSet asCurrent;
 113     private PrintService psCurrent;
 114     private DocFlavor docFlavor;
 115     private int status;
 116 
 117     private ValidatingFileChooser jfc;
 118 
 119     private GeneralPanel pnlGeneral;
 120     private PageSetupPanel pnlPageSetup;
 121     private AppearancePanel pnlAppearance;
 122 
 123     private boolean isAWT = false;
 124     static {
 125         initResource();
 126     }
 127 
 128 
 129     /**
 130      * Constructor for the "standard" print dialog (containing all relevant
 131      * tabs)
 132      */
 133     public ServiceDialog(GraphicsConfiguration gc,
 134                          int x, int y,
 135                          PrintService[] services,
 136                          int defaultServiceIndex,
 137                          DocFlavor flavor,
 138                          PrintRequestAttributeSet attributes,
 139                          Dialog dialog)
 140     {
 141         super(dialog, getMsg("dialog.printtitle"), true, gc);
 142         initPrintDialog(x, y, services, defaultServiceIndex,
 143                         flavor, attributes);
 144     }
 145 
 146 
 147 
 148     /**
 149      * Constructor for the "standard" print dialog (containing all relevant
 150      * tabs)
 151      */
 152     public ServiceDialog(GraphicsConfiguration gc,
 153                          int x, int y,
 154                          PrintService[] services,
 155                          int defaultServiceIndex,
 156                          DocFlavor flavor,
 157                          PrintRequestAttributeSet attributes,
 158                          Frame frame)
 159     {
 160         super(frame, getMsg("dialog.printtitle"), true, gc);
 161         initPrintDialog(x, y, services, defaultServiceIndex,
 162                         flavor, attributes);
 163     }
 164 
 165 
 166     /**
 167      * Initialize print dialog.
 168      */
 169     void initPrintDialog(int x, int y,
 170                          PrintService[] services,
 171                          int defaultServiceIndex,
 172                          DocFlavor flavor,
 173                          PrintRequestAttributeSet attributes)
 174     {
 175         this.services = services;
 176         this.defaultServiceIndex = defaultServiceIndex;
 177         this.asOriginal = attributes;
 178         this.asCurrent = new HashPrintRequestAttributeSet(attributes);
 179         this.psCurrent = services[defaultServiceIndex];
 180         this.docFlavor = flavor;
 181         SunPageSelection pages =
 182             (SunPageSelection)attributes.get(SunPageSelection.class);
 183         if (pages != null) {
 184             isAWT = true;
 185         }
 186 
 187         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         @SuppressWarnings("deprecation")
 796         public void actionPerformed(ActionEvent e) {
 797             Object source = e.getSource();
 798 
 799             if (source == btnProperties) {
 800                 if (uiFactory != null) {
 801                     JDialog dialog = (JDialog)uiFactory.getUI(
 802                                                ServiceUIFactory.MAIN_UIROLE,
 803                                                ServiceUIFactory.JDIALOG_UI);
 804 
 805                     if (dialog != null) {
 806                         dialog.show();
 807                     } else {
 808                         DocumentPropertiesUI docPropertiesUI = null;
 809                         try {
 810                             docPropertiesUI =
 811                                 (DocumentPropertiesUI)uiFactory.getUI
 812                                 (DocumentPropertiesUI.DOCUMENTPROPERTIES_ROLE,
 813                                  DocumentPropertiesUI.DOCPROPERTIESCLASSNAME);
 814                         } catch (Exception ex) {
 815                         }
 816                         if (docPropertiesUI != null) {
 817                             PrinterJobWrapper wrapper = (PrinterJobWrapper)
 818                                 asCurrent.get(PrinterJobWrapper.class);
 819                             if (wrapper == null) {
 820                                 return; // should not happen, defensive only.
 821                             }
 822                             PrinterJob job = wrapper.getPrinterJob();
 823                             if (job == null) {
 824                                 return;  // should not happen, defensive only.
 825                             }
 826                             PrintRequestAttributeSet newAttrs =
 827                                docPropertiesUI.showDocumentProperties
 828                                (job, ServiceDialog.this, psCurrent, asCurrent);
 829                             if (newAttrs != null) {
 830                                 asCurrent.addAll(newAttrs);
 831                                 updatePanels();
 832                             }
 833                         }
 834                     }
 835                 }
 836             }
 837         }
 838 
 839         public void itemStateChanged(ItemEvent e) {
 840             if (e.getStateChange() == ItemEvent.SELECTED) {
 841                 int index = cbName.getSelectedIndex();
 842 
 843                 if ((index >= 0) && (index < services.length)) {
 844                     if (!services[index].equals(psCurrent)) {
 845                         psCurrent = services[index];
 846                         uiFactory = psCurrent.getServiceUIFactory();
 847                         changedService = true;
 848 
 849                         Destination dest =
 850                             (Destination)asOriginal.get(Destination.class);
 851                         // to preserve the state of Print To File
 852                         if ((dest != null || isPrintToFileSelected())
 853                             && psCurrent.isAttributeCategorySupported(
 854                                                         Destination.class)) {
 855 
 856                             if (dest != null) {
 857                                 asCurrent.add(dest);
 858                             } else {
 859                                 dest = (Destination)psCurrent.
 860                                     getDefaultAttributeValue(Destination.class);
 861                                 // "dest" should not be null. The following code
 862                                 // is only added to safeguard against a possible
 863                                 // buggy implementation of a PrintService having a
 864                                 // null default Destination.
 865                                 if (dest == null) {
 866                                     try {
 867                                         dest =
 868                                             new Destination(new URI("file:out.prn"));
 869                                     } catch (URISyntaxException ue) {
 870                                     }
 871                                 }
 872 
 873                                 if (dest != null) {
 874                                     asCurrent.add(dest);
 875                                 }
 876                             }
 877                         } else {
 878                             asCurrent.remove(Destination.class);
 879                         }
 880                     }
 881                 }
 882             }
 883         }
 884 
 885         public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
 886             changedService = false;
 887         }
 888 
 889         public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
 890             if (changedService) {
 891                 changedService = false;
 892                 updatePanels();
 893             }
 894         }
 895 
 896         public void popupMenuCanceled(PopupMenuEvent e) {
 897         }
 898 
 899         /**
 900          * We disable the "Print To File" checkbox if this returns false
 901          */
 902         private boolean allowedToPrintToFile() {
 903             try {
 904                 throwPrintToFile();
 905                 return true;
 906             } catch (SecurityException e) {
 907                 return false;
 908             }
 909         }
 910 
 911         /**
 912          * Break this out as it may be useful when we allow API to
 913          * specify printing to a file. In that case its probably right
 914          * to throw a SecurityException if the permission is not granted.
 915          */
 916         private void throwPrintToFile() {
 917             SecurityManager security = System.getSecurityManager();
 918             if (security != null) {
 919                 if (printToFilePermission == null) {
 920                     printToFilePermission =
 921                         new FilePermission("<<ALL FILES>>", "read,write");
 922                 }
 923                 security.checkPermission(printToFilePermission);
 924             }
 925         }
 926 
 927         public void updateInfo() {
 928             Class<Destination> dstCategory = Destination.class;
 929             boolean dstSupported = false;
 930             boolean dstSelected = false;
 931             boolean dstAllowed = filePermission ?
 932                 allowedToPrintToFile() : false;
 933 
 934             // setup Destination (print-to-file) widgets
 935             Destination dst = (Destination)asCurrent.get(dstCategory);
 936             if (dst != null) {
 937                 try {
 938                      dst.getURI().toURL();
 939                      if (psCurrent.isAttributeValueSupported(dst, docFlavor,
 940                                                              asCurrent)) {
 941                          dstSupported = true;
 942                          dstSelected = true;
 943                      }
 944                  } catch (MalformedURLException ex) {
 945                      dstSupported = true;
 946                  }
 947             } else {
 948                 if (psCurrent.isAttributeCategorySupported(dstCategory)) {
 949                     dstSupported = true;
 950                 }
 951             }
 952             cbPrintToFile.setEnabled(dstSupported && dstAllowed);
 953             cbPrintToFile.setSelected(dstSelected && dstAllowed
 954                                       && dstSupported);
 955 
 956             // setup PrintService information widgets
 957             Attribute type = psCurrent.getAttribute(PrinterMakeAndModel.class);
 958             if (type != null) {
 959                 lblType.setText(type.toString());
 960             }
 961             Attribute status =
 962                 psCurrent.getAttribute(PrinterIsAcceptingJobs.class);
 963             if (status != null) {
 964                 lblStatus.setText(getMsg(status.toString()));
 965             }
 966             Attribute info = psCurrent.getAttribute(PrinterInfo.class);
 967             if (info != null) {
 968                 lblInfo.setText(info.toString());
 969             }
 970             btnProperties.setEnabled(uiFactory != null);
 971         }
 972     }
 973 
 974     @SuppressWarnings("serial") // Superclass is not serializable across versions
 975     private class PrintRangePanel extends JPanel
 976         implements ActionListener, FocusListener
 977     {
 978         private final String strTitle = getMsg("border.printrange");
 979         private final PageRanges prAll = new PageRanges(1, Integer.MAX_VALUE);
 980         private JRadioButton rbAll, rbPages, rbSelect;
 981         private JFormattedTextField tfRangeFrom, tfRangeTo;
 982         private JLabel lblRangeTo;
 983         private boolean prSupported;
 984         private boolean prPgRngSupported;
 985 
 986         public PrintRangePanel() {
 987             super();
 988 
 989             GridBagLayout gridbag = new GridBagLayout();
 990             GridBagConstraints c = new GridBagConstraints();
 991 
 992             setLayout(gridbag);
 993             setBorder(BorderFactory.createTitledBorder(strTitle));
 994 
 995             c.fill = GridBagConstraints.BOTH;
 996             c.insets = compInsets;
 997             c.gridwidth = GridBagConstraints.REMAINDER;
 998 
 999             ButtonGroup bg = new ButtonGroup();
1000             JPanel pnlTop = new JPanel(new FlowLayout(FlowLayout.LEADING));
1001             rbAll = createRadioButton("radiobutton.rangeall", this);
1002             rbAll.setSelected(true);
1003             bg.add(rbAll);
1004             pnlTop.add(rbAll);
1005             addToGB(pnlTop, this, gridbag, c);
1006 
1007             // Selection never seemed to work so I'm commenting this part.
1008             /*
1009             if (isAWT) {
1010                 JPanel pnlMiddle  =
1011                     new JPanel(new FlowLayout(FlowLayout.LEADING));
1012                 rbSelect =
1013                     createRadioButton("radiobutton.selection", this);
1014                 bg.add(rbSelect);
1015                 pnlMiddle.add(rbSelect);
1016                 addToGB(pnlMiddle, this, gridbag, c);
1017             }
1018             */
1019 
1020             JPanel pnlBottom = new JPanel(new FlowLayout(FlowLayout.LEADING));
1021             rbPages = createRadioButton("radiobutton.rangepages", this);
1022             bg.add(rbPages);
1023             pnlBottom.add(rbPages);
1024             DecimalFormat format = new DecimalFormat("####0");
1025             format.setMinimumFractionDigits(0);
1026             format.setMaximumFractionDigits(0);
1027             format.setMinimumIntegerDigits(0);
1028             format.setMaximumIntegerDigits(5);
1029             format.setParseIntegerOnly(true);
1030             format.setDecimalSeparatorAlwaysShown(false);
1031             NumberFormatter nf = new NumberFormatter(format);
1032             nf.setMinimum(1);
1033             nf.setMaximum(Integer.MAX_VALUE);
1034             nf.setAllowsInvalid(true);
1035             nf.setCommitsOnValidEdit(true);
1036             tfRangeFrom = new JFormattedTextField(nf);
1037             tfRangeFrom.setColumns(4);
1038             tfRangeFrom.setEnabled(false);
1039             tfRangeFrom.addActionListener(this);
1040             tfRangeFrom.addFocusListener(this);
1041             tfRangeFrom.setFocusLostBehavior(
1042                 JFormattedTextField.PERSIST);
1043             tfRangeFrom.getAccessibleContext().setAccessibleName(
1044                                           getMsg("radiobutton.rangepages"));
1045             pnlBottom.add(tfRangeFrom);
1046             lblRangeTo = new JLabel(getMsg("label.rangeto"));
1047             lblRangeTo.setEnabled(false);
1048             pnlBottom.add(lblRangeTo);
1049             NumberFormatter nfto;
1050             try {
1051                 nfto = (NumberFormatter)nf.clone();
1052             } catch (CloneNotSupportedException e) {
1053                 nfto = new NumberFormatter();
1054             }
1055             tfRangeTo = new JFormattedTextField(nfto);
1056             tfRangeTo.setColumns(4);
1057             tfRangeTo.setEnabled(false);
1058             tfRangeTo.addFocusListener(this);
1059             tfRangeTo.getAccessibleContext().setAccessibleName(
1060                                           getMsg("label.rangeto"));
1061             pnlBottom.add(tfRangeTo);
1062             addToGB(pnlBottom, this, gridbag, c);
1063         }
1064 
1065         public void actionPerformed(ActionEvent e) {
1066             Object source = e.getSource();
1067             SunPageSelection select = SunPageSelection.ALL;
1068 
1069             setupRangeWidgets();
1070 
1071             if (source == rbAll) {
1072                 asCurrent.add(prAll);
1073             } else if (source == rbSelect) {
1074                 select = SunPageSelection.SELECTION;
1075             } else if (source == rbPages ||
1076                        source == tfRangeFrom ||
1077                        source == tfRangeTo) {
1078                 updateRangeAttribute();
1079                 select = SunPageSelection.RANGE;
1080             }
1081 
1082             if (isAWT) {
1083                 asCurrent.add(select);
1084             }
1085         }
1086 
1087         public void focusLost(FocusEvent e) {
1088             Object source = e.getSource();
1089 
1090             if ((source == tfRangeFrom) || (source == tfRangeTo)) {
1091                 updateRangeAttribute();
1092             }
1093         }
1094 
1095         public void focusGained(FocusEvent e) {}
1096 
1097         private void setupRangeWidgets() {
1098             boolean rangeEnabled = (rbPages.isSelected() && prPgRngSupported);
1099             tfRangeFrom.setEnabled(rangeEnabled);
1100             tfRangeTo.setEnabled(rangeEnabled);
1101             lblRangeTo.setEnabled(rangeEnabled);
1102         }
1103 
1104         private void updateRangeAttribute() {
1105             String strFrom = tfRangeFrom.getText();
1106             String strTo = tfRangeTo.getText();
1107 
1108             int min;
1109             int max;
1110 
1111             try {
1112                 min = Integer.parseInt(strFrom);
1113             } catch (NumberFormatException e) {
1114                 min = 1;
1115             }
1116 
1117             try {
1118                 max = Integer.parseInt(strTo);
1119             } catch (NumberFormatException e) {
1120                 max = min;
1121             }
1122 
1123             if (min < 1) {
1124                 min = 1;
1125                 tfRangeFrom.setValue(1);
1126             }
1127 
1128             if (max < min) {
1129                 max = min;
1130                 tfRangeTo.setValue(min);
1131             }
1132 
1133             PageRanges pr = new PageRanges(min, max);
1134             asCurrent.add(pr);
1135         }
1136 
1137         public void updateInfo() {
1138             Class<PageRanges> prCategory = PageRanges.class;
1139             prSupported = false;
1140 
1141             if (psCurrent.isAttributeCategorySupported(prCategory) ||
1142                    isAWT) {
1143                 prSupported = true;
1144                 prPgRngSupported = psCurrent.isAttributeValueSupported(prAll,
1145                                                                      docFlavor,
1146                                                                      asCurrent);
1147             }
1148 
1149             SunPageSelection select = SunPageSelection.ALL;
1150             int min = 1;
1151             int max = 1;
1152 
1153             PageRanges pr = (PageRanges)asCurrent.get(prCategory);
1154             if (pr != null) {
1155                 if (!pr.equals(prAll)) {
1156                     select = SunPageSelection.RANGE;
1157 
1158                     int[][] members = pr.getMembers();
1159                     if ((members.length > 0) &&
1160                         (members[0].length > 1)) {
1161                         min = members[0][0];
1162                         max = members[0][1];
1163                     }
1164                 }
1165             }
1166 
1167             if (isAWT) {
1168                 select = (SunPageSelection)asCurrent.get(
1169                                                 SunPageSelection.class);
1170             }
1171 
1172             if (select == SunPageSelection.ALL) {
1173                 rbAll.setSelected(true);
1174             } else if (select == SunPageSelection.SELECTION) {
1175                 // Comment this for now -  rbSelect is not initialized
1176                 // because Selection button is not added.
1177                 // See PrintRangePanel above.
1178 
1179                 //rbSelect.setSelected(true);
1180             } else { // RANGE
1181                 rbPages.setSelected(true);
1182             }
1183             tfRangeFrom.setValue(min);
1184             tfRangeTo.setValue(max);
1185             rbAll.setEnabled(prSupported);
1186             rbPages.setEnabled(prPgRngSupported);
1187             setupRangeWidgets();
1188         }
1189     }
1190 
1191     @SuppressWarnings("serial") // Superclass is not serializable across versions
1192     private class CopiesPanel extends JPanel
1193         implements ActionListener, ChangeListener
1194     {
1195         private final String strTitle = getMsg("border.copies");
1196         private SpinnerNumberModel snModel;
1197         private JSpinner spinCopies;
1198         private JLabel lblCopies;
1199         private JCheckBox cbCollate;
1200         private boolean scSupported;
1201 
1202         public CopiesPanel() {
1203             super();
1204 
1205             GridBagLayout gridbag = new GridBagLayout();
1206             GridBagConstraints c = new GridBagConstraints();
1207 
1208             setLayout(gridbag);
1209             setBorder(BorderFactory.createTitledBorder(strTitle));
1210 
1211             c.fill = GridBagConstraints.HORIZONTAL;
1212             c.insets = compInsets;
1213 
1214             lblCopies = new JLabel(getMsg("label.numcopies"), JLabel.TRAILING);
1215             lblCopies.setDisplayedMnemonic(getMnemonic("label.numcopies"));
1216             lblCopies.getAccessibleContext().setAccessibleName(
1217                                              getMsg("label.numcopies"));
1218             addToGB(lblCopies, this, gridbag, c);
1219 
1220             snModel = new SpinnerNumberModel(1, 1, 999, 1);
1221             spinCopies = new JSpinner(snModel);
1222             lblCopies.setLabelFor(spinCopies);
1223             // REMIND
1224             ((JSpinner.NumberEditor)spinCopies.getEditor()).getTextField().setColumns(3);
1225             spinCopies.addChangeListener(this);
1226             c.gridwidth = GridBagConstraints.REMAINDER;
1227             addToGB(spinCopies, this, gridbag, c);
1228 
1229             cbCollate = createCheckBox("checkbox.collate", this);
1230             cbCollate.setEnabled(false);
1231             addToGB(cbCollate, this, gridbag, c);
1232         }
1233 
1234         public void actionPerformed(ActionEvent e) {
1235             if (cbCollate.isSelected()) {
1236                 asCurrent.add(SheetCollate.COLLATED);
1237             } else {
1238                 asCurrent.add(SheetCollate.UNCOLLATED);
1239             }
1240         }
1241 
1242         public void stateChanged(ChangeEvent e) {
1243             updateCollateCB();
1244 
1245             asCurrent.add(new Copies(snModel.getNumber().intValue()));
1246         }
1247 
1248         private void updateCollateCB() {
1249             int num = snModel.getNumber().intValue();
1250             if (isAWT) {
1251                 // sync with IPPPrintService#getSupportedAttributeCategories
1252                 // where for linux, SheetCollate is not added to supported category
1253                 String osname = java.security.AccessController.doPrivileged(
1254                             new sun.security.action.GetPropertyAction("os.name"));
1255                 if (osname.equals("Linux")) {
1256                     cbCollate.setEnabled((num > 1) && scSupported);
1257                 } else {
1258                     cbCollate.setEnabled(true);
1259                 }
1260             } else {
1261                 cbCollate.setEnabled((num > 1) && scSupported);
1262             }
1263         }
1264 
1265         public void updateInfo() {
1266             Class<Copies> cpCategory = Copies.class;
1267             Class<SheetCollate> scCategory = SheetCollate.class;
1268             boolean cpSupported = false;
1269             scSupported = false;
1270 
1271             // setup Copies spinner
1272             if (psCurrent.isAttributeCategorySupported(cpCategory)) {
1273                 cpSupported = true;
1274             }
1275             CopiesSupported cs =
1276                 (CopiesSupported)psCurrent.getSupportedAttributeValues(
1277                                                        cpCategory, null, null);
1278             if (cs == null) {
1279                 cs = new CopiesSupported(1, 999);
1280             }
1281             Copies cp = (Copies)asCurrent.get(cpCategory);
1282             if (cp == null) {
1283                 cp = (Copies)psCurrent.getDefaultAttributeValue(cpCategory);
1284                 if (cp == null) {
1285                     cp = new Copies(1);
1286                 }
1287             }
1288             spinCopies.setEnabled(cpSupported);
1289             lblCopies.setEnabled(cpSupported);
1290 
1291             int[][] members = cs.getMembers();
1292             int min, max;
1293             if ((members.length > 0) && (members[0].length > 0)) {
1294                 min = members[0][0];
1295                 max = members[0][1];
1296             } else {
1297                 min = 1;
1298                 max = Integer.MAX_VALUE;
1299             }
1300             snModel.setMinimum(min);
1301             snModel.setMaximum(max);
1302 
1303             int value = cp.getValue();
1304             if ((value < min) || (value > max)) {
1305                 value = min;
1306             }
1307             snModel.setValue(value);
1308 
1309             // setup Collate checkbox
1310             if (psCurrent.isAttributeCategorySupported(scCategory)) {
1311                 scSupported = true;
1312             }
1313             SheetCollate sc = (SheetCollate)asCurrent.get(scCategory);
1314             if (sc == null) {
1315                 sc = (SheetCollate)psCurrent.getDefaultAttributeValue(scCategory);
1316                 if (sc == null) {
1317                     sc = SheetCollate.UNCOLLATED;
1318                 }
1319                 if (sc != null &&
1320                     !psCurrent.isAttributeValueSupported(sc, docFlavor, asCurrent)) {
1321                     scSupported = false;
1322                 }
1323             } else {
1324                 if (!psCurrent.isAttributeValueSupported(sc, docFlavor, asCurrent)) {
1325                     scSupported = false;
1326                 }
1327             }
1328             cbCollate.setSelected(sc == SheetCollate.COLLATED && scSupported);
1329             updateCollateCB();
1330         }
1331     }
1332 
1333 
1334 
1335 
1336     /**
1337      * The "Page Setup" tab.  Includes the controls for MediaSource/MediaTray,
1338      * OrientationRequested, and Sides.
1339      */
1340     @SuppressWarnings("serial") // Superclass is not serializable across versions
1341     private class PageSetupPanel extends JPanel {
1342 
1343         private MediaPanel pnlMedia;
1344         private OrientationPanel pnlOrientation;
1345         private MarginsPanel pnlMargins;
1346 
1347         public PageSetupPanel() {
1348             super();
1349 
1350             GridBagLayout gridbag = new GridBagLayout();
1351             GridBagConstraints c = new GridBagConstraints();
1352 
1353             setLayout(gridbag);
1354 
1355             c.fill = GridBagConstraints.BOTH;
1356             c.insets = panelInsets;
1357             c.weightx = 1.0;
1358             c.weighty = 1.0;
1359 
1360             c.gridwidth = GridBagConstraints.REMAINDER;
1361             pnlMedia = new MediaPanel();
1362             addToGB(pnlMedia, this, gridbag, c);
1363 
1364             pnlOrientation = new OrientationPanel();
1365             c.gridwidth = GridBagConstraints.RELATIVE;
1366             addToGB(pnlOrientation, this, gridbag, c);
1367 
1368             pnlMargins = new MarginsPanel();
1369             pnlOrientation.addOrientationListener(pnlMargins);
1370             pnlMedia.addMediaListener(pnlMargins);
1371             c.gridwidth = GridBagConstraints.REMAINDER;
1372             addToGB(pnlMargins, this, gridbag, c);
1373         }
1374 
1375         public void updateInfo() {
1376             pnlMedia.updateInfo();
1377             pnlOrientation.updateInfo();
1378             pnlMargins.updateInfo();
1379         }
1380     }
1381 
1382     @SuppressWarnings("serial") // Superclass is not serializable across versions
1383     private class MarginsPanel extends JPanel
1384                                implements ActionListener, FocusListener {
1385 
1386         private final String strTitle = getMsg("border.margins");
1387         private JFormattedTextField leftMargin, rightMargin,
1388                                     topMargin, bottomMargin;
1389         private JLabel lblLeft, lblRight, lblTop, lblBottom;
1390         private int units = MediaPrintableArea.MM;
1391         // storage for the last margin values calculated, -ve is uninitialised
1392         private float lmVal = -1f,rmVal = -1f, tmVal = -1f, bmVal = -1f;
1393         // storage for margins as objects mapped into orientation for display
1394         private Float lmObj,rmObj,tmObj,bmObj;
1395 
1396         public MarginsPanel() {
1397             super();
1398 
1399             GridBagLayout gridbag = new GridBagLayout();
1400             GridBagConstraints c = new GridBagConstraints();
1401             c.fill = GridBagConstraints.HORIZONTAL;
1402             c.weightx = 1.0;
1403             c.weighty = 0.0;
1404             c.insets = compInsets;
1405 
1406             setLayout(gridbag);
1407             setBorder(BorderFactory.createTitledBorder(strTitle));
1408 
1409             String unitsKey = "label.millimetres";
1410             String defaultCountry = Locale.getDefault().getCountry();
1411             if (defaultCountry != null &&
1412                 (defaultCountry.equals("") ||
1413                  defaultCountry.equals(Locale.US.getCountry()) ||
1414                  defaultCountry.equals(Locale.CANADA.getCountry()))) {
1415                 unitsKey = "label.inches";
1416                 units = MediaPrintableArea.INCH;
1417             }
1418             String unitsMsg = getMsg(unitsKey);
1419 
1420             DecimalFormat format;
1421             if (units == MediaPrintableArea.MM) {
1422                 format = new DecimalFormat("###.##");
1423                 format.setMaximumIntegerDigits(3);
1424             } else {
1425                 format = new DecimalFormat("##.##");
1426                 format.setMaximumIntegerDigits(2);
1427             }
1428 
1429             format.setMinimumFractionDigits(1);
1430             format.setMaximumFractionDigits(2);
1431             format.setMinimumIntegerDigits(1);
1432             format.setParseIntegerOnly(false);
1433             format.setDecimalSeparatorAlwaysShown(true);
1434             NumberFormatter nf = new NumberFormatter(format);
1435             nf.setMinimum(Float.valueOf(0.0f));
1436             nf.setMaximum(Float.valueOf(999.0f));
1437             nf.setAllowsInvalid(true);
1438             nf.setCommitsOnValidEdit(true);
1439 
1440             leftMargin = new JFormattedTextField(nf);
1441             leftMargin.addFocusListener(this);
1442             leftMargin.addActionListener(this);
1443             leftMargin.getAccessibleContext().setAccessibleName(
1444                                               getMsg("label.leftmargin"));
1445             rightMargin = new JFormattedTextField(nf);
1446             rightMargin.addFocusListener(this);
1447             rightMargin.addActionListener(this);
1448             rightMargin.getAccessibleContext().setAccessibleName(
1449                                               getMsg("label.rightmargin"));
1450             topMargin = new JFormattedTextField(nf);
1451             topMargin.addFocusListener(this);
1452             topMargin.addActionListener(this);
1453             topMargin.getAccessibleContext().setAccessibleName(
1454                                               getMsg("label.topmargin"));
1455 
1456             bottomMargin = new JFormattedTextField(nf);
1457             bottomMargin.addFocusListener(this);
1458             bottomMargin.addActionListener(this);
1459             bottomMargin.getAccessibleContext().setAccessibleName(
1460                                               getMsg("label.bottommargin"));
1461 
1462             c.gridwidth = GridBagConstraints.RELATIVE;
1463             lblLeft = new JLabel(getMsg("label.leftmargin") + " " + unitsMsg,
1464                                  JLabel.LEADING);
1465             lblLeft.setDisplayedMnemonic(getMnemonic("label.leftmargin"));
1466             lblLeft.setLabelFor(leftMargin);
1467             addToGB(lblLeft, this, gridbag, c);
1468 
1469             c.gridwidth = GridBagConstraints.REMAINDER;
1470             lblRight = new JLabel(getMsg("label.rightmargin") + " " + unitsMsg,
1471                                   JLabel.LEADING);
1472             lblRight.setDisplayedMnemonic(getMnemonic("label.rightmargin"));
1473             lblRight.setLabelFor(rightMargin);
1474             addToGB(lblRight, this, gridbag, c);
1475 
1476             c.gridwidth = GridBagConstraints.RELATIVE;
1477             addToGB(leftMargin, this, gridbag, c);
1478 
1479             c.gridwidth = GridBagConstraints.REMAINDER;
1480             addToGB(rightMargin, this, gridbag, c);
1481 
1482             // add an invisible spacing component.
1483             addToGB(new JPanel(), this, gridbag, c);
1484 
1485             c.gridwidth = GridBagConstraints.RELATIVE;
1486             lblTop = new JLabel(getMsg("label.topmargin") + " " + unitsMsg,
1487                                 JLabel.LEADING);
1488             lblTop.setDisplayedMnemonic(getMnemonic("label.topmargin"));
1489             lblTop.setLabelFor(topMargin);
1490             addToGB(lblTop, this, gridbag, c);
1491 
1492             c.gridwidth = GridBagConstraints.REMAINDER;
1493             lblBottom = new JLabel(getMsg("label.bottommargin") +
1494                                    " " + unitsMsg, JLabel.LEADING);
1495             lblBottom.setDisplayedMnemonic(getMnemonic("label.bottommargin"));
1496             lblBottom.setLabelFor(bottomMargin);
1497             addToGB(lblBottom, this, gridbag, c);
1498 
1499             c.gridwidth = GridBagConstraints.RELATIVE;
1500             addToGB(topMargin, this, gridbag, c);
1501 
1502             c.gridwidth = GridBagConstraints.REMAINDER;
1503             addToGB(bottomMargin, this, gridbag, c);
1504 
1505         }
1506 
1507         public void actionPerformed(ActionEvent e) {
1508             Object source = e.getSource();
1509             updateMargins(source);
1510         }
1511 
1512         public void focusLost(FocusEvent e) {
1513             Object source = e.getSource();
1514             updateMargins(source);
1515         }
1516 
1517         public void focusGained(FocusEvent e) {}
1518 
1519         /* Get the numbers, use to create a MPA.
1520          * If its valid, accept it and update the attribute set.
1521          * If its not valid, then reject it and call updateInfo()
1522          * to re-establish the previous entries.
1523          */
1524         public void updateMargins(Object source) {
1525             if (!(source instanceof JFormattedTextField)) {
1526                 return;
1527             } else {
1528                 JFormattedTextField tf = (JFormattedTextField)source;
1529                 Float val = (Float)tf.getValue();
1530                 if (val == null) {
1531                     return;
1532                 }
1533                 if (tf == leftMargin && val.equals(lmObj)) {
1534                     return;
1535                 }
1536                 if (tf == rightMargin && val.equals(rmObj)) {
1537                     return;
1538                 }
1539                 if (tf == topMargin && val.equals(tmObj)) {
1540                     return;
1541                 }
1542                 if (tf == bottomMargin && val.equals(bmObj)) {
1543                     return;
1544                 }
1545             }
1546 
1547             Float lmTmpObj = (Float)leftMargin.getValue();
1548             Float rmTmpObj = (Float)rightMargin.getValue();
1549             Float tmTmpObj = (Float)topMargin.getValue();
1550             Float bmTmpObj = (Float)bottomMargin.getValue();
1551 
1552             float lm = lmTmpObj.floatValue();
1553             float rm = rmTmpObj.floatValue();
1554             float tm = tmTmpObj.floatValue();
1555             float bm = bmTmpObj.floatValue();
1556 
1557             /* adjust for orientation */
1558             Class<OrientationRequested> orCategory = OrientationRequested.class;
1559             OrientationRequested or =
1560                 (OrientationRequested)asCurrent.get(orCategory);
1561 
1562             if (or == null) {
1563                 or = (OrientationRequested)
1564                      psCurrent.getDefaultAttributeValue(orCategory);
1565             }
1566 
1567             float tmp;
1568             if (or == OrientationRequested.REVERSE_PORTRAIT) {
1569                 tmp = lm; lm = rm; rm = tmp;
1570                 tmp = tm; tm = bm; bm = tmp;
1571             } else if (or == OrientationRequested.LANDSCAPE) {
1572                 tmp = lm;
1573                 lm = tm;
1574                 tm = rm;
1575                 rm = bm;
1576                 bm = tmp;
1577             } else if (or == OrientationRequested.REVERSE_LANDSCAPE) {
1578                 tmp = lm;
1579                 lm = bm;
1580                 bm = rm;
1581                 rm = tm;
1582                 tm = tmp;
1583             }
1584             MediaPrintableArea mpa;
1585             if ((mpa = validateMargins(lm, rm, tm, bm)) != null) {
1586                 asCurrent.add(mpa);
1587                 lmVal = lm;
1588                 rmVal = rm;
1589                 tmVal = tm;
1590                 bmVal = bm;
1591                 lmObj = lmTmpObj;
1592                 rmObj = rmTmpObj;
1593                 tmObj = tmTmpObj;
1594                 bmObj = bmTmpObj;
1595             } else {
1596                 if (lmObj == null || rmObj == null ||
1597                     tmObj == null || bmObj == null) {
1598                     return;
1599                 } else {
1600                     leftMargin.setValue(lmObj);
1601                     rightMargin.setValue(rmObj);
1602                     topMargin.setValue(tmObj);
1603                     bottomMargin.setValue(bmObj);
1604 
1605                 }
1606             }
1607         }
1608 
1609         /*
1610          * This method either accepts the values and creates a new
1611          * MediaPrintableArea, or does nothing.
1612          * It should not attempt to create a printable area from anything
1613          * other than the exact values passed in.
1614          * But REMIND/TBD: it would be user friendly to replace margins the
1615          * user entered but are out of bounds with the minimum.
1616          * At that point this method will need to take responsibility for
1617          * updating the "stored" values and the UI.
1618          */
1619         private MediaPrintableArea validateMargins(float lm, float rm,
1620                                                    float tm, float bm) {
1621 
1622             Class<MediaPrintableArea> mpaCategory = MediaPrintableArea.class;
1623             MediaPrintableArea mpa;
1624             MediaPrintableArea mpaMax = null;
1625             MediaSize mediaSize = null;
1626 
1627             Media media = (Media)asCurrent.get(Media.class);
1628             if (media == null || !(media instanceof MediaSizeName)) {
1629                 media = (Media)psCurrent.getDefaultAttributeValue(Media.class);
1630             }
1631             if (media != null && (media instanceof MediaSizeName)) {
1632                 MediaSizeName msn = (MediaSizeName)media;
1633                 mediaSize = MediaSize.getMediaSizeForName(msn);
1634             }
1635             if (mediaSize == null) {
1636                 mediaSize = new MediaSize(8.5f, 11f, Size2DSyntax.INCH);
1637             }
1638 
1639             if (media != null) {
1640                 PrintRequestAttributeSet tmpASet =
1641                     new HashPrintRequestAttributeSet(asCurrent);
1642                 tmpASet.add(media);
1643 
1644                 Object values =
1645                     psCurrent.getSupportedAttributeValues(mpaCategory,
1646                                                           docFlavor,
1647                                                           tmpASet);
1648                 if (values instanceof MediaPrintableArea[] &&
1649                     ((MediaPrintableArea[])values).length > 0) {
1650                     mpaMax = ((MediaPrintableArea[])values)[0];
1651 
1652                 }
1653             }
1654             if (mpaMax == null) {
1655                 mpaMax = new MediaPrintableArea(0f, 0f,
1656                                                 mediaSize.getX(units),
1657                                                 mediaSize.getY(units),
1658                                                 units);
1659             }
1660 
1661             float wid = mediaSize.getX(units);
1662             float hgt = mediaSize.getY(units);
1663             float pax = lm;
1664             float pay = tm;
1665             float par = rm;
1666             float pab = bm;
1667             float paw = wid - lm - rm;
1668             float pah = hgt - tm - bm;
1669 
1670             if (paw <= 0f || pah <= 0f || pax < 0f || pay < 0f ||
1671                 par <= 0f || pab <= 0f ||
1672                 pax < mpaMax.getX(units) || paw > mpaMax.getWidth(units) ||
1673                 pay < mpaMax.getY(units) || pah > mpaMax.getHeight(units)) {
1674                 return null;
1675             } else {
1676                 return new MediaPrintableArea(lm, tm, paw, pah, units);
1677             }
1678         }
1679 
1680         /* This is complex as a MediaPrintableArea is valid only within
1681          * a particular context of media size.
1682          * So we need a MediaSize as well as a MediaPrintableArea.
1683          * MediaSize can be obtained from MediaSizeName.
1684          * If the application specifies a MediaPrintableArea, we accept it
1685          * to the extent its valid for the Media they specify. If they
1686          * don't specify a Media, then the default is assumed.
1687          *
1688          * If an application doesn't define a MediaPrintableArea, we need to
1689          * create a suitable one, this is created using the specified (or
1690          * default) Media and default 1 inch margins. This is validated
1691          * against the paper in case this is too large for tiny media.
1692          */
1693         public void updateInfo() {
1694 
1695             if (isAWT) {
1696                 leftMargin.setEnabled(false);
1697                 rightMargin.setEnabled(false);
1698                 topMargin.setEnabled(false);
1699                 bottomMargin.setEnabled(false);
1700                 lblLeft.setEnabled(false);
1701                 lblRight.setEnabled(false);
1702                 lblTop.setEnabled(false);
1703                 lblBottom.setEnabled(false);
1704                 return;
1705             }
1706 
1707             Class<MediaPrintableArea> mpaCategory = MediaPrintableArea.class;
1708             MediaPrintableArea mpa =
1709                  (MediaPrintableArea)asCurrent.get(mpaCategory);
1710             MediaPrintableArea mpaMax = null;
1711             MediaSize mediaSize = null;
1712 
1713             Media media = (Media)asCurrent.get(Media.class);
1714             if (media == null || !(media instanceof MediaSizeName)) {
1715                 media = (Media)psCurrent.getDefaultAttributeValue(Media.class);
1716             }
1717             if (media != null && (media instanceof MediaSizeName)) {
1718                 MediaSizeName msn = (MediaSizeName)media;
1719                 mediaSize = MediaSize.getMediaSizeForName(msn);
1720             }
1721             if (mediaSize == null) {
1722                 mediaSize = new MediaSize(8.5f, 11f, Size2DSyntax.INCH);
1723             }
1724 
1725             if (media != null) {
1726                 PrintRequestAttributeSet tmpASet =
1727                     new HashPrintRequestAttributeSet(asCurrent);
1728                 tmpASet.add(media);
1729 
1730                 Object values =
1731                     psCurrent.getSupportedAttributeValues(mpaCategory,
1732                                                           docFlavor,
1733                                                           tmpASet);
1734                 if (values instanceof MediaPrintableArea[] &&
1735                     ((MediaPrintableArea[])values).length > 0) {
1736                     mpaMax = ((MediaPrintableArea[])values)[0];
1737 
1738                 } else if (values instanceof MediaPrintableArea) {
1739                     mpaMax = (MediaPrintableArea)values;
1740                 }
1741             }
1742             if (mpaMax == null) {
1743                 mpaMax = new MediaPrintableArea(0f, 0f,
1744                                                 mediaSize.getX(units),
1745                                                 mediaSize.getY(units),
1746                                                 units);
1747             }
1748 
1749             /*
1750              * At this point we now know as best we can :-
1751              * - the media size
1752              * - the maximum corresponding printable area
1753              * - the media printable area specified by the client, if any.
1754              * The next step is to create a default MPA if none was specified.
1755              * 1" margins are used unless they are disproportionately
1756              * large compared to the size of the media.
1757              */
1758 
1759             float wid = mediaSize.getX(MediaPrintableArea.INCH);
1760             float hgt = mediaSize.getY(MediaPrintableArea.INCH);
1761             float maxMarginRatio = 5f;
1762             float xMgn, yMgn;
1763             if (wid > maxMarginRatio) {
1764                 xMgn = 1f;
1765             } else {
1766                 xMgn = wid / maxMarginRatio;
1767             }
1768             if (hgt > maxMarginRatio) {
1769                 yMgn = 1f;
1770             } else {
1771                 yMgn = hgt / maxMarginRatio;
1772             }
1773 
1774             if (mpa == null) {
1775                 mpa = new MediaPrintableArea(xMgn, yMgn,
1776                                              wid-(2*xMgn), hgt-(2*yMgn),
1777                                              MediaPrintableArea.INCH);
1778                 asCurrent.add(mpa);
1779             }
1780             float pax = mpa.getX(units);
1781             float pay = mpa.getY(units);
1782             float paw = mpa.getWidth(units);
1783             float pah = mpa.getHeight(units);
1784             float paxMax = mpaMax.getX(units);
1785             float payMax = mpaMax.getY(units);
1786             float pawMax = mpaMax.getWidth(units);
1787             float pahMax = mpaMax.getHeight(units);
1788 
1789 
1790             boolean invalid = false;
1791 
1792             // If the paper is set to something which is too small to
1793             // accommodate a specified printable area, perhaps carried
1794             // over from a larger paper, the adjustment that needs to be
1795             // performed should seem the most natural from a user's viewpoint.
1796             // Since the user is specifying margins, then we are biased
1797             // towards keeping the margins as close to what is specified as
1798             // possible, shrinking or growing the printable area.
1799             // But the API uses printable area, so you need to know the
1800             // media size in which the margins were previously interpreted,
1801             // or at least have a record of the margins.
1802             // In the case that this is the creation of this UI we do not
1803             // have this record, so we are somewhat reliant on the client
1804             // to supply a reasonable default
1805             wid = mediaSize.getX(units);
1806             hgt = mediaSize.getY(units);
1807             if (lmVal >= 0f) {
1808                 invalid = true;
1809 
1810                 if (lmVal + rmVal > wid) {
1811                     // margins impossible, but maintain P.A if can
1812                     if (paw > pawMax) {
1813                         paw = pawMax;
1814                     }
1815                     // try to centre the printable area.
1816                     pax = (wid - paw)/2f;
1817                 } else {
1818                     pax = (lmVal >= paxMax) ? lmVal : paxMax;
1819                     paw = wid - pax - rmVal;
1820                 }
1821                 if (tmVal + bmVal > hgt) {
1822                     if (pah > pahMax) {
1823                         pah = pahMax;
1824                     }
1825                     pay = (hgt - pah)/2f;
1826                 } else {
1827                     pay = (tmVal >= payMax) ? tmVal : payMax;
1828                     pah = hgt - pay - bmVal;
1829                 }
1830             }
1831             if (pax < paxMax) {
1832                 invalid = true;
1833                 pax = paxMax;
1834             }
1835             if (pay < payMax) {
1836                 invalid = true;
1837                 pay = payMax;
1838             }
1839             if (paw > pawMax) {
1840                 invalid = true;
1841                 paw = pawMax;
1842             }
1843             if (pah > pahMax) {
1844                 invalid = true;
1845                 pah = pahMax;
1846             }
1847 
1848             if ((pax + paw > paxMax + pawMax) || (paw <= 0f)) {
1849                 invalid = true;
1850                 pax = paxMax;
1851                 paw = pawMax;
1852             }
1853             if ((pay + pah > payMax + pahMax) || (pah <= 0f)) {
1854                 invalid = true;
1855                 pay = payMax;
1856                 pah = pahMax;
1857             }
1858 
1859             if (invalid) {
1860                 mpa = new MediaPrintableArea(pax, pay, paw, pah, units);
1861                 asCurrent.add(mpa);
1862             }
1863 
1864             /* We now have a valid printable area.
1865              * Turn it into margins, using the mediaSize
1866              */
1867             lmVal = pax;
1868             tmVal = pay;
1869             rmVal = mediaSize.getX(units) - pax - paw;
1870             bmVal = mediaSize.getY(units) - pay - pah;
1871 
1872             lmObj = lmVal;
1873             rmObj = rmVal;
1874             tmObj = tmVal;
1875             bmObj = bmVal;
1876 
1877             /* Now we know the values to use, we need to assign them
1878              * to the fields appropriate for the orientation.
1879              * Note: if orientation changes this method must be called.
1880              */
1881             Class<OrientationRequested> orCategory = OrientationRequested.class;
1882             OrientationRequested or =
1883                 (OrientationRequested)asCurrent.get(orCategory);
1884 
1885             if (or == null) {
1886                 or = (OrientationRequested)
1887                      psCurrent.getDefaultAttributeValue(orCategory);
1888             }
1889 
1890             Float tmp;
1891 
1892             if (or == OrientationRequested.REVERSE_PORTRAIT) {
1893                 tmp = lmObj; lmObj = rmObj; rmObj = tmp;
1894                 tmp = tmObj; tmObj = bmObj; bmObj = tmp;
1895             }  else if (or == OrientationRequested.LANDSCAPE) {
1896                 tmp = lmObj;
1897                 lmObj = bmObj;
1898                 bmObj = rmObj;
1899                 rmObj = tmObj;
1900                 tmObj = tmp;
1901             }  else if (or == OrientationRequested.REVERSE_LANDSCAPE) {
1902                 tmp = lmObj;
1903                 lmObj = tmObj;
1904                 tmObj = rmObj;
1905                 rmObj = bmObj;
1906                 bmObj = tmp;
1907             }
1908 
1909             leftMargin.setValue(lmObj);
1910             rightMargin.setValue(rmObj);
1911             topMargin.setValue(tmObj);
1912             bottomMargin.setValue(bmObj);
1913         }
1914     }
1915 
1916     @SuppressWarnings("serial") // Superclass is not serializable across versions
1917     private class MediaPanel extends JPanel implements ItemListener {
1918 
1919         private final String strTitle = getMsg("border.media");
1920         private JLabel lblSize, lblSource;
1921         private JComboBox<Object> cbSize, cbSource;
1922         private Vector<MediaSizeName> sizes = new Vector<>();
1923         private Vector<MediaTray> sources = new Vector<>();
1924         private MarginsPanel pnlMargins = null;
1925 
1926         public MediaPanel() {
1927             super();
1928 
1929             GridBagLayout gridbag = new GridBagLayout();
1930             GridBagConstraints c = new GridBagConstraints();
1931 
1932             setLayout(gridbag);
1933             setBorder(BorderFactory.createTitledBorder(strTitle));
1934 
1935             cbSize = new JComboBox<>();
1936             cbSource = new JComboBox<>();
1937 
1938             c.fill = GridBagConstraints.BOTH;
1939             c.insets = compInsets;
1940             c.weighty = 1.0;
1941 
1942             c.weightx = 0.0;
1943             lblSize = new JLabel(getMsg("label.size"), JLabel.TRAILING);
1944             lblSize.setDisplayedMnemonic(getMnemonic("label.size"));
1945             lblSize.setLabelFor(cbSize);
1946             addToGB(lblSize, this, gridbag, c);
1947             c.weightx = 1.0;
1948             c.gridwidth = GridBagConstraints.REMAINDER;
1949             addToGB(cbSize, this, gridbag, c);
1950 
1951             c.weightx = 0.0;
1952             c.gridwidth = 1;
1953             lblSource = new JLabel(getMsg("label.source"), JLabel.TRAILING);
1954             lblSource.setDisplayedMnemonic(getMnemonic("label.source"));
1955             lblSource.setLabelFor(cbSource);
1956             addToGB(lblSource, this, gridbag, c);
1957             c.gridwidth = GridBagConstraints.REMAINDER;
1958             addToGB(cbSource, this, gridbag, c);
1959         }
1960 
1961         private String getMediaName(String key) {
1962             try {
1963                 // replace characters that would be invalid in
1964                 // a resource key with valid characters
1965                 String newkey = key.replace(' ', '-');
1966                 newkey = newkey.replace('#', 'n');
1967 
1968                 return messageRB.getString(newkey);
1969             } catch (java.util.MissingResourceException e) {
1970                 return key;
1971             }
1972         }
1973 
1974         public void itemStateChanged(ItemEvent e) {
1975             Object source = e.getSource();
1976 
1977             if (e.getStateChange() == ItemEvent.SELECTED) {
1978                 if (source == cbSize) {
1979                     int index = cbSize.getSelectedIndex();
1980 
1981                     if ((index >= 0) && (index < sizes.size())) {
1982                         if ((cbSource.getItemCount() > 1) &&
1983                             (cbSource.getSelectedIndex() >= 1))
1984                         {
1985                             int src = cbSource.getSelectedIndex() - 1;
1986                             MediaTray mt = sources.get(src);
1987                             asCurrent.add(new SunAlternateMedia(mt));
1988                         }
1989                         asCurrent.add(sizes.get(index));
1990                     }
1991                 } else if (source == cbSource) {
1992                     int index = cbSource.getSelectedIndex();
1993 
1994                     if ((index >= 1) && (index < (sources.size() + 1))) {
1995                        asCurrent.remove(SunAlternateMedia.class);
1996                        MediaTray newTray = sources.get(index - 1);
1997                        Media m = (Media)asCurrent.get(Media.class);
1998                        if (m == null || m instanceof MediaTray) {
1999                            asCurrent.add(newTray);
2000                        } else if (m instanceof MediaSizeName) {
2001                            MediaSizeName msn = (MediaSizeName)m;
2002                            Media def = (Media)psCurrent.getDefaultAttributeValue(Media.class);
2003                            if (def instanceof MediaSizeName && def.equals(msn)) {
2004                                asCurrent.add(newTray);
2005                            } else {
2006                                /* Non-default paper size, so need to store tray
2007                                 * as SunAlternateMedia
2008                                 */
2009                                asCurrent.add(new SunAlternateMedia(newTray));
2010                            }
2011                        }
2012                     } else if (index == 0) {
2013                         asCurrent.remove(SunAlternateMedia.class);
2014                         if (cbSize.getItemCount() > 0) {
2015                             int size = cbSize.getSelectedIndex();
2016                             asCurrent.add(sizes.get(size));
2017                         }
2018                     }
2019                 }
2020             // orientation affects display of margins.
2021                 if (pnlMargins != null) {
2022                     pnlMargins.updateInfo();
2023                 }
2024             }
2025         }
2026 
2027 
2028         /* this is ad hoc to keep things simple */
2029         public void addMediaListener(MarginsPanel pnl) {
2030             pnlMargins = pnl;
2031         }
2032         public void updateInfo() {
2033             Class<Media> mdCategory = Media.class;
2034             Class<SunAlternateMedia> amCategory = SunAlternateMedia.class;
2035             boolean mediaSupported = false;
2036 
2037             cbSize.removeItemListener(this);
2038             cbSize.removeAllItems();
2039             cbSource.removeItemListener(this);
2040             cbSource.removeAllItems();
2041             cbSource.addItem(getMediaName("auto-select"));
2042 
2043             sizes.clear();
2044             sources.clear();
2045 
2046             if (psCurrent.isAttributeCategorySupported(mdCategory)) {
2047                 mediaSupported = true;
2048 
2049                 Object values =
2050                     psCurrent.getSupportedAttributeValues(mdCategory,
2051                                                           docFlavor,
2052                                                           asCurrent);
2053 
2054                 if (values instanceof Media[]) {
2055                     Media[] media = (Media[])values;
2056 
2057                     for (int i = 0; i < media.length; i++) {
2058                         Media medium = media[i];
2059 
2060                         if (medium instanceof MediaSizeName) {
2061                             sizes.add((MediaSizeName)medium);
2062                             cbSize.addItem(getMediaName(medium.toString()));
2063                         } else if (medium instanceof MediaTray) {
2064                             sources.add((MediaTray)medium);
2065                             cbSource.addItem(getMediaName(medium.toString()));
2066                         }
2067                     }
2068                 }
2069             }
2070 
2071             boolean msSupported = (mediaSupported && (sizes.size() > 0));
2072             lblSize.setEnabled(msSupported);
2073             cbSize.setEnabled(msSupported);
2074 
2075             if (isAWT) {
2076                 cbSource.setEnabled(false);
2077                 lblSource.setEnabled(false);
2078             } else {
2079                 cbSource.setEnabled(mediaSupported);
2080             }
2081 
2082             if (mediaSupported) {
2083 
2084                 Media medium = (Media)asCurrent.get(mdCategory);
2085 
2086                // initialize size selection to default
2087                 Media defMedia = (Media)psCurrent.getDefaultAttributeValue(mdCategory);
2088                 if (defMedia instanceof MediaSizeName) {
2089                     cbSize.setSelectedIndex(sizes.size() > 0 ? sizes.indexOf(defMedia) : -1);
2090                 }
2091 
2092                 if (medium == null ||
2093                     !psCurrent.isAttributeValueSupported(medium,
2094                                                          docFlavor, asCurrent)) {
2095 
2096                     medium = defMedia;
2097 
2098                     if (medium == null) {
2099                         if (sizes.size() > 0) {
2100                             medium = (Media)sizes.get(0);
2101                         }
2102                     }
2103                     if (medium != null) {
2104                         asCurrent.add(medium);
2105                     }
2106                 }
2107                 if (medium != null) {
2108                     if (medium instanceof MediaSizeName) {
2109                         MediaSizeName ms = (MediaSizeName)medium;
2110                         cbSize.setSelectedIndex(sizes.indexOf(ms));
2111                     } else if (medium instanceof MediaTray) {
2112                         MediaTray mt = (MediaTray)medium;
2113                         cbSource.setSelectedIndex(sources.indexOf(mt) + 1);
2114                     }
2115                 } else {
2116                     cbSize.setSelectedIndex(sizes.size() > 0 ? 0 : -1);
2117                     cbSource.setSelectedIndex(0);
2118                 }
2119 
2120                 SunAlternateMedia alt = (SunAlternateMedia)asCurrent.get(amCategory);
2121                 if (alt != null) {
2122                     Media md = alt.getMedia();
2123                     if (md instanceof MediaTray) {
2124                         MediaTray mt = (MediaTray)md;
2125                         cbSource.setSelectedIndex(sources.indexOf(mt) + 1);
2126                     }
2127                 }
2128 
2129                 int selIndex = cbSize.getSelectedIndex();
2130                 if ((selIndex >= 0) && (selIndex < sizes.size())) {
2131                   asCurrent.add(sizes.get(selIndex));
2132                 }
2133 
2134                 selIndex = cbSource.getSelectedIndex();
2135                 if ((selIndex >= 1) && (selIndex < (sources.size()+1))) {
2136                     MediaTray mt = sources.get(selIndex-1);
2137                     if (medium instanceof MediaTray) {
2138                         asCurrent.add(mt);
2139                     } else {
2140                         asCurrent.add(new SunAlternateMedia(mt));
2141                     }
2142                 }
2143 
2144 
2145             }
2146             cbSize.addItemListener(this);
2147             cbSource.addItemListener(this);
2148         }
2149     }
2150 
2151     @SuppressWarnings("serial") // Superclass is not serializable across versions
2152     private class OrientationPanel extends JPanel
2153         implements ActionListener
2154     {
2155         private final String strTitle = getMsg("border.orientation");
2156         private IconRadioButton rbPortrait, rbLandscape,
2157                                 rbRevPortrait, rbRevLandscape;
2158         private MarginsPanel pnlMargins = null;
2159 
2160         public OrientationPanel() {
2161             super();
2162 
2163             GridBagLayout gridbag = new GridBagLayout();
2164             GridBagConstraints c = new GridBagConstraints();
2165 
2166             setLayout(gridbag);
2167             setBorder(BorderFactory.createTitledBorder(strTitle));
2168 
2169             c.fill = GridBagConstraints.BOTH;
2170             c.insets = compInsets;
2171             c.weighty = 1.0;
2172             c.gridwidth = GridBagConstraints.REMAINDER;
2173 
2174             ButtonGroup bg = new ButtonGroup();
2175             rbPortrait = new IconRadioButton("radiobutton.portrait",
2176                                              "orientPortrait.png", true,
2177                                              bg, this);
2178             rbPortrait.addActionListener(this);
2179             addToGB(rbPortrait, this, gridbag, c);
2180             rbLandscape = new IconRadioButton("radiobutton.landscape",
2181                                               "orientLandscape.png", false,
2182                                               bg, this);
2183             rbLandscape.addActionListener(this);
2184             addToGB(rbLandscape, this, gridbag, c);
2185             rbRevPortrait = new IconRadioButton("radiobutton.revportrait",
2186                                                 "orientRevPortrait.png", false,
2187                                                 bg, this);
2188             rbRevPortrait.addActionListener(this);
2189             addToGB(rbRevPortrait, this, gridbag, c);
2190             rbRevLandscape = new IconRadioButton("radiobutton.revlandscape",
2191                                                  "orientRevLandscape.png", false,
2192                                                  bg, this);
2193             rbRevLandscape.addActionListener(this);
2194             addToGB(rbRevLandscape, this, gridbag, c);
2195         }
2196 
2197         public void actionPerformed(ActionEvent e) {
2198             Object source = e.getSource();
2199 
2200             if (rbPortrait.isSameAs(source)) {
2201                 asCurrent.add(OrientationRequested.PORTRAIT);
2202             } else if (rbLandscape.isSameAs(source)) {
2203                 asCurrent.add(OrientationRequested.LANDSCAPE);
2204             } else if (rbRevPortrait.isSameAs(source)) {
2205                 asCurrent.add(OrientationRequested.REVERSE_PORTRAIT);
2206             } else if (rbRevLandscape.isSameAs(source)) {
2207                 asCurrent.add(OrientationRequested.REVERSE_LANDSCAPE);
2208             }
2209             // orientation affects display of margins.
2210             if (pnlMargins != null) {
2211                 pnlMargins.updateInfo();
2212             }
2213         }
2214 
2215         /* This is ad hoc to keep things simple */
2216         void addOrientationListener(MarginsPanel pnl) {
2217             pnlMargins = pnl;
2218         }
2219 
2220         public void updateInfo() {
2221             Class<OrientationRequested> orCategory = OrientationRequested.class;
2222             boolean pSupported = false;
2223             boolean lSupported = false;
2224             boolean rpSupported = false;
2225             boolean rlSupported = false;
2226 
2227             if (isAWT) {
2228                 pSupported = true;
2229                 lSupported = true;
2230             } else
2231             if (psCurrent.isAttributeCategorySupported(orCategory)) {
2232                 Object values =
2233                     psCurrent.getSupportedAttributeValues(orCategory,
2234                                                           docFlavor,
2235                                                           asCurrent);
2236 
2237                 if (values instanceof OrientationRequested[]) {
2238                     OrientationRequested[] ovalues =
2239                         (OrientationRequested[])values;
2240 
2241                     for (int i = 0; i < ovalues.length; i++) {
2242                         OrientationRequested value = ovalues[i];
2243 
2244                         if (value == OrientationRequested.PORTRAIT) {
2245                             pSupported = true;
2246                         } else if (value == OrientationRequested.LANDSCAPE) {
2247                             lSupported = true;
2248                         } else if (value == OrientationRequested.REVERSE_PORTRAIT) {
2249                             rpSupported = true;
2250                         } else if (value == OrientationRequested.REVERSE_LANDSCAPE) {
2251                             rlSupported = true;
2252                         }
2253                     }
2254                 }
2255             }
2256 
2257 
2258             rbPortrait.setEnabled(pSupported);
2259             rbLandscape.setEnabled(lSupported);
2260             rbRevPortrait.setEnabled(rpSupported);
2261             rbRevLandscape.setEnabled(rlSupported);
2262 
2263             OrientationRequested or = (OrientationRequested)asCurrent.get(orCategory);
2264             if (or == null ||
2265                 !psCurrent.isAttributeValueSupported(or, docFlavor, asCurrent)) {
2266 
2267                 or = (OrientationRequested)psCurrent.getDefaultAttributeValue(orCategory);
2268                 // need to validate if default is not supported
2269                 if ((or != null) &&
2270                    !psCurrent.isAttributeValueSupported(or, docFlavor, asCurrent)) {
2271                     or = null;
2272                     Object values =
2273                         psCurrent.getSupportedAttributeValues(orCategory,
2274                                                               docFlavor,
2275                                                               asCurrent);
2276                     if (values instanceof OrientationRequested[]) {
2277                         OrientationRequested[] orValues =
2278                                             (OrientationRequested[])values;
2279                         if (orValues.length > 1) {
2280                             // get the first in the list
2281                             or = orValues[0];
2282                         }
2283                     }
2284                 }
2285 
2286                 if (or == null) {
2287                     or = OrientationRequested.PORTRAIT;
2288                 }
2289                 asCurrent.add(or);
2290             }
2291 
2292             if (or == OrientationRequested.PORTRAIT) {
2293                 rbPortrait.setSelected(true);
2294             } else if (or == OrientationRequested.LANDSCAPE) {
2295                 rbLandscape.setSelected(true);
2296             } else if (or == OrientationRequested.REVERSE_PORTRAIT) {
2297                 rbRevPortrait.setSelected(true);
2298             } else { // if (or == OrientationRequested.REVERSE_LANDSCAPE)
2299                 rbRevLandscape.setSelected(true);
2300             }
2301         }
2302     }
2303 
2304 
2305 
2306     /**
2307      * The "Appearance" tab.  Includes the controls for Chromaticity,
2308      * PrintQuality, JobPriority, JobName, and other related job attributes.
2309      */
2310     @SuppressWarnings("serial") // Superclass is not serializable across versions
2311     private class AppearancePanel extends JPanel {
2312 
2313         private ChromaticityPanel pnlChromaticity;
2314         private QualityPanel pnlQuality;
2315         private JobAttributesPanel pnlJobAttributes;
2316         private SidesPanel pnlSides;
2317 
2318         public AppearancePanel() {
2319             super();
2320 
2321             GridBagLayout gridbag = new GridBagLayout();
2322             GridBagConstraints c = new GridBagConstraints();
2323 
2324             setLayout(gridbag);
2325 
2326             c.fill = GridBagConstraints.BOTH;
2327             c.insets = panelInsets;
2328             c.weightx = 1.0;
2329             c.weighty = 1.0;
2330 
2331             c.gridwidth = GridBagConstraints.RELATIVE;
2332             pnlChromaticity = new ChromaticityPanel();
2333             addToGB(pnlChromaticity, this, gridbag, c);
2334 
2335             c.gridwidth = GridBagConstraints.REMAINDER;
2336             pnlQuality = new QualityPanel();
2337             addToGB(pnlQuality, this, gridbag, c);
2338 
2339             c.gridwidth = 1;
2340             pnlSides = new SidesPanel();
2341             addToGB(pnlSides, this, gridbag, c);
2342 
2343             c.gridwidth = GridBagConstraints.REMAINDER;
2344             pnlJobAttributes = new JobAttributesPanel();
2345             addToGB(pnlJobAttributes, this, gridbag, c);
2346 
2347         }
2348 
2349         public void updateInfo() {
2350             pnlChromaticity.updateInfo();
2351             pnlQuality.updateInfo();
2352             pnlSides.updateInfo();
2353             pnlJobAttributes.updateInfo();
2354         }
2355     }
2356 
2357     @SuppressWarnings("serial") // Superclass is not serializable across versions
2358     private class ChromaticityPanel extends JPanel
2359         implements ActionListener
2360     {
2361         private final String strTitle = getMsg("border.chromaticity");
2362         private JRadioButton rbMonochrome, rbColor;
2363 
2364         public ChromaticityPanel() {
2365             super();
2366 
2367             GridBagLayout gridbag = new GridBagLayout();
2368             GridBagConstraints c = new GridBagConstraints();
2369 
2370             setLayout(gridbag);
2371             setBorder(BorderFactory.createTitledBorder(strTitle));
2372 
2373             c.fill = GridBagConstraints.BOTH;
2374             c.gridwidth = GridBagConstraints.REMAINDER;
2375             c.weighty = 1.0;
2376 
2377             ButtonGroup bg = new ButtonGroup();
2378             rbMonochrome = createRadioButton("radiobutton.monochrome", this);
2379             rbMonochrome.setSelected(true);
2380             bg.add(rbMonochrome);
2381             addToGB(rbMonochrome, this, gridbag, c);
2382             rbColor = createRadioButton("radiobutton.color", this);
2383             bg.add(rbColor);
2384             addToGB(rbColor, this, gridbag, c);
2385         }
2386 
2387         public void actionPerformed(ActionEvent e) {
2388             Object source = e.getSource();
2389 
2390             // REMIND: use isSameAs if we move to a IconRB in the future
2391             if (source == rbMonochrome) {
2392                 asCurrent.add(Chromaticity.MONOCHROME);
2393             } else if (source == rbColor) {
2394                 asCurrent.add(Chromaticity.COLOR);
2395             }
2396         }
2397 
2398         public void updateInfo() {
2399             Class<Chromaticity> chCategory = Chromaticity.class;
2400             boolean monoSupported = false;
2401             boolean colorSupported = false;
2402 
2403             if (isAWT) {
2404                 monoSupported = true;
2405                 colorSupported = true;
2406             } else
2407             if (psCurrent.isAttributeCategorySupported(chCategory)) {
2408                 Object values =
2409                     psCurrent.getSupportedAttributeValues(chCategory,
2410                                                           docFlavor,
2411                                                           asCurrent);
2412 
2413                 if (values instanceof Chromaticity[]) {
2414                     Chromaticity[] cvalues = (Chromaticity[])values;
2415 
2416                     for (int i = 0; i < cvalues.length; i++) {
2417                         Chromaticity value = cvalues[i];
2418 
2419                         if (value == Chromaticity.MONOCHROME) {
2420                             monoSupported = true;
2421                         } else if (value == Chromaticity.COLOR) {
2422                             colorSupported = true;
2423                         }
2424                     }
2425                 }
2426             }
2427 
2428 
2429             rbMonochrome.setEnabled(monoSupported);
2430             rbColor.setEnabled(colorSupported);
2431 
2432             Chromaticity ch = (Chromaticity)asCurrent.get(chCategory);
2433             if (ch == null) {
2434                 ch = (Chromaticity)psCurrent.getDefaultAttributeValue(chCategory);
2435                 if (ch == null) {
2436                     ch = Chromaticity.MONOCHROME;
2437                 }
2438             }
2439 
2440             if (ch == Chromaticity.MONOCHROME) {
2441                 rbMonochrome.setSelected(true);
2442             } else { // if (ch == Chromaticity.COLOR)
2443                 rbColor.setSelected(true);
2444             }
2445         }
2446     }
2447 
2448     @SuppressWarnings("serial") // Superclass is not serializable across versions
2449     private class QualityPanel extends JPanel
2450         implements ActionListener
2451     {
2452         private final String strTitle = getMsg("border.quality");
2453         private JRadioButton rbDraft, rbNormal, rbHigh;
2454 
2455         public QualityPanel() {
2456             super();
2457 
2458             GridBagLayout gridbag = new GridBagLayout();
2459             GridBagConstraints c = new GridBagConstraints();
2460 
2461             setLayout(gridbag);
2462             setBorder(BorderFactory.createTitledBorder(strTitle));
2463 
2464             c.fill = GridBagConstraints.BOTH;
2465             c.gridwidth = GridBagConstraints.REMAINDER;
2466             c.weighty = 1.0;
2467 
2468             ButtonGroup bg = new ButtonGroup();
2469             rbDraft = createRadioButton("radiobutton.draftq", this);
2470             bg.add(rbDraft);
2471             addToGB(rbDraft, this, gridbag, c);
2472             rbNormal = createRadioButton("radiobutton.normalq", this);
2473             rbNormal.setSelected(true);
2474             bg.add(rbNormal);
2475             addToGB(rbNormal, this, gridbag, c);
2476             rbHigh = createRadioButton("radiobutton.highq", this);
2477             bg.add(rbHigh);
2478             addToGB(rbHigh, this, gridbag, c);
2479         }
2480 
2481         public void actionPerformed(ActionEvent e) {
2482             Object source = e.getSource();
2483 
2484             if (source == rbDraft) {
2485                 asCurrent.add(PrintQuality.DRAFT);
2486             } else if (source == rbNormal) {
2487                 asCurrent.add(PrintQuality.NORMAL);
2488             } else if (source == rbHigh) {
2489                 asCurrent.add(PrintQuality.HIGH);
2490             }
2491         }
2492 
2493         public void updateInfo() {
2494             Class<PrintQuality> pqCategory = PrintQuality.class;
2495             boolean draftSupported = false;
2496             boolean normalSupported = false;
2497             boolean highSupported = false;
2498 
2499             if (isAWT) {
2500                 draftSupported = true;
2501                 normalSupported = true;
2502                 highSupported = true;
2503             } else
2504             if (psCurrent.isAttributeCategorySupported(pqCategory)) {
2505                 Object values =
2506                     psCurrent.getSupportedAttributeValues(pqCategory,
2507                                                           docFlavor,
2508                                                           asCurrent);
2509 
2510                 if (values instanceof PrintQuality[]) {
2511                     PrintQuality[] qvalues = (PrintQuality[])values;
2512 
2513                     for (int i = 0; i < qvalues.length; i++) {
2514                         PrintQuality value = qvalues[i];
2515 
2516                         if (value == PrintQuality.DRAFT) {
2517                             draftSupported = true;
2518                         } else if (value == PrintQuality.NORMAL) {
2519                             normalSupported = true;
2520                         } else if (value == PrintQuality.HIGH) {
2521                             highSupported = true;
2522                         }
2523                     }
2524                 }
2525             }
2526 
2527             rbDraft.setEnabled(draftSupported);
2528             rbNormal.setEnabled(normalSupported);
2529             rbHigh.setEnabled(highSupported);
2530 
2531             PrintQuality pq = (PrintQuality)asCurrent.get(pqCategory);
2532             if (pq == null) {
2533                 pq = (PrintQuality)psCurrent.getDefaultAttributeValue(pqCategory);
2534                 if (pq == null) {
2535                     pq = PrintQuality.NORMAL;
2536                 }
2537             }
2538 
2539             if (pq == PrintQuality.DRAFT) {
2540                 rbDraft.setSelected(true);
2541             } else if (pq == PrintQuality.NORMAL) {
2542                 rbNormal.setSelected(true);
2543             } else { // if (pq == PrintQuality.HIGH)
2544                 rbHigh.setSelected(true);
2545             }
2546         }
2547 
2548 
2549     }
2550 
2551     @SuppressWarnings("serial") // Superclass is not serializable across versions
2552     private class SidesPanel extends JPanel
2553         implements ActionListener
2554     {
2555         private final String strTitle = getMsg("border.sides");
2556         private IconRadioButton rbOneSide, rbTumble, rbDuplex;
2557 
2558         public SidesPanel() {
2559             super();
2560 
2561             GridBagLayout gridbag = new GridBagLayout();
2562             GridBagConstraints c = new GridBagConstraints();
2563 
2564             setLayout(gridbag);
2565             setBorder(BorderFactory.createTitledBorder(strTitle));
2566 
2567             c.fill = GridBagConstraints.BOTH;
2568             c.insets = compInsets;
2569             c.weighty = 1.0;
2570             c.gridwidth = GridBagConstraints.REMAINDER;
2571 
2572             ButtonGroup bg = new ButtonGroup();
2573             rbOneSide = new IconRadioButton("radiobutton.oneside",
2574                                             "oneside.png", true,
2575                                             bg, this);
2576             rbOneSide.addActionListener(this);
2577             addToGB(rbOneSide, this, gridbag, c);
2578             rbTumble = new IconRadioButton("radiobutton.tumble",
2579                                            "tumble.png", false,
2580                                            bg, this);
2581             rbTumble.addActionListener(this);
2582             addToGB(rbTumble, this, gridbag, c);
2583             rbDuplex = new IconRadioButton("radiobutton.duplex",
2584                                            "duplex.png", false,
2585                                            bg, this);
2586             rbDuplex.addActionListener(this);
2587             c.gridwidth = GridBagConstraints.REMAINDER;
2588             addToGB(rbDuplex, this, gridbag, c);
2589         }
2590 
2591         public void actionPerformed(ActionEvent e) {
2592             Object source = e.getSource();
2593 
2594             if (rbOneSide.isSameAs(source)) {
2595                 asCurrent.add(Sides.ONE_SIDED);
2596             } else if (rbTumble.isSameAs(source)) {
2597                 asCurrent.add(Sides.TUMBLE);
2598             } else if (rbDuplex.isSameAs(source)) {
2599                 asCurrent.add(Sides.DUPLEX);
2600             }
2601         }
2602 
2603         public void updateInfo() {
2604             Class<Sides> sdCategory = Sides.class;
2605             boolean osSupported = false;
2606             boolean tSupported = false;
2607             boolean dSupported = false;
2608 
2609             if (psCurrent.isAttributeCategorySupported(sdCategory)) {
2610                 Object values =
2611                     psCurrent.getSupportedAttributeValues(sdCategory,
2612                                                           docFlavor,
2613                                                           asCurrent);
2614 
2615                 if (values instanceof Sides[]) {
2616                     Sides[] svalues = (Sides[])values;
2617 
2618                     for (int i = 0; i < svalues.length; i++) {
2619                         Sides value = svalues[i];
2620 
2621                         if (value == Sides.ONE_SIDED) {
2622                             osSupported = true;
2623                         } else if (value == Sides.TUMBLE) {
2624                             tSupported = true;
2625                         } else if (value == Sides.DUPLEX) {
2626                             dSupported = true;
2627                         }
2628                     }
2629                 }
2630             }
2631             rbOneSide.setEnabled(osSupported);
2632             rbTumble.setEnabled(tSupported);
2633             rbDuplex.setEnabled(dSupported);
2634 
2635             Sides sd = (Sides)asCurrent.get(sdCategory);
2636             if (sd == null) {
2637                 sd = (Sides)psCurrent.getDefaultAttributeValue(sdCategory);
2638                 if (sd == null) {
2639                     sd = Sides.ONE_SIDED;
2640                 }
2641             }
2642 
2643             if (sd == Sides.ONE_SIDED) {
2644                 rbOneSide.setSelected(true);
2645             } else if (sd == Sides.TUMBLE) {
2646                 rbTumble.setSelected(true);
2647             } else { // if (sd == Sides.DUPLEX)
2648                 rbDuplex.setSelected(true);
2649             }
2650         }
2651     }
2652 
2653 
2654     @SuppressWarnings("serial") // Superclass is not serializable across versions
2655     private class JobAttributesPanel extends JPanel
2656         implements ActionListener, ChangeListener, FocusListener
2657     {
2658         private final String strTitle = getMsg("border.jobattributes");
2659         private JLabel lblPriority, lblJobName, lblUserName;
2660         private JSpinner spinPriority;
2661         private SpinnerNumberModel snModel;
2662         private JCheckBox cbJobSheets;
2663         private JTextField tfJobName, tfUserName;
2664 
2665         public JobAttributesPanel() {
2666             super();
2667 
2668             GridBagLayout gridbag = new GridBagLayout();
2669             GridBagConstraints c = new GridBagConstraints();
2670 
2671             setLayout(gridbag);
2672             setBorder(BorderFactory.createTitledBorder(strTitle));
2673 
2674             c.fill = GridBagConstraints.NONE;
2675             c.insets = compInsets;
2676             c.weighty = 1.0;
2677 
2678             cbJobSheets = createCheckBox("checkbox.jobsheets", this);
2679             c.anchor = GridBagConstraints.LINE_START;
2680             addToGB(cbJobSheets, this, gridbag, c);
2681 
2682             JPanel pnlTop = new JPanel();
2683             lblPriority = new JLabel(getMsg("label.priority"), JLabel.TRAILING);
2684             lblPriority.setDisplayedMnemonic(getMnemonic("label.priority"));
2685 
2686             pnlTop.add(lblPriority);
2687             snModel = new SpinnerNumberModel(1, 1, 100, 1);
2688             spinPriority = new JSpinner(snModel);
2689             lblPriority.setLabelFor(spinPriority);
2690             // REMIND
2691             ((JSpinner.NumberEditor)spinPriority.getEditor()).getTextField().setColumns(3);
2692             spinPriority.addChangeListener(this);
2693             pnlTop.add(spinPriority);
2694             c.anchor = GridBagConstraints.LINE_END;
2695             c.gridwidth = GridBagConstraints.REMAINDER;
2696             pnlTop.getAccessibleContext().setAccessibleName(
2697                                        getMsg("label.priority"));
2698             addToGB(pnlTop, this, gridbag, c);
2699 
2700             c.fill = GridBagConstraints.HORIZONTAL;
2701             c.anchor = GridBagConstraints.CENTER;
2702             c.weightx = 0.0;
2703             c.gridwidth = 1;
2704             char jmnemonic = getMnemonic("label.jobname");
2705             lblJobName = new JLabel(getMsg("label.jobname"), JLabel.TRAILING);
2706             lblJobName.setDisplayedMnemonic(jmnemonic);
2707             addToGB(lblJobName, this, gridbag, c);
2708             c.weightx = 1.0;
2709             c.gridwidth = GridBagConstraints.REMAINDER;
2710             tfJobName = new JTextField();
2711             lblJobName.setLabelFor(tfJobName);
2712             tfJobName.addFocusListener(this);
2713             tfJobName.setFocusAccelerator(jmnemonic);
2714             tfJobName.getAccessibleContext().setAccessibleName(
2715                                              getMsg("label.jobname"));
2716             addToGB(tfJobName, this, gridbag, c);
2717 
2718             c.weightx = 0.0;
2719             c.gridwidth = 1;
2720             char umnemonic = getMnemonic("label.username");
2721             lblUserName = new JLabel(getMsg("label.username"), JLabel.TRAILING);
2722             lblUserName.setDisplayedMnemonic(umnemonic);
2723             addToGB(lblUserName, this, gridbag, c);
2724             c.gridwidth = GridBagConstraints.REMAINDER;
2725             tfUserName = new JTextField();
2726             lblUserName.setLabelFor(tfUserName);
2727             tfUserName.addFocusListener(this);
2728             tfUserName.setFocusAccelerator(umnemonic);
2729             tfUserName.getAccessibleContext().setAccessibleName(
2730                                              getMsg("label.username"));
2731             addToGB(tfUserName, this, gridbag, c);
2732         }
2733 
2734         public void actionPerformed(ActionEvent e) {
2735             if (cbJobSheets.isSelected()) {
2736                 asCurrent.add(JobSheets.STANDARD);
2737             } else {
2738                 asCurrent.add(JobSheets.NONE);
2739             }
2740         }
2741 
2742         public void stateChanged(ChangeEvent e) {
2743             asCurrent.add(new JobPriority(snModel.getNumber().intValue()));
2744         }
2745 
2746         public void focusLost(FocusEvent e) {
2747             Object source = e.getSource();
2748 
2749             if (source == tfJobName) {
2750                 asCurrent.add(new JobName(tfJobName.getText(),
2751                                           Locale.getDefault()));
2752             } else if (source == tfUserName) {
2753                 asCurrent.add(new RequestingUserName(tfUserName.getText(),
2754                                                      Locale.getDefault()));
2755             }
2756         }
2757 
2758         public void focusGained(FocusEvent e) {}
2759 
2760         public void updateInfo() {
2761             Class<JobSheets>          jsCategory = JobSheets.class;
2762             Class<JobPriority>        jpCategory = JobPriority.class;
2763             Class<JobName>            jnCategory = JobName.class;
2764             Class<RequestingUserName> unCategory = RequestingUserName.class;
2765             boolean jsSupported = false;
2766             boolean jpSupported = false;
2767             boolean jnSupported = false;
2768             boolean unSupported = false;
2769 
2770             // setup JobSheets checkbox
2771             if (psCurrent.isAttributeCategorySupported(jsCategory)) {
2772                 jsSupported = true;
2773             }
2774             JobSheets js = (JobSheets)asCurrent.get(jsCategory);
2775             if (js == null) {
2776                 js = (JobSheets)psCurrent.getDefaultAttributeValue(jsCategory);
2777                 if (js == null) {
2778                     js = JobSheets.STANDARD;
2779                 }
2780             }
2781             cbJobSheets.setSelected(js != JobSheets.NONE && jsSupported);
2782             cbJobSheets.setEnabled(jsSupported);
2783 
2784             // setup JobPriority spinner
2785             if (!isAWT && psCurrent.isAttributeCategorySupported(jpCategory)) {
2786                 jpSupported = true;
2787             }
2788             JobPriority jp = (JobPriority)asCurrent.get(jpCategory);
2789             if (jp == null) {
2790                 jp = (JobPriority)psCurrent.getDefaultAttributeValue(jpCategory);
2791                 if (jp == null) {
2792                     jp = new JobPriority(1);
2793                 }
2794             }
2795             int value = jp.getValue();
2796             if ((value < 1) || (value > 100)) {
2797                 value = 1;
2798             }
2799             snModel.setValue(value);
2800             lblPriority.setEnabled(jpSupported);
2801             spinPriority.setEnabled(jpSupported);
2802 
2803             // setup JobName text field
2804             if (psCurrent.isAttributeCategorySupported(jnCategory)) {
2805                 jnSupported = true;
2806             }
2807             JobName jn = (JobName)asCurrent.get(jnCategory);
2808             if (jn == null) {
2809                 jn = (JobName)psCurrent.getDefaultAttributeValue(jnCategory);
2810                 if (jn == null) {
2811                     jn = new JobName("", Locale.getDefault());
2812                 }
2813             }
2814             tfJobName.setText(jn.getValue());
2815             tfJobName.setEnabled(jnSupported);
2816             lblJobName.setEnabled(jnSupported);
2817 
2818             // setup RequestingUserName text field
2819             if (!isAWT && psCurrent.isAttributeCategorySupported(unCategory)) {
2820                 unSupported = true;
2821             }
2822             RequestingUserName un = (RequestingUserName)asCurrent.get(unCategory);
2823             if (un == null) {
2824                 un = (RequestingUserName)psCurrent.getDefaultAttributeValue(unCategory);
2825                 if (un == null) {
2826                     un = new RequestingUserName("", Locale.getDefault());
2827                 }
2828             }
2829             tfUserName.setText(un.getValue());
2830             tfUserName.setEnabled(unSupported);
2831             lblUserName.setEnabled(unSupported);
2832         }
2833     }
2834 
2835 
2836 
2837 
2838     /**
2839      * A special widget that groups a JRadioButton with an associated icon,
2840      * placed to the left of the radio button.
2841      */
2842     @SuppressWarnings("serial") // Superclass is not serializable across versions
2843     private class IconRadioButton extends JPanel {
2844 
2845         private JRadioButton rb;
2846         private JLabel lbl;
2847 
2848         public IconRadioButton(String key, String img, boolean selected,
2849                                ButtonGroup bg, ActionListener al)
2850         {
2851             super(new FlowLayout(FlowLayout.LEADING));
2852             final URL imgURL = getImageResource(img);
2853             Icon icon = java.security.AccessController.doPrivileged(
2854                                  new java.security.PrivilegedAction<Icon>() {
2855                 public Icon run() {
2856                     Icon icon = new ImageIcon(imgURL);
2857                     return icon;
2858                 }
2859             });
2860             lbl = new JLabel(icon);
2861             add(lbl);
2862 
2863             rb = createRadioButton(key, al);
2864             rb.setSelected(selected);
2865             addToBG(rb, this, bg);
2866         }
2867 
2868         public void addActionListener(ActionListener al) {
2869             rb.addActionListener(al);
2870         }
2871 
2872         public boolean isSameAs(Object source) {
2873             return (rb == source);
2874         }
2875 
2876         public void setEnabled(boolean enabled) {
2877             rb.setEnabled(enabled);
2878             lbl.setEnabled(enabled);
2879         }
2880 
2881         public boolean isSelected() {
2882             return rb.isSelected();
2883         }
2884 
2885         public void setSelected(boolean selected) {
2886             rb.setSelected(selected);
2887         }
2888     }
2889 
2890     /**
2891      * Similar in functionality to the default JFileChooser, except this
2892      * chooser will pop up a "Do you want to overwrite..." dialog if the
2893      * user selects a file that already exists.
2894      */
2895     @SuppressWarnings("serial") // JDK implementation class
2896     private class ValidatingFileChooser extends JFileChooser {
2897         public void approveSelection() {
2898             File selected = getSelectedFile();
2899             boolean exists;
2900 
2901             try {
2902                 exists = selected.exists();
2903             } catch (SecurityException e) {
2904                 exists = false;
2905             }
2906 
2907             if (exists) {
2908                 int val;
2909                 val = JOptionPane.showConfirmDialog(this,
2910                                                     getMsg("dialog.overwrite"),
2911                                                     getMsg("dialog.owtitle"),
2912                                                     JOptionPane.YES_NO_OPTION);
2913                 if (val != JOptionPane.YES_OPTION) {
2914                     return;
2915                 }
2916             }
2917 
2918             try {
2919                 if (selected.createNewFile()) {
2920                     selected.delete();
2921                 }
2922             }  catch (IOException ioe) {
2923                 JOptionPane.showMessageDialog(this,
2924                                    getMsg("dialog.writeerror")+" "+selected,
2925                                    getMsg("dialog.owtitle"),
2926                                    JOptionPane.WARNING_MESSAGE);
2927                 return;
2928             } catch (SecurityException se) {
2929                 //There is already file read/write access so at this point
2930                 // only delete access is denied.  Just ignore it because in
2931                 // most cases the file created in createNewFile gets
2932                 // overwritten anyway.
2933             }
2934             File pFile = selected.getParentFile();
2935             if ((selected.exists() &&
2936                       (!selected.isFile() || !selected.canWrite())) ||
2937                      ((pFile != null) &&
2938                       (!pFile.exists() || (pFile.exists() && !pFile.canWrite())))) {
2939                 JOptionPane.showMessageDialog(this,
2940                                    getMsg("dialog.writeerror")+" "+selected,
2941                                    getMsg("dialog.owtitle"),
2942                                    JOptionPane.WARNING_MESSAGE);
2943                 return;
2944             }
2945 
2946             super.approveSelection();
2947         }
2948     }
2949 }