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