source: josm/trunk/src/org/openstreetmap/josm/gui/io/LayerNameAndFilePathTableCell.java@ 5886

Last change on this file since 5886 was 5886, checked in by Don-vip, 11 years ago

see #4429 - Right click menu "undo, cut, copy, paste, delete, select all" for each text component (originally based on patch by NooN)

File size: 8.7 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.io;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Color;
7import java.awt.Component;
8import java.awt.Dimension;
9import java.awt.Font;
10import java.awt.GridBagLayout;
11import java.awt.event.ActionEvent;
12import java.awt.event.FocusAdapter;
13import java.awt.event.FocusEvent;
14import java.io.File;
15import java.util.EventObject;
16import java.util.concurrent.CopyOnWriteArrayList;
17
18import javax.swing.AbstractAction;
19import javax.swing.BorderFactory;
20import javax.swing.JButton;
21import javax.swing.JLabel;
22import javax.swing.JPanel;
23import javax.swing.JTable;
24import javax.swing.event.CellEditorListener;
25import javax.swing.event.ChangeEvent;
26import javax.swing.table.TableCellEditor;
27import javax.swing.table.TableCellRenderer;
28
29import org.openstreetmap.josm.actions.SaveActionBase;
30import org.openstreetmap.josm.tools.GBC;
31import org.openstreetmap.josm.gui.widgets.JosmTextField;
32
33class LayerNameAndFilePathTableCell extends JPanel implements TableCellRenderer, TableCellEditor {
34 private final static Color colorError = new Color(255,197,197);
35 private final static String separator = System.getProperty("file.separator");
36 private final static String ellipsis = "…" + separator;
37
38 private final JLabel lblLayerName = new JLabel();
39 private final JLabel lblFilename = new JLabel("");
40 private final JosmTextField tfFilename = new JosmTextField();
41 private final JButton btnFileChooser = new JButton(new LaunchFileChooserAction());
42
43 private final static GBC defaultCellStyle = GBC.eol().fill(GBC.HORIZONTAL).insets(2, 0, 2, 0);
44
45 private CopyOnWriteArrayList<CellEditorListener> listeners;
46 private File value;
47
48
49 /** constructor that sets the default on each element **/
50 public LayerNameAndFilePathTableCell() {
51 setLayout(new GridBagLayout());
52
53 lblLayerName.setPreferredSize(new Dimension(lblLayerName.getPreferredSize().width, 19));
54 lblLayerName.setFont(lblLayerName.getFont().deriveFont(Font.BOLD));
55
56 lblFilename.setPreferredSize(new Dimension(lblFilename.getPreferredSize().width, 19));
57 lblFilename.setOpaque(true);
58
59 tfFilename.setToolTipText(tr("Either edit the path manually in the text field or click the \"...\" button to open a file chooser."));
60 tfFilename.setPreferredSize(new Dimension(tfFilename.getPreferredSize().width, 19));
61 tfFilename.addFocusListener(
62 new FocusAdapter() {
63 @Override public void focusGained(FocusEvent e) {
64 tfFilename.selectAll();
65 }
66 }
67 );
68 // hide border
69 tfFilename.setBorder(BorderFactory.createLineBorder(getBackground()));
70
71 btnFileChooser.setPreferredSize(new Dimension(20, 19));
72 btnFileChooser.setOpaque(true);
73
74 listeners = new CopyOnWriteArrayList<CellEditorListener>();
75 }
76
77 /** renderer used while not editing the file path **/
78 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
79 boolean hasFocus, int row, int column) {
80 removeAll();
81 SaveLayerInfo info = (SaveLayerInfo)value;
82 StringBuilder sb = new StringBuilder();
83 sb.append("<html>");
84 sb.append(addLblLayerName(info));
85 sb.append("<br>");
86 add(btnFileChooser, GBC.std());
87 sb.append(addLblFilename(info));
88
89 sb.append("</html>");
90 setToolTipText(sb.toString());
91 return this;
92 }
93
94 /** renderer used while the file path is being edited **/
95 public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected,
96 int row, int column) {
97 removeAll();
98 SaveLayerInfo info = (SaveLayerInfo)value;
99 value = info.getFile();
100 tfFilename.setText(value == null ? "" : value.toString());
101
102 StringBuilder sb = new StringBuilder();
103 sb.append("<html>");
104 sb.append(addLblLayerName(info));
105 sb.append("<br/>");
106
107 add(btnFileChooser, GBC.std());
108 add(tfFilename, GBC.eol().fill(GBC.HORIZONTAL).insets(1, 0, 0, 0));
109 tfFilename.selectAll();
110
111 sb.append(tfFilename.getToolTipText());
112 sb.append("</html>");
113 setToolTipText(sb.toString());
114 return this;
115 }
116
117 private static boolean canWrite(File f) {
118 if (f == null) return false;
119 if (f.isDirectory()) return false;
120 if (f.exists() && f.canWrite()) return true;
121 if (!f.exists() && f.getParentFile() != null && f.getParentFile().canWrite())
122 return true;
123 return false;
124 }
125
126 /** adds layer name label to (this) using the given info. Returns tooltip that
127 * should be added to the panel **/
128 private String addLblLayerName(SaveLayerInfo info) {
129 lblLayerName.setIcon(info.getLayer().getIcon());
130 lblLayerName.setText(info.getName());
131 add(lblLayerName, defaultCellStyle);
132 return tr("The bold text is the name of the layer.");
133 }
134
135 /** adds filename label to (this) using the given info. Returns tooltip that
136 * should be added to the panel */
137 private String addLblFilename(SaveLayerInfo info) {
138 String tooltip = "";
139 boolean error = false;
140 if (info.getFile() == null) {
141 error = info.isDoSaveToFile();
142 lblFilename.setText(tr("Click here to choose save path"));
143 lblFilename.setFont(lblFilename.getFont().deriveFont(Font.ITALIC));
144 tooltip = tr("Layer ''{0}'' is not backed by a file", info.getName());
145 } else {
146 String t = info.getFile().getPath();
147 lblFilename.setText(makePathFit(t));
148 tooltip = info.getFile().getAbsolutePath();
149 if (info.isDoSaveToFile() && !canWrite(info.getFile())) {
150 error = true;
151 tooltip = tr("File ''{0}'' is not writable. Please enter another file name.", info.getFile().getPath());
152 }
153 }
154
155 lblFilename.setBackground(error ? colorError : getBackground());
156 btnFileChooser.setBackground(error ? colorError : getBackground());
157
158 add(lblFilename, defaultCellStyle);
159 return tr("Click cell to change the file path.") + "<br/>" + tooltip;
160 }
161
162 /** makes the given path fit lblFilename, appends ellipsis on the left if it doesn’t fit.
163 * Idea: /home/user/josm → …/user/josm → …/josm; and take the first one that fits */
164 private String makePathFit(String t) {
165 boolean hasEllipsis = false;
166 while(t != null && !t.isEmpty()) {
167 int txtwidth = lblFilename.getFontMetrics(lblFilename.getFont()).stringWidth(t);
168 if(txtwidth < lblFilename.getWidth() || t.lastIndexOf(separator) < ellipsis.length()) {
169 break;
170 }
171 // remove ellipsis, if present
172 t = hasEllipsis ? t.substring(ellipsis.length()) : t;
173 // cut next block, and re-add ellipsis
174 t = ellipsis + t.substring(t.indexOf(separator) + 1);
175 hasEllipsis = true;
176 }
177 return t;
178 }
179
180 public void addCellEditorListener(CellEditorListener l) {
181 if (l != null) {
182 listeners.addIfAbsent(l);
183 }
184 }
185
186 protected void fireEditingCanceled() {
187 for (CellEditorListener l: listeners) {
188 l.editingCanceled(new ChangeEvent(this));
189 }
190 }
191
192 protected void fireEditingStopped() {
193 for (CellEditorListener l: listeners) {
194 l.editingStopped(new ChangeEvent(this));
195 }
196 }
197
198 public void cancelCellEditing() {
199 fireEditingCanceled();
200 }
201
202 public Object getCellEditorValue() {
203 return value;
204 }
205
206 public boolean isCellEditable(EventObject anEvent) {
207 return true;
208 }
209
210 public void removeCellEditorListener(CellEditorListener l) {
211 listeners.remove(l);
212 }
213
214 public boolean shouldSelectCell(EventObject anEvent) {
215 return true;
216 }
217
218 public boolean stopCellEditing() {
219 if (tfFilename.getText() == null || tfFilename.getText().trim().equals("")) {
220 value = null;
221 } else {
222 value = new File(tfFilename.getText());
223 }
224 fireEditingStopped();
225 return true;
226 }
227
228 private class LaunchFileChooserAction extends AbstractAction {
229 public LaunchFileChooserAction() {
230 putValue(NAME, "...");
231 putValue(SHORT_DESCRIPTION, tr("Launch a file chooser to select a file"));
232 }
233
234 public void actionPerformed(ActionEvent e) {
235 File f = SaveActionBase.createAndOpenSaveFileChooser(tr("Select filename"), "osm");
236 if (f != null) {
237 tfFilename.setText(f.toString());
238 stopCellEditing();
239 }
240 }
241 }
242}
Note: See TracBrowser for help on using the repository browser.