org.colombbus.tangara.EditorFrame.java Source code

Java tutorial

Introduction

Here is the source code for org.colombbus.tangara.EditorFrame.java

Source

/**
 * Tangara is an educational platform to get started with programming.
 * Copyright (C) 2008 Colombbus (http://www.colombbus.org)
 *
 * 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
 * (at your option) 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.colombbus.tangara;

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.GraphicsEnvironment;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.Insets;
import java.awt.MediaTracker;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.text.MessageFormat;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextPane;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.filechooser.FileFilter;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.TabSet;
import javax.swing.text.TabStop;

import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
import org.colombbus.helpengine.HelpEngine;
import org.colombbus.tangara.io.ScriptReader;
import org.colombbus.tangara.io.ScriptWriter;
import org.gjt.sp.jedit.textarea.TextArea;

/**
 * The main framework of Tangara. It contains all panes of the UI when you run
 * Tangara in normal mode (with no arguments in main)
 */
@SuppressWarnings("serial")
public class EditorFrame extends TFrame {

    private static final int optionType = JOptionPane.OK_CANCEL_OPTION;

    private static Logger LOG = Logger.getLogger(EditorFrame.class);

    private static final double DIVIDER1_LOCATION = 0.65;
    private static final double DIVIDER2_LOCATION = 0.5;

    private int commandHistory = 0;

    private PopupManager popupManager;

    private boolean writingHelp = true;
    private boolean commandMode = false;
    private boolean displayLineNumbers = false;

    private int[] programDividerLocation = null;
    private int[] commandDividerLocation = null;

    private static final String ICON_PATH = "logo_tangara.png"; //$NON-NLS-1$

    private Font font;
    private int interfaceLevel;
    private Configuration configuration;
    public int commandModifier;

    private JSplitPane jSplitPane1 = null;
    private JPanel basePanel = null;
    private JPanel controlPanel = null;
    private JSplitPane jSplitPane2 = null;

    private JLabel toProgramLabel = null;
    private JLabel selectAllLabel = null;
    private JLabel deselectAllLabel = null;

    private JPanel pageEnd = null;

    private LogConsole console = null;

    private boolean noCodeSelected = true;

    private JPanel msgButtons = null;
    private JPanel msgButtonsPanel = null;
    private JPanel msgButtonsMainPanel = null;

    private JScrollPane scrollPane = null;
    private JPanel commandPanel = null;
    private JPanel cmdButtonPanel = null;
    private JButton cmdRunButton = null;
    private JButton refreshButton = null;
    private TextPane commandPane = null;
    private JPanel editionPanel = null;
    private GraphicsPane graphicsPane = null;
    private Banner banner = null;
    private JFileChooser fileChooser = null;
    private JFileChooser fileChooserWithoutFilter = null;
    private JPanel modePanel = null;
    private OptionPanel optionPanel = null;
    protected TextPaneManager programManager = null;
    private CommandTransferHandler commandHandler = null;
    private int programIndex = -1;

    private static int tabSize = 0;

    private AbstractAction cutAction = null;
    private AbstractAction copyAction = null;
    private AbstractAction pasteAction = null;
    private AbstractAction undoAction = null;
    private AbstractAction redoAction = null;

    private Editable focusOwner;
    private HelpEngine helpEngine;

    public static enum QuoteMode {
        // the old mode with many sharps before each quotation mark
        SHARP,
        // the sharps are placed automatically. The sharps can still
        // be placed to force the level.
        INTUITIVE
    }

    public static QuoteMode quoteMode = QuoteMode.INTUITIVE;

    /**
     * Creates a new instance of EditorFrame with the specified configuration
     *
     * @param the
     *            current configuration
     */
    public EditorFrame(Configuration configuration, HelpEngine helpEngine) {
        this.configuration = configuration;
        this.helpEngine = helpEngine;
        loadParameters();
        initialize();
    }

    /**
     * Loads all the required parameters that are written in the
     * tangara.properties file.
     */
    public void loadParameters() {
        // This parameter must be loaded here and not later. We take note of the
        // font to use.
        font = new Font(configuration.getProperty("editor.font"), Font.PLAIN, //$NON-NLS-1$
                Integer.parseInt(configuration.getProperty("editor.fontSize"))); //$NON-NLS-1$

        // We read the default difficulty level from the configuration file.
        // We get back the value of tangara.level (cf. tangara properties).
        interfaceLevel = Integer.parseInt(configuration.getProperty("tangara.level")); //$NON-NLS-1$

        // We read the default activation of writing help.
        writingHelp = configuration.getProperty("popup.display").equals("1"); //$NON-NLS-1$ //$NON-NLS-2$

        // We read the default activation of display line numbers.
        displayLineNumbers = configuration.getProperty("lineNumbers.display").equals("1"); //$NON-NLS-1$ //$NON-NLS-2$
        TextPane.setDisplayLineNumbers(displayLineNumbers);

        // We read the default quote mode from the configuration file.
        // We get back the value of tangara.quoteMode (cf. tangara properties).
        String mode = configuration.getProperty("quote.mode"); //$NON-NLS-1$
        setQuoteMode(mode);

        // We find which operating system is used.
        String os = System.getProperty("os.name").toLowerCase(); //$NON-NLS-1$
        if (os.indexOf("mac") != -1) //$NON-NLS-1$
            commandModifier = java.awt.event.InputEvent.META_DOWN_MASK;
        else
            commandModifier = java.awt.event.InputEvent.CTRL_DOWN_MASK;

        tabSize = Integer.parseInt(configuration.getProperty("tab.size")); //$NON-NLS-1$
    }

    /**
     * This method changes the standard size of a TAB for a given JTextPane.
     *
     * @param the
     *            JTextPane where to apply this method.
     */
    public void setTabSizeOf(JTextPane textPane) {
        int spacesPerTab = tabSize;
        FontMetrics fm = textPane.getFontMetrics(textPane.getFont());
        int charWidth = fm.charWidth(' ');
        int tabWidth = charWidth * spacesPerTab;

        TabStop[] tabStops = new TabStop[200];

        for (int j = 0; j < tabStops.length; j++) {
            int tab = j + 1;
            tabStops[j] = new TabStop(tab * tabWidth);
        }

        TabSet tabSet = new TabSet(tabStops);

        Style style = textPane.getLogicalStyle();
        StyleConstants.setTabSet(style, tabSet);
        textPane.setLogicalStyle(style);
    }

    /**
     * Gets the current font
     *
     * @return the used font
     */
    public Font getFontParameter() {
        return font;
    }

    /**
     * Returns the current mode (command or program)
     *
     * @return true if we are in command mode, false in program mode
     */
    public boolean getMode() {
        return commandMode;
    }

    /**
     * Returns the pane in command mode
     *
     * @return the pane in command mode
     */
    public TextPane catchEditorPane() {
        return commandPane;
    }

