2323import javafx .scene .image .Image ;
2424import javafx .scene .input .*;
2525import javafx .scene .paint .Color ;
26+ import javafx .stage .FileChooser ;
2627import javafx .stage .Stage ;
2728
2829import java .io .File ;
@@ -51,7 +52,8 @@ public class Controller {
5152 private static final String PROGRAM_NAME_EXTENSION_SEPARATOR = " - " ;
5253 private static final String OPEN_FOLDER_ERROR_DIALOG_TITLE = "Error while opening image folder" ;
5354 private static final String OPEN_FOLDER_ERROR_DIALOG_HEADER = "The selected folder is not a valid image folder." ;
54- private static final String SAVE_IMAGE_ANNOTATIONS_DIRECTORY_CHOOSER_TITLE = "Save image annotations" ;
55+ private static final String SAVE_IMAGE_ANNOTATIONS_DIRECTORY_CHOOSER_TITLE = "Save image annotations to a folder" ;
56+ private static final String SAVE_IMAGE_ANNOTATIONS_FILE_CHOOSER_TITLE = "Save image annotations to a file" ;
5557 private static final String LOAD_IMAGE_FOLDER_ERROR_DIALOG_TITLE = "Error loading image folder" ;
5658 private static final String LOAD_IMAGE_FOLDER_ERROR_DIALOG_CONTENT = "The chosen folder does not contain any valid image files." ;
5759 private static final String CATEGORY_INPUT_ERROR_DIALOG_TITLE = "Category Creation Error" ;
@@ -89,6 +91,7 @@ public class Controller {
8991 private static final String ANNOTATIONS_SAVE_FORMAT_DIALOG_HEADER = "Choose the format for the saved annotations." ;
9092 private static final String ANNOTATIONS_SAVE_FORMAT_DIALOG_CONTENT = "Annotation format: " ;
9193 private static final String KEEP_EXISTING_CATEGORIES_DIALOG_TEXT = "Keep existing categories?" ;
94+ private static final String DEFAULT_JSON_EXPORT_FILENAME = "annotations.json" ;
9295
9396 private final Stage stage ;
9497 private final MainView view = new MainView ();
@@ -192,12 +195,11 @@ public void onRegisterSaveAnnotationsAction(ImageAnnotationSaveStrategy.Type sav
192195 return ;
193196 }
194197
195- final File saveDirectory = MainView .displayDirectoryChooserAndGetChoice (SAVE_IMAGE_ANNOTATIONS_DIRECTORY_CHOOSER_TITLE , stage ,
196- currentAnnotationSavingDirectory );
198+ File destination = getAnnotationSavingDestination (saveFormat );
197199
198- if (saveDirectory != null ) {
199- new AnnotationSaverService (saveDirectory , saveFormat ).startAndShowProgressDialog ();
200- currentAnnotationSavingDirectory = saveDirectory ;
200+ if (destination != null ) {
201+ new AnnotationSaverService (destination , saveFormat ).startAndShowProgressDialog ();
202+ setCurrentAnnotationSavingDirectory ( destination ) ;
201203 }
202204 }
203205
@@ -550,21 +552,29 @@ private void initiateAnnotationSavingWithFormatChoiceAndRunOnSaveSuccess(Runnabl
550552
551553 formatChoice .ifPresent (choice -> {
552554 // Ask for annotation save directory.
553- final File saveDirectory = MainView .displayDirectoryChooserAndGetChoice (SAVE_IMAGE_ANNOTATIONS_DIRECTORY_CHOOSER_TITLE , stage ,
554- currentAnnotationSavingDirectory );
555+ final File destination = getAnnotationSavingDestination (choice );
555556
556- if (saveDirectory != null ) {
557+ if (destination != null ) {
557558 // Save annotations.
558- AnnotationSaverService annotationSaverService = new AnnotationSaverService (saveDirectory , choice );
559+ AnnotationSaverService annotationSaverService = new AnnotationSaverService (destination , choice );
559560 annotationSaverService .runOnSuccess (() -> {
560- currentAnnotationSavingDirectory = saveDirectory ;
561+ setCurrentAnnotationSavingDirectory ( destination ) ;
561562 runnable .run ();
562563 });
563564 annotationSaverService .startAndShowProgressDialog ();
564565 }
565566 });
566567 }
567568
569+ private void setCurrentAnnotationSavingDirectory (File destination ) {
570+ if (destination .isDirectory ()) {
571+ currentAnnotationSavingDirectory = destination ;
572+ } else if (destination .isFile () && destination .getParentFile ().isDirectory ()
573+ && destination .getParentFile ().exists ()) {
574+ currentAnnotationSavingDirectory = destination .getParentFile ();
575+ }
576+ }
577+
568578 private void initiateAnnotationSavingWithFormatChoiceAndRunInAnyCase (Runnable runnable ) {
569579 // Ask for annotation save format.
570580 Optional <ImageAnnotationSaveStrategy .Type > formatChoice =
@@ -576,14 +586,13 @@ private void initiateAnnotationSavingWithFormatChoiceAndRunInAnyCase(Runnable ru
576586
577587 formatChoice .ifPresentOrElse (choice -> {
578588 // Ask for annotation save directory.
579- final File saveDirectory = MainView .displayDirectoryChooserAndGetChoice (SAVE_IMAGE_ANNOTATIONS_DIRECTORY_CHOOSER_TITLE , stage ,
580- currentAnnotationSavingDirectory );
589+ final File destination = getAnnotationSavingDestination (choice );
581590
582- if (saveDirectory != null ) {
591+ if (destination != null ) {
583592 // Save annotations.
584- AnnotationSaverService annotationSaverService = new AnnotationSaverService (saveDirectory , choice );
593+ AnnotationSaverService annotationSaverService = new AnnotationSaverService (destination , choice );
585594 annotationSaverService .runOnSuccess (() -> {
586- currentAnnotationSavingDirectory = saveDirectory ;
595+ setCurrentAnnotationSavingDirectory ( destination ) ;
587596 runnable .run ();
588597 });
589598 annotationSaverService .startAndShowProgressDialog ();
@@ -593,6 +602,21 @@ private void initiateAnnotationSavingWithFormatChoiceAndRunInAnyCase(Runnable ru
593602 }, runnable );
594603 }
595604
605+ private File getAnnotationSavingDestination (ImageAnnotationSaveStrategy .Type saveFormat ) {
606+ File destination ;
607+
608+ if (saveFormat .equals (ImageAnnotationSaveStrategy .Type .JSON )) {
609+ destination = MainView .displayFileChooserAndGetChoice (SAVE_IMAGE_ANNOTATIONS_FILE_CHOOSER_TITLE , stage ,
610+ currentAnnotationSavingDirectory , DEFAULT_JSON_EXPORT_FILENAME ,
611+ new FileChooser .ExtensionFilter ("JSON files" , "*.json" , "*.JSON" ));
612+ } else {
613+ destination = MainView .displayDirectoryChooserAndGetChoice (SAVE_IMAGE_ANNOTATIONS_DIRECTORY_CHOOSER_TITLE , stage ,
614+ currentAnnotationSavingDirectory );
615+ }
616+
617+ return destination ;
618+ }
619+
596620 private void interruptDirectoryWatcher () {
597621 if (directoryWatcher != null && directoryWatcher .isAlive ()) {
598622 directoryWatcher .interrupt ();
@@ -958,11 +982,11 @@ private KeyCombinations() {
958982 }
959983
960984 class AnnotationSaverService extends Service <IOResult > implements OnSuccessRunner <Runnable >, ProgressShower {
961- private final File saveDirectory ;
985+ private final File destination ;
962986 private final ImageAnnotationSaveStrategy .Type saveFormat ;
963987
964- AnnotationSaverService (File saveDirectory , ImageAnnotationSaveStrategy .Type saveFormat ) {
965- this .saveDirectory = saveDirectory ;
988+ AnnotationSaverService (File destination , ImageAnnotationSaveStrategy .Type saveFormat ) {
989+ this .destination = destination ;
966990 this .saveFormat = saveFormat ;
967991 setOnSucceeded (successEvent -> defaultOnSucceededHandler ());
968992 setOnFailed (failedEvent -> defaultOnFailedHandler ());
@@ -992,7 +1016,7 @@ protected IOResult call() throws Exception {
9921016
9931017 saver .progressProperty ().addListener ((observable , oldValue , newValue ) -> updateProgress (newValue .doubleValue (), 1.0 ));
9941018
995- return saver .save (model .getImageAnnotationData (), Paths .get (saveDirectory .getPath ()));
1019+ return saver .save (model .getImageAnnotationData (), Paths .get (destination .getPath ()));
9961020 }
9971021 };
9981022 }
0 commit comments