1 /* 2 * Copyright (c) 2005, 2013, 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 package sun.swing; 26 27 import javax.swing.*; 28 import java.awt.*; 29 import java.awt.event.ActionEvent; 30 import java.awt.event.WindowAdapter; 31 import java.awt.event.WindowEvent; 32 import java.awt.print.PageFormat; 33 import java.awt.print.Printable; 34 import java.awt.print.PrinterException; 35 import java.awt.print.PrinterJob; 36 import java.text.MessageFormat; 37 import java.util.concurrent.atomic.AtomicBoolean; 38 import java.lang.reflect.InvocationTargetException; 39 40 /** 41 * The {@code PrintingStatus} provides a dialog that displays progress 42 * of the printing job and provides a way to abort it 43 * <p/> 44 * Methods of these class are thread safe, although most Swing methods 45 * are not. Please see 46 * <A HREF="https://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html">Concurrency 47 * in Swing</A> for more information. 48 * 49 * @author Alexander Potochkin 50 * @since 1.6 51 */ 52 53 public class PrintingStatus { 54 55 private final PrinterJob job; 56 private final Component parent; 57 private JDialog abortDialog; 58 59 private JButton abortButton; 60 private JLabel statusLabel; 61 private MessageFormat statusFormat; 62 private final AtomicBoolean isAborted = new AtomicBoolean(false); 63 64 // the action that will abort printing 65 private final Action abortAction = new AbstractAction() { 66 public void actionPerformed(ActionEvent ae) { 67 if (!isAborted.get()) { 68 isAborted.set(true); 69 70 // update the status abortDialog to indicate aborting 71 abortButton.setEnabled(false); 72 abortDialog.setTitle( 73 UIManager.getString("PrintingDialog.titleAbortingText")); 74 statusLabel.setText( 75 UIManager.getString("PrintingDialog.contentAbortingText")); 76 77 // cancel the PrinterJob 78 job.cancel(); 79 } 80 } 81 }; 82 83 private final WindowAdapter closeListener = new WindowAdapter() { 84 public void windowClosing(WindowEvent we) { 85 abortAction.actionPerformed(null); 86 } 87 }; 88 89 /** 90 * Creates PrintingStatus instance 91 * 92 * @param parent a <code>Component</code> object to be used 93 * as parent component for PrintingStatus dialog 94 * @param job a <code>PrinterJob</code> object to be cancelled 95 * using this <code>PrintingStatus</code> dialog 96 * @return a <code>PrintingStatus</code> object 97 */ 98 public static PrintingStatus 99 createPrintingStatus(Component parent, PrinterJob job) { 100 return new PrintingStatus(parent, job); 101 } 102 103 protected PrintingStatus(Component parent, PrinterJob job) { 104 this.job = job; 105 this.parent = parent; 106 } 107 108 private void init() { 109 // prepare the status JOptionPane 110 String progressTitle = 111 UIManager.getString("PrintingDialog.titleProgressText"); 112 113 String dialogInitialContent = 114 UIManager.getString("PrintingDialog.contentInitialText"); 115 116 // this one's a MessageFormat since it must include the page 117 // number in its text 118 statusFormat = new MessageFormat( 119 UIManager.getString("PrintingDialog.contentProgressText")); 120 121 String abortText = 122 UIManager.getString("PrintingDialog.abortButtonText"); 123 String abortTooltip = 124 UIManager.getString("PrintingDialog.abortButtonToolTipText"); 125 int abortMnemonic = 126 getInt("PrintingDialog.abortButtonMnemonic", -1); 127 int abortMnemonicIndex = 128 getInt("PrintingDialog.abortButtonDisplayedMnemonicIndex", -1); 129 130 abortButton = new JButton(abortText); 131 abortButton.addActionListener(abortAction); 132 133 abortButton.setToolTipText(abortTooltip); 134 if (abortMnemonic != -1) { 135 abortButton.setMnemonic(abortMnemonic); 136 } 137 if (abortMnemonicIndex != -1) { 138 abortButton.setDisplayedMnemonicIndex(abortMnemonicIndex); 139 } 140 statusLabel = new JLabel(dialogInitialContent); 141 JOptionPane abortPane = new JOptionPane(statusLabel, 142 JOptionPane.INFORMATION_MESSAGE, 143 JOptionPane.DEFAULT_OPTION, 144 null, new Object[]{abortButton}, 145 abortButton); 146 abortPane.getActionMap().put("close", abortAction); 147 148 // The dialog should be centered over the viewport if the table is in one 149 if (parent != null && parent.getParent() instanceof JViewport) { 150 abortDialog = 151 abortPane.createDialog(parent.getParent(), progressTitle); 152 } else { 153 abortDialog = abortPane.createDialog(parent, progressTitle); 154 } 155 // clicking the X button should not hide the dialog 156 abortDialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); 157 abortDialog.addWindowListener(closeListener); 158 } 159 160 /** 161 * Shows PrintingStatus dialog. 162 * if dialog is modal this method returns only 163 * after <code>dispose()</code> was called otherwise returns immediately 164 * 165 * @param isModal <code>true</code> this dialog should be modal; 166 * <code>false</code> otherwise. 167 * @see #dispose 168 */ 169 public void showModal(final boolean isModal) { 170 if (SwingUtilities.isEventDispatchThread()) { 171 showModalOnEDT(isModal); 172 } else { 173 try { 174 SwingUtilities.invokeAndWait(new Runnable() { 175 public void run() { 176 showModalOnEDT(isModal); 177 } 178 }); 179 } catch(InterruptedException e) { 180 throw new RuntimeException(e); 181 } catch(InvocationTargetException e) { 182 Throwable cause = e.getCause(); 183 if (cause instanceof RuntimeException) { 184 throw (RuntimeException) cause; 185 } else if (cause instanceof Error) { 186 throw (Error) cause; 187 } else { 188 throw new RuntimeException(cause); 189 } 190 } 191 } 192 } 193 194 /** 195 * The EDT part of the showModal method. 196 * 197 * This method is to be called on the EDT only. 198 */ 199 private void showModalOnEDT(boolean isModal) { 200 assert SwingUtilities.isEventDispatchThread(); 201 init(); 202 abortDialog.setModal(isModal); 203 abortDialog.setVisible(true); 204 } 205 206 /** 207 * Disposes modal PrintingStatus dialog 208 * 209 * @see #showModal(boolean) 210 */ 211 public void dispose() { 212 if (SwingUtilities.isEventDispatchThread()) { 213 disposeOnEDT(); 214 } else { 215 SwingUtilities.invokeLater(new Runnable() { 216 public void run() { 217 disposeOnEDT(); 218 } 219 }); 220 } 221 } 222 223 /** 224 * The EDT part of the dispose method. 225 * 226 * This method is to be called on the EDT only. 227 */ 228 private void disposeOnEDT() { 229 assert SwingUtilities.isEventDispatchThread(); 230 if (abortDialog != null) { 231 abortDialog.removeWindowListener(closeListener); 232 abortDialog.dispose(); 233 abortDialog = null; 234 } 235 } 236 237 /** 238 * Returns whether the printng was aborted using this PrintingStatus 239 * 240 * @return whether the printng was aborted using this PrintingStatus 241 */ 242 public boolean isAborted() { 243 return isAborted.get(); 244 } 245 246 /** 247 * Returns printable which is used to track the current page being 248 * printed in this PrintingStatus 249 * 250 * @param printable to be used to create notification printable 251 * @return printable which is used to track the current page being 252 * printed in this PrintingStatus 253 * @throws NullPointerException if <code>printable</code> is <code>null</code> 254 */ 255 public Printable createNotificationPrintable(Printable printable) { 256 return new NotificationPrintable(printable); 257 } 258 259 private class NotificationPrintable implements Printable { 260 private final Printable printDelegatee; 261 262 public NotificationPrintable(Printable delegatee) { 263 if (delegatee == null) { 264 throw new NullPointerException("Printable is null"); 265 } 266 this.printDelegatee = delegatee; 267 } 268 269 public int print(final Graphics graphics, 270 final PageFormat pageFormat, final int pageIndex) 271 throws PrinterException { 272 273 final int retVal = 274 printDelegatee.print(graphics, pageFormat, pageIndex); 275 if (retVal != NO_SUCH_PAGE && !isAborted()) { 276 if (SwingUtilities.isEventDispatchThread()) { 277 updateStatusOnEDT(pageIndex); 278 } else { 279 SwingUtilities.invokeLater(new Runnable() { 280 public void run() { 281 updateStatusOnEDT(pageIndex); 282 } 283 }); 284 } 285 } 286 return retVal; 287 } 288 289 /** 290 * The EDT part of the print method. 291 * 292 * This method is to be called on the EDT only. 293 */ 294 private void updateStatusOnEDT(int pageIndex) { 295 assert SwingUtilities.isEventDispatchThread(); 296 Object[] pageNumber = new Object[]{ 297 new Integer(pageIndex + 1)}; 298 statusLabel.setText(statusFormat.format(pageNumber)); 299 } 300 } 301 302 /** 303 * Duplicated from UIManager to make it visible 304 */ 305 static int getInt(Object key, int defaultValue) { 306 Object value = UIManager.get(key); 307 if (value instanceof Integer) { 308 return ((Integer) value).intValue(); 309 } 310 if (value instanceof String) { 311 try { 312 return Integer.parseInt((String) value); 313 } catch(NumberFormatException nfe) { 314 } 315 } 316 return defaultValue; 317 } 318 }