diff --git a/mix/AssemblyDebuggerCtrl.cpp b/mix/AssemblyDebuggerCtrl.cpp index 36318ccb7..87bbb83f0 100644 --- a/mix/AssemblyDebuggerCtrl.cpp +++ b/mix/AssemblyDebuggerCtrl.cpp @@ -78,10 +78,10 @@ void AssemblyDebuggerCtrl::keyPressed(int _key) //we need to wrap states in a QObject before sending to QML. QList wStates; - for(int i = 0; i < debuggingContent.states.size(); i++) + for (int i = 0; i < debuggingContent.machineStates.size(); i++) { DebuggingStateWrapper* s = new DebuggingStateWrapper(debuggingContent.executionCode, debuggingContent.executionData.toBytes(), this); - s->setState(debuggingContent.states.at(i)); + s->setState(debuggingContent.machineStates.at(i)); wStates.append(s); } AssemblyDebuggerData code = DebuggingStateWrapper::getHumanReadableCode(debuggingContent.executionCode, this); diff --git a/mix/AssemblyDebuggerModel.cpp b/mix/AssemblyDebuggerModel.cpp index bcf58835c..01ef1f4a7 100644 --- a/mix/AssemblyDebuggerModel.cpp +++ b/mix/AssemblyDebuggerModel.cpp @@ -38,7 +38,7 @@ AssemblyDebuggerModel::AssemblyDebuggerModel() DebuggingContent AssemblyDebuggerModel::getContractInitiationDebugStates(dev::bytesConstRef _rawTransaction) { - QList states; + QList machineStates; Transaction tr(_rawTransaction); m_currentExecution->create(tr.sender(), tr.value(), tr.gasPrice(), tr.gas(), &tr.data(), tr.sender()); std::vector levels; @@ -58,11 +58,11 @@ DebuggingContent AssemblyDebuggerModel::getContractInitiationDebugStates(dev::by } if (levels.size() < ext.depth) - levels.push_back(&states.back()); + levels.push_back(&machineStates.back()); else levels.resize(ext.depth); - states.append(DebuggingState({steps, ext.myAddress, vm.curPC(), inst, newMemSize, vm.gas(), + machineStates.append(DebuggingState({steps, ext.myAddress, vm.curPC(), inst, newMemSize, vm.gas(), vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels})); }; @@ -70,7 +70,7 @@ DebuggingContent AssemblyDebuggerModel::getContractInitiationDebugStates(dev::by m_currentExecution->finalize(onOp); DebuggingContent d; - d.states = states; + d.machineStates = machineStates; d.executionCode = code; d.executionData = data; d.contentAvailable = true; diff --git a/mix/CodeEditorExtensionManager.cpp b/mix/CodeEditorExtensionManager.cpp index 83298e0d1..4859c84e1 100644 --- a/mix/CodeEditorExtensionManager.cpp +++ b/mix/CodeEditorExtensionManager.cpp @@ -49,7 +49,17 @@ void CodeEditorExtensionManager::loadEditor(QQuickItem* _editor) { QQuickTextDocument* qqdoc = doc.value(); if (qqdoc) - m_doc = qqdoc->textDocument(); + { + m_doc = qqdoc->textDocument(); + auto args = QApplication::arguments(); + if (args.length() > 1) + { + QString path = args[1]; + QFile file(path); + if (file.exists() && file.open(QFile::ReadOnly)) + m_doc->setPlainText(file.readAll()); + } + } } } catch (...) diff --git a/mix/DebuggingStateWrapper.cpp b/mix/DebuggingStateWrapper.cpp index 2315cd5a8..d83506dae 100644 --- a/mix/DebuggingStateWrapper.cpp +++ b/mix/DebuggingStateWrapper.cpp @@ -44,7 +44,7 @@ std::tuple, QQMLMap*> DebuggingStateWrapper::getHumanReadableCod { QString s = QString::fromStdString(instructionInfo((Instruction)b).name); std::ostringstream out; - out << hex << std::setw(4) << std::setfill('0') << i; + out << std::hex << std::setw(4) << std::setfill('0') << i; codeMapping[i] = codeStr.size(); int line = i; if (b >= (byte)Instruction::PUSH1 && b <= (byte)Instruction::PUSH32) @@ -103,7 +103,7 @@ QStringList DebuggingStateWrapper::levels() std::ostringstream out; out << m_state.cur.abridged(); if (i) - out << " " << instructionInfo(m_state.inst).name << " @0x" << hex << m_state.curPC; + out << " " << instructionInfo(m_state.inst).name << " @0x" << std::hex << m_state.curPC; levelsStr.append(QString::fromStdString(out.str())); } return levelsStr; @@ -112,7 +112,7 @@ QStringList DebuggingStateWrapper::levels() QString DebuggingStateWrapper::headerInfo() { std::ostringstream ss; - ss << dec << " STEP: " << m_state.steps << " | PC: 0x" << hex << m_state.curPC << " : " << dev::eth::instructionInfo(m_state.inst).name << " | ADDMEM: " << dec << m_state.newMemSize << " words | COST: " << dec << m_state.gasCost << " | GAS: " << dec << m_state.gas; + ss << std::dec << " STEP: " << m_state.steps << " | PC: 0x" << std::hex << m_state.curPC << " : " << dev::eth::instructionInfo(m_state.inst).name << " | ADDMEM: " << std::dec << m_state.newMemSize << " words | COST: " << std::dec << m_state.gasCost << " | GAS: " << std::dec << m_state.gas; return QString::fromStdString(ss.str()); } diff --git a/mix/DebuggingStateWrapper.h b/mix/DebuggingStateWrapper.h index 09089fe58..f7ea900b5 100644 --- a/mix/DebuggingStateWrapper.h +++ b/mix/DebuggingStateWrapper.h @@ -49,7 +49,7 @@ struct DebuggingState struct DebuggingContent { - QList states; + QList machineStates; bytes executionCode; bytesConstRef executionData; bool contentAvailable; diff --git a/mix/QBasicNodeDefinition.h b/mix/QBasicNodeDefinition.h index 5b4cc6c9a..2357fc870 100644 --- a/mix/QBasicNodeDefinition.h +++ b/mix/QBasicNodeDefinition.h @@ -38,7 +38,7 @@ public: QBasicNodeDefinition(): QObject() {} ~QBasicNodeDefinition() {} QBasicNodeDefinition(std::shared_ptr _d, QObject* _parent): QObject(_parent), m_dec(_d) {} - QString name() { return QString::fromStdString(m_dec.get()->getName()); } + QString name() const { return QString::fromStdString(m_dec.get()->getName()); } protected: std::shared_ptr m_dec; diff --git a/mix/QFunctionDefinition.h b/mix/QFunctionDefinition.h index 4db296348..280ef0fe0 100644 --- a/mix/QFunctionDefinition.h +++ b/mix/QFunctionDefinition.h @@ -38,9 +38,9 @@ class QFunctionDefinition: public QBasicNodeDefinition public: QFunctionDefinition(std::shared_ptr _f, QObject* _parent, int _index): QBasicNodeDefinition(_f, _parent), m_index(_index) { initQParameters(); } - QList parameters() { return m_parameters; } - QList returnParameters() { return m_returnParameters; } - int index() { return m_index; } + QList parameters() const { return m_parameters; } + QList returnParameters() const { return m_returnParameters; } + int index() const { return m_index; } private: QList m_parameters; diff --git a/mix/QVariableDeclaration.h b/mix/QVariableDeclaration.h index c9aa0d6cd..fb161c96b 100644 --- a/mix/QVariableDeclaration.h +++ b/mix/QVariableDeclaration.h @@ -36,7 +36,7 @@ class QVariableDeclaration: public QBasicNodeDefinition public: QVariableDeclaration(std::shared_ptr _v, QObject* _parent): QBasicNodeDefinition(_v, _parent), m_var(_v) {} - QString type() { return QString::fromStdString(m_var.get()->getType()->toString()); } + QString type() const { return QString::fromStdString(m_var.get()->getType()->toString()); } private: std::shared_ptr m_var; diff --git a/mix/TransactionListModel.cpp b/mix/TransactionListModel.cpp index b4cbfc83c..fad530d5e 100644 --- a/mix/TransactionListModel.cpp +++ b/mix/TransactionListModel.cpp @@ -22,23 +22,45 @@ #include #include +#include +#include #include "TransactionListModel.h" +#include "QContractDefinition.h" +#include "QFunctionDefinition.h" +#include "QVariableDeclaration.h" +#include "libdevcore/CommonJS.h" namespace dev { namespace mix { -TransactionListModel::TransactionListModel(QObject* _parent) : - QAbstractListModel(_parent) + +u256 fromQString(QString const& _s) +{ + return dev::jsToU256(_s.toStdString()); +} + +QString toQString(u256 _value) { - m_transactions.push_back(Transaction(0, "testTr", 0, 0, 0)); + std::ostringstream s; + s << _value; + return QString::fromStdString(s.str()); } +TransactionListItem::TransactionListItem(int _index, Transaction const& _t, QObject* _parent): + QObject(_parent), m_index(_index), m_title(_t.title), m_functionId(_t.functionId), m_value(toQString(_t.value)), + m_gas(toQString(_t.gas)), m_gasPrice(toQString(_t.gasPrice)) +{} + +TransactionListModel::TransactionListModel(QObject* _parent, QTextDocument* _document): + QAbstractListModel(_parent), m_document(_document) +{} + QHash TransactionListModel::roleNames() const { QHash roles; roles[TitleRole] = "title"; - roles[IdRole] = "transactionId"; + roles[IdRole] = "transactionIndex"; return roles; } @@ -48,26 +70,98 @@ int TransactionListModel::rowCount(QModelIndex const& _parent) const return m_transactions.size(); } -QVariant TransactionListModel::data(QModelIndex const& _index, int role) const +QVariant TransactionListModel::data(QModelIndex const& _index, int _role) const { if(_index.row() < 0 || _index.row() >= (int)m_transactions.size()) return QVariant(); auto const& transaction = m_transactions.at(_index.row()); - switch(role) + switch(_role) { case TitleRole: return QVariant(transaction.title); case IdRole: - return QVariant(transaction.id); + return QVariant(_index.row()); default: return QVariant(); } } +//TODO: get parameters from code model +QList buildParameters(QTextDocument* _document, Transaction const& _transaction, QString const& _functionId) +{ + QList params; + try + { + QString code = _document->toPlainText().replace("\n", ""); //TODO: is this required? + std::unique_ptr contract(QContractDefinition::Contract(code, nullptr)); + auto functions = contract->functions(); + for(auto qf : functions) + { + QFunctionDefinition const& f = (QFunctionDefinition const&) *qf; + if (f.name() != _functionId) + continue; + + auto parameters = f.parameters(); + for(auto qp : parameters) + { + QVariableDeclaration const& p = (QVariableDeclaration const&) *qp; + QString paramValue; + if (f.name() == _transaction.functionId) + { + auto paramValueIter = _transaction.parameterValues.find(p.name()); + if (paramValueIter != _transaction.parameterValues.cend()) + paramValue = toQString(paramValueIter->second); + } + + TransactionParameterItem* item = new TransactionParameterItem(p.name(), p.type(), paramValue); + QQmlEngine::setObjectOwnership(item, QQmlEngine::JavaScriptOwnership); + params.append(item); + } + } + } + catch (boost::exception const&) + { + //TODO: + } + + return params; +} + +//TODO: get fnctions from code model +QList TransactionListModel::getFunctions() +{ + QList functionNames; + try + { + QString code = m_document->toPlainText().replace("\n", ""); //TODO: is this required? + std::unique_ptr contract(QContractDefinition::Contract(code, nullptr)); + auto functions = contract->functions(); + for(auto qf : functions) + { + QFunctionDefinition const& f = (QFunctionDefinition const&) *qf; + functionNames.append(f.name()); + } + } + catch (boost::exception const&) + { + } + return functionNames; +} + +QVariantList TransactionListModel::getParameters(int _index, QString const& _functionId) +{ + Transaction const& transaction = (_index >=0 && _index < (int)m_transactions.size()) ? m_transactions[_index] : Transaction(); + auto plist = buildParameters(m_document, transaction, _functionId); + QVariantList vl; + for(QObject* p : plist) + vl.append(QVariant::fromValue(p)); + return vl; +} + QObject* TransactionListModel::getItem(int _index) { Transaction const& transaction = (_index >=0 && _index < (int)m_transactions.size()) ? m_transactions[_index] : Transaction(); - QObject* item = new TransactionListItem(transaction, nullptr); + TransactionListItem* item = new TransactionListItem(_index, transaction, nullptr); QQmlEngine::setObjectOwnership(item, QQmlEngine::JavaScriptOwnership); return item; } @@ -75,21 +169,34 @@ QObject* TransactionListModel::getItem(int _index) void TransactionListModel::edit(QObject* _data) { //these properties come from TransactionDialog QML object - int id = _data->property("transactionId").toInt(); - const QString title = _data->property("transactionTitle").toString(); - + int index = _data->property("transactionIndex").toInt(); + QString title = _data->property("transactionTitle").toString(); + QString gas = _data->property("gas").toString(); + QString gasPrice = _data->property("gasPrice").toString(); + QString value = _data->property("transactionValue").toString(); + QString functionId = _data->property("functionId").toString(); + QAbstractListModel* paramsModel = qvariant_cast(_data->property("transactionParams")); + Transaction transaction(title, functionId, fromQString(value), fromQString(gas), fromQString(gasPrice)); + int paramCount = paramsModel->rowCount(QModelIndex()); + for(int p = 0; p < paramCount; ++p) + { + QString paramName = paramsModel->data(paramsModel->index(p, 0), Qt::DisplayRole).toString(); + QString paramValue = paramsModel->data(paramsModel->index(p, 0), Qt::DisplayRole + 2).toString(); + if (!paramValue.isEmpty() && !paramName.isEmpty()) + transaction.parameterValues[paramName] = fromQString(paramValue); + } - if (id >= 0 && id < (int)m_transactions.size()) + if (index >= 0 && index < (int)m_transactions.size()) { - beginRemoveRows(QModelIndex(), id, id); - m_transactions.erase(m_transactions.begin() + id); + beginRemoveRows(QModelIndex(), index, index); + m_transactions.erase(m_transactions.begin() + index); endRemoveRows(); } else - id = rowCount(QModelIndex()); + index = rowCount(QModelIndex()); - beginInsertRows(QModelIndex(), id, id); - m_transactions.push_back(Transaction(id, title, 0, 0, 0)); + beginInsertRows(QModelIndex(), index, index); + m_transactions.push_back(transaction); emit transactionAdded(); emit countChanged(); endInsertRows(); diff --git a/mix/TransactionListModel.h b/mix/TransactionListModel.h index 10bb3c61b..469f40636 100644 --- a/mix/TransactionListModel.h +++ b/mix/TransactionListModel.h @@ -29,54 +29,96 @@ #include #include +class QTextDocument; + namespace dev { namespace mix { -struct TransacionParameterValue -{ - QVariant value; -}; - +/// Backend transaction config class struct Transaction { Transaction(): - id(-1), value(0), gas(10000), gasPrice(10) {} + value(0), gas(10000), gasPrice(10) {} - Transaction(int _id, QString const& _title, u256 _value, u256 _gas, u256 _gasPrice): - id(_id), title(_title), value(_value), gas(_gas), gasPrice(_gasPrice) {} + Transaction(QString const& _title, QString const& _functionId, u256 _value, u256 _gas, u256 _gasPrice): + title(_title), functionId(_functionId), value(_value), gas(_gas), gasPrice(_gasPrice) {} - int id; + /// User specified transaction title QString title; + /// Contract function name + QString functionId; + /// Transaction value u256 value; + /// Gas u256 gas; + /// Gas price u256 gasPrice; - QString functionId; - std::vector parameterValues; + /// Mapping from contract function parameter name to value + std::map parameterValues; +}; + +/// QML transaction parameter class +class TransactionParameterItem: public QObject +{ + Q_OBJECT + Q_PROPERTY(QString name READ name CONSTANT) + Q_PROPERTY(QString type READ type CONSTANT) + Q_PROPERTY(QString value READ value CONSTANT) +public: + TransactionParameterItem(QString const& _name, QString const& _type, QString const& _value): + m_name(_name), m_type(_type), m_value(_value) {} + + /// Parameter name, set by contract definition + QString name() { return m_name; } + /// Parameter type, set by contract definition + QString type() { return m_type; } + /// Parameter value, set by user + QString value() { return m_value; } + +private: + QString m_name; + QString m_type; + QString m_value; }; class TransactionListItem: public QObject { Q_OBJECT - Q_PROPERTY(int transactionId READ transactionId CONSTANT) + Q_PROPERTY(int index READ index CONSTANT) Q_PROPERTY(QString title READ title CONSTANT) - Q_PROPERTY(bool selected READ selected CONSTANT) + Q_PROPERTY(QString functionId READ functionId CONSTANT) + Q_PROPERTY(QString gas READ gas CONSTANT) + Q_PROPERTY(QString gasPrice READ gasPrice CONSTANT) + Q_PROPERTY(QString value READ value CONSTANT) public: - TransactionListItem(Transaction const& _t, QObject* _parent): - QObject(_parent), m_id(_t.id), m_title(_t.title), m_selected(false) {} + TransactionListItem(int _index, Transaction const& _t, QObject* _parent); + + /// User specified transaction title QString title() { return m_title; } - int transactionId() { return m_id; } - bool selected() { return m_selected; } + /// Gas + QString gas() { return m_gas; } + /// Gas cost + QString gasPrice() { return m_gasPrice; } + /// Transaction value + QString value() { return m_value; } + /// Contract function name + QString functionId() { return m_functionId; } + /// Index of this transaction in the transactions list + int index() { return m_index; } private: - int m_id; + int m_index; QString m_title; - bool m_selected; + QString m_functionId; + QString m_value; + QString m_gas; + QString m_gasPrice; }; - +/// QML model for a list of transactions class TransactionListModel: public QAbstractListModel { Q_OBJECT @@ -89,15 +131,22 @@ enum Roles }; public: - TransactionListModel(QObject* _parent); + TransactionListModel(QObject* _parent, QTextDocument* _document); ~TransactionListModel() {} QHash roleNames() const override; int rowCount(QModelIndex const& _parent) const override; QVariant data(QModelIndex const& _index, int _role) const override; int getCount() const; + /// Apply changes from transaction dialog. Argument is a dialog model as defined in TransactionDialog.qml + /// @todo Change that to transaction item Q_INVOKABLE void edit(QObject* _data); + /// @returns transaction item for a give index Q_INVOKABLE QObject* getItem(int _index); + /// @returns a list of functions for current contract + Q_INVOKABLE QList getFunctions(); + /// @returns function parameters along with parameter values if set. @see TransactionParameterItem + Q_INVOKABLE QVariantList getParameters(int _id, QString const& _functionId); signals: void transactionAdded(); @@ -105,8 +154,10 @@ signals: private: std::vector m_transactions; + QTextDocument* m_document; }; } } + diff --git a/mix/TransactionListView.cpp b/mix/TransactionListView.cpp index 0c5a4913e..c1ff88c4b 100644 --- a/mix/TransactionListView.cpp +++ b/mix/TransactionListView.cpp @@ -32,7 +32,7 @@ using namespace dev::mix; TransactionListView::TransactionListView(QTextDocument* _doc): Extension(ExtensionDisplayBehavior::RightTab) { m_editor = _doc; - m_model.reset(new TransactionListModel(this)); + m_model.reset(new TransactionListModel(this, _doc)); m_appEngine->rootContext()->setContextProperty("transactionListModel", m_model.get()); } diff --git a/mix/TransactionListView.h b/mix/TransactionListView.h index cfb1fe1eb..785ad963c 100644 --- a/mix/TransactionListView.h +++ b/mix/TransactionListView.h @@ -30,6 +30,8 @@ namespace mix class TransactionListModel; +/// Transactions list control +/// @todo This should be moved into state as a sequence class TransactionListView: public Extension { Q_OBJECT diff --git a/mix/qml/BasicContent.qml b/mix/qml/BasicContent.qml index ea1017186..ff31dc86f 100644 --- a/mix/qml/BasicContent.qml +++ b/mix/qml/BasicContent.qml @@ -7,13 +7,13 @@ Rectangle { height: parent.height color: "lightgray" Text { - font.pointSize: 7 + font.pointSize: 9 anchors.left: parent.left anchors.top: parent.top anchors.topMargin: 3 anchors.leftMargin: 3 height: 9 - font.family: "Sego UI light" + font.family: "Monospace" objectName: "status" id: status } @@ -23,8 +23,8 @@ Rectangle { anchors.leftMargin: 10 anchors.top: status.bottom anchors.topMargin: 3 - font.pointSize: 7 - font.family: "Sego UI light" + font.pointSize: 9 + font.family: "Monospace" height: parent.height * 0.8 width: parent.width - 20 wrapMode: Text.Wrap diff --git a/mix/qml/MainContent.qml b/mix/qml/MainContent.qml index a154493e0..2d9dec7a7 100644 --- a/mix/qml/MainContent.qml +++ b/mix/qml/MainContent.qml @@ -32,8 +32,8 @@ Rectangle { TextArea { id: codeEditor height: parent.height - font.family: "Verdana" - font.pointSize: 9 + font.family: "Monospace" + font.pointSize: 12 width: parent.width anchors.centerIn: parent tabChangesFocus: false diff --git a/mix/qml/TransactionDialog.qml b/mix/qml/TransactionDialog.qml index 58d071f9d..6cab49b6c 100644 --- a/mix/qml/TransactionDialog.qml +++ b/mix/qml/TransactionDialog.qml @@ -13,13 +13,44 @@ Dialog { property alias focus : titleField.focus property alias transactionTitle : titleField.text - property int transactionId - property int transactionParams; - - function reset(id, model) { - var item = model.getItem(id); - transactionId = id; + property int transactionIndex + property alias transactionParams : paramsModel; + property alias gas : gasField.text; + property alias gasPrice : gasPriceField.text; + property alias transactionValue : valueField.text; + property alias functionId : functionComboBox.currentText; + property var model; + + function reset(index, m) { + model = m; + var item = model.getItem(index); + transactionIndex = index; transactionTitle = item.title; + gas = item.gas; + gasPrice = item.gasPrice; + transactionValue = item.value; + var functionId = item.functionId; + functionsModel.clear(); + var functionIndex = -1; + var functions = model.getFunctions(); + for (var f = 0; f < functions.length; f++) { + functionsModel.append({ text: functions[f] }); + if (functions[f] === item.functionId) + functionIndex = f; + } + functionComboBox.currentIndex = functionIndex; + } + + function loadParameters() { + if (!paramsModel) + return; + paramsModel.clear(); + if (functionComboBox.currentIndex >= 0 && functionComboBox.currentIndex < functionsModel.count) { + var parameters = model.getParameters(transactionIndex, functionsModel.get(functionComboBox.currentIndex).text); + for (var p = 0; p < parameters.length; p++) { + paramsModel.append({ name: parameters[p].name, type: parameters[p].type, value: parameters[p].value }); + } + } } GridLayout { @@ -41,11 +72,20 @@ Dialog { Label { text: qsTr("Function") } - TextField { - id: functionField + + ComboBox { + id: functionComboBox Layout.fillWidth: true + currentIndex: -1 + textRole: "text" + editable: false + model: ListModel { + id: functionsModel + } + onCurrentIndexChanged: { + loadParameters(); + } } - Label { text: qsTr("Value") } @@ -97,14 +137,9 @@ Dialog { return editableDelegate; } } - } ListModel { id: paramsModel - Component.onCompleted: { - for (var i=0 ; i < 3 ; ++i) - paramsModel.append({"name":"Param " + i , "Type": "int", "value": i}) - } } Component { @@ -127,14 +162,11 @@ Dialog { anchors.margins: 4 Connections { target: loaderEditor.item - onAccepted: { - //if (typeof styleData.value === 'number') - // paramsModel.setProperty(styleData.row, styleData.role, Number(parseFloat(loaderEditor.item.text).toFixed(0))) - //else - // paramsModel.setProperty(styleData.row, styleData.role, loaderEditor.item.text) + onTextChanged: { + paramsModel.setProperty(styleData.row, styleData.role, loaderEditor.item.text); } } - sourceComponent: styleData.selected ? editor : null + sourceComponent: (styleData.selected /*&& styleData.role === "value"*/) ? editor : null Component { id: editor TextInput { diff --git a/mix/qml/TransactionList.qml b/mix/qml/TransactionList.qml index 3d6e2840d..5acb356ae 100644 --- a/mix/qml/TransactionList.qml +++ b/mix/qml/TransactionList.qml @@ -65,7 +65,7 @@ Rectangle { text: qsTr("Edit"); Layout.fillHeight: true onClicked: { - transactionDialog.reset(transactionId, transactionListModel); + transactionDialog.reset(index, transactionListModel); transactionDialog.open(); transactionDialog.focus = true; }