package com.dgex.offspring.ui; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import nxt.Constants; import nxt.Nxt; import nxt.Transaction; import nxt.util.Convert; import org.apache.log4j.Logger; import org.eclipse.e4.ui.di.UISynchronize; import org.eclipse.e4.ui.services.IStylingEngine; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.TitleAreaDialog; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Link; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; import org.json.simple.JSONObject; import com.dgex.offspring.config.IContactsService; import com.dgex.offspring.config.JSonWriter; import com.dgex.offspring.nxtCore.service.INxtService; import com.dgex.offspring.ui.controls.TransactionTypes; import com.dgex.offspring.user.service.IUserService; public class InspectTransactionDialog extends TitleAreaDialog { static final Logger logger = Logger.getLogger(InspectTransactionDialog.class); private final List<Long> history = new ArrayList<Long>(); private int historyCursor = 0; private Long transactionId; private Composite container; private Button previousButton; private Button nextButton; private Label timeLabel; private Link senderLink; private Link receiverLink; private Label heightLabel; private Label deadlineLabel; private Label amountLabel; private Label feeLabel; private Link blockLink; private Text hashText; private Text signatureText; private Text jsonText; private Link referencedLink; private final IStylingEngine engine; private final INxtService nxt; private final IUserService userService; private final UISynchronize sync; private final IContactsService contactsService; static InspectTransactionDialog INSTANCE = null; static final SimpleDateFormat dateFormat = new SimpleDateFormat( "dd MMM yy H:mm:ss"); public InspectTransactionDialog(Shell shell, Long transactionId, INxtService nxt, IStylingEngine engine, IUserService userService, UISynchronize sync, IContactsService contactsService) { super(shell); this.transactionId = transactionId; this.engine = engine; this.nxt = nxt; this.userService = userService; this.sync = sync; this.contactsService = contactsService; } @Override protected void setShellStyle(int newShellStyle) { super.setShellStyle(SWT.CLOSE | SWT.MODELESS | SWT.BORDER | SWT.TITLE | SWT.RESIZE); setBlockOnOpen(false); } @Override public void create() { super.create(); setTitle("Transaction"); setMessage(Convert.toUnsignedLong(transactionId)); } /** * Static method that opens a new dialog or switches the existing dialog to * another account id. The dialog shows back and forward buttons to navigate * between accounts inspected. * * @param transactionId * @return */ public static void show(final Long transactionId, final INxtService nxt, final IStylingEngine engine, final IUserService userService, final UISynchronize sync, final IContactsService contactsService) { sync.syncExec(new Runnable() { @Override public void run() { Shell shell = Display.getCurrent().getActiveShell(); if (shell != null) { while (shell.getParent() != null) { shell = shell.getParent().getShell(); } } if (INSTANCE == null) { INSTANCE = new InspectTransactionDialog(shell, transactionId, nxt, engine, userService, sync, contactsService); INSTANCE.history.add(transactionId); INSTANCE.historyCursor = 0; INSTANCE.open(); } else { INSTANCE.history.add(transactionId); INSTANCE.historyCursor = INSTANCE.history.size() - 1; INSTANCE.setTransactionId(transactionId); INSTANCE.getShell().forceActive(); } } }); } @Override public boolean close() { INSTANCE = null; return super.close(); } public void setTransactionId(Long transactionId) { this.transactionId = transactionId; Transaction transaction = Nxt.getBlockchain().getTransaction(transactionId); if (transaction == null) { logger.warn("Cannot open non-existing transaction"); return; } setTitle("Transaction " + Convert.toUnsignedLong(transactionId)); setMessage("Type: " + TransactionTypes.getTransactionType(transaction)); updateNavigateButtons(); timeLabel.setText(dateFormat.format(convertTimestamp(transaction .getTimestamp())) + " (" + transaction.getTimestamp() + ")"); blockLink.setText("<A>" + Convert.toUnsignedLong(transaction.getBlockId()) + "</A>"); if (transaction.getReferencedTransactionId() != null) referencedLink.setText("<A>" + Convert.toUnsignedLong(transaction.getReferencedTransactionId()) + "</A>"); else referencedLink.setText(""); senderLink.setText("<A>" + Convert.toUnsignedLong(transaction.getSenderId()) + "</A>"); receiverLink.setText("<A>" + Convert.toUnsignedLong(transaction.getRecipientId()) + "</A>"); amountLabel.setText(Integer.toString(transaction.getAmount())); feeLabel.setText(Integer.toString(transaction.getFee())); heightLabel.setText(Integer.toString(transaction.getHeight())); deadlineLabel.setText(Integer.toString(transaction.getDeadline())); hashText.setText(transaction.getHash()); signatureText.setText(Convert.toHexString(transaction.getSignature())); jsonText.setText(prettyPrint(transaction.getJSONObject())); timeLabel.pack(); blockLink.pack(); senderLink.pack(); receiverLink.pack(); amountLabel.pack(); feeLabel.pack(); heightLabel.pack(); deadlineLabel.pack(); container.layout(); } private void updateNavigateButtons() { previousButton.setEnabled(historyCursor > 0); nextButton.setEnabled(historyCursor < (history.size() - 1)); } private Date convertTimestamp(int timestamp) { return new Date((timestamp * 1000l) + (Constants.EPOCH_BEGINNING - 500)); } private void labelData(Control control) { GridDataFactory.swtDefaults().align(SWT.BEGINNING, SWT.CENTER) .applyTo(control); } private void fieldData(Control control) { GridDataFactory.swtDefaults().align(SWT.BEGINNING, SWT.CENTER) .grab(true, false).applyTo(control); } @Override protected Control createDialogArea(Composite parent) { initializeDialogUnits(parent); Composite outerContainer = (Composite) super.createDialogArea(parent); GridLayout layout = new GridLayout(4, false); layout.horizontalSpacing = 15; layout.marginTop = 10; layout.marginLeft = 10; GridData gd = new GridData(GridData.FILL, GridData.FILL, false, true); gd.widthHint = Math .round((convertHorizontalDLUsToPixels(IDialogConstants.MINIMUM_MESSAGE_AREA_WIDTH) / 2) * 3); container = new Composite(outerContainer, SWT.NONE); container.setLayoutData(gd); container.setLayout(layout); Label label = new Label(container, SWT.NONE); label.setText("Time"); labelData(label); timeLabel = new Label(container, SWT.NONE); fieldData(timeLabel); Composite navigationComposite = new Composite(container, SWT.NONE); GridDataFactory.fillDefaults().span(2, 1).grab(true, false) .align(SWT.END, SWT.CENTER).applyTo(navigationComposite); navigationComposite.setLayout(new FillLayout()); previousButton = new Button(navigationComposite, SWT.PUSH); previousButton.setEnabled(false); previousButton.setText("<"); previousButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { historyCursor = Math.max(0, historyCursor - 1); setTransactionId(history.get(historyCursor)); } }); nextButton = new Button(navigationComposite, SWT.PUSH); nextButton.setEnabled(false); nextButton.setText(">"); nextButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { historyCursor = Math.min(history.size() - 1, historyCursor + 1); setTransactionId(history.get(historyCursor)); } }); // ----- label = new Label(container, SWT.NONE); label.setText("Sender"); labelData(label); senderLink = new Link(container, SWT.NONE); fieldData(senderLink); senderLink.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { Transaction t = Nxt.getBlockchain().getTransaction(transactionId); if (t != null) { InspectAccountDialog.show(t.getSenderId(), nxt, engine, userService, sync, contactsService); } } }); label = new Label(container, SWT.NONE); label.setText("Receiver"); labelData(label); receiverLink = new Link(container, SWT.NONE); fieldData(receiverLink); receiverLink.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { Transaction t = Nxt.getBlockchain().getTransaction(transactionId); if (t != null) { InspectAccountDialog.show(t.getRecipientId(), nxt, engine, userService, sync, contactsService); } } }); // ----- label = new Label(container, SWT.NONE); label.setText("Block"); labelData(label); blockLink = new Link(container, SWT.NONE); fieldData(blockLink); blockLink.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { Transaction t = Nxt.getBlockchain().getTransaction(transactionId); if (t != null) { InspectBlockDialog.show(t.getBlockId(), nxt, engine, userService, sync, contactsService); } } }); label = new Label(container, SWT.NONE); label.setText("Referenced"); labelData(label); referencedLink = new Link(container, SWT.NONE); fieldData(referencedLink); // ----- label = new Label(container, SWT.NONE); label.setText("Amount"); labelData(label); amountLabel = new Label(container, SWT.NONE); fieldData(amountLabel); label = new Label(container, SWT.NONE); label.setText("Fee"); labelData(label); feeLabel = new Label(container, SWT.NONE); fieldData(feeLabel); // ----- label = new Label(container, SWT.NONE); label.setText("Height"); labelData(label); heightLabel = new Label(container, SWT.NONE); fieldData(heightLabel); label = new Label(container, SWT.NONE); label.setText("Deadline"); labelData(label); deadlineLabel = new Label(container, SWT.NONE); fieldData(deadlineLabel); // ----- label = new Label(container, SWT.NONE); label.setText("Hash"); labelData(label); hashText = new Text(container, SWT.BORDER); GridDataFactory.swtDefaults().span(3, 1).grab(true, false) .align(SWT.FILL, SWT.CENTER).applyTo(hashText); label = new Label(container, SWT.NONE); label.setText("Signature"); labelData(label); signatureText = new Text(container, SWT.BORDER); GridDataFactory.swtDefaults().span(3, 1).grab(true, false) .align(SWT.FILL, SWT.CENTER).applyTo(signatureText); jsonText = new Text(container, SWT.MULTI | SWT.BORDER | SWT.WRAP | SWT.V_SCROLL | SWT.H_SCROLL); GridDataFactory.swtDefaults().span(4, 1).grab(true, true) .align(SWT.FILL, SWT.CENTER).applyTo(jsonText); ((GridData) jsonText.getLayoutData()).minimumHeight = 220; sync.asyncExec(new Runnable() { @Override public void run() { setTransactionId(transactionId); } }); return outerContainer; } @Override protected void createButtonsForButtonBar(Composite parent) { createButton(parent, IDialogConstants.OK_ID, IDialogConstants.CLOSE_LABEL, true); } @Override protected boolean isResizable() { return true; } private String prettyPrint(JSONObject object) { JSonWriter writer = new JSonWriter(); try { object.writeJSONString(writer); return writer.toString(); } catch (IOException e) { logger.error("Could not serialize JSON", e); } finally { try { writer.close(); } catch (IOException e) {} } return "ERROR"; } }