package org.jabref.gui.menus;
import java.awt.event.ActionEvent;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import org.jabref.Globals;
import org.jabref.gui.BasePanel;
import org.jabref.gui.EntryMarker;
import org.jabref.gui.IconTheme;
import org.jabref.gui.JabRefFrame;
import org.jabref.gui.actions.Actions;
import org.jabref.gui.filelist.FileListTableModel;
import org.jabref.gui.keyboard.KeyBinding;
import org.jabref.gui.mergeentries.FetchAndMergeEntry;
import org.jabref.gui.specialfields.SpecialFieldMenuAction;
import org.jabref.gui.specialfields.SpecialFieldValueViewModel;
import org.jabref.gui.specialfields.SpecialFieldViewModel;
import org.jabref.gui.worker.MarkEntriesAction;
import org.jabref.logic.citationstyle.CitationStyle;
import org.jabref.logic.l10n.Localization;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.FieldName;
import org.jabref.model.entry.specialfields.SpecialField;
import org.jabref.model.entry.specialfields.SpecialFieldValue;
import org.jabref.preferences.JabRefPreferences;
import org.jabref.preferences.PreviewPreferences;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class RightClickMenu extends JPopupMenu implements PopupMenuListener {
private static final Log LOGGER = LogFactory.getLog(RightClickMenu.class);
private final BasePanel panel;
private final JMenuItem groupAdd;
private final JMenuItem groupRemove;
private final JMenuItem groupMoveTo;
public RightClickMenu(JabRefFrame frame, BasePanel panel) {
this.panel = panel;
JMenu typeMenu = new ChangeEntryTypeMenu().getChangeEntryTypeMenu(panel);
// Are multiple entries selected?
boolean multiple = areMultipleEntriesSelected();
// If only one entry is selected, get a reference to it for adapting the menu.
BibEntry be = null;
if (panel.getMainTable().getSelectedRowCount() == 1) {
be = panel.getMainTable().getSelected().get(0);
}
addPopupMenuListener(this);
JMenu copySpecialMenu = new JMenu(Localization.lang("Copy") + "...");
copySpecialMenu.add(new GeneralAction(Actions.COPY_TITLE, Localization.lang("Copy title"), KeyBinding.COPY_TITLE));
copySpecialMenu.add(new GeneralAction(Actions.COPY_KEY, Localization.lang("Copy BibTeX key"), KeyBinding.COPY_BIBTEX_KEY));
copySpecialMenu.add(new GeneralAction(Actions.COPY_CITE_KEY, Localization.lang("Copy \\cite{BibTeX key}"), KeyBinding.COPY_CITE_BIBTEX_KEY));
copySpecialMenu.add(new GeneralAction(Actions.COPY_KEY_AND_TITLE, Localization.lang("Copy BibTeX key and title"), KeyBinding.COPY_BIBTEX_KEY_AND_TITLE));
copySpecialMenu.add(new GeneralAction(Actions.COPY_KEY_AND_LINK, Localization.lang("Copy BibTeX key and link"), KeyBinding.COPY_BIBTEX_KEY_AND_LINK));
// the submenu will behave dependent on what style is currently selected (citation/preview)
PreviewPreferences previewPreferences = Globals.prefs.getPreviewPreferences();
String style = previewPreferences.getPreviewCycle().get(previewPreferences.getPreviewCyclePosition());
if (CitationStyle.isCitationStyleFile(style)) {
copySpecialMenu.add(new GeneralAction(Actions.COPY_CITATION_HTML, Localization.menuTitle("Copy citation") + " (HTML)", KeyBinding.COPY_PREVIEW));
JMenu copyCitationMenu = new JMenu(Localization.menuTitle("Copy citation") + "...");
copyCitationMenu.add(new GeneralAction(Actions.COPY_CITATION_TEXT, "Text"));
copyCitationMenu.add(new GeneralAction(Actions.COPY_CITATION_RTF, "RTF"));
copyCitationMenu.add(new GeneralAction(Actions.COPY_CITATION_ASCII_DOC, "AsciiDoc"));
copyCitationMenu.add(new GeneralAction(Actions.COPY_CITATION_XSLFO, "XSL-FO"));
copySpecialMenu.add(copyCitationMenu);
} else {
copySpecialMenu.add(new GeneralAction(Actions.COPY_CITATION_HTML, Localization.lang("Copy preview"), KeyBinding.COPY_PREVIEW));
}
copySpecialMenu.add(new GeneralAction(Actions.EXPORT_TO_CLIPBOARD, Localization.lang("Export to clipboard"),
IconTheme.JabRefIcon.EXPORT_TO_CLIPBOARD.getSmallIcon()));
add(new GeneralAction(Actions.COPY, Localization.lang("Copy"), IconTheme.JabRefIcon.COPY.getSmallIcon(), KeyBinding.COPY));
add(copySpecialMenu);
add(new GeneralAction(Actions.PASTE, Localization.lang("Paste"), IconTheme.JabRefIcon.PASTE.getSmallIcon(), KeyBinding.PASTE));
add(new GeneralAction(Actions.CUT, Localization.lang("Cut"), IconTheme.JabRefIcon.CUT.getSmallIcon(), KeyBinding.CUT));
add(new GeneralAction(Actions.DELETE, Localization.lang("Delete"), IconTheme.JabRefIcon.DELETE_ENTRY.getSmallIcon(), KeyBinding.DELETE_ENTRY));
GeneralAction printPreviewAction = new GeneralAction(Actions.PRINT_PREVIEW, Localization.lang("Print entry preview"), IconTheme.JabRefIcon.PRINTED.getSmallIcon());
printPreviewAction.setEnabled(!multiple);
add(printPreviewAction);
addSeparator();
add(new GeneralAction(Actions.SEND_AS_EMAIL, Localization.lang("Send as email"), IconTheme.JabRefIcon.EMAIL.getSmallIcon()));
addSeparator();
JMenu markSpecific = JabRefFrame.subMenu(Localization.menuTitle("Mark specific color"));
markSpecific.setIcon(IconTheme.JabRefIcon.MARK_ENTRIES.getSmallIcon());
for (int i = 0; i < EntryMarker.MAX_MARKING_LEVEL; i++) {
markSpecific.add(new MarkEntriesAction(frame, i).getMenuItem());
}
if (multiple) {
add(new GeneralAction(Actions.MARK_ENTRIES, Localization.lang("Mark entries"), IconTheme.JabRefIcon.MARK_ENTRIES.getSmallIcon(), KeyBinding.MARK_ENTRIES));
add(markSpecific);
add(new GeneralAction(Actions.UNMARK_ENTRIES, Localization.lang("Unmark entries"), IconTheme.JabRefIcon.UNMARK_ENTRIES.getSmallIcon(), KeyBinding.UNMARK_ENTRIES));
} else if (be != null) {
Optional<String> marked = be.getField(FieldName.MARKED_INTERNAL);
// We have to check for "" too as the marked field may be empty
if ((!marked.isPresent()) || marked.get().isEmpty()) {
add(new GeneralAction(Actions.MARK_ENTRIES, Localization.lang("Mark entry"), IconTheme.JabRefIcon.MARK_ENTRIES.getSmallIcon(), KeyBinding.MARK_ENTRIES));
add(markSpecific);
} else {
add(markSpecific);
add(new GeneralAction(Actions.UNMARK_ENTRIES, Localization.lang("Unmark entry"), IconTheme.JabRefIcon.UNMARK_ENTRIES.getSmallIcon(), KeyBinding.UNMARK_ENTRIES));
}
}
if (Globals.prefs.getBoolean(JabRefPreferences.SPECIALFIELDSENABLED)) {
if (Globals.prefs.getBoolean(JabRefPreferences.SHOWCOLUMN_RANKING)) {
JMenu rankingMenu = new JMenu();
RightClickMenu.populateSpecialFieldMenu(rankingMenu, SpecialField.RANKING, frame);
add(rankingMenu);
}
// TODO: multiple handling for relevance and quality-assurance
// if multiple values are selected ("if (multiple)"), two options (set / clear) should be offered
// if one value is selected either set or clear should be offered
if (Globals.prefs.getBoolean(JabRefPreferences.SHOWCOLUMN_RELEVANCE)) {
add(new SpecialFieldMenuAction(new SpecialFieldValueViewModel(SpecialField.RELEVANCE.getValues().get(0)), frame));
}
if (Globals.prefs.getBoolean(JabRefPreferences.SHOWCOLUMN_QUALITY)) {
add(new SpecialFieldMenuAction(new SpecialFieldValueViewModel(SpecialField.QUALITY.getValues().get(0)), frame));
}
if (Globals.prefs.getBoolean(JabRefPreferences.SHOWCOLUMN_PRINTED)) {
add(new SpecialFieldMenuAction(new SpecialFieldValueViewModel(SpecialField.PRINTED.getValues().get(0)), frame));
}
if (Globals.prefs.getBoolean(JabRefPreferences.SHOWCOLUMN_PRIORITY)) {
JMenu priorityMenu = new JMenu();
RightClickMenu.populateSpecialFieldMenu(priorityMenu, SpecialField.PRIORITY, frame);
add(priorityMenu);
}
if (Globals.prefs.getBoolean(JabRefPreferences.SHOWCOLUMN_READ)) {
JMenu readStatusMenu = new JMenu();
RightClickMenu.populateSpecialFieldMenu(readStatusMenu, SpecialField.READ_STATUS, frame);
add(readStatusMenu);
}
}
addSeparator();
GeneralAction openFolderAction = new GeneralAction(Actions.OPEN_FOLDER, Localization.lang("Open folder"),
KeyBinding.OPEN_FOLDER);
openFolderAction.setEnabled(isFieldSetForSelectedEntry(FieldName.FILE));
add(openFolderAction);
GeneralAction openFileAction = new GeneralAction(Actions.OPEN_EXTERNAL_FILE, Localization.lang("Open file"),
getFileIconForSelectedEntry(), KeyBinding.OPEN_FILE);
openFileAction.setEnabled(isFieldSetForSelectedEntry(FieldName.FILE));
add(openFileAction);
GeneralAction openUrlAction = new GeneralAction(Actions.OPEN_URL, Localization.lang("Open URL or DOI"),
IconTheme.JabRefIcon.WWW.getSmallIcon(), KeyBinding.OPEN_URL_OR_DOI);
openUrlAction.setEnabled(isFieldSetForSelectedEntry(FieldName.URL) || isFieldSetForSelectedEntry(FieldName.DOI));
add(openUrlAction);
addSeparator();
add(typeMenu);
GeneralAction mergeFetchedEntryAction = new GeneralAction(Actions.MERGE_WITH_FETCHED_ENTRY,
Localization.lang("Get BibTeX data from %0", FetchAndMergeEntry.getDisplayNameOfSupportedFields()));
mergeFetchedEntryAction.setEnabled(isAnyFieldSetForSelectedEntry(FetchAndMergeEntry.SUPPORTED_FIELDS));
add(mergeFetchedEntryAction);
add(frame.getMassSetField());
GeneralAction attachFileAction = new GeneralAction(Actions.ADD_FILE_LINK, Localization.lang("Attach file"),
IconTheme.JabRefIcon.ATTACH_FILE.getSmallIcon());
attachFileAction.setEnabled(!multiple);
add(attachFileAction);
add(frame.getManageKeywords());
GeneralAction mergeEntriesAction = new GeneralAction(Actions.MERGE_ENTRIES,
Localization.lang("Merge entries") + "...", IconTheme.JabRefIcon.MERGE_ENTRIES.getSmallIcon());
mergeEntriesAction.setEnabled(areExactlyTwoEntriesSelected());
add(mergeEntriesAction);
addSeparator(); // for "add/move/remove to/from group" entries (appended here)
groupAdd = new JMenuItem(new GeneralAction(Actions.ADD_TO_GROUP, Localization.lang("Add to group")));
add(groupAdd);
groupRemove = new JMenuItem(new GeneralAction(Actions.REMOVE_FROM_GROUP, Localization.lang("Remove from group")));
add(groupRemove);
groupMoveTo = add(new GeneralAction(Actions.MOVE_TO_GROUP, Localization.lang("Move to group")));
add(groupMoveTo);
// create disabledIcons for all menu entries
frame.createDisabledIconsForMenuEntries(this);
}
private boolean areMultipleEntriesSelected() {
return panel.getMainTable().getSelectedRowCount() > 1;
}
private boolean areExactlyTwoEntriesSelected() {
return panel.getMainTable().getSelectedRowCount() == 2;
}
/**
* Remove all types from the menu.
* Then cycle through all available values, and add them.
*/
public static void populateSpecialFieldMenu(JMenu menu, SpecialField field, JabRefFrame frame) {
SpecialFieldViewModel viewModel = new SpecialFieldViewModel(field);
menu.setText(viewModel.getLocalization());
menu.setIcon(viewModel.getRepresentingIcon());
for (SpecialFieldValue val : field.getValues()) {
menu.add(new SpecialFieldMenuAction(new SpecialFieldValueViewModel(val), frame));
}
}
/**
* Set the dynamic contents of "Add to group ..." submenu.
*/
@Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
panel.storeCurrentEdit();
boolean groupsPresent = panel.getBibDatabaseContext().getMetaData().getGroups().isPresent();
groupAdd.setEnabled(groupsPresent);
groupRemove.setEnabled(groupsPresent);
groupMoveTo.setEnabled(groupsPresent);
}
@Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
// Nothing to do
}
@Override
public void popupMenuCanceled(PopupMenuEvent e) {
// nothing to do
}
private boolean isFieldSetForSelectedEntry(String fieldname) {
return isAnyFieldSetForSelectedEntry(Arrays.asList(fieldname));
}
private boolean isAnyFieldSetForSelectedEntry(List<String> fieldnames) {
if (panel.getMainTable().getSelectedRowCount() == 1) {
BibEntry entry = panel.getMainTable().getSelected().get(0);
return !Collections.disjoint(fieldnames, entry.getFieldNames());
}
return false;
}
private Icon getFileIconForSelectedEntry() {
if (panel.getMainTable().getSelectedRowCount() == 1) {
BibEntry entry = panel.getMainTable().getSelected().get(0);
if (entry.hasField(FieldName.FILE)) {
JLabel label = FileListTableModel.getFirstLabel(entry.getField(FieldName.FILE).get());
if (label != null) {
return label.getIcon();
}
}
}
return IconTheme.JabRefIcon.FILE.getSmallIcon();
}
class GeneralAction extends AbstractAction {
private final String command;
public GeneralAction(String command, String name) {
super(name);
this.command = command;
}
public GeneralAction(String command, String name, Icon icon) {
super(name, icon);
this.command = command;
}
public GeneralAction(String command, String name, KeyBinding key) {
super(name);
this.command = command;
putValue(Action.ACCELERATOR_KEY, Globals.getKeyPrefs().getKey(key));
}
public GeneralAction(String command, String name, Icon icon, KeyBinding key) {
super(name, icon);
this.command = command;
putValue(Action.ACCELERATOR_KEY, Globals.getKeyPrefs().getKey(key));
}
@Override
public void actionPerformed(ActionEvent e) {
try {
panel.runCommand(command);
} catch (Throwable ex) {
LOGGER.debug("Cannot execute command " + command + ".", ex);
}
}
}
}