@ -1,377 +0,0 @@ |
|||||||
import QtQuick 2.0 |
|
||||||
import QtQuick.Controls 1.0; |
|
||||||
import QtQuick.Layouts 1.0; |
|
||||||
import QtQuick.Dialogs 1.0; |
|
||||||
import QtQuick.Window 2.1; |
|
||||||
import QtQuick.Controls.Styles 1.1 |
|
||||||
import Ethereum 1.0 |
|
||||||
|
|
||||||
ApplicationWindow { |
|
||||||
id: win |
|
||||||
visible: false |
|
||||||
title: "IceCREAM" |
|
||||||
minimumWidth: 1280 |
|
||||||
minimumHeight: 700 |
|
||||||
width: 1290 |
|
||||||
height: 750 |
|
||||||
|
|
||||||
property alias codeText: codeEditor.text |
|
||||||
property alias dataText: rawDataField.text |
|
||||||
|
|
||||||
onClosing: { |
|
||||||
//compileTimer.stop() |
|
||||||
} |
|
||||||
|
|
||||||
MenuBar { |
|
||||||
Menu { |
|
||||||
title: "Debugger" |
|
||||||
MenuItem { |
|
||||||
text: "Run" |
|
||||||
shortcut: "Ctrl+r" |
|
||||||
onTriggered: debugCurrent() |
|
||||||
} |
|
||||||
|
|
||||||
MenuItem { |
|
||||||
text: "Next" |
|
||||||
shortcut: "Ctrl+n" |
|
||||||
onTriggered: dbg.next() |
|
||||||
} |
|
||||||
|
|
||||||
MenuItem { |
|
||||||
text: "Continue" |
|
||||||
shortcut: "Ctrl+g" |
|
||||||
onTriggered: dbg.continue() |
|
||||||
} |
|
||||||
MenuItem { |
|
||||||
text: "Command" |
|
||||||
shortcut: "Ctrl+l" |
|
||||||
onTriggered: { |
|
||||||
dbgCommand.focus = true |
|
||||||
} |
|
||||||
} |
|
||||||
MenuItem { |
|
||||||
text: "Focus code" |
|
||||||
shortcut: "Ctrl+1" |
|
||||||
onTriggered: { |
|
||||||
codeEditor.focus = true |
|
||||||
} |
|
||||||
} |
|
||||||
MenuItem { |
|
||||||
text: "Focus data" |
|
||||||
shortcut: "Ctrl+2" |
|
||||||
onTriggered: { |
|
||||||
rawDataField.focus = true |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/* |
|
||||||
MenuItem { |
|
||||||
text: "Close window" |
|
||||||
shortcut: "Ctrl+w" |
|
||||||
onTriggered: { |
|
||||||
win.close() |
|
||||||
} |
|
||||||
} |
|
||||||
*/ |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
SplitView { |
|
||||||
anchors.fill: parent |
|
||||||
property var asmModel: ListModel { |
|
||||||
id: asmModel |
|
||||||
} |
|
||||||
|
|
||||||
TableView { |
|
||||||
id: asmTableView |
|
||||||
width: 200 |
|
||||||
TableViewColumn{ role: "value" ; title: "" ; width: asmTableView.width - 2 } |
|
||||||
model: asmModel |
|
||||||
} |
|
||||||
|
|
||||||
Rectangle { |
|
||||||
color: "#00000000" |
|
||||||
anchors.left: asmTableView.right |
|
||||||
anchors.right: parent.right |
|
||||||
SplitView { |
|
||||||
orientation: Qt.Vertical |
|
||||||
anchors.fill: parent |
|
||||||
|
|
||||||
Rectangle { |
|
||||||
color: "#00000000" |
|
||||||
height: 330 |
|
||||||
anchors.left: parent.left |
|
||||||
anchors.right: parent.right |
|
||||||
|
|
||||||
TextArea { |
|
||||||
id: codeEditor |
|
||||||
anchors.top: parent.top |
|
||||||
anchors.bottom: parent.bottom |
|
||||||
anchors.left: parent.left |
|
||||||
anchors.right: settings.left |
|
||||||
focus: true |
|
||||||
|
|
||||||
/* |
|
||||||
Timer { |
|
||||||
id: compileTimer |
|
||||||
interval: 500 ; running: true ; repeat: true |
|
||||||
onTriggered: { |
|
||||||
dbg.autoComp(codeEditor.text) |
|
||||||
} |
|
||||||
} |
|
||||||
*/ |
|
||||||
} |
|
||||||
|
|
||||||
Column { |
|
||||||
id: settings |
|
||||||
spacing: 5 |
|
||||||
width: 300 |
|
||||||
height: parent.height |
|
||||||
anchors.right: parent.right |
|
||||||
anchors.top: parent.top |
|
||||||
anchors.bottom: parent.bottom |
|
||||||
|
|
||||||
Label { |
|
||||||
text: "Arbitrary data" |
|
||||||
} |
|
||||||
TextArea { |
|
||||||
id: rawDataField |
|
||||||
anchors.left: parent.left |
|
||||||
anchors.right: parent.right |
|
||||||
height: 150 |
|
||||||
} |
|
||||||
|
|
||||||
Label { |
|
||||||
text: "Amount" |
|
||||||
} |
|
||||||
TextField { |
|
||||||
id: txValue |
|
||||||
width: 200 |
|
||||||
placeholderText: "Amount" |
|
||||||
validator: RegExpValidator { regExp: /\d*/ } |
|
||||||
} |
|
||||||
Label { |
|
||||||
text: "Amount of gas" |
|
||||||
} |
|
||||||
TextField { |
|
||||||
id: txGas |
|
||||||
width: 200 |
|
||||||
validator: RegExpValidator { regExp: /\d*/ } |
|
||||||
text: "10000" |
|
||||||
placeholderText: "Gas" |
|
||||||
} |
|
||||||
Label { |
|
||||||
text: "Gas price" |
|
||||||
} |
|
||||||
TextField { |
|
||||||
id: txGasPrice |
|
||||||
width: 200 |
|
||||||
placeholderText: "Gas price" |
|
||||||
text: "1000000000000" |
|
||||||
validator: RegExpValidator { regExp: /\d*/ } |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
SplitView { |
|
||||||
orientation: Qt.Vertical |
|
||||||
id: inspectorPane |
|
||||||
height: 500 |
|
||||||
|
|
||||||
SplitView { |
|
||||||
orientation: Qt.Horizontal |
|
||||||
height: 150 |
|
||||||
|
|
||||||
TableView { |
|
||||||
id: stackTableView |
|
||||||
property var stackModel: ListModel { |
|
||||||
id: stackModel |
|
||||||
} |
|
||||||
height: parent.height |
|
||||||
width: 300 |
|
||||||
TableViewColumn{ role: "value" ; title: "Temp" ; width: 200 } |
|
||||||
model: stackModel |
|
||||||
} |
|
||||||
|
|
||||||
TableView { |
|
||||||
id: memoryTableView |
|
||||||
property var memModel: ListModel { |
|
||||||
id: memModel |
|
||||||
} |
|
||||||
height: parent.height |
|
||||||
width: parent.width - stackTableView.width |
|
||||||
TableViewColumn{ id:mnumColmn ; role: "num" ; title: "#" ; width: 50} |
|
||||||
TableViewColumn{ role: "value" ; title: "Memory" ; width: 750} |
|
||||||
model: memModel |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
Rectangle { |
|
||||||
height: 100 |
|
||||||
width: parent.width |
|
||||||
TableView { |
|
||||||
id: storageTableView |
|
||||||
property var memModel: ListModel { |
|
||||||
id: storageModel |
|
||||||
} |
|
||||||
height: parent.height |
|
||||||
width: parent.width |
|
||||||
TableViewColumn{ id: key ; role: "key" ; title: "#" ; width: storageTableView.width / 2} |
|
||||||
TableViewColumn{ role: "value" ; title: "Storage" ; width: storageTableView.width / 2} |
|
||||||
model: storageModel |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
Rectangle { |
|
||||||
height: 200 |
|
||||||
width: parent.width |
|
||||||
TableView { |
|
||||||
id: logTableView |
|
||||||
property var logModel: ListModel { |
|
||||||
id: logModel |
|
||||||
} |
|
||||||
height: parent.height |
|
||||||
width: parent.width |
|
||||||
TableViewColumn{ id: message ; role: "message" ; title: "log" ; width: logTableView.width - 2 } |
|
||||||
model: logModel |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
function exec() { |
|
||||||
dbg.execCommand(dbgCommand.text); |
|
||||||
dbgCommand.text = ""; |
|
||||||
} |
|
||||||
statusBar: StatusBar { |
|
||||||
height: 30 |
|
||||||
|
|
||||||
|
|
||||||
TextField { |
|
||||||
id: dbgCommand |
|
||||||
y: 1 |
|
||||||
x: asmTableView.width |
|
||||||
width: 500 |
|
||||||
placeholderText: "Debugger (type 'help')" |
|
||||||
Keys.onReturnPressed: { |
|
||||||
exec() |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
toolBar: ToolBar { |
|
||||||
height: 30 |
|
||||||
RowLayout { |
|
||||||
spacing: 5 |
|
||||||
|
|
||||||
Button { |
|
||||||
property var enabled: true |
|
||||||
id: debugStart |
|
||||||
onClicked: { |
|
||||||
debugCurrent() |
|
||||||
} |
|
||||||
text: "Debug" |
|
||||||
} |
|
||||||
|
|
||||||
Button { |
|
||||||
property var enabled: true |
|
||||||
id: debugNextButton |
|
||||||
onClicked: { |
|
||||||
dbg.next() |
|
||||||
} |
|
||||||
text: "Next" |
|
||||||
} |
|
||||||
|
|
||||||
Button { |
|
||||||
id: debugContinueButton |
|
||||||
onClicked: { |
|
||||||
dbg.continue() |
|
||||||
} |
|
||||||
text: "Continue" |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
ComboBox { |
|
||||||
id: snippets |
|
||||||
anchors.right: parent.right |
|
||||||
model: ListModel { |
|
||||||
ListElement { text: "Snippets" ; value: "" } |
|
||||||
ListElement { text: "Call Contract" ; value: "var[2] in;\nvar ret;\n\nin[0] = \"arg1\"\nin[1] = 0xdeadbeef\n\nvar success = call(0x0c542ddea93dae0c2fcb2cf175f03ad80d6be9a0, 0, 7000, in, ret)\n\nreturn ret" } |
|
||||||
} |
|
||||||
onCurrentIndexChanged: { |
|
||||||
if(currentIndex != 0) { |
|
||||||
var code = snippets.model.get(currentIndex).value; |
|
||||||
codeEditor.insert(codeEditor.cursorPosition, code) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
function debugCurrent() { |
|
||||||
dbg.debug(txValue.text, txGas.text, txGasPrice.text, codeEditor.text, rawDataField.text) |
|
||||||
} |
|
||||||
|
|
||||||
function setAsm(asm) { |
|
||||||
asmModel.append({asm: asm}) |
|
||||||
} |
|
||||||
|
|
||||||
function clearAsm() { |
|
||||||
asmModel.clear() |
|
||||||
} |
|
||||||
|
|
||||||
function setInstruction(num) { |
|
||||||
asmTableView.selection.clear() |
|
||||||
asmTableView.selection.select(num) |
|
||||||
} |
|
||||||
|
|
||||||
function setMem(mem) { |
|
||||||
memModel.append({num: mem.num, value: mem.value}) |
|
||||||
} |
|
||||||
function clearMem(){ |
|
||||||
memModel.clear() |
|
||||||
} |
|
||||||
|
|
||||||
function setStack(stack) { |
|
||||||
stackModel.append({value: stack}) |
|
||||||
} |
|
||||||
function addDebugMessage(message){ |
|
||||||
debuggerLog.append({value: message}) |
|
||||||
} |
|
||||||
|
|
||||||
function clearStack() { |
|
||||||
stackModel.clear() |
|
||||||
} |
|
||||||
|
|
||||||
function clearStorage() { |
|
||||||
storageModel.clear() |
|
||||||
} |
|
||||||
|
|
||||||
function setStorage(storage) { |
|
||||||
storageModel.append({key: storage.key, value: storage.value}) |
|
||||||
} |
|
||||||
|
|
||||||
function setLog(msg) { |
|
||||||
// Remove first item once we've reached max log items |
|
||||||
if(logModel.count > 250) { |
|
||||||
logModel.remove(0) |
|
||||||
} |
|
||||||
|
|
||||||
if(msg.len != 0) { |
|
||||||
if(logTableView.flickableItem.atYEnd) { |
|
||||||
logModel.append({message: msg}) |
|
||||||
logTableView.positionViewAtRow(logTableView.rowCount - 1, ListView.Contain) |
|
||||||
} else { |
|
||||||
logModel.append({message: msg}) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
function clearLog() { |
|
||||||
logModel.clear() |
|
||||||
} |
|
||||||
} |
|
@ -1,31 +0,0 @@ |
|||||||
var Filter = function(options) { |
|
||||||
this.callbacks = {}; |
|
||||||
this.seed = Math.floor(Math.random() * 1000000); |
|
||||||
this.options = options; |
|
||||||
|
|
||||||
if(options == "chain") { |
|
||||||
eth.registerFilterString(options, this.seed); |
|
||||||
} else if(typeof options === "object") { |
|
||||||
eth.registerFilter(options, this.seed); |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
Filter.prototype.changed = function(callback) { |
|
||||||
var cbseed = Math.floor(Math.random() * 1000000); |
|
||||||
eth.registerFilterCallback(this.seed, cbseed); |
|
||||||
|
|
||||||
var self = this; |
|
||||||
message.connect(function(messages, seed, callbackSeed) { |
|
||||||
if(seed == self.seed && callbackSeed == cbseed) { |
|
||||||
callback.call(self, messages); |
|
||||||
} |
|
||||||
}); |
|
||||||
}; |
|
||||||
|
|
||||||
Filter.prototype.uninstall = function() { |
|
||||||
eth.uninstallFilter(this.seed) |
|
||||||
} |
|
||||||
|
|
||||||
Filter.prototype.messages = function() { |
|
||||||
return JSON.parse(eth.messages(this.options)) |
|
||||||
} |
|
@ -1,37 +0,0 @@ |
|||||||
// Helper function for generating pseudo callbacks and sending data to the QML part of the application
|
|
||||||
function postData(data, cb) { |
|
||||||
data._seed = Math.floor(Math.random() * 1000000) |
|
||||||
if(cb) { |
|
||||||
eth._callbacks[data._seed] = cb; |
|
||||||
} |
|
||||||
|
|
||||||
if(data.args === undefined) { |
|
||||||
data.args = []; |
|
||||||
} |
|
||||||
|
|
||||||
navigator.qt.postMessage(JSON.stringify(data)); |
|
||||||
} |
|
||||||
|
|
||||||
navigator.qt.onmessage = function(ev) { |
|
||||||
var data = JSON.parse(ev.data) |
|
||||||
|
|
||||||
if(data._event !== undefined) { |
|
||||||
eth.trigger(data._event, data.data); |
|
||||||
} else { |
|
||||||
if(data._seed) { |
|
||||||
var cb = eth._callbacks[data._seed]; |
|
||||||
if(cb) { |
|
||||||
// Figure out whether the returned data was an array
|
|
||||||
// array means multiple return arguments (multiple params)
|
|
||||||
if(data.data instanceof Array) { |
|
||||||
cb.apply(this, data.data) |
|
||||||
} else { |
|
||||||
cb.call(this, data.data) |
|
||||||
} |
|
||||||
|
|
||||||
// Remove the "trigger" callback
|
|
||||||
delete eth._callbacks[ev._seed]; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,753 +0,0 @@ |
|||||||
import QtQuick 2.0 |
|
||||||
import QtQuick.Controls 1.0; |
|
||||||
import QtQuick.Layouts 1.0; |
|
||||||
import QtQuick.Dialogs 1.0; |
|
||||||
import QtQuick.Window 2.1; |
|
||||||
import QtQuick.Controls.Styles 1.1 |
|
||||||
import Ethereum 1.0 |
|
||||||
|
|
||||||
import "../ext/filter.js" as Eth |
|
||||||
|
|
||||||
ApplicationWindow { |
|
||||||
id: root |
|
||||||
|
|
||||||
property alias miningButtonText: miningButton.text |
|
||||||
|
|
||||||
|
|
||||||
width: 900 |
|
||||||
height: 600 |
|
||||||
minimumHeight: 300 |
|
||||||
|
|
||||||
title: "Ether browser" |
|
||||||
|
|
||||||
// This signal is used by the filter API. The filter API connects using this signal handler from |
|
||||||
// the different QML files and plugins. |
|
||||||
signal message(var callback, int seed, int seedCallback); |
|
||||||
function invokeFilterCallback(data, receiverSeed, callbackSeed) { |
|
||||||
var messages = JSON.parse(data) |
|
||||||
// Signal handler |
|
||||||
message(messages, receiverSeed, callbackSeed); |
|
||||||
} |
|
||||||
|
|
||||||
TextField { |
|
||||||
id: copyElementHax |
|
||||||
visible: false |
|
||||||
} |
|
||||||
|
|
||||||
function copyToClipboard(text) { |
|
||||||
copyElementHax.text = text |
|
||||||
copyElementHax.selectAll() |
|
||||||
copyElementHax.copy() |
|
||||||
} |
|
||||||
|
|
||||||
// Takes care of loading all default plugins |
|
||||||
Component.onCompleted: { |
|
||||||
var walletView = addPlugin("./views/wallet.qml", {noAdd: true, section: "ethereum", active: true}) |
|
||||||
var historyView = addPlugin("./views/history.qml", {noAdd: true, section: "legacy"}) |
|
||||||
var newTxView = addPlugin("./views/transaction.qml", {noAdd: true, section: "legacy"}) |
|
||||||
var chainView = addPlugin("./views/chain.qml", {noAdd: true, section: "legacy"}) |
|
||||||
var infoView = addPlugin("./views/info.qml", {noAdd: true, section: "legacy"}) |
|
||||||
var pendingTxView = addPlugin("./views/pending_tx.qml", {noAdd: true, section: "legacy"}) |
|
||||||
var pendingTxView = addPlugin("./views/javascript.qml", {noAdd: true, section: "legacy"}) |
|
||||||
|
|
||||||
// Call the ready handler |
|
||||||
gui.done() |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
function addPlugin(path, options) { |
|
||||||
var component = Qt.createComponent(path); |
|
||||||
if(component.status != Component.Ready) { |
|
||||||
if(component.status == Component.Error) { |
|
||||||
console.debug("Error:"+ component.errorString()); |
|
||||||
} |
|
||||||
|
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
var views = mainSplit.addComponent(component, options) |
|
||||||
views.menuItem.path = path |
|
||||||
|
|
||||||
mainSplit.views.push(views); |
|
||||||
|
|
||||||
if(!options.noAdd) { |
|
||||||
gui.addPlugin(path) |
|
||||||
} |
|
||||||
|
|
||||||
return views.view |
|
||||||
} |
|
||||||
|
|
||||||
MenuBar { |
|
||||||
Menu { |
|
||||||
title: "File" |
|
||||||
MenuItem { |
|
||||||
text: "Import App" |
|
||||||
shortcut: "Ctrl+o" |
|
||||||
onTriggered: { |
|
||||||
generalFileDialog.show(true, importApp) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
MenuItem { |
|
||||||
text: "Browser" |
|
||||||
onTriggered: eth.openBrowser() |
|
||||||
} |
|
||||||
|
|
||||||
MenuItem { |
|
||||||
text: "Add plugin" |
|
||||||
onTriggered: { |
|
||||||
generalFileDialog.show(true, function(path) { |
|
||||||
addPlugin(path, {canClose: true, section: "apps"}) |
|
||||||
}) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
MenuSeparator {} |
|
||||||
|
|
||||||
MenuItem { |
|
||||||
text: "Import key" |
|
||||||
shortcut: "Ctrl+i" |
|
||||||
onTriggered: { |
|
||||||
generalFileDialog.show(true, function(path) { |
|
||||||
gui.importKey(path) |
|
||||||
}) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
MenuItem { |
|
||||||
text: "Export keys" |
|
||||||
shortcut: "Ctrl+e" |
|
||||||
onTriggered: { |
|
||||||
generalFileDialog.show(false, function(path) { |
|
||||||
}) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
Menu { |
|
||||||
title: "Developer" |
|
||||||
MenuItem { |
|
||||||
text: "Debugger" |
|
||||||
shortcut: "Ctrl+d" |
|
||||||
onTriggered: eth.startDebugger() |
|
||||||
} |
|
||||||
|
|
||||||
MenuItem { |
|
||||||
text: "Import Tx" |
|
||||||
onTriggered: { |
|
||||||
txImportDialog.visible = true |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
MenuItem { |
|
||||||
text: "Run JS file" |
|
||||||
onTriggered: { |
|
||||||
generalFileDialog.show(true, function(path) { |
|
||||||
eth.evalJavascriptFile(path) |
|
||||||
}) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
MenuItem { |
|
||||||
text: "Dump state" |
|
||||||
onTriggered: { |
|
||||||
generalFileDialog.show(false, function(path) { |
|
||||||
// Empty hash for latest |
|
||||||
gui.dumpState("", path) |
|
||||||
}) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
MenuSeparator {} |
|
||||||
|
|
||||||
MenuItem { |
|
||||||
id: miningSpeed |
|
||||||
text: "Mining: Turbo" |
|
||||||
onTriggered: { |
|
||||||
gui.toggleTurboMining() |
|
||||||
if(text == "Mining: Turbo") { |
|
||||||
text = "Mining: Normal"; |
|
||||||
} else { |
|
||||||
text = "Mining: Turbo"; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
Menu { |
|
||||||
title: "Network" |
|
||||||
MenuItem { |
|
||||||
text: "Add Peer" |
|
||||||
shortcut: "Ctrl+p" |
|
||||||
onTriggered: { |
|
||||||
addPeerWin.visible = true |
|
||||||
} |
|
||||||
} |
|
||||||
MenuItem { |
|
||||||
text: "Show Peers" |
|
||||||
shortcut: "Ctrl+e" |
|
||||||
onTriggered: { |
|
||||||
peerWindow.visible = true |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
Menu { |
|
||||||
title: "Help" |
|
||||||
MenuItem { |
|
||||||
text: "About" |
|
||||||
onTriggered: { |
|
||||||
aboutWin.visible = true |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
statusBar: StatusBar { |
|
||||||
height: 32 |
|
||||||
RowLayout { |
|
||||||
Button { |
|
||||||
id: miningButton |
|
||||||
text: "Start Mining" |
|
||||||
onClicked: { |
|
||||||
gui.toggleMining() |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
Button { |
|
||||||
id: importAppButton |
|
||||||
text: "Browser" |
|
||||||
onClicked: { |
|
||||||
eth.openBrowser() |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
RowLayout { |
|
||||||
Label { |
|
||||||
id: walletValueLabel |
|
||||||
|
|
||||||
font.pixelSize: 10 |
|
||||||
styleColor: "#797979" |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
Label { |
|
||||||
y: 6 |
|
||||||
objectName: "miningLabel" |
|
||||||
visible: true |
|
||||||
font.pixelSize: 10 |
|
||||||
anchors.right: lastBlockLabel.left |
|
||||||
anchors.rightMargin: 5 |
|
||||||
} |
|
||||||
|
|
||||||
Label { |
|
||||||
y: 6 |
|
||||||
id: lastBlockLabel |
|
||||||
objectName: "lastBlockLabel" |
|
||||||
visible: true |
|
||||||
text: "" |
|
||||||
font.pixelSize: 10 |
|
||||||
anchors.right: peerGroup.left |
|
||||||
anchors.rightMargin: 5 |
|
||||||
} |
|
||||||
|
|
||||||
ProgressBar { |
|
||||||
id: syncProgressIndicator |
|
||||||
visible: false |
|
||||||
objectName: "syncProgressIndicator" |
|
||||||
y: 3 |
|
||||||
width: 140 |
|
||||||
indeterminate: true |
|
||||||
anchors.right: peerGroup.left |
|
||||||
anchors.rightMargin: 5 |
|
||||||
} |
|
||||||
|
|
||||||
RowLayout { |
|
||||||
id: peerGroup |
|
||||||
y: 7 |
|
||||||
anchors.right: parent.right |
|
||||||
MouseArea { |
|
||||||
onDoubleClicked: peerWindow.visible = true |
|
||||||
anchors.fill: parent |
|
||||||
} |
|
||||||
|
|
||||||
Label { |
|
||||||
id: peerLabel |
|
||||||
font.pixelSize: 8 |
|
||||||
text: "0 / 0" |
|
||||||
} |
|
||||||
Image { |
|
||||||
id: peerImage |
|
||||||
width: 10; height: 10 |
|
||||||
source: "../network.png" |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
property var blockModel: ListModel { |
|
||||||
id: blockModel |
|
||||||
} |
|
||||||
|
|
||||||
SplitView { |
|
||||||
property var views: []; |
|
||||||
|
|
||||||
id: mainSplit |
|
||||||
anchors.fill: parent |
|
||||||
resizing: false |
|
||||||
|
|
||||||
function setView(view, menu) { |
|
||||||
for(var i = 0; i < views.length; i++) { |
|
||||||
views[i].view.visible = false |
|
||||||
|
|
||||||
views[i].menuItem.border.color = "#00000000" |
|
||||||
views[i].menuItem.color = "#00000000" |
|
||||||
} |
|
||||||
view.visible = true |
|
||||||
|
|
||||||
menu.border.color = "#CCCCCC" |
|
||||||
menu.color = "#FFFFFFFF" |
|
||||||
} |
|
||||||
|
|
||||||
function addComponent(component, options) { |
|
||||||
var view = mainView.createView(component, options) |
|
||||||
view.visible = false |
|
||||||
view.anchors.fill = mainView |
|
||||||
|
|
||||||
if( !view.hasOwnProperty("iconSource") ) { |
|
||||||
console.log("Could not load plugin. Property 'iconSourc' not found on view."); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
var menuItem = menu.createMenuItem(view.iconSource, view, options); |
|
||||||
if( view.hasOwnProperty("menuItem") ) { |
|
||||||
view.menuItem = menuItem; |
|
||||||
} |
|
||||||
|
|
||||||
if( view.hasOwnProperty("onReady") ) { |
|
||||||
view.onReady.call(view) |
|
||||||
} |
|
||||||
|
|
||||||
if( options.active ) { |
|
||||||
setView(view, menuItem) |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
return {view: view, menuItem: menuItem} |
|
||||||
} |
|
||||||
|
|
||||||
/********************* |
|
||||||
* Main menu. |
|
||||||
********************/ |
|
||||||
Rectangle { |
|
||||||
id: menu |
|
||||||
Layout.minimumWidth: 180 |
|
||||||
Layout.maximumWidth: 180 |
|
||||||
anchors.top: parent.top |
|
||||||
color: "#ececec" |
|
||||||
|
|
||||||
Component { |
|
||||||
id: menuItemTemplate |
|
||||||
Rectangle { |
|
||||||
id: menuItem |
|
||||||
property var view; |
|
||||||
property var path; |
|
||||||
|
|
||||||
property alias title: label.text |
|
||||||
property alias icon: icon.source |
|
||||||
property alias secondaryTitle: secondary.text |
|
||||||
|
|
||||||
width: 180 |
|
||||||
height: 28 |
|
||||||
border.color: "#00000000" |
|
||||||
border.width: 1 |
|
||||||
radius: 5 |
|
||||||
color: "#00000000" |
|
||||||
|
|
||||||
anchors { |
|
||||||
left: parent.left |
|
||||||
leftMargin: 4 |
|
||||||
} |
|
||||||
|
|
||||||
MouseArea { |
|
||||||
anchors.fill: parent |
|
||||||
onClicked: { |
|
||||||
mainSplit.setView(view, menuItem) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
Image { |
|
||||||
id: icon |
|
||||||
height: 20 |
|
||||||
width: 20 |
|
||||||
anchors { |
|
||||||
left: parent.left |
|
||||||
verticalCenter: parent.verticalCenter |
|
||||||
leftMargin: 3 |
|
||||||
} |
|
||||||
MouseArea { |
|
||||||
anchors.fill: parent |
|
||||||
onClicked: { |
|
||||||
menuItem.closeApp() |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
Text { |
|
||||||
id: label |
|
||||||
anchors { |
|
||||||
left: icon.right |
|
||||||
verticalCenter: parent.verticalCenter |
|
||||||
leftMargin: 3 |
|
||||||
} |
|
||||||
|
|
||||||
color: "#0D0A01" |
|
||||||
font.pixelSize: 12 |
|
||||||
} |
|
||||||
|
|
||||||
Text { |
|
||||||
id: secondary |
|
||||||
anchors { |
|
||||||
right: parent.right |
|
||||||
rightMargin: 8 |
|
||||||
verticalCenter: parent.verticalCenter |
|
||||||
} |
|
||||||
color: "#AEADBE" |
|
||||||
font.pixelSize: 12 |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
function closeApp() { |
|
||||||
if(this.view.hasOwnProperty("onDestroy")) { |
|
||||||
this.view.onDestroy.call(this.view) |
|
||||||
} |
|
||||||
|
|
||||||
this.view.destroy() |
|
||||||
this.destroy() |
|
||||||
gui.removePlugin(this.path) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
function createMenuItem(icon, view, options) { |
|
||||||
if(options === undefined) { |
|
||||||
options = {}; |
|
||||||
} |
|
||||||
|
|
||||||
var section; |
|
||||||
switch(options.section) { |
|
||||||
case "ethereum": |
|
||||||
section = menuDefault; |
|
||||||
break; |
|
||||||
case "legacy": |
|
||||||
section = menuLegacy; |
|
||||||
break; |
|
||||||
default: |
|
||||||
section = menuApps; |
|
||||||
break; |
|
||||||
} |
|
||||||
|
|
||||||
var comp = menuItemTemplate.createObject(section) |
|
||||||
|
|
||||||
comp.view = view |
|
||||||
comp.title = view.title |
|
||||||
comp.icon = view.iconSource |
|
||||||
/* |
|
||||||
if(view.secondary !== undefined) { |
|
||||||
comp.secondary = view.secondary |
|
||||||
} |
|
||||||
*/ |
|
||||||
|
|
||||||
return comp |
|
||||||
|
|
||||||
/* |
|
||||||
if(options.canClose) { |
|
||||||
//comp.closeButton.visible = options.canClose |
|
||||||
} |
|
||||||
*/ |
|
||||||
} |
|
||||||
|
|
||||||
ColumnLayout { |
|
||||||
id: menuColumn |
|
||||||
y: 10 |
|
||||||
width: parent.width |
|
||||||
anchors.left: parent.left |
|
||||||
anchors.right: parent.right |
|
||||||
spacing: 3 |
|
||||||
|
|
||||||
Text { |
|
||||||
text: "ETHEREUM" |
|
||||||
font.bold: true |
|
||||||
anchors { |
|
||||||
left: parent.left |
|
||||||
leftMargin: 5 |
|
||||||
} |
|
||||||
color: "#888888" |
|
||||||
} |
|
||||||
|
|
||||||
ColumnLayout { |
|
||||||
id: menuDefault |
|
||||||
spacing: 3 |
|
||||||
anchors { |
|
||||||
left: parent.left |
|
||||||
right: parent.right |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
Text { |
|
||||||
text: "APPS" |
|
||||||
font.bold: true |
|
||||||
anchors { |
|
||||||
left: parent.left |
|
||||||
leftMargin: 5 |
|
||||||
} |
|
||||||
color: "#888888" |
|
||||||
} |
|
||||||
|
|
||||||
ColumnLayout { |
|
||||||
id: menuApps |
|
||||||
spacing: 3 |
|
||||||
anchors { |
|
||||||
left: parent.left |
|
||||||
right: parent.right |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
Text { |
|
||||||
text: "DEBUG" |
|
||||||
font.bold: true |
|
||||||
anchors { |
|
||||||
left: parent.left |
|
||||||
leftMargin: 5 |
|
||||||
} |
|
||||||
color: "#888888" |
|
||||||
} |
|
||||||
|
|
||||||
ColumnLayout { |
|
||||||
id: menuLegacy |
|
||||||
spacing: 3 |
|
||||||
anchors { |
|
||||||
left: parent.left |
|
||||||
right: parent.right |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/********************* |
|
||||||
* Main view |
|
||||||
********************/ |
|
||||||
Rectangle { |
|
||||||
id: mainView |
|
||||||
color: "#00000000" |
|
||||||
|
|
||||||
anchors.right: parent.right |
|
||||||
anchors.left: menu.right |
|
||||||
anchors.bottom: parent.bottom |
|
||||||
anchors.top: parent.top |
|
||||||
|
|
||||||
function createView(component) { |
|
||||||
var view = component.createObject(mainView) |
|
||||||
|
|
||||||
return view; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/****************** |
|
||||||
* Dialogs |
|
||||||
*****************/ |
|
||||||
FileDialog { |
|
||||||
id: generalFileDialog |
|
||||||
property var callback; |
|
||||||
onAccepted: { |
|
||||||
var path = this.fileUrl.toString(); |
|
||||||
callback.call(this, path); |
|
||||||
} |
|
||||||
|
|
||||||
function show(selectExisting, callback) { |
|
||||||
generalFileDialog.callback = callback; |
|
||||||
generalFileDialog.selectExisting = selectExisting; |
|
||||||
|
|
||||||
this.open(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/****************** |
|
||||||
* Wallet functions |
|
||||||
*****************/ |
|
||||||
function importApp(path) { |
|
||||||
var ext = path.split('.').pop() |
|
||||||
if(ext == "html" || ext == "htm") { |
|
||||||
eth.openHtml(path) |
|
||||||
}else if(ext == "qml"){ |
|
||||||
addPlugin(path, {canClose: true, section: "apps"}) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
function setWalletValue(value) { |
|
||||||
walletValueLabel.text = value |
|
||||||
} |
|
||||||
|
|
||||||
function loadPlugin(name) { |
|
||||||
console.log("Loading plugin" + name) |
|
||||||
var view = mainView.addPlugin(name) |
|
||||||
} |
|
||||||
|
|
||||||
function setPeers(text) { |
|
||||||
peerLabel.text = text |
|
||||||
} |
|
||||||
|
|
||||||
function addPeer(peer) { |
|
||||||
// We could just append the whole peer object but it cries if you try to alter them |
|
||||||
peerModel.append({ip: peer.ip, port: peer.port, lastResponse:timeAgo(peer.lastSend), latency: peer.latency, version: peer.version}) |
|
||||||
} |
|
||||||
|
|
||||||
function resetPeers(){ |
|
||||||
peerModel.clear() |
|
||||||
} |
|
||||||
|
|
||||||
function timeAgo(unixTs){ |
|
||||||
var lapsed = (Date.now() - new Date(unixTs*1000)) / 1000 |
|
||||||
return (lapsed + " seconds ago") |
|
||||||
} |
|
||||||
|
|
||||||
function convertToPretty(unixTs){ |
|
||||||
var a = new Date(unixTs*1000); |
|
||||||
var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; |
|
||||||
var year = a.getFullYear(); |
|
||||||
var month = months[a.getMonth()]; |
|
||||||
var date = a.getDate(); |
|
||||||
var hour = a.getHours(); |
|
||||||
var min = a.getMinutes(); |
|
||||||
var sec = a.getSeconds(); |
|
||||||
var time = date+' '+month+' '+year+' '+hour+':'+min+':'+sec ; |
|
||||||
return time; |
|
||||||
} |
|
||||||
|
|
||||||
/********************** |
|
||||||
* Windows |
|
||||||
*********************/ |
|
||||||
Window { |
|
||||||
id: peerWindow |
|
||||||
//flags: Qt.CustomizeWindowHint | Qt.Tool | Qt.WindowCloseButtonHint |
|
||||||
height: 200 |
|
||||||
width: 700 |
|
||||||
Rectangle { |
|
||||||
anchors.fill: parent |
|
||||||
property var peerModel: ListModel { |
|
||||||
id: peerModel |
|
||||||
} |
|
||||||
TableView { |
|
||||||
anchors.fill: parent |
|
||||||
id: peerTable |
|
||||||
model: peerModel |
|
||||||
TableViewColumn{width: 100; role: "ip" ; title: "IP" } |
|
||||||
TableViewColumn{width: 60; role: "port" ; title: "Port" } |
|
||||||
TableViewColumn{width: 140; role: "lastResponse"; title: "Last event" } |
|
||||||
TableViewColumn{width: 100; role: "latency"; title: "Latency" } |
|
||||||
TableViewColumn{width: 260; role: "version" ; title: "Version" } |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
Window { |
|
||||||
id: aboutWin |
|
||||||
visible: false |
|
||||||
title: "About" |
|
||||||
minimumWidth: 350 |
|
||||||
maximumWidth: 350 |
|
||||||
maximumHeight: 200 |
|
||||||
minimumHeight: 200 |
|
||||||
|
|
||||||
Image { |
|
||||||
id: aboutIcon |
|
||||||
height: 150 |
|
||||||
width: 150 |
|
||||||
fillMode: Image.PreserveAspectFit |
|
||||||
smooth: true |
|
||||||
source: "../facet.png" |
|
||||||
x: 10 |
|
||||||
y: 10 |
|
||||||
} |
|
||||||
|
|
||||||
Text { |
|
||||||
anchors.left: aboutIcon.right |
|
||||||
anchors.leftMargin: 10 |
|
||||||
font.pointSize: 12 |
|
||||||
text: "<h2>Ethereal - Aitne</h2><br><h3>Development</h3>Jeffrey Wilcke<br>Maran Hidskes<br>Viktor Trón<br>" |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
Window { |
|
||||||
id: txImportDialog |
|
||||||
minimumWidth: 270 |
|
||||||
maximumWidth: 270 |
|
||||||
maximumHeight: 50 |
|
||||||
minimumHeight: 50 |
|
||||||
TextField { |
|
||||||
id: txImportField |
|
||||||
width: 170 |
|
||||||
anchors.verticalCenter: parent.verticalCenter |
|
||||||
anchors.left: parent.left |
|
||||||
anchors.leftMargin: 10 |
|
||||||
onAccepted: { |
|
||||||
} |
|
||||||
} |
|
||||||
Button { |
|
||||||
anchors.left: txImportField.right |
|
||||||
anchors.verticalCenter: parent.verticalCenter |
|
||||||
anchors.leftMargin: 5 |
|
||||||
text: "Import" |
|
||||||
onClicked: { |
|
||||||
eth.importTx(txImportField.text) |
|
||||||
txImportField.visible = false |
|
||||||
} |
|
||||||
} |
|
||||||
Component.onCompleted: { |
|
||||||
addrField.focus = true |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
Window { |
|
||||||
id: addPeerWin |
|
||||||
visible: false |
|
||||||
minimumWidth: 230 |
|
||||||
maximumWidth: 230 |
|
||||||
maximumHeight: 50 |
|
||||||
minimumHeight: 50 |
|
||||||
|
|
||||||
TextField { |
|
||||||
id: addrField |
|
||||||
anchors.verticalCenter: parent.verticalCenter |
|
||||||
anchors.left: parent.left |
|
||||||
anchors.leftMargin: 10 |
|
||||||
placeholderText: "address:port" |
|
||||||
onAccepted: { |
|
||||||
eth.connectToPeer(addrField.text) |
|
||||||
addPeerWin.visible = false |
|
||||||
} |
|
||||||
} |
|
||||||
Button { |
|
||||||
anchors.left: addrField.right |
|
||||||
anchors.verticalCenter: parent.verticalCenter |
|
||||||
anchors.leftMargin: 5 |
|
||||||
text: "Add" |
|
||||||
onClicked: { |
|
||||||
eth.connectToPeer(addrField.text) |
|
||||||
addPeerWin.visible = false |
|
||||||
} |
|
||||||
} |
|
||||||
Component.onCompleted: { |
|
||||||
addrField.focus = true |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,347 +0,0 @@ |
|||||||
import QtQuick 2.0 |
|
||||||
import QtWebKit 3.0 |
|
||||||
import QtWebKit.experimental 1.0 |
|
||||||
import QtQuick.Controls 1.0; |
|
||||||
import QtQuick.Controls.Styles 1.0 |
|
||||||
import QtQuick.Layouts 1.0; |
|
||||||
import QtQuick.Window 2.1; |
|
||||||
import Ethereum 1.0 |
|
||||||
|
|
||||||
ApplicationWindow { |
|
||||||
id: window |
|
||||||
title: "Ethereum" |
|
||||||
width: 1000 |
|
||||||
height: 800 |
|
||||||
minimumHeight: 300 |
|
||||||
|
|
||||||
property alias url: webview.url |
|
||||||
property alias webView: webview |
|
||||||
|
|
||||||
Item { |
|
||||||
objectName: "root" |
|
||||||
id: root |
|
||||||
anchors.fill: parent |
|
||||||
state: "inspectorShown" |
|
||||||
|
|
||||||
RowLayout { |
|
||||||
id: navBar |
|
||||||
height: 40 |
|
||||||
anchors { |
|
||||||
left: parent.left |
|
||||||
right: parent.right |
|
||||||
leftMargin: 7 |
|
||||||
} |
|
||||||
|
|
||||||
Button { |
|
||||||
id: back |
|
||||||
onClicked: { |
|
||||||
webview.goBack() |
|
||||||
} |
|
||||||
style: ButtonStyle { |
|
||||||
background: Image { |
|
||||||
source: "../back.png" |
|
||||||
width: 30 |
|
||||||
height: 30 |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
TextField { |
|
||||||
anchors { |
|
||||||
left: back.right |
|
||||||
right: toggleInspector.left |
|
||||||
leftMargin: 5 |
|
||||||
rightMargin: 5 |
|
||||||
} |
|
||||||
id: uriNav |
|
||||||
y: parent.height / 2 - this.height / 2 |
|
||||||
|
|
||||||
Keys.onReturnPressed: { |
|
||||||
webview.url = this.text; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
Button { |
|
||||||
id: toggleInspector |
|
||||||
anchors { |
|
||||||
right: parent.right |
|
||||||
} |
|
||||||
iconSource: "../bug.png" |
|
||||||
onClicked: { |
|
||||||
if(inspector.visible == true){ |
|
||||||
inspector.visible = false |
|
||||||
}else{ |
|
||||||
inspector.visible = true |
|
||||||
inspector.url = webview.experimental.remoteInspectorUrl |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
WebView { |
|
||||||
objectName: "webView" |
|
||||||
id: webview |
|
||||||
anchors { |
|
||||||
left: parent.left |
|
||||||
right: parent.right |
|
||||||
bottom: parent.bottom |
|
||||||
top: navBar.bottom |
|
||||||
} |
|
||||||
onTitleChanged: { window.title = title } |
|
||||||
|
|
||||||
property var cleanPath: false |
|
||||||
onNavigationRequested: { |
|
||||||
if(!this.cleanPath) { |
|
||||||
var uri = request.url.toString(); |
|
||||||
if(!/.*\:\/\/.*/.test(uri)) { |
|
||||||
uri = "http://" + uri; |
|
||||||
} |
|
||||||
|
|
||||||
var reg = /(^https?\:\/\/(?:www\.)?)([a-zA-Z0-9_\-]*\.eth)(.*)/ |
|
||||||
|
|
||||||
if(reg.test(uri)) { |
|
||||||
uri.replace(reg, function(match, pre, domain, path) { |
|
||||||
uri = pre; |
|
||||||
|
|
||||||
var lookup = ui.lookupDomain(domain.substring(0, domain.length - 4)); |
|
||||||
var ip = []; |
|
||||||
for(var i = 0, l = lookup.length; i < l; i++) { |
|
||||||
ip.push(lookup.charCodeAt(i)) |
|
||||||
} |
|
||||||
|
|
||||||
if(ip.length != 0) { |
|
||||||
uri += lookup; |
|
||||||
} else { |
|
||||||
uri += domain; |
|
||||||
} |
|
||||||
|
|
||||||
uri += path; |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
this.cleanPath = true; |
|
||||||
|
|
||||||
webview.url = uri; |
|
||||||
} else { |
|
||||||
// Prevent inf loop. |
|
||||||
this.cleanPath = false; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
experimental.preferences.javascriptEnabled: true |
|
||||||
experimental.preferences.navigatorQtObjectEnabled: true |
|
||||||
experimental.preferences.developerExtrasEnabled: true |
|
||||||
experimental.userScripts: ["../ext/pre.js", "../ext/big.js", "../ext/string.js", "../ext/ethereum.js"] |
|
||||||
experimental.onMessageReceived: { |
|
||||||
console.log("[onMessageReceived]: ", message.data) |
|
||||||
// TODO move to messaging.js |
|
||||||
var data = JSON.parse(message.data) |
|
||||||
|
|
||||||
try { |
|
||||||
switch(data.call) { |
|
||||||
case "getCoinBase": |
|
||||||
postData(data._seed, eth.coinBase()) |
|
||||||
|
|
||||||
break |
|
||||||
|
|
||||||
case "getIsListening": |
|
||||||
postData(data._seed, eth.isListening()) |
|
||||||
|
|
||||||
break |
|
||||||
|
|
||||||
case "getIsMining": |
|
||||||
postData(data._seed, eth.isMining()) |
|
||||||
|
|
||||||
break |
|
||||||
|
|
||||||
case "getPeerCount": |
|
||||||
postData(data._seed, eth.peerCount()) |
|
||||||
|
|
||||||
break |
|
||||||
|
|
||||||
case "getTxCountAt": |
|
||||||
require(1) |
|
||||||
postData(data._seed, eth.txCountAt(data.args[0])) |
|
||||||
|
|
||||||
break |
|
||||||
|
|
||||||
case "getBlockByNumber": |
|
||||||
var block = eth.blockByNumber(data.args[0]) |
|
||||||
postData(data._seed, block) |
|
||||||
|
|
||||||
break |
|
||||||
|
|
||||||
case "getBlockByHash": |
|
||||||
var block = eth.blockByHash(data.args[0]) |
|
||||||
postData(data._seed, block) |
|
||||||
|
|
||||||
break |
|
||||||
|
|
||||||
case "transact": |
|
||||||
require(5) |
|
||||||
|
|
||||||
var tx = eth.transact(data.args[0], data.args[1], data.args[2],data.args[3],data.args[4],data.args[5]) |
|
||||||
postData(data._seed, tx) |
|
||||||
|
|
||||||
break |
|
||||||
|
|
||||||
case "getStorage": |
|
||||||
require(2); |
|
||||||
|
|
||||||
var stateObject = eth.stateObject(data.args[0]) |
|
||||||
var storage = stateObject.storageAt(data.args[1]) |
|
||||||
postData(data._seed, storage) |
|
||||||
|
|
||||||
break |
|
||||||
|
|
||||||
case "getEachStorage": |
|
||||||
require(1); |
|
||||||
var storage = JSON.parse(eth.eachStorage(data.args[0])) |
|
||||||
postData(data._seed, storage) |
|
||||||
|
|
||||||
break |
|
||||||
|
|
||||||
case "getTransactionsFor": |
|
||||||
require(1); |
|
||||||
var txs = eth.transactionsFor(data.args[0], true) |
|
||||||
postData(data._seed, txs) |
|
||||||
|
|
||||||
break |
|
||||||
|
|
||||||
case "getBalance": |
|
||||||
require(1); |
|
||||||
|
|
||||||
postData(data._seed, eth.stateObject(data.args[0]).value()); |
|
||||||
|
|
||||||
break |
|
||||||
|
|
||||||
case "getKey": |
|
||||||
var key = eth.key().privateKey; |
|
||||||
|
|
||||||
postData(data._seed, key) |
|
||||||
break |
|
||||||
|
|
||||||
/* |
|
||||||
case "watch": |
|
||||||
require(1) |
|
||||||
eth.watch(data.args[0], data.args[1]); |
|
||||||
|
|
||||||
break |
|
||||||
*/ |
|
||||||
case "watch": |
|
||||||
require(2) |
|
||||||
eth.watch(data.args[0], data.args[1]) |
|
||||||
|
|
||||||
case "disconnect": |
|
||||||
require(1) |
|
||||||
postData(data._seed, null) |
|
||||||
|
|
||||||
break; |
|
||||||
|
|
||||||
case "getSecretToAddress": |
|
||||||
require(1) |
|
||||||
postData(data._seed, eth.secretToAddress(data.args[0])) |
|
||||||
|
|
||||||
break; |
|
||||||
|
|
||||||
case "messages": |
|
||||||
require(1); |
|
||||||
|
|
||||||
var messages = JSON.parse(eth.getMessages(data.args[0])) |
|
||||||
postData(data._seed, messages) |
|
||||||
|
|
||||||
break |
|
||||||
|
|
||||||
case "mutan": |
|
||||||
require(1) |
|
||||||
|
|
||||||
var code = eth.compileMutan(data.args[0]) |
|
||||||
postData(data._seed, "0x"+code) |
|
||||||
|
|
||||||
break; |
|
||||||
} |
|
||||||
} catch(e) { |
|
||||||
console.log(data.call + ": " + e) |
|
||||||
|
|
||||||
postData(data._seed, null); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
function post(seed, data) { |
|
||||||
console.log("data", data) |
|
||||||
postData(data._seed, data) |
|
||||||
} |
|
||||||
|
|
||||||
function require(args, num) { |
|
||||||
if(args.length < num) { |
|
||||||
throw("required argument count of "+num+" got "+args.length); |
|
||||||
} |
|
||||||
} |
|
||||||
function postData(seed, data) { |
|
||||||
webview.experimental.postMessage(JSON.stringify({data: data, _seed: seed})) |
|
||||||
} |
|
||||||
function postEvent(event, data) { |
|
||||||
webview.experimental.postMessage(JSON.stringify({data: data, _event: event})) |
|
||||||
} |
|
||||||
|
|
||||||
function onWatchedCb(data, id) { |
|
||||||
var messages = JSON.parse(data) |
|
||||||
postEvent("watched:"+id, messages) |
|
||||||
} |
|
||||||
|
|
||||||
function onNewBlockCb(block) { |
|
||||||
postEvent("block:new", block) |
|
||||||
} |
|
||||||
function onObjectChangeCb(stateObject) { |
|
||||||
postEvent("object:"+stateObject.address(), stateObject) |
|
||||||
} |
|
||||||
function onStorageChangeCb(storageObject) { |
|
||||||
var ev = ["storage", storageObject.stateAddress, storageObject.address].join(":"); |
|
||||||
postEvent(ev, [storageObject.address, storageObject.value]) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
Rectangle { |
|
||||||
id: sizeGrip |
|
||||||
color: "gray" |
|
||||||
visible: false |
|
||||||
height: 10 |
|
||||||
anchors { |
|
||||||
left: root.left |
|
||||||
right: root.right |
|
||||||
} |
|
||||||
y: Math.round(root.height * 2 / 3) |
|
||||||
|
|
||||||
MouseArea { |
|
||||||
anchors.fill: parent |
|
||||||
drag.target: sizeGrip |
|
||||||
drag.minimumY: 0 |
|
||||||
drag.maximumY: root.height |
|
||||||
drag.axis: Drag.YAxis |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
WebView { |
|
||||||
id: inspector |
|
||||||
visible: false |
|
||||||
anchors { |
|
||||||
left: root.left |
|
||||||
right: root.right |
|
||||||
top: sizeGrip.bottom |
|
||||||
bottom: root.bottom |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
states: [ |
|
||||||
State { |
|
||||||
name: "inspectorShown" |
|
||||||
PropertyChanges { |
|
||||||
target: inspector |
|
||||||
} |
|
||||||
} |
|
||||||
] |
|
||||||
} |
|
||||||
} |
|
Before Width: | Height: | Size: 1004 B After Width: | Height: | Size: 1004 B |
After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 905 B After Width: | Height: | Size: 905 B |
@ -0,0 +1,435 @@ |
|||||||
|
import QtQuick 2.0 |
||||||
|
import QtQuick.Controls 1.0; |
||||||
|
import QtQuick.Layouts 1.0; |
||||||
|
import QtQuick.Dialogs 1.0; |
||||||
|
import QtQuick.Window 2.1; |
||||||
|
import QtQuick.Controls.Styles 1.1 |
||||||
|
import Ethereum 1.0 |
||||||
|
|
||||||
|
ApplicationWindow { |
||||||
|
id: win |
||||||
|
visible: false |
||||||
|
title: "IceCREAM" |
||||||
|
minimumWidth: 1280 |
||||||
|
minimumHeight: 700 |
||||||
|
width: 1290 |
||||||
|
height: 750 |
||||||
|
|
||||||
|
property alias codeText: codeEditor.text |
||||||
|
property alias dataText: rawDataField.text |
||||||
|
|
||||||
|
onClosing: { |
||||||
|
dbg.Stop() |
||||||
|
} |
||||||
|
|
||||||
|
menuBar: MenuBar { |
||||||
|
Menu { |
||||||
|
title: "Edit" |
||||||
|
MenuItem { |
||||||
|
text: "Focus code" |
||||||
|
shortcut: "Ctrl+1" |
||||||
|
onTriggered: { |
||||||
|
codeEditor.focus = true |
||||||
|
} |
||||||
|
} |
||||||
|
MenuItem { |
||||||
|
text: "Focus data" |
||||||
|
shortcut: "Ctrl+2" |
||||||
|
onTriggered: { |
||||||
|
rawDataField.focus = true |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
MenuItem { |
||||||
|
text: "Command" |
||||||
|
shortcut: "Ctrl+l" |
||||||
|
onTriggered: { |
||||||
|
dbgCommand.focus = true |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Menu { |
||||||
|
title: "Debugger" |
||||||
|
MenuItem { |
||||||
|
text: "Run" |
||||||
|
shortcut: "Ctrl+r" |
||||||
|
onTriggered: debugCurrent() |
||||||
|
} |
||||||
|
|
||||||
|
MenuItem { |
||||||
|
text: "Stop" |
||||||
|
onTriggered: dbp.stop() |
||||||
|
} |
||||||
|
|
||||||
|
MenuSeparator {} |
||||||
|
|
||||||
|
MenuItem { |
||||||
|
text: "Next" |
||||||
|
shortcut: "Ctrl+n" |
||||||
|
onTriggered: dbg.next() |
||||||
|
} |
||||||
|
|
||||||
|
MenuItem { |
||||||
|
text: "Continue" |
||||||
|
shortcut: "Ctrl+g" |
||||||
|
onTriggered: dbg.continue() |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
SplitView { |
||||||
|
anchors.fill: parent |
||||||
|
property var asmModel: ListModel { |
||||||
|
id: asmModel |
||||||
|
} |
||||||
|
|
||||||
|
TableView { |
||||||
|
id: asmTableView |
||||||
|
width: 200 |
||||||
|
headerVisible: false |
||||||
|
TableViewColumn{ role: "value" ; title: "" ; width: asmTableView.width - 2 } |
||||||
|
model: asmModel |
||||||
|
/* |
||||||
|
alternatingRowColors: false |
||||||
|
itemDelegate: Item { |
||||||
|
Rectangle { |
||||||
|
anchors.fill: parent |
||||||
|
color: "#DDD" |
||||||
|
Text { |
||||||
|
anchors { |
||||||
|
left: parent.left |
||||||
|
right: parent.right |
||||||
|
leftMargin: 10 |
||||||
|
verticalCenter: parent.verticalCenter |
||||||
|
} |
||||||
|
color: "#333" |
||||||
|
elide: styleData.elideMode |
||||||
|
text: styleData.value |
||||||
|
font.pixelSize: 11 |
||||||
|
MouseArea { |
||||||
|
acceptedButtons: Qt.LeftButton |
||||||
|
anchors.fill: parent |
||||||
|
onClicked: { |
||||||
|
mouse.accepted = true |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
*/ |
||||||
|
} |
||||||
|
|
||||||
|
Rectangle { |
||||||
|
color: "#00000000" |
||||||
|
anchors.left: asmTableView.right |
||||||
|
anchors.right: parent.right |
||||||
|
SplitView { |
||||||
|
orientation: Qt.Vertical |
||||||
|
anchors.fill: parent |
||||||
|
|
||||||
|
Rectangle { |
||||||
|
color: "#00000000" |
||||||
|
height: 330 |
||||||
|
anchors.left: parent.left |
||||||
|
anchors.right: parent.right |
||||||
|
|
||||||
|
TextArea { |
||||||
|
id: codeEditor |
||||||
|
anchors.top: parent.top |
||||||
|
anchors.bottom: parent.bottom |
||||||
|
anchors.left: parent.left |
||||||
|
anchors.right: settings.left |
||||||
|
focus: true |
||||||
|
|
||||||
|
/* |
||||||
|
Timer { |
||||||
|
id: compileTimer |
||||||
|
interval: 500 ; running: true ; repeat: true |
||||||
|
onTriggered: { |
||||||
|
dbg.autoComp(codeEditor.text) |
||||||
|
} |
||||||
|
} |
||||||
|
*/ |
||||||
|
} |
||||||
|
|
||||||
|
Column { |
||||||
|
id: settings |
||||||
|
spacing: 5 |
||||||
|
width: 300 |
||||||
|
height: parent.height |
||||||
|
anchors.right: parent.right |
||||||
|
anchors.top: parent.top |
||||||
|
anchors.bottom: parent.bottom |
||||||
|
|
||||||
|
Label { |
||||||
|
text: "Arbitrary data" |
||||||
|
} |
||||||
|
TextArea { |
||||||
|
id: rawDataField |
||||||
|
anchors.left: parent.left |
||||||
|
anchors.right: parent.right |
||||||
|
height: 150 |
||||||
|
} |
||||||
|
|
||||||
|
Label { |
||||||
|
text: "Amount" |
||||||
|
} |
||||||
|
TextField { |
||||||
|
id: txValue |
||||||
|
width: 200 |
||||||
|
placeholderText: "Amount" |
||||||
|
validator: RegExpValidator { regExp: /\d*/ } |
||||||
|
} |
||||||
|
Label { |
||||||
|
text: "Amount of gas" |
||||||
|
} |
||||||
|
TextField { |
||||||
|
id: txGas |
||||||
|
width: 200 |
||||||
|
validator: RegExpValidator { regExp: /\d*/ } |
||||||
|
text: "10000" |
||||||
|
placeholderText: "Gas" |
||||||
|
} |
||||||
|
Label { |
||||||
|
text: "Gas price" |
||||||
|
} |
||||||
|
TextField { |
||||||
|
id: txGasPrice |
||||||
|
width: 200 |
||||||
|
placeholderText: "Gas price" |
||||||
|
text: "1000000000000" |
||||||
|
validator: RegExpValidator { regExp: /\d*/ } |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
SplitView { |
||||||
|
orientation: Qt.Vertical |
||||||
|
id: inspectorPane |
||||||
|
height: 500 |
||||||
|
|
||||||
|
SplitView { |
||||||
|
orientation: Qt.Horizontal |
||||||
|
height: 150 |
||||||
|
|
||||||
|
TableView { |
||||||
|
id: stackTableView |
||||||
|
property var stackModel: ListModel { |
||||||
|
id: stackModel |
||||||
|
} |
||||||
|
height: parent.height |
||||||
|
width: 300 |
||||||
|
TableViewColumn{ role: "value" ; title: "Local VM stack" ; width: stackTableView.width - 2 } |
||||||
|
model: stackModel |
||||||
|
} |
||||||
|
|
||||||
|
TableView { |
||||||
|
id: memoryTableView |
||||||
|
property var memModel: ListModel { |
||||||
|
id: memModel |
||||||
|
} |
||||||
|
height: parent.height |
||||||
|
width: parent.width - stackTableView.width |
||||||
|
TableViewColumn{ id:mnumColmn ; role: "num" ; title: "#" ; width: 50 } |
||||||
|
TableViewColumn{ role: "value" ; title: "Memory" ; width: 650 } |
||||||
|
model: memModel |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Rectangle { |
||||||
|
height: 100 |
||||||
|
width: parent.width |
||||||
|
TableView { |
||||||
|
id: storageTableView |
||||||
|
property var memModel: ListModel { |
||||||
|
id: storageModel |
||||||
|
} |
||||||
|
height: parent.height |
||||||
|
width: parent.width |
||||||
|
TableViewColumn{ id: key ; role: "key" ; title: "#" ; width: storageTableView.width / 2 - 1} |
||||||
|
TableViewColumn{ role: "value" ; title: "Storage" ; width: storageTableView.width / 2 - 1} |
||||||
|
model: storageModel |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Rectangle { |
||||||
|
height: 200 |
||||||
|
width: parent.width * 0.66 |
||||||
|
TableView { |
||||||
|
id: logTableView |
||||||
|
property var logModel: ListModel { |
||||||
|
id: logModel |
||||||
|
} |
||||||
|
height: parent.height |
||||||
|
width: parent.width |
||||||
|
TableViewColumn{ id: message ; role: "message" ; title: "log" ; width: logTableView.width - 2 } |
||||||
|
model: logModel |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function exec() { |
||||||
|
dbg.execCommand(dbgCommand.text); |
||||||
|
dbgCommand.text = ""; |
||||||
|
} |
||||||
|
statusBar: StatusBar { |
||||||
|
height: 30 |
||||||
|
|
||||||
|
|
||||||
|
TextField { |
||||||
|
id: dbgCommand |
||||||
|
y: 1 |
||||||
|
x: asmTableView.width |
||||||
|
width: 500 |
||||||
|
placeholderText: "Debugger (type 'help')" |
||||||
|
Keys.onReturnPressed: { |
||||||
|
exec() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
RowLayout { |
||||||
|
anchors.left: dbgCommand.right |
||||||
|
anchors.leftMargin: 10 |
||||||
|
spacing: 5 |
||||||
|
y: parent.height / 2 - this.height / 2 |
||||||
|
|
||||||
|
Text { |
||||||
|
objectName: "stackFrame" |
||||||
|
font.pixelSize: 10 |
||||||
|
text: "<b>stack ptr</b>: 0" |
||||||
|
} |
||||||
|
|
||||||
|
Text { |
||||||
|
objectName: "stackSize" |
||||||
|
font.pixelSize: 10 |
||||||
|
text: "<b>stack size</b>: 0" |
||||||
|
} |
||||||
|
|
||||||
|
Text { |
||||||
|
objectName: "memSize" |
||||||
|
font.pixelSize: 10 |
||||||
|
text: "<b>mem size</b>: 0" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
toolBar: ToolBar { |
||||||
|
height: 30 |
||||||
|
RowLayout { |
||||||
|
spacing: 10 |
||||||
|
|
||||||
|
Button { |
||||||
|
property var enabled: true |
||||||
|
id: debugStart |
||||||
|
onClicked: { |
||||||
|
debugCurrent() |
||||||
|
} |
||||||
|
text: "Debug" |
||||||
|
} |
||||||
|
|
||||||
|
Button { |
||||||
|
property var enabled: true |
||||||
|
id: debugNextButton |
||||||
|
onClicked: { |
||||||
|
dbg.next() |
||||||
|
} |
||||||
|
text: "Next" |
||||||
|
} |
||||||
|
|
||||||
|
Button { |
||||||
|
id: debugContinueButton |
||||||
|
onClicked: { |
||||||
|
dbg.continue() |
||||||
|
} |
||||||
|
text: "Continue" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
ComboBox { |
||||||
|
id: snippets |
||||||
|
anchors.right: parent.right |
||||||
|
model: ListModel { |
||||||
|
ListElement { text: "Snippets" ; value: "" } |
||||||
|
ListElement { text: "Call Contract" ; value: "var[2] in;\nvar ret;\n\nin[0] = \"arg1\"\nin[1] = 0xdeadbeef\n\nvar success = call(0x0c542ddea93dae0c2fcb2cf175f03ad80d6be9a0, 0, 7000, in, ret)\n\nreturn ret" } |
||||||
|
} |
||||||
|
onCurrentIndexChanged: { |
||||||
|
if(currentIndex != 0) { |
||||||
|
var code = snippets.model.get(currentIndex).value; |
||||||
|
codeEditor.insert(codeEditor.cursorPosition, code) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
function debugCurrent() { |
||||||
|
dbg.debug(txValue.text, txGas.text, txGasPrice.text, codeEditor.text, rawDataField.text) |
||||||
|
} |
||||||
|
|
||||||
|
function setAsm(asm) { |
||||||
|
asmModel.append({asm: asm}) |
||||||
|
} |
||||||
|
|
||||||
|
function clearAsm() { |
||||||
|
asmModel.clear() |
||||||
|
} |
||||||
|
|
||||||
|
function setInstruction(num) { |
||||||
|
asmTableView.selection.clear() |
||||||
|
asmTableView.selection.select(num) |
||||||
|
asmTableView.positionViewAtRow(num, ListView.Center) |
||||||
|
} |
||||||
|
|
||||||
|
function setMem(mem) { |
||||||
|
memModel.append({num: mem.num, value: mem.value}) |
||||||
|
} |
||||||
|
function clearMem(){ |
||||||
|
memModel.clear() |
||||||
|
} |
||||||
|
|
||||||
|
function setStack(stack) { |
||||||
|
stackModel.append({value: stack}) |
||||||
|
} |
||||||
|
function addDebugMessage(message){ |
||||||
|
debuggerLog.append({value: message}) |
||||||
|
} |
||||||
|
|
||||||
|
function clearStack() { |
||||||
|
stackModel.clear() |
||||||
|
} |
||||||
|
|
||||||
|
function clearStorage() { |
||||||
|
storageModel.clear() |
||||||
|
} |
||||||
|
|
||||||
|
function setStorage(storage) { |
||||||
|
storageModel.append({key: storage.key, value: storage.value}) |
||||||
|
} |
||||||
|
|
||||||
|
function setLog(msg) { |
||||||
|
// Remove first item once we've reached max log items |
||||||
|
if(logModel.count > 250) { |
||||||
|
logModel.remove(0) |
||||||
|
} |
||||||
|
|
||||||
|
if(msg.len != 0) { |
||||||
|
if(logTableView.flickableItem.atYEnd) { |
||||||
|
logModel.append({message: msg}) |
||||||
|
logTableView.positionViewAtRow(logTableView.rowCount - 1, ListView.Contain) |
||||||
|
} else { |
||||||
|
logModel.append({message: msg}) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function clearLog() { |
||||||
|
logModel.clear() |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,49 @@ |
|||||||
|
var ethx = { |
||||||
|
prototype: Object, |
||||||
|
|
||||||
|
watch: function(options) { |
||||||
|
return new Filter(options); |
||||||
|
}, |
||||||
|
|
||||||
|
note: function() { |
||||||
|
var args = Array.prototype.slice.call(arguments, 0); |
||||||
|
var o = [] |
||||||
|
for(var i = 0; i < args.length; i++) { |
||||||
|
o.push(args[i].toString()) |
||||||
|
} |
||||||
|
|
||||||
|
eth.notef(o); |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
var Filter = function(options) { |
||||||
|
this.callbacks = []; |
||||||
|
this.options = options; |
||||||
|
|
||||||
|
if(options === "chain") { |
||||||
|
this.id = eth.newFilterString(options); |
||||||
|
} else if(typeof options === "object") { |
||||||
|
this.id = eth.newFilter(options); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
Filter.prototype.changed = function(callback) { |
||||||
|
this.callbacks.push(callback); |
||||||
|
|
||||||
|
var self = this; |
||||||
|
messages.connect(function(messages, id) { |
||||||
|
if(id == self.id) { |
||||||
|
for(var i = 0; i < self.callbacks.length; i++) { |
||||||
|
self.callbacks[i].call(self, messages); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
Filter.prototype.uninstall = function() { |
||||||
|
eth.uninstallFilter(this.id) |
||||||
|
} |
||||||
|
|
||||||
|
Filter.prototype.messages = function() { |
||||||
|
return eth.messages(this.id) |
||||||
|
} |
@ -0,0 +1,481 @@ |
|||||||
|
// The magic return variable. The magic return variable will be set during the execution of the QML call.
|
||||||
|
(function(window) { |
||||||
|
function message(type, data) { |
||||||
|
document.title = JSON.stringify({type: type, data: data}); |
||||||
|
|
||||||
|
return window.____returnData; |
||||||
|
} |
||||||
|
|
||||||
|
function isPromise(o) { |
||||||
|
return typeof o === "object" && o.then |
||||||
|
} |
||||||
|
|
||||||
|
window.eth = { |
||||||
|
_callbacks: {}, |
||||||
|
_events: {}, |
||||||
|
prototype: Object(), |
||||||
|
|
||||||
|
toHex: function(str) { |
||||||
|
var hex = ""; |
||||||
|
for(var i = 0; i < str.length; i++) { |
||||||
|
var n = str.charCodeAt(i).toString(16); |
||||||
|
hex += n.length < 2 ? '0' + n : n; |
||||||
|
} |
||||||
|
|
||||||
|
return hex; |
||||||
|
}, |
||||||
|
|
||||||
|
toAscii: function(hex) { |
||||||
|
// Find termination
|
||||||
|
var str = ""; |
||||||
|
var i = 0, l = hex.length; |
||||||
|
for(; i < l; i+=2) { |
||||||
|
var code = hex.charCodeAt(i) |
||||||
|
if(code == 0) { |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); |
||||||
|
} |
||||||
|
|
||||||
|
return str; |
||||||
|
}, |
||||||
|
|
||||||
|
fromAscii: function(str, pad) { |
||||||
|
if(pad === undefined) { |
||||||
|
pad = 32 |
||||||
|
} |
||||||
|
|
||||||
|
var hex = this.toHex(str); |
||||||
|
|
||||||
|
while(hex.length < pad*2) |
||||||
|
hex += "00"; |
||||||
|
|
||||||
|
return hex |
||||||
|
}, |
||||||
|
|
||||||
|
block: function(numberOrHash) { |
||||||
|
return new Promise(function(resolve, reject) { |
||||||
|
var func; |
||||||
|
if(typeof numberOrHash == "string") { |
||||||
|
func = "getBlockByHash"; |
||||||
|
} else { |
||||||
|
func = "getBlockByNumber"; |
||||||
|
} |
||||||
|
|
||||||
|
postData({call: func, args: [numberOrHash]}, function(block) { |
||||||
|
if(block) |
||||||
|
resolve(block); |
||||||
|
else |
||||||
|
reject("not found"); |
||||||
|
|
||||||
|
}); |
||||||
|
}); |
||||||
|
}, |
||||||
|
|
||||||
|
transact: function(params) { |
||||||
|
if(params === undefined) { |
||||||
|
params = {}; |
||||||
|
} |
||||||
|
|
||||||
|
if(params.endowment !== undefined) |
||||||
|
params.value = params.endowment; |
||||||
|
if(params.code !== undefined) |
||||||
|
params.data = params.code; |
||||||
|
|
||||||
|
|
||||||
|
var promises = [] |
||||||
|
if(isPromise(params.to)) { |
||||||
|
promises.push(params.to.then(function(_to) { params.to = _to; })); |
||||||
|
} |
||||||
|
if(isPromise(params.from)) { |
||||||
|
promises.push(params.from.then(function(_from) { params.from = _from; })); |
||||||
|
} |
||||||
|
|
||||||
|
if(isPromise(params.data)) { |
||||||
|
promises.push(params.data.then(function(_code) { params.data = _code; })); |
||||||
|
} else { |
||||||
|
if(typeof params.data === "object") { |
||||||
|
data = ""; |
||||||
|
for(var i = 0; i < params.data.length; i++) { |
||||||
|
data += params.data[i] |
||||||
|
} |
||||||
|
} else { |
||||||
|
data = params.data; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Make sure everything is string
|
||||||
|
var fields = ["value", "gas", "gasPrice"]; |
||||||
|
for(var i = 0; i < fields.length; i++) { |
||||||
|
if(params[fields[i]] === undefined) { |
||||||
|
params[fields[i]] = ""; |
||||||
|
} |
||||||
|
params[fields[i]] = params[fields[i]].toString(); |
||||||
|
} |
||||||
|
|
||||||
|
// Load promises then call the last "transact".
|
||||||
|
return Q.all(promises).then(function() { |
||||||
|
return new Promise(function(resolve, reject) { |
||||||
|
postData({call: "transact", args: params}, function(data) { |
||||||
|
if(data[1]) |
||||||
|
reject(data[0]); |
||||||
|
else |
||||||
|
resolve(data[0]); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}) |
||||||
|
}, |
||||||
|
|
||||||
|
compile: function(code) { |
||||||
|
return new Promise(function(resolve, reject) { |
||||||
|
postData({call: "compile", args: [code]}, function(data) { |
||||||
|
if(data[1]) |
||||||
|
reject(data[0]); |
||||||
|
else |
||||||
|
resolve(data[0]); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}, |
||||||
|
|
||||||
|
balanceAt: function(address) { |
||||||
|
var promises = []; |
||||||
|
|
||||||
|
if(isPromise(address)) { |
||||||
|
promises.push(address.then(function(_address) { address = _address; })); |
||||||
|
} |
||||||
|
|
||||||
|
return Q.all(promises).then(function() { |
||||||
|
return new Promise(function(resolve, reject) { |
||||||
|
postData({call: "getBalanceAt", args: [address]}, function(balance) { |
||||||
|
resolve(balance); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}, |
||||||
|
|
||||||
|
countAt: function(address) { |
||||||
|
var promises = []; |
||||||
|
|
||||||
|
if(isPromise(address)) { |
||||||
|
promises.push(address.then(function(_address) { address = _address; })); |
||||||
|
} |
||||||
|
|
||||||
|
return Q.all(promises).then(function() { |
||||||
|
return new Promise(function(resolve, reject) { |
||||||
|
postData({call: "getCountAt", args: [address]}, function(count) { |
||||||
|
resolve(count); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}, |
||||||
|
|
||||||
|
codeAt: function(address) { |
||||||
|
var promises = []; |
||||||
|
|
||||||
|
if(isPromise(address)) { |
||||||
|
promises.push(address.then(function(_address) { address = _address; })); |
||||||
|
} |
||||||
|
|
||||||
|
return Q.all(promises).then(function() { |
||||||
|
return new Promise(function(resolve, reject) { |
||||||
|
postData({call: "getCodeAt", args: [address]}, function(code) { |
||||||
|
resolve(code); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}, |
||||||
|
|
||||||
|
storageAt: function(address, storageAddress) { |
||||||
|
var promises = []; |
||||||
|
|
||||||
|
if(isPromise(address)) { |
||||||
|
promises.push(address.then(function(_address) { address = _address; })); |
||||||
|
} |
||||||
|
|
||||||
|
if(isPromise(storageAddress)) { |
||||||
|
promises.push(storageAddress.then(function(_sa) { storageAddress = _sa; })); |
||||||
|
} |
||||||
|
|
||||||
|
return Q.all(promises).then(function() { |
||||||
|
return new Promise(function(resolve, reject) { |
||||||
|
postData({call: "getStorageAt", args: [address, storageAddress]}, function(entry) { |
||||||
|
resolve(entry); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}, |
||||||
|
|
||||||
|
stateAt: function(address, storageAddress) { |
||||||
|
return this.storageAt(address, storageAddress); |
||||||
|
}, |
||||||
|
|
||||||
|
call: function(params) { |
||||||
|
if(params === undefined) { |
||||||
|
params = {}; |
||||||
|
} |
||||||
|
|
||||||
|
if(params.endowment !== undefined) |
||||||
|
params.value = params.endowment; |
||||||
|
if(params.code !== undefined) |
||||||
|
params.data = params.code; |
||||||
|
|
||||||
|
|
||||||
|
var promises = [] |
||||||
|
if(isPromise(params.to)) { |
||||||
|
promises.push(params.to.then(function(_to) { params.to = _to; })); |
||||||
|
} |
||||||
|
if(isPromise(params.from)) { |
||||||
|
promises.push(params.from.then(function(_from) { params.from = _from; })); |
||||||
|
} |
||||||
|
|
||||||
|
if(isPromise(params.data)) { |
||||||
|
promises.push(params.data.then(function(_code) { params.data = _code; })); |
||||||
|
} else { |
||||||
|
if(typeof params.data === "object") { |
||||||
|
data = ""; |
||||||
|
for(var i = 0; i < params.data.length; i++) { |
||||||
|
data += params.data[i] |
||||||
|
} |
||||||
|
} else { |
||||||
|
data = params.data; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Make sure everything is string
|
||||||
|
var fields = ["value", "gas", "gasPrice"]; |
||||||
|
for(var i = 0; i < fields.length; i++) { |
||||||
|
if(params[fields[i]] === undefined) { |
||||||
|
params[fields[i]] = ""; |
||||||
|
} |
||||||
|
params[fields[i]] = params[fields[i]].toString(); |
||||||
|
} |
||||||
|
|
||||||
|
// Load promises then call the last "transact".
|
||||||
|
return Q.all(promises).then(function() { |
||||||
|
return new Promise(function(resolve, reject) { |
||||||
|
postData({call: "call", args: params}, function(data) { |
||||||
|
if(data[1]) |
||||||
|
reject(data[0]); |
||||||
|
else |
||||||
|
resolve(data[0]); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}) |
||||||
|
}, |
||||||
|
|
||||||
|
watch: function(params) { |
||||||
|
return new Filter(params); |
||||||
|
}, |
||||||
|
|
||||||
|
secretToAddress: function(key) { |
||||||
|
var promises = []; |
||||||
|
if(isPromise(key)) { |
||||||
|
promises.push(key.then(function(_key) { key = _key; })); |
||||||
|
} |
||||||
|
|
||||||
|
return Q.all(promises).then(function() { |
||||||
|
return new Promise(function(resolve, reject) { |
||||||
|
postData({call: "getSecretToAddress", args: [key]}, function(address) { |
||||||
|
resolve(address); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}, |
||||||
|
|
||||||
|
on: function(event, cb) { |
||||||
|
if(eth._events[event] === undefined) { |
||||||
|
eth._events[event] = []; |
||||||
|
} |
||||||
|
|
||||||
|
eth._events[event].push(cb); |
||||||
|
|
||||||
|
return this |
||||||
|
}, |
||||||
|
|
||||||
|
off: function(event, cb) { |
||||||
|
if(eth._events[event] !== undefined) { |
||||||
|
var callbacks = eth._events[event]; |
||||||
|
for(var i = 0; i < callbacks.length; i++) { |
||||||
|
if(callbacks[i] === cb) { |
||||||
|
delete callbacks[i]; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return this |
||||||
|
}, |
||||||
|
|
||||||
|
trigger: function(event, data) { |
||||||
|
var callbacks = eth._events[event]; |
||||||
|
if(callbacks !== undefined) { |
||||||
|
for(var i = 0; i < callbacks.length; i++) { |
||||||
|
// Figure out whether the returned data was an array
|
||||||
|
// array means multiple return arguments (multiple params)
|
||||||
|
if(data instanceof Array) { |
||||||
|
callbacks[i].apply(this, data); |
||||||
|
} else { |
||||||
|
callbacks[i].call(this, data); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
// Eth object properties
|
||||||
|
Object.defineProperty(eth, "key", { |
||||||
|
get: function() { |
||||||
|
return new Promise(function(resolve, reject) { |
||||||
|
postData({call: "getKey"}, function(k) { |
||||||
|
resolve(k); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}, |
||||||
|
}); |
||||||
|
|
||||||
|
Object.defineProperty(eth, "gasPrice", { |
||||||
|
get: function() { |
||||||
|
return "1000000000000" |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
Object.defineProperty(eth, "coinbase", { |
||||||
|
get: function() { |
||||||
|
return new Promise(function(resolve, reject) { |
||||||
|
postData({call: "getCoinBase"}, function(coinbase) { |
||||||
|
resolve(coinbase); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}, |
||||||
|
}); |
||||||
|
|
||||||
|
Object.defineProperty(eth, "listening", { |
||||||
|
get: function() { |
||||||
|
return new Promise(function(resolve, reject) { |
||||||
|
postData({call: "getIsListening"}, function(listening) { |
||||||
|
resolve(listening); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}, |
||||||
|
}); |
||||||
|
|
||||||
|
|
||||||
|
Object.defineProperty(eth, "mining", { |
||||||
|
get: function() { |
||||||
|
return new Promise(function(resolve, reject) { |
||||||
|
postData({call: "getIsMining"}, function(mining) { |
||||||
|
resolve(mining); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}, |
||||||
|
}); |
||||||
|
|
||||||
|
Object.defineProperty(eth, "peerCount", { |
||||||
|
get: function() { |
||||||
|
return new Promise(function(resolve, reject) { |
||||||
|
postData({call: "getPeerCount"}, function(peerCount) { |
||||||
|
resolve(peerCount); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}, |
||||||
|
}); |
||||||
|
|
||||||
|
var filters = []; |
||||||
|
var Filter = function(options) { |
||||||
|
filters.push(this); |
||||||
|
|
||||||
|
this.callbacks = []; |
||||||
|
this.options = options; |
||||||
|
|
||||||
|
var call; |
||||||
|
if(options === "chain") { |
||||||
|
call = "newFilterString" |
||||||
|
} else if(typeof options === "object") { |
||||||
|
call = "newFilter" |
||||||
|
} |
||||||
|
|
||||||
|
var self = this; // Cheaper than binding
|
||||||
|
this.promise = new Promise(function(resolve, reject) { |
||||||
|
postData({call: call, args: [options]}, function(id) { |
||||||
|
self.id = id; |
||||||
|
|
||||||
|
resolve(id); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
Filter.prototype.changed = function(callback) { |
||||||
|
var self = this; |
||||||
|
this.promise.then(function(id) { |
||||||
|
self.callbacks.push(callback); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
Filter.prototype.trigger = function(messages, id) { |
||||||
|
if(id == this.id) { |
||||||
|
for(var i = 0; i < this.callbacks.length; i++) { |
||||||
|
this.callbacks[i].call(this, messages); |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
Filter.prototype.uninstall = function() { |
||||||
|
this.promise.then(function(id) { |
||||||
|
postData({call: "uninstallFilter", args:[id]}); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
Filter.prototype.messages = function() { |
||||||
|
var self=this; |
||||||
|
return Q.all([this.promise]).then(function() { |
||||||
|
var id = self.id |
||||||
|
return new Promise(function(resolve, reject) { |
||||||
|
postData({call: "getMessages", args: [id]}, function(messages) { |
||||||
|
resolve(messages); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
// Register to the messages callback. "messages" will be emitted when new messages
|
||||||
|
// from the client have been created.
|
||||||
|
eth.on("messages", function(messages, id) { |
||||||
|
for(var i = 0; i < filters.length; i++) { |
||||||
|
filters[i].trigger(messages, id); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
var g_seed = 1; |
||||||
|
function postData(data, cb) { |
||||||
|
data._seed = g_seed; |
||||||
|
if(cb) { |
||||||
|
eth._callbacks[data._seed] = cb; |
||||||
|
} |
||||||
|
|
||||||
|
if(data.args === undefined) { |
||||||
|
data.args = []; |
||||||
|
} |
||||||
|
|
||||||
|
g_seed++; |
||||||
|
|
||||||
|
navigator.qt.postMessage(JSON.stringify(data)); |
||||||
|
} |
||||||
|
|
||||||
|
navigator.qt.onmessage = function(ev) { |
||||||
|
var data = JSON.parse(ev.data) |
||||||
|
|
||||||
|
if(data._event !== undefined) { |
||||||
|
eth.trigger(data._event, data.data); |
||||||
|
} else { |
||||||
|
if(data._seed) { |
||||||
|
var cb = eth._callbacks[data._seed]; |
||||||
|
if(cb) { |
||||||
|
cb.call(this, data.data) |
||||||
|
|
||||||
|
// Remove the "trigger" callback
|
||||||
|
delete eth._callbacks[ev._seed]; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
})(this); |
@ -0,0 +1,13 @@ |
|||||||
|
// this function is included locally, but you can also include separately via a header definition
|
||||||
|
function request(url, callback) { |
||||||
|
var xhr = new XMLHttpRequest(); |
||||||
|
xhr.onreadystatechange = (function(req) { |
||||||
|
return function() { |
||||||
|
if(req.readyState === 4) { |
||||||
|
callback(req); |
||||||
|
} |
||||||
|
} |
||||||
|
})(xhr); |
||||||
|
xhr.open('GET', url, true); |
||||||
|
xhr.send(''); |
||||||
|
} |
@ -0,0 +1,3 @@ |
|||||||
|
if(typeof(Promise) === "undefined") { |
||||||
|
window.Promise = Q.Promise; |
||||||
|
} |
@ -0,0 +1,13 @@ |
|||||||
|
function HandleMessage(data) { |
||||||
|
var message; |
||||||
|
try { message = JSON.parse(data) } catch(e) {}; |
||||||
|
|
||||||
|
if(message) { |
||||||
|
switch(message.type) { |
||||||
|
case "coinbase": |
||||||
|
return eth.coinBase(); |
||||||
|
case "block": |
||||||
|
return eth.blockByNumber(0); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 932 B After Width: | Height: | Size: 932 B |
After Width: | Height: | Size: 82 KiB |
@ -0,0 +1,184 @@ |
|||||||
|
import QtQuick 2.0 |
||||||
|
import QtQuick.Controls 1.0; |
||||||
|
import QtQuick.Layouts 1.0; |
||||||
|
import QtQuick.Dialogs 1.0; |
||||||
|
import QtQuick.Window 2.1; |
||||||
|
import QtQuick.Controls.Styles 1.1 |
||||||
|
|
||||||
|
Rectangle { |
||||||
|
id: root |
||||||
|
property var title: "JeffCoin" |
||||||
|
property var iconSource: "./views/jeffcoin/jeff.png" |
||||||
|
property var menuItem |
||||||
|
property var filter |
||||||
|
property var address: "fc0a9436890478bb9b1c6ed7455c2535366f4a99" |
||||||
|
|
||||||
|
function insertTx(message, blockNumber) { |
||||||
|
if(!message) return; |
||||||
|
|
||||||
|
var from = message.from |
||||||
|
var to = message.input.substr(24, 40) |
||||||
|
var value = eth.fromNumber(message.input.substr(64, 64)) |
||||||
|
|
||||||
|
var me = eth.key().address; |
||||||
|
if((to == me|| from == me) && message.input.length == 128) { |
||||||
|
txModel.insert(0, {confirmations: blockNumber - message.number, from: from, to: to, value: value}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function setBalance() { |
||||||
|
var jeffCoinAmount = eth.fromNumber(eth.storageAt(address, eth.key().address)) + " JΞF" |
||||||
|
menuItem.secondaryTitle = jeffCoinAmount |
||||||
|
|
||||||
|
balance.text = "<b>Balance</b>: " + jeffCoinAmount; |
||||||
|
} |
||||||
|
|
||||||
|
function onReady() { |
||||||
|
setBalance() |
||||||
|
|
||||||
|
filter = new ethx.watch({latest: -1, to: address}) |
||||||
|
filter.changed(function(messages) { |
||||||
|
setBalance() |
||||||
|
|
||||||
|
var blockNumber = eth.block(-1).number; |
||||||
|
for(var i = 0; i < messages.length; i++) { |
||||||
|
insertTx(messages.get(i), blockNumber); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
var blockNumber = eth.block(-1).number; |
||||||
|
var messages = filter.messages() |
||||||
|
for(var i = messages.length-1; i >= 0; i--) { |
||||||
|
var message = messages.get(i) |
||||||
|
|
||||||
|
insertTx(message, blockNumber) |
||||||
|
} |
||||||
|
|
||||||
|
var chainChanged = ethx.watch("chain") |
||||||
|
chainChanged.changed(function() { |
||||||
|
for(var i = 0; i < txModel.count; i++) { |
||||||
|
var entry = txModel.get(i); |
||||||
|
entry.confirmations++; |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
function onDestroy() { |
||||||
|
filter.uninstall() |
||||||
|
} |
||||||
|
|
||||||
|
ColumnLayout { |
||||||
|
spacing: 10 |
||||||
|
y: 40 |
||||||
|
anchors.fill: parent |
||||||
|
|
||||||
|
Text { |
||||||
|
id: balance |
||||||
|
text: "<b>Balance</b>: " + eth.fromNumber(eth.storageAt(address, eth.key().address)) + " JΞF" |
||||||
|
font.pixelSize: 24 |
||||||
|
anchors { |
||||||
|
horizontalCenter: parent.horizontalCenter |
||||||
|
top: parent.top |
||||||
|
topMargin: 20 |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Rectangle { |
||||||
|
id: newTxPane |
||||||
|
color: "#ececec" |
||||||
|
border.color: "#cccccc" |
||||||
|
border.width: 1 |
||||||
|
anchors { |
||||||
|
top: balance.bottom |
||||||
|
topMargin: 10 |
||||||
|
left: parent.left |
||||||
|
leftMargin: 5 |
||||||
|
right: parent.right |
||||||
|
rightMargin: 5 |
||||||
|
} |
||||||
|
height: 100 |
||||||
|
|
||||||
|
RowLayout { |
||||||
|
id: amountFields |
||||||
|
spacing: 10 |
||||||
|
anchors { |
||||||
|
top: parent.top |
||||||
|
topMargin: 20 |
||||||
|
left: parent.left |
||||||
|
leftMargin: 20 |
||||||
|
} |
||||||
|
|
||||||
|
Text { |
||||||
|
text: "JΞF " |
||||||
|
} |
||||||
|
|
||||||
|
// There's something off with the row layout where textfields won't listen to the width setting |
||||||
|
Rectangle { |
||||||
|
width: 50 |
||||||
|
height: 20 |
||||||
|
TextField { |
||||||
|
id: txValue |
||||||
|
width: parent.width |
||||||
|
placeholderText: "0.00" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
RowLayout { |
||||||
|
id: toFields |
||||||
|
spacing: 10 |
||||||
|
anchors { |
||||||
|
top: amountFields.bottom |
||||||
|
topMargin: 5 |
||||||
|
left: parent.left |
||||||
|
leftMargin: 20 |
||||||
|
} |
||||||
|
|
||||||
|
Text { |
||||||
|
text: "To" |
||||||
|
} |
||||||
|
|
||||||
|
Rectangle { |
||||||
|
width: 200 |
||||||
|
height: 20 |
||||||
|
TextField { |
||||||
|
id: txTo |
||||||
|
width: parent.width |
||||||
|
placeholderText: "Address or name" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Button { |
||||||
|
text: "Send" |
||||||
|
onClicked: { |
||||||
|
eth.transact({from: eth.key().privateKey, to:address, gas: "9000", gasPrice: "100000000000", data: ["0x"+txTo.text, txValue.text]}) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Rectangle { |
||||||
|
anchors { |
||||||
|
left: parent.left |
||||||
|
right: parent.right |
||||||
|
top: newTxPane.bottom |
||||||
|
topMargin: 10 |
||||||
|
bottom: parent.bottom |
||||||
|
} |
||||||
|
TableView { |
||||||
|
id: txTableView |
||||||
|
anchors.fill : parent |
||||||
|
TableViewColumn{ role: "value" ; title: "Amount" ; width: 100 } |
||||||
|
TableViewColumn{ role: "from" ; title: "From" ; width: 280 } |
||||||
|
TableViewColumn{ role: "to" ; title: "To" ; width: 280 } |
||||||
|
TableViewColumn{ role: "confirmations" ; title: "Confirmations" ; width: 100 } |
||||||
|
|
||||||
|
model: ListModel { |
||||||
|
id: txModel |
||||||
|
Component.onCompleted: { |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,894 @@ |
|||||||
|
import QtQuick 2.0 |
||||||
|
import QtQuick.Controls 1.0; |
||||||
|
import QtQuick.Layouts 1.0; |
||||||
|
import QtQuick.Dialogs 1.0; |
||||||
|
import QtQuick.Window 2.1; |
||||||
|
import QtQuick.Controls.Styles 1.1 |
||||||
|
import Ethereum 1.0 |
||||||
|
|
||||||
|
import "../ext/filter.js" as Eth |
||||||
|
import "../ext/http.js" as Http |
||||||
|
|
||||||
|
ApplicationWindow { |
||||||
|
id: root |
||||||
|
|
||||||
|
property alias miningButtonText: miningButton.text |
||||||
|
property var ethx : Eth.ethx |
||||||
|
property var web |
||||||
|
|
||||||
|
width: 1200 |
||||||
|
height: 820 |
||||||
|
minimumHeight: 300 |
||||||
|
|
||||||
|
title: "Mist" |
||||||
|
|
||||||
|
// This signal is used by the filter API. The filter API connects using this signal handler from |
||||||
|
// the different QML files and plugins. |
||||||
|
signal messages(var messages, int id); |
||||||
|
function invokeFilterCallback(data, receiverSeed) { |
||||||
|
//var messages = JSON.parse(data) |
||||||
|
// Signal handler |
||||||
|
messages(data, receiverSeed); |
||||||
|
root.web.messages(data, receiverSeed); |
||||||
|
} |
||||||
|
|
||||||
|
TextField { |
||||||
|
id: copyElementHax |
||||||
|
visible: false |
||||||
|
} |
||||||
|
|
||||||
|
function copyToClipboard(text) { |
||||||
|
copyElementHax.text = text |
||||||
|
copyElementHax.selectAll() |
||||||
|
copyElementHax.copy() |
||||||
|
} |
||||||
|
|
||||||
|
// Takes care of loading all default plugins |
||||||
|
Component.onCompleted: { |
||||||
|
var wallet = addPlugin("./views/wallet.qml", {noAdd: true, close: false, section: "ethereum", active: true}); |
||||||
|
var browser = addPlugin("./webapp.qml", {noAdd: true, close: false, section: "ethereum", active: true}); |
||||||
|
root.web = browser.view; |
||||||
|
|
||||||
|
addPlugin("./views/transaction.qml", {noAdd: true, close: false, section: "legacy"}); |
||||||
|
addPlugin("./views/chain.qml", {noAdd: true, close: false, section: "legacy"}); |
||||||
|
addPlugin("./views/info.qml", {noAdd: true, close: false, section: "legacy"}); |
||||||
|
addPlugin("./views/pending_tx.qml", {noAdd: true, close: false, section: "legacy"}); |
||||||
|
addPlugin("./views/javascript.qml", {noAdd: true, close: false, section: "legacy"}); |
||||||
|
|
||||||
|
addPlugin("./views/jeffcoin/jeffcoin.qml", {noAdd: true, close: false, section: "apps"}) |
||||||
|
|
||||||
|
mainSplit.setView(wallet.view, wallet.menuItem); |
||||||
|
|
||||||
|
// Call the ready handler |
||||||
|
gui.done(); |
||||||
|
} |
||||||
|
|
||||||
|
function addViews(view, path, options) { |
||||||
|
var views = mainSplit.addComponent(view, options) |
||||||
|
views.menuItem.path = path |
||||||
|
|
||||||
|
mainSplit.views.push(views); |
||||||
|
|
||||||
|
if(!options.noAdd) { |
||||||
|
gui.addPlugin(path) |
||||||
|
} |
||||||
|
|
||||||
|
return views |
||||||
|
} |
||||||
|
|
||||||
|
function addPlugin(path, options) { |
||||||
|
try { |
||||||
|
if(typeof(path) === "string" && /^https?/.test(path)) { |
||||||
|
console.log('load http') |
||||||
|
Http.request(path, function(o) { |
||||||
|
if(o.status === 200) { |
||||||
|
var view = Qt.createQmlObject(o.responseText, mainView, path) |
||||||
|
addViews(view, path, options) |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
var component = Qt.createComponent(path); |
||||||
|
if(component.status != Component.Ready) { |
||||||
|
if(component.status == Component.Error) { |
||||||
|
ethx.note("error: ", component.errorString()); |
||||||
|
} |
||||||
|
|
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
var view = mainView.createView(component, options) |
||||||
|
var views = addViews(view, path, options) |
||||||
|
|
||||||
|
return views |
||||||
|
} catch(e) { |
||||||
|
ethx.note(e) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
menuBar: MenuBar { |
||||||
|
Menu { |
||||||
|
title: "File" |
||||||
|
MenuItem { |
||||||
|
text: "Import App" |
||||||
|
shortcut: "Ctrl+o" |
||||||
|
onTriggered: { |
||||||
|
generalFileDialog.show(true, importApp) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
MenuItem { |
||||||
|
text: "Browser" |
||||||
|
onTriggered: eth.openBrowser() |
||||||
|
} |
||||||
|
*/ |
||||||
|
|
||||||
|
MenuItem { |
||||||
|
text: "Add plugin" |
||||||
|
onTriggered: { |
||||||
|
generalFileDialog.show(true, function(path) { |
||||||
|
addPlugin(path, {close: true, section: "apps"}) |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
MenuSeparator {} |
||||||
|
|
||||||
|
MenuItem { |
||||||
|
text: "Import key" |
||||||
|
shortcut: "Ctrl+i" |
||||||
|
onTriggered: { |
||||||
|
generalFileDialog.show(true, function(path) { |
||||||
|
gui.importKey(path) |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
MenuItem { |
||||||
|
text: "Export keys" |
||||||
|
shortcut: "Ctrl+e" |
||||||
|
onTriggered: { |
||||||
|
generalFileDialog.show(false, function(path) { |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
Menu { |
||||||
|
title: "Developer" |
||||||
|
MenuItem { |
||||||
|
iconSource: "../icecream.png" |
||||||
|
text: "Debugger" |
||||||
|
shortcut: "Ctrl+d" |
||||||
|
onTriggered: eth.startDebugger() |
||||||
|
} |
||||||
|
|
||||||
|
MenuItem { |
||||||
|
text: "Import Tx" |
||||||
|
onTriggered: { |
||||||
|
txImportDialog.visible = true |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
MenuItem { |
||||||
|
text: "Run JS file" |
||||||
|
onTriggered: { |
||||||
|
generalFileDialog.show(true, function(path) { |
||||||
|
eth.evalJavascriptFile(path) |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
MenuItem { |
||||||
|
text: "Dump state" |
||||||
|
onTriggered: { |
||||||
|
generalFileDialog.show(false, function(path) { |
||||||
|
// Empty hash for latest |
||||||
|
gui.dumpState("", path) |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
MenuSeparator {} |
||||||
|
|
||||||
|
MenuItem { |
||||||
|
id: miningSpeed |
||||||
|
text: "Mining: Turbo" |
||||||
|
onTriggered: { |
||||||
|
gui.toggleTurboMining() |
||||||
|
if(text == "Mining: Turbo") { |
||||||
|
text = "Mining: Normal"; |
||||||
|
} else { |
||||||
|
text = "Mining: Turbo"; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Menu { |
||||||
|
title: "Network" |
||||||
|
MenuItem { |
||||||
|
text: "Add Peer" |
||||||
|
shortcut: "Ctrl+p" |
||||||
|
onTriggered: { |
||||||
|
addPeerWin.visible = true |
||||||
|
} |
||||||
|
} |
||||||
|
MenuItem { |
||||||
|
text: "Show Peers" |
||||||
|
shortcut: "Ctrl+e" |
||||||
|
onTriggered: { |
||||||
|
peerWindow.visible = true |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Menu { |
||||||
|
title: "Help" |
||||||
|
MenuItem { |
||||||
|
text: "About" |
||||||
|
onTriggered: { |
||||||
|
aboutWin.visible = true |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Menu { |
||||||
|
title: "GLOBAL SHORTCUTS" |
||||||
|
visible: false |
||||||
|
MenuItem { |
||||||
|
visible: false |
||||||
|
shortcut: "Ctrl+l" |
||||||
|
onTriggered: { |
||||||
|
url.focus = true |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
statusBar: StatusBar { |
||||||
|
height: 32 |
||||||
|
RowLayout { |
||||||
|
Button { |
||||||
|
id: miningButton |
||||||
|
text: "Start Mining" |
||||||
|
onClicked: { |
||||||
|
gui.toggleMining() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
RowLayout { |
||||||
|
Label { |
||||||
|
id: walletValueLabel |
||||||
|
|
||||||
|
font.pixelSize: 10 |
||||||
|
styleColor: "#797979" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Label { |
||||||
|
y: 6 |
||||||
|
objectName: "miningLabel" |
||||||
|
visible: true |
||||||
|
font.pixelSize: 10 |
||||||
|
anchors.right: lastBlockLabel.left |
||||||
|
anchors.rightMargin: 5 |
||||||
|
} |
||||||
|
|
||||||
|
Label { |
||||||
|
y: 6 |
||||||
|
id: lastBlockLabel |
||||||
|
objectName: "lastBlockLabel" |
||||||
|
visible: true |
||||||
|
text: "" |
||||||
|
font.pixelSize: 10 |
||||||
|
anchors.right: peerGroup.left |
||||||
|
anchors.rightMargin: 5 |
||||||
|
} |
||||||
|
|
||||||
|
ProgressBar { |
||||||
|
id: syncProgressIndicator |
||||||
|
visible: false |
||||||
|
objectName: "syncProgressIndicator" |
||||||
|
y: 3 |
||||||
|
width: 140 |
||||||
|
indeterminate: true |
||||||
|
anchors.right: peerGroup.left |
||||||
|
anchors.rightMargin: 5 |
||||||
|
} |
||||||
|
|
||||||
|
RowLayout { |
||||||
|
id: peerGroup |
||||||
|
y: 7 |
||||||
|
anchors.right: parent.right |
||||||
|
MouseArea { |
||||||
|
onDoubleClicked: peerWindow.visible = true |
||||||
|
anchors.fill: parent |
||||||
|
} |
||||||
|
|
||||||
|
Label { |
||||||
|
id: peerLabel |
||||||
|
font.pixelSize: 8 |
||||||
|
text: "0 / 0" |
||||||
|
} |
||||||
|
Image { |
||||||
|
id: peerImage |
||||||
|
width: 10; height: 10 |
||||||
|
source: "../network.png" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
property var blockModel: ListModel { |
||||||
|
id: blockModel |
||||||
|
} |
||||||
|
|
||||||
|
SplitView { |
||||||
|
property var views: []; |
||||||
|
|
||||||
|
id: mainSplit |
||||||
|
anchors.fill: parent |
||||||
|
resizing: false |
||||||
|
|
||||||
|
function setView(view, menu) { |
||||||
|
for(var i = 0; i < views.length; i++) { |
||||||
|
views[i].view.visible = false |
||||||
|
views[i].menuItem.setSelection(false) |
||||||
|
} |
||||||
|
view.visible = true |
||||||
|
|
||||||
|
//menu.border.color = "#CCCCCC" |
||||||
|
//menu.color = "#FFFFFFFF" |
||||||
|
menu.setSelection(true) |
||||||
|
} |
||||||
|
|
||||||
|
function addComponent(view, options) { |
||||||
|
view.visible = false |
||||||
|
view.anchors.fill = mainView |
||||||
|
|
||||||
|
if( !view.hasOwnProperty("iconSource") ) { |
||||||
|
console.log("Could not load plugin. Property 'iconSourc' not found on view."); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
var menuItem = menu.createMenuItem(view.iconSource, view, options); |
||||||
|
if( view.hasOwnProperty("menuItem") ) { |
||||||
|
view.menuItem = menuItem; |
||||||
|
} |
||||||
|
|
||||||
|
if( view.hasOwnProperty("onReady") ) { |
||||||
|
view.onReady.call(view) |
||||||
|
} |
||||||
|
|
||||||
|
if( options.active ) { |
||||||
|
setView(view, menuItem) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
return {view: view, menuItem: menuItem} |
||||||
|
} |
||||||
|
|
||||||
|
/********************* |
||||||
|
* Main menu. |
||||||
|
********************/ |
||||||
|
Rectangle { |
||||||
|
id: menu |
||||||
|
Layout.minimumWidth: 210 |
||||||
|
Layout.maximumWidth: 210 |
||||||
|
anchors.top: parent.top |
||||||
|
color: "#ececec" |
||||||
|
|
||||||
|
Component { |
||||||
|
id: menuItemTemplate |
||||||
|
Rectangle { |
||||||
|
id: menuItem |
||||||
|
property var view; |
||||||
|
property var path; |
||||||
|
property var closable; |
||||||
|
|
||||||
|
property alias title: label.text |
||||||
|
property alias icon: icon.source |
||||||
|
property alias secondaryTitle: secondary.text |
||||||
|
function setSelection(on) { |
||||||
|
sel.visible = on |
||||||
|
} |
||||||
|
|
||||||
|
width: 206 |
||||||
|
height: 28 |
||||||
|
color: "#00000000" |
||||||
|
|
||||||
|
anchors { |
||||||
|
left: parent.left |
||||||
|
leftMargin: 4 |
||||||
|
} |
||||||
|
|
||||||
|
Rectangle { |
||||||
|
id: sel |
||||||
|
visible: false |
||||||
|
anchors.fill: parent |
||||||
|
color: "#00000000" |
||||||
|
Rectangle { |
||||||
|
id: r |
||||||
|
anchors.fill: parent |
||||||
|
border.color: "#CCCCCC" |
||||||
|
border.width: 1 |
||||||
|
radius: 5 |
||||||
|
color: "#FFFFFFFF" |
||||||
|
} |
||||||
|
Rectangle { |
||||||
|
anchors { |
||||||
|
top: r.top |
||||||
|
bottom: r.bottom |
||||||
|
right: r.right |
||||||
|
} |
||||||
|
width: 10 |
||||||
|
color: "#FFFFFFFF" |
||||||
|
|
||||||
|
Rectangle { |
||||||
|
anchors { |
||||||
|
left: parent.left |
||||||
|
right: parent.right |
||||||
|
top: parent.top |
||||||
|
} |
||||||
|
height: 1 |
||||||
|
color: "#CCCCCC" |
||||||
|
} |
||||||
|
|
||||||
|
Rectangle { |
||||||
|
anchors { |
||||||
|
left: parent.left |
||||||
|
right: parent.right |
||||||
|
bottom: parent.bottom |
||||||
|
} |
||||||
|
height: 1 |
||||||
|
color: "#CCCCCC" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
MouseArea { |
||||||
|
anchors.fill: parent |
||||||
|
onClicked: { |
||||||
|
mainSplit.setView(view, menuItem) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Image { |
||||||
|
id: icon |
||||||
|
height: 20 |
||||||
|
width: 20 |
||||||
|
anchors { |
||||||
|
left: parent.left |
||||||
|
verticalCenter: parent.verticalCenter |
||||||
|
leftMargin: 3 |
||||||
|
} |
||||||
|
MouseArea { |
||||||
|
anchors.fill: parent |
||||||
|
onClicked: { |
||||||
|
menuItem.closeApp() |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Text { |
||||||
|
id: label |
||||||
|
anchors { |
||||||
|
left: icon.right |
||||||
|
verticalCenter: parent.verticalCenter |
||||||
|
leftMargin: 3 |
||||||
|
} |
||||||
|
|
||||||
|
color: "#0D0A01" |
||||||
|
font.pixelSize: 12 |
||||||
|
} |
||||||
|
|
||||||
|
Text { |
||||||
|
id: secondary |
||||||
|
anchors { |
||||||
|
right: parent.right |
||||||
|
rightMargin: 8 |
||||||
|
verticalCenter: parent.verticalCenter |
||||||
|
} |
||||||
|
color: "#AEADBE" |
||||||
|
font.pixelSize: 12 |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
function closeApp() { |
||||||
|
if(!this.closable) { return; } |
||||||
|
|
||||||
|
if(this.view.hasOwnProperty("onDestroy")) { |
||||||
|
this.view.onDestroy.call(this.view) |
||||||
|
} |
||||||
|
|
||||||
|
this.view.destroy() |
||||||
|
this.destroy() |
||||||
|
gui.removePlugin(this.path) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function createMenuItem(icon, view, options) { |
||||||
|
if(options === undefined) { |
||||||
|
options = {}; |
||||||
|
} |
||||||
|
|
||||||
|
var section; |
||||||
|
switch(options.section) { |
||||||
|
case "ethereum": |
||||||
|
section = menuDefault; |
||||||
|
break; |
||||||
|
case "legacy": |
||||||
|
section = menuLegacy; |
||||||
|
break; |
||||||
|
default: |
||||||
|
section = menuApps; |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
var comp = menuItemTemplate.createObject(section) |
||||||
|
|
||||||
|
comp.view = view |
||||||
|
comp.title = view.title |
||||||
|
comp.icon = view.iconSource |
||||||
|
comp.closable = options.close; |
||||||
|
|
||||||
|
return comp |
||||||
|
} |
||||||
|
|
||||||
|
ColumnLayout { |
||||||
|
id: menuColumn |
||||||
|
y: 10 |
||||||
|
width: parent.width |
||||||
|
anchors.left: parent.left |
||||||
|
anchors.right: parent.right |
||||||
|
spacing: 3 |
||||||
|
|
||||||
|
Text { |
||||||
|
text: "ETHEREUM" |
||||||
|
font.bold: true |
||||||
|
anchors { |
||||||
|
left: parent.left |
||||||
|
leftMargin: 5 |
||||||
|
} |
||||||
|
color: "#888888" |
||||||
|
} |
||||||
|
|
||||||
|
ColumnLayout { |
||||||
|
id: menuDefault |
||||||
|
spacing: 3 |
||||||
|
anchors { |
||||||
|
left: parent.left |
||||||
|
right: parent.right |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
Text { |
||||||
|
text: "APPS" |
||||||
|
font.bold: true |
||||||
|
anchors { |
||||||
|
left: parent.left |
||||||
|
leftMargin: 5 |
||||||
|
} |
||||||
|
color: "#888888" |
||||||
|
} |
||||||
|
|
||||||
|
ColumnLayout { |
||||||
|
id: menuApps |
||||||
|
spacing: 3 |
||||||
|
anchors { |
||||||
|
left: parent.left |
||||||
|
right: parent.right |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Text { |
||||||
|
text: "DEBUG" |
||||||
|
font.bold: true |
||||||
|
anchors { |
||||||
|
left: parent.left |
||||||
|
leftMargin: 5 |
||||||
|
} |
||||||
|
color: "#888888" |
||||||
|
} |
||||||
|
|
||||||
|
ColumnLayout { |
||||||
|
id: menuLegacy |
||||||
|
spacing: 3 |
||||||
|
anchors { |
||||||
|
left: parent.left |
||||||
|
right: parent.right |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/********************* |
||||||
|
* Main view |
||||||
|
********************/ |
||||||
|
Rectangle { |
||||||
|
anchors.right: parent.right |
||||||
|
anchors.left: menu.right |
||||||
|
anchors.bottom: parent.bottom |
||||||
|
anchors.top: parent.top |
||||||
|
color: "#00000000" |
||||||
|
|
||||||
|
Rectangle { |
||||||
|
id: urlPane |
||||||
|
height: 40 |
||||||
|
color: "#00000000" |
||||||
|
anchors { |
||||||
|
left: parent.left |
||||||
|
right: parent.right |
||||||
|
leftMargin: 5 |
||||||
|
rightMargin: 5 |
||||||
|
top: parent.top |
||||||
|
topMargin: 5 |
||||||
|
} |
||||||
|
TextField { |
||||||
|
id: url |
||||||
|
objectName: "url" |
||||||
|
placeholderText: "DApp URL" |
||||||
|
anchors { |
||||||
|
left: parent.left |
||||||
|
right: parent.right |
||||||
|
top: parent.top |
||||||
|
topMargin: 5 |
||||||
|
rightMargin: 5 |
||||||
|
leftMargin: 5 |
||||||
|
} |
||||||
|
|
||||||
|
Keys.onReturnPressed: { |
||||||
|
addPlugin(this.text, {close: true, section: "apps"}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
// Border |
||||||
|
Rectangle { |
||||||
|
id: divider |
||||||
|
anchors { |
||||||
|
left: parent.left |
||||||
|
right: parent.right |
||||||
|
top: urlPane.bottom |
||||||
|
} |
||||||
|
z: -1 |
||||||
|
height: 1 |
||||||
|
color: "#CCCCCC" |
||||||
|
} |
||||||
|
|
||||||
|
Rectangle { |
||||||
|
id: mainView |
||||||
|
color: "#00000000" |
||||||
|
anchors.right: parent.right |
||||||
|
anchors.left: parent.left |
||||||
|
anchors.bottom: parent.bottom |
||||||
|
anchors.top: divider.bottom |
||||||
|
|
||||||
|
function createView(component) { |
||||||
|
var view = component.createObject(mainView) |
||||||
|
|
||||||
|
return view; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/****************** |
||||||
|
* Dialogs |
||||||
|
*****************/ |
||||||
|
FileDialog { |
||||||
|
id: generalFileDialog |
||||||
|
property var callback; |
||||||
|
onAccepted: { |
||||||
|
var path = this.fileUrl.toString(); |
||||||
|
callback.call(this, path); |
||||||
|
} |
||||||
|
|
||||||
|
function show(selectExisting, callback) { |
||||||
|
generalFileDialog.callback = callback; |
||||||
|
generalFileDialog.selectExisting = selectExisting; |
||||||
|
|
||||||
|
this.open(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/****************** |
||||||
|
* Wallet functions |
||||||
|
*****************/ |
||||||
|
function importApp(path) { |
||||||
|
var ext = path.split('.').pop() |
||||||
|
if(ext == "html" || ext == "htm") { |
||||||
|
eth.openHtml(path) |
||||||
|
}else if(ext == "qml"){ |
||||||
|
addPlugin(path, {close: true, section: "apps"}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
function setWalletValue(value) { |
||||||
|
walletValueLabel.text = value |
||||||
|
} |
||||||
|
|
||||||
|
function loadPlugin(name) { |
||||||
|
console.log("Loading plugin" + name) |
||||||
|
var view = mainView.addPlugin(name) |
||||||
|
} |
||||||
|
|
||||||
|
function setPeers(text) { |
||||||
|
peerLabel.text = text |
||||||
|
} |
||||||
|
|
||||||
|
function addPeer(peer) { |
||||||
|
// We could just append the whole peer object but it cries if you try to alter them |
||||||
|
peerModel.append({ip: peer.ip, port: peer.port, lastResponse:timeAgo(peer.lastSend), latency: peer.latency, version: peer.version}) |
||||||
|
} |
||||||
|
|
||||||
|
function resetPeers(){ |
||||||
|
peerModel.clear() |
||||||
|
} |
||||||
|
|
||||||
|
function timeAgo(unixTs){ |
||||||
|
var lapsed = (Date.now() - new Date(unixTs*1000)) / 1000 |
||||||
|
return (lapsed + " seconds ago") |
||||||
|
} |
||||||
|
|
||||||
|
function convertToPretty(unixTs){ |
||||||
|
var a = new Date(unixTs*1000); |
||||||
|
var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; |
||||||
|
var year = a.getFullYear(); |
||||||
|
var month = months[a.getMonth()]; |
||||||
|
var date = a.getDate(); |
||||||
|
var hour = a.getHours(); |
||||||
|
var min = a.getMinutes(); |
||||||
|
var sec = a.getSeconds(); |
||||||
|
var time = date+' '+month+' '+year+' '+hour+':'+min+':'+sec ; |
||||||
|
return time; |
||||||
|
} |
||||||
|
|
||||||
|
/********************** |
||||||
|
* Windows |
||||||
|
*********************/ |
||||||
|
Window { |
||||||
|
id: peerWindow |
||||||
|
//flags: Qt.CustomizeWindowHint | Qt.Tool | Qt.WindowCloseButtonHint |
||||||
|
height: 200 |
||||||
|
width: 700 |
||||||
|
Rectangle { |
||||||
|
anchors.fill: parent |
||||||
|
property var peerModel: ListModel { |
||||||
|
id: peerModel |
||||||
|
} |
||||||
|
TableView { |
||||||
|
anchors.fill: parent |
||||||
|
id: peerTable |
||||||
|
model: peerModel |
||||||
|
TableViewColumn{width: 100; role: "ip" ; title: "IP" } |
||||||
|
TableViewColumn{width: 60; role: "port" ; title: "Port" } |
||||||
|
TableViewColumn{width: 140; role: "lastResponse"; title: "Last event" } |
||||||
|
TableViewColumn{width: 100; role: "latency"; title: "Latency" } |
||||||
|
TableViewColumn{width: 260; role: "version" ; title: "Version" } |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Window { |
||||||
|
id: aboutWin |
||||||
|
visible: false |
||||||
|
title: "About" |
||||||
|
minimumWidth: 350 |
||||||
|
maximumWidth: 350 |
||||||
|
maximumHeight: 200 |
||||||
|
minimumHeight: 200 |
||||||
|
|
||||||
|
Image { |
||||||
|
id: aboutIcon |
||||||
|
height: 150 |
||||||
|
width: 150 |
||||||
|
fillMode: Image.PreserveAspectFit |
||||||
|
smooth: true |
||||||
|
source: "../facet.png" |
||||||
|
x: 10 |
||||||
|
y: 10 |
||||||
|
} |
||||||
|
|
||||||
|
Text { |
||||||
|
anchors.left: aboutIcon.right |
||||||
|
anchors.leftMargin: 10 |
||||||
|
anchors.top: parent.top |
||||||
|
anchors.topMargin: 30 |
||||||
|
font.pointSize: 12 |
||||||
|
text: "<h2>Mist (0.6.5)</h2><h4>Amalthea</h4><br><h3>Development</h3>Jeffrey Wilcke<br>Viktor Trón<br><h3>Building</h3>Maran Hidskes" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Window { |
||||||
|
id: txImportDialog |
||||||
|
minimumWidth: 270 |
||||||
|
maximumWidth: 270 |
||||||
|
maximumHeight: 50 |
||||||
|
minimumHeight: 50 |
||||||
|
TextField { |
||||||
|
id: txImportField |
||||||
|
width: 170 |
||||||
|
anchors.verticalCenter: parent.verticalCenter |
||||||
|
anchors.left: parent.left |
||||||
|
anchors.leftMargin: 10 |
||||||
|
onAccepted: { |
||||||
|
} |
||||||
|
} |
||||||
|
Button { |
||||||
|
anchors.left: txImportField.right |
||||||
|
anchors.verticalCenter: parent.verticalCenter |
||||||
|
anchors.leftMargin: 5 |
||||||
|
text: "Import" |
||||||
|
onClicked: { |
||||||
|
eth.importTx(txImportField.text) |
||||||
|
txImportField.visible = false |
||||||
|
} |
||||||
|
} |
||||||
|
Component.onCompleted: { |
||||||
|
addrField.focus = true |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Window { |
||||||
|
id: addPeerWin |
||||||
|
visible: false |
||||||
|
minimumWidth: 300 |
||||||
|
maximumWidth: 300 |
||||||
|
maximumHeight: 50 |
||||||
|
minimumHeight: 50 |
||||||
|
title: "Connect to peer" |
||||||
|
|
||||||
|
ComboBox { |
||||||
|
id: addrField |
||||||
|
anchors.verticalCenter: parent.verticalCenter |
||||||
|
anchors.left: parent.left |
||||||
|
anchors.right: addPeerButton.left |
||||||
|
anchors.leftMargin: 10 |
||||||
|
anchors.rightMargin: 10 |
||||||
|
onAccepted: { |
||||||
|
eth.connectToPeer(addrField.currentText) |
||||||
|
addPeerWin.visible = false |
||||||
|
} |
||||||
|
|
||||||
|
editable: true |
||||||
|
model: ListModel { id: pastPeers } |
||||||
|
|
||||||
|
Component.onCompleted: { |
||||||
|
var ips = eth.pastPeers() |
||||||
|
for(var i = 0; i < ips.length; i++) { |
||||||
|
pastPeers.append({text: ips.get(i)}) |
||||||
|
} |
||||||
|
|
||||||
|
pastPeers.insert(0, {text: "poc-6.ethdev.com:30303"}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Button { |
||||||
|
id: addPeerButton |
||||||
|
anchors.right: parent.right |
||||||
|
anchors.verticalCenter: parent.verticalCenter |
||||||
|
anchors.rightMargin: 10 |
||||||
|
text: "Add" |
||||||
|
onClicked: { |
||||||
|
eth.connectToPeer(addrField.currentText) |
||||||
|
addPeerWin.visible = false |
||||||
|
} |
||||||
|
} |
||||||
|
Component.onCompleted: { |
||||||
|
addrField.focus = true |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,409 @@ |
|||||||
|
import QtQuick 2.0 |
||||||
|
import QtWebKit 3.0 |
||||||
|
import QtWebKit.experimental 1.0 |
||||||
|
import QtQuick.Controls 1.0; |
||||||
|
import QtQuick.Controls.Styles 1.0 |
||||||
|
import QtQuick.Layouts 1.0; |
||||||
|
import QtQuick.Window 2.1; |
||||||
|
import Ethereum 1.0 |
||||||
|
|
||||||
|
import "../ext/qml_messaging.js" as Messaging |
||||||
|
|
||||||
|
//ApplicationWindow { |
||||||
|
Rectangle { |
||||||
|
id: window |
||||||
|
property var title: "Browser" |
||||||
|
property var iconSource: "../browser.png" |
||||||
|
property var menuItem |
||||||
|
|
||||||
|
property alias url: webview.url |
||||||
|
property alias webView: webview |
||||||
|
|
||||||
|
Component.onCompleted: { |
||||||
|
webview.url = "http://etherian.io" |
||||||
|
} |
||||||
|
|
||||||
|
signal messages(var messages, int id); |
||||||
|
onMessages: { |
||||||
|
// Bit of a cheat to get proper JSON |
||||||
|
var m = JSON.parse(JSON.parse(JSON.stringify(messages))) |
||||||
|
webview.postEvent("messages", [m, id]); |
||||||
|
} |
||||||
|
|
||||||
|
Item { |
||||||
|
objectName: "root" |
||||||
|
id: root |
||||||
|
anchors.fill: parent |
||||||
|
state: "inspectorShown" |
||||||
|
|
||||||
|
RowLayout { |
||||||
|
id: navBar |
||||||
|
height: 40 |
||||||
|
anchors { |
||||||
|
left: parent.left |
||||||
|
right: parent.right |
||||||
|
leftMargin: 7 |
||||||
|
} |
||||||
|
|
||||||
|
Button { |
||||||
|
id: back |
||||||
|
onClicked: { |
||||||
|
webview.goBack() |
||||||
|
} |
||||||
|
style: ButtonStyle { |
||||||
|
background: Image { |
||||||
|
source: "../back.png" |
||||||
|
width: 30 |
||||||
|
height: 30 |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TextField { |
||||||
|
anchors { |
||||||
|
left: back.right |
||||||
|
right: toggleInspector.left |
||||||
|
leftMargin: 5 |
||||||
|
rightMargin: 5 |
||||||
|
} |
||||||
|
text: "http://etherian.io" |
||||||
|
id: uriNav |
||||||
|
y: parent.height / 2 - this.height / 2 |
||||||
|
|
||||||
|
Keys.onReturnPressed: { |
||||||
|
webview.url = this.text; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Button { |
||||||
|
id: toggleInspector |
||||||
|
anchors { |
||||||
|
right: parent.right |
||||||
|
} |
||||||
|
iconSource: "../bug.png" |
||||||
|
onClicked: { |
||||||
|
if(inspector.visible == true){ |
||||||
|
inspector.visible = false |
||||||
|
}else{ |
||||||
|
inspector.visible = true |
||||||
|
inspector.url = webview.experimental.remoteInspectorUrl |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
WebView { |
||||||
|
objectName: "webView" |
||||||
|
id: webview |
||||||
|
anchors { |
||||||
|
left: parent.left |
||||||
|
right: parent.right |
||||||
|
bottom: parent.bottom |
||||||
|
top: navBar.bottom |
||||||
|
} |
||||||
|
|
||||||
|
property var cleanPath: false |
||||||
|
onNavigationRequested: { |
||||||
|
if(!this.cleanPath) { |
||||||
|
var uri = request.url.toString(); |
||||||
|
if(!/.*\:\/\/.*/.test(uri)) { |
||||||
|
uri = "http://" + uri; |
||||||
|
} |
||||||
|
|
||||||
|
var reg = /(^https?\:\/\/(?:www\.)?)([a-zA-Z0-9_\-]*\.eth)(.*)/ |
||||||
|
|
||||||
|
if(reg.test(uri)) { |
||||||
|
uri.replace(reg, function(match, pre, domain, path) { |
||||||
|
uri = pre; |
||||||
|
|
||||||
|
var lookup = eth.lookupDomain(domain.substring(0, domain.length - 4)); |
||||||
|
var ip = []; |
||||||
|
for(var i = 0, l = lookup.length; i < l; i++) { |
||||||
|
ip.push(lookup.charCodeAt(i)) |
||||||
|
} |
||||||
|
|
||||||
|
if(ip.length != 0) { |
||||||
|
uri += lookup; |
||||||
|
} else { |
||||||
|
uri += domain; |
||||||
|
} |
||||||
|
|
||||||
|
uri += path; |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
this.cleanPath = true; |
||||||
|
|
||||||
|
webview.url = uri; |
||||||
|
} else { |
||||||
|
// Prevent inf loop. |
||||||
|
this.cleanPath = false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function sendMessage(data) { |
||||||
|
webview.experimental.postMessage(JSON.stringify(data)) |
||||||
|
} |
||||||
|
|
||||||
|
onTitleChanged: { |
||||||
|
var data = Messaging.HandleMessage(title); |
||||||
|
if(data) { |
||||||
|
sendMessage(data) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
experimental.preferences.javascriptEnabled: true |
||||||
|
experimental.preferences.navigatorQtObjectEnabled: true |
||||||
|
experimental.preferences.developerExtrasEnabled: true |
||||||
|
experimental.userScripts: ["../ext/q.js", "../ext/pre.js", "../ext/big.js", "../ext/string.js", "../ext/html_messaging.js"] |
||||||
|
experimental.onMessageReceived: { |
||||||
|
console.log("[onMessageReceived]: ", message.data) |
||||||
|
// TODO move to messaging.js |
||||||
|
var data = JSON.parse(message.data) |
||||||
|
|
||||||
|
try { |
||||||
|
switch(data.call) { |
||||||
|
case "compile": |
||||||
|
postData(data._seed, eth.compile(data.args[0])) |
||||||
|
break |
||||||
|
|
||||||
|
case "getCoinBase": |
||||||
|
postData(data._seed, eth.coinBase()) |
||||||
|
|
||||||
|
break |
||||||
|
|
||||||
|
case "getIsListening": |
||||||
|
postData(data._seed, eth.isListening()) |
||||||
|
|
||||||
|
break |
||||||
|
|
||||||
|
case "getIsMining": |
||||||
|
postData(data._seed, eth.isMining()) |
||||||
|
|
||||||
|
break |
||||||
|
|
||||||
|
case "getPeerCount": |
||||||
|
postData(data._seed, eth.peerCount()) |
||||||
|
|
||||||
|
break |
||||||
|
|
||||||
|
case "getCountAt": |
||||||
|
require(1) |
||||||
|
postData(data._seed, eth.txCountAt(data.args[0])) |
||||||
|
|
||||||
|
break |
||||||
|
|
||||||
|
case "getCodeAt": |
||||||
|
require(1) |
||||||
|
var code = eth.codeAt(data.args[0]) |
||||||
|
postData(data._seed, code); |
||||||
|
|
||||||
|
break |
||||||
|
|
||||||
|
case "getBlockByNumber": |
||||||
|
var block = eth.blockByNumber(data.args[0]) |
||||||
|
postData(data._seed, block) |
||||||
|
|
||||||
|
break |
||||||
|
|
||||||
|
case "getBlockByHash": |
||||||
|
var block = eth.blockByHash(data.args[0]) |
||||||
|
postData(data._seed, block) |
||||||
|
|
||||||
|
break |
||||||
|
|
||||||
|
case "transact": |
||||||
|
require(5) |
||||||
|
|
||||||
|
var tx = eth.transact(data.args) |
||||||
|
postData(data._seed, tx) |
||||||
|
|
||||||
|
break |
||||||
|
|
||||||
|
case "getStorageAt": |
||||||
|
require(2); |
||||||
|
|
||||||
|
var storage = eth.storageAt(data.args[0], data.args[1]); |
||||||
|
postData(data._seed, storage) |
||||||
|
|
||||||
|
break |
||||||
|
|
||||||
|
case "call": |
||||||
|
require(1); |
||||||
|
var ret = eth.call(data.args) |
||||||
|
postData(data._seed, ret) |
||||||
|
|
||||||
|
break |
||||||
|
|
||||||
|
case "getEachStorage": |
||||||
|
require(1); |
||||||
|
var storage = JSON.parse(eth.eachStorage(data.args[0])) |
||||||
|
postData(data._seed, storage) |
||||||
|
|
||||||
|
break |
||||||
|
|
||||||
|
case "getTransactionsFor": |
||||||
|
require(1); |
||||||
|
var txs = eth.transactionsFor(data.args[0], true) |
||||||
|
postData(data._seed, txs) |
||||||
|
|
||||||
|
break |
||||||
|
|
||||||
|
case "getBalanceAt": |
||||||
|
require(1); |
||||||
|
|
||||||
|
postData(data._seed, eth.balanceAt(data.args[0])); |
||||||
|
|
||||||
|
break |
||||||
|
|
||||||
|
case "getKey": |
||||||
|
var key = eth.key().privateKey; |
||||||
|
|
||||||
|
postData(data._seed, key) |
||||||
|
break |
||||||
|
|
||||||
|
case "watch": |
||||||
|
require(2) |
||||||
|
eth.watch(data.args[0], data.args[1]) |
||||||
|
|
||||||
|
case "disconnect": |
||||||
|
require(1) |
||||||
|
postData(data._seed, null) |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case "getSecretToAddress": |
||||||
|
require(1) |
||||||
|
|
||||||
|
var addr = eth.secretToAddress(data.args[0]) |
||||||
|
console.log("getsecret", addr) |
||||||
|
postData(data._seed, addr) |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case "messages": |
||||||
|
require(1); |
||||||
|
|
||||||
|
var messages = JSON.parse(eth.getMessages(data.args[0])) |
||||||
|
postData(data._seed, messages) |
||||||
|
|
||||||
|
break |
||||||
|
|
||||||
|
case "mutan": |
||||||
|
require(1) |
||||||
|
|
||||||
|
var code = eth.compileMutan(data.args[0]) |
||||||
|
postData(data._seed, "0x"+code) |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case "newFilterString": |
||||||
|
require(1) |
||||||
|
var id = eth.newFilterString(data.args[0]) |
||||||
|
postData(data._seed, id); |
||||||
|
break; |
||||||
|
case "newFilter": |
||||||
|
require(1) |
||||||
|
var id = eth.newFilter(data.args[0]) |
||||||
|
|
||||||
|
postData(data._seed, id); |
||||||
|
break; |
||||||
|
|
||||||
|
case "getMessages": |
||||||
|
require(1); |
||||||
|
|
||||||
|
var messages = eth.messages(data.args[0]); |
||||||
|
var m = JSON.parse(JSON.parse(JSON.stringify(messages))) |
||||||
|
postData(data._seed, m); |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case "deleteFilter": |
||||||
|
require(1); |
||||||
|
eth.uninstallFilter(data.args[0]) |
||||||
|
break; |
||||||
|
} |
||||||
|
} catch(e) { |
||||||
|
console.log(data.call + ": " + e) |
||||||
|
|
||||||
|
postData(data._seed, null); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
function post(seed, data) { |
||||||
|
postData(data._seed, data) |
||||||
|
} |
||||||
|
|
||||||
|
function require(args, num) { |
||||||
|
if(args.length < num) { |
||||||
|
throw("required argument count of "+num+" got "+args.length); |
||||||
|
} |
||||||
|
} |
||||||
|
function postData(seed, data) { |
||||||
|
webview.experimental.postMessage(JSON.stringify({data: data, _seed: seed})) |
||||||
|
} |
||||||
|
function postEvent(event, data) { |
||||||
|
webview.experimental.postMessage(JSON.stringify({data: data, _event: event})) |
||||||
|
} |
||||||
|
|
||||||
|
function onWatchedCb(data, id) { |
||||||
|
var messages = JSON.parse(data) |
||||||
|
postEvent("watched:"+id, messages) |
||||||
|
} |
||||||
|
|
||||||
|
function onNewBlockCb(block) { |
||||||
|
postEvent("block:new", block) |
||||||
|
} |
||||||
|
function onObjectChangeCb(stateObject) { |
||||||
|
postEvent("object:"+stateObject.address(), stateObject) |
||||||
|
} |
||||||
|
function onStorageChangeCb(storageObject) { |
||||||
|
var ev = ["storage", storageObject.stateAddress, storageObject.address].join(":"); |
||||||
|
postEvent(ev, [storageObject.address, storageObject.value]) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
Rectangle { |
||||||
|
id: sizeGrip |
||||||
|
color: "gray" |
||||||
|
visible: false |
||||||
|
height: 10 |
||||||
|
anchors { |
||||||
|
left: root.left |
||||||
|
right: root.right |
||||||
|
} |
||||||
|
y: Math.round(root.height * 2 / 3) |
||||||
|
|
||||||
|
MouseArea { |
||||||
|
anchors.fill: parent |
||||||
|
drag.target: sizeGrip |
||||||
|
drag.minimumY: 0 |
||||||
|
drag.maximumY: root.height |
||||||
|
drag.axis: Drag.YAxis |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
WebView { |
||||||
|
id: inspector |
||||||
|
visible: false |
||||||
|
anchors { |
||||||
|
left: root.left |
||||||
|
right: root.right |
||||||
|
top: sizeGrip.bottom |
||||||
|
bottom: root.bottom |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
states: [ |
||||||
|
State { |
||||||
|
name: "inspectorShown" |
||||||
|
PropertyChanges { |
||||||
|
target: inspector |
||||||
|
} |
||||||
|
} |
||||||
|
] |
||||||
|
} |
||||||
|
} |
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
@ -0,0 +1,148 @@ |
|||||||
|
package main |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/json" |
||||||
|
"fmt" |
||||||
|
"os" |
||||||
|
"strconv" |
||||||
|
|
||||||
|
"github.com/ethereum/eth-go/ethchain" |
||||||
|
"github.com/ethereum/eth-go/ethlog" |
||||||
|
"github.com/ethereum/eth-go/ethpipe" |
||||||
|
"github.com/ethereum/eth-go/ethutil" |
||||||
|
"github.com/ethereum/go-ethereum/utils" |
||||||
|
) |
||||||
|
|
||||||
|
type plugin struct { |
||||||
|
Name string `json:"name"` |
||||||
|
Path string `json:"path"` |
||||||
|
} |
||||||
|
|
||||||
|
func (gui *Gui) Println(v ...interface{}) { |
||||||
|
gui.printLog(fmt.Sprintln(v...)) |
||||||
|
} |
||||||
|
|
||||||
|
func (gui *Gui) Printf(format string, v ...interface{}) { |
||||||
|
gui.printLog(fmt.Sprintf(format, v...)) |
||||||
|
} |
||||||
|
|
||||||
|
// Print function that logs directly to the GUI
|
||||||
|
func (gui *Gui) printLog(s string) { |
||||||
|
/* |
||||||
|
str := strings.TrimRight(s, "\n") |
||||||
|
lines := strings.Split(str, "\n") |
||||||
|
|
||||||
|
view := gui.getObjectByName("infoView") |
||||||
|
for _, line := range lines { |
||||||
|
view.Call("addLog", line) |
||||||
|
} |
||||||
|
*/ |
||||||
|
} |
||||||
|
func (gui *Gui) Transact(recipient, value, gas, gasPrice, d string) (*ethpipe.JSReceipt, error) { |
||||||
|
var data string |
||||||
|
if len(recipient) == 0 { |
||||||
|
code, err := ethutil.Compile(d, false) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
data = ethutil.Bytes2Hex(code) |
||||||
|
} else { |
||||||
|
data = ethutil.Bytes2Hex(utils.FormatTransactionData(d)) |
||||||
|
} |
||||||
|
|
||||||
|
return gui.pipe.Transact(gui.privateKey(), recipient, value, gas, gasPrice, data) |
||||||
|
} |
||||||
|
|
||||||
|
func (gui *Gui) SetCustomIdentifier(customIdentifier string) { |
||||||
|
gui.clientIdentity.SetCustomIdentifier(customIdentifier) |
||||||
|
gui.config.Save("id", customIdentifier) |
||||||
|
} |
||||||
|
|
||||||
|
func (gui *Gui) GetCustomIdentifier() string { |
||||||
|
return gui.clientIdentity.GetCustomIdentifier() |
||||||
|
} |
||||||
|
|
||||||
|
func (gui *Gui) ToggleTurboMining() { |
||||||
|
gui.miner.ToggleTurbo() |
||||||
|
} |
||||||
|
|
||||||
|
// functions that allow Gui to implement interface ethlog.LogSystem
|
||||||
|
func (gui *Gui) SetLogLevel(level ethlog.LogLevel) { |
||||||
|
gui.logLevel = level |
||||||
|
gui.stdLog.SetLogLevel(level) |
||||||
|
gui.config.Save("loglevel", level) |
||||||
|
} |
||||||
|
|
||||||
|
func (gui *Gui) GetLogLevel() ethlog.LogLevel { |
||||||
|
return gui.logLevel |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Gui) AddPlugin(pluginPath string) { |
||||||
|
self.plugins[pluginPath] = plugin{Name: pluginPath, Path: pluginPath} |
||||||
|
|
||||||
|
json, _ := json.MarshalIndent(self.plugins, "", " ") |
||||||
|
ethutil.WriteFile(ethutil.Config.ExecPath+"/plugins.json", json) |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Gui) RemovePlugin(pluginPath string) { |
||||||
|
delete(self.plugins, pluginPath) |
||||||
|
|
||||||
|
json, _ := json.MarshalIndent(self.plugins, "", " ") |
||||||
|
ethutil.WriteFile(ethutil.Config.ExecPath+"/plugins.json", json) |
||||||
|
} |
||||||
|
|
||||||
|
// this extra function needed to give int typecast value to gui widget
|
||||||
|
// that sets initial loglevel to default
|
||||||
|
func (gui *Gui) GetLogLevelInt() int { |
||||||
|
return int(gui.logLevel) |
||||||
|
} |
||||||
|
func (self *Gui) DumpState(hash, path string) { |
||||||
|
var stateDump []byte |
||||||
|
|
||||||
|
if len(hash) == 0 { |
||||||
|
stateDump = self.eth.StateManager().CurrentState().Dump() |
||||||
|
} else { |
||||||
|
var block *ethchain.Block |
||||||
|
if hash[0] == '#' { |
||||||
|
i, _ := strconv.Atoi(hash[1:]) |
||||||
|
block = self.eth.BlockChain().GetBlockByNumber(uint64(i)) |
||||||
|
} else { |
||||||
|
block = self.eth.BlockChain().GetBlock(ethutil.Hex2Bytes(hash)) |
||||||
|
} |
||||||
|
|
||||||
|
if block == nil { |
||||||
|
logger.Infof("block err: not found %s\n", hash) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
stateDump = block.State().Dump() |
||||||
|
} |
||||||
|
|
||||||
|
file, err := os.OpenFile(path[7:], os.O_CREATE|os.O_RDWR, os.ModePerm) |
||||||
|
if err != nil { |
||||||
|
logger.Infoln("dump err: ", err) |
||||||
|
return |
||||||
|
} |
||||||
|
defer file.Close() |
||||||
|
|
||||||
|
logger.Infof("dumped state (%s) to %s\n", hash, path) |
||||||
|
|
||||||
|
file.Write(stateDump) |
||||||
|
} |
||||||
|
func (gui *Gui) ToggleMining() { |
||||||
|
var txt string |
||||||
|
if gui.eth.Mining { |
||||||
|
utils.StopMining(gui.eth) |
||||||
|
txt = "Start mining" |
||||||
|
|
||||||
|
gui.getObjectByName("miningLabel").Set("visible", false) |
||||||
|
} else { |
||||||
|
utils.StartMining(gui.eth) |
||||||
|
gui.miner = utils.GetMiner() |
||||||
|
txt = "Stop mining" |
||||||
|
|
||||||
|
gui.getObjectByName("miningLabel").Set("visible", true) |
||||||
|
} |
||||||
|
|
||||||
|
gui.win.Root().Set("miningButtonText", txt) |
||||||
|
} |
@ -0,0 +1,36 @@ |
|||||||
|
package main |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"os" |
||||||
|
|
||||||
|
"gopkg.in/qml.v1" |
||||||
|
) |
||||||
|
|
||||||
|
func ErrorWindow(err error) { |
||||||
|
engine := qml.NewEngine() |
||||||
|
component, e := engine.LoadString("local", qmlErr) |
||||||
|
if e != nil { |
||||||
|
fmt.Println("err:", err) |
||||||
|
os.Exit(1) |
||||||
|
} |
||||||
|
|
||||||
|
win := component.CreateWindow(nil) |
||||||
|
win.Root().ObjectByName("label").Set("text", err.Error()) |
||||||
|
win.Show() |
||||||
|
win.Wait() |
||||||
|
} |
||||||
|
|
||||||
|
const qmlErr = ` |
||||||
|
import QtQuick 2.0; import QtQuick.Controls 1.0; |
||||||
|
ApplicationWindow { |
||||||
|
width: 600; height: 150; |
||||||
|
flags: Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowCloseButtonHint |
||||||
|
title: "Error" |
||||||
|
Text { |
||||||
|
x: parent.width / 2 - this.width / 2; |
||||||
|
y: parent.height / 2 - this.height / 2; |
||||||
|
objectName: "label"; |
||||||
|
} |
||||||
|
} |
||||||
|
` |