/** * 为指定文本框添加 批量缩进 和 批量删除 功能 * * @param textArea */ public static void addIndentFunction( final JTextArea textArea ) { Keymap parent = textArea.getKeymap(); Keymap newMap = JTextComponent.addKeymap(textArea.getName(), parent); // 添加缩进 newMap.addActionForKeyStroke(KeyStroke.getKeyStroke("pressed TAB"), new AbstractAction() { @Override public void actionPerformed( ActionEvent e ) { // 获得选中文本 String selection = textArea.getSelectedText(); // 没有选中文本直接插入制表符 if( selection == null ) textArea.insert("/t", textArea.getCaretPosition()); // 有选中文本对选中行进行缩进 else { // 将选中文本按行分割 String[] lines = selection.split("/r?/n"); if( lines.length == 0 ) return; StringBuilder builder = new StringBuilder(); // 先为第一行以外的每行行首添加一个缩进 builder.append(lines[0]); for( int i = 1; i < lines.length; i++ ) builder.append(NEWLINE).append("/t").append(lines[i]); // 缩进后选中部分的开始位置: 维持选中原来开始位置的字符 // 因为行首将会多一个缩进, 所以位置后移一位来达到效果 int selectionStart = textArea.getSelectionStart() + 1; // 缩进后选中部分的结束位置: 维持选中原来结束位置的字符 // 把开始位置向后偏移 选中部分增加缩进后的长度 int selectionEnd = selectionStart + builder.length(); try { // --- 处理选中部分第一行行首的缩进 : // 此变量用于存储第一行的行首位置, 初始值: 选中部分开始位置的前一个位置 int lineHead = textArea.getSelectionStart() - 1; // 寻找上一行行末; 并将第一行中未选中部分添加进来, 以缩进第一行 while( lineHead >= 0 && !textArea.getText(lineHead, 1).equals("/n") ) { builder.insert(0, textArea.getText(lineHead, 1)); lineHead--; } // 确定行首位置 lineHead++; // 为第一行行首添加缩进 builder.insert(0, "/t"); // 将 第一行行首到原选中部分结束位置 替换为已添加缩进的处理结果 textArea.replaceRange(builder.toString(), lineHead, textArea.getSelectionEnd()/*(selectionStart - lineHead) + selection.length() - 1/**/); // 设置处理后的选中部分 textArea.setSelectionStart(selectionStart); textArea.setSelectionEnd(selectionEnd); } catch( BadLocationException ex ) { Logger.getLogger(TextComponentUtils.class.getName()).log(Level.SEVERE, null, ex); } } } }); // 删除缩进 newMap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, Event.SHIFT_MASK), new AbstractAction() { @Override public void actionPerformed( ActionEvent e ) { // 获得选中文本 String selection = textArea.getSelectedText(); // 没有选中文本什么事也不干 if( selection == null ) return; // 将选中文本按行分割 String[] lines = selection.split("/r?/n"); if( lines.length == 0 ) return; StringBuilder builder = new StringBuilder(); // 先删除第一行以外每行行首的一个缩进 for( int i = 1; i < lines.length; i++ ) { builder.append(NEWLINE); if( lines[i].startsWith("/t") ) builder.append(lines[i].substring(1)); else if( lines[i].startsWith(" ") ) { int start = 0; // 四个空格算作一个缩进 while( start < lines[i].length() && lines[i].charAt(start) == ' ' && start < 4 ) start++; builder.append(lines[i].substring(start)); } else builder.append(lines[i]); } try { // --- 处理选中部分第一行行首的缩进 : // 此变量用于存储第一行的行首位置, 初始值: 选中部分开始位置的前一个位置 int lineHead = textArea.getSelectionStart() - 1; // 寻找上一行行末; 并将第一行中未选中部分添加进来, 以删除第一行的缩进 while( lineHead >= 0 && !textArea.getText(lineHead, 1).equals("/n") ) lineHead--; // 确定行首位置 lineHead++; // 删除缩进后选中部分的开始位置: 维持选中原来开始位置的字符 int selectionStart = textArea.getSelectionStart(); // 缩进后选中部分的结束位置: 维持选中原来结束位置的字符 // 把开始位置向后偏移 选中部分删除缩进后的长度 int selectionEnd = selectionStart + builder.length() + lines[0].length(); String firstLine = textArea.getText(lineHead, textArea.getSelectionStart() - lineHead) + lines[0]; // 为第一行行首删除缩进 if( firstLine.startsWith("/t") ) { // 根据删除的缩进调整选中部分位置 selectionStart--; selectionEnd--; builder.insert(0, firstLine.substring(1)); } else if( firstLine.startsWith(" ") ) { int start = 0; // 四个空格算作一个缩进 while( start < firstLine.length() && firstLine.charAt(start) == ' ' && start < 4 ) { // 根据删除的缩进调整选中部分位置 selectionStart--; selectionEnd--; start++; } builder.insert(0, firstLine.substring(start)); } else builder.insert(0, firstLine); // 将 第一行行首到原选中部分结束位置 替换为已添加缩进的处理结果 textArea.replaceRange(builder.toString(), lineHead, textArea.getSelectionEnd()/*(selectionStart - lineHead) + selection.length() - 1/**/); // 设置处理后的选中部分 textArea.setSelectionStart(selectionStart); textArea.setSelectionEnd(selectionEnd); } catch( BadLocationException ex ) { Logger.getLogger(TextComponentUtils.class.getName()).log(Level.SEVERE, null, ex); } } }); textArea.setKeymap(newMap); }