Java tutorial
/* * QueryEditorTextPanel.java * * Copyright (C) 2002-2015 Takis Diakoumis * * 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 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 org.executequery.gui.editor; import java.awt.AWTException; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Graphics; import java.awt.Insets; import java.awt.Robot; import java.awt.event.KeyEvent; import java.awt.event.MouseListener; import java.sql.ResultSet; import java.sql.SQLException; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.swing.Action; import javax.swing.ActionMap; import javax.swing.InputMap; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextPane; import javax.swing.KeyStroke; import javax.swing.border.Border; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import org.apache.commons.lang.StringUtils; import org.executequery.GUIUtilities; import org.executequery.gui.editor.autocomplete.AutoCompletePopupProvider; import org.executequery.gui.text.TextUtilities; import org.executequery.log.Log; import org.underworldlabs.swing.GUIUtils; /** * This object is the primary mediator between the parent * <code>QueryEditor</code> object and the actual Query Editor's * text pane - the <code>QueryEditorTextPane</code>. All text commands for the * text pane are propagated through here. * * @author Takis Diakoumis * @version $Revision: 1487 $ * @date $Date: 2015-08-23 22:21:42 +1000 (Sun, 23 Aug 2015) $ */ public class QueryEditorTextPanel extends JPanel { private static final String SQL_COMMENT_REGEX = "^\\s*--"; private static final String SQL_COMMENT = "--"; /** The SQL text pane */ private QueryEditorTextPane queryPane; /** The editor's controller */ private QueryEditor queryEditor; private static final String AUTO_COMPLETE_POPUP_ACTION_KEY = "autoCompletePopupActionKey"; private AutoCompletePopupProvider autoCompletePopup; /** Constructs a new instance. */ public QueryEditorTextPanel(QueryEditor queryEditor) { super(new BorderLayout()); this.queryEditor = queryEditor; try { init(); } catch (Exception e) { e.printStackTrace(); } } /** Initializes the state of this instance. */ private void init() throws Exception { // setup the query text panel and associated scroller queryPane = new QueryEditorTextPane(this); JScrollPane queryScroller = new JScrollPane(); queryScroller.getViewport().add(queryPane, BorderLayout.CENTER); queryScroller.setRowHeaderView(queryPane.getLineBorder()); queryScroller.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); queryScroller.setBorder(new EditorScrollerBorder()); add(queryScroller, BorderLayout.CENTER); } public void deregisterAutoCompletePopup() { if (autoCompletePopup != null) { Action autoCompletePopupAction = autoCompletePopup.getPopupAction(); queryPane.getActionMap().remove(AUTO_COMPLETE_POPUP_ACTION_KEY); queryPane.getInputMap().remove((KeyStroke) autoCompletePopupAction.getValue(Action.ACCELERATOR_KEY)); autoCompletePopup = null; } } public void registerAutoCompletePopup(AutoCompletePopupProvider autoCompletePopup) { this.autoCompletePopup = autoCompletePopup; Action autoCompletePopupAction = autoCompletePopup.getPopupAction(); queryPane.getActionMap().put(AUTO_COMPLETE_POPUP_ACTION_KEY, autoCompletePopupAction); queryPane.getInputMap().put((KeyStroke) autoCompletePopupAction.getValue(Action.ACCELERATOR_KEY), AUTO_COMPLETE_POPUP_ACTION_KEY); } public void addEditorPaneMouseListener(MouseListener listener) { queryPane.addMouseListener(listener); } public boolean isLogEnabled() { return OutputLogger.isLogEnabled(); } public void log(String message) { if (isLogEnabled()) { OutputLogger.info(message); } } public void setTextPaneBackground(Color c) { queryPane.setBackground(c); } public void setSQLKeywords(boolean reset) { queryPane.setSQLKeywords(reset); } protected ActionMap getTextPaneActionMap() { return queryPane.getActionMap(); } protected InputMap getTextPaneInputMap() { return queryPane.getInputMap(); } /** * Indicates that the editor is closing and performs some cleanup. */ protected void closingEditor() { queryEditor = null; } public void showLineNumbers(boolean show) { queryPane.showLineNumbers(show); } protected void setTextFocus() { GUIUtils.requestFocusInWindow(queryPane); } /** * Resets the executing line within the line * number border panel. */ public void resetExecutingLine() { queryPane.resetExecutingLine(); } /** * Resets the text pane's caret position to zero. */ public void resetCaretPosition() { queryPane.setCaretPosition(0); } /** * Enters the specified text at the editor's current * insertion point. * * @param text - the text to insert */ public void insertTextAtCaret(String text) { queryPane.replaceSelection(text); setTextFocus(); } public JTextPane getQueryArea() { return queryPane; } public void selectAll() { TextUtilities.selectAll(queryPane); } public void selectNone() { TextUtilities.selectNone(queryPane); queryEditor.focusGained(); } public void focusLost() { if (queryEditor != null) { queryEditor.focusLost(); } } public void focusGained() { if (queryEditor != null) { queryEditor.focusGained(); } } public void commitModeChanged(boolean autoCommit) { queryEditor.commitModeChanged(autoCommit); } /** * Sets the editor's text content that specified. * * @param s - the text to be set */ public void setQueryAreaText(String s) { try { // uninstall listeners on the text pane queryPane.uninstallListeners(); // clear the current held edits // queryPane.clearEdits(); // set the text queryPane.setText(s); } finally { // reinstall listeners on the text pane queryPane.reinstallListeners(); } } public void insertTextAfter(int after, String text) { queryPane.insertTextAfter(after, text); } /** * Loads the specified text into a blank 'offscreen' document * before switching to the SQL document. */ public void loadText(String text) { queryPane.loadText(text); } public QueryEditorStatusBar getStatusBar() { return queryEditor.getStatusBar(); } public void disableUpdates(boolean disable) { queryPane.disableUpdates(disable); } public void disableCaretUpdate(boolean disable) { queryPane.disableCaretUpdate(disable); } public void preferencesChanged() { queryPane.resetAttributeSets(); } // ----------------------------------- // regex replacement arrays // ----------------------------------- private static final String[] REGEX_CHARS = { "\\*", "\\^", "\\.", "\\[", "\\]", "\\(", "\\)", "\\?", "\\&", "\\{", "\\}", "\\+" }; private static final String[] REGEX_SUBS = { "\\\\*", "\\\\^", "\\\\.", "\\\\[", "\\\\]", "\\\\(", "\\\\)", "\\\\?", "\\\\&", "\\\\{", "\\\\}", "\\\\+" }; /** * Moves the caret to the beginning of the specified query. * * @param query - the query to move the cursor to */ public void caretToQuery(String query) { // replace any regex control chars for (int i = 0; i < REGEX_CHARS.length; i++) { query = query.replaceAll(REGEX_CHARS[i], REGEX_SUBS[i]); } Matcher matcher = Pattern.compile(query, Pattern.DOTALL).matcher(queryPane.getText()); if (matcher.find()) { int index = matcher.start(); if (index != -1) { queryPane.setCaretPosition(index); } } matcher = null; GUIUtils.requestFocusInWindow(queryPane); } /** * Returns the currently selected text, or null if not text * is currently selected. * * @return selected text */ public String getSelectedText() { String selection = queryPane.getSelectedText(); if (StringUtils.isNotBlank(selection)) { return selection; } return null; } public void replaceRegion(int start, int end, String replacement) { queryPane.select(start, end); queryPane.replaceSelection(replacement); } public int getSelectionStart() { return queryPane.getSelectionStart(); } public int getSelectionEnd() { return queryPane.getSelectionEnd(); } public String getCompleteWordEndingAtCursor() { return queryPane.getCompleteWordEndingAtCursor(); } protected String getWordToCursor() { return queryPane.getWordEndingAtCursor(); } protected QueryWithPosition getQueryAtCursor() { return queryPane.getQueryAtCursor(); } protected void setExecutingQuery(String query) { queryPane.setExecutingQuery(query); } public void goToRow(int row) { queryPane.goToRow(row); } public void destroyTable() { queryEditor.destroyTable(); } /** * Sets the table results to the specified * <code>ResultSet</code> object for display. * * @param the table results to display * @param the executed query of the result set */ public void setResultSet(ResultSet rset, String query) throws SQLException { queryEditor.setResultSet(rset, query); } public void setResult(int updateCount, int type) { queryEditor.setResultText(updateCount, type); } public String getQueryAreaText() { return queryPane.getText(); } private void addCommentToRows(int startRow, int endRow) { Matcher matcher = sqlCommentMatcher(); for (int i = startRow; i <= endRow; i++) { String text = queryPane.getTextAtRow(i); matcher.reset(text); if (!matcher.find()) { int index = queryPane.getRowStartOffset(i); queryPane.insertTextAtOffset(index, SQL_COMMENT); } } } private void removeCommentFromRows(int startRow, int endRow) throws BadLocationException { Document document = queryPane.getDocument(); Matcher matcher = sqlCommentMatcher(); for (int i = startRow; i <= endRow; i++) { String text = queryPane.getTextAtRow(i); matcher.reset(text); if (matcher.find()) { // retrieve the exact index of '--' since // matcher will return first whitespace int index = text.indexOf(SQL_COMMENT); int startOffset = queryPane.getRowPosition(i); document.remove(startOffset + index, 2); } } } /** * Adds a comment tag to the beginning of the current line * or selected lines. */ public void commentLines() { int selectionStart = queryPane.getSelectionStart(); int selectionEnd = queryPane.getSelectionEnd(); boolean singleRow = (selectionStart == selectionEnd); int startRow = queryPane.getRowAt(selectionStart); int endRow = queryPane.getRowAt(selectionEnd); int endRowStartIndex = queryPane.getRowStartOffset(endRow); if (!singleRow && selectionEnd == endRowStartIndex) { endRow--; } try { if (rowsHaveComments(startRow, endRow, true)) { removeCommentFromRows(startRow, endRow); } else if (rowsHaveComments(startRow, endRow, false)) { if (singleRow) { removeCommentFromRows(startRow, endRow); } else { // if any one row of a multi-row selection has // a comment, comment the rest also addCommentToRows(startRow, endRow); } } else { addCommentToRows(startRow, endRow); } } catch (BadLocationException e) { // nothing we can do here e.printStackTrace(); } if (!singleRow) { queryPane.setSelectionStart(queryPane.getRowStartOffset(startRow)); queryPane.setSelectionEnd(queryPane.getRowEndOffset(endRow)); } } /** pattern matcher to check for comments to be removed */ private Matcher sqlCommentMatcher; private Matcher sqlCommentMatcher() { if (sqlCommentMatcher == null) { sqlCommentMatcher = Pattern.compile(SQL_COMMENT_REGEX).matcher(""); } return sqlCommentMatcher; } private boolean rowsHaveComments(int startRow, int endRow, boolean allRows) throws BadLocationException { Matcher matcher = sqlCommentMatcher(); for (int i = startRow; i <= endRow; i++) { String text = queryPane.getTextAtRow(i); matcher.reset(text); if (matcher.find()) { if (!allRows) { return true; } } else if (allRows) { return false; } } return allRows; } /** * Shifts the text on the current line or the currently * selected text to the right one TAB. */ public void shiftTextRight() { if (getSelectedText() == null) { int start = queryPane.getCurrentRowStart(); queryPane.shiftTextRight(start); } else { // simulate a tab key for selected text try { Robot robot = new Robot(); robot.keyPress(KeyEvent.VK_TAB); robot.keyRelease(KeyEvent.VK_TAB); } catch (AWTException e) { e.printStackTrace(); } } } public void moveSelectionUp() { queryPane.moveSelectionUp(); } public void moveSelectionDown() { queryPane.moveSelectionDown(); } public void duplicateRowUp() { queryPane.duplicateTextUp(); } public void duplicateRowDown() { queryPane.duplicateTextDown(); } /** * Shifts the text on the current line or the currently * selected text to the left one TAB. */ public void shiftTextLeft() { if (getSelectedText() == null) { int start = queryPane.getCurrentRowStart(); int end = queryPane.getCurrentRowEnd(); queryPane.shiftTextLeft(start, end); } else { // simulate a tab key for selected text try { Robot robot = new Robot(); robot.keyPress(KeyEvent.VK_SHIFT); robot.keyPress(KeyEvent.VK_TAB); robot.keyRelease(KeyEvent.VK_TAB); robot.keyRelease(KeyEvent.VK_SHIFT); } catch (AWTException e) { if (Log.isDebugEnabled()) { Log.error("Error simulating tab key events", e); } } } } // --------------------------------------------- // TextFunction implementation // --------------------------------------------- public void paste() { queryPane.paste(); } public void copy() { queryPane.copy(); } public void cut() { queryPane.cut(); } public void changeSelectionToCamelCase() { TextUtilities.changeSelectionToCamelCase(queryPane); } public void changeSelectionToUnderscore() { TextUtilities.changeSelectionToUnderscore(queryPane); } public void changeSelectionCase(boolean upper) { TextUtilities.changeSelectionCase(queryPane, upper); } public void deleteLine() { TextUtilities.deleteLine(queryPane); } public void deleteWord() { TextUtilities.deleteWord(queryPane); } public void deleteSelection() { TextUtilities.deleteSelection(queryPane); } public void insertFromFile() { TextUtilities.insertFromFile(queryPane); } public void insertLineAfter() { TextUtilities.insertLineAfter(queryPane); } public void insertLineBefore() { TextUtilities.insertLineBefore(queryPane); } // --------------------------------------------- /** * Propagates the call to the parent QueryEditor object * that the text content has been altered from the original * or previously saved state. * * @param true | false */ public void setContentChanged(boolean contentChanged) { queryEditor.setContentChanged(contentChanged); } private static Insets borderInsets; private static Color borderColour; private class EditorScrollerBorder implements Border { protected EditorScrollerBorder() { if (borderInsets == null) { borderInsets = new Insets(0, 0, 0, 0); } if (borderColour == null) { borderColour = GUIUtilities.getDefaultBorderColour(); } } public Insets getBorderInsets(Component c) { return borderInsets; } public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { g.setColor(borderColour); g.drawLine(x, height - 1, width, height - 1); } public boolean isBorderOpaque() { return false; } } public void editorShortcutsUpdated() { queryPane.editorShortcutsUpdated(); } }