diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..02b1de6 --- /dev/null +++ b/pom.xml @@ -0,0 +1,80 @@ + + + 4.0.0 + osm.surveyor + ReStamp + 3.0-SNAPSHOT + jar + + + + hayashi_repository + Hayashi Repository + http://surveyor.mydns.jp/archiva/repository/hayashi_repository/ + + true + + + false + + + + + + + org.hamcrest + hamcrest-core + 1.3 + test + jar + + + junit + junit + 4.12 + test + jar + + + org.apache.commons + commons-imaging + 1.0-alpha1 + jar + + + + UTF-8 + 11 + 11 + + + + + + + maven-assembly-plugin + 3.2.0 + + + + jar-with-dependencies + + + + osm.jp.gpx.matchtime.gui.AdjustTime + + + + + + make-assembly + package + + single + + + + + + + \ No newline at end of file diff --git a/src/main/java/osm/surveyor/matchtime/AppParameters.java b/src/main/java/osm/surveyor/matchtime/AppParameters.java new file mode 100644 index 0000000..f6e619c --- /dev/null +++ b/src/main/java/osm/surveyor/matchtime/AppParameters.java @@ -0,0 +1,216 @@ +package osm.surveyor.matchtime; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Properties; + +@SuppressWarnings("serial") +public class AppParameters extends Properties { + static final String FILE_PATH = "AdjustTime.ini"; + + // GPX: 時間的に間隔が開いたGPXログを別のセグメントに分割する。 {ON | OFF} + public static String GPX_GPXSPLIT = "GPX.gpxSplit"; + + // GPX: セグメントの最初の1ノードは無視する。 {ON | OFF} + public static String GPX_NO_FIRST_NODE = "GPX.noFirstNode"; + + // GPX: 生成されたGPXファイル(ファイル名が'_.gpx'で終わるもの)も対象にする。 {ON | OFF} + public static String GPX_REUSE = "GPX.REUSE"; + + // GPX: 基準時刻 {FILE_UPDATE | EXIF_TIME} + public static String GPX_BASETIME = "GPX.BASETIME"; + + // GPX: ファイル更新時刻 yyyy:MM:dd HH:mm:ss + public static String IMG_TIME = "IMG.TIME"; + + // 対象IMGフォルダ:(位置情報を付加したい画像ファイルが格納されているフォルダ) + public static String IMG_SOURCE_FOLDER = "IMG.SOURCE_FOLDER"; + + // 基準時刻画像(正確な撮影時刻が判明できる画像) + public static String IMG_BASE_FILE = "IMG.BASE_FILE"; + + // 対象GPXフォルダ:(GPXファイルが格納されているフォルダ) + public static String GPX_SOURCE_FOLDER = "GPX.SOURCE_FOLDER"; + + // 出力フォルダ:(変換した画像ファイルとGPXファイルを出力するフォルダ) + public static String IMG_OUTPUT_FOLDER = "IMG.OUTPUT_FOLDER"; + + // 出力IMG: IMG出力をする {ON | OFF} + public static String IMG_OUTPUT = "IMG.OUTPUT"; + + // 出力IMG: 'out of time'も IMG出力の対象とする {ON | OFF} + //   この場合は、対象IMGフォルダ内のすべてのIMGファイルが出力フォルダに出力される + public static String IMG_OUTPUT_ALL = "IMG.OUTPUT_ALL"; + + // 出力IMG: EXIFを変換する + public static String IMG_OUTPUT_EXIF = "IMG.OUTPUT_EXIF"; + + // 出力GPX: を上書き出力する {ON | OFF} + public static String GPX_OUTPUT_SPEED = "GPX.OUTPUT_SPEED"; + + // 出力GPX: ソースGPXのを無視する {ON | OFF} + public static String GPX_OVERWRITE_MAGVAR = "GPX.OVERWRITE_MAGVAR"; + + // 出力GPX: マーカーを出力する {ON | OFF} + public static String GPX_OUTPUT_WPT = "GPX.OUTPUT_WPT"; + + File file; + + public AppParameters() throws FileNotFoundException, IOException { + super(); + this.file = new File(FILE_PATH); + syncFile(); + } + + public AppParameters(Properties defaults) throws FileNotFoundException, IOException { + super(defaults); + this.file = new File(FILE_PATH); + syncFile(); + } + + public AppParameters(String iniFileName) throws FileNotFoundException, IOException { + super(); + this.file = new File(iniFileName); + syncFile(); + } + + private void syncFile() throws FileNotFoundException, IOException { + boolean update = false; + + if (this.file.exists()) { + // ファイルが存在すれば、その内容をロードする。 + this.load(new FileInputStream(file)); + } + else { + update = true; + } + + //------------------------------------------------ + // 対象フォルダ:(位置情報を付加したい画像ファイルが格納されているフォルダ) + String valueStr = this.getProperty(IMG_SOURCE_FOLDER); + if (valueStr == null) { + update = true; + this.setProperty(IMG_SOURCE_FOLDER, (new File(".")).getAbsolutePath()); + } + + //------------------------------------------------ + // 対象フォルダ:(GPXファイルが格納されているフォルダ) + valueStr = this.getProperty(GPX_SOURCE_FOLDER); + if (valueStr == null) { + update = true; + this.setProperty(GPX_SOURCE_FOLDER, (new File(".")).getAbsolutePath()); + } + + //------------------------------------------------ + // 基準時刻画像(正確な撮影時刻が判明できる画像) + valueStr = this.getProperty(IMG_BASE_FILE); + if (valueStr == null) { + update = true; + this.setProperty(IMG_BASE_FILE, ""); + } + + //------------------------------------------------ + // 出力フォルダ:(変換した画像ファイルとGPXファイルを出力するフォルダ) + valueStr = this.getProperty(IMG_OUTPUT_FOLDER); + if (valueStr == null) { + update = true; + this.setProperty(IMG_OUTPUT_FOLDER, (new File(".")).getAbsolutePath()); + } + + //------------------------------------------------ + // IMG出力: IMGを出力する + valueStr = this.getProperty(IMG_OUTPUT); + if (valueStr == null) { + update = true; + valueStr = String.valueOf(true); + } + this.setProperty(IMG_OUTPUT, String.valueOf(valueStr)); + + //------------------------------------------------ + // 出力IMG: 'out of time'も IMG出力の対象とする + valueStr = this.getProperty(IMG_OUTPUT_ALL); + if (valueStr == null) { + update = true; + valueStr = String.valueOf(false); + } + this.setProperty(IMG_OUTPUT_ALL, String.valueOf(valueStr)); + + //------------------------------------------------ + // IMG出力: EXIFを変換する + valueStr = this.getProperty(IMG_OUTPUT_EXIF); + if (valueStr == null) { + update = true; + valueStr = String.valueOf(true); + } + this.setProperty(IMG_OUTPUT_EXIF, String.valueOf(valueStr)); + + //------------------------------------------------ + // GPX出力: 時間的に間隔が開いたGPXログを別のセグメントに分割する。 {ON | OFF} + valueStr = this.getProperty(GPX_GPXSPLIT); + if (valueStr == null) { + update = true; + this.setProperty(GPX_GPXSPLIT, String.valueOf(true)); + } + + //------------------------------------------------ + // GPX出力: セグメントの最初の1ノードは無視する。 {ON | OFF} + valueStr = this.getProperty(GPX_NO_FIRST_NODE); + if (valueStr == null) { + update = true; + this.setProperty(GPX_NO_FIRST_NODE, String.valueOf(true)); + } + + //------------------------------------------------ + // GPX出力: ポイントマーカーを出力する {ON | OFF} + valueStr = this.getProperty(GPX_OUTPUT_WPT); + if (valueStr == null) { + update = true; + this.setProperty(GPX_OUTPUT_WPT, String.valueOf(false)); + } + + //------------------------------------------------ + // GPX出力: ソースGPXのを無視する {ON | OFF} + valueStr = this.getProperty(GPX_OVERWRITE_MAGVAR); + if (valueStr == null) { + update = true; + this.setProperty(GPX_OVERWRITE_MAGVAR, String.valueOf(false)); + } + + //------------------------------------------------ + // GPX出力: を上書き出力する {ON | OFF} + valueStr = this.getProperty(GPX_OUTPUT_SPEED); + if (valueStr == null) { + update = true; + this.setProperty(GPX_OUTPUT_SPEED, String.valueOf(false)); + } + + //------------------------------------------------ + // GPX出力: 生成されたGPXファイル(ファイル名が'_.gpx'で終わるもの)も対象にする。 {ON | OFF} + valueStr = this.getProperty(GPX_REUSE); + if (valueStr == null) { + update = true; + this.setProperty(GPX_REUSE, String.valueOf(false)); + } + + //------------------------------------------------ + // GPX: 基準時刻 {FILE_UPDATE | EXIF} + valueStr = this.getProperty(GPX_BASETIME); + if (valueStr == null) { + update = true; + this.setProperty(GPX_BASETIME, "FILE_UPDATE"); + } + + if (update) { + // ・ファイルがなければ新たに作る + // ・項目が足りない時は書き足す。 + this.store(new FileOutputStream(this.file), "defuilt settings"); + } + } + + public void store() throws FileNotFoundException, IOException { + this.store(new FileOutputStream(this.file), "by AdjustTime"); + } +} diff --git a/src/main/java/osm/surveyor/matchtime/Restamp.java b/src/main/java/osm/surveyor/matchtime/Restamp.java new file mode 100644 index 0000000..b770042 --- /dev/null +++ b/src/main/java/osm/surveyor/matchtime/Restamp.java @@ -0,0 +1,229 @@ +package osm.surveyor.matchtime; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Comparator; +import java.util.Date; +import java.util.List; +import java.util.ResourceBundle; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.apache.commons.imaging.ImageReadException; + +/** + * 動画から一定間隔で切り出したIMAGEファイルの更新日時を書き換える + * + * @author yuu + */ +public class Restamp extends Thread { + static public final String TIME_PATTERN = "yyyy-MM-dd HH:mm:ss z"; + + /** + * 実行中に発生したExceptionを保持する場所 + */ + public Exception ex = null; + + /** + * ログ設定プロパティファイルのファイル内容 + */ + protected static final String LOGGING_PROPERTIES_DATA + = "handlers=java.util.logging.ConsoleHandler\n" + + ".level=FINEST\n" + + "java.util.logging.ConsoleHandler.level=INFO\n" + + "java.util.logging.ConsoleHandler.formatter=osm.jp.gpx.YuuLogFormatter"; + + /** + * メイン + * 動画から一定間隔で切り出したIMAGEのファイル更新日時を書き換える + * + * ・画像ファイルの更新日付を書き換えます。(Exi情報は無視します) + * ※ 指定されたディレクトリ内のすべての'*.jpg'ファイルを処理の対象とします + * ・画像は連番形式(名前順に並べられること)の名称となっていること + * + * 1.予め、動画から画像を切り出す + * ソースファイル(mp4ファイル); 「-i 20160427_104154.mp4」 + * 出力先: 「-f image2 img/%06d.jpg」 imgフォルダに6桁の連番ファイルを差出力する + * 切り出し開始秒数→ 「-ss 0」 (ファイルの0秒から切り出し開始) + * 切り出し間隔; 「-r 30」 (1秒間隔=30fps間隔) + * ``` + * $ cd /home/yuu/Desktop/OSM/20180325_横浜新道 + * $ ffmpeg -ss 0 -i 20160427_104154.mp4 -f image2 -r 15 img/%06d.jpg + * ``` + * + * 2. ファイルの更新日付を書き換える + * ``` + * $ cd /home/yuu/Desktop/workspace/AdjustTime/importPicture/dist + * $ java -cp .:AdjustTime2.jar osm.jp.gpx.Restamp /home/yuu/Desktop/OSM/20180325_横浜新道/img 000033.jpg 2018-03-25_12:20:32 003600.jpg 2018-03-25_13:20:09 + * ``` + * + * exp) $ java -jar Restamp.jar argv[0] argv[1] argv[2] argv[3] argv[4] + * + * @param argv + * argv[0] = 画像ファイルが格納されているディレクトリ --> imgDir + * argv[1] = 時刻補正の基準とする画像ファイル --> baseFile1 + * argv[2] = 基準画像ファイルの精確な撮影日時 "yyyy-MM-dd HH:mm:ss z" --> baseTime1 + * argv[3] = 時刻補正の基準とする画像ファイル --> baseFile2 + * argv[4] = 基準画像ファイルの精確な撮影日時 "yyyy-MM-dd HH:mm:ss z" --> baseTime2 + * @throws ImageReadException + */ + public static void main(String[] argv) throws Exception + { + if (argv.length < 5) { + System.out.println("java Restamp "); + return; + } + + Path imgDir = Paths.get(argv[0]); + if (!Files.exists(imgDir)) { + // "[error] が存在しません。" + System.out.println(i18n.getString("msg.200")); + return; + } + if (!Files.isDirectory(imgDir)) { + // "[error] がフォルダじゃない" + System.out.println(i18n.getString("msg.210")); + return; + } + + Path baseFile1 = Paths.get(imgDir.toString(), argv[1]); + if (!Files.exists(baseFile1)) { + // "[error] が存在しません。" + System.out.println(i18n.getString("msg.220")); + return; + } + if (!Files.isRegularFile(baseFile1)) { + // "[error] がファイルじゃない" + System.out.println(i18n.getString("msg.230")); + return; + } + + DateFormat df1 = new SimpleDateFormat(TIME_PATTERN); + Date baseTime1 = df1.parse(argv[2]); + + Path baseFile2 = Paths.get(imgDir.toString(), argv[3]); + if (!Files.exists(baseFile2)) { + // "[error] が存在しません。" + System.out.println(i18n.getString("msg.240")); + return; + } + if (!Files.isRegularFile(baseFile2)) { + // "[error] がファイルじゃない" + System.out.println(i18n.getString("msg.250")); + return; + } + + Date baseTime2 = df1.parse(argv[4]); + + Restamp obj = new Restamp(); + obj.setUp(imgDir, baseFile1, baseTime1, baseFile2, baseTime2); + } + + Path imgDir; + //Path outDir; + Date baseTime1; + Date baseTime2; + Path baseFile1; + Path baseFile2; + public static ResourceBundle i18n = ResourceBundle.getBundle("i18n"); + + @SuppressWarnings("Convert2Lambda") + public void setUp( + Path imgDir, + Path baseFile1, Date baseTime1, + Path baseFile2, Date baseTime2) throws Exception { + this.imgDir = imgDir; + this.baseTime1 = baseTime1; + this.baseTime2 = baseTime2; + this.baseFile1 = baseFile1; + this.baseFile2 = baseFile2; + + /* + File outDir = new File(imgDir, "restamp.out"); + if (outDir.exists()) { + // "[error] が存在する。" + if (!outDir.isDirectory()) { + // "[error] がフォルダじゃない" + System.out.println(i18n.getString("msg.270")); + return; + } + } + else { + // "が存在しない。" + outDir.mkdir(); + } + this.outDir = outDir; + */ + + this.start(); + try { + this.join(); + } catch(InterruptedException end) {} + if (this.ex != null) { + throw this.ex; + } + } + + @Override + public void run() { + int bCount1 = 0; + int bCount2 = 0; + boolean base1 = false; + boolean base2 = false; + ArrayList jpgFiles = new ArrayList<>(); + + try { + // 指定されたディレクトリ内のJPEGファイルすべてを対象とする + Stream files = Files.list(Paths.get(imgDir.toString())); + List sortedList = files.sorted(Comparator.naturalOrder()).collect(Collectors.toList()); + + for (Path p : sortedList) { + if (Files.exists(p) && Files.isRegularFile(p)) { + String filename = p.getFileName().toString(); + if (filename.toUpperCase().endsWith(".JPG")) { + jpgFiles.add(p); + bCount1 += (base1 ? 0 : 1); + bCount2 += (base2 ? 0 : 1); + if (p.getFileName().equals(baseFile1.getFileName())) { + base1 = true; + } + if (p.getFileName().equals(baseFile2.getFileName())) { + base2 = true; + } + } + } + } + if (!jpgFiles.isEmpty()) { + DateFormat df2 = new SimpleDateFormat(TIME_PATTERN); + + // imgDir内の画像ファイルを処理する + @SuppressWarnings("LocalVariableHidesMemberVariable") + long span = this.baseTime2.getTime() - this.baseTime1.getTime(); + span = span / (bCount2 - bCount1); + int i = 0; + System.out.println("-------------------------------"); + System.out.println("Update last modified date time."); + for (Path jpgFile : jpgFiles) { + long deltaMsec = (i - (bCount1 -1)) * span; + i++; + Calendar cal = Calendar.getInstance(); + cal.setTime(this.baseTime1); + cal.add(Calendar.MILLISECOND, (int) deltaMsec); + + System.out.println(String.format("\t%s --> %s", df2.format(cal.getTime()), jpgFile.getFileName())); + jpgFile.toFile().setLastModified(cal.getTimeInMillis()); + } + System.out.println("-------------------------------"); + } + } + catch(IOException e) { + e.printStackTrace(); + this.ex = new Exception(e); + } + } +} \ No newline at end of file diff --git a/src/main/java/osm/surveyor/matchtime/gui/AboutDialog.java b/src/main/java/osm/surveyor/matchtime/gui/AboutDialog.java new file mode 100644 index 0000000..6c72aec --- /dev/null +++ b/src/main/java/osm/surveyor/matchtime/gui/AboutDialog.java @@ -0,0 +1,130 @@ +package osm.surveyor.matchtime.gui; +import java.awt.*; + +@SuppressWarnings("serial") +public class AboutDialog extends Dialog +{ + //{{DECLARE_CONTROLS + java.awt.Label label1; + java.awt.Button okButton; + java.awt.Label label2; + //}} + + // Used for addNotify redundency check. + boolean fComponentsAdjusted = false; + + class SymWindow extends java.awt.event.WindowAdapter + { + @Override + public void windowClosing(java.awt.event.WindowEvent event) { + Object object = event.getSource(); + if (object == AboutDialog.this) { + AboutDialog_WindowClosing(event); + } + } + } + + class SymAction implements java.awt.event.ActionListener + { + @Override + public void actionPerformed(java.awt.event.ActionEvent event) { + Object object = event.getSource(); + if (object == okButton) { + okButton_Clicked(event); + } + } + } + + @SuppressWarnings("OverridableMethodCallInConstructor") + public AboutDialog(Frame parent, boolean modal) { + super(parent, modal); + + // This code is automatically generated by Visual Cafe when you add + // components to the visual environment. It instantiates and initializes + // the components. To modify the code, only use code syntax that matches + // what Visual Cafe can generate, or Visual Cafe may be unable to back + // parse your Java file into its visual environment. + + + //{{INIT_CONTROLS + setLayout(null); + setVisible(false); + setSize(360,114); + label1 = new java.awt.Label(ReStamp.PROGRAM_NAME +" Version "+ ReStamp.PROGRAM_VARSION +" ("+ ReStamp.PROGRAM_UPDATE +")", Label.CENTER); + label1.setBounds(10,10,340,20); + add(label1); + okButton = new java.awt.Button(); + okButton.setLabel("OK"); + okButton.setBounds(145,65,66,27); + add(okButton); + label2 = new java.awt.Label("Copyright(C) 2014,2020, yuuhayashi \n The MIT License (MIT).",Label.RIGHT); + label2.setBounds(10,40,340,20); + add(label2); + setTitle("About... "+ ReStamp.PROGRAM_NAME); + //}} + + //{{REGISTER_LISTENERS + SymWindow aSymWindow = new SymWindow(); + this.addWindowListener(aSymWindow); + SymAction lSymAction = new SymAction(); + okButton.addActionListener(lSymAction); + //}} + } + + @SuppressWarnings("OverridableMethodCallInConstructor") + public AboutDialog(Frame parent, String title, boolean modal) { + this(parent, modal); + setTitle(title); + } + + @Override + public void addNotify() { + // Record the size of the window prior to calling parents addNotify. + + super.addNotify(); + + // Only do this once. + if (fComponentsAdjusted) { + return; + } + + // Adjust components according to the insets + setSize(getInsets().left + getInsets().right + getSize().width, getInsets().top + getInsets().bottom + getSize().height); + Component components[] = getComponents(); + for (Component component : components) { + Point p = component.getLocation(); + p.translate(getInsets().left, getInsets().top); + component.setLocation(p); + } + + // Used for addNotify check. + fComponentsAdjusted = true; + } + + /** + * Shows or hides the component depending on the boolean flag b. + * @param b if true, show the component; otherwise, hide the component. + * @see java.awt.Component#isVisible + */ + @Override + public void setVisible(boolean b) { + if(b) { + Rectangle bounds = getParent().getBounds(); + Rectangle abounds = getBounds(); + setLocation(bounds.x + (bounds.width - abounds.width)/ 2, + bounds.y + (bounds.height - abounds.height)/2); + } + super.setVisible(b); + } + + void AboutDialog_WindowClosing(java.awt.event.WindowEvent event) { + dispose(); + } + + void okButton_Clicked(java.awt.event.ActionEvent event) { + //{{CONNECTION + // Clicked from okButton Hide the Dialog + dispose(); + //}} + } +} diff --git a/src/main/java/osm/surveyor/matchtime/gui/Card.java b/src/main/java/osm/surveyor/matchtime/gui/Card.java new file mode 100644 index 0000000..4181a47 --- /dev/null +++ b/src/main/java/osm/surveyor/matchtime/gui/Card.java @@ -0,0 +1,123 @@ +package osm.surveyor.matchtime.gui; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.util.ArrayList; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.JTabbedPane; +import static osm.surveyor.matchtime.gui.ReStamp.i18n; + +public class Card extends JPanel { + JTabbedPane tabbe; + public JPanel mainPanel; + String title; + int backNumber = -1; + int nextNumber = -1; + public JButton nextButton; // [次へ]ボタン + public JButton backButton; // [戻る]ボタン + + public Card(JTabbedPane tabbe, String title, int backNumber, int nextNumber) { + super(); + this.tabbe = tabbe; + this.title = title; + this.backNumber = backNumber; + this.nextNumber = nextNumber; + + // INIT_CONTROLS + this.setLayout(new BorderLayout()); + + //---- CENTER ----- + mainPanel = new JPanel(); + mainPanel.setLayout(new BorderLayout()); + this.add(mainPanel, BorderLayout.CENTER); + + //---- SOUTH ----- + JPanel buttonPanel = new JPanel(new BorderLayout()); + buttonPanel.add(Box.createVerticalStrut(10), BorderLayout.SOUTH); + buttonPanel.add(Box.createVerticalStrut(10), BorderLayout.NORTH); + this.add(buttonPanel, BorderLayout.SOUTH); + + //{{REGISTER_LISTENERS + SymAction lSymAction = new SymAction(); + if (nextNumber >= 0) { + nextButton = new JButton(i18n.getString("button.next")); + nextButton.setEnabled(false); + buttonPanel.add(nextButton, BorderLayout.EAST); + nextButton.addActionListener(lSymAction); + } + + if (backNumber >= 0) { + backButton = new JButton(i18n.getString("button.previous")); + backButton.setEnabled(false); + buttonPanel.add(backButton, BorderLayout.WEST); + backButton.addActionListener(lSymAction); + } + //}} + } + + public static JPanel packLine(JComponent[] components, JPanel panel) { + panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); + int max = 0; + for (JComponent component : components) { + panel.add(component); + Dimension size = component.getMaximumSize(); + if (max < size.height) { + max = size.height; + } + } + Dimension size = new Dimension(); + size.width = Short.MAX_VALUE; + size.height = max; + panel.setMaximumSize(size); + return panel; + } + + public static JPanel packLine(JComponent component, JPanel panel) { + ArrayList array = new ArrayList<>(); + array.add(component); + return packLine(array.toArray(new JComponent[array.size()]), panel); + } + + @Override + public void setEnabled(boolean enabled) { + this.tabbe.setEnabledAt(nextNumber - 1, enabled); + } + + public String getTitle() { + return this.title; + } + + class SymAction implements java.awt.event.ActionListener { + @Override + public void actionPerformed(java.awt.event.ActionEvent event) { + Object object = event.getSource(); + if (object == nextButton) { + nextButton_Action(event); + } + else if (object == backButton) { + backButton_Action(event); + } + } + } + + /** + * [次へ]ボタンをクリックした時の動作 + * @param event + */ + void nextButton_Action(ActionEvent event) { + this.tabbe.setSelectedIndex(this.nextNumber); + } + + /** + * [戻る]ボタンをクリックした時の動作 + * @param event + */ + void backButton_Action(ActionEvent event) { + this.tabbe.setSelectedIndex(this.backNumber); + } +} diff --git a/src/main/java/osm/surveyor/matchtime/gui/CardExifPerform.java b/src/main/java/osm/surveyor/matchtime/gui/CardExifPerform.java new file mode 100644 index 0000000..9b22b17 --- /dev/null +++ b/src/main/java/osm/surveyor/matchtime/gui/CardExifPerform.java @@ -0,0 +1,197 @@ +package osm.surveyor.matchtime.gui; + +import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTabbedPane; +import osm.surveyor.matchtime.AppParameters; +import static osm.surveyor.matchtime.gui.ReStamp.dfjp; +import static osm.surveyor.matchtime.gui.ReStamp.i18n; +import osm.surveyor.util.Exif; + +/** + * 実行パネル + * @author yuu + */ +public class CardExifPerform extends Card implements PanelAction { + ParameterPanelTime arg_basetime; // 画像の基準時刻: + ParameterPanelGpx arg_gpxFile; // GPX file or Folder + ParameterPanelOutput arg_output; // EXIF & 書き出しフォルダ + JButton doButton; // [処理実行]ボタン + + /** + * コンストラクタ + * @param tabbe parent panel + * @param arg_basetime // 開始画像の基準時刻: + * @param arg_gpxFile // GPX file or Folder: + * @param arg_output // EXIF & 書き出しフォルダ + * @param text + * @param pre + * @param next + */ + public CardExifPerform( + JTabbedPane tabbe, + ParameterPanelTime arg_basetime, + ParameterPanelGpx arg_gpxFile, + ParameterPanelOutput arg_output, + String text, + int pre, int next + ) { + super(tabbe, text, pre, next); + this.arg_basetime = arg_basetime; + this.arg_gpxFile = arg_gpxFile; + this.arg_output = arg_output; + + SymAction lSymAction = new SymAction(); + JPanel argsPanel = new JPanel(); + argsPanel.setLayout(new BoxLayout(argsPanel, BoxLayout.PAGE_AXIS)); + + // 5. EXIF変換を行うかどうかを選択してください。 + // - EXIF変換を行う場合には、変換ファイルを出力するフォルダも指定する必要があります。 + // - 出力フォルダには、書き込み権限と、十分な空き容量が必要です。 + JLabel label5 = new JLabel(); + label5.setText( + String.format( + "

