package org.f2o.absurdum.puck.gui.panels.code;
import java.awt.BorderLayout;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.JTextComponent;
import javax.swing.text.Utilities;
import jsyntaxpane.actions.DocumentSearchData;
import jsyntaxpane.components.Markers;
import org.f2o.absurdum.puck.gui.codeassist.CodeAssistMenuHandler;
import org.f2o.absurdum.puck.gui.codeassist.CodeInsertActionBuilder;
import org.f2o.absurdum.puck.gui.config.PuckConfiguration;
import org.f2o.absurdum.puck.i18n.UIMessages;
public class JSyntaxBSHCodeFrame extends JFrame
{
private JEditorPane jep;
//private EnhancedJEditTextArea jep;
//private JEditorPane externalJep;
private JEditorPane externalJep;
private JButton savButton = new JButton(UIMessages.getInstance().getMessage("button.sav"));
private JButton canButton = new JButton(UIMessages.getInstance().getMessage("button.can"));
//context attribute: specifies which type of panel it is, used to know which code templates are available on menus
private String context;
private JSyntaxBSHCodePanel codePanel = null;
public String getContext()
{
return context;
}
public void refresh()
{
//jep.setText(externalJep.getText());
updateFontSize();
jep.setDocument(externalJep.getDocument());
restoreSearchDialogs();
if ( codePanel != null ) this.setTitle(codePanel.getPanelName());
//jep.setText(externalJep.getText());
}
final JLabel lineNumLabel = new JLabel(" : ");
private DocumentSearchData searchData = null;
private void saveSearchDialogs()
{
searchData = (DocumentSearchData)jep.getDocument().getProperty("SearchData");
}
private void restoreSearchDialogs()
{
jep.getDocument().putProperty("SearchData",searchData);
}
public static int getCaretRowPosition(JTextComponent comp) {
try {
Rectangle r = comp.modelToView(comp.getCaretPosition());
if ( r == null ) return 0;
int y = r.y;
int line = y; ///getRowHeight(comp);
int lineHeight = comp.getFontMetrics(comp.getFont()).getHeight();
int posLine = (y / lineHeight);
return posLine;
} catch (BadLocationException e) {
}
return -1;
}
public static int getCaretColumnPosition(JTextComponent comp) {
int offset = comp.getCaretPosition();
int column;
try
{
int rowStart = Utilities.getRowStart(comp, offset);
if ( rowStart < 0 ) return 0;
column = offset - rowStart;
}
catch (BadLocationException e)
{
column = -1;
}
return column;
}
private void updateLineNumberLabel()
{
int line = getCaretRowPosition(jep); //jep. .getCaretLine();
//int lineCount = 0; //getCaretColumnPosition(jep); //jep .getLineCount();
int column = getCaretColumnPosition(jep); //jep.getCaretPosition() - jep .getLineStartOffset(line);
lineNumLabel.setText((line+1) + " : " + (column+1));
}
/**
* This instance list will be used to close all code frames on closing a world.
*/
private static List instances = new ArrayList();
public static void closeAllInstances()
{
for ( int i = 0 ; i < instances.size() ; i++ )
{
JSyntaxBSHCodeFrame bcf = (JSyntaxBSHCodeFrame) instances.get(i);
bcf.codePanel.unsetCodeFrame();
bcf.dispose();
}
instances.clear();
}
public static List getAllInstances() //used for validation
{
return instances;
}
//public JSyntaxBSHCodeFrame( String title , JEditorPane toWriteTo )
public JSyntaxBSHCodeFrame( String title , JEditorPane toWriteTo , String context , JSyntaxBSHCodePanel codePanel )
{
//DefaultSyntaxKit.initKit();
this.codePanel = codePanel;
this.context = context;
instances.add(this);
setTitle(title);
setSize(600,600);
externalJep = toWriteTo;
//jep = new JEditorPane();
jep = new JEditorPane();
JScrollPane scrPane = new JScrollPane(jep);
refresh();
getContentPane().setLayout( new BorderLayout() );
getContentPane().add ( scrPane , BorderLayout.CENTER );
JPanel southPanel = new JPanel(); //( new GridLayout(1,5) );
//southPanel.add(new JPanel());
//southPanel.add(new JPanel());
updateLineNumberLabel();
southPanel.setLayout(new BorderLayout());
JPanel lineNumPanel = new JPanel( );
lineNumPanel.add(lineNumLabel);
//lineNumPanel.setMinimumSize(new Dimension(200,100));
//lineNumPanel.setPreferredSize(new Dimension(40,1));
southPanel.add(lineNumLabel,BorderLayout.EAST);
//restore this for save, cancel buttons:
//southPanel.add(savButton);
//southPanel.add(canButton);
//southPanel.add(new JPanel());
//southPanel.add(new JPanel());
getContentPane().add ( southPanel , BorderLayout.SOUTH );
savButton.addActionListener ( new ActionListener()
{
public void actionPerformed ( ActionEvent evt )
{
//externalJep.setText(jep.getText());
Markers.removeMarkers(jep);
externalJep.setDocument(jep.getDocument());
//jep.getDocument().putProperty("SearchData",null); //with this we discard the find/replace dialog instances associated with the document.
saveSearchDialogs();
JSyntaxBSHCodeFrame.this.codePanel.restoreSearchDialogs();
//TODO: remove the previous line when JSyntaxPane is updated so that dialogs are associated to (document,editor) pairs rather than to documents.
setVisible(false);
}
}
);
canButton.addActionListener ( new ActionListener()
{
public void actionPerformed ( ActionEvent evt )
{
saveSearchDialogs();
setVisible(false);
}
}
);
System.out.println("The context: " + context);
//jep.addPopupMenu(CodeAssistMenuHandler.getInstance().getMenuForContext(context, new CodeInsertActionBuilder(jep)));
jep.addCaretListener ( new CaretListener()
{
public void caretUpdate(CaretEvent e)
{
updateLineNumberLabel();
//lineNumLabel.setText("line: " + (jep.getCaretLine()+1) + "/" + jep.getLineCount());
}
}
);
jep.setContentType("text/java");
//PuckConfiguration.getInstance().setProperty("fontSizeProperty", fontSizeProperty);
updateFontSize();
jep.add(CodeAssistMenuHandler.getInstance().getMenuForContext(context, new CodeInsertActionBuilder(jep)));
//jep.getEditorKit().
jep.getComponentPopupMenu().add(CodeAssistMenuHandler.getInstance().getMenuForContext(context, new CodeInsertActionBuilder(jep)),0);
jep.getComponentPopupMenu().add(new JSeparator(),1);
JMenuItem moreFontSize = new JMenuItem(UIMessages.getInstance().getMessage("menu.font.more"));
JMenuItem lessFontSize = new JMenuItem(UIMessages.getInstance().getMessage("menu.font.less"));
JMenu fontSize = new JMenu(UIMessages.getInstance().getMessage("menu.font.size"));
fontSize.add(moreFontSize);
fontSize.add(lessFontSize);
moreFontSize.addActionListener(new ActionListener(){
public void actionPerformed ( ActionEvent evt )
{
incrementFontSize();
}
});
lessFontSize.addActionListener(new ActionListener(){
public void actionPerformed ( ActionEvent evt )
{
decrementFontSize();
}
});
jep.getComponentPopupMenu().add(fontSize);
}
private void updateFontSize()
{
float codeFrameFontSize = getCodeFrameFontSize();
if ( jep.getFont().getSize() != (int)codeFrameFontSize )
jep.setFont(jep.getFont().deriveFont((float)codeFrameFontSize));
}
private static void changeCodeFrameFontSize(float increment)
{
float currentSize = getCodeFrameFontSize();
float newSize = currentSize + increment;
PuckConfiguration.getInstance().setProperty("codeFrameFontSize", String.valueOf(newSize));
}
private static float getCodeFrameFontSize()
{
float codeFrameFontSize = (float) 18.0;
String fontSizeProperty = PuckConfiguration.getInstance().getProperty("codeFrameFontSize");
try
{
codeFrameFontSize = Float.valueOf(fontSizeProperty).floatValue();
}
catch ( NumberFormatException nfe )
{
System.err.println("Warning: invalid value for codeFrameFontSize property, defaulting to 18.0");
}
return codeFrameFontSize;
}
private void incrementFontSize()
{
changeCodeFrameFontSize((float)1.0);
updateFontSize();
}
private void decrementFontSize()
{
changeCodeFrameFontSize((float)-1.0);
updateFontSize();
}
}