    /**
     * Prints the "Welcome" message
     *
     */
    private void showWelcomeMessage() {
        String message = Messages.getString("EditorFrame.welcome"); //$NON-NLS-1$
        Program.instance().writeMessage(message);
    }

    /**
     * Clears the code editor pane
     *
     */
    public void clearCommandPane() {
        commandPane.getBuffer().remove(0, commandPane.getBufferLength());
    }

    /**
     * Gets the code in <code>commandPane</code> and launches its interpretation
     *
     */
    public void executeInputScript() {
        // move to the beginning document
        commandPane.moveCaretPosition(0);

        String commands = ""; //$NON-NLS-1$
        switch (quoteMode) {
        case SHARP:
            commands = commandPane.getText();
            break;

        case INTUITIVE:
            commands = commandPane.getText();
            commands = StringParser.addQuoteDelimiters(commands);
            break;
        }

        // executes the commands with reference to the default GameArea object
        commands = commands.trim();
        programIndex = -1;
        Program.instance().executeScript(commands);
        clearCommandPane();
        // and get the focus
        commandPane.requestFocusInWindow();
        commandHistory = 0;
    }

    /**
     * Gets the code in <code>programPane</code> and launches its interpretation
     */
    public void executeProgram(String commands) {
        executeProgram(commands, -1);
    }

    public void executeProgram(String commands, int currentIndex) {
        programIndex = currentIndex;
        refresh();
        optionPanel.setCommandMode();

        if (quoteMode == QuoteMode.INTUITIVE)
            commands = StringParser.addQuoteDelimiters(commands);

        commands = commands.trim();
        if (Program.instance().getDesignMode())
            optionPanel.changeDesignMode();
        Program.instance().executeScript(commands);

    }

