Java tutorial
/* * Copyright 2012 - 2016 Manuel Laggner * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.tinymediamanager.ui.tvshows.dialogs; import java.awt.Dimension; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.util.Comparator; import java.util.List; import java.util.Map.Entry; import java.util.ResourceBundle; import javax.swing.JButton; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTable; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.JViewport; import javax.swing.SwingWorker; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import org.apache.commons.lang3.LocaleUtils; import org.tinymediamanager.core.tvshow.TvShowEpisodeAndSeasonParser; import org.tinymediamanager.core.tvshow.TvShowModuleManager; import org.tinymediamanager.core.tvshow.entities.TvShowEpisode; import org.tinymediamanager.scraper.MediaScrapeOptions; import org.tinymediamanager.scraper.MediaScraper; import org.tinymediamanager.scraper.entities.MediaEpisode; import org.tinymediamanager.scraper.entities.MediaType; import org.tinymediamanager.scraper.mediaprovider.ITvShowMetadataProvider; import org.tinymediamanager.ui.EqualsLayout; import org.tinymediamanager.ui.IconManager; import org.tinymediamanager.ui.UTF8Control; import org.tinymediamanager.ui.components.EnhancedTextField; import org.tinymediamanager.ui.dialogs.TmmDialog; import org.tinymediamanager.ui.tvshows.TvShowEpisodeChooserModel; import com.jgoodies.forms.factories.FormFactory; import com.jgoodies.forms.layout.ColumnSpec; import com.jgoodies.forms.layout.FormLayout; import com.jgoodies.forms.layout.FormSpecs; import com.jgoodies.forms.layout.RowSpec; import ca.odell.glazedlists.BasicEventList; import ca.odell.glazedlists.FilterList; import ca.odell.glazedlists.GlazedLists; import ca.odell.glazedlists.ObservableElementList; import ca.odell.glazedlists.SortedList; import ca.odell.glazedlists.TextFilterator; import ca.odell.glazedlists.gui.TableFormat; import ca.odell.glazedlists.matchers.MatcherEditor; import ca.odell.glazedlists.swing.AdvancedTableModel; import ca.odell.glazedlists.swing.DefaultEventSelectionModel; import ca.odell.glazedlists.swing.GlazedListsSwing; import ca.odell.glazedlists.swing.TextComponentMatcherEditor; /** * The TvShowEpisodeChooserDialog is used for searching a special episode * * @author Manuel Laggner */ public class TvShowEpisodeChooserDialog extends TmmDialog implements ActionListener { private static final long serialVersionUID = 3317576458848699068L; /** * @wbp.nls.resourceBundle messages */ private static final ResourceBundle BUNDLE = ResourceBundle.getBundle("messages", new UTF8Control()); //$NON-NLS-1$ private TvShowEpisode episode; private MediaScraper mediaScraper; private MediaEpisode metadata; private ObservableElementList<TvShowEpisodeChooserModel> episodeEventList; private final List<TvShowEpisodeChooserModel> selectedEpisodes; private final SortedList<TvShowEpisodeChooserModel> sortedEpisodes; private JTable table; private JTextArea taPlot; private JTextField textField; public TvShowEpisodeChooserDialog(TvShowEpisode ep, MediaScraper mediaScraper) { super(BUNDLE.getString("tvshowepisode.choose"), "episodeChooser"); //$NON-NLS-1$ setBounds(5, 5, 600, 400); this.episode = ep; this.mediaScraper = mediaScraper; this.metadata = new MediaEpisode(mediaScraper.getId()); episodeEventList = new ObservableElementList<>( GlazedLists.threadSafeList(new BasicEventList<TvShowEpisodeChooserModel>()), GlazedLists.beanConnector(TvShowEpisodeChooserModel.class)); sortedEpisodes = new SortedList<>(GlazedListsSwing.swingThreadProxyList(episodeEventList), new EpisodeComparator()); getContentPane().setLayout(new FormLayout( new ColumnSpec[] { FormSpecs.RELATED_GAP_COLSPEC, ColumnSpec.decode("590px:grow"), FormSpecs.RELATED_GAP_COLSPEC, }, new RowSpec[] { FormSpecs.RELATED_GAP_ROWSPEC, RowSpec.decode("200dlu:grow"), FormSpecs.RELATED_GAP_ROWSPEC, RowSpec.decode("fill:37px"), FormSpecs.RELATED_GAP_ROWSPEC, })); { JSplitPane splitPane = new JSplitPane(); getContentPane().add(splitPane, "2, 2, fill, fill"); JPanel panelLeft = new JPanel(); panelLeft.setLayout(new FormLayout( new ColumnSpec[] { FormSpecs.RELATED_GAP_COLSPEC, ColumnSpec.decode("150dlu:grow"), FormSpecs.RELATED_GAP_COLSPEC, }, new RowSpec[] { FormSpecs.LINE_GAP_ROWSPEC, FormSpecs.DEFAULT_ROWSPEC, FormSpecs.RELATED_GAP_ROWSPEC, RowSpec.decode("default:grow"), FormSpecs.RELATED_GAP_ROWSPEC, })); textField = EnhancedTextField.createSearchTextField(); panelLeft.add(textField, "2, 2, fill, default"); textField.setColumns(10); JScrollPane scrollPane = new JScrollPane(); scrollPane.setMinimumSize(new Dimension(200, 23)); panelLeft.add(scrollPane, "2, 4, fill, fill"); splitPane.setLeftComponent(panelLeft); MatcherEditor<TvShowEpisodeChooserModel> textMatcherEditor = new TextComponentMatcherEditor<>(textField, new TvShowEpisodeChooserModelFilterator()); FilterList<TvShowEpisodeChooserModel> textFilteredEpisodes = new FilterList<>(sortedEpisodes, textMatcherEditor); AdvancedTableModel<TvShowEpisodeChooserModel> episodeTableModel = GlazedListsSwing .eventTableModelWithThreadProxyList(textFilteredEpisodes, new EpisodeTableFormat()); DefaultEventSelectionModel<TvShowEpisodeChooserModel> selectionModel = new DefaultEventSelectionModel<>( textFilteredEpisodes); selectedEpisodes = selectionModel.getSelected(); selectionModel.addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { if (e.getValueIsAdjusting()) { return; } // display first selected episode if (!selectedEpisodes.isEmpty()) { TvShowEpisodeChooserModel episode = selectedEpisodes.get(0); taPlot.setText(episode.getOverview()); } else { taPlot.setText(""); } taPlot.setCaretPosition(0); } }); table = new JTable(episodeTableModel); table.setSelectionModel(selectionModel); scrollPane.setViewportView(table); JPanel panelRight = new JPanel(); panelRight.setLayout(new FormLayout( new ColumnSpec[] { FormSpecs.RELATED_GAP_COLSPEC, ColumnSpec.decode("150dlu:grow"), FormSpecs.RELATED_GAP_COLSPEC, }, new RowSpec[] { FormSpecs.LINE_GAP_ROWSPEC, RowSpec.decode("default:grow"), FormSpecs.RELATED_GAP_ROWSPEC, })); JScrollPane scrollPane_1 = new JScrollPane(); panelRight.add(scrollPane_1, "2, 2, fill, fill"); splitPane.setRightComponent(panelRight); taPlot = new JTextArea(); taPlot.setEditable(false); taPlot.setWrapStyleWord(true); taPlot.setLineWrap(true); scrollPane_1.setViewportView(taPlot); splitPane.setDividerLocation(300); } JPanel bottomPanel = new JPanel(); getContentPane().add(bottomPanel, "2, 4, fill, top"); bottomPanel.setLayout(new FormLayout( new ColumnSpec[] { FormFactory.LABEL_COMPONENT_GAP_COLSPEC, FormFactory.DEFAULT_COLSPEC, FormFactory.RELATED_GAP_COLSPEC, ColumnSpec.decode("default:grow"), FormFactory.DEFAULT_COLSPEC, FormFactory.RELATED_GAP_COLSPEC, }, new RowSpec[] { FormFactory.LINE_GAP_ROWSPEC, RowSpec.decode("25px"), FormFactory.RELATED_GAP_ROWSPEC, })); JPanel buttonPane = new JPanel(); bottomPanel.add(buttonPane, "5, 2, fill, fill"); EqualsLayout layout = new EqualsLayout(5); layout.setMinWidth(100); buttonPane.setLayout(layout); final JButton okButton = new JButton(BUNDLE.getString("Button.ok")); //$NON-NLS-1$ okButton.setToolTipText(BUNDLE.getString("tvshow.change")); okButton.setIcon(IconManager.APPLY); buttonPane.add(okButton); okButton.setActionCommand("OK"); okButton.addActionListener(this); JButton cancelButton = new JButton(BUNDLE.getString("Button.cancel")); //$NON-NLS-1$ cancelButton.setToolTipText(BUNDLE.getString("edit.discard")); cancelButton.setIcon(IconManager.CANCEL); buttonPane.add(cancelButton); cancelButton.setActionCommand("Cancel"); cancelButton.addActionListener(this); // column widths table.getColumnModel().getColumn(0).setMaxWidth(50); table.getColumnModel().getColumn(1).setMaxWidth(50); SearchTask task = new SearchTask(); task.execute(); MouseListener mouseListener = new MouseListener() { @Override public void mouseReleased(MouseEvent e) { } @Override public void mousePressed(MouseEvent e) { } @Override public void mouseExited(MouseEvent e) { } @Override public void mouseEntered(MouseEvent e) { } @Override public void mouseClicked(MouseEvent e) { if (e.getClickCount() >= 2 && !e.isConsumed() && e.getButton() == MouseEvent.BUTTON1) { actionPerformed(new ActionEvent(okButton, ActionEvent.ACTION_PERFORMED, "OK")); } } }; table.addMouseListener(mouseListener); } @Override public void actionPerformed(ActionEvent e) { // assign episode if ("OK".equals(e.getActionCommand())) { if (!selectedEpisodes.isEmpty()) { TvShowEpisodeChooserModel episode = selectedEpisodes.get(0); if (episode != TvShowEpisodeChooserModel.emptyResult) { metadata = episode.getMediaEpisode(); } setVisible(false); } } // cancel if ("Cancel".equals(e.getActionCommand())) { setVisible(false); } } public MediaEpisode getMetadata() { return metadata; } private class SearchTask extends SwingWorker<Void, Void> { @Override public Void doInBackground() { MediaScrapeOptions options = new MediaScrapeOptions(MediaType.TV_EPISODE); options.setLanguage(LocaleUtils.toLocale(TvShowModuleManager.SETTINGS.getScraperLanguage().name())); options.setCountry(TvShowModuleManager.SETTINGS.getCertificationCountry()); for (Entry<String, Object> entry : episode.getTvShow().getIds().entrySet()) { options.setId(entry.getKey(), entry.getValue().toString()); } try { for (MediaEpisode episode : ((ITvShowMetadataProvider) mediaScraper.getMediaProvider()) .getEpisodeList(options)) { episodeEventList.add(new TvShowEpisodeChooserModel(mediaScraper, episode)); } } catch (Exception e) { } return null; } @Override protected void done() { if (textField.getText().isEmpty()) { int index = -1; // search for a match and preselect it // with file name for (int i = 0; i < sortedEpisodes.size(); i++) { TvShowEpisodeChooserModel model = sortedEpisodes.get(i); if (equals( TvShowEpisodeAndSeasonParser.cleanEpisodeTitle( episode.getVideoBasenameWithoutStacking(), episode.getTvShow().getTitle()), model.getTitle())) { index = i; break; } } // with ep title if (index == 0) { for (int i = 0; i < sortedEpisodes.size(); i++) { TvShowEpisodeChooserModel model = sortedEpisodes.get(i); if (equals(TvShowEpisodeAndSeasonParser.cleanEpisodeTitle(episode.getTitle(), episode.getTvShow().getTitle()), model.getTitle())) { index = i; break; } } } if (index > -1) { // preselect the entry table.getSelectionModel().setSelectionInterval(index, index); // and scroll it to the top scrollToVisible(index, 0); // Rectangle rect = table.getCellRect(index, 0, true); // table.scrollRectToVisible(rect); } } } private boolean equals(String title1, String title2) { String cleaned1 = title1.replaceAll("[!?,._-]", " ").replaceAll("\\s+", " ").trim(); String cleaned2 = title2.replaceAll("[!?,._-]", " ").replaceAll("\\s+", " ").trim(); return cleaned1.equalsIgnoreCase(cleaned2); } private void scrollToVisible(int rowIndex, int vColIndex) { if (!(table.getParent() instanceof JViewport)) { return; } if (table.getRowCount() < 1) { return; } // view dimension Dimension viewportExtentSize = ((JViewport) table.getParent()).getExtentSize(); Dimension cellDimension = new Dimension(0, 0); Rectangle rect = table.getCellRect(rowIndex, vColIndex, true); Rectangle rectOne; if (rowIndex + 1 < table.getRowCount()) { if (vColIndex + 1 < table.getColumnCount()) { vColIndex++; } rectOne = table.getCellRect(rowIndex + 1, vColIndex, true); cellDimension.width = rectOne.x - rect.x; cellDimension.height = rectOne.y - rect.y; } rect.setLocation(rect.x + viewportExtentSize.width - cellDimension.width, rect.y + viewportExtentSize.height - cellDimension.height); table.scrollRectToVisible(rect); } } private class TvShowEpisodeChooserModelFilterator implements TextFilterator<TvShowEpisodeChooserModel> { @Override public void getFilterStrings(List<String> baseList, TvShowEpisodeChooserModel model) { baseList.add(model.getTitle()); baseList.add(model.getOverview()); } } private class EpisodeComparator implements Comparator<TvShowEpisodeChooserModel> { @Override public int compare(TvShowEpisodeChooserModel o1, TvShowEpisodeChooserModel o2) { if (o1.getSeason() < o2.getSeason()) { return -1; } if (o1.getSeason() > o2.getSeason()) { return 1; } if (o1.getEpisode() < o2.getEpisode()) { return -1; } if (o1.getEpisode() > o2.getEpisode()) { return 1; } return 0; } } private class EpisodeTableFormat implements TableFormat<TvShowEpisodeChooserModel> { @Override public int getColumnCount() { return 3; } @Override public String getColumnName(int column) { switch (column) { case 0: return BUNDLE.getString("metatag.season"); //$NON-NLS-1$ case 1: return BUNDLE.getString("metatag.episode"); //$NON-NLS-1$ case 2: return BUNDLE.getString("metatag.title"); //$NON-NLS-1$ } return null; } @Override public Object getColumnValue(TvShowEpisodeChooserModel baseObject, int column) { switch (column) { case 0: return baseObject.getSeason(); case 1: return baseObject.getEpisode(); case 2: return baseObject.getTitle(); } return null; } } }