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.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JCheckBoxMenuItem;
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.KeyStroke;
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;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
import org.fife.ui.rtextarea.RTextScrollPane;
public class RSyntaxBSHCodeFrame extends JFrame
{
/**The code editing component in this frame.*/
private RSyntaxTextArea theTextArea;
/**The code editing component in the form code panel associated with this frame.*/
private RSyntaxTextArea externalTextArea;
/**Context attribute: specifies which type of panel it is, used to know which code templates are available on menus*/
private String context;
/**Save button*/
private JButton savButton = new JButton(UIMessages.getInstance().getMessage("button.sav"));
/**Cancel button*/
private JButton canButton = new JButton(UIMessages.getInstance().getMessage("button.can"));
/**The associated form code panel*/
private RSyntaxBSHCodePanel codePanel = null;
public String getContext()
{
return context;
}
public void refresh()
{
updateFontSize();
theTextArea.setDocument(externalTextArea.getDocument());
//restoreSearchDialogs(); //jsyntax-specific
if ( codePanel != null ) this.setTitle(codePanel.getPanelName());
}
/**
* Label for line numbers
*/
private final JLabel lineNumLabel = new JLabel(" : ");
/**
* Gets the caret row to show in line number label
* @param comp
* @return
*/
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;
}
/**
* Gets the caret column to shown in column number label
* @param comp
* @return
*/
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;
}
/**
* Updates the line number label
*/
private void updateLineNumberLabel()
{
int line = getCaretRowPosition(theTextArea);
int column = getCaretColumnPosition(theTextArea);
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();
/**
* Closes all open RSyntaxBSHCodeFrames
*/
public static void closeAllInstances()
{
for ( int i = 0 ; i < instances.size() ; i++ )
{
RSyntaxBSHCodeFrame bcf = (RSyntaxBSHCodeFrame) instances.get(i);
bcf.codePanel.unsetCodeFrame();
bcf.dispose();
}
instances.clear();
}
/**
* Obtains all the instances. Used for validation.
* @return
*/
public static List getAllInstances() //used for validation
{
return instances;
}
//public JSyntaxBSHCodeFrame( String title , JEditorPane toWriteTo )
public RSyntaxBSHCodeFrame( String title , RSyntaxTextArea toWriteTo , String context , RSyntaxBSHCodePanel rSyntaxBSHCodePanel )
{
this.codePanel = rSyntaxBSHCodePanel;
this.context = context;
instances.add(this);
setTitle(title);
setSize(600,600);
externalTextArea = toWriteTo;
theTextArea = RSyntaxTextAreaRegistry.getInstance().createLargeTextArea();
RTextScrollPane scrPane = new RTextScrollPane(theTextArea);
scrPane.setFoldIndicatorEnabled(true);
refresh();
getContentPane().setLayout( new BorderLayout() );
getContentPane().add ( scrPane , BorderLayout.CENTER );
JPanel southPanel = new JPanel(); //( new GridLayout(1,5) );
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 )
{
//Markers.removeMarkers(theTextArea); //this was for jsyntax
externalTextArea.setDocument(theTextArea.getDocument());
//saveSearchDialogs(); //this was for jsyntax
//RSyntaxBSHCodeFrame.this.codePanel.restoreSearchDialogs(); //this was for jsyntax
setVisible(false);
}
}
);
canButton.addActionListener ( new ActionListener()
{
public void actionPerformed ( ActionEvent evt )
{
//saveSearchDialogs(); //this was for jsyntax
setVisible(false);
}
}
);
//jep.addPopupMenu(CodeAssistMenuHandler.getInstance().getMenuForContext(context, new CodeInsertActionBuilder(jep)));
theTextArea.addCaretListener ( new CaretListener()
{
public void caretUpdate(CaretEvent e)
{
updateLineNumberLabel();
}
}
);
setDefaults ( theTextArea );
updateFontSize();
theTextArea.add(CodeAssistMenuHandler.getInstance().getMenuForContext(context, new CodeInsertActionBuilder(theTextArea)));
configureMenus ( theTextArea , context );
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();
}
});
//theTextArea.getComponentPopupMenu().add(fontSize);
theTextArea.getPopupMenu().add(new JSeparator());
theTextArea.getPopupMenu().add(fontSize);
updateFontSize();
}
/**
* Sets default options for an RSyntaxTextArea.
* @param theTextArea
*/
public static void setDefaults ( RSyntaxTextArea theTextArea )
{
//jep.setContentType("text/java");
theTextArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA);
theTextArea.setCodeFoldingEnabled(true);
theTextArea.setAntiAliasingEnabled(true);
theTextArea.setMarkOccurrences(true);
theTextArea.setTabSize(4);
theTextArea.setLineWrap(RSyntaxOption.getInstanceFor("rsyntaxWordWrap").isOptionEnabled());
theTextArea.setPaintTabLines(RSyntaxOption.getInstanceFor("rsyntaxShowTabLines").isOptionEnabled());
theTextArea.setTabsEmulated(RSyntaxOption.getInstanceFor("rsyntaxTabsEmulated").isOptionEnabled());
//theTextArea.setTabsEmulated(true);
}
/**
* Adds the keybindings and menus to an RSyntaxTextArea.
* @param theTextArea
* @param context
*/
public static void configureMenus ( RSyntaxTextArea theTextArea , String context )
{
Action findDialogAction = new RSyntaxShowFindDialogAction(theTextArea);
Action replaceDialogAction = new RSyntaxShowReplaceDialogAction(theTextArea);
Action findNextAction = new RSyntaxFindNextOrPrevAction(theTextArea,true);
Action findPrevAction = new RSyntaxFindNextOrPrevAction(theTextArea,false);
//find/replace keybindings
theTextArea.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_F,InputEvent.CTRL_DOWN_MASK),"showFindDialog");
theTextArea.getActionMap().put("showFindDialog", findDialogAction);
theTextArea.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_H,InputEvent.CTRL_DOWN_MASK),"showReplaceDialog");
theTextArea.getActionMap().put("showReplaceDialog", replaceDialogAction);
theTextArea.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_F3,0),"findNext");
theTextArea.getActionMap().put("findNext", findNextAction);
theTextArea.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_F3,InputEvent.SHIFT_DOWN_MASK),"findPrev");
theTextArea.getActionMap().put("findPrev", findPrevAction);
//popup menu options
theTextArea.getPopupMenu().add(new JSeparator());
theTextArea.getPopupMenu().add(findDialogAction);
theTextArea.getPopupMenu().add(replaceDialogAction);
theTextArea.getPopupMenu().add(findNextAction);
theTextArea.getPopupMenu().add(findPrevAction);
//getComponentPopupMenu() not supported by RSyntaxTextArea for java 1.4 compatibility reasons, so this won't fly:
//theTextArea.getComponentPopupMenu().add(CodeAssistMenuHandler.getInstance().getMenuForContext(context, new CodeInsertActionBuilder(theTextArea)),0);
//theTextArea.getComponentPopupMenu().add(new JSeparator(),1);
//and instead we use this:
theTextArea.getPopupMenu().add(CodeAssistMenuHandler.getInstance().getMenuForContext(context, new CodeInsertActionBuilder(theTextArea)),0);
theTextArea.getPopupMenu().add(new JSeparator(),1);
}
/**
* Configure the view options affecting both areas at the same time.
* @param frameArea
* @param panelArea
*/
public static void configureSyncedMenus ( RSyntaxTextArea frameArea , RSyntaxTextArea panelArea )
{
RSyntaxOptionToggleAction wordWrapToggleAction = RSyntaxOptionToggleAction.getInstanceFor(UIMessages.getInstance().getMessage("rsyntax.wrap"), "rsyntaxWordWrap");
RSyntaxOptionToggleAction tabLinesToggleAction = RSyntaxOptionToggleAction.getInstanceFor(UIMessages.getInstance().getMessage("rsyntax.tablines"), "rsyntaxShowTabLines");
RSyntaxOptionToggleAction tabsEmulatedToggleAction = RSyntaxOptionToggleAction.getInstanceFor(UIMessages.getInstance().getMessage("rsyntax.tabs.emulated"), "rsyntaxTabsEmulated");
String[] themeNames = RSyntaxOption.getThemeNames();
RSyntaxOptionToggleAction[] themeToggleActions = new RSyntaxOptionToggleAction[themeNames.length];
for ( int i = 0 ; i < themeNames.length ; i++ )
{
themeToggleActions[i] = RSyntaxOptionToggleAction.getInstanceFor(themeNames[i],"rsyntaxTheme"+themeNames[i]);
}
for ( int i = 0 ; i < themeNames.length ; i++ )
{
themeToggleActions[i].setGroup(themeToggleActions);
}
//No. This is wrong because it was assuming there are only two text areas, where actually there are two PER ENTITY. Need to re-think this.
/*
//RSyntaxWordWrapAction toggleWordWrapAction = new RSyntaxWordWrapAction ( frameArea , panelArea );
RSyntaxOptionToggleAction wordWrapToggleAction = new RSyntaxOptionToggleAction ( frameArea , panelArea , "Word Wrap" , "rsyntaxWordWrap" , new RSyntaxOption(){
public void setOptionEnabled(RSyntaxTextArea ta1,
RSyntaxTextArea ta2, boolean enabled)
{
System.err.println("Setting line wrap to " + enabled + " on " + ta1 + " and " + ta2);
Thread.dumpStack();
ta1.setLineWrap(enabled);
ta2.setLineWrap(enabled);
}
@Override
public boolean isOptionEnabled(RSyntaxTextArea ta1,
RSyntaxTextArea ta2) {
return ta1.getLineWrap();
}
});
*/
RSyntaxTextArea[] areas = new RSyntaxTextArea[] { frameArea , panelArea };
for ( int i = 0 ; i < areas.length ; i++ )
{
areas[i].getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_W,InputEvent.CTRL_DOWN_MASK),"toggleWordWrap");
areas[i].getActionMap().put("toggleWordWrap", wordWrapToggleAction);
areas[i].getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_L,InputEvent.CTRL_DOWN_MASK),"showTabLines");
areas[i].getActionMap().put("showTabLines", tabLinesToggleAction);
areas[i].getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_S,InputEvent.CTRL_DOWN_MASK),"tabsToSpaces");
areas[i].getActionMap().put("tabsToSpaces", tabsEmulatedToggleAction);
areas[i].getPopupMenu().add(new JSeparator());
JMenu viewMenu = new JMenu( UIMessages.getInstance().getMessage("rsyntax.view") );
viewMenu.add(wordWrapToggleAction.getCheckBox());
viewMenu.add(tabLinesToggleAction.getCheckBox());
viewMenu.add(tabsEmulatedToggleAction.getCheckBox());
JMenu themesMenu = new JMenu( UIMessages.getInstance().getMessage("rsyntax.themes") );
for ( int j = 0 ; j < themeToggleActions.length ; j++ )
themesMenu.add(themeToggleActions[j].getCheckBox());
viewMenu.add(themesMenu);
areas[i].getPopupMenu().add ( viewMenu );
}
}
private void updateFontSize()
{
float codeFrameFontSize = getCodeFrameFontSize();
if ( theTextArea.getFont().getSize() != (int)codeFrameFontSize )
theTextArea.setFont(theTextArea.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;
}
/**
* Increments the selected font size for all RSyntaxBSHCodeFrame instances.
*/
private static void incrementFontSize()
{
changeCodeFrameFontSize((float)1.0);
for ( Iterator iter = instances.iterator() ; iter.hasNext() ; )
{
RSyntaxBSHCodeFrame aFrame = (RSyntaxBSHCodeFrame) iter.next();
aFrame.updateFontSize();
}
}
/**
* Decrements the selected font size for all RSyntaxBSHCodeFrame instances.
*/
private static void decrementFontSize()
{
changeCodeFrameFontSize((float)-1.0);
for ( Iterator iter = instances.iterator() ; iter.hasNext() ; )
{
RSyntaxBSHCodeFrame aFrame = (RSyntaxBSHCodeFrame) iter.next();
aFrame.updateFontSize();
}
}
public RSyntaxTextArea getTextArea()
{
return theTextArea;
}
}