fix bad import, AGAIN.
[nikiroo-utils.git] / ui / UIUtils.java
1 package be.nikiroo.utils.ui;
2
3 import java.awt.Color;
4 import java.awt.Component;
5 import java.awt.Desktop;
6 import java.awt.GradientPaint;
7 import java.awt.Graphics;
8 import java.awt.Graphics2D;
9 import java.awt.GraphicsConfiguration;
10 import java.awt.GraphicsDevice;
11 import java.awt.GraphicsEnvironment;
12 import java.awt.Paint;
13 import java.awt.Point;
14 import java.awt.RadialGradientPaint;
15 import java.awt.Rectangle;
16 import java.awt.RenderingHints;
17 import java.awt.Window;
18 import java.io.IOException;
19 import java.net.URISyntaxException;
20
21 import javax.swing.JButton;
22 import javax.swing.JComponent;
23 import javax.swing.JEditorPane;
24 import javax.swing.JLabel;
25 import javax.swing.JOptionPane;
26 import javax.swing.JScrollPane;
27 import javax.swing.UIManager;
28 import javax.swing.UnsupportedLookAndFeelException;
29 import javax.swing.event.HyperlinkEvent;
30 import javax.swing.event.HyperlinkListener;
31
32 import be.nikiroo.utils.Version;
33 import be.nikiroo.utils.VersionCheck;
34
35 /**
36 * Some Java Swing utilities.
37 *
38 * @author niki
39 */
40 public class UIUtils {
41 static private Color buttonNormal;
42 static private Color buttonPressed;
43
44 /**
45 * Set a fake "native Look & Feel" for the application if possible
46 * (check for the one currently in use, then try GTK).
47 * <p>
48 * <b>Must</b> be called prior to any GUI work.
49 *
50 * @return TRUE if it succeeded
51 */
52 static public boolean setLookAndFeel() {
53 // native look & feel
54 String noLF = "javax.swing.plaf.metal.MetalLookAndFeel";
55 String lf = UIManager.getSystemLookAndFeelClassName();
56 if (lf.equals(noLF))
57 lf = "com.sun.java.swing.plaf.gtk.GTKLookAndFeel";
58
59 return setLookAndFeel(lf);
60 }
61
62 /**
63 * Switch to the given Look &amp; Feel for the application if possible
64 * (check for the one currently in use, then try GTK).
65 * <p>
66 * <b>Must</b> be called prior to any GUI work.
67 *
68 * @param laf
69 * the Look &amp; Feel to use
70 *
71 * @return TRUE if it succeeded
72 */
73 static public boolean setLookAndFeel(String laf) {
74 try {
75 UIManager.setLookAndFeel(laf);
76 return true;
77 } catch (InstantiationException e) {
78 } catch (ClassNotFoundException e) {
79 } catch (UnsupportedLookAndFeelException e) {
80 } catch (IllegalAccessException e) {
81 }
82
83 return false;
84 }
85
86 /**
87 * Draw a 3D-looking ellipse at the given location, if the given
88 * {@link Graphics} object is compatible (with {@link Graphics2D}); draw a
89 * simple ellipse if not.
90 *
91 * @param g
92 * the {@link Graphics} to draw on
93 * @param color
94 * the base colour
95 * @param x
96 * the X coordinate
97 * @param y
98 * the Y coordinate
99 * @param width
100 * the width radius
101 * @param height
102 * the height radius
103 */
104 static public void drawEllipse3D(Graphics g, Color color, int x, int y,
105 int width, int height) {
106 drawEllipse3D(g, color, x, y, width, height, true);
107 }
108
109 /**
110 * Draw a 3D-looking ellipse at the given location, if the given
111 * {@link Graphics} object is compatible (with {@link Graphics2D}); draw a
112 * simple ellipse if not.
113 *
114 * @param g
115 * the {@link Graphics} to draw on
116 * @param color
117 * the base colour
118 * @param x
119 * the X coordinate of the upper left corner
120 * @param y
121 * the Y coordinate of the upper left corner
122 * @param width
123 * the width radius
124 * @param height
125 * the height radius
126 * @param fill
127 * fill the content of the ellipse
128 */
129 static public void drawEllipse3D(Graphics g, Color color, int x, int y,
130 int width, int height, boolean fill) {
131 if (g instanceof Graphics2D) {
132 Graphics2D g2 = (Graphics2D) g;
133 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
134 RenderingHints.VALUE_ANTIALIAS_ON);
135
136 // Retains the previous state
137 Paint oldPaint = g2.getPaint();
138
139 // Base shape
140 g2.setColor(color);
141 if (fill) {
142 g2.fillOval(x, y, width, height);
143 } else {
144 g2.drawOval(x, y, width, height);
145 }
146
147 // Compute dark/bright colours
148 Paint p = null;
149 Color dark = color.darker().darker();
150 Color bright = color.brighter().brighter();
151 Color darkEnd = new Color(dark.getRed(), dark.getGreen(),
152 dark.getBlue(), 0);
153 Color darkPartial = new Color(dark.getRed(), dark.getGreen(),
154 dark.getBlue(), 64);
155 Color brightEnd = new Color(bright.getRed(), bright.getGreen(),
156 bright.getBlue(), 0);
157
158 // Adds shadows at the bottom left
159 p = new GradientPaint(0, height, dark, width, 0, darkEnd);
160 g2.setPaint(p);
161 if (fill) {
162 g2.fillOval(x, y, width, height);
163 } else {
164 g2.drawOval(x, y, width, height);
165 }
166 // Adds highlights at the top right
167 p = new GradientPaint(width, 0, bright, 0, height, brightEnd);
168 g2.setPaint(p);
169 if (fill) {
170 g2.fillOval(x, y, width, height);
171 } else {
172 g2.drawOval(x, y, width, height);
173 }
174
175 // Darken the edges
176 p = new RadialGradientPaint(x + width / 2f, y + height / 2f,
177 Math.min(width / 2f, height / 2f), new float[] { 0f, 1f },
178 new Color[] { darkEnd, darkPartial },
179 RadialGradientPaint.CycleMethod.NO_CYCLE);
180 g2.setPaint(p);
181 if (fill) {
182 g2.fillOval(x, y, width, height);
183 } else {
184 g2.drawOval(x, y, width, height);
185 }
186
187 // Adds inner highlight at the top right
188 p = new RadialGradientPaint(x + 3f * width / 4f, y + height / 4f,
189 Math.min(width / 4f, height / 4f),
190 new float[] { 0.0f, 0.8f },
191 new Color[] { bright, brightEnd },
192 RadialGradientPaint.CycleMethod.NO_CYCLE);
193 g2.setPaint(p);
194 if (fill) {
195 g2.fillOval(x * 2, y, width, height);
196 } else {
197 g2.drawOval(x * 2, y, width, height);
198 }
199
200 // Reset original paint
201 g2.setPaint(oldPaint);
202 } else {
203 g.setColor(color);
204 if (fill) {
205 g.fillOval(x, y, width, height);
206 } else {
207 g.drawOval(x, y, width, height);
208 }
209 }
210 }
211
212 /**
213 * Add a {@link JScrollPane} around the given panel and use a sensible (for
214 * me) increment for the mouse wheel.
215 *
216 * @param pane
217 * the panel to wrap in a {@link JScrollPane}
218 * @param allowHorizontal
219 * allow horizontal scrolling (not always desired)
220 *
221 * @return the {@link JScrollPane}
222 */
223 static public JScrollPane scroll(JComponent pane, boolean allowHorizontal) {
224 return scroll(pane, allowHorizontal, true);
225 }
226
227 /**
228 * Add a {@link JScrollPane} around the given panel and use a sensible (for
229 * me) increment for the mouse wheel.
230 *
231 * @param pane
232 * the panel to wrap in a {@link JScrollPane}
233 * @param allowHorizontal
234 * allow horizontal scrolling (not always desired)
235 * @param allowVertical
236 * allow vertical scrolling (usually yes, but sometimes you only
237 * want horizontal)
238 *
239 * @return the {@link JScrollPane}
240 */
241 static public JScrollPane scroll(JComponent pane, boolean allowHorizontal,
242 boolean allowVertical) {
243 JScrollPane scroll = new JScrollPane(pane);
244
245 scroll.getVerticalScrollBar().setUnitIncrement(16);
246 scroll.getHorizontalScrollBar().setUnitIncrement(16);
247
248 if (!allowHorizontal) {
249 scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
250 }
251 if (!allowVertical) {
252 scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
253 }
254
255 return scroll;
256 }
257
258 /**
259 * Show a confirmation message to the user to show him the changes since
260 * last version.
261 * <p>
262 * HTML 3.2 supported, links included (the user browser will be launched if
263 * possible).
264 * <p>
265 * If this is already the latest version, a message will still be displayed.
266 *
267 * @param parentComponent
268 * determines the {@link java.awt.Frame} in which the dialog is
269 * displayed; if <code>null</code>, or if the
270 * <code>parentComponent</code> has no {@link java.awt.Frame}, a
271 * default {@link java.awt.Frame} is used
272 * @param updates
273 * the new version
274 * @param introText
275 * an introduction text before the list of changes
276 * @param title
277 * the title of the dialog
278 *
279 * @return TRUE if the user clicked on OK, false if the dialog was dismissed
280 */
281 static public boolean showUpdatedDialog(Component parentComponent,
282 VersionCheck updates, String introText, String title) {
283
284 StringBuilder builder = new StringBuilder();
285 final JEditorPane updateMessage = new JEditorPane("text/html", "");
286 if (introText != null && !introText.isEmpty()) {
287 builder.append(introText);
288 builder.append("<br>");
289 builder.append("<br>");
290 }
291 for (Version v : updates.getNewer()) {
292 builder.append("\t<b>" //
293 + "Version " + v.toString() //
294 + "</b>");
295 builder.append("<br>");
296 builder.append("<ul>");
297 for (String item : updates.getChanges().get(v)) {
298 builder.append("<li>" + item + "</li>");
299 }
300 builder.append("</ul>");
301 }
302
303 // html content
304 updateMessage.setText("<html><body>" //
305 + builder//
306 + "</body></html>");
307
308 // handle link events
309 updateMessage.addHyperlinkListener(new HyperlinkListener() {
310 @Override
311 public void hyperlinkUpdate(HyperlinkEvent e) {
312 if (e.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED))
313 try {
314 Desktop.getDesktop().browse(e.getURL().toURI());
315 } catch (IOException ee) {
316 ee.printStackTrace();
317 } catch (URISyntaxException ee) {
318 ee.printStackTrace();
319 }
320 }
321 });
322 updateMessage.setEditable(false);
323 updateMessage.setBackground(new JLabel().getBackground());
324 updateMessage.addHyperlinkListener(new HyperlinkListener() {
325 @Override
326 public void hyperlinkUpdate(HyperlinkEvent evn) {
327 if (evn.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
328 if (Desktop.isDesktopSupported()) {
329 try {
330 Desktop.getDesktop().browse(evn.getURL().toURI());
331 } catch (IOException e) {
332 } catch (URISyntaxException e) {
333 }
334 }
335 }
336 }
337 });
338
339 return JOptionPane.showConfirmDialog(parentComponent, updateMessage,
340 title, JOptionPane.DEFAULT_OPTION) == JOptionPane.OK_OPTION;
341 }
342
343 /**
344 * Set the given {@link JButton} as "pressed" (selected, but with more UI
345 * visibility).
346 * <p>
347 * The {@link JButton} will answer {@link JButton#isSelected()} if it is
348 * pressed.
349 *
350 * @param button
351 * the button to select/press
352 * @param pressed
353 * the new "pressed" state
354 */
355 static public void setButtonPressed(JButton button, boolean pressed) {
356 if (buttonNormal == null) {
357 JButton defButton = new JButton(" ");
358 buttonNormal = defButton.getBackground();
359 if (buttonNormal.getBlue() >= 128) {
360 buttonPressed = new Color( //
361 Math.max(buttonNormal.getRed() - 100, 0), //
362 Math.max(buttonNormal.getGreen() - 100, 0), //
363 Math.max(buttonNormal.getBlue() - 100, 0));
364 } else {
365 buttonPressed = new Color( //
366 Math.min(buttonNormal.getRed() + 100, 255), //
367 Math.min(buttonNormal.getGreen() + 100, 255), //
368 Math.min(buttonNormal.getBlue() + 100, 255));
369 }
370 }
371
372 button.setSelected(pressed);
373 button.setBackground(pressed ? buttonPressed : buttonNormal);
374 }
375
376 /**
377 * Set the given {@link Window} to full screen mode, on the desktop it
378 * currently resides on.
379 * <p>
380 * Can be cancelled by calling again with a NULL value.
381 *
382 * @param win
383 * the window to set to full screen
384 */
385 static public void setFullscreenWindow(Window win) {
386 GraphicsEnvironment env = GraphicsEnvironment
387 .getLocalGraphicsEnvironment();
388 GraphicsDevice[] screens = env.getScreenDevices();
389
390 if (win == null) {
391 for (GraphicsDevice screen : screens) {
392 if (win == null) {
393 screen.setFullScreenWindow(null);
394 }
395 }
396
397 return;
398 }
399
400 Rectangle r = win.getBounds();
401 Point center = new Point(r.x + r.width / 2, r.y + r.height / 2);
402
403 GraphicsDevice current = null;
404 for (GraphicsDevice screen : screens) {
405 GraphicsConfiguration[] confs = screen.getConfigurations();
406 for (GraphicsConfiguration conf : confs) {
407 if (conf.getBounds().contains(center)) {
408 current = screen;
409 break;
410 }
411 }
412
413 if (current != null)
414 break;
415 }
416
417 if (current == null) {
418 current = GraphicsEnvironment.getLocalGraphicsEnvironment()
419 .getDefaultScreenDevice();
420 }
421
422 current.setFullScreenWindow(win);
423 }
424 }