20fd94267500e10a5433a8e765328f726d8ce123
[debian/jabref.git] / src / java / net / sf / jabref / StringDialog.java
1 /*
2 Copyright (C) 2003 Nizar N. Batada, Morten O. Alver
3
4 All programs in this directory and
5 subdirectories are published under the GNU General Public License as
6 described below.
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or (at
11 your option) any later version.
12
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 USA
22
23 Further information about the GNU GPL is available at:
24 http://www.gnu.org/copyleft/gpl.ja.html
25
26 */
27
28 package net.sf.jabref;
29
30 import java.awt.event.*;
31 import java.awt.*;
32 import javax.swing.*;
33 import javax.swing.table.*;
34 import java.util.*;
35 import net.sf.jabref.undo.*;
36 import net.sf.jabref.export.LatexFieldFormatter;
37 import javax.swing.undo.CompoundEdit;
38
39 public class StringDialog extends JDialog {
40
41     // A reference to the entry this object works on.
42     BibtexDatabase base;
43     JabRefFrame frame;
44     BasePanel panel;
45     JabRefPreferences prefs;
46     TreeSet stringsSet; // Our locally sorted set of strings.
47     Object[] strings;
48
49     // Layout objects.
50     GridBagLayout gbl = new GridBagLayout();
51     GridBagConstraints con = new GridBagConstraints();
52     JLabel lab;
53     Container conPane = getContentPane();
54     JToolBar tlb = new JToolBar();
55     JPanel pan = new JPanel();
56     StringTable table;
57     HelpAction helpAction;
58
59     public StringDialog(JabRefFrame frame, BasePanel panel,
60                         BibtexDatabase base, JabRefPreferences prefs) {
61         super(frame);
62         this.frame = frame;
63         this.panel = panel;
64         this.base = base;
65         this.prefs = prefs;
66
67         sortStrings();
68
69         helpAction = new HelpAction
70             (frame.helpDiag, GUIGlobals.stringEditorHelp, "Help");
71
72
73         addWindowListener(new WindowAdapter() {
74                 public void windowClosing(WindowEvent e) {
75                     closeAction.actionPerformed(null);
76                 }
77             });
78
79         // We replace the default FocusTraversalPolicy with a subclass
80         // that only allows the StringTable to gain keyboard focus.
81         setFocusTraversalPolicy(new LayoutFocusTraversalPolicy() {
82                 protected boolean accept(Component c) {
83                     return (super.accept(c) && (c instanceof StringTable));
84                 }
85             });
86
87         setLocation(prefs.getInt("stringsPosX"), prefs.getInt("stringsPosY"));
88         setSize(prefs.getInt("stringsSizeX"), prefs.getInt("stringsSizeY"));
89
90         pan.setLayout(gbl);
91         con.fill = GridBagConstraints.BOTH;
92         con.weighty = 1;
93         con.weightx = 1;
94
95         StringTableModel stm = new StringTableModel(this, base);
96         table = new StringTable(stm);
97         if (base.getStringCount() > 0)
98             table.setRowSelectionInterval(0,0);
99
100         gbl.setConstraints(table.getPane(), con);
101         pan.add(table.getPane());
102
103         InputMap im = tlb.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
104         ActionMap am = tlb.getActionMap();
105         im.put(prefs.getKey("String dialog, add string"), "add");
106         am.put("add", newStringAction);
107         im.put(prefs.getKey("String dialog, remove string"), "remove");
108         am.put("remove", removeStringAction);
109         //im.put(prefs.getKey("String dialog, move string up"), "up");
110         //am.put("up", stringUpAction);
111         //im.put(prefs.getKey("String dialog, move string down"), "down");
112         //am.put("down", stringDownAction);
113         im.put(prefs.getKey("Close dialog"), "close");
114         am.put("close", closeAction);
115         im.put(prefs.getKey("Help"), "help");
116         am.put("help", helpAction);
117         im.put(prefs.getKey("Undo"), "undo");
118         am.put("undo", undoAction);
119         im.put(prefs.getKey("Redo"), "redo");
120         am.put("redo", redoAction);
121
122         //tlb.add(closeAction);
123         //tlb.addSeparator();
124         tlb.add(newStringAction);
125         tlb.add(removeStringAction);
126         tlb.addSeparator();
127         //tlb.add(stringUpAction);
128         //tlb.add(stringDownAction);
129         tlb.addSeparator();
130         tlb.add(helpAction);
131         conPane.add(tlb, BorderLayout.NORTH);
132         conPane.add(pan, BorderLayout.CENTER);
133
134         if (panel.file != null)
135             setTitle(Globals.lang(GUIGlobals.stringsTitle)+": "+panel.file.getName());
136         else
137             setTitle(Globals.lang(GUIGlobals.stringsTitle)+": "+Globals.lang(GUIGlobals.untitledTitle));
138
139     }
140
141     class StringTable extends JTable {
142         JScrollPane sp = new JScrollPane((JTable)this);
143         public StringTable(StringTableModel stm) {
144             super(stm);
145             setShowVerticalLines(true);
146             setShowHorizontalLines(true);
147             setColumnSelectionAllowed(true);
148             DefaultCellEditor dce = new DefaultCellEditor(new JTextField());
149             dce.setClickCountToStart(2);
150             setDefaultEditor(String.class, dce);
151             TableColumnModel cm = getColumnModel();
152             cm.getColumn(0).setPreferredWidth(800);
153             cm.getColumn(1).setPreferredWidth(2000);
154             sp.getViewport().setBackground(Globals.prefs.getColor("tableBackground"));
155             // getInputMap().remove(GUIGlobals.exitDialog);
156             getInputMap().put(frame.prefs.getKey("Close dialog"), "close");
157             getActionMap().put("close", closeAction);
158             getInputMap().put(frame.prefs.getKey("Help"), "help");
159             getActionMap().put("help", helpAction);
160
161         }
162
163         public JComponent getPane() {
164             return sp;
165         }
166
167     }
168
169     private void sortStrings() {
170         // Rebuild our sorted set of strings:
171         stringsSet = new TreeSet(new BibtexStringComparator(false));
172         Iterator i = base.getStringKeySet().iterator();
173         for (;i.hasNext();) {
174             stringsSet.add(base.getString(i.next()));
175         }
176         strings = stringsSet.toArray();
177     }
178
179     public void refreshTable() {
180         sortStrings();
181         table.revalidate();
182         table.clearSelection();
183         table.repaint();
184     }
185
186     class StringTableModel extends AbstractTableModel {
187
188         BibtexDatabase base;
189         StringDialog parent;
190
191         public StringTableModel(StringDialog parent, BibtexDatabase base) {
192             this.parent = parent;
193             this.base = base;
194         }
195
196         public Object getValueAt(int row, int col) {
197             return ((col == 0) ?
198                     ((BibtexString)strings[row]).getName() :
199                     ((BibtexString)strings[row]).getContent());
200         }
201
202         public void setValueAt(Object value, int row, int col) {
203             //      if (row >= base.getStringCount())
204             //  return; // After a Remove operation the program somehow
205                         // thinks the user is still editing an entry,
206                         // which might now be outside
207             if (col == 0) {
208                 // Change name of string.
209                 if (!((String)value).equals(((BibtexString)strings[row]).getName())) {
210                     if (base.hasStringLabel((String)value))
211                         JOptionPane.showMessageDialog(parent,
212                                                       Globals.lang("A string with that label "
213                                                                    +"already exists"),
214                                                       Globals.lang("Label"),
215                                                       JOptionPane.ERROR_MESSAGE);
216                       else if (((String)value).indexOf(" ") >= 0) {
217                         JOptionPane.showMessageDialog
218                             (parent,
219                              Globals.lang("The label of the string can not contain spaces."),
220                              Globals.lang("Label"),
221                              JOptionPane.ERROR_MESSAGE);
222                       }
223                       else if (((String)value).indexOf("#") >= 0) {
224                         JOptionPane.showMessageDialog
225                             (parent,
226                             Globals.lang("The label of the string can not contain the '#' character."),
227                             Globals.lang("Label"),
228                             JOptionPane.ERROR_MESSAGE);
229                       }   
230                       else if ((value != null) && isNumber((String)value)) {
231                           JOptionPane.showMessageDialog
232                               (parent,
233                                Globals.lang("The label of the string can not be a number."),
234                                Globals.lang("Label"),
235                                JOptionPane.ERROR_MESSAGE);
236                     }
237                     else {
238                         // Store undo information.
239                         BibtexString subject = (BibtexString)strings[row];
240                         panel.undoManager.addEdit
241                             (new UndoableStringChange
242                              (panel, subject, true,
243                               subject.getName(), (String)value));
244                         subject.setName((String)value);
245                         panel.markBaseChanged();
246                         refreshTable();
247                     }
248                 }
249             } else {
250                 // Change content of string.
251                 BibtexString subject = (BibtexString)strings[row];
252
253                 if (!((String)value).equals(subject.getContent())) {
254                     try {
255                         (new LatexFieldFormatter()).format((String)value, "__dummy");
256                     } catch (IllegalArgumentException ex) {
257                         return;
258                     }
259                     // Store undo information.
260                     panel.undoManager.addEdit
261                         (new UndoableStringChange
262                          (panel, subject, false,
263                           subject.getContent(), (String)value));
264
265                     subject.setContent((String)value);
266                     panel.markBaseChanged();
267                 }
268             }
269         }
270
271         public int getColumnCount() {
272             return 2;
273         }
274
275         public int getRowCount() {
276             return strings.length; //base.getStringCount();
277         }
278
279         public String getColumnName(int col) {
280             return ((col == 0) ?
281                     Globals.lang("Name") : Globals.lang("Content"));
282         }
283
284         public boolean isCellEditable(int row, int col) {
285             return true;
286         }
287     }
288
289     protected boolean isNumber(String name) {
290         // A pure integer number can not be used as a string label,
291         // since Bibtex will read it as a number.
292         try {
293             Integer.parseInt(name);
294             return true;
295         } catch (NumberFormatException ex) {
296             return false;
297         }
298
299     }
300
301     protected void assureNotEditing() {
302         if (table.isEditing()) {
303             int col = table.getEditingColumn(),
304                 row = table.getEditingRow();
305             table.getCellEditor(row, col).stopCellEditing();
306         }
307     }
308
309     // The action concerned with closing the window.
310     CloseAction closeAction = new CloseAction(this);
311     class CloseAction extends AbstractAction {
312         StringDialog parent;
313         public CloseAction(StringDialog parent) {
314             super("Close window");
315             //, new ImageIcon(GUIGlobals.closeIconFile));
316             putValue(SHORT_DESCRIPTION, Globals.lang("Close dialog"));
317             this.parent = parent;
318         }
319         public void actionPerformed(ActionEvent e) {
320             panel.stringsClosing();
321             dispose();
322             Point p = getLocation();
323             Dimension d = getSize();
324             prefs.putInt("stringsPosX", p.x);
325             prefs.putInt("stringsPosY", p.y);
326             prefs.putInt("stringsSizeX", d.width);
327             prefs.putInt("stringsSizeY", d.height);
328         }
329     }
330
331     NewStringAction newStringAction = new NewStringAction(this);
332     class NewStringAction extends AbstractAction {
333         StringDialog parent;
334         public NewStringAction(StringDialog parent) {
335             super("New string",
336                   new ImageIcon(GUIGlobals.addIconFile));
337             putValue(SHORT_DESCRIPTION, Globals.lang("New string"));
338             this.parent = parent;
339         }
340         public void actionPerformed(ActionEvent e) {
341             String name =
342                 JOptionPane.showInputDialog(parent,
343                                             Globals.lang("Please enter the string's label"));
344             if (name == null)
345                 return;
346             if (isNumber(name)) {
347                 JOptionPane.showMessageDialog
348                     (parent,
349                      Globals.lang("The label of the string can not be a number."),
350                      Globals.lang("Label"),
351                      JOptionPane.ERROR_MESSAGE);
352                 return;
353             }
354             if (name.indexOf("#") >= 0) {
355              JOptionPane.showMessageDialog
356                  (parent,
357                   Globals.lang("The label of the string can not contain the '#' character."),
358                   Globals.lang("Label"),
359                   JOptionPane.ERROR_MESSAGE);
360              return;
361            }           
362            if (name.indexOf(" ") >= 0) {
363              JOptionPane.showMessageDialog
364                  (parent,
365                   Globals.lang("The label of the string can not contain spaces."),
366                   Globals.lang("Label"),
367                   JOptionPane.ERROR_MESSAGE);
368              return;
369            }
370             try {
371                 String newId = Util.createNeutralId();
372                 BibtexString bs = new BibtexString(newId, name, "");
373
374                 // Store undo information:
375                 panel.undoManager.addEdit
376                     (new UndoableInsertString
377                      (panel, panel.database, bs));
378
379                 base.addString(bs);
380                 refreshTable();
381                 //              table.revalidate();
382                 panel.markBaseChanged();
383             } catch (KeyCollisionException ex) {
384                 JOptionPane.showMessageDialog(parent,
385                                               Globals.lang("A string with that label "
386                                                            +"already exists"),
387                                               Globals.lang("Label"),
388                                               JOptionPane.ERROR_MESSAGE);
389             }
390         }
391     }
392
393     StoreContentAction storeContentAction = new StoreContentAction(this);
394     class StoreContentAction extends AbstractAction {
395         StringDialog parent;
396         public StoreContentAction(StringDialog parent) {
397             super("Store string",
398                   new ImageIcon(GUIGlobals.addIconFile));
399             putValue(SHORT_DESCRIPTION, Globals.lang("Store string"));
400             this.parent = parent;
401         }
402         public void actionPerformed(ActionEvent e) {
403         }
404     }
405
406     RemoveStringAction removeStringAction = new RemoveStringAction(this);
407     class RemoveStringAction extends AbstractAction {
408         StringDialog parent;
409         public RemoveStringAction(StringDialog parent) {
410             super("Remove selected strings",
411                   new ImageIcon(GUIGlobals.removeIconFile));
412             putValue(SHORT_DESCRIPTION, Globals.lang("Remove selected strings"));
413             this.parent = parent;
414         }
415         public void actionPerformed(ActionEvent e) {
416             int[] sel = table.getSelectedRows();
417             if (sel.length > 0) {
418
419                 // Make sure no cell is being edited, as caused by the
420                 // keystroke. This makes the content hang on the screen.
421                 assureNotEditing();
422
423                 String msg = Globals.lang("Really delete the selected")+" "+
424                     ((sel.length>1) ? sel.length+" "+Globals.lang("entries")
425                      : Globals.lang("entry"))+"?";
426                 int answer = JOptionPane.showConfirmDialog(parent, msg, Globals.lang("Delete strings"),
427                                                            JOptionPane.YES_NO_OPTION,
428                                                            JOptionPane.QUESTION_MESSAGE);
429                 if (answer == JOptionPane.YES_OPTION) {
430                     CompoundEdit ce = new CompoundEdit();
431                     for (int i=sel.length-1; i>=0; i--) {
432                         // Delete the strings backwards to avoid moving indexes.
433
434                         BibtexString subject = (BibtexString)strings[sel[i]];
435
436                         // Store undo information:
437                         ce.addEdit(new UndoableRemoveString
438                                    (panel, base,
439                                     subject));
440
441                         base.removeString(subject.getId());
442                     }
443                     ce.end();
444                     panel.undoManager.addEdit(ce);
445
446                     //table.revalidate();
447                     refreshTable();
448                     if (base.getStringCount() > 0)
449                         table.setRowSelectionInterval(0,0);
450                     //table.repaint();
451                     //panel.markBaseChanged();
452                 }
453             }
454         }
455     }
456
457     /*    StringUpAction stringUpAction = new StringUpAction();
458     class StringUpAction extends AbstractAction {
459         public StringUpAction() {
460             super("Move string up",
461                   new ImageIcon(GUIGlobals.upIconFile));
462             putValue(SHORT_DESCRIPTION, Globals.lang("Move string up"));
463         }
464         public void actionPerformed(ActionEvent e) {
465             int[] sel = table.getSelectedRows();
466             if ((sel.length == 1) && (sel[0] > 0)) {
467
468                 // Make sure no cell is being edited, as caused by the
469                 // keystroke. This makes the content hang on the screen.
470                 assureNotEditing();
471                 // Store undo information:
472                 panel.undoManager.addEdit(new UndoableMoveString
473                                               (panel, base, sel[0], true));
474
475                 BibtexString bs = base.getString(sel[0]);
476                 base.removeString(sel[0]);
477                 try {
478                     base.addString(bs, sel[0]-1);
479                 } catch (KeyCollisionException ex) {}
480                 table.revalidate();
481                 table.setRowSelectionInterval(sel[0]-1, sel[0]-1);
482                 table.repaint();
483                 panel.markBaseChanged();
484             }
485         }
486     }
487
488     StringDownAction stringDownAction = new StringDownAction();
489     class StringDownAction extends AbstractAction {
490         public StringDownAction() {
491             super("Move string down",
492                   new ImageIcon(GUIGlobals.downIconFile));
493             putValue(SHORT_DESCRIPTION, Globals.lang("Move string down"));
494         }
495         public void actionPerformed(ActionEvent e) {
496             int[] sel = table.getSelectedRows();
497             if ((sel.length == 1) && (sel[0]+1 < base.getStringCount())) {
498
499                 // Make sure no cell is being edited, as caused by the
500                 // keystroke. This makes the content hang on the screen.
501                 assureNotEditing();
502
503
504                 // Store undo information:
505                 panel.undoManager.addEdit(new UndoableMoveString
506                                               (panel, base, sel[0], false));
507
508
509                 BibtexString bs = base.getString(sel[0]);
510                 base.removeString(sel[0]);
511                 try {
512                     base.addString(bs, sel[0]+1);
513                 } catch (KeyCollisionException ex) {}
514                 table.revalidate();
515                 table.setRowSelectionInterval(sel[0]+1, sel[0]+1);
516                 table.repaint();
517                 panel.markBaseChanged();
518             }
519
520         }
521     }*/
522
523     UndoAction undoAction = new UndoAction();
524     class UndoAction extends AbstractAction {
525         public UndoAction() {
526             super("Undo", new ImageIcon(GUIGlobals.undoIconFile));
527             putValue(SHORT_DESCRIPTION, Globals.lang("Undo"));
528         }
529         public void actionPerformed(ActionEvent e) {
530             try {
531                 panel.runCommand("undo");
532             } catch (Throwable ex) {}
533         }
534     }
535
536     RedoAction redoAction = new RedoAction();
537     class RedoAction extends AbstractAction {
538         public RedoAction() {
539             super("Undo", new ImageIcon(GUIGlobals.redoIconFile));
540             putValue(SHORT_DESCRIPTION, Globals.lang("Redo"));
541         }
542         public void actionPerformed(ActionEvent e) {
543             try {
544                 panel.runCommand("redo");
545             } catch (Throwable ex) {}
546         }
547     }
548
549
550 }