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