/* * Created on February 16, 2007 * * Copyright (c) 2007 Jens Gulden * * http://www.frinika.com * * This file is part of Frinika. * * Frinika is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * Frinika 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 Frinika; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package com.frinika.project.scripting.gui; import com.frinika.project.scripting.FrinikaScript; import com.frinika.project.scripting.FrinikaScriptingEngine; import com.frinika.project.scripting.DefaultFrinikaScript; import com.frinika.project.scripting.ScriptListener; import javax.swing.JInternalFrame; import javax.swing.ImageIcon; import java.awt.event.KeyEvent; import java.awt.Color; import java.io.File; import java.io.IOException; /** * A simple text-editor for script source-code. * * (Created with NetBeans 5.5 gui-editor, see corresponding .form file.) * * @see ScriptingDialog * @author Jens Gulden */ class ScriptEditorInternalFrame extends JInternalFrame implements ScriptListener, Runnable { final int EXTERNAL_CHANGE_TEST_INTERVAL = 250; // milliseconds to test whether file externally overwritten protected FrinikaScript script; protected boolean dirty; protected ScriptingDialog dialog; protected long lastSaveTimestamp = 0; /** Creates new form ScriptEditorInternalFrame */ public ScriptEditorInternalFrame(FrinikaScript script, ScriptingDialog dialog) { super(); this.script = script; this.dialog = dialog; dirty = false; initComponents(); if (! (script instanceof DefaultFrinikaScript) ) { editorPane.setEditable( false ); editorPane.setBackground(new Color(240, 240, 240)); } getRootPane().setDefaultButton(runButton); //runStopButtonGroup.add(runToggleButton); //runStopButtonGroup.add(stopToggleButton); scriptExited(script, null); // to set initial state of buttons /*editorPane.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { if ( ! editorPane.isEditable() ) return; if ( ( e.isControlDown() ) && (e.getKeyCode() == KeyEvent.VK_ENTER) ) { // Ctrl-Enter: execute script runButton.doClick(); e.consume(); } else { //setDirty(true); } } @Override public void keyTyped(KeyEvent e) { char c = e.getKeyChar(); if (Character.isDefined(c)) { // 'real' character has been entered setDirty(true); } } }); editorPane.addFocusListener(new FocusAdapter() { public void focusLost(FocusEvent e) { update(); } });*/ /*editorPane.getDocument().addDocumentListener(new DocumentListener() { public void changedUpdate(DocumentEvent e) { setDirty(true); } public void insertUpdate(DocumentEvent e) { setDirty(true); } public void removeUpdate(DocumentEvent e) { setDirty(true); } });*/ refresh(); dialog.engine.addScriptListener(this); //editorPane.requestFocus(); if ( script instanceof DefaultFrinikaScript ) { String filename = ((DefaultFrinikaScript)script).getFilename(); if (filename != null) { File file = new File(filename); if (file.exists()) { lastSaveTimestamp = file.lastModified(); } } (new Thread(this)).start(); // watchdog for external changes } } public void toFront() { super.toFront(); editorPane.requestFocus(); } void setDirty(boolean d) { if (dirty != d) { dirty = d; updateTitle(); } } boolean hasBeenModifiedWithoutSaving() { return dirty; } boolean hasBeenModifiedByExternalApplication() { if (script instanceof DefaultFrinikaScript) { if (lastSaveTimestamp > 0) { String filename = ((DefaultFrinikaScript)script).getFilename(); File file = new File(filename); return (file.lastModified() != lastSaveTimestamp); } else { return false; } } else { return false; } } FrinikaScript getScript() { return script; } void setScript(FrinikaScript script) { this.script = script; refresh(); setDirty(false); } void reload() { try { //dialog.engine.reloadScript((DefaultFrinikaScript)script); DefaultFrinikaScript script = ((DefaultFrinikaScript)this.script); String filename = script.getFilename(); File file = new File(filename); String source = FrinikaScriptingEngine.loadString(file); script.setSource(source); refresh(); lastSaveTimestamp = file.lastModified(); setDirty(false); } catch (IOException ioe) { dialog.frame.error(ioe); } } public void refresh() { editorPane.setText(script.getSource()); updateTitle(); } public void update() { if (script instanceof DefaultFrinikaScript) { ((DefaultFrinikaScript)script).setSource(editorPane.getText()); } } protected void updateTitle() { /*String t = this.getTitle(); boolean e = t.endsWith(" *"); if ( (!dirty) && e ) { t = t.substring(0, t.length() - 2); } else if ( dirty && (!e) ) { t = t + " *"; }*/ String t = script.getName(); if (dirty) { t = t + " *"; } if (! t.equals(this.getTitle()) ) { this.setTitle(t); dialog.updateMenus(); } } public void scriptStarted(FrinikaScript script) { //System.out.println("script started "+script); if (script == this.script) { //runToggleButton.setSelected(true); runButton.setEnabled(false); stopButton.setEnabled(true); } } public void scriptExited(FrinikaScript script, Object returnValue) { //System. out.println("script exited "+script); if (script == this.script) { //stopToggleButton.setSelected(true); runButton.setEnabled(true); stopButton.setEnabled(false); } } /** * Background thread for watching external changes. */ public void run() { String filename = ((DefaultFrinikaScript)script).getFilename(); //System. out.println("starting watchdog thread for " + filename); while ( ! this.isClosed() ) { if ( hasBeenModifiedByExternalApplication() ) { if ( dialog.frame.confirm("Script " + filename + " has been modified by an external application. Reload?") ) { if ( ( ! hasBeenModifiedWithoutSaving() ) || dialog.frame.confirm( "This will DESTROY local changes. Reload anyway?" ) ) { reload(); } } else { // don't reload, leave external changes for now lastSaveTimestamp = 0; setDirty(true); } } try { Thread.sleep( EXTERNAL_CHANGE_TEST_INTERVAL ); } catch (InterruptedException ie) { // nop } } //System. out.println("watchdog thread for " + ((DefaultFrinikaScript)script).getFilename() + " has ended"); } /* update(); dialog.engine.executeScript(this.getScript(), dialog.frame); dialog.engine.stopScript(this.getScript()); */ /** This method is called from within the constructor to * initialize the form. * WARNING: Do NOT modify this code. The content of this method is * always regenerated by the Form Editor. */ // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents private void initComponents() { scrollPane = new javax.swing.JScrollPane(); editorPane = new javax.swing.JEditorPane(); toolBar = new javax.swing.JToolBar(); jPanel1 = new javax.swing.JPanel(); runButton = new javax.swing.JButton(); stopButton = new javax.swing.JButton(); setClosable(true); setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE); setIconifiable(true); setMaximizable(true); setResizable(true); editorPane.setFont(new java.awt.Font("DialogInput", 0, 12)); editorPane.addFocusListener(new java.awt.event.FocusAdapter() { public void focusLost(java.awt.event.FocusEvent evt) { editorPaneFocusLost(evt); } }); editorPane.addKeyListener(new java.awt.event.KeyAdapter() { public void keyPressed(java.awt.event.KeyEvent evt) { editorPaneKeyPressed(evt); } public void keyTyped(java.awt.event.KeyEvent evt) { editorPaneKeyTyped(evt); } }); scrollPane.setViewportView(editorPane); getContentPane().add(scrollPane, java.awt.BorderLayout.CENTER); jPanel1.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT, 1, 0)); runButton.setIcon(new ImageIcon(ClassLoader.getSystemResource("icons/play.png"))); runButton.setToolTipText("Ctrl-Enter"); runButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { runButtonActionPerformed(evt); } }); jPanel1.add(runButton); stopButton.setIcon(new ImageIcon(ClassLoader.getSystemResource("icons/stop.png"))); stopButton.setMnemonic('S'); stopButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { stopButtonActionPerformed(evt); } }); jPanel1.add(stopButton); toolBar.add(jPanel1); getContentPane().add(toolBar, java.awt.BorderLayout.NORTH); pack(); }// </editor-fold>//GEN-END:initComponents private void editorPaneKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_editorPaneKeyPressed if ( ! editorPane.isEditable() ) return; if ( ( evt.isControlDown() ) && (evt.getKeyCode() == KeyEvent.VK_ENTER) ) { // Ctrl-Enter: execute script runButton.doClick(); evt.consume(); } else { //setDirty(true); } }//GEN-LAST:event_editorPaneKeyPressed private void editorPaneKeyTyped(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_editorPaneKeyTyped //char c = evt.getKeyChar(); //if (Character.isDefined(c)) { // 'real' character has been entered setDirty(true); //} }//GEN-LAST:event_editorPaneKeyTyped private void editorPaneFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_editorPaneFocusLost update(); }//GEN-LAST:event_editorPaneFocusLost private void runButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_runButtonActionPerformed update(); dialog.executeScript(script); }//GEN-LAST:event_runButtonActionPerformed private void stopButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_stopButtonActionPerformed dialog.stopScript(script); }//GEN-LAST:event_stopButtonActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JEditorPane editorPane; private javax.swing.JPanel jPanel1; private javax.swing.JButton runButton; private javax.swing.JScrollPane scrollPane; private javax.swing.JButton stopButton; private javax.swing.JToolBar toolBar; // End of variables declaration//GEN-END:variables }