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