mirror of
https://github.com/status-im/ethereumj-personal.git
synced 2025-02-03 15:44:51 +00:00
Adapting for Ethereym as a library style:
+ adding gui files from the split
This commit is contained in:
parent
a7ef7d4f7e
commit
39528c10ba
@ -0,0 +1,141 @@
|
||||
package org.ethereum.gui;
|
||||
|
||||
import org.ethereum.core.Block;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.table.DefaultTableCellRenderer;
|
||||
import javax.swing.table.TableCellRenderer;
|
||||
import java.awt.*;
|
||||
import java.awt.datatransfer.Clipboard;
|
||||
import java.awt.datatransfer.StringSelection;
|
||||
import java.awt.event.*;
|
||||
|
||||
/**
|
||||
* www.ethereumJ.com
|
||||
* @author: Roman Mandeleil
|
||||
* Created on: 15/05/14 12:36
|
||||
*/
|
||||
public class BlockChainTable extends JFrame {
|
||||
|
||||
private JPanel topPanel;
|
||||
private JTable table;
|
||||
private JScrollPane scrollPane;
|
||||
|
||||
private int lastFindIndex = 0;
|
||||
|
||||
ToolBar toolBar;
|
||||
|
||||
public BlockChainTable(ToolBar toolBar) {
|
||||
|
||||
this.toolBar = toolBar;
|
||||
addCloseAction();
|
||||
|
||||
final BlockChainTable blockchainTable = this;
|
||||
|
||||
setTitle("Block Chain Table");
|
||||
setSize(700, 400);
|
||||
setLocation(315, 180);
|
||||
setBackground(Color.gray);
|
||||
|
||||
java.net.URL url = ClassLoader.getSystemResource("ethereum-icon.png");
|
||||
Toolkit kit = Toolkit.getDefaultToolkit();
|
||||
Image img = kit.createImage(url);
|
||||
this.setIconImage(img);
|
||||
|
||||
// Create a panel to hold all other components
|
||||
topPanel = new JPanel();
|
||||
topPanel.setLayout(new BorderLayout());
|
||||
getContentPane().add(topPanel);
|
||||
|
||||
// Create a new table getInstance()
|
||||
table = new JTable();
|
||||
table.setModel(new BlockTableModel());
|
||||
|
||||
table.setFont(new Font("Courier New", Font.PLAIN, 13));
|
||||
table.setForeground(Color.GRAY);
|
||||
table.setTableHeader(null);
|
||||
|
||||
TableCellRenderer tcr = table.getDefaultRenderer(String.class);
|
||||
DefaultTableCellRenderer renderer = (DefaultTableCellRenderer) tcr;
|
||||
renderer.setHorizontalAlignment(SwingConstants.LEFT);
|
||||
renderer.setVerticalAlignment(SwingConstants.TOP);
|
||||
|
||||
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
table.setCellSelectionEnabled(true);
|
||||
|
||||
table.setRowMargin(3);
|
||||
table.setRowHeight(420);
|
||||
|
||||
table.getColumnModel().getColumn(0).setCellRenderer(new TableCellLongTextRenderer());
|
||||
|
||||
table.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_C, KeyEvent.CTRL_DOWN_MASK), "Copy");
|
||||
table.getActionMap().put("Copy", new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
|
||||
if (UIEthereumManager.ethereum.getBlockChainSize() - 1 < lastFindIndex) return;
|
||||
|
||||
Block block = UIEthereumManager.ethereum.getBlockByIndex(lastFindIndex);;
|
||||
StringSelection stsel = new StringSelection(block.toString());
|
||||
Clipboard system = Toolkit.getDefaultToolkit().getSystemClipboard();
|
||||
system.setContents(stsel,stsel);
|
||||
}
|
||||
});
|
||||
|
||||
table.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_F, KeyEvent.CTRL_DOWN_MASK), "Find");
|
||||
table.getActionMap().put("Find", new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
|
||||
String toFind = JOptionPane.showInputDialog(blockchainTable, "Find:",
|
||||
"Find in BlockChain", JOptionPane.QUESTION_MESSAGE);
|
||||
|
||||
if (toFind.equals("")) {
|
||||
lastFindIndex = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = lastFindIndex + 1; i < UIEthereumManager.ethereum.getBlockChainSize(); ++i) {
|
||||
|
||||
if (UIEthereumManager.ethereum.getBlockChainSize() - 1 < i) return;
|
||||
Block block = UIEthereumManager.ethereum.getBlockByIndex(i);
|
||||
boolean found = block.toString().toLowerCase().contains(toFind.toLowerCase());
|
||||
if (found) {
|
||||
// TODO: now we find the first occur
|
||||
// TODO: in the future I should keep
|
||||
// TODO: all of them and allow to jump over them
|
||||
table.scrollRectToVisible(table.getCellRect(i, 0, true));
|
||||
lastFindIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
table.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
lastFindIndex = ((JTable) (e.getSource())).rowAtPoint(e.getPoint());
|
||||
super.mouseClicked(e);
|
||||
}
|
||||
});
|
||||
// Add the table to a scrolling pane
|
||||
scrollPane = new JScrollPane(table);
|
||||
topPanel.add(scrollPane, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
public void addCloseAction() {
|
||||
this.addWindowListener(new WindowAdapter() {
|
||||
public void windowClosing(WindowEvent e) {
|
||||
toolBar.chainToggle.setSelected(false);
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
BlockChainTable mainFrame = new BlockChainTable(null);
|
||||
mainFrame.setVisible(true);
|
||||
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package org.ethereum.gui;
|
||||
|
||||
import org.ethereum.core.Block;
|
||||
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
|
||||
/**
|
||||
* www.ethereumJ.com
|
||||
*
|
||||
* @author: Roman Mandeleil
|
||||
* Created on: 15/05/14 12:42
|
||||
*/
|
||||
public class BlockTableModel extends AbstractTableModel {
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
|
||||
fireTableDataChanged();
|
||||
int rowCount = (int) UIEthereumManager.ethereum.getBlockChainSize();
|
||||
return rowCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValueAt(int rowIndex, int columnIndex) {
|
||||
|
||||
Block block = UIEthereumManager.ethereum.getBlockByIndex(rowIndex);
|
||||
|
||||
if (block == null) return "";
|
||||
|
||||
return block.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,150 @@
|
||||
package org.ethereum.gui;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import org.ethereum.config.SystemProperties;
|
||||
import org.ethereum.net.PeerListener;
|
||||
import org.ethereum.net.client.ClientPeer;
|
||||
import org.fife.ui.rsyntaxtextarea.*;
|
||||
import org.fife.ui.rtextarea.RTextScrollPane;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* A simple example showing how to modify the fonts and colors used in an
|
||||
* RSyntaxTextArea. There are two methods to do this - via the Java API, and via
|
||||
* an XML file. The latter method is preferred since it's more modular, and
|
||||
* provides a way for your users to customize RSTA in your application.<p>
|
||||
*
|
||||
* This example uses RSyntaxTextArea 2.0.1.<p>
|
||||
*
|
||||
* Project Home: http://fifesoft.com/rsyntaxtextarea<br>
|
||||
* Downloads: https://sourceforge.net/projects/rsyntaxtextarea
|
||||
*/
|
||||
public class ConnectionConsoleWindow extends JFrame implements PeerListener {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(getClass());
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private boolean autoScroll = false;
|
||||
private RSyntaxTextArea textArea;
|
||||
private ToolBar toolBar = null;
|
||||
|
||||
/**
|
||||
* ERROR (exceptions) WARN (when something happens that's not supposed to)
|
||||
* INFO (wire output)
|
||||
* DEBUG (test/displaying intermediate values),
|
||||
* TRACE (start/end method)
|
||||
*/
|
||||
public ConnectionConsoleWindow(ToolBar toolBar) {
|
||||
final ConnectionConsoleWindow thisConsole = this;
|
||||
this.toolBar = toolBar;
|
||||
|
||||
java.net.URL url = ClassLoader.getSystemResource("ethereum-icon.png");
|
||||
Toolkit kit = Toolkit.getDefaultToolkit();
|
||||
Image img = kit.createImage(url);
|
||||
this.setIconImage(img);
|
||||
addCloseAction();
|
||||
|
||||
JPanel cp = new JPanel(new BorderLayout());
|
||||
|
||||
AbstractTokenMakerFactory atmf = (AbstractTokenMakerFactory) TokenMakerFactory.getDefaultInstance();
|
||||
atmf.putMapping("text/console", "org.ethereum.gui.ConsoleTokenMaker");
|
||||
|
||||
textArea = new RSyntaxTextArea(16, 44);
|
||||
textArea.setSyntaxEditingStyle("text/console");
|
||||
// textArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_LISP);
|
||||
textArea.setCodeFoldingEnabled(true);
|
||||
textArea.setAntiAliasingEnabled(true);
|
||||
|
||||
changeStyleProgrammatically();
|
||||
RTextScrollPane sp = new RTextScrollPane(textArea);
|
||||
|
||||
cp.add(sp);
|
||||
|
||||
setContentPane(cp);
|
||||
setTitle("Connection Console");
|
||||
// setDefaultCloseOperation(EXIT_ON_CLOSE);
|
||||
pack();
|
||||
setLocation(802, 460);
|
||||
|
||||
Thread t = new Thread() {
|
||||
public void run() {
|
||||
ClientPeer clientPeer = UIEthereumManager.ethereum.getDefaultPeer();
|
||||
clientPeer.setPeerListener(thisConsole);
|
||||
clientPeer.connect(SystemProperties.CONFIG.activePeerIP(),
|
||||
SystemProperties.CONFIG.activePeerPort());
|
||||
}
|
||||
};
|
||||
t.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void console(final String output) {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
textArea.append(output);
|
||||
textArea.append("\n");
|
||||
|
||||
if (autoScroll)
|
||||
textArea.setCaretPosition(textArea.getText().length());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void changeStyleProgrammatically() {
|
||||
|
||||
// Set the font for all token types.
|
||||
setFont(textArea, new Font("Courier New", Font.PLAIN, 12));
|
||||
|
||||
// Change a few things here and there.
|
||||
SyntaxScheme scheme = textArea.getSyntaxScheme();
|
||||
scheme.getStyle(Token.RESERVED_WORD).background = Color.white;
|
||||
scheme.getStyle(Token.RESERVED_WORD).foreground = Color.MAGENTA.darker().darker();
|
||||
|
||||
scheme.getStyle(Token.DATA_TYPE).foreground = Color.blue;
|
||||
scheme.getStyle(Token.LITERAL_STRING_DOUBLE_QUOTE).underline = true;
|
||||
scheme.getStyle(Token.LITERAL_NUMBER_HEXADECIMAL).underline = true;
|
||||
scheme.getStyle(Token.LITERAL_NUMBER_HEXADECIMAL).background = Color.pink;
|
||||
|
||||
scheme.getStyle(Token.COMMENT_EOL).font = new Font("Georgia",
|
||||
Font.ITALIC, 10);
|
||||
|
||||
textArea.revalidate();
|
||||
}
|
||||
|
||||
public static void setFont(RSyntaxTextArea textArea, Font font) {
|
||||
if (font != null) {
|
||||
SyntaxScheme ss = textArea.getSyntaxScheme();
|
||||
ss = (SyntaxScheme) ss.clone();
|
||||
for (int i = 0; i < ss.getStyleCount(); i++) {
|
||||
if (ss.getStyle(i) != null) {
|
||||
ss.getStyle(i).font = font;
|
||||
}
|
||||
}
|
||||
textArea.setSyntaxScheme(ss);
|
||||
textArea.setFont(font);
|
||||
}
|
||||
}
|
||||
|
||||
public void addCloseAction() {
|
||||
this.addWindowListener(new WindowAdapter() {
|
||||
public void windowClosing(WindowEvent e) {
|
||||
toolBar.logToggle.setSelected(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// Start all Swing applications on the EDT.
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
new ConnectionConsoleWindow(null).setVisible(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,928 @@
|
||||
|
||||
package org.ethereum.gui;
|
||||
|
||||
import javax.swing.text.Segment;
|
||||
|
||||
import org.fife.ui.rsyntaxtextarea.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
/**
|
||||
* www.ethereumJ.com
|
||||
* @author: Roman Mandeleil
|
||||
* Created on: 24/04/14 11:52
|
||||
*/
|
||||
public class ConsoleTokenMaker extends AbstractTokenMaker {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger("gui");
|
||||
|
||||
protected final String operators = "+-*/%!=<>^&|?:";
|
||||
|
||||
protected final String separators = "()[]{}";
|
||||
|
||||
protected final String separators2 = ".,;"; // Characters you don't want syntax highlighted but separate identifiers.
|
||||
|
||||
protected final String hexCharacters = "0123456789ABCDEFabcdef";
|
||||
|
||||
protected final String numberEndChars = "FfLl"; // Characters used to specify literal number types.
|
||||
|
||||
private int currentTokenStart;
|
||||
private int currentTokenType;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public ConsoleTokenMaker() {
|
||||
super(); // Initializes tokensToHighlight.
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks the token to give it the exact ID it deserves before
|
||||
* being passed up to the super method.
|
||||
*
|
||||
* @param segment <code>Segment</code> to get text from.
|
||||
* @param start Start offset in <code>segment</code> of token.
|
||||
* @param end End offset in <code>segment</code> of token.
|
||||
* @param tokenType The token's type.
|
||||
* @param startOffset The offset in the document at which the token occurs.
|
||||
*/
|
||||
public void addToken(Segment segment, int start, int end, int tokenType, int startOffset) {
|
||||
|
||||
switch (tokenType) {
|
||||
// Since reserved words, functions, and data types are all passed
|
||||
// into here as "identifiers," we have to see what the token
|
||||
// really is...
|
||||
case Token.IDENTIFIER:
|
||||
int value = wordsToHighlight.get(segment, start,end);
|
||||
if (value!=-1)
|
||||
tokenType = value;
|
||||
break;
|
||||
case Token.WHITESPACE:
|
||||
case Token.SEPARATOR:
|
||||
case Token.OPERATOR:
|
||||
case Token.ERROR_IDENTIFIER:
|
||||
case Token.ERROR_NUMBER_FORMAT:
|
||||
case Token.ERROR_STRING_DOUBLE:
|
||||
case Token.ERROR_CHAR:
|
||||
case Token.COMMENT_EOL:
|
||||
case Token.COMMENT_MULTILINE:
|
||||
case Token.LITERAL_BOOLEAN:
|
||||
case Token.LITERAL_NUMBER_DECIMAL_INT:
|
||||
case Token.LITERAL_NUMBER_FLOAT:
|
||||
case Token.LITERAL_NUMBER_HEXADECIMAL:
|
||||
break;
|
||||
case Token.LITERAL_STRING_DOUBLE_QUOTE:
|
||||
case Token.LITERAL_CHAR:
|
||||
break;
|
||||
|
||||
default:
|
||||
logger.error("Unknown tokenType: '" + tokenType + "'");
|
||||
tokenType = Token.IDENTIFIER;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
super.addToken(segment, start, end, tokenType, startOffset);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the text to place at the beginning and end of a
|
||||
* line to "comment" it in a this programming language.
|
||||
*
|
||||
* @return The start and end strings to add to a line to "comment"
|
||||
* it out.
|
||||
*/
|
||||
public String[] getLineCommentStartAndEnd() {
|
||||
return new String[] { "//", null };
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the words to highlight for the JavaScript programming language.
|
||||
*
|
||||
* @return A <code>TokenMap</code> containing the words to highlight for
|
||||
* the JavaScript programming language.
|
||||
* @see org.fife.ui.rsyntaxtextarea.AbstractTokenMaker#getWordsToHighlight
|
||||
*/
|
||||
public TokenMap getWordsToHighlight() {
|
||||
|
||||
TokenMap tokenMap = new TokenMap(52);
|
||||
|
||||
int reservedWord = Token.RESERVED_WORD;
|
||||
tokenMap.put("connecting", reservedWord);
|
||||
|
||||
int literalBoolean = Token.LITERAL_BOOLEAN;
|
||||
tokenMap.put("false", literalBoolean);
|
||||
tokenMap.put("true", literalBoolean);
|
||||
|
||||
int dataType = Token.DATA_TYPE;
|
||||
tokenMap.put("boolean", dataType);
|
||||
|
||||
return tokenMap;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> always as JavaScript uses curly braces
|
||||
* to denote code blocks.
|
||||
*
|
||||
* @return <code>true</code> always.
|
||||
*/
|
||||
public boolean getCurlyBracesDenoteCodeBlocks() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns the first token in the linked list of tokens generated
|
||||
* from <code>text</code>. This method must be implemented by
|
||||
* subclasses so they can correctly implement syntax highlighting.
|
||||
*
|
||||
* @param text The text from which to get tokens.
|
||||
* @param initialTokenType The token type we should start with.
|
||||
* @param startOffset The offset into the document at which
|
||||
* <code>text</code> starts.
|
||||
* @return The first <code>Token</code> in a linked list representing
|
||||
* the syntax highlighted text.
|
||||
*/
|
||||
public Token getTokenList(Segment text, int initialTokenType,
|
||||
int startOffset) {
|
||||
|
||||
|
||||
resetTokenList();
|
||||
|
||||
char[] array = text.array;
|
||||
int offset = text.offset;
|
||||
int count = text.count;
|
||||
int end = offset + count;
|
||||
|
||||
// See, when we find a token, its starting position is always of the
|
||||
// form: 'startOffset + (currentTokenStart-offset)'; but since
|
||||
// startOffset and offset are constant, tokens' starting positions
|
||||
// become: 'newStartOffset+currentTokenStart' for one less subtraction
|
||||
// operation.
|
||||
int newStartOffset = startOffset - offset;
|
||||
|
||||
currentTokenStart = offset;
|
||||
currentTokenType = initialTokenType;
|
||||
boolean backslash = false;
|
||||
boolean numContainsExponent = false;
|
||||
boolean numContainsEndCharacter = false;
|
||||
|
||||
for (int i=offset; i<end; i++) {
|
||||
|
||||
char c = array[i];
|
||||
|
||||
switch (currentTokenType) {
|
||||
|
||||
case Token.NULL:
|
||||
|
||||
currentTokenStart = i; // Starting a new token here.
|
||||
|
||||
switch (c) {
|
||||
|
||||
case ' ':
|
||||
case '\t':
|
||||
currentTokenType = Token.WHITESPACE;
|
||||
break;
|
||||
|
||||
case '"':
|
||||
if (backslash) { // Escaped double quote => call '"' an identifier..
|
||||
addToken(text, currentTokenStart,i, Token.IDENTIFIER, newStartOffset+currentTokenStart);
|
||||
backslash = false;
|
||||
}
|
||||
else {
|
||||
currentTokenType = Token.ERROR_STRING_DOUBLE;
|
||||
}
|
||||
break;
|
||||
|
||||
case '\'':
|
||||
if (backslash) { // Escaped single quote => call '\'' an identifier.
|
||||
addToken(text, currentTokenStart,i, Token.IDENTIFIER, newStartOffset+currentTokenStart);
|
||||
backslash = false;
|
||||
}
|
||||
else {
|
||||
currentTokenType = Token.ERROR_CHAR;
|
||||
}
|
||||
break;
|
||||
|
||||
case '\\':
|
||||
addToken(text, currentTokenStart,i, Token.IDENTIFIER, newStartOffset+currentTokenStart);
|
||||
currentTokenType = Token.NULL;
|
||||
backslash = !backslash;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
if (RSyntaxUtilities.isDigit(c)) {
|
||||
currentTokenType = Token.LITERAL_NUMBER_DECIMAL_INT;
|
||||
break;
|
||||
}
|
||||
else if (RSyntaxUtilities.isLetter(c) || c=='_') {
|
||||
currentTokenType = Token.IDENTIFIER;
|
||||
break;
|
||||
}
|
||||
int indexOf = operators.indexOf(c,0);
|
||||
if (indexOf>-1) {
|
||||
currentTokenStart = i;
|
||||
currentTokenType = Token.OPERATOR;
|
||||
break;
|
||||
}
|
||||
indexOf = separators.indexOf(c,0);
|
||||
if (indexOf>-1) {
|
||||
addToken(text, currentTokenStart,i, Token.SEPARATOR, newStartOffset+currentTokenStart);
|
||||
currentTokenType = Token.NULL;
|
||||
break;
|
||||
}
|
||||
indexOf = separators2.indexOf(c,0);
|
||||
if (indexOf>-1) {
|
||||
addToken(text, currentTokenStart,i, Token.IDENTIFIER, newStartOffset+currentTokenStart);
|
||||
currentTokenType = Token.NULL;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
currentTokenType = Token.ERROR_IDENTIFIER;
|
||||
break;
|
||||
}
|
||||
|
||||
} // End of switch (c).
|
||||
|
||||
break;
|
||||
|
||||
case Token.WHITESPACE:
|
||||
|
||||
switch (c) {
|
||||
|
||||
case ' ':
|
||||
case '\t':
|
||||
break; // Still whitespace.
|
||||
|
||||
case '\\':
|
||||
addToken(text, currentTokenStart,i-1, Token.WHITESPACE, newStartOffset+currentTokenStart);
|
||||
addToken(text, i,i, Token.IDENTIFIER, newStartOffset+i);
|
||||
currentTokenType = Token.NULL;
|
||||
backslash = true; // Previous char whitespace => this must be first backslash.
|
||||
break;
|
||||
|
||||
case '"': // Don't need to worry about backslashes as previous char is space.
|
||||
addToken(text, currentTokenStart,i-1, Token.WHITESPACE, newStartOffset+currentTokenStart);
|
||||
currentTokenStart = i;
|
||||
currentTokenType = Token.ERROR_STRING_DOUBLE;
|
||||
backslash = false;
|
||||
break;
|
||||
|
||||
case '\'': // Don't need to worry about backslashes as previous char is space.
|
||||
addToken(text, currentTokenStart,i-1, Token.WHITESPACE, newStartOffset+currentTokenStart);
|
||||
currentTokenStart = i;
|
||||
currentTokenType = Token.ERROR_CHAR;
|
||||
backslash = false;
|
||||
break;
|
||||
|
||||
default: // Add the whitespace token and start anew.
|
||||
|
||||
addToken(text, currentTokenStart,i-1, Token.WHITESPACE, newStartOffset+currentTokenStart);
|
||||
currentTokenStart = i;
|
||||
|
||||
if (RSyntaxUtilities.isDigit(c)) {
|
||||
currentTokenType = Token.LITERAL_NUMBER_DECIMAL_INT;
|
||||
break;
|
||||
}
|
||||
else if (RSyntaxUtilities.isLetter(c) || c=='_') {
|
||||
currentTokenType = Token.IDENTIFIER;
|
||||
break;
|
||||
}
|
||||
int indexOf = operators.indexOf(c,0);
|
||||
if (indexOf>-1) {
|
||||
currentTokenStart = i;
|
||||
currentTokenType = Token.OPERATOR;
|
||||
break;
|
||||
}
|
||||
indexOf = separators.indexOf(c,0);
|
||||
if (indexOf>-1) {
|
||||
addToken(text, i,i, Token.SEPARATOR, newStartOffset+i);
|
||||
currentTokenType = Token.NULL;
|
||||
break;
|
||||
}
|
||||
indexOf = separators2.indexOf(c,0);
|
||||
if (indexOf>-1) {
|
||||
addToken(text, i,i, Token.IDENTIFIER, newStartOffset+i);
|
||||
currentTokenType = Token.NULL;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
currentTokenType = Token.ERROR_IDENTIFIER;
|
||||
}
|
||||
|
||||
} // End of switch (c).
|
||||
|
||||
break;
|
||||
|
||||
default: // Should never happen
|
||||
|
||||
|
||||
case Token.LITERAL_NUMBER_HEXADECIMAL:
|
||||
|
||||
switch (c) {
|
||||
|
||||
case ' ':
|
||||
case '\t':
|
||||
addToken(text, currentTokenStart,i-1, Token.LITERAL_NUMBER_HEXADECIMAL, newStartOffset+currentTokenStart);
|
||||
currentTokenStart = i;
|
||||
currentTokenType = Token.WHITESPACE;
|
||||
break;
|
||||
|
||||
case '"': // Don't need to worry about backslashes as previous char is non-backslash.
|
||||
addToken(text, currentTokenStart,i-1, Token.LITERAL_NUMBER_HEXADECIMAL, newStartOffset+currentTokenStart);
|
||||
currentTokenStart = i;
|
||||
currentTokenType = Token.ERROR_STRING_DOUBLE;
|
||||
backslash = false;
|
||||
break;
|
||||
|
||||
case '\'': // Don't need to worry about backslashes as previous char is non-backslash.
|
||||
addToken(text, currentTokenStart,i-1, Token.LITERAL_NUMBER_HEXADECIMAL, newStartOffset+currentTokenStart);
|
||||
currentTokenStart = i;
|
||||
currentTokenType = Token.ERROR_CHAR;
|
||||
backslash = false;
|
||||
break;
|
||||
|
||||
case '\\':
|
||||
addToken(text, currentTokenStart,i-1, Token.LITERAL_NUMBER_HEXADECIMAL, newStartOffset+currentTokenStart);
|
||||
addToken(text, i,i, Token.IDENTIFIER, newStartOffset+i);
|
||||
currentTokenType = Token.NULL;
|
||||
backslash = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
if (c=='e') { // Exponent.
|
||||
if (numContainsExponent==false) {
|
||||
numContainsExponent = true;
|
||||
}
|
||||
else {
|
||||
currentTokenType = Token.ERROR_NUMBER_FORMAT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
int indexOf = hexCharacters.indexOf(c);
|
||||
if (indexOf>-1) {
|
||||
break; // Still a hexadecimal number.
|
||||
}
|
||||
indexOf = numberEndChars.indexOf(c);
|
||||
if (indexOf>-1) { // Numbers can end in 'f','F','l','L', etc.
|
||||
if (numContainsEndCharacter==true) {
|
||||
currentTokenType = Token.ERROR_NUMBER_FORMAT;
|
||||
}
|
||||
else {
|
||||
numContainsEndCharacter = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
indexOf = operators.indexOf(c);
|
||||
if (indexOf>-1) {
|
||||
addToken(text, currentTokenStart,i-1, Token.LITERAL_NUMBER_HEXADECIMAL, newStartOffset+currentTokenStart);
|
||||
currentTokenStart = i;
|
||||
currentTokenType = Token.OPERATOR;
|
||||
break;
|
||||
}
|
||||
indexOf = separators.indexOf(c);
|
||||
if (indexOf>-1) {
|
||||
addToken(text, currentTokenStart,i-1, Token.LITERAL_NUMBER_HEXADECIMAL, newStartOffset+currentTokenStart);
|
||||
addToken(text, i,i, Token.SEPARATOR, newStartOffset+i);
|
||||
currentTokenType = Token.NULL;
|
||||
break;
|
||||
}
|
||||
indexOf = separators2.indexOf(c);
|
||||
if (indexOf>-1) {
|
||||
addToken(text, currentTokenStart,i-1, Token.LITERAL_NUMBER_HEXADECIMAL, newStartOffset+currentTokenStart);
|
||||
addToken(text, i,i, Token.IDENTIFIER, newStartOffset+i);
|
||||
currentTokenType = Token.NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
// Otherwise, the token is an error.
|
||||
currentTokenType = Token.ERROR_NUMBER_FORMAT;
|
||||
|
||||
} // End of switch (c).
|
||||
|
||||
break;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
case Token.IDENTIFIER:
|
||||
|
||||
switch (c) {
|
||||
|
||||
case ' ':
|
||||
case '\t':
|
||||
addToken(text, currentTokenStart,i-1, Token.IDENTIFIER, newStartOffset+currentTokenStart);
|
||||
currentTokenStart = i;
|
||||
currentTokenType = Token.WHITESPACE;
|
||||
break;
|
||||
|
||||
case '"': // Don't need to worry about backslashes as previous char is non-backslash.
|
||||
addToken(text, currentTokenStart,i-1, Token.IDENTIFIER, newStartOffset+currentTokenStart);
|
||||
currentTokenStart = i;
|
||||
currentTokenType = Token.ERROR_STRING_DOUBLE;
|
||||
backslash = false;
|
||||
break;
|
||||
|
||||
case '\'': // Don't need to worry about backslashes as previous char is non-backslash.
|
||||
addToken(text, currentTokenStart,i-1, Token.IDENTIFIER, newStartOffset+currentTokenStart);
|
||||
currentTokenStart = i;
|
||||
currentTokenType = Token.ERROR_CHAR;
|
||||
backslash = false;
|
||||
break;
|
||||
|
||||
case '\\':
|
||||
addToken(text, currentTokenStart,i-1, Token.IDENTIFIER, newStartOffset+currentTokenStart);
|
||||
addToken(text, i,i, Token.IDENTIFIER, newStartOffset+i);
|
||||
currentTokenType = Token.NULL;
|
||||
backslash = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (RSyntaxUtilities.isLetterOrDigit(c) || c=='_') {
|
||||
break; // Still an identifier of some type.
|
||||
}
|
||||
int indexOf = operators.indexOf(c);
|
||||
if (indexOf>-1) {
|
||||
addToken(text, currentTokenStart,i-1, Token.IDENTIFIER, newStartOffset+currentTokenStart);
|
||||
currentTokenStart = i;
|
||||
currentTokenType = Token.OPERATOR;
|
||||
break;
|
||||
}
|
||||
indexOf = separators.indexOf(c,0);
|
||||
if (indexOf>-1) {
|
||||
addToken(text, currentTokenStart,i-1, Token.IDENTIFIER, newStartOffset+currentTokenStart);
|
||||
addToken(text, i,i, Token.SEPARATOR, newStartOffset+i);
|
||||
currentTokenType = Token.NULL;
|
||||
break;
|
||||
}
|
||||
indexOf = separators2.indexOf(c,0);
|
||||
if (indexOf>-1) {
|
||||
addToken(text, currentTokenStart,i-1, Token.IDENTIFIER, newStartOffset+currentTokenStart);
|
||||
addToken(text, i,i, Token.IDENTIFIER, newStartOffset+i);
|
||||
currentTokenType = Token.NULL;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
currentTokenType = Token.ERROR_IDENTIFIER;
|
||||
}
|
||||
|
||||
} // End of switch (c).
|
||||
|
||||
break;
|
||||
|
||||
case Token.LITERAL_NUMBER_DECIMAL_INT:
|
||||
|
||||
// Reset our boolean states if we only have one digit char before
|
||||
// the current one.
|
||||
if (currentTokenStart==i-1) {
|
||||
numContainsExponent = false;
|
||||
numContainsEndCharacter = false;
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
|
||||
case ' ':
|
||||
case '\t':
|
||||
addToken(text, currentTokenStart,i-1, Token.LITERAL_NUMBER_DECIMAL_INT, newStartOffset+currentTokenStart);
|
||||
currentTokenStart = i;
|
||||
currentTokenType = Token.WHITESPACE;
|
||||
break;
|
||||
|
||||
case '"': // Don't need to worry about backslashes as previous char is non-backslash.
|
||||
addToken(text, currentTokenStart,i-1, Token.LITERAL_NUMBER_DECIMAL_INT, newStartOffset+currentTokenStart);
|
||||
currentTokenStart = i;
|
||||
currentTokenType = Token.ERROR_STRING_DOUBLE;
|
||||
backslash = false;
|
||||
break;
|
||||
|
||||
case '\'': // Don't need to worry about backslashes as previous char is non-backslash.
|
||||
addToken(text, currentTokenStart,i-1, Token.LITERAL_NUMBER_DECIMAL_INT, newStartOffset+currentTokenStart);
|
||||
currentTokenStart = i;
|
||||
currentTokenType = Token.ERROR_CHAR;
|
||||
backslash = false;
|
||||
break;
|
||||
|
||||
case '\\':
|
||||
addToken(text, currentTokenStart,i-1, Token.LITERAL_NUMBER_DECIMAL_INT, newStartOffset+currentTokenStart);
|
||||
addToken(text, i,i, Token.IDENTIFIER, newStartOffset+i);
|
||||
currentTokenType = Token.NULL;
|
||||
backslash = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
if (RSyntaxUtilities.isDigit(c)) {
|
||||
break; // Still a literal number.
|
||||
}
|
||||
else if (c=='e') { // Exponent.
|
||||
if (numContainsExponent==false) {
|
||||
numContainsExponent = true;
|
||||
}
|
||||
else {
|
||||
currentTokenType = Token.ERROR_NUMBER_FORMAT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if (c=='.') { // Decimal point.
|
||||
if (numContainsExponent==true) {
|
||||
currentTokenType = Token.ERROR_NUMBER_FORMAT;
|
||||
}
|
||||
else {
|
||||
currentTokenType = Token.LITERAL_NUMBER_FLOAT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
int indexOf = numberEndChars.indexOf(c);
|
||||
if (indexOf>-1) { // Numbers can end in 'f','F','l','L', etc.
|
||||
if (numContainsEndCharacter==true) {
|
||||
currentTokenType = Token.ERROR_NUMBER_FORMAT;
|
||||
}
|
||||
else {
|
||||
numContainsEndCharacter = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
indexOf = operators.indexOf(c);
|
||||
if (indexOf>-1) {
|
||||
addToken(text, currentTokenStart,i-1, Token.LITERAL_NUMBER_DECIMAL_INT, newStartOffset+currentTokenStart);
|
||||
currentTokenStart = i;
|
||||
currentTokenType = Token.OPERATOR;
|
||||
break;
|
||||
}
|
||||
indexOf = separators.indexOf(c);
|
||||
if (indexOf>-1) {
|
||||
addToken(text, currentTokenStart,i-1, Token.LITERAL_NUMBER_DECIMAL_INT, newStartOffset+currentTokenStart);
|
||||
addToken(text, i,i, Token.SEPARATOR, newStartOffset+i);
|
||||
currentTokenType = Token.NULL;
|
||||
break;
|
||||
}
|
||||
indexOf = separators2.indexOf(c);
|
||||
if (indexOf>-1) {
|
||||
addToken(text, currentTokenStart,i-1, Token.LITERAL_NUMBER_DECIMAL_INT, newStartOffset+currentTokenStart);
|
||||
addToken(text, i,i, Token.IDENTIFIER, newStartOffset+i);
|
||||
currentTokenType = Token.NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
// Otherwise, the token is an error.
|
||||
currentTokenType = Token.ERROR_NUMBER_FORMAT;
|
||||
|
||||
} // End of switch (c).
|
||||
|
||||
break;
|
||||
|
||||
case Token.LITERAL_NUMBER_FLOAT:
|
||||
|
||||
switch (c) {
|
||||
|
||||
case ' ':
|
||||
case '\t':
|
||||
addToken(text, currentTokenStart,i-1, Token.LITERAL_NUMBER_FLOAT, newStartOffset+currentTokenStart);
|
||||
currentTokenStart = i;
|
||||
currentTokenType = Token.WHITESPACE;
|
||||
break;
|
||||
|
||||
case '"': // Don't need to worry about backslashes as previous char is non-backslash.
|
||||
addToken(text, currentTokenStart,i-1, Token.LITERAL_NUMBER_FLOAT, newStartOffset+currentTokenStart);
|
||||
currentTokenStart = i;
|
||||
currentTokenType = Token.ERROR_STRING_DOUBLE;
|
||||
backslash = false;
|
||||
break;
|
||||
|
||||
case '\'': // Don't need to worry about backslashes as previous char is non-backslash.
|
||||
addToken(text, currentTokenStart,i-1, Token.LITERAL_NUMBER_FLOAT, newStartOffset+currentTokenStart);
|
||||
currentTokenStart = i;
|
||||
currentTokenType = Token.ERROR_CHAR;
|
||||
backslash = false;
|
||||
break;
|
||||
|
||||
case '\\':
|
||||
addToken(text, currentTokenStart,i-1, Token.LITERAL_NUMBER_FLOAT, newStartOffset+currentTokenStart);
|
||||
addToken(text, i,i, Token.IDENTIFIER, newStartOffset+i);
|
||||
currentTokenType = Token.NULL;
|
||||
backslash = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
if (RSyntaxUtilities.isDigit(c)) {
|
||||
break; // Still a literal number.
|
||||
}
|
||||
else if (c=='e') { // Exponent.
|
||||
if (numContainsExponent==false) {
|
||||
numContainsExponent = true;
|
||||
}
|
||||
else {
|
||||
currentTokenType = Token.ERROR_NUMBER_FORMAT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if (c=='.') { // Second decimal point; must catch now because it's a "separator" below.
|
||||
currentTokenType = Token.ERROR_NUMBER_FORMAT;
|
||||
break;
|
||||
}
|
||||
int indexOf = numberEndChars.indexOf(c);
|
||||
if (indexOf>-1) { // Numbers can end in 'f','F','l','L', etc.
|
||||
if (numContainsEndCharacter==true) {
|
||||
currentTokenType = Token.ERROR_NUMBER_FORMAT;
|
||||
}
|
||||
else {
|
||||
numContainsEndCharacter = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
indexOf = operators.indexOf(c);
|
||||
if (indexOf>-1) {
|
||||
addToken(text, currentTokenStart,i-1, Token.LITERAL_NUMBER_FLOAT, newStartOffset+currentTokenStart);
|
||||
currentTokenStart = i;
|
||||
currentTokenType = Token.OPERATOR;
|
||||
break;
|
||||
}
|
||||
indexOf = separators.indexOf(c);
|
||||
if (indexOf>-1) {
|
||||
addToken(text, currentTokenStart,i-1, Token.LITERAL_NUMBER_FLOAT, newStartOffset+currentTokenStart);
|
||||
addToken(text, i,i, Token.SEPARATOR, newStartOffset+i);
|
||||
currentTokenType = Token.NULL;
|
||||
break;
|
||||
}
|
||||
indexOf = separators2.indexOf(c);
|
||||
if (indexOf>-1) {
|
||||
addToken(text, currentTokenStart,i-1, Token.LITERAL_NUMBER_FLOAT, newStartOffset+currentTokenStart);
|
||||
addToken(text, i,i, Token.IDENTIFIER, newStartOffset+i);
|
||||
currentTokenType = Token.NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
// Otherwise, the token is an error.
|
||||
currentTokenType = Token.ERROR_NUMBER_FORMAT;
|
||||
|
||||
} // End of switch (c).
|
||||
|
||||
break;
|
||||
|
||||
|
||||
case Token.COMMENT_MULTILINE:
|
||||
|
||||
// Find either end of MLC or end of the current line.
|
||||
while (i < end-1) {
|
||||
if (array[i]=='*' && array[i+1]=='/') {
|
||||
addToken(text, currentTokenStart,i+1, Token.COMMENT_MULTILINE, newStartOffset+currentTokenStart);
|
||||
i = i + 1;
|
||||
currentTokenType = Token.NULL;
|
||||
backslash = false; // Backslashes can't accumulate before and after a comment...
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case Token.COMMENT_EOL:
|
||||
i = end - 1;
|
||||
addToken(text, currentTokenStart,i, Token.COMMENT_EOL, newStartOffset+currentTokenStart);
|
||||
// We need to set token type to null so at the bottom we don't add one more token.
|
||||
currentTokenType = Token.NULL;
|
||||
break;
|
||||
|
||||
// We need this state because comments always start with '/', which is an operator.
|
||||
// Note that when we enter this state, the PREVIOUS character was an operator.
|
||||
case Token.OPERATOR:
|
||||
|
||||
if (array[i-1]=='/') { // Possibility of comments.
|
||||
|
||||
if (c=='*') {
|
||||
currentTokenType = Token.COMMENT_MULTILINE;
|
||||
break;
|
||||
}
|
||||
|
||||
else if (c=='/') {
|
||||
currentTokenType = Token.COMMENT_EOL;
|
||||
i = end - 1; // Since we know the rest of the line is in this token.
|
||||
}
|
||||
|
||||
else {
|
||||
// We MUST add the token at the previous char now; if we don't and let
|
||||
// operators accumulate before we print them, we will mess up syntax
|
||||
// highlighting if we get an end-of-line comment.
|
||||
addToken(text, currentTokenStart,i-1, Token.OPERATOR, newStartOffset+currentTokenStart);
|
||||
currentTokenType = Token.NULL;
|
||||
i = i - 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
else {
|
||||
|
||||
addToken(text, currentTokenStart,i-1, Token.OPERATOR, newStartOffset+currentTokenStart);
|
||||
|
||||
// Hack to keep code size down...
|
||||
i--;
|
||||
currentTokenType = Token.NULL;
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case Token.ERROR_IDENTIFIER:
|
||||
|
||||
switch (c) {
|
||||
|
||||
case ' ':
|
||||
case '\t':
|
||||
addToken(text, currentTokenStart,i-1, Token.ERROR_IDENTIFIER, newStartOffset+currentTokenStart);
|
||||
currentTokenStart = i;
|
||||
currentTokenType = Token.WHITESPACE;
|
||||
break;
|
||||
|
||||
case '"': // Don't need to worry about backslashes as previous char is non-backslash.
|
||||
addToken(text, currentTokenStart,i-1, Token.ERROR_IDENTIFIER, newStartOffset+currentTokenStart);
|
||||
currentTokenStart = i;
|
||||
currentTokenType = Token.ERROR_STRING_DOUBLE;
|
||||
backslash = false;
|
||||
break;
|
||||
|
||||
case '\'': // Don't need to worry about backslashes as previous char is non-backslash.
|
||||
addToken(text, currentTokenStart,i-1, Token.ERROR_IDENTIFIER, newStartOffset+currentTokenStart);
|
||||
currentTokenStart = i;
|
||||
currentTokenType = Token.ERROR_CHAR;
|
||||
backslash = false;
|
||||
break;
|
||||
|
||||
case ';':
|
||||
addToken(text, currentTokenStart,i-1, Token.ERROR_IDENTIFIER, newStartOffset+currentTokenStart);
|
||||
addToken(text, i,i, Token.IDENTIFIER, newStartOffset+i);
|
||||
currentTokenType = Token.NULL;
|
||||
break;
|
||||
|
||||
case '\\':
|
||||
addToken(text, currentTokenStart,i-1, Token.ERROR_IDENTIFIER, newStartOffset+currentTokenStart);
|
||||
addToken(text, i,i, Token.IDENTIFIER, newStartOffset+i);
|
||||
currentTokenType = Token.NULL;
|
||||
backslash = true; // Must be first backslash in a row since previous character is identifier char.
|
||||
break;
|
||||
|
||||
default:
|
||||
int indexOf = operators.indexOf(c);
|
||||
if (indexOf>-1) {
|
||||
addToken(text, currentTokenStart,i-1, Token.ERROR_IDENTIFIER, newStartOffset+currentTokenStart);
|
||||
currentTokenStart = i;
|
||||
currentTokenType = Token.OPERATOR;
|
||||
}
|
||||
indexOf = separators.indexOf(c);
|
||||
if (indexOf>-1) {
|
||||
addToken(text, currentTokenStart,i-1, Token.ERROR_IDENTIFIER, newStartOffset+currentTokenStart);
|
||||
addToken(text, i,i, Token.SEPARATOR, newStartOffset+i);
|
||||
currentTokenType = Token.NULL;
|
||||
}
|
||||
indexOf = separators2.indexOf(c);
|
||||
if (indexOf>-1) {
|
||||
addToken(text, currentTokenStart,i-1, Token.ERROR_IDENTIFIER, newStartOffset+currentTokenStart);
|
||||
addToken(text, i,i, Token.IDENTIFIER, newStartOffset+i);
|
||||
currentTokenType = Token.NULL;
|
||||
}
|
||||
|
||||
} // End of switch (c).
|
||||
|
||||
break;
|
||||
|
||||
case Token.ERROR_NUMBER_FORMAT:
|
||||
|
||||
switch (c) {
|
||||
|
||||
case ' ':
|
||||
case '\t':
|
||||
addToken(text, currentTokenStart,i-1, Token.ERROR_NUMBER_FORMAT, newStartOffset+currentTokenStart);
|
||||
currentTokenStart = i;
|
||||
currentTokenType = Token.WHITESPACE;
|
||||
break;
|
||||
|
||||
case '"': // Don't need to worry about backslashes as previous char is non-backslash.
|
||||
addToken(text, currentTokenStart,i-1, Token.ERROR_NUMBER_FORMAT, newStartOffset+currentTokenStart);
|
||||
currentTokenStart = i;
|
||||
currentTokenType = Token.ERROR_STRING_DOUBLE;
|
||||
backslash = false;
|
||||
break;
|
||||
|
||||
case '\'': // Don't need to worry about backslashes as previous char is non-backslash.
|
||||
addToken(text, currentTokenStart,i-1, Token.ERROR_NUMBER_FORMAT, newStartOffset+currentTokenStart);
|
||||
currentTokenStart = i;
|
||||
currentTokenType = Token.ERROR_CHAR;
|
||||
backslash = false;
|
||||
break;
|
||||
|
||||
case ';':
|
||||
addToken(text, currentTokenStart,i-1, Token.ERROR_NUMBER_FORMAT, newStartOffset+currentTokenStart);
|
||||
addToken(text, i,i, Token.IDENTIFIER, newStartOffset+i);
|
||||
currentTokenType = Token.NULL;
|
||||
break;
|
||||
|
||||
case '\\':
|
||||
addToken(text, currentTokenStart,i-1, Token.ERROR_NUMBER_FORMAT, newStartOffset+currentTokenStart);
|
||||
addToken(text, i,i, Token.IDENTIFIER, newStartOffset+i);
|
||||
currentTokenType = Token.NULL;
|
||||
backslash = true; // Must be first backslash in a row since previous char is a number char.
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
// Could be going into hexadecimal.
|
||||
int indexOf = hexCharacters.indexOf(c);
|
||||
if (indexOf>-1 && (i-currentTokenStart==2 && array[i-1]=='x' && array[i-2]=='0')) {
|
||||
currentTokenType = Token.LITERAL_NUMBER_HEXADECIMAL;
|
||||
break;
|
||||
}
|
||||
|
||||
indexOf = operators.indexOf(c);
|
||||
if (indexOf>-1) {
|
||||
addToken(text, currentTokenStart,i-1, Token.ERROR_NUMBER_FORMAT, newStartOffset+currentTokenStart);
|
||||
currentTokenStart = i;
|
||||
currentTokenType = Token.OPERATOR;
|
||||
}
|
||||
indexOf = separators.indexOf(c);
|
||||
if (indexOf>-1) {
|
||||
addToken(text, currentTokenStart,i-1, Token.ERROR_NUMBER_FORMAT, newStartOffset+currentTokenStart);
|
||||
addToken(text, i,i, Token.SEPARATOR, newStartOffset+i);
|
||||
currentTokenType = Token.NULL;
|
||||
}
|
||||
indexOf = separators2.indexOf(c);
|
||||
if (indexOf>-1) {
|
||||
addToken(text, currentTokenStart,i-1, Token.ERROR_NUMBER_FORMAT, newStartOffset+currentTokenStart);
|
||||
addToken(text, i,i, Token.IDENTIFIER, newStartOffset+i);
|
||||
currentTokenType = Token.NULL;
|
||||
}
|
||||
|
||||
} // End of switch (c).
|
||||
|
||||
break;
|
||||
|
||||
case Token.ERROR_CHAR:
|
||||
|
||||
if (c=='\\') {
|
||||
backslash = !backslash; // Okay because if we got in here, backslash was initially false.
|
||||
}
|
||||
else {
|
||||
|
||||
if (c=='\'' && !backslash) {
|
||||
addToken(text, currentTokenStart,i, Token.LITERAL_CHAR, newStartOffset+currentTokenStart);
|
||||
currentTokenType = Token.NULL;
|
||||
// backslash is definitely false when we leave.
|
||||
}
|
||||
|
||||
backslash = false; // Need to set backslash to false here as a character was typed.
|
||||
|
||||
}
|
||||
// Otherwise, we're still an unclosed char...
|
||||
|
||||
break;
|
||||
|
||||
case Token.ERROR_STRING_DOUBLE:
|
||||
|
||||
if (c=='\\') {
|
||||
backslash = !backslash; // Okay because if we got in here, backslash was initially false.
|
||||
}
|
||||
else {
|
||||
if (c=='"' && !backslash) {
|
||||
addToken(text, currentTokenStart,i, Token.LITERAL_STRING_DOUBLE_QUOTE, newStartOffset+currentTokenStart);
|
||||
currentTokenType = Token.NULL;
|
||||
// backslash is definitely false when we leave.
|
||||
}
|
||||
|
||||
backslash = false; // Need to set backslash to false here as a character was typed.
|
||||
|
||||
}
|
||||
// Otherwise, we're still an unclosed string...
|
||||
|
||||
break;
|
||||
|
||||
} // End of switch (currentTokenType).
|
||||
|
||||
} // End of for (int i=offset; i<end; i++).
|
||||
|
||||
// Deal with the (possibly there) last token.
|
||||
if (currentTokenType != Token.NULL) {
|
||||
addToken(text, currentTokenStart,end-1, currentTokenType, newStartOffset+currentTokenStart);
|
||||
}
|
||||
if (currentTokenType!=Token.COMMENT_MULTILINE) {
|
||||
addNullToken();
|
||||
}
|
||||
|
||||
|
||||
// Return the first token in our linked list.
|
||||
return firstToken;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,473 @@
|
||||
package org.ethereum.gui;
|
||||
|
||||
import org.ethereum.core.Account;
|
||||
import org.ethereum.core.Transaction;
|
||||
import org.ethereum.db.ContractDetails;
|
||||
import org.ethereum.manager.WorldManager;
|
||||
import org.ethereum.net.client.ClientPeer;
|
||||
import org.ethereum.util.ByteUtil;
|
||||
import org.ethereum.util.Utils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.spongycastle.util.BigIntegers;
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.border.EtchedBorder;
|
||||
import javax.swing.plaf.ComboBoxUI;
|
||||
import javax.swing.plaf.basic.BasicComboPopup;
|
||||
import javax.swing.table.DefaultTableModel;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.math.BigInteger;
|
||||
import java.net.URL;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* www.ethereumJ.com
|
||||
* @author: Roman Mandeleil
|
||||
* Created on: 18/05/14 22:21
|
||||
*/
|
||||
class ContractCallDialog extends JDialog implements MessageAwareDialog {
|
||||
|
||||
private static final long serialVersionUID = -7561153561155037293L;
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger("ui");
|
||||
|
||||
private ContractCallDialog dialog;
|
||||
private JComboBox<AccountWrapper> creatorAddressCombo;
|
||||
private final JTextField gasInput;
|
||||
private final JTextField contractAddrInput;
|
||||
|
||||
private JScrollPane contractDataInput;
|
||||
private JTextArea msgDataTA;
|
||||
|
||||
private JLabel statusMsg = null;
|
||||
private JLabel playLabel = null;
|
||||
private JLabel rejectLabel = null;
|
||||
private JLabel approveLabel = null;
|
||||
|
||||
public ContractCallDialog(Frame parent) {
|
||||
super(parent, "Call Contract: ", false);
|
||||
dialog = this;
|
||||
|
||||
contractAddrInput = new JTextField(5);
|
||||
GUIUtils.addStyle(contractAddrInput, "Contract Address: ");
|
||||
contractAddrInput.addFocusListener(new FocusAdapter() {
|
||||
@Override
|
||||
public void focusLost(FocusEvent e) {
|
||||
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
populateContractDetails();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
contractAddrInput.setBounds(70, 30, 350, 45);
|
||||
this.getContentPane().add(contractAddrInput);
|
||||
|
||||
gasInput = new JTextField(5);
|
||||
GUIUtils.addStyle(gasInput, "Gas: ");
|
||||
|
||||
msgDataTA = new JTextArea();
|
||||
msgDataTA.setLineWrap(true);
|
||||
contractDataInput = new JScrollPane(msgDataTA);
|
||||
GUIUtils.addStyle(msgDataTA, null, false);
|
||||
GUIUtils.addStyle(contractDataInput, "Input:");
|
||||
|
||||
msgDataTA.setText("");
|
||||
msgDataTA.setCaretPosition(0);
|
||||
|
||||
this.getContentPane().setBackground(Color.WHITE);
|
||||
this.getContentPane().setLayout(null);
|
||||
|
||||
contractDataInput.setBounds(70, 80, 350, 165);
|
||||
this.getContentPane().add(contractDataInput);
|
||||
|
||||
gasInput.setBounds(330, 260, 90, 45);
|
||||
this.getContentPane().add(gasInput);
|
||||
|
||||
URL rejectIconURL = ClassLoader.getSystemResource("buttons/reject.png");
|
||||
ImageIcon rejectIcon = new ImageIcon(rejectIconURL);
|
||||
rejectLabel = new JLabel(rejectIcon);
|
||||
rejectLabel.setToolTipText("Cancel");
|
||||
rejectLabel.setCursor(new Cursor(Cursor.HAND_CURSOR));
|
||||
|
||||
URL playIconURL = ClassLoader.getSystemResource("buttons/play.png");
|
||||
ImageIcon playIcon = new ImageIcon(playIconURL);
|
||||
playLabel = new JLabel(playIcon);
|
||||
playLabel.setToolTipText("Play Drafted");
|
||||
playLabel.setCursor(new Cursor(Cursor.HAND_CURSOR));
|
||||
playLabel.addMouseListener(
|
||||
new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
ContractCallDialog.this.playContractCall();
|
||||
}}
|
||||
);
|
||||
|
||||
playLabel.setBounds(438, 100, 42, 42);
|
||||
this.getContentPane().add(playLabel);
|
||||
|
||||
JLabel statusMessage = new JLabel("");
|
||||
statusMessage.setBounds(50, 360, 400, 50);
|
||||
statusMessage.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
this.statusMsg = statusMessage;
|
||||
this.getContentPane().add(statusMessage);
|
||||
|
||||
rejectLabel.setBounds(260, 325, 45, 45);
|
||||
this.getContentPane().add(rejectLabel);
|
||||
rejectLabel.setVisible(true);
|
||||
rejectLabel.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
dialog.dispose();
|
||||
}
|
||||
});
|
||||
|
||||
URL approveIconURL = ClassLoader.getSystemResource("buttons/approve.png");
|
||||
ImageIcon approveIcon = new ImageIcon(approveIconURL);
|
||||
approveLabel = new JLabel(approveIcon);
|
||||
approveLabel.setToolTipText("Submit the transaction");
|
||||
approveLabel.setCursor(new Cursor(Cursor.HAND_CURSOR));
|
||||
|
||||
approveLabel.setBounds(200, 325, 45, 45);
|
||||
this.getContentPane().add(approveLabel);
|
||||
approveLabel.setVisible(true);
|
||||
|
||||
approveLabel.addMouseListener(
|
||||
new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
submitContractCall();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
gasInput.setText("1000");
|
||||
|
||||
JComboBox<AccountWrapper> creatorAddressCombo = new JComboBox<AccountWrapper>() {
|
||||
private static final long serialVersionUID = -3748305421049121671L;
|
||||
@Override
|
||||
public ComboBoxUI getUI() {
|
||||
return super.getUI();
|
||||
}
|
||||
};
|
||||
creatorAddressCombo.setOpaque(true);
|
||||
creatorAddressCombo.setEnabled(true);
|
||||
|
||||
creatorAddressCombo.setBackground(Color.WHITE);
|
||||
creatorAddressCombo.setFocusable(false);
|
||||
|
||||
this.creatorAddressCombo = creatorAddressCombo;
|
||||
|
||||
final Border line = BorderFactory.createLineBorder(Color.LIGHT_GRAY);
|
||||
JComponent editor = (JComponent)(creatorAddressCombo.getEditor().getEditorComponent());
|
||||
editor.setForeground(Color.RED);
|
||||
|
||||
Collection<Account> accounts =
|
||||
WorldManager.getInstance().getWallet().getAccountCollection();
|
||||
|
||||
for (Account account : accounts) {
|
||||
creatorAddressCombo.addItem(new AccountWrapper(account));
|
||||
}
|
||||
|
||||
creatorAddressCombo.setRenderer(new DefaultListCellRenderer() {
|
||||
private static final long serialVersionUID = 6100091092527477892L;
|
||||
|
||||
@Override
|
||||
public void paint(Graphics g) {
|
||||
setBackground(Color.WHITE);
|
||||
setForeground(new Color(143, 170, 220));
|
||||
setFont(new Font("Monospaced", 0, 13));
|
||||
setBorder(BorderFactory.createEmptyBorder());
|
||||
super.paint(g);
|
||||
}
|
||||
});
|
||||
|
||||
creatorAddressCombo.setPopupVisible(false);
|
||||
|
||||
Object child = creatorAddressCombo.getAccessibleContext().getAccessibleChild(0);
|
||||
BasicComboPopup popup = (BasicComboPopup)child;
|
||||
|
||||
JList list = popup.getList();
|
||||
list.setSelectionBackground(Color.cyan);
|
||||
list.setBorder(null);
|
||||
|
||||
for (int i = 0; i < creatorAddressCombo.getComponentCount(); i++) {
|
||||
if (creatorAddressCombo.getComponent(i) instanceof CellRendererPane) {
|
||||
CellRendererPane crp = ((CellRendererPane) (creatorAddressCombo.getComponent(i)));
|
||||
}
|
||||
if (creatorAddressCombo.getComponent(i) instanceof AbstractButton) {
|
||||
((AbstractButton) creatorAddressCombo.getComponent(i)).setBorder(line);
|
||||
}
|
||||
}
|
||||
creatorAddressCombo.setBounds(70, 267, 230, 36);
|
||||
this.getContentPane().add(creatorAddressCombo);
|
||||
|
||||
this.getContentPane().revalidate();
|
||||
this.getContentPane().repaint();
|
||||
this.setResizable(false);
|
||||
}
|
||||
|
||||
private void populateContractDetails() {
|
||||
|
||||
String contractAddr = contractAddrInput.getText();
|
||||
if (!Pattern.matches("[0-9a-fA-F]+", contractAddr) || (contractAddr.length() != 40)) {
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] contractAddress = Hex.decode( contractAddr );
|
||||
final byte[] programCode = WorldManager.getInstance().getRepository().getCode(contractAddress);
|
||||
final Map storageMap = WorldManager.getInstance().getRepository().getContractDetails(contractAddress).getStorage();
|
||||
|
||||
contractDataInput.setBounds(70, 80, 350, 145);
|
||||
contractDataInput.setViewportView(msgDataTA);
|
||||
|
||||
URL expandIconURL = ClassLoader.getSystemResource("buttons/add-23x23.png");
|
||||
ImageIcon expandIcon = new ImageIcon(expandIconURL);
|
||||
final JLabel expandLabel = new JLabel(expandIcon);
|
||||
expandLabel.setToolTipText("Cancel");
|
||||
expandLabel.setCursor(new Cursor(Cursor.HAND_CURSOR));
|
||||
expandLabel.setBounds(235, 232, 23, 23);
|
||||
this.getContentPane().add(expandLabel);
|
||||
expandLabel.setVisible(true);
|
||||
|
||||
final Border border = BorderFactory.createEtchedBorder(EtchedBorder.LOWERED);
|
||||
final JPanel detailPanel = new JPanel();
|
||||
detailPanel.setBorder(border);
|
||||
detailPanel.setBounds(135, 242, 230, 2);
|
||||
|
||||
final JPanel spacer = new JPanel();
|
||||
spacer.setForeground(Color.white);
|
||||
spacer.setBackground(Color.white);
|
||||
spacer.setBorder(null);
|
||||
spacer.setBounds(225, 232, 40, 20);
|
||||
|
||||
this.getContentPane().add(spacer);
|
||||
this.getContentPane().add(detailPanel);
|
||||
|
||||
expandLabel.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
ContractCallDialog.this.setSize(500, 530);
|
||||
|
||||
ContractCallDialog.this.creatorAddressCombo.setBounds(70, 367, 230, 36);
|
||||
ContractCallDialog.this.gasInput.setBounds(330, 360, 90, 45);
|
||||
ContractCallDialog.this.rejectLabel.setBounds(260, 425, 45, 45);
|
||||
ContractCallDialog.this.approveLabel.setBounds(200, 425, 45, 45);
|
||||
ContractCallDialog.this.statusMsg.setBounds(50, 460, 400, 50);
|
||||
|
||||
spacer.setVisible(false);
|
||||
expandLabel.setVisible(false);
|
||||
detailPanel.setVisible(false);
|
||||
|
||||
JTextField contractCode = new JTextField(15);
|
||||
contractCode.setText(Hex.toHexString( programCode ));
|
||||
GUIUtils.addStyle(contractCode, "Code: ");
|
||||
contractCode.setBounds(70, 230, 350, 45);
|
||||
|
||||
JTable storage = new JTable(2, 2);
|
||||
storage.setTableHeader(null);
|
||||
storage.setShowGrid(false);
|
||||
storage.setIntercellSpacing(new Dimension(15, 0));
|
||||
storage.setCellSelectionEnabled(false);
|
||||
GUIUtils.addStyle(storage);
|
||||
|
||||
JTableStorageModel tableModel = new JTableStorageModel(storageMap);
|
||||
storage.setModel(tableModel);
|
||||
|
||||
JScrollPane scrollPane = new JScrollPane(storage);
|
||||
scrollPane.setBorder(null);
|
||||
scrollPane.getViewport().setBackground(Color.WHITE);
|
||||
scrollPane.setBounds(70, 290, 350, 50);
|
||||
|
||||
|
||||
ContractCallDialog.this.getContentPane().add(contractCode);
|
||||
ContractCallDialog.this.getContentPane().add(scrollPane);
|
||||
}
|
||||
});
|
||||
this.repaint();
|
||||
}
|
||||
|
||||
private void playContractCall() {
|
||||
|
||||
byte[] contractAddress = Hex.decode(contractAddrInput.getText());
|
||||
ContractDetails contractDetails = WorldManager.getInstance().getRepository().getContractDetails(contractAddress);
|
||||
if (contractDetails == null) {
|
||||
alertStatusMsg("No contract for that address");
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] programCode = WorldManager.getInstance().getRepository().getCode(contractAddress);
|
||||
if (programCode == null || programCode.length == 0) {
|
||||
alertStatusMsg("Such account exist but no code in the db");
|
||||
return;
|
||||
}
|
||||
|
||||
Transaction tx = createTransaction();
|
||||
if (tx == null) return;
|
||||
|
||||
ProgramPlayDialog.createAndShowGUI(programCode, tx, WorldManager.getInstance().getBlockchain().getLastBlock());
|
||||
}
|
||||
|
||||
protected JRootPane createRootPane() {
|
||||
|
||||
Container parent = this.getParent();
|
||||
|
||||
if (parent != null) {
|
||||
Dimension parentSize = parent.getSize();
|
||||
Point p = parent.getLocation();
|
||||
setLocation(p.x + parentSize.width / 4, p.y + 10);
|
||||
}
|
||||
|
||||
JRootPane rootPane = new JRootPane();
|
||||
KeyStroke stroke = KeyStroke.getKeyStroke("ESCAPE");
|
||||
Action actionListener = new AbstractAction() {
|
||||
public void actionPerformed(ActionEvent actionEvent) {
|
||||
dispose();
|
||||
}
|
||||
};
|
||||
InputMap inputMap = rootPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
|
||||
inputMap.put(stroke, "ESCAPE");
|
||||
rootPane.getActionMap().put("ESCAPE", actionListener);
|
||||
|
||||
this.setSize(500, 430);
|
||||
this.setVisible(true);
|
||||
|
||||
return rootPane;
|
||||
}
|
||||
|
||||
public void infoStatusMsg(String text) {
|
||||
this.statusMsg.setForeground(Color.GREEN.darker().darker());
|
||||
this.statusMsg.setText(text);
|
||||
}
|
||||
|
||||
public void alertStatusMsg(String text) {
|
||||
this.statusMsg.setForeground(Color.RED);
|
||||
this.statusMsg.setText(text);
|
||||
}
|
||||
|
||||
public void submitContractCall() {
|
||||
|
||||
ClientPeer peer = WorldManager.getInstance().getActivePeer();
|
||||
if (peer == null) {
|
||||
dialog.alertStatusMsg("Not connected to any peer");
|
||||
return;
|
||||
}
|
||||
|
||||
Transaction tx = createTransaction();
|
||||
if (tx == null) return;
|
||||
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("tx.hash: {}", (new BigInteger(tx.getHash()).toString(16)));
|
||||
}
|
||||
// SwingWorker
|
||||
DialogWorker worker = new DialogWorker(tx, this);
|
||||
worker.execute();
|
||||
}
|
||||
|
||||
private Transaction createTransaction() {
|
||||
|
||||
byte[] data;
|
||||
if (!msgDataTA.getText().trim().equals("")) {
|
||||
Object[] lexaList = msgDataTA.getText().split(",");
|
||||
data = ByteUtil.encodeDataList(lexaList);
|
||||
} else {
|
||||
data = new byte[] {};
|
||||
}
|
||||
|
||||
byte[] contractAddress = Hex.decode( contractAddrInput.getText());
|
||||
|
||||
Account account = ((AccountWrapper)creatorAddressCombo.getSelectedItem()).getAccount();
|
||||
|
||||
byte[] senderPrivKey = account.getEcKey().getPrivKeyBytes();
|
||||
byte[] nonce = account.getNonce() == BigInteger.ZERO ? null : account.getNonce().toByteArray();
|
||||
byte[] gasPrice = new BigInteger("10000000000000").toByteArray();
|
||||
|
||||
BigInteger gasBI = new BigInteger(gasInput.getText());
|
||||
byte[] gasValue = BigIntegers.asUnsignedByteArray(gasBI);
|
||||
byte[] endowment = BigIntegers.asUnsignedByteArray(new BigInteger("1000"));
|
||||
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Contract call:");
|
||||
logger.info("tx.nonce: {}", nonce == null ? "null" : Hex.toHexString(nonce));
|
||||
logger.info("tx.gasPrice: {}", Hex.toHexString(gasPrice));
|
||||
logger.info("tx.gasValue: {}", Hex.toHexString(gasValue));
|
||||
logger.info("tx.address: {}", Hex.toHexString(contractAddress));
|
||||
logger.info("tx.endowment: {}", Hex.toHexString(endowment));
|
||||
logger.info("tx.data: {}", Hex.toHexString(data));
|
||||
}
|
||||
|
||||
Transaction tx = new Transaction(nonce, gasPrice, gasValue,
|
||||
contractAddress, endowment, data);
|
||||
|
||||
try {
|
||||
tx.sign(senderPrivKey);
|
||||
} catch (Exception e1) {
|
||||
dialog.alertStatusMsg("Failed to sign the transaction");
|
||||
return null;
|
||||
}
|
||||
return tx;
|
||||
}
|
||||
|
||||
public class AccountWrapper {
|
||||
|
||||
private Account account;
|
||||
|
||||
public AccountWrapper(Account account) {
|
||||
this.account = account;
|
||||
}
|
||||
|
||||
public Account getAccount() {
|
||||
return account;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String addressShort = Utils.getAddressShortString(account.getEcKey().getAddress());
|
||||
String valueShort = Utils.getValueShortString(account.getBalance());
|
||||
String result = String.format(" By: [%s] %s", addressShort,
|
||||
valueShort);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private class JTableStorageModel extends DefaultTableModel {
|
||||
private JTableStorageModel(Map<String, String> data) {
|
||||
|
||||
if (data != null) {
|
||||
|
||||
this.setColumnCount(2);
|
||||
this.setRowCount(data.size());
|
||||
|
||||
int i = 0;
|
||||
for (String key : data.keySet()) {
|
||||
this.setValueAt(key, i, 0);
|
||||
this.setValueAt(data.get(key), i, 1);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
ContractCallDialog ccd = new ContractCallDialog(null);
|
||||
ccd.setVisible(true);
|
||||
ccd.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
|
||||
ccd.addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowClosed(WindowEvent e) {
|
||||
WorldManager.getInstance().close();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,344 @@
|
||||
package org.ethereum.gui;
|
||||
|
||||
import org.ethereum.core.Account;
|
||||
import org.ethereum.core.AccountState;
|
||||
import org.ethereum.core.Transaction;
|
||||
import org.ethereum.manager.WorldManager;
|
||||
import org.ethereum.net.client.ClientPeer;
|
||||
import org.ethereum.util.Utils;
|
||||
import org.spongycastle.util.BigIntegers;
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.plaf.ComboBoxUI;
|
||||
import javax.swing.plaf.basic.BasicComboPopup;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.math.BigInteger;
|
||||
import java.net.URL;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* www.ethereumJ.com
|
||||
* @author: Roman Mandeleil
|
||||
* Created on: 18/05/14 22:21
|
||||
*/
|
||||
class ContractSubmitDialog extends JDialog implements MessageAwareDialog {
|
||||
|
||||
private static final long serialVersionUID = -3622984456084608996L;
|
||||
|
||||
ContractSubmitDialog dialog;
|
||||
JComboBox<AccountWrapper> creatorAddressCombo;
|
||||
final JTextField gasInput;
|
||||
final JTextField contractAddrInput;
|
||||
|
||||
private byte[] initByteCode;
|
||||
|
||||
|
||||
JLabel statusMsg = null;
|
||||
|
||||
public ContractSubmitDialog(Frame parent, byte[] byteCode) {
|
||||
super(parent, "Contract Details: ", false);
|
||||
dialog = this;
|
||||
this.initByteCode = byteCode;
|
||||
|
||||
contractAddrInput = new JTextField(5);
|
||||
GUIUtils.addStyle(contractAddrInput, "Contract Address: ");
|
||||
|
||||
contractAddrInput.setBounds(70, 30, 350, 45);
|
||||
this.getContentPane().add(contractAddrInput);
|
||||
|
||||
gasInput = new JTextField(5);
|
||||
GUIUtils.addStyle(gasInput, "Gas: ");
|
||||
|
||||
JTextArea contractDataTA = new JTextArea();
|
||||
contractDataTA.setLineWrap(true);
|
||||
JScrollPane contractDataInput = new JScrollPane(contractDataTA);
|
||||
GUIUtils.addStyle(contractDataTA, null, false);
|
||||
GUIUtils.addStyle(contractDataInput, "Code:");
|
||||
|
||||
String byteCodeText = GUIUtils.getHexStyledText(byteCode);
|
||||
contractDataTA.setText(byteCodeText);
|
||||
contractDataTA.setEditable(false);
|
||||
contractDataTA.setCaretPosition(0);
|
||||
|
||||
this.getContentPane().setBackground(Color.WHITE);
|
||||
this.getContentPane().setLayout(null);
|
||||
|
||||
|
||||
contractDataInput.setBounds(70, 80, 350, 165);
|
||||
this.getContentPane().add(contractDataInput);
|
||||
|
||||
gasInput.setBounds(330, 260, 90, 45);
|
||||
this.getContentPane().add(gasInput);
|
||||
|
||||
URL rejectIconURL = ClassLoader.getSystemResource("buttons/reject.png");
|
||||
ImageIcon rejectIcon = new ImageIcon(rejectIconURL);
|
||||
JLabel rejectLabel = new JLabel(rejectIcon);
|
||||
rejectLabel.setToolTipText("Cancel");
|
||||
rejectLabel.setCursor(new Cursor(Cursor.HAND_CURSOR));
|
||||
|
||||
URL playIconURL = ClassLoader.getSystemResource("buttons/play.png");
|
||||
ImageIcon playIcon = new ImageIcon(playIconURL);
|
||||
JLabel playLabel = new JLabel(playIcon);
|
||||
playLabel.setToolTipText("Play Drafted");
|
||||
playLabel.setCursor(new Cursor(Cursor.HAND_CURSOR));
|
||||
|
||||
playLabel.setBounds(438, 100, 42, 42);
|
||||
this.getContentPane().add(playLabel);
|
||||
|
||||
playLabel.addMouseListener(
|
||||
new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
|
||||
Transaction tx = null;
|
||||
try {
|
||||
tx = createTransaction();
|
||||
} catch (Exception e1) {
|
||||
|
||||
dialog.alertStatusMsg("Failed to sign the transaction");
|
||||
return;
|
||||
}
|
||||
contractAddrInput.setText(Hex.toHexString(tx.getContractAddress()));
|
||||
|
||||
ProgramPlayDialog.createAndShowGUI(tx.getData(), tx,
|
||||
WorldManager.getInstance().getBlockchain().getLastBlock());
|
||||
}}
|
||||
);
|
||||
|
||||
JLabel statusMessage = new JLabel("");
|
||||
statusMessage.setBounds(50, 360, 400, 50);
|
||||
statusMessage.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
this.statusMsg = statusMessage;
|
||||
this.getContentPane().add(statusMessage);
|
||||
|
||||
rejectLabel.setBounds(260, 325, 45, 45);
|
||||
this.getContentPane().add(rejectLabel);
|
||||
rejectLabel.setVisible(true);
|
||||
rejectLabel.addMouseListener(
|
||||
new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
|
||||
dialog.dispose();
|
||||
}}
|
||||
);
|
||||
|
||||
URL approveIconURL = ClassLoader.getSystemResource("buttons/approve.png");
|
||||
ImageIcon approveIcon = new ImageIcon(approveIconURL);
|
||||
JLabel approveLabel = new JLabel(approveIcon);
|
||||
approveLabel.setToolTipText("Submit the transaction");
|
||||
approveLabel.setCursor(new Cursor(Cursor.HAND_CURSOR));
|
||||
|
||||
approveLabel.setBounds(200, 325, 45, 45);
|
||||
this.getContentPane().add(approveLabel);
|
||||
approveLabel.setVisible(true);
|
||||
|
||||
approveLabel.addMouseListener(
|
||||
new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
submitContract();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
gasInput.setText("1000");
|
||||
|
||||
JComboBox<AccountWrapper> creatorAddressCombo = new JComboBox<AccountWrapper>() {
|
||||
@Override
|
||||
public ComboBoxUI getUI() {
|
||||
return super.getUI();
|
||||
}
|
||||
};
|
||||
creatorAddressCombo.setOpaque(true);
|
||||
creatorAddressCombo.setEnabled(true);
|
||||
|
||||
creatorAddressCombo.setBackground(Color.WHITE);
|
||||
creatorAddressCombo.setFocusable(false);
|
||||
|
||||
this.creatorAddressCombo = creatorAddressCombo;
|
||||
|
||||
final Border line = BorderFactory.createLineBorder(Color.LIGHT_GRAY);
|
||||
JComponent editor = (JComponent)(creatorAddressCombo.getEditor().getEditorComponent());
|
||||
editor.setForeground(Color.RED);
|
||||
|
||||
Collection<Account> accounts =
|
||||
WorldManager.getInstance().getWallet().getAccountCollection();
|
||||
|
||||
for (Account account : accounts) {
|
||||
creatorAddressCombo.addItem(new AccountWrapper(account));
|
||||
}
|
||||
|
||||
creatorAddressCombo.setRenderer(new DefaultListCellRenderer() {
|
||||
|
||||
@Override
|
||||
public void paint(Graphics g) {
|
||||
setBackground(Color.WHITE);
|
||||
setForeground(new Color(143, 170, 220));
|
||||
setFont(new Font("Monospaced", 0, 13));
|
||||
setBorder(BorderFactory.createEmptyBorder());
|
||||
super.paint(g);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
creatorAddressCombo.setPopupVisible(false);
|
||||
|
||||
Object child = creatorAddressCombo.getAccessibleContext().getAccessibleChild(0);
|
||||
BasicComboPopup popup = (BasicComboPopup)child;
|
||||
|
||||
JList list = popup.getList();
|
||||
list.setSelectionBackground(Color.cyan);
|
||||
list.setBorder(null);
|
||||
|
||||
for (int i = 0; i < creatorAddressCombo.getComponentCount(); i++) {
|
||||
if (creatorAddressCombo.getComponent(i) instanceof CellRendererPane) {
|
||||
CellRendererPane crp = ((CellRendererPane)
|
||||
(creatorAddressCombo.getComponent(i)));
|
||||
}
|
||||
if (creatorAddressCombo.getComponent(i) instanceof AbstractButton) {
|
||||
((AbstractButton) creatorAddressCombo.getComponent(i))
|
||||
.setBorder(line);
|
||||
}
|
||||
}
|
||||
creatorAddressCombo.setBounds(73, 267, 230, 36);
|
||||
this.getContentPane().add(creatorAddressCombo);
|
||||
this.getContentPane().revalidate();
|
||||
this.getContentPane().repaint();
|
||||
this.setResizable(false);
|
||||
}
|
||||
|
||||
protected JRootPane createRootPane() {
|
||||
|
||||
Container parent = this.getParent();
|
||||
|
||||
if (parent != null) {
|
||||
Dimension parentSize = parent.getSize();
|
||||
Point p = parent.getLocation();
|
||||
setLocation(p.x + parentSize.width / 4, p.y + 10);
|
||||
}
|
||||
|
||||
JRootPane rootPane = new JRootPane();
|
||||
KeyStroke stroke = KeyStroke.getKeyStroke("ESCAPE");
|
||||
Action actionListener = new AbstractAction() {
|
||||
public void actionPerformed(ActionEvent actionEvent) {
|
||||
dispose();
|
||||
}
|
||||
};
|
||||
InputMap inputMap = rootPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
|
||||
inputMap.put(stroke, "ESCAPE");
|
||||
rootPane.getActionMap().put("ESCAPE", actionListener);
|
||||
|
||||
this.setSize(500, 430);
|
||||
this.setVisible(true);
|
||||
return rootPane;
|
||||
}
|
||||
|
||||
public void infoStatusMsg(String text) {
|
||||
this.statusMsg.setForeground(Color.GREEN.darker().darker());
|
||||
this.statusMsg.setText(text);
|
||||
}
|
||||
|
||||
public void alertStatusMsg(String text) {
|
||||
this.statusMsg.setForeground(Color.RED);
|
||||
this.statusMsg.setText(text);
|
||||
}
|
||||
|
||||
public void submitContract() {
|
||||
|
||||
if (!validInput())
|
||||
return;
|
||||
|
||||
Transaction tx = null;
|
||||
try {
|
||||
tx = createTransaction();
|
||||
} catch (Exception e1) {
|
||||
|
||||
dialog.alertStatusMsg("Failed to sign the transaction");
|
||||
return;
|
||||
}
|
||||
contractAddrInput.setText(Hex.toHexString(tx.getContractAddress()));
|
||||
|
||||
ClientPeer peer = WorldManager.getInstance().getActivePeer();
|
||||
if (peer == null) {
|
||||
dialog.alertStatusMsg("Not connected to any peer");
|
||||
return;
|
||||
}
|
||||
// SwingWorker
|
||||
DialogWorker worker = new DialogWorker(tx, this);
|
||||
worker.execute();
|
||||
}
|
||||
|
||||
private Transaction createTransaction() {
|
||||
|
||||
Account account = ((AccountWrapper)creatorAddressCombo.getSelectedItem()).getAccount();
|
||||
|
||||
byte[] senderPrivKey = account.getEcKey().getPrivKeyBytes();
|
||||
byte[] nonce = account.getNonce() == BigInteger.ZERO ? null : account.getNonce().toByteArray();
|
||||
byte[] gasPrice = new BigInteger("10000000000000").toByteArray();
|
||||
|
||||
BigInteger gasBI = new BigInteger(gasInput.getText());
|
||||
byte[] gasValue = BigIntegers.asUnsignedByteArray(gasBI);
|
||||
byte[] endowment = BigIntegers.asUnsignedByteArray(new BigInteger("1000"));
|
||||
|
||||
byte[] zeroAddress = null;
|
||||
|
||||
Transaction tx = new Transaction(nonce, gasPrice, gasValue,
|
||||
zeroAddress, endowment, initByteCode);
|
||||
|
||||
tx.sign(senderPrivKey);
|
||||
|
||||
return tx;
|
||||
}
|
||||
|
||||
private boolean validInput() {
|
||||
|
||||
Account account = ((AccountWrapper)creatorAddressCombo.getSelectedItem()).getAccount();
|
||||
BigInteger currentBalance = account.getBalance();
|
||||
BigInteger gasPrice = BigInteger.valueOf(WorldManager.getInstance().getBlockchain().getGasPrice());
|
||||
BigInteger gasInput = new BigInteger( this.gasInput.getText());
|
||||
|
||||
boolean canAfford = currentBalance.compareTo(gasPrice.multiply(gasInput)) >= 0;
|
||||
|
||||
if (!canAfford) {
|
||||
alertStatusMsg("The address can't afford this transaction");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
AccountState as = new AccountState();
|
||||
ContractSubmitDialog pod = new ContractSubmitDialog(null, null);
|
||||
pod.setVisible(true);
|
||||
}
|
||||
|
||||
public class AccountWrapper {
|
||||
|
||||
private Account account;
|
||||
|
||||
public AccountWrapper(Account account) {
|
||||
this.account = account;
|
||||
}
|
||||
|
||||
public Account getAccount() {
|
||||
return account;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String addressShort = Utils.getAddressShortString(account.getEcKey().getAddress());
|
||||
String valueShort = Utils.getValueShortString(account.getBalance());
|
||||
|
||||
String result = String.format(" By: [%s] %s", addressShort, valueShort);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,64 @@
|
||||
package org.ethereum.gui;
|
||||
|
||||
import org.ethereum.core.Transaction;
|
||||
import org.ethereum.manager.WorldManager;
|
||||
import org.ethereum.net.submit.TransactionExecutor;
|
||||
import org.ethereum.net.submit.TransactionTask;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import static org.ethereum.config.SystemProperties.CONFIG;
|
||||
|
||||
/**
|
||||
* www.ethereumJ.com
|
||||
* @author: Roman Mandeleil
|
||||
* Created on: 26/05/2014 12:27
|
||||
*/
|
||||
public class DialogWorker extends SwingWorker<Transaction, Object> {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(DialogWorker.class);
|
||||
|
||||
private Transaction tx;
|
||||
private MessageAwareDialog dialog;
|
||||
|
||||
public DialogWorker(Transaction tx, MessageAwareDialog dialog) {
|
||||
this.tx = tx;
|
||||
this.dialog = dialog;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Transaction doInBackground() throws Exception {
|
||||
TransactionTask transactionTask = new TransactionTask(tx);
|
||||
Future<Transaction> future = TransactionExecutor.instance.submitTransaction(transactionTask);
|
||||
dialog.infoStatusMsg("Transaction sent to the network, waiting for approve");
|
||||
|
||||
try {
|
||||
future.get(CONFIG.transactionApproveTimeout(), TimeUnit.SECONDS);
|
||||
} catch (TimeoutException toe) {
|
||||
logger.error(toe.getMessage(), toe);
|
||||
dialog.alertStatusMsg("Transaction wasn't approved, network timeout");
|
||||
return null;
|
||||
} catch (InterruptedException ie) {
|
||||
logger.error(ie.getMessage(), ie);
|
||||
dialog.alertStatusMsg("Transaction wasn't approved");
|
||||
return null;
|
||||
} catch (ExecutionException ee) {
|
||||
logger.error(ee.getMessage(), ee);
|
||||
dialog.alertStatusMsg("Transaction wasn't approved");
|
||||
return null;
|
||||
} finally {
|
||||
future.cancel(true);
|
||||
}
|
||||
|
||||
dialog.infoStatusMsg("Transaction got approved");
|
||||
WorldManager.getInstance().getWallet().applyTransaction(tx);
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
package org.ethereum.gui;
|
||||
|
||||
import org.ethereum.serpent.SerpentCompiler;
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.border.CompoundBorder;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.border.TitledBorder;
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* www.ethereumJ.com
|
||||
* @author: Roman Mandeleil
|
||||
* Created on: 23/05/2014 13:51
|
||||
*/
|
||||
public class GUIUtils {
|
||||
|
||||
public static void addStyle(JTextField textField, String labelName) {
|
||||
textField.setHorizontalAlignment(SwingConstants.RIGHT);
|
||||
Border line = BorderFactory.createLineBorder(Color.LIGHT_GRAY);
|
||||
TitledBorder titled = BorderFactory.createTitledBorder(line, labelName);
|
||||
titled.setTitleFont(new Font("Verdana", 0, 13));
|
||||
titled.setTitleColor(new Color(213, 225, 185));
|
||||
Border empty = new EmptyBorder(5, 8, 5, 8);
|
||||
CompoundBorder border = new CompoundBorder(titled, empty);
|
||||
textField.setBorder(border);
|
||||
textField.setForeground(new Color(143, 170, 220));
|
||||
textField.setFont(new Font("Monospaced", 0, 13));
|
||||
}
|
||||
|
||||
public static void addStyle(JTextArea textArea, String labelName, boolean isBorder) {
|
||||
|
||||
Border border = null;
|
||||
if (isBorder) {
|
||||
Border line = BorderFactory.createLineBorder(Color.LIGHT_GRAY);
|
||||
TitledBorder titled = BorderFactory.createTitledBorder(line, labelName);
|
||||
titled.setTitleFont(new Font("Verdana", 0, 13));
|
||||
titled.setTitleColor(new Color(213, 225, 185));
|
||||
Border empty = new EmptyBorder(5, 8, 5, 8);
|
||||
CompoundBorder cBorder = new CompoundBorder(titled, empty);
|
||||
}
|
||||
textArea.setBorder(border);
|
||||
textArea.setForeground(new Color(143, 170, 220));
|
||||
textArea.setFont(new Font("Monospaced", 0, 13));
|
||||
}
|
||||
|
||||
public static void addStyle(JScrollPane jScrollPane, String labelName) {
|
||||
Border line = BorderFactory.createLineBorder(Color.LIGHT_GRAY);
|
||||
TitledBorder titled = BorderFactory.createTitledBorder(line, labelName);
|
||||
titled.setTitleFont(new Font("Verdana", 0, 13));
|
||||
titled.setTitleColor(new Color(213, 225, 185));
|
||||
Border empty = new EmptyBorder(5, 8, 5, 8);
|
||||
CompoundBorder border = new CompoundBorder(titled, empty);
|
||||
jScrollPane.setBorder(border);
|
||||
jScrollPane.setForeground(new Color(143, 170, 220));
|
||||
jScrollPane.setBackground(Color.WHITE);
|
||||
jScrollPane.setFont(new Font("Monospaced", 0, 13));
|
||||
jScrollPane.setHorizontalScrollBar(null);
|
||||
}
|
||||
|
||||
public static void addStyle(JTable jTable) {
|
||||
jTable.setForeground(new Color(143, 170, 220));
|
||||
jTable.setBackground(Color.WHITE);
|
||||
jTable.setFont(new Font("Monospaced", 0, 13));
|
||||
}
|
||||
|
||||
public static String getHexStyledText(byte[] data) {
|
||||
String[] dataHex = Hex.toHexString(data).split("(?<=\\G.{2})");
|
||||
StringBuffer sb = new StringBuffer();
|
||||
|
||||
for (int i = 0; i < dataHex.length; ++i) {
|
||||
sb.append(dataHex[i]).append(" ");
|
||||
if ((i + 1) % 8 == 0 && i != 0) sb.append("\n");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String getStyledAsmCode(String asmCode) {
|
||||
|
||||
String initBlock = SerpentCompiler.extractInitBlock(asmCode);
|
||||
String codeBlock = SerpentCompiler.extractCodeBlock(asmCode);
|
||||
|
||||
return String.format(" \n\n *** [Init] *** \n\n %s \n" +
|
||||
"\n *** [Code] *** \n\n %s \n\n", initBlock, codeBlock);
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package org.ethereum.gui;
|
||||
|
||||
/**
|
||||
* This interface describes the methods required
|
||||
* for any dialog that displays info- and alert status messages.
|
||||
*/
|
||||
public interface MessageAwareDialog {
|
||||
|
||||
public void infoStatusMsg(final String text);
|
||||
|
||||
public void alertStatusMsg(final String text);
|
||||
}
|
@ -0,0 +1,273 @@
|
||||
package org.ethereum.gui;
|
||||
|
||||
import org.ethereum.core.Account;
|
||||
import org.ethereum.core.AccountState;
|
||||
import org.ethereum.core.Transaction;
|
||||
import org.ethereum.manager.WorldManager;
|
||||
import org.ethereum.net.client.ClientPeer;
|
||||
import org.spongycastle.util.BigIntegers;
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.math.BigInteger;
|
||||
import java.net.URL;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
/**
|
||||
* www.ethereumJ.com
|
||||
* @author: Roman Mandeleil
|
||||
* Created on: 18/05/14 22:21
|
||||
*/
|
||||
class PayOutDialog extends JDialog implements MessageAwareDialog {
|
||||
|
||||
private static final long serialVersionUID = -2838121935782110981L;
|
||||
|
||||
private PayOutDialog dialog;
|
||||
|
||||
private AccountState accountState = null;
|
||||
private JLabel statusMsg = null;
|
||||
|
||||
private final JTextField receiverInput;
|
||||
private final JTextField amountInput;
|
||||
private final JTextField feeInput;
|
||||
|
||||
public PayOutDialog(Frame parent, final Account account) {
|
||||
super(parent, "Payout details: ", false);
|
||||
dialog = this;
|
||||
|
||||
this.accountState = account;
|
||||
|
||||
receiverInput = new JTextField(18);
|
||||
GUIUtils.addStyle(receiverInput, "Pay to:");
|
||||
|
||||
amountInput = new JTextField(18);
|
||||
GUIUtils.addStyle(amountInput, "Amount: ");
|
||||
|
||||
feeInput = new JTextField(5);
|
||||
GUIUtils.addStyle(feeInput, "Fee: ");
|
||||
|
||||
this.getContentPane().setBackground(Color.WHITE);
|
||||
this.getContentPane().setLayout(null);
|
||||
|
||||
receiverInput.setBounds(70, 30, 350, 45);
|
||||
this.getContentPane().add(receiverInput);
|
||||
|
||||
amountInput.setBounds(70, 80, 250, 45);
|
||||
this.getContentPane().add(amountInput);
|
||||
|
||||
feeInput.setBounds(330, 80, 90, 45);
|
||||
this.getContentPane().add(feeInput);
|
||||
|
||||
URL rejectIconURL = ClassLoader.getSystemResource("buttons/reject.png");
|
||||
ImageIcon rejectIcon = new ImageIcon(rejectIconURL);
|
||||
JLabel rejectLabel = new JLabel(rejectIcon);
|
||||
rejectLabel.setToolTipText("Cancel");
|
||||
rejectLabel.setCursor(new Cursor(Cursor.HAND_CURSOR));
|
||||
|
||||
JLabel statusMessage = new JLabel("");
|
||||
statusMessage.setBounds(50, 180, 400, 50);
|
||||
statusMessage.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
this.statusMsg = statusMessage;
|
||||
this.getContentPane().add(statusMessage);
|
||||
|
||||
rejectLabel.setBounds(260, 145, 45, 45);
|
||||
this.getContentPane().add(rejectLabel);
|
||||
rejectLabel.setVisible(true);
|
||||
rejectLabel.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
dialog.dispose();
|
||||
}
|
||||
});
|
||||
|
||||
URL approveIconURL = ClassLoader.getSystemResource("buttons/approve.png");
|
||||
ImageIcon approveIcon = new ImageIcon(approveIconURL);
|
||||
JLabel approveLabel = new JLabel(approveIcon);
|
||||
approveLabel.setToolTipText("Submit the transaction");
|
||||
approveLabel.setCursor(new Cursor(Cursor.HAND_CURSOR));
|
||||
|
||||
approveLabel.setBounds(200, 145, 45, 45);
|
||||
this.getContentPane().add(approveLabel);
|
||||
approveLabel.setVisible(true);
|
||||
|
||||
approveLabel.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
|
||||
if (!validInput()) {
|
||||
return;
|
||||
}
|
||||
|
||||
BigInteger fee = new BigInteger(feeInput.getText());
|
||||
BigInteger value = new BigInteger(amountInput.getText());
|
||||
byte[] address = Hex.decode(receiverInput.getText());
|
||||
|
||||
// Client
|
||||
ClientPeer peer = WorldManager.getInstance().getActivePeer();
|
||||
|
||||
if (peer == null) {
|
||||
dialog.alertStatusMsg("Not connected to any peer");
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] senderPrivKey = account.getEcKey().getPrivKeyBytes();
|
||||
byte[] nonce = accountState.getNonce() == BigInteger.ZERO ? null : accountState.getNonce().toByteArray();
|
||||
|
||||
byte[] gasPrice = BigInteger.valueOf( WorldManager.getInstance().getBlockchain().getGasPrice()).toByteArray();
|
||||
|
||||
Transaction tx = new Transaction(nonce, gasPrice, BigIntegers
|
||||
.asUnsignedByteArray(fee), address, BigIntegers
|
||||
.asUnsignedByteArray(value), null);
|
||||
|
||||
try {
|
||||
tx.sign(senderPrivKey);
|
||||
} catch (Exception e1) {
|
||||
dialog.alertStatusMsg("Failed to sign the transaction");
|
||||
return;
|
||||
}
|
||||
|
||||
// SwingWorker
|
||||
DialogWorker worker = new DialogWorker(tx, dialog);
|
||||
worker.execute();
|
||||
}
|
||||
});
|
||||
|
||||
feeInput.setText("1000");
|
||||
amountInput.setText("0");
|
||||
|
||||
this.getContentPane().revalidate();
|
||||
this.getContentPane().repaint();
|
||||
this.setResizable(false);
|
||||
}
|
||||
|
||||
private boolean validInput() {
|
||||
|
||||
String receiverText = receiverInput.getText();
|
||||
if (receiverText == null || receiverText.isEmpty()) {
|
||||
alertStatusMsg("Should specify valid receiver address");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Pattern.matches("[0-9a-fA-F]+", receiverText)) {
|
||||
alertStatusMsg("Should specify valid receiver address");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Hex.decode(receiverText).length != 20) {
|
||||
alertStatusMsg("Should specify valid receiver address");
|
||||
return false;
|
||||
}
|
||||
|
||||
String amountText = amountInput.getText();
|
||||
if (amountText == null || amountText.isEmpty()) {
|
||||
alertStatusMsg("Should specify amount to transfer");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Pattern.matches("[0-9]+", amountText)) {
|
||||
alertStatusMsg("Should specify numeric value for amount ");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (amountText.equals("0")) {
|
||||
alertStatusMsg("Should specify more than zero for transaction");
|
||||
return false;
|
||||
}
|
||||
|
||||
String feeText = feeInput.getText();
|
||||
if (feeText == null || feeText.isEmpty()) {
|
||||
alertStatusMsg("Should specify fee to fund the transaction");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Pattern.matches("[0-9]+", feeText)) {
|
||||
alertStatusMsg("Should specify numeric value for a fee");
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if the tx is affordable
|
||||
BigInteger ammountValue = new BigInteger(amountText);
|
||||
BigInteger feeValue = new BigInteger(feeText);
|
||||
BigInteger gasPrice = BigInteger.valueOf(WorldManager.getInstance().getBlockchain().getGasPrice());
|
||||
BigInteger currentBalance = accountState.getBalance();
|
||||
|
||||
boolean canAfford = gasPrice.multiply(feeValue).add(ammountValue).compareTo(currentBalance) != 1;
|
||||
|
||||
if (!canAfford) {
|
||||
alertStatusMsg("The address can't afford this transaction");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected JRootPane createRootPane() {
|
||||
|
||||
Container parent = this.getParent();
|
||||
|
||||
if (parent != null) {
|
||||
Dimension parentSize = parent.getSize();
|
||||
Point p = parent.getLocation();
|
||||
setLocation(p.x + parentSize.width / 4, p.y + parentSize.height / 4);
|
||||
}
|
||||
|
||||
JRootPane rootPane = new JRootPane();
|
||||
KeyStroke stroke = KeyStroke.getKeyStroke("ESCAPE");
|
||||
Action actionListener = new AbstractAction() {
|
||||
public void actionPerformed(ActionEvent actionEvent) {
|
||||
dispose();
|
||||
}
|
||||
};
|
||||
InputMap inputMap = rootPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
|
||||
inputMap.put(stroke, "ESCAPE");
|
||||
rootPane.getActionMap().put("ESCAPE", actionListener);
|
||||
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
setSize(500, 255);
|
||||
setVisible(true);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return rootPane;
|
||||
}
|
||||
|
||||
public void infoStatusMsg(final String text) {
|
||||
|
||||
final PayOutDialog dialog = this;
|
||||
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
dialog.statusMsg.setForeground(Color.GREEN.darker().darker());
|
||||
dialog.statusMsg.setText(text);
|
||||
dialog.revalidate();
|
||||
dialog.repaint();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void alertStatusMsg(final String text) {
|
||||
final PayOutDialog dialog = this;
|
||||
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
dialog.statusMsg.setForeground(Color.RED);
|
||||
dialog.statusMsg.setText(text);
|
||||
dialog.revalidate();
|
||||
dialog.repaint();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
Account account = new Account();
|
||||
PayOutDialog pod = new PayOutDialog(null, account);
|
||||
pod.setVisible(true);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,169 @@
|
||||
package org.ethereum.gui;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
import java.util.Timer;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
|
||||
|
||||
import org.ethereum.geo.IpGeoDB;
|
||||
import org.ethereum.manager.WorldManager;
|
||||
import org.ethereum.net.client.PeerData;
|
||||
import org.ethereum.net.message.HelloMessage;
|
||||
import org.ethereum.util.Utils;
|
||||
|
||||
import com.maxmind.geoip.Location;
|
||||
|
||||
/**
|
||||
* www.ethereumJ.com
|
||||
* @author: Roman Mandeleil
|
||||
* Created on: 25/04/14 07:04
|
||||
*/
|
||||
public class PeersTableModel extends AbstractTableModel {
|
||||
|
||||
private List<PeerInfo> peerInfoList = new ArrayList<PeerInfo>();
|
||||
Timer updater = new Timer();
|
||||
|
||||
public PeersTableModel() {
|
||||
updater.scheduleAtFixedRate(new TimerTask() {
|
||||
public void run() {
|
||||
SwingUtilities.invokeLater(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateModel();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}, 0, 100);
|
||||
}
|
||||
|
||||
public String getColumnName(int column) {
|
||||
if (column == 0) return "Location";
|
||||
if (column == 1) return "IP";
|
||||
if (column == 2) return "Environment";
|
||||
if (column == 3) return "Live";
|
||||
else return "";
|
||||
}
|
||||
|
||||
public boolean isCellEditable(int row, int column) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Class<?> getColumnClass(int column) {
|
||||
if (column == 0) return ImageIcon.class;
|
||||
if (column == 1) return String.class;
|
||||
if (column == 2) return String.class;
|
||||
if (column == 3) return ImageIcon.class;
|
||||
else return String.class;
|
||||
}
|
||||
|
||||
public Object getValueAt(int row, int column) {
|
||||
|
||||
PeerInfo peerInfo = peerInfoList.get(row);
|
||||
|
||||
if (column == 0) {
|
||||
String countryCode = peerInfo.getLocation().countryCode;
|
||||
|
||||
ImageIcon flagIcon = null;
|
||||
if (countryCode != null){
|
||||
URL flagURL = ClassLoader.getSystemResource("flags/" + countryCode.toLowerCase() + ".png");
|
||||
flagIcon = new ImageIcon(flagURL);
|
||||
}
|
||||
return flagIcon;
|
||||
}
|
||||
if (column == 1)
|
||||
return peerInfo.getIp().getHostAddress();
|
||||
|
||||
if (column == 2) {
|
||||
|
||||
if (peerInfo.getLastAccessed() == 0)
|
||||
return "?";
|
||||
else
|
||||
return (System.currentTimeMillis() - peerInfo.getLastAccessed()) / 1000 + " seconds ago";
|
||||
}
|
||||
|
||||
if (column == 3) {
|
||||
|
||||
ImageIcon flagIcon = null;
|
||||
if (peerInfo.isConnected()) {
|
||||
flagIcon = Utils.getImageIcon("connected.png");
|
||||
} else {
|
||||
flagIcon = Utils.getImageIcon("disconnected.png");
|
||||
}
|
||||
return flagIcon;
|
||||
}
|
||||
else return "";
|
||||
}
|
||||
|
||||
public int getRowCount() {
|
||||
return this.peerInfoList.size();
|
||||
}
|
||||
|
||||
public int getColumnCount() {
|
||||
return 4;
|
||||
}
|
||||
|
||||
public void updateModel() {
|
||||
synchronized (peerInfoList) {
|
||||
peerInfoList.clear();
|
||||
|
||||
final Queue<PeerData> peers = WorldManager.getInstance().getPeers();
|
||||
for (PeerData peer : peers) {
|
||||
InetAddress addr = peer.getInetAddress();
|
||||
Location cr = IpGeoDB.getLocationForIp(addr);
|
||||
peerInfoList.add(new PeerInfo(cr, addr, peer.isOnline(), peer.getHandshake(), peer.getLastCheckTime()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class PeerInfo {
|
||||
|
||||
Location location;
|
||||
InetAddress ip;
|
||||
boolean connected;
|
||||
HelloMessage handshake;
|
||||
long lastAccessed = 0;
|
||||
|
||||
|
||||
private PeerInfo(Location location, InetAddress ip, boolean isConnected,
|
||||
HelloMessage handshake, long lastAccessed) {
|
||||
|
||||
if (location == null)
|
||||
this.location = new Location();
|
||||
else
|
||||
this.location = location;
|
||||
|
||||
this.ip = ip;
|
||||
this.connected = isConnected;
|
||||
this.handshake = handshake;
|
||||
this.lastAccessed = lastAccessed;
|
||||
}
|
||||
|
||||
private Location getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
private InetAddress getIp() {
|
||||
return ip;
|
||||
}
|
||||
|
||||
private boolean isConnected() {
|
||||
return connected;
|
||||
}
|
||||
|
||||
public HelloMessage getHandshake() {
|
||||
return handshake;
|
||||
}
|
||||
|
||||
public long getLastAccessed() {
|
||||
return lastAccessed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,114 @@
|
||||
package org.ethereum.gui;
|
||||
|
||||
import org.ethereum.manager.WorldManager;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.table.DefaultTableCellRenderer;
|
||||
import javax.swing.table.TableCellRenderer;
|
||||
|
||||
import static org.ethereum.config.SystemProperties.CONFIG;
|
||||
|
||||
/**
|
||||
* www.ethereumJ.com
|
||||
* @author: Roman Mandeleil
|
||||
* Created on: 25/04/14 07:11
|
||||
*/
|
||||
public class PeersTableWindow extends JFrame {
|
||||
|
||||
// Instance attributes used in this example
|
||||
private JPanel topPanel;
|
||||
private JTable table;
|
||||
private JScrollPane scrollPane;
|
||||
private Timer updater = new Timer();
|
||||
|
||||
private ToolBar toolBar;
|
||||
|
||||
// Constructor of main frame
|
||||
public PeersTableWindow(ToolBar toolBar) {
|
||||
|
||||
this.toolBar = toolBar;
|
||||
addCloseAction();
|
||||
|
||||
// Set the frame characteristics
|
||||
setTitle("Ethereum Peers");
|
||||
setSize(515, 400);
|
||||
setLocation(615, 30);
|
||||
|
||||
java.net.URL url = ClassLoader.getSystemResource("ethereum-icon.png");
|
||||
Toolkit kit = Toolkit.getDefaultToolkit();
|
||||
Image img = kit.createImage(url);
|
||||
this.setIconImage(img);
|
||||
|
||||
// Create a panel to hold all other components
|
||||
topPanel = new JPanel();
|
||||
topPanel.setLayout(new BorderLayout());
|
||||
topPanel.setBackground(Color.WHITE);
|
||||
|
||||
getContentPane().add(topPanel);
|
||||
getContentPane().setBackground(Color.WHITE);
|
||||
|
||||
// Create a new table instance
|
||||
table = new JTable();
|
||||
table.setModel(new PeersTableModel());
|
||||
|
||||
table.setFont(new Font("Courier New", Font.PLAIN, 15));
|
||||
table.setForeground(Color.GRAY);
|
||||
table.setTableHeader(null);
|
||||
|
||||
TableCellRenderer tcr = table.getDefaultRenderer(String.class);
|
||||
DefaultTableCellRenderer renderer = (DefaultTableCellRenderer) tcr;
|
||||
renderer.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
|
||||
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
table.setCellSelectionEnabled(true);
|
||||
|
||||
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
|
||||
table.getColumnModel().getColumn(0).setPreferredWidth(60);
|
||||
table.getColumnModel().getColumn(1).setPreferredWidth(200);
|
||||
table.getColumnModel().getColumn(2).setPreferredWidth(160);
|
||||
table.getColumnModel().getColumn(3).setPreferredWidth(60);
|
||||
|
||||
table.setRowMargin(3);
|
||||
table.setRowHeight(50);
|
||||
|
||||
table.setShowHorizontalLines(true);
|
||||
table.setShowVerticalLines(true);
|
||||
table.setGridColor(new Color(230, 230, 230));
|
||||
|
||||
// Add the table to a scrolling pane
|
||||
scrollPane = new JScrollPane(table);
|
||||
scrollPane.getViewport().setBackground(Color.WHITE);
|
||||
|
||||
topPanel.add(scrollPane, BorderLayout.CENTER);
|
||||
|
||||
updater.scheduleAtFixedRate(new TimerTask() {
|
||||
public void run() {
|
||||
table.revalidate();
|
||||
table.repaint();
|
||||
}
|
||||
}, 1000, 1000);
|
||||
|
||||
if (CONFIG.peerDiscovery())
|
||||
WorldManager.getInstance().startPeerDiscovery();
|
||||
}
|
||||
|
||||
public void addCloseAction() {
|
||||
this.addWindowListener(new WindowAdapter() {
|
||||
public void windowClosing(WindowEvent e) {
|
||||
toolBar.peersToggle.setSelected(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
PeersTableWindow mainFrame = new PeersTableWindow(null);
|
||||
mainFrame.setVisible(true);
|
||||
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
}
|
||||
}
|
@ -0,0 +1,190 @@
|
||||
package org.ethereum.gui;
|
||||
|
||||
import org.ethereum.core.Block;
|
||||
import org.ethereum.core.Transaction;
|
||||
import org.ethereum.db.Repository;
|
||||
import org.ethereum.manager.WorldManager;
|
||||
import org.ethereum.vm.*;
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* www.ethereumJ.com
|
||||
* @author: Roman Mandeleil
|
||||
* Created on: 02/06/2014 16:58
|
||||
*/
|
||||
|
||||
public class ProgramPlayDialog extends JPanel implements ActionListener,
|
||||
ChangeListener, Program.ProgramListener {
|
||||
|
||||
private List<String> outputList;
|
||||
private JTextArea console;
|
||||
private JSlider stepSlider;
|
||||
|
||||
public ProgramPlayDialog(byte[] code) {
|
||||
|
||||
outputList = new ArrayList<String>();
|
||||
VM vm = new VM();
|
||||
|
||||
ProgramInvoke pi = new ProgramInvokeMockImpl();
|
||||
Program program = new Program(code, pi);
|
||||
|
||||
program.addListener(this);
|
||||
program.fullTrace();
|
||||
vm.play(program);
|
||||
|
||||
doGUI();
|
||||
}
|
||||
|
||||
public ProgramPlayDialog(byte[] code, Transaction tx, Block lastBlock) {
|
||||
|
||||
outputList = new ArrayList<String>();
|
||||
VM vm = new VM();
|
||||
|
||||
Repository tractRepository = WorldManager.getInstance().getRepository().getTrack();
|
||||
|
||||
Program program = new Program(code ,
|
||||
ProgramInvokeFactory.createProgramInvoke(tx, lastBlock, tractRepository));
|
||||
|
||||
program.addListener(this);
|
||||
program.fullTrace();
|
||||
vm.play(program);
|
||||
|
||||
tractRepository.rollback();
|
||||
|
||||
doGUI();
|
||||
}
|
||||
|
||||
public void doGUI() {
|
||||
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
|
||||
|
||||
//Create the slider.
|
||||
stepSlider = new JSlider(JSlider.HORIZONTAL,
|
||||
0, outputList.size() - 1, 0);
|
||||
|
||||
stepSlider.addChangeListener(this);
|
||||
|
||||
//Turn on labels at major tick marks.
|
||||
|
||||
stepSlider.setMajorTickSpacing(1);
|
||||
if (outputList.size() > 40)
|
||||
stepSlider.setMajorTickSpacing(3);
|
||||
if (outputList.size() > 100)
|
||||
stepSlider.setMajorTickSpacing(20);
|
||||
|
||||
stepSlider.setMinorTickSpacing(1);
|
||||
stepSlider.setPaintTicks(true);
|
||||
stepSlider.setPaintLabels(true);
|
||||
stepSlider.setBorder(
|
||||
BorderFactory.createEmptyBorder(0, 0, 10, 0));
|
||||
Font font = new Font("Courier New", Font.PLAIN, 10);
|
||||
stepSlider.setFont(font);
|
||||
stepSlider.addChangeListener(this);
|
||||
|
||||
//Create the label that displays the animation.
|
||||
|
||||
int i = stepSlider.getValue();
|
||||
console = new JTextArea(outputList.get(i));
|
||||
console.setFont(new Font("Courier New", Font.PLAIN, 13));
|
||||
console.setForeground(new Color(183, 209, 253));
|
||||
console.setBackground(Color.BLACK);
|
||||
console.setLineWrap(true);
|
||||
|
||||
stepSlider.setFocusable(true);
|
||||
|
||||
JScrollPane scrollPane = new JScrollPane(console,
|
||||
ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
|
||||
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
scrollPane.getVerticalScrollBar().setPreferredSize(new Dimension(10, 0));
|
||||
|
||||
add(scrollPane);
|
||||
add(stepSlider);
|
||||
}
|
||||
|
||||
public void setFocus() {
|
||||
stepSlider.requestFocus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
JSlider source = (JSlider)e.getSource();
|
||||
int step = (int)source.getValue();
|
||||
|
||||
int i = source.getValue();
|
||||
String out = outputList.get(i);
|
||||
|
||||
console.setText(out);
|
||||
console.setCaretPosition(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the GUI and show it. For thread safety,
|
||||
* this method should be invoked from the
|
||||
* event-dispatching thread.
|
||||
*/
|
||||
public static void createAndShowGUI(byte[] runCode, Transaction tx, Block lastBlock) {
|
||||
|
||||
ProgramPlayDialog ppd;
|
||||
if (tx != null)
|
||||
ppd = new ProgramPlayDialog(runCode, tx, lastBlock);
|
||||
else{
|
||||
ppd = new ProgramPlayDialog(runCode);
|
||||
}
|
||||
|
||||
//Create and set up the window.
|
||||
JFrame frame = new JFrame("Program Draft Play");
|
||||
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
|
||||
|
||||
java.net.URL url = ClassLoader.getSystemResource("ethereum-icon.png");
|
||||
Toolkit kit = Toolkit.getDefaultToolkit();
|
||||
Image img = kit.createImage(url);
|
||||
frame.setIconImage(img);
|
||||
|
||||
frame.setPreferredSize(new Dimension(580, 500));
|
||||
frame.setLocation(400, 200);
|
||||
|
||||
//Add content to the window.
|
||||
frame.add(ppd, BorderLayout.CENTER);
|
||||
|
||||
//Display the window.
|
||||
frame.pack();
|
||||
frame.setVisible(true);
|
||||
ppd.setFocus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void output(String out) {
|
||||
outputList.add(out);
|
||||
}
|
||||
|
||||
public static void main(String []args) {
|
||||
|
||||
/* Turn off metal's use of bold fonts */
|
||||
UIManager.put("swing.boldMetal", Boolean.FALSE);
|
||||
|
||||
//Schedule a job for the event-dispatching thread:
|
||||
//creating and showing this application's GUI.
|
||||
|
||||
String asmCode ="11 0 MSTORE 22 32 MSTORE 33 64 MSTORE 44 96 MSTORE 55 128 MSTORE 66 160 MSTORE 192 0 RETURN";
|
||||
// final byte[] code = SerpentCompiler.compileAssemblyToMachine(asmCode);
|
||||
|
||||
final byte[] code = Hex.decode("7f4e616d65526567000000000000000000000000000000000000000000000000003057307f4e616d6552656700000000000000000000000000000000000000000000000000573360455760415160566000396000f20036602259604556330e0f600f5933ff33560f601e5960003356576000335700604158600035560f602b590033560f60365960003356573360003557600035335700");
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
createAndShowGUI(code, null, null);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,481 @@
|
||||
package org.ethereum.gui;
|
||||
|
||||
import org.ethereum.serpent.SerpentCompiler;
|
||||
import org.fife.ui.rsyntaxtextarea.*;
|
||||
import org.fife.ui.rtextarea.RTextScrollPane;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.util.Scanner;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.ethereum.config.SystemProperties.CONFIG;
|
||||
|
||||
/**
|
||||
* www.ethereumJ.com
|
||||
* @author: Roman Mandeleil
|
||||
* Created on: 24/04/14 11:32
|
||||
*/
|
||||
public class SerpentEditor extends JFrame {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger("gui");
|
||||
|
||||
private String codeSample = "\n\n\n" +
|
||||
"" +
|
||||
"if !contract.storage[msg.data[0]]:\n" +
|
||||
" contract.storage[msg.data[0]] = msg.data[1]\n" +
|
||||
" return(1)\n" +
|
||||
"else:\n" +
|
||||
" return(0)\n";
|
||||
|
||||
private String codeSample2 = "\n\n\n" +
|
||||
"" +
|
||||
"\n" +
|
||||
"\n" +
|
||||
"a=block.gaslimit\n" +
|
||||
"b = block.difficulty\n" +
|
||||
"if 10*2+5 > 15:\n" +
|
||||
" b = 2\n" +
|
||||
"elif 2*6+5 < a ^ 6:\n" +
|
||||
" c = 4\n" +
|
||||
"else:\n" +
|
||||
" d = 5\n" +
|
||||
" \n" +
|
||||
"\n" +
|
||||
"return(0)\n";
|
||||
|
||||
private String defaultCode = "\n" +
|
||||
"\n" +
|
||||
"init: \n" +
|
||||
"\n" +
|
||||
" // [init block] - executed once when contract\n" +
|
||||
" // being initialized.\n" +
|
||||
" contract.storage[999] = 3 \n" +
|
||||
"\n" +
|
||||
"code:\n" +
|
||||
"\n" +
|
||||
" // [code block] - the actual code\n" +
|
||||
" // executed when the call msg\n" +
|
||||
" // hit the peer\n" +
|
||||
" a = contract.storage[999]\n" +
|
||||
" b = msg.data[a]\n";
|
||||
|
||||
private final RSyntaxTextArea codeArea;
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final JSplitPane splitPanel;
|
||||
private final JTextArea result;
|
||||
private final JPanel contentPane;
|
||||
private JFileChooser fileChooser = null;
|
||||
|
||||
private ToolBar toolBar = null;
|
||||
|
||||
public SerpentEditor(ToolBar toolBar) {
|
||||
|
||||
this.toolBar = toolBar;
|
||||
addCloseAction();
|
||||
contentPane = new JPanel(new BorderLayout());
|
||||
|
||||
URL url = ClassLoader.getSystemResource("ethereum-icon.png");
|
||||
Toolkit kit = Toolkit.getDefaultToolkit();
|
||||
Image img = kit.createImage(url);
|
||||
this.setIconImage(img);
|
||||
this.setLocation(30, 70);
|
||||
|
||||
AbstractTokenMakerFactory atmf = (AbstractTokenMakerFactory)TokenMakerFactory.getDefaultInstance();
|
||||
atmf.putMapping("text/serpent", "org.ethereum.gui.SerpentTokenMaker");
|
||||
|
||||
codeArea = new RSyntaxTextArea(32, 80);
|
||||
codeArea.setSyntaxEditingStyle("text/serpent");
|
||||
codeArea.setCodeFoldingEnabled(true);
|
||||
codeArea.setAntiAliasingEnabled(true);
|
||||
codeArea.setText(defaultCode);
|
||||
|
||||
changeStyleProgrammatically();
|
||||
|
||||
RTextScrollPane sp = new RTextScrollPane(codeArea);
|
||||
|
||||
sp.setFoldIndicatorEnabled(true);
|
||||
contentPane.setLayout(new BorderLayout());
|
||||
|
||||
splitPanel = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
|
||||
splitPanel.setOneTouchExpandable(true);
|
||||
splitPanel.setDividerSize(5);
|
||||
splitPanel.setContinuousLayout(true);
|
||||
|
||||
contentPane.add(splitPanel, BorderLayout.CENTER);
|
||||
splitPanel.add(sp);
|
||||
|
||||
result = new JTextArea();
|
||||
result.setLineWrap(true);
|
||||
result.setWrapStyleWord(true);
|
||||
result.setVisible(false);
|
||||
|
||||
splitPanel.add(result);
|
||||
|
||||
JPanel controlsPanel = new JPanel();
|
||||
FlowLayout fl = new FlowLayout(FlowLayout.CENTER, 10, 5);
|
||||
// fl.setAlignment(FlowLayout.RIGHT);
|
||||
controlsPanel.setLayout(fl);
|
||||
controlsPanel.setMaximumSize(new Dimension(10000, 20));
|
||||
controlsPanel.setPreferredSize(new Dimension(600, 20));
|
||||
controlsPanel.setMinimumSize(new Dimension(1, 20));
|
||||
|
||||
contentPane.add(controlsPanel, BorderLayout.SOUTH);
|
||||
|
||||
createToolBar();
|
||||
|
||||
setContentPane(contentPane);
|
||||
setTitle("Serpent Editor");
|
||||
|
||||
pack();
|
||||
this.revalidate();
|
||||
this.repaint();
|
||||
}
|
||||
|
||||
private void changeStyleProgrammatically() {
|
||||
|
||||
// Set the font for all token types.
|
||||
|
||||
// Change a few things here and there.
|
||||
SyntaxScheme scheme = codeArea.getSyntaxScheme();
|
||||
|
||||
scheme.getStyle(Token.RESERVED_WORD).background = Color.white;
|
||||
scheme.getStyle(Token.RESERVED_WORD).foreground = Color.BLUE.darker();
|
||||
|
||||
scheme.getStyle(Token.IDENTIFIER).foreground = Color.black;
|
||||
|
||||
scheme.getStyle(Token.RESERVED_WORD_2).background = Color.WHITE;
|
||||
scheme.getStyle(Token.RESERVED_WORD_2).foreground = Color.MAGENTA.darker();
|
||||
|
||||
scheme.getStyle(Token.ANNOTATION).foreground = Color.ORANGE;
|
||||
scheme.getStyle(Token.ANNOTATION).background = Color.black;
|
||||
scheme.getStyle(Token.ANNOTATION).font = new Font("Consolas", Font.BOLD, 15);
|
||||
|
||||
// scheme.getStyle(Token.LITERAL_STRING_DOUBLE_QUOTE).underline = true;
|
||||
// scheme.getStyle(Token.LITERAL_NUMBER_HEXADECIMAL).underline = true;
|
||||
// scheme.getStyle(Token.LITERAL_NUMBER_HEXADECIMAL).background = Color.pink;
|
||||
|
||||
scheme.getStyle(Token.COMMENT_EOL).foreground = Color.lightGray;
|
||||
|
||||
// scheme.getStyle(Token.COMMENT_EOL).font = new Font("Georgia", Font.ITALIC, 10);
|
||||
|
||||
codeArea.revalidate();
|
||||
}
|
||||
|
||||
protected void compileCode() {
|
||||
|
||||
String code = codeArea.getText();
|
||||
String asmResult = "";
|
||||
|
||||
Pattern pattern = Pattern.compile("(.*?)init:(.*?)code:(.*?)", Pattern.DOTALL);
|
||||
Matcher matcher = pattern.matcher(code);
|
||||
|
||||
try {
|
||||
if (matcher.find()) {
|
||||
asmResult = SerpentCompiler.compileFullNotion(codeArea.getText());
|
||||
asmResult = GUIUtils.getStyledAsmCode(asmResult);
|
||||
} else {
|
||||
asmResult = SerpentCompiler.compile(codeArea.getText());
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
logger.error(th.getMessage(), th);
|
||||
|
||||
splitPanel.setDividerLocation(0.8);
|
||||
result.setVisible(true);
|
||||
result.setText(th.getMessage());
|
||||
result.setForeground(Color.RED);
|
||||
return ;
|
||||
}
|
||||
result.setForeground(Color.BLACK.brighter());
|
||||
result.setVisible(true);
|
||||
result.setText(asmResult);
|
||||
|
||||
splitPanel.setDividerLocation(
|
||||
1 - result.getPreferredSize().getHeight() / codeArea.getPreferredSize().getHeight());
|
||||
|
||||
this.repaint();
|
||||
}
|
||||
|
||||
protected byte[] prepareCodeForSend() {
|
||||
|
||||
String asmResult = "";
|
||||
byte[] machineCode = null;
|
||||
|
||||
try {
|
||||
String code = codeArea.getText();
|
||||
|
||||
Pattern pattern = Pattern.compile("(.*?)init:(.*?)code:(.*?)", Pattern.DOTALL);
|
||||
Matcher matcher = pattern.matcher(code);
|
||||
if (matcher.find()) {
|
||||
asmResult = SerpentCompiler.compileFullNotion(codeArea.getText());
|
||||
machineCode = SerpentCompiler.compileFullNotionAssemblyToMachine(asmResult);
|
||||
} else {
|
||||
asmResult = SerpentCompiler.compile(codeArea.getText());
|
||||
machineCode = SerpentCompiler.compileAssemblyToMachine(asmResult);
|
||||
machineCode = SerpentCompiler.encodeMachineCodeForVMRun(machineCode, null);
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
logger.error(th.getMessage(), th);
|
||||
splitPanel.setDividerLocation(0.7);
|
||||
result.setVisible(true);
|
||||
result.setText(th.getMessage());
|
||||
result.setForeground(Color.RED);
|
||||
return null;
|
||||
}
|
||||
return machineCode;
|
||||
}
|
||||
|
||||
public void createToolBar() {
|
||||
|
||||
JToolBar toolbar = new JToolBar(SwingConstants.VERTICAL);
|
||||
toolbar.putClientProperty("JToolBar.isRollover", Boolean.TRUE);
|
||||
toolbar.setFloatable(false);
|
||||
final JPanel mainContentPane = SerpentEditor.this.contentPane;
|
||||
|
||||
{
|
||||
URL url = ClassLoader.getSystemResource("buttons/open-file.png");
|
||||
Toolkit kit = Toolkit.getDefaultToolkit();
|
||||
Image img = kit.createImage(url);
|
||||
ImageIcon imageIcon = new ImageIcon(img);
|
||||
final JButton button = new JButton(imageIcon);
|
||||
button.setToolTipText("Open File < Ctrl + O >");
|
||||
|
||||
Action openFile = new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
button.doClick();
|
||||
}
|
||||
};
|
||||
|
||||
mainContentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).
|
||||
put(KeyStroke.getKeyStroke(KeyEvent.VK_O, InputEvent.CTRL_DOWN_MASK),
|
||||
"OpenFileButton");
|
||||
|
||||
mainContentPane.getActionMap().put("OpenFileButton",openFile);
|
||||
|
||||
button.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
File file = callFileChooser();
|
||||
try {
|
||||
if (file == null)
|
||||
return;
|
||||
String content = new Scanner(file).useDelimiter("\\Z").next();
|
||||
codeArea.setText(content);
|
||||
} catch (FileNotFoundException e1) {
|
||||
logger.error(e1.getMessage(), e1);
|
||||
} catch (java.util.NoSuchElementException e2) {
|
||||
// don't worry it's just the file is empty
|
||||
codeArea.setText("");
|
||||
}
|
||||
}
|
||||
});
|
||||
toolbar.add(button);
|
||||
}
|
||||
|
||||
{
|
||||
URL url = ClassLoader.getSystemResource("buttons/save-file.png");
|
||||
Toolkit kit = Toolkit.getDefaultToolkit();
|
||||
Image img = kit.createImage(url);
|
||||
ImageIcon imageIcon = new ImageIcon(img);
|
||||
final JButton button = new JButton(imageIcon);
|
||||
button.setToolTipText("Save File < Ctrl + S >");
|
||||
|
||||
Action saveNewFile = new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
button.doClick();
|
||||
}
|
||||
};
|
||||
|
||||
mainContentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).
|
||||
put(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK),
|
||||
"OpenSaveButtonAlways");
|
||||
|
||||
mainContentPane.getActionMap().put("OpenSaveButtonAlways",saveNewFile);
|
||||
|
||||
Action saveFile = new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
button.doClick();
|
||||
}
|
||||
};
|
||||
|
||||
mainContentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).
|
||||
put(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK),
|
||||
"OpenSaveButton");
|
||||
|
||||
mainContentPane.getActionMap().put("OpenSaveButton",saveFile);
|
||||
|
||||
button.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
|
||||
File file = null;
|
||||
|
||||
if (e.getModifiers() == (InputEvent.CTRL_MASK | InputEvent.SHIFT_MASK)) {
|
||||
file = callFileChooser();
|
||||
if (file == null)
|
||||
return;
|
||||
} else if (fileChooser == null
|
||||
|| fileChooser.getSelectedFile() == null) {
|
||||
file = callFileChooser();
|
||||
if (file == null)
|
||||
return;
|
||||
} else {
|
||||
file = fileChooser.getSelectedFile();
|
||||
}
|
||||
|
||||
try {
|
||||
BufferedWriter out = new BufferedWriter(new FileWriter(file), 32768);
|
||||
out.write(codeArea.getText());
|
||||
out.close();
|
||||
} catch (IOException e1) {
|
||||
logger.error(e1.getMessage(), e1);
|
||||
}
|
||||
}
|
||||
});
|
||||
toolbar.add(button);
|
||||
}
|
||||
toolbar.addSeparator();
|
||||
|
||||
{
|
||||
URL url = ClassLoader.getSystemResource("buttons/compile.png");
|
||||
Toolkit kit = Toolkit.getDefaultToolkit();
|
||||
Image img = kit.createImage(url);
|
||||
ImageIcon imageIcon = new ImageIcon(img);
|
||||
final JButton button = new JButton(imageIcon);
|
||||
button.setToolTipText("Compile the contract < Ctrl + F9 >");
|
||||
|
||||
Action compile = new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
button.doClick();
|
||||
}
|
||||
};
|
||||
|
||||
mainContentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).
|
||||
put(KeyStroke.getKeyStroke(KeyEvent.VK_F9, InputEvent.CTRL_DOWN_MASK),
|
||||
"CompileButton");
|
||||
|
||||
mainContentPane.getActionMap().put("CompileButton",compile);
|
||||
|
||||
button.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
compileCode();
|
||||
}
|
||||
});
|
||||
toolbar.add(button);
|
||||
}
|
||||
|
||||
{
|
||||
URL url = ClassLoader.getSystemResource("buttons/deploy.png");
|
||||
Toolkit kit = Toolkit.getDefaultToolkit();
|
||||
Image img = kit.createImage(url);
|
||||
ImageIcon imageIcon = new ImageIcon(img);
|
||||
final JButton button = new JButton(imageIcon);
|
||||
button.setToolTipText("Deploy contract to the chain < Ctrl + Shift + F9 >");
|
||||
|
||||
Action deploy = new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
button.doClick();
|
||||
}
|
||||
};
|
||||
|
||||
mainContentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).
|
||||
put(KeyStroke.getKeyStroke(KeyEvent.VK_F9,
|
||||
InputEvent.SHIFT_DOWN_MASK | InputEvent.CTRL_DOWN_MASK),
|
||||
"DeployButton");
|
||||
|
||||
mainContentPane.getActionMap().put("DeployButton",deploy);
|
||||
|
||||
button.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
byte[] machineCode = prepareCodeForSend();
|
||||
if (machineCode == null) return;
|
||||
ContractSubmitDialog payOutDialog =
|
||||
new ContractSubmitDialog((Frame) SwingUtilities.getAncestorOfClass(JFrame.class,
|
||||
contentPane), machineCode);
|
||||
}
|
||||
});
|
||||
toolbar.add(button);
|
||||
}
|
||||
|
||||
{
|
||||
URL url = ClassLoader.getSystemResource("buttons/call.png");
|
||||
Toolkit kit = Toolkit.getDefaultToolkit();
|
||||
Image img = kit.createImage(url);
|
||||
ImageIcon imageIcon = new ImageIcon(img);
|
||||
final JButton button = new JButton(imageIcon);
|
||||
button.setToolTipText("Call contract <Ctrl + F8>");
|
||||
|
||||
Action call = new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
button.doClick();
|
||||
}
|
||||
};
|
||||
|
||||
mainContentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).
|
||||
put(KeyStroke.getKeyStroke(KeyEvent.VK_F8,
|
||||
InputEvent.CTRL_DOWN_MASK),
|
||||
"CallButton");
|
||||
|
||||
mainContentPane.getActionMap().put("CallButton", call);
|
||||
|
||||
button.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
ContractCallDialog payOutDialog = new ContractCallDialog(
|
||||
(Frame) SwingUtilities.getAncestorOfClass(
|
||||
JFrame.class, contentPane));
|
||||
}
|
||||
});
|
||||
toolbar.add(button);
|
||||
}
|
||||
this.contentPane.add(toolbar, BorderLayout.EAST);
|
||||
}
|
||||
|
||||
|
||||
protected File callFileChooser() {
|
||||
|
||||
File file = null;
|
||||
|
||||
if (fileChooser == null) {
|
||||
fileChooser = new JFileChooser(CONFIG.samplesDir());
|
||||
fileChooser.setMultiSelectionEnabled(false);
|
||||
}
|
||||
|
||||
switch (fileChooser.showOpenDialog(SerpentEditor.this)) {
|
||||
case JFileChooser.APPROVE_OPTION:
|
||||
file = fileChooser.getSelectedFile();
|
||||
break;
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
public void addCloseAction() {
|
||||
this.addWindowListener(new WindowAdapter() {
|
||||
public void windowClosing(WindowEvent e) {
|
||||
toolBar.editorToggle.setSelected(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// Start all Swing applications on the EDT.
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
new SerpentEditor(null).setVisible(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,498 @@
|
||||
package org.ethereum.gui;
|
||||
|
||||
import javax.swing.text.Segment;
|
||||
|
||||
import org.ethereum.vm.OpCode;
|
||||
import org.fife.ui.rsyntaxtextarea.*;
|
||||
|
||||
/**
|
||||
* www.ethereumJ.com
|
||||
* @author: Roman Mandeleil
|
||||
* Created on: 24/04/14 11:52
|
||||
*/
|
||||
public class SerpentTokenMaker extends AbstractTokenMaker {
|
||||
|
||||
// http://fifesoft.com/rsyntaxtextarea/doc/CustomSyntaxHighlighting.html
|
||||
|
||||
protected final String operators = ".@:*<>=?|!";
|
||||
|
||||
private int currentTokenStart;
|
||||
private int currentTokenType;
|
||||
|
||||
private boolean bracketVariable; // Whether a variable is of the format %{...}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public SerpentTokenMaker() {
|
||||
super(); // Initializes tokensToHighlight.
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the token to give it the exact ID it deserves before
|
||||
* being passed up to the super method.
|
||||
*
|
||||
* @param segment <code>Segment</code> to get text from.
|
||||
* @param start Start offset in <code>segment</code> of token.
|
||||
* @param end End offset in <code>segment</code> of token.
|
||||
* @param tokenType The token's type.
|
||||
* @param startOffset The offset in the document at which the token occurs.
|
||||
*/
|
||||
@Override
|
||||
public void addToken(Segment segment, int start, int end, int tokenType, int startOffset) {
|
||||
|
||||
switch (tokenType) {
|
||||
// Since reserved words, functions, and data types are all passed
|
||||
// into here as "identifiers," we have to see what the token
|
||||
// really is...
|
||||
case Token.IDENTIFIER:
|
||||
int value = wordsToHighlight.get(segment, start,end);
|
||||
if (value!=-1)
|
||||
tokenType = value;
|
||||
break;
|
||||
|
||||
case Token.ANNOTATION:
|
||||
value = wordsToHighlight.get(segment, start,end);
|
||||
if (value!=-1)
|
||||
tokenType = value;
|
||||
break;
|
||||
}
|
||||
super.addToken(segment, start, end, tokenType, startOffset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the text to place at the beginning and end of a
|
||||
* line to "comment" it in a this programming language.
|
||||
*
|
||||
* @return The start and end strings to add to a line to "comment"
|
||||
* it out.
|
||||
*/
|
||||
@Override
|
||||
public String[] getLineCommentStartAndEnd() {
|
||||
return new String[] { "#", null };
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether tokens of the specified type should have "mark
|
||||
* occurrences" enabled for the current programming language.
|
||||
*
|
||||
* @param type The token type.
|
||||
* @return Whether tokens of this type should have "mark occurrences"
|
||||
* enabled.
|
||||
*/
|
||||
@Override
|
||||
public boolean getMarkOccurrencesOfTokenType(int type) {
|
||||
return type==Token.IDENTIFIER || type==Token.VARIABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the words to highlight for Windows batch files.
|
||||
*
|
||||
* @return A <code>TokenMap</code> containing the words to highlight for
|
||||
* Windows batch files.
|
||||
* @see org.fife.ui.rsyntaxtextarea.AbstractTokenMaker#getWordsToHighlight
|
||||
*/
|
||||
@Override
|
||||
public TokenMap getWordsToHighlight() {
|
||||
|
||||
TokenMap tokenMap = new TokenMap(false); // Ignore case.
|
||||
|
||||
int reservedWord = Token.RESERVED_WORD;
|
||||
tokenMap.put("set", reservedWord);
|
||||
tokenMap.put("if", reservedWord);
|
||||
tokenMap.put("else", reservedWord);
|
||||
tokenMap.put("elif", reservedWord);
|
||||
tokenMap.put("seq", reservedWord);
|
||||
tokenMap.put("while", reservedWord);
|
||||
tokenMap.put("byte", reservedWord);
|
||||
tokenMap.put("access", reservedWord);
|
||||
tokenMap.put("arrset", reservedWord);
|
||||
tokenMap.put("set_and_inc", reservedWord);
|
||||
tokenMap.put("array", reservedWord);
|
||||
tokenMap.put("getch", reservedWord);
|
||||
tokenMap.put("setch", reservedWord);
|
||||
tokenMap.put("string", reservedWord);
|
||||
tokenMap.put("send", reservedWord);
|
||||
tokenMap.put("create", reservedWord);
|
||||
tokenMap.put("sha3", reservedWord);
|
||||
tokenMap.put("sha3bytes", reservedWord);
|
||||
tokenMap.put("sload", reservedWord);
|
||||
tokenMap.put("sstore", reservedWord);
|
||||
tokenMap.put("calldataload", reservedWord);
|
||||
tokenMap.put("id", reservedWord);
|
||||
tokenMap.put("return", reservedWord);
|
||||
tokenMap.put("suicide", reservedWord);
|
||||
|
||||
tokenMap.put("stop", reservedWord);
|
||||
|
||||
int function = Token.FUNCTION;
|
||||
tokenMap.put("msg", function);
|
||||
tokenMap.put("data", function);
|
||||
tokenMap.put("contract", function);
|
||||
tokenMap.put("storage", function);
|
||||
tokenMap.put("block", function);
|
||||
tokenMap.put("tx", function);
|
||||
|
||||
// ALL the assembly tokens
|
||||
int reservedWord2 = Token.RESERVED_WORD_2;
|
||||
for (OpCode value : OpCode.values()) {
|
||||
tokenMap.put(value.name(), reservedWord2);
|
||||
tokenMap.put("[asm", reservedWord2);
|
||||
tokenMap.put("asm]", reservedWord2);
|
||||
}
|
||||
|
||||
int dataType = Token.ANNOTATION;
|
||||
tokenMap.put("init", dataType);
|
||||
tokenMap.put("code", dataType);
|
||||
|
||||
return tokenMap;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a peerInfoList of tokens representing the given text.
|
||||
*
|
||||
* @param text The text to break into tokens.
|
||||
* @param startTokenType The token with which to start tokenizing.
|
||||
* @param startOffset The offset at which the line of tokens begins.
|
||||
* @return A linked peerInfoList of tokens representing <code>text</code>.
|
||||
*/
|
||||
public Token getTokenList(Segment text, int startTokenType, final int startOffset) {
|
||||
|
||||
resetTokenList();
|
||||
|
||||
char[] array = text.array;
|
||||
int offset = text.offset;
|
||||
int count = text.count;
|
||||
int end = offset + count;
|
||||
|
||||
// See, when we find a token, its starting position is always of the form:
|
||||
// 'startOffset + (currentTokenStart-offset)'; but since startOffset and
|
||||
// offset are constant, tokens' starting positions become:
|
||||
// 'newStartOffset+currentTokenStart' for one less subtraction operation.
|
||||
int newStartOffset = startOffset - offset;
|
||||
|
||||
currentTokenStart = offset;
|
||||
currentTokenType = startTokenType;
|
||||
|
||||
//beginning:
|
||||
for (int i=offset; i<end; i++) {
|
||||
|
||||
char c = array[i];
|
||||
|
||||
switch (currentTokenType) {
|
||||
|
||||
case Token.NULL:
|
||||
|
||||
currentTokenStart = i; // Starting a new token here.
|
||||
|
||||
switch (c) {
|
||||
|
||||
case '#':
|
||||
currentTokenType = Token.COMMENT_EOL;
|
||||
break;
|
||||
|
||||
case ' ':
|
||||
case '\t':
|
||||
currentTokenType = Token.WHITESPACE;
|
||||
break;
|
||||
|
||||
case '"':
|
||||
currentTokenType = Token.ERROR_STRING_DOUBLE;
|
||||
break;
|
||||
|
||||
|
||||
// The "separators".
|
||||
case '(':
|
||||
case ')':
|
||||
addToken(text, currentTokenStart,i, Token.SEPARATOR, newStartOffset+currentTokenStart);
|
||||
currentTokenType = Token.NULL;
|
||||
break;
|
||||
|
||||
// The "separators2".
|
||||
case ',':
|
||||
case ';':
|
||||
addToken(text, currentTokenStart,i, Token.IDENTIFIER, newStartOffset+currentTokenStart);
|
||||
currentTokenType = Token.NULL;
|
||||
break;
|
||||
|
||||
// Newer version of EOL comments, or a label
|
||||
case ':':
|
||||
// If this will be the first token added, it is
|
||||
// a new-style comment or a label
|
||||
if (firstToken == null) {
|
||||
if (i < end - 1 && array[i + 1] == ':') { // new-style comment
|
||||
currentTokenType = Token.COMMENT_EOL;
|
||||
}
|
||||
else { // Label
|
||||
currentTokenType = Token.PREPROCESSOR;
|
||||
}
|
||||
}
|
||||
else { // Just a colon
|
||||
currentTokenType = Token.IDENTIFIER;
|
||||
}
|
||||
break;
|
||||
// Newer version of EOL comments, or a label
|
||||
|
||||
default:
|
||||
// Just to speed things up a tad, as this will usually be the case (if spaces above failed).
|
||||
if (RSyntaxUtilities.isLetterOrDigit(c) || c=='\\') {
|
||||
currentTokenType = Token.IDENTIFIER;
|
||||
break;
|
||||
}
|
||||
|
||||
int indexOf = operators.indexOf(c,0);
|
||||
if (indexOf > -1) {
|
||||
addToken(text, currentTokenStart,i, Token.OPERATOR, newStartOffset+currentTokenStart);
|
||||
currentTokenType = Token.NULL;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
currentTokenType = Token.IDENTIFIER;
|
||||
break;
|
||||
}
|
||||
} // End of switch (c).
|
||||
break;
|
||||
|
||||
case Token.WHITESPACE:
|
||||
|
||||
switch (c) {
|
||||
|
||||
case '/':
|
||||
addToken(text, currentTokenStart,i-1,
|
||||
Token.COMMENT_EOL, newStartOffset+currentTokenStart);
|
||||
currentTokenStart = i;
|
||||
currentTokenType = Token.COMMENT_EOL;
|
||||
break;
|
||||
case ' ':
|
||||
case '\t':
|
||||
break; // Still whitespace.
|
||||
|
||||
case '"':
|
||||
addToken(text, currentTokenStart,i-1, Token.WHITESPACE, newStartOffset+currentTokenStart);
|
||||
currentTokenStart = i;
|
||||
currentTokenType = Token.ERROR_STRING_DOUBLE;
|
||||
break;
|
||||
|
||||
|
||||
// The "separators".
|
||||
case '(':
|
||||
case ')':
|
||||
addToken(text, currentTokenStart,i-1, Token.WHITESPACE, newStartOffset+currentTokenStart);
|
||||
addToken(text, i,i, Token.SEPARATOR, newStartOffset+i);
|
||||
currentTokenType = Token.NULL;
|
||||
break;
|
||||
|
||||
// The "separators2".
|
||||
case ',':
|
||||
case ';':
|
||||
addToken(text, currentTokenStart,i-1, Token.WHITESPACE, newStartOffset+currentTokenStart);
|
||||
addToken(text, i,i, Token.IDENTIFIER, newStartOffset+i);
|
||||
currentTokenType = Token.NULL;
|
||||
break;
|
||||
|
||||
// Newer version of EOL comments, or a label
|
||||
case ':':
|
||||
addToken(text, currentTokenStart,i-1, Token.WHITESPACE, newStartOffset+currentTokenStart);
|
||||
currentTokenStart = i;
|
||||
// If the previous (whitespace) token was the first token
|
||||
// added, this is a new-style comment or a label
|
||||
if (firstToken.getNextToken() == null) {
|
||||
if (i < end - 1 && array[i + 1] == ':') { // new-style comment
|
||||
currentTokenType = Token.COMMENT_EOL;
|
||||
}
|
||||
else { // Label
|
||||
currentTokenType = Token.PREPROCESSOR;
|
||||
}
|
||||
}
|
||||
else { // Just a colon
|
||||
currentTokenType = Token.IDENTIFIER;
|
||||
}
|
||||
break;
|
||||
|
||||
default: // Add the whitespace token and start anew.
|
||||
addToken(text, currentTokenStart,i-1, Token.WHITESPACE, newStartOffset+currentTokenStart);
|
||||
currentTokenStart = i;
|
||||
|
||||
// Just to speed things up a tad, as this will usually be the case (if spaces above failed).
|
||||
if (RSyntaxUtilities.isLetterOrDigit(c) || c=='\\') {
|
||||
currentTokenType = Token.IDENTIFIER;
|
||||
break;
|
||||
}
|
||||
|
||||
int indexOf = operators.indexOf(c,0);
|
||||
if (indexOf > -1) {
|
||||
addToken(text, currentTokenStart,i, Token.OPERATOR, newStartOffset+currentTokenStart);
|
||||
currentTokenType = Token.NULL;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
currentTokenType = Token.IDENTIFIER;
|
||||
}
|
||||
} // End of switch (c).
|
||||
break;
|
||||
|
||||
default: // Should never happen
|
||||
case Token.IDENTIFIER:
|
||||
|
||||
switch (c) {
|
||||
|
||||
case ' ':
|
||||
case '\t':
|
||||
// Check for REM comments.
|
||||
if (i-currentTokenStart==3 &&
|
||||
(array[i-3]=='r' || array[i-3]=='R') &&
|
||||
(array[i-2]=='e' || array[i-2]=='E') &&
|
||||
(array[i-1]=='m' || array[i-1]=='M')) {
|
||||
currentTokenType = Token.COMMENT_EOL;
|
||||
break;
|
||||
}
|
||||
addToken(text, currentTokenStart,i-1, Token.IDENTIFIER, newStartOffset+currentTokenStart);
|
||||
currentTokenStart = i;
|
||||
currentTokenType = Token.WHITESPACE;
|
||||
break;
|
||||
|
||||
case '"':
|
||||
addToken(text, currentTokenStart,i-1, Token.IDENTIFIER, newStartOffset+currentTokenStart);
|
||||
currentTokenStart = i;
|
||||
currentTokenType = Token.ERROR_STRING_DOUBLE;
|
||||
break;
|
||||
|
||||
|
||||
// Should be part of identifiers, but not at end of "REM".
|
||||
case '\\':
|
||||
// Check for REM comments.
|
||||
if (i-currentTokenStart==3 &&
|
||||
(array[i-3]=='r' || array[i-3]=='R') &&
|
||||
(array[i-2]=='e' || array[i-2]=='E') &&
|
||||
(array[i-1]=='m' || array[i-1]=='M')) {
|
||||
currentTokenType = Token.COMMENT_EOL;
|
||||
}
|
||||
break;
|
||||
|
||||
// case '.':
|
||||
case '_':
|
||||
break; // Characters good for identifiers.
|
||||
|
||||
// The "separators".
|
||||
case '(':
|
||||
case ')':
|
||||
addToken(text, currentTokenStart,i-1, Token.IDENTIFIER, newStartOffset+currentTokenStart);
|
||||
addToken(text, i,i, Token.SEPARATOR, newStartOffset+i);
|
||||
currentTokenType = Token.NULL;
|
||||
break;
|
||||
|
||||
// The "separators2".
|
||||
case ',':
|
||||
case ';':
|
||||
addToken(text, currentTokenStart,i-1, Token.IDENTIFIER, newStartOffset+currentTokenStart);
|
||||
addToken(text, i,i, Token.IDENTIFIER, newStartOffset+i);
|
||||
currentTokenType = Token.NULL;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
// Just to speed things up a tad, as this will usually be the case.
|
||||
if (RSyntaxUtilities.isLetterOrDigit(c) || c=='\\') {
|
||||
break;
|
||||
}
|
||||
|
||||
int indexOf = operators.indexOf(c);
|
||||
if (indexOf>-1) {
|
||||
addToken(text, currentTokenStart,i-1, Token.IDENTIFIER, newStartOffset+currentTokenStart);
|
||||
addToken(text, i,i, Token.OPERATOR, newStartOffset+i);
|
||||
currentTokenType = Token.NULL;
|
||||
break;
|
||||
}
|
||||
// Otherwise, fall through and assume we're still okay as an IDENTIFIER...
|
||||
} // End of switch (c).
|
||||
break;
|
||||
|
||||
case Token.COMMENT_EOL:
|
||||
|
||||
if (i + 1 >= array.length)
|
||||
break;
|
||||
|
||||
char nextC = array[i+1];
|
||||
if (nextC == '/') {
|
||||
|
||||
i = end - 1;
|
||||
addToken(text, currentTokenStart,i, Token.COMMENT_EOL, newStartOffset+currentTokenStart);
|
||||
// We need to set token type to null so at the bottom we don't add one more token.
|
||||
currentTokenType = Token.NULL;
|
||||
}
|
||||
break;
|
||||
|
||||
case Token.PREPROCESSOR: // Used for labels
|
||||
i = end - 1;
|
||||
addToken(text, currentTokenStart,i, Token.PREPROCESSOR, newStartOffset+currentTokenStart);
|
||||
// We need to set token type to null so at the bottom we don't add one more token.
|
||||
currentTokenType = Token.NULL;
|
||||
break;
|
||||
|
||||
case Token.ERROR_STRING_DOUBLE:
|
||||
|
||||
if (c == '"') {
|
||||
addToken(text, currentTokenStart,i, Token.LITERAL_STRING_DOUBLE_QUOTE, newStartOffset+currentTokenStart);
|
||||
currentTokenStart = i + 1;
|
||||
currentTokenType = Token.NULL;
|
||||
}
|
||||
// Otherwise, we're still an unclosed string...
|
||||
break;
|
||||
|
||||
case Token.VARIABLE:
|
||||
|
||||
if (i == currentTokenStart + 1) { // first character after '%'.
|
||||
bracketVariable = false;
|
||||
switch (c) {
|
||||
case '{':
|
||||
bracketVariable = true;
|
||||
break;
|
||||
default:
|
||||
if (RSyntaxUtilities.isLetter(c) || c==' ') { // No tab, just space; spaces are okay in variable names.
|
||||
break;
|
||||
}
|
||||
else if (RSyntaxUtilities.isDigit(c)) { // Single-digit command-line argument ("%1").
|
||||
addToken(text, currentTokenStart,i, Token.VARIABLE, newStartOffset+currentTokenStart);
|
||||
currentTokenType = Token.NULL;
|
||||
break;
|
||||
}
|
||||
else { // Anything else, ???.
|
||||
addToken(text, currentTokenStart,i-1, Token.VARIABLE, newStartOffset+currentTokenStart); // ???
|
||||
i--;
|
||||
currentTokenType = Token.NULL;
|
||||
break;
|
||||
}
|
||||
} // End of switch (c).
|
||||
}
|
||||
else { // Character other than first after the '%'.
|
||||
if (bracketVariable == true) {
|
||||
if (c == '}') {
|
||||
addToken(text, currentTokenStart, i, Token.VARIABLE, newStartOffset + currentTokenStart);
|
||||
currentTokenType = Token.NULL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
} // End of switch (currentTokenType).
|
||||
|
||||
} // End of for (int i=offset; i<end; i++).
|
||||
|
||||
// Deal with the (possibly there) last token.
|
||||
if (currentTokenType != Token.NULL) {
|
||||
// Check for REM comments.
|
||||
if (end-currentTokenStart==3 &&
|
||||
(array[end-3]=='r' || array[end-3]=='R') &&
|
||||
(array[end-2]=='e' || array[end-2]=='E') &&
|
||||
(array[end-1]=='m' || array[end-1]=='M')) {
|
||||
currentTokenType = Token.COMMENT_EOL;
|
||||
}
|
||||
addToken(text, currentTokenStart,end-1, currentTokenType, newStartOffset+currentTokenStart);
|
||||
}
|
||||
addNullToken();
|
||||
// Return the first token in our linked peerInfoList.
|
||||
return firstToken;
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package org.ethereum.gui;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.table.TableCellRenderer;
|
||||
|
||||
/**
|
||||
* www.ethereumJ.com
|
||||
* @author: Roman Mandeleil
|
||||
* Created on: 15/05/14 13:08
|
||||
*/
|
||||
public class TableCellLongTextRenderer extends JEditorPane implements TableCellRenderer{
|
||||
|
||||
protected static Border m_noFocusBorder;
|
||||
|
||||
public TableCellLongTextRenderer() {
|
||||
m_noFocusBorder = new EmptyBorder(1, 2, 1, 2);
|
||||
setOpaque(true);
|
||||
setBorder(m_noFocusBorder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
|
||||
this.setText((String) value);
|
||||
this.setSelectedTextColor(Color.BLUE);
|
||||
// this.setWrapStyleWord(true);
|
||||
// this.setLineWrap(true);
|
||||
|
||||
setBackground(isSelected && !hasFocus ? table.getSelectionBackground() : table.getBackground());
|
||||
setForeground(isSelected && !hasFocus ? table.getSelectionForeground() : table.getForeground());
|
||||
setBorder(hasFocus? UIManager.getBorder("Table.focusCellHighlightBorder") : m_noFocusBorder);
|
||||
|
||||
//set the JTextArea to the width of the table column
|
||||
setSize(table.getColumnModel().getColumn(column).getWidth(), getPreferredSize().height);
|
||||
if (table.getRowHeight(row) != getPreferredSize().height) {
|
||||
//set the height of the table row to the calculated height of the JTextArea
|
||||
table.setRowHeight(row, getPreferredSize().height);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
242
ethereumj-studio/src/main/java/org/ethereum/gui/ToolBar.java
Normal file
242
ethereumj-studio/src/main/java/org/ethereum/gui/ToolBar.java
Normal file
@ -0,0 +1,242 @@
|
||||
package org.ethereum.gui;
|
||||
|
||||
import org.ethereum.config.SystemProperties;
|
||||
import org.ethereum.util.Utils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.ItemEvent;
|
||||
import java.awt.event.ItemListener;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
|
||||
/**
|
||||
* www.ethereumJ.com
|
||||
* @author: Roman Mandeleil
|
||||
* Created on: 30/04/14 06:29
|
||||
*/
|
||||
public class ToolBar extends JFrame {
|
||||
|
||||
private Logger introLogger = LoggerFactory.getLogger("Intro");
|
||||
|
||||
private ConnectionConsoleWindow connectionConsoleWindow = null;
|
||||
private PeersTableWindow mainFrame = null;
|
||||
private BlockChainTable blockchainWindow = null;
|
||||
private WalletWindow walletWindow = null;
|
||||
private SerpentEditor serpentEditor = null;
|
||||
|
||||
JToggleButton editorToggle;
|
||||
JToggleButton logToggle;
|
||||
JToggleButton peersToggle;
|
||||
JToggleButton chainToggle;
|
||||
JToggleButton walletToggle;
|
||||
|
||||
public ToolBar() throws HeadlessException {
|
||||
|
||||
String version = SystemProperties.CONFIG.projectVersion();
|
||||
|
||||
introLogger.info("");
|
||||
introLogger.info("|Ξ| EthereumJ [v" + version + "] by RomanJ");
|
||||
introLogger.info("|Ξ| Code by Roman Mandeleil, (c) 2014.");
|
||||
introLogger.info("|Ξ| Contribution: Nick Savers ");
|
||||
introLogger.info("|Ξ| Based on a design by Vitalik Buterin.");
|
||||
introLogger.info("");
|
||||
introLogger.info("java.version: " + System.getProperty("java.version"));
|
||||
introLogger.info("java.home: " + System.getProperty("java.home"));
|
||||
introLogger.info("java.vendor: " + System.getProperty("java.vendor"));
|
||||
introLogger.info("");
|
||||
|
||||
if (Utils.JAVA_VERSION < 1.7 && Utils.JAVA_VERSION != 0) {
|
||||
introLogger.info("EthereumJ support version 1.7 and higher of Java Runtime please update");
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
final JPanel cp = new JPanel(new FlowLayout());
|
||||
cp.setBackground(Color.WHITE);
|
||||
|
||||
java.net.URL url = ClassLoader.getSystemResource("ethereum-icon.png");
|
||||
Toolkit kit = Toolkit.getDefaultToolkit();
|
||||
Image img = kit.createImage(url);
|
||||
this.setIconImage(img);
|
||||
this.setSize(350, 200);
|
||||
this.setLocation(460, 25);
|
||||
this.setAlwaysOnTop(true);
|
||||
this.setResizable(false);
|
||||
this.setBackground(Color.WHITE);
|
||||
|
||||
setTitle("EthereumJ Studio");
|
||||
setDefaultCloseOperation(EXIT_ON_CLOSE);
|
||||
|
||||
addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowClosing(WindowEvent e) {
|
||||
UIEthereumManager.ethereum.close();
|
||||
}
|
||||
});
|
||||
|
||||
this.setContentPane(cp);
|
||||
|
||||
java.net.URL imageURL_1 = ClassLoader.getSystemResource("buttons/feedly.png");
|
||||
ImageIcon image_1 = new ImageIcon(imageURL_1);
|
||||
|
||||
java.net.URL imageURL_2 = ClassLoader.getSystemResource("buttons/winamp.png");
|
||||
ImageIcon image_2 = new ImageIcon(imageURL_2);
|
||||
|
||||
java.net.URL imageURL_3 = ClassLoader.getSystemResource("buttons/browser.png");
|
||||
ImageIcon image_3 = new ImageIcon(imageURL_3);
|
||||
|
||||
java.net.URL imageURL_4 = ClassLoader.getSystemResource("buttons/shazam.png");
|
||||
ImageIcon image_4 = new ImageIcon(imageURL_4);
|
||||
|
||||
java.net.URL imageURL_5 = ClassLoader.getSystemResource("buttons/wallet.png");
|
||||
ImageIcon image_5 = new ImageIcon(imageURL_5);
|
||||
|
||||
editorToggle = new JToggleButton("");
|
||||
editorToggle.setIcon(image_1);
|
||||
editorToggle.setContentAreaFilled(true);
|
||||
editorToggle.setToolTipText("Serpent Editor");
|
||||
editorToggle.setBackground(Color.WHITE);
|
||||
editorToggle.setBorderPainted(false);
|
||||
editorToggle.setFocusPainted(false);
|
||||
editorToggle.setCursor(new Cursor(Cursor.HAND_CURSOR));
|
||||
editorToggle.addItemListener(new ItemListener() {
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
if (e.getStateChange() == ItemEvent.SELECTED) {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
if (serpentEditor == null)
|
||||
serpentEditor = new SerpentEditor(ToolBar.this);
|
||||
serpentEditor.setVisible(true);
|
||||
}
|
||||
});
|
||||
} else if (e.getStateChange() == ItemEvent.DESELECTED) {
|
||||
serpentEditor.setVisible(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
logToggle = new JToggleButton();
|
||||
logToggle.setIcon(image_2);
|
||||
logToggle.setToolTipText("Connect");
|
||||
logToggle.setContentAreaFilled(true);
|
||||
logToggle.setBackground(Color.WHITE);
|
||||
logToggle.setBorderPainted(false);
|
||||
logToggle.setFocusPainted(false);
|
||||
logToggle.setCursor(new Cursor(Cursor.HAND_CURSOR));
|
||||
logToggle.addItemListener(new ItemListener() {
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
if (e.getStateChange() == ItemEvent.SELECTED) {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
if (connectionConsoleWindow == null)
|
||||
connectionConsoleWindow = new ConnectionConsoleWindow(ToolBar.this);
|
||||
connectionConsoleWindow.setVisible(true);
|
||||
}
|
||||
});
|
||||
} else if (e.getStateChange() == ItemEvent.DESELECTED) {
|
||||
connectionConsoleWindow.setVisible(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
peersToggle = new JToggleButton();
|
||||
peersToggle.setIcon(image_3);
|
||||
peersToggle.setToolTipText("Peers");
|
||||
peersToggle.setContentAreaFilled(true);
|
||||
peersToggle.setBackground(Color.WHITE);
|
||||
peersToggle.setBorderPainted(false);
|
||||
peersToggle.setFocusPainted(false);
|
||||
peersToggle.setCursor(new Cursor(Cursor.HAND_CURSOR));
|
||||
peersToggle.addItemListener(new ItemListener() {
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
if (e.getStateChange() == ItemEvent.SELECTED) {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
if (mainFrame == null)
|
||||
mainFrame = new PeersTableWindow(ToolBar.this);
|
||||
mainFrame.setVisible( true );
|
||||
}
|
||||
});
|
||||
} else if (e.getStateChange() == ItemEvent.DESELECTED) {
|
||||
mainFrame.setVisible( false );
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
chainToggle = new JToggleButton();
|
||||
chainToggle.setIcon(image_4);
|
||||
chainToggle.setToolTipText("Block Chain");
|
||||
chainToggle.setContentAreaFilled(true);
|
||||
chainToggle.setBackground(Color.WHITE);
|
||||
chainToggle.setBorderPainted(false);
|
||||
chainToggle.setFocusPainted(false);
|
||||
chainToggle.setCursor(new Cursor(Cursor.HAND_CURSOR));
|
||||
chainToggle.addItemListener(new ItemListener() {
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
if (e.getStateChange() == ItemEvent.SELECTED) {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
|
||||
if (blockchainWindow == null)
|
||||
blockchainWindow = new BlockChainTable(ToolBar.this);
|
||||
blockchainWindow.setVisible(true);
|
||||
}
|
||||
});
|
||||
} else if (e.getStateChange() == ItemEvent.DESELECTED) {
|
||||
blockchainWindow.setVisible(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
walletToggle = new JToggleButton();
|
||||
walletToggle.setIcon(image_5);
|
||||
walletToggle.setToolTipText("Wallet");
|
||||
walletToggle.setContentAreaFilled(true);
|
||||
walletToggle.setBackground(Color.WHITE);
|
||||
walletToggle.setBorderPainted(false);
|
||||
walletToggle.setFocusPainted(false);
|
||||
walletToggle.setCursor(new Cursor(Cursor.HAND_CURSOR));
|
||||
walletToggle.addItemListener(
|
||||
new ItemListener() {
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
if (e.getStateChange() == ItemEvent.SELECTED) {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
if (walletWindow == null)
|
||||
walletWindow = new WalletWindow(ToolBar.this);
|
||||
walletWindow.setVisible(true);
|
||||
}
|
||||
});
|
||||
} else if (e.getStateChange() == ItemEvent.DESELECTED) {
|
||||
walletWindow.setVisible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
cp.add(editorToggle);
|
||||
cp.add(logToggle);
|
||||
cp.add(peersToggle);
|
||||
cp.add(chainToggle);
|
||||
cp.add(walletToggle);
|
||||
|
||||
UIEthereumManager.ethereum.loadBlockChain();
|
||||
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
new ToolBar().setVisible(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
package org.ethereum.gui;
|
||||
|
||||
import org.ethereum.core.Account;
|
||||
import org.ethereum.util.Utils;
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.border.CompoundBorder;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* www.ethereumJ.com
|
||||
* @author: Roman Mandeleil
|
||||
* Created on: 17/05/14 12:32
|
||||
*/
|
||||
public class WalletAddressPanel extends JPanel{
|
||||
|
||||
public WalletAddressPanel(final Account account) {
|
||||
|
||||
final WalletAddressPanel walletAddressPanel = this;
|
||||
|
||||
this.setBackground(Color.WHITE);
|
||||
this.setPreferredSize(new Dimension(500, 45));
|
||||
|
||||
JTextField addressField = new JTextField();
|
||||
Border line = BorderFactory.createLineBorder(Color.LIGHT_GRAY);
|
||||
Border empty = new EmptyBorder(5, 8, 5, 8);
|
||||
CompoundBorder border = new CompoundBorder(line, empty);
|
||||
addressField.setBorder(border);
|
||||
addressField.setEnabled(true);
|
||||
addressField.setEditable(false);
|
||||
addressField.setText(Hex.toHexString(account.getEcKey().getAddress()).toUpperCase());
|
||||
addressField.setForeground(new Color(143, 170, 220));
|
||||
addressField.setFont(new Font("Monospaced", 0, 12));
|
||||
addressField.setPreferredSize(new Dimension(300, 35));
|
||||
addressField.setBackground(Color.WHITE);
|
||||
this.add(addressField);
|
||||
|
||||
JTextField amount = new JTextField();
|
||||
amount.setBorder(border);
|
||||
amount.setEnabled(true);
|
||||
amount.setEditable(false);
|
||||
amount.setText(Utils.getValueShortString(account.getBalance()));
|
||||
amount.setForeground(new Color(143, 170, 220));
|
||||
amount.setBackground(Color.WHITE);
|
||||
amount.setPreferredSize(new Dimension(100, 35));
|
||||
amount.setFont(new Font("Monospaced", 0, 13));
|
||||
amount.setHorizontalAlignment(SwingConstants.RIGHT);
|
||||
this.add(amount);
|
||||
|
||||
URL payoutIconURL = ClassLoader.getSystemResource("buttons/wallet-pay.png");
|
||||
ImageIcon payOutIcon = new ImageIcon(payoutIconURL);
|
||||
JLabel payOutLabel = new JLabel(payOutIcon);
|
||||
payOutLabel.setToolTipText("Payout for address");
|
||||
payOutLabel.setCursor(new Cursor(Cursor.HAND_CURSOR));
|
||||
payOutLabel.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
|
||||
PayOutDialog payOutDialog =
|
||||
new PayOutDialog((Frame)SwingUtilities.getAncestorOfClass(JFrame.class,
|
||||
walletAddressPanel), account);
|
||||
}
|
||||
});
|
||||
|
||||
this.add(payOutLabel);
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package org.ethereum.gui;
|
||||
|
||||
import org.ethereum.util.Utils;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.border.CompoundBorder;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.math.BigInteger;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* www.ethereumJ.com
|
||||
* @author: Roman Mandeleil
|
||||
* Created on: 17/05/14 12:32
|
||||
*/
|
||||
public class WalletSumPanel extends JPanel{
|
||||
|
||||
public WalletSumPanel(BigInteger balance) {
|
||||
|
||||
this.setBackground(Color.WHITE);
|
||||
double width = this.getSize().getWidth();
|
||||
this.setPreferredSize(new Dimension(500, 50));
|
||||
Border line = BorderFactory.createLineBorder(Color.LIGHT_GRAY);
|
||||
Border empty = new EmptyBorder(5, 8, 5, 8);
|
||||
CompoundBorder border = new CompoundBorder(line, empty);
|
||||
|
||||
JLabel addressField = new JLabel();
|
||||
addressField.setPreferredSize(new Dimension(300, 35));
|
||||
this.add(addressField);
|
||||
|
||||
JTextField amount = new JTextField();
|
||||
amount.setBorder(border);
|
||||
amount.setEnabled(true);
|
||||
amount.setEditable(false);
|
||||
amount.setText(Utils.getValueShortString(balance));
|
||||
|
||||
amount.setPreferredSize(new Dimension(100, 35));
|
||||
amount.setForeground(new Color(143, 170, 220));
|
||||
amount.setHorizontalAlignment(SwingConstants.RIGHT);
|
||||
amount.setFont(new Font("Monospaced", 0, 13));
|
||||
amount.setBackground(Color.WHITE);
|
||||
this.add(amount);
|
||||
|
||||
URL payoutIconURL = ClassLoader.getSystemResource("buttons/wallet-pay.png");
|
||||
ImageIcon payOutIcon = new ImageIcon(payoutIconURL);
|
||||
JLabel payOutLabel = new JLabel(payOutIcon);
|
||||
payOutLabel.setToolTipText("Payout for all address list");
|
||||
payOutLabel.setCursor(new Cursor(Cursor.HAND_CURSOR));
|
||||
payOutLabel.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
JOptionPane.showMessageDialog(null, "Under construction");
|
||||
}
|
||||
});
|
||||
|
||||
this.add(payOutLabel);
|
||||
}
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
package org.ethereum.gui;
|
||||
|
||||
import org.ethereum.core.Account;
|
||||
import org.ethereum.core.Wallet;
|
||||
import org.ethereum.manager.WorldManager;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* www.ethereumJ.com
|
||||
* @author: Roman Mandeleil
|
||||
* Created on: 17/05/14 12:00
|
||||
*/
|
||||
public class WalletWindow extends JFrame implements Wallet.WalletListener{
|
||||
|
||||
WalletWindow walletWindow;
|
||||
ToolBar toolBar;
|
||||
|
||||
public WalletWindow(ToolBar toolBar) {
|
||||
|
||||
addCloseAction();
|
||||
this.toolBar = toolBar;
|
||||
|
||||
walletWindow = this;
|
||||
URL url = ClassLoader.getSystemResource("ethereum-icon.png");
|
||||
Toolkit kit = Toolkit.getDefaultToolkit();
|
||||
Image img = kit.createImage(url);
|
||||
this.setIconImage(img);
|
||||
setTitle("Ethereum Wallet");
|
||||
setSize(550, 280);
|
||||
setLocation(215, 280);
|
||||
setResizable(false);
|
||||
|
||||
Container contentPane = this.getContentPane();
|
||||
contentPane.setBackground(new Color(255, 255, 255));
|
||||
|
||||
Wallet wallet = WorldManager.getInstance().getWallet();
|
||||
wallet.addListener(this);
|
||||
loadWallet();
|
||||
|
||||
}
|
||||
|
||||
private void loadWallet() {
|
||||
|
||||
Container contentPane = this.getContentPane();
|
||||
contentPane.removeAll();
|
||||
contentPane.setLayout(new FlowLayout());
|
||||
|
||||
Wallet wallet = WorldManager.getInstance().getWallet();
|
||||
|
||||
for (Account account : wallet.getAccountCollection()) {
|
||||
WalletAddressPanel rowPanel = new WalletAddressPanel(account);
|
||||
contentPane.add(rowPanel);
|
||||
}
|
||||
|
||||
WalletSumPanel sumPanel = new WalletSumPanel(wallet.totalBalance());
|
||||
contentPane.add(sumPanel);
|
||||
|
||||
// TODO: move this to some add button method
|
||||
URL addAddressIconURL = ClassLoader.getSystemResource("buttons/add-address.png");
|
||||
ImageIcon addAddressIcon = new ImageIcon(addAddressIconURL);
|
||||
JLabel addAddressLabel = new JLabel(addAddressIcon);
|
||||
addAddressLabel.setToolTipText("Add new address");
|
||||
addAddressLabel.setCursor(new Cursor(Cursor.HAND_CURSOR));
|
||||
addAddressLabel.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
|
||||
Wallet wallet = WorldManager.getInstance().getWallet();
|
||||
if (wallet.getAccountCollection().size() >= 5) {
|
||||
JOptionPane.showMessageDialog(walletWindow,
|
||||
"Hey do you really need more than 5 address for a demo wallet");
|
||||
return;
|
||||
}
|
||||
|
||||
wallet.addNewAccount();
|
||||
Dimension dimension = walletWindow.getSize();
|
||||
int height = dimension.height;
|
||||
int width = dimension.width;
|
||||
|
||||
Dimension newDimension = new Dimension(width, (height + 45));
|
||||
walletWindow.setSize(newDimension);
|
||||
}
|
||||
});
|
||||
contentPane.add(addAddressLabel);
|
||||
contentPane.revalidate();
|
||||
contentPane.repaint();
|
||||
}
|
||||
|
||||
public void addCloseAction() {
|
||||
this.addWindowListener(new WindowAdapter() {
|
||||
public void windowClosing(WindowEvent e) {
|
||||
toolBar.walletToggle.setSelected(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void valueChanged() {
|
||||
loadWallet();
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user