6e5199ade4a92eac44e84396604389cd826a6320
[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         defaults.put("citeCommand", "cite");
301         defaults.put("floatMarkedEntries", Boolean.TRUE);
302         
303         //defaults.put("tempDir", System.getProperty("java.io.tmpdir"));
304         //Util.pr(System.getProperty("java.io.tempdir"));
305
306         //defaults.put("keyPattern", new LabelPattern(KEY_PATTERN));
307
308         restoreKeyBindings();
309
310         customExports = new CustomExportList(this, new ExportComparator());
311         customImports = new CustomImportList(this);
312
313         //defaults.put("oooWarning", Boolean.TRUE);
314         updateSpecialFieldHandling();
315         WRAPPED_USERNAME = "["+get("defaultOwner")+"]";
316
317         // TODO: remove temporary registering of external file types?
318         externalFileTypes.put("pdf", new ExternalFileType("PDF", "pdf", "acroread", null));
319         externalFileTypes.put("ps", new ExternalFileType("PostScript", "ps", "gs", null));
320         externalFileTypes.put("doc", new ExternalFileType("Word file", "doc", "oowriter", null));
321         externalFileTypes.put("odt", new ExternalFileType("OpenDocument text", "odt", "oowriter", null));
322
323     }
324
325     public boolean putBracesAroundCapitals(String fieldName) {
326         return putBracesAroundCapitalsFields.contains(fieldName);
327     }
328
329     public void updateSpecialFieldHandling() {
330         putBracesAroundCapitalsFields.clear();
331         String fieldString = get("putBracesAroundCapitals");
332         if (fieldString.length() > 0) {
333             String[] fields = fieldString.split(";");
334             for (int i=0; i<fields.length; i++)
335                 putBracesAroundCapitalsFields.add(fields[i]);
336         }
337         nonWrappableFields.clear();
338         fieldString = get("nonWrappableFields");
339         if (fieldString.length() > 0) {
340             String[] fields = fieldString.split(";");
341             for (int i=0; i<fields.length; i++)
342                 nonWrappableFields.add(fields[i]);
343         }
344
345     }
346
347     /**
348      * Check whether a key is set (differently from null).
349      * @param key The key to check.
350      * @return true if the key is set, false otherwise.
351      */
352     public boolean hasKey(String key) {
353         return prefs.get(key, null) != null;
354     }
355
356     public String get(String key) {
357         return prefs.get(key, (String)defaults.get(key));
358     }
359
360     public String get(String key, String def) {
361         return prefs.get(key, def);
362     }
363
364     public boolean getBoolean(String key) {
365         return prefs.getBoolean(key, ((Boolean)defaults.get(key)).booleanValue());
366     }
367
368     public double getDouble(String key) {
369         return prefs.getDouble(key, ((Double)defaults.get(key)).doubleValue());
370     }
371
372     public int getInt(String key) {
373         return prefs.getInt(key, ((Integer)defaults.get(key)).intValue());
374     }
375
376     public byte[] getByteArray(String key) {
377         return prefs.getByteArray(key, (byte[])defaults.get(key));
378     }
379
380     public void put(String key, String value) {
381         prefs.put(key, value);
382     }
383
384     public void putBoolean(String key, boolean value) {
385         prefs.putBoolean(key, value);
386     }
387
388     public void putDouble(String key, double value) {
389         prefs.putDouble(key, value);
390     }
391
392     public void putInt(String key, int value) {
393         prefs.putInt(key, value);
394     }
395
396     public void putByteArray(String key, byte[] value) {
397         prefs.putByteArray(key, value);
398     }
399
400     public void remove(String key) {
401         prefs.remove(key);
402     }
403
404     /**
405      * Puts a string array into the Preferences, by linking its elements
406      * with ';' into a single string. Escape characters make the process
407      * transparent even if strings contain ';'.
408      */
409     public void putStringArray(String key, String[] value) {
410         if (value == null) {
411             remove(key);
412             return;
413         }
414
415         if (value.length > 0) {
416             StringBuffer linked = new StringBuffer();
417             for (int i=0; i<value.length-1; i++) {
418                 linked.append(makeEscape(value[i]));
419                 linked.append(";");
420             }
421             linked.append(makeEscape(value[value.length-1]));
422             put(key, linked.toString());
423         } else {
424             put(key, "");
425         }
426     }
427
428     /**
429      * Returns a String[] containing the chosen columns.
430      */
431     public String[] getStringArray(String key) {
432         String names = get(key);
433         if (names == null)
434             return null;
435         //Util.pr(key+"\n"+names);
436         StringReader rd = new StringReader(names);
437         Vector arr = new Vector();
438         String rs;
439         try {
440             while ((rs = getNextUnit(rd)) != null) {
441                 arr.add(rs);
442             }
443         } catch (IOException ex) {}
444         String[] res = new String[arr.size()];
445         for (int i=0; i<res.length; i++)
446             res[i] = (String)arr.elementAt(i);
447
448         return res;
449     }
450
451     /**
452      * Looks up a color definition in preferences, and returns the Color object.
453      * @param key The key for this setting.
454      * @return The color corresponding to the setting.
455      */
456     public Color getColor(String key) {
457         String value = get(key);
458         int[] rgb = getRgb(value);
459         return new Color(rgb[0], rgb[1], rgb[2]);
460     }
461
462     public Color getDefaultColor(String key) {
463         String value = (String)defaults.get(key);
464         int[] rgb = getRgb(value);
465         return new Color(rgb[0], rgb[1], rgb[2]);
466     }
467
468     /**
469      * Stores a color in preferences.
470      * @param key The key for this setting.
471      * @param color The Color to store.
472      */
473     public void putColor(String key, Color color) {
474         StringBuffer sb = new StringBuffer();
475         sb.append(String.valueOf(color.getRed()));
476         sb.append(':');
477         sb.append(String.valueOf(color.getGreen()));
478         sb.append(':');
479         sb.append(String.valueOf(color.getBlue()));
480         put(key, sb.toString());
481     }
482
483     /**
484      * Looks up a color definition in preferences, and returns an array containing the RGB values.
485      * @param key The key for this setting.
486      * @return The RGB values corresponding to this color setting.
487      */
488     public int[] getRgb(String value) {
489         String[] elements = value.split(":");
490         int[] values = new int[3];
491         values[0] = Integer.parseInt(elements[0]);
492         values[1] = Integer.parseInt(elements[1]);
493         values[2] = Integer.parseInt(elements[2]);
494         return values;
495     }
496
497     /**
498      * Returns the KeyStroke for this binding, as defined by the
499      * defaults, or in the Preferences.
500      */
501     public KeyStroke getKey(String bindName) {
502
503         String s = (String)keyBinds.get(bindName);
504         // If the current key bindings don't contain the one asked for,
505         // we fall back on the default. This should only happen when a
506         // user has his own set in Preferences, and has upgraded to a
507         // new version where new bindings have been introduced.
508         if (s == null) {
509             s = (String)defKeyBinds.get(bindName);
510             // So, if this happens, we add the default value to the current
511             // hashmap, so this doesn't happen again, and so this binding
512             // will appear in the KeyBindingsDialog.
513             keyBinds.put(bindName, s);
514         }
515         if (s == null) {
516           Globals.logger("Could not get key binding for \"" + bindName + "\"");
517           //throw new RuntimeException("");
518         }
519
520         if (Globals.ON_MAC)
521           return getKeyForMac(KeyStroke.getKeyStroke(s));
522         else
523           return KeyStroke.getKeyStroke(s);
524     }
525
526     /**
527      * Returns the KeyStroke for this binding, as defined by the
528      * defaults, or in the Preferences, but adapted for Mac users,
529      * with the Command key preferred instead of Control.
530      */
531     private KeyStroke getKeyForMac(KeyStroke ks) {
532       if (ks == null) return null;
533       int keyCode = ks.getKeyCode();
534       if ((ks.getModifiers() & KeyEvent.CTRL_MASK) == 0) {
535         return ks;
536       }
537       else {
538         if ((ks.getModifiers() & KeyEvent.SHIFT_MASK) != 0) {
539           return KeyStroke.getKeyStroke(keyCode, Globals.SHORTCUT_MASK+KeyEvent.SHIFT_MASK);
540         }
541         return KeyStroke.getKeyStroke(keyCode, Globals.SHORTCUT_MASK);
542       }
543     }
544
545     /**
546      * Returns the HashMap containing all key bindings.
547      */
548     public HashMap getKeyBindings() {
549         return keyBinds;
550     }
551
552     /**
553      * Returns the HashMap containing default key bindings.
554      */
555     public HashMap getDefaultKeys() {
556         return defKeyBinds;
557     }
558
559     public void flush() {
560     try {
561         prefs.flush();
562     } catch (BackingStoreException ex) {
563         ex.printStackTrace();
564     }
565     }
566
567     /**
568      * Stores new key bindings into Preferences, provided they
569      * actually differ from the old ones.
570      */
571     public void setNewKeyBindings(HashMap newBindings) {
572         if (!newBindings.equals(keyBinds)) {
573             // This confirms that the bindings have actually changed.
574             String[] bindNames = new String[newBindings.size()],
575                 bindings = new String[newBindings.size()];
576             int index = 0;
577             for (Iterator i=newBindings.keySet().iterator();
578                  i.hasNext();) {
579                 String nm = (String)i.next();
580                 String bnd = (String)newBindings.get(nm);
581                 bindNames[index] = nm;
582                 bindings[index] = bnd;
583                 index++;
584             }
585             putStringArray("bindNames", bindNames);
586             putStringArray("bindings", bindings);
587             keyBinds = newBindings;
588         }
589     }
590
591
592         public LabelPattern getKeyPattern(){
593
594             keyPattern = new LabelPattern(KEY_PATTERN);
595             Preferences pre = Preferences.userNodeForPackage
596                 (net.sf.jabref.labelPattern.LabelPattern.class);
597             try {
598                 String[] keys = pre.keys();
599             if (keys.length > 0) for (int i=0; i<keys.length; i++)
600                 keyPattern.addLabelPattern(keys[i], pre.get(keys[i], null));
601             } catch (BackingStoreException ex) {
602                 Globals.logger("BackingStoreException in JabRefPreferences.getKeyPattern");
603             }
604
605             ///
606             //keyPattern.addLabelPattern("article", "[author][year]");
607             //putKeyPattern(keyPattern);
608             ///
609
610             return keyPattern;
611         }
612
613         public void putKeyPattern(LabelPattern pattern){
614             keyPattern = pattern;
615             LabelPattern parent = pattern.getParent();
616             if (parent == null)
617                 return;
618
619             // Store overridden definitions to Preferences.
620             Preferences pre = Preferences.userNodeForPackage
621                 (net.sf.jabref.labelPattern.LabelPattern.class);
622             try {
623                 pre.clear(); // We remove all old entries.
624             } catch (BackingStoreException ex) {
625                 Globals.logger("BackingStoreException in JabRefPreferences.putKeyPattern");
626             }
627
628             Iterator i = pattern.keySet().iterator();
629             while (i.hasNext()) {
630                 String s = (String)i.next();
631                 if (!(pattern.get(s)).equals(parent.get(s)))
632                     pre.put(s, pattern.getValue(s).get(0).toString());
633             }
634         }
635
636     private void restoreKeyBindings() {
637         // Define default keybindings.
638         defineDefaultKeyBindings();
639
640         // First read the bindings, and their names.
641         String[] bindNames = getStringArray("bindNames"),
642             bindings = getStringArray("bindings");
643
644         // Then set up the key bindings HashMap.
645         if ((bindNames == null) || (bindings == null)
646             || (bindNames.length != bindings.length)) {
647             // Nothing defined in Preferences, or something is wrong.
648             setDefaultKeyBindings();
649             return;
650         }
651
652         for (int i=0; i<bindNames.length; i++)
653             keyBinds.put(bindNames[i], bindings[i]);
654     }
655
656     private void setDefaultKeyBindings() {
657         keyBinds = defKeyBinds;
658     }
659
660     private void defineDefaultKeyBindings() {
661       defKeyBinds.put("Push to LyX","ctrl L");
662       defKeyBinds.put("Push to WinEdt","ctrl shift W");
663       defKeyBinds.put("Push to Emacs","ctrl shift E");
664         defKeyBinds.put("Quit JabRef", "ctrl Q");
665         defKeyBinds.put("Open database", "ctrl O");
666         defKeyBinds.put("Save database", "ctrl S");
667         defKeyBinds.put("Save database as ...", "ctrl shift S");
668         defKeyBinds.put("Close database", "ctrl W");
669         defKeyBinds.put("New entry", "ctrl N");
670         defKeyBinds.put("Cut", "ctrl X");
671         defKeyBinds.put("Copy", "ctrl C");
672         defKeyBinds.put("Paste", "ctrl V");
673         defKeyBinds.put("Undo", "ctrl Z");
674         defKeyBinds.put("Redo", "ctrl Y");
675         defKeyBinds.put("Help", "F1");
676         defKeyBinds.put("New article", "ctrl shift A");
677         defKeyBinds.put("New book", "ctrl shift B");
678         defKeyBinds.put("New phdthesis", "ctrl shift T");
679         defKeyBinds.put("New inbook", "ctrl shift I");
680         defKeyBinds.put("New mastersthesis", "ctrl shift M");
681         defKeyBinds.put("New proceedings", "ctrl shift P");
682         defKeyBinds.put("New unpublished", "ctrl shift U");
683         defKeyBinds.put("Edit strings", "ctrl T");
684         defKeyBinds.put("Edit preamble", "ctrl P");
685         defKeyBinds.put("Select all", "ctrl A");
686         defKeyBinds.put("Toggle groups interface", "ctrl shift G");
687         defKeyBinds.put("Autogenerate BibTeX keys", "ctrl G");
688         defKeyBinds.put("Search", "ctrl F");
689         defKeyBinds.put("Incremental search", "ctrl shift F");
690         defKeyBinds.put("Repeat incremental search", "ctrl shift F");
691         defKeyBinds.put("Close dialog", "ESCAPE");
692         defKeyBinds.put("Close entry editor", "ESCAPE");
693         defKeyBinds.put("Close preamble editor", "ESCAPE");
694         defKeyBinds.put("Back, help dialog", "LEFT");
695         defKeyBinds.put("Forward, help dialog", "RIGHT");
696         defKeyBinds.put("Preamble editor, store changes", "alt S");
697         defKeyBinds.put("Clear search", "ESCAPE");
698         defKeyBinds.put("Entry editor, next panel", "ctrl TAB");//"ctrl PLUS");//"shift Right");
699         defKeyBinds.put("Entry editor, previous panel", "ctrl shift TAB");//"ctrl MINUS");
700         defKeyBinds.put("Entry editor, next panel 2", "ctrl PLUS");//"ctrl PLUS");//"shift Right");
701         defKeyBinds.put("Entry editor, previous panel 2", "ctrl MINUS");//"ctrl MINUS");
702         defKeyBinds.put("Entry editor, next entry", "ctrl shift DOWN");
703         defKeyBinds.put("Entry editor, previous entry", "ctrl shift UP");
704         defKeyBinds.put("Entry editor, store field", "alt S");
705         defKeyBinds.put("String dialog, add string", "ctrl N");
706         defKeyBinds.put("String dialog, remove string", "shift DELETE");
707         defKeyBinds.put("String dialog, move string up", "ctrl UP");
708         defKeyBinds.put("String dialog, move string down", "ctrl DOWN");
709         defKeyBinds.put("Save session", "F11");
710         defKeyBinds.put("Load session", "F12");
711         defKeyBinds.put("Copy \\cite{BibTeX key}", "ctrl K");
712         defKeyBinds.put("Next tab", "ctrl PAGE_DOWN");
713         defKeyBinds.put("Previous tab", "ctrl PAGE_UP");
714         defKeyBinds.put("Replace string", "ctrl R");
715         defKeyBinds.put("Delete", "DELETE");
716         defKeyBinds.put("Open PDF or PS", "F4");
717         defKeyBinds.put("Open URL or DOI", "F3");
718         defKeyBinds.put("Toggle entry preview", "ctrl F9");
719         defKeyBinds.put("Switch preview layout", "F9");
720         defKeyBinds.put("Edit entry", "ctrl E");
721         defKeyBinds.put("Mark entries", "ctrl M");
722         defKeyBinds.put("Unmark entries", "ctrl shift M");
723         defKeyBinds.put("Fetch Medline", "F5");
724         defKeyBinds.put("Fetch CiteSeer", "F6");
725         defKeyBinds.put("New from plain text", "ctrl shift N");
726         defKeyBinds.put("Import Fields from CiteSeer", "ctrl shift C");
727         defKeyBinds.put("Fetch citations from CiteSeer", "F7");
728         defKeyBinds.put("Synchronize PDF", "shift F4");
729         defKeyBinds.put("Synchronize PS", "ctrl F4");
730         defKeyBinds.put("Abbreviate", "ctrl alt A");
731         defKeyBinds.put("Unabbreviate", "ctrl alt shift A");
732         defKeyBinds.put("Search IEEXplore", "F8");
733         
734         //defKeyBinds.put("Select value", "ctrl B");
735     }
736
737     private String getNextUnit(Reader data) throws IOException {
738         int c;
739         boolean escape = false, done = false;
740         StringBuffer res = new StringBuffer();
741         while (!done && ((c = data.read()) != -1)) {
742             if (c == '\\') {
743                 if (!escape)
744                     escape = true;
745                 else {
746                     escape = false;
747                     res.append('\\');
748                 }
749             } else {
750                 if (c == ';') {
751                     if (!escape)
752                         done = true;
753                     else
754                         res.append(';');
755                 } else {
756                     res.append((char)c);
757                 }
758                 escape = false;
759             }
760         }
761         if (res.length() > 0)
762             return res.toString();
763         else
764             return null;
765     }
766
767     private String makeEscape(String s) {
768         StringBuffer sb = new StringBuffer();
769         int c;
770         for (int i=0; i<s.length(); i++) {
771             c = s.charAt(i);
772             if ((c == '\\') || (c == ';'))
773                 sb.append('\\');
774             sb.append((char)c);
775         }
776         return sb.toString();
777     }
778
779     /**
780      * Stores all information about the entry type in preferences, with
781      * the tag given by number.
782      */
783     public void storeCustomEntryType(CustomEntryType tp, int number) {
784         String nr = ""+number;
785         put(CUSTOM_TYPE_NAME+nr, tp.getName());
786         putStringArray(CUSTOM_TYPE_REQ+nr, tp.getRequiredFields());
787         putStringArray(CUSTOM_TYPE_OPT+nr, tp.getOptionalFields());
788
789     }
790
791     /**
792      * Retrieves all information about the entry type in preferences,
793      * with the tag given by number.
794      */
795     public CustomEntryType getCustomEntryType(int number) {
796         String nr = ""+number;
797         String
798             name = get(CUSTOM_TYPE_NAME+nr);
799         String[]
800             req = getStringArray(CUSTOM_TYPE_REQ+nr),
801             opt = getStringArray(CUSTOM_TYPE_OPT+nr);
802         if (name == null)
803             return null;
804         else return new CustomEntryType
805             (Util.nCase(name), req, opt);
806
807
808     }
809
810     /**
811      * Look up the external file type registered for this extension, if any.
812      * @param extension The file extension.
813      * @return The ExternalFileType registered, or null if none.
814      */
815     public ExternalFileType getExternalFileType(String extension) {
816         return (ExternalFileType)externalFileTypes.get(extension);
817     }
818
819
820     /**
821      * Removes all information about custom entry types with tags of
822      * @param number or higher.
823      */
824     public void purgeCustomEntryTypes(int number) {
825     purgeSeries(CUSTOM_TYPE_NAME, number);
826     purgeSeries(CUSTOM_TYPE_REQ, number);
827     purgeSeries(CUSTOM_TYPE_OPT, number);
828
829         /*while (get(CUSTOM_TYPE_NAME+number) != null) {
830             remove(CUSTOM_TYPE_NAME+number);
831             remove(CUSTOM_TYPE_REQ+number);
832             remove(CUSTOM_TYPE_OPT+number);
833             number++;
834             }*/
835     }
836
837     /**
838      * Removes all entries keyed by prefix+number, where number
839      * is equal to or higher than the given number.
840      * @param number or higher.
841      */
842     public void purgeSeries(String prefix, int number) {
843         while (get(prefix+number) != null) {
844             remove(prefix+number);
845             number++;
846         }
847     }
848
849     public EntryEditorTabList getEntryEditorTabList() {
850     if (tabList == null)
851         updateEntryEditorTabList();
852     return tabList;
853     }
854
855     public void updateEntryEditorTabList() {
856     tabList = new EntryEditorTabList();
857     }
858
859     /**
860      * Exports Preferences to an XML file.
861      *
862      * @param filename String File to export to
863      */
864     public void exportPreferences(String filename) throws IOException {
865       File f = new File(filename);
866       OutputStream os = new FileOutputStream(f);
867       try {
868         prefs.exportSubtree(os);
869       } catch (BackingStoreException ex) {
870         throw new IOException(ex.getMessage());
871       }
872     }
873
874       /**
875        * Imports Preferences from an XML file.
876        *
877        * @param filename String File to import from
878        */
879       public void importPreferences(String filename) throws IOException {
880         File f = new File(filename);
881         InputStream is = new FileInputStream(f);
882         try {
883           Preferences.importPreferences(is);
884         } catch (InvalidPreferencesFormatException ex) {
885           throw new IOException(ex.getMessage());
886         }
887       }
888
889     /**
890      * Determines whether the given field should be written without any sort of wrapping.
891      * @param fieldName The field name.
892      * @return true if the field should not be wrapped.
893      */
894     public boolean isNonWrappableField(String fieldName) {
895
896         return nonWrappableFields.contains(fieldName);
897     }
898 }