Prep for 2019 release
[fanfix.git] / src / jexer / backend / SwingComponent.java
CommitLineData
42873e30
KL
1/*
2 * Jexer - Java Text User Interface
3 *
4 * The MIT License (MIT)
5 *
a69ed767 6 * Copyright (C) 2019 Kevin Lamonte
42873e30
KL
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 */
29package jexer.backend;
30
31import java.awt.Color;
32import java.awt.Cursor;
33import java.awt.Font;
34import java.awt.Graphics;
35import java.awt.Insets;
36import java.awt.Point;
37import java.awt.Toolkit;
38import java.awt.event.ComponentListener;
39import java.awt.event.KeyListener;
40import java.awt.event.MouseListener;
41import java.awt.event.MouseMotionListener;
42import java.awt.event.MouseWheelListener;
43import java.awt.event.WindowListener;
44import java.awt.image.BufferedImage;
45import java.awt.image.BufferStrategy;
46import javax.swing.JComponent;
47import javax.swing.JFrame;
48
49/**
50 * Wrapper for integrating with Swing, because JFrame and JComponent have
51 * separate hierarchies.
52 */
53class SwingComponent {
54
d36057df
KL
55 // ------------------------------------------------------------------------
56 // Variables --------------------------------------------------------------
57 // ------------------------------------------------------------------------
58
42873e30
KL
59 /**
60 * If true, use triple buffering when drawing to a JFrame.
61 */
88a99379 62 public static boolean tripleBuffer = true;
42873e30 63
d36057df
KL
64 /**
65 * The frame reference, if we are drawing to a JFrame.
66 */
67 private JFrame frame;
68
69 /**
70 * The component reference, if we are drawing to a JComponent.
71 */
72 private JComponent component;
73
051e2913
KL
74 /**
75 * An optional border in pixels to add.
76 */
a69ed767 77 private static final int BORDER = 1;
051e2913
KL
78
79 /**
80 * Adjustable Insets for this component. This has the effect of adding a
81 * black border around the drawing area.
82 */
a69ed767 83 Insets adjustInsets = new Insets(BORDER + 5, BORDER, BORDER, BORDER);
051e2913 84
d36057df
KL
85 // ------------------------------------------------------------------------
86 // Constructors -----------------------------------------------------------
87 // ------------------------------------------------------------------------
88
89 /**
90 * Construct using a JFrame.
91 *
92 * @param frame the JFrame to draw to
93 */
94 public SwingComponent(final JFrame frame) {
95 this.frame = frame;
96 setupFrame();
97 }
98
99 /**
100 * Construct using a JComponent.
101 *
102 * @param component the JComponent to draw to
103 */
104 public SwingComponent(final JComponent component) {
105 this.component = component;
106 setupComponent();
107 }
108
109 // ------------------------------------------------------------------------
110 // SwingComponent ---------------------------------------------------------
111 // ------------------------------------------------------------------------
112
42873e30
KL
113 /**
114 * Get the BufferStrategy object needed for triple-buffering.
115 *
116 * @return the BufferStrategy
117 * @throws IllegalArgumentException if this function is called when
118 * not rendering to a JFrame
119 */
120 public BufferStrategy getBufferStrategy() {
121 if (frame != null) {
122 return frame.getBufferStrategy();
123 } else {
124 throw new IllegalArgumentException("BufferStrategy not used " +
125 "for JComponent access");
126 }
127 }
128
42873e30
KL
129 /**
130 * Get the JFrame reference.
131 *
132 * @return the frame, or null if this is drawing to a JComponent
133 */
134 public JFrame getFrame() {
135 return frame;
136 }
137
138 /**
139 * Get the JComponent reference.
140 *
141 * @return the component, or null if this is drawing to a JFrame
142 */
143 public JComponent getComponent() {
144 return component;
145 }
146
42873e30
KL
147 /**
148 * Setup to render to an existing JComponent.
149 */
150 public void setupComponent() {
151 component.setBackground(Color.black);
152
153 // Kill the X11 cursor
154 // Transparent 16 x 16 pixel cursor image.
155 BufferedImage cursorImg = new BufferedImage(16, 16,
156 BufferedImage.TYPE_INT_ARGB);
157 // Create a new blank cursor.
158 Cursor blankCursor = Toolkit.getDefaultToolkit().createCustomCursor(
159 cursorImg, new Point(0, 0), "blank cursor");
160 component.setCursor(blankCursor);
161
162 // Be capable of seeing Tab / Shift-Tab
163 component.setFocusTraversalKeysEnabled(false);
164 }
165
166 /**
167 * Setup to render to an existing JFrame.
168 */
169 public void setupFrame() {
170 frame.setTitle("Jexer Application");
171 frame.setBackground(Color.black);
172 frame.pack();
173
174 // Kill the X11 cursor
175 // Transparent 16 x 16 pixel cursor image.
176 BufferedImage cursorImg = new BufferedImage(16, 16,
177 BufferedImage.TYPE_INT_ARGB);
178 // Create a new blank cursor.
179 Cursor blankCursor = Toolkit.getDefaultToolkit().createCustomCursor(
180 cursorImg, new Point(0, 0), "blank cursor");
181 frame.setCursor(blankCursor);
182
183 // Be capable of seeing Tab / Shift-Tab
184 frame.setFocusTraversalKeysEnabled(false);
185
186 // Setup triple-buffering
187 if (tripleBuffer) {
188 frame.setIgnoreRepaint(true);
189 frame.createBufferStrategy(3);
190 }
191 }
192
193 /**
194 * Set the window title.
195 *
196 * @param title the new title
197 */
198 public void setTitle(final String title) {
199 if (frame != null) {
200 frame.setTitle(title);
201 }
202 }
203
204 /**
205 * Paints this component.
206 *
207 * @param g the graphics context to use for painting
208 */
209 public void paint(Graphics g) {
210 if (frame != null) {
211 frame.paint(g);
212 } else {
213 component.paint(g);
214 }
215 }
216
217 /**
218 * Repaints this component.
219 */
220 public void repaint() {
221 if (frame != null) {
222 frame.repaint();
223 } else {
224 component.repaint();
225 }
226 }
227
228 /**
229 * Repaints the specified rectangle of this component.
230 *
231 * @param x the x coordinate
232 * @param y the y coordinate
233 * @param width the width
234 * @param height the height
235 */
236 public void repaint(int x, int y, int width, int height) {
237 if (frame != null) {
238 frame.repaint(x, y, width, height);
239 } else {
240 component.repaint(x, y, width, height);
241 }
242 }
243
244 /**
245 * If a border has been set on this component, returns the border's
246 * insets; otherwise calls super.getInsets.
247 *
248 * @return the value of the insets property
249 */
250 public Insets getInsets() {
051e2913 251 Insets swingInsets = null;
42873e30 252 if (frame != null) {
051e2913 253 swingInsets = frame.getInsets();
42873e30 254 } else {
051e2913 255 swingInsets = component.getInsets();
42873e30 256 }
051e2913
KL
257 Insets result = new Insets(swingInsets.top + adjustInsets.top,
258 swingInsets.left + adjustInsets.left,
259 swingInsets.bottom + adjustInsets.bottom,
260 swingInsets.right + adjustInsets.right);
261 return result;
42873e30
KL
262 }
263
264 /**
265 * Returns the current width of this component.
266 *
267 * @return the current width of this component
268 */
269 public int getWidth() {
270 if (frame != null) {
271 return frame.getWidth();
272 } else {
273 return component.getWidth();
274 }
275 }
276
277 /**
278 * Returns the current height of this component.
279 *
280 * @return the current height of this component
281 */
282 public int getHeight() {
283 if (frame != null) {
284 return frame.getHeight();
285 } else {
286 return component.getHeight();
287 }
288 }
289
290 /**
291 * Gets the font of this component.
292 *
293 * @return this component's font; if a font has not been set for this
294 * component, the font of its parent is returned
295 */
296 public Font getFont() {
297 if (frame != null) {
298 return frame.getFont();
299 } else {
300 return component.getFont();
301 }
302 }
303
304 /**
305 * Sets the font of this component.
306 *
307 * @param f the font to become this component's font; if this parameter
308 * is null then this component will inherit the font of its parent
309 */
310 public void setFont(final Font f) {
311 if (frame != null) {
312 frame.setFont(f);
313 } else {
314 component.setFont(f);
315 }
316 }
317
318 /**
319 * Shows or hides this Window depending on the value of parameter b.
320 *
321 * @param b if true, make visible, else make invisible
322 */
323 public void setVisible(final boolean b) {
324 if (frame != null) {
325 frame.setVisible(b);
326 } else {
327 component.setVisible(b);
328 }
329 }
330
331 /**
332 * Creates a graphics context for this component. This method will return
333 * null if this component is currently not displayable.
334 *
335 * @return a graphics context for this component, or null if it has none
336 */
337 public Graphics getGraphics() {
338 if (frame != null) {
339 return frame.getGraphics();
340 } else {
341 return component.getGraphics();
342 }
343 }
344
345 /**
346 * Releases all of the native screen resources used by this Window, its
347 * subcomponents, and all of its owned children. That is, the resources
348 * for these Components will be destroyed, any memory they consume will
349 * be returned to the OS, and they will be marked as undisplayable.
350 */
351 public void dispose() {
352 if (frame != null) {
353 frame.dispose();
354 } else {
355 component.getParent().remove(component);
356 }
357 }
358
359 /**
360 * Resize the component to match the font dimensions.
361 *
362 * @param width the new width in pixels
363 * @param height the new height in pixels
364 */
365 public void setDimensions(final int width, final int height) {
366 // Figure out the thickness of borders and use that to set the final
367 // size.
42873e30 368 if (frame != null) {
a69ed767 369 Insets insets = getInsets();
42873e30
KL
370 frame.setSize(width + insets.left + insets.right,
371 height + insets.top + insets.bottom);
372 } else {
a69ed767 373 Insets insets = getInsets();
42873e30
KL
374 component.setSize(width + insets.left + insets.right,
375 height + insets.top + insets.bottom);
376 }
377 }
378
379 /**
380 * Adds the specified component listener to receive component events from
381 * this component. If listener l is null, no exception is thrown and no
382 * action is performed.
383 *
384 * @param l the component listener
385 */
386 public void addComponentListener(ComponentListener l) {
387 if (frame != null) {
388 frame.addComponentListener(l);
389 } else {
390 component.addComponentListener(l);
391 }
392 }
393
394 /**
395 * Adds the specified key listener to receive key events from this
396 * component. If l is null, no exception is thrown and no action is
397 * performed.
398 *
399 * @param l the key listener.
400 */
401 public void addKeyListener(KeyListener l) {
402 if (frame != null) {
403 frame.addKeyListener(l);
404 } else {
405 component.addKeyListener(l);
406 }
407 }
408
409 /**
410 * Adds the specified mouse listener to receive mouse events from this
411 * component. If listener l is null, no exception is thrown and no action
412 * is performed.
413 *
414 * @param l the mouse listener
415 */
416 public void addMouseListener(MouseListener l) {
417 if (frame != null) {
418 frame.addMouseListener(l);
419 } else {
420 component.addMouseListener(l);
421 }
422 }
423
424 /**
425 * Adds the specified mouse motion listener to receive mouse motion
426 * events from this component. If listener l is null, no exception is
427 * thrown and no action is performed.
428 *
429 * @param l the mouse motion listener
430 */
431 public void addMouseMotionListener(MouseMotionListener l) {
432 if (frame != null) {
433 frame.addMouseMotionListener(l);
434 } else {
435 component.addMouseMotionListener(l);
436 }
437 }
438
439 /**
440 * Adds the specified mouse wheel listener to receive mouse wheel events
441 * from this component. Containers also receive mouse wheel events from
442 * sub-components.
443 *
444 * @param l the mouse wheel listener
445 */
446 public void addMouseWheelListener(MouseWheelListener l) {
447 if (frame != null) {
448 frame.addMouseWheelListener(l);
449 } else {
450 component.addMouseWheelListener(l);
451 }
452 }
453
454 /**
455 * Adds the specified window listener to receive window events from this
456 * window. If l is null, no exception is thrown and no action is
457 * performed.
458 *
459 * @param l the window listener
460 */
461 public void addWindowListener(WindowListener l) {
462 if (frame != null) {
463 frame.addWindowListener(l);
464 }
465 }
466
467}