import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
+import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
+import java.util.ArrayList;
import java.util.Collections;
+import java.util.Comparator;
import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.AbstractAction;
+import javax.swing.AbstractCellEditor;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.BorderFactory;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JCheckBox;
+import javax.swing.JColorChooser;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.KeyStroke;
+import javax.swing.SwingConstants;
+import javax.swing.event.TableModelEvent;
+import javax.swing.event.TableModelListener;
+import javax.swing.table.AbstractTableModel;
+import javax.swing.table.DefaultTableCellRenderer;
+import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumnModel;
import charactermanaj.Main;
import charactermanaj.model.AppConfig;
-import charactermanaj.ui.model.AbstractTableModelWithComboBoxModel;
+import charactermanaj.util.BeanPropertiesUtilities;
+import charactermanaj.util.BeanPropertiesUtilities.PropertyAccessor;
+import charactermanaj.util.BeanPropertiesUtilities.PropertyAccessorMap;
import charactermanaj.util.ConfigurationDirUtilities;
import charactermanaj.util.DesktopUtilities;
import charactermanaj.util.ErrorMessageHelper;
/**
* アプリケーション設定ダイアログ
- *
+ *
* @author seraphy
*/
public class AppConfigDialog extends JDialog {
private static final long serialVersionUID = 1L;
-
+
private static final Logger logger = Logger.getLogger(AppConfigDialog.class.getName());
private AppConfigTableModel appConfigTableModel;
-
+
private JTable appConfigTable;
-
+
private JCheckBox chkResetDoNotAskAgain;
-
+
private RecentCharactersDir recentCharactersDir;
-
+
+ private AbstractAction actApply;
+
+ private boolean orgDoNotAskAgain;
+
+ public enum ColumnDef {
+ NAME("column.key", String.class, false) {
+ @Override
+ public Object getValue(AppConfigRow row) {
+ return row.getDisplayName();
+ }
+ },
+ VALUE("column.value", String.class, true) {
+ @Override
+ public Object getValue(AppConfigRow row) {
+ return row.getValue();
+ }
+ @Override
+ public void setValue(AppConfigRow row, Object value) {
+ row.setValue(value);
+ }
+ };
+
+ private final String reskey;
+
+ private final Class<?> type;
+
+ private final boolean editable;
+
+ ColumnDef(String reskey, Class<?> type, boolean editable) {
+ this.reskey = reskey;
+ this.type = type;
+ this.editable = editable;
+ }
+
+ public boolean isEditable() {
+ return editable;
+ }
+
+ public String getResourceKey() {
+ return reskey;
+ }
+
+ public Class<?> getType() {
+ return type;
+ }
+
+ public abstract Object getValue(AppConfigRow row);
+
+ public void setValue(AppConfigRow row, Object value) {
+ throw new UnsupportedOperationException(name());
+ }
+ }
+
+ private static class AppConfigRow {
+
+ private final String name;
+
+ private final PropertyAccessor accessor;
+
+ private String order = "";
+
+ private String displayName;
+
+ private Object orgValue;
+
+ private Object value;
+
+ private boolean rejected;
+
+ public AppConfigRow(String name, PropertyAccessor accessor, Object value) {
+ this.name = name;
+ this.accessor = accessor;
+ this.value = value;
+ this.orgValue = value;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Class<?> getPropertyType() {
+ Class<?> dataType = accessor.getPropertyType();
+ // JTableのセルレンダラーではプリミティブ型の編集は対応していないので
+ // ラッパー型に置き換える
+ if (dataType.isPrimitive()) {
+ if (dataType.equals(int.class)) {
+ dataType = Integer.class;
+ } else if (dataType.equals(long.class)) {
+ dataType = Long.class;
+ } else if (dataType.equals(float.class)) {
+ dataType = Float.class;
+ } else if (dataType.equals(double.class)) {
+ dataType = Double.class;
+ } else if (dataType.equals(boolean.class)) {
+ dataType = Boolean.class;
+ }
+ }
+ return dataType;
+ }
+
+ public String getOrder() {
+ return order;
+ }
+
+ public void setOrder(String order) {
+ if (order == null) {
+ order = "";
+ }
+ this.order = order;
+ }
+
+ public String getDisplayName() {
+ return (displayName == null || displayName.length() == 0) ? name : displayName;
+ }
+
+ public void setDisplayName(String displayName) {
+ this.displayName = displayName;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ public void setValue(Object value) {
+ this.value = value;
+ }
+
+ public boolean isRejected() {
+ return rejected;
+ }
+
+ public void setRejected(boolean rejected) {
+ this.rejected = rejected;
+ }
+
+ public boolean isModified() {
+ return orgValue == null ? value != null : !orgValue.equals(value);
+ }
+ }
+
+ private static class AppConfigTableModel extends AbstractTableModel {
+
+ private static final long serialVersionUID = 1L;
+
+ protected static final ColumnDef[] COLUMNS = ColumnDef.values();
+
+ private List<AppConfigRow> items = Collections.emptyList();
+
+ public List<AppConfigRow> getItems() {
+ return items;
+ }
+
+ public void setItems(List<AppConfigRow> items) {
+ if (items == null) {
+ items = Collections.emptyList();
+ }
+ this.items = items;
+ fireTableDataChanged();
+ }
+
+ public void setRejectNames(Set<String> rejectNames) {
+ if (rejectNames == null) {
+ rejectNames = Collections.emptySet();
+ }
+ for (AppConfigRow item : items) {
+ String key = item.getName();
+ boolean rejected = rejectNames.contains(key);
+ item.setRejected(rejected);
+ }
+ fireTableDataChanged();
+ }
+
+ /**
+ * 編集されているか?
+ *
+ * @return 編集されていればtrue、そうでなければfalse
+ */
+ public boolean isModified() {
+ return false;
+ }
+
+
+ @Override
+ public int getRowCount() {
+ return items.size();
+ }
+
+ public int getColumnCount() {
+ return COLUMNS.length;
+ }
+
+ @Override
+ public Class<?> getColumnClass(int columnIndex) {
+ return COLUMNS[columnIndex].getType();
+ }
+
+ @Override
+ public String getColumnName(int column) {
+ Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
+ .getLocalizedProperties("languages/appconfigdialog");
+ String reskey = COLUMNS[column].getResourceKey();
+ return strings.getProperty(reskey, reskey);
+ }
+
+ @Override
+ public boolean isCellEditable(int rowIndex, int columnIndex) {
+ return COLUMNS[columnIndex].isEditable();
+ }
+
+ public Object getValueAt(int rowIndex, int columnIndex) {
+ AppConfigRow row = items.get(rowIndex);
+ return COLUMNS[columnIndex].getValue(row);
+ }
+
+ @Override
+ public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
+ AppConfigRow row = items.get(rowIndex);
+ COLUMNS[columnIndex].setValue(row, aValue);
+ fireTableRowsUpdated(rowIndex, rowIndex);
+ }
+
+ public void adjustColumnModel(TableColumnModel columnModel) {
+ Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
+ .getLocalizedProperties("languages/appconfigdialog");
+ int mx = columnModel.getColumnCount();
+ for (int idx = 0; idx < mx; idx++) {
+ String reskey = COLUMNS[idx].getResourceKey() + ".width";
+ int width = Integer.parseInt(strings.getProperty(reskey));
+ columnModel.getColumn(idx).setPreferredWidth(width);
+ }
+ }
+ }
+
public AppConfigDialog(JFrame parent) {
super(parent, true);
try {
});
initComponent();
-
+
loadData();
-
+
} catch (RuntimeException ex) {
logger.log(Level.SEVERE, "appConfig construct failed.", ex);
dispose();
throw ex;
}
}
-
+
private void initComponent() {
-
+
Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
.getLocalizedProperties("languages/appconfigdialog");
-
+
setTitle(strings.getProperty("title"));
Container contentPane = getContentPane();
btnPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 45));
GridBagLayout btnPanelLayout = new GridBagLayout();
btnPanel.setLayout(btnPanelLayout);
-
+
GridBagConstraints gbc = new GridBagConstraints();
-
- Action actApply = new AbstractAction(strings.getProperty("btn.apply")) {
+
+ actApply = new AbstractAction(strings.getProperty("btn.apply")) {
private static final long serialVersionUID = 1L;
public void actionPerformed(ActionEvent e) {
onUpdate();
onSetupLocalization();
}
};
-
+
chkResetDoNotAskAgain = new JCheckBox(strings.getProperty("chk.askForCharactersDir"));
+ chkResetDoNotAskAgain.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ // 保存ボタンの状態更新のため
+ updateUIState();
+ }
+ });
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 1.;
gbc.weighty = 0.;
btnPanel.add(chkResetDoNotAskAgain, gbc);
-
+
gbc.gridx = 0;
gbc.gridy = 1;
gbc.gridheight = 1;
gbc.weightx = 1.;
gbc.weighty = 0.;
btnPanel.add(Box.createHorizontalGlue(), gbc);
-
+
gbc.gridx = Main.isLinuxOrMacOSX() ? 2 : 1;
gbc.weightx = 0.;
JButton btnApply = new JButton(actApply);
btnPanel.add(btnApply, gbc);
-
+
gbc.gridx = Main.isLinuxOrMacOSX() ? 1 : 2;
gbc.weightx = 0.;
JButton btnCancel = new JButton(actCancel);
btnPanel.add(btnCancel, gbc);
-
+
add(btnPanel, BorderLayout.SOUTH);
-
- setSize(350, 400);
+
+ setSize(400, 400);
setLocationRelativeTo(getParent());
-
+
// Notes
JLabel lblCaution = new JLabel(strings.getProperty("caution"), JLabel.CENTER);
lblCaution.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
lblCaution.setForeground(Color.red);
contentPane.add(lblCaution, BorderLayout.NORTH);
-
+
// Model
appConfigTableModel = new AppConfigTableModel();
// JTable
AppConfig appConfig = AppConfig.getInstance();
final Color invalidBgColor = appConfig.getInvalidBgColor();
+
appConfigTable = new JTable(appConfigTableModel) {
private static final long serialVersionUID = 1L;
+
@Override
public Component prepareRenderer(TableCellRenderer renderer,
int row, int column) {
Component comp = super.prepareRenderer(renderer, row, column);
- AppConfigRowModel rowModel = appConfigTableModel.getRow(row);
- if (rowModel.isRejected()) {
+ AppConfigRow configRow = appConfigTableModel.getItems().get(row);
+ if (configRow.isRejected()) {
+ // 差し戻された項目は警告色とする
comp.setBackground(invalidBgColor);
} else {
+ // そうでなければ標準色に戻す
if (isCellSelected(row, column)) {
comp.setBackground(getSelectionBackground());
} else {
comp.setBackground(getBackground());
}
}
+
+ if (configRow.isModified() && !configRow.isRejected()) {
+ // 変更行の色を警告色にする
+ // (ただし、rejectのものは背景色を変えているので何もしない)
+ comp.setForeground(invalidBgColor);
+ } else {
+ // そうでなければ標準色に戻す
+ comp.setForeground(getForeground());
+ }
return comp;
}
+
@Override
public String getToolTipText(MouseEvent event) {
int row = rowAtPoint(event.getPoint());
int col = columnAtPoint(event.getPoint());
- if (col == 0) {
- AppConfigRowModel rowModel = appConfigTableModel.getRow(row);
- return rowModel.getDisplayName();
+ if (AppConfigTableModel.COLUMNS[col] == ColumnDef.NAME) {
+ // 最初の列の表示をツールチップとして表示させる
+ int modelRow = convertRowIndexToModel(row);
+ return appConfigTableModel.getItems().get(modelRow).getDisplayName();
}
return super.getToolTipText(event);
}
+
+ // 1つの列で複数のデータタイプの編集を可能にする
+ // Jtable with different types of cells depending on data type
+ // https://stackoverflow.com/questions/16970824/jtable-with-different-types-of-cells-depending-on-data-type
+
+ private Class<?> editingClass;
+
+ @Override
+ public TableCellRenderer getCellRenderer(int row, int column) {
+ editingClass = null;
+ int modelColumn = convertColumnIndexToModel(column);
+ if (AppConfigTableModel.COLUMNS[modelColumn] == ColumnDef.VALUE) {
+ // VALUE列の場合
+ int modelRow = convertRowIndexToModel(row);
+ AppConfigRow rowData = appConfigTableModel.getItems().get(modelRow);
+
+ // 行のデータ型に対応するレンダラーを取得する
+ Class<?> dataType = rowData.getPropertyType();
+ TableCellRenderer renderer = getDefaultRenderer(dataType);
+ if (renderer != null) {
+ return renderer;
+ }
+ }
+ // VALUE列以外は、標準のまま (もしくはレンダラーがみつからない場合)
+ return super.getCellRenderer(row, column);
+ }
+
+ @Override
+ public TableCellEditor getCellEditor(int row, int column) {
+ editingClass = null;
+ int modelColumn = convertColumnIndexToModel(column);
+ if (AppConfigTableModel.COLUMNS[modelColumn] == ColumnDef.VALUE) {
+ // VALUE列の場合
+ int modelRow = convertRowIndexToModel(row);
+ AppConfigRow rowData = appConfigTableModel.getItems().get(modelRow);
+
+ // 行のデータ型に対応するレンダラーを取得する
+ editingClass = rowData.getPropertyType();
+ return getDefaultEditor(editingClass);
+
+ } else {
+ // VALUE列以外は、標準のまま
+ return super.getCellEditor(row, column);
+ }
+ }
+
+ // This method is also invoked by the editor when the value in the editor
+ // component is saved in the TableModel. The class was saved when the
+ // editor was invoked so the proper class can be created.
+
+ @Override
+ public Class<?> getColumnClass(int column) {
+ return editingClass != null ? editingClass : super.getColumnClass(column);
+ }
};
- appConfigTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
+
appConfigTable.setShowGrid(true);
- appConfigTable.setGridColor(AppConfig.getInstance().getGridColor());
+ appConfigTable.setGridColor(appConfig.getGridColor());
appConfigTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
+ appConfigTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
+
+ // データタイプがColorの場合のセルレンダラーとエディタを設定する
+ appConfigTable.setDefaultRenderer(Color.class, new ColorCellRender());
+ appConfigTable.setDefaultEditor(Color.class, new ColorCellEditor());
+
appConfigTableModel.adjustColumnModel(appConfigTable.getColumnModel());
+
+ appConfigTableModel.addTableModelListener(new TableModelListener() {
+ @Override
+ public void tableChanged(TableModelEvent e) {
+ // テーブルが変更された場合、保存ボタンの活性やReject状態の変更のため
+ updateUIState();
+ }
+ });
+
JScrollPane appConfigTableSP = new JScrollPane(appConfigTable);
appConfigTableSP.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createEmptyBorder(0, 3, 0, 3),
);
appConfigTableSP.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
contentPane.add(appConfigTableSP, BorderLayout.CENTER);
-
+
// RootPane
Toolkit tk = Toolkit.getDefaultToolkit();
JRootPane rootPane = getRootPane();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, tk.getMenuShortcutKeyMask()), "closeAppConfigDialog");
am.put("closeAppConfigDialog", actCancel);
+ // 保存ボタンの活性制御
+ updateUIState();
+ }
+
+ /**
+ * 保存ボタンの活性制御とテーブルの編集状態の表示の更新
+ * (テーブルが編集された場合に更新される)
+ */
+ protected void updateUIState() {
+ boolean hasModified = false;
+
+ // テーブルを走査して変更を確認する
+ for (AppConfigRow itemRow : appConfigTableModel.getItems()) {
+ if (itemRow.isModified()) {
+ // 変更あり
+ hasModified = true;
+ } else if (itemRow.isRejected()) {
+ // 変更されていない状態であれば、差し戻し状態を解除する
+ itemRow.setRejected(false);
+ }
+ }
+
+ // キャラクターデータディレクトリの問い合わせ状態が変わっているか?
+ if (orgDoNotAskAgain != chkResetDoNotAskAgain.isSelected()) {
+ hasModified = true;
+ }
+
// 保存先が無効であれば適用ボタンを有効にしない.
+ AppConfig appConfig = AppConfig.getInstance();
boolean enableSave = !appConfig.getPrioritySaveFileList().isEmpty();
- btnApply.setEnabled(enableSave);
+
+ // 保存が有効であり、且つ、変更された行があれば保存ボタンを有効とする
+ actApply.setEnabled(enableSave && hasModified);
}
-
+
private void loadData() {
- Properties original = AppConfig.getInstance().getProperties();
- appConfigTableModel.initModel(original);
+ Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
+ .getLocalizedProperties("languages/appconfigdialog");
+
+ // AppConfigへのアクセッサを取得する
+ PropertyAccessorMap accessorMap = BeanPropertiesUtilities.getPropertyAccessorMap(AppConfig.class);
+
+ AppConfig appConfig = AppConfig.getInstance();
+ accessorMap.setBean(appConfig);
+
+ List<AppConfigRow> items = new ArrayList<AppConfigRow>();
+ int fallbackOrder = 1000;
+ for (Map.Entry<String, PropertyAccessor> accessorEntry : accessorMap.entrySet()) {
+ // プロパティ名と現在値を取得する
+ String name = accessorEntry.getKey();
+ PropertyAccessor accessor = accessorEntry.getValue();
+ Object value = accessor.getValue();
+
+ // リソースからプロパティ名に対応する表示名を取得する(なければプロパティ名のまま)
+ String displayName = strings.getProperty(name, name);
+ int pt = displayName.indexOf(";");
+ String order = Integer.toString(fallbackOrder++);
+ if (pt > 0) {
+ order = displayName.substring(0, pt);
+ displayName = displayName.substring(pt + 1);
+ }
+
+ // 行オブジェクト作成
+ AppConfigRow rowItem = new AppConfigRow(name, accessor, value);
+ rowItem.setDisplayName(displayName);
+ rowItem.setOrder(order);
+
+ items.add(rowItem);
+ }
+
+ // 表示順に並べる
+ Collections.sort(items, new Comparator<AppConfigRow>() {
+ @Override
+ public int compare(AppConfigRow o1, AppConfigRow o2) {
+ int ret = o1.getOrder().compareTo(o2.getOrder());
+ if (ret == 0) {
+ ret = o1.getDisplayName().compareTo(o2.getDisplayName());
+ }
+ if (ret == 0) {
+ ret = o1.getName().compareTo(o2.getName());
+ }
+ return ret;
+ }
+ });
+
+ appConfigTableModel.setItems(items);
+
+ // 最後に使ったキャラクターデータディレクトリの自動選択設定
try {
recentCharactersDir = RecentCharactersDir.load();
if (recentCharactersDir != null) {
File lastUseCharactersDir = recentCharactersDir.getLastUseCharacterDir();
- boolean enableLastUseCharacterDir = lastUseCharactersDir != null && lastUseCharactersDir.isDirectory();
+ boolean enableLastUseCharacterDir = lastUseCharactersDir != null && lastUseCharactersDir.isDirectory();
boolean doNotAskAgain = enableLastUseCharacterDir && recentCharactersDir.isDoNotAskAgain();
chkResetDoNotAskAgain.setEnabled(enableLastUseCharacterDir);
chkResetDoNotAskAgain.setSelected(!doNotAskAgain);
recentCharactersDir = null;
logger.log(Level.WARNING, "RecentCharactersDir load failed.", ex);
}
+
+ // 初期状態の保存
+ this.orgDoNotAskAgain = chkResetDoNotAskAgain.isSelected();
}
/**
JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) != JOptionPane.OK_OPTION) {
return;
}
-
+
try {
File baseDir = ConfigurationDirUtilities.getUserDataDir();
SetupLocalization setup = new SetupLocalization(baseDir);
ErrorMessageHelper.showErrorDialog(this, ex);
}
}
-
+
protected void onClose() {
if (appConfigTableModel.isModified()) {
Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
}
dispose();
}
-
+
+ /**
+ * AppConfigと、キャラクターデータディレクトリの起動時の選択有無の設定値を保存する。
+ */
protected void onUpdate() {
-
+
if (appConfigTable.isEditing()) {
// 編集中ならば許可しない.
Toolkit tk = Toolkit.getDefaultToolkit();
tk.beep();
return;
}
-
+
Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
.getLocalizedProperties("languages/appconfigdialog");
- // 編集されたプロパティを取得する.
- Properties props = appConfigTableModel.getProperties();
+ // 編集されたAppConfigの設定値を取得する. (変更のあるもののみ)
+ Map<String, Object> modifiedValues = new HashMap<String, Object>();
+ for (AppConfigRow rowItem : appConfigTableModel.getItems()) {
+ if (rowItem.isModified()) {
+ String name = rowItem.getName();
+ Object value = rowItem.getValue();
+ modifiedValues.put(name, value);
+ }
+ }
- // 編集されたプロパティが適用可能か検証する.
- Set<String> rejectNames = AppConfig.checkProperties(props);
- if (!rejectNames.isEmpty()) {
- // エラーがある場合
- appConfigTableModel.setRejectNames(rejectNames);
+ // キャラクターデータディレクトリの起動時の選択状態の変更状態
+ boolean updateRecentCharactersDir = (orgDoNotAskAgain != chkResetDoNotAskAgain.isSelected());
- JOptionPane.showMessageDialog(this, strings.getProperty("error.message"),
- strings.getProperty("error.caption"), JOptionPane.ERROR_MESSAGE);
+ if (!updateRecentCharactersDir && modifiedValues.isEmpty()) {
+ // 変更点がないので何もしない
return;
}
- try {
- // アプリケーション設定を更新し、保存する.
- AppConfig appConfig = AppConfig.getInstance();
- appConfig.update(props);
- appConfig.saveConfig();
-
- // キャラクターデータディレクトリの起動時の選択
- if (chkResetDoNotAskAgain.isEnabled()) {
- boolean doNotAskAgain = !chkResetDoNotAskAgain.isSelected();
- if (doNotAskAgain != recentCharactersDir.isDoNotAskAgain()) {
- recentCharactersDir.setDoNotAskAgain(doNotAskAgain);
- recentCharactersDir.saveRecents();
- }
+ // AppConfigの保存
+ if (!modifiedValues.isEmpty()) {
+ // 編集されたプロパティが適用可能か検証する.
+ Set<String> rejectNames = AppConfig.checkProperties(modifiedValues);
+ if (!rejectNames.isEmpty()) {
+ // エラーがある場合
+ appConfigTableModel.setRejectNames(rejectNames);
+
+ JOptionPane.showMessageDialog(this, strings.getProperty("error.message"),
+ strings.getProperty("error.caption"), JOptionPane.ERROR_MESSAGE);
+ return;
}
- } catch (Exception ex) {
- ErrorMessageHelper.showErrorDialog(this, ex);
- return;
+ try {
+ // アプリケーション設定を更新し、保存する.
+ AppConfig appConfig = AppConfig.getInstance();
+ appConfig.update(modifiedValues);
+ appConfig.saveConfig();
+
+ } catch (Exception ex) {
+ ErrorMessageHelper.showErrorDialog(this, ex);
+ return;
+ }
}
-
+
+ // キャラクターデータディレクトリの起動時の選択の保存
+ if (updateRecentCharactersDir) {
+ try {
+ if (chkResetDoNotAskAgain.isEnabled()) {
+ boolean doNotAskAgain = !chkResetDoNotAskAgain.isSelected();
+ if (doNotAskAgain != recentCharactersDir.isDoNotAskAgain()) {
+ recentCharactersDir.setDoNotAskAgain(doNotAskAgain);
+ recentCharactersDir.saveRecents();
+ }
+ }
+ } catch (Exception ex) {
+ ErrorMessageHelper.showErrorDialog(this, ex);
+ return;
+ }
+ }
+
// アプリケーションの再起動が必要なことを示すダイアログを表示する.
String message = strings.getProperty("caution");
JOptionPane.showMessageDialog(this, message);
dispose();
}
-
}
-class AppConfigRowModel implements Comparable<AppConfigRowModel> {
-
- private Properties target;
-
- private String key;
-
- private String displayName;
-
- private boolean rejected;
-
- public AppConfigRowModel(Properties target, String key, String displayName) {
- this.target = target;
- this.key = key;
- this.displayName = displayName;
+/**
+ * カラーセル
+ */
+class ColorCell extends JPanel {
+ private static final long serialVersionUID = 1L;
+
+ private String title = "Color";
+
+ private JPanel box;
+
+ private JLabel label;
+
+ private JButton button;
+
+ private ActionListener actionListener;
+
+ public ColorCell() {
+ this(null);
}
- @Override
- public int hashCode() {
- return key == null ? 0 : key.hashCode();
+ /**
+ * ボタンのアクションリスナを指定して構築する
+ * @param actionListener
+ */
+ public ColorCell(ActionListener actionListener) {
+ super(new BorderLayout());
+ this.actionListener = actionListener;
+
+ box = new JPanel(new BorderLayout());
+
+ label = new JLabel();
+ label.setHorizontalAlignment(SwingConstants.CENTER);
+ box.add(label, BorderLayout.CENTER);
+ box.setBorder(BorderFactory.createEtchedBorder());
+
+ button = new JButton();
+ Dimension dim = button.getPreferredSize();
+ dim.width = 24;
+ button.setPreferredSize(dim);
+ button.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ onClick(e);
+ }
+ });
+
+ add(box, BorderLayout.CENTER);
+ add(button, BorderLayout.EAST);
+ setSelectedColor(Color.BLACK);
}
-
- @Override
- public boolean equals(Object obj) {
- if (obj == this) {
- return true;
- }
- if (obj != null && obj instanceof AppConfigRowModel) {
- AppConfigRowModel o = (AppConfigRowModel) obj;
- return key == null ? o.key == null : key.equals(o.key);
- }
- return false;
+
+ public String getTitle() {
+ return title;
}
-
- public int compareTo(AppConfigRowModel o) {
- if (o == this) {
- return 0;
- }
- int ret = displayName.compareTo(o.displayName);
- if (ret == 0) {
- ret = key.compareTo(o.key);
+
+ public void setTitle(String title) {
+ String old = this.title;
+ if (old == null ? title != null : !old.equals(title)) {
+ this.title = title;
+ firePropertyChange("title", old, title);
}
- return ret;
- }
-
- public String getKey() {
- return key;
}
-
- public void setValue(String value) {
- if (value == null) {
- value = "";
+
+ protected void onClick(ActionEvent e) {
+ // ※ カラー選択ダイアログは、Java7以降でないとアルファ値の設定はできない。
+ // Java6で実行するとアルファチャネルが消されたものになる。
+ // (設定ファイルとしては手作業では設定可能なので、とりあえず、このまま。)
+ Color selColor = JColorChooser.showDialog(ColorCell.this, title, selectedColor);
+ if (selColor != null) {
+ setSelectedColor(selColor);
+ if (actionListener != null) {
+ actionListener.actionPerformed(e);
+ }
}
- target.setProperty(key, value);
}
-
- public String getValue() {
- return target.getProperty(key);
+
+ private Color selectedColor;
+
+ public Color getSelectedColor() {
+ return selectedColor;
}
- public String getDisplayName() {
- int sep = displayName.indexOf(';');
- if (sep >= 0) {
- return displayName.substring(sep + 1);
+ public void setSelectedColor(Color color) {
+ if (color == null) {
+ color = Color.BLACK;
+ }
+ Color old = this.selectedColor;
+ if (old == null ? color != null : !old.equals(color)) {
+ this.selectedColor = color;
+
+ Color colorForeground = new Color(color.getRGB() ^ 0xffffff).brighter();
+
+ int alpha = color.getAlpha();
+
+ // JPanelの背景色としてアルファの透過色をそのまま使うと
+ // 親コンポーネントの背景色と混じり、色のカタログとして用をなさないので
+ // 白を背景色とした合成済みに補正しておく
+ // (アルファが255の場合はそのままで良い)
+ Color premultipliedColor;
+ if (alpha == 255) {
+ premultipliedColor = color;
+ } else {
+ float[] rgb = color.getRGBColorComponents(null);
+ float[] bgRgb = Color.WHITE.getRGBColorComponents(null); // 背景色 = 白色
+ // アルファを合成済みにする
+ float a = ((float) alpha) / 255f;
+ rgb[0] = rgb[0] * a + bgRgb[0] * (1 - a);
+ rgb[1] = rgb[1] * a + bgRgb[1] * (1 - a);
+ rgb[2] = rgb[2] * a + bgRgb[2] * (1 - a);
+ premultipliedColor = new Color(rgb[0], rgb[1], rgb[2]);
+ }
+
+ box.setBackground(premultipliedColor);
+ label.setForeground(colorForeground);
+
+ String msg;
+ if (alpha != 255) {
+ // アルファが255以外の場合はアルファ値も含めてARGBで表示する
+ msg = String.format("#%08X", ((long) color.getRGB()) & 0xffffffffL);
+ } else {
+ // アルファが255の場合はRGBのみ表示する。
+ msg = String.format("#%06X", ((long) color.getRGB()) & 0xffffffL);
+ }
+ label.setText(msg);
+
+ firePropertyChange("selectedColor", old, color);
}
- return displayName;
- }
-
- public boolean isRejected() {
- return rejected;
- }
-
- public void setRejected(boolean rejected) {
- this.rejected = rejected;
}
}
-class AppConfigTableModel extends AbstractTableModelWithComboBoxModel<AppConfigRowModel> {
+/**
+ * カラーセルのレンダラー
+ */
+class ColorCellRender extends DefaultTableCellRenderer {
private static final long serialVersionUID = 1L;
- private static final String[] COLUMN_NAMES;
-
- private static final int[] COLUMN_WIDTHS;
-
- private Properties target = new Properties();
-
- private Properties original;
-
- static {
- Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
- .getLocalizedProperties("languages/appconfigdialog");
+ private ColorCell panel = new ColorCell();
- COLUMN_NAMES = new String[] {
- strings.getProperty("column.key"),
- strings.getProperty("column.value"),
- };
- COLUMN_WIDTHS = new int[] {
- Integer.parseInt(strings.getProperty("column.key.width")),
- Integer.parseInt(strings.getProperty("column.value.width")),
- };
+ @Override
+ public Component getTableCellRendererComponent(JTable table, Object value,
+ boolean isSelected, boolean hasFocus, int row, int column) {
+ panel.setSelectedColor((Color) value);
+ return panel;
}
-
- public void initModel(Properties original) {
- clear();
- target.clear();
-
- this.original = original;
- if (original != null) {
- target.putAll(original);
-
- Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
- .getLocalizedProperties("languages/appconfigdialog");
+}
- for (Object key : target.keySet()) {
- String displayName = strings.getProperty((String) key);
- if (displayName == null || displayName.length() == 0) {
- displayName = (String) key;
- }
-
- AppConfigRowModel rowModel = new AppConfigRowModel(target, (String) key, displayName);
- addRow(rowModel);
- }
- }
+/**
+ * カラーセルを編集モードにした場合のエディタ
+ */
+class ColorCellEditor extends AbstractCellEditor implements TableCellEditor {
- sort();
- }
-
- public void sort() {
- Collections.sort(elements);
- fireTableDataChanged();
- }
-
- public void setRejectNames(Set<String> rejectNames) {
- for (AppConfigRowModel rowModel : elements) {
- String key = rowModel.getKey();
- boolean rejected = (rejectNames != null && rejectNames.contains(key));
- rowModel.setRejected(rejected);
- }
- fireTableDataChanged();
- }
-
- /**
- * 編集されているか?
- *
- * @return 編集されていればtrue、そうでなければfalse
- */
- public boolean isModified() {
- if (original == null) {
- return true;
- }
- return !original.equals(target);
- }
-
- public Properties getProperties() {
- return target;
- }
-
-
- public int getColumnCount() {
- return COLUMN_NAMES.length;
- }
-
- @Override
- public Class<?> getColumnClass(int columnIndex) {
- return String.class;
- }
-
- @Override
- public String getColumnName(int column) {
- return COLUMN_NAMES[column];
- }
-
- @Override
- public boolean isCellEditable(int rowIndex, int columnIndex) {
- if (columnIndex == 1) {
- return true;
- }
- return false;
- }
-
- public Object getValueAt(int rowIndex, int columnIndex) {
- AppConfigRowModel rowModel = getRow(rowIndex);
- switch (columnIndex) {
- case 0:
- return rowModel.getDisplayName();
- case 1:
- return rowModel.getValue();
- }
- return "";
- }
-
- @Override
- public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
- AppConfigRowModel rowModel = getRow(rowIndex);
- if (columnIndex == 1) {
- rowModel.setValue((String) aValue);
- fireTableCellUpdated(rowIndex, columnIndex);
+ private static final long serialVersionUID = 1L;
+
+ private ColorCell colorCell = new ColorCell(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ fireEditingStopped();
}
+ });
+
+ public Component getTableCellEditorComponent(final JTable table, final Object value,
+ final boolean isSelected, final int row, final int column) {
+ colorCell.setSelectedColor((Color) value);
+ return colorCell;
}
-
- public void adjustColumnModel(TableColumnModel columnModel) {
- int mx = columnModel.getColumnCount();
- for (int idx = 0; idx < mx; idx++) {
- columnModel.getColumn(idx).setWidth(COLUMN_WIDTHS[idx]);
- }
+
+ public Object getCellEditorValue() {
+ return colorCell.getSelectedColor();
}
}