arkpar
10 years ago
283 changed files with 0 additions and 45180 deletions
@ -1 +0,0 @@ |
|||
*.pro |
@ -1,97 +0,0 @@ |
|||
cmake_policy(SET CMP0015 NEW) |
|||
# let cmake autolink dependencies on windows |
|||
cmake_policy(SET CMP0020 NEW) |
|||
# this policy was introduced in cmake 3.0 |
|||
# remove if, once 3.0 will be used on unix |
|||
if (${CMAKE_MAJOR_VERSION} GREATER 2) |
|||
cmake_policy(SET CMP0043 OLD) |
|||
endif() |
|||
|
|||
set(CMAKE_INCLUDE_CURRENT_DIR ON) |
|||
|
|||
aux_source_directory(. SRC_LIST) |
|||
|
|||
include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS}) |
|||
include_directories(${Boost_INCLUDE_DIRS}) |
|||
include_directories(BEFORE ..) |
|||
|
|||
if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") AND NOT (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "3.6") AND NOT APPLE) |
|||
# Supress warnings for qt headers for clang+ccache |
|||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-inconsistent-missing-override") |
|||
endif () |
|||
|
|||
#TODO: remove once qt 5.5.1 is out |
|||
if (APPLE) |
|||
qt5_add_resources(UI_RESOURCES osx.qrc) |
|||
endif() |
|||
|
|||
find_package (Qt5WebEngine) |
|||
if (APPLE AND (NOT "${Qt5Core_VERSION_STRING}" VERSION_LESS "5.5")) |
|||
# TODO: remove indirect dependencies once macdeployqt is fixed |
|||
find_package (Qt5WebEngineCore) |
|||
find_package (Qt5DBus) |
|||
find_package (Qt5PrintSupport) |
|||
endif() |
|||
qt5_add_resources(UI_RESOURCES res.qrc qml.qrc) |
|||
|
|||
file(GLOB HEADERS "*.h") |
|||
|
|||
set(EXECUTABLE mix) |
|||
|
|||
if ("${Qt5WebEngine_VERSION_STRING}" VERSION_GREATER "5.3.0") |
|||
set (ETH_HAVE_WEBENGINE TRUE) |
|||
qt5_add_resources(UI_RESOURCES web.qrc) |
|||
else() |
|||
qt5_add_resources(UI_RESOURCES noweb.qrc) |
|||
endif() |
|||
|
|||
if (CMAKE_BUILD_TYPE MATCHES Debug) |
|||
add_definitions(-DQT_QML_DEBUG) |
|||
endif() |
|||
|
|||
# eth_add_executable is defined in cmake/EthExecutableHelper.cmake |
|||
eth_add_executable(${EXECUTABLE} |
|||
ICON mix |
|||
UI_RESOURCES ${UI_RESOURCES} |
|||
) |
|||
|
|||
|
|||
set(LIBRARIES "Qt5::Core;Qt5::Gui;Qt5::Widgets;Qt5::Network;Qt5::Quick;Qt5::Qml;webthree;ethereum;evm;ethcore;devcrypto;solidity;evmcore;devcore;jsqrc;web3jsonrpc") |
|||
if (${ETH_HAVE_WEBENGINE}) |
|||
add_definitions(-DETH_HAVE_WEBENGINE) |
|||
list(APPEND LIBRARIES "Qt5::WebEngine") |
|||
endif() |
|||
if (APPLE AND (NOT "${Qt5Core_VERSION_STRING}" VERSION_LESS "5.5")) |
|||
list(APPEND LIBRARIES "Qt5::WebEngineCore") |
|||
list(APPEND LIBRARIES "Qt5::DBus") |
|||
list(APPEND LIBRARIES "Qt5::PrintSupport") |
|||
endif() |
|||
|
|||
target_link_libraries(${EXECUTABLE} ${LIBRARIES}) |
|||
|
|||
# eth_install_executable is defined in cmake/EthExecutableHelper.cmake |
|||
eth_install_executable(${EXECUTABLE} |
|||
QMLDIR ${CMAKE_CURRENT_SOURCE_DIR}/qml |
|||
) |
|||
|
|||
#add qml asnd stdc files to project tree in Qt creator |
|||
file(GLOB_RECURSE QMLFILES "qml/*.*") |
|||
file(GLOB_RECURSE TESTFILES "test/qml/*.*") |
|||
file(GLOB_RECURSE SOLFILES "stdc/*.*") |
|||
add_custom_target(mix_qml SOURCES ${QMLFILES} ${SOLFILES} ${TESTFILES}) |
|||
|
|||
#test target |
|||
find_package(Qt5QuickTest REQUIRED) |
|||
find_package(Qt5Test REQUIRED) |
|||
set(TEST_EXECUTABLE mix_test) |
|||
list(APPEND LIBRARIES "Qt5::QuickTest") |
|||
list(APPEND LIBRARIES "Qt5::Test") |
|||
list(REMOVE_ITEM SRC_LIST "./main.cpp") |
|||
aux_source_directory(test SRC_LIST) |
|||
file(GLOB HEADERS "test/*.h") |
|||
add_executable(${TEST_EXECUTABLE} ${UI_RESOURCES} ${SRC_LIST} ${HEADERS}) |
|||
target_link_libraries(${TEST_EXECUTABLE} ${LIBRARIES}) |
|||
set_target_properties(${TEST_EXECUTABLE} PROPERTIES EXCLUDE_FROM_ALL 1 EXCLUDE_FROM_DEFAULT_BUILD 1) |
|||
|
|||
|
|||
|
@ -1,986 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file ClientModel.cpp
|
|||
* @author Yann yann@ethdev.com |
|||
* @author Arkadiy Paronyan arkadiy@ethdev.com |
|||
* @date 2015 |
|||
* Ethereum IDE client. |
|||
*/ |
|||
|
|||
// Make sure boost/asio.hpp is included before windows.h.
|
|||
#include <boost/asio.hpp> |
|||
|
|||
#include "ClientModel.h" |
|||
#include <QtConcurrent/QtConcurrent> |
|||
#include <QDebug> |
|||
#include <QQmlContext> |
|||
#include <QQmlApplicationEngine> |
|||
#include <QStandardPaths> |
|||
#include <jsonrpccpp/server.h> |
|||
#include <libethcore/CommonJS.h> |
|||
#include <libethereum/Transaction.h> |
|||
#include <libdevcore/FixedHash.h> |
|||
#include "DebuggingStateWrapper.h" |
|||
#include "Exceptions.h" |
|||
#include "QContractDefinition.h" |
|||
#include "QVariableDeclaration.h" |
|||
#include "QVariableDefinition.h" |
|||
#include "ContractCallDataEncoder.h" |
|||
#include "CodeModel.h" |
|||
#include "QEther.h" |
|||
#include "Web3Server.h" |
|||
#include "MixClient.h" |
|||
|
|||
using namespace dev; |
|||
using namespace dev::eth; |
|||
using namespace std; |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace mix |
|||
{ |
|||
|
|||
class RpcConnector: public jsonrpc::AbstractServerConnector |
|||
{ |
|||
public: |
|||
virtual bool StartListening() override { return true; } |
|||
virtual bool StopListening() override { return true; } |
|||
virtual bool SendResponse(string const& _response, void*) override |
|||
{ |
|||
m_response = QString::fromStdString(_response); |
|||
return true; |
|||
} |
|||
QString response() const { return m_response; } |
|||
|
|||
private: |
|||
QString m_response; |
|||
}; |
|||
|
|||
|
|||
ClientModel::ClientModel(): |
|||
m_running(false), m_rpcConnector(new RpcConnector()) |
|||
{ |
|||
qRegisterMetaType<QBigInt*>("QBigInt*"); |
|||
qRegisterMetaType<QVariableDefinition*>("QVariableDefinition*"); |
|||
qRegisterMetaType<QList<QVariableDefinition*>>("QList<QVariableDefinition*>"); |
|||
qRegisterMetaType<QList<QVariableDeclaration*>>("QList<QVariableDeclaration*>"); |
|||
qRegisterMetaType<QVariableDeclaration*>("QVariableDeclaration*"); |
|||
qRegisterMetaType<QSolidityType*>("QSolidityType*"); |
|||
qRegisterMetaType<QMachineState*>("QMachineState"); |
|||
qRegisterMetaType<QInstruction*>("QInstruction"); |
|||
qRegisterMetaType<QCode*>("QCode"); |
|||
qRegisterMetaType<QCallData*>("QCallData"); |
|||
qRegisterMetaType<RecordLogEntry*>("RecordLogEntry*"); |
|||
} |
|||
|
|||
ClientModel::~ClientModel() |
|||
{ |
|||
m_runFuture.waitForFinished(); |
|||
} |
|||
|
|||
void ClientModel::init(QString _dbpath) |
|||
{ |
|||
m_dbpath = _dbpath; |
|||
if (m_dbpath.isEmpty()) |
|||
m_client.reset(new MixClient(QStandardPaths::writableLocation(QStandardPaths::TempLocation).toStdString())); |
|||
else |
|||
m_client.reset(new MixClient(m_dbpath.toStdString())); |
|||
|
|||
m_ethAccounts = make_shared<FixedAccountHolder>([=](){return m_client.get();}, std::vector<KeyPair>()); |
|||
m_web3Server.reset(new Web3Server(*m_rpcConnector.get(), m_ethAccounts, std::vector<KeyPair>(), m_client.get())); |
|||
connect(m_web3Server.get(), &Web3Server::newTransaction, this, &ClientModel::onNewTransaction, Qt::DirectConnection); |
|||
} |
|||
|
|||
QString ClientModel::apiCall(QString const& _message) |
|||
{ |
|||
try |
|||
{ |
|||
m_rpcConnector->OnRequest(_message.toStdString(), nullptr); |
|||
return m_rpcConnector->response(); |
|||
} |
|||
catch (...) |
|||
{ |
|||
cerr << boost::current_exception_diagnostic_information(); |
|||
return QString(); |
|||
} |
|||
} |
|||
|
|||
void ClientModel::mine() |
|||
{ |
|||
if (m_mining) |
|||
BOOST_THROW_EXCEPTION(ExecutionStateException()); |
|||
m_mining = true; |
|||
emit miningStarted(); |
|||
emit miningStateChanged(); |
|||
m_runFuture = QtConcurrent::run([=]() |
|||
{ |
|||
try |
|||
{ |
|||
m_client->mine(); |
|||
newBlock(); |
|||
m_mining = false; |
|||
emit miningComplete(); |
|||
} |
|||
catch (...) |
|||
{ |
|||
m_mining = false; |
|||
cerr << boost::current_exception_diagnostic_information(); |
|||
emit runFailed(QString::fromStdString(boost::current_exception_diagnostic_information())); |
|||
} |
|||
emit miningStateChanged(); |
|||
}); |
|||
} |
|||
|
|||
QString ClientModel::newSecret() |
|||
{ |
|||
KeyPair a = KeyPair::create(); |
|||
return QString::fromStdString(dev::toHex(a.secret().ref())); |
|||
} |
|||
|
|||
QString ClientModel::address(QString const& _secret) |
|||
{ |
|||
return QString::fromStdString(dev::toHex(KeyPair(Secret(_secret.toStdString())).address().ref())); |
|||
} |
|||
|
|||
QString ClientModel::toHex(QString const& _int) |
|||
{ |
|||
return QString::fromStdString(dev::toHex(dev::u256(_int.toStdString()))); |
|||
} |
|||
|
|||
QString ClientModel::encodeAbiString(QString _string) |
|||
{ |
|||
ContractCallDataEncoder encoder; |
|||
return QString::fromStdString(dev::toHex(encoder.encodeBytes(_string))); |
|||
} |
|||
|
|||
QString ClientModel::encodeStringParam(QString const& _param) |
|||
{ |
|||
ContractCallDataEncoder encoder; |
|||
return QString::fromStdString(dev::toHex(encoder.encodeStringParam(_param, 32))); |
|||
} |
|||
|
|||
QStringList ClientModel::encodeParams(QVariant const& _param, QString const& _contract, QString const& _function) |
|||
{ |
|||
QStringList ret; |
|||
CompiledContract const& compilerRes = m_codeModel->contract(_contract); |
|||
QList<QVariableDeclaration*> paramsList; |
|||
shared_ptr<QContractDefinition> contractDef = compilerRes.sharedContract(); |
|||
if (_contract == _function) |
|||
paramsList = contractDef->constructor()->parametersList(); |
|||
else |
|||
for (QFunctionDefinition* tf: contractDef->functionsList()) |
|||
if (tf->name() == _function) |
|||
{ |
|||
paramsList = tf->parametersList(); |
|||
break; |
|||
} |
|||
if (paramsList.length() > 0) |
|||
for (QVariableDeclaration* var: paramsList) |
|||
{ |
|||
ContractCallDataEncoder encoder; |
|||
QSolidityType const* type = var->type(); |
|||
QVariant value = _param.toMap().value(var->name()); |
|||
encoder.encode(value, type->type()); |
|||
ret.push_back(QString::fromStdString(dev::toHex(encoder.encodedData()))); |
|||
} |
|||
return ret; |
|||
} |
|||
|
|||
QVariantMap ClientModel::contractAddresses() const |
|||
{ |
|||
QVariantMap res; |
|||
for (auto const& c: m_contractAddresses) |
|||
res.insert(c.first.first, QString::fromStdString(toJS(c.second))); |
|||
return res; |
|||
} |
|||
|
|||
QVariantList ClientModel::gasCosts() const |
|||
{ |
|||
QVariantList res; |
|||
for (auto const& c: m_gasCosts) |
|||
res.append(QVariant::fromValue(static_cast<int>(c))); |
|||
return res; |
|||
} |
|||
|
|||
void ClientModel::addAccount(QString const& _secret) |
|||
{ |
|||
KeyPair key(Secret(_secret.toStdString())); |
|||
m_accountsSecret.push_back(key); |
|||
Address address = key.address(); |
|||
m_accounts[address] = Account(u256(0), Account::NormalCreation); |
|||
m_ethAccounts->setAccounts(m_accountsSecret); |
|||
} |
|||
|
|||
QString ClientModel::resolveAddress(QString const& _secret) |
|||
{ |
|||
KeyPair key(Secret(_secret.toStdString())); |
|||
return "0x" + QString::fromStdString(key.address().hex()); |
|||
} |
|||
|
|||
void ClientModel::setupScenario(QVariantMap _scenario) |
|||
{ |
|||
onStateReset(); |
|||
WriteGuard(x_queueTransactions); |
|||
m_running = true; |
|||
|
|||
QVariantList blocks = _scenario.value("blocks").toList(); |
|||
QVariantList stateAccounts = _scenario.value("accounts").toList(); |
|||
QVariantList stateContracts = _scenario.value("contracts").toList(); |
|||
|
|||
m_accounts.clear(); |
|||
m_accountsSecret.clear(); |
|||
for (auto const& b: stateAccounts) |
|||
{ |
|||
QVariantMap account = b.toMap(); |
|||
Address address = {}; |
|||
if (account.contains("secret")) |
|||
{ |
|||
KeyPair key(Secret(account.value("secret").toString().toStdString())); |
|||
m_accountsSecret.push_back(key); |
|||
address = key.address(); |
|||
} |
|||
else if (account.contains("address")) |
|||
address = Address(fromHex(account.value("address").toString().toStdString())); |
|||
if (!address) |
|||
continue; |
|||
|
|||
m_accounts[address] = Account(qvariant_cast<QEther*>(account.value("balance"))->toU256Wei(), Account::NormalCreation); |
|||
} |
|||
m_ethAccounts->setAccounts(m_accountsSecret); |
|||
|
|||
for (auto const& c: stateContracts) |
|||
{ |
|||
QVariantMap contract = c.toMap(); |
|||
Address address = Address(fromHex(contract.value("address").toString().toStdString())); |
|||
Account account(qvariant_cast<QEther*>(contract.value("balance"))->toU256Wei(), Account::ContractConception); |
|||
bytes code = fromHex(contract.value("code").toString().toStdString()); |
|||
account.setCode(std::move(code)); |
|||
QVariantMap storageMap = contract.value("storage").toMap(); |
|||
for(auto s = storageMap.cbegin(); s != storageMap.cend(); ++s) |
|||
account.setStorage(fromBigEndian<u256>(fromHex(s.key().toStdString())), fromBigEndian<u256>(fromHex(s.value().toString().toStdString()))); |
|||
m_accounts[address] = account; |
|||
} |
|||
|
|||
bool trToExecute = false; |
|||
for (auto const& b: blocks) |
|||
{ |
|||
QVariantList transactions = b.toMap().value("transactions").toList(); |
|||
m_queueTransactions.push_back(transactions); |
|||
trToExecute = transactions.size() > 0; |
|||
} |
|||
m_client->resetState(m_accounts, Secret(_scenario.value("miner").toMap().value("secret").toString().toStdString())); |
|||
if (m_queueTransactions.count() > 0 && trToExecute) |
|||
{ |
|||
setupExecutionChain(); |
|||
processNextTransactions(); |
|||
} |
|||
else |
|||
m_running = false; |
|||
} |
|||
|
|||
void ClientModel::setupExecutionChain() |
|||
{ |
|||
connect(this, &ClientModel::newBlock, this, &ClientModel::processNextTransactions, Qt::QueuedConnection); |
|||
connect(this, &ClientModel::runFailed, this, &ClientModel::stopExecution, Qt::QueuedConnection); |
|||
connect(this, &ClientModel::runStateChanged, this, &ClientModel::finalizeBlock, Qt::QueuedConnection); |
|||
} |
|||
|
|||
void ClientModel::stopExecution() |
|||
{ |
|||
disconnect(this, &ClientModel::newBlock, this, &ClientModel::processNextTransactions); |
|||
disconnect(this, &ClientModel::runStateChanged, this, &ClientModel::finalizeBlock); |
|||
disconnect(this, &ClientModel::runFailed, this, &ClientModel::stopExecution); |
|||
m_running = false; |
|||
} |
|||
|
|||
void ClientModel::finalizeBlock() |
|||
{ |
|||
m_queueTransactions.pop_front();// pop last execution group. The last block is never mined (pending block)
|
|||
if (m_queueTransactions.size() > 0) |
|||
mine(); |
|||
else |
|||
{ |
|||
stopExecution(); |
|||
emit runComplete(); |
|||
} |
|||
} |
|||
|
|||
TransactionSettings ClientModel::transaction(QVariant const& _tr) const |
|||
{ |
|||
QVariantMap transaction = _tr.toMap(); |
|||
QString contractId = transaction.value("contractId").toString(); |
|||
QString functionId = transaction.value("functionId").toString(); |
|||
bool gasAuto = transaction.value("gasAuto").toBool(); |
|||
u256 gas = 0; |
|||
if (transaction.value("gas").data()) |
|||
gas = boost::get<u256>(qvariant_cast<QBigInt*>(transaction.value("gas"))->internalValue()); |
|||
else |
|||
gasAuto = true; |
|||
|
|||
u256 value = (qvariant_cast<QEther*>(transaction.value("value")))->toU256Wei(); |
|||
u256 gasPrice = (qvariant_cast<QEther*>(transaction.value("gasPrice")))->toU256Wei(); |
|||
QString sender = transaction.value("sender").toString(); |
|||
bool isContractCreation = transaction.value("isContractCreation").toBool(); |
|||
bool isFunctionCall = transaction.value("isFunctionCall").toBool(); |
|||
if (contractId.isEmpty() && m_codeModel->hasContract()) //TODO: This is to support old project files, remove later
|
|||
contractId = m_codeModel->contracts().keys()[0]; |
|||
Secret f = Secret(sender.toStdString()); |
|||
TransactionSettings transactionSettings(contractId, functionId, value, gas, gasAuto, gasPrice, f, isContractCreation, isFunctionCall); |
|||
transactionSettings.parameterValues = transaction.value("parameters").toMap(); |
|||
if (contractId == functionId || functionId == "Constructor") |
|||
transactionSettings.functionId.clear(); |
|||
return transactionSettings; |
|||
} |
|||
|
|||
void ClientModel::processNextTransactions() |
|||
{ |
|||
WriteGuard(x_queueTransactions); |
|||
vector<TransactionSettings> transactionSequence; |
|||
for (auto const& t: m_queueTransactions.front()) |
|||
{ |
|||
TransactionSettings transactionSettings = transaction(t); |
|||
transactionSequence.push_back(transactionSettings); |
|||
} |
|||
executeSequence(transactionSequence); |
|||
} |
|||
|
|||
void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence) |
|||
{ |
|||
if (m_running) |
|||
{ |
|||
qWarning() << "Waiting for current execution to complete"; |
|||
m_runFuture.waitForFinished(); |
|||
} |
|||
emit runStarted(); |
|||
//run sequence
|
|||
m_runFuture = QtConcurrent::run([=]() |
|||
{ |
|||
try |
|||
{ |
|||
m_gasCosts.clear(); |
|||
for (TransactionSettings const& transaction: _sequence) |
|||
{ |
|||
std::pair<QString, int> ctrInstance = resolvePair(transaction.contractId); |
|||
QString address = resolveToken(ctrInstance); |
|||
if (!transaction.isFunctionCall) |
|||
{ |
|||
callAddress(Address(address.toStdString()), bytes(), transaction); |
|||
onNewTransaction(); |
|||
continue; |
|||
} |
|||
ContractCallDataEncoder encoder; |
|||
//encode data
|
|||
CompiledContract const& compilerRes = m_codeModel->contract(ctrInstance.first); |
|||
QFunctionDefinition const* f = nullptr; |
|||
bytes contractCode = compilerRes.bytes(); |
|||
shared_ptr<QContractDefinition> contractDef = compilerRes.sharedContract(); |
|||
if (transaction.functionId.isEmpty()) |
|||
f = contractDef->constructor(); |
|||
else |
|||
for (QFunctionDefinition const* tf: contractDef->functionsList()) |
|||
if (tf->name() == transaction.functionId) |
|||
{ |
|||
f = tf; |
|||
break; |
|||
} |
|||
if (!f) |
|||
emit runFailed("Function '" + transaction.functionId + tr("' not found. Please check transactions or the contract code.")); |
|||
if (!transaction.functionId.isEmpty()) |
|||
encoder.encode(f); |
|||
for (QVariableDeclaration const* p: f->parametersList()) |
|||
{ |
|||
QSolidityType const* type = p->type(); |
|||
QVariant value = transaction.parameterValues.value(p->name()); |
|||
if (type->type().type == SolidityType::Type::Address) |
|||
{ |
|||
if (type->array()) |
|||
{ |
|||
QJsonArray jsonDoc = QJsonDocument::fromJson(value.toString().toUtf8()).array(); |
|||
int k = 0; |
|||
for (QJsonValue const& item: jsonDoc) |
|||
{ |
|||
if (item.toString().startsWith("<")) |
|||
{ |
|||
std::pair<QString, int> ctrParamInstance = resolvePair(item.toString()); |
|||
jsonDoc.replace(k, resolveToken(ctrParamInstance)); |
|||
} |
|||
k++; |
|||
} |
|||
QJsonDocument doc(jsonDoc); |
|||
value = QVariant(doc.toJson(QJsonDocument::Compact)); |
|||
} |
|||
else if (value.toString().startsWith("<")) |
|||
{ |
|||
std::pair<QString, int> ctrParamInstance = resolvePair(value.toString()); |
|||
value = QVariant(resolveToken(ctrParamInstance)); |
|||
} |
|||
} |
|||
encoder.encode(value, type->type()); |
|||
} |
|||
|
|||
if (transaction.functionId.isEmpty() || transaction.functionId == ctrInstance.first) |
|||
{ |
|||
bytes param = encoder.encodedData(); |
|||
contractCode.insert(contractCode.end(), param.begin(), param.end()); |
|||
Address newAddress = deployContract(contractCode, transaction); |
|||
std::pair<QString, int> contractToken = retrieveToken(transaction.contractId); |
|||
m_contractAddresses[contractToken] = newAddress; |
|||
m_contractNames[newAddress] = contractToken.first; |
|||
contractAddressesChanged(); |
|||
gasCostsChanged(); |
|||
} |
|||
else |
|||
{ |
|||
auto contractAddressIter = m_contractAddresses.find(ctrInstance); |
|||
if (contractAddressIter == m_contractAddresses.end()) |
|||
{ |
|||
emit runFailed("Contract '" + transaction.contractId + tr(" not deployed.") + "' " + tr(" Cannot call ") + transaction.functionId); |
|||
Address fakeAddress = Address::random(); |
|||
std::pair<QString, int> contractToken = resolvePair(transaction.contractId); |
|||
m_contractNames[fakeAddress] = contractToken.first; |
|||
callAddress(fakeAddress, encoder.encodedData(), transaction); //Transact to a random fake address to that transaction is added to the list anyway
|
|||
} |
|||
else |
|||
callAddress(contractAddressIter->second, encoder.encodedData(), transaction); |
|||
} |
|||
m_gasCosts.append(m_client->lastExecution().gasUsed); |
|||
onNewTransaction(); |
|||
TransactionException exception = m_client->lastExecution().excepted; |
|||
if (exception != TransactionException::None) |
|||
break; |
|||
} |
|||
emit runComplete(); |
|||
} |
|||
catch(boost::exception const&) |
|||
{ |
|||
cerr << boost::current_exception_diagnostic_information(); |
|||
emit runFailed(QString::fromStdString(boost::current_exception_diagnostic_information())); |
|||
} |
|||
catch(exception const& e) |
|||
{ |
|||
cerr << boost::current_exception_diagnostic_information(); |
|||
emit runFailed(e.what()); |
|||
} |
|||
emit runStateChanged(); |
|||
}); |
|||
} |
|||
|
|||
void ClientModel::executeTr(QVariantMap _tr) |
|||
{ |
|||
WriteGuard(x_queueTransactions); |
|||
QVariantList trs; |
|||
trs.push_back(_tr); |
|||
m_queueTransactions.push_back(trs); |
|||
if (!m_running) |
|||
{ |
|||
m_running = true; |
|||
setupExecutionChain(); |
|||
processNextTransactions(); |
|||
} |
|||
} |
|||
|
|||
std::pair<QString, int> ClientModel::resolvePair(QString const& _contractId) |
|||
{ |
|||
std::pair<QString, int> ret = std::make_pair(_contractId, 0); |
|||
if (_contractId.startsWith("<") && _contractId.endsWith(">")) |
|||
{ |
|||
QStringList values = ret.first.remove("<").remove(">").split(" - "); |
|||
ret = std::make_pair(values[0], values[1].toUInt()); |
|||
} |
|||
if (_contractId.startsWith("0x")) |
|||
ret = std::make_pair(_contractId, -2); |
|||
return ret; |
|||
} |
|||
|
|||
QString ClientModel::resolveToken(std::pair<QString, int> const& _value) |
|||
{ |
|||
if (_value.second == -2) //-2: first contains a real address
|
|||
return _value.first; |
|||
else if (m_contractAddresses.size() > 0) |
|||
return QString::fromStdString("0x" + dev::toHex(m_contractAddresses[_value].ref())); |
|||
else |
|||
return _value.first; |
|||
} |
|||
|
|||
std::pair<QString, int> ClientModel::retrieveToken(QString const& _value) |
|||
{ |
|||
std::pair<QString, int> ret; |
|||
ret.first = _value; |
|||
ret.second = m_contractAddresses.size(); |
|||
return ret; |
|||
} |
|||
|
|||
void ClientModel::showDebugger() |
|||
{ |
|||
ExecutionResult last = m_client->lastExecution(); |
|||
showDebuggerForTransaction(last); |
|||
} |
|||
|
|||
void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t) |
|||
{ |
|||
//we need to wrap states in a QObject before sending to QML.
|
|||
QDebugData* debugData = new QDebugData(); |
|||
QQmlEngine::setObjectOwnership(debugData, QQmlEngine::JavaScriptOwnership); |
|||
QList<QCode*> codes; |
|||
QList<QHash<int, int>> codeMaps; |
|||
QList<AssemblyItems> codeItems; |
|||
QList<CompiledContract const*> contracts; |
|||
for (MachineCode const& code: _t.executionCode) |
|||
{ |
|||
QHash<int, int> codeMap; |
|||
codes.push_back(QMachineState::getHumanReadableCode(debugData, code.address, code.code, codeMap)); |
|||
codeMaps.push_back(move(codeMap)); |
|||
//try to resolve contract for source level debugging
|
|||
auto nameIter = m_contractNames.find(code.address); |
|||
CompiledContract const* compilerRes = nullptr; |
|||
if (nameIter != m_contractNames.end() && (compilerRes = m_codeModel->tryGetContract(nameIter->second))) //returned object is guaranteed to live till the end of event handler in main thread
|
|||
{ |
|||
eth::AssemblyItems assemblyItems = !_t.isConstructor() ? compilerRes->assemblyItems() : compilerRes->constructorAssemblyItems(); |
|||
codes.back()->setDocument(compilerRes->documentId()); |
|||
codeItems.push_back(move(assemblyItems)); |
|||
contracts.push_back(compilerRes); |
|||
} |
|||
else |
|||
{ |
|||
codeItems.push_back(AssemblyItems()); |
|||
contracts.push_back(nullptr); |
|||
} |
|||
} |
|||
|
|||
QList<QCallData*> data; |
|||
for (bytes const& d: _t.transactionData) |
|||
data.push_back(QMachineState::getDebugCallData(debugData, d)); |
|||
|
|||
QVariantList states; |
|||
QVariantList solCallStack; |
|||
map<int, QVariableDeclaration*> solLocals; //<stack pos, decl>
|
|||
map<QString, QVariableDeclaration*> storageDeclarations; //<name, decl>
|
|||
|
|||
unsigned prevInstructionIndex = 0; |
|||
for (MachineState const& s: _t.machineStates) |
|||
{ |
|||
int instructionIndex = codeMaps[s.codeIndex][static_cast<unsigned>(s.curPC)]; |
|||
QSolState* solState = nullptr; |
|||
if (!codeItems[s.codeIndex].empty() && contracts[s.codeIndex]) |
|||
{ |
|||
CompiledContract const* contract = contracts[s.codeIndex]; |
|||
AssemblyItem const& instruction = codeItems[s.codeIndex][instructionIndex]; |
|||
|
|||
if (instruction.type() == eth::Push && !instruction.data()) |
|||
{ |
|||
//register new local variable initialization
|
|||
auto localIter = contract->locals().find(LocationPair(instruction.getLocation().start, instruction.getLocation().end)); |
|||
if (localIter != contract->locals().end()) |
|||
solLocals[s.stack.size()] = new QVariableDeclaration(debugData, localIter.value().name.toStdString(), localIter.value().type); |
|||
} |
|||
|
|||
if (instruction.type() == eth::Tag) |
|||
{ |
|||
//track calls into functions
|
|||
AssemblyItem const& prevInstruction = codeItems[s.codeIndex][prevInstructionIndex]; |
|||
QString functionName = m_codeModel->resolveFunctionName(instruction.getLocation()); |
|||
if (!functionName.isEmpty() && ((prevInstruction.getJumpType() == AssemblyItem::JumpType::IntoFunction) || solCallStack.empty())) |
|||
solCallStack.push_front(QVariant::fromValue(functionName)); |
|||
else if (prevInstruction.getJumpType() == AssemblyItem::JumpType::OutOfFunction && !solCallStack.empty()) |
|||
{ |
|||
solCallStack.pop_front(); |
|||
solLocals.clear(); |
|||
} |
|||
} |
|||
|
|||
//format solidity context values
|
|||
QVariantMap locals; |
|||
QVariantList localDeclarations; |
|||
QVariantMap localValues; |
|||
for (auto l: solLocals) |
|||
if (l.first < (int)s.stack.size()) |
|||
{ |
|||
if (l.second->type()->name().startsWith("mapping")) |
|||
break; //mapping type not yet managed
|
|||
localDeclarations.push_back(QVariant::fromValue(l.second)); |
|||
localValues[l.second->name()] = formatValue(l.second->type()->type(), s.stack[l.first]); |
|||
} |
|||
locals["variables"] = localDeclarations; |
|||
locals["values"] = localValues; |
|||
|
|||
QVariantMap storage; |
|||
QVariantList storageDeclarationList; |
|||
QVariantMap storageValues; |
|||
for (auto st: s.storage) |
|||
if (st.first < numeric_limits<unsigned>::max()) |
|||
{ |
|||
auto storageIter = contract->storage().find(static_cast<unsigned>(st.first)); |
|||
if (storageIter != contract->storage().end()) |
|||
{ |
|||
QVariableDeclaration* storageDec = nullptr; |
|||
for (SolidityDeclaration const& codeDec : storageIter.value()) |
|||
{ |
|||
if (codeDec.type.name.startsWith("mapping")) |
|||
continue; //mapping type not yet managed
|
|||
auto decIter = storageDeclarations.find(codeDec.name); |
|||
if (decIter != storageDeclarations.end()) |
|||
storageDec = decIter->second; |
|||
else |
|||
{ |
|||
storageDec = new QVariableDeclaration(debugData, codeDec.name.toStdString(), codeDec.type); |
|||
storageDeclarations[storageDec->name()] = storageDec; |
|||
} |
|||
storageDeclarationList.push_back(QVariant::fromValue(storageDec)); |
|||
storageValues[storageDec->name()] = formatStorageValue(storageDec->type()->type(), s.storage, codeDec.offset, codeDec.slot); |
|||
} |
|||
} |
|||
} |
|||
storage["variables"] = storageDeclarationList; |
|||
storage["values"] = storageValues; |
|||
|
|||
prevInstructionIndex = instructionIndex; |
|||
|
|||
// filter out locations that match whole function or contract
|
|||
SourceLocation location = instruction.getLocation(); |
|||
QString source; |
|||
if (location.sourceName) |
|||
source = QString::fromUtf8(location.sourceName->c_str()); |
|||
if (m_codeModel->isContractOrFunctionLocation(location)) |
|||
location = dev::SourceLocation(-1, -1, location.sourceName); |
|||
|
|||
solState = new QSolState(debugData, move(storage), move(solCallStack), move(locals), location.start, location.end, source); |
|||
} |
|||
|
|||
states.append(QVariant::fromValue(new QMachineState(debugData, instructionIndex, s, codes[s.codeIndex], data[s.dataIndex], solState))); |
|||
} |
|||
|
|||
debugData->setStates(move(states)); |
|||
debugDataReady(debugData); |
|||
} |
|||
|
|||
QVariant ClientModel::formatValue(SolidityType const& _type, u256 const& _value) |
|||
{ |
|||
ContractCallDataEncoder decoder; |
|||
bytes val = toBigEndian(_value); |
|||
QVariant res = decoder.decode(_type, val); |
|||
return res; |
|||
} |
|||
|
|||
QVariant ClientModel::formatStorageValue(SolidityType const& _type, unordered_map<u256, u256> const& _storage, unsigned _offset, u256 const& _slot) |
|||
{ |
|||
u256 slot = _slot; |
|||
QVariantList values; |
|||
ContractCallDataEncoder decoder; |
|||
u256 count = 1; |
|||
if (_type.dynamicSize) |
|||
{ |
|||
count = _storage.at(slot); |
|||
slot = fromBigEndian<u256>(sha3(toBigEndian(slot)).asBytes()); |
|||
} |
|||
else if (_type.array) |
|||
count = _type.count; |
|||
|
|||
unsigned offset = _offset; |
|||
while (count--) |
|||
{ |
|||
|
|||
auto slotIter = _storage.find(slot); |
|||
u256 slotValue = slotIter != _storage.end() ? slotIter->second : u256(); |
|||
bytes slotBytes = toBigEndian(slotValue); |
|||
auto start = slotBytes.end() - _type.size - offset; |
|||
bytes val(32 - _type.size); //prepend with zeroes
|
|||
if (_type.type == SolidityType::SignedInteger && (*start & 0x80)) //extend sign
|
|||
std::fill(val.begin(), val.end(), 0xff); |
|||
val.insert(val.end(), start, start + _type.size); |
|||
values.append(decoder.decode(_type, val)); |
|||
offset += _type.size; |
|||
if ((offset + _type.size) > 32) |
|||
{ |
|||
slot++; |
|||
offset = 0; |
|||
} |
|||
} |
|||
|
|||
if (!_type.array) |
|||
return values[0]; |
|||
|
|||
return QVariant::fromValue(values); |
|||
} |
|||
|
|||
void ClientModel::emptyRecord() |
|||
{ |
|||
debugDataReady(new QDebugData()); |
|||
} |
|||
|
|||
void ClientModel::debugRecord(unsigned _index) |
|||
{ |
|||
ExecutionResult e = m_client->execution(_index); |
|||
showDebuggerForTransaction(e); |
|||
} |
|||
|
|||
Address ClientModel::deployContract(bytes const& _code, TransactionSettings const& _ctrTransaction) |
|||
{ |
|||
eth::TransactionSkeleton ts; |
|||
ts.creation = true; |
|||
ts.value = _ctrTransaction.value; |
|||
ts.data = _code; |
|||
ts.gas = _ctrTransaction.gas; |
|||
ts.gasPrice = _ctrTransaction.gasPrice; |
|||
ts.from = toAddress(_ctrTransaction.sender); |
|||
return m_client->submitTransaction(ts, _ctrTransaction.sender, _ctrTransaction.gasAuto).second; |
|||
} |
|||
|
|||
void ClientModel::callAddress(Address const& _contract, bytes const& _data, TransactionSettings const& _tr) |
|||
{ |
|||
eth::TransactionSkeleton ts; |
|||
ts.creation = false; |
|||
ts.value = _tr.value; |
|||
ts.to = _contract; |
|||
ts.data = _data; |
|||
ts.gas = _tr.gas; |
|||
ts.gasPrice = _tr.gasPrice; |
|||
ts.from = toAddress(_tr.sender); |
|||
m_client->submitTransaction(ts, _tr.sender, _tr.gasAuto); |
|||
} |
|||
|
|||
RecordLogEntry* ClientModel::lastBlock() const |
|||
{ |
|||
eth::BlockInfo blockInfo = m_client->blockInfo(); |
|||
stringstream strGas; |
|||
strGas << blockInfo.gasUsed(); |
|||
stringstream strNumber; |
|||
strNumber << blockInfo.number(); |
|||
RecordLogEntry* record = new RecordLogEntry(0, QString::fromStdString(strNumber.str()), tr(" - Block - "), tr("Hash: ") + QString(QString::fromStdString(dev::toHex(blockInfo.hash().ref()))), QString(), QString(), QString(), false, RecordLogEntry::RecordType::Block, QString::fromStdString(strGas.str()), QString(), tr("Block"), QVariantMap(), QVariantMap(), QVariantList()); |
|||
QQmlEngine::setObjectOwnership(record, QQmlEngine::JavaScriptOwnership); |
|||
return record; |
|||
} |
|||
|
|||
void ClientModel::onStateReset() |
|||
{ |
|||
m_contractAddresses.clear(); |
|||
m_contractNames.clear(); |
|||
m_stdContractAddresses.clear(); |
|||
m_stdContractNames.clear(); |
|||
m_queueTransactions.clear(); |
|||
emit stateCleared(); |
|||
} |
|||
|
|||
void ClientModel::onNewTransaction() |
|||
{ |
|||
ExecutionResult const& tr = m_client->lastExecution(); |
|||
|
|||
switch (tr.excepted) |
|||
{ |
|||
case TransactionException::None: |
|||
break; |
|||
case TransactionException::NotEnoughCash: |
|||
emit runFailed("Insufficient balance"); |
|||
break; |
|||
case TransactionException::OutOfGasIntrinsic: |
|||
case TransactionException::OutOfGasBase: |
|||
case TransactionException::OutOfGas: |
|||
emit runFailed("Not enough gas"); |
|||
break; |
|||
case TransactionException::BlockGasLimitReached: |
|||
emit runFailed("Block gas limit reached"); |
|||
break; |
|||
case TransactionException::BadJumpDestination: |
|||
emit runFailed("Solidity exception (bad jump)"); |
|||
break; |
|||
case TransactionException::OutOfStack: |
|||
emit runFailed("Out of stack"); |
|||
break; |
|||
case TransactionException::StackUnderflow: |
|||
emit runFailed("Stack underflow"); |
|||
//these should not happen in mix
|
|||
case TransactionException::Unknown: |
|||
case TransactionException::BadInstruction: |
|||
case TransactionException::InvalidSignature: |
|||
case TransactionException::InvalidNonce: |
|||
case TransactionException::InvalidFormat: |
|||
case TransactionException::BadRLP: |
|||
emit runFailed("Internal execution error"); |
|||
break; |
|||
} |
|||
|
|||
|
|||
unsigned block = m_client->number() + 1; |
|||
unsigned recordIndex = tr.executonIndex; |
|||
QString transactionIndex = tr.isCall() ? QObject::tr("Call") : QString("%1:%2").arg(block).arg(tr.transactionIndex); |
|||
QString address = QString::fromStdString(toJS(tr.address)); |
|||
QString value = QString::fromStdString(toString(tr.value)); |
|||
QString contract = address; |
|||
QString function; |
|||
QString returned; |
|||
QString gasUsed; |
|||
|
|||
bool creation = (bool)tr.contractAddress; |
|||
|
|||
if (!tr.isCall()) |
|||
gasUsed = QString::fromStdString(toString(tr.gasUsed)); |
|||
|
|||
//TODO: handle value transfer
|
|||
FixedHash<4> functionHash; |
|||
bool abi = false; |
|||
if (creation) |
|||
{ |
|||
//contract creation
|
|||
function = QObject::tr("Constructor"); |
|||
address = QObject::tr("(Create contract)"); |
|||
} |
|||
else |
|||
{ |
|||
//transaction/call
|
|||
if (tr.inputParameters.size() >= 4) |
|||
{ |
|||
functionHash = FixedHash<4>(tr.inputParameters.data(), FixedHash<4>::ConstructFromPointer); |
|||
function = QString::fromStdString(toJS(functionHash)); |
|||
abi = true; |
|||
} |
|||
else |
|||
function = QObject::tr("<none>"); |
|||
} |
|||
|
|||
if (creation) |
|||
returned = QString::fromStdString(toJS(tr.contractAddress)); |
|||
|
|||
Address contractAddress = (bool)tr.address ? tr.address : tr.contractAddress; |
|||
auto contractAddressIter = m_contractNames.find(contractAddress); |
|||
QVariantMap inputParameters; |
|||
QVariantMap returnParameters; |
|||
QVariantList logs; |
|||
if (contractAddressIter != m_contractNames.end()) |
|||
{ |
|||
ContractCallDataEncoder encoder; |
|||
CompiledContract const& compilerRes = m_codeModel->contract(contractAddressIter->second); |
|||
const QContractDefinition* def = compilerRes.contract(); |
|||
contract = def->name(); |
|||
if (creation) |
|||
function = contract; |
|||
if (abi) |
|||
{ |
|||
QFunctionDefinition const* funcDef = def->getFunction(functionHash); |
|||
if (funcDef) |
|||
{ |
|||
function = funcDef->name(); |
|||
QStringList returnValues = encoder.decode(funcDef->returnParameters(), tr.result.output); |
|||
returned += "("; |
|||
returned += returnValues.join(", "); |
|||
returned += ")"; |
|||
|
|||
QStringList returnParams = encoder.decode(funcDef->returnParameters(), tr.result.output); |
|||
for (int k = 0; k < returnParams.length(); ++k) |
|||
returnParameters.insert(funcDef->returnParameters().at(k)->name(), returnParams.at(k)); |
|||
|
|||
bytes data = tr.inputParameters; |
|||
data.erase(data.begin(), data.begin() + 4); |
|||
QStringList parameters = encoder.decode(funcDef->parametersList(), data); |
|||
for (int k = 0; k < parameters.length(); ++k) |
|||
inputParameters.insert(funcDef->parametersList().at(k)->name(), parameters.at(k)); |
|||
} |
|||
} |
|||
|
|||
// Fill generated logs and decode parameters
|
|||
for (auto const& log: tr.logs) |
|||
{ |
|||
QVariantMap l; |
|||
l.insert("address", QString::fromStdString(log.address.hex())); |
|||
std::ostringstream s; |
|||
s << log.data; |
|||
l.insert("data", QString::fromStdString(s.str())); |
|||
std::ostringstream streamTopic; |
|||
streamTopic << log.topics; |
|||
l.insert("topic", QString::fromStdString(streamTopic.str())); |
|||
auto const& sign = log.topics.front(); // first hash supposed to be the event signature. To check
|
|||
auto dataIterator = log.data.begin(); |
|||
int topicDataIndex = 1; |
|||
for (auto const& event: def->eventsList()) |
|||
{ |
|||
if (sign == event->fullHash()) |
|||
{ |
|||
QVariantList paramsList; |
|||
l.insert("name", event->name()); |
|||
for (auto const& e: event->parametersList()) |
|||
{ |
|||
bytes data; |
|||
QString param; |
|||
if (!e->isIndexed()) |
|||
{ |
|||
data = bytes(dataIterator, dataIterator + 32); |
|||
dataIterator = dataIterator + 32; |
|||
} |
|||
else |
|||
{ |
|||
data = log.topics.at(topicDataIndex).asBytes(); |
|||
topicDataIndex++; |
|||
} |
|||
param = encoder.decode(e, data); |
|||
QVariantMap p; |
|||
p.insert("indexed", e->isIndexed()); |
|||
p.insert("value", param); |
|||
p.insert("name", e->name()); |
|||
paramsList.push_back(p); |
|||
} |
|||
l.insert("param", paramsList); |
|||
break; |
|||
} |
|||
} |
|||
logs.push_back(l); |
|||
} |
|||
} |
|||
|
|||
QString sender; |
|||
for (auto const& secret: m_accountsSecret) |
|||
{ |
|||
if (secret.address() == tr.sender) |
|||
{ |
|||
sender = QString::fromStdString(dev::toHex(secret.secret().ref())); |
|||
break; |
|||
} |
|||
} |
|||
QString label; |
|||
if (function != QObject::tr("<none>")) |
|||
label = contract + "." + function + "()"; |
|||
else |
|||
label = contract; |
|||
|
|||
if (!creation) |
|||
for (auto const& ctr: m_contractAddresses) |
|||
{ |
|||
if (ctr.second == tr.address) |
|||
{ |
|||
contract = "<" + ctr.first.first + " - " + QString::number(ctr.first.second) + ">"; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
RecordLogEntry* log = new RecordLogEntry(recordIndex, transactionIndex, contract, function, value, address, returned, tr.isCall(), RecordLogEntry::RecordType::Transaction, |
|||
gasUsed, sender, label, inputParameters, returnParameters, logs); |
|||
QQmlEngine::setObjectOwnership(log, QQmlEngine::JavaScriptOwnership); |
|||
emit newRecord(log); |
|||
|
|||
// retrieving all accounts balance
|
|||
QVariantMap state; |
|||
QVariantMap accountBalances; |
|||
for (auto const& ctr : m_contractAddresses) |
|||
{ |
|||
u256 wei = m_client->balanceAt(ctr.second, PendingBlock); |
|||
accountBalances.insert("0x" + QString::fromStdString(ctr.second.hex()), QEther(wei, QEther::Wei).format()); |
|||
} |
|||
for (auto const& account : m_accounts) |
|||
{ |
|||
u256 wei = m_client->balanceAt(account.first, PendingBlock); |
|||
accountBalances.insert("0x" + QString::fromStdString(account.first.hex()), QEther(wei, QEther::Wei).format()); |
|||
} |
|||
state.insert("accounts", accountBalances); |
|||
emit newState(recordIndex, state); |
|||
} |
|||
|
|||
} |
|||
} |
@ -1,298 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file ClientModel.h
|
|||
* @author Yann yann@ethdev.com |
|||
* @author Arkadiy Paronyan arkadiy@ethdev.com |
|||
* @date 2015 |
|||
* Ethereum IDE client. |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <atomic> |
|||
#include <map> |
|||
#include <QString> |
|||
#include <QQmlListProperty> |
|||
#include <QVariantMap> |
|||
#include <QFuture> |
|||
#include <QVariableDeclaration.h> |
|||
#include <libethereum/Account.h> |
|||
#include "MachineStates.h" |
|||
#include "QEther.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
|
|||
namespace eth { class FixedAccountHolder; } |
|||
|
|||
namespace mix |
|||
{ |
|||
|
|||
class Web3Server; |
|||
class RpcConnector; |
|||
class QEther; |
|||
class QDebugData; |
|||
class MixClient; |
|||
class QVariableDefinition; |
|||
class CodeModel; |
|||
struct SolidityType; |
|||
|
|||
/// Backend transaction config class
|
|||
struct TransactionSettings |
|||
{ |
|||
TransactionSettings() {} |
|||
TransactionSettings(QString const& _contractId, QString const& _functionId, u256 _value, u256 _gas, bool _gasAuto, u256 _gasPrice, Secret const& _sender, bool _isContractCreation, bool _isFunctionCall): |
|||
contractId(_contractId), functionId(_functionId), value(_value), gas(_gas), gasAuto(_gasAuto), gasPrice(_gasPrice), sender(_sender), isContractCreation(_isContractCreation), isFunctionCall(_isFunctionCall) {} |
|||
TransactionSettings(QString const& _stdContractName, QString const& _stdContractUrl): |
|||
contractId(_stdContractName), gasAuto(true), stdContractUrl(_stdContractUrl), isContractCreation(true), isFunctionCall(true) {} |
|||
|
|||
/// Contract name
|
|||
QString contractId; |
|||
/// Contract function name
|
|||
QString functionId; |
|||
/// Transaction value
|
|||
u256 value; |
|||
/// Gas
|
|||
u256 gas; |
|||
/// Calculate gas automatically
|
|||
bool gasAuto = true; |
|||
/// Gas price
|
|||
u256 gasPrice; |
|||
/// Mapping from contract function parameter name to value
|
|||
QVariantMap parameterValues; |
|||
/// Standard contract url
|
|||
QString stdContractUrl; |
|||
/// Sender
|
|||
Secret sender; |
|||
/// Tr deploys a contract
|
|||
bool isContractCreation; |
|||
/// Tr call a ctr function
|
|||
bool isFunctionCall; |
|||
}; |
|||
|
|||
|
|||
/// UI Transaction log record
|
|||
class RecordLogEntry: public QObject |
|||
{ |
|||
Q_OBJECT |
|||
Q_ENUMS(RecordType) |
|||
/// Recording index
|
|||
Q_PROPERTY(unsigned recordIndex MEMBER m_recordIndex CONSTANT) |
|||
/// Human readable transaction bloack and transaction index
|
|||
Q_PROPERTY(QString transactionIndex MEMBER m_transactionIndex CONSTANT) |
|||
/// Contract name if any
|
|||
Q_PROPERTY(QString contract MEMBER m_contract CONSTANT) |
|||
/// Function name if any
|
|||
Q_PROPERTY(QString function MEMBER m_function CONSTANT) |
|||
/// Transaction value
|
|||
Q_PROPERTY(QString value MEMBER m_value CONSTANT) |
|||
/// Receiving address
|
|||
Q_PROPERTY(QString address MEMBER m_address CONSTANT) |
|||
/// Returned value or transaction address in case of creation
|
|||
Q_PROPERTY(QString returned MEMBER m_returned CONSTANT) |
|||
/// true if call, false if transaction
|
|||
Q_PROPERTY(bool call MEMBER m_call CONSTANT) |
|||
/// @returns record type
|
|||
Q_PROPERTY(RecordType type MEMBER m_type CONSTANT) |
|||
/// Gas used
|
|||
Q_PROPERTY(QString gasUsed MEMBER m_gasUsed CONSTANT) |
|||
/// Sender
|
|||
Q_PROPERTY(QString sender MEMBER m_sender CONSTANT) |
|||
/// label
|
|||
Q_PROPERTY(QString label MEMBER m_label CONSTANT) |
|||
/// input parameters
|
|||
Q_PROPERTY(QVariantMap parameters MEMBER m_inputParameters CONSTANT) |
|||
/// return parameters
|
|||
Q_PROPERTY(QVariantMap returnParameters MEMBER m_returnParameters CONSTANT) |
|||
/// logs
|
|||
Q_PROPERTY(QVariantList logs MEMBER m_logs CONSTANT) |
|||
|
|||
public: |
|||
enum RecordType |
|||
{ |
|||
Transaction, |
|||
Block |
|||
}; |
|||
|
|||
RecordLogEntry(): |
|||
m_recordIndex(0), m_call(false), m_type(RecordType::Transaction) {} |
|||
RecordLogEntry(unsigned _recordIndex, QString _transactionIndex, QString _contract, QString _function, QString _value, QString _address, QString _returned, bool _call, RecordType _type, QString _gasUsed, |
|||
QString _sender, QString _label, QVariantMap _inputParameters, QVariantMap _returnParameters, QVariantList _logs): |
|||
m_recordIndex(_recordIndex), m_transactionIndex(_transactionIndex), m_contract(_contract), m_function(_function), m_value(_value), m_address(_address), m_returned(_returned), m_call(_call), m_type(_type), m_gasUsed(_gasUsed), |
|||
m_sender(_sender), m_label(_label), m_inputParameters(_inputParameters), m_returnParameters(_returnParameters), m_logs(_logs) {} |
|||
|
|||
private: |
|||
unsigned m_recordIndex; |
|||
QString m_transactionIndex; |
|||
QString m_contract; |
|||
QString m_function; |
|||
QString m_value; |
|||
QString m_address; |
|||
QString m_returned; |
|||
bool m_call; |
|||
RecordType m_type; |
|||
QString m_gasUsed; |
|||
QString m_sender; |
|||
QString m_label; |
|||
QVariantMap m_inputParameters; |
|||
QVariantMap m_returnParameters; |
|||
QVariantList m_logs; |
|||
}; |
|||
|
|||
/**
|
|||
* @brief Ethereum state control |
|||
*/ |
|||
class ClientModel: public QObject |
|||
{ |
|||
Q_OBJECT |
|||
|
|||
public: |
|||
ClientModel(); |
|||
~ClientModel(); |
|||
/// @returns true if currently executing contract code
|
|||
Q_PROPERTY(bool running MEMBER m_running NOTIFY runStateChanged) |
|||
/// @returns true if currently mining
|
|||
Q_PROPERTY(bool mining MEMBER m_mining NOTIFY miningStateChanged) |
|||
/// @returns deployed contracts addresses
|
|||
Q_PROPERTY(QVariantMap contractAddresses READ contractAddresses NOTIFY contractAddressesChanged) |
|||
/// @returns deployed contracts gas costs
|
|||
Q_PROPERTY(QVariantList gasCosts READ gasCosts NOTIFY gasCostsChanged) |
|||
/// @returns the last block
|
|||
Q_PROPERTY(RecordLogEntry* lastBlock READ lastBlock CONSTANT) |
|||
/// ethereum.js RPC request entry point
|
|||
/// @param _message RPC request in Json format
|
|||
/// @returns RPC response in Json format
|
|||
Q_INVOKABLE QString apiCall(QString const& _message); |
|||
/// Simulate mining. Creates a new block
|
|||
Q_INVOKABLE void mine(); |
|||
/// Get/set code model. Should be set from qml
|
|||
Q_PROPERTY(CodeModel* codeModel MEMBER m_codeModel) |
|||
/// Encode parameters
|
|||
Q_INVOKABLE QStringList encodeParams(QVariant const& _param, QString const& _contract, QString const& _function); |
|||
/// Encode parameter
|
|||
Q_INVOKABLE QString encodeStringParam(QString const& _param); |
|||
/// To Hex number
|
|||
Q_INVOKABLE QString toHex(QString const& _int); |
|||
/// Add new account to the model
|
|||
Q_INVOKABLE void addAccount(QString const& _secret); |
|||
/// Return the address associated with the current secret
|
|||
Q_INVOKABLE QString resolveAddress(QString const& _secret); |
|||
/// Compute required gas for a list of transactions @arg _tr
|
|||
QBigInt computeRequiredGas(QVariantList _tr); |
|||
/// init eth client
|
|||
Q_INVOKABLE void init(QString _dbpath); |
|||
|
|||
public slots: |
|||
/// Setup scenario, run transaction sequence, show debugger for the last transaction
|
|||
/// @param _state JS object with state configuration
|
|||
void setupScenario(QVariantMap _scenario); |
|||
/// Execute the given @param _tr on the current state
|
|||
void executeTr(QVariantMap _tr); |
|||
/// Show the debugger for a specified record
|
|||
Q_INVOKABLE void debugRecord(unsigned _index); |
|||
/// Show the debugger for an empty record
|
|||
Q_INVOKABLE void emptyRecord(); |
|||
/// Generate new secret
|
|||
Q_INVOKABLE QString newSecret(); |
|||
/// retrieve the address of @arg _secret
|
|||
Q_INVOKABLE QString address(QString const& _secret); |
|||
/// Encode a string to ABI parameter. Returns a hex string
|
|||
Q_INVOKABLE QString encodeAbiString(QString _string); |
|||
|
|||
private slots: |
|||
/// Update UI with machine states result. Display a modal dialog.
|
|||
void showDebugger(); |
|||
|
|||
signals: |
|||
/// Transaction execution started
|
|||
void runStarted(); |
|||
/// Transaction execution completed successfully
|
|||
void runComplete(); |
|||
/// Mining has started
|
|||
void miningStarted(); |
|||
/// Mined a new block
|
|||
void miningComplete(); |
|||
/// Mining stopped or started
|
|||
void miningStateChanged(); |
|||
/// Transaction execution completed with error
|
|||
/// @param _message Error message
|
|||
void runFailed(QString const& _message); |
|||
/// Contract address changed
|
|||
void contractAddressesChanged(); |
|||
/// Gas costs updated
|
|||
void gasCostsChanged(); |
|||
/// Execution state changed
|
|||
void newBlock(); |
|||
/// Execution state changed
|
|||
void runStateChanged(); |
|||
/// Show debugger window request
|
|||
void debugDataReady(QObject* _debugData); |
|||
/// ethereum.js RPC response ready
|
|||
/// @param _message RPC response in Json format
|
|||
void apiResponse(QString const& _message); |
|||
/// New transaction log entry
|
|||
void newRecord(RecordLogEntry* _r); |
|||
/// State (transaction log) cleared
|
|||
void stateCleared(); |
|||
/// new state has been processed
|
|||
void newState(unsigned _record, QVariantMap _accounts); |
|||
|
|||
private: |
|||
RecordLogEntry* lastBlock() const; |
|||
QVariantMap contractAddresses() const; |
|||
QVariantList gasCosts() const; |
|||
void executeSequence(std::vector<TransactionSettings> const& _sequence); |
|||
Address deployContract(bytes const& _code, TransactionSettings const& _tr = TransactionSettings()); |
|||
void callAddress(Address const& _contract, bytes const& _data, TransactionSettings const& _tr); |
|||
void onNewTransaction(); |
|||
void onStateReset(); |
|||
void showDebuggerForTransaction(ExecutionResult const& _t); |
|||
QVariant formatValue(SolidityType const& _type, dev::u256 const& _value); |
|||
QString resolveToken(std::pair<QString, int> const& _value); |
|||
std::pair<QString, int> retrieveToken(QString const& _value); |
|||
std::pair<QString, int> resolvePair(QString const& _contractId); |
|||
QVariant formatStorageValue(SolidityType const& _type, std::unordered_map<dev::u256, dev::u256> const& _storage, unsigned _offset, dev::u256 const& _slot); |
|||
void processNextTransactions(); |
|||
void finalizeBlock(); |
|||
void stopExecution(); |
|||
void setupExecutionChain(); |
|||
TransactionSettings transaction(QVariant const& _tr) const; |
|||
|
|||
std::atomic<bool> m_running; |
|||
std::atomic<bool> m_mining; |
|||
QFuture<void> m_runFuture; |
|||
std::unique_ptr<MixClient> m_client; |
|||
std::unique_ptr<RpcConnector> m_rpcConnector; |
|||
std::unique_ptr<Web3Server> m_web3Server; |
|||
std::shared_ptr<eth::FixedAccountHolder> m_ethAccounts; |
|||
std::unordered_map<Address, eth::Account> m_accounts; |
|||
std::vector<KeyPair> m_accountsSecret; |
|||
QList<u256> m_gasCosts; |
|||
std::map<std::pair<QString, int>, Address> m_contractAddresses; |
|||
std::map<Address, QString> m_contractNames; |
|||
std::map<QString, Address> m_stdContractAddresses; |
|||
std::map<Address, QString> m_stdContractNames; |
|||
CodeModel* m_codeModel = nullptr; |
|||
QList<QVariantList> m_queueTransactions; |
|||
mutable boost::shared_mutex x_queueTransactions; |
|||
QString m_dbpath; |
|||
}; |
|||
|
|||
} |
|||
} |
|||
|
|||
Q_DECLARE_METATYPE(dev::mix::RecordLogEntry*) |
@ -1,40 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file Clipboard.cpp
|
|||
* @author Yann yann@ethdev.com |
|||
* @date 2015 |
|||
*/ |
|||
|
|||
#include "Clipboard.h" |
|||
#include <QApplication> |
|||
#include <QClipboard> |
|||
|
|||
using namespace dev::mix; |
|||
|
|||
Clipboard::Clipboard() |
|||
{ |
|||
connect(QApplication::clipboard(), &QClipboard::dataChanged, [this] { emit clipboardChanged();}); |
|||
} |
|||
|
|||
QString Clipboard::text() const |
|||
{ |
|||
QClipboard *clipboard = QApplication::clipboard(); |
|||
return clipboard->text(); |
|||
} |
|||
|
|||
void Clipboard::setText(QString _text) |
|||
{ |
|||
QClipboard *clipboard = QApplication::clipboard(); |
|||
clipboard->setText(_text); |
|||
} |
@ -1,52 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file Clipboard.h
|
|||
* @author Yann yann@ethdev.com |
|||
* @date 2015 |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <QObject> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace mix |
|||
{ |
|||
|
|||
/**
|
|||
* @brief Provides access to system clipboard |
|||
*/ |
|||
|
|||
class Clipboard: public QObject |
|||
{ |
|||
Q_OBJECT |
|||
Q_PROPERTY(QString text READ text WRITE setText NOTIFY clipboardChanged) |
|||
|
|||
public: |
|||
Clipboard(); |
|||
/// Copy text to clipboard
|
|||
void setText(QString _text); |
|||
/// Get text from clipboard
|
|||
QString text() const; |
|||
|
|||
signals: |
|||
void clipboardChanged(); |
|||
}; |
|||
|
|||
} |
|||
} |
@ -1,171 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file CodeHighlighter.cpp
|
|||
* @author Arkadiy Paronyan arkadiy@ethdev.com |
|||
* @date 2015 |
|||
* Ethereum IDE client. |
|||
*/ |
|||
|
|||
#include <algorithm> |
|||
#include <QRegularExpression> |
|||
#include <QTextDocument> |
|||
#include <QTextBlock> |
|||
#include <QTextLayout> |
|||
#include <libsolidity/ASTVisitor.h> |
|||
#include <libsolidity/AST.h> |
|||
#include <libsolidity/Scanner.h> |
|||
#include <libsolidity/Exceptions.h> |
|||
#include "CodeHighlighter.h" |
|||
|
|||
using namespace dev::mix; |
|||
|
|||
CodeHighlighterSettings::CodeHighlighterSettings() |
|||
{ |
|||
backgroundColor = QColor(0x00, 0x2b, 0x36); |
|||
foregroundColor = QColor(0xee, 0xe8, 0xd5); |
|||
formats[Keyword].setForeground(QColor(0x93, 0xa1, 0xa1)); |
|||
formats[Comment].setForeground(QColor(0x85, 0x99, 0x00)); |
|||
formats[StringLiteral].setForeground(QColor(0xdc, 0x32, 0x2f)); |
|||
formats[NumLiteral].setForeground(foregroundColor); |
|||
formats[Import].setForeground(QColor(0x6c, 0x71, 0xc4)); |
|||
formats[CompilationError].setUnderlineColor(Qt::red); |
|||
formats[CompilationError].setUnderlineStyle(QTextCharFormat::SingleUnderline); |
|||
} |
|||
|
|||
namespace |
|||
{ |
|||
using namespace dev::solidity; |
|||
class HighlightVisitor: public ASTConstVisitor |
|||
{ |
|||
public: |
|||
HighlightVisitor(CodeHighlighter::Formats* _formats) { m_formats = _formats; } |
|||
private: |
|||
CodeHighlighter::Formats* m_formats; |
|||
|
|||
virtual bool visit(ImportDirective const& _node) |
|||
{ |
|||
m_formats->push_back(CodeHighlighter::FormatRange(CodeHighlighterSettings::Import, _node.getLocation())); |
|||
return true; |
|||
} |
|||
}; |
|||
} |
|||
|
|||
CodeHighlighter::FormatRange::FormatRange(CodeHighlighterSettings::Token _t, dev::SourceLocation const& _location): |
|||
token(_t), start(_location.start), length(_location.end - _location.start) |
|||
{} |
|||
|
|||
void CodeHighlighter::processSource(std::string const& _source) |
|||
{ |
|||
processComments(_source); |
|||
solidity::CharStream stream(_source); |
|||
solidity::Scanner scanner(stream); |
|||
solidity::Token::Value token = scanner.getCurrentToken(); |
|||
while (token != Token::EOS) |
|||
{ |
|||
if ((token >= Token::Break && token < Token::TypesEnd) || |
|||
token == Token::In || token == Token::Delete || token == Token::NullLiteral || token == Token::TrueLiteral || token == Token::FalseLiteral) |
|||
m_formats.push_back(FormatRange(CodeHighlighterSettings::Keyword, scanner.getCurrentLocation())); |
|||
else if (token == Token::StringLiteral) |
|||
m_formats.push_back(FormatRange(CodeHighlighterSettings::StringLiteral, scanner.getCurrentLocation())); |
|||
else if (token == Token::CommentLiteral) |
|||
m_formats.push_back(FormatRange(CodeHighlighterSettings::Comment, scanner.getCurrentLocation())); |
|||
else if (token == Token::Number) |
|||
m_formats.push_back(FormatRange(CodeHighlighterSettings::NumLiteral, scanner.getCurrentLocation())); |
|||
|
|||
token = scanner.next(); |
|||
} |
|||
std::sort(m_formats.begin(), m_formats.end()); |
|||
} |
|||
|
|||
void CodeHighlighter::processAST(dev::solidity::ASTNode const& _ast) |
|||
{ |
|||
HighlightVisitor visitor(&m_formats); |
|||
_ast.accept(visitor); |
|||
|
|||
std::sort(m_formats.begin(), m_formats.end()); |
|||
} |
|||
|
|||
void CodeHighlighter::processError(dev::Exception const& _exception) |
|||
{ |
|||
SourceLocation const* location = boost::get_error_info<errinfo_sourceLocation>(_exception); |
|||
if (location) |
|||
m_formats.push_back(FormatRange(CodeHighlighterSettings::CompilationError, *location)); |
|||
} |
|||
|
|||
void CodeHighlighter::processComments(std::string const& _source) |
|||
{ |
|||
unsigned i = 0; |
|||
size_t size = _source.size(); |
|||
if (size == 0) |
|||
return; |
|||
while (i < size - 1) |
|||
{ |
|||
if (_source[i] == '/' && _source[i + 1] == '/') |
|||
{ |
|||
//add single line comment
|
|||
int start = i; |
|||
i += 2; |
|||
while (i < size && _source[i] != '\n') |
|||
++i; |
|||
m_formats.push_back(FormatRange(CodeHighlighterSettings::Comment, start, i - start)); |
|||
} |
|||
else if (_source[i] == '/' && _source[i + 1] == '*') |
|||
{ |
|||
//add multiline comment
|
|||
int start = i; |
|||
i += 2; |
|||
while ((_source[i] != '/' || _source[i - 1] != '*') && i < size) |
|||
++i; |
|||
m_formats.push_back(FormatRange(CodeHighlighterSettings::Comment, start, i - start + 1)); |
|||
} |
|||
++i; |
|||
} |
|||
} |
|||
|
|||
void CodeHighlighter::updateFormatting(QTextDocument* _document, CodeHighlighterSettings const& _settings) |
|||
{ |
|||
QTextBlock block = _document->firstBlock(); |
|||
QList<QTextLayout::FormatRange> ranges; |
|||
|
|||
Formats::const_iterator format = m_formats.begin(); |
|||
while (true) |
|||
{ |
|||
while ((format == m_formats.end() || (block.position() + block.length() <= format->start)) && block.isValid()) |
|||
{ |
|||
auto layout = block.layout(); |
|||
layout->clearAdditionalFormats(); |
|||
layout->setAdditionalFormats(ranges); |
|||
_document->markContentsDirty(block.position(), block.length()); |
|||
block = block.next(); |
|||
ranges.clear(); |
|||
} |
|||
if (!block.isValid()) |
|||
break; |
|||
|
|||
int intersectionStart = std::max(format->start, block.position()); |
|||
int intersectionLength = std::min(format->start + format->length, block.position() + block.length()) - intersectionStart; |
|||
if (intersectionLength > 0) |
|||
{ |
|||
QTextLayout::FormatRange range; |
|||
range.format = _settings.formats[format->token]; |
|||
range.start = format->start - block.position(); |
|||
range.length = format->length; |
|||
ranges.append(range); |
|||
} |
|||
++format; |
|||
} |
|||
} |
@ -1,108 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file CodeHighlighter.h
|
|||
* @author Arkadiy Paronyan arkadiy@ethdev.com |
|||
* @date 2015 |
|||
* Ethereum IDE client. |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <vector> |
|||
#include <QString> |
|||
#include <QTextCharFormat> |
|||
|
|||
class QTextDocument; |
|||
|
|||
namespace dev |
|||
{ |
|||
|
|||
struct Exception; |
|||
struct SourceLocation; |
|||
namespace solidity |
|||
{ |
|||
class ASTNode; |
|||
} |
|||
|
|||
namespace mix |
|||
{ |
|||
|
|||
/// Code highligting settings
|
|||
class CodeHighlighterSettings |
|||
{ |
|||
public: |
|||
enum Token |
|||
{ |
|||
Import, |
|||
Keyword, |
|||
Comment, |
|||
StringLiteral, |
|||
NumLiteral, |
|||
CompilationError, |
|||
Size, //this must be kept last
|
|||
}; |
|||
|
|||
CodeHighlighterSettings(); |
|||
///Format for each token
|
|||
QTextCharFormat formats[Size]; |
|||
///Background color
|
|||
QColor backgroundColor; |
|||
///Foreground color
|
|||
QColor foregroundColor; |
|||
}; |
|||
|
|||
/// Code highlighting engine class
|
|||
class CodeHighlighter |
|||
{ |
|||
public: |
|||
/// Formatting range
|
|||
struct FormatRange |
|||
{ |
|||
FormatRange(CodeHighlighterSettings::Token _t, int _start, int _length): token(_t), start(_start), length(_length) {} |
|||
FormatRange(CodeHighlighterSettings::Token _t, SourceLocation const& _location); |
|||
bool operator<(FormatRange const& _other) const { return start < _other.start || (start == _other.start && length < _other.length); } |
|||
|
|||
CodeHighlighterSettings::Token token; |
|||
int start; |
|||
int length; |
|||
}; |
|||
using Formats = std::vector<FormatRange>; // Sorted by start position
|
|||
|
|||
public: |
|||
/// Collect highligting information by lexing the source
|
|||
void processSource(std::string const& _source); |
|||
/// Collect additional highligting information from AST
|
|||
void processAST(solidity::ASTNode const& _ast); |
|||
/// Collect highlighting information from compilation exception
|
|||
void processError(dev::Exception const& _exception); |
|||
|
|||
/// Apply formatting for a text document
|
|||
/// @todo Remove this once editor is reworked
|
|||
void updateFormatting(QTextDocument* _document, CodeHighlighterSettings const& _settings); |
|||
|
|||
private: |
|||
/// Collect highligting information by paring for comments
|
|||
/// @todo Support this in solidity?
|
|||
void processComments(std::string const& _source); |
|||
|
|||
private: |
|||
Formats m_formats; |
|||
}; |
|||
|
|||
} |
|||
|
|||
} |
@ -1,698 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file CodeModel.cpp
|
|||
* @author Arkadiy Paronyan arkadiy@ethdev.com |
|||
* @date 2014 |
|||
* Ethereum IDE client. |
|||
*/ |
|||
|
|||
#include <sstream> |
|||
#include <memory> |
|||
#include <QDebug> |
|||
#include <QApplication> |
|||
#include <QtQml> |
|||
#include <libdevcore/Common.h> |
|||
#include <libevmasm/SourceLocation.h> |
|||
#include <libsolidity/AST.h> |
|||
#include <libsolidity/Types.h> |
|||
#include <libsolidity/ASTVisitor.h> |
|||
#include <libsolidity/CompilerStack.h> |
|||
#include <libsolidity/SourceReferenceFormatter.h> |
|||
#include <libsolidity/InterfaceHandler.h> |
|||
#include <libsolidity/GasEstimator.h> |
|||
#include <libsolidity/SourceReferenceFormatter.h> |
|||
#include <libevmcore/Instruction.h> |
|||
#include <libethcore/CommonJS.h> |
|||
#include "QContractDefinition.h" |
|||
#include "QFunctionDefinition.h" |
|||
#include "QVariableDeclaration.h" |
|||
#include "CodeHighlighter.h" |
|||
#include "FileIo.h" |
|||
#include "CodeModel.h" |
|||
|
|||
using namespace dev::mix; |
|||
|
|||
const std::set<std::string> c_predefinedContracts = |
|||
{ "Config", "Coin", "CoinReg", "coin", "service", "owned", "mortal", "NameReg", "named", "std", "configUser" }; |
|||
|
|||
|
|||
namespace |
|||
{ |
|||
using namespace dev::eth; |
|||
using namespace dev::solidity; |
|||
|
|||
class CollectLocalsVisitor: public ASTConstVisitor |
|||
{ |
|||
public: |
|||
CollectLocalsVisitor(QHash<LocationPair, SolidityDeclaration>* _locals): |
|||
m_locals(_locals), m_functionScope(false) {} |
|||
|
|||
private: |
|||
LocationPair nodeLocation(ASTNode const& _node) |
|||
{ |
|||
return LocationPair(_node.getLocation().start, _node.getLocation().end); |
|||
} |
|||
|
|||
virtual bool visit(FunctionDefinition const&) override |
|||
{ |
|||
m_functionScope = true; |
|||
return true; |
|||
} |
|||
|
|||
virtual void endVisit(FunctionDefinition const&) override |
|||
{ |
|||
m_functionScope = false; |
|||
} |
|||
|
|||
virtual bool visit(VariableDeclaration const& _node) override |
|||
{ |
|||
SolidityDeclaration decl; |
|||
decl.type = CodeModel::nodeType(_node.getType().get()); |
|||
decl.name = QString::fromStdString(_node.getName()); |
|||
decl.slot = 0; |
|||
decl.offset = 0; |
|||
if (m_functionScope) |
|||
m_locals->insert(nodeLocation(_node), decl); |
|||
return true; |
|||
} |
|||
|
|||
private: |
|||
QHash<LocationPair, SolidityDeclaration>* m_locals; |
|||
bool m_functionScope; |
|||
}; |
|||
|
|||
class CollectLocationsVisitor: public ASTConstVisitor |
|||
{ |
|||
public: |
|||
CollectLocationsVisitor(SourceMap* _sourceMap): |
|||
m_sourceMap(_sourceMap) {} |
|||
|
|||
private: |
|||
LocationPair nodeLocation(ASTNode const& _node) |
|||
{ |
|||
return LocationPair(_node.getLocation().start, _node.getLocation().end); |
|||
} |
|||
|
|||
virtual bool visit(FunctionDefinition const& _node) override |
|||
{ |
|||
m_sourceMap->functions.insert(nodeLocation(_node), QString::fromStdString(_node.getName())); |
|||
return true; |
|||
} |
|||
|
|||
virtual bool visit(ContractDefinition const& _node) override |
|||
{ |
|||
m_sourceMap->contracts.insert(nodeLocation(_node), QString::fromStdString(_node.getName())); |
|||
return true; |
|||
} |
|||
|
|||
private: |
|||
SourceMap* m_sourceMap; |
|||
}; |
|||
|
|||
QHash<unsigned, SolidityDeclarations> collectStorage(dev::solidity::ContractDefinition const& _contract) |
|||
{ |
|||
QHash<unsigned, SolidityDeclarations> result; |
|||
dev::solidity::ContractType contractType(_contract); |
|||
|
|||
for (auto v : contractType.getStateVariables()) |
|||
{ |
|||
dev::solidity::VariableDeclaration const* declaration = std::get<0>(v); |
|||
dev::u256 slot = std::get<1>(v); |
|||
unsigned offset = std::get<2>(v); |
|||
result[static_cast<unsigned>(slot)].push_back(SolidityDeclaration { QString::fromStdString(declaration->getName()), CodeModel::nodeType(declaration->getType().get()), slot, offset }); |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
} //namespace
|
|||
|
|||
void BackgroundWorker::queueCodeChange(int _jobId) |
|||
{ |
|||
m_model->runCompilationJob(_jobId); |
|||
} |
|||
|
|||
CompiledContract::CompiledContract(const dev::solidity::CompilerStack& _compiler, QString const& _contractName, QString const& _source): |
|||
QObject(nullptr), |
|||
m_sourceHash(qHash(_source)) |
|||
{ |
|||
std::string name = _contractName.toStdString(); |
|||
ContractDefinition const& contractDefinition = _compiler.getContractDefinition(name); |
|||
m_contract.reset(new QContractDefinition(nullptr, &contractDefinition)); |
|||
QQmlEngine::setObjectOwnership(m_contract.get(), QQmlEngine::CppOwnership); |
|||
m_contract->moveToThread(QApplication::instance()->thread()); |
|||
m_bytes = _compiler.getBytecode(_contractName.toStdString()); |
|||
|
|||
dev::solidity::InterfaceHandler interfaceHandler; |
|||
m_contractInterface = QString::fromStdString(interfaceHandler.getABIInterface(contractDefinition)); |
|||
if (m_contractInterface.isEmpty()) |
|||
m_contractInterface = "[]"; |
|||
if (contractDefinition.getLocation().sourceName.get()) |
|||
m_documentId = QString::fromStdString(*contractDefinition.getLocation().sourceName); |
|||
|
|||
CollectLocalsVisitor visitor(&m_locals); |
|||
m_storage = collectStorage(contractDefinition); |
|||
contractDefinition.accept(visitor); |
|||
m_assemblyItems = *_compiler.getRuntimeAssemblyItems(name); |
|||
m_constructorAssemblyItems = *_compiler.getAssemblyItems(name); |
|||
} |
|||
|
|||
QString CompiledContract::codeHex() const |
|||
{ |
|||
return QString::fromStdString(toJS(m_bytes)); |
|||
} |
|||
|
|||
CodeModel::CodeModel(): |
|||
m_compiling(false), |
|||
m_codeHighlighterSettings(new CodeHighlighterSettings()), |
|||
m_backgroundWorker(this), |
|||
m_backgroundJobId(0) |
|||
{ |
|||
m_backgroundThread.start(); |
|||
m_backgroundWorker.moveToThread(&m_backgroundThread); |
|||
connect(this, &CodeModel::scheduleCompilationJob, &m_backgroundWorker, &BackgroundWorker::queueCodeChange, Qt::QueuedConnection); |
|||
qRegisterMetaType<CompiledContract*>("CompiledContract*"); |
|||
qRegisterMetaType<QContractDefinition*>("QContractDefinition*"); |
|||
qRegisterMetaType<QFunctionDefinition*>("QFunctionDefinition*"); |
|||
qRegisterMetaType<QVariableDeclaration*>("QVariableDeclaration*"); |
|||
qmlRegisterType<QFunctionDefinition>("org.ethereum.qml", 1, 0, "QFunctionDefinition"); |
|||
qmlRegisterType<QVariableDeclaration>("org.ethereum.qml", 1, 0, "QVariableDeclaration"); |
|||
} |
|||
|
|||
CodeModel::~CodeModel() |
|||
{ |
|||
stop(); |
|||
disconnect(this); |
|||
releaseContracts(); |
|||
if (m_gasCostsMaps) |
|||
delete m_gasCostsMaps; |
|||
} |
|||
|
|||
void CodeModel::stop() |
|||
{ |
|||
///@todo: cancel bg job
|
|||
m_backgroundThread.exit(); |
|||
m_backgroundThread.wait(); |
|||
} |
|||
|
|||
void CodeModel::reset(QVariantMap const& _documents) |
|||
{ |
|||
///@todo: cancel bg job
|
|||
Guard l(x_contractMap); |
|||
releaseContracts(); |
|||
Guard pl(x_pendingContracts); |
|||
m_pendingContracts.clear(); |
|||
|
|||
for (QVariantMap::const_iterator d = _documents.cbegin(); d != _documents.cend(); ++d) |
|||
m_pendingContracts[d.key()] = d.value().toString(); |
|||
// launch the background thread
|
|||
m_compiling = true; |
|||
emit stateChanged(); |
|||
emit scheduleCompilationJob(++m_backgroundJobId); |
|||
} |
|||
|
|||
void CodeModel::unregisterContractSrc(QString const& _documentId) |
|||
{ |
|||
{ |
|||
Guard pl(x_pendingContracts); |
|||
m_pendingContracts.erase(_documentId); |
|||
} |
|||
|
|||
// launch the background thread
|
|||
m_compiling = true; |
|||
emit stateChanged(); |
|||
emit scheduleCompilationJob(++m_backgroundJobId); |
|||
} |
|||
|
|||
void CodeModel::registerCodeChange(QString const& _documentId, QString const& _code) |
|||
{ |
|||
{ |
|||
Guard pl(x_pendingContracts); |
|||
m_pendingContracts[_documentId] = _code; |
|||
} |
|||
|
|||
// launch the background thread
|
|||
m_compiling = true; |
|||
emit stateChanged(); |
|||
emit scheduleCompilationJob(++m_backgroundJobId); |
|||
} |
|||
|
|||
QVariantMap CodeModel::contracts() const |
|||
{ |
|||
QVariantMap result; |
|||
Guard l(x_contractMap); |
|||
for (ContractMap::const_iterator c = m_contractMap.cbegin(); c != m_contractMap.cend(); ++c) |
|||
result.insert(c.key(), QVariant::fromValue(c.value())); |
|||
return result; |
|||
} |
|||
|
|||
CompiledContract* CodeModel::contractByDocumentId(QString const& _documentId) const |
|||
{ |
|||
Guard l(x_contractMap); |
|||
for (ContractMap::const_iterator c = m_contractMap.cbegin(); c != m_contractMap.cend(); ++c) |
|||
if (c.value()->m_documentId == _documentId) |
|||
return c.value(); |
|||
return nullptr; |
|||
} |
|||
|
|||
CompiledContract const& CodeModel::contract(QString const& _name) const |
|||
{ |
|||
Guard l(x_contractMap); |
|||
CompiledContract* res = m_contractMap.value(_name); |
|||
if (res == nullptr) |
|||
BOOST_THROW_EXCEPTION(dev::Exception() << dev::errinfo_comment("Contract not found: " + _name.toStdString())); |
|||
return *res; |
|||
} |
|||
|
|||
CompiledContract const* CodeModel::tryGetContract(QString const& _name) const |
|||
{ |
|||
Guard l(x_contractMap); |
|||
CompiledContract* res = m_contractMap.value(_name); |
|||
return res; |
|||
} |
|||
|
|||
void CodeModel::releaseContracts() |
|||
{ |
|||
for (ContractMap::iterator c = m_contractMap.begin(); c != m_contractMap.end(); ++c) |
|||
c.value()->deleteLater(); |
|||
m_contractMap.clear(); |
|||
m_sourceMaps.clear(); |
|||
} |
|||
|
|||
void CodeModel::runCompilationJob(int _jobId) |
|||
{ |
|||
if (_jobId != m_backgroundJobId) |
|||
return; //obsolete job
|
|||
solidity::CompilerStack cs(true); |
|||
try |
|||
{ |
|||
cs.addSource("configUser", R"(contract configUser{function configAddr()constant returns(address a){ return 0xf025d81196b72fba60a1d4dddad12eeb8360d828;}})"); |
|||
std::vector<std::string> sourceNames; |
|||
{ |
|||
Guard l(x_pendingContracts); |
|||
for (auto const& c: m_pendingContracts) |
|||
{ |
|||
cs.addSource(c.first.toStdString(), c.second.toStdString()); |
|||
sourceNames.push_back(c.first.toStdString()); |
|||
} |
|||
} |
|||
cs.compile(m_optimizeCode); |
|||
gasEstimation(cs); |
|||
collectContracts(cs, sourceNames); |
|||
} |
|||
catch (dev::Exception const& _exception) |
|||
{ |
|||
std::stringstream error; |
|||
solidity::SourceReferenceFormatter::printExceptionInformation(error, _exception, "Error", cs); |
|||
QString message = QString::fromStdString(error.str()); |
|||
QVariantMap firstLocation; |
|||
QVariantList secondLocations; |
|||
if (SourceLocation const* first = boost::get_error_info<solidity::errinfo_sourceLocation>(_exception)) |
|||
firstLocation = resolveCompilationErrorLocation(cs, *first); |
|||
if (SecondarySourceLocation const* second = boost::get_error_info<solidity::errinfo_secondarySourceLocation>(_exception)) |
|||
{ |
|||
for (auto const& c: second->infos) |
|||
secondLocations.push_back(resolveCompilationErrorLocation(cs, c.second)); |
|||
} |
|||
compilationError(message, firstLocation, secondLocations); |
|||
} |
|||
m_compiling = false; |
|||
emit stateChanged(); |
|||
} |
|||
|
|||
QVariantMap CodeModel::resolveCompilationErrorLocation(CompilerStack const& _compiler, SourceLocation const& _location) |
|||
{ |
|||
std::tuple<int, int, int, int> pos = _compiler.positionFromSourceLocation(_location); |
|||
QVariantMap startError; |
|||
startError.insert("line", std::get<0>(pos) > 1 ? (std::get<0>(pos) - 1) : 1); |
|||
startError.insert("column", std::get<1>(pos) > 1 ? (std::get<1>(pos) - 1) : 1); |
|||
QVariantMap endError; |
|||
endError.insert("line", std::get<2>(pos) > 1 ? (std::get<2>(pos) - 1) : 1); |
|||
endError.insert("column", std::get<3>(pos) > 1 ? (std::get<3>(pos) - 1) : 1); |
|||
QVariantMap error; |
|||
error.insert("start", startError); |
|||
error.insert("end", endError); |
|||
QString sourceName; |
|||
if (_location.sourceName) |
|||
sourceName = QString::fromStdString(*_location.sourceName); |
|||
error.insert("source", sourceName); |
|||
if (!sourceName.isEmpty()) |
|||
if (CompiledContract* contract = contractByDocumentId(sourceName)) |
|||
sourceName = contract->contract()->name(); //substitute the location to match our contract names
|
|||
error.insert("contractName", sourceName); |
|||
return error; |
|||
} |
|||
|
|||
void CodeModel::gasEstimation(solidity::CompilerStack const& _cs) |
|||
{ |
|||
if (m_gasCostsMaps) |
|||
m_gasCostsMaps->deleteLater(); |
|||
m_gasCostsMaps = new GasMapWrapper; |
|||
for (std::string n: _cs.getContractNames()) |
|||
{ |
|||
ContractDefinition const& contractDefinition = _cs.getContractDefinition(n); |
|||
QString sourceName = QString::fromStdString(*contractDefinition.getLocation().sourceName); |
|||
|
|||
if (!m_gasCostsMaps->contains(sourceName)) |
|||
m_gasCostsMaps->insert(sourceName, QVariantList()); |
|||
|
|||
if (!contractDefinition.isFullyImplemented()) |
|||
continue; |
|||
dev::solidity::SourceUnit const& sourceUnit = _cs.getAST(*contractDefinition.getLocation().sourceName); |
|||
AssemblyItems const* items = _cs.getRuntimeAssemblyItems(n); |
|||
std::map<ASTNode const*, GasMeter::GasConsumption> gasCosts = GasEstimator::breakToStatementLevel(GasEstimator::structuralEstimation(*items, std::vector<ASTNode const*>({&sourceUnit})), {&sourceUnit}); |
|||
|
|||
auto gasToString = [](GasMeter::GasConsumption const& _gas) |
|||
{ |
|||
if (_gas.isInfinite) |
|||
return QString("0"); |
|||
else |
|||
return QString::fromStdString(toString(_gas.value)); |
|||
}; |
|||
|
|||
// Structural gas costs (per opcode)
|
|||
for (auto gasItem = gasCosts.begin(); gasItem != gasCosts.end(); ++gasItem) |
|||
{ |
|||
SourceLocation const& location = gasItem->first->getLocation(); |
|||
GasMeter::GasConsumption cost = gasItem->second; |
|||
m_gasCostsMaps->push(sourceName, location.start, location.end, gasToString(cost), cost.isInfinite, GasMap::type::Statement); |
|||
} |
|||
|
|||
eth::AssemblyItems const& runtimeAssembly = *_cs.getRuntimeAssemblyItems(n); |
|||
QString contractName = QString::fromStdString(contractDefinition.getName()); |
|||
// Functional gas costs (per function, but also for accessors)
|
|||
for (auto it: contractDefinition.getInterfaceFunctions()) |
|||
{ |
|||
if (!it.second->hasDeclaration()) |
|||
continue; |
|||
SourceLocation loc = it.second->getDeclaration().getLocation(); |
|||
GasMeter::GasConsumption cost = GasEstimator::functionalEstimation(runtimeAssembly, it.second->externalSignature()); |
|||
m_gasCostsMaps->push(sourceName, loc.start, loc.end, gasToString(cost), cost.isInfinite, GasMap::type::Function, |
|||
contractName, QString::fromStdString(it.second->getDeclaration().getName())); |
|||
} |
|||
if (auto const* fallback = contractDefinition.getFallbackFunction()) |
|||
{ |
|||
SourceLocation loc = fallback->getLocation(); |
|||
GasMeter::GasConsumption cost = GasEstimator::functionalEstimation(runtimeAssembly, "INVALID"); |
|||
m_gasCostsMaps->push(sourceName, loc.start, loc.end, gasToString(cost), cost.isInfinite, GasMap::type::Function, |
|||
contractName, "fallback"); |
|||
} |
|||
for (auto const& it: contractDefinition.getDefinedFunctions()) |
|||
{ |
|||
if (it->isPartOfExternalInterface() || it->isConstructor()) |
|||
continue; |
|||
SourceLocation loc = it->getLocation(); |
|||
size_t entry = _cs.getFunctionEntryPoint(n, *it); |
|||
GasEstimator::GasConsumption cost = GasEstimator::GasConsumption::infinite(); |
|||
if (entry > 0) |
|||
cost = GasEstimator::functionalEstimation(runtimeAssembly, entry, *it); |
|||
m_gasCostsMaps->push(sourceName, loc.start, loc.end, gasToString(cost), cost.isInfinite, GasMap::type::Function, |
|||
contractName, QString::fromStdString(it->getName())); |
|||
} |
|||
if (auto const* constructor = contractDefinition.getConstructor()) |
|||
{ |
|||
SourceLocation loc = constructor->getLocation(); |
|||
GasMeter::GasConsumption cost = GasEstimator::functionalEstimation(*_cs.getAssemblyItems(n)); |
|||
m_gasCostsMaps->push(sourceName, loc.start, loc.end, gasToString(cost), cost.isInfinite, GasMap::type::Constructor, |
|||
contractName, contractName); |
|||
} |
|||
} |
|||
} |
|||
|
|||
QVariantList CodeModel::gasCostByDocumentId(QString const& _documentId) const |
|||
{ |
|||
if (m_gasCostsMaps) |
|||
return m_gasCostsMaps->gasCostsByDocId(_documentId); |
|||
else |
|||
return QVariantList(); |
|||
} |
|||
|
|||
QVariantList CodeModel::gasCostBy(QString const& _contractName, QString const& _functionName) const |
|||
{ |
|||
if (m_gasCostsMaps) |
|||
return m_gasCostsMaps->gasCostsBy(_contractName, _functionName); |
|||
else |
|||
return QVariantList(); |
|||
} |
|||
|
|||
void CodeModel::collectContracts(dev::solidity::CompilerStack const& _cs, std::vector<std::string> const& _sourceNames) |
|||
{ |
|||
Guard pl(x_pendingContracts); |
|||
Guard l(x_contractMap); |
|||
ContractMap result; |
|||
SourceMaps sourceMaps; |
|||
for (std::string const& sourceName: _sourceNames) |
|||
{ |
|||
dev::solidity::SourceUnit const& source = _cs.getAST(sourceName); |
|||
SourceMap sourceMap; |
|||
CollectLocationsVisitor collector(&sourceMap); |
|||
source.accept(collector); |
|||
sourceMaps.insert(QString::fromStdString(sourceName), std::move(sourceMap)); |
|||
} |
|||
for (std::string n: _cs.getContractNames()) |
|||
{ |
|||
if (c_predefinedContracts.count(n) != 0) |
|||
continue; |
|||
QString name = QString::fromStdString(n); |
|||
ContractDefinition const& contractDefinition = _cs.getContractDefinition(n); |
|||
if (!contractDefinition.isFullyImplemented()) |
|||
continue; |
|||
QString sourceName = QString::fromStdString(*contractDefinition.getLocation().sourceName); |
|||
auto sourceIter = m_pendingContracts.find(sourceName); |
|||
QString source = sourceIter != m_pendingContracts.end() ? sourceIter->second : QString(); |
|||
CompiledContract* contract = new CompiledContract(_cs, name, source); |
|||
QQmlEngine::setObjectOwnership(contract, QQmlEngine::CppOwnership); |
|||
result[name] = contract; |
|||
CompiledContract* prevContract = nullptr; |
|||
// find previous contract by name
|
|||
for (ContractMap::const_iterator c = m_contractMap.cbegin(); c != m_contractMap.cend(); ++c) |
|||
if (c.value()->contract()->name() == contract->contract()->name()) |
|||
prevContract = c.value(); |
|||
|
|||
// if not found, try by documentId
|
|||
if (!prevContract) |
|||
{ |
|||
for (ContractMap::const_iterator c = m_contractMap.cbegin(); c != m_contractMap.cend(); ++c) |
|||
if (c.value()->documentId() == contract->documentId()) |
|||
{ |
|||
//make sure there are no other contracts in the same source, otherwise it is not a rename
|
|||
if (!std::any_of(result.begin(),result.end(), [=](ContractMap::const_iterator::value_type _v) { return _v != contract && _v->documentId() == contract->documentId(); })) |
|||
prevContract = c.value(); |
|||
} |
|||
} |
|||
if (prevContract != nullptr && prevContract->contractInterface() != result[name]->contractInterface()) |
|||
emit contractInterfaceChanged(name); |
|||
if (prevContract == nullptr) |
|||
emit newContractCompiled(name); |
|||
else if (prevContract->contract()->name() != name) |
|||
emit contractRenamed(contract->documentId(), prevContract->contract()->name(), name); |
|||
} |
|||
releaseContracts(); |
|||
m_contractMap.swap(result); |
|||
m_sourceMaps.swap(sourceMaps); |
|||
emit codeChanged(); |
|||
emit compilationComplete(); |
|||
} |
|||
|
|||
bool CodeModel::hasContract() const |
|||
{ |
|||
Guard l(x_contractMap); |
|||
return m_contractMap.size() != 0; |
|||
} |
|||
|
|||
dev::bytes const& CodeModel::getStdContractCode(const QString& _contractName, const QString& _url) |
|||
{ |
|||
auto cached = m_compiledContracts.find(_contractName); |
|||
if (cached != m_compiledContracts.end()) |
|||
return cached->second; |
|||
|
|||
FileIo fileIo; |
|||
std::string source = fileIo.readFile(_url).toStdString(); |
|||
solidity::CompilerStack cs(false); |
|||
cs.setSource(source); |
|||
cs.compile(false); |
|||
for (std::string const& name: cs.getContractNames()) |
|||
{ |
|||
dev::bytes code = cs.getBytecode(name); |
|||
m_compiledContracts.insert(std::make_pair(QString::fromStdString(name), std::move(code))); |
|||
} |
|||
return m_compiledContracts.at(_contractName); |
|||
} |
|||
|
|||
void CodeModel::retrieveSubType(SolidityType& _wrapperType, dev::solidity::Type const* _type) |
|||
{ |
|||
if (_type->getCategory() == Type::Category::Array) |
|||
{ |
|||
ArrayType const* arrayType = dynamic_cast<ArrayType const*>(_type); |
|||
_wrapperType.baseType = std::make_shared<dev::mix::SolidityType const>(nodeType(arrayType->getBaseType().get())); |
|||
} |
|||
} |
|||
|
|||
SolidityType CodeModel::nodeType(dev::solidity::Type const* _type) |
|||
{ |
|||
SolidityType r { SolidityType::Type::UnsignedInteger, 32, 1, false, false, QString::fromStdString(_type->toString(true)), std::vector<SolidityDeclaration>(), std::vector<QString>(), nullptr }; |
|||
if (!_type) |
|||
return r; |
|||
switch (_type->getCategory()) |
|||
{ |
|||
case Type::Category::Integer: |
|||
{ |
|||
IntegerType const* it = dynamic_cast<IntegerType const*>(_type); |
|||
r.size = it->getNumBits() / 8; |
|||
r.type = it->isAddress() ? SolidityType::Type::Address : it->isSigned() ? SolidityType::Type::SignedInteger : SolidityType::Type::UnsignedInteger; |
|||
} |
|||
break; |
|||
case Type::Category::Bool: |
|||
r.type = SolidityType::Type::Bool; |
|||
break; |
|||
case Type::Category::FixedBytes: |
|||
{ |
|||
FixedBytesType const* b = dynamic_cast<FixedBytesType const*>(_type); |
|||
r.type = SolidityType::Type::Bytes; |
|||
r.size = static_cast<unsigned>(b->numBytes()); |
|||
} |
|||
break; |
|||
case Type::Category::Contract: |
|||
r.type = SolidityType::Type::Address; |
|||
break; |
|||
case Type::Category::Array: |
|||
{ |
|||
ArrayType const* array = dynamic_cast<ArrayType const*>(_type); |
|||
if (array->isString()) |
|||
r.type = SolidityType::Type::String; |
|||
else if (array->isByteArray()) |
|||
r.type = SolidityType::Type::Bytes; |
|||
else |
|||
{ |
|||
SolidityType elementType = nodeType(array->getBaseType().get()); |
|||
elementType.name = r.name; |
|||
r = elementType; |
|||
} |
|||
r.count = static_cast<unsigned>(array->getLength()); |
|||
r.dynamicSize = _type->isDynamicallySized(); |
|||
r.array = true; |
|||
retrieveSubType(r, _type); |
|||
} |
|||
break; |
|||
case Type::Category::Enum: |
|||
{ |
|||
r.type = SolidityType::Type::Enum; |
|||
EnumType const* e = dynamic_cast<EnumType const*>(_type); |
|||
for(auto const& enumValue: e->getEnumDefinition().getMembers()) |
|||
r.enumNames.push_back(QString::fromStdString(enumValue->getName())); |
|||
} |
|||
break; |
|||
case Type::Category::Struct: |
|||
{ |
|||
r.type = SolidityType::Type::Struct; |
|||
StructType const* s = dynamic_cast<StructType const*>(_type); |
|||
for(auto const& structMember: s->getMembers()) |
|||
{ |
|||
auto slotAndOffset = s->getStorageOffsetsOfMember(structMember.name); |
|||
r.members.push_back(SolidityDeclaration { QString::fromStdString(structMember.name), nodeType(structMember.type.get()), slotAndOffset.first, slotAndOffset.second }); |
|||
} |
|||
} |
|||
break; |
|||
case Type::Category::Function: |
|||
case Type::Category::IntegerConstant: |
|||
case Type::Category::StringLiteral: |
|||
case Type::Category::Magic: |
|||
case Type::Category::Mapping: |
|||
case Type::Category::Modifier: |
|||
case Type::Category::Real: |
|||
case Type::Category::TypeType: |
|||
case Type::Category::Void: |
|||
default: |
|||
break; |
|||
} |
|||
return r; |
|||
} |
|||
|
|||
bool CodeModel::isContractOrFunctionLocation(dev::SourceLocation const& _location) |
|||
{ |
|||
if (!_location.sourceName) |
|||
return false; |
|||
Guard l(x_contractMap); |
|||
auto sourceMapIter = m_sourceMaps.find(QString::fromStdString(*_location.sourceName)); |
|||
if (sourceMapIter != m_sourceMaps.cend()) |
|||
{ |
|||
LocationPair location(_location.start, _location.end); |
|||
return sourceMapIter.value().contracts.contains(location) || sourceMapIter.value().functions.contains(location); |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
QString CodeModel::resolveFunctionName(dev::SourceLocation const& _location) |
|||
{ |
|||
if (!_location.sourceName) |
|||
return QString(); |
|||
Guard l(x_contractMap); |
|||
auto sourceMapIter = m_sourceMaps.find(QString::fromStdString(*_location.sourceName)); |
|||
if (sourceMapIter != m_sourceMaps.cend()) |
|||
{ |
|||
LocationPair location(_location.start, _location.end); |
|||
auto functionNameIter = sourceMapIter.value().functions.find(location); |
|||
if (functionNameIter != sourceMapIter.value().functions.cend()) |
|||
return functionNameIter.value(); |
|||
} |
|||
return QString(); |
|||
} |
|||
|
|||
void CodeModel::setOptimizeCode(bool _value) |
|||
{ |
|||
m_optimizeCode = _value; |
|||
emit scheduleCompilationJob(++m_backgroundJobId); |
|||
} |
|||
|
|||
void GasMapWrapper::push(QString _source, int _start, int _end, QString _value, bool _isInfinite, GasMap::type _type, QString _contractName, QString _functionName) |
|||
{ |
|||
GasMap* gas = new GasMap(_start, _end, _value, _isInfinite, _type, _contractName, _functionName, this); |
|||
m_gasMaps.find(_source).value().push_back(QVariant::fromValue(gas)); |
|||
} |
|||
|
|||
bool GasMapWrapper::contains(QString _key) |
|||
{ |
|||
return m_gasMaps.contains(_key); |
|||
} |
|||
|
|||
void GasMapWrapper::insert(QString _source, QVariantList _variantList) |
|||
{ |
|||
m_gasMaps.insert(_source, _variantList); |
|||
} |
|||
|
|||
QVariantList GasMapWrapper::gasCostsByDocId(QString _source) |
|||
{ |
|||
auto gasIter = m_gasMaps.find(_source); |
|||
if (gasIter != m_gasMaps.end()) |
|||
return gasIter.value(); |
|||
else |
|||
return QVariantList(); |
|||
} |
|||
|
|||
QVariantList GasMapWrapper::gasCostsBy(QString _contractName, QString _functionName) |
|||
{ |
|||
QVariantList gasMap; |
|||
for (auto const& map: m_gasMaps) |
|||
{ |
|||
for (auto const& gas: map) |
|||
{ |
|||
if (gas.value<GasMap*>()->contractName() == _contractName && (_functionName.isEmpty() || gas.value<GasMap*>()->functionName() == _functionName)) |
|||
gasMap.push_back(gas); |
|||
} |
|||
} |
|||
return gasMap; |
|||
} |
|||
|
@ -1,308 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file CodeModel.h
|
|||
* @author Arkadiy Paronyan arkadiy@ethdev.com |
|||
* @date 2014 |
|||
* Ethereum IDE client. |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <memory> |
|||
#include <atomic> |
|||
#include <map> |
|||
#include <QObject> |
|||
#include <QThread> |
|||
#include <QHash> |
|||
#include <QMetaEnum> |
|||
#include <libdevcore/Common.h> |
|||
#include <libdevcore/Guards.h> |
|||
#include <libevmcore/Params.h> |
|||
#include <libevmasm/Assembly.h> |
|||
#include <libdevcore/SHA3.h> |
|||
#include "SolidityType.h" |
|||
#include "QBigInt.h" |
|||
|
|||
class QTextDocument; |
|||
|
|||
namespace dev |
|||
{ |
|||
|
|||
namespace solidity |
|||
{ |
|||
class CompilerStack; |
|||
class Type; |
|||
} |
|||
|
|||
namespace mix |
|||
{ |
|||
|
|||
class CodeModel; |
|||
class CodeHighlighter; |
|||
class CodeHighlighterSettings; |
|||
class QContractDefinition; |
|||
|
|||
//utility class to perform tasks in background thread
|
|||
class BackgroundWorker: public QObject |
|||
{ |
|||
Q_OBJECT |
|||
|
|||
public: |
|||
BackgroundWorker(CodeModel* _model): QObject(), m_model(_model) {} |
|||
|
|||
public slots: |
|||
void queueCodeChange(int _jobId); |
|||
private: |
|||
CodeModel* m_model; |
|||
}; |
|||
|
|||
using LocationPair = QPair<int, int>; |
|||
|
|||
///Compilation result model. Contains all the compiled contract data required by UI
|
|||
class CompiledContract: public QObject |
|||
{ |
|||
Q_OBJECT |
|||
Q_PROPERTY(QContractDefinition* contract READ contract) |
|||
Q_PROPERTY(QString contractInterface READ contractInterface CONSTANT) |
|||
Q_PROPERTY(QString codeHex READ codeHex CONSTANT) |
|||
Q_PROPERTY(QString documentId READ documentId CONSTANT) |
|||
|
|||
public: |
|||
/// Successful compilation result constructor
|
|||
CompiledContract(solidity::CompilerStack const& _compiler, QString const& _contractName, QString const& _source); |
|||
|
|||
/// @returns contract definition for QML property
|
|||
QContractDefinition* contract() const { return m_contract.get(); } |
|||
/// @returns contract definition
|
|||
std::shared_ptr<QContractDefinition> sharedContract() const { return m_contract; } |
|||
/// @returns contract bytecode
|
|||
dev::bytes const& bytes() const { return m_bytes; } |
|||
/// @returns contract bytecode as hex string
|
|||
QString codeHex() const; |
|||
/// @returns contract definition in JSON format
|
|||
QString contractInterface() const { return m_contractInterface; } |
|||
/// @return assebly item locations
|
|||
eth::AssemblyItems const& assemblyItems() const { return m_assemblyItems; } |
|||
eth::AssemblyItems const& constructorAssemblyItems() const { return m_constructorAssemblyItems; } |
|||
/// @returns contract source Id
|
|||
QString documentId() const { return m_documentId; } |
|||
|
|||
QHash<LocationPair, SolidityDeclaration> const& locals() const { return m_locals; } |
|||
QHash<unsigned, SolidityDeclarations> const& storage() const { return m_storage; } |
|||
|
|||
private: |
|||
uint m_sourceHash; |
|||
std::shared_ptr<QContractDefinition> m_contract; |
|||
QString m_compilerMessage; ///< @todo: use some structure here
|
|||
dev::bytes m_bytes; |
|||
QString m_contractInterface; |
|||
QString m_documentId; |
|||
eth::AssemblyItems m_assemblyItems; |
|||
eth::AssemblyItems m_constructorAssemblyItems; |
|||
QHash<LocationPair, SolidityDeclaration> m_locals; |
|||
QHash<unsigned, SolidityDeclarations> m_storage; |
|||
|
|||
friend class CodeModel; |
|||
}; |
|||
|
|||
using ContractMap = QMap<QString, CompiledContract*>; //needs to be sorted
|
|||
|
|||
/// Source map
|
|||
using LocationMap = QHash<LocationPair, QString>; |
|||
|
|||
struct SourceMap |
|||
{ |
|||
LocationMap contracts; |
|||
LocationMap functions; |
|||
}; |
|||
|
|||
using SourceMaps = QMap<QString, SourceMap>; //by source id
|
|||
using GasCostsMaps = QMap<QString, QVariantList>; //gas cost by contract name
|
|||
|
|||
class GasMap: public QObject |
|||
{ |
|||
Q_OBJECT |
|||
Q_ENUMS(type) |
|||
Q_PROPERTY(int start MEMBER m_start CONSTANT) |
|||
Q_PROPERTY(int end MEMBER m_end CONSTANT) |
|||
Q_PROPERTY(QString gas MEMBER m_gas CONSTANT) |
|||
Q_PROPERTY(bool isInfinite MEMBER m_isInfinite CONSTANT) |
|||
Q_PROPERTY(QString codeBlockType READ codeBlockType CONSTANT) |
|||
Q_PROPERTY(QString contractName MEMBER m_contractName CONSTANT) |
|||
Q_PROPERTY(QString functionName MEMBER m_functionName CONSTANT) |
|||
|
|||
public: |
|||
|
|||
enum type |
|||
{ |
|||
Statement, |
|||
Function, |
|||
Constructor |
|||
}; |
|||
|
|||
GasMap(int _start, int _end, QString _gas, bool _isInfinite, type _type, QString _contractName, QString _functionName, QObject* _parent): QObject(_parent), |
|||
m_start(_start), m_end(_end), m_gas(_gas), m_isInfinite(_isInfinite), m_type(_type), m_contractName(_contractName), m_functionName(_functionName) {} |
|||
QString contractName() { return m_contractName; } |
|||
QString functionName() { return m_functionName; } |
|||
|
|||
private: |
|||
int m_start; |
|||
int m_end; |
|||
QString m_gas; |
|||
bool m_isInfinite; |
|||
type m_type; |
|||
QString m_contractName; |
|||
QString m_functionName; |
|||
|
|||
QString codeBlockType() const |
|||
{ |
|||
QMetaEnum units = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("type")); |
|||
if (m_type) |
|||
{ |
|||
const char* key = units.valueToKey(m_type); |
|||
return QString(key).toLower(); |
|||
} |
|||
return QString(""); |
|||
} |
|||
}; |
|||
|
|||
class GasMapWrapper: public QObject |
|||
{ |
|||
Q_OBJECT |
|||
|
|||
Q_PROPERTY(GasCostsMaps gasMaps MEMBER m_gasMaps CONSTANT) |
|||
|
|||
public: |
|||
GasMapWrapper(QObject* _parent = nullptr): QObject(_parent){} |
|||
void push(QString _source, int _start, int _end, QString _value, bool _isInfinite, GasMap::type _type, QString _contractName = "", QString _functionName = ""); |
|||
bool contains(QString _key); |
|||
void insert(QString _source, QVariantList _variantList); |
|||
QVariantList gasCostsByDocId(QString _source); |
|||
QVariantList gasCostsBy(QString _contractName, QString _functionName = ""); |
|||
|
|||
private: |
|||
GasCostsMaps m_gasMaps; |
|||
}; |
|||
|
|||
/// Code compilation model. Compiles contracts in background an provides compiled contract data
|
|||
class CodeModel: public QObject |
|||
{ |
|||
Q_OBJECT |
|||
|
|||
public: |
|||
CodeModel(); |
|||
~CodeModel(); |
|||
|
|||
Q_PROPERTY(QVariantMap contracts READ contracts NOTIFY codeChanged) |
|||
Q_PROPERTY(bool compiling READ isCompiling NOTIFY stateChanged) |
|||
Q_PROPERTY(bool hasContract READ hasContract NOTIFY codeChanged) |
|||
Q_PROPERTY(bool optimizeCode MEMBER m_optimizeCode WRITE setOptimizeCode) |
|||
Q_PROPERTY(int callStipend READ callStipend) |
|||
Q_PROPERTY(int txGas READ txGas) |
|||
|
|||
/// @returns latest compilation results for contracts
|
|||
QVariantMap contracts() const; |
|||
/// @returns compilation status
|
|||
bool isCompiling() const { return m_compiling; } |
|||
/// @returns true there is a contract which has at least one function
|
|||
bool hasContract() const; |
|||
/// Get contract code by url. Contract is compiled on first access and cached
|
|||
dev::bytes const& getStdContractCode(QString const& _contractName, QString const& _url); |
|||
/// Get contract by name
|
|||
/// Throws if not found
|
|||
CompiledContract const& contract(QString const& _name) const; |
|||
/// Get contract by name
|
|||
/// @returns nullptr if not found
|
|||
Q_INVOKABLE CompiledContract const* tryGetContract(QString const& _name) const; |
|||
/// Find a contract by document id
|
|||
/// @returns CompiledContract object or null if not found
|
|||
Q_INVOKABLE CompiledContract* contractByDocumentId(QString const& _documentId) const; |
|||
/// Reset code model
|
|||
Q_INVOKABLE void reset() { reset(QVariantMap()); } |
|||
/// Delete a contract source
|
|||
Q_INVOKABLE void unregisterContractSrc(QString const& _documentId); |
|||
/// Convert solidity type info to mix type
|
|||
static SolidityType nodeType(dev::solidity::Type const* _type); |
|||
/// Retrieve subtype
|
|||
static void retrieveSubType(SolidityType& _wrapperType, dev::solidity::Type const* _type); |
|||
/// Check if given location belongs to contract or function
|
|||
bool isContractOrFunctionLocation(dev::SourceLocation const& _location); |
|||
/// Get funciton name by location
|
|||
QString resolveFunctionName(dev::SourceLocation const& _location); |
|||
/// Gas estimation for compiled sources
|
|||
void gasEstimation(solidity::CompilerStack const& _cs); |
|||
/// Gas cost by doc id
|
|||
Q_INVOKABLE QVariantList gasCostByDocumentId(QString const& _documentId) const; |
|||
/// Gas cost by @arg contractName @arg functionName
|
|||
Q_INVOKABLE QVariantList gasCostBy(QString const& _contractName, QString const& _functionName) const; |
|||
/// Set optimize code
|
|||
Q_INVOKABLE void setOptimizeCode(bool _value); |
|||
/// sha3
|
|||
Q_INVOKABLE QString sha3(QString _source) { return QString::fromStdString(dev::sha3(_source.toStdString()).hex()); } |
|||
int txGas() { return static_cast<int>(dev::eth::c_txGas); } |
|||
int callStipend() { return static_cast<int>(dev::eth::c_callStipend); } |
|||
|
|||
signals: |
|||
/// Emited on compilation state change
|
|||
void stateChanged(); |
|||
/// Emitted on compilation complete
|
|||
void compilationComplete(); |
|||
/// Emitted on compilation error
|
|||
void compilationError(QString _error, QVariantMap _firstErrorLoc, QVariantList _secondErrorLoc); |
|||
/// Internal signal used to transfer compilation job to background thread
|
|||
void scheduleCompilationJob(int _jobId); |
|||
/// Emitted if there are any changes in the code model
|
|||
void codeChanged(); |
|||
/// Emitted if there are any changes in the contract interface
|
|||
void contractInterfaceChanged(QString _documentId); |
|||
/// Emitted if there is a new contract compiled for the first time
|
|||
void newContractCompiled(QString _documentId); |
|||
/// Emitted if a contract name has been changed
|
|||
void contractRenamed(QString _documentId, QString _oldName, QString _newName); |
|||
|
|||
public slots: |
|||
/// Update code model on source code change
|
|||
void registerCodeChange(QString const& _documentId, QString const& _code); |
|||
/// Reset code model for a new project
|
|||
void reset(QVariantMap const& _documents); |
|||
|
|||
private: |
|||
void runCompilationJob(int _jobId); |
|||
void stop(); |
|||
void releaseContracts(); |
|||
void collectContracts(dev::solidity::CompilerStack const& _cs, std::vector<std::string> const& _sourceNames); |
|||
QVariantMap resolveCompilationErrorLocation(dev::solidity::CompilerStack const& _cs, dev::SourceLocation const& _location); |
|||
|
|||
std::atomic<bool> m_compiling; |
|||
mutable dev::Mutex x_contractMap; |
|||
ContractMap m_contractMap; |
|||
SourceMaps m_sourceMaps; |
|||
GasMapWrapper* m_gasCostsMaps = 0; |
|||
std::unique_ptr<CodeHighlighterSettings> m_codeHighlighterSettings; |
|||
QThread m_backgroundThread; |
|||
BackgroundWorker m_backgroundWorker; |
|||
int m_backgroundJobId = 0; //protects from starting obsolete compilation job
|
|||
std::map<QString, dev::bytes> m_compiledContracts; //by name
|
|||
dev::Mutex x_pendingContracts; |
|||
std::map<QString, QString> m_pendingContracts; //name to source
|
|||
bool m_optimizeCode = false; |
|||
friend class BackgroundWorker; |
|||
}; |
|||
|
|||
} |
|||
|
|||
} |
@ -1,383 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file ContractCallDataEncoder.cpp
|
|||
* @author Yann yann@ethdev.com |
|||
* @date 2014 |
|||
* Ethereum IDE client. |
|||
*/ |
|||
|
|||
#include <vector> |
|||
#include <QtCore/qmath.h> |
|||
#include <QMap> |
|||
#include <QStringList> |
|||
#include <QJsonArray> |
|||
#include <QJsonDocument> |
|||
#include <QJsonObject> |
|||
#include <libethcore/CommonJS.h> |
|||
#include <libsolidity/AST.h> |
|||
#include "QVariableDeclaration.h" |
|||
#include "QVariableDefinition.h" |
|||
#include "QFunctionDefinition.h" |
|||
#include "ContractCallDataEncoder.h" |
|||
using namespace std; |
|||
using namespace dev; |
|||
using namespace dev::solidity; |
|||
using namespace dev::mix; |
|||
|
|||
bytes ContractCallDataEncoder::encodedData() |
|||
{ |
|||
bytes r(m_encodedData); |
|||
size_t headerSize = m_encodedData.size() & ~0x1fUL; //ignore any prefix that is not 32-byte aligned
|
|||
//apply offsets
|
|||
for (auto const& p: m_dynamicOffsetMap) |
|||
{ |
|||
vector_ref<byte> offsetRef(m_dynamicData.data() + p.first, 32); |
|||
toBigEndian(p.second + headerSize, offsetRef); //add header size minus signature hash
|
|||
} |
|||
for (auto const& p: m_staticOffsetMap) |
|||
{ |
|||
vector_ref<byte> offsetRef(r.data() + p.first, 32); |
|||
toBigEndian(p.second + headerSize, offsetRef); //add header size minus signature hash
|
|||
} |
|||
if (m_dynamicData.size() > 0) |
|||
r.insert(r.end(), m_dynamicData.begin(), m_dynamicData.end()); |
|||
return r; |
|||
} |
|||
|
|||
void ContractCallDataEncoder::encode(QFunctionDefinition const* _function) |
|||
{ |
|||
bytes hash = _function->hash().asBytes(); |
|||
m_encodedData.insert(m_encodedData.end(), hash.begin(), hash.end()); |
|||
} |
|||
|
|||
void ContractCallDataEncoder::encodeArray(QJsonArray const& _array, SolidityType const& _type, bytes& _content) |
|||
{ |
|||
size_t offsetStart = _content.size(); |
|||
if (_type.dynamicSize) |
|||
{ |
|||
bytes count = bytes(32); |
|||
toBigEndian((u256)_array.size(), count); |
|||
_content += count; //reserved space for count
|
|||
} |
|||
|
|||
int k = 0; |
|||
for (QJsonValue const& c: _array) |
|||
{ |
|||
if (c.isArray()) |
|||
{ |
|||
if (_type.baseType->dynamicSize) |
|||
m_dynamicOffsetMap.push_back(std::make_pair(m_dynamicData.size() + offsetStart + 32 + k * 32, m_dynamicData.size() + _content.size())); |
|||
encodeArray(c.toArray(), *_type.baseType, _content); |
|||
} |
|||
else |
|||
{ |
|||
// encode single item
|
|||
if (c.isDouble()) |
|||
encodeSingleItem(QString::number(c.toDouble()), _type, _content); |
|||
else if (c.isString()) |
|||
encodeSingleItem(c.toString(), _type, _content); |
|||
} |
|||
k++; |
|||
} |
|||
} |
|||
|
|||
void ContractCallDataEncoder::encode(QVariant const& _data, SolidityType const& _type) |
|||
{ |
|||
if (_type.dynamicSize && (_type.type == SolidityType::Type::Bytes || _type.type == SolidityType::Type::String)) |
|||
{ |
|||
bytes empty(32); |
|||
size_t sizePos = m_dynamicData.size(); |
|||
m_dynamicData += empty; //reserve space for count
|
|||
encodeSingleItem(_data.toString(), _type, m_dynamicData); |
|||
vector_ref<byte> sizeRef(m_dynamicData.data() + sizePos, 32); |
|||
toBigEndian(static_cast<unsigned>(_data.toString().size()), sizeRef); |
|||
m_staticOffsetMap.push_back(std::make_pair(m_encodedData.size(), sizePos)); |
|||
m_encodedData += empty; //reserve space for offset
|
|||
} |
|||
else if (_type.array) |
|||
{ |
|||
bytes content; |
|||
size_t size = m_encodedData.size(); |
|||
if (_type.dynamicSize) |
|||
{ |
|||
m_encodedData += bytes(32); // reserve space for offset
|
|||
m_staticOffsetMap.push_back(std::make_pair(size, m_dynamicData.size())); |
|||
} |
|||
QJsonDocument jsonDoc = QJsonDocument::fromJson(_data.toString().toUtf8()); |
|||
encodeArray(jsonDoc.array(), _type, content); |
|||
|
|||
if (!_type.dynamicSize) |
|||
m_encodedData.insert(m_encodedData.end(), content.begin(), content.end()); |
|||
else |
|||
m_dynamicData.insert(m_dynamicData.end(), content.begin(), content.end()); |
|||
} |
|||
else |
|||
encodeSingleItem(_data.toString(), _type, m_encodedData); |
|||
} |
|||
|
|||
unsigned ContractCallDataEncoder::encodeSingleItem(QString const& _data, SolidityType const& _type, bytes& _dest) |
|||
{ |
|||
if (_type.type == SolidityType::Type::Struct) |
|||
BOOST_THROW_EXCEPTION(dev::Exception() << dev::errinfo_comment("Struct parameters are not supported yet")); |
|||
|
|||
unsigned const alignSize = 32; |
|||
QString src = _data; |
|||
bytes result; |
|||
|
|||
if ((src.startsWith("\"") && src.endsWith("\"")) || (src.startsWith("\'") && src.endsWith("\'"))) |
|||
src = src.remove(src.length() - 1, 1).remove(0, 1); |
|||
|
|||
if (src.startsWith("0x")) |
|||
{ |
|||
result = fromHex(src.toStdString().substr(2)); |
|||
if (_type.type != SolidityType::Type::Bytes) |
|||
result = padded(result, alignSize); |
|||
} |
|||
else |
|||
{ |
|||
try |
|||
{ |
|||
bigint i(src.toStdString()); |
|||
result = bytes(alignSize); |
|||
toBigEndian((u256)i, result); |
|||
} |
|||
catch (std::exception const&) |
|||
{ |
|||
// manage input as a string.
|
|||
result = encodeStringParam(src, alignSize); |
|||
} |
|||
} |
|||
|
|||
size_t dataSize = _type.dynamicSize ? result.size() : alignSize; |
|||
if (result.size() % alignSize != 0) |
|||
result.resize((result.size() & ~(alignSize - 1)) + alignSize); |
|||
_dest.insert(_dest.end(), result.begin(), result.end()); |
|||
return dataSize; |
|||
} |
|||
|
|||
bigint ContractCallDataEncoder::decodeInt(dev::bytes const& _rawValue) |
|||
{ |
|||
dev::u256 un = dev::fromBigEndian<dev::u256>(_rawValue); |
|||
if (un >> 255) |
|||
return (-s256(~un + 1)); |
|||
return un; |
|||
} |
|||
|
|||
QString ContractCallDataEncoder::toString(dev::bigint const& _int) |
|||
{ |
|||
std::stringstream str; |
|||
str << std::dec << _int; |
|||
return QString::fromStdString(str.str()); |
|||
} |
|||
|
|||
dev::bytes ContractCallDataEncoder::encodeBool(QString const& _str) |
|||
{ |
|||
bytes b(1); |
|||
b[0] = _str == "1" || _str.toLower() == "true " ? 1 : 0; |
|||
return padded(b, 32); |
|||
} |
|||
|
|||
bool ContractCallDataEncoder::decodeBool(dev::bytes const& _rawValue) |
|||
{ |
|||
byte ret = _rawValue.at(_rawValue.size() - 1); |
|||
return (ret != 0); |
|||
} |
|||
|
|||
QString ContractCallDataEncoder::toString(bool _b) |
|||
{ |
|||
return _b ? "true" : "false"; |
|||
} |
|||
|
|||
dev::bytes ContractCallDataEncoder::encodeStringParam(QString const& _str, unsigned alignSize) |
|||
{ |
|||
bytes result; |
|||
QByteArray bytesAr = _str.toLocal8Bit(); |
|||
result = bytes(bytesAr.begin(), bytesAr.end()); |
|||
return paddedRight(result, alignSize); |
|||
} |
|||
|
|||
dev::bytes ContractCallDataEncoder::encodeBytes(QString const& _str) |
|||
{ |
|||
QByteArray bytesAr = _str.toLocal8Bit(); |
|||
bytes r = bytes(bytesAr.begin(), bytesAr.end()); |
|||
return padded(r, 32); |
|||
} |
|||
|
|||
dev::bytes ContractCallDataEncoder::decodeBytes(dev::bytes const& _rawValue) |
|||
{ |
|||
return _rawValue; |
|||
} |
|||
|
|||
QString ContractCallDataEncoder::toString(dev::bytes const& _b) |
|||
{ |
|||
return QString::fromStdString(dev::toJS(_b)); |
|||
} |
|||
|
|||
QString ContractCallDataEncoder::toChar(dev::bytes const& _b) |
|||
{ |
|||
QString str; |
|||
asString(_b, str); |
|||
return str; |
|||
} |
|||
|
|||
QJsonValue ContractCallDataEncoder::decodeArrayContent(SolidityType const& _type, bytes const& _value, int& pos) |
|||
{ |
|||
if (_type.baseType->array) |
|||
{ |
|||
QJsonArray sub = decodeArray(*_type.baseType, _value, pos); |
|||
return sub; |
|||
} |
|||
else |
|||
{ |
|||
bytesConstRef value(_value.data() + pos, 32); |
|||
bytes rawParam(32); |
|||
value.populate(&rawParam); |
|||
QVariant i = decode(*_type.baseType, rawParam); |
|||
pos = pos + 32; |
|||
return i.toString(); |
|||
} |
|||
} |
|||
|
|||
QJsonArray ContractCallDataEncoder::decodeArray(SolidityType const& _type, bytes const& _value, int& pos) |
|||
{ |
|||
QJsonArray array; |
|||
bytesConstRef value(&_value); |
|||
int count = 0; |
|||
bigint offset = pos; |
|||
int valuePosition = pos; |
|||
if (!_type.dynamicSize) |
|||
count = _type.count; |
|||
else |
|||
{ |
|||
bytesConstRef value(_value.data() + pos, 32); // offset
|
|||
bytes rawParam(32); |
|||
value.populate(&rawParam); |
|||
offset = decodeInt(rawParam); |
|||
valuePosition = static_cast<int>(offset) + 32; |
|||
pos += 32; |
|||
value = bytesConstRef(_value.data() + static_cast<int>(offset), 32); // count
|
|||
value.populate(&rawParam); |
|||
count = static_cast<int>(decodeInt(rawParam)); |
|||
} |
|||
if (_type.type == QSolidityType::Type::Bytes || _type.type == QSolidityType::Type::String) |
|||
{ |
|||
bytesConstRef value(_value.data() + (static_cast<int>(offset) + 32), 32); |
|||
bytes rawParam(count); |
|||
value.populate(&rawParam); |
|||
if (_type.type == QSolidityType::Type::Bytes) |
|||
array.append(toString(decodeBytes(rawParam))); |
|||
else |
|||
array.append(toChar(decodeBytes(rawParam))); |
|||
} |
|||
else |
|||
{ |
|||
for (int k = 0; k < count; ++k) |
|||
{ |
|||
if (_type.dynamicSize) |
|||
array.append(decodeArrayContent(_type, _value, valuePosition)); |
|||
else |
|||
array.append(decodeArrayContent(_type, _value, pos)); |
|||
} |
|||
} |
|||
return array; |
|||
} |
|||
|
|||
QVariant ContractCallDataEncoder::decode(SolidityType const& _type, bytes const& _value) |
|||
{ |
|||
bytesConstRef value(&_value); |
|||
bytes rawParam(32); |
|||
value.populate(&rawParam); |
|||
QSolidityType::Type type = _type.type; |
|||
if (type == QSolidityType::Type::SignedInteger || type == QSolidityType::Type::UnsignedInteger) |
|||
return QVariant::fromValue(toString(decodeInt(rawParam))); |
|||
else if (type == QSolidityType::Type::Bool) |
|||
return QVariant::fromValue(toString(decodeBool(rawParam))); |
|||
else if (type == QSolidityType::Type::Bytes || type == QSolidityType::Type::Hash) |
|||
return QVariant::fromValue(toString(decodeBytes(rawParam))); |
|||
else if (type == QSolidityType::Type::String) |
|||
return QVariant::fromValue(toChar(decodeBytes(rawParam))); |
|||
else if (type == QSolidityType::Type::Struct) |
|||
return QVariant::fromValue(QString("struct")); //TODO
|
|||
else if (type == QSolidityType::Type::Address) |
|||
return QVariant::fromValue(toString(decodeBytes(unpadLeft(rawParam)))); |
|||
else if (type == QSolidityType::Type::Enum) |
|||
return QVariant::fromValue(decodeEnum(rawParam)); |
|||
else |
|||
BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Parameter declaration not found")); |
|||
} |
|||
|
|||
QString ContractCallDataEncoder::decodeEnum(bytes _value) |
|||
{ |
|||
return toString(decodeInt(_value)); |
|||
} |
|||
|
|||
QString ContractCallDataEncoder::decode(QVariableDeclaration* const& _param, bytes _value) |
|||
{ |
|||
SolidityType const& type = _param->type()->type(); |
|||
return decode(type, _value).toString(); |
|||
} |
|||
|
|||
QStringList ContractCallDataEncoder::decode(QList<QVariableDeclaration*> const& _returnParameters, bytes _value) |
|||
{ |
|||
bytes _v = _value; |
|||
QStringList r; |
|||
int readPosition = 0; |
|||
for (int k = 0; k <_returnParameters.length(); k++) |
|||
{ |
|||
QVariableDeclaration* dec = static_cast<QVariableDeclaration*>(_returnParameters.at(k)); |
|||
SolidityType const& type = dec->type()->type(); |
|||
if (type.array) |
|||
{ |
|||
QJsonArray array = decodeArray(type, _v, readPosition); |
|||
if (type.type == SolidityType::String && array.count() <= 1) |
|||
{ |
|||
if (array.count() == 1) |
|||
r.append(array[0].toString()); |
|||
else |
|||
r.append(QString()); |
|||
} |
|||
else |
|||
{ |
|||
QJsonDocument jsonDoc = QJsonDocument::fromVariant(array.toVariantList()); |
|||
r.append(jsonDoc.toJson(QJsonDocument::Compact)); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
bytesConstRef value(_value.data() + readPosition, 32); |
|||
bytes rawParam(32); |
|||
value.populate(&rawParam); |
|||
r.append(decode(type, rawParam).toString()); |
|||
readPosition += 32; |
|||
} |
|||
} |
|||
return r; |
|||
} |
|||
|
|||
|
|||
bool ContractCallDataEncoder::asString(dev::bytes const& _b, QString& _str) |
|||
{ |
|||
dev::bytes bunPad = unpadded(_b); |
|||
for (unsigned i = 0; i < bunPad.size(); i++) |
|||
{ |
|||
if (bunPad.at(i) < 9 || bunPad.at(i) > 127) |
|||
return false; |
|||
else |
|||
_str += QString::fromStdString(dev::toJS(bunPad.at(i))).replace("0x", ""); |
|||
} |
|||
return true; |
|||
} |
@ -1,91 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file ContractCallDataEncoder.h
|
|||
* @author Yann yann@ethdev.com |
|||
* @date 2014 |
|||
* Ethereum IDE client. |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include "QVariableDeclaration.h" |
|||
#include "QVariableDefinition.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace mix |
|||
{ |
|||
|
|||
class QFunctionDefinition; |
|||
class QVariableDeclaration; |
|||
class QVariableDefinition; |
|||
class QSolidityType; |
|||
|
|||
/**
|
|||
* @brief Encode/Decode data to be sent to a transaction or to be displayed in a view. |
|||
*/ |
|||
class ContractCallDataEncoder |
|||
{ |
|||
public: |
|||
ContractCallDataEncoder() {} |
|||
/// Encode hash of the function to call.
|
|||
void encode(QFunctionDefinition const* _function); |
|||
/// Encode data for corresponding type
|
|||
void encode(QVariant const& _data, SolidityType const& _type); |
|||
/// Decode variable in order to be sent to QML view.
|
|||
QStringList decode(QList<QVariableDeclaration*> const& _dec, bytes _value); |
|||
/// Decode @param _parameter
|
|||
QString decode(QVariableDeclaration* const& _param, bytes _value); |
|||
/// Decode single variable
|
|||
QVariant decode(SolidityType const& _type, bytes const& _value); |
|||
/// Get all encoded data encoded by encode function.
|
|||
bytes encodedData(); |
|||
/// Encode a string to bytes (in order to be used as funtion param)
|
|||
dev::bytes encodeStringParam(QString const& _str, unsigned _alignSize); |
|||
/// Encode a string to ABI bytes
|
|||
dev::bytes encodeBytes(QString const& _str); |
|||
/// Decode bytes from ABI
|
|||
dev::bytes decodeBytes(dev::bytes const& _rawValue); |
|||
/// Decode array
|
|||
QJsonArray decodeArray(SolidityType const& _type, bytes const& _value, int& pos); |
|||
/// Decode array items
|
|||
QJsonValue decodeArrayContent(SolidityType const& _type, bytes const& _value, int& pos); |
|||
/// Decode enum
|
|||
QString decodeEnum(bytes _value); |
|||
|
|||
private: |
|||
unsigned encodeSingleItem(QString const& _data, SolidityType const& _type, bytes& _dest); |
|||
bigint decodeInt(dev::bytes const& _rawValue); |
|||
dev::bytes encodeInt(QString const& _str); |
|||
QString toString(dev::bigint const& _int); |
|||
dev::bytes encodeBool(QString const& _str); |
|||
bool decodeBool(dev::bytes const& _rawValue); |
|||
QString toString(bool _b); |
|||
QString toString(dev::bytes const& _b); |
|||
bool asString(dev::bytes const& _b, QString& _str); |
|||
void encodeArray(QJsonArray const& _array, SolidityType const& _type, bytes& _content); |
|||
QString toChar(dev::bytes const& _b); |
|||
|
|||
private: |
|||
bytes m_encodedData; |
|||
bytes m_dynamicData; |
|||
std::vector<std::pair<size_t, size_t>> m_dynamicOffsetMap; |
|||
std::vector<std::pair<size_t, size_t>> m_staticOffsetMap; |
|||
}; |
|||
|
|||
} |
|||
} |
@ -1,185 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file DebuggingStateWrapper.cpp
|
|||
* @author Yann yann@ethdev.com |
|||
* @date 2014 |
|||
* Used to translate c++ type (u256, bytes, ...) into friendly value (to be used by QML). |
|||
*/ |
|||
|
|||
#include <tuple> |
|||
#include <QDebug> |
|||
#include <QPointer> |
|||
#include <QQmlEngine> |
|||
#include <QVariantList> |
|||
#include <libevmcore/Instruction.h> |
|||
#include <libethcore/CommonJS.h> |
|||
#include <libdevcrypto/Common.h> |
|||
#include <libevmcore/Instruction.h> |
|||
#include <libdevcore/Common.h> |
|||
#include "DebuggingStateWrapper.h" |
|||
#include "QBigInt.h" |
|||
using namespace dev; |
|||
using namespace dev::eth; |
|||
using namespace dev::mix; |
|||
|
|||
namespace |
|||
{ |
|||
static QVariantList memDumpToList(bytes const& _bytes, unsigned _width, bool _includeAddress = false) |
|||
{ |
|||
QVariantList dumpList; |
|||
for (unsigned i = 0; i < _bytes.size(); i += _width) |
|||
{ |
|||
std::stringstream ret; |
|||
if (_includeAddress) |
|||
{ |
|||
ret << std::setfill('0') << std::setw(6) << std::hex << i << " "; |
|||
ret << " "; |
|||
} |
|||
for (unsigned j = i; j < i + _width; ++j) |
|||
if (j < _bytes.size()) |
|||
if (_bytes[j] >= 32 && _bytes[j] < 127) |
|||
ret << (char)_bytes[j]; |
|||
else |
|||
ret << '?'; |
|||
else |
|||
ret << ' '; |
|||
QString strPart = QString::fromStdString(ret.str()); |
|||
|
|||
ret.clear(); |
|||
ret.str(std::string()); |
|||
|
|||
for (unsigned j = i; j < i + _width && j < _bytes.size(); ++j) |
|||
ret << std::setfill('0') << std::setw(2) << std::hex << (unsigned)_bytes[j] << " "; |
|||
QString hexPart = QString::fromStdString(ret.str()); |
|||
|
|||
QStringList line = { strPart, hexPart }; |
|||
dumpList.push_back(line); |
|||
} |
|||
return dumpList; |
|||
} |
|||
} |
|||
|
|||
QCode* QMachineState::getHumanReadableCode(QObject* _owner, const Address& _address, const bytes& _code, QHash<int, int>& o_codeMap) |
|||
{ |
|||
QVariantList codeStr; |
|||
for (unsigned i = 0; i <= _code.size(); ++i) |
|||
{ |
|||
byte b = i < _code.size() ? _code[i] : 0; |
|||
try |
|||
{ |
|||
QString s = QString::fromStdString(instructionInfo((Instruction)b).name); |
|||
std::ostringstream out; |
|||
out << std::hex << std::setw(4) << std::setfill('0') << i; |
|||
int offset = i; |
|||
if (b >= (byte)Instruction::PUSH1 && b <= (byte)Instruction::PUSH32) |
|||
{ |
|||
unsigned bc = getPushNumber((Instruction)b); |
|||
s = "PUSH 0x" + QString::fromStdString(toHex(bytesConstRef(&_code[i + 1], bc))); |
|||
i += bc; |
|||
} |
|||
o_codeMap[offset] = codeStr.size(); |
|||
codeStr.append(QVariant::fromValue(new QInstruction(_owner, QString::fromStdString(out.str()) + " " + s))); |
|||
} |
|||
catch (...) |
|||
{ |
|||
qDebug() << QString("Unhandled exception!") << endl << |
|||
QString::fromStdString(boost::current_exception_diagnostic_information()); |
|||
break; // probably hit data segment
|
|||
} |
|||
} |
|||
return new QCode(_owner, QString::fromStdString(toString(_address)), std::move(codeStr)); |
|||
} |
|||
|
|||
QBigInt* QMachineState::gasCost() |
|||
{ |
|||
return new QBigInt(m_state.gasCost); |
|||
} |
|||
|
|||
QBigInt* QMachineState::gas() |
|||
{ |
|||
return new QBigInt(m_state.gas); |
|||
} |
|||
|
|||
QBigInt* QMachineState::newMemSize() |
|||
{ |
|||
return new QBigInt(m_state.newMemSize); |
|||
} |
|||
|
|||
QStringList QMachineState::debugStack() |
|||
{ |
|||
QStringList stack; |
|||
for (std::vector<u256>::reverse_iterator i = m_state.stack.rbegin(); i != m_state.stack.rend(); ++i) |
|||
stack.append(QString::fromStdString(prettyU256(*i))); |
|||
return stack; |
|||
} |
|||
|
|||
QStringList QMachineState::debugStorage() |
|||
{ |
|||
QStringList storage; |
|||
for (auto const& i: m_state.storage) |
|||
{ |
|||
std::stringstream s; |
|||
s << "@" << prettyU256(i.first) << "\t" << prettyU256(i.second); |
|||
storage.append(QString::fromStdString(s.str())); |
|||
} |
|||
return storage; |
|||
} |
|||
|
|||
QVariantList QMachineState::debugMemory() |
|||
{ |
|||
return memDumpToList(m_state.memory, 16, true); |
|||
} |
|||
|
|||
QCallData* QMachineState::getDebugCallData(QObject* _owner, bytes const& _data) |
|||
{ |
|||
return new QCallData(_owner, memDumpToList(_data, 16)); |
|||
} |
|||
|
|||
QVariantList QMachineState::levels() |
|||
{ |
|||
QVariantList levelList; |
|||
for (unsigned l: m_state.levels) |
|||
levelList.push_back(l); |
|||
return levelList; |
|||
} |
|||
|
|||
QString QMachineState::instruction() |
|||
{ |
|||
return QString::fromStdString(dev::eth::instructionInfo(m_state.inst).name); |
|||
} |
|||
|
|||
QString QMachineState::endOfDebug() |
|||
{ |
|||
if (m_state.gasCost > m_state.gas) |
|||
return QObject::tr("OUT-OF-GAS"); |
|||
else if (m_state.inst == Instruction::RETURN && m_state.stack.size() >= 2) |
|||
{ |
|||
unsigned from = (unsigned)m_state.stack.back(); |
|||
unsigned size = (unsigned)m_state.stack[m_state.stack.size() - 2]; |
|||
unsigned o = 0; |
|||
bytes out(size, 0); |
|||
for (; o < size && from + o < m_state.memory.size(); ++o) |
|||
out[o] = m_state.memory[from + o]; |
|||
return QObject::tr("RETURN") + " " + QString::fromStdString(dev::memDump(out, 16, false)); |
|||
} |
|||
else if (m_state.inst == Instruction::STOP) |
|||
return QObject::tr("STOP"); |
|||
else if (m_state.inst == Instruction::SUICIDE && m_state.stack.size() >= 1) |
|||
return QObject::tr("SUICIDE") + " 0x" + QString::fromStdString(toString(right160(m_state.stack.back()))); |
|||
else |
|||
return QObject::tr("EXCEPTION"); |
|||
} |
@ -1,204 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file DebuggingStateWrapper.h
|
|||
* @author Yann yann@ethdev.com |
|||
* @date 2014 |
|||
* Ethereum IDE client. |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
// Make sure boost/asio.hpp is included before windows.h.
|
|||
#include <boost/asio.hpp> |
|||
|
|||
#include <QStringList> |
|||
#include <QHash> |
|||
#include <libdevcore/Common.h> |
|||
#include <libethereum/State.h> |
|||
#include <libethereum/Executive.h> |
|||
#include "MachineStates.h" |
|||
#include "QVariableDefinition.h" |
|||
#include "QBigInt.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace mix |
|||
{ |
|||
|
|||
/**
|
|||
* @brief Contains the line nb of the assembly code and the corresponding index in the code bytes array. |
|||
*/ |
|||
class QInstruction: public QObject |
|||
{ |
|||
Q_OBJECT |
|||
Q_PROPERTY(QString line MEMBER m_line CONSTANT) |
|||
|
|||
public: |
|||
QInstruction(QObject* _owner, QString _line): QObject(_owner), m_line(_line) {} |
|||
|
|||
private: |
|||
QString m_line; |
|||
}; |
|||
|
|||
/**
|
|||
* @brief Solidity state |
|||
*/ |
|||
class QSolState: public QObject |
|||
{ |
|||
Q_OBJECT |
|||
Q_PROPERTY(QVariantMap storage MEMBER m_storage CONSTANT) |
|||
Q_PROPERTY(QVariantList callStack MEMBER m_callStack CONSTANT) |
|||
Q_PROPERTY(QVariantMap locals MEMBER m_locals CONSTANT) |
|||
Q_PROPERTY(int start MEMBER m_start CONSTANT) |
|||
Q_PROPERTY(int end MEMBER m_end CONSTANT) |
|||
Q_PROPERTY(QString sourceName MEMBER m_sourceName CONSTANT) |
|||
|
|||
public: |
|||
QSolState(QObject* _parent, QVariantMap&& _storage, QVariantList&& _callStack, QVariantMap&& _locals, int _start, int _end, QString _sourceName): |
|||
QObject(_parent), m_storage(std::move(_storage)), m_callStack(std::move(_callStack)), m_locals(std::move(_locals)), m_start(_start), m_end(_end), m_sourceName(_sourceName) |
|||
{ } |
|||
|
|||
private: |
|||
QVariantMap m_storage; |
|||
QVariantList m_callStack; |
|||
QVariantMap m_locals; |
|||
int m_start; |
|||
int m_end; |
|||
QString m_sourceName; |
|||
}; |
|||
|
|||
/**
|
|||
* @brief Shared container for lines |
|||
*/ |
|||
class QCode: public QObject |
|||
{ |
|||
Q_OBJECT |
|||
Q_PROPERTY(QVariantList instructions MEMBER m_instructions CONSTANT) |
|||
Q_PROPERTY(QString address MEMBER m_address CONSTANT) |
|||
Q_PROPERTY(QString documentId MEMBER m_document CONSTANT) |
|||
|
|||
public: |
|||
QCode(QObject* _owner, QString const& _address, QVariantList&& _instrunctions): QObject(_owner), m_instructions(std::move(_instrunctions)), m_address(_address) {} |
|||
void setDocument(QString const& _documentId) { m_document = _documentId; } |
|||
|
|||
private: |
|||
QVariantList m_instructions; |
|||
QString m_address; |
|||
QString m_document; |
|||
}; |
|||
|
|||
/**
|
|||
* @brief Shared container for call data |
|||
*/ |
|||
class QCallData: public QObject |
|||
{ |
|||
Q_OBJECT |
|||
Q_PROPERTY(QVariantList items MEMBER m_items CONSTANT) |
|||
|
|||
public: |
|||
QCallData(QObject* _owner, QVariantList&& _items): QObject(_owner), m_items(std::move(_items)) {} |
|||
|
|||
private: |
|||
QVariantList m_items; |
|||
}; |
|||
|
|||
/**
|
|||
* @brief Shared container for machine states |
|||
*/ |
|||
class QDebugData: public QObject |
|||
{ |
|||
Q_OBJECT |
|||
Q_PROPERTY(QVariantList states MEMBER m_states CONSTANT) |
|||
|
|||
public: |
|||
QDebugData() { } |
|||
void setStates(QVariantList&& _states) { m_states = std::move(_states); } |
|||
|
|||
private: |
|||
QVariantList m_states; |
|||
}; |
|||
|
|||
/**
|
|||
* @brief Wrap MachineState in QObject |
|||
*/ |
|||
class QMachineState: public QObject |
|||
{ |
|||
Q_OBJECT |
|||
Q_PROPERTY(int step READ step CONSTANT) |
|||
Q_PROPERTY(int curPC READ curPC CONSTANT) |
|||
Q_PROPERTY(int instructionIndex MEMBER m_instructionIndex CONSTANT) |
|||
Q_PROPERTY(QBigInt* gasCost READ gasCost CONSTANT) |
|||
Q_PROPERTY(QBigInt* gas READ gas CONSTANT) |
|||
Q_PROPERTY(QString instruction READ instruction CONSTANT) |
|||
Q_PROPERTY(QStringList debugStack READ debugStack CONSTANT) |
|||
Q_PROPERTY(QStringList debugStorage READ debugStorage CONSTANT) |
|||
Q_PROPERTY(QVariantList debugMemory READ debugMemory CONSTANT) |
|||
Q_PROPERTY(QObject* code MEMBER m_code CONSTANT) |
|||
Q_PROPERTY(QObject* callData MEMBER m_callData CONSTANT) |
|||
Q_PROPERTY(QString endOfDebug READ endOfDebug CONSTANT) |
|||
Q_PROPERTY(QBigInt* newMemSize READ newMemSize CONSTANT) |
|||
Q_PROPERTY(QVariantList levels READ levels CONSTANT) |
|||
Q_PROPERTY(unsigned codeIndex READ codeIndex CONSTANT) |
|||
Q_PROPERTY(unsigned dataIndex READ dataIndex CONSTANT) |
|||
Q_PROPERTY(QObject* solidity MEMBER m_solState CONSTANT) |
|||
|
|||
public: |
|||
QMachineState(QObject* _owner, int _instructionIndex, MachineState const& _state, QCode* _code, QCallData* _callData, QSolState* _solState): |
|||
QObject(_owner), m_instructionIndex(_instructionIndex), m_state(_state), m_code(_code), m_callData(_callData), m_solState(_solState) { } |
|||
/// Get the step of this machine states.
|
|||
int step() { return (int)m_state.steps; } |
|||
/// Get the proccessed code index.
|
|||
int curPC() { return (int)m_state.curPC; } |
|||
/// Get the code id
|
|||
unsigned codeIndex() { return m_state.codeIndex; } |
|||
/// Get the call data id
|
|||
unsigned dataIndex() { return m_state.dataIndex; } |
|||
/// Get gas cost.
|
|||
QBigInt* gasCost(); |
|||
/// Get gas used.
|
|||
QBigInt* gas(); |
|||
/// Get stack.
|
|||
QStringList debugStack(); |
|||
/// Get storage.
|
|||
QStringList debugStorage(); |
|||
/// Get memory.
|
|||
QVariantList debugMemory(); |
|||
/// Get end of debug information.
|
|||
QString endOfDebug(); |
|||
/// Get the new memory size.
|
|||
QBigInt* newMemSize(); |
|||
/// Get current instruction
|
|||
QString instruction(); |
|||
/// Get all previous steps.
|
|||
QVariantList levels(); |
|||
/// Get the current processed machine state.
|
|||
MachineState const& state() const { return m_state; } |
|||
/// Convert all machine states in human readable code.
|
|||
static QCode* getHumanReadableCode(QObject* _owner, const Address& _address, const bytes& _code, QHash<int, int>& o_codeMap); |
|||
/// Convert call data into human readable form
|
|||
static QCallData* getDebugCallData(QObject* _owner, bytes const& _data); |
|||
|
|||
private: |
|||
int m_instructionIndex; |
|||
MachineState m_state; |
|||
QCode* m_code; |
|||
QCallData* m_callData; |
|||
QSolState* m_solState; |
|||
}; |
|||
|
|||
} |
|||
} |
@ -1,31 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file Exceptions.cpp
|
|||
* @author Arkadiy Paronyan arkadiy@ethdev.com |
|||
* @date 2015 |
|||
* Ethereum IDE client. |
|||
*/ |
|||
|
|||
#include <ostream> |
|||
#include <QQmlError> |
|||
#include "Exceptions.h" |
|||
|
|||
std::ostream& operator<<(std::ostream& _out, QQmlError const& _error) |
|||
{ |
|||
_out << _error.toString().toStdString(); |
|||
return _out; |
|||
} |
@ -1,52 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file Exceptions.h
|
|||
* @author Arkadiy Paronyan arkadiy@ethdev.com |
|||
* @date 2015 |
|||
* Ethereum IDE client. |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <iosfwd> |
|||
#include <libdevcore/Exceptions.h> |
|||
|
|||
class QTextDocument; |
|||
class QQmlError; |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace mix |
|||
{ |
|||
|
|||
struct QmlLoadException: virtual Exception {}; |
|||
struct FileIoException: virtual Exception {}; |
|||
struct InvalidBlockException: virtual Exception {}; |
|||
struct FunctionNotFoundException: virtual Exception {}; |
|||
struct ExecutionStateException: virtual Exception {}; |
|||
struct ParameterChangedException: virtual Exception {}; |
|||
struct OutOfGasException: virtual Exception {}; |
|||
|
|||
using QmlErrorInfo = boost::error_info<struct tagQmlError, QQmlError>; |
|||
using FileError = boost::error_info<struct tagFileError, std::string>; |
|||
using BlockIndex = boost::error_info<struct tagBlockIndex, unsigned>; |
|||
using FunctionName = boost::error_info<struct tagFunctionName, std::string>; |
|||
|
|||
} |
|||
} |
|||
|
|||
std::ostream& operator<<(std::ostream& _out, QQmlError const& _error); |
@ -1,234 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file FileIo.cpp
|
|||
* @author Arkadiy Paronyan arkadiy@ethdev.com |
|||
* @date 2015 |
|||
* Ethereum IDE client. |
|||
*/ |
|||
|
|||
#include <QFileSystemWatcher> |
|||
#include <QDebug> |
|||
#include <QDesktopServices> |
|||
#include <QMimeDatabase> |
|||
#include <QDirIterator> |
|||
#include <QDir> |
|||
#include <QFile> |
|||
#include <QFileInfo> |
|||
#include <QTextStream> |
|||
#include <QUrl> |
|||
#include <json/json.h> |
|||
#include <libdevcrypto/CryptoPP.h> |
|||
#include <libdevcrypto/Common.h> |
|||
#include <libdevcore/RLP.h> |
|||
#include <libdevcore/SHA3.h> |
|||
#include "FileIo.h" |
|||
|
|||
using namespace dev; |
|||
using namespace dev::crypto; |
|||
using namespace dev::mix; |
|||
|
|||
FileIo::FileIo(): m_watcher(new QFileSystemWatcher(this)) |
|||
{ |
|||
connect(m_watcher, &QFileSystemWatcher::fileChanged, this, &FileIo::fileChanged); |
|||
} |
|||
|
|||
void FileIo::openFileBrowser(QString const& _dir) |
|||
{ |
|||
QDesktopServices::openUrl(QUrl(_dir)); |
|||
} |
|||
|
|||
QString FileIo::pathFromUrl(QString const& _url) |
|||
{ |
|||
QUrl url(_url); |
|||
QString path(url.path()); |
|||
if (url.scheme() == "qrc") |
|||
path = ":" + path; |
|||
#ifdef WIN32 |
|||
if (url.scheme() == "file") |
|||
{ |
|||
if (path.startsWith("/")) |
|||
path = path.right(path.length() - 1); |
|||
if (!url.host().isEmpty()) |
|||
path = url.host() + ":/" + path; |
|||
} |
|||
#endif |
|||
return path; |
|||
} |
|||
|
|||
void FileIo::makeDir(QString const& _url) |
|||
{ |
|||
QDir dirPath(pathFromUrl(_url)); |
|||
if (dirPath.exists()) |
|||
dirPath.removeRecursively(); |
|||
dirPath.mkpath(dirPath.path()); |
|||
} |
|||
|
|||
void FileIo::deleteDir(QString const& _url) |
|||
{ |
|||
QDir dirPath(pathFromUrl(_url)); |
|||
if (dirPath.exists()) |
|||
dirPath.removeRecursively(); |
|||
} |
|||
|
|||
QString FileIo::readFile(QString const& _url) |
|||
{ |
|||
QFile file(pathFromUrl(_url)); |
|||
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) |
|||
{ |
|||
QTextStream stream(&file); |
|||
QString data = stream.readAll(); |
|||
return data; |
|||
} |
|||
else |
|||
error(tr("Error reading file %1").arg(_url)); |
|||
return QString(); |
|||
} |
|||
|
|||
void FileIo::writeFile(QString const& _url, QString const& _data) |
|||
{ |
|||
QString path = pathFromUrl(_url); |
|||
m_watcher->removePath(path); |
|||
QFile file(path); |
|||
if (file.open(QIODevice::WriteOnly | QIODevice::Text)) |
|||
{ |
|||
QTextStream stream(&file); |
|||
stream << _data; |
|||
} |
|||
else |
|||
error(tr("Error writing file %1").arg(_url)); |
|||
file.close(); |
|||
m_watcher->addPath(path); |
|||
} |
|||
|
|||
void FileIo::copyFile(QString const& _sourceUrl, QString const& _destUrl) |
|||
{ |
|||
if (QUrl(_sourceUrl).scheme() == "qrc") |
|||
{ |
|||
writeFile(_destUrl, readFile(_sourceUrl)); |
|||
return; |
|||
} |
|||
|
|||
if (!QFile::copy(pathFromUrl(_sourceUrl), pathFromUrl(_destUrl))) |
|||
error(tr("Error copying file %1 to %2").arg(_sourceUrl).arg(_destUrl)); |
|||
} |
|||
|
|||
QString FileIo::getHomePath() const |
|||
{ |
|||
return QDir::homePath(); |
|||
} |
|||
|
|||
void FileIo::moveFile(QString const& _sourceUrl, QString const& _destUrl) |
|||
{ |
|||
if (!QFile::rename(pathFromUrl(_sourceUrl), pathFromUrl(_destUrl))) |
|||
error(tr("Error moving file %1 to %2").arg(_sourceUrl).arg(_destUrl)); |
|||
} |
|||
|
|||
bool FileIo::fileExists(QString const& _url) |
|||
{ |
|||
QFile file(pathFromUrl(_url)); |
|||
return file.exists(); |
|||
} |
|||
|
|||
QStringList FileIo::makePackage(QString const& _deploymentFolder) |
|||
{ |
|||
Json::Value manifest; |
|||
Json::Value entries(Json::arrayValue); |
|||
|
|||
QDir deployDir = QDir(pathFromUrl(_deploymentFolder)); |
|||
dev::RLPStream rlpStr; |
|||
int k = 1; |
|||
std::vector<bytes> files; |
|||
QMimeDatabase mimeDb; |
|||
for (auto item: deployDir.entryInfoList(QDir::Files)) |
|||
{ |
|||
QFile qFile(item.filePath()); |
|||
if (qFile.open(QIODevice::ReadOnly)) |
|||
{ |
|||
k++; |
|||
QFileInfo fileInfo = QFileInfo(qFile.fileName()); |
|||
Json::Value jsonValue; |
|||
std::string path = fileInfo.fileName() == "index.html" ? "/" : fileInfo.fileName().toStdString(); |
|||
jsonValue["path"] = path; //TODO: Manage relative sub folder
|
|||
jsonValue["file"] = "/" + fileInfo.fileName().toStdString(); |
|||
jsonValue["contentType"] = mimeDb.mimeTypeForFile(qFile.fileName()).name().toStdString(); |
|||
QByteArray a = qFile.readAll(); |
|||
bytes data = bytes(a.begin(), a.end()); |
|||
files.push_back(data); |
|||
jsonValue["hash"] = toHex(dev::sha3(data).ref()); |
|||
entries.append(jsonValue); |
|||
} |
|||
qFile.close(); |
|||
} |
|||
rlpStr.appendList(k); |
|||
|
|||
manifest["entries"] = entries; |
|||
std::stringstream jsonStr; |
|||
jsonStr << manifest; |
|||
QByteArray b = QString::fromStdString(jsonStr.str()).toUtf8(); |
|||
rlpStr.append(bytesConstRef((const unsigned char*)b.data(), b.size())); |
|||
|
|||
for (unsigned int k = 0; k < files.size(); k++) |
|||
rlpStr.append(files.at(k)); |
|||
|
|||
bytes dapp = rlpStr.out(); |
|||
dev::h256 dappHash = dev::sha3(dapp); |
|||
//encrypt
|
|||
KeyPair key((Secret(dappHash))); |
|||
Secp256k1PP enc; |
|||
enc.encrypt(key.pub(), dapp); |
|||
|
|||
QUrl url(_deploymentFolder + "package.dapp"); |
|||
QFile compressed(url.path()); |
|||
QByteArray qFileBytes((char*)dapp.data(), static_cast<int>(dapp.size())); |
|||
if (compressed.open(QIODevice::WriteOnly | QIODevice::Truncate)) |
|||
{ |
|||
compressed.write(qFileBytes); |
|||
compressed.flush(); |
|||
} |
|||
else |
|||
error(tr("Error creating package.dapp")); |
|||
compressed.close(); |
|||
QStringList ret; |
|||
ret.append(QString::fromStdString(toHex(dappHash.ref()))); |
|||
ret.append(qFileBytes.toBase64()); |
|||
ret.append(url.toString()); |
|||
return ret; |
|||
} |
|||
|
|||
void FileIo::watchFileChanged(QString const& _path) |
|||
{ |
|||
m_watcher->addPath(pathFromUrl(_path)); |
|||
} |
|||
|
|||
void FileIo::stopWatching(QString const& _path) |
|||
{ |
|||
m_watcher->removePath(pathFromUrl(_path)); |
|||
} |
|||
|
|||
void FileIo::deleteFile(QString const& _path) |
|||
{ |
|||
QFile file(pathFromUrl(_path)); |
|||
file.remove(); |
|||
} |
|||
|
|||
QUrl FileIo::pathFolder(QString const& _path) |
|||
{ |
|||
QFileInfo info(_path); |
|||
if (info.exists() && info.isDir()) |
|||
return QUrl::fromLocalFile(_path); |
|||
return QUrl::fromLocalFile(QFileInfo(_path).absolutePath()); |
|||
} |
@ -1,86 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file FileIo.h
|
|||
* @author Arkadiy Paronyan arkadiy@ethdev.com |
|||
* @date 2015 |
|||
* Ethereum IDE client. |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <libdevcore/CommonData.h> |
|||
#include <QObject> |
|||
|
|||
class QFileSystemWatcher; |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace mix |
|||
{ |
|||
|
|||
///File services for QML
|
|||
class FileIo: public QObject |
|||
{ |
|||
Q_OBJECT |
|||
Q_PROPERTY(QString homePath READ getHomePath CONSTANT) |
|||
|
|||
signals: |
|||
/// Signalled in case of IO error
|
|||
void error(QString const& _errorText); |
|||
/// Signnalled when a file is changed.
|
|||
void fileChanged(QString const& _filePath); |
|||
|
|||
public: |
|||
FileIo(); |
|||
/// Create a directory if it does not exist. Signals on failure.
|
|||
Q_INVOKABLE void makeDir(QString const& _url); |
|||
/// Read file contents to a string. Signals on failure.
|
|||
Q_INVOKABLE QString readFile(QString const& _url); |
|||
/// Write contents to a file. Signals on failure.
|
|||
Q_INVOKABLE void writeFile(QString const& _url, QString const& _data); |
|||
/// Copy a file from _sourcePath to _destPath. Signals on failure.
|
|||
Q_INVOKABLE void copyFile(QString const& _sourceUrl, QString const& _destUrl); |
|||
/// Move (rename) a file from _sourcePath to _destPath. Signals on failure.
|
|||
Q_INVOKABLE void moveFile(QString const& _sourceUrl, QString const& _destUrl); |
|||
/// Check if file exists
|
|||
Q_INVOKABLE bool fileExists(QString const& _url); |
|||
/// Compress a folder, @returns sha3 of the compressed file.
|
|||
Q_INVOKABLE QStringList makePackage(QString const& _deploymentFolder); |
|||
/// Open a file browser.
|
|||
Q_INVOKABLE void openFileBrowser(QString const& _dir); |
|||
/// Listen for files change in @arg _path.
|
|||
Q_INVOKABLE void watchFileChanged(QString const& _path); |
|||
/// Stop Listenning for files change in @arg _path.
|
|||
Q_INVOKABLE void stopWatching(QString const& _path); |
|||
/// Delete a file
|
|||
Q_INVOKABLE void deleteFile(QString const& _path); |
|||
/// delete a directory
|
|||
Q_INVOKABLE void deleteDir(QString const& _url); |
|||
|
|||
//TODO: remove once qt 5.5.1 is out
|
|||
Q_INVOKABLE QString urlToPath(QUrl const& _url) { return _url.toLocalFile(); } |
|||
Q_INVOKABLE QUrl pathToUrl(QString const& _path) { return QUrl::fromLocalFile(_path); } |
|||
Q_INVOKABLE QUrl pathFolder(QString const& _path); |
|||
|
|||
private: |
|||
QString getHomePath() const; |
|||
QString pathFromUrl(QString const& _url); |
|||
QFileSystemWatcher* m_watcher; |
|||
}; |
|||
|
|||
} |
|||
} |
@ -1,192 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file HttpServer.cpp
|
|||
* @author Arkadiy Paronyan arkadiy@ethdev.com |
|||
* @date 2015 |
|||
* Ethereum IDE client. |
|||
*/ |
|||
|
|||
#include <memory> |
|||
#include <QTcpSocket> |
|||
#include "HttpServer.h" |
|||
|
|||
|
|||
using namespace dev::mix; |
|||
|
|||
HttpServer::HttpServer() |
|||
: m_port(0) , m_listen(false) , m_accept(true) , m_componentCompleted(true) |
|||
{ |
|||
} |
|||
|
|||
HttpServer::~HttpServer() |
|||
{ |
|||
setListen(false); |
|||
} |
|||
|
|||
void HttpServer::classBegin() |
|||
{ |
|||
m_componentCompleted = false; |
|||
} |
|||
|
|||
void HttpServer::componentComplete() |
|||
{ |
|||
init(); |
|||
m_componentCompleted = true; |
|||
} |
|||
|
|||
QUrl HttpServer::url() const |
|||
{ |
|||
QUrl url; |
|||
url.setPort(m_port); |
|||
url.setHost("localhost"); |
|||
url.setScheme("http"); |
|||
return url; |
|||
} |
|||
|
|||
void HttpServer::setPort(int _port) |
|||
{ |
|||
if (_port == m_port) |
|||
return; |
|||
|
|||
m_port = _port; |
|||
emit portChanged(_port); |
|||
emit urlChanged(url()); |
|||
|
|||
if (m_componentCompleted && this->isListening()) |
|||
updateListening(); |
|||
} |
|||
|
|||
QString HttpServer::errorString() const |
|||
{ |
|||
return QTcpServer::errorString(); |
|||
} |
|||
|
|||
void HttpServer::setListen(bool _listen) |
|||
{ |
|||
if (_listen == m_listen) |
|||
return; |
|||
|
|||
m_listen = _listen; |
|||
emit listenChanged(_listen); |
|||
|
|||
if (m_componentCompleted) |
|||
updateListening(); |
|||
} |
|||
|
|||
void HttpServer::setAccept(bool _accept) |
|||
{ |
|||
if (_accept == m_accept) |
|||
return; |
|||
|
|||
m_accept = _accept; |
|||
emit acceptChanged(_accept); |
|||
} |
|||
|
|||
void HttpServer::init() |
|||
{ |
|||
updateListening(); |
|||
} |
|||
|
|||
void HttpServer::updateListening() |
|||
{ |
|||
if (this->isListening()) |
|||
this->close(); |
|||
|
|||
if (!m_listen) |
|||
return; |
|||
|
|||
if (!QTcpServer::listen(QHostAddress::LocalHost, m_port)) |
|||
{ |
|||
errorStringChanged(); |
|||
return; |
|||
} |
|||
|
|||
if (m_port != QTcpServer::serverPort()) |
|||
{ |
|||
m_port = QTcpServer::serverPort(); |
|||
emit portChanged(m_port); |
|||
emit urlChanged(url()); |
|||
} |
|||
} |
|||
|
|||
void HttpServer::incomingConnection(qintptr _socket) |
|||
{ |
|||
if (!m_accept) |
|||
return; |
|||
|
|||
QTcpSocket* s = new QTcpSocket(this); |
|||
connect(s, SIGNAL(readyRead()), this, SLOT(readClient())); |
|||
connect(s, SIGNAL(disconnected()), this, SLOT(discardClient())); |
|||
s->setSocketDescriptor(_socket); |
|||
} |
|||
|
|||
void HttpServer::readClient() |
|||
{ |
|||
if (!m_accept) |
|||
return; |
|||
|
|||
QTcpSocket* socket = (QTcpSocket*)sender(); |
|||
try |
|||
{ |
|||
if (socket->canReadLine()) |
|||
{ |
|||
QString hdr = QString(socket->readLine()); |
|||
QVariantMap headers; |
|||
if (hdr.startsWith("POST") || hdr.startsWith("GET")) |
|||
{ |
|||
QUrl url(hdr.split(' ')[1]); |
|||
QString l; |
|||
do |
|||
{ |
|||
l = socket->readLine(); |
|||
//collect headers
|
|||
int colon = l.indexOf(':'); |
|||
if (colon > 0) |
|||
headers[l.left(colon).trimmed().toLower()] = l.right(l.length() - colon - 1).trimmed(); |
|||
} |
|||
while (!(l.isEmpty() || l == "\r" || l == "\r\n")); |
|||
|
|||
QString content = socket->readAll(); |
|||
std::unique_ptr<HttpRequest> request(new HttpRequest(this, std::move(url), std::move(content), std::move(headers))); |
|||
clientConnected(request.get()); |
|||
QTextStream os(socket); |
|||
os.setAutoDetectUnicode(true); |
|||
QString q; |
|||
///@todo: allow setting response content-type, charset, etc
|
|||
os << "HTTP/1.0 200 Ok\r\n"; |
|||
if (!request->m_responseContentType.isEmpty()) |
|||
os << "Content-Type: " << request->m_responseContentType << "; "; |
|||
os << "charset=\"utf-8\"\r\n\r\n"; |
|||
os << request->m_response; |
|||
} |
|||
} |
|||
} |
|||
catch(...) |
|||
{ |
|||
delete socket; |
|||
throw; |
|||
} |
|||
socket->close(); |
|||
if (socket->state() == QTcpSocket::UnconnectedState) |
|||
delete socket; |
|||
} |
|||
|
|||
void HttpServer::discardClient() |
|||
{ |
|||
QTcpSocket* socket = (QTcpSocket*)sender(); |
|||
socket->deleteLater(); |
|||
} |
@ -1,131 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file HttpServer.h
|
|||
* @author Arkadiy Paronyan arkadiy@ethdev.com |
|||
* @date 2015 |
|||
* Ethereum IDE client. |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <QObject> |
|||
#include <QTcpServer> |
|||
#include <QUrl> |
|||
#include <QVariantMap> |
|||
#include <QQmlParserStatus> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace mix |
|||
{ |
|||
|
|||
/// Simple http server for serving jsonrpc requests
|
|||
class HttpRequest: public QObject |
|||
{ |
|||
Q_OBJECT |
|||
/// Request url
|
|||
Q_PROPERTY(QUrl url MEMBER m_url CONSTANT) |
|||
/// Request body contents
|
|||
Q_PROPERTY(QString content MEMBER m_content CONSTANT) |
|||
/// Request HTTP headers
|
|||
Q_PROPERTY(QVariantMap headers MEMBER m_headers CONSTANT) |
|||
|
|||
private: |
|||
HttpRequest(QObject* _parent, QUrl&& _url, QString&& _content, QVariantMap&& _headers): |
|||
QObject(_parent), m_url(std::move(_url)), m_content(std::move(_content)), m_headers(std::move(_headers)) |
|||
{ |
|||
} |
|||
|
|||
public: |
|||
/// Set response for a request
|
|||
/// @param _response Response body. If no response is set, server returns status 200 with empty body
|
|||
Q_INVOKABLE void setResponse(QString const& _response) { m_response = _response; } |
|||
/// Set response content type
|
|||
/// @param _contentType Response content type string. text/plain by default
|
|||
Q_INVOKABLE void setResponseContentType(QString const& _contentType) { m_responseContentType = _contentType ; } |
|||
|
|||
private: |
|||
QUrl m_url; |
|||
QString m_content; |
|||
QString m_response; |
|||
QString m_responseContentType; |
|||
QVariantMap m_headers; |
|||
friend class HttpServer; |
|||
}; |
|||
|
|||
class HttpServer: public QTcpServer, public QQmlParserStatus |
|||
{ |
|||
Q_OBJECT |
|||
Q_DISABLE_COPY(HttpServer) |
|||
Q_INTERFACES(QQmlParserStatus) |
|||
|
|||
/// Server url
|
|||
Q_PROPERTY(QUrl url READ url NOTIFY urlChanged) |
|||
/// Server port
|
|||
Q_PROPERTY(int port READ port WRITE setPort NOTIFY portChanged) |
|||
/// Listen for connections
|
|||
Q_PROPERTY(bool listen READ listen WRITE setListen NOTIFY listenChanged) |
|||
/// Accept new connections
|
|||
Q_PROPERTY(bool accept READ accept WRITE setAccept NOTIFY acceptChanged) |
|||
/// Error string if any
|
|||
Q_PROPERTY(QString errorString READ errorString NOTIFY errorStringChanged) |
|||
|
|||
public: |
|||
explicit HttpServer(); |
|||
virtual ~HttpServer(); |
|||
|
|||
QUrl url() const; |
|||
int port() const { return m_port; } |
|||
void setPort(int _port); |
|||
bool listen() const { return m_listen; } |
|||
void setListen(bool _listen); |
|||
bool accept() const { return m_accept; } |
|||
void setAccept(bool _accept); |
|||
QString errorString() const; |
|||
|
|||
protected: |
|||
virtual void classBegin() override; |
|||
virtual void componentComplete() override; |
|||
virtual void incomingConnection(qintptr _socket) override; |
|||
|
|||
signals: |
|||
void clientConnected(HttpRequest* _request); |
|||
void errorStringChanged(); |
|||
void urlChanged(QUrl const& _url); |
|||
void portChanged(int _port); |
|||
void listenChanged(bool _listen); |
|||
void acceptChanged(bool _accept); |
|||
|
|||
private: |
|||
void init(); |
|||
void updateListening(); |
|||
void newConnection(); |
|||
void serverError(); |
|||
|
|||
private slots: |
|||
void readClient(); |
|||
void discardClient(); |
|||
|
|||
private: |
|||
int m_port; |
|||
bool m_listen; |
|||
bool m_accept; |
|||
bool m_componentCompleted; |
|||
}; |
|||
|
|||
} |
|||
} |
@ -1,59 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file InverseMouseArea.cpp
|
|||
* @author Yann yann@ethdev.com |
|||
* @date 2015 |
|||
* Ethereum IDE client. |
|||
*/ |
|||
|
|||
#include <QQuickWindow> |
|||
#include <QDebug> |
|||
#include <QQuickItem> |
|||
#include <QGraphicsSceneMouseEvent> |
|||
#include "InverseMouseArea.h" |
|||
|
|||
using namespace dev::mix; |
|||
|
|||
void InverseMouseArea::itemChange(ItemChange _c, const ItemChangeData& _v) |
|||
{ |
|||
Q_UNUSED(_v); |
|||
if (this->m_active && _c == ItemSceneChange && window()) |
|||
window()->installEventFilter(this); |
|||
} |
|||
|
|||
bool InverseMouseArea::eventFilter(QObject* _obj, QEvent* _ev) |
|||
{ |
|||
Q_UNUSED(_obj); |
|||
if (this->m_active && _ev->type() == QEvent::MouseButtonPress && !this->contains(static_cast<QMouseEvent*>(_ev)->pos())) |
|||
emit clickedOutside(QPointF(static_cast<QMouseEvent*>(_ev)->pos())); |
|||
return false; |
|||
} |
|||
|
|||
bool InverseMouseArea::contains(const QPointF& _point) const |
|||
{ |
|||
if (!this->m_active) |
|||
return false; |
|||
QPointF global = this->parentItem()->mapToItem(0, QPointF(0, 0)); |
|||
return QRectF(global.x(), global.y(), this->parentItem()->width(), this->parentItem()->height()).contains(_point); |
|||
} |
|||
|
|||
void InverseMouseArea::setActive(bool _v) |
|||
{ |
|||
m_active = _v; |
|||
if (m_active && window()) |
|||
window()->installEventFilter(this); |
|||
} |
@ -1,57 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file InverseMouseArea.h
|
|||
* @author Yann yann@ethdev.com |
|||
* @date 2015 |
|||
* Ethereum IDE client. |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <QQuickWindow> |
|||
#include <QQuickItem> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace mix |
|||
{ |
|||
|
|||
class InverseMouseArea: public QQuickItem |
|||
{ |
|||
Q_OBJECT |
|||
Q_PROPERTY(bool active MEMBER m_active WRITE setActive) |
|||
|
|||
public: |
|||
InverseMouseArea(QQuickItem* _parent = 0): QQuickItem(_parent) {} |
|||
~InverseMouseArea() { if (window()) { window()->removeEventFilter(this); } } |
|||
void setActive(bool _v); |
|||
|
|||
protected: |
|||
void itemChange(ItemChange _c, const ItemChangeData& _v) override; |
|||
bool eventFilter(QObject* _obj, QEvent *_ev) override; |
|||
bool contains(const QPointF& _point) const override; |
|||
|
|||
private: |
|||
bool m_active; |
|||
|
|||
signals: |
|||
void clickedOutside(QPointF _point); |
|||
}; |
|||
|
|||
} |
|||
} |
|||
|
@ -1,96 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file MixClient.h
|
|||
* @author Yann yann@ethdev.com |
|||
* @author Arkadiy Paronyan arkadiy@ethdev.com |
|||
* @date 2015 |
|||
* Ethereum IDE client. |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <vector> |
|||
#include <map> |
|||
#include <stdint.h> |
|||
#include <libdevcore/Common.h> |
|||
#include <libdevcrypto/Common.h> |
|||
#include <libevmcore/Instruction.h> |
|||
#include <libethereum/Transaction.h> |
|||
#include <libethereum/TransactionReceipt.h> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace mix |
|||
{ |
|||
|
|||
/**
|
|||
* @brief Store information about a machine state. |
|||
*/ |
|||
struct MachineState |
|||
{ |
|||
uint64_t steps; |
|||
dev::u256 curPC; |
|||
dev::eth::Instruction inst; |
|||
dev::bigint newMemSize; |
|||
dev::u256 gas; |
|||
dev::u256s stack; |
|||
dev::bytes memory; |
|||
dev::bigint gasCost; |
|||
std::unordered_map<dev::u256, dev::u256> storage; |
|||
std::vector<unsigned> levels; |
|||
unsigned codeIndex; |
|||
unsigned dataIndex; |
|||
}; |
|||
|
|||
/**
|
|||
* @brief Executed conract code info |
|||
*/ |
|||
struct MachineCode |
|||
{ |
|||
dev::Address address; |
|||
bytes code; |
|||
}; |
|||
|
|||
/**
|
|||
* @brief Store information about a machine states. |
|||
*/ |
|||
struct ExecutionResult |
|||
{ |
|||
ExecutionResult(): transactionIndex(std::numeric_limits<unsigned>::max()) {} |
|||
|
|||
std::vector<MachineState> machineStates; |
|||
std::vector<bytes> transactionData; |
|||
std::vector<MachineCode> executionCode; |
|||
dev::eth::ExecutionResult result; |
|||
dev::Address address; |
|||
dev::Address sender; |
|||
dev::Address contractAddress; |
|||
dev::u256 value; |
|||
dev::u256 gasUsed; |
|||
unsigned transactionIndex; |
|||
unsigned executonIndex = 0; |
|||
bytes inputParameters; |
|||
eth::LocalisedLogEntries logs; |
|||
eth::TransactionException excepted = eth::TransactionException::Unknown; |
|||
|
|||
bool isCall() const { return transactionIndex == std::numeric_limits<unsigned>::max(); } |
|||
bool isConstructor() const { return !isCall() && !address; } |
|||
}; |
|||
|
|||
using ExecutionResults = std::vector<ExecutionResult>; |
|||
} |
|||
} |
@ -1,116 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file MixApplication.cpp
|
|||
* @author Yann yann@ethdev.com |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#include "MixApplication.h" |
|||
#include <boost/exception/diagnostic_information.hpp> |
|||
#include <QQmlApplicationEngine> |
|||
#include <QUrl> |
|||
#include <QIcon> |
|||
#include <QFont> |
|||
#ifdef ETH_HAVE_WEBENGINE |
|||
#include <QtWebEngine/QtWebEngine> |
|||
#endif |
|||
#include "CodeModel.h" |
|||
#include "ClientModel.h" |
|||
#include "FileIo.h" |
|||
#include "QEther.h" |
|||
#include "QVariableDeclaration.h" |
|||
#include "SortFilterProxyModel.h" |
|||
#include "Clipboard.h" |
|||
#include "HttpServer.h" |
|||
#include "InverseMouseArea.h" |
|||
|
|||
extern int qInitResources_js(); |
|||
using namespace dev::mix; |
|||
|
|||
|
|||
|
|||
|
|||
ApplicationService::ApplicationService() |
|||
{ |
|||
#ifdef ETH_HAVE_WEBENGINE |
|||
QtWebEngine::initialize(); |
|||
#endif |
|||
QFont f; |
|||
m_systemPointSize = f.pointSize(); |
|||
} |
|||
|
|||
MixApplication::MixApplication(int& _argc, char* _argv[]): |
|||
QApplication(_argc, _argv), m_engine(new QQmlApplicationEngine()) |
|||
{ |
|||
setWindowIcon(QIcon(":/res/mix_256x256x32.png")); |
|||
m_engine->load(QUrl("qrc:/qml/Application.qml")); |
|||
} |
|||
|
|||
void MixApplication::initialize() |
|||
{ |
|||
#if __linux |
|||
//work around ubuntu appmenu-qt5 bug
|
|||
//https://bugs.launchpad.net/ubuntu/+source/appmenu-qt5/+bug/1323853
|
|||
putenv((char*)"QT_QPA_PLATFORMTHEME="); |
|||
putenv((char*)"QSG_RENDER_LOOP=threaded"); |
|||
#endif |
|||
#if (defined(_WIN32) || defined(_WIN64)) |
|||
if (!getenv("OPENSSL_CONF")) |
|||
putenv((char*)"OPENSSL_CONF=c:\\"); |
|||
#endif |
|||
#ifdef ETH_HAVE_WEBENGINE |
|||
qInitResources_js(); |
|||
#endif |
|||
|
|||
setOrganizationName(tr("Ethereum")); |
|||
setOrganizationDomain(tr("ethereum.org")); |
|||
setApplicationName(tr("Mix")); |
|||
setApplicationVersion("0.1"); |
|||
|
|||
qmlRegisterType<CodeModel>("org.ethereum.qml.CodeModel", 1, 0, "CodeModel"); |
|||
qmlRegisterType<ClientModel>("org.ethereum.qml.ClientModel", 1, 0, "ClientModel"); |
|||
qmlRegisterType<ApplicationService>("org.ethereum.qml.ApplicationService", 1, 0, "ApplicationService"); |
|||
qmlRegisterType<FileIo>("org.ethereum.qml.FileIo", 1, 0, "FileIo"); |
|||
qmlRegisterType<QEther>("org.ethereum.qml.QEther", 1, 0, "QEther"); |
|||
qmlRegisterType<QBigInt>("org.ethereum.qml.QBigInt", 1, 0, "QBigInt"); |
|||
qmlRegisterType<QVariableDeclaration>("org.ethereum.qml.QVariableDeclaration", 1, 0, "QVariableDeclaration"); |
|||
qmlRegisterType<RecordLogEntry>("org.ethereum.qml.RecordLogEntry", 1, 0, "RecordLogEntry"); |
|||
qmlRegisterType<SortFilterProxyModel>("org.ethereum.qml.SortFilterProxyModel", 1, 0, "SortFilterProxyModel"); |
|||
qmlRegisterType<QSolidityType>("org.ethereum.qml.QSolidityType", 1, 0, "QSolidityType"); |
|||
qmlRegisterType<Clipboard>("org.ethereum.qml.Clipboard", 1, 0, "Clipboard"); |
|||
qmlRegisterType<HttpServer>("HttpServer", 1, 0, "HttpServer"); |
|||
qmlRegisterType<InverseMouseArea>("org.ethereum.qml.InverseMouseArea", 1, 0, "InverseMouseArea"); |
|||
qRegisterMetaType<CodeModel*>("CodeModel*"); |
|||
qRegisterMetaType<ClientModel*>("ClientModel*"); |
|||
} |
|||
|
|||
MixApplication::~MixApplication() |
|||
{ |
|||
} |
|||
|
|||
bool MixApplication::notify(QObject * receiver, QEvent * event) |
|||
{ |
|||
try |
|||
{ |
|||
return QApplication::notify(receiver, event); |
|||
} |
|||
catch (...) |
|||
{ |
|||
std::cerr << boost::current_exception_diagnostic_information(); |
|||
} |
|||
return false; |
|||
} |
@ -1,72 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file MixApplication.h
|
|||
* @author Yann yann@ethdev.com |
|||
* @date 2014 |
|||
* This class will be use instead of QApplication to launch the application. the method 'notify' allows to catch all exceptions. |
|||
* Not use for now: TODO. |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <memory> |
|||
#include <QApplication> |
|||
|
|||
class QQmlApplicationEngine; |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace mix |
|||
{ |
|||
|
|||
class ApplicationService: public QObject |
|||
{ |
|||
Q_OBJECT |
|||
Q_PROPERTY(int systemPointSize READ systemPointSize CONSTANT) |
|||
Q_PROPERTY(bool haveWebEngine READ haveWebEngine CONSTANT) |
|||
|
|||
public: |
|||
ApplicationService(); |
|||
int systemPointSize() const { return m_systemPointSize; } |
|||
#ifdef ETH_HAVE_WEBENGINE |
|||
bool haveWebEngine() const { return true; } |
|||
#else |
|||
bool haveWebEngine() const { return false; } |
|||
#endif |
|||
|
|||
private: |
|||
int m_systemPointSize = 0; |
|||
}; |
|||
|
|||
|
|||
class MixApplication: public QApplication |
|||
{ |
|||
Q_OBJECT |
|||
|
|||
public: |
|||
MixApplication(int& _argc, char* _argv[]); |
|||
static void initialize(); |
|||
virtual ~MixApplication(); |
|||
QQmlApplicationEngine* engine() { return m_engine.get(); } |
|||
bool notify(QObject* _receiver, QEvent* _event) override; |
|||
|
|||
private: |
|||
std::unique_ptr<QQmlApplicationEngine> m_engine; |
|||
}; |
|||
|
|||
} |
|||
} |
@ -1,384 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file MixClient.cpp
|
|||
* @author Arkadiy Paronyan arkadiy@ethdev.com |
|||
* @date 2015 |
|||
* Ethereum IDE client. |
|||
*/ |
|||
|
|||
#include "MixClient.h" |
|||
#include <vector> |
|||
#include <utility> |
|||
#include <libdevcore/Exceptions.h> |
|||
#include <libethcore/Params.h> |
|||
#include <libethcore/BasicAuthority.h> |
|||
#include <libethereum/CanonBlockChain.h> |
|||
#include <libethereum/Transaction.h> |
|||
#include <libethereum/Executive.h> |
|||
#include <libethereum/ExtVM.h> |
|||
#include <libethereum/BlockChain.h> |
|||
#include <libevm/VM.h> |
|||
#include "Exceptions.h" |
|||
using namespace std; |
|||
using namespace dev; |
|||
using namespace dev::eth; |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace mix |
|||
{ |
|||
|
|||
u256 const c_mixGenesisDifficulty = 131072; //TODO: make it lower for Mix somehow
|
|||
|
|||
namespace |
|||
{ |
|||
} |
|||
|
|||
MixBlockChain::MixBlockChain(std::string const& _path, h256 _stateRoot): |
|||
FullBlockChain<NoProof>(createGenesisBlock(_stateRoot), std::unordered_map<Address, Account>(), _path, WithExisting::Kill) |
|||
{ |
|||
} |
|||
|
|||
bytes MixBlockChain::createGenesisBlock(h256 _stateRoot) |
|||
{ |
|||
RLPStream block(3); |
|||
block.appendList(13) |
|||
<< h256() << EmptyListSHA3 << h160() << _stateRoot << EmptyTrie << EmptyTrie |
|||
<< LogBloom() << c_mixGenesisDifficulty << 0 << 3141592 << 0 << (unsigned)0 |
|||
<< std::string(); |
|||
block.appendRaw(RLPEmptyList); |
|||
block.appendRaw(RLPEmptyList); |
|||
return block.out(); |
|||
} |
|||
|
|||
MixClient::MixClient(std::string const& _dbPath): |
|||
m_dbPath(_dbPath) |
|||
{ |
|||
resetState(std::unordered_map<Address, Account>()); |
|||
} |
|||
|
|||
MixClient::~MixClient() |
|||
{ |
|||
} |
|||
|
|||
void MixClient::resetState(std::unordered_map<Address, Account> const& _accounts, Secret const& _miner) |
|||
{ |
|||
WriteGuard l(x_state); |
|||
Guard fl(x_filtersWatches); |
|||
|
|||
m_filters.clear(); |
|||
for (auto& i: m_specialFilters) |
|||
i.second.clear(); |
|||
m_watches.clear(); |
|||
|
|||
m_stateDB = OverlayDB(); |
|||
SecureTrieDB<Address, MemoryDB> accountState(&m_stateDB); |
|||
accountState.init(); |
|||
|
|||
dev::eth::commit(_accounts, accountState); |
|||
h256 stateRoot = accountState.root(); |
|||
m_bc.reset(); |
|||
m_bc.reset(new MixBlockChain(m_dbPath, stateRoot)); |
|||
Block b(m_stateDB, BaseState::PreExisting, KeyPair(_miner).address()); |
|||
b.sync(bc()); |
|||
m_preMine = b; |
|||
m_postMine = b; |
|||
WriteGuard lx(x_executions); |
|||
m_executions.clear(); |
|||
} |
|||
|
|||
Transaction MixClient::replaceGas(Transaction const& _t, u256 const& _gas, Secret const& _secret) |
|||
{ |
|||
Transaction ret; |
|||
if (_secret) |
|||
{ |
|||
if (_t.isCreation()) |
|||
ret = Transaction(_t.value(), _t.gasPrice(), _gas, _t.data(), _t.nonce(), _secret); |
|||
else |
|||
ret = Transaction(_t.value(), _t.gasPrice(), _gas, _t.receiveAddress(), _t.data(), _t.nonce(), _secret); |
|||
} |
|||
else |
|||
{ |
|||
if (_t.isCreation()) |
|||
ret = Transaction(_t.value(), _t.gasPrice(), _gas, _t.data(), _t.nonce()); |
|||
else |
|||
ret = Transaction(_t.value(), _t.gasPrice(), _gas, _t.receiveAddress(), _t.data(), _t.nonce()); |
|||
ret.forceSender(_t.safeSender()); |
|||
} |
|||
return ret; |
|||
} |
|||
|
|||
// TODO: prototype changed - will need rejigging.
|
|||
ExecutionResult MixClient::debugTransaction(Transaction const& _t, State const& _state, EnvInfo const& _envInfo, bool _call) |
|||
{ |
|||
State execState = _state; |
|||
execState.addBalance(_t.sender(), _t.gas() * _t.gasPrice()); //give it enough balance for gas estimation
|
|||
eth::ExecutionResult er; |
|||
Executive execution(execState, _envInfo); |
|||
execution.setResultRecipient(er); |
|||
|
|||
ExecutionResult d; |
|||
d.address = _t.receiveAddress(); |
|||
d.sender = _t.sender(); |
|||
d.value = _t.value(); |
|||
d.inputParameters = _t.data(); |
|||
d.executonIndex = m_executions.size(); |
|||
if (!_call) |
|||
d.transactionIndex = m_postMine.pending().size(); |
|||
|
|||
try |
|||
{ |
|||
execution.initialize(_t); |
|||
execution.execute(); |
|||
} |
|||
catch (Exception const& _e) |
|||
{ |
|||
d.excepted = toTransactionException(_e); |
|||
d.transactionData.push_back(_t.data()); |
|||
return d; |
|||
} |
|||
|
|||
std::vector<MachineState> machineStates; |
|||
std::vector<unsigned> levels; |
|||
std::vector<MachineCode> codes; |
|||
std::map<bytes const*, unsigned> codeIndexes; |
|||
std::vector<bytes> data; |
|||
std::map<bytesConstRef const*, unsigned> dataIndexes; |
|||
bytes const* lastCode = nullptr; |
|||
bytesConstRef const* lastData = nullptr; |
|||
unsigned codeIndex = 0; |
|||
unsigned dataIndex = 0; |
|||
auto onOp = [&](uint64_t steps, Instruction inst, bigint newMemSize, bigint gasCost, bigint gas, void* voidVM, void const* voidExt) |
|||
{ |
|||
VM& vm = *static_cast<VM*>(voidVM); |
|||
ExtVM const& ext = *static_cast<ExtVM const*>(voidExt); |
|||
if (lastCode == nullptr || lastCode != &ext.code) |
|||
{ |
|||
auto const& iter = codeIndexes.find(&ext.code); |
|||
if (iter != codeIndexes.end()) |
|||
codeIndex = iter->second; |
|||
else |
|||
{ |
|||
codeIndex = codes.size(); |
|||
codes.push_back(MachineCode({ext.myAddress, ext.code})); |
|||
codeIndexes[&ext.code] = codeIndex; |
|||
} |
|||
lastCode = &ext.code; |
|||
} |
|||
|
|||
if (lastData == nullptr || lastData != &ext.data) |
|||
{ |
|||
auto const& iter = dataIndexes.find(&ext.data); |
|||
if (iter != dataIndexes.end()) |
|||
dataIndex = iter->second; |
|||
else |
|||
{ |
|||
dataIndex = data.size(); |
|||
data.push_back(ext.data.toBytes()); |
|||
dataIndexes[&ext.data] = dataIndex; |
|||
} |
|||
lastData = &ext.data; |
|||
} |
|||
|
|||
if (levels.size() < ext.depth) |
|||
levels.push_back(machineStates.size() - 1); |
|||
else |
|||
levels.resize(ext.depth); |
|||
|
|||
machineStates.push_back(MachineState{ |
|||
steps, |
|||
vm.curPC(), |
|||
inst, |
|||
newMemSize, |
|||
static_cast<u256>(gas), |
|||
vm.stack(), |
|||
vm.memory(), |
|||
gasCost, |
|||
ext.state().storage(ext.myAddress), |
|||
std::move(levels), |
|||
codeIndex, |
|||
dataIndex |
|||
}); |
|||
}; |
|||
|
|||
execution.go(onOp); |
|||
execution.finalize(); |
|||
|
|||
d.excepted = er.excepted; |
|||
d.result = er; |
|||
d.machineStates = machineStates; |
|||
d.executionCode = std::move(codes); |
|||
d.transactionData = std::move(data); |
|||
d.gasUsed = er.gasUsed + er.gasRefunded + c_callStipend; |
|||
if (_t.isCreation()) |
|||
d.contractAddress = right160(sha3(rlpList(_t.sender(), _t.nonce()))); |
|||
return d; |
|||
} |
|||
|
|||
|
|||
void MixClient::executeTransaction(Transaction const& _t, Block& _block, bool _call, bool _gasAuto, Secret const& _secret) |
|||
{ |
|||
Transaction t = _gasAuto ? replaceGas(_t, m_postMine.gasLimitRemaining()) : _t; |
|||
|
|||
// do debugging run first
|
|||
EnvInfo envInfo(bc().info(), bc().lastHashes()); |
|||
ExecutionResult d = debugTransaction(t, _block.state(), envInfo, _call); |
|||
|
|||
// execute on a state
|
|||
if (!_call && d.excepted == TransactionException::None) |
|||
{ |
|||
u256 useGas = min(d.gasUsed, _block.gasLimitRemaining()); |
|||
t = _gasAuto ? replaceGas(_t, useGas, _secret) : _t; |
|||
eth::ExecutionResult const& er = _block.execute(envInfo.lastHashes(), t); |
|||
if (t.isCreation() && _block.state().code(d.contractAddress).empty()) |
|||
BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Not enough gas for contract deployment")); |
|||
d.gasUsed = er.gasUsed + er.gasRefunded + er.gasForDeposit + c_callStipend; |
|||
LocalisedLogEntries logs; |
|||
TransactionReceipt const& tr = _block.receipt(_block.pending().size() - 1); |
|||
|
|||
LogEntries le = tr.log(); |
|||
if (le.size()) |
|||
for (unsigned j = 0; j < le.size(); ++j) |
|||
logs.insert(logs.begin(), LocalisedLogEntry(le[j])); |
|||
d.logs = logs; |
|||
} |
|||
WriteGuard l(x_executions); |
|||
m_executions.emplace_back(std::move(d)); |
|||
} |
|||
|
|||
void MixClient::mine() |
|||
{ |
|||
WriteGuard l(x_state); |
|||
m_postMine.commitToSeal(bc()); |
|||
|
|||
NoProof::BlockHeader h(m_postMine.info()); |
|||
RLPStream header; |
|||
h.streamRLP(header); |
|||
m_postMine.sealBlock(header.out()); |
|||
bc().import(m_postMine.blockData(), m_postMine.state().db(), (ImportRequirements::Everything & ~ImportRequirements::ValidSeal) != 0); |
|||
m_postMine.sync(bc()); |
|||
m_preMine = m_postMine; |
|||
} |
|||
|
|||
ExecutionResult MixClient::lastExecution() const |
|||
{ |
|||
ReadGuard l(x_executions); |
|||
return m_executions.empty() ? ExecutionResult() : m_executions.back(); |
|||
} |
|||
|
|||
ExecutionResult MixClient::execution(unsigned _index) const |
|||
{ |
|||
ReadGuard l(x_executions); |
|||
return m_executions.at(_index); |
|||
} |
|||
|
|||
Block MixClient::asOf(h256 const& _block) const |
|||
{ |
|||
ReadGuard l(x_state); |
|||
Block ret(m_stateDB); |
|||
ret.populateFromChain(bc(), _block); |
|||
return ret; |
|||
} |
|||
|
|||
pair<h256, Address> MixClient::submitTransaction(eth::TransactionSkeleton const& _ts, Secret const& _secret, bool _gasAuto) |
|||
{ |
|||
WriteGuard l(x_state); |
|||
TransactionSkeleton ts = _ts; |
|||
ts.from = toAddress(_secret); |
|||
ts.nonce = m_postMine.transactionsFrom(ts.from); |
|||
eth::Transaction t(ts, _secret); |
|||
executeTransaction(t, m_postMine, false, _gasAuto, _secret); |
|||
return make_pair(t.sha3(), toAddress(ts.from, ts.nonce)); |
|||
} |
|||
|
|||
dev::eth::ExecutionResult MixClient::call(Address const& _from, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, bool _gasAuto, FudgeFactor _ff) |
|||
{ |
|||
(void)_blockNumber; |
|||
Block block = asOf(eth::PendingBlock); |
|||
u256 n = block.transactionsFrom(_from); |
|||
Transaction t(_value, _gasPrice, _gas, _dest, _data, n); |
|||
t.forceSender(_from); |
|||
if (_ff == FudgeFactor::Lenient) |
|||
block.mutableState().addBalance(_from, (u256)(t.gasRequired() * t.gasPrice() + t.value())); |
|||
WriteGuard lw(x_state); //TODO: lock is required only for last execution state
|
|||
executeTransaction(t, block, true, _gasAuto); |
|||
return lastExecution().result; |
|||
} |
|||
|
|||
dev::eth::ExecutionResult MixClient::call(Address const& _from, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, eth::FudgeFactor _ff) |
|||
{ |
|||
return call(_from, _value, _dest, _data, _gas, _gasPrice, _blockNumber, false, _ff); |
|||
} |
|||
|
|||
dev::eth::ExecutionResult MixClient::create(Address const& _from, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber, eth::FudgeFactor _ff) |
|||
{ |
|||
(void)_blockNumber; |
|||
u256 n; |
|||
Block temp; |
|||
{ |
|||
ReadGuard lr(x_state); |
|||
temp = asOf(eth::PendingBlock); |
|||
n = temp.transactionsFrom(_from); |
|||
} |
|||
Transaction t(_value, _gasPrice, _gas, _data, n); |
|||
t.forceSender(_from); |
|||
if (_ff == FudgeFactor::Lenient) |
|||
temp.mutableState().addBalance(_from, (u256)(t.gasRequired() * t.gasPrice() + t.value())); |
|||
WriteGuard lw(x_state); //TODO: lock is required only for last execution state
|
|||
executeTransaction(t, temp, true, false); |
|||
return lastExecution().result; |
|||
} |
|||
|
|||
eth::BlockInfo MixClient::blockInfo() const |
|||
{ |
|||
ReadGuard l(x_state); |
|||
return BlockInfo(bc().block()); |
|||
} |
|||
|
|||
void MixClient::setBeneficiary(Address _us) |
|||
{ |
|||
WriteGuard l(x_state); |
|||
m_postMine.setBeneficiary(_us); |
|||
} |
|||
|
|||
void MixClient::startMining() |
|||
{ |
|||
//no-op
|
|||
} |
|||
|
|||
void MixClient::stopMining() |
|||
{ |
|||
//no-op
|
|||
} |
|||
|
|||
bool MixClient::isMining() const |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
u256 MixClient::hashrate() const |
|||
{ |
|||
return 0; |
|||
} |
|||
|
|||
eth::WorkingProgress MixClient::miningProgress() const |
|||
{ |
|||
return eth::WorkingProgress(); |
|||
} |
|||
|
|||
} |
|||
} |
@ -1,135 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file MixClient.h
|
|||
* @author Yann yann@ethdev.com |
|||
* @author Arkadiy Paronyan arkadiy@ethdev.com |
|||
* @date 2015 |
|||
* Ethereum IDE client. |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <vector> |
|||
#include <string> |
|||
#include <libethcore/BasicAuthority.h> |
|||
#include <libethereum/ExtVM.h> |
|||
#include <libethereum/ClientBase.h> |
|||
#include <libethereum/Client.h> |
|||
#include "MachineStates.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace eth { class EnvInfo; } |
|||
|
|||
namespace mix |
|||
{ |
|||
|
|||
class NoProof |
|||
{ |
|||
class BlockHeaderRaw: public dev::eth::BlockInfo |
|||
{ |
|||
public: |
|||
static const unsigned SealFields = 0; |
|||
|
|||
protected: |
|||
BlockHeaderRaw() = default; |
|||
BlockHeaderRaw(BlockInfo const& _bi): BlockInfo(_bi) {} |
|||
|
|||
void populateFromHeader(RLP const& _header, dev::eth::Strictness _s) { (void) _header; (void) _s; } |
|||
void populateFromParent(BlockHeaderRaw const& _parent) { (void)_parent; } |
|||
void streamRLPFields(RLPStream& _s) const { (void) _s; } |
|||
}; |
|||
|
|||
public: |
|||
|
|||
static std::string name() { return "NoProof"; } |
|||
static unsigned revision() { return 0; } |
|||
using BlockHeader = dev::eth::BlockHeaderPolished<BlockHeaderRaw>; |
|||
|
|||
private: |
|||
static AddressHash s_authorities; |
|||
}; |
|||
|
|||
class MixBlockChain: public dev::eth::FullBlockChain<NoProof> |
|||
{ |
|||
public: |
|||
MixBlockChain(std::string const& _path, h256 _stateRoot); |
|||
|
|||
static bytes createGenesisBlock(h256 _stateRoot); |
|||
}; |
|||
|
|||
class MixClient: public dev::eth::ClientBase |
|||
{ |
|||
public: |
|||
MixClient(std::string const& _dbPath); |
|||
virtual ~MixClient(); |
|||
/// Reset state to the empty state with given balance.
|
|||
void resetState(std::unordered_map<dev::Address, dev::eth::Account> const& _accounts, Secret const& _miner = Secret()); |
|||
void mine(); |
|||
ExecutionResult lastExecution() const; |
|||
ExecutionResult execution(unsigned _index) const; |
|||
|
|||
dev::eth::ExecutionResult call(Address const& _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, eth::BlockNumber _blockNumber = eth::PendingBlock, eth::FudgeFactor _ff = eth::FudgeFactor::Strict) override; |
|||
dev::eth::ExecutionResult create(Address const& _secret, u256 _value, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * eth::szabo, eth::BlockNumber _blockNumber = eth::PendingBlock, eth::FudgeFactor _ff = eth::FudgeFactor::Strict) override; |
|||
|
|||
using ClientBase::submitTransaction; |
|||
virtual std::pair<h256, Address> submitTransaction(eth::TransactionSkeleton const& _ts, Secret const& _secret) override { return submitTransaction(_ts, _secret, false); } |
|||
std::pair<h256, Address> submitTransaction(eth::TransactionSkeleton const& _ts, Secret const& _secret, bool _gasAuto); |
|||
dev::eth::ExecutionResult call(Address const& _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, eth::BlockNumber _blockNumber, bool _gasAuto, eth::FudgeFactor _ff = eth::FudgeFactor::Strict); |
|||
ExecutionResult debugTransaction(dev::eth::Transaction const& _t, eth:: State const& _state, eth::EnvInfo const& _envInfo, bool _call); |
|||
void setBeneficiary(Address _us) override; |
|||
void startMining() override; |
|||
void stopMining() override; |
|||
bool isMining() const override; |
|||
u256 hashrate() const override; |
|||
eth::WorkingProgress miningProgress() const override; |
|||
virtual void flushTransactions() override {} |
|||
|
|||
/// @returns the last mined block information
|
|||
using Interface::blockInfo; // to remove warning about hiding virtual function
|
|||
eth::BlockInfo blockInfo() const; |
|||
|
|||
/// return the new address generated by the last tr (if creation). returns empty address if other cases.
|
|||
Address lastCreatedContractAddr() const; |
|||
|
|||
protected: |
|||
/// ClientBase methods
|
|||
using ClientBase::asOf; |
|||
virtual dev::eth::Block asOf(h256 const& _block) const override; |
|||
virtual dev::eth::BlockChain& bc() override { return *m_bc; } |
|||
virtual dev::eth::BlockChain const& bc() const override { return *m_bc; } |
|||
virtual dev::eth::Block preMine() const override { ReadGuard l(x_state); return m_preMine; } |
|||
virtual dev::eth::Block postMine() const override { ReadGuard l(x_state); return m_postMine; } |
|||
virtual void prepareForTransaction() override {} |
|||
|
|||
private: |
|||
void executeTransaction(dev::eth::Transaction const& _t, eth::Block& _block, bool _call, bool _gasAuto, dev::Secret const& _secret = dev::Secret()); |
|||
dev::eth::Transaction replaceGas(dev::eth::Transaction const& _t, dev::u256 const& _gas, dev::Secret const& _secret = dev::Secret()); |
|||
|
|||
eth::Block m_preMine; |
|||
eth::Block m_postMine; |
|||
OverlayDB m_stateDB; |
|||
std::unique_ptr<MixBlockChain> m_bc; |
|||
mutable boost::shared_mutex x_state; |
|||
mutable boost::shared_mutex x_executions; |
|||
ExecutionResults m_executions; |
|||
std::string m_dbPath; |
|||
}; |
|||
|
|||
} |
|||
|
|||
} |
@ -1,41 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file QBasicNodeDefinition.cpp
|
|||
* @author Yann yann@ethdev.com |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#include "QBasicNodeDefinition.h" |
|||
#include <libsolidity/AST.h> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace mix |
|||
{ |
|||
|
|||
QBasicNodeDefinition::QBasicNodeDefinition(QObject* _parent, solidity::Declaration const* _d): |
|||
QObject(_parent), m_name(QString::fromStdString(_d->getName())), m_location(_d->getLocation()) |
|||
{ |
|||
} |
|||
|
|||
QBasicNodeDefinition::QBasicNodeDefinition(QObject* _parent, std::string const& _name): |
|||
QObject(_parent), m_name(QString::fromStdString(_name)) |
|||
{ |
|||
} |
|||
|
|||
} |
|||
} |
@ -1,59 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file QBasicNodeDefinition.h
|
|||
* @author Yann yann@ethdev.com |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <string> |
|||
#include <QObject> |
|||
#include <libevmasm/SourceLocation.h> |
|||
|
|||
namespace dev |
|||
{ |
|||
|
|||
namespace solidity |
|||
{ |
|||
class Declaration; |
|||
} |
|||
|
|||
namespace mix |
|||
{ |
|||
|
|||
class QBasicNodeDefinition: public QObject |
|||
{ |
|||
Q_OBJECT |
|||
Q_PROPERTY(QString name READ name CONSTANT) |
|||
|
|||
public: |
|||
QBasicNodeDefinition(QObject* _parent = nullptr): QObject(_parent) {} |
|||
~QBasicNodeDefinition() {} |
|||
QBasicNodeDefinition(QObject* _parent, solidity::Declaration const* _d); |
|||
QBasicNodeDefinition(QObject* _parent, std::string const& _name); |
|||
/// Get the name of the node.
|
|||
QString name() const { return m_name; } |
|||
dev::SourceLocation const& location() { return m_location; } |
|||
|
|||
private: |
|||
QString m_name; |
|||
dev::SourceLocation m_location; |
|||
}; |
|||
|
|||
} |
|||
} |
@ -1,96 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file QBigInt.cpp
|
|||
* @author Yann yann@ethdev.com |
|||
* @date 2015 |
|||
*/ |
|||
|
|||
#include <boost/variant/multivisitors.hpp> |
|||
#include <boost/variant.hpp> |
|||
#include <libethcore/CommonJS.h> |
|||
#include "QBigInt.h" |
|||
|
|||
using namespace dev; |
|||
using namespace dev::mix; |
|||
|
|||
QString QBigInt::value() const |
|||
{ |
|||
std::ostringstream s; |
|||
s << m_internalValue; |
|||
return QString::fromStdString(s.str()); |
|||
} |
|||
|
|||
QBigInt* QBigInt::subtract(QBigInt* const& _value) const |
|||
{ |
|||
BigIntVariant toSubtract = _value->internalValue(); |
|||
return new QBigInt(boost::apply_visitor(mix::subtract(), m_internalValue, toSubtract)); |
|||
} |
|||
|
|||
QBigInt* QBigInt::add(QBigInt* const& _value) const |
|||
{ |
|||
BigIntVariant toAdd = _value->internalValue(); |
|||
return new QBigInt(boost::apply_visitor(mix::add(), m_internalValue, toAdd)); |
|||
} |
|||
|
|||
QBigInt* QBigInt::multiply(QBigInt* const& _value) const |
|||
{ |
|||
BigIntVariant toMultiply = _value->internalValue(); |
|||
return new QBigInt(boost::apply_visitor(mix::multiply(), m_internalValue, toMultiply)); |
|||
} |
|||
|
|||
QBigInt* QBigInt::divide(QBigInt* const& _value) const |
|||
{ |
|||
BigIntVariant toDivide = _value->internalValue(); |
|||
return new QBigInt(boost::apply_visitor(mix::divide(), m_internalValue, toDivide)); |
|||
} |
|||
|
|||
QVariantMap QBigInt::checkAgainst(QString const& _type) const |
|||
{ |
|||
QVariantMap ret; |
|||
QString type = _type; |
|||
QString capacity = type.replace("uint", "").replace("int", ""); |
|||
if (capacity.isEmpty()) |
|||
capacity = "256"; |
|||
bigint range = 1; |
|||
for (int k = 0; k < capacity.toInt() / 8; ++k) |
|||
range = range * 256; |
|||
bigint value = boost::get<bigint>(this->internalValue()); |
|||
ret.insert("valid", true); |
|||
if (_type.startsWith("uint") && value > range - 1) |
|||
{ |
|||
ret.insert("minValue", "0"); |
|||
std::ostringstream s; |
|||
s << range - 1; |
|||
ret.insert("maxValue", QString::fromStdString(s.str())); |
|||
if (value > range) |
|||
ret["valid"] = false; |
|||
} |
|||
else if (_type.startsWith("int")) |
|||
{ |
|||
range = range / 2; |
|||
std::ostringstream s; |
|||
s << -range; |
|||
ret.insert("minValue", QString::fromStdString(s.str())); |
|||
s.str(""); |
|||
s.clear(); |
|||
s << range - 1; |
|||
ret.insert("maxValue", QString::fromStdString(s.str())); |
|||
if (-range > value || value > range - 1) |
|||
ret["valid"] = false; |
|||
} |
|||
return ret; |
|||
} |
@ -1,109 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file QBigInt.h
|
|||
* @author Yann yann@ethdev.com |
|||
* @date 2015 |
|||
* Represent a big integer (u256, bigint) to be used in QML. |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include "boost/variant.hpp" |
|||
#include "boost/variant/multivisitors.hpp" |
|||
#include <QObject> |
|||
#include <QQmlEngine> |
|||
#include <libethcore/CommonJS.h> |
|||
#include <libdevcore/Common.h> |
|||
|
|||
using namespace dev; |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace mix |
|||
{ |
|||
|
|||
using BigIntVariant = boost::variant<dev::u256, dev::bigint, dev::s256>; |
|||
|
|||
struct add: public boost::static_visitor<BigIntVariant> |
|||
{ |
|||
template<class T1, class T2> |
|||
BigIntVariant operator()(T1 const& _value, T2 const& _otherValue) const { return _value + _otherValue; } |
|||
}; |
|||
|
|||
struct subtract: public boost::static_visitor<BigIntVariant> |
|||
{ |
|||
template<class T1, class T2> |
|||
BigIntVariant operator()(T1 const& _value, T2 const& _otherValue) const { return _value - _otherValue; } |
|||
}; |
|||
|
|||
struct multiply: public boost::static_visitor<BigIntVariant> |
|||
{ |
|||
template<class T1, class T2> |
|||
BigIntVariant operator()(T1 const& _value, T2 const& _otherValue) const { return _value * _otherValue; } |
|||
}; |
|||
|
|||
struct divide: public boost::static_visitor<BigIntVariant> |
|||
{ |
|||
template<class T1, class T2> |
|||
BigIntVariant operator()(T1 const& _value, T2 const& _otherValue) const { return _value / _otherValue; } |
|||
}; |
|||
|
|||
/*
|
|||
* Represent big integer like big int and u256 in QML. |
|||
* The ownership is set by default to Javascript. |
|||
*/ |
|||
class QBigInt: public QObject |
|||
{ |
|||
Q_OBJECT |
|||
|
|||
public: |
|||
QBigInt(QObject* _parent = 0): QObject(_parent), m_internalValue(dev::u256(0)) { QQmlEngine::setObjectOwnership(this, QQmlEngine::JavaScriptOwnership); } |
|||
QBigInt(dev::u256 const& _value, QObject* _parent = 0): QObject(_parent), m_internalValue(_value) { QQmlEngine::setObjectOwnership(this, QQmlEngine::JavaScriptOwnership); } |
|||
QBigInt(dev::bigint const& _value, QObject* _parent = 0): QObject(_parent), m_internalValue(_value) { QQmlEngine::setObjectOwnership(this, QQmlEngine::JavaScriptOwnership); } |
|||
QBigInt(BigIntVariant const& _value, QObject* _parent = 0): QObject(_parent), m_internalValue(_value){ QQmlEngine::setObjectOwnership(this, QQmlEngine::JavaScriptOwnership); } |
|||
QBigInt(dev::s256 const& _value, QObject* _parent = 0): QObject(_parent), m_internalValue(_value) { QQmlEngine::setObjectOwnership(this, QQmlEngine::JavaScriptOwnership); } |
|||
~QBigInt() {} |
|||
|
|||
/// @returns the current used big integer.
|
|||
BigIntVariant internalValue() const { return m_internalValue; } |
|||
/// @returns a string representation of the big integer used. Invokable from QML.
|
|||
Q_INVOKABLE QString value() const; |
|||
/// hex value.
|
|||
Q_INVOKABLE QString hexValue() const { return QString::fromStdString(dev::toHex(dev::u256(value().toStdString()))); } |
|||
/// Set the value of the BigInteger used. Will use u256 type. Invokable from QML.
|
|||
Q_INVOKABLE void setValue(QString const& _value) { m_internalValue = dev::jsToU256(_value.toStdString()); } |
|||
Q_INVOKABLE void setBigInt(QString const& _value) { m_internalValue = bigint(_value.toStdString()); } |
|||
void setBigInt(u256 const& _value) { m_internalValue = _value; } |
|||
/// Subtract by @a _value. Invokable from QML.
|
|||
Q_INVOKABLE QBigInt* subtract(QBigInt* const& _value) const; |
|||
/// Add @a _value to the current big integer. Invokable from QML.
|
|||
Q_INVOKABLE QBigInt* add(QBigInt* const& _value) const; |
|||
/// Multiply by @a _value. Invokable from QML.
|
|||
Q_INVOKABLE QBigInt* multiply(QBigInt* const& _value) const; |
|||
/// divide by @a _value. Invokable from QML.
|
|||
Q_INVOKABLE QBigInt* divide(QBigInt* const& _value) const; |
|||
/// check if the current value satisfy the given type
|
|||
Q_INVOKABLE QVariantMap checkAgainst(QString const& _type) const; |
|||
|
|||
protected: |
|||
BigIntVariant m_internalValue; |
|||
}; |
|||
|
|||
} |
|||
} |
|||
|
|||
|
@ -1,57 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file QContractDefinition.cpp
|
|||
* @author Yann yann@ethdev.com |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#include <QObject> |
|||
|
|||
#include "QContractDefinition.h" |
|||
#include <libsolidity/CompilerStack.h> |
|||
#include <libsolidity/AST.h> |
|||
#include <libsolidity/Scanner.h> |
|||
#include <libsolidity/Parser.h> |
|||
#include <libsolidity/Scanner.h> |
|||
#include <libsolidity/NameAndTypeResolver.h> |
|||
using namespace dev::solidity; |
|||
using namespace dev::mix; |
|||
|
|||
QContractDefinition::QContractDefinition(QObject* _parent, dev::solidity::ContractDefinition const* _contract): QBasicNodeDefinition(_parent, _contract) |
|||
{ |
|||
QObject* parent = _parent ? _parent : this; |
|||
if (_contract->getConstructor() != nullptr) |
|||
m_constructor = new QFunctionDefinition(parent, ContractType(*_contract).getConstructorType()); |
|||
else |
|||
m_constructor = new QFunctionDefinition(parent); |
|||
|
|||
for (auto const& it: _contract->getInterfaceFunctions()) |
|||
m_functions.append(new QFunctionDefinition(parent, it.second)); |
|||
|
|||
for (auto const& it: _contract->getEvents()) |
|||
m_events.append(new QFunctionDefinition(parent, it)); |
|||
|
|||
} |
|||
|
|||
QFunctionDefinition const* QContractDefinition::getFunction(dev::FixedHash<4> _hash) const |
|||
{ |
|||
for (auto const& f: m_functions) |
|||
if (f->hash() == _hash) |
|||
return f; |
|||
|
|||
return nullptr; |
|||
} |
@ -1,65 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file QContractDefinition.h
|
|||
* @author Yann yann@ethdev.com |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <QObject> |
|||
#include <QQmlListProperty> |
|||
#include <libsolidity/AST.h> |
|||
#include "QFunctionDefinition.h" |
|||
#include "QBasicNodeDefinition.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace mix |
|||
{ |
|||
|
|||
class QContractDefinition: public QBasicNodeDefinition |
|||
{ |
|||
Q_OBJECT |
|||
Q_PROPERTY(QQmlListProperty<dev::mix::QFunctionDefinition> functions READ functions CONSTANT) |
|||
Q_PROPERTY(dev::mix::QFunctionDefinition* constructor READ constructor CONSTANT) |
|||
Q_PROPERTY(QQmlListProperty<dev::mix::QFunctionDefinition> events READ events CONSTANT) |
|||
|
|||
public: |
|||
QContractDefinition(QObject* _parent, solidity::ContractDefinition const* _contract); |
|||
/// Get all the functions of the contract.
|
|||
QQmlListProperty<QFunctionDefinition> functions() const { return QQmlListProperty<QFunctionDefinition>(const_cast<QContractDefinition*>(this), const_cast<QContractDefinition*>(this)->m_functions); } |
|||
/// Get the constructor of the contract.
|
|||
QFunctionDefinition* constructor() const { return m_constructor; } |
|||
/// Get all the functions of the contract.
|
|||
QList<QFunctionDefinition*> const& functionsList() const { return m_functions; } |
|||
/// Find function by hash, returns nullptr if not found
|
|||
QFunctionDefinition const* getFunction(dev::FixedHash<4> _hash) const; |
|||
/// Get events
|
|||
QQmlListProperty<QFunctionDefinition> events() const { return QQmlListProperty<QFunctionDefinition>(const_cast<QContractDefinition*>(this), const_cast<QContractDefinition*>(this)->m_events); } |
|||
/// Get events
|
|||
QList<QFunctionDefinition*> const& eventsList() const { return m_events; } |
|||
|
|||
private: |
|||
QList<QFunctionDefinition*> m_functions; |
|||
QFunctionDefinition* m_constructor; |
|||
QList<QFunctionDefinition*> m_events; |
|||
}; |
|||
|
|||
} |
|||
} |
|||
|
@ -1,55 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file QEther.cpp
|
|||
* @author Yann yann@ethdev.com |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#include <QMetaEnum> |
|||
#include "QEther.h" |
|||
|
|||
using namespace dev::mix; |
|||
|
|||
QString QEther::format() const |
|||
{ |
|||
return QString::fromStdString(dev::eth::formatBalance(boost::get<dev::u256>(toWei()->internalValue()))); |
|||
} |
|||
|
|||
QBigInt* QEther::toWei() const |
|||
{ |
|||
QMetaEnum units = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("EtherUnit")); |
|||
const char* key = units.valueToKey(m_currentUnit); |
|||
for (std::pair<dev::u256, std::string> rawUnit: dev::eth::units()) |
|||
{ |
|||
if (QString::fromStdString(rawUnit.second).toLower() == QString(key).toLower()) |
|||
return multiply(new QBigInt(rawUnit.first)); |
|||
} |
|||
return new QBigInt(dev::u256(0)); |
|||
} |
|||
|
|||
void QEther::setUnit(QString const& _unit) |
|||
{ |
|||
QMetaEnum units = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("EtherUnit")); |
|||
for (int k = 0; k < units.keyCount(); k++) |
|||
{ |
|||
if (QString(units.key(k)).toLower() == _unit.toLower()) |
|||
{ |
|||
m_currentUnit = static_cast<EtherUnit>(units.keysToValue(units.key(k))); |
|||
return; |
|||
} |
|||
} |
|||
} |
@ -1,92 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file QEther.h
|
|||
* @author Yann yann@ethdev.com |
|||
* @date 2014 |
|||
* Represent an amount of Ether in QML (mapped to u256 in c++). |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <QObject> |
|||
#include <libethcore/Common.h> |
|||
#include "QBigInt.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace mix |
|||
{ |
|||
|
|||
class QEther: public QBigInt |
|||
{ |
|||
Q_OBJECT |
|||
Q_ENUMS(EtherUnit) |
|||
Q_PROPERTY(QString value READ value WRITE setValue NOTIFY valueChanged) |
|||
Q_PROPERTY(EtherUnit unit READ unit WRITE setUnit NOTIFY unitChanged) |
|||
|
|||
public: |
|||
enum EtherUnit |
|||
{ |
|||
Uether, |
|||
Vether, |
|||
Dether, |
|||
Nether, |
|||
Yether, |
|||
Zether, |
|||
Eether, |
|||
Pether, |
|||
Tether, |
|||
Gether, |
|||
Mether, |
|||
Grand, |
|||
Ether, |
|||
Finney, |
|||
Szabo, |
|||
Gwei, |
|||
Mwei, |
|||
Kwei, |
|||
Wei |
|||
}; |
|||
|
|||
QEther(QObject* _parent = 0): QBigInt(dev::u256(0), _parent), m_currentUnit(EtherUnit::Wei) {} |
|||
QEther(dev::u256 _value, EtherUnit _unit, QObject* _parent = 0): QBigInt(_value, _parent), m_currentUnit(_unit) {} |
|||
~QEther() {} |
|||
|
|||
/// @returns user-friendly string representation of the amount of ether. Invokable from QML.
|
|||
Q_INVOKABLE QString format() const; |
|||
/// @returns the current amount of Ether in Wei. Invokable from QML.
|
|||
Q_INVOKABLE QBigInt* toWei() const; |
|||
/// @returns the current unit used. Invokable from QML.
|
|||
Q_INVOKABLE EtherUnit unit() const { return m_currentUnit; } |
|||
/// Set the unit to be used. Invokable from QML.
|
|||
Q_INVOKABLE void setUnit(EtherUnit const& _unit) { m_currentUnit = _unit; } |
|||
/// Set the unit to be used. Invokable from QML.
|
|||
Q_INVOKABLE void setUnit(QString const& _unit); |
|||
/// @returns the u256 value of the current amount of Ether in Wei.
|
|||
dev::u256 toU256Wei() { return boost::get<dev::u256>(toWei()->internalValue()); } |
|||
|
|||
private: |
|||
EtherUnit m_currentUnit; |
|||
|
|||
signals: |
|||
void valueChanged(); |
|||
void unitChanged(); |
|||
}; |
|||
|
|||
} |
|||
} |
|||
|
@ -1,67 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file QFunctionDefinition.cpp
|
|||
* @author Yann yann@ethdev.com |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#include <libsolidity/AST.h> |
|||
#include <libdevcore/SHA3.h> |
|||
#include <libdevcore/Exceptions.h> |
|||
#include "QVariableDeclaration.h" |
|||
#include "QFunctionDefinition.h" |
|||
|
|||
using namespace dev::solidity; |
|||
using namespace dev::mix; |
|||
|
|||
QFunctionDefinition::QFunctionDefinition(QObject* _parent, dev::solidity::FunctionTypePointer const& _f): QBasicNodeDefinition(_parent, &_f->getDeclaration()), m_hash(dev::sha3(_f->externalSignature())), |
|||
m_fullHash(dev::sha3(_f->externalSignature())) |
|||
{ |
|||
init(_f); |
|||
} |
|||
|
|||
QFunctionDefinition::QFunctionDefinition(QObject* _parent, ASTPointer<FunctionDefinition> const& _f): QBasicNodeDefinition(_parent, _f.get()), m_hash(dev::sha3(_f->externalSignature())), |
|||
m_fullHash(dev::sha3(_f->externalSignature())) |
|||
{ |
|||
for (unsigned i = 0; i < _f->getParameters().size(); ++i) |
|||
m_parameters.append(new QVariableDeclaration(parent(), _f->getParameters().at(i))); |
|||
|
|||
for (unsigned i = 0; i < _f->getReturnParameters().size(); ++i) |
|||
m_returnParameters.append(new QVariableDeclaration(parent(), _f->getReturnParameters().at(i))); |
|||
} |
|||
|
|||
QFunctionDefinition::QFunctionDefinition(QObject* _parent, ASTPointer<dev::solidity::EventDefinition> const& _e): QBasicNodeDefinition(_parent, _e.get()) |
|||
{ |
|||
for (unsigned i = 0; i < _e->getParameters().size(); ++i) |
|||
m_parameters.append(new QVariableDeclaration(parent(), _e->getParameters().at(i))); |
|||
FunctionTypePointer _f = std::make_shared<FunctionType>(*_e); |
|||
m_hash = (FixedHash<4>)dev::sha3(_f->externalSignature(_e->getName())); |
|||
m_fullHash = dev::sha3(_f->externalSignature(_e->getName())); |
|||
} |
|||
|
|||
void QFunctionDefinition::init(dev::solidity::FunctionTypePointer _f) |
|||
{ |
|||
auto paramNames = _f->getParameterNames(); |
|||
auto paramTypes = _f->getParameterTypes(); |
|||
auto returnNames = _f->getReturnParameterNames(); |
|||
auto returnTypes = _f->getReturnParameterTypes(); |
|||
for (unsigned i = 0; i < paramNames.size(); ++i) |
|||
m_parameters.append(new QVariableDeclaration(parent(), paramNames[i], paramTypes[i].get())); |
|||
|
|||
for (unsigned i = 0; i < returnNames.size(); ++i) |
|||
m_returnParameters.append(new QVariableDeclaration(parent(), returnNames[i], returnTypes[i].get())); |
|||
} |
@ -1,71 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file QFunctionDefinition.h
|
|||
* @author Yann yann@ethdev.com |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <QObject> |
|||
#include <QQmlListProperty> |
|||
#include <libsolidity/AST.h> |
|||
#include "QVariableDeclaration.h" |
|||
#include "QBasicNodeDefinition.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace mix |
|||
{ |
|||
|
|||
class QFunctionDefinition: public QBasicNodeDefinition |
|||
{ |
|||
Q_OBJECT |
|||
Q_PROPERTY(QQmlListProperty<dev::mix::QVariableDeclaration> parameters READ parameters) |
|||
|
|||
public: |
|||
QFunctionDefinition(){} |
|||
QFunctionDefinition(QObject* _parent): QBasicNodeDefinition(_parent) {} |
|||
QFunctionDefinition(QObject* _parent, solidity::FunctionTypePointer const& _f); |
|||
QFunctionDefinition(QObject* _parent, solidity::ASTPointer<solidity::EventDefinition> const& _f); |
|||
QFunctionDefinition(QObject* _parent, solidity::ASTPointer<solidity::FunctionDefinition> const& _f); |
|||
/// Init members
|
|||
void init(dev::solidity::FunctionTypePointer _f); |
|||
/// Get all input parameters of this function.
|
|||
QList<QVariableDeclaration*> const& parametersList() const { return m_parameters; } |
|||
/// Get all input parameters of this function as QML property.
|
|||
QQmlListProperty<QVariableDeclaration> parameters() const { return QQmlListProperty<QVariableDeclaration>(const_cast<QFunctionDefinition*>(this), const_cast<QFunctionDefinition*>(this)->m_parameters); } |
|||
/// Get all return parameters of this function.
|
|||
QList<QVariableDeclaration*> returnParameters() const { return m_returnParameters; } |
|||
/// Get the hash of this function declaration on the contract ABI.
|
|||
FixedHash<4> hash() const { return m_hash; } |
|||
/// Get the full hash of this function declaration on the contract ABI.
|
|||
FixedHash<32> fullHash() const { return m_fullHash; } |
|||
/// Get the hash of this function declaration on the contract ABI. returns QString
|
|||
Q_INVOKABLE QString qhash() const { return QString::fromStdString(m_hash.hex()); } |
|||
|
|||
private: |
|||
int m_index; |
|||
FixedHash<4> m_hash; |
|||
FixedHash<32> m_fullHash; |
|||
QList<QVariableDeclaration*> m_parameters; |
|||
QList<QVariableDeclaration*> m_returnParameters; |
|||
void initQParameters(); |
|||
}; |
|||
|
|||
} |
|||
} |
@ -1,72 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file QVariableDeclaration.app
|
|||
* @author Yann yann@ethdev.com |
|||
* @author Arkadiy Paronyan arkadiy@ethdev.com |
|||
* @date 2015 |
|||
*/ |
|||
|
|||
#include "QVariableDeclaration.h" |
|||
#include <libsolidity/AST.h> |
|||
#include "CodeModel.h" |
|||
|
|||
using namespace solidity; |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace mix |
|||
{ |
|||
|
|||
QVariableDeclaration::QVariableDeclaration(QObject* _parent, ASTPointer<VariableDeclaration> const _v): |
|||
QBasicNodeDefinition(_parent, _v.get()), |
|||
m_type(new QSolidityType(this, CodeModel::nodeType(_v->getType().get()))), m_isIndexed(_v->isIndexed()) |
|||
{ |
|||
} |
|||
|
|||
QVariableDeclaration::QVariableDeclaration(QObject* _parent, std::string const& _name, SolidityType const& _type, bool _isIndexed): |
|||
QBasicNodeDefinition(_parent, _name), |
|||
m_type(new QSolidityType(_parent, _type)), m_isIndexed(_isIndexed) |
|||
{ |
|||
} |
|||
|
|||
QVariableDeclaration::QVariableDeclaration(QObject* _parent, std::string const& _name, solidity::Type const* _type, bool _isIndexed): |
|||
QBasicNodeDefinition(_parent, _name), |
|||
m_type(new QSolidityType(this, CodeModel::nodeType(_type))), m_isIndexed(_isIndexed) |
|||
{ |
|||
} |
|||
|
|||
QSolidityType::QSolidityType(QObject* _parent, SolidityType const& _type): |
|||
QObject(_parent), |
|||
m_type(_type) |
|||
{ |
|||
} |
|||
|
|||
QVariantList QSolidityType::members() const |
|||
{ |
|||
QVariantList members; |
|||
if (m_type.type == Type::Struct) |
|||
for (auto const& structMember: m_type.members) |
|||
members.push_back(QVariant::fromValue(new QVariableDeclaration(parent(), structMember.name.toStdString(), structMember.type))); |
|||
if (m_type.type == Type::Enum) |
|||
for (auto const& enumName: m_type.enumNames) |
|||
members.push_back(QVariant::fromValue(enumName)); |
|||
return members; |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
@ -1,103 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file QVariableDeclaration.h
|
|||
* @author Yann yann@ethdev.com |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#include <QDebug> |
|||
#include <QVariantList> |
|||
#include <libsolidity/AST.h> |
|||
#include "QBasicNodeDefinition.h" |
|||
#include "SolidityType.h" |
|||
|
|||
#pragma once |
|||
|
|||
namespace dev |
|||
{ |
|||
|
|||
namespace solidity |
|||
{ |
|||
class Type; |
|||
class VariableDeclaration; |
|||
} |
|||
|
|||
namespace mix |
|||
{ |
|||
|
|||
/// UI wrapper around solidity type
|
|||
class QSolidityType: public QObject |
|||
{ |
|||
Q_OBJECT |
|||
Q_PROPERTY(int category READ category CONSTANT) //qml does not support enum properties
|
|||
Q_PROPERTY(int size READ size CONSTANT) |
|||
Q_PROPERTY(QString name READ name CONSTANT) |
|||
Q_PROPERTY(QVariantList members READ members CONSTANT) |
|||
Q_PROPERTY(bool array READ array CONSTANT) |
|||
|
|||
public: |
|||
QSolidityType() {} |
|||
QSolidityType(QObject* _parent, SolidityType const& _type); |
|||
using Type = SolidityType::Type; |
|||
enum QmlType //TODO: Q_ENUMS does not support enum forwarding. Keep in sync with SolidityType::Type
|
|||
{ |
|||
SignedInteger, |
|||
UnsignedInteger, |
|||
Hash, |
|||
Bool, |
|||
Address, |
|||
Bytes, |
|||
String, |
|||
Enum, |
|||
Struct |
|||
}; |
|||
|
|||
Q_ENUMS(QmlType) |
|||
SolidityType const& type() const { return m_type; } |
|||
Type category() const { return m_type.type; } |
|||
int size() const { return m_type.size; } |
|||
QString name() const { return m_type.name; } |
|||
QVariantList members() const; |
|||
bool array() const { return m_type.array; } |
|||
|
|||
private: |
|||
SolidityType m_type; |
|||
}; |
|||
|
|||
/// UI wrapper around declaration (name + type)
|
|||
class QVariableDeclaration: public QBasicNodeDefinition |
|||
{ |
|||
Q_OBJECT |
|||
Q_PROPERTY(QSolidityType* type READ type CONSTANT) |
|||
|
|||
public: |
|||
QVariableDeclaration() {} |
|||
QVariableDeclaration(QObject* _parent, solidity::ASTPointer<solidity::VariableDeclaration> const _v); |
|||
QVariableDeclaration(QObject* _parent, std::string const& _name, SolidityType const& _type, bool _isIndexed = false); |
|||
QVariableDeclaration(QObject* _parent, std::string const& _name, solidity::Type const* _type, bool _isIndexed = false); |
|||
QSolidityType* type() const { return m_type; } |
|||
void setType(QSolidityType* _type) { m_type = _type; } |
|||
bool isIndexed() { return m_isIndexed; } |
|||
|
|||
private: |
|||
QSolidityType* m_type; |
|||
bool m_isIndexed; |
|||
}; |
|||
|
|||
|
|||
} |
|||
} |
@ -1,37 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file QVariableDefinition.cpp
|
|||
* @author Yann yann@ethdev.com |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#include "QVariableDefinition.h" |
|||
#include <libdevcore/CommonJS.h> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace mix |
|||
{ |
|||
|
|||
QString QVariableDefinition::encodeValueAsString() |
|||
{ |
|||
return QString::fromStdString(dev::toHex(encodeValue())); |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
@ -1,71 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file QVariableDefinition.h
|
|||
* @author Yann yann@ethdev.com |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <QObject> |
|||
#include <libdevcore/Common.h> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace mix |
|||
{ |
|||
class QVariableDeclaration; |
|||
|
|||
class QVariableDefinition: public QObject |
|||
{ |
|||
Q_OBJECT |
|||
|
|||
Q_PROPERTY(QString value READ value CONSTANT) |
|||
Q_PROPERTY(QVariableDeclaration* declaration READ declaration CONSTANT) |
|||
|
|||
public: |
|||
QVariableDefinition() {} |
|||
QVariableDefinition(QVariableDeclaration* _def, QString _value): QObject(), m_value(_value), m_dec(_def) {} |
|||
|
|||
/// Return the associated declaration of this variable definition. Invokable from QML.
|
|||
Q_INVOKABLE QVariableDeclaration* declaration() const { return m_dec; } |
|||
/// Return the variable value.
|
|||
QString value() const { return m_value; } |
|||
/// Set a new value for this instance. Invokable from QML.
|
|||
Q_INVOKABLE void setValue(QString _value) { m_value = _value; } |
|||
/// Set a new Declaration for this instance. Invokable from QML.
|
|||
Q_INVOKABLE void setDeclaration(QVariableDeclaration* _dec) { m_dec = _dec; } |
|||
/// Encode the current value in order to be used as function parameter.
|
|||
virtual bytes encodeValue() = 0; |
|||
/// Decode the return value @a _rawValue.
|
|||
virtual void decodeValue(dev::bytes const& _rawValue) = 0; |
|||
/// returns String representation of the encoded value.
|
|||
Q_INVOKABLE QString encodeValueAsString(); |
|||
|
|||
protected: |
|||
QString m_value; |
|||
|
|||
private: |
|||
QVariableDeclaration* m_dec; |
|||
}; |
|||
|
|||
|
|||
|
|||
|
|||
} |
|||
} |
|||
|
@ -1,74 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file SolidityType.h
|
|||
* @author Yann yann@ethdev.com |
|||
* @author Arkadiy Paronyan arkadiy@ethdev.com |
|||
* @date 2015 |
|||
* Ethereum IDE client. |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <QString> |
|||
#include <vector> |
|||
#include <libdevcore/Common.h> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace mix |
|||
{ |
|||
|
|||
struct SolidityDeclaration; |
|||
|
|||
//Type info extracted from solidity AST
|
|||
struct SolidityType |
|||
{ |
|||
enum Type //keep in sync with QSolidity::Type
|
|||
{ |
|||
SignedInteger, |
|||
UnsignedInteger, |
|||
Hash, //TODO: remove
|
|||
Bool, |
|||
Address, |
|||
Bytes, |
|||
String, |
|||
Enum, |
|||
Struct |
|||
}; |
|||
Type type; |
|||
unsigned size; //in bytes,
|
|||
unsigned count; |
|||
bool array; |
|||
bool dynamicSize; |
|||
QString name; |
|||
std::vector<SolidityDeclaration> members; //for struct
|
|||
std::vector<QString> enumNames; //for enum
|
|||
std::shared_ptr<SolidityType const> baseType; |
|||
}; |
|||
|
|||
struct SolidityDeclaration |
|||
{ |
|||
QString name; |
|||
SolidityType type; |
|||
dev::u256 slot; |
|||
unsigned offset; |
|||
}; |
|||
|
|||
using SolidityDeclarations = std::vector<SolidityDeclaration>; |
|||
|
|||
} |
|||
} |
@ -1,156 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/**
|
|||
* @author Yann <yann@ethdev.com> |
|||
* @date 2015 |
|||
* Proxy used to filter a QML TableView. |
|||
*/ |
|||
|
|||
|
|||
#include "SortFilterProxyModel.h" |
|||
#include <QtDebug> |
|||
#include <QtQml> |
|||
|
|||
using namespace dev::mix; |
|||
|
|||
SortFilterProxyModel::SortFilterProxyModel(QObject* _parent) : QSortFilterProxyModel(_parent) |
|||
{ |
|||
connect(this, &SortFilterProxyModel::rowsInserted, this, &SortFilterProxyModel::countChanged); |
|||
connect(this, &SortFilterProxyModel::rowsRemoved, this, &SortFilterProxyModel::countChanged); |
|||
} |
|||
|
|||
int SortFilterProxyModel::count() const |
|||
{ |
|||
return rowCount(); |
|||
} |
|||
|
|||
QObject* SortFilterProxyModel::source() const |
|||
{ |
|||
return sourceModel(); |
|||
} |
|||
|
|||
void SortFilterProxyModel::setSource(QObject* _source) |
|||
{ |
|||
setSourceModel(qobject_cast<QAbstractItemModel*>(_source)); |
|||
} |
|||
|
|||
QByteArray SortFilterProxyModel::sortRole() const |
|||
{ |
|||
return roleNames().value(QSortFilterProxyModel::sortRole()); |
|||
} |
|||
|
|||
void SortFilterProxyModel::setSortRole(QByteArray const& _role) |
|||
{ |
|||
QSortFilterProxyModel::setSortRole(roleKey(_role)); |
|||
} |
|||
|
|||
void SortFilterProxyModel::setSortOrder(Qt::SortOrder _order) |
|||
{ |
|||
QSortFilterProxyModel::sort(0, _order); |
|||
} |
|||
|
|||
QString SortFilterProxyModel::filterString() const |
|||
{ |
|||
return filterRegExp().pattern(); |
|||
} |
|||
|
|||
void SortFilterProxyModel::setFilterString(QString const& _filter) |
|||
{ |
|||
setFilterRegExp(QRegExp(_filter, filterCaseSensitivity(), static_cast<QRegExp::PatternSyntax>(filterSyntax()))); |
|||
} |
|||
|
|||
SortFilterProxyModel::FilterSyntax SortFilterProxyModel::filterSyntax() const |
|||
{ |
|||
return static_cast<FilterSyntax>(filterRegExp().patternSyntax()); |
|||
} |
|||
|
|||
void SortFilterProxyModel::setFilterSyntax(SortFilterProxyModel::FilterSyntax _syntax) |
|||
{ |
|||
setFilterRegExp(QRegExp(filterString(), filterCaseSensitivity(), static_cast<QRegExp::PatternSyntax>(_syntax))); |
|||
} |
|||
|
|||
QJSValue SortFilterProxyModel::get(int _idx) const |
|||
{ |
|||
QJSEngine *engine = qmlEngine(this); |
|||
QJSValue value = engine->newObject(); |
|||
if (_idx >= 0 && _idx < count()) |
|||
{ |
|||
QHash<int, QByteArray> roles = roleNames(); |
|||
QHashIterator<int, QByteArray> it(roles); |
|||
while (it.hasNext()) |
|||
{ |
|||
it.next(); |
|||
value.setProperty(QString::fromUtf8(it.value()), data(index(_idx, 0), it.key()).toString()); |
|||
} |
|||
} |
|||
return value; |
|||
} |
|||
|
|||
int SortFilterProxyModel::roleKey(QByteArray const& _role) const |
|||
{ |
|||
QHash<int, QByteArray> roles = roleNames(); |
|||
QHashIterator<int, QByteArray> it(roles); |
|||
while (it.hasNext()) |
|||
{ |
|||
it.next(); |
|||
if (it.value() == _role) |
|||
return it.key(); |
|||
} |
|||
return -1; |
|||
} |
|||
|
|||
QHash<int, QByteArray> SortFilterProxyModel::roleNames() const |
|||
{ |
|||
if (QAbstractItemModel* source = sourceModel()) |
|||
return source->roleNames(); |
|||
return QHash<int, QByteArray>(); |
|||
} |
|||
|
|||
bool SortFilterProxyModel::filterAcceptsRow(int _sourceRow, QModelIndex const& _sourceParent) const |
|||
{ |
|||
QAbstractItemModel* model = sourceModel(); |
|||
QModelIndex sourceIndex = model->index(_sourceRow, 0, _sourceParent); |
|||
if (!sourceIndex.isValid()) |
|||
return true; |
|||
|
|||
QString keyType = model->data(sourceIndex, roleKey(type.toUtf8())).toString(); |
|||
QString keyContent = model->data(sourceIndex, roleKey(content.toUtf8())).toString(); |
|||
return keyType.contains(m_filterType) && keyContent.contains(m_filterContent); |
|||
} |
|||
|
|||
void SortFilterProxyModel::setFilterType(QString const& _type) |
|||
{ |
|||
m_filterType = QRegExp(_type, filterCaseSensitivity(), static_cast<QRegExp::PatternSyntax>(filterSyntax())); |
|||
setFilterRegExp(_type); |
|||
} |
|||
|
|||
QString SortFilterProxyModel::filterType() const |
|||
{ |
|||
return m_filterType.pattern(); |
|||
} |
|||
|
|||
void SortFilterProxyModel::setFilterContent(QString const& _content) |
|||
{ |
|||
m_filterContent = QRegExp(_content, filterCaseSensitivity(), static_cast<QRegExp::PatternSyntax>(filterSyntax())); |
|||
setFilterRegExp(_content); |
|||
} |
|||
|
|||
QString SortFilterProxyModel::filterContent() const |
|||
{ |
|||
return m_filterContent.pattern(); |
|||
} |
|||
|
@ -1,97 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/**
|
|||
* @author Yann <yann@ethdev.com> |
|||
* @date 2015 |
|||
* Proxy used to filter a QML TableView. |
|||
*/ |
|||
|
|||
|
|||
#pragma once |
|||
|
|||
#include <QtCore/qsortfilterproxymodel.h> |
|||
#include <QtQml/qjsvalue.h> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace mix |
|||
{ |
|||
|
|||
class SortFilterProxyModel: public QSortFilterProxyModel |
|||
{ |
|||
Q_OBJECT |
|||
Q_PROPERTY(int count READ count NOTIFY countChanged) |
|||
Q_PROPERTY(QObject* source READ source WRITE setSource) |
|||
|
|||
Q_PROPERTY(QByteArray sortRole READ sortRole WRITE setSortRole) |
|||
Q_PROPERTY(Qt::SortOrder sortOrder READ sortOrder WRITE setSortOrder) |
|||
|
|||
Q_PROPERTY(QString filterContent READ filterContent WRITE setFilterContent) |
|||
Q_PROPERTY(QString filterType READ filterType WRITE setFilterType) |
|||
Q_PROPERTY(QString filterString READ filterString WRITE setFilterString) |
|||
Q_PROPERTY(FilterSyntax filterSyntax READ filterSyntax WRITE setFilterSyntax) |
|||
|
|||
Q_ENUMS(FilterSyntax) |
|||
|
|||
public: |
|||
explicit SortFilterProxyModel(QObject* _parent = 0); |
|||
|
|||
QObject* source() const; |
|||
void setSource(QObject* _source); |
|||
|
|||
QByteArray sortRole() const; |
|||
void setSortRole(QByteArray const& _role); |
|||
|
|||
void setSortOrder(Qt::SortOrder _order); |
|||
|
|||
QString filterContent() const; |
|||
void setFilterContent(QString const& _content); |
|||
QString filterType() const; |
|||
void setFilterType(QString const& _type); |
|||
|
|||
QString filterString() const; |
|||
void setFilterString(QString const& _filter); |
|||
|
|||
enum FilterSyntax { |
|||
RegExp, |
|||
Wildcard, |
|||
FixedString |
|||
}; |
|||
|
|||
FilterSyntax filterSyntax() const; |
|||
void setFilterSyntax(FilterSyntax _syntax); |
|||
|
|||
int count() const; |
|||
Q_INVOKABLE QJSValue get(int _index) const; |
|||
|
|||
signals: |
|||
void countChanged(); |
|||
|
|||
protected: |
|||
int roleKey(QByteArray const& _role) const; |
|||
QHash<int, QByteArray> roleNames() const; |
|||
bool filterAcceptsRow(int _sourceRow, QModelIndex const& _sourceParent) const; |
|||
|
|||
private: |
|||
QRegExp m_filterType; |
|||
QRegExp m_filterContent; |
|||
const QString type = "type"; |
|||
const QString content = "content"; |
|||
}; |
|||
|
|||
} |
|||
} |
@ -1,166 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file Web3Server.h.cpp
|
|||
* @author Arkadiy Paronyan arkadiy@ethdev.com |
|||
* @date 2014 |
|||
* Ethereum IDE client. |
|||
*/ |
|||
|
|||
#include <libdevcore/Exceptions.h> |
|||
#include <libdevcore/Log.h> |
|||
#include <libethereum/Interface.h> |
|||
#include <libwebthree/WebThree.h> |
|||
#include <libweb3jsonrpc/AccountHolder.h> |
|||
#include "Web3Server.h" |
|||
|
|||
using namespace dev::mix; |
|||
using namespace dev; |
|||
|
|||
namespace |
|||
{ |
|||
class EmptyNetwork : public dev::WebThreeNetworkFace |
|||
{ |
|||
std::vector<p2p::PeerSessionInfo> peers() override |
|||
{ |
|||
return std::vector<p2p::PeerSessionInfo>(); |
|||
} |
|||
|
|||
size_t peerCount() const override |
|||
{ |
|||
return 0; |
|||
} |
|||
|
|||
void addNode(p2p::NodeId const& _node, bi::tcp::endpoint const& _hostEndpoint) override |
|||
{ |
|||
(void)_node; |
|||
(void)_hostEndpoint; |
|||
} |
|||
|
|||
void requirePeer(p2p::NodeId const& _node, bi::tcp::endpoint const& _endpoint) override |
|||
{ |
|||
(void)_node; |
|||
(void)_endpoint; |
|||
} |
|||
|
|||
dev::bytes saveNetwork() override |
|||
{ |
|||
return dev::bytes(); |
|||
} |
|||
|
|||
void setIdealPeerCount(size_t _n) override |
|||
{ |
|||
(void)_n; |
|||
} |
|||
|
|||
bool haveNetwork() const override |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
p2p::NetworkPreferences const& networkPreferences() const override |
|||
{ |
|||
static const p2p::NetworkPreferences c_ret; |
|||
return c_ret; |
|||
} |
|||
|
|||
void setNetworkPreferences(p2p::NetworkPreferences const& _n, bool _dropPeers) override |
|||
{ |
|||
(void)_n; |
|||
(void)_dropPeers; |
|||
} |
|||
|
|||
p2p::NodeInfo nodeInfo() const override { return p2p::NodeInfo(); } |
|||
|
|||
std::string enode() const override { return ""; } |
|||
|
|||
p2p::NodeId id() const override |
|||
{ |
|||
return p2p::NodeId(); |
|||
} |
|||
|
|||
p2p::Peers nodes() const override |
|||
{ |
|||
return p2p::Peers(); |
|||
} |
|||
|
|||
void startNetwork() override |
|||
{ |
|||
} |
|||
|
|||
void stopNetwork() override |
|||
{ |
|||
} |
|||
|
|||
bool isNetworkStarted() const override |
|||
{ |
|||
return false; |
|||
} |
|||
}; |
|||
|
|||
} |
|||
|
|||
Web3Server::Web3Server(jsonrpc::AbstractServerConnector& _conn, std::shared_ptr<eth::AccountHolder> const& _ethAccounts, std::vector<dev::KeyPair> const& _shhAccounts, dev::eth::Interface* _client): |
|||
WebThreeStubServerBase(_conn, _ethAccounts, _shhAccounts), |
|||
m_client(_client), |
|||
m_network(new EmptyNetwork()) |
|||
{ |
|||
} |
|||
|
|||
Web3Server::~Web3Server() |
|||
{ |
|||
} |
|||
|
|||
std::shared_ptr<dev::shh::Interface> Web3Server::face() |
|||
{ |
|||
BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::shh::Interface")); |
|||
} |
|||
|
|||
dev::WebThreeNetworkFace* Web3Server::network() |
|||
{ |
|||
return m_network.get(); |
|||
} |
|||
|
|||
std::string Web3Server::get(std::string const& _name, std::string const& _key) |
|||
{ |
|||
std::string k(_name + "/" + _key); |
|||
return m_db[k]; |
|||
} |
|||
|
|||
void Web3Server::put(std::string const& _name, std::string const& _key, std::string const& _value) |
|||
{ |
|||
std::string k(_name + "/" + _key); |
|||
m_db[k] = _value; |
|||
} |
|||
|
|||
Json::Value Web3Server::eth_getFilterChanges(std::string const& _filterId) |
|||
{ |
|||
return WebThreeStubServerBase::eth_getFilterChanges(_filterId); |
|||
} |
|||
|
|||
std::string Web3Server::eth_sendTransaction(Json::Value const& _json) |
|||
{ |
|||
std::string ret = WebThreeStubServerBase::eth_sendTransaction(_json); |
|||
emit newTransaction(); |
|||
return ret; |
|||
} |
|||
|
|||
std::string Web3Server::eth_call(Json::Value const& _json, std::string const& _blockNumber) |
|||
{ |
|||
std::string ret = WebThreeStubServerBase::eth_call(_json, _blockNumber); |
|||
emit newTransaction(); |
|||
return ret; |
|||
} |
@ -1,70 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file Web3Server.h
|
|||
* @author Arkadiy Paronyan arkadiy@ethdev.com |
|||
* @date 2015 |
|||
* Ethereum IDE client. |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <map> |
|||
#include <string> |
|||
#include <QObject> |
|||
#include <libweb3jsonrpc/AccountHolder.h> |
|||
#include <libweb3jsonrpc/WebThreeStubServerBase.h> |
|||
|
|||
namespace dev |
|||
{ |
|||
|
|||
namespace mix |
|||
{ |
|||
|
|||
class Web3Server: public QObject, public dev::WebThreeStubServerBase, public dev::WebThreeStubDatabaseFace |
|||
{ |
|||
Q_OBJECT |
|||
|
|||
public: |
|||
Web3Server(jsonrpc::AbstractServerConnector& _conn, std::shared_ptr<eth::AccountHolder> const& _ethAccounts, std::vector<dev::KeyPair> const& _shhAccounts, dev::eth::Interface* _client); |
|||
virtual ~Web3Server(); |
|||
|
|||
signals: |
|||
void newTransaction(); |
|||
|
|||
protected: |
|||
virtual Json::Value eth_getFilterChanges(std::string const& _filterId) override; |
|||
virtual std::string eth_sendTransaction(Json::Value const& _json) override; |
|||
virtual std::string eth_call(Json::Value const& _json, std::string const& _blockNumber) override; |
|||
|
|||
private: |
|||
dev::eth::Interface* client() override { return m_client; } |
|||
std::shared_ptr<dev::shh::Interface> face() override; |
|||
dev::WebThreeNetworkFace* network() override; |
|||
dev::WebThreeStubDatabaseFace* db() override { return this; } |
|||
|
|||
std::string get(std::string const& _name, std::string const& _key) override; |
|||
void put(std::string const& _name, std::string const& _key, std::string const& _value) override; |
|||
|
|||
private: |
|||
dev::eth::Interface* m_client; |
|||
std::map<std::string, std::string> m_db; |
|||
std::unique_ptr<dev::WebThreeNetworkFace> m_network; |
|||
}; |
|||
|
|||
} |
|||
|
|||
} |
@ -1,47 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file main.cpp
|
|||
* @author Yann yann@ethdev.com |
|||
* @date 2014 |
|||
* Ethereum IDE client. |
|||
*/ |
|||
|
|||
#include <iostream> |
|||
#include <stdlib.h> |
|||
#include <boost/exception/exception.hpp> |
|||
#include <boost/exception/diagnostic_information.hpp> |
|||
#include "MixApplication.h" |
|||
|
|||
using namespace dev::mix; |
|||
|
|||
int main(int _argc, char* _argv[]) |
|||
{ |
|||
try |
|||
{ |
|||
MixApplication::initialize(); |
|||
MixApplication app(_argc, _argv); |
|||
return app.exec(); |
|||
} |
|||
catch (boost::exception const& _e) |
|||
{ |
|||
std::cerr << boost::diagnostic_information(_e); |
|||
} |
|||
catch (std::exception const& _e) |
|||
{ |
|||
std::cerr << _e.what(); |
|||
} |
|||
} |
@ -1,6 +0,0 @@ |
|||
<RCC> |
|||
<qresource prefix="/"> |
|||
<file alias="qml/WebPreview.qml">qml/WebPreviewStub.qml</file> |
|||
<file alias="qml/CodeEditor.qml">qml/CodeEditor.qml</file> |
|||
</qresource> |
|||
</RCC> |
@ -1,5 +0,0 @@ |
|||
<RCC> |
|||
<qresource prefix="/"> |
|||
<file alias="qml/QFileDialog.qml">qml/MacFileDialog.qml</file> |
|||
</qresource> |
|||
</RCC> |
@ -1,81 +0,0 @@ |
|||
<RCC> |
|||
<qresource prefix="/"> |
|||
<file>qml/AlertMessageDialog.qml</file> |
|||
<file>qml/Application.qml</file> |
|||
<file>qml/BasicMessage.qml</file> |
|||
<file>qml/BigIntValue.qml</file> |
|||
<file>qml/CallStack.qml</file> |
|||
<file>qml/CodeEditorStyle.qml</file> |
|||
<file>qml/CodeEditorView.qml</file> |
|||
<file>qml/CommonSeparator.qml</file> |
|||
<file>qml/DebugBasicInfo.qml</file> |
|||
<file>qml/DebugInfoList.qml</file> |
|||
<file>qml/Debugger.qml</file> |
|||
<file>qml/DebuggerPaneStyle.qml</file> |
|||
<file>qml/DefaultLabel.qml</file> |
|||
<file>qml/DefaultTextField.qml</file> |
|||
<file>qml/DeploymentDialog.qml</file> |
|||
<file>qml/Ether.qml</file> |
|||
<file>qml/EtherValue.qml</file> |
|||
<file>qml/FilesSection.qml</file> |
|||
<file>qml/ItemDelegateDataDump.qml</file> |
|||
<file>qml/LogsPane.qml</file> |
|||
<file>qml/LogsPaneStyle.qml</file> |
|||
<file>qml/MainContent.qml</file> |
|||
<file>qml/ModalDialog.qml</file> |
|||
<file>qml/NewProjectDialog.qml</file> |
|||
<file>qml/ProjectFilesStyle.qml</file> |
|||
<file>qml/ProjectList.qml</file> |
|||
<file>qml/ProjectModel.qml</file> |
|||
<file>qml/QBoolTypeView.qml</file> |
|||
<file>qml/QHashTypeView.qml</file> |
|||
<file>qml/QIntTypeView.qml</file> |
|||
<file>qml/QRealTypeView.qml</file> |
|||
<file>qml/QStringTypeView.qml</file> |
|||
<file>qml/QVariableDeclaration.qml</file> |
|||
<file>qml/QVariableDefinition.qml</file> |
|||
<file>qml/QFileDialog.qml</file> |
|||
<file>qml/SourceSansProBold.qml</file> |
|||
<file>qml/SourceSansProLight.qml</file> |
|||
<file>qml/SourceSansProRegular.qml</file> |
|||
<file>qml/Splitter.qml</file> |
|||
<file>qml/StateDialog.qml</file> |
|||
<file>qml/StateDialogStyle.qml</file> |
|||
<file>qml/StateList.qml</file> |
|||
<file>qml/StateListModel.qml</file> |
|||
<file>qml/StateStyle.qml</file> |
|||
<file>qml/StatusPane.qml</file> |
|||
<file>qml/StatusPaneStyle.qml</file> |
|||
<file>qml/StepActionImage.qml</file> |
|||
<file>qml/StorageView.qml</file> |
|||
<file>qml/StatesComboBox.qml</file> |
|||
<file>qml/StructView.qml</file> |
|||
<file>qml/Style.qml</file> |
|||
<file>qml/TabStyle.qml</file> |
|||
<file>qml/TransactionDialog.qml</file> |
|||
<file>qml/TransactionLog.qml</file> |
|||
<file>qml/VariablesView.qml</file> |
|||
<file>qml/WebPreviewStyle.qml</file> |
|||
<file>qml/js/Debugger.js</file> |
|||
<file>qml/js/ErrorLocationFormater.js</file> |
|||
<file>qml/js/ProjectModel.js</file> |
|||
<file>qml/js/QEtherHelper.js</file> |
|||
<file>qml/js/TransactionHelper.js</file> |
|||
<file>qml/js/Printer.js</file> |
|||
<file>qml/js/ansi2html.js</file> |
|||
<file>qml/js/NetworkDeployment.js</file> |
|||
<file>qml/js/InputValidator.js</file> |
|||
<file>qml/Block.qml</file> |
|||
<file>qml/BlockChain.qml</file> |
|||
<file>qml/ScenarioExecution.qml</file> |
|||
<file>qml/ScenarioLoader.qml</file> |
|||
<file>qml/ScenarioButton.qml</file> |
|||
<file>qml/Watchers.qml</file> |
|||
<file>qml/KeyValuePanel.qml</file> |
|||
<file>qml/DeployContractStep.qml</file> |
|||
<file>qml/RegisteringStep.qml</file> |
|||
<file>qml/DeploymentDialogSteps.qml</file> |
|||
<file>qml/PackagingStep.qml</file> |
|||
<file>qml/DeploymentWorker.qml</file> |
|||
</qresource> |
|||
</RCC> |
@ -1,30 +0,0 @@ |
|||
import QtQuick 2.2 |
|||
import QtQuick.Window 2.0 |
|||
|
|||
Window |
|||
{ |
|||
id: alertMessageDialog |
|||
title: "" |
|||
modality: Qt.WindowModal |
|||
height: 150 |
|||
width: 200 |
|||
visible: false |
|||
Loader |
|||
{ |
|||
focus: true |
|||
id: alertMessageDialogContent |
|||
objectName: "alertMessageDialogContent" |
|||
anchors.fill: parent |
|||
} |
|||
function open() |
|||
{ |
|||
visible = true |
|||
} |
|||
function close() |
|||
{ |
|||
visible = false; |
|||
alertMessageDialogContent.source = ""; |
|||
alertMessageDialogContent.sourceComponent = undefined; |
|||
alertMessageDialog.destroy(); |
|||
} |
|||
} |
@ -1,438 +0,0 @@ |
|||
import QtQuick 2.2 |
|||
import QtQuick.Controls 1.1 |
|||
import QtQuick.Controls.Styles 1.1 |
|||
import QtQuick.Dialogs 1.1 |
|||
import QtQuick.Layouts 1.1 |
|||
import QtQuick.Window 2.1 |
|||
import QtQuick.PrivateWidgets 1.1 |
|||
import Qt.labs.settings 1.0 |
|||
import org.ethereum.qml.QEther 1.0 |
|||
import org.ethereum.qml.CodeModel 1.0 |
|||
import org.ethereum.qml.ClientModel 1.0 |
|||
import org.ethereum.qml.FileIo 1.0 |
|||
import org.ethereum.qml.Clipboard 1.0 |
|||
import org.ethereum.qml.ApplicationService 1.0 |
|||
|
|||
ApplicationWindow { |
|||
|
|||
id: mainApplication |
|||
signal loaded; |
|||
visible: true |
|||
width: 1200 |
|||
height: 800 |
|||
minimumWidth: 400 |
|||
minimumHeight: 300 |
|||
title: qsTr("Mix") |
|||
property alias systemPointSize: appService.systemPointSize; |
|||
property alias mainContent: mainContent; |
|||
property alias codeModel: codeModel; |
|||
property alias clientModel: clientModel; |
|||
property alias projectModel: projectModel; |
|||
property alias appService: appService; |
|||
property bool trackLastProject: true; |
|||
|
|||
ApplicationService { |
|||
id: appService |
|||
} |
|||
|
|||
CodeModel { |
|||
id: codeModel |
|||
} |
|||
|
|||
ClientModel { |
|||
id: clientModel |
|||
codeModel: codeModel |
|||
Component.onCompleted: |
|||
{ |
|||
init("/tmp") |
|||
} |
|||
} |
|||
|
|||
ProjectModel { |
|||
id: projectModel |
|||
} |
|||
|
|||
FileIo { |
|||
id: fileIo |
|||
} |
|||
|
|||
Clipboard { |
|||
id: clipboard |
|||
} |
|||
|
|||
Style { |
|||
id: appStyle |
|||
} |
|||
|
|||
Connections { |
|||
target: mainApplication |
|||
onClosing: |
|||
{ |
|||
mainApplication.close(); |
|||
close.accepted = false; |
|||
} |
|||
} |
|||
|
|||
Component.onCompleted: { |
|||
loaded(); |
|||
} |
|||
|
|||
function close() { |
|||
projectModel.appIsClosing = true; |
|||
if (projectModel.projectPath !== "") |
|||
projectModel.closeProject(function() { Qt.quit(); }) |
|||
else |
|||
Qt.quit(); |
|||
} |
|||
|
|||
menuBar: MenuBar { |
|||
Menu { |
|||
title: qsTr("File") |
|||
MenuItem { action: createProjectAction } |
|||
MenuItem { action: openProjectAction } |
|||
MenuSeparator {} |
|||
MenuItem { action: saveAllFilesAction } |
|||
MenuItem { action: saveCurrentDocument } |
|||
MenuSeparator {} |
|||
MenuItem { action: addExistingFileAction } |
|||
MenuItem { action: addNewJsFileAction } |
|||
MenuItem { action: addNewHtmlFileAction } |
|||
MenuItem { action: addNewCssFileAction } |
|||
MenuSeparator {} |
|||
MenuItem { action: addNewContractAction } |
|||
MenuItem { action: closeProjectAction } |
|||
MenuSeparator {} |
|||
MenuItem { action: exitAppAction } |
|||
} |
|||
Menu { |
|||
title: qsTr("Deploy") |
|||
MenuItem { action: mineAction } |
|||
MenuSeparator {} |
|||
MenuItem { action: deployViaRpcAction } |
|||
MenuSeparator {} |
|||
MenuItem { action: toggleRunOnLoadAction } |
|||
} |
|||
Menu { |
|||
title: qsTr("Scenario") |
|||
MenuItem { action: editStatesAction } |
|||
} |
|||
Menu { |
|||
title: qsTr("Debug") |
|||
MenuItem { action: debugRunAction } |
|||
MenuSeparator {} |
|||
MenuItem { action: toggleAssemblyDebuggingAction } |
|||
} |
|||
Menu { |
|||
title: qsTr("Tools") |
|||
MenuItem { action: gasEstimationAction } |
|||
MenuItem { action: optimizeCodeAction } |
|||
} |
|||
Menu { |
|||
title: qsTr("Windows") |
|||
MenuItem { action: openNextDocumentAction } |
|||
MenuItem { action: openPrevDocumentAction } |
|||
MenuSeparator {} |
|||
MenuItem { action: toggleProjectNavigatorAction } |
|||
MenuItem { action: showHideRightPanelAction } |
|||
MenuItem { action: toggleWebPreviewAction } |
|||
MenuItem { action: toggleWebPreviewOrientationAction } |
|||
} |
|||
} |
|||
|
|||
MainContent { |
|||
id: mainContent; |
|||
anchors.fill: parent |
|||
} |
|||
|
|||
ModalDialog { |
|||
objectName: "dialog" |
|||
id: dialog |
|||
} |
|||
|
|||
AlertMessageDialog { |
|||
objectName: "alertMessageDialog" |
|||
id: messageDialog |
|||
} |
|||
|
|||
Settings { |
|||
id: mainWindowSettings |
|||
property alias mainWidth: mainApplication.width |
|||
property alias mainHeight: mainApplication.height |
|||
property alias mainX: mainApplication.x |
|||
property alias mainY: mainApplication.y |
|||
} |
|||
|
|||
Action { |
|||
id: exitAppAction |
|||
text: qsTr("Exit") |
|||
shortcut: "Ctrl+Q" |
|||
onTriggered: |
|||
{ |
|||
mainApplication.close(); |
|||
} |
|||
} |
|||
|
|||
Action { |
|||
id: mineAction |
|||
text: qsTr("New Block") |
|||
shortcut: "Ctrl+M" |
|||
onTriggered: clientModel.mine(); |
|||
enabled: codeModel.hasContract && !clientModel.running && !clientModel.mining |
|||
} |
|||
|
|||
StateList { |
|||
id: stateList |
|||
} |
|||
|
|||
Action { |
|||
id: editStatesAction |
|||
text: qsTr("Edit Scenarii") |
|||
shortcut: "Ctrl+Alt+E" |
|||
onTriggered: stateList.open(); |
|||
} |
|||
|
|||
Connections { |
|||
target: projectModel.stateListModel |
|||
|
|||
function updateRunLabel() |
|||
{ |
|||
debugRunAction.text = qsTr("Deploy") + " \"" + projectModel.stateListModel.defaultStateName() + "\""; |
|||
} |
|||
|
|||
onDefaultStateChanged: updateRunLabel() |
|||
onStateListModelReady: updateRunLabel() |
|||
} |
|||
|
|||
Action { |
|||
id: debugRunAction |
|||
text: qsTr("Deploy") |
|||
shortcut: "F5" |
|||
onTriggered: mainContent.startQuickDebugging() |
|||
enabled: codeModel.hasContract && !clientModel.running && projectModel.stateListModel.defaultStateName() !== "" |
|||
} |
|||
|
|||
Action { |
|||
id: toggleAssemblyDebuggingAction |
|||
text: qsTr("Show VM Code") |
|||
shortcut: "Ctrl+Alt+V" |
|||
onTriggered: mainContent.debuggerPanel.assemblyMode = !mainContent.debuggerPanel.assemblyMode; |
|||
checked: mainContent.debuggerPanel.assemblyMode; |
|||
checkable: true |
|||
enabled: true |
|||
} |
|||
|
|||
Action { |
|||
id: toggleWebPreviewAction |
|||
text: qsTr("Show Web View") |
|||
shortcut: "F2" |
|||
checkable: true |
|||
checked: mainContent.webViewVisible |
|||
onTriggered: mainContent.toggleWebPreview(); |
|||
} |
|||
|
|||
Action { |
|||
id: toggleProjectNavigatorAction |
|||
text: qsTr("Show Project Navigator") |
|||
shortcut: "Alt+0" |
|||
checkable: true |
|||
checked: mainContent.projectViewVisible |
|||
onTriggered: mainContent.toggleProjectView(); |
|||
} |
|||
|
|||
Action { |
|||
id: toggleWebPreviewOrientationAction |
|||
text: qsTr("Horizontal Web View") |
|||
shortcut: "" |
|||
checkable: true |
|||
checked: mainContent.webViewHorizontal |
|||
onTriggered: mainContent.toggleWebPreviewOrientation(); |
|||
} |
|||
|
|||
Action { |
|||
id: toggleRunOnLoadAction |
|||
text: qsTr("Load State on Startup") |
|||
shortcut: "" |
|||
checkable: true |
|||
checked: mainContent.runOnProjectLoad |
|||
onTriggered: mainContent.runOnProjectLoad = !mainContent.runOnProjectLoad |
|||
} |
|||
|
|||
Action { |
|||
id: showHideRightPanelAction |
|||
text: qsTr("Show Right View") |
|||
shortcut: "F7" |
|||
checkable: true |
|||
checked: mainContent.rightViewVisible |
|||
onTriggered: mainContent.toggleRightView(); |
|||
} |
|||
|
|||
Action { |
|||
id: createProjectAction |
|||
text: qsTr("&New Project") |
|||
shortcut: "Ctrl+N" |
|||
enabled: true; |
|||
onTriggered: projectModel.createProject(); |
|||
} |
|||
|
|||
Action { |
|||
id: openProjectAction |
|||
text: qsTr("&Open Project") |
|||
shortcut: "Ctrl+O" |
|||
enabled: true; |
|||
onTriggered: openProjectFileDialog.open() |
|||
} |
|||
|
|||
QFileDialog { |
|||
id: openProjectFileDialog |
|||
visible: false |
|||
title: qsTr("Open a Project") |
|||
selectFolder: true |
|||
selectExisting: true |
|||
onAccepted: { |
|||
var path = openProjectFileDialog.fileUrl.toString(); |
|||
path += "/"; |
|||
projectModel.loadProject(path); |
|||
} |
|||
} |
|||
|
|||
Action { |
|||
id: addNewJsFileAction |
|||
text: qsTr("New JavaScript File") |
|||
shortcut: "Ctrl+Alt+J" |
|||
enabled: !projectModel.isEmpty |
|||
onTriggered: projectModel.newJsFile(); |
|||
} |
|||
|
|||
Action { |
|||
id: addNewHtmlFileAction |
|||
text: qsTr("New HTML File") |
|||
shortcut: "Ctrl+Alt+H" |
|||
enabled: !projectModel.isEmpty |
|||
onTriggered: projectModel.newHtmlFile(); |
|||
} |
|||
|
|||
Action { |
|||
id: addNewCssFileAction |
|||
text: qsTr("New CSS File") |
|||
shortcut: "Ctrl+Alt+S" |
|||
enabled: !projectModel.isEmpty |
|||
onTriggered: projectModel.newCssFile(); |
|||
} |
|||
|
|||
Action { |
|||
id: addNewContractAction |
|||
text: qsTr("New Contract") |
|||
shortcut: "Ctrl+Alt+C" |
|||
enabled: !projectModel.isEmpty |
|||
onTriggered: projectModel.newContract(); |
|||
} |
|||
|
|||
Action { |
|||
id: addExistingFileAction |
|||
text: qsTr("Add Existing File") |
|||
shortcut: "Ctrl+Alt+A" |
|||
enabled: !projectModel.isEmpty |
|||
onTriggered: addExistingFileDialog.open() |
|||
} |
|||
|
|||
QFileDialog { |
|||
id: addExistingFileDialog |
|||
visible: false |
|||
title: qsTr("Add a File") |
|||
selectFolder: false |
|||
selectExisting: true |
|||
onAccepted: { |
|||
var paths = addExistingFileDialog.fileUrls; |
|||
projectModel.addExistingFiles(paths); |
|||
} |
|||
} |
|||
|
|||
Action { |
|||
id: saveAllFilesAction |
|||
text: qsTr("Save All") |
|||
shortcut: "Ctrl+Shift+A" |
|||
enabled: !projectModel.isEmpty |
|||
onTriggered: projectModel.saveAll(); |
|||
} |
|||
|
|||
Action { |
|||
id: saveCurrentDocument |
|||
text: qsTr("Save Current Document") |
|||
shortcut: "Ctrl+S" |
|||
enabled: !projectModel.isEmpty |
|||
onTriggered: projectModel.saveCurrentDocument(); |
|||
} |
|||
|
|||
Action { |
|||
id: closeProjectAction |
|||
text: qsTr("Close Project") |
|||
shortcut: "Ctrl+W" |
|||
enabled: !projectModel.isEmpty |
|||
onTriggered: projectModel.closeProject(); |
|||
} |
|||
|
|||
Action { |
|||
id: openNextDocumentAction |
|||
text: qsTr("Next Document") |
|||
shortcut: "Ctrl+Tab" |
|||
enabled: !projectModel.isEmpty |
|||
onTriggered: projectModel.openNextDocument(); |
|||
} |
|||
|
|||
Action { |
|||
id: openPrevDocumentAction |
|||
text: qsTr("Previous Document") |
|||
shortcut: "Ctrl+Shift+Tab" |
|||
enabled: !projectModel.isEmpty |
|||
onTriggered: projectModel.openPrevDocument(); |
|||
} |
|||
|
|||
Action { |
|||
id: toggleBreakpointAction |
|||
text: qsTr("Toggle Breakpoint") |
|||
shortcut: "F9" |
|||
enabled: mainContent.codeEditor.editingContract(); |
|||
onTriggered: mainContent.toggleBreakpoint(); |
|||
} |
|||
|
|||
Action { |
|||
id: deployViaRpcAction |
|||
text: qsTr("Deploy to Network") |
|||
shortcut: "Ctrl+Shift+D" |
|||
enabled: !projectModel.isEmpty && codeModel.hasContract |
|||
onTriggered: projectModel.deployProject(); |
|||
} |
|||
|
|||
Action { |
|||
id: goToCompilationError |
|||
text: qsTr("Go to compilation error") |
|||
shortcut: "F4" |
|||
onTriggered: |
|||
{ |
|||
mainContent.codeEditor.goToCompilationError(); |
|||
} |
|||
} |
|||
|
|||
Action { |
|||
id: gasEstimationAction |
|||
text: qsTr("Display gas estimation") |
|||
shortcut: "Ctrl+G" |
|||
checkable: true |
|||
onTriggered: mainContent.codeEditor.displayGasEstimation(checked); |
|||
} |
|||
|
|||
Action { |
|||
id: optimizeCodeAction |
|||
text: qsTr("Enable optimized compilation") |
|||
shortcut: "Ctrl+Shift+O" |
|||
checkable: true |
|||
onTriggered: codeModel.setOptimizeCode(checked); |
|||
} |
|||
|
|||
Settings { |
|||
id: appSettings |
|||
property alias gasEstimation: gasEstimationAction.checked |
|||
property alias optimizeCode: optimizeCodeAction.checked |
|||
property string nodeAddress: "http://localhost:8545" |
|||
} |
|||
} |
@ -1,22 +0,0 @@ |
|||
import QtQuick 2.2 |
|||
import QtQuick.Controls 1.1 |
|||
import QtQuick.Controls.Styles 1.1 |
|||
import QtQuick.Dialogs 1.1 |
|||
import QtQuick.Layouts 1.1 |
|||
|
|||
Rectangle { |
|||
anchors.fill: parent |
|||
color: "lightgrey" |
|||
Label |
|||
{ |
|||
width: parent.width |
|||
height: parent.height |
|||
horizontalAlignment: "AlignHCenter" |
|||
verticalAlignment: "AlignVCenter" |
|||
objectName: "messageContent" |
|||
id: messageTxt |
|||
text: "" |
|||
wrapMode: "Wrap" |
|||
} |
|||
} |
|||
|
@ -1,13 +0,0 @@ |
|||
/* |
|||
* Used to instanciate a QEther obj using Qt.createComponent function. |
|||
*/ |
|||
import QtQuick 2.2 |
|||
import QtQuick.Controls 1.1 |
|||
import QtQuick.Layouts 1.1 |
|||
import QtQuick.Controls.Styles 1.1 |
|||
import org.ethereum.qml.QBigInt 1.0 |
|||
|
|||
QBigInt |
|||
{ |
|||
id: bigInt |
|||
} |
@ -1,378 +0,0 @@ |
|||
import QtQuick 2.2 |
|||
import QtQuick.Controls 1.1 |
|||
import QtQuick.Controls.Styles 1.1 |
|||
import QtQuick.Dialogs 1.1 |
|||
import QtQuick.Layouts 1.1 |
|||
import Qt.labs.settings 1.0 |
|||
import "js/Debugger.js" as Debugger |
|||
import "js/ErrorLocationFormater.js" as ErrorLocationFormater |
|||
import "." |
|||
|
|||
ColumnLayout |
|||
{ |
|||
id: root |
|||
property variant transactions |
|||
property variant transactionModel |
|||
property string status |
|||
property int number |
|||
property int blockWidth: Layout.preferredWidth - statusWidth - horizontalMargin |
|||
property int horizontalMargin: 10 |
|||
property int trHeight: 35 |
|||
spacing: 0 |
|||
property int openedTr: 0 |
|||
property int blockIndex |
|||
property variant scenario |
|||
property string labelColor: "#414141" |
|||
|
|||
|
|||
property string selectedBlockColor: "#accbf2" |
|||
property string selectedBlockForeground: "#445e7f" |
|||
|
|||
property int scenarioIndex |
|||
signal txSelected(var txIndex) |
|||
|
|||
function calculateHeight() |
|||
{ |
|||
if (transactions) |
|||
{ |
|||
if (index >= 0) |
|||
return trHeight + trHeight * transactions.count + openedTr |
|||
else |
|||
return trHeight |
|||
} |
|||
else |
|||
return trHeight |
|||
} |
|||
|
|||
function editTx(txIndex) |
|||
{ |
|||
transactionDialog.stateAccounts = scenario.accounts |
|||
transactionDialog.execute = false |
|||
transactionDialog.editMode = true |
|||
transactionDialog.open(txIndex, blockIndex, transactions.get(txIndex)) |
|||
} |
|||
|
|||
function select(txIndex) |
|||
{ |
|||
transactionRepeater.itemAt(txIndex).select() |
|||
} |
|||
|
|||
onOpenedTrChanged: |
|||
{ |
|||
Layout.preferredHeight = calculateHeight() |
|||
height = calculateHeight() |
|||
} |
|||
|
|||
DebuggerPaneStyle { |
|||
id: dbgStyle |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
id: top |
|||
Layout.preferredWidth: blockWidth |
|||
height: 10 |
|||
anchors.bottom: rowHeader.top |
|||
color: "#DEDCDC" |
|||
radius: 15 |
|||
anchors.left: parent.left |
|||
anchors.leftMargin: statusWidth |
|||
anchors.bottomMargin: -5 |
|||
} |
|||
|
|||
RowLayout |
|||
{ |
|||
Layout.preferredHeight: trHeight |
|||
Layout.preferredWidth: blockWidth |
|||
id: rowHeader |
|||
spacing: 0 |
|||
Rectangle |
|||
{ |
|||
Layout.preferredWidth: blockWidth |
|||
Layout.preferredHeight: trHeight |
|||
color: "#DEDCDC" |
|||
anchors.left: parent.left |
|||
anchors.leftMargin: statusWidth |
|||
Label { |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
anchors.left: parent.left |
|||
anchors.leftMargin: horizontalMargin |
|||
font.pointSize: dbgStyle.absoluteSize(1) |
|||
color: "#adadad" |
|||
text: |
|||
{ |
|||
if (number === -2) |
|||
return qsTr("GENESIS PARAMETERS") |
|||
else if (status === "mined") |
|||
return qsTr("BLOCK") + " " + number |
|||
else |
|||
return qsTr("PENDING TRANSACTIONS") |
|||
} |
|||
} |
|||
|
|||
Image { |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
anchors.right: parent.right |
|||
anchors.rightMargin: 14 |
|||
visible: number === -2 |
|||
source: "qrc:/qml/img/edit_combox.png" |
|||
height: 15 |
|||
fillMode: Image.PreserveAspectFit |
|||
|
|||
MouseArea |
|||
{ |
|||
anchors.fill: parent |
|||
onClicked: |
|||
{ |
|||
// load edit block panel |
|||
projectModel.stateListModel.editState(scenarioIndex) |
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
|||
} |
|||
|
|||
Repeater // List of transactions |
|||
{ |
|||
id: transactionRepeater |
|||
model: transactions |
|||
RowLayout |
|||
{ |
|||
id: rowTransaction |
|||
Layout.preferredHeight: trHeight |
|||
spacing: 0 |
|||
|
|||
function select() |
|||
{ |
|||
rowContentTr.select() |
|||
} |
|||
|
|||
function displayContent() |
|||
{ |
|||
logsText.text = "" |
|||
if (index >= 0 && transactions.get(index).logs && transactions.get(index).logs.count) |
|||
{ |
|||
for (var k = 0; k < transactions.get(index).logs.count; k++) |
|||
{ |
|||
var log = transactions.get(index).logs.get(k) |
|||
if (log.name) |
|||
logsText.text += log.name + ":\n" |
|||
else |
|||
logsText.text += "log:\n" |
|||
|
|||
if (log.param) |
|||
for (var i = 0; i < log.param.count; i++) |
|||
{ |
|||
var p = log.param.get(i) |
|||
logsText.text += p.name + " = " + p.value + " - indexed:" + p.indexed + "\n" |
|||
} |
|||
else { |
|||
logsText.text += "From : " + log.address + "\n" |
|||
} |
|||
} |
|||
logsText.text += "\n\n" |
|||
} |
|||
rowDetailedContent.visible = !rowDetailedContent.visible |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
id: trSaveStatus |
|||
Layout.preferredWidth: statusWidth |
|||
Layout.preferredHeight: parent.height |
|||
color: "transparent" |
|||
anchors.top: parent.top |
|||
property bool saveStatus |
|||
Image { |
|||
anchors.top: parent.top |
|||
anchors.left: parent.left |
|||
anchors.leftMargin: -4 |
|||
anchors.topMargin: 0 |
|||
id: saveStatusImage |
|||
source: "qrc:/qml/img/recyclediscard@2x.png" |
|||
width: statusWidth + 10 |
|||
fillMode: Image.PreserveAspectFit |
|||
} |
|||
|
|||
Component.onCompleted: |
|||
{ |
|||
if (index >= 0) |
|||
saveStatus = transactions.get(index).saveStatus |
|||
} |
|||
|
|||
onSaveStatusChanged: |
|||
{ |
|||
if (saveStatus) |
|||
saveStatusImage.source = "qrc:/qml/img/recyclekeep@2x.png" |
|||
else |
|||
saveStatusImage.source = "qrc:/qml/img/recyclediscard@2x.png" |
|||
|
|||
if (index >= 0) |
|||
transactions.get(index).saveStatus = saveStatus |
|||
transactionModel[index].saveStatus = saveStatus |
|||
} |
|||
|
|||
MouseArea { |
|||
id: statusMouseArea |
|||
anchors.fill: parent |
|||
onClicked: |
|||
{ |
|||
parent.saveStatus = !parent.saveStatus |
|||
} |
|||
} |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
Layout.preferredWidth: blockWidth |
|||
Layout.preferredHeight: trHeight |
|||
height: trHeight |
|||
color: "#DEDCDC" |
|||
id: rowContentTr |
|||
anchors.top: parent.top |
|||
|
|||
property bool selected: false |
|||
Connections |
|||
{ |
|||
target: blockChainPanel |
|||
onTxSelected: { |
|||
if (root.blockIndex !== blockIndex || index !== txIndex) |
|||
rowContentTr.deselect() |
|||
} |
|||
} |
|||
|
|||
function select() |
|||
{ |
|||
rowContentTr.selected = true |
|||
rowContentTr.color = selectedBlockColor |
|||
hash.color = selectedBlockForeground |
|||
func.color = selectedBlockForeground |
|||
txSelected(index) |
|||
|
|||
} |
|||
|
|||
function deselect() |
|||
{ |
|||
rowContentTr.selected = false |
|||
rowContentTr.color = "#DEDCDC" |
|||
hash.color = labelColor |
|||
func.color = labelColor |
|||
} |
|||
|
|||
MouseArea |
|||
{ |
|||
anchors.fill: parent |
|||
onClicked: { |
|||
if (!rowContentTr.selected) |
|||
rowContentTr.select() |
|||
else |
|||
rowContentTr.deselect() |
|||
|
|||
} |
|||
onDoubleClicked: |
|||
{ |
|||
root.editTx(index) |
|||
} |
|||
} |
|||
|
|||
RowLayout |
|||
{ |
|||
Layout.fillWidth: true |
|||
Layout.preferredHeight: trHeight - 10 |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
Rectangle |
|||
{ |
|||
Layout.preferredWidth: fromWidth |
|||
anchors.left: parent.left |
|||
anchors.leftMargin: horizontalMargin |
|||
Text |
|||
{ |
|||
id: hash |
|||
width: parent.width - 30 |
|||
elide: Text.ElideRight |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
maximumLineCount: 1 |
|||
color: labelColor |
|||
font.pointSize: dbgStyle.absoluteSize(1) |
|||
font.bold: true |
|||
text: { |
|||
if (index >= 0) |
|||
return clientModel.resolveAddress(transactions.get(index).sender) |
|||
else |
|||
return "" |
|||
} |
|||
} |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
Layout.preferredWidth: toWidth |
|||
Text |
|||
{ |
|||
id: func |
|||
text: { |
|||
if (index >= 0) |
|||
parent.parent.userFrienldyToken(transactions.get(index).label) |
|||
else |
|||
return "" |
|||
} |
|||
elide: Text.ElideRight |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
color: labelColor |
|||
font.pointSize: dbgStyle.absoluteSize(1) |
|||
font.bold: true |
|||
maximumLineCount: 1 |
|||
width: parent.width |
|||
} |
|||
} |
|||
|
|||
function userFrienldyToken(value) |
|||
{ |
|||
if (value && value.indexOf("<") === 0) |
|||
{ |
|||
if (value.split("> ")[1] === " - ") |
|||
return value.split(" - ")[0].replace("<", "") |
|||
else |
|||
return value.split(" - ")[0].replace("<", "") + "." + value.split("> ")[1] + "()"; |
|||
} |
|||
else |
|||
return value |
|||
} |
|||
} |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
width: debugActionWidth |
|||
height: trHeight - 10 |
|||
anchors.right: rowContentTr.right |
|||
anchors.top: rowContentTr.top |
|||
anchors.rightMargin: 10 |
|||
color: "transparent" |
|||
|
|||
Image { |
|||
id: debugImg |
|||
source: "qrc:/qml/img/rightarrowcircle.png" |
|||
width: debugActionWidth |
|||
fillMode: Image.PreserveAspectFit |
|||
anchors.horizontalCenter: parent.horizontalCenter |
|||
visible: transactions.get(index).recordIndex !== undefined |
|||
} |
|||
MouseArea |
|||
{ |
|||
anchors.fill: parent |
|||
onClicked: |
|||
{ |
|||
if (transactions.get(index).recordIndex !== undefined) |
|||
{ |
|||
debugTrRequested = [ blockIndex, index ] |
|||
clientModel.debugRecord(transactions.get(index).recordIndex); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
@ -1,664 +0,0 @@ |
|||
import QtQuick 2.2 |
|||
import QtQuick.Controls 1.1 |
|||
import QtQuick.Controls.Styles 1.1 |
|||
import QtQuick.Dialogs 1.1 |
|||
import QtQuick.Layouts 1.1 |
|||
import Qt.labs.settings 1.0 |
|||
import org.ethereum.qml.QEther 1.0 |
|||
import "js/Debugger.js" as Debugger |
|||
import "js/ErrorLocationFormater.js" as ErrorLocationFormater |
|||
import "js/TransactionHelper.js" as TransactionHelper |
|||
import "js/QEtherHelper.js" as QEtherHelper |
|||
import "." |
|||
|
|||
ColumnLayout { |
|||
id: blockChainPanel |
|||
property alias trDialog: transactionDialog |
|||
property alias blockChainRepeater: blockChainRepeater |
|||
property variant model |
|||
property int scenarioIndex |
|||
property var states: ({}) |
|||
spacing: 0 |
|||
property int previousWidth |
|||
property variant debugTrRequested: [] |
|||
signal chainChanged(var blockIndex, var txIndex, var item) |
|||
signal chainReloaded |
|||
signal txSelected(var blockIndex, var txIndex) |
|||
signal rebuilding |
|||
signal accountAdded(string address, string amount) |
|||
|
|||
Connections |
|||
{ |
|||
target: projectModel.stateListModel |
|||
onAccountsValidated: |
|||
{ |
|||
if (rebuild.accountsSha3 !== codeModel.sha3(JSON.stringify(_accounts))) |
|||
rebuild.needRebuild("AccountsChanged") |
|||
else |
|||
rebuild.notNeedRebuild("AccountsChanged") |
|||
} |
|||
onContractsValidated: |
|||
{ |
|||
if (rebuild.contractsSha3 !== codeModel.sha3(JSON.stringify(_contracts))) |
|||
rebuild.needRebuild("ContractsChanged") |
|||
else |
|||
rebuild.notNeedRebuild("ContractsChanged") |
|||
} |
|||
} |
|||
|
|||
Connections |
|||
{ |
|||
target: codeModel |
|||
onContractRenamed: { |
|||
rebuild.needRebuild("ContractRenamed") |
|||
} |
|||
onNewContractCompiled: { |
|||
rebuild.needRebuild("NewContractCompiled") |
|||
} |
|||
onCompilationComplete: { |
|||
for (var c in rebuild.contractsHex) |
|||
{ |
|||
if (codeModel.contracts[c] === undefined || codeModel.contracts[c].codeHex !== rebuild.contractsHex[c]) |
|||
{ |
|||
if (!rebuild.containsRebuildCause("CodeChanged")) |
|||
{ |
|||
rebuild.needRebuild("CodeChanged") |
|||
} |
|||
return |
|||
} |
|||
} |
|||
rebuild.notNeedRebuild("CodeChanged") |
|||
} |
|||
} |
|||
|
|||
|
|||
onChainChanged: { |
|||
if (rebuild.txSha3[blockIndex][txIndex] !== codeModel.sha3(JSON.stringify(model.blocks[blockIndex].transactions[txIndex]))) |
|||
{ |
|||
rebuild.txChanged.push(rebuild.txSha3[blockIndex][txIndex]) |
|||
rebuild.needRebuild("txChanged") |
|||
} |
|||
else { |
|||
for (var k in rebuild.txChanged) |
|||
{ |
|||
if (rebuild.txChanged[k] === rebuild.txSha3[blockIndex][txIndex]) |
|||
{ |
|||
rebuild.txChanged.splice(k, 1) |
|||
break |
|||
} |
|||
} |
|||
if (rebuild.txChanged.length === 0) |
|||
rebuild.notNeedRebuild("txChanged") |
|||
} |
|||
} |
|||
|
|||
onWidthChanged: |
|||
{ |
|||
var minWidth = scenarioMinWidth - 20 // margin |
|||
if (width <= minWidth || previousWidth <= minWidth) |
|||
{ |
|||
fromWidth = 250 |
|||
toWidth = 240 |
|||
} |
|||
else |
|||
{ |
|||
var diff = (width - previousWidth) / 3; |
|||
fromWidth = fromWidth + diff < 250 ? 250 : fromWidth + diff |
|||
toWidth = toWidth + diff < 240 ? 240 : toWidth + diff |
|||
} |
|||
previousWidth = width |
|||
} |
|||
|
|||
function getState(record) |
|||
{ |
|||
return states[record] |
|||
} |
|||
|
|||
function load(scenario, index) |
|||
{ |
|||
if (!scenario) |
|||
return; |
|||
if (model) |
|||
rebuild.startBlinking() |
|||
model = scenario |
|||
scenarioIndex = index |
|||
genesis.scenarioIndex = index |
|||
states = [] |
|||
blockModel.clear() |
|||
for (var b in model.blocks) |
|||
blockModel.append(model.blocks[b]) |
|||
previousWidth = width |
|||
} |
|||
|
|||
property int statusWidth: 30 |
|||
property int fromWidth: 250 |
|||
property int toWidth: 240 |
|||
property int debugActionWidth: 40 |
|||
property int horizontalMargin: 10 |
|||
property int cellSpacing: 10 |
|||
|
|||
RowLayout |
|||
{ |
|||
Layout.preferredHeight: 10 |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
Layout.preferredHeight: 500 |
|||
Layout.preferredWidth: parent.width |
|||
border.color: "#cccccc" |
|||
border.width: 2 |
|||
color: "white" |
|||
ScrollView |
|||
{ |
|||
id: blockChainScrollView |
|||
anchors.fill: parent |
|||
anchors.topMargin: 8 |
|||
ColumnLayout |
|||
{ |
|||
id: blockChainLayout |
|||
width: parent.width |
|||
spacing: 20 |
|||
|
|||
Block |
|||
{ |
|||
id: genesis |
|||
scenario: blockChainPanel.model |
|||
scenarioIndex: scenarioIndex |
|||
Layout.preferredWidth: blockChainScrollView.width |
|||
Layout.preferredHeight: 60 |
|||
blockIndex: -1 |
|||
transactions: [] |
|||
status: "" |
|||
number: -2 |
|||
trHeight: 60 |
|||
} |
|||
|
|||
Repeater // List of blocks |
|||
{ |
|||
id: blockChainRepeater |
|||
model: blockModel |
|||
|
|||
function editTx(blockIndex, txIndex) |
|||
{ |
|||
itemAt(blockIndex).editTx(txIndex) |
|||
} |
|||
|
|||
function select(blockIndex, txIndex) |
|||
{ |
|||
itemAt(blockIndex).select(txIndex) |
|||
} |
|||
|
|||
Block |
|||
{ |
|||
Connections |
|||
{ |
|||
target: block |
|||
onTxSelected: |
|||
{ |
|||
blockChainPanel.txSelected(index, txIndex) |
|||
} |
|||
} |
|||
id: block |
|||
scenario: blockChainPanel.model |
|||
Layout.preferredWidth: blockChainScrollView.width |
|||
Layout.preferredHeight: |
|||
{ |
|||
return calculateHeight() |
|||
} |
|||
blockIndex: index |
|||
transactions: |
|||
{ |
|||
if (index >= 0) |
|||
return blockModel.get(index).transactions |
|||
else |
|||
return [] |
|||
} |
|||
transactionModel: |
|||
{ |
|||
if (index >= 0) |
|||
return scenario.blocks[index].transactions |
|||
else |
|||
return [] |
|||
} |
|||
|
|||
status: |
|||
{ |
|||
if (index >= 0) |
|||
return blockModel.get(index).status |
|||
else |
|||
return "" |
|||
} |
|||
|
|||
number: |
|||
{ |
|||
if (index >= 0) |
|||
return blockModel.get(index).number |
|||
else |
|||
return 0 |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
ListModel |
|||
{ |
|||
id: blockModel |
|||
|
|||
function appendBlock(block) |
|||
{ |
|||
blockModel.append(block); |
|||
} |
|||
|
|||
function appendTransaction(tr) |
|||
{ |
|||
blockModel.get(blockModel.count - 1).transactions.append(tr) |
|||
} |
|||
|
|||
function removeTransaction(blockIndex, trIndex) |
|||
{ |
|||
blockModel.get(blockIndex).transactions.remove(trIndex) |
|||
} |
|||
|
|||
function removeLastBlock() |
|||
{ |
|||
blockModel.remove(blockModel.count - 1) |
|||
} |
|||
|
|||
function removeBlock(index) |
|||
{ |
|||
blockModel.remove(index) |
|||
} |
|||
|
|||
function getTransaction(block, tr) |
|||
{ |
|||
return blockModel.get(block).transactions.get(tr) |
|||
} |
|||
|
|||
function setTransaction(blockIndex, trIndex, tr) |
|||
{ |
|||
blockModel.get(blockIndex).transactions.set(trIndex, tr) |
|||
} |
|||
|
|||
function setTransactionProperty(blockIndex, trIndex, propertyName, value) |
|||
{ |
|||
blockModel.get(blockIndex).transactions.set(trIndex, { propertyName: value }) |
|||
} |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
Layout.preferredWidth: parent.width |
|||
Layout.preferredHeight: 70 |
|||
color: "transparent" |
|||
RowLayout |
|||
{ |
|||
anchors.horizontalCenter: parent.horizontalCenter |
|||
anchors.top: parent.top |
|||
anchors.topMargin: 10 |
|||
spacing: 20 |
|||
|
|||
Rectangle { |
|||
Layout.preferredWidth: 100 |
|||
Layout.preferredHeight: 30 |
|||
ScenarioButton { |
|||
id: rebuild |
|||
text: qsTr("Rebuild") |
|||
width: 100 |
|||
height: 30 |
|||
roundLeft: true |
|||
roundRight: true |
|||
property variant contractsHex: ({}) |
|||
property variant txSha3: ({}) |
|||
property variant accountsSha3 |
|||
property variant contractsSha3 |
|||
property variant txChanged: [] |
|||
property var blinkReasons: [] |
|||
|
|||
function needRebuild(reason) |
|||
{ |
|||
rebuild.startBlinking() |
|||
blinkReasons.push(reason) |
|||
} |
|||
|
|||
function containsRebuildCause(reason) |
|||
{ |
|||
for (var c in blinkReasons) |
|||
{ |
|||
if (blinkReasons[c] === reason) |
|||
return true |
|||
} |
|||
return false |
|||
} |
|||
|
|||
|
|||
function notNeedRebuild(reason) |
|||
{ |
|||
for (var c in blinkReasons) |
|||
{ |
|||
if (blinkReasons[c] === reason) |
|||
{ |
|||
blinkReasons.splice(c, 1) |
|||
break |
|||
} |
|||
} |
|||
if (blinkReasons.length === 0) |
|||
rebuild.stopBlinking() |
|||
} |
|||
|
|||
onClicked: |
|||
{ |
|||
if (ensureNotFuturetime.running) |
|||
return; |
|||
rebuilding() |
|||
stopBlinking() |
|||
states = [] |
|||
var retBlocks = []; |
|||
var bAdded = 0; |
|||
for (var j = 0; j < model.blocks.length; j++) |
|||
{ |
|||
var b = model.blocks[j]; |
|||
var block = { |
|||
hash: b.hash, |
|||
number: b.number, |
|||
transactions: [], |
|||
status: b.status |
|||
} |
|||
for (var k = 0; k < model.blocks[j].transactions.length; k++) |
|||
{ |
|||
if (blockModel.get(j).transactions.get(k).saveStatus) |
|||
{ |
|||
var tr = model.blocks[j].transactions[k] |
|||
tr.saveStatus = true |
|||
block.transactions.push(tr); |
|||
} |
|||
|
|||
} |
|||
if (block.transactions.length > 0) |
|||
{ |
|||
bAdded++ |
|||
block.number = bAdded |
|||
block.status = "mined" |
|||
retBlocks.push(block) |
|||
} |
|||
} |
|||
if (retBlocks.length === 0) |
|||
retBlocks.push(projectModel.stateListModel.createEmptyBlock()) |
|||
else |
|||
{ |
|||
var last = retBlocks[retBlocks.length - 1] |
|||
last.number = -1 |
|||
last.status = "pending" |
|||
} |
|||
|
|||
model.blocks = retBlocks |
|||
blockModel.clear() |
|||
for (var j = 0; j < model.blocks.length; j++) |
|||
blockModel.append(model.blocks[j]) |
|||
|
|||
ensureNotFuturetime.start() |
|||
takeCodeSnapshot() |
|||
takeTxSnaphot() |
|||
takeAccountsSnapshot() |
|||
takeContractsSnapShot() |
|||
blinkReasons = [] |
|||
clientModel.setupScenario(model); |
|||
} |
|||
|
|||
function takeContractsSnapShot() |
|||
{ |
|||
contractsSha3 = codeModel.sha3(JSON.stringify(model.contracts)) |
|||
} |
|||
|
|||
function takeAccountsSnapshot() |
|||
{ |
|||
accountsSha3 = codeModel.sha3(JSON.stringify(model.accounts)) |
|||
} |
|||
|
|||
function takeCodeSnapshot() |
|||
{ |
|||
contractsHex = {} |
|||
for (var c in codeModel.contracts) |
|||
contractsHex[c] = codeModel.contracts[c].codeHex |
|||
} |
|||
|
|||
function takeTxSnaphot() |
|||
{ |
|||
txSha3 = {} |
|||
txChanged = [] |
|||
for (var j = 0; j < model.blocks.length; j++) |
|||
{ |
|||
for (var k = 0; k < model.blocks[j].transactions.length; k++) |
|||
{ |
|||
if (txSha3[j] === undefined) |
|||
txSha3[j] = {} |
|||
txSha3[j][k] = codeModel.sha3(JSON.stringify(model.blocks[j].transactions[k])) |
|||
} |
|||
} |
|||
} |
|||
|
|||
buttonShortcut: "" |
|||
sourceImg: "qrc:/qml/img/recycleicon@2x.png" |
|||
} |
|||
} |
|||
|
|||
|
|||
Rectangle |
|||
{ |
|||
Layout.preferredWidth: 200 |
|||
Layout.preferredHeight: 30 |
|||
color: "transparent" |
|||
|
|||
ScenarioButton { |
|||
id: addTransaction |
|||
text: qsTr("Add Tx") |
|||
onClicked: |
|||
{ |
|||
if (model && model.blocks) |
|||
{ |
|||
var lastBlock = model.blocks[model.blocks.length - 1]; |
|||
if (lastBlock.status === "mined") |
|||
{ |
|||
var newblock = projectModel.stateListModel.createEmptyBlock() |
|||
blockModel.appendBlock(newblock) |
|||
model.blocks.push(newblock); |
|||
} |
|||
|
|||
var item = TransactionHelper.defaultTransaction() |
|||
transactionDialog.stateAccounts = model.accounts |
|||
transactionDialog.execute = true |
|||
transactionDialog.editMode = false |
|||
transactionDialog.open(model.blocks[model.blocks.length - 1].transactions.length, model.blocks.length - 1, item) |
|||
} |
|||
} |
|||
width: 100 |
|||
height: 30 |
|||
buttonShortcut: "" |
|||
sourceImg: "qrc:/qml/img/sendtransactionicon@2x.png" |
|||
roundLeft: true |
|||
roundRight: false |
|||
} |
|||
|
|||
Timer |
|||
{ |
|||
id: ensureNotFuturetime |
|||
interval: 1000 |
|||
repeat: false |
|||
running: false |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
width: 1 |
|||
height: parent.height |
|||
anchors.right: addBlockBtn.left |
|||
color: "#ededed" |
|||
} |
|||
|
|||
ScenarioButton { |
|||
id: addBlockBtn |
|||
text: qsTr("Add Block...") |
|||
anchors.left: addTransaction.right |
|||
roundLeft: false |
|||
roundRight: true |
|||
onClicked: |
|||
{ |
|||
if (ensureNotFuturetime.running) |
|||
return |
|||
if (clientModel.mining || clientModel.running) |
|||
return |
|||
if (model.blocks.length > 0) |
|||
{ |
|||
var lastBlock = model.blocks[model.blocks.length - 1] |
|||
if (lastBlock.status === "pending") |
|||
{ |
|||
ensureNotFuturetime.start() |
|||
clientModel.mine() |
|||
} |
|||
else |
|||
addNewBlock() |
|||
} |
|||
else |
|||
addNewBlock() |
|||
} |
|||
|
|||
function addNewBlock() |
|||
{ |
|||
var block = projectModel.stateListModel.createEmptyBlock() |
|||
model.blocks.push(block) |
|||
blockModel.appendBlock(block) |
|||
} |
|||
width: 100 |
|||
height: 30 |
|||
|
|||
buttonShortcut: "" |
|||
sourceImg: "qrc:/qml/img/newblock@2x.png" |
|||
} |
|||
} |
|||
|
|||
|
|||
Connections |
|||
{ |
|||
target: clientModel |
|||
onNewBlock: |
|||
{ |
|||
if (!clientModel.running) |
|||
{ |
|||
var lastBlock = model.blocks[model.blocks.length - 1] |
|||
lastBlock.status = "mined" |
|||
lastBlock.number = model.blocks.length |
|||
var lastB = blockModel.get(model.blocks.length - 1) |
|||
lastB.status = "mined" |
|||
lastB.number = model.blocks.length |
|||
addBlockBtn.addNewBlock() |
|||
} |
|||
} |
|||
onStateCleared: |
|||
{ |
|||
} |
|||
onNewRecord: |
|||
{ |
|||
var blockIndex = parseInt(_r.transactionIndex.split(":")[0]) - 1 |
|||
var trIndex = parseInt(_r.transactionIndex.split(":")[1]) |
|||
if (blockIndex <= model.blocks.length - 1) |
|||
{ |
|||
var item = model.blocks[blockIndex] |
|||
if (trIndex <= item.transactions.length - 1) |
|||
{ |
|||
var tr = item.transactions[trIndex] |
|||
tr.returned = _r.returned |
|||
tr.recordIndex = _r.recordIndex |
|||
tr.logs = _r.logs |
|||
tr.sender = _r.sender |
|||
tr.returnParameters = _r.returnParameters |
|||
var trModel = blockModel.getTransaction(blockIndex, trIndex) |
|||
trModel.returned = _r.returned |
|||
trModel.recordIndex = _r.recordIndex |
|||
trModel.logs = _r.logs |
|||
trModel.sender = _r.sender |
|||
trModel.returnParameters = _r.returnParameters |
|||
blockModel.setTransaction(blockIndex, trIndex, trModel) |
|||
blockChainRepeater.select(blockIndex, trIndex) |
|||
return; |
|||
} |
|||
} |
|||
// tr is not in the list. |
|||
var itemTr = TransactionHelper.defaultTransaction() |
|||
itemTr.saveStatus = false |
|||
itemTr.functionId = _r.function |
|||
itemTr.contractId = _r.contract |
|||
itemTr.gasAuto = true |
|||
itemTr.parameters = _r.parameters |
|||
itemTr.isContractCreation = itemTr.functionId === itemTr.contractId |
|||
itemTr.label = _r.label |
|||
itemTr.isFunctionCall = itemTr.functionId !== "" && itemTr.functionId !== "<none>" |
|||
itemTr.returned = _r.returned |
|||
itemTr.value = QEtherHelper.createEther(_r.value, QEther.Wei) |
|||
itemTr.sender = _r.sender |
|||
itemTr.recordIndex = _r.recordIndex |
|||
itemTr.logs = _r.logs |
|||
itemTr.returnParameters = _r.returnParameters |
|||
model.blocks[model.blocks.length - 1].transactions.push(itemTr) |
|||
blockModel.appendTransaction(itemTr) |
|||
blockChainRepeater.select(blockIndex, trIndex) |
|||
} |
|||
|
|||
onNewState: { |
|||
states[_record] = _accounts |
|||
} |
|||
|
|||
onMiningComplete: |
|||
{ |
|||
} |
|||
} |
|||
|
|||
ScenarioButton { |
|||
id: newAccount |
|||
text: qsTr("New Account...") |
|||
onClicked: { |
|||
var ac = projectModel.stateListModel.newAccount("O", QEther.Wei) |
|||
model.accounts.push(ac) |
|||
clientModel.addAccount(ac.secret); |
|||
for (var k in Object.keys(blockChainPanel.states)) |
|||
blockChainPanel.states[k].accounts["0x" + ac.address] = "0 wei" // add the account in all the previous state (balance at O) |
|||
accountAdded("0x" + ac.address, "0") |
|||
} |
|||
Layout.preferredWidth: 100 |
|||
Layout.preferredHeight: 30 |
|||
buttonShortcut: "" |
|||
sourceImg: "qrc:/qml/img/newaccounticon@2x.png" |
|||
roundLeft: true |
|||
roundRight: true |
|||
} |
|||
} |
|||
} |
|||
|
|||
TransactionDialog { |
|||
id: transactionDialog |
|||
property bool execute |
|||
onAccepted: { |
|||
var item = transactionDialog.getItem() |
|||
if (execute) |
|||
{ |
|||
var lastBlock = model.blocks[model.blocks.length - 1]; |
|||
if (lastBlock.status === "mined") |
|||
{ |
|||
var newBlock = projectModel.stateListModel.createEmptyBlock(); |
|||
model.blocks.push(newBlock); |
|||
blockModel.appendBlock(newBlock) |
|||
} |
|||
if (!clientModel.running) |
|||
clientModel.executeTr(item) |
|||
} |
|||
else { |
|||
model.blocks[blockIndex].transactions[transactionIndex] = item |
|||
blockModel.setTransaction(blockIndex, transactionIndex, item) |
|||
chainChanged(blockIndex, transactionIndex, item) |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
@ -1,73 +0,0 @@ |
|||
import QtQuick 2.2 |
|||
import QtQuick.Controls 1.1 |
|||
import QtQuick.Controls.Styles 1.1 |
|||
import QtQuick.Layouts 1.1 |
|||
|
|||
DebugInfoList |
|||
{ |
|||
id: callStack |
|||
collapsible: true |
|||
title : qsTr("Call Stack") |
|||
enableSelection: true |
|||
itemDelegate: |
|||
Item { |
|||
anchors.fill: parent |
|||
|
|||
Rectangle { |
|||
anchors.fill: parent |
|||
color: "#4A90E2" |
|||
visible: styleData.selected; |
|||
} |
|||
|
|||
RowLayout |
|||
{ |
|||
id: row |
|||
anchors.fill: parent |
|||
Rectangle |
|||
{ |
|||
color: "#f7f7f7" |
|||
Layout.fillWidth: true |
|||
Layout.minimumWidth: 30 |
|||
Layout.maximumWidth: 30 |
|||
Text { |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
anchors.left: parent.left |
|||
font.family: "monospace" |
|||
anchors.leftMargin: 5 |
|||
color: "#4a4a4a" |
|||
text: styleData.row; |
|||
font.pointSize: dbgStyle.general.basicFontSize |
|||
width: parent.width - 5 |
|||
elide: Text.ElideRight |
|||
} |
|||
} |
|||
Rectangle |
|||
{ |
|||
color: "transparent" |
|||
Layout.fillWidth: true |
|||
Layout.minimumWidth: parent.width - 30 |
|||
Layout.maximumWidth: parent.width - 30 |
|||
Text { |
|||
anchors.leftMargin: 5 |
|||
width: parent.width - 5 |
|||
wrapMode: Text.NoWrap |
|||
anchors.left: parent.left |
|||
font.family: "monospace" |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
color: "#4a4a4a" |
|||
text: styleData.value; |
|||
elide: Text.ElideRight |
|||
font.pointSize: dbgStyle.general.basicFontSize |
|||
} |
|||
} |
|||
} |
|||
|
|||
Rectangle { |
|||
anchors.top: row.bottom |
|||
width: parent.width; |
|||
height: 1; |
|||
color: "#cccccc" |
|||
anchors.bottom: parent.bottom |
|||
} |
|||
} |
|||
} |
@ -1,89 +0,0 @@ |
|||
import QtQuick 2.0 |
|||
import QtQuick.Window 2.0 |
|||
import QtQuick.Layouts 1.0 |
|||
import QtQuick.Controls 1.0 |
|||
import QtQuick.Controls.Styles 1.1 |
|||
import "." |
|||
|
|||
Item { |
|||
signal editorTextChanged |
|||
|
|||
function setText(text) { |
|||
codeEditor.text = text; |
|||
} |
|||
|
|||
function getText() { |
|||
return codeEditor.text; |
|||
} |
|||
|
|||
function setFocus() { |
|||
codeEditor.forceActiveFocus(); |
|||
} |
|||
|
|||
anchors.fill: parent |
|||
id: contentView |
|||
width: parent.width |
|||
height: parent.height * 0.7 |
|||
|
|||
CodeEditorStyle { |
|||
id: style |
|||
} |
|||
|
|||
Rectangle { |
|||
id: lineColumn |
|||
property int rowHeight: codeEditor.font.pixelSize + 3 |
|||
color: "#202020" |
|||
width: 50 |
|||
height: parent.height |
|||
Column { |
|||
y: -codeEditor.flickableItem.contentY + 4 |
|||
width: parent.width |
|||
Repeater { |
|||
model: Math.max(codeEditor.lineCount + 2, (lineColumn.height/lineColumn.rowHeight)) |
|||
delegate: Text { |
|||
id: text |
|||
color: codeEditor.textColor |
|||
font: codeEditor.font |
|||
width: lineColumn.width - 4 |
|||
horizontalAlignment: Text.AlignRight |
|||
verticalAlignment: Text.AlignVCenter |
|||
height: lineColumn.rowHeight |
|||
renderType: Text.NativeRendering |
|||
text: index + 1 |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
TextArea { |
|||
id: codeEditor |
|||
textColor: "#EEE8D5" |
|||
style: TextAreaStyle { |
|||
backgroundColor: "#002B36" |
|||
} |
|||
|
|||
anchors.left: lineColumn.right |
|||
anchors.right: parent.right |
|||
anchors.top: parent.top |
|||
anchors.bottom: parent.bottom |
|||
wrapMode: TextEdit.NoWrap |
|||
frameVisible: false |
|||
|
|||
height: parent.height |
|||
font.family: "Monospace" |
|||
font.pointSize: style.general.basicFontSize |
|||
width: parent.width |
|||
|
|||
tabChangesFocus: false |
|||
Keys.onPressed: { |
|||
if (event.key === Qt.Key_Tab) { |
|||
codeEditor.insert(codeEditor.cursorPosition, "\t"); |
|||
event.accepted = true; |
|||
} |
|||
} |
|||
onTextChanged: { |
|||
editorTextChanged(); |
|||
} |
|||
|
|||
} |
|||
} |
@ -1,13 +0,0 @@ |
|||
import QtQuick 2.0 |
|||
|
|||
QtObject { |
|||
|
|||
function absoluteSize(rel) |
|||
{ |
|||
return systemPointSize + rel; |
|||
} |
|||
|
|||
property QtObject general: QtObject { |
|||
property int basicFontSize: absoluteSize(1) |
|||
} |
|||
} |
@ -1,387 +0,0 @@ |
|||
import QtQuick 2.0 |
|||
import QtQuick.Window 2.0 |
|||
import QtQuick.Layouts 1.0 |
|||
import QtQuick.Controls 1.0 |
|||
import QtQuick.Dialogs 1.1 |
|||
import Qt.labs.settings 1.0 |
|||
|
|||
Item { |
|||
id: codeEditorView |
|||
property string currentDocumentId: "" |
|||
property string sourceInError |
|||
property int openDocCount: 0 |
|||
signal documentEdit(string documentId) |
|||
signal breakpointsChanged(string documentId) |
|||
signal isCleanChanged(var isClean, string documentId) |
|||
signal loadComplete |
|||
|
|||
function getDocumentText(documentId) { |
|||
for (var i = 0; i < openDocCount; i++) { |
|||
if (editorListModel.get(i).documentId === documentId) { |
|||
return editors.itemAt(i).item.getText(); |
|||
} |
|||
} |
|||
return ""; |
|||
} |
|||
|
|||
function getContracts() |
|||
{ |
|||
var ctr = [] |
|||
for (var i = 0; i < openDocCount; i++) |
|||
{ |
|||
if (editorListModel.get(i).isContract) |
|||
{ |
|||
ctr.push(editors.itemAt(i).item) |
|||
} |
|||
} |
|||
return ctr; |
|||
} |
|||
|
|||
function isDocumentOpen(documentId) { |
|||
for (var i = 0; i < openDocCount; i++) |
|||
if (editorListModel.get(i).documentId === documentId && |
|||
editors.itemAt(i).item) |
|||
return true; |
|||
return false; |
|||
} |
|||
|
|||
function openDocument(document) { |
|||
loadDocument(document); |
|||
currentDocumentId = document.documentId; |
|||
} |
|||
|
|||
function loadDocument(document) { |
|||
for (var i = 0; i < openDocCount; i++) |
|||
if (editorListModel.get(i).documentId === document.documentId) |
|||
return; //already open |
|||
|
|||
if (editorListModel.count <= openDocCount) |
|||
editorListModel.append(document); |
|||
else |
|||
{ |
|||
editorListModel.set(openDocCount, document); |
|||
doLoadDocument(editors.itemAt(openDocCount).item, editorListModel.get(openDocCount), false) |
|||
loadComplete(); |
|||
} |
|||
openDocCount++; |
|||
} |
|||
|
|||
function doLoadDocument(editor, document, create) { |
|||
var data = fileIo.readFile(document.path); |
|||
if (create) |
|||
{ |
|||
editor.onLoadComplete.connect(function() { |
|||
codeEditorView.loadComplete(); |
|||
}); |
|||
editor.onEditorTextChanged.connect(function() { |
|||
documentEdit(editor.document.documentId); |
|||
if (editor.document.isContract) |
|||
codeModel.registerCodeChange(editor.document.documentId, editor.getText()); |
|||
}); |
|||
editor.onBreakpointsChanged.connect(function() { |
|||
if (editor.document.isContract) |
|||
breakpointsChanged(editor.document.documentId); |
|||
}); |
|||
editor.onIsCleanChanged.connect(function() { |
|||
isCleanChanged(editor.isClean, editor.document.documentId); |
|||
}); |
|||
} |
|||
editor.document = document; |
|||
editor.setFontSize(editorSettings.fontSize); |
|||
editor.sourceName = document.documentId; |
|||
editor.setText(data, document.syntaxMode); |
|||
editor.changeGeneration(); |
|||
} |
|||
|
|||
function getEditor(documentId) { |
|||
for (var i = 0; i < openDocCount; i++) |
|||
{ |
|||
if (editorListModel.get(i).documentId === documentId) |
|||
return editors.itemAt(i).item; |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
function highlightExecution(documentId, location) |
|||
{ |
|||
var editor = getEditor(documentId); |
|||
if (editor) |
|||
{ |
|||
if (documentId !== location.sourceName) |
|||
findAndHightlight(location.start, location.end, location.sourceName) |
|||
else |
|||
editor.highlightExecution(location); |
|||
} |
|||
} |
|||
|
|||
// Execution is not in the current document. Try: |
|||
// Open targeted document and hightlight (TODO) or |
|||
// Warn user that file is not available |
|||
function findAndHightlight(start, end, sourceName) |
|||
{ |
|||
var editor = getEditor(currentDocumentId); |
|||
if (editor) |
|||
editor.showWarning(qsTr("Currently debugging in " + sourceName + ". Source not available.")); |
|||
} |
|||
|
|||
function editingContract() { |
|||
for (var i = 0; i < openDocCount; i++) |
|||
if (editorListModel.get(i).documentId === currentDocumentId) |
|||
return editorListModel.get(i).isContract; |
|||
return false; |
|||
} |
|||
|
|||
function getBreakpoints() { |
|||
var bpMap = {}; |
|||
for (var i = 0; i < openDocCount; i++) { |
|||
var documentId = editorListModel.get(i).documentId; |
|||
var editor = editors.itemAt(i).item; |
|||
if (editor) { |
|||
bpMap[documentId] = editor.getBreakpoints(); |
|||
} |
|||
} |
|||
return bpMap; |
|||
} |
|||
|
|||
function toggleBreakpoint() { |
|||
var editor = getEditor(currentDocumentId); |
|||
if (editor) |
|||
editor.toggleBreakpoint(); |
|||
} |
|||
|
|||
function resetEditStatus(docId) { |
|||
var editor = getEditor(docId); |
|||
if (editor) |
|||
editor.changeGeneration(); |
|||
} |
|||
|
|||
function goToCompilationError() { |
|||
if (sourceInError === "") |
|||
return; |
|||
if (currentDocumentId !== sourceInError) |
|||
projectModel.openDocument(sourceInError); |
|||
for (var i = 0; i < openDocCount; i++) |
|||
{ |
|||
var doc = editorListModel.get(i); |
|||
if (doc.isContract && doc.documentId === sourceInError) |
|||
{ |
|||
var editor = editors.itemAt(i).item; |
|||
if (editor) |
|||
editor.goToCompilationError(); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
function setFontSize(size) { |
|||
if (size <= 10 || size >= 48) |
|||
return; |
|||
editorSettings.fontSize = size; |
|||
for (var i = 0; i < editors.count; i++) |
|||
editors.itemAt(i).item.setFontSize(size); |
|||
} |
|||
|
|||
function displayGasEstimation(checked) |
|||
{ |
|||
var editor = getEditor(currentDocumentId); |
|||
if (editor) |
|||
editor.displayGasEstimation(checked); |
|||
} |
|||
|
|||
Component.onCompleted: projectModel.codeEditor = codeEditorView; |
|||
|
|||
Connections { |
|||
target: codeModel |
|||
onCompilationError: { |
|||
sourceInError = _firstErrorLoc.source; |
|||
} |
|||
onCompilationComplete: { |
|||
sourceInError = ""; |
|||
var gasCosts = codeModel.gasCostByDocumentId(currentDocumentId); |
|||
var editor = getEditor(currentDocumentId); |
|||
if (editor) |
|||
editor.setGasCosts(gasCosts); |
|||
} |
|||
} |
|||
|
|||
Connections { |
|||
target: projectModel |
|||
onDocumentOpened: { |
|||
openDocument(document); |
|||
} |
|||
|
|||
onProjectSaving: { |
|||
for (var i = 0; i < openDocCount; i++) |
|||
{ |
|||
var doc = editorListModel.get(i); |
|||
if (editors.itemAt(i)) |
|||
{ |
|||
var editor = editors.itemAt(i).item; |
|||
if (editor) |
|||
fileIo.writeFile(doc.path, editor.getText()); |
|||
} |
|||
} |
|||
} |
|||
|
|||
onProjectSaved: { |
|||
if (projectModel.appIsClosing || projectModel.projectIsClosing) |
|||
return; |
|||
for (var i = 0; i < openDocCount; i++) |
|||
{ |
|||
var doc = editorListModel.get(i); |
|||
resetEditStatus(doc.documentId); |
|||
} |
|||
} |
|||
|
|||
onProjectClosed: { |
|||
currentDocumentId = ""; |
|||
openDocCount = 0; |
|||
} |
|||
|
|||
onDocumentSaved: { |
|||
resetEditStatus(documentId); |
|||
} |
|||
|
|||
onContractSaved: { |
|||
resetEditStatus(documentId); |
|||
} |
|||
|
|||
onDocumentSaving: { |
|||
for (var i = 0; i < editorListModel.count; i++) |
|||
{ |
|||
var doc = editorListModel.get(i); |
|||
if (doc.path === document.path) |
|||
{ |
|||
fileIo.writeFile(document.path, editors.itemAt(i).item.getText()); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
CodeEditorStyle |
|||
{ |
|||
id: style; |
|||
} |
|||
|
|||
MessageDialog |
|||
{ |
|||
id: messageDialog |
|||
title: qsTr("File Changed") |
|||
text: qsTr("This file has been changed outside of the editor. Do you want to reload it?") |
|||
standardButtons: StandardButton.Yes | StandardButton.No |
|||
property variant item |
|||
property variant doc |
|||
onYes: { |
|||
doLoadDocument(item, doc, false); |
|||
resetEditStatus(doc.documentId); |
|||
} |
|||
} |
|||
|
|||
Repeater { |
|||
id: editors |
|||
model: editorListModel |
|||
onItemRemoved: { |
|||
item.item.unloaded = true; |
|||
} |
|||
delegate: Loader { |
|||
id: loader |
|||
active: false |
|||
asynchronous: true |
|||
anchors.fill: parent |
|||
source: appService.haveWebEngine ? "WebCodeEditor.qml" : "CodeEditor.qml" |
|||
visible: (index >= 0 && index < openDocCount && currentDocumentId === editorListModel.get(index).documentId) |
|||
property bool changed: false |
|||
onVisibleChanged: { |
|||
loadIfNotLoaded() |
|||
if (visible && item) |
|||
{ |
|||
loader.item.setFocus(); |
|||
if (changed) |
|||
{ |
|||
changed = false; |
|||
messageDialog.item = loader.item; |
|||
messageDialog.doc = editorListModel.get(index); |
|||
messageDialog.open(); |
|||
} |
|||
loader.item.displayGasEstimation(gasEstimationAction.checked); |
|||
} |
|||
} |
|||
Component.onCompleted: { |
|||
loadIfNotLoaded() |
|||
} |
|||
onLoaded: { |
|||
doLoadDocument(loader.item, editorListModel.get(index), true) |
|||
} |
|||
|
|||
Connections |
|||
{ |
|||
target: projectModel |
|||
onDocumentChanged: { |
|||
if (!item) |
|||
return; |
|||
var current = editorListModel.get(index); |
|||
if (documentId === current.documentId) |
|||
{ |
|||
if (currentDocumentId === current.documentId) |
|||
{ |
|||
messageDialog.item = loader.item; |
|||
messageDialog.doc = editorListModel.get(index); |
|||
messageDialog.open(); |
|||
} |
|||
else |
|||
changed = true |
|||
} |
|||
} |
|||
|
|||
onDocumentUpdated: { |
|||
var document = projectModel.getDocument(documentId); |
|||
for (var i = 0; i < editorListModel.count; i++) |
|||
if (editorListModel.get(i).documentId === documentId) |
|||
{ |
|||
editorListModel.set(i, document); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
onDocumentRemoved: { |
|||
for (var i = 0; i < editorListModel.count; i++) |
|||
if (editorListModel.get(i).documentId === documentId) |
|||
{ |
|||
editorListModel.remove(i); |
|||
openDocCount--; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
function loadIfNotLoaded () { |
|||
if (visible && !active) { |
|||
active = true; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
ListModel { |
|||
id: editorListModel |
|||
} |
|||
|
|||
Action { |
|||
id: increaseFontSize |
|||
text: qsTr("Increase Font Size") |
|||
shortcut: "Ctrl+=" |
|||
onTriggered: setFontSize(editorSettings.fontSize + 1) |
|||
} |
|||
|
|||
Action { |
|||
id: decreaseFontSize |
|||
text: qsTr("Decrease Font Size") |
|||
shortcut: "Ctrl+-" |
|||
onTriggered: setFontSize(editorSettings.fontSize - 1) |
|||
} |
|||
|
|||
Settings { |
|||
id: editorSettings |
|||
property int fontSize: 12; |
|||
} |
|||
} |
@ -1,9 +0,0 @@ |
|||
import QtQuick 2.0 |
|||
import "." |
|||
|
|||
Rectangle |
|||
{ |
|||
height: 1 |
|||
color: appStyle.generic.layout.separatorColor |
|||
} |
|||
|
@ -1,33 +0,0 @@ |
|||
import QtQuick 2.2 |
|||
import QtQuick.Controls 1.1 |
|||
import QtQuick.Layouts 1.0 |
|||
import QtQuick.Controls.Styles 1.1 |
|||
|
|||
RowLayout { |
|||
property string titleStr |
|||
|
|||
function update(_value) |
|||
{ |
|||
currentStepValue.text = _value; |
|||
} |
|||
|
|||
Rectangle { |
|||
width: 120 |
|||
height: parent.height |
|||
color: "#e5e5e5" |
|||
Text |
|||
{ |
|||
id: title |
|||
font.pixelSize: 12 |
|||
anchors.centerIn: parent |
|||
color: "#a2a2a2" |
|||
font.family: "Sans Serif" |
|||
text: titleStr |
|||
} |
|||
} |
|||
Text |
|||
{ |
|||
font.pixelSize: 13 |
|||
id: currentStepValue |
|||
} |
|||
} |
@ -1,152 +0,0 @@ |
|||
import QtQuick 2.2 |
|||
import QtQuick.Controls 1.1 |
|||
import QtQuick.Layouts 1.0 |
|||
import QtQuick.Controls.Styles 1.1 |
|||
|
|||
ColumnLayout { |
|||
id: root |
|||
property string title |
|||
property variant listModel; |
|||
property bool collapsible; |
|||
property bool collapsed; |
|||
property bool enableSelection: false; |
|||
property real storedHeight: 0; |
|||
property Component itemDelegate |
|||
property Component componentDelegate |
|||
property alias item: loader.item |
|||
signal rowActivated(int index) |
|||
spacing: 0 |
|||
|
|||
function collapse() |
|||
{ |
|||
storedHeight = childrenRect.height; |
|||
storageContainer.collapse(); |
|||
} |
|||
|
|||
function show() |
|||
{ |
|||
storageContainer.expand(); |
|||
} |
|||
|
|||
Component.onCompleted: |
|||
{ |
|||
if (storageContainer.parent.parent.height === 25) |
|||
storageContainer.collapse(); |
|||
else |
|||
{ |
|||
if (storageContainer.parent.parent.height === 0) |
|||
storageContainer.parent.parent.height = 200; |
|||
storageContainer.expand(); |
|||
} |
|||
} |
|||
|
|||
RowLayout { |
|||
height: 25 |
|||
id: header |
|||
Image { |
|||
source: "img/closedtriangleindicator.png" |
|||
width: 15 |
|||
height: 15 |
|||
id: storageImgArrow |
|||
} |
|||
|
|||
Text { |
|||
anchors.left: storageImgArrow.right |
|||
color: "#8b8b8b" |
|||
text: title |
|||
id: storageListTitle |
|||
} |
|||
|
|||
MouseArea |
|||
{ |
|||
enabled: collapsible |
|||
anchors.fill: parent |
|||
onClicked: { |
|||
if (collapsible) |
|||
{ |
|||
if (collapsed) |
|||
{ |
|||
storageContainer.expand(); |
|||
if (storedHeight <= 25) |
|||
storedHeight = 200; |
|||
storageContainer.parent.parent.height = storedHeight; |
|||
} |
|||
else |
|||
{ |
|||
storedHeight = root.childrenRect.height; |
|||
storageContainer.collapse(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Rectangle |
|||
{ |
|||
id: storageContainer |
|||
border.width: 3 |
|||
border.color: "#deddd9" |
|||
Layout.fillWidth: true |
|||
Layout.fillHeight: true |
|||
|
|||
function collapse() { |
|||
storageImgArrow.source = "qrc:/qml/img/closedtriangleindicator.png"; |
|||
if (storageContainer.parent.parent.height > 25) |
|||
storageContainer.parent.parent.height = 25; |
|||
collapsed = true; |
|||
} |
|||
|
|||
function expand() { |
|||
storageImgArrow.source = "qrc:/qml/img/opentriangleindicator.png"; |
|||
collapsed = false; |
|||
} |
|||
|
|||
Loader |
|||
{ |
|||
id: loader |
|||
anchors.top: parent.top |
|||
anchors.left: parent.left |
|||
anchors.topMargin: 3 |
|||
anchors.leftMargin: 3 |
|||
width: parent.width - 3 |
|||
height: parent.height - 6 |
|||
onHeightChanged: { |
|||
if (height <= 0 && collapsible) { |
|||
storageContainer.collapse(); |
|||
} |
|||
else if (height > 0 && collapsed) { |
|||
storageContainer.expand(); |
|||
} |
|||
} |
|||
|
|||
sourceComponent: componentDelegate ? componentDelegate : table |
|||
} |
|||
Component |
|||
{ |
|||
id: table |
|||
TableView |
|||
{ |
|||
clip: true; |
|||
alternatingRowColors: false |
|||
anchors.fill: parent |
|||
model: listModel |
|||
selectionMode: enableSelection ? SelectionMode.SingleSelection : SelectionMode.NoSelection |
|||
headerDelegate: null |
|||
itemDelegate: root.itemDelegate |
|||
onActivated: rowActivated(row); |
|||
Keys.onPressed: { |
|||
if ((event.modifiers & Qt.ControlModifier) && event.key === Qt.Key_C && currentRow >=0 && currentRow < listModel.length) { |
|||
var str = ""; |
|||
for (var i = 0; i < listModel.length; i++) |
|||
str += listModel[i] + "\n"; |
|||
clipboard.text = str; |
|||
} |
|||
} |
|||
|
|||
TableViewColumn { |
|||
role: "modelData" |
|||
width: parent.width |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
@ -1,671 +0,0 @@ |
|||
import QtQuick 2.2 |
|||
import QtQuick.Controls 1.1 |
|||
import QtQuick.Controls.Styles 1.1 |
|||
import QtQuick.Dialogs 1.1 |
|||
import QtQuick.Layouts 1.1 |
|||
import Qt.labs.settings 1.0 |
|||
import "js/Debugger.js" as Debugger |
|||
import "js/ErrorLocationFormater.js" as ErrorLocationFormater |
|||
import "." |
|||
|
|||
Rectangle { |
|||
id: debugPanel |
|||
|
|||
property alias debugSlider: statesSlider |
|||
property alias solLocals: solLocals |
|||
property alias solStorage: solStorage |
|||
property alias solCallStack: solCallStack |
|||
property alias vmCallStack: callStack |
|||
property alias vmStorage: storage |
|||
property alias vmMemory: memoryDump |
|||
property alias vmCallData: callDataDump |
|||
signal debugExecuteLocation(string documentId, var location) |
|||
property string compilationErrorMessage |
|||
property bool assemblyMode: false |
|||
signal panelClosed |
|||
objectName: "debugPanel" |
|||
color: "#ededed" |
|||
clip: true |
|||
|
|||
onVisibleChanged: |
|||
{ |
|||
if (visible) |
|||
forceActiveFocus(); |
|||
} |
|||
|
|||
onAssemblyModeChanged: |
|||
{ |
|||
Debugger.updateMode(); |
|||
machineStates.updateHeight(); |
|||
} |
|||
|
|||
function setTr(tr) |
|||
{ |
|||
trName.text = tr.label |
|||
} |
|||
|
|||
function displayCompilationErrorIfAny() |
|||
{ |
|||
debugScrollArea.visible = false; |
|||
compilationErrorArea.visible = true; |
|||
machineStates.visible = false; |
|||
var errorInfo = ErrorLocationFormater.extractErrorInfo(compilationErrorMessage, false); |
|||
errorLocation.text = errorInfo.errorLocation; |
|||
errorDetail.text = errorInfo.errorDetail; |
|||
errorLine.text = errorInfo.line; |
|||
} |
|||
|
|||
function update(data, giveFocus) |
|||
{ |
|||
if (data === null) |
|||
Debugger.init(null); |
|||
else if (data.states.length === 0) |
|||
Debugger.init(null); |
|||
else if (codeModel.hasContract) |
|||
{ |
|||
Debugger.init(data); |
|||
debugScrollArea.visible = true; |
|||
machineStates.visible = true; |
|||
} |
|||
if (giveFocus) |
|||
forceActiveFocus(); |
|||
} |
|||
|
|||
function setBreakpoints(bp) |
|||
{ |
|||
Debugger.setBreakpoints(bp); |
|||
} |
|||
|
|||
DebuggerPaneStyle { |
|||
id: dbgStyle |
|||
} |
|||
|
|||
Connections { |
|||
target: clientModel |
|||
onDebugDataReady: { |
|||
update(_debugData, false); |
|||
} |
|||
} |
|||
|
|||
Connections { |
|||
target: codeModel |
|||
onCompilationComplete: { |
|||
debugPanel.compilationErrorMessage = ""; |
|||
} |
|||
|
|||
onCompilationError: { |
|||
debugPanel.compilationErrorMessage = _error; |
|||
} |
|||
} |
|||
|
|||
Settings { |
|||
id: splitSettings |
|||
property alias callStackHeight: callStackRect.height |
|||
property alias storageHeightSettings: storageRect.height |
|||
property alias memoryDumpHeightSettings: memoryRect.height |
|||
property alias callDataHeightSettings: callDataRect.height |
|||
property alias solCallStackHeightSettings: solStackRect.height |
|||
property alias solStorageHeightSettings: solStorageRect.height |
|||
property alias solLocalsHeightSettings: solLocalsRect.height |
|||
} |
|||
|
|||
ColumnLayout { |
|||
id: debugScrollArea |
|||
anchors.fill: parent |
|||
spacing: 0 |
|||
RowLayout |
|||
{ |
|||
Layout.preferredWidth: parent.width |
|||
Layout.preferredHeight: 30 |
|||
Rectangle |
|||
{ |
|||
Layout.preferredWidth: parent.width |
|||
Layout.preferredHeight: parent.height |
|||
color: "transparent" |
|||
Text { |
|||
anchors.centerIn: parent |
|||
text: qsTr("Current Transaction") |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
anchors.left: parent.left |
|||
anchors.leftMargin: 10 |
|||
width: 30 |
|||
height: parent.height |
|||
color: "transparent" |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
Image { |
|||
source: "qrc:/qml/img/leftarrow@2x.png" |
|||
width: parent.width |
|||
fillMode: Image.PreserveAspectFit |
|||
anchors.centerIn: parent |
|||
} |
|||
MouseArea |
|||
{ |
|||
anchors.fill: parent |
|||
onClicked: |
|||
{ |
|||
Debugger.init(null); |
|||
panelClosed() |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
RowLayout |
|||
{ |
|||
Layout.preferredWidth: parent.width |
|||
Layout.preferredHeight: 30 |
|||
Rectangle |
|||
{ |
|||
Layout.preferredWidth: parent.width |
|||
Layout.preferredHeight: parent.height |
|||
color: "#2C79D3" |
|||
Text { |
|||
id: trName |
|||
color: "white" |
|||
anchors.centerIn: parent |
|||
} |
|||
} |
|||
} |
|||
|
|||
ScrollView |
|||
{ |
|||
property int sideMargin: 10 |
|||
id: machineStates |
|||
Layout.fillWidth: true |
|||
Layout.fillHeight: true |
|||
function updateHeight() { |
|||
var h = buttonRow.childrenRect.height; |
|||
if (assemblyMode) |
|||
h += assemblyCodeRow.childrenRect.height + callStackRect.childrenRect.height + storageRect.childrenRect.height + memoryRect.childrenRect.height + callDataRect.childrenRect.height; |
|||
else |
|||
h += solStackRect.childrenRect.height + solLocalsRect.childrenRect.height + solStorageRect.childrenRect.height; |
|||
statesLayout.height = h + 120; |
|||
} |
|||
|
|||
Component.onCompleted: updateHeight(); |
|||
|
|||
ColumnLayout { |
|||
id: statesLayout |
|||
anchors.top: parent.top |
|||
anchors.topMargin: 15 |
|||
anchors.left: parent.left; |
|||
anchors.leftMargin: machineStates.sideMargin |
|||
width: debugScrollArea.width - machineStates.sideMargin * 2 - 20 |
|||
spacing: machineStates.sideMargin |
|||
|
|||
Rectangle { |
|||
// step button + slider |
|||
id: buttonRow |
|||
height: 30 |
|||
Layout.fillWidth: true |
|||
color: "transparent" |
|||
|
|||
Rectangle { |
|||
anchors.fill: parent |
|||
color: "transparent" |
|||
RowLayout { |
|||
anchors.fill: parent |
|||
id: jumpButtons |
|||
spacing: 3 |
|||
layoutDirection: Qt.LeftToRight |
|||
|
|||
StepActionImage |
|||
{ |
|||
id: runBackAction; |
|||
enabledStateImg: "qrc:/qml/img/jumpoutback.png" |
|||
disableStateImg: "qrc:/qml/img/jumpoutbackdisabled.png" |
|||
onClicked: Debugger.runBack() |
|||
width: 23 |
|||
buttonShortcut: "Ctrl+Shift+F5" |
|||
buttonTooltip: qsTr("Run Back") |
|||
visible: false |
|||
} |
|||
|
|||
StepActionImage |
|||
{ |
|||
id: jumpOutBackAction; |
|||
enabledStateImg: "qrc:/qml/img/jumpoutback.png" |
|||
disableStateImg: "qrc:/qml/img/jumpoutbackdisabled.png" |
|||
onClicked: Debugger.stepOutBack() |
|||
width: 23 |
|||
buttonShortcut: "Ctrl+Shift+F11" |
|||
buttonTooltip: qsTr("Step Out Back") |
|||
} |
|||
|
|||
StepActionImage |
|||
{ |
|||
id: jumpIntoBackAction |
|||
enabledStateImg: "qrc:/qml/img/jumpintoback.png" |
|||
disableStateImg: "qrc:/qml/img/jumpintobackdisabled.png" |
|||
onClicked: Debugger.stepIntoBack() |
|||
width: 23 |
|||
buttonShortcut: "Ctrl+F11" |
|||
buttonTooltip: qsTr("Step Into Back") |
|||
} |
|||
|
|||
StepActionImage |
|||
{ |
|||
id: jumpOverBackAction |
|||
enabledStateImg: "qrc:/qml/img/jumpoverback.png" |
|||
disableStateImg: "qrc:/qml/img/jumpoverbackdisabled.png" |
|||
onClicked: Debugger.stepOverBack() |
|||
width: 23 |
|||
buttonShortcut: "Ctrl+F10" |
|||
buttonTooltip: qsTr("Step Over Back") |
|||
} |
|||
|
|||
StepActionImage |
|||
{ |
|||
id: jumpOverForwardAction |
|||
enabledStateImg: "qrc:/qml/img/jumpoverforward.png" |
|||
disableStateImg: "qrc:/qml/img/jumpoverforwarddisabled.png" |
|||
onClicked: Debugger.stepOverForward() |
|||
width: 23 |
|||
buttonShortcut: "F10" |
|||
buttonTooltip: qsTr("Step Over Forward") |
|||
} |
|||
|
|||
StepActionImage |
|||
{ |
|||
id: jumpIntoForwardAction |
|||
enabledStateImg: "qrc:/qml/img/jumpintoforward.png" |
|||
disableStateImg: "qrc:/qml/img/jumpintoforwarddisabled.png" |
|||
onClicked: Debugger.stepIntoForward() |
|||
width: 23 |
|||
buttonShortcut: "F11" |
|||
buttonTooltip: qsTr("Step Into Forward") |
|||
} |
|||
|
|||
StepActionImage |
|||
{ |
|||
id: jumpOutForwardAction |
|||
enabledStateImg: "qrc:/qml/img/jumpoutforward.png" |
|||
disableStateImg: "qrc:/qml/img/jumpoutforwarddisabled.png" |
|||
onClicked: Debugger.stepOutForward() |
|||
width: 45 |
|||
buttonShortcut: "Shift+F11" |
|||
buttonTooltip: qsTr("Step Out Forward") |
|||
buttonRight: true |
|||
} |
|||
|
|||
StepActionImage |
|||
{ |
|||
id: runForwardAction |
|||
enabledStateImg: "qrc:/qml/img/jumpoutforward.png" |
|||
disableStateImg: "qrc:/qml/img/jumpoutforwarddisabled.png" |
|||
onClicked: Debugger.runForward() |
|||
width: 45 |
|||
buttonShortcut: "Ctrl+F5" |
|||
buttonTooltip: qsTr("Run Forward") |
|||
visible: false |
|||
buttonRight: true |
|||
} |
|||
|
|||
Rectangle { |
|||
anchors.top: parent.top |
|||
anchors.bottom: parent.bottom |
|||
anchors.right: parent.right |
|||
color: "transparent" |
|||
Layout.fillWidth: true |
|||
Layout.minimumWidth: parent.width * 0.2 |
|||
Layout.alignment: Qt.AlignRight |
|||
|
|||
Slider { |
|||
id: statesSlider |
|||
anchors.fill: parent |
|||
tickmarksEnabled: true |
|||
stepSize: 1.0 |
|||
onValueChanged: Debugger.jumpTo(value); |
|||
style: SliderStyle { |
|||
groove: Rectangle { |
|||
implicitHeight: 3 |
|||
color: "#7da4cd" |
|||
radius: 8 |
|||
} |
|||
handle: Rectangle { |
|||
anchors.centerIn: parent |
|||
color: control.pressed ? "white" : "lightgray" |
|||
border.color: "gray" |
|||
border.width: 2 |
|||
implicitWidth: 10 |
|||
implicitHeight: 10 |
|||
radius: 12 |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
Rectangle { |
|||
// Assembly code |
|||
id: assemblyCodeRow |
|||
Layout.fillWidth: true |
|||
height: 405 |
|||
implicitHeight: 405 |
|||
color: "transparent" |
|||
visible: assemblyMode |
|||
|
|||
Rectangle |
|||
{ |
|||
id: stateListContainer |
|||
anchors.top : parent.top |
|||
anchors.bottom: parent.bottom |
|||
anchors.left: parent.left |
|||
width: parent.width * 0.4 |
|||
height: parent.height |
|||
border.width: 3 |
|||
border.color: "#deddd9" |
|||
color: "white" |
|||
TableView { |
|||
id: statesList |
|||
anchors.fill: parent |
|||
anchors.leftMargin: 3 |
|||
anchors.rightMargin: 3 |
|||
anchors.topMargin: 3 |
|||
anchors.bottomMargin: 3 |
|||
clip: true |
|||
headerDelegate: null |
|||
itemDelegate: renderDelegate |
|||
model: ListModel {} |
|||
TableViewColumn { |
|||
role: "line" |
|||
width: parent.width - 10 |
|||
} |
|||
|
|||
} |
|||
|
|||
Component { |
|||
id: highlightBar |
|||
Rectangle { |
|||
radius: 4 |
|||
anchors.fill: parent |
|||
y: statesList.currentItem.y |
|||
color: "#4A90E2" |
|||
} |
|||
} |
|||
|
|||
Component { |
|||
id: renderDelegate |
|||
Item { |
|||
Rectangle { |
|||
radius: 4 |
|||
anchors.fill: parent |
|||
color: "#4A90E2" |
|||
visible: styleData.selected; |
|||
} |
|||
|
|||
RowLayout { |
|||
id: wrapperItem |
|||
anchors.fill: parent |
|||
spacing: 5 |
|||
|
|||
|
|||
Text { |
|||
anchors.left: parent.left |
|||
anchors.leftMargin: 10 |
|||
width: 15 |
|||
color: "#b2b3ae" |
|||
text: styleData.value.split(' ')[0] |
|||
font.family: "monospace" |
|||
font.pointSize: dbgStyle.general.basicFontSize |
|||
wrapMode: Text.NoWrap |
|||
id: id |
|||
} |
|||
Text { |
|||
anchors.left: id.right; |
|||
wrapMode: Text.NoWrap |
|||
color: styleData.selected ? "white" : "black" |
|||
font.family: "monospace" |
|||
text: styleData.value.replace(styleData.value.split(' ')[0], '') |
|||
font.pointSize: dbgStyle.general.basicFontSize |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
Rectangle { |
|||
id: debugInfoContainer |
|||
width: parent.width * 0.6 - machineStates.sideMargin |
|||
anchors.top : parent.top |
|||
anchors.bottom: parent.bottom |
|||
anchors.right: parent.right |
|||
height: parent.height |
|||
color: "transparent" |
|||
ColumnLayout |
|||
{ |
|||
width: parent.width |
|||
anchors.fill: parent |
|||
spacing: 0 |
|||
DebugBasicInfo { |
|||
id: currentStep |
|||
titleStr: qsTr("Current Step") |
|||
Layout.fillWidth: true |
|||
height: 30 |
|||
} |
|||
DebugBasicInfo { |
|||
id: mem |
|||
titleStr: qsTr("Adding Memory") |
|||
Layout.fillWidth: true |
|||
height: 30 |
|||
} |
|||
DebugBasicInfo { |
|||
id: stepCost |
|||
titleStr: qsTr("Step Cost") |
|||
Layout.fillWidth: true |
|||
height: 30 |
|||
} |
|||
DebugBasicInfo { |
|||
id: gasSpent |
|||
titleStr: qsTr("Total Gas Spent") |
|||
Layout.fillWidth: true |
|||
height: 30 |
|||
} |
|||
DebugInfoList |
|||
{ |
|||
Layout.fillHeight: true |
|||
Layout.fillWidth: true |
|||
id: stack |
|||
collapsible: false |
|||
title : qsTr("Stack") |
|||
itemDelegate: Item { |
|||
id: renderedItem |
|||
width: parent.width |
|||
RowLayout |
|||
{ |
|||
anchors.fill: parent |
|||
Rectangle |
|||
{ |
|||
id: indexColumn |
|||
color: "#f7f7f7" |
|||
Layout.fillWidth: true |
|||
Layout.minimumWidth: 30 |
|||
Layout.preferredWidth: 30 |
|||
Layout.maximumWidth: 30 |
|||
Layout.minimumHeight: parent.height |
|||
Text { |
|||
anchors.centerIn: parent |
|||
anchors.leftMargin: 5 |
|||
font.family: "monospace" |
|||
color: "#4a4a4a" |
|||
text: styleData.row; |
|||
font.pointSize: dbgStyle.general.basicFontSize |
|||
} |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
anchors.left: indexColumn.right |
|||
Layout.fillWidth: true |
|||
Layout.minimumWidth: 15 |
|||
Layout.preferredWidth: 15 |
|||
Layout.minimumHeight: parent.height |
|||
Text { |
|||
anchors.left: parent.left |
|||
anchors.leftMargin: 5 |
|||
font.family: "monospace" |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
color: "#4a4a4a" |
|||
text: styleData.value |
|||
font.pointSize: dbgStyle.general.basicFontSize |
|||
} |
|||
} |
|||
} |
|||
|
|||
Rectangle { |
|||
id: separator |
|||
width: parent.width; |
|||
height: 1; |
|||
color: "#cccccc" |
|||
anchors.bottom: parent.bottom |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
SplitView |
|||
{ |
|||
id: splitInfoList |
|||
Layout.fillHeight: true |
|||
Layout.fillWidth: true |
|||
orientation: Qt.Vertical |
|||
|
|||
Rectangle |
|||
{ |
|||
id: solStackRect; |
|||
color: "transparent" |
|||
Layout.minimumHeight: 25 |
|||
Layout.maximumHeight: 800 |
|||
onHeightChanged: machineStates.updateHeight(); |
|||
visible: !assemblyMode |
|||
CallStack { |
|||
anchors.fill: parent |
|||
id: solCallStack |
|||
} |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
id: solLocalsRect; |
|||
color: "transparent" |
|||
Layout.minimumHeight: 25 |
|||
Layout.maximumHeight: 800 |
|||
onHeightChanged: machineStates.updateHeight(); |
|||
visible: !assemblyMode |
|||
VariablesView { |
|||
title : qsTr("Locals") |
|||
anchors.fill: parent |
|||
id: solLocals |
|||
} |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
id: solStorageRect; |
|||
color: "transparent" |
|||
Layout.minimumHeight: 25 |
|||
Layout.maximumHeight: 800 |
|||
onHeightChanged: machineStates.updateHeight(); |
|||
visible: !assemblyMode |
|||
VariablesView { |
|||
title : qsTr("Members") |
|||
anchors.fill: parent |
|||
id: solStorage |
|||
} |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
id: callStackRect; |
|||
color: "transparent" |
|||
Layout.minimumHeight: 25 |
|||
Layout.maximumHeight: 800 |
|||
onHeightChanged: machineStates.updateHeight(); |
|||
visible: assemblyMode |
|||
CallStack { |
|||
anchors.fill: parent |
|||
id: callStack |
|||
onRowActivated: Debugger.displayFrame(index); |
|||
} |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
id: storageRect |
|||
color: "transparent" |
|||
width: parent.width |
|||
Layout.minimumHeight: 25 |
|||
Layout.maximumHeight: 800 |
|||
onHeightChanged: machineStates.updateHeight(); |
|||
visible: assemblyMode |
|||
StorageView { |
|||
anchors.fill: parent |
|||
id: storage |
|||
} |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
id: memoryRect; |
|||
color: "transparent" |
|||
width: parent.width |
|||
Layout.minimumHeight: 25 |
|||
Layout.maximumHeight: 800 |
|||
onHeightChanged: machineStates.updateHeight(); |
|||
visible: assemblyMode |
|||
DebugInfoList { |
|||
id: memoryDump |
|||
anchors.fill: parent |
|||
collapsible: true |
|||
title: qsTr("Memory Dump") |
|||
itemDelegate: |
|||
Item { |
|||
height: 29 |
|||
width: parent.width - 3; |
|||
ItemDelegateDataDump {} |
|||
} |
|||
} |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
id: callDataRect |
|||
color: "transparent" |
|||
width: parent.width |
|||
Layout.minimumHeight: 25 |
|||
Layout.maximumHeight: 800 |
|||
onHeightChanged: machineStates.updateHeight(); |
|||
visible: assemblyMode |
|||
DebugInfoList { |
|||
id: callDataDump |
|||
anchors.fill: parent |
|||
collapsible: true |
|||
title: qsTr("Call Data") |
|||
itemDelegate: |
|||
Item { |
|||
height: 29 |
|||
width: parent.width - 3; |
|||
ItemDelegateDataDump {} |
|||
} |
|||
} |
|||
} |
|||
Rectangle |
|||
{ |
|||
id: bottomRect; |
|||
width: parent.width |
|||
Layout.minimumHeight: 20 |
|||
color: "transparent" |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
@ -1,16 +0,0 @@ |
|||
import QtQuick 2.0 |
|||
|
|||
QtObject { |
|||
|
|||
function absoluteSize(rel) |
|||
{ |
|||
return systemPointSize + rel; |
|||
} |
|||
|
|||
property QtObject general: QtObject { |
|||
property int basicFontSize: absoluteSize(-2) |
|||
property string basicColor: "#4a4a4a" |
|||
property string basicFont: "monospace" |
|||
property int dataDumpFontSize: absoluteSize(-3) |
|||
} |
|||
} |
@ -1,10 +0,0 @@ |
|||
import QtQuick 2.0 |
|||
import QtQuick.Controls 1.1 |
|||
|
|||
Label { |
|||
text: text |
|||
} |
|||
|
|||
|
|||
|
|||
|
@ -1,6 +0,0 @@ |
|||
import QtQuick 2.0 |
|||
import QtQuick.Controls 1.1 |
|||
|
|||
TextField { |
|||
id: titleField |
|||
} |
@ -1,540 +0,0 @@ |
|||
import QtQuick 2.0 |
|||
import QtQuick.Layouts 1.0 |
|||
import QtQuick.Controls 1.1 |
|||
import QtQuick.Controls.Styles 1.3 |
|||
import Qt.labs.settings 1.0 |
|||
import "js/TransactionHelper.js" as TransactionHelper |
|||
import "js/NetworkDeployment.js" as NetworkDeploymentCode |
|||
import "js/QEtherHelper.js" as QEtherHelper |
|||
import org.ethereum.qml.QEther 1.0 |
|||
|
|||
Rectangle { |
|||
property variant paramsModel: [] |
|||
property variant worker |
|||
property variant gas: [] |
|||
property alias gasPrice: gasPriceInput |
|||
color: "#E3E3E3E3" |
|||
signal deployed |
|||
anchors.fill: parent |
|||
id: root |
|||
|
|||
property int labelWidth: 150 |
|||
property bool verifyDeploy: true |
|||
|
|||
function show() |
|||
{ |
|||
visible = true |
|||
contractList.currentIndex = 0 |
|||
contractList.change() |
|||
accountsModel.clear() |
|||
for (var k in worker.accounts) |
|||
accountsModel.append(worker.accounts[k]) |
|||
|
|||
if (worker.currentAccount === "" && worker.accounts.length > 0) |
|||
{ |
|||
worker.currentAccount = worker.accounts[0].id |
|||
accountsList.currentIndex = 0 |
|||
} |
|||
|
|||
verifyDeployedContract() |
|||
deployedAddresses.refresh() |
|||
|
|||
worker.renewCtx() |
|||
verifyDeploy = true |
|||
worker.pooler.onTriggered.connect(function() { |
|||
if (root.visible && verifyDeploy) |
|||
verifyDeployedContract(); |
|||
}) |
|||
} |
|||
|
|||
function verifyDeployedContract() |
|||
{ |
|||
if (projectModel.deployBlockNumber !== -1) |
|||
{ |
|||
worker.verifyHashes(projectModel.deploymentTrHashes, function (bn, trLost) |
|||
{ |
|||
root.updateVerification(bn, trLost) |
|||
}); |
|||
} |
|||
} |
|||
|
|||
function updateVerification(blockNumber, trLost) |
|||
{ |
|||
var nb = parseInt(blockNumber - projectModel.deployBlockNumber) |
|||
verificationTextArea.visible = false |
|||
verificationLabel.visible = true |
|||
if (nb >= 10) |
|||
{ |
|||
verificationLabel.text = qsTr("contracts deployment verified") |
|||
verificationLabel.color = "green" |
|||
} |
|||
else |
|||
{ |
|||
verificationLabel.text = nb |
|||
if (trLost.length > 0) |
|||
{ |
|||
verifyDeploy = false |
|||
verificationTextArea.visible = true |
|||
verificationLabel.visible = false |
|||
verificationTextArea.text = "" |
|||
deploymentStepChanged("following transactions are invalidated:") |
|||
verificationTextArea.text += "\n" + qsTr("Transactions lost") + "\n" |
|||
verificationTextArea.textColor = "red" |
|||
for (var k in trLost) |
|||
{ |
|||
deploymentStepChanged(trLost[k]) |
|||
verificationTextArea.text += trLost[k] + "\n" |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
RowLayout |
|||
{ |
|||
anchors.fill: parent |
|||
anchors.margins: 10 |
|||
ColumnLayout |
|||
{ |
|||
anchors.top: parent.top |
|||
Layout.preferredWidth: parent.width * 0.40 - 20 |
|||
Layout.fillHeight: true |
|||
id: scenarioList |
|||
|
|||
Label |
|||
{ |
|||
Layout.fillWidth: true |
|||
text: qsTr("Pick Scenario to deploy") |
|||
} |
|||
|
|||
ComboBox |
|||
{ |
|||
id: contractList |
|||
Layout.preferredWidth: parent.width - 20 |
|||
model: projectModel.stateListModel |
|||
textRole: "title" |
|||
onCurrentIndexChanged: |
|||
{ |
|||
if (root.visible) |
|||
change() |
|||
} |
|||
|
|||
function change() |
|||
{ |
|||
trListModel.clear() |
|||
if (currentIndex > -1) |
|||
{ |
|||
for (var k = 0; k < projectModel.stateListModel.get(currentIndex).blocks.count; k++) |
|||
{ |
|||
for (var j = 0; j < projectModel.stateListModel.get(currentIndex).blocks.get(k).transactions.count; j++) |
|||
trListModel.append(projectModel.stateListModel.get(currentIndex).blocks.get(k).transactions.get(j)); |
|||
} |
|||
for (var k = 0; k < trListModel.count; k++) |
|||
trList.itemAt(k).init() |
|||
ctrDeployCtrLabel.calculateContractDeployGas(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
Layout.fillHeight: true |
|||
Layout.preferredWidth: parent.width - 20 |
|||
id: trContainer |
|||
color: "white" |
|||
border.color: "#cccccc" |
|||
border.width: 1 |
|||
ScrollView |
|||
{ |
|||
anchors.fill: parent |
|||
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff |
|||
ColumnLayout |
|||
{ |
|||
spacing: 0 |
|||
|
|||
ListModel |
|||
{ |
|||
id: trListModel |
|||
} |
|||
|
|||
Repeater |
|||
{ |
|||
id: trList |
|||
model: trListModel |
|||
ColumnLayout |
|||
{ |
|||
Layout.fillWidth: true |
|||
spacing: 5 |
|||
Layout.preferredHeight: |
|||
{ |
|||
if (index > -1) |
|||
return 20 + trListModel.get(index)["parameters"].count * 20 |
|||
else |
|||
return 20 |
|||
} |
|||
|
|||
function init() |
|||
{ |
|||
paramList.clear() |
|||
if (trListModel.get(index).parameters) |
|||
{ |
|||
for (var k in trListModel.get(index).parameters) |
|||
paramList.append({ "name": k, "value": trListModel.get(index).parameters[k] }) |
|||
} |
|||
} |
|||
|
|||
Label |
|||
{ |
|||
id: trLabel |
|||
Layout.preferredHeight: 20 |
|||
anchors.left: parent.left |
|||
anchors.top: parent.top |
|||
anchors.topMargin: 5 |
|||
anchors.leftMargin: 10 |
|||
text: |
|||
{ |
|||
if (index > -1) |
|||
return trListModel.get(index).label |
|||
else |
|||
return "" |
|||
} |
|||
} |
|||
|
|||
ListModel |
|||
{ |
|||
id: paramList |
|||
} |
|||
|
|||
Repeater |
|||
{ |
|||
Layout.preferredHeight: |
|||
{ |
|||
if (index > -1) |
|||
return trListModel.get(index)["parameters"].count * 20 |
|||
else |
|||
return 0 |
|||
} |
|||
model: paramList |
|||
Label |
|||
{ |
|||
Layout.preferredHeight: 20 |
|||
anchors.left: parent.left |
|||
anchors.leftMargin: 20 |
|||
text: name + "=" + value |
|||
font.italic: true |
|||
} |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
Layout.preferredWidth: scenarioList.width |
|||
Layout.preferredHeight: 1 |
|||
color: "#cccccc" |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
ColumnLayout |
|||
{ |
|||
anchors.top: parent.top |
|||
Layout.preferredHeight: parent.height - 25 |
|||
ColumnLayout |
|||
{ |
|||
anchors.top: parent.top |
|||
Layout.preferredWidth: parent.width * 0.60 |
|||
Layout.fillHeight: true |
|||
id: deploymentOption |
|||
spacing: 8 |
|||
|
|||
Label |
|||
{ |
|||
anchors.left: parent.left |
|||
anchors.leftMargin: 105 |
|||
text: qsTr("Deployment options") |
|||
} |
|||
|
|||
ListModel |
|||
{ |
|||
id: accountsModel |
|||
} |
|||
|
|||
RowLayout |
|||
{ |
|||
Layout.fillWidth: true |
|||
Rectangle |
|||
{ |
|||
width: labelWidth |
|||
Label |
|||
{ |
|||
text: qsTr("Account") |
|||
anchors.right: parent.right |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
} |
|||
} |
|||
|
|||
ComboBox |
|||
{ |
|||
id: accountsList |
|||
textRole: "id" |
|||
model: accountsModel |
|||
Layout.preferredWidth: 235 |
|||
onCurrentTextChanged: |
|||
{ |
|||
worker.currentAccount = currentText |
|||
accountBalance.text = worker.balance(currentText).format() |
|||
} |
|||
} |
|||
|
|||
Label |
|||
{ |
|||
id: accountBalance |
|||
} |
|||
} |
|||
|
|||
RowLayout |
|||
{ |
|||
Layout.fillWidth: true |
|||
Rectangle |
|||
{ |
|||
width: labelWidth |
|||
Label |
|||
{ |
|||
text: qsTr("Gas Price") |
|||
anchors.right: parent.right |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
} |
|||
} |
|||
|
|||
Ether |
|||
{ |
|||
id: gasPriceInput |
|||
displayUnitSelection: true |
|||
displayFormattedValue: true |
|||
edit: true |
|||
|
|||
function toHexWei() |
|||
{ |
|||
return "0x" + gasPriceInput.value.toWei().hexValue() |
|||
} |
|||
} |
|||
|
|||
Connections |
|||
{ |
|||
target: gasPriceInput |
|||
onValueChanged: |
|||
{ |
|||
ctrDeployCtrLabel.calculateContractDeployGas() |
|||
} |
|||
onAmountChanged: |
|||
{ |
|||
ctrDeployCtrLabel.setCost() |
|||
} |
|||
onUnitChanged: |
|||
{ |
|||
ctrDeployCtrLabel.setCost() |
|||
} |
|||
} |
|||
|
|||
Connections |
|||
{ |
|||
target: worker |
|||
id: gasPriceLoad |
|||
property bool loaded: false |
|||
onGasPriceLoaded: |
|||
{ |
|||
gasPriceInput.value = QEtherHelper.createEther(worker.gasPriceInt.value(), QEther.Wei) |
|||
gasPriceLoad.loaded = true |
|||
ctrDeployCtrLabel.calculateContractDeployGas() |
|||
} |
|||
} |
|||
} |
|||
|
|||
RowLayout |
|||
{ |
|||
id: ctrDeployCtrLabel |
|||
Layout.fillWidth: true |
|||
property int cost |
|||
function calculateContractDeployGas() |
|||
{ |
|||
if (!root.visible) |
|||
return; |
|||
var sce = projectModel.stateListModel.getState(contractList.currentIndex) |
|||
worker.estimateGas(sce, function(gas) { |
|||
if (gasPriceLoad.loaded) |
|||
{ |
|||
root.gas = gas |
|||
cost = 0 |
|||
for (var k in gas) |
|||
cost += gas[k] |
|||
setCost() |
|||
} |
|||
}); |
|||
} |
|||
|
|||
function setCost() |
|||
{ |
|||
var ether = QEtherHelper.createBigInt(cost); |
|||
var gasTotal = ether.multiply(gasPriceInput.value); |
|||
gasToUseInput.value = QEtherHelper.createEther(gasTotal.value(), QEther.Wei, parent); |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
width: labelWidth |
|||
Label |
|||
{ |
|||
text: qsTr("Deployment Cost") |
|||
anchors.right: parent.right |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
} |
|||
} |
|||
|
|||
Ether |
|||
{ |
|||
id: gasToUseInput |
|||
displayUnitSelection: false |
|||
displayFormattedValue: true |
|||
edit: false |
|||
Layout.preferredWidth: 350 |
|||
} |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
border.color: "#cccccc" |
|||
border.width: 2 |
|||
Layout.fillWidth: true |
|||
Layout.preferredHeight: parent.height + 25 |
|||
color: "transparent" |
|||
id: rectDeploymentVariable |
|||
ScrollView |
|||
{ |
|||
anchors.fill: parent |
|||
anchors.topMargin: 4 |
|||
anchors.bottomMargin: 4 |
|||
ColumnLayout |
|||
{ |
|||
RowLayout |
|||
{ |
|||
id: deployedRow |
|||
Layout.fillWidth: true |
|||
Rectangle |
|||
{ |
|||
width: labelWidth |
|||
Label |
|||
{ |
|||
id: labelAddresses |
|||
text: qsTr("Deployed Contracts") |
|||
anchors.right: parent.right |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
} |
|||
} |
|||
|
|||
ColumnLayout |
|||
{ |
|||
anchors.top: parent.top |
|||
anchors.topMargin: 1 |
|||
width: parent.width |
|||
id: deployedAddresses |
|||
function refresh() |
|||
{ |
|||
textAddresses.text = "" |
|||
deployedRow.visible = Object.keys(projectModel.deploymentAddresses).length > 0 |
|||
textAddresses.text = JSON.stringify(projectModel.deploymentAddresses, null, ' ') |
|||
} |
|||
TextArea |
|||
{ |
|||
anchors.fill: parent |
|||
id: textAddresses |
|||
} |
|||
} |
|||
} |
|||
|
|||
RowLayout |
|||
{ |
|||
id: verificationRow |
|||
Layout.fillWidth: true |
|||
visible: Object.keys(projectModel.deploymentAddresses).length > 0 |
|||
Rectangle |
|||
{ |
|||
width: labelWidth |
|||
Label |
|||
{ |
|||
text: qsTr("Verifications") |
|||
anchors.right: parent.right |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
} |
|||
} |
|||
|
|||
TextArea |
|||
{ |
|||
id: verificationTextArea |
|||
visible: false |
|||
} |
|||
|
|||
Label |
|||
{ |
|||
id: verificationLabel |
|||
visible: true |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
Layout.preferredWidth: parent.width |
|||
Layout.alignment: Qt.BottomEdge |
|||
Button |
|||
{ |
|||
Layout.preferredHeight: 22 |
|||
anchors.right: deployBtn.left |
|||
text: qsTr("Reset") |
|||
action: clearDeployAction |
|||
} |
|||
|
|||
Action { |
|||
id: clearDeployAction |
|||
onTriggered: { |
|||
worker.forceStopPooling() |
|||
if (projectModel.deploymentDir && projectModel.deploymentDir !== "") |
|||
fileIo.deleteDir(projectModel.deploymentDir) |
|||
projectModel.cleanDeploymentStatus() |
|||
deploymentDialog.steps.reset() |
|||
} |
|||
} |
|||
|
|||
Button |
|||
{ |
|||
id: deployBtn |
|||
anchors.right: parent.right |
|||
text: qsTr("Deploy Contracts") |
|||
onClicked: |
|||
{ |
|||
projectModel.deployedScenarioIndex = contractList.currentIndex |
|||
NetworkDeploymentCode.deployContracts(root.gas, gasPriceInput.toHexWei(), function(addresses, trHashes) |
|||
{ |
|||
projectModel.deploymentTrHashes = trHashes |
|||
worker.verifyHashes(trHashes, function (nb, trLost) |
|||
{ |
|||
projectModel.deployBlockNumber = nb |
|||
projectModel.saveProject() |
|||
root.updateVerification(nb, trLost) |
|||
root.deployed() |
|||
}) |
|||
projectModel.deploymentAddresses = addresses |
|||
projectModel.saveProject() |
|||
deployedAddresses.refresh() |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
@ -1,187 +0,0 @@ |
|||
import QtQuick 2.2 |
|||
import QtQuick.Controls 1.1 |
|||
import QtQuick.Layouts 1.1 |
|||
import QtQuick.Window 2.0 |
|||
import QtQuick.Dialogs 1.2 |
|||
import QtQuick.Controls.Styles 1.3 |
|||
import org.ethereum.qml.QEther 1.0 |
|||
import "js/TransactionHelper.js" as TransactionHelper |
|||
import "js/NetworkDeployment.js" as NetworkDeploymentCode |
|||
import "js/QEtherHelper.js" as QEtherHelper |
|||
import "." |
|||
|
|||
|
|||
Dialog { |
|||
id: modalDeploymentDialog |
|||
modality: Qt.ApplicationModal |
|||
width: 1000 |
|||
height: 450 |
|||
visible: false |
|||
|
|||
property alias deployStep: deployStep |
|||
property alias packageStep: packageStep |
|||
property alias registerStep: registerStep |
|||
property alias worker: worker |
|||
property alias steps: steps |
|||
|
|||
function close() |
|||
{ |
|||
visible = false; |
|||
worker.pooler.running = false |
|||
} |
|||
|
|||
function open() |
|||
{ |
|||
deployStep.visible = false |
|||
packageStep.visible = false |
|||
registerStep.visible = false |
|||
steps.init() |
|||
worker.renewCtx() |
|||
worker.pooler.running = true |
|||
visible = true; |
|||
} |
|||
|
|||
DeploymentWorker |
|||
{ |
|||
id: worker |
|||
} |
|||
|
|||
contentItem: Rectangle { |
|||
color: appStyle.generic.layout.backgroundColor |
|||
anchors.fill: parent |
|||
ColumnLayout |
|||
{ |
|||
spacing: 5 |
|||
anchors.fill: parent |
|||
anchors.margins: 10 |
|||
|
|||
Rectangle |
|||
{ |
|||
id: explanation |
|||
Layout.preferredWidth: parent.width - 50 |
|||
Layout.preferredHeight: 50 |
|||
color: "transparent" |
|||
Label |
|||
{ |
|||
id: info |
|||
anchors.centerIn: parent |
|||
text: qsTr("Putting your dapp live is a multi step process. You can read more about it on the") |
|||
} |
|||
|
|||
Text { |
|||
anchors.left: info.right |
|||
anchors.leftMargin: 7 |
|||
id: linkText |
|||
text: '<html><style type="text/css"></style><a href="https://github.com/ethereum/wiki/wiki/Mix:-The-DApp-IDE#deployment-to-network">guide to uploading</a></html>' |
|||
onLinkActivated: Qt.openUrlExternally("https://github.com/ethereum/wiki/wiki/Mix:-The-DApp-IDE#deployment-to-network") |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
MouseArea |
|||
{ |
|||
anchors.fill: parent |
|||
cursorShape: Qt.PointingHandCursor |
|||
} |
|||
} |
|||
} |
|||
|
|||
RowLayout |
|||
{ |
|||
ColumnLayout |
|||
{ |
|||
Layout.preferredHeight: parent.height - 50 |
|||
Layout.preferredWidth: 200 |
|||
DeploymentDialogSteps |
|||
{ |
|||
id: steps |
|||
worker: worker |
|||
} |
|||
} |
|||
|
|||
Connections |
|||
{ |
|||
target: steps |
|||
property variant selected |
|||
onSelected: |
|||
{ |
|||
if (selected) |
|||
selected.visible = false |
|||
switch (step) |
|||
{ |
|||
case "deploy": |
|||
{ |
|||
selected = deployStep |
|||
break; |
|||
} |
|||
case "package": |
|||
{ |
|||
selected = packageStep |
|||
break; |
|||
} |
|||
case "register": |
|||
{ |
|||
selected = registerStep |
|||
break; |
|||
} |
|||
} |
|||
selected.show() |
|||
} |
|||
} |
|||
|
|||
ColumnLayout |
|||
{ |
|||
Layout.preferredHeight: parent.height - 50 |
|||
Layout.preferredWidth: parent.width - 200 |
|||
DeployContractStep |
|||
{ |
|||
id: deployStep |
|||
visible: false |
|||
worker: worker |
|||
} |
|||
|
|||
PackagingStep |
|||
{ |
|||
id: packageStep |
|||
visible: false |
|||
worker: worker |
|||
} |
|||
|
|||
RegisteringStep |
|||
{ |
|||
id: registerStep |
|||
visible: false |
|||
worker: worker |
|||
} |
|||
} |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
Layout.fillWidth: true |
|||
Layout.preferredHeight: 30 |
|||
color: "transparent" |
|||
Button |
|||
{ |
|||
text: qsTr("Cancel") |
|||
anchors.right: parent.right |
|||
anchors.rightMargin: 10 |
|||
onClicked: |
|||
{ |
|||
modalDeploymentDialog.close() |
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
|||
MessageDialog { |
|||
id: deployDialog |
|||
standardButtons: StandardButton.Ok |
|||
icon: StandardIcon.Warning |
|||
} |
|||
|
|||
MessageDialog { |
|||
id: errorDialog |
|||
standardButtons: StandardButton.Ok |
|||
icon: StandardIcon.Critical |
|||
} |
|||
} |
@ -1,292 +0,0 @@ |
|||
import QtQuick 2.0 |
|||
import QtQuick.Layouts 1.0 |
|||
import QtQuick.Controls 1.1 |
|||
import QtQuick.Controls.Styles 1.3 |
|||
|
|||
Rectangle { |
|||
anchors.fill: parent |
|||
color: "white" |
|||
property var worker |
|||
property variant sel |
|||
signal selected(string step) |
|||
id: root |
|||
|
|||
function refreshCurrent() |
|||
{ |
|||
menu.itemAt(sel).select() |
|||
} |
|||
|
|||
function init() |
|||
{ |
|||
menu.itemAt(0).select() |
|||
} |
|||
|
|||
function itemClicked(step) |
|||
{ |
|||
selected(step) |
|||
} |
|||
|
|||
function reset() |
|||
{ |
|||
for (var k in deployLogs.logs) |
|||
{ |
|||
deployLogs.logs[k] = "" |
|||
} |
|||
deployLogs.switchLogs() |
|||
refreshCurrent() |
|||
} |
|||
|
|||
border.color: "#cccccc" |
|||
border.width: 1 |
|||
|
|||
|
|||
Connections |
|||
{ |
|||
id: deployStatus |
|||
target: deploymentDialog.deployStep |
|||
onDeployed: |
|||
{ |
|||
console.log("deployed") |
|||
} |
|||
} |
|||
|
|||
Connections |
|||
{ |
|||
id: packagedStatus |
|||
target: deploymentDialog.packageStep |
|||
onPackaged: |
|||
{ |
|||
console.log("packaged") |
|||
} |
|||
} |
|||
|
|||
Connections |
|||
{ |
|||
id: registerStatus |
|||
target: deploymentDialog.registerStep |
|||
onRegistered: |
|||
{ |
|||
console.log("registered") |
|||
} |
|||
} |
|||
|
|||
ColumnLayout |
|||
{ |
|||
anchors.fill: parent |
|||
anchors.margins: 1 |
|||
spacing: 0 |
|||
Repeater |
|||
{ |
|||
id: menu |
|||
model: [ |
|||
{ |
|||
step: 1, |
|||
type:"deploy", |
|||
label: qsTr("Deploy contracts") |
|||
}, |
|||
{ |
|||
step: 2, |
|||
type:"package", |
|||
label: qsTr("Package Dapp") |
|||
}, |
|||
{ |
|||
step: 3, |
|||
type:"register", |
|||
label: qsTr("Register Dapp") |
|||
} |
|||
] |
|||
|
|||
Rectangle |
|||
{ |
|||
Layout.preferredHeight: 50 |
|||
Layout.fillWidth: true |
|||
color: "white" |
|||
id: itemContainer |
|||
|
|||
function select() |
|||
{ |
|||
if (sel !== undefined) |
|||
{ |
|||
menu.itemAt(sel).unselect() |
|||
} |
|||
labelContainer.state = "selected" |
|||
sel = index |
|||
itemClicked(menu.model[index].type) |
|||
deployLogs.switchLogs() |
|||
} |
|||
|
|||
function unselect() |
|||
{ |
|||
labelContainer.state = "" |
|||
} |
|||
|
|||
Rectangle { |
|||
width: 40 |
|||
height: 40 |
|||
color: "transparent" |
|||
border.color: "#cccccc" |
|||
border.width: 2 |
|||
radius: width*0.5 |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
anchors.left: parent.left |
|||
anchors.leftMargin: 10 |
|||
id: labelContainer |
|||
Label |
|||
{ |
|||
color: "#cccccc" |
|||
id: label |
|||
anchors.centerIn: parent |
|||
text: menu.model[index].step |
|||
} |
|||
states: [ |
|||
State { |
|||
name: "selected" |
|||
PropertyChanges { target: label; color: "white" } |
|||
PropertyChanges { target: labelContainer.border; color: "white" } |
|||
PropertyChanges { target: detail; color: "white" } |
|||
PropertyChanges { target: itemContainer; color: "#3395FE" } |
|||
} |
|||
] |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
anchors.left: label.parent.right |
|||
width: parent.width - 40 |
|||
height: 40 |
|||
color: "transparent" |
|||
Label |
|||
{ |
|||
id: detail |
|||
color: "black" |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
anchors.left: parent.left |
|||
anchors.leftMargin: 10 |
|||
text: menu.model[index].label |
|||
} |
|||
} |
|||
|
|||
MouseArea |
|||
{ |
|||
anchors.fill: parent |
|||
onClicked: |
|||
{ |
|||
itemContainer.select() |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
Connections { |
|||
property var logs: ({}) |
|||
id: deployLogs |
|||
|
|||
function switchLogs() |
|||
{ |
|||
if (root.sel) |
|||
{ |
|||
if (!logs[root.sel]) |
|||
logs[root.sel] = "" |
|||
log.text = logs[root.sel] |
|||
} |
|||
} |
|||
|
|||
target: projectModel |
|||
onDeploymentStarted: |
|||
{ |
|||
if (!logs[root.sel]) |
|||
logs[root.sel] = "" |
|||
logs[root.sel] = logs[root.sel] + qsTr("Running deployment...") + "\n" |
|||
log.text = logs[root.sel] |
|||
} |
|||
|
|||
onDeploymentError: |
|||
{ |
|||
if (!logs[root.sel]) |
|||
logs[root.sel] = "" |
|||
logs[root.sel] = logs[root.sel] + error + "\n" |
|||
log.text = logs[root.sel] |
|||
} |
|||
|
|||
onDeploymentComplete: |
|||
{ |
|||
if (!logs[root.sel]) |
|||
logs[root.sel] = "" |
|||
logs[root.sel] = logs[root.sel] + qsTr("Deployment complete") + "\n" |
|||
log.text = logs[root.sel] |
|||
} |
|||
|
|||
onDeploymentStepChanged: |
|||
{ |
|||
if (!logs[root.sel]) |
|||
logs[root.sel] = "" |
|||
logs[root.sel] = logs[root.sel] + message + "\n" |
|||
log.text = logs[root.sel] |
|||
} |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
Layout.fillWidth: true |
|||
Layout.preferredHeight: 2 |
|||
color: "#cccccc" |
|||
} |
|||
|
|||
ScrollView |
|||
{ |
|||
Layout.fillHeight: true |
|||
Layout.fillWidth: true |
|||
Text |
|||
{ |
|||
anchors.left: parent.left |
|||
anchors.leftMargin: 2 |
|||
font.pointSize: 9 |
|||
font.italic: true |
|||
id: log |
|||
} |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
Layout.preferredHeight: 20 |
|||
Layout.fillWidth: true |
|||
color: "#cccccc" |
|||
LogsPaneStyle |
|||
{ |
|||
id: style |
|||
} |
|||
|
|||
Label |
|||
{ |
|||
anchors.horizontalCenter: parent.horizontalCenter |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
text: qsTr("Logs") |
|||
font.italic: true |
|||
font.pointSize: style.absoluteSize(-1) |
|||
} |
|||
|
|||
Button |
|||
{ |
|||
height: 20 |
|||
width: 20 |
|||
anchors.right: parent.right |
|||
action: clearAction |
|||
iconSource: "qrc:/qml/img/cleariconactive.png" |
|||
tooltip: qsTr("Clear Messages") |
|||
} |
|||
|
|||
Action { |
|||
id: clearAction |
|||
enabled: log.text !== "" |
|||
tooltip: qsTr("Clear") |
|||
onTriggered: { |
|||
deployLogs.logs[root.sel] = "" |
|||
log.text = deployLogs.logs[root.sel] |
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
@ -1,250 +0,0 @@ |
|||
import QtQuick 2.2 |
|||
import QtQuick.Controls 1.1 |
|||
import QtQuick.Layouts 1.1 |
|||
import QtQuick.Window 2.0 |
|||
import QtQuick.Dialogs 1.2 |
|||
import QtQuick.Controls.Styles 1.3 |
|||
import org.ethereum.qml.QEther 1.0 |
|||
import org.ethereum.qml.CodeModel 1.0 |
|||
import org.ethereum.qml.ClientModel 1.0 |
|||
import "js/TransactionHelper.js" as TransactionHelper |
|||
import "js/NetworkDeployment.js" as NetworkDeploymentCode |
|||
import "js/QEtherHelper.js" as QEtherHelper |
|||
import "." |
|||
|
|||
Item |
|||
{ |
|||
property string currentAccount |
|||
property string gasPrice |
|||
property alias gasPriceInt: gasPriceInt |
|||
property variant balances: ({}) |
|||
property variant accounts: [] |
|||
property alias pooler: pooler |
|||
signal gasPriceLoaded() |
|||
|
|||
function renewCtx() |
|||
{ |
|||
accounts = [] |
|||
balances = {} |
|||
var requests = [{ |
|||
//accounts |
|||
jsonrpc: "2.0", |
|||
method: "eth_accounts", |
|||
params: null, |
|||
id: 0 |
|||
}]; |
|||
|
|||
TransactionHelper.rpcCall(requests, function(arg1, arg2) |
|||
{ |
|||
|
|||
var ids = JSON.parse(arg2)[0].result; |
|||
requests = []; |
|||
accounts = [] |
|||
for (var k in ids) |
|||
{ |
|||
requests.push({ |
|||
//accounts |
|||
jsonrpc: "2.0", |
|||
method: "eth_getBalance", |
|||
params: [ids[k], 'latest'], |
|||
id: k |
|||
}); |
|||
accounts.push({ "id": ids[k] }) |
|||
} |
|||
|
|||
TransactionHelper.rpcCall(requests, function (request, response){ |
|||
var balanceRet = JSON.parse(response); |
|||
balances = {} |
|||
for (var k in balanceRet) |
|||
{ |
|||
var ether = QEtherHelper.createEther(balanceRet[k].result, QEther.Wei); |
|||
balances[accounts[k].id] = ether |
|||
} |
|||
}, function(){}); |
|||
}, function(){}); |
|||
|
|||
NetworkDeploymentCode.gasPrice(function(price) { |
|||
gasPrice = price; |
|||
gasPriceInt.setValue(price); |
|||
gasPriceLoaded() |
|||
}, function(){}); |
|||
} |
|||
|
|||
function balance(account) |
|||
{ |
|||
for (var k in accounts) |
|||
{ |
|||
if (accounts[k].id === account) |
|||
return balances[account] |
|||
} |
|||
return null |
|||
} |
|||
|
|||
function stopForInputError(inError) |
|||
{ |
|||
errorDialog.text = ""; |
|||
if (inError.length > 0) |
|||
{ |
|||
errorDialog.text = qsTr("The length of a string cannot exceed 32 characters.\nPlease verify the following value(s):\n\n") |
|||
for (var k in inError) |
|||
errorDialog.text += inError[k] + "\n"; |
|||
errorDialog.open(); |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
function forceStopPooling() |
|||
{ |
|||
poolLog.stop() |
|||
} |
|||
|
|||
function waitForTrReceipt(hash, callback) |
|||
{ |
|||
poolLog.callBack = callback; |
|||
poolLog.hash = hash |
|||
poolLog.elapsed = 0; |
|||
poolLog.start(); |
|||
} |
|||
|
|||
function verifyHash(tr, hash, callBack) |
|||
{ |
|||
var h = {} |
|||
h[tr] = hash |
|||
verifyHashes(h, function (bn, trLost) |
|||
{ |
|||
callBack(bn, trLost) |
|||
}); |
|||
} |
|||
|
|||
function verifyHashes(trHashes, callback) |
|||
{ |
|||
//trHashes : { "trLabel": 'hash' } |
|||
var requests = []; |
|||
var req = 0 |
|||
requests.push({ |
|||
jsonrpc: "2.0", |
|||
method: "eth_blockNumber", |
|||
params: [], |
|||
id: req |
|||
}); |
|||
var label = [] |
|||
for (var k in trHashes) |
|||
{ |
|||
req++ |
|||
label[req] = k |
|||
requests.push({ |
|||
jsonrpc: "2.0", |
|||
method: "eth_getTransactionReceipt", |
|||
params: [trHashes[k]], |
|||
id: req |
|||
}); |
|||
} |
|||
|
|||
TransactionHelper.rpcCall(requests, function (httpRequest, response){ |
|||
var ret = JSON.parse(response) |
|||
var b = ret[0].result; |
|||
var trLost = [] |
|||
for (var k in ret) |
|||
{ |
|||
if (!ret[k].result) |
|||
trLost.push(label[ret[k].id]) |
|||
} |
|||
callback(parseInt(b, 16), trLost) |
|||
}); |
|||
} |
|||
|
|||
Component.onCompleted: |
|||
{ |
|||
renewCtx() |
|||
} |
|||
|
|||
BigIntValue |
|||
{ |
|||
id: gasPriceInt |
|||
} |
|||
|
|||
function estimateGas(scenario, callback) |
|||
{ |
|||
if (!clientModelGasEstimation.running) |
|||
{ |
|||
for (var si = 0; si < projectModel.listModel.count; si++) |
|||
{ |
|||
var document = projectModel.listModel.get(si); |
|||
if (document.isContract) |
|||
codeModelGasEstimation.registerCodeChange(document.documentId, fileIo.readFile(document.path)); |
|||
} |
|||
gasEstimationConnect.callback = callback |
|||
clientModelGasEstimation.setupScenario(scenario) |
|||
} |
|||
} |
|||
|
|||
Connections |
|||
{ |
|||
id: gasEstimationConnect |
|||
target: clientModelGasEstimation |
|||
property var callback |
|||
onRunComplete: { |
|||
callback(clientModelGasEstimation.gasCosts) |
|||
} |
|||
} |
|||
|
|||
CodeModel { |
|||
id: codeModelGasEstimation |
|||
} |
|||
|
|||
ClientModel |
|||
{ |
|||
id: clientModelGasEstimation |
|||
codeModel: codeModelGasEstimation |
|||
Component.onCompleted: |
|||
{ |
|||
init("/tmp/bcgas/") |
|||
} |
|||
} |
|||
|
|||
Timer |
|||
{ |
|||
id: pooler |
|||
interval: 5000 |
|||
repeat: true |
|||
running: false |
|||
} |
|||
|
|||
Timer |
|||
{ |
|||
id: poolLog |
|||
property var callBack |
|||
property int elapsed |
|||
property string hash |
|||
interval: 2000 |
|||
running: false |
|||
repeat: true |
|||
onTriggered: { |
|||
elapsed += interval; |
|||
var requests = []; |
|||
var jsonRpcRequestId = 0; |
|||
requests.push({ |
|||
jsonrpc: "2.0", |
|||
method: "eth_getTransactionReceipt", |
|||
params: [ hash ], |
|||
id: jsonRpcRequestId++ |
|||
}); |
|||
TransactionHelper.rpcCall(requests, function (httpRequest, response){ |
|||
console.log(response) |
|||
var receipt = JSON.parse(response)[0].result |
|||
if (receipt) |
|||
{ |
|||
stop(); |
|||
callBack(1, receipt); |
|||
} |
|||
else if (elapsed > 2500000) |
|||
{ |
|||
stop(); |
|||
callBack(-1, null); |
|||
} |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
|
@ -1,99 +0,0 @@ |
|||
/* |
|||
* Display a row containing : |
|||
* - The amount of Ether. |
|||
* - The unit used. |
|||
* - User-friendly string representation of the amout of Ether (if displayFormattedValue == true). |
|||
* 'value' has to be a QEther obj. |
|||
*/ |
|||
import QtQuick 2.2 |
|||
import QtQuick.Controls 1.1 |
|||
import QtQuick.Layouts 1.1 |
|||
import QtQuick.Controls.Styles 1.1 |
|||
|
|||
RowLayout { |
|||
id: etherEdition |
|||
property bool displayFormattedValue; |
|||
property bool edit; |
|||
property variant value; |
|||
property bool displayUnitSelection |
|||
onValueChanged: update() |
|||
Component.onCompleted: update() |
|||
signal amountChanged |
|||
signal unitChanged |
|||
|
|||
|
|||
function update() |
|||
{ |
|||
if (value) |
|||
{ |
|||
etherValueEdit.text = value.value; |
|||
selectUnit(value.unit); |
|||
} |
|||
} |
|||
|
|||
function selectUnit(unit) |
|||
{ |
|||
units.currentIndex = unit; |
|||
} |
|||
|
|||
|
|||
DefaultTextField |
|||
{ |
|||
onTextChanged: |
|||
{ |
|||
if (value !== undefined) |
|||
{ |
|||
value.setValue(text) |
|||
formattedValue.text = value.format(); |
|||
amountChanged() |
|||
} |
|||
} |
|||
readOnly: !edit |
|||
visible: edit |
|||
id: etherValueEdit; |
|||
} |
|||
|
|||
ComboBox |
|||
{ |
|||
id: units |
|||
visible: displayUnitSelection; |
|||
onCurrentTextChanged: |
|||
{ |
|||
if (value) |
|||
{ |
|||
value.setUnit(currentText); |
|||
formattedValue.text = value.format(); |
|||
unitChanged() |
|||
} |
|||
} |
|||
|
|||
model: ListModel { |
|||
id: unitsModel |
|||
ListElement { text: "Uether"; } |
|||
ListElement { text: "Vether"; } |
|||
ListElement { text: "Dether"; } |
|||
ListElement { text: "Nether"; } |
|||
ListElement { text: "Yether"; } |
|||
ListElement { text: "Zether"; } |
|||
ListElement { text: "Eether"; } |
|||
ListElement { text: "Pether"; } |
|||
ListElement { text: "Tether"; } |
|||
ListElement { text: "Gether"; } |
|||
ListElement { text: "Mether"; } |
|||
ListElement { text: "grand"; } |
|||
ListElement { text: "ether"; } |
|||
ListElement { text: "finney"; } |
|||
ListElement { text: "szabo"; } |
|||
ListElement { text: "Gwei"; } |
|||
ListElement { text: "Mwei"; } |
|||
ListElement { text: "Kwei"; } |
|||
ListElement { text: "wei"; } |
|||
} |
|||
} |
|||
|
|||
Text |
|||
{ |
|||
visible: displayFormattedValue |
|||
id: formattedValue |
|||
} |
|||
} |
@ -1,15 +0,0 @@ |
|||
/* |
|||
* Used to instanciate a QEther obj using Qt.createComponent function. |
|||
*/ |
|||
import QtQuick 2.2 |
|||
import QtQuick.Controls 1.1 |
|||
import QtQuick.Layouts 1.1 |
|||
import QtQuick.Controls.Styles 1.1 |
|||
import org.ethereum.qml.QEther 1.0 |
|||
|
|||
QEther |
|||
{ |
|||
id: basicEther |
|||
value: "100000000000" |
|||
unit: QEther.Wei |
|||
} |
@ -1,302 +0,0 @@ |
|||
import QtQuick 2.0 |
|||
import QtQuick.Window 2.0 |
|||
import QtQuick.Layouts 1.0 |
|||
import QtQuick.Controls 1.0 |
|||
import QtQuick.Controls.Styles 1.3 |
|||
import QtQuick.Dialogs 1.2 |
|||
import "." |
|||
|
|||
|
|||
Rectangle |
|||
{ |
|||
Layout.fillWidth: true |
|||
Layout.minimumHeight: hiddenHeightTopLevel() |
|||
height: hiddenHeightTopLevel() |
|||
Layout.maximumHeight: hiddenHeightTopLevel() |
|||
id: wrapperItem |
|||
signal documentSelected(string doc, string groupName) |
|||
property alias model: filesList.model |
|||
property string sectionName; |
|||
property variant selManager; |
|||
property int index; |
|||
color: index % 2 === 0 ? "transparent" : projectFilesStyle.title.background |
|||
|
|||
function hiddenHeightTopLevel() |
|||
{ |
|||
return section.state === "hidden" ? projectFilesStyle.documentsList.height : projectFilesStyle.documentsList.fileNameHeight * model.count + projectFilesStyle.documentsList.height; |
|||
} |
|||
|
|||
function hiddenHeightRepeater() |
|||
{ |
|||
return section.state === "hidden" ? 0 : projectFilesStyle.documentsList.fileNameHeight * wrapperItem.model.count; |
|||
} |
|||
|
|||
function hiddenHeightElement() |
|||
{ |
|||
return section.state === "hidden" ? 0 : projectFilesStyle.documentsList.fileNameHeight; |
|||
} |
|||
|
|||
function getDocumentIndex(documentId) |
|||
{ |
|||
for (var i = 0; i < model.count; i++) |
|||
if (model.get(i).documentId === documentId) |
|||
return i; |
|||
return -1; |
|||
} |
|||
|
|||
function removeDocument(documentId) |
|||
{ |
|||
var i = getDocumentIndex(documentId); |
|||
if (i !== -1) |
|||
model.remove(i); |
|||
} |
|||
|
|||
ColumnLayout { |
|||
anchors.fill: parent |
|||
spacing: 0 |
|||
|
|||
SourceSansProRegular |
|||
{ |
|||
id: fileNameFont |
|||
} |
|||
|
|||
SourceSansProBold |
|||
{ |
|||
id: boldFont |
|||
} |
|||
|
|||
RowLayout |
|||
{ |
|||
anchors.top: parent.top |
|||
id: rowCol |
|||
height: projectFilesStyle.documentsList.height |
|||
Layout.fillWidth: true |
|||
|
|||
|
|||
Image { |
|||
source: "qrc:/qml/img/opentriangleindicator_filesproject.png" |
|||
width: 15 |
|||
sourceSize.width: 12 |
|||
id: imgArrow |
|||
anchors.right: section.left |
|||
anchors.rightMargin: 8 |
|||
anchors.top: parent.top |
|||
anchors.topMargin: 10 |
|||
} |
|||
|
|||
Text |
|||
{ |
|||
id: section |
|||
text: sectionName |
|||
anchors.left: parent.left |
|||
anchors.leftMargin: projectFilesStyle.general.leftMargin |
|||
color: projectFilesStyle.documentsList.sectionColor |
|||
font.family: boldFont.name |
|||
font.pointSize: projectFilesStyle.documentsList.sectionFontSize |
|||
states: [ |
|||
State { |
|||
name: "hidden" |
|||
PropertyChanges { target: filesList; visible: false; } |
|||
PropertyChanges { target: rowCol; Layout.minimumHeight: projectFilesStyle.documentsList.height; Layout.maximumHeight: projectFilesStyle.documentsList.height; height: projectFilesStyle.documentsList.height; } |
|||
PropertyChanges { target: imgArrow; source: "qrc:/qml/img/closedtriangleindicator_filesproject.png" } |
|||
} |
|||
] |
|||
} |
|||
|
|||
MouseArea { |
|||
id: titleMouseArea |
|||
anchors.fill: parent |
|||
hoverEnabled: true |
|||
z: 2 |
|||
onClicked: { |
|||
if (section.state === "hidden") |
|||
section.state = ""; |
|||
else |
|||
section.state = "hidden"; |
|||
} |
|||
} |
|||
} |
|||
|
|||
ColumnLayout { |
|||
height: wrapperItem.hiddenHeightRepeater() |
|||
Layout.minimumHeight: wrapperItem.hiddenHeightRepeater() |
|||
Layout.preferredHeight: wrapperItem.hiddenHeightRepeater() |
|||
Layout.maximumHeight: wrapperItem.hiddenHeightRepeater() |
|||
width: parent.width |
|||
visible: section.state !== "hidden" |
|||
spacing: 0 |
|||
|
|||
Repeater |
|||
{ |
|||
id: filesList |
|||
visible: section.state !== "hidden" |
|||
Rectangle |
|||
{ |
|||
visible: section.state !== "hidden" |
|||
id: rootItem |
|||
Layout.fillWidth: true |
|||
Layout.minimumHeight: wrapperItem.hiddenHeightElement() |
|||
Layout.preferredHeight: wrapperItem.hiddenHeightElement() |
|||
Layout.maximumHeight: wrapperItem.hiddenHeightElement() |
|||
height: wrapperItem.hiddenHeightElement() |
|||
color: isSelected ? projectFilesStyle.documentsList.highlightColor : "transparent" |
|||
property bool isSelected |
|||
property bool renameMode |
|||
|
|||
Row { |
|||
spacing: 3 |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
anchors.fill: parent |
|||
anchors.left: parent.left |
|||
anchors.leftMargin: projectFilesStyle.general.leftMargin + 2 |
|||
Text { |
|||
id: nameText |
|||
height: parent.height |
|||
visible: !renameMode |
|||
color: rootItem.isSelected ? projectFilesStyle.documentsList.selectedColor : projectFilesStyle.documentsList.color |
|||
text: name; |
|||
font.family: fileNameFont.name |
|||
font.pointSize: projectFilesStyle.documentsList.fontSize |
|||
verticalAlignment: Text.AlignVCenter |
|||
|
|||
Connections |
|||
{ |
|||
target: selManager |
|||
onSelected: { |
|||
if (groupName != sectionName) |
|||
rootItem.isSelected = false; |
|||
else if (doc === documentId) |
|||
rootItem.isSelected = true; |
|||
else |
|||
rootItem.isSelected = false; |
|||
|
|||
if (rootItem.isSelected && section.state === "hidden") |
|||
section.state = ""; |
|||
} |
|||
onIsCleanChanged: { |
|||
if (groupName === sectionName && doc === documentId) |
|||
editStatusLabel.visible = !isClean; |
|||
} |
|||
} |
|||
} |
|||
|
|||
DefaultLabel { |
|||
id: editStatusLabel |
|||
visible: false |
|||
color: rootItem.isSelected ? projectFilesStyle.documentsList.selectedColor : projectFilesStyle.documentsList.color |
|||
verticalAlignment: Text.AlignVCenter |
|||
text: "*" |
|||
width: 10 |
|||
height: parent.height |
|||
} |
|||
} |
|||
|
|||
TextInput { |
|||
id: textInput |
|||
text: nameText.text |
|||
visible: renameMode |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
anchors.left: parent.left |
|||
anchors.leftMargin: projectFilesStyle.general.leftMargin |
|||
MouseArea { |
|||
id: textMouseArea |
|||
anchors.fill: parent |
|||
hoverEnabled: true |
|||
z: 2 |
|||
onClicked: { |
|||
textInput.forceActiveFocus(); |
|||
} |
|||
} |
|||
|
|||
onVisibleChanged: { |
|||
if (visible) { |
|||
selectAll(); |
|||
forceActiveFocus(); |
|||
} |
|||
} |
|||
|
|||
onAccepted: close(true); |
|||
onCursorVisibleChanged: { |
|||
if (!cursorVisible) |
|||
close(false); |
|||
} |
|||
onFocusChanged: { |
|||
if (!focus) |
|||
close(false); |
|||
} |
|||
function close(accept) { |
|||
rootItem.renameMode = false; |
|||
if (accept) |
|||
{ |
|||
var i = getDocumentIndex(documentId); |
|||
projectModel.renameDocument(documentId, textInput.text); |
|||
wrapperItem.model.set(i, projectModel.getDocument(documentId)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
MouseArea { |
|||
id: mouseArea |
|||
z: 1 |
|||
hoverEnabled: false |
|||
anchors.fill: parent |
|||
acceptedButtons: Qt.LeftButton | Qt.RightButton |
|||
onClicked:{ |
|||
if (mouse.button === Qt.RightButton) |
|||
{ |
|||
if (isContract) |
|||
contextMenuContract.popup(); |
|||
else |
|||
contextMenu.popup(); |
|||
} |
|||
else if (mouse.button === Qt.LeftButton) |
|||
{ |
|||
rootItem.isSelected = true; |
|||
projectModel.openDocument(documentId); |
|||
documentSelected(documentId, groupName); |
|||
} |
|||
} |
|||
} |
|||
|
|||
Menu { |
|||
id: contextMenu |
|||
MenuItem { |
|||
text: qsTr("Rename") |
|||
onTriggered: { |
|||
rootItem.renameMode = true; |
|||
} |
|||
} |
|||
MenuItem { |
|||
text: qsTr("Delete") |
|||
onTriggered: { |
|||
deleteConfirmation.open(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
Menu { |
|||
id: contextMenuContract |
|||
MenuItem { |
|||
text: qsTr("Delete") |
|||
onTriggered: { |
|||
deleteConfirmation.open(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
MessageDialog |
|||
{ |
|||
id: deleteConfirmation |
|||
text: qsTr("Are you sure to delete this file ?") |
|||
standardButtons: StandardIcon.Ok | StandardIcon.Cancel |
|||
onAccepted: |
|||
{ |
|||
projectModel.removeDocument(documentId); |
|||
wrapperItem.removeDocument(documentId); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
@ -1,61 +0,0 @@ |
|||
import QtQuick 2.2 |
|||
import QtQuick.Controls 1.1 |
|||
import QtQuick.Layouts 1.0 |
|||
import QtQuick.Controls.Styles 1.1 |
|||
import "." |
|||
|
|||
Rectangle { |
|||
anchors.fill: parent |
|||
RowLayout |
|||
{ |
|||
id: row; |
|||
anchors.fill: parent |
|||
spacing: 2 |
|||
Rectangle |
|||
{ |
|||
id: firstCol; |
|||
color: "#f7f7f7" |
|||
Layout.fillWidth: true |
|||
Layout.minimumWidth: 35 |
|||
Layout.preferredWidth: 35 |
|||
Layout.maximumWidth: 35 |
|||
Layout.minimumHeight: parent.height |
|||
Text { |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
anchors.left: parent.left |
|||
anchors.leftMargin: 3 |
|||
font.family: "monospace" |
|||
font.bold: true |
|||
color: "#4a4a4a" |
|||
text: modelData[0] |
|||
font.pointSize: dbgStyle.general.dataDumpFontSize; |
|||
} |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
Layout.fillWidth: true |
|||
Layout.minimumWidth: 110 |
|||
Layout.preferredWidth: 110 |
|||
Layout.maximumWidth: 110 |
|||
Layout.minimumHeight: parent.height |
|||
Text { |
|||
font.family: "monospace" |
|||
font.bold: true |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
anchors.left: parent.left |
|||
anchors.leftMargin: 4 |
|||
color: "#4a4a4a" |
|||
text: modelData[1] |
|||
font.pointSize: dbgStyle.general.dataDumpFontSize |
|||
} |
|||
} |
|||
} |
|||
|
|||
Rectangle { |
|||
width: parent.width; |
|||
height: 1; |
|||
color: "#cccccc" |
|||
anchors.bottom: parent.bottom |
|||
} |
|||
} |
@ -1,133 +0,0 @@ |
|||
import QtQuick 2.2 |
|||
import QtQuick.Controls 1.1 |
|||
import QtQuick.Controls.Styles 1.1 |
|||
import QtQuick.Dialogs 1.1 |
|||
import QtQuick.Layouts 1.1 |
|||
import Qt.labs.settings 1.0 |
|||
import org.ethereum.qml.QEther 1.0 |
|||
import "js/Debugger.js" as Debugger |
|||
import "js/ErrorLocationFormater.js" as ErrorLocationFormater |
|||
import "js/TransactionHelper.js" as TransactionHelper |
|||
import "js/QEtherHelper.js" as QEtherHelper |
|||
import "." |
|||
|
|||
ColumnLayout { |
|||
id: root |
|||
property alias title: titleLabel.text |
|||
property variant _data |
|||
property string role |
|||
property alias model: modelKeyValue |
|||
|
|||
function add(key, value) |
|||
{ |
|||
modelKeyValue.append({ "key": key, "value": value }) |
|||
} |
|||
|
|||
function clear() |
|||
{ |
|||
modelKeyValue.clear() |
|||
} |
|||
|
|||
function init() |
|||
{ |
|||
modelKeyValue.clear() |
|||
if (typeof(computeData) !== "undefined" && computeData instanceof Function) |
|||
computeData() |
|||
else |
|||
{ |
|||
if (_data !== undefined && _data[role] !== undefined) |
|||
{ |
|||
var keys = Object.keys(_data[role]) |
|||
for (var k in keys) |
|||
{ |
|||
modelKeyValue.append({ "key": keys[k] === "" ? "undefined" : keys[k], "value": _data[role][keys[k]] }) |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
RowLayout |
|||
{ |
|||
Layout.preferredHeight: 20 |
|||
Layout.fillWidth: true |
|||
Label |
|||
{ |
|||
id: titleLabel |
|||
anchors.left: parent.left |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
color: "white" |
|||
} |
|||
} |
|||
|
|||
RowLayout |
|||
{ |
|||
Layout.fillWidth: true |
|||
Layout.preferredHeight: 100 |
|||
ListModel |
|||
{ |
|||
id: modelKeyValue |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
Layout.fillWidth: true |
|||
Layout.fillHeight: true |
|||
color: "white" |
|||
radius: 2 |
|||
ScrollView |
|||
{ |
|||
id: columnValues |
|||
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff |
|||
anchors.fill: parent |
|||
clip: true |
|||
ColumnLayout |
|||
{ |
|||
spacing: 0 |
|||
id: colValue |
|||
anchors.top: parent.top |
|||
anchors.topMargin: 5 |
|||
Repeater |
|||
{ |
|||
id: repeaterKeyValue |
|||
model: modelKeyValue |
|||
Row |
|||
{ |
|||
Layout.preferredHeight: 30 |
|||
spacing: 5 |
|||
anchors.left: colValue.left |
|||
anchors.leftMargin: 5 |
|||
Label |
|||
{ |
|||
maximumLineCount: 1 |
|||
text: { |
|||
if (index >= 0 && repeaterKeyValue.model.get(index).key !== undefined) |
|||
return repeaterKeyValue.model.get(index).key |
|||
else |
|||
return "" |
|||
} |
|||
} |
|||
|
|||
Label |
|||
{ |
|||
text: "=" |
|||
} |
|||
|
|||
Label |
|||
{ |
|||
maximumLineCount: 1 |
|||
text: { |
|||
if (index >= 0 && repeaterKeyValue.model.get(index).value !== undefined) |
|||
return repeaterKeyValue.model.get(index).value |
|||
else |
|||
return "" |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
@ -1,648 +0,0 @@ |
|||
import QtQuick 2.0 |
|||
import QtQuick.Layouts 1.0 |
|||
import QtQuick.Controls 1.1 |
|||
import QtQuick.Controls.Styles 1.3 |
|||
import org.ethereum.qml.SortFilterProxyModel 1.0 |
|||
|
|||
Rectangle |
|||
{ |
|||
property variant statusPane |
|||
property variant currentStatus |
|||
property int contentXPos: logStyle.generic.layout.dateWidth + logStyle.generic.layout.typeWidth - 70 |
|||
|
|||
function clear() |
|||
{ |
|||
logsModel.clear(); |
|||
statusPane.clear(); |
|||
currentStatus = undefined; |
|||
} |
|||
|
|||
function push(_level, _type, _content) |
|||
{ |
|||
_content = _content.replace(/\n/g, " ") |
|||
logsModel.insert(0, { "type": _type, "date": Qt.formatDateTime(new Date(), "hh:mm:ss"), "content": _content, "level": _level }); |
|||
} |
|||
|
|||
onVisibleChanged: |
|||
{ |
|||
if (currentStatus && visible && (logsModel.count === 0 || logsModel.get(0).content !== currentStatus.content || logsModel.get(0).date !== currentStatus.date)) |
|||
logsModel.insert(0, { "type": currentStatus.type, "date": currentStatus.date, "content": currentStatus.content, "level": currentStatus.level }); |
|||
else if (!visible) |
|||
{ |
|||
for (var k = 0; k < logsModel.count; k++) |
|||
{ |
|||
if (logsModel.get(k).type === "Comp") //do not keep compilation logs. |
|||
logsModel.remove(k); |
|||
} |
|||
} |
|||
} |
|||
|
|||
anchors.fill: parent |
|||
radius: 10 |
|||
color: "transparent" |
|||
id: logsPane |
|||
Column { |
|||
z: 2 |
|||
height: parent.height - rowAction.height |
|||
width: parent.width |
|||
spacing: 0 |
|||
ListModel { |
|||
id: logsModel |
|||
} |
|||
|
|||
ScrollView |
|||
{ |
|||
id: scrollView |
|||
height: parent.height |
|||
width: parent.width |
|||
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff |
|||
Column |
|||
{ |
|||
id: logsRect |
|||
spacing: 0 |
|||
Repeater { |
|||
id: logsRepeater |
|||
clip: true |
|||
property string frontColor: "transparent" |
|||
model: SortFilterProxyModel { |
|||
id: proxyModel |
|||
source: logsModel |
|||
property var roles: ["-", "javascript", "run", "state", "comp", "deployment"] |
|||
|
|||
Component.onCompleted: { |
|||
filterType = regEx(proxyModel.roles); |
|||
} |
|||
|
|||
function search(_value) |
|||
{ |
|||
filterContent = _value; |
|||
} |
|||
|
|||
function toogleFilter(_value) |
|||
{ |
|||
var count = roles.length; |
|||
for (var i in roles) |
|||
{ |
|||
if (roles[i] === _value) |
|||
{ |
|||
roles.splice(i, 1); |
|||
break; |
|||
} |
|||
} |
|||
if (count === roles.length) |
|||
roles.push(_value); |
|||
|
|||
filterType = regEx(proxyModel.roles); |
|||
} |
|||
|
|||
function regEx(_value) |
|||
{ |
|||
return "(?:" + roles.join('|') + ")"; |
|||
} |
|||
|
|||
filterType: "(?:javascript|run|state|comp|deployment)" |
|||
filterContent: "" |
|||
filterSyntax: SortFilterProxyModel.RegExp |
|||
filterCaseSensitivity: Qt.CaseInsensitive |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
width: logStyle.generic.layout.dateWidth + logStyle.generic.layout.contentWidth + logStyle.generic.layout.typeWidth |
|||
height: 30 |
|||
color: |
|||
{ |
|||
var cl; |
|||
if (level === "warning" || level === "error") |
|||
cl = logStyle.generic.layout.errorColor; |
|||
else |
|||
cl = index % 2 === 0 ? "transparent" : logStyle.generic.layout.logAlternateColor; |
|||
if (index === 0) |
|||
logsRepeater.frontColor = cl; |
|||
return cl; |
|||
} |
|||
|
|||
Component.onCompleted: |
|||
{ |
|||
logsPane.contentXPos = logContent.x |
|||
} |
|||
|
|||
MouseArea |
|||
{ |
|||
anchors.fill: parent |
|||
onClicked: |
|||
{ |
|||
if (logContent.elide === Text.ElideNone) |
|||
{ |
|||
logContent.elide = Text.ElideRight; |
|||
logContent.wrapMode = Text.NoWrap |
|||
parent.height = 30; |
|||
} |
|||
else |
|||
{ |
|||
logContent.elide = Text.ElideNone; |
|||
logContent.wrapMode = Text.WordWrap; |
|||
parent.height = logContent.lineCount * 30; |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
DefaultLabel { |
|||
id: dateLabel |
|||
text: date; |
|||
font.family: logStyle.generic.layout.logLabelFont |
|||
width: logStyle.generic.layout.dateWidth |
|||
font.pointSize: appStyle.absoluteSize(-1) |
|||
clip: true |
|||
elide: Text.ElideRight |
|||
anchors.left: parent.left |
|||
anchors.leftMargin: 15 |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
color: { |
|||
parent.getColor(level); |
|||
} |
|||
} |
|||
|
|||
DefaultLabel { |
|||
text: type; |
|||
id: typeLabel |
|||
font.family: logStyle.generic.layout.logLabelFont |
|||
width: logStyle.generic.layout.typeWidth |
|||
clip: true |
|||
elide: Text.ElideRight |
|||
font.pointSize: appStyle.absoluteSize(-1) |
|||
anchors.left: dateLabel.right |
|||
anchors.leftMargin: 2 |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
color: { |
|||
parent.getColor(level); |
|||
} |
|||
} |
|||
|
|||
Text { |
|||
id: logContent |
|||
text: content; |
|||
font.family: logStyle.generic.layout.logLabelFont |
|||
width: logStyle.generic.layout.contentWidth |
|||
font.pointSize: appStyle.absoluteSize(-1) |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
elide: Text.ElideRight |
|||
anchors.left: typeLabel.right |
|||
anchors.leftMargin: 2 |
|||
color: { |
|||
parent.getColor(level); |
|||
} |
|||
} |
|||
|
|||
function getColor() |
|||
{ |
|||
if (level === "error") |
|||
return "red"; |
|||
else if (level === "warning") |
|||
return "orange"; |
|||
else |
|||
return "#808080"; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
Component { |
|||
id: itemDelegate |
|||
DefaultLabel { |
|||
text: styleData.value; |
|||
font.family: logStyle.generic.layout.logLabelFont |
|||
font.pointSize: appStyle.absoluteSize(-1) |
|||
color: { |
|||
if (proxyModel.get(styleData.row).level === "error") |
|||
return "red"; |
|||
else if (proxyModel.get(styleData.row).level === "warning") |
|||
return "orange"; |
|||
else |
|||
return "#808080"; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
gradient: Gradient { |
|||
GradientStop { position: 0.0; color: "#f1f1f1" } |
|||
GradientStop { position: 1.0; color: "#d9d7da" } |
|||
} |
|||
Layout.preferredHeight: logStyle.generic.layout.headerHeight |
|||
height: logStyle.generic.layout.headerHeight |
|||
width: logsPane.width |
|||
anchors.bottom: parent.bottom |
|||
Row |
|||
{ |
|||
id: rowAction |
|||
anchors.leftMargin: logStyle.generic.layout.leftMargin |
|||
anchors.left: parent.left |
|||
spacing: logStyle.generic.layout.headerButtonSpacing |
|||
height: parent.height |
|||
Rectangle |
|||
{ |
|||
color: "transparent" |
|||
height: parent.height |
|||
width: 40 |
|||
DefaultLabel |
|||
{ |
|||
width: 40 |
|||
elide: Text.ElideRight |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
color: logStyle.generic.layout.logLabelColor |
|||
font.pointSize: appStyle.absoluteSize(-3) |
|||
font.family: logStyle.generic.layout.logLabelFont |
|||
text: qsTr("Show:") |
|||
} |
|||
} |
|||
|
|||
Rectangle { |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
width: 1; |
|||
height: parent.height |
|||
color: logStyle.generic.layout.buttonSeparatorColor1 |
|||
} |
|||
|
|||
Rectangle { |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
width: 2; |
|||
height: parent.height |
|||
color: logStyle.generic.layout.buttonSeparatorColor2 |
|||
} |
|||
|
|||
ToolButton { |
|||
id: javascriptButton |
|||
checkable: true |
|||
height: logStyle.generic.layout.headerButtonHeight |
|||
width: 30 |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
checked: true |
|||
onCheckedChanged: { |
|||
proxyModel.toogleFilter("javascript") |
|||
} |
|||
tooltip: qsTr("JavaScript") |
|||
style: |
|||
ButtonStyle { |
|||
label: |
|||
Item { |
|||
Rectangle |
|||
{ |
|||
width: labelJs.width |
|||
height: labelJs.height |
|||
anchors.centerIn: parent |
|||
color: "transparent" |
|||
DefaultLabel { |
|||
id: labelJs |
|||
width: 15 |
|||
elide: Text.ElideRight |
|||
anchors.horizontalCenter: parent.horizontalCenter |
|||
font.family: logStyle.generic.layout.logLabelFont |
|||
font.pointSize: appStyle.absoluteSize(-3) |
|||
color: logStyle.generic.layout.logLabelColor |
|||
text: qsTr("JS") |
|||
} |
|||
} |
|||
} |
|||
background: |
|||
Rectangle { |
|||
color: javascriptButton.checked ? logStyle.generic.layout.buttonSelected : "transparent" |
|||
} |
|||
} |
|||
} |
|||
|
|||
Rectangle { |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
width: 1; |
|||
height: parent.height |
|||
color: logStyle.generic.layout.buttonSeparatorColor1 |
|||
} |
|||
|
|||
Rectangle { |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
width: 2; |
|||
height: parent.height |
|||
color: logStyle.generic.layout.buttonSeparatorColor2 |
|||
} |
|||
|
|||
ToolButton { |
|||
id: runButton |
|||
checkable: true |
|||
height: logStyle.generic.layout.headerButtonHeight |
|||
width: 40 |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
checked: true |
|||
onCheckedChanged: { |
|||
proxyModel.toogleFilter("run") |
|||
} |
|||
tooltip: qsTr("Run") |
|||
style: |
|||
ButtonStyle { |
|||
label: |
|||
Item { |
|||
Rectangle |
|||
{ |
|||
width: labelRun.width |
|||
height: labelRun.height |
|||
anchors.centerIn: parent |
|||
color: "transparent" |
|||
DefaultLabel { |
|||
id: labelRun |
|||
width: 25 |
|||
anchors.horizontalCenter: parent.horizontalCenter |
|||
elide: Text.ElideRight |
|||
font.family: logStyle.generic.layout.logLabelFont |
|||
font.pointSize: appStyle.absoluteSize(-3) |
|||
color: logStyle.generic.layout.logLabelColor |
|||
text: qsTr("Run") |
|||
} |
|||
} |
|||
} |
|||
background: |
|||
Rectangle { |
|||
color: runButton.checked ? logStyle.generic.layout.buttonSelected : "transparent" |
|||
} |
|||
} |
|||
} |
|||
|
|||
Rectangle { |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
width: 1; |
|||
height: parent.height |
|||
color: logStyle.generic.layout.buttonSeparatorColor1 |
|||
} |
|||
|
|||
Rectangle { |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
width: 2; |
|||
height: parent.height |
|||
color: logStyle.generic.layout.buttonSeparatorColor2 |
|||
} |
|||
|
|||
ToolButton { |
|||
id: stateButton |
|||
checkable: true |
|||
height: logStyle.generic.layout.headerButtonHeight |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
width: 40 |
|||
checked: true |
|||
onCheckedChanged: { |
|||
proxyModel.toogleFilter("state") |
|||
} |
|||
tooltip: qsTr("State") |
|||
style: |
|||
ButtonStyle { |
|||
label: |
|||
Item { |
|||
Rectangle |
|||
{ |
|||
width: labelState.width |
|||
height: labelState.height |
|||
anchors.centerIn: parent |
|||
color: "transparent" |
|||
DefaultLabel { |
|||
id: labelState |
|||
width: 30 |
|||
anchors.horizontalCenter: parent.horizontalCenter |
|||
elide: Text.ElideRight |
|||
font.family: logStyle.generic.layout.logLabelFont |
|||
font.pointSize: appStyle.absoluteSize(-3) |
|||
color: logStyle.generic.layout.logLabelColor |
|||
text: qsTr("State") |
|||
} |
|||
} |
|||
} |
|||
background: |
|||
Rectangle { |
|||
color: stateButton.checked ? logStyle.generic.layout.buttonSelected : "transparent" |
|||
} |
|||
} |
|||
} |
|||
|
|||
Rectangle { |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
width: 1; |
|||
height: parent.height |
|||
color: logStyle.generic.layout.buttonSeparatorColor1 |
|||
} |
|||
|
|||
Rectangle { |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
width: 2; |
|||
height: parent.height |
|||
color: logStyle.generic.layout.buttonSeparatorColor2 |
|||
} |
|||
|
|||
ToolButton { |
|||
id: deloyButton |
|||
checkable: true |
|||
height: logStyle.generic.layout.headerButtonHeight |
|||
width: 50 |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
checked: true |
|||
onCheckedChanged: { |
|||
proxyModel.toogleFilter("deployment") |
|||
} |
|||
tooltip: qsTr("Deployment") |
|||
style: |
|||
ButtonStyle { |
|||
label: |
|||
Item { |
|||
Rectangle |
|||
{ |
|||
width: labelDeploy.width |
|||
height: labelDeploy.height |
|||
anchors.centerIn: parent |
|||
color: "transparent" |
|||
DefaultLabel { |
|||
width: 40 |
|||
id: labelDeploy |
|||
anchors.horizontalCenter: parent.horizontalCenter |
|||
elide: Text.ElideRight |
|||
font.family: logStyle.generic.layout.logLabelFont |
|||
font.pointSize: appStyle.absoluteSize(-3) |
|||
color: logStyle.generic.layout.logLabelColor |
|||
text: qsTr("Deploy.") |
|||
} |
|||
} |
|||
} |
|||
background: |
|||
Rectangle { |
|||
color: deloyButton.checked ? logStyle.generic.layout.buttonSelected : "transparent" |
|||
} |
|||
} |
|||
} |
|||
|
|||
Rectangle { |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
width: 1; |
|||
height: parent.height |
|||
color: logStyle.generic.layout.buttonSeparatorColor1 |
|||
} |
|||
|
|||
Rectangle { |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
width: 2; |
|||
height: parent.height |
|||
color: logStyle.generic.layout.buttonSeparatorColor2 |
|||
} |
|||
} |
|||
|
|||
Row |
|||
{ |
|||
height: parent.height |
|||
anchors.right: parent.right |
|||
anchors.rightMargin: 10 |
|||
spacing: 10 |
|||
Rectangle |
|||
{ |
|||
height: logStyle.generic.layout.headerButtonHeight |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
color: "transparent" |
|||
width: 20 |
|||
Button |
|||
{ |
|||
id: clearButton |
|||
action: clearAction |
|||
anchors.fill: parent |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
height: 25 |
|||
style: |
|||
ButtonStyle { |
|||
background: |
|||
Rectangle { |
|||
height: logStyle.generic.layout.headerButtonHeight |
|||
implicitHeight: logStyle.generic.layout.headerButtonHeight |
|||
color: "transparent" |
|||
} |
|||
} |
|||
} |
|||
|
|||
Image { |
|||
id: clearImage |
|||
source: clearAction.enabled ? "qrc:/qml/img/cleariconactive.png" : "qrc:/qml/img/clearicon.png" |
|||
anchors.centerIn: parent |
|||
fillMode: Image.PreserveAspectFit |
|||
width: 20 |
|||
height: 25 |
|||
} |
|||
|
|||
Action { |
|||
id: clearAction |
|||
enabled: logsModel.count > 0 |
|||
tooltip: qsTr("Clear") |
|||
onTriggered: { |
|||
logsPane.clear() |
|||
} |
|||
} |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
height: logStyle.generic.layout.headerButtonHeight |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
color: "transparent" |
|||
width: 20 |
|||
Button |
|||
{ |
|||
id: copyButton |
|||
action: copyAction |
|||
anchors.fill: parent |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
height: 25 |
|||
style: |
|||
ButtonStyle { |
|||
background: |
|||
Rectangle { |
|||
height: logStyle.generic.layout.headerButtonHeight |
|||
implicitHeight: logStyle.generic.layout.headerButtonHeight |
|||
color: "transparent" |
|||
} |
|||
} |
|||
} |
|||
|
|||
Image { |
|||
id: copyImage |
|||
source: copyAction.enabled ? "qrc:/qml/img/copyiconactive.png" : "qrc:/qml/img/copyicon.png" |
|||
anchors.centerIn: parent |
|||
fillMode: Image.PreserveAspectFit |
|||
width: 20 |
|||
height: 25 |
|||
} |
|||
|
|||
Action { |
|||
id: copyAction |
|||
enabled: logsModel.count > 0 |
|||
tooltip: qsTr("Copy to Clipboard") |
|||
onTriggered: { |
|||
var content = ""; |
|||
for (var k = 0; k < logsModel.count; k++) |
|||
{ |
|||
var log = logsModel.get(k); |
|||
content += log.type + "\t" + log.level + "\t" + log.date + "\t" + log.content + "\n"; |
|||
} |
|||
clipboard.text = content; |
|||
} |
|||
} |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
width: 120 |
|||
radius: 10 |
|||
height: 25 |
|||
color: "white" |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
|
|||
Image |
|||
{ |
|||
id: searchImg |
|||
source: "qrc:/qml/img/searchicon.png" |
|||
fillMode: Image.PreserveAspectFit |
|||
width: 20 |
|||
height: 25 |
|||
z: 3 |
|||
} |
|||
|
|||
DefaultTextField |
|||
{ |
|||
id: searchBox |
|||
z: 2 |
|||
width: 100 |
|||
anchors.left: searchImg.right |
|||
anchors.leftMargin: -7 |
|||
font.family: logStyle.generic.layout.logLabelFont |
|||
font.pointSize: appStyle.absoluteSize(-3) |
|||
font.italic: true |
|||
text: qsTr(" - Search - ") |
|||
onFocusChanged: |
|||
{ |
|||
if (!focus && text === "") |
|||
text = qsTr(" - Search - "); |
|||
else if (focus && text === qsTr(" - Search - ")) |
|||
text = ""; |
|||
} |
|||
|
|||
onTextChanged: { |
|||
if (text === qsTr(" - Search - ")) |
|||
proxyModel.search(""); |
|||
else |
|||
proxyModel.search(text); |
|||
} |
|||
|
|||
style: |
|||
TextFieldStyle { |
|||
background: Rectangle { |
|||
radius: 10 |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
@ -1,30 +0,0 @@ |
|||
import QtQuick 2.0 |
|||
|
|||
QtObject { |
|||
|
|||
function absoluteSize(rel) |
|||
{ |
|||
return systemPointSize + rel; |
|||
} |
|||
|
|||
property QtObject generic: QtObject { |
|||
property QtObject layout: QtObject { |
|||
property string backgroundColor: "#f7f7f7" |
|||
property int headerHeight: 30 |
|||
property int headerButtonSpacing: 0 |
|||
property int leftMargin: 10 |
|||
property int headerButtonHeight: 30 |
|||
property string logLabelColor: "#4a4a4a" |
|||
property string logLabelFont: "sans serif" |
|||
property int headerInputWidth: 200 |
|||
property int dateWidth: 100 |
|||
property int typeWidth: 100 |
|||
property int contentWidth: 560 |
|||
property string logAlternateColor: "#f6f5f6" |
|||
property string errorColor: "#fffcd5" |
|||
property string buttonSeparatorColor1: "#d3d0d0" |
|||
property string buttonSeparatorColor2: "#f2f1f2" |
|||
property string buttonSelected: "#dcdcdc" |
|||
} |
|||
} |
|||
} |
@ -1,298 +0,0 @@ |
|||
import QtQuick 2.2 |
|||
import QtQuick.Controls 1.2 |
|||
import QtQuick.Controls.Private 1.0 as ControlsPrivate |
|||
import QtQuick.Dialogs 1.2 |
|||
import QtQuick.Dialogs.Private 1.1 |
|||
import QtQuick.Layouts 1.1 |
|||
import QtQuick.Window 2.1 |
|||
import Qt.labs.folderlistmodel 2.1 |
|||
import Qt.labs.settings 1.0 |
|||
|
|||
AbstractDialog { |
|||
|
|||
id: root |
|||
|
|||
property string folder: view.model.folder |
|||
property var nameFilters: [] |
|||
property bool selectFolder: false |
|||
property bool selectExisting: true |
|||
property int selectedNameFilterIndex: -1 |
|||
property var selectedNameFilterExtensions: [] |
|||
property string selection: "" |
|||
property alias fileUrl: root.selection |
|||
|
|||
|
|||
function selectNameFilter(text) { |
|||
} |
|||
|
|||
function clearSelection(text) { |
|||
selection = ""; |
|||
} |
|||
|
|||
function addSelection(text) { |
|||
selection = text; |
|||
} |
|||
|
|||
onVisibleChanged: { |
|||
if (visible) { |
|||
view.needsWidthAdjustment = true |
|||
view.selection.clear() |
|||
view.focus = true |
|||
} |
|||
} |
|||
|
|||
Component.onCompleted: { |
|||
folder = fileIo.pathToUrl(fileIo.homePath); |
|||
view.model.nameFilters = root.selectedNameFilterExtensions |
|||
filterField.currentIndex = root.selectedNameFilterIndex |
|||
root.favoriteFolders = settings.favoriteFolders |
|||
} |
|||
|
|||
Component.onDestruction: { |
|||
settings.favoriteFolders = root.favoriteFolders |
|||
} |
|||
|
|||
property Settings settings: Settings { |
|||
category: "QQControlsFileDialog" |
|||
property alias width: root.width |
|||
property alias height: root.height |
|||
property variant favoriteFolders: [] |
|||
} |
|||
|
|||
property bool showFocusHighlight: false |
|||
property SystemPalette palette: SystemPalette { } |
|||
property var favoriteFolders: [] |
|||
|
|||
function dirDown(path) { |
|||
view.selection.clear() |
|||
root.folder = "file://" + path |
|||
} |
|||
function dirUp() { |
|||
view.selection.clear() |
|||
if (view.model.parentFolder != "") |
|||
root.folder = view.model.parentFolder |
|||
} |
|||
function acceptSelection() { |
|||
// transfer the view's selections to QQuickFileDialog |
|||
clearSelection() |
|||
if (selectFolder && view.selection.count === 0) |
|||
addSelection(folder) |
|||
else { |
|||
view.selection.forEach(function(idx) { |
|||
if (view.model.isFolder(idx)) { |
|||
if (selectFolder) |
|||
addSelection(view.model.get(idx, "fileURL")) |
|||
} else { |
|||
if (!selectFolder) |
|||
addSelection(view.model.get(idx, "fileURL")) |
|||
} |
|||
}) |
|||
} |
|||
accept() |
|||
} |
|||
|
|||
property Action dirUpAction: Action { |
|||
text: "\ue810" |
|||
shortcut: "Ctrl+U" |
|||
onTriggered: dirUp() |
|||
tooltip: qsTr("Go up to the folder containing this one") |
|||
} |
|||
|
|||
Rectangle { |
|||
id: window |
|||
implicitWidth: Math.min(root.__maximumDimension, Math.max(Screen.pixelDensity * 100, view.implicitWidth)) |
|||
implicitHeight: Math.min(root.__maximumDimension, Screen.pixelDensity * 80) |
|||
color: root.palette.window |
|||
|
|||
Binding { |
|||
target: view.model |
|||
property: "folder" |
|||
value: root.folder |
|||
} |
|||
Binding { |
|||
target: currentPathField |
|||
property: "text" |
|||
value: fileIo.urlToPath(root.folder) |
|||
} |
|||
Keys.onPressed: { |
|||
event.accepted = true |
|||
switch (event.key) { |
|||
case Qt.Key_Back: |
|||
case Qt.Key_Escape: |
|||
reject() |
|||
break |
|||
default: |
|||
event.accepted = false |
|||
break |
|||
} |
|||
} |
|||
Keys.forwardTo: [view.flickableItem] |
|||
|
|||
|
|||
TableView { |
|||
id: view |
|||
sortIndicatorVisible: true |
|||
width: parent.width |
|||
anchors.top: titleBar.bottom |
|||
anchors.bottom: bottomBar.top |
|||
|
|||
property bool needsWidthAdjustment: true |
|||
selectionMode: root.selectMultiple ? |
|||
(ControlsPrivate.Settings.hasTouchScreen ? SelectionMode.MultiSelection : SelectionMode.ExtendedSelection) : |
|||
SelectionMode.SingleSelection |
|||
onRowCountChanged: if (needsWidthAdjustment && rowCount > 0) { |
|||
resizeColumnsToContents() |
|||
needsWidthAdjustment = false |
|||
} |
|||
model: FolderListModel { |
|||
showFiles: !root.selectFolder |
|||
nameFilters: root.selectedNameFilterExtensions |
|||
sortField: (view.sortIndicatorColumn === 0 ? FolderListModel.Name : |
|||
(view.sortIndicatorColumn === 1 ? FolderListModel.Type : |
|||
(view.sortIndicatorColumn === 2 ? FolderListModel.Size : FolderListModel.LastModified))) |
|||
sortReversed: view.sortIndicatorOrder === Qt.DescendingOrder |
|||
} |
|||
|
|||
onActivated: if (view.focus) { |
|||
if (view.selection.count > 0 && view.model.isFolder(row)) { |
|||
dirDown(view.model.get(row, "filePath")) |
|||
} else { |
|||
root.acceptSelection() |
|||
} |
|||
} |
|||
onClicked: currentPathField.text = view.model.get(row, "filePath") |
|||
|
|||
|
|||
TableViewColumn { |
|||
id: fileNameColumn |
|||
role: "fileName" |
|||
title: qsTr("Filename") |
|||
delegate: Item { |
|||
implicitWidth: pathText.implicitWidth + pathText.anchors.leftMargin + pathText.anchors.rightMargin |
|||
|
|||
Text { |
|||
id: fileIcon |
|||
width: height |
|||
verticalAlignment: Text.AlignVCenter |
|||
font.family: iconFont.name |
|||
property alias unicode: fileIcon.text |
|||
FontLoader { id: iconFont; source: "qrc:/QtQuick/Dialogs/qml/icons.ttf"; onNameChanged: console.log("custom font" + name) } |
|||
x: 4 |
|||
height: parent.height - 2 |
|||
unicode: view.model.isFolder(styleData.row) ? "\ue804" : "\ue802" |
|||
} |
|||
Text { |
|||
id: pathText |
|||
text: styleData.value |
|||
anchors { |
|||
left: parent.left |
|||
right: parent.right |
|||
leftMargin: 36 + 6 |
|||
rightMargin: 4 |
|||
verticalCenter: parent.verticalCenter |
|||
} |
|||
color: styleData.textColor |
|||
elide: Text.ElideRight |
|||
renderType: ControlsPrivate.Settings.isMobile ? Text.QtRendering : Text.NativeRendering |
|||
} |
|||
} |
|||
} |
|||
TableViewColumn { |
|||
role: "fileSuffix" |
|||
title: qsTr("Type", "file type (extension)") |
|||
// TODO should not need to create a whole new component just to customize the text value |
|||
// something like textFormat: function(text) { return view.model.get(styleData.row, "fileIsDir") ? "folder" : text } |
|||
delegate: Item { |
|||
implicitWidth: sizeText.implicitWidth + sizeText.anchors.leftMargin + sizeText.anchors.rightMargin |
|||
Text { |
|||
id: sizeText |
|||
text: view.model.get(styleData.row, "fileIsDir") ? "folder" : styleData.value |
|||
anchors { |
|||
left: parent.left |
|||
right: parent.right |
|||
leftMargin: 4 |
|||
rightMargin: 4 |
|||
verticalCenter: parent.verticalCenter |
|||
} |
|||
color: styleData.textColor |
|||
elide: Text.ElideRight |
|||
renderType: ControlsPrivate.Settings.isMobile ? Text.QtRendering : Text.NativeRendering |
|||
} |
|||
} |
|||
} |
|||
TableViewColumn { |
|||
role: "fileSize" |
|||
title: qsTr("Size", "file size") |
|||
horizontalAlignment: Text.AlignRight |
|||
} |
|||
TableViewColumn { id: modifiedColumn; role: "fileModified" ; title: qsTr("Modified", "last-modified time") } |
|||
TableViewColumn { id: accessedColumn; role: "fileAccessed" ; title: qsTr("Accessed", "last-accessed time") } |
|||
} |
|||
|
|||
ToolBar { |
|||
id: titleBar |
|||
RowLayout { |
|||
anchors.fill: parent |
|||
ToolButton { |
|||
action: dirUpAction |
|||
//style: IconButtonStyle { } |
|||
Layout.maximumWidth: height * 1.5 |
|||
} |
|||
TextField { |
|||
id: currentPathField |
|||
Layout.fillWidth: true |
|||
function doAccept() { |
|||
root.clearSelection() |
|||
if (root.addSelection(fileIo.pathToUrl(text))) |
|||
root.accept() |
|||
else |
|||
root.folder = fileIo.pathFolder(text) |
|||
} |
|||
onAccepted: doAccept() |
|||
} |
|||
} |
|||
} |
|||
Item { |
|||
id: bottomBar |
|||
width: parent.width |
|||
height: buttonRow.height + buttonRow.spacing * 2 |
|||
anchors.bottom: parent.bottom |
|||
|
|||
Row { |
|||
id: buttonRow |
|||
anchors.right: parent.right |
|||
anchors.rightMargin: spacing |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
spacing: 4 |
|||
ComboBox { |
|||
id: filterField |
|||
model: root.nameFilters |
|||
visible: !selectFolder |
|||
width: bottomBar.width - cancelButton.width - okButton.width - parent.spacing * 6 |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
onCurrentTextChanged: { |
|||
root.selectNameFilter(currentText) |
|||
view.model.nameFilters = root.selectedNameFilterExtensions |
|||
} |
|||
} |
|||
Button { |
|||
id: cancelButton |
|||
text: qsTr("Cancel") |
|||
onClicked: root.reject() |
|||
} |
|||
Button { |
|||
id: okButton |
|||
text: root.selectFolder ? qsTr("Choose") : (selectExisting ? qsTr("Open") : qsTr("Save")) |
|||
onClicked: { |
|||
if (view.model.isFolder(view.currentIndex) && !selectFolder) |
|||
dirDown(view.model.get(view.currentIndex, "filePath")) |
|||
else if (!(root.selectExisting)) |
|||
currentPathField.doAccept() |
|||
else |
|||
root.acceptSelection() |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
@ -1,246 +0,0 @@ |
|||
import QtQuick 2.2 |
|||
import QtQuick.Controls 1.1 |
|||
import QtQuick.Layouts 1.0 |
|||
import QtQuick.Controls.Styles 1.1 |
|||
import Qt.labs.settings 1.0 |
|||
import org.ethereum.qml.QEther 1.0 |
|||
import "js/QEtherHelper.js" as QEtherHelper |
|||
import "js/TransactionHelper.js" as TransactionHelper |
|||
import "." |
|||
|
|||
Rectangle { |
|||
objectName: "mainContent" |
|||
signal keyPressed(variant event) |
|||
focus: true |
|||
Keys.enabled: true |
|||
Keys.onPressed: |
|||
{ |
|||
root.keyPressed(event.key); |
|||
} |
|||
anchors.fill: parent |
|||
id: root |
|||
|
|||
property alias rightViewVisible: scenarioExe.visible |
|||
property alias webViewVisible: webPreview.visible |
|||
property alias webView: webPreview |
|||
property alias projectViewVisible: projectList.visible |
|||
property alias projectNavigator: projectList |
|||
property alias runOnProjectLoad: mainSettings.runOnProjectLoad |
|||
property alias rightPane: scenarioExe |
|||
property alias debuggerPanel: debugPanel |
|||
property alias codeEditor: codeEditor |
|||
property bool webViewHorizontal: codeWebSplitter.orientation === Qt.Vertical //vertical splitter positions elements vertically, splits screen horizontally |
|||
property bool firstCompile: true |
|||
property int scenarioMinWidth: 620 |
|||
|
|||
Connections { |
|||
target: codeModel |
|||
onCompilationComplete: { |
|||
if (firstCompile) { |
|||
firstCompile = false; |
|||
if (runOnProjectLoad) |
|||
startQuickDebugging(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
Connections { |
|||
target: debugPanel |
|||
onDebugExecuteLocation: { |
|||
codeEditor.highlightExecution(documentId, location); |
|||
} |
|||
} |
|||
|
|||
Connections { |
|||
target: codeEditor |
|||
onBreakpointsChanged: { |
|||
debugPanel.setBreakpoints(codeEditor.getBreakpoints()); |
|||
} |
|||
} |
|||
|
|||
function startQuickDebugging() |
|||
{ |
|||
ensureRightView(); |
|||
projectModel.stateListModel.debugDefaultState(); |
|||
} |
|||
|
|||
function toggleRightView() { |
|||
scenarioExe.visible = !scenarioExe.visible; |
|||
} |
|||
|
|||
function ensureRightView() { |
|||
scenarioExe.visible = true; |
|||
} |
|||
|
|||
function rightViewIsVisible() |
|||
{ |
|||
return scenarioExe.visible; |
|||
} |
|||
|
|||
function hideRightView() { |
|||
scenarioExe.visible = lfalse; |
|||
} |
|||
|
|||
function toggleWebPreview() { |
|||
webPreview.visible = !webPreview.visible; |
|||
} |
|||
|
|||
function toggleProjectView() { |
|||
projectList.visible = !projectList.visible; |
|||
} |
|||
|
|||
function toggleWebPreviewOrientation() { |
|||
codeWebSplitter.orientation = (codeWebSplitter.orientation === Qt.Vertical ? Qt.Horizontal : Qt.Vertical); |
|||
} |
|||
|
|||
//TODO: move this to debugger.js after refactoring, introduce events |
|||
function toggleBreakpoint() { |
|||
codeEditor.toggleBreakpoint(); |
|||
} |
|||
|
|||
function displayCompilationErrorIfAny() |
|||
{ |
|||
scenarioExe.visible = true; |
|||
scenarioExe.displayCompilationErrorIfAny(); |
|||
} |
|||
|
|||
Settings { |
|||
id: mainSettings |
|||
property alias codeWebOrientation: codeWebSplitter.orientation |
|||
property alias webWidth: webPreview.width |
|||
property alias webHeight: webPreview.height |
|||
property alias showProjectView: projectList.visible |
|||
property bool runOnProjectLoad: true |
|||
property int scenarioMinWidth: scenarioMinWidth |
|||
} |
|||
|
|||
ColumnLayout |
|||
{ |
|||
id: mainColumn |
|||
anchors.fill: parent |
|||
spacing: 0 |
|||
Rectangle { |
|||
width: parent.width |
|||
height: 50 |
|||
Layout.row: 0 |
|||
Layout.fillWidth: true |
|||
Layout.preferredHeight: 50 |
|||
id: headerView |
|||
Rectangle |
|||
{ |
|||
gradient: Gradient { |
|||
GradientStop { position: 0.0; color: "#f1f1f1" } |
|||
GradientStop { position: 1.0; color: "#d9d7da" } |
|||
} |
|||
id: headerPaneContainer |
|||
anchors.fill: parent |
|||
StatusPane |
|||
{ |
|||
anchors.fill: parent |
|||
webPreview: webPreview |
|||
} |
|||
} |
|||
} |
|||
|
|||
Rectangle { |
|||
Layout.fillWidth: true |
|||
height: 1 |
|||
color: "#8c8c8c" |
|||
} |
|||
|
|||
Rectangle { |
|||
Layout.fillWidth: true |
|||
Layout.preferredHeight: root.height - headerView.height; |
|||
|
|||
Settings { |
|||
id: splitSettings |
|||
property alias projectWidth: projectList.width |
|||
property alias contentViewWidth: contentView.width |
|||
property alias rightViewWidth: scenarioExe.width |
|||
} |
|||
|
|||
Splitter |
|||
{ |
|||
anchors.fill: parent |
|||
orientation: Qt.Horizontal |
|||
|
|||
ProjectList { |
|||
id: projectList |
|||
width: 350 |
|||
Layout.minimumWidth: 250 |
|||
Layout.fillHeight: true |
|||
Connections { |
|||
target: projectModel.codeEditor |
|||
} |
|||
} |
|||
|
|||
Rectangle { |
|||
id: contentView |
|||
Layout.fillHeight: true |
|||
Layout.fillWidth: true |
|||
|
|||
Splitter { |
|||
id: codeWebSplitter |
|||
anchors.fill: parent |
|||
orientation: Qt.Vertical |
|||
CodeEditorView { |
|||
id: codeEditor |
|||
height: parent.height * 0.6 |
|||
anchors.top: parent.top |
|||
Layout.fillWidth: true |
|||
Layout.fillHeight: true |
|||
} |
|||
WebPreview { |
|||
id: webPreview |
|||
height: parent.height * 0.4 |
|||
Layout.fillWidth: codeWebSplitter.orientation === Qt.Vertical |
|||
Layout.fillHeight: codeWebSplitter.orientation === Qt.Horizontal |
|||
Layout.minimumHeight: 200 |
|||
Layout.minimumWidth: 200 |
|||
} |
|||
} |
|||
} |
|||
|
|||
ScenarioExecution |
|||
{ |
|||
id: scenarioExe; |
|||
visible: false; |
|||
Layout.fillHeight: true |
|||
Keys.onEscapePressed: visible = false |
|||
Layout.minimumWidth: scenarioMinWidth |
|||
anchors.right: parent.right |
|||
} |
|||
|
|||
Debugger |
|||
{ |
|||
id: debugPanel |
|||
visible: false |
|||
Layout.fillHeight: true |
|||
Keys.onEscapePressed: visible = false |
|||
Layout.minimumWidth: scenarioMinWidth |
|||
anchors.right: parent.right |
|||
} |
|||
|
|||
Connections { |
|||
target: clientModel |
|||
onDebugDataReady: { |
|||
scenarioExe.visible = false |
|||
debugPanel.visible = true |
|||
debugPanel.width = scenarioExe.width |
|||
if (scenarioExe.bc.debugTrRequested) |
|||
debugPanel.setTr(scenarioExe.bc.model.blocks[scenarioExe.bc.debugTrRequested[0]].transactions[scenarioExe.bc.debugTrRequested[1]]) |
|||
} |
|||
} |
|||
|
|||
Connections { |
|||
target: debugPanel |
|||
onPanelClosed: { |
|||
debugPanel.visible = false |
|||
scenarioExe.visible = true |
|||
scenarioExe.width = debugPanel.width |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
@ -1,30 +0,0 @@ |
|||
import QtQuick 2.2 |
|||
import QtQuick.Window 2.0 |
|||
|
|||
Window |
|||
{ |
|||
id: modalDialog |
|||
title: "" |
|||
modality: Qt.WindowModal |
|||
height: 400 |
|||
width: 700 |
|||
visible: false |
|||
Loader |
|||
{ |
|||
focus: true |
|||
id: modalDialogContent |
|||
objectName: "modalDialogContent" |
|||
anchors.fill: parent |
|||
} |
|||
function open() |
|||
{ |
|||
visible = true; |
|||
} |
|||
function close() |
|||
{ |
|||
visible = false; |
|||
modalDialogContent.source = ""; |
|||
modalDialogContent.sourceComponent = undefined; |
|||
modalDialog.destroy(); |
|||
} |
|||
} |
@ -1,123 +0,0 @@ |
|||
import QtQuick 2.2 |
|||
import QtQuick.Controls 1.1 |
|||
import QtQuick.Layouts 1.1 |
|||
import QtQuick.Dialogs 1.2 |
|||
import QtQuick.Window 2.0 |
|||
import QtQuick.Dialogs 1.1 |
|||
|
|||
Item |
|||
{ |
|||
property alias projectTitle: titleField.text |
|||
readonly property string projectPath: "file://" + pathField.text |
|||
property alias pathFieldText: pathField.text |
|||
signal accepted |
|||
function open() { |
|||
newProjectWin.visible = true; |
|||
titleField.focus = true; |
|||
} |
|||
|
|||
function close() { |
|||
newProjectWin.visible = false; |
|||
} |
|||
|
|||
function acceptAndClose() { |
|||
close(); |
|||
accepted(); |
|||
} |
|||
|
|||
Dialog { |
|||
id: newProjectWin |
|||
modality: Qt.ApplicationModal |
|||
title: qsTr("New Project"); |
|||
|
|||
width: 640 |
|||
height: 120 |
|||
|
|||
visible: false |
|||
|
|||
contentItem: Rectangle { |
|||
anchors.fill: parent |
|||
GridLayout |
|||
{ |
|||
id: dialogContent |
|||
columns: 2 |
|||
anchors.fill: parent |
|||
anchors.margins: 10 |
|||
rowSpacing: 10 |
|||
columnSpacing: 10 |
|||
|
|||
Label { |
|||
text: qsTr("Title") |
|||
} |
|||
TextField { |
|||
id: titleField |
|||
focus: true |
|||
Layout.fillWidth: true |
|||
Keys.onReturnPressed: { |
|||
if (okButton.enabled) |
|||
acceptAndClose(); |
|||
} |
|||
} |
|||
|
|||
Label { |
|||
text: qsTr("Path") |
|||
} |
|||
RowLayout { |
|||
TextField { |
|||
id: pathField |
|||
Layout.fillWidth: true |
|||
Keys.onReturnPressed: { |
|||
if (okButton.enabled) |
|||
acceptAndClose(); |
|||
} |
|||
} |
|||
Button { |
|||
text: qsTr("Browse") |
|||
onClicked: |
|||
{ |
|||
newProjectWin.close() |
|||
createProjectFileDialog.open() |
|||
} |
|||
} |
|||
} |
|||
|
|||
RowLayout |
|||
{ |
|||
anchors.bottom: parent.bottom |
|||
anchors.right: parent.right; |
|||
|
|||
Button { |
|||
id: okButton; |
|||
enabled: titleField.text != "" && pathField.text != "" |
|||
text: qsTr("OK"); |
|||
onClicked: { |
|||
acceptAndClose(); |
|||
} |
|||
} |
|||
Button { |
|||
text: qsTr("Cancel"); |
|||
onClicked: close(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Component.onCompleted: pathField.text = fileIo.homePath |
|||
|
|||
} |
|||
|
|||
QFileDialog { |
|||
id: createProjectFileDialog |
|||
visible: false |
|||
title: qsTr("Please choose a path for the project") |
|||
selectFolder: true |
|||
onAccepted: { |
|||
var u = createProjectFileDialog.fileUrl.toString(); |
|||
if (u.indexOf("file://") == 0) |
|||
u = u.substring(7, u.length) |
|||
if (Qt.platform.os == "windows" && u.indexOf("/") == 0) |
|||
u = u.substring(1, u.length); |
|||
pathField.text = u; |
|||
newProjectWin.open() |
|||
} |
|||
} |
|||
} |
@ -1,212 +0,0 @@ |
|||
import QtQuick 2.0 |
|||
import QtQuick.Layouts 1.0 |
|||
import QtQuick.Controls 1.1 |
|||
import QtQuick.Controls.Styles 1.3 |
|||
import QtQuick.Dialogs 1.1 |
|||
import Qt.labs.settings 1.0 |
|||
import "js/TransactionHelper.js" as TransactionHelper |
|||
import "js/NetworkDeployment.js" as NetworkDeploymentCode |
|||
import "js/QEtherHelper.js" as QEtherHelper |
|||
|
|||
Rectangle { |
|||
property variant paramsModel: [] |
|||
property variant worker |
|||
color: "#E3E3E3E3" |
|||
anchors.fill: parent |
|||
id: root |
|||
property string packageHash |
|||
property string packageBase64 |
|||
property alias localPackageUrl: localPackageUrl.text |
|||
property alias lastDeployDate: lastDeployLabel.text |
|||
property string deploymentId |
|||
property string packageDir |
|||
signal packaged |
|||
|
|||
function show() |
|||
{ |
|||
visible = true |
|||
} |
|||
|
|||
QFileDialog { |
|||
id: ressourcesFolder |
|||
visible: false |
|||
title: qsTr("Please choose a path") |
|||
selectFolder: true |
|||
selectExisting: true |
|||
property variant target |
|||
onAccepted: { |
|||
var u = ressourcesFolder.fileUrl.toString(); |
|||
if (u.indexOf("file://") == 0) |
|||
u = u.substring(7, u.length) |
|||
if (Qt.platform.os == "windows" && u.indexOf("/") == 0) |
|||
u = u.substring(1, u.length); |
|||
target.text = u; |
|||
} |
|||
} |
|||
|
|||
ColumnLayout |
|||
{ |
|||
anchors.top: parent.top |
|||
anchors.topMargin: 10 |
|||
width: parent.width |
|||
|
|||
id: col |
|||
spacing: 20 |
|||
anchors.left: parent.left |
|||
anchors.leftMargin: 10 |
|||
|
|||
Label |
|||
{ |
|||
anchors.top: parent.top |
|||
Layout.fillWidth: true |
|||
|
|||
text: qsTr("Upload and update your Dapp assets") |
|||
} |
|||
|
|||
RowLayout |
|||
{ |
|||
Layout.fillWidth: true |
|||
Layout.preferredHeight: 20 |
|||
Rectangle |
|||
{ |
|||
Layout.preferredWidth: col.width / 5 |
|||
Label |
|||
{ |
|||
text: qsTr("Save Package to") |
|||
anchors.left: parent.left |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
} |
|||
} |
|||
|
|||
DefaultTextField |
|||
{ |
|||
id: packageFolder |
|||
visible: true |
|||
Layout.preferredWidth: 360 |
|||
text: projectPath + "package/" |
|||
} |
|||
|
|||
Button |
|||
{ |
|||
text: qsTr("select") |
|||
onClicked: { |
|||
ressourcesFolder.target = packageFolder |
|||
ressourcesFolder.open() |
|||
} |
|||
} |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
Layout.fillWidth: true |
|||
Layout.preferredHeight: 40 |
|||
color: "transparent" |
|||
Button |
|||
{ |
|||
id: generatePackageBtn |
|||
anchors.left: parent.left |
|||
anchors.leftMargin: 10 |
|||
text: qsTr("Generate Package") |
|||
onClicked: |
|||
{ |
|||
NetworkDeploymentCode.packageDapp(projectModel.deploymentAddresses); |
|||
projectModel.saveProject() |
|||
root.packaged() |
|||
} |
|||
} |
|||
|
|||
RowLayout |
|||
{ |
|||
anchors.top: generatePackageBtn.bottom |
|||
anchors.topMargin: 10 |
|||
visible: root.lastDeployDate !== "" |
|||
Label |
|||
{ |
|||
id: lastPackage |
|||
text: qsTr("Last Package") |
|||
} |
|||
|
|||
Label |
|||
{ |
|||
id: lastDeployLabel |
|||
} |
|||
} |
|||
} |
|||
|
|||
RowLayout |
|||
{ |
|||
Layout.fillWidth: true |
|||
Layout.preferredHeight: 20 |
|||
Rectangle |
|||
{ |
|||
Layout.preferredWidth: col.width / 5 |
|||
Label |
|||
{ |
|||
text: qsTr("Local package URL") |
|||
anchors.left: parent.left |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
} |
|||
} |
|||
|
|||
DefaultTextField |
|||
{ |
|||
id: localPackageUrl |
|||
Layout.preferredWidth: 450 |
|||
readOnly: true |
|||
} |
|||
} |
|||
|
|||
Label |
|||
{ |
|||
Layout.preferredWidth: 300 |
|||
text: qsTr("You have to upload the package to a remote folder, or use a service like pastebin") |
|||
wrapMode: Text.WordWrap |
|||
clip: true |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
color: "transparent" |
|||
Layout.fillWidth: true |
|||
Layout.preferredHeight: 20 |
|||
Button |
|||
{ |
|||
Layout.preferredWidth: 200 |
|||
text: qsTr("Copy Base64") |
|||
anchors.left: parent.left |
|||
anchors.leftMargin: 10 |
|||
onClicked: |
|||
{ |
|||
clipboard.text = deploymentDialog.packageStep.packageBase64; |
|||
} |
|||
} |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
color: "transparent" |
|||
Layout.fillWidth: true |
|||
Layout.preferredHeight: 20 |
|||
Button |
|||
{ |
|||
Layout.preferredWidth: 200 |
|||
text: qsTr("Open pastebin") |
|||
anchors.left: parent.left |
|||
anchors.leftMargin: 10 |
|||
onClicked: |
|||
{ |
|||
Qt.openUrlExternally("http://pastebin.com/"); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
@ -1,32 +0,0 @@ |
|||
import QtQuick 2.0 |
|||
|
|||
QtObject { |
|||
|
|||
function absoluteSize(rel) |
|||
{ |
|||
return systemPointSize + rel; |
|||
} |
|||
|
|||
property QtObject general: QtObject { |
|||
property int leftMargin: 45 |
|||
} |
|||
|
|||
property QtObject title: QtObject { |
|||
property string color: "#808080" |
|||
property string background: "#f0f0f0" |
|||
property int height: 55 |
|||
property int fontSize: absoluteSize(7);// 18 |
|||
} |
|||
|
|||
property QtObject documentsList: QtObject { |
|||
property string background: "#f7f7f7" |
|||
property string color: "#4d4d4d" |
|||
property string sectionColor: "#808080" |
|||
property string selectedColor: "white" |
|||
property string highlightColor: "#4a90e2" |
|||
property int height: 35 |
|||
property int fileNameHeight: 30 |
|||
property int fontSize: absoluteSize(2)// 13 |
|||
property int sectionFontSize: absoluteSize(2)// 13 |
|||
} |
|||
} |
@ -1,225 +0,0 @@ |
|||
import QtQuick 2.0 |
|||
import QtQuick.Window 2.0 |
|||
import QtQuick.Layouts 1.0 |
|||
import QtQuick.Controls 1.0 |
|||
import QtQuick.Controls.Styles 1.3 |
|||
import Qt.labs.settings 1.0 |
|||
import "." |
|||
|
|||
Item { |
|||
property bool renameMode: false; |
|||
property alias sections: sectionRepeater |
|||
|
|||
ProjectFilesStyle { |
|||
id: projectFilesStyle |
|||
} |
|||
|
|||
ColumnLayout { |
|||
anchors.fill: parent |
|||
id: filesCol |
|||
spacing: 0 |
|||
|
|||
SourceSansProLight |
|||
{ |
|||
id: srcSansProLight |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
color: projectFilesStyle.title.background |
|||
height: projectFilesStyle.title.height |
|||
Layout.fillWidth: true |
|||
Image { |
|||
id: projectIcon |
|||
source: "qrc:/qml/img/dappProjectIcon.png" |
|||
anchors.right: projectTitle.left |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
anchors.rightMargin: 6 |
|||
fillMode: Image.PreserveAspectFit |
|||
width: 32 |
|||
height: 32 |
|||
} |
|||
|
|||
Text |
|||
{ |
|||
id: projectTitle |
|||
color: projectFilesStyle.title.color |
|||
text: projectModel.projectTitle |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
visible: !projectModel.isEmpty; |
|||
anchors.left: parent.left |
|||
anchors.leftMargin: projectFilesStyle.general.leftMargin |
|||
font.family: srcSansProLight.name |
|||
font.pointSize: projectFilesStyle.title.fontSize |
|||
font.weight: Font.Light |
|||
} |
|||
|
|||
Text |
|||
{ |
|||
text: "-" |
|||
anchors.right: parent.right |
|||
anchors.rightMargin: 15 |
|||
font.family: srcSansProLight.name |
|||
font.pointSize: projectFilesStyle.title.fontSize |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
font.weight: Font.Light |
|||
} |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
Layout.fillWidth: true |
|||
height: 3 |
|||
color: projectFilesStyle.documentsList.background |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
Layout.fillWidth: true |
|||
Layout.fillHeight: true |
|||
color: projectFilesStyle.documentsList.background |
|||
|
|||
ColumnLayout |
|||
{ |
|||
anchors.top: parent.top |
|||
width: parent.width |
|||
spacing: 0 |
|||
Repeater { |
|||
model: [qsTr("Contracts"), qsTr("Javascript"), qsTr("Web Pages"), qsTr("Styles"), qsTr("Images"), qsTr("Misc")]; |
|||
signal selected(string doc, string groupName) |
|||
signal isCleanChanged(string doc, string groupName, var isClean) |
|||
property int incr: -1; |
|||
id: sectionRepeater |
|||
FilesSection |
|||
{ |
|||
id: section; |
|||
sectionName: modelData |
|||
index: |
|||
{ |
|||
for (var k in sectionRepeater.model) |
|||
{ |
|||
if (sectionRepeater.model[k] === modelData) |
|||
return k; |
|||
} |
|||
} |
|||
|
|||
model: sectionModel |
|||
selManager: sectionRepeater |
|||
|
|||
onDocumentSelected: { |
|||
selManager.selected(doc, groupName); |
|||
} |
|||
|
|||
ListModel |
|||
{ |
|||
id: sectionModel |
|||
} |
|||
|
|||
Connections { |
|||
target: codeModel |
|||
onContractRenamed: { |
|||
if (modelData === "Contracts") |
|||
{ |
|||
var ci = 0; |
|||
for (var si = 0; si < projectModel.listModel.count; si++) { |
|||
var document = projectModel.listModel.get(si); |
|||
if (document.isContract) { |
|||
var compiledDoc = codeModel.contractByDocumentId(document.documentId); |
|||
if (_documentId === document.documentId && _newName !== document.name) { |
|||
document.name = _newName; |
|||
projectModel.listModel.set(si, document); |
|||
sectionModel.set(ci, document); |
|||
} |
|||
ci++; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
onCompilationComplete: { |
|||
if (modelData === "Contracts") { |
|||
var ci = 0; |
|||
for (var si = 0; si < projectModel.listModel.count; si++) { |
|||
var document = projectModel.listModel.get(si); |
|||
if (document.isContract) { |
|||
var compiledDoc = codeModel.contractByDocumentId(document.documentId); |
|||
if (compiledDoc && compiledDoc.documentId === document.documentId && compiledDoc.contract.name !== document.name) { |
|||
document.name = compiledDoc.contract.name; |
|||
projectModel.listModel.set(si, document); |
|||
sectionModel.set(ci, document); |
|||
} |
|||
ci++; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
Connections { |
|||
id: projectModelConnection |
|||
target: projectModel |
|||
|
|||
function addDocToSubModel() |
|||
{ |
|||
for (var k = 0; k < projectModel.listModel.count; k++) |
|||
{ |
|||
var item = projectModel.listModel.get(k); |
|||
if (item.groupName === modelData) |
|||
sectionModel.append(item); |
|||
} |
|||
} |
|||
|
|||
onIsCleanChanged: { |
|||
for (var si = 0; si < sectionModel.count; si++) { |
|||
var document = sectionModel.get(si); |
|||
if (documentId === document.documentId && document.groupName === modelData) |
|||
{ |
|||
selManager.isCleanChanged(documentId, modelData, isClean); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
onDocumentOpened: { |
|||
if (document.groupName === modelData) |
|||
sectionRepeater.selected(document.documentId, modelData); |
|||
} |
|||
|
|||
onNewProject: { |
|||
sectionModel.clear(); |
|||
} |
|||
|
|||
onProjectClosed: { |
|||
sectionModel.clear(); |
|||
} |
|||
|
|||
onProjectLoaded: { |
|||
sectionModel.clear(); |
|||
addDocToSubModel(); |
|||
if (modelData === "Contracts") |
|||
{ |
|||
var selItem = projectModel.listModel.get(0); |
|||
projectModel.openDocument(selItem.documentId); |
|||
sectionRepeater.selected(selItem.documentId, modelData); |
|||
} |
|||
} |
|||
|
|||
onDocumentAdded: |
|||
{ |
|||
var newDoc = projectModel.getDocument(documentId); |
|||
if (newDoc.groupName === modelData) |
|||
{ |
|||
sectionModel.append(newDoc); |
|||
projectModel.openDocument(newDoc.documentId); |
|||
sectionRepeater.selected(newDoc.documentId, modelData); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
@ -1,230 +0,0 @@ |
|||
import QtQuick 2.0 |
|||
import QtQuick.Window 2.0 |
|||
import QtQuick.Layouts 1.0 |
|||
import QtQuick.Controls 1.0 |
|||
import QtQuick.Dialogs 1.1 |
|||
import Qt.labs.settings 1.0 |
|||
import "js/ProjectModel.js" as ProjectModelCode |
|||
import "js/NetworkDeployment.js" as NetworkDeploymentCode |
|||
|
|||
Item { |
|||
id: projectModel |
|||
|
|||
signal projectClosed |
|||
signal projectLoading(var projectData) |
|||
signal projectLoaded() |
|||
signal documentSaving(var document) |
|||
signal documentChanged(var documentId) |
|||
signal documentOpened(var document) |
|||
signal documentRemoved(var documentId) |
|||
signal documentUpdated(var documentId) //renamed |
|||
signal documentAdded(var documentId) |
|||
signal projectSaving() |
|||
signal projectFileSaving(var projectData) |
|||
signal projectSaved() |
|||
signal projectFileSaved() |
|||
signal newProject(var projectData) |
|||
signal documentSaved(var documentId) |
|||
signal contractSaved(var documentId) |
|||
signal deploymentStarted() |
|||
signal deploymentStepChanged(string message) |
|||
signal deploymentComplete() |
|||
signal deploymentError(string error) |
|||
signal isCleanChanged(var isClean, string documentId) |
|||
|
|||
property bool isEmpty: (projectPath === "") |
|||
readonly property string projectFileName: ".mix" |
|||
|
|||
property bool appIsClosing: false |
|||
property bool projectIsClosing: false |
|||
property string projectPath: "" |
|||
property string projectTitle: "" |
|||
property string currentDocumentId: "" |
|||
property var deploymentAddresses: ({}) |
|||
property string deploymentDir |
|||
property var listModel: projectListModel |
|||
property var stateListModel: projectStateListModel.model |
|||
property alias stateDialog: projectStateListModel.stateDialog |
|||
property CodeEditorView codeEditor: null |
|||
property var unsavedFiles: [] |
|||
property alias newProjectDialog: newProjectDialog |
|||
property int deployedScenarioIndex |
|||
property string applicationUrlEth |
|||
property string applicationUrlHttp |
|||
property string deployBlockNumber |
|||
property var deploymentTrHashes |
|||
property string registerContentHashTrHash |
|||
property string registerUrlTrHash |
|||
property int registerContentHashBlockNumber: -1 |
|||
property int registerUrlBlockNumber: -1 |
|||
|
|||
//interface |
|||
function saveAll() { ProjectModelCode.saveAll(); } |
|||
function saveCurrentDocument() { ProjectModelCode.saveCurrentDocument(); } |
|||
function createProject() { ProjectModelCode.createProject(); } |
|||
function closeProject(callBack) { ProjectModelCode.closeProject(callBack); } |
|||
function saveProject() { ProjectModelCode.saveProject(); } |
|||
function saveProjectFile() { ProjectModelCode.saveProjectFile(); } |
|||
function loadProject(path) { ProjectModelCode.loadProject(path); } |
|||
function newHtmlFile() { ProjectModelCode.newHtmlFile(); } |
|||
function newJsFile() { ProjectModelCode.newJsFile(); } |
|||
function newCssFile() { ProjectModelCode.newCssFile(); } |
|||
function newContract() { ProjectModelCode.newContract(); } |
|||
function openDocument(documentId) { ProjectModelCode.openDocument(documentId); } |
|||
function openNextDocument() { ProjectModelCode.openNextDocument(); } |
|||
function openPrevDocument() { ProjectModelCode.openPrevDocument(); } |
|||
function renameDocument(documentId, newName) { ProjectModelCode.renameDocument(documentId, newName); } |
|||
function removeDocument(documentId) { ProjectModelCode.removeDocument(documentId); } |
|||
function getDocument(documentId) { return ProjectModelCode.getDocument(documentId); } |
|||
function getDocumentIdByName(documentName) { return ProjectModelCode.getDocumentIdByName(documentName); } |
|||
function getDocumentIndex(documentId) { return ProjectModelCode.getDocumentIndex(documentId); } |
|||
function addExistingFiles(paths) { ProjectModelCode.doAddExistingFiles(paths); } |
|||
function deployProject() { NetworkDeploymentCode.deployProject(false); } |
|||
function registerToUrlHint(url, gasPrice, callback) { NetworkDeploymentCode.registerToUrlHint(url, gasPrice, callback); } |
|||
function formatAppUrl() { NetworkDeploymentCode.formatAppUrl(url); } |
|||
|
|||
function cleanDeploymentStatus() |
|||
{ |
|||
deployedScenarioIndex = 0 |
|||
deployBlockNumber = "" |
|||
deploymentTrHashes = {} |
|||
deploymentAddresses = {} |
|||
deploymentDir = "" |
|||
deploymentDialog.packageStep.packageHash = "" |
|||
deploymentDialog.packageStep.packageBase64 = "" |
|||
deploymentDialog.packageStep.packageDir = "" |
|||
deploymentDialog.packageStep.lastDeployDate = "" |
|||
deploymentDialog.packageStep.localPackageUrl = "" |
|||
saveProject() |
|||
cleanRegisteringStatus() |
|||
} |
|||
|
|||
function cleanRegisteringStatus() |
|||
{ |
|||
applicationUrlEth = "" |
|||
applicationUrlHttp = "" |
|||
registerContentHashTrHash = "" |
|||
registerUrlTrHash = "" |
|||
registerContentHashBlockNumber = -1 |
|||
registerUrlBlockNumber = -1 |
|||
saveProject() |
|||
} |
|||
|
|||
Connections { |
|||
target: mainApplication |
|||
onLoaded: { |
|||
if (mainApplication.trackLastProject && projectSettings.lastProjectPath && projectSettings.lastProjectPath !== "") |
|||
projectModel.loadProject(projectSettings.lastProjectPath) |
|||
} |
|||
} |
|||
|
|||
Connections { |
|||
target: codeEditor |
|||
onIsCleanChanged: { |
|||
for (var i in unsavedFiles) |
|||
{ |
|||
if (unsavedFiles[i] === documentId && isClean) |
|||
unsavedFiles.splice(i, 1); |
|||
} |
|||
if (!isClean) |
|||
unsavedFiles.push(documentId); |
|||
isCleanChanged(isClean, documentId); |
|||
} |
|||
} |
|||
|
|||
NewProjectDialog { |
|||
id: newProjectDialog |
|||
visible: false |
|||
onAccepted: { |
|||
var title = newProjectDialog.projectTitle; |
|||
var path = newProjectDialog.projectPath; |
|||
ProjectModelCode.doCreateProject(title, path); |
|||
} |
|||
} |
|||
|
|||
Connections |
|||
{ |
|||
target: fileIo |
|||
property bool saving: false |
|||
onFileChanged: |
|||
{ |
|||
fileIo.watchFileChanged(_filePath); |
|||
var documentId = ProjectModelCode.getDocumentByPath(_filePath); |
|||
documentChanged(documentId); |
|||
} |
|||
} |
|||
|
|||
MessageDialog { |
|||
id: saveMessageDialog |
|||
title: qsTr("Project") |
|||
text: qsTr("Some files require to be saved. Do you want to save changes?"); |
|||
standardButtons: StandardButton.Yes | StandardButton.No | StandardButton.Cancel |
|||
icon: StandardIcon.Question |
|||
property var callBack; |
|||
onYes: { |
|||
projectIsClosing = true; |
|||
projectModel.saveAll(); |
|||
unsavedFiles = []; |
|||
ProjectModelCode.doCloseProject(); |
|||
if (callBack) |
|||
callBack(); |
|||
} |
|||
onRejected: {} |
|||
onNo: { |
|||
projectIsClosing = true; |
|||
unsavedFiles = []; |
|||
ProjectModelCode.doCloseProject(); |
|||
if (callBack) |
|||
callBack(); |
|||
} |
|||
} |
|||
|
|||
MessageDialog { |
|||
id: deployWarningDialog |
|||
title: qsTr("Project") |
|||
text: |
|||
{ |
|||
if (Object.keys(projectModel.deploymentAddresses).length > 0) |
|||
return qsTr("This project has been already deployed to the network. Do you want to redeploy it? (Contract state will be reset)") |
|||
else |
|||
return qsTr("This action will deploy to the network. Do you want to deploy it?") |
|||
} |
|||
icon: StandardIcon.Question |
|||
standardButtons: StandardButton.Ok | StandardButton.Abort |
|||
onAccepted: { |
|||
NetworkDeploymentCode.startDeployProject(true); |
|||
} |
|||
} |
|||
|
|||
MessageDialog { |
|||
id: deployResourcesDialog |
|||
title: qsTr("Project") |
|||
standardButtons: StandardButton.Ok |
|||
} |
|||
|
|||
DeploymentDialog |
|||
{ |
|||
id: deploymentDialog |
|||
} |
|||
|
|||
ListModel { |
|||
id: projectListModel |
|||
} |
|||
|
|||
StateListModel { |
|||
id: projectStateListModel |
|||
} |
|||
|
|||
Connections |
|||
{ |
|||
target: projectModel |
|||
onProjectClosed: { |
|||
projectPath = ""; |
|||
} |
|||
} |
|||
|
|||
Settings { |
|||
id: projectSettings |
|||
property string lastProjectPath; |
|||
} |
|||
} |
@ -1,226 +0,0 @@ |
|||
import QtQuick 2.0 |
|||
import QtQuick.Controls 1.3 |
|||
import QtQuick.Controls.Styles 1.3 |
|||
import QtQuick.Layouts 1.1 |
|||
import "js/InputValidator.js" as InputValidator |
|||
|
|||
ColumnLayout |
|||
{ |
|||
property alias value: textinput.text |
|||
property alias accountRef: ctrModel |
|||
property string subType |
|||
property bool readOnly |
|||
property alias currentIndex: trCombobox.currentIndex |
|||
property alias displayInput: textInputRect.visible |
|||
property variant accounts |
|||
signal indexChanged() |
|||
spacing: 0 |
|||
id: editRoot |
|||
height: |
|||
{ |
|||
if (isArray() && !readOnly) |
|||
return 60 |
|||
else |
|||
return 30 |
|||
} |
|||
|
|||
width: 320 |
|||
|
|||
SourceSansProBold |
|||
{ |
|||
id: boldFont |
|||
} |
|||
|
|||
function isArray() |
|||
{ |
|||
InputValidator.init() |
|||
return InputValidator.isArray(subType) |
|||
} |
|||
|
|||
function currentValue() { |
|||
return value; |
|||
} |
|||
|
|||
function currentType() |
|||
{ |
|||
return accountRef.get(trCombobox.currentIndex).type; |
|||
} |
|||
|
|||
function current() |
|||
{ |
|||
return accountRef.get(trCombobox.currentIndex); |
|||
} |
|||
|
|||
function load() |
|||
{ |
|||
accountRef.clear(); |
|||
if (subType.indexOf("contract") !== -1 || subType.indexOf("address") !== -1) |
|||
{ |
|||
var trCr = 0; |
|||
if (blockChainPanel) |
|||
for (var k = 0; k < blockChainPanel.model.blocks.length; k++) |
|||
{ |
|||
if (k > blockIndex) |
|||
break; |
|||
for (var i = 0; i < blockChainPanel.model.blocks[k].transactions.length; i++) |
|||
{ |
|||
if (i > transactionIndex) |
|||
break; |
|||
var tr = blockChainPanel.model.blocks[k].transactions[i] |
|||
if (tr.functionId === tr.contractId) |
|||
{ |
|||
accountRef.append({ "itemid": tr.contractId + " - " + trCr, "value": "<" + tr.contractId + " - " + trCr + ">", "type": "contract" }); |
|||
trCr++; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
if (subType.indexOf("address") !== -1) |
|||
{ |
|||
for (k = 0; k < accounts.length; k++) |
|||
{ |
|||
if (accounts[k].address === undefined) |
|||
accounts[k].address = clientModel.address(accounts[k].secret); |
|||
accountRef.append({ "itemid": accounts[k].name, "value": "0x" + accounts[k].address, "type": "address" }); |
|||
} |
|||
} |
|||
} |
|||
|
|||
function init() |
|||
{ |
|||
btnAdd.visible = isArray() |
|||
textinput.readOnly = readOnly |
|||
if (isArray() || readOnly) |
|||
displayInput = true |
|||
else |
|||
displayInput = false |
|||
|
|||
if (isArray() || !readOnly) |
|||
trCombobox.visible = true |
|||
else |
|||
trCombobox.visible = false |
|||
|
|||
if (!trCombobox.visible) |
|||
{ |
|||
rowCombobox.visible = false |
|||
rowCombobox.height = 0 |
|||
trCombobox.height = 0 |
|||
textinput.anchors.top = textinput.parent.top |
|||
} |
|||
|
|||
if (!readOnly) |
|||
{ |
|||
trCombobox.currentIndex = 0 |
|||
for (var k = 0; k < ctrModel.count; k++) |
|||
{ |
|||
if (ctrModel.get(k).value === value) |
|||
{ |
|||
trCombobox.currentIndex = k |
|||
break |
|||
} |
|||
} |
|||
} |
|||
if (!isArray()) |
|||
trCombobox.update() |
|||
else if (value === "") |
|||
textinput.text = "[]" |
|||
} |
|||
|
|||
function select(address) |
|||
{ |
|||
for (var k = 0; k < accountRef.count; k++) |
|||
{ |
|||
if (accountRef.get(k).value === address) |
|||
{ |
|||
trCombobox.currentIndex = k; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
ListModel |
|||
{ |
|||
id: ctrModel |
|||
} |
|||
|
|||
Row |
|||
{ |
|||
anchors.top: parent.top |
|||
height: 30 |
|||
id: rowCombobox |
|||
ComboBox |
|||
{ |
|||
property bool selected: false |
|||
id: trCombobox |
|||
model: ctrModel |
|||
width: 265 |
|||
textRole: "itemid" |
|||
function update() |
|||
{ |
|||
trCombobox.selected = false; |
|||
if (currentText === "") |
|||
return; |
|||
else if (currentText !== " - ") |
|||
{ |
|||
if (model.get(currentIndex).type === "contract") |
|||
textinput.text = "<" + currentText + ">"; |
|||
else |
|||
textinput.text = model.get(currentIndex).value; //address |
|||
trCombobox.selected = true; |
|||
} |
|||
else if (textinput.text.indexOf("<") === 0) |
|||
{ |
|||
textinput.text = ""; |
|||
} |
|||
indexChanged(); |
|||
} |
|||
|
|||
onCurrentIndexChanged: { |
|||
if (!isArray()) |
|||
update() |
|||
} |
|||
} |
|||
|
|||
Button |
|||
{ |
|||
id: btnAdd |
|||
text: qsTr("Add") |
|||
visible: false |
|||
onClicked: |
|||
{ |
|||
var ar = JSON.parse(textinput.text) |
|||
ar.push(trCombobox.model.get(currentIndex).value) |
|||
textinput.text = JSON.stringify(ar) |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
Rectangle { |
|||
radius: 4 |
|||
width: 350 |
|||
height: 30 |
|||
id: textInputRect |
|||
TextField { |
|||
id: textinput |
|||
text: value |
|||
anchors.fill: parent |
|||
clip: true |
|||
font.family: boldFont.name |
|||
MouseArea { |
|||
id: mouseArea |
|||
anchors.fill: parent |
|||
hoverEnabled: true |
|||
onClicked: textinput.forceActiveFocus() |
|||
} |
|||
onTextChanged: |
|||
{ |
|||
if (trCombobox.selected) |
|||
{ |
|||
trCombobox.currentIndex = 0; |
|||
trCombobox.selected = false; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
@ -1,109 +0,0 @@ |
|||
import QtQuick 2.0 |
|||
import QtQuick.Controls 1.3 |
|||
import "js/InputValidator.js" as InputValidator |
|||
|
|||
Item |
|||
{ |
|||
id: editRoot |
|||
property string value |
|||
property string defaultValue |
|||
property bool readOnly: !boolCombo.enabled |
|||
property string subType |
|||
height: 20 |
|||
width: 150 |
|||
|
|||
onReadOnlyChanged: { |
|||
boolCombo.enabled = !readOnly; |
|||
} |
|||
|
|||
function isArray() |
|||
{ |
|||
InputValidator.init() |
|||
return InputValidator.isArray(subType) |
|||
} |
|||
|
|||
function init() |
|||
{ |
|||
if (!isArray()) |
|||
{ |
|||
boolArray.visible = false |
|||
boolCombo.visible = true |
|||
|
|||
value = format(value) |
|||
|
|||
var setValue = "1" |
|||
if (value === "") |
|||
setValue = parseInt(defaultValue); |
|||
else |
|||
setValue = parseInt(value); |
|||
boolCombo.checked = setValue === "1" ? true: false |
|||
boolCombo.enabled = !readOnly; |
|||
} |
|||
else |
|||
{ |
|||
boolArray.visible = true |
|||
boolCombo.visible = false |
|||
if (value === "") |
|||
boolArray.text = "[]" |
|||
else |
|||
boolArray.text = value |
|||
|
|||
var formattedparam = [] |
|||
var param = JSON.parse(boolArray.text) |
|||
for (var k in JSON.parse(boolArray.text)) |
|||
formattedparam.push(parseInt(format(param[k]))) |
|||
boolArray.text = JSON.stringify(formattedparam) |
|||
} |
|||
} |
|||
|
|||
function finalize() |
|||
{ |
|||
if (isArray()) |
|||
value = boolArray.text |
|||
} |
|||
|
|||
function format(value) |
|||
{ |
|||
value = value === true ? "1" : value |
|||
value = value === false ? "0" : value; |
|||
value = value === "true" ? "1" : value |
|||
value = value === "false" ? "0" : value; |
|||
return value |
|||
} |
|||
|
|||
Rectangle { |
|||
color: "transparent" |
|||
anchors.fill: parent |
|||
CheckBox |
|||
{ |
|||
property bool inited; |
|||
Component.onCompleted: |
|||
{ |
|||
init(); |
|||
inited = true; |
|||
} |
|||
|
|||
id: boolCombo |
|||
anchors.fill: parent |
|||
onCheckedChanged: |
|||
{ |
|||
if (inited) |
|||
value = checked ? "1" : "0" |
|||
|
|||
} |
|||
text: qsTr("True") |
|||
} |
|||
|
|||
TextField |
|||
{ |
|||
id: boolArray |
|||
onTextChanged: |
|||
{ |
|||
value = text |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
|
@ -1,5 +0,0 @@ |
|||
import QtQuick 2.2 |
|||
import QtQuick.Controls 1.2 |
|||
import QtQuick.Dialogs 1.2 |
|||
|
|||
FileDialog { } |
@ -1,27 +0,0 @@ |
|||
import QtQuick 2.0 |
|||
|
|||
Item |
|||
{ |
|||
property alias value: textinput.text |
|||
property alias readOnly: textinput.readOnly |
|||
id: editRoot |
|||
height: 20 |
|||
width: 150 |
|||
|
|||
SourceSansProBold |
|||
{ |
|||
id: boldFont |
|||
} |
|||
|
|||
TextInput { |
|||
id: textinput |
|||
text: value |
|||
wrapMode: Text.WrapAnywhere |
|||
MouseArea { |
|||
id: mouseArea |
|||
anchors.fill: parent |
|||
hoverEnabled: true |
|||
onClicked: textinput.forceActiveFocus() |
|||
} |
|||
} |
|||
} |
@ -1,26 +0,0 @@ |
|||
import QtQuick 2.0 |
|||
import QtQuick.Controls 1.1 |
|||
Item |
|||
{ |
|||
property alias value: textinput.text |
|||
property alias readOnly: textinput.readOnly |
|||
id: editRoot |
|||
width: 350 |
|||
DebuggerPaneStyle { |
|||
id: dbgStyle |
|||
} |
|||
|
|||
TextField { |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
id: textinput |
|||
selectByMouse: true |
|||
text: value |
|||
implicitWidth: 350 |
|||
MouseArea { |
|||
id: mouseArea |
|||
anchors.fill: parent |
|||
hoverEnabled: true |
|||
onClicked: textinput.forceActiveFocus() |
|||
} |
|||
} |
|||
} |
@ -1,15 +0,0 @@ |
|||
import QtQuick 2.0 |
|||
|
|||
Component |
|||
{ |
|||
Rectangle { |
|||
anchors.fill: parent |
|||
Text{ |
|||
anchors.fill: parent |
|||
text: qsTr("Real") |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
|
@ -1,31 +0,0 @@ |
|||
import QtQuick 2.0 |
|||
import QtQuick.Controls 1.1 |
|||
|
|||
Item |
|||
{ |
|||
property alias value: textinput.text |
|||
property alias readOnly: textinput.readOnly |
|||
id: editRoot |
|||
width: 350 |
|||
|
|||
DebuggerPaneStyle { |
|||
id: dbgStyle |
|||
} |
|||
|
|||
TextField { |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
id: textinput |
|||
selectByMouse: true |
|||
text: value |
|||
width: 350 |
|||
MouseArea { |
|||
id: mouseArea |
|||
anchors.fill: parent |
|||
hoverEnabled: true |
|||
onClicked: textinput.forceActiveFocus() |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
|
@ -1,7 +0,0 @@ |
|||
import QtQuick 2.0 |
|||
import org.ethereum.qml.QVariableDeclaration 1.0 |
|||
|
|||
QVariableDeclaration |
|||
{ |
|||
} |
|||
|
@ -1,13 +0,0 @@ |
|||
/* |
|||
* Used to instanciate a QVariableDefinition obj using Qt.createComponent function. |
|||
*/ |
|||
import QtQuick 2.2 |
|||
import QtQuick.Controls 1.1 |
|||
import QtQuick.Layouts 1.1 |
|||
import QtQuick.Controls.Styles 1.1 |
|||
import org.ethereum.qml.QVariableDefinition 1.0 |
|||
|
|||
QVariableDefinition |
|||
{ |
|||
id: qVariableDefinition |
|||
} |
@ -1,352 +0,0 @@ |
|||
import QtQuick 2.0 |
|||
import QtQuick.Layouts 1.0 |
|||
import QtQuick.Controls 1.1 |
|||
import QtQuick.Controls.Styles 1.3 |
|||
import org.ethereum.qml.QEther 1.0 |
|||
import Qt.labs.settings 1.0 |
|||
import "js/TransactionHelper.js" as TransactionHelper |
|||
import "js/NetworkDeployment.js" as NetworkDeploymentCode |
|||
import "js/QEtherHelper.js" as QEtherHelper |
|||
import "." |
|||
|
|||
Rectangle { |
|||
property variant worker |
|||
property string eth: registrarAddr.text |
|||
property int ownedRegistrarDeployGas: 1179075 // TODO: Use sol library to calculate gas requirement for each tr. |
|||
property int ownedRegistrarSetSubRegistrarGas: 50000 |
|||
property int ownedRegistrarSetContentHashGas: 50000 |
|||
property int urlHintSuggestUrlGas: 70000 |
|||
id: root |
|||
color: "#E3E3E3E3" |
|||
anchors.fill: parent |
|||
signal registered |
|||
|
|||
function show() |
|||
{ |
|||
ctrRegisterLabel.calculateRegisterGas() |
|||
if (applicationUrlHttpCtrl.text === "") |
|||
applicationUrlHttpCtrl.text = projectModel.applicationUrlHttp |
|||
|
|||
if (applicationUrlEthCtrl.text === "") |
|||
applicationUrlEthCtrl.text = projectModel.applicationUrlEth |
|||
|
|||
visible = true |
|||
|
|||
worker.pooler.onTriggered.connect(function() { |
|||
if (root.visible) |
|||
verifyRegistering(); |
|||
}) |
|||
} |
|||
|
|||
function verifyRegistering() |
|||
{ |
|||
verificationEthUrl.text = "" |
|||
if (projectModel.registerContentHashTrHash !== "" && projectModel.registerContentHashBlockNumber !== -1) |
|||
{ |
|||
worker.verifyHash("registerHash", projectModel.registerContentHashTrHash, function(bn, trLost) |
|||
{ |
|||
updateVerification(projectModel.registerContentHashBlockNumber, bn, trLost, verificationEthUrl, "registerHash") |
|||
}); |
|||
} |
|||
else if (projectModel.registerContentHashTrHash !== "" && projectModel.registerContentHashBlockNumber === -1) |
|||
verificationEthUrl.text = qsTr("waiting verifications") |
|||
|
|||
verificationUrl.text = "" |
|||
if (projectModel.registerUrlTrHash !== "" && projectModel.registerUrlBlockNumber !== -1) |
|||
{ |
|||
worker.verifyHash("registerUrl", projectModel.registerUrlTrHash, function(bn, trLost) |
|||
{ |
|||
updateVerification(projectModel.registerUrlBlockNumber, bn, trLost, verificationUrl, "registerUrl") |
|||
}); |
|||
} |
|||
else if (projectModel.registerUrlTrHash !== "" && projectModel.registerUrlBlockNumber === -1) |
|||
verificationUrl.text = qsTr("waiting verifications") |
|||
} |
|||
|
|||
function updateVerification(originbn, bn, trLost, ctrl, trContext) |
|||
{ |
|||
if (trLost.length === 0) |
|||
{ |
|||
ctrl.text = bn - originbn |
|||
if (parseInt(bn - originbn) >= 10) |
|||
{ |
|||
ctrl.color= "green" |
|||
ctrl.text= qsTr("verified") |
|||
} |
|||
else |
|||
ctrl.text += qsTr(" verifications") |
|||
} |
|||
else |
|||
{ |
|||
deploymentStepChanged(trContext + qsTr(" has been invalidated.") + trLost[0] + " " + qsTr("no longer present") ) |
|||
ctrl.text = qsTr("invalidated") |
|||
} |
|||
} |
|||
|
|||
ColumnLayout |
|||
{ |
|||
anchors.top: parent.top |
|||
width: parent.width |
|||
anchors.topMargin: 10 |
|||
id: col |
|||
spacing: 20 |
|||
anchors.left: parent.left |
|||
anchors.leftMargin: 10 |
|||
Label |
|||
{ |
|||
anchors.top: parent.top |
|||
Layout.fillWidth: true |
|||
text: qsTr("Register your Dapp on the Name registrar Contract") |
|||
} |
|||
|
|||
RowLayout |
|||
{ |
|||
Layout.fillWidth: true |
|||
Layout.preferredHeight: 20 |
|||
Rectangle |
|||
{ |
|||
Layout.preferredWidth: col.width / 5 |
|||
Label |
|||
{ |
|||
text: qsTr("Root Registrar address") |
|||
anchors.left: parent.left |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
} |
|||
} |
|||
|
|||
DefaultTextField |
|||
{ |
|||
id: registrarAddr |
|||
text: "c6d9d2cd449a754c494264e1809c50e34d64562b" |
|||
visible: true |
|||
Layout.preferredWidth: 450 |
|||
} |
|||
} |
|||
|
|||
RowLayout |
|||
{ |
|||
Layout.fillWidth: true |
|||
Layout.preferredHeight: 20 |
|||
Rectangle |
|||
{ |
|||
Layout.preferredWidth: col.width / 5 |
|||
Label |
|||
{ |
|||
text: qsTr("Http URL") |
|||
anchors.left: parent.left |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
} |
|||
} |
|||
|
|||
DefaultTextField |
|||
{ |
|||
id: applicationUrlHttpCtrl |
|||
Layout.preferredWidth: 450 |
|||
} |
|||
|
|||
Label |
|||
{ |
|||
id: verificationUrl |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
font.italic: true |
|||
} |
|||
} |
|||
|
|||
RowLayout |
|||
{ |
|||
Layout.fillWidth: true |
|||
Layout.preferredHeight: 20 |
|||
Rectangle |
|||
{ |
|||
Layout.preferredWidth: col.width / 5 |
|||
Label |
|||
{ |
|||
text: qsTr("Registration Cost") |
|||
anchors.left: parent.left |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
id: ctrRegisterLabel |
|||
function calculateRegisterGas() |
|||
{ |
|||
if (!modalDeploymentDialog.visible) |
|||
return; |
|||
appUrlFormatted.text = NetworkDeploymentCode.formatAppUrl(applicationUrlEthCtrl.text).join('/'); |
|||
NetworkDeploymentCode.checkPathCreationCost(applicationUrlEthCtrl.text, function(pathCreationCost) |
|||
{ |
|||
var ether = QEtherHelper.createBigInt(pathCreationCost); |
|||
if (deploymentDialog.deployStep.gasPrice.value) |
|||
{ |
|||
var gasTotal = ether.multiply(deploymentDialog.deployStep.gasPrice.value.toWei()); |
|||
gasToUseDeployInput.value = QEtherHelper.createEther(gasTotal.value(), QEther.Wei, parent); |
|||
gasToUseDeployInput.update(); |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
|
|||
Ether |
|||
{ |
|||
id: gasToUseDeployInput |
|||
displayUnitSelection: false |
|||
displayFormattedValue: true |
|||
edit: false |
|||
Layout.preferredWidth: 235 |
|||
} |
|||
} |
|||
|
|||
RowLayout |
|||
{ |
|||
Layout.fillWidth: true |
|||
Layout.preferredHeight: 20 |
|||
Rectangle |
|||
{ |
|||
Layout.preferredWidth: col.width / 5 |
|||
Label |
|||
{ |
|||
text: qsTr("Ethereum URL") |
|||
anchors.left: parent.left |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
} |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
height: 25 |
|||
color: "transparent" |
|||
Layout.preferredWidth: 450 |
|||
DefaultTextField |
|||
{ |
|||
width: 450 |
|||
id: applicationUrlEthCtrl |
|||
onTextChanged: { |
|||
ctrRegisterLabel.calculateRegisterGas(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
RowLayout |
|||
{ |
|||
Layout.fillWidth: true |
|||
Layout.preferredHeight: 20 |
|||
Rectangle |
|||
{ |
|||
Layout.preferredWidth: col.width / 4 |
|||
Label |
|||
{ |
|||
text: qsTr("Formatted Ethereum URL") |
|||
anchors.left: parent.left |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
} |
|||
} |
|||
|
|||
DefaultLabel |
|||
{ |
|||
id: appUrlFormatted |
|||
anchors.verticalCenter: parent.verticalCenter; |
|||
anchors.topMargin: 10 |
|||
font.italic: true |
|||
font.pointSize: appStyle.absoluteSize(-1) |
|||
} |
|||
|
|||
Label |
|||
{ |
|||
id: verificationEthUrl |
|||
anchors.verticalCenter: parent.verticalCenter; |
|||
anchors.topMargin: 10 |
|||
font.italic: true |
|||
font.pointSize: appStyle.absoluteSize(-1) |
|||
} |
|||
} |
|||
} |
|||
|
|||
RowLayout |
|||
{ |
|||
anchors.bottom: parent.bottom |
|||
anchors.bottomMargin: 10 |
|||
width: parent.width |
|||
|
|||
function registerHash(gasPrice, callback) |
|||
{ |
|||
var inError = []; |
|||
var ethUrl = NetworkDeploymentCode.formatAppUrl(applicationUrlEthCtrl.text); |
|||
for (var k in ethUrl) |
|||
{ |
|||
if (ethUrl[k].length > 32) |
|||
inError.push(qsTr("Member too long: " + ethUrl[k]) + "\n"); |
|||
} |
|||
if (!worker.stopForInputError(inError)) |
|||
{ |
|||
NetworkDeploymentCode.registerDapp(ethUrl, gasPrice, function(){ |
|||
projectModel.applicationUrlEth = applicationUrlEthCtrl.text |
|||
projectModel.saveProject() |
|||
verificationEthUrl.text = qsTr("waiting verifications") |
|||
worker.waitForTrReceipt(projectModel.registerContentHashTrHash, function(status, receipt) |
|||
{ |
|||
worker.verifyHash("registerHash", projectModel.registerContentHashTrHash, function(bn, trLost) |
|||
{ |
|||
projectModel.registerContentHashBlockNumber = bn |
|||
projectModel.saveProject() |
|||
root.updateVerification(bn, bn, trLost, verificationEthUrl) |
|||
callback() |
|||
}); |
|||
}); |
|||
}) |
|||
} |
|||
} |
|||
|
|||
function registerUrl(gasPrice, callback) |
|||
{ |
|||
if (applicationUrlHttp.text === "" || deploymentDialog.packageHash === "") |
|||
{ |
|||
deployDialog.title = text; |
|||
deployDialog.text = qsTr("Please provide the link where the resources are stored and ensure the package is aleary built using the deployment step.") |
|||
deployDialog.open(); |
|||
return; |
|||
} |
|||
var inError = []; |
|||
if (applicationUrlHttpCtrl.text.length > 32) |
|||
inError.push(qsTr(applicationUrlHttpCtrl.text)); |
|||
if (!worker.stopForInputError(inError)) |
|||
{ |
|||
var url = applicationUrlHttpCtrl.text |
|||
url = url.replace("http://", "").replace("https://", "") |
|||
registerToUrlHint(url, gasPrice, function(){ |
|||
projectModel.applicationUrlHttp = applicationUrlHttpCtrl.text |
|||
projectModel.saveProject() |
|||
verificationUrl.text = qsTr("waiting verifications") |
|||
worker.waitForTrReceipt(projectModel.registerUrlTrHash, function(status, receipt) |
|||
{ |
|||
worker.verifyHash("registerUrl", projectModel.registerUrlTrHash, function(bn, trLost) |
|||
{ |
|||
projectModel.registerUrlBlockNumber = bn |
|||
projectModel.saveProject() |
|||
root.updateVerification(bn, bn, trLost, verificationUrl) |
|||
root.registered() |
|||
callback() |
|||
}); |
|||
}) |
|||
}) |
|||
} |
|||
} |
|||
|
|||
Button |
|||
{ |
|||
anchors.right: parent.right |
|||
anchors.rightMargin: 10 |
|||
text: qsTr("Register Dapp") |
|||
width: 30 |
|||
onClicked: |
|||
{ |
|||
verificationEthUrl.text = "" |
|||
verificationUrl.text = "" |
|||
projectModel.cleanRegisteringStatus() |
|||
var gasPrice = deploymentDialog.deployStep.gasPrice.toHexWei() |
|||
parent.registerHash(gasPrice, function(){ |
|||
parent.registerUrl(gasPrice, function(){}) |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
@ -1,130 +0,0 @@ |
|||
import QtQuick 2.2 |
|||
import QtQuick.Controls 1.1 |
|||
import QtQuick.Layouts 1.0 |
|||
import QtQuick.Controls.Styles 1.1 |
|||
import QtGraphicalEffects 1.0 |
|||
|
|||
Rectangle { |
|||
id: buttonActionContainer |
|||
property string text |
|||
property string buttonShortcut |
|||
property string sourceImg |
|||
property string fillColor |
|||
property alias roundLeft: left.visible |
|||
property alias roundRight: right.visible |
|||
signal clicked |
|||
|
|||
function startBlinking() |
|||
{ |
|||
if (!blinkTimer.running) |
|||
blinkTimer.start() |
|||
} |
|||
|
|||
function stopBlinking() |
|||
{ |
|||
blinkTimer.stop() |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
id: left |
|||
width: 10 |
|||
height: parent.height |
|||
anchors.left: contentRectangle.left |
|||
anchors.leftMargin: -4 |
|||
radius: 15 |
|||
} |
|||
|
|||
Rectangle { |
|||
id: contentRectangle |
|||
anchors.fill: parent |
|||
color: "white" |
|||
property variant colorGradient: ["#FFFFFF", "#FFFEFC", "#FFFDF9", "#FFFCF7", "#FFFBF4", "#FFFAF2", "#FFF9EF", "#FFF8EC", "#FFF7EA", "#FFF6E7", "#FFF5E5", "#FFF5E2", "#FFF4E0", "#FFF3DD", "#FFF2DA", "#FFF1D8", "#FFF0D5", "#FFEFD3", "#FFEED0", "#FFEDCE", "#FFECCB", "#FFEBC8", "#FFEBC6", "#FFEAC3", "#FFE9C1", "#FFE8BE", "#FFE7BC", "#FFE6B9", "#FFE5B6", "#FFE4B4", "#FFE3B1", "#FFE2AF", "#FFE1AC", "#FFE1AA", "#FFE0A7", "#FFDFA4", "#FFDEA2", "#FFDD9F", "#FFDC9D", "#FFDB9A", "#FFDA97", "#FFD995", "#FFD892", "#FFD790", "#FFD78D", "#FFD68B", "#FFD588", "#FFD485", "#FFD383", "#FFD280", "#FFD17E", "#FFD07B", "#FFCF79", "#FFCE76", "#FFCD73", "#FFCD71", "#FFCC6E", "#FFCB6C", "#FFCA69", "#FFC967", "#FFC864", "#FFC761", "#FFC65F", "#FFC55C", "#FFC45A", "#FFC357", "#FFC355", "#FFC252", "#FFC14F", "#FFC04D", "#FFBF4A", "#FFBE48", "#FFBD45", "#FFBC42", "#FFBB40", "#FFBA3D", "#FFB93B", "#FFB938", "#FFB836", "#FFB733", "#FFB630", "#FFB52E", "#FFB42B", "#FFB329", "#FFB226", "#FFB124", "#FFB021", "#FFAF1E", "#FFAF1C", "#FFAE19", "#FFAD17", "#FFAC14", "#FFAB12", "#FFAA0F", "#FFA90C", "#FFA80A", "#FFA707", "#FFA605", "#FFA502", "#FFA500"] |
|||
|
|||
Timer |
|||
{ |
|||
id: blinkTimer |
|||
repeat: true |
|||
interval: 40 |
|||
running: false |
|||
property int index: 0 |
|||
property int direction: 1 |
|||
onTriggered: { |
|||
index = index + direction |
|||
var color = parent.colorGradient[index] |
|||
left.color = color |
|||
right.color = color |
|||
parent.color = parent.colorGradient[index] |
|||
if (index >= parent.colorGradient.length - 1) |
|||
direction = -1 |
|||
else if (index <= 0) |
|||
direction = 1 |
|||
} |
|||
onRunningChanged: { |
|||
if (!running) |
|||
{ |
|||
left.color = "white" |
|||
right.color = "white" |
|||
parent.color = "white" |
|||
index = 0 |
|||
direction = 1 |
|||
} |
|||
} |
|||
} |
|||
|
|||
Image { |
|||
id: debugImage |
|||
anchors { |
|||
left: parent.left |
|||
right: parent.right |
|||
top: parent.top |
|||
bottom: parent.bottom |
|||
bottomMargin: debugImg.pressed ? -2 : 0; |
|||
topMargin: debugImg.pressed ? 2 : 0; |
|||
} |
|||
source: sourceImg |
|||
fillMode: Image.PreserveAspectFit |
|||
} |
|||
|
|||
Button { |
|||
anchors.fill: parent |
|||
id: debugImg |
|||
action: buttonAction |
|||
style: ButtonStyle { |
|||
background: Rectangle { |
|||
color: "transparent" |
|||
} |
|||
} |
|||
} |
|||
|
|||
Action { |
|||
id: buttonAction |
|||
shortcut: buttonShortcut |
|||
onTriggered: { |
|||
buttonActionContainer.clicked(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
id: right |
|||
width: 10 |
|||
height: parent.height |
|||
anchors.right: contentRectangle.right |
|||
anchors.rightMargin: -4 |
|||
radius: 15 |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
anchors.top: contentRectangle.bottom |
|||
anchors.topMargin: 15 |
|||
width: parent.width |
|||
Label |
|||
{ |
|||
text: buttonActionContainer.text |
|||
anchors.centerIn: parent |
|||
} |
|||
} |
|||
} |
@ -1,128 +0,0 @@ |
|||
import QtQuick 2.2 |
|||
import QtQuick.Controls 1.1 |
|||
import QtQuick.Controls.Styles 1.1 |
|||
import QtQuick.Dialogs 1.1 |
|||
import QtQuick.Layouts 1.1 |
|||
import Qt.labs.settings 1.0 |
|||
import "js/Debugger.js" as Debugger |
|||
import "js/ErrorLocationFormater.js" as ErrorLocationFormater |
|||
import "." |
|||
|
|||
|
|||
Rectangle { |
|||
color: "#ededed" |
|||
property alias bc: blockChain |
|||
|
|||
Connections |
|||
{ |
|||
target: projectModel |
|||
onProjectLoaded: { |
|||
loader.init() |
|||
} |
|||
} |
|||
|
|||
ScrollView |
|||
{ |
|||
anchors.fill: parent |
|||
onWidthChanged: { |
|||
columnExe.width = width - 40 |
|||
} |
|||
|
|||
ColumnLayout |
|||
{ |
|||
id: columnExe |
|||
Layout.preferredWidth: parent.width |
|||
anchors.left: parent.left |
|||
anchors.leftMargin: 15 |
|||
ColumnLayout |
|||
{ |
|||
id: scenarioColumn |
|||
width: parent.width |
|||
spacing: 10 |
|||
ScenarioLoader |
|||
{ |
|||
anchors.horizontalCenter: parent.horizontalCenter |
|||
height: 100 |
|||
Layout.preferredWidth: 400 |
|||
width: 400 |
|||
id: loader |
|||
} |
|||
|
|||
Connections |
|||
{ |
|||
target: blockChain |
|||
onChainChanged: |
|||
{ |
|||
loader.needSaveOrReload() |
|||
} |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
Layout.preferredWidth: parent.width |
|||
height: 1 |
|||
color: "#cccccc" |
|||
} |
|||
|
|||
Connections |
|||
{ |
|||
target: loader |
|||
onLoaded: |
|||
{ |
|||
watchers.clear() |
|||
blockChain.load(scenario, loader.selectedScenarioIndex) |
|||
} |
|||
} |
|||
|
|||
BlockChain |
|||
{ |
|||
id: blockChain |
|||
width: parent.width |
|||
} |
|||
|
|||
Connections |
|||
{ |
|||
target: blockChain |
|||
property var currentSelectedBlock |
|||
property var currentSelectedTx |
|||
onTxSelected: |
|||
{ |
|||
currentSelectedBlock = blockIndex |
|||
currentSelectedTx = txIndex |
|||
updateWatchers(blockIndex, txIndex) |
|||
} |
|||
|
|||
function updateWatchers(blockIndex, txIndex) |
|||
{ |
|||
var tx = blockChain.model.blocks[blockIndex].transactions[txIndex] |
|||
var state = blockChain.getState(tx.recordIndex) |
|||
watchers.updateWidthTx(tx, state, blockIndex, txIndex) |
|||
} |
|||
|
|||
onRebuilding: { |
|||
watchers.clear() |
|||
} |
|||
|
|||
onAccountAdded: { |
|||
watchers.addAccount(address, "0 wei") |
|||
} |
|||
} |
|||
} |
|||
|
|||
Watchers |
|||
{ |
|||
id: watchers |
|||
bc: blockChain |
|||
Layout.fillWidth: true |
|||
Layout.preferredHeight: 740 |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
color: "transparent" |
|||
Layout.preferredHeight: 50 |
|||
Layout.fillWidth: true |
|||
} |
|||
} |
|||
} |
|||
} |
@ -1,386 +0,0 @@ |
|||
import QtQuick 2.2 |
|||
import QtQuick.Controls 1.1 |
|||
import QtQuick.Controls.Styles 1.1 |
|||
import QtQuick.Layouts 1.1 |
|||
import QtQuick.Dialogs 1.2 |
|||
import QtQuick.Window 2.0 |
|||
import QtQuick.Dialogs 1.1 |
|||
import Qt.labs.settings 1.0 |
|||
import org.ethereum.qml.InverseMouseArea 1.0 |
|||
import "js/Debugger.js" as Debugger |
|||
import "js/ErrorLocationFormater.js" as ErrorLocationFormater |
|||
import "." |
|||
|
|||
ColumnLayout |
|||
{ |
|||
id: blockChainSelector |
|||
signal restored(variant scenario) |
|||
signal saved(variant scenario) |
|||
signal duplicated(variant scenario) |
|||
signal loaded(variant scenario) |
|||
signal renamed(variant scenario) |
|||
signal deleted() |
|||
property alias selectedScenarioIndex: scenarioList.currentIndex |
|||
spacing: 0 |
|||
function init() |
|||
{ |
|||
scenarioList.load() |
|||
} |
|||
|
|||
function needSaveOrReload() |
|||
{ |
|||
} |
|||
|
|||
RowLayout |
|||
{ |
|||
Layout.preferredWidth: 560 |
|||
anchors.horizontalCenter: parent.horizontalCenter |
|||
Layout.preferredHeight: 75 |
|||
spacing: 0 |
|||
anchors.top: parent.top |
|||
anchors.topMargin: 10 |
|||
|
|||
Row |
|||
{ |
|||
Layout.preferredWidth: 100 * 5 + 30 |
|||
Layout.preferredHeight: 50 |
|||
spacing: 25 |
|||
|
|||
Rectangle |
|||
{ |
|||
color: "white" |
|||
width: 251 + 30 |
|||
height: 30 |
|||
|
|||
Rectangle |
|||
{ |
|||
id: left |
|||
width: 10 |
|||
height: parent.height |
|||
anchors.left: parent.left |
|||
anchors.leftMargin: -5 |
|||
radius: 15 |
|||
} |
|||
|
|||
Image { |
|||
source: "qrc:/qml/img/edittransaction.png" |
|||
height: parent.height - 10 |
|||
fillMode: Image.PreserveAspectFit |
|||
anchors.left: parent.left |
|||
anchors.top: parent.top |
|||
anchors.topMargin: 4 |
|||
id: editImg |
|||
MouseArea |
|||
{ |
|||
anchors.fill: parent |
|||
onClicked: |
|||
{ |
|||
scenarioNameEdit.toggleEdit() |
|||
} |
|||
} |
|||
} |
|||
|
|||
Image { |
|||
source: "qrc:/qml/img/delete_sign.png" |
|||
height: parent.height - 16 |
|||
fillMode: Image.PreserveAspectFit |
|||
id: deleteImg |
|||
anchors.left: editImg.right |
|||
anchors.top: parent.top |
|||
anchors.topMargin: 8 |
|||
visible: projectModel.stateListModel.count > 1 |
|||
MouseArea |
|||
{ |
|||
anchors.fill: parent |
|||
onClicked: |
|||
{ |
|||
if (projectModel.stateListModel.count > 1) |
|||
{ |
|||
projectModel.stateListModel.deleteState(scenarioList.currentIndex) |
|||
scenarioList.init() |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
Label |
|||
{ |
|||
|
|||
MouseArea |
|||
{ |
|||
anchors.fill: parent |
|||
onClicked: |
|||
{ |
|||
if (projectModel.stateListModel.count > 1) |
|||
{ |
|||
projectModel.stateListModel.deleteState(scenarioList.currentIndex) |
|||
scenarioList.init() |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
Connections |
|||
{ |
|||
target: projectModel.stateListModel |
|||
onStateDeleted: { |
|||
scenarioList.init() |
|||
} |
|||
} |
|||
|
|||
ComboBox |
|||
{ |
|||
id: scenarioList |
|||
anchors.left: deleteImg.right |
|||
anchors.leftMargin: 2 |
|||
model: projectModel.stateListModel |
|||
anchors.top: parent.top |
|||
textRole: "title" |
|||
height: parent.height |
|||
width: 150 |
|||
signal updateView() |
|||
|
|||
onCurrentIndexChanged: |
|||
{ |
|||
restoreScenario.restore() |
|||
} |
|||
|
|||
function init() |
|||
{ |
|||
scenarioList.currentIndex = 0 |
|||
deleted() |
|||
} |
|||
|
|||
function load() |
|||
{ |
|||
var state = projectModel.stateListModel.getState(currentIndex) |
|||
if (state) |
|||
loaded(state) |
|||
} |
|||
|
|||
style: ComboBoxStyle { |
|||
id: style |
|||
background: Rectangle { |
|||
color: "white" |
|||
anchors.fill: parent |
|||
} |
|||
label: Rectangle { |
|||
property alias label: comboLabel |
|||
anchors.fill: parent |
|||
color: "white" |
|||
Label { |
|||
id: comboLabel |
|||
maximumLineCount: 1 |
|||
elide: Text.ElideRight |
|||
width: parent.width |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
anchors.left: parent.left |
|||
Component.onCompleted: |
|||
{ |
|||
comboLabel.updateLabel() |
|||
} |
|||
|
|||
function updateLabel() |
|||
{ |
|||
comboLabel.text = "" |
|||
if (scenarioList.currentIndex > - 1 && scenarioList.currentIndex < projectModel.stateListModel.count) |
|||
comboLabel.text = projectModel.stateListModel.getState(scenarioList.currentIndex).title |
|||
} |
|||
|
|||
Connections { |
|||
target: blockChainSelector |
|||
onLoaded: { |
|||
if (projectModel.stateListModel.count > 0) |
|||
comboLabel.text = projectModel.stateListModel.getState(scenarioList.currentIndex).title |
|||
else |
|||
return "" |
|||
} |
|||
onRenamed: { |
|||
comboLabel.text = scenario.title |
|||
scenarioNameEdit.text = scenario.title |
|||
} |
|||
onDeleted: { |
|||
comboLabel.updateLabel() |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
TextField |
|||
{ |
|||
id: scenarioNameEdit |
|||
anchors.left: deleteImg.right |
|||
anchors.leftMargin: 2 |
|||
height: parent.height |
|||
z: 5 |
|||
visible: false |
|||
width: 150 |
|||
Keys.onEnterPressed: |
|||
{ |
|||
toggleEdit() |
|||
} |
|||
|
|||
Keys.onReturnPressed: |
|||
{ |
|||
toggleEdit() |
|||
} |
|||
|
|||
function toggleEdit() |
|||
{ |
|||
scenarioList.visible = !scenarioList.visible |
|||
scenarioNameEdit.visible = !scenarioNameEdit.visible |
|||
if (!scenarioNameEdit.visible) |
|||
scenarioNameEdit.save() |
|||
else |
|||
{ |
|||
scenarioNameEdit.text = projectModel.stateListModel.getState(scenarioList.currentIndex).title |
|||
scenarioNameEdit.forceActiveFocus() |
|||
outsideClick.active = true |
|||
} |
|||
} |
|||
|
|||
function save() |
|||
{ |
|||
outsideClick.active = false |
|||
projectModel.stateListModel.getState(scenarioList.currentIndex).title = scenarioNameEdit.text |
|||
projectModel.saveProjectFile() |
|||
saved(state) |
|||
scenarioList.model.get(scenarioList.currentIndex).title = scenarioNameEdit.text |
|||
scenarioList.currentIndex = scenarioList.currentIndex |
|||
renamed(projectModel.stateListModel.getState(scenarioList.currentIndex)) |
|||
} |
|||
|
|||
style: TextFieldStyle { |
|||
background: Rectangle { |
|||
radius: 2 |
|||
implicitWidth: 100 |
|||
implicitHeight: 30 |
|||
color: "white" |
|||
border.color: "#cccccc" |
|||
border.width: 1 |
|||
} |
|||
} |
|||
|
|||
InverseMouseArea { |
|||
id: outsideClick |
|||
anchors.fill: parent |
|||
active: false |
|||
onClickedOutside: { |
|||
scenarioNameEdit.toggleEdit() |
|||
} |
|||
} |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
width: 1 |
|||
height: parent.height |
|||
anchors.right: addScenario.left |
|||
color: "#ededed" |
|||
} |
|||
|
|||
ScenarioButton { |
|||
id: addScenario |
|||
anchors.left: scenarioList.right |
|||
width: 100 |
|||
height: parent.height |
|||
buttonShortcut: "" |
|||
sourceImg: "qrc:/qml/img/restoreicon@2x.png" |
|||
onClicked: { |
|||
var item = projectModel.stateListModel.createDefaultState(); |
|||
item.title = qsTr("New Scenario") |
|||
projectModel.stateListModel.appendState(item) |
|||
projectModel.stateListModel.save() |
|||
scenarioList.currentIndex = projectModel.stateListModel.count - 1 |
|||
clientModel.setupScenario(item); |
|||
} |
|||
text: qsTr("New...") |
|||
roundRight: true |
|||
roundLeft: false |
|||
} |
|||
} |
|||
|
|||
|
|||
Rectangle |
|||
{ |
|||
width: 100 * 3 |
|||
height: 30 |
|||
color: "transparent" |
|||
|
|||
ScenarioButton { |
|||
id: restoreScenario |
|||
width: 100 |
|||
height: parent.height |
|||
buttonShortcut: "" |
|||
sourceImg: "qrc:/qml/img/restoreicon@2x.png" |
|||
onClicked: { |
|||
restore() |
|||
} |
|||
text: qsTr("Restore") |
|||
function restore() |
|||
{ |
|||
var state = projectModel.stateListModel.reloadStateFromProject(scenarioList.currentIndex) |
|||
if (state) |
|||
{ |
|||
restored(state) |
|||
loaded(state) |
|||
} |
|||
} |
|||
roundRight: false |
|||
roundLeft: true |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
width: 1 |
|||
height: parent.height |
|||
anchors.right: saveScenario.left |
|||
color: "#ededed" |
|||
} |
|||
|
|||
ScenarioButton { |
|||
id: saveScenario |
|||
anchors.left: restoreScenario.right |
|||
text: qsTr("Save") |
|||
onClicked: { |
|||
projectModel.saveProjectFile() |
|||
saved(state) |
|||
} |
|||
width: 100 |
|||
height: parent.height |
|||
buttonShortcut: "" |
|||
sourceImg: "qrc:/qml/img/saveicon@2x.png" |
|||
roundRight: false |
|||
roundLeft: false |
|||
} |
|||
|
|||
Rectangle |
|||
{ |
|||
width: 1 |
|||
height: parent.height |
|||
anchors.right: duplicateScenario.left |
|||
color: "#ededed" |
|||
} |
|||
|
|||
ScenarioButton |
|||
{ |
|||
id: duplicateScenario |
|||
anchors.left: saveScenario.right |
|||
text: qsTr("Duplicate") |
|||
onClicked: { |
|||
projectModel.stateListModel.duplicateState(scenarioList.currentIndex) |
|||
duplicated(state) |
|||
} |
|||
width: 100 |
|||
height: parent.height |
|||
buttonShortcut: "" |
|||
sourceImg: "qrc:/qml/img/duplicateicon@2x.png" |
|||
roundRight: true |
|||
roundLeft: false |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue