Adapting for Ethereym as a library style:

+ adding gui files from the split
This commit is contained in:
romanman 2014-09-05 09:45:25 +03:00
parent a7ef7d4f7e
commit 39528c10ba
20 changed files with 4496 additions and 0 deletions

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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);
}
});
}
}

View File

@ -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;
}
}

View File

@ -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();
}
});
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
});
}
}

View File

@ -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);
}
});
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View 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);
}
});
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}