source: trunk/src/org/expeditee/gio/swing/SwingGraphicsManager.java@ 1198

Last change on this file since 1198 was 1198, checked in by bln4, 5 years ago

org.expeditee.gio.GraphicsManager ->
org.expeditee.gio.javafx.JavaFXGraphicsManager ->
org.expeditee.gio.swing.SwingGraphicsManager ->

Graphics managers can be now consulted to obtain the height of a character in a given font.

File size: 18.8 KB
Line 
1package org.expeditee.gio.swing;
2
3import java.awt.AlphaComposite;
4import java.awt.Component;
5import java.awt.Container;
6import java.awt.FontMetrics;
7import java.awt.Graphics;
8import java.awt.Graphics2D;
9import java.awt.GraphicsEnvironment;
10import java.awt.RenderingHints;
11import java.awt.Toolkit;
12import java.awt.event.ComponentListener;
13import java.awt.event.KeyListener;
14import java.awt.event.WindowListener;
15import java.awt.event.WindowStateListener;
16import java.awt.geom.AffineTransform;
17import java.awt.image.ImageObserver;
18
19import javax.swing.JFrame;
20import javax.swing.JLayeredPane;
21import javax.swing.JMenuBar;
22import javax.swing.JOptionPane;
23import javax.swing.RepaintManager;
24import javax.swing.SwingUtilities;
25import javax.swing.TransferHandler;
26
27import org.expeditee.core.Clip;
28import org.expeditee.core.Colour;
29import org.expeditee.core.Cursor;
30import org.expeditee.core.Dimension;
31import org.expeditee.core.Fill;
32import org.expeditee.core.Font;
33import org.expeditee.core.GradientFill;
34import org.expeditee.core.Image;
35import org.expeditee.core.Point;
36import org.expeditee.core.Stroke;
37import org.expeditee.core.TextLayout;
38import org.expeditee.core.bounds.PolygonBounds;
39import org.expeditee.gio.GraphicsManager;
40import org.expeditee.gio.GraphicsSurfaceStack;
41import org.expeditee.gio.swing.SwingImageManager.BlockingImageObserver;
42import org.expeditee.gio.swing.SwingImageManager.SelfAnimatingImageObserver;
43import org.expeditee.gui.DisplayController;
44import org.expeditee.items.widgets.SwingWidget;
45import org.expeditee.items.widgets.Widget;
46
47// TODO: Make stack thread-safe
48public class SwingGraphicsManager extends GraphicsManager {
49
50 private static SwingGraphicsManager _instance;
51
52 public static SwingGraphicsManager getInstance() {
53 if (_instance == null) {
54 try {
55 SwingUtilities.invokeAndWait(new Runnable() {
56 @Override
57 public void run() {
58 _instance = new SwingGraphicsManager();
59 }
60 });
61 } catch (Exception e) {
62 System.err.println("Error while initialising GraphicsManager. Aborting...");
63 e.printStackTrace(System.err);
64 System.exit(1);
65 }
66
67 // Block until the window is ready
68
69 }
70
71 return _instance;
72 }
73
74 private JFrame _jFrame;
75
76 private GraphicsSurfaceStack<Graphics2D> _surfaceStack;
77
78 private SwingGraphicsManager() {
79 _jFrame = new JFrame() {
80 private static final long serialVersionUID = 5179259234365906415L;
81
82 // Override the paint() method so that Expeditee's graphics are drawn
83 // when the window refreshes.
84 @Override
85 public void paint(Graphics g) {
86 DisplayController.requestRefresh(false);
87 }
88 };
89
90 setWindowIcon();
91
92 /*
93 * See Java bug ID 4016934. They say that window closed events are called once
94 * the JFrame is disposed.
95 */
96 _jFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
97
98 // Expeditee handles its own repainting of AWT/Swing components
99 RepaintManager.setCurrentManager(ExpediteeRepaintManager.getInstance());
100
101 // required to accept TAB key
102 _jFrame.setFocusTraversalKeysEnabled(false);
103
104 _jFrame.pack();
105
106 // Moved to here to work with JFXPanel
107 // TODO: Is the above comment still relevant? cts16
108 _jFrame.getContentPane().setLayout(new AbsoluteLayout());
109
110 // Set visible must be just after DisplayIO.Init for the message box to be the
111 // right size
112 // TODO: Is the above comment still relevant? cts16
113 _jFrame.setVisible(true);
114
115 // Create the surface stack
116 _surfaceStack = new GraphicsSurfaceStack<Graphics2D>() {
117 @Override
118 public Graphics2D getSurfaceFromImage(Image image) {
119 return SwingMiscManager.getIfUsingSwingImageManager().getImageGraphics(image);
120 }
121
122 @Override
123 public void setSurfaceClip(Graphics2D surface, Clip clip) {
124 if (surface == null) {
125 return;
126 }
127
128 if (clip == null) {
129 surface.setClip(null);
130 return;
131 }
132
133 if (clip.isFullyClipped()) {
134 return;
135 }
136
137 surface.setClip(SwingConversions.toSwingRectangle(clip.getBounds()));
138 }
139 };
140 refreshRootSurface();
141 }
142
143 @Override
144 protected GraphicsSurfaceStack<?> getGraphicsSurfaceStack() {
145 return _surfaceStack;
146 }
147
148 @Override
149 public Dimension getScreenSize() {
150 java.awt.Dimension size = Toolkit.getDefaultToolkit().getScreenSize();
151
152 return SwingConversions.fromSwingDimension(size);
153 }
154
155 @Override
156 public Point getWindowLocation() {
157 return SwingConversions.fromSwingPoint(_jFrame.getContentPane().getLocationOnScreen());
158 }
159
160 @Override
161 public void setWindowLocation(Point p) {
162 _jFrame.setLocation(p.getX(), p.getY());
163 }
164
165 @Override
166 public Dimension getWindowSize() {
167 return SwingConversions.fromSwingDimension(_jFrame.getContentPane().getSize());
168 }
169
170 @Override
171 public void setWindowSize(Dimension d) {
172 _jFrame.setSize(d.width, d.height);
173 _jFrame.setPreferredSize(SwingConversions.toSwingDimension(d));
174 // Issue a command to graphics so that the JFrame draws itself white.
175 _jFrame.getGraphics().clearRect(0, 0, d.width, d.height);
176 }
177
178 @Override
179 public void requestFocus() {
180 _jFrame.requestFocus();
181 }
182
183 @Override
184 public void setCompositeAlpha(float alpha) {
185 _surfaceStack.getCurrentSurface().setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
186 }
187
188 @Override
189 public void setAntialiasing(boolean on) {
190 setAntialiasing(_surfaceStack.getCurrentSurface(), on);
191 }
192
193 private void setAntialiasing(Graphics2D surface, boolean on) {
194 if (surface == null) {
195 return;
196 }
197
198 if (on) {
199 surface.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
200 } else {
201 surface.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
202 }
203 }
204
205 @Override
206 public void setFont(Font font) {
207 _surfaceStack.getCurrentSurface().setFont(SwingMiscManager.getIfUsingSwingFontManager().getInternalFont(font));
208 }
209
210 @Override
211 public void drawImage(Image image, Point topLeft, Dimension size, double angle, Point cropTopLeft,
212 Dimension cropSize) {
213 // Can't draw nothing
214 if (image == null) {
215 return;
216 }
217
218 // Can't draw nowhere
219 if (topLeft == null) {
220 return;
221 }
222
223 // If the cropped area is degenerate, abort
224 if (cropSize != null && (cropSize.width <= 0 || cropSize.height <= 0)) {
225 return;
226 }
227
228 // If the crop area is the entire image, pretend we are not cropping
229 if (Point.ORIGIN.equals(cropTopLeft) && image.getSize().equals(cropSize)) {
230 cropTopLeft = null;
231 cropSize = null;
232 }
233
234 SwingImageManager manager = SwingMiscManager.getIfUsingSwingImageManager();
235
236 ImageObserver animator = null;
237 if (image.isAnimated()) {
238 animator = manager.getAnimator(image);
239 if (animator != null) {
240 ((SelfAnimatingImageObserver) animator).activate();
241 }
242 } else {
243 animator = new SwingImageManager.LoadCompletedImageObserver();
244 }
245
246 // Default is the entire image
247 Image croppedImage = image;
248
249 // If we are cropping, do this now into a temporary image
250 if (cropTopLeft != null && cropSize != null) {
251 croppedImage = manager.createImage(cropSize.width, cropSize.height);
252 manager.getInternalImage(croppedImage).getGraphics().drawImage(manager.getInternalImage(image), 0, 0,
253 cropSize.width - 1, cropSize.height - 1, cropTopLeft.getX(), cropTopLeft.getY(),
254 cropTopLeft.getX() + cropSize.width - 1, cropTopLeft.getY() + cropSize.height - 1, animator);
255 animator = new BlockingImageObserver();
256 }
257
258 // Transform the image
259 AffineTransform tx = new AffineTransform();
260 if (size != null) {
261 tx.scale(((double) size.width) / croppedImage.getWidth(),
262 ((double) size.height) / croppedImage.getHeight());
263 }
264 if (angle != 0.0) {
265 tx.rotate(angle);
266 }
267 if (topLeft != null) {
268 tx.translate(topLeft.getX(), topLeft.getY());
269 }
270
271 // Draw the image to the current surface
272 boolean drawn = _surfaceStack.getCurrentSurface().drawImage(manager.getInternalImage(croppedImage), tx,
273 animator);
274
275 // If the draw didn't succeed, try again after waiting for the image to load
276 if (!drawn && animator instanceof BlockingImageObserver) {
277 ((BlockingImageObserver) animator).attemptWait();
278 _surfaceStack.getCurrentSurface().drawImage(manager.getInternalImage(croppedImage), tx, animator);
279 }
280 }
281
282 @Override
283 public void drawRectangle(Point topLeft, Dimension size, double angle, Fill fill, Colour borderColour,
284 Stroke borderStroke, Dimension cornerRadius) {
285 Graphics2D g = (Graphics2D) _surfaceStack.getCurrentSurface().create();
286
287 if (angle != 0.0) {
288 AffineTransform tx = new AffineTransform();
289 tx.rotate(angle);
290 g.transform(tx);
291 }
292
293 if (fill != null) {
294 if (fill instanceof GradientFill) {
295 g.setPaint(SwingConversions.toSwingGradientPaint((GradientFill) fill));
296 } else {
297 g.setColor(SwingConversions.toSwingColor(fill.getColour()));
298 }
299
300 if (cornerRadius != null) {
301 g.fillRoundRect(topLeft.getX(), topLeft.getY(), size.width, size.height, cornerRadius.width, cornerRadius.height);
302 } else {
303 g.fillRect(topLeft.getX(), topLeft.getY(), size.width, size.height);
304 }
305 }
306
307 if (borderColour != null && borderStroke != null) {
308 g.setColor(SwingConversions.toSwingColor(borderColour));
309 g.setStroke(SwingConversions.toSwingStroke(borderStroke));
310
311 if (cornerRadius != null) {
312 g.drawRoundRect(topLeft.getX(), topLeft.getY(), size.width, size.height, cornerRadius.width, cornerRadius.height);
313 } else {
314 g.drawRect(topLeft.getX(), topLeft.getY(), size.width, size.height);
315 }
316 }
317
318 g.dispose();
319 }
320
321 @Override
322 public void drawOval(Point centre, Dimension diameters, double angle, Fill fill, Colour borderColour,
323 Stroke borderStroke) {
324 Graphics2D g = (Graphics2D) _surfaceStack.getCurrentSurface().create();
325
326 if (angle != 0.0) {
327 AffineTransform tx = new AffineTransform();
328 tx.rotate(angle);
329 g.transform(tx);
330 }
331
332 Point topLeft = new Point(centre.getX() - diameters.width / 2, centre.getY() - diameters.height / 2);
333
334 if (fill != null) {
335 if (fill instanceof GradientFill) {
336 g.setPaint(SwingConversions.toSwingGradientPaint((GradientFill) fill));
337 } else {
338 g.setColor(SwingConversions.toSwingColor(fill.getColour()));
339 }
340
341 g.fillOval(topLeft.getX(), topLeft.getY(), diameters.width, diameters.height);
342 }
343
344 if (borderColour != null && borderStroke != null) {
345 g.setColor(SwingConversions.toSwingColor(borderColour));
346 g.setStroke(SwingConversions.toSwingStroke(borderStroke));
347
348 g.drawOval(topLeft.getX(), topLeft.getY(), diameters.width, diameters.height);
349 }
350
351 g.dispose();
352 }
353
354 @Override
355 public void drawPolygon(PolygonBounds points, Point offset, Dimension scale, double angle, Fill fill,
356 Colour borderColour, Stroke borderStroke) {
357 if (points == null || points.getPointCount() < 2) {
358 return;
359 }
360 if (fill == null && (borderColour == null || borderStroke == null)) {
361 return;
362 }
363
364 Graphics2D g = (Graphics2D) _surfaceStack.getCurrentSurface().create();
365
366 AffineTransform tx = new AffineTransform();
367 Point centre = points.getCentre();
368 tx.translate(-centre.getX(), -centre.getY());
369 if (angle != 0.0) {
370 tx.rotate(angle);
371 }
372 if (scale != null) {
373 double xScale = ((double) scale.width) / (points.getMaxX() - points.getMinX());
374 double yScale = ((double) scale.height) / (points.getMaxY() - points.getMinY());
375 tx.scale(xScale, yScale);
376 }
377 tx.translate(centre.getX(), centre.getY());
378 if (offset != null) {
379 tx.translate(offset.getX(), offset.getY());
380 }
381 g.transform(tx);
382
383 if (fill != null) {
384 if (fill instanceof GradientFill) {
385 g.setPaint(SwingConversions.toSwingGradientPaint((GradientFill) fill));
386 } else {
387 g.setColor(SwingConversions.toSwingColor(fill.getColour()));
388 }
389
390 g.fillPolygon(SwingConversions.toSwingPolygon(points));
391 }
392
393 if (borderColour != null && borderStroke != null) {
394 g.setColor(SwingConversions.toSwingColor(borderColour));
395 g.setStroke(SwingConversions.toSwingStroke(borderStroke));
396
397 if (points.isClosed()) {
398 g.drawPolygon(SwingConversions.toSwingPolygon(points));
399 } else {
400 int nPoints = points.getPointCount();
401 int[] xPoints = new int[nPoints];
402 int[] yPoints = new int[nPoints];
403
404 for (int i = 0; i < nPoints; i++) {
405 Point point = points.getPoint(i);
406 xPoints[i] = point.getX();
407 yPoints[i] = point.getY();
408 }
409
410 g.drawPolyline(xPoints, yPoints, nPoints);
411 }
412 }
413
414 g.dispose();
415 }
416
417 @Override
418 public void drawLine(int x1, int y1, int x2, int y2, Colour colour, Stroke stroke) {
419 if (colour == null || stroke == null) {
420 return;
421 }
422
423 Graphics2D g = (Graphics2D) _surfaceStack.getCurrentSurface().create();
424 g.setColor(SwingConversions.toSwingColor(colour));
425 g.setStroke(SwingConversions.toSwingStroke(stroke));
426
427 g.drawLine(x1, y1, x2, y2);
428
429 g.dispose();
430 }
431
432 @Override
433 public void drawString(String string, Point position, Font font, Colour colour) {
434 if (string == null || position == null || font == null || colour == null) {
435 return;
436 }
437
438 Graphics2D g = (Graphics2D) _surfaceStack.getCurrentSurface().create();
439 g.setColor(SwingConversions.toSwingColor(colour));
440 g.setFont(SwingMiscManager.getIfUsingSwingFontManager().getInternalFont(font));
441
442 g.drawString(string, position.getX(), position.getY());
443
444 g.dispose();
445 }
446
447 @Override
448 public void drawTextLayout(TextLayout layout, Point position, Colour colour) {
449 if (layout == null) {
450 return;
451 }
452
453 SwingTextLayoutManager layoutManager = SwingMiscManager.getIfUsingSwingTextLayoutManager();
454 if (layoutManager == null) {
455 drawString(layout.getLine(), position, layout.getFont(), colour);
456 return;
457 }
458
459 java.awt.font.TextLayout swingLayout = layoutManager.getInternalLayout(layout);
460
461 Graphics2D g = (Graphics2D) _surfaceStack.getCurrentSurface().create();
462 g.setColor(SwingConversions.toSwingColor(colour));
463
464 swingLayout.draw(g, position.getX(), position.getY());
465 }
466
467 private Dimension _preFullscreenSize = null;
468 private boolean _fullScreenTransitionPending = false;
469
470 /**
471 * Whether or not we are in the middle of transitioning to/from fullscreen.
472 */
473 public boolean isFullscreenTransitionPending() {
474 return _fullScreenTransitionPending;
475 }
476
477 /**
478 * Should be called when the SwingInputManager is notified that the fullscreen
479 * transition has finished.
480 */
481 public void finishFullscreenTransition() {
482 _fullScreenTransitionPending = false;
483 }
484
485 @Override
486 public boolean canGoFullscreen() {
487 return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().isFullScreenSupported();
488 }
489
490 @Override
491 public boolean isFullscreen() {
492 return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice()
493 .getFullScreenWindow() == _jFrame;
494 }
495
496 @Override
497 public void goFullscreen() {
498 if (!canGoFullscreen()) {
499 System.err.println("Warning: GraphicsManager::goFullScreen() called when not available -- ignoring");
500 return;
501 }
502
503 _preFullscreenSize = getWindowSize();
504
505 _fullScreenTransitionPending = true;
506
507 _jFrame.dispose();
508 _jFrame.setUndecorated(true);
509 _jFrame.pack();
510 _jFrame.setVisible(true);
511
512 GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().setFullScreenWindow(_jFrame);
513 }
514
515 @Override
516 public void exitFullscreen() {
517 _fullScreenTransitionPending = true;
518
519 _jFrame.dispose();
520 _jFrame.setUndecorated(false);
521 setWindowSize(_preFullscreenSize);
522
523 GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().setFullScreenWindow(null);
524 _jFrame.pack();
525 _jFrame.setVisible(true);
526
527 }
528
529 @Override
530 public void setCursor(Cursor cursor) {
531 if (cursor == null) {
532 return;
533 }
534
535 java.awt.Cursor swingCursor;
536 if (cursor.getType() == Cursor.CursorType.CUSTOM) {
537 SwingImageManager imageManager = SwingMiscManager.getIfUsingSwingImageManager();
538
539 if (imageManager == null) {
540 return;
541 }
542
543 swingCursor = Toolkit.getDefaultToolkit().createCustomCursor(
544 imageManager.getInternalImage(cursor.getImage()),
545 SwingConversions.toSwingPoint(cursor.getHotspot()), cursor.getName());
546 } else {
547 swingCursor = new java.awt.Cursor(SwingConversions.toSwingCursorType(cursor.getType()));
548 }
549
550 _jFrame.setCursor(swingCursor);
551 }
552
553 @Override
554 public Dimension getBestCursorSize(Dimension desiredSize) {
555 Toolkit toolkit = Toolkit.getDefaultToolkit();
556 java.awt.Dimension best_cursor_dim = toolkit.getBestCursorSize(desiredSize.width, desiredSize.height);
557 return SwingConversions.fromSwingDimension(best_cursor_dim);
558 }
559
560 @Override
561 public void setWindowTitle(String title) {
562 _jFrame.setTitle(title);
563 }
564
565 @Override
566 public boolean showDialog(String title, String message) {
567 int result = JOptionPane.showConfirmDialog(_jFrame, message, title, JOptionPane.OK_CANCEL_OPTION,
568 JOptionPane.WARNING_MESSAGE);
569 return result == JOptionPane.OK_OPTION;
570 }
571
572 @Override
573 public Dimension getCurrentDrawingSurfaceSize() {
574 Image currentImage = _surfaceStack.getCurrentImage();
575
576 if (currentImage != null) {
577 return currentImage.getSize();
578 } else {
579 return getWindowSize();
580 }
581 }
582
583 public void refreshRootSurface() {
584 Graphics2D rootSurface = (Graphics2D) _jFrame.getContentPane().getGraphics();
585 setAntialiasing(rootSurface, true);
586 final java.awt.Font rootSurfaceFont = rootSurface.getFont().deriveFont(40f);
587 rootSurface.setFont(rootSurfaceFont);
588 _surfaceStack.setRootSurface(rootSurface);
589 }
590
591 public JFrame getJFrame() {
592 return _jFrame;
593 }
594
595 public Container getContentPane() {
596 return _jFrame.getContentPane();
597 }
598
599 public JLayeredPane getLayeredPane() {
600 return _jFrame.getLayeredPane();
601 }
602
603 public void setTransferHandler(TransferHandler newHandler) {
604 _jFrame.setTransferHandler(newHandler);
605 }
606
607 public void addWindowListener(WindowListener l) {
608 _jFrame.addWindowListener(l);
609 }
610
611 public void addWindowStateListener(WindowStateListener l) {
612 _jFrame.addWindowStateListener(l);
613 }
614
615 public void addKeyListener(KeyListener l) {
616 _jFrame.addKeyListener(l);
617 }
618
619 public void addComponentListener(ComponentListener l) {
620 _jFrame.addComponentListener(l);
621 }
622
623 public void setGlassPane(Component glassPane) {
624 _jFrame.setGlassPane(glassPane);
625 }
626
627 public Component getGlassPane() {
628 return _jFrame.getGlassPane();
629 }
630
631 public JMenuBar getJMenuBar() {
632 return _jFrame.getJMenuBar();
633 }
634
635 public FontMetrics getFontMetrics(java.awt.Font font) {
636 return _jFrame.getFontMetrics(font);
637 }
638
639 public java.awt.Font getFont() {
640 return _jFrame.getFont();
641 }
642
643 public Graphics2D getCurrentSurface() {
644 return _surfaceStack.getCurrentSurface();
645 }
646
647 @Override
648 public boolean addInteractiveWidget(Widget iw) {
649 if (super.addInteractiveWidget(iw)) {
650 _jFrame.getContentPane().add(((SwingWidget) iw).getComponent());
651 return true;
652 }
653
654 return false;
655 }
656
657 @Override
658 public boolean removeInteractiveWidget(Widget iw) {
659 if (super.removeInteractiveWidget(iw)) {
660 _jFrame.getContentPane().remove(((SwingWidget) iw).getComponent());
661 return true;
662 }
663
664 return false;
665 }
666
667 @Override
668 protected void setWindowIcon(Image image) {
669 SwingImageManager imageManager = SwingMiscManager.getIfUsingSwingImageManager();
670
671 if (imageManager != null) {
672 _jFrame.setIconImage(imageManager.getInternalImage(image));
673 }
674 }
675
676 @Override
677 public int getFontHeight(final Font font) {
678 final java.awt.Font awtFont = SwingFontManager.getInstance().getInternalFont(font);
679 final FontMetrics fontMetrics = _jFrame.getGraphics().getFontMetrics(awtFont);
680 return fontMetrics.getHeight();
681 }
682}
Note: See TracBrowser for help on using the repository browser.