1 package charactermanaj.ui;
\r
3 import java.awt.BorderLayout;
\r
4 import java.awt.Container;
\r
5 import java.awt.Dimension;
\r
6 import java.awt.Font;
\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.dnd.DropTarget;
\r
12 import java.awt.event.ActionEvent;
\r
13 import java.awt.event.FocusAdapter;
\r
14 import java.awt.event.FocusEvent;
\r
15 import java.awt.event.KeyEvent;
\r
16 import java.awt.event.WindowAdapter;
\r
17 import java.awt.event.WindowEvent;
\r
18 import java.io.File;
\r
19 import java.util.ArrayList;
\r
20 import java.util.List;
\r
21 import java.util.Properties;
\r
22 import java.util.logging.Level;
\r
23 import java.util.logging.Logger;
\r
25 import javax.swing.AbstractAction;
\r
26 import javax.swing.BorderFactory;
\r
27 import javax.swing.Box;
\r
28 import javax.swing.InputMap;
\r
29 import javax.swing.JButton;
\r
30 import javax.swing.JCheckBox;
\r
31 import javax.swing.JComboBox;
\r
32 import javax.swing.JDialog;
\r
33 import javax.swing.JFileChooser;
\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.KeyStroke;
\r
41 import charactermanaj.Main;
\r
42 import charactermanaj.model.io.WorkingSetPersist;
\r
43 import charactermanaj.ui.util.FileDropTarget;
\r
44 import charactermanaj.util.ErrorMessageHelper;
\r
45 import charactermanaj.util.LocalizedResourcePropertyLoader;
\r
48 * 起動時にキャラクターデータディレクトリを選択するためのモーダルダイアログ.<br>
\r
52 public class SelectCharatersDirDialog extends JDialog {
\r
54 private static final long serialVersionUID = -888834575856349442L;
\r
56 private static final Logger logger = Logger.getLogger(SelectCharatersDirDialog.class.getName());
\r
59 * 最後に使用したキャラクターデータディレクトリと、その履歴情報.
\r
61 private final RecentCharactersDir recentCharactersDir;
\r
66 private File defaultCharactersDir;
\r
72 private File selectedCharacterDir;
\r
77 private boolean doNotAskAgain;
\r
83 private JComboBox combDir;
\r
86 * 次回起動時に問い合わせないチェックボックス
\r
88 private JCheckBox chkDoNotAsk;
\r
92 public File getDefaultCharactersDir() {
\r
93 return defaultCharactersDir;
\r
96 public void setDefaultCharactersDir(File defaultCharactersDir) {
\r
97 this.defaultCharactersDir = defaultCharactersDir;
\r
100 public File getSelectedCharacterDir() {
\r
101 return selectedCharacterDir;
\r
104 public boolean isDoNotAskAgain() {
\r
105 return doNotAskAgain;
\r
113 * @param recentCharactersDir
\r
114 * 最後に使用したキャラクターデータディレクトリと、その履歴情報.
\r
116 protected SelectCharatersDirDialog(JFrame parent, RecentCharactersDir recentCharactersDir) {
\r
117 super(parent, true);
\r
119 if (recentCharactersDir == null) {
\r
120 throw new IllegalArgumentException(
\r
121 "recentCharactersDirにnullは指定できません。");
\r
123 this.recentCharactersDir = recentCharactersDir;
\r
125 setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
\r
126 addWindowListener(new WindowAdapter() {
\r
128 public void windowClosing(WindowEvent e) {
\r
134 } catch (RuntimeException ex) {
\r
135 logger.log(Level.SEVERE, "キャラクターディレクトリ選択ダイアログの生成に失敗しました。", ex);
\r
141 private void initComponent() {
\r
142 Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
\r
143 .getLocalizedProperties("languages/selectCharatersDirDialog");
\r
145 Container contentPane = getContentPane();
\r
146 contentPane.setLayout(new BorderLayout(3, 3));
\r
148 AbstractAction actOk = new AbstractAction(strings.getProperty("btn.ok")) {
\r
149 private static final long serialVersionUID = 1L;
\r
150 public void actionPerformed(ActionEvent e) {
\r
155 AbstractAction actClose = new AbstractAction(strings.getProperty("btn.cancel")) {
\r
156 private static final long serialVersionUID = 1L;
\r
157 public void actionPerformed(ActionEvent e) {
\r
162 AbstractAction actBrowse = new AbstractAction(strings.getProperty("btn.chooseDir")) {
\r
163 private static final long serialVersionUID = 1L;
\r
164 public void actionPerformed(ActionEvent e) {
\r
169 AbstractAction actRemoveRecent = new AbstractAction(strings.getProperty("btn.clearRecentList")) {
\r
170 private static final long serialVersionUID = 1L;
\r
171 public void actionPerformed(ActionEvent e) {
\r
176 AbstractAction actRemoveWorkingSets = new AbstractAction(
\r
177 strings.getProperty("btn.clearWorkingSets")) {
\r
178 private static final long serialVersionUID = 1L;
\r
179 public void actionPerformed(ActionEvent e) {
\r
180 onRemoveWorkingSets();
\r
184 final JButton btnRemoveWorkingSets = new JButton(actRemoveWorkingSets);
\r
185 final JButton btnRemoveRecent = new JButton(actRemoveRecent);
\r
186 final JButton btnOK = new JButton(actOk);
\r
187 final JButton btnCancel = new JButton(actClose);
\r
188 final JButton btnBroseForDir = new JButton(actBrowse);
\r
190 Toolkit tk = Toolkit.getDefaultToolkit();
\r
191 final JRootPane rootPane = getRootPane();
\r
192 FocusAdapter focusAdapter = new FocusAdapter() {
\r
194 public void focusGained(FocusEvent e) {
\r
195 JButton btn = (JButton) e.getSource();
\r
196 rootPane.setDefaultButton(btn);
\r
199 public void focusLost(FocusEvent e) {
\r
200 rootPane.setDefaultButton(btnOK);
\r
203 rootPane.setDefaultButton(btnOK);
\r
204 InputMap im = rootPane.getInputMap();
\r
205 im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "close");
\r
206 im.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, tk.getMenuShortcutKeyMask()), "close");
\r
207 rootPane.getActionMap().put("close", actClose);
\r
209 btnRemoveWorkingSets.addFocusListener(focusAdapter);
\r
210 btnRemoveRecent.addFocusListener(focusAdapter);
\r
211 btnOK.addFocusListener(focusAdapter);
\r
212 btnCancel.addFocusListener(focusAdapter);
\r
213 btnBroseForDir.addFocusListener(focusAdapter);
\r
216 JPanel dirPanel = new JPanel(new BorderLayout(3, 3));
\r
217 dirPanel.setBorder(BorderFactory.createEmptyBorder(3, 10, 3, 3));
\r
219 JLabel lbl = new JLabel(strings.getProperty("caption"), JLabel.CENTER);
\r
220 lbl.setFont(lbl.getFont().deriveFont(Font.BOLD));
\r
221 lbl.setBorder(BorderFactory.createEmptyBorder(5, 0, 5, 0));
\r
222 Dimension dim = lbl.getPreferredSize();
\r
223 dim.width = Integer.parseInt(strings.getProperty("width"));
\r
224 lbl.setPreferredSize(dim);
\r
225 dirPanel.add(lbl, BorderLayout.NORTH);
\r
227 combDir = new JComboBox();
\r
228 combDir.setEditable(true);
\r
230 dirPanel.add(combDir, BorderLayout.CENTER);
\r
232 dirPanel.add(new JLabel(strings.getProperty("lbl.dir")), BorderLayout.WEST);
\r
233 dirPanel.add(btnBroseForDir, BorderLayout.EAST);
\r
235 contentPane.add(dirPanel, BorderLayout.NORTH);
\r
237 JPanel btnPanel = new JPanel();
\r
238 GridBagLayout gbl = new GridBagLayout();
\r
239 btnPanel.setLayout(gbl);
\r
241 chkDoNotAsk = new JCheckBox(strings.getProperty("chk.doNotAskAgein"));
\r
242 chkDoNotAsk.setSelected(recentCharactersDir.isDoNotAskAgain());
\r
244 GridBagConstraints gbc = new GridBagConstraints();
\r
248 gbc.gridheight = 1;
\r
251 gbc.fill = GridBagConstraints.BOTH;
\r
252 gbc.anchor = GridBagConstraints.WEST;
\r
255 gbc.insets = new Insets(3, 3, 3, 3);
\r
257 btnPanel.add(chkDoNotAsk, gbc);
\r
262 gbc.gridheight = 1;
\r
265 btnPanel.add(btnRemoveRecent, gbc);
\r
270 gbc.gridheight = 1;
\r
273 btnPanel.add(btnRemoveWorkingSets, gbc);
\r
278 gbc.gridheight = 1;
\r
282 btnPanel.add(Box.createGlue(), gbc);
\r
284 gbc.gridx = Main.isLinuxOrMacOSX() ? 4 : 3;
\r
287 gbc.gridheight = 1;
\r
291 btnPanel.add(btnOK, gbc);
\r
294 gbc.gridx = Main.isLinuxOrMacOSX() ? 3 : 4;
\r
297 gbc.gridheight = 1;
\r
300 btnPanel.add(btnCancel, gbc);
\r
305 gbc.gridheight = 1;
\r
311 btnPanel.add(Box.createGlue(), gbc);
\r
313 setTitle(strings.getProperty("title"));
\r
314 setResizable(false);
\r
316 contentPane.add(btnPanel, BorderLayout.SOUTH);
\r
318 // フォルダのドロップによる入力を許可
\r
319 new DropTarget(this, new FileDropTarget() {
\r
321 protected void onDropFiles(List<File> dropFiles) {
\r
322 setSelectFile(dropFiles);
\r
326 protected void onException(Exception ex) {
\r
327 ErrorMessageHelper.showErrorDialog(SelectCharatersDirDialog.this, ex);
\r
332 setLocationRelativeTo(null);
\r
336 * ドロップによるファイル名の設定.<br>
\r
337 * 最初の1つだけを使用する.<br>
\r
338 * リストが空であるか、最初のファイルが、フォルダでなければ何もしない.<br>
\r
343 protected void setSelectFile(List<File> dropFiles) {
\r
344 if (dropFiles.isEmpty()) {
\r
347 File dropFile = dropFiles.get(0);
\r
348 if ( !dropFile.exists() || !dropFile.isDirectory()) {
\r
351 combDir.setSelectedItem(dropFile);
\r
354 protected void onClose() {
\r
355 selectedCharacterDir = null;
\r
359 protected void onOK() {
\r
361 Object value = combDir.getSelectedItem();
\r
362 if (value != null && value instanceof String) {
\r
363 value = new File((String) value);
\r
366 if (value != null && value instanceof File) {
\r
367 File file = (File) value;
\r
368 if (!file.exists()) {
\r
369 boolean result = file.mkdirs();
\r
370 logger.log(Level.INFO, "mkdirs(" + file+ ") succeeded=" + result);
\r
372 if (file.isDirectory()) {
\r
373 logger.log(Level.CONFIG, "selectedCharactersDir=" + file);
\r
374 selectedCharacterDir = file;
\r
375 doNotAskAgain = chkDoNotAsk.isSelected();
\r
381 // 選択されていないかファイルでない場合
\r
382 Toolkit tk = Toolkit.getDefaultToolkit();
\r
385 } catch (Exception ex) {
\r
386 ErrorMessageHelper.showErrorDialog(this, ex);
\r
390 protected void onBrowse() {
\r
392 Object selectedItem = combDir.getSelectedItem();
\r
393 String directoryTxt = null;
\r
394 if (selectedItem != null) {
\r
395 directoryTxt = selectedItem.toString();
\r
397 JFileChooser dirChooser = new JFileChooser(directoryTxt);
\r
398 dirChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
\r
400 if (dirChooser.showOpenDialog(this) != JFileChooser.APPROVE_OPTION) {
\r
403 File dir = dirChooser.getSelectedFile();
\r
405 combDir.setSelectedItem(dir);
\r
408 } catch (Exception ex) {
\r
409 ErrorMessageHelper.showErrorDialog(this, ex);
\r
413 protected void onRemoveWorkingSets() {
\r
415 Properties strings = LocalizedResourcePropertyLoader
\r
416 .getCachedInstance().getLocalizedProperties(
\r
417 "languages/selectCharatersDirDialog");
\r
420 if (JOptionPane.showConfirmDialog(this,
\r
421 strings.getProperty("confirm.clearWorkingSets"),
\r
422 strings.getProperty("confirm.clearWorkingSets.title"),
\r
423 JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE) != JOptionPane.YES_OPTION) {
\r
427 // 全てのワーキングセットをクリアする.
\r
428 WorkingSetPersist workingSetPersist = WorkingSetPersist
\r
430 workingSetPersist.removeAllWorkingSet();
\r
432 } catch (Exception ex) {
\r
433 ErrorMessageHelper.showErrorDialog(this, ex);
\r
437 protected void onRemoveRecent() {
\r
439 Object current = combDir.getSelectedItem();
\r
441 recentCharactersDir.clrar();
\r
444 if (current != null) {
\r
445 combDir.setSelectedItem(current);
\r
448 } catch (Exception ex) {
\r
449 ErrorMessageHelper.showErrorDialog(this, ex);
\r
453 protected void setRecents() {
\r
455 while (combDir.getItemCount() > 0) {
\r
456 combDir.removeItemAt(0);
\r
459 // 前回使用したディレクトリを最優先候補
\r
460 ArrayList<File> priorityDirs = new ArrayList<File>();
\r
461 File lastUseCatacterDir = recentCharactersDir.getLastUseCharacterDir();
\r
462 if (lastUseCatacterDir != null) {
\r
463 if (defaultCharactersDir != null && !lastUseCatacterDir.equals(defaultCharactersDir)) {
\r
464 combDir.addItem(lastUseCatacterDir);
\r
465 priorityDirs.add(lastUseCatacterDir);
\r
468 // デフォルトのキャラクターデータを第2位に設定
\r
469 if (defaultCharactersDir != null) {
\r
470 combDir.addItem(defaultCharactersDir);
\r
471 priorityDirs.add(defaultCharactersDir);
\r
474 for (File charactersDir : recentCharactersDir
\r
475 .getRecentCharacterDirsOrderByNewly()) {
\r
476 if (charactersDir == null) {
\r
479 if (!priorityDirs.contains(charactersDir)) {
\r
480 combDir.addItem(charactersDir);
\r
484 if (combDir.getItemCount() > 0) {
\r
485 combDir.setSelectedIndex(0);
\r
491 * キャラクターデータディレクトリを履歴および既定のディレクトリから、任意の使用するディレクトリを選択する.<br>
\r
492 * 既定のディレクトリは常に選択候補とする.<br>
\r
493 * 新しいディレクトリを指定した場合は、履歴に追加される.<br>
\r
494 * 「再度問い合わせなし」を選択している場合で、そのディレクトリが実在すれば、選択ダイアログを表示せず、それを返す.<br>
\r
496 * @param defaultCharacterDir
\r
498 * @return 選択したディレクトリ、キャンセルした場合はnull
\r
500 public static File getCharacterDir(File defaultCharacterDir) {
\r
501 RecentCharactersDir recentChars;
\r
503 recentChars = RecentCharactersDir.load();
\r
505 } catch (Exception ex) {
\r
506 logger.log(Level.WARNING, "最後に使用したキャラクターディレクトリ情報の読み込みに失敗しました。", ex);
\r
507 recentChars = null;
\r
509 if (recentChars == null) {
\r
510 recentChars = new RecentCharactersDir();
\r
513 logger.log(Level.CONFIG, "RecentCharacterDirs.doNotAskAgain=" + recentChars.isDoNotAskAgain());
\r
514 if (recentChars.isDoNotAskAgain()) {
\r
515 // 「再度問い合わせ無し」の場合で、過去のディレクトリが有効であれば、それを返す.
\r
516 File recentCharDir = recentChars.getLastUseCharacterDir();
\r
517 if (recentCharDir != null && recentCharDir.exists() && recentCharDir.isDirectory()) {
\r
518 return recentCharDir;
\r
520 recentChars.setDoNotAskAgain(false); // 不正である場合は「再度問い合わせ無し」をリセットする.
\r
523 File selectedCharacterDir;
\r
524 SelectCharatersDirDialog dlg = new SelectCharatersDirDialog(null, recentChars);
\r
525 dlg.setDefaultCharactersDir(defaultCharacterDir);
\r
527 dlg.setVisible(true);
\r
529 selectedCharacterDir = dlg.getSelectedCharacterDir();
\r
530 if (selectedCharacterDir != null) {
\r
531 recentChars.setLastUseCharacterDir(selectedCharacterDir);
\r
533 recentChars.setDoNotAskAgain(dlg.isDoNotAskAgain());
\r
534 recentChars.saveRecents();
\r
536 } catch (Exception ex) {
\r
537 logger.log(Level.WARNING, "最後に使用したキャラクターディレクトリ情報の保存に失敗しました。",
\r
541 return selectedCharacterDir;
\r