/*
 * QueryEditor.java
 *
 * Copyright (C) 2002-2009 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.BorderLayout;
import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.print.Printable;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Vector;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.BorderFactory;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.KeyStroke;
import javax.swing.text.JTextComponent;

import org.apache.commons.lang.StringUtils;
import org.executequery.Constants;
import org.executequery.EventMediator;
import org.executequery.GUIUtilities;
import org.executequery.base.DefaultTabView;
import org.executequery.databasemediators.DatabaseConnection;
import org.executequery.datasource.ConnectionManager;
import org.executequery.event.ApplicationEvent;
import org.executequery.event.ConnectionEvent;
import org.executequery.event.ConnectionListener;
import org.executequery.event.KeywordEvent;
import org.executequery.event.KeywordListener;
import org.executequery.event.QueryBookmarkEvent;
import org.executequery.event.QueryBookmarkListener;
import org.executequery.event.UserPreferenceEvent;
import org.executequery.event.UserPreferenceListener;
import org.executequery.gui.FocusablePanel;
import org.executequery.gui.SaveFunction;
import org.executequery.gui.editor.autocomplete.QueryEditorAutoCompletePopupProvider;
import org.executequery.gui.resultset.ResultSetTableModel;
import org.executequery.gui.text.TextEditor;
import org.executequery.log.Log;
import org.executequery.print.TablePrinter;
import org.executequery.print.TextPrinter;
import org.executequery.sql.TokenizingFormatter;
import org.executequery.util.UserProperties;
import org.underworldlabs.swing.NumberTextField;
import org.underworldlabs.util.MiscUtils;

/** 
 * The Query Editor.
 *
 * @author   Takis Diakoumis
 * @version  $Revision: 1525 $
 * @date     $Date: 2009-05-17 12:40:04 +1000 (Sun, 17 May 2009) $
 */
