Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Emacs key bindings #6037

Merged
merged 20 commits into from
Nov 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ to the page field for cases where the page numbers are missing. [#7019](https://
- We added a new fetcher to enable users to search jstor.org [#6627](https://github.com/JabRef/jabref/issues/6627)
- We added an error message in the New Entry dialog that is shown in case the fetcher did not find anything . [#7000](https://github.com/JabRef/jabref/issues/7000)
- We added a new formatter to output shorthand month format. [#6579](https://github.com/JabRef/jabref/issues/6579)
- We reintroduced emacs/bash-like keybindings. [#6017](https://github.com/JabRef/jabref/issues/6017)

### Changed

Expand Down
21 changes: 13 additions & 8 deletions src/main/java/org/jabref/gui/JabRefGUI.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.input.KeyEvent;
import javafx.stage.Screen;
import javafx.stage.Stage;

Expand All @@ -16,6 +17,7 @@
import org.jabref.gui.icon.IconTheme;
import org.jabref.gui.importer.ParserResultWarningDialog;
import org.jabref.gui.importer.actions.OpenDatabaseAction;
import org.jabref.gui.keyboard.TextInputKeyBindings;
import org.jabref.gui.shared.SharedDatabaseUIManager;
import org.jabref.logic.autosaveandbackup.BackupManager;
import org.jabref.logic.importer.OpenDatabase;
Expand Down Expand Up @@ -86,6 +88,10 @@ private void openWindow(Stage mainStage) {

Scene scene = new Scene(root, 800, 800);
Globals.prefs.getTheme().installCss(scene);

// Handle TextEditor key bindings
scene.addEventFilter(KeyEvent.KEY_PRESSED, event -> TextInputKeyBindings.call(scene, event));

mainStage.setTitle(JabRefFrame.FRAME_TITLE);
mainStage.getIcons().addAll(IconTheme.getLogoSetFX());
mainStage.setScene(scene);
Expand Down Expand Up @@ -219,14 +225,13 @@ private void saveWindowState(Stage mainStage) {
*/
private void debugLogWindowState(Stage mainStage) {
if (LOGGER.isDebugEnabled()) {
StringBuilder debugLogString = new StringBuilder();
debugLogString.append("SCREEN DATA:");
debugLogString.append("mainStage.WINDOW_MAXIMISED: ").append(mainStage.isMaximized()).append("\n");
debugLogString.append("mainStage.POS_X: ").append(mainStage.getX()).append("\n");
debugLogString.append("mainStage.POS_Y: ").append(mainStage.getY()).append("\n");
debugLogString.append("mainStage.SIZE_X: ").append(mainStage.getWidth()).append("\n");
debugLogString.append("mainStages.SIZE_Y: ").append(mainStage.getHeight()).append("\n");
LOGGER.debug(debugLogString.toString());
String debugLogString = "SCREEN DATA:" +
"mainStage.WINDOW_MAXIMISED: " + mainStage.isMaximized() + "\n" +
"mainStage.POS_X: " + mainStage.getX() + "\n" +
"mainStage.POS_Y: " + mainStage.getY() + "\n" +
"mainStage.SIZE_X: " + mainStage.getWidth() + "\n" +
"mainStages.SIZE_Y: " + mainStage.getHeight() + "\n";
LOGGER.debug(debugLogString);
}
}

Expand Down
19 changes: 7 additions & 12 deletions src/main/java/org/jabref/gui/entryeditor/SourceTab.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Tooltip;
import javafx.scene.input.InputMethodRequests;
import javafx.scene.input.KeyEvent;

import org.jabref.gui.DialogService;
import org.jabref.gui.Globals;
Expand All @@ -26,6 +27,7 @@
import org.jabref.gui.actions.SimpleCommand;
import org.jabref.gui.actions.StandardActions;
import org.jabref.gui.icon.IconTheme;
import org.jabref.gui.keyboard.CodeAreaKeyBindings;
import org.jabref.gui.keyboard.KeyBindingRepository;
import org.jabref.gui.undo.CountingUndoManager;
import org.jabref.gui.undo.NamedCompound;
Expand Down Expand Up @@ -85,18 +87,10 @@ public EditAction(StandardActions command) {
@Override
public void execute() {
switch (command) {
case COPY:
codeArea.copy();
break;
case CUT:
codeArea.cut();
break;
case PASTE:
codeArea.paste();
break;
case SELECT_ALL:
codeArea.selectAll();
break;
case COPY -> codeArea.copy();
case CUT -> codeArea.cut();
case PASTE -> codeArea.paste();
case SELECT_ALL -> codeArea.selectAll();
}
codeArea.requestFocus();
}
Expand Down Expand Up @@ -178,6 +172,7 @@ private void setupSourceEditor() {
}
});
codeArea.setId("bibtexSourceCodeArea");
codeArea.addEventFilter(KeyEvent.KEY_PRESSED, event -> CodeAreaKeyBindings.call(codeArea, event));

ActionFactory factory = new ActionFactory(keyBindingRepository);
ContextMenu contextMenu = new ContextMenu();
Expand Down
113 changes: 113 additions & 0 deletions src/main/java/org/jabref/gui/keyboard/CodeAreaKeyBindings.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package org.jabref.gui.keyboard;

import javafx.scene.input.KeyEvent;

import org.jabref.gui.Globals;
import org.jabref.logic.util.strings.StringManipulator;
import org.jabref.model.util.ResultingStringState;

import org.fxmisc.richtext.CodeArea;
import org.fxmisc.richtext.NavigationActions;

public class CodeAreaKeyBindings {

public static void call(CodeArea codeArea, KeyEvent event) {
KeyBindingRepository keyBindingRepository = Globals.getKeyPrefs();
keyBindingRepository.mapToKeyBinding(event).ifPresent(binding -> {
switch (binding) {
case EDITOR_DELETE -> {
codeArea.deleteNextChar();
event.consume();
}
case EDITOR_BACKWARD -> {
codeArea.previousChar(NavigationActions.SelectionPolicy.CLEAR);
event.consume();
}
case EDITOR_FORWARD -> {
codeArea.nextChar(NavigationActions.SelectionPolicy.CLEAR);
event.consume();
}
case EDITOR_WORD_BACKWARD -> {
codeArea.wordBreaksBackwards(2, NavigationActions.SelectionPolicy.CLEAR);
event.consume();
}
case EDITOR_WORD_FORWARD -> {
codeArea.wordBreaksForwards(2, NavigationActions.SelectionPolicy.CLEAR);
event.consume();
}
case EDITOR_BEGINNING_DOC -> {
codeArea.start(NavigationActions.SelectionPolicy.CLEAR);
event.consume();
}
case EDITOR_UP -> {
codeArea.paragraphStart(NavigationActions.SelectionPolicy.CLEAR);
event.consume();
}
case EDITOR_BEGINNING -> {
codeArea.lineStart(NavigationActions.SelectionPolicy.CLEAR);
event.consume();
}
case EDITOR_END_DOC -> {
codeArea.end(NavigationActions.SelectionPolicy.CLEAR);
event.consume();
}
case EDITOR_DOWN -> {
codeArea.paragraphEnd(NavigationActions.SelectionPolicy.CLEAR);
event.consume();
}
case EDITOR_END -> {
codeArea.lineEnd(NavigationActions.SelectionPolicy.CLEAR);
event.consume();
}
case EDITOR_CAPITALIZE -> {
int pos = codeArea.getCaretPosition();
String text = codeArea.getText(0, codeArea.getText().length());
ResultingStringState res = StringManipulator.capitalize(pos, text);
codeArea.replaceText(res.text);
codeArea.displaceCaret(res.caretPosition);
event.consume();
}
case EDITOR_LOWERCASE -> {
int pos = codeArea.getCaretPosition();
String text = codeArea.getText(0, codeArea.getText().length());
ResultingStringState res = StringManipulator.lowercase(pos, text);
codeArea.replaceText(res.text);
codeArea.displaceCaret(res.caretPosition);
event.consume();
}
case EDITOR_UPPERCASE -> {
int pos = codeArea.getCaretPosition();
String text = codeArea.getText(0, codeArea.getText().length());
ResultingStringState res = StringManipulator.uppercase(pos, text);
codeArea.clear();
codeArea.replaceText(res.text);
codeArea.displaceCaret(res.caretPosition);
event.consume();
}
case EDITOR_KILL_LINE -> {
int pos = codeArea.getCaretPosition();
codeArea.replaceText(codeArea.getText(0, pos));
codeArea.displaceCaret(pos);
event.consume();
}
case EDITOR_KILL_WORD -> {
int pos = codeArea.getCaretPosition();
String text = codeArea.getText(0, codeArea.getText().length());
ResultingStringState res = StringManipulator.killWord(pos, text);
codeArea.replaceText(res.text);
codeArea.displaceCaret(res.caretPosition);
event.consume();
}
case EDITOR_KILL_WORD_BACKWARD -> {
int pos = codeArea.getCaretPosition();
String text = codeArea.getText(0, codeArea.getText().length());
ResultingStringState res = StringManipulator.backwardKillWord(pos, text);
codeArea.replaceText(res.text);
codeArea.displaceCaret(res.caretPosition);
event.consume();
}
}
});
}
}

18 changes: 18 additions & 0 deletions src/main/java/org/jabref/gui/keyboard/KeyBinding.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,24 @@
import org.jabref.logic.l10n.Localization;

public enum KeyBinding {
EDITOR_DELETE("Delete", Localization.lang("Delete text"), "", KeyBindingCategory.EDITOR),
// DELETE BACKWARDS = Rubout
EDITOR_BACKWARD("Move caret left", Localization.lang("Move caret left"), "", KeyBindingCategory.EDITOR),
EDITOR_FORWARD("Move caret right", Localization.lang("Move caret right"), "", KeyBindingCategory.EDITOR),
EDITOR_WORD_BACKWARD("Move caret to previous word", Localization.lang("Move caret to previous word"), "", KeyBindingCategory.EDITOR),
EDITOR_WORD_FORWARD("Move caret to next word", Localization.lang("Move caret to next word"), "", KeyBindingCategory.EDITOR),
EDITOR_BEGINNING("Move caret to beginning of line", Localization.lang("Move caret to beginning of line"), "", KeyBindingCategory.EDITOR),
EDITOR_END("Move caret to of line", Localization.lang("Move caret to end of line"), "", KeyBindingCategory.EDITOR),
EDITOR_BEGINNING_DOC("Move caret to beginning of text", Localization.lang("Move the caret to the beginning of text"), "", KeyBindingCategory.EDITOR),
EDITOR_END_DOC("Move caret to end of text", Localization.lang("Move the caret to the end of text"), "", KeyBindingCategory.EDITOR),
EDITOR_UP("Move caret up", Localization.lang("Move the caret up"), "", KeyBindingCategory.EDITOR),
EDITOR_DOWN("Move caret down", Localization.lang("Move the caret down"), "", KeyBindingCategory.EDITOR),
EDITOR_CAPITALIZE("Capitalize word", Localization.lang("Capitalize current word"), "", KeyBindingCategory.EDITOR),
EDITOR_LOWERCASE("Lowercase word", Localization.lang("Make current word lowercase"), "", KeyBindingCategory.EDITOR),
EDITOR_UPPERCASE("Uppercase word", Localization.lang("Make current word uppercase"), "", KeyBindingCategory.EDITOR),
EDITOR_KILL_LINE("Remove all characters caret to end of line", Localization.lang("Remove line after caret"), "", KeyBindingCategory.EDITOR),
EDITOR_KILL_WORD("Remove characters until next word", Localization.lang("Remove characters until next word"), "", KeyBindingCategory.EDITOR),
EDITOR_KILL_WORD_BACKWARD("Characters until previous word", Localization.lang("Remove the current word backwards"), "", KeyBindingCategory.EDITOR),

ABBREVIATE("Abbreviate", Localization.lang("Abbreviate journal names"), "ctrl+alt+A", KeyBindingCategory.TOOLS),
AUTOGENERATE_CITATION_KEYS("Autogenerate citation keys", Localization.lang("Autogenerate citation keys"), "ctrl+G", KeyBindingCategory.QUALITY),
Expand Down
5 changes: 3 additions & 2 deletions src/main/java/org/jabref/gui/keyboard/KeyBindingCategory.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ public enum KeyBindingCategory {
VIEW(Localization.lang("View")),
BIBTEX(BibDatabaseMode.BIBTEX.getFormattedName()),
QUALITY(Localization.lang("Quality")),
TOOLS(Localization.lang("Tools"));
TOOLS(Localization.lang("Tools")),
EDITOR(Localization.lang("Text editor"));

private final String name;

private KeyBindingCategory(String name) {
KeyBindingCategory(String name) {
this.name = name;
}

Expand Down
20 changes: 16 additions & 4 deletions src/main/java/org/jabref/gui/keyboard/KeyBindingViewModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,11 @@ public String getBinding() {
private void setBinding(String bind) {
this.realBinding = bind;
String[] parts = bind.split(" ");
String displayBind = "";
StringBuilder displayBind = new StringBuilder();
for (String part : parts) {
displayBind += CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, part) + " ";
displayBind.append(CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, part)).append(" ");
}
this.shownBinding.set(displayBind.trim().replace(" ", " + "));
this.shownBinding.set(displayBind.toString().trim().replace(" ", " + "));
}

private void setDisplayName() {
Expand Down Expand Up @@ -135,7 +135,19 @@ public void resetToDefault() {
}
}

public Optional<JabRefIcon> getIcon() {
public void clear() {
if (!isCategory()) {
String key = getKeyBinding().getConstant();
keyBindingRepository.put(key, "");
setBinding(keyBindingRepository.get(key));
}
}

public Optional<JabRefIcon> getResetIcon() {
return isCategory() ? Optional.empty() : Optional.of(IconTheme.JabRefIcons.REFRESH);
}

public Optional<JabRefIcon> getClearIcon() {
return isCategory() ? Optional.empty() : Optional.of(IconTheme.JabRefIcons.CLEANUP_ENTRIES);
}
}
14 changes: 10 additions & 4 deletions src/main/java/org/jabref/gui/keyboard/KeyBindingsDialog.fxml
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,29 @@

<?import javafx.scene.control.ButtonType?>
<?import javafx.scene.control.DialogPane?>
<?import javafx.scene.control.MenuButton?>
<?import javafx.scene.control.TreeTableColumn?>
<?import javafx.scene.control.TreeTableView?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<DialogPane xmlns:fx="http://javafx.com/fxml/1" minHeight="450.0" minWidth="375.0" prefHeight="450.0" prefWidth="407.0"
xmlns="http://javafx.com/javafx/8.0.65" fx:controller="org.jabref.gui.keyboard.KeyBindingsDialogView">
<content>
<VBox>
<VBox spacing="4">
<TreeTableView fx:id="keyBindingsTable" showRoot="false" styleClass="keybinding-table">
<columns>
<TreeTableColumn fx:id="actionColumn" prefWidth="75.0" text="%Action"/>
<TreeTableColumn fx:id="shortcutColumn" prefWidth="75.0" text="%Shortcut"/>
<TreeTableColumn fx:id="resetColumn" maxWidth="600.0" prefWidth="75.0"/>
<TreeTableColumn fx:id="actionColumn" prefWidth="200.0" text="%Action"/>
<TreeTableColumn fx:id="shortcutColumn" prefWidth="100.0" text="%Shortcut"/>
<TreeTableColumn fx:id="resetColumn" maxWidth="25.0" prefWidth="25.0" minWidth="25.0"/>
<TreeTableColumn fx:id="clearColumn" maxWidth="25.0" prefWidth="25.0" minWidth="25.0"/>
</columns>
<columnResizePolicy>
<TreeTableView fx:constant="CONSTRAINED_RESIZE_POLICY"/>
</columnResizePolicy>
</TreeTableView>
<HBox spacing="4" alignment="CENTER_RIGHT">
<MenuButton fx:id="presetsButton" text="%Presets"/>
</HBox>
</VBox>
</content>
<ButtonType fx:id="resetButton" text="%Reset Bindings" buttonData="LEFT"/>
Expand Down
19 changes: 18 additions & 1 deletion src/main/java/org/jabref/gui/keyboard/KeyBindingsDialogView.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

import javafx.fxml.FXML;
import javafx.scene.control.ButtonType;
import javafx.scene.control.MenuButton;
import javafx.scene.control.MenuItem;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.SelectionModel;
import javafx.scene.control.TreeItem;
Expand All @@ -12,6 +14,7 @@

import org.jabref.gui.DialogService;
import org.jabref.gui.icon.JabRefIcon;
import org.jabref.gui.keyboard.presets.KeyBindingPreset;
import org.jabref.gui.util.BaseDialog;
import org.jabref.gui.util.ControlHelper;
import org.jabref.gui.util.RecursiveTreeItem;
Expand All @@ -30,6 +33,8 @@ public class KeyBindingsDialogView extends BaseDialog<Void> {
@FXML private TreeTableColumn<KeyBindingViewModel, String> actionColumn;
@FXML private TreeTableColumn<KeyBindingViewModel, String> shortcutColumn;
@FXML private TreeTableColumn<KeyBindingViewModel, KeyBindingViewModel> resetColumn;
@FXML private TreeTableColumn<KeyBindingViewModel, KeyBindingViewModel> clearColumn;
@FXML private MenuButton presetsButton;

@Inject private KeyBindingRepository keyBindingRepository;
@Inject private DialogService dialogService;
Expand Down Expand Up @@ -66,9 +71,21 @@ private void initialize() {
actionColumn.setCellValueFactory(cellData -> cellData.getValue().getValue().nameProperty());
shortcutColumn.setCellValueFactory(cellData -> cellData.getValue().getValue().shownBindingProperty());
new ViewModelTreeTableCellFactory<KeyBindingViewModel>()
.withGraphic(keyBinding -> keyBinding.getIcon().map(JabRefIcon::getGraphicNode).orElse(null))
.withGraphic(keyBinding -> keyBinding.getResetIcon().map(JabRefIcon::getGraphicNode).orElse(null))
.withOnMouseClickedEvent(keyBinding -> evt -> keyBinding.resetToDefault())
.install(resetColumn);
new ViewModelTreeTableCellFactory<KeyBindingViewModel>()
.withGraphic(keyBinding -> keyBinding.getClearIcon().map(JabRefIcon::getGraphicNode).orElse(null))
.withOnMouseClickedEvent(keyBinding -> evt -> keyBinding.clear())
.install(clearColumn);

viewModel.keyBindingPresets().forEach(preset -> presetsButton.getItems().add(createMenuItem(preset)));
}

private MenuItem createMenuItem(KeyBindingPreset preset) {
MenuItem item = new MenuItem(preset.getName());
item.setOnAction((event) -> viewModel.loadPreset(preset));
return item;
}

@FXML
Expand Down
Loading