1 package charactermanaj.ui;
\r
3 import static java.lang.Math.max;
\r
4 import static java.lang.Math.min;
\r
6 import java.awt.BorderLayout;
\r
7 import java.awt.Color;
\r
8 import java.awt.Component;
\r
9 import java.awt.Container;
\r
10 import java.awt.Cursor;
\r
11 import java.awt.Dimension;
\r
12 import java.awt.Font;
\r
13 import java.awt.Frame;
\r
14 import java.awt.GraphicsEnvironment;
\r
15 import java.awt.Point;
\r
16 import java.awt.Rectangle;
\r
17 import java.awt.Toolkit;
\r
18 import java.awt.dnd.DropTarget;
\r
19 import java.awt.event.ActionEvent;
\r
20 import java.awt.event.ActionListener;
\r
21 import java.awt.event.MouseWheelEvent;
\r
22 import java.awt.event.MouseWheelListener;
\r
23 import java.awt.event.WindowAdapter;
\r
24 import java.awt.event.WindowEvent;
\r
25 import java.awt.image.BufferedImage;
\r
26 import java.io.File;
\r
27 import java.io.IOException;
\r
28 import java.lang.reflect.InvocationTargetException;
\r
29 import java.net.URI;
\r
30 import java.util.ArrayList;
\r
31 import java.util.Collections;
\r
32 import java.util.List;
\r
33 import java.util.Map;
\r
34 import java.util.Properties;
\r
35 import java.util.TreeMap;
\r
36 import java.util.UUID;
\r
37 import java.util.logging.Level;
\r
38 import java.util.logging.Logger;
\r
40 import javax.swing.Box;
\r
41 import javax.swing.BoxLayout;
\r
42 import javax.swing.JCheckBox;
\r
43 import javax.swing.JColorChooser;
\r
44 import javax.swing.JFrame;
\r
45 import javax.swing.JMenu;
\r
46 import javax.swing.JMenuBar;
\r
47 import javax.swing.JMenuItem;
\r
48 import javax.swing.JOptionPane;
\r
49 import javax.swing.JPanel;
\r
50 import javax.swing.JPopupMenu;
\r
51 import javax.swing.JScrollBar;
\r
52 import javax.swing.JScrollPane;
\r
53 import javax.swing.JSeparator;
\r
54 import javax.swing.JSplitPane;
\r
55 import javax.swing.JViewport;
\r
56 import javax.swing.SwingUtilities;
\r
57 import javax.swing.event.AncestorEvent;
\r
58 import javax.swing.event.AncestorListener;
\r
59 import javax.swing.event.MenuEvent;
\r
60 import javax.swing.event.MenuListener;
\r
62 import charactermanaj.Main;
\r
63 import charactermanaj.clipboardSupport.ClipboardUtil;
\r
64 import charactermanaj.graphics.AsyncImageBuilder;
\r
65 import charactermanaj.graphics.ColorConvertedImageCachedLoader;
\r
66 import charactermanaj.graphics.ImageBuildJobAbstractAdaptor;
\r
67 import charactermanaj.graphics.ImageBuilder.ImageOutput;
\r
68 import charactermanaj.graphics.io.ImageSaveHelper;
\r
69 import charactermanaj.graphics.io.OutputOption;
\r
70 import charactermanaj.graphics.io.UkagakaImageSaveHelper;
\r
71 import charactermanaj.model.AppConfig;
\r
72 import charactermanaj.model.CharacterData;
\r
73 import charactermanaj.model.ColorGroup;
\r
74 import charactermanaj.model.IndependentPartsSetInfo;
\r
75 import charactermanaj.model.PartsCategory;
\r
76 import charactermanaj.model.PartsColorInfo;
\r
77 import charactermanaj.model.PartsColorManager;
\r
78 import charactermanaj.model.PartsIdentifier;
\r
79 import charactermanaj.model.PartsSet;
\r
80 import charactermanaj.model.RecommendationURL;
\r
81 import charactermanaj.model.WorkingSet;
\r
82 import charactermanaj.model.WorkingSet2;
\r
83 import charactermanaj.model.io.CharacterDataPersistent;
\r
84 import charactermanaj.model.io.PartsImageDirectoryWatchAgent;
\r
85 import charactermanaj.model.io.PartsImageDirectoryWatchAgentFactory;
\r
86 import charactermanaj.model.io.PartsImageDirectoryWatchEvent;
\r
87 import charactermanaj.model.io.PartsImageDirectoryWatchListener;
\r
88 import charactermanaj.model.io.RecentDataPersistent;
\r
89 import charactermanaj.model.io.WorkingSetPersist;
\r
90 import charactermanaj.ui.ImageSelectPanel.ImageSelectPanelEvent;
\r
91 import charactermanaj.ui.ImageSelectPanel.ImageSelectPanelListener;
\r
92 import charactermanaj.ui.ManageFavoriteDialog.FavoriteManageCallback;
\r
93 import charactermanaj.ui.PreviewPanel.PreviewPanelEvent;
\r
94 import charactermanaj.ui.PreviewPanel.PreviewPanelListener;
\r
95 import charactermanaj.ui.model.ColorChangeEvent;
\r
96 import charactermanaj.ui.model.ColorChangeListener;
\r
97 import charactermanaj.ui.model.ColorGroupCoordinator;
\r
98 import charactermanaj.ui.model.FavoritesChangeEvent;
\r
99 import charactermanaj.ui.model.FavoritesChangeListener;
\r
100 import charactermanaj.ui.model.FavoritesChangeObserver;
\r
101 import charactermanaj.ui.model.PartsColorCoordinator;
\r
102 import charactermanaj.ui.model.PartsSelectionManager;
\r
103 import charactermanaj.ui.model.WallpaperFactory;
\r
104 import charactermanaj.ui.model.WallpaperFactoryErrorRecoverHandler;
\r
105 import charactermanaj.ui.model.WallpaperFactoryException;
\r
106 import charactermanaj.ui.model.WallpaperInfo;
\r
107 import charactermanaj.ui.scrollablemenu.JScrollableMenu;
\r
108 import charactermanaj.ui.util.FileDropTarget;
\r
109 import charactermanaj.ui.util.WindowAdjustLocationSupport;
\r
110 import charactermanaj.util.DesktopUtilities;
\r
111 import charactermanaj.util.ErrorMessageHelper;
\r
112 import charactermanaj.util.LocalizedResourcePropertyLoader;
\r
113 import charactermanaj.util.SystemUtil;
\r
114 import charactermanaj.util.UIHelper;
\r
119 * アプリケーションがアクティブである場合は最低でも1つのメインフレームが表示されている.<br>
\r
123 public class MainFrame extends JFrame implements FavoritesChangeListener {
\r
125 private static final long serialVersionUID = 1L;
\r
127 private static final Logger logger = Logger.getLogger(MainFrame.class.getName());
\r
130 protected static final String STRINGS_RESOURCE = "languages/mainframe";
\r
132 protected static final String MENU_STRINGS_RESOURCE = "menu/menu";
\r
135 * メインフレームのアイコン.<br>
\r
137 protected BufferedImage icon;
\r
141 * 現在アクティブなメインフレーム.<br>
\r
142 * フォーカスが切り替わるたびにアクティブフレームを追跡する.<br>
\r
143 * Mac OS XのAbout/Preferences/Quitのシステムメニューからよびだされた場合に
\r
144 * オーナーたるべきメインフレームを識別するためのもの.<br>
\r
146 private static volatile MainFrame activedMainFrame;
\r
150 * このメインフレームが対象とするキャラクターデータ.<br>
\r
152 protected CharacterData characterData;
\r
158 private PreviewPanel previewPane;
\r
163 protected PartsSelectionManager partsSelectionManager;
\r
168 private boolean minimizeMode;
\r
174 protected ImageSelectPanelList imageSelectPanels;
\r
177 * パーツ選択パネルを納めるスクロールペイン
\r
179 protected JScrollPane imgSelectPanelsPanelSp;
\r
184 protected ColorGroupCoordinator colorGroupCoordinator;
\r
189 protected PartsColorCoordinator partsColorCoordinator;
\r
193 * キャッシュつきのイメージローダ.<br>
\r
195 private ColorConvertedImageCachedLoader imageLoader;
\r
198 * パーツを組み立てて1つのプレビュー可能なイメージを構築するためのビルダ
\r
200 private AsyncImageBuilder imageBuilder;
\r
204 * パーツイメージを画像として保存する場合のヘルパー.<br>
\r
205 * 最後に使ったディレクトリを保持するためのメンバ変数としている.<br>
\r
207 private ImageSaveHelper imageSaveHelper = new ImageSaveHelper();
\r
211 * 最後に使ったディレクトリ、ファイル名、モードなどを保持するためのメンバ変数としている.<br>
\r
213 private UkagakaImageSaveHelper ukagakaImageSaveHelper = new UkagakaImageSaveHelper();
\r
216 * パーツディレクトリを定期的にチェックし、パーツイメージが変更・追加・削除されている場合に パーツリストを更新するためのウォッチャー
\r
218 private PartsImageDirectoryWatchAgent watchAgent;
\r
223 private String defaultPartsSetTitle;
\r
226 * 最後に使用したプリセット.<br>
\r
227 * (一度もプリセットを使用していなければnull).
\r
229 private PartsSet lastUsePresetParts;
\r
232 * 最後に使用した検索ダイアログ.<br>
\r
233 * nullであれば一度も使用していない.<br>
\r
234 * (nullでなくとも閉じられている可能性がある.)<br>
\r
236 private SearchPartsDialog lastUseSearchPartsDialog;
\r
239 * 最後に使用したお気に入りダイアログ.<br>
\r
240 * nullであれば一度も使用していない.<br>
\r
241 * (nullでなくとも閉じられている可能性がある.)
\r
243 private ManageFavoriteDialog lastUseManageFavoritesDialog;
\r
248 private WallpaperInfo wallpaperInfo;
\r
252 * アクティブなメインフレームを設定する.
\r
257 public static void setActivedMainFrame(MainFrame mainFrame) {
\r
258 if (mainFrame == null) {
\r
259 throw new IllegalArgumentException();
\r
261 activedMainFrame = mainFrame;
\r
265 * 現在アクティブなメインフレームを取得する. まだメインフレームが開かれていない場合はnull.<br>
\r
266 * 最後のメインフレームが破棄中、もしくは破棄済みであれば破棄されたフレームを示すことに注意.<br>
\r
268 * @return メインフレーム、もしくはnull
\r
270 public static MainFrame getActivedMainFrame() {
\r
271 return activedMainFrame;
\r
275 * インポートされパーツが増減した可能性がある場合に呼び出される.
\r
283 * @throws IOException
\r
286 public static void notifyImportedPartsOrFavorites(
\r
287 final CharacterData cd,
\r
288 final CharacterData newCd,
\r
289 final Component caller
\r
290 ) throws IOException {
\r
291 if (cd == null || newCd == null || caller == null) {
\r
292 throw new IllegalArgumentException();
\r
295 if (!cd.isValid() || !newCd.isValid()) {
\r
296 // 変更前もしくは変更後が無効なキャラクターデータであれば
\r
300 logger.log(Level.FINE, "parts imported for active profiles: " + newCd);
\r
303 if ( !cd.isSameStructure(newCd)) {
\r
304 // キャラクターデータそのものが変更されている場合
\r
305 notifyChangeCharacterData(cd, newCd, caller);
\r
308 // パーツ構成は変更されていない場合
\r
310 // Frameのうち、ネイティブリソースと関連づけられているアクティブなフレームを調査
\r
311 for (Frame frame : JFrame.getFrames()) {
\r
312 if (frame.isDisplayable() && frame instanceof MainFrame) {
\r
313 final MainFrame mainFrame = (MainFrame) frame;
\r
314 if (mainFrame.characterData == null || !mainFrame.characterData.isValid()) {
\r
315 // 無効なキャラクターデータを保持している場合は、そのメインフレームは処理対象外
\r
318 SwingUtilities.invokeLater(new Runnable() {
\r
319 public void run() {
\r
320 // パーツ及びお気に入りを再取得する場合.
\r
322 Cursor oldCur = mainFrame.getCursor();
\r
323 mainFrame.setCursor(Cursor
\r
324 .getPredefinedCursor(Cursor.WAIT_CURSOR));
\r
326 mainFrame.reloadPartsAndFavorites(newCd, true);
\r
329 mainFrame.setCursor(oldCur != null ? oldCur : Cursor.getDefaultCursor());
\r
332 } catch (Exception ex) {
\r
333 ErrorMessageHelper.showErrorDialog(mainFrame, ex);
\r
343 * キャラクターデータが変更されたことを通知される.<br>
\r
350 * 呼び出しもとコンポーネント(ウェイトカーソル表示用)
\r
351 * @param structureCompatible
\r
353 * @throws IOException
\r
354 * 新しいキャラクターデータのパーツセットのロードに失敗した場合 (メインフレームは変更されていません.)
\r
356 public static void notifyChangeCharacterData(
\r
357 final CharacterData cd,
\r
358 final CharacterData newCd,
\r
359 final Component caller
\r
360 ) throws IOException {
\r
361 if (cd == null || newCd == null || caller == null) {
\r
362 throw new IllegalArgumentException();
\r
365 if (!cd.isValid() || !newCd.isValid()) {
\r
366 // 変更前もしくは変更後が無効なキャラクターデータであれば
\r
370 logger.log(Level.FINE, "change active profile: " + newCd);
\r
372 if (!ProfileListManager.isUsingCharacterData(cd)) {
\r
373 // 使用中のプロファイルではないので何もしない.
\r
377 caller.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
\r
379 // キャラクターデータが、まだ読み込まれていなければ読み込む.
\r
380 if (!newCd.isPartsLoaded()) {
\r
381 ProfileListManager.loadCharacterData(newCd);
\r
382 ProfileListManager.loadFavorites(newCd);
\r
385 // Frameのうち、ネイティブリソースと関連づけられているアクティブなフレームを調査
\r
386 for (Frame frame : JFrame.getFrames()) {
\r
387 if (frame.isDisplayable() && frame instanceof MainFrame) {
\r
388 final MainFrame mainFrame = (MainFrame) frame;
\r
389 if (mainFrame.characterData == null || !mainFrame.characterData.isValid()) {
\r
390 // 無効なキャラクターデータを保持している場合は、そのメインフレームは処理対象外
\r
394 // メインフレームが保持しているキャラクターデータのdocbaseと
\r
395 // 変更対象となったキャラクターデータのdocbaseが等しければ、メインフレームを更新する必要がある.
\r
396 String docbaseOrg = cd.getDocBase().toString();
\r
397 String docbaseMine = mainFrame.characterData.getDocBase().toString();
\r
398 if (docbaseOrg.equals(docbaseMine)) {
\r
399 SwingUtilities.invokeLater(new Runnable() {
\r
400 public void run() {
\r
402 Cursor oldCur = mainFrame.getCursor();
\r
403 mainFrame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
\r
406 mainFrame.saveWorkingSet();
\r
409 mainFrame.initComponent(newCd);
\r
412 mainFrame.setCursor(oldCur != null ? oldCur : Cursor.getDefaultCursor());
\r
415 } catch (RuntimeException ex) {
\r
416 ErrorMessageHelper.showErrorDialog(mainFrame, ex);
\r
425 caller.setCursor(Cursor.getDefaultCursor());
\r
430 * お気に入りデータが変更された場合に通知される.
\r
434 public void notifyChangeFavorites(FavoritesChangeEvent e) {
\r
435 CharacterData cd = e.getCharacterData();
\r
436 if (cd.getDocBase().equals(MainFrame.this.characterData.getDocBase())) {
\r
437 if (!MainFrame.this.equals(e.getSource())) {
\r
439 // (ただし、自分自身から送信したイベントの場合はリロードの必要はない)
\r
440 refreshFavorites();
\r
443 // お気に入り管理ダイアログ上のお気に入り一覧を最新に更新する.
\r
444 if (lastUseManageFavoritesDialog != null
\r
445 && lastUseManageFavoritesDialog.isDisplayable()) {
\r
446 lastUseManageFavoritesDialog.initListModel();
\r
454 * @param characterData
\r
457 public MainFrame(CharacterData characterData) {
\r
459 if (characterData == null) {
\r
460 throw new IllegalArgumentException();
\r
463 setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
\r
464 addWindowListener(new WindowAdapter() {
\r
466 public void windowClosing(WindowEvent e) {
\r
470 public void windowClosed(WindowEvent e) {
\r
474 public void windowActivated(WindowEvent e) {
\r
475 setActivedMainFrame(MainFrame.this);
\r
478 public void windowOpened(WindowEvent e) {
\r
484 icon = UIHelper.getInstance().getImage("icons/icon.png");
\r
485 setIconImage(icon);
\r
488 initComponent(characterData);
\r
489 JMenuBar menuBar = createMenuBar();
\r
490 setJMenuBar(menuBar);
\r
493 FavoritesChangeObserver.getDefault().addFavoritesChangeListener(
\r
496 // メインスクリーンサイズを取得する.
\r
497 GraphicsEnvironment genv = GraphicsEnvironment.getLocalGraphicsEnvironment();
\r
498 Rectangle desktopSize = genv.getMaximumWindowBounds(); // メインスクリーンのサイズ(デスクトップ領域のみ)
\r
499 logger.log(Level.CONFIG, "desktopSize=" + desktopSize);
\r
501 Dimension imageSize = characterData.getImageSize();
\r
502 // 画像サイズ300x400を基準サイズとして、それ以下にはならない.
\r
503 // アプリケーション設定の最大サイズ以上の場合はウィンドウサイズは固定してスクロールバーに任せる
\r
504 AppConfig appConfig = AppConfig.getInstance();
\r
505 int maxWidth = min(desktopSize.width, appConfig.getMainFrameMaxWidth());
\r
506 int maxHeight = min(desktopSize.height, appConfig.getMainFrameMaxHeight());
\r
507 int imageWidth = min(maxWidth, max(300, imageSize != null ? imageSize.width : 0));
\r
508 int imageHeight = min(maxHeight, max(400, imageSize != null ? imageSize.height : 0));
\r
509 // 300x400の画像の場合にメインフレームが600x550だとちょうどいい感じ.
\r
510 // それ以上大きい画像の場合は増えた分だけフレームを大きくしておく.
\r
511 setSize(imageWidth - 300 + 600, imageHeight - 400 + 550);
\r
513 // 次回表示時にプラットフォーム固有位置に表示するように予約
\r
514 setLocationByPlatform(true);
\r
516 } catch (RuntimeException ex) {
\r
517 logger.log(Level.SEVERE, "メインフレームの構築中に予期せぬ例外が発生しました。", ex);
\r
518 dispose(); // コンストラクタが呼ばれた時点でJFrameは構築済みなのでdisposeの必要がある.
\r
520 } catch (Error ex) {
\r
521 logger.log(Level.SEVERE, "メインフレームの構築中に致命的な例外が発生しました。", ex);
\r
522 dispose(); // コンストラクタが呼ばれた時点でJFrameは構築済みなのでdisposeの必要がある.
\r
528 * メインフレームを表示する.<br>
\r
529 * デスクトップ領域からはみ出した場合は位置を補正する.<br>
\r
531 public void showMainFrame() {
\r
532 // メインスクリーンサイズを取得する.
\r
533 GraphicsEnvironment genv = GraphicsEnvironment.getLocalGraphicsEnvironment();
\r
534 Rectangle desktopSize = genv.getMaximumWindowBounds(); // メインスクリーンのサイズ(デスクトップ領域のみ)
\r
535 logger.log(Level.CONFIG, "desktopSize=" + desktopSize);
\r
537 // プラットフォーム固有の位置あわせで表示する.
\r
538 // 表示した結果、はみ出している場合は0,0に補正する.
\r
540 Point loc = getLocation();
\r
541 logger.log(Level.CONFIG, "windowLocation=" + loc);
\r
542 Dimension windowSize = getSize();
\r
543 if (loc.y + windowSize.height >= desktopSize.height) {
\r
546 if (loc.x + windowSize.width >= desktopSize.width) {
\r
549 if (loc.x == 0 || loc.y == 0) {
\r
553 // デスクトップよりも大きい場合は小さくする.
\r
554 boolean resize = false;
\r
555 Dimension dim = getSize();
\r
556 if (dim.height > desktopSize.height) {
\r
557 dim.height = desktopSize.height;
\r
560 if (dim.width > desktopSize.width) {
\r
561 dim.width = desktopSize.width;
\r
570 * このメインフレームに関連づけられているエージェントスレッドを停止します.<br>
\r
571 * すでに停止している場合は何もしません。
\r
573 protected void stopAgents() {
\r
575 if (watchAgent != null) {
\r
577 watchAgent.disconnect();
\r
579 } catch (Throwable ex) {
\r
580 logger.log(Level.SEVERE, "フォルダ監視スレッドの停止に失敗しました。", ex);
\r
585 if (imageBuilder != null) {
\r
587 imageBuilder.stop();
\r
589 } catch (Throwable ex) {
\r
590 logger.log(Level.SEVERE, "非同期イメージビルダスレッドの停止に失敗しました。", ex);
\r
592 imageBuilder = null;
\r
597 * メインフレームを破棄します.<br>
\r
600 public void dispose() {
\r
601 FavoritesChangeObserver.getDefault()
\r
602 .removeFavoritesChangeListener(this);
\r
603 imageLoader.close();
\r
609 * 画面コンポーネントを設定します.<br>
\r
610 * すでに設定されている場合は一旦削除されたのちに再作成されます.<br>
\r
612 private void initComponent(CharacterData characterData) {
\r
614 CharacterData oldCd;
\r
615 synchronized (this) {
\r
616 oldCd = this.characterData;
\r
617 if (oldCd != null) {
\r
618 // 使用中のキャラクターデータであることを登録解除する。
\r
619 ProfileListManager.unregisterUsedCharacterData(oldCd);
\r
621 this.characterData = characterData;
\r
623 // 使用中のキャラクターデータであることを登録する.
\r
624 ProfileListManager.registerUsedCharacterData(characterData);
\r
628 AppConfig appConfig = AppConfig.getInstance();
\r
629 Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
\r
630 .getLocalizedProperties(STRINGS_RESOURCE);
\r
634 if (Main.isMacOSX()) {
\r
635 // Mac OS Xの場合はウィンドウにタイトルはつけない。
\r
638 title = strings.getProperty("title");
\r
640 setTitle(title + characterData.getName());
\r
643 defaultPartsSetTitle = strings.getProperty("defaultPartsSetTitle");
\r
649 Container contentPane = getContentPane();
\r
652 for (Component comp : contentPane.getComponents()) {
\r
653 contentPane.remove(comp);
\r
655 // 開いている検索ダイアログを閉じる
\r
656 closeSearchDialog();
\r
658 // 開いているお気に入り管理ダイアログを閉じる
\r
659 closeManageFavoritesDialog();
\r
661 PartsColorManager partsColorManager = characterData.getPartsColorManager();
\r
664 Color bgColor = appConfig.getDefaultImageBgColor();
\r
665 wallpaperInfo = new WallpaperInfo();
\r
666 wallpaperInfo.setBackgroundColor(bgColor);
\r
668 if (imageLoader != null) {
\r
669 imageLoader.close();
\r
671 imageLoader = new ColorConvertedImageCachedLoader();
\r
672 imageBuilder = new AsyncImageBuilder(imageLoader);
\r
673 partsSelectionManager = new PartsSelectionManager(partsColorManager,
\r
674 new PartsSelectionManager.ImageBgColorProvider() {
\r
675 public Color getImageBgColor() {
\r
676 return wallpaperInfo.getBackgroundColor();
\r
678 public void setImageBgColor(Color imageBgColor) {
\r
679 applyBackgroundColorOnly(imageBgColor);
\r
682 colorGroupCoordinator = new ColorGroupCoordinator(partsSelectionManager, partsColorManager);
\r
683 partsColorCoordinator = new PartsColorCoordinator(characterData, partsColorManager, colorGroupCoordinator);
\r
684 PartsImageDirectoryWatchAgentFactory agentFactory = PartsImageDirectoryWatchAgentFactory.getFactory();
\r
685 watchAgent = agentFactory.getAgent(characterData);
\r
687 previewPane = new PreviewPanel();
\r
688 previewPane.setTitle(defaultPartsSetTitle);
\r
689 previewPane.addPreviewPanelListener(new PreviewPanelListener() {
\r
690 public void addFavorite(PreviewPanelEvent e) {
\r
691 if (!e.isShiftKeyPressed()) {
\r
693 onRegisterFavorite();
\r
696 // シフトキーにて、お気に入りの管理を開く
\r
697 onManageFavorites();
\r
700 public void changeBackgroundColor(PreviewPanelEvent e) {
\r
701 if ( !e.isShiftKeyPressed()) {
\r
703 onChangeWallpaper();
\r
710 public void copyPicture(PreviewPanelEvent e) {
\r
711 onCopy(e.isShiftKeyPressed());
\r
713 public void savePicture(PreviewPanelEvent e) {
\r
714 if ( !e.isShiftKeyPressed()) {
\r
723 public void showInformation(PreviewPanelEvent e) {
\r
726 public void flipHorizontal(PreviewPanelEvent e) {
\r
727 onFlipHolizontal();
\r
731 imageSelectPanels = new ImageSelectPanelList();
\r
733 JPanel imgSelectPanelsPanel = new JPanel();
\r
734 BoxLayout bl = new BoxLayout(imgSelectPanelsPanel, BoxLayout.PAGE_AXIS);
\r
735 imgSelectPanelsPanel.setLayout(bl);
\r
736 for (PartsCategory category : characterData.getPartsCategories()) {
\r
737 final ImageSelectPanel imageSelectPanel = new ImageSelectPanel(category, characterData);
\r
738 imgSelectPanelsPanel.add(imageSelectPanel);
\r
739 imageSelectPanels.add(imageSelectPanel);
\r
740 partsSelectionManager.register(imageSelectPanel);
\r
743 imgSelectPanelsPanelSp = new JScrollPane(imgSelectPanelsPanel) {
\r
744 private static final long serialVersionUID = 1L;
\r
746 public JScrollBar createVerticalScrollBar() {
\r
747 JScrollBar sb = super.createVerticalScrollBar();
\r
748 sb.setUnitIncrement(12);
\r
752 imgSelectPanelsPanelSp.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
\r
754 JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, imgSelectPanelsPanelSp, previewPane);
\r
755 contentPane.add(splitPane, BorderLayout.CENTER);
\r
758 imgSelectPanelsPanelSp.requestFocus();
\r
760 ArrayList<ColorGroup> colorGroups = new ArrayList<ColorGroup>();
\r
761 colorGroups.addAll(characterData.getColorGroups());
\r
763 final ColorChangeListener colorChangeListener = new ColorChangeListener() {
\r
764 public void onColorGroupChange(ColorChangeEvent event) {
\r
767 public void onColorChange(ColorChangeEvent event) {
\r
768 MainFrame.this.requestPreview();
\r
771 colorGroupCoordinator.addColorChangeListener(colorChangeListener);
\r
773 for (int idx = 0; idx < imageSelectPanels.size(); idx++) {
\r
774 ImageSelectPanel imageSelectPanel = imageSelectPanels.get(idx);
\r
775 final PartsCategory partsCategory = imageSelectPanel.getPartsCategory();
\r
776 final ColorDialog colorDialog = new ColorDialog(this, partsCategory, colorGroups);
\r
777 colorGroupCoordinator.registerColorDialog(colorDialog);
\r
778 partsColorCoordinator.register(imageSelectPanel, colorDialog);
\r
779 final int curidx = idx;
\r
780 imageSelectPanel.addImageSelectListener(new ImageSelectPanelListener() {
\r
781 public void onChangeColor(ImageSelectPanelEvent event) {
\r
782 WindowAdjustLocationSupport.alignRight(
\r
783 MainFrame.this, colorDialog, curidx, false);
\r
784 colorDialog.setVisible(!colorDialog.isVisible());
\r
786 public void onPreferences(ImageSelectPanelEvent event) {
\r
787 // do nothing. (not supported)
\r
789 public void onChange(ImageSelectPanelEvent event) {
\r
790 MainFrame.this.requestPreview();
\r
792 public void onSelectChange(ImageSelectPanelEvent event) {
\r
795 public void onTitleClick(ImageSelectPanelEvent event) {
\r
796 PartsCategory partsCategory = (event != null) ?
\r
797 event.getImageSelectPanel().getPartsCategory() : null;
\r
798 MainFrame.this.onClickPartsCategoryTitle(partsCategory, false);
\r
800 public void onTitleDblClick(ImageSelectPanelEvent event) {
\r
801 PartsCategory partsCategory = (event != null) ?
\r
802 event.getImageSelectPanel().getPartsCategory() : null;
\r
803 MainFrame.this.onClickPartsCategoryTitle(partsCategory, true);
\r
806 imageSelectPanel.addAncestorListener(new AncestorListener() {
\r
807 public void ancestorAdded(AncestorEvent event) {
\r
809 public void ancestorMoved(AncestorEvent event) {
\r
811 public void ancestorRemoved(AncestorEvent event) {
\r
812 // パネルもしくは、その親が削除されたときにダイアログも非表示とする。
\r
813 colorDialog.setVisible(false);
\r
819 partsSelectionManager.loadParts();
\r
821 // 保存されているワーキングセットを復元する.
\r
822 // 復元できなかった場合はパーツセットを初期選択する.
\r
823 if ( !loadWorkingSet()) {
\r
824 if (showDefaultParts(true)) {
\r
829 // 選択されているパーツを見える状態にする
\r
830 scrollToSelectedParts();
\r
833 if (!imageBuilder.isAlive()) {
\r
834 imageBuilder.start();
\r
838 new DropTarget(imgSelectPanelsPanelSp, new FileDropTarget() {
\r
840 protected void onDropFiles(final List<File> dropFiles) {
\r
841 if (dropFiles == null || dropFiles.isEmpty()) {
\r
845 // ドロップソースの処理がブロッキングしないように、
\r
846 // ドロップハンドラの処理を終了してからインポートダイアログが開くようにする.
\r
847 SwingUtilities.invokeLater(new Runnable() {
\r
848 public void run() {
\r
849 onImport(dropFiles);
\r
854 protected void onException(Exception ex) {
\r
855 ErrorMessageHelper.showErrorDialog(MainFrame.this, ex);
\r
859 // ディレクトリを監視し変更があった場合にパーツをリロードするリスナ
\r
860 watchAgent.addPartsImageDirectoryWatchListener(new PartsImageDirectoryWatchListener() {
\r
861 public void detectPartsImageChange(PartsImageDirectoryWatchEvent e) {
\r
862 Runnable refreshJob = new Runnable() {
\r
863 public void run() {
\r
864 onDetectPartsImageChange();
\r
867 if (SwingUtilities.isEventDispatchThread()) {
\r
870 SwingUtilities.invokeLater(refreshJob);
\r
875 // 監視が有効であれば、ディレクトリの監視をスタートする
\r
876 if (appConfig.isEnableDirWatch() && characterData.isWatchDirectory()) {
\r
877 watchAgent.connect();
\r
880 // パーツカテゴリの自動縮小が設定されている場合
\r
881 minimizeMode = false;
\r
882 if (appConfig.isEnableAutoShrinkPanel()) {
\r
883 onClickPartsCategoryTitle(null, true);
\r
887 if (oldCd != null) {
\r
893 * パーツが変更されたことを検知した場合.<br>
\r
894 * パーツデータをリロードし、各カテゴリのパーツ一覧を再表示させ、プレビューを更新する.<br>
\r
896 protected void onDetectPartsImageChange() {
\r
898 reloadPartsAndFavorites(null, true);
\r
900 } catch (IOException ex) {
\r
901 logger.log(Level.SEVERE, "parts reload failed. " + characterData, ex);
\r
906 * すべてのカテゴリのリストで選択中のアイテムが見えるようにスクロールする.
\r
908 protected void scrollToSelectedParts() {
\r
909 partsSelectionManager.scrollToSelectedParts();
\r
913 * 指定したパーツカテゴリ以外のパーツ選択パネルを最小化する.
\r
915 * @param partsCategory
\r
916 * パーツカテゴリ、nullの場合は全て最小化する.
\r
920 protected void onClickPartsCategoryTitle(PartsCategory partsCategory, boolean dblClick) {
\r
921 if (logger.isLoggable(Level.FINE)) {
\r
922 logger.log(Level.FINE, "onClickPartsCategoryTitle category="
\r
923 + partsCategory + "/clickCount=" + dblClick);
\r
926 minimizeMode = !minimizeMode;
\r
927 if (!minimizeMode) {
\r
928 partsSelectionManager.setMinimizeModeIfOther(null, false);
\r
932 if (minimizeMode) {
\r
933 if (partsSelectionManager.isNotMinimizeModeJust(partsCategory)) {
\r
934 partsSelectionManager.setMinimizeModeIfOther(null, true); // 全部縮小
\r
937 partsSelectionManager.setMinimizeModeIfOther(partsCategory, true);
\r
938 if (partsCategory != null) {
\r
939 // 対象のパネルがスクロールペイン内に見える用にスクロールする.
\r
940 // スクロールバーの位置指定などの座標系の操作は「要求」であり、実際に適用されるまで本当の位置は分らない。
\r
941 // よって以下の処理は非同期に行い、先に座標を確定させたものに対して行う必要がある。
\r
942 final ImageSelectPanel panel = imageSelectPanels.findByPartsCategory(partsCategory);
\r
943 SwingUtilities.invokeLater(new Runnable() {
\r
944 public void run() {
\r
945 final Point pt = panel.getLocation();
\r
946 JViewport viewPort = imgSelectPanelsPanelSp.getViewport();
\r
947 viewPort.setViewPosition(pt);
\r
948 viewPort.revalidate();
\r
957 * デフォルトパーツを選択する.<br>
\r
958 * デフォルトパーツがなければお気に入りの最初のものを選択する.<br>
\r
959 * それもなければ空として表示する.<br>
\r
960 * パーツの適用に失敗した場合はfalseを返します.(例外は返されません.)<br>
\r
963 * すでに選択があっても選択しなおす場合はtrue、falseの場合は選択があれば何もしない.
\r
964 * @return パーツ選択された場合。force=trueの場合はエラーがなければ常にtrueとなります。
\r
966 protected boolean showDefaultParts(boolean force) {
\r
969 // 現在選択中のパーツを取得する.(なければ空)
\r
970 PartsSet sel = partsSelectionManager.createPartsSet();
\r
971 if (!sel.isEmpty()) {
\r
972 // 強制選択でない場合、すでに選択済みのパーツがあれば何もしない.
\r
977 // デフォルトのパーツセットを取得する
\r
978 String defaultPresetId = characterData.getDefaultPartsSetId();
\r
979 PartsSet partsSet = null;
\r
980 if (defaultPresetId != null) {
\r
981 partsSet = characterData.getPartsSets().get(defaultPresetId);
\r
984 // デフォルトのパーツセットがなければ、お気に入りの最初を選択する.
\r
985 if (partsSet == null) {
\r
986 List<PartsSet> partssets = getPartsSetList();
\r
987 if (!partssets.isEmpty()) {
\r
988 partsSet = partssets.get(0);
\r
992 // パーツセットがあれば、それを表示要求する.
\r
993 // パーツセットがなければカラーダイアログを初期化するのみ
\r
994 if (partsSet == null) {
\r
995 partsColorCoordinator.initColorDialog();
\r
998 selectPresetParts(partsSet);
\r
1001 } catch (Exception ex) {
\r
1002 logger.log(Level.WARNING, "パーツのデフォルト適用に失敗しました。", ex);
\r
1009 * プリセットを適用しキャラクターイメージを再構築します.<br>
\r
1010 * 実行時エラーは画面のレポートされます.<br>
\r
1012 * @param presetParts
\r
1013 * パーツセット, nullの場合は何もしない.
\r
1015 protected void selectPresetParts(PartsSet presetParts) {
\r
1016 if (presetParts == null) {
\r
1020 // 最後に使用したプリセットとして記憶する.
\r
1021 lastUsePresetParts = presetParts;
\r
1022 // プリセットパーツで選択を変える
\r
1023 partsSelectionManager.selectPartsSet(presetParts);
\r
1024 // カラーパネルを選択されているアイテムをもとに再設定する
\r
1025 partsColorCoordinator.initColorDialog();
\r
1029 } catch (Exception ex) {
\r
1030 ErrorMessageHelper.showErrorDialog(this, ex);
\r
1035 * プリセットとお気に入りを表示順に並べて返す.
\r
1037 * @return プリセットとお気に入りのリスト(表示順)
\r
1039 protected List<PartsSet> getPartsSetList() {
\r
1040 ArrayList<PartsSet> partssets = new ArrayList<PartsSet>();
\r
1041 partssets.addAll(characterData.getPartsSets().values());
\r
1042 Collections.sort(partssets, PartsSet.DEFAULT_COMPARATOR);
\r
1046 protected static final class TreeLeaf implements Comparable<TreeLeaf> {
\r
1048 public enum TreeLeafType {
\r
1052 private String name;
\r
1054 private TreeLeafType typ;
\r
1056 public TreeLeaf(TreeLeafType typ, String name) {
\r
1057 if (name == null) {
\r
1064 public String getName() {
\r
1068 public TreeLeafType getTyp() {
\r
1073 public boolean equals(Object obj) {
\r
1074 if (obj != null && obj instanceof TreeLeaf) {
\r
1075 TreeLeaf o = (TreeLeaf) obj;
\r
1076 return typ == o.typ && name.equals(o.name);
\r
1082 public int hashCode() {
\r
1083 return typ.hashCode() ^ name.hashCode();
\r
1086 public int compareTo(TreeLeaf o) {
\r
1087 int ret = name.compareTo(o.name);
\r
1089 ret = (typ.ordinal() - o.typ.ordinal());
\r
1095 public String toString() {
\r
1100 protected TreeMap<TreeLeaf, Object> buildFavoritesItemTree(
\r
1101 List<PartsSet> partssets) {
\r
1102 if (partssets == null) {
\r
1103 partssets = Collections.emptyList();
\r
1105 TreeMap<TreeLeaf, Object> favTree = new TreeMap<TreeLeaf, Object>();
\r
1106 for (PartsSet partsSet : partssets) {
\r
1107 String flatname = partsSet.getLocalizedName();
\r
1108 String[] tokens = flatname.split("\\|");
\r
1109 if (tokens.length == 0) {
\r
1113 TreeMap<TreeLeaf, Object> r = favTree;
\r
1114 for (int idx = 0; idx < tokens.length - 1; idx++) {
\r
1115 String name = tokens[idx];
\r
1116 TreeLeaf leafName = new TreeLeaf(TreeLeaf.TreeLeafType.NODE,
\r
1118 @SuppressWarnings("unchecked")
\r
1119 TreeMap<TreeLeaf, Object> n = (TreeMap<TreeLeaf, Object>) r
\r
1122 n = new TreeMap<TreeLeaf, Object>();
\r
1123 r.put(leafName, n);
\r
1127 String lastName = tokens[tokens.length - 1];
\r
1128 TreeLeaf lastLeafName = new TreeLeaf(TreeLeaf.TreeLeafType.LEAF,
\r
1130 @SuppressWarnings("unchecked")
\r
1131 List<PartsSet> leafValue = (List<PartsSet>) r.get(lastLeafName);
\r
1132 if (leafValue == null) {
\r
1133 leafValue = new ArrayList<PartsSet>();
\r
1134 r.put(lastLeafName, leafValue);
\r
1136 leafValue.add(partsSet);
\r
1141 protected interface FavoriteMenuItemBuilder {
\r
1142 JMenuItem createFavoriteMenuItem(String name, PartsSet partsSet);
\r
1143 JMenu createSubMenu(String name);
\r
1146 private void buildFavoritesMenuItems(List<JMenuItem> menuItems,
\r
1147 FavoriteMenuItemBuilder favMenuItemBuilder,
\r
1148 TreeMap<TreeLeaf, Object> favTree) {
\r
1149 for (Map.Entry<TreeLeaf, Object> entry : favTree.entrySet()) {
\r
1150 TreeLeaf treeLeaf = entry.getKey();
\r
1151 String name = treeLeaf.getName();
\r
1152 if (treeLeaf.getTyp() == TreeLeaf.TreeLeafType.LEAF) {
\r
1153 // 葉ノードには、JMenuItemを設定する.
\r
1154 @SuppressWarnings("unchecked")
\r
1155 List<PartsSet> leafValue = (List<PartsSet>) entry.getValue();
\r
1156 for (final PartsSet partsSet : leafValue) {
\r
1157 JMenuItem favoriteMenu = favMenuItemBuilder
\r
1158 .createFavoriteMenuItem(name, partsSet);
\r
1159 menuItems.add(favoriteMenu);
\r
1162 } else if (treeLeaf.getTyp() == TreeLeaf.TreeLeafType.NODE) {
\r
1163 // 枝ノードは、サブメニューを作成し、子ノードを設定する
\r
1164 @SuppressWarnings("unchecked")
\r
1165 TreeMap<TreeLeaf, Object> childNode = (TreeMap<TreeLeaf, Object>) entry
\r
1167 JMenu subMenu = favMenuItemBuilder.createSubMenu(name);
\r
1168 menuItems.add(subMenu);
\r
1169 ArrayList<JMenuItem> subMenuItems = new ArrayList<JMenuItem>();
\r
1170 buildFavoritesMenuItems(subMenuItems, favMenuItemBuilder, childNode);
\r
1171 for (JMenuItem subMenuItem : subMenuItems) {
\r
1172 subMenu.add(subMenuItem);
\r
1176 throw new RuntimeException("unknown type: " + treeLeaf);
\r
1182 * お気に入りのJMenuItemを作成するファンクションオブジェクト
\r
1184 private FavoriteMenuItemBuilder favMenuItemBuilder = new FavoriteMenuItemBuilder() {
\r
1185 private MenuBuilder menuBuilder = new MenuBuilder();
\r
1190 public JMenuItem createFavoriteMenuItem(final String name,
\r
1191 final PartsSet partsSet) {
\r
1192 JMenuItem favoriteMenu = menuBuilder.createJMenuItem();
\r
1193 favoriteMenu.setName(partsSet.getPartsSetId());
\r
1194 favoriteMenu.setText(name);
\r
1195 if (partsSet.isPresetParts()) {
\r
1196 Font font = favoriteMenu.getFont();
\r
1197 favoriteMenu.setFont(font.deriveFont(Font.BOLD));
\r
1199 favoriteMenu.addActionListener(new ActionListener() {
\r
1200 public void actionPerformed(ActionEvent e) {
\r
1201 selectPresetParts(partsSet);
\r
1205 // メニューアイテム上でマウスホイールを動かした場合は上下にスクロールさせる.
\r
1206 // (ただし、OSXのスクリーンメニュー使用時は無視する.)
\r
1207 addMouseWheelListener(favoriteMenu);
\r
1209 return favoriteMenu;
\r
1215 public JMenu createSubMenu(String name) {
\r
1216 JMenu menu = menuBuilder.createJMenu();
\r
1217 menu.setText(name);
\r
1219 // メニューアイテム上でマウスホイールを動かした場合は上下にスクロールさせる.
\r
1220 // (ただし、OSXのスクリーンメニュー使用時は無視する.)
\r
1221 addMouseWheelListener(menu);
\r
1227 * メニューアイテム上でホイールを上下させたときにメニューをスクロールさせるためのホイールハンドラを設定する.
\r
1229 * @param favoriteMenu
\r
1231 protected void addMouseWheelListener(final JMenuItem favoriteMenu) {
\r
1232 if (JScrollableMenu.isScreenMenu()) {
\r
1235 favoriteMenu.addMouseWheelListener(new MouseWheelListener() {
\r
1236 public void mouseWheelMoved(MouseWheelEvent e) {
\r
1237 int rotation = e.getWheelRotation();
\r
1238 JPopupMenu popupMenu = (JPopupMenu) favoriteMenu
\r
1240 JMenu parentMenu = (JMenu) popupMenu.getInvoker();
\r
1241 if (parentMenu != null
\r
1242 && parentMenu instanceof JScrollableMenu) {
\r
1243 final JScrollableMenu favMenu = (JScrollableMenu) parentMenu;
\r
1244 favMenu.doScroll(rotation < 0);
\r
1257 protected void onSelectedFavoriteMenu(JMenu menu) {
\r
1259 List<PartsSet> partssets = getPartsSetList();
\r
1260 TreeMap<TreeLeaf, Object> favTree = buildFavoritesItemTree(partssets);
\r
1263 ArrayList<JMenuItem> favoritesMenuItems = new ArrayList<JMenuItem>();
\r
1264 buildFavoritesMenuItems(favoritesMenuItems, favMenuItemBuilder, favTree);
\r
1266 if (menu instanceof JScrollableMenu) {
\r
1268 JScrollableMenu favMenu = (JScrollableMenu) menu;
\r
1271 favMenu.initScroller();
\r
1273 // スクロールメニューアイテムの設定
\r
1274 favMenu.setScrollableItems(favoritesMenuItems);
\r
1277 // お気に入りメニューが選択された場合、
\r
1278 // お気に入りアイテム一覧を表示するよりも前に
\r
1279 // 表示可能なアイテム数を現在のウィンドウの高さから算定する.
\r
1280 Toolkit tk = Toolkit.getDefaultToolkit();
\r
1281 Dimension scrsiz = tk.getScreenSize();
\r
1282 int height = scrsiz.height; // MainFrame.this.getHeight();
\r
1283 favMenu.adjustMaxVisible(height);
\r
1284 logger.log(Level.FINE,
\r
1285 "scrollableMenu maxVisible=" + favMenu.getMaxVisible());
\r
1289 // 既存メニューの位置をセパレータより判断する.
\r
1290 int mx = menu.getMenuComponentCount();
\r
1291 int separatorIdx = -1;
\r
1292 for (int idx = 0; idx < mx; idx++) {
\r
1293 Component item = menu.getMenuComponent(idx);
\r
1294 if (item instanceof JSeparator) {
\r
1295 separatorIdx = idx;
\r
1300 if (separatorIdx > 0) {
\r
1301 while (menu.getMenuComponentCount() > separatorIdx + 1) {
\r
1302 menu.remove(separatorIdx + 1);
\r
1306 // お気に入りアイテムのメニューを登録する.
\r
1307 for (JMenuItem menuItem : favoritesMenuItems) {
\r
1308 menu.add(menuItem);
\r
1315 * ヘルプメニューを開いたときにお勧めメニューを構築する.
\r
1319 protected void onSelectedRecommendationMenu(JMenu mnuRecomendation) {
\r
1320 // 現在のお勧めメニューを一旦削除
\r
1321 while (mnuRecomendation.getMenuComponentCount() > 0) {
\r
1322 mnuRecomendation.remove(0);
\r
1325 // お勧めリンクの定義がない場合はデフォルトを用いる.(明示的な空の場合は何もしない.)
\r
1326 CharacterDataPersistent persist = CharacterDataPersistent.getInstance();
\r
1327 persist.compensateRecommendationList(characterData);
\r
1329 // お勧めリンクメニューを作成する.
\r
1330 List<RecommendationURL> recommendations = characterData.getRecommendationURLList();
\r
1331 if (recommendations != null) {
\r
1332 MenuBuilder menuBuilder = new MenuBuilder();
\r
1333 for (RecommendationURL recommendation : recommendations) {
\r
1334 String displayName = recommendation.getDisplayName();
\r
1335 String url = recommendation.getUrl();
\r
1337 JMenuItem mnuItem = menuBuilder.createJMenuItem();
\r
1338 mnuItem.setText(displayName);
\r
1339 mnuItem.addActionListener(
\r
1340 DesktopUtilities.createBrowseAction(MainFrame.this, url, displayName)
\r
1342 mnuRecomendation.add(mnuItem);
\r
1346 // お勧めリンクメニューのリストがnullでなく空でもない場合は有効、そうでなければ無効にする.
\r
1347 mnuRecomendation.setEnabled(recommendations != null && !recommendations.isEmpty());
\r
1352 * 最後に選択されたお気に入りと同じ構成であれば、 このお気に入りの名前をプレビューペインのタイトルに設定する.<br>
\r
1353 * そうでなければデフォルトのパーツセット名(no titleとか)を表示する.<br>
\r
1354 * 色情報が異なる場合に末尾に「*」マークがつけられる.<br>
\r
1356 * @param requestPartsSet
\r
1357 * 表示するパーツセット(名前は設定されていなくて良い。お気に入り側を使うので。), nullの場合はデフォルトのパーツ名
\r
1359 protected void showPresetName(PartsSet requestPartsSet) {
\r
1360 String title = getSuggestPartsSetName(requestPartsSet, true);
\r
1361 if (title == null) {
\r
1362 title = defaultPartsSetTitle;
\r
1364 previewPane.setTitle(title);
\r
1368 * パーツセット名を推定する.<br>
\r
1369 * 最後に選択されたお気に入りと同じ構成であれば、 このお気に入りの名前を返す.<br>
\r
1370 * お気に入りが選択されていないか構成が異なる場合、お気に入りに名前がない場合はnullを返す.<br>
\r
1372 * @param requestPartsSet
\r
1373 * 表示するパーツセット(名前は設定されていなくて良い。お気に入り側を使うので。)
\r
1374 * @param markColorChange
\r
1375 * 色情報が異なる場合に末尾に「*」マークをつける場合はtrue
\r
1377 private String getSuggestPartsSetName(PartsSet requestPartsSet, boolean markColorChange) {
\r
1378 String partsSetTitle = null;
\r
1379 if (lastUsePresetParts != null &&
\r
1380 PartsSet.isSameStructure(requestPartsSet, lastUsePresetParts)) {
\r
1381 partsSetTitle = lastUsePresetParts.getLocalizedName();
\r
1382 if (markColorChange && !PartsSet.isSameColor(requestPartsSet, lastUsePresetParts)) {
\r
1383 if (partsSetTitle != null) {
\r
1384 partsSetTitle += "*";
\r
1388 if (partsSetTitle != null && partsSetTitle.trim().length() > 0) {
\r
1389 return partsSetTitle;
\r
1395 * プレビューの更新を要求する. 更新は非同期に行われる.
\r
1397 protected void requestPreview() {
\r
1398 if (!characterData.isValid()) {
\r
1402 // 選択されているパーツの各イメージを取得しレイヤー順に並び替えて合成する.
\r
1403 // 合成は別スレッドにて非同期に行われる.
\r
1404 // リクエストは随時受け付けて、最新のリクエストだけが処理される.
\r
1405 // (処理がはじまる前に新しいリクエストで上書きされた場合、前のリクエストは単に捨てられる.)
\r
1406 imageBuilder.requestJob(new ImageBuildJobAbstractAdaptor(characterData) {
\r
1411 private PartsSet requestPartsSet;
\r
1414 * 非同期のイメージ構築要求の番号.<br>
\r
1416 private long ticket;
\r
1419 public void onQueueing(long ticket) {
\r
1420 this.ticket = ticket;
\r
1421 previewPane.setLoadingRequest(ticket);
\r
1424 public void buildImage(ImageOutput output) {
\r
1425 // 合成結果のイメージを引数としてイメージビルダから呼び出される.
\r
1426 final BufferedImage img = output.getImageOutput();
\r
1427 Runnable refreshJob = new Runnable() {
\r
1428 public void run() {
\r
1429 previewPane.setPreviewImage(img);
\r
1430 previewPane.setLoadingComplete(ticket);
\r
1431 showPresetName(requestPartsSet);
\r
1434 if (SwingUtilities.isEventDispatchThread()) {
\r
1438 SwingUtilities.invokeAndWait(refreshJob);
\r
1439 } catch (Exception ex) {
\r
1440 logger.log(Level.WARNING, "build image failed.", ex);
\r
1445 public void handleException(final Throwable ex) {
\r
1446 // 合成中に例外が発生した場合、イメージビルダから呼び出される.
\r
1447 Runnable showExceptionJob = new Runnable() {
\r
1448 public void run() {
\r
1449 ErrorMessageHelper.showErrorDialog(MainFrame.this, ex);
\r
1452 if (SwingUtilities.isEventDispatchThread()) {
\r
1453 showExceptionJob.run();
\r
1455 SwingUtilities.invokeLater(showExceptionJob);
\r
1459 protected PartsSet getPartsSet() {
\r
1460 // 合成できる状態になった時点でイメージビルダから呼び出される.
\r
1461 final PartsSet[] result = new PartsSet[1];
\r
1462 Runnable collectPartsSetJob = new Runnable() {
\r
1463 public void run() {
\r
1464 PartsSet partsSet = partsSelectionManager.createPartsSet();
\r
1465 result[0] = partsSet;
\r
1468 if (SwingUtilities.isEventDispatchThread()) {
\r
1469 collectPartsSetJob.run();
\r
1472 // スレッドによるSwingのイベントディスパッチスレッド以外からの呼び出しの場合、
\r
1473 // Swingディスパッチスレッドでパーツの選択状態を取得する.
\r
1474 SwingUtilities.invokeAndWait(collectPartsSetJob);
\r
1476 } catch (InvocationTargetException e) {
\r
1477 throw new RuntimeException(e.getMessage(), e);
\r
1478 } catch (InterruptedException e) {
\r
1479 throw new RuntimeException("interrupted:" + e, e);
\r
1482 if (logger.isLoggable(Level.FINE)) {
\r
1483 logger.log(Level.FINE, "preview: " + result[0]);
\r
1485 requestPartsSet = result[0];
\r
1486 return requestPartsSet;
\r
1494 protected void onOpenProfile() {
\r
1496 MainFrame main2 = ProfileListManager.openProfile(this);
\r
1497 if (main2 != null) {
\r
1498 main2.showMainFrame();
\r
1501 } catch (Exception ex) {
\r
1502 ErrorMessageHelper.showErrorDialog(this, ex);
\r
1509 protected void onChangeBgColor() {
\r
1510 getJMenuBar().setEnabled(false);
\r
1512 Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
\r
1513 .getLocalizedProperties(STRINGS_RESOURCE);
\r
1515 Color color = wallpaperInfo.getBackgroundColor();
\r
1516 color = JColorChooser.showDialog(this, strings.getProperty("chooseBgColor"), color);
\r
1517 if (color != null) {
\r
1518 applyBackgroundColorOnly(color);
\r
1521 getJMenuBar().setEnabled(true);
\r
1528 protected void onChangeWallpaper() {
\r
1530 WallpaperDialog wallpaperDialog = new WallpaperDialog(this);
\r
1532 // 最後に使用した壁紙情報でダイアログを設定する.
\r
1533 wallpaperDialog.setWallpaperInfo(this.wallpaperInfo);
\r
1535 // 壁紙情報を設定するモーダルダイアログを開く
\r
1536 WallpaperInfo wallpaperInfo = wallpaperDialog.showDialog();
\r
1537 if (wallpaperInfo == null) {
\r
1541 // 壁紙情報を保存し、その情報をもとに背景を再描画する.
\r
1542 applyWallpaperInfo(wallpaperInfo, false);
\r
1544 } catch (WallpaperFactoryException ex) {
\r
1545 ErrorMessageHelper.showErrorDialog(this, ex);
\r
1547 } catch (RuntimeException ex) {
\r
1548 ErrorMessageHelper.showErrorDialog(this, ex);
\r
1553 * 背景色のみ変更し、背景を再描画する.<br>
\r
1554 * 壁紙情報全体の更新よりも効率化するためのメソッドである.<br>
\r
1559 protected void applyBackgroundColorOnly(Color bgColor) {
\r
1560 wallpaperInfo.setBackgroundColor(bgColor);
\r
1561 previewPane.getWallpaper()
\r
1562 .setBackgroundColor(wallpaperInfo.getBackgroundColor());
\r
1566 * 壁紙情報を保存し、その情報をもとに背景を再描画する.<br>
\r
1567 * ignoreErrorがtrueである場合、適用に失敗した場合はログに記録するのみで、 壁紙情報は保存されず、壁紙も更新されない.<br>
\r
1569 * @param wallpaperInfo
\r
1571 * @param ignoreError
\r
1573 * @throws IOException
\r
1576 protected void applyWallpaperInfo(WallpaperInfo wallpaperInfo, boolean ignoreError) throws WallpaperFactoryException {
\r
1577 if (wallpaperInfo == null) {
\r
1578 throw new IllegalArgumentException();
\r
1580 // 壁紙情報から壁紙インスタンスを生成する.
\r
1581 WallpaperFactory wallpaperFactory = WallpaperFactory.getInstance();
\r
1582 Wallpaper wallpaper = null;
\r
1585 // 壁紙情報の構築時に問題が発生した場合、
\r
1586 // 回復処理をして継続するかエラーとするか?
\r
1587 WallpaperFactoryErrorRecoverHandler handler = null;
\r
1588 if (ignoreError) {
\r
1589 handler = new WallpaperFactoryErrorRecoverHandler();
\r
1593 wallpaper = wallpaperFactory.createWallpaper(wallpaperInfo, handler);
\r
1595 } catch (WallpaperFactoryException ex) {
\r
1596 logger.log(Level.WARNING, "壁紙情報の適用に失敗しました。", ex);
\r
1597 if ( !ignoreError) {
\r
1601 } catch (RuntimeException ex) {
\r
1602 logger.log(Level.WARNING, "壁紙情報の適用に失敗しました。", ex);
\r
1603 if ( !ignoreError) {
\r
1608 if (wallpaper == null) {
\r
1613 previewPane.setWallpaper(wallpaper);
\r
1616 this.wallpaperInfo = wallpaperInfo;
\r
1620 * プリビューしている画像をファイルに保存する。 サポートしているのはPNG/JPEGのみ。
\r
1622 protected void onSavePicture() {
\r
1623 Toolkit tk = Toolkit.getDefaultToolkit();
\r
1624 BufferedImage img = previewPane.getPreviewImage();
\r
1625 Color imgBgColor = wallpaperInfo.getBackgroundColor();
\r
1626 if (img == null) {
\r
1633 OutputOption outputOption = imageSaveHelper.getOutputOption();
\r
1634 outputOption.setZoomFactor(previewPane.getZoomFactor());
\r
1635 outputOption.changeRecommend();
\r
1636 imageSaveHelper.setOutputOption(outputOption);
\r
1639 File outFile = imageSaveHelper.showSaveFileDialog(this);
\r
1640 if (outFile == null) {
\r
1643 logger.log(Level.FINE, "savePicture: " + outFile);
\r
1644 logger.log(Level.FINE, "outputOption: " + outputOption);
\r
1647 StringBuilder warnings = new StringBuilder();
\r
1649 setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
\r
1651 imageSaveHelper.savePicture(img, imgBgColor, outFile, warnings);
\r
1654 setCursor(Cursor.getDefaultCursor());
\r
1656 if (warnings.length() > 0) {
\r
1657 JOptionPane.showMessageDialog(this, warnings.toString(), "WARNINGS", JOptionPane.WARNING_MESSAGE);
\r
1660 } catch (Exception ex) {
\r
1661 ErrorMessageHelper.showErrorDialog(this, ex);
\r
1668 protected void onSaveAsUkagaka() {
\r
1669 BufferedImage img = previewPane.getPreviewImage();
\r
1670 Color bgColor = wallpaperInfo.getBackgroundColor();
\r
1671 if (img == null) {
\r
1672 Toolkit tk = Toolkit.getDefaultToolkit();
\r
1678 ukagakaImageSaveHelper.save(this, img, bgColor);
\r
1680 } catch (IOException ex) {
\r
1681 ErrorMessageHelper.showErrorDialog(this, ex);
\r
1688 protected void onConvertUkagaka() {
\r
1690 Color colorKey = wallpaperInfo.getBackgroundColor();
\r
1691 ukagakaImageSaveHelper.convertChooseFiles(this, colorKey);
\r
1693 } catch (IOException ex) {
\r
1694 ErrorMessageHelper.showErrorDialog(this, ex);
\r
1701 protected void onBrowseProfileDir() {
\r
1702 if (!characterData.isValid()) {
\r
1703 Toolkit tk = Toolkit.getDefaultToolkit();
\r
1708 DesktopUtilities.browseBaseDir(characterData.getDocBase());
\r
1710 } catch (Exception ex) {
\r
1711 ErrorMessageHelper.showErrorDialog(this, ex);
\r
1718 protected void onEditProfile() {
\r
1719 if (!characterData.isValid()) {
\r
1720 Toolkit tk = Toolkit.getDefaultToolkit();
\r
1725 CharacterData cd = this.characterData;
\r
1726 CharacterData newCd = ProfileListManager.editProfile(this, cd);
\r
1727 if (newCd != null) {
\r
1728 MainFrame.notifyChangeCharacterData(cd, newCd, this);
\r
1731 } catch (Exception ex) {
\r
1732 ErrorMessageHelper.showErrorDialog(this, ex);
\r
1737 * パーツの管理ダイアログを開く.<br>
\r
1739 protected void onManageParts() {
\r
1740 if (!characterData.isValid()) {
\r
1741 Toolkit tk = Toolkit.getDefaultToolkit();
\r
1746 PartsManageDialog mrgDlg = new PartsManageDialog(this, characterData);
\r
1747 mrgDlg.setVisible(true);
\r
1749 if (mrgDlg.isUpdated()) {
\r
1750 // パーツ管理情報が更新された場合、
\r
1752 if (characterData.reloadPartsData()) {
\r
1753 partsSelectionManager.loadParts();
\r
1760 * 「パーツ検索」ダイアログを開く.<br>
\r
1761 * すでに開いているダイアログがあれば、それにフォーカスを当てる.<br>
\r
1763 protected void openSearchDialog() {
\r
1764 if (!characterData.isValid()) {
\r
1765 Toolkit tk = Toolkit.getDefaultToolkit();
\r
1770 if (lastUseSearchPartsDialog != null) {
\r
1771 // 開いているダイアログがあれば、それにフォーカスを当てる.
\r
1772 if (lastUseSearchPartsDialog.isDisplayable() && lastUseSearchPartsDialog.isVisible()) {
\r
1773 lastUseSearchPartsDialog.requestFocus();
\r
1778 SearchPartsDialog searchPartsDlg = new SearchPartsDialog(this, characterData, partsSelectionManager);
\r
1779 WindowAdjustLocationSupport.alignRight(this, searchPartsDlg, 0, true);
\r
1780 searchPartsDlg.setVisible(true);
\r
1781 lastUseSearchPartsDialog = searchPartsDlg;
\r
1785 * 「パーツ検索」ダイアログを閉じる.<br>
\r
1787 protected void closeSearchDialog() {
\r
1788 lastUseSearchPartsDialog = null;
\r
1789 for (SearchPartsDialog dlg : SearchPartsDialog.getDialogs()) {
\r
1790 if (dlg != null && dlg.isDisplayable() && dlg.getParent() == this) {
\r
1797 * 「お気に入りの管理」ダイアログを閉じる
\r
1799 protected void closeManageFavoritesDialog() {
\r
1800 if (lastUseManageFavoritesDialog != null) {
\r
1801 if (lastUseManageFavoritesDialog.isDisplayable()) {
\r
1802 lastUseManageFavoritesDialog.dispose();
\r
1804 lastUseManageFavoritesDialog = null;
\r
1811 * @param screenImage
\r
1814 protected void onCopy(boolean screenImage) {
\r
1816 BufferedImage img = previewPane.getPreviewImage();
\r
1817 if (img == null) {
\r
1818 Toolkit tk = Toolkit.getDefaultToolkit();
\r
1823 if (screenImage) {
\r
1824 // 表示している内容をそのままコピーする.
\r
1825 img = previewPane.getScreenImage();
\r
1828 Color imgBgColor = wallpaperInfo.getBackgroundColor();
\r
1829 ClipboardUtil.setImage(img, imgBgColor);
\r
1831 } catch (Exception ex) {
\r
1832 ErrorMessageHelper.showErrorDialog(this, ex);
\r
1837 * アプリケーションの設定ダイアログを開く
\r
1839 protected void onPreferences() {
\r
1840 AppConfigDialog appConfigDlg = new AppConfigDialog(this);
\r
1841 appConfigDlg.setVisible(true);
\r
1845 * 新規モードでインポートウィザードを実行する.<br>
\r
1847 protected void onImportNew() {
\r
1848 if (!characterData.isValid()) {
\r
1849 Toolkit tk = Toolkit.getDefaultToolkit();
\r
1855 // インポートウィザードの実行(新規モード)
\r
1856 ImportWizardDialog importWizDialog = new ImportWizardDialog(this, null, null);
\r
1857 importWizDialog.setVisible(true);
\r
1858 int exitCode = importWizDialog.getExitCode();
\r
1859 if (exitCode == ImportWizardDialog.EXIT_PROFILE_CREATED) {
\r
1860 CharacterData cd = importWizDialog.getImportedCharacterData();
\r
1861 if (cd != null && cd.isValid()) {
\r
1862 // インポートしたキャラクターデータのプロファイルを開く.
\r
1863 setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
\r
1865 MainFrame mainFrame = ProfileListManager.openProfile(cd);
\r
1866 mainFrame.setVisible(true);
\r
1869 setCursor(Cursor.getDefaultCursor());
\r
1874 } catch (Exception ex) {
\r
1875 ErrorMessageHelper.showErrorDialog(this, ex);
\r
1880 * 現在のプロファイルに対するインポートウィザードを実行する.<br>
\r
1881 * インポートが実行された場合は、パーツをリロードする.<br>
\r
1882 * インポートウィザード表示中は監視スレッドは停止される.<br>
\r
1885 * アーカイブファィルまたはディレクトリ、指定がなければnull
\r
1887 protected void onImport(List<File> initFiles) {
\r
1888 if (!characterData.isValid()) {
\r
1889 Toolkit tk = Toolkit.getDefaultToolkit();
\r
1895 watchAgent.suspend();
\r
1898 ImportWizardDialog importWizDialog = new ImportWizardDialog(this, characterData, initFiles);
\r
1899 importWizDialog.setVisible(true);
\r
1901 if (importWizDialog.getExitCode() == ImportWizardDialog.EXIT_PROFILE_UPDATED) {
\r
1902 CharacterData importedCd = importWizDialog.getImportedCharacterData();
\r
1903 notifyImportedPartsOrFavorites(characterData, importedCd, this);
\r
1907 watchAgent.resume();
\r
1910 } catch (Exception ex) {
\r
1911 ErrorMessageHelper.showErrorDialog(this, ex);
\r
1916 * パーツとお気に入りをリロードする.<br>
\r
1917 * まだロードされていない場合はあらたにロードする.<br>
\r
1918 * 引数newCdが指定されている場合は、現在のキャラクター定義の説明文を更新する.<br>
\r
1919 * (説明文の更新以外には使用されない.)<br>
\r
1922 * 説明文更新のための更新されたキャラクターデータを指定する。null可
\r
1923 * @param forceRepaint
\r
1925 * @throws IOException
\r
1928 protected synchronized void reloadPartsAndFavorites(CharacterData newCd,
\r
1929 boolean forceRepaint) throws IOException {
\r
1930 if (newCd != null) {
\r
1931 // (インポート画面では説明文のみ更新するので、それだけ取得)
\r
1932 characterData.setDescription(newCd.getDescription());
\r
1935 if ( !characterData.isPartsLoaded()) {
\r
1936 // キャラクターデータが、まだ読み込まれていなければ読み込む.
\r
1937 ProfileListManager.loadCharacterData(characterData);
\r
1938 ProfileListManager.loadFavorites(characterData);
\r
1939 partsSelectionManager.loadParts();
\r
1943 if (characterData.reloadPartsData()) {
\r
1944 partsSelectionManager.loadParts();
\r
1948 CharacterDataPersistent persiste = CharacterDataPersistent.getInstance();
\r
1949 persiste.loadFavorites(characterData);
\r
1951 // お気に入りが更新されたことを通知する.
\r
1952 FavoritesChangeObserver.getDefault().notifyFavoritesChange(
\r
1953 MainFrame.this, characterData);
\r
1956 // 現在選択されているパーツセットがない場合はデフォルトのパーツセットを選択する.
\r
1957 if (showDefaultParts(false) || forceRepaint) {
\r
1962 protected void onExport() {
\r
1963 if (!characterData.isValid()) {
\r
1964 Toolkit tk = Toolkit.getDefaultToolkit();
\r
1968 ExportWizardDialog exportWizDlg = new ExportWizardDialog(this, characterData, previewPane.getPreviewImage());
\r
1969 exportWizDlg.setVisible(true);
\r
1972 protected void onResetColor() {
\r
1973 if (!characterData.isValid()) {
\r
1974 Toolkit tk = Toolkit.getDefaultToolkit();
\r
1979 Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
\r
1980 .getLocalizedProperties(STRINGS_RESOURCE);
\r
1982 if (JOptionPane.showConfirmDialog(this, strings.get("confirm.resetcolors"), strings.getProperty("confirm"),
\r
1983 JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) != JOptionPane.YES_OPTION) {
\r
1986 characterData.getPartsColorManager().resetPartsColorInfo();
\r
1987 partsColorCoordinator.initColorDialog();
\r
1994 protected void onCloseProfile() {
\r
1996 ProfileListManager.unregisterUsedCharacterData(characterData);
\r
1998 if (characterData.isValid()) {
\r
2000 // 最後に使用したキャラクターデータとして記憶する.
\r
2002 RecentDataPersistent recentPersist = RecentDataPersistent.getInstance();
\r
2003 recentPersist.saveRecent(characterData);
\r
2005 } catch (Exception ex) {
\r
2006 logger.log(Level.WARNING, "recent data saving failed.", ex);
\r
2007 // recent情報の記録に失敗しても致命的ではないので、これは無視する.
\r
2011 // イメージビルダスレッド・ディレクトリ監視スレッドを停止する.
\r
2014 // フレームウィンドウを破棄する.
\r
2017 // 破棄されたことをロギングする.
\r
2018 logger.log(Level.FINE, "dispose mainframe.");
\r
2022 * 開いている、すべてのプロファイルを閉じる.<br>
\r
2023 * (Mac OS Xのcmd+Qで閉じる場合などで使用される.)<br>
\r
2025 public static void closeAllProfiles() {
\r
2026 // ウィンドウが閉じられることでアクティブなフレームが切り替わる場合を想定し、
\r
2027 // 現在のアクティブなウィンドウをあらかじめ記憶しておく
\r
2028 MainFrame mainFrame = activedMainFrame;
\r
2030 // gcをかけてファイナライズを促進させる
\r
2033 // ファイナライズされていないFrameのうち、ネイティブリソースと関連づけられている
\r
2034 // フレームについて、それがMainFrameのインスタンスであれば閉じる.
\r
2035 // ただし、現在アクティブなものは除く
\r
2036 for (Frame frame : JFrame.getFrames()) {
\r
2038 if (frame.isDisplayable()) {
\r
2039 // ネイティブリソースと関連づけられているフレーム
\r
2040 if (frame instanceof MainFrame && frame != mainFrame) {
\r
2041 // MainFrameのインスタンスであるので閉じる処理が可能.
\r
2042 // (現在アクティブなメインフレームは最後に閉じるため、ここでは閉じない.)
\r
2043 ((MainFrame) frame).onCloseProfile();
\r
2047 } catch (Throwable ex) {
\r
2048 logger.log(Level.SEVERE, "mainframe closing failed.", ex);
\r
2049 // フレームを閉じるときに失敗した場合、通常、致命的問題だが
\r
2050 // クローズ処理は継続しなければならない.
\r
2054 // 現在アクティブなフレームを閉じる.
\r
2055 // 最後に閉じることで「最後に使ったプロファイル」として記憶させる.
\r
2056 if (activedMainFrame != null && activedMainFrame.isDisplayable()) {
\r
2058 activedMainFrame.onCloseProfile();
\r
2060 } catch (Throwable ex) {
\r
2061 logger.log(Level.SEVERE, "mainframe closing failed.", ex);
\r
2062 // フレームを閉じるときに失敗した場合、通常、致命的問題だが
\r
2063 // クローズ処理は継続しなければならない.
\r
2071 protected void saveWorkingSet() {
\r
2072 if (!characterData.isValid()) {
\r
2077 WorkingSet workingSet = new WorkingSet();
\r
2078 workingSet.setCharacterDocBase(characterData.getDocBase());
\r
2079 workingSet.setCharacterDataRev(characterData.getRev());
\r
2080 PartsSet partsSet = partsSelectionManager.createPartsSet();
\r
2081 workingSet.setPartsSet(partsSet);
\r
2082 workingSet.setPartsColorInfoMap(characterData
\r
2083 .getPartsColorManager().getPartsColorInfoMap());
\r
2084 workingSet.setLastUsedSaveDir(imageSaveHelper.getLastUsedSaveDir());
\r
2085 workingSet.setLastUsedExportDir(ExportWizardDialog.getLastUsedDir());
\r
2086 workingSet.setLastUsePresetParts(lastUsePresetParts);
\r
2088 .setCharacterData(characterData.duplicateBasicInfo(false)); // パーツセットは保存しない.
\r
2089 workingSet.setWallpaperInfo(wallpaperInfo);
\r
2091 // XML形式でのワーキングセットの保存
\r
2092 WorkingSetPersist workingSetPersist = WorkingSetPersist
\r
2094 workingSetPersist.saveWorkingSet(workingSet);
\r
2096 } catch (Exception ex) {
\r
2097 ErrorMessageHelper.showErrorDialog(this, ex);
\r
2104 * @return ワーキングセットを読み込んだ場合はtrue、そうでなければfalse
\r
2106 protected boolean loadWorkingSet() {
\r
2107 if (!characterData.isValid()) {
\r
2111 WorkingSetPersist workingSetPersist = WorkingSetPersist
\r
2113 WorkingSet2 workingSet2 = workingSetPersist
\r
2114 .loadWorkingSet(characterData);
\r
2115 if (workingSet2 == null) {
\r
2120 URI docBase = characterData.getDocBase();
\r
2121 if (docBase != null
\r
2122 && !docBase.equals(workingSet2.getCharacterDocBase())) {
\r
2126 String sig = characterData.toSignatureString();
\r
2127 if (!sig.equals(workingSet2.getCharacterDataSig())) {
\r
2133 Map<PartsIdentifier, PartsColorInfo> partsColorInfoMap = characterData
\r
2134 .getPartsColorManager().getPartsColorInfoMap();
\r
2135 workingSet2.createCompatible(characterData, partsColorInfoMap);
\r
2138 IndependentPartsSetInfo partsSetInfo = workingSet2
\r
2139 .getCurrentPartsSet();
\r
2140 if (partsSetInfo != null) {
\r
2141 PartsSet partsSet = IndependentPartsSetInfo.convertPartsSet(
\r
2142 partsSetInfo, characterData, false);
\r
2143 selectPresetParts(partsSet);
\r
2145 // 最後に選択したお気に入り情報の復元
\r
2146 IndependentPartsSetInfo lastUsePresetPartsInfo = workingSet2
\r
2147 .getLastUsePresetParts();
\r
2148 if (lastUsePresetPartsInfo != null
\r
2149 && lastUsePresetPartsInfo.getId() != null
\r
2150 && lastUsePresetPartsInfo.getId().trim().length() > 0) {
\r
2151 PartsSet lastUsePresetParts = IndependentPartsSetInfo
\r
2152 .convertPartsSet(lastUsePresetPartsInfo,
\r
2153 characterData, false);
\r
2154 if (lastUsePresetParts.isSameStructure(partsSet)) {
\r
2155 this.lastUsePresetParts = lastUsePresetParts;
\r
2156 showPresetName(lastUsePresetParts);
\r
2161 // 最後に保存したディレクトリを復元する.
\r
2162 imageSaveHelper.setLastUseSaveDir(workingSet2.getLastUsedSaveDir());
\r
2163 ExportWizardDialog.setLastUsedDir(workingSet2
\r
2164 .getLastUsedExportDir());
\r
2167 WallpaperInfo wallpaperInfo = workingSet2.getWallpaperInfo();
\r
2168 if (wallpaperInfo != null) {
\r
2169 // 壁紙情報を保存し、その情報をもとに背景を再描画する.
\r
2170 // (適用に失敗した場合はエラーは無視し、壁紙情報は保存しない.)
\r
2171 applyWallpaperInfo(wallpaperInfo, true);
\r
2175 } catch (Exception ex) {
\r
2176 ErrorMessageHelper.showErrorDialog(this, ex);
\r
2182 protected void onAbout() {
\r
2184 AboutBox aboutBox = new AboutBox(this);
\r
2185 aboutBox.showAboutBox();
\r
2187 } catch (Exception ex) {
\r
2188 ErrorMessageHelper.showErrorDialog(this, ex);
\r
2192 protected void onHelp() {
\r
2193 Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
\r
2194 .getLocalizedProperties(STRINGS_RESOURCE);
\r
2195 String helpURL = strings.getProperty("help.url");
\r
2196 String helpDescription = strings.getProperty("help.show");
\r
2197 DesktopUtilities.browse(this, helpURL, helpDescription);
\r
2200 protected void onFlipHolizontal() {
\r
2201 if (!characterData.isValid()) {
\r
2202 Toolkit tk = Toolkit.getDefaultToolkit();
\r
2207 double[] affineTransformParameter = partsSelectionManager.getAffineTransformParameter();
\r
2208 if (affineTransformParameter == null) {
\r
2209 // 左右フリップするアフィン変換パラメータを構築する.
\r
2210 Dimension siz = characterData.getImageSize();
\r
2211 if (siz != null) {
\r
2212 affineTransformParameter = new double[] {-1., 0, 0, 1., siz.width, 0};
\r
2215 // アフィン変換パラメータをクリアする.
\r
2216 affineTransformParameter = null;
\r
2218 partsSelectionManager.setAffineTransformParameter(affineTransformParameter);
\r
2222 protected void onSetDefaultPicture() {
\r
2223 if (!characterData.isValid()) {
\r
2224 Toolkit tk = Toolkit.getDefaultToolkit();
\r
2229 BufferedImage samplePicture = previewPane.getPreviewImage();
\r
2230 if (samplePicture != null) {
\r
2231 CharacterDataPersistent persist = CharacterDataPersistent.getInstance();
\r
2232 persist.saveSamplePicture(characterData, samplePicture);
\r
2235 } catch (Exception ex) {
\r
2236 ErrorMessageHelper.showErrorDialog(this, ex);
\r
2240 protected void onInformation() {
\r
2241 if (!characterData.isValid()) {
\r
2242 Toolkit tk = Toolkit.getDefaultToolkit();
\r
2247 PartsSet partsSet = partsSelectionManager.createPartsSet();
\r
2248 InformationDialog infoDlg = new InformationDialog(this, characterData, partsSet);
\r
2249 infoDlg.setVisible(true);
\r
2252 protected void onManageFavorites() {
\r
2253 if (!characterData.isValid()) {
\r
2254 Toolkit tk = Toolkit.getDefaultToolkit();
\r
2259 if (lastUseManageFavoritesDialog != null) {
\r
2260 // 開いているダイアログがあれば、それにフォーカスを当てる.
\r
2261 if (lastUseManageFavoritesDialog.isDisplayable()
\r
2262 && lastUseManageFavoritesDialog.isVisible()) {
\r
2263 lastUseManageFavoritesDialog.requestFocus();
\r
2268 // お気に入り編集ダイアログを開く
\r
2269 ManageFavoriteDialog dlg = new ManageFavoriteDialog(this, characterData);
\r
2270 dlg.setFavoriteManageCallback(new FavoriteManageCallback() {
\r
2272 public void refreshFavorites(CharacterData cd) {
\r
2273 // お気に入りの状態を最新にリフレッシュする.
\r
2274 MainFrame.this.refreshFavorites();
\r
2277 public void selectFavorites(PartsSet partsSet) {
\r
2278 // お気に入り編集ダイアログで選択されたパーツを選択表示する.
\r
2279 selectPresetParts(partsSet);
\r
2282 public void updateFavorites(CharacterData characterData,
\r
2283 boolean savePreset) {
\r
2286 setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
\r
2288 CharacterDataPersistent persiste = CharacterDataPersistent
\r
2291 persiste.updateProfile(characterData);
\r
2294 persiste.saveFavorites(characterData);
\r
2296 // お気に入りが更新されたことを通知する.
\r
2297 FavoritesChangeObserver.getDefault()
\r
2298 .notifyFavoritesChange(MainFrame.this,
\r
2302 setCursor(Cursor.getDefaultCursor());
\r
2305 } catch (Exception ex) {
\r
2306 ErrorMessageHelper.showErrorDialog(MainFrame.this, ex);
\r
2310 WindowAdjustLocationSupport.alignRight(this, dlg, 0, true);
\r
2311 dlg.setVisible(true);
\r
2312 lastUseManageFavoritesDialog = dlg;
\r
2316 * 最新のお気に入りの状態を取り出す.<br>
\r
2318 protected void refreshFavorites() {
\r
2319 logger.log(Level.FINE, "refresh Favorites.: " + characterData);
\r
2321 setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
\r
2323 CharacterDataPersistent persiste = CharacterDataPersistent.getInstance();
\r
2324 characterData.clearPartsSets(true);
\r
2325 persiste.loadFavorites(characterData);
\r
2327 setCursor(Cursor.getDefaultCursor());
\r
2330 } catch (Exception ex) {
\r
2331 logger.log(Level.WARNING, "can't refresh favorites: " + characterData, ex);
\r
2335 protected void onRegisterFavorite() {
\r
2336 if (!characterData.isValid()) {
\r
2337 Toolkit tk = Toolkit.getDefaultToolkit();
\r
2343 PartsSet partsSet = partsSelectionManager.createPartsSet();
\r
2344 if (partsSet.isEmpty()) {
\r
2345 // 空のパーツセットは登録しない.
\r
2349 Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
\r
2350 .getLocalizedProperties(STRINGS_RESOURCE);
\r
2352 // お気に入りに登録するパーツセットが最後に使用したお気に入りと同じ構成であれば、
\r
2354 String initName = getSuggestPartsSetName(partsSet, false);
\r
2356 // カラー情報の有無のチェックボックス.
\r
2357 JCheckBox chkColorInfo = new JCheckBox(strings.getProperty("input.favoritesColorInfo"));
\r
2358 chkColorInfo.setSelected(true);
\r
2359 String partsSetId = null;
\r
2360 if (initName != null && lastUsePresetParts != null) {
\r
2361 partsSetId = lastUsePresetParts.getPartsSetId();
\r
2364 // 上書き保存の可否のチェックボックス
\r
2365 JCheckBox chkOverwrite = new JCheckBox(strings.getProperty("input.favoritesOverwrite"));
\r
2366 chkOverwrite.setSelected(partsSetId != null && partsSetId.length() > 0);
\r
2367 chkOverwrite.setEnabled(partsSetId != null && partsSetId.length() > 0);
\r
2370 Box checkboxsPanel = new Box(BoxLayout.PAGE_AXIS);
\r
2371 checkboxsPanel.add(chkColorInfo);
\r
2372 checkboxsPanel.add(chkOverwrite);
\r
2375 String name = (String) JOptionPane.showInputDialog(this,
\r
2377 strings.getProperty("input.favorites"),
\r
2378 JOptionPane.QUESTION_MESSAGE,
\r
2381 initName == null ? "" : initName);
\r
2382 if (name == null || name.trim().length() == 0) {
\r
2386 boolean includeColorInfo = chkColorInfo.isSelected();
\r
2387 if (!includeColorInfo) {
\r
2389 partsSet.removeColorInfo();
\r
2392 // 新規の場合、もしくは上書きしない場合はIDを設定する.
\r
2393 if (partsSetId == null || !chkOverwrite.isSelected()) {
\r
2394 partsSetId = "ps" + UUID.randomUUID().toString();
\r
2396 partsSet.setPartsSetId(partsSetId);
\r
2399 partsSet.setLocalizedName(name);
\r
2402 setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
\r
2404 CharacterDataPersistent persiste = CharacterDataPersistent.getInstance();
\r
2406 characterData.clearPartsSets(true);
\r
2407 persiste.loadFavorites(characterData);
\r
2410 characterData.addPartsSet(partsSet);
\r
2412 persiste.saveFavorites(characterData);
\r
2414 // お気に入りが更新されたことを通知する.
\r
2415 FavoritesChangeObserver.getDefault().notifyFavoritesChange(
\r
2416 MainFrame.this, characterData);
\r
2419 setCursor(Cursor.getDefaultCursor());
\r
2422 // 最後に選択したお気に入りにする
\r
2423 lastUsePresetParts = partsSet;
\r
2424 showPresetName(partsSet);
\r
2426 } catch (Exception ex) {
\r
2427 ErrorMessageHelper.showErrorDialog(this, ex);
\r
2432 * すべての解除可能なパーツの選択を解除する。
\r
2434 protected void onDeselectAll() {
\r
2435 partsSelectionManager.deselectAll();
\r
2439 * 単一選択カテゴリのパーツの解除を許可する。
\r
2441 protected void onDeselectableAllCategory() {
\r
2442 partsSelectionManager
\r
2443 .setDeselectableSingleCategory( !partsSelectionManager
\r
2444 .isDeselectableSingleCategory());
\r
2448 * プレビューのズームボックスの表示制御
\r
2450 protected void onEnableZoom() {
\r
2451 previewPane.setVisibleZoomBox( !previewPane.isVisibleZoomBox());
\r
2459 protected JMenuBar createMenuBar() {
\r
2460 final Properties strings = LocalizedResourcePropertyLoader
\r
2461 .getCachedInstance().getLocalizedProperties(STRINGS_RESOURCE);
\r
2463 MenuDataFactory[] menus = new MenuDataFactory[] {
\r
2464 new MenuDataFactory("menu.file", new MenuDataFactory[] {
\r
2465 new MenuDataFactory("file.openProfile", new ActionListener() {
\r
2466 public void actionPerformed(ActionEvent e) {
\r
2470 new MenuDataFactory("file.savePicture", new ActionListener() {
\r
2471 public void actionPerformed(ActionEvent e) {
\r
2475 new MenuDataFactory("file.ukagaka", new MenuDataFactory[] {
\r
2476 new MenuDataFactory("file.saveAsUkagaka", new ActionListener() {
\r
2477 public void actionPerformed(ActionEvent e) {
\r
2478 onSaveAsUkagaka();
\r
2481 new MenuDataFactory("file.convertUkagaka", new ActionListener() {
\r
2482 public void actionPerformed(ActionEvent e) {
\r
2483 onConvertUkagaka();
\r
2488 new MenuDataFactory("file.editprofile", new ActionListener() {
\r
2489 public void actionPerformed(ActionEvent e) {
\r
2493 new MenuDataFactory("file.opendir", new ActionListener() {
\r
2494 public void actionPerformed(ActionEvent e) {
\r
2495 onBrowseProfileDir();
\r
2498 new MenuDataFactory("file.import", new MenuDataFactory[] {
\r
2499 new MenuDataFactory("file.importMe", new ActionListener() {
\r
2500 public void actionPerformed(ActionEvent e) {
\r
2504 new MenuDataFactory("file.importNew", new ActionListener() {
\r
2505 public void actionPerformed(ActionEvent e) {
\r
2510 new MenuDataFactory("file.export", new ActionListener() {
\r
2511 public void actionPerformed(ActionEvent e) {
\r
2515 new MenuDataFactory("file.manageParts", new ActionListener() {
\r
2516 public void actionPerformed(ActionEvent e) {
\r
2520 new MenuDataFactory("file.preferences", new ActionListener() {
\r
2521 public void actionPerformed(ActionEvent e) {
\r
2526 new MenuDataFactory("file.closeProfile", new ActionListener() {
\r
2527 public void actionPerformed(ActionEvent e) {
\r
2532 new MenuDataFactory("menu.edit", new MenuDataFactory[] {
\r
2533 new MenuDataFactory("edit.search", new ActionListener() {
\r
2534 public void actionPerformed(ActionEvent e) {
\r
2535 openSearchDialog();
\r
2538 new MenuDataFactory("edit.copy", new ActionListener() {
\r
2539 public void actionPerformed(ActionEvent e) {
\r
2540 onCopy((e.getModifiers() & ActionEvent.SHIFT_MASK) != 0);
\r
2543 new MenuDataFactory("edit.flipHorizontal", new ActionListener() {
\r
2544 public void actionPerformed(ActionEvent e) {
\r
2545 onFlipHolizontal();
\r
2548 new MenuDataFactory("edit.resetcolor", new ActionListener() {
\r
2549 public void actionPerformed(ActionEvent e) {
\r
2554 new MenuDataFactory("edit.setDefaultPicture", new ActionListener() {
\r
2555 public void actionPerformed(ActionEvent e) {
\r
2556 onSetDefaultPicture();
\r
2559 new MenuDataFactory("edit.information", new ActionListener() {
\r
2560 public void actionPerformed(ActionEvent e) {
\r
2565 new MenuDataFactory("edit.deselectall", new ActionListener() {
\r
2566 public void actionPerformed(ActionEvent e) {
\r
2570 new MenuDataFactory("edit.deselectparts", true, new ActionListener() {
\r
2571 public void actionPerformed(ActionEvent e) {
\r
2572 onDeselectableAllCategory();
\r
2575 new MenuDataFactory("edit.enableAutoShrink", true, new ActionListener() {
\r
2576 public void actionPerformed(ActionEvent e) {
\r
2577 onClickPartsCategoryTitle(null, true);
\r
2581 new MenuDataFactory("edit.enableZoomBox", true, new ActionListener() {
\r
2582 public void actionPerformed(ActionEvent e) {
\r
2587 new MenuDataFactory("edit.changeBgColor", new ActionListener() {
\r
2588 public void actionPerformed(ActionEvent e) {
\r
2589 onChangeBgColor();
\r
2592 new MenuDataFactory("edit.changeWallpaper", new ActionListener() {
\r
2593 public void actionPerformed(ActionEvent e) {
\r
2594 onChangeWallpaper();
\r
2599 new MenuDataFactory("menu.favorite", new MenuDataFactory[] {
\r
2600 new MenuDataFactory("favorite.register", new ActionListener() {
\r
2601 public void actionPerformed(ActionEvent e) {
\r
2602 onRegisterFavorite();
\r
2605 new MenuDataFactory("favorite.manage", new ActionListener() {
\r
2606 public void actionPerformed(ActionEvent e) {
\r
2607 onManageFavorites();
\r
2612 new MenuDataFactory("menu.help", new MenuDataFactory[] {
\r
2613 new MenuDataFactory("help.recommendations", (ActionListener) null),
\r
2615 new MenuDataFactory("help.help", new ActionListener() {
\r
2616 public void actionPerformed(ActionEvent e) {
\r
2620 new MenuDataFactory("help.forum",
\r
2621 DesktopUtilities.createBrowseAction(
\r
2623 strings.getProperty("help.forum.url"),
\r
2624 strings.getProperty("help.forum.description"))
\r
2626 new MenuDataFactory("help.bugreport",
\r
2627 DesktopUtilities.createBrowseAction(
\r
2629 strings.getProperty("help.reportbugs.url"),
\r
2630 strings.getProperty("help.reportbugs.description"))
\r
2632 new MenuDataFactory("help.about", new ActionListener() {
\r
2633 public void actionPerformed(ActionEvent e) {
\r
2639 final MenuBuilder menuBuilder = new MenuBuilder();
\r
2641 JMenuBar menuBar = menuBuilder.createMenuBar(menus);
\r
2643 menuBuilder.getJMenu("menu.edit").addMenuListener(new MenuListener() {
\r
2644 public void menuCanceled(MenuEvent e) {
\r
2647 public void menuDeselected(MenuEvent e) {
\r
2650 public void menuSelected(MenuEvent e) {
\r
2651 menuBuilder.getJMenuItem("edit.copy").setEnabled(previewPane.getPreviewImage() != null);
\r
2652 menuBuilder.getJMenuItem("edit.deselectparts").setSelected(
\r
2653 partsSelectionManager.isDeselectableSingleCategory());
\r
2654 menuBuilder.getJMenuItem("edit.enableAutoShrink").setSelected(minimizeMode);
\r
2655 menuBuilder.getJMenuItem("edit.enableZoomBox").setSelected(previewPane.isVisibleZoomBox());
\r
2658 final JMenu mnuFavorites = menuBuilder.getJMenu("menu.favorite");
\r
2659 mnuFavorites.addMenuListener(new MenuListener() {
\r
2660 public void menuCanceled(MenuEvent e) {
\r
2663 public void menuDeselected(MenuEvent e) {
\r
2666 public void menuSelected(MenuEvent e) {
\r
2667 onSelectedFavoriteMenu(mnuFavorites);
\r
2671 // J2SE5の場合は「パーツディレクトリを開く」コマンドは使用不可とする.
\r
2672 if (System.getProperty("java.version").startsWith("1.5")) {
\r
2673 menuBuilder.getJMenuItem("file.opendir").setEnabled(false);
\r
2677 final JMenu mnuRecomendation = menuBuilder.getJMenu("help.recommendations");
\r
2678 JMenu mnuHelp = menuBuilder.getJMenu("menu.help");
\r
2679 mnuHelp.addMenuListener(new MenuListener() {
\r
2680 public void menuCanceled(MenuEvent e) {
\r
2683 public void menuDeselected(MenuEvent e) {
\r
2686 public void menuSelected(MenuEvent e) {
\r
2687 onSelectedRecommendationMenu(mnuRecomendation);
\r