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