2 * Copyright (C) 2003 Morten O. Alver, Nizar N. Batada
4 * All programs in this directory and subdirectories are published under the GNU
5 * General Public License as described below.
7 * This program is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option) any later
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
21 * Further information about the GNU GPL is available at:
22 * http://www.gnu.org/copyleft/gpl.ja.html
25 package net.sf.jabref;
28 import java.awt.event.FocusEvent;
29 import java.awt.event.FocusListener;
30 import java.util.HashMap;
31 import java.util.HashSet;
32 import java.util.Iterator;
33 import java.util.List;
36 import javax.swing.event.DocumentEvent;
37 import javax.swing.event.DocumentListener;
38 import javax.swing.text.JTextComponent;
40 import net.sf.jabref.gui.AutoCompleteListener;
41 import net.sf.jabref.gui.AutoCompleter;
42 import net.sf.jabref.gui.FileListEditor;
44 import com.jgoodies.forms.builder.DefaultFormBuilder;
45 import com.jgoodies.forms.layout.FormLayout;
48 * A single tab displayed in the EntryEditor holding several FieldEditors.
50 * @author $Author: coezbek $
51 * @version $Revision: 2488 $ ($Date: 2007-11-14 01:25:31 +0100 (Wed, 14 Nov 2007) $)
54 public class EntryEditorTab {
56 private JPanel panel = new JPanel();
58 private String[] fields;
60 private EntryEditor parent;
62 private HashMap<String, FieldEditor> editors = new HashMap<String, FieldEditor>();
64 private FieldEditor activeField = null;
66 public EntryEditorTab(JabRefFrame frame, BasePanel panel, List<String> fields, EntryEditor parent,
67 boolean addKeyField, String name) {
69 this.fields = fields.toArray(new String[0]);
71 this.fields = new String[] {};
75 setupPanel(frame, panel, addKeyField, name);
78 * The following line makes sure focus cycles inside tab instead of
79 * being lost to other parts of the frame:
81 panel.setFocusCycleRoot(true);
85 void setupPanel(JabRefFrame frame, BasePanel bPanel, boolean addKeyField, String title) {
87 InputMap im = panel.getInputMap(JComponent.WHEN_FOCUSED);
88 ActionMap am = panel.getActionMap();
90 im.put(Globals.prefs.getKey("Entry editor, previous entry"), "prev");
91 am.put("prev", parent.prevEntryAction);
92 im.put(Globals.prefs.getKey("Entry editor, next entry"), "next");
93 am.put("next", parent.nextEntryAction);
95 im.put(Globals.prefs.getKey("Entry editor, store field"), "store");
96 am.put("store", parent.storeFieldAction);
97 im.put(Globals.prefs.getKey("Entry editor, next panel"), "right");
98 im.put(Globals.prefs.getKey("Entry editor, next panel 2"), "right");
99 am.put("left", parent.switchLeftAction);
100 im.put(Globals.prefs.getKey("Entry editor, previous panel"), "left");
101 im.put(Globals.prefs.getKey("Entry editor, previous panel 2"), "left");
102 am.put("right", parent.switchRightAction);
103 im.put(Globals.prefs.getKey("Help"), "help");
104 am.put("help", parent.helpAction);
105 im.put(Globals.prefs.getKey("Save database"), "save");
106 am.put("save", parent.saveDatabaseAction);
107 im.put(Globals.prefs.getKey("Next tab"), "nexttab");
108 am.put("nexttab", parent.frame.nextTab);
109 im.put(Globals.prefs.getKey("Previous tab"), "prevtab");
110 am.put("prevtab", parent.frame.prevTab);
113 panel.setName(title);
114 //String rowSpec = "left:pref, 4dlu, fill:pref:grow, 4dlu, fill:pref";
115 String colSpec = "fill:pref, 1dlu, fill:pref:grow, 1dlu, fill:pref";
116 StringBuffer sb = new StringBuffer();
117 for (int i = 0; i < fields.length; i++) {
118 sb.append("fill:pref:grow, ");
121 sb.append("4dlu, fill:pref");
123 sb.delete(sb.length()-2, sb.length());
124 String rowSpec = sb.toString();
126 DefaultFormBuilder builder = new DefaultFormBuilder
127 (new FormLayout(colSpec, rowSpec), panel);
129 for (int i = 0; i < fields.length; i++) {
130 // Create the text area:
131 int editorType = BibtexFields.getEditorType(fields[i]);
133 final FieldEditor ta;
134 if (editorType == GUIGlobals.FILE_LIST_EDITOR)
135 ta = new FileListEditor(frame, bPanel.metaData(), fields[i], null, parent);
137 ta = new FieldTextArea(fields[i], null);
139 JComponent ex = parent.getExtra(fields[i], ta);
140 setupJTextComponent(ta.getTextComponent());
142 // Add autocompleter listener, if required for this field:
143 AutoCompleter autoComp = bPanel.getAutoCompleter(fields[i]);
144 if (autoComp != null) {
145 ta.getTextComponent().addKeyListener(new AutoCompleteListener(autoComp));
148 // Store the editor for later reference:
149 editors.put(fields[i], ta);
152 //System.out.println(fields[i]+": "+BibtexFields.getFieldWeight(fields[i]));
153 ta.getPane().setPreferredSize(new Dimension(100,
154 (int)(50.0*BibtexFields.getFieldWeight(fields[i]))));
155 builder.append(ta.getLabel());
157 builder.append(ta.getPane(), 3);
159 builder.append(ta.getPane());
160 JPanel pan = new JPanel();
161 pan.setLayout(new BorderLayout());
162 pan.add(ex, BorderLayout.NORTH);
168 // Add the edit field for Bibtex-key.
170 final FieldTextField tf = new FieldTextField(BibtexFields.KEY_FIELD, parent
171 .getEntry().getField(BibtexFields.KEY_FIELD), true);
172 setupJTextComponent(tf);
174 editors.put("bibtexkey", tf);
176 * If the key field is the only field, we should have only one
177 * editor, and this one should be set as active initially:
179 if (editors.size() == 1)
182 builder.append(tf.getLabel());
183 builder.append(tf, 3);
190 public BibtexEntry getEntry() {
194 boolean isFieldModified(FieldEditor f) {
195 String text = f.getText().trim();
197 if (text.length() == 0) {
198 return getEntry().getField(f.getFieldName()) != null;
200 Object entryValue = getEntry().getField(f.getFieldName());
201 return entryValue == null || !entryValue.toString().equals(text);
205 public void markIfModified(FieldEditor f) {
206 // Only mark as changed if not already is and the field was indeed
208 if (!updating && !parent.panel.isBaseChanged() && isFieldModified(f)) {
213 void markBaseChanged() {
214 parent.panel.markBaseChanged();
218 * Only sets the activeField variable but does not focus it.
220 * Call activate afterwards.
224 public void setActive(FieldEditor c) {
228 public FieldEditor getActive() {
232 public List<String> getFields() {
233 return java.util.Arrays.asList(fields);
236 public void activate() {
237 if (activeField != null){
239 * Corrected to fix [ 1594169 ] Entry editor: navigation between panels
241 new FocusRequester(activeField.getTextComponent());
246 * Reset all fields from the data in the BibtexEntry.
249 public void updateAll() {
250 setEntry(getEntry());
253 protected boolean updating = false;
255 public void setEntry(BibtexEntry entry) {
258 Iterator<FieldEditor> i = editors.values().iterator();
259 while (i.hasNext()) {
260 FieldEditor editor = i.next();
261 Object content = entry.getField(editor.getFieldName());
262 editor.setText((content == null) ? "" : content.toString());
270 public boolean updateField(String field, String content) {
271 if (!editors.containsKey(field))
273 FieldEditor ed = editors.get(field);
278 public void validateAllFields() {
279 for (Iterator<String> i = editors.keySet().iterator(); i.hasNext();) {
280 String field = i.next();
281 FieldEditor ed = editors.get(field);
283 if (((Component) ed).hasFocus())
284 ed.setBackground(GUIGlobals.activeEditor);
286 ed.setBackground(GUIGlobals.validFieldBackground);
290 public void setEnabled(boolean enabled) {
291 Iterator<FieldEditor> i = editors.values().iterator();
292 while (i.hasNext()) {
293 FieldEditor editor = i.next();
294 editor.setEnabled(enabled);
298 public Component getPane() {
303 * Set up key bindings and focus listener for the FieldEditor.
307 public void setupJTextComponent(final JComponent component) {
309 component.addFocusListener(fieldListener);
311 InputMap im = component.getInputMap(JComponent.WHEN_FOCUSED);
312 ActionMap am = component.getActionMap();
314 im.put(Globals.prefs.getKey("Entry editor, previous entry"), "prev");
315 am.put("prev", parent.prevEntryAction);
316 im.put(Globals.prefs.getKey("Entry editor, next entry"), "next");
317 am.put("next", parent.nextEntryAction);
319 im.put(Globals.prefs.getKey("Entry editor, store field"), "store");
320 am.put("store", parent.storeFieldAction);
321 im.put(Globals.prefs.getKey("Entry editor, next panel"), "right");
322 im.put(Globals.prefs.getKey("Entry editor, next panel 2"), "right");
323 am.put("left", parent.switchLeftAction);
324 im.put(Globals.prefs.getKey("Entry editor, previous panel"), "left");
325 im.put(Globals.prefs.getKey("Entry editor, previous panel 2"), "left");
326 am.put("right", parent.switchRightAction);
327 im.put(Globals.prefs.getKey("Help"), "help");
328 am.put("help", parent.helpAction);
329 im.put(Globals.prefs.getKey("Save database"), "save");
330 am.put("save", parent.saveDatabaseAction);
331 im.put(Globals.prefs.getKey("Next tab"), "nexttab");
332 am.put("nexttab", parent.frame.nextTab);
333 im.put(Globals.prefs.getKey("Previous tab"), "prevtab");
334 am.put("prevtab", parent.frame.prevTab);
337 HashSet<AWTKeyStroke> keys = new HashSet<AWTKeyStroke>(component
338 .getFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS));
340 keys.add(AWTKeyStroke.getAWTKeyStroke("pressed TAB"));
341 component.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, keys);
342 keys = new HashSet<AWTKeyStroke>(component
343 .getFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS));
345 keys.add(KeyStroke.getKeyStroke("shift pressed TAB"));
346 component.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, keys);
347 } catch (Throwable t) {
348 System.err.println(t);
354 * Focus listener that fires the storeFieldAction when a FieldTextArea loses
357 * TODO: It would be nice to test this thoroughly.
359 FocusListener fieldListener = new FocusListener() {
365 public void focusGained(FocusEvent e) {
369 c.getDocument().removeDocumentListener(d);
374 if (e.getSource() instanceof JTextComponent) {
376 c = (JTextComponent) e.getSource();
378 * [ 1553552 ] Not properly detecting changes to flag as
381 d = new DocumentListener() {
383 void fire(DocumentEvent e) {
384 if (c.isFocusOwner()) {
385 markIfModified((FieldEditor) c);
389 public void changedUpdate(DocumentEvent e) {
393 public void insertUpdate(DocumentEvent e) {
397 public void removeUpdate(DocumentEvent e) {
401 c.getDocument().addDocumentListener(d);
405 setActive((FieldEditor) e.getSource());
409 public void focusLost(FocusEvent e) {
410 synchronized (this) {
412 c.getDocument().removeDocumentListener(d);
417 if (!e.isTemporary())
418 parent.updateField(e.getSource());