9fc09f4cb3260978b7385b215ce573fa76122abd
[debian/jabref.git] / src / main / java / net / sf / jabref / JabRefPreferences.java
1 /*  Copyright (C) 2003-2012 JabRef contributors.
2  This program is free software; you can redistribute it and/or modify
3  it under the terms of the GNU General Public License as published by
4  the Free Software Foundation; either version 2 of the License, or
5  (at your option) any later version.
6
7  This program is distributed in the hope that it will be useful,
8  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  GNU General Public License for more details.
11
12  You should have received a copy of the GNU General Public License along
13  with this program; if not, write to the Free Software Foundation, Inc.,
14  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
15  */
16 package net.sf.jabref;
17
18 import java.awt.Color;
19 import java.awt.event.KeyEvent;
20 import java.io.File;
21 import java.io.FileInputStream;
22 import java.io.FileOutputStream;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.OutputStream;
26 import java.io.Reader;
27 import java.io.StringReader;
28 import java.util.*;
29 import java.util.prefs.BackingStoreException;
30 import java.util.prefs.InvalidPreferencesFormatException;
31 import java.util.prefs.Preferences;
32 import java.net.InetAddress;
33 import java.net.UnknownHostException;
34
35 import javax.swing.JTable;
36 import javax.swing.KeyStroke;
37
38 import net.sf.jabref.export.CustomExportList;
39 import net.sf.jabref.export.ExportComparator;
40 import net.sf.jabref.external.DroppedFileHandler;
41 import net.sf.jabref.external.ExternalFileType;
42 import net.sf.jabref.external.UnknownExternalFileType;
43 import net.sf.jabref.gui.CleanUpAction;
44 import net.sf.jabref.gui.PersistenceTableColumnListener;
45 import net.sf.jabref.imports.CustomImportList;
46 import net.sf.jabref.labelPattern.LabelPattern;
47 import net.sf.jabref.specialfields.SpecialFieldsUtils;
48
49 public class JabRefPreferences {
50
51     public final static String CUSTOM_TYPE_NAME = "customTypeName_",
52             CUSTOM_TYPE_REQ = "customTypeReq_",
53             CUSTOM_TYPE_OPT = "customTypeOpt_",
54             CUSTOM_TYPE_PRIOPT = "customTypePriOpt_",
55             CUSTOM_TAB_NAME = "customTabName_",
56             CUSTOM_TAB_FIELDS = "customTabFields_",
57             EMACS_PATH = "emacsPath",
58             EMACS_ADDITIONAL_PARAMETERS = "emacsParameters",
59             EMACS_23 = "emacsUseV23InsertString",
60             EDIT_GROUP_MEMBERSHIP_MODE = "groupEditGroupMembershipMode",
61             PDF_PREVIEW = "pdfPreview",
62             SHOWONELETTERHEADINGFORICONCOLUMNS = "showOneLetterHeadingForIconColumns",
63             EDITOR_EMACS_KEYBINDINGS = "editorEMACSkeyBindings",
64             EDITOR_EMACS_KEYBINDINGS_REBIND_CA = "editorEMACSkeyBindingsRebindCA",
65             SHORTEST_TO_COMPLETE = "shortestToComplete",
66             AUTOCOMPLETE_FIRSTNAME_MODE = "autoCompFirstNameMode",
67             // here are the possible values for _MODE:
68             AUTOCOMPLETE_FIRSTNAME_MODE_BOTH = "both",
69             AUTOCOMPLETE_FIRSTNAME_MODE_ONLY_FULL = "fullOnly",
70             AUTOCOMPLETE_FIRSTNAME_MODE_ONLY_ABBR = "abbrOnly",
71             WRITEFIELD_ADDSPACES = "writeFieldAddSpaces",
72             WRITEFIELD_CAMELCASENAME = "writeFieldCamelCase",
73             UPDATE_TIMESTAMP = "updateTimestamp",
74             PRIMARY_SORT_FIELD = "priSort",
75             PRIMARY_SORT_DESCENDING = "priDescending",
76             SECONDARY_SORT_FIELD = "secSort",
77             SECONDARY_SORT_DESCENDING = "secDescending",
78             TERTIARY_SORT_FIELD = "terSort",
79             TERTIARY_SORT_DESCENDING = "terDescending",
80             SAVE_IN_ORIGINAL_ORDER = "saveInOriginalOrder",
81             SAVE_IN_SPECIFIED_ORDER = "saveInSpecifiedOrder",
82             SAVE_PRIMARY_SORT_FIELD = "savePriSort",
83             SAVE_PRIMARY_SORT_DESCENDING = "savePriDescending",
84             SAVE_SECONDARY_SORT_FIELD = "saveSecSort",
85             SAVE_SECONDARY_SORT_DESCENDING = "saveSecDescending",
86             SAVE_TERTIARY_SORT_FIELD = "saveTerSort",
87             SAVE_TERTIARY_SORT_DESCENDING = "saveTerDescending",
88             EXPORT_IN_ORIGINAL_ORDER = "exportInOriginalOrder",
89             EXPORT_IN_SPECIFIED_ORDER = "exportInSpecifiedOrder",
90             EXPORT_PRIMARY_SORT_FIELD = "exportPriSort",
91             EXPORT_PRIMARY_SORT_DESCENDING = "exportPriDescending",
92             EXPORT_SECONDARY_SORT_FIELD = "exportSecSort",
93             EXPORT_SECONDARY_SORT_DESCENDING = "exportSecDescending",
94             EXPORT_TERTIARY_SORT_FIELD = "exportTerSort",
95             EXPORT_TERTIARY_SORT_DESCENDING = "exportTerDescending",
96             WRITEFIELD_SORTSTYLE = "writefieldSortStyle",
97             WRITEFIELD_USERDEFINEDORDER = "writefieldUserdefinedOrder",
98             WRITEFIELD_WRAPFIELD="wrapFieldLine";
99     
100
101     // This String is used in the encoded list in prefs of external file type
102     // modifications, in order to indicate a removed default file type:
103     public static final String FILE_TYPE_REMOVED_FLAG = "REMOVED";
104
105     private static final char[][] VALUE_DELIMITERS
106             = new char[][]{{'"', '"'}, {'{', '}'}};
107     public static final String XMP_PRIVACY_FILTERS = "xmpPrivacyFilters";
108     public static final String USE_XMP_PRIVACY_FILTER = "useXmpPrivacyFilter";
109
110     public static final String NEWLINE = "newline";
111
112     public String WRAPPED_USERNAME, MARKING_WITH_NUMBER_PATTERN;
113
114     Preferences prefs;
115     public HashMap<String, Object> defaults = new HashMap<String, Object>();
116     public HashMap<String, String> keyBinds = new HashMap<String, String>(),
117             defKeyBinds = new HashMap<String, String>();
118     private HashSet<String> putBracesAroundCapitalsFields = new HashSet<String>(4);
119     private HashSet<String> nonWrappableFields = new HashSet<String>(5);
120     private static LabelPattern keyPattern;
121
122     // Object containing custom export formats:
123     public CustomExportList customExports;
124
125     /**
126      * Set with all custom {@link net.sf.jabref.imports.ImportFormat}s
127      */
128     public CustomImportList customImports;
129
130     // Object containing info about customized entry editor tabs.
131     private EntryEditorTabList tabList = null;
132     // Map containing all registered external file types:
133     private TreeSet<ExternalFileType> externalFileTypes = new TreeSet<ExternalFileType>();
134
135     public final ExternalFileType HTML_FALLBACK_TYPE
136             = new ExternalFileType("URL", "html", "text/html", "", "www");
137
138     // The following field is used as a global variable during the export of a database.
139     // By setting this field to the path of the database's default file directory, formatters
140     // that should resolve external file paths can access this field. This is an ugly hack
141     // to solve the problem of formatters not having access to any context except for the
142     // string to be formatted and possible formatter arguments.
143     public String[] fileDirForDatabase = null;
144
145     // Similarly to the previous variable, this is a global that can be used during
146     // the export of a database if the database filename should be output. If a database
147     // is tied to a file on disk, this variable is set to that file before export starts:
148     public File databaseFile = null;
149
150     // The following field is used as a global variable during the export of a database.
151     // It is used to hold custom name formatters defined by a custom export filter.
152     // It is set before the export starts:
153     public HashMap<String, String> customExportNameFormatters = null;
154
155     // The only instance of this class:
156     private static JabRefPreferences singleton = null;
157
158     public static JabRefPreferences getInstance() {
159         if (singleton == null) {
160             singleton = new JabRefPreferences();
161         }
162         return singleton;
163     }
164
165     // Upgrade the preferences for the current version
166     // The old preference is kept in case an old version of JabRef is used with 
167     // these preferences, but it is only used when the new preference does not 
168     // exist
169     private void upgradeOldPreferences() {
170         if (prefs.get(SAVE_IN_SPECIFIED_ORDER, null) == null) {
171             if (prefs.getBoolean("saveInStandardOrder", false)) {
172                 putBoolean(SAVE_IN_SPECIFIED_ORDER, true);
173                 put(SAVE_PRIMARY_SORT_FIELD, "author");
174                 put(SAVE_SECONDARY_SORT_FIELD, "editor");
175                 put(SAVE_TERTIARY_SORT_FIELD, "year");
176                 putBoolean(SAVE_PRIMARY_SORT_DESCENDING, false);
177                 putBoolean(SAVE_SECONDARY_SORT_DESCENDING, false);
178                 putBoolean(SAVE_TERTIARY_SORT_DESCENDING, false);
179             } else if (prefs.getBoolean("saveInTitleOrder", false)) {
180                 // saveInTitleOrder => title, author, editor
181                 putBoolean(SAVE_IN_SPECIFIED_ORDER, true);
182                 put(SAVE_PRIMARY_SORT_FIELD, "title");
183                 put(SAVE_SECONDARY_SORT_FIELD, "author");
184                 put(SAVE_TERTIARY_SORT_FIELD, "editor");
185                 putBoolean(SAVE_PRIMARY_SORT_DESCENDING, false);
186                 putBoolean(SAVE_SECONDARY_SORT_DESCENDING, false);
187                 putBoolean(SAVE_TERTIARY_SORT_DESCENDING, false);
188             }
189         }
190         
191         if (prefs.get(EXPORT_IN_SPECIFIED_ORDER, null) == null) {
192             if (prefs.getBoolean("exportInStandardOrder", false)) {
193                 putBoolean(EXPORT_IN_SPECIFIED_ORDER, true);
194                 put(EXPORT_PRIMARY_SORT_FIELD, "author");
195                 put(EXPORT_SECONDARY_SORT_FIELD, "editor");
196                 put(EXPORT_TERTIARY_SORT_FIELD, "year");
197                 putBoolean(EXPORT_PRIMARY_SORT_DESCENDING, false);
198                 putBoolean(EXPORT_SECONDARY_SORT_DESCENDING, false);
199                 putBoolean(EXPORT_TERTIARY_SORT_DESCENDING, false);
200             } else if (prefs.getBoolean("exportInTitleOrder", false)) {
201                 // exportInTitleOrder => title, author, editor
202                 putBoolean(EXPORT_IN_SPECIFIED_ORDER, true);
203                 put(EXPORT_PRIMARY_SORT_FIELD, "title");
204                 put(EXPORT_SECONDARY_SORT_FIELD, "author");
205                 put(EXPORT_TERTIARY_SORT_FIELD, "editor");
206                 putBoolean(EXPORT_PRIMARY_SORT_DESCENDING, false);
207                 putBoolean(EXPORT_SECONDARY_SORT_DESCENDING, false);
208                 putBoolean(EXPORT_TERTIARY_SORT_DESCENDING, false);
209             }
210         }
211     }
212
213     // The constructor is made private to enforce this as a singleton class:
214     private JabRefPreferences() {
215
216         try {
217             if (new File("jabref.xml").exists()) {
218                 importPreferences("jabref.xml");
219             }
220         } catch (IOException e) {
221             Globals.logger("Could not import preferences from jabref.xml:" + e.getLocalizedMessage());
222         }
223
224         // load user preferences 
225         prefs = Preferences.userNodeForPackage(JabRef.class);
226         upgradeOldPreferences();
227
228         if (Globals.osName.equals(Globals.MAC)) {
229             //defaults.put("pdfviewer", "/Applications/Preview.app");
230             //defaults.put("psviewer", "/Applications/Preview.app");
231             //defaults.put("htmlviewer", "/Applications/Safari.app");
232             defaults.put(EMACS_PATH, "emacsclient");
233             defaults.put(EMACS_23, true);
234             defaults.put(EMACS_ADDITIONAL_PARAMETERS, "-n -e");
235             defaults.put("fontFamily", "SansSerif");
236
237         } else if (Globals.osName.toLowerCase().startsWith("windows")) {
238             //defaults.put("pdfviewer", "cmd.exe /c start /b");
239             //defaults.put("psviewer", "cmd.exe /c start /b");
240             //defaults.put("htmlviewer", "cmd.exe /c start /b");
241             defaults.put("lookAndFeel", "com.jgoodies.looks.windows.WindowsLookAndFeel");
242             defaults.put("winEdtPath", "C:\\Program Files\\WinEdt Team\\WinEdt\\WinEdt.exe");
243             defaults.put("latexEditorPath", "C:\\Program Files\\LEd\\LEd.exe");
244             defaults.put(EMACS_PATH, "emacsclient.exe");
245             defaults.put(EMACS_23, true);
246             defaults.put(EMACS_ADDITIONAL_PARAMETERS, "-n -e");
247             defaults.put("fontFamily", "Arial");
248
249         } else {
250             //defaults.put("pdfviewer", "evince");
251             //defaults.put("psviewer", "gv");
252             //defaults.put("htmlviewer", "firefox");
253             defaults.put("lookAndFeel", "com.jgoodies.plaf.plastic.Plastic3DLookAndFeel");
254             defaults.put("fontFamily", "SansSerif");
255
256             // linux
257             defaults.put(EMACS_PATH, "gnuclient");
258             defaults.put(EMACS_23, false);
259             defaults.put(EMACS_ADDITIONAL_PARAMETERS, "-batch -eval");
260         }
261         defaults.put("useProxy", Boolean.FALSE);
262         defaults.put("proxyHostname", "my proxy host");
263         defaults.put("proxyPort", "my proxy port");
264         defaults.put(PDF_PREVIEW, Boolean.FALSE);
265         defaults.put("useDefaultLookAndFeel", Boolean.TRUE);
266         defaults.put("lyxpipe", System.getProperty("user.home") + File.separator + ".lyx/lyxpipe");
267         defaults.put("vim", "vim");
268         defaults.put("vimServer", "vim");
269         defaults.put("posX", 0);
270         defaults.put("posY", 0);
271         defaults.put("sizeX", 840);
272         defaults.put("sizeY", 680);
273         defaults.put("windowMaximised", Boolean.FALSE);
274         defaults.put("autoResizeMode", JTable.AUTO_RESIZE_ALL_COLUMNS);
275         defaults.put("previewPanelHeight", 200);
276         defaults.put("entryEditorHeight", 400);
277         defaults.put("tableColorCodesOn", Boolean.TRUE);
278         defaults.put("namesAsIs", Boolean.FALSE); // "Show names unchanged"
279         defaults.put("namesFf", Boolean.FALSE); // "Show 'Firstname Lastname'"
280         defaults.put("namesLf", Boolean.FALSE); // "Show 'Lastname, Firstname'"
281         defaults.put("namesNatbib", Boolean.TRUE);  // "Natbib style"
282         defaults.put("abbrAuthorNames", Boolean.TRUE); // "Abbreviate names"
283         defaults.put("namesLastOnly", Boolean.TRUE); // "Show last names only"
284         defaults.put("language", "en");
285         defaults.put("showShort", Boolean.TRUE);
286
287         // Sorting preferences
288         defaults.put(PRIMARY_SORT_FIELD, "author");
289         defaults.put(PRIMARY_SORT_DESCENDING, Boolean.FALSE);
290         defaults.put(SECONDARY_SORT_FIELD, "year");
291         defaults.put(SECONDARY_SORT_DESCENDING, Boolean.TRUE);
292         defaults.put(TERTIARY_SORT_FIELD, "author");
293         defaults.put(TERTIARY_SORT_DESCENDING, Boolean.FALSE);
294         defaults.put(SAVE_IN_ORIGINAL_ORDER, Boolean.FALSE);
295         defaults.put(SAVE_IN_SPECIFIED_ORDER, Boolean.FALSE);
296         defaults.put(SAVE_PRIMARY_SORT_FIELD, "bibtexkey");
297         defaults.put(SAVE_PRIMARY_SORT_DESCENDING, Boolean.FALSE);
298         defaults.put(SAVE_SECONDARY_SORT_FIELD, "author");
299         defaults.put(SAVE_SECONDARY_SORT_DESCENDING, Boolean.TRUE);
300         defaults.put(SAVE_TERTIARY_SORT_FIELD, "");
301         defaults.put(SAVE_TERTIARY_SORT_DESCENDING, Boolean.TRUE);
302         defaults.put(EXPORT_IN_ORIGINAL_ORDER, Boolean.FALSE);
303         defaults.put(EXPORT_IN_SPECIFIED_ORDER, Boolean.FALSE);
304         defaults.put(EXPORT_PRIMARY_SORT_FIELD, "bibtexkey");
305         defaults.put(EXPORT_PRIMARY_SORT_DESCENDING, Boolean.FALSE);
306         defaults.put(EXPORT_SECONDARY_SORT_FIELD, "author");
307         defaults.put(EXPORT_SECONDARY_SORT_DESCENDING, Boolean.TRUE);
308         defaults.put(EXPORT_TERTIARY_SORT_FIELD, "");
309         defaults.put(EXPORT_TERTIARY_SORT_DESCENDING, Boolean.TRUE);
310
311         defaults.put(NEWLINE, System.getProperty("line.separator"));
312         
313         defaults.put("columnNames", "entrytype;author;title;year;journal;owner;timestamp;bibtexkey");
314         defaults.put("columnWidths", "75;280;400;60;100;100;100;100");
315         defaults.put(PersistenceTableColumnListener.ACTIVATE_PREF_KEY,
316                 PersistenceTableColumnListener.DEFAULT_ENABLED);
317         defaults.put(XMP_PRIVACY_FILTERS, "pdf;timestamp;keywords;owner;note;review");
318         defaults.put(USE_XMP_PRIVACY_FILTER, Boolean.FALSE);
319         defaults.put("numberColWidth", GUIGlobals.NUMBER_COL_LENGTH);
320         defaults.put("workingDirectory", System.getProperty("user.home"));
321         defaults.put("exportWorkingDirectory", System.getProperty("user.home"));
322         defaults.put("importWorkingDirectory", System.getProperty("user.home"));
323         defaults.put("fileWorkingDirectory", System.getProperty("user.home"));
324         defaults.put("autoOpenForm", Boolean.TRUE);
325         defaults.put("entryTypeFormHeightFactor", 1);
326         defaults.put("entryTypeFormWidth", 1);
327         defaults.put("backup", Boolean.TRUE);
328         defaults.put("openLastEdited", Boolean.TRUE);
329         defaults.put("lastEdited", null);
330         defaults.put("stringsPosX", 0);
331         defaults.put("stringsPosY", 0);
332         defaults.put("stringsSizeX", 600);
333         defaults.put("stringsSizeY", 400);
334         defaults.put("defaultShowSource", Boolean.FALSE);
335         defaults.put("showSource", Boolean.TRUE);
336         defaults.put("defaultAutoSort", Boolean.FALSE);
337         defaults.put("caseSensitiveSearch", Boolean.FALSE);
338         defaults.put("searchReq", Boolean.TRUE);
339         defaults.put("searchOpt", Boolean.TRUE);
340         defaults.put("searchGen", Boolean.TRUE);
341         defaults.put("searchAll", Boolean.FALSE);
342         defaults.put("incrementS", Boolean.FALSE);
343         defaults.put("searchAutoComplete", Boolean.TRUE);
344
345         defaults.put("selectS", Boolean.FALSE);
346         defaults.put("regExpSearch", Boolean.TRUE);
347         defaults.put("highLightWords", Boolean.TRUE);
348         defaults.put("searchPanePosX", 0);
349         defaults.put("searchPanePosY", 0);
350         defaults.put(EDITOR_EMACS_KEYBINDINGS, Boolean.FALSE);
351         defaults.put(EDITOR_EMACS_KEYBINDINGS_REBIND_CA, Boolean.TRUE);
352         defaults.put("autoComplete", Boolean.TRUE);
353         defaults.put("autoCompleteFields", "author;editor;title;journal;publisher;keywords;crossref");
354         defaults.put("autoCompFF", Boolean.FALSE); // "Autocomplete names in 'Firstname Lastname' format only"
355         defaults.put("autoCompLF", Boolean.FALSE); // "Autocomplete names in 'Lastname, Firstname' format only"
356         defaults.put(SHORTEST_TO_COMPLETE, 2);
357         defaults.put(AUTOCOMPLETE_FIRSTNAME_MODE, AUTOCOMPLETE_FIRSTNAME_MODE_BOTH);
358         defaults.put("groupSelectorVisible", Boolean.TRUE);
359         defaults.put("groupFloatSelections", Boolean.TRUE);
360         defaults.put("groupIntersectSelections", Boolean.TRUE);
361         defaults.put("groupInvertSelections", Boolean.FALSE);
362         defaults.put("groupShowOverlapping", Boolean.FALSE);
363         defaults.put("groupSelectMatches", Boolean.FALSE);
364         defaults.put("groupsDefaultField", "keywords");
365         defaults.put("groupShowIcons", Boolean.TRUE);
366         defaults.put("groupShowDynamic", Boolean.TRUE);
367         defaults.put("groupExpandTree", Boolean.TRUE);
368         defaults.put("groupAutoShow", Boolean.TRUE);
369         defaults.put("groupAutoHide", Boolean.TRUE);
370         defaults.put(GROUP_SHOW_NUMBER_OF_ELEMENTS, Boolean.FALSE);
371         defaults.put("autoAssignGroup", Boolean.TRUE);
372         defaults.put("groupKeywordSeparator", ", ");
373         defaults.put(EDIT_GROUP_MEMBERSHIP_MODE, Boolean.FALSE);
374         defaults.put("highlightGroupsMatchingAny", Boolean.FALSE);
375         defaults.put("highlightGroupsMatchingAll", Boolean.FALSE);
376         defaults.put("toolbarVisible", Boolean.TRUE);
377         defaults.put("searchPanelVisible", Boolean.FALSE);
378         defaults.put("defaultEncoding", System.getProperty("file.encoding"));
379         defaults.put("groupsVisibleRows", 8);
380         defaults.put("defaultOwner", System.getProperty("user.name"));
381         defaults.put("preserveFieldFormatting", Boolean.FALSE);
382         defaults.put("memoryStickMode", Boolean.FALSE);
383         defaults.put("renameOnMoveFileToFileDir", Boolean.TRUE);
384
385         // The general fields stuff is made obsolete by the CUSTOM_TAB_... entries.
386         defaults.put("generalFields", "crossref;keywords;file;doi;url;urldate;"
387                 + "pdf;comment;owner");
388
389         defaults.put("useCustomIconTheme", Boolean.FALSE);
390         defaults.put("customIconThemeFile", "/home/alver/div/crystaltheme_16/Icons.properties");
391
392         //defaults.put("recentFiles", "/home/alver/Documents/bibk_dok/hovedbase.bib");
393         defaults.put("historySize", 8);
394         defaults.put("fontStyle", java.awt.Font.PLAIN);
395         defaults.put("fontSize", 12);
396         defaults.put("overrideDefaultFonts", Boolean.FALSE);
397         defaults.put("menuFontFamily", "Times");
398         defaults.put("menuFontStyle", java.awt.Font.PLAIN);
399         defaults.put("menuFontSize", 11);
400         defaults.put("tableRowPadding", GUIGlobals.TABLE_ROW_PADDING);
401         defaults.put("tableShowGrid", Boolean.FALSE);
402         // Main table color settings:
403         defaults.put("tableBackground", "255:255:255");
404         defaults.put("tableReqFieldBackground", "230:235:255");
405         defaults.put("tableOptFieldBackground", "230:255:230");
406         defaults.put("tableText", "0:0:0");
407         defaults.put("gridColor", "210:210:210");
408         defaults.put("grayedOutBackground", "210:210:210");
409         defaults.put("grayedOutText", "40:40:40");
410         defaults.put("veryGrayedOutBackground", "180:180:180");
411         defaults.put("veryGrayedOutText", "40:40:40");
412         defaults.put("markedEntryBackground0", "255:255:180");
413         defaults.put("markedEntryBackground1", "255:220:180");
414         defaults.put("markedEntryBackground2", "255:180:160");
415         defaults.put("markedEntryBackground3", "255:120:120");
416         defaults.put("markedEntryBackground4", "255:75:75");
417         defaults.put("markedEntryBackground5", "220:255:220");
418         defaults.put("validFieldBackgroundColor", "255:255:255");
419         defaults.put("invalidFieldBackgroundColor", "255:0:0");
420         defaults.put("activeFieldEditorBackgroundColor", "220:220:255");
421         defaults.put("fieldEditorTextColor", "0:0:0");
422
423         defaults.put("incompleteEntryBackground", "250:175:175");
424
425         defaults.put("antialias", Boolean.FALSE);
426         defaults.put("ctrlClick", Boolean.FALSE);
427         defaults.put("disableOnMultipleSelection", Boolean.FALSE);
428         defaults.put("pdfColumn", Boolean.FALSE);
429         defaults.put("urlColumn", Boolean.TRUE);
430         defaults.put("preferUrlDoi", Boolean.FALSE);
431         defaults.put("fileColumn", Boolean.TRUE);
432         defaults.put("arxivColumn", Boolean.FALSE);
433
434         defaults.put("extraFileColumns", Boolean.FALSE);
435         defaults.put("listOfFileColumns","");
436
437         defaults.put(SpecialFieldsUtils.PREF_SPECIALFIELDSENABLED, SpecialFieldsUtils.PREF_SPECIALFIELDSENABLED_DEFAULT);
438         defaults.put(SpecialFieldsUtils.PREF_SHOWCOLUMN_PRIORITY, SpecialFieldsUtils.PREF_SHOWCOLUMN_PRIORITY_DEFAULT);
439         defaults.put(SpecialFieldsUtils.PREF_SHOWCOLUMN_QUALITY, SpecialFieldsUtils.PREF_SHOWCOLUMN_QUALITY_DEFAULT);
440         defaults.put(SpecialFieldsUtils.PREF_SHOWCOLUMN_RANKING, SpecialFieldsUtils.PREF_SHOWCOLUMN_RANKING_DEFAULT);
441         defaults.put(SpecialFieldsUtils.PREF_RANKING_COMPACT, SpecialFieldsUtils.PREF_RANKING_COMPACT_DEFAULT);
442         defaults.put(SpecialFieldsUtils.PREF_SHOWCOLUMN_RELEVANCE, SpecialFieldsUtils.PREF_SHOWCOLUMN_RELEVANCE_DEFAULT);
443         defaults.put(SpecialFieldsUtils.PREF_SHOWCOLUMN_PRINTED, SpecialFieldsUtils.PREF_SHOWCOLUMN_PRINTED_DEFAULT);
444         defaults.put(SpecialFieldsUtils.PREF_SHOWCOLUMN_READ, SpecialFieldsUtils.PREF_SHOWCOLUMN_READ_DEFAULT);
445         defaults.put(SpecialFieldsUtils.PREF_AUTOSYNCSPECIALFIELDSTOKEYWORDS, SpecialFieldsUtils.PREF_AUTOSYNCSPECIALFIELDSTOKEYWORDS_DEFAULT);
446         defaults.put(SpecialFieldsUtils.PREF_SERIALIZESPECIALFIELDS, SpecialFieldsUtils.PREF_SERIALIZESPECIALFIELDS_DEFAULT);
447
448         defaults.put(SHOWONELETTERHEADINGFORICONCOLUMNS, Boolean.FALSE);
449
450         defaults.put("useOwner", Boolean.FALSE);
451         defaults.put("overwriteOwner", Boolean.FALSE);
452         defaults.put("allowTableEditing", Boolean.FALSE);
453         defaults.put("dialogWarningForDuplicateKey", Boolean.TRUE);
454         defaults.put("dialogWarningForEmptyKey", Boolean.TRUE);
455         defaults.put("displayKeyWarningDialogAtStartup", Boolean.TRUE);
456         defaults.put("avoidOverwritingKey", Boolean.FALSE);
457         defaults.put("warnBeforeOverwritingKey", Boolean.TRUE);
458         defaults.put("confirmDelete", Boolean.TRUE);
459         defaults.put("grayOutNonHits", Boolean.TRUE);
460         defaults.put("floatSearch", Boolean.TRUE);
461         defaults.put("showSearchInDialog", Boolean.FALSE);
462         defaults.put("searchAllBases", Boolean.FALSE);
463         defaults.put("defaultLabelPattern", "[auth][year]");
464         defaults.put("previewEnabled", Boolean.TRUE);
465         defaults.put("activePreview", 0);
466         defaults.put("preview0", "<font face=\"arial\">"
467                 + "<b><i>\\bibtextype</i><a name=\"\\bibtexkey\">\\begin{bibtexkey} (\\bibtexkey)</a>"
468                 + "\\end{bibtexkey}</b><br>__NEWLINE__"
469                 + "\\begin{author} \\format[Authors(LastFirst,Initials,Semicolon,Amp),HTMLChars]{\\author}<BR>\\end{author}__NEWLINE__"
470                 + "\\begin{editor} \\format[Authors(LastFirst,Initials,Semicolon,Amp),HTMLChars]{\\editor} "
471                 + "<i>(\\format[IfPlural(Eds.,Ed.)]{\\editor})</i><BR>\\end{editor}__NEWLINE__"
472                 + "\\begin{title} \\format[HTMLChars]{\\title} \\end{title}<BR>__NEWLINE__"
473                 + "\\begin{chapter} \\format[HTMLChars]{\\chapter}<BR>\\end{chapter}__NEWLINE__"
474                 + "\\begin{journal} <em>\\format[HTMLChars]{\\journal}, </em>\\end{journal}__NEWLINE__"
475                 // Include the booktitle field for @inproceedings, @proceedings, etc.
476                 + "\\begin{booktitle} <em>\\format[HTMLChars]{\\booktitle}, </em>\\end{booktitle}__NEWLINE__"
477                 + "\\begin{school} <em>\\format[HTMLChars]{\\school}, </em>\\end{school}__NEWLINE__"
478                 + "\\begin{institution} <em>\\format[HTMLChars]{\\institution}, </em>\\end{institution}__NEWLINE__"
479                 + "\\begin{publisher} <em>\\format[HTMLChars]{\\publisher}, </em>\\end{publisher}__NEWLINE__"
480                 + "\\begin{year}<b>\\year</b>\\end{year}\\begin{volume}<i>, \\volume</i>\\end{volume}"
481                 + "\\begin{pages}, \\format[FormatPagesForHTML]{\\pages} \\end{pages}__NEWLINE__"
482                 + "\\begin{abstract}<BR><BR><b>Abstract: </b> \\format[HTMLChars]{\\abstract} \\end{abstract}__NEWLINE__"
483                 + "\\begin{review}<BR><BR><b>Review: </b> \\format[HTMLChars]{\\review} \\end{review}"
484                 + "</dd>__NEWLINE__<p></p></font>");
485         defaults.put("preview1", "<font face=\"arial\">"
486                 + "<b><i>\\bibtextype</i><a name=\"\\bibtexkey\">\\begin{bibtexkey} (\\bibtexkey)</a>"
487                 + "\\end{bibtexkey}</b><br>__NEWLINE__"
488                 + "\\begin{author} \\format[Authors(LastFirst,Initials,Semicolon,Amp),HTMLChars]{\\author}<BR>\\end{author}__NEWLINE__"
489                 + "\\begin{editor} \\format[Authors(LastFirst,Initials,Semicolon,Amp),HTMLChars]{\\editor} "
490                 + "<i>(\\format[IfPlural(Eds.,Ed.)]{\\editor})</i><BR>\\end{editor}__NEWLINE__"
491                 + "\\begin{title} \\format[HTMLChars]{\\title} \\end{title}<BR>__NEWLINE__"
492                 + "\\begin{chapter} \\format[HTMLChars]{\\chapter}<BR>\\end{chapter}__NEWLINE__"
493                 + "\\begin{journal} <em>\\format[HTMLChars]{\\journal}, </em>\\end{journal}__NEWLINE__"
494                 // Include the booktitle field for @inproceedings, @proceedings, etc.
495                 + "\\begin{booktitle} <em>\\format[HTMLChars]{\\booktitle}, </em>\\end{booktitle}__NEWLINE__"
496                 + "\\begin{school} <em>\\format[HTMLChars]{\\school}, </em>\\end{school}__NEWLINE__"
497                 + "\\begin{institution} <em>\\format[HTMLChars]{\\institution}, </em>\\end{institution}__NEWLINE__"
498                 + "\\begin{publisher} <em>\\format[HTMLChars]{\\publisher}, </em>\\end{publisher}__NEWLINE__"
499                 + "\\begin{year}<b>\\year</b>\\end{year}\\begin{volume}<i>, \\volume</i>\\end{volume}"
500                 + "\\begin{pages}, \\format[FormatPagesForHTML]{\\pages} \\end{pages}"
501                 + "</dd>__NEWLINE__<p></p></font>");
502
503         // TODO: Currently not possible to edit this setting:
504         defaults.put("previewPrintButton", Boolean.FALSE);
505         defaults.put("autoDoubleBraces", Boolean.FALSE);
506         defaults.put("doNotResolveStringsFor", "url");
507         defaults.put("resolveStringsAllFields", Boolean.FALSE);
508         defaults.put("putBracesAroundCapitals", "");//"title;journal;booktitle;review;abstract");
509         defaults.put("nonWrappableFields", "pdf;ps;url;doi;file");
510         defaults.put("useImportInspectionDialog", Boolean.TRUE);
511         defaults.put("useImportInspectionDialogForSingle", Boolean.TRUE);
512         defaults.put("generateKeysAfterInspection", Boolean.TRUE);
513         defaults.put("markImportedEntries", Boolean.TRUE);
514         defaults.put("unmarkAllEntriesBeforeImporting", Boolean.TRUE);
515         defaults.put("warnAboutDuplicatesInInspection", Boolean.TRUE);
516         defaults.put("useTimeStamp", Boolean.FALSE);
517         defaults.put("overwriteTimeStamp", Boolean.FALSE);
518         defaults.put("timeStampFormat", "yyyy.MM.dd");
519 //        defaults.put("timeStampField", "timestamp");
520         defaults.put("timeStampField", BibtexFields.TIMESTAMP);
521         defaults.put(UPDATE_TIMESTAMP, Boolean.FALSE);
522         defaults.put("generateKeysBeforeSaving", Boolean.FALSE);
523
524         // behavior of JabRef before 2.10: both: false
525         defaults.put(WRITEFIELD_ADDSPACES, Boolean.TRUE);
526         defaults.put(WRITEFIELD_CAMELCASENAME, Boolean.TRUE);
527         
528         //behavior of JabRef before LWang_AdjustableFieldOrder 1
529         //0 sorted order (2.10 default), 1 unsorted order (2.9.2 default), 2 user defined
530         defaults.put(WRITEFIELD_SORTSTYLE, 0);
531         defaults.put(WRITEFIELD_USERDEFINEDORDER, "author;title;journal;year;volume;number;pages;month;note;volume;pages;part;eid");
532         defaults.put(WRITEFIELD_WRAPFIELD, Boolean.FALSE);
533
534         defaults.put("useRemoteServer", Boolean.FALSE);
535         defaults.put("remoteServerPort", 6050);
536
537         defaults.put("personalJournalList", null);
538         defaults.put("externalJournalLists", null);
539         defaults.put("citeCommand", "cite"); // obsoleted by the app-specific ones
540         defaults.put("citeCommandVim", "\\cite");
541         defaults.put("citeCommandEmacs", "\\cite");
542         defaults.put("citeCommandWinEdt", "\\cite");
543         defaults.put("citeCommandLed", "\\cite");
544         defaults.put("floatMarkedEntries", Boolean.TRUE);
545
546         defaults.put("useNativeFileDialogOnMac", Boolean.FALSE);
547         defaults.put("filechooserDisableRename", Boolean.TRUE);
548
549         defaults.put("lastUsedExport", null);
550         defaults.put("sidePaneWidth", -1);
551
552         defaults.put("importInspectionDialogWidth", 650);
553         defaults.put("importInspectionDialogHeight", 650);
554         defaults.put("searchDialogWidth", 650);
555         defaults.put("searchDialogHeight", 500);
556         defaults.put("showFileLinksUpgradeWarning", Boolean.TRUE);
557         defaults.put(AUTOLINK_EXACT_KEY_ONLY, Boolean.TRUE);
558         defaults.put("numericFields", "mittnum;author");
559         defaults.put("runAutomaticFileSearch", Boolean.FALSE);
560         defaults.put("useLockFiles", Boolean.TRUE);
561         defaults.put("autoSave", Boolean.TRUE);
562         defaults.put("autoSaveInterval", 5);
563         defaults.put("promptBeforeUsingAutosave", Boolean.TRUE);
564         defaults.put("deletePlugins", "");
565         defaults.put("enforceLegalBibtexKey", Boolean.TRUE);
566         defaults.put("biblatexMode", Boolean.FALSE);
567         // Curly brackets ({}) are the default delimiters, not quotes (") as these cause trouble when they appear within the field value:
568         // Currently, JabRef does not escape them
569         defaults.put("valueDelimiters", 1);
570         defaults.put("includeEmptyFields", Boolean.FALSE);
571         defaults.put("keyGenFirstLetterA", Boolean.TRUE);
572         defaults.put("keyGenAlwaysAddLetter", Boolean.FALSE);
573         defaults.put(JabRefPreferences.EMAIL_SUBJECT, Globals.lang("References"));
574         defaults.put(JabRefPreferences.OPEN_FOLDERS_OF_ATTACHED_FILES, Boolean.FALSE);
575         defaults.put("allowFileAutoOpenBrowse", Boolean.TRUE);
576         defaults.put("webSearchVisible", Boolean.FALSE);
577         defaults.put("selectedFetcherIndex", 0);
578         defaults.put("bibLocationAsFileDir", Boolean.TRUE);
579         defaults.put("bibLocAsPrimaryDir", Boolean.FALSE);
580         defaults.put("dbConnectServerType", "MySQL");
581         defaults.put("dbConnectHostname", "localhost");
582         defaults.put("dbConnectDatabase", "jabref");
583         defaults.put("dbConnectUsername", "root");
584         CleanUpAction.putDefaults(defaults);
585
586         // defaults for DroppedFileHandler UI
587         defaults.put(DroppedFileHandler.DFH_LEAVE, Boolean.FALSE);
588         defaults.put(DroppedFileHandler.DFH_COPY, Boolean.TRUE);
589         defaults.put(DroppedFileHandler.DFH_MOVE, Boolean.FALSE);
590         defaults.put(DroppedFileHandler.DFH_RENAME, Boolean.FALSE);
591
592         //defaults.put("lastAutodetectedImport", "");
593         //defaults.put("autoRemoveExactDuplicates", Boolean.FALSE);
594         //defaults.put("confirmAutoRemoveExactDuplicates", Boolean.TRUE);
595         //defaults.put("tempDir", System.getProperty("java.io.tmpdir"));
596         //Util.pr(System.getProperty("java.io.tempdir"));
597         //defaults.put("keyPattern", new LabelPattern(KEY_PATTERN));
598         defaults.put(ImportSettingsTab.PREF_IMPORT_ALWAYSUSE, Boolean.FALSE);
599         defaults.put(ImportSettingsTab.PREF_IMPORT_DEFAULT_PDF_IMPORT_STYLE, ImportSettingsTab.DEFAULT_STYLE);
600         defaults.put(ImportSettingsTab.PREF_IMPORT_FILENAMEPATTERN, ImportSettingsTab.DEFAULT_FILENAMEPATTERNS[0]);
601
602         restoreKeyBindings();
603
604         customExports = new CustomExportList(new ExportComparator());
605         customImports = new CustomImportList(this);
606
607         //defaults.put("oooWarning", Boolean.TRUE);
608         updateSpecialFieldHandling();
609         WRAPPED_USERNAME = "[" + get("defaultOwner") + "]";
610         MARKING_WITH_NUMBER_PATTERN = "\\[" + get("defaultOwner").replaceAll("\\\\", "\\\\\\\\") + ":(\\d+)\\]";
611
612         String defaultExpression = "**/.*[bibtexkey].*\\\\.[extension]";
613         defaults.put(DEFAULT_REG_EXP_SEARCH_EXPRESSION_KEY, defaultExpression);
614         defaults.put(REG_EXP_SEARCH_EXPRESSION_KEY, defaultExpression);
615         defaults.put(USE_REG_EXP_SEARCH_KEY, Boolean.FALSE);
616         defaults.put("useIEEEAbrv", Boolean.TRUE);
617         defaults.put("useConvertToEquation", Boolean.FALSE);
618         defaults.put("useCaseKeeperOnSearch", Boolean.TRUE);
619         defaults.put("useUnitFormatterOnSearch", Boolean.TRUE);
620
621         defaults.put("userFileDir", GUIGlobals.FILE_FIELD + "Directory");
622         try {
623             defaults.put("userFileDirInd_Legacy", GUIGlobals.FILE_FIELD + "Directory" + "-" + get("defaultOwner") + "@" + InetAddress.getLocalHost().getHostName()); // Legacy setting name - was a bug: @ not allowed inside BibTeX comment text. Retained for backward comp.
624             defaults.put("userFileDirIndividual", GUIGlobals.FILE_FIELD + "Directory" + "-" + get("defaultOwner") + "-" + InetAddress.getLocalHost().getHostName()); // Valid setting name
625         } catch (UnknownHostException ex) {
626             Globals.logger("Hostname not found.");
627             defaults.put("userFileDirInd_Legacy", GUIGlobals.FILE_FIELD + "Directory" + "-" + get("defaultOwner"));
628             defaults.put("userFileDirIndividual", GUIGlobals.FILE_FIELD + "Directory" + "-" + get("defaultOwner"));
629         }
630     }
631
632     public void setLanguageDependentDefaultValues() {
633
634         // Entry editor tab 0:
635         defaults.put(CUSTOM_TAB_NAME + "_def0", Globals.lang("General"));
636         defaults.put(CUSTOM_TAB_FIELDS + "_def0", "crossref;keywords;file;doi;url;"
637                 + "comment;owner;timestamp");
638
639         // Entry editor tab 1:
640         defaults.put(CUSTOM_TAB_FIELDS + "_def1", "abstract");
641         defaults.put(CUSTOM_TAB_NAME + "_def1", Globals.lang("Abstract"));
642
643         // Entry editor tab 2: Review Field - used for research comments, etc.
644         defaults.put(CUSTOM_TAB_FIELDS + "_def2", "review");
645         defaults.put(CUSTOM_TAB_NAME + "_def2", Globals.lang("Review"));
646
647     }
648
649     public static final String DEFAULT_REG_EXP_SEARCH_EXPRESSION_KEY = "defaultRegExpSearchExpression";
650     public static final String REG_EXP_SEARCH_EXPRESSION_KEY = "regExpSearchExpression";
651     public static final String USE_REG_EXP_SEARCH_KEY = "useRegExpSearch";
652     public static final String AUTOLINK_EXACT_KEY_ONLY = "autolinkExactKeyOnly";
653
654     public static final String EMAIL_SUBJECT = "emailSubject";
655     public static final String OPEN_FOLDERS_OF_ATTACHED_FILES = "openFoldersOfAttachedFiles";
656
657     public static final String GROUP_SHOW_NUMBER_OF_ELEMENTS = "groupShowNumberOfElements";
658
659     public boolean putBracesAroundCapitals(String fieldName) {
660         return putBracesAroundCapitalsFields.contains(fieldName);
661     }
662
663     public void updateSpecialFieldHandling() {
664         putBracesAroundCapitalsFields.clear();
665         String fieldString = get("putBracesAroundCapitals");
666         if (fieldString.length() > 0) {
667             String[] fields = fieldString.split(";");
668             for (String field : fields) {
669                 putBracesAroundCapitalsFields.add(field.trim());
670             }
671         }
672         nonWrappableFields.clear();
673         fieldString = get("nonWrappableFields");
674         if (fieldString.length() > 0) {
675             String[] fields = fieldString.split(";");
676             for (String field : fields) {
677                 nonWrappableFields.add(field.trim());
678             }
679         }
680
681     }
682
683     public char getValueDelimiters(int index) {
684         return getValueDelimiters()[index];
685     }
686
687     public char[] getValueDelimiters() {
688         return VALUE_DELIMITERS[getInt(
689                 "valueDelimiters")];
690     }
691
692     /**
693      * Check whether a key is set (differently from null).
694      *
695      * @param key The key to check.
696      * @return true if the key is set, false otherwise.
697      */
698     public boolean hasKey(String key) {
699         return prefs.get(key, null) != null;
700     }
701
702     public String get(String key) {
703         String result = prefs.get(key, (String) defaults.get(key));
704         //System.out.println("READ PREF [" + key + "]=" + result);
705         return result;
706     }
707
708     public String get(String key, String def) {
709         return prefs.get(key, def);
710     }
711
712     public boolean getBoolean(String key) {
713         return prefs.getBoolean(key, getBooleanDefault(key));
714     }
715
716     public boolean getBooleanDefault(String key) {
717         return (Boolean) defaults.get(key);
718     }
719
720     public double getDouble(String key) {
721         return prefs.getDouble(key, getDoubleDefault(key));
722     }
723
724     public double getDoubleDefault(String key) {
725         return (Double) defaults.get(key);
726     }
727
728     public int getInt(String key) {
729         return prefs.getInt(key, getIntDefault(key));
730     }
731
732     public int getIntDefault(String key) {
733         return (Integer) defaults.get(key);
734     }
735
736     public byte[] getByteArray(String key) {
737         return prefs.getByteArray(key, getByteArrayDefault(key));
738     }
739
740     public byte[] getByteArrayDefault(String key) {
741         return (byte[]) defaults.get(key);
742     }
743
744     public void put(String key, String value) {
745         //System.out.println("WRITE PREF [" + key + "]=" + value);
746         prefs.put(key, value);
747     }
748
749     public void putBoolean(String key, boolean value) {
750         prefs.putBoolean(key, value);
751     }
752
753     public void putDouble(String key, double value) {
754         prefs.putDouble(key, value);
755     }
756
757     public void putInt(String key, int value) {
758         prefs.putInt(key, value);
759     }
760
761     public void putByteArray(String key, byte[] value) {
762         prefs.putByteArray(key, value);
763     }
764
765     public void remove(String key) {
766         prefs.remove(key);
767     }
768
769     /**
770      * Puts a string array into the Preferences, by linking its elements with
771      * ';' into a single string. Escape characters make the process transparent
772      * even if strings contain ';'.
773      */
774     public void putStringArray(String key, String[] value) {
775         if (value == null) {
776             remove(key);
777             return;
778         }
779
780         if (value.length > 0) {
781             StringBuilder linked = new StringBuilder();
782             for (int i = 0; i < value.length - 1; i++) {
783                 linked.append(makeEscape(value[i]));
784                 linked.append(";");
785             }
786             linked.append(makeEscape(value[value.length - 1]));
787             put(key, linked.toString());
788         } else {
789             put(key, "");
790         }
791     }
792
793     /**
794      * Returns a String[] containing the chosen columns.
795      */
796     public String[] getStringArray(String key) {
797         String names = get(key);
798         if (names == null) {
799             return null;
800         }
801
802         StringReader rd = new StringReader(names);
803         Vector<String> arr = new Vector<String>();
804         String rs;
805         try {
806             while ((rs = getNextUnit(rd)) != null) {
807                 arr.add(rs);
808             }
809         } catch (IOException ignored) {
810         }
811         String[] res = new String[arr.size()];
812         for (int i = 0; i < res.length; i++) {
813             res[i] = arr.elementAt(i);
814         }
815
816         return res;
817     }
818
819     /**
820      * Looks up a color definition in preferences, and returns the Color object.
821      *
822      * @param key The key for this setting.
823      * @return The color corresponding to the setting.
824      */
825     public Color getColor(String key) {
826         String value = get(key);
827         int[] rgb = getRgb(value);
828         return new Color(rgb[0], rgb[1], rgb[2]);
829     }
830
831     public Color getDefaultColor(String key) {
832         String value = (String) defaults.get(key);
833         int[] rgb = getRgb(value);
834         return new Color(rgb[0], rgb[1], rgb[2]);
835     }
836
837     /**
838      * Set the default value for a key. This is useful for plugins that need to
839      * add default values for the prefs keys they use.
840      *
841      * @param key The preferences key.
842      * @param value The default value.
843      */
844     public void putDefaultValue(String key, Object value) {
845         defaults.put(key, value);
846     }
847
848     /**
849      * Stores a color in preferences.
850      *
851      * @param key The key for this setting.
852      * @param color The Color to store.
853      */
854     public void putColor(String key, Color color) {
855         String rgb = String.valueOf(color.getRed()) + ':' + String.valueOf(color.getGreen()) + ':' + String.valueOf(color.getBlue());
856         put(key, rgb);
857     }
858
859     /**
860      * Looks up a color definition in preferences, and returns an array
861      * containing the RGB values.
862      *
863      * @param value The key for this setting.
864      * @return The RGB values corresponding to this color setting.
865      */
866     public int[] getRgb(String value) {
867         String[] elements = value.split(":");
868         int[] values = new int[3];
869         values[0] = Integer.parseInt(elements[0]);
870         values[1] = Integer.parseInt(elements[1]);
871         values[2] = Integer.parseInt(elements[2]);
872         return values;
873     }
874
875     /**
876      * Returns the KeyStroke for this binding, as defined by the defaults, or in
877      * the Preferences.
878      */
879     public KeyStroke getKey(String bindName) {
880
881         String s = keyBinds.get(bindName);
882         // If the current key bindings don't contain the one asked for,
883         // we fall back on the default. This should only happen when a
884         // user has his own set in Preferences, and has upgraded to a
885         // new version where new bindings have been introduced.
886         if (s == null) {
887             s = defKeyBinds.get(bindName);
888             // So, if this happens, we add the default value to the current
889             // hashmap, so this doesn't happen again, and so this binding
890             // will appear in the KeyBindingsDialog.
891             Globals.logger("Could not get key binding for \"" + bindName + "\"");
892             s = "Not associated"; // if the item of menu not in defKeyBind list
893             keyBinds.put(bindName, s);
894         }
895
896         if (Globals.ON_MAC) {
897             return getKeyForMac(KeyStroke.getKeyStroke(s));
898         } else {
899             return KeyStroke.getKeyStroke(s);
900         }
901     }
902
903     /**
904      * Returns the KeyStroke for this binding, as defined by the defaults, or in
905      * the Preferences, but adapted for Mac users, with the Command key
906      * preferred instead of Control.
907      */
908     private KeyStroke getKeyForMac(KeyStroke ks) {
909         if (ks == null) {
910             return null;
911         }
912         int keyCode = ks.getKeyCode();
913         if ((ks.getModifiers() & KeyEvent.CTRL_MASK) == 0) {
914             return ks;
915         } else {
916             int modifiers = 0;
917             if ((ks.getModifiers() & KeyEvent.SHIFT_MASK) != 0) {
918                 modifiers = modifiers | KeyEvent.SHIFT_MASK;
919             }
920             if ((ks.getModifiers() & KeyEvent.ALT_MASK) != 0) {
921                 modifiers = modifiers | KeyEvent.ALT_MASK;
922             }
923
924             return KeyStroke.getKeyStroke(keyCode, Globals.getShortcutMask() + modifiers);
925         }
926     }
927
928     /**
929      * Returns the HashMap containing all key bindings.
930      */
931     public HashMap<String, String> getKeyBindings() {
932         return keyBinds;
933     }
934
935     /**
936      * Returns the HashMap containing default key bindings.
937      */
938     public HashMap<String, String> getDefaultKeys() {
939         return defKeyBinds;
940     }
941
942     /**
943      * Clear all preferences.
944      *
945      * @throws BackingStoreException
946      */
947     public void clear() throws BackingStoreException {
948         prefs.clear();
949     }
950
951     public void clear(String key) {
952         prefs.remove(key);
953     }
954
955     /**
956      * Calling this method will write all preferences into the preference store.
957      */
958     public void flush() {
959         if (getBoolean("memoryStickMode")) {
960             try {
961                 exportPreferences("jabref.xml");
962             } catch (IOException e) {
963                 Globals.logger("Could not save preferences for memory stick mode: " + e.getLocalizedMessage());
964             }
965         }
966         try {
967             prefs.flush();
968         } catch (BackingStoreException ex) {
969             ex.printStackTrace();
970         }
971     }
972
973     /**
974      * Stores new key bindings into Preferences, provided they actually differ
975      * from the old ones.
976      */
977     public void setNewKeyBindings(HashMap<String, String> newBindings) {
978         if (!newBindings.equals(keyBinds)) {
979             // This confirms that the bindings have actually changed.
980             String[] bindNames = new String[newBindings.size()],
981                     bindings = new String[newBindings.size()];
982             int index = 0;
983             for (String nm : newBindings.keySet()) {
984                 String bnd = newBindings.get(nm);
985                 bindNames[index] = nm;
986                 bindings[index] = bnd;
987                 index++;
988             }
989             putStringArray("bindNames", bindNames);
990             putStringArray("bindings", bindings);
991             keyBinds = newBindings;
992         }
993     }
994
995     /**
996      * Fetches key patterns from preferences Not cached
997      *
998      * @return LabelPattern containing all keys. Returned LabelPattern has no
999      * parent
1000      */
1001     public LabelPattern getKeyPattern() {
1002         keyPattern = new LabelPattern();
1003         Preferences pre = Preferences.userNodeForPackage(net.sf.jabref.labelPattern.LabelPattern.class);
1004         try {
1005             String[] keys = pre.keys();
1006             if (keys.length > 0) {
1007                 for (String key : keys) {
1008                     keyPattern.addLabelPattern(key, pre.get(key, null));
1009                 }
1010             }
1011         } catch (BackingStoreException ex) {
1012             Globals.logger("BackingStoreException in JabRefPreferences.getKeyPattern");
1013         }
1014         return keyPattern;
1015     }
1016
1017     /**
1018      * Adds the given key pattern to the preferences
1019      *
1020      * @param pattern the pattern to store
1021      */
1022     public void putKeyPattern(LabelPattern pattern) {
1023         keyPattern = pattern;
1024
1025         // Store overridden definitions to Preferences.
1026         Preferences pre = Preferences.userNodeForPackage(net.sf.jabref.labelPattern.LabelPattern.class);
1027         try {
1028             pre.clear(); // We remove all old entries.
1029         } catch (BackingStoreException ex) {
1030             Globals.logger("BackingStoreException in JabRefPreferences.putKeyPattern");
1031         }
1032
1033         for (String s : pattern.keySet()) {
1034             ArrayList<String> value = pattern.get(s);
1035             if (value != null) {
1036                 // no default value
1037                 // the first entry in the array is the full pattern
1038                 // see net.sf.jabref.labelPattern.LabelPatternUtil.split(String)
1039                 pre.put(s, value.get(0));
1040             }
1041         }
1042     }
1043
1044     private void restoreKeyBindings() {
1045         // Define default keybindings.
1046         defineDefaultKeyBindings();
1047
1048         // First read the bindings, and their names.
1049         String[] bindNames = getStringArray("bindNames"),
1050                 bindings = getStringArray("bindings");
1051
1052         // Then set up the key bindings HashMap.
1053         if ((bindNames == null) || (bindings == null)
1054                 || (bindNames.length != bindings.length)) {
1055             // Nothing defined in Preferences, or something is wrong.
1056             setDefaultKeyBindings();
1057             return;
1058         }
1059
1060         for (int i = 0; i < bindNames.length; i++) {
1061             keyBinds.put(bindNames[i], bindings[i]);
1062         }
1063     }
1064
1065     private void setDefaultKeyBindings() {
1066         keyBinds = defKeyBinds;
1067     }
1068
1069     private void defineDefaultKeyBindings() {
1070         defKeyBinds.put("Push to application", "ctrl L");
1071         defKeyBinds.put("Push to LyX", "ctrl L");
1072         defKeyBinds.put("Push to WinEdt", "ctrl shift W");
1073         defKeyBinds.put("Quit JabRef", "ctrl Q");
1074         defKeyBinds.put("Open database", "ctrl O");
1075         defKeyBinds.put("Save database", "ctrl S");
1076         defKeyBinds.put("Save database as ...", "ctrl shift S");
1077         defKeyBinds.put("Save all", "ctrl alt S");
1078         defKeyBinds.put("Close database", "ctrl W");
1079         defKeyBinds.put("New entry", "ctrl N");
1080         defKeyBinds.put("Cut", "ctrl X");
1081         defKeyBinds.put("Copy", "ctrl C");
1082         defKeyBinds.put("Paste", "ctrl V");
1083         defKeyBinds.put("Undo", "ctrl Z");
1084         defKeyBinds.put("Redo", "ctrl Y");
1085         defKeyBinds.put("Help", "F1");
1086         defKeyBinds.put("New article", "ctrl shift A");
1087         defKeyBinds.put("New book", "ctrl shift B");
1088         defKeyBinds.put("New phdthesis", "ctrl shift T");
1089         defKeyBinds.put("New inbook", "ctrl shift I");
1090         defKeyBinds.put("New mastersthesis", "ctrl shift M");
1091         defKeyBinds.put("New proceedings", "ctrl shift P");
1092         defKeyBinds.put("New unpublished", "ctrl shift U");
1093         defKeyBinds.put("Edit strings", "ctrl T");
1094         defKeyBinds.put("Edit preamble", "ctrl P");
1095         defKeyBinds.put("Select all", "ctrl A");
1096         defKeyBinds.put("Toggle groups interface", "ctrl shift G");
1097         defKeyBinds.put("Autogenerate BibTeX keys", "ctrl G");
1098         defKeyBinds.put("Search", "ctrl F");
1099         defKeyBinds.put("Incremental search", "ctrl shift F");
1100         defKeyBinds.put("Repeat incremental search", "ctrl shift F");
1101         defKeyBinds.put("Close dialog", "ESCAPE");
1102         defKeyBinds.put("Close entry editor", "ESCAPE");
1103         defKeyBinds.put("Close preamble editor", "ESCAPE");
1104         defKeyBinds.put("Back, help dialog", "LEFT");
1105         defKeyBinds.put("Forward, help dialog", "RIGHT");
1106         defKeyBinds.put("Preamble editor, store changes", "alt S");
1107         defKeyBinds.put("Clear search", "ESCAPE");
1108         defKeyBinds.put("Entry editor, next panel", "ctrl TAB");//"ctrl PLUS");//"shift Right");
1109         defKeyBinds.put("Entry editor, previous panel", "ctrl shift TAB");//"ctrl MINUS");
1110         defKeyBinds.put("Entry editor, next panel 2", "ctrl PLUS");//"ctrl PLUS");//"shift Right");
1111         defKeyBinds.put("Entry editor, previous panel 2", "ctrl MINUS");//"ctrl MINUS");
1112         defKeyBinds.put("Entry editor, next entry", "ctrl shift DOWN");
1113         defKeyBinds.put("Entry editor, previous entry", "ctrl shift UP");
1114         defKeyBinds.put("Entry editor, store field", "alt S");
1115         defKeyBinds.put("String dialog, add string", "ctrl N");
1116         defKeyBinds.put("String dialog, remove string", "shift DELETE");
1117         defKeyBinds.put("String dialog, move string up", "ctrl UP");
1118         defKeyBinds.put("String dialog, move string down", "ctrl DOWN");
1119         defKeyBinds.put("Save session", "F11");
1120         defKeyBinds.put("Load session", "F12");
1121         defKeyBinds.put("Copy \\cite{BibTeX key}", "ctrl K");
1122         defKeyBinds.put("Copy BibTeX key", "ctrl shift K");
1123         defKeyBinds.put("Copy BibTeX key and title", "ctrl shift alt K");
1124         defKeyBinds.put("Next tab", "ctrl PAGE_DOWN");
1125         defKeyBinds.put("Previous tab", "ctrl PAGE_UP");
1126         defKeyBinds.put("Replace string", "ctrl R");
1127         defKeyBinds.put("Delete", "DELETE");
1128         defKeyBinds.put("Open file", "F4");
1129         defKeyBinds.put("Open PDF or PS", "shift F5");
1130         defKeyBinds.put("Open URL or DOI", "F3");
1131         defKeyBinds.put("Open SPIRES entry", "ctrl F3");
1132         defKeyBinds.put("Toggle entry preview", "ctrl F9");
1133         defKeyBinds.put("Switch preview layout", "F9");
1134         defKeyBinds.put("Edit entry", "ctrl E");
1135         defKeyBinds.put("Mark entries", "ctrl M");
1136         defKeyBinds.put("Unmark entries", "ctrl shift M");
1137         defKeyBinds.put("Fetch Medline", "F5");
1138         defKeyBinds.put("Search ScienceDirect", "ctrl F5");
1139         defKeyBinds.put("Search ADS", "ctrl shift F6");
1140         defKeyBinds.put("New from plain text", "ctrl shift N");
1141         defKeyBinds.put("Synchronize files", "ctrl F4");
1142         defKeyBinds.put("Synchronize PDF", "shift F4");
1143         defKeyBinds.put("Synchronize PS", "ctrl shift F4");
1144         defKeyBinds.put("Focus entry table", "ctrl shift E");
1145
1146         defKeyBinds.put("Abbreviate", "ctrl alt A");
1147         defKeyBinds.put("Unabbreviate", "ctrl alt shift A");
1148         defKeyBinds.put("Search IEEEXplore", "alt F8");
1149         defKeyBinds.put("Search ACM Portal", "ctrl shift F8");
1150         defKeyBinds.put("Fetch ArXiv.org", "shift F8");
1151         defKeyBinds.put("Search JSTOR", "shift F9");
1152         defKeyBinds.put("Cleanup", "ctrl shift F7");
1153         defKeyBinds.put("Write XMP", "ctrl F7");
1154         defKeyBinds.put("New file link", "ctrl N");
1155         defKeyBinds.put("Fetch SPIRES", "ctrl F8");
1156         defKeyBinds.put("Fetch INSPIRE", "ctrl F2");
1157         defKeyBinds.put("Back", "alt LEFT");
1158         defKeyBinds.put("Forward", "alt RIGHT");
1159         defKeyBinds.put("Import into current database", "ctrl I");
1160         defKeyBinds.put("Import into new database", "ctrl alt I");
1161         defKeyBinds.put(FindUnlinkedFilesDialog.ACTION_COMMAND, "");
1162         defKeyBinds.put("Increase table font size", "ctrl PLUS");
1163         defKeyBinds.put("Decrease table font size", "ctrl MINUS");
1164         defKeyBinds.put("Automatically link files", "alt F");
1165         defKeyBinds.put("Resolve duplicate BibTeX keys", "ctrl shift D");
1166         defKeyBinds.put("Refresh OO", "ctrl alt O");
1167         defKeyBinds.put("File list editor, move entry up", "ctrl UP");
1168         defKeyBinds.put("File list editor, move entry down", "ctrl DOWN");
1169         defKeyBinds.put("Minimize to system tray", "ctrl alt W");
1170     }
1171
1172     private String getNextUnit(Reader data) throws IOException {
1173         // character last read
1174         // -1 if end of stream
1175         // initialization necessary, because of Java compiler
1176         int c = -1;
1177
1178         // last character was escape symbol
1179         boolean escape = false;
1180
1181         // true if a ";" is found
1182         boolean done = false;
1183
1184         StringBuffer res = new StringBuffer();
1185         while (!done && ((c = data.read()) != -1)) {
1186             if (c == '\\') {
1187                 if (!escape) {
1188                     escape = true;
1189                 } else {
1190                     escape = false;
1191                     res.append('\\');
1192                 }
1193             } else {
1194                 if (c == ';') {
1195                     if (!escape) {
1196                         done = true;
1197                     } else {
1198                         res.append(';');
1199                     }
1200                 } else {
1201                     res.append((char) c);
1202                 }
1203                 escape = false;
1204             }
1205         }
1206         if (res.length() > 0) {
1207             return res.toString();
1208         } else if (c == -1) {
1209             // end of stream
1210             return null;
1211         } else {
1212             return "";
1213         }
1214     }
1215
1216     private String makeEscape(String s) {
1217         StringBuffer sb = new StringBuffer();
1218         int c;
1219         for (int i = 0; i < s.length(); i++) {
1220             c = s.charAt(i);
1221             if ((c == '\\') || (c == ';')) {
1222                 sb.append('\\');
1223             }
1224             sb.append((char) c);
1225         }
1226         return sb.toString();
1227     }
1228
1229     /**
1230      * Stores all information about the entry type in preferences, with the tag
1231      * given by number.
1232      */
1233     public void storeCustomEntryType(CustomEntryType tp, int number) {
1234         String nr = "" + number;
1235         put(CUSTOM_TYPE_NAME + nr, tp.getName());
1236         put(CUSTOM_TYPE_REQ + nr, tp.getRequiredFieldsString());//tp.getRequiredFields());
1237         putStringArray(CUSTOM_TYPE_OPT + nr, tp.getOptionalFields());
1238         putStringArray(CUSTOM_TYPE_PRIOPT + nr, tp.getPrimaryOptionalFields());
1239
1240     }
1241
1242     /**
1243      * Retrieves all information about the entry type in preferences, with the
1244      * tag given by number.
1245      */
1246     public CustomEntryType getCustomEntryType(int number) {
1247         String nr = "" + number;
1248         String name = get(CUSTOM_TYPE_NAME + nr);
1249         String[] req = getStringArray(CUSTOM_TYPE_REQ + nr),
1250                 opt = getStringArray(CUSTOM_TYPE_OPT + nr),
1251                 priOpt = getStringArray(CUSTOM_TYPE_PRIOPT + nr);
1252         if (name == null) {
1253             return null;
1254         }
1255         if (priOpt == null) {
1256             return new CustomEntryType(Util.nCase(name), req, opt);
1257         }
1258         ArrayList<String> secOpt = new ArrayList<String>();
1259         Collections.addAll(secOpt, opt);
1260         for (String aPriOpt : priOpt) {
1261             secOpt.remove(aPriOpt);
1262         }
1263         return new CustomEntryType(Util.nCase(name), req, priOpt,
1264                 secOpt.toArray(new String[secOpt.size()]));
1265
1266     }
1267
1268     public List<ExternalFileType> getDefaultExternalFileTypes() {
1269         List<ExternalFileType> list = new ArrayList<ExternalFileType>();
1270         list.add(new ExternalFileType("PDF", "pdf", "application/pdf", "evince", "pdfSmall"));
1271         list.add(new ExternalFileType("PostScript", "ps", "application/postscript", "evince", "psSmall"));
1272         list.add(new ExternalFileType("Word", "doc", "application/msword", "oowriter", "openoffice"));
1273         list.add(new ExternalFileType("Word 2007+", "docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "oowriter", "openoffice"));
1274         list.add(new ExternalFileType("OpenDocument text", "odt", "application/vnd.oasis.opendocument.text", "oowriter", "openoffice"));
1275         list.add(new ExternalFileType("Excel", "xls", "application/excel", "oocalc", "openoffice"));
1276         list.add(new ExternalFileType("Excel 2007+", "xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "oocalc", "openoffice"));
1277         list.add(new ExternalFileType("OpenDocument spreadsheet", "ods", "application/vnd.oasis.opendocument.spreadsheet", "oocalc", "openoffice"));
1278         list.add(new ExternalFileType("PowerPoint", "ppt", "application/vnd.ms-powerpoint", "ooimpress", "openoffice"));
1279         list.add(new ExternalFileType("PowerPoint 2007+", "pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation", "ooimpress", "openoffice"));
1280         list.add(new ExternalFileType("OpenDocument presentation", "odp", "application/vnd.oasis.opendocument.presentation", "ooimpress", "openoffice"));
1281         list.add(new ExternalFileType("Rich Text Format", "rtf", "application/rtf", "oowriter", "openoffice"));
1282         list.add(new ExternalFileType("PNG image", "png", "image/png", "gimp", "picture"));
1283         list.add(new ExternalFileType("GIF image", "gif", "image/gif", "gimp", "picture"));
1284         list.add(new ExternalFileType("JPG image", "jpg", "image/jpeg", "gimp", "picture"));
1285         list.add(new ExternalFileType("Djvu", "djvu", "", "evince", "psSmall"));
1286         list.add(new ExternalFileType("Text", "txt", "text/plain", "emacs", "emacs"));
1287         list.add(new ExternalFileType("LaTeX", "tex", "application/x-latex", "emacs", "emacs"));
1288         list.add(new ExternalFileType("CHM", "chm", "application/mshelp", "gnochm", "www"));
1289         list.add(new ExternalFileType("TIFF image", "tiff", "image/tiff", "gimp", "picture"));
1290         list.add(new ExternalFileType("URL", "html", "text/html", "firefox", "www"));
1291         list.add(new ExternalFileType("MHT", "mht", "multipart/related", "firefox", "www"));
1292         list.add(new ExternalFileType("ePUB", "epub", "application/epub+zip", "firefox", "www"));
1293
1294         // On all OSes there is a generic application available to handle file opening,
1295         // so we don't need the default application settings anymore:
1296         for (ExternalFileType type : list) {
1297             type.setOpenWith("");
1298         }
1299
1300         return list;
1301     }
1302
1303     public ExternalFileType[] getExternalFileTypeSelection() {
1304         return externalFileTypes.toArray(new ExternalFileType[externalFileTypes.size()]);
1305     }
1306
1307     /**
1308      * Look up the external file type registered with this name, if any.
1309      *
1310      * @param name The file type name.
1311      * @return The ExternalFileType registered, or null if none.
1312      */
1313     public ExternalFileType getExternalFileTypeByName(String name) {
1314         for (ExternalFileType type : externalFileTypes) {
1315             if (type.getName().equals(name)) {
1316                 return type;
1317             }
1318         }
1319         // Return an instance that signifies an unknown file type:
1320         return new UnknownExternalFileType(name);
1321     }
1322
1323     /**
1324      * Look up the external file type registered for this extension, if any.
1325      *
1326      * @param extension The file extension.
1327      * @return The ExternalFileType registered, or null if none.
1328      */
1329     public ExternalFileType getExternalFileTypeByExt(String extension) {
1330         for (ExternalFileType type : externalFileTypes) {
1331             if ((type.getExtension() != null) && type.getExtension().equalsIgnoreCase(extension)) {
1332                 return type;
1333             }
1334         }
1335         return null;
1336     }
1337
1338     /**
1339      * Look up the external file type registered for this filename, if any.
1340      *
1341      * @param filename The name of the file whose type to look up.
1342      * @return The ExternalFileType registered, or null if none.
1343      */
1344     public ExternalFileType getExternalFileTypeForName(String filename) {
1345         int longestFound = -1;
1346         ExternalFileType foundType = null;
1347         for (ExternalFileType type : externalFileTypes) {
1348             if ((type.getExtension() != null) && filename.toLowerCase().
1349                     endsWith(type.getExtension().toLowerCase())) {
1350                 if (type.getExtension().length() > longestFound) {
1351                     longestFound = type.getExtension().length();
1352                     foundType = type;
1353                 }
1354             }
1355         }
1356         return foundType;
1357     }
1358
1359     /**
1360      * Look up the external file type registered for this MIME type, if any.
1361      *
1362      * @param mimeType The MIME type.
1363      * @return The ExternalFileType registered, or null if none. For the mime
1364      * type "text/html", a valid file type is guaranteed to be returned.
1365      */
1366     public ExternalFileType getExternalFileTypeByMimeType(String mimeType) {
1367         for (ExternalFileType type : externalFileTypes) {
1368             if ((type.getMimeType() != null) && type.getMimeType().equals(mimeType)) {
1369                 return type;
1370             }
1371         }
1372         if (mimeType.equals("text/html")) {
1373             return HTML_FALLBACK_TYPE;
1374         } else {
1375             return null;
1376         }
1377     }
1378
1379     /**
1380      * Reset the List of external file types after user customization.
1381      *
1382      * @param types The new List of external file types. This is the complete
1383      * list, not just new entries.
1384      */
1385     public void setExternalFileTypes(List<ExternalFileType> types) {
1386
1387         // First find a list of the default types:
1388         List<ExternalFileType> defTypes = getDefaultExternalFileTypes();
1389         // Make a list of types that are unchanged:
1390         List<ExternalFileType> unchanged = new ArrayList<ExternalFileType>();
1391
1392         externalFileTypes.clear();
1393         for (ExternalFileType type : types) {
1394             externalFileTypes.add(type);
1395
1396             // See if we can find a type with matching name in the default type list:
1397             ExternalFileType found = null;
1398             for (ExternalFileType defType : defTypes) {
1399                 if (defType.getName().equals(type.getName())) {
1400                     found = defType;
1401                     break;
1402                 }
1403             }
1404             if (found != null) {
1405                 // Found it! Check if it is an exact match, or if it has been customized:
1406                 if (found.equals(type)) {
1407                     unchanged.add(type);
1408                 } else {
1409                     // It was modified. Remove its entry from the defaults list, since
1410                     // the type hasn't been removed:
1411                     defTypes.remove(found);
1412                 }
1413             }
1414         }
1415
1416         // Go through unchanged types. Remove them from the ones that should be stored,
1417         // and from the list of defaults, since we don't need to mention these in prefs:
1418         for (ExternalFileType type : unchanged) {
1419             defTypes.remove(type);
1420             types.remove(type);
1421         }
1422
1423         // Now set up the array to write to prefs, containing all new types, all modified
1424         // types, and a flag denoting each default type that has been removed:
1425         String[][] array = new String[types.size() + defTypes.size()][];
1426         int i = 0;
1427         for (ExternalFileType type : types) {
1428             array[i] = type.getStringArrayRepresentation();
1429             i++;
1430         }
1431         for (ExternalFileType type : defTypes) {
1432             array[i] = new String[]{type.getName(), FILE_TYPE_REMOVED_FLAG};
1433             i++;
1434         }
1435         //System.out.println("Encoded: '"+Util.encodeStringArray(array)+"'");
1436         put("externalFileTypes", Util.encodeStringArray(array));
1437     }
1438
1439     /**
1440      * Set up the list of external file types, either from default values, or
1441      * from values recorded in Preferences.
1442      */
1443     public void updateExternalFileTypes() {
1444         // First get a list of the default file types as a starting point:
1445         List<ExternalFileType> types = getDefaultExternalFileTypes();
1446         // If no changes have been stored, simply use the defaults:
1447         if (prefs.get("externalFileTypes", null) == null) {
1448             externalFileTypes.clear();
1449             externalFileTypes.addAll(types);
1450             return;
1451         }
1452         // Read the prefs information for file types:
1453         String[][] vals = Util.decodeStringDoubleArray(prefs.get("externalFileTypes", ""));
1454         for (String[] val : vals) {
1455             if ((val.length == 2) && (val[1].equals(FILE_TYPE_REMOVED_FLAG))) {
1456                 // This entry indicates that a default entry type should be removed:
1457                 ExternalFileType toRemove = null;
1458                 for (ExternalFileType type : types) {
1459                     if (type.getName().equals(val[0])) {
1460                         toRemove = type;
1461                         break;
1462                     }
1463                 }
1464                 // If we found it, remove it from the type list:
1465                 if (toRemove != null) {
1466                     types.remove(toRemove);
1467                 }
1468             } else {
1469                 // A new or modified entry type. Construct it from the string array:
1470                 ExternalFileType type = new ExternalFileType(val);
1471                 // Check if there is a default type with the same name. If so, this is a
1472                 // modification of that type, so remove the default one:
1473                 ExternalFileType toRemove = null;
1474                 for (ExternalFileType defType : types) {
1475                     if (type.getName().equals(defType.getName())) {
1476                         toRemove = defType;
1477                         break;
1478                     }
1479                 }
1480                 // If we found it, remove it from the type list:
1481                 if (toRemove != null) {
1482                     types.remove(toRemove);
1483                 }
1484
1485                 // Then add the new one:
1486                 types.add(type);
1487             }
1488         }
1489
1490         // Finally, build the list of types based on the modified defaults list:
1491         for (ExternalFileType type : types) {
1492             externalFileTypes.add(type);
1493         }
1494     }
1495
1496     /**
1497      * Removes all information about custom entry types with tags of
1498      *
1499      * @param number or higher.
1500      */
1501     public void purgeCustomEntryTypes(int number) {
1502         purgeSeries(CUSTOM_TYPE_NAME, number);
1503         purgeSeries(CUSTOM_TYPE_REQ, number);
1504         purgeSeries(CUSTOM_TYPE_OPT, number);
1505         purgeSeries(CUSTOM_TYPE_PRIOPT, number);
1506     }
1507
1508     /**
1509      * Removes all entries keyed by prefix+number, where number is equal to or
1510      * higher than the given number.
1511      *
1512      * @param number or higher.
1513      */
1514     public void purgeSeries(String prefix, int number) {
1515         while (get(prefix + number) != null) {
1516             remove(prefix + number);
1517             number++;
1518         }
1519     }
1520
1521     public EntryEditorTabList getEntryEditorTabList() {
1522         if (tabList == null) {
1523             updateEntryEditorTabList();
1524         }
1525         return tabList;
1526     }
1527
1528     public void updateEntryEditorTabList() {
1529         tabList = new EntryEditorTabList();
1530     }
1531
1532     /**
1533      * Exports Preferences to an XML file.
1534      *
1535      * @param filename String File to export to
1536      */
1537     public void exportPreferences(String filename) throws IOException {
1538         File f = new File(filename);
1539         OutputStream os = new FileOutputStream(f);
1540         try {
1541             prefs.exportSubtree(os);
1542         } catch (BackingStoreException ex) {
1543             throw new IOException(ex.getMessage());
1544         }
1545     }
1546
1547     /**
1548      * Imports Preferences from an XML file.
1549      *
1550      * @param filename String File to import from
1551      */
1552     public void importPreferences(String filename) throws IOException {
1553         File f = new File(filename);
1554         InputStream is = new FileInputStream(f);
1555         try {
1556             Preferences.importPreferences(is);
1557         } catch (InvalidPreferencesFormatException ex) {
1558             throw new IOException(ex.getMessage());
1559         }
1560     }
1561
1562     /**
1563      * Determines whether the given field should be written without any sort of
1564      * wrapping.
1565      *
1566      * @param fieldName The field name.
1567      * @return true if the field should not be wrapped.
1568      */
1569     public boolean isNonWrappableField(String fieldName) {
1570         return nonWrappableFields.contains(fieldName);
1571     }
1572 }