FileTree.hx 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677
  1. package hide.view;
  2. typedef ExtensionOptions = {
  3. ?icon : String,
  4. ?createNew : String,
  5. ?name : String,
  6. };
  7. typedef ExtensionDesc = {
  8. var component : String;
  9. var extensions : Array<String>;
  10. var options : ExtensionOptions;
  11. }
  12. class FileTree extends FileView {
  13. var tree : hide.comp.IconTree<String>;
  14. var ignorePatterns : Array<EReg> = [];
  15. var modifiedFiles : Array<String> = [];
  16. public function new(state) {
  17. super(state);
  18. var exclPatterns : Array<String> = ide.currentConfig.get("filetree.excludes", []);
  19. for(pat in exclPatterns)
  20. ignorePatterns.push(new EReg(pat, "i"));
  21. if( state.path == null ) {
  22. ide.chooseDirectory(function(dir) {
  23. if( dir == null ) {
  24. close();
  25. return;
  26. }
  27. state.path = dir.split("\\").join("/")+"/";
  28. saveState();
  29. rebuild();
  30. },true);
  31. }
  32. keys.register("search", function() tree.openFilter());
  33. }
  34. override function canSave() {
  35. return false;
  36. }
  37. public static function getExtension( file : String ) {
  38. var ext = new haxe.io.Path(file).ext;
  39. if( ext == null ) return null;
  40. ext = ext.toLowerCase();
  41. if( ext == "json" ) {
  42. try {
  43. var obj : Dynamic = haxe.Json.parse(sys.io.File.getContent(file));
  44. if( obj.type != null && Std.isOfType(obj.type, String) ) {
  45. var e = EXTENSIONS.get("json." + obj.type);
  46. if( e != null ) return e;
  47. }
  48. } catch( e : Dynamic ) {
  49. }
  50. }
  51. return EXTENSIONS.get(ext);
  52. }
  53. override function getTitle() {
  54. if( state.path == "" )
  55. return "Resources";
  56. if( state.path == null )
  57. return "";
  58. return super.getTitle();
  59. }
  60. override function onFileChanged(wasDeleted:Bool, rebuildView:Bool = true) {
  61. }
  62. override function onDisplay() {
  63. if( state.path == null ) return;
  64. var panel = new Element("<div class='hide-scroll'>").appendTo(element);
  65. tree = new hide.comp.IconTree(panel,null);
  66. tree.async = true;
  67. tree.allowRename = true;
  68. tree.saveDisplayKey = saveDisplayKey;
  69. tree.element.addClass("small");
  70. var isSVNAvailable = ide.isSVNAvailable();
  71. tree.get = function(path) {
  72. if( path == null ) path = "";
  73. var basePath = getFilePath(path);
  74. var content = new Array<hide.comp.IconTree.IconTreeItem<String>>();
  75. for( c in sys.FileSystem.readDirectory(basePath) ) {
  76. var fullPath = basePath + "/" + c;
  77. if( isIgnored(fullPath) ) continue;
  78. var isDir = sys.FileSystem.isDirectory(fullPath);
  79. var ext = getExtension(fullPath);
  80. var id = ( path == "" ? c : path+"/"+c );
  81. content.push({
  82. value : id,
  83. text : c,
  84. icon : "ico ico-" + (isDir ? "folder" : (ext != null && ext.options.icon != null ? ext.options.icon : "file-text")),
  85. children : isDir,
  86. });
  87. if (!isDir && (isSVNAvailable && (ide.ideConfig.svnShowModifiedFiles || ide.ideConfig.svnShowVersionedFiles)))
  88. watch(fullPath, function() refreshSVNStatusIcons(), { checkDelete: true });
  89. }
  90. watch(basePath, function() rebuild(), { checkDelete: true });
  91. content.sort(function(a,b) { if( a.children != b.children ) return a.children?-1:1; return Reflect.compare(a.text,b.text); });
  92. return content;
  93. };
  94. tree.onRename = (path:String, name:String) -> {
  95. // add old extension if previous one is missing
  96. if (name.indexOf(".") == -1) {
  97. var ext = path.split(".").pop();
  98. name += "." + ext;
  99. }
  100. doRename(path, name);
  101. };
  102. element.contextmenu(function(e) {
  103. var over = tree.getCurrentOver();
  104. if( over != null && !tree.getSelection().contains(over))
  105. tree.setSelection([over]);
  106. var selection = tree.getSelection();
  107. e.preventDefault();
  108. var allowedNew : Array<String> = config.get("filetree.allowednew");
  109. function allowed( ext : String ) return allowedNew.indexOf(ext) >= 0 || allowedNew.indexOf("*") >= 0;
  110. var newMenu = [for( e in EXTENSIONS ) if( e.options.createNew != null && Lambda.exists(e.extensions, allowed) ) {
  111. label : e.options.createNew,
  112. click : createNew.bind(selection[0], e),
  113. icon : e.options.icon,
  114. }];
  115. if( allowed("dir") )
  116. newMenu.unshift({
  117. label : "Directory",
  118. click : createNew.bind(selection[0], { options : { createNew : "Directory" }, extensions : null, component : null }),
  119. icon : "folder",
  120. });
  121. var options : Array<hide.comp.ContextMenu.MenuItem> = [
  122. { label : "New...", menu:newMenu },
  123. { label : "Collapse", click : tree.collapseAll },
  124. { label : "", isSeparator: true },
  125. { label : "Copy Path", enabled : selection.length == 1, click : function() { ide.setClipboard(selection[0]); } },
  126. { label : "Copy Absolute Path", enabled : selection.length == 1, click : function() { ide.setClipboard(Ide.inst.getPath(selection[0])); } },
  127. { label : "Open in Explorer", enabled : selection.length == 1, click : function() { onExploreFile(selection[0]); } },
  128. { label : "Find References", enabled : selection.length == 1, click : onFindPathRef.bind(selection[0])},
  129. { label : "", isSeparator: true },
  130. { label : "Clone", enabled : selection.length == 1, click : function() {
  131. try {
  132. if (onCloneFile(selection[0])) {
  133. tree.refresh();
  134. }
  135. } catch (e : Dynamic) {
  136. js.Browser.window.alert(e);
  137. }
  138. } },
  139. { label : "Rename", enabled : selection.length == 1, click : function() {
  140. try {
  141. onRenameFile(selection[0]);
  142. } catch (e : Dynamic) {
  143. js.Browser.window.alert(e);
  144. }
  145. } },
  146. { label : "Move", enabled : selection.length > 0, click : function() {
  147. ide.chooseDirectory(function(dir) {
  148. for (current in selection) {
  149. doRename(current, "/"+dir+"/"+current.split("/").pop());
  150. }
  151. });
  152. }},
  153. { label : "Delete", enabled : selection.length > 0, click : function() {
  154. if( js.Browser.window.confirm("Delete " + selection.join(", ") + "?") ) {
  155. for (current in selection) {
  156. onDeleteFile(current);
  157. }
  158. tree.refresh();
  159. }
  160. }},
  161. { label: "Replace Refs With", enabled: selection.length > 0, click : function() {
  162. ide.chooseFile(["*"], (newPath: String) -> {
  163. if(ide.confirm('Replace all refs of $selection with $newPath ? This action can not be undone')) {
  164. for (oldPath in selection) {
  165. replacePathInFiles(oldPath, newPath, false);
  166. }
  167. ide.message("Done");
  168. }
  169. });
  170. }}
  171. ];
  172. if (ide.isSVNAvailable()) {
  173. options.push({ label : "", isSeparator: true });
  174. options.push({ label: "SVN Revert", enabled: selection.length == 1, click : function() {
  175. var path = ide.getPath(selection[0]);
  176. js.node.ChildProcess.exec('cmd.exe /c start "" TortoiseProc.exe /command:revert /path:"$path"', { cwd: ide.getPath(ide.resourceDir) }, (error, stdout, stderr) -> {
  177. if (error != null)
  178. ide.quickError('Error while trying to revert file ${path} : ${error}');
  179. });
  180. }});
  181. options.push({ label: "SVN Log", enabled: selection.length == 1, click : function() {
  182. var path = ide.getPath(selection[0]);
  183. js.node.ChildProcess.exec('cmd.exe /c start "" TortoiseProc.exe /command:log /path:"$path"', { cwd: ide.getPath(ide.resourceDir) }, (error, stdout, stderr) -> {
  184. if (error != null)
  185. ide.quickError('Error while trying to log file ${path} : ${error}');
  186. });
  187. }});
  188. options.push({ label: "SVN Blame", enabled: selection.length == 1, click : function() {
  189. var path = ide.getPath(selection[0]);
  190. js.node.ChildProcess.exec('cmd.exe /c start "" TortoiseProc.exe /command:blame /path:"$path"', { cwd: ide.getPath(ide.resourceDir) }, (error, stdout, stderr) -> {
  191. if (error != null)
  192. ide.quickError('Error while trying to blame file ${path} : ${error}');
  193. });
  194. }});
  195. }
  196. hide.comp.ContextMenu.createFromEvent(cast e, options);
  197. });
  198. tree.onDblClick = onOpenFile;
  199. tree.onAllowMove = onAllowMove;
  200. tree.onMove = doMove;
  201. var svnIcons = ide.isSVNAvailable() && (ide.ideConfig.svnShowModifiedFiles || ide.ideConfig.svnShowVersionedFiles);
  202. tree.init(svnIcons ? () -> refreshSVNStatusIcons() : null);
  203. if (svnIcons)
  204. tree.applyStyle = (e : String, el : Element) -> refreshSVNStatusIcons(false, e, el);
  205. }
  206. function refreshSVNStatusIcons(rec : Bool = true, ?p : String, ?el : Element) {
  207. if (!rec) {
  208. var isModified = false;
  209. for (f in modifiedFiles) {
  210. if (ide.getPath(f).indexOf(p) >= 0) {
  211. isModified = true;
  212. break;
  213. }
  214. }
  215. if (isModified) {
  216. if (ide.ideConfig.svnShowModifiedFiles)
  217. el.addClass("svn-modified");
  218. el.removeClass("svn-versioned");
  219. }
  220. else {
  221. if (ide.ideConfig.svnShowVersionedFiles)
  222. el.addClass("svn-versioned");
  223. el.removeClass("svn-modified");
  224. }
  225. return;
  226. }
  227. modifiedFiles = [];//Ide.inst.getSVNModifiedFiles();
  228. if (el == null)
  229. el = tree.element;
  230. var prevModified = el.find(".svn-modified");
  231. prevModified.removeClass("sv-modified");
  232. if (ide.ideConfig.svnShowVersionedFiles)
  233. el.find(".jstree-node").addClass("svn-versioned");
  234. if (ide.ideConfig.svnShowModifiedFiles) {
  235. for (f in modifiedFiles) {
  236. var relPath = f.substr(f.indexOf("res/") + 4);
  237. var p = "";
  238. for (sp in relPath.split("/")) {
  239. p += p.length > 0 ? "/"+sp : sp;
  240. var el = tree.getElement(p);
  241. if (!(el is hide.Element))
  242. continue;
  243. el.removeClass("svn-versioned");
  244. el.addClass("svn-modified");
  245. }
  246. }
  247. }
  248. }
  249. function onRenameFile( path : String ) {
  250. var oldName = path.substring( path.lastIndexOf("/") + 1 );
  251. var newFilename = ide.ask("New name:", oldName);
  252. // If the user removed the extension, add the old one
  253. if (newFilename.indexOf(".") == -1) {
  254. var ext = oldName.split(".").pop();
  255. newFilename += "." + ext;
  256. }
  257. while ( newFilename != null && sys.FileSystem.exists(ide.getPath(newFilename))) {
  258. newFilename = ide.ask("This file already exists. Another new name:");
  259. }
  260. if (newFilename == null) {
  261. return false;
  262. }
  263. doRename(path, newFilename);
  264. return true;
  265. }
  266. public static function doRename(path:String, name:String) {
  267. var isDir = sys.FileSystem.isDirectory(hide.Ide.inst.getPath(path));
  268. if( isDir ) hide.Ide.inst.fileWatcher.pause();
  269. var ret = onRenameRec(path, name);
  270. if( isDir ) hide.Ide.inst.fileWatcher.resume();
  271. return ret;
  272. }
  273. function onFindPathRef(path: String) {
  274. var refs = ide.search(path, ["hx", "prefab", "fx", "cdb", "json", "props", "ddt"], ["bin"]);
  275. ide.open("hide.view.RefViewer", null, null, function(view) {
  276. var refViewer : hide.view.RefViewer = cast view;
  277. refViewer.showRefs(refs, path, function() {
  278. ide.openFile(path);
  279. });
  280. });
  281. }
  282. public static function onRenameRec(path:String, name:String) {
  283. var ide = hide.Ide.inst;
  284. var parts = path.split("/");
  285. parts.pop();
  286. for( n in name.split("/") ) {
  287. if( n == ".." )
  288. parts.pop();
  289. else
  290. parts.push(n);
  291. }
  292. var newPath = name.charAt(0) == "/" ? name.substr(1) : parts.join("/");
  293. if( newPath == path )
  294. return false;
  295. if( sys.FileSystem.exists(ide.getPath(newPath)) ) {
  296. function addPath(path:String,rand:String) {
  297. var p = path.split(".");
  298. if( p.length > 1 )
  299. p[p.length-2] += rand;
  300. else
  301. p[p.length-1] += rand;
  302. return p.join(".");
  303. }
  304. if( path.toLowerCase() == newPath.toLowerCase() ) {
  305. // case change
  306. var rand = "__tmp"+Std.random(10000);
  307. onRenameRec(path, "/"+addPath(path,rand));
  308. onRenameRec(addPath(path,rand), name);
  309. } else {
  310. if( !ide.confirm(newPath+" already exists, invert files?") )
  311. return false;
  312. var rand = "__tmp"+Std.random(10000);
  313. onRenameRec(path, "/"+addPath(path,rand));
  314. onRenameRec(newPath, "/"+path);
  315. onRenameRec(addPath(path,rand), name);
  316. }
  317. return false;
  318. }
  319. var isDir = sys.FileSystem.isDirectory(ide.getPath(path));
  320. var wasRenamed = false;
  321. var isSVNRepo = sys.FileSystem.exists(ide.projectDir+"/.svn") || js.node.ChildProcess.spawnSync("svn",["info"], { cwd : ide.resourceDir }).status == 0; // handle not root dirs
  322. if( isSVNRepo ) {
  323. if( js.node.ChildProcess.spawnSync("svn",["--version"]).status != 0 ) {
  324. if( isDir && !ide.confirm("Renaming a SVN directory, but 'svn' system command was not found. Continue ?") )
  325. return false;
  326. } else {
  327. // Check if origin file and target directory are versioned
  328. var isFileVersioned = js.node.ChildProcess.spawnSync("svn",["info", ide.getPath(path)]).status == 0;
  329. var newAbsPath = ide.getPath(newPath);
  330. var parentFolder = newAbsPath.substring(0, newAbsPath.lastIndexOf('/'));
  331. var isDirVersioned = js.node.ChildProcess.spawnSync("svn",["info", parentFolder]).status == 0;
  332. if (isFileVersioned && isDirVersioned) {
  333. var cwd = Sys.getCwd();
  334. Sys.setCwd(ide.resourceDir);
  335. var code = Sys.command("svn",["rename", path, newPath]);
  336. Sys.setCwd(cwd);
  337. if( code == 0 )
  338. wasRenamed = true;
  339. else {
  340. if( !ide.confirm("SVN rename failure, perform file rename ?") )
  341. return false;
  342. }
  343. }
  344. }
  345. }
  346. if( !wasRenamed )
  347. sys.FileSystem.rename(ide.getPath(path), ide.getPath(newPath));
  348. replacePathInFiles(path, newPath, isDir);
  349. var dataDir = new haxe.io.Path(path);
  350. if( dataDir.ext != "dat" ) {
  351. dataDir.ext = "dat";
  352. var dataPath = dataDir.toString();
  353. if( sys.FileSystem.isDirectory(ide.getPath(dataPath)) ) {
  354. var destPath = new haxe.io.Path(name);
  355. destPath.ext = "dat";
  356. onRenameRec(dataPath, destPath.toString());
  357. }
  358. }
  359. // update Materials.props if an FBX is moved/renamed
  360. var newSysPath = new haxe.io.Path(name);
  361. var oldSysPath = new haxe.io.Path(path);
  362. if (newSysPath.dir == null) {
  363. newSysPath.dir = oldSysPath.dir;
  364. }
  365. if (newSysPath.ext?.toLowerCase() == "fbx" && oldSysPath.ext?.toLowerCase() == "fbx") {
  366. function remLeadingSlash(s:String) {
  367. if(StringTools.startsWith(s,"/")) {
  368. return s.length > 1 ? s.substr(1) : "";
  369. }
  370. return s;
  371. }
  372. var oldMatPropsPath = ide.getPath(remLeadingSlash((oldSysPath.dir ?? "") + "/materials.props"));
  373. if (sys.FileSystem.exists(oldMatPropsPath)) {
  374. var newMatPropsPath = ide.getPath(remLeadingSlash((newSysPath.dir ?? "") + "/materials.props"));
  375. var oldMatProps = haxe.Json.parse(sys.io.File.getContent(oldMatPropsPath));
  376. var newMatProps : Dynamic =
  377. if (sys.FileSystem.exists(newMatPropsPath))
  378. haxe.Json.parse(sys.io.File.getContent(newMatPropsPath))
  379. else {};
  380. var oldNameExt = oldSysPath.file + "." + oldSysPath.ext;
  381. var newNameExt = newSysPath.file + "." + newSysPath.ext;
  382. function moveRec(originalData: Dynamic, oldData:Dynamic, newData: Dynamic) {
  383. for (field in Reflect.fields(originalData)) {
  384. if (StringTools.endsWith(field, oldNameExt)) {
  385. var innerData = Reflect.getProperty(originalData, field);
  386. var newField = StringTools.replace(field, oldNameExt, newNameExt);
  387. Reflect.setProperty(newData, newField, innerData);
  388. Reflect.deleteField(oldData, field);
  389. }
  390. else {
  391. var originalInner = Reflect.getProperty(originalData, field);
  392. if (Type.typeof(originalInner) != TObject)
  393. continue;
  394. var oldInner = Reflect.getProperty(oldData, field);
  395. var newInner = Reflect.getProperty(newData, field) ?? {};
  396. moveRec(originalInner, oldInner, newInner);
  397. // Avoid creating empty fields
  398. if (Reflect.fields(newInner).length > 0) {
  399. Reflect.setProperty(newData, field, newInner);
  400. }
  401. // Cleanup removed fields in old props
  402. if (Reflect.fields(oldInner).length == 0) {
  403. Reflect.deleteField(oldData, field);
  404. }
  405. }
  406. }
  407. }
  408. var sourceData = oldMatProps;
  409. var oldDataToSave = oldMatPropsPath == newMatPropsPath ? newMatProps : haxe.Json.parse(haxe.Json.stringify(oldMatProps));
  410. moveRec(oldMatProps, oldDataToSave, newMatProps);
  411. sys.io.File.saveContent(newMatPropsPath, haxe.Json.stringify(newMatProps, null, "\t"));
  412. if (oldMatPropsPath != newMatPropsPath) {
  413. if (Reflect.fields(oldMatProps).length > 0) {
  414. sys.io.File.saveContent(oldMatPropsPath, haxe.Json.stringify(oldDataToSave, null, "\t"));
  415. } else {
  416. sys.FileSystem.deleteFile(oldMatPropsPath);
  417. }
  418. }
  419. // Clear caches
  420. @:privateAccess
  421. {
  422. if (h3d.mat.MaterialSetup.current != null) {
  423. h3d.mat.MaterialSetup.current.database.db.remove(ide.makeRelative(oldMatPropsPath));
  424. h3d.mat.MaterialSetup.current.database.db.remove(ide.makeRelative(newMatPropsPath));
  425. }
  426. hxd.res.Loader.currentInstance.cache.remove(ide.makeRelative(oldMatPropsPath));
  427. hxd.res.Loader.currentInstance.cache.remove(ide.makeRelative(newMatPropsPath));
  428. }
  429. }
  430. }
  431. return true;
  432. }
  433. public static function replacePathInFiles(oldPath: String, newPath: String, isDir: Bool = false) {
  434. function filter(ctx: hide.Ide.FilterPathContext) {
  435. var p = ctx.valueCurrent;
  436. if( p == null )
  437. return;
  438. if( p == oldPath ) {
  439. ctx.change(newPath);
  440. return;
  441. }
  442. if( p == "/"+oldPath ) {
  443. ctx.change(newPath);
  444. return;
  445. }
  446. if( isDir ) {
  447. if( StringTools.startsWith(p,oldPath+"/") ) {
  448. ctx.change(newPath + p.substr(oldPath.length));
  449. return;
  450. }
  451. if( StringTools.startsWith(p,"/"+oldPath+"/") ) {
  452. ctx.change("/"+newPath + p.substr(oldPath.length+1));
  453. return;
  454. }
  455. }
  456. }
  457. hide.Ide.inst.filterPaths(filter);
  458. }
  459. function onAllowMove(e: String, to : String) {
  460. var destAbsPath = Ide.inst.getPath(to);
  461. return sys.FileSystem.isDirectory(destAbsPath);
  462. }
  463. static function doMove(e : String, to : String, index : Int) {
  464. var dest = "/" + to + "/" + e.split("/").pop();
  465. doRename(e, dest);
  466. }
  467. function onExploreFile( path : String ) {
  468. Ide.showFileInExplorer(path);
  469. }
  470. function onCloneFile( path : String ) {
  471. var sourcePath = getFilePath(path);
  472. var nameNewFile = ide.ask("New filename:", new haxe.io.Path(sourcePath).file);
  473. if (nameNewFile == null || nameNewFile.length == 0) {
  474. return false;
  475. }
  476. var targetPath = new haxe.io.Path(sourcePath).dir + "/" + nameNewFile;
  477. if ( sys.FileSystem.exists(targetPath) ) {
  478. throw "File already exists";
  479. }
  480. if( sys.FileSystem.isDirectory(sourcePath) ) {
  481. sys.FileSystem.createDirectory(targetPath + "/");
  482. for( f in sys.FileSystem.readDirectory(sourcePath) ) {
  483. sys.io.File.saveBytes(targetPath + "/" + f, sys.io.File.getBytes(sourcePath + "/" + f));
  484. }
  485. } else {
  486. var extensionNewFile = getExtension(targetPath);
  487. if (extensionNewFile == null) {
  488. var extensionSourceFile = '';
  489. var sourceExt = sourcePath.substr(sourcePath.lastIndexOf('.') + 1);
  490. for (e in getExtension(sourcePath).extensions) {
  491. if (e == sourceExt)
  492. extensionSourceFile = e;
  493. }
  494. if (extensionSourceFile != null) {
  495. targetPath = targetPath + "." + extensionSourceFile;
  496. }
  497. }
  498. sys.io.File.saveBytes(targetPath, sys.io.File.getBytes(sourcePath));
  499. }
  500. return true;
  501. }
  502. function onDeleteFile( path : String ) {
  503. var fullPath = getFilePath(path);
  504. if( sys.FileSystem.isDirectory(fullPath) ) {
  505. for( f in sys.FileSystem.readDirectory(fullPath) )
  506. onDeleteFile(path + "/" + f);
  507. sys.FileSystem.deleteDirectory(fullPath);
  508. } else
  509. sys.FileSystem.deleteFile(fullPath);
  510. }
  511. function getFilePath(path:String) {
  512. if( path == "" )
  513. return ide.getPath(state.path).substr(0, -1);
  514. return ide.getPath(state.path) + path;
  515. }
  516. function onOpenFile( path : String ) {
  517. var fullPath = getFilePath(path);
  518. if( sys.FileSystem.isDirectory(fullPath) )
  519. return false;
  520. var ext = getExtension(fullPath);
  521. if( ext == null )
  522. return false;
  523. ide.openFile(fullPath);
  524. if (Ide.inst.ideConfig.closeSearchOnFileOpen)
  525. tree.closeFilter();
  526. return true;
  527. }
  528. public function revealNode( path : String ) {
  529. var folders = path.split("/");
  530. var currentFolder = "";
  531. function _revealNode() {
  532. if( folders.length == 0 ) {
  533. tree.setSelection([path]);
  534. tree.revealNode(currentFolder);
  535. return;
  536. }
  537. if( currentFolder.length > 0 ) currentFolder += "/";
  538. currentFolder += folders.shift();
  539. tree.openNodeAsync(currentFolder, _revealNode);
  540. }
  541. _revealNode();
  542. }
  543. function createNew( basePath : String, ext : ExtensionDesc ) {
  544. if( basePath == null )
  545. basePath = "";
  546. var fullPath = getFilePath(basePath);
  547. if( !sys.FileSystem.isDirectory(fullPath) ) {
  548. basePath = new haxe.io.Path(basePath).dir;
  549. if( basePath == null ) basePath = "";
  550. fullPath = getFilePath(basePath);
  551. }
  552. var file = ide.ask(ext.options.createNew + " name:");
  553. if( file == null ) return;
  554. if( file.indexOf(".") < 0 && ext.extensions != null )
  555. file += "." + ext.extensions[0].split(".").shift();
  556. if( sys.FileSystem.exists(fullPath + "/" + file) ) {
  557. ide.error("File '" + file+"' already exists");
  558. createNew(basePath, ext);
  559. return;
  560. }
  561. // directory
  562. if( ext.component == null ) {
  563. sys.FileSystem.createDirectory(fullPath + "/" + file);
  564. return;
  565. }
  566. var view : hide.view.FileView = Type.createEmptyInstance(Type.resolveClass(ext.component));
  567. view.ide = ide;
  568. view.state = { path : basePath+"/"+file };
  569. sys.io.File.saveBytes(fullPath + "/" + file, view.getDefaultContent());
  570. var fpath = basePath == "" ? file : basePath + "/" + file;
  571. tree.refresh(function() tree.setSelection([fpath]));
  572. onOpenFile(fpath);
  573. }
  574. function isIgnored( fullpath : String ) {
  575. for(pat in ignorePatterns) {
  576. if(pat.match(fullpath))
  577. return true;
  578. }
  579. return false;
  580. }
  581. static var EXTENSIONS = new Map<String,ExtensionDesc>();
  582. public static function registerExtension<T>( c : Class<hide.ui.View<T>>, extensions : Array<String>, ?options : ExtensionOptions ) {
  583. hide.ui.View.register(c);
  584. for (e in extensions) {
  585. var registered = EXTENSIONS.get(e);
  586. if (registered == null) {
  587. registered = {component: Type.getClassName(c), options: {}, extensions: extensions };
  588. EXTENSIONS.set(e, registered);
  589. }
  590. if( options == null ) options = {};
  591. for (field in Reflect.fields(options)) {
  592. Reflect.setField(registered.options, field, Reflect.field(options, field));
  593. }
  594. }
  595. return null;
  596. }
  597. static var _ = hide.ui.View.register(FileTree, { width : 350, position : Left });
  598. }