1 package charactermanaj.ui;
3 import java.awt.BorderLayout;
5 import java.awt.Component;
6 import java.awt.Container;
7 import java.awt.Dimension;
8 import java.awt.GridBagConstraints;
9 import java.awt.GridBagLayout;
10 import java.awt.Insets;
11 import java.awt.Toolkit;
12 import java.awt.event.ActionEvent;
13 import java.awt.event.ActionListener;
14 import java.awt.event.KeyEvent;
15 import java.awt.event.MouseEvent;
16 import java.awt.event.WindowAdapter;
17 import java.awt.event.WindowEvent;
19 import java.util.ArrayList;
20 import java.util.Collections;
21 import java.util.Comparator;
22 import java.util.EnumSet;
23 import java.util.HashMap;
24 import java.util.List;
26 import java.util.Properties;
28 import java.util.logging.Level;
29 import java.util.logging.Logger;
31 import javax.swing.AbstractAction;
32 import javax.swing.AbstractCellEditor;
33 import javax.swing.Action;
34 import javax.swing.ActionMap;
35 import javax.swing.BorderFactory;
36 import javax.swing.Box;
37 import javax.swing.InputMap;
38 import javax.swing.JButton;
39 import javax.swing.JCheckBox;
40 import javax.swing.JColorChooser;
41 import javax.swing.JComponent;
42 import javax.swing.JDialog;
43 import javax.swing.JFrame;
44 import javax.swing.JLabel;
45 import javax.swing.JOptionPane;
46 import javax.swing.JPanel;
47 import javax.swing.JRootPane;
48 import javax.swing.JScrollPane;
49 import javax.swing.JTable;
50 import javax.swing.KeyStroke;
51 import javax.swing.SwingConstants;
52 import javax.swing.event.TableModelEvent;
53 import javax.swing.event.TableModelListener;
54 import javax.swing.table.AbstractTableModel;
55 import javax.swing.table.DefaultTableCellRenderer;
56 import javax.swing.table.TableCellEditor;
57 import javax.swing.table.TableCellRenderer;
58 import javax.swing.table.TableColumnModel;
60 import charactermanaj.Main;
61 import charactermanaj.model.AppConfig;
62 import charactermanaj.util.BeanPropertiesUtilities;
63 import charactermanaj.util.BeanPropertiesUtilities.PropertyAccessor;
64 import charactermanaj.util.BeanPropertiesUtilities.PropertyAccessorMap;
65 import charactermanaj.util.ConfigurationDirUtilities;
66 import charactermanaj.util.DesktopUtilities;
67 import charactermanaj.util.ErrorMessageHelper;
68 import charactermanaj.util.LocalizedResourcePropertyLoader;
69 import charactermanaj.util.SetupLocalization;
77 public class AppConfigDialog extends JDialog {
79 private static final long serialVersionUID = 1L;
81 private static final Logger logger = Logger.getLogger(AppConfigDialog.class.getName());
83 private AppConfigTableModel appConfigTableModel;
85 private JTable appConfigTable;
87 private JCheckBox chkResetDoNotAskAgain;
89 private RecentCharactersDir recentCharactersDir;
91 private AbstractAction actApply;
93 private boolean orgDoNotAskAgain;
95 public enum ColumnDef {
96 NAME("column.key", String.class, false) {
98 public Object getValue(AppConfigRow row) {
99 return row.getDisplayName();
102 VALUE("column.value", String.class, true) {
104 public Object getValue(AppConfigRow row) {
105 return row.getValue();
108 public void setValue(AppConfigRow row, Object value) {
113 private final String reskey;
115 private final Class<?> type;
117 private final boolean editable;
119 ColumnDef(String reskey, Class<?> type, boolean editable) {
120 this.reskey = reskey;
122 this.editable = editable;
125 public boolean isEditable() {
129 public String getResourceKey() {
133 public Class<?> getType() {
137 public abstract Object getValue(AppConfigRow row);
139 public void setValue(AppConfigRow row, Object value) {
140 throw new UnsupportedOperationException(name());
144 private static class AppConfigRow {
146 private final String name;
148 private final PropertyAccessor accessor;
150 private String order = "";
152 private String displayName;
154 private Object orgValue;
156 private Object value;
158 private boolean rejected;
160 public AppConfigRow(String name, PropertyAccessor accessor, Object value) {
162 this.accessor = accessor;
164 this.orgValue = value;
167 public String getName() {
171 public Class<?> getPropertyType() {
172 Class<?> dataType = accessor.getPropertyType();
173 // JTableのセルレンダラーではプリミティブ型の編集は対応していないので
175 if (dataType.isPrimitive()) {
176 if (dataType.equals(int.class)) {
177 dataType = Integer.class;
178 } else if (dataType.equals(long.class)) {
179 dataType = Long.class;
180 } else if (dataType.equals(float.class)) {
181 dataType = Float.class;
182 } else if (dataType.equals(double.class)) {
183 dataType = Double.class;
184 } else if (dataType.equals(boolean.class)) {
185 dataType = Boolean.class;
191 public String getOrder() {
195 public void setOrder(String order) {
202 public String getDisplayName() {
203 return (displayName == null || displayName.length() == 0) ? name : displayName;
206 public void setDisplayName(String displayName) {
207 this.displayName = displayName;
210 public Object getValue() {
214 public void setValue(Object value) {
218 public boolean isRejected() {
222 public void setRejected(boolean rejected) {
223 this.rejected = rejected;
226 public boolean isModified() {
227 return orgValue == null ? value != null : !orgValue.equals(value);
231 private static class AppConfigTableModel extends AbstractTableModel {
233 private static final long serialVersionUID = 1L;
235 protected static final ColumnDef[] COLUMNS = ColumnDef.values();
237 private List<AppConfigRow> items = Collections.emptyList();
239 public List<AppConfigRow> getItems() {
243 public void setItems(List<AppConfigRow> items) {
245 items = Collections.emptyList();
248 fireTableDataChanged();
251 public void setRejectNames(Set<String> rejectNames) {
252 if (rejectNames == null) {
253 rejectNames = Collections.emptySet();
255 for (AppConfigRow item : items) {
256 String key = item.getName();
257 boolean rejected = rejectNames.contains(key);
258 item.setRejected(rejected);
260 fireTableDataChanged();
266 * @return 編集されていればtrue、そうでなければfalse
268 public boolean isModified() {
274 public int getRowCount() {
278 public int getColumnCount() {
279 return COLUMNS.length;
283 public Class<?> getColumnClass(int columnIndex) {
284 return COLUMNS[columnIndex].getType();
288 public String getColumnName(int column) {
289 Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
290 .getLocalizedProperties("languages/appconfigdialog");
291 String reskey = COLUMNS[column].getResourceKey();
292 return strings.getProperty(reskey, reskey);
296 public boolean isCellEditable(int rowIndex, int columnIndex) {
297 return COLUMNS[columnIndex].isEditable();
300 public Object getValueAt(int rowIndex, int columnIndex) {
301 AppConfigRow row = items.get(rowIndex);
302 return COLUMNS[columnIndex].getValue(row);
306 public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
307 AppConfigRow row = items.get(rowIndex);
308 COLUMNS[columnIndex].setValue(row, aValue);
309 fireTableRowsUpdated(rowIndex, rowIndex);
312 public void adjustColumnModel(TableColumnModel columnModel) {
313 Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
314 .getLocalizedProperties("languages/appconfigdialog");
315 int mx = columnModel.getColumnCount();
316 for (int idx = 0; idx < mx; idx++) {
317 String reskey = COLUMNS[idx].getResourceKey() + ".width";
318 int width = Integer.parseInt(strings.getProperty(reskey));
319 columnModel.getColumn(idx).setPreferredWidth(width);
324 public AppConfigDialog(JFrame parent) {
327 setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
328 addWindowListener(new WindowAdapter() {
330 public void windowClosing(WindowEvent e) {
339 } catch (RuntimeException ex) {
340 logger.log(Level.SEVERE, "appConfig construct failed.", ex);
346 private void initComponent() {
348 Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
349 .getLocalizedProperties("languages/appconfigdialog");
351 setTitle(strings.getProperty("title"));
353 Container contentPane = getContentPane();
354 contentPane.setLayout(new BorderLayout());
357 JPanel btnPanel = new JPanel();
358 btnPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 45));
359 GridBagLayout btnPanelLayout = new GridBagLayout();
360 btnPanel.setLayout(btnPanelLayout);
362 GridBagConstraints gbc = new GridBagConstraints();
364 actApply = new AbstractAction(strings.getProperty("btn.apply")) {
365 private static final long serialVersionUID = 1L;
366 public void actionPerformed(ActionEvent e) {
370 Action actCancel = new AbstractAction(strings.getProperty("btn.cancel")) {
371 private static final long serialVersionUID = 1L;
372 public void actionPerformed(ActionEvent e) {
376 Action actLocalization = new AbstractAction(strings.getProperty("btn.setupLocalization")) {
377 private static final long serialVersionUID = 1L;
378 public void actionPerformed(ActionEvent e) {
379 onSetupLocalization();
383 chkResetDoNotAskAgain = new JCheckBox(strings.getProperty("chk.askForCharactersDir"));
384 chkResetDoNotAskAgain.addActionListener(new ActionListener() {
386 public void actionPerformed(ActionEvent e) {
396 gbc.anchor = GridBagConstraints.WEST;
397 gbc.fill = GridBagConstraints.NONE;
398 gbc.insets = new Insets(0, 0, 0, 0);
403 btnPanel.add(chkResetDoNotAskAgain, gbc);
409 gbc.anchor = GridBagConstraints.WEST;
410 gbc.fill = GridBagConstraints.NONE;
411 gbc.insets = new Insets(3, 3, 3, 3);
416 btnPanel.add(new JButton(actLocalization), gbc);
422 gbc.fill = GridBagConstraints.BOTH;
425 btnPanel.add(Box.createHorizontalGlue(), gbc);
427 gbc.gridx = Main.isLinuxOrMacOSX() ? 2 : 1;
429 JButton btnApply = new JButton(actApply);
430 btnPanel.add(btnApply, gbc);
432 gbc.gridx = Main.isLinuxOrMacOSX() ? 1 : 2;
434 JButton btnCancel = new JButton(actCancel);
435 btnPanel.add(btnCancel, gbc);
437 add(btnPanel, BorderLayout.SOUTH);
440 setLocationRelativeTo(getParent());
443 JLabel lblCaution = new JLabel(strings.getProperty("caution"), JLabel.CENTER);
444 lblCaution.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
445 lblCaution.setForeground(Color.red);
446 contentPane.add(lblCaution, BorderLayout.NORTH);
449 appConfigTableModel = new AppConfigTableModel();
452 AppConfig appConfig = AppConfig.getInstance();
453 final Color invalidBgColor = appConfig.getInvalidBgColor();
455 appConfigTable = new JTable(appConfigTableModel) {
456 private static final long serialVersionUID = 1L;
459 public Component prepareRenderer(TableCellRenderer renderer,
460 int row, int column) {
461 Component comp = super.prepareRenderer(renderer, row, column);
462 AppConfigRow configRow = appConfigTableModel.getItems().get(row);
463 if (configRow.isRejected()) {
465 comp.setBackground(invalidBgColor);
468 if (isCellSelected(row, column)) {
469 comp.setBackground(getSelectionBackground());
471 comp.setBackground(getBackground());
475 if (configRow.isModified() && !configRow.isRejected()) {
477 // (ただし、rejectのものは背景色を変えているので何もしない)
478 comp.setForeground(invalidBgColor);
481 comp.setForeground(getForeground());
487 public String getToolTipText(MouseEvent event) {
488 int row = rowAtPoint(event.getPoint());
489 int col = columnAtPoint(event.getPoint());
490 if (AppConfigTableModel.COLUMNS[col] == ColumnDef.NAME) {
491 // 最初の列の表示をツールチップとして表示させる
492 int modelRow = convertRowIndexToModel(row);
493 return appConfigTableModel.getItems().get(modelRow).getDisplayName();
495 return super.getToolTipText(event);
498 // 1つの列で複数のデータタイプの編集を可能にする
499 // Jtable with different types of cells depending on data type
500 // https://stackoverflow.com/questions/16970824/jtable-with-different-types-of-cells-depending-on-data-type
502 private Class<?> editingClass;
505 public TableCellRenderer getCellRenderer(int row, int column) {
507 int modelColumn = convertColumnIndexToModel(column);
508 if (AppConfigTableModel.COLUMNS[modelColumn] == ColumnDef.VALUE) {
510 int modelRow = convertRowIndexToModel(row);
511 AppConfigRow rowData = appConfigTableModel.getItems().get(modelRow);
513 // 行のデータ型に対応するレンダラーを取得する
514 Class<?> dataType = rowData.getPropertyType();
515 TableCellRenderer renderer = getDefaultRenderer(dataType);
516 if (renderer != null) {
520 // VALUE列以外は、標準のまま (もしくはレンダラーがみつからない場合)
521 return super.getCellRenderer(row, column);
525 public TableCellEditor getCellEditor(int row, int column) {
527 int modelColumn = convertColumnIndexToModel(column);
528 if (AppConfigTableModel.COLUMNS[modelColumn] == ColumnDef.VALUE) {
530 int modelRow = convertRowIndexToModel(row);
531 AppConfigRow rowData = appConfigTableModel.getItems().get(modelRow);
533 // 行のデータ型に対応するレンダラーを取得する
534 editingClass = rowData.getPropertyType();
535 return getDefaultEditor(editingClass);
539 return super.getCellEditor(row, column);
543 // This method is also invoked by the editor when the value in the editor
544 // component is saved in the TableModel. The class was saved when the
545 // editor was invoked so the proper class can be created.
548 public Class<?> getColumnClass(int column) {
549 return editingClass != null ? editingClass : super.getColumnClass(column);
553 appConfigTable.setShowGrid(true);
554 appConfigTable.setGridColor(appConfig.getGridColor());
555 appConfigTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
556 appConfigTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
558 // データタイプがColorの場合のセルレンダラーとエディタを設定する
559 appConfigTable.setDefaultRenderer(Color.class, new ColorCellRender());
560 appConfigTable.setDefaultEditor(Color.class, new ColorCellEditor());
562 appConfigTableModel.adjustColumnModel(appConfigTable.getColumnModel());
564 appConfigTableModel.addTableModelListener(new TableModelListener() {
566 public void tableChanged(TableModelEvent e) {
567 // テーブルが変更された場合、保存ボタンの活性やReject状態の変更のため
572 JScrollPane appConfigTableSP = new JScrollPane(appConfigTable);
573 appConfigTableSP.setBorder(BorderFactory.createCompoundBorder(
574 BorderFactory.createEmptyBorder(0, 3, 0, 3),
575 BorderFactory.createTitledBorder(strings.getProperty("table.caption")))
577 appConfigTableSP.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
578 contentPane.add(appConfigTableSP, BorderLayout.CENTER);
581 Toolkit tk = Toolkit.getDefaultToolkit();
582 JRootPane rootPane = getRootPane();
583 InputMap im = rootPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
584 ActionMap am = rootPane.getActionMap();
585 im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "closeAppConfigDialog");
586 im.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, tk.getMenuShortcutKeyMask()), "closeAppConfigDialog");
587 am.put("closeAppConfigDialog", actCancel);
594 * 保存ボタンの活性制御とテーブルの編集状態の表示の更新
595 * (テーブルが編集された場合に更新される)
597 protected void updateUIState() {
598 boolean hasModified = false;
601 for (AppConfigRow itemRow : appConfigTableModel.getItems()) {
602 if (itemRow.isModified()) {
605 } else if (itemRow.isRejected()) {
606 // 変更されていない状態であれば、差し戻し状態を解除する
607 itemRow.setRejected(false);
611 // キャラクターデータディレクトリの問い合わせ状態が変わっているか?
612 if (orgDoNotAskAgain != chkResetDoNotAskAgain.isSelected()) {
616 // 保存先が無効であれば適用ボタンを有効にしない.
617 AppConfig appConfig = AppConfig.getInstance();
618 boolean enableSave = !appConfig.getPrioritySaveFileList().isEmpty();
620 // 保存が有効であり、且つ、変更された行があれば保存ボタンを有効とする
621 actApply.setEnabled(enableSave && hasModified);
624 private void loadData() {
626 Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
627 .getLocalizedProperties("languages/appconfigdialog");
629 // AppConfigへのアクセッサを取得する
630 PropertyAccessorMap accessorMap = BeanPropertiesUtilities.getPropertyAccessorMap(AppConfig.class);
632 AppConfig appConfig = AppConfig.getInstance();
633 accessorMap.setBean(appConfig);
635 List<AppConfigRow> items = new ArrayList<AppConfigRow>();
636 int fallbackOrder = 1000;
637 for (Map.Entry<String, PropertyAccessor> accessorEntry : accessorMap.entrySet()) {
639 String name = accessorEntry.getKey();
640 PropertyAccessor accessor = accessorEntry.getValue();
641 Object value = accessor.getValue();
643 // リソースからプロパティ名に対応する表示名を取得する(なければプロパティ名のまま)
644 String displayName = strings.getProperty(name, name);
645 int pt = displayName.indexOf(";");
646 String order = Integer.toString(fallbackOrder++);
648 order = displayName.substring(0, pt);
649 displayName = displayName.substring(pt + 1);
653 AppConfigRow rowItem = new AppConfigRow(name, accessor, value);
654 rowItem.setDisplayName(displayName);
655 rowItem.setOrder(order);
661 Collections.sort(items, new Comparator<AppConfigRow>() {
663 public int compare(AppConfigRow o1, AppConfigRow o2) {
664 int ret = o1.getOrder().compareTo(o2.getOrder());
666 ret = o1.getDisplayName().compareTo(o2.getDisplayName());
669 ret = o1.getName().compareTo(o2.getName());
675 appConfigTableModel.setItems(items);
677 // 最後に使ったキャラクターデータディレクトリの自動選択設定
679 recentCharactersDir = RecentCharactersDir.load();
681 if (recentCharactersDir != null) {
682 File lastUseCharactersDir = recentCharactersDir.getLastUseCharacterDir();
683 boolean enableLastUseCharacterDir = lastUseCharactersDir != null && lastUseCharactersDir.isDirectory();
684 boolean doNotAskAgain = enableLastUseCharacterDir && recentCharactersDir.isDoNotAskAgain();
685 chkResetDoNotAskAgain.setEnabled(enableLastUseCharacterDir);
686 chkResetDoNotAskAgain.setSelected(!doNotAskAgain);
689 } catch (Exception ex) {
690 recentCharactersDir = null;
691 logger.log(Level.WARNING, "RecentCharactersDir load failed.", ex);
695 this.orgDoNotAskAgain = chkResetDoNotAskAgain.isSelected();
699 * ローカライズリソースをユーザディレクトリ上に展開する.
701 protected void onSetupLocalization() {
702 Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
703 .getLocalizedProperties("languages/appconfigdialog");
704 if (JOptionPane.showConfirmDialog(this,
705 strings.getProperty("setupLocalization"),
706 strings.getProperty("confirm.setupLocalization.caption"),
707 JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) != JOptionPane.OK_OPTION) {
712 File baseDir = ConfigurationDirUtilities.getUserDataDir();
713 SetupLocalization setup = new SetupLocalization(baseDir);
715 EnumSet.allOf(SetupLocalization.Resources.class), true);
717 File resourceDir = setup.getResourceDir();
718 DesktopUtilities.open(resourceDir);
720 } catch (Exception ex) {
721 ErrorMessageHelper.showErrorDialog(this, ex);
725 protected void onClose() {
726 if (appConfigTableModel.isModified()) {
727 Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
728 .getLocalizedProperties("languages/appconfigdialog");
729 if (JOptionPane.showConfirmDialog(this, strings.getProperty("confirm.close"),
730 strings.getProperty("confirm.close.caption"), JOptionPane.YES_NO_OPTION,
731 JOptionPane.QUESTION_MESSAGE) != JOptionPane.YES_OPTION) {
739 * AppConfigと、キャラクターデータディレクトリの起動時の選択有無の設定値を保存する。
741 protected void onUpdate() {
743 if (appConfigTable.isEditing()) {
745 Toolkit tk = Toolkit.getDefaultToolkit();
750 Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
751 .getLocalizedProperties("languages/appconfigdialog");
753 // 編集されたAppConfigの設定値を取得する. (変更のあるもののみ)
754 Map<String, Object> modifiedValues = new HashMap<String, Object>();
755 for (AppConfigRow rowItem : appConfigTableModel.getItems()) {
756 if (rowItem.isModified()) {
757 String name = rowItem.getName();
758 Object value = rowItem.getValue();
759 modifiedValues.put(name, value);
763 // キャラクターデータディレクトリの起動時の選択状態の変更状態
764 boolean updateRecentCharactersDir = (orgDoNotAskAgain != chkResetDoNotAskAgain.isSelected());
766 if (!updateRecentCharactersDir && modifiedValues.isEmpty()) {
772 if (!modifiedValues.isEmpty()) {
773 // 編集されたプロパティが適用可能か検証する.
774 Set<String> rejectNames = AppConfig.checkProperties(modifiedValues);
775 if (!rejectNames.isEmpty()) {
777 appConfigTableModel.setRejectNames(rejectNames);
779 JOptionPane.showMessageDialog(this, strings.getProperty("error.message"),
780 strings.getProperty("error.caption"), JOptionPane.ERROR_MESSAGE);
785 // アプリケーション設定を更新し、保存する.
786 AppConfig appConfig = AppConfig.getInstance();
787 appConfig.update(modifiedValues);
788 appConfig.saveConfig();
790 } catch (Exception ex) {
791 ErrorMessageHelper.showErrorDialog(this, ex);
796 // キャラクターデータディレクトリの起動時の選択の保存
797 if (updateRecentCharactersDir) {
799 if (chkResetDoNotAskAgain.isEnabled()) {
800 boolean doNotAskAgain = !chkResetDoNotAskAgain.isSelected();
801 if (doNotAskAgain != recentCharactersDir.isDoNotAskAgain()) {
802 recentCharactersDir.setDoNotAskAgain(doNotAskAgain);
803 recentCharactersDir.saveRecents();
806 } catch (Exception ex) {
807 ErrorMessageHelper.showErrorDialog(this, ex);
812 // アプリケーションの再起動が必要なことを示すダイアログを表示する.
813 String message = strings.getProperty("caution");
814 JOptionPane.showMessageDialog(this, message);
823 class ColorCell extends JPanel {
824 private static final long serialVersionUID = 1L;
826 private String title = "Color";
830 private JLabel label;
832 private JButton button;
834 private ActionListener actionListener;
841 * ボタンのアクションリスナを指定して構築する
842 * @param actionListener
844 public ColorCell(ActionListener actionListener) {
845 super(new BorderLayout());
846 this.actionListener = actionListener;
848 box = new JPanel(new BorderLayout());
850 label = new JLabel();
851 label.setHorizontalAlignment(SwingConstants.CENTER);
852 box.add(label, BorderLayout.CENTER);
853 box.setBorder(BorderFactory.createEtchedBorder());
855 button = new JButton();
856 Dimension dim = button.getPreferredSize();
858 button.setPreferredSize(dim);
859 button.addActionListener(new ActionListener() {
861 public void actionPerformed(ActionEvent e) {
866 add(box, BorderLayout.CENTER);
867 add(button, BorderLayout.EAST);
868 setSelectedColor(Color.BLACK);
871 public String getTitle() {
875 public void setTitle(String title) {
876 String old = this.title;
877 if (old == null ? title != null : !old.equals(title)) {
879 firePropertyChange("title", old, title);
883 protected void onClick(ActionEvent e) {
884 // ※ カラー選択ダイアログは、Java7以降でないとアルファ値の設定はできない。
885 // Java6で実行するとアルファチャネルが消されたものになる。
886 // (設定ファイルとしては手作業では設定可能なので、とりあえず、このまま。)
887 Color selColor = JColorChooser.showDialog(ColorCell.this, title, selectedColor);
888 if (selColor != null) {
889 setSelectedColor(selColor);
890 if (actionListener != null) {
891 actionListener.actionPerformed(e);
896 private Color selectedColor;
898 public Color getSelectedColor() {
899 return selectedColor;
902 public void setSelectedColor(Color color) {
906 Color old = this.selectedColor;
907 if (old == null ? color != null : !old.equals(color)) {
908 this.selectedColor = color;
910 Color colorForeground = new Color(color.getRGB() ^ 0xffffff).brighter();
912 int alpha = color.getAlpha();
914 // JPanelの背景色としてアルファの透過色をそのまま使うと
915 // 親コンポーネントの背景色と混じり、色のカタログとして用をなさないので
916 // 白を背景色とした合成済みに補正しておく
917 // (アルファが255の場合はそのままで良い)
918 Color premultipliedColor;
920 premultipliedColor = color;
922 float[] rgb = color.getRGBColorComponents(null);
923 float[] bgRgb = Color.WHITE.getRGBColorComponents(null); // 背景色 = 白色
925 float a = ((float) alpha) / 255f;
926 rgb[0] = rgb[0] * a + bgRgb[0] * (1 - a);
927 rgb[1] = rgb[1] * a + bgRgb[1] * (1 - a);
928 rgb[2] = rgb[2] * a + bgRgb[2] * (1 - a);
929 premultipliedColor = new Color(rgb[0], rgb[1], rgb[2]);
932 box.setBackground(premultipliedColor);
933 label.setForeground(colorForeground);
937 // アルファが255以外の場合はアルファ値も含めてARGBで表示する
938 msg = String.format("#%08X", ((long) color.getRGB()) & 0xffffffffL);
940 // アルファが255の場合はRGBのみ表示する。
941 msg = String.format("#%06X", ((long) color.getRGB()) & 0xffffffL);
945 firePropertyChange("selectedColor", old, color);
953 class ColorCellRender extends DefaultTableCellRenderer {
955 private static final long serialVersionUID = 1L;
957 private ColorCell panel = new ColorCell();
960 public Component getTableCellRendererComponent(JTable table, Object value,
961 boolean isSelected, boolean hasFocus, int row, int column) {
962 panel.setSelectedColor((Color) value);
968 * カラーセルを編集モードにした場合のエディタ
970 class ColorCellEditor extends AbstractCellEditor implements TableCellEditor {
972 private static final long serialVersionUID = 1L;
974 private ColorCell colorCell = new ColorCell(new ActionListener() {
976 public void actionPerformed(ActionEvent e) {
977 fireEditingStopped();
981 public Component getTableCellEditorComponent(final JTable table, final Object value,
982 final boolean isSelected, final int row, final int column) {
983 colorCell.setSelectedColor((Color) value);
987 public Object getCellEditorValue() {
988 return colorCell.getSelectedColor();