001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.microedition.lcdui.pda;
043:
044: import java.util.*;
045: import java.io.*;
046: import javax.microedition.io.*;
047: import javax.microedition.io.file.*;
048: import javax.microedition.lcdui.*;
049:
050: /**
051: * The <code>FileBrowser</code> custom component lets the user list files and
052: * directories. It's uses FileConnection Optional Package (JSR 75). The FileConnection
053: * Optional Package APIs give J2ME devices access to file systems residing on mobile devices,
054: * primarily access to removable storage media such as external memory cards.
055: * @author breh
056: */
057:
058: public class FileBrowser extends List implements CommandListener {
059:
060: /**
061: * Command fired on file selection.
062: */
063: public static final Command SELECT_FILE_COMMAND = new Command(
064: "Select", Command.OK, 1);
065:
066: private String currDirName;
067: private String currFile;
068: private Image dirIcon;
069: private Image fileIcon;
070: private Image[] iconList;
071: private CommandListener commandListener;
072:
073: /* special string denotes upper directory */
074: private static final String UP_DIRECTORY = "..";
075:
076: /* special string that denotes upper directory accessible by this browser.
077: * this virtual directory contains all roots.
078: */
079: private static final String MEGA_ROOT = "/";
080:
081: /* separator string as defined by FC specification */
082: private static final String SEP_STR = "/";
083:
084: /* separator character as defined by FC specification */
085: private static final char SEP = '/';
086:
087: private Display display;
088:
089: private String selectedURL;
090:
091: private String filter = null;
092:
093: private String title;
094:
095: /**
096: * Creates a new instance of FileBrowser for given <code>Display</code> object.
097: * @param display non null display object.
098: */
099: public FileBrowser(Display display) {
100: super ("", IMPLICIT);
101: currDirName = MEGA_ROOT;
102: this .display = display;
103: super .setCommandListener(this );
104: setSelectCommand(SELECT_FILE_COMMAND);
105: try {
106: dirIcon = Image
107: .createImage("/org/netbeans/microedition/resources/dir.png");
108: } catch (IOException e) {
109: dirIcon = null;
110: }
111: try {
112: fileIcon = Image
113: .createImage("/org/netbeans/microedition/resources/file.png");
114: } catch (IOException e) {
115: fileIcon = null;
116: }
117: iconList = new Image[] { fileIcon, dirIcon };
118:
119: showDir();
120: }
121:
122: private void showDir() {
123: new Thread(new Runnable() {
124:
125: public void run() {
126: try {
127: showCurrDir();
128: } catch (SecurityException e) {
129: Alert alert = new Alert(
130: "Error",
131: "You are not authorized to access the restricted API",
132: null, AlertType.ERROR);
133: alert.setTimeout(2000);
134: display.setCurrent(alert, FileBrowser.this );
135: } catch (Exception e) {
136: e.printStackTrace();
137: }
138: }
139: }).start();
140: }
141:
142: /**
143: * Indicates that a command event has occurred on Displayable d.
144: * @param c a <code>Command</code> object identifying the command. This is either
145: * one of the applications have been added to <code>Displayable</code> with <code>addCommand(Command)</code>
146: * or is the implicit <code>SELECT_COMMAND</code> of List.
147: * @param d the <code>Displayable</code> on which this event has occurred
148: */
149: public void commandAction(Command c, Displayable d) {
150: if (c.equals(SELECT_FILE_COMMAND)) {
151: List curr = (List) d;
152: currFile = curr.getString(curr.getSelectedIndex());
153: new Thread(new Runnable() {
154:
155: public void run() {
156: if (currFile.endsWith(SEP_STR)
157: || currFile.equals(UP_DIRECTORY)) {
158: openDir(currFile);
159: } else {
160: //switch To Next
161: doDismiss();
162: }
163: }
164: }).start();
165: } else {
166: commandListener.commandAction(c, d);
167: }
168: }
169:
170: /**
171: * Sets component's title.
172: * @param title component's title.
173: */
174: public void setTitle(String title) {
175: this .title = title;
176: super .setTitle(title);
177: }
178:
179: /**
180: * Show file list in the current directory .
181: */
182: private void showCurrDir() {
183: if (title == null) {
184: super .setTitle(currDirName);
185: }
186: Enumeration e = null;
187: FileConnection currDir = null;
188:
189: deleteAll();
190: if (MEGA_ROOT.equals(currDirName)) {
191: append(UP_DIRECTORY, dirIcon);
192: e = FileSystemRegistry.listRoots();
193: } else {
194: try {
195: currDir = (FileConnection) Connector.open("file:///"
196: + currDirName);
197: e = currDir.list();
198: } catch (IOException ioe) {
199: }
200: append(UP_DIRECTORY, dirIcon);
201: }
202:
203: if (e == null) {
204: try {
205: currDir.close();
206: } catch (IOException ioe) {
207: ioe.printStackTrace();
208: }
209: return;
210: }
211:
212: while (e.hasMoreElements()) {
213: String fileName = (String) e.nextElement();
214: if (fileName.charAt(fileName.length() - 1) == SEP) {
215: // This is directory
216: append(fileName, dirIcon);
217: } else {
218: // this is regular file
219: if (filter == null || fileName.indexOf(filter) > -1) {
220: append(fileName, fileIcon);
221: }
222: }
223: }
224:
225: if (currDir != null) {
226: try {
227: currDir.close();
228: } catch (IOException ioe) {
229: ioe.printStackTrace();
230: }
231: }
232: }
233:
234: private void openDir(String fileName) {
235: /* In case of directory just change the current directory
236: * and show it
237: */
238: if (currDirName.equals(MEGA_ROOT)) {
239: if (fileName.equals(UP_DIRECTORY)) {
240: // can not go up from MEGA_ROOT
241: return;
242: }
243: currDirName = fileName;
244: } else if (fileName.equals(UP_DIRECTORY)) {
245: // Go up one directory
246: // TODO use setFileConnection when implemented
247: int i = currDirName.lastIndexOf(SEP,
248: currDirName.length() - 2);
249: if (i != -1) {
250: currDirName = currDirName.substring(0, i + 1);
251: } else {
252: currDirName = MEGA_ROOT;
253: }
254: } else {
255: currDirName = currDirName + fileName;
256: }
257: showDir();
258: }
259:
260: /**
261: * Returns selected file as a <code>FileConnection</code> object.
262: * @return non null <code>FileConection</code> object
263: */
264: public FileConnection getSelectedFile() throws IOException {
265: FileConnection fileConnection = (FileConnection) Connector
266: .open(selectedURL);
267: return fileConnection;
268: }
269:
270: /**
271: * Returns selected <code>FileURL</code> object.
272: * @return non null <code>FileURL</code> object
273: */
274: public String getSelectedFileURL() {
275: return selectedURL;
276: }
277:
278: /**
279: * Sets the file filter.
280: * @param filter file filter String object
281: */
282: public void setFilter(String filter) {
283: this .filter = filter;
284: }
285:
286: /**
287: * Returns command listener.
288: * @return non null <code>CommandListener</code> object
289: */
290: protected CommandListener getCommandListener() {
291: return commandListener;
292: }
293:
294: /**
295: * Sets command listener to this component.
296: * @param commandListener <code>CommandListener</code> to be used
297: */
298: public void setCommandListener(CommandListener commandListener) {
299: this .commandListener = commandListener;
300: }
301:
302: private void doDismiss() {
303: selectedURL = "file:///" + currDirName + SEP_STR + currFile;
304: CommandListener commandListener = getCommandListener();
305: if (commandListener != null) {
306: commandListener.commandAction(SELECT_FILE_COMMAND, this);
307: }
308: }
309: }
|