OSDN Git Service

dmg作成バージョンの取得方法の修正
[charactermanaj/CharacterManaJ.git] / src / main / java / charactermanaj / ui / AppConfigDialog.java
1 package charactermanaj.ui;\r
2 \r
3 import java.awt.BorderLayout;\r
4 import java.awt.Color;\r
5 import java.awt.Component;\r
6 import java.awt.Container;\r
7 import java.awt.GridBagConstraints;\r
8 import java.awt.GridBagLayout;\r
9 import java.awt.Insets;\r
10 import java.awt.Toolkit;\r
11 import java.awt.event.ActionEvent;\r
12 import java.awt.event.KeyEvent;\r
13 import java.awt.event.MouseEvent;\r
14 import java.awt.event.WindowAdapter;\r
15 import java.awt.event.WindowEvent;\r
16 import java.io.File;\r
17 import java.util.Collections;\r
18 import java.util.EnumSet;\r
19 import java.util.Properties;\r
20 import java.util.Set;\r
21 import java.util.logging.Level;\r
22 import java.util.logging.Logger;\r
23 \r
24 import javax.swing.AbstractAction;\r
25 import javax.swing.Action;\r
26 import javax.swing.ActionMap;\r
27 import javax.swing.BorderFactory;\r
28 import javax.swing.Box;\r
29 import javax.swing.InputMap;\r
30 import javax.swing.JButton;\r
31 import javax.swing.JCheckBox;\r
32 import javax.swing.JComponent;\r
33 import javax.swing.JDialog;\r
34 import javax.swing.JFrame;\r
35 import javax.swing.JLabel;\r
36 import javax.swing.JOptionPane;\r
37 import javax.swing.JPanel;\r
38 import javax.swing.JRootPane;\r
39 import javax.swing.JScrollPane;\r
40 import javax.swing.JTable;\r
41 import javax.swing.KeyStroke;\r
42 import javax.swing.table.TableCellRenderer;\r
43 import javax.swing.table.TableColumnModel;\r
44 \r
45 import charactermanaj.Main;\r
46 import charactermanaj.model.AppConfig;\r
47 import charactermanaj.ui.model.AbstractTableModelWithComboBoxModel;\r
48 import charactermanaj.util.ConfigurationDirUtilities;\r
49 import charactermanaj.util.DesktopUtilities;\r
50 import charactermanaj.util.ErrorMessageHelper;\r
51 import charactermanaj.util.LocalizedResourcePropertyLoader;\r
52 import charactermanaj.util.SetupLocalization;\r
53 \r
54 \r
55 /**\r
56  * アプリケーション設定ダイアログ\r
57  * \r
58  * @author seraphy\r
59  */\r
60 public class AppConfigDialog extends JDialog {\r
61 \r
62         private static final long serialVersionUID = 1L;\r
63         \r
64         private static final Logger logger = Logger.getLogger(AppConfigDialog.class.getName());\r
65 \r
66         private AppConfigTableModel appConfigTableModel;\r
67         \r
68         private JTable appConfigTable;\r
69         \r
70         private JCheckBox chkResetDoNotAskAgain;\r
71         \r
72         private RecentCharactersDir recentCharactersDir;\r
73         \r
74         public AppConfigDialog(JFrame parent) {\r
75                 super(parent, true);\r
76                 try {\r
77                         setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);\r
78                         addWindowListener(new WindowAdapter() {\r
79                                 @Override\r
80                                 public void windowClosing(WindowEvent e) {\r
81                                         onClose();\r
82                                 }\r
83                         });\r
84 \r
85                         initComponent();\r
86                         \r
87                         loadData();\r
88                         \r
89                 } catch (RuntimeException ex) {\r
90                         logger.log(Level.SEVERE, "appConfig construct failed.", ex);\r
91                         dispose();\r
92                         throw ex;\r
93                 }\r
94         }\r
95         \r
96         private void initComponent() {\r
97                 \r
98                 Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()\r
99                                 .getLocalizedProperties("languages/appconfigdialog");\r
100                 \r
101                 setTitle(strings.getProperty("title"));\r
102 \r
103                 Container contentPane = getContentPane();\r
104                 contentPane.setLayout(new BorderLayout());\r
105 \r
106                 // buttons\r
107                 JPanel btnPanel = new JPanel();\r
108                 btnPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 45));\r
109                 GridBagLayout btnPanelLayout = new GridBagLayout();\r
110                 btnPanel.setLayout(btnPanelLayout);\r
111                 \r
112                 GridBagConstraints gbc = new GridBagConstraints();\r
113                 \r
114                 Action actApply = new AbstractAction(strings.getProperty("btn.apply")) {\r
115                         private static final long serialVersionUID = 1L;\r
116                         public void actionPerformed(ActionEvent e) {\r
117                                 onUpdate();\r
118                         }\r
119                 };\r
120                 Action actCancel = new AbstractAction(strings.getProperty("btn.cancel")) {\r
121                         private static final long serialVersionUID = 1L;\r
122                         public void actionPerformed(ActionEvent e) {\r
123                                 onClose();\r
124                         }\r
125                 };\r
126                 Action actLocalization = new AbstractAction(strings.getProperty("btn.setupLocalization")) {\r
127                         private static final long serialVersionUID = 1L;\r
128                         public void actionPerformed(ActionEvent e) {\r
129                                 onSetupLocalization();\r
130                         }\r
131                 };\r
132                 \r
133                 chkResetDoNotAskAgain = new JCheckBox(strings.getProperty("chk.askForCharactersDir"));\r
134 \r
135                 gbc.gridx = 0;\r
136                 gbc.gridy = 0;\r
137                 gbc.gridheight = 1;\r
138                 gbc.gridwidth = 3;\r
139                 gbc.anchor = GridBagConstraints.WEST;\r
140                 gbc.fill = GridBagConstraints.NONE;\r
141                 gbc.insets = new Insets(0, 0, 0, 0);\r
142                 gbc.ipadx = 0;\r
143                 gbc.ipady = 0;\r
144                 gbc.weightx = 1.;\r
145                 gbc.weighty = 0.;\r
146                 btnPanel.add(chkResetDoNotAskAgain, gbc);\r
147                 \r
148                 gbc.gridx = 0;\r
149                 gbc.gridy = 1;\r
150                 gbc.gridheight = 1;\r
151                 gbc.gridwidth = 3;\r
152                 gbc.anchor = GridBagConstraints.WEST;\r
153                 gbc.fill = GridBagConstraints.NONE;\r
154                 gbc.insets = new Insets(3, 3, 3, 3);\r
155                 gbc.ipadx = 0;\r
156                 gbc.ipady = 0;\r
157                 gbc.weightx = 1.;\r
158                 gbc.weighty = 0.;\r
159                 btnPanel.add(new JButton(actLocalization), gbc);\r
160 \r
161                 gbc.gridx = 0;\r
162                 gbc.gridy = 2;\r
163                 gbc.gridheight = 1;\r
164                 gbc.gridwidth = 1;\r
165                 gbc.fill = GridBagConstraints.BOTH;\r
166                 gbc.weightx = 1.;\r
167                 gbc.weighty = 0.;\r
168                 btnPanel.add(Box.createHorizontalGlue(), gbc);\r
169                 \r
170                 gbc.gridx = Main.isLinuxOrMacOSX() ? 2 : 1;\r
171                 gbc.weightx = 0.;\r
172                 JButton btnApply = new JButton(actApply);\r
173                 btnPanel.add(btnApply, gbc);\r
174                 \r
175                 gbc.gridx = Main.isLinuxOrMacOSX() ? 1 : 2;\r
176                 gbc.weightx = 0.;\r
177                 JButton btnCancel = new JButton(actCancel);\r
178                 btnPanel.add(btnCancel, gbc);\r
179                 \r
180                 add(btnPanel, BorderLayout.SOUTH);\r
181                 \r
182                 setSize(350, 400);\r
183                 setLocationRelativeTo(getParent());\r
184                 \r
185                 // Notes\r
186                 JLabel lblCaution = new JLabel(strings.getProperty("caution"), JLabel.CENTER);\r
187                 lblCaution.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));\r
188                 lblCaution.setForeground(Color.red);\r
189                 contentPane.add(lblCaution, BorderLayout.NORTH);\r
190                 \r
191                 // Model\r
192                 appConfigTableModel = new AppConfigTableModel();\r
193 \r
194                 // JTable\r
195                 AppConfig appConfig = AppConfig.getInstance();\r
196                 final Color invalidBgColor = appConfig.getInvalidBgColor();\r
197                 appConfigTable = new JTable(appConfigTableModel) {\r
198                         private static final long serialVersionUID = 1L;\r
199                         @Override\r
200                         public Component prepareRenderer(TableCellRenderer renderer,\r
201                                         int row, int column) {\r
202                                 Component comp = super.prepareRenderer(renderer, row, column);\r
203                                 AppConfigRowModel rowModel = appConfigTableModel.getRow(row);\r
204                                 if (rowModel.isRejected()) {\r
205                                         comp.setBackground(invalidBgColor);\r
206                                 } else {\r
207                                         if (isCellSelected(row, column)) {\r
208                                                 comp.setBackground(getSelectionBackground());\r
209                                         } else {\r
210                                                 comp.setBackground(getBackground());\r
211                                         }\r
212                                 }\r
213                                 return comp;\r
214                         }\r
215                         @Override\r
216                         public String getToolTipText(MouseEvent event) {\r
217                                 int row = rowAtPoint(event.getPoint());\r
218                                 int col = columnAtPoint(event.getPoint());\r
219                                 if (col == 0) {\r
220                                         AppConfigRowModel rowModel = appConfigTableModel.getRow(row);\r
221                                         return rowModel.getDisplayName();\r
222                                 }\r
223                                 return super.getToolTipText(event);\r
224                         }\r
225                 };\r
226                 appConfigTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);\r
227                 appConfigTable.setShowGrid(true);\r
228                 appConfigTable.setGridColor(AppConfig.getInstance().getGridColor());\r
229                 appConfigTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);\r
230                 appConfigTableModel.adjustColumnModel(appConfigTable.getColumnModel());\r
231                 JScrollPane appConfigTableSP = new JScrollPane(appConfigTable);\r
232                 appConfigTableSP.setBorder(BorderFactory.createCompoundBorder(\r
233                                 BorderFactory.createEmptyBorder(0, 3, 0, 3),\r
234                                 BorderFactory.createTitledBorder(strings.getProperty("table.caption")))\r
235                                 );\r
236                 appConfigTableSP.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);\r
237                 contentPane.add(appConfigTableSP, BorderLayout.CENTER);\r
238                 \r
239                 // RootPane\r
240                 Toolkit tk = Toolkit.getDefaultToolkit();\r
241                 JRootPane rootPane = getRootPane();\r
242                 InputMap im = rootPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);\r
243                 ActionMap am = rootPane.getActionMap();\r
244                 im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "closeAppConfigDialog");\r
245                 im.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, tk.getMenuShortcutKeyMask()), "closeAppConfigDialog");\r
246                 am.put("closeAppConfigDialog", actCancel);\r
247 \r
248                 // 保存先が無効であれば適用ボタンを有効にしない.\r
249                 boolean enableSave = !appConfig.getPrioritySaveFileList().isEmpty();\r
250                 btnApply.setEnabled(enableSave);\r
251         }\r
252         \r
253         private void loadData() {\r
254                 Properties original = AppConfig.getInstance().getProperties();\r
255                 appConfigTableModel.initModel(original);\r
256 \r
257                 try {\r
258                         recentCharactersDir = RecentCharactersDir.load();\r
259 \r
260                         if (recentCharactersDir != null) {\r
261                                 File lastUseCharactersDir = recentCharactersDir.getLastUseCharacterDir();\r
262                                 boolean enableLastUseCharacterDir = lastUseCharactersDir != null && lastUseCharactersDir.isDirectory(); \r
263                                 boolean doNotAskAgain = enableLastUseCharacterDir && recentCharactersDir.isDoNotAskAgain();\r
264                                 chkResetDoNotAskAgain.setEnabled(enableLastUseCharacterDir);\r
265                                 chkResetDoNotAskAgain.setSelected(!doNotAskAgain);\r
266                         }\r
267 \r
268                 } catch (Exception ex) {\r
269                         recentCharactersDir = null;\r
270                         logger.log(Level.WARNING, "RecentCharactersDir load failed.", ex);\r
271                 }\r
272         }\r
273 \r
274         /**\r
275          * ローカライズリソースをユーザディレクトリ上に展開する.\r
276          */\r
277         protected void onSetupLocalization() {\r
278                 Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()\r
279                         .getLocalizedProperties("languages/appconfigdialog");\r
280                 if (JOptionPane.showConfirmDialog(this,\r
281                                 strings.getProperty("setupLocalization"),\r
282                                 strings.getProperty("confirm.setupLocalization.caption"),\r
283                                 JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) != JOptionPane.OK_OPTION) {\r
284                         return;\r
285                 }\r
286                 \r
287                 try {\r
288                         File baseDir = ConfigurationDirUtilities.getUserDataDir();\r
289                         SetupLocalization setup = new SetupLocalization(baseDir);\r
290                         setup.setupToLocal(\r
291                                         EnumSet.allOf(SetupLocalization.Resources.class), true);\r
292 \r
293                         File resourceDir = setup.getResourceDir();\r
294                         DesktopUtilities.open(resourceDir);\r
295 \r
296                 } catch (Exception ex) {\r
297                         ErrorMessageHelper.showErrorDialog(this, ex);\r
298                 }\r
299         }\r
300         \r
301         protected void onClose() {\r
302                 if (appConfigTableModel.isModified()) {\r
303                         Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()\r
304                                         .getLocalizedProperties("languages/appconfigdialog");\r
305                         if (JOptionPane.showConfirmDialog(this, strings.getProperty("confirm.close"),\r
306                                         strings.getProperty("confirm.close.caption"), JOptionPane.YES_NO_OPTION,\r
307                                         JOptionPane.QUESTION_MESSAGE) != JOptionPane.YES_OPTION) {\r
308                                 return;\r
309                         }\r
310                 }\r
311                 dispose();\r
312         }\r
313         \r
314         protected void onUpdate() {\r
315                 \r
316                 if (appConfigTable.isEditing()) {\r
317                         // 編集中ならば許可しない.\r
318                         Toolkit tk = Toolkit.getDefaultToolkit();\r
319                         tk.beep();\r
320                         return;\r
321                 }\r
322                 \r
323                 Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()\r
324                                 .getLocalizedProperties("languages/appconfigdialog");\r
325 \r
326                 // 編集されたプロパティを取得する.\r
327                 Properties props = appConfigTableModel.getProperties();\r
328 \r
329                 // 編集されたプロパティが適用可能か検証する.\r
330                 Set<String> rejectNames = AppConfig.checkProperties(props);\r
331                 if (!rejectNames.isEmpty()) {\r
332                         // エラーがある場合\r
333                         appConfigTableModel.setRejectNames(rejectNames);\r
334 \r
335                         JOptionPane.showMessageDialog(this, strings.getProperty("error.message"),\r
336                                         strings.getProperty("error.caption"), JOptionPane.ERROR_MESSAGE);\r
337                         return;\r
338                 }\r
339 \r
340                 try {\r
341                         // アプリケーション設定を更新し、保存する.\r
342                         AppConfig appConfig = AppConfig.getInstance();\r
343                         appConfig.update(props);\r
344                         appConfig.saveConfig();\r
345 \r
346                         // キャラクターデータディレクトリの起動時の選択\r
347                         if (chkResetDoNotAskAgain.isEnabled()) {\r
348                                 boolean doNotAskAgain = !chkResetDoNotAskAgain.isSelected();\r
349                                 if (doNotAskAgain != recentCharactersDir.isDoNotAskAgain()) {\r
350                                         recentCharactersDir.setDoNotAskAgain(doNotAskAgain);\r
351                                         recentCharactersDir.saveRecents();\r
352                                 }\r
353                         }\r
354 \r
355                 } catch (Exception ex) {\r
356                         ErrorMessageHelper.showErrorDialog(this, ex);\r
357                         return;\r
358                 }\r
359                 \r
360                 // アプリケーションの再起動が必要なことを示すダイアログを表示する.\r
361                 String message = strings.getProperty("caution");\r
362                 JOptionPane.showMessageDialog(this, message);\r
363 \r
364                 dispose();\r
365         }\r
366         \r
367 }\r
368 \r
369 class AppConfigRowModel implements Comparable<AppConfigRowModel> {\r
370 \r
371         private Properties target;\r
372         \r
373         private String key;\r
374         \r
375         private String displayName;\r
376         \r
377         private boolean rejected;\r
378         \r
379         public AppConfigRowModel(Properties target, String key, String displayName) {\r
380                 this.target = target;\r
381                 this.key = key;\r
382                 this.displayName = displayName;\r
383         }\r
384 \r
385         @Override\r
386         public int hashCode() {\r
387                 return key == null ? 0 : key.hashCode();\r
388         }\r
389         \r
390         @Override\r
391         public boolean equals(Object obj) {\r
392                 if (obj == this) {\r
393                         return true;\r
394                 }\r
395                 if (obj != null && obj instanceof AppConfigRowModel) {\r
396                         AppConfigRowModel o = (AppConfigRowModel) obj;\r
397                         return key == null ? o.key == null : key.equals(o.key);\r
398                 }\r
399                 return false;\r
400         }\r
401         \r
402         public int compareTo(AppConfigRowModel o) {\r
403                 if (o == this) {\r
404                         return 0;\r
405                 }\r
406                 int ret = displayName.compareTo(o.displayName);\r
407                 if (ret == 0) {\r
408                         ret = key.compareTo(o.key);\r
409                 }\r
410                 return ret;\r
411         }\r
412         \r
413         public String getKey() {\r
414                 return key;\r
415         }\r
416         \r
417         public void setValue(String value) {\r
418                 if (value == null) {\r
419                         value = "";\r
420                 }\r
421                 target.setProperty(key, value);\r
422         }\r
423         \r
424         public String getValue() {\r
425                 return target.getProperty(key);\r
426         }\r
427 \r
428         public String getDisplayName() {\r
429                 int sep = displayName.indexOf(';');\r
430                 if (sep >= 0) {\r
431                         return displayName.substring(sep + 1);\r
432                 }\r
433                 return displayName;\r
434         }\r
435         \r
436         public boolean isRejected() {\r
437                 return rejected;\r
438         }\r
439         \r
440         public void setRejected(boolean rejected) {\r
441                 this.rejected = rejected;\r
442         }\r
443 }\r
444 \r
445 class AppConfigTableModel extends AbstractTableModelWithComboBoxModel<AppConfigRowModel> {\r
446 \r
447         private static final long serialVersionUID = 1L;\r
448 \r
449         private static final String[] COLUMN_NAMES;\r
450         \r
451         private static final int[] COLUMN_WIDTHS;\r
452         \r
453         private Properties target = new Properties();\r
454         \r
455         private Properties original;\r
456         \r
457         static {\r
458                 Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()\r
459                                 .getLocalizedProperties("languages/appconfigdialog");\r
460 \r
461                 COLUMN_NAMES = new String[] {\r
462                                 strings.getProperty("column.key"),\r
463                                 strings.getProperty("column.value"),\r
464                 };\r
465                 COLUMN_WIDTHS = new int[] {\r
466                                 Integer.parseInt(strings.getProperty("column.key.width")),\r
467                                 Integer.parseInt(strings.getProperty("column.value.width")),\r
468                 };\r
469         }\r
470         \r
471         public void initModel(Properties original) {\r
472                 clear();\r
473                 target.clear();\r
474 \r
475                 this.original = original;\r
476                 if (original != null) {\r
477                         target.putAll(original);\r
478                         \r
479                         Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()\r
480                                         .getLocalizedProperties("languages/appconfigdialog");\r
481 \r
482                         for (Object key : target.keySet()) {\r
483                                 String displayName = strings.getProperty((String) key);\r
484                                 if (displayName == null || displayName.length() == 0) {\r
485                                         displayName = (String) key;\r
486                                 }\r
487                                 \r
488                                 AppConfigRowModel rowModel = new AppConfigRowModel(target, (String) key, displayName);\r
489                                 addRow(rowModel);\r
490                         }\r
491                 }\r
492 \r
493                 sort();\r
494         }\r
495         \r
496         public void sort() {\r
497                 Collections.sort(elements);\r
498                 fireTableDataChanged();\r
499         }\r
500         \r
501         public void setRejectNames(Set<String> rejectNames) {\r
502                 for (AppConfigRowModel rowModel : elements) {\r
503                         String key = rowModel.getKey();\r
504                         boolean rejected = (rejectNames != null && rejectNames.contains(key));\r
505                         rowModel.setRejected(rejected);\r
506                 }\r
507                 fireTableDataChanged();\r
508         }\r
509         \r
510         /**\r
511          * 編集されているか?\r
512          * \r
513          * @return 編集されていればtrue、そうでなければfalse\r
514          */\r
515         public boolean isModified() {\r
516                 if (original == null) {\r
517                         return true;\r
518                 }\r
519                 return !original.equals(target);\r
520         }\r
521         \r
522         public Properties getProperties() {\r
523                 return target;\r
524         }\r
525         \r
526         \r
527         public int getColumnCount() {\r
528                 return COLUMN_NAMES.length;\r
529         }\r
530         \r
531         @Override\r
532         public Class<?> getColumnClass(int columnIndex) {\r
533                 return String.class;\r
534         }\r
535         \r
536         @Override\r
537         public String getColumnName(int column) {\r
538                 return COLUMN_NAMES[column];\r
539         }\r
540         \r
541         @Override\r
542         public boolean isCellEditable(int rowIndex, int columnIndex) {\r
543                 if (columnIndex == 1) {\r
544                         return true;\r
545                 }\r
546                 return false;\r
547         }\r
548         \r
549         public Object getValueAt(int rowIndex, int columnIndex) {\r
550                 AppConfigRowModel rowModel = getRow(rowIndex);\r
551                 switch (columnIndex) {\r
552                 case 0:\r
553                         return rowModel.getDisplayName();\r
554                 case 1:\r
555                         return rowModel.getValue();\r
556                 }\r
557                 return "";\r
558         }\r
559         \r
560         @Override\r
561         public void setValueAt(Object aValue, int rowIndex, int columnIndex) {\r
562                 AppConfigRowModel rowModel = getRow(rowIndex);\r
563                 if (columnIndex == 1) {\r
564                         rowModel.setValue((String) aValue);\r
565                         fireTableCellUpdated(rowIndex, columnIndex);\r
566                 }\r
567         }\r
568         \r
569         public void adjustColumnModel(TableColumnModel columnModel) {\r
570                 int mx = columnModel.getColumnCount();\r
571                 for (int idx = 0; idx < mx; idx++) {\r
572                         columnModel.getColumn(idx).setWidth(COLUMN_WIDTHS[idx]);\r
573                 }\r
574         }\r
575 }\r