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 | ||
1fdc7577 NR |
25 | import be.nikiroo.utils.Version; |
26 | import be.nikiroo.utils.VersionCheck; | |
8caeb8bd NR |
27 | |
28 | /** | |
29 | * Some Java Swing utilities. | |
30 | * | |
31 | * @author niki | |
32 | */ | |
33 | public 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 & 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 & 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 | } |