Skip to content

Commit b9a1ec5

Browse files
committed
8288137: The set of available printers is not updated without application restart
Reviewed-by: kcr, arapte
1 parent 0132ac8 commit b9a1ec5

File tree

3 files changed

+213
-17
lines changed

3 files changed

+213
-17
lines changed

modules/javafx.graphics/src/main/java/com/sun/prism/j2d/PrismPrintPipeline.java

Lines changed: 94 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import javax.print.PrintService;
3333
import javax.print.PrintServiceLookup;
3434
import java.util.Comparator;
35+
import java.util.HashMap;
3536
import java.util.Set;
3637
import java.util.TreeSet;
3738
import java.awt.Graphics;
@@ -62,8 +63,6 @@ public PrinterJobImpl createPrinterJob(PrinterJob job) {
6263

6364
private static Printer defaultPrinter = null;
6465
public synchronized Printer getDefaultPrinter() {
65-
// Eventually this needs to be updated to reflect when
66-
// the default has changed.
6766
if (defaultPrinter == null) {
6867
PrintService defPrt =
6968
PrintServiceLookup.lookupDefaultPrintService();
@@ -96,13 +95,16 @@ public int compare(Printer p1, Printer p2) {
9695

9796
private static final NameComparator nameComparator = new NameComparator();
9897

99-
// This is static. Eventually I want it to be dynamic, but first
100-
// it needs to be enhanced to only create new instances where
101-
// there really has been a change, which will be rare.
98+
// The map is useful when updating
99+
private static HashMap<PrintService, Printer> pMap = new HashMap<>();
100+
101+
private static long lastTime = 0L;
102102
private static ObservableSet<Printer> printerSet = null;
103+
private static ObservableSet<Printer> returnedPrinterSet = null;
104+
103105
public synchronized ObservableSet<Printer> getAllPrinters() {
104-
if (printerSet == null) {
105-
Set printers = new TreeSet<Printer>(nameComparator);
106+
if (returnedPrinterSet == null) {
107+
TreeSet<Printer> printers = new TreeSet<Printer>(nameComparator);
106108
// Trigger getting default first, so we don't recreate that.
107109
Printer defPrinter = getDefaultPrinter();
108110
PrintService defService = null;
@@ -116,17 +118,94 @@ public synchronized ObservableSet<Printer> getAllPrinters() {
116118
for (int i=0; i<allServices.length;i++) {
117119
if (defService != null && defService.equals(allServices[i])) {
118120
printers.add(defPrinter);
121+
pMap.put(defService, defPrinter);
119122
} else {
120-
PrinterImpl impl = new J2DPrinter(allServices[i]);
121-
Printer printer = PrintHelper.createPrinter(impl);
122-
impl.setPrinter(printer);
123-
printers.add(printer);
123+
addNew(allServices[i], printers);
124+
}
125+
}
126+
printerSet = FXCollections.observableSet(printers);
127+
returnedPrinterSet =
128+
FXCollections.unmodifiableObservableSet(printerSet);
129+
lastTime = System.currentTimeMillis();
130+
} else {
131+
PrintService[] newServices =
132+
PrintServiceLookup.lookupPrintServices(null, null);
133+
if ((newServices.length != printerSet.size()) ||
134+
(lastTime + 120000) < System.currentTimeMillis()) {
135+
updatePrinters(newServices);
136+
lastTime = System.currentTimeMillis();
137+
}
138+
}
139+
return returnedPrinterSet;
140+
}
141+
142+
private void addNew(PrintService s, Set<Printer> printers) {
143+
PrinterImpl impl = new J2DPrinter(s);
144+
Printer printer = PrintHelper.createPrinter(impl);
145+
impl.setPrinter(printer);
146+
printers.add(printer);
147+
pMap.put(s, printer);
148+
}
149+
150+
/* Only change a Printer instance if Java 2D changed it.
151+
* Otherwise re-map back to the existing Printer instance.
152+
* This relies on Java 2D not re-creating a printer too.
153+
* Update the existing set so that an app that has cached the printer list
154+
* automatically gets the updates. They can also observe changes .. but
155+
* if doing so they could see the work in progress, but that's probably
156+
* a good thing for an app that is interested.
157+
* Two passes -
158+
* First pass remove any printers that no longer exist.
159+
* Second pass add any new printers.
160+
* Finally update the default printer - if needed.
161+
*/
162+
private void updatePrinters(PrintService[] newServices) {
163+
164+
Set<PrintService> oldServiceSet = pMap.keySet();
165+
PrintService[] oldServices = oldServiceSet.toArray(new PrintService[0]);
166+
167+
for (PrintService os : oldServices) {
168+
boolean present = false;
169+
for (PrintService ns : newServices) {
170+
if (os.equals(ns)) {
171+
present = true;
172+
break;
124173
}
125174
}
126-
printerSet =
127-
FXCollections.unmodifiableObservableSet
128-
(FXCollections.observableSet(printers));
175+
if (!present) {
176+
Printer printer = pMap.get(os);
177+
pMap.remove(os);
178+
printerSet.remove(printer);
179+
}
180+
}
181+
for (PrintService s : newServices) {
182+
if (!pMap.containsKey(s)) {
183+
addNew(s, printerSet);
184+
}
185+
}
186+
PrintService oldDefaultService =
187+
(defaultPrinter == null) ? null :
188+
((J2DPrinter)PrintHelper.getPrinterImpl(defaultPrinter)).getService();
189+
PrintService newDefaultService = PrintServiceLookup.lookupDefaultPrintService();
190+
if (newDefaultService != null) {
191+
if (oldDefaultService == null ||
192+
(!oldDefaultService.equals(newDefaultService)))
193+
{
194+
defaultPrinter = findDefaultPrinter(printerSet, newDefaultService);
195+
}
196+
} else {
197+
defaultPrinter = null;
198+
}
199+
}
200+
201+
private static Printer findDefaultPrinter(Set<Printer> printers,
202+
PrintService defaultService) {
203+
for (Printer p : printers) {
204+
PrintService s = ((J2DPrinter)PrintHelper.getPrinterImpl(p)).getService();
205+
if (s.getName().equals(defaultService.getName())) {
206+
return p;
207+
}
129208
}
130-
return printerSet;
209+
return null;
131210
}
132211
}

modules/javafx.graphics/src/main/java/javafx/print/Printer.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public final class Printer {
5858
/**
5959
* Retrieve the installed printers.
6060
* The set of printers may be dynamic.
61-
* Consequently there is no guarantee that the result will be
61+
* Consequently, there is no guarantee that the result will be
6262
* the same from call to call, but should change only as
6363
* a result of the default changing in the environment of the
6464
* application.
@@ -86,10 +86,12 @@ private static ReadOnlyObjectWrapper<Printer> defaultPrinterImpl() {
8686
if (security != null) {
8787
security.checkPrintJobAccess();
8888
}
89+
Printer p = PrintPipeline.getPrintPipeline().getDefaultPrinter();
8990
if (defaultPrinter == null) {
90-
Printer p = PrintPipeline.getPrintPipeline().getDefaultPrinter();
9191
defaultPrinter =
9292
new ReadOnlyObjectWrapper<Printer>(null, "defaultPrinter", p);
93+
} else {
94+
defaultPrinter.setValue(p);
9395
}
9496
return defaultPrinter;
9597
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/*
2+
* Copyright (c) 2022, 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+
import javafx.collections.FXCollections;
27+
import javafx.collections.ObservableSet;
28+
import javafx.collections.SetChangeListener;
29+
30+
import javafx.application.Application;
31+
import javafx.print.PrinterJob;
32+
import javafx.print.Printer;
33+
import javafx.scene.Scene;
34+
import javafx.stage.Stage;
35+
import javafx.scene.control.Button;
36+
import javafx.scene.layout.VBox;
37+
import javafx.scene.text.Text;
38+
39+
public class PrinterListenerTest extends Application {
40+
41+
public static void main(String[] args) {
42+
launch(args);
43+
}
44+
45+
ObservableSet<Printer> printers;
46+
Printer defaultPrinter;
47+
Stage window;
48+
49+
public void start(Stage stage) {
50+
window = stage;
51+
printPrinters();
52+
printers.addListener(new SetChangeListener<Printer>() {
53+
public void onChanged(SetChangeListener.Change<? extends Printer> change) {
54+
printChanged(change);
55+
}
56+
});
57+
58+
VBox root = new VBox();
59+
Scene scene = new Scene(root);
60+
Button b = new Button("List Printers");
61+
b.setOnAction(e -> printPrinters());
62+
root.getChildren().add(b);
63+
Button p = new Button("Show Print Dialog");
64+
p.setOnAction(e -> showPrintDialog());
65+
root.getChildren().add(p);
66+
Text t = new Text();
67+
t.setWrappingWidth(400);
68+
t.setText(
69+
"This is a very manual test which to be useful " +
70+
"requires you to be adding and removing printers and changing " +
71+
"the default from System Settings or whatever is the norm for " +
72+
"the platform being tested and then pressing 'List Printers'. \n" +
73+
"Updates happen only when you call the API - no background thread. " +
74+
"The Added or Removed printers will be reported by the change listener " +
75+
"demonstrating that the ObservableList works.\n" +
76+
"The Print Dialog can be used to verify what is listed matches the dialog.");
77+
78+
root.getChildren().add(t);
79+
stage.setScene(scene);
80+
stage.show();
81+
}
82+
83+
public void showPrintDialog() {
84+
PrinterJob job = PrinterJob.createPrinterJob();
85+
job.showPrintDialog(window);
86+
}
87+
88+
public void printPrinters() {
89+
if (printers != null) {
90+
System.out.println("Current default printer="+defaultPrinter);
91+
System.out.println("Current Printers :");
92+
for (Printer p : printers) System.out.println(p);
93+
System.out.println();
94+
}
95+
96+
printers = Printer.getAllPrinters();
97+
defaultPrinter = Printer.getDefaultPrinter();
98+
99+
System.out.println("New Default Printer ="+defaultPrinter);
100+
System.out.println("New Printers :");
101+
for (Printer p : printers) System.out.println(p);
102+
System.out.println();
103+
}
104+
105+
static void printChanged(SetChangeListener.Change<? extends Printer> c) {
106+
if (c.wasAdded()) {
107+
System.out.println("Added : " + c.getElementAdded());
108+
} else if (c.wasRemoved()) {
109+
System.out.println("Removed : " + c.getElementRemoved());
110+
} else {
111+
System.out.println("Other change");
112+
}
113+
114+
}
115+
}

0 commit comments

Comments
 (0)