1 /*
2  * aTunes
3  * Copyright (C) Alex Aranda, Sylvain Gaudard and contributors
4  *
5  * See http://www.atunes.org/wiki/index.php?title=Contributing for information about contributors
6  *
7  * http://www.atunes.org
8  * http://sourceforge.net/projects/atunes
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  */
20 
21 package net.sourceforge.atunes.kernel.modules.pattern;
22 
23 import java.awt.BorderLayout;
24 import java.awt.GridBagConstraints;
25 import java.awt.GridBagLayout;
26 import java.awt.GridLayout;
27 import java.awt.Insets;
28 import java.awt.event.ActionEvent;
29 import java.awt.event.ActionListener;
30 import java.awt.event.KeyAdapter;
31 import java.awt.event.KeyEvent;
32 import java.awt.event.MouseAdapter;
33 import java.awt.event.MouseEvent;
34 import java.util.ArrayList;
35 import java.util.Collections;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.Map.Entry;
39 
40 import javax.swing.BorderFactory;
41 import javax.swing.JButton;
42 import javax.swing.JComboBox;
43 import javax.swing.JPanel;
44 import javax.swing.JScrollPane;
45 import javax.swing.JTable;
46 import javax.swing.JTextArea;
47 import javax.swing.JTextField;
48 import javax.swing.SwingUtilities;
49 import javax.swing.table.DefaultTableModel;
50 
51 import net.sourceforge.atunes.gui.images.Images;
52 import net.sourceforge.atunes.gui.views.controls.AbstractCustomDialog;
53 import net.sourceforge.atunes.model.IControlsBuilder;
54 import net.sourceforge.atunes.model.IFrame;
55 import net.sourceforge.atunes.model.IStateRepository;
56 import net.sourceforge.atunes.utils.I18nUtils;
57 import net.sourceforge.atunes.utils.StringUtils;
58 
59 /**
60  * This class represents a dialog used to enter a pattern It contains a preview
61  * table to see in real time how pattern will be applied
62  *
63  * @author fleax
64  */
65 public final class PatternInputDialog extends AbstractCustomDialog {
66 
67 	private final class OkActionListener implements ActionListener {
68 		private final boolean massiveRecognition;
69 
OkActionListener(final boolean massiveRecognition)70 		private OkActionListener(final boolean massiveRecognition) {
71 			this.massiveRecognition = massiveRecognition;
72 		}
73 
74 		@Override
actionPerformed(final ActionEvent e)75 		public void actionPerformed(final ActionEvent e) {
76 			PatternInputDialog.this.result = (String) PatternInputDialog.this.patternComboBox
77 					.getSelectedItem();
78 
79 			if (PatternInputDialog.this.result != null
80 					&& !PatternInputDialog.this.result.trim().equals("")) {
81 				// Upper case all patterns found in result
82 				for (AbstractPattern pattern : PatternInputDialog.this.patterns
83 						.getPatternsList()) {
84 					PatternInputDialog.this.result = PatternInputDialog.this.result
85 							.replace(pattern.getPattern().toLowerCase(),
86 									pattern.getPattern());
87 				}
88 
89 				// If pattern was not already used add to list of previously
90 				// used patterns
91 				List<String> previousPatterns = null;
92 				if (this.massiveRecognition) {
93 					previousPatterns = PatternInputDialog.this.stateRepository
94 							.getMassiveRecognitionPatterns();
95 				} else {
96 					previousPatterns = PatternInputDialog.this.stateRepository
97 							.getRecognitionPatterns();
98 				}
99 
100 				// Create previous list if necessary
101 				if (previousPatterns == null) {
102 					previousPatterns = new ArrayList<String>();
103 					if (this.massiveRecognition) {
104 						PatternInputDialog.this.stateRepository
105 								.setMassiveRecognitionPatterns(previousPatterns);
106 					} else {
107 						PatternInputDialog.this.stateRepository
108 								.setRecognitionPatterns(previousPatterns);
109 					}
110 				}
111 
112 				// Test
113 				if (!previousPatterns.contains(PatternInputDialog.this.result)) {
114 					previousPatterns.add(PatternInputDialog.this.result);
115 				}
116 			}
117 			dispose();
118 		}
119 	}
120 
121 	private static final long serialVersionUID = -5789081662254435503L;
122 
123 	/** The combo box used to enter or select pattern */
124 	private JComboBox patternComboBox;
125 
126 	/** Table used to show pattern replace preview */
127 	private JTable patternPreviewTable;
128 
129 	/** Table used to show available patterns */
130 	private JTable availablePatternsTable;
131 
132 	/** String used to preview pattern entered */
133 	private String previewString = null;
134 
135 	/** The pattern entered */
136 	private String result = null;
137 
138 	private static final String[] PREVIEW_COLUMN_NAMES = new String[] {
139 			I18nUtils.getString("NAME"), I18nUtils.getString("VALUE") };
140 
141 	private IStateRepository stateRepository;
142 
143 	private final boolean massiveRecognition;
144 
145 	private Patterns patterns;
146 
147 	private PatternMatcher patternMatcher;
148 
149 	/**
150 	 * @param patternMatcher
151 	 */
setPatternMatcher(final PatternMatcher patternMatcher)152 	public void setPatternMatcher(final PatternMatcher patternMatcher) {
153 		this.patternMatcher = patternMatcher;
154 	}
155 
156 	/**
157 	 * @param patterns
158 	 */
setPatterns(final Patterns patterns)159 	public void setPatterns(final Patterns patterns) {
160 		this.patterns = patterns;
161 	}
162 
163 	/**
164 	 * @param stateRepository
165 	 */
setStateRepository(final IStateRepository stateRepository)166 	public void setStateRepository(final IStateRepository stateRepository) {
167 		this.stateRepository = stateRepository;
168 	}
169 
170 	/**
171 	 * Instantiates a new pattern input dialog.
172 	 *
173 	 * @param frame
174 	 * @param massiveRecognition
175 	 *            <code>true</code> if the dialog will be used to enter a
176 	 *            pattern for massive recognition or <code>false</code> for
177 	 *            non-massive recognition (single file level)
178 	 * @param controlsBuilder
179 	 */
PatternInputDialog(final IFrame frame, final boolean massiveRecognition, final IControlsBuilder controlsBuilder)180 	public PatternInputDialog(final IFrame frame,
181 			final boolean massiveRecognition,
182 			final IControlsBuilder controlsBuilder) {
183 		super(frame, 550, 350, controlsBuilder);
184 		this.massiveRecognition = massiveRecognition;
185 	}
186 
187 	@Override
initialize()188 	public void initialize() {
189 		setResizable(false);
190 		setIconImage(Images.getImage(Images.APP_LOGO_16).getImage());
191 		setTitle(I18nUtils.getString("PATTERN_INPUT"));
192 
193 		// Label with instructions
194 		JTextArea textArea = getControlsBuilder().createTextArea();
195 		textArea.setText(I18nUtils.getString("PATTERN_INPUT_INSTRUCTIONS"));
196 		textArea.setBorder(BorderFactory.createEmptyBorder());
197 		textArea.setWrapStyleWord(true);
198 		textArea.setEditable(false);
199 		textArea.setLineWrap(true);
200 		textArea.setOpaque(false);
201 
202 		// Combo box used to enter pattern
203 		List<String> previousPatterns = null;
204 		if (this.massiveRecognition) {
205 			previousPatterns = this.stateRepository
206 					.getMassiveRecognitionPatterns();
207 		} else {
208 			previousPatterns = this.stateRepository.getRecognitionPatterns();
209 		}
210 		// Sort list
211 		if (previousPatterns != null) {
212 			Collections.sort(previousPatterns);
213 		}
214 		this.patternComboBox = new JComboBox(
215 				previousPatterns != null ? previousPatterns
216 						.toArray(new String[previousPatterns.size()])
217 						: new String[0]);
218 		this.patternComboBox.setEditable(true);
219 
220 		JPanel patternPreviewPanel = new JPanel(new BorderLayout());
221 		this.patternPreviewTable = getLookAndFeel().getTable();
222 		JScrollPane patternPreviewTableScrollPane = getControlsBuilder()
223 				.createScrollPane(this.patternPreviewTable);
224 		patternPreviewPanel.add(patternPreviewTableScrollPane,
225 				BorderLayout.CENTER);
226 		patternPreviewPanel.setBorder(BorderFactory.createTitledBorder(
227 				BorderFactory.createEmptyBorder(),
228 				I18nUtils.getString("PREVIEW")));
229 
230 		JPanel availablePatternsPanel = new JPanel(new BorderLayout());
231 		this.availablePatternsTable = getLookAndFeel().getTable();
232 		this.availablePatternsTable.addMouseListener(new MouseAdapter() {
233 			@Override
234 			public void mouseClicked(final MouseEvent e) {
235 				if (e.getClickCount() == 2) {
236 					// Get pattern being clicked
237 					int row = PatternInputDialog.this.availablePatternsTable
238 							.rowAtPoint(e.getPoint());
239 					// Access model to get pattern selected
240 					String pattern = ((AvailablePatternsDefaultTableModel) PatternInputDialog.this.availablePatternsTable
241 							.getModel()).getPatternAtRow(row);
242 					// Add pattern to current one
243 					String newPattern = StringUtils.getString(
244 							PatternInputDialog.this.patternComboBox.getEditor()
245 									.getItem(), pattern);
246 					PatternInputDialog.this.patternComboBox.getEditor()
247 							.setItem(newPattern);
248 					PatternInputDialog.this.patternComboBox
249 							.setSelectedItem(newPattern);
250 					// Update pattern preview
251 					previewPattern(PatternInputDialog.this.massiveRecognition);
252 				}
253 			}
254 		});
255 		JScrollPane availablePatternsScrollPane = getControlsBuilder()
256 				.createScrollPane(this.availablePatternsTable);
257 		availablePatternsPanel.add(availablePatternsScrollPane,
258 				BorderLayout.CENTER);
259 		availablePatternsPanel.setBorder(BorderFactory.createTitledBorder(
260 				BorderFactory.createEmptyBorder(),
261 				I18nUtils.getString("AVAILABLE_PATTERNS")));
262 
263 		JButton okButton = new JButton(I18nUtils.getString("OK"));
264 		ActionListener okListener = new OkActionListener(
265 				this.massiveRecognition);
266 		okButton.addActionListener(okListener);
267 		JButton cancelButton = new JButton(I18nUtils.getString("CANCEL"));
268 		cancelButton.addActionListener(new ActionListener() {
269 			@Override
270 			public void actionPerformed(final ActionEvent e) {
271 				PatternInputDialog.this.result = null;
272 				dispose();
273 			}
274 		});
275 
276 		this.patternComboBox.addActionListener(new ActionListener() {
277 			@Override
278 			public void actionPerformed(final ActionEvent e) {
279 				previewPattern(PatternInputDialog.this.massiveRecognition);
280 			}
281 		});
282 
283 		this.patternComboBox.getEditor().getEditorComponent()
284 				.addKeyListener(new KeyAdapter() {
285 					@Override
286 					public void keyTyped(final KeyEvent e) {
287 						super.keyTyped(e);
288 
289 						SwingUtilities.invokeLater(new Runnable() {
290 							@Override
291 							public void run() {
292 								previewPattern(PatternInputDialog.this.massiveRecognition);
293 							}
294 						});
295 					}
296 				});
297 
298 		GridLayout gl = new GridLayout(1, 2, 30, 0);
299 		JPanel auxPanel = new JPanel(gl);
300 		auxPanel.add(okButton);
301 		auxPanel.add(cancelButton);
302 
303 		arrangeDialog(textArea, patternPreviewPanel, availablePatternsPanel,
304 				okButton, auxPanel);
305 	}
306 
arrangeDialog(final JTextArea textArea, final JPanel patternPreviewPanel, final JPanel availablePatternsPanel, final JButton okButton, final JPanel auxPanel)307 	private void arrangeDialog(final JTextArea textArea,
308 			final JPanel patternPreviewPanel,
309 			final JPanel availablePatternsPanel, final JButton okButton,
310 			final JPanel auxPanel) {
311 		JPanel panel = new JPanel(new GridBagLayout());
312 		GridBagConstraints c = new GridBagConstraints();
313 		c.gridx = 0;
314 		c.gridy = 0;
315 		c.weightx = 1;
316 		c.gridwidth = 2;
317 		c.fill = GridBagConstraints.HORIZONTAL;
318 		c.insets = new Insets(10, 10, 10, 10);
319 		panel.add(textArea, c);
320 		c.gridy = 1;
321 		c.insets = new Insets(5, 30, 5, 30);
322 		panel.add(this.patternComboBox, c);
323 		c.gridx = 0;
324 		c.gridy = 2;
325 		c.weightx = 0.7;
326 		c.weighty = 1;
327 		c.gridwidth = 1;
328 		c.fill = GridBagConstraints.BOTH;
329 		c.insets = new Insets(5, 5, 5, 5);
330 		panel.add(patternPreviewPanel, c);
331 		c.gridx = 1;
332 		c.weightx = 0.3;
333 		panel.add(availablePatternsPanel, c);
334 		c.gridx = 0;
335 		c.gridy = 3;
336 		c.gridwidth = 2;
337 		c.weighty = 0;
338 		c.fill = GridBagConstraints.NONE;
339 		c.anchor = GridBagConstraints.CENTER;
340 		c.insets = new Insets(10, 10, 10, 10);
341 		panel.add(auxPanel, c);
342 		add(panel);
343 		getRootPane().setDefaultButton(okButton);
344 	}
345 
346 	/**
347 	 * Updated preview
348 	 *
349 	 * @param massiveRecognition
350 	 */
previewPattern(final boolean massiveRecognition)351 	void previewPattern(final boolean massiveRecognition) {
352 		Map<String, String> matches = this.patternMatcher.getPatternMatches(
353 				((JTextField) this.patternComboBox.getEditor()
354 						.getEditorComponent()).getText(), this.previewString,
355 				massiveRecognition);
356 
357 		String[][] data = new String[matches.size()][2];
358 		int i = 0;
359 		for (Entry<String, String> entry : matches.entrySet()) {
360 			data[i][0] = I18nUtils.getString(entry.getKey());
361 			data[i][1] = entry.getValue();
362 			i++;
363 		}
364 
365 		((DefaultTableModel) this.patternPreviewTable.getModel())
366 				.setDataVector(data, PREVIEW_COLUMN_NAMES);
367 	}
368 
369 	/**
370 	 * Gets the result.
371 	 *
372 	 * @return the result
373 	 */
getResult()374 	public String getResult() {
375 		return this.result;
376 	}
377 
378 	/**
379 	 * Show this dialog
380 	 *
381 	 * @param availablePatterns
382 	 * @param previewString
383 	 */
show(final List<AbstractPattern> availablePatterns, final String previewString)384 	public void show(final List<AbstractPattern> availablePatterns,
385 			final String previewString) {
386 		this.previewString = previewString;
387 		this.patternComboBox.setSelectedIndex(-1);
388 
389 		String[][] patternsMatrix = new String[availablePatterns.size()][2];
390 		int i = 0;
391 		for (AbstractPattern p : availablePatterns) {
392 			patternsMatrix[i][0] = p.getPattern();
393 			patternsMatrix[i][1] = p.getDescription();
394 			i++;
395 		}
396 		// Disable autoresize, as we will control it
397 		this.patternPreviewTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
398 		this.patternPreviewTable
399 				.setColumnModel(new PatternPreviewDefaultTableColumnModel());
400 		DefaultTableModel patternPreviewTableModel = new PatternPreviewDefaultTableModel(
401 				new String[0][2], PREVIEW_COLUMN_NAMES);
402 		this.patternPreviewTable.setModel(patternPreviewTableModel);
403 
404 		DefaultTableModel availablePatternsTableModel = new AvailablePatternsDefaultTableModel(
405 				patternsMatrix, new String[] { I18nUtils.getString("PATTERN"),
406 						I18nUtils.getString("DESCRIPTION") });
407 		this.availablePatternsTable.setModel(availablePatternsTableModel);
408 		setVisible(true);
409 		this.patternComboBox.requestFocus();
410 	}
411 
412 }
413