aboutsummaryrefslogtreecommitdiffstats
path: root/cmd/mist/assets/qml
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/mist/assets/qml')
-rw-r--r--cmd/mist/assets/qml/QmlApp.qml22
-rw-r--r--cmd/mist/assets/qml/depricated_browser.qml486
-rw-r--r--cmd/mist/assets/qml/first_run.qml155
-rwxr-xr-xcmd/mist/assets/qml/fonts/Simple-Line-Icons.ttfbin0 -> 35304 bytes
-rw-r--r--cmd/mist/assets/qml/fonts/SourceSansPro-Black.ttfbin0 -> 117460 bytes
-rw-r--r--cmd/mist/assets/qml/fonts/SourceSansPro-BlackIt.ttfbin0 -> 123840 bytes
-rw-r--r--cmd/mist/assets/qml/fonts/SourceSansPro-Bold.ttfbin0 -> 119096 bytes
-rw-r--r--cmd/mist/assets/qml/fonts/SourceSansPro-BoldIt.ttfbin0 -> 123756 bytes
-rw-r--r--cmd/mist/assets/qml/fonts/SourceSansPro-ExtraLight.ttfbin0 -> 120812 bytes
-rw-r--r--cmd/mist/assets/qml/fonts/SourceSansPro-ExtraLightIt.ttfbin0 -> 125720 bytes
-rw-r--r--cmd/mist/assets/qml/fonts/SourceSansPro-It.ttfbin0 -> 124668 bytes
-rw-r--r--cmd/mist/assets/qml/fonts/SourceSansPro-Light.ttfbin0 -> 121124 bytes
-rw-r--r--cmd/mist/assets/qml/fonts/SourceSansPro-LightIt.ttfbin0 -> 125480 bytes
-rw-r--r--cmd/mist/assets/qml/fonts/SourceSansPro-Regular.ttfbin0 -> 120672 bytes
-rw-r--r--cmd/mist/assets/qml/fonts/SourceSansPro-Semibold.ttfbin0 -> 120036 bytes
-rw-r--r--cmd/mist/assets/qml/fonts/SourceSansPro-SemiboldIt.ttfbin0 -> 123840 bytes
-rw-r--r--cmd/mist/assets/qml/fonts/SourceSerifPro-Bold.ttfbin0 -> 113188 bytes
-rw-r--r--cmd/mist/assets/qml/fonts/SourceSerifPro-Regular.ttfbin0 -> 113640 bytes
-rw-r--r--cmd/mist/assets/qml/fonts/SourceSerifPro-Semibold.ttfbin0 -> 114352 bytes
-rw-r--r--cmd/mist/assets/qml/main.qml1043
-rw-r--r--cmd/mist/assets/qml/muted.qml74
-rw-r--r--cmd/mist/assets/qml/test_app.qml70
-rw-r--r--cmd/mist/assets/qml/transactions.qml9
-rw-r--r--cmd/mist/assets/qml/views/browser.qml506
-rw-r--r--cmd/mist/assets/qml/views/catalog.qml144
-rw-r--r--cmd/mist/assets/qml/views/chain.qml261
-rw-r--r--cmd/mist/assets/qml/views/history.qml51
-rw-r--r--cmd/mist/assets/qml/views/info.qml184
-rw-r--r--cmd/mist/assets/qml/views/jeffcoin/jeff.pngbin0 -> 84076 bytes
-rw-r--r--cmd/mist/assets/qml/views/jeffcoin/jeffcoin.qml190
-rw-r--r--cmd/mist/assets/qml/views/miner.qml294
-rw-r--r--cmd/mist/assets/qml/views/pending_tx.qml53
-rw-r--r--cmd/mist/assets/qml/views/transaction.qml216
-rw-r--r--cmd/mist/assets/qml/views/wallet.qml190
-rw-r--r--cmd/mist/assets/qml/views/whisper.qml74
35 files changed, 4022 insertions, 0 deletions
diff --git a/cmd/mist/assets/qml/QmlApp.qml b/cmd/mist/assets/qml/QmlApp.qml
new file mode 100644
index 000000000..f5c503f4c
--- /dev/null
+++ b/cmd/mist/assets/qml/QmlApp.qml
@@ -0,0 +1,22 @@
+import QtQuick 2.0
+import QtQuick.Controls 1.0;
+import QtQuick.Layouts 1.0;
+import Ethereum 1.0
+
+ApplicationWindow {
+ minimumWidth: 500
+ maximumWidth: 500
+ maximumHeight: 400
+ minimumHeight: 400
+
+ function onNewBlockCb(block) {
+ console.log("Please overwrite onNewBlock(block):", block)
+ }
+ function onObjectChangeCb(stateObject) {
+ console.log("Please overwrite onObjectChangeCb(object)", stateObject)
+ }
+ function onStorageChangeCb(storageObject) {
+ var ev = ["storage", storageObject.stateAddress, storageObject.address].join(":");
+ console.log("Please overwrite onStorageChangeCb(object)", ev)
+ }
+}
diff --git a/cmd/mist/assets/qml/depricated_browser.qml b/cmd/mist/assets/qml/depricated_browser.qml
new file mode 100644
index 000000000..7056dbbf3
--- /dev/null
+++ b/cmd/mist/assets/qml/depricated_browser.qml
@@ -0,0 +1,486 @@
+import QtQuick 2.1
+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
+
+Rectangle {
+ id: window
+ anchors.fill: parent
+ color: "#00000000"
+
+ property var title: "DApps"
+ property var iconSource: "../browser.png"
+ property var menuItem
+ property var hideUrl: true
+
+ property alias url: webview.url
+ property alias windowTitle: webview.title
+ property alias webView: webview
+
+ property var cleanPath: false
+ property var open: function(url) {
+ if(!window.cleanPath) {
+ var uri = url;
+ 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;
+ });
+ }
+
+ window.cleanPath = true;
+
+ webview.url = uri;
+
+ //uriNav.text = uri.text.replace(/(^https?\:\/\/(?:www\.)?)([a-zA-Z0-9_\-]*\.\w{2,3})(.*)/, "$1$2<span style='color:#CCC'>$3</span>");
+ uriNav.text = uri;
+ } else {
+ // Prevent inf loop.
+ window.cleanPath = false;
+ }
+ }
+
+ Component.onCompleted: {
+ webview.url = "http://etherian.io"
+ }
+
+ function messages(messages, id) {
+ // Bit of a cheat to get proper JSON
+ var m = JSON.parse(JSON.parse(JSON.stringify(messages)))
+ webview.postEvent("eth_changed", id, m);
+ }
+
+ function onShhMessage(message, id) {
+ webview.postEvent("shh_changed", id, message)
+ }
+
+ 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: 10
+ rightMargin: 10
+ }
+ text: webview.url;
+ 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
+ }
+ }
+ }
+ }
+
+ // Border
+ Rectangle {
+ id: divider
+ anchors {
+ left: parent.left
+ right: parent.right
+ top: navBar.bottom
+ }
+ z: -1
+ height: 1
+ color: "#CCCCCC"
+ }
+
+ ScrollView {
+ anchors {
+ left: parent.left
+ right: parent.right
+ bottom: parent.bottom
+ top: divider.bottom
+ }
+ WebView {
+ objectName: "webView"
+ id: webview
+ anchors.fill: parent
+
+ function sendMessage(data) {
+ webview.experimental.postMessage(JSON.stringify(data))
+ }
+
+ experimental.preferences.javascriptEnabled: true
+ experimental.preferences.webAudioEnabled: true
+ experimental.preferences.pluginsEnabled: true
+ experimental.preferences.navigatorQtObjectEnabled: true
+ experimental.preferences.developerExtrasEnabled: true
+ experimental.preferences.webGLEnabled: true
+ experimental.preferences.notificationsEnabled: true
+ experimental.preferences.localStorageEnabled: true
+ experimental.userAgent:"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Mist/0.1 Safari/537.36"
+
+ experimental.itemSelector: MouseArea {
+ // To avoid conflicting with ListView.model when inside Initiator context.
+ property QtObject selectorModel: model
+ anchors.fill: parent
+ onClicked: selectorModel.reject()
+
+ Menu {
+ visible: true
+ id: itemSelector
+
+ Instantiator {
+ model: selectorModel.items
+ delegate: MenuItem {
+ text: model.text
+ onTriggered: {
+ selectorModel.accept(index)
+ }
+ }
+ onObjectAdded: itemSelector.insertItem(index, object)
+ onObjectRemoved: itemSelector.removeItem(object)
+ }
+ }
+
+ Component.onCompleted: {
+ itemSelector.popup()
+ }
+ }
+ experimental.userScripts: ["../ext/q.js", "../ext/ethereum.js/lib/web3.js", "../ext/ethereum.js/lib/qt.js", "../ext/setup.js"]
+ experimental.onMessageReceived: {
+ //console.log("[onMessageReceived]: ", message.data)
+ var data = JSON.parse(message.data)
+
+ try {
+ switch(data.call) {
+ case "eth_compile":
+ postData(data._id, eth.compile(data.args[0]))
+ break
+
+ case "eth_coinbase":
+ postData(data._id, eth.coinBase())
+
+ case "eth_account":
+ postData(data._id, eth.key().address);
+
+ case "eth_istening":
+ postData(data._id, eth.isListening())
+
+ break
+
+ case "eth_mining":
+ postData(data._id, eth.isMining())
+
+ break
+
+ case "eth_peerCount":
+ postData(data._id, eth.peerCount())
+
+ break
+
+ case "eth_countAt":
+ require(1)
+ postData(data._id, eth.txCountAt(data.args[0]))
+
+ break
+
+ case "eth_codeAt":
+ require(1)
+ var code = eth.codeAt(data.args[0])
+ postData(data._id, code);
+
+ break
+
+ case "eth_blockByNumber":
+ require(1)
+ var block = eth.blockByNumber(data.args[0])
+ postData(data._id, block)
+ break
+
+ case "eth_blockByHash":
+ require(1)
+ var block = eth.blockByHash(data.args[0])
+ postData(data._id, block)
+ break
+
+ require(2)
+ var block = eth.blockByHash(data.args[0])
+ postData(data._id, block.transactions[data.args[1]])
+ break
+
+ case "eth_transactionByHash":
+ case "eth_transactionByNumber":
+ require(2)
+
+ var block;
+ if (data.call === "transactionByHash")
+ block = eth.blockByHash(data.args[0])
+ else
+ block = eth.blockByNumber(data.args[0])
+
+ var tx = block.transactions.get(data.args[1])
+
+ postData(data._id, tx)
+ break
+
+ case "eth_uncleByHash":
+ case "eth_uncleByNumber":
+ require(2)
+
+ var block;
+ if (data.call === "uncleByHash")
+ block = eth.blockByHash(data.args[0])
+ else
+ block = eth.blockByNumber(data.args[0])
+
+ var uncle = block.uncles.get(data.args[1])
+
+ postData(data._id, uncle)
+
+ break
+
+ case "transact":
+ require(5)
+
+ var tx = eth.transact(data.args)
+ postData(data._id, tx)
+
+ break
+
+ case "eth_stateAt":
+ require(2);
+
+ var storage = eth.storageAt(data.args[0], data.args[1]);
+ postData(data._id, storage)
+
+ break
+
+ case "eth_call":
+ require(1);
+ var ret = eth.call(data.args)
+ postData(data._id, ret)
+ break
+
+ case "eth_balanceAt":
+ require(1);
+
+ postData(data._id, eth.balanceAt(data.args[0]));
+ break
+
+ case "eth_watch":
+ require(2)
+ eth.watch(data.args[0], data.args[1])
+
+ case "eth_disconnect":
+ require(1)
+ postData(data._id, null)
+ break;
+
+ case "eth_newFilterString":
+ require(1)
+ var id = eth.newFilterString(data.args[0], window)
+ postData(data._id, id);
+ break;
+
+ case "eth_newFilter":
+ require(1)
+ var id = eth.newFilter(data.args[0], window)
+
+ postData(data._id, id);
+ break;
+
+ case "eth_filterLogs":
+ require(1);
+
+ var messages = eth.messages(data.args[0]);
+ var m = JSON.parse(JSON.parse(JSON.stringify(messages)))
+ postData(data._id, m);
+
+ break;
+
+ case "eth_deleteFilter":
+ require(1);
+ eth.uninstallFilter(data.args[0])
+ break;
+
+
+ case "shh_newFilter":
+ require(1);
+ var id = shh.watch(data.args[0], window);
+ postData(data._id, id);
+ break;
+
+ case "shh_newIdentity":
+ var id = shh.newIdentity()
+ postData(data._id, id)
+
+ break
+
+ case "shh_post":
+ require(1);
+
+ var params = data.args[0];
+ var fields = ["payload", "to", "from"];
+ for(var i = 0; i < fields.length; i++) {
+ params[fields[i]] = params[fields[i]] || "";
+ }
+ if(typeof params.payload !== "object") { params.payload = [params.payload]; } //params.payload = params.payload.join(""); }
+ params.topics = params.topics || [];
+ params.priority = params.priority || 1000;
+ params.ttl = params.ttl || 100;
+
+ shh.post(params.payload, params.to, params.from, params.topics, params.priority, params.ttl);
+
+ break;
+
+ case "shh_getMessages":
+ require(1);
+
+ var m = shh.messages(data.args[0]);
+ var messages = JSON.parse(JSON.parse(JSON.stringify(m)));
+ postData(data._id, messages);
+
+ break;
+
+ case "ssh_newGroup":
+ postData(data._id, "");
+ break;
+ }
+ } catch(e) {
+ console.log(data.call + ": " + e)
+
+ postData(data._id, null);
+ }
+ }
+
+ function post(seed, data) {
+ postData(data._id, 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, _id: seed}))
+ }
+ function postEvent(event, id, data) {
+ webview.experimental.postMessage(JSON.stringify({data: data, _id: id, _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
+ }
+ }
+ ]
+ }
+}
diff --git a/cmd/mist/assets/qml/first_run.qml b/cmd/mist/assets/qml/first_run.qml
new file mode 100644
index 000000000..0b1dac4c6
--- /dev/null
+++ b/cmd/mist/assets/qml/first_run.qml
@@ -0,0 +1,155 @@
+import QtQuick 2.0
+import Ethereum 1.0
+
+// Which ones do we actually need?
+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 QtQuick.Dialogs 1.1
+
+ApplicationWindow {
+ id: wizardRoot
+ width: 500
+ height: 400
+ title: "Ethereal first run setup"
+
+ Column {
+ spacing: 5
+ anchors.leftMargin: 10
+ anchors.left: parent.left
+
+ Text {
+ visible: true
+ text: "<h2>Ethereal setup</h2>"
+ }
+
+ Column {
+ id: restoreColumn
+ spacing: 5
+ Text {
+ visible: true
+ font.pointSize: 14
+ text: "Restore your Ethereum account"
+ id: restoreLabel
+ }
+
+ TextField {
+ id: txPrivKey
+ width: 480
+ placeholderText: "Private key or mnemonic words"
+ focus: true
+ onTextChanged: {
+ if(this.text.length == 64){
+ detailLabel.text = "Private (hex) key detected."
+ actionButton.enabled = true
+ }
+ else if(this.text.split(" ").length == 24){
+ detailLabel.text = "Mnemonic key detected."
+ actionButton.enabled = true
+ }else{
+ detailLabel.text = ""
+ actionButton.enabled = false
+ }
+ }
+ }
+ Row {
+ spacing: 10
+ Button {
+ id: actionButton
+ text: "Restore"
+ enabled: false
+ onClicked: {
+ var success = lib.importAndSetPrivKey(txPrivKey.text)
+ if(success){
+ importedDetails.visible = true
+ restoreColumn.visible = false
+ newKey.visible = false
+ wizardRoot.height = 120
+ }
+ }
+ }
+ Text {
+ id: detailLabel
+ font.pointSize: 12
+ anchors.topMargin: 10
+ }
+ }
+ }
+ Column {
+ id: importedDetails
+ visible: false
+ Text {
+ text: "<b>Your account has been imported. Please close the application and restart it again to let the changes take effect.</b>"
+ wrapMode: Text.WordWrap
+ width: 460
+ }
+ }
+ Column {
+ spacing: 5
+ id: newDetailsColumn
+ visible: false
+ Text {
+ font.pointSize: 14
+ text: "Your account details"
+ }
+ Label {
+ text: "Address"
+ }
+ TextField {
+ id: addressInput
+ readOnly:true
+ width: 480
+ }
+ Label {
+ text: "Private key"
+ }
+ TextField {
+ id: privkeyInput
+ readOnly:true
+ width: 480
+ }
+ Label {
+ text: "Mnemonic words"
+ }
+ TextField {
+ id: mnemonicInput
+ readOnly:true
+ width: 480
+ }
+ Label {
+ text: "<b>A new account has been created. Please take the time to write down the <i>24 words</i>. You can use those to restore your account at a later date.</b>"
+ wrapMode: Text.WordWrap
+ width: 480
+ }
+ Label {
+ text: "Please restart the application once you have completed the steps above."
+ wrapMode: Text.WordWrap
+ width: 480
+ }
+ }
+
+ }
+ Button {
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ anchors.rightMargin: 10
+ anchors.bottomMargin: 10
+ id: newKey
+ text: "I don't have an account yet"
+ onClicked: {
+ var res = lib.createAndSetPrivKey()
+ mnemonicInput.text = res[0]
+ addressInput.text = res[1]
+ privkeyInput.text = res[2]
+
+ // Hide restore
+ restoreColumn.visible = false
+
+ // Show new details
+ newDetailsColumn.visible = true
+ newKey.visible = false
+ }
+ }
+}
diff --git a/cmd/mist/assets/qml/fonts/Simple-Line-Icons.ttf b/cmd/mist/assets/qml/fonts/Simple-Line-Icons.ttf
new file mode 100755
index 000000000..2194f1f87
--- /dev/null
+++ b/cmd/mist/assets/qml/fonts/Simple-Line-Icons.ttf
Binary files differ
diff --git a/cmd/mist/assets/qml/fonts/SourceSansPro-Black.ttf b/cmd/mist/assets/qml/fonts/SourceSansPro-Black.ttf
new file mode 100644
index 000000000..be1a3108e
--- /dev/null
+++ b/cmd/mist/assets/qml/fonts/SourceSansPro-Black.ttf
Binary files differ
diff --git a/cmd/mist/assets/qml/fonts/SourceSansPro-BlackIt.ttf b/cmd/mist/assets/qml/fonts/SourceSansPro-BlackIt.ttf
new file mode 100644
index 000000000..ac5c4ef7c
--- /dev/null
+++ b/cmd/mist/assets/qml/fonts/SourceSansPro-BlackIt.ttf
Binary files differ
diff --git a/cmd/mist/assets/qml/fonts/SourceSansPro-Bold.ttf b/cmd/mist/assets/qml/fonts/SourceSansPro-Bold.ttf
new file mode 100644
index 000000000..f47161c6b
--- /dev/null
+++ b/cmd/mist/assets/qml/fonts/SourceSansPro-Bold.ttf
Binary files differ
diff --git a/cmd/mist/assets/qml/fonts/SourceSansPro-BoldIt.ttf b/cmd/mist/assets/qml/fonts/SourceSansPro-BoldIt.ttf
new file mode 100644
index 000000000..6b3db698b
--- /dev/null
+++ b/cmd/mist/assets/qml/fonts/SourceSansPro-BoldIt.ttf
Binary files differ
diff --git a/cmd/mist/assets/qml/fonts/SourceSansPro-ExtraLight.ttf b/cmd/mist/assets/qml/fonts/SourceSansPro-ExtraLight.ttf
new file mode 100644
index 000000000..0a3e51fdb
--- /dev/null
+++ b/cmd/mist/assets/qml/fonts/SourceSansPro-ExtraLight.ttf
Binary files differ
diff --git a/cmd/mist/assets/qml/fonts/SourceSansPro-ExtraLightIt.ttf b/cmd/mist/assets/qml/fonts/SourceSansPro-ExtraLightIt.ttf
new file mode 100644
index 000000000..a0eb86aca
--- /dev/null
+++ b/cmd/mist/assets/qml/fonts/SourceSansPro-ExtraLightIt.ttf
Binary files differ
diff --git a/cmd/mist/assets/qml/fonts/SourceSansPro-It.ttf b/cmd/mist/assets/qml/fonts/SourceSansPro-It.ttf
new file mode 100644
index 000000000..fcc95fc7c
--- /dev/null
+++ b/cmd/mist/assets/qml/fonts/SourceSansPro-It.ttf
Binary files differ
diff --git a/cmd/mist/assets/qml/fonts/SourceSansPro-Light.ttf b/cmd/mist/assets/qml/fonts/SourceSansPro-Light.ttf
new file mode 100644
index 000000000..9cae13c97
--- /dev/null
+++ b/cmd/mist/assets/qml/fonts/SourceSansPro-Light.ttf
Binary files differ
diff --git a/cmd/mist/assets/qml/fonts/SourceSansPro-LightIt.ttf b/cmd/mist/assets/qml/fonts/SourceSansPro-LightIt.ttf
new file mode 100644
index 000000000..2ed784284
--- /dev/null
+++ b/cmd/mist/assets/qml/fonts/SourceSansPro-LightIt.ttf
Binary files differ
diff --git a/cmd/mist/assets/qml/fonts/SourceSansPro-Regular.ttf b/cmd/mist/assets/qml/fonts/SourceSansPro-Regular.ttf
new file mode 100644
index 000000000..8e8255e17
--- /dev/null
+++ b/cmd/mist/assets/qml/fonts/SourceSansPro-Regular.ttf
Binary files differ
diff --git a/cmd/mist/assets/qml/fonts/SourceSansPro-Semibold.ttf b/cmd/mist/assets/qml/fonts/SourceSansPro-Semibold.ttf
new file mode 100644
index 000000000..121ee9bbf
--- /dev/null
+++ b/cmd/mist/assets/qml/fonts/SourceSansPro-Semibold.ttf
Binary files differ
diff --git a/cmd/mist/assets/qml/fonts/SourceSansPro-SemiboldIt.ttf b/cmd/mist/assets/qml/fonts/SourceSansPro-SemiboldIt.ttf
new file mode 100644
index 000000000..6ceaa885f
--- /dev/null
+++ b/cmd/mist/assets/qml/fonts/SourceSansPro-SemiboldIt.ttf
Binary files differ
diff --git a/cmd/mist/assets/qml/fonts/SourceSerifPro-Bold.ttf b/cmd/mist/assets/qml/fonts/SourceSerifPro-Bold.ttf
new file mode 100644
index 000000000..ac7837fd9
--- /dev/null
+++ b/cmd/mist/assets/qml/fonts/SourceSerifPro-Bold.ttf
Binary files differ
diff --git a/cmd/mist/assets/qml/fonts/SourceSerifPro-Regular.ttf b/cmd/mist/assets/qml/fonts/SourceSerifPro-Regular.ttf
new file mode 100644
index 000000000..7201a8890
--- /dev/null
+++ b/cmd/mist/assets/qml/fonts/SourceSerifPro-Regular.ttf
Binary files differ
diff --git a/cmd/mist/assets/qml/fonts/SourceSerifPro-Semibold.ttf b/cmd/mist/assets/qml/fonts/SourceSerifPro-Semibold.ttf
new file mode 100644
index 000000000..db2fc804b
--- /dev/null
+++ b/cmd/mist/assets/qml/fonts/SourceSerifPro-Semibold.ttf
Binary files differ
diff --git a/cmd/mist/assets/qml/main.qml b/cmd/mist/assets/qml/main.qml
new file mode 100644
index 000000000..a909916b7
--- /dev/null
+++ b/cmd/mist/assets/qml/main.qml
@@ -0,0 +1,1043 @@
+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
+
+ //flags: Qt.FramelessWindowHint
+ // Use this to make the window frameless. But then you'll need to do move and resize by hand
+
+ property var ethx : Eth.ethx
+ property var catalog;
+
+ width: 1200
+ height: 820
+ minimumHeight: 600
+ minimumWidth: 800
+
+ title: "Mist"
+
+ TextField {
+ id: copyElementHax
+ visible: false
+ }
+
+ function copyToClipboard(text) {
+ copyElementHax.text = text
+ copyElementHax.selectAll()
+ copyElementHax.copy()
+ }
+
+ // Takes care of loading all default plugins
+ Component.onCompleted: {
+
+ catalog = addPlugin("./views/catalog.qml", {noAdd: true, close: false, section: "begin", active: true});
+
+ var walletWeb = addPlugin("./views/browser.qml", {noAdd: true, close: false, section: "ethereum", active: false});
+ walletWeb.view.url = "http://ethereum-dapp-wallet.meteor.com/";
+ walletWeb.menuItem.title = "Wallet";
+
+ addPlugin("./views/miner.qml", {noAdd: true, close: false, section: "ethereum", active: false});
+ addPlugin("./views/transaction.qml", {noAdd: true, close: false, section: "legacy"});
+ addPlugin("./views/whisper.qml", {noAdd: true, close: false, section: "legacy"});
+ addPlugin("./views/chain.qml", {noAdd: true, close: false, section: "legacy"});
+ addPlugin("./views/pending_tx.qml", {noAdd: true, close: false, section: "legacy"});
+ addPlugin("./views/info.qml", {noAdd: true, close: false, section: "legacy"});
+
+ mainSplit.setView(catalog.view, catalog.menuItem);
+
+ //newBrowserTab("http://ethereum-dapp-catalog.meteor.com");
+
+ // Command setup
+ gui.sendCommand(0)
+ }
+
+ function activeView(view, menuItem) {
+ mainSplit.setView(view, menuItem)
+ /*if (view.hideUrl) {
+ urlPane.visible = false;
+ mainView.anchors.top = rootView.top
+ } else {
+ urlPane.visible = true;
+ mainView.anchors.top = divider.bottom
+ }*/
+
+ }
+
+ 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) {
+ console.log(e)
+ }
+ }
+
+ function newBrowserTab(url) {
+
+ var urlMatches = url.toString().match(/^[a-z]*\:\/\/([^\/?#]+)(?:[\/?#]|$)/i);
+ var requestedDomain = urlMatches && urlMatches[1];
+
+ var domainAlreadyOpen = false;
+
+ for(var i = 0; i < mainSplit.views.length; i++) {
+ if (mainSplit.views[i].view.url) {
+ var matches = mainSplit.views[i].view.url.toString().match(/^[a-z]*\:\/\/(?:www\.)?([^\/?#]+)(?:[\/?#]|$)/i);
+ var existingDomain = matches && matches[1];
+ if (requestedDomain == existingDomain) {
+ domainAlreadyOpen = true;
+
+ if (mainSplit.views[i].view.url != url){
+ mainSplit.views[i].view.url = url;
+ }
+
+ activeView(mainSplit.views[i].view, mainSplit.views[i].menuItem);
+ }
+ }
+ }
+
+ if (!domainAlreadyOpen) {
+ var window = addPlugin("./views/browser.qml", {noAdd: true, close: true, section: "apps", active: true});
+ window.view.url = url;
+ window.menuItem.title = "Mist";
+ activeView(window.view, window.menuItem);
+ }
+ }
+
+
+
+ menuBar: MenuBar {
+ Menu {
+ title: "File"
+ MenuItem {
+ text: "New tab"
+ shortcut: "Ctrl+t"
+ onTriggered: {
+ activeView(catalog.view, catalog.menuItem);
+ }
+ }
+
+ 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 {}
+ }
+
+ 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
+ }
+ }
+ }
+
+ }
+
+ property var blockModel: ListModel {
+ id: blockModel
+ }
+
+ SplitView {
+ property var views: [];
+
+ id: mainSplit
+ anchors.fill: parent
+ //resizing: false // this is NOT where we remove that damning resizing handle..
+ handleDelegate: Item {
+ //This handle is a way to remove the line between the split views
+ Rectangle {
+ anchors.fill: parent
+ }
+ }
+
+ 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.setSelection(true)
+ }
+
+ function addComponent(view, options) {
+ view.visible = false
+ view.anchors.fill = mainView
+
+ var menuItem = menu.createMenuItem(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: 192
+ Layout.maximumWidth: 192
+
+ FontLoader {
+ id: sourceSansPro
+ source: "fonts/SourceSansPro-Regular.ttf"
+ }
+ FontLoader {
+ source: "fonts/SourceSansPro-Semibold.ttf"
+ }
+ FontLoader {
+ source: "fonts/SourceSansPro-Bold.ttf"
+ }
+ FontLoader {
+ source: "fonts/SourceSansPro-Black.ttf"
+ }
+ FontLoader {
+ source: "fonts/SourceSansPro-Light.ttf"
+ }
+ FontLoader {
+ source: "fonts/SourceSansPro-ExtraLight.ttf"
+ }
+ FontLoader {
+ id: simpleLineIcons
+ source: "fonts/Simple-Line-Icons.ttf"
+ }
+
+ Rectangle {
+ color: "steelblue"
+ anchors.fill: parent
+
+ MouseArea {
+ anchors.fill: parent
+ property real lastMouseX: 0
+ property real lastMouseY: 0
+ onPressed: {
+ lastMouseX = mouseX
+ lastMouseY = mouseY
+ }
+ onPositionChanged: {
+ root.x += (mouseX - lastMouseX)
+ root.y += (mouseY - lastMouseY)
+ }
+ /*onDoubleClicked: {
+ //!maximized ? view.set_max() : view.set_normal()}
+ visibility = "Minimized"
+ }*/
+ }
+ }
+
+
+
+ anchors.top: parent.top
+ Rectangle {
+ width: parent.height
+ height: parent.width
+ anchors.centerIn: parent
+ rotation: 90
+
+ gradient: Gradient {
+ GradientStop { position: 0.0; color: "#E2DEDE" }
+ GradientStop { position: 0.1; color: "#EBE8E8" }
+ GradientStop { position: 1.0; color: "#EBE8E8" }
+ }
+ }
+
+ Component {
+ id: menuItemTemplate
+ Rectangle {
+ id: menuItem
+ property var view;
+ property var path;
+ property var closable;
+ property var badgeContent;
+
+ property alias title: label.text
+ property alias icon: icon.source
+ property alias secondaryTitle: secondary.text
+ property alias badgeNumber: badgeNumberLabel.text
+ property alias badgeIcon: badgeIconLabel.text
+
+ function setSelection(on) {
+ sel.visible = on
+
+ if (this.closable == true) {
+ closeIcon.visible = on
+ }
+ }
+
+ function setAsBigButton(on) {
+ newAppButton.visible = on
+ label.visible = !on
+ buttonLabel.visible = on
+ }
+
+ width: 192
+ height: 55
+ color: "#00000000"
+
+ anchors {
+ left: parent.left
+ leftMargin: 4
+ }
+
+ Rectangle {
+ // New App Button
+ id: newAppButton
+ visible: false
+ anchors.fill: parent
+ anchors.rightMargin: 8
+ border.width: 0
+ radius: 5
+ height: 55
+ width: 180
+ color: "#F3F1F3"
+ }
+
+ Rectangle {
+ id: sel
+ visible: false
+ anchors.fill: parent
+ color: "#00000000"
+ Rectangle {
+ id: r
+ anchors.fill: parent
+ border.width: 0
+ radius: 5
+ color: "#FAFAFA"
+ }
+ Rectangle {
+ anchors {
+ top: r.top
+ bottom: r.bottom
+ right: r.right
+ }
+ width: 10
+ color: "#FAFAFA"
+ border.width:0
+
+ Rectangle {
+ // Small line on top of selection. What's this for?
+ anchors {
+ left: parent.left
+ right: parent.right
+ top: parent.top
+ }
+ height: 1
+ color: "#FAFAFA"
+ }
+
+ Rectangle {
+ // Small line on bottom of selection. What's this for again?
+ anchors {
+ left: parent.left
+ right: parent.right
+ bottom: parent.bottom
+ }
+ height: 1
+ color: "#FAFAFA"
+ }
+ }
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ hoverEnabled: true
+ onClicked: {
+ activeView(view, menuItem);
+ }
+ onEntered: {
+ if (parent.closable == true) {
+ closeIcon.visible = sel.visible
+ }
+ }
+ onExited: {
+ closeIcon.visible = false
+ }
+ }
+
+ Image {
+ id: icon
+ height: 28
+ width: 28
+ anchors {
+ left: parent.left
+ verticalCenter: parent.verticalCenter
+ leftMargin: 6
+ }
+ }
+
+ Text {
+ id: buttonLabel
+ visible: false
+ text: "GO TO NEW APP"
+ font.family: sourceSansPro.name
+ font.weight: Font.DemiBold
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.verticalCenter: parent.verticalCenter
+ color: "#AAA0A0"
+ }
+
+ Text {
+ id: label
+ font.family: sourceSansPro.name
+ font.weight: Font.DemiBold
+ elide: Text.ElideRight
+ x:250
+ color: "#665F5F"
+ font.pixelSize: 14
+ anchors {
+ left: icon.right
+ right: parent.right
+ verticalCenter: parent.verticalCenter
+ leftMargin: 6
+ rightMargin: 8
+ verticalCenterOffset: (secondaryTitle == "") ? 0 : -10;
+ }
+
+
+
+
+ }
+
+ Text {
+ id: secondary
+ font.family: sourceSansPro.name
+ font.weight: Font.Light
+ anchors {
+ left: icon.right
+ leftMargin: 6
+ top: label.bottom
+ }
+ color: "#6691C2"
+ font.pixelSize: 12
+ }
+
+ Rectangle {
+ id: closeIcon
+ visible: false
+ width: 10
+ height: 10
+ color: "#FAFAFA"
+ anchors {
+ fill: icon
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ menuItem.closeApp()
+ }
+ }
+
+ Text {
+
+ font.family: simpleLineIcons.name
+ anchors {
+ centerIn: parent
+ }
+ color: "#665F5F"
+ font.pixelSize: 20
+ text: "\ue082"
+ }
+ }
+
+ Rectangle {
+ id: badge
+ visible: (badgeContent == "icon" || badgeContent == "number" )? true : false
+ width: 32
+ color: "#05000000"
+ anchors {
+ right: parent.right;
+ top: parent.top;
+ bottom: parent.bottom;
+ rightMargin: 4;
+ }
+
+ Text {
+ id: badgeIconLabel
+ visible: (badgeContent == "icon") ? true : false;
+ font.family: simpleLineIcons.name
+ anchors {
+ centerIn: parent
+ }
+ horizontalAlignment: Text.AlignCenter
+ color: "#AAA0A0"
+ font.pixelSize: 20
+ text: badgeIcon
+ }
+
+ Text {
+ id: badgeNumberLabel
+ visible: (badgeContent == "number") ? true : false;
+ anchors {
+ centerIn: parent
+ }
+ horizontalAlignment: Text.AlignCenter
+ font.family: sourceSansPro.name
+ font.weight: Font.Light
+ color: "#AAA0A0"
+ font.pixelSize: 18
+ text: badgeNumber
+ }
+ }
+
+
+
+ function closeApp() {
+ if(!this.closable) { return; }
+
+ if(this.view.hasOwnProperty("onDestroy")) {
+ this.view.onDestroy.call(this.view)
+ }
+
+ this.view.destroy()
+ this.destroy()
+ for (var i = 0; i < mainSplit.views.length; i++) {
+ var view = mainSplit.views[i];
+ if (view.menuItem === this) {
+ mainSplit.views.splice(i, 1);
+ break;
+ }
+ }
+ gui.removePlugin(this.path)
+ activeView(mainSplit.views[0].view, mainSplit.views[0].menuItem);
+ }
+ }
+ }
+
+ function createMenuItem(view, options) {
+ if(options === undefined) {
+ options = {};
+ }
+
+ var section;
+ switch(options.section) {
+ case "begin":
+ section = menuBegin
+ break;
+ 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
+
+ if(view.hasOwnProperty("iconSource")) {
+ comp.icon = view.iconSource;
+ }
+ comp.closable = options.close;
+
+ if (options.section === "begin") {
+ comp.setAsBigButton(true)
+ }
+
+ return comp
+ }
+
+ ColumnLayout {
+ id: menuColumn
+ y: 10
+ width: parent.width
+ anchors.left: parent.left
+ anchors.right: parent.right
+ spacing: 3
+
+
+
+ ColumnLayout {
+ id: menuBegin
+ spacing: 3
+ anchors {
+ left: parent.left
+ right: parent.right
+ }
+ }
+
+ Rectangle {
+ height: 55
+ color: "transparent"
+ Text {
+ text: "ETHEREUM"
+ font.family: sourceSansPro.name
+ font.weight: Font.DemiBold
+ anchors {
+ left: parent.left
+ top: parent.verticalCenter
+ leftMargin: 16
+ }
+ color: "#AAA0A0"
+ }
+ }
+
+
+ ColumnLayout {
+ id: menuDefault
+ spacing: 3
+ anchors {
+ left: parent.left
+ right: parent.right
+ }
+ }
+
+ Rectangle {
+ height: 55
+ color: "transparent"
+ Text {
+ text: "APPS"
+ font.family: sourceSansPro.name
+ font.weight: Font.DemiBold
+ anchors {
+ left: parent.left
+ top: parent.verticalCenter
+ leftMargin: 16
+ }
+ color: "#AAA0A0"
+ }
+ }
+
+ ColumnLayout {
+ id: menuApps
+ spacing: 3
+ anchors {
+ left: parent.left
+ right: parent.right
+ }
+ }
+
+ Rectangle {
+ height: 55
+ color: "transparent"
+ Text {
+ text: "DEBUG"
+ font.family: sourceSansPro.name
+ font.weight: Font.DemiBold
+ anchors {
+ left: parent.left
+ top: parent.verticalCenter
+ leftMargin: 16
+ }
+ color: "#AAA0A0"
+ }
+ }
+
+
+ ColumnLayout {
+ id: menuLegacy
+ spacing: 3
+ anchors {
+ left: parent.left
+ right: parent.right
+ }
+ }
+ }
+ }
+
+ /*********************
+ * Main view
+ ********************/
+ Rectangle {
+ id: rootView
+ 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: {
+ if(/^https?/.test(this.text)) {
+ newBrowserTab(this.text);
+ } else {
+ 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: 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, {close: true, section: "apps"})
+ }
+ }
+
+ function setWalletValue(value) {
+ //walletValueLabel.text = value
+ }
+
+ function loadPlugin(name) {
+ console.log("Loading plugin" + name)
+ var view = mainView.addPlugin(name)
+ }
+
+ function clearPeers() { peerModel.clear() }
+ function addPeer(peer) { peerModel.append(peer) }
+
+ function setPeerCounters(text) {
+ //peerCounterLabel.text = text
+ }
+
+ 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: 180; role: "addr" ; title: "Remote Address" }
+ TableViewColumn{width: 280; role: "nodeID" ; title: "Node ID" }
+ TableViewColumn{width: 100; role: "name" ; title: "Name" }
+ TableViewColumn{width: 40; role: "caps" ; title: "Capabilities" }
+ }
+ }
+ }
+
+ Window {
+ id: aboutWin
+ visible: false
+ title: "About"
+ minimumWidth: 350
+ maximumWidth: 350
+ maximumHeight: 280
+ minimumHeight: 280
+
+ Image {
+ id: aboutIcon
+ height: 150
+ width: 150
+ fillMode: Image.PreserveAspectFit
+ smooth: true
+ source: "../facet.png"
+ x: 10
+ y: 30
+ }
+
+ Text {
+ anchors.left: aboutIcon.right
+ anchors.leftMargin: 10
+ anchors.top: parent.top
+ anchors.topMargin: 30
+ font.pointSize: 12
+ text: "<h2>Mist (0.8.6)</h2><br><h3>Development</h3>Jeffrey Wilcke<br>Viktor Trón<br>Felix Lange<br>Taylor Gerring<br>Daniel Nagy<br>Gustav Simonsson<br><h3>UX/UI</h3>Alex van de Sande<br>Fabian Vogelsteller"
+ }
+ }
+
+ 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: 400
+ maximumWidth: 400
+ maximumHeight: 50
+ minimumHeight: 50
+ title: "Connect to peer"
+
+ TextField {
+ id: addrField
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.left: parent.left
+ anchors.right: addPeerButton.left
+ anchors.leftMargin: 10
+ anchors.rightMargin: 10
+ placeholderText: "enode://<hex node id>:<IP address>:<port>"
+ onAccepted: {
+ if(addrField.text.length != 0) {
+ eth.connectToPeer(addrField.text)
+ addPeerWin.visible = false
+ }
+ }
+ }
+
+ Button {
+ id: addPeerButton
+ anchors.right: parent.right
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.rightMargin: 10
+ text: "Connect"
+ onClicked: {
+ if(addrField.text.length != 0) {
+ eth.connectToPeer(addrField.text)
+ addPeerWin.visible = false
+ }
+ }
+ }
+ Component.onCompleted: {
+ addrField.focus = true
+ }
+ }
+ }
diff --git a/cmd/mist/assets/qml/muted.qml b/cmd/mist/assets/qml/muted.qml
new file mode 100644
index 000000000..fac8267c4
--- /dev/null
+++ b/cmd/mist/assets/qml/muted.qml
@@ -0,0 +1,74 @@
+import QtQuick 2.0
+import QtWebKit 3.0
+import QtWebKit.experimental 1.0
+import QtQuick.Controls 1.0;
+import QtQuick.Layouts 1.0;
+import QtQuick.Window 2.1;
+import Ethereum 1.0
+
+ApplicationWindow {
+ id: window
+ title: "muted"
+ width: 900
+ height: 600
+ minimumHeight: 300
+
+ property alias url: webView.url
+ property alias webView: webView
+
+
+ Item {
+ id: root
+ anchors.fill: parent
+ WebView {
+ objectName: "webView"
+ id: webView
+ anchors {
+ top: root.top
+ right: root.right
+ left: root.left
+ bottom: root.bottom
+ //bottom: sizeGrip.top
+ }
+
+ experimental.preferences.javascriptEnabled: true
+ experimental.preferences.navigatorQtObjectEnabled: true
+ experimental.onMessageReceived: {
+ var data = JSON.parse(message.data)
+
+ switch(data.call) {
+ case "log":
+ console.log.apply(this, data.args)
+ break;
+ }
+ }
+ 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}))
+ }
+ }
+
+ /*
+ Rectangle {
+ id: sizeGrip
+ color: "gray"
+ height: 5
+ 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 - sizeGrip.height
+ drag.axis: Drag.YAxis
+ }
+ }
+ */
+ }
+}
diff --git a/cmd/mist/assets/qml/test_app.qml b/cmd/mist/assets/qml/test_app.qml
new file mode 100644
index 000000000..c69587839
--- /dev/null
+++ b/cmd/mist/assets/qml/test_app.qml
@@ -0,0 +1,70 @@
+import QtQuick 2.0
+import QtQuick.Controls 1.0;
+import QtQuick.Layouts 1.0;
+import Ethereum 1.0
+
+QmlApp {
+ minimumWidth: 350
+ maximumWidth: 350
+ maximumHeight: 80
+ minimumHeight: 80
+
+ title: "Generic Coin"
+
+ property string contractAddr: "f299f6c74515620e4c4cd8fe3d205b5c4f2e25c8"
+ property string addr: "2ef47100e0787b915105fd5e3f4ff6752079d5cb"
+
+ Component.onCompleted: {
+ eth.watch(contractAddr, addr)
+ eth.watch(addr, contractAddr)
+ setAmount()
+ }
+
+ function onStorageChangeCb(storageObject) {
+ setAmount()
+ }
+
+ function setAmount(){
+ var state = eth.getStateObject(contractAddr)
+ var storage = state.getStorage(addr)
+ amountLabel.text = storage
+ }
+ Column {
+ spacing: 5
+ Row {
+ spacing: 20
+ Label {
+ id: genLabel
+ text: "Generic coin balance:"
+ }
+ Label {
+ id: amountLabel
+ }
+ }
+ Row {
+ spacing: 20
+ TextField {
+ id: address
+ placeholderText: "Address"
+ }
+ TextField {
+ id: amount
+ placeholderText: "Amount"
+ }
+ }
+ Button {
+ text: "Send coins"
+ onClicked: {
+ var privKey = eth.getKey().privateKey
+ if(privKey){
+ var result = eth.transact(privKey, contractAddr, 0,"100000","250", "0x" + address.text + "\n" + amount.text)
+ resultTx.text = result.hash
+ }
+ }
+ }
+ Label {
+ id: resultTx
+ }
+ }
+
+}
diff --git a/cmd/mist/assets/qml/transactions.qml b/cmd/mist/assets/qml/transactions.qml
new file mode 100644
index 000000000..e9a035a85
--- /dev/null
+++ b/cmd/mist/assets/qml/transactions.qml
@@ -0,0 +1,9 @@
+import QtQuick 2.0
+import QtQuick.Controls 1.0;
+import QtQuick.Layouts 1.0;
+
+Rectangle {
+ id: transactionView
+ visible: false
+ Text { text: "TX VIEW" }
+}
diff --git a/cmd/mist/assets/qml/views/browser.qml b/cmd/mist/assets/qml/views/browser.qml
new file mode 100644
index 000000000..edecc8696
--- /dev/null
+++ b/cmd/mist/assets/qml/views/browser.qml
@@ -0,0 +1,506 @@
+import QtQuick 2.0
+import QtQuick.Controls 1.0;
+import QtQuick.Controls.Styles 1.0
+import QtQuick.Layouts 1.0;
+import QtWebEngine 1.0
+import QtWebEngine.experimental 1.0
+import QtQuick.Window 2.0;
+
+Rectangle {
+ id: window
+ anchors.fill: parent
+ color: "#00000000"
+
+ property var title: ""
+ property var iconSource: "../browser.png"
+ property var menuItem
+ property var hideUrl: true
+
+ property alias url: webview.url
+ property alias windowTitle: webview.title
+ property alias webView: webview
+
+ property var cleanPath: false
+ property var open: function(url) {
+ if(!window.cleanPath) {
+ var uri = url;
+ 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;
+ });
+ }
+
+ window.cleanPath = true;
+
+ webview.url = uri;
+
+ //uriNav.text = uri.text.replace(/(^https?\:\/\/(?:www\.)?)([a-zA-Z0-9_\-]*\.\w{2,3})(.*)/, "$1$2<span style='color:#CCC'>$3</span>");
+ uriNav.text = uri;
+
+ } else {
+ // Prevent inf loop.
+ window.cleanPath = false;
+ }
+ }
+
+ function showFullUrlBar(on){
+ if (uriNav.focus == false ) {
+ if (on == false) {
+ clickAnywhereOnApp.visible = false
+ navBar.state = "titleVisible"
+ } else {
+ clickAnywhereOnApp.visible = true
+ navBar.state = "fullUrlVisible"
+ }
+ }
+
+ }
+
+ Component.onCompleted: {
+ }
+
+ Item {
+ objectName: "root"
+ id: root
+ anchors {
+ fill: parent
+ }
+
+ state: "inspectorShown"
+
+ MouseArea {
+ id: clickAnywhereOnApp
+ z:15
+ // Using a secondary screen to catch on mouse exits for the area, because
+ // there are many hover actions conflicting
+
+ anchors {
+ top: parent.top
+ topMargin: 50
+ right: parent.right
+ bottom: parent.bottom
+ left: parent.left
+ }
+ hoverEnabled: true
+
+ onEntered: {
+ showFullUrlBar(false);
+ }
+
+ onClicked: {
+ uriNav.focus = false
+ showFullUrlBar(false);
+ }
+
+ // Rectangle {
+ // anchors.fill: parent
+ // color: "#88888888"
+ // }
+ }
+
+ RowLayout {
+ id: navBar
+ height: 74
+ z: 20
+ anchors {
+ left: parent.left
+ right: parent.right
+ }
+
+ Button {
+ id: back
+ z: 30
+ onClicked: {
+ webview.goBack()
+ }
+
+ anchors {
+ left: parent.left
+ leftMargin: 6
+ }
+
+ style: ButtonStyle {
+ background: Image {
+ source: (webview.canGoBack) ?
+ (control.hovered ? "../../backButtonHover.png" : "../../backButton.png") :
+ "../../backButtonDisabled.png"
+ width: 20
+ height: 30
+ }
+ }
+ }
+
+ Rectangle {
+ id: appInfoPane
+ height: 28
+ color: "#FFFFFF"
+ radius: 6
+ z:2
+ MouseArea {
+ anchors.fill: parent
+ z: 10
+ hoverEnabled: true
+
+ onEntered: {
+ showFullUrlBar(true);
+ }
+ /*onExited: {
+ showFullUrlBar(false);
+ }*/
+
+ }
+
+ anchors {
+ left: back.right
+ right: parent.right
+ leftMargin: 10
+ rightMargin: 10
+ }
+
+ Text {
+ id: appTitle
+ text: "LOADING"
+ font.bold: true
+ font.capitalization: Font.AllUppercase
+ horizontalAlignment: Text.AlignRight
+ verticalAlignment: Text.AlignVCenter
+ elide: Text.ElideRight
+
+ anchors {
+ left: parent.left
+ right: parent.horizontalCenter
+ top: parent.top
+ bottom: parent.bottom
+ leftMargin: 32
+ }
+ color: "#928484"
+ }
+
+ Text {
+ id: appDomain
+ text: "loading domain"
+ font.bold: false
+ horizontalAlignment: Text.AlignLeft
+ verticalAlignment: Text.AlignVCenter
+ elide: Text.ElideLeft
+
+ anchors {
+ left: parent.horizontalCenter
+ right: parent.right
+ top: parent.top
+ bottom: parent.bottom
+ leftMargin: 32
+
+ }
+ color: "#C0AFAF"
+ }
+
+
+ TextField {
+ id: uriNav
+ opacity: 0.0
+
+ anchors {
+ left: parent.left
+ right: parent.right
+ leftMargin: 16
+ }
+
+ horizontalAlignment: Text.AlignHCenter
+
+ style: TextFieldStyle {
+ textColor: "#928484"
+ background: Rectangle {
+ border.width: 0
+ color: "transparent"
+ }
+ }
+ text: webview.url;
+ y: parent.height / 2 - this.height / 2
+ z: 20
+ activeFocusOnPress: true
+ Keys.onReturnPressed: {
+ // if there's no http, add it.
+ var url = this.text,
+ matches = url.match(/^([a-z]*\:\/\/)?([^\/.]+)(:?\/)(.*|$)/i),
+ requestedProtocol = (matches && matches[1] != "undefined")? "" : "http://";
+
+ webview.url = requestedProtocol + url;
+ }
+ }
+
+
+ }
+
+ Rectangle {
+ id: appInfoPaneShadow
+ width: 10
+ height: 30
+ color: "#BDB6B6"
+ radius: 6
+ z:1
+
+ anchors {
+ left: back.right
+ right: parent.right
+ leftMargin:10
+ rightMargin:10
+ top: parent.top
+ topMargin: 23
+ }
+ }
+
+ Rectangle {
+ id: navBarBackground
+ anchors.fill: parent
+ z:-1
+ gradient: Gradient {
+ GradientStop { position: 0.0; color: "#F6F1F2" }
+ GradientStop { position: 1.0; color: "#DED5D5" }
+ }
+ }
+
+ states: [
+ State {
+ name: "fullUrlVisible"
+ PropertyChanges {
+ target: appTitle
+ anchors.rightMargin: -50
+ opacity: 0.0
+ }
+ PropertyChanges {
+ target: appDomain
+ anchors.leftMargin: -50
+ opacity: 0.0
+ }
+ PropertyChanges {
+ target: uriNav
+ anchors.leftMargin: 0
+ opacity: 1.0
+ }
+ },
+ State {
+ name: "titleVisible"
+
+ PropertyChanges {
+ target: appTitle
+ anchors.rightMargin: 10
+ opacity: 1.0
+ }
+ PropertyChanges {
+ target: appDomain
+ anchors.leftMargin: 10
+ opacity: 1.0
+ }
+ PropertyChanges {
+ target: uriNav
+ anchors.leftMargin: -50
+ opacity: 0.0
+ }
+ }
+
+ ]
+
+ transitions: [
+ // This adds a transition that defaults to applying to all state changes
+
+ Transition {
+
+ // This applies a default NumberAnimation to any changes a state change makes to x or y properties
+ NumberAnimation {
+ properties: "anchors.leftMargin, anchors.rightMargin, opacity"
+ easing.type: Easing.InOutQuad //Easing.InOutBack
+ duration: 300
+ }
+ }
+ ]
+
+ }
+
+ WebEngineView {
+ objectName: "webView"
+ id: webview
+ experimental.settings.javascriptCanAccessClipboard: true
+ //experimental.settings.localContentCanAccessRemoteUrls: true
+ anchors {
+ left: parent.left
+ right: parent.right
+ bottom: parent.bottom
+ top: navBar.bottom
+ }
+ z: 10
+
+ Timer {
+ interval: 2000; running: true; repeat: true
+ onTriggered: {
+ webview.runJavaScript("try{document.querySelector('meta[name=ethereum-dapp-info]').getAttribute('content')}catch(e){}", function(extraInfo) {
+ if (extraInfo) {
+ menuItem.secondaryTitle = extraInfo;
+ }
+ });
+ webview.runJavaScript("try{document.querySelector('meta[name=ethereum-dapp-badge]').getAttribute('content')}catch(e){}", function(badge) {
+ if (badge) {
+ if (Number(badge)>0 && Number(badge)<999) {
+ menuItem.badgeNumber = Number(badge);
+ menuItem.badgeContent = "number"
+ } else if (badge == "warning") {
+ menuItem.badgeIcon = "\ue00e"
+ menuItem.badgeContent = "icon"
+
+ } else if (badge == "ghost") {
+ menuItem.badgeIcon = "\ue01a"
+ menuItem.badgeContent = "icon"
+
+ } else if (badge == "question") {
+ menuItem.badgeIcon = "\ue05d"
+ menuItem.badgeContent = "icon"
+
+ } else if (badge == "info") {
+ menuItem.badgeIcon = "\ue08b"
+ menuItem.badgeContent = "icon"
+
+ } else if (badge == "check") {
+ menuItem.badgeIcon = "\ue080"
+ menuItem.badgeContent = "icon"
+
+ } else if (badge == "gear") {
+ menuItem.badgeIcon = "\ue09a"
+ menuItem.badgeContent = "icon"
+
+ } else {
+ menuItem.badgeContent = ""
+ }
+ } else {
+ menuItem.badgeContent = ""
+ }
+ });
+ }
+ }
+
+ onLoadingChanged: {
+ if (loadRequest.status == WebEngineView.LoadSucceededStatus) {
+
+ webview.runJavaScript("document.title", function(pageTitle) {
+ menuItem.title = pageTitle;
+ });
+
+ webView.runJavaScript("try{document.querySelector(\"link[rel='icon']\").getAttribute(\"href\")}catch(e){}", function(sideIcon){
+ if(sideIcon){
+ menuItem.icon = webview.url + sideIcon;
+ };
+ });
+
+ webView.runJavaScript("try{document.querySelector(\"meta[name='ethereum-dapp-url-bar-style']\").getAttribute(\"content\")}catch(e){}", function(topBarStyle){
+ if (!topBarStyle) {
+ showFullUrlBar(true);
+ navBarBackground.visible = true;
+ back.visible = true;
+ appInfoPane.anchors.leftMargin = 0;
+ appInfoPaneShadow.anchors.leftMargin = 0;
+ webview.anchors.topMargin = 0;
+ return;
+ }
+
+ if (topBarStyle=="transparent") {
+ // Adjust for a transparent sidebar Dapp
+ navBarBackground.visible = false;
+ back.visible = false;
+ appInfoPane.anchors.leftMargin = -16;
+ appInfoPaneShadow.anchors.leftMargin = -16;
+ webview.anchors.topMargin = -74;
+ webview.runJavaScript("document.querySelector('body').classList.add('ethereum-dapp-url-bar-style-transparent')")
+
+ } else {
+ navBarBackground.visible = true;
+ back.visible = true;
+ appInfoPane.anchors.leftMargin = 0;
+ appInfoPaneShadow.anchors.leftMargin = 0;
+ webview.anchors.topMargin = 0;
+ };
+ });
+
+
+ webview.runJavaScript(eth.readFile("bignumber.min.js"));
+ webview.runJavaScript(eth.readFile("ethereum.js/dist/ethereum.js"));
+ webview.runJavaScript(eth.readFile("mist.js"));
+
+ var cleanTitle = webview.url.toString()
+ var matches = cleanTitle.match(/^[a-z]*\:\/\/([^\/?#]+)(?:[\/?#]|$)/i);
+ var domain = matches && matches[1];
+
+
+ if (domain)
+ appDomain.text = domain //webview.url.replace("a", "z")
+ if (webview.title)
+ appTitle.text = webview.title
+
+ showFullUrlBar(false);
+ }
+ }
+ onJavaScriptConsoleMessage: {
+ console.log(sourceID + ":" + lineNumber + ":" + JSON.stringify(message));
+ }
+ }
+
+ 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
+ }
+ }
+
+ WebEngineView {
+ id: inspector
+ visible: false
+ anchors {
+ left: root.left
+ right: root.right
+ top: sizeGrip.bottom
+ bottom: root.bottom
+ }
+
+ }
+
+ states: [
+ State {
+ name: "inspectorShown"
+ PropertyChanges {
+ target: inspector
+ }
+ }
+ ]
+ }
+}
diff --git a/cmd/mist/assets/qml/views/catalog.qml b/cmd/mist/assets/qml/views/catalog.qml
new file mode 100644
index 000000000..29e133074
--- /dev/null
+++ b/cmd/mist/assets/qml/views/catalog.qml
@@ -0,0 +1,144 @@
+import QtQuick 2.0
+import QtQuick.Controls 1.0;
+import QtQuick.Controls.Styles 1.0
+import QtQuick.Layouts 1.0;
+import QtWebEngine 1.0
+import QtWebEngine.experimental 1.0
+import QtQuick.Window 2.0;
+
+
+Rectangle {
+ id: window
+ anchors.fill: parent
+ color: "#00000000"
+
+ property var title: "Catalog"
+ property var iconSource: ""
+ property var menuItem
+ property var hideUrl: true
+
+ property alias url: webview.url
+ property alias windowTitle: webview.title
+ property alias webView: webview
+
+ property var cleanPath: false
+ property var open: function(url) {
+ if(!window.cleanPath) {
+ var uri = url;
+ 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;
+ });
+ }
+
+ window.cleanPath = true;
+
+ webview.url = uri;
+
+ //uriNav.text = uri.text.replace(/(^https?\:\/\/(?:www\.)?)([a-zA-Z0-9_\-]*\.\w{2,3})(.*)/, "$1$2<span style='color:#CCC'>$3</span>");
+ uriNav.text = uri;
+
+ } else {
+ // Prevent inf loop.
+ window.cleanPath = false;
+ }
+ }
+
+ Item {
+ objectName: "root"
+ id: root
+ anchors.fill: parent
+ state: "inspectorShown"
+
+ WebEngineView {
+ objectName: "webView"
+ id: webview
+ anchors.fill: parent
+
+ property var protocol: "http://"
+ //property var domain: "localhost:3000"
+ property var domain: "ethereum-dapp-catalog.meteor.com"
+ url: protocol + domain
+
+ experimental.settings.javascriptCanAccessClipboard: true
+
+
+ onJavaScriptConsoleMessage: {
+ console.log(sourceID + ":" + lineNumber + ":" + JSON.stringify(message));
+ }
+
+ onNavigationRequested: {
+ // this checks if the domain of the requested link is the same as the catalog's
+ // If it is, it opens on the same window, if it's not it opens a new tab
+
+ var cleanTitle = request.url.toString()
+ var matches = cleanTitle.match(/^[a-z]*\:\/\/([^\/?#]+)(?:[\/?#]|$)/i);
+ var requestedDomain = matches && matches[1];
+
+
+ if(request.navigationType==0){
+
+ if (requestedDomain === this.domain){
+ request.action = WebEngineView.AcceptRequest;
+ } else {
+ request.action = WebEngineView.IgnoreRequest;
+ newBrowserTab(request.url);
+ }
+
+ }
+ }
+ onLoadingChanged: {
+ if (loadRequest.status == WebEngineView.LoadSucceededStatus) {
+ webview.runJavaScript(eth.readFile("mist.js"));
+ }
+ }
+ }
+
+
+
+
+
+
+ WebEngineView {
+ id: inspector
+ visible: false
+ z:10
+ anchors {
+ left: root.left
+ right: root.right
+ top: root.top
+ bottom: root.bottom
+ }
+
+ }
+
+ states: [
+ State {
+ name: "inspectorShown"
+ PropertyChanges {
+ target: inspector
+ }
+ }
+ ]
+ }
+}
diff --git a/cmd/mist/assets/qml/views/chain.qml b/cmd/mist/assets/qml/views/chain.qml
new file mode 100644
index 000000000..4d1bc0e03
--- /dev/null
+++ b/cmd/mist/assets/qml/views/chain.qml
@@ -0,0 +1,261 @@
+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
+
+Rectangle {
+ id: root
+ property var title: "Block Chain"
+ property var menuItem
+
+ objectName: "chainView"
+ visible: false
+ anchors.fill: parent
+
+ TableView {
+ id: blockTable
+ width: parent.width
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ TableViewColumn{ role: "number" ; title: "#" ; width: 100 }
+ TableViewColumn{ role: "hash" ; title: "Hash" ; width: 560 }
+ TableViewColumn{ role: "txAmount" ; title: "Tx amount" ; width: 100 }
+
+ model: blockModel
+
+ itemDelegate: Item {
+ Text {
+ anchors {
+ left: parent.left
+ right: parent.right
+ leftMargin: 10
+ verticalCenter: parent.verticalCenter
+ }
+ color: styleData.textColor
+ elide: styleData.elideMode
+ text: styleData.value
+ font.pixelSize: 11
+ MouseArea {
+ acceptedButtons: Qt.LeftButton | Qt.RightButton
+ propagateComposedEvents: true
+ anchors.fill: parent
+ onClicked: {
+ blockTable.selection.clear()
+ blockTable.selection.select(styleData.row)
+
+ if(mouse.button == Qt.RightButton) {
+ contextMenu.row = styleData.row;
+ contextMenu.popup()
+ }
+ }
+
+ onDoubleClicked: {
+ popup.visible = true
+ popup.setDetails(blockModel.get(styleData.row))
+ }
+ }
+ }
+
+ }
+
+ Menu {
+ id: contextMenu
+ property var row
+ MenuItem {
+ text: "Details"
+ onTriggered: {
+ popup.visible = true
+ popup.setDetails(blockModel.get(contextMenu.row))
+ }
+ }
+
+ MenuSeparator{}
+
+ MenuItem {
+ text: "Copy"
+ onTriggered: {
+ copyToClipboard(blockModel.get(contextMenu.row).hash)
+ }
+ }
+
+ MenuItem {
+ text: "Dump State"
+ onTriggered: {
+ generalFileDialog.show(false, function(path) {
+ var hash = blockModel.get(contextMenu.row).hash;
+
+ gui.dumpState(hash, path);
+ });
+ }
+ }
+ }
+ }
+
+
+
+ function addBlock(block, initial) {
+ if(initial == undefined){
+ initial = false
+ }
+
+ var amount = block.transactions.length;
+ var txs = [];
+ for(var i = 0; i < block.transactions.length; i++) {
+ var tx = JSON.parse(block.transactions.getAsJson(i));
+ txs.push(tx);
+ }
+
+ if(initial){
+ blockModel.append({raw: block.raw, bloom: block.bloom, size: block.size, number: block.number, name: block.name, gasLimit: block.gasLimit, gasUsed: block.gasUsed, coinbase: block.coinbase, hash: block.hash, txs: txs, txAmount: amount, time: block.time, prettyTime: convertToPretty(block.time)})
+ } else {
+ blockModel.insert(0, {raw: block.raw, bloom: block.bloom, size: block.size, number: block.number, name: block.name, gasLimit: block.gasLimit, gasUsed: block.gasUsed, coinbase: block.coinbase, hash: block.hash, txs: txs, txAmount: amount, time: block.time, prettyTime: convertToPretty(block.time)})
+ }
+ }
+
+ Window {
+ id: popup
+ visible: false
+ //flags: Qt.CustomizeWindowHint | Qt.Tool | Qt.WindowCloseButtonHint
+ property var block
+ width: root.width
+ height: 300
+ Component{
+ id: blockDetailsDelegate
+ Rectangle {
+ color: "#252525"
+ width: popup.width
+ height: 150
+ Column {
+ anchors.leftMargin: 10
+ anchors.topMargin: 5
+ anchors.top: parent.top
+ anchors.left: parent.left
+ Text { text: '<h3>Block details</h3>'; color: "#F2F2F2"}
+ Text { text: '<b>Block number:</b> ' + number + " (Size: " + size + ")"; color: "#F2F2F2"}
+ Text { text: '<b>Hash:</b> ' + hash; color: "#F2F2F2"}
+ Text { text: '<b>Bloom:</b> ' + bloom; color: "#F2F2F2"}
+ Text { text: '<b>Coinbase:</b> &lt;' + name + '&gt; ' + coinbase; color: "#F2F2F2"}
+ Text { text: '<b>Block found at:</b> ' + prettyTime; color: "#F2F2F2"}
+ Text { text: '<b>Gas used:</b> ' + gasUsed + " / " + gasLimit; color: "#F2F2F2"}
+ }
+ }
+ }
+ ListView {
+ model: singleBlock
+ delegate: blockDetailsDelegate
+ anchors.top: parent.top
+ height: 100
+ anchors.leftMargin: 20
+ id: listViewThing
+ Layout.maximumHeight: 40
+ }
+ TableView {
+ id: txView
+ anchors.top: listViewThing.bottom
+ anchors.topMargin: 50
+ width: parent.width
+
+ TableViewColumn{width: 90; role: "value" ; title: "Value" }
+ TableViewColumn{width: 200; role: "hash" ; title: "Hash" }
+ TableViewColumn{width: 200; role: "sender" ; title: "Sender" }
+ TableViewColumn{width: 200;role: "address" ; title: "Receiver" }
+ TableViewColumn{width: 60; role: "gas" ; title: "Gas" }
+ TableViewColumn{width: 60; role: "gasPrice" ; title: "Gas Price" }
+ TableViewColumn{width: 60; role: "isContract" ; title: "Contract" }
+
+ model: transactionModel
+ onClicked: {
+ var tx = transactionModel.get(row)
+ if(tx.data) {
+ popup.showContractData(tx)
+ }else{
+ popup.height = 440
+ }
+ }
+ }
+
+ function showContractData(tx) {
+ txDetailsDebugButton.tx = tx
+ if(tx.createsContract) {
+ contractData.text = tx.data
+ contractLabel.text = "<h4> Transaction created contract " + tx.address + "</h4>"
+ }else{
+ contractLabel.text = "<h4> Transaction ran contract " + tx.address + "</h4>"
+ contractData.text = tx.rawData
+ }
+ popup.height = 540
+ }
+
+ Rectangle {
+ id: txDetails
+ width: popup.width
+ height: 300
+ anchors.left: listViewThing.left
+ anchors.top: txView.bottom
+ Label {
+ text: "<h4>Contract data</h4>"
+ anchors.top: parent.top
+ anchors.left: parent.left
+ id: contractLabel
+ anchors.leftMargin: 10
+ }
+ Button {
+ property var tx
+ id: txDetailsDebugButton
+ anchors.right: parent.right
+ anchors.rightMargin: 10
+ anchors.top: parent.top
+ anchors.topMargin: 10
+ text: "Debug contract"
+ onClicked: {
+ if(tx && tx.createsContract){
+ eth.startDbWithCode(tx.rawData)
+ }else {
+ eth.startDbWithContractAndData(tx.address, tx.rawData)
+ }
+ }
+ }
+ TextArea {
+ id: contractData
+ text: "Contract"
+ anchors.top: contractLabel.bottom
+ anchors.left: parent.left
+ anchors.right: parent.right
+ wrapMode: Text.Wrap
+ height: 80
+ }
+ TextArea {
+ id: dumpData
+ anchors.top: contractData.bottom
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ height: 300
+ }
+ }
+ property var transactionModel: ListModel {
+ id: transactionModel
+ }
+ property var singleBlock: ListModel {
+ id: singleBlock
+ }
+ function setDetails(bl){
+ singleBlock.set(0, bl)
+ popup.height = 300
+ transactionModel.clear()
+ if(bl.txs !== undefined){
+ for(var i = 0; i < bl.txs.count; i++) {
+ transactionModel.insert(0, bl.txs.get(i))
+ }
+ if(bl.txs.count > 0 && bl.txs.get(0).data){
+ popup.showContractData(bl.txs.get(0))
+ }
+ }
+ txView.forceActiveFocus()
+ dumpData.text = bl.raw;
+ }
+ }
+}
diff --git a/cmd/mist/assets/qml/views/history.qml b/cmd/mist/assets/qml/views/history.qml
new file mode 100644
index 000000000..c72f8f3ae
--- /dev/null
+++ b/cmd/mist/assets/qml/views/history.qml
@@ -0,0 +1,51 @@
+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
+
+Rectangle {
+ property var title: "Transactions"
+ property var menuItem
+
+
+ id: historyView
+ visible: false
+ anchors.fill: parent
+ objectName: "transactionView"
+
+ property var txModel: ListModel {
+ id: txModel
+ }
+ TableView {
+ id: txTableView
+ anchors.fill: parent
+ TableViewColumn{ role: "inout" ; title: "" ; width: 40 }
+ TableViewColumn{ role: "value" ; title: "Value" ; width: 100 }
+ TableViewColumn{ role: "address" ; title: "Address" ; width: 430 }
+ TableViewColumn{ role: "contract" ; title: "Contract" ; width: 100 }
+
+ model: txModel
+ }
+
+ function addTx(tx, inout) {
+ var isContract
+ if (tx.contract == true){
+ isContract = "Yes"
+ }else{
+ isContract = "No"
+ }
+
+
+ var address;
+ if(inout == "recv") {
+ address = tx.sender;
+ } else {
+ address = tx.address;
+ }
+
+ txModel.insert(0, {inout: inout, hash: tx.hash, address: address, value: tx.value, contract: isContract})
+ }
+}
diff --git a/cmd/mist/assets/qml/views/info.qml b/cmd/mist/assets/qml/views/info.qml
new file mode 100644
index 000000000..b2d2f521c
--- /dev/null
+++ b/cmd/mist/assets/qml/views/info.qml
@@ -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
+import Ethereum 1.0
+
+Rectangle {
+ property var title: "Debug Info"
+ property var menuItem
+
+ objectName: "infoView"
+ visible: false
+ anchors.fill: parent
+
+ color: "#00000000"
+
+ Column {
+ id: info
+ spacing: 3
+ anchors.fill: parent
+ anchors.topMargin: 5
+ anchors.leftMargin: 5
+
+ Label {
+ id: addressLabel
+ text: "Address"
+ }
+ TextField {
+ text: eth.coinbase()
+ width: 500
+ }
+
+ TextArea {
+ objectName: "statsPane"
+ width: parent.width
+ height: 200
+ selectByMouse: true
+ readOnly: true
+ font.family: "Courier"
+ }
+ }
+
+ RowLayout {
+ id: logLayout
+ width: parent.width
+ height: 200
+ anchors.bottom: parent.bottom
+
+ TableView {
+ id: addressView
+ width: parent.width
+ height: 200
+ anchors {
+ left: parent.left
+ right: logLevelSlider.left
+ bottom: parent.bottom
+ top: parent.top
+ }
+ TableViewColumn{ role: "name"; title: "name" }
+ TableViewColumn{ role: "address"; title: "address"; width: 300}
+
+ property var addressModel: ListModel {
+ id: addressModel
+ }
+
+ model: addressModel
+ itemDelegate: Item {
+ Text {
+ anchors {
+ left: parent.left
+ right: parent.right
+ leftMargin: 10
+ verticalCenter: parent.verticalCenter
+ }
+ color: styleData.textColor
+ elide: styleData.elideMode
+ text: styleData.value
+ font.pixelSize: 11
+ MouseArea {
+ acceptedButtons: Qt.LeftButton | Qt.RightButton
+ propagateComposedEvents: true
+ anchors.fill: parent
+ onClicked: {
+ addressView.selection.clear()
+ addressView.selection.select(styleData.row)
+
+ if(mouse.button == Qt.RightButton) {
+ contextMenu.row = styleData.row;
+ contextMenu.popup()
+ }
+ }
+ }
+ }
+ }
+
+ Menu {
+ id: contextMenu
+ property var row;
+
+ MenuItem {
+ text: "Copy"
+ onTriggered: {
+ copyToClipboard(addressModel.get(this.row).address)
+ }
+ }
+ }
+ }
+
+ /*
+ TableView {
+ id: logView
+ headerVisible: false
+ anchors {
+ right: logLevelSlider.left
+ left: parent.left
+ bottom: parent.bottom
+ top: parent.top
+ }
+
+ TableViewColumn{ role: "description" ; title: "log" }
+
+ model: logModel
+ }
+ */
+
+ Slider {
+ id: logLevelSlider
+ value: gui.getLogLevelInt()
+ anchors {
+ right: parent.right
+ top: parent.top
+ bottom: parent.bottom
+
+ rightMargin: 5
+ leftMargin: 5
+ topMargin: 5
+ bottomMargin: 5
+ }
+
+ orientation: Qt.Vertical
+ maximumValue: 5
+ stepSize: 1
+
+ onValueChanged: {
+ gui.setLogLevel(value)
+ }
+ }
+ }
+
+ property var logModel: ListModel {
+ id: logModel
+ }
+
+ function addDebugMessage(message){
+ debuggerLog.append({value: message})
+ }
+
+ function addAddress(address) {
+ addressModel.append({name: address.name, address: address.address})
+ }
+
+ function clearAddress() {
+ addressModel.clear()
+ }
+
+ function addLog(str) {
+ // Remove first item once we've reached max log items
+ if(logModel.count > 250) {
+ logModel.remove(0)
+ }
+
+ if(str.len != 0) {
+ if(logView.flickableItem.atYEnd) {
+ logModel.append({description: str})
+ logView.positionViewAtRow(logView.rowCount - 1, ListView.Contain)
+ } else {
+ logModel.append({description: str})
+ }
+ }
+
+ }
+}
diff --git a/cmd/mist/assets/qml/views/jeffcoin/jeff.png b/cmd/mist/assets/qml/views/jeffcoin/jeff.png
new file mode 100644
index 000000000..2b9c6651a
--- /dev/null
+++ b/cmd/mist/assets/qml/views/jeffcoin/jeff.png
Binary files differ
diff --git a/cmd/mist/assets/qml/views/jeffcoin/jeffcoin.qml b/cmd/mist/assets/qml/views/jeffcoin/jeffcoin.qml
new file mode 100644
index 000000000..23502d334
--- /dev/null
+++ b/cmd/mist/assets/qml/views/jeffcoin/jeffcoin.qml
@@ -0,0 +1,190 @@
+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) {
+ var to = eth.lookupName(to)
+ var from = eth.lookupName(from)
+ 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 msgs = filter.messages()
+ for(var i = msgs.length-1; i >= 0; i--) {
+ var message = JSON.parse(msgs.getAsJson(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: {
+ var lookup = eth.lookupAddress(address)
+ if(lookup.length == 0)
+ lookup = address
+
+ eth.transact({from: eth.key().privateKey, to:lookup, gas: "9000", gasPrice: "10000000000000", 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: {
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/cmd/mist/assets/qml/views/miner.qml b/cmd/mist/assets/qml/views/miner.qml
new file mode 100644
index 000000000..e239c7d7b
--- /dev/null
+++ b/cmd/mist/assets/qml/views/miner.qml
@@ -0,0 +1,294 @@
+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
+
+Rectangle {
+ id: root
+ property var title: "Miner"
+ property var iconSource: "../mining-icon.png"
+ property var menuItem
+
+ color: "#00000000"
+
+ Label {
+ visible: false
+ id: lastBlockLabel
+ objectName: "lastBlockLabel"
+ text: "---"
+ onTextChanged: {
+ //menuItem.secondaryTitle = text
+ }
+ }
+
+ Label {
+ objectName: "miningLabel"
+ visible: false
+ font.pixelSize: 10
+ anchors.right: lastBlockLabel.left
+ anchors.rightMargin: 5
+ onTextChanged: {
+ menuItem.secondaryTitle = text
+ }
+ }
+
+ ColumnLayout {
+ spacing: 10
+ anchors.fill: parent
+
+ Rectangle {
+ id: mainPane
+ color: "#00000000"
+ anchors {
+ top: parent.top
+ bottom: localTxPane.top
+ left: parent.left
+ right: parent.right
+ }
+
+ Rectangle {
+ id: menu
+ height: 25
+ anchors {
+ left: parent.left
+ }
+
+ RowLayout {
+ id: tools
+ anchors {
+ left: parent.left
+ right: parent.right
+ }
+
+ Button {
+ text: "Start"
+ onClicked: {
+ eth.setGasPrice(minGasPrice.text || "10000000000000");
+ eth.setExtra(blockExtra.text)
+ if (eth.toggleMining()) {
+ this.text = "Stop";
+ } else {
+ this.text = "Start";
+ }
+ }
+ }
+
+ Rectangle {
+ id: minGasPriceRect
+ anchors.top: parent.top
+ anchors.topMargin: 2
+ width: 200
+ TextField {
+ id: minGasPrice
+ placeholderText: "Min Gas: 10000000000000"
+ width: 200
+ validator: RegExpValidator { regExp: /\d*/ }
+ }
+ }
+
+ Rectangle {
+ width: 300
+ anchors {
+ left: minGasPriceRect.right
+ leftMargin: 5
+ top: parent.top
+ topMargin: 2
+ }
+
+ TextField {
+ id: blockExtra
+ placeholderText: "Extra"
+ width: parent.width
+ maximumLength: 1024
+ }
+ }
+ }
+ }
+
+ Column {
+ anchors {
+ left: parent.left
+ right: parent.right
+ top: menu.bottom
+ topMargin: 5
+ }
+
+ Text {
+ text: "<b>Merged mining options</b>"
+ }
+
+ TableView {
+ id: mergedMiningTable
+ height: 300
+ anchors {
+ left: parent.left
+ right: parent.right
+ }
+ Component {
+ id: checkBoxDelegate
+
+ Item {
+ id: test
+ CheckBox {
+ anchors.fill: parent
+ checked: styleData.value
+
+ onClicked: {
+ var model = mergedMiningModel.get(styleData.row)
+
+ if (this.checked) {
+ model.id = txModel.createLocalTx(model.address, "0", "5000", "0", "")
+ } else {
+ txModel.removeWithId(model.id);
+ model.id = 0;
+ }
+ }
+ }
+ }
+ }
+ TableViewColumn{ role: "checked" ; title: "" ; width: 40 ; delegate: checkBoxDelegate }
+ TableViewColumn{ role: "name" ; title: "Name" ; width: 480 }
+ model: ListModel {
+ objectName: "mergedMiningModel"
+ id: mergedMiningModel
+ function addMergedMiningOption(model) {
+ this.append(model);
+ }
+ }
+ Component.onCompleted: {
+ /*
+ // XXX Temp. replace with above eventually
+ var tmpItems = ["JEVCoin", "Some coin", "Other coin", "Etc coin"];
+ var address = "e6716f9544a56c530d868e4bfbacb172315bdead";
+ for (var i = 0; i < tmpItems.length; i++) {
+ mergedMiningModel.append({checked: false, name: tmpItems[i], address: address, id: 0, itemId: i});
+ }
+ */
+ }
+ }
+ }
+ }
+
+ Rectangle {
+ id: localTxPane
+ color: "#ececec"
+ border.color: "#cccccc"
+ border.width: 1
+ anchors {
+ left: parent.left
+ right: parent.right
+ bottom: parent.bottom
+ }
+ height: 300
+
+ ColumnLayout {
+ spacing: 10
+ anchors.fill: parent
+ RowLayout {
+ id: newLocalTx
+ anchors {
+ left: parent.left
+ leftMargin: 5
+ top: parent.top
+ topMargin: 5
+ bottomMargin: 5
+ }
+
+ Text {
+ text: "Local tx"
+ }
+
+ Rectangle {
+ width: 250
+ color: "#00000000"
+ anchors.top: parent.top
+ anchors.topMargin: 2
+
+ TextField {
+ id: to
+ placeholderText: "To"
+ width: 250
+ validator: RegExpValidator { regExp: /[abcdefABCDEF1234567890]*/ }
+ }
+ }
+ TextField {
+ property var defaultGas: "5000"
+ id: gas
+ placeholderText: "Gas"
+ text: defaultGas
+ validator: RegExpValidator { regExp: /\d*/ }
+ }
+ TextField {
+ id: gasPrice
+ placeholderText: "Price"
+ validator: RegExpValidator { regExp: /\d*/ }
+ }
+ TextField {
+ id: value
+ placeholderText: "Amount"
+ text: "0"
+ validator: RegExpValidator { regExp: /\d*/ }
+ }
+ TextField {
+ id: data
+ placeholderText: "Data"
+ validator: RegExpValidator { regExp: /[abcdefABCDEF1234567890]*/ }
+ }
+ Button {
+ text: "Create"
+ onClicked: {
+ if (to.text.length == 40 && gasPrice.text.length != 0 && value.text.length != 0 && gas.text.length != 0) {
+ txModel.createLocalTx(to.text, gasPrice.text, gas.text, value.text, data.text);
+
+ to.text = ""; gasPrice.text = "";
+ gas.text = gas.defaultGas;
+ value.text = "0"
+ }
+ }
+ }
+ }
+
+ TableView {
+ id: txTableView
+ anchors {
+ top: newLocalTx.bottom
+ topMargin: 5
+ left: parent.left
+ right: parent.right
+ bottom: parent.bottom
+ }
+ TableViewColumn{ role: "to" ; title: "To" ; width: 480 }
+ TableViewColumn{ role: "gas" ; title: "Gas" ; width: 100 }
+ TableViewColumn{ role: "gasPrice" ; title: "Gas Price" ; width: 100 }
+ TableViewColumn{ role: "value" ; title: "Amount" ; width: 100 }
+ TableViewColumn{ role: "data" ; title: "Data" ; width: 100 }
+
+ model: ListModel {
+ id: txModel
+ Component.onCompleted: {
+ }
+ function removeWithId(id) {
+ for (var i = 0; i < this.count; i++) {
+ if (txModel.get(i).id == id) {
+ this.remove(i);
+ eth.removeLocalTransaction(id)
+ break;
+ }
+ }
+ }
+
+ function createLocalTx(to, gasPrice, gas, value, data) {
+ var id = eth.addLocalTransaction(to, data, gas, gasPrice, value)
+ txModel.insert(0, {to: to, gas: gas, gasPrice: gasPrice, value: value, data: data, id: id});
+
+ return id
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/cmd/mist/assets/qml/views/pending_tx.qml b/cmd/mist/assets/qml/views/pending_tx.qml
new file mode 100644
index 000000000..3dcedeff2
--- /dev/null
+++ b/cmd/mist/assets/qml/views/pending_tx.qml
@@ -0,0 +1,53 @@
+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
+
+Rectangle {
+ property var title: "Pending Transactions"
+ property var menuItem
+
+ objectName: "pendingTxView"
+ anchors.fill: parent
+ visible: false
+ id: pendingTxView
+
+ property var pendingTxModel: ListModel {
+ id: pendingTxModel
+ }
+
+ TableView {
+ id: pendingTxTableView
+ anchors.fill: parent
+ TableViewColumn{ role: "value" ; title: "Value" ; width: 100 }
+ TableViewColumn{ role: "from" ; title: "sender" ; width: 230 }
+ TableViewColumn{ role: "to" ; title: "Reciever" ; width: 230 }
+ TableViewColumn{ role: "contract" ; title: "Contract" ; width: 100 }
+
+ model: pendingTxModel
+ }
+
+ function addTx(tx, inout) {
+ var isContract
+ if (tx.contract == true){
+ isContract = "Yes"
+ }else{
+ isContract = "No"
+ }
+
+
+ pendingTxModel.insert(0, {hash: tx.hash, to: tx.address, from: tx.sender, value: tx.value, contract: isContract})
+ }
+
+ function removeTx(tx) {
+ for (var i = 0; i < pendingTxModel.count; i++) {
+ if (tx.hash === pendingTxModel.get(i).hash) {
+ pendingTxModel.remove(i);
+ break;
+ }
+ }
+ }
+}
diff --git a/cmd/mist/assets/qml/views/transaction.qml b/cmd/mist/assets/qml/views/transaction.qml
new file mode 100644
index 000000000..df798a9c0
--- /dev/null
+++ b/cmd/mist/assets/qml/views/transaction.qml
@@ -0,0 +1,216 @@
+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
+
+Rectangle {
+ property var title: "New Transaction"
+ property var menuItem
+
+ objectName: "newTxView"
+ visible: false
+ anchors.fill: parent
+ color: "#00000000"
+
+ Column {
+ id: mainContractColumn
+ anchors.fill: parent
+
+
+ states: [
+ State{
+ name: "ERROR"
+
+ PropertyChanges { target: txResult; visible:true}
+ PropertyChanges { target: codeView; visible:true}
+ },
+ State {
+ name: "DONE"
+
+ PropertyChanges { target: txValue; visible:false}
+ PropertyChanges { target: txGas; visible:false}
+ PropertyChanges { target: txGasPrice; visible:false}
+ PropertyChanges { target: codeView; visible:false}
+ PropertyChanges { target: txButton; visible:false}
+ PropertyChanges { target: txDataLabel; visible:false}
+ PropertyChanges { target: atLabel; visible:false}
+ PropertyChanges { target: txFuelRecipient; visible:false}
+ PropertyChanges { target: valueDenom; visible:false}
+ PropertyChanges { target: gasDenom; visible:false}
+
+ PropertyChanges { target: txResult; visible:true}
+ PropertyChanges { target: txOutput; visible:true}
+ PropertyChanges { target: newTxButton; visible:true}
+ },
+ State {
+ name: "SETUP"
+
+ PropertyChanges { target: txValue; visible:true; text: ""}
+ PropertyChanges { target: txGas; visible:true;}
+ PropertyChanges { target: txGasPrice; visible:true;}
+ PropertyChanges { target: codeView; visible:true; text: ""}
+ PropertyChanges { target: txButton; visible:true}
+ PropertyChanges { target: txDataLabel; visible:true}
+ PropertyChanges { target: valueDenom; visible:true}
+ PropertyChanges { target: gasDenom; visible:true}
+
+ PropertyChanges { target: txResult; visible:false}
+ PropertyChanges { target: txOutput; visible:false}
+ PropertyChanges { target: newTxButton; visible:false}
+ }
+ ]
+ width: 400
+ spacing: 5
+ anchors.left: parent.left
+ anchors.top: parent.top
+ anchors.leftMargin: 5
+ anchors.topMargin: 5
+
+ ListModel {
+ id: denomModel
+ ListElement { text: "Wei" ; zeros: "" }
+ ListElement { text: "Ada" ; zeros: "000" }
+ ListElement { text: "Babbage" ; zeros: "000000" }
+ ListElement { text: "Shannon" ; zeros: "000000000" }
+ ListElement { text: "Szabo" ; zeros: "000000000000" }
+ ListElement { text: "Finney" ; zeros: "000000000000000" }
+ ListElement { text: "Ether" ; zeros: "000000000000000000" }
+ ListElement { text: "Einstein" ;zeros: "000000000000000000000" }
+ ListElement { text: "Douglas" ; zeros: "000000000000000000000000000000000000000000" }
+ }
+
+
+ TextField {
+ id: txFuelRecipient
+ placeholderText: "Address / Name or empty for contract"
+ //validator: RegExpValidator { regExp: /[a-f0-9]{40}/ }
+ width: 400
+ }
+
+ RowLayout {
+ TextField {
+ id: txValue
+ width: 222
+ placeholderText: "Amount"
+ validator: RegExpValidator { regExp: /\d*/ }
+ onTextChanged: {
+ contractFormReady()
+ }
+ }
+
+ ComboBox {
+ id: valueDenom
+ currentIndex: 5
+ model: denomModel
+ }
+ }
+
+ RowLayout {
+ TextField {
+ id: txGas
+ width: 50
+ validator: RegExpValidator { regExp: /\d*/ }
+ placeholderText: "Gas"
+ text: "5000"
+ }
+ Label {
+ id: atLabel
+ text: "@"
+ }
+
+ TextField {
+ id: txGasPrice
+ width: 200
+ placeholderText: "Gas price"
+ text: "10"
+ validator: RegExpValidator { regExp: /\d*/ }
+ }
+
+ ComboBox {
+ id: gasDenom
+ currentIndex: 4
+ model: denomModel
+ }
+ }
+
+ Label {
+ id: txDataLabel
+ text: "Data"
+ }
+
+ TextArea {
+ id: codeView
+ height: 300
+ anchors.topMargin: 5
+ width: 400
+ onTextChanged: {
+ contractFormReady()
+ }
+ }
+
+
+ Button {
+ id: txButton
+ /* enabled: false */
+ states: [
+ State {
+ name: "READY"
+ PropertyChanges { target: txButton; /*enabled: true*/}
+ },
+ State {
+ name: "NOTREADY"
+ PropertyChanges { target: txButton; /*enabled:false*/}
+ }
+ ]
+ text: "Send"
+ onClicked: {
+ var value = txValue.text + denomModel.get(valueDenom.currentIndex).zeros;
+ var gasPrice = txGasPrice.text + denomModel.get(gasDenom.currentIndex).zeros;
+ var res = gui.transact(txFuelRecipient.text, value, txGas.text, gasPrice, codeView.text)
+ if(res[1]) {
+ txResult.text = "Your contract <b>could not</b> be sent over the network:\n<b>"
+ txResult.text += res[1].error()
+ txResult.text += "</b>"
+ mainContractColumn.state = "ERROR"
+ } else {
+ txResult.text = "Your transaction has been submitted:\n"
+ txOutput.text = res.toString()
+ mainContractColumn.state = "DONE"
+
+ console.log(res)
+ }
+ }
+ }
+ Text {
+ id: txResult
+ visible: false
+ }
+ TextField {
+ id: txOutput
+ visible: false
+ width: 530
+ }
+ Button {
+ id: newTxButton
+ visible: false
+ text: "Create a new transaction"
+ onClicked: {
+ this.visible = false
+ txResult.text = ""
+ txOutput.text = ""
+ mainContractColumn.state = "SETUP"
+ }
+ }
+ }
+
+ function contractFormReady(){
+ if(codeView.text.length > 0 && txValue.text.length > 0 && txGas.text.length > 0 && txGasPrice.length > 0) {
+ txButton.state = "READY"
+ }else{
+ txButton.state = "NOTREADY"
+ }
+ }
+}
diff --git a/cmd/mist/assets/qml/views/wallet.qml b/cmd/mist/assets/qml/views/wallet.qml
new file mode 100644
index 000000000..2369390c3
--- /dev/null
+++ b/cmd/mist/assets/qml/views/wallet.qml
@@ -0,0 +1,190 @@
+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
+
+Rectangle {
+ id: root
+ property var title: "Wallet"
+ property var menuItem
+
+ objectName: "walletView"
+ anchors.fill: parent
+
+ Label {
+ objectName: "balanceLabel"
+ visible: false
+ onTextChanged: {
+ balance.text = text
+ menuItem.secondaryTitle = text
+ }
+ }
+
+ function onReady() {
+ setBalance()
+ }
+
+ function setBalance() {
+ balance.text = "<b>Balance</b>: " + eth.numberToHuman(eth.balanceAt(eth.coinbase()))
+ if(menuItem)
+ menuItem.secondaryTitle = eth.numberToHuman(eth.balanceAt(eth.coinbase()))
+ }
+
+ ListModel {
+ id: denomModel
+ ListElement { text: "Wei" ; zeros: "" }
+ ListElement { text: "Ada" ; zeros: "000" }
+ ListElement { text: "Babbage" ; zeros: "000000" }
+ ListElement { text: "Shannon" ; zeros: "000000000" }
+ ListElement { text: "Szabo" ; zeros: "000000000000" }
+ ListElement { text: "Finney" ; zeros: "000000000000000" }
+ ListElement { text: "Ether" ; zeros: "000000000000000000" }
+ ListElement { text: "Einstein" ;zeros: "000000000000000000000" }
+ ListElement { text: "Douglas" ; zeros: "000000000000000000000000000000000000000000" }
+ }
+
+ ColumnLayout {
+ spacing: 10
+ y: 40
+ anchors.fill: parent
+
+ Text {
+ id: balance
+ 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: "Ξ "
+ }
+
+ // 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"
+ }
+ }
+
+ ComboBox {
+ id: valueDenom
+ currentIndex: 5
+ model: denomModel
+ }
+
+ }
+
+ 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: {
+ var value = txValue.text + denomModel.get(valueDenom.currentIndex).zeros;
+ var gasPrice = "10000000000000"
+ var res = eth.transact({from: eth.coinbase(), to: txTo.text, value: value, gas: "500", gasPrice: gasPrice})
+ }
+ }
+ }
+ }
+
+ Rectangle {
+ anchors {
+ left: parent.left
+ right: parent.right
+ top: newTxPane.bottom
+ topMargin: 10
+ bottom: parent.bottom
+ }
+ TableView {
+ id: txTableView
+ anchors.fill : parent
+ TableViewColumn{ role: "num" ; title: "#" ; width: 30 }
+ TableViewColumn{ role: "from" ; title: "From" ; width: 340 }
+ TableViewColumn{ role: "to" ; title: "To" ; width: 340 }
+ TableViewColumn{ role: "value" ; title: "Amount" ; width: 100 }
+
+ model: ListModel {
+ id: txModel
+ Component.onCompleted: {
+ }
+
+ function addTxs(messages) {
+ /*
+ setBalance()
+
+ for(var i = 0; i < messages.length; i++) {
+ var message = messages.get(i);
+ var to = eth.lookupName(message.to);
+ var from;
+ if(message.from.length == 0) {
+ from = "- MINED -";
+ } else {
+ from = eth.lookupName(message.from);
+ }
+ txModel.insert(0, {num: txModel.count, from: from, to: to, value: eth.numberToHuman(message.value)})
+ }
+ */
+ }
+ }
+ }
+ }
+
+ }
+}
diff --git a/cmd/mist/assets/qml/views/whisper.qml b/cmd/mist/assets/qml/views/whisper.qml
new file mode 100644
index 000000000..dc097b806
--- /dev/null
+++ b/cmd/mist/assets/qml/views/whisper.qml
@@ -0,0 +1,74 @@
+
+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
+
+Rectangle {
+ id: root
+ property var title: "Whisper Traffic"
+ property var menuItem
+
+ objectName: "whisperView"
+ anchors.fill: parent
+
+ property var identity: ""
+ Component.onCompleted: {
+ identity = shh.newIdentity()
+
+ var t = shh.watch({}, root)
+ }
+
+ function onShhMessage(message, i) {
+ whisperModel.insert(0, {from: message.from, payload: eth.toAscii(message.payload)})
+ }
+
+ RowLayout {
+ id: input
+ anchors {
+ left: parent.left
+ leftMargin: 20
+ top: parent.top
+ topMargin: 20
+ }
+
+ TextField {
+ id: to
+ placeholderText: "To"
+ }
+ TextField {
+ id: data
+ placeholderText: "Data"
+ }
+ TextField {
+ id: topics
+ placeholderText: "topic1, topic2, topic3, ..."
+ }
+ Button {
+ text: "Send"
+ onClicked: {
+ shh.post([eth.toHex(data.text)], "", identity, topics.text.split(","), 500, 50)
+ }
+ }
+ }
+
+ TableView {
+ id: txTableView
+ anchors {
+ top: input.bottom
+ topMargin: 10
+ bottom: parent.bottom
+ left: parent.left
+ right: parent.right
+ }
+ TableViewColumn{ id: fromRole; role: "from" ; title: "From"; width: 300 }
+ TableViewColumn{ role: "payload" ; title: "Payload" ; width: parent.width - fromRole.width - 2 }
+
+ model: ListModel {
+ id: whisperModel
+ }
+ }
+}