600 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 {
10 years ago
role: "label"
10 years ago
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(
10 years ago
styleData.row).label
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: {
10 years ago
if (titleField.text === "")
alertDialog.open()
else
{
close()
accepted()
}
}
}
Button {
text: qsTr("Cancel")
onClicked: close()
}
}
10 years ago
MessageDialog
{
id: alertDialog
text: qsTr("Please provide a name.")
}
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)
}
}
}
}
}
}
}