5. %s

  • %s
  • %s
", + i18n.getString("label.500"), + i18n.getString("label.501"), + i18n.getString("label.502") + ) + ); + argsPanel.add(packLine(label5, new JPanel())); + + // 出力フォルダ + //argsPanel.add(packLine(new JLabel(i18n.getString("label.530")), new JPanel())); + argsPanel.add(arg_output); + + // チェックボックス "IMGの変換をする" + if (arg_output.outputIMG != null) { + arg_output.outputIMG.addActionListener(lSymAction); + argsPanel.add(arg_output.outputIMG); + } + + // チェックボックス "IMGの変換をする" + if (arg_output.outputIMG_all != null) { + argsPanel.add(arg_output.outputIMG_all); + } + + // チェックボックス "EXIFの変換をする" + if (arg_output.exifON != null) { + argsPanel.add(arg_output.exifON); + } + + // チェックボックス "ポイントマーカーをGPXファイルに出力する" + if (arg_output.gpxOutputWpt != null) { + argsPanel.add(arg_output.gpxOutputWpt); + } + + // チェックボックス "ソースGPXのを無視する" + if (arg_output.gpxOverwriteMagvar != null) { + argsPanel.add(arg_output.gpxOverwriteMagvar); + } + + // チェックボックス "出力GPXに[SPEED]を上書きする" + if (arg_output.gpxOutputSpeed != null) { + argsPanel.add(arg_output.gpxOutputSpeed); + } + + // [処理実行]ボタン + doButton = new JButton( + i18n.getString("button.execute"), + ReStamp.createImageIcon("images/media_playback_start.png") + ); + argsPanel.add(doButton); + + this.mainPanel.add(argsPanel, BorderLayout.CENTER); + + //{{REGISTER_LISTENERS + doButton.addActionListener(lSymAction); + //}} + } + + class SymAction implements java.awt.event.ActionListener { + @Override + public void actionPerformed(java.awt.event.ActionEvent event) { + Object object = event.getSource(); + if (object == doButton) { + doButton_Action(event); + } + else if (object == arg_output.outputIMG) { + outputIMG_Action(event); + } + } + } + + /** + * checkbox[IMG変換]を変更した場合のアクション + * ON ー> IMG出力フォルダのフィールドを有効にする + * OFF -> IMG出力フォルダのフィールドを無効にする + * @param event + */ + void outputIMG_Action (ActionEvent event) { + setEnabled(isEnabled()); + } + + /** + * [実行]ボタンをクリックしたときの動作 + * @param event + */ + @SuppressWarnings("UseSpecificCatch") + void doButton_Action(java.awt.event.ActionEvent event) { + doButton.setEnabled(false); + + ParameterPanelImageFile arg_baseTimeImg = arg_basetime.imageFile; // 基準時刻画像 + ParameterPanelFolder arg_srcFolder = arg_baseTimeImg.paramDir; + + try { + AppParameters params = new AppParameters(); + + String[] argv = new String[0]; + params.setProperty(AppParameters.GPX_NO_FIRST_NODE, String.valueOf(arg_gpxFile.isNoFirstNodeSelected())); + params.setProperty(AppParameters.GPX_REUSE, String.valueOf(arg_gpxFile.isGpxReuseSelected())); + params.setProperty(AppParameters.GPX_SOURCE_FOLDER, arg_gpxFile.getText()); + if ((arg_basetime.exifBase != null) && arg_basetime.exifBase.isSelected()) { + params.setProperty(AppParameters.GPX_BASETIME, "EXIF_TIME"); + } + else { + params.setProperty(AppParameters.GPX_BASETIME, "FILE_UPDATE"); + } + params.setProperty(AppParameters.IMG_SOURCE_FOLDER, arg_srcFolder.getText()); + params.setProperty(AppParameters.IMG_BASE_FILE, arg_baseTimeImg.getText()); + params.setProperty(AppParameters.IMG_TIME, Exif.toUTCString(dfjp.parse(arg_basetime.getText()))); + params.setProperty(AppParameters.IMG_OUTPUT, String.valueOf(arg_output.outputIMG.isSelected())); + params.setProperty(AppParameters.IMG_OUTPUT_ALL, String.valueOf(arg_output.outputIMG_all.isSelected())); + params.setProperty(AppParameters.IMG_OUTPUT_FOLDER, arg_output.getText()); + params.setProperty(AppParameters.IMG_OUTPUT_EXIF, String.valueOf(arg_output.exifON.isSelected())); + params.setProperty(AppParameters.GPX_OVERWRITE_MAGVAR, String.valueOf(arg_output.gpxOverwriteMagvar.isSelected())); + params.setProperty(AppParameters.GPX_OUTPUT_SPEED, String.valueOf(arg_output.gpxOutputSpeed.isSelected())); + params.setProperty(AppParameters.GPX_OUTPUT_WPT, String.valueOf(arg_output.gpxOutputWpt.isSelected())); + params.store(); + } + catch(Exception e) { + e.printStackTrace(); + } + + (new DoDialog(new String[0])).setVisible(true); + + doButton.setEnabled(true); + } + + /** + * 入力条件が満たされているかどうか + * @return + */ + @Override + public boolean isEnable() { + return (arg_basetime.isEnable() && arg_gpxFile.isEnable()); + } + + @Override + @SuppressWarnings("empty-statement") + public void openAction() { + ; // 何もしない + } +} diff --git a/src/main/java/osm/surveyor/matchtime/gui/CardGpxFile.java b/src/main/java/osm/surveyor/matchtime/gui/CardGpxFile.java new file mode 100644 index 0000000..b69cbba --- /dev/null +++ b/src/main/java/osm/surveyor/matchtime/gui/CardGpxFile.java @@ -0,0 +1,74 @@ +package osm.surveyor.matchtime.gui; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import javax.swing.BoxLayout; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTabbedPane; +import static osm.surveyor.matchtime.gui.ReStamp.i18n; + +/** + * [GPXファイル]選択パネル + * @author yuu + */ +public class CardGpxFile extends Card implements PanelAction { + ParameterPanelGpx arg_gpxFile; + + /** + * コンストラクタ + * @param tabbe parent panel + * @param arg_gpxFile // 開始画像の基準時刻: + * @param text + * @param pre + * @param next + */ + public CardGpxFile( + JTabbedPane tabbe, + ParameterPanelGpx arg_gpxFile, + String text, + int pre, int next + ) { + super(tabbe, text, pre, next); + this.arg_gpxFile = arg_gpxFile; + + // 4. ヒモ付を行うGPXファイルを選択してください。 + // - フォルダを指定すると、フォルダ内のすべてのGPXファイルを対象とします。 + JPanel argsPanel = new JPanel(); + argsPanel.setLayout(new BoxLayout(argsPanel, BoxLayout.PAGE_AXIS)); + argsPanel.add(packLine(new JLabel(i18n.getString("label.400")), new JPanel())); + argsPanel.add(arg_gpxFile); + + // "セグメント'trkseg'の最初の1ノードは無視する。" + if (arg_gpxFile.noFirstNode != null) { + argsPanel.add(arg_gpxFile.noFirstNode); + } + + // "生成されたGPXファイル(ファイル名が'_.gpx'で終わるもの)も変換の対象にする" + if (arg_gpxFile.gpxReuse != null) { + argsPanel.add(arg_gpxFile.gpxReuse); + } + + JPanel space = new JPanel(); + space.setMinimumSize(new Dimension(40, 20)); + space.setMaximumSize(new Dimension(40, Short.MAX_VALUE)); + argsPanel.add(space); + + this.mainPanel.add(argsPanel, BorderLayout.CENTER); + } + + /** + * 入力条件が満たされているかどうか + * @return + */ + @Override + public boolean isEnable() { + return (arg_gpxFile.isEnable()); + } + + @Override + @SuppressWarnings("empty-statement") + public void openAction() { + ; // 何もしない + } +} diff --git a/src/main/java/osm/surveyor/matchtime/gui/Command.java b/src/main/java/osm/surveyor/matchtime/gui/Command.java new file mode 100644 index 0000000..174d38c --- /dev/null +++ b/src/main/java/osm/surveyor/matchtime/gui/Command.java @@ -0,0 +1,70 @@ +package osm.surveyor.matchtime.gui; + +import java.lang.reflect.InvocationTargetException; +import java.text.SimpleDateFormat; + +public class Command extends Thread { + String[] args; // コマンドパラメータ + private String commandName = ""; // コマンド名 + @SuppressWarnings({ "rawtypes" }) + private final Class cmd; // 実行対象インスタンス + + /** + * コンストラクタ:実行対象のインスタンスを得る + * @param cmd + */ + public Command(Class cmd) { + super(); + this.cmd = cmd; + this.commandName = cmd.getName(); + this.args = new String[0]; + } + + /** + * コマンドパラメータの設定 + * @param args + */ + public void setArgs(String[] args) { + this.args = args; + } + + public void setCommandName(String name) { + this.commandName = name; + } + public String getCommandName() { + return this.commandName; + } + + @SuppressWarnings("unchecked") + @Override + public void run() { + System.out.println("[START:"+ (new SimpleDateFormat("yyyy/MM/dd-HH:mm:ss")).format(new java.util.Date()) +"]\t"+ this.commandName); + for (int i=0; i < args.length; i++) { + System.out.println(" args["+ i +"]: "+ this.args[i]); + } + System.out.println(); + + try { + try { + java.lang.reflect.Method method = this.cmd.getMethod("main", new Class[] {String[].class}); + method.setAccessible(true); + method.invoke(null, new Object[]{this.args}); + + System.out.println(); + System.out.println("[END:"+ (new SimpleDateFormat("yyyy/MM/dd-HH:mm:ss")).format(new java.util.Date()) +"]\t"+ this.commandName); + } + catch (InvocationTargetException e) { + System.out.println("[ERR!:"+ (new SimpleDateFormat("yyyy/MM/dd-HH:mm:ss")).format(new java.util.Date()) +"]\t"+ this.commandName); + throw e; + } + catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException e) { + System.out.println("[ERR!:"+ (new SimpleDateFormat("yyyy/MM/dd-HH:mm:ss")).format(new java.util.Date()) +"]\t"+ this.commandName); + throw e; + } + } + catch(InvocationTargetException | NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException e) { + e.printStackTrace(System.out); + } + System.out.println(); + } +} diff --git a/src/main/java/osm/surveyor/matchtime/gui/DoDialog.java b/src/main/java/osm/surveyor/matchtime/gui/DoDialog.java new file mode 100644 index 0000000..eac4820 --- /dev/null +++ b/src/main/java/osm/surveyor/matchtime/gui/DoDialog.java @@ -0,0 +1,230 @@ +package osm.surveyor.matchtime.gui; +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Point; +import java.awt.event.ActionEvent; +import java.io.*; +import javax.swing.*; + +/** + * 処理 + */ +@SuppressWarnings("serial") +public class DoDialog extends JDialog { + public static final String TITLE = "Do Command"; + + // Used for addNotify check. + boolean fComponentsAdjusted = false; + String[] args; + + //{{DECLARE_CONTROLS + JPanel buttonPanel; // ボタン配置パネル (下部) + JButton closeButton; // [クローズ]ボタン + JButton doButton; // [実行]ボタン + JTextArea textArea; // 実行結果を表示するJTextArea (中央) + //}} + + @SuppressWarnings("OverridableMethodCallInConstructor") + public DoDialog(String[] args) { + super(); // モーダルダイアログを基盤にする + this.args = args; + + // INIT_CONTROLS + @SuppressWarnings("OverridableMethodCallInConstructor") + Container container = getContentPane(); + container.setLayout(new BorderLayout()); + //parentFrame.setVisible(false); + setSize(getInsets().left + getInsets().right + 980, getInsets().top + getInsets().bottom + 480); + setTitle(DoDialog.TITLE); + + // コントロールパネル + buttonPanel = new JPanel(); + + doButton = new JButton("実行"); + doButton.setToolTipText("処理を実行します."); + doButton.setEnabled(true); + doButton.addActionListener((ActionEvent event) -> { + // 処理中であることを示すため + // ボタンの文字列を変更し,使用不可にする + doButton.setText("処理中..."); + doButton.setEnabled(false); + + // SwingWorker を生成し,実行する + LongTaskWorker worker = new LongTaskWorker(doButton); + worker.execute(); + }); + buttonPanel.add(doButton); + + closeButton = new JButton("閉じる"); + closeButton.setToolTipText("処理を終了します."); + closeButton.addActionListener((ActionEvent event) -> { + dispose(); + }); + buttonPanel.add(closeButton); + + this.getContentPane().add("South", buttonPanel); + + // 説明文 + textArea = new JTextArea(); + JScrollPane sc=new JScrollPane(textArea); + textArea.setFont(new Font(Font.MONOSPACED,Font.PLAIN,12)); + textArea.setTabSize(4); + this.getContentPane().add("Center", sc); + + try { + textArea.append("> java -cp importPicture.jar osm.jp.gpx.ImportPicture"); + for (String arg : args) { + textArea.append(" '" + arg + "'"); + } + textArea.append("\n\n"); + } + catch (Exception e) { + System.out.println(e.toString()); + } + + // JFrameの表示 + //parentFrame.setVisible(true); + } + + /** + * Shows or hides the component depending on the boolean flag b. + * @param b trueのときコンポーネントを表示; その他のとき, componentを隠す. + * @see java.awt.Component#isVisible + */ + @Override + public void setVisible(boolean b) { + if(b) { + setLocation(80, 80); + } + super.setVisible(b); + } + + @Override + public void addNotify() { + // Record the size of the window prior to calling parents addNotify. + Dimension d = getSize(); + + super.addNotify(); + + if (fComponentsAdjusted) { + return; + } + + // Adjust components according to the insets + setSize(getInsets().left + getInsets().right + d.width, getInsets().top + getInsets().bottom + d.height); + Component components[] = getComponents(); + for (Component component : components) { + Point p = component.getLocation(); + p.translate(getInsets().left, getInsets().top); + component.setLocation(p); + } + fComponentsAdjusted = true; + } + + + /** + * JTextAreaに書き出すOutputStream + */ + public static class JTextAreaOutputStream extends OutputStream { + private final ByteArrayOutputStream os; + + /** 書き出し対象 */ + private final JTextArea textArea; + private final String encode; + + public JTextAreaOutputStream(JTextArea textArea, String encode) { + this.textArea = textArea; + this.encode = encode; + this.os = new ByteArrayOutputStream(); + } + + /** + * OutputStream#write(byte[])のオーバーライド + * @param arg + * @throws java.io.IOException + */ + @Override + public void write(int arg) throws IOException { + this.os.write(arg); + } + + /** + * flush()でJTextAreaに書き出す + * @throws java.io.IOException + */ + @Override + public void flush() throws IOException { + // 文字列のエンコード + final String str = new String(this.os.toByteArray(), this.encode); + // 実際の書き出し処理 + SwingUtilities.invokeLater( + new Runnable(){ + @Override + public void run() { + JTextAreaOutputStream.this.textArea.append(str); + } + } + ); + // 書き出した内容はクリアする + this.os.reset(); + } + } + + // 非同期に行う処理を記述するためのクラス + class LongTaskWorker extends SwingWorker { + private final JButton button; + + public LongTaskWorker(JButton button) { + this.button = button; + } + + // 非同期に行われる処理 + @Override + @SuppressWarnings("SleepWhileInLoop") + public Object doInBackground() { + // ながーい処理 + PrintStream defOut = System.out; + PrintStream defErr = System.err; + + OutputStream os = new JTextAreaOutputStream(textArea, "UTF-8"); + PrintStream stdout = new PrintStream(os, true); // 自動flushをtrueにしておく + + // System.out にJTextAreaOutputStreamに書き出すPrintStreamを設定 + System.setOut(stdout); + System.setErr(stdout); + + try { + Command command = new Command(osm.surveyor.matchtime.Restamp.class); + command.setArgs(args); + command.start(); // コマンドを実行 + while (command.isAlive()) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) {} + } + } + catch(Exception e) { + e.printStackTrace(stdout); + } + finally { + System.setOut(defOut); + System.setErr(defErr); + doButton.setEnabled(true); + } + + return null; + } + + // 非同期処理後に実行 + @Override + protected void done() { + // 処理が終了したので,文字列を元に戻し + // ボタンを使用可能にする + button.setText("実行"); + button.setEnabled(true); + } + } +} diff --git a/src/main/java/osm/surveyor/matchtime/gui/GpxAndFolderFilter.java b/src/main/java/osm/surveyor/matchtime/gui/GpxAndFolderFilter.java new file mode 100644 index 0000000..aaf44ff --- /dev/null +++ b/src/main/java/osm/surveyor/matchtime/gui/GpxAndFolderFilter.java @@ -0,0 +1,25 @@ +package osm.surveyor.matchtime.gui; + +import java.io.File; +import javax.swing.filechooser.*; + +public class GpxAndFolderFilter extends FileFilter { + + @Override + public boolean accept(File f) { + if (f.isDirectory()) { + return true; + } + + String extension = Utils.getExtension(f); + if (extension != null) { + return extension.equals("gpx"); + } + return false; + } + + @Override + public String getDescription() { + return "Just GPXs"; + } +} diff --git a/src/main/java/osm/surveyor/matchtime/gui/ImageFileView.java b/src/main/java/osm/surveyor/matchtime/gui/ImageFileView.java new file mode 100644 index 0000000..6e42bb7 --- /dev/null +++ b/src/main/java/osm/surveyor/matchtime/gui/ImageFileView.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Oracle or the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package osm.surveyor.matchtime.gui; + +import java.io.File; +import javax.swing.*; +import javax.swing.filechooser.*; + +/* ImageFileView.java is used by FileChooserDemo2.java. */ +public class ImageFileView extends FileView { + ImageIcon jpgIcon = Utils.createImageIcon("images/jpgIcon.gif"); + ImageIcon gifIcon = Utils.createImageIcon("images/gifIcon.gif"); + ImageIcon tiffIcon = Utils.createImageIcon("images/tiffIcon.gif"); + ImageIcon pngIcon = Utils.createImageIcon("images/pngIcon.png"); + + @Override + public String getName(File f) { + return null; //let the L&F FileView figure this out + } + + @Override + public String getDescription(File f) { + return null; //let the L&F FileView figure this out + } + + @Override + public Boolean isTraversable(File f) { + return null; //let the L&F FileView figure this out + } + + @Override + public String getTypeDescription(File f) { + String extension = Utils.getExtension(f); + String type = null; + + if (extension != null) { + switch (extension) { + case Utils.JPEG: + case Utils.JPG: + type = "JPEG Image"; + break; + case Utils.GIF: + type = "GIF Image"; + break; + case Utils.TIFF: + case Utils.TIF: + type = "TIFF Image"; + break; + case Utils.PNG: + type = "PNG Image"; + break; + default: + break; + } + } + return type; + } + + @Override + public Icon getIcon(File f) { + String extension = Utils.getExtension(f); + Icon icon = null; + + if (extension != null) { + switch (extension) { + case Utils.JPEG: + case Utils.JPG: + icon = jpgIcon; + break; + case Utils.GIF: + icon = gifIcon; + break; + case Utils.TIFF: + case Utils.TIF: + icon = tiffIcon; + break; + case Utils.PNG: + icon = pngIcon; + break; + default: + break; + } + } + return icon; + } +} diff --git a/src/main/java/osm/surveyor/matchtime/gui/ImageFilter.java b/src/main/java/osm/surveyor/matchtime/gui/ImageFilter.java new file mode 100644 index 0000000..c3b6556 --- /dev/null +++ b/src/main/java/osm/surveyor/matchtime/gui/ImageFilter.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Oracle or the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package osm.surveyor.matchtime.gui; + +import java.io.File; +import javax.swing.filechooser.*; + +/* ImageFilter.java is used by FileChooserDemo2.java. */ +public class ImageFilter extends FileFilter { + + //Accept all directories and all gif, jpg, tiff, or png files. + @Override + public boolean accept(File f) { + if (f.isDirectory()) { + return true; + } + + String extension = Utils.getExtension(f); + if (extension != null) { + return extension.equals(Utils.TIFF) || + extension.equals(Utils.TIF) || + extension.equals(Utils.GIF) || + extension.equals(Utils.JPEG) || + extension.equals(Utils.JPG) || + extension.equals(Utils.PNG); + } + return false; + } + + //The description of this filter + @Override + public String getDescription() { + return "Just Images"; + } +} diff --git a/src/main/java/osm/surveyor/matchtime/gui/ImagePreview.java b/src/main/java/osm/surveyor/matchtime/gui/ImagePreview.java new file mode 100644 index 0000000..e4cb3d6 --- /dev/null +++ b/src/main/java/osm/surveyor/matchtime/gui/ImagePreview.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Oracle or the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package osm.surveyor.matchtime.gui; + +import javax.swing.*; + +import java.beans.*; +import java.awt.*; +import java.io.File; + +/* ImagePreview.java by FileChooserDemo2.java. */ +@SuppressWarnings("serial") +public class ImagePreview extends JComponent implements PropertyChangeListener { + ImageIcon thumbnail = null; + File file = null; + static int IMAGE_SIZE_X = 320; + static int IMAGE_SIZE_Y = 240; + + @SuppressWarnings({"LeakingThisInConstructor", "OverridableMethodCallInConstructor"}) + public ImagePreview(JFileChooser fc) { + setPreferredSize(new Dimension(IMAGE_SIZE_X + 10, IMAGE_SIZE_Y + 10)); + fc.addPropertyChangeListener(this); + } + + public void loadImage() { + if (file == null) { + thumbnail = null; + return; + } + + //Don't use createImageIcon (which is a wrapper for getResource) + //because the image we're trying to load is probably not one + //of this program's own resources. + ImageIcon tmpIcon = new ImageIcon(file.getPath()); + if (tmpIcon.getIconWidth() > IMAGE_SIZE_X) { + thumbnail = new ImageIcon(tmpIcon.getImage(). + getScaledInstance(IMAGE_SIZE_X, -1, Image.SCALE_DEFAULT)); + } else { //no need to miniaturize + thumbnail = tmpIcon; + } + } + + @Override + public void propertyChange(PropertyChangeEvent e) { + boolean update = false; + String prop = e.getPropertyName(); + + if (JFileChooser.DIRECTORY_CHANGED_PROPERTY.equals(prop)) { + file = null; + update = true; + } + else if (JFileChooser.SELECTED_FILE_CHANGED_PROPERTY.equals(prop)) { + file = (File) e.getNewValue(); + update = true; + } + if (update) { + thumbnail = null; + if (isShowing()) { + loadImage(); + repaint(); + } + } + } + + @Override + protected void paintComponent(Graphics g) { + if (thumbnail == null) { + loadImage(); + } + if (thumbnail != null) { + int x = getWidth()/2 - thumbnail.getIconWidth()/2; + int y = getHeight()/2 - thumbnail.getIconHeight()/2; + + if (y < 0) { + y = 0; + } + + if (x < 5) { + x = 5; + } + thumbnail.paintIcon(this, g, x, y); + } + } +} diff --git a/src/main/java/osm/surveyor/matchtime/gui/PanelAction.java b/src/main/java/osm/surveyor/matchtime/gui/PanelAction.java new file mode 100644 index 0000000..c089101 --- /dev/null +++ b/src/main/java/osm/surveyor/matchtime/gui/PanelAction.java @@ -0,0 +1,11 @@ +package osm.surveyor.matchtime.gui; + +public interface PanelAction { + void openAction(); + + /** + * 入力条件が満たされているかどうか + * @return + */ + boolean isEnable(); +} diff --git a/src/main/java/osm/surveyor/matchtime/gui/ParamAction.java b/src/main/java/osm/surveyor/matchtime/gui/ParamAction.java new file mode 100644 index 0000000..7465e2f --- /dev/null +++ b/src/main/java/osm/surveyor/matchtime/gui/ParamAction.java @@ -0,0 +1,7 @@ +package osm.surveyor.matchtime.gui; + +public interface ParamAction { + boolean isEnable(); + void setText(String text); + String getText(); +} diff --git a/src/main/java/osm/surveyor/matchtime/gui/ParameterData.java b/src/main/java/osm/surveyor/matchtime/gui/ParameterData.java new file mode 100644 index 0000000..fe428ff --- /dev/null +++ b/src/main/java/osm/surveyor/matchtime/gui/ParameterData.java @@ -0,0 +1,23 @@ +package osm.surveyor.matchtime.gui; + +import java.util.Observable; + +public class ParameterData extends Observable { + String content = ""; + + String getContent() { + return content; + } + + void setContent(String content) { + this.content = content; + setChanged(); + super.notifyObservers(content); + clearChanged(); + } + + @Override + public void notifyObservers(Object arg) { + setContent(arg.toString()); + } +} diff --git a/src/main/java/osm/surveyor/matchtime/gui/ParameterPanel.java b/src/main/java/osm/surveyor/matchtime/gui/ParameterPanel.java new file mode 100644 index 0000000..7f6909e --- /dev/null +++ b/src/main/java/osm/surveyor/matchtime/gui/ParameterPanel.java @@ -0,0 +1,54 @@ +package osm.surveyor.matchtime.gui; + +import java.awt.Dimension; +import java.util.ResourceBundle; + +import javax.swing.BoxLayout; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; + +/** + * パラメータを設定する為のパネル。 + * この1インスタンスで、1パラメータをあらわす。 + */ +public abstract class ParameterPanel extends JPanel implements ParamAction { + private static final long serialVersionUID = 4629824800747170556L; + public JTextField argField; + public JLabel argLabel; + public ResourceBundle i18n = ResourceBundle.getBundle("i18n"); + + @SuppressWarnings("OverridableMethodCallInConstructor") + public ParameterPanel(String label, String text) { + this(); + this.argLabel.setText(label); + this.argField.setText(text); + } + + public ParameterPanel() { + super(); + + argLabel = new JLabel(); + argField = new JTextField(); + + this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); + this.setMaximumSize(new Dimension(1920, 40)); + this.add(argLabel); + this.add(argField); + } + + public ParameterPanel setLabel(String label) { + this.argLabel.setText(label); + return this; + } + + @Override + public void setText(String text) { + this.argField.setText(text); + } + + @Override + public String getText() { + return this.argField.getText(); + } +} diff --git a/src/main/java/osm/surveyor/matchtime/gui/ParameterPanelFolder.java b/src/main/java/osm/surveyor/matchtime/gui/ParameterPanelFolder.java new file mode 100644 index 0000000..a42139e --- /dev/null +++ b/src/main/java/osm/surveyor/matchtime/gui/ParameterPanelFolder.java @@ -0,0 +1,108 @@ +package osm.surveyor.matchtime.gui; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.io.FileNotFoundException; +import javax.swing.JButton; +import javax.swing.JFileChooser; + +@SuppressWarnings("serial") +public class ParameterPanelFolder extends ParameterPanel implements ActionListener +{ + JFileChooser fc; + JButton selectButton; + int chooser; + + /** + * コンストラクタ + * ディレクトリのみ選択可能なダイアログ + * @param label + * @param text + */ + public ParameterPanelFolder(String label, String text) { + this(label, text, JFileChooser.DIRECTORIES_ONLY); + } + + @SuppressWarnings({"OverridableMethodCallInConstructor", "LeakingThisInConstructor"}) + public ParameterPanelFolder(String label, String text, int chooser) { + super(label, text); + + // Create a file chooser + this.chooser = chooser; + + // "選択..." + selectButton = new JButton( + i18n.getString("button.select"), + ReStamp.createImageIcon("images/Open16.gif") + ); + selectButton.addActionListener(this); + this.add(selectButton); + } + + public void setEnable(boolean f) { + super.setEnabled(f); + selectButton.setEnabled(f); + } + + public File getDirectory() throws FileNotFoundException { + String path = this.argField.getText(); + if (path == null) { + throw new FileNotFoundException("Folder is Not specifiyed yet."); + } + File sdir = new File(path); + if (!sdir.exists()) { + throw new FileNotFoundException(String.format("Folder '%s' is Not exists.", path)); + } + if (!sdir.isDirectory()) { + throw new FileNotFoundException(String.format("Folder '%s' is Not directory.", path)); + } + return sdir; + } + + @Override + public void actionPerformed(ActionEvent e) { + if (e.getSource() == selectButton){ + File sdir; + try { + sdir = getDirectory(); + } catch (FileNotFoundException ex) { + sdir = new File("."); + this.argField.setText(sdir.getAbsolutePath()); + } + if (sdir.exists()) { + this.fc = new JFileChooser(sdir); + } + else { + this.fc = new JFileChooser(); + } + this.fc.setFileSelectionMode(this.chooser); + + int returnVal = this.fc.showOpenDialog(ParameterPanelFolder.this); + + if (returnVal == JFileChooser.APPROVE_OPTION) { + File file = this.fc.getSelectedFile(); + this.argField.setText(file.getAbsolutePath()); + } + } + } + + /** + * 有効な値が設定されているかどうか + * @return + */ + @Override + public boolean isEnable() { + String text = this.argField.getText(); + if (text == null) { + return false; + } + try { + File dir = new File(text); + return (dir.exists() && dir.isDirectory()); + } + catch (Exception e) { + return false; + } + } +} \ No newline at end of file diff --git a/src/main/java/osm/surveyor/matchtime/gui/ParameterPanelGpx.java b/src/main/java/osm/surveyor/matchtime/gui/ParameterPanelGpx.java new file mode 100644 index 0000000..4f53ae0 --- /dev/null +++ b/src/main/java/osm/surveyor/matchtime/gui/ParameterPanelGpx.java @@ -0,0 +1,126 @@ +package osm.surveyor.matchtime.gui; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; + +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JFileChooser; +import osm.surveyor.matchtime.AppParameters; + +@SuppressWarnings("serial") +public class ParameterPanelGpx extends ParameterPanel implements ActionListener +{ + JFileChooser fc; + JButton selectButton; + public JCheckBox noFirstNode; // CheckBox: "セグメント'trkseg'の最初の1ノードは無視する。" + public JCheckBox gpxReuse; // CheckBox: "生成されたGPXファイル(ファイル名が'_.gpx'で終わるもの)も変換の対象にする" + + /** + * コンストラクタ + * @param label + * @param text + */ + @SuppressWarnings({"OverridableMethodCallInConstructor", "LeakingThisInConstructor"}) + public ParameterPanelGpx(String label, String text) { + super(label, text); + + // "選択..." + selectButton = new JButton( + i18n.getString("button.select"), + ReStamp.createImageIcon("images/Open16.gif") + ); + selectButton.addActionListener(this); + this.add(selectButton); + } + + @Override + public void actionPerformed(ActionEvent e) { + if (e.getSource() == selectButton){ + System.out.println("ParameterPanelGpx.actionPerformed(openButton)"); + File sdir = new File(this.argField.getText()); + if (sdir.exists()) { + this.fc = new JFileChooser(sdir); + } + else { + this.fc = new JFileChooser(); + } + this.fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); + this.fc.addChoosableFileFilter(new GpxAndFolderFilter()); + this.fc.setAcceptAllFileFilterUsed(false); + + int returnVal = this.fc.showOpenDialog(ParameterPanelGpx.this); + + if (returnVal == JFileChooser.APPROVE_OPTION) { + File file = this.fc.getSelectedFile(); + this.argField.setText(file.getAbsolutePath()); + } + } + } + + public File getGpxFile() { + if (isEnable()) { + return new File(getText()); + } + return null; + } + + /** + * "セグメント'trkseg'の最初の1ノードは無視する。" + * @param label テキスト + * @param params プロパティ + */ + public void addNoFirstNode(String label, AppParameters params) { + boolean selected = false; + if (params.getProperty(AppParameters.GPX_NO_FIRST_NODE).equals("true")) { + selected = true; + } + noFirstNode = new JCheckBox(label, selected); + } + + public boolean isNoFirstNodeSelected() { + return (noFirstNode != null) && noFirstNode.isSelected(); + } + + /** + * "生成されたGPXファイル(ファイル名が'_.gpx'で終わるもの)も変換の対象にする" + * @param label テキスト + * @param params プロパティ + */ + public void addGpxReuse(String label, AppParameters params) { + boolean selected = false; + if (params.getProperty(AppParameters.GPX_REUSE).equals("true")) { + selected = true; + } + gpxReuse = new JCheckBox(label, selected); + } + + public boolean isGpxReuseSelected() { + return (gpxReuse != null) && gpxReuse.isSelected(); + } + + /** + * このフィールドに有効な値が設定されているかどうか + * @return + */ + @Override + public boolean isEnable() { + String text = this.argField.getText(); + if (text != null) { + File file = new File(text); + if (file.exists()) { + if (file.isFile()) { + String name = file.getName().toUpperCase(); + if (name.endsWith(".GPX")) { + return true; + } + } + else if (file.isDirectory()) { + return true; + } + } + } + return false; + } +} diff --git a/src/main/java/osm/surveyor/matchtime/gui/ParameterPanelImageFile.java b/src/main/java/osm/surveyor/matchtime/gui/ParameterPanelImageFile.java new file mode 100644 index 0000000..4e84c9b --- /dev/null +++ b/src/main/java/osm/surveyor/matchtime/gui/ParameterPanelImageFile.java @@ -0,0 +1,109 @@ +package osm.surveyor.matchtime.gui; + +import java.awt.event.ActionEvent; +import java.io.File; +import java.io.FileNotFoundException; +import javax.swing.JButton; +import javax.swing.JFileChooser; + +@SuppressWarnings("serial") +public class ParameterPanelImageFile extends ParameterPanel { + JFileChooser fc; + public JButton openButton; + public ParameterPanelFolder paramDir; + + @SuppressWarnings("OverridableMethodCallInConstructor") + public ParameterPanelImageFile( + String label, String text, + ParameterPanelFolder paramDir + ) { + super(label, text); + + // "選択..." + SelectButtonAction buttonAction = new SelectButtonAction(); + openButton = new JButton(i18n.getString("button.select")); + openButton.addActionListener(buttonAction); + this.add(openButton); + + //Create a file chooser + this.paramDir = paramDir; + } + + class SelectButtonAction implements java.awt.event.ActionListener + { + @SuppressWarnings("override") + public void actionPerformed(ActionEvent e) { + selectImage_Action(e); + } + } + + public void selectImage_Action(ActionEvent ev) { + File sdir = new File(paramDir.getText()); + System.out.println(sdir.toPath()); + if (sdir.isDirectory()) { + fc = new JFileChooser(sdir); + } + else { + fc = new JFileChooser(); + } + + fc.addChoosableFileFilter(new ImageFilter()); + fc.setAcceptAllFileFilterUsed(false); + fc.setFileView(new ImageFileView()); + fc.setAccessory(new ImagePreview(fc)); + + //Show it. "選択" + int returnVal = fc.showDialog(ParameterPanelImageFile.this, i18n.getString("dialog.select")); + if (returnVal == JFileChooser.APPROVE_OPTION) { + File file = fc.getSelectedFile(); + this.argField.setText(file.getName()); + } + fc.setSelectedFile(null); + } + + public File getImageFile() { + if (this.paramDir.isEnable()) { + String text = this.argField.getText(); + if (text != null) { + try { + File dir = this.paramDir.getDirectory(); + File file = new File(dir, text); + if (file.exists() && file.isFile()) { + return file; + } + } + catch (FileNotFoundException e) { + return null; + } + } + } + return null; + } + + /** + * + * @return + */ + @Override + public boolean isEnable() { + if (this.paramDir.isEnable()) { + String text = this.argField.getText(); + if (text != null) { + try { + File dir = this.paramDir.getDirectory(); + File file = new File(dir, text); + if (file.exists() && file.isFile()) { + String name = file.getName().toUpperCase(); + if (name.endsWith(".JPG") || name.endsWith(".JPEG")) { + return true; + } + } + } + catch (FileNotFoundException e) { + return false; + } + } + } + return false; + } +} \ No newline at end of file diff --git a/src/main/java/osm/surveyor/matchtime/gui/ParameterPanelOutput.java b/src/main/java/osm/surveyor/matchtime/gui/ParameterPanelOutput.java new file mode 100644 index 0000000..0101ce1 --- /dev/null +++ b/src/main/java/osm/surveyor/matchtime/gui/ParameterPanelOutput.java @@ -0,0 +1,123 @@ +package osm.surveyor.matchtime.gui; + +import javax.swing.JCheckBox; +import javax.swing.JFileChooser; +import osm.surveyor.matchtime.AppParameters; + +@SuppressWarnings("serial") +public class ParameterPanelOutput extends ParameterPanelFolder +{ + JCheckBox outputIMG; // IMGの変換 する/しない + JCheckBox outputIMG_all; // 'out of GPX time'でもIMGの変換をする {ON | OFF} + JCheckBox exifON; // EXIF 書き出しモード / !(EXIFの書き換えはしない) + JCheckBox gpxOutputWpt; // GPXにを書き出す + JCheckBox gpxOverwriteMagvar; // ソースGPXのを無視する + JCheckBox gpxOutputSpeed; // GPXにを書き出す + + /** + * コンストラクタ + * ディレクトリのみ選択可能なダイアログ + * @param label + * @param text + */ + public ParameterPanelOutput(String label, String text) { + super(label, text, JFileChooser.DIRECTORIES_ONLY); + } + + /** + * チェックボックス "IMGの変換をする" + * @param label テキスト + * @param params プロパティ + */ + public void addCheckChangeImage(String label, AppParameters params) { + boolean selected = false; + if (params.getProperty(AppParameters.IMG_OUTPUT).equals("true")) { + selected = true; + } + outputIMG = new JCheckBox(label, selected); + } + + /** + * チェックボックス "GPXファイル時間外のファイルもコピーする" + * @param label + * @param params + */ + public void addCheckOutofGpxTime(String label, AppParameters params) { + boolean selected = false; + if (params.getProperty(AppParameters.IMG_OUTPUT_ALL).equals("true")) { + selected = true; + } + outputIMG_all = new JCheckBox(label, selected); + } + + /** + * チェックボックス "EXIFの変換をする" + * @param label + * @param params + */ + public void addCheckOutputExif(String label, AppParameters params) { + boolean selected = false; + if (params.getProperty(AppParameters.IMG_OUTPUT_EXIF).equals("true")) { + selected = true; + } + exifON = new JCheckBox(label, selected); + } + + /** + * チェックボックス "ポイントマーカー[WPT]をGPXファイルに出力する" + * @param label + * @param params + */ + public void addCheckOutputWpt(String label, AppParameters params) { + boolean selected = false; + if (params.getProperty(AppParameters.GPX_OUTPUT_WPT).equals("true")) { + selected = true; + } + gpxOutputWpt = new JCheckBox(label, selected); + gpxOutputWpt.setEnabled(true); + } + + /** + * チェックボックス "ソースGPXの<MAGVAR>を無視する" + * @param label + * @param params + */ + public void addCheckIgnoreMagvar(String label, AppParameters params) { + boolean selected = false; + if (params.getProperty(AppParameters.GPX_OVERWRITE_MAGVAR).equals("true")) { + selected = true; + } + gpxOverwriteMagvar = new JCheckBox(label, selected); + gpxOverwriteMagvar.setEnabled(true); + } + + /** + * チェックボックス "出力GPXに[SPEED]を上書きする" + * @param label + * @param params + */ + public void addCheckOutputSpeed(String label, AppParameters params) { + boolean selected = false; + if (params.getProperty(AppParameters.GPX_OUTPUT_SPEED).equals("true")) { + selected = true; + } + gpxOutputSpeed = new JCheckBox(label, selected); + gpxOutputSpeed.setEnabled(true); + } + + /** + * checkbox[IMG変換]を変更した場合のアクション + * ON ー> IMG出力フォルダのフィールドを有効にする + * OFF -> IMG出力フォルダのフィールドを無効にする + * @param event + */ + class ChangeImageAction implements java.awt.event.ActionListener { + @Override + public void actionPerformed(java.awt.event.ActionEvent event) { + Object object = event.getSource(); + if (object == outputIMG) { + setEnabled(outputIMG.isEnabled()); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/osm/surveyor/matchtime/gui/ParameterPanelSelecter.java b/src/main/java/osm/surveyor/matchtime/gui/ParameterPanelSelecter.java new file mode 100644 index 0000000..0a7f768 --- /dev/null +++ b/src/main/java/osm/surveyor/matchtime/gui/ParameterPanelSelecter.java @@ -0,0 +1,45 @@ +package osm.surveyor.matchtime.gui; + +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JPanel; + +@SuppressWarnings("serial") +public class ParameterPanelSelecter extends JPanel implements ActionListener { + public static final int ITEM_WIDTH_1 = 160; + public static final int ITEM_WIDTH_2 = 240; + public static final int LINE_WIDTH = ITEM_WIDTH_1 + ITEM_WIDTH_2; + public static final int LINE_HEIGHT = 18; + public JLabel label; + public JComboBox field; + public String value; + + @SuppressWarnings({"OverridableMethodCallInConstructor", "LeakingThisInConstructor"}) + public ParameterPanelSelecter(String title, String[] items) { + super(null); + this.value = items[0]; + + this.label = new JLabel(title, JLabel.RIGHT); + this.label.setBounds(0, 0, ITEM_WIDTH_1 - 6, LINE_HEIGHT); + add(this.label); + + this.field = new JComboBox<>(); + this.field.addActionListener(this); + for (String item : items) { + this.field.addItem(item); + } + this.field.setBounds(ITEM_WIDTH_1, 0, ITEM_WIDTH_2, LINE_HEIGHT); + add(this.field); + + setPreferredSize(new Dimension(ITEM_WIDTH_1, LINE_HEIGHT)); + } + + @Override + public void actionPerformed(ActionEvent e) { + this.value = (String)this.field.getSelectedItem(); + } +} \ No newline at end of file diff --git a/src/main/java/osm/surveyor/matchtime/gui/ParameterPanelTime.java b/src/main/java/osm/surveyor/matchtime/gui/ParameterPanelTime.java new file mode 100644 index 0000000..10c4bb0 --- /dev/null +++ b/src/main/java/osm/surveyor/matchtime/gui/ParameterPanelTime.java @@ -0,0 +1,195 @@ +package osm.surveyor.matchtime.gui; + +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.io.File; +import java.io.IOException; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JRadioButton; +import org.apache.commons.imaging.ImageReadException; +import org.apache.commons.imaging.Imaging; +import org.apache.commons.imaging.common.ImageMetadata; +import org.apache.commons.imaging.formats.jpeg.JpegImageMetadata; +import org.apache.commons.imaging.formats.tiff.TiffImageMetadata; +import org.apache.commons.imaging.formats.tiff.constants.ExifTagConstants; +import osm.surveyor.matchtime.AppParameters; +import osm.surveyor.matchtime.Restamp; +import static osm.surveyor.matchtime.gui.ReStamp.dfjp; +import osm.surveyor.matchtime.gui.restamp.DialogCorectTime; + +/** + * パラメータを設定する為のパネル。 + * この1インスタンスで、1パラメータをあらわす。 + */ +public class ParameterPanelTime extends ParameterPanel { + SimpleDateFormat sdf = (SimpleDateFormat)DateFormat.getDateTimeInstance(); + ParameterPanelImageFile imageFile; // 基準時刻画像 + + + // 基準時刻の指定グループ (排他選択) + public ButtonGroup baseTimeGroup = new ButtonGroup(); + public JRadioButton exifBase = null; // EXIF日時を基準にする/ !(ファイル更新日時を基準にする) + public JRadioButton fupdateBase = null; // File更新日時を基準にする/ !(EXIF日時を基準にする) + + public JButton updateButton; + public JButton resetButton; + Window owner; + + @SuppressWarnings("OverridableMethodCallInConstructor") + public ParameterPanelTime( + String label, + String text, + ParameterPanelImageFile imageFile + ) { + super(label, text); + this.imageFile = imageFile; + + // "ボタン[変更...]" + UpdateButtonAction buttonAction = new UpdateButtonAction(this); + updateButton = new JButton(i18n.getString("button.update")); + updateButton.addActionListener(buttonAction); + this.add(updateButton); + + // "ボタン[再設定...]" + ResetButtonAction resetAction = new ResetButtonAction(this); + resetButton = new JButton(i18n.getString("button.reset")); + resetButton.addActionListener(resetAction); + resetButton.setVisible(false); + this.add(resetButton); + } + + public ParameterPanelTime setOwner(Window owner) { + this.owner = owner; + return this; + } + + /** + * 「EXIFの日時を基準にする」 + * @param label テキスト + * @param params プロパティ + */ + public void addExifBase(String label, AppParameters params) { + boolean selected = false; + if (params.getProperty(AppParameters.GPX_BASETIME).equals("EXIF_TIME")) { + selected = true; + } + exifBase = new JRadioButton(label, selected); + baseTimeGroup.add(exifBase); + } + + /** + * 「File更新日時を基準にする」 + * @param label テキスト + * @param params プロパティ + */ + public void addFileUpdate(String label, AppParameters params) { + boolean selected = false; + if (params.getProperty(AppParameters.GPX_BASETIME).equals("FILE_UPDATE")) { + selected = true; + } + fupdateBase = new JRadioButton(label, selected); + baseTimeGroup.add(fupdateBase); + } + + public ParameterPanelImageFile getImageFile() { + return this.imageFile; + } + + + /** + * [変更...]ボタンのアクション + */ + class UpdateButtonAction implements java.awt.event.ActionListener + { + ParameterPanelTime param; + + public UpdateButtonAction(ParameterPanelTime param) { + this.param = param; + } + + @SuppressWarnings("override") + public void actionPerformed(ActionEvent e) { + fileSelect_Action(param); + (new DialogCorectTime(param, owner)).setVisible(true); + } + } + + /** + * [再設定...]ボタンのアクション + */ + class ResetButtonAction implements java.awt.event.ActionListener + { + ParameterPanelTime paramPanelTime; + + public ResetButtonAction(ParameterPanelTime param) { + this.paramPanelTime = param; + } + + @SuppressWarnings("override") + public void actionPerformed(ActionEvent e) { + fileSelect_Action(paramPanelTime); + } + } + + /** + * 画像ファイルが選択されたときのアクション + * 1.ラジオボタンの選択を参照してTEXTフィールドにファイルの「日時」を設定する + * @param param + */ + void fileSelect_Action(ParameterPanelTime param) { + if (imageFile.isEnable()) { + File timeFile = imageFile.getImageFile(); + + // Radio Selecter + sdf.applyPattern(Restamp.TIME_PATTERN); + if ((exifBase != null) && exifBase.isSelected()) { + try { + ImageMetadata meta = Imaging.getMetadata(timeFile); + JpegImageMetadata jpegMetadata = (JpegImageMetadata)meta; + if (jpegMetadata != null) { + TiffImageMetadata exif = jpegMetadata.getExif(); + if (exif != null) { + String dateTimeOriginal = exif.getFieldValue(ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL)[0]; + long lastModifyTime = sdf.parse(dateTimeOriginal).getTime(); + param.argField.setText(dfjp.format(new Date(lastModifyTime))); + } + else { + param.argField.setText("exif == null"); + } + } + } + catch (IOException | ParseException | ImageReadException ex) {} + } + else { + long lastModified = timeFile.lastModified(); + param.argField.setText(sdf.format(new Date(lastModified))); + } + } + else { + param.argField.setText(""); + } + } + + @Override + public boolean isEnable() { + if (this.imageFile.isEnable()) { + String text = this.argField.getText(); + if (text != null) { + try { + sdf.applyPattern(Restamp.TIME_PATTERN); + sdf.parse(text); + return true; + } + catch (ParseException e) { + return false; + } + } + } + return false; + } +} diff --git a/src/main/java/osm/surveyor/matchtime/gui/QuitDialog.java b/src/main/java/osm/surveyor/matchtime/gui/QuitDialog.java new file mode 100644 index 0000000..aafb9a6 --- /dev/null +++ b/src/main/java/osm/surveyor/matchtime/gui/QuitDialog.java @@ -0,0 +1,107 @@ +package osm.surveyor.matchtime.gui; + +import java.awt.Font; +import java.awt.Rectangle; +import java.awt.Toolkit; +import java.awt.Window; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; +import java.util.ResourceBundle; + +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JLabel; + +@SuppressWarnings("serial") +public class QuitDialog extends JDialog implements WindowListener +{ + JButton yesButton; + JButton noButton; + JLabel label1; + + @SuppressWarnings("OverridableMethodCallInConstructor") + public QuitDialog(JFrame parent, boolean modal) { + super(parent, modal); + + ResourceBundle i18n = ResourceBundle.getBundle("i18n"); + + addWindowListener((WindowListener) this); + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + + setLayout(null); + setSize(getInsets().left + getInsets().right + 337, getInsets().top + getInsets().bottom + 135); + + yesButton = new JButton(String.format(" %s ", i18n.getString("dialog.quit"))); + yesButton.addActionListener(new java.awt.event.ActionListener() { + @Override + public void actionPerformed(java.awt.event.ActionEvent evt) { + Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(new WindowEvent((Window)getParent(), 201)); + System.exit(0); + } + }); + yesButton.setBounds(getInsets().left + 72, getInsets().top + 80, 79, 22); + yesButton.setFont(new Font("Dialog", 1, 12)); + add(yesButton); + + noButton = new JButton(i18n.getString("dialog.cancel")); + noButton.addActionListener(new java.awt.event.ActionListener() { + @Override + public void actionPerformed(java.awt.event.ActionEvent evt) { + Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(new WindowEvent(QuitDialog.this, WindowEvent.WINDOW_CLOSING)); + setVisible(false); + } + }); + noButton.setBounds(getInsets().left + 185, getInsets().top + 80, 99, 22); + noButton.setFont(new Font("Dialog", 1, 12)); + add(noButton); + + label1 = new JLabel(i18n.getString("dialog.msg1"), JLabel.CENTER); + label1.setBounds(78, 33, 180, 23); + add(label1); + setTitle(i18n.getString("dialog.msg1")); + setResizable(false); + setVisible(true); + } + + @Override + public void setVisible(boolean b) { + if(b) { + Rectangle bounds = getParent().getBounds(); + Rectangle abounds = getBounds(); + setLocation(bounds.x + (bounds.width - abounds.width) / 2, bounds.y + (bounds.height - abounds.height) / 2); + } + super.setVisible(b); + } + + + @Override + public void windowActivated(WindowEvent e) { + } + + @Override + public void windowClosed(WindowEvent e) { + setVisible(false); + } + + @Override + public void windowClosing(WindowEvent e) { + setVisible(false); + } + + @Override + public void windowDeactivated(WindowEvent e) { + } + + @Override + public void windowDeiconified(WindowEvent e) { + } + + @Override + public void windowIconified(WindowEvent e) { + } + + @Override + public void windowOpened(WindowEvent e) { + } +} diff --git a/src/main/java/osm/surveyor/matchtime/gui/ReStamp.java b/src/main/java/osm/surveyor/matchtime/gui/ReStamp.java new file mode 100644 index 0000000..d3727f1 --- /dev/null +++ b/src/main/java/osm/surveyor/matchtime/gui/ReStamp.java @@ -0,0 +1,382 @@ +package osm.surveyor.matchtime.gui; + +import osm.surveyor.matchtime.AppParameters; +import java.awt.*; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.ResourceBundle; +import java.util.TimeZone; +import javax.swing.*; +import javax.swing.event.DocumentEvent; +import osm.surveyor.matchtime.gui.restamp.CardImageFile; +import osm.surveyor.matchtime.gui.restamp.CardPerformFile; +import osm.surveyor.matchtime.gui.restamp.CardSourceFolder; + +/** + * 本プログラムのメインクラス + */ +@SuppressWarnings("serial") +public class ReStamp extends JFrame +{ + public static final String PROGRAM_NAME = "ReStamp for Movie2jpeg"; + public static final String PROGRAM_VARSION = "3.00a"; + public static final String PROGRAM_UPDATE = "2020-01-19"; + + AppParameters params; + public static SimpleDateFormat dfjp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z"); + + // Used for addNotify check. + boolean fComponentsAdjusted = false; + public static ResourceBundle i18n = ResourceBundle.getBundle("i18n"); + + //{{DECLARE_CONTROLS + JTabbedPane cardPanel; // ウィザード形式パネル(タブ型) + Card[] cards; + //}} + + //---入力フィールド---------------------------------------------------- + ParameterPanelFolder arg1_srcFolder; // 対象フォルダ + ParameterPanelImageFile arg2_baseTimeImg; // 開始画像ファイルパス + ParameterPanelTime arg2_basetime; // 開始画像の基準時刻: + ParameterPanelImageFile arg3_baseTimeImg; // 終了画像ファイルパス + ParameterPanelTime arg3_basetime; // 終了画像の基準時刻: + ParameterPanelOutput arg4_output; // EXIF & 書き出しフォルダ + + //{{DECLARE_MENUS + java.awt.MenuBar mainMenuBar; + java.awt.Menu menu1; + java.awt.MenuItem miDoNewFileList; + java.awt.MenuItem miDoDirSize; + java.awt.MenuItem miDoReadXML; + java.awt.MenuItem miExit; + java.awt.Menu menu3; + java.awt.MenuItem miAbout; + //}} + + class SymWindow extends java.awt.event.WindowAdapter { + /** + * このFrameが閉じられるときの動作。 + * このパネルが閉じられたら、このアプリケーションも終了させる。 + */ + @Override + public void windowClosing(java.awt.event.WindowEvent event) { + Object object = event.getSource(); + if (object == ReStamp.this) { + DbMang_WindowClosing(event); + } + } + } + + class SymAction implements java.awt.event.ActionListener { + @Override + public void actionPerformed(java.awt.event.ActionEvent event) { + Object object = event.getSource(); + if (object == miAbout) { + miAbout_Action(event); + } + else if (object == miExit) { + miExit_Action(event); + } + } + } + + /** + * データベース内のテーブルを一覧で表示するFrame + * @throws IOException + */ + @SuppressWarnings("OverridableMethodCallInConstructor") + public ReStamp() throws IOException + { + dfjp.setTimeZone(TimeZone.getTimeZone("JST")); + + // INIT_CONTROLS + Container container = getContentPane(); + container.setLayout(new BorderLayout()); + setSize( + getInsets().left + getInsets().right + 720, + getInsets().top + getInsets().bottom + 480 + ); + setTitle(ReStamp.PROGRAM_NAME +" v"+ ReStamp.PROGRAM_VARSION); + + //---- CENTER ----- + JPanel mainPanel = new JPanel(); + mainPanel.setLayout(new BorderLayout()); + container.add(mainPanel, BorderLayout.CENTER); + + //---- SOUTH ----- + JPanel southPanel = new JPanel(new BorderLayout()); + southPanel.add(Box.createVerticalStrut(10), BorderLayout.SOUTH); + southPanel.add(Box.createVerticalStrut(10), BorderLayout.NORTH); + container.add(southPanel, BorderLayout.SOUTH); + + //---- SPACE ----- + container.add(Box.createVerticalStrut(30), BorderLayout.NORTH); + container.add(Box.createHorizontalStrut(10), BorderLayout.WEST); + container.add(Box.createHorizontalStrut(10), BorderLayout.EAST); + + params = new AppParameters(); + + //--------------------------------------------------------------------- + cardPanel = new JTabbedPane(JTabbedPane.LEFT); + mainPanel.add(cardPanel, BorderLayout.CENTER); + + cards = new Card[4]; + int cardNo = 0; + + //--------------------------------------------------------------------- + // 1.[対象フォルダ]設定パネル + { + arg1_srcFolder = new ParameterPanelFolder( + i18n.getString("label.110") +": ", + params.getProperty(AppParameters.IMG_SOURCE_FOLDER) + ); + arg1_srcFolder.argField + .getDocument() + .addDocumentListener( + new SimpleDocumentListener() { + @Override + public void update(DocumentEvent e) { + toEnable(0, arg1_srcFolder.isEnable()); + } + } + ); + + Card card = new CardSourceFolder(cardPanel, arg1_srcFolder); + cardPanel.addTab(card.getTitle(), card); + cardPanel.setEnabledAt(cardNo, true); + cards[cardNo] = card; + cardNo++; + } + + //--------------------------------------------------------------------- + // 2. [基準画像(開始)]選択パネル + // 2.[基準時刻画像]設定パネル + // 2a.基準時刻の入力画面 + { + // 基準時刻画像 + arg2_baseTimeImg = new ParameterPanelImageFile( + i18n.getString("label.210") +": ", + null, + arg1_srcFolder + ); + + // 2a. 基準時刻: + arg2_basetime = new ParameterPanelTime( + i18n.getString("label.310"), + null, + arg2_baseTimeImg + ); + arg2_basetime.argField.getDocument().addDocumentListener( + new SimpleDocumentListener() { + @Override + public void update(DocumentEvent e) { + toEnable(1, arg2_basetime.isEnable()); + } + } + ); + + CardImageFile card = new CardImageFile( + cardPanel, arg2_basetime, (Window)this, + ReStamp.i18n.getString("tab.restamp.200"), 0, 2); + cardPanel.addTab(card.getTitle(), card); + cardPanel.setEnabledAt(cardNo, false); + cards[cardNo] = card; + cardNo++; + } + + //--------------------------------------------------------------------- + // 3. 最終画像の本当の時刻を設定の入力画面 + { + // 基準時刻画像 + arg3_baseTimeImg = new ParameterPanelImageFile( + i18n.getString("label.210") +": ", + null, + arg1_srcFolder + ); + + // 3a. 基準時刻: + arg3_basetime = new ParameterPanelTime( + i18n.getString("label.310"), + null, + arg3_baseTimeImg + ); + arg3_basetime.argField.getDocument().addDocumentListener( + new SimpleDocumentListener() { + @Override + public void update(DocumentEvent e) { + toEnable(2, arg3_basetime.isEnable()); + } + } + ); + + CardImageFile card = new CardImageFile( + cardPanel, arg3_basetime, (Window)this, + ReStamp.i18n.getString("tab.restamp.250"), 1, 3 + ); + cardPanel.addTab(card.getTitle(), card); + cardPanel.setEnabledAt(cardNo, false); + cards[cardNo] = card; + cardNo++; + } + + //--------------------------------------------------------------------- + // 4. 実行画面 + { + // 4. "出力フォルダ: " + arg4_output = new ParameterPanelOutput( + i18n.getString("label.530") + ": ", + params.getProperty(AppParameters.IMG_OUTPUT_FOLDER) + ); + arg4_output.argField.getDocument().addDocumentListener( + new SimpleDocumentListener() { + @Override + public void update(DocumentEvent e) { + toEnable(3, arg4_output.isEnable()); + } + } + ); + + // パネル表示 + CardPerformFile card = new CardPerformFile( + cardPanel, + arg2_basetime, + arg3_basetime, + arg4_output, + ReStamp.i18n.getString("tab.restamp.400"), 2, -1 + ); + cardPanel.addTab(card.getTitle(), card); + cardPanel.setEnabledAt(cardNo, false); + cards[cardNo] = card; + cardNo++; + } + + + //--------------------------------------------------------------------- + // INIT_MENUS + menu1 = new java.awt.Menu("File"); + miExit = new java.awt.MenuItem(i18n.getString("menu.quit")); + miExit.setFont(new Font("Dialog", Font.PLAIN, 12)); + menu1.add(miExit); + + miAbout = new java.awt.MenuItem("About..."); + miAbout.setFont(new Font("Dialog", Font.PLAIN, 12)); + + menu3 = new java.awt.Menu("Help"); + menu3.setFont(new Font("Dialog", Font.PLAIN, 12)); + menu3.add(miAbout); + + mainMenuBar = new java.awt.MenuBar(); + mainMenuBar.setHelpMenu(menu3); + mainMenuBar.add(menu1); + mainMenuBar.add(menu3); + setMenuBar(mainMenuBar); + + //{{REGISTER_LISTENERS + SymWindow aSymWindow = new SymWindow(); + this.addWindowListener(aSymWindow); + SymAction lSymAction = new SymAction(); + miAbout.addActionListener(lSymAction); + miExit.addActionListener(lSymAction); + arg2_baseTimeImg.openButton.addActionListener(lSymAction); + //}} + } + + /** + * Shows or hides the component depending on the boolean flag b. + * @param b trueのときコンポーネントを表示; その他のとき, componentを隠す. + * @see java.awt.Component#isVisible + */ + @Override + public void setVisible(boolean b) { + if(b) { + setLocation(50, 50); + } + super.setVisible(b); + } + + /** + * このクラスをインスタンスを生成して表示する。 + * コマンドラインの引数はありません。 + * @param args + */ + @SuppressWarnings("UseSpecificCatch") + static public void main(String args[]) { + SwingUtilities.invokeLater(() -> { + try { + createAndShowGUI(); + } catch (Exception e) { + e.printStackTrace(); + } + }); + } + private static void createAndShowGUI() throws IOException { + (new ReStamp()).setVisible(true); + } + + @Override + public void addNotify() { + // Record the size of the window prior to calling parents addNotify. + Dimension d = getSize(); + + super.addNotify(); + + if (fComponentsAdjusted) + return; + + // Adjust components according to the insets + setSize(getInsets().left + getInsets().right + d.width, getInsets().top + getInsets().bottom + d.height); + Component components[] = getComponents(); + for (Component component : components) { + Point p = component.getLocation(); + p.translate(getInsets().left, getInsets().top); + component.setLocation(p); + } + fComponentsAdjusted = true; + } + + void DbMang_WindowClosing(java.awt.event.WindowEvent event) { + setVisible(false); // hide the Manager + dispose(); // free the system resources + System.exit(0); // close the application + } + + void miAbout_Action(java.awt.event.ActionEvent event) { + // Action from About Create and show as modal + (new AboutDialog(this, true)).setVisible(true); + } + + void miExit_Action(java.awt.event.ActionEvent event) { + // Action from Exit Create and show as modal + //(new hayashi.yuu.tools.gui.QuitDialog(this, true)).setVisible(true); + (new QuitDialog(this, true)).setVisible(true); + } + + void toEnable(final int cardNo, final boolean enable) { + if ((cardNo >= 0) && (cardNo < cards.length)) { + cardPanel.setEnabledAt(cardNo, enable); + if ((cardNo -1) >= 0) { + cards[cardNo -1].nextButton.setEnabled(enable); + } + if ((cardNo +1) < cards.length) { + cardPanel.setEnabledAt(cardNo+1, enable); + cards[cardNo +1].backButton.setEnabled(enable); + cards[cardNo].nextButton.setEnabled(enable); + } + } + } + + //ImageIcon refImage; + + /** Returns an ImageIcon, or null if the path was invalid. + * @param path + * @return */ + public static ImageIcon createImageIcon(String path) { + java.net.URL imgURL = ReStamp.class.getResource(path); + if (imgURL != null) { + return new ImageIcon(imgURL); + } else { + System.err.println("Couldn't find file: " + path); + return null; + } + } +} diff --git a/src/main/java/osm/surveyor/matchtime/gui/SimpleDocumentListener.java b/src/main/java/osm/surveyor/matchtime/gui/SimpleDocumentListener.java new file mode 100644 index 0000000..9a2bfbf --- /dev/null +++ b/src/main/java/osm/surveyor/matchtime/gui/SimpleDocumentListener.java @@ -0,0 +1,25 @@ +package osm.surveyor.matchtime.gui; + +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; + +@FunctionalInterface +public interface SimpleDocumentListener extends DocumentListener { + void update(DocumentEvent e); + + @Override + default void insertUpdate(DocumentEvent e) { + update(e); + } + + @Override + default void removeUpdate(DocumentEvent e) { + update(e); + } + + @Override + default void changedUpdate(DocumentEvent e) { + update(e); + } +} + diff --git a/src/main/java/osm/surveyor/matchtime/gui/Utils.java b/src/main/java/osm/surveyor/matchtime/gui/Utils.java new file mode 100644 index 0000000..48cd907 --- /dev/null +++ b/src/main/java/osm/surveyor/matchtime/gui/Utils.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Oracle or the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package osm.surveyor.matchtime.gui; + +import java.io.File; +import javax.swing.ImageIcon; + +/* Utils.java is used by FileChooserDemo2.java. */ +public class Utils { + + /** + * + */ + public final static String JPEG = "jpeg"; + public final static String JPG = "jpg"; + public final static String GIF = "gif"; + public final static String TIFF = "tiff"; + public final static String TIF = "tif"; + public final static String PNG = "png"; + + /* + * Get the extension of a file. + */ + public static String getExtension(File f) { + String ext = null; + String s = f.getName(); + int i = s.lastIndexOf('.'); + + if (i > 0 && i < s.length() - 1) { + ext = s.substring(i+1).toLowerCase(); + } + return ext; + } + + /** Returns an ImageIcon, or null if the path was invalid. + * @param path + * @return + */ + protected static ImageIcon createImageIcon(String path) { + java.net.URL imgURL = Utils.class.getResource(path); + if (imgURL != null) { + return new ImageIcon(imgURL); + } else { + System.err.println("Couldn't find file: " + path); + return null; + } + } +} diff --git a/src/main/java/osm/surveyor/matchtime/gui/restamp/CardImageFile.java b/src/main/java/osm/surveyor/matchtime/gui/restamp/CardImageFile.java new file mode 100644 index 0000000..968137a --- /dev/null +++ b/src/main/java/osm/surveyor/matchtime/gui/restamp/CardImageFile.java @@ -0,0 +1,89 @@ +package osm.surveyor.matchtime.gui.restamp; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.Window; +import javax.swing.BoxLayout; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTabbedPane; +import static osm.surveyor.matchtime.gui.ReStamp.i18n; +import osm.surveyor.matchtime.gui.Card; +import osm.surveyor.matchtime.gui.PanelAction; +import osm.surveyor.matchtime.gui.ParameterPanelImageFile; +import osm.surveyor.matchtime.gui.ParameterPanelTime; + +/** + * [基準画像(開始/終了)]選択パネル + * @author yuu + */ +public class CardImageFile extends Card implements PanelAction { + ParameterPanelImageFile arg_baseTimeImg; + ParameterPanelTime arg_basetime; + + /** + * コンストラクタ + * @param tabbe parent panel + * @param arg_basetime // 開始画像の基準時刻: + * @param owner + * @param text + * @param pre + * @param next + */ + public CardImageFile( + JTabbedPane tabbe, + ParameterPanelTime arg_basetime, + Window owner, + String text, + int pre, int next + ) { + super(tabbe, text, pre, next); + arg_basetime.setOwner(owner); + this.arg_baseTimeImg = arg_basetime.getImageFile(); + this.arg_basetime = arg_basetime; + + JPanel argsPanel = new JPanel(); + argsPanel.setLayout(new BoxLayout(argsPanel, BoxLayout.PAGE_AXIS)); + argsPanel.add(packLine(new JLabel(i18n.getString("label.200")), new JPanel())); + argsPanel.add(arg_baseTimeImg); + + JPanel separater = new JPanel(); + separater.setMinimumSize(new Dimension(40, 20)); + argsPanel.add(separater); + + argsPanel.add(packLine(new JLabel(i18n.getString("label.300")), new JPanel())); + argsPanel.add(arg_basetime); + + // ラジオボタン: 「EXIF日時を基準にする」 + if (arg_basetime.exifBase != null) { + argsPanel.add(arg_basetime.exifBase); + } + + // ラジオボタン: 「File更新日時を基準にする」 + if (arg_basetime.fupdateBase != null) { + argsPanel.add(arg_basetime.fupdateBase); + } + + JPanel space = new JPanel(); + space.setMinimumSize(new Dimension(40, 20)); + space.setMaximumSize(new Dimension(40, Short.MAX_VALUE)); + argsPanel.add(space); + + this.mainPanel.add(argsPanel, BorderLayout.CENTER); + } + + /** + * 入力条件が満たされているかどうか + * @return + */ + @Override + public boolean isEnable() { + return (arg_baseTimeImg.isEnable() && arg_basetime.isEnable()); + } + + @Override + @SuppressWarnings("empty-statement") + public void openAction() { + ; // 何もしない + } +} diff --git a/src/main/java/osm/surveyor/matchtime/gui/restamp/CardPerformFile.java b/src/main/java/osm/surveyor/matchtime/gui/restamp/CardPerformFile.java new file mode 100644 index 0000000..67b6ea9 --- /dev/null +++ b/src/main/java/osm/surveyor/matchtime/gui/restamp/CardPerformFile.java @@ -0,0 +1,134 @@ +package osm.surveyor.matchtime.gui.restamp; + +import java.awt.BorderLayout; +import java.io.File; +import java.util.ArrayList; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTabbedPane; +import osm.surveyor.matchtime.gui.ReStamp; +import static osm.surveyor.matchtime.gui.ReStamp.i18n; +import osm.surveyor.matchtime.gui.Card; +import osm.surveyor.matchtime.gui.PanelAction; +import osm.surveyor.matchtime.gui.ParameterPanelOutput; +import osm.surveyor.matchtime.gui.ParameterPanelTime; + +/** + * [基準画像(開始/終了)]選択パネル + * @author yuu + */ +public class CardPerformFile extends Card implements PanelAction { + //JPanel argsPanel; // パラメータ設定パネル (上部) + ParameterPanelTime arg1_basetime; + ParameterPanelTime arg2_basetime; + ParameterPanelOutput arg_output; // EXIF & 書き出しフォルダ + JButton doButton; // [処理実行]ボタン + + /** + * コンストラクタ + * @param tabbe parent panel + * @param arg1_basetime // 開始画像の基準時刻: + * @param arg2_basetime // 開始画像の基準時刻: + * @param arg_output // EXIF & 書き出しフォルダ + * @param text + * @param pre + * @param next + */ + public CardPerformFile( + JTabbedPane tabbe, + ParameterPanelTime arg1_basetime, + ParameterPanelTime arg2_basetime, + ParameterPanelOutput arg_output, + String text, + int pre, int next + ) { + super(tabbe, text, pre, next); + this.arg1_basetime = arg1_basetime; + this.arg2_basetime = arg2_basetime; + this.arg_output = arg_output; + + JPanel argsPanel = new JPanel(); + argsPanel.setLayout(new BoxLayout(argsPanel, BoxLayout.PAGE_AXIS)); + argsPanel.add(packLine(new JLabel(i18n.getString("label.200")), new JPanel())); + + // 出力フォルダ + // 5. 変換先のフォルダを選択してください。 + // - 変換先フォルダには、書き込み権限と、十分な空き容量が必要です。 + JLabel label5 = new JLabel(); + label5.setText( + String.format( + "

