From 5e15b84de519037a378892ad966be64354a863af Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Wed, 1 Feb 2017 00:00:04 +0100 Subject: [PATCH 1/4] Add button for adding a new group --- src/main/java/net/sf/jabref/gui/Dialog.java | 7 + .../java/net/sf/jabref/gui/DialogService.java | 8 + .../net/sf/jabref/gui/FXDialogService.java | 5 + .../net/sf/jabref/gui/groups/GroupDialog.java | 77 ++- .../jabref/gui/groups/GroupNodeViewModel.java | 11 +- .../sf/jabref/gui/groups/GroupSelector.java | 534 ++++++++---------- .../net/sf/jabref/gui/groups/GroupTree.css | 18 +- .../net/sf/jabref/gui/groups/GroupTree.fxml | 51 +- .../gui/groups/GroupTreeController.java | 9 +- .../jabref/gui/groups/GroupTreeViewModel.java | 30 +- .../java/net/sf/jabref/model/TreeNode.java | 26 +- .../gui/groups/GroupTreeViewModelTest.java | 6 +- 12 files changed, 423 insertions(+), 359 deletions(-) create mode 100644 src/main/java/net/sf/jabref/gui/Dialog.java diff --git a/src/main/java/net/sf/jabref/gui/Dialog.java b/src/main/java/net/sf/jabref/gui/Dialog.java new file mode 100644 index 00000000000..f0bd4b52cbf --- /dev/null +++ b/src/main/java/net/sf/jabref/gui/Dialog.java @@ -0,0 +1,7 @@ +package net.sf.jabref.gui; + +import java.util.Optional; + +public interface Dialog { + Optional showAndWait(); +} diff --git a/src/main/java/net/sf/jabref/gui/DialogService.java b/src/main/java/net/sf/jabref/gui/DialogService.java index 7409c95dada..2ab07b62ca9 100644 --- a/src/main/java/net/sf/jabref/gui/DialogService.java +++ b/src/main/java/net/sf/jabref/gui/DialogService.java @@ -88,6 +88,14 @@ Optional showCustomButtonDialogAndWait(Alert.AlertType type, String */ Optional showCustomDialogAndWait(String title, DialogPane contentPane, ButtonType... buttonTypes); + /** + * Shows a custom dialog and returns the result. + * + * @param dialog dialog to show + * @param type of result + */ + Optional showCustomDialogAndWait(Dialog dialog); + /** * Notify the user in an non-blocking way (i.e., update status message instead of showing a dialog). * @param message the message to show. diff --git a/src/main/java/net/sf/jabref/gui/FXDialogService.java b/src/main/java/net/sf/jabref/gui/FXDialogService.java index 39c10c71455..79842560165 100644 --- a/src/main/java/net/sf/jabref/gui/FXDialogService.java +++ b/src/main/java/net/sf/jabref/gui/FXDialogService.java @@ -87,6 +87,11 @@ public Optional showCustomDialogAndWait(String title, DialogPane con return alert.showAndWait(); } + @Override + public Optional showCustomDialogAndWait(Dialog dialog) { + return dialog.showAndWait(); + } + @Override public void notify(String message) { JabRefGUI.getMainFrame().output(message); diff --git a/src/main/java/net/sf/jabref/gui/groups/GroupDialog.java b/src/main/java/net/sf/jabref/gui/groups/GroupDialog.java index 29a330580d9..b69b291cf8f 100644 --- a/src/main/java/net/sf/jabref/gui/groups/GroupDialog.java +++ b/src/main/java/net/sf/jabref/gui/groups/GroupDialog.java @@ -7,6 +7,7 @@ import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ItemListener; +import java.util.Optional; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; @@ -28,6 +29,8 @@ import javax.swing.event.CaretListener; import net.sf.jabref.Globals; +import net.sf.jabref.JabRefGUI; +import net.sf.jabref.gui.Dialog; import net.sf.jabref.gui.JabRefFrame; import net.sf.jabref.gui.fieldeditors.TextField; import net.sf.jabref.gui.keyboard.KeyBinding; @@ -51,7 +54,7 @@ * Dialog for creating or modifying groups. Operates directly on the Vector * containing group information. */ -class GroupDialog extends JDialog { +class GroupDialog extends JDialog implements Dialog { private static final int INDEX_EXPLICIT_GROUP = 0; private static final int INDEX_KEYWORD_GROUP = 1; @@ -94,13 +97,10 @@ public Dimension getPreferredSize() { return d; } }; - + private final CardLayout optionsLayout = new CardLayout(); private boolean isOkPressed; - private AbstractGroup resultingGroup; - private final CardLayout optionsLayout = new CardLayout(); - /** * Shows a group add/edit dialog. * @@ -340,6 +340,36 @@ public void actionPerformed(ActionEvent e) { } } + public GroupDialog() { + this(JabRefGUI.getMainFrame(), null); + } + + private static String formatRegExException(String regExp, Exception e) { + String[] sa = e.getMessage().split("\\n"); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < sa.length; ++i) { + if (i > 0) { + sb.append("
"); + } + sb.append(StringUtil.quoteForHTML(sa[i])); + } + String s = Localization.lang( + "The regular expression %0 is invalid:", + StringUtil.quoteForHTML(regExp)) + + "

" + + sb + + ""; + if (!(e instanceof PatternSyntaxException)) { + return s; + } + int lastNewline = s.lastIndexOf("
"); + int hat = s.lastIndexOf('^'); + if ((lastNewline >= 0) && (hat >= 0) && (hat > lastNewline)) { + return s.substring(0, lastNewline + 4) + s.substring(lastNewline + 4).replace(" ", " "); + } + return s; + } + public boolean okPressed() { return isOkPressed; } @@ -433,32 +463,6 @@ private void setDescription(String description) { descriptionLabel.setText("" + description + ""); } - private static String formatRegExException(String regExp, Exception e) { - String[] sa = e.getMessage().split("\\n"); - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < sa.length; ++i) { - if (i > 0) { - sb.append("
"); - } - sb.append(StringUtil.quoteForHTML(sa[i])); - } - String s = Localization.lang( - "The regular expression %0 is invalid:", - StringUtil.quoteForHTML(regExp)) - + "

" - + sb - + ""; - if (!(e instanceof PatternSyntaxException)) { - return s; - } - int lastNewline = s.lastIndexOf("
"); - int hat = s.lastIndexOf('^'); - if ((lastNewline >= 0) && (hat >= 0) && (hat > lastNewline)) { - return s.substring(0, lastNewline + 4) + s.substring(lastNewline + 4).replace(" ", " "); - } - return s; - } - /** * Sets the font of the name entry field. */ @@ -495,4 +499,15 @@ private void setContext(GroupHierarchyType context) { independentButton.setSelected(true); } } + + @Override + public Optional showAndWait() { + this.setVisible(true); + if (this.okPressed()) { + AbstractGroup newGroup = getResultingGroup(); + return Optional.of(newGroup); + } else { + return Optional.empty(); + } + } } diff --git a/src/main/java/net/sf/jabref/gui/groups/GroupNodeViewModel.java b/src/main/java/net/sf/jabref/gui/groups/GroupNodeViewModel.java index c0cc7f7e894..a3c2fa23c92 100644 --- a/src/main/java/net/sf/jabref/gui/groups/GroupNodeViewModel.java +++ b/src/main/java/net/sf/jabref/gui/groups/GroupNodeViewModel.java @@ -1,11 +1,9 @@ package net.sf.jabref.gui.groups; import java.util.Objects; -import java.util.stream.Collectors; import javafx.application.Platform; import javafx.beans.property.SimpleIntegerProperty; -import javafx.collections.FXCollections; import javafx.collections.ObservableList; import net.sf.jabref.logic.l10n.Localization; @@ -16,6 +14,7 @@ import net.sf.jabref.model.groups.GroupTreeNode; import com.google.common.eventbus.Subscribe; +import org.fxmisc.easybind.EasyBind; public class GroupNodeViewModel { @@ -23,7 +22,7 @@ public class GroupNodeViewModel { private final boolean isRoot; private final String iconCode; private final boolean isLeaf; - private final ObservableList children = FXCollections.observableArrayList(); + private final ObservableList children; private final BibDatabaseContext databaseContext; private final GroupTreeNode groupNode; private final SimpleIntegerProperty hits; @@ -36,7 +35,7 @@ public GroupNodeViewModel(BibDatabaseContext databaseContext, GroupTreeNode grou isRoot = groupNode.isRoot(); iconCode = ""; isLeaf = groupNode.isLeaf(); - children.addAll(groupNode.getChildren().stream().map(child -> new GroupNodeViewModel(databaseContext, child)).collect(Collectors.toList())); + children = EasyBind.map(groupNode.getChildren(), child -> new GroupNodeViewModel(databaseContext, child)); hits = new SimpleIntegerProperty(0); calculateNumberOfMatches(); @@ -145,4 +144,8 @@ private void calculateNumberOfMatches() { Platform.runLater(() -> hits.setValue(newHits)); }).start(); } + + public void addSubgroup(AbstractGroup subgroup) { + groupNode.addSubgroup(subgroup); + } } diff --git a/src/main/java/net/sf/jabref/gui/groups/GroupSelector.java b/src/main/java/net/sf/jabref/gui/groups/GroupSelector.java index 4e5c077f6fa..d83fc1728ff 100644 --- a/src/main/java/net/sf/jabref/gui/groups/GroupSelector.java +++ b/src/main/java/net/sf/jabref/gui/groups/GroupSelector.java @@ -2,7 +2,6 @@ import java.awt.BorderLayout; import java.awt.Color; -import java.awt.Dimension; import java.awt.Font; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; @@ -87,11 +86,9 @@ public class GroupSelector extends SidePaneComponent implements TreeSelectionListener { private static final Log LOGGER = LogFactory.getLog(GroupSelector.class); - - private final GroupsTree groupsTree; - private DefaultTreeModel groupsTreeModel; - private GroupTreeNodeViewModel groupsRoot; + private static final String MOVE_ONE_GROUP = Localization.lang("Please select exactly one group to move."); protected final JabRefFrame frame; + private final GroupsTree groupsTree; private final JPopupMenu groupsContextMenu = new JPopupMenu(); private final JPopupMenu settings = new JPopupMenu(); private final JRadioButtonMenuItem hideNonHits; @@ -105,13 +102,8 @@ public class GroupSelector extends SidePaneComponent implements TreeSelectionLis Localization.lang("Automatically assign new entry to selected groups")); private final JCheckBoxMenuItem editModeCb = new JCheckBoxMenuItem(Localization.lang("Edit group membership"), false); - private boolean editModeIndicator; - - private static final String MOVE_ONE_GROUP = Localization.lang("Please select exactly one group to move."); - private final JMenu moveSubmenu = new JMenu(Localization.lang("Move")); private final JMenu sortSubmenu = new JMenu(Localization.lang("Sort alphabetically")); - private final AbstractAction editGroupAction = new EditGroupAction(); private final NodeAction editGroupPopupAction = new EditGroupAction(); private final NodeAction addGroupPopupAction = new AddGroupAction(); @@ -130,8 +122,10 @@ public class GroupSelector extends SidePaneComponent implements TreeSelectionLis private final AddToGroupAction addToGroup = new AddToGroupAction(false); private final AddToGroupAction moveToGroup = new AddToGroupAction(true); private final RemoveFromGroupAction removeFromGroup = new RemoveFromGroupAction(); - private final ToggleAction toggleAction; + private DefaultTreeModel groupsTreeModel; + private GroupTreeNodeViewModel groupsRoot; + private boolean editModeIndicator; /** @@ -223,34 +217,13 @@ public void stateChanged(ChangeEvent event) { editModeCb.addActionListener(e -> setEditMode(editModeCb.getState())); - JButton newButton = new JButton(IconTheme.JabRefIcon.ADD_NOBOX.getSmallIcon()); - int butSize = newButton.getIcon().getIconHeight() + 5; - Dimension butDim = new Dimension(butSize, butSize); - newButton.setPreferredSize(butDim); - newButton.setMinimumSize(butDim); JButton helpButton = new HelpAction(Localization.lang("Help on groups"), HelpFile.GROUP) .getHelpButton(); - helpButton.setPreferredSize(butDim); - helpButton.setMinimumSize(butDim); JButton autoGroup = new JButton(IconTheme.JabRefIcon.AUTO_GROUP.getSmallIcon()); - autoGroup.setPreferredSize(butDim); - autoGroup.setMinimumSize(butDim); - openSettings.setPreferredSize(butDim); - openSettings.setMinimumSize(butDim); Insets butIns = new Insets(0, 0, 0, 0); helpButton.setMargin(butIns); openSettings.setMargin(butIns); - newButton.addActionListener(e -> { - GroupDialog gd = new GroupDialog(frame, null); - gd.setVisible(true); - if (gd.okPressed()) { - AbstractGroup newGroup = gd.getResultingGroup(); - groupsRoot.addNewGroup(newGroup, panel.getUndoManager()); - panel.markBaseChanged(); - frame.output(Localization.lang("Created group \"%0\".", newGroup.getName())); - } - }); andCb.addActionListener(e -> valueChanged(null)); orCb.addActionListener(e -> valueChanged(null)); invCb.addActionListener(e -> valueChanged(null)); @@ -266,7 +239,6 @@ public void stateChanged(ChangeEvent event) { highlCb.addActionListener(e -> valueChanged(null)); hideNonHits.addActionListener(e -> valueChanged(null)); grayOut.addActionListener(e -> valueChanged(null)); - newButton.setToolTipText(Localization.lang("New group")); andCb.setToolTipText(Localization.lang("Display only entries belonging to all selected groups.")); orCb.setToolTipText(Localization.lang("Display all entries belonging to one or more of the selected groups.")); autoGroup.setToolTipText(Localization.lang("Automatically create groups for database.")); @@ -295,8 +267,6 @@ public void stateChanged(ChangeEvent event) { con.gridy = 0; con.gridx = 0; - gbl.setConstraints(newButton, con); - rootPanel.add(newButton); con.gridx = 1; gbl.setConstraints(autoGroup, con); @@ -622,47 +592,6 @@ private GroupTreeNodeViewModel getFirstSelectedNode() { return null; } - class GroupingWorker extends AbstractWorker { - - private final SearchMatcher matcher; - private final List matches = new ArrayList<>(); - private final boolean showOverlappingGroupsP; - - public GroupingWorker(SearchMatcher matcher) { - this.matcher = matcher; - showOverlappingGroupsP = showOverlappingGroups.isSelected(); - } - - @Override - public void run() { - for (BibEntry entry : panel.getDatabase().getEntries()) { - boolean hit = matcher.isMatch(entry); - entry.setGroupHit(hit); - if (hit && showOverlappingGroupsP) { - matches.add(entry); - } - } - } - - @Override - public void update() { - // Show the result in the chosen way: - if (hideNonHits.isSelected()) { - panel.getMainTable().getTableModel().updateGroupingState(MainTableDataModel.DisplayOption.FILTER); - } else if (grayOut.isSelected()) { - panel.getMainTable().getTableModel().updateGroupingState(MainTableDataModel.DisplayOption.FLOAT); - } - panel.getMainTable().getTableModel().updateSortOrder(); - panel.getMainTable().getTableModel().updateGroupFilter(); - panel.getMainTable().scrollTo(0); - - if (showOverlappingGroupsP) { - showOverlappingGroups(matches); - } - frame.output(Localization.lang("Updated group selection") + "."); - } - } - /** * Revalidate the groups tree (e.g. after the data stored in the model has been changed) and maintain the current * selection and expansion state. @@ -767,6 +696,250 @@ public void addGroups(GroupTreeNode newGroups, CompoundEdit ce) { ce.addEdit(undo); } + public TreePath getSelectionPath() { + return groupsTree.getSelectionPath(); + } + + /** + * @param node The node to move + * @return true if move was successful, false if not. + */ + public boolean moveNodeUp(GroupTreeNodeViewModel node, boolean checkSingleSelection) { + if (checkSingleSelection && (groupsTree.getSelectionCount() != 1)) { + frame.output(MOVE_ONE_GROUP); + return false; // not possible + } + Optional moveChange; + if (!node.canMoveUp() || (!(moveChange = node.moveUp()).isPresent())) { + frame.output(Localization.lang("Cannot move group \"%0\" up.", node.getNode().getGroup().getName())); + return false; // not possible + } + // update selection/expansion state (not really needed when + // moving among siblings, but I'm paranoid) + revalidateGroups(groupsTree.refreshPaths(groupsTree.getSelectionPaths()), + groupsTree.refreshPaths(getExpandedPaths())); + concludeMoveGroup(moveChange.get(), node); + return true; + } + + /** + * @param node The node to move + * @return true if move was successful, false if not. + */ + public boolean moveNodeDown(GroupTreeNodeViewModel node, boolean checkSingleSelection) { + if (checkSingleSelection && (groupsTree.getSelectionCount() != 1)) { + frame.output(MOVE_ONE_GROUP); + return false; // not possible + } + Optional moveChange; + if (!node.canMoveDown() || (!(moveChange = node.moveDown()).isPresent())) { + frame.output(Localization.lang("Cannot move group \"%0\" down.", node.getNode().getGroup().getName())); + return false; // not possible + } + // update selection/expansion state (not really needed when + // moving among siblings, but I'm paranoid) + revalidateGroups(groupsTree.refreshPaths(groupsTree.getSelectionPaths()), + groupsTree.refreshPaths(getExpandedPaths())); + concludeMoveGroup(moveChange.get(), node); + return true; + } + + /** + * @param node The node to move + * @return true if move was successful, false if not. + */ + public boolean moveNodeLeft(GroupTreeNodeViewModel node, boolean checkSingleSelection) { + if (checkSingleSelection && (groupsTree.getSelectionCount() != 1)) { + frame.output(MOVE_ONE_GROUP); + return false; // not possible + } + Optional moveChange; + if (!node.canMoveLeft() || (!(moveChange = node.moveLeft()).isPresent())) { + frame.output(Localization.lang("Cannot move group \"%0\" left.", node.getNode().getGroup().getName())); + return false; // not possible + } + // update selection/expansion state + revalidateGroups(groupsTree.refreshPaths(groupsTree.getSelectionPaths()), + groupsTree.refreshPaths(getExpandedPaths())); + concludeMoveGroup(moveChange.get(), node); + return true; + } + + /** + * @param node The node to move + * @return true if move was successful, false if not. + */ + public boolean moveNodeRight(GroupTreeNodeViewModel node, boolean checkSingleSelection) { + if (checkSingleSelection && (groupsTree.getSelectionCount() != 1)) { + frame.output(MOVE_ONE_GROUP); + return false; // not possible + } + Optional moveChange; + if (!node.canMoveRight() || (!(moveChange = node.moveRight()).isPresent())) { + frame.output(Localization.lang("Cannot move group \"%0\" right.", node.getNode().getGroup().getName())); + return false; // not possible + } + // update selection/expansion state + revalidateGroups(groupsTree.refreshPaths(groupsTree.getSelectionPaths()), + groupsTree.refreshPaths(getExpandedPaths())); + concludeMoveGroup(moveChange.get(), node); + return true; + } + + /** + * Concludes the moving of a group tree node by storing the specified undo information, marking the change, and + * setting the status line. + * + * @param moveChange Undo information for the move operation. + * @param node The node that has been moved. + */ + public void concludeMoveGroup(MoveGroupChange moveChange, GroupTreeNodeViewModel node) { + panel.getUndoManager().addEdit(new UndoableMoveGroup(this.groupsRoot, moveChange)); + panel.markBaseChanged(); + frame.output(Localization.lang("Moved group \"%0\".", node.getNode().getGroup().getName())); + } + + public void concludeAssignment(AbstractUndoableEdit undo, GroupTreeNode node, int assignedEntries) { + if (undo == null) { + frame.output(Localization.lang("The group \"%0\" already contains the selection.", + node.getGroup().getName())); + return; + } + panel.getUndoManager().addEdit(undo); + panel.markBaseChanged(); + panel.updateEntryEditorIfShowing(); + final String groupName = node.getGroup().getName(); + if (assignedEntries == 1) { + frame.output(Localization.lang("Assigned 1 entry to group \"%0\".", groupName)); + } else { + frame.output(Localization.lang("Assigned %0 entries to group \"%1\".", String.valueOf(assignedEntries), + groupName)); + } + } + + private GroupTreeNodeViewModel getGroupTreeRoot() { + return groupsRoot; + } + + public Enumeration getExpandedPaths() { + return groupsTree.getExpandedDescendants(groupsRoot.getTreePath()); + } + + /** + * panel may be null to indicate that no file is currently open. + */ + @Override + public void setActiveBasePanel(BasePanel panel) { + super.setActiveBasePanel(panel); + if (panel == null) { // hide groups + frame.getSidePaneManager().hide(GroupSelector.class); + return; + } + MetaData metaData = panel.getBibDatabaseContext().getMetaData(); + if (metaData.getGroups().isPresent()) { + setGroups(metaData.getGroups().get()); + } else { + GroupTreeNode newGroupsRoot = GroupTreeNode + .fromGroup(new AllEntriesGroup(Localization.lang("All entries"))); + metaData.setGroups(newGroupsRoot); + setGroups(newGroupsRoot); + } + + metaData.registerListener(this); + + synchronized (getTreeLock()) { + validateTree(); + } + } + + /** + * Highlight all groups that contain any/all of the specified entries. If entries is null or has zero length, + * highlight is cleared. + */ + public void showMatchingGroups(List list, boolean requireAll) { + if ((list == null) || (list.isEmpty())) { // nothing selected + groupsTree.setMatchingGroups(Collections.emptyList()); + groupsTree.revalidate(); + return; + } + List nodeList = groupsRoot.getNode().getContainingGroups(list, requireAll); + groupsTree.setMatchingGroups(nodeList); + // ensure that all highlighted nodes are visible + for (GroupTreeNode node : nodeList) { + node.getParent().ifPresent( + parentNode -> groupsTree.expandPath(new GroupTreeNodeViewModel(parentNode).getTreePath())); + } + groupsTree.revalidate(); + } + + /** + * Show groups that, if selected, would show at least one of the entries in the specified list. + */ + private void showOverlappingGroups(List matches) { + List nodes = groupsRoot.getNode().getMatchingGroups(matches); + groupsTree.setOverlappingGroups(nodes); + } + + public GroupsTree getGroupsTree() { + return this.groupsTree; + } + + @Subscribe + public void listen(GroupUpdatedEvent updateEvent) { + setGroups(updateEvent.getMetaData().getGroups().orElse(null)); + } + + @Override + public void grabFocus() { + groupsTree.grabFocus(); + } + + @Override + public ToggleAction getToggleAction() { + return toggleAction; + } + + class GroupingWorker extends AbstractWorker { + + private final SearchMatcher matcher; + private final List matches = new ArrayList<>(); + private final boolean showOverlappingGroupsP; + + public GroupingWorker(SearchMatcher matcher) { + this.matcher = matcher; + showOverlappingGroupsP = showOverlappingGroups.isSelected(); + } + + @Override + public void run() { + for (BibEntry entry : panel.getDatabase().getEntries()) { + boolean hit = matcher.isMatch(entry); + entry.setGroupHit(hit); + if (hit && showOverlappingGroupsP) { + matches.add(entry); + } + } + } + + @Override + public void update() { + // Show the result in the chosen way: + if (hideNonHits.isSelected()) { + panel.getMainTable().getTableModel().updateGroupingState(MainTableDataModel.DisplayOption.FILTER); + } else if (grayOut.isSelected()) { + panel.getMainTable().getTableModel().updateGroupingState(MainTableDataModel.DisplayOption.FLOAT); + } + panel.getMainTable().getTableModel().updateSortOrder(); + panel.getMainTable().getTableModel().updateGroupFilter(); + panel.getMainTable().scrollTo(0); + + if (showOverlappingGroupsP) { + showOverlappingGroups(matches); + } + frame.output(Localization.lang("Updated group selection") + "."); + } + } + private abstract class NodeAction extends AbstractAction { private GroupTreeNodeViewModel node; @@ -976,12 +1149,6 @@ public void actionPerformed(ActionEvent e) { } } - - public TreePath getSelectionPath() { - return groupsTree.getSelectionPath(); - } - - private class SortDirectSubgroupsAction extends NodeAction { public SortDirectSubgroupsAction() { @@ -1096,207 +1263,4 @@ public void actionPerformed(ActionEvent e) { } } - - /** - * @param node The node to move - * @return true if move was successful, false if not. - */ - public boolean moveNodeUp(GroupTreeNodeViewModel node, boolean checkSingleSelection) { - if (checkSingleSelection && (groupsTree.getSelectionCount() != 1)) { - frame.output(MOVE_ONE_GROUP); - return false; // not possible - } - Optional moveChange; - if (!node.canMoveUp() || (! (moveChange = node.moveUp()).isPresent())) { - frame.output(Localization.lang("Cannot move group \"%0\" up.", node.getNode().getGroup().getName())); - return false; // not possible - } - // update selection/expansion state (not really needed when - // moving among siblings, but I'm paranoid) - revalidateGroups(groupsTree.refreshPaths(groupsTree.getSelectionPaths()), - groupsTree.refreshPaths(getExpandedPaths())); - concludeMoveGroup(moveChange.get(), node); - return true; - } - - /** - * @param node The node to move - * @return true if move was successful, false if not. - */ - public boolean moveNodeDown(GroupTreeNodeViewModel node, boolean checkSingleSelection) { - if (checkSingleSelection && (groupsTree.getSelectionCount() != 1)) { - frame.output(MOVE_ONE_GROUP); - return false; // not possible - } - Optional moveChange; - if (!node.canMoveDown() || (! (moveChange = node.moveDown()).isPresent())) { - frame.output(Localization.lang("Cannot move group \"%0\" down.", node.getNode().getGroup().getName())); - return false; // not possible - } - // update selection/expansion state (not really needed when - // moving among siblings, but I'm paranoid) - revalidateGroups(groupsTree.refreshPaths(groupsTree.getSelectionPaths()), - groupsTree.refreshPaths(getExpandedPaths())); - concludeMoveGroup(moveChange.get(), node); - return true; - } - - /** - * @param node The node to move - * @return true if move was successful, false if not. - */ - public boolean moveNodeLeft(GroupTreeNodeViewModel node, boolean checkSingleSelection) { - if (checkSingleSelection && (groupsTree.getSelectionCount() != 1)) { - frame.output(MOVE_ONE_GROUP); - return false; // not possible - } - Optional moveChange; - if (!node.canMoveLeft() || (! (moveChange = node.moveLeft()).isPresent())) { - frame.output(Localization.lang("Cannot move group \"%0\" left.", node.getNode().getGroup().getName())); - return false; // not possible - } - // update selection/expansion state - revalidateGroups(groupsTree.refreshPaths(groupsTree.getSelectionPaths()), - groupsTree.refreshPaths(getExpandedPaths())); - concludeMoveGroup(moveChange.get(), node); - return true; - } - - /** - * @param node The node to move - * @return true if move was successful, false if not. - */ - public boolean moveNodeRight(GroupTreeNodeViewModel node, boolean checkSingleSelection) { - if (checkSingleSelection && (groupsTree.getSelectionCount() != 1)) { - frame.output(MOVE_ONE_GROUP); - return false; // not possible - } - Optional moveChange; - if (!node.canMoveRight() || (! (moveChange = node.moveRight()).isPresent())) { - frame.output(Localization.lang("Cannot move group \"%0\" right.", node.getNode().getGroup().getName())); - return false; // not possible - } - // update selection/expansion state - revalidateGroups(groupsTree.refreshPaths(groupsTree.getSelectionPaths()), - groupsTree.refreshPaths(getExpandedPaths())); - concludeMoveGroup(moveChange.get(), node); - return true; - } - - /** - * Concludes the moving of a group tree node by storing the specified undo information, marking the change, and - * setting the status line. - * - * @param moveChange Undo information for the move operation. - * @param node The node that has been moved. - */ - public void concludeMoveGroup(MoveGroupChange moveChange, GroupTreeNodeViewModel node) { - panel.getUndoManager().addEdit(new UndoableMoveGroup(this.groupsRoot, moveChange)); - panel.markBaseChanged(); - frame.output(Localization.lang("Moved group \"%0\".", node.getNode().getGroup().getName())); - } - - public void concludeAssignment(AbstractUndoableEdit undo, GroupTreeNode node, int assignedEntries) { - if (undo == null) { - frame.output(Localization.lang("The group \"%0\" already contains the selection.", - node.getGroup().getName())); - return; - } - panel.getUndoManager().addEdit(undo); - panel.markBaseChanged(); - panel.updateEntryEditorIfShowing(); - final String groupName = node.getGroup().getName(); - if (assignedEntries == 1) { - frame.output(Localization.lang("Assigned 1 entry to group \"%0\".", groupName)); - } else { - frame.output(Localization.lang("Assigned %0 entries to group \"%1\".", String.valueOf(assignedEntries), - groupName)); - } - } - - - - private GroupTreeNodeViewModel getGroupTreeRoot() { - return groupsRoot; - } - - public Enumeration getExpandedPaths() { - return groupsTree.getExpandedDescendants(groupsRoot.getTreePath()); - } - - /** - * panel may be null to indicate that no file is currently open. - */ - @Override - public void setActiveBasePanel(BasePanel panel) { - super.setActiveBasePanel(panel); - if (panel == null) { // hide groups - frame.getSidePaneManager().hide(GroupSelector.class); - return; - } - MetaData metaData = panel.getBibDatabaseContext().getMetaData(); - if (metaData.getGroups().isPresent()) { - setGroups(metaData.getGroups().get()); - } else { - GroupTreeNode newGroupsRoot = GroupTreeNode - .fromGroup(new AllEntriesGroup(Localization.lang("All entries"))); - metaData.setGroups(newGroupsRoot); - setGroups(newGroupsRoot); - } - - metaData.registerListener(this); - - synchronized (getTreeLock()) { - validateTree(); - } - - } - - /** - * Highlight all groups that contain any/all of the specified entries. If entries is null or has zero length, - * highlight is cleared. - */ - public void showMatchingGroups(List list, boolean requireAll) { - if ((list == null) || (list.isEmpty())) { // nothing selected - groupsTree.setMatchingGroups(Collections.emptyList()); - groupsTree.revalidate(); - return; - } - List nodeList = groupsRoot.getNode().getContainingGroups(list, requireAll); - groupsTree.setMatchingGroups(nodeList); - // ensure that all highlighted nodes are visible - for (GroupTreeNode node : nodeList) { - node.getParent().ifPresent( - parentNode -> groupsTree.expandPath(new GroupTreeNodeViewModel(parentNode).getTreePath())); - } - groupsTree.revalidate(); - } - - /** - * Show groups that, if selected, would show at least one of the entries in the specified list. - */ - private void showOverlappingGroups(List matches) { - List nodes = groupsRoot.getNode().getMatchingGroups(matches); - groupsTree.setOverlappingGroups(nodes); - } - - public GroupsTree getGroupsTree() { - return this.groupsTree; - } - - @Subscribe - public void listen(GroupUpdatedEvent updateEvent) { - setGroups(updateEvent.getMetaData().getGroups().orElse(null)); - } - - @Override - public void grabFocus() { - groupsTree.grabFocus(); - } - - @Override - public ToggleAction getToggleAction() { - return toggleAction; - } - } diff --git a/src/main/java/net/sf/jabref/gui/groups/GroupTree.css b/src/main/java/net/sf/jabref/gui/groups/GroupTree.css index a934e49348f..a33853d4a2b 100644 --- a/src/main/java/net/sf/jabref/gui/groups/GroupTree.css +++ b/src/main/java/net/sf/jabref/gui/groups/GroupTree.css @@ -46,11 +46,11 @@ .tree-table-row-cell:root > .numberColumn { -fx-padding: 0.40em 0.2em 0.40em 0em; } + .tree-table-row-cell:root > .disclosureNodeColumn { -fx-padding: 0.45em 0.2em 0.45em 0.2em; } - .tree-table-row-cell:hover { -fx-background-color: lightgrey; } @@ -99,3 +99,19 @@ -fx-translate-x: -5px; } +#buttonBarBottom { + -fx-background-color: #dadad8; + -fx-border-color: dimgray; + -fx-border-width: 1 0 0 0; +} + +.flatButton { + -fx-shadow-highlight-color: transparent; + -fx-outer-border: transparent; + -fx-inner-border: transparent; + -fx-focus-color: #6A9FCD; + -fx-faint-focus-color: transparent; + -fx-background-color: transparent; + -fx-text-background-color: dimgray; + -fx-padding: 0.5em; +} diff --git a/src/main/java/net/sf/jabref/gui/groups/GroupTree.fxml b/src/main/java/net/sf/jabref/gui/groups/GroupTree.fxml index 36db69a08ab..97a2504ab78 100644 --- a/src/main/java/net/sf/jabref/gui/groups/GroupTree.fxml +++ b/src/main/java/net/sf/jabref/gui/groups/GroupTree.fxml @@ -1,18 +1,41 @@ + + + - - - - - - - - - - - - - - + + + +

+ + + + + + + + + + +
+ + + + + + + + diff --git a/src/main/java/net/sf/jabref/gui/groups/GroupTreeController.java b/src/main/java/net/sf/jabref/gui/groups/GroupTreeController.java index 774f588e096..e4c3a47b36c 100644 --- a/src/main/java/net/sf/jabref/gui/groups/GroupTreeController.java +++ b/src/main/java/net/sf/jabref/gui/groups/GroupTreeController.java @@ -3,6 +3,7 @@ import javax.inject.Inject; import javafx.css.PseudoClass; +import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.scene.control.Control; import javafx.scene.control.SelectionModel; @@ -14,6 +15,7 @@ import javafx.scene.text.Text; import net.sf.jabref.gui.AbstractController; +import net.sf.jabref.gui.DialogService; import net.sf.jabref.gui.StateManager; import net.sf.jabref.gui.util.RecursiveTreeItem; import net.sf.jabref.gui.util.ViewModelTreeTableCellFactory; @@ -28,10 +30,11 @@ public class GroupTreeController extends AbstractController @FXML private TreeTableColumn disclosureNodeColumn; @Inject private StateManager stateManager; + @Inject private DialogService dialogService; @FXML public void initialize() { - viewModel = new GroupTreeViewModel(stateManager); + viewModel = new GroupTreeViewModel(stateManager, dialogService); // Set-up bindings groupTree.rootProperty().bind( @@ -103,4 +106,8 @@ public void initialize() { return row; }); } + + public void addNewGroup(ActionEvent actionEvent) { + viewModel.addNewGroupToRoot(); + } } diff --git a/src/main/java/net/sf/jabref/gui/groups/GroupTreeViewModel.java b/src/main/java/net/sf/jabref/gui/groups/GroupTreeViewModel.java index dd3d218df11..55fb8382fb3 100644 --- a/src/main/java/net/sf/jabref/gui/groups/GroupTreeViewModel.java +++ b/src/main/java/net/sf/jabref/gui/groups/GroupTreeViewModel.java @@ -7,8 +7,10 @@ import javafx.beans.property.SimpleObjectProperty; import net.sf.jabref.gui.AbstractViewModel; +import net.sf.jabref.gui.DialogService; import net.sf.jabref.gui.StateManager; import net.sf.jabref.model.database.BibDatabaseContext; +import net.sf.jabref.model.groups.AbstractGroup; import net.sf.jabref.model.metadata.MetaData; public class GroupTreeViewModel extends AbstractViewModel { @@ -16,18 +18,12 @@ public class GroupTreeViewModel extends AbstractViewModel { private final ObjectProperty rootGroup = new SimpleObjectProperty<>(); private final ObjectProperty selectedGroup = new SimpleObjectProperty<>(); private final StateManager stateManager; + private final DialogService dialogService; private Optional currentDatabase; - public ObjectProperty rootGroupProperty() { - return rootGroup; - } - - public ObjectProperty selectedGroupProperty() { - return selectedGroup; - } - - public GroupTreeViewModel(StateManager stateManager) { + public GroupTreeViewModel(StateManager stateManager, DialogService dialogService) { this.stateManager = Objects.requireNonNull(stateManager); + this.dialogService = Objects.requireNonNull(dialogService); // Init onActiveDatabaseChanged(stateManager.activeDatabaseProperty().getValue()); @@ -37,6 +33,14 @@ public GroupTreeViewModel(StateManager stateManager) { selectedGroup.addListener((observable, oldValue, newValue) -> onSelectedGroupChanged(newValue)); } + public ObjectProperty rootGroupProperty() { + return rootGroup; + } + + public ObjectProperty selectedGroupProperty() { + return selectedGroup; + } + /** * Gets invoked if the user selects a different group. * We need to notify the {@link StateManager} about this change so that the main table gets updated. @@ -62,4 +66,12 @@ private void onActiveDatabaseChanged(Optional newDatabase) { rootGroup.setValue(newRoot); } } + + /** + * Opens "New Group Dialog" and add the resulting group to the root + */ + public void addNewGroupToRoot() { + Optional newGroup = dialogService.showCustomDialogAndWait(new GroupDialog()); + newGroup.ifPresent(group -> rootGroup.get().addSubgroup(group)); + } } diff --git a/src/main/java/net/sf/jabref/model/TreeNode.java b/src/main/java/net/sf/jabref/model/TreeNode.java index fbff81022ee..21217acaf5d 100644 --- a/src/main/java/net/sf/jabref/model/TreeNode.java +++ b/src/main/java/net/sf/jabref/model/TreeNode.java @@ -1,13 +1,15 @@ package net.sf.jabref.model; import java.util.ArrayList; -import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.function.Consumer; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; + /** * Represents a node in a tree. *

@@ -29,14 +31,20 @@ // We use some explicit casts of the form "(T) this". The constructor ensures that this cast is valid. @SuppressWarnings("unchecked") public abstract class TreeNode> { + /** + * Array of children, may be empty if this node has no children (but never null) + */ + private final ObservableList children; /** * This node's parent, or null if this node has no parent */ private T parent; /** - * Array of children, may be empty if this node has no children (but never null) + * The function which is invoked when something changed in the subtree. */ - private final List children; + private Consumer onDescendantChanged = t -> { + /* Do nothing */ + }; /** * Constructs a tree node without parent and no children. @@ -46,7 +54,7 @@ */ public TreeNode(Class derivingClass) { parent = null; - children = new ArrayList<>(); + children = FXCollections.observableArrayList(); if (!derivingClass.isInstance(this)) { throw new UnsupportedOperationException("The class extending TreeNode has to derive from T"); @@ -400,8 +408,8 @@ public boolean isNodeDescendant(T anotherNode) { * * @return a list of this node's children */ - public List getChildren() { - return Collections.unmodifiableList(children); + public ObservableList getChildren() { + return FXCollections.unmodifiableObservableList(children); } /** @@ -573,12 +581,6 @@ public T copySubtree() { */ public abstract T copyNode(); - /** - * The function which is invoked when something changed in the subtree. - */ - private Consumer onDescendantChanged = t -> { - /* Do nothing */ }; - /** * Adds the given function to the list of subscribers which are notified when something changes in the subtree. * diff --git a/src/test/java/net/sf/jabref/gui/groups/GroupTreeViewModelTest.java b/src/test/java/net/sf/jabref/gui/groups/GroupTreeViewModelTest.java index e26e81b5b6f..5e215d84996 100644 --- a/src/test/java/net/sf/jabref/gui/groups/GroupTreeViewModelTest.java +++ b/src/test/java/net/sf/jabref/gui/groups/GroupTreeViewModelTest.java @@ -2,6 +2,7 @@ import java.util.Optional; +import net.sf.jabref.gui.DialogService; import net.sf.jabref.gui.StateManager; import net.sf.jabref.model.database.BibDatabaseContext; import net.sf.jabref.model.groups.AllEntriesGroup; @@ -10,6 +11,7 @@ import org.junit.Test; import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; public class GroupTreeViewModelTest { StateManager stateManager; @@ -21,7 +23,7 @@ public void setUp() throws Exception { databaseContext = new BibDatabaseContext(); stateManager = new StateManager(); stateManager.activeDatabaseProperty().setValue(Optional.of(databaseContext)); - groupTree = new GroupTreeViewModel(stateManager); + groupTree = new GroupTreeViewModel(stateManager, mock(DialogService.class)); } @Test @@ -29,4 +31,4 @@ public void rootGroupIsAllEntriesByDefault() throws Exception { AllEntriesGroup allEntriesGroup = new AllEntriesGroup("All entries"); assertEquals(new GroupNodeViewModel(databaseContext, allEntriesGroup), groupTree.rootGroupProperty().getValue()); } -} \ No newline at end of file +} From 90876cc66901c0c6d3e42d87a5064f9ca70fdb01 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Wed, 1 Feb 2017 22:14:43 +0100 Subject: [PATCH 2/4] Add context menu for adding a new subgroup --- .../jabref/gui/groups/GroupNodeViewModel.java | 23 ++++++----- .../sf/jabref/gui/groups/GroupSelector.java | 32 --------------- .../gui/groups/GroupTreeController.java | 41 ++++++++++++++----- .../jabref/gui/groups/GroupTreeViewModel.java | 22 +++++++++- 4 files changed, 63 insertions(+), 55 deletions(-) diff --git a/src/main/java/net/sf/jabref/gui/groups/GroupNodeViewModel.java b/src/main/java/net/sf/jabref/gui/groups/GroupNodeViewModel.java index a3c2fa23c92..72f100dae75 100644 --- a/src/main/java/net/sf/jabref/gui/groups/GroupNodeViewModel.java +++ b/src/main/java/net/sf/jabref/gui/groups/GroupNodeViewModel.java @@ -3,6 +3,8 @@ import java.util.Objects; import javafx.application.Platform; +import javafx.beans.binding.Bindings; +import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleIntegerProperty; import javafx.collections.ObservableList; @@ -21,11 +23,11 @@ public class GroupNodeViewModel { private final String name; private final boolean isRoot; private final String iconCode; - private final boolean isLeaf; private final ObservableList children; private final BibDatabaseContext databaseContext; private final GroupTreeNode groupNode; private final SimpleIntegerProperty hits; + private final SimpleBooleanProperty hasChildren; public GroupNodeViewModel(BibDatabaseContext databaseContext, GroupTreeNode groupNode) { this.databaseContext = Objects.requireNonNull(databaseContext); @@ -34,8 +36,9 @@ public GroupNodeViewModel(BibDatabaseContext databaseContext, GroupTreeNode grou name = groupNode.getName(); isRoot = groupNode.isRoot(); iconCode = ""; - isLeaf = groupNode.isLeaf(); children = EasyBind.map(groupNode.getChildren(), child -> new GroupNodeViewModel(databaseContext, child)); + hasChildren = new SimpleBooleanProperty(); + hasChildren.bind(Bindings.isNotEmpty(children)); hits = new SimpleIntegerProperty(0); calculateNumberOfMatches(); @@ -43,6 +46,7 @@ public GroupNodeViewModel(BibDatabaseContext databaseContext, GroupTreeNode grou databaseContext.getDatabase().registerListener(this); } + public GroupNodeViewModel(BibDatabaseContext databaseContext, AbstractGroup group) { this(databaseContext, new GroupTreeNode(group)); } @@ -51,6 +55,10 @@ static GroupNodeViewModel getAllEntriesGroup(BibDatabaseContext newDatabase) { return new GroupNodeViewModel(newDatabase, new AllEntriesGroup(Localization.lang("All entries"))); } + public SimpleBooleanProperty hasChildrenProperty() { + return hasChildren; + } + public String getName() { return name; } @@ -75,7 +83,6 @@ public boolean equals(Object o) { GroupNodeViewModel that = (GroupNodeViewModel) o; if (isRoot != that.isRoot) return false; - if (isLeaf != that.isLeaf) return false; if (!name.equals(that.name)) return false; if (!iconCode.equals(that.iconCode)) return false; if (!children.equals(that.children)) return false; @@ -90,7 +97,6 @@ public String toString() { "name='" + name + '\'' + ", isRoot=" + isRoot + ", iconCode='" + iconCode + '\'' + - ", isLeaf=" + isLeaf + ", children=" + children + ", databaseContext=" + databaseContext + ", groupNode=" + groupNode + @@ -103,7 +109,6 @@ public int hashCode() { int result = name.hashCode(); result = 31 * result + (isRoot ? 1 : 0); result = 31 * result + iconCode.hashCode(); - result = 31 * result + (isLeaf ? 1 : 0); result = 31 * result + children.hashCode(); result = 31 * result + databaseContext.hashCode(); result = 31 * result + groupNode.hashCode(); @@ -115,10 +120,6 @@ public String getIconCode() { return iconCode; } - public boolean isLeaf() { - return isLeaf; - } - public ObservableList getChildren() { return children; } @@ -145,7 +146,7 @@ private void calculateNumberOfMatches() { }).start(); } - public void addSubgroup(AbstractGroup subgroup) { - groupNode.addSubgroup(subgroup); + public GroupTreeNode addSubgroup(AbstractGroup subgroup) { + return groupNode.addSubgroup(subgroup); } } diff --git a/src/main/java/net/sf/jabref/gui/groups/GroupSelector.java b/src/main/java/net/sf/jabref/gui/groups/GroupSelector.java index d83fc1728ff..bbf0a56771e 100644 --- a/src/main/java/net/sf/jabref/gui/groups/GroupSelector.java +++ b/src/main/java/net/sf/jabref/gui/groups/GroupSelector.java @@ -107,7 +107,6 @@ public class GroupSelector extends SidePaneComponent implements TreeSelectionLis private final AbstractAction editGroupAction = new EditGroupAction(); private final NodeAction editGroupPopupAction = new EditGroupAction(); private final NodeAction addGroupPopupAction = new AddGroupAction(); - private final NodeAction addSubgroupPopupAction = new AddSubgroupAction(); private final NodeAction removeGroupAndSubgroupsPopupAction = new RemoveGroupAndSubgroupsAction(); private final NodeAction removeSubgroupsPopupAction = new RemoveSubgroupsAction(); private final NodeAction removeGroupKeepSubgroupsPopupAction = new RemoveGroupKeepSubgroupsAction(); @@ -332,7 +331,6 @@ private void definePopup() { // BasePanel (entryTable.addKeyListener(...)). groupsContextMenu.add(editGroupPopupAction); groupsContextMenu.add(addGroupPopupAction); - groupsContextMenu.add(addSubgroupPopupAction); groupsContextMenu.addSeparator(); groupsContextMenu.add(removeGroupAndSubgroupsPopupAction); groupsContextMenu.add(removeGroupKeepSubgroupsPopupAction); @@ -411,7 +409,6 @@ public void popupMenuCanceled(PopupMenuEvent e) { private void showPopup(MouseEvent e) { final TreePath path = groupsTree.getPathForLocation(e.getPoint().x, e.getPoint().y); addGroupPopupAction.setEnabled(true); - addSubgroupPopupAction.setEnabled(path != null); editGroupPopupAction.setEnabled(path != null); removeGroupAndSubgroupsPopupAction.setEnabled(path != null); removeGroupKeepSubgroupsPopupAction.setEnabled(path != null); @@ -426,7 +423,6 @@ private void showPopup(MouseEvent e) { if (path != null) { // some path dependent enabling/disabling GroupTreeNodeViewModel node = (GroupTreeNodeViewModel) path.getLastPathComponent(); editGroupPopupAction.setNode(node); - addSubgroupPopupAction.setNode(node); removeGroupAndSubgroupsPopupAction.setNode(node); removeSubgroupsPopupAction.setNode(node); removeGroupKeepSubgroupsPopupAction.setNode(node); @@ -483,7 +479,6 @@ private void showPopup(MouseEvent e) { } else { editGroupPopupAction.setNode(null); addGroupPopupAction.setNode(null); - addSubgroupPopupAction.setNode(null); removeGroupAndSubgroupsPopupAction.setNode(null); removeSubgroupsPopupAction.setNode(null); removeGroupKeepSubgroupsPopupAction.setNode(null); @@ -1045,33 +1040,6 @@ public void actionPerformed(ActionEvent e) { } } - private class AddSubgroupAction extends NodeAction { - - public AddSubgroupAction() { - super(Localization.lang("Add subgroup")); - } - - @Override - public void actionPerformed(ActionEvent e) { - final GroupDialog gd = new GroupDialog(frame, null); - gd.setVisible(true); - if (!gd.okPressed()) { - return; // ignore - } - final AbstractGroup newGroup = gd.getResultingGroup(); - final GroupTreeNode newNode = GroupTreeNode.fromGroup(newGroup); - final GroupTreeNodeViewModel node = getNodeToUse(); - node.getNode().addChild(newNode); - UndoableAddOrRemoveGroup undo = new UndoableAddOrRemoveGroup(groupsRoot, - new GroupTreeNodeViewModel(newNode), UndoableAddOrRemoveGroup.ADD_NODE); - groupsTree.expandPath(node.getTreePath()); - // Store undo information. - panel.getUndoManager().addEdit(undo); - panel.markBaseChanged(); - frame.output(Localization.lang("Added group \"%0\".", newGroup.getName())); - } - } - private class RemoveGroupAndSubgroupsAction extends NodeAction { public RemoveGroupAndSubgroupsAction() { diff --git a/src/main/java/net/sf/jabref/gui/groups/GroupTreeController.java b/src/main/java/net/sf/jabref/gui/groups/GroupTreeController.java index e4c3a47b36c..890c14c896a 100644 --- a/src/main/java/net/sf/jabref/gui/groups/GroupTreeController.java +++ b/src/main/java/net/sf/jabref/gui/groups/GroupTreeController.java @@ -5,7 +5,9 @@ import javafx.css.PseudoClass; import javafx.event.ActionEvent; import javafx.fxml.FXML; +import javafx.scene.control.ContextMenu; import javafx.scene.control.Control; +import javafx.scene.control.MenuItem; import javafx.scene.control.SelectionModel; import javafx.scene.control.TreeItem; import javafx.scene.control.TreeTableColumn; @@ -19,6 +21,7 @@ import net.sf.jabref.gui.StateManager; import net.sf.jabref.gui.util.RecursiveTreeItem; import net.sf.jabref.gui.util.ViewModelTreeTableCellFactory; +import net.sf.jabref.logic.l10n.Localization; import org.fxmisc.easybind.EasyBind; @@ -73,17 +76,14 @@ public void initialize() { disclosureNodeColumn.setCellValueFactory(cellData -> cellData.getValue().valueProperty()); disclosureNodeColumn.setCellFactory(new ViewModelTreeTableCellFactory() .withGraphic(viewModel -> { - if (viewModel.isLeaf()) { - return null; - } else { - final StackPane disclosureNode = new StackPane(); - disclosureNode.getStyleClass().setAll("tree-disclosure-node"); - - final StackPane disclosureNodeArrow = new StackPane(); - disclosureNodeArrow.getStyleClass().setAll("arrow"); - disclosureNode.getChildren().add(disclosureNodeArrow); - return disclosureNode; - } + final StackPane disclosureNode = new StackPane(); + disclosureNode.visibleProperty().bind(viewModel.hasChildrenProperty()); + disclosureNode.getStyleClass().setAll("tree-disclosure-node"); + + final StackPane disclosureNodeArrow = new StackPane(); + disclosureNodeArrow.getStyleClass().setAll("arrow"); + disclosureNode.getChildren().add(disclosureNodeArrow); + return disclosureNode; })); // Set pseudo-classes to indicate if row is root or sub-item ( > 1 deep) @@ -103,10 +103,29 @@ public void initialize() { // Simply setting to null is not enough since it would be replaced by the default node on every change row.setDisclosureNode(null); row.disclosureNodeProperty().addListener((observable, oldValue, newValue) -> row.setDisclosureNode(null)); + + // Add context menu (only for non-null items) + row.contextMenuProperty().bind( + EasyBind.monadic(row.itemProperty()) + .map(this::createContextMenuForGroup) + .orElse((ContextMenu) null) + ); + + return row; }); } + private ContextMenu createContextMenuForGroup(GroupNodeViewModel group) { + ContextMenu menu = new ContextMenu(); + + MenuItem addSubgroup = new MenuItem(Localization.lang("Add subgroup")); + addSubgroup.setOnAction(event -> viewModel.addNewSubgroup(group)); + + menu.getItems().add(addSubgroup); + return menu; + } + public void addNewGroup(ActionEvent actionEvent) { viewModel.addNewGroupToRoot(); } diff --git a/src/main/java/net/sf/jabref/gui/groups/GroupTreeViewModel.java b/src/main/java/net/sf/jabref/gui/groups/GroupTreeViewModel.java index 55fb8382fb3..44c6915d53b 100644 --- a/src/main/java/net/sf/jabref/gui/groups/GroupTreeViewModel.java +++ b/src/main/java/net/sf/jabref/gui/groups/GroupTreeViewModel.java @@ -9,8 +9,10 @@ import net.sf.jabref.gui.AbstractViewModel; import net.sf.jabref.gui.DialogService; import net.sf.jabref.gui.StateManager; +import net.sf.jabref.logic.l10n.Localization; import net.sf.jabref.model.database.BibDatabaseContext; import net.sf.jabref.model.groups.AbstractGroup; +import net.sf.jabref.model.groups.GroupTreeNode; import net.sf.jabref.model.metadata.MetaData; public class GroupTreeViewModel extends AbstractViewModel { @@ -71,7 +73,25 @@ private void onActiveDatabaseChanged(Optional newDatabase) { * Opens "New Group Dialog" and add the resulting group to the root */ public void addNewGroupToRoot() { + addNewSubgroup(rootGroup.get()); + } + + /** + * Opens "New Group Dialog" and add the resulting group to the specified group + */ + public void addNewSubgroup(GroupNodeViewModel parent) { Optional newGroup = dialogService.showCustomDialogAndWait(new GroupDialog()); - newGroup.ifPresent(group -> rootGroup.get().addSubgroup(group)); + newGroup.ifPresent(group -> { + GroupTreeNode newGroupNode = parent.addSubgroup(group); + + // TODO: Add undo + //UndoableAddOrRemoveGroup undo = new UndoableAddOrRemoveGroup(parent, new GroupTreeNodeViewModel(newGroupNode), UndoableAddOrRemoveGroup.ADD_NODE); + //panel.getUndoManager().addEdit(undo); + + // TODO: Expand parent to make new group visible + //parent.expand(); + + dialogService.notify(Localization.lang("Added group \"%0\".", group.getName())); + }); } } From 0515174143dbb4c9608a567752145f9b7c27bf21 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Thu, 2 Feb 2017 20:29:17 +0100 Subject: [PATCH 3/4] Update localization --- src/main/resources/l10n/JabRef_da.properties | 1 - src/main/resources/l10n/JabRef_de.properties | 1 - src/main/resources/l10n/JabRef_en.properties | 1 - src/main/resources/l10n/JabRef_es.properties | 1 - src/main/resources/l10n/JabRef_fa.properties | 1 - src/main/resources/l10n/JabRef_fr.properties | 1 - src/main/resources/l10n/JabRef_in.properties | 1 - src/main/resources/l10n/JabRef_it.properties | 1 - src/main/resources/l10n/JabRef_ja.properties | 1 - src/main/resources/l10n/JabRef_nl.properties | 1 - src/main/resources/l10n/JabRef_no.properties | 1 - src/main/resources/l10n/JabRef_pt_BR.properties | 1 - src/main/resources/l10n/JabRef_ru.properties | 1 - src/main/resources/l10n/JabRef_sv.properties | 1 - src/main/resources/l10n/JabRef_tr.properties | 1 - src/main/resources/l10n/JabRef_vi.properties | 1 - 16 files changed, 16 deletions(-) diff --git a/src/main/resources/l10n/JabRef_da.properties b/src/main/resources/l10n/JabRef_da.properties index 1b0c61cac93..ef6fa454e37 100644 --- a/src/main/resources/l10n/JabRef_da.properties +++ b/src/main/resources/l10n/JabRef_da.properties @@ -1709,7 +1709,6 @@ Could_not_connect_to_running_OpenOffice/LibreOffice.= Make_sure_you_have_installed_OpenOffice/LibreOffice_with_Java_support.= If_connecting_manually,_please_verify_program_and_library_paths.= Error_message\:= -Created_group_"%0".= If_a_pasted_or_imported_entry_already_has_the_field_set,_overwrite.= Import_metadata_from_PDF= Not_connected_to_any_Writer_document._Please_make_sure_a_document_is_open,_and_use_the_'Select_Writer_document'_button_to_connect_to_it.= diff --git a/src/main/resources/l10n/JabRef_de.properties b/src/main/resources/l10n/JabRef_de.properties index e045ed8037f..609c9bef2d8 100644 --- a/src/main/resources/l10n/JabRef_de.properties +++ b/src/main/resources/l10n/JabRef_de.properties @@ -1709,7 +1709,6 @@ Could_not_connect_to_running_OpenOffice/LibreOffice.=Verbindung_zu_OpenOffice/Li Make_sure_you_have_installed_OpenOffice/LibreOffice_with_Java_support.=Vergewissern_Sie_sich,_dass_Sie_OpenOffice/LibreOffice_mit_Java-Unterstützung_installiert_haben. If_connecting_manually,_please_verify_program_and_library_paths.=Bei_manueller_Verbindung_überprüfen_Sie_bitte_die_Programm-_und_Library-Pfade. Error_message\:=Fehlermeldung\: -Created_group_"%0".=Gruppe_"%0"_wurde_erstellt. If_a_pasted_or_imported_entry_already_has_the_field_set,_overwrite.=Falls_bei_einem_eingefügten_oder_importierten_Eintrag_das_Feld_bereits_belegt_ist,_überschreiben. Import_metadata_from_PDF=Metadaten_aus_PDF_importieren Not_connected_to_any_Writer_document._Please_make_sure_a_document_is_open,_and_use_the_'Select_Writer_document'_button_to_connect_to_it.=Keine_Verbindung_zu_einem_Writer-Dokument._Bitte_vergewissern_Sie_sich,_dass_ein_Dokument_geöffnet_ist,_und_benutzen_Sie_die_Schaltfläche_'Writer-Dokument_wählen',_um_eine_Verbindung_herzustellen. diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index 0b46322fce1..98096a5c097 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -1709,7 +1709,6 @@ Could_not_connect_to_running_OpenOffice/LibreOffice.=Could_not_connect_to_runnin Make_sure_you_have_installed_OpenOffice/LibreOffice_with_Java_support.=Make_sure_you_have_installed_OpenOffice/LibreOffice_with_Java_support. If_connecting_manually,_please_verify_program_and_library_paths.=If_connecting_manually,_please_verify_program_and_library_paths. Error_message\:=Error_message\: -Created_group_"%0".=Created_group_"%0". If_a_pasted_or_imported_entry_already_has_the_field_set,_overwrite.=If_a_pasted_or_imported_entry_already_has_the_field_set,_overwrite. Import_metadata_from_PDF=Import_metadata_from_PDF Not_connected_to_any_Writer_document._Please_make_sure_a_document_is_open,_and_use_the_'Select_Writer_document'_button_to_connect_to_it.=Not_connected_to_any_Writer_document._Please_make_sure_a_document_is_open,_and_use_the_'Select_Writer_document'_button_to_connect_to_it. diff --git a/src/main/resources/l10n/JabRef_es.properties b/src/main/resources/l10n/JabRef_es.properties index 53d1b98495c..c7469ad9ac7 100644 --- a/src/main/resources/l10n/JabRef_es.properties +++ b/src/main/resources/l10n/JabRef_es.properties @@ -1709,7 +1709,6 @@ Could_not_connect_to_running_OpenOffice/LibreOffice.=No_se_puede_conectar_a_un_O Make_sure_you_have_installed_OpenOffice/LibreOffice_with_Java_support.=Asegúrese_de_que_tiene_OpenOffice/LibreOffice_instalado_con_soporte_para_Java. If_connecting_manually,_please_verify_program_and_library_paths.=lf__connecting_manually,_por_favor_verifique_las_ruta_de_programa_y_librerías. Error_message\:=Mensaje_de_error\: -Created_group_"%0".=Creado_grupo_"%0". If_a_pasted_or_imported_entry_already_has_the_field_set,_overwrite.=Si_una_entrada_importada_o_pegada_ya_tiene_el_campo_establecido,_sobreescribir. Import_metadata_from_PDF=Importar_metadatos_desde_PDF Not_connected_to_any_Writer_document._Please_make_sure_a_document_is_open,_and_use_the_'Select_Writer_document'_button_to_connect_to_it.=No_conectado_a_ningún_documento_Writer._Asegúrese_de_que_un_documento_está_abierto_y_use_el_botón_"Seleccionar_documento_Writer'_para_conectar_con_él. diff --git a/src/main/resources/l10n/JabRef_fa.properties b/src/main/resources/l10n/JabRef_fa.properties index f2b63055374..a2ccd7db1ea 100644 --- a/src/main/resources/l10n/JabRef_fa.properties +++ b/src/main/resources/l10n/JabRef_fa.properties @@ -1709,7 +1709,6 @@ Could_not_connect_to_running_OpenOffice/LibreOffice.= Make_sure_you_have_installed_OpenOffice/LibreOffice_with_Java_support.= If_connecting_manually,_please_verify_program_and_library_paths.= Error_message\:= -Created_group_"%0".= If_a_pasted_or_imported_entry_already_has_the_field_set,_overwrite.= Import_metadata_from_PDF= Not_connected_to_any_Writer_document._Please_make_sure_a_document_is_open,_and_use_the_'Select_Writer_document'_button_to_connect_to_it.= diff --git a/src/main/resources/l10n/JabRef_fr.properties b/src/main/resources/l10n/JabRef_fr.properties index ea364df7e73..114a1ff622b 100644 --- a/src/main/resources/l10n/JabRef_fr.properties +++ b/src/main/resources/l10n/JabRef_fr.properties @@ -1709,7 +1709,6 @@ Could_not_connect_to_running_OpenOffice/LibreOffice.=La_connexion_à_OpenOffice/ Make_sure_you_have_installed_OpenOffice/LibreOffice_with_Java_support.=Assurez-vous_qu'OpenOffice/LibreOffice_est_installé_avec_le_support_Java. If_connecting_manually,_please_verify_program_and_library_paths.=En_cas_de_connexion_manuelle,_vérifiez_les_chemins_du_programme_et_de_la_bibliothèque. Error_message\:=Message_d'erreur\: -Created_group_"%0".=Groupe_"%0"_créé. If_a_pasted_or_imported_entry_already_has_the_field_set,_overwrite.=Si_une_entrée_collée_ou_importée_a_le_champ_déjà_paramétré,_écraser. Import_metadata_from_PDF=Importer_les_méta-données_à_partir_du_PDF Not_connected_to_any_Writer_document._Please_make_sure_a_document_is_open,_and_use_the_'Select_Writer_document'_button_to_connect_to_it.=Pas_de_connexion_à_un_document_Writer._S'il_vous_plait,_assurez-vous_qu'un_document_est_ouvert_et_utiliser_le_bouton_'Sélectionner_un_document_Writer'_pour_vous_y_connecter. diff --git a/src/main/resources/l10n/JabRef_in.properties b/src/main/resources/l10n/JabRef_in.properties index 71b75ee0d37..ea949e3fa73 100644 --- a/src/main/resources/l10n/JabRef_in.properties +++ b/src/main/resources/l10n/JabRef_in.properties @@ -1709,7 +1709,6 @@ Could_not_connect_to_running_OpenOffice/LibreOffice.=Tidak_bisa_menyambung_denga Make_sure_you_have_installed_OpenOffice/LibreOffice_with_Java_support.=Pastikan_OpenOffice/LibreOffice_diinstal_dengan_dukungan_Java. If_connecting_manually,_please_verify_program_and_library_paths.=Kalau_menyambung_secara_manual,_pastikan_lokasi_program_dan_pustaka. Error_message\:=Pesan_kesalahan\: -Created_group_"%0".=Grup_"%0"_dibuat. If_a_pasted_or_imported_entry_already_has_the_field_set,_overwrite.= Import_metadata_from_PDF=Impor_metadata_dari_PDF Not_connected_to_any_Writer_document._Please_make_sure_a_document_is_open,_and_use_the_'Select_Writer_document'_button_to_connect_to_it.=Tidak_tersambung_dengan_dokumen_Writer._Pastikan_salah_satu_dokumen_terbuka_dan_gunakan_tombol_'Select_Writer_document'_untuk_menyambung_dengan_dokumen_itu. diff --git a/src/main/resources/l10n/JabRef_it.properties b/src/main/resources/l10n/JabRef_it.properties index 8739e7fdd52..7680dfb4f31 100644 --- a/src/main/resources/l10n/JabRef_it.properties +++ b/src/main/resources/l10n/JabRef_it.properties @@ -1709,7 +1709,6 @@ Could_not_connect_to_running_OpenOffice/LibreOffice.=Impossibile_la_connessione_ Make_sure_you_have_installed_OpenOffice/LibreOffice_with_Java_support.=Assicurarsi_che_OpenOffice/LibreOffice_sia_installato_con_supporto_per_Java. If_connecting_manually,_please_verify_program_and_library_paths.=Se_si_effettua_la_connessione_manualmente_verificare_i_percorsi_al_programma_e_alla_libreria. Error_message\:=Messaggio_di_errore\: -Created_group_"%0".=Creato_il_gruppo_"%0". If_a_pasted_or_imported_entry_already_has_the_field_set,_overwrite.=Se_la_voce_incollata_o_importata_ha_il_campo_già_impostato,_sovrascrivere. Import_metadata_from_PDF=Importa_metadati_dal_file_PDF Not_connected_to_any_Writer_document._Please_make_sure_a_document_is_open,_and_use_the_'Select_Writer_document'_button_to_connect_to_it.=Non_connesso_ad_alcun_documento_Writer._Assicurarsi_che_un_documento_sia_aperto_e_connetterlo_con_il_bottone_"Selezionare_il_documento_Writer". diff --git a/src/main/resources/l10n/JabRef_ja.properties b/src/main/resources/l10n/JabRef_ja.properties index 1ccbe3e3da9..aa34edec208 100644 --- a/src/main/resources/l10n/JabRef_ja.properties +++ b/src/main/resources/l10n/JabRef_ja.properties @@ -1709,7 +1709,6 @@ Could_not_connect_to_running_OpenOffice/LibreOffice.=実行中のOpenOffice/Libr Make_sure_you_have_installed_OpenOffice/LibreOffice_with_Java_support.=OpenOffice/LibreOfficeがJavaサポートとともに導入されていることを確認してください。 If_connecting_manually,_please_verify_program_and_library_paths.=手動で接続しようとしている場合には、プログラムとライブラリのパスを確認してください。 Error_message\:=エラーメッセージは以下の通り\: -Created_group_"%0".=グループ「%0」を作成しました。 If_a_pasted_or_imported_entry_already_has_the_field_set,_overwrite.=既にフィールドセットのある項目を貼り付けたり読み込んだりした場合には、上書きする。 Import_metadata_from_PDF=PDFからMetadataを読み込む Not_connected_to_any_Writer_document._Please_make_sure_a_document_is_open,_and_use_the_'Select_Writer_document'_button_to_connect_to_it.=どのWriter文書にも接続されていません。文書が開かれているか確認して、「Writer文書を選択」ボタンを押して接続してください。 diff --git a/src/main/resources/l10n/JabRef_nl.properties b/src/main/resources/l10n/JabRef_nl.properties index 20819acf310..caa03b15dcd 100644 --- a/src/main/resources/l10n/JabRef_nl.properties +++ b/src/main/resources/l10n/JabRef_nl.properties @@ -1709,7 +1709,6 @@ Could_not_connect_to_running_OpenOffice/LibreOffice.= Make_sure_you_have_installed_OpenOffice/LibreOffice_with_Java_support.= If_connecting_manually,_please_verify_program_and_library_paths.= Error_message\:= -Created_group_"%0".= If_a_pasted_or_imported_entry_already_has_the_field_set,_overwrite.= Import_metadata_from_PDF= Not_connected_to_any_Writer_document._Please_make_sure_a_document_is_open,_and_use_the_'Select_Writer_document'_button_to_connect_to_it.= diff --git a/src/main/resources/l10n/JabRef_no.properties b/src/main/resources/l10n/JabRef_no.properties index 9966fd66858..cf063636867 100644 --- a/src/main/resources/l10n/JabRef_no.properties +++ b/src/main/resources/l10n/JabRef_no.properties @@ -1709,7 +1709,6 @@ Could_not_connect_to_running_OpenOffice/LibreOffice.= Make_sure_you_have_installed_OpenOffice/LibreOffice_with_Java_support.= If_connecting_manually,_please_verify_program_and_library_paths.= Error_message\:= -Created_group_"%0".= If_a_pasted_or_imported_entry_already_has_the_field_set,_overwrite.= Import_metadata_from_PDF= Not_connected_to_any_Writer_document._Please_make_sure_a_document_is_open,_and_use_the_'Select_Writer_document'_button_to_connect_to_it.= diff --git a/src/main/resources/l10n/JabRef_pt_BR.properties b/src/main/resources/l10n/JabRef_pt_BR.properties index a953034003e..e198fba6c1a 100644 --- a/src/main/resources/l10n/JabRef_pt_BR.properties +++ b/src/main/resources/l10n/JabRef_pt_BR.properties @@ -1709,7 +1709,6 @@ Could_not_connect_to_running_OpenOffice/LibreOffice.=Não_foi_possível_conectar Make_sure_you_have_installed_OpenOffice/LibreOffice_with_Java_support.=Assegure-se_que_o_OpenOffice/LibreOffice_está_instalado_com_suporte_Java. If_connecting_manually,_please_verify_program_and_library_paths.=Se_estiver_conectando_manualmente,_por_favor_verifique_o_caminho_do_programa_e_da_biblioteca. Error_message\:=Mensagem_de_erro\: -Created_group_"%0".=Grupo_criado_"%0". If_a_pasted_or_imported_entry_already_has_the_field_set,_overwrite.=Se_a_referência_colada_ou_importada_já_possuir_o_campo_definido,_sobrescrever. Import_metadata_from_PDF=Importar_Metadados_do_PDF Not_connected_to_any_Writer_document._Please_make_sure_a_document_is_open,_and_use_the_'Select_Writer_document'_button_to_connect_to_it.=Não_conectado_a_nenhum_documento_do_Writer._Por_favor_assegure-se_de_que_um_documento_está_aberto,_e_use_o_botão_'Selecionar_documento_do_Writer'_para_conectar_a_ele. diff --git a/src/main/resources/l10n/JabRef_ru.properties b/src/main/resources/l10n/JabRef_ru.properties index 3e0a9a539fe..a3e3f33b8fc 100644 --- a/src/main/resources/l10n/JabRef_ru.properties +++ b/src/main/resources/l10n/JabRef_ru.properties @@ -1709,7 +1709,6 @@ Could_not_connect_to_running_OpenOffice/LibreOffice.=Не_удалось_под Make_sure_you_have_installed_OpenOffice/LibreOffice_with_Java_support.=Убедитесь,_что_установлен_OpenOffice/LibreOffice_с_поддержкой_Java. If_connecting_manually,_please_verify_program_and_library_paths.=При_подключении_вручную,_проверьте_правильность_путей_для_приложения_и_библиотеки. Error_message\:=Сообщение_об_ошибке\: -Created_group_"%0".=Создана_группа_"%0". If_a_pasted_or_imported_entry_already_has_the_field_set,_overwrite.=Перезаписать,_если_поле_для_вставленной_или_импортированной_записи_уже_задано. Import_metadata_from_PDF=Импорт_метаданных_из_PDF Not_connected_to_any_Writer_document._Please_make_sure_a_document_is_open,_and_use_the_'Select_Writer_document'_button_to_connect_to_it.=Отсутствует_подключение_к_документу_Writer._Убедитесь,_что_документ_открыт_и_нажмите_кнопку_'Выбрать_документ_Writer'_для_подключения. diff --git a/src/main/resources/l10n/JabRef_sv.properties b/src/main/resources/l10n/JabRef_sv.properties index b46fbd8db6b..beb07ea6bd1 100644 --- a/src/main/resources/l10n/JabRef_sv.properties +++ b/src/main/resources/l10n/JabRef_sv.properties @@ -1709,7 +1709,6 @@ Could_not_connect_to_running_OpenOffice/LibreOffice.=Kunde_inte_ansluta_till_Ope Make_sure_you_have_installed_OpenOffice/LibreOffice_with_Java_support.=Kontrollera_att_du_har_installerat_OpenOffice/LibreOffice_med_Java-stöd. If_connecting_manually,_please_verify_program_and_library_paths.=Om_du_ansluter_manuellt,_kontrollera_sökvägar_till_program_och_bibliotek. Error_message\:=Felmeddelande\: -Created_group_"%0".=Skapade_grupp_"%0". If_a_pasted_or_imported_entry_already_has_the_field_set,_overwrite.= Import_metadata_from_PDF=Importera_metadata_från_PDF Not_connected_to_any_Writer_document._Please_make_sure_a_document_is_open,_and_use_the_'Select_Writer_document'_button_to_connect_to_it.= diff --git a/src/main/resources/l10n/JabRef_tr.properties b/src/main/resources/l10n/JabRef_tr.properties index f6e83f2b262..37462950893 100644 --- a/src/main/resources/l10n/JabRef_tr.properties +++ b/src/main/resources/l10n/JabRef_tr.properties @@ -1709,7 +1709,6 @@ Could_not_connect_to_running_OpenOffice/LibreOffice.=Çalışan_OpenOffice/Libre Make_sure_you_have_installed_OpenOffice/LibreOffice_with_Java_support.=OpenOffice/LibreOffice'i_Java_desteğiyle_kurduğunuza_emin_olun. If_connecting_manually,_please_verify_program_and_library_paths.=Manuel_bağlanıyorsanız_program_ve_kütüphane_yollarını_kontrol_edin. Error_message\:=Hata_mesajı\: -Created_group_"%0".=Grup_"%0"_oluşturuldu. If_a_pasted_or_imported_entry_already_has_the_field_set,_overwrite.=Eğer_yapıştırılmış_ya_da_içe_aktarılmış_bir_girdi_alanı_atadıysa_üzerine_yaz. Import_metadata_from_PDF=Metadata'yı_PDF'ten_İçe_Aktar Not_connected_to_any_Writer_document._Please_make_sure_a_document_is_open,_and_use_the_'Select_Writer_document'_button_to_connect_to_it.=Herhangi_bir_Writer_belgesine_bağlanılmadı._Lütfen_bir_belgenin_açık_olduğuna_emin_olun_ve_ona_bağlanmak_için_"Write_belgesini_seç"_düğmesini_kullanın. diff --git a/src/main/resources/l10n/JabRef_vi.properties b/src/main/resources/l10n/JabRef_vi.properties index 335840f35c3..b46f9f2804c 100644 --- a/src/main/resources/l10n/JabRef_vi.properties +++ b/src/main/resources/l10n/JabRef_vi.properties @@ -1709,7 +1709,6 @@ Could_not_connect_to_running_OpenOffice/LibreOffice.= Make_sure_you_have_installed_OpenOffice/LibreOffice_with_Java_support.= If_connecting_manually,_please_verify_program_and_library_paths.= Error_message\:= -Created_group_"%0".= If_a_pasted_or_imported_entry_already_has_the_field_set,_overwrite.= Import_metadata_from_PDF= Not_connected_to_any_Writer_document._Please_make_sure_a_document_is_open,_and_use_the_'Select_Writer_document'_button_to_connect_to_it.= From 6b10f2e1b3592625605a6ac5da7736cf1dafee25 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Thu, 2 Feb 2017 20:29:27 +0100 Subject: [PATCH 4/4] Rest of upadet --- src/main/resources/l10n/JabRef_zh.properties | 1 - src/main/resources/l10n/Menu_ru.properties | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/resources/l10n/JabRef_zh.properties b/src/main/resources/l10n/JabRef_zh.properties index 2352c308f06..aeed8f96e44 100644 --- a/src/main/resources/l10n/JabRef_zh.properties +++ b/src/main/resources/l10n/JabRef_zh.properties @@ -1709,7 +1709,6 @@ Could_not_connect_to_running_OpenOffice/LibreOffice.= Make_sure_you_have_installed_OpenOffice/LibreOffice_with_Java_support.= If_connecting_manually,_please_verify_program_and_library_paths.= Error_message\:= -Created_group_"%0".= If_a_pasted_or_imported_entry_already_has_the_field_set,_overwrite.= Import_metadata_from_PDF= Not_connected_to_any_Writer_document._Please_make_sure_a_document_is_open,_and_use_the_'Select_Writer_document'_button_to_connect_to_it.= diff --git a/src/main/resources/l10n/Menu_ru.properties b/src/main/resources/l10n/Menu_ru.properties index 056e95d0d56..a353c95e8ba 100644 --- a/src/main/resources/l10n/Menu_ru.properties +++ b/src/main/resources/l10n/Menu_ru.properties @@ -1,5 +1,5 @@ #! -#! created/edited by wuw (30/12/16) +#! created/edited by Popeye version 0.55 (github.com/JabRef/popeye) #! encoding:UTF-8 Abbreviate_journal_names_(ISO)=Сокращения_названий_журналов_(ISO)