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