OSDN Git Service

ver0.95リリース 2011/02/21
[charactermanaj/CharacterManaJ.git] / src / charactermanaj / ui / ColorDialog.java
1 package charactermanaj.ui;\r
2 \r
3 import java.awt.BorderLayout;\r
4 import java.awt.Container;\r
5 import java.awt.GridBagConstraints;\r
6 import java.awt.GridBagLayout;\r
7 import java.awt.GridLayout;\r
8 import java.awt.Insets;\r
9 import java.awt.Point;\r
10 import java.awt.event.ActionEvent;\r
11 import java.awt.event.ActionListener;\r
12 import java.awt.event.WindowAdapter;\r
13 import java.awt.event.WindowEvent;\r
14 import java.util.Collection;\r
15 import java.util.HashMap;\r
16 import java.util.LinkedList;\r
17 import java.util.Map;\r
18 import java.util.Properties;\r
19 import java.util.concurrent.Semaphore;\r
20 import java.util.concurrent.atomic.AtomicInteger;\r
21 import java.util.logging.Level;\r
22 import java.util.logging.Logger;\r
23 \r
24 import javax.swing.AbstractAction;\r
25 import javax.swing.Action;\r
26 import javax.swing.BorderFactory;\r
27 import javax.swing.Box;\r
28 import javax.swing.BoxLayout;\r
29 import javax.swing.JButton;\r
30 import javax.swing.JCheckBox;\r
31 import javax.swing.JComboBox;\r
32 import javax.swing.JDialog;\r
33 import javax.swing.JFrame;\r
34 import javax.swing.JLabel;\r
35 import javax.swing.JPanel;\r
36 import javax.swing.JSlider;\r
37 import javax.swing.JSpinner;\r
38 import javax.swing.JTabbedPane;\r
39 import javax.swing.SpinnerNumberModel;\r
40 import javax.swing.event.ChangeEvent;\r
41 import javax.swing.event.ChangeListener;\r
42 \r
43 import charactermanaj.graphics.filters.ColorConv;\r
44 import charactermanaj.graphics.filters.ColorConvertParameter;\r
45 import charactermanaj.model.AppConfig;\r
46 import charactermanaj.model.ColorGroup;\r
47 import charactermanaj.model.Layer;\r
48 import charactermanaj.model.PartsCategory;\r
49 import charactermanaj.model.PartsIdentifier;\r
50 import charactermanaj.ui.model.ColorChangeEvent;\r
51 import charactermanaj.ui.model.ColorChangeListener;\r
52 import charactermanaj.util.LocalizedResourcePropertyLoader;\r
53 \r
54 \r
55 /**\r
56  * カラーダイアログ.<br>\r
57  * カラーダイアログはカテゴリ別に関連づけられており、カテゴリ内の各レイヤーに対応するタブを持つ.<br>\r
58  * @author seraphy\r
59  */\r
60 public class ColorDialog extends JDialog {\r
61 \r
62         private static final long serialVersionUID = 1L;\r
63 \r
64         /**\r
65          * ロガー\r
66          */\r
67         private static final Logger logger = Logger.getLogger(ColorDialog.class.getName());\r
68 \r
69         /**\r
70          * このカラーダイアログが対応するカテゴリ\r
71          */\r
72         private final PartsCategory partsCategory;\r
73 \r
74         /**\r
75          * レイヤーごとのタブのマップ\r
76          */\r
77         private HashMap<Layer, ColorDialogTabPanel> tabs = new HashMap<Layer, ColorDialogTabPanel>();\r
78         \r
79         /**\r
80          * タブペイン\r
81          */\r
82         private JTabbedPane tabbedPane;\r
83         \r
84         /**\r
85          * レイヤーに対するタブインデックスのマップ\r
86          */\r
87         private HashMap<Layer, Integer> tabbedPaneIndexMap = new HashMap<Layer, Integer>();\r
88         \r
89         /**\r
90          * 色変更イベントのリスナのコレクション\r
91          */\r
92         private LinkedList<ColorChangeListener> listeners = new LinkedList<ColorChangeListener>();\r
93 \r
94         /**\r
95          * キャプションのプレフィックス\r
96          */\r
97         private String captionBase;\r
98         \r
99         /**\r
100          * 現在表示しているカラー情報の対象としているパーツ識別子\r
101          */\r
102         private PartsIdentifier partsIdentifier;\r
103         \r
104         /**\r
105          * カテゴリ全体に適用するチェックボックス\r
106          */\r
107         private JCheckBox chkApplyAll;\r
108         \r
109         /**\r
110          * コンストラクタ\r
111          * @param parent 親フレーム\r
112          * @param partsCategory カテゴリ\r
113          * @param colorGroups 選択可能なカラーグループのコレクション\r
114          */\r
115         public ColorDialog(JFrame parent, PartsCategory partsCategory, Collection<ColorGroup> colorGroups) {\r
116                 super(parent);\r
117                 this.partsCategory = partsCategory;\r
118 \r
119                 final Properties strings = LocalizedResourcePropertyLoader.getInstance().getLocalizedProperties("strings/colordialog");\r
120                 \r
121                 String caption = strings.getProperty("colordialog.caption");\r
122                 String name = partsCategory.getLocalizedCategoryName();\r
123                 captionBase = caption + name;\r
124                 setTitle(captionBase);\r
125                 \r
126                 addWindowListener(new WindowAdapter() {\r
127                         @Override\r
128                         public void windowClosing(WindowEvent e) {\r
129                                 setVisible(false);\r
130                         }\r
131                 });\r
132                 \r
133                 Container container = getContentPane();\r
134                 \r
135                 this.tabbedPane = new JTabbedPane(JTabbedPane.TOP, JTabbedPane.WRAP_TAB_LAYOUT);\r
136                 for (final Layer layer : partsCategory.getLayers()) {\r
137                         \r
138                         ColorDialogTabPanel tabContainer = new ColorDialogTabPanel(this, layer, colorGroups);\r
139                         final ColorChangeListener innerListener = new ColorChangeListener() {\r
140                                 private Semaphore semaphore = new Semaphore(1); // イベントが循環発生することを防ぐ\r
141                                 public void onColorChange(ColorChangeEvent event) {\r
142                                         if (semaphore.tryAcquire()) {\r
143                                                 try {\r
144                                                         ColorDialog.this.fireColorChangeEvent(layer, false);\r
145                                                 } finally {\r
146                                                         semaphore.release();\r
147                                                 }\r
148                                         }\r
149                                 }\r
150                                 public void onColorGroupChange(ColorChangeEvent event) {\r
151                                         if (semaphore.tryAcquire()) {\r
152                                                 try {\r
153                                                         ColorDialog.this.fireColorGroupChangeEvent(layer);\r
154                                                         ColorDialog.this.fireColorChangeEvent(layer, false);\r
155                                                 } finally {\r
156                                                         semaphore.release();\r
157                                                 }\r
158                                         }\r
159                                 }\r
160                         };\r
161                         tabContainer.addColorChangeListener(innerListener);\r
162 \r
163                         tabbedPane.addTab(layer.getLocalizedName(), tabContainer);\r
164                         tabbedPaneIndexMap.put(layer, tabbedPane.getTabCount() - 1);\r
165                         tabs.put(layer, tabContainer);\r
166                 }\r
167 \r
168                 Action actApply = new AbstractAction(strings.getProperty("button.apply")) {\r
169                         private static final long serialVersionUID = 1L;\r
170                         public void actionPerformed(ActionEvent e) {\r
171                                 apply();\r
172                         }\r
173                 };\r
174                 Action actReset = new AbstractAction(strings.getProperty("button.reset")) {\r
175                         private static final long serialVersionUID = 1L;\r
176                         public void actionPerformed(ActionEvent e) {\r
177                                 ColorDialogTabPanel tab = (ColorDialogTabPanel) tabbedPane\r
178                                                 .getSelectedComponent();\r
179                                 if (tab != null) {\r
180                                         resetColor(tab);\r
181                                         apply();\r
182                                 }\r
183                         }\r
184                 };\r
185 \r
186                 JPanel btnPanel = new JPanel();\r
187                 btnPanel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));\r
188                 GridBagLayout gbl = new GridBagLayout();\r
189                 btnPanel.setLayout(gbl);\r
190 \r
191                 int colIdx = 0;\r
192                 \r
193                 GridBagConstraints gbc = new GridBagConstraints();\r
194                 gbc.gridx = colIdx++;\r
195                 gbc.gridy = 0;\r
196                 gbc.ipadx = 0;\r
197                 gbc.ipady = 0;\r
198                 gbc.anchor = GridBagConstraints.EAST;\r
199                 gbc.fill = GridBagConstraints.BOTH;\r
200                 gbc.gridheight = 1;\r
201                 gbc.gridwidth = 1;\r
202                 gbc.weightx = 0.;\r
203                 gbc.weighty = 0.;\r
204                 chkApplyAll = new JCheckBox(strings.getProperty("checkbox.applyAllItems"));\r
205                 chkApplyAll.setSelected(!partsCategory.isMultipleSelectable());\r
206                 chkApplyAll.addActionListener(new ActionListener() {\r
207                         public void actionPerformed(ActionEvent e) {\r
208                                 // すべてに適用のチェックが変更された場合は全レイヤーの色の変更通知を出す.\r
209                                 apply();\r
210                         }\r
211                 });\r
212                 btnPanel.add(chkApplyAll, gbc);\r
213                 \r
214                 gbc.gridx = colIdx++;\r
215                 gbc.gridy = 0;\r
216                 gbc.weightx = 1.;\r
217                 btnPanel.add(Box.createHorizontalGlue(), gbc);\r
218 \r
219                 JButton btnApply = null;\r
220                 AppConfig appConfig = AppConfig.getInstance();\r
221                 if ( !appConfig.isEnableAutoColorChange()) {\r
222                         gbc.gridx = colIdx++;\r
223                         gbc.gridy = 0;\r
224                         gbc.weightx = 0.;\r
225                         btnApply = new JButton(actApply);\r
226                         btnPanel.add(btnApply, gbc);\r
227                 }\r
228 \r
229                 gbc.gridx = colIdx++;\r
230                 gbc.gridy = 0;\r
231                 gbc.weightx = 0.;\r
232                 JButton btnReset = new JButton(actReset);\r
233                 btnPanel.add(btnReset, gbc);\r
234 \r
235                 container.setLayout(new BorderLayout());\r
236                 container.add(tabbedPane, BorderLayout.CENTER);\r
237                 container.add(btnPanel, BorderLayout.SOUTH);\r
238                 \r
239                 if (btnApply != null) {\r
240                         getRootPane().setDefaultButton(btnApply);\r
241                 }\r
242                 \r
243                 pack();\r
244         }\r
245         \r
246         /**\r
247          * ダイアログの表示位置を調整する.<br>\r
248          * 横位置Xはメインフレームの右側とし、縦位置Yはメインフレームの上位置からのoffset_yを加えた位置とする.<br>\r
249          * @param offset_y オフセットY\r
250          */\r
251         public void adjustLocation(int offset_y) {\r
252                 Point pt = getParent().getLocation();\r
253                 Insets insets = getParent().getInsets();\r
254                 pt.x += getParent().getWidth();\r
255                 pt.y += (offset_y * insets.top);\r
256                 setLocation(pt);\r
257         }\r
258 \r
259         /**\r
260          * このカラーダイアログが対応するパーツカテゴリを取得する.<br>\r
261          * @return パーツカテゴリ\r
262          */\r
263         public PartsCategory getPartsCategory() {\r
264                 return partsCategory;\r
265         }\r
266         \r
267         /**\r
268          * 指定したレイヤーのカラーグループが「連動」しているか?<br>\r
269          * カテゴリに属していないレイヤーを指定した場合は常にfalseを返す.<br>\r
270          * @param layer レイヤー\r
271          * @return 連動している場合はtrue、そうでなければfalse\r
272          */\r
273         public boolean isSyncColorGroup(Layer layer) {\r
274                 ColorDialogTabPanel tab = tabs.get(layer);\r
275                 if (tab == null) {\r
276                         return false;\r
277                 }\r
278                 return tab.isSyncColorGroup();\r
279         }\r
280 \r
281         /**\r
282          * 指定したレイヤーのカラーグループの連動フラグを設定する.<br>\r
283          * カテゴリに属していないレイヤーを指定した場合は何もしない.<br>\r
284          * @param layer レイヤー\r
285          * @param selected 連動フラグ\r
286          */\r
287         public void setSyncColorGroup(Layer layer, boolean selected) {\r
288                 ColorDialogTabPanel tab = tabs.get(layer);\r
289                 if (tab != null) {\r
290                         tab.setSyncColorGroup(selected);\r
291                 }\r
292         }\r
293 \r
294         /**\r
295          * レイヤーごとの色情報のマップを指定して、各レイヤーに色情報を設定する.<br>\r
296          * カテゴリに属していないレイヤーが含まれる場合は例外となる.<br>\r
297          * @param params レイヤーと色情報のマップ\r
298          */\r
299         public void setColorConvertParameters(Map<Layer, ColorConvertParameter> params) {\r
300                 if (params == null) {\r
301                         throw new IllegalArgumentException();\r
302                 }\r
303                 for (Layer layer : partsCategory.getLayers()) {\r
304                         ColorConvertParameter param = params.get(layer);\r
305                         if (param == null) {\r
306                                 param = new ColorConvertParameter();\r
307                         }\r
308                         setColorConvertParameter(layer, param);\r
309                 }\r
310         }\r
311         \r
312         /**\r
313          * 対象となるパーツ識別子を指定する。<br>\r
314          * カラーダイアログのキャプションにパーツ名を設定される.<br>\r
315          * nullを指定した場合はキャプションからパーツ名が除去される.<br>\r
316          * @param partsIdentifier パーツ識別子、もしくはnull\r
317          */\r
318         public void setPartsIdentifier(PartsIdentifier partsIdentifier) {\r
319                 this.partsIdentifier = partsIdentifier;\r
320                 if (partsIdentifier == null) {\r
321                         setTitle(captionBase);\r
322                 } else {\r
323                         setTitle(captionBase + "(" + partsIdentifier.getLocalizedPartsName() + ")");\r
324                 }\r
325         }\r
326 \r
327         /**\r
328          * 対象となるパーツ識別子を取得する.<br>\r
329          * 設定されていなければnullが返される.<br>\r
330          * @return パーツ識別子、もしくはnull\r
331          */\r
332         public PartsIdentifier getPartsIdentifier() {\r
333                 return partsIdentifier;\r
334         }\r
335 \r
336         /**\r
337          * 各レイヤーのタブの有効・無効状態を設定します.<br>\r
338          * カテゴリに属さないレイヤーは無視されます.<br>\r
339          * nullを指定した場合は、すべてのレイヤーが「有効」となります.<br>\r
340          * @param layers 有効とするレイヤーのコレクション、もしくはnull\r
341          */\r
342         public void setEnableLayers(Collection<Layer> layers) {\r
343                 for (Map.Entry<Layer, ColorDialogTabPanel> entry : tabs.entrySet()) {\r
344                         Layer layer = entry.getKey();\r
345                         boolean enabled = (layers == null) || layers.contains(layer);\r
346                         Integer tabIndex = tabbedPaneIndexMap.get(layer);\r
347                         if (tabIndex != null) {\r
348                                 tabbedPane.setEnabledAt(tabIndex, enabled);\r
349                                 \r
350                                 if (logger.isLoggable(Level.FINEST)) {\r
351                                         logger.log(Level.FINEST, "setEnableLayers(" + layer + ")=" + enabled);\r
352                                 }\r
353                         }\r
354                 }\r
355         }\r
356 \r
357         /**\r
358          * 「すべてに適用」フラグを取得する.<br>\r
359          * @return すべてに適用フラグ\r
360          */\r
361         public boolean isApplyAll() {\r
362                 return chkApplyAll.isSelected();\r
363         }\r
364         \r
365         /**\r
366          * 各レイヤーと、その色情報をマップとして取得する.<br>\r
367          * @return 各レイヤーと、その色情報のマップ\r
368          */\r
369         public Map<Layer, ColorConvertParameter> getColorConvertParameters() {\r
370                 HashMap<Layer, ColorConvertParameter> params = new HashMap<Layer, ColorConvertParameter>();\r
371                 for (Layer layer : partsCategory.getLayers()) {\r
372                         ColorDialogTabPanel tab = tabs.get(layer);\r
373                         ColorConvertParameter param = tab.getColorConvertParameter();\r
374                         params.put(layer, param);\r
375                 }\r
376                 return params;\r
377         }\r
378 \r
379         /**\r
380          * レイヤーを指定して、色情報を設定する.<br>\r
381          * カテゴリに属していないレイヤーを指定した場合は例外となる.<br>\r
382          * @param layer レイヤー\r
383          * @param param 色情報\r
384          */\r
385         public void setColorConvertParameter(Layer layer, ColorConvertParameter param) {\r
386                 if (layer == null || param == null) {\r
387                         throw new IllegalArgumentException();\r
388                 }\r
389                 ColorDialogTabPanel tab = tabs.get(layer);\r
390                 if (tab == null) {\r
391                         throw new IllegalArgumentException("layer not found. " + layer + "/tabs=" + tabs);\r
392                 }\r
393                 tab.setColorConvertParameter(param);\r
394         }\r
395 \r
396         /**\r
397          * 指定したレイヤーの色情報を取得する.<br>\r
398          * カテゴリに属していないレイヤーを指定した場合は例外となる.<br>\r
399          * @param layer レイヤー\r
400          * @return 色情報\r
401          */\r
402         public ColorConvertParameter getColorConvertParameter(Layer layer) {\r
403                 if (layer == null) {\r
404                         throw new IllegalArgumentException();\r
405                 }\r
406                 ColorDialogTabPanel tab = tabs.get(layer);\r
407                 if (tab == null) {\r
408                         throw new IllegalArgumentException("layer not found. " + layer);\r
409                 }\r
410                 return tab.getColorConvertParameter();\r
411         }\r
412         \r
413         /**\r
414          * 指定したレイヤーのカラーグループを取得する.<br>\r
415          * カテゴリに属さないレイヤーを指定した場合は例外となる.<br>\r
416          * @param layer レイヤー\r
417          * @return カラーグループ\r
418          */\r
419         public ColorGroup getColorGroup(Layer layer) {\r
420                 if (layer == null) {\r
421                         throw new IllegalArgumentException();\r
422                 }\r
423                 ColorDialogTabPanel tab = tabs.get(layer);\r
424                 if (tab == null) {\r
425                         throw new IllegalArgumentException("layer not found. " + layer);\r
426                 }\r
427                 return tab.getColorGroup();\r
428         }\r
429         \r
430         /**\r
431          * 指定したレイヤーのカラーグループを設定する.<br>\r
432          * カテゴリに属さないレイヤーを指定した場合は例外となる.<br>\r
433          * @param layer レイヤー\r
434          * @param colorGroup カラーグループ\r
435          */\r
436         public void setColorGroup(Layer layer, ColorGroup colorGroup) {\r
437                 if (layer == null || colorGroup == null) {\r
438                         throw new IllegalArgumentException();\r
439                 }\r
440                 ColorDialogTabPanel tab = tabs.get(layer);\r
441                 if (tab != null) {\r
442                         tab.setColorGroup(colorGroup);\r
443                 }\r
444         }\r
445         \r
446         /**\r
447          * 色ダイアログが変更された場合に通知を受けるリスナーを登録する.<br>\r
448          * @param listener リスナー\r
449          */\r
450         public void addColorChangeListener(ColorChangeListener listener) {\r
451                 if (listener == null) {\r
452                         throw new IllegalArgumentException();\r
453                 }\r
454                 listeners.add(listener);\r
455         }\r
456 \r
457         /**\r
458          * 色ダイアログが変更された場合に通知を受けるリスナーを登録解除する.<br>\r
459          * @param listener\r
460          */\r
461         public void removeColorChangeListener(ColorChangeListener listener) {\r
462                 listeners.remove(listener);\r
463         }\r
464 \r
465         /**\r
466          * 全レイヤーに対するカラー変更イベントを明示的に送信する.\r
467          */\r
468         protected void apply() {\r
469                 for (Layer layer : getPartsCategory().getLayers()) {\r
470                         ColorDialog.this.fireColorChangeEvent(layer, true);\r
471                 }\r
472         }\r
473         \r
474         /**\r
475          * カラーをリセットする.\r
476          */\r
477         protected void resetColor(ColorDialogTabPanel tab) {\r
478                 tab.resetColor();\r
479         }\r
480 \r
481         /**\r
482          * 指定したレイヤーに対するカラー変更イベントを通知する.<br>\r
483          * ただし、force引数がfalseである場合、アプリケーション設定で即時プレビューが指定されていない場合は通知しない.<br>\r
484          * @param layer レイヤー\r
485          * @param force アプリケーション設定に関わらず送信する場合はtrue\r
486          */\r
487         protected void fireColorChangeEvent(Layer layer, boolean force) {\r
488                 if (layer == null) {\r
489                         throw new IllegalArgumentException();\r
490                 }\r
491                 if (!force) {\r
492                         AppConfig appConfig = AppConfig.getInstance();\r
493                         if (!appConfig.isEnableAutoColorChange()) {\r
494                                 return;\r
495                         }\r
496                 }\r
497                 ColorChangeEvent event = new ColorChangeEvent(this, layer);\r
498                 for (ColorChangeListener listener : listeners) {\r
499                         listener.onColorChange(event);\r
500                 }\r
501         }\r
502 \r
503         /**\r
504          * 色グループが変更されたことを通知する.<br>\r
505          * @param layer レイヤー\r
506          */\r
507         protected void fireColorGroupChangeEvent(Layer layer) {\r
508                 if (layer == null) {\r
509                         throw new IllegalArgumentException();\r
510                 }\r
511                 ColorChangeEvent event = new ColorChangeEvent(this, layer);\r
512                 for (ColorChangeListener listener : listeners) {\r
513                         listener.onColorGroupChange(event);\r
514                 }\r
515         }\r
516         \r
517         @Override\r
518         public String toString() {\r
519                 return "ColorDialog(partsCategory:" + partsCategory + ")";\r
520         }\r
521 }\r
522 \r
523 \r
524 class ColorDialogTabPanel extends JPanel {\r
525         private static final long serialVersionUID = 1L;\r
526 \r
527         private JComboBox cmbColorReplace;\r
528         \r
529         private JSpinner spinGray;\r
530         \r
531         private JSpinner spinOffsetR;\r
532         \r
533         private JSpinner spinOffsetG;\r
534         \r
535         private JSpinner spinOffsetB;\r
536         \r
537         private JSpinner spinOffsetA;\r
538 \r
539         private JSpinner spinFactorR;\r
540         \r
541         private JSpinner spinFactorG;\r
542         \r
543         private JSpinner spinFactorB;\r
544         \r
545         private JSpinner spinFactorA;\r
546         \r
547         private JSpinner spinHue;\r
548         \r
549         private JSpinner spinSaturation;\r
550         \r
551         private JSpinner spinBrightness;\r
552 \r
553         private JSpinner spinGammaR;\r
554 \r
555         private JSpinner spinGammaG;\r
556         \r
557         private JSpinner spinGammaB;\r
558         \r
559         private JSpinner spinGammaA;\r
560         \r
561         private JComboBox cmbColorGroup;\r
562         \r
563         private JCheckBox chkColorGroupSync;\r
564 \r
565         private final ColorDialog parent;\r
566         \r
567         /**\r
568          * パラメータの明示的変更時に他のパラメータへの反映イベントを停止させるためのセマフォ\r
569          */\r
570         private AtomicInteger changeEventDisableSemaphore = new AtomicInteger();\r
571         \r
572         /**\r
573          * 明示的に設定されたカラーパラメータを保存する.(リセットに使用するため)\r
574          */\r
575         private ColorConvertParameter paramOrg = new ColorConvertParameter();\r
576         \r
577         private ColorConvertParameter chachedParam;\r
578         \r
579         private LinkedList<ColorChangeListener> listeners = new LinkedList<ColorChangeListener>();\r
580         \r
581         public void addColorChangeListener(ColorChangeListener listener) {\r
582                 if (listener == null) {\r
583                         throw new IllegalArgumentException();\r
584                 }\r
585                 listeners.add(listener);\r
586         }\r
587         \r
588         public void removeColorChangeListener(ColorChangeListener listener) {\r
589                 listeners.remove(listener);\r
590         }\r
591         \r
592         protected void fireColorChangeEvent(Layer layer) {\r
593                 if (layer == null) {\r
594                         throw new IllegalArgumentException();\r
595                 }\r
596                 chachedParam = null;\r
597                 if (changeEventDisableSemaphore.get() <= 0) {\r
598                         ColorChangeEvent event = new ColorChangeEvent(parent, layer);\r
599                         for (ColorChangeListener listener : listeners) {\r
600                                 listener.onColorChange(event);\r
601                         }\r
602                 }\r
603         }\r
604         \r
605         protected void fireColorGroupChangeEvent(Layer layer) {\r
606                 if (layer == null) {\r
607                         throw new IllegalArgumentException();\r
608                 }\r
609                 chachedParam = null;\r
610                 if (changeEventDisableSemaphore.get() <= 0) {\r
611                         ColorChangeEvent event = new ColorChangeEvent(parent, layer);\r
612                         for (ColorChangeListener listener : listeners) {\r
613                                 listener.onColorGroupChange(event);\r
614                         }\r
615                 }\r
616         }\r
617 \r
618         public ColorDialogTabPanel(final ColorDialog parent, final Layer layer, Collection<ColorGroup> colorGroups) {\r
619                 if (parent == null || layer == null || colorGroups == null) {\r
620                         throw new IllegalArgumentException();\r
621                 }\r
622                 this.parent = parent;\r
623 \r
624                 final Properties strings = LocalizedResourcePropertyLoader.getInstance().getLocalizedProperties("strings/colordialog");\r
625 \r
626                 setLayout(new BorderLayout());\r
627                 JPanel container = new JPanel();\r
628                 BoxLayout boxlayout = new BoxLayout(container, BoxLayout.PAGE_AXIS);\r
629                 container.setLayout(boxlayout);\r
630                 add(container, BorderLayout.NORTH);\r
631                 \r
632                 // 変更イベントハンドラ\r
633                 final ChangeListener changeEventHandler = new ChangeListener() {\r
634                         public void stateChanged(ChangeEvent e) {\r
635                                 fireColorChangeEvent(layer);\r
636                         }\r
637                 };\r
638                 \r
639                 // 色置換パネル\r
640                 \r
641                 JPanel colorReplacePanel = new JPanel();\r
642                 colorReplacePanel.setBorder(BorderFactory.createCompoundBorder(\r
643                                 BorderFactory.createEmptyBorder(3, 3, 3, 3),\r
644                                 BorderFactory.createTitledBorder(strings.getProperty("group.replacergb.caption"))));\r
645                 GridBagLayout gbl = new GridBagLayout();\r
646                 colorReplacePanel.setLayout(gbl);\r
647                 GridBagConstraints gbc = new GridBagConstraints();\r
648                 \r
649                 JLabel lblColorReplace = new JLabel(strings.getProperty("replacergb"), JLabel.RIGHT);\r
650                 cmbColorReplace = new JComboBox(ColorConv.values());\r
651                 JLabel lblGray = new JLabel(strings.getProperty("bright"), JLabel.RIGHT);\r
652                 SpinnerNumberModel grayModel = new SpinnerNumberModel(1., 0., 1., 0.05);\r
653                 grayModel.addChangeListener(changeEventHandler);\r
654                 cmbColorReplace.addActionListener(new ActionListener() {\r
655                         public void actionPerformed(ActionEvent e) {\r
656                                 fireColorChangeEvent(layer);\r
657                         }\r
658                 });\r
659                 spinGray = new JSpinner(grayModel);\r
660                 \r
661                 gbc.gridx = 0;\r
662                 gbc.gridy = 0;\r
663                 gbc.gridwidth = 1;\r
664                 gbc.gridheight = 1;\r
665                 gbc.weightx = 0.;\r
666                 gbc.weighty = 0.;\r
667                 gbc.anchor = GridBagConstraints.WEST;\r
668                 gbc.fill = GridBagConstraints.BOTH;\r
669                 gbc.ipadx = 0;\r
670                 gbc.ipady = 0;\r
671                 gbc.insets = new Insets(3, 3, 3, 3);\r
672                 colorReplacePanel.add(lblColorReplace, gbc);\r
673                 \r
674                 gbc.gridx = 1;\r
675                 gbc.gridy = 0;\r
676                 gbc.gridwidth = 1;\r
677                 gbc.gridheight = 1;\r
678                 gbc.weightx = 1.;\r
679                 gbc.weighty = 0.;\r
680                 colorReplacePanel.add(cmbColorReplace, gbc);\r
681                 \r
682                 gbc.gridx = 2;\r
683                 gbc.gridy = 0;\r
684                 gbc.gridwidth = 1;\r
685                 gbc.gridheight = 1;\r
686                 gbc.weightx = 0.;\r
687                 gbc.weighty = 0.;\r
688                 colorReplacePanel.add(lblGray, gbc);\r
689 \r
690                 gbc.gridx = 3;\r
691                 gbc.gridy = 0;\r
692                 gbc.weightx = 1.;\r
693                 gbc.weighty = 0.;\r
694                 gbc.gridwidth = 1;\r
695                 gbc.gridheight = 1;\r
696                 colorReplacePanel.add(spinGray, gbc);\r
697 \r
698                 // RGB変更パネル\r
699                 \r
700                 JPanel colorLevelPanel = new JPanel();\r
701                 colorLevelPanel.setBorder(BorderFactory.createCompoundBorder(\r
702                                 BorderFactory.createEmptyBorder(3, 3, 3, 3),\r
703                                 BorderFactory.createTitledBorder(strings.getProperty("group.rgb.caption"))));\r
704                 GridLayout gl = new GridLayout(4, 5);\r
705                 gl.setHgap(2);\r
706                 gl.setVgap(2);\r
707                 colorLevelPanel.setLayout(gl);\r
708                 colorLevelPanel.add(Box.createGlue());\r
709                 colorLevelPanel.add(new JLabel(strings.getProperty("red"), JLabel.CENTER));\r
710                 colorLevelPanel.add(new JLabel(strings.getProperty("green"), JLabel.CENTER));\r
711                 colorLevelPanel.add(new JLabel(strings.getProperty("blue"), JLabel.CENTER));\r
712                 colorLevelPanel.add(new JLabel(strings.getProperty("alpha"), JLabel.CENTER));\r
713                 colorLevelPanel.add(new JLabel(strings.getProperty("offset"), JLabel.RIGHT));\r
714                 SpinnerNumberModel offsetModelR = new SpinnerNumberModel(0, -255, 255, 1);\r
715                 SpinnerNumberModel offsetModelG = new SpinnerNumberModel(0, -255, 255, 1);\r
716                 SpinnerNumberModel offsetModelB = new SpinnerNumberModel(0, -255, 255, 1);\r
717                 SpinnerNumberModel offsetModelA = new SpinnerNumberModel(0, -255, 255, 1);\r
718                 offsetModelR.addChangeListener(changeEventHandler);\r
719                 offsetModelG.addChangeListener(changeEventHandler);\r
720                 offsetModelB.addChangeListener(changeEventHandler);\r
721                 offsetModelA.addChangeListener(changeEventHandler);\r
722                 spinOffsetR = new JSpinner(offsetModelR);\r
723                 spinOffsetG = new JSpinner(offsetModelG);\r
724                 spinOffsetB = new JSpinner(offsetModelB);\r
725                 spinOffsetA = new JSpinner(offsetModelA);\r
726                 colorLevelPanel.add(spinOffsetR);\r
727                 colorLevelPanel.add(spinOffsetG);\r
728                 colorLevelPanel.add(spinOffsetB);\r
729                 colorLevelPanel.add(spinOffsetA);\r
730                 colorLevelPanel.add(new JLabel(strings.getProperty("factor"), JLabel.RIGHT));\r
731                 SpinnerNumberModel factorModelR = new SpinnerNumberModel(1., 0.01, 99, 0.01);\r
732                 SpinnerNumberModel factorModelG = new SpinnerNumberModel(1., 0.01, 99, 0.01);\r
733                 SpinnerNumberModel factorModelB = new SpinnerNumberModel(1., 0.01, 99, 0.01);\r
734                 SpinnerNumberModel factorModelA = new SpinnerNumberModel(1., 0.01, 99, 0.01);\r
735                 factorModelR.addChangeListener(changeEventHandler);\r
736                 factorModelG.addChangeListener(changeEventHandler);\r
737                 factorModelB.addChangeListener(changeEventHandler);\r
738                 factorModelA.addChangeListener(changeEventHandler);\r
739                 spinFactorR = new JSpinner(factorModelR);\r
740                 spinFactorG = new JSpinner(factorModelG);\r
741                 spinFactorB = new JSpinner(factorModelB);\r
742                 spinFactorA = new JSpinner(factorModelA);\r
743                 colorLevelPanel.add(spinFactorR);\r
744                 colorLevelPanel.add(spinFactorG);\r
745                 colorLevelPanel.add(spinFactorB);\r
746                 colorLevelPanel.add(spinFactorA);\r
747                 colorLevelPanel.add(new JLabel(strings.getProperty("gamma"), JLabel.RIGHT));\r
748                 SpinnerNumberModel gammaModelR = new SpinnerNumberModel(1., 0.01, 99, 0.01);\r
749                 SpinnerNumberModel gammaModelG = new SpinnerNumberModel(1., 0.01, 99, 0.01);\r
750                 SpinnerNumberModel gammaModelB = new SpinnerNumberModel(1., 0.01, 99, 0.01);\r
751                 SpinnerNumberModel gammaModelA = new SpinnerNumberModel(1., 0.01, 99, 0.01);\r
752                 gammaModelR.addChangeListener(changeEventHandler);\r
753                 gammaModelG.addChangeListener(changeEventHandler);\r
754                 gammaModelB.addChangeListener(changeEventHandler);\r
755                 gammaModelA.addChangeListener(changeEventHandler);\r
756                 spinGammaR = new JSpinner(gammaModelR);\r
757                 spinGammaG = new JSpinner(gammaModelG);\r
758                 spinGammaB = new JSpinner(gammaModelB);\r
759                 spinGammaA = new JSpinner(gammaModelA);\r
760                 colorLevelPanel.add(spinGammaR);\r
761                 colorLevelPanel.add(spinGammaG);\r
762                 colorLevelPanel.add(spinGammaB);\r
763                 colorLevelPanel.add(spinGammaA);\r
764                 \r
765                 // 色調パネル\r
766                 \r
767                 JPanel colorTunePanel = new JPanel();\r
768                 colorTunePanel.setBorder(BorderFactory.createCompoundBorder(\r
769                                 BorderFactory.createEmptyBorder(3, 3, 3, 3),\r
770                                 BorderFactory.createTitledBorder(strings.getProperty("group.hsb.caption"))));\r
771                 GridLayout gl2 = new GridLayout(3, 3);\r
772                 gl2.setHgap(3);\r
773                 gl2.setVgap(3);\r
774                 colorTunePanel.setLayout(gl2);\r
775                 colorTunePanel.add(new JLabel(strings.getProperty("hue"), JLabel.CENTER)); // Hue 色相\r
776                 colorTunePanel.add(new JLabel(strings.getProperty("saturation"), JLabel.CENTER)); // Saturation 彩度\r
777                 colorTunePanel.add(new JLabel(strings.getProperty("brightness"), JLabel.CENTER)); // Brightness 明度\r
778 \r
779                 SpinnerNumberModel hsbModelH = new SpinnerNumberModel(0., -1., 1., 0.01);\r
780                 SpinnerNumberModel hsbModelS = new SpinnerNumberModel(0., -1., 1., 0.01);\r
781                 SpinnerNumberModel hsbModelB = new SpinnerNumberModel(0., -1., 1., 0.01);\r
782                 \r
783                 hsbModelH.addChangeListener(changeEventHandler);\r
784                 hsbModelS.addChangeListener(changeEventHandler);\r
785                 hsbModelB.addChangeListener(changeEventHandler);\r
786                 \r
787                 spinHue = new JSpinner(hsbModelH);\r
788                 spinSaturation = new JSpinner(hsbModelS);\r
789                 spinBrightness = new JSpinner(hsbModelB);\r
790                 \r
791                 colorTunePanel.add(spinHue);\r
792                 colorTunePanel.add(spinSaturation);\r
793                 colorTunePanel.add(spinBrightness);\r
794                 \r
795                 JSlider sliderHue = new JSlider();\r
796                 JSlider sliderSaturation = new JSlider();\r
797                 JSlider sliderBrightness = new JSlider();\r
798                 sliderHue.setPreferredSize(spinHue.getPreferredSize());\r
799                 sliderSaturation.setPreferredSize(spinSaturation.getPreferredSize());\r
800                 sliderBrightness.setPreferredSize(spinBrightness.getPreferredSize());\r
801                 colorTunePanel.add(sliderHue);\r
802                 colorTunePanel.add(sliderSaturation);\r
803                 colorTunePanel.add(sliderBrightness);\r
804 \r
805                 JSlider sliders[] = new JSlider[] {sliderHue, sliderSaturation, sliderBrightness};\r
806                 JSpinner spinners[] = new JSpinner[] {spinHue, spinSaturation, spinBrightness};\r
807                 \r
808                 for (int idx = 0; idx < spinners.length; idx++) {\r
809                         final JSlider sl = sliders[idx];\r
810                         final JSpinner sp = spinners[idx];\r
811                         sl.setMinimum(-100);\r
812                         sl.setMaximum(100);\r
813                         sl.setValue((int)(((Number) sp.getValue()).doubleValue() * 100.));\r
814                         final Semaphore loopBlocker = new Semaphore(1); // イベントが循環発生することを防ぐ\r
815                         sl.addChangeListener(new ChangeListener() {\r
816                                 public void stateChanged(ChangeEvent e) {\r
817                                         if (loopBlocker.tryAcquire()) {\r
818                                                 try {\r
819                                                         double rate = sl.getValue() / 100.;\r
820                                                         sp.setValue(Double.valueOf(rate));\r
821                                                 } finally {\r
822                                                         loopBlocker.release();\r
823                                                 }\r
824                                         }\r
825                                 }\r
826                         });\r
827                         sp.addChangeListener(new ChangeListener() {\r
828                                 public void stateChanged(ChangeEvent e) {\r
829                                         if (loopBlocker.tryAcquire()) {\r
830                                                 try {\r
831                                                         int rate = (int)(((Number) sp.getValue()).doubleValue() * 100.);\r
832                                                         sl.setValue(rate);\r
833                                                 } finally {\r
834                                                         loopBlocker.release();\r
835                                                 }\r
836                                         }\r
837                                 }\r
838                         });\r
839                 }\r
840                 \r
841                 // カラーグループ\r
842                 ColorGroup colorGroup = layer.getColorGroup();\r
843                 JPanel colorGroupPanel = new JPanel();\r
844                 colorGroupPanel.setBorder(BorderFactory.createCompoundBorder(\r
845                                 BorderFactory.createEmptyBorder(3, 3, 3, 3),\r
846                                 BorderFactory.createTitledBorder(strings.getProperty("colorgroup"))));\r
847                 GridBagLayout gbl2 = new GridBagLayout();\r
848                 colorGroupPanel.setLayout(gbl2);\r
849                 GridBagConstraints gbc2 = new GridBagConstraints();\r
850 \r
851                 JLabel lblColorGroup = new JLabel(strings.getProperty("group"), JLabel.RIGHT);\r
852                 cmbColorGroup = new JComboBox(colorGroups.toArray(new ColorGroup[colorGroups.size()]));\r
853                 cmbColorGroup.setSelectedItem(colorGroup);\r
854                 cmbColorGroup.addActionListener(new ActionListener() {\r
855                         public void actionPerformed(ActionEvent e) {\r
856                                 ColorGroup selColorGroup = (ColorGroup) cmbColorGroup.getSelectedItem();\r
857                                 chkColorGroupSync.setSelected(selColorGroup.isEnabled());\r
858                                 fireColorGroupChangeEvent(layer);\r
859                         }\r
860                 });\r
861                 chkColorGroupSync = new JCheckBox(strings.getProperty("synchronized"));\r
862                 chkColorGroupSync.setSelected(layer.isInitSync());\r
863 \r
864                 gbc2.gridx = 0;\r
865                 gbc2.gridy = 0;\r
866                 gbc2.gridwidth = 1;\r
867                 gbc2.gridheight = 1;\r
868                 gbc2.weightx = 0.;\r
869                 gbc2.weighty = 0.;\r
870                 gbc2.anchor = GridBagConstraints.WEST;\r
871                 gbc2.fill = GridBagConstraints.BOTH;\r
872                 gbc2.ipadx = 0;\r
873                 gbc2.ipady = 0;\r
874                 gbc2.insets = new Insets(3, 3, 3, 3);\r
875                 colorGroupPanel.add(lblColorGroup, gbc2);\r
876                 \r
877                 gbc2.gridx = 1;\r
878                 gbc2.gridy = 0;\r
879                 gbc2.gridwidth = 1;\r
880                 gbc2.gridheight = 1;\r
881                 gbc2.weightx = 1.;\r
882                 gbc2.weighty = 0.;\r
883                 colorGroupPanel.add(cmbColorGroup, gbc2);\r
884                 \r
885                 gbc2.gridx = 2;\r
886                 gbc2.gridy = 0;\r
887                 gbc2.gridwidth = GridBagConstraints.REMAINDER;\r
888                 gbc2.gridheight = 1;\r
889                 gbc2.weightx = 0.;\r
890                 gbc2.weighty = 0.;\r
891                 colorGroupPanel.add(chkColorGroupSync, gbc2);\r
892                 \r
893                 if (colorGroupPanel != null) {\r
894                         container.add(colorGroupPanel);\r
895                 }\r
896                 container.add(colorLevelPanel);\r
897                 container.add(colorReplacePanel);\r
898                 container.add(colorTunePanel);\r
899         }\r
900         \r
901         public void resetColor() {\r
902                 setColorConvertParameter(paramOrg);\r
903         }\r
904         \r
905         public void setColorConvertParameter(ColorConvertParameter param) {\r
906                 if (param == null) {\r
907                         throw new IllegalArgumentException();\r
908                 }\r
909                 paramOrg = param.clone();\r
910                 ColorConv colorReplace = param.getColorReplace();\r
911                 if (colorReplace == null) {\r
912                         colorReplace = ColorConv.NONE;\r
913                 }\r
914                 changeEventDisableSemaphore.incrementAndGet();\r
915                 try {\r
916                         cmbColorReplace.setSelectedItem(colorReplace);\r
917                         spinGray.setValue(Double.valueOf(param.getGrayLevel()));\r
918                         spinOffsetR.setValue(Integer.valueOf(param.getOffsetR()));\r
919                         spinOffsetG.setValue(Integer.valueOf(param.getOffsetG()));\r
920                         spinOffsetB.setValue(Integer.valueOf(param.getOffsetB()));\r
921                         spinOffsetA.setValue(Integer.valueOf(param.getOffsetA()));\r
922                         spinFactorR.setValue(Double.valueOf(param.getFactorR()));\r
923                         spinFactorG.setValue(Double.valueOf(param.getFactorG()));\r
924                         spinFactorB.setValue(Double.valueOf(param.getFactorB()));\r
925                         spinFactorA.setValue(Double.valueOf(param.getFactorA()));\r
926                         spinGammaR.setValue(Double.valueOf(param.getGammaR()));\r
927                         spinGammaG.setValue(Double.valueOf(param.getGammaG()));\r
928                         spinGammaB.setValue(Double.valueOf(param.getGammaB()));\r
929                         spinGammaA.setValue(Double.valueOf(param.getGammaA()));\r
930                         spinHue.setValue(Double.valueOf(param.getHue()));\r
931                         spinSaturation.setValue(Double.valueOf(param.getSaturation()));\r
932                         spinBrightness.setValue(Double.valueOf(param.getBrightness()));\r
933                 } finally {\r
934                         changeEventDisableSemaphore.decrementAndGet();\r
935                 }\r
936                 chachedParam = param;\r
937         }\r
938         \r
939         public ColorConvertParameter getColorConvertParameter() {\r
940                 if (chachedParam != null) {\r
941                         return chachedParam;\r
942                 }\r
943                 ColorConvertParameter param = new ColorConvertParameter();\r
944                 param.setColorReplace((ColorConv) cmbColorReplace.getSelectedItem());\r
945                 param.setGrayLevel(((Number) spinGray.getValue()).floatValue());\r
946                 param.setOffsetR(((Number) spinOffsetR.getValue()).intValue());\r
947                 param.setOffsetG(((Number) spinOffsetG.getValue()).intValue());\r
948                 param.setOffsetB(((Number) spinOffsetB.getValue()).intValue());\r
949                 param.setOffsetA(((Number) spinOffsetA.getValue()).intValue());\r
950                 param.setFactorR(((Number) spinFactorR.getValue()).floatValue());\r
951                 param.setFactorG(((Number) spinFactorG.getValue()).floatValue());\r
952                 param.setFactorB(((Number) spinFactorB.getValue()).floatValue());\r
953                 param.setFactorA(((Number) spinFactorA.getValue()).floatValue());\r
954                 param.setGammaR(((Number) spinGammaR.getValue()).floatValue());\r
955                 param.setGammaG(((Number) spinGammaG.getValue()).floatValue());\r
956                 param.setGammaB(((Number) spinGammaB.getValue()).floatValue());\r
957                 param.setGammaA(((Number) spinGammaA.getValue()).floatValue());\r
958                 param.setHue(((Number) spinHue.getValue()).floatValue());\r
959                 param.setSaturation(((Number) spinSaturation.getValue()).floatValue());\r
960                 param.setBrightness(((Number) spinBrightness.getValue()).floatValue());\r
961                 chachedParam = param;\r
962                 return param;\r
963         }\r
964         \r
965         public ColorGroup getColorGroup() {\r
966                 return (ColorGroup) cmbColorGroup.getSelectedItem();\r
967         }\r
968         \r
969         public void setColorGroup(ColorGroup colorGroup) {\r
970                 if (colorGroup == null) {\r
971                         colorGroup = ColorGroup.NA;\r
972                 }\r
973                 changeEventDisableSemaphore.incrementAndGet();\r
974                 try {\r
975                         cmbColorGroup.setSelectedItem(colorGroup);\r
976                 } finally {\r
977                         changeEventDisableSemaphore.decrementAndGet();\r
978                 }\r
979         }\r
980         \r
981         public boolean isSyncColorGroup() {\r
982                 return chkColorGroupSync == null ? false : chkColorGroupSync.isSelected();\r
983         }\r
984         \r
985         public void setSyncColorGroup(boolean selected) {\r
986                 if (chkColorGroupSync != null) {\r
987                         changeEventDisableSemaphore.incrementAndGet();\r
988                         try {\r
989                                 chkColorGroupSync.setSelected(selected);\r
990                         } finally {\r
991                                 changeEventDisableSemaphore.decrementAndGet();\r
992                         }\r
993                 }\r
994         }\r
995 }\r