public class QueryEditor extends DefaultTabView
                         implements ConnectionListener,
                                    QueryBookmarkListener,
                                    UserPreferenceListener,
                                    TextEditor,
                                    KeywordListener,
                                    FocusablePanel {

    public static final String TITLE = "Query Editor";
    public static final String FRAME_ICON = "Edit16.gif";

    private static final String DEFAULT_SCRIPT_PREFIX = "script";
    
    private static final String DEFAULT_SCRIPT_SUFFIX = ".sql";
    
    /** editor open count for title numbering */
    private static int editorCountSequence = 1;
    
    /** The editor's status bar */
    private QueryEditorStatusBar statusBar;
    
    /** The editor's text pan panel */
    private QueryEditorTextPanel editorPanel;
    
    /** The editor's results panel */
    private QueryEditorResultsPanel resultsPanel;
    
    private ScriptFile scriptFile;

    /** flags the content as having being changed */
    private boolean contentChanged;
    
    /** the editor's tool bar */
    private QueryEditorToolBar toolBar;
    
    /** the active connections combo */
    private OpenConnectionsComboBox connectionsCombo;
    
    /** The text pane's popup menu */
    private QueryEditorPopupMenu popup;

    /** the max row count returned field */
    private NumberTextField maxRowCountField;
    
    /** the result pane base panel */
    private JPanel resultsBase;
    
    /** the editor split pane */
    private JSplitPane splitPane;
    
    /** sql query execution delegate */
    private QueryEditorDelegate delegate;
    
    private TokenizingFormatter formatter;
    
    /** Constructs a new instance. */
    public QueryEditor() {
        
        this(null, null);
    }

    /**
     * Creates a new query editor with the specified text content.
     *
     * @param the text content to be set
     */
    public QueryEditor(String text) {
        
        this(text, null);
    }

    /**
     * Creates a new query editor with the specified text content
     * and the specified absolute file path.
     *
     * @param the text content to be set
     * @param the absolute file path;
     */
    public QueryEditor(String text, String absolutePath) {
        
        super(new GridBagLayout());
        
        try  {

            init();

        } catch (Exception e) {

            e.printStackTrace();
        }

        scriptFile = new ScriptFile();
        scriptFile.setFileName(defaultScriptName());
        scriptFile.setAbsolutePath(absolutePath);

        if (text != null) {

            loadText(text);
        }

        contentChanged = false;
        
        formatter = new TokenizingFormatter();
    }

    private String defaultScriptName() {
        return DEFAULT_SCRIPT_PREFIX 
            + (editorCountSequence++) + DEFAULT_SCRIPT_SUFFIX;
    }
    
    private void init() throws Exception {

        // construct the two query text area and results panels
        statusBar = new QueryEditorStatusBar();
        statusBar.setBorder(BorderFactory.createEmptyBorder(2,-1,-2,-2));

        editorPanel = new QueryEditorTextPanel(this);
        resultsPanel = new QueryEditorResultsPanel(this);

        delegate = new QueryEditorDelegate(this);
        
        popup = new QueryEditorPopupMenu(delegate);
        editorPanel.addEditorPaneMouseListener(popup);
        
        JPanel baseEditorPanel = new JPanel(new BorderLayout());
        baseEditorPanel.add(editorPanel, BorderLayout.CENTER);
        baseEditorPanel.add(statusBar, BorderLayout.SOUTH);
        baseEditorPanel.setBorder(BorderFactory.createMatteBorder(
                                1, 1, 1, 1, GUIUtilities.getDefaultBorderColour()));
        
        // add to a base panel - when last tab closed visible set 
        // to false on the tab pane and split collapses - want to avoid this
        resultsBase = new JPanel(new BorderLayout());
        resultsBase.add(resultsPanel, BorderLayout.CENTER);
        
        if (GUIUtilities.getLookAndFeel() < Constants.GTK_LAF) {
            splitPane = new EditorSplitPane(JSplitPane.VERTICAL_SPLIT,
                                            baseEditorPanel, resultsBase);
        }
        else {
            splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
                                       baseEditorPanel, resultsBase);
        }

        splitPane.setDividerSize(4);
        splitPane.setResizeWeight(0.5);
        
        // ---------------------------------------
        // the tool bar and conn combo
        toolBar = new QueryEditorToolBar(
                editorPanel.getTextPaneActionMap(), editorPanel.getTextPaneInputMap());
        
        Vector<DatabaseConnection> connections = 
            ConnectionManager.getActiveConnections();
        connectionsCombo = new OpenConnectionsComboBox(this, connections);

        maxRowCountField = new MaxRowCountField(this);
        
        JPanel toolsPanel = new JPanel(new GridBagLayout());
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.insets = new Insets(0, 0, 0, 0);
        gbc.anchor = GridBagConstraints.NORTHWEST;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.gridy++;
        gbc.gridx++;
        gbc.weightx = 1.0;
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        toolsPanel.add(toolBar, gbc);
        gbc.gridy++;
        gbc.weightx = 0;
        gbc.gridwidth = 1;
        gbc.insets.top = 5;
        gbc.insets.left = 5;
        gbc.insets.right = 10;
        toolsPanel.add(createLabel("Connection:", 'C'), gbc);
        gbc.gridx++;
        gbc.weightx = 1.0;
        gbc.insets.top = 2;
        gbc.insets.bottom = 2;
        gbc.insets.left = 0;
        gbc.insets.right = 0;
        toolsPanel.add(connectionsCombo, gbc);

        gbc.gridx++;
        gbc.weightx = 0;
        gbc.insets.top = 5;
        gbc.insets.left = 10;
        gbc.insets.right = 10;
        toolsPanel.add(createLabel("Max Rows:", 'R'), gbc);
        gbc.gridx++;
        gbc.weightx = 0.8;
        gbc.insets.top = 2;
        gbc.insets.bottom = 2;
        gbc.insets.left = 0;
        gbc.insets.right = 2;
        gbc.fill = GridBagConstraints.BOTH;
        toolsPanel.add(maxRowCountField, gbc);

        splitPane.setBorder(BorderFactory.createEmptyBorder(0,3,3,3));
        
        JPanel base = new JPanel(new BorderLayout());
        base.add(toolsPanel, BorderLayout.NORTH);
        base.add(splitPane, BorderLayout.CENTER);
        
        gbc.gridy = 1;
        gbc.gridx = 1;
        gbc.weightx = 1.0;
        gbc.weighty = 1.0;
        gbc.insets.top = 0;
        gbc.insets.bottom = 0;
        gbc.insets.left = 0;
        gbc.insets.right = 0;
        gbc.fill = GridBagConstraints.BOTH;
        gbc.anchor = GridBagConstraints.SOUTHEAST;
        add(base, gbc);
        
        // register for connection and keyword events
        EventMediator.registerListener(this);

        addDeleteLineActionMapping();
        
        setEditorPreferences();
        
        statusBar.setCaretPosition(1,1);
        statusBar.setInsertionMode("INS");
    }

    public void addConnectionChangeListener(
            final ConnectionChangeListener connectionChangeListener) {

        connectionsCombo.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {

                connectionChangeListener.connectionChanged(getSelectedConnection());
            }
        });
    }
    
    public void removePopupComponent(JComponent component) {

        GUIUtilities.getFrameLayeredPane().remove(component);
        GUIUtilities.getFrameLayeredPane().repaint();
    }
    
    public void addPopupComponent(JComponent component) {
        
        GUIUtilities.getFrameLayeredPane().add(component, JLayeredPane.POPUP_LAYER);
        GUIUtilities.getFrameLayeredPane().repaint();
    }
    
    private void addDeleteLineActionMapping() {
        
        Action action = new AbstractAction() {
            
            public void actionPerformed(ActionEvent e) {
                
                deleteLine();
            }
        };

        KeyStroke keyStroke = KeyStroke.getKeyStroke("control D");

        ActionMap textPaneActionMap = editorPanel.getTextPaneActionMap(); 
        InputMap textPaneInputMap = editorPanel.getTextPaneInputMap();

        String actionMapKey = "editor-delete-line";

        textPaneActionMap.put(actionMapKey, action);
        textPaneInputMap.put(keyStroke, actionMapKey);
    }
    
    private JLabel createLabel(String text, char mnemonic) {

        final JLabel label = new JLabel(text);
        label.setDisplayedMnemonic(mnemonic);

        return label;
    }

    /** the last divider location before a output hide */
    private int lastDividerLocation;

    /**
     * Toggles the output pane visible or not.
     */
    public void toggleOutputPaneVisible() {
        if (resultsBase.isVisible()) {
            lastDividerLocation = splitPane.getDividerLocation();
            resultsBase.setVisible(false);
        } else {
            resultsBase.setVisible(true);
            splitPane.setDividerLocation(lastDividerLocation);
        }
    }
    
    /**
     * Enters the specified text at the editor's current 
     * insertion point.
     *
     * @param text - the text to insert
     */
    public void insertTextAtCaret(String text) {
        editorPanel.insertTextAtCaret(text);
    }
    
    /**
     * Returns the default focus component, the query text
     * editor component.
     *
     * @return the editor component
     */
    public Component getDefaultFocusComponent() {
        return editorPanel.getQueryArea();
    }
    
    /**
     * Sets the editor user preferences.
     */
    public void setEditorPreferences() {

        setPanelBackgrounds();
        
        statusBar.setVisible(isStatusBarVisible());

        editorPanel.showLineNumbers(isLineNumbersVisible());
        
        editorPanel.preferencesChanged();
        
        delegate.preferencesChanged();
        
        delegate.setCommitMode(isAutoCommit());
        
        popup.setCommitMode(isAutoCommit());
        
        resultsPanel.setTableProperties();
        
        if (isAutoCompleteOn()) {
            
            editorPanel.registerAutoCompletePopup(
                    new QueryEditorAutoCompletePopupProvider(this));

        } else {
            
            editorPanel.deregisterAutoCompletePopup();
        }
        
    }

    private boolean isAutoCompleteOn() {
        
        return userProperties().getBooleanProperty("editor.autocomplete.on");
    }
    
    private boolean isAutoCommit() {
        
        return userProperties().getBooleanProperty("editor.connection.commit");
    }

    private boolean isLineNumbersVisible() {
        
        return userProperties().getBooleanProperty("editor.display.linenums");
    }

    private boolean isStatusBarVisible() {
        
        return userProperties().getBooleanProperty("editor.display.statusbar");
    }
    
    private UserProperties userProperties() {
        
        return UserProperties.getInstance();
    }
    
    /**
     * Called to inform this component of a change/update
     * to the user defined key words.
     */
    public void updateSQLKeywords() {
        
        editorPanel.setSQLKeywords(true);
    }
    
    /**
     * Notification of a new keyword added to the list.
     */
    public void keywordsAdded(KeywordEvent e) {
        
        editorPanel.setSQLKeywords(true);
    }

    /**
     * Notification of a keyword removed from the list.
     */
    public void keywordsRemoved(KeywordEvent e) {
        
        editorPanel.setSQLKeywords(true);
    }

    /**
     * Sets the activity status bar label text to 
     * that specified.
     *
     * @param the activity label text
     */
    public void executing() {

        popup.statementExecuting();
        
        setStopButtonEnabled(true);
        
        statusBar.startProgressBar();
        statusBar.setExecutionTime(" Executing.. ");
    }

    /**
     * Notifies that the query execute is finished.
     */
    public void finished(String message) {

        popup.statementFinished();
        
        resultsPanel.finished();
        
        statusBar.stopProgressBar();
        
        setStopButtonEnabled(false);
        
        statusBar.setExecutionTime(message);
    }

    /**
     * Sets the right status bar label text to 
     * that specified.
     *
     * @param the right label text
     */
    public void commitModeChanged(boolean autoCommit) {
        
        statusBar.setCommitStatus(autoCommit);
    }
    
    /**
     * Sets the text of the left status label.
     *
     * @param the text to be set
     */
    public void setLeftStatusText(String s) {
        
        statusBar.setStatus(s);
    }
    
    /**
     * Propagates the call to interrupt an executing process.
     */
    public void interrupt() {
        
        resultsPanel.interrupt();
    }
    
    /**
     * Sets the result set object.
     *
     * @param the executed result set
     * @param whether to return the result set row count
     */
    public int setResultSet(ResultSet rset, boolean showRowNumber)
        throws SQLException {  
        
        return setResultSet(rset, showRowNumber, null);
    }

    /**
     * Returns the vakue from the max record count fields.
     *
     * @return the max row count to be shown
     */
    public int getMaxRecords() {
        
        int maxRecords = maxRowCountField.getValue();
        
        if (maxRecords <= 0) {
            
            maxRecords = -1;
            maxRowCountField.setValue(-1);
        }
        
        return maxRecords;
    }
    
    /**
     * Requests focus on connection combo
     */
    public void selectConnectionCombo() {
        
        connectionsCombo.attemptToFocus();
    }
    
    /**
     * Sets the result set object.
     *
     * @param the executed result set
     * @param whether to return the result set row count
     * @param the executed query of the result set
     */
    public int setResultSet(ResultSet rset, boolean showRowNumber, String query)
        throws SQLException {
        
        int rowCount = resultsPanel.setResultSet(
                rset, showRowNumber, getMaxRecords());
        
        revalidate();
        
        return rowCount;
    }
    
    /**
     * Sets the result set object.
     *
     * @param rset the executed result set
     */
    public void setResultSet(ResultSet rset) throws SQLException {
        
        resultsPanel.setResultSet(rset, true, getMaxRecords());
        revalidate();
    }

    /**
     * Sets the result set object.
     *
     * @param the executed result set
     * @param the executed query of the result set
     */
    public void setResultSet(ResultSet rset, String query) throws SQLException {
        
        resultsPanel.setResultSet(rset, true, getMaxRecords(), query);
        //revalidate();
    }

    public void destroyTable() {
        
        resultsPanel.destroyTable();
    }

    /**
     * Sets to display the result set meta data for the
     * currently selected result set tab.
     */
    public void displayResultSetMetaData() {
        
        resultsPanel.displayResultSetMetaData();
    }
    
    /**
     * Returns the editor status bar.
     *
     * @return the editor's status bar panel
     */
    public QueryEditorStatusBar getStatusBar() {
        
        return statusBar;
    }
    
    /**
     * Disables/enables the listener updates as specified.
     */
    public void disableUpdates(boolean disable) {
        
        editorPanel.disableUpdates(disable);
    }

    /**
     * Returns true that a search can be performed on the editor.
     */
    public boolean canSearch() {
        
        return true;
    }
    
    /**
     * Disables/enables the caret update as specified.
     */
    public void disableCaretUpdate(boolean disable) {
        
        editorPanel.disableCaretUpdate(disable);
    }
    
    public ResultSetTableModel getResultSetTableModel() {
        
        return resultsPanel.getResultSetTableModel();
    }
    
    public void setResultText(int updateCount, int type) {
        
        resultsPanel.setResultText(updateCount, type);
    }
    
    /**
     * Returns whether a result set panel is selected and that
     * that panel has a result set row count > 0.
     *
     * @return true | false
     */
    public boolean isResultSetSelected() {
        
        return resultsPanel.isResultSetSelected();
    }

    /**
     * Sets the respective panel background colours within 
     * the editor as specified by the user defined properties.
     */
    public void setPanelBackgrounds() {
        editorPanel.setTextPaneBackground(
                userProperties().getColourProperty("editor.text.background.colour"));
        resultsPanel.setResultBackground(
                userProperties().getColourProperty("editor.results.background.colour"));
    }

    /**
     * Sets the text of the editor pane to the previous
     * query available in the history list. Where no previous
     * query exists, nothing is changed.
     */
    public void selectPreviousQuery() {

        try {

            GUIUtilities.showWaitCursor();
            
            String query = delegate.getPreviousQuery();
            
            setEditorText(query);
            
        } finally {

            GUIUtilities.showNormalCursor();
        }
    }

    /**
     * Sets the text of the editor pane to the next
     * query available in the history list. Where no
     * next query exists, nothing is changed.
     */
    public void selectNextQuery() {

        try {
        
            String query = delegate.getNextQuery();
            
            setEditorText(query);

        } finally {
          
            GUIUtilities.showNormalCursor();
        }
    }
    
    /**
     * Enables/disables the show meta data button.
     */
    public void setMetaDataButtonEnabled(boolean enable) {
        
        if (retainMetaData()) {

            getTools().setMetaDataButtonEnabled(enable);

        } else {

            getTools().setMetaDataButtonEnabled(false);
        }
    }
    
    /**
     * Sets the history next button enabled as specified.
     */
    public void setHasNextStatement(boolean enabled) {
        
        getTools().setNextButtonEnabled(enabled);
    }

    /**
     * Sets the history previous button enabled as specified.
     */
    public void setHasPreviousStatement(boolean enabled) {
        
        getTools().setPreviousButtonEnabled(enabled);
    }

    /**
     * Enables/disables the transaction related buttons.
     */
    public void setCommitsEnabled(boolean enable) {
        
        getTools().setCommitsEnabled(enable);
    }

    /**
     * Enables/disables the export result set button.
     */
    public void setExportButtonEnabled(boolean enable) {
        
        getTools().setExportButtonEnabled(enable);
    }
    
    /**
     * Enables/disables the query execution stop button.
     */
    public void setStopButtonEnabled(boolean enable) {        
        
        getTools().setStopButtonEnabled(enable);
    }

    public void resetCaretPositionToLast() {        
        
        editorPanel.setTextFocus();
    }
    
    /**
     * Updates the interface and any system buttons as 
     * required on a focus gain.
     */
    public void focusGained() {

        QueryEditorToolBar tools = getTools();
        
        tools.setMetaDataButtonEnabled(
                resultsPanel.hasResultSetMetaData() && retainMetaData());
        
        tools.setCommitsEnabled(!delegate.getCommitMode());
        tools.setNextButtonEnabled(delegate.hasNextStatement());
        tools.setPreviousButtonEnabled(delegate.hasPreviousStatement());
        tools.setStopButtonEnabled(delegate.isExecuting());
        tools.setExportButtonEnabled(resultsPanel.isResultSetSelected());

        editorPanel.setTextFocus();
    }

    public void focusLost() {
        // nothing to do here
    }

    private boolean retainMetaData() {

        return userProperties().getBooleanProperty("editor.results.metadata");
    }

    private QueryEditorToolBar getTools() {
     
        return toolBar;
    }
    
    public void destroyConnection() {
        
        delegate.destroyConnection();
    }
    
    public void toggleCommitMode() {
        
        boolean mode = !delegate.getCommitMode();

        delegate.setCommitMode(mode);
        popup.setCommitMode(mode);
        getTools().setCommitsEnabled(!mode);
    }
    
    
    // --------------------------------------------
    // TabView implementation
    // --------------------------------------------

    /**
     * Indicates the panel is being removed from the pane
     */
    public boolean tabViewClosing() {
        
        UserProperties properties = UserProperties.getInstance();
        
        if (properties.getBooleanProperty("general.save.prompt") && contentChanged) {

            if (!GUIUtilities.saveOpenChanges(this)) {

                return false;
            }

        }

        cleanup();

        return true;
    }

    /**
     * Indicates the panel is being selected in the pane
     */
    public boolean tabViewSelected() {
        
        focusGained();
        
        return true;
    }

    /**
     * Indicates the panel is being de-selected in the pane
     */
    public boolean tabViewDeselected() {
        
        return true;
    }

    // --------------------------------------------

    /**
     * Performs any resource clean up for a pending removal.
     */
    public void cleanup() {
        
        /* ------------------------------------------------
         * profiling found the popup keeps the 
         * editor from being garbage collected at all!!
         * a call to removeAll() is a work around for now.
         * ------------------------------------------------ 
         */
        popup.removeAll();

        editorPanel.closingEditor();
        resultsPanel.cleanup();
        statusBar.cleanup();
        
        resultsPanel = null;
        statusBar = null;
        toolBar = null;
        editorPanel = null;

        removeAll();
        EventMediator.deregisterListener(this);
        GUIUtilities.registerUndoRedoComponent(null);
    }

    public void interruptStatement() {

        if (Log.isDebugEnabled()) {

            Log.debug("Interrupt statement selected");
        }

        delegate.interrupt();
    }

    public void clearOutputPane() {
        
        if (!delegate.isExecuting()) { 
        
            resultsPanel.clearOutputPane();
        }

    }
    
    public void selectAll() {
        
        editorPanel.selectAll();
    }
    
    public void goToRow(int row) {
        
        editorPanel.goToRow(row);
    }
    
    public void selectNone() {
        
        editorPanel.selectNone();
    }
    
    public Vector<String> getHistoryList() {
        
        return delegate.getHistoryList();
    }
    
    /**
     * Executes the currently selected query text.
     */
    public void executeSelection() {
        
        String query = editorPanel.getSelectedText();
        
        if (query != null) {

            executeSQLQuery(query);
        }

    }
    
    /**
     * Returns the currently selcted connection properties object.
     *
     * @return the selected connection
     */
    public DatabaseConnection getSelectedConnection() {
        if (connectionsCombo.getSelectedIndex() != -1) {

            return (DatabaseConnection) connectionsCombo.getSelectedItem();
        }
        return null;
    }

    public void setSelectedConnection(DatabaseConnection databaseConnection) {
        connectionsCombo.setSelectedItem(databaseConnection);
    }
    
    public void preExecute() {
        
        resultsPanel.preExecute();
    }
    
    public String getWordAtCursor() {
        
        return editorPanel.getWordAtCursor();
    }
    
    /**
     * Executes the specified query.
     *
     * @param the query
     */
    public void executeSQLQuery(String query) {

        preExecute();
        
        if (query == null) {

            query = editorPanel.getQueryAreaText();
        }

        editorPanel.resetExecutingLine();

        delegate.executeQuery(getSelectedConnection(), query, false);
    }

    public void executeSQLAtCursor() {

        preExecute();
        
        String query = editorPanel.getQueryAtCursor();
        
        if (StringUtils.isNotBlank(query)) {

            editorPanel.setExecutingQuery(query);
            
            delegate.executeQuery(query);
        }
    }

    public JTextComponent getEditorTextComponent() {
        
        return editorPanel.getQueryArea();
    }
    
    /**
     * Adds a comment tag to the beginning of the current line
     * or selected lines.
     */
    public void commentLines() {
        
        editorPanel.commentLines();
    }

    /**
     * Shifts the text on the current line or the currently
     * selected text to the right one TAB.
     */
    public void shiftTextRight() {
        
        editorPanel.shiftTextRight();
    }

    /**
     * Shifts the text on the current line or the currently
     * selected text to the left one TAB.
     */
    public void shiftTextLeft() {
        
        editorPanel.shiftTextLeft();
    }

    /**
     * Sets the editor's text content that specified.
     *
     * @param s - the text to be set
     */
    public void setEditorText(String s) {
        
        editorPanel.setQueryAreaText(s);
    }
    
    /**
     * Moves the caret to the beginning of the specified query.
     *
     * @param query - the query to move the cursor to
     */
    public void caretToQuery(String query) {
        
        editorPanel.caretToQuery(query);
    }

    /**
     * Loads the specified text into a blank 'offscreen' document
     * before switching to the SQL document.
     */
    public void loadText(String text) {
        
        editorPanel.loadText(text);
    }
    
    public void insertTextAtEnd(String text) {
        int end = getEditorText().length();
        insertTextAfter(end - 1, text);
        caretToQuery(text);
    }
    
    public void insertTextAfter(int after, String text) {
        editorPanel.insertTextAfter(after, text);
    }

    public boolean hasText() {
        return !(MiscUtils.isNull(getEditorText()));
    }
    
    public String getEditorText() {
        return editorPanel.getQueryAreaText();
    }

    public void setOutputMessage(int type, String text) {
        resultsPanel.setOutputMessage(type, text);
    }

    public void setOutputMessage(int type, String text, boolean selectTab) {
        resultsPanel.setOutputMessage(type, text, selectTab);
        //revalidate();        
    }

    /**
     * Sets the state for an open file.
     *
     * @param the absolute file path
     */
    public void setOpenFilePath(String absolutePath) {
        scriptFile.setAbsolutePath(absolutePath);
    }
    
    /**
     * Returns whether the text component is in a printable state.
     *
     * @return true | false
     */
    public boolean canPrint() {
        return true;
    }
    
    public Printable getPrintable() {

        return getPrintableForResultSet();
    }
    
    public Printable getPrintableForResultSet() {

        return new TablePrinter(resultsPanel.getResultsTable(),
                "Query: " + editorPanel.getQueryAreaText());
    }
    
    public Printable getPrintableForQueryArea() {

        return new TextPrinter(editorPanel.getQueryAreaText());
    }
    
    public String getPrintJobName() {
        return "Execute Query - editor";
    }
    
    // ---------------------------------------------
    // TextEditor implementation
    // ---------------------------------------------

    public void paste() {
        editorPanel.paste();
    }
    
    public void copy() {
        editorPanel.copy();
    }
    
    public void cut() {
        editorPanel.cut();
    }
    
    public void deleteLine() {
        editorPanel.deleteLine();
    }
    
    public void deleteWord() {
        editorPanel.deleteWord();
    }
    
    public void deleteSelection() {
        editorPanel.deleteSelection();
    }
    
    public void insertFromFile() {
        editorPanel.insertFromFile();
    }
    
    public void insertLineAfter() {
        editorPanel.insertLineAfter();
    }
    
    public void insertLineBefore() {
        editorPanel.insertLineBefore();
    }
    
    public void changeSelectionCase(boolean upper) {
        editorPanel.changeSelectionCase(upper);
    }
    
    // ---------------------------------------------
    
    // ---------------------------------------------
    // SaveFunction implementation
    // ---------------------------------------------
    
    public String getDisplayName() {
        return toString();
    }
    
    public boolean contentCanBeSaved() {
        return contentChanged;
    }
    
    public int save(boolean saveAs) {
        
        String text = editorPanel.getQueryAreaText();

        QueryEditorFileWriter writer = new QueryEditorFileWriter();
        
        boolean saved = writer.write(text, scriptFile, saveAs);
        
        if (saved) {

            GUIUtilities.setTabTitleForComponent(this, getDisplayName());
            statusBar.setStatus(" File saved to " + scriptFile.getFileName());

            contentChanged = false;
        }

        return SaveFunction.SAVE_COMPLETE;
    }
    
    // ---------------------------------------------
    
    /**
     * Returns the display name of this panel. This may
     * include the path of any open file.
     *
     * @return the display name
     */
    public String toString() {

        return String.format("%s - %s", TITLE, scriptFile.getFileName());
    }

    /**
     * Returns whether the content has changed for a 
     * possible document save.
     *
     * @return true if text content changed, false otherwise
     */
    public boolean isContentChanged() {
        return contentChanged;
    }
    
    /**
     * Sets that the text content of the editor has changed from
     * the original or previously saved state.
     *
     * @param true | false
     */
    public void setContentChanged(boolean contentChanged) {
        this.contentChanged = contentChanged;
    }

    // ---------------------------------------------
    // ConnectionListener implementation
    // ---------------------------------------------
    
    /**
     * Indicates a connection has been established.
     * 
     * @param the encapsulating event
     */
    public void connected(ConnectionEvent connectionEvent) {
        connectionsCombo.addElement(connectionEvent.getDatabaseConnection());
    }

    /**
     * Indicates a connection has been closed.
     * 
     * @param the encapsulating event
     */
    public void disconnected(ConnectionEvent connectionEvent) {
        connectionsCombo.removeElement(connectionEvent.getDatabaseConnection());
        // TODO: NEED TO CHECK OPEN CONN        
    }

    // ---------------------------------------------

    public boolean canHandleEvent(ApplicationEvent event) {
        return (event instanceof ConnectionEvent) 
            || (event instanceof KeywordEvent)
            || (event instanceof UserPreferenceEvent)
            || (event instanceof QueryBookmarkEvent);
    }

    public void queryBookmarkAdded(QueryBookmarkEvent e) {
        handleBookmarkEvent(e);
    }

    public void queryBookmarkRemoved(QueryBookmarkEvent e) {
        handleBookmarkEvent(e);
    }

    private void handleBookmarkEvent(QueryBookmarkEvent e) {
        toolBar.reloadBookmarkItems();
    }

    public void formatSQLtext() {
        
        String text = getSelectedText();
        
        if (text == null) {

            text = getEditorText();
        }
        
        String formattedText = formatter.format(text);
        setEditorText(formattedText);
    }

    private String getSelectedText() {

        return editorPanel.getSelectedText();
    }

    public void preferencesChanged(UserPreferenceEvent event) {

        QueryEditorSettings.initialise();

        if (event.getEventType() == UserPreferenceEvent.QUERY_EDITOR
                ||event.getEventType() == UserPreferenceEvent.ALL) {
            
            setEditorPreferences();
        }
        
    }

}




