MultiBackend and MultiScreen
[fanfix.git] / src / jexer / backend / SwingComponent.java
1 /*
2 * Jexer - Java Text User Interface
3 *
4 * The MIT License (MIT)
5 *
6 * Copyright (C) 2017 Kevin Lamonte
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
25 *
26 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
27 * @version 1
28 */
29 package jexer.backend;
30
31 import java.awt.Color;
32 import java.awt.Cursor;
33 import java.awt.Font;
34 import java.awt.Graphics;
35 import java.awt.Insets;
36 import java.awt.Point;
37 import java.awt.Toolkit;
38 import java.awt.event.ComponentListener;
39 import java.awt.event.KeyListener;
40 import java.awt.event.MouseListener;
41 import java.awt.event.MouseMotionListener;
42 import java.awt.event.MouseWheelListener;
43 import java.awt.event.WindowListener;
44 import java.awt.image.BufferedImage;
45 import java.awt.image.BufferStrategy;
46 import javax.swing.JComponent;
47 import javax.swing.JFrame;
48
49 /**
50 * Wrapper for integrating with Swing, because JFrame and JComponent have
51 * separate hierarchies.
52 */
53 class SwingComponent {
54
55 /**
56 * If true, use triple buffering when drawing to a JFrame.
57 */
58 public static boolean tripleBuffer = true;
59
60 /**
61 * Get the BufferStrategy object needed for triple-buffering.
62 *
63 * @return the BufferStrategy
64 * @throws IllegalArgumentException if this function is called when
65 * not rendering to a JFrame
66 */
67 public BufferStrategy getBufferStrategy() {
68 if (frame != null) {
69 return frame.getBufferStrategy();
70 } else {
71 throw new IllegalArgumentException("BufferStrategy not used " +
72 "for JComponent access");
73 }
74 }
75
76 /**
77 * The frame reference, if we are drawing to a JFrame.
78 */
79 private JFrame frame;
80
81 /**
82 * The component reference, if we are drawing to a JComponent.
83 */
84 private JComponent component;
85
86 /**
87 * Get the JFrame reference.
88 *
89 * @return the frame, or null if this is drawing to a JComponent
90 */
91 public JFrame getFrame() {
92 return frame;
93 }
94
95 /**
96 * Get the JComponent reference.
97 *
98 * @return the component, or null if this is drawing to a JFrame
99 */
100 public JComponent getComponent() {
101 return component;
102 }
103
104 /**
105 * Construct using a JFrame.
106 *
107 * @param frame the JFrame to draw to
108 */
109 public SwingComponent(final JFrame frame) {
110 this.frame = frame;
111 setupFrame();
112 }
113
114 /**
115 * Construct using a JComponent.
116 *
117 * @param component the JComponent to draw to
118 */
119 public SwingComponent(final JComponent component) {
120 this.component = component;
121 setupComponent();
122 }
123
124 /**
125 * Setup to render to an existing JComponent.
126 */
127 public void setupComponent() {
128 component.setBackground(Color.black);
129
130 // Kill the X11 cursor
131 // Transparent 16 x 16 pixel cursor image.
132 BufferedImage cursorImg = new BufferedImage(16, 16,
133 BufferedImage.TYPE_INT_ARGB);
134 // Create a new blank cursor.
135 Cursor blankCursor = Toolkit.getDefaultToolkit().createCustomCursor(
136 cursorImg, new Point(0, 0), "blank cursor");
137 component.setCursor(blankCursor);
138
139 // Be capable of seeing Tab / Shift-Tab
140 component.setFocusTraversalKeysEnabled(false);
141 }
142
143 /**
144 * Setup to render to an existing JFrame.
145 */
146 public void setupFrame() {
147 frame.setTitle("Jexer Application");
148 frame.setBackground(Color.black);
149 frame.pack();
150
151 // Kill the X11 cursor
152 // Transparent 16 x 16 pixel cursor image.
153 BufferedImage cursorImg = new BufferedImage(16, 16,
154 BufferedImage.TYPE_INT_ARGB);
155 // Create a new blank cursor.
156 Cursor blankCursor = Toolkit.getDefaultToolkit().createCustomCursor(
157 cursorImg, new Point(0, 0), "blank cursor");
158 frame.setCursor(blankCursor);
159
160 // Be capable of seeing Tab / Shift-Tab
161 frame.setFocusTraversalKeysEnabled(false);
162
163 // Setup triple-buffering
164 if (tripleBuffer) {
165 frame.setIgnoreRepaint(true);
166 frame.createBufferStrategy(3);
167 }
168 }
169
170 /**
171 * Set the window title.
172 *
173 * @param title the new title
174 */
175 public void setTitle(final String title) {
176 if (frame != null) {
177 frame.setTitle(title);
178 }
179 }
180
181 /**
182 * Paints this component.
183 *
184 * @param g the graphics context to use for painting
185 */
186 public void paint(Graphics g) {
187 if (frame != null) {
188 frame.paint(g);
189 } else {
190 component.paint(g);
191 }
192 }
193
194 /**
195 * Repaints this component.
196 */
197 public void repaint() {
198 if (frame != null) {
199 frame.repaint();
200 } else {
201 component.repaint();
202 }
203 }
204
205 /**
206 * Repaints the specified rectangle of this component.
207 *
208 * @param x the x coordinate
209 * @param y the y coordinate
210 * @param width the width
211 * @param height the height
212 */
213 public void repaint(int x, int y, int width, int height) {
214 if (frame != null) {
215 frame.repaint(x, y, width, height);
216 } else {
217 component.repaint(x, y, width, height);
218 }
219 }
220
221 /**
222 * If a border has been set on this component, returns the border's
223 * insets; otherwise calls super.getInsets.
224 *
225 * @return the value of the insets property
226 */
227 public Insets getInsets() {
228 if (frame != null) {
229 return frame.getInsets();
230 } else {
231 return component.getInsets();
232 }
233 }
234
235 /**
236 * Returns the current width of this component.
237 *
238 * @return the current width of this component
239 */
240 public int getWidth() {
241 if (frame != null) {
242 return frame.getWidth();
243 } else {
244 return component.getWidth();
245 }
246 }
247
248 /**
249 * Returns the current height of this component.
250 *
251 * @return the current height of this component
252 */
253 public int getHeight() {
254 if (frame != null) {
255 return frame.getHeight();
256 } else {
257 return component.getHeight();
258 }
259 }
260
261 /**
262 * Gets the font of this component.
263 *
264 * @return this component's font; if a font has not been set for this
265 * component, the font of its parent is returned
266 */
267 public Font getFont() {
268 if (frame != null) {
269 return frame.getFont();
270 } else {
271 return component.getFont();
272 }
273 }
274
275 /**
276 * Sets the font of this component.
277 *
278 * @param f the font to become this component's font; if this parameter
279 * is null then this component will inherit the font of its parent
280 */
281 public void setFont(final Font f) {
282 if (frame != null) {
283 frame.setFont(f);
284 } else {
285 component.setFont(f);
286 }
287 }
288
289 /**
290 * Shows or hides this Window depending on the value of parameter b.
291 *
292 * @param b if true, make visible, else make invisible
293 */
294 public void setVisible(final boolean b) {
295 if (frame != null) {
296 frame.setVisible(b);
297 } else {
298 component.setVisible(b);
299 }
300 }
301
302 /**
303 * Creates a graphics context for this component. This method will return
304 * null if this component is currently not displayable.
305 *
306 * @return a graphics context for this component, or null if it has none
307 */
308 public Graphics getGraphics() {
309 if (frame != null) {
310 return frame.getGraphics();
311 } else {
312 return component.getGraphics();
313 }
314 }
315
316 /**
317 * Releases all of the native screen resources used by this Window, its
318 * subcomponents, and all of its owned children. That is, the resources
319 * for these Components will be destroyed, any memory they consume will
320 * be returned to the OS, and they will be marked as undisplayable.
321 */
322 public void dispose() {
323 if (frame != null) {
324 frame.dispose();
325 } else {
326 component.getParent().remove(component);
327 }
328 }
329
330 /**
331 * Resize the component to match the font dimensions.
332 *
333 * @param width the new width in pixels
334 * @param height the new height in pixels
335 */
336 public void setDimensions(final int width, final int height) {
337 // Figure out the thickness of borders and use that to set the final
338 // size.
339 Insets insets = getInsets();
340
341 if (frame != null) {
342 frame.setSize(width + insets.left + insets.right,
343 height + insets.top + insets.bottom);
344 } else {
345 component.setSize(width + insets.left + insets.right,
346 height + insets.top + insets.bottom);
347 }
348 }
349
350 /**
351 * Adds the specified component listener to receive component events from
352 * this component. If listener l is null, no exception is thrown and no
353 * action is performed.
354 *
355 * @param l the component listener
356 */
357 public void addComponentListener(ComponentListener l) {
358 if (frame != null) {
359 frame.addComponentListener(l);
360 } else {
361 component.addComponentListener(l);
362 }
363 }
364
365 /**
366 * Adds the specified key listener to receive key events from this
367 * component. If l is null, no exception is thrown and no action is
368 * performed.
369 *
370 * @param l the key listener.
371 */
372 public void addKeyListener(KeyListener l) {
373 if (frame != null) {
374 frame.addKeyListener(l);
375 } else {
376 component.addKeyListener(l);
377 }
378 }
379
380 /**
381 * Adds the specified mouse listener to receive mouse events from this
382 * component. If listener l is null, no exception is thrown and no action
383 * is performed.
384 *
385 * @param l the mouse listener
386 */
387 public void addMouseListener(MouseListener l) {
388 if (frame != null) {
389 frame.addMouseListener(l);
390 } else {
391 component.addMouseListener(l);
392 }
393 }
394
395 /**
396 * Adds the specified mouse motion listener to receive mouse motion
397 * events from this component. If listener l is null, no exception is
398 * thrown and no action is performed.
399 *
400 * @param l the mouse motion listener
401 */
402 public void addMouseMotionListener(MouseMotionListener l) {
403 if (frame != null) {
404 frame.addMouseMotionListener(l);
405 } else {
406 component.addMouseMotionListener(l);
407 }
408 }
409
410 /**
411 * Adds the specified mouse wheel listener to receive mouse wheel events
412 * from this component. Containers also receive mouse wheel events from
413 * sub-components.
414 *
415 * @param l the mouse wheel listener
416 */
417 public void addMouseWheelListener(MouseWheelListener l) {
418 if (frame != null) {
419 frame.addMouseWheelListener(l);
420 } else {
421 component.addMouseWheelListener(l);
422 }
423 }
424
425 /**
426 * Adds the specified window listener to receive window events from this
427 * window. If l is null, no exception is thrown and no action is
428 * performed.
429 *
430 * @param l the window listener
431 */
432 public void addWindowListener(WindowListener l) {
433 if (frame != null) {
434 frame.addWindowListener(l);
435 }
436 }
437
438 }