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;
27 import java.awt.AWTKeyStroke;
28 import java.awt.BorderLayout;
29 import java.awt.Component;
30 import java.awt.Dimension;
31 import java.awt.KeyboardFocusManager;
32 import java.awt.event.FocusEvent;
33 import java.awt.event.FocusListener;
34 import java.util.HashMap;
35 import java.util.HashSet;
36 import java.util.Iterator;
37 import java.util.List;
39 import javax.swing.ActionMap;
40 import javax.swing.InputMap;
41 import javax.swing.JComponent;
42 import javax.swing.JPanel;
43 import javax.swing.KeyStroke;
44 import javax.swing.event.DocumentEvent;
45 import javax.swing.event.DocumentListener;
46 import javax.swing.text.JTextComponent;
48 import com.jgoodies.forms.builder.DefaultFormBuilder;
49 import com.jgoodies.forms.layout.FormLayout;
50 import net.sf.jabref.gui.AutoCompleter;
51 import net.sf.jabref.gui.AutoCompleteListener;
52 import net.sf.jabref.gui.FileListEditor;
55 * A single tab displayed in the EntryEditor holding several FieldEditors.
57 * @author $Author: mortenalver $
58 * @version $Revision: 2114 $ ($Date: 2007-06-13 22:47:58 +0200 (Wed, 13 Jun 2007) $)
61 public class EntryEditorTab {
63 private JPanel panel = new JPanel();
65 private String[] fields;
67 private EntryEditor parent;
69 private HashMap editors = new HashMap();
71 private FieldEditor activeField = null;
73 private Component firstComponent;
75 public EntryEditorTab(JabRefFrame frame, BasePanel panel, List fields, EntryEditor parent,
76 boolean addKeyField, String name) {
78 this.fields = (String[]) fields.toArray(new String[0]);
80 this.fields = new String[] {};
84 setupPanel(frame, panel, addKeyField, name);
87 * The following line makes sure focus cycles inside tab instead of
88 * being lost to other parts of the frame:
90 panel.setFocusCycleRoot(true);
94 void setupPanel(JabRefFrame frame, BasePanel bPanel, boolean addKeyField, String title) {
96 InputMap im = panel.getInputMap(JComponent.WHEN_FOCUSED);
97 ActionMap am = panel.getActionMap();
99 im.put(Globals.prefs.getKey("Entry editor, previous entry"), "prev");
100 am.put("prev", parent.prevEntryAction);
101 im.put(Globals.prefs.getKey("Entry editor, next entry"), "next");
102 am.put("next", parent.nextEntryAction);
104 im.put(Globals.prefs.getKey("Entry editor, store field"), "store");
105 am.put("store", parent.storeFieldAction);
106 im.put(Globals.prefs.getKey("Entry editor, next panel"), "right");
107 im.put(Globals.prefs.getKey("Entry editor, next panel 2"), "right");
108 am.put("left", parent.switchLeftAction);
109 im.put(Globals.prefs.getKey("Entry editor, previous panel"), "left");
110 im.put(Globals.prefs.getKey("Entry editor, previous panel 2"), "left");
111 am.put("right", parent.switchRightAction);
112 im.put(Globals.prefs.getKey("Help"), "help");
113 am.put("help", parent.helpAction);
114 im.put(Globals.prefs.getKey("Save database"), "save");
115 am.put("save", parent.saveDatabaseAction);
116 im.put(Globals.prefs.getKey("Next tab"), "nexttab");
117 am.put("nexttab", parent.frame.nextTab);
118 im.put(Globals.prefs.getKey("Previous tab"), "prevtab");
119 am.put("prevtab", parent.frame.prevTab);
122 panel.setName(title);
123 //String rowSpec = "left:pref, 4dlu, fill:pref:grow, 4dlu, fill:pref";
124 String colSpec = "fill:pref, 1dlu, fill:pref:grow, 1dlu, fill:pref";
125 StringBuffer sb = new StringBuffer();
126 for (int i = 0; i < fields.length; i++) {
127 sb.append("fill:pref:grow, ");
130 sb.append("4dlu, fill:pref");
132 sb.delete(sb.length()-2, sb.length());
133 String rowSpec = sb.toString();
135 DefaultFormBuilder builder = new DefaultFormBuilder
136 (panel, new FormLayout(colSpec, rowSpec));
138 for (int i = 0; i < fields.length; i++) {
139 // Create the text area:
140 int editorType = BibtexFields.getEditorType(fields[i]);
142 final FieldEditor ta;
143 if (editorType == GUIGlobals.FILE_LIST_EDITOR)
144 ta = new FileListEditor(frame, bPanel.metaData(), fields[i], null, parent);
146 ta = new FieldTextArea(fields[i], null);
148 JComponent ex = parent.getExtra(fields[i], ta);
149 setupJTextComponent(ta.getTextComponent());
151 // Add autocompleter listener, if required for this field:
152 AutoCompleter autoComp = bPanel.getAutoCompleter(fields[i]);
153 if (autoComp != null) {
154 ta.getTextComponent().addKeyListener(new AutoCompleteListener(autoComp));
157 // Store the editor for later reference:
158 editors.put(fields[i], ta);
161 //System.out.println(fields[i]+": "+BibtexFields.getFieldWeight(fields[i]));
162 ta.getPane().setPreferredSize(new Dimension(100,
163 (int)(50.0*BibtexFields.getFieldWeight(fields[i]))));
164 builder.append(ta.getLabel());
166 builder.append(ta.getPane(), 3);
168 builder.append(ta.getPane());
169 JPanel pan = new JPanel();
170 pan.setLayout(new BorderLayout());
171 pan.add(ex, BorderLayout.NORTH);
177 // Add the edit field for Bibtex-key.
179 final FieldTextField tf = new FieldTextField(BibtexFields.KEY_FIELD, (String) parent
180 .getEntry().getField(BibtexFields.KEY_FIELD), true);
181 setupJTextComponent(tf);
183 editors.put("bibtexkey", tf);
185 * If the key field is the only field, we should have only one
186 * editor, and this one should be set as active initially:
188 if (editors.size() == 1)
191 builder.append(tf.getLabel());
192 builder.append(tf, 3);
199 public BibtexEntry getEntry() {
203 boolean isFieldModified(FieldEditor f) {
204 String text = f.getText().trim();
206 if (text.length() == 0) {
207 return getEntry().getField(f.getFieldName()) != null;
209 Object entryValue = getEntry().getField(f.getFieldName());
210 return entryValue == null || !entryValue.toString().equals(text);
214 public void markIfModified(FieldEditor f) {
215 // Only mark as changed if not already is and the field was indeed
217 if (!updating && !parent.panel.isBaseChanged() && isFieldModified(f)) {
222 void markBaseChanged() {
223 parent.panel.markBaseChanged();
227 * Only sets the activeField variable but does not focus it.
229 * Call activate afterwards.
233 public void setActive(FieldEditor c) {
237 public FieldEditor getActive() {
241 public List getFields() {
242 return java.util.Arrays.asList(fields);
245 public void activate() {
246 if (activeField != null){
248 * Corrected to fix [ 1594169 ] Entry editor: navigation between panels
250 new FocusRequester(activeField.getTextComponent());
255 * Reset all fields from the data in the BibtexEntry.
258 public void updateAll() {
259 setEntry(getEntry());
262 protected boolean updating = false;
264 public void setEntry(BibtexEntry entry) {
267 Iterator i = editors.values().iterator();
268 while (i.hasNext()) {
269 FieldEditor editor = (FieldEditor) i.next();
270 Object content = entry.getField(editor.getFieldName());
271 editor.setText((content == null) ? "" : content.toString());
279 public boolean updateField(String field, String content) {
280 if (!editors.containsKey(field))
282 FieldEditor ed = (FieldEditor) editors.get(field);
287 public void validateAllFields() {
288 for (Iterator i = editors.keySet().iterator(); i.hasNext();) {
289 String field = (String) i.next();
290 FieldEditor ed = (FieldEditor) editors.get(field);
292 if (((Component) ed).hasFocus())
293 ed.setBackground(GUIGlobals.activeEditor);
295 ed.setBackground(GUIGlobals.validFieldBackground);
299 public void setEnabled(boolean enabled) {
300 Iterator i = editors.values().iterator();
301 while (i.hasNext()) {
302 FieldEditor editor = (FieldEditor) i.next();
303 editor.setEnabled(enabled);
307 public Component getPane() {
312 * Set up key bindings and focus listener for the FieldEditor.
316 public void setupJTextComponent(final JComponent component) {
318 component.addFocusListener(fieldListener);
320 InputMap im = component.getInputMap(JComponent.WHEN_FOCUSED);
321 ActionMap am = component.getActionMap();
323 im.put(Globals.prefs.getKey("Entry editor, previous entry"), "prev");
324 am.put("prev", parent.prevEntryAction);
325 im.put(Globals.prefs.getKey("Entry editor, next entry"), "next");
326 am.put("next", parent.nextEntryAction);
328 im.put(Globals.prefs.getKey("Entry editor, store field"), "store");
329 am.put("store", parent.storeFieldAction);
330 im.put(Globals.prefs.getKey("Entry editor, next panel"), "right");
331 im.put(Globals.prefs.getKey("Entry editor, next panel 2"), "right");
332 am.put("left", parent.switchLeftAction);
333 im.put(Globals.prefs.getKey("Entry editor, previous panel"), "left");
334 im.put(Globals.prefs.getKey("Entry editor, previous panel 2"), "left");
335 am.put("right", parent.switchRightAction);
336 im.put(Globals.prefs.getKey("Help"), "help");
337 am.put("help", parent.helpAction);
338 im.put(Globals.prefs.getKey("Save database"), "save");
339 am.put("save", parent.saveDatabaseAction);
340 im.put(Globals.prefs.getKey("Next tab"), "nexttab");
341 am.put("nexttab", parent.frame.nextTab);
342 im.put(Globals.prefs.getKey("Previous tab"), "prevtab");
343 am.put("prevtab", parent.frame.prevTab);
346 HashSet keys = new HashSet(component
347 .getFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS));
349 keys.add(AWTKeyStroke.getAWTKeyStroke("pressed TAB"));
350 component.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, keys);
351 keys = new HashSet(component
352 .getFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS));
354 keys.add(KeyStroke.getKeyStroke("shift pressed TAB"));
355 component.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, keys);
356 } catch (Throwable t) {
357 System.err.println(t);
363 * Focus listener that fires the storeFieldAction when a FieldTextArea loses
366 * TODO: It would be nice to test this thoroughly.
368 FocusListener fieldListener = new FocusListener() {
374 public void focusGained(FocusEvent e) {
378 c.getDocument().removeDocumentListener(d);
383 if (e.getSource() instanceof JTextComponent) {
385 c = (JTextComponent) e.getSource();
387 * [ 1553552 ] Not properly detecting changes to flag as
390 d = new DocumentListener() {
392 void fire(DocumentEvent e) {
393 if (c.isFocusOwner()) {
394 markIfModified((FieldEditor) c);
398 public void changedUpdate(DocumentEvent e) {
402 public void insertUpdate(DocumentEvent e) {
406 public void removeUpdate(DocumentEvent e) {
410 c.getDocument().addDocumentListener(d);
414 setActive((FieldEditor) e.getSource());
418 public void focusLost(FocusEvent e) {
419 synchronized (this) {
421 c.getDocument().removeDocumentListener(d);
426 if (!e.isTemporary())
427 parent.updateField((FieldEditor) e.getSource());