12 changed files with 431 additions and 15 deletions
@ -0,0 +1,68 @@ |
|||
/*
|
|||
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 <stdexcept> |
|||
#include <QDir> |
|||
#include <QFile> |
|||
#include <QFileInfo> |
|||
#include <QTextStream> |
|||
#include "FileIo.h" |
|||
|
|||
using namespace dev::mix; |
|||
|
|||
void FileIo::makeDir(QString const& _path) |
|||
{ |
|||
QDir dirPath(_path); |
|||
if (!dirPath.exists()) |
|||
dirPath.mkpath(dirPath.path()); |
|||
} |
|||
|
|||
QString FileIo::readFile(QString const& _path) |
|||
{ |
|||
QFile file(_path); |
|||
if(file.open(QIODevice::ReadOnly | QIODevice::Text)) |
|||
{ |
|||
QTextStream stream(&file); |
|||
QString data = stream.readAll(); |
|||
return data; |
|||
} |
|||
else |
|||
throw std::runtime_error(tr("Error reading file %1").arg(_path).toStdString()); |
|||
} |
|||
|
|||
void FileIo::writeFile(QString const& _path, QString const& _data) |
|||
{ |
|||
QFile file(_path); |
|||
if(file.open(QIODevice::WriteOnly | QIODevice::Text)) |
|||
{ |
|||
QTextStream stream(&file); |
|||
stream << _data; |
|||
} |
|||
else |
|||
throw std::runtime_error(tr("Error writing file %1").arg(_path).toStdString()); |
|||
} |
|||
|
|||
void FileIo::copyFile(QString const& _sourcePath, QString const& _destPath) |
|||
{ |
|||
if (!QFile::copy(_sourcePath, _destPath)) |
|||
throw std::runtime_error(tr("Error copying file %1 to %2").arg(_sourcePath).arg(_destPath).toStdString()); |
|||
} |
@ -0,0 +1,49 @@ |
|||
/*
|
|||
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 <QObject> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace mix |
|||
{ |
|||
|
|||
///File services for QML
|
|||
class FileIo : public QObject |
|||
{ |
|||
Q_OBJECT |
|||
|
|||
public: |
|||
/// Create a directory if it does not exist. Throws on failure.
|
|||
Q_INVOKABLE void makeDir(QString const& _path); |
|||
/// Read file contents to a string. Throws on failure.
|
|||
Q_INVOKABLE QString readFile(QString const& _path); |
|||
/// Write contents to a file. Throws on failure.
|
|||
Q_INVOKABLE void writeFile(QString const& _path, QString const& _data); |
|||
/// Copy a file from _sourcePath to _destPath. Throws on failure.
|
|||
Q_INVOKABLE void copyFile(QString const& _sourcePath, QString const& _destPath); |
|||
}; |
|||
|
|||
} |
|||
} |
@ -1,16 +1,19 @@ |
|||
<RCC> |
|||
<qresource prefix="/"> |
|||
<file>qml/BasicContent.qml</file> |
|||
<file>qml/main.qml</file> |
|||
<file>qml/MainContent.qml</file> |
|||
<file>qml/TabStyle.qml</file> |
|||
<file>qml/Debugger.qml</file> |
|||
<file>qml/js/Debugger.js</file> |
|||
<file>qml/AlertMessageDialog.qml</file> |
|||
<file>qml/BasicContent.qml</file> |
|||
<file>qml/BasicMessage.qml</file> |
|||
<file>qml/TransactionDialog.qml</file> |
|||
<file>qml/Debugger.qml</file> |
|||
<file>qml/MainContent.qml</file> |
|||
<file>qml/ModalDialog.qml</file> |
|||
<file>qml/AlertMessageDialog.qml</file> |
|||
<file>qml/ProjectList.qml</file> |
|||
<file>qml/StateDialog.qml</file> |
|||
<file>qml/StateList.qml</file> |
|||
<file>qml/TabStyle.qml</file> |
|||
<file>qml/TransactionDialog.qml</file> |
|||
<file>qml/js/Debugger.js</file> |
|||
<file>qml/NewProjectDialog.qml</file> |
|||
<file>qml/ProjectModel.qml</file> |
|||
</qresource> |
|||
</RCC> |
|||
|
@ -0,0 +1,92 @@ |
|||
import QtQuick 2.2 |
|||
import QtQuick.Controls 1.2 |
|||
import QtQuick.Layouts 1.1 |
|||
import QtQuick.Window 2.0 |
|||
import QtQuick.Dialogs 1.2 |
|||
|
|||
Window { |
|||
|
|||
modality: Qt.WindowModal |
|||
|
|||
width: 640 |
|||
height: 280 |
|||
|
|||
visible: false |
|||
|
|||
property alias projectTitle : titleField.text |
|||
property alias projectPath : pathField.text |
|||
signal accepted |
|||
|
|||
function open() { |
|||
visible = true; |
|||
titleField.focus = true; |
|||
} |
|||
|
|||
function close() { |
|||
visible = false; |
|||
} |
|||
|
|||
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 |
|||
} |
|||
|
|||
Label { |
|||
text: qsTr("Path") |
|||
} |
|||
RowLayout { |
|||
TextField { |
|||
id: pathField |
|||
Layout.fillWidth: true |
|||
} |
|||
Button { |
|||
text: qsTr("Browse") |
|||
onClicked: createProjectFileDialog.open() |
|||
} |
|||
} |
|||
|
|||
RowLayout |
|||
{ |
|||
anchors.bottom: parent.bottom |
|||
anchors.right: parent.right; |
|||
|
|||
Button { |
|||
enabled: titleField.text != "" && pathField.text != "" |
|||
text: qsTr("Ok"); |
|||
onClicked: { |
|||
close(); |
|||
accepted(); |
|||
} |
|||
} |
|||
Button { |
|||
text: qsTr("Cancel"); |
|||
onClicked: close(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
FileDialog { |
|||
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) |
|||
pathField.text = u; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,39 @@ |
|||
import QtQuick 2.0 |
|||
import QtQuick.Window 2.0 |
|||
import QtQuick.Layouts 1.0 |
|||
import QtQuick.Controls 1.0 |
|||
import org.ethereum.qml.ProjectModel 1.0 |
|||
|
|||
Item { |
|||
ListView { |
|||
model: ProjectModel.listModel |
|||
delegate: renderDelegate |
|||
} |
|||
|
|||
Component { |
|||
id: renderDelegate |
|||
Item { |
|||
id: wrapperItem |
|||
height: 20 |
|||
width: parent.width |
|||
RowLayout { |
|||
anchors.fill: parent |
|||
Text { |
|||
Layout.fillWidth: true |
|||
Layout.fillHeight: true |
|||
text: title |
|||
font.pointSize: 12 |
|||
verticalAlignment: Text.AlignBottom |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
Action { |
|||
id: createProjectAction |
|||
text: qsTr("&New project") |
|||
shortcut: "Ctrl+N" |
|||
enabled: true; |
|||
onTriggered: ProjectModel.createProject(); |
|||
} |
|||
} |
@ -0,0 +1,140 @@ |
|||
pragma Singleton |
|||
|
|||
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: projectModel |
|||
|
|||
signal projectClosed |
|||
signal projectLoaded |
|||
|
|||
property bool isEmpty: projectFile === "" |
|||
readonly property string projectFileName: ".mix" |
|||
|
|||
property bool haveUnsavedChanges: false |
|||
property string projectFile: "" |
|||
property var projectData: null |
|||
property var listModel: projectListModel |
|||
|
|||
function saveAll() { |
|||
saveProject(); |
|||
} |
|||
|
|||
function createProject() { |
|||
closeProject(); |
|||
newProjectDialog.open(); |
|||
} |
|||
|
|||
function closeProject() { |
|||
console.log("closing project"); |
|||
if (haveUnsavedChanges) |
|||
saveMessageDialog.open(); |
|||
else |
|||
doCloseProject(); |
|||
} |
|||
|
|||
function saveProject() { |
|||
if (!isEmpty) { |
|||
var json = JSON.stringify(projectData); |
|||
fileIo.writeFile(projectFile, json) |
|||
} |
|||
} |
|||
|
|||
function loadProject(path) { |
|||
if (!isEmpty) |
|||
closeProject(); |
|||
console.log("loading project at " + path); |
|||
var json = fileIo.readFile(path); |
|||
projectData = JSON.parse(json); |
|||
projectFile = path; |
|||
if (!projectData.files) |
|||
projectData.files = []; |
|||
|
|||
for(var i = 0; i < projectData.files; i++) { |
|||
var p = projectData.files[i]; |
|||
projectListModel.append({ |
|||
path: p, |
|||
name: p.substring(p.lastIndexOf("/") + 1, p.length) |
|||
}); |
|||
} |
|||
onProjectLoaded(); |
|||
} |
|||
|
|||
function doCloseProject() { |
|||
projectListModel.clear(); |
|||
projectFile = ""; |
|||
projectData = null; |
|||
projectClosed(); |
|||
} |
|||
|
|||
function doCreateProject(title, path) { |
|||
if (!isEmpty) |
|||
closeProject(); |
|||
console.log("creating project " + title + " at " + path); |
|||
if (path[path.length - 1] !== "/") |
|||
path += "/"; |
|||
var dirPath = path + title; |
|||
fileIo.makeDir(dirPath); |
|||
var projectFile = dirPath + "/" + projectFileName; |
|||
fileIo.writeFile(projectFile, ""); |
|||
loadProject(projectFile); |
|||
} |
|||
|
|||
NewProjectDialog { |
|||
id: newProjectDialog |
|||
visible: false |
|||
onAccepted: { |
|||
var title = newProjectDialog.projectTitle; |
|||
var path = newProjectDialog.projectPath; |
|||
projectModel.doCreateProject(title, path); |
|||
} |
|||
} |
|||
|
|||
MessageDialog { |
|||
id: saveMessageDialog |
|||
title: qsTr("Project") |
|||
text: qsTr("Do you want to save changes?") |
|||
standardButtons: StandardButton.Ok | StandardButton.Cancel |
|||
icon: StandardIcon.Question |
|||
onAccepted: { |
|||
projectModel.saveAll(); |
|||
projectModel.doCloseProject(); |
|||
} |
|||
onRejected: { |
|||
projectModel.doCloseProject(); |
|||
} |
|||
} |
|||
|
|||
ListModel { |
|||
id: projectListModel |
|||
} |
|||
|
|||
Component { |
|||
id: renderDelegate |
|||
Item { |
|||
id: wrapperItem |
|||
height: 20 |
|||
width: parent.width |
|||
RowLayout { |
|||
anchors.fill: parent |
|||
Text { |
|||
Layout.fillWidth: true |
|||
Layout.fillHeight: true |
|||
text: title |
|||
font.pointSize: 12 |
|||
verticalAlignment: Text.AlignBottom |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
Settings { |
|||
id: projectSettings |
|||
property string lastProjectPath; |
|||
} |
|||
} |
Loading…
Reference in new issue