You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

588 lines
14 KiB

import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.1
import QtQuick.Window 2.0
import QtQuick.Controls.Styles 1.3
import org.ethereum.qml.QEther 1.0
import "js/QEtherHelper.js" as QEtherHelper
import "js/TransactionHelper.js" as TransactionHelper
import "."
Dialog {
id: modalStateDialog
modality: Qt.ApplicationModal
width: 630
height: 660
title: qsTr("Edit State")
visible: false
property alias stateTitle: titleField.text
property alias isDefault: defaultCheckBox.checked
property alias model: transactionsModel
property alias transactionDialog: transactionDialog
property alias minerComboBox: comboMiner
property alias newAccAction: newAccountAction
property int stateIndex
property var stateTransactions: []
property var stateAccounts: []
property var stateContracts: []
signal accepted
StateDialogStyle {
id: stateDialogStyle
}
function open(index, item, setDefault) {
stateIndex = index
stateTitle = item.title
transactionsModel.clear()
stateTransactions = []
var transactions = item.transactions
for (var t = 0; t < transactions.length; t++) {
transactionsModel.append(item.transactions[t])
stateTransactions.push(item.transactions[t])
}
accountsModel.clear()
stateAccounts = []
var miner = 0
for (var k = 0; k < item.accounts.length; k++) {
accountsModel.append(item.accounts[k])
stateAccounts.push(item.accounts[k])
if (item.miner && item.accounts[k].name === item.miner.name)
miner = k
}
stateContracts = []
if (item.contracts) {
for (k = 0; k < item.contracts.length; k++) {
contractsModel.append(item.contracts[k])
stateContracts.push(item.contracts[k])
}
}
visible = true
isDefault = setDefault
titleField.focus = true
defaultCheckBox.enabled = !isDefault
comboMiner.model = stateAccounts
comboMiner.currentIndex = miner
forceActiveFocus()
}
function acceptAndClose() {
close()
accepted()
}
function close() {
visible = false
}
function getItem() {
var item = {
title: stateDialog.stateTitle,
transactions: stateTransactions,
accounts: stateAccounts,
contracts: stateContracts
}
for (var k = 0; k < stateAccounts.length; k++) {
if (stateAccounts[k].name === comboMiner.currentText) {
item.miner = stateAccounts[k]
break
}
}
return item
}
contentItem: Rectangle {
color: stateDialogStyle.generic.backgroundColor
Rectangle {
color: stateDialogStyle.generic.backgroundColor
anchors.top: parent.top
anchors.margins: 10
anchors.fill: parent
ColumnLayout {
anchors.fill: parent
anchors.margins: 10
ColumnLayout {
id: dialogContent
anchors.top: parent.top
RowLayout {
Layout.fillWidth: true
DefaultLabel {
Layout.preferredWidth: 85
text: qsTr("Title")
}
DefaultTextField {
id: titleField
Layout.fillWidth: true
}
}
CommonSeparator {
Layout.fillWidth: true
}
RowLayout {
Layout.fillWidth: true
Rectangle {
Layout.preferredWidth: 85
DefaultLabel {
id: contractsLabel
Layout.preferredWidth: 85
wrapMode: Text.WrapAnywhere
text: qsTr("Genesis\nContracts")
}
Button {
id: importStateButton
anchors.top: contractsLabel.bottom
anchors.topMargin: 10
action: importStateAction
}
Action {
id: importStateAction
tooltip: qsTr("Import genesis state from JSON file")
text: qsTr("Import...")
onTriggered: {
importJsonFileDialog.open()
}
}
FileDialog {
id: importJsonFileDialog
visible: false
title: qsTr("Select State File")
nameFilters: Qt.platform.os === "osx" ? [] : [qsTr("JSON files (*.json)", "All files (*)")] //qt 5.4 segfaults with filter string on OSX
onAccepted: {
var path = importJsonFileDialog.fileUrl.toString()
var jsonData = fileIo.readFile(path)
if (jsonData) {
var json = JSON.parse(jsonData)
for (var address in json) {
var account = {
address: address,
name: (json[address].name ? json[address].name : address),
balance: QEtherHelper.createEther(json[address].wei, QEther.Wei),
code: json[address].code,
storage: json[address].storage
}
if (account.code) {
contractsModel.append(account)
stateContracts.push(account)
} else {
accountsModel.append(account)
stateAccounts.push(account)
}
}
}
}
}
}
TableView {
id: genesisContractsView
Layout.fillWidth: true
model: contractsModel
headerVisible: false
TableViewColumn {
role: "name"
title: qsTr("Name")
width: 230
delegate: Item {
RowLayout {
height: 25
width: parent.width
anchors.verticalCenter: parent.verticalCenter
Button {
iconSource: "qrc:/qml/img/delete_sign.png"
action: deleteContractAction
}
Action {
id: deleteContractAction
tooltip: qsTr("Delete Contract")
onTriggered: {
stateContracts.splice(styleData.row, 1)
contractsModel.remove(styleData.row)
}
}
DefaultTextField {
anchors.verticalCenter: parent.verticalCenter
onTextChanged: {
if (styleData.row > -1)
stateContracts[styleData.row].name = text
}
text: styleData.value
}
}
}
}
TableViewColumn {
role: "balance"
title: qsTr("Balance")
width: 200
delegate: Item {
Ether {
edit: true
displayFormattedValue: false
value: styleData.value
}
}
}
rowDelegate: Rectangle {
color: styleData.alternate ? "transparent" : "#f0f0f0"
height: 30
}
}
}
CommonSeparator {
Layout.fillWidth: true
}
RowLayout {
Layout.fillWidth: true
Rectangle {
Layout.preferredWidth: 85
DefaultLabel {
id: accountsLabel
Layout.preferredWidth: 85
text: qsTr("Accounts")
}
Button {
id: newAccountButton
anchors.top: accountsLabel.bottom
anchors.topMargin: 10
iconSource: "qrc:/qml/img/plus.png"
action: newAccountAction
}
Action {
id: newAccountAction
tooltip: qsTr("Add new Account")
onTriggered: {
add()
}
function add() {
var account = stateListModel.newAccount(
"1000000", QEther.Ether)
stateAccounts.push(account)
accountsModel.append(account)
return account
}
}
}
MessageDialog {
id: alertAlreadyUsed
text: qsTr("This account is in use. You cannot remove it. The first account is used to deploy config contract and cannot be removed.")
icon: StandardIcon.Warning
standardButtons: StandardButton.Ok
}
TableView {
id: accountsView
Layout.fillWidth: true
model: accountsModel
headerVisible: false
TableViewColumn {
role: "name"
title: qsTr("Name")
width: 230
delegate: Item {
RowLayout {
height: 25
width: parent.width
Button {
iconSource: "qrc:/qml/img/delete_sign.png"
action: deleteAccountAction
}
Action {
id: deleteAccountAction
tooltip: qsTr("Delete Account")
onTriggered: {
if (transactionsModel.isUsed(
stateAccounts[styleData.row].secret))
alertAlreadyUsed.open()
else {
if (stateAccounts[styleData.row].name
=== comboMiner.currentText)
comboMiner.currentIndex = 0
stateAccounts.splice(
styleData.row,
1)
accountsModel.remove(
styleData.row)
comboMiner.model = stateAccounts //TODO: filter accounts wo private keys
comboMiner.update()
}
}
}
DefaultTextField {
anchors.verticalCenter: parent.verticalCenter
onTextChanged: {
if (styleData.row > -1) {
stateAccounts[styleData.row].name = text
var index = comboMiner.currentIndex
comboMiner.model = stateAccounts
comboMiner.currentIndex = index
}
}
text: {
return styleData.value
}
}
}
}
}
TableViewColumn {
role: "balance"
title: qsTr("Balance")
width: 200
delegate: Item {
Ether {
edit: true
displayFormattedValue: false
value: styleData.value
}
}
}
rowDelegate: Rectangle {
color: styleData.alternate ? "transparent" : "#f0f0f0"
height: 30
}
}
}
CommonSeparator {
Layout.fillWidth: true
}
RowLayout {
Layout.fillWidth: true
DefaultLabel {
Layout.preferredWidth: 85
text: qsTr("Miner")
}
ComboBox {
id: comboMiner
textRole: "name"
Layout.fillWidth: true
}
}
CommonSeparator {
Layout.fillWidth: true
}
RowLayout {
Layout.fillWidth: true
DefaultLabel {
Layout.preferredWidth: 85
text: qsTr("Default")
}
CheckBox {
id: defaultCheckBox
Layout.fillWidth: true
}
}
CommonSeparator {
Layout.fillWidth: true
}
RowLayout {
Layout.fillWidth: true
Rectangle {
Layout.preferredWidth: 85
DefaultLabel {
id: transactionsLabel
Layout.preferredWidth: 85
text: qsTr("Transactions")
}
Button {
anchors.top: transactionsLabel.bottom
anchors.topMargin: 10
iconSource: "qrc:/qml/img/plus.png"
action: newTrAction
}
Action {
id: newTrAction
tooltip: qsTr("Create a new transaction")
onTriggered: transactionsModel.addTransaction()
}
}
TableView {
id: transactionsView
Layout.fillWidth: true
model: transactionsModel
headerVisible: false
TableViewColumn {
role: "name"
title: qsTr("Name")
width: 150
delegate: Item {
RowLayout {
height: 30
width: parent.width
Button {
iconSource: "qrc:/qml/img/delete_sign.png"
action: deleteTransactionAction
}
Action {
id: deleteTransactionAction
tooltip: qsTr("Delete")
onTriggered: transactionsModel.deleteTransaction(
styleData.row)
}
Button {
iconSource: "qrc:/qml/img/edit.png"
action: editAction
visible: styleData.row
>= 0 ? !transactionsModel.get(
styleData.row).stdContract : false
width: 10
height: 10
Action {
id: editAction
tooltip: qsTr("Edit")
onTriggered: transactionsModel.editTransaction(
styleData.row)
}
}
DefaultLabel {
Layout.preferredWidth: 150
text: {
if (styleData.row >= 0)
return transactionsModel.get(
styleData.row).functionId
else
return ""
}
}
}
}
}
rowDelegate: Rectangle {
color: styleData.alternate ? "transparent" : "#f0f0f0"
height: 30
}
}
}
}
RowLayout {
anchors.bottom: parent.bottom
anchors.right: parent.right
Button {
text: qsTr("Delete")
enabled: !modalStateDialog.isDefault
onClicked: {
projectModel.stateListModel.deleteState(stateIndex)
close()
}
}
Button {
text: qsTr("OK")
onClicked: {
close()
accepted()
}
}
Button {
text: qsTr("Cancel")
onClicked: close()
}
}
ListModel {
id: accountsModel
function removeAccount(_i) {
accountsModel.remove(_i)
stateAccounts.splice(_i, 1)
}
}
ListModel {
id: contractsModel
function removeContract(_i) {
contractsModel.remove(_i)
stateContracts.splice(_i, 1)
}
}
ListModel {
id: transactionsModel
function editTransaction(index) {
transactionDialog.stateAccounts = stateAccounts
transactionDialog.open(index,
transactionsModel.get(index))
}
function addTransaction() {
// Set next id here to work around Qt bug
// https://bugreports.qt-project.org/browse/QTBUG-41327
// Second call to signal handler would just edit the item that was just created, no harm done
var item = TransactionHelper.defaultTransaction()
transactionDialog.stateAccounts = stateAccounts
transactionDialog.open(transactionsModel.count, item)
}
function deleteTransaction(index) {
stateTransactions.splice(index, 1)
transactionsModel.remove(index)
}
function isUsed(secret) {
for (var i in stateTransactions) {
if (stateTransactions[i].sender === secret)
return true
}
return false
}
}
TransactionDialog {
id: transactionDialog
onAccepted: {
var item = transactionDialog.getItem()
if (transactionDialog.transactionIndex < transactionsModel.count) {
transactionsModel.set(
transactionDialog.transactionIndex,
item)
stateTransactions[transactionDialog.transactionIndex] = item
} else {
transactionsModel.append(item)
stateTransactions.push(item)
}
}
}
}
}
}
}