--- /dev/null
+package charactermanaj.ui;\r
+\r
+import java.awt.BorderLayout;\r
+import java.awt.Color;\r
+import java.awt.Component;\r
+import java.awt.Container;\r
+import java.awt.Dimension;\r
+import java.awt.GridBagConstraints;\r
+import java.awt.GridBagLayout;\r
+import java.awt.Insets;\r
+import java.awt.Point;\r
+import java.awt.Toolkit;\r
+import java.awt.Window;\r
+import java.awt.event.ActionEvent;\r
+import java.awt.event.ActionListener;\r
+import java.awt.event.KeyEvent;\r
+import java.awt.event.WindowAdapter;\r
+import java.awt.event.WindowEvent;\r
+import java.beans.PropertyChangeEvent;\r
+import java.beans.PropertyChangeListener;\r
+import java.beans.PropertyChangeSupport;\r
+import java.util.ArrayList;\r
+import java.util.Collections;\r
+import java.util.Comparator;\r
+import java.util.EventListener;\r
+import java.util.EventObject;\r
+import java.util.HashMap;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.Properties;\r
+import java.util.concurrent.atomic.AtomicInteger;\r
+\r
+import javax.swing.AbstractAction;\r
+import javax.swing.ActionMap;\r
+import javax.swing.BorderFactory;\r
+import javax.swing.Box;\r
+import javax.swing.InputMap;\r
+import javax.swing.JButton;\r
+import javax.swing.JComboBox;\r
+import javax.swing.JComponent;\r
+import javax.swing.JDialog;\r
+import javax.swing.JLabel;\r
+import javax.swing.JList;\r
+import javax.swing.JOptionPane;\r
+import javax.swing.JPanel;\r
+import javax.swing.JRootPane;\r
+import javax.swing.JScrollPane;\r
+import javax.swing.JTable;\r
+import javax.swing.JTextField;\r
+import javax.swing.KeyStroke;\r
+import javax.swing.ListCellRenderer;\r
+import javax.swing.ListSelectionModel;\r
+import javax.swing.UIManager;\r
+import javax.swing.event.DocumentEvent;\r
+import javax.swing.event.DocumentListener;\r
+import javax.swing.event.EventListenerList;\r
+import javax.swing.event.TableModelEvent;\r
+import javax.swing.event.TableModelListener;\r
+import javax.swing.plaf.basic.BasicComboBoxEditor;\r
+import javax.swing.table.AbstractTableModel;\r
+import javax.swing.table.TableColumnModel;\r
+\r
+import charactermanaj.Main;\r
+import charactermanaj.model.CustomLayerOrder;\r
+import charactermanaj.model.Layer;\r
+import charactermanaj.model.ListChangeListener;\r
+import charactermanaj.model.ObservableList;\r
+import charactermanaj.model.PartsCategory;\r
+import charactermanaj.ui.model.SimpleComboBoxModel;\r
+import charactermanaj.util.LocalizedResourcePropertyLoader;\r
+\r
+/**\r
+ * レイヤーのカスタマイズを行う編集ダイアログ\r
+ */\r
+public class LayerOrderCustomizeDialog extends JDialog {\r
+\r
+ private static final long serialVersionUID = 525988497443897372L;\r
+\r
+ private static final String STRINGS_RESOURCE = "languages/layerordercustomizedialog";\r
+\r
+ /**\r
+ * レイヤーの編集がされたときに通知されるリスナ\r
+ */\r
+ public interface LayerOrderCustomizeListener extends EventListener {\r
+\r
+ public static class Change extends EventObject {\r
+ private static final long serialVersionUID = -6203020622537017109L;\r
+\r
+ public Change(LayerOrderCustomizeDialog source) {\r
+ super(source);\r
+ }\r
+\r
+ @Override\r
+ public LayerOrderCustomizeDialog getSource() {\r
+ return (LayerOrderCustomizeDialog) super.getSource();\r
+ }\r
+ }\r
+\r
+ void onChange(Change e);\r
+ }\r
+\r
+ /**\r
+ * カテゴリのリスト\r
+ */\r
+ private List<PartsCategory> categories;\r
+\r
+ /**\r
+ * コンストラクタ\r
+ * @param window 親ウィンドウ\r
+ * @param categories カテゴリのリスト\r
+ */\r
+ public LayerOrderCustomizeDialog(Window window, List<PartsCategory> categories) {\r
+ super(window);\r
+ try {\r
+ setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);\r
+ addWindowListener(new WindowAdapter() {\r
+ @Override\r
+ public void windowClosing(WindowEvent e) {\r
+ onClosing();\r
+ }\r
+ });\r
+\r
+ this.categories = categories;\r
+ initLayout();\r
+\r
+ } catch (RuntimeException ex) {\r
+ dispose();\r
+ throw ex;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * イベントリスナ\r
+ */\r
+ private final EventListenerList listeners = new EventListenerList();\r
+\r
+ /**\r
+ * このダイアログのデータモデル\r
+ */\r
+ private LayerOrderCustomizeDialogModel model;\r
+\r
+ /**\r
+ * 変更通知リスナを登録する\r
+ * @param l リスナ\r
+ */\r
+ public void addLayerOrderCustomizeListener(LayerOrderCustomizeListener l) {\r
+ listeners.add(LayerOrderCustomizeListener.class, l);\r
+ }\r
+\r
+ /**\r
+ * 変更通知リスナを登録解除する\r
+ * @param l リスナ\r
+ */\r
+ public void removeLayerOrderCustomizeListener(LayerOrderCustomizeListener l) {\r
+ listeners.remove(LayerOrderCustomizeListener.class, l);\r
+ }\r
+\r
+ /**\r
+ * 全ての変更通知リスナに対して変更イベントを通知する。\r
+ * @param type 変更タイプ\r
+ * @param name 名前\r
+ */\r
+ protected void fireEvent() {\r
+ // Guaranteed to return a non-null array\r
+ Object[] ll = listeners.getListenerList();\r
+ // Process the listeners last to first, notifying\r
+ // those that are interested in this event\r
+ // ※ 逆順で通知するのがSwingの作法らしい。\r
+ LayerOrderCustomizeListener.Change event = null;\r
+ for (int i = ll.length - 2; i >= 0; i -= 2) {\r
+ if (ll[i] == LayerOrderCustomizeListener.class) {\r
+ // Lazily create the event:\r
+ if (event == null) {\r
+ event = new LayerOrderCustomizeListener.Change(this);\r
+ }\r
+ ((LayerOrderCustomizeListener) ll[i + 1]).onChange(event);\r
+ }\r
+ }\r
+ }\r
+\r
+ public LayerOrderCustomizeDialogModel getModel() {\r
+ return model;\r
+ }\r
+\r
+ /**\r
+ * LayerOrderCustomizeDialogModelの更新リスナ\r
+ */\r
+ private final LayerOrderCustomizeDialogModel.ChangeListener modelChangeListener =\r
+ new LayerOrderCustomizeDialogModel.ChangeListener() {\r
+ @Override\r
+ public void onChange(Change change) {\r
+ String name = change.getName();\r
+ if (name == null || name.isEmpty()) {\r
+ // ※空文字の場合はcurrentプロパティの変更\r
+ return;\r
+ }\r
+\r
+ switch (change.getChangeType()) {\r
+ case ADD:\r
+ // レイヤーパターンが追加された場合\r
+ if (patternsModel.indexOf(name) < 0) {\r
+ patternsModel.add(name);\r
+ patternsModel.setSelectedItem(name); // 登録された名前で選択しなおす為\r
+ }\r
+ break;\r
+\r
+ case MODIFY:\r
+ // レイヤーパターンが更新された場合\r
+ break;\r
+\r
+ case REMOVE:\r
+ // レイヤーパターンが削除された場合\r
+ if (name != null) {\r
+ patternsModel.remove(name);\r
+ // 削除時は現在の選択もクリアする。(現在選択のものを削除しているので)\r
+ patternsModel.setSelectedItem(null);\r
+ }\r
+ break;\r
+ }\r
+ }\r
+ };\r
+\r
+ public void setModel(LayerOrderCustomizeDialogModel model) {\r
+ LayerOrderCustomizeDialogModel old = this.model;\r
+ if (old != null) {\r
+ old.removeListChangeListener(modelChangeListener);\r
+ }\r
+\r
+ this.model = model;\r
+ loadModel();\r
+\r
+ if (model != null) {\r
+ model.addListChangeListener(modelChangeListener);\r
+ }\r
+\r
+ if (old == null ? model != null : !old.equals(model)) {\r
+ firePropertyChange("model", old, model);\r
+ }\r
+ }\r
+\r
+ private void loadModel() {\r
+ patternsModel.clear();\r
+ if (model == null) {\r
+ return;\r
+ }\r
+\r
+ patternsModel.addAll(model.getPatternNames());\r
+ patternsModel.setSelectedItem(null);\r
+\r
+ dataModel.setList(model.getCurrentList());\r
+ lastPatternName = null;\r
+ }\r
+\r
+ private JComboBox cmbPatternName = new JComboBox();\r
+\r
+ private SimpleComboBoxModel<String> patternsModel = new SimpleComboBoxModel<String>();\r
+\r
+ private final JTable tblLayerOrder = new JTable();\r
+\r
+ private final LayerOrderTableModel dataModel = new LayerOrderTableModel();\r
+\r
+ private String lastPatternName;\r
+\r
+ private AbstractAction actRemove;\r
+\r
+ private AbstractAction actSave;\r
+\r
+ private void initLayout() {\r
+ final Properties strings = LocalizedResourcePropertyLoader\r
+ .getCachedInstance().getLocalizedProperties(STRINGS_RESOURCE);\r
+\r
+ setTitle(strings.getProperty("title"));\r
+ Container container = getContentPane();\r
+ container.setLayout(new BorderLayout());\r
+\r
+ JPanel selectorPanel = new JPanel(new BorderLayout(3, 3));\r
+ selectorPanel.add(new JLabel(strings.getProperty("patternName")), BorderLayout.WEST);\r
+\r
+ cmbPatternName.setEditable(true);\r
+ selectorPanel.add(cmbPatternName, BorderLayout.CENTER);\r
+\r
+ cmbPatternName.setModel(patternsModel);\r
+ cmbPatternName.setSelectedItem(null);\r
+\r
+ cmbPatternName.addActionListener(new AbstractAction() {\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ @Override\r
+ public void actionPerformed(ActionEvent e) {\r
+ String name = (String) cmbPatternName.getSelectedItem();\r
+ if (patternsModel.contains(name)) {\r
+ onSelect(name);\r
+ }\r
+ }\r
+ });\r
+\r
+ Box selectorBtns = Box.createHorizontalBox();\r
+\r
+ actSave = new AbstractAction(strings.getProperty("btnSave")) {\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ @Override\r
+ public void actionPerformed(ActionEvent e) {\r
+ String name = (String) cmbPatternName.getSelectedItem();\r
+ if (name != null && name.trim().length() > 0) {\r
+ onSave(name.trim());\r
+\r
+ // 現在の入力でパターン名が増えたので、\r
+ // このパターンを削除可能にするためUI更新を呼び出す\r
+ onChangeComboText();\r
+ }\r
+ }\r
+ };\r
+ actRemove = new AbstractAction(strings.getProperty("btnRemove")) {\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ @Override\r
+ public void actionPerformed(ActionEvent e) {\r
+ String name = (String) cmbPatternName.getSelectedItem();\r
+ onRemove(name);\r
+ }\r
+ };\r
+\r
+ // コンボボックスのテキストが既存のパターン名と合致するかによって\r
+ // ADD/REMOVEボタンを制御するためのリスナ\r
+ final BasicComboBoxEditor cmbEditor = (BasicComboBoxEditor) cmbPatternName.getEditor();\r
+ JTextField cmbEditorField = (JTextField) cmbEditor.getEditorComponent();\r
+ cmbEditorField.getDocument().addDocumentListener(new DocumentListener() {\r
+ @Override\r
+ public void removeUpdate(DocumentEvent e) {\r
+ changed(e);\r
+ }\r
+\r
+ @Override\r
+ public void insertUpdate(DocumentEvent e) {\r
+ changed(e);\r
+ }\r
+\r
+ @Override\r
+ public void changedUpdate(DocumentEvent e) {\r
+ changed(e);\r
+ }\r
+\r
+ protected void changed(DocumentEvent e) {\r
+ onChangeComboText();\r
+ }\r
+ });\r
+\r
+ selectorBtns.add(new JButton(actSave));\r
+ selectorBtns.add(new JButton(actRemove));\r
+\r
+ selectorPanel.add(selectorBtns, BorderLayout.EAST);\r
+ selectorPanel.setBorder(BorderFactory.createTitledBorder(\r
+ strings.getProperty("pattern.groupTitle")));\r
+\r
+ container.add(selectorPanel, BorderLayout.NORTH);\r
+\r
+ // セルの編集はフォーカスの移動でコミットする\r
+ tblLayerOrder.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);\r
+ tblLayerOrder.setModel(dataModel);\r
+\r
+ dataModel.addTableModelListener(new TableModelListener() {\r
+ @Override\r
+ public void tableChanged(TableModelEvent e) {\r
+ fireEvent();\r
+ }});\r
+\r
+ JPanel tablePanel = new JPanel(new BorderLayout(3, 3));\r
+ tablePanel.setBorder(BorderFactory.createTitledBorder(\r
+ strings.getProperty("edittable.groupTitle")));\r
+ JScrollPane scr = new JScrollPane(tblLayerOrder);\r
+ scr.setPreferredSize(new Dimension(450, 250));\r
+ tablePanel.add(scr);\r
+ tblLayerOrder.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);\r
+ tblLayerOrder.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);\r
+\r
+ dataModel.adjustColumnModel(tblLayerOrder.getColumnModel());\r
+\r
+ AbstractAction actAddLayer = new AbstractAction(strings.getProperty("btnAdd")) {\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ @Override\r
+ public void actionPerformed(ActionEvent e) {\r
+ onAddLayer();\r
+ }\r
+ };\r
+ AbstractAction actDeleteLayer = new AbstractAction(strings.getProperty("btnDelete")) {\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ @Override\r
+ public void actionPerformed(ActionEvent e) {\r
+ onDeleteLayer();\r
+ }\r
+ };\r
+\r
+ JButton btnOK = new JButton(actAddLayer);\r
+ JButton btnCancel = new JButton(actDeleteLayer);\r
+\r
+ JPanel layerOpeBtns = new JPanel(new GridBagLayout());\r
+ GridBagConstraints gbc = new GridBagConstraints();\r
+ gbc.gridx = 0;\r
+ gbc.gridy = 0;\r
+ gbc.weightx = 1;\r
+ gbc.fill = GridBagConstraints.BOTH;\r
+ layerOpeBtns.add(btnOK, gbc);\r
+ gbc.gridx = 0;\r
+ gbc.gridy = 1;\r
+ layerOpeBtns.add(btnCancel, gbc);\r
+\r
+ gbc.gridx = 0;\r
+ gbc.gridy = 2;\r
+ gbc.weighty = 1;\r
+ layerOpeBtns.add(Box.createGlue(), gbc);\r
+\r
+ tablePanel.add(layerOpeBtns, BorderLayout.EAST);\r
+\r
+ container.add(tablePanel, BorderLayout.CENTER);\r
+\r
+ AbstractAction actClose = new AbstractAction(strings.getProperty("btnClose")) {\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ @Override\r
+ public void actionPerformed(ActionEvent e) {\r
+ onClosing();\r
+ }\r
+ };\r
+ JButton btnClose = new JButton(actClose);\r
+\r
+ Box btns = Box.createHorizontalBox();\r
+ btns.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 42));\r
+ btns.add(Box.createHorizontalGlue());\r
+ btns.add(btnClose);\r
+ container.add(btns, BorderLayout.SOUTH);\r
+\r
+ Toolkit tk = Toolkit.getDefaultToolkit();\r
+ JRootPane rootPane = getRootPane();\r
+ rootPane.setDefaultButton(btnOK);\r
+\r
+ InputMap im = rootPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);\r
+ ActionMap am = rootPane.getActionMap();\r
+ im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "closeLayerOrderCustomizeDialog");\r
+ im.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, tk.getMenuShortcutKeyMask()), "closeLayerOrderCustomizeDialog");\r
+ am.put("closeLayerOrderCustomizeDialog", actClose);\r
+\r
+ setModel(new LayerOrderCustomizeDialogModel());\r
+\r
+ // コンボボックスの状態からボタンのUI状態を更新する。\r
+ onChangeComboText();\r
+\r
+ pack();\r
+ }\r
+\r
+ /**\r
+ * パターン名コンボボックスのテキストが入力された場合に\r
+ * ADD/REMOVEボタンのUI状態を更新するためのハンドラ\r
+ */\r
+ protected void onChangeComboText() {\r
+ BasicComboBoxEditor cmbEditor = (BasicComboBoxEditor) cmbPatternName.getEditor();\r
+ String inpName = (String) cmbEditor.getItem();\r
+ if (inpName == null || inpName.trim().length() == 0) {\r
+ // 空の場合\r
+ actRemove.setEnabled(false);\r
+ actSave.setEnabled(false);\r
+ } else {\r
+ actRemove.setEnabled(patternsModel.contains(inpName)); // 既存のパターン名と合致すれば削除可\r
+ actSave.setEnabled(true); // 文字が入力されていれば登録・更新可\r
+ }\r
+ }\r
+\r
+ /**\r
+ * カラム定義\r
+ */\r
+ private enum ColumnDef {\r
+ CATEGORY("column.category", String.class, 120) {\r
+ @Override\r
+ public Object getValue(CustomLayerOrder item) {\r
+ return item.getCategory().getLocalizedCategoryName();\r
+ }\r
+ },\r
+ LAYER("column.layer", String.class, 120) {\r
+ @Override\r
+ public Object getValue(CustomLayerOrder item) {\r
+ return item.getLayer().getLocalizedName();\r
+ }\r
+ },\r
+ DEFAULT_ORDER("column.defaultOrder", Integer.class, 80) {\r
+ @Override\r
+ public Object getValue(CustomLayerOrder item) {\r
+ return item.getLayer().getOrder();\r
+ }\r
+ },\r
+ CUSTOM_ORDER("column.order", Integer.class, 80, true) {\r
+ @Override\r
+ public Object getValue(CustomLayerOrder item) {\r
+ return item.getLayerOrder();\r
+ }\r
+ };\r
+\r
+ private final String resourceKey;\r
+\r
+ private final Class<?> dataType;\r
+\r
+ private final int prefWidth;\r
+\r
+ private final boolean editable;\r
+\r
+ ColumnDef(String resourceKey, Class<?> dataType, int prefWidth) {\r
+ this(resourceKey, dataType, prefWidth, false);\r
+ }\r
+\r
+ ColumnDef(String resourceKey, Class<?> dataType, int prefWidth, boolean editable) {\r
+ this.resourceKey = resourceKey;\r
+ this.dataType = dataType;\r
+ this.prefWidth = prefWidth;\r
+ this.editable = editable;\r
+ }\r
+\r
+ public String getResourceKey() {\r
+ return resourceKey;\r
+ }\r
+\r
+ public int getPrefWidth() {\r
+ return prefWidth;\r
+ }\r
+\r
+ public abstract Object getValue(CustomLayerOrder item);\r
+\r
+ public Class<?> getDataType() {\r
+ return dataType;\r
+ }\r
+\r
+ public boolean isEditable() {\r
+ return editable;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * レイヤー編集テーブルのテーブルモデル\r
+ */\r
+ protected static class LayerOrderTableModel extends AbstractTableModel {\r
+\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ private final Properties strings = LocalizedResourcePropertyLoader\r
+ .getCachedInstance().getLocalizedProperties(STRINGS_RESOURCE);\r
+\r
+ /**\r
+ * カラム定義リスト\r
+ */\r
+ private static final ColumnDef[] columns = ColumnDef.values();\r
+\r
+ /**\r
+ * テーブルに設定されているレイヤーリスト。\r
+ * このリストでデータの追加更新削除を行い、変更リスナによってUI更新を行わせる。\r
+ */\r
+ private ObservableList<CustomLayerOrder> layerOrderList;\r
+\r
+ /**\r
+ * テーブルに設定されているレイヤーリストが変更された場合に\r
+ * テーブルモデルが更新されたことをUIに通知するための接続用リスナ。\r
+ */\r
+ private final ListChangeListener<CustomLayerOrder> changeListener = new ListChangeListener<CustomLayerOrder>() {\r
+ @Override\r
+ public void onChanged(Change<? extends CustomLayerOrder> c) {\r
+ int idx = c.getIndex();\r
+ if (idx >= 0) {\r
+ switch (c.getType()) {\r
+ case ADD:\r
+ fireTableRowsInserted(idx, idx);\r
+ return;\r
+\r
+ case MODIFIY:\r
+ fireTableRowsUpdated(idx, idx);\r
+ return;\r
+\r
+ case REMOVE:\r
+ fireTableRowsDeleted(idx, idx);\r
+ return;\r
+\r
+ default:\r
+ break;\r
+ }\r
+ }\r
+ fireTableDataChanged();\r
+ }\r
+ };\r
+\r
+ /**\r
+ * コンストラクタ\r
+ */\r
+ public LayerOrderTableModel() {\r
+ setList(new ObservableList<CustomLayerOrder>());\r
+ }\r
+\r
+ /**\r
+ * カラム幅を設定する。\r
+ * @param columnModel\r
+ */\r
+ public void adjustColumnModel(TableColumnModel columnModel) {\r
+ for (int idx = 0; idx < columns.length; idx++) {\r
+ columnModel.getColumn(idx).setPreferredWidth(\r
+ columns[idx].getPrefWidth());\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 現在編集中のテーブル上のレイヤーリストを差し替える。\r
+ * レイヤーリストはcategory, layerの順序となるようにソートされる。\r
+ * @param layerOrderList\r
+ */\r
+ public final void setList(ObservableList<CustomLayerOrder> layerOrderList) {\r
+ if (this.layerOrderList != null) {\r
+ this.layerOrderList.removeListChangeListener(changeListener);\r
+ }\r
+\r
+ // テーブルには既定の順序で並べる\r
+ Collections.sort(layerOrderList, new Comparator<CustomLayerOrder>() {\r
+ @Override\r
+ public int compare(CustomLayerOrder o1, CustomLayerOrder o2) {\r
+ int ret = o1.getCategory().compareTo(o2.getCategory());\r
+ if (ret == 0) {\r
+ ret = o1.getLayer().compareTo(o2.getLayer());\r
+ }\r
+ if (ret == 0) {\r
+ ret = o1.getLayerOrder() - o2.getLayerOrder();\r
+ }\r
+ return ret;\r
+ }\r
+ });\r
+\r
+ this.layerOrderList = layerOrderList;\r
+ layerOrderList.addListChangeListener(changeListener);\r
+\r
+ fireTableDataChanged();\r
+ }\r
+\r
+ /**\r
+ * 現在編集中のテーブル上のレイヤーリストを取得する。\r
+ * @return\r
+ */\r
+ public final List<CustomLayerOrder> getList() {\r
+ return layerOrderList;\r
+ }\r
+\r
+ /**\r
+ * レイヤーを追加する。\r
+ * レイヤーは既存のレイヤーのリストに対してcategory, layerの順序となるように挿入される。\r
+ * @param category\r
+ * @param layer\r
+ * @param layerOrder\r
+ */\r
+ public void add(PartsCategory category, Layer layer, int layerOrder) {\r
+ if (category == null || layer == null) {\r
+ throw new NullPointerException("category, layerにnullは指定できません。");\r
+ }\r
+\r
+ CustomLayerOrder item = null;\r
+ int idx = 0;\r
+ for (; idx < layerOrderList.size(); idx++) {\r
+ CustomLayerOrder selItem = layerOrderList.get(idx);\r
+\r
+ PartsCategory selCategory = selItem.getCategory();\r
+ Layer selLayer = selItem.getLayer();\r
+\r
+ int ret = category.compareTo(selCategory);\r
+ if (ret == 0) {\r
+ ret = layer.compareTo(selLayer);\r
+ }\r
+\r
+ if (ret == 0) {\r
+ // 一致するアイテムが発見された。\r
+ item = selItem;\r
+ break;\r
+\r
+ } else if (ret < 0) {\r
+ // 自分より大きなアイテムを発見\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (item == null) {\r
+ item = new CustomLayerOrder();\r
+ item.setCategory(category);\r
+ item.setLayer(layer);\r
+ item.setLayerOrder(layerOrder);\r
+ layerOrderList.add(idx, item);\r
+\r
+ } else {\r
+ item.setLayerOrder(layerOrder);\r
+ }\r
+ }\r
+\r
+ public void remove(int rowIndex) {\r
+ layerOrderList.remove(rowIndex);\r
+ }\r
+\r
+ @Override\r
+ public int getRowCount() {\r
+ return layerOrderList.size();\r
+ }\r
+\r
+ @Override\r
+ public int getColumnCount() {\r
+ return columns.length;\r
+ }\r
+\r
+ @Override\r
+ public String getColumnName(int column) {\r
+ return strings.getProperty(columns[column].getResourceKey());\r
+ }\r
+\r
+ @Override\r
+ public Class<?> getColumnClass(int columnIndex) {\r
+ return columns[columnIndex].getDataType();\r
+ }\r
+\r
+ @Override\r
+ public Object getValueAt(int rowIndex, int columnIndex) {\r
+ CustomLayerOrder item = layerOrderList.get(rowIndex);\r
+ return columns[columnIndex].getValue(item);\r
+ }\r
+\r
+ @Override\r
+ public void setValueAt(Object aValue, int rowIndex, int columnIndex) {\r
+ if (columns[columnIndex].isEditable()) {\r
+ CustomLayerOrder item = layerOrderList.get(rowIndex);\r
+ item.setLayerOrder(((Number) aValue).intValue());\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public boolean isCellEditable(int rowIndex, int columnIndex) {\r
+ return columns[columnIndex].isEditable();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * ダイアログを閉じる\r
+ */\r
+ protected void onClosing() {\r
+ if (canDiscardChanges()) {\r
+ dispose();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 現在編集中のカスタムレイヤーパターンを取得する。\r
+ * @return\r
+ */\r
+ public List<CustomLayerOrder> getEdittingCustomLayerOrderList() {\r
+ return dataModel.getList();\r
+ }\r
+\r
+ /**\r
+ * 現在の編集を破棄してよいか確認する。\r
+ * パターンをロードしている場合は変更があれば保存するか問い合わせる。\r
+ * パターンをロードしていない場合は破棄してよいか問い合わせる。\r
+ * @return 破棄して良い場合(もしくは保存した場合)\r
+ */\r
+ private boolean canDiscardChanges() {\r
+ final Properties strings = LocalizedResourcePropertyLoader\r
+ .getCachedInstance().getLocalizedProperties(STRINGS_RESOURCE);\r
+\r
+ if (lastPatternName == null || lastPatternName.isEmpty()) {\r
+ // パターンをロードしていない場合\r
+ ObservableList<CustomLayerOrder> oldLayerOrderList = model.getCurrentList();\r
+ if (!oldLayerOrderList.equals(dataModel.getList())) {\r
+ int ret = JOptionPane.showConfirmDialog(this,\r
+ strings.getProperty("confirm.discardChanges.message"),\r
+ strings.getProperty("confirm.discardChanges.title"),\r
+ JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);\r
+ if (ret != JOptionPane.YES_OPTION) {\r
+ // コンボボックスの選択を空にして戻す\r
+ patternsModel.setSelectedItem(null);\r
+ return false; // 破棄不可\r
+ }\r
+ }\r
+ } else {\r
+ // すでにパターンをロードしており、且つ、レイヤーリストを修正している場合\r
+ // 新たに選択したパターンのロード前に、現在の編集を保存するか確認する。\r
+ ObservableList<CustomLayerOrder> oldLayerOrderList = model.getCopy(lastPatternName);\r
+ if (oldLayerOrderList == null) {\r
+ oldLayerOrderList = model.createObservableList();\r
+ }\r
+ if (!oldLayerOrderList.equals(dataModel.getList())) {\r
+ // パターンを編集中の場合、保存するか問い合わせる\r
+ int ret = JOptionPane.showConfirmDialog(this,\r
+ strings.getProperty("confirm.savechanges.message") + lastPatternName,\r
+ strings.getProperty("confirm.savechanges.title"),\r
+ JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);\r
+ if (ret == JOptionPane.YES_OPTION) {\r
+ onSave(lastPatternName); // 現在の編集を保存する\r
+ }\r
+ }\r
+ }\r
+ return true; // 現在の編集は破棄して良い\r
+ }\r
+\r
+ /**\r
+ * レイヤーパターン名を選択した場合\r
+ * @param name\r
+ */\r
+ protected void onSelect(String name) {\r
+ if (canDiscardChanges()) {\r
+ // 選択したパターンのロード\r
+ ObservableList<CustomLayerOrder> layerOrderList = model.getCopy(name);\r
+ if (layerOrderList == null) {\r
+ layerOrderList = model.createObservableList();\r
+ }\r
+\r
+ dataModel.setList(layerOrderList);\r
+ String old = this.lastPatternName;\r
+ this.lastPatternName = name;\r
+\r
+ firePropertyChange("lastPatternName", old, name);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 最後に選択したパターン名を取得する\r
+ * @return\r
+ */\r
+ public String getLastPatternName() {\r
+ return lastPatternName;\r
+ }\r
+\r
+ /**\r
+ * 現在の編集をパターン名をつけて保存し、現在の選択パターン名とする。\r
+ * @param name\r
+ */\r
+ protected void onSave(String name) {\r
+ model.put(name, dataModel.getList());\r
+\r
+ String old = this.lastPatternName;\r
+ if (old == null ? name != null : !old.equals(name)) {\r
+ this.lastPatternName = name;\r
+ firePropertyChange("lastPatternName", old, name);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 指定したパターン名を削除して、選択状態を解除する。\r
+ * @param name\r
+ */\r
+ protected void onRemove(String name) {\r
+ final Properties strings = LocalizedResourcePropertyLoader\r
+ .getCachedInstance().getLocalizedProperties(STRINGS_RESOURCE);\r
+\r
+ int ret = JOptionPane.showConfirmDialog(this,\r
+ strings.getProperty("confirm.removepattern.message") + lastPatternName,\r
+ strings.getProperty("confirm.removepattern.title"),\r
+ JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);\r
+ if (ret != JOptionPane.YES_OPTION) {\r
+ return;\r
+ }\r
+\r
+ model.remove(name);\r
+\r
+ String old = this.lastPatternName;\r
+ if (old != null) {\r
+ this.lastPatternName = null;\r
+ firePropertyChange("lastPatternName", old, null);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * レイヤーの追加ダイアログのデータモデル\r
+ */\r
+ public static class AddLayerDialogModel {\r
+\r
+ public static final String PARTS_CATEGORIES = "partsCategories";\r
+\r
+ public static final String RESULT = "result";\r
+\r
+ public static final String SELECTED_PARTS_CATEGORY = "partsCategory";\r
+\r
+ public static final String SELECTED_LAYER = "layer";\r
+\r
+ public static final String DEFAULT_ORDER = "defaultOrder";\r
+\r
+ public static final String ORDER = "order";\r
+\r
+ private final PropertyChangeSupport propChangeSupport = new PropertyChangeSupport(this);\r
+\r
+ /**\r
+ * コンボボックスの候補とするカテゴリのリスト\r
+ */\r
+ private List<PartsCategory> partsCategories = new ArrayList<PartsCategory>();\r
+\r
+ /**\r
+ * ダイアログを閉じたときのOK/CANCEL状態\r
+ */\r
+ private boolean result;\r
+\r
+ /**\r
+ * 選択されているカテゴリ、なければnull\r
+ */\r
+ private PartsCategory selectedPartsCategory;\r
+\r
+ /**\r
+ * 選択されているレイヤー、なければnull\r
+ */\r
+ private Layer selectedLayer;\r
+\r
+ /**\r
+ * デフォルト順序\r
+ */\r
+ private int defaultOrder;\r
+\r
+ /**\r
+ * 順序\r
+ */\r
+ private int order;\r
+\r
+ /**\r
+ * 入力確定可能であるか?\r
+ * @return\r
+ */\r
+ public boolean isValid() {\r
+ return selectedPartsCategory != null && selectedLayer != null;\r
+ }\r
+\r
+ public List<PartsCategory> getPartsCategories() {\r
+ return partsCategories;\r
+ }\r
+\r
+ public void setPartsCategories(List<PartsCategory> partsCategories) {\r
+ List<PartsCategory> old = this.partsCategories;\r
+ this.partsCategories = partsCategories;\r
+ propChangeSupport.firePropertyChange(PARTS_CATEGORIES, old, partsCategories);\r
+ }\r
+\r
+ public void setResult(boolean result) {\r
+ boolean old = this.result;\r
+ this.result = result;\r
+ propChangeSupport.firePropertyChange(RESULT, old, result);\r
+ }\r
+\r
+ public boolean isResult() {\r
+ return result;\r
+ }\r
+\r
+ public void setSelectedPartsCategory(PartsCategory selectedPartsCategory) {\r
+ PartsCategory old = this.selectedPartsCategory;\r
+ if (!(old == null && selectedPartsCategory == null) || (old != null && !old.equals(selectedPartsCategory))) {\r
+ this.selectedPartsCategory = selectedPartsCategory;\r
+ propChangeSupport.firePropertyChange(SELECTED_PARTS_CATEGORY, old, selectedPartsCategory);\r
+ }\r
+ }\r
+\r
+ public PartsCategory getSelectedPartsCategory() {\r
+ return selectedPartsCategory;\r
+ }\r
+\r
+ public void setSelectedLayer(Layer selectedLayer) {\r
+ Layer old = this.selectedLayer;\r
+ if (!(old == null && selectedLayer == null) || (old != null && !old.equals(selectedLayer))) {\r
+ this.selectedLayer = selectedLayer;\r
+ propChangeSupport.firePropertyChange(SELECTED_LAYER, old, selectedLayer);\r
+ }\r
+ }\r
+\r
+ public Layer getSelectedLayer() {\r
+ return selectedLayer;\r
+ }\r
+\r
+ public void setOrder(int order) {\r
+ int old = this.order;\r
+ if (old != order) {\r
+ this.order = order;\r
+ propChangeSupport.firePropertyChange(ORDER, old, order);\r
+ }\r
+ }\r
+\r
+ public int getOrder() {\r
+ return order;\r
+ }\r
+\r
+ public void setDefaultOrder(int defaultOrder) {\r
+ int old = this.defaultOrder;\r
+ this.defaultOrder = defaultOrder;\r
+ if (old != defaultOrder) {\r
+ this.defaultOrder = defaultOrder;\r
+ propChangeSupport.firePropertyChange(DEFAULT_ORDER, old, defaultOrder);\r
+ }\r
+ }\r
+\r
+ public int getDefaultOrder() {\r
+ return defaultOrder;\r
+ }\r
+\r
+ public void addPropertyChangeListener(PropertyChangeListener listener) {\r
+ propChangeSupport.addPropertyChangeListener(listener);\r
+ }\r
+\r
+ public void removePropertyChangeListener(PropertyChangeListener listener) {\r
+ propChangeSupport.removePropertyChangeListener(listener);\r
+ }\r
+\r
+ public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {\r
+ propChangeSupport.addPropertyChangeListener(propertyName, listener);\r
+ }\r
+\r
+ public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {\r
+ propChangeSupport.removePropertyChangeListener(propertyName, listener);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * レイヤーの追加ダイアログ\r
+ */\r
+ public static class AddLayerDialog extends JDialog {\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ public static final String MODEL = "model";\r
+\r
+ public AddLayerDialog(JDialog parent, boolean modal) {\r
+ super(parent, modal);\r
+ try {\r
+ initLayout();\r
+\r
+ } catch (RuntimeException ex) {\r
+ dispose();\r
+ throw ex;\r
+ }\r
+ }\r
+\r
+ private SimpleComboBoxModel<Layer> cmbLayerModel = new SimpleComboBoxModel<Layer>();\r
+\r
+ private SimpleComboBoxModel<PartsCategory> cmbCategoryModel = new SimpleComboBoxModel<PartsCategory>();\r
+\r
+ private JTextField txtDefaultOrder = new JTextField();\r
+\r
+ private JTextField txtOrder = new JTextField();\r
+\r
+ private AbstractAction actOK;\r
+\r
+ private void initLayout() {\r
+ final Properties strings = LocalizedResourcePropertyLoader\r
+ .getCachedInstance().getLocalizedProperties(STRINGS_RESOURCE);\r
+\r
+ setTitle(strings.getProperty("addLayerDialog.title"));\r
+\r
+ Container container = getContentPane();\r
+ container.setLayout(new BorderLayout());\r
+\r
+ JPanel editPanel = new JPanel(new GridBagLayout());\r
+ GridBagConstraints gbc = new GridBagConstraints();\r
+ gbc.insets = new Insets(3, 3, 3, 3);\r
+\r
+ JComboBox cmbCategory = new JComboBox(cmbCategoryModel);\r
+\r
+ JComboBox cmbLayer = new JComboBox(cmbLayerModel);\r
+ cmbLayer.setRenderer(new ListCellRenderer() {\r
+\r
+ private JLabel label = new JLabel();\r
+\r
+ @Override\r
+ public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,\r
+ boolean cellHasFocus) {\r
+ Layer selectedLayer = (Layer) value;\r
+\r
+ // 背景色透過制御\r
+ label.setOpaque(isSelected && index >= 0);\r
+\r
+ if (isSelected) {\r
+ label.setBackground(list.getSelectionBackground());\r
+ label.setForeground(list.getSelectionForeground());\r
+ } else {\r
+ label.setBackground(list.getBackground());\r
+ label.setForeground(list.getForeground());\r
+ }\r
+\r
+ if (selectedLayer == null) {\r
+ label.setText("");\r
+ } else {\r
+ label.setFont(list.getFont());\r
+ label.setText(selectedLayer.getLocalizedName());\r
+ }\r
+ return label;\r
+ }\r
+ });\r
+\r
+ gbc.gridx = 0;\r
+ gbc.gridy = 0;\r
+ editPanel.add(new JLabel(strings.getProperty("column.category")), gbc);\r
+\r
+ gbc.gridx = 0;\r
+ gbc.gridy = 1;\r
+ editPanel.add(new JLabel(strings.getProperty("column.layer")), gbc);\r
+\r
+ gbc.gridx = 0;\r
+ gbc.gridy = 2;\r
+ editPanel.add(new JLabel(strings.getProperty("column.defaultOrder")), gbc);\r
+\r
+ gbc.gridx = 0;\r
+ gbc.gridy = 3;\r
+ editPanel.add(new JLabel(strings.getProperty("column.order")), gbc);\r
+\r
+ gbc.gridx = 1;\r
+ gbc.gridy = 0;\r
+ gbc.weightx = 1;\r
+ gbc.fill = GridBagConstraints.BOTH;\r
+ editPanel.add(cmbCategory, gbc);\r
+\r
+ gbc.gridx = 1;\r
+ gbc.gridy = 1;\r
+ editPanel.add(cmbLayer, gbc);\r
+\r
+ gbc.gridx = 1;\r
+ gbc.gridy = 2;\r
+ editPanel.add(txtDefaultOrder, gbc);\r
+ txtDefaultOrder.setEditable(false);\r
+\r
+ gbc.gridx = 1;\r
+ gbc.gridy = 3;\r
+ editPanel.add(txtOrder, gbc);\r
+\r
+ gbc.gridx = 0;\r
+ gbc.gridy = 4;\r
+ gbc.weighty = 1;\r
+ editPanel.add(Box.createGlue(), gbc);\r
+\r
+ container.add(editPanel, BorderLayout.CENTER);\r
+\r
+ actOK = new AbstractAction(strings.getProperty("btnOK")) {\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ @Override\r
+ public void actionPerformed(ActionEvent e) {\r
+ onOK();\r
+ }\r
+ };\r
+\r
+ AbstractAction actCancel = new AbstractAction(strings.getProperty("btnCancel")) {\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ @Override\r
+ public void actionPerformed(ActionEvent e) {\r
+ onCancel();\r
+ }\r
+ };\r
+\r
+ JButton btnOK = new JButton(actOK);\r
+ JButton btnCancel = new JButton(actCancel);\r
+\r
+ Box btnPanel = Box.createHorizontalBox();\r
+ btnPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 42));\r
+ btnPanel.add(Box.createHorizontalGlue());\r
+\r
+ if (Main.isLinuxOrMacOSX()) {\r
+ btnPanel.add(btnCancel);\r
+ btnPanel.add(btnOK);\r
+ } else {\r
+ btnPanel.add(btnOK);\r
+ btnPanel.add(btnCancel);\r
+ }\r
+\r
+ container.add(btnPanel, BorderLayout.SOUTH);\r
+\r
+ // カタログの選択が変更された場合、初期化中のイベントでなければハンドラに渡す\r
+ cmbCategory.addActionListener(new ActionListener() {\r
+ @Override\r
+ public void actionPerformed(ActionEvent e) {\r
+ if (updaingCmbCategoryModel.get() == 0) {\r
+ onChangeSelectCategory((PartsCategory) cmbCategoryModel.getSelectedItem());\r
+ }\r
+ }\r
+ });\r
+\r
+ // レイヤーの選択が変更された場合、初期渦中のイベントでなければハンドラに渡す\r
+ cmbLayer.addActionListener(new ActionListener() {\r
+ @Override\r
+ public void actionPerformed(ActionEvent e) {\r
+ if (updaingCmbLayerModel.get() == 0) {\r
+ onChangeSelectLayer((Layer) cmbLayerModel.getSelectedItem());\r
+ }\r
+ }\r
+ });\r
+\r
+ // レイヤー順序テキストボックスの入力イベントが初期化中のイベントでなければ\r
+ // UIモデルの更新のハンドラに引き渡す。\r
+ txtOrder.getDocument().addDocumentListener(new DocumentListener() {\r
+ @Override\r
+ public void removeUpdate(DocumentEvent e) {\r
+ onChange(e);\r
+ }\r
+\r
+ @Override\r
+ public void insertUpdate(DocumentEvent e) {\r
+ onChange(e);\r
+ }\r
+\r
+ @Override\r
+ public void changedUpdate(DocumentEvent e) {\r
+ onChange(e);\r
+ }\r
+\r
+ protected void onChange(DocumentEvent e) {\r
+ if (updaingTxtOrderModel.get() == 0) {\r
+ onChangeTxtOrder(txtOrder.getText());\r
+ }\r
+ }\r
+ });\r
+\r
+ // AddLayerDialogModelのプロパティ変更を監視し、対応するUIを更新するためのリスナ\r
+ modelPropChangeListener = new PropertyChangeListener() {\r
+ @Override\r
+ public void propertyChange(PropertyChangeEvent evt) {\r
+ String propName = evt.getPropertyName();\r
+ if (AddLayerDialogModel.SELECTED_PARTS_CATEGORY.equals(propName)) {\r
+ updateCmbCategory();\r
+ } else if (AddLayerDialogModel.SELECTED_LAYER.equals(propName)) {\r
+ updateCmbLayer();\r
+ } else if (AddLayerDialogModel.DEFAULT_ORDER.equals(propName)) {\r
+ updateTxtDefaultOrder();\r
+ } else if (AddLayerDialogModel.ORDER.equals(propName)) {\r
+ updateTxtOrder();\r
+ }\r
+\r
+ // モデルの状態をOKボタンの状態に反映する\r
+ updateButtonState();\r
+ }\r
+ };\r
+\r
+ // デフォルトキーの設定\r
+ Toolkit tk = Toolkit.getDefaultToolkit();\r
+ JRootPane rootPane = getRootPane();\r
+ rootPane.setDefaultButton(btnOK);\r
+\r
+ InputMap im = rootPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);\r
+ ActionMap am = rootPane.getActionMap();\r
+ im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "closeAddLayerDialog");\r
+ im.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, tk.getMenuShortcutKeyMask()), "closeAddLayerDialog");\r
+ am.put("closeAddLayerDialog", actCancel);\r
+\r
+ // コンボボックスの推奨幅を明示的に指定する。(言語によっては幅が小さくなりすぎるため)\r
+ Dimension prefSize = cmbCategory.getPreferredSize();\r
+ prefSize.width = 200;\r
+ cmbCategory.setPreferredSize(prefSize);\r
+ pack();\r
+\r
+ // 初期化モデルの設定(dummy)\r
+ setModel(new AddLayerDialogModel());\r
+ }\r
+\r
+ /**\r
+ * AddLayerDialogModelのプロパティ変更を監視するリスナ\r
+ */\r
+ private PropertyChangeListener modelPropChangeListener;\r
+\r
+ /**\r
+ * レイヤー追加ダイアログのモデル\r
+ */\r
+ private AddLayerDialogModel model = new AddLayerDialogModel();\r
+\r
+ public AddLayerDialogModel getModel() {\r
+ return model;\r
+ }\r
+\r
+ public void setModel(AddLayerDialogModel model) {\r
+ if (model == null) {\r
+ throw new NullPointerException();\r
+ }\r
+\r
+ AddLayerDialogModel old = this.model;\r
+ if (old.equals(model)) {\r
+ // 同じなので何もしない\r
+ return;\r
+ }\r
+\r
+ old.removePropertyChangeListener(modelPropChangeListener);\r
+ this.model = model;\r
+ model.addPropertyChangeListener(modelPropChangeListener);\r
+\r
+ // モデルの内容でUIを更新する。\r
+ // ※ UI更新中はイベントの発生を無視させる\r
+ initCmbCategory();\r
+ initCmbLayer();\r
+ initDefaultOrder();\r
+ initOrder();\r
+\r
+ updateButtonState();\r
+\r
+ firePropertyChange(MODEL, old, model);\r
+ }\r
+\r
+ /**\r
+ * cmbCategoryModelのUIの初期化中であることを表す。\r
+ * UIのイベントリスナで初期化中のイベントは別コンポーネントに連携させないようにする。\r
+ */\r
+ private final AtomicInteger updaingCmbCategoryModel = new AtomicInteger();\r
+\r
+ protected void initCmbCategory() {\r
+ updaingCmbCategoryModel.incrementAndGet();\r
+ try {\r
+ List<PartsCategory> categories = model.getPartsCategories();\r
+ cmbCategoryModel.setAll(categories);\r
+ cmbCategoryModel.setSelectedItem(model.getSelectedPartsCategory());\r
+ } finally {\r
+ updaingCmbCategoryModel.decrementAndGet();\r
+ }\r
+ }\r
+\r
+ protected void onChangeSelectCategory(PartsCategory selectedCategory) {\r
+ model.setSelectedPartsCategory(selectedCategory);\r
+ model.setSelectedLayer(null);\r
+ }\r
+\r
+ /**\r
+ * モデルのCategory現在値が変更されたので画面に反映する\r
+ */\r
+ protected void updateCmbCategory() {\r
+ PartsCategory selCategory = model.getSelectedPartsCategory();\r
+ cmbCategoryModel.setSelectedItem(selCategory);\r
+ initCmbLayer();\r
+ }\r
+\r
+ /**\r
+ * cmbLayerModelのUIの初期化中であることを表す。\r
+ * UIのイベントリスナで初期化中のイベントは別コンポーネントに連携させないようにする。\r
+ */\r
+ private final AtomicInteger updaingCmbLayerModel = new AtomicInteger();\r
+\r
+ protected void initCmbLayer() {\r
+ updaingCmbLayerModel.incrementAndGet();\r
+ try {\r
+ PartsCategory selCategory = model.getSelectedPartsCategory();\r
+ if (selCategory != null) {\r
+ cmbLayerModel.setAll(selCategory.getLayers());\r
+ } else {\r
+ cmbLayerModel.setAll(null);\r
+ }\r
+ cmbLayerModel.setSelectedItem(model.getSelectedLayer());\r
+\r
+ } finally {\r
+ updaingCmbLayerModel.decrementAndGet();\r
+ }\r
+ }\r
+\r
+ protected void onChangeSelectLayer(Layer selectedLayer) {\r
+ model.setSelectedLayer(selectedLayer);\r
+\r
+ // レイヤーの既定の順序を設定する\r
+ int defaultOrder = 0;\r
+ if (selectedLayer != null) {\r
+ defaultOrder = selectedLayer.getOrder();\r
+ }\r
+ model.setDefaultOrder(defaultOrder);\r
+ model.setOrder(defaultOrder);\r
+ }\r
+\r
+ /**\r
+ * モデルのLayer現在値が変更されたので画面に反映する\r
+ */\r
+ protected void updateCmbLayer() {\r
+ Layer selLayer = model.getSelectedLayer();\r
+ cmbLayerModel.setSelectedItem(selLayer);\r
+ }\r
+\r
+ private void initDefaultOrder() {\r
+ // txtDefaultOrderは編集不可のテキストボックスでイベントは監視していない。\r
+ if (model.getSelectedLayer() != null) {\r
+ txtDefaultOrder.setText(Integer.toString(model.getDefaultOrder()));\r
+ } else {\r
+ txtDefaultOrder.setText("");\r
+ }\r
+ }\r
+\r
+ /**\r
+ * txtOrderのUIの初期化中であることを表す。\r
+ * UIのイベントリスナで初期化中のイベントは別コンポーネントに連携させないようにする。\r
+ */\r
+ private final AtomicInteger updaingTxtOrderModel = new AtomicInteger();\r
+\r
+ /**\r
+ * txtOrderを初期設定する\r
+ * この処理期間中は{@link #updaingCmbLayerModel}を0以上とすることで\r
+ * コンボボックスからのイベントが、この初期設定によるものであることを判別できる。\r
+ */\r
+ private void initOrder() {\r
+ updaingTxtOrderModel.incrementAndGet();\r
+ try {\r
+ txtOrder.setText(Integer.toString(model.getOrder()));\r
+ updateTxtOrderState(false);\r
+\r
+ } finally {\r
+ updaingTxtOrderModel.decrementAndGet();\r
+ }\r
+ }\r
+\r
+ private void updateTxtOrderState(boolean error) {\r
+ Color color;\r
+ if (error) {\r
+ color = Color.RED;\r
+ } else {\r
+ color = UIManager.getColor("TextField.background");\r
+ }\r
+ txtOrder.setBackground(color);\r
+ }\r
+\r
+ /**\r
+ * ユーザー操作によりテキストボックスが変更された場合\r
+ * @param text\r
+ */\r
+ protected void onChangeTxtOrder(String text) {\r
+ boolean error = true;\r
+ try {\r
+ if (text != null && text.length() > 0) {\r
+ int order = Integer.parseInt(text);\r
+ model.setOrder(order);\r
+ error = false;\r
+ }\r
+ } catch (RuntimeException ex) {\r
+ // なにもしない\r
+ }\r
+\r
+ updateTxtOrderState(error);\r
+ }\r
+\r
+ protected void updateTxtDefaultOrder() {\r
+ // DefaultOrderテキストボックスは編集はしないのでinitと同じで良い\r
+ initDefaultOrder();\r
+ }\r
+\r
+ /**\r
+ * モデルのorderが変更されたので画面に反映する\r
+ */\r
+ protected void updateTxtOrder() {\r
+ int order = model.getOrder();\r
+ Integer old;\r
+ try {\r
+ old = Integer.parseInt(txtOrder.getText());\r
+ } catch (RuntimeException ex) {\r
+ old = null;\r
+ }\r
+ if (old == null || old != order) {\r
+ // 現在の入力している値と数値的に異なる場合のみ再設定する\r
+ // (自分のテキストボックス変更イベントによりモデルが変更され、その結果、\r
+ // テキストボックスへの値変更が呼び出された場合に状態エラーが発生する。)\r
+ txtOrder.setText(Integer.toString(order));\r
+ updateTxtOrderState(false);\r
+ }\r
+ }\r
+\r
+ protected void updateButtonState() {\r
+ actOK.setEnabled(model.isValid());\r
+ }\r
+\r
+ protected void onOK() {\r
+ if (!model.isValid()) {\r
+ return;\r
+ }\r
+ model.setResult(true);\r
+ dispose();\r
+ }\r
+\r
+ protected void onCancel() {\r
+ model.setResult(false);\r
+ dispose();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * レイヤーの追加ダイアログを開く\r
+ */\r
+ protected void onAddLayer() {\r
+ AddLayerDialogModel model = new AddLayerDialogModel();\r
+ model.setPartsCategories(categories);\r
+\r
+ final AddLayerDialog dlg = new AddLayerDialog(this, true);\r
+ dlg.setModel(model);\r
+\r
+ // 中央に配置\r
+ Point loc = getLocationOnScreen();\r
+ int centerX = loc.x + getWidth() / 2;\r
+ int centerY = loc.y + getHeight() / 2;\r
+\r
+ int x = centerX - dlg.getWidth() / 2;\r
+ int y = centerY - dlg.getHeight() / 2;\r
+ dlg.setLocation(x, y);\r
+ dlg.setVisible(true);\r
+\r
+ if (model.isResult()) {\r
+ PartsCategory category = model.getSelectedPartsCategory();\r
+ Layer layer = model.getSelectedLayer();\r
+ int layerOrder = model.getOrder();\r
+ dataModel.add(category, layer, layerOrder);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 選択レイヤーを削除する\r
+ */\r
+ protected void onDeleteLayer() {\r
+ int selrow = tblLayerOrder.getSelectedRow();\r
+ if (selrow >= 0) {\r
+ dataModel.remove(selrow);\r
+ }\r
+ }\r
+}\r
+\r
+/**\r
+ * レイヤーカスタマイズダイアログのデータモデル\r
+ */\r
+class LayerOrderCustomizeDialogModel {\r
+\r
+ /**\r
+ * 保持しているレイヤーパターンの登録・変更・削除を通知されるリスナ\r
+ */\r
+ public interface ChangeListener extends EventListener {\r
+\r
+ /**\r
+ * 通知タイプ\r
+ */\r
+ public enum ChangeType {\r
+ ADD, MODIFY, REMOVE\r
+ }\r
+\r
+ /**\r
+ * イベント\r
+ */\r
+ public class Change extends EventObject {\r
+\r
+ private static final long serialVersionUID = -4578290841626577210L;\r
+\r
+ /**\r
+ * レイヤーパターン名\r
+ */\r
+ private String name;\r
+\r
+ /**\r
+ * 変更タイプ\r
+ */\r
+ private ChangeType changeType;\r
+\r
+ public Change(LayerOrderCustomizeDialogModel source, String name, ChangeType changeType) {\r
+ super(source);\r
+ this.name = name;\r
+ this.changeType = changeType;\r
+ }\r
+\r
+ public String getName() {\r
+ return name;\r
+ }\r
+\r
+ public ChangeType getChangeType() {\r
+ return changeType;\r
+ }\r
+\r
+ @Override\r
+ public LayerOrderCustomizeDialogModel getSource() {\r
+ return (LayerOrderCustomizeDialogModel) super.getSource();\r
+ }\r
+\r
+ @Override\r
+ public String toString() {\r
+ return "Change [name=" + name + ", changeType=" + changeType + "]";\r
+ }\r
+ }\r
+\r
+ void onChange(Change change);\r
+ }\r
+\r
+ /**\r
+ * イベントリスナのリスト\r
+ */\r
+ private final EventListenerList listeners = new EventListenerList();\r
+\r
+ /**\r
+ * レイヤー名パターンを保持する。(patternsMapから導出され、以後、監視される。)\r
+ */\r
+ private final ObservableList<String> patternNames = new ObservableList<String>();\r
+\r
+ /**\r
+ * レイヤーパターン名をキーとして、レイヤーの構成リストを値とするマップ。\r
+ * キーはpatternNamesの監視リストと連携する。\r
+ */\r
+ private final Map<String, List<CustomLayerOrder>> patternsMap = new HashMap<String, List<CustomLayerOrder>>();\r
+\r
+ /**\r
+ * レイヤー編集画面の初期値\r
+ */\r
+ private List<CustomLayerOrder> currentList = Collections.emptyList();\r
+\r
+ /**\r
+ * コンストラクタ\r
+ */\r
+ public LayerOrderCustomizeDialogModel() {\r
+ patternNames.addListChangeListener(new ListChangeListener<String>() {\r
+ @Override\r
+ public void onChanged(Change<? extends String> c) {\r
+ switch (c.getType()) {\r
+ case ADD:\r
+ // パターン名が追加された場合は、あわせて導出元のマップも追加する\r
+ String newName = c.getNewValue();\r
+ if (!patternsMap.containsKey(newName)) {\r
+ // まだマップに存在しなければ空のリストを作成しておく\r
+ patternsMap.put(newName, new ArrayList<CustomLayerOrder>());\r
+ fireEvent(ChangeListener.ChangeType.ADD, newName);\r
+ }\r
+ break;\r
+\r
+ case REMOVE:\r
+ // パターン名が削除された場合は、導出元のマップも削除する。\r
+ String oldName = c.getOldValue();\r
+ patternsMap.remove(oldName);\r
+ fireEvent(ChangeListener.ChangeType.REMOVE, oldName);\r
+ break;\r
+ }\r
+ }\r
+ });\r
+ }\r
+\r
+ /**\r
+ * 変更通知リスナを登録する\r
+ * @param l リスナ\r
+ */\r
+ public void addListChangeListener(ChangeListener l) {\r
+ listeners.add(ChangeListener.class, l);\r
+ }\r
+\r
+ /**\r
+ * 変更通知リスナを登録解除する\r
+ * @param l リスナ\r
+ */\r
+ public void removeListChangeListener(ChangeListener l) {\r
+ listeners.remove(ChangeListener.class, l);\r
+ }\r
+\r
+ /**\r
+ * 全ての変更通知リスナに対して変更イベントを通知する。\r
+ * @param type 変更タイプ\r
+ * @param name 名前\r
+ */\r
+ protected void fireEvent(ChangeListener.ChangeType type, String name) {\r
+ // Guaranteed to return a non-null array\r
+ Object[] ll = listeners.getListenerList();\r
+ // Process the listeners last to first, notifying\r
+ // those that are interested in this event\r
+ // ※ 逆順で通知するのがSwingの作法らしい。\r
+ ChangeListener.Change event = null;\r
+ for (int i = ll.length - 2; i >= 0; i -= 2) {\r
+ if (ll[i] == ChangeListener.class) {\r
+ // Lazily create the event:\r
+ if (event == null) {\r
+ event = new ChangeListener.Change(this, name, type);\r
+ }\r
+ ((ChangeListener) ll[i + 1]).onChange(event);\r
+ }\r
+ }\r
+ }\r
+\r
+ public ObservableList<CustomLayerOrder> getCurrentList() {\r
+ return copyWithoutListeners(currentList);\r
+ }\r
+\r
+ public void setCurrentList(List<CustomLayerOrder> currentList) {\r
+ if (!currentList.equals(this.currentList)) {\r
+ this.currentList = copyWithoutListeners(\r
+ currentList != null ? currentList : new ArrayList<CustomLayerOrder>());\r
+ fireEvent(ChangeListener.ChangeType.MODIFY, "");\r
+ }\r
+ }\r
+\r
+ public ObservableList<String> getPatternNames() {\r
+ return patternNames;\r
+ }\r
+\r
+ public ObservableList<CustomLayerOrder> getCopy(String name) {\r
+ return copyWithoutListeners(patternsMap.get(name));\r
+ }\r
+\r
+ public void put(String name, List<CustomLayerOrder> layerOrderList) {\r
+ boolean exist = patternsMap.containsKey(name);\r
+ patternsMap.put(name, copyWithoutListeners(layerOrderList));\r
+ if (!exist) {\r
+ patternNames.add(name);\r
+ fireEvent(ChangeListener.ChangeType.ADD, name);\r
+ } else {\r
+ fireEvent(ChangeListener.ChangeType.MODIFY, name);\r
+ }\r
+ }\r
+\r
+ public void remove(String name) {\r
+ if (patternNames.contains(name)) {\r
+ // 名前の削除によりリスナーでマップも削除される\r
+ patternNames.remove(name);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 監視可能なリストをコピーして返す。ただし、既存のリスナーはコピーされない。\r
+ * 返される可能可能リストは要素の変更を監視してリストの変更通知として返すように設定される。\r
+ * @param src\r
+ * @return\r
+ */\r
+ public ObservableList<CustomLayerOrder> copyWithoutListeners(List<CustomLayerOrder> src) {\r
+ if (src == null) {\r
+ return null;\r
+ }\r
+ ObservableList<CustomLayerOrder> list = createObservableList();\r
+ for (CustomLayerOrder item : src) {\r
+ list.add(item.copy());\r
+ }\r
+ return list;\r
+ }\r
+\r
+ /**\r
+ * 内包する要素の変更通知をリストの更新通知に接続する要素を含めた監視可能なリストを作成して返す。\r
+ * @return\r
+ */\r
+ public ObservableList<CustomLayerOrder> createObservableList() {\r
+ final List<CustomLayerOrder> rawList = new ArrayList<CustomLayerOrder>();\r
+ final ObservableList<CustomLayerOrder> obsList = new ObservableList<CustomLayerOrder>(rawList);\r
+ final PropertyChangeListener listener = new PropertyChangeListener() {\r
+ @Override\r
+ public void propertyChange(PropertyChangeEvent evt) {\r
+ CustomLayerOrder source = (CustomLayerOrder) evt.getSource();\r
+ int index = rawList.indexOf(source);\r
+ obsList.fireEvent(ListChangeListener.ChangeType.MODIFIY, index, null, source);\r
+ }\r
+ };\r
+\r
+ ObservableList.Hook<CustomLayerOrder> hook = new ObservableList.Hook<CustomLayerOrder>() {\r
+ @Override\r
+ public void add(CustomLayerOrder item) {\r
+ item.addPropertyChangeListener(listener);\r
+ }\r
+ @Override\r
+ public void remove(CustomLayerOrder item) {\r
+ item.removePropertyChangeListener(listener);\r
+ }\r
+ };\r
+ obsList.setHook(hook);\r
+\r
+ return obsList;\r
+ }\r
+}\r
-package charactermanaj.ui;
-
-import static java.lang.Math.*;
-
-import java.awt.BorderLayout;
-import java.awt.Color;
-import java.awt.Component;
-import java.awt.Container;
-import java.awt.Cursor;
-import java.awt.Dimension;
-import java.awt.Font;
-import java.awt.Frame;
-import java.awt.GraphicsEnvironment;
-import java.awt.Point;
-import java.awt.Rectangle;
-import java.awt.Toolkit;
-import java.awt.dnd.DropTarget;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.MouseWheelEvent;
-import java.awt.event.MouseWheelListener;
-import java.awt.event.WindowAdapter;
-import java.awt.event.WindowEvent;
-import java.awt.image.BufferedImage;
-import java.io.File;
-import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
-import java.net.URI;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-import java.util.TreeMap;
-import java.util.UUID;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import javax.swing.Box;
-import javax.swing.BoxLayout;
-import javax.swing.JCheckBox;
-import javax.swing.JColorChooser;
-import javax.swing.JFrame;
-import javax.swing.JMenu;
-import javax.swing.JMenuBar;
-import javax.swing.JMenuItem;
-import javax.swing.JOptionPane;
-import javax.swing.JPanel;
-import javax.swing.JPopupMenu;
-import javax.swing.JScrollBar;
-import javax.swing.JScrollPane;
-import javax.swing.JSeparator;
-import javax.swing.JSplitPane;
-import javax.swing.JViewport;
-import javax.swing.SwingUtilities;
-import javax.swing.event.AncestorEvent;
-import javax.swing.event.AncestorListener;
-import javax.swing.event.MenuEvent;
-import javax.swing.event.MenuListener;
-
-import charactermanaj.Main;
-import charactermanaj.clipboardSupport.ClipboardUtil;
-import charactermanaj.graphics.AsyncImageBuilder;
-import charactermanaj.graphics.ColorConvertedImageCachedLoader;
-import charactermanaj.graphics.ImageBuildJobAbstractAdaptor;
-import charactermanaj.graphics.ImageBuilder.ImageOutput;
-import charactermanaj.graphics.io.ImageSaveHelper;
-import charactermanaj.graphics.io.OutputOption;
-import charactermanaj.graphics.io.UkagakaImageSaveHelper;
-import charactermanaj.model.AppConfig;
-import charactermanaj.model.CharacterData;
-import charactermanaj.model.CharacterDataChangeEvent;
-import charactermanaj.model.CharacterDataChangeListener;
-import charactermanaj.model.CharacterDataChangeObserver;
-import charactermanaj.model.ColorGroup;
-import charactermanaj.model.IndependentPartsSetInfo;
-import charactermanaj.model.PartsCategory;
-import charactermanaj.model.PartsColorInfo;
-import charactermanaj.model.PartsColorManager;
-import charactermanaj.model.PartsIdentifier;
-import charactermanaj.model.PartsSet;
-import charactermanaj.model.RecommendationURL;
-import charactermanaj.model.WorkingSet;
-import charactermanaj.model.WorkingSet2;
-import charactermanaj.model.io.CharacterDataPersistent;
-import charactermanaj.model.io.PartsImageDirectoryWatchAgent;
-import charactermanaj.model.io.PartsImageDirectoryWatchAgentFactory;
-import charactermanaj.model.io.PartsImageDirectoryWatchEvent;
-import charactermanaj.model.io.PartsImageDirectoryWatchListener;
-import charactermanaj.model.io.RecentDataPersistent;
-import charactermanaj.model.io.WorkingSetPersist;
-import charactermanaj.ui.ImageSelectPanel.ImageSelectPanelEvent;
-import charactermanaj.ui.ImageSelectPanel.ImageSelectPanelListener;
-import charactermanaj.ui.ManageFavoriteDialog.FavoriteManageCallback;
-import charactermanaj.ui.PreviewPanel.PreviewPanelEvent;
-import charactermanaj.ui.PreviewPanel.PreviewPanelListener;
-import charactermanaj.ui.model.ColorChangeEvent;
-import charactermanaj.ui.model.ColorChangeListener;
-import charactermanaj.ui.model.ColorGroupCoordinator;
-import charactermanaj.ui.model.FavoritesChangeEvent;
-import charactermanaj.ui.model.FavoritesChangeListener;
-import charactermanaj.ui.model.FavoritesChangeObserver;
-import charactermanaj.ui.model.PartsColorCoordinator;
-import charactermanaj.ui.model.PartsSelectionManager;
-import charactermanaj.ui.model.WallpaperFactory;
-import charactermanaj.ui.model.WallpaperFactoryErrorRecoverHandler;
-import charactermanaj.ui.model.WallpaperFactoryException;
-import charactermanaj.ui.model.WallpaperInfo;
-import charactermanaj.ui.scrollablemenu.JScrollableMenu;
-import charactermanaj.ui.util.FileDropTarget;
-import charactermanaj.ui.util.WindowAdjustLocationSupport;
-import charactermanaj.util.DesktopUtilities;
-import charactermanaj.util.ErrorMessageHelper;
-import charactermanaj.util.LocalizedResourcePropertyLoader;
-import charactermanaj.util.SystemUtil;
-import charactermanaj.util.UIHelper;
-
-
-/**
- * メインフレーム.<br>
- * アプリケーションがアクティブである場合は最低でも1つのメインフレームが表示されている.<br>
- *
- * @author seraphy
- */
-public class MainFrame extends JFrame
- implements
- FavoritesChangeListener,
- CharacterDataChangeListener {
-
- private static final long serialVersionUID = 1L;
-
- private static final Logger logger = Logger.getLogger(MainFrame.class.getName());
-
-
- protected static final String STRINGS_RESOURCE = "languages/mainframe";
-
- protected static final String MENU_STRINGS_RESOURCE = "menu/menu";
-
- /**
- * メインフレームのアイコン.<br>
- */
- protected BufferedImage icon;
-
-
- /**
- * 現在アクティブなメインフレーム.<br>
- * フォーカスが切り替わるたびにアクティブフレームを追跡する.<br>
- * Mac OS XのAbout/Preferences/Quitのシステムメニューからよびだされた場合に
- * オーナーたるべきメインフレームを識別するためのもの.<br>
- */
- private static volatile MainFrame activedMainFrame;
-
-
- /**
- * このメインフレームが対象とするキャラクターデータ.<br>
- */
- protected CharacterData characterData;
-
-
- /**
- * プレビューペイン
- */
- private PreviewPanel previewPane;
-
- /**
- * パーツ選択マネージャ
- */
- protected PartsSelectionManager partsSelectionManager;
-
- /**
- * パネルの最小化モード
- */
- private boolean minimizeMode;
-
-
- /**
- * パーツ選択パネルリスト
- */
- protected ImageSelectPanelList imageSelectPanels;
-
- /**
- * パーツ選択パネルを納めるスクロールペイン
- */
- protected JScrollPane imgSelectPanelsPanelSp;
-
- /**
- * カラーグループのマネージャ
- */
- protected ColorGroupCoordinator colorGroupCoordinator;
-
- /**
- * パーツカラーのマネージャ
- */
- protected PartsColorCoordinator partsColorCoordinator;
-
-
- /**
- * キャッシュつきのイメージローダ.<br>
- */
- private ColorConvertedImageCachedLoader imageLoader;
-
- /**
- * パーツを組み立てて1つのプレビュー可能なイメージを構築するためのビルダ
- */
- private AsyncImageBuilder imageBuilder;
-
-
- /**
- * パーツイメージを画像として保存する場合のヘルパー.<br>
- * 最後に使ったディレクトリを保持するためのメンバ変数としている.<br>
- */
- private ImageSaveHelper imageSaveHelper = new ImageSaveHelper();
-
- /**
- * 伺か用出力ヘルパ.<br>
- * 最後に使ったディレクトリ、ファイル名、モードなどを保持するためのメンバ変数としている.<br>
- */
- private UkagakaImageSaveHelper ukagakaImageSaveHelper = new UkagakaImageSaveHelper();
-
- /**
- * パーツディレクトリを定期的にチェックし、パーツイメージが変更・追加・削除されている場合に パーツリストを更新するためのウォッチャー
- */
- private PartsImageDirectoryWatchAgent watchAgent;
-
- /**
- * デフォルトのパーツセット表示名
- */
- private String defaultPartsSetTitle;
-
- /**
- * 最後に使用したプリセット.<br>
- * (一度もプリセットを使用していなければnull).
- */
- private PartsSet lastUsePresetParts;
-
- /**
- * 最後に使用した検索ダイアログ.<br>
- * nullであれば一度も使用していない.<br>
- * (nullでなくとも閉じられている可能性がある.)<br>
- */
- private SearchPartsDialog lastUseSearchPartsDialog;
-
- /**
- * 最後に使用したお気に入りダイアログ.<br>
- * nullであれば一度も使用していない.<br>
- * (nullでなくとも閉じられている可能性がある.)
- */
- private ManageFavoriteDialog lastUseManageFavoritesDialog;
-
- /**
- * 最後に使用したパーツのランダム選択ダイアログ.<br>
- * nullであれば一度も使用していない.<br>
- * (nullでなくとも閉じられている可能性がある.)
- */
- private PartsRandomChooserDialog lastUsePartsRandomChooserDialog;
-
- /**
- * 最後に使用した壁紙情報
- */
- private WallpaperInfo wallpaperInfo;
-
-
- /**
- * アクティブなメインフレームを設定する.
- *
- * @param mainFrame
- * メインフレーム
- */
- public static void setActivedMainFrame(MainFrame mainFrame) {
- if (mainFrame == null) {
- throw new IllegalArgumentException();
- }
- activedMainFrame = mainFrame;
- }
-
- /**
- * 現在アクティブなメインフレームを取得する. まだメインフレームが開かれていない場合はnull.<br>
- * 最後のメインフレームが破棄中、もしくは破棄済みであれば破棄されたフレームを示すことに注意.<br>
- *
- * @return メインフレーム、もしくはnull
- */
- public static MainFrame getActivedMainFrame() {
- return activedMainFrame;
- }
-
- /**
- * キャラクターデータが変更された場合に通知される.
- */
- public void notifyChangeCharacterData(final CharacterDataChangeEvent e) {
- final CharacterData cd = e.getCharacterData();
- if (cd != null
- && cd.getDocBase().equals(
- MainFrame.this.characterData.getDocBase())) {
- SwingUtilities.invokeLater(new Runnable() {
- public void run() {
- try {
- Cursor oldCur = getCursor();
- setCursor(Cursor
- .getPredefinedCursor(Cursor.WAIT_CURSOR));
- try {
- if (e.isChangeStructure()) {
- // 現在情報の保存
- saveWorkingSet();
-
- // 画面構成の再構築
- initComponent(cd);
- }
-
- if (e.isReloadPartsAndFavorites()) {
- // パーツとお気に入りのリロード
- reloadPartsAndFavorites(cd, true);
- }
-
- } finally {
- setCursor(oldCur != null ? oldCur : Cursor
- .getDefaultCursor());
- }
-
- } catch (Exception ex) {
- ErrorMessageHelper.showErrorDialog(MainFrame.this, ex);
- }
- }
- });
- }
- }
-
- /**
- * お気に入りデータが変更された場合に通知される.
- *
- * @param e
- */
- public void notifyChangeFavorites(FavoritesChangeEvent e) {
- CharacterData cd = e.getCharacterData();
- if (cd != null
- && cd.getDocBase().equals(
- MainFrame.this.characterData.getDocBase())) {
- if (!MainFrame.this.equals(e.getSource())
- && !characterData.equals(cd)) {
- // プリセットとお気に入りを最新化する.
- // ただし、自分自身から送信したイベントの場合は最新化は不要.
- characterData.clearPartsSets(false);
- for (Map.Entry<String, PartsSet> entry : cd.getPartsSets()
- .entrySet()) {
- PartsSet partsSet = entry.getValue();
- characterData.addPartsSet(partsSet);
- }
- }
-
- // お気に入り管理ダイアログ上のお気に入り一覧を最新に更新する.
- if (lastUseManageFavoritesDialog != null
- && lastUseManageFavoritesDialog.isDisplayable()) {
- lastUseManageFavoritesDialog.initListModel();
- }
- }
- }
-
- /**
- * メインフレームを構築する.
- *
- * @param characterData
- * キャラクターデータ
- */
- public MainFrame(CharacterData characterData) {
- try {
- if (characterData == null) {
- throw new IllegalArgumentException();
- }
-
- setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
- addWindowListener(new WindowAdapter() {
- @Override
- public void windowClosing(WindowEvent e) {
- onCloseProfile();
- }
- @Override
- public void windowClosed(WindowEvent e) {
- stopAgents();
- }
- @Override
- public void windowActivated(WindowEvent e) {
- setActivedMainFrame(MainFrame.this);
- }
- @Override
- public void windowOpened(WindowEvent e) {
- // do nothing.
- }
- });
-
- // アイコンの設定
- icon = UIHelper.getInstance().getImage("icons/icon.png");
- setIconImage(icon);
-
- // 画面コンポーネント作成
- initComponent(characterData);
- JMenuBar menuBar = createMenuBar();
- setJMenuBar(menuBar);
-
- // お気に入り変更通知を受け取る
- FavoritesChangeObserver.getDefault().addFavoritesChangeListener(
- this);
- // キャラクターデータの変更通知を受け取る
- CharacterDataChangeObserver.getDefault()
- .addCharacterDataChangeListener(this);
-
- } catch (RuntimeException ex) {
- logger.log(Level.SEVERE, "メインフレームの構築中に予期せぬ例外が発生しました。", ex);
- dispose(); // コンストラクタが呼ばれた時点でJFrameは構築済みなのでdisposeの必要がある.
- throw ex;
- } catch (Error ex) {
- logger.log(Level.SEVERE, "メインフレームの構築中に致命的な例外が発生しました。", ex);
- dispose(); // コンストラクタが呼ばれた時点でJFrameは構築済みなのでdisposeの必要がある.
- throw ex;
- }
- }
-
- /**
- * デフォルトのウィンドウ位置とサイズの設定
- */
- private void setDefaultWindowLocation() {
- // メインスクリーンサイズを取得する.
- GraphicsEnvironment genv = GraphicsEnvironment.getLocalGraphicsEnvironment();
- Rectangle desktopSize = genv.getMaximumWindowBounds(); // メインスクリーンのサイズ(デスクトップ領域のみ)
- logger.log(Level.CONFIG, "desktopSize=" + desktopSize);
-
- Dimension imageSize = characterData.getImageSize();
- // 画像サイズ300x400を基準サイズとして、それ以下にはならない.
- // アプリケーション設定の最大サイズ以上の場合はウィンドウサイズは固定してスクロールバーに任せる
- AppConfig appConfig = AppConfig.getInstance();
- int maxWidth = min(desktopSize.width, appConfig.getMainFrameMaxWidth());
- int maxHeight = min(desktopSize.height, appConfig.getMainFrameMaxHeight());
- int imageWidth = min(maxWidth, max(300, imageSize != null ? imageSize.width : 0));
- int imageHeight = min(maxHeight, max(400, imageSize != null ? imageSize.height : 0));
- // 300x400の画像の場合にメインフレームが600x550だとちょうどいい感じ.
- // それ以上大きい画像の場合は増えた分だけフレームを大きくしておく.
- setSize(imageWidth - 300 + 600, imageHeight - 400 + 550);
-
- // 次回表示時にプラットフォーム固有位置に表示するように予約
- setLocationByPlatform(true);
- }
-
- /**
- * メインフレームを表示する.<br>
- * デスクトップ領域からはみ出した場合は位置を補正する.<br>
- */
- public void showMainFrame() {
- // メインスクリーンサイズを取得する.
- GraphicsEnvironment genv = GraphicsEnvironment.getLocalGraphicsEnvironment();
- Rectangle desktopSize = genv.getMaximumWindowBounds(); // メインスクリーンのサイズ(デスクトップ領域のみ)
- logger.log(Level.CONFIG, "desktopSize=" + desktopSize);
-
- // プラットフォーム固有の位置あわせで表示する.
- // 表示した結果、はみ出している場合は0,0に補正する.
- setVisible(true);
- Point loc = getLocation();
- logger.log(Level.CONFIG, "windowLocation=" + loc);
- Dimension windowSize = getSize();
- if (loc.y + windowSize.height >= desktopSize.height) {
- loc.y = 0;
- }
- if (loc.x + windowSize.width >= desktopSize.width) {
- loc.x = 0;
- }
- if (loc.x == 0 || loc.y == 0) {
- setLocation(loc);
- }
-
- // デスクトップよりも大きい場合は小さくする.
- boolean resize = false;
- Dimension dim = getSize();
- if (dim.height > desktopSize.height) {
- dim.height = desktopSize.height;
- resize = true;
- }
- if (dim.width > desktopSize.width) {
- dim.width = desktopSize.width;
- resize = true;
- }
- if (resize) {
- setSize(dim);
- }
- }
-
- /**
- * このメインフレームに関連づけられているエージェントスレッドを停止します.<br>
- * すでに停止している場合は何もしません。
- */
- protected void stopAgents() {
- // エージェントを停止
- if (watchAgent != null) {
- try {
- watchAgent.disconnect();
-
- } catch (Throwable ex) {
- logger.log(Level.SEVERE, "フォルダ監視スレッドの停止に失敗しました。", ex);
- }
- watchAgent = null;
- }
- // イメージビルダを停止
- if (imageBuilder != null) {
- try {
- imageBuilder.stop();
-
- } catch (Throwable ex) {
- logger.log(Level.SEVERE, "非同期イメージビルダスレッドの停止に失敗しました。", ex);
- }
- imageBuilder = null;
- }
- }
-
- /**
- * メインフレームを破棄します.<br>
- */
- @Override
- public void dispose() {
- FavoritesChangeObserver.getDefault()
- .removeFavoritesChangeListener(this);
- CharacterDataChangeObserver.getDefault()
- .removeCharacterDataChangeListener(this);
- imageLoader.close();
- stopAgents();
- super.dispose();
- }
-
- /**
- * 画面コンポーネントを設定します.<br>
- * すでに設定されている場合は一旦削除されたのちに再作成されます.<br>
- */
- private void initComponent(CharacterData characterData) {
-
- CharacterData oldCd;
- synchronized (this) {
- oldCd = this.characterData;
- if (oldCd != null) {
- // 使用中のキャラクターデータであることを登録解除する。
- ProfileListManager.unregisterUsedCharacterData(oldCd);
- }
- this.characterData = characterData;
-
- // 使用中のキャラクターデータであることを登録する.
- ProfileListManager.registerUsedCharacterData(characterData);
- }
-
- // 設定まわり準備
- AppConfig appConfig = AppConfig.getInstance();
- Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
- .getLocalizedProperties(STRINGS_RESOURCE);
-
- // タイトル表示
- String title;
- if (Main.isMacOSX()) {
- // Mac OS Xの場合はウィンドウにタイトルはつけない。
- title = "";
- } else {
- title = strings.getProperty("title");
- }
- setTitle(title + characterData.getName());
-
- // デフォルトのパーツセット表示名
- defaultPartsSetTitle = strings.getProperty("defaultPartsSetTitle");
-
- // エージェントの停止
- stopAgents();
-
- // コンポーネント配置
- Container contentPane = getContentPane();
-
- // すでにあるコンポーネントを削除
- for (Component comp : contentPane.getComponents()) {
- contentPane.remove(comp);
- }
- // 開いている検索ダイアログを閉じる
- closeSearchDialog();
-
- // 開いているお気に入り管理ダイアログを閉じる
- closeManageFavoritesDialog();
-
- // 開いているランダム選択ダイアログを閉じる.
- closePartsRandomChooserDialog();
-
- PartsColorManager partsColorManager = characterData.getPartsColorManager();
-
- // デフォルトの背景色の設定
- Color bgColor = appConfig.getDefaultImageBgColor();
- wallpaperInfo = new WallpaperInfo();
- wallpaperInfo.setBackgroundColor(bgColor);
-
- if (imageLoader != null) {
- imageLoader.close();
- }
- imageLoader = new ColorConvertedImageCachedLoader();
- imageBuilder = new AsyncImageBuilder(imageLoader);
- partsSelectionManager = new PartsSelectionManager(partsColorManager,
- new PartsSelectionManager.ImageBgColorProvider() {
- public Color getImageBgColor() {
- return wallpaperInfo.getBackgroundColor();
- }
- public void setImageBgColor(Color imageBgColor) {
- applyBackgroundColorOnly(imageBgColor);
- }
- });
- colorGroupCoordinator = new ColorGroupCoordinator(partsSelectionManager, partsColorManager);
- partsColorCoordinator = new PartsColorCoordinator(characterData, partsColorManager, colorGroupCoordinator);
- PartsImageDirectoryWatchAgentFactory agentFactory = PartsImageDirectoryWatchAgentFactory.getFactory();
- watchAgent = agentFactory.getAgent(characterData);
-
- previewPane = new PreviewPanel();
- previewPane.setTitle(defaultPartsSetTitle);
- previewPane.addPreviewPanelListener(new PreviewPanelListener() {
- public void addFavorite(PreviewPanelEvent e) {
- if (!e.isShiftKeyPressed()) {
- // お気に入り登録
- onRegisterFavorite();
-
- } else {
- // シフトキーにて、お気に入りの管理を開く
- onManageFavorites();
- }
- }
- public void changeBackgroundColor(PreviewPanelEvent e) {
- if ( !e.isShiftKeyPressed()) {
- // 壁紙選択
- onChangeWallpaper();
-
- } else {
- // シフトキーにて背景色変更
- onChangeBgColor();
- }
- }
- public void copyPicture(PreviewPanelEvent e) {
- onCopy(e.isShiftKeyPressed());
- }
- public void savePicture(PreviewPanelEvent e) {
- if ( !e.isShiftKeyPressed()) {
- // 画像出力
- onSavePicture();
-
- } else {
- // シフトキーにて「伺か」用出力
- onSaveAsUkagaka();
- }
- }
- public void showInformation(PreviewPanelEvent e) {
- onInformation();
- }
- public void flipHorizontal(PreviewPanelEvent e) {
- onFlipHolizontal();
- }
- });
-
- imageSelectPanels = new ImageSelectPanelList();
-
- JPanel imgSelectPanelsPanel = new JPanel();
- BoxLayout bl = new BoxLayout(imgSelectPanelsPanel, BoxLayout.PAGE_AXIS);
- imgSelectPanelsPanel.setLayout(bl);
- for (PartsCategory category : characterData.getPartsCategories()) {
- final ImageSelectPanel imageSelectPanel = new ImageSelectPanel(category, characterData);
- imgSelectPanelsPanel.add(imageSelectPanel);
- imageSelectPanels.add(imageSelectPanel);
- partsSelectionManager.register(imageSelectPanel);
- }
-
- imgSelectPanelsPanelSp = new JScrollPane(imgSelectPanelsPanel) {
- private static final long serialVersionUID = 1L;
- @Override
- public JScrollBar createVerticalScrollBar() {
- JScrollBar sb = super.createVerticalScrollBar();
- sb.setUnitIncrement(12);
- return sb;
- }
- };
- imgSelectPanelsPanelSp.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
-
- JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, imgSelectPanelsPanelSp, previewPane);
- contentPane.add(splitPane, BorderLayout.CENTER);
-
-
- imgSelectPanelsPanelSp.requestFocus();
-
- ArrayList<ColorGroup> colorGroups = new ArrayList<ColorGroup>();
- colorGroups.addAll(characterData.getColorGroups());
-
- final ColorChangeListener colorChangeListener = new ColorChangeListener() {
- public void onColorGroupChange(ColorChangeEvent event) {
- // do nothing.
- }
- public void onColorChange(ColorChangeEvent event) {
- MainFrame.this.requestPreview();
- }
- };
- colorGroupCoordinator.addColorChangeListener(colorChangeListener);
-
- for (int idx = 0; idx < imageSelectPanels.size(); idx++) {
- ImageSelectPanel imageSelectPanel = imageSelectPanels.get(idx);
- final PartsCategory partsCategory = imageSelectPanel.getPartsCategory();
- final ColorDialog colorDialog = new ColorDialog(this, partsCategory, colorGroups);
- colorGroupCoordinator.registerColorDialog(colorDialog);
- partsColorCoordinator.register(imageSelectPanel, colorDialog);
- final int curidx = idx;
- imageSelectPanel.addImageSelectListener(new ImageSelectPanelListener() {
- public void onChangeColor(ImageSelectPanelEvent event) {
- WindowAdjustLocationSupport.alignRight(
- MainFrame.this, colorDialog, curidx, false);
- colorDialog.setVisible(!colorDialog.isVisible());
- }
- public void onPreferences(ImageSelectPanelEvent event) {
- // do nothing. (not supported)
- }
- public void onChange(ImageSelectPanelEvent event) {
- MainFrame.this.requestPreview();
- }
- public void onSelectChange(ImageSelectPanelEvent event) {
- // do nothing.
- }
- public void onTitleClick(ImageSelectPanelEvent event) {
- PartsCategory partsCategory = (event != null) ?
- event.getImageSelectPanel().getPartsCategory() : null;
- MainFrame.this.onClickPartsCategoryTitle(partsCategory, false);
- }
- public void onTitleDblClick(ImageSelectPanelEvent event) {
- PartsCategory partsCategory = (event != null) ?
- event.getImageSelectPanel().getPartsCategory() : null;
- MainFrame.this.onClickPartsCategoryTitle(partsCategory, true);
- }
- });
- imageSelectPanel.addAncestorListener(new AncestorListener() {
- public void ancestorAdded(AncestorEvent event) {
- }
- public void ancestorMoved(AncestorEvent event) {
- }
- public void ancestorRemoved(AncestorEvent event) {
- // パネルもしくは、その親が削除されたときにダイアログも非表示とする。
- colorDialog.setVisible(false);
- }
- });
- }
-
- // 全パーツのロード
- partsSelectionManager.loadParts();
-
- // 保存されているワーキングセットを復元する.
- // 復元できなかった場合はパーツセットを初期選択する.
- if ( !loadWorkingSet()) {
- // ワーキングセットがない場合は
- // デフォルトのウィンドウ位置とサイズ
- setDefaultWindowLocation();
-
- // デフォルトのパーツセットの表示
- if (showDefaultParts(true)) {
- requestPreview();
- }
- }
-
- // 選択されているパーツを見える状態にする
- scrollToSelectedParts();
-
- // 非同期イメージローダの処理開始
- if (!imageBuilder.isAlive()) {
- imageBuilder.start();
- }
-
- // ドロップターゲットの設定
- new DropTarget(imgSelectPanelsPanelSp, new FileDropTarget() {
- @Override
- protected void onDropFiles(final List<File> dropFiles) {
- if (dropFiles == null || dropFiles.isEmpty()) {
- return;
- }
- // インポートダイアログを開く.
- // ドロップソースの処理がブロッキングしないように、
- // ドロップハンドラの処理を終了してからインポートダイアログが開くようにする.
- SwingUtilities.invokeLater(new Runnable() {
- public void run() {
- onImport(dropFiles);
- }
- });
- }
- @Override
- protected void onException(Exception ex) {
- ErrorMessageHelper.showErrorDialog(MainFrame.this, ex);
- }
- });
-
- // ディレクトリを監視し変更があった場合にパーツをリロードするリスナ
- watchAgent.addPartsImageDirectoryWatchListener(new PartsImageDirectoryWatchListener() {
- public void detectPartsImageChange(PartsImageDirectoryWatchEvent e) {
- Runnable refreshJob = new Runnable() {
- public void run() {
- onDetectPartsImageChange();
- }
- };
- if (SwingUtilities.isEventDispatchThread()) {
- refreshJob.run();
- } else {
- SwingUtilities.invokeLater(refreshJob);
- }
- }
- });
-
- // 監視が有効であれば、ディレクトリの監視をスタートする
- if (appConfig.isEnableDirWatch() && characterData.isWatchDirectory()) {
- watchAgent.connect();
- }
-
- // パーツカテゴリの自動縮小が設定されている場合
- minimizeMode = false;
- if (appConfig.isEnableAutoShrinkPanel()) {
- onClickPartsCategoryTitle(null, true);
- }
-
- // コンポーネントの再構築の場合
- if (oldCd != null) {
- validate();
- }
- }
-
- /**
- * パーツが変更されたことを検知した場合.<br>
- * パーツデータをリロードし、各カテゴリのパーツ一覧を再表示させ、プレビューを更新する.<br>
- */
- protected void onDetectPartsImageChange() {
- try {
- reloadPartsAndFavorites(null, true);
-
- } catch (IOException ex) {
- logger.log(Level.SEVERE, "parts reload failed. " + characterData, ex);
- }
- }
-
- /**
- * すべてのカテゴリのリストで選択中のアイテムが見えるようにスクロールする.
- */
- protected void scrollToSelectedParts() {
- partsSelectionManager.scrollToSelectedParts();
- }
-
- /**
- * 指定したパーツカテゴリ以外のパーツ選択パネルを最小化する.
- *
- * @param partsCategory
- * パーツカテゴリ、nullの場合は全て最小化する.
- * @param dblClick
- * ダブルクリック
- */
- protected void onClickPartsCategoryTitle(PartsCategory partsCategory, boolean dblClick) {
- if (logger.isLoggable(Level.FINE)) {
- logger.log(Level.FINE, "onClickPartsCategoryTitle category="
- + partsCategory + "/clickCount=" + dblClick);
- }
- if (dblClick) {
- minimizeMode = !minimizeMode;
- if (!minimizeMode) {
- partsSelectionManager.setMinimizeModeIfOther(null, false);
- return;
- }
- }
- if (minimizeMode) {
- if (partsSelectionManager.isNotMinimizeModeJust(partsCategory)) {
- partsSelectionManager.setMinimizeModeIfOther(null, true); // 全部縮小
-
- } else {
- partsSelectionManager.setMinimizeModeIfOther(partsCategory, true);
- if (partsCategory != null) {
- // 対象のパネルがスクロールペイン内に見える用にスクロールする.
- // スクロールバーの位置指定などの座標系の操作は「要求」であり、実際に適用されるまで本当の位置は分らない。
- // よって以下の処理は非同期に行い、先に座標を確定させたものに対して行う必要がある。
- final ImageSelectPanel panel = imageSelectPanels.findByPartsCategory(partsCategory);
- SwingUtilities.invokeLater(new Runnable() {
- public void run() {
- final Point pt = panel.getLocation();
- JViewport viewPort = imgSelectPanelsPanelSp.getViewport();
- viewPort.setViewPosition(pt);
- viewPort.revalidate();
- }
- });
- }
- }
- }
- }
-
- /**
- * デフォルトパーツを選択する.<br>
- * デフォルトパーツがなければお気に入りの最初のものを選択する.<br>
- * それもなければ空として表示する.<br>
- * パーツの適用に失敗した場合はfalseを返します.(例外は返されません.)<br>
- *
- * @param force
- * すでに選択があっても選択しなおす場合はtrue、falseの場合は選択があれば何もしない.
- * @return パーツ選択された場合。force=trueの場合はエラーがなければ常にtrueとなります。
- */
- protected boolean showDefaultParts(boolean force) {
- try {
- if (!force) {
- // 現在選択中のパーツを取得する.(なければ空)
- PartsSet sel = partsSelectionManager.createPartsSet();
- if (!sel.isEmpty()) {
- // 強制選択でない場合、すでに選択済みのパーツがあれば何もしない.
- return false;
- }
- }
-
- // デフォルトのパーツセットを取得する
- String defaultPresetId = characterData.getDefaultPartsSetId();
- PartsSet partsSet = null;
- if (defaultPresetId != null) {
- partsSet = characterData.getPartsSets().get(defaultPresetId);
- }
-
- // デフォルトのパーツセットがなければ、お気に入りの最初を選択する.
- if (partsSet == null) {
- List<PartsSet> partssets = getPartsSetList();
- if (!partssets.isEmpty()) {
- partsSet = partssets.get(0);
- }
- }
-
- // パーツセットがあれば、それを表示要求する.
- // パーツセットがなければカラーダイアログを初期化するのみ
- if (partsSet == null) {
- partsColorCoordinator.initColorDialog();
-
- } else {
- selectPresetParts(partsSet);
- }
-
- } catch (Exception ex) {
- logger.log(Level.WARNING, "パーツのデフォルト適用に失敗しました。", ex);
- return false;
- }
- return true;
- }
-
- /**
- * プリセットを適用しキャラクターイメージを再構築します.<br>
- * 実行時エラーは画面のレポートされます.<br>
- *
- * @param presetParts
- * パーツセット, nullの場合は何もしない.
- */
- protected void selectPresetParts(PartsSet presetParts) {
- if (presetParts == null) {
- return;
- }
- try {
- // 最後に使用したプリセットとして記憶する.
- lastUsePresetParts = presetParts;
- // プリセットパーツで選択を変える
- partsSelectionManager.selectPartsSet(presetParts);
- // カラーパネルを選択されているアイテムをもとに再設定する
- partsColorCoordinator.initColorDialog();
- // 再表示
- requestPreview();
-
- } catch (Exception ex) {
- ErrorMessageHelper.showErrorDialog(this, ex);
- }
- }
-
- /**
- * プリセットとお気に入りを表示順に並べて返す.
- *
- * @return プリセットとお気に入りのリスト(表示順)
- */
- protected List<PartsSet> getPartsSetList() {
- ArrayList<PartsSet> partssets = new ArrayList<PartsSet>();
- partssets.addAll(characterData.getPartsSets().values());
- Collections.sort(partssets, PartsSet.DEFAULT_COMPARATOR);
- return partssets;
- }
-
- protected static final class TreeLeaf implements Comparable<TreeLeaf> {
-
- public enum TreeLeafType {
- NODE, LEAF
- }
-
- private String name;
-
- private TreeLeafType typ;
-
- public TreeLeaf(TreeLeafType typ, String name) {
- if (name == null) {
- name = "";
- }
- this.typ = typ;
- this.name = name;
- }
-
- public String getName() {
- return name;
- }
-
- public TreeLeafType getTyp() {
- return typ;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj != null && obj instanceof TreeLeaf) {
- TreeLeaf o = (TreeLeaf) obj;
- return typ == o.typ && name.equals(o.name);
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return typ.hashCode() ^ name.hashCode();
- }
-
- public int compareTo(TreeLeaf o) {
- int ret = name.compareTo(o.name);
- if (ret == 0) {
- ret = (typ.ordinal() - o.typ.ordinal());
- }
- return ret;
- }
-
- @Override
- public String toString() {
- return name;
- }
- }
-
- protected TreeMap<TreeLeaf, Object> buildFavoritesItemTree(
- List<PartsSet> partssets) {
- if (partssets == null) {
- partssets = Collections.emptyList();
- }
- TreeMap<TreeLeaf, Object> favTree = new TreeMap<TreeLeaf, Object>();
- for (PartsSet partsSet : partssets) {
- String flatname = partsSet.getLocalizedName();
- String[] tokens = flatname.split("\\|");
- if (tokens.length == 0) {
- continue;
- }
-
- TreeMap<TreeLeaf, Object> r = favTree;
- for (int idx = 0; idx < tokens.length - 1; idx++) {
- String name = tokens[idx];
- TreeLeaf leafName = new TreeLeaf(TreeLeaf.TreeLeafType.NODE,
- name);
- @SuppressWarnings("unchecked")
- TreeMap<TreeLeaf, Object> n = (TreeMap<TreeLeaf, Object>) r
- .get(leafName);
- if (n == null) {
- n = new TreeMap<TreeLeaf, Object>();
- r.put(leafName, n);
- }
- r = n;
- }
- String lastName = tokens[tokens.length - 1];
- TreeLeaf lastLeafName = new TreeLeaf(TreeLeaf.TreeLeafType.LEAF,
- lastName);
- @SuppressWarnings("unchecked")
- List<PartsSet> leafValue = (List<PartsSet>) r.get(lastLeafName);
- if (leafValue == null) {
- leafValue = new ArrayList<PartsSet>();
- r.put(lastLeafName, leafValue);
- }
- leafValue.add(partsSet);
- }
- return favTree;
- }
-
- protected interface FavoriteMenuItemBuilder {
- JMenuItem createFavoriteMenuItem(String name, PartsSet partsSet);
- JMenu createSubMenu(String name);
- }
-
- private void buildFavoritesMenuItems(List<JMenuItem> menuItems,
- FavoriteMenuItemBuilder favMenuItemBuilder,
- TreeMap<TreeLeaf, Object> favTree) {
- for (Map.Entry<TreeLeaf, Object> entry : favTree.entrySet()) {
- TreeLeaf treeLeaf = entry.getKey();
- String name = treeLeaf.getName();
- if (treeLeaf.getTyp() == TreeLeaf.TreeLeafType.LEAF) {
- // 葉ノードには、JMenuItemを設定する.
- @SuppressWarnings("unchecked")
- List<PartsSet> leafValue = (List<PartsSet>) entry.getValue();
- for (final PartsSet partsSet : leafValue) {
- JMenuItem favoriteMenu = favMenuItemBuilder
- .createFavoriteMenuItem(name, partsSet);
- menuItems.add(favoriteMenu);
- }
-
- } else if (treeLeaf.getTyp() == TreeLeaf.TreeLeafType.NODE) {
- // 枝ノードは、サブメニューを作成し、子ノードを設定する
- @SuppressWarnings("unchecked")
- TreeMap<TreeLeaf, Object> childNode = (TreeMap<TreeLeaf, Object>) entry
- .getValue();
- JMenu subMenu = favMenuItemBuilder.createSubMenu(name);
- menuItems.add(subMenu);
- ArrayList<JMenuItem> subMenuItems = new ArrayList<JMenuItem>();
- buildFavoritesMenuItems(subMenuItems, favMenuItemBuilder, childNode);
- for (JMenuItem subMenuItem : subMenuItems) {
- subMenu.add(subMenuItem);
- }
-
- } else {
- throw new RuntimeException("unknown type: " + treeLeaf);
- }
- }
- }
-
- /**
- * お気に入りのJMenuItemを作成するファンクションオブジェクト
- */
- private FavoriteMenuItemBuilder favMenuItemBuilder = new FavoriteMenuItemBuilder() {
- private MenuBuilder menuBuilder = new MenuBuilder();
-
- /**
- * お気に入りメニューの作成
- */
- public JMenuItem createFavoriteMenuItem(final String name,
- final PartsSet partsSet) {
- JMenuItem favoriteMenu = menuBuilder.createJMenuItem();
- favoriteMenu.setName(partsSet.getPartsSetId());
- favoriteMenu.setText(name);
- if (partsSet.isPresetParts()) {
- Font font = favoriteMenu.getFont();
- favoriteMenu.setFont(font.deriveFont(Font.BOLD));
- }
- favoriteMenu.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- selectPresetParts(partsSet);
- }
- });
-
- // メニューアイテム上でマウスホイールを動かした場合は上下にスクロールさせる.
- // (ただし、OSXのスクリーンメニュー使用時は無視する.)
- addMouseWheelListener(favoriteMenu);
-
- return favoriteMenu;
- }
-
- /**
- * サブメニューの作成
- */
- public JMenu createSubMenu(String name) {
- JMenu menu = menuBuilder.createJMenu();
- menu.setText(name);
-
- // メニューアイテム上でマウスホイールを動かした場合は上下にスクロールさせる.
- // (ただし、OSXのスクリーンメニュー使用時は無視する.)
- addMouseWheelListener(menu);
-
- return menu;
- }
-
- /**
- * メニューアイテム上でホイールを上下させたときにメニューをスクロールさせるためのホイールハンドラを設定する.
- *
- * @param favoriteMenu
- */
- protected void addMouseWheelListener(final JMenuItem favoriteMenu) {
- if (JScrollableMenu.isScreenMenu()) {
- return;
- }
- favoriteMenu.addMouseWheelListener(new MouseWheelListener() {
- public void mouseWheelMoved(MouseWheelEvent e) {
- int rotation = e.getWheelRotation();
- JPopupMenu popupMenu = (JPopupMenu) favoriteMenu
- .getParent();
- JMenu parentMenu = (JMenu) popupMenu.getInvoker();
- if (parentMenu != null
- && parentMenu instanceof JScrollableMenu) {
- final JScrollableMenu favMenu = (JScrollableMenu) parentMenu;
- favMenu.doScroll(rotation < 0);
- }
- e.consume();
- }
- });
- }
- };
-
- /**
- * お気に入りメニューが開いたとき
- *
- * @param menu
- */
- protected void onSelectedFavoriteMenu(JMenu menu) {
- // 表示順にソート
- List<PartsSet> partssets = getPartsSetList();
- TreeMap<TreeLeaf, Object> favTree = buildFavoritesItemTree(partssets);
-
- // メニューの再構築
- ArrayList<JMenuItem> favoritesMenuItems = new ArrayList<JMenuItem>();
- buildFavoritesMenuItems(favoritesMenuItems, favMenuItemBuilder, favTree);
-
- if (menu instanceof JScrollableMenu) {
- // スクロールメニューの場合
- JScrollableMenu favMenu = (JScrollableMenu) menu;
-
- // スクロールメニューの初期化
- favMenu.initScroller();
-
- // スクロールメニューアイテムの設定
- favMenu.setScrollableItems(favoritesMenuItems);
-
- // 高さを補正する
- // お気に入りメニューが選択された場合、
- // お気に入りアイテム一覧を表示するよりも前に
- // 表示可能なアイテム数を現在のウィンドウの高さから算定する.
- Toolkit tk = Toolkit.getDefaultToolkit();
- Dimension scrsiz = tk.getScreenSize();
- int height = scrsiz.height; // MainFrame.this.getHeight();
- favMenu.adjustMaxVisible(height);
- logger.log(Level.FINE,
- "scrollableMenu maxVisible=" + favMenu.getMaxVisible());
-
- } else {
- // 通常メニューの場合
- // 既存メニューの位置をセパレータより判断する.
- int mx = menu.getMenuComponentCount();
- int separatorIdx = -1;
- for (int idx = 0; idx < mx; idx++) {
- Component item = menu.getMenuComponent(idx);
- if (item instanceof JSeparator) {
- separatorIdx = idx;
- break;
- }
- }
- // 既存メニューの削除
- if (separatorIdx > 0) {
- while (menu.getMenuComponentCount() > separatorIdx + 1) {
- menu.remove(separatorIdx + 1);
- }
- }
-
- // お気に入りアイテムのメニューを登録する.
- for (JMenuItem menuItem : favoritesMenuItems) {
- menu.add(menuItem);
- }
- }
-
- }
-
- /**
- * ヘルプメニューを開いたときにお勧めメニューを構築する.
- *
- * @param menu
- */
- protected void onSelectedRecommendationMenu(JMenu mnuRecomendation) {
- // 現在のお勧めメニューを一旦削除
- while (mnuRecomendation.getMenuComponentCount() > 0) {
- mnuRecomendation.remove(0);
- }
-
- // お勧めリンクの定義がない場合はデフォルトを用いる.(明示的な空の場合は何もしない.)
- CharacterDataPersistent persist = CharacterDataPersistent.getInstance();
- persist.compensateRecommendationList(characterData);
-
- // お勧めリンクメニューを作成する.
- List<RecommendationURL> recommendations = characterData.getRecommendationURLList();
- if (recommendations != null) {
- MenuBuilder menuBuilder = new MenuBuilder();
- for (RecommendationURL recommendation : recommendations) {
- String displayName = recommendation.getDisplayName();
- String url = recommendation.getUrl();
-
- JMenuItem mnuItem = menuBuilder.createJMenuItem();
- mnuItem.setText(displayName);
- mnuItem.addActionListener(
- DesktopUtilities.createBrowseAction(MainFrame.this, url, displayName)
- );
- mnuRecomendation.add(mnuItem);
- }
- }
-
- // お勧めリンクメニューのリストがnullでなく空でもない場合は有効、そうでなければ無効にする.
- mnuRecomendation.setEnabled(recommendations != null && !recommendations.isEmpty());
- }
-
-
- /**
- * 最後に選択されたお気に入りと同じ構成であれば、 このお気に入りの名前をプレビューペインのタイトルに設定する.<br>
- * そうでなければデフォルトのパーツセット名(no titleとか)を表示する.<br>
- * 色情報が異なる場合に末尾に「*」マークがつけられる.<br>
- *
- * @param requestPartsSet
- * 表示するパーツセット(名前は設定されていなくて良い。お気に入り側を使うので。), nullの場合はデフォルトのパーツ名
- */
- protected void showPresetName(PartsSet requestPartsSet) {
- String title = getSuggestPartsSetName(requestPartsSet, true);
- if (title == null) {
- title = defaultPartsSetTitle;
- }
- previewPane.setTitle(title);
- }
-
- /**
- * パーツセット名を推定する.<br>
- * 最後に選択されたお気に入りと同じ構成であれば、 このお気に入りの名前を返す.<br>
- * お気に入りが選択されていないか構成が異なる場合、お気に入りに名前がない場合はnullを返す.<br>
- *
- * @param requestPartsSet
- * 表示するパーツセット(名前は設定されていなくて良い。お気に入り側を使うので。)
- * @param markColorChange
- * 色情報が異なる場合に末尾に「*」マークをつける場合はtrue
- */
- private String getSuggestPartsSetName(PartsSet requestPartsSet, boolean markColorChange) {
- String partsSetTitle = null;
- if (lastUsePresetParts != null &&
- PartsSet.isSameStructure(requestPartsSet, lastUsePresetParts)) {
- partsSetTitle = lastUsePresetParts.getLocalizedName();
- if (markColorChange && !PartsSet.isSameColor(requestPartsSet, lastUsePresetParts)) {
- if (partsSetTitle != null) {
- partsSetTitle += "*";
- }
- }
- }
- if (partsSetTitle != null && partsSetTitle.trim().length() > 0) {
- return partsSetTitle;
- }
- return null;
- }
-
- /**
- * プレビューの更新を要求する. 更新は非同期に行われる.
- */
- protected void requestPreview() {
- if (!characterData.isValid()) {
- return;
- }
-
- // 選択されているパーツの各イメージを取得しレイヤー順に並び替えて合成する.
- // 合成は別スレッドにて非同期に行われる.
- // リクエストは随時受け付けて、最新のリクエストだけが処理される.
- // (処理がはじまる前に新しいリクエストで上書きされた場合、前のリクエストは単に捨てられる.)
- imageBuilder.requestJob(new ImageBuildJobAbstractAdaptor(characterData) {
-
- /**
- * 構築するパーツセット情報
- */
- private PartsSet requestPartsSet;
-
- /**
- * 非同期のイメージ構築要求の番号.<br>
- */
- private long ticket;
-
- @Override
- public void onQueueing(long ticket) {
- this.ticket = ticket;
- previewPane.setLoadingRequest(ticket);
- }
- @Override
- public void buildImage(ImageOutput output) {
- // 合成結果のイメージを引数としてイメージビルダから呼び出される.
- final BufferedImage img = output.getImageOutput();
- Runnable refreshJob = new Runnable() {
- public void run() {
- previewPane.setPreviewImage(img);
- previewPane.setLoadingComplete(ticket);
- showPresetName(requestPartsSet);
- }
- };
- if (SwingUtilities.isEventDispatchThread()) {
- refreshJob.run();
- } else {
- try {
- SwingUtilities.invokeAndWait(refreshJob);
- } catch (Exception ex) {
- logger.log(Level.WARNING, "build image failed.", ex);
- }
- }
- }
- @Override
- public void handleException(final Throwable ex) {
- // 合成中に例外が発生した場合、イメージビルダから呼び出される.
- Runnable showExceptionJob = new Runnable() {
- public void run() {
- ErrorMessageHelper.showErrorDialog(MainFrame.this, ex);
- }
- };
- if (SwingUtilities.isEventDispatchThread()) {
- showExceptionJob.run();
- } else {
- SwingUtilities.invokeLater(showExceptionJob);
- }
- }
- @Override
- protected PartsSet getPartsSet() {
- // 合成できる状態になった時点でイメージビルダから呼び出される.
- final PartsSet[] result = new PartsSet[1];
- Runnable collectPartsSetJob = new Runnable() {
- public void run() {
- PartsSet partsSet = partsSelectionManager.createPartsSet();
- result[0] = partsSet;
- }
- };
- if (SwingUtilities.isEventDispatchThread()) {
- collectPartsSetJob.run();
- } else {
- try {
- // スレッドによるSwingのイベントディスパッチスレッド以外からの呼び出しの場合、
- // Swingディスパッチスレッドでパーツの選択状態を取得する.
- SwingUtilities.invokeAndWait(collectPartsSetJob);
-
- } catch (InvocationTargetException e) {
- throw new RuntimeException(e.getMessage(), e);
- } catch (InterruptedException e) {
- throw new RuntimeException("interrupted:" + e, e);
- }
- }
- if (logger.isLoggable(Level.FINE)) {
- logger.log(Level.FINE, "preview: " + result[0]);
- }
- requestPartsSet = result[0];
- return requestPartsSet;
- }
- });
- }
-
- /**
- * プロファイルを開く
- */
- protected void onOpenProfile() {
- try {
- MainFrame main2 = ProfileListManager.openProfile(this);
- if (main2 != null) {
- main2.showMainFrame();
- }
-
- } catch (Exception ex) {
- ErrorMessageHelper.showErrorDialog(this, ex);
- }
- }
-
- /**
- * 背景色を変更する.
- */
- protected void onChangeBgColor() {
- getJMenuBar().setEnabled(false);
- try {
- Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
- .getLocalizedProperties(STRINGS_RESOURCE);
-
- Color color = wallpaperInfo.getBackgroundColor();
- color = JColorChooser.showDialog(this, strings.getProperty("chooseBgColor"), color);
- if (color != null) {
- applyBackgroundColorOnly(color);
- }
- } finally {
- getJMenuBar().setEnabled(true);
- }
- }
-
- /**
- * 壁紙を変更する.
- */
- protected void onChangeWallpaper() {
- try {
- WallpaperDialog wallpaperDialog = new WallpaperDialog(this);
-
- // 最後に使用した壁紙情報でダイアログを設定する.
- wallpaperDialog.setWallpaperInfo(this.wallpaperInfo);
-
- // 壁紙情報を設定するモーダルダイアログを開く
- WallpaperInfo wallpaperInfo = wallpaperDialog.showDialog();
- if (wallpaperInfo == null) {
- return;
- }
-
- // 壁紙情報を保存し、その情報をもとに背景を再描画する.
- applyWallpaperInfo(wallpaperInfo, false);
-
- } catch (WallpaperFactoryException ex) {
- ErrorMessageHelper.showErrorDialog(this, ex);
-
- } catch (RuntimeException ex) {
- ErrorMessageHelper.showErrorDialog(this, ex);
- }
- }
-
- /**
- * 背景色のみ変更し、背景を再描画する.<br>
- * 壁紙情報全体の更新よりも効率化するためのメソッドである.<br>
- *
- * @param bgColor
- * 背景色
- */
- protected void applyBackgroundColorOnly(Color bgColor) {
- wallpaperInfo.setBackgroundColor(bgColor);
- previewPane.getWallpaper()
- .setBackgroundColor(wallpaperInfo.getBackgroundColor());
- }
-
- /**
- * 壁紙情報を保存し、その情報をもとに背景を再描画する.<br>
- * ignoreErrorがtrueである場合、適用に失敗した場合はログに記録するのみで、 壁紙情報は保存されず、壁紙も更新されない.<br>
- *
- * @param wallpaperInfo
- * 壁紙情報、null不可
- * @param ignoreError
- * 失敗を無視する場合
- * @throws IOException
- * 失敗
- */
- protected void applyWallpaperInfo(WallpaperInfo wallpaperInfo, boolean ignoreError) throws WallpaperFactoryException {
- if (wallpaperInfo == null) {
- throw new IllegalArgumentException();
- }
- // 壁紙情報から壁紙インスタンスを生成する.
- WallpaperFactory wallpaperFactory = WallpaperFactory.getInstance();
- Wallpaper wallpaper = null;
-
- try {
- // 壁紙情報の構築時に問題が発生した場合、
- // 回復処理をして継続するかエラーとするか?
- WallpaperFactoryErrorRecoverHandler handler = null;
- if (ignoreError) {
- handler = new WallpaperFactoryErrorRecoverHandler();
- }
-
- // 壁紙情報
- wallpaper = wallpaperFactory.createWallpaper(wallpaperInfo, handler);
-
- } catch (WallpaperFactoryException ex) {
- logger.log(Level.WARNING, "壁紙情報の適用に失敗しました。", ex);
- if ( !ignoreError) {
- throw ex;
- }
-
- } catch (RuntimeException ex) {
- logger.log(Level.WARNING, "壁紙情報の適用に失敗しました。", ex);
- if ( !ignoreError) {
- throw ex;
- }
- }
-
- if (wallpaper == null) {
- return;
- }
-
- // 壁紙を更新する.
- previewPane.setWallpaper(wallpaper);
-
- // 壁紙情報として記憶する.
- this.wallpaperInfo = wallpaperInfo;
- }
-
- /**
- * プレビューしている画像をファイルに保存する。 サポートしているのはPNG/JPEGのみ。
- */
- protected void onSavePicture() {
- Toolkit tk = Toolkit.getDefaultToolkit();
- BufferedImage img = previewPane.getPreviewImage();
- Color imgBgColor = wallpaperInfo.getBackgroundColor();
- if (img == null) {
- tk.beep();
- return;
- }
-
- try {
- // 出力オプションの調整
- OutputOption outputOption = imageSaveHelper.getOutputOption();
- outputOption.setZoomFactor(previewPane.getZoomFactor());
- outputOption.changeRecommend();
- imageSaveHelper.setOutputOption(outputOption);
-
- // ファイルダイアログ表示
- File outFile = imageSaveHelper.showSaveFileDialog(this);
- if (outFile == null) {
- return;
- }
- logger.log(Level.FINE, "savePicture: " + outFile);
- logger.log(Level.FINE, "outputOption: " + outputOption);
-
- // 画像のファイルへの出力
- StringBuilder warnings = new StringBuilder();
-
- setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
- try {
- imageSaveHelper.savePicture(img, imgBgColor, outFile, warnings);
-
- } finally {
- setCursor(Cursor.getDefaultCursor());
- }
- if (warnings.length() > 0) {
- JOptionPane.showMessageDialog(this, warnings.toString(), "WARNINGS", JOptionPane.WARNING_MESSAGE);
- }
-
- } catch (Exception ex) {
- ErrorMessageHelper.showErrorDialog(this, ex);
- }
- }
-
- /**
- * 伺か用PNG/PNAの出力.
- */
- protected void onSaveAsUkagaka() {
- BufferedImage img = previewPane.getPreviewImage();
- Color bgColor = wallpaperInfo.getBackgroundColor();
- if (img == null) {
- Toolkit tk = Toolkit.getDefaultToolkit();
- tk.beep();
- return;
- }
-
- try {
- ukagakaImageSaveHelper.save(this, img, bgColor);
-
- } catch (IOException ex) {
- ErrorMessageHelper.showErrorDialog(this, ex);
- }
- }
-
- /**
- * 伺か用PNG/PNAの変換
- */
- protected void onConvertUkagaka() {
- try {
- Color colorKey = wallpaperInfo.getBackgroundColor();
- ukagakaImageSaveHelper.convertChooseFiles(this, colorKey);
-
- } catch (IOException ex) {
- ErrorMessageHelper.showErrorDialog(this, ex);
- }
- }
-
- /**
- * プロファイルの場所を開く
- */
- protected void onBrowseProfileDir() {
- if (!characterData.isValid()) {
- Toolkit tk = Toolkit.getDefaultToolkit();
- tk.beep();
- return;
- }
- try {
- DesktopUtilities.browseBaseDir(characterData.getDocBase());
-
- } catch (Exception ex) {
- ErrorMessageHelper.showErrorDialog(this, ex);
- }
- }
-
- /**
- * このプロファイルを編集する.
- */
- protected void onEditProfile() {
- if (!characterData.isValid()) {
- Toolkit tk = Toolkit.getDefaultToolkit();
- tk.beep();
- return;
- }
- try {
- CharacterData cd = this.characterData;
- CharacterData newCd = ProfileListManager.editProfile(this, cd);
- if (newCd != null) {
- CharacterDataChangeObserver.getDefault()
- .notifyCharacterDataChange(this, newCd, true, true);
- }
-
- } catch (Exception ex) {
- ErrorMessageHelper.showErrorDialog(this, ex);
- }
- }
-
- /**
- * パーツの管理ダイアログを開く.<br>
- */
- protected void onManageParts() {
- if (!characterData.isValid()) {
- Toolkit tk = Toolkit.getDefaultToolkit();
- tk.beep();
- return;
- }
-
- PartsManageDialog mrgDlg = new PartsManageDialog(this, characterData);
- mrgDlg.setVisible(true);
-
- if (mrgDlg.isUpdated()) {
- // パーツ管理情報が更新された場合、
- // パーツデータをリロードする.
- if (characterData.reloadPartsData()) {
- partsSelectionManager.loadParts();
- requestPreview();
- }
- }
- }
-
- /**
- * 「パーツ検索」ダイアログを開く.<br>
- * すでに開いているダイアログがあれば、それにフォーカスを当てる.<br>
- */
- protected void openSearchDialog() {
- if (!characterData.isValid()) {
- Toolkit tk = Toolkit.getDefaultToolkit();
- tk.beep();
- return;
- }
-
- if (lastUseSearchPartsDialog != null) {
- // 開いているダイアログがあれば、それにフォーカスを当てる.
- if (lastUseSearchPartsDialog.isDisplayable() && lastUseSearchPartsDialog.isVisible()) {
- lastUseSearchPartsDialog.requestFocus();
- return;
- }
- }
-
- SearchPartsDialog searchPartsDlg = new SearchPartsDialog(this, characterData, partsSelectionManager);
- WindowAdjustLocationSupport.alignRight(this, searchPartsDlg, 0, true);
- searchPartsDlg.setVisible(true);
- lastUseSearchPartsDialog = searchPartsDlg;
- }
-
- /**
- * 「パーツ検索」ダイアログを閉じる.<br>
- */
- protected void closeSearchDialog() {
- lastUseSearchPartsDialog = null;
- for (SearchPartsDialog dlg : SearchPartsDialog.getDialogs()) {
- if (dlg != null && dlg.isDisplayable() && dlg.getParent() == this) {
- dlg.dispose();
- }
- }
- }
-
- /**
- * 「お気に入りの管理」ダイアログを閉じる
- */
- protected void closeManageFavoritesDialog() {
- if (lastUseManageFavoritesDialog != null) {
- if (lastUseManageFavoritesDialog.isDisplayable()) {
- lastUseManageFavoritesDialog.dispose();
- }
- lastUseManageFavoritesDialog = null;
- }
- }
-
- /**
- * 「パーツのランダム選択ダイアログ」を閉じる
- */
- protected void closePartsRandomChooserDialog() {
- if (lastUsePartsRandomChooserDialog != null) {
- if (lastUsePartsRandomChooserDialog.isDisplayable()) {
- lastUsePartsRandomChooserDialog.dispose();
- }
- lastUsePartsRandomChooserDialog = null;
- }
- }
-
- /**
- * クリップボードにコピー
- *
- * @param screenImage
- * スクリーンイメージ
- */
- protected void onCopy(boolean screenImage) {
- try {
- BufferedImage img = previewPane.getPreviewImage();
- if (img == null) {
- Toolkit tk = Toolkit.getDefaultToolkit();
- tk.beep();
- return;
- }
-
- if (screenImage) {
- // 表示している内容をそのままコピーする.
- img = previewPane.getScreenImage();
- }
-
- Color imgBgColor = wallpaperInfo.getBackgroundColor();
- ClipboardUtil.setImage(img, imgBgColor);
-
- } catch (Exception ex) {
- ErrorMessageHelper.showErrorDialog(this, ex);
- }
- }
-
- /**
- * アプリケーションの設定ダイアログを開く
- */
- public void onPreferences() {
- AppConfigDialog appConfigDlg = new AppConfigDialog(this);
- appConfigDlg.setVisible(true);
- }
-
- /**
- * 新規モードでインポートウィザードを実行する.<br>
- */
- protected void onImportNew() {
- if (!characterData.isValid()) {
- Toolkit tk = Toolkit.getDefaultToolkit();
- tk.beep();
- return;
- }
-
- try {
- // インポートウィザードの実行(新規モード)
- ImportWizardDialog importWizDialog = new ImportWizardDialog(this, null, null);
- importWizDialog.setVisible(true);
- int exitCode = importWizDialog.getExitCode();
- if (exitCode == ImportWizardDialog.EXIT_PROFILE_CREATED) {
- CharacterData cd = importWizDialog.getImportedCharacterData();
- if (cd != null && cd.isValid()) {
- // インポートしたキャラクターデータのプロファイルを開く.
- setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
- try {
- MainFrame mainFrame = ProfileListManager.openProfile(cd);
- mainFrame.setVisible(true);
-
- } finally {
- setCursor(Cursor.getDefaultCursor());
- }
- }
- }
-
- } catch (Exception ex) {
- ErrorMessageHelper.showErrorDialog(this, ex);
- }
- }
-
- /**
- * 現在のプロファイルに対するインポートウィザードを実行する.<br>
- * インポートが実行された場合は、パーツをリロードする.<br>
- * インポートウィザード表示中は監視スレッドは停止される.<br>
- *
- * @param initFile
- * アーカイブファィルまたはディレクトリ、指定がなければnull
- */
- protected void onImport(List<File> initFiles) {
- if (!characterData.isValid()) {
- Toolkit tk = Toolkit.getDefaultToolkit();
- tk.beep();
- return;
- }
-
- try {
- watchAgent.suspend();
- try {
- // インポートウィザードの実行
- ImportWizardDialog importWizDialog = new ImportWizardDialog(this, characterData, initFiles);
- importWizDialog.setVisible(true);
-
- if (importWizDialog.getExitCode() == ImportWizardDialog.EXIT_PROFILE_UPDATED) {
- CharacterData importedCd = importWizDialog.getImportedCharacterData();
- CharacterDataChangeObserver.getDefault()
- .notifyCharacterDataChange(this, importedCd,
- false, true);
- }
-
- } finally {
- watchAgent.resume();
- }
-
- } catch (Exception ex) {
- ErrorMessageHelper.showErrorDialog(this, ex);
- }
- }
-
- /**
- * パーツとお気に入りをリロードする.<br>
- * まだロードされていない場合はあらたにロードする.<br>
- * 引数newCdが指定されている場合は、現在のキャラクター定義の説明文を更新する.<br>
- * (説明文の更新以外には使用されない.)<br>
- *
- * @param newCd
- * 説明文更新のための更新されたキャラクターデータを指定する。null可
- * @param forceRepaint
- * 必ず再描画する場合
- * @throws IOException
- * 失敗
- */
- protected synchronized void reloadPartsAndFavorites(CharacterData newCd,
- boolean forceRepaint) throws IOException {
- if (newCd != null) {
- // (インポート画面では説明文のみ更新するので、それだけ取得)
- characterData.setDescription(newCd.getDescription());
- }
-
- if ( !characterData.isPartsLoaded()) {
- // キャラクターデータが、まだ読み込まれていなければ読み込む.
- ProfileListManager.loadCharacterData(characterData);
- ProfileListManager.loadFavorites(characterData);
- partsSelectionManager.loadParts();
-
- } else {
- // パーツデータをリロードする.
- if (characterData.reloadPartsData()) {
- partsSelectionManager.loadParts();
- }
-
- // お気に入りをリロードする.
- CharacterDataPersistent persiste = CharacterDataPersistent.getInstance();
- persiste.loadFavorites(characterData);
-
- // お気に入りが更新されたことを通知する.
- FavoritesChangeObserver.getDefault().notifyFavoritesChange(
- MainFrame.this, characterData);
- }
-
- // 現在選択されているパーツセットがない場合はデフォルトのパーツセットを選択する.
- if (showDefaultParts(false) || forceRepaint) {
- requestPreview();
- }
- }
-
- protected void onExport() {
- if (!characterData.isValid()) {
- Toolkit tk = Toolkit.getDefaultToolkit();
- tk.beep();
- return;
- }
- ExportWizardDialog exportWizDlg = new ExportWizardDialog(this, characterData, previewPane.getPreviewImage());
- exportWizDlg.setVisible(true);
- }
-
- protected void onResetColor() {
- if (!characterData.isValid()) {
- Toolkit tk = Toolkit.getDefaultToolkit();
- tk.beep();
- return;
- }
-
- Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
- .getLocalizedProperties(STRINGS_RESOURCE);
-
- if (JOptionPane.showConfirmDialog(this, strings.get("confirm.resetcolors"), strings.getProperty("confirm"),
- JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) != JOptionPane.YES_OPTION) {
- return;
- }
- characterData.getPartsColorManager().resetPartsColorInfo();
- partsColorCoordinator.initColorDialog();
- requestPreview();
- }
-
- /**
- * プロファイルを閉じる.
- */
- protected void onCloseProfile() {
- saveWorkingSet();
- ProfileListManager.unregisterUsedCharacterData(characterData);
-
- if (characterData.isValid()) {
-
- // 最後に使用したキャラクターデータとして記憶する.
- try {
- RecentDataPersistent recentPersist = RecentDataPersistent.getInstance();
- recentPersist.saveRecent(characterData);
-
- } catch (Exception ex) {
- logger.log(Level.WARNING, "recent data saving failed.", ex);
- // recent情報の記録に失敗しても致命的ではないので、これは無視する.
- }
- }
-
- // イメージビルダスレッド・ディレクトリ監視スレッドを停止する.
- stopAgents();
-
- // フレームウィンドウを破棄する.
- dispose();
-
- // 破棄されたことをロギングする.
- logger.log(Level.FINE, "dispose mainframe.");
- }
-
- /**
- * 開いている、すべてのプロファイルを閉じる.<br>
- * (Mac OS Xのcmd+Qで閉じる場合などで使用される.)<br>
- */
- public static void closeAllProfiles() {
- // ウィンドウが閉じられることでアクティブなフレームが切り替わる場合を想定し、
- // 現在のアクティブなウィンドウをあらかじめ記憶しておく
- MainFrame mainFrame = activedMainFrame;
-
- // gcをかけてファイナライズを促進させる
- SystemUtil.gc();
-
- // ファイナライズされていないFrameのうち、ネイティブリソースと関連づけられている
- // フレームについて、それがMainFrameのインスタンスであれば閉じる.
- // ただし、現在アクティブなものは除く
- for (Frame frame : JFrame.getFrames()) {
- try {
- if (frame.isDisplayable()) {
- // ネイティブリソースと関連づけられているフレーム
- if (frame instanceof MainFrame && frame != mainFrame) {
- // MainFrameのインスタンスであるので閉じる処理が可能.
- // (現在アクティブなメインフレームは最後に閉じるため、ここでは閉じない.)
- ((MainFrame) frame).onCloseProfile();
- }
- }
-
- } catch (Throwable ex) {
- logger.log(Level.SEVERE, "mainframe closing failed.", ex);
- // フレームを閉じるときに失敗した場合、通常、致命的問題だが
- // クローズ処理は継続しなければならない.
- }
- }
-
- // 現在アクティブなフレームを閉じる.
- // 最後に閉じることで「最後に使ったプロファイル」として記憶させる.
- if (activedMainFrame != null && activedMainFrame.isDisplayable()) {
- try {
- activedMainFrame.onCloseProfile();
-
- } catch (Throwable ex) {
- logger.log(Level.SEVERE, "mainframe closing failed.", ex);
- // フレームを閉じるときに失敗した場合、通常、致命的問題だが
- // クローズ処理は継続しなければならない.
- }
- }
- }
-
- /**
- * 画面の作業状態を保存する.
- */
- protected void saveWorkingSet() {
- if (!characterData.isValid()) {
- return;
- }
- try {
- // ワーキングセットの作成
- WorkingSet workingSet = new WorkingSet();
- workingSet.setCharacterDocBase(characterData.getDocBase());
- workingSet.setCharacterDataRev(characterData.getRev());
- PartsSet partsSet = partsSelectionManager.createPartsSet();
- workingSet.setPartsSet(partsSet);
- workingSet.setPartsColorInfoMap(characterData
- .getPartsColorManager().getPartsColorInfoMap());
- workingSet.setLastUsedSaveDir(imageSaveHelper.getLastUsedSaveDir());
- workingSet.setLastUsedExportDir(ExportWizardDialog.getLastUsedDir());
- workingSet.setLastUsePresetParts(lastUsePresetParts);
- workingSet
- .setCharacterData(characterData.duplicateBasicInfo(false)); // パーツセットは保存しない.
- workingSet.setWallpaperInfo(wallpaperInfo);
-
- workingSet.setZoomFactor(previewPane.getZoomFactor());
- workingSet.setViewPosition(previewPane.getViewPosition());
-
- Dimension windowSize = getSize();
- Point windowPos = getLocation();
- Rectangle windowRect = new Rectangle(windowPos, windowSize);
- workingSet.setWindowRect(windowRect);
-
- // XML形式でのワーキングセットの保存
- WorkingSetPersist workingSetPersist = WorkingSetPersist
- .getInstance();
- workingSetPersist.saveWorkingSet(workingSet);
-
- } catch (Exception ex) {
- ErrorMessageHelper.showErrorDialog(this, ex);
- }
- }
-
- /**
- * 画面の作業状態を復元する.
- *
- * @return ワーキングセットを読み込んだ場合はtrue、そうでなければfalse
- */
- protected boolean loadWorkingSet() {
- if (!characterData.isValid()) {
- return false;
- }
- try {
- WorkingSetPersist workingSetPersist = WorkingSetPersist
- .getInstance();
- WorkingSet2 workingSet2 = workingSetPersist
- .loadWorkingSet(characterData);
- if (workingSet2 == null) {
- // ワーキングセットがない場合.
- return false;
- }
-
- AppConfig appConfig = AppConfig.getInstance();
- Rectangle windowRect = workingSet2.getWindowRect();
- if (appConfig.isEnableRestoreWindow() && windowRect != null) {
- // 位置の復元
- GraphicsEnvironment genv = GraphicsEnvironment.getLocalGraphicsEnvironment();
- Rectangle desktopSize = genv.getMaximumWindowBounds(); // メインスクリーンのサイズ(デスクトップ領域のみ)
- Point windowPos = windowRect.getLocation();
- if (desktopSize.contains(windowPos)) {
- setLocation(windowPos);
- }
-
- // サイズの復元
- Dimension dim = windowRect.getSize();
- if (dim.width < 100) {
- dim.width = 100;
- }
- if (dim.height < 100) {
- dim.height = 100;
- }
- setSize(dim);
- } else {
- // デフォルトのウィンドウ位置とサイズ
- setDefaultWindowLocation();
- }
-
- URI docBase = characterData.getDocBase();
- if (docBase != null
- && !docBase.equals(workingSet2.getCharacterDocBase())) {
- // docBaseが一致せず
- return false;
- }
- String sig = characterData.toSignatureString();
- if (!sig.equals(workingSet2.getCharacterDataSig())) {
- // 構造が一致せず.
- return false;
- }
-
- // パーツの色情報を復元する.
- Map<PartsIdentifier, PartsColorInfo> partsColorInfoMap = characterData
- .getPartsColorManager().getPartsColorInfoMap();
- workingSet2.createCompatible(characterData, partsColorInfoMap);
-
- // 選択されているパーツの復元
- IndependentPartsSetInfo partsSetInfo = workingSet2
- .getCurrentPartsSet();
- if (partsSetInfo != null) {
- PartsSet partsSet = IndependentPartsSetInfo.convertPartsSet(
- partsSetInfo, characterData, false);
- selectPresetParts(partsSet);
-
- // 最後に選択したお気に入り情報の復元
- IndependentPartsSetInfo lastUsePresetPartsInfo = workingSet2
- .getLastUsePresetParts();
- if (lastUsePresetPartsInfo != null
- && lastUsePresetPartsInfo.getId() != null
- && lastUsePresetPartsInfo.getId().trim().length() > 0) {
- PartsSet lastUsePresetParts = IndependentPartsSetInfo
- .convertPartsSet(lastUsePresetPartsInfo,
- characterData, false);
- if (lastUsePresetParts.isSameStructure(partsSet)) {
- this.lastUsePresetParts = lastUsePresetParts;
- showPresetName(lastUsePresetParts);
- }
- }
- }
-
- // 最後に保存したディレクトリを復元する.
- imageSaveHelper.setLastUseSaveDir(workingSet2.getLastUsedSaveDir());
- ExportWizardDialog.setLastUsedDir(workingSet2
- .getLastUsedExportDir());
-
- // 壁紙情報を取得する.
- WallpaperInfo wallpaperInfo = workingSet2.getWallpaperInfo();
- if (wallpaperInfo != null) {
- // 壁紙情報を保存し、その情報をもとに背景を再描画する.
- // (適用に失敗した場合はエラーは無視し、壁紙情報は保存しない.)
- applyWallpaperInfo(wallpaperInfo, true);
- }
-
- // ズーム状態を復元する
- Double zoomFactor = workingSet2.getZoomFactor();
- if (appConfig.isEnableRestoreWindow() && zoomFactor != null) {
- previewPane.setZoomFactor(zoomFactor);
- final Point viewPosition = workingSet2.getViewPosition();
- if (viewPosition != null) {
- previewPane.setViewPosition(viewPosition);
- }
- }
-
- return true;
-
- } catch (Exception ex) {
- ErrorMessageHelper.showErrorDialog(this, ex);
- }
- return false;
- }
-
-
- public void onAbout() {
- try {
- AboutBox aboutBox = new AboutBox(this);
- aboutBox.showAboutBox();
-
- } catch (Exception ex) {
- ErrorMessageHelper.showErrorDialog(this, ex);
- }
- }
-
- protected void onHelp() {
- Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
- .getLocalizedProperties(STRINGS_RESOURCE);
- String helpURL = strings.getProperty("help.url");
- String helpDescription = strings.getProperty("help.show");
- DesktopUtilities.browse(this, helpURL, helpDescription);
- }
-
- protected void onFlipHolizontal() {
- if (!characterData.isValid()) {
- Toolkit tk = Toolkit.getDefaultToolkit();
- tk.beep();
- return;
- }
-
- double[] affineTransformParameter = partsSelectionManager.getAffineTransformParameter();
- if (affineTransformParameter == null) {
- // 左右フリップするアフィン変換パラメータを構築する.
- Dimension siz = characterData.getImageSize();
- if (siz != null) {
- affineTransformParameter = new double[] {-1., 0, 0, 1., siz.width, 0};
- }
- } else {
- // アフィン変換パラメータをクリアする.
- affineTransformParameter = null;
- }
- partsSelectionManager.setAffineTransformParameter(affineTransformParameter);
- requestPreview();
- }
-
- protected void onSetDefaultPicture() {
- if (!characterData.isValid()) {
- Toolkit tk = Toolkit.getDefaultToolkit();
- tk.beep();
- return;
- }
- try {
- BufferedImage samplePicture = previewPane.getPreviewImage();
- if (samplePicture != null) {
- CharacterDataPersistent persist = CharacterDataPersistent.getInstance();
- persist.saveSamplePicture(characterData, samplePicture);
- }
-
- } catch (Exception ex) {
- ErrorMessageHelper.showErrorDialog(this, ex);
- }
- }
-
- protected void onInformation() {
- if (!characterData.isValid()) {
- Toolkit tk = Toolkit.getDefaultToolkit();
- tk.beep();
- return;
- }
-
- PartsSet partsSet = partsSelectionManager.createPartsSet();
- InformationDialog infoDlg = new InformationDialog(this, characterData, partsSet);
- infoDlg.setVisible(true);
- }
-
- protected void onManageFavorites() {
- if (!characterData.isValid()) {
- Toolkit tk = Toolkit.getDefaultToolkit();
- tk.beep();
- return;
- }
-
- if (lastUseManageFavoritesDialog != null) {
- // 開いているダイアログがあれば、それにフォーカスを当てる.
- if (lastUseManageFavoritesDialog.isDisplayable()
- && lastUseManageFavoritesDialog.isVisible()) {
- lastUseManageFavoritesDialog.requestFocus();
- return;
- }
- }
-
- // お気に入り編集ダイアログを開く
- ManageFavoriteDialog dlg = new ManageFavoriteDialog(this, characterData);
- dlg.setFavoriteManageCallback(new FavoriteManageCallback() {
-
- public void selectFavorites(PartsSet partsSet) {
- // お気に入り編集ダイアログで選択されたパーツを選択表示する.
- selectPresetParts(partsSet);
- }
-
- public void updateFavorites(CharacterData characterData,
- boolean savePreset) {
- // お気に入りを登録する.
- try {
- setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
- try {
- CharacterDataPersistent persiste = CharacterDataPersistent
- .getInstance();
- if (savePreset) {
- persiste.updateProfile(characterData);
- }
-
- persiste.saveFavorites(characterData);
-
- // お気に入りが更新されたことを通知する.
- FavoritesChangeObserver.getDefault()
- .notifyFavoritesChange(MainFrame.this,
- characterData);
-
- } finally {
- setCursor(Cursor.getDefaultCursor());
- }
-
- } catch (Exception ex) {
- ErrorMessageHelper.showErrorDialog(MainFrame.this, ex);
- }
- }
- });
- WindowAdjustLocationSupport.alignRight(this, dlg, 0, true);
- dlg.setVisible(true);
- lastUseManageFavoritesDialog = dlg;
- }
-
- protected void onRegisterFavorite() {
- if (!characterData.isValid()) {
- Toolkit tk = Toolkit.getDefaultToolkit();
- tk.beep();
- return;
- }
- try {
- // パーツセットを生成
- PartsSet partsSet = partsSelectionManager.createPartsSet();
- if (partsSet.isEmpty()) {
- // 空のパーツセットは登録しない.
- return;
- }
-
- Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
- .getLocalizedProperties(STRINGS_RESOURCE);
-
- // お気に入りに登録するパーツセットが最後に使用したお気に入りと同じ構成であれば、
- // そのお気に入り名を使用する.
- String initName = getSuggestPartsSetName(partsSet, false);
-
- // カラー情報の有無のチェックボックス.
- JCheckBox chkColorInfo = new JCheckBox(strings.getProperty("input.favoritesColorInfo"));
- chkColorInfo.setSelected(true);
- String partsSetId = null;
- if (initName != null && lastUsePresetParts != null) {
- partsSetId = lastUsePresetParts.getPartsSetId();
- }
-
- // 上書き保存の可否のチェックボックス
- JCheckBox chkOverwrite = new JCheckBox(strings.getProperty("input.favoritesOverwrite"));
- chkOverwrite.setSelected(partsSetId != null && partsSetId.length() > 0);
- chkOverwrite.setEnabled(partsSetId != null && partsSetId.length() > 0);
-
- // チェックボックスパネル
- Box checkboxsPanel = new Box(BoxLayout.PAGE_AXIS);
- checkboxsPanel.add(chkColorInfo);
- checkboxsPanel.add(chkOverwrite);
-
- // 入力ダイアログを開く
- String name = (String) JOptionPane.showInputDialog(this,
- checkboxsPanel,
- strings.getProperty("input.favorites"),
- JOptionPane.QUESTION_MESSAGE,
- null,
- null,
- initName == null ? "" : initName);
- if (name == null || name.trim().length() == 0) {
- return;
- }
-
- boolean includeColorInfo = chkColorInfo.isSelected();
- if (!includeColorInfo) {
- // カラー情報を除去する.
- partsSet.removeColorInfo();
- }
-
- // 新規の場合、もしくは上書きしない場合はIDを設定する.
- if (partsSetId == null || !chkOverwrite.isSelected()) {
- partsSetId = "ps" + UUID.randomUUID().toString();
- }
- partsSet.setPartsSetId(partsSetId);
-
- // 名前を設定する.
- partsSet.setLocalizedName(name);
-
- // ファイルに保存
- setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
- try {
- CharacterDataPersistent persiste = CharacterDataPersistent.getInstance();
- // 現在の最新情報を取り出す.
- characterData.clearPartsSets(true);
- persiste.loadFavorites(characterData);
-
- // お気に入りコレクションに登録
- characterData.addPartsSet(partsSet);
-
- persiste.saveFavorites(characterData);
-
- // お気に入りが更新されたことを通知する.
- FavoritesChangeObserver.getDefault().notifyFavoritesChange(
- MainFrame.this, characterData);
-
- } finally {
- setCursor(Cursor.getDefaultCursor());
- }
-
- // 最後に選択したお気に入りにする
- lastUsePresetParts = partsSet;
- showPresetName(partsSet);
-
- } catch (Exception ex) {
- ErrorMessageHelper.showErrorDialog(this, ex);
- }
- }
-
- /**
- * ランダム選択ダイアログを開く.
- */
- protected void onToolRandom() {
- if (!characterData.isValid()) {
- Toolkit tk = Toolkit.getDefaultToolkit();
- tk.beep();
- return;
- }
-
- if (lastUsePartsRandomChooserDialog != null) {
- // 開いているダイアログがあれば、それにフォーカスを当てる.
- if (lastUsePartsRandomChooserDialog.isDisplayable()
- && lastUsePartsRandomChooserDialog.isVisible()) {
- lastUsePartsRandomChooserDialog.requestFocus();
- return;
- }
- }
-
- // お気に入り編集ダイアログを開く
- PartsRandomChooserDialog dlg = new PartsRandomChooserDialog(this,
- characterData,
- new PartsRandomChooserDialog.PartsSetSynchronizer() {
- public PartsSet getCurrentPartsSet() {
- // 現在のパーツセットを生成
- return partsSelectionManager.createPartsSet();
- }
-
- public void setPartsSet(PartsSet partsSet) {
- selectPresetParts(partsSet);
- }
-
- public boolean
- isExcludePartsIdentifier(PartsIdentifier partsIdentifier) {
- Boolean exclude = randomExcludePartsIdentifierMap
- .get(partsIdentifier);
- return exclude != null && exclude.booleanValue();
- }
-
- public void
- setExcludePartsIdentifier(PartsIdentifier partsIdentifier,
- boolean exclude) {
- randomExcludePartsIdentifierMap.put(partsIdentifier,
- exclude);
- }
- });
-
- WindowAdjustLocationSupport.alignRight(this, dlg, 0, true);
- dlg.setVisible(true);
- lastUsePartsRandomChooserDialog = dlg;
- }
-
- /**
- * ランダム選択パーツで選択候補から除外するパーツのマップ.
- */
- private HashMap<PartsIdentifier, Boolean> randomExcludePartsIdentifierMap =
- new HashMap<PartsIdentifier, Boolean>();
-
- /**
- * すべての解除可能なパーツの選択を解除する。
- */
- protected void onDeselectAll() {
- partsSelectionManager.deselectAll();
- }
-
- /**
- * 単一選択カテゴリのパーツの解除を許可する。
- */
- protected void onDeselectableAllCategory() {
- partsSelectionManager
- .setDeselectableSingleCategory( !partsSelectionManager
- .isDeselectableSingleCategory());
- }
-
- /**
- * プレビューのズームボックスの表示制御
- */
- protected void onEnableZoom() {
- previewPane.setVisibleZoomBox( !previewPane.isVisibleZoomBox());
- }
-
- /**
- * メニューバーを構築します.
- *
- * @return メニューバー
- */
- protected JMenuBar createMenuBar() {
- final Properties strings = LocalizedResourcePropertyLoader
- .getCachedInstance().getLocalizedProperties(STRINGS_RESOURCE);
-
- MenuDataFactory[] menus = new MenuDataFactory[] {
- new MenuDataFactory("menu.file", new MenuDataFactory[] {
- new MenuDataFactory("file.openProfile", new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- onOpenProfile();
- }
- }),
- new MenuDataFactory("file.savePicture", new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- onSavePicture();
- }
- }),
- new MenuDataFactory("file.ukagaka", new MenuDataFactory[] {
- new MenuDataFactory("file.saveAsUkagaka", new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- onSaveAsUkagaka();
- };
- }),
- new MenuDataFactory("file.convertUkagaka", new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- onConvertUkagaka();
- };
- }),
- }),
- null,
- new MenuDataFactory("file.editprofile", new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- onEditProfile();
- }
- }),
- new MenuDataFactory("file.opendir", new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- onBrowseProfileDir();
- }
- }),
- new MenuDataFactory("file.import", new MenuDataFactory[] {
- new MenuDataFactory("file.importMe", new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- onImport(null);
- };
- }),
- new MenuDataFactory("file.importNew", new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- onImportNew();
- };
- }),
- }),
- new MenuDataFactory("file.export", new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- onExport();
- };
- }),
- new MenuDataFactory("file.manageParts", new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- onManageParts();
- }
- }),
- new MenuDataFactory("file.preferences", new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- onPreferences();
- };
- }),
- null,
- new MenuDataFactory("file.closeProfile", new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- onCloseProfile();
- }
- }),
- }),
- new MenuDataFactory("menu.edit", new MenuDataFactory[] {
- new MenuDataFactory("edit.search", new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- openSearchDialog();
- }
- }),
- new MenuDataFactory("edit.copy", new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- onCopy((e.getModifiers() & ActionEvent.SHIFT_MASK) != 0);
- }
- }),
- new MenuDataFactory("edit.flipHorizontal", new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- onFlipHolizontal();
- }
- }),
- new MenuDataFactory("edit.resetcolor", new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- onResetColor();
- }
- }),
- null,
- new MenuDataFactory("edit.setDefaultPicture", new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- onSetDefaultPicture();
- }
- }),
- new MenuDataFactory("edit.information", new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- onInformation();
- }
- }),
- null,
- new MenuDataFactory("edit.deselectall", new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- onDeselectAll();
- }
- }),
- new MenuDataFactory("edit.deselectparts", true, new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- onDeselectableAllCategory();
- }
- }),
- new MenuDataFactory("edit.enableAutoShrink", true, new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- onClickPartsCategoryTitle(null, true);
- }
- }),
- null,
- new MenuDataFactory("edit.enableZoomBox", true, new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- onEnableZoom();
- }
- }),
- null,
- new MenuDataFactory("edit.changeBgColor", new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- onChangeBgColor();
- }
- }),
- new MenuDataFactory("edit.changeWallpaper", new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- onChangeWallpaper();
- }
- }),
- }),
- new MenuDataFactory("menu.favorite", new MenuDataFactory[] {
- new MenuDataFactory("favorite.register", new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- onRegisterFavorite();
- }
- }),
- new MenuDataFactory("favorite.manage", new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- onManageFavorites();
- }
- }),
- null,
- }),
- new MenuDataFactory("menu.tool",
- new MenuDataFactory[]{new MenuDataFactory(
- "tool.random", new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- onToolRandom();
- }
- }),}),
- new MenuDataFactory("menu.help", new MenuDataFactory[] {
- new MenuDataFactory("help.recommendations", (ActionListener) null),
- null,
- new MenuDataFactory("help.help", new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- onHelp();
- }
- }),
- new MenuDataFactory("help.forum",
- DesktopUtilities.createBrowseAction(
- MainFrame.this,
- strings.getProperty("help.forum.url"),
- strings.getProperty("help.forum.description"))
- ),
- new MenuDataFactory("help.bugreport",
- DesktopUtilities.createBrowseAction(
- MainFrame.this,
- strings.getProperty("help.reportbugs.url"),
- strings.getProperty("help.reportbugs.description"))
- ),
- new MenuDataFactory("help.about", new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- onAbout();
- }
- }),
- }), };
-
- final MenuBuilder menuBuilder = new MenuBuilder();
-
- JMenuBar menuBar = menuBuilder.createMenuBar(menus);
-
- menuBuilder.getJMenu("menu.edit").addMenuListener(new MenuListener() {
- public void menuCanceled(MenuEvent e) {
- // do nothing.
- }
- public void menuDeselected(MenuEvent e) {
- // do nothing.
- }
- public void menuSelected(MenuEvent e) {
- menuBuilder.getJMenuItem("edit.copy").setEnabled(previewPane.getPreviewImage() != null);
- menuBuilder.getJMenuItem("edit.deselectparts").setSelected(
- partsSelectionManager.isDeselectableSingleCategory());
- menuBuilder.getJMenuItem("edit.enableAutoShrink").setSelected(minimizeMode);
- menuBuilder.getJMenuItem("edit.enableZoomBox").setSelected(previewPane.isVisibleZoomBox());
- }
- });
- final JMenu mnuFavorites = menuBuilder.getJMenu("menu.favorite");
- mnuFavorites.addMenuListener(new MenuListener() {
- public void menuCanceled(MenuEvent e) {
- // do nothing.
- }
- public void menuDeselected(MenuEvent e) {
- // do nothing.
- }
- public void menuSelected(MenuEvent e) {
- onSelectedFavoriteMenu(mnuFavorites);
- }
- });
-
- // J2SE5の場合は「パーツディレクトリを開く」コマンドは使用不可とする.
- if (System.getProperty("java.version").startsWith("1.5")) {
- menuBuilder.getJMenuItem("file.opendir").setEnabled(false);
- }
-
- // お勧めサイトメニュー構築
- final JMenu mnuRecomendation = menuBuilder.getJMenu("help.recommendations");
- JMenu mnuHelp = menuBuilder.getJMenu("menu.help");
- mnuHelp.addMenuListener(new MenuListener() {
- public void menuCanceled(MenuEvent e) {
- // do nothing.
- }
- public void menuDeselected(MenuEvent e) {
- // do nothing.
- }
- public void menuSelected(MenuEvent e) {
- onSelectedRecommendationMenu(mnuRecomendation);
- }
- });
-
- return menuBar;
- }
-
-}
+package charactermanaj.ui;\r
+\r
+import static java.lang.Math.*;\r
+\r
+import java.awt.BorderLayout;\r
+import java.awt.Color;\r
+import java.awt.Component;\r
+import java.awt.Container;\r
+import java.awt.Cursor;\r
+import java.awt.Dialog.ModalityType;\r
+import java.awt.Dimension;\r
+import java.awt.Font;\r
+import java.awt.Frame;\r
+import java.awt.GraphicsEnvironment;\r
+import java.awt.Point;\r
+import java.awt.Rectangle;\r
+import java.awt.Toolkit;\r
+import java.awt.dnd.DropTarget;\r
+import java.awt.event.ActionEvent;\r
+import java.awt.event.ActionListener;\r
+import java.awt.event.MouseWheelEvent;\r
+import java.awt.event.MouseWheelListener;\r
+import java.awt.event.WindowAdapter;\r
+import java.awt.event.WindowEvent;\r
+import java.awt.image.BufferedImage;\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.lang.reflect.InvocationTargetException;\r
+import java.net.URI;\r
+import java.util.ArrayList;\r
+import java.util.Collections;\r
+import java.util.HashMap;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.Properties;\r
+import java.util.TreeMap;\r
+import java.util.UUID;\r
+import java.util.logging.Level;\r
+import java.util.logging.Logger;\r
+\r
+import javax.swing.Box;\r
+import javax.swing.BoxLayout;\r
+import javax.swing.JCheckBox;\r
+import javax.swing.JCheckBoxMenuItem;\r
+import javax.swing.JColorChooser;\r
+import javax.swing.JFrame;\r
+import javax.swing.JMenu;\r
+import javax.swing.JMenuBar;\r
+import javax.swing.JMenuItem;\r
+import javax.swing.JOptionPane;\r
+import javax.swing.JPanel;\r
+import javax.swing.JPopupMenu;\r
+import javax.swing.JScrollBar;\r
+import javax.swing.JScrollPane;\r
+import javax.swing.JSeparator;\r
+import javax.swing.JSplitPane;\r
+import javax.swing.JViewport;\r
+import javax.swing.SwingUtilities;\r
+import javax.swing.event.AncestorEvent;\r
+import javax.swing.event.AncestorListener;\r
+import javax.swing.event.MenuEvent;\r
+import javax.swing.event.MenuListener;\r
+\r
+import charactermanaj.Main;\r
+import charactermanaj.clipboardSupport.ClipboardUtil;\r
+import charactermanaj.graphics.AsyncImageBuilder;\r
+import charactermanaj.graphics.ColorConvertedImageCachedLoader;\r
+import charactermanaj.graphics.ImageBuildJobAbstractAdaptor;\r
+import charactermanaj.graphics.ImageBuilder.ImageOutput;\r
+import charactermanaj.graphics.io.ImageSaveHelper;\r
+import charactermanaj.graphics.io.OutputOption;\r
+import charactermanaj.graphics.io.UkagakaImageSaveHelper;\r
+import charactermanaj.model.AppConfig;\r
+import charactermanaj.model.CharacterData;\r
+import charactermanaj.model.CharacterDataChangeEvent;\r
+import charactermanaj.model.CharacterDataChangeListener;\r
+import charactermanaj.model.CharacterDataChangeObserver;\r
+import charactermanaj.model.ColorGroup;\r
+import charactermanaj.model.CustomLayerOrder;\r
+import charactermanaj.model.IndependentPartsSetInfo;\r
+import charactermanaj.model.PartsCategory;\r
+import charactermanaj.model.PartsColorInfo;\r
+import charactermanaj.model.PartsColorManager;\r
+import charactermanaj.model.PartsIdentifier;\r
+import charactermanaj.model.PartsSet;\r
+import charactermanaj.model.RecommendationURL;\r
+import charactermanaj.model.WorkingSet;\r
+import charactermanaj.model.WorkingSet2;\r
+import charactermanaj.model.io.CharacterDataPersistent;\r
+import charactermanaj.model.io.CustomLayerOrderPersist;\r
+import charactermanaj.model.io.CustomLayerOrderPersist.CustomLayerOrderPersistListener;\r
+import charactermanaj.model.io.PartsImageCollectionParser.LayerOrderMapper;\r
+import charactermanaj.model.io.PartsImageDirectoryWatchAgent;\r
+import charactermanaj.model.io.PartsImageDirectoryWatchAgentFactory;\r
+import charactermanaj.model.io.PartsImageDirectoryWatchEvent;\r
+import charactermanaj.model.io.PartsImageDirectoryWatchListener;\r
+import charactermanaj.model.io.RecentDataPersistent;\r
+import charactermanaj.model.io.WorkingSetPersist;\r
+import charactermanaj.ui.ImageSelectPanel.ImageSelectPanelEvent;\r
+import charactermanaj.ui.ImageSelectPanel.ImageSelectPanelListener;\r
+import charactermanaj.ui.LayerOrderCustomizeDialog.LayerOrderCustomizeListener;\r
+import charactermanaj.ui.ManageFavoriteDialog.FavoriteManageCallback;\r
+import charactermanaj.ui.PreviewPanel.PreviewPanelEvent;\r
+import charactermanaj.ui.PreviewPanel.PreviewPanelListener;\r
+import charactermanaj.ui.model.ColorChangeEvent;\r
+import charactermanaj.ui.model.ColorChangeListener;\r
+import charactermanaj.ui.model.ColorGroupCoordinator;\r
+import charactermanaj.ui.model.CustomLayerPatternMgr;\r
+import charactermanaj.ui.model.FavoritesChangeEvent;\r
+import charactermanaj.ui.model.FavoritesChangeListener;\r
+import charactermanaj.ui.model.FavoritesChangeObserver;\r
+import charactermanaj.ui.model.PartsColorCoordinator;\r
+import charactermanaj.ui.model.PartsSelectionManager;\r
+import charactermanaj.ui.model.WallpaperFactory;\r
+import charactermanaj.ui.model.WallpaperFactoryErrorRecoverHandler;\r
+import charactermanaj.ui.model.WallpaperFactoryException;\r
+import charactermanaj.ui.model.WallpaperInfo;\r
+import charactermanaj.ui.scrollablemenu.JScrollableMenu;\r
+import charactermanaj.ui.util.FileDropTarget;\r
+import charactermanaj.ui.util.WindowAdjustLocationSupport;\r
+import charactermanaj.util.DesktopUtilities;\r
+import charactermanaj.util.ErrorMessageHelper;\r
+import charactermanaj.util.LocalizedResourcePropertyLoader;\r
+import charactermanaj.util.SystemUtil;\r
+import charactermanaj.util.UIHelper;\r
+\r
+\r
+/**\r
+ * メインフレーム.<br>\r
+ * アプリケーションがアクティブである場合は最低でも1つのメインフレームが表示されている.<br>\r
+ *\r
+ * @author seraphy\r
+ */\r
+public class MainFrame extends JFrame\r
+ implements\r
+ FavoritesChangeListener,\r
+ CharacterDataChangeListener,\r
+ CustomLayerOrderPersistListener {\r
+\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ private static final Logger logger = Logger.getLogger(MainFrame.class.getName());\r
+\r
+\r
+ protected static final String STRINGS_RESOURCE = "languages/mainframe";\r
+\r
+ protected static final String MENU_STRINGS_RESOURCE = "menu/menu";\r
+\r
+ /**\r
+ * メインフレームのアイコン.<br>\r
+ */\r
+ protected BufferedImage icon;\r
+\r
+\r
+ /**\r
+ * 現在アクティブなメインフレーム.<br>\r
+ * フォーカスが切り替わるたびにアクティブフレームを追跡する.<br>\r
+ * Mac OS XのAbout/Preferences/Quitのシステムメニューからよびだされた場合に\r
+ * オーナーたるべきメインフレームを識別するためのもの.<br>\r
+ */\r
+ private static volatile MainFrame activedMainFrame;\r
+\r
+\r
+ /**\r
+ * このメインフレームが対象とするキャラクターデータ.<br>\r
+ */\r
+ protected CharacterData characterData;\r
+\r
+\r
+ /**\r
+ * プレビューペイン\r
+ */\r
+ private PreviewPanel previewPane;\r
+\r
+ /**\r
+ * パーツ選択マネージャ\r
+ */\r
+ protected PartsSelectionManager partsSelectionManager;\r
+\r
+ /**\r
+ * パネルの最小化モード\r
+ */\r
+ private boolean minimizeMode;\r
+\r
+\r
+ /**\r
+ * パーツ選択パネルリスト\r
+ */\r
+ protected ImageSelectPanelList imageSelectPanels;\r
+\r
+ /**\r
+ * パーツ選択パネルを納めるスクロールペイン\r
+ */\r
+ protected JScrollPane imgSelectPanelsPanelSp;\r
+\r
+ /**\r
+ * カラーグループのマネージャ\r
+ */\r
+ protected ColorGroupCoordinator colorGroupCoordinator;\r
+\r
+ /**\r
+ * パーツカラーのマネージャ\r
+ */\r
+ protected PartsColorCoordinator partsColorCoordinator;\r
+\r
+\r
+ /**\r
+ * キャッシュつきのイメージローダ.<br>\r
+ */\r
+ private ColorConvertedImageCachedLoader imageLoader;\r
+\r
+ /**\r
+ * パーツを組み立てて1つのプレビュー可能なイメージを構築するためのビルダ\r
+ */\r
+ private AsyncImageBuilder imageBuilder;\r
+\r
+\r
+ /**\r
+ * パーツイメージを画像として保存する場合のヘルパー.<br>\r
+ * 最後に使ったディレクトリを保持するためのメンバ変数としている.<br>\r
+ */\r
+ private ImageSaveHelper imageSaveHelper = new ImageSaveHelper();\r
+\r
+ /**\r
+ * 伺か用出力ヘルパ.<br>\r
+ * 最後に使ったディレクトリ、ファイル名、モードなどを保持するためのメンバ変数としている.<br>\r
+ */\r
+ private UkagakaImageSaveHelper ukagakaImageSaveHelper = new UkagakaImageSaveHelper();\r
+\r
+ /**\r
+ * パーツディレクトリを定期的にチェックし、パーツイメージが変更・追加・削除されている場合に パーツリストを更新するためのウォッチャー\r
+ */\r
+ private PartsImageDirectoryWatchAgent watchAgent;\r
+\r
+ /**\r
+ * デフォルトのパーツセット表示名\r
+ */\r
+ private String defaultPartsSetTitle;\r
+\r
+ /**\r
+ * 最後に使用したプリセット.<br>\r
+ * (一度もプリセットを使用していなければnull).\r
+ */\r
+ private PartsSet lastUsePresetParts;\r
+\r
+ /**\r
+ * 最後に使用した検索ダイアログ.<br>\r
+ * nullであれば一度も使用していない.<br>\r
+ * (nullでなくとも閉じられている可能性がある.)<br>\r
+ */\r
+ private SearchPartsDialog lastUseSearchPartsDialog;\r
+\r
+ /**\r
+ * 最後に使用したお気に入りダイアログ.<br>\r
+ * nullであれば一度も使用していない.<br>\r
+ * (nullでなくとも閉じられている可能性がある.)\r
+ */\r
+ private ManageFavoriteDialog lastUseManageFavoritesDialog;\r
+\r
+ /**\r
+ * 最後に使用したパーツのランダム選択ダイアログ.<br>\r
+ * nullであれば一度も使用していない.<br>\r
+ * (nullでなくとも閉じられている可能性がある.)\r
+ */\r
+ private PartsRandomChooserDialog lastUsePartsRandomChooserDialog;\r
+\r
+ /**\r
+ * 最後に使用した壁紙情報\r
+ */\r
+ private WallpaperInfo wallpaperInfo;\r
+\r
+ /**\r
+ * カスタムレイヤーパターンのリストを管理する\r
+ */\r
+ private final CustomLayerPatternMgr customLayerPatternMgr = new CustomLayerPatternMgr();\r
+\r
+ /**\r
+ * アクティブなメインフレームを設定する.\r
+ *\r
+ * @param mainFrame\r
+ * メインフレーム\r
+ */\r
+ public static void setActivedMainFrame(MainFrame mainFrame) {\r
+ if (mainFrame == null) {\r
+ throw new IllegalArgumentException();\r
+ }\r
+ activedMainFrame = mainFrame;\r
+ }\r
+\r
+ /**\r
+ * 現在アクティブなメインフレームを取得する. まだメインフレームが開かれていない場合はnull.<br>\r
+ * 最後のメインフレームが破棄中、もしくは破棄済みであれば破棄されたフレームを示すことに注意.<br>\r
+ *\r
+ * @return メインフレーム、もしくはnull\r
+ */\r
+ public static MainFrame getActivedMainFrame() {\r
+ return activedMainFrame;\r
+ }\r
+\r
+ /**\r
+ * キャラクターデータが変更された場合に通知される.\r
+ */\r
+ public void notifyChangeCharacterData(final CharacterDataChangeEvent e) {\r
+ final CharacterData cd = e.getCharacterData();\r
+ if (cd != null\r
+ && cd.getDocBase().equals(\r
+ MainFrame.this.characterData.getDocBase())) {\r
+ SwingUtilities.invokeLater(new Runnable() {\r
+ public void run() {\r
+ try {\r
+ Cursor oldCur = getCursor();\r
+ setCursor(Cursor\r
+ .getPredefinedCursor(Cursor.WAIT_CURSOR));\r
+ try {\r
+ if (e.isChangeStructure()) {\r
+ // 現在情報の保存\r
+ saveWorkingSet();\r
+\r
+ // 画面構成の再構築\r
+ initComponent(cd);\r
+ }\r
+\r
+ if (e.isReloadPartsAndFavorites()) {\r
+ // パーツとお気に入りのリロード\r
+ reloadPartsAndFavorites(cd, true);\r
+ }\r
+\r
+ } finally {\r
+ setCursor(oldCur != null ? oldCur : Cursor\r
+ .getDefaultCursor());\r
+ }\r
+\r
+ } catch (Exception ex) {\r
+ ErrorMessageHelper.showErrorDialog(MainFrame.this, ex);\r
+ }\r
+ }\r
+ });\r
+ }\r
+ }\r
+\r
+ /**\r
+ * お気に入りデータが変更された場合に通知される.\r
+ *\r
+ * @param e\r
+ */\r
+ @Override\r
+ public void notifyChangeFavorites(FavoritesChangeEvent e) {\r
+ CharacterData cd = e.getCharacterData();\r
+ if (cd != null\r
+ && cd.getDocBase().equals(\r
+ MainFrame.this.characterData.getDocBase())) {\r
+ if (!MainFrame.this.equals(e.getSource())\r
+ && !characterData.equals(cd)) {\r
+ // プリセットとお気に入りを最新化する.\r
+ // ただし、自分自身から送信したイベントの場合は最新化は不要.\r
+ characterData.clearPartsSets(false);\r
+ for (Map.Entry<String, PartsSet> entry : cd.getPartsSets()\r
+ .entrySet()) {\r
+ PartsSet partsSet = entry.getValue();\r
+ characterData.addPartsSet(partsSet);\r
+ }\r
+ }\r
+\r
+ // お気に入り管理ダイアログ上のお気に入り一覧を最新に更新する.\r
+ if (lastUseManageFavoritesDialog != null\r
+ && lastUseManageFavoritesDialog.isDisplayable()) {\r
+ lastUseManageFavoritesDialog.initListModel();\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 外部でカスタムレイヤーの定義が変更された場合に呼び出される\r
+ * @param e\r
+ */\r
+ @Override\r
+ public void notifyChangeCustomLayerOrder(CustomLayerOrderPersistListener.Change e) {\r
+ if (!e.getSource().equals(characterData)) { // 自分が送信元の場合は何もしない\r
+ Map<String, List<CustomLayerOrder>> map = e.getCustomLayerOrderMap();\r
+ if (map != null) {\r
+ customLayerPatternMgr.setMap(map);\r
+ requestPreview();\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * メインフレームを構築する.\r
+ *\r
+ * @param characterData\r
+ * キャラクターデータ\r
+ */\r
+ public MainFrame(CharacterData characterData) {\r
+ try {\r
+ if (characterData == null) {\r
+ throw new IllegalArgumentException();\r
+ }\r
+\r
+ setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);\r
+ addWindowListener(new WindowAdapter() {\r
+ @Override\r
+ public void windowClosing(WindowEvent e) {\r
+ onCloseProfile();\r
+ }\r
+ @Override\r
+ public void windowClosed(WindowEvent e) {\r
+ stopAgents();\r
+ }\r
+ @Override\r
+ public void windowActivated(WindowEvent e) {\r
+ setActivedMainFrame(MainFrame.this);\r
+ }\r
+ @Override\r
+ public void windowOpened(WindowEvent e) {\r
+ // do nothing.\r
+ }\r
+ });\r
+\r
+ // アイコンの設定\r
+ icon = UIHelper.getInstance().getImage("icons/icon.png");\r
+ setIconImage(icon);\r
+\r
+ // 画面コンポーネント作成\r
+ initComponent(characterData);\r
+ JMenuBar menuBar = createMenuBar();\r
+ setJMenuBar(menuBar);\r
+\r
+ // お気に入り変更通知を受け取る\r
+ FavoritesChangeObserver.getDefault().addFavoritesChangeListener(\r
+ this);\r
+ // キャラクターデータの変更通知を受け取る\r
+ CharacterDataChangeObserver.getDefault()\r
+ .addCharacterDataChangeListener(this);\r
+\r
+ // レイヤーパターンの変更通知を受け取る\r
+ CustomLayerOrderPersist.newInstance(characterData)\r
+ .addCustomLayerOrderPersistListener(this);\r
+\r
+ } catch (RuntimeException ex) {\r
+ logger.log(Level.SEVERE, "メインフレームの構築中に予期せぬ例外が発生しました。", ex);\r
+ dispose(); // コンストラクタが呼ばれた時点でJFrameは構築済みなのでdisposeの必要がある.\r
+ throw ex;\r
+ } catch (Error ex) {\r
+ logger.log(Level.SEVERE, "メインフレームの構築中に致命的な例外が発生しました。", ex);\r
+ dispose(); // コンストラクタが呼ばれた時点でJFrameは構築済みなのでdisposeの必要がある.\r
+ throw ex;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * デフォルトのウィンドウ位置とサイズの設定\r
+ */\r
+ private void setDefaultWindowLocation() {\r
+ // メインスクリーンサイズを取得する.\r
+ GraphicsEnvironment genv = GraphicsEnvironment.getLocalGraphicsEnvironment();\r
+ Rectangle desktopSize = genv.getMaximumWindowBounds(); // メインスクリーンのサイズ(デスクトップ領域のみ)\r
+ logger.log(Level.CONFIG, "desktopSize=" + desktopSize);\r
+\r
+ Dimension imageSize = characterData.getImageSize();\r
+ // 画像サイズ300x400を基準サイズとして、それ以下にはならない.\r
+ // アプリケーション設定の最大サイズ以上の場合はウィンドウサイズは固定してスクロールバーに任せる\r
+ AppConfig appConfig = AppConfig.getInstance();\r
+ int maxWidth = min(desktopSize.width, appConfig.getMainFrameMaxWidth());\r
+ int maxHeight = min(desktopSize.height, appConfig.getMainFrameMaxHeight());\r
+ int imageWidth = min(maxWidth, max(300, imageSize != null ? imageSize.width : 0));\r
+ int imageHeight = min(maxHeight, max(400, imageSize != null ? imageSize.height : 0));\r
+ // 300x400の画像の場合にメインフレームが600x550だとちょうどいい感じ.\r
+ // それ以上大きい画像の場合は増えた分だけフレームを大きくしておく.\r
+ setSize(imageWidth - 300 + 600, imageHeight - 400 + 550);\r
+\r
+ // 次回表示時にプラットフォーム固有位置に表示するように予約\r
+ setLocationByPlatform(true);\r
+ }\r
+\r
+ /**\r
+ * メインフレームを表示する.<br>\r
+ * デスクトップ領域からはみ出した場合は位置を補正する.<br>\r
+ */\r
+ public void showMainFrame() {\r
+ // メインスクリーンサイズを取得する.\r
+ GraphicsEnvironment genv = GraphicsEnvironment.getLocalGraphicsEnvironment();\r
+ Rectangle desktopSize = genv.getMaximumWindowBounds(); // メインスクリーンのサイズ(デスクトップ領域のみ)\r
+ logger.log(Level.CONFIG, "desktopSize=" + desktopSize);\r
+\r
+ // プラットフォーム固有の位置あわせで表示する.\r
+ // 表示した結果、はみ出している場合は0,0に補正する.\r
+ setVisible(true);\r
+ Point loc = getLocation();\r
+ logger.log(Level.CONFIG, "windowLocation=" + loc);\r
+ Dimension windowSize = getSize();\r
+ if (loc.y + windowSize.height >= desktopSize.height) {\r
+ loc.y = 0;\r
+ }\r
+ if (loc.x + windowSize.width >= desktopSize.width) {\r
+ loc.x = 0;\r
+ }\r
+ if (loc.x == 0 || loc.y == 0) {\r
+ setLocation(loc);\r
+ }\r
+\r
+ // デスクトップよりも大きい場合は小さくする.\r
+ boolean resize = false;\r
+ Dimension dim = getSize();\r
+ if (dim.height > desktopSize.height) {\r
+ dim.height = desktopSize.height;\r
+ resize = true;\r
+ }\r
+ if (dim.width > desktopSize.width) {\r
+ dim.width = desktopSize.width;\r
+ resize = true;\r
+ }\r
+ if (resize) {\r
+ setSize(dim);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * このメインフレームに関連づけられているエージェントスレッドを停止します.<br>\r
+ * すでに停止している場合は何もしません。\r
+ */\r
+ protected void stopAgents() {\r
+ // エージェントを停止\r
+ if (watchAgent != null) {\r
+ try {\r
+ watchAgent.disconnect();\r
+\r
+ } catch (Throwable ex) {\r
+ logger.log(Level.SEVERE, "フォルダ監視スレッドの停止に失敗しました。", ex);\r
+ }\r
+ watchAgent = null;\r
+ }\r
+ // イメージビルダを停止\r
+ if (imageBuilder != null) {\r
+ try {\r
+ imageBuilder.stop();\r
+\r
+ } catch (Throwable ex) {\r
+ logger.log(Level.SEVERE, "非同期イメージビルダスレッドの停止に失敗しました。", ex);\r
+ }\r
+ imageBuilder = null;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * メインフレームを破棄します.<br>\r
+ */\r
+ @Override\r
+ public void dispose() {\r
+ FavoritesChangeObserver.getDefault()\r
+ .removeFavoritesChangeListener(this);\r
+ CharacterDataChangeObserver.getDefault()\r
+ .removeCharacterDataChangeListener(this);\r
+ CustomLayerOrderPersist.newInstance(characterData)\r
+ .removeCustomLayerOrderPersistListener(this);\r
+\r
+ imageLoader.close();\r
+ stopAgents();\r
+ super.dispose();\r
+ }\r
+\r
+ /**\r
+ * 画面コンポーネントを設定します.<br>\r
+ * すでに設定されている場合は一旦削除されたのちに再作成されます.<br>\r
+ */\r
+ private void initComponent(CharacterData characterData) {\r
+\r
+ CharacterData oldCd;\r
+ synchronized (this) {\r
+ oldCd = this.characterData;\r
+ if (oldCd != null) {\r
+ // 使用中のキャラクターデータであることを登録解除する。\r
+ ProfileListManager.unregisterUsedCharacterData(oldCd);\r
+ }\r
+ this.characterData = characterData;\r
+\r
+ // 使用中のキャラクターデータであることを登録する.\r
+ ProfileListManager.registerUsedCharacterData(characterData);\r
+ }\r
+\r
+ // 設定まわり準備\r
+ AppConfig appConfig = AppConfig.getInstance();\r
+ Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()\r
+ .getLocalizedProperties(STRINGS_RESOURCE);\r
+\r
+ // タイトル表示\r
+ String title;\r
+ if (Main.isMacOSX()) {\r
+ // Mac OS Xの場合はウィンドウにタイトルはつけない。\r
+ title = "";\r
+ } else {\r
+ title = strings.getProperty("title");\r
+ }\r
+ setTitle(title + characterData.getName());\r
+\r
+ // デフォルトのパーツセット表示名\r
+ defaultPartsSetTitle = strings.getProperty("defaultPartsSetTitle");\r
+\r
+ // エージェントの停止\r
+ stopAgents();\r
+\r
+ // コンポーネント配置\r
+ Container contentPane = getContentPane();\r
+\r
+ // すでにあるコンポーネントを削除\r
+ for (Component comp : contentPane.getComponents()) {\r
+ contentPane.remove(comp);\r
+ }\r
+ // 開いている検索ダイアログを閉じる\r
+ closeSearchDialog();\r
+\r
+ // 開いているお気に入り管理ダイアログを閉じる\r
+ closeManageFavoritesDialog();\r
+\r
+ // 開いているランダム選択ダイアログを閉じる.\r
+ closePartsRandomChooserDialog();\r
+\r
+ PartsColorManager partsColorManager = characterData.getPartsColorManager();\r
+\r
+ // デフォルトの背景色の設定\r
+ Color bgColor = appConfig.getDefaultImageBgColor();\r
+ wallpaperInfo = new WallpaperInfo();\r
+ wallpaperInfo.setBackgroundColor(bgColor);\r
+\r
+ if (imageLoader != null) {\r
+ imageLoader.close();\r
+ }\r
+ imageLoader = new ColorConvertedImageCachedLoader();\r
+ imageBuilder = new AsyncImageBuilder(imageLoader);\r
+ partsSelectionManager = new PartsSelectionManager(partsColorManager,\r
+ new PartsSelectionManager.ImageBgColorProvider() {\r
+ public Color getImageBgColor() {\r
+ return wallpaperInfo.getBackgroundColor();\r
+ }\r
+ public void setImageBgColor(Color imageBgColor) {\r
+ applyBackgroundColorOnly(imageBgColor);\r
+ }\r
+ });\r
+ colorGroupCoordinator = new ColorGroupCoordinator(partsSelectionManager, partsColorManager);\r
+ partsColorCoordinator = new PartsColorCoordinator(characterData, partsColorManager, colorGroupCoordinator);\r
+ PartsImageDirectoryWatchAgentFactory agentFactory = PartsImageDirectoryWatchAgentFactory.getFactory();\r
+ watchAgent = agentFactory.getAgent(characterData);\r
+\r
+ previewPane = new PreviewPanel();\r
+ previewPane.setTitle(defaultPartsSetTitle);\r
+ previewPane.addPreviewPanelListener(new PreviewPanelListener() {\r
+ public void addFavorite(PreviewPanelEvent e) {\r
+ if (!e.isShiftKeyPressed()) {\r
+ // お気に入り登録\r
+ onRegisterFavorite();\r
+\r
+ } else {\r
+ // シフトキーにて、お気に入りの管理を開く\r
+ onManageFavorites();\r
+ }\r
+ }\r
+ public void changeBackgroundColor(PreviewPanelEvent e) {\r
+ if ( !e.isShiftKeyPressed()) {\r
+ // 壁紙選択\r
+ onChangeWallpaper();\r
+\r
+ } else {\r
+ // シフトキーにて背景色変更\r
+ onChangeBgColor();\r
+ }\r
+ }\r
+ public void copyPicture(PreviewPanelEvent e) {\r
+ onCopy(e.isShiftKeyPressed());\r
+ }\r
+ public void savePicture(PreviewPanelEvent e) {\r
+ if ( !e.isShiftKeyPressed()) {\r
+ // 画像出力\r
+ onSavePicture();\r
+\r
+ } else {\r
+ // シフトキーにて「伺か」用出力\r
+ onSaveAsUkagaka();\r
+ }\r
+ }\r
+ public void showInformation(PreviewPanelEvent e) {\r
+ onInformation();\r
+ }\r
+ public void flipHorizontal(PreviewPanelEvent e) {\r
+ onFlipHolizontal();\r
+ }\r
+ });\r
+\r
+ imageSelectPanels = new ImageSelectPanelList();\r
+\r
+ JPanel imgSelectPanelsPanel = new JPanel();\r
+ BoxLayout bl = new BoxLayout(imgSelectPanelsPanel, BoxLayout.PAGE_AXIS);\r
+ imgSelectPanelsPanel.setLayout(bl);\r
+ for (PartsCategory category : characterData.getPartsCategories()) {\r
+ final ImageSelectPanel imageSelectPanel = new ImageSelectPanel(category, characterData);\r
+ imgSelectPanelsPanel.add(imageSelectPanel);\r
+ imageSelectPanels.add(imageSelectPanel);\r
+ partsSelectionManager.register(imageSelectPanel);\r
+ }\r
+\r
+ imgSelectPanelsPanelSp = new JScrollPane(imgSelectPanelsPanel) {\r
+ private static final long serialVersionUID = 1L;\r
+ @Override\r
+ public JScrollBar createVerticalScrollBar() {\r
+ JScrollBar sb = super.createVerticalScrollBar();\r
+ sb.setUnitIncrement(12);\r
+ return sb;\r
+ }\r
+ };\r
+ imgSelectPanelsPanelSp.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);\r
+\r
+ JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, imgSelectPanelsPanelSp, previewPane);\r
+ contentPane.add(splitPane, BorderLayout.CENTER);\r
+\r
+\r
+ imgSelectPanelsPanelSp.requestFocus();\r
+\r
+ ArrayList<ColorGroup> colorGroups = new ArrayList<ColorGroup>();\r
+ colorGroups.addAll(characterData.getColorGroups());\r
+\r
+ final ColorChangeListener colorChangeListener = new ColorChangeListener() {\r
+ public void onColorGroupChange(ColorChangeEvent event) {\r
+ // do nothing.\r
+ }\r
+ public void onColorChange(ColorChangeEvent event) {\r
+ MainFrame.this.requestPreview();\r
+ }\r
+ };\r
+ colorGroupCoordinator.addColorChangeListener(colorChangeListener);\r
+\r
+ for (int idx = 0; idx < imageSelectPanels.size(); idx++) {\r
+ ImageSelectPanel imageSelectPanel = imageSelectPanels.get(idx);\r
+ final PartsCategory partsCategory = imageSelectPanel.getPartsCategory();\r
+ final ColorDialog colorDialog = new ColorDialog(this, partsCategory, colorGroups);\r
+ colorGroupCoordinator.registerColorDialog(colorDialog);\r
+ partsColorCoordinator.register(imageSelectPanel, colorDialog);\r
+ final int curidx = idx;\r
+ imageSelectPanel.addImageSelectListener(new ImageSelectPanelListener() {\r
+ public void onChangeColor(ImageSelectPanelEvent event) {\r
+ WindowAdjustLocationSupport.alignRight(\r
+ MainFrame.this, colorDialog, curidx, false);\r
+ colorDialog.setVisible(!colorDialog.isVisible());\r
+ }\r
+ public void onPreferences(ImageSelectPanelEvent event) {\r
+ // do nothing. (not supported)\r
+ }\r
+ public void onChange(ImageSelectPanelEvent event) {\r
+ MainFrame.this.requestPreview();\r
+ }\r
+ public void onSelectChange(ImageSelectPanelEvent event) {\r
+ // do nothing.\r
+ }\r
+ public void onTitleClick(ImageSelectPanelEvent event) {\r
+ PartsCategory partsCategory = (event != null) ?\r
+ event.getImageSelectPanel().getPartsCategory() : null;\r
+ MainFrame.this.onClickPartsCategoryTitle(partsCategory, false);\r
+ }\r
+ public void onTitleDblClick(ImageSelectPanelEvent event) {\r
+ PartsCategory partsCategory = (event != null) ?\r
+ event.getImageSelectPanel().getPartsCategory() : null;\r
+ MainFrame.this.onClickPartsCategoryTitle(partsCategory, true);\r
+ }\r
+ });\r
+ imageSelectPanel.addAncestorListener(new AncestorListener() {\r
+ public void ancestorAdded(AncestorEvent event) {\r
+ }\r
+ public void ancestorMoved(AncestorEvent event) {\r
+ }\r
+ public void ancestorRemoved(AncestorEvent event) {\r
+ // パネルもしくは、その親が削除されたときにダイアログも非表示とする。\r
+ colorDialog.setVisible(false);\r
+ }\r
+ });\r
+ }\r
+\r
+ // 全パーツのロード\r
+ partsSelectionManager.loadParts();\r
+\r
+ // 登録されているカスタムレイヤーパターンのロード\r
+ loadCustomLayerOrder();\r
+\r
+ // 保存されているワーキングセットを復元する.\r
+ // 復元できなかった場合はパーツセットを初期選択する.\r
+ if ( !loadWorkingSet()) {\r
+ // ワーキングセットがない場合は\r
+ // デフォルトのウィンドウ位置とサイズ\r
+ setDefaultWindowLocation();\r
+\r
+ // デフォルトのパーツセットの表示\r
+ if (showDefaultParts(true)) {\r
+ requestPreview();\r
+ }\r
+ }\r
+\r
+ // 選択されているパーツを見える状態にする\r
+ scrollToSelectedParts();\r
+\r
+ // 非同期イメージローダの処理開始\r
+ if (!imageBuilder.isAlive()) {\r
+ imageBuilder.start();\r
+ }\r
+\r
+ // ドロップターゲットの設定\r
+ new DropTarget(imgSelectPanelsPanelSp, new FileDropTarget() {\r
+ @Override\r
+ protected void onDropFiles(final List<File> dropFiles) {\r
+ if (dropFiles == null || dropFiles.isEmpty()) {\r
+ return;\r
+ }\r
+ // インポートダイアログを開く.\r
+ // ドロップソースの処理がブロッキングしないように、\r
+ // ドロップハンドラの処理を終了してからインポートダイアログが開くようにする.\r
+ SwingUtilities.invokeLater(new Runnable() {\r
+ public void run() {\r
+ onImport(dropFiles);\r
+ }\r
+ });\r
+ }\r
+ @Override\r
+ protected void onException(Exception ex) {\r
+ ErrorMessageHelper.showErrorDialog(MainFrame.this, ex);\r
+ }\r
+ });\r
+\r
+ // ディレクトリを監視し変更があった場合にパーツをリロードするリスナ\r
+ watchAgent.addPartsImageDirectoryWatchListener(new PartsImageDirectoryWatchListener() {\r
+ public void detectPartsImageChange(PartsImageDirectoryWatchEvent e) {\r
+ Runnable refreshJob = new Runnable() {\r
+ public void run() {\r
+ onDetectPartsImageChange();\r
+ }\r
+ };\r
+ if (SwingUtilities.isEventDispatchThread()) {\r
+ refreshJob.run();\r
+ } else {\r
+ SwingUtilities.invokeLater(refreshJob);\r
+ }\r
+ }\r
+ });\r
+\r
+ // 監視が有効であれば、ディレクトリの監視をスタートする\r
+ if (appConfig.isEnableDirWatch() && characterData.isWatchDirectory()) {\r
+ watchAgent.connect();\r
+ }\r
+\r
+ // パーツカテゴリの自動縮小が設定されている場合\r
+ minimizeMode = false;\r
+ if (appConfig.isEnableAutoShrinkPanel()) {\r
+ onClickPartsCategoryTitle(null, true);\r
+ }\r
+\r
+ // コンポーネントの再構築の場合\r
+ if (oldCd != null) {\r
+ validate();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * パーツが変更されたことを検知した場合.<br>\r
+ * パーツデータをリロードし、各カテゴリのパーツ一覧を再表示させ、プレビューを更新する.<br>\r
+ */\r
+ protected void onDetectPartsImageChange() {\r
+ try {\r
+ reloadPartsAndFavorites(null, true);\r
+\r
+ } catch (IOException ex) {\r
+ logger.log(Level.SEVERE, "parts reload failed. " + characterData, ex);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * すべてのカテゴリのリストで選択中のアイテムが見えるようにスクロールする.\r
+ */\r
+ protected void scrollToSelectedParts() {\r
+ partsSelectionManager.scrollToSelectedParts();\r
+ }\r
+\r
+ /**\r
+ * 指定したパーツカテゴリ以外のパーツ選択パネルを最小化する.\r
+ *\r
+ * @param partsCategory\r
+ * パーツカテゴリ、nullの場合は全て最小化する.\r
+ * @param dblClick\r
+ * ダブルクリック\r
+ */\r
+ protected void onClickPartsCategoryTitle(PartsCategory partsCategory, boolean dblClick) {\r
+ if (logger.isLoggable(Level.FINE)) {\r
+ logger.log(Level.FINE, "onClickPartsCategoryTitle category="\r
+ + partsCategory + "/clickCount=" + dblClick);\r
+ }\r
+ if (dblClick) {\r
+ minimizeMode = !minimizeMode;\r
+ if (!minimizeMode) {\r
+ partsSelectionManager.setMinimizeModeIfOther(null, false);\r
+ return;\r
+ }\r
+ }\r
+ if (minimizeMode) {\r
+ if (partsSelectionManager.isNotMinimizeModeJust(partsCategory)) {\r
+ partsSelectionManager.setMinimizeModeIfOther(null, true); // 全部縮小\r
+\r
+ } else {\r
+ partsSelectionManager.setMinimizeModeIfOther(partsCategory, true);\r
+ if (partsCategory != null) {\r
+ // 対象のパネルがスクロールペイン内に見える用にスクロールする.\r
+ // スクロールバーの位置指定などの座標系の操作は「要求」であり、実際に適用されるまで本当の位置は分らない。\r
+ // よって以下の処理は非同期に行い、先に座標を確定させたものに対して行う必要がある。\r
+ final ImageSelectPanel panel = imageSelectPanels.findByPartsCategory(partsCategory);\r
+ SwingUtilities.invokeLater(new Runnable() {\r
+ public void run() {\r
+ final Point pt = panel.getLocation();\r
+ JViewport viewPort = imgSelectPanelsPanelSp.getViewport();\r
+ viewPort.setViewPosition(pt);\r
+ viewPort.revalidate();\r
+ }\r
+ });\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * デフォルトパーツを選択する.<br>\r
+ * デフォルトパーツがなければお気に入りの最初のものを選択する.<br>\r
+ * それもなければ空として表示する.<br>\r
+ * パーツの適用に失敗した場合はfalseを返します.(例外は返されません.)<br>\r
+ *\r
+ * @param force\r
+ * すでに選択があっても選択しなおす場合はtrue、falseの場合は選択があれば何もしない.\r
+ * @return パーツ選択された場合。force=trueの場合はエラーがなければ常にtrueとなります。\r
+ */\r
+ protected boolean showDefaultParts(boolean force) {\r
+ try {\r
+ if (!force) {\r
+ // 現在選択中のパーツを取得する.(なければ空)\r
+ PartsSet sel = partsSelectionManager.createPartsSet();\r
+ if (!sel.isEmpty()) {\r
+ // 強制選択でない場合、すでに選択済みのパーツがあれば何もしない.\r
+ return false;\r
+ }\r
+ }\r
+\r
+ // デフォルトのパーツセットを取得する\r
+ String defaultPresetId = characterData.getDefaultPartsSetId();\r
+ PartsSet partsSet = null;\r
+ if (defaultPresetId != null) {\r
+ partsSet = characterData.getPartsSets().get(defaultPresetId);\r
+ }\r
+\r
+ // デフォルトのパーツセットがなければ、お気に入りの最初を選択する.\r
+ if (partsSet == null) {\r
+ List<PartsSet> partssets = getPartsSetList();\r
+ if (!partssets.isEmpty()) {\r
+ partsSet = partssets.get(0);\r
+ }\r
+ }\r
+\r
+ // パーツセットがあれば、それを表示要求する.\r
+ // パーツセットがなければカラーダイアログを初期化するのみ\r
+ if (partsSet == null) {\r
+ partsColorCoordinator.initColorDialog();\r
+\r
+ } else {\r
+ selectPresetParts(partsSet);\r
+ }\r
+\r
+ } catch (Exception ex) {\r
+ logger.log(Level.WARNING, "パーツのデフォルト適用に失敗しました。", ex);\r
+ return false;\r
+ }\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * プリセットを適用しキャラクターイメージを再構築します.<br>\r
+ * 実行時エラーは画面のレポートされます.<br>\r
+ *\r
+ * @param presetParts\r
+ * パーツセット, nullの場合は何もしない.\r
+ */\r
+ protected void selectPresetParts(PartsSet presetParts) {\r
+ if (presetParts == null) {\r
+ return;\r
+ }\r
+ try {\r
+ // 最後に使用したプリセットとして記憶する.\r
+ lastUsePresetParts = presetParts;\r
+ // プリセットパーツで選択を変える\r
+ partsSelectionManager.selectPartsSet(presetParts);\r
+ // カラーパネルを選択されているアイテムをもとに再設定する\r
+ partsColorCoordinator.initColorDialog();\r
+ // 再表示\r
+ requestPreview();\r
+\r
+ } catch (Exception ex) {\r
+ ErrorMessageHelper.showErrorDialog(this, ex);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * プリセットとお気に入りを表示順に並べて返す.\r
+ *\r
+ * @return プリセットとお気に入りのリスト(表示順)\r
+ */\r
+ protected List<PartsSet> getPartsSetList() {\r
+ ArrayList<PartsSet> partssets = new ArrayList<PartsSet>();\r
+ partssets.addAll(characterData.getPartsSets().values());\r
+ Collections.sort(partssets, PartsSet.DEFAULT_COMPARATOR);\r
+ return partssets;\r
+ }\r
+\r
+ protected static final class TreeLeaf implements Comparable<TreeLeaf> {\r
+\r
+ public enum TreeLeafType {\r
+ NODE, LEAF\r
+ }\r
+\r
+ private String name;\r
+\r
+ private TreeLeafType typ;\r
+\r
+ public TreeLeaf(TreeLeafType typ, String name) {\r
+ if (name == null) {\r
+ name = "";\r
+ }\r
+ this.typ = typ;\r
+ this.name = name;\r
+ }\r
+\r
+ public String getName() {\r
+ return name;\r
+ }\r
+\r
+ public TreeLeafType getTyp() {\r
+ return typ;\r
+ }\r
+\r
+ @Override\r
+ public boolean equals(Object obj) {\r
+ if (obj != null && obj instanceof TreeLeaf) {\r
+ TreeLeaf o = (TreeLeaf) obj;\r
+ return typ == o.typ && name.equals(o.name);\r
+ }\r
+ return false;\r
+ }\r
+\r
+ @Override\r
+ public int hashCode() {\r
+ return typ.hashCode() ^ name.hashCode();\r
+ }\r
+\r
+ public int compareTo(TreeLeaf o) {\r
+ int ret = name.compareTo(o.name);\r
+ if (ret == 0) {\r
+ ret = (typ.ordinal() - o.typ.ordinal());\r
+ }\r
+ return ret;\r
+ }\r
+\r
+ @Override\r
+ public String toString() {\r
+ return name;\r
+ }\r
+ }\r
+\r
+ protected TreeMap<TreeLeaf, Object> buildFavoritesItemTree(\r
+ List<PartsSet> partssets) {\r
+ if (partssets == null) {\r
+ partssets = Collections.emptyList();\r
+ }\r
+ TreeMap<TreeLeaf, Object> favTree = new TreeMap<TreeLeaf, Object>();\r
+ for (PartsSet partsSet : partssets) {\r
+ String flatname = partsSet.getLocalizedName();\r
+ String[] tokens = flatname.split("\\|");\r
+ if (tokens.length == 0) {\r
+ continue;\r
+ }\r
+\r
+ TreeMap<TreeLeaf, Object> r = favTree;\r
+ for (int idx = 0; idx < tokens.length - 1; idx++) {\r
+ String name = tokens[idx];\r
+ TreeLeaf leafName = new TreeLeaf(TreeLeaf.TreeLeafType.NODE,\r
+ name);\r
+ @SuppressWarnings("unchecked")\r
+ TreeMap<TreeLeaf, Object> n = (TreeMap<TreeLeaf, Object>) r\r
+ .get(leafName);\r
+ if (n == null) {\r
+ n = new TreeMap<TreeLeaf, Object>();\r
+ r.put(leafName, n);\r
+ }\r
+ r = n;\r
+ }\r
+ String lastName = tokens[tokens.length - 1];\r
+ TreeLeaf lastLeafName = new TreeLeaf(TreeLeaf.TreeLeafType.LEAF,\r
+ lastName);\r
+ @SuppressWarnings("unchecked")\r
+ List<PartsSet> leafValue = (List<PartsSet>) r.get(lastLeafName);\r
+ if (leafValue == null) {\r
+ leafValue = new ArrayList<PartsSet>();\r
+ r.put(lastLeafName, leafValue);\r
+ }\r
+ leafValue.add(partsSet);\r
+ }\r
+ return favTree;\r
+ }\r
+\r
+ protected interface FavoriteMenuItemBuilder {\r
+ JMenuItem createFavoriteMenuItem(String name, PartsSet partsSet);\r
+ JMenu createSubMenu(String name);\r
+ }\r
+\r
+ private void buildFavoritesMenuItems(List<JMenuItem> menuItems,\r
+ FavoriteMenuItemBuilder favMenuItemBuilder,\r
+ TreeMap<TreeLeaf, Object> favTree) {\r
+ for (Map.Entry<TreeLeaf, Object> entry : favTree.entrySet()) {\r
+ TreeLeaf treeLeaf = entry.getKey();\r
+ String name = treeLeaf.getName();\r
+ if (treeLeaf.getTyp() == TreeLeaf.TreeLeafType.LEAF) {\r
+ // 葉ノードには、JMenuItemを設定する.\r
+ @SuppressWarnings("unchecked")\r
+ List<PartsSet> leafValue = (List<PartsSet>) entry.getValue();\r
+ for (final PartsSet partsSet : leafValue) {\r
+ JMenuItem favoriteMenu = favMenuItemBuilder\r
+ .createFavoriteMenuItem(name, partsSet);\r
+ menuItems.add(favoriteMenu);\r
+ }\r
+\r
+ } else if (treeLeaf.getTyp() == TreeLeaf.TreeLeafType.NODE) {\r
+ // 枝ノードは、サブメニューを作成し、子ノードを設定する\r
+ @SuppressWarnings("unchecked")\r
+ TreeMap<TreeLeaf, Object> childNode = (TreeMap<TreeLeaf, Object>) entry\r
+ .getValue();\r
+ JMenu subMenu = favMenuItemBuilder.createSubMenu(name);\r
+ menuItems.add(subMenu);\r
+ ArrayList<JMenuItem> subMenuItems = new ArrayList<JMenuItem>();\r
+ buildFavoritesMenuItems(subMenuItems, favMenuItemBuilder, childNode);\r
+ for (JMenuItem subMenuItem : subMenuItems) {\r
+ subMenu.add(subMenuItem);\r
+ }\r
+\r
+ } else {\r
+ throw new RuntimeException("unknown type: " + treeLeaf);\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * お気に入りのJMenuItemを作成するファンクションオブジェクト\r
+ */\r
+ private FavoriteMenuItemBuilder favMenuItemBuilder = new FavoriteMenuItemBuilder() {\r
+ private MenuBuilder menuBuilder = new MenuBuilder();\r
+\r
+ /**\r
+ * お気に入りメニューの作成\r
+ */\r
+ public JMenuItem createFavoriteMenuItem(final String name,\r
+ final PartsSet partsSet) {\r
+ JMenuItem favoriteMenu = menuBuilder.createJMenuItem();\r
+ favoriteMenu.setName(partsSet.getPartsSetId());\r
+ favoriteMenu.setText(name);\r
+ if (partsSet.isPresetParts()) {\r
+ Font font = favoriteMenu.getFont();\r
+ favoriteMenu.setFont(font.deriveFont(Font.BOLD));\r
+ }\r
+ favoriteMenu.addActionListener(new ActionListener() {\r
+ public void actionPerformed(ActionEvent e) {\r
+ selectPresetParts(partsSet);\r
+ }\r
+ });\r
+\r
+ // メニューアイテム上でマウスホイールを動かした場合は上下にスクロールさせる.\r
+ // (ただし、OSXのスクリーンメニュー使用時は無視する.)\r
+ addMouseWheelListener(favoriteMenu);\r
+\r
+ return favoriteMenu;\r
+ }\r
+\r
+ /**\r
+ * サブメニューの作成\r
+ */\r
+ public JMenu createSubMenu(String name) {\r
+ JMenu menu = menuBuilder.createJMenu();\r
+ menu.setText(name);\r
+\r
+ // メニューアイテム上でマウスホイールを動かした場合は上下にスクロールさせる.\r
+ // (ただし、OSXのスクリーンメニュー使用時は無視する.)\r
+ addMouseWheelListener(menu);\r
+\r
+ return menu;\r
+ }\r
+\r
+ /**\r
+ * メニューアイテム上でホイールを上下させたときにメニューをスクロールさせるためのホイールハンドラを設定する.\r
+ *\r
+ * @param favoriteMenu\r
+ */\r
+ protected void addMouseWheelListener(final JMenuItem favoriteMenu) {\r
+ if (JScrollableMenu.isScreenMenu()) {\r
+ return;\r
+ }\r
+ favoriteMenu.addMouseWheelListener(new MouseWheelListener() {\r
+ public void mouseWheelMoved(MouseWheelEvent e) {\r
+ int rotation = e.getWheelRotation();\r
+ JPopupMenu popupMenu = (JPopupMenu) favoriteMenu\r
+ .getParent();\r
+ JMenu parentMenu = (JMenu) popupMenu.getInvoker();\r
+ if (parentMenu != null\r
+ && parentMenu instanceof JScrollableMenu) {\r
+ final JScrollableMenu favMenu = (JScrollableMenu) parentMenu;\r
+ favMenu.doScroll(rotation < 0);\r
+ }\r
+ e.consume();\r
+ }\r
+ });\r
+ }\r
+ };\r
+\r
+ /**\r
+ * お気に入りメニューが開いたとき\r
+ *\r
+ * @param menu\r
+ */\r
+ protected void onSelectedFavoriteMenu(JMenu menu) {\r
+ // 表示順にソート\r
+ List<PartsSet> partssets = getPartsSetList();\r
+ TreeMap<TreeLeaf, Object> favTree = buildFavoritesItemTree(partssets);\r
+\r
+ // メニューの再構築\r
+ ArrayList<JMenuItem> favoritesMenuItems = new ArrayList<JMenuItem>();\r
+ buildFavoritesMenuItems(favoritesMenuItems, favMenuItemBuilder, favTree);\r
+\r
+ replaceMenuItems(menu, favoritesMenuItems);\r
+ }\r
+\r
+ /**\r
+ * スクロール可能JMenu/通常JMenuのメニューアイテムの差し替え\r
+ * @param menu\r
+ * @param items\r
+ */\r
+ private void replaceMenuItems(JMenu menu, List<JMenuItem> items) {\r
+ if (menu instanceof JScrollableMenu) {\r
+ // スクロールメニューの場合\r
+ JScrollableMenu favMenu = (JScrollableMenu) menu;\r
+\r
+ // スクロールメニューの初期化\r
+ favMenu.initScroller();\r
+\r
+ // スクロールメニューアイテムの設定\r
+ favMenu.setScrollableItems(items);\r
+\r
+ // 高さを補正する\r
+ // お気に入りメニューが選択された場合、\r
+ // お気に入りアイテム一覧を表示するよりも前に\r
+ // 表示可能なアイテム数を現在のウィンドウの高さから算定する.\r
+ Toolkit tk = Toolkit.getDefaultToolkit();\r
+ Dimension scrsiz = tk.getScreenSize();\r
+ int height = scrsiz.height; // MainFrame.this.getHeight();\r
+ favMenu.adjustMaxVisible(height);\r
+ logger.log(Level.FINE,\r
+ "scrollableMenu maxVisible=" + favMenu.getMaxVisible());\r
+\r
+ } else {\r
+ // 通常メニューの場合\r
+ // 既存メニューの位置をセパレータより判断する.\r
+ int mx = menu.getMenuComponentCount();\r
+ int separatorIdx = -1;\r
+ for (int idx = 0; idx < mx; idx++) {\r
+ Component item = menu.getMenuComponent(idx);\r
+ if (item instanceof JSeparator) {\r
+ separatorIdx = idx;\r
+ break;\r
+ }\r
+ }\r
+ // 既存メニューの削除\r
+ if (separatorIdx > 0) {\r
+ while (menu.getMenuComponentCount() > separatorIdx + 1) {\r
+ menu.remove(separatorIdx + 1);\r
+ }\r
+ }\r
+\r
+ // メニューを登録する.\r
+ for (JMenuItem menuItem : items) {\r
+ menu.add(menuItem);\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * カスタムレイヤーメニューを開いたとき\r
+ * @param menu\r
+ */\r
+ protected void onSelectedCustomLayerMenu(JMenu menu) {\r
+ // メニューの再構築\r
+ ArrayList<JMenuItem> menuItems = new ArrayList<JMenuItem>();\r
+ for (Map.Entry<String, List<CustomLayerOrder>> entry : customLayerPatternMgr.getMap().entrySet()) {\r
+ final String name = entry.getKey();\r
+ final JCheckBoxMenuItem menuItem = new JCheckBoxMenuItem(name);\r
+ menuItem.setSelected(customLayerPatternMgr.isSelected(name));\r
+ menuItem.addActionListener(new ActionListener() {\r
+ @Override\r
+ public void actionPerformed(ActionEvent e) {\r
+ customLayerPatternMgr.setSelected(name, menuItem.isSelected());\r
+ requestPreview();\r
+ }\r
+ });\r
+ menuItems.add(menuItem);\r
+ }\r
+ replaceMenuItems(menu, menuItems);\r
+ }\r
+\r
+ /**\r
+ * ヘルプメニューを開いたときにお勧めメニューを構築する.\r
+ *\r
+ * @param menu\r
+ */\r
+ protected void onSelectedRecommendationMenu(JMenu mnuRecomendation) {\r
+ // 現在のお勧めメニューを一旦削除\r
+ while (mnuRecomendation.getMenuComponentCount() > 0) {\r
+ mnuRecomendation.remove(0);\r
+ }\r
+\r
+ // お勧めリンクの定義がない場合はデフォルトを用いる.(明示的な空の場合は何もしない.)\r
+ CharacterDataPersistent persist = CharacterDataPersistent.getInstance();\r
+ persist.compensateRecommendationList(characterData);\r
+\r
+ // お勧めリンクメニューを作成する.\r
+ List<RecommendationURL> recommendations = characterData.getRecommendationURLList();\r
+ if (recommendations != null) {\r
+ MenuBuilder menuBuilder = new MenuBuilder();\r
+ for (RecommendationURL recommendation : recommendations) {\r
+ String displayName = recommendation.getDisplayName();\r
+ String url = recommendation.getUrl();\r
+\r
+ JMenuItem mnuItem = menuBuilder.createJMenuItem();\r
+ mnuItem.setText(displayName);\r
+ mnuItem.addActionListener(\r
+ DesktopUtilities.createBrowseAction(MainFrame.this, url, displayName)\r
+ );\r
+ mnuRecomendation.add(mnuItem);\r
+ }\r
+ }\r
+\r
+ // お勧めリンクメニューのリストがnullでなく空でもない場合は有効、そうでなければ無効にする.\r
+ mnuRecomendation.setEnabled(recommendations != null && !recommendations.isEmpty());\r
+ }\r
+\r
+\r
+ /**\r
+ * 最後に選択されたお気に入りと同じ構成であれば、 このお気に入りの名前をプレビューペインのタイトルに設定する.<br>\r
+ * そうでなければデフォルトのパーツセット名(no titleとか)を表示する.<br>\r
+ * 色情報が異なる場合に末尾に「*」マークがつけられる.<br>\r
+ *\r
+ * @param requestPartsSet\r
+ * 表示するパーツセット(名前は設定されていなくて良い。お気に入り側を使うので。), nullの場合はデフォルトのパーツ名\r
+ */\r
+ protected void showPresetName(PartsSet requestPartsSet) {\r
+ String title = getSuggestPartsSetName(requestPartsSet, true);\r
+ if (title == null) {\r
+ title = defaultPartsSetTitle;\r
+ }\r
+ previewPane.setTitle(title);\r
+ }\r
+\r
+ /**\r
+ * パーツセット名を推定する.<br>\r
+ * 最後に選択されたお気に入りと同じ構成であれば、 このお気に入りの名前を返す.<br>\r
+ * お気に入りが選択されていないか構成が異なる場合、お気に入りに名前がない場合はnullを返す.<br>\r
+ *\r
+ * @param requestPartsSet\r
+ * 表示するパーツセット(名前は設定されていなくて良い。お気に入り側を使うので。)\r
+ * @param markColorChange\r
+ * 色情報が異なる場合に末尾に「*」マークをつける場合はtrue\r
+ */\r
+ private String getSuggestPartsSetName(PartsSet requestPartsSet, boolean markColorChange) {\r
+ String partsSetTitle = null;\r
+ if (lastUsePresetParts != null &&\r
+ PartsSet.isSameStructure(requestPartsSet, lastUsePresetParts)) {\r
+ partsSetTitle = lastUsePresetParts.getLocalizedName();\r
+ if (markColorChange && !PartsSet.isSameColor(requestPartsSet, lastUsePresetParts)) {\r
+ if (partsSetTitle != null) {\r
+ partsSetTitle += "*";\r
+ }\r
+ }\r
+ }\r
+ if (partsSetTitle != null && partsSetTitle.trim().length() > 0) {\r
+ return partsSetTitle;\r
+ }\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * プレビューの更新を要求する. 更新は非同期に行われる.\r
+ */\r
+ protected void requestPreview() {\r
+ if (!characterData.isValid()) {\r
+ return;\r
+ }\r
+\r
+ // 選択されているパーツの各イメージを取得しレイヤー順に並び替えて合成する.\r
+ // 合成は別スレッドにて非同期に行われる.\r
+ // リクエストは随時受け付けて、最新のリクエストだけが処理される.\r
+ // (処理がはじまる前に新しいリクエストで上書きされた場合、前のリクエストは単に捨てられる.)\r
+ imageBuilder.requestJob(new ImageBuildJobAbstractAdaptor(characterData) {\r
+\r
+ /**\r
+ * 構築するパーツセット情報\r
+ */\r
+ private PartsSet requestPartsSet;\r
+\r
+ /**\r
+ * 非同期のイメージ構築要求の番号.<br>\r
+ */\r
+ private long ticket;\r
+\r
+ @Override\r
+ public void onQueueing(long ticket) {\r
+ this.ticket = ticket;\r
+ previewPane.setLoadingRequest(ticket);\r
+ }\r
+ @Override\r
+ public void buildImage(ImageOutput output) {\r
+ // 合成結果のイメージを引数としてイメージビルダから呼び出される.\r
+ final BufferedImage img = output.getImageOutput();\r
+ Runnable refreshJob = new Runnable() {\r
+ public void run() {\r
+ previewPane.setPreviewImage(img);\r
+ previewPane.setLoadingComplete(ticket);\r
+ showPresetName(requestPartsSet);\r
+ }\r
+ };\r
+ if (SwingUtilities.isEventDispatchThread()) {\r
+ refreshJob.run();\r
+ } else {\r
+ try {\r
+ SwingUtilities.invokeAndWait(refreshJob);\r
+ } catch (Exception ex) {\r
+ logger.log(Level.WARNING, "build image failed.", ex);\r
+ }\r
+ }\r
+ }\r
+ @Override\r
+ public void handleException(final Throwable ex) {\r
+ // 合成中に例外が発生した場合、イメージビルダから呼び出される.\r
+ Runnable showExceptionJob = new Runnable() {\r
+ public void run() {\r
+ ErrorMessageHelper.showErrorDialog(MainFrame.this, ex);\r
+ }\r
+ };\r
+ if (SwingUtilities.isEventDispatchThread()) {\r
+ showExceptionJob.run();\r
+ } else {\r
+ SwingUtilities.invokeLater(showExceptionJob);\r
+ }\r
+ }\r
+ @Override\r
+ protected PartsSet getPartsSet() {\r
+ // 合成できる状態になった時点でイメージビルダから呼び出される.\r
+ final PartsSet[] result = new PartsSet[1];\r
+ Runnable collectPartsSetJob = new Runnable() {\r
+ public void run() {\r
+ PartsSet partsSet = partsSelectionManager.createPartsSet();\r
+ result[0] = partsSet;\r
+ }\r
+ };\r
+ if (SwingUtilities.isEventDispatchThread()) {\r
+ collectPartsSetJob.run();\r
+ } else {\r
+ try {\r
+ // スレッドによるSwingのイベントディスパッチスレッド以外からの呼び出しの場合、\r
+ // Swingディスパッチスレッドでパーツの選択状態を取得する.\r
+ SwingUtilities.invokeAndWait(collectPartsSetJob);\r
+\r
+ } catch (InvocationTargetException e) {\r
+ throw new RuntimeException(e.getMessage(), e);\r
+ } catch (InterruptedException e) {\r
+ throw new RuntimeException("interrupted:" + e, e);\r
+ }\r
+ }\r
+ if (logger.isLoggable(Level.FINE)) {\r
+ logger.log(Level.FINE, "preview: " + result[0]);\r
+ }\r
+ requestPartsSet = result[0];\r
+ return requestPartsSet;\r
+ }\r
+ @Override\r
+ protected LayerOrderMapper getLayerOrderMapper() {\r
+ return customLayerPatternMgr;\r
+ }\r
+ });\r
+ }\r
+\r
+ /**\r
+ * プロファイルを開く\r
+ */\r
+ protected void onOpenProfile() {\r
+ try {\r
+ MainFrame main2 = ProfileListManager.openProfile(this);\r
+ if (main2 != null) {\r
+ main2.showMainFrame();\r
+ }\r
+\r
+ } catch (Exception ex) {\r
+ ErrorMessageHelper.showErrorDialog(this, ex);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 背景色を変更する.\r
+ */\r
+ protected void onChangeBgColor() {\r
+ getJMenuBar().setEnabled(false);\r
+ try {\r
+ Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()\r
+ .getLocalizedProperties(STRINGS_RESOURCE);\r
+\r
+ Color color = wallpaperInfo.getBackgroundColor();\r
+ color = JColorChooser.showDialog(this, strings.getProperty("chooseBgColor"), color);\r
+ if (color != null) {\r
+ applyBackgroundColorOnly(color);\r
+ }\r
+ } finally {\r
+ getJMenuBar().setEnabled(true);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 壁紙を変更する.\r
+ */\r
+ protected void onChangeWallpaper() {\r
+ try {\r
+ WallpaperDialog wallpaperDialog = new WallpaperDialog(this);\r
+\r
+ // 最後に使用した壁紙情報でダイアログを設定する.\r
+ wallpaperDialog.setWallpaperInfo(this.wallpaperInfo);\r
+\r
+ // 壁紙情報を設定するモーダルダイアログを開く\r
+ WallpaperInfo wallpaperInfo = wallpaperDialog.showDialog();\r
+ if (wallpaperInfo == null) {\r
+ return;\r
+ }\r
+\r
+ // 壁紙情報を保存し、その情報をもとに背景を再描画する.\r
+ applyWallpaperInfo(wallpaperInfo, false);\r
+\r
+ } catch (WallpaperFactoryException ex) {\r
+ ErrorMessageHelper.showErrorDialog(this, ex);\r
+\r
+ } catch (RuntimeException ex) {\r
+ ErrorMessageHelper.showErrorDialog(this, ex);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 背景色のみ変更し、背景を再描画する.<br>\r
+ * 壁紙情報全体の更新よりも効率化するためのメソッドである.<br>\r
+ *\r
+ * @param bgColor\r
+ * 背景色\r
+ */\r
+ protected void applyBackgroundColorOnly(Color bgColor) {\r
+ wallpaperInfo.setBackgroundColor(bgColor);\r
+ previewPane.getWallpaper()\r
+ .setBackgroundColor(wallpaperInfo.getBackgroundColor());\r
+ }\r
+\r
+ /**\r
+ * 壁紙情報を保存し、その情報をもとに背景を再描画する.<br>\r
+ * ignoreErrorがtrueである場合、適用に失敗した場合はログに記録するのみで、 壁紙情報は保存されず、壁紙も更新されない.<br>\r
+ *\r
+ * @param wallpaperInfo\r
+ * 壁紙情報、null不可\r
+ * @param ignoreError\r
+ * 失敗を無視する場合\r
+ * @throws IOException\r
+ * 失敗\r
+ */\r
+ protected void applyWallpaperInfo(WallpaperInfo wallpaperInfo, boolean ignoreError) throws WallpaperFactoryException {\r
+ if (wallpaperInfo == null) {\r
+ throw new IllegalArgumentException();\r
+ }\r
+ // 壁紙情報から壁紙インスタンスを生成する.\r
+ WallpaperFactory wallpaperFactory = WallpaperFactory.getInstance();\r
+ Wallpaper wallpaper = null;\r
+\r
+ try {\r
+ // 壁紙情報の構築時に問題が発生した場合、\r
+ // 回復処理をして継続するかエラーとするか?\r
+ WallpaperFactoryErrorRecoverHandler handler = null;\r
+ if (ignoreError) {\r
+ handler = new WallpaperFactoryErrorRecoverHandler();\r
+ }\r
+\r
+ // 壁紙情報\r
+ wallpaper = wallpaperFactory.createWallpaper(wallpaperInfo, handler);\r
+\r
+ } catch (WallpaperFactoryException ex) {\r
+ logger.log(Level.WARNING, "壁紙情報の適用に失敗しました。", ex);\r
+ if ( !ignoreError) {\r
+ throw ex;\r
+ }\r
+\r
+ } catch (RuntimeException ex) {\r
+ logger.log(Level.WARNING, "壁紙情報の適用に失敗しました。", ex);\r
+ if ( !ignoreError) {\r
+ throw ex;\r
+ }\r
+ }\r
+\r
+ if (wallpaper == null) {\r
+ return;\r
+ }\r
+\r
+ // 壁紙を更新する.\r
+ previewPane.setWallpaper(wallpaper);\r
+\r
+ // 壁紙情報として記憶する.\r
+ this.wallpaperInfo = wallpaperInfo;\r
+ }\r
+\r
+ /**\r
+ * プレビューしている画像をファイルに保存する。 サポートしているのはPNG/JPEGのみ。\r
+ */\r
+ protected void onSavePicture() {\r
+ Toolkit tk = Toolkit.getDefaultToolkit();\r
+ BufferedImage img = previewPane.getPreviewImage();\r
+ Color imgBgColor = wallpaperInfo.getBackgroundColor();\r
+ if (img == null) {\r
+ tk.beep();\r
+ return;\r
+ }\r
+\r
+ try {\r
+ // 出力オプションの調整\r
+ OutputOption outputOption = imageSaveHelper.getOutputOption();\r
+ outputOption.setZoomFactor(previewPane.getZoomFactor());\r
+ outputOption.changeRecommend();\r
+ imageSaveHelper.setOutputOption(outputOption);\r
+\r
+ // ファイルダイアログ表示\r
+ File outFile = imageSaveHelper.showSaveFileDialog(this);\r
+ if (outFile == null) {\r
+ return;\r
+ }\r
+ logger.log(Level.FINE, "savePicture: " + outFile);\r
+ logger.log(Level.FINE, "outputOption: " + outputOption);\r
+\r
+ // 画像のファイルへの出力\r
+ StringBuilder warnings = new StringBuilder();\r
+\r
+ setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));\r
+ try {\r
+ imageSaveHelper.savePicture(img, imgBgColor, outFile, warnings);\r
+\r
+ } finally {\r
+ setCursor(Cursor.getDefaultCursor());\r
+ }\r
+ if (warnings.length() > 0) {\r
+ JOptionPane.showMessageDialog(this, warnings.toString(), "WARNINGS", JOptionPane.WARNING_MESSAGE);\r
+ }\r
+\r
+ } catch (Exception ex) {\r
+ ErrorMessageHelper.showErrorDialog(this, ex);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 伺か用PNG/PNAの出力.\r
+ */\r
+ protected void onSaveAsUkagaka() {\r
+ BufferedImage img = previewPane.getPreviewImage();\r
+ Color bgColor = wallpaperInfo.getBackgroundColor();\r
+ if (img == null) {\r
+ Toolkit tk = Toolkit.getDefaultToolkit();\r
+ tk.beep();\r
+ return;\r
+ }\r
+\r
+ try {\r
+ ukagakaImageSaveHelper.save(this, img, bgColor);\r
+\r
+ } catch (IOException ex) {\r
+ ErrorMessageHelper.showErrorDialog(this, ex);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 伺か用PNG/PNAの変換\r
+ */\r
+ protected void onConvertUkagaka() {\r
+ try {\r
+ Color colorKey = wallpaperInfo.getBackgroundColor();\r
+ ukagakaImageSaveHelper.convertChooseFiles(this, colorKey);\r
+\r
+ } catch (IOException ex) {\r
+ ErrorMessageHelper.showErrorDialog(this, ex);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * プロファイルの場所を開く\r
+ */\r
+ protected void onBrowseProfileDir() {\r
+ if (!characterData.isValid()) {\r
+ Toolkit tk = Toolkit.getDefaultToolkit();\r
+ tk.beep();\r
+ return;\r
+ }\r
+ try {\r
+ DesktopUtilities.browseBaseDir(characterData.getDocBase());\r
+\r
+ } catch (Exception ex) {\r
+ ErrorMessageHelper.showErrorDialog(this, ex);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * このプロファイルを編集する.\r
+ */\r
+ protected void onEditProfile() {\r
+ if (!characterData.isValid()) {\r
+ Toolkit tk = Toolkit.getDefaultToolkit();\r
+ tk.beep();\r
+ return;\r
+ }\r
+ try {\r
+ CharacterData cd = this.characterData;\r
+ CharacterData newCd = ProfileListManager.editProfile(this, cd);\r
+ if (newCd != null) {\r
+ CharacterDataChangeObserver.getDefault()\r
+ .notifyCharacterDataChange(this, newCd, true, true);\r
+ }\r
+\r
+ } catch (Exception ex) {\r
+ ErrorMessageHelper.showErrorDialog(this, ex);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * パーツの管理ダイアログを開く.<br>\r
+ */\r
+ protected void onManageParts() {\r
+ if (!characterData.isValid()) {\r
+ Toolkit tk = Toolkit.getDefaultToolkit();\r
+ tk.beep();\r
+ return;\r
+ }\r
+\r
+ PartsManageDialog mrgDlg = new PartsManageDialog(this, characterData);\r
+ mrgDlg.setVisible(true);\r
+\r
+ if (mrgDlg.isUpdated()) {\r
+ // パーツ管理情報が更新された場合、\r
+ // パーツデータをリロードする.\r
+ if (characterData.reloadPartsData()) {\r
+ partsSelectionManager.loadParts();\r
+ requestPreview();\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 「パーツ検索」ダイアログを開く.<br>\r
+ * すでに開いているダイアログがあれば、それにフォーカスを当てる.<br>\r
+ */\r
+ protected void openSearchDialog() {\r
+ if (!characterData.isValid()) {\r
+ Toolkit tk = Toolkit.getDefaultToolkit();\r
+ tk.beep();\r
+ return;\r
+ }\r
+\r
+ if (lastUseSearchPartsDialog != null) {\r
+ // 開いているダイアログがあれば、それにフォーカスを当てる.\r
+ if (lastUseSearchPartsDialog.isDisplayable() && lastUseSearchPartsDialog.isVisible()) {\r
+ lastUseSearchPartsDialog.requestFocus();\r
+ return;\r
+ }\r
+ }\r
+\r
+ SearchPartsDialog searchPartsDlg = new SearchPartsDialog(this, characterData, partsSelectionManager);\r
+ WindowAdjustLocationSupport.alignRight(this, searchPartsDlg, 0, true);\r
+ searchPartsDlg.setVisible(true);\r
+ lastUseSearchPartsDialog = searchPartsDlg;\r
+ }\r
+\r
+ /**\r
+ * 「パーツ検索」ダイアログを閉じる.<br>\r
+ */\r
+ protected void closeSearchDialog() {\r
+ lastUseSearchPartsDialog = null;\r
+ for (SearchPartsDialog dlg : SearchPartsDialog.getDialogs()) {\r
+ if (dlg != null && dlg.isDisplayable() && dlg.getParent() == this) {\r
+ dlg.dispose();\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 「お気に入りの管理」ダイアログを閉じる\r
+ */\r
+ protected void closeManageFavoritesDialog() {\r
+ if (lastUseManageFavoritesDialog != null) {\r
+ if (lastUseManageFavoritesDialog.isDisplayable()) {\r
+ lastUseManageFavoritesDialog.dispose();\r
+ }\r
+ lastUseManageFavoritesDialog = null;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 「パーツのランダム選択ダイアログ」を閉じる\r
+ */\r
+ protected void closePartsRandomChooserDialog() {\r
+ if (lastUsePartsRandomChooserDialog != null) {\r
+ if (lastUsePartsRandomChooserDialog.isDisplayable()) {\r
+ lastUsePartsRandomChooserDialog.dispose();\r
+ }\r
+ lastUsePartsRandomChooserDialog = null;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * クリップボードにコピー\r
+ *\r
+ * @param screenImage\r
+ * スクリーンイメージ\r
+ */\r
+ protected void onCopy(boolean screenImage) {\r
+ try {\r
+ BufferedImage img = previewPane.getPreviewImage();\r
+ if (img == null) {\r
+ Toolkit tk = Toolkit.getDefaultToolkit();\r
+ tk.beep();\r
+ return;\r
+ }\r
+\r
+ if (screenImage) {\r
+ // 表示している内容をそのままコピーする.\r
+ img = previewPane.getScreenImage();\r
+ }\r
+\r
+ Color imgBgColor = wallpaperInfo.getBackgroundColor();\r
+ ClipboardUtil.setImage(img, imgBgColor);\r
+\r
+ } catch (Exception ex) {\r
+ ErrorMessageHelper.showErrorDialog(this, ex);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * アプリケーションの設定ダイアログを開く\r
+ */\r
+ public void onPreferences() {\r
+ AppConfigDialog appConfigDlg = new AppConfigDialog(this);\r
+ appConfigDlg.setVisible(true);\r
+ }\r
+\r
+ /**\r
+ * 新規モードでインポートウィザードを実行する.<br>\r
+ */\r
+ protected void onImportNew() {\r
+ if (!characterData.isValid()) {\r
+ Toolkit tk = Toolkit.getDefaultToolkit();\r
+ tk.beep();\r
+ return;\r
+ }\r
+\r
+ try {\r
+ // インポートウィザードの実行(新規モード)\r
+ ImportWizardDialog importWizDialog = new ImportWizardDialog(this, null, null);\r
+ importWizDialog.setVisible(true);\r
+ int exitCode = importWizDialog.getExitCode();\r
+ if (exitCode == ImportWizardDialog.EXIT_PROFILE_CREATED) {\r
+ CharacterData cd = importWizDialog.getImportedCharacterData();\r
+ if (cd != null && cd.isValid()) {\r
+ // インポートしたキャラクターデータのプロファイルを開く.\r
+ setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));\r
+ try {\r
+ MainFrame mainFrame = ProfileListManager.openProfile(cd);\r
+ mainFrame.setVisible(true);\r
+\r
+ } finally {\r
+ setCursor(Cursor.getDefaultCursor());\r
+ }\r
+ }\r
+ }\r
+\r
+ } catch (Exception ex) {\r
+ ErrorMessageHelper.showErrorDialog(this, ex);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 現在のプロファイルに対するインポートウィザードを実行する.<br>\r
+ * インポートが実行された場合は、パーツをリロードする.<br>\r
+ * インポートウィザード表示中は監視スレッドは停止される.<br>\r
+ *\r
+ * @param initFile\r
+ * アーカイブファィルまたはディレクトリ、指定がなければnull\r
+ */\r
+ protected void onImport(List<File> initFiles) {\r
+ if (!characterData.isValid()) {\r
+ Toolkit tk = Toolkit.getDefaultToolkit();\r
+ tk.beep();\r
+ return;\r
+ }\r
+\r
+ try {\r
+ watchAgent.suspend();\r
+ try {\r
+ // インポートウィザードの実行\r
+ ImportWizardDialog importWizDialog = new ImportWizardDialog(this, characterData, initFiles);\r
+ importWizDialog.setVisible(true);\r
+\r
+ if (importWizDialog.getExitCode() == ImportWizardDialog.EXIT_PROFILE_UPDATED) {\r
+ CharacterData importedCd = importWizDialog.getImportedCharacterData();\r
+ CharacterDataChangeObserver.getDefault()\r
+ .notifyCharacterDataChange(this, importedCd,\r
+ false, true);\r
+ }\r
+\r
+ } finally {\r
+ watchAgent.resume();\r
+ }\r
+\r
+ } catch (Exception ex) {\r
+ ErrorMessageHelper.showErrorDialog(this, ex);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * パーツとお気に入りをリロードする.<br>\r
+ * まだロードされていない場合はあらたにロードする.<br>\r
+ * 引数newCdが指定されている場合は、現在のキャラクター定義の説明文を更新する.<br>\r
+ * (説明文の更新以外には使用されない.)<br>\r
+ *\r
+ * @param newCd\r
+ * 説明文更新のための更新されたキャラクターデータを指定する。null可\r
+ * @param forceRepaint\r
+ * 必ず再描画する場合\r
+ * @throws IOException\r
+ * 失敗\r
+ */\r
+ protected synchronized void reloadPartsAndFavorites(CharacterData newCd,\r
+ boolean forceRepaint) throws IOException {\r
+ if (newCd != null) {\r
+ // (インポート画面では説明文のみ更新するので、それだけ取得)\r
+ characterData.setDescription(newCd.getDescription());\r
+ }\r
+\r
+ if ( !characterData.isPartsLoaded()) {\r
+ // キャラクターデータが、まだ読み込まれていなければ読み込む.\r
+ ProfileListManager.loadCharacterData(characterData);\r
+ ProfileListManager.loadFavorites(characterData);\r
+ partsSelectionManager.loadParts();\r
+\r
+ } else {\r
+ // パーツデータをリロードする.\r
+ if (characterData.reloadPartsData()) {\r
+ partsSelectionManager.loadParts();\r
+ }\r
+\r
+ // お気に入りをリロードする.\r
+ CharacterDataPersistent persiste = CharacterDataPersistent.getInstance();\r
+ persiste.loadFavorites(characterData);\r
+\r
+ // お気に入りが更新されたことを通知する.\r
+ FavoritesChangeObserver.getDefault().notifyFavoritesChange(\r
+ MainFrame.this, characterData);\r
+ }\r
+\r
+ // 現在選択されているパーツセットがない場合はデフォルトのパーツセットを選択する.\r
+ if (showDefaultParts(false) || forceRepaint) {\r
+ requestPreview();\r
+ }\r
+ }\r
+\r
+ protected void onExport() {\r
+ if (!characterData.isValid()) {\r
+ Toolkit tk = Toolkit.getDefaultToolkit();\r
+ tk.beep();\r
+ return;\r
+ }\r
+ ExportWizardDialog exportWizDlg = new ExportWizardDialog(this, characterData, previewPane.getPreviewImage());\r
+ exportWizDlg.setVisible(true);\r
+ }\r
+\r
+ protected void onResetColor() {\r
+ if (!characterData.isValid()) {\r
+ Toolkit tk = Toolkit.getDefaultToolkit();\r
+ tk.beep();\r
+ return;\r
+ }\r
+\r
+ Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()\r
+ .getLocalizedProperties(STRINGS_RESOURCE);\r
+\r
+ if (JOptionPane.showConfirmDialog(this, strings.get("confirm.resetcolors"), strings.getProperty("confirm"),\r
+ JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) != JOptionPane.YES_OPTION) {\r
+ return;\r
+ }\r
+ characterData.getPartsColorManager().resetPartsColorInfo();\r
+ partsColorCoordinator.initColorDialog();\r
+ requestPreview();\r
+ }\r
+\r
+ /**\r
+ * プロファイルを閉じる.\r
+ */\r
+ protected void onCloseProfile() {\r
+ saveWorkingSet();\r
+ ProfileListManager.unregisterUsedCharacterData(characterData);\r
+\r
+ if (characterData.isValid()) {\r
+\r
+ // 最後に使用したキャラクターデータとして記憶する.\r
+ try {\r
+ RecentDataPersistent recentPersist = RecentDataPersistent.getInstance();\r
+ recentPersist.saveRecent(characterData);\r
+\r
+ } catch (Exception ex) {\r
+ logger.log(Level.WARNING, "recent data saving failed.", ex);\r
+ // recent情報の記録に失敗しても致命的ではないので、これは無視する.\r
+ }\r
+ }\r
+\r
+ // イメージビルダスレッド・ディレクトリ監視スレッドを停止する.\r
+ stopAgents();\r
+\r
+ // フレームウィンドウを破棄する.\r
+ dispose();\r
+\r
+ // 破棄されたことをロギングする.\r
+ logger.log(Level.FINE, "dispose mainframe.");\r
+ }\r
+\r
+ /**\r
+ * 開いている、すべてのプロファイルを閉じる.<br>\r
+ * (Mac OS Xのcmd+Qで閉じる場合などで使用される.)<br>\r
+ */\r
+ public static void closeAllProfiles() {\r
+ // ウィンドウが閉じられることでアクティブなフレームが切り替わる場合を想定し、\r
+ // 現在のアクティブなウィンドウをあらかじめ記憶しておく\r
+ MainFrame mainFrame = activedMainFrame;\r
+\r
+ // gcをかけてファイナライズを促進させる\r
+ SystemUtil.gc();\r
+\r
+ // ファイナライズされていないFrameのうち、ネイティブリソースと関連づけられている\r
+ // フレームについて、それがMainFrameのインスタンスであれば閉じる.\r
+ // ただし、現在アクティブなものは除く\r
+ for (Frame frame : JFrame.getFrames()) {\r
+ try {\r
+ if (frame.isDisplayable()) {\r
+ // ネイティブリソースと関連づけられているフレーム\r
+ if (frame instanceof MainFrame && frame != mainFrame) {\r
+ // MainFrameのインスタンスであるので閉じる処理が可能.\r
+ // (現在アクティブなメインフレームは最後に閉じるため、ここでは閉じない.)\r
+ ((MainFrame) frame).onCloseProfile();\r
+ }\r
+ }\r
+\r
+ } catch (Throwable ex) {\r
+ logger.log(Level.SEVERE, "mainframe closing failed.", ex);\r
+ // フレームを閉じるときに失敗した場合、通常、致命的問題だが\r
+ // クローズ処理は継続しなければならない.\r
+ }\r
+ }\r
+\r
+ // 現在アクティブなフレームを閉じる.\r
+ // 最後に閉じることで「最後に使ったプロファイル」として記憶させる.\r
+ if (activedMainFrame != null && activedMainFrame.isDisplayable()) {\r
+ try {\r
+ activedMainFrame.onCloseProfile();\r
+\r
+ } catch (Throwable ex) {\r
+ logger.log(Level.SEVERE, "mainframe closing failed.", ex);\r
+ // フレームを閉じるときに失敗した場合、通常、致命的問題だが\r
+ // クローズ処理は継続しなければならない.\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 画面の作業状態を保存する.\r
+ */\r
+ protected void saveWorkingSet() {\r
+ if (!characterData.isValid()) {\r
+ return;\r
+ }\r
+ try {\r
+ // ワーキングセットの作成\r
+ WorkingSet workingSet = new WorkingSet();\r
+ workingSet.setCharacterDocBase(characterData.getDocBase());\r
+ workingSet.setCharacterDataRev(characterData.getRev());\r
+ PartsSet partsSet = partsSelectionManager.createPartsSet();\r
+ workingSet.setPartsSet(partsSet);\r
+ workingSet.setPartsColorInfoMap(characterData\r
+ .getPartsColorManager().getPartsColorInfoMap());\r
+ workingSet.setLastUsedSaveDir(imageSaveHelper.getLastUsedSaveDir());\r
+ workingSet.setLastUsedExportDir(ExportWizardDialog.getLastUsedDir());\r
+ workingSet.setLastUsePresetParts(lastUsePresetParts);\r
+ workingSet\r
+ .setCharacterData(characterData.duplicateBasicInfo(false)); // パーツセットは保存しない.\r
+ workingSet.setWallpaperInfo(wallpaperInfo);\r
+\r
+ workingSet.setZoomFactor(previewPane.getZoomFactor());\r
+ workingSet.setViewPosition(previewPane.getViewPosition());\r
+\r
+ Dimension windowSize = getSize();\r
+ Point windowPos = getLocation();\r
+ Rectangle windowRect = new Rectangle(windowPos, windowSize);\r
+ workingSet.setWindowRect(windowRect);\r
+\r
+ // XML形式でのワーキングセットの保存\r
+ WorkingSetPersist workingSetPersist = WorkingSetPersist\r
+ .getInstance();\r
+ workingSetPersist.saveWorkingSet(workingSet);\r
+\r
+ } catch (Exception ex) {\r
+ ErrorMessageHelper.showErrorDialog(this, ex);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 保存されているカスタムレイヤーパターンをロードする\r
+ */\r
+ protected void loadCustomLayerOrder() {\r
+ try {\r
+ CustomLayerOrderPersist persist = CustomLayerOrderPersist.newInstance(characterData);\r
+ Map<String, List<CustomLayerOrder>> map = persist.load();\r
+ if (map != null) {\r
+ customLayerPatternMgr.setMap(map);\r
+ }\r
+\r
+ } catch (Exception ex) {\r
+ ErrorMessageHelper.showErrorDialog(this, ex);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * 画面の作業状態を復元する.\r
+ *\r
+ * @return ワーキングセットを読み込んだ場合はtrue、そうでなければfalse\r
+ */\r
+ protected boolean loadWorkingSet() {\r
+ if (!characterData.isValid()) {\r
+ return false;\r
+ }\r
+ try {\r
+ WorkingSetPersist workingSetPersist = WorkingSetPersist\r
+ .getInstance();\r
+ WorkingSet2 workingSet2 = workingSetPersist\r
+ .loadWorkingSet(characterData);\r
+ if (workingSet2 == null) {\r
+ // ワーキングセットがない場合.\r
+ return false;\r
+ }\r
+\r
+ AppConfig appConfig = AppConfig.getInstance();\r
+ Rectangle windowRect = workingSet2.getWindowRect();\r
+ if (appConfig.isEnableRestoreWindow() && windowRect != null) {\r
+ // 位置の復元\r
+ GraphicsEnvironment genv = GraphicsEnvironment.getLocalGraphicsEnvironment();\r
+ Rectangle desktopSize = genv.getMaximumWindowBounds(); // メインスクリーンのサイズ(デスクトップ領域のみ)\r
+ Point windowPos = windowRect.getLocation();\r
+ if (desktopSize.contains(windowPos)) {\r
+ setLocation(windowPos);\r
+ }\r
+\r
+ // サイズの復元\r
+ Dimension dim = windowRect.getSize();\r
+ if (dim.width < 100) {\r
+ dim.width = 100;\r
+ }\r
+ if (dim.height < 100) {\r
+ dim.height = 100;\r
+ }\r
+ setSize(dim);\r
+ } else {\r
+ // デフォルトのウィンドウ位置とサイズ\r
+ setDefaultWindowLocation();\r
+ }\r
+\r
+ URI docBase = characterData.getDocBase();\r
+ if (docBase != null\r
+ && !docBase.equals(workingSet2.getCharacterDocBase())) {\r
+ // docBaseが一致せず\r
+ return false;\r
+ }\r
+ String sig = characterData.toSignatureString();\r
+ if (!sig.equals(workingSet2.getCharacterDataSig())) {\r
+ // 構造が一致せず.\r
+ return false;\r
+ }\r
+\r
+ // パーツの色情報を復元する.\r
+ Map<PartsIdentifier, PartsColorInfo> partsColorInfoMap = characterData\r
+ .getPartsColorManager().getPartsColorInfoMap();\r
+ workingSet2.createCompatible(characterData, partsColorInfoMap);\r
+\r
+ // 選択されているパーツの復元\r
+ IndependentPartsSetInfo partsSetInfo = workingSet2\r
+ .getCurrentPartsSet();\r
+ if (partsSetInfo != null) {\r
+ PartsSet partsSet = IndependentPartsSetInfo.convertPartsSet(\r
+ partsSetInfo, characterData, false);\r
+ selectPresetParts(partsSet);\r
+\r
+ // 最後に選択したお気に入り情報の復元\r
+ IndependentPartsSetInfo lastUsePresetPartsInfo = workingSet2\r
+ .getLastUsePresetParts();\r
+ if (lastUsePresetPartsInfo != null\r
+ && lastUsePresetPartsInfo.getId() != null\r
+ && lastUsePresetPartsInfo.getId().trim().length() > 0) {\r
+ PartsSet lastUsePresetParts = IndependentPartsSetInfo\r
+ .convertPartsSet(lastUsePresetPartsInfo,\r
+ characterData, false);\r
+ if (lastUsePresetParts.isSameStructure(partsSet)) {\r
+ this.lastUsePresetParts = lastUsePresetParts;\r
+ showPresetName(lastUsePresetParts);\r
+ }\r
+ }\r
+ }\r
+\r
+ // 最後に保存したディレクトリを復元する.\r
+ imageSaveHelper.setLastUseSaveDir(workingSet2.getLastUsedSaveDir());\r
+ ExportWizardDialog.setLastUsedDir(workingSet2\r
+ .getLastUsedExportDir());\r
+\r
+ // 壁紙情報を取得する.\r
+ WallpaperInfo wallpaperInfo = workingSet2.getWallpaperInfo();\r
+ if (wallpaperInfo != null) {\r
+ // 壁紙情報を保存し、その情報をもとに背景を再描画する.\r
+ // (適用に失敗した場合はエラーは無視し、壁紙情報は保存しない.)\r
+ applyWallpaperInfo(wallpaperInfo, true);\r
+ }\r
+\r
+ // ズーム状態を復元する\r
+ Double zoomFactor = workingSet2.getZoomFactor();\r
+ if (appConfig.isEnableRestoreWindow() && zoomFactor != null) {\r
+ previewPane.setZoomFactor(zoomFactor);\r
+ final Point viewPosition = workingSet2.getViewPosition();\r
+ if (viewPosition != null) {\r
+ previewPane.setViewPosition(viewPosition);\r
+ }\r
+ }\r
+\r
+ return true;\r
+\r
+ } catch (Exception ex) {\r
+ ErrorMessageHelper.showErrorDialog(this, ex);\r
+ }\r
+ return false;\r
+ }\r
+\r
+\r
+ public void onAbout() {\r
+ try {\r
+ AboutBox aboutBox = new AboutBox(this);\r
+ aboutBox.showAboutBox();\r
+\r
+ } catch (Exception ex) {\r
+ ErrorMessageHelper.showErrorDialog(this, ex);\r
+ }\r
+ }\r
+\r
+ protected void onHelp() {\r
+ Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()\r
+ .getLocalizedProperties(STRINGS_RESOURCE);\r
+ String helpURL = strings.getProperty("help.url");\r
+ String helpDescription = strings.getProperty("help.show");\r
+ DesktopUtilities.browse(this, helpURL, helpDescription);\r
+ }\r
+\r
+ protected void onFlipHolizontal() {\r
+ if (!characterData.isValid()) {\r
+ Toolkit tk = Toolkit.getDefaultToolkit();\r
+ tk.beep();\r
+ return;\r
+ }\r
+\r
+ double[] affineTransformParameter = partsSelectionManager.getAffineTransformParameter();\r
+ if (affineTransformParameter == null) {\r
+ // 左右フリップするアフィン変換パラメータを構築する.\r
+ Dimension siz = characterData.getImageSize();\r
+ if (siz != null) {\r
+ affineTransformParameter = new double[] {-1., 0, 0, 1., siz.width, 0};\r
+ }\r
+ } else {\r
+ // アフィン変換パラメータをクリアする.\r
+ affineTransformParameter = null;\r
+ }\r
+ partsSelectionManager.setAffineTransformParameter(affineTransformParameter);\r
+ requestPreview();\r
+ }\r
+\r
+ protected void onSetDefaultPicture() {\r
+ if (!characterData.isValid()) {\r
+ Toolkit tk = Toolkit.getDefaultToolkit();\r
+ tk.beep();\r
+ return;\r
+ }\r
+ try {\r
+ BufferedImage samplePicture = previewPane.getPreviewImage();\r
+ if (samplePicture != null) {\r
+ CharacterDataPersistent persist = CharacterDataPersistent.getInstance();\r
+ persist.saveSamplePicture(characterData, samplePicture);\r
+ }\r
+\r
+ } catch (Exception ex) {\r
+ ErrorMessageHelper.showErrorDialog(this, ex);\r
+ }\r
+ }\r
+\r
+ protected void onInformation() {\r
+ if (!characterData.isValid()) {\r
+ Toolkit tk = Toolkit.getDefaultToolkit();\r
+ tk.beep();\r
+ return;\r
+ }\r
+\r
+ PartsSet partsSet = partsSelectionManager.createPartsSet();\r
+ InformationDialog infoDlg = new InformationDialog(this, characterData, partsSet, customLayerPatternMgr);\r
+ infoDlg.setVisible(true);\r
+ }\r
+\r
+ protected void onManageCustomLayer() {\r
+ List<PartsCategory> categories = characterData.getPartsCategories();\r
+\r
+ // レイヤー編集ダイアログの構築\r
+ final LayerOrderCustomizeDialog layerOrderCustomDlg = new LayerOrderCustomizeDialog(this, categories);\r
+ layerOrderCustomDlg.setModalityType(ModalityType.APPLICATION_MODAL);\r
+ layerOrderCustomDlg.addLayerOrderCustomizeListener(new LayerOrderCustomizeListener() {\r
+ @Override\r
+ public void onChange(charactermanaj.ui.LayerOrderCustomizeDialog.LayerOrderCustomizeListener.Change e) {\r
+ // レイヤーパターンの編集がされた場合、プレビューする\r
+ customLayerPatternMgr.applyCustomLayerOrder(layerOrderCustomDlg.getEdittingCustomLayerOrderList());\r
+ requestPreview();\r
+ }\r
+ });\r
+\r
+ // 現在保持しているレイヤーパターンの一覧を取得する\r
+ final LayerOrderCustomizeDialogModel layerOrderCustomDialogModel = new LayerOrderCustomizeDialogModel();\r
+ for (Map.Entry<String, List<CustomLayerOrder>> entry : customLayerPatternMgr.getMap().entrySet()) {\r
+ String patternName = entry.getKey();\r
+ List<CustomLayerOrder> customLayerOrderList = entry.getValue();\r
+ layerOrderCustomDialogModel.put(patternName, customLayerOrderList);\r
+ }\r
+\r
+ // 現在選択しているアクティブなレイヤーパターンを取得する\r
+ List<CustomLayerOrder> currentList = customLayerPatternMgr.getMergedCustomLayerOrderList();\r
+ layerOrderCustomDialogModel.setCurrentList(currentList);\r
+\r
+ layerOrderCustomDlg.setModel(layerOrderCustomDialogModel);\r
+\r
+ // パターンの保存時\r
+ layerOrderCustomDialogModel.addListChangeListener(new LayerOrderCustomizeDialogModel.ChangeListener() {\r
+ @Override\r
+ public void onChange(Change change) {\r
+ try {\r
+ // 編集されたレイヤーパターンの一覧を保存する。\r
+ Map<String, List<CustomLayerOrder>> customLayerPatternMap =\r
+ new HashMap<String, List<CustomLayerOrder>>();\r
+ for (String patternName : layerOrderCustomDialogModel.getPatternNames()) {\r
+ List<CustomLayerOrder> customLayerOrderList = layerOrderCustomDialogModel.getCopy(patternName);\r
+ if (customLayerOrderList != null) {\r
+ customLayerPatternMap.put(patternName, customLayerOrderList);\r
+ }\r
+ }\r
+\r
+ // この画面のカスタムレイヤー管理を更新する\r
+ customLayerPatternMgr.setMap(customLayerPatternMap);\r
+\r
+ // 設定したレイヤーパターンをファイルに永続化する\r
+ CustomLayerOrderPersist persist = CustomLayerOrderPersist.newInstance(characterData);\r
+ persist.save(customLayerPatternMap);\r
+\r
+ } catch (Exception ex) {\r
+ ErrorMessageHelper.showErrorDialog(MainFrame.this, ex);\r
+ }\r
+ }\r
+ });\r
+\r
+ // モーダルでダイアログを表示する\r
+ layerOrderCustomDlg.setLocationByPlatform(true);\r
+ layerOrderCustomDlg.setVisible(true);\r
+\r
+ // 最後に選択または保存したパターン名をデフォルトで選択状態とする\r
+ String lastPatternName = layerOrderCustomDlg.getLastPatternName();\r
+ if (lastPatternName != null && lastPatternName.trim().length() > 0) {\r
+ customLayerPatternMgr.setSelected(lastPatternName, true);\r
+ }\r
+\r
+ requestPreview();\r
+ }\r
+\r
+ protected void onManageFavorites() {\r
+ if (!characterData.isValid()) {\r
+ Toolkit tk = Toolkit.getDefaultToolkit();\r
+ tk.beep();\r
+ return;\r
+ }\r
+\r
+ if (lastUseManageFavoritesDialog != null) {\r
+ // 開いているダイアログがあれば、それにフォーカスを当てる.\r
+ if (lastUseManageFavoritesDialog.isDisplayable()\r
+ && lastUseManageFavoritesDialog.isVisible()) {\r
+ lastUseManageFavoritesDialog.requestFocus();\r
+ return;\r
+ }\r
+ }\r
+\r
+ // お気に入り編集ダイアログを開く\r
+ ManageFavoriteDialog dlg = new ManageFavoriteDialog(this, characterData);\r
+ dlg.setFavoriteManageCallback(new FavoriteManageCallback() {\r
+\r
+ public void selectFavorites(PartsSet partsSet) {\r
+ // お気に入り編集ダイアログで選択されたパーツを選択表示する.\r
+ selectPresetParts(partsSet);\r
+ }\r
+\r
+ public void updateFavorites(CharacterData characterData,\r
+ boolean savePreset) {\r
+ // お気に入りを登録する.\r
+ try {\r
+ setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));\r
+ try {\r
+ CharacterDataPersistent persiste = CharacterDataPersistent\r
+ .getInstance();\r
+ if (savePreset) {\r
+ persiste.updateProfile(characterData);\r
+ }\r
+\r
+ persiste.saveFavorites(characterData);\r
+\r
+ // お気に入りが更新されたことを通知する.\r
+ FavoritesChangeObserver.getDefault()\r
+ .notifyFavoritesChange(MainFrame.this,\r
+ characterData);\r
+\r
+ } finally {\r
+ setCursor(Cursor.getDefaultCursor());\r
+ }\r
+\r
+ } catch (Exception ex) {\r
+ ErrorMessageHelper.showErrorDialog(MainFrame.this, ex);\r
+ }\r
+ }\r
+ });\r
+ WindowAdjustLocationSupport.alignRight(this, dlg, 0, true);\r
+ dlg.setVisible(true);\r
+ lastUseManageFavoritesDialog = dlg;\r
+ }\r
+\r
+ protected void onRegisterFavorite() {\r
+ if (!characterData.isValid()) {\r
+ Toolkit tk = Toolkit.getDefaultToolkit();\r
+ tk.beep();\r
+ return;\r
+ }\r
+ try {\r
+ // パーツセットを生成\r
+ PartsSet partsSet = partsSelectionManager.createPartsSet();\r
+ if (partsSet.isEmpty()) {\r
+ // 空のパーツセットは登録しない.\r
+ return;\r
+ }\r
+\r
+ Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()\r
+ .getLocalizedProperties(STRINGS_RESOURCE);\r
+\r
+ // お気に入りに登録するパーツセットが最後に使用したお気に入りと同じ構成であれば、\r
+ // そのお気に入り名を使用する.\r
+ String initName = getSuggestPartsSetName(partsSet, false);\r
+\r
+ // カラー情報の有無のチェックボックス.\r
+ JCheckBox chkColorInfo = new JCheckBox(strings.getProperty("input.favoritesColorInfo"));\r
+ chkColorInfo.setSelected(true);\r
+ String partsSetId = null;\r
+ if (initName != null && lastUsePresetParts != null) {\r
+ partsSetId = lastUsePresetParts.getPartsSetId();\r
+ }\r
+\r
+ // 上書き保存の可否のチェックボックス\r
+ JCheckBox chkOverwrite = new JCheckBox(strings.getProperty("input.favoritesOverwrite"));\r
+ chkOverwrite.setSelected(partsSetId != null && partsSetId.length() > 0);\r
+ chkOverwrite.setEnabled(partsSetId != null && partsSetId.length() > 0);\r
+\r
+ // チェックボックスパネル\r
+ Box checkboxsPanel = new Box(BoxLayout.PAGE_AXIS);\r
+ checkboxsPanel.add(chkColorInfo);\r
+ checkboxsPanel.add(chkOverwrite);\r
+\r
+ // 入力ダイアログを開く\r
+ String name = (String) JOptionPane.showInputDialog(this,\r
+ checkboxsPanel,\r
+ strings.getProperty("input.favorites"),\r
+ JOptionPane.QUESTION_MESSAGE,\r
+ null,\r
+ null,\r
+ initName == null ? "" : initName);\r
+ if (name == null || name.trim().length() == 0) {\r
+ return;\r
+ }\r
+\r
+ boolean includeColorInfo = chkColorInfo.isSelected();\r
+ if (!includeColorInfo) {\r
+ // カラー情報を除去する.\r
+ partsSet.removeColorInfo();\r
+ }\r
+\r
+ // 新規の場合、もしくは上書きしない場合はIDを設定する.\r
+ if (partsSetId == null || !chkOverwrite.isSelected()) {\r
+ partsSetId = "ps" + UUID.randomUUID().toString();\r
+ }\r
+ partsSet.setPartsSetId(partsSetId);\r
+\r
+ // 名前を設定する.\r
+ partsSet.setLocalizedName(name);\r
+\r
+ // ファイルに保存\r
+ setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));\r
+ try {\r
+ CharacterDataPersistent persiste = CharacterDataPersistent.getInstance();\r
+ // 現在の最新情報を取り出す.\r
+ characterData.clearPartsSets(true);\r
+ persiste.loadFavorites(characterData);\r
+\r
+ // お気に入りコレクションに登録\r
+ characterData.addPartsSet(partsSet);\r
+\r
+ persiste.saveFavorites(characterData);\r
+\r
+ // お気に入りが更新されたことを通知する.\r
+ FavoritesChangeObserver.getDefault().notifyFavoritesChange(\r
+ MainFrame.this, characterData);\r
+\r
+ } finally {\r
+ setCursor(Cursor.getDefaultCursor());\r
+ }\r
+\r
+ // 最後に選択したお気に入りにする\r
+ lastUsePresetParts = partsSet;\r
+ showPresetName(partsSet);\r
+\r
+ } catch (Exception ex) {\r
+ ErrorMessageHelper.showErrorDialog(this, ex);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * ランダム選択ダイアログを開く.\r
+ */\r
+ protected void onToolRandom() {\r
+ if (!characterData.isValid()) {\r
+ Toolkit tk = Toolkit.getDefaultToolkit();\r
+ tk.beep();\r
+ return;\r
+ }\r
+\r
+ if (lastUsePartsRandomChooserDialog != null) {\r
+ // 開いているダイアログがあれば、それにフォーカスを当てる.\r
+ if (lastUsePartsRandomChooserDialog.isDisplayable()\r
+ && lastUsePartsRandomChooserDialog.isVisible()) {\r
+ lastUsePartsRandomChooserDialog.requestFocus();\r
+ return;\r
+ }\r
+ }\r
+\r
+ // お気に入り編集ダイアログを開く\r
+ PartsRandomChooserDialog dlg = new PartsRandomChooserDialog(this,\r
+ characterData,\r
+ new PartsRandomChooserDialog.PartsSetSynchronizer() {\r
+ public PartsSet getCurrentPartsSet() {\r
+ // 現在のパーツセットを生成\r
+ return partsSelectionManager.createPartsSet();\r
+ }\r
+\r
+ public void setPartsSet(PartsSet partsSet) {\r
+ selectPresetParts(partsSet);\r
+ }\r
+\r
+ public boolean\r
+ isExcludePartsIdentifier(PartsIdentifier partsIdentifier) {\r
+ Boolean exclude = randomExcludePartsIdentifierMap\r
+ .get(partsIdentifier);\r
+ return exclude != null && exclude.booleanValue();\r
+ }\r
+\r
+ public void\r
+ setExcludePartsIdentifier(PartsIdentifier partsIdentifier,\r
+ boolean exclude) {\r
+ randomExcludePartsIdentifierMap.put(partsIdentifier,\r
+ exclude);\r
+ }\r
+ });\r
+\r
+ WindowAdjustLocationSupport.alignRight(this, dlg, 0, true);\r
+ dlg.setVisible(true);\r
+ lastUsePartsRandomChooserDialog = dlg;\r
+ }\r
+\r
+ /**\r
+ * ランダム選択パーツで選択候補から除外するパーツのマップ.\r
+ */\r
+ private HashMap<PartsIdentifier, Boolean> randomExcludePartsIdentifierMap =\r
+ new HashMap<PartsIdentifier, Boolean>();\r
+\r
+ /**\r
+ * すべての解除可能なパーツの選択を解除する。\r
+ */\r
+ protected void onDeselectAll() {\r
+ partsSelectionManager.deselectAll();\r
+ }\r
+\r
+ /**\r
+ * 単一選択カテゴリのパーツの解除を許可する。\r
+ */\r
+ protected void onDeselectableAllCategory() {\r
+ partsSelectionManager\r
+ .setDeselectableSingleCategory( !partsSelectionManager\r
+ .isDeselectableSingleCategory());\r
+ }\r
+\r
+ /**\r
+ * プレビューのズームボックスの表示制御\r
+ */\r
+ protected void onEnableZoom() {\r
+ previewPane.setVisibleZoomBox( !previewPane.isVisibleZoomBox());\r
+ }\r
+\r
+ /**\r
+ * メニューバーを構築します.\r
+ *\r
+ * @return メニューバー\r
+ */\r
+ protected JMenuBar createMenuBar() {\r
+ final Properties strings = LocalizedResourcePropertyLoader\r
+ .getCachedInstance().getLocalizedProperties(STRINGS_RESOURCE);\r
+\r
+ MenuDataFactory[] menus = new MenuDataFactory[] {\r
+ new MenuDataFactory("menu.file", new MenuDataFactory[] {\r
+ new MenuDataFactory("file.openProfile", new ActionListener() {\r
+ public void actionPerformed(ActionEvent e) {\r
+ onOpenProfile();\r
+ }\r
+ }),\r
+ new MenuDataFactory("file.savePicture", new ActionListener() {\r
+ public void actionPerformed(ActionEvent e) {\r
+ onSavePicture();\r
+ }\r
+ }),\r
+ new MenuDataFactory("file.ukagaka", new MenuDataFactory[] {\r
+ new MenuDataFactory("file.saveAsUkagaka", new ActionListener() {\r
+ public void actionPerformed(ActionEvent e) {\r
+ onSaveAsUkagaka();\r
+ };\r
+ }),\r
+ new MenuDataFactory("file.convertUkagaka", new ActionListener() {\r
+ public void actionPerformed(ActionEvent e) {\r
+ onConvertUkagaka();\r
+ };\r
+ }),\r
+ }),\r
+ null,\r
+ new MenuDataFactory("file.editprofile", new ActionListener() {\r
+ public void actionPerformed(ActionEvent e) {\r
+ onEditProfile();\r
+ }\r
+ }),\r
+ new MenuDataFactory("file.opendir", new ActionListener() {\r
+ public void actionPerformed(ActionEvent e) {\r
+ onBrowseProfileDir();\r
+ }\r
+ }),\r
+ new MenuDataFactory("file.import", new MenuDataFactory[] {\r
+ new MenuDataFactory("file.importMe", new ActionListener() {\r
+ public void actionPerformed(ActionEvent e) {\r
+ onImport(null);\r
+ };\r
+ }),\r
+ new MenuDataFactory("file.importNew", new ActionListener() {\r
+ public void actionPerformed(ActionEvent e) {\r
+ onImportNew();\r
+ };\r
+ }),\r
+ }),\r
+ new MenuDataFactory("file.export", new ActionListener() {\r
+ public void actionPerformed(ActionEvent e) {\r
+ onExport();\r
+ };\r
+ }),\r
+ new MenuDataFactory("file.manageParts", new ActionListener() {\r
+ public void actionPerformed(ActionEvent e) {\r
+ onManageParts();\r
+ }\r
+ }),\r
+ new MenuDataFactory("file.preferences", new ActionListener() {\r
+ public void actionPerformed(ActionEvent e) {\r
+ onPreferences();\r
+ };\r
+ }),\r
+ null,\r
+ new MenuDataFactory("file.closeProfile", new ActionListener() {\r
+ public void actionPerformed(ActionEvent e) {\r
+ onCloseProfile();\r
+ }\r
+ }),\r
+ }),\r
+ new MenuDataFactory("menu.edit", new MenuDataFactory[] {\r
+ new MenuDataFactory("edit.search", new ActionListener() {\r
+ public void actionPerformed(ActionEvent e) {\r
+ openSearchDialog();\r
+ }\r
+ }),\r
+ new MenuDataFactory("edit.copy", new ActionListener() {\r
+ public void actionPerformed(ActionEvent e) {\r
+ onCopy((e.getModifiers() & ActionEvent.SHIFT_MASK) != 0);\r
+ }\r
+ }),\r
+ new MenuDataFactory("edit.flipHorizontal", new ActionListener() {\r
+ public void actionPerformed(ActionEvent e) {\r
+ onFlipHolizontal();\r
+ }\r
+ }),\r
+ new MenuDataFactory("edit.resetcolor", new ActionListener() {\r
+ public void actionPerformed(ActionEvent e) {\r
+ onResetColor();\r
+ }\r
+ }),\r
+ null,\r
+ new MenuDataFactory("edit.setDefaultPicture", new ActionListener() {\r
+ public void actionPerformed(ActionEvent e) {\r
+ onSetDefaultPicture();\r
+ }\r
+ }),\r
+ new MenuDataFactory("edit.information", new ActionListener() {\r
+ public void actionPerformed(ActionEvent e) {\r
+ onInformation();\r
+ }\r
+ }),\r
+ null,\r
+ new MenuDataFactory("edit.deselectall", new ActionListener() {\r
+ public void actionPerformed(ActionEvent e) {\r
+ onDeselectAll();\r
+ }\r
+ }),\r
+ new MenuDataFactory("edit.deselectparts", true, new ActionListener() {\r
+ public void actionPerformed(ActionEvent e) {\r
+ onDeselectableAllCategory();\r
+ }\r
+ }),\r
+ new MenuDataFactory("edit.enableAutoShrink", true, new ActionListener() {\r
+ public void actionPerformed(ActionEvent e) {\r
+ onClickPartsCategoryTitle(null, true);\r
+ }\r
+ }),\r
+ null,\r
+ new MenuDataFactory("edit.enableZoomBox", true, new ActionListener() {\r
+ public void actionPerformed(ActionEvent e) {\r
+ onEnableZoom();\r
+ }\r
+ }),\r
+ null,\r
+ new MenuDataFactory("edit.changeBgColor", new ActionListener() {\r
+ public void actionPerformed(ActionEvent e) {\r
+ onChangeBgColor();\r
+ }\r
+ }),\r
+ new MenuDataFactory("edit.changeWallpaper", new ActionListener() {\r
+ public void actionPerformed(ActionEvent e) {\r
+ onChangeWallpaper();\r
+ }\r
+ }),\r
+ }),\r
+ new MenuDataFactory("menu.favorite", new MenuDataFactory[] {\r
+ new MenuDataFactory("favorite.register", new ActionListener() {\r
+ public void actionPerformed(ActionEvent e) {\r
+ onRegisterFavorite();\r
+ }\r
+ }),\r
+ new MenuDataFactory("favorite.manage", new ActionListener() {\r
+ public void actionPerformed(ActionEvent e) {\r
+ onManageFavorites();\r
+ }\r
+ }),\r
+ null,\r
+ }),\r
+ new MenuDataFactory("menu.customlayer", new MenuDataFactory[] {\r
+ new MenuDataFactory("customlayer.manage", new ActionListener() {\r
+ public void actionPerformed(ActionEvent e) {\r
+ onManageCustomLayer();\r
+ }\r
+ }),\r
+ null,\r
+ }),\r
+ new MenuDataFactory("menu.tool",\r
+ new MenuDataFactory[]{new MenuDataFactory(\r
+ "tool.random", new ActionListener() {\r
+ public void actionPerformed(ActionEvent e) {\r
+ onToolRandom();\r
+ }\r
+ }),}),\r
+ new MenuDataFactory("menu.help", new MenuDataFactory[] {\r
+ new MenuDataFactory("help.recommendations", (ActionListener) null),\r
+ null,\r
+ new MenuDataFactory("help.help", new ActionListener() {\r
+ public void actionPerformed(ActionEvent e) {\r
+ onHelp();\r
+ }\r
+ }),\r
+ new MenuDataFactory("help.forum",\r
+ DesktopUtilities.createBrowseAction(\r
+ MainFrame.this,\r
+ strings.getProperty("help.forum.url"),\r
+ strings.getProperty("help.forum.description"))\r
+ ),\r
+ new MenuDataFactory("help.bugreport",\r
+ DesktopUtilities.createBrowseAction(\r
+ MainFrame.this,\r
+ strings.getProperty("help.reportbugs.url"),\r
+ strings.getProperty("help.reportbugs.description"))\r
+ ),\r
+ new MenuDataFactory("help.about", new ActionListener() {\r
+ public void actionPerformed(ActionEvent e) {\r
+ onAbout();\r
+ }\r
+ }),\r
+ }), };\r
+\r
+ final MenuBuilder menuBuilder = new MenuBuilder();\r
+\r
+ JMenuBar menuBar = menuBuilder.createMenuBar(menus);\r
+\r
+ menuBuilder.getJMenu("menu.edit").addMenuListener(new MenuListener() {\r
+ public void menuCanceled(MenuEvent e) {\r
+ // do nothing.\r
+ }\r
+ public void menuDeselected(MenuEvent e) {\r
+ // do nothing.\r
+ }\r
+ public void menuSelected(MenuEvent e) {\r
+ menuBuilder.getJMenuItem("edit.copy").setEnabled(previewPane.getPreviewImage() != null);\r
+ menuBuilder.getJMenuItem("edit.deselectparts").setSelected(\r
+ partsSelectionManager.isDeselectableSingleCategory());\r
+ menuBuilder.getJMenuItem("edit.enableAutoShrink").setSelected(minimizeMode);\r
+ menuBuilder.getJMenuItem("edit.enableZoomBox").setSelected(previewPane.isVisibleZoomBox());\r
+ }\r
+ });\r
+ final JMenu mnuFavorites = menuBuilder.getJMenu("menu.favorite");\r
+ mnuFavorites.addMenuListener(new MenuListener() {\r
+ public void menuCanceled(MenuEvent e) {\r
+ // do nothing.\r
+ }\r
+ public void menuDeselected(MenuEvent e) {\r
+ // do nothing.\r
+ }\r
+ public void menuSelected(MenuEvent e) {\r
+ onSelectedFavoriteMenu(mnuFavorites);\r
+ }\r
+ });\r
+ final JMenu mnuCustomLayer = menuBuilder.getJMenu("menu.customlayer");\r
+ mnuCustomLayer.addMenuListener(new MenuListener() {\r
+ public void menuCanceled(MenuEvent e) {\r
+ // do nothing.\r
+ }\r
+ public void menuDeselected(MenuEvent e) {\r
+ // do nothing.\r
+ }\r
+ public void menuSelected(MenuEvent e) {\r
+ onSelectedCustomLayerMenu(mnuCustomLayer);\r
+ }\r
+ });\r
+\r
+ // J2SE5の場合は「パーツディレクトリを開く」コマンドは使用不可とする.\r
+ if (System.getProperty("java.version").startsWith("1.5")) {\r
+ menuBuilder.getJMenuItem("file.opendir").setEnabled(false);\r
+ }\r
+\r
+ // お勧めサイトメニュー構築\r
+ final JMenu mnuRecomendation = menuBuilder.getJMenu("help.recommendations");\r
+ JMenu mnuHelp = menuBuilder.getJMenu("menu.help");\r
+ mnuHelp.addMenuListener(new MenuListener() {\r
+ public void menuCanceled(MenuEvent e) {\r
+ // do nothing.\r
+ }\r
+ public void menuDeselected(MenuEvent e) {\r
+ // do nothing.\r
+ }\r
+ public void menuSelected(MenuEvent e) {\r
+ onSelectedRecommendationMenu(mnuRecomendation);\r
+ }\r
+ });\r
+\r
+ return menuBar;\r
+ }\r
+\r
+}\r