1 package net.sf.bibkeeper;
2
3 import javax.swing;
4 import javax.swing.event;
5 import javax.swing.text.JTextComponent;
6 import java.awt;
7 import java.util;
8 import java.awt.event;
9 import java.awt.datatransfer;
10 import java.io.StringWriter;
11 import java.io.IOException;
12 import net.sf.bibkeeper.undo.UndoableFieldChange;
13 import net.sf.bibkeeper.undo.UndoableInsertEntry;
14 import net.sf.bibkeeper.undo.UndoableRemoveEntry;
15 import net.sf.bibkeeper.undo.NamedCompound;
16 import net.sf.bibkeeper.export.LatexFieldFormatter;
17 import java.beans;
18
19 public class EntryTypeForm extends JDialog implements VetoableChangeListener {
20
21 /*
22 * GUI component that allows editing of the fields of a BibtexEntry.
23 * EntryTypeForm also registers itself as a VetoableChangeListener,
24 * receiving events whenever a field of the entry changes, enabling the
25 * text fields to update themselves if the change is made from somewhere
26 * else.
27 */
28
29 // A reference to the entry this object works on.
30 BibtexEntry entry;
31
32 CloseAction closeAction;
33 // The action concerned with closing the window.
34
35 CopyKeyAction copyKeyAction;
36 // The action concerned with copying the BibTeX key to the clipboard.
37
38 StoreFieldAction storeFieldAction;
39 // The action concerned with storing a field value.
40
41
42 SwitchLeftAction switchLeftAction = new SwitchLeftAction();
43 SwitchRightAction switchRightAction = new SwitchRightAction();
44 // The actions concerned with switching the panels.
45
46 GenerateKeyAction generateKeyAction ;
47 // The action which generates a bibtexkey for this entry.
48
49 Container contentPane = getContentPane();
50 JPanel mainPanel = new JPanel(), // The area below the toolbar.
51 srcPanel = new JPanel();
52 FieldPanel reqPanel = new FieldPanel(),
53 optPanel = new FieldPanel(),
54 genPanel = new FieldPanel();
55 JTextField bibtexKey;
56 JTextArea source;
57 JTabbedPane tabbed = new JTabbedPane();
58 GridBagLayout gbl = new GridBagLayout();
59 GridBagConstraints con = new GridBagConstraints();
60 JLabel lab;
61 BibtexBaseFrame baseFrame; // The BibtexBaseFrame this dialog belongs to.
62
63
64 boolean updateSource = true; // This can be set to false to stop the source
65 // text area from gettin updated. This is used in cases where the source
66 // couldn't be parsed, and the user is given the option to edit it.
67 boolean lastSourceAccepted = true; // This indicates whether the last attempt
68 // at parsing the source was successful. It is used to determine whether the
69 // dialog should close; it should stay open if the user received an error
70 // message about the source, whatever he or she chose to do about it.
71 String lastSourceStringAccepted = null; // This is used to prevent double
72 // updates after editing source.
73 double optW = 0, reqW = 1, genW = 0; // Variables for total weight of fields.
74 // These values can be used to calculate the preferred height for the form.
75 // reqW starts at 1 because it needs room for the bibtex key field.
76
77 private final int REQ=0, OPT=1, GEN=2, FIELD_WIDTH=40, FIELD_HEIGHT=2;
78 private final String KEY_PROPERTY = "bibtexkey";
79 BibkeeperPrefs prefs;
80 HelpAction helpAction;
81
82 public EntryTypeForm(BibtexBaseFrame baseFrame_, BibtexEntry entry_, BibkeeperPrefs prefs_) {
83 super(baseFrame_);
84 baseFrame = baseFrame_;
85 entry = entry_;
86 prefs = prefs_;
87
88 entry.addPropertyChangeListener(this);
89
90 setTitle(entry.getType().getName());
91 setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
92 helpAction = new HelpAction
93 (baseFrame.helpDiag, GUIGlobals.entryEditorHelp, "Help (F1)");
94 closeAction = new CloseAction();
95 copyKeyAction = new CopyKeyAction();
96 // generateKeyAction = new GenerateKeyAction(baseFrame,entry);
97 generateKeyAction = new GenerateKeyAction(baseFrame);
98 storeFieldAction = new StoreFieldAction(this);
99 addWindowListener(new WindowAdapter() {
100 public void windowClosing(WindowEvent e) {
101 closeAction.actionPerformed(null);
102 }
103 public void windowOpened(WindowEvent e) {
104 switch (tabbed.getSelectedIndex()) {
105 case 0:
106 reqPanel.activate();
107 break;
108 case 1:
109 optPanel.activate();
110 break;
111 case 2:
112 genPanel.activate();
113 break;
114 case 3:
115 source.requestFocus();
116 break;
117 }
118 }
119 });
120
121
122 contentPane.setLayout(new BorderLayout());
123 setupToolBar();
124 setupFieldPanels(reqPanel, optPanel, genPanel);
125 setupSourcePanel();
126 tabbed.setTabPlacement(SwingConstants.TOP);
127 tabbed.addTab("Required fields", new ImageIcon(GUIGlobals.showReqIconFile),
128 reqPanel.getPane(), "Show required fields");
129 tabbed.addTab("Optional fields", new ImageIcon(GUIGlobals.showOptIconFile),
130 optPanel.getPane(), "Show optional fields");
131 tabbed.addTab("General fields", new ImageIcon(GUIGlobals.showGenIconFile),
132 genPanel.getPane(), "Show general fields");
133 tabbed.addTab("Bibtex source", new ImageIcon(GUIGlobals.sourceIconFile),
134 srcPanel, "Show/edit bibtex source");
135 tabbed.addChangeListener(new TabListener());
136
137 contentPane.add(tabbed, BorderLayout.CENTER);
138
139 // We replace the default FocusTraversalPolicy with a subclass
140 // that only allows FieldEditor components to gain keyboard focus.
141 setFocusTraversalPolicy(new LayoutFocusTraversalPolicy() {
142 protected boolean accept(Component c) {
143 return (super.accept(c) && (c instanceof JTextComponent));
144 }
145 });
146
147 //Util.pr("opt: "+optW+" req:"+reqW);
148 int prefHeight = (int)(Math.max(genW, Math.max(optW, reqW))*GUIGlobals.FORM_HEIGHT[prefs.getInt("entryTypeFormHeightFactor")]);
149 setSize(GUIGlobals.FORM_WIDTH[prefs.getInt("entryTypeFormWidth")], prefHeight);
150 if (prefs.getBoolean("defaultShowSource")) {
151 tabbed.setSelectedIndex(3);
152 }
153 setResizable(true);
154 }
155
156 private void setupToolBar() {
157 JToolBar tlb = new JToolBar();
158
159 // The toolbar carries all the key bindings that are valid for the whole
160 // window.
161 ActionMap am = tlb.getActionMap();
162 InputMap im = tlb.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
163
164 im.put(GUIGlobals.exitDialog, "close");
165 am.put("close", closeAction);
166 im.put(KeyStroke.getKeyStroke(GUIGlobals.storeFieldKey), "store");
167 am.put("copyKey", copyKeyAction);
168 im.put(GUIGlobals.generateKeyStroke, "generateKey");
169 am.put("generateKey", generateKeyAction);
170 im.put(GUIGlobals.switchPanelLeft, "left");
171 am.put("left", switchLeftAction);
172 im.put(GUIGlobals.switchPanelRight, "right");
173 am.put("right", switchRightAction);
174 im.put(GUIGlobals.undoStroke, "undo");
175 am.put("undo", undoAction);
176 im.put(GUIGlobals.redoStroke, "redo");
177 am.put("redo", redoAction);
178 im.put(GUIGlobals.helpKeyStroke, "help");
179 am.put("help", helpAction);
180
181 tlb.setFloatable(false);
182 //tlb.add(closeAction);
183 //tlb.addSeparator();
184 tlb.add(copyKeyAction);
185 tlb.add(generateKeyAction);
186 tlb.addSeparator();
187 //tlb.add(undoAction);
188 //tlb.add(redoAction);
189 //tlb.addSeparator();
190 tlb.add(helpAction);
191 contentPane.add(tlb, BorderLayout.NORTH);
192 }
193
194 private void setupFieldPanels(FieldPanel req, FieldPanel opt, FieldPanel gen) {
195
196 // First we ask the BibtexEntry which fields are optional and
197 // required.
198 String[] reqFields = entry.getRequiredFields(),
199 optFields = entry.getOptionalFields(),
200 // genFields = new String[] {"crossref", "url", "abstract", "comment"}; // May change...
201 genFields = entry.getGeneralFields() ;
202
203 int iter, rmax, omax, gmax;
204
205 gmax = genFields.length;
206
207 if (reqFields == null) {
208 iter = optFields.length;
209 rmax = 0;
210 omax = optFields.length;
211 } else if (optFields == null) {
212 iter = reqFields.length;
213 rmax = reqFields.length;
214 omax = 0;
215 } else {
216 iter = Math.max(reqFields.length, optFields.length);
217 rmax = reqFields.length;
218 omax = optFields.length;
219 }
220 FieldTextArea ta1 = null, ta2 = null, ta3 = null, firstR = null, firstO = null;
221 String stringContent;
222 Object content;
223
224 req.setLayout(gbl);
225 opt.setLayout(gbl);
226 gen.setLayout(gbl);
227 con.insets = new Insets(10,5,0,5);
228 //con.fill = GridBagConstraints.HORIZONTAL;
229
230 con.anchor = GridBagConstraints.WEST;
231
232 for (int i=0; i<iter; i++) {
233
234 // Constraints for the labels.
235 con.gridwidth = 1;
236 con.weightx = 0;
237 con.weighty = 0;
238 con.fill = GridBagConstraints.NONE;
239 if (i<rmax) {
240 if ((content = entry.getField(reqFields[i])) != null) {
241 stringContent = content.toString();
242 } else
243 stringContent = null;
244
245 ta1 = new FieldTextArea(reqFields[i], stringContent);
246
247 setupJTextComponent(ta1);
248 if (i==0) {
249 firstR = ta1;
250 req.setActive(ta1);
251 }
252 }
253 if (i<omax) {
254 if ((content = entry.getField(optFields[i])) != null) {
255 stringContent = content.toString();
256 } else
257 stringContent = null;
258
259 ta2 = new FieldTextArea(optFields[i], stringContent);
260 setupJTextComponent(ta2);
261 if (i==0) {
262 firstO = ta2;
263 opt.setActive(ta2);
264 }
265 }
266 if (i<gmax) {
267 if ((content = entry.getField(genFields[i])) != null) {
268 stringContent = content.toString();
269 } else
270 stringContent = null;
271
272 ta3 = new FieldTextArea(genFields[i], stringContent);
273 setupJTextComponent(ta3);
274 if (i==0) {
275 firstO = ta3;
276 gen.setActive(ta3);
277 }
278 }
279 if (i<rmax) {
280 gbl.setConstraints(ta1.getLabel(),con);
281 req.add(ta1.getLabel());
282 }
283 if (i<omax) {
284 gbl.setConstraints(ta2.getLabel(),con);
285 opt.add(ta2.getLabel());
286 }
287 if (i<gmax) {
288 gbl.setConstraints(ta3.getLabel(),con);
289 gen.add(ta3.getLabel());
290 }
291
292 // Constraints for the text fields.
293 con.gridwidth = GridBagConstraints.REMAINDER;
294 con.weightx = 1;
295
296 con.fill = GridBagConstraints.BOTH;
297 if (i<rmax) {
298 con.weighty = GUIGlobals.getFieldWeight(reqFields[i]);
299 reqW += con.weighty;
300 //Util.pr(reqFields[i]+" "+con.weighty+"");
301 gbl.setConstraints(ta1.getPane(),con);
302 req.add(ta1.getPane());
303 }
304 if (i<omax) {
305 con.weighty = GUIGlobals.getFieldWeight(optFields[i]);
306 optW += con.weighty;
307 gbl.setConstraints(ta2.getPane(),con);
308 opt.add(ta2.getPane());
309 }
310 if (i<gmax) {
311 con.weighty = GUIGlobals.getFieldWeight(genFields[i]);
312 genW += con.weighty;
313 gbl.setConstraints(ta3.getPane(),con);
314 gen.add(ta3.getPane());
315 }
316 }
317 // Add the edit field for Bibtex-key.
318
319 con.insets.top += 25;
320 con.insets.bottom = 10;
321 con.gridwidth = 1;
322 con.weighty = 0;
323 con.weightx = 0;
324 con.anchor = GridBagConstraints.SOUTHWEST;
325 con.fill = GridBagConstraints.NONE;
326 FieldTextField tf =
327 new FieldTextField(KEY_PROPERTY,
328 (String)entry.getField(KEY_PROPERTY));
329 gbl.setConstraints(tf.getLabel(),con);
330 req.add(tf.getLabel());
331 con.gridwidth = GridBagConstraints.REMAINDER;
332 // con.anchor = GridBagConstraints.WEST;
333 con.weightx = 1;
334 con.fill = GridBagConstraints.HORIZONTAL;
335 setupJTextComponent(tf);
336 gbl.setConstraints(tf,con);
337 req.add(tf);
338
339 }
340
341 private void setupSourcePanel() {
342 source = new JTextArea();
343 con = new GridBagConstraints();
344 con.insets = new Insets(10,10,10,10);
345 con.fill = GridBagConstraints.BOTH;
346 con.gridwidth = GridBagConstraints.REMAINDER;
347 con.gridheight = GridBagConstraints.REMAINDER;
348 con.weightx = 1;
349 con.weighty = 1;
350 srcPanel.setLayout(gbl);
351 source.setEditable(prefs.getBoolean("enableSourceEditing"));
352 source.setLineWrap(true);
353 source.setTabSize(GUIGlobals.INDENT);
354 setupJTextComponent(source);
355 updateSource();
356
357
358 JScrollPane sp = new JScrollPane(source,
359 JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
360 JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
361 gbl.setConstraints(sp, con);
362 srcPanel.add(sp);
363
364
365 }
366
367 private void updateSource() {
368 if (updateSource) {
369 StringWriter sw = new StringWriter(200);
370 try {
371 entry.write(sw, new net.sf.bibkeeper.export.LatexFieldFormatter());
372 String srcString = sw.getBuffer().toString();
373 source.setText(srcString);
374 } catch (IOException ex) {
375 source.setText("Error: "+ex.getMessage()+"\n\n"
376 +"Correct the entry, and "
377 +"reopen editor to display/edit source.");
378 source.setEditable(false);
379 }
380 }
381 }
382
383 private void setupJTextComponent(JTextComponent ta) {
384
385 // Activate autocompletion if it should be used for this field.
386 if ((ta instanceof FieldTextArea) &&
387 (prefs.getBoolean("autoComplete"))) {
388 FieldTextArea fta = (FieldTextArea)ta;
389 Completer comp = baseFrame.getAutoCompleter(fta.getFieldName());
390 if (comp != null)
391 fta.setAutoComplete(comp);
392 }
393
394 // Set up key bindings and focus listener for the FieldEditor.
395 InputMap im = ta.getInputMap(JComponent.WHEN_FOCUSED);
396 ActionMap am = ta.getActionMap();
397 //im.put(KeyStroke.getKeyStroke(GUIGlobals.closeKey), "close");
398 //am.put("close", closeAction);
399 im.put(KeyStroke.getKeyStroke(GUIGlobals.storeFieldKey), "store");
400 am.put("store", storeFieldAction);
401 im.put(GUIGlobals.switchPanelLeft, "left");
402 am.put("left", switchLeftAction);
403 im.put(GUIGlobals.switchPanelRight, "right");
404 am.put("right", switchRightAction);
405 im.put(GUIGlobals.helpKeyStroke, "help");
406 am.put("help", helpAction);
407
408 try{
409 int i = 0 ;
410 HashSet keys = new HashSet(ta.getFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS)) ;
411 keys.add(AWTKeyStroke.getAWTKeyStroke("pressed TAB")) ;
412 ta.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, keys) ;
413 keys = new HashSet(ta.getFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS)) ;
414 keys.add(KeyStroke.getKeyStroke("shift pressed TAB")) ;
415 ta.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, keys) ;
416 }catch(Throwable t){
417 System.err.println(t) ;
418 }
419
420
421 ta.addFocusListener(new FieldListener());
422 }
423
424 class FieldListener extends FocusAdapter {
425 /*
426 * Focus listener that fires the storeFieldAction when a FieldTextArea
427 * loses focus.
428 */
429 public void focusGained(FocusEvent e) {
430 //Util.pr("Gained focus "+e.getSource().toString().substring(0,30));
431 if (e.getSource() instanceof FieldEditor) {
432 FieldEditor ta = (FieldEditor)e.getSource();
433 Component parent = ta.getParent();
434 while (!(parent instanceof FieldPanel)) {
435 parent = parent.getParent();
436 }
437 ((FieldPanel)parent).setActive(ta);
438 } else {
439 // The source panel must have been chosen. Update it.
440 if (baseFrame.baseChanged)
441 updateSource();
442 }
443 }
444
445 public void focusLost(FocusEvent e) {
446 //Util.pr("Lost focus "+e.getSource().toString().substring(0,30));
447 if (!e.isTemporary()) {
448 storeFieldAction.actionPerformed
449 (new ActionEvent(e.getSource(), 0, ""));
450 }
451 }
452
453 }
454
455 class FieldPanel extends JPanel {
456
457 /*
458 * This extension to JPanel keeps a reference to its active
459 * field, on behalf of which it requests the focus when
460 * it is told to.
461 */
462
463 FieldEditor activeField = null;
464 JScrollPane sp;
465
466 public JComponent getPane() {
467 return this; // Component to add. Return the scrollpane, if there is one.
468 }
469
470 public void setActive(FieldEditor c) {
471 activeField = c;
472 }
473
474 public Vector getFields(){
475 Vector textFields = new Vector() ;
476 Component[] components = this.getComponents() ;
477
478 try{
479 for(int i = 0 ; i < components.length ; i++){
480 if(components[i] instanceof FieldEditor){
481 textFields.add(components[i]) ;
482 }
483 //else if ((components[i] instanceof JScrollPane)) {
484 // Util.pr(((JScrollPane)components[i]).getViewport().getComponent(0).toString().substring(0,50));
485 //}
486 else if (components[i] instanceof JScrollPane) {
487 textFields.add(((JScrollPane)components[i]).getViewport()
488 .getComponent(0));
489 }
490 }
491
492
493 return textFields ;
494 }catch(ClassCastException cce){
495 System.err.println("caught in getFields: "+cce) ;
496 }
497 return null ;
498 }
499
500
501
502 public void activate() {
503 if (activeField != null)
504 activeField.requestFocus();
505 }
506
507 }
508
509 class TabListener implements ChangeListener {
510 public void stateChanged(ChangeEvent e) {
511 if (((JTabbedPane)e.getSource()).getSelectedIndex() < 3) {
512 FieldPanel fp = (FieldPanel)(((JTabbedPane)e.getSource()).getSelectedComponent());
513 fp.activate();
514 } else {
515 source.requestFocus();
516 }
517 }
518 }
519
520
521
522 class CloseAction extends AbstractAction {
523 public CloseAction() {
524 super("Close window",
525 new ImageIcon(GUIGlobals.closeIconFile));
526 putValue(SHORT_DESCRIPTION, "Close window (Ctrl-Q)");
527 }
528 public void actionPerformed(ActionEvent e) {
529 if (tabbed.getSelectedComponent() == srcPanel) {
530 storeFieldAction.actionPerformed(new ActionEvent(source, 0, ""));
531 if (lastSourceAccepted) {
532 baseFrame.entryTypeFormClosing(entry.getId());
533 dispose();
534 }
535 } else {
536 baseFrame.entryTypeFormClosing(entry.getId());
537 dispose();
538 }
539 }
540 }
541
542 class CopyKeyAction extends AbstractAction {
543 public CopyKeyAction() {
544 super("Copy BibTeX key to clipboard",
545 new ImageIcon(GUIGlobals.copyKeyIconFile));
546 putValue(SHORT_DESCRIPTION, "Copy BibTeX key to clipboard (Ctrl-K)");
547 //putValue(MNEMONIC_KEY, GUIGlobals.copyKeyCode);
548 }
549
550
551 public void actionPerformed(ActionEvent e) {
552 String s = (String)(entry.getField(KEY_PROPERTY));
553 StringSelection ss = new StringSelection(s);
554 if (s != null) {
555 Toolkit.getDefaultToolkit().getSystemClipboard().setContents(ss,ss);
556 }
557 }
558 }
559
560
561
562
563 class StoreFieldAction extends AbstractAction {
564 JDialog parent;
565 public StoreFieldAction(JDialog parent) {
566 super("Store field value");
567 this.parent = parent;
568 putValue(SHORT_DESCRIPTION, "Store field value");
569 }
570 public void actionPerformed(ActionEvent e) {
571 if (e.getSource() instanceof FieldEditor) {
572 String toSet = null, fieldName = null;
573 FieldEditor fe = (FieldEditor)e.getSource();
574 boolean set;
575 if (fe.getText().length() > 0)
576 toSet = fe.getText();
577 // We check if the field has changed, since we don't want to mark the
578 // base as changed unless we have a real change.
579 if (toSet == null) {
580 if (entry.getField(fe.getFieldName()) == null)
581 set = false;
582 else
583 set = true;
584 } else {
585 if ((entry.getField(fe.getFieldName()) != null)
586 && toSet.equals(entry.getField(fe.getFieldName()).toString()))
587 set = false;
588 else
589 set = true;
590 }
591
592 if (set) try {
593
594 // The following statement attempts to write the
595 // new contents into a StringWriter, and this will
596 // cause an IOException if the field is not
597 // properly formatted. If that happens, the field
598 // is not stored and the textarea turns red.
599
600 if (toSet != null)
601 (new LatexFieldFormatter()).format(toSet);
602
603 Object oldValue = entry.getField(fe.getFieldName());
604 entry.setField(fe.getFieldName(), toSet);
605 if ((toSet != null) && (toSet.length() > 0)) {
606 fe.setLabelColor(GUIGlobals.validFieldColor);
607 fe.setBackground(GUIGlobals.validFieldBackground);
608 } else {
609 fe.setLabelColor(GUIGlobals.nullFieldColor);
610 fe.setBackground(GUIGlobals.validFieldBackground);
611 }
612
613 // Add an UndoableFieldChange to the baseframe's undoManager.
614 baseFrame.undoManager.addEdit
615 (new UndoableFieldChange(entry, fe.getFieldName(),
616 oldValue, toSet));
617
618 baseFrame.refreshTable();
619 baseFrame.markBaseChanged();
620
621 } catch (IllegalArgumentException ex) {
622 baseFrame.output("Invalid field format: "+ex.getMessage());
623 fe.setLabelColor(GUIGlobals.invalidFieldColor);
624 fe.setBackground(GUIGlobals.invalidFieldBackground);
625 } /*catch (java.io.IOException ex2) {
626 fe.setLabelColor(GUIGlobals.invalidFieldColor);
627 fe.setBackground(GUIGlobals.invalidFieldBackground);
628 }*/
629 else {
630 // set == false
631 // We set the field and label color.
632 fe.setBackground(GUIGlobals.validFieldBackground);
633 fe.setLabelColor((toSet == null) ?
634 GUIGlobals.nullFieldColor :
635 GUIGlobals.validFieldColor);
636 }
637
638 } else if ((source.isEditable())
639 && (source.getText() != lastSourceStringAccepted)) {
640 // Store edited bibtex code.
641 BibtexParser bp = new BibtexParser
642 (new java.io.StringReader(source.getText()));
643 try {
644 BibtexDatabase db = bp.parse().getDatabase();
645 if (db.getEntryCount() > 1)
646 throw new Exception("More than one entry found.");
647 if (db.getEntryCount() < 1)
648 throw new Exception("No entries found.");
649 String id = entry.getId();
650
651 NamedCompound compound = new NamedCompound("source edit");
652 BibtexEntry nu = db.getEntryByID
653 ((String)db.getKeySet().iterator().next());
654 boolean anyChanged = false;
655
656 // First, remove fields that the user have removed.
657 Object[] fields = entry.getAllFields();
658 for (int i=0; i<fields.length; i++)
659 if (GUIGlobals.isWriteableField(fields[i].toString()))
660 if (nu.getField(fields[i].toString()) == null) {
661 compound.addEdit(new UndoableFieldChange
662 (entry, fields[i].toString(),
663 entry.getField(fields[i].toString()),
664 (Object)null));
665 entry.setField(fields[i].toString(), null);
666 anyChanged = true;
667 }
668
669 // Then set all fields that have been set by the user.
670 fields = nu.getAllFields();
671 for (int i=0; i<fields.length; i++)
672 if (entry.getField(fields[i].toString()) !=
673 nu.getField(fields[i].toString()))
674 {
675 compound.addEdit
676 (new UndoableFieldChange
677 (entry, fields[i].toString(),
678 entry.getField(fields[i].toString()),
679 nu.getField(fields[i].toString())));
680 entry.setField(fields[i].toString(),
681 nu.getField(fields[i].toString()));
682 anyChanged = true;
683 }
684 compound.end();
685 if (anyChanged)
686 baseFrame.undoManager.addEdit(compound);
687 lastSourceStringAccepted = source.getText();
688 updateAllFields();
689 lastSourceAccepted = true;
690
691 updateSource = true;
692 baseFrame.refreshTable();
693 baseFrame.markBaseChanged();
694 } catch (Throwable ex) {
695 // The source couldn't be parsed, so the user is given an
696 // error message, and the choice to keep or revert the contents
697 // of the source text field.
698 updateSource = false;
699 lastSourceAccepted = false;
700 tabbed.setSelectedComponent(srcPanel);
701
702 Object[] options = { "Edit","Revert to original source" };
703
704 int answer = JOptionPane.showOptionDialog
705 (parent, "Error: "+ex.getMessage(),
706 "Problem with parsing entry",
707 JOptionPane.YES_NO_OPTION,
708 JOptionPane.ERROR_MESSAGE,null, options,options[0]);
709 if (answer == 0) {
710 //updateSource = true;
711 } else {
712 updateSource = true;
713 updateSource();
714 }
715 }
716
717 }
718 }
719 }
720
721 class SwitchLeftAction extends AbstractAction {
722 public SwitchLeftAction() {
723 super("Switch to the panel to the left");
724 }
725 public void actionPerformed(ActionEvent e) {
726 int i = tabbed.getSelectedIndex();
727 tabbed.setSelectedIndex((i>0 ? i-1 : tabbed.getTabCount()-1));
728 if (tabbed.getSelectedComponent() instanceof FieldPanel)
729 ((FieldPanel)tabbed.getSelectedComponent()).activate();
730 // Set focus to the last used textfield.
731 }
732 }
733
734 class SwitchRightAction extends AbstractAction {
735 public SwitchRightAction() {
736 super("Switch to the panel to the right");
737 }
738 public void actionPerformed(ActionEvent e) {
739 int i = tabbed.getSelectedIndex();
740 tabbed.setSelectedIndex(i<tabbed.getTabCount()-1 ? i+1 : 0);
741 if (tabbed.getSelectedComponent() instanceof FieldPanel)
742 ((FieldPanel)tabbed.getSelectedComponent()).activate();
743 // Set focus to the last used textfield.
744 }
745 }
746
747 /*
748 class ShowReqAction extends AbstractAction {
749 public ShowReqAction() {
750 super("Show required",
751 new ImageIcon(GUIGlobals.showReqIconFile));
752 putValue(SHORT_DESCRIPTION, "Show required fields");
753 putValue(MNEMONIC_KEY, GUIGlobals.showReqKeyCode);
754 }
755
756 public void actionPerformed(ActionEvent e) {
757 //System.out.println("Show required fields");
758 tabbed.setSelectedIndex(REQ);
759 reqPanel.activate(); // Set focus to the last used textfield.
760 }
761 }
762
763 class ShowOptAction extends AbstractAction {
764 public ShowOptAction() {
765 super("Show optional",
766 new ImageIcon(GUIGlobals.showOptIconFile));
767 putValue(SHORT_DESCRIPTION, "Show optional fields");
768 putValue(MNEMONIC_KEY, GUIGlobals.showOptKeyCode);
769 }
770
771 public void actionPerformed(ActionEvent e) {
772 tabbed.setSelectedIndex(OPT);
773 optPanel.activate(); // Set focus to the last used textfield.
774 }
775 }
776
777
778 class ShowGenAction extends AbstractAction {
779 public ShowGenAction() {
780 super("Show general",
781 new ImageIcon(GUIGlobals.showGenIconFile));
782 putValue(SHORT_DESCRIPTION, "Show general fields");
783 putValue(MNEMONIC_KEY, GUIGlobals.showGenKeyCode);
784 }
785
786 public void actionPerformed(ActionEvent e) {
787 tabbed.setSelectedIndex(GEN);
788 genPanel.activate(); // Set focus to the last used textfield.
789 }
790 }
791 */
792
793
794 class GenerateKeyAction extends AbstractAction {
795 BibtexBaseFrame parent ;
796 BibtexEntry selectedEntry ;
797 // public GenerateKeyAction(BibtexBaseFrame parentFrame,BibtexEntry newEntry) {
798 public GenerateKeyAction(BibtexBaseFrame parentFrame) {
799 super("Generate Bibtexkey",
800 new ImageIcon(GUIGlobals.genKeyIconFile));
801 parent = parentFrame ;
802 // selectedEntry = newEntry ;
803 putValue(SHORT_DESCRIPTION, "Generate Bibtexkey (Ctrl-G)");
804 // putValue(MNEMONIC_KEY, GUIGlobals.showGenKeyCode);
805 }
806
807 public void actionPerformed(ActionEvent e) {
808 // tabbed.setSelectedIndex(GEN);
809 // genPanel.activate(); // Set focus to the last used textfield.
810 // JOptionPane.showMessageDialog(parent, "Geneting a key for this field ","Close", JOptionPane.INFORMATION_MESSAGE);
811 // 1. get Bitexentry for selected index (already have)
812 // 2. run the LabelMaker by it
813 // LabelMaker labelMaker = new LabelMaker() ;
814 // System.out.println("the entry: "+selectedEntry) ;
815 // selectedEntry = labelMaker.applyRule(selectedEntry) ;
816 try {
817 // this updates the table automatically, on close, but not within the tab
818 Object oldValue = entry.getField(GUIGlobals.KEY_FIELD);
819 entry = parent.labelMaker.applyRule(entry) ;
820
821 // Store undo information:
822 parent.undoManager.addEdit(new UndoableFieldChange
823 (entry, GUIGlobals.KEY_FIELD, oldValue,
824 entry.getField(GUIGlobals.KEY_FIELD)));
825
826 // here we update the field
827 String bibtexKeyData = (String) entry.getField(BibtexBaseFrame.KEY_PROPERTY) ;
828 // set the field named for "bibtexkey"
829 setField(BibtexBaseFrame.KEY_PROPERTY,bibtexKeyData) ;
830 baseFrame.markBaseChanged();
831 baseFrame.refreshTable();
832 }
833 catch (Throwable t){
834 System.err.println("error setting key: " +t) ;
835 }
836 }
837 }
838
839 UndoAction undoAction = new UndoAction();
840 class UndoAction extends AbstractAction {
841 public UndoAction() {
842 super("Undo", new ImageIcon(GUIGlobals.undoIconFile));
843 putValue(SHORT_DESCRIPTION, "Undo");
844 }
845 public void actionPerformed(ActionEvent e) {
846 baseFrame.undoAction.actionPerformed(null);
847 }
848 }
849
850 RedoAction redoAction = new RedoAction();
851 class RedoAction extends AbstractAction {
852 public RedoAction() {
853 super("Undo", new ImageIcon(GUIGlobals.redoIconFile));
854 putValue(SHORT_DESCRIPTION, "Redo");
855 }
856 public void actionPerformed(ActionEvent e) {
857 baseFrame.redoAction.actionPerformed(null);
858 }
859 }
860
861 public boolean setField(String fieldName, String newFieldData){
862 // iterate through all tabs and fields within those tabs until we get
863 // the appropriate field name.
864 // Thanks to reflection, this shouldn't be too bad
865
866 // search each panel individually
867
868 try{
869
870 if (setFieldInPanel(reqPanel, fieldName, newFieldData))
871 return true;
872 if (setFieldInPanel(optPanel, fieldName, newFieldData))
873 return true;
874 if (setFieldInPanel(genPanel, fieldName, newFieldData))
875 return true;
876
877 }catch(ClassCastException cce){
878 System.err.println("caught in setField: "+cce) ;
879 return false ;
880 }
881
882 return false ;
883 }
884
885 private boolean setFieldInPanel(FieldPanel pan, String fieldName,
886 String newFieldData) throws ClassCastException {
887 Vector fields = pan.getFields() ;
888 for(int i = 0 ; i < fields.size() ; i++){
889 if(((FieldEditor) fields.elementAt(i)).getFieldName().equals(fieldName)){
890 ((FieldEditor) fields.elementAt(i)).setText(newFieldData) ;
891 return true ;
892 }
893 }
894 return false; // Nothing found.
895 }
896
897 private void updateAllFields() {
898 FieldPanel[] panels = new FieldPanel[] {reqPanel, optPanel, genPanel};
899 for (int i=0; i<panels.length; i++) {
900 Vector fields = panels[i].getFields();
901 for (int j=0; j<fields.size(); j++) {
902 FieldEditor ed = (FieldEditor)fields.elementAt(j);
903 Object content = entry.getField(ed.getFieldName());
904 ed.setText(content == null ? "" : content.toString());
905 //if (ed.getFieldName().equals("year"))
906 // Util.pr(content.toString());
907 }
908 }
909 }
910
911 // Update the JTextArea when a field has changed.
912 public void vetoableChange(PropertyChangeEvent e) {
913 setField(e.getPropertyName(), (String)(e.getNewValue()));
914 //Util.pr(e.getPropertyName());
915 }
916
917 }