    /**
     * Initializes the banner and the line mode before displaying the welcome
     * message
     */
    @Override
    public void afterInit() {
        // move back the separator of the basis panel
        jSplitPane1.setDividerLocation(DIVIDER1_LOCATION);
        banner.setCommandMode();
        ((java.awt.CardLayout) modePanel.getLayout()).show(modePanel, "commandMode"); //$NON-NLS-1$
        setInterfaceLevel(interfaceLevel);
        showWelcomeMessage();
        commandPane.requestFocusInWindow();
        commandMode = true;
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                // places the second separator.
                jSplitPane2.setDividerLocation(DIVIDER2_LOCATION);
                programDividerLocation = new int[2];
                // first time : set dividerLocation to fullScreen
                programDividerLocation[0] = 0;
                programDividerLocation[1] = jSplitPane2.getDividerLocation() + jSplitPane1.getDividerLocation();
            }
        });
    }

    /**
     * Selects the command mode, simpler than the program mode. It allows the
     * user to see immediately the effect of the typed commands.
     */
    public void setCommandMode() {
        // We check if we are not already in this mode.
        if (!commandMode) {
            if (programDividerLocation != null) {
                programDividerLocation[0] = jSplitPane1.getDividerLocation();
                programDividerLocation[1] = jSplitPane2.getDividerLocation();
            }
            Runnable commandModeRun = new Runnable() {
                @Override
                public void run() {
                    graphicsPane.freeze(false);
                    ((java.awt.CardLayout) modePanel.getLayout()).show(modePanel, "commandMode"); //$NON-NLS-1$
                    banner.setCommandMode();
                    commandPane.requestFocusInWindow();
                    commandMode = true;
                    console.disableDragAndDrop();
                    if (popupManager != null)
                        popupManager.closePopup();
                    if (commandDividerLocation != null) {
                        jSplitPane1.setDividerLocation(commandDividerLocation[0]);
                        // InvokeLater is required here, otherwise this second
                        // setDividerLocation is ignored
                        SwingUtilities.invokeLater(new Runnable() {
                            @Override
                            public void run() {
                                jSplitPane2.setDividerLocation(commandDividerLocation[1]);
                            }
                        });
                    }
                }
            };
            if (SwingUtilities.isEventDispatchThread()) {
                commandModeRun.run();
            } else {
                try {
                    SwingUtilities.invokeAndWait(commandModeRun);
                } catch (InterruptedException e) {
                    LOG.error(e);
                } catch (InvocationTargetException e) {
                    LOG.error(e);
                }
            }
        }
    }

    /**
     * Selects program mode, more complex than the command mode. It allows the
     * user to insert commands from the history list.
     */
    public void setProgramMode() {
        // We check if we are not already in this mode.
        if (commandMode) {
            if (commandDividerLocation == null) {
                commandDividerLocation = new int[2];
            }
            commandDividerLocation[0] = jSplitPane1.getDividerLocation();
            commandDividerLocation[1] = jSplitPane2.getDividerLocation();
            Runnable programModeRun = new Runnable() {
                @Override
                public void run() {
                    graphicsPane.freeze(true);
                    ((java.awt.CardLayout) modePanel.getLayout()).show(modePanel, "programMode"); //$NON-NLS-1$
                    banner.setProgramMode();
                    getProgramManager().requestFocus();
                    commandMode = false;
                    console.enableDragAndDrop();
                    if (popupManager != null)
                        popupManager.closePopup();
                    if (programDividerLocation != null) {
                        jSplitPane1.setDividerLocation(programDividerLocation[0]);
                        // InvokeLater is required here, otherwise this second
                        // setDividerLocation is ignored
                        SwingUtilities.invokeLater(new Runnable() {
                            @Override
                            public void run() {
                                jSplitPane2.setDividerLocation(programDividerLocation[1]);
                            }
                        });
                    }
                }
            };
            if (SwingUtilities.isEventDispatchThread()) {
                programModeRun.run();
            } else {
                try {
                    SwingUtilities.invokeAndWait(programModeRun);
                } catch (InterruptedException e) {
                    LOG.error(e);
                } catch (InvocationTargetException e) {
                    LOG.error(e);
                }
            }
        }
    }

    /**
     * Allows to choose which files to export
     * @throws IOException
     *
     */
    public void exportProgram() throws IOException {
        graphicsPane.freeze(true);
        fileChooserWithoutFilter.setCurrentDirectory(getProgramManager().getCurrentDirectory());
        fileChooserWithoutFilter.setSelectedFile(new File("")); //$NON-NLS-1$
        fileChooserWithoutFilter.setDialogTitle(Messages.getString("EditorFrame.file.open.title")); //$NON-NLS-1$
        int returnVal = fileChooserWithoutFilter.showOpenDialog(this);
        if (returnVal == JFileChooser.APPROVE_OPTION) {
            File selected = fileChooserWithoutFilter.getSelectedFile();
            (new FileSelection(this, true, selected)).setVisible(true);
        }
        graphicsPane.freeze(false);
    }

    /**
     * Allows to choose which file to open
     *
     */
    public void openFile() {
        graphicsPane.freeze(true);
        fileChooser.setSelectedFile(new File(getProgramManager().getCurrentDirectory(), ".tgr")); //$NON-NLS-1$
        fileChooser.setDialogTitle(Messages.getString("EditorFrame.file.open.title")); //$NON-NLS-1$
        int returnVal = fileChooser.showOpenDialog(this);
        if (returnVal == JFileChooser.APPROVE_OPTION) {
            String scriptPathname = fileChooser.getSelectedFile().getAbsolutePath();
            if (!FileUtils.isTangaraFile(scriptPathname)) {
                JOptionPane.showMessageDialog(this,
                        MessageFormat.format(Messages.getString("EditorFrame.file.open.error.fileType"), //$NON-NLS-1$
                                scriptPathname),
                        Messages.getString("EditorFrame.file.open.error.title"), JOptionPane.ERROR_MESSAGE); //$NON-NLS-1$
                return;
            }

            try {
                File scriptFile = new File(scriptPathname);
                String script = loadScript(scriptFile);

                int index = getProgramManager().getFileIndex(scriptFile);
                if (index > -1) {
                    // The file is already open : we just select the corresponding tab
                    getProgramManager().setSelectedIndex(index);
                } else {
                    // The file is not already open : we add a pane
                    getProgramManager().addPane(scriptFile, script);
                }
                setCurrentDirectory(scriptFile.getParentFile());
                // Reset undo manager
                programResetUndo();
                if (commandMode) {
                    executeProgram(script);
                }
            } catch (Throwable th) {
                JOptionPane.showMessageDialog(this,
                        MessageFormat.format(Messages.getString("EditorFrame.file.open.error.fileOpen"), //$NON-NLS-1$
                                scriptPathname),
                        Messages.getString("EditorFrame.file.open.error.title"), JOptionPane.ERROR_MESSAGE); //$NON-NLS-1$
                LOG.error("Unable to read file " + scriptPathname, th); //$NON-NLS-1$
            }
        }
        graphicsPane.freeze(false);
    }

    private String loadScript(File sourceFile) throws IOException {
        ScriptReader reader = new ScriptReader();
        String script = reader.readScript(sourceFile);
        return script;
    }

    /**
     * Allows to create a new program when you choose "Make program" in the file
     * menu from the command historic. Only in command mode
     *
     */
    public void makeProgram() {
        graphicsPane.freeze(true);
        (new CommandSelection(this, true, CommandSelection.MODE_PROGRAM_CREATION)).setVisible(true);
        graphicsPane.freeze(false);
    }

    /**
     * Only in program mode. Allows to save your program.
     *
     */
    public void setConfiguration() {
        graphicsPane.freeze(true);
        (new ConfigurationWindow(this)).setVisible(true);
        graphicsPane.freeze(false);
    }

    /**
     * Opens the Library Window and freezes temporarily the GraphicsPane.
     */
    public void makeLibrary() {
        graphicsPane.freeze(true);
        (new LibraryWindow(this)).setVisible(true);
        graphicsPane.freeze(false);
    }

    /**
     * Opens the About Window and freezes temporarily the GraphicsPane.
     */
    public void makeAbout() {
        graphicsPane.freeze(true);
        Image backgroundImage = Main.getSplashScreenImage().getImage();
        AboutWindow aboutDlg = new AboutWindow(this, backgroundImage);
        aboutDlg.setVisible(true);
        graphicsPane.freeze(false);
    }

    /**
     * Only in program mode. Allows to save your program.
     *
     */
    public void saveProgram() {
        File currentFile = getProgramManager().getCurrentFile();
        if (currentFile == null)
            saveProgramAs();
        else
            saveProgram(currentFile);
    }

    /**
     * Only in program mode. Saves the program according to the chosen
     * destination file
     *
     * @param destinationFile
     *            the destination file
     */
    private void saveProgram(File destinationFile) {
        try {
            saveBufferToFile(destinationFile);
            succeedToSaveFile(destinationFile);
            getProgramManager().setCurrentFile(destinationFile);
        } catch (Throwable th) {
            failToSaveFile(destinationFile, th);
        }
    }

    private void saveBufferToFile(File destinationFile) throws IOException {
        String script = getProgramManager().getCurrentContents();
        OutputStream out = null;
        try {
            out = new FileOutputStream(destinationFile);
            ScriptWriter writer = new ScriptWriter(Configuration.instance().getScriptHeader());
            writer.writeScript(script, out);
        } finally {
            IOUtils.closeQuietly(out);
        }

    }

    private void succeedToSaveFile(File destinationFile) {
        printInfo(MessageFormat.format(Messages.getString("EditorFrame.program.saveOk"), destinationFile //$NON-NLS-1$
                .getName()));
        //currentProgram = destinationFile;
        getProgramManager().setCurrentPaneModified(false);
        setCurrentDirectory(destinationFile.getParentFile());
    }

    private void failToSaveFile(File destinationFile, Throwable th) {
        LOG.error("Unable to write to program file \"" //$NON-NLS-1$
                + destinationFile.getAbsolutePath() + "\"", th); //$NON-NLS-1$
        String msgPattern = Messages.getString("EditorFrame.program.saveError"); //$NON-NLS-1$
        printError(MessageFormat.format(msgPattern, destinationFile.getName()));
    }

    /**
     * Only in program mode. Allows to save your program by selecting the
     * filename
     *
     */
    public void saveProgramAs() {
        graphicsPane.freeze(true);
        String dialogTitle = Messages.getString("EditorFrame.program.save.title");//$NON-NLS-1$

        if (getProgramManager().getCurrentFile() == null)
            fileChooser.setSelectedFile(new File(getProgramManager().getCurrentDirectory(),
                    Messages.getString("EditorFrame.file.newFile"))); //$NON-NLS-1$
        else
            fileChooser.setSelectedFile(getProgramManager().getCurrentFile());
        fileChooser.setDialogTitle(dialogTitle);
        int returnVal = fileChooser.showSaveDialog(this);
        if (returnVal == JFileChooser.APPROVE_OPTION) {
            File destinationFile = fileChooser.getSelectedFile();
            String fileName = destinationFile.getAbsolutePath();
            if (FileUtils.isTangaraFile(fileName) == false) {
                fileName = fileName + '.' + FileUtils.getTangaraFileExt();
                destinationFile = new File(fileName);
            }
            if (destinationFile.exists()) {
                if (userConfirmFileOverride(destinationFile) == false) {
                    return;
                }
            }
            saveProgram(destinationFile);
        }
        graphicsPane.freeze(false);
    }

    private boolean userConfirmFileOverride(File destinationFile) {
        String messagePattern = Messages.getString("EditorFrame.program.override.message"); //$NON-NLS-1$
        String message = MessageFormat.format(messagePattern, destinationFile.getName());
        String title = Messages.getString("EditorFrame.program.override.title"); //$NON-NLS-1$
        Object[] options = { Messages.getString("tangara.yes"), Messages.getString("tangara.cancel") }; //$NON-NLS-1$ //$NON-NLS-2$
        Icon icon = null;
        int messageType = JOptionPane.QUESTION_MESSAGE;
        int answer = JOptionPane.showOptionDialog(EditorFrame.this, message, title, optionType, messageType, icon,
                options, options[0]);
        return answer == JOptionPane.OK_OPTION;
    }

    /**
     * Creates a new tab and set the text as newContent.
     *
     * @param newContent
     *            the text to set in the new tab's programPane.
     */
    public void newProgram(String newContent) {
        getProgramManager().addPane(Program.instance().getCurrentDirectory(), newContent);
        // Reset undo manager
        programResetUndo();
    }

    /**
     * Inserts commands from history
     *
     */
    public void insertCommandsFromHistory() {
        graphicsPane.freeze(true);
        (new CommandSelection(this, true, CommandSelection.MODE_COMMANDS_INSERTION)).setVisible(true);
        graphicsPane.freeze(false);
    }

    /**
     * Inserts commands to the program (in program mode) (launched by command
     * selection)
     *
     * @param commands
     *            the list of commands to insert
     */
    public void insertCommands(String commands) {
        if (commands.length() == 0)
            return;
        if (commandMode) {
            optionPanel.setProgramMode();
            newProgram(commands);
        } else {
            TextPane tp = getProgramManager().getCurrentPane();
            String previousText = tp.getText();
            int carretPosition = tp.getCaretPosition();
            int firstLine = tp.getFirstLine();

            String textBefore = ""; //$NON-NLS-1$
            for (int i = 0; i < carretPosition; i++) {
                textBefore += previousText.charAt(i);
            }

            String textAfter = ""; //$NON-NLS-1$
            for (int i = carretPosition; i < previousText.length(); i++) {
                textAfter += previousText.charAt(i);
            }
            if (carretPosition == 0 || previousText.charAt(carretPosition - 1) == '\n') {
                tp.setText(textBefore + commands + textAfter);
                tp.moveCaretPosition(carretPosition + commands.length(), TextArea.NO_SCROLL);
            } else {
                tp.setText(textBefore + "\n" + commands + textAfter); //$NON-NLS-1$
                tp.moveCaretPosition(carretPosition + commands.length() + 1, TextArea.NO_SCROLL);
            }
            int newLine = tp.getCaretLine();
            if (newLine - firstLine + 2 > tp.getVisibleLines()) {
                tp.setFirstLine(newLine - tp.getVisibleLines() + 2);
            } else {
                tp.setFirstLine(firstLine);
            }
        }
        setProgramMode();
    }

    public void closePane() {
        getProgramManager().closeCurrentPane();
    }

    public void newPane() {
        getProgramManager().addPane();
        programResetUndo();
    }

    /**
     * Gets the file chooser of Tangara frame
     *
     * @return javax.swing.JFileChooser
     */
    public JFileChooser getFileChooser() {
        return fileChooser;
    }

    /**
     * Copies the selected area (from command editor pane, program pane, or
     * output pane).
     *
     */
    public void copy() {
        if (focusOwner != null)
            focusOwner.copy();
    }

    /**
     * Cuts the selected area (from command editor pane, program pane, or output
     * pane).
     *
     */
    public void cut() {
        if (focusOwner != null)
            focusOwner.cut();
    }

    /**
     * Paste the content of the clipboard system in the selected pane (according
     * to the mode)
     *
     */
    public void paste() {
        if (focusOwner != null)
            focusOwner.paste();
    }

    public void enableEdit() {
        if (cutAction != null)
            cutAction.setEnabled(true);
        if (copyAction != null)
            copyAction.setEnabled(true);
    }

    public void disableEdit() {
        if (cutAction != null)
            cutAction.setEnabled(false);
        if (copyAction != null)
            copyAction.setEnabled(false);
    }

    /**
     * Quit Tangara (close the window and the program).
     *
     */
    public void exit() {
        graphicsPane.freeze(true);
        String title = Messages.getString("EditorFrame.exit.title"); //$NON-NLS-1$
        String message = Messages.getString("EditorFrame.exit.message"); //$NON-NLS-1$
        Object[] options = { Messages.getString("tangara.yes"), //$NON-NLS-1$
                Messages.getString("tangara.cancel") }; //$NON-NLS-1$
        int answer = JOptionPane.showOptionDialog(this, message, title, optionType, JOptionPane.QUESTION_MESSAGE,
                null, // do not use a custom Icon
                options, // the titles of buttons
                options[0]);

        if (answer == JOptionPane.OK_OPTION) {
            if (getProgramManager().checkProgramChanged()) {
                dispose();
                Program.instance().exit();
            }
        } else
            graphicsPane.freeze(false);
    }

    /**
     * Prints the error passed as parameters (with a newline)
     *
     * @param text
     *            the error to print
     */
    public void printError(String text) {
        Program.instance().printError(text);
    }

    /**
     * Prints the message passed as parameters (with a newline)
     *
     * @param text
     *            the message to print
     */
    public void printInfo(String text) {
        Program.instance().printOutputMessage(text);
    }

    /**
     * Gets the current directory of the file chooser
     *
     * @return java.io.File the path of the directory
     */
    public File getCurrentDirectory() {
        return fileChooser.getCurrentDirectory();
    }

    /**
     * Sets the current directory for the file chooser
     *
     * @param directory
     *            the new directory
     */
    public void setCurrentDirectory(File directory) {
        fileChooser.setCurrentDirectory(directory);
        Program.instance().setCurrentDirectory(directory);
    }

    /**
     * Sets the writing help value
     *
     * @param value
     */
    public void setWritingHelp(boolean value) {
        writingHelp = value;
    }

    /**
     * Gets the writing help value
     *
     * @return writingHelp
     */
    public boolean getWritingHelp() {
        return writingHelp;
    }

    /**
     * Displays the popup manager (to add methods) after "."
     *
     * @param pane
     *            the pane in Tangara where you are typing your code
     * @param aClass
     *            the class of the object that calls one of its methods
     */
    private void setPopupDisplay(TextPane pane, Class<?> aClass) {
        if (popupManager != null) {
            popupManager.setVisible(false);
        }
        popupManager = new PopupManager(this, pane, aClass);
        popupManager.start();
    }

    /**
     * Stop displaying the popup manager designed to choose methods.
     */
    public void stopPopupDisplay() {
        if (popupManager != null) {
            popupManager.setVisible(false);
            popupManager = null;
        }
    }

    /**
     * Sets the interface level in the options bar and the banner
     *
     * @param level
     *            advanced or basic
     */
    public void setInterfaceLevel(int level) {
        if (level == Configuration.LEVEL_ADVANCED) {
            optionPanel.setVisible(true);
            msgButtonsMainPanel.setVisible(true);
            banner.setAdvancedLevel();
            console.setEnabled(true);
        } else // any other value : LEVEL_BASIC
        {
            //if (checkProgramChanged()) {
            optionPanel.setCommandMode();
            optionPanel.setVisible(false);
            msgButtonsMainPanel.setVisible(false);
            banner.setBasicLevel();
            console.setEnabled(false);
            //}
        }
        interfaceLevel = level;
    }

    /**
     * Gets the interface level
     *
     * @return the interface level (basic or advanced)
     */
    public int getInterfaceLevel() {
        return interfaceLevel;
    }

    /**
     * Changes the quote mode in accordance with the given string. There are
     * three possible modes: SHARP, INTUITIVE, COLOR
     *
     * @param newMode
     */
    public void setQuoteMode(String newMode) {
        if (newMode.equals("SHARP")) { //$NON-NLS-1$
            quoteMode = QuoteMode.SHARP;
        } else if (newMode.equals("INTUITIVE")) { //$NON-NLS-1$
            quoteMode = QuoteMode.INTUITIVE;
        } else {
            LOG.error("Unknown quote mode"); //$NON-NLS-1$
            Program.instance().writeMessage("Error - Unknown quote mode."); //$NON-NLS-1$
        }
    }

    /**
     * Shows the program pane when you are in advanced level
     *
     */
    public void showProgram() {
        if (getInterfaceLevel() > Configuration.LEVEL_BASIC) {
            optionPanel.setProgramMode();
        }
    }

    /**
     * window listener to quit
     */
    private WindowListener windowListener = new WindowAdapter() {
        @Override
        public void windowClosing(WindowEvent evt) {
            exit(); // @jve:decl-index=0:
        }
    };

    // /////////// THE FRAME DESIGN STARTS HERE

    /**
     * This method initializes the design of the frame.
     *
     */
    private void initialize() {
        GraphicsEnvironment graphicsEnvironment = GraphicsEnvironment.getLocalGraphicsEnvironment();
        Rectangle bounds = graphicsEnvironment.getMaximumWindowBounds();
        setPreferredSize(bounds.getSize());

        this.setJMenuBar(getBanner());

        this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);

        // Sets the main menu (the one separated by jsplitPane1)
        this.setContentPane(getBasePanel());
        this.setTitle(Messages.getString("EditorFrame.application.title")); //$NON-NLS-1$
        addWindowListener(this.windowListener);
        try {
            // Associates the icon (frame.icon = icon_tangara.png) to Tangara
            URL url = EditorFrame.class.getResource(ICON_PATH);
            MediaTracker attenteChargement = new MediaTracker(this);
            Image image = Toolkit.getDefaultToolkit().getImage(url);
            attenteChargement.addImage(image, 0);
            attenteChargement.waitForAll();
            setIconImage(image);
        } catch (InterruptedException e) {
            LOG.warn("Error while loading icon"); //$NON-NLS-1$
        }
        // fileChooser allows to easily choose a file
        // when you open (FILE->OPEN...) you have the choice between .txt or
        // .tgr
        fileChooser = new JFileChooser(Program.instance().getCurrentDirectory());

        // for TangaraFile ".tgr"
        fileChooser.addChoosableFileFilter(new FileFilter() {
            @Override
            public boolean accept(File f) {
                if (f.isDirectory())
                    return true;
                return FileUtils.isTangaraFile(f);
            }

            @Override
            public String getDescription() {
                return Messages.getString("EditorFrame.file.programFilesDescription"); //$NON-NLS-1$
            }
        });

        fileChooserWithoutFilter = new JFileChooser(Program.instance().getCurrentDirectory());
        pack();
        setVisible(true);
        setExtendedState(java.awt.Frame.MAXIMIZED_BOTH);
    }

    /**
     * Returns the cut action
     *
     * @return
     */
    public Action getCutAction() {
        if (cutAction == null) {
            cutAction = new AbstractAction(Messages.getString("Banner.menu.cut")) { //$NON-NLS-1$

                @Override
                public void actionPerformed(ActionEvent e) {
                    cut();
                }
            };
            cutAction.setEnabled(false);
            cutAction.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("control X"));
        }
        return cutAction;
    }

    /**
     * Returns the copy action
     *
     * @return
     */
    public Action getCopyAction() {
        if (copyAction == null) {
            copyAction = new AbstractAction(Messages.getString("Banner.menu.copy")) { //$NON-NLS-1$

                @Override
                public void actionPerformed(ActionEvent e) {
                    copy();
                }
            };
            copyAction.setEnabled(false);
            copyAction.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("control C"));
        }
        return copyAction;
    }

    /**
     * Returns the paste action
     *
     * @return
     */
    public Action getPasteAction() {
        if (pasteAction == null) {
            pasteAction = new AbstractAction(Messages.getString("Banner.menu.paste")) { //$NON-NLS-1$

                @Override
                public void actionPerformed(ActionEvent e) {
                    paste();
                }
            };
            pasteAction.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("control V"));
        }

        return pasteAction;
    }

    /**
     * Returns the undo action
     *
     * @return
     */
    public Action getUndoAction() {
        if (undoAction == null) {
            undoAction = new AbstractAction(Messages.getString("Banner.menu.undo")) { //$NON-NLS-1$

                @Override
                public void actionPerformed(ActionEvent e) {
                    programUndo();
                }
            };
            undoAction.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("control Z"));
        }
        return undoAction;
    }

    /**
      * Returns the undo action
      *
      * @return
      */
    public Action getRedoAction() {
        if (redoAction == null) {
            redoAction = new AbstractAction(Messages.getString("Banner.menu.redo")) { //$NON-NLS-1$

                @Override
                public void actionPerformed(ActionEvent e) {
                    programRedo();
                }
            };
            redoAction.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("control Y"));
        }
        return redoAction;
    }

    /**
     * This method initializes jSplitPane1
     *
     * @return javax.swing.JSplitPane
     */
    private JSplitPane getJSplitPane1() {
        if (jSplitPane1 == null) {
            jSplitPane1 = new JSplitPane();
            jSplitPane1.setOrientation(JSplitPane.VERTICAL_SPLIT);
            jSplitPane1.setTopComponent(getGraphicsPane());
            jSplitPane1.setBottomComponent(getControlPanel());
            jSplitPane1.setDividerSize(5);
        }
        return jSplitPane1;
    }

    /**
     * This method initializes basePanel
     *
     * @return javax.swing.JPanel
     */
    private JPanel getBasePanel() {
        if (basePanel == null) {
            basePanel = new JPanel();
            basePanel.setLayout(new BorderLayout());
            basePanel.add(getJSplitPane1(), BorderLayout.CENTER);
        }
        return basePanel;
    }

    /**
     * This method initializes controlPanel
     *
     * @return javax.swing.JPanel
     */
    private JPanel getControlPanel() {
        if (controlPanel == null) {
            controlPanel = new JPanel();
            controlPanel.setLayout(new BorderLayout());
            controlPanel.add(getJSplitPane2(), BorderLayout.CENTER);
        }
        return controlPanel;
    }

    /**
     * This method initializes jSplitPane2
     *
     * @return javax.swing.JSplitPane
     */
    private JSplitPane getJSplitPane2() {
        if (jSplitPane2 == null) {
            jSplitPane2 = new JSplitPane();
            jSplitPane2.setDividerSize(5);
            jSplitPane2.setResizeWeight(1.0D);
            jSplitPane2.setTopComponent(getEditionPanel());

            jSplitPane2.setBottomComponent(getPageEnd());

            //jSplitPane2.setBottomComponent(getMsgScrollPane());
            jSplitPane2.setOrientation(JSplitPane.VERTICAL_SPLIT);
        }
        return jSplitPane2;
    }

    /**
     * This method initializes pageEnd
     *
     * @return javax.swing.JPanel
     */
    private JPanel getPageEnd() {
        if (pageEnd == null) {
            pageEnd = new JPanel();
            pageEnd.setLayout(new BorderLayout());

            scrollPane = new JScrollPane(getConsole());

            pageEnd.add(scrollPane, BorderLayout.CENTER);

            pageEnd.add(getMsgButtonsMainPanel(), BorderLayout.NORTH);
        }
        return pageEnd;
    }

    /**
     * This method initializes msgButtonsPanel
     *
     * @return javax.swing.JPanel
     */
    private JPanel getMsgButtonsMainPanel() {
        if (msgButtonsMainPanel == null) {
            msgButtonsMainPanel = new JPanel();
            msgButtonsMainPanel.setLayout(new BorderLayout());

            msgButtonsMainPanel.setBackground(Color.white);

            msgButtonsMainPanel.setSize(new Dimension(811, 34));
            msgButtonsMainPanel.setPreferredSize(new Dimension(84, 25));
            msgButtonsMainPanel.add(getMsgButtonsPanel(), BorderLayout.WEST);
        }
        return msgButtonsMainPanel;
    }

    /**
     * This method initializes msgButtonsPanel
     *
     * @return javax.swing.JPanel
     */
    private JPanel getMsgButtonsPanel() {
        if (msgButtonsPanel == null) {
            msgButtonsPanel = new JPanel();
            msgButtonsPanel.setLayout(new BorderLayout());
            msgButtonsPanel.setBackground(Color.white);

            msgButtonsPanel.add(getMsgButtons(), BorderLayout.WEST);

            JLabel endIcon = new JLabel();
            endIcon.setText(""); //$NON-NLS-1$
            endIcon.setBackground(Color.white);
            endIcon.setIcon(new ImageIcon(getClass().getResource("/org/colombbus/tangara/main_end.png"))); //$NON-NLS-1$
            msgButtonsPanel.add(endIcon, BorderLayout.EAST);
        }
        return msgButtonsPanel;
    }

    /**
     * This method initializes console
     *
     * @return org.colombbus.tangara.Console
     */
    private LogConsole getConsole() {
        if (console == null) {
            console = new LogConsole(this);
            console.addListSelectionListener(new ListSelectionListener() {
                @Override
                public void valueChanged(ListSelectionEvent e) {
                    if (toProgramLabel != null) {
                        if (console.isCodeSelected()) {
                            if (!toProgramLabel.isEnabled()) {
                                toProgramLabel.setEnabled(true);
                                toProgramLabel.setIcon(new ImageIcon(
                                        getClass().getResource("/org/colombbus/tangara/to_program.png"))); //$NON-NLS-1$
                            }
                        } else if (toProgramLabel.isEnabled()) {
                            toProgramLabel.setEnabled(false);
                            toProgramLabel.setIcon(new ImageIcon(
                                    getClass().getResource("/org/colombbus/tangara/to_program_off.png"))); //$NON-NLS-1$
                        }
                    }
                }
            });
        }
        return console;
    }

    /**
     * This method initializes msgButtons
     *
     * @return javax.swing.JPanel
     */
    private JPanel getMsgButtons() {
        if (msgButtons == null) {
            toProgramLabel = new JLabel();
            toProgramLabel.setText(Messages.getString("EditorFrame.button.copyInProgramMode")); //$NON-NLS-1$
            toProgramLabel.setFont(new Font("Lucida Grande", Font.PLAIN, 13)); //$NON-NLS-1$
            toProgramLabel.setForeground(new Color(60, 87, 174));
            toProgramLabel
                    .setIcon(new ImageIcon(getClass().getResource("/org/colombbus/tangara/to_program_off.png"))); //$NON-NLS-1$
            toProgramLabel.setEnabled(false);
            toProgramLabel.addMouseListener(new java.awt.event.MouseAdapter() {
                @Override
                public void mousePressed(java.awt.event.MouseEvent e) {
                    console.insertCodeToProgram();
                }

                @Override
                public void mouseExited(java.awt.event.MouseEvent e) {
                    if (!noCodeSelected) {
                        setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
                        toProgramLabel.setForeground(new Color(60, 87, 174));
                    }
                }

                @Override
                public void mouseEntered(java.awt.event.MouseEvent e) {
                    if (!noCodeSelected) {
                        setCursor(new Cursor(Cursor.HAND_CURSOR));
                        toProgramLabel.setForeground(new Color(100, 100, 255));
                    }
                }
            });

            selectAllLabel = new JLabel();
            selectAllLabel.setText(Messages.getString("EditorFrame.button.selectAll")); //$NON-NLS-1$
            selectAllLabel.setFont(new Font("Lucida Grande", Font.PLAIN, 13)); //$NON-NLS-1$
            selectAllLabel.setForeground(new Color(60, 87, 174));
            selectAllLabel.setIcon(new ImageIcon(getClass().getResource("/org/colombbus/tangara/select_all.png"))); //$NON-NLS-1$
            selectAllLabel.addMouseListener(new java.awt.event.MouseAdapter() {
                @Override
                public void mousePressed(java.awt.event.MouseEvent e) {
                    console.selectAll();
                }

                @Override
                public void mouseExited(java.awt.event.MouseEvent e) {
                    setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
                    selectAllLabel.setForeground(new Color(60, 87, 174));
                }

                @Override
                public void mouseEntered(java.awt.event.MouseEvent e) {
                    setCursor(new Cursor(Cursor.HAND_CURSOR));
                    selectAllLabel.setForeground(new Color(100, 100, 255));
                }
            });

            deselectAllLabel = new JLabel();
            deselectAllLabel.setText(Messages.getString("EditorFrame.button.deselectAll")); //$NON-NLS-1$
            deselectAllLabel.setFont(new Font("Lucida Grande", Font.PLAIN, 13)); //$NON-NLS-1$
            deselectAllLabel.setForeground(new Color(60, 87, 174));
            deselectAllLabel
                    .setIcon(new ImageIcon(getClass().getResource("/org/colombbus/tangara/select_none.png"))); //$NON-NLS-1$
            deselectAllLabel.addMouseListener(new java.awt.event.MouseAdapter() {
                @Override
                public void mousePressed(java.awt.event.MouseEvent e) {
                    console.clearSelection();
                }

                @Override
                public void mouseExited(java.awt.event.MouseEvent e) {
                    setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
                    deselectAllLabel.setForeground(new Color(60, 87, 174));
                }

                @Override
                public void mouseEntered(java.awt.event.MouseEvent e) {
                    setCursor(new Cursor(Cursor.HAND_CURSOR));
                    deselectAllLabel.setForeground(new Color(100, 100, 255));
                }
            });

            JLabel separatorLabel1 = new JLabel("        "); //$NON-NLS-1$
            JLabel separatorLabel2 = new JLabel("        "); //$NON-NLS-1$

            msgButtons = new JPanel();
            msgButtons.setLayout(new FlowLayout());
            msgButtons.setBackground(new Color(240, 240, 240));
            msgButtons.add(toProgramLabel);
            msgButtons.add(separatorLabel1);
            msgButtons.add(selectAllLabel);
            msgButtons.add(separatorLabel2);
            msgButtons.add(deselectAllLabel);
        }
        return msgButtons;
    }

    /**
     * This method writes a message on the MsgTable
     *
     */
    @Override
    public void addLogMsg(String message, int style, int lineNumber) {
        if (style == LogConsole.STYLE_ERROR && lineNumber > -1 && programIndex > -1) {
            message = MessageFormat.format(Messages.getString("EditorFrame.error.lineNumber"), message,
                    lineNumber + 1);
        }
        console.log(message, style, lineNumber, programIndex);
    }

    @Override
    public int getCurrentLogIndex() {
        return console.getCurrentIndex();
    }

    @Override
    public void setErrorLines(int index, int number, int errorLineNumber) {
        console.setErrors(index, number, errorLineNumber, programIndex);
    }

    /**
     * This method initializes jPanel2
     *
     * @return javax.swing.JPanel
     */
    private JPanel getCommandPanel() {
        if (commandPanel == null) {
            commandPanel = new JPanel();
            commandPanel.setLayout(new BorderLayout());
            commandPanel.add(getCommandPane(), BorderLayout.CENTER);
            commandPanel.add(getCmdButtonPanel(), BorderLayout.EAST);
        }
        return commandPanel;
    }

    /**
     * This method initializes cmdButtonPanel
     *
     * @return javax.swing.JPanel
     */
    private JPanel getCmdButtonPanel() {
        if (cmdButtonPanel == null) {
            GridBagConstraints gridBagConstraints1 = new GridBagConstraints();
            gridBagConstraints1.insets = new Insets(4, 9, 18, 4);
            gridBagConstraints1.gridy = 1;
            gridBagConstraints1.ipadx = 60;
            gridBagConstraints1.ipady = -1;
            gridBagConstraints1.gridx = 0;
            GridBagConstraints gridBagConstraints = new GridBagConstraints();
            gridBagConstraints.insets = new Insets(16, 9, 4, 4);
            gridBagConstraints.gridy = 0;
            gridBagConstraints.ipadx = 49;
            gridBagConstraints.ipady = -1;
            gridBagConstraints.gridx = 0;
            cmdButtonPanel = new JPanel();
            cmdButtonPanel.setBackground(new Color(197, 214, 219));//Color(156, 199, 213));
            cmdButtonPanel.setBorder(BorderFactory.createLineBorder(new Color(102, 102, 102), 1));
            cmdButtonPanel.setPreferredSize(new Dimension(150, 140));
            cmdButtonPanel.setLayout(new GridBagLayout());
            cmdButtonPanel.add(getCmdRunButton(), gridBagConstraints);
            cmdButtonPanel.add(getRefreshButton(), gridBagConstraints1);
        }
        return cmdButtonPanel;
    }

    /**
     * This method initializes cmdRunButton
     *
     * @return javax.swing.JButton
     */
    private JButton getCmdRunButton() {
        if (cmdRunButton == null) {
            cmdRunButton = new JButton();
            cmdRunButton.setForeground(new Color(51, 51, 51));
            cmdRunButton.setBackground(new Color(156, 199, 213));
            cmdRunButton.setFont(new Font("Lucida Grande", Font.PLAIN, 13)); //$NON-NLS-1$
            cmdRunButton
                    .setIcon(new ImageIcon(getClass().getResource("/org/colombbus/tangara/control_play_blue.png"))); //$NON-NLS-1$
            cmdRunButton.setPreferredSize(new Dimension(86, 30));
            cmdRunButton.setText(Messages.getString("EditorFrame.button.execute")); //$NON-NLS-1$
            cmdRunButton.addActionListener(new java.awt.event.ActionListener() {
                @Override
                public void actionPerformed(java.awt.event.ActionEvent e) {
                    executeInputScript();
                }
            });
        }
        return cmdRunButton;
    }

    /**
     * This method initializes refreshButton
     *
     * @return javax.swing.JButton
     */
    private JButton getRefreshButton() {
        if (refreshButton == null) {
            refreshButton = new JButton();
            refreshButton.setBackground(new Color(156, 199, 213));
            refreshButton.setText(Messages.getString("EditorFrame.button.clean")); //$NON-NLS-1$
            refreshButton.setFont(new Font("Lucida Grande", Font.PLAIN, 13)); //$NON-NLS-1$
            refreshButton.setIcon(new ImageIcon(getClass().getResource("/org/colombbus/tangara/cross.png"))); //$NON-NLS-1$
            refreshButton.setPreferredSize(new Dimension(75, 30));
            refreshButton.setForeground(new Color(51, 51, 51));
            refreshButton.addActionListener(new java.awt.event.ActionListener() {
                @Override
                public void actionPerformed(java.awt.event.ActionEvent e) {
                    graphicsPane.freeze(true);
                    String message = Messages.getString("EditorFrame.confirmClean.text"); //$NON-NLS-1$
                    String title = Messages.getString("EditorFrame.confirmClean.title"); //$NON-NLS-1$
                    Object[] options = { Messages.getString("tangara.yes"), Messages.getString("tangara.cancel") }; //$NON-NLS-1$ //$NON-NLS-2$
                    int answer = JOptionPane.showOptionDialog(EditorFrame.this, message, title, optionType,
                            JOptionPane.QUESTION_MESSAGE, null, options, options[0]);
                    if (answer == JOptionPane.OK_OPTION) {
                        refresh();
                    }
                    graphicsPane.freeze(false);
                    commandPane.requestFocus();
                }
            });
        }
        return refreshButton;
    }

    /**
     * This method refreshs the state of Tangara : delete every object and erase
     * history.
     *
     */
    private void refresh() {
        Program.instance().reset();
        clearCommandPane();
        console.clear();
        commandHistory = 0;
    }

    /**
     * This method initializes and returns commandPane, for the command mode
     * window.
     *
     * @return javax.swing.JEditorPane
     */
    public TextPane getCommandPane() {
        if (commandPane == null) {
            commandPane = new TextPane(this, TextPane.getProperties(true), true);
            commandPane.setBorder(BorderFactory.createLineBorder(new Color(102, 102, 102), 1));
        }
        return commandPane;
    }

    public void historyUp() {
        commandHistory++;
        String previousCommand = Program.instance().getCommandFromHistory(commandHistory);
        if (previousCommand != null) {
            clearCommandPane();
            commandPane.getBuffer().insert(0, previousCommand);
        } else // end of history has been reached
        {
            commandHistory--;
        }
    }

    public void historyDown() {
        if (commandHistory > 0) {
            commandHistory--;
            if (commandHistory == 0) {
                clearCommandPane();
            } else {
                String nextCommand = Program.instance().getCommandFromHistory(commandHistory);
                if (nextCommand != null) {
                    clearCommandPane();
                    commandPane.getBuffer().insert(0, nextCommand);
                }
            }
        }
    }

    public void clearCommand() {
        if (commandHistory > 0) {
            commandHistory = 0;
            clearCommandPane();
        }
    }

    public void displayWritingHelp(Class<?> objectClass) {
        if (writingHelp) {
            if (commandMode)
                setPopupDisplay(commandPane, objectClass);
            else
                setPopupDisplay(getProgramManager().getCurrentPane(), objectClass);
        }
    }

    /**
     * Get panel containing the game panel
     *
     * @return the panel containing the game area
     */
    @Override
    public GraphicsPane getGraphicsPane() {
        if (graphicsPane == null) {
            try {
                try {
                    String language = Configuration.instance().getLanguage();
                    String className = "org.colombbus.tangara." + language //$NON-NLS-1$
                            + ".GraphicsPane_" + language; //$NON-NLS-1$
                    Class<?> type = Class.forName(className);
                    graphicsPane = (GraphicsPane) type.newInstance();

                } catch (ClassNotFoundException e) {
                    // If the language is unknown, the English version used.
                    String className = "org.colombbus.tangara.en.GraphicsPane_en"; //$NON-NLS-1$
                    Class<?> type;
                    type = Class.forName(className);
                    graphicsPane = (GraphicsPane) type.newInstance();
                }
            } catch (Exception e) {
                LOG.error("error get GraphisPane", e); //$NON-NLS-1$
            }

            graphicsPane.setLayout(null);
            graphicsPane.setBackground(Color.white);
        }
        return graphicsPane;
    }

    /**
     * Initializes the banner of Tangara window
     *
     * @return Tangara banner
     */
    private Banner getBanner() {
        if (banner == null) {
            banner = new Banner(this, helpEngine);
        }
        return banner;
    }

    /**
     * This method initializes editionPanel
     *
     * @return javax.swing.JPanel
     */
    private JPanel getEditionPanel() {
        if (editionPanel == null) {
            editionPanel = new JPanel();
            editionPanel.setLayout(new BorderLayout());
            editionPanel.add(getOptionPanel(), BorderLayout.NORTH);
            editionPanel.add(getModePanel(), BorderLayout.CENTER);
        }
        return editionPanel;
    }

    /**
     * This method initializes modePanel
     *
     * @return javax.swing.JPanel
     */
    private JPanel getModePanel() {
        if (modePanel == null) {
            modePanel = new JPanel();
            modePanel.setLayout(new CardLayout());
            modePanel.setBackground(new Color(220, 220, 220));
            modePanel.add(getCommandPanel(), "commandMode"); //$NON-NLS-1$
            modePanel.add(getProgramManager(), "programMode"); //$NON-NLS-1$
        }
        return modePanel;
    }

    /**
     * This method initializes optionPanel
     *
     * @return org.colombbus.tangara.OptionPanel
     */
    private OptionPanel getOptionPanel() {
        if (optionPanel == null) {
            optionPanel = new OptionPanel(this);
        }
        return optionPanel;
    }

    public TextPaneManager getProgramManager() {
        if (programManager == null) {
            programManager = new TextPaneManager(this);
            programManager.setPreferredSize(new Dimension(410, 50));
        }
        return programManager;
    }

    /**
     * Undo the last command
     *
     */
    public void programUndo() {
        getProgramManager().getCurrentPane().undo();
    }

    /**
     * Redo the last command
     *
     */
    public void programRedo() {
        getProgramManager().getCurrentPane().redo();
    }

    public void enableUndo() {
        if (undoAction != null)
            undoAction.setEnabled(true);
    }

    public void disableUndo() {
        if (undoAction != null)
            undoAction.setEnabled(false);
    }

    public void enableRedo() {
        if (redoAction != null)
            redoAction.setEnabled(true);
    }

    public void disableRedo() {
        if (redoAction != null)
            redoAction.setEnabled(false);
    }

    /**
     * Resets all undo managers
     *
     */
    public void programResetUndo() {
        // Reset undo manager
        undoAction.setEnabled(false);
        redoAction.setEnabled(false);
    }

    @Override
    public boolean computeSize() {
        // Do not compute the size of the main frame
        return false;
    }

    public void displaySearch() {
        getProgramManager().displaySearch();
    }

    public Action getSearchAction() {
        return getProgramManager().getSearchAction();
    }

    public CommandTransferHandler getCommandHandler() {
        if (commandHandler == null) {
            commandHandler = new CommandTransferHandler();
        }
        return commandHandler;
    }

    public void setFocusOwner(Editable component) {
        focusOwner = component;
        if (component.mayCopy())
            this.enableEdit();
        else
            this.disableEdit();
    }

    public void setDisplayLineNumbers(boolean state) {
        programManager.setDisplayLineNumbers(state);
        displayLineNumbers = state;
    }

    public boolean getDisplayLineNumbers() {
        return displayLineNumbers;
    }

    public void selectLine(int programIndex, int lineNumber) {
        // 1st set Program Mode
        setProgramMode();
        // 2nd ask the manager to select and show the required line
        programManager.selectLine(programIndex, lineNumber);
    }
}