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