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