1
0
mirror of https://github.com/mitb-archive/filebot synced 2024-12-24 08:48:51 -05:00

+ usability enhancements regarding FormatEditor

This commit is contained in:
Reinhard Pointner 2013-12-18 05:53:59 +00:00
parent f81e2fa9ea
commit 0d6ae94ae9
4 changed files with 161 additions and 164 deletions

View File

@ -1,21 +1,17 @@
package net.sourceforge.filebot.format;
public class BindingException extends RuntimeException {
public BindingException(String message, Throwable cause) {
super(message, cause);
}
public BindingException(String binding, String innerMessage) {
this(binding, innerMessage, null);
}
public BindingException(String binding, String innerMessage, Throwable cause) {
this(String.format("BindingError: \"%s\": %s", binding, innerMessage), cause);
this(String.format("BindingException: \"%s\": %s", binding, innerMessage), cause);
}
}

View File

@ -141,7 +141,7 @@ public class FormatDialog extends JDialog {
}
}
public FormatDialog(Window owner) {
public FormatDialog(Window owner, Mode initMode, MediaBindingBean lockOnBinding) {
super(owner, ModalityType.DOCUMENT_MODAL);
// initialize hidden
@ -223,26 +223,29 @@ public class FormatDialog extends JDialog {
// install editor suggestions popup
editor.setComponentPopupMenu(createRecentFormatPopup());
// episode mode by default
setMode(Mode.Episode);
// initialize window properties
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
setSize(610, 430);
// initialize data
setState(initMode, lockOnBinding != null ? lockOnBinding : restoreSample(initMode), lockOnBinding != null);
}
public void setMode(Mode mode) {
public void setState(Mode mode, MediaBindingBean bindings, boolean locked) {
this.mode = mode;
this.setTitle(String.format("%s Format", mode));
this.setTitle(String.format(locked ? "%s Format - %s ⇔ %s" : "%s Format", mode, bindings.getInfoObject(), bindings.getMediaFile().getName()));
title.setText(this.getTitle());
status.setVisible(false);
switchEditModeAction.putValue(Action.NAME, String.format("Switch to %s Format", mode.next()));
switchEditModeAction.setEnabled(!locked);
changeSampleAction.setEnabled(!locked);
updateHelpPanel(mode);
// update preview to current format
sample = restoreSample(mode);
sample = bindings;
// restore editor state
setFormatCode(mode.persistentFormatHistory().isEmpty() ? "" : mode.persistentFormatHistory().get(0));
@ -388,7 +391,7 @@ public class FormatDialog extends JDialog {
return panel;
}
private MediaBindingBean restoreSample(Mode mode) {
protected MediaBindingBean restoreSample(Mode mode) {
Object info = null;
File media = null;
@ -646,7 +649,8 @@ public class FormatDialog extends JDialog {
@Override
public void actionPerformed(ActionEvent e) {
setMode(mode.next());
Mode next = mode.next();
setState(next, restoreSample(next), false);
}
};

View File

@ -1,7 +1,5 @@
package net.sourceforge.filebot.ui.rename;
import static net.sourceforge.tuned.FileUtilities.*;
import java.beans.PropertyChangeEvent;
@ -31,7 +29,6 @@ import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.TransformedList;
import ca.odell.glazedlists.event.ListEvent;
public class RenameModel extends MatchModel<Object, File> {
private final FormattedFutureEventList names = new FormattedFutureEventList(this.values());
@ -45,13 +42,11 @@ public class RenameModel extends MatchModel<Object, File> {
return true;
}
@Override
public String preview(Match<?, ?> match) {
return format(match, null);
}
@Override
public String format(Match<?, ?> match, Map<?, ?> context) {
// clean up path separators like / or \
@ -61,27 +56,22 @@ public class RenameModel extends MatchModel<Object, File> {
private boolean preserveExtension = true;
public EventList<FormattedFuture> names() {
return names;
}
public EventList<File> files() {
return candidates();
}
public boolean preserveExtension() {
return preserveExtension;
}
public void setPreserveExtension(boolean preserveExtension) {
this.preserveExtension = preserveExtension;
}
public Map<File, String> getRenameMap() {
Map<File, String> map = new LinkedHashMap<File, String>();
@ -122,7 +112,6 @@ public class RenameModel extends MatchModel<Object, File> {
return map;
}
public void useFormatter(Object key, MatchFormatter formatter) {
if (formatter != null) {
formatters.put(key, formatter);
@ -134,7 +123,6 @@ public class RenameModel extends MatchModel<Object, File> {
names.refresh();
}
private MatchFormatter getFormatter(Match<Object, File> match) {
for (MatchFormatter formatter : formatters.values()) {
if (formatter.canFormat(match)) {
@ -145,6 +133,21 @@ public class RenameModel extends MatchModel<Object, File> {
return defaultFormatter;
}
public Map<File, Object> getMatchContext() {
return new AbstractMap<File, Object>() {
@Override
public Set<Entry<File, Object>> entrySet() {
Set<Entry<File, Object>> context = new LinkedHashSet<Entry<File, Object>>();
for (Match<Object, File> it : matches()) {
if (it.getValue() != null && it.getCandidate() != null) {
context.add(new SimpleImmutableEntry<File, Object>(it.getCandidate(), it.getValue()));
}
}
return context;
}
};
}
private class FormattedFutureEventList extends TransformedList<Object, FormattedFuture> {
@ -152,32 +155,27 @@ public class RenameModel extends MatchModel<Object, File> {
private final Executor backgroundFormatter = new ThreadPoolExecutor(0, 1, 5L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
public FormattedFutureEventList(EventList<Object> source) {
super(source);
this.source.addListEventListener(this);
}
@Override
public FormattedFuture get(int index) {
return futures.get(index);
}
@Override
protected boolean isWritable() {
// can't write to source directly
return false;
}
@Override
public void add(int index, FormattedFuture value) {
source.add(index, value.getMatch().getValue());
}
@Override
public FormattedFuture set(int index, FormattedFuture value) {
FormattedFuture obsolete = get(index);
@ -187,7 +185,6 @@ public class RenameModel extends MatchModel<Object, File> {
return obsolete;
}
@Override
public FormattedFuture remove(int index) {
FormattedFuture obsolete = get(index);
@ -197,7 +194,6 @@ public class RenameModel extends MatchModel<Object, File> {
return obsolete;
}
@Override
public void listChanged(ListEvent<Object> listChanges) {
updates.beginEvent(true);
@ -210,7 +206,7 @@ public class RenameModel extends MatchModel<Object, File> {
Match<Object, File> match = getMatch(index);
// create new future
final FormattedFuture future = new FormattedFuture(match, getFormatter(match), getContext());
final FormattedFuture future = new FormattedFuture(match, getFormatter(match), getMatchContext());
// update data
if (type == ListEvent.INSERT) {
@ -252,13 +248,12 @@ public class RenameModel extends MatchModel<Object, File> {
updates.commitEvent();
}
public void refresh() {
updates.beginEvent(true);
for (int i = 0; i < size(); i++) {
FormattedFuture obsolete = futures.get(i);
FormattedFuture future = new FormattedFuture(obsolete.getMatch(), getFormatter(obsolete.getMatch()), getContext());
FormattedFuture future = new FormattedFuture(obsolete.getMatch(), getFormatter(obsolete.getMatch()), getMatchContext());
// replace and cancel old future
cancel(futures.set(i, future));
@ -272,31 +267,12 @@ public class RenameModel extends MatchModel<Object, File> {
updates.commitEvent();
}
private Map<File, Object> getContext() {
return new AbstractMap<File, Object>() {
@Override
public Set<Entry<File, Object>> entrySet() {
Set<Entry<File, Object>> context = new LinkedHashSet<Entry<File, Object>>();
for (Match<Object, File> it : matches()) {
if (it.getValue() != null && it.getCandidate() != null) {
context.add(new SimpleImmutableEntry<File, Object>(it.getCandidate(), it.getValue()));
}
}
return context;
}
};
}
private void submit(FormattedFuture future) {
// observe and enqueue worker task
future.addPropertyChangeListener(futureListener);
backgroundFormatter.execute(future);
}
private void cancel(FormattedFuture future) {
// remove listener and cancel worker task
future.removePropertyChangeListener(futureListener);
@ -321,7 +297,6 @@ public class RenameModel extends MatchModel<Object, File> {
};
}
public static class FormattedFuture extends SwingWorker<String, Void> {
private final Match<Object, File> match;
@ -329,35 +304,29 @@ public class RenameModel extends MatchModel<Object, File> {
private final MatchFormatter formatter;
private FormattedFuture(Match<Object, File> match, MatchFormatter formatter, Map<File, Object> context) {
this.match = match;
this.formatter = formatter;
this.context = context;
}
public boolean isComplexFormat() {
return formatter instanceof ExpressionFormatter;
}
public Match<Object, File> getMatch() {
return match;
}
public String preview() {
return formatter.preview(match).trim();
}
@Override
protected String doInBackground() throws Exception {
return formatter.format(match, context).trim();
}
@Override
public String toString() {
if (isDone()) {

View File

@ -23,6 +23,7 @@ import java.util.EnumSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -50,8 +51,10 @@ import net.sourceforge.filebot.ResourceManager;
import net.sourceforge.filebot.Settings;
import net.sourceforge.filebot.StandardRenameAction;
import net.sourceforge.filebot.WebServices;
import net.sourceforge.filebot.format.MediaBindingBean;
import net.sourceforge.filebot.similarity.Match;
import net.sourceforge.filebot.ui.Language;
import net.sourceforge.filebot.ui.rename.FormatDialog.Mode;
import net.sourceforge.filebot.ui.rename.RenameModel.FormattedFuture;
import net.sourceforge.filebot.web.AudioTrack;
import net.sourceforge.filebot.web.AudioTrackFormat;
@ -85,6 +88,8 @@ public class RenamePanel extends JComponent {
private static final PreferencesEntry<String> persistentEpisodeFormat = Settings.forPackage(RenamePanel.class).entry("rename.format.episode");
private static final PreferencesEntry<String> persistentMovieFormat = Settings.forPackage(RenamePanel.class).entry("rename.format.movie");
private static final PreferencesEntry<String> persistentMusicFormat = Settings.forPackage(RenamePanel.class).entry("rename.format.music");
private static final PreferencesEntry<String> persistentLastFormatState = Settings.forPackage(RenamePanel.class).entry("rename.last.format.state");
private static final PreferencesEntry<String> persistentPreferredLanguage = Settings.forPackage(RenamePanel.class).entry("rename.language").defaultValue("en");
private static final PreferencesEntry<String> persistentPreferredEpisodeOrder = Settings.forPackage(RenamePanel.class).entry("rename.episode.order").defaultValue("Airdate");
@ -245,19 +250,15 @@ public class RenamePanel extends JComponent {
@Override
public void mouseClicked(MouseEvent evt) {
if (evt.getClickCount() == 2) {
getWindow(evt.getSource()).setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
try {
JList list = (JList) evt.getSource();
if (list.getSelectedIndex() >= 0) {
Object item = ((FormattedFuture) list.getSelectedValue()).getMatch().getValue();
if (item instanceof Movie) {
getWindow(evt.getSource()).setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
Movie m = (Movie) item;
if (m.getTmdbId() > 0) {
Desktop.getDesktop().browse(WebServices.TMDb.getMoviePageLink(m.getTmdbId()));
} else if (m.getImdbId() > 0) {
Desktop.getDesktop().browse(WebServices.IMDb.getMoviePageLink(m.getImdbId()));
}
}
Match<Object, File> match = renameModel.getMatch(list.getSelectedIndex());
Map<File, Object> context = renameModel.getMatchContext();
MediaBindingBean sample = new MediaBindingBean(match.getValue(), match.getCandidate(), context);
showFormatEditor(sample);
}
} catch (Exception e) {
Logger.getLogger(RenamePanel.class.getName()).log(Level.WARNING, e.getMessage());
@ -312,26 +313,7 @@ public class RenamePanel extends JComponent {
@Override
public void actionPerformed(ActionEvent evt) {
FormatDialog dialog = new FormatDialog(getWindowAncestor(RenamePanel.this));
dialog.setLocation(getOffsetLocation(dialog.getOwner()));
dialog.setVisible(true);
if (dialog.submit()) {
switch (dialog.getMode()) {
case Episode:
renameModel.useFormatter(Episode.class, new ExpressionFormatter(dialog.getFormat().getExpression(), EpisodeFormat.SeasonEpisode, Episode.class));
persistentEpisodeFormat.setValue(dialog.getFormat().getExpression());
break;
case Movie:
renameModel.useFormatter(Movie.class, new ExpressionFormatter(dialog.getFormat().getExpression(), MovieFormat.NameYear, Movie.class));
persistentMovieFormat.setValue(dialog.getFormat().getExpression());
break;
case Music:
renameModel.useFormatter(AudioTrack.class, new ExpressionFormatter(dialog.getFormat().getExpression(), new AudioTrackFormat(), AudioTrack.class));
persistentMusicFormat.setValue(dialog.getFormat().getExpression());
break;
}
}
showFormatEditor(null);
}
});
@ -408,6 +390,52 @@ public class RenamePanel extends JComponent {
return actionPopup;
}
protected void showFormatEditor(MediaBindingBean lockOnBinding) {
// default to Episode mode
Mode initMode = null;
if (lockOnBinding == null || lockOnBinding.getInfoObject() instanceof Episode) {
initMode = Mode.Episode;
} else if (lockOnBinding.getInfoObject() instanceof Movie) {
initMode = Mode.Movie;
} else if (lockOnBinding.getInfoObject() instanceof AudioTrack) {
initMode = Mode.Music;
}
// restore previous mode
if (lockOnBinding == null) {
try {
initMode = Mode.valueOf(persistentLastFormatState.getValue());
} catch (Exception e) {
Logger.getLogger(RenamePanel.class.getName()).log(Level.WARNING, e.getMessage());
}
}
FormatDialog dialog = new FormatDialog(getWindowAncestor(RenamePanel.this), initMode, lockOnBinding);
dialog.setLocation(getOffsetLocation(dialog.getOwner()));
dialog.setVisible(true);
if (dialog.submit()) {
switch (dialog.getMode()) {
case Episode:
renameModel.useFormatter(Episode.class, new ExpressionFormatter(dialog.getFormat().getExpression(), EpisodeFormat.SeasonEpisode, Episode.class));
persistentEpisodeFormat.setValue(dialog.getFormat().getExpression());
break;
case Movie:
renameModel.useFormatter(Movie.class, new ExpressionFormatter(dialog.getFormat().getExpression(), MovieFormat.NameYear, Movie.class));
persistentMovieFormat.setValue(dialog.getFormat().getExpression());
break;
case Music:
renameModel.useFormatter(AudioTrack.class, new ExpressionFormatter(dialog.getFormat().getExpression(), new AudioTrackFormat(), AudioTrack.class));
persistentMusicFormat.setValue(dialog.getFormat().getExpression());
break;
}
if (lockOnBinding == null) {
persistentLastFormatState.setValue(dialog.getMode().name());
}
}
}
protected final Action clearFilesAction = new AbstractAction("Clear", ResourceManager.getIcon("action.clear")) {
@Override