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.

589 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 {
10 years ago
id: modalStateDialog
modality: Qt.ApplicationModal
10 years ago
width: 630
height: 660
title: qsTr("Edit State")
visible: false
10 years ago
property alias stateTitle: titleField.text
property alias isDefault: defaultCheckBox.checked
10 years ago
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
10 years ago
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 {
10 years ago
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 {
10 years ago
Layout.preferredWidth: 85
DefaultLabel {
id: accountsLabel
10 years ago
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 {
10 years ago
Layout.preferredWidth: 85
text: qsTr("Default")
}
CheckBox {
id: defaultCheckBox
Layout.fillWidth: true
}
}
CommonSeparator {
Layout.fillWidth: true
}
RowLayout {
10 years ago
Layout.fillWidth: true
Rectangle {
10 years ago
Layout.preferredWidth: 85
DefaultLabel {
id: transactionsLabel
Layout.preferredWidth: 85
text: qsTr("Transactions")
}
Button {
10 years ago
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 {
10 years ago
id: transactionsView
Layout.fillWidth: true
model: transactionsModel
headerVisible: false
TableViewColumn {
role: "name"
title: qsTr("Name")
width: 150
delegate: Item {
RowLayout {
10 years ago
height: 30
width: parent.width
Button {
10 years ago
iconSource: "qrc:/qml/img/delete_sign.png"
action: deleteTransactionAction
}
Action {
id: deleteTransactionAction
tooltip: qsTr("Delete")
onTriggered: transactionsModel.deleteTransaction(
styleData.row)
10 years ago
}
Button {
10 years ago
iconSource: "qrc:/qml/img/edit.png"
action: editAction
visible: styleData.row
>= 0 ? !transactionsModel.get(
styleData.row).stdContract : false
10 years ago
width: 10
height: 10
Action {
id: editAction
tooltip: qsTr("Edit")
onTriggered: transactionsModel.editTransaction(
styleData.row)
10 years ago
}
}
DefaultLabel {
Layout.preferredWidth: 150
text: {
if (styleData.row >= 0)
return transactionsModel.get(
styleData.row).functionId
else
return ""
}
10 years ago
}
}
}
}
rowDelegate: Rectangle {
10 years ago
color: styleData.alternate ? "transparent" : "#f0f0f0"
height: 30
}
}
}
}
RowLayout {
anchors.bottom: parent.bottom
anchors.right: parent.right
10 years ago
Button {
text: qsTr("Delete")
10 years ago
enabled: !modalStateDialog.isDefault
onClicked: {
projectModel.stateListModel.deleteState(stateIndex)
close()
10 years ago
}
}
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)
}
}
}
}
}
}
}