1 package be
.nikiroo
.utils
.ui
;
3 import java
.awt
.BorderLayout
;
5 import java
.awt
.Cursor
;
6 import java
.awt
.Dimension
;
7 import java
.awt
.Graphics2D
;
8 import java
.awt
.event
.ActionEvent
;
9 import java
.awt
.event
.ActionListener
;
10 import java
.awt
.event
.MouseAdapter
;
11 import java
.awt
.event
.MouseEvent
;
12 import java
.awt
.image
.BufferedImage
;
14 import java
.io
.IOException
;
16 import javax
.swing
.Icon
;
17 import javax
.swing
.ImageIcon
;
18 import javax
.swing
.InputVerifier
;
19 import javax
.swing
.JButton
;
20 import javax
.swing
.JCheckBox
;
21 import javax
.swing
.JColorChooser
;
22 import javax
.swing
.JComboBox
;
23 import javax
.swing
.JComponent
;
24 import javax
.swing
.JFileChooser
;
25 import javax
.swing
.JLabel
;
26 import javax
.swing
.JOptionPane
;
27 import javax
.swing
.JPanel
;
28 import javax
.swing
.JPasswordField
;
29 import javax
.swing
.JTextField
;
30 import javax
.swing
.plaf
.basic
.BasicArrowButton
;
32 import be
.nikiroo
.utils
.Image
;
33 import be
.nikiroo
.utils
.StringUtils
;
34 import be
.nikiroo
.utils
.StringUtils
.Alignment
;
35 import be
.nikiroo
.utils
.resources
.Bundle
;
36 import be
.nikiroo
.utils
.resources
.Meta
.Format
;
37 import be
.nikiroo
.utils
.resources
.MetaInfo
;
40 * A graphical item that reflect a configuration option from the given
43 * This graphical item can be edited, and the result will be saved back into the
44 * linked {@link MetaInfo}; you still have to save the {@link MetaInfo} should
45 * you wish to, of course.
50 * the type of {@link Bundle} to edit
52 public class ConfigItem
<E
extends Enum
<E
>> extends JPanel
{
53 private static final long serialVersionUID
= 1L;
55 /** A small (?) blue in PNG, base64 encoded. */
56 private static String infoImage64
= //
58 + "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAACXBI"
59 + "WXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4wURFRg6IrtcdgAAATdJREFUOMvtkj8sQ1EUxr9z/71G"
60 + "m1RDogYxq7WDDYMYTSajSG4n6YRYzSaSLibWbiaDIGwdiLIYDFKDNJEgKu969xi8UNHy7H7LPcN3"
61 + "v/Odcy+hG9oOIeIcBCJS9MAvlZtOMtHxsrFrJHGqe0RVGnHAHpcIbPlng8BS3HmKBJYzabGUzcrJ"
62 + "XK+ckIrqANYR2JEv2nYDEVck0WKGfHzyq82Go+btxoX3XAcAIqTj8wPqOH6mtMeM4bGCLhyfhTMA"
63 + "qlLhKHqujCfaweCAmV0p50dPzsNpEKpK01V/n55HIvTnfDC2odKlfeYadZN/T+AqDACUsnkhqaU1"
64 + "LRIVuX1x7ciuSWQxVIrunONrfq3dI6oh+T94Z8453vEem/HTqT8ZpFJ0qDXtGkPbAGAMeSRngQCA"
65 + "eUvgn195AwlZWyvjtQdhAAAAAElFTkSuQmCC";
68 * Create a new {@link ConfigItem} for the given {@link MetaInfo}.
71 * the {@link MetaInfo}
73 public ConfigItem(MetaInfo
<E
> info
) {
74 this.setLayout(new BorderLayout());
76 // TODO: support arrays
77 Format fmt
= info
.getFormat();
84 addBooleanField(info
);
90 addBrowseField(info
, false);
93 addBrowseField(info
, true);
96 addComboboxField(info
, true);
99 addComboboxField(info
, false);
105 addPasswordField(info
);
108 case LOCALE
: // TODO?
110 addStringField(info
);
115 private void addStringField(final MetaInfo
<E
> info
) {
116 final JTextField field
= new JTextField();
117 field
.setToolTipText(info
.getDescription());
118 field
.setText(info
.getString());
120 info
.addReloadedListener(new Runnable() {
123 field
.setText(info
.getString());
126 info
.addSaveListener(new Runnable() {
129 info
.setString(field
.getText());
133 this.add(label(info
), BorderLayout
.WEST
);
134 this.add(field
, BorderLayout
.CENTER
);
137 private void addBooleanField(final MetaInfo
<E
> info
) {
138 final JCheckBox field
= new JCheckBox();
139 field
.setToolTipText(info
.getDescription());
140 Boolean state
= info
.getBoolean();
142 info
.getDefaultBoolean();
145 // Should not happen!
148 .println("No default value given for BOOLEAN parameter \""
149 + info
.getName() + "\", we consider it is FALSE");
153 field
.setSelected(state
);
155 info
.addReloadedListener(new Runnable() {
158 Boolean state
= info
.getBoolean();
160 info
.getDefaultBoolean();
166 field
.setSelected(state
);
169 info
.addSaveListener(new Runnable() {
172 info
.setBoolean(field
.isSelected());
176 this.add(label(info
), BorderLayout
.WEST
);
177 this.add(field
, BorderLayout
.CENTER
);
180 private void addColorField(final MetaInfo
<E
> info
) {
181 final JTextField field
= new JTextField();
182 field
.setToolTipText(info
.getDescription());
183 field
.setText(info
.getString());
185 info
.addReloadedListener(new Runnable() {
188 field
.setText(info
.getString());
191 info
.addSaveListener(new Runnable() {
194 info
.setString(field
.getText());
198 this.add(label(info
), BorderLayout
.WEST
);
199 JPanel pane
= new JPanel(new BorderLayout());
201 final JButton colorWheel
= new JButton();
202 colorWheel
.setIcon(getIcon(17, info
.getColor()));
203 colorWheel
.addActionListener(new ActionListener() {
205 public void actionPerformed(ActionEvent e
) {
206 Color initialColor
= new Color(info
.getColor(), true);
207 Color newColor
= JColorChooser
.showDialog(ConfigItem
.this,
208 info
.getName(), initialColor
);
209 if (newColor
!= null) {
210 info
.setColor(newColor
.getRGB());
211 field
.setText(info
.getString());
212 colorWheel
.setIcon(getIcon(17, info
.getColor()));
216 pane
.add(colorWheel
, BorderLayout
.WEST
);
217 pane
.add(field
, BorderLayout
.CENTER
);
218 this.add(pane
, BorderLayout
.CENTER
);
221 private void addBrowseField(final MetaInfo
<E
> info
, final boolean dir
) {
222 final JTextField field
= new JTextField();
223 field
.setToolTipText(info
.getDescription());
224 field
.setText(info
.getString());
226 info
.addReloadedListener(new Runnable() {
229 field
.setText(info
.getString());
232 info
.addSaveListener(new Runnable() {
235 info
.setString(field
.getText());
239 JButton browseButton
= new JButton("...");
240 browseButton
.addActionListener(new ActionListener() {
242 public void actionPerformed(ActionEvent e
) {
243 JFileChooser chooser
= new JFileChooser();
244 chooser
.setCurrentDirectory(null);
245 chooser
.setFileSelectionMode(dir ? JFileChooser
.DIRECTORIES_ONLY
246 : JFileChooser
.FILES_ONLY
);
247 if (chooser
.showOpenDialog(ConfigItem
.this) == JFileChooser
.APPROVE_OPTION
) {
248 File file
= chooser
.getSelectedFile();
250 info
.setString(file
.getAbsolutePath());
251 field
.setText(info
.getString());
257 JPanel pane
= new JPanel(new BorderLayout());
258 this.add(label(info
), BorderLayout
.WEST
);
259 pane
.add(browseButton
, BorderLayout
.WEST
);
260 pane
.add(field
, BorderLayout
.CENTER
);
261 this.add(pane
, BorderLayout
.CENTER
);
264 private void addComboboxField(final MetaInfo
<E
> info
, boolean editable
) {
265 // rawtypes for Java 1.6 (and 1.7 ?) support
266 @SuppressWarnings({ "rawtypes", "unchecked" })
267 final JComboBox field
= new JComboBox(info
.getAllowedValues());
268 field
.setEditable(editable
);
269 field
.setSelectedItem(info
.getString());
271 info
.addReloadedListener(new Runnable() {
274 field
.setSelectedItem(info
.getString());
277 info
.addSaveListener(new Runnable() {
280 info
.setString(field
.getSelectedItem().toString());
284 this.add(label(info
), BorderLayout
.WEST
);
285 this.add(field
, BorderLayout
.CENTER
);
288 private void addPasswordField(final MetaInfo
<E
> info
) {
289 final JPasswordField field
= new JPasswordField();
290 field
.setToolTipText(info
.getDescription());
291 field
.setText(info
.getString());
293 info
.addReloadedListener(new Runnable() {
296 field
.setText(info
.getString());
299 info
.addSaveListener(new Runnable() {
302 info
.setString(new String(field
.getPassword()));
306 this.add(label(info
), BorderLayout
.WEST
);
307 this.add(field
, BorderLayout
.CENTER
);
310 private void addIntField(final MetaInfo
<E
> info
) {
311 final JTextField field
= new JTextField();
312 field
.setToolTipText(info
.getDescription());
313 field
.setText(info
.getString());
314 field
.setInputVerifier(new InputVerifier() {
316 public boolean verify(JComponent input
) {
317 String text
= field
.getText().trim();
318 if (text
.startsWith("-")) {
319 text
= text
.substring(1).trim();
322 return text
.replaceAll("[0-9]", "").isEmpty();
326 info
.addReloadedListener(new Runnable() {
329 field
.setText(info
.getString());
332 info
.addSaveListener(new Runnable() {
335 info
.setString(field
.getText());
336 Integer value
= info
.getInteger();
340 info
.setInteger(value
);
342 field
.setText(info
.getString());
346 JButton up
= new BasicArrowButton(BasicArrowButton
.NORTH
);
347 JButton down
= new BasicArrowButton(BasicArrowButton
.SOUTH
);
349 up
.addActionListener(new ActionListener() {
351 public void actionPerformed(ActionEvent ae
) {
354 value
= Integer
.parseInt(field
.getText());
355 } catch (NumberFormatException e
) {
358 field
.setText(Integer
.toString(value
+ 1));
362 down
.addActionListener(new ActionListener() {
364 public void actionPerformed(ActionEvent ae
) {
367 value
= Integer
.parseInt(field
.getText());
368 } catch (NumberFormatException e
) {
371 field
.setText(Integer
.toString(value
- 1));
375 JPanel upDown
= new JPanel(new BorderLayout());
376 upDown
.add(up
, BorderLayout
.NORTH
);
377 upDown
.add(down
, BorderLayout
.SOUTH
);
379 JPanel pane
= new JPanel(new BorderLayout());
380 pane
.add(upDown
, BorderLayout
.WEST
);
381 pane
.add(field
, BorderLayout
.CENTER
);
383 this.add(label(info
), BorderLayout
.WEST
);
384 this.add(pane
, BorderLayout
.CENTER
);
388 * Create a label which width is constrained in lock steps.
391 * the {@link MetaInfo} for which we want to add a label
395 private JComponent
label(final MetaInfo
<E
> info
) {
396 final JLabel label
= new JLabel(info
.getName());
398 Dimension ps
= label
.getPreferredSize();
400 ps
= label
.getSize();
405 for (int i
= 2 * step
; i
< 10 * step
; i
+= step
) {
412 final Runnable showInfo
= new Runnable() {
415 StringBuilder builder
= new StringBuilder();
416 String text
= info
.getDescription().replace("\\n", "\n");
417 for (String line
: StringUtils
.justifyText(text
, 80,
419 if (builder
.length() > 0) {
420 builder
.append("\n");
422 builder
.append(line
);
424 text
= builder
.toString();
425 JOptionPane
.showMessageDialog(ConfigItem
.this, text
,
426 info
.getName(), JOptionPane
.INFORMATION_MESSAGE
);
430 JLabel help
= new JLabel("");
431 help
.setCursor(Cursor
.getPredefinedCursor(Cursor
.HAND_CURSOR
));
433 Image img
= new Image(infoImage64
);
435 BufferedImage bImg
= ImageUtilsAwt
.fromImage(img
);
436 help
.setIcon(new ImageIcon(bImg
));
440 } catch (IOException e
) {
441 // This is an hard-coded image, should not happen
445 help
.addMouseListener(new MouseAdapter() {
447 public void mouseClicked(MouseEvent e
) {
452 JPanel pane2
= new JPanel(new BorderLayout());
453 pane2
.add(help
, BorderLayout
.WEST
);
454 pane2
.add(new JLabel(" "), BorderLayout
.CENTER
);
456 JPanel pane
= new JPanel(new BorderLayout());
457 pane
.add(label
, BorderLayout
.WEST
);
458 pane
.add(pane2
, BorderLayout
.CENTER
);
460 ps
.width
= w
+ 30; // 30 for the (?) sign
462 pane
.setPreferredSize(ps
);
468 * Return an {@link Icon} to use as a colour badge for the colour field
472 * the size of the badge
474 * the colour of the badge
478 private Icon
getIcon(int size
, int color
) {
479 Color c
= new Color(color
, true);
480 int avg
= (c
.getRed() + c
.getGreen() + c
.getBlue()) / 3;
481 Color border
= (avg
>= 128 ? Color
.BLACK
: Color
.WHITE
);
483 BufferedImage img
= new BufferedImage(size
, size
,
484 BufferedImage
.TYPE_4BYTE_ABGR
);
486 Graphics2D g
= img
.createGraphics();
489 g
.fillRect(0, 0, img
.getWidth(), img
.getHeight());
491 g
.drawRect(0, 0, img
.getWidth() - 1, img
.getHeight() - 1);
496 return new ImageIcon(img
);