dlangide/src/dlangide/ui/newfile.d

316 lines
12 KiB
D

module dlangide.ui.newfile;
import dlangui.core.types;
import dlangui.core.i18n;
import dlangui.platforms.common.platform;
import dlangui.dialogs.dialog;
import dlangui.dialogs.filedlg;
import dlangui.widgets.widget;
import dlangui.widgets.layouts;
import dlangui.widgets.editors;
import dlangui.widgets.controls;
import dlangui.widgets.lists;
import dlangui.dml.parser;
import dlangui.core.stdaction;
import dlangui.core.files;
import dlangide.workspace.project;
import dlangide.workspace.workspace;
import dlangide.ui.commands;
import dlangide.ui.frame;
import std.algorithm : startsWith, endsWith, equal;
import std.array : empty;
import std.utf : toUTF32;
import std.file;
import std.path;
class FileCreationResult {
Project project;
string filename;
this(Project project, string filename) {
this.project = project;
this.filename = filename;
}
}
class NewFileDlg : Dialog {
IDEFrame _ide;
Project _project;
ProjectFolder _folder;
string[] _sourcePaths;
this(IDEFrame parent, Project currentProject, ProjectFolder folder) {
super(UIString.fromRaw("New source file"d), parent.window,
DialogFlag.Modal | DialogFlag.Resizable | DialogFlag.Popup, 500, 400);
_ide = parent;
_icon = "dlangui-logo1";
this._project = currentProject;
this._folder = folder;
_location = folder ? folder.filename : currentProject.dir;
_sourcePaths = currentProject.sourcePaths;
if (_sourcePaths.length)
_location = _sourcePaths[0];
if (folder)
_location = folder.filename;
}
/// override to implement creation of dialog controls
override void initialize() {
super.initialize();
initTemplates();
Widget content;
try {
content = parseML(q{
VerticalLayout {
id: vlayout
padding: Rect { 5, 5, 5, 5 }
layoutWidth: fill; layoutHeight: fill
HorizontalLayout {
layoutWidth: fill; layoutHeight: fill
VerticalLayout {
margins: 5
layoutWidth: 50%; layoutHeight: fill
TextWidget { text: "Project template" }
StringListWidget {
id: projectTemplateList
layoutWidth: wrap; layoutHeight: fill
}
}
VerticalLayout {
margins: 5
layoutWidth: 50%; layoutHeight: fill
TextWidget { text: "Template description" }
EditBox {
id: templateDescription; readOnly: true
layoutWidth: fill; layoutHeight: fill
}
}
}
TableLayout {
margins: 5
colCount: 2
layoutWidth: fill; layoutHeight: wrap
TextWidget { text: "Name" }
EditLine { id: edName; text: "newfile"; layoutWidth: fill }
TextWidget { text: "Location" }
DirEditLine { id: edLocation; layoutWidth: fill }
TextWidget { text: "Module name" }
EditLine { id: edModuleName; text: ""; layoutWidth: fill; readOnly: true }
TextWidget { text: "File path" }
EditLine { id: edFilePath; text: ""; layoutWidth: fill; readOnly: true }
}
TextWidget { id: statusText; text: ""; layoutWidth: fill; textColor: #FF0000 }
}
});
} catch (Exception e) {
Log.e("Exceptin while parsing DML", e);
throw e;
}
_projectTemplateList = content.childById!StringListWidget("projectTemplateList");
_templateDescription = content.childById!EditBox("templateDescription");
_edFileName = content.childById!EditLine("edName");
_edFilePath = content.childById!EditLine("edFilePath");
_edModuleName = content.childById!EditLine("edModuleName");
_edLocation = content.childById!DirEditLine("edLocation");
_edLocation.text = toUTF32(_location);
_statusText = content.childById!TextWidget("statusText");
_edLocation.filetypeIcons[".d"] = "text-d";
_edLocation.filetypeIcons["dub.json"] = "project-d";
_edLocation.filetypeIcons["package.json"] = "project-d";
_edLocation.filetypeIcons[".dlangidews"] = "project-development";
_edLocation.addFilter(FileFilterEntry(UIString.fromRaw("DlangIDE files"d), "*.dlangidews;*.d;*.dd;*.di;*.ddoc;*.dh;*.json;*.xml;*.ini"));
_edLocation.caption = "Select directory"d;
// fill templates
dstring[] names;
foreach(t; _templates)
names ~= t.name;
_projectTemplateList.items = names;
_projectTemplateList.selectedItemIndex = 0;
templateSelected(0);
// listeners
_edLocation.contentChange = delegate (EditableContent source) {
_location = toUTF8(source.text);
validate();
};
_edFileName.contentChange = delegate (EditableContent source) {
_fileName = toUTF8(source.text);
validate();
};
_projectTemplateList.itemSelected = delegate (Widget source, int itemIndex) {
templateSelected(itemIndex);
return true;
};
_projectTemplateList.itemClick = delegate (Widget source, int itemIndex) {
templateSelected(itemIndex);
return true;
};
addChild(content);
addChild(createButtonsPanel([ACTION_FILE_NEW_SOURCE_FILE, ACTION_CANCEL], 0, 0));
}
StringListWidget _projectTemplateList;
EditBox _templateDescription;
DirEditLine _edLocation;
EditLine _edFileName;
EditLine _edModuleName;
EditLine _edFilePath;
TextWidget _statusText;
string _fileName = "newfile";
string _location;
string _moduleName;
string _packageName;
string _fullPathName;
int _currentTemplateIndex = -1;
ProjectTemplate _currentTemplate;
ProjectTemplate[] _templates;
static bool isSubdirOf(string path, string basePath) {
if (path.equal(basePath))
return true;
if (path.length > basePath.length + 1 && path.startsWith(basePath)) {
char ch = path[basePath.length];
return ch == '/' || ch == '\\';
}
return false;
}
bool findSource(string path, ref string sourceFolderPath, ref string relativePath) {
foreach(dir; _sourcePaths) {
if (isSubdirOf(path, dir)) {
sourceFolderPath = dir;
relativePath = path[sourceFolderPath.length .. $];
if (relativePath.length > 0 && (relativePath[0] == '\\' || relativePath[0] == '/'))
relativePath = relativePath[1 .. $];
return true;
}
}
return false;
}
bool setError(dstring msg) {
_statusText.text = msg;
return msg.empty;
}
bool validate() {
string filename = _fileName;
string fullFileName = filename;
if (!_currentTemplate.fileExtension.empty && filename.endsWith(_currentTemplate.fileExtension))
filename = filename[0 .. $ - _currentTemplate.fileExtension.length];
else
fullFileName = fullFileName ~ _currentTemplate.fileExtension;
_fullPathName = buildNormalizedPath(_location, fullFileName);
_edFilePath.text = toUTF32(_fullPathName);
if (!isValidFileName(filename))
return setError("Invalid file name");
if (!exists(_location) || !isDir(_location))
return setError("Location directory does not exist");
if (_currentTemplate.isModule) {
string sourcePath, relativePath;
if (!findSource(_location, sourcePath, relativePath))
return setError("Location is outside of source path");
if (!isValidModuleName(filename))
return setError("Invalid file name");
_moduleName = filename;
char[] buf;
foreach(ch; relativePath) {
if (ch == '/' || ch == '\\')
buf ~= '.';
else
buf ~= ch;
}
_packageName = buf.dup;
string m = !_packageName.empty ? _packageName ~ '.' ~ _moduleName : _moduleName;
_edModuleName.text = toUTF32(m);
_packageName = m;
} else {
string projectPath = _project.dir;
if (!isSubdirOf(_location, projectPath))
return setError("Location is outside of project path");
_edModuleName.text = "";
_moduleName = "";
_packageName = "";
}
return true;
}
private FileCreationResult _result;
bool createItem() {
try {
if (_currentTemplate.isModule) {
string txt = "module " ~ _packageName ~ ";\n\n" ~ _currentTemplate.srccode;
write(_fullPathName, txt);
} else {
write(_fullPathName, _currentTemplate.srccode);
}
} catch (Exception e) {
Log.e("Cannot create file", e);
return setError("Cannot create file");
}
_result = new FileCreationResult(_project, _fullPathName);
return true;
}
override void close(const Action action) {
Action newaction = action.clone();
if (action.id == IDEActions.FileNew) {
if (!validate()) {
window.showMessageBox(UIString.fromRaw("Error"d), UIString.fromRaw("Invalid parameters"));
return;
}
if (!createItem()) {
window.showMessageBox(UIString.fromRaw("Error"d), UIString.fromRaw("Failed to create project item"));
return;
}
newaction.objectParam = _result;
}
super.close(newaction);
}
protected void templateSelected(int index) {
if (_currentTemplateIndex == index)
return;
_currentTemplateIndex = index;
_currentTemplate = _templates[index];
_templateDescription.text = _currentTemplate.description;
//updateDirLayout();
validate();
}
void initTemplates() {
_templates ~= new ProjectTemplate("Empty module"d, "Empty D module file."d, ".d",
"\n", true);
_templates ~= new ProjectTemplate("Text file"d, "Empty text file."d, ".txt",
"\n", true);
_templates ~= new ProjectTemplate("JSON file"d, "Empty json file."d, ".json",
"{\n}\n", true);
}
}
class ProjectTemplate {
dstring name;
dstring description;
string fileExtension;
string srccode;
bool isModule;
this(dstring name, dstring description, string fileExtension, string srccode, bool isModule) {
this.name = name;
this.description = description;
this.fileExtension = fileExtension;
this.srccode = srccode;
this.isModule = isModule;
}
}