2009-11-21 14:21:46 -05:00
|
|
|
|
2011-09-22 01:03:46 -04:00
|
|
|
package net.sourceforge.filebot.ui.rename;
|
2009-11-21 14:21:46 -05:00
|
|
|
|
|
|
|
|
|
|
|
import static net.sourceforge.filebot.MediaTypes.*;
|
2012-06-30 04:46:55 -04:00
|
|
|
import static net.sourceforge.filebot.Settings.*;
|
2011-12-26 13:10:53 -05:00
|
|
|
import static net.sourceforge.filebot.media.MediaDetection.*;
|
2012-02-22 03:30:50 -05:00
|
|
|
import static net.sourceforge.filebot.similarity.Normalization.*;
|
2009-11-22 07:51:23 -05:00
|
|
|
import static net.sourceforge.tuned.FileUtilities.*;
|
2009-11-21 20:27:05 -05:00
|
|
|
import static net.sourceforge.tuned.ui.TunedUtilities.*;
|
2009-11-21 14:21:46 -05:00
|
|
|
|
2011-11-28 05:24:46 -05:00
|
|
|
import java.awt.Component;
|
2009-11-21 14:21:46 -05:00
|
|
|
import java.io.File;
|
2012-01-02 04:33:50 -05:00
|
|
|
import java.util.AbstractMap.SimpleEntry;
|
2009-11-21 14:21:46 -05:00
|
|
|
import java.util.ArrayList;
|
2011-11-14 06:43:22 -05:00
|
|
|
import java.util.Collection;
|
2009-11-22 07:51:23 -05:00
|
|
|
import java.util.Collections;
|
|
|
|
import java.util.Comparator;
|
2010-01-26 14:08:09 -05:00
|
|
|
import java.util.HashMap;
|
2011-12-30 10:34:02 -05:00
|
|
|
import java.util.LinkedHashSet;
|
2012-01-02 01:09:00 -05:00
|
|
|
import java.util.LinkedList;
|
2009-11-21 14:21:46 -05:00
|
|
|
import java.util.List;
|
2011-08-08 13:37:45 -04:00
|
|
|
import java.util.Locale;
|
2010-01-26 14:08:09 -05:00
|
|
|
import java.util.Map;
|
2011-12-05 10:38:41 -05:00
|
|
|
import java.util.Map.Entry;
|
2012-02-14 09:16:13 -05:00
|
|
|
import java.util.NoSuchElementException;
|
2011-12-30 10:34:02 -05:00
|
|
|
import java.util.Set;
|
2010-01-26 14:08:09 -05:00
|
|
|
import java.util.SortedSet;
|
2012-06-15 06:45:35 -04:00
|
|
|
import java.util.TreeMap;
|
2010-01-26 14:08:09 -05:00
|
|
|
import java.util.TreeSet;
|
2009-11-21 20:27:05 -05:00
|
|
|
import java.util.concurrent.Callable;
|
2012-01-02 04:33:50 -05:00
|
|
|
import java.util.concurrent.ExecutorService;
|
|
|
|
import java.util.concurrent.Executors;
|
|
|
|
import java.util.concurrent.Future;
|
2009-11-21 20:27:05 -05:00
|
|
|
import java.util.concurrent.FutureTask;
|
|
|
|
import java.util.concurrent.RunnableFuture;
|
2012-02-14 09:16:13 -05:00
|
|
|
import java.util.logging.Level;
|
|
|
|
import java.util.logging.Logger;
|
2009-11-21 20:27:05 -05:00
|
|
|
|
|
|
|
import javax.swing.Action;
|
|
|
|
import javax.swing.SwingUtilities;
|
2009-11-21 14:21:46 -05:00
|
|
|
|
2011-09-21 09:29:21 -04:00
|
|
|
import net.sourceforge.filebot.Analytics;
|
2012-02-14 09:16:13 -05:00
|
|
|
import net.sourceforge.filebot.MediaTypes;
|
2012-06-15 06:45:35 -04:00
|
|
|
import net.sourceforge.filebot.similarity.CommonSequenceMatcher;
|
2009-11-21 14:21:46 -05:00
|
|
|
import net.sourceforge.filebot.similarity.Match;
|
2012-01-02 01:09:00 -05:00
|
|
|
import net.sourceforge.filebot.similarity.NameSimilarityMetric;
|
|
|
|
import net.sourceforge.filebot.similarity.SimilarityMetric;
|
2009-11-21 20:27:05 -05:00
|
|
|
import net.sourceforge.filebot.ui.SelectDialog;
|
2011-09-22 08:55:04 -04:00
|
|
|
import net.sourceforge.filebot.web.Movie;
|
2009-11-21 14:21:46 -05:00
|
|
|
import net.sourceforge.filebot.web.MovieIdentificationService;
|
2011-09-18 15:08:03 -04:00
|
|
|
import net.sourceforge.filebot.web.MoviePart;
|
2012-02-13 04:54:57 -05:00
|
|
|
import net.sourceforge.filebot.web.SortOrder;
|
2012-04-29 01:59:12 -04:00
|
|
|
import net.sourceforge.tuned.FileUtilities.ParentFilter;
|
2009-11-21 14:21:46 -05:00
|
|
|
|
|
|
|
|
|
|
|
class MovieHashMatcher implements AutoCompleteMatcher {
|
|
|
|
|
|
|
|
private final MovieIdentificationService service;
|
|
|
|
|
2011-12-05 10:38:41 -05:00
|
|
|
|
2009-11-21 14:21:46 -05:00
|
|
|
public MovieHashMatcher(MovieIdentificationService service) {
|
|
|
|
this.service = service;
|
|
|
|
}
|
|
|
|
|
2011-12-05 10:38:41 -05:00
|
|
|
|
2009-11-21 14:21:46 -05:00
|
|
|
@Override
|
2012-02-13 04:54:57 -05:00
|
|
|
public List<Match<File, ?>> match(final List<File> files, final SortOrder sortOrder, final Locale locale, final boolean autodetect, final Component parent) throws Exception {
|
2012-06-15 06:45:35 -04:00
|
|
|
// ignore sample files
|
|
|
|
List<File> fileset = filter(files, NON_CLUTTER_FILES);
|
|
|
|
|
2009-11-22 07:51:23 -05:00
|
|
|
// handle movie files
|
2012-06-15 06:45:35 -04:00
|
|
|
List<File> movieFiles = filter(fileset, VIDEO_FILES);
|
|
|
|
List<File> nfoFiles = filter(fileset, MediaTypes.getDefaultFilter("application/nfo"));
|
2011-12-30 10:34:02 -05:00
|
|
|
|
2012-06-15 06:45:35 -04:00
|
|
|
List<File> orphanedFiles = new ArrayList<File>(filter(fileset, FILES));
|
2012-02-14 09:16:13 -05:00
|
|
|
orphanedFiles.removeAll(movieFiles);
|
|
|
|
orphanedFiles.removeAll(nfoFiles);
|
2012-02-12 21:11:01 -05:00
|
|
|
|
2012-02-10 11:43:09 -05:00
|
|
|
Map<File, List<File>> derivatesByMovieFile = new HashMap<File, List<File>>();
|
|
|
|
for (File movieFile : movieFiles) {
|
|
|
|
derivatesByMovieFile.put(movieFile, new ArrayList<File>());
|
|
|
|
}
|
2012-02-14 09:16:13 -05:00
|
|
|
for (File file : orphanedFiles) {
|
2012-02-10 11:43:09 -05:00
|
|
|
for (File movieFile : movieFiles) {
|
2012-02-12 21:11:01 -05:00
|
|
|
if (isDerived(file, movieFile)) {
|
2012-02-10 11:43:09 -05:00
|
|
|
derivatesByMovieFile.get(movieFile).add(file);
|
|
|
|
break;
|
|
|
|
}
|
2012-01-07 09:43:55 -05:00
|
|
|
}
|
2011-12-30 10:34:02 -05:00
|
|
|
}
|
2012-02-10 11:43:09 -05:00
|
|
|
for (List<File> derivates : derivatesByMovieFile.values()) {
|
2012-02-14 09:16:13 -05:00
|
|
|
orphanedFiles.removeAll(derivates);
|
2012-02-10 11:43:09 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// match movie hashes online
|
2012-02-14 09:16:13 -05:00
|
|
|
final Map<File, Movie> movieByFile = new HashMap<File, Movie>();
|
2012-02-10 11:43:09 -05:00
|
|
|
if (movieFiles.size() > 0) {
|
|
|
|
try {
|
|
|
|
Map<File, Movie> hashLookup = service.getMovieDescriptors(movieFiles, locale);
|
|
|
|
movieByFile.putAll(hashLookup);
|
|
|
|
Analytics.trackEvent(service.getName(), "HashLookup", "Movie", hashLookup.size()); // number of positive hash lookups
|
|
|
|
} catch (UnsupportedOperationException e) {
|
|
|
|
// ignore
|
|
|
|
}
|
|
|
|
}
|
2012-02-14 09:16:13 -05:00
|
|
|
for (File nfo : nfoFiles) {
|
|
|
|
try {
|
2012-02-15 07:40:18 -05:00
|
|
|
Movie movie = grepMovie(nfo, service, locale);
|
|
|
|
movieByFile.put(nfo, movie);
|
|
|
|
|
|
|
|
// match movie info to movie files that match the nfo file name
|
2012-04-29 01:59:12 -04:00
|
|
|
SortedSet<File> siblingMovieFiles = new TreeSet<File>(filter(movieFiles, new ParentFilter(nfo.getParentFile())));
|
2012-02-16 04:42:06 -05:00
|
|
|
String baseName = stripReleaseInfo(getName(nfo));
|
|
|
|
|
2012-02-15 07:40:18 -05:00
|
|
|
for (File movieFile : siblingMovieFiles) {
|
2012-02-16 04:42:06 -05:00
|
|
|
if (baseName.equalsIgnoreCase(stripReleaseInfo(getName(movieFile)))) {
|
2012-02-15 07:40:18 -05:00
|
|
|
movieByFile.put(movieFile, movie);
|
|
|
|
}
|
|
|
|
}
|
2012-02-14 09:16:13 -05:00
|
|
|
} catch (NoSuchElementException e) {
|
|
|
|
Logger.getLogger(getClass().getName()).log(Level.WARNING, "Failed to grep IMDbID: " + nfo.getName());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-15 07:40:18 -05:00
|
|
|
// collect files that will be matched one by one
|
2012-02-14 09:16:13 -05:00
|
|
|
List<File> movieMatchFiles = new ArrayList<File>();
|
|
|
|
movieMatchFiles.addAll(movieFiles);
|
|
|
|
movieMatchFiles.addAll(nfoFiles);
|
2012-06-15 06:45:35 -04:00
|
|
|
movieMatchFiles.addAll(filter(files, DISK_FOLDERS));
|
2012-02-14 09:16:13 -05:00
|
|
|
movieMatchFiles.addAll(filter(orphanedFiles, SUBTITLE_FILES)); // run movie detection only on orphaned subtitle files
|
2012-02-10 11:43:09 -05:00
|
|
|
|
2012-02-15 07:40:18 -05:00
|
|
|
// match remaining movies file by file in parallel
|
|
|
|
List<Callable<Entry<File, Movie>>> grabMovieJobs = new ArrayList<Callable<Entry<File, Movie>>>();
|
|
|
|
|
2012-06-15 06:45:35 -04:00
|
|
|
// remember user decisions and only bother user once
|
|
|
|
final Map<String, Movie> selectionMemory = new TreeMap<String, Movie>(CommonSequenceMatcher.getLenientCollator(Locale.ROOT));
|
|
|
|
final Map<String, String> inputMemory = new TreeMap<String, String>(CommonSequenceMatcher.getLenientCollator(Locale.ROOT));
|
|
|
|
|
2010-01-26 14:08:09 -05:00
|
|
|
// map all files by movie
|
2012-02-10 11:43:09 -05:00
|
|
|
for (final File file : movieMatchFiles) {
|
2012-01-02 04:33:50 -05:00
|
|
|
grabMovieJobs.add(new Callable<Entry<File, Movie>>() {
|
2011-09-21 09:29:21 -04:00
|
|
|
|
2012-01-02 04:33:50 -05:00
|
|
|
@Override
|
|
|
|
public Entry<File, Movie> call() throws Exception {
|
|
|
|
// unknown hash, try via imdb id from nfo file
|
2012-02-14 09:16:13 -05:00
|
|
|
if (!movieByFile.containsKey(file) || !autodetect) {
|
2012-06-15 06:45:35 -04:00
|
|
|
Movie result = grabMovieName(file, locale, autodetect, selectionMemory, inputMemory, parent, movieByFile.get(file));
|
2012-01-02 04:33:50 -05:00
|
|
|
if (result != null) {
|
|
|
|
Analytics.trackEvent(service.getName(), "SearchMovie", result.toString(), 1);
|
|
|
|
}
|
|
|
|
return new SimpleEntry<File, Movie>(file, result);
|
|
|
|
}
|
2012-02-14 09:16:13 -05:00
|
|
|
return new SimpleEntry<File, Movie>(file, movieByFile.get(file));
|
2011-09-21 09:29:21 -04:00
|
|
|
}
|
2012-01-02 04:33:50 -05:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2012-02-14 09:16:13 -05:00
|
|
|
// map movies to (possibly multiple) files (in natural order)
|
|
|
|
Map<Movie, SortedSet<File>> filesByMovie = new HashMap<Movie, SortedSet<File>>();
|
|
|
|
|
2012-06-30 04:46:55 -04:00
|
|
|
ExecutorService executor = Executors.newFixedThreadPool(getPreferredThreadPoolSize());
|
2012-01-02 04:33:50 -05:00
|
|
|
try {
|
|
|
|
for (Future<Entry<File, Movie>> it : executor.invokeAll(grabMovieJobs)) {
|
|
|
|
// check if we managed to lookup the movie descriptor
|
2012-01-02 11:57:41 -05:00
|
|
|
File file = it.get().getKey();
|
|
|
|
Movie movie = it.get().getValue();
|
|
|
|
|
|
|
|
// get file list for movie
|
|
|
|
if (movie != null) {
|
2012-01-02 04:33:50 -05:00
|
|
|
SortedSet<File> movieParts = filesByMovie.get(movie);
|
|
|
|
if (movieParts == null) {
|
|
|
|
movieParts = new TreeSet<File>();
|
|
|
|
filesByMovie.put(movie, movieParts);
|
|
|
|
}
|
|
|
|
movieParts.add(file);
|
2009-11-21 20:27:05 -05:00
|
|
|
}
|
2010-01-26 14:08:09 -05:00
|
|
|
}
|
2012-01-02 04:33:50 -05:00
|
|
|
} finally {
|
|
|
|
executor.shutdown();
|
2010-01-26 14:08:09 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// collect all File/MoviePart matches
|
|
|
|
List<Match<File, ?>> matches = new ArrayList<Match<File, ?>>();
|
|
|
|
|
2011-09-22 08:55:04 -04:00
|
|
|
for (Entry<Movie, SortedSet<File>> entry : filesByMovie.entrySet()) {
|
2012-02-14 09:16:13 -05:00
|
|
|
for (List<File> fileSet : mapByExtension(entry.getValue()).values()) {
|
|
|
|
// resolve movie parts
|
|
|
|
for (int i = 0; i < fileSet.size(); i++) {
|
|
|
|
Movie moviePart = entry.getKey();
|
|
|
|
if (fileSet.size() > 1) {
|
|
|
|
moviePart = new MoviePart(moviePart, i + 1, fileSet.size());
|
|
|
|
}
|
|
|
|
|
2012-02-16 02:19:12 -05:00
|
|
|
matches.add(new Match<File, Movie>(fileSet.get(i), moviePart.clone()));
|
2012-02-14 09:16:13 -05:00
|
|
|
|
|
|
|
// automatically add matches for derivate files
|
|
|
|
List<File> derivates = derivatesByMovieFile.get(fileSet.get(i));
|
|
|
|
if (derivates != null) {
|
|
|
|
for (File derivate : derivates) {
|
2012-02-16 02:19:12 -05:00
|
|
|
matches.add(new Match<File, Movie>(derivate, moviePart.clone()));
|
2012-02-14 09:16:13 -05:00
|
|
|
}
|
2012-02-10 11:43:09 -05:00
|
|
|
}
|
2009-11-22 07:51:23 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// restore original order
|
|
|
|
Collections.sort(matches, new Comparator<Match<File, ?>>() {
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int compare(Match<File, ?> o1, Match<File, ?> o2) {
|
|
|
|
return files.indexOf(o1.getValue()) - files.indexOf(o2.getValue());
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2009-11-21 14:21:46 -05:00
|
|
|
return matches;
|
|
|
|
}
|
|
|
|
|
2009-11-21 20:27:05 -05:00
|
|
|
|
2012-06-15 06:45:35 -04:00
|
|
|
protected Movie grabMovieName(File movieFile, Locale locale, boolean autodetect, Map<String, Movie> selectionMemory, Map<String, String> inputMemory, Component parent, Movie... suggestions) throws Exception {
|
2011-12-30 10:34:02 -05:00
|
|
|
Set<Movie> options = new LinkedHashSet<Movie>();
|
2009-11-21 20:27:05 -05:00
|
|
|
|
2011-08-26 05:46:02 -04:00
|
|
|
// add default value if any
|
2011-09-22 08:55:04 -04:00
|
|
|
for (Movie it : suggestions) {
|
2011-08-26 05:46:02 -04:00
|
|
|
if (it != null) {
|
|
|
|
options.add(it);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-30 10:34:02 -05:00
|
|
|
// auto-detect movie from nfo or folder / file name
|
2011-12-31 05:21:58 -05:00
|
|
|
options.addAll(detectMovie(movieFile, null, service, locale, false));
|
2011-08-21 23:43:22 -04:00
|
|
|
|
|
|
|
// allow manual user input
|
|
|
|
if (options.isEmpty() || !autodetect) {
|
2011-12-30 10:34:02 -05:00
|
|
|
String suggestion = options.isEmpty() ? stripReleaseInfo(getName(movieFile)) : options.iterator().next().getName();
|
2011-11-26 04:50:31 -05:00
|
|
|
|
|
|
|
String input = null;
|
2012-06-15 06:45:35 -04:00
|
|
|
synchronized (inputMemory) {
|
2012-06-22 03:47:26 -04:00
|
|
|
synchronized (this) {
|
|
|
|
input = inputMemory.get(suggestion);
|
|
|
|
if (input == null || suggestion == null || suggestion.isEmpty()) {
|
|
|
|
input = showInputDialog("Enter movie name:", suggestion, String.format("%s/%s", movieFile.getParentFile().getName(), movieFile.getName()), parent);
|
|
|
|
inputMemory.put(suggestion, input);
|
|
|
|
}
|
2012-06-15 06:45:35 -04:00
|
|
|
}
|
2011-11-26 04:50:31 -05:00
|
|
|
}
|
2011-07-06 22:08:50 -04:00
|
|
|
|
2011-12-30 10:34:02 -05:00
|
|
|
// we only care about results from manual input from here on out
|
|
|
|
options.clear();
|
|
|
|
|
2011-08-21 23:43:22 -04:00
|
|
|
if (input != null) {
|
2011-12-30 10:34:02 -05:00
|
|
|
options.addAll(service.searchMovie(input, locale));
|
2011-07-06 22:08:50 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-15 06:45:35 -04:00
|
|
|
return options.isEmpty() ? null : selectMovie(movieFile, options, selectionMemory, parent);
|
2009-11-21 20:27:05 -05:00
|
|
|
}
|
|
|
|
|
2011-12-05 10:38:41 -05:00
|
|
|
|
2012-06-15 06:45:35 -04:00
|
|
|
protected Movie selectMovie(final File movieFile, final Collection<Movie> options, final Map<String, Movie> selectionMemory, final Component parent) throws Exception {
|
|
|
|
// 1. movie by filename
|
|
|
|
final String fileQuery = stripReleaseInfo(getName(movieFile));
|
|
|
|
|
|
|
|
// 2. movie by directory
|
|
|
|
final File movieFolder = guessMovieFolder(movieFile);
|
|
|
|
final String folderQuery = (movieFolder == null) ? "" : stripReleaseInfo(movieFolder.getName());
|
2012-02-22 03:30:50 -05:00
|
|
|
|
|
|
|
// auto-ignore invalid files
|
2012-06-15 06:45:35 -04:00
|
|
|
if (fileQuery.length() < 2 && folderQuery.length() < 2) {
|
2012-02-22 03:30:50 -05:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2009-11-21 20:27:05 -05:00
|
|
|
if (options.size() == 1) {
|
2011-12-30 10:34:02 -05:00
|
|
|
return options.iterator().next();
|
2009-11-21 20:27:05 -05:00
|
|
|
}
|
|
|
|
|
2012-02-22 03:30:50 -05:00
|
|
|
// auto-select perfect match
|
|
|
|
for (Movie movie : options) {
|
|
|
|
String movieIdentifier = normalizePunctuation(movie.toString()).toLowerCase();
|
2012-06-15 06:45:35 -04:00
|
|
|
if (fileQuery.toLowerCase().startsWith(movieIdentifier) || folderQuery.toLowerCase().startsWith(movieIdentifier)) {
|
2012-02-22 03:30:50 -05:00
|
|
|
return movie;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-02 01:09:00 -05:00
|
|
|
// auto-select most probable search result
|
|
|
|
final List<Movie> probableMatches = new LinkedList<Movie>();
|
|
|
|
|
|
|
|
final SimilarityMetric metric = new NameSimilarityMetric();
|
|
|
|
|
|
|
|
// find probable matches using name similarity >= 0.9
|
|
|
|
for (Movie result : options) {
|
2012-02-22 03:30:50 -05:00
|
|
|
if (metric.getSimilarity(fileQuery, result.getName()) >= 0.9 || metric.getSimilarity(folderQuery, result.getName()) >= 0.9) {
|
2012-01-02 01:09:00 -05:00
|
|
|
probableMatches.add(result);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// auto-select first and only probable search result
|
|
|
|
if (probableMatches.size() == 1) {
|
|
|
|
return probableMatches.get(0);
|
|
|
|
}
|
|
|
|
|
2009-11-21 20:27:05 -05:00
|
|
|
// show selection dialog on EDT
|
2011-09-22 08:55:04 -04:00
|
|
|
final RunnableFuture<Movie> showSelectDialog = new FutureTask<Movie>(new Callable<Movie>() {
|
2009-11-21 20:27:05 -05:00
|
|
|
|
|
|
|
@Override
|
2011-09-22 08:55:04 -04:00
|
|
|
public Movie call() throws Exception {
|
2009-11-21 20:27:05 -05:00
|
|
|
// multiple results have been found, user must select one
|
2011-11-28 05:24:46 -05:00
|
|
|
SelectDialog<Movie> selectDialog = new SelectDialog<Movie>(parent, options);
|
2009-11-21 20:27:05 -05:00
|
|
|
|
2012-06-15 08:11:28 -04:00
|
|
|
selectDialog.setTitle(folderQuery.isEmpty() ? fileQuery : String.format("%s / %s", folderQuery, fileQuery));
|
2012-06-15 06:45:35 -04:00
|
|
|
selectDialog.getHeaderLabel().setText(String.format("Movies matching '%s':", fileQuery.length() >= 2 || folderQuery.length() <= 2 ? fileQuery : folderQuery));
|
2009-11-21 20:27:05 -05:00
|
|
|
selectDialog.getCancelAction().putValue(Action.NAME, "Ignore");
|
2012-01-01 22:48:24 -05:00
|
|
|
selectDialog.pack();
|
2009-11-21 20:27:05 -05:00
|
|
|
|
|
|
|
// show dialog
|
|
|
|
selectDialog.setLocation(getOffsetLocation(selectDialog.getOwner()));
|
|
|
|
selectDialog.setVisible(true);
|
|
|
|
|
|
|
|
// selected value or null if the dialog was canceled by the user
|
|
|
|
return selectDialog.getSelectedValue();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// allow only one select dialog at a time
|
2012-06-22 03:47:26 -04:00
|
|
|
synchronized (selectionMemory) {
|
|
|
|
synchronized (this) {
|
2012-06-15 06:45:35 -04:00
|
|
|
if (selectionMemory.containsKey(fileQuery)) {
|
|
|
|
return selectionMemory.get(fileQuery);
|
|
|
|
}
|
|
|
|
|
|
|
|
SwingUtilities.invokeAndWait(showSelectDialog);
|
|
|
|
|
|
|
|
// cache selected value
|
|
|
|
selectionMemory.put(fileQuery, showSelectDialog.get());
|
|
|
|
return showSelectDialog.get();
|
|
|
|
}
|
2009-11-21 20:27:05 -05:00
|
|
|
}
|
|
|
|
}
|
2009-11-21 14:21:46 -05:00
|
|
|
}
|