Java tutorial
/* Copyright (C) 2003-2012 JabRef contributors. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.sf.jabref.util; import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashSet; import java.util.TreeSet; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.ActionMap; import javax.swing.BorderFactory; import javax.swing.ButtonGroup; import javax.swing.DefaultListModel; import javax.swing.InputMap; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JList; import javax.swing.JRadioButton; import javax.swing.JScrollPane; import javax.swing.JTextField; import net.sf.jabref.BasePanel; import net.sf.jabref.BibtexEntry; import net.sf.jabref.Globals; import net.sf.jabref.JabRef; import net.sf.jabref.JabRefFrame; import net.sf.jabref.MnemonicAwareAction; import net.sf.jabref.autocompleter.AutoCompleter; import net.sf.jabref.gui.AutoCompleteListener; import net.sf.jabref.specialfields.Printed; import net.sf.jabref.specialfields.Priority; import net.sf.jabref.specialfields.Quality; import net.sf.jabref.specialfields.Rank; import net.sf.jabref.specialfields.ReadStatus; import net.sf.jabref.specialfields.Relevance; import net.sf.jabref.specialfields.SpecialFieldsUtils; import net.sf.jabref.undo.NamedCompound; import com.jgoodies.forms.builder.ButtonBarBuilder; import com.jgoodies.forms.builder.DefaultFormBuilder; import com.jgoodies.forms.layout.FormLayout; /** * An Action for launching mass field. * * Functionality: * * Defaults to selected entries, or all entries if none are selected. * * Input field name * * Either set field, or clear field. */ public class ManageKeywordsAction extends MnemonicAwareAction { private final JabRefFrame frame; private JDialog diag = null; // keyword to add private JTextField keyword; private DefaultListModel keywordListModel; private JList keywordList; private JRadioButton intersectKeywords, mergeKeywords; private boolean cancelled; private final TreeSet<String> sortedKeywordsOfAllEntriesBeforeUpdateByUser = new TreeSet<String>(); public ManageKeywordsAction(JabRefFrame frame) { putValue(Action.NAME, "Manage keywords"); this.frame = frame; } private void createDialog() { keyword = new JTextField(); keywordListModel = new DefaultListModel(); keywordList = new JList(keywordListModel); keywordList.setVisibleRowCount(8); JScrollPane kPane = new JScrollPane(keywordList); diag = new JDialog(frame, Globals.lang("Manage keywords"), true); JButton ok = new JButton(Globals.lang("Ok")); JButton cancel = new JButton(Globals.lang("Cancel")); JButton add = new JButton(Globals.lang("Add")); JButton remove = new JButton(Globals.lang("Remove")); keywordList.setVisibleRowCount(10); intersectKeywords = new JRadioButton("Display keywords appearing in ALL entries"); mergeKeywords = new JRadioButton("Display keywords appearing in ANY entry"); ButtonGroup group = new ButtonGroup(); group.add(intersectKeywords); group.add(mergeKeywords); ActionListener stateChanged = new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { fillKeyWordList(); } }; intersectKeywords.addActionListener(stateChanged); mergeKeywords.addActionListener(stateChanged); intersectKeywords.setSelected(true); DefaultFormBuilder builder = new DefaultFormBuilder( new FormLayout("fill:200dlu, 4dlu, left:pref, 4dlu, left:pref", "")); builder.appendSeparator(Globals.lang("Keywords of selected entries")); builder.append(intersectKeywords, 5); builder.nextLine(); builder.append(mergeKeywords, 5); builder.nextLine(); builder.append(kPane, 3); builder.add(remove); builder.nextLine(); builder.append(keyword, 3); builder.append(add); builder.nextLine(); ButtonBarBuilder bb = new ButtonBarBuilder(); bb.addGlue(); bb.addButton(ok); bb.addButton(cancel); bb.addGlue(); builder.getPanel().setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); bb.getPanel().setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); ok.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { cancelled = false; diag.dispose(); } }); AbstractAction cancelAction = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { cancelled = true; diag.dispose(); } }; cancel.addActionListener(cancelAction); final ActionListener addActionListener = new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { String text = keyword.getText().trim(); if (text.isEmpty()) { // no text to add, do nothing return; } if (keywordListModel.isEmpty()) { keywordListModel.addElement(text); } else { int idx = 0; String element = (String) keywordListModel.getElementAt(idx); while ((idx < keywordListModel.size()) && (element.compareTo(text) < 0)) { idx++; } if (idx == keywordListModel.size()) { // list is empty or word is greater than last word in list keywordListModel.addElement(text); } else if (element.compareTo(text) == 0) { // nothing to do, word already in table } else { keywordListModel.add(idx, text); } } keyword.setText(null); keyword.requestFocusInWindow(); } }; add.addActionListener(addActionListener); final ActionListener removeActionListenter = new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { // keywordList.getSelectedIndices(); does not work, therefore we operate on the values String[] values = (String[]) keywordList.getSelectedValues(); for (String val : values) { keywordListModel.removeElement(val); } } }; remove.addActionListener(removeActionListenter); keywordList.addKeyListener(new KeyListener() { @Override public void keyTyped(KeyEvent arg0) { } @Override public void keyReleased(KeyEvent arg0) { } @Override public void keyPressed(KeyEvent arg0) { if (arg0.getKeyCode() == KeyEvent.VK_DELETE) { removeActionListenter.actionPerformed(null); } } }); AutoCompleter autoComp = JabRef.jrf.basePanel().getAutoCompleters().get("keywords"); AutoCompleteListener acl = new AutoCompleteListener(autoComp); keyword.addKeyListener(acl); keyword.addFocusListener(acl); keyword.addKeyListener(new KeyListener() { @Override public void keyTyped(KeyEvent e) { } @Override public void keyReleased(KeyEvent e) { } @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER) { addActionListener.actionPerformed(null); } } }); // Key bindings: ActionMap am = builder.getPanel().getActionMap(); InputMap im = builder.getPanel().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); im.put(Globals.prefs.getKey("Close dialog"), "close"); am.put("close", cancelAction); diag.getContentPane().add(builder.getPanel(), BorderLayout.CENTER); diag.getContentPane().add(bb.getPanel(), BorderLayout.SOUTH); //diag.pack(); } @Override public void actionPerformed(ActionEvent e) { BasePanel bp = frame.basePanel(); if (bp == null) { return; } if (bp.getSelectedEntries().length == 0) { // no entries selected, silently ignore action return; } // Lazy creation of the dialog: if (diag == null) { createDialog(); } cancelled = true; fillKeyWordList(); diag.pack(); Util.placeDialog(diag, frame); diag.setVisible(true); if (cancelled) { return; } HashSet<String> keywordsToAdd = new HashSet<String>(); HashSet<String> userSelectedKeywords = new HashSet<String>(); // build keywordsToAdd and userSelectedKeywords in parallel for (Enumeration keywords = keywordListModel.elements(); keywords.hasMoreElements();) { String keyword = (String) keywords.nextElement(); userSelectedKeywords.add(keyword); if (!sortedKeywordsOfAllEntriesBeforeUpdateByUser.contains(keyword)) { keywordsToAdd.add(keyword); } } HashSet<String> keywordsToRemove = new HashSet<String>(); for (String keyword : sortedKeywordsOfAllEntriesBeforeUpdateByUser) { if (!userSelectedKeywords.contains(keyword)) { keywordsToRemove.add(keyword); } } if (keywordsToAdd.isEmpty() && keywordsToRemove.isEmpty()) { // nothing to be done if nothing is new and nothing is obsolete return; } if (SpecialFieldsUtils.keywordSyncEnabled()) { if (!keywordsToAdd.isEmpty()) { // we need to check whether a special field is added // for each field: // check if something is added // if yes, add all keywords of that special fields to the keywords to be removed HashSet<String> clone; // Priority clone = createClone(keywordsToAdd); clone.retainAll(Priority.getInstance().getKeyWords()); if (!clone.isEmpty()) { keywordsToRemove.addAll(Priority.getInstance().getKeyWords()); } // Quality clone = createClone(keywordsToAdd); clone.retainAll(Quality.getInstance().getKeyWords()); if (!clone.isEmpty()) { keywordsToRemove.addAll(Quality.getInstance().getKeyWords()); } // Rank clone = createClone(keywordsToAdd); clone.retainAll(Rank.getInstance().getKeyWords()); if (!clone.isEmpty()) { keywordsToRemove.addAll(Rank.getInstance().getKeyWords()); } // Relevance clone = createClone(keywordsToAdd); clone.retainAll(Relevance.getInstance().getKeyWords()); if (!clone.isEmpty()) { keywordsToRemove.addAll(Relevance.getInstance().getKeyWords()); } // Read status clone = createClone(keywordsToAdd); clone.retainAll(ReadStatus.getInstance().getKeyWords()); if (!clone.isEmpty()) { keywordsToRemove.addAll(ReadStatus.getInstance().getKeyWords()); } // Printed clone = createClone(keywordsToAdd); clone.retainAll(Printed.getInstance().getKeyWords()); if (!clone.isEmpty()) { keywordsToRemove.addAll(Printed.getInstance().getKeyWords()); } } } BibtexEntry[] entries = bp.getSelectedEntries(); NamedCompound ce = new NamedCompound(Globals.lang("Update keywords")); for (BibtexEntry entry : entries) { ArrayList<String> separatedKeywords = Util.getSeparatedKeywords(entry); // we "intercept" with a treeset // pro: no duplicates // possible con: alphabetical sorting of the keywords TreeSet<String> keywords = new TreeSet<String>(); keywords.addAll(separatedKeywords); // update keywords keywords.removeAll(keywordsToRemove); keywords.addAll(keywordsToAdd); // put keywords back separatedKeywords.clear(); separatedKeywords.addAll(keywords); Util.putKeywords(entry, separatedKeywords, ce); if (SpecialFieldsUtils.keywordSyncEnabled()) { SpecialFieldsUtils.syncSpecialFieldsFromKeywords(entry, ce); } } ce.end(); bp.undoManager.addEdit(ce); bp.markBaseChanged(); } @SuppressWarnings("unchecked") private HashSet<String> createClone(HashSet<String> keywordsToAdd) { return (HashSet<String>) keywordsToAdd.clone(); } private void fillKeyWordList() { BasePanel bp = frame.basePanel(); BibtexEntry[] entries = bp.getSelectedEntries(); // fill dialog with values keywordListModel.clear(); sortedKeywordsOfAllEntriesBeforeUpdateByUser.clear(); if (mergeKeywords.isSelected()) { for (BibtexEntry entry : entries) { ArrayList<String> separatedKeywords = Util.getSeparatedKeywords(entry); sortedKeywordsOfAllEntriesBeforeUpdateByUser.addAll(separatedKeywords); } } else { assert (intersectKeywords.isSelected()); // all keywords from first entry have to be added BibtexEntry firstEntry = entries[0]; ArrayList<String> separatedKeywords = Util.getSeparatedKeywords(firstEntry); sortedKeywordsOfAllEntriesBeforeUpdateByUser.addAll(separatedKeywords); // for the remaining entries, intersection has to be used // this approach ensures that one empty keyword list leads to an empty set of common keywords for (int i = 1; i < entries.length; i++) { BibtexEntry entry = entries[i]; separatedKeywords = Util.getSeparatedKeywords(entry); sortedKeywordsOfAllEntriesBeforeUpdateByUser.retainAll(separatedKeywords); } } for (String s : sortedKeywordsOfAllEntriesBeforeUpdateByUser) { keywordListModel.addElement(s); } } }