001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: * The Original Software is NetBeans. The Initial Developer of the Original
026: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
027: * Microsystems, Inc. All Rights Reserved.
028: *
029: * If you wish your version of this file to be governed by only the CDDL
030: * or only the GPL Version 2, indicate your decision by adding
031: * "[Contributor] elects to include this software in this distribution
032: * under the [CDDL or GPL Version 2] license." If you do not indicate a
033: * single choice of license, a recipient has the option to distribute
034: * your version of this file under either the CDDL, the GPL Version 2 or
035: * to extend the choice of license to its licensees as provided above.
036: * However, if you add GPL Version 2 code and therefore, elected the GPL
037: * Version 2 license, then the option applies only if the new code is
038: * made subject to such option by the copyright holder.
039: */
040:
041: package org.netbeans.lib.profiler.ui;
042:
043: import java.awt.*;
044: import java.awt.event.ComponentAdapter;
045: import java.awt.event.ComponentEvent;
046: import java.awt.image.BufferedImage;
047: import java.awt.image.PixelGrabber;
048: import java.lang.reflect.InvocationTargetException;
049: import java.util.logging.Logger;
050: import javax.swing.*;
051: import javax.swing.event.TreeExpansionEvent;
052: import javax.swing.event.TreeExpansionListener;
053: import javax.swing.plaf.basic.BasicButtonListener;
054: import javax.swing.table.JTableHeader;
055: import javax.swing.tree.TreeModel;
056: import javax.swing.tree.TreePath;
057:
058: /** Various UI utilities used in the JFluid UI
059: *
060: * @author Ian Formanek
061: * @author Jiri Sedlacek
062: */
063: public final class UIUtils {
064: //~ Static fields/initializers -----------------------------------------------------------------------------------------------
065:
066: private static final Logger LOGGER = Logger.getLogger(UIUtils.class
067: .getName());
068: public static final float ALTERNATE_ROW_DARKER_FACTOR = 0.96f;
069: private static boolean toolTipValuesInitialized = false;
070: private static Color unfocusedSelBg;
071: private static Color unfocusedSelFg;
072:
073: //~ Methods ------------------------------------------------------------------------------------------------------------------
074:
075: /** Determines if current L&F is AquaLookAndFeel */
076: public static boolean isAquaLookAndFeel() {
077: // is current L&F some kind of AquaLookAndFeel?
078: return UIManager.getLookAndFeel().getID().equals("Aqua"); //NOI18N
079: }
080:
081: public static Color getDarker(Color c) {
082: if (c.equals(Color.WHITE)) {
083: return new Color(244, 244, 244);
084: }
085:
086: return getSafeColor(
087: (int) (c.getRed() * ALTERNATE_ROW_DARKER_FACTOR),
088: (int) (c.getGreen() * ALTERNATE_ROW_DARKER_FACTOR),
089: (int) (c.getBlue() * ALTERNATE_ROW_DARKER_FACTOR));
090: }
091:
092: public static Color getDarkerLine(Color c,
093: float alternateRowDarkerFactor) {
094: return getSafeColor(
095: (int) (c.getRed() * alternateRowDarkerFactor), (int) (c
096: .getGreen() * alternateRowDarkerFactor),
097: (int) (c.getBlue() * alternateRowDarkerFactor));
098: }
099:
100: public static int getDefaultRowHeight() {
101: return new JLabel("X").getPreferredSize().height + 2; //NOI18N
102: }
103:
104: /** Determines if current L&F is GTKLookAndFeel */
105: public static boolean isGTKLookAndFeel() {
106: // is current L&F some kind of GTKLookAndFeel?
107: return UIManager.getLookAndFeel().getID().equals("GTK"); //NOI18N
108: }
109:
110: /** Determines if current L&F is MetalLookAndFeel */
111: public static boolean isMetalLookAndFeel() {
112: // is current L&F some kind of MetalLookAndFeel?
113: return UIManager.getLookAndFeel().getID().equals("Metal"); //NOI18N
114: }
115:
116: // Returns next enabled tab of JTabbedPane
117: public static int getNextSubTabIndex(JTabbedPane tabs, int tabIndex) {
118: int nextTabIndex = tabIndex;
119:
120: for (int i = 0; i < tabs.getComponentCount(); i++) {
121: nextTabIndex++;
122:
123: if (nextTabIndex == tabs.getComponentCount()) {
124: nextTabIndex = 0;
125: }
126:
127: if (tabs.isEnabledAt(nextTabIndex)) {
128: break;
129: }
130: }
131:
132: return nextTabIndex;
133: }
134:
135: public static Window getParentWindow(Component comp) {
136: while ((comp != null) && !(comp instanceof Window)) {
137: comp = comp.getParent();
138: }
139:
140: return (Window) comp;
141: }
142:
143: // Returns previous enabled tab of JTabbedPane
144: public static int getPreviousSubTabIndex(JTabbedPane tabs,
145: int tabIndex) {
146: int previousTabIndex = tabIndex;
147:
148: for (int i = 0; i < tabs.getComponentCount(); i++) {
149: previousTabIndex--;
150:
151: if (previousTabIndex < 0) {
152: previousTabIndex = tabs.getComponentCount() - 1;
153: }
154:
155: if (tabs.isEnabledAt(previousTabIndex)) {
156: break;
157: }
158: }
159:
160: return previousTabIndex;
161: }
162:
163: public static Color getSafeColor(int red, int green, int blue) {
164: red = Math.max(red, 0);
165: red = Math.min(red, 255);
166: green = Math.max(green, 0);
167: green = Math.min(green, 255);
168: blue = Math.max(blue, 0);
169: blue = Math.min(blue, 255);
170:
171: return new Color(red, green, blue);
172: }
173:
174: // Copied from org.openide.awt.HtmlLabelUI
175: /** Get the system-wide unfocused selection background color */
176: public static Color getUnfocusedSelectionBackground() {
177: if (unfocusedSelBg == null) {
178: //allow theme/ui custom definition
179: unfocusedSelBg = UIManager
180: .getColor("nb.explorer.unfocusedSelBg"); //NOI18N
181:
182: if (unfocusedSelBg == null) {
183: //try to get standard shadow color
184: unfocusedSelBg = UIManager.getColor("controlShadow"); //NOI18N
185:
186: if (unfocusedSelBg == null) {
187: //Okay, the look and feel doesn't suport it, punt
188: unfocusedSelBg = Color.lightGray;
189: }
190:
191: //Lighten it a bit because disabled text will use controlShadow/
192: //gray
193: if (!Color.WHITE.equals(unfocusedSelBg.brighter())) {
194: unfocusedSelBg = unfocusedSelBg.brighter();
195: }
196: }
197: }
198:
199: return unfocusedSelBg;
200: }
201:
202: // Copied from org.openide.awt.HtmlLabelUI
203: /** Get the system-wide unfocused selection foreground color */
204: public static Color getUnfocusedSelectionForeground() {
205: if (unfocusedSelFg == null) {
206: //allow theme/ui custom definition
207: unfocusedSelFg = UIManager
208: .getColor("nb.explorer.unfocusedSelFg"); //NOI18N
209:
210: if (unfocusedSelFg == null) {
211: //try to get standard shadow color
212: unfocusedSelFg = UIManager.getColor("textText"); //NOI18N
213:
214: if (unfocusedSelFg == null) {
215: //Okay, the look and feel doesn't suport it, punt
216: unfocusedSelFg = Color.BLACK;
217: }
218: }
219: }
220:
221: return unfocusedSelFg;
222: }
223:
224: private static Color profilerResultsBackground;
225:
226: private static Color getGTKProfilerResultsBackground() {
227: int[] pixels = new int[1];
228: pixels[0] = -1;
229:
230: // Prepare textarea to grab the color from
231: JTextArea textArea = new JTextArea();
232: textArea.setSize(new Dimension(10, 10));
233: textArea.doLayout();
234:
235: // Print the textarea to an image
236: Image image = new BufferedImage(textArea.getSize().width,
237: textArea.getSize().height, BufferedImage.TYPE_INT_RGB);
238: textArea.printAll(image.getGraphics());
239:
240: // Grab appropriate pixels to get the color
241: PixelGrabber pixelGrabber = new PixelGrabber(image, 5, 5, 1, 1,
242: pixels, 0, 1);
243: try {
244: pixelGrabber.grabPixels();
245: if (pixels[0] == -1)
246: return Color.WHITE; // System background not customized
247: } catch (InterruptedException e) {
248: return getNonGTKProfilerResultsBackground();
249: }
250:
251: return pixels[0] != -1 ? new Color(pixels[0])
252: : getNonGTKProfilerResultsBackground();
253: }
254:
255: private static Color getNonGTKProfilerResultsBackground() {
256: return UIManager.getColor("Table.background"); // NOI18N
257: }
258:
259: public static Color getProfilerResultsBackground() {
260: if (profilerResultsBackground == null) {
261: if (isGTKLookAndFeel()) {
262: profilerResultsBackground = getGTKProfilerResultsBackground();
263: } else {
264: profilerResultsBackground = getNonGTKProfilerResultsBackground();
265: }
266: if (profilerResultsBackground == null)
267: profilerResultsBackground = Color.WHITE;
268: }
269:
270: return profilerResultsBackground;
271: }
272:
273: /** Determines if current L&F is Windows Classic LookAndFeel */
274: public static boolean isWindowsClassicLookAndFeel() {
275: if (!isWindowsLookAndFeel()) {
276: return false;
277: }
278:
279: return (!isWindowsXPLookAndFeel());
280: }
281:
282: /** Determines if current L&F is WindowsLookAndFeel */
283: public static boolean isWindowsLookAndFeel() {
284: // is current L&F some kind of WindowsLookAndFeel?
285: return UIManager.getLookAndFeel().getID().equals("Windows"); //NOI18N
286: }
287:
288: /** Determines if current L&F is Windows XP LookAndFeel */
289: public static boolean isWindowsXPLookAndFeel() {
290: if (!isWindowsLookAndFeel()) {
291: return false;
292: }
293:
294: // is XP theme active in the underlying OS?
295: boolean xpThemeActiveOS = Boolean.TRUE.equals(Toolkit
296: .getDefaultToolkit().getDesktopProperty(
297: "win.xpstyle.themeActive")); //NOI18N
298: // is XP theme disabled by the application?
299:
300: boolean xpThemeDisabled = (System.getProperty("swing.noxp") != null); // NOI18N
301:
302: return ((xpThemeActiveOS) && (!xpThemeDisabled));
303: }
304:
305: /** Checks give TreePath for the last node, and if it ends with a node with just one child,
306: * it keeps expanding further.
307: * Current implementation expands through the first child that is not leaf. To more correctly
308: * fulfil expected semantics in case maxChildToExpand is > 1, it should expand all paths through
309: * all children.
310: *
311: * @param tree
312: * @param path
313: * @param maxChildToExpand
314: */
315: public static void autoExpand(JTree tree, TreePath path,
316: int maxChildToExpand, boolean dontExpandToLeafs) {
317: TreeModel model = tree.getModel();
318: Object node = path.getLastPathComponent();
319: TreePath newPath = path;
320:
321: while (!model.isLeaf(node) && (model.getChildCount(node) > 0)
322: && (model.getChildCount(node) <= maxChildToExpand)) {
323: for (int i = 0; i < model.getChildCount(node); i++) {
324: node = tree.getModel().getChild(node, i);
325:
326: if (!model.isLeaf(node)) {
327: if (dontExpandToLeafs && hasOnlyLeafs(tree, node)) {
328: break;
329: }
330:
331: newPath = newPath.pathByAddingChild(node); // if the leaf is added the path will not expand
332:
333: break; // from for
334: }
335: }
336: }
337:
338: tree.expandPath(newPath);
339: }
340:
341: /** Checks if the root of the provided tree has only one child, and if so,
342: * it autoexpands it.
343: *
344: * @param tree The tree whose root should be autoexpanded
345: */
346: public static void autoExpandRoot(JTree tree) {
347: autoExpandRoot(tree, 1);
348: }
349:
350: /** Checks if the root of the provided tree has only one child, and if so,
351: * it autoexpands it.
352: *
353: * @param tree The tree whose root should be autoexpanded
354: */
355: public static void autoExpandRoot(JTree tree, int maxChildToExpand) {
356: Object root = tree.getModel().getRoot();
357:
358: if (root == null) {
359: return;
360: }
361:
362: TreePath rootPath = new TreePath(root);
363: autoExpand(tree, rootPath, maxChildToExpand, false);
364: }
365:
366: public static long[] copyArray(long[] array) {
367: if (array == null) {
368: return null;
369: }
370:
371: if (array.length == 0) {
372: return new long[0];
373: } else {
374: long[] ret = new long[array.length];
375: System.arraycopy(array, 0, ret, 0, array.length);
376:
377: return ret;
378: }
379: }
380:
381: public static int[] copyArray(int[] array) {
382: if (array == null) {
383: return null;
384: }
385:
386: if (array.length == 0) {
387: return new int[0];
388: } else {
389: int[] ret = new int[array.length];
390: System.arraycopy(array, 0, ret, 0, array.length);
391:
392: return ret;
393: }
394: }
395:
396: public static float[] copyArray(float[] array) {
397: if (array == null) {
398: return null;
399: }
400:
401: if (array.length == 0) {
402: return new float[0];
403: } else {
404: float[] ret = new float[array.length];
405: System.arraycopy(array, 0, ret, 0, array.length);
406:
407: return ret;
408: }
409: }
410:
411: public static BufferedImage createScreenshot(Component component) {
412: if (component instanceof JScrollPane) {
413: JScrollPane scrollPane = (JScrollPane) component;
414:
415: return createComponentScreenshot(scrollPane.getViewport());
416: } else {
417: return createComponentScreenshot(component);
418: }
419: }
420:
421: public static void ensureMinimumSize(Component comp) {
422: comp = getParentWindow(comp);
423:
424: if (comp != null) {
425: final Component top = comp;
426: top.addComponentListener(new ComponentAdapter() {
427: public void componentResized(ComponentEvent e) {
428: Dimension d = top.getSize();
429: Dimension min = top.getMinimumSize();
430:
431: if ((d.width < min.width)
432: || (d.height < min.height)) {
433: top.setSize(Math.max(d.width, min.width), Math
434: .max(d.height, min.height));
435: }
436: }
437: });
438: }
439: }
440:
441: // Classic Windows LaF doesn't draw dotted focus rectangle inside JButton if parent is JToolBar,
442: // XP Windows LaF doesn't draw dotted focus rectangle inside JButton at all
443: // This method installs customized Windows LaF that draws dotted focus rectangle inside JButton always
444:
445: // On JDK 1.5 the XP Windows LaF enforces special border to all buttons, overriding any custom border
446: // set by setBorder(). Class responsible for this is WindowsButtonListener. See Issue 71546.
447: // Also fixes buttons size in JToolbar.
448:
449: /** Ensures that focus will be really painted if button is focused
450: * and fixes using custom border for JDK 1.5 & XP LaF
451: */
452: public static void fixButtonUI(AbstractButton button) {
453: // JButton
454: if (button.getUI() instanceof com.sun.java.swing.plaf.windows.WindowsButtonUI) {
455: button
456: .setUI(new com.sun.java.swing.plaf.windows.WindowsButtonUI() {
457: protected BasicButtonListener createButtonListener(
458: AbstractButton b) {
459: return new BasicButtonListener(b); // Fix for Issue 71546
460: }
461:
462: protected void paintFocus(Graphics g,
463: AbstractButton b, Rectangle viewRect,
464: Rectangle textRect, Rectangle iconRect) {
465: int width = b.getWidth();
466: int height = b.getHeight();
467: g.setColor(getFocusColor());
468: javax.swing.plaf.basic.BasicGraphicsUtils
469: .drawDashedRect(
470: g,
471: dashedRectGapX,
472: dashedRectGapY,
473: width - dashedRectGapWidth,
474: height
475: - dashedRectGapHeight);
476: }
477: });
478: }
479: // JToggleButton
480: else if (button.getUI() instanceof com.sun.java.swing.plaf.windows.WindowsToggleButtonUI) {
481: button
482: .setUI(new com.sun.java.swing.plaf.windows.WindowsToggleButtonUI() {
483: protected BasicButtonListener createButtonListener(
484: AbstractButton b) {
485: return new BasicButtonListener(b); // Fix for Issue 71546
486: }
487:
488: protected void paintFocus(Graphics g,
489: AbstractButton b, Rectangle viewRect,
490: Rectangle textRect, Rectangle iconRect) {
491: int width = b.getWidth();
492: int height = b.getHeight();
493: g.setColor(getFocusColor());
494: javax.swing.plaf.basic.BasicGraphicsUtils
495: .drawDashedRect(
496: g,
497: dashedRectGapX,
498: dashedRectGapY,
499: width - dashedRectGapWidth,
500: height
501: - dashedRectGapHeight);
502: }
503: });
504: }
505: }
506:
507: public static boolean hasOnlyLeafs(JTree tree, Object node) {
508: TreeModel model = tree.getModel();
509:
510: for (int i = 0; i < model.getChildCount(node); i++) {
511: if (!model.isLeaf(model.getChild(node, i))) {
512: return false;
513: }
514: }
515:
516: return true;
517: }
518:
519: /** By calling this method, the provided tree will become auto-expandable, i.e.
520: * When a node is expanded, if it has only one child, that child gets expanded, and so on.
521: * This is very useful for trees that have a deep node hierarchy with typical paths from
522: * root to leaves containing only one node along the whole path.
523: *
524: * @param tree The tree to make auto-expandable
525: */
526: public static void makeTreeAutoExpandable(JTree tree) {
527: makeTreeAutoExpandable(tree, 1, false);
528: }
529:
530: public static void makeTreeAutoExpandable(JTree tree,
531: final boolean dontExpandToLeafs) {
532: makeTreeAutoExpandable(tree, 1, dontExpandToLeafs);
533: }
534:
535: /** By calling this method, the provided tree will become auto-expandable, i.e.
536: * When a node is expanded, if it has only one child, that child gets expanded, and so on.
537: * This is very useful for trees that have a deep node hierarchy with typical paths from
538: * root to leaves containing only one node along the whole path.
539: *
540: * @param tree The tree to make auto-expandable
541: */
542: public static void makeTreeAutoExpandable(JTree tree,
543: final int maxChildToExpand) {
544: makeTreeAutoExpandable(tree, maxChildToExpand, false);
545: }
546:
547: public static void makeTreeAutoExpandable(JTree tree,
548: final int maxChildToExpand, final boolean dontExpandToLeafs) {
549: tree.addTreeExpansionListener(new TreeExpansionListener() {
550: boolean internalChange = false;
551:
552: public void treeCollapsed(TreeExpansionEvent event) {
553: }
554:
555: public void treeExpanded(TreeExpansionEvent event) {
556: if (internalChange) {
557: return;
558: }
559:
560: // Auto expand more if the just expanded child has only one child
561: TreePath path = event.getPath();
562: JTree tree = (JTree) event.getSource();
563: internalChange = true;
564: autoExpand(tree, path, maxChildToExpand,
565: dontExpandToLeafs);
566: internalChange = false;
567: }
568: });
569: }
570:
571: public static void runInEventDispatchThread(final Runnable r) {
572: if (SwingUtilities.isEventDispatchThread()) {
573: r.run();
574: } else {
575: SwingUtilities.invokeLater(r);
576: }
577: }
578:
579: public static void runInEventDispatchThreadAndWait(final Runnable r) {
580: if (SwingUtilities.isEventDispatchThread()) {
581: r.run();
582: } else {
583: try {
584: SwingUtilities.invokeAndWait(r);
585: } catch (InvocationTargetException e) {
586: LOGGER.severe(e.getMessage());
587: } catch (InterruptedException e) {
588: LOGGER.severe(e.getMessage());
589: }
590: }
591: }
592:
593: private static BufferedImage createComponentScreenshot(
594: final Component component) {
595: final BufferedImage[] result = new BufferedImage[1];
596:
597: final Runnable screenshotPerformer = new Runnable() {
598: public void run() {
599: if (component instanceof JTable
600: || (component instanceof JViewport && ((JViewport) component)
601: .getView() instanceof JTable)) {
602: result[0] = createTableScreenshot(component);
603: } else {
604: result[0] = createGeneralComponentScreenshot(component);
605: }
606: }
607: };
608:
609: try {
610: if (SwingUtilities.isEventDispatchThread())
611: screenshotPerformer.run();
612: else
613: SwingUtilities.invokeAndWait(screenshotPerformer);
614: } catch (Exception e) {
615: return null;
616: }
617:
618: return result[0];
619: }
620:
621: private static BufferedImage createGeneralComponentScreenshot(
622: Component component) {
623: Component source;
624: Dimension sourceSize;
625:
626: if (component instanceof JViewport) {
627: JViewport viewport = (JViewport) component;
628: Component contents = viewport.getView();
629:
630: if (contents.getSize().height > viewport.getSize().height) {
631: source = component;
632: sourceSize = component.getSize();
633: } else {
634: source = contents;
635: sourceSize = contents.getSize();
636: }
637: } else {
638: source = component;
639: sourceSize = component.getSize();
640: }
641:
642: BufferedImage componentScreenshot = new BufferedImage(
643: sourceSize.width, sourceSize.height,
644: BufferedImage.TYPE_INT_RGB);
645: Graphics componentScreenshotGraphics = componentScreenshot
646: .getGraphics();
647: source.printAll(componentScreenshotGraphics);
648:
649: return componentScreenshot;
650: }
651:
652: private static BufferedImage createTableScreenshot(
653: Component component) {
654: Component source;
655: Dimension sourceSize;
656: JTable table;
657:
658: if (component instanceof JTable) {
659: table = (JTable) component;
660:
661: if ((table.getTableHeader() == null)
662: || !table.getTableHeader().isVisible()) {
663: return createGeneralComponentScreenshot(component);
664: }
665:
666: source = table;
667: sourceSize = table.getSize();
668: } else if (component instanceof JViewport
669: && ((JViewport) component).getView() instanceof JTable) {
670: JViewport viewport = (JViewport) component;
671: table = (JTable) viewport.getView();
672:
673: if ((table.getTableHeader() == null)
674: || !table.getTableHeader().isVisible()) {
675: return createGeneralComponentScreenshot(component);
676: }
677:
678: if (table.getSize().height > viewport.getSize().height) {
679: source = viewport;
680: sourceSize = viewport.getSize();
681: } else {
682: source = table;
683: sourceSize = table.getSize();
684: }
685: } else {
686: throw new IllegalArgumentException(
687: "Component can only be JTable or JViewport holding JTable"); // NOI18N
688: }
689:
690: final JTableHeader tableHeader = table.getTableHeader();
691: Dimension tableHeaderSize = tableHeader.getSize();
692:
693: BufferedImage tableScreenshot = new BufferedImage(
694: sourceSize.width, tableHeaderSize.height
695: + sourceSize.height, BufferedImage.TYPE_INT_RGB);
696: final Graphics tableScreenshotGraphics = tableScreenshot
697: .getGraphics();
698:
699: // Component.printAll has to run in AWT Thread to print component contents correctly
700: if (SwingUtilities.isEventDispatchThread()) {
701: tableHeader.printAll(tableScreenshotGraphics);
702: } else {
703: try {
704: SwingUtilities.invokeAndWait(new Runnable() {
705: public void run() {
706: tableHeader.printAll(tableScreenshotGraphics);
707: }
708: });
709: } catch (Exception e) {
710: }
711: }
712:
713: tableScreenshotGraphics.translate(0, tableHeaderSize.height);
714:
715: final Component printSrc = source;
716:
717: // Component.printAll has to run in AWT Thread to print component contents correctly
718: if (SwingUtilities.isEventDispatchThread()) {
719: printSrc.printAll(tableScreenshotGraphics);
720: } else {
721: try {
722: SwingUtilities.invokeAndWait(new Runnable() {
723: public void run() {
724: printSrc.printAll(tableScreenshotGraphics);
725: }
726: });
727: } catch (Exception e) {
728: }
729: }
730:
731: return tableScreenshot;
732: }
733: }
|