/** * LineFilter.bsh - a BeanShell macro for the jEdit 4.3 text editor * that filters lines of a buffer due to a provided regular expression. * * :folding=indent:collapseFolds=1: * * The filter actions are: - Write to a new buffer (grep-like). * - Remove from current buffer. * The filter is applied on: - matching lines, or * - not matching lines * The filter works on: - the whole buffer, or * - the selection, if it spans over mor than one line * * * Copyright (C) 2008 Robert Schwenn * * History: * 08.08.2005 1.0 initial Version * 29.01.2008 1.2 - changed regexp to java.util.regex * - consolidated gui and code * 01.06.2008 1.3 - "Remove from current buffer" is only available, * if the buffer is editable and nor multiple or * rectangular selection is present. * - Input field is pre-filled with the selection. * * * This macro is based on the following macros: * - Retain_Or_Remove_Lines.bsh by Jia Zhiming * - Grep_Buffer.bsh, Reverse_Grep_Buffer.bsh by Robert Fletcher * * 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 2 * of the License, or 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. */ import java.util.regex.*; import javax.swing.border.*; // constants used in more than one methods final String DELETE_COMMAND = "delete"; final String WRITE_TO_NEW_BUFFER_COMMAND = "writeToNewBuffer"; // GUI void LineFilterDialog() { // // constants final static String processNotMatchedCommand = "notMatching"; final static String processMatchedCommand = "matching"; // Common Dialog Labels final static String PROPERTY_COMMON_OK = jEdit.getProperty("common.ok"); final static String PROPERTY_COMMON_CANCEL = jEdit.getProperty("common.cancel"); final static String NotEditableMessage = jEdit.getProperty("macro.rs.general.ErrorNotEditableDialog.message", "Buffer is not editable"); final static String MultipleSelectionMessage = jEdit.getProperty("macro.rs.LineFilter.MultipleSelection.message", "Multiple selection is not supported"); final static String BlockSelectionMessage = jEdit.getProperty("macro.rs.LineFilter.BlockSelection.message", "Rectangular selection is not supported"); final static String DeleteActionDisabledMessage = jEdit.getProperty("macro.rs.LineFilter.DeleteActionDisabled.message", "Delete is not available"); // Special Dialog Labels final static String MainDialogTitle = jEdit.getProperty("macro.rs.LineFilter.MainDialog.title", "LineFilter"); final static String ignoreCaseCheckBoxText = jEdit.getProperty("macro.rs.LineFilter.ignoreCaseCheckBox.label", "Ignore case"); final static String regexpPanelBorderTitle = jEdit.getProperty("macro.rs.LineFilter.regexpPanelBorder.title", "Pattern to compare (Regular Expression)"); final static String processModeRadioButtonMatching = jEdit.getProperty("macro.rs.LineFilter.processModeRadioButtonMatching.label", "Matching"); final static String processModeRadioButtonNotMatching = jEdit.getProperty("macro.rs.LineFilter.processModeRadioButtonNotMatching.label", "Not Matching"); final static String processModeOptionPanelBorderTitle = jEdit.getProperty("macro.rs.LineFilter.processModeOptionPanelBorder.title", "Lines"); final static String actionModeRadioButtonWrite = jEdit.getProperty("macro.rs.LineFilter.actionModeRadioButtonWrite.label", "Write to new buffer"); final static String actionModeRadioButtonDelete = jEdit.getProperty("macro.rs.LineFilter.actionModeRadioButtonDelete.label", "Delete"); final static String actionModeOptionPanelBorderTitle = jEdit.getProperty("macro.rs.LineFilter.actionModeOptionPanelBorder.title", "Action"); // // *** Eventhandler ********************************************************* // Button oder ENTER key pressed public void actionPerformed(ActionEvent e) { //Macros.message(view, "actionPerformed: ''" + e.getActionCommand() + "'"); boolean processMatchedLines; if (e.getActionCommand() != PROPERTY_COMMON_CANCEL) { // evaluate status of radio buttons if (processModeButtonGroup.getSelection().getActionCommand() == processMatchedCommand) { processMatchedLines = true; } else { processMatchedLines = false; } //String actionModeCommand = e.getactionModeCommand(); String actionModeCommand = actionModeButtonGroup.getSelection().getActionCommand(); String regExp = regexpTextfield.getText(); boolean ignoreCase = ignoreCaseCheckBox.isSelected(); regexpTextfield.addCurrentToHistory(); if (processText(regExp, ignoreCase, processMatchedLines, actionModeCommand)) dialog.dispose(); } else { dialog.dispose(); } } public void keyPressed(KeyEvent e) { //Macros.message(view, "keyPressed..." + e.getKeyCode()); if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { //Macros.message(view, "ESCAPE pressed."); dialog.setVisible(false); dialog.dispose(); } } // to avoid BeanShell throwing exceptions each time one of these events occur. public void keyReleased(KeyEvent e) { } public void keyTyped(KeyEvent e) { } // **************************************************************************** // // prepare dialog dialog = new JDialog(view, MainDialogTitle, true); content = new JPanel(new BorderLayout()); content.setBorder(new EmptyBorder(8, 8, 8, 8)); dialog.setContentPane(content); // prepare Buttons and button panel JButton okButton = new JButton(PROPERTY_COMMON_OK); JButton cancelButton = new JButton(PROPERTY_COMMON_CANCEL); dialog.getRootPane().setDefaultButton(okButton); buttonPanel = new JPanel(); buttonPanel.setLayout(new BoxLayout(buttonPanel,BoxLayout.X_AXIS)); buttonPanel.setBorder(new EmptyBorder(10, 10, 0, 10)); buttonPanel.add(Box.createGlue()); buttonPanel.add(cancelButton); buttonPanel.add(Box.createHorizontalStrut(15)); buttonPanel.add(okButton); buttonPanel.add(Box.createGlue()); // prepare regexp input frame regexpTextfield = new HistoryTextField("macro.rs.LineFilter.regexp"); regexpTextfield.setActionCommand(dialog.getRootPane().getDefaultButton().getActionCommand()); ignoreCaseCheckBox = new JCheckBox(ignoreCaseCheckBoxText, false); regexpPanel = new JPanel(new GridLayout(2, 1, 5, 8)); regexpPanel.add(regexpTextfield); regexpPanel.add(ignoreCaseCheckBox); regexpPanel.setBorder(BorderFactory.createCompoundBorder( BorderFactory.createTitledBorder(" " + regexpPanelBorderTitle + " "), BorderFactory.createEmptyBorder(0,0,0,0))); // prepare process mode option panel ButtonGroup processModeButtonGroup = new ButtonGroup(); JRadioButton[] processModeRadioButtons = new JRadioButton[2]; processModeRadioButtons[0] = new JRadioButton(processModeRadioButtonMatching); processModeRadioButtons[1] = new JRadioButton(processModeRadioButtonNotMatching); processModeRadioButtons[0].setActionCommand(processMatchedCommand); processModeRadioButtons[1].setActionCommand(processNotMatchedCommand); processModeRadioButtons[0].setSelected(true); processModeButtonGroup.add(processModeRadioButtons[0]); processModeButtonGroup.add(processModeRadioButtons[1]); processModeOptionPanel = new JPanel(new GridLayout(2, 1, 0, 0)); processModeOptionPanel.add(processModeRadioButtons[0]); processModeOptionPanel.add(processModeRadioButtons[1]); processModeOptionPanel.setBorder(BorderFactory.createCompoundBorder( BorderFactory.createTitledBorder(" " + processModeOptionPanelBorderTitle + " "), BorderFactory.createEmptyBorder(0,0,0,0))); // prepare action mode option panel ButtonGroup actionModeButtonGroup = new ButtonGroup(); JRadioButton[] actionModeRadioButtons = new JRadioButton[2]; actionModeRadioButtons[0] = new JRadioButton(actionModeRadioButtonWrite); actionModeRadioButtons[1] = new JRadioButton(actionModeRadioButtonDelete); actionModeRadioButtons[0].setActionCommand(WRITE_TO_NEW_BUFFER_COMMAND); actionModeRadioButtons[1].setActionCommand(DELETE_COMMAND); actionModeRadioButtons[0].setSelected(true); actionModeButtonGroup.add(actionModeRadioButtons[0]); actionModeButtonGroup.add(actionModeRadioButtons[1]); actionModeOptionPanel = new JPanel(new GridLayout(2, 1, 0, 0)); actionModeOptionPanel.add(actionModeRadioButtons[0]); actionModeOptionPanel.add(actionModeRadioButtons[1]); actionModeOptionPanel.setBorder(BorderFactory.createCompoundBorder( BorderFactory.createTitledBorder(" " + actionModeOptionPanelBorderTitle + " "), BorderFactory.createEmptyBorder(0,0,0,0))); // place panels at dialog content.add(regexpPanel, BorderLayout.NORTH); content.add(processModeOptionPanel, BorderLayout.WEST); content.add(actionModeOptionPanel, BorderLayout.EAST); content.add(buttonPanel, BorderLayout.SOUTH); // // add event listeners to components okButton.addActionListener(this); cancelButton.addActionListener(this); regexpTextfield.addActionListener(this); okButton.addKeyListener(this); cancelButton.addKeyListener(this); regexpTextfield.addKeyListener(this); ignoreCaseCheckBox.addKeyListener(this); processModeRadioButtons[0].addKeyListener(this); processModeRadioButtons[1].addKeyListener(this); actionModeRadioButtons[0].addKeyListener(this); actionModeRadioButtons[1].addKeyListener(this); // // change GUI environment-dependent Selection[] selection = textArea.getSelection(); // pre-fill input field with selection if (selection.length != 0) { if (selection[0].getStartLine() == selection[0].getEndLine()) { String SelectedText = textArea.getSelectedText(selection[0]); regexpTextfield.setText(SelectedText); } } // availability of delete action is conditioned: delete_ok = true; if (! buffer.isEditable()) { delete_ok = false; Msg = NotEditableMessage; } if (selection.length > 1) { // multiple selection present! delete_ok = false; Msg = MultipleSelectionMessage; } else if (selection.length == 1) { if ((textArea.getSelectedLines().length > 1) && (! (selection[0] instanceof Selection.Range))) { // single selection consists of more than one line, but is not of range type. delete_ok = false; Msg = BlockSelectionMessage; } } if (! delete_ok) { actionModeRadioButtons[1].setEnabled(false); view.getStatus().setMessageAndClear(MainDialogTitle + ": " + DeleteActionDisabledMessage + " (" + Msg + ")"); } // // start dialog dialog.pack(); dialog.setLocationRelativeTo(view); dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); dialog.setVisible(true); } // do the job boolean processText(String regexpString, boolean ignoreCase, boolean processMatchedLines, String actionModeCommand) { // returns false, if regular expression is invalid, else true. // debug //Macros.message(view, "processText: regexpString = ''" + regexpString + "'" // + "'\nprocessMatchedLines = " + processMatchedLines // + "\nactionModeCommand = " + actionModeCommand // ); // labels final static String ErrorRegexpDialogTitle = jEdit.getProperty("macro.rs.LineFilter.ErrorRegexpDialog.title", "Invalid regular expression"); final static String ErrorRegexpDialogMessage = jEdit.getProperty("macro.rs.LineFilter.ErrorRegexpDialog.message", "The Regular expression is invalid!"); final static String SummaryDeletedText = jEdit.getProperty("macro.rs.LineFilter.SummaryDeleted.label", "Lines deleted!"); final static String SummaryWrittenText = jEdit.getProperty("macro.rs.LineFilter.SummaryWritten.label", "Lines written to a new buffer."); // declarations boolean writeLinesToNewBuffer = false; boolean deleteLines = false; boolean lineMatches; boolean appendLine; int regexpFlags = 0; int deletedLines = 0; int writtenLines = 0; int sbfLength; StringBuffer sbf = new StringBuffer(); Pattern pattern = null; Matcher matcher = null; // evaluate parameters if (actionModeCommand == DELETE_COMMAND) deleteLines = true; if (actionModeCommand == WRITE_TO_NEW_BUFFER_COMMAND) writeLinesToNewBuffer = true; if (ignoreCase) regexpFlags += Pattern.CASE_INSENSITIVE; // // create the regexp matcher try { pattern = Pattern.compile(regexpString, regexpFlags); matcher = pattern.matcher("x"); } catch (PatternSyntaxException e) { JOptionPane.showMessageDialog(view, ErrorRegexpDialogMessage, ErrorRegexpDialogTitle, JOptionPane.ERROR_MESSAGE); return false; } // // get lines to process int[] lines = textArea.getSelectedLines(); if (lines == null || lines.length <= 1) { // no selection ==> process all lines iMax = textArea.getLineCount(); linesSelected = false; } else { // selection exist iMax = lines.length; linesSelected = true; } // parse every line to process for (int i = 0; i < iMax; i++) { // get currentLineNr if (linesSelected) { currentLineNr = lines[i]; } else { currentLineNr = i; } // compare current line's text against regxep lineText = textArea.getLineText(currentLineNr); matcher.reset(lineText); lineMatches = matcher.find(); appendLine = false; //Macros.message(view, "matcher.matches() = " + matcher.find()); // collect lines for result (which is a new or the current buffer) if (writeLinesToNewBuffer) { appendLine = ((processMatchedLines && lineMatches) || (!processMatchedLines && !lineMatches)); } else if (deleteLines) { appendLine = ((processMatchedLines && !lineMatches) || (!processMatchedLines && lineMatches)); } if (appendLine) sbf.append(lineText).append("\n"); } sbfLength = sbf.length(); // write collected lines (to new or current buffer) if (deleteLines) { if (sbfLength > 0) sbf.deleteCharAt(sbfLength - 1); int oldLineCount = textArea.getLineCount(); if (linesSelected) { textArea.setSelectedText(sbf.toString()); } else { textArea.setText(sbf.toString()); } int newLineCount = textArea.getLineCount(); deletedLines = oldLineCount - newLineCount; Msg = deletedLines.toString() + " " + SummaryDeletedText; } else if (writeLinesToNewBuffer) { if (sbfLength > 0) { jEdit.newFile(view); textArea.setSelectedText(sbf.toString()); writtenLines = textArea.getLineCount() - 1; } Msg = writtenLines.toString() + " " + SummaryWrittenText; } view.getStatus().setMessageAndClear(Msg); return true; } // starting point LineFilterDialog();