diff --git a/src/java/net/sf/jabref/EntryEditorPrefsTab.java b/src/java/net/sf/jabref/EntryEditorPrefsTab.java index 68af8420f4d..f17612510cb 100644 --- a/src/java/net/sf/jabref/EntryEditorPrefsTab.java +++ b/src/java/net/sf/jabref/EntryEditorPrefsTab.java @@ -22,6 +22,8 @@ import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; +import org.xnap.commons.gui.shortcut.EmacsKeyBindings; + import net.sf.jabref.autocompleter.AbstractAutoCompleter; import com.jgoodies.forms.builder.DefaultFormBuilder; @@ -31,7 +33,7 @@ public class EntryEditorPrefsTab extends JPanel implements PrefsTab { private JCheckBox autoOpenForm, showSource, - defSource, disableOnMultiple, autoComplete; + defSource, emacsMode, disableOnMultiple, autoComplete; private JRadioButton autoCompBoth, autoCompFF, autoCompLF, autoCompFirstNameMode_Full, autoCompFirstNameMode_Abbr, autoCompFirstNameMode_Both; boolean oldAutoCompFF, oldAutoCompLF, @@ -62,6 +64,7 @@ public EntryEditorPrefsTab(JabRefFrame frame, JabRefPreferences prefs) { autoOpenForm = new JCheckBox(Globals.lang("Open editor when a new entry is created")); defSource = new JCheckBox(Globals.lang("Show BibTeX source by default")); showSource = new JCheckBox(Globals.lang("Show BibTeX source panel")); + emacsMode = new JCheckBox(Globals.lang("Use Emacs keybindings")); disableOnMultiple = new JCheckBox(Globals.lang("Disable entry editor when multiple entries are selected")); autoComplete = new JCheckBox(Globals.lang("Enable word/name autocompletion")); @@ -105,13 +108,14 @@ public void stateChanged(ChangeEvent event) { ); FormLayout layout = new FormLayout - ("8dlu, left:pref, 8dlu, fill:150dlu, 4dlu, fill:pref", // 4dlu, left:pref, 4dlu", + (// columns + "8dlu, left:pref, 8dlu, fill:150dlu, 4dlu, fill:pref", // 4dlu, left:pref, 4dlu", // rows 1 to 10 "pref, 6dlu, pref, 6dlu, pref, 6dlu, pref, 6dlu, pref, 6dlu, " + // rows 11 to 20 - "pref, 6dlu, pref, 6dlu, pref, 6dlu, pref, pref, pref, pref, " + - // rows 21 to 26 - "6dlu, pref, pref, pref, pref"); + "pref, 6dlu, pref, 6dlu, pref, 6dlu, pref, 6dlu, pref, pref, " + + // rows 21 to 27 + "pref, pref, 6dlu, pref, pref, pref, pref"); DefaultFormBuilder builder = new DefaultFormBuilder(layout); CellConstraints cc = new CellConstraints(); builder.addSeparator(Globals.lang("Editor options"), cc.xyw(1, 1, 5)); @@ -119,9 +123,10 @@ public void stateChanged(ChangeEvent event) { builder.add(disableOnMultiple, cc.xy(2, 5)); builder.add(showSource, cc.xy(2, 7)); builder.add(defSource, cc.xy(2, 9)); + builder.add(emacsMode, cc.xy(2, 11)); - builder.addSeparator(Globals.lang("Autocompletion options"), cc.xyw(1, 11, 5)); - builder.add(autoComplete, cc.xy(2, 13)); + builder.addSeparator(Globals.lang("Autocompletion options"), cc.xyw(1, 13, 5)); + builder.add(autoComplete, cc.xy(2, 15)); DefaultFormBuilder builder3 = new DefaultFormBuilder(new FormLayout("left:pref, 4dlu, fill:150dlu","")); JLabel label = new JLabel(Globals.lang("Use autocompletion for the following fields")+":"); @@ -130,17 +135,17 @@ public void stateChanged(ChangeEvent event) { JLabel label2 = new JLabel(Globals.lang("Autocomplete after following number of characters")+":"); builder3.append(label2); builder3.append(shortestToComplete); - builder.add(builder3.getPanel(), cc.xyw(2, 15, 3)); + builder.add(builder3.getPanel(), cc.xyw(2, 17, 3)); - builder.addSeparator(Globals.lang("Name format used for autocompletion"), cc.xyw(2, 17, 4)); - builder.add(autoCompFF, cc.xy(2,18)); - builder.add(autoCompLF, cc.xy(2,19)); - builder.add(autoCompBoth, cc.xy(2,20)); + builder.addSeparator(Globals.lang("Name format used for autocompletion"), cc.xyw(2, 19, 4)); + builder.add(autoCompFF, cc.xy(2,20)); + builder.add(autoCompLF, cc.xy(2,21)); + builder.add(autoCompBoth, cc.xy(2,22)); - builder.addSeparator(Globals.lang("Treatment of first names"), cc.xyw(2, 22, 4)); - builder.add(autoCompFirstNameMode_Abbr, cc.xy(2,23)); - builder.add(autoCompFirstNameMode_Full, cc.xy(2,24)); - builder.add(autoCompFirstNameMode_Both, cc.xy(2,25)); + builder.addSeparator(Globals.lang("Treatment of first names"), cc.xyw(2, 24, 4)); + builder.add(autoCompFirstNameMode_Abbr, cc.xy(2,25)); + builder.add(autoCompFirstNameMode_Full, cc.xy(2,26)); + builder.add(autoCompFirstNameMode_Both, cc.xy(2,27)); JPanel pan = builder.getPanel(); pan.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); @@ -151,6 +156,7 @@ public void setValues() { autoOpenForm.setSelected(_prefs.getBoolean("autoOpenForm")); defSource.setSelected(_prefs.getBoolean("defaultShowSource")); showSource.setSelected(_prefs.getBoolean("showSource")); + emacsMode.setSelected(_prefs.getBoolean(JabRefPreferences.EDITOR_EMACS_KEYBINDINGS)); disableOnMultiple.setSelected(_prefs.getBoolean("disableOnMultipleSelection")); autoComplete.setSelected(_prefs.getBoolean("autoComplete")); autoCompFields.setText(_prefs.get("autoCompleteFields")); @@ -184,6 +190,16 @@ else if (_prefs.get(JabRefPreferences.AUTOCOMPLETE_FIRSTNAME_MODE).equals(JabRef public void storeSettings() { _prefs.putBoolean("autoOpenForm", autoOpenForm.isSelected()); _prefs.putBoolean("defaultShowSource", defSource.isSelected()); + if (_prefs.getBoolean(JabRefPreferences.EDITOR_EMACS_KEYBINDINGS) != emacsMode.isSelected()) { + // user has changed settings of EMACS mode + // immediately apply the change + if (emacsMode.isSelected()) { + EmacsKeyBindings.load(); + } else { + EmacsKeyBindings.unload(); + } + _prefs.putBoolean(JabRefPreferences.EDITOR_EMACS_KEYBINDINGS, emacsMode.isSelected()); + } _prefs.putBoolean("disableOnMultipleSelection", disableOnMultiple.isSelected()); // We want to know if the following settings have been modified: boolean oldAutoComplete = _prefs.getBoolean("autoComplete"); diff --git a/src/java/net/sf/jabref/GUIGlobals.java b/src/java/net/sf/jabref/GUIGlobals.java index 44fdf0bf5ce..b07de7b0a85 100644 --- a/src/java/net/sf/jabref/GUIGlobals.java +++ b/src/java/net/sf/jabref/GUIGlobals.java @@ -28,6 +28,8 @@ import javax.swing.ImageIcon; import javax.swing.JLabel; +import org.xnap.commons.gui.shortcut.EmacsKeyBindings; + import net.sf.jabref.specialfields.Priority; import net.sf.jabref.specialfields.Quality; import net.sf.jabref.specialfields.Rank; @@ -426,6 +428,10 @@ public static void init() { tableIcons.put(SpecialFieldsUtils.FIELDNAME_PRIORITY, lab); //jabRefFont = new Font("arial", Font.ITALIC/*+Font.BOLD*/, 20); + + if (Globals.prefs.getBoolean(JabRefPreferences.EDITOR_EMACS_KEYBINDINGS)) { + EmacsKeyBindings.load(); + } } } diff --git a/src/java/net/sf/jabref/JTextAreaWithHighlighting.java b/src/java/net/sf/jabref/JTextAreaWithHighlighting.java index 36758025be5..b9cc9cd04c3 100644 --- a/src/java/net/sf/jabref/JTextAreaWithHighlighting.java +++ b/src/java/net/sf/jabref/JTextAreaWithHighlighting.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2003-2011 JabRef contributors. +/* Copyright (C) 2003-2012 JabRef contributors. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or @@ -18,15 +18,17 @@ import java.awt.event.ActionEvent; import java.util.ArrayList; import java.util.regex.Matcher; -import java.util.regex.Pattern; -import javax.swing.*; +import javax.swing.AbstractAction; +import javax.swing.JTextArea; +import javax.swing.KeyStroke; import javax.swing.event.UndoableEditEvent; import javax.swing.event.UndoableEditListener; import javax.swing.text.BadLocationException; import javax.swing.text.DefaultHighlighter; import javax.swing.text.Document; import javax.swing.text.Highlighter; +import javax.swing.text.Keymap; import javax.swing.undo.CannotRedoException; import javax.swing.undo.CannotUndoException; import javax.swing.undo.UndoManager; @@ -109,8 +111,28 @@ public void actionPerformed(ActionEvent evt) { } }); - // Bind the redo action to ctl-Y - getInputMap().put(Globals.prefs.getKey("Redo"), "Redo"); + // Bind the redo action to ctrl-Y + boolean bind = true; + KeyStroke redoKey = Globals.prefs.getKey("Redo"); + if (Globals.prefs.getBoolean(JabRefPreferences.EDITOR_EMACS_KEYBINDINGS)) { + // If emacs is enabled, check if we have a conflict at keys + // If yes, do not bind + // Typically, we have: CTRL+y is "yank" in emacs and REDO in 'normal' settings + // The Emacs key bindings are stored in the keymap, not in the input map. + Keymap keymap2 = getKeymap(); + KeyStroke[] keys = keymap2.getBoundKeyStrokes(); + int i = 0; + while ((i do not bind + bind = false; + } + } + if (bind) { + getInputMap().put(redoKey, "Redo"); + } } /** diff --git a/src/java/net/sf/jabref/JabRefPreferences.java b/src/java/net/sf/jabref/JabRefPreferences.java index bc09f726a40..5c7ba961cdb 100644 --- a/src/java/net/sf/jabref/JabRefPreferences.java +++ b/src/java/net/sf/jabref/JabRefPreferences.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2003-2011 JabRef contributors. +/* Copyright (C) 2003-2012 JabRef contributors. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or @@ -70,6 +70,8 @@ public class JabRefPreferences { SHOWONELETTERHEADINGFORICONCOLUMNS = "showOneLetterHeadingForIconColumns", + EDITOR_EMACS_KEYBINDINGS = "editorEMACSkeyBindings", + SHORTEST_TO_COMPLETE = "shortestToComplete", AUTOCOMPLETE_FIRSTNAME_MODE = "autoCompFirstNameMode", // here are the possible values for _MODE: @@ -248,6 +250,7 @@ private JabRefPreferences() { defaults.put("highLightWords", Boolean.TRUE); defaults.put("searchPanePosX", new Integer(0)); defaults.put("searchPanePosY", new Integer(0)); + defaults.put(EDITOR_EMACS_KEYBINDINGS, Boolean.FALSE); defaults.put("autoComplete", Boolean.TRUE); defaults.put("autoCompleteFields", "author;editor;title;journal;publisher;keywords;crossref"); defaults.put("autoCompFF", Boolean.FALSE); // "Autocomplete names in 'Firstname Lastname' format only" diff --git a/src/java/org/xnap/commons/gui/shortcut/package-info.java b/src/java/org/xnap/commons/gui/shortcut/package-info.java new file mode 100644 index 00000000000..faba338e1b8 --- /dev/null +++ b/src/java/org/xnap/commons/gui/shortcut/package-info.java @@ -0,0 +1,5 @@ +/** + * This package is a subset of the same package of XNap Commons (http://xnap-commons.sourceforge.net/) + * License: LGPL 2.1 + */ +package org.xnap.commons.gui.shortcut; \ No newline at end of file diff --git a/src/txt/CHANGELOG b/src/txt/CHANGELOG index c8748693cfe..356b9103905 100644 --- a/src/txt/CHANGELOG +++ b/src/txt/CHANGELOG @@ -1,4 +1,5 @@ [master branch] + - Added support for Emacs key bindings at the entry editor - Added a formatter for units which keeps the case and adds non-breaking separators - Added a Merge entries functionality - Added a setting to choose either a DOI link or a URL to be standard (feature 710)