%s

  • %s
", + i18n.getString("label.restamp.500"), + i18n.getString("label.restamp.501") + ) + ); + argsPanel.add(packLine(label5, new JPanel())); + argsPanel.add(arg_output); + + // [処理実行]ボタン + doButton = new JButton( + i18n.getString("button.execute"), + ReStamp.createImageIcon("images/media_playback_start.png") + ); + argsPanel.add(doButton); + + this.mainPanel.add(argsPanel, BorderLayout.CENTER); + + //{{REGISTER_LISTENERS + SymAction lSymAction = new SymAction(); + doButton.addActionListener(lSymAction); + //}} + } + + class SymAction implements java.awt.event.ActionListener { + @Override + public void actionPerformed(java.awt.event.ActionEvent event) { + Object object = event.getSource(); + if (object == doButton) { + doButton_Action(event); + } + } + } + + /** + * [実行]ボタンをクリックしたときの動作 + * @param event + */ + @SuppressWarnings("UseSpecificCatch") + void doButton_Action(java.awt.event.ActionEvent event) { + doButton.setEnabled(false); + + ArrayList arry = new ArrayList<>(); + File file = arg1_basetime.getImageFile().getImageFile(); + File dir = file.getParentFile(); + arry.add(dir.getAbsolutePath()); + arry.add(file.getName()); + arry.add(arg1_basetime.argField.getText()); + file = arg2_basetime.getImageFile().getImageFile(); + arry.add(file.getName()); + arry.add(arg2_basetime.argField.getText()); + arry.add(arg_output.getText()); + + String[] argv = arry.toArray(new String[arry.size()]); + (new DoRestamp(argv)).setVisible(true); + + doButton.setEnabled(true); + } + + /** + * 入力条件が満たされているかどうか + * @return + */ + @Override + public boolean isEnable() { + return (arg1_basetime.isEnable() && arg2_basetime.isEnable()); + } + + @Override + @SuppressWarnings("empty-statement") + public void openAction() { + ; // 何もしない + } +} diff --git a/src/main/java/osm/surveyor/matchtime/gui/restamp/CardSourceFolder.java b/src/main/java/osm/surveyor/matchtime/gui/restamp/CardSourceFolder.java new file mode 100644 index 0000000..dcfec52 --- /dev/null +++ b/src/main/java/osm/surveyor/matchtime/gui/restamp/CardSourceFolder.java @@ -0,0 +1,51 @@ +package osm.surveyor.matchtime.gui.restamp; + +import java.awt.BorderLayout; +import javax.swing.BoxLayout; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTabbedPane; +import osm.surveyor.matchtime.gui.ReStamp; +import static osm.surveyor.matchtime.gui.ReStamp.i18n; +import osm.surveyor.matchtime.gui.Card; +import osm.surveyor.matchtime.gui.PanelAction; +import osm.surveyor.matchtime.gui.ParameterPanelFolder; + +/** + * [対象フォルダ]設定パネル + * @author yuu + */ +public class CardSourceFolder extends Card implements PanelAction { + ParameterPanelFolder arg_srcFolder; // 対象フォルダ + + /** + * コンストラクタ + * @param tabbe parent panel + * @param arg_srcFolder 対象フォルダ + */ + public CardSourceFolder(JTabbedPane tabbe, ParameterPanelFolder arg_srcFolder) { + super(tabbe, ReStamp.i18n.getString("tab.100"), -1, 1); + this.arg_srcFolder = arg_srcFolder; + this.mainPanel.add(new JLabel(i18n.getString("label.100")), BorderLayout.NORTH); + + JPanel argsPanel = new JPanel(); // パラメータ設定パネル (上部) + argsPanel.setLayout(new BoxLayout(argsPanel, BoxLayout.Y_AXIS)); + argsPanel.add(arg_srcFolder); + this.mainPanel.add(argsPanel, BorderLayout.CENTER); + } + + /** + * 入力条件が満たされているかどうか + * @return + */ + @Override + public boolean isEnable() { + return this.arg_srcFolder.isEnable(); + } + + @Override + @SuppressWarnings("empty-statement") + public void openAction() { + ; // 何もしない + } +} diff --git a/src/main/java/osm/surveyor/matchtime/gui/restamp/DialogCorectTime.java b/src/main/java/osm/surveyor/matchtime/gui/restamp/DialogCorectTime.java new file mode 100644 index 0000000..c66751b --- /dev/null +++ b/src/main/java/osm/surveyor/matchtime/gui/restamp/DialogCorectTime.java @@ -0,0 +1,230 @@ +package osm.surveyor.matchtime.gui.restamp; + +import java.awt.BorderLayout; +import java.awt.Dialog; +import java.awt.GridLayout; +import java.awt.Image; +import java.awt.Rectangle; +import java.awt.Window; +import javax.swing.BoxLayout; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import osm.surveyor.matchtime.gui.ReStamp; +import static osm.surveyor.matchtime.gui.ReStamp.createImageIcon; +import static osm.surveyor.matchtime.gui.ReStamp.i18n; +import osm.surveyor.matchtime.gui.PanelAction; +import osm.surveyor.matchtime.gui.ParameterPanelTime; + +/** + * [基準画像(開始)]選択パネル + * @author yuu + */ +public class DialogCorectTime extends JDialog implements PanelAction { + public JPanel mainPanel; + ParameterPanelTime arg_basetime; // 開始画像の基準時刻(parent) + ParameterPanelTime basetime; // 開始画像の基準時刻(tempolarry) + java.awt.Button closeButton; + JButton expandButton; + JButton zoomInButton; + JButton zoomOutButton; + JLabel imageLabel; // 開始画像の基準時刻画像表示 + JScrollPane imageSPane; // スクロールパネル + + /** + * コンストラクタ + * @param arg3_basetime 開始画像の基準時刻: + * @param owner + */ + @SuppressWarnings("OverridableMethodCallInConstructor") + public DialogCorectTime(ParameterPanelTime arg3_basetime, Window owner) { + super(owner, ReStamp.i18n.getString("tab.restamp.300"), Dialog.ModalityType.DOCUMENT_MODAL); + this.arg_basetime = arg3_basetime; + + // INIT_CONTROLS + setLayout(new BorderLayout()); + setSize( + getInsets().left + getInsets().right + 720, + getInsets().top + getInsets().bottom + 480 + ); + + //---- CENTER ----- + JPanel centerPanel = new JPanel(); + centerPanel.setLayout(new BorderLayout()); + add(centerPanel, BorderLayout.CENTER); + + //---- CENTER.NORTH ----- + JPanel argsPanel; // パラメータ設定パネル (上部) + argsPanel = new JPanel(); + argsPanel.setLayout(new GridLayout(2, 1)); + + // 3. 正確な撮影時刻を入力してください。 + // カメラの時計が正確ならば、設定を変更する必要はありません。 + JLabel label3 = new JLabel(); + label3.setText(i18n.getString("label.300")); + argsPanel.add(label3); + + basetime = new ParameterPanelTime( + arg_basetime.argLabel.getText(), + "", + arg_basetime.getImageFile() + ); + basetime.updateButton.setVisible(false); + basetime.resetButton.setVisible(true); + argsPanel.add(basetime); + centerPanel.add(argsPanel, BorderLayout.NORTH); + + //---- CENTER.CENTER ----- + // 参考画像 + imageLabel = new JLabel(); + imageSPane = new JScrollPane(imageLabel); + centerPanel.add(imageSPane, BorderLayout.CENTER); + + //---- CENTER.SOUTH ----- + // 画像ファイル選択ダイアログを起動するボタン + JPanel buttonPanel = new JPanel(); + buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS)); + expandButton = new JButton(createImageIcon(java.util.ResourceBundle.getBundle("i18n_ja_JP").getString("IMAGES/FIT16.GIF"))); + buttonPanel.add(expandButton); + zoomInButton = new JButton(createImageIcon("images/ZoomIn16.gif")); + buttonPanel.add(zoomInButton); + zoomOutButton = new JButton(createImageIcon("images/ZoomOut16.gif")); + buttonPanel.add(zoomOutButton); + centerPanel.add(buttonPanel, BorderLayout.SOUTH); + + //---- SOUTH ----- + closeButton = new java.awt.Button(); + closeButton.setLabel(i18n.getString("button.close") ); + closeButton.setBounds(145,65,66,27); + add(closeButton, BorderLayout.SOUTH); + + // 選択された画像ファイルを表示する + imageView_Action(); + + //{{REGISTER_LISTENERS + SymWindow aSymWindow = new SymWindow(); + this.addWindowListener(aSymWindow); + SymAction lSymAction = new SymAction(); + closeButton.addActionListener(lSymAction); + expandButton.addActionListener(lSymAction); + zoomInButton.addActionListener(lSymAction); + zoomOutButton.addActionListener(lSymAction); + //}} + } + + class SymWindow extends java.awt.event.WindowAdapter + { + @Override + public void windowClosing(java.awt.event.WindowEvent event) { + Object object = event.getSource(); + if (object == DialogCorectTime.this) { + dialog_WindowClosing(); + } + } + } + + class SymAction implements java.awt.event.ActionListener + { + @Override + public void actionPerformed(java.awt.event.ActionEvent event) { + Object object = event.getSource(); + if (object == closeButton) { + dialog_WindowClosing(); + } + else if (object == expandButton) { + imageView_Action(); + } + else if (object == zoomInButton) { + zoomin_Action(); + } + else if (object == zoomOutButton) { + zoomout_Action(); + } + } + } + + ImageIcon refImage; + + /** + * 選択された画像ファイルを表示する + * 基準画像ボタンがクリックされた時に、基準時刻フィールドに基準画像の作成日時を設定する。 + */ + @SuppressWarnings("UseSpecificCatch") + public void imageView_Action() { + try { + String path = basetime.getImageFile().getImageFile().getAbsolutePath(); + + // View Image File + int size_x = imageSPane.getWidth() - 8; + ImageIcon tmpIcon = new ImageIcon(path); + refImage = tmpIcon; + if (tmpIcon.getIconWidth() > size_x) { + refImage = new ImageIcon(tmpIcon.getImage().getScaledInstance(size_x, -1, Image.SCALE_DEFAULT)); + } + imageLabel.setIcon(refImage); + } + catch(NullPointerException e) { + // 何もしない + } + repaint(); + } + + public void zoomin_Action() { + if (refImage != null) { + int size_x = imageLabel.getWidth(); + String path = basetime.getImageFile().getImageFile().getAbsolutePath(); + ImageIcon tmpIcon = new ImageIcon(path); + refImage = new ImageIcon(tmpIcon.getImage().getScaledInstance(size_x * 2, -1, Image.SCALE_DEFAULT)); + imageLabel.setIcon(refImage); + repaint(); + } + } + + public void zoomout_Action() { + if (refImage != null) { + int size_x = imageLabel.getWidth(); + ImageIcon tmpIcon = refImage; + refImage = new ImageIcon(tmpIcon.getImage().getScaledInstance(size_x / 2, -1, Image.SCALE_DEFAULT)); + imageLabel.setIcon(refImage); + repaint(); + } + } + + /** + * ダイアログが閉じられるときのアクション + */ + void dialog_WindowClosing() { + String workStr = basetime.getText(); + arg_basetime.setText(workStr); + dispose(); + } + + @Override + public void setVisible(boolean b) { + if(b) { + Rectangle bounds = getParent().getBounds(); + Rectangle abounds = getBounds(); + setLocation(bounds.x + (bounds.width - abounds.width)/ 2, + bounds.y + (bounds.height - abounds.height)/2); + } + super.setVisible(b); + } + + @Override + @SuppressWarnings("empty-statement") + public void openAction() { + ; // 何もしない + } + + /** + * 入力条件が満たされているかどうか + * @return + */ + @Override + public boolean isEnable() { + return this.basetime.isEnable(); + } +} diff --git a/src/main/java/osm/surveyor/matchtime/gui/restamp/DoRestamp.java b/src/main/java/osm/surveyor/matchtime/gui/restamp/DoRestamp.java new file mode 100644 index 0000000..6e260a8 --- /dev/null +++ b/src/main/java/osm/surveyor/matchtime/gui/restamp/DoRestamp.java @@ -0,0 +1,227 @@ +package osm.surveyor.matchtime.gui.restamp; +import osm.surveyor.matchtime.gui.Command; +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Point; +import java.awt.event.ActionEvent; +import java.io.*; +import javax.swing.*; + +/** + * 処理 + */ +@SuppressWarnings("serial") +public class DoRestamp extends JDialog { + public static final String TITLE = "Do Command"; + + // Used for addNotify check. + boolean fComponentsAdjusted = false; + String[] args; + + //{{DECLARE_CONTROLS + JPanel buttonPanel; // ボタン配置パネル (下部) + JButton closeButton; // [クローズ]ボタン + JButton doButton; // [実行]ボタン + JTextArea textArea; // 実行結果を表示するJTextArea (中央) + //}} + + @SuppressWarnings("OverridableMethodCallInConstructor") + public DoRestamp(String[] args) { + super(); // 親フォームなしのモーダルダイアログを基盤にする + this.args = args; + + // INIT_CONTROLS + @SuppressWarnings("OverridableMethodCallInConstructor") + Container container = getContentPane(); + container.setLayout(new BorderLayout()); + setSize(getInsets().left + getInsets().right + 980, getInsets().top + getInsets().bottom + 480); + setTitle(DoRestamp.TITLE); + + // コントロールパネル + buttonPanel = new JPanel(); + + doButton = new JButton("実行"); + doButton.setToolTipText("処理を実行します."); + doButton.setEnabled(true); + doButton.addActionListener((ActionEvent event) -> { + // 処理中であることを示すため + // ボタンの文字列を変更し,使用不可にする + doButton.setText("処理中..."); + doButton.setEnabled(false); + + // SwingWorker を生成し,実行する + LongTaskWorker worker = new LongTaskWorker(doButton); + worker.execute(); + }); + buttonPanel.add(doButton); + + closeButton = new JButton("閉じる"); + closeButton.setToolTipText("処理を終了します."); + closeButton.addActionListener((ActionEvent event) -> { + dispose(); + }); + buttonPanel.add(closeButton); + + this.getContentPane().add("South", buttonPanel); + + // 説明文 + textArea = new JTextArea(); + JScrollPane sc=new JScrollPane(textArea); + textArea.setFont(new Font(Font.MONOSPACED,Font.PLAIN,12)); + textArea.setTabSize(4); + this.getContentPane().add("Center", sc); + + try { + textArea.append("> java -jar ReStamp.jar osm.surveyor.matchtime.Restamp"); + for (String arg : args) { + textArea.append(" '" + arg + "'"); + } + textArea.append("\n\n"); + } + catch (Exception e) { + System.out.println(e.toString()); + } + } + + /** + * Shows or hides the component depending on the boolean flag b. + * @param b trueのときコンポーネントを表示; その他のとき, componentを隠す. + * @see java.awt.Component#isVisible + */ + @Override + public void setVisible(boolean b) { + if(b) { + setLocation(80, 80); + } + super.setVisible(b); + } + + @Override + public void addNotify() { + // Record the size of the window prior to calling parents addNotify. + Dimension d = getSize(); + + super.addNotify(); + + if (fComponentsAdjusted) { + return; + } + + // Adjust components according to the insets + setSize(getInsets().left + getInsets().right + d.width, getInsets().top + getInsets().bottom + d.height); + Component components[] = getComponents(); + for (Component component : components) { + Point p = component.getLocation(); + p.translate(getInsets().left, getInsets().top); + component.setLocation(p); + } + fComponentsAdjusted = true; + } + + + /** + * JTextAreaに書き出すOutputStream + */ + public static class JTextAreaOutputStream extends OutputStream { + private final ByteArrayOutputStream os; + + /** 書き出し対象 */ + private final JTextArea textArea; + private final String encode; + + public JTextAreaOutputStream(JTextArea textArea, String encode) { + this.textArea = textArea; + this.encode = encode; + this.os = new ByteArrayOutputStream(); + } + + /** + * OutputStream#write(byte[])のオーバーライド + * @param arg + * @throws java.io.IOException + */ + @Override + public void write(int arg) throws IOException { + this.os.write(arg); + } + + /** + * flush()でJTextAreaに書き出す + * @throws java.io.IOException + */ + @Override + public void flush() throws IOException { + // 文字列のエンコード + final String str = new String(this.os.toByteArray(), this.encode); + // 実際の書き出し処理 + SwingUtilities.invokeLater( + new Runnable(){ + @Override + public void run() { + JTextAreaOutputStream.this.textArea.append(str); + } + } + ); + // 書き出した内容はクリアする + this.os.reset(); + } + } + + // 非同期に行う処理を記述するためのクラス + class LongTaskWorker extends SwingWorker { + private final JButton button; + + public LongTaskWorker(JButton button) { + this.button = button; + } + + // 非同期に行われる処理 + @Override + @SuppressWarnings("SleepWhileInLoop") + public Object doInBackground() { + // ながーい処理 + PrintStream defOut = System.out; + PrintStream defErr = System.err; + + OutputStream os = new JTextAreaOutputStream(textArea, "UTF-8"); + PrintStream stdout = new PrintStream(os, true); // 自動flushをtrueにしておく + + // System.out にJTextAreaOutputStreamに書き出すPrintStreamを設定 + System.setOut(stdout); + System.setErr(stdout); + + try { + Command command = new Command(osm.surveyor.matchtime.Restamp.class); + command.setArgs(args); + command.start(); // コマンドを実行 + while (command.isAlive()) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) {} + } + } + catch(Exception e) { + e.printStackTrace(stdout); + } + finally { + System.setOut(defOut); + System.setErr(defErr); + button.setEnabled(true); + } + + return null; + } + + // 非同期処理後に実行 + @Override + protected void done() { + // 処理が終了したので,文字列を元に戻し + // ボタンを使用可能にする + button.setText("実行"); + button.setEnabled(true); + } + } +} diff --git a/src/main/java/osm/surveyor/util/Exif.java b/src/main/java/osm/surveyor/util/Exif.java new file mode 100644 index 0000000..a7ac358 --- /dev/null +++ b/src/main/java/osm/surveyor/util/Exif.java @@ -0,0 +1,80 @@ +package osm.surveyor.util; + +import java.io.*; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + +public class Exif extends Thread { + public static final String TIME_FORMAT_STRING = "yyyy-MM-dd'T'HH:mm:ss'Z'"; + private static final String EXIF_DATE_TIME_FORMAT_STRING = "yyyy:MM:dd HH:mm:ss"; + + /** + * 対象は '*.JPG' のみ対象とする + * @return + * @param name + */ + public static boolean checkFile(String name) { + return ((name != null) && name.toUpperCase().endsWith(".JPG")); + } + + /** + * DateをEXIFの文字列に変換する。 + * 注意:EXiFの撮影時刻はUTC時間ではない + * @param localdate + * @return + */ + public static String toEXIFString(Date localdate) { + DateFormat dfUTC = new SimpleDateFormat(EXIF_DATE_TIME_FORMAT_STRING); + return dfUTC.format(localdate); + } + + /** + * EXIFの文字列をDateに変換する。 + * 注意:EXiFの撮影時刻はUTC時間ではない + * @param timeStr + * @return + * @throws ParseException + */ + public static Date toEXIFDate(String timeStr) throws ParseException { + DateFormat dfUTC = new SimpleDateFormat(EXIF_DATE_TIME_FORMAT_STRING); + //dfUTC.setTimeZone(TimeZone.getTimeZone("UTC")); + return dfUTC.parse(timeStr); + } + + public static String toUTCString(Date localdate) { + DateFormat dfUTC = new SimpleDateFormat(TIME_FORMAT_STRING); + dfUTC.setTimeZone(TimeZone.getTimeZone("UTC")); + return dfUTC.format(localdate); + } + + public static Date toUTCDate(String timeStr) throws ParseException { + DateFormat dfUTC = new SimpleDateFormat(TIME_FORMAT_STRING); + dfUTC.setTimeZone(TimeZone.getTimeZone("UTC")); + return dfUTC.parse(timeStr); + } + + static String getShortPathName(File dir, File iFile) { + String dirPath = dir.getAbsolutePath(); + String filePath = iFile.getAbsolutePath(); + if (filePath.startsWith(dirPath)) { + return filePath.substring(dirPath.length()+1); + } + else { + return filePath; + } + } + + /** + * JPEGファイルフィルター + * @author yuu + */ + class JpegFileFilter implements FilenameFilter { + @Override + public boolean accept(File dir, String name) { + return name.toUpperCase().matches(".*\\.JPG$"); + } + } +} \ No newline at end of file diff --git a/src/main/java/osm/surveyor/util/GeoDistance.java b/src/main/java/osm/surveyor/util/GeoDistance.java new file mode 100644 index 0000000..960794f --- /dev/null +++ b/src/main/java/osm/surveyor/util/GeoDistance.java @@ -0,0 +1,71 @@ +package osm.surveyor.util; + +/** + * The MIT License (MIT) + * Copyright(C) 2007-2012 やまだらけ + * http://yamadarake.jp/trdi/report000001.html + * 「Cords.java」を改変 + * 2016-10-03 + * + * @author やまだらけ yama_darake@yahoo.co.jp + * + */ +public class GeoDistance { + + public static final double GRS80_A = 6378137.000; // 赤道半径(m) + public static final double GRS80_E2 = 0.00669438002301188; + public static final double GRS80_MNUM = 6335439.32708317; // + + public static final double WGS84_A = 6378137.000; + public static final double WGS84_E2 = 0.00669437999019758; + public static final double WGS84_MNUM = 6335439.32729246; + + /** + * 角度(180度)をラジアン(2π)に変換する + * @param deg + * @return + */ + public static double deg2rad(double deg){ + return deg * Math.PI / 180.0; + } + + /** + * 距離(m)を返す + * @param lat1 + * @param lng1 + * @param lat2 + * @param lng2 + * @return + */ + public static double calcDistHubeny(double lat1, double lng1, + double lat2, double lng2){ + double my = deg2rad((lat1 + lat2) / 2.0); // 平均緯度 + double dy = deg2rad(lat1 - lat2); // 2点間の緯度 + double dx = deg2rad(lng1 - lng2); // 2点間の経度 + + double sin = Math.sin(my); + double w = Math.sqrt(1.0 - GRS80_E2 * sin * sin); + double m = GRS80_MNUM / (w * w * w); + double n = GRS80_A / w; + + double dym = dy * m; + double dxncos = dx * n * Math.cos(my); + + return Math.sqrt(dym * dym + dxncos * dxncos); + } + + + public static void main(String[] args){ + System.out.println("Coords Test Program"); + double lat1, lng1, lat2, lng2; + + lat1 = Double.parseDouble(args[0]); + lng1 = Double.parseDouble(args[1]); + lat2 = Double.parseDouble(args[2]); + lng2 = Double.parseDouble(args[3]); + + double d = calcDistHubeny(lat1, lng1, lat2, lng2); + + System.out.println("Distance = " + d + " m"); + } +} \ No newline at end of file diff --git a/src/main/java/osm/surveyor/util/TimeComparator.java b/src/main/java/osm/surveyor/util/TimeComparator.java new file mode 100644 index 0000000..c6997e0 --- /dev/null +++ b/src/main/java/osm/surveyor/util/TimeComparator.java @@ -0,0 +1,21 @@ +package osm.surveyor.util; + +import java.util.Comparator; +import java.util.Date; + +/** + * java.util.Date型をコレクションのKEYにした時に、時間順に並べ替える + * + */ +public class TimeComparator implements Comparator +{ + /** + * 日付順にソート + * @param arg0 + * @param arg1 + */ + @Override + public int compare(Date arg0, Date arg1) { + return arg0.compareTo(arg1); + } +} diff --git a/src/main/java/osm/surveyor/util/YuuLogFormatter.java b/src/main/java/osm/surveyor/util/YuuLogFormatter.java new file mode 100644 index 0000000..f78bf16 --- /dev/null +++ b/src/main/java/osm/surveyor/util/YuuLogFormatter.java @@ -0,0 +1,48 @@ +package osm.surveyor.util; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.logging.Formatter; +import java.util.logging.Level; +import java.util.logging.LogRecord; + +/** + * シンプルなサンプルログフォーマッタ + */ +public class YuuLogFormatter extends Formatter { + private final SimpleDateFormat sdFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + + @Override + public String format(final LogRecord argLogRecord) { + final StringBuffer buf = new StringBuffer(); + + buf.append(sdFormat.format(new Date(argLogRecord.getMillis()))).append(" "); + + if (argLogRecord.getLevel() == Level.FINEST) { + buf.append("[FINEST]"); + } + else if (argLogRecord.getLevel() == Level.FINER) { + buf.append("[FINER]"); + } + else if (argLogRecord.getLevel() == Level.FINE) { + buf.append("[FINE]"); + } + else if (argLogRecord.getLevel() == Level.CONFIG) { + buf.append("[CONFIG]"); + } + else if (argLogRecord.getLevel() == Level.INFO) { + buf.append("[INFO]"); + } + else if (argLogRecord.getLevel() == Level.WARNING) { + buf.append("[WARN]"); + } + else if (argLogRecord.getLevel() == Level.SEVERE) { + buf.append("[SEVERE]"); + } + else { + buf.append(Integer.toString(argLogRecord.getLevel().intValue())).append(" "); + } + buf.append(" ").append(argLogRecord.getMessage()).append("\n"); + return buf.toString(); + } +} \ No newline at end of file diff --git a/src/main/resources/AdjustTime.ini b/src/main/resources/AdjustTime.ini new file mode 100644 index 0000000..3d439b1 --- /dev/null +++ b/src/main/resources/AdjustTime.ini @@ -0,0 +1,13 @@ +#by AdjustTime +GPX.BASETIME=EXIF_TIME +IMG.OUTPUT_EXIF=true +GPX.OUTPUT_SPEED=false +GPX.noFirstNode=true +IMG.OUTPUT=false +GPX.gpxSplit=true +IMG.TIME=2016-08-14T11\:45\:47 +GPX.REUSE=true +IMG.BASE_FILE=IMG_0182.jpg +IMG.SOURCE_FOLDER=. +GPX.SOURCE_FOLDER=. +IMG.OUTPUT_FOLDER=. \ No newline at end of file diff --git a/src/main/resources/AdjustTime2.bat b/src/main/resources/AdjustTime2.bat new file mode 100644 index 0000000..96ed170 --- /dev/null +++ b/src/main/resources/AdjustTime2.bat @@ -0,0 +1 @@ +javaw -cp .;AdjustTime2.jar;commons-compress-1.14.jar;commons-imaging-1.0-20170205.201009-115.jar osm.jp.gpx.matchtime.gui.AdjustTime diff --git a/src/main/resources/AdjustTime2.jnlp b/src/main/resources/AdjustTime2.jnlp new file mode 100644 index 0000000..d865797 --- /dev/null +++ b/src/main/resources/AdjustTime2.jnlp @@ -0,0 +1,35 @@ + + + + + SwingSet2 Demo Application + Sun Microsystems, Inc. + + SwingSet2 Demo Application + A demo of the capabilities +of the Swing Graphical User Interface. + + + + + + + + + + + + + + SwingSet2 Demo on Linux + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/AdjustTime2.sh b/src/main/resources/AdjustTime2.sh new file mode 100644 index 0000000..bae5196 --- /dev/null +++ b/src/main/resources/AdjustTime2.sh @@ -0,0 +1 @@ +javaw -cp .:AdjustTime2.jar:commons-compress-1.14.jar:commons-imaging-1.0-20170205.201009-115.jar osm.jp.gpx.matchtime.gui.AdjustTime diff --git a/src/main/resources/LICENSE.txt b/src/main/resources/LICENSE.txt new file mode 100644 index 0000000..a37e276 --- /dev/null +++ b/src/main/resources/LICENSE.txt @@ -0,0 +1,43 @@ +The MIT License (MIT) + +Copyright (c) 2014 Yuu Hayashi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +以下に定める条件に従い、本ソフトウェアおよび関連文書のファイル(以下「ソフトウェア」)の複製を取得するすべて +の人に対し、ソフトウェアを無制限に扱うことを無償で許可します。これには、ソフトウェアの複製を使用、複写、変 +更、結合、掲載、頒布、サブライセンス、および/または販売する権利、およびソフトウェアを提供する相手に同じこ +とを許可する権利も無制限に含まれます。 + +上記の著作権表示および本許諾表示を、ソフトウェアのすべての複製または重要な部分に記載するものとします。 + +ソフトウェアは「現状のまま」で、明示であるか暗黙であるかを問わず、何らの保証もなく提供されます。ここでいう保証 +とは、商品性、特定の目的への適合性、および権利非侵害についての保証も含みますが、それに限定されるもので +はありません。 作者または著作権者は、契約行為、不法行為、またはそれ以外であろうと、ソフトウェアに起因または +関連し、あるいはソフトウェアの使用またはその他の扱いによって生じる一切の請求、損害、その他の義務について何 +らの責任も負わないものとします。 + +---------------- + +osm.jp.gpx.GeoDistance.java は'やまだらけ'様の著作物です。 + Copyright (C) 2007-2012 やまだらけ + The MIT License (MIT) + 参照元: http://yamadarake.jp/trdi/report000001.html + 「Cords.java」を改変 diff --git a/src/main/resources/README.jp.txt b/src/main/resources/README.jp.txt new file mode 100644 index 0000000..2749310 --- /dev/null +++ b/src/main/resources/README.jp.txt @@ -0,0 +1,128 @@ +[ AdjustTime ] + +GPSログファイル(GPX)を元にして写真へ「位置情報(緯度経度)」と「方向」を追記します。(EXIF更新) + +[概要] +GPSログの記録時刻とデジカメの撮影時刻とを見比べて、GPSログ内に写真へのリンク情報を付加した新しいGPSログファイルを作成します。 + + ※ 対象とする画像ファイルは'*.jpg'のみです。 + ※ GPSログの形式は「GPX」形式に対応しています。 + * 画像ファイルの撮影日時をファイルの更新日時/EXIF撮影日時から選択することができます。 + - ファイル更新日時: 高速処理が可能です。 + 一部のトイカメラ系のデジカメにはEXIF情報が正しく付加されないものがあります。そのような機種におすすめです。 + - EXIF撮影日時: ファイル更新日時が利用できない場合はこちらを使ってください。 + iPadなど直接ファイルを扱えないデバイスの場合はファイル更新日時が使えません。 + うっかりファイルをコピーしてしまった場合は、ファイル更新日時が撮影日時を意味しなくなります。その時もEXIFにしてください。 + * 画像の精確な撮影時刻を入力することでGPSログとの時差を自動補正します。 + * 結果は、取り込み元のGPXファイルとは別に、元ファイル名にアンダーバー「_」を付加した.ファイルに出力します。 + - SPEED(速度): 出力GPXにタグを付加することができます。 + - MAGVAR(方向): 'MAGVAR'とは磁気方位のことです。直前のポイントとの2点間の位置関係を'MAGVAR'として出力できます。 + - 出力先のGPXに写真へのリンク情報を付加する/付加しないを選択可能にしました。 + [☑ 出力GPXにポイントマーカーを書き出す] + * 画像にEXIF情報を付加することができます。 + - 緯度経度: GPSログから算出した緯度・経度情報をEXIFに書き出すことができます。 + - 撮影方向: GPSログから移動方向を擬似撮影方向としてEXIFに書き出すことができます。(カメラの向きではありません) + +http://sourceforge.jp/projects/importpicture/wiki/FrontPage + +[起動] +下記のように'AdjustTime'を起動するとGUIでパラメータを逐次設定可能です。(推奨起動方法) + +> java -cp .:AdjustTime2.jar:commons-imaging-1.0-SNAPSHOT.jar osm.jp.gpx.matchtime.gui.AdjustTime + + +下記のコマンドラインによる起動方式は度重なる機能追加によりパラメーターが増大したため複雑になりすぎ作者でさえわけがわからなくなりました。 +一応、過去の起動方法を記載しておきます。しかし、コマンドラインからの引数は2016-10-03版以降は正しく引き継がれません。 +'AdjustTime2.jar'と'AdjustTime.ini'を使ってください。 + +> java -jar importPicture.jar