Source code: jmx/util/ReadFilesJButton.java
1 /*
2 * ReadFilesJButton.java 0.1.1.0 20th February 2001
3 *
4 * Copyright (C) 2000 Adam Kirby
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 *
15 * See the GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 */
21
22 package jmx.util;
23
24 import java.awt.Component;
25 import java.awt.event.ActionListener;
26 import java.awt.event.ActionEvent;
27 import java.io.File;
28 import javax.swing.JButton;
29 import javax.swing.JFileChooser;
30
31 import jm.music.data.Score;
32
33 /**
34 * A button which allows user to select MIDI and/or jMusic files to import.
35 *
36 * <P>After each successful import of a Score, any registered ReadListeners
37 * are notified and can update and use the score read. The listeners are
38 * guaranteed to be notified in a LILO (Last In Last Out) order. As an example,
39 * if you wanted a Score quantised and then analysed, you would the quantising
40 * ReadListener first, then the analysing one.
41 *
42 * @author Adam Kirby
43 * @version 1.0,Sun Feb 25 18:35:35 2001
44 *
45 * @see ReadListener
46 */
47 public class ReadFilesJButton extends JButton {
48
49 /**
50 * Constant defining the state of a ReadFileJButton that reads in a
51 * single jm or midi file.
52 */
53 public static final ReadFilesMode SINGLE_FILE_MODE =
54 new ReadFilesMode("Single File");
55
56 /**
57 * Constant defining the state of a ReadFileJButton that reads in a
58 * selection of jm and/or midi files.
59 */
60 public static final ReadFilesMode MULTIPLE_FILES_MODE =
61 new ReadFilesMode("Multiple Files");
62
63 /**
64 * Constant defining the state of a ReadFileJButton that reads in a
65 * folder of jm and/or midi files.
66 */
67 public static final ReadFilesMode FOLDER_MODE =
68 new ReadFilesMode("Folder");
69
70 /**
71 * Class defining the states of a ReadFilesJButton
72 *
73 * @see {@link ReadFilesJButton#setMode
74 */
75 private static class ReadFilesMode {
76 /**
77 * Unique identifier for instances of this class.
78 */
79 private final String name;
80
81 /**
82 * This constructor has the private access modifier. Instances of this
83 * class can only be accessed through the constants defined in this
84 * class. This ensures that no modes, other than the ones defined here,
85 * can be created.
86 *
87 * @param name Unique identifier for the instance
88 */
89 private ReadFilesMode(String name) {
90 this.name = name;
91 }
92 }
93
94 /** The current import mode of this button */
95 private ReadFilesMode mode;
96
97 /** List of ReadListener's associated with this button */
98 private ReadListenerLinkedList readListenerList;
99
100 /**
101 * JFileChooser that generates the dialogs for selecting the music files
102 * to import
103 */
104 private JFileChooser chooser = new JFileChooser();
105
106 /**
107 * Parent component for this button. Control to this component will be
108 * suspended when file chooser dialogs and error dialogs are displayed.
109 */
110 private Component owner;
111
112 /**
113 * Constructs a JButton for reading in MIDI or jMusic files. Uses the
114 * default mode of {@link #MULTIPLE_FILES_MODE} which allows
115 * for a selection of files to be imported
116 *
117 * @param owner Component which is the owner of this button. Access to this
118 * Component will be suspended when the user is selecting a
119 * music file and when error messages are displayed.
120 */
121 public ReadFilesJButton(final Component owner) {
122 this(owner, MULTIPLE_FILES_MODE);
123 }
124
125 /**
126 * Constructs a JButton for reading in music files using the specified mode
127 *
128 * @param owner Component which is the owner of this button. Access to
129 * this Component will be suspended when the user is
130 * selecting a music file and when error messages are
131 * displayed.
132 * @param readMode ReadFilesMode specified the file/folder selection mode
133 */
134 public ReadFilesJButton(final Component owner,
135 final ReadFilesMode mode) {
136 super();
137 this.owner = owner;
138 setMode(mode);
139 addActionListener(new ActionListener() {
140 public void actionPerformed(ActionEvent evt) {
141 Runnable processRun = new Runnable() {
142 public void run() {
143 readListenerList.startedReading();
144
145 int chooserReturnValue = chooser.showOpenDialog(owner);
146 if (chooserReturnValue != chooser.APPROVE_OPTION) {
147 return;
148 }
149
150 if (mode == SINGLE_FILE_MODE) {
151 processFile(chooser.getSelectedFile());
152 } else if (mode == MULTIPLE_FILES_MODE) {
153 processFiles(chooser.getSelectedFiles());
154 } else if (mode == FOLDER_MODE) {
155 File file = chooser.getSelectedFile();
156 if (file.isDirectory()) {
157 /*
158 * When jMusic supports only JDK1.2 and later,
159 * the following code can be simplified to:
160 *
161 * processFiles(file.listFiles(
162 * new ReadFilenameFilter()));
163 */
164 String[] filenames = file.list(
165 new ReadFilenameFilter());
166 for (int i = 0; i < filenames.length; i++) {
167 processFile(new File(file.getAbsolutePath(),
168 filenames[i]));
169 }
170 }
171 }
172
173 if (readListenerList != null) {
174 readListenerList.finishedReading();
175 }
176 }
177 };
178 Thread processThread = new Thread(processRun, "processThread");
179 processThread.start();
180
181 }
182 });
183 }
184
185 /**
186 * Attempts to convert the <CODE>file</CODE> to a score, and if successful
187 * notifies any registered {@link ReadListener ReadListeners}
188 *
189 * <P>The task of file reading and conversion is delegated to {@link
190 * Read.midiOrJmWithSwingMessaging(File, Component)}.
191 *
192 * @param file File to convert to a score
193 *
194 * @see #processFiles
195 */
196 private void processFile(final File file) {
197 Score score = Read.midiOrJmWithSwingMessaging(file, owner);
198 if (score == null) {
199 return;
200 }
201 if (readListenerList != null) {
202 score = readListenerList.scoreRead(score);
203 }
204 }
205
206 /**
207 * Attempts to convert the series of <CODE>files</CODE> to scores, notifying
208 * any registered {@link ReadListener ReadListeners} after successful
209 * imports.
210 *
211 * @param file File to convert to a score
212 *
213 * @see #processFile
214 */
215 private void processFiles(final File[] files) {
216 if (files == null) {
217 return;
218 }
219 for (int i = 0; i < files.length; i++) {
220 processFile(files[i]);
221 }
222 }
223
224 /**
225 * Sets the mode of this button to <CODE>mode</CODE>
226 *
227 * @param mode ReadFilesMode for this button
228 *
229 * @see #getMode
230 */
231 public void setMode(ReadFilesMode mode) {
232 this.mode = mode;
233 if (mode == SINGLE_FILE_MODE) {
234 setText("Read File");
235 chooser.setDialogTitle("Select a MIDI or jMusic file to import");
236 chooser.setMultiSelectionEnabled(false);
237 chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
238 } else if (mode == MULTIPLE_FILES_MODE) {
239 setText("Read Files");
240 chooser.setDialogTitle("Select MIDI and/or jMusic files to import");
241 chooser.setMultiSelectionEnabled(true);
242 chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
243 } else if (mode == FOLDER_MODE) {
244 setText("Read Folder");
245 chooser.setDialogTitle("Select a folder of MIDI or jMusic files to "
246 + "import");
247 chooser.setMultiSelectionEnabled(false);
248 chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
249 }
250 }
251
252 /**
253 * Returns the current mode of this button
254 *
255 * @return the current mode of this button
256 *
257 * @see #setMode
258 */
259 public ReadFilesMode getMode() {
260 return mode;
261 }
262
263 /**
264 * Registers a ReadListener to recieve successful read notifications
265 *
266 * @param l ReadListener to add
267 *
268 * @see #removeReadListener
269 */
270 public void addReadListener(ReadListener l) {
271 if (l == null) {
272 return;
273 }
274 if (readListenerList == null) {
275 readListenerList = new ReadListenerLinkedList(l);
276 } else {
277 readListenerList.add(l);
278 }
279 }
280
281 /**
282 * Unregisters a ReadListener from recieving read notifications
283 *
284 * @param l ReadListner to remove
285 *
286 * @see #addReadListener
287 */
288 public void removeReadListener(ReadListener l) {
289 if (readListenerList == null) {
290 return;
291 }
292 if (readListenerList.getListener() == l) {
293 readListenerList = readListenerList.getNext();
294 }
295 }
296 }