1a0f58a7c40098c33edcc0ab970878696809ec20
[debian/jabref.git] / src / java / net / sf / jabref / JabRefPreferences.java
1 /*
2 Copyright (C) 2003 Morten O. Alver, Nizar N. Batada
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.io.*;
31 import javax.swing.*;
32
33 import net.sf.jabref.labelPattern.DefaultLabelPatterns;
34 import net.sf.jabref.labelPattern.LabelPattern;
35 import net.sf.jabref.export.CustomExportList;
36 import net.sf.jabref.imports.CustomImportList;
37 import java.awt.*;
38 import java.util.prefs.*;
39 import java.util.*;
40 import java.awt.event.*;
41 import net.sf.jabref.export.ExportComparator;
42 import net.sf.jabref.external.ExternalFileType;
43
44 public class JabRefPreferences {
45
46     public final String
47         CUSTOM_TYPE_NAME = "customTypeName_",
48         CUSTOM_TYPE_REQ = "customTypeReq_",
49         CUSTOM_TYPE_OPT = "customTypeOpt_",
50     CUSTOM_TAB_NAME = "customTabName_",
51     CUSTOM_TAB_FIELDS = "customTabFields_";
52
53     public String WRAPPED_USERNAME;
54
55
56     Preferences prefs;
57     public HashMap defaults = new HashMap(),
58         keyBinds = new HashMap(),
59         defKeyBinds = new HashMap();
60     private HashSet putBracesAroundCapitalsFields = new HashSet(4);
61     private HashSet nonWrappableFields = new HashSet(4);
62     private static final LabelPattern KEY_PATTERN = new DefaultLabelPatterns();
63     private static LabelPattern keyPattern;
64
65     // Object containing custom export formats:
66     public CustomExportList customExports;
67
68     /** Set with all custom {@link ImportFormat}s */
69     public CustomImportList customImports;
70
71     // Object containing info about customized entry editor tabs.
72     private EntryEditorTabList tabList = null;
73
74     // Map containing all registered external file types:
75     private Map externalFileTypes = new HashMap();
76
77     // The only instance of this class:
78     private static JabRefPreferences INSTANCE = null;
79
80     public static JabRefPreferences getInstance() {
81     if (INSTANCE == null)
82         INSTANCE = new JabRefPreferences();
83     return INSTANCE;
84     }
85
86     // The constructor is made private to enforce this as a singleton class:
87     private JabRefPreferences() {
88
89         prefs = Preferences.userNodeForPackage(JabRef.class);
90         //Util.pr(prefs.toString());
91
92         if (Globals.osName.equals(Globals.MAC)) {
93             defaults.put("pdfviewer","/Applications/Preview.app");
94             defaults.put("psviewer","/Applications/Preview.app");
95             defaults.put("htmlviewer","/Applications/Safari.app");
96         }
97         else if (Globals.osName.toLowerCase().startsWith("windows")) {
98           defaults.put("pdfviewer","cmd.exe /c start /b");
99           defaults.put("psviewer","cmd.exe /c start /b");
100           defaults.put("htmlviewer","cmd.exe /c start /b");
101           defaults.put("lookAndFeel", "com.jgoodies.plaf.windows.ExtWindowsLookAndFeel");
102
103
104         }
105
106         else {
107             defaults.put("pdfviewer","acroread");
108             defaults.put("psviewer","gv");
109             defaults.put("htmlviewer","mozilla");
110             defaults.put("lookAndFeel", "com.jgoodies.plaf.plastic.Plastic3DLookAndFeel");
111
112         }
113         defaults.put("useDefaultLookAndFeel", Boolean.TRUE);
114         defaults.put("lyxpipe", System.getProperty("user.home")+File.separator+".lyx/lyxpipe");
115         defaults.put("posX", new Integer(0));
116         defaults.put("posY", new Integer(0));
117         defaults.put("sizeX", new Integer(840));
118         defaults.put("sizeY", new Integer(680));
119         defaults.put("autoResizeMode", new Integer(JTable.AUTO_RESIZE_OFF));
120         defaults.put("tableColorCodesOn", Boolean.TRUE);
121         defaults.put("namesAsIs", Boolean.FALSE);
122         defaults.put("namesFf", Boolean.FALSE);
123         defaults.put("namesLf", Boolean.FALSE);
124         defaults.put("namesNatbib", Boolean.TRUE);
125         defaults.put("abbrAuthorNames", Boolean.TRUE);
126         defaults.put("namesLastOnly", Boolean.TRUE);
127         defaults.put("language", "en");
128         defaults.put("showShort", Boolean.TRUE);
129         defaults.put("priSort", "author");
130         defaults.put("priDescending", Boolean.FALSE);
131         defaults.put("priBinary", Boolean.FALSE);
132         defaults.put("secSort", "year");
133         defaults.put("secDescending", Boolean.TRUE);
134         defaults.put("terSort", "author");
135         defaults.put("terDescending", Boolean.FALSE);
136         defaults.put("columnNames", "entrytype;author;title;year;journal;owner;timestamp;bibtexkey");
137         defaults.put("columnWidths","75;280;400;60;100;100;100;100");
138         defaults.put("numberColWidth",new Integer(GUIGlobals.NUMBER_COL_LENGTH));
139         defaults.put("workingDirectory", System.getProperty("user.home"));
140         defaults.put("exportWorkingDirectory", System.getProperty("user.home"));
141         defaults.put("autoOpenForm", Boolean.TRUE);
142         defaults.put("entryTypeFormHeightFactor", new Integer(1));
143         defaults.put("entryTypeFormWidth", new Integer(1));
144         defaults.put("backup", Boolean.TRUE);
145         defaults.put("openLastEdited", Boolean.TRUE);
146         defaults.put("lastEdited", null);
147         defaults.put("stringsPosX", new Integer(0));
148         defaults.put("stringsPosY", new Integer(0));
149         defaults.put("stringsSizeX", new Integer(600));
150         defaults.put("stringsSizeY", new Integer(400));
151         defaults.put("defaultShowSource", Boolean.FALSE);
152         defaults.put("showSource", Boolean.TRUE);
153         defaults.put("defaultAutoSort", Boolean.FALSE);
154         defaults.put("enableSourceEditing", Boolean.TRUE);
155         defaults.put("caseSensitiveSearch", Boolean.FALSE);
156         defaults.put("searchReq", Boolean.TRUE);
157         defaults.put("searchOpt", Boolean.TRUE);
158         defaults.put("searchGen", Boolean.TRUE);
159         defaults.put("searchAll", Boolean.FALSE);
160         defaults.put("incrementS", Boolean.FALSE);
161         defaults.put("saveInStandardOrder", Boolean.TRUE);
162         defaults.put("saveInOriginalOrder", Boolean.FALSE);
163         defaults.put("selectS", Boolean.FALSE);
164         defaults.put("regExpSearch", Boolean.TRUE);
165         defaults.put("searchPanePosX", new Integer(0));
166         defaults.put("searchPanePosY", new Integer(0));
167         defaults.put("autoComplete", Boolean.TRUE);
168         defaults.put("autoCompFields", new byte[] {0, 1, 28});
169         defaults.put("groupSelectorVisible", Boolean.TRUE);
170         defaults.put("groupFloatSelections", Boolean.TRUE);
171         defaults.put("groupIntersectSelections", Boolean.TRUE);
172         defaults.put("groupInvertSelections", Boolean.FALSE);
173         defaults.put("groupShowOverlapping", Boolean.FALSE);
174         defaults.put("groupSelectMatches", Boolean.FALSE);
175         defaults.put("groupsDefaultField", "keywords");
176         defaults.put("groupShowIcons", Boolean.TRUE);
177         defaults.put("groupShowDynamic", Boolean.TRUE);
178         defaults.put("groupExpandTree", Boolean.TRUE);
179         defaults.put("groupAutoShow", Boolean.TRUE);
180         defaults.put("groupAutoHide", Boolean.TRUE);
181         defaults.put("groupKeywordSeparator", ", ");
182         defaults.put("highlightGroupsMatchingAny", Boolean.FALSE);
183         defaults.put("highlightGroupsMatchingAll", Boolean.FALSE);
184         defaults.put("searchPanelVisible", Boolean.FALSE);
185
186         defaults.put("defaultEncoding", System.getProperty("file.encoding"));
187         defaults.put("winEdtPath", "C:\\Program Files\\WinEdt Team\\WinEdt\\WinEdt.exe");
188         defaults.put("latexEditorPath", "C:\\TEMP\\Led.exe");
189         defaults.put("groupsVisibleRows", new Integer(8));
190         defaults.put("defaultOwner", System.getProperty("user.name"));
191         defaults.put("preserveFieldFormatting", Boolean.FALSE);
192     // The general fields stuff is made obsolete by the CUSTOM_TAB_... entries.
193         defaults.put("generalFields", "crossref;keywords;doi;url;urldate;citeseerurl;"+
194                      "pdf;comment;owner");
195
196     // Entry editor tab 0:
197     defaults.put(CUSTOM_TAB_NAME+"_def0", Globals.lang("General"));
198         defaults.put(CUSTOM_TAB_FIELDS+"_def0", "crossref;keywords;doi;url;citeseerurl;"+
199                      "pdf;comment;owner;timestamp");
200
201     // Entry editor tab 1:
202         defaults.put(CUSTOM_TAB_FIELDS+"_def1", "abstract");
203     defaults.put(CUSTOM_TAB_NAME+"_def1", Globals.lang("Abstract"));
204
205   // Entry editor tab 2: Review Field - used for research comments, etc.
206         defaults.put(CUSTOM_TAB_FIELDS+"_def2", "review");
207     defaults.put(CUSTOM_TAB_NAME+"_def2", Globals.lang("Review"));
208
209         //defaults.put("recentFiles", "/home/alver/Documents/bibk_dok/hovedbase.bib");
210         defaults.put("historySize", new Integer(8));
211         defaults.put("fontFamily", "Times");
212         defaults.put("fontStyle", new Integer(java.awt.Font.PLAIN));
213         defaults.put("fontSize", new Integer(12));
214         defaults.put("menuFontFamily", "Times");
215         defaults.put("menuFontStyle", new Integer(java.awt.Font.PLAIN));
216         defaults.put("menuFontSize", new Integer(11));
217         // Main table color settings:
218         defaults.put("tableBackground", "255:255:255");
219         defaults.put("tableReqFieldBackground", "230:235:255");
220         defaults.put("tableOptFieldBackground", "230:255:230");
221         defaults.put("tableText", "0:0:0");
222         defaults.put("gridColor", "210:210:210");
223         defaults.put("grayedOutBackground", "210:210:210");
224         defaults.put("grayedOutText", "40:40:40");
225         defaults.put("veryGrayedOutBackground", "180:180:180");
226         defaults.put("veryGrayedOutText", "40:40:40");
227         defaults.put("markedEntryBackground", "255:255:180");
228         defaults.put("incompleteEntryBackground", "250:175:175");
229
230         defaults.put("antialias", Boolean.FALSE);
231         defaults.put("ctrlClick", Boolean.FALSE);
232         defaults.put("disableOnMultipleSelection", Boolean.FALSE);
233         defaults.put("pdfColumn", Boolean.TRUE);
234         defaults.put("urlColumn", Boolean.TRUE);
235         defaults.put("citeseerColumn", Boolean.FALSE);
236         defaults.put("useOwner", Boolean.TRUE);
237         defaults.put("allowTableEditing", Boolean.FALSE);
238         defaults.put("dialogWarningForDuplicateKey", Boolean.TRUE);
239         defaults.put("dialogWarningForEmptyKey", Boolean.TRUE);
240         defaults.put("displayKeyWarningDialogAtStartup", Boolean.TRUE);
241         defaults.put("avoidOverwritingKey", Boolean.FALSE);
242         defaults.put("warnBeforeOverwritingKey", Boolean.TRUE);
243         defaults.put("confirmDelete", Boolean.TRUE);
244         defaults.put("grayOutNonHits", Boolean.TRUE);
245         defaults.put("defaultLabelPattern", "[auth][year]");
246         defaults.put("previewEnabled", Boolean.TRUE);
247         defaults.put("preview0", "<font face=\"arial\">"
248                      +"<b><i>\\bibtextype</i><a name=\"\\bibtexkey\">\\begin{bibtexkey} (\\bibtexkey)</a>"
249                      +"\\end{bibtexkey}</b><br>__NEWLINE__"
250                      +"\\begin{author} \\format[AuthorLastFirst,HTMLChars,AuthorAbbreviator,AuthorAndsReplacer]{\\author}<BR>\\end{author}__NEWLINE__"
251                      +"\\begin{editor} \\format[AuthorLastFirst,HTMLChars,AuthorAbbreviator,AuthorAndsReplacer]{\\editor} <i>(ed.)</i><BR>\\end{editor}__NEWLINE__"
252                      +"\\begin{title} \\format[HTMLChars]{\\title} \\end{title}<BR>__NEWLINE__"
253                      +"\\begin{chapter} \\format[HTMLChars]{\\chapter}<BR>\\end{chapter}__NEWLINE__"
254                      +"\\begin{journal} <em>\\format[HTMLChars]{\\journal}, </em>\\end{journal}__NEWLINE__"
255                      // Include the booktitle field for @inproceedings, @proceedings, etc.
256                      +"\\begin{booktitle} <em>\\format[HTMLChars]{\\booktitle}, </em>\\end{booktitle}__NEWLINE__"
257                      +"\\begin{school} <em>\\format[HTMLChars]{\\school}, </em>\\end{school}__NEWLINE__"
258                      +"\\begin{institution} <em>\\format[HTMLChars]{\\institution}, </em>\\end{institution}__NEWLINE__"
259                      +"\\begin{publisher} <em>\\format[HTMLChars]{\\publisher}, </em>\\end{publisher}__NEWLINE__"
260                      +"\\begin{year}<b>\\year</b>\\end{year}\\begin{volume}<i>, \\volume</i>\\end{volume}"
261                      +"\\begin{pages}, \\format[FormatPagesForHTML]{\\pages} \\end{pages}"
262                      +"</dd>__NEWLINE__<p></p></font>");
263
264         defaults.put("preview1", "<font face=\"arial\">"
265                      +"<b><i>\\bibtextype</i><a name=\"\\bibtexkey\">\\begin{bibtexkey} (\\bibtexkey)</a>"
266                      +"\\end{bibtexkey}</b><br>__NEWLINE__"
267                      +"\\begin{author} \\format[AuthorLastFirst,HTMLChars,AuthorAbbreviator,AuthorAndsReplacer]{\\author}<BR>\\end{author}__NEWLINE__"
268                      +"\\begin{editor} \\format[AuthorLastFirst,HTMLChars,AuthorAbbreviator,AuthorAndsReplacer]{\\editor} <i>(ed.)</i><BR>\\end{editor}__NEWLINE__"
269                      +"\\begin{title} \\format[HTMLChars]{\\title} \\end{title}<BR>__NEWLINE__"
270                      +"\\begin{chapter} \\format[HTMLChars]{\\chapter}<BR>\\end{chapter}__NEWLINE__"
271                      +"\\begin{journal} <em>\\format[HTMLChars]{\\journal}, </em>\\end{journal}__NEWLINE__"
272                      // Include the booktitle field for @inproceedings, @proceedings, etc.
273                      +"\\begin{booktitle} <em>\\format[HTMLChars]{\\booktitle}, </em>\\end{booktitle}__NEWLINE__"
274                      +"\\begin{school} <em>\\format[HTMLChars]{\\school}, </em>\\end{school}__NEWLINE__"
275                      +"\\begin{institution} <em>\\format[HTMLChars]{\\institution}, </em>\\end{institution}__NEWLINE__"
276                      +"\\begin{publisher} <em>\\format[HTMLChars]{\\publisher}, </em>\\end{publisher}__NEWLINE__"
277                      +"\\begin{year}<b>\\year</b>\\end{year}\\begin{volume}<i>, \\volume</i>\\end{volume}"
278                      +"\\begin{pages}, \\format[FormatPagesForHTML]{\\pages} \\end{pages}__NEWLINE__"
279                      +"\\begin{abstract}<BR><BR><b>Abstract: </b> \\format[HTMLChars]{\\abstract} \\end{abstract}__NEWLINE__"
280                      +"\\begin{review}<BR><BR><b>Review: </b> \\format[HTMLChars]{\\review} \\end{review}"
281                      +"</dd>__NEWLINE__<p></p></font>");
282         defaults.put("autoDoubleBraces", Boolean.FALSE);
283         defaults.put("putBracesAroundCapitals","");//"title;journal;booktitle;review;abstract");
284         defaults.put("nonWrappableFields", "pdf;ps;url;doi");
285         defaults.put("useImportInspectionDialog", Boolean.TRUE);
286         defaults.put("useImportInspectionDialogForSingle", Boolean.FALSE);
287         defaults.put("generateKeysAfterInspection", Boolean.TRUE);
288         defaults.put("warnAboutDuplicatesInInspection", Boolean.TRUE);
289         defaults.put("useTimeStamp", Boolean.TRUE);
290         defaults.put("timeStampFormat", "yyyy.MM.dd");
291 //        defaults.put("timeStampField", "timestamp");
292         defaults.put("timeStampField", BibtexFields.TIMESTAMP);
293         defaults.put("generateKeysBeforeSaving", Boolean.FALSE);
294
295         defaults.put("useRemoteServer", Boolean.FALSE);
296         defaults.put("remoteServerPort", new Integer(6050));
297
298         defaults.put("personalJournalList", null);
299         defaults.put("externalJournalLists", null);
300
301         defaults.put("citeCommand", "cite");
302         //defaults.put("tempDir", System.getProperty("java.io.tmpdir"));
303         //Util.pr(System.getProperty("java.io.tempdir"));
304
305         //defaults.put("keyPattern", new LabelPattern(KEY_PATTERN));
306
307         restoreKeyBindings();
308
309         customExports = new CustomExportList(this, new ExportComparator());
310         customImports = new CustomImportList(this);
311
312         //defaults.put("oooWarning", Boolean.TRUE);
313         updateSpecialFieldHandling();
314         WRAPPED_USERNAME = "["+get("defaultOwner")+"]";
315
316         // TODO: remove temporary registering of external file types?
317         externalFileTypes.put("pdf", new ExternalFileType("PDF", "pdf", "acroread", null));
318         externalFileTypes.put("ps", new ExternalFileType("PostScript", "ps", "gs", null));
319         externalFileTypes.put("doc", new ExternalFileType("Word file", "doc", "oowriter", null));
320         externalFileTypes.put("odt", new ExternalFileType("OpenDocument text", "odt", "oowriter", null));
321
322     }
323
324     public boolean putBracesAroundCapitals(String fieldName) {
325         return putBracesAroundCapitalsFields.contains(fieldName);
326     }
327
328     public void updateSpecialFieldHandling() {
329         putBracesAroundCapitalsFields.clear();
330         String fieldString = get("putBracesAroundCapitals");
331         if (fieldString.length() > 0) {
332             String[] fields = fieldString.split(";");
333             for (int i=0; i<fields.length; i++)
334                 putBracesAroundCapitalsFields.add(fields[i]);
335         }
336         nonWrappableFields.clear();
337         fieldString = get("nonWrappableFields");
338         if (fieldString.length() > 0) {
339             String[] fields = fieldString.split(";");
340             for (int i=0; i<fields.length; i++)
341                 nonWrappableFields.add(fields[i]);
342         }
343
344     }
345
346     /**
347      * Check whether a key is set (differently from null).
348      * @param key The key to check.
349      * @return true if the key is set, false otherwise.
350      */
351     public boolean hasKey(String key) {
352         return prefs.get(key, null) != null;
353     }
354
355     public String get(String key) {
356         return prefs.get(key, (String)defaults.get(key));
357     }
358
359     public String get(String key, String def) {
360         return prefs.get(key, def);
361     }
362
363     public boolean getBoolean(String key) {
364         return prefs.getBoolean(key, ((Boolean)defaults.get(key)).booleanValue());
365     }
366
367     public double getDouble(String key) {
368         return prefs.getDouble(key, ((Double)defaults.get(key)).doubleValue());
369     }
370
371     public int getInt(String key) {
372         return prefs.getInt(key, ((Integer)defaults.get(key)).intValue());
373     }
374
375     public byte[] getByteArray(String key) {
376         return prefs.getByteArray(key, (byte[])defaults.get(key));
377     }
378
379     public void put(String key, String value) {
380         prefs.put(key, value);
381     }
382
383     public void putBoolean(String key, boolean value) {
384         prefs.putBoolean(key, value);
385     }
386
387     public void putDouble(String key, double value) {
388         prefs.putDouble(key, value);
389     }
390
391     public void putInt(String key, int value) {
392         prefs.putInt(key, value);
393     }
394
395     public void putByteArray(String key, byte[] value) {
396         prefs.putByteArray(key, value);
397     }
398
399     public void remove(String key) {
400         prefs.remove(key);
401     }
402
403     /**
404      * Puts a string array into the Preferences, by linking its elements
405      * with ';' into a single string. Escape characters make the process
406      * transparent even if strings contain ';'.
407      */
408     public void putStringArray(String key, String[] value) {
409         if (value == null) {
410             remove(key);
411             return;
412         }
413
414         if (value.length > 0) {
415             StringBuffer linked = new StringBuffer();
416             for (int i=0; i<value.length-1; i++) {
417                 linked.append(makeEscape(value[i]));
418                 linked.append(";");
419             }
420             linked.append(makeEscape(value[value.length-1]));
421             put(key, linked.toString());
422         } else {
423             put(key, "");
424         }
425     }
426
427     /**
428      * Returns a String[] containing the chosen columns.
429      */
430     public String[] getStringArray(String key) {
431         String names = get(key);
432         if (names == null)
433             return null;
434         //Util.pr(key+"\n"+names);
435         StringReader rd = new StringReader(names);
436         Vector arr = new Vector();
437         String rs;
438         try {
439             while ((rs = getNextUnit(rd)) != null) {
440                 arr.add(rs);
441             }
442         } catch (IOException ex) {}
443         String[] res = new String[arr.size()];
444         for (int i=0; i<res.length; i++)
445             res[i] = (String)arr.elementAt(i);
446
447         return res;
448     }
449
450     /**
451      * Looks up a color definition in preferences, and returns the Color object.
452      * @param key The key for this setting.
453      * @return The color corresponding to the setting.
454      */
455     public Color getColor(String key) {
456         String value = get(key);
457         int[] rgb = getRgb(value);
458         return new Color(rgb[0], rgb[1], rgb[2]);
459     }
460
461     public Color getDefaultColor(String key) {
462         String value = (String)defaults.get(key);
463         int[] rgb = getRgb(value);
464         return new Color(rgb[0], rgb[1], rgb[2]);
465     }
466
467     /**
468      * Stores a color in preferences.
469      * @param key The key for this setting.
470      * @param color The Color to store.
471      */
472     public void putColor(String key, Color color) {
473         StringBuffer sb = new StringBuffer();
474         sb.append(String.valueOf(color.getRed()));
475         sb.append(':');
476         sb.append(String.valueOf(color.getGreen()));
477         sb.append(':');
478         sb.append(String.valueOf(color.getBlue()));
479         put(key, sb.toString());
480     }
481
482     /**
483      * Looks up a color definition in preferences, and returns an array containing the RGB values.
484      * @param key The key for this setting.
485      * @return The RGB values corresponding to this color setting.
486      */
487     public int[] getRgb(String value) {
488         String[] elements = value.split(":");
489         int[] values = new int[3];
490         values[0] = Integer.parseInt(elements[0]);
491         values[1] = Integer.parseInt(elements[1]);
492         values[2] = Integer.parseInt(elements[2]);
493         return values;
494     }
495
496     /**
497      * Returns the KeyStroke for this binding, as defined by the
498      * defaults, or in the Preferences.
499      */
500     public KeyStroke getKey(String bindName) {
501
502         String s = (String)keyBinds.get(bindName);
503         // If the current key bindings don't contain the one asked for,
504         // we fall back on the default. This should only happen when a
505         // user has his own set in Preferences, and has upgraded to a
506         // new version where new bindings have been introduced.
507         if (s == null) {
508             s = (String)defKeyBinds.get(bindName);
509             // So, if this happens, we add the default value to the current
510             // hashmap, so this doesn't happen again, and so this binding
511             // will appear in the KeyBindingsDialog.
512             keyBinds.put(bindName, s);
513         }
514         if (s == null) {
515           Globals.logger("Could not get key binding for \"" + bindName + "\"");
516           //throw new RuntimeException("");
517         }
518
519         if (Globals.ON_MAC)
520           return getKeyForMac(KeyStroke.getKeyStroke(s));
521         else
522           return KeyStroke.getKeyStroke(s);
523     }
524
525     /**
526      * Returns the KeyStroke for this binding, as defined by the
527      * defaults, or in the Preferences, but adapted for Mac users,
528      * with the Command key preferred instead of Control.
529      */
530     private KeyStroke getKeyForMac(KeyStroke ks) {
531       if (ks == null) return null;
532       int keyCode = ks.getKeyCode();
533       if ((ks.getModifiers() & KeyEvent.CTRL_MASK) == 0) {
534         return ks;
535       }
536       else {
537         if ((ks.getModifiers() & KeyEvent.SHIFT_MASK) != 0) {
538           return KeyStroke.getKeyStroke(keyCode, Globals.SHORTCUT_MASK+KeyEvent.SHIFT_MASK);
539         }
540         return KeyStroke.getKeyStroke(keyCode, Globals.SHORTCUT_MASK);
541       }
542     }
543
544     /**
545      * Returns the HashMap containing all key bindings.
546      */
547     public HashMap getKeyBindings() {
548         return keyBinds;
549     }
550
551     /**
552      * Returns the HashMap containing default key bindings.
553      */
554     public HashMap getDefaultKeys() {
555         return defKeyBinds;
556     }
557
558     public void flush() {
559     try {
560         prefs.flush();
561     } catch (BackingStoreException ex) {
562         ex.printStackTrace();
563     }
564     }
565
566     /**
567      * Stores new key bindings into Preferences, provided they
568      * actually differ from the old ones.
569      */
570     public void setNewKeyBindings(HashMap newBindings) {
571         if (!newBindings.equals(keyBinds)) {
572             // This confirms that the bindings have actually changed.
573             String[] bindNames = new String[newBindings.size()],
574                 bindings = new String[newBindings.size()];
575             int index = 0;
576             for (Iterator i=newBindings.keySet().iterator();
577                  i.hasNext();) {
578                 String nm = (String)i.next();
579                 String bnd = (String)newBindings.get(nm);
580                 bindNames[index] = nm;
581                 bindings[index] = bnd;
582                 index++;
583             }
584             putStringArray("bindNames", bindNames);
585             putStringArray("bindings", bindings);
586             keyBinds = newBindings;
587         }
588     }
589
590
591         public LabelPattern getKeyPattern(){
592
593             keyPattern = new LabelPattern(KEY_PATTERN);
594             Preferences pre = Preferences.userNodeForPackage
595                 (net.sf.jabref.labelPattern.LabelPattern.class);
596             try {
597                 String[] keys = pre.keys();
598             if (keys.length > 0) for (int i=0; i<keys.length; i++)
599                 keyPattern.addLabelPattern(keys[i], pre.get(keys[i], null));
600             } catch (BackingStoreException ex) {
601                 Globals.logger("BackingStoreException in JabRefPreferences.getKeyPattern");
602             }
603
604             ///
605             //keyPattern.addLabelPattern("article", "[author][year]");
606             //putKeyPattern(keyPattern);
607             ///
608
609             return keyPattern;
610         }
611
612         public void putKeyPattern(LabelPattern pattern){
613             keyPattern = pattern;
614             LabelPattern parent = pattern.getParent();
615             if (parent == null)
616                 return;
617
618             // Store overridden definitions to Preferences.
619             Preferences pre = Preferences.userNodeForPackage
620                 (net.sf.jabref.labelPattern.LabelPattern.class);
621             try {
622                 pre.clear(); // We remove all old entries.
623             } catch (BackingStoreException ex) {
624                 Globals.logger("BackingStoreException in JabRefPreferences.putKeyPattern");
625             }
626
627             Iterator i = pattern.keySet().iterator();
628             while (i.hasNext()) {
629                 String s = (String)i.next();
630                 if (!(pattern.get(s)).equals(parent.get(s)))
631                     pre.put(s, pattern.getValue(s).get(0).toString());
632             }
633         }
634
635     private void restoreKeyBindings() {
636         // Define default keybindings.
637         defineDefaultKeyBindings();
638
639         // First read the bindings, and their names.
640         String[] bindNames = getStringArray("bindNames"),
641             bindings = getStringArray("bindings");
642
643         // Then set up the key bindings HashMap.
644         if ((bindNames == null) || (bindings == null)
645             || (bindNames.length != bindings.length)) {
646             // Nothing defined in Preferences, or something is wrong.
647             setDefaultKeyBindings();
648             return;
649         }
650
651         for (int i=0; i<bindNames.length; i++)
652             keyBinds.put(bindNames[i], bindings[i]);
653     }
654
655     private void setDefaultKeyBindings() {
656         keyBinds = defKeyBinds;
657     }
658
659     private void defineDefaultKeyBindings() {
660       defKeyBinds.put("Push to LyX","ctrl L");
661       defKeyBinds.put("Push to WinEdt","ctrl shift W");
662       defKeyBinds.put("Push to Emacs","ctrl shift E");
663         defKeyBinds.put("Quit JabRef", "ctrl Q");
664         defKeyBinds.put("Open database", "ctrl O");
665         defKeyBinds.put("Save database", "ctrl S");
666         defKeyBinds.put("Save database as ...", "ctrl shift S");
667         defKeyBinds.put("Close database", "ctrl W");
668         defKeyBinds.put("New entry", "ctrl N");
669         defKeyBinds.put("Cut", "ctrl X");
670         defKeyBinds.put("Copy", "ctrl C");
671         defKeyBinds.put("Paste", "ctrl V");
672         defKeyBinds.put("Undo", "ctrl Z");
673         defKeyBinds.put("Redo", "ctrl Y");
674         defKeyBinds.put("Help", "F1");
675         defKeyBinds.put("New article", "ctrl shift A");
676         defKeyBinds.put("New book", "ctrl shift B");
677         defKeyBinds.put("New phdthesis", "ctrl shift T");
678         defKeyBinds.put("New inbook", "ctrl shift I");
679         defKeyBinds.put("New mastersthesis", "ctrl shift M");
680         defKeyBinds.put("New proceedings", "ctrl shift P");
681         defKeyBinds.put("New unpublished", "ctrl shift U");
682         defKeyBinds.put("Edit strings", "ctrl T");
683         defKeyBinds.put("Edit preamble", "ctrl P");
684         defKeyBinds.put("Select all", "ctrl A");
685         defKeyBinds.put("Toggle groups interface", "ctrl shift G");
686         defKeyBinds.put("Autogenerate BibTeX keys", "ctrl G");
687         defKeyBinds.put("Search", "ctrl F");
688         defKeyBinds.put("Incremental search", "ctrl shift F");
689         defKeyBinds.put("Repeat incremental search", "ctrl shift F");
690         defKeyBinds.put("Close dialog", "ESCAPE");
691         defKeyBinds.put("Close entry editor", "ESCAPE");
692         defKeyBinds.put("Close preamble editor", "ESCAPE");
693         defKeyBinds.put("Back, help dialog", "LEFT");
694         defKeyBinds.put("Forward, help dialog", "RIGHT");
695         defKeyBinds.put("Preamble editor, store changes", "alt S");
696         defKeyBinds.put("Clear search", "ESCAPE");
697         defKeyBinds.put("Entry editor, next panel", "ctrl TAB");//"ctrl PLUS");//"shift Right");
698         defKeyBinds.put("Entry editor, previous panel", "ctrl shift TAB");//"ctrl MINUS");
699         defKeyBinds.put("Entry editor, next panel 2", "ctrl PLUS");//"ctrl PLUS");//"shift Right");
700         defKeyBinds.put("Entry editor, previous panel 2", "ctrl MINUS");//"ctrl MINUS");
701         defKeyBinds.put("Entry editor, next entry", "ctrl shift DOWN");
702         defKeyBinds.put("Entry editor, previous entry", "ctrl shift UP");
703         defKeyBinds.put("Entry editor, store field", "alt S");
704         defKeyBinds.put("String dialog, add string", "ctrl N");
705         defKeyBinds.put("String dialog, remove string", "shift DELETE");
706         defKeyBinds.put("String dialog, move string up", "ctrl UP");
707         defKeyBinds.put("String dialog, move string down", "ctrl DOWN");
708         defKeyBinds.put("Save session", "F11");
709         defKeyBinds.put("Load session", "F12");
710         defKeyBinds.put("Copy \\cite{BibTeX key}", "ctrl K");
711         defKeyBinds.put("Next tab", "ctrl PAGE_DOWN");
712         defKeyBinds.put("Previous tab", "ctrl PAGE_UP");
713         defKeyBinds.put("Replace string", "ctrl R");
714         defKeyBinds.put("Delete", "DELETE");
715         defKeyBinds.put("Open PDF or PS", "F4");
716         defKeyBinds.put("Open URL or DOI", "F3");
717         defKeyBinds.put("Toggle entry preview", "ctrl F9");
718         defKeyBinds.put("Switch preview layout", "F9");
719         defKeyBinds.put("Edit entry", "ctrl E");
720         defKeyBinds.put("Mark entries", "ctrl M");
721         defKeyBinds.put("Unmark entries", "ctrl shift M");
722         defKeyBinds.put("Fetch Medline", "F5");
723         defKeyBinds.put("Fetch CiteSeer", "F6");
724         defKeyBinds.put("New from plain text", "ctrl shift N");
725         defKeyBinds.put("Import Fields from CiteSeer", "ctrl shift C");
726         defKeyBinds.put("Fetch citations from CiteSeer", "F7");
727         defKeyBinds.put("Synchronize PDF", "shift F4");
728         defKeyBinds.put("Synchronize PS", "ctrl F4");
729         defKeyBinds.put("Abbreviate", "ctrl alt A");
730         defKeyBinds.put("Unabbreviate", "ctrl alt shift A");
731         defKeyBinds.put("Search IEEXplore", "F8");
732         
733         //defKeyBinds.put("Select value", "ctrl B");
734     }
735
736     private String getNextUnit(Reader data) throws IOException {
737         int c;
738         boolean escape = false, done = false;
739         StringBuffer res = new StringBuffer();
740         while (!done && ((c = data.read()) != -1)) {
741             if (c == '\\') {
742                 if (!escape)
743                     escape = true;
744                 else {
745                     escape = false;
746                     res.append('\\');
747                 }
748             } else {
749                 if (c == ';') {
750                     if (!escape)
751                         done = true;
752                     else
753                         res.append(';');
754                 } else {
755                     res.append((char)c);
756                 }
757                 escape = false;
758             }
759         }
760         if (res.length() > 0)
761             return res.toString();
762         else
763             return null;
764     }
765
766     private String makeEscape(String s) {
767         StringBuffer sb = new StringBuffer();
768         int c;
769         for (int i=0; i<s.length(); i++) {
770             c = s.charAt(i);
771             if ((c == '\\') || (c == ';'))
772                 sb.append('\\');
773             sb.append((char)c);
774         }
775         return sb.toString();
776     }
777
778     /**
779      * Stores all information about the entry type in preferences, with
780      * the tag given by number.
781      */
782     public void storeCustomEntryType(CustomEntryType tp, int number) {
783         String nr = ""+number;
784         put(CUSTOM_TYPE_NAME+nr, tp.getName());
785         putStringArray(CUSTOM_TYPE_REQ+nr, tp.getRequiredFields());
786         putStringArray(CUSTOM_TYPE_OPT+nr, tp.getOptionalFields());
787
788     }
789
790     /**
791      * Retrieves all information about the entry type in preferences,
792      * with the tag given by number.
793      */
794     public CustomEntryType getCustomEntryType(int number) {
795         String nr = ""+number;
796         String
797             name = get(CUSTOM_TYPE_NAME+nr);
798         String[]
799             req = getStringArray(CUSTOM_TYPE_REQ+nr),
800             opt = getStringArray(CUSTOM_TYPE_OPT+nr);
801         if (name == null)
802             return null;
803         else return new CustomEntryType
804             (Util.nCase(name), req, opt);
805
806
807     }
808
809     /**
810      * Look up the external file type registered for this extension, if any.
811      * @param extension The file extension.
812      * @return The ExternalFileType registered, or null if none.
813      */
814     public ExternalFileType getExternalFileType(String extension) {
815         return (ExternalFileType)externalFileTypes.get(extension);
816     }
817
818
819     /**
820      * Removes all information about custom entry types with tags of
821      * @param number or higher.
822      */
823     public void purgeCustomEntryTypes(int number) {
824     purgeSeries(CUSTOM_TYPE_NAME, number);
825     purgeSeries(CUSTOM_TYPE_REQ, number);
826     purgeSeries(CUSTOM_TYPE_OPT, number);
827
828         /*while (get(CUSTOM_TYPE_NAME+number) != null) {
829             remove(CUSTOM_TYPE_NAME+number);
830             remove(CUSTOM_TYPE_REQ+number);
831             remove(CUSTOM_TYPE_OPT+number);
832             number++;
833             }*/
834     }
835
836     /**
837      * Removes all entries keyed by prefix+number, where number
838      * is equal to or higher than the given number.
839      * @param number or higher.
840      */
841     public void purgeSeries(String prefix, int number) {
842         while (get(prefix+number) != null) {
843             remove(prefix+number);
844             number++;
845         }
846     }
847
848     public EntryEditorTabList getEntryEditorTabList() {
849     if (tabList == null)
850         updateEntryEditorTabList();
851     return tabList;
852     }
853
854     public void updateEntryEditorTabList() {
855     tabList = new EntryEditorTabList();
856     }
857
858     /**
859      * Exports Preferences to an XML file.
860      *
861      * @param filename String File to export to
862      */
863     public void exportPreferences(String filename) throws IOException {
864       File f = new File(filename);
865       OutputStream os = new FileOutputStream(f);
866       try {
867         prefs.exportSubtree(os);
868       } catch (BackingStoreException ex) {
869         throw new IOException(ex.getMessage());
870       }
871     }
872
873       /**
874        * Imports Preferences from an XML file.
875        *
876        * @param filename String File to import from
877        */
878       public void importPreferences(String filename) throws IOException {
879         File f = new File(filename);
880         InputStream is = new FileInputStream(f);
881         try {
882           Preferences.importPreferences(is);
883         } catch (InvalidPreferencesFormatException ex) {
884           throw new IOException(ex.getMessage());
885         }
886       }
887
888     /**
889      * Determines whether the given field should be written without any sort of wrapping.
890      * @param fieldName The field name.
891      * @return true if the field should not be wrapped.
892      */
893     public boolean isNonWrappableField(String fieldName) {
894
895         return nonWrappableFields.contains(fieldName);
896     }
897 }