public void moveToGroup(List<BibEntry> entries, NamedCompound undoAll) { List<GroupTreeNode> groupsContainingEntries = node.getNode() .getRoot() .getContainingGroups(entries, false) .stream() .filter(node -> node.getGroup().supportsRemove()) .collect(Collectors.toList()); List<AbstractGroup> affectedGroups = groupsContainingEntries.stream().map(GroupTreeNode::getGroup).collect(Collectors.toList()); affectedGroups.add(node.getNode().getGroup()); if (!WarnAssignmentSideEffects.warnAssignmentSideEffects(affectedGroups, panel.frame())) { return; // user aborted operation } // first remove for (GroupTreeNode group : groupsContainingEntries) { Optional<EntriesGroupChange> undoRemove = group.getGroup().remove(entries); if (undoRemove.isPresent()) { undoAll.addEdit(UndoableChangeEntriesOfGroup.getUndoableEdit(node, undoRemove.get())); } } // then add Optional<EntriesGroupChange> undoAdd = node.addEntriesToGroup(entries); if (undoAdd.isPresent()) { undoAll.addEdit(UndoableChangeEntriesOfGroup.getUndoableEdit(node, undoAdd.get())); } }
/** Converts to BibLatex format */ public static void convertToBiblatex(BibtexEntry entry, NamedCompound ce) { for (Map.Entry<String, String> alias : EntryConverter.FIELD_ALIASES_TEX_TO_LTX.entrySet()) { String oldFieldName = alias.getKey(); String newFieldName = alias.getValue(); String oldValue = entry.getField(oldFieldName); String newValue = entry.getField(newFieldName); if ((oldValue != null) && (!oldValue.isEmpty()) && (newValue == null)) { // There is content in the old field and no value in the new, so just copy entry.setField(newFieldName, oldValue); ce.addEdit(new UndoableFieldChange(entry, newFieldName, null, oldValue)); entry.setField(oldFieldName, null); ce.addEdit(new UndoableFieldChange(entry, oldFieldName, oldValue, null)); } } // Dates: create date out of year and month, save it and delete old fields if ((entry.getField("date") == null) || (entry.getField("date").isEmpty())) { String newDate = entry.getFieldOrAlias("date"); String oldYear = entry.getField("year"); String oldMonth = entry.getField("month"); entry.setField("date", newDate); entry.setField("year", null); entry.setField("month", null); ce.addEdit(new UndoableFieldChange(entry, "date", null, newDate)); ce.addEdit(new UndoableFieldChange(entry, "year", oldYear, null)); ce.addEdit(new UndoableFieldChange(entry, "month", oldMonth, null)); } }
@Override public void actionPerformed(ActionEvent e) { BasePanel bp = frame.basePanel(); if (bp == null) { return; } BibtexEntry[] entries = bp.getSelectedEntries(); // Lazy creation of the dialog: if (diag == null) { createDialog(); } cancelled = true; prepareDialog(entries.length > 0); Util.placeDialog(diag, frame); diag.setVisible(true); if (cancelled) { return; } Collection<BibtexEntry> entryList; // If all entries should be treated, change the entries array: if (all.isSelected()) { entryList = bp.database().getEntries(); } else { entryList = Arrays.asList(entries); } String toSet = text.getText(); if (toSet.isEmpty()) { toSet = null; } String[] fields = getFieldNames(field.getText().trim().toLowerCase()); NamedCompound ce = new NamedCompound(Globals.lang("Set field")); if (rename.isSelected()) { if (fields.length > 1) { // TODO: message: can only rename a single field } else { ce.addEdit( Util.massRenameField(entryList, fields[0], renameTo.getText(), overwrite.isSelected())); } } else { for (String field1 : fields) { ce.addEdit( Util.massSetField( entryList, field1, set.isSelected() ? toSet : null, overwrite.isSelected())); } } ce.end(); bp.undoManager.addEdit(ce); bp.markBaseChanged(); }
private static void fixWrongFileEntries(BibtexEntry entry, NamedCompound ce) { String oldValue = entry.getField(Globals.FILE_FIELD); if (oldValue == null) { return; } FileListTableModel flModel = new FileListTableModel(); flModel.setContent(oldValue); if (flModel.getRowCount() == 0) { return; } boolean changed = false; for (int i = 0; i < flModel.getRowCount(); i++) { FileListEntry flEntry = flModel.getEntry(i); String link = flEntry.getLink(); String description = flEntry.getDescription(); if ("".equals(link) && (!"".equals(description))) { // link and description seem to be switched, quickly fix that flEntry.setLink(flEntry.getDescription()); flEntry.setDescription(""); changed = true; } } if (changed) { String newValue = flModel.getStringRepresentation(); assert (!oldValue.equals(newValue)); entry.setField(Globals.FILE_FIELD, newValue); ce.addEdit(new UndoableFieldChange(entry, Globals.FILE_FIELD, oldValue, newValue)); } }
private void doMakePathsRelative(BibtexEntry entry, NamedCompound ce) { String oldValue = entry.getField(Globals.FILE_FIELD); if (oldValue == null) { return; } FileListTableModel flModel = new FileListTableModel(); flModel.setContent(oldValue); if (flModel.getRowCount() == 0) { return; } boolean changed = false; for (int i = 0; i < flModel.getRowCount(); i++) { FileListEntry flEntry = flModel.getEntry(i); String oldFileName = flEntry.getLink(); String newFileName = FileUtil.shortenFileName( new File(oldFileName), panel.metaData().getFileDirectory(Globals.FILE_FIELD)) .toString(); if (!oldFileName.equals(newFileName)) { flEntry.setLink(newFileName); changed = true; } } if (changed) { String newValue = flModel.getStringRepresentation(); assert (!oldValue.equals(newValue)); entry.setField(Globals.FILE_FIELD, newValue); ce.addEdit(new UndoableFieldChange(entry, Globals.FILE_FIELD, oldValue, newValue)); } }
@Override public AbstractUndoableEdit remove(BibtexEntry[] entries) { if (!supportsRemove()) { return null; } if (entries != null && entries.length > 0) { NamedCompound ce = new NamedCompound(Globals.lang("remove from group")); boolean modified = false; for (BibtexEntry entry : entries) { if (getSearchRule().applyRule(SearchRule.NULL_QUERY, entry)) { String oldContent = entry.getField(searchField); removeMatches(entry); // Store undo information. ce.addEdit( new UndoableFieldChange(entry, searchField, oldContent, entry.getField(searchField))); modified = true; } } if (modified) { ce.end(); } return modified ? ce : null; } return null; }
@Override public AbstractUndoableEdit add(BibtexEntry[] entries) { if (!supportsAdd()) { return null; } if (entries != null && entries.length > 0) { NamedCompound ce = new NamedCompound(Globals.lang("add entries to group")); boolean modified = false; for (BibtexEntry entry : entries) { if (!getSearchRule().applyRule(SearchRule.NULL_QUERY, entry)) { String oldContent = entry.getField(searchField); String pre = Globals.prefs.get(JabRefPreferences.GROUP_KEYWORD_SEPARATOR); String newContent = (oldContent == null ? "" : oldContent + pre) + searchExpression; entry.setField(searchField, newContent); // Store undo information. ce.addEdit(new UndoableFieldChange(entry, searchField, oldContent, newContent)); modified = true; } } if (modified) { ce.end(); } return modified ? ce : null; } return null; }
@Override public boolean makeChange(BasePanel panel, BibtexDatabase secondary, NamedCompound undoEdit) { panel.database().removeEntry(memEntry.getId()); undoEdit.addEdit(new UndoableRemoveEntry(panel.database(), memEntry, panel)); secondary.removeEntry(tmpEntry.getId()); return true; }
public void addToGroup(List<BibEntry> entries, NamedCompound undo) { if (!WarnAssignmentSideEffects.warnAssignmentSideEffects( node.getNode().getGroup(), panel.frame())) { return; // user aborted operation } Optional<EntriesGroupChange> undoAdd = node.addEntriesToGroup(entries); if (undoAdd.isPresent()) { undo.addEdit(UndoableChangeEntriesOfGroup.getUndoableEdit(node, undoAdd.get())); } }
/** * Converts the text in 1st, 2nd, ... to real superscripts by wrapping in \textsuperscript{st}, * ... */ private static void doCleanUpSuperscripts(BibtexEntry entry, NamedCompound ce) { for (String name : entry.getFieldNames()) { String oldValue = entry.getField(name); // run formatter String newValue = BibtexFieldFormatters.SUPERSCRIPTS.format(oldValue); // undo action if (!oldValue.equals(newValue)) { entry.setField(name, newValue); ce.addEdit(new UndoableFieldChange(entry, name, oldValue, newValue)); } } }
/** * Removes the http://... for each DOI Moves DOIs from URL and NOTE filed to DOI field * * @param ce */ private static void doCleanUpDOI(BibtexEntry bes, NamedCompound ce) { // fields to check String[] fields = {"note", "url", "ee"}; // First check if the Doi Field is empty if (bes.getField("doi") != null) { String doiFieldValue = bes.getField("doi"); Optional<DOI> doi = DOI.build(doiFieldValue); if (doi.isPresent()) { String newValue = doi.get().getDOI(); if (!doiFieldValue.equals(newValue)) { ce.addEdit(new UndoableFieldChange(bes, "doi", doiFieldValue, newValue)); bes.setField("doi", newValue); } // Doi field seems to contain Doi // -> cleanup note, url, ee field for (String field : fields) { DOI.build(bes.getField((field))).ifPresent(unused -> removeFieldValue(bes, field, ce)); } } } else { // As the Doi field is empty we now check if note, url, or ee field contains a Doi for (String field : fields) { Optional<DOI> doi = DOI.build(bes.getField(field)); if (doi.isPresent()) { // update Doi String oldValue = bes.getField("doi"); String newValue = doi.get().getDOI(); ce.addEdit(new UndoableFieldChange(bes, "doi", oldValue, newValue)); bes.setField("doi", newValue); removeFieldValue(bes, field, ce); } } } }
/** Converts HTML code to LaTeX code */ private static void doConvertHTML(BibtexEntry entry, NamedCompound ce) { final String field = "title"; String oldValue = entry.getField(field); if (oldValue == null) { return; } final HTMLConverter htmlConverter = new HTMLConverter(); String newValue = htmlConverter.format(oldValue); if (!oldValue.equals(newValue)) { entry.setField(field, newValue); ce.addEdit(new UndoableFieldChange(entry, field, oldValue, newValue)); } }
public static void removeDOIfromBibtexEntryField( BibtexEntry bes, String fieldName, NamedCompound ce) { String origValue = bes.getField(fieldName); String value = origValue; value = value.replaceAll(REGEXP_DOI_WITH_HTTP_PREFIX, ""); value = value.replaceAll(REGEXP_PLAINDOI, ""); value = value.trim(); if (value.isEmpty()) { value = null; } if (!origValue.equals(value)) { ce.addEdit(new UndoableFieldChange(bes, fieldName, origValue, value)); bes.setField(fieldName, value); } }
/** Converts Unicode characters to LaTeX code */ private static void doConvertUnicode(BibtexEntry entry, NamedCompound ce) { final String[] fields = {"title", "author", "abstract"}; for (String field : fields) { String oldValue = entry.getField(field); if (oldValue == null) { return; } final HTMLConverter htmlConverter = new HTMLConverter(); String newValue = htmlConverter.formatUnicode(oldValue); if (!oldValue.equals(newValue)) { entry.setField(field, newValue); ce.addEdit(new UndoableFieldChange(entry, field, oldValue, newValue)); } } }
/** Runs the field formatter on the entry and records the change. */ private static void doFieldFormatterCleanup( BibtexEntry entry, FieldFormatterCleanup cleaner, NamedCompound ce) { String oldValue = entry.getField(cleaner.getField()); if (oldValue == null) { return; } // run formatter cleaner.cleanup(entry); String newValue = entry.getField(cleaner.getField()); // undo action if (!oldValue.equals(newValue)) { ce.addEdit(new UndoableFieldChange(entry, cleaner.getField(), oldValue, newValue)); } }
private static void doCleanUpMonth(BibtexEntry entry, NamedCompound ce) { // implementation based on patch 3470076 by Mathias Walter String oldValue = entry.getField("month"); if (oldValue == null) { return; } String newValue = oldValue; MonthUtil.Month month = MonthUtil.getMonth(oldValue); if (month.isValid()) { newValue = month.bibtexFormat; } if (!oldValue.equals(newValue)) { entry.setField("month", newValue); ce.addEdit(new UndoableFieldChange(entry, "month", oldValue, newValue)); } }
/** Runs the cleanup on the entry and records the change. */ private void doCleanup(CleanupPreset preset, BibEntry entry, NamedCompound ce) { // Run cleaner CleanupWorker cleaner = new CleanupWorker( preset, panel.getBibDatabaseContext().getMetaData().getFileDirectory(Globals.FILE_FIELD)); List<FieldChange> changes = cleaner.cleanup(entry); unsuccessfulRenames = cleaner.getUnsuccessfulRenames(); if (changes.isEmpty()) { return; } // Register undo action for (FieldChange change : changes) { ce.addEdit(new UndoableFieldChange(change)); } }
public static AbstractUndoableEdit getUndoableEdit( GroupTreeNodeViewModel node, EntriesGroupChange change) { if (change.getOldEntries().size() != change.getNewEntries().size()) { return new UndoableChangeAssignment(node, change.getOldEntries(), change.getNewEntries()); } boolean hasEntryChanges = false; NamedCompound entryChangeCompound = new NamedCompound(Localization.lang("change entries of group")); for (FieldChange fieldChange : change.getEntryChanges()) { hasEntryChanges = true; entryChangeCompound.addEdit(new UndoableFieldChange(fieldChange)); } if (hasEntryChanges) { entryChangeCompound.end(); return entryChangeCompound; } return null; }
@Override public void run() { if (!goOn) { panel.output( Localization.lang("This operation requires one or more entries to be selected.")); return; } entriesChangedCount = 0; panel.frame().setProgressBarValue(0); panel.frame().setProgressBarVisible(true); int weightAutoSet = 10; // autoSet takes 10 (?) times longer than checkExisting int progressBarMax = (autoSet ? weightAutoSet * sel.size() : 0) + (checkExisting ? sel.size() : 0); panel.frame().setProgressBarMaximum(progressBarMax); int progress = 0; final NamedCompound ce = new NamedCompound(Localization.lang("Automatically set file links")); Set<BibEntry> changedEntries = new HashSet<>(); // First we try to autoset fields if (autoSet) { Collection<BibEntry> entries = new ArrayList<>(sel); // Start the automatically setting process: Runnable r = AutoSetLinks.autoSetLinks( entries, ce, changedEntries, null, panel.getBibDatabaseContext(), null, null); JabRefExecutorService.INSTANCE.executeAndWait(r); } progress += sel.size() * weightAutoSet; panel.frame().setProgressBarValue(progress); // The following loop checks all external links that are already set. if (checkExisting) { boolean removeAllBroken = false; mainLoop: for (BibEntry aSel : sel) { panel.frame().setProgressBarValue(progress++); final String old = aSel.getField(Globals.FILE_FIELD); // Check if a extension is set: if ((old != null) && !(old.isEmpty())) { FileListTableModel tableModel = new FileListTableModel(); tableModel.setContentDontGuessTypes(old); // We need to specify which directories to search in for Util.expandFilename: List<String> dirsS = panel.getBibDatabaseContext().getFileDirectory(); List<File> dirs = new ArrayList<>(); for (String dirs1 : dirsS) { dirs.add(new File(dirs1)); } for (int j = 0; j < tableModel.getRowCount(); j++) { FileListEntry flEntry = tableModel.getEntry(j); // See if the link looks like an URL: boolean httpLink = flEntry.link.toLowerCase(Locale.ENGLISH).startsWith("http"); if (httpLink) { continue; // Don't check the remote file. // TODO: should there be an option to check remote links? } // A variable to keep track of whether this link gets deleted: boolean deleted = false; // Get an absolute path representation: Optional<File> file = FileUtil.expandFilename(flEntry.link, dirsS); if ((!file.isPresent()) || !file.get().exists()) { int answer; if (removeAllBroken) { answer = 2; // We should delete this link. } else { answer = JOptionPane.showOptionDialog( panel.frame(), Localization.lang( "<HTML>Could not find file '%0'<BR>linked from entry '%1'</HTML>", flEntry.link, aSel.getCiteKey()), Localization.lang("Broken link"), JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, brokenLinkOptions, brokenLinkOptions[0]); } switch (answer) { case 1: // Assign new file. FileListEntryEditor flEditor = new FileListEntryEditor( panel.frame(), flEntry, false, true, panel.getBibDatabaseContext()); flEditor.setVisible(true, true); break; case 2: // Clear field: tableModel.removeEntry(j); deleted = true; // Make sure we don't investigate this link further. j--; // Step back in the iteration, because we removed an entry. break; case 3: // Clear field: tableModel.removeEntry(j); deleted = true; // Make sure we don't investigate this link further. j--; // Step back in the iteration, because we removed an entry. removeAllBroken = true; // Notify for further cases. break; default: // Cancel break mainLoop; } } // Unless we deleted this link, see if its file type is recognized: if (!deleted && flEntry.type.isPresent() && (flEntry.type.get() instanceof UnknownExternalFileType)) { String[] options = new String[] { Localization.lang("Define '%0'", flEntry.type.get().getName()), Localization.lang("Change file type"), Localization.lang("Cancel") }; String defOption = options[0]; int answer = JOptionPane.showOptionDialog( panel.frame(), Localization.lang( "One or more file links are of the type '%0', which is undefined. What do you want to do?", flEntry.type.get().getName()), Localization.lang("Undefined file type"), JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, defOption); if (answer == JOptionPane.CANCEL_OPTION) { // User doesn't want to handle this unknown link type. } else if (answer == JOptionPane.YES_OPTION) { // User wants to define the new file type. Show the dialog: ExternalFileType newType = new ExternalFileType( flEntry.type.get().getName(), "", "", "", "new", IconTheme.JabRefIcon.FILE.getSmallIcon()); ExternalFileTypeEntryEditor editor = new ExternalFileTypeEntryEditor(panel.frame(), newType); editor.setVisible(true); if (editor.okPressed()) { // Get the old list of types, add this one, and update the list in prefs: List<ExternalFileType> fileTypes = new ArrayList<>( ExternalFileTypes.getInstance().getExternalFileTypeSelection()); fileTypes.add(newType); Collections.sort(fileTypes); ExternalFileTypes.getInstance().setExternalFileTypes(fileTypes); panel.getMainTable().repaint(); } } else { // User wants to change the type of this link. // First get a model of all file links for this entry: FileListEntryEditor editor = new FileListEntryEditor( panel.frame(), flEntry, false, true, panel.getBibDatabaseContext()); editor.setVisible(true, false); } } } if (!tableModel.getStringRepresentation().equals(old)) { // The table has been modified. Store the change: String toSet = tableModel.getStringRepresentation(); if (toSet.isEmpty()) { ce.addEdit(new UndoableFieldChange(aSel, Globals.FILE_FIELD, old, null)); aSel.clearField(Globals.FILE_FIELD); } else { ce.addEdit(new UndoableFieldChange(aSel, Globals.FILE_FIELD, old, toSet)); aSel.setField(Globals.FILE_FIELD, toSet); } changedEntries.add(aSel); } } } } if (!changedEntries.isEmpty()) { // Add the undo edit: ce.end(); panel.getUndoManager().addEdit(ce); panel.markBaseChanged(); entriesChangedCount = changedEntries.size(); } }
private void doRenamePDFs(BibtexEntry entry, NamedCompound ce) { // Extract the path String oldValue = entry.getField(Globals.FILE_FIELD); if (oldValue == null) { return; } FileListTableModel flModel = new FileListTableModel(); flModel.setContent(oldValue); if (flModel.getRowCount() == 0) { return; } boolean changed = false; for (int i = 0; i < flModel.getRowCount(); i++) { String realOldFilename = flModel.getEntry(i).getLink(); if (cleanUpRenamePDFonlyRelativePaths.isSelected() && (new File(realOldFilename).isAbsolute())) { continue; } String newFilename = Util.getLinkedFileName(panel.database(), entry); // String oldFilename = bes.getField(GUIGlobals.FILE_FIELD); // would have to be stored for // undoing purposes // Add extension to newFilename newFilename = newFilename + "." + flModel.getEntry(i).getType().getExtension(); // get new Filename with path // Create new Path based on old Path and new filename File expandedOldFile = FileUtil.expandFilename( realOldFilename, panel.metaData().getFileDirectory(Globals.FILE_FIELD)); if (expandedOldFile.getParent() == null) { // something went wrong. Just skip this entry continue; } String newPath = expandedOldFile .getParent() .concat(System.getProperty("file.separator")) .concat(newFilename); if (new File(newPath).exists()) { // we do not overwrite files // TODO: we could check here if the newPath file is linked with the current entry. And if // not, we could add a link continue; } // do rename boolean renameSuccessful = FileUtil.renameFile(expandedOldFile.toString(), newPath); if (renameSuccessful) { changed = true; // Change the path for this entry String description = flModel.getEntry(i).getDescription(); ExternalFileType type = flModel.getEntry(i).getType(); flModel.removeEntry(i); // we cannot use "newPath" to generate a FileListEntry as newPath is absolute, but we want // to keep relative paths whenever possible File parent = (new File(realOldFilename)).getParentFile(); String newFileEntryFileName; if (parent == null) { newFileEntryFileName = newFilename; } else { newFileEntryFileName = parent.toString().concat(System.getProperty("file.separator")).concat(newFilename); } flModel.addEntry(i, new FileListEntry(description, newFileEntryFileName, type)); } else { unsuccessfulRenames++; } } if (changed) { String newValue = flModel.getStringRepresentation(); assert (!oldValue.equals(newValue)); entry.setField(Globals.FILE_FIELD, newValue); // we put an undo of the field content here // the file is not being renamed back, which leads to inconsistencies // if we put a null undo object here, the change by "doMakePathsRelative" would overwrite the // field value nevertheless. ce.addEdit(new UndoableFieldChange(entry, Globals.FILE_FIELD, oldValue, newValue)); } }
private static void removeFieldValue( BibtexEntry entry, String fieldName, NamedCompound compound) { String origValue = entry.getField(fieldName); compound.addEdit(new UndoableFieldChange(entry, fieldName, origValue, null)); entry.setField(fieldName, null); }