001: package snow.utils.gui;
002:
003: import snow.lookandfeel.ThemesManager;
004: import snow.utils.storage.*;
005: import snow.files.*;
006: import javax.swing.*;
007: import java.awt.Component;
008: import java.awt.Insets;
009: import java.awt.GridBagConstraints;
010: import java.awt.BorderLayout;
011: import java.awt.GridBagLayout;
012: import java.awt.Dimension;
013: import java.awt.event.*;
014: import java.awt.FlowLayout;
015: import javax.swing.event.*;
016: import javax.swing.table.*;
017: import java.io.*;
018: import java.util.*;
019:
020: /** Manages a file list, separated with semicolons ';' (used for example in the tIDE class path).
021: */
022: public class FileListField extends JPanel {
023: final private int fileSelectionMode;
024: final private JButton editPathButton = new JButton("...");
025: final private JTextField pathField = new JTextField("");
026: final private Object parentDialogOrFrame;
027: final private String fieldName;
028: final List<File> alternatePaths = new ArrayList<File>();
029:
030: public File defaultFile = null;
031:
032: /** @param fileSelectionMode is for example JFileChooser.FILES_AND_DIRECTORIES
033: */
034: public FileListField(String fieldName, int fileSelectionMode,
035: Vector<File> files, Object parentDialogOrFrame) {
036: super ();
037: this .fileSelectionMode = fileSelectionMode;
038: this .parentDialogOrFrame = parentDialogOrFrame;
039: this .fieldName = fieldName;
040:
041: GridBagLayout gridbag = new GridBagLayout();
042: GridBagConstraints constr = new GridBagConstraints();
043: setLayout(gridbag);
044:
045: constr.weightx = 1;
046: constr.fill = GridBagConstraints.HORIZONTAL;
047: gridbag.setConstraints(pathField, constr);
048: add(pathField);
049: setFiles(files);
050:
051: constr.weightx = 0;
052: gridbag.setConstraints(editPathButton, constr);
053: add(editPathButton);
054:
055: Dimension dim = new Dimension((int) pathField
056: .getPreferredSize().getHeight(), (int) pathField
057: .getPreferredSize().getHeight());
058:
059: editPathButton.setPreferredSize(dim);
060: editPathButton.setMaximumSize(dim);
061: editPathButton.setMinimumSize(dim);
062: editPathButton.setFocusPainted(false);
063:
064: editPathButton.addActionListener(new ActionListener() {
065: public void actionPerformed(ActionEvent e) {
066: editAction();
067: }
068: });
069: /*
070: pathField.addActionListener(new ActionListener()
071: {
072: public void actionPerformed(ActionEvent e)
073: {
074: notifyActionListeners();
075: }
076: });
077: */
078:
079: pathField.addMouseListener(new MouseAdapter() {
080: @Override
081: public void mousePressed(MouseEvent me) {
082: if (me.isPopupTrigger())
083: showPopup(me);
084: }
085:
086: @Override
087: public void mouseReleased(MouseEvent me) {
088: if (me.isPopupTrigger())
089: showPopup(me);
090: }
091:
092: private void showPopup(MouseEvent me) {
093: colorizeIfInvalid();
094:
095: // offer to complete with paths not already present...
096: if (alternatePaths.size() == 0)
097: return;
098: JPopupMenu pop = new JPopupMenu();
099: for (final File fi : alternatePaths) {
100: if (!hasPath(fi)) {
101: JMenuItem pi = new JMenuItem("add "
102: + fi.getAbsolutePath());
103: pop.add(pi);
104: pi.addActionListener(new ActionListener() {
105: public void actionPerformed(ActionEvent ae) {
106: appendToPath(fi.getAbsolutePath());
107: }
108: });
109: }
110: }
111:
112: pop.show(pathField, 0, pathField.getHeight());
113: }
114: });
115:
116: colorizeIfInvalid();
117:
118: // important: allow smooth resizes in GL3
119: this .setMinimumSize(this .getPreferredSize());
120:
121: } // Constructor
122:
123: public boolean hasPath(File p) {
124: return getAllFiles().contains(p);
125: }
126:
127: public void appendToPath(final String p) {
128: final StringBuilder np = new StringBuilder(pathField.getText()
129: .trim());
130: if (np.length() > 0) {
131: np.append("; ");
132: }
133: np.append(p);
134:
135: pathField.setText(np.toString());
136:
137: colorizeIfInvalid();
138: }
139:
140: public void setFiles(List<File> files) {
141: if (files == null) {
142: pathField.setText("");
143: return;
144: }
145: pathField.setText(FileUtils.filesToList(files));
146: pathField.setCaretPosition(0);
147:
148: colorizeIfInvalid();
149: }
150:
151: public final List<File> getAllFiles() {
152: return FileUtils.getFilesFromList(pathField.getText(), false);
153: }
154:
155: public void colorizeIfInvalid() {
156: this .getTextField().setForeground(
157: UIManager.getColor("TextField.foreground"));
158: for (final File fi : getAllFiles()) {
159: if (!fi.exists()) {
160: this .getTextField().setForeground(
161: ThemesManager.getInstance().getRed());
162: return;
163: }
164: }
165: }
166:
167: private void editAction() {
168: showFilesDialog();
169: }
170:
171: public void setComponentWidth(int width) {
172: Dimension dim = pathField.getPreferredSize();
173: dim.width = width;
174: pathField.setMinimumSize(dim);
175: pathField.setPreferredSize(dim);
176: pathField.setMaximumSize(dim);
177:
178: // important: allow smooth resizes in GL3
179: this .setMinimumSize(this .getPreferredSize());
180:
181: }
182:
183: public JTextField getTextField() {
184: return pathField;
185: }
186:
187: /** Remembers the actual paths.
188: * Should be activated with offerRememberedPaths.
189: */
190: public void rememberPathForGlobalCompletion(AppProperties props,
191: String key) {
192: List<File> allKnownPaths = FileUtils.getFilesFromList(props
193: .getProperty(key, ""), false);
194: List<File> rem = new ArrayList<File>();
195:
196: // remove not existing items... (an if on a memory stick ?)
197: for (File fi : allKnownPaths) {
198: if (!fi.exists()) {
199: rem.add(fi);
200: }
201: }
202: allKnownPaths.removeAll(rem);
203:
204: // remember even if not existing...
205: for (File fi : getAllFiles()) {
206: if (!allKnownPaths.contains(fi)) {
207: allKnownPaths.add(fi);
208: }
209: }
210:
211: props.setProperty(key, FileUtils.filesToList(allKnownPaths));
212: }
213:
214: /** Must have been stored previously with rememberPathsForGlobalCompletion().
215: */
216: public void offerRememberedPaths(AppProperties props, String key) {
217: setAlternatePaths(FileUtils.getFilesFromList(props.getProperty(
218: key, ""), false));
219: }
220:
221: /** Will be offered as completion when pressing right mouse on the field...
222: * useful for "system wide" completions
223: */
224: public void setAlternatePaths(final List<File> paths) {
225: alternatePaths.clear();
226: alternatePaths.addAll(paths);
227: }
228:
229: private void showFilesDialog() {
230: JPanel jpp = new JPanel(new BorderLayout(0, 0));
231:
232: final FilesTableModel filesTableModel = new FilesTableModel(
233: getAllFiles());
234: final JTable table = new JTable(filesTableModel);
235: jpp.add(new JScrollPane(table), BorderLayout.CENTER);
236:
237: JPanel controlPanel = new JPanel(new FlowLayout(
238: FlowLayout.LEFT, 2, 1));
239: controlPanel.setBorder(null);
240: jpp.add(controlPanel, BorderLayout.NORTH);
241: final JButton addButton = new JButton("Add", Icons.sharedPlus);
242: controlPanel.add(addButton);
243: addButton.setFocusPainted(false);
244: addButton.addActionListener(new ActionListener() {
245: public void actionPerformed(ActionEvent ae) {
246: File sel = filesTableModel.getLastFileOrDefault();
247: String fss = "";
248: if (sel != null)
249: fss = sel.getAbsolutePath();
250: JFileChooser fs = new JFileChooser(fss);
251: fs.setAccessory(new FileChooserFilter(fs));
252: fs.setMultiSelectionEnabled(true);
253: fs.setFileSelectionMode(fileSelectionMode);
254: int rep = fs.showOpenDialog(addButton);
255: if (rep != JFileChooser.APPROVE_OPTION)
256: return;
257: for (File f : fs.getSelectedFiles()) {
258: filesTableModel.add(f);
259: }
260: }
261: });
262:
263: final JButton removeButton = new JButton("Remove selected",
264: Icons.sharedCross);
265: controlPanel.add(removeButton);
266: removeButton.setFocusPainted(false);
267: removeButton.addActionListener(new ActionListener() {
268: public void actionPerformed(ActionEvent ae) {
269: filesTableModel.deleteSelected(table.getSelectedRows());
270: }
271: });
272:
273: final JButton moveUp = new JButton(new Icons.UpWedge(10, 10,
274: false));
275: controlPanel.add(moveUp);
276: moveUp.setFocusPainted(false);
277: moveUp.addActionListener(new ActionListener() {
278: public void actionPerformed(ActionEvent ae) {
279: int ns = filesTableModel.moveUp(table.getSelectedRow());
280: if (ns >= 0) {
281: table.getSelectionModel().setSelectionInterval(ns,
282: ns);
283: }
284: }
285: });
286:
287: final JButton moveDown = new JButton(new Icons.DownWedge(10,
288: 10, false));
289: controlPanel.add(moveDown);
290: moveDown.setFocusPainted(false);
291: moveDown.addActionListener(new ActionListener() {
292: public void actionPerformed(ActionEvent ae) {
293: int ns = filesTableModel.moveDown(table
294: .getSelectedRow());
295: if (ns >= 0) {
296: table.getSelectionModel().setSelectionInterval(ns,
297: ns);
298: }
299: }
300: });
301:
302: addButton.setMargin(new Insets(0, 2, 0, 2));
303: removeButton.setMargin(new Insets(0, 2, 0, 2));
304:
305: moveDown.setMargin(new Insets(0, 2, 0, 2));
306: moveUp.setMargin(new Insets(0, 2, 0, 2));
307:
308: table.getSelectionModel().addListSelectionListener(
309: new ListSelectionListener() {
310: public void valueChanged(ListSelectionEvent lse) {
311: moveUp
312: .setEnabled(table.getSelectedRows().length == 1);
313: moveDown
314: .setEnabled(table.getSelectedRows().length == 1);
315: removeButton
316: .setEnabled(table.getSelectedRows().length > 0);
317:
318: }
319: });
320: moveUp.setEnabled(false);
321: moveDown.setEnabled(false);
322: removeButton.setEnabled(false);
323:
324: //UGLY table.setDefaultEditor(Object.class, new FileNameCellEditor());
325: final FileCellRenderer rend = new FileCellRenderer();
326: table.setDefaultRenderer(String.class, rend);
327: table.setDefaultRenderer(Object.class, rend);
328:
329: final JDialog modalDialog;
330: if (this .parentDialogOrFrame instanceof JFrame) {
331: modalDialog = new JDialog(
332: (JFrame) this .parentDialogOrFrame, fieldName, true);
333: } else if (this .parentDialogOrFrame instanceof JDialog) {
334: modalDialog = new JDialog(
335: (JDialog) this .parentDialogOrFrame, fieldName, true);
336: } else {
337: modalDialog = new JDialog((JFrame) null, fieldName, true);
338: }
339:
340: modalDialog.add(jpp, BorderLayout.CENTER);
341: CloseControlPanel ccp = new CloseControlPanel(modalDialog,
342: true, true, "Ok");
343: modalDialog.add(ccp, BorderLayout.SOUTH);
344:
345: modalDialog.setSize(400, 250);
346: modalDialog.setLocation((int) this .editPathButton
347: .getLocationOnScreen().getX() - 150,
348: (int) this .editPathButton.getLocationOnScreen().getY());
349: modalDialog.setVisible(true);
350:
351: // if accepted => set the path field back
352: if (!ccp.getWasCancelled()) {
353: setFiles(filesTableModel.getAllFiles());
354: }
355:
356: }
357:
358: class FileCellRenderer extends DefaultTableCellRenderer {
359: @Override
360: public Component getTableCellRendererComponent(
361: final JTable table, final Object val, boolean sel,
362: boolean foc, int row, int col) {
363: Component cmp = super .getTableCellRendererComponent(table,
364: val, sel, foc, row, col);
365: File f = new File("" + val);
366: if (!f.exists()) {
367: cmp.setForeground(ThemesManager.getInstance().getRed());
368: } else {
369: cmp.setForeground(UIManager
370: .getColor("Table.foreground"));
371: }
372: return cmp;
373: }
374: }
375:
376: class FilesTableModel extends AbstractTableModel {
377: List<File> files = new ArrayList<File>();
378:
379: public FilesTableModel(List<File> allFiles) {
380: if (allFiles != null)
381: files.addAll(allFiles);
382: }
383:
384: public File getLastFileOrDefault() {
385: if (files.size() == 0)
386: return defaultFile;
387: return files.get(files.size() - 1);
388: }
389:
390: public List<File> getAllFiles() {
391: return files;
392: }
393:
394: public int getColumnCount() {
395: return 1;
396: }
397:
398: public int getRowCount() {
399: return files.size();
400: }
401:
402: @Override
403: public String getColumnName(int col) {
404: return "Jar file";
405: }
406:
407: @Override
408: public Object getValueAt(int row, int col) {
409: return files.get(row);
410: }
411:
412: @Override
413: public void setValueAt(Object val, int row, int col) {
414:
415: files.set(row, new File("" + val));
416: //this.fireTableDataChanged();
417: }
418:
419: @Override
420: public boolean isCellEditable(int row, int col) {
421: return true;
422: }
423:
424: public void add(File file) {
425: files.add(file);
426: this .fireTableDataChanged();
427: }
428:
429: public void deleteSelected(int[] sel) {
430: Arrays.sort(sel);
431: for (int i = sel.length - 1; i >= 0; i--) {
432: int pos = sel[i];
433: files.remove(pos);
434: }
435: this .fireTableDataChanged();
436: }
437:
438: public int moveUp(int sel) {
439: if (sel < 1)
440: return -1;
441: File f = files.remove(sel);
442: files.add(sel - 1, f);
443: this .fireTableDataChanged();
444: return sel - 1;
445: }
446:
447: public int moveDown(int sel) {
448: if (sel >= files.size() - 1)
449: return -1;
450: File f = files.remove(sel);
451: files.add(sel + 1, f);
452: this .fireTableDataChanged();
453: return sel + 1;
454: }
455: }
456:
457: /*test
458: public static void main( String[] arguments )
459: {
460: JFrame jf = new JFrame("test");
461: FileListField ff = new FileListField("Hello", JFileChooser.FILES_ONLY, null, jf);
462:
463: jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
464:
465: jf.setContentPane(ff);
466: jf.pack();
467: jf.setVisible(true);
468: jf.setLocationRelativeTo(null);
469: }*/
470:
471: }
|