Path: blob/master/src/java.desktop/share/classes/javax/print/ServiceUI.java
41153 views
/*1* Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package javax.print;2627import java.awt.GraphicsConfiguration;28import java.awt.GraphicsEnvironment;29import java.awt.HeadlessException;30import java.awt.Rectangle;31import java.awt.Window;3233import javax.print.attribute.Attribute;34import javax.print.attribute.AttributeSet;35import javax.print.attribute.standard.DialogOwner;36import javax.print.attribute.PrintRequestAttributeSet;37import javax.print.attribute.standard.Destination;38import javax.print.attribute.standard.Fidelity;3940import sun.print.ServiceDialog;41import sun.print.SunAlternateMedia;4243/**44* This class is a collection of UI convenience methods which provide a45* graphical user dialog for browsing print services looked up through the Java46* Print Service API.47* <p>48* The dialogs follow a standard pattern of acting as a continue/cancel option49* for a user as well as allowing the user to select the print service to use50* and specify choices such as paper size and number of copies.51* <p>52* The dialogs are designed to work with pluggable print services though the53* public APIs of those print services.54* <p>55* If a print service provides any vendor extensions these may be made56* accessible to the user through a vendor supplied tab panel {@code Component}.57* Such a vendor extension is encouraged to use Swing! and to support its58* accessibility APIs. The vendor extensions should return the settings as part59* of the {@code AttributeSet}. Applications which want to preserve the user60* settings should use those settings to specify the print job. Note that this61* class is not referenced by any other part of the Java Print Service and may62* not be included in profiles which cannot depend on the presence of the AWT63* packages.64*/65public class ServiceUI {6667/**68* Constructs a {@code ServiceUI}.69*/70public ServiceUI() {}7172/**73* Presents a dialog to the user for selecting a print service (printer). It74* is displayed at the location specified by the application and is modal.75* If the specification is invalid or would make the dialog not visible it76* will be displayed at a location determined by the implementation. The77* dialog blocks its calling thread and is application modal.78* <p>79* The dialog may include a tab panel with custom UI lazily obtained from80* the {@code PrintService}'s {@code ServiceUIFactory} when the81* {@code PrintService} is browsed. The dialog will attempt to locate a82* {@code MAIN_UIROLE} first as a {@code JComponent}, then as a83* {@code Panel}. If there is no {@code ServiceUIFactory} or no matching84* role the custom tab will be empty or not visible.85* <p>86* The dialog returns the print service selected by the user if the user87* OK's the dialog and {@code null} if the user cancels the dialog.88* <p>89* An application must pass in an array of print services to browse. The90* array must be {@code non-null} and non-empty. Typically an application91* will pass in only {@code PrintServices} capable of printing a particular92* document flavor.93* <p>94* An application may pass in a {@code PrintService} to be initially95* displayed. A {@code non-null} parameter must be included in the array of96* browsable services. If this parameter is {@code null} a service is chosen97* by the implementation.98* <p>99* An application may optionally pass in the flavor to be printed. If this100* is {@code non-null} choices presented to the user can be better validated101* against those supported by the services. An application must pass in a102* {@code PrintRequestAttributeSet} for returning user choices. On calling103* the {@code PrintRequestAttributeSet} may be empty, or may contain104* application-specified values.105* <p>106* These are used to set the initial settings for the initially displayed107* print service. Values which are not supported by the print service are108* ignored. As the user browses print services, attributes and values are109* copied to the new display. If a user browses a print service which does110* not support a particular attribute-value, the default for that service is111* used as the new value to be copied.112* <p>113* If the user cancels the dialog, the returned attributes will not reflect114* any changes made by the user.115* <p>116* A typical basic usage of this method may be:117* <pre>{@code118* PrintService[] services = PrintServiceLookup.lookupPrintServices(119* DocFlavor.INPUT_STREAM.JPEG, null);120* PrintRequestAttributeSet attributes = new HashPrintRequestAttributeSet();121* if (services.length > 0) {122* PrintService service = ServiceUI.printDialog(null, 50, 50,123* services, services[0],124* null,125* attributes);126* if (service != null) {127* ... print ...128* }129* }130* }</pre>131*132* @param gc used to select screen, {@code null} means primary or default133* screen134* @param x location of dialog including border in screen coordinates135* relative to the origin of {@code gc}136* @param y location of dialog including border in screen coordinates137* relative to the origin of {@code gc}138* @param services to be browsable, must be {@code non-null}139* @param defaultService initial {@code PrintService} to display140* @param flavor the flavor to be printed, or {@code null}141* @param attributes on input is the initial application supplied142* preferences. This cannot be {@code null} but may be empty. On143* output the attributes reflect changes made by the user.144* @return print service selected by the user, or {@code null} if the user145* cancelled the dialog146* @throws HeadlessException if {@code GraphicsEnvironment.isHeadless()}147* returns {@code true}148* @throws IllegalArgumentException if services is {@code null} or empty, or149* attributes is {@code null}, or the initial {@code PrintService}150* is not in the list of browsable services151*/152@SuppressWarnings("deprecation")153public static PrintService printDialog(GraphicsConfiguration gc,154int x, int y,155PrintService[] services,156PrintService defaultService,157DocFlavor flavor,158PrintRequestAttributeSet attributes)159throws HeadlessException160{161int defaultIndex = -1;162163if (GraphicsEnvironment.isHeadless()) {164throw new HeadlessException();165} else if ((services == null) || (services.length == 0)) {166throw new IllegalArgumentException("services must be non-null " +167"and non-empty");168} else if (attributes == null) {169throw new IllegalArgumentException("attributes must be non-null");170}171172if (defaultService != null) {173for (int i = 0; i < services.length; i++) {174if (services[i].equals(defaultService)) {175defaultIndex = i;176break;177}178}179180if (defaultIndex < 0) {181throw new IllegalArgumentException("services must contain " +182"defaultService");183}184} else {185defaultIndex = 0;186}187188DialogOwner dlgOwner = (DialogOwner)attributes.get(DialogOwner.class);189Window owner = (dlgOwner != null) ? dlgOwner.getOwner() : null;190boolean setOnTop = (dlgOwner != null) && (owner == null);191192Rectangle gcBounds = (gc == null) ? GraphicsEnvironment.193getLocalGraphicsEnvironment().getDefaultScreenDevice().194getDefaultConfiguration().getBounds() : gc.getBounds();195196x += gcBounds.x;197y += gcBounds.y;198ServiceDialog dialog = new ServiceDialog(gc,199x,200y,201services, defaultIndex,202flavor, attributes,203owner);204if (setOnTop) {205try {206dialog.setAlwaysOnTop(true);207} catch (SecurityException e) {208}209}210Rectangle dlgBounds = dialog.getBounds();211212// if portion of dialog is not within the gc boundary213if (!gcBounds.contains(dlgBounds)) {214// check if dialog exceed window bounds at left or bottom215// Then position the dialog by moving it by the amount it exceeds216// the window bounds217// If it results in dialog moving beyond the window bounds at218// top/left then position it at window top/left219if (dlgBounds.x + dlgBounds.width > gcBounds.x + gcBounds.width) {220if ((gcBounds.x + gcBounds.width - dlgBounds.width) > gcBounds.x) {221x = (gcBounds.x + gcBounds.width) - dlgBounds.width;222} else {223x = gcBounds.x;224}225}226if (dlgBounds.y + dlgBounds.height > gcBounds.y + gcBounds.height) {227if ((gcBounds.y + gcBounds.height - dlgBounds.height) > gcBounds.y) {228y = (gcBounds.y + gcBounds.height) - dlgBounds.height;229} else {230y = gcBounds.y;231}232}233dialog.setBounds(x, y, dlgBounds.width, dlgBounds.height);234}235dialog.show();236237if (dialog.getStatus() == ServiceDialog.APPROVE) {238PrintRequestAttributeSet newas = dialog.getAttributes();239Class<?> dstCategory = Destination.class;240Class<?> amCategory = SunAlternateMedia.class;241Class<?> fdCategory = Fidelity.class;242243if (attributes.containsKey(dstCategory) &&244!newas.containsKey(dstCategory))245{246attributes.remove(dstCategory);247}248249if (attributes.containsKey(amCategory) &&250!newas.containsKey(amCategory))251{252attributes.remove(amCategory);253}254255attributes.addAll(newas);256257Fidelity fd = (Fidelity)attributes.get(fdCategory);258if (fd != null) {259if (fd == Fidelity.FIDELITY_TRUE) {260removeUnsupportedAttributes(dialog.getPrintService(),261flavor, attributes);262}263}264}265266return dialog.getPrintService();267}268269/**270* POSSIBLE FUTURE API: This method may be used down the road if we271* decide to allow developers to explicitly display a "page setup" dialog.272* Currently we use that functionality internally for the AWT print model.273*/274/*275public static void pageDialog(GraphicsConfiguration gc,276int x, int y,277PrintService service,278DocFlavor flavor,279PrintRequestAttributeSet attributes)280throws HeadlessException281{282if (GraphicsEnvironment.isHeadless()) {283throw new HeadlessException();284} else if (service == null) {285throw new IllegalArgumentException("service must be non-null");286} else if (attributes == null) {287throw new IllegalArgumentException("attributes must be non-null");288}289290ServiceDialog dialog = new ServiceDialog(gc, x, y, service,291flavor, attributes);292dialog.show();293294if (dialog.getStatus() == ServiceDialog.APPROVE) {295PrintRequestAttributeSet newas = dialog.getAttributes();296Class amCategory = SunAlternateMedia.class;297298if (attributes.containsKey(amCategory) &&299!newas.containsKey(amCategory))300{301attributes.remove(amCategory);302}303304attributes.addAll(newas.values());305}306307dialog.getOwner().dispose();308}309*/310311/**312* Removes any attributes from the given {@code AttributeSet} that are313* unsupported by the given {@code PrintService/DocFlavor} combination.314*/315private static void removeUnsupportedAttributes(PrintService ps,316DocFlavor flavor,317AttributeSet aset)318{319AttributeSet asUnsupported = ps.getUnsupportedAttributes(flavor,320aset);321322if (asUnsupported != null) {323Attribute[] usAttrs = asUnsupported.toArray();324325for (int i=0; i<usAttrs.length; i++) {326Class<? extends Attribute> category = usAttrs[i].getCategory();327328if (ps.isAttributeCategorySupported(category)) {329Attribute attr =330(Attribute)ps.getDefaultAttributeValue(category);331332if (attr != null) {333aset.add(attr);334} else {335aset.remove(category);336}337} else {338aset.remove(category);339}340}341}342}343}344345346