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