OSDN Git Service

機能追加: EXIF日時を基準にする/ !(ファイル更新日時を基準にする)
[importpicture/importpicture.git] / importPicture / src / osm / jp / gpx / matchtime / gui / AdjustTime.java
1 package osm.jp.gpx.matchtime.gui;
2 import java.awt.*;
3 import java.awt.event.ActionEvent;
4 import java.io.File;
5 import java.io.FileNotFoundException;
6 import java.io.IOException;
7 import java.text.SimpleDateFormat;
8 import java.util.Date;
9
10 import javax.swing.*;
11
12 import osm.jp.gpx.*;
13 import org.apache.commons.imaging.ImageReadException;
14 import org.apache.commons.imaging.ImageWriteException;
15 import org.apache.commons.imaging.Imaging;
16 import org.apache.commons.imaging.common.ImageMetadata;
17 import org.apache.commons.imaging.formats.jpeg.JpegImageMetadata;
18 import org.apache.commons.imaging.formats.tiff.TiffImageMetadata;
19 import org.apache.commons.imaging.formats.tiff.constants.ExifTagConstants;
20
21 /**
22  * 本プログラムのメインクラス
23  */
24 @SuppressWarnings("serial")
25 public class AdjustTime extends JFrame
26 {
27     public static final String PROGRAM_NAME = "AdjustTime for JOSM";
28     public static final String PROGRAM_VARSION = "3.0";
29     public static final String PROGRAM_UPDATE = "2015/07/26";
30         
31     // Used for addNotify check.
32     boolean fComponentsAdjusted = false;
33     
34     //{{DECLARE_CONTROLS
35     JPanel cardPanel;       // ウィザード形式パネル(カード型)
36     JPanel[] cards;
37     int cardPanelNo = 0;
38     JPanel argsPanel;           // パラメータ設定パネル       (上部)
39     JScrollPane imageSPane;     // スクロールパネル
40     JLabel imageLabel;          // 基準時刻画像表示
41     ButtonGroup baseTimeGroup;  // 基準時刻の指定グループ
42     JRadioButton exifBase;              // EXIF日時を基準にする/ !(ファイル更新日時を基準にする)
43     JRadioButton fupdateBase;   // File更新日時を基準にする/ !(XIF日時を基準にする)
44     JCheckBox noFirstNode;      // GPX: <trkseg>セグメントの最初の1ノードは無視する。 {ON | OFF}
45     JCheckBox gpxReuse;         // 生成されたGPXファイル(ファイル名が'_.gpx'で終わるもの)も対象にする。 {ON | OFF}
46     JTextArea textArea;         // 実行結果表示領域
47     //ImagePreview imagePane;   // 基準時刻画像表示
48     ParameterPanelFolder arg1_srcFolder;
49     ParameterPanelImageFile arg2_baseTimeImg;
50     ParameterPanel arg3_basetiome;
51     JCheckBox exifON;           // EXIF 書き出しモード / !(EXIFの書き換えはしない)
52     ParameterPanelFolder arg5_outputFolder;       // EXIF 書き出しフォルダ
53     ParameterPanelFolder arg4_gpxFolder;       // GPXファイル・フォルダ
54     
55     JPanel buttonPanel;         // ボタンパネル   (下部)
56     JButton openButton;         // [Fit]ボタン
57     JButton zoomInButton;       // [Zoom in]ボタン
58     JButton zoomOutButton;      // [Zoom out]ボタン
59     JButton nextButton;     // [次へ]ボタン
60     JButton backButton;     // [戻る]ボタン
61     JButton doButton;       // [処理実行]ボタン
62     //}}
63
64     //{{DECLARE_MENUS
65     java.awt.MenuBar mainMenuBar;
66     java.awt.Menu menu1;
67     java.awt.MenuItem miDoNewFileList;
68     java.awt.MenuItem miDoDirSize;
69     java.awt.MenuItem miDoReadXML;
70     java.awt.MenuItem miExit;
71     java.awt.Menu menu3;
72     java.awt.MenuItem miAbout;
73     //}}
74
75     class SymWindow extends java.awt.event.WindowAdapter {
76         /**
77          * このFrameが閉じられるときの動作。
78          * このパネルが閉じられたら、このアプリケーションも終了させる。
79          */
80         @Override
81         public void windowClosing(java.awt.event.WindowEvent event) {
82             Object object = event.getSource();
83             if (object == AdjustTime.this) {
84                 DbMang_WindowClosing(event);
85             }
86         }
87     }
88
89     class SymAction implements java.awt.event.ActionListener {
90         @Override
91         public void actionPerformed(java.awt.event.ActionEvent event) {
92             Object object = event.getSource();
93             if (object == miAbout) {
94                 miAbout_Action(event);
95             }
96             else if (object == miExit) {
97                 miExit_Action(event);
98             }
99             else if (object == openButton) {
100                 imageView_Action(event);
101             }
102             else if (object == zoomInButton) {
103                 zoomin_Action(event);
104             }
105             else if (object == zoomOutButton) {
106                 zoomout_Action(event);
107             }
108             else if (object == arg2_baseTimeImg.argField) {
109                 imageView_Action(event);
110             }
111             else if (object == arg2_baseTimeImg.openButton) {
112                 selectImage_Action(event);
113                 imageView_Action(event);
114             }
115             else if (object == exifON) {
116                 exifON_Action(event);
117             }
118             else if (object == doButton) {
119                 doButton_Action(event);
120             }
121             else if (object == nextButton) {
122                 nextButton_Action(event);
123             }
124             else if (object == backButton) {
125                 backButton_Action(event);
126             }
127         }
128     }
129     
130     AppParameters params;
131
132         /**
133          * データベース内のテーブルを一覧で表示するFrame
134          * @throws IOException 
135          */
136     public AdjustTime() throws IOException
137     {
138         // INIT_CONTROLS
139         Container container = getContentPane();
140         container.setLayout(new BorderLayout());
141         setSize(getInsets().left + getInsets().right + 960,getInsets().top + getInsets().bottom + 480);
142         setTitle(AdjustTime.PROGRAM_NAME +" v"+ AdjustTime.PROGRAM_VARSION);
143         
144         params = new AppParameters();
145         
146         //---------------------------------------------------------------------
147         cardPanel = new JPanel();
148         cardPanel.setLayout(new CardLayout());
149         container.add(cardPanel, BorderLayout.CENTER);
150         
151         nextButton = new JButton("次へ");
152         container.add(nextButton, BorderLayout.EAST);
153
154         backButton = new JButton("戻る");
155         container.add(backButton, BorderLayout.WEST);
156
157         cards = new JPanel[5];
158         for (int i=0; i < 5; i++) {
159             cards[i] = new JPanel();
160             cardPanel.add(cards[i], String.valueOf(i));
161         }
162         cardPanelNo = 0;
163
164         //---------------------------------------------------------------------
165         // 1.[対象フォルダ]設定パネル
166         int cardNo = 0;
167         cards[cardNo].setLayout(new BorderLayout());
168         JLabel label1 = new JLabel();
169         label1.setText("<html><p>位置情報を付加したい画像ファイルが格納されているフォルダを選択してください。</p><ul><li>コピー動作を行うと、ファイル更新時刻がコピーを実行した時刻に書き換わってしまうことがあります。オリジナルのカメラUSB内のフォルダを直接指定することをおすすめします。</li></ul>");
170         cards[cardNo].add(label1, BorderLayout.NORTH);
171         
172         argsPanel = new JPanel();
173         argsPanel.setLayout(new BoxLayout(argsPanel, BoxLayout.Y_AXIS));
174         arg1_srcFolder = new ParameterPanelFolder("対象フォルダ: ", params.getProperty(AppParameters.IMG_SOURCE_FOLDER));
175         argsPanel.add(arg1_srcFolder);
176         cards[cardNo].add(argsPanel, BorderLayout.CENTER);
177         
178         //---------------------------------------------------------------------
179         // 2.[基準時刻画像]設定パネル
180         cardNo++;
181         cards[cardNo].setLayout(new BorderLayout());
182         JLabel label2 = new JLabel();
183         label2.setText("<html><p>正確な撮影時刻が判明できる画像を選んでください。</p><ul><li>スマートフォンの時計画面(秒が判別できること)を撮影した画像</li><li>カメラの時計が正確ならば、どの画像を選んでも構いません。</li></ul>");
184         cards[cardNo].add(label2, BorderLayout.NORTH);
185
186         argsPanel = new JPanel();
187         argsPanel.setLayout(new BoxLayout(argsPanel, BoxLayout.Y_AXIS));
188         arg2_baseTimeImg = new ParameterPanelImageFile("基準時刻画像: ", params.getProperty(AppParameters.IMG_BASE_FILE), arg1_srcFolder);
189         argsPanel.add(arg2_baseTimeImg);
190         
191         baseTimeGroup = new ButtonGroup();
192         exifBase = new JRadioButton("EXIFの日時を基準にする");
193         fupdateBase = new JRadioButton("File更新日時を基準にする");
194         baseTimeGroup.add(exifBase);
195         baseTimeGroup.add(fupdateBase);
196         argsPanel.add(exifBase);
197         argsPanel.add(fupdateBase);
198         if (params.getProperty(AppParameters.GPX_BASETIME).equals("EXIF_TIME")) {
199                 fupdateBase.setSelected(false);
200                 exifBase.setSelected(true);
201         }
202         else {
203                 fupdateBase.setSelected(true);
204                 exifBase.setSelected(false);
205         }
206         cards[cardNo].add(argsPanel, BorderLayout.CENTER);
207         
208         //---------------------------------------------------------------------
209         // 3.基準時刻の入力画面
210         cardNo++;
211         cards[cardNo].setLayout(new BorderLayout());
212         argsPanel = new JPanel();
213         argsPanel.setLayout(new GridLayout(2, 1));
214
215         JLabel label3 = new JLabel();
216         label3.setText("<html><p>正確な撮影時刻を入力してください。</p><ul><li>カメラの時計が正確ならば、設定を変更する必要はありません。</li></ul>");
217         argsPanel.add(label3);
218         arg3_basetiome = new ParameterPanel("  基準時刻: ", ImportPicture.TIME_FORMAT_STRING);
219         argsPanel.add(arg3_basetiome);
220         cards[cardNo].add(argsPanel, BorderLayout.NORTH);
221
222         // 参考画像
223         imageLabel = new JLabel();
224         imageSPane = new JScrollPane(imageLabel);
225         cards[cardNo].add(imageSPane, BorderLayout.CENTER);
226               
227         // 画像ファイル選択ダイアログを起動するボタン
228         buttonPanel = new JPanel();
229         buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));
230         openButton = new JButton(createImageIcon("images/Fit16.gif"));
231         buttonPanel.add(openButton);
232         zoomInButton = new JButton(createImageIcon("images/ZoomIn16.gif"));
233         buttonPanel.add(zoomInButton);
234         zoomOutButton = new JButton(createImageIcon("images/ZoomOut16.gif"));
235         buttonPanel.add(zoomOutButton);
236         cards[cardNo].add(buttonPanel, BorderLayout.SOUTH);
237         
238         //---------------------------------------------------------------------
239         // 4.GPXファイル設定画面
240         cardNo++;
241         cards[cardNo].setLayout(new BorderLayout());
242         JLabel label4 = new JLabel();
243         label4.setText("<html><p>ヒモ付を行うGPXファイルを選択してください。</p><ul><li>フォルダを指定すると、フォルダ内にあるすべてのGPXファイルを対象とします。</li><li>カメラの時計が正確ならば、どの画像を選んでも構いません。</li></ul>");
244         cards[cardNo].add(label4, BorderLayout.NORTH);
245         
246         JPanel tmpPanel4a = new JPanel();
247         tmpPanel4a.setLayout(new BoxLayout(tmpPanel4a, BoxLayout.Y_AXIS));
248         File gpxDir = new File(".");
249         arg4_gpxFolder = new ParameterPanelFolder("GPXフォルダ: ", gpxDir.getAbsolutePath(), JFileChooser.FILES_AND_DIRECTORIES);
250         tmpPanel4a.add(arg4_gpxFolder);
251         noFirstNode = new JCheckBox("<trkseg>セグメントの最初の1ノードは無視する。", params.getProperty(AppParameters.GPX_NO_FIRST_NODE).equals("ON"));
252         tmpPanel4a.add(noFirstNode);
253         gpxReuse = new JCheckBox("生成されたGPXファイル(ファイル名が'_.gpx'で終わるもの)も対象にする。", params.getProperty(AppParameters.GPX_REUSE).equals("ON"));
254         gpxReuse.setEnabled(true);
255         tmpPanel4a.add(gpxReuse);
256         cards[cardNo].add(tmpPanel4a, BorderLayout.CENTER);
257         
258         //---------------------------------------------------------------------
259         // 5.EXIF更新設定画面
260         cardNo++;
261         cards[cardNo].setLayout(new BorderLayout());
262         JLabel label5 = new JLabel();
263         label5.setText("<html><p>EXIF変換を行うかどうかを選択してください。</p><ul><li>EXIF変換を行う場合には、変換ファイルを出力するフォルダも指定する必要があります。</li><li>出力フォルダには、書き込み権限と、十分な空き容量が必要です。</li></ul>");
264         cards[cardNo].add(label5, BorderLayout.NORTH);
265         
266         JPanel tmpPanel5 = new JPanel();
267         tmpPanel5.setLayout(new BoxLayout(tmpPanel5, BoxLayout.Y_AXIS));
268
269         exifON = new JCheckBox("EXIFの変換をする", false);
270         tmpPanel5.add(exifON);
271         
272         File outputDir = new File(".");
273         arg5_outputFolder = new ParameterPanelFolder("書き出しフォルダ: ", outputDir.getAbsolutePath());
274         tmpPanel5.add(arg5_outputFolder);
275         cards[cardNo].add(tmpPanel5, BorderLayout.CENTER);
276         
277         doButton = new JButton("処理実行", AdjustTime.createImageIcon("images/media_playback_start.png"));
278         cards[cardNo].add(doButton, BorderLayout.SOUTH);
279
280
281         //---------------------------------------------------------------------
282         // INIT_MENUS
283         menu1 = new java.awt.Menu("File");
284         miExit = new java.awt.MenuItem(QuitDialog.TITLE);
285         miExit.setFont(new Font("Dialog", Font.PLAIN, 12));
286         menu1.add(miExit);
287
288         miAbout = new java.awt.MenuItem("About...");
289         miAbout.setFont(new Font("Dialog", Font.PLAIN, 12));
290
291         menu3 = new java.awt.Menu("Help");
292         menu3.setFont(new Font("Dialog", Font.PLAIN, 12));
293         menu3.add(miAbout);
294
295         mainMenuBar = new java.awt.MenuBar();
296         mainMenuBar.setHelpMenu(menu3);
297         mainMenuBar.add(menu1);
298         mainMenuBar.add(menu3);
299         setMenuBar(mainMenuBar);
300         
301
302         //{{REGISTER_LISTENERS
303         SymWindow aSymWindow = new SymWindow();
304         this.addWindowListener(aSymWindow);
305         SymAction lSymAction = new SymAction();
306         miAbout.addActionListener(lSymAction);
307         miExit.addActionListener(lSymAction);
308         openButton.addActionListener(lSymAction);
309         zoomOutButton.addActionListener(lSymAction);
310         zoomInButton.addActionListener(lSymAction);
311         arg2_baseTimeImg.argField.addActionListener(lSymAction);
312         arg2_baseTimeImg.openButton.addActionListener(lSymAction);
313         doButton.addActionListener(lSymAction);
314         exifON.addActionListener(lSymAction);
315         noFirstNode.addActionListener(lSymAction);
316         gpxReuse.addActionListener(lSymAction);
317         //arg1Panel_4.field.addActionListener(lSymAction);
318         nextButton.addActionListener(lSymAction);
319         backButton.addActionListener(lSymAction);
320         //}}
321     }
322     
323     /**
324      * Shows or hides the component depending on the boolean flag b.
325      * @param b trueのときコンポーネントを表示; その他のとき, componentを隠す.
326      * @see java.awt.Component#isVisible
327      */
328     @Override
329     public void setVisible(boolean b) {
330         if(b) {
331             setLocation(50, 50);
332         }
333         super.setVisible(b);
334     }
335     
336     /**
337      * このクラスをインスタンスを生成して表示する。
338      * コマンドラインの引数はありません。
339      * @param args
340      */    
341     static public void main(String args[]) {
342         SwingUtilities.invokeLater(() -> {
343             try {
344                                 createAndShowGUI();
345                         } catch (Exception e) {
346                                 e.printStackTrace();
347                         }
348         });
349     }
350     private static void createAndShowGUI() throws IOException {
351         (new AdjustTime()).setVisible(true);
352     }
353
354     @Override
355     public void addNotify()     {
356         // Record the size of the window prior to calling parents addNotify.
357         Dimension d = getSize();
358
359         super.addNotify();
360
361         if (fComponentsAdjusted)
362             return;
363
364         // Adjust components according to the insets
365         setSize(getInsets().left + getInsets().right + d.width, getInsets().top + getInsets().bottom + d.height);
366         Component components[] = getComponents();
367         for (Component component : components) {
368             Point p = component.getLocation();
369             p.translate(getInsets().left, getInsets().top);
370             component.setLocation(p);
371         }
372         fComponentsAdjusted = true;
373     }
374
375     void DbMang_WindowClosing(java.awt.event.WindowEvent event) {
376         setVisible(false);  // hide the Manager
377         dispose();                      // free the system resources
378         System.exit(0);         // close the application
379     }
380
381     void miAbout_Action(java.awt.event.ActionEvent event) {
382         // Action from About Create and show as modal
383         (new AboutDialog(this, true)).setVisible(true);
384     }
385     
386     void miExit_Action(java.awt.event.ActionEvent event) {
387         // Action from Exit Create and show as modal
388         //(new hayashi.yuu.tools.gui.QuitDialog(this, true)).setVisible(true);
389         (new QuitDialog(this, true)).setVisible(true);
390     }
391
392     ImageIcon refImage;
393     
394     /**
395      * 選択された画像ファイルを表示する
396      * 基準画像ボタンがクリックされた時に、基準時刻フィールドに基準画像の作成日時を設定する。
397      * @param ev
398      * @throws ImageReadException 
399      * @throws ImageWriteException 
400      */
401     public void imageView_Action(ActionEvent ev) {
402         String path = (new File(arg1_srcFolder.getText(), arg2_baseTimeImg.getText())).getPath();
403         
404         File timeFile = new File(path);
405         long lastModifyTime = timeFile.lastModified();
406         arg3_basetiome.argField.setText(ImportPicture.dfjp.format(new Date(lastModifyTime)));
407
408         if (exifBase.isSelected()) {
409                 try {
410                 ImageMetadata meta = Imaging.getMetadata(timeFile);
411                 arg3_basetiome.argField.setText("imageView_Action : 412");
412
413                 JpegImageMetadata jpegMetadata = (JpegImageMetadata)meta;
414                 if (jpegMetadata != null) {
415                     TiffImageMetadata exif = jpegMetadata.getExif();
416                     if (exif != null) {
417                         String dateTimeOriginal = exif.getFieldValue(ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL)[0];
418                         lastModifyTime = (new SimpleDateFormat("yyyy:MM:dd HH:mm:ss")).parse(dateTimeOriginal).getTime();
419                         arg3_basetiome.argField.setText(ImportPicture.dfjp.format(new Date(lastModifyTime)));
420                     }
421                     else {
422                         arg3_basetiome.argField.setText("exif == null");
423                     }
424                 }
425                 }
426                 catch (Exception e) {}
427         }
428         
429         int size_x = imageSPane.getWidth() - 8;
430         ImageIcon tmpIcon = new ImageIcon(path);
431         refImage = tmpIcon;
432         if (tmpIcon.getIconWidth() > size_x) {
433             refImage = new ImageIcon(tmpIcon.getImage().getScaledInstance(size_x, -1, Image.SCALE_DEFAULT));
434         }
435         imageLabel.setIcon(refImage);
436         repaint();
437     }
438
439     public void zoomin_Action(ActionEvent ev) {
440         if (refImage != null) {
441                 int size_x = imageLabel.getWidth();
442                 String path = (new File(arg1_srcFolder.getText(), arg2_baseTimeImg.getText())).getPath();
443                 ImageIcon tmpIcon = new ImageIcon(path);
444                 refImage = new ImageIcon(tmpIcon.getImage().getScaledInstance(size_x * 2, -1, Image.SCALE_DEFAULT));
445                 imageLabel.setIcon(refImage);
446             repaint();
447         }
448     }
449
450     public void zoomout_Action(ActionEvent ev) {
451         if (refImage != null) {
452                 int size_x = imageLabel.getWidth();
453                 ImageIcon tmpIcon = refImage;
454                 refImage = new ImageIcon(tmpIcon.getImage().getScaledInstance(size_x / 2, -1, Image.SCALE_DEFAULT));
455                 imageLabel.setIcon(refImage);
456             repaint();
457         }
458     }
459     
460     /**
461      * ソースディレクトリが選択された時のアクション
462      * → AdjustTime.ini に書き込む
463      * @param ev
464      */
465     public void selectSource_Action(ActionEvent ev) {
466     }
467     
468     public void selectImage_Action(ActionEvent ev) {
469         JFileChooser fc;
470
471         //Set up the file chooser.
472                 File sdir = new File(arg1_srcFolder.getText());
473                 System.out.println(sdir.getPath());
474                 if (sdir.isDirectory()) {
475                 fc = new JFileChooser(sdir);
476                 }
477                 else {
478                 fc = new JFileChooser();
479                 }
480
481             //Add a custom file filter and disable the default
482             //(Accept All) file filter.
483         fc.addChoosableFileFilter(new ImageFilter());
484         fc.setAcceptAllFileFilterUsed(false);
485
486         //Add custom icons for file types.
487         fc.setFileView(new ImageFileView());
488
489         //Add the preview pane.
490         fc.setAccessory(new ImagePreview(fc));
491
492         //Show it.
493         int returnVal = fc.showDialog(this, "選択");
494
495         //Process the results.
496         if (returnVal == JFileChooser.APPROVE_OPTION) {
497             File file = fc.getSelectedFile();
498             arg2_baseTimeImg.argField.setText(file.getName());
499         }
500
501         //Reset the file chooser for the next time it's shown.
502         fc.setSelectedFile(null);
503     }
504     
505     /** Returns an ImageIcon, or null if the path was invalid.
506      * @param path
507      * @return  */
508     public static ImageIcon createImageIcon(String path) {
509         java.net.URL imgURL = AdjustTime.class.getResource(path);
510         if (imgURL != null) {
511             return new ImageIcon(imgURL);
512         } else {
513             System.err.println("Couldn't find file: " + path);
514             return null;
515         }
516     }
517     
518     /**
519      * checkbox[EXIF書き出し]を変更した場合のアクション
520      *  ON ー> EXIF変換先フォルダのフィールドを有効にする
521      *  OFF -> EXIF変換先フォルダのフィールドを無効にする
522      * @param event
523      */
524     void exifON_Action (ActionEvent event) {
525                 arg5_outputFolder.setEnabled(exifON.isEnabled());
526     }
527
528     /**
529      * [実行]ボタンをクリックしたときの動作
530      * @param event
531      * @throws IOException 
532      * @throws FileNotFoundException 
533      */
534     void doButton_Action(java.awt.event.ActionEvent event) {
535         doButton.setEnabled(false);
536
537         String[] argv = new String[5];
538         argv[0] = arg1_srcFolder.getText();
539         argv[1] = arg2_baseTimeImg.getText();
540         argv[2] = arg3_basetiome.getText();
541         argv[3] = (exifON.isSelected() ? arg5_outputFolder.getText() : "noEXIF");
542         argv[4] = arg4_gpxFolder.getText();
543         
544         this.params.setProperty(AppParameters.GPX_NO_FIRST_NODE, (noFirstNode.isSelected() ? "ON" : "OFF"));
545         this.params.setProperty(AppParameters.GPX_REUSE, (gpxReuse.isSelected() ? "ON" : "OFF"));
546         this.params.setProperty(AppParameters.IMG_SOURCE_FOLDER, arg1_srcFolder.getText());
547         this.params.setProperty(AppParameters.IMG_BASE_FILE, arg2_baseTimeImg.getText());
548         this.params.setProperty(AppParameters.GPX_BASETIME, arg3_basetiome.getText());
549         this.params.setProperty(AppParameters.IMG_OUTPUT_FOLDER, arg4_gpxFolder.getText());
550         try {
551             this.params.store();
552         }
553         catch(Exception e) {}
554
555         (new DoDialog(this, argv)).setVisible(true);
556                 
557         doButton.setEnabled(true);
558     }
559     
560     /**
561      * [次へ]ボタンをクリックした時の動作
562      * @param event 
563      */
564     void nextButton_Action(ActionEvent event) {
565         if ((cardPanelNo + 1) < cards.length) {
566             cardPanelNo++;
567             CardLayout cl = (CardLayout)(cardPanel.getLayout());
568             cl.show(cardPanel, String.valueOf(cardPanelNo));
569         }
570     }
571
572     /**
573      * [戻る]ボタンをクリックした時の動作
574      * @param event
575      */
576     void backButton_Action(ActionEvent event) {
577         if ((cardPanelNo - 1) >= 0) {
578             cardPanelNo--;
579             CardLayout cl = (CardLayout)(cardPanel.getLayout());
580             cl.show(cardPanel, String.valueOf(cardPanelNo));
581         }
582     }
583 }