3 import "FileSystemIterator"
5 class ImportFolderFSI : NormalFileSystemIterator
7 ProjectView projectView;
8 Array<ProjectNode> stack { };
10 bool OnFolder(const char * folderPath)
12 char name[MAX_LOCATION];
13 ProjectNode parentNode = stack.lastIterator.data;
15 GetLastDirectory(folderPath, name);
16 folder = parentNode.FindSpecial(name, false, true, true);
18 folder = projectView.NewFolder(parentNode, name, false);
23 void OutFolder(const char * folderPath, bool isRoot)
25 stack.lastIterator.Remove(); //stack.Remove();
28 bool OnFile(const char * filePath)
30 ProjectNode parentNode = stack.lastIterator.data;
31 if(!projectView.AddFile(parentNode, filePath, parentNode.isInResources, false))
33 char * msg = PrintString($"This file can't be imported due to a conflict.\n\n", filePath,
34 "\n\nThis occurs with identical file paths and with conflicting file names.\n");
35 MessageBox { master = ide, type = ok, text = "Import File Conflict", contents = msg }.Modal();
42 static Array<FileFilter> fileFilters
44 { $"eC/C/C++ Files (*.ec, *.eh, *.c, *.cpp, *.cc, *.cxx, *.h, *.hpp, *.hh, *.hxx)", "ec, eh, c, cpp, cc, cxx, h, hpp, hh, hxx" },
45 { $"eC/C/C++ Source Files (*.ec, *.c, *.cpp, *.cc, *.cxx)", "ec, eh, c, cpp, cc, cxx" },
46 { $"Header Files for eC/C/C++ (*.eh, *.h, *.hpp, *.hh, *.hxx)", "eh, h, hpp, hh, hxx" },
47 { $"All files", null }
50 static Array<FileFilter> resourceFilters
52 { $"Image Files (*.jpg, *.jpeg, *.bmp, *.pcx, *.png, *.gif)", "jpg, jpeg, bmp, pcx, png, gif" },
53 { $"3D Studio Model Files (*.3ds)", "3ds" },
54 { $"Translations (*.mo)", "mo" },
55 { $"All files", null }
58 static Array<FileFilter> projectFilters
60 { $"Project Files (*.epj)", ProjectExtension },
61 { $"Workspace Files (*.ews)", WorkspaceExtension }
64 static Array<FileType> projectTypes
66 { $"Ecere IDE Project", ProjectExtension },
67 { $"Ecere IDE Workspace", WorkspaceExtension }
70 static const char * iconNames[] =
72 "<:ecere>mimeTypes/file.png", /*genFile*/
73 "<:ecere>mimeTypes/textEcereWorkspace.png", /*ewsFile*/
74 "<:ecere>mimeTypes/textEcereProject.png", /*epjFile*/
75 "<:ecere>places/folder.png", /*folder*/
76 "<:ecere>status/folderOpen.png", /*openFolder*/
77 "<:ecere>mimeTypes/textEcereSource.png", /*ecFile*/
78 "<:ecere>mimeTypes/textEcereHeader.png", /*ehFile*/
79 "<:ecere>mimeTypes/textCSource.png", /*sFile*/ // TODO: change sFile icon to differentiate from cFile icon
80 "<:ecere>mimeTypes/textCSource.png", /*cFile*/
81 "<:ecere>mimeTypes/textCHeader.png", /*hFile*/
82 "<:ecere>mimeTypes/textC++Source.png", /*cppFile*/
83 "<:ecere>mimeTypes/textC++Header.png", /*hppFile*/
84 "<:ecere>mimeTypes/text.png", /*textFile*/
85 "<:ecere>mimeTypes/textHyperTextMarkup.png", /*webFile*/
86 "<:ecere>mimeTypes/image.png", /*pictureFile*/
87 "<:ecere>status/audioVolumeHigh.png", /*soundFile*/
88 "<:ecere>mimeTypes/package.png", /*archiveFile*/
89 "<:ecere>mimeTypes/packageSoftware.png", /*packageFile*/
90 "<:ecere>mimeTypes/packageOpticalDisc.png", /*opticalMediaImageFile*/
91 "<:ecere>mimeTypes/file.png", /*mFile*/ //TODO: create icon for .m file
92 "<:ecere>mimeTypes/file.png" /*mmFile*/ //TODO: create icon for .mm file
95 enum PrepareMakefileMethod { normal, force, forceExists };
97 enum CleanType { clean, realClean, cleanTarget };
98 enum BuildType { build, rebuild, relink, run, start, restart, clean, install };
101 none, buildingMainProject, buildingSecondaryProject, compilingFile;
103 property bool { get { return this != none; } }
104 //property bool actualBuild { get { return this == buildingMainProject || this == buildingSecondaryProject; } }
106 enum BuildOutputMode { normal, raw, verbose, justPrint }; // note: verbose and justPrint both imply raw output
107 Array<const String> bldMnuStrBuild
110 $"Build (Raw Output)",
112 $"Build (Just Print Commands)"
114 Array<const String> bldMnuStrRelink
117 $"Relink (Raw Output)",
119 $"Relink (Just Print Commands)"
121 Array<const String> bldMnuStrRebuild
124 $"Rebuild (Raw Output)",
125 $"Rebuild (Verbose)",
126 $"Rebuild (Just Print Commands)"
128 Array<const String> bldMnuStrCleanTarget
131 $"Clean Target (Raw Output)",
132 $"Clean Target (Verbose)",
133 $"Clean Target (Just Print Commands)"
135 Array<const String> bldMnuStrClean
138 $"Clean (Raw Output)",
140 $"Clean (Just Print Commands)"
142 Array<const String> bldMnuStrRealClean
145 $"Real Clean (Raw Output)",
146 $"Real Clean (Verbose)",
147 $"Real Clean (Just Print Commands)"
149 Array<const String> bldMnuStrCompile
152 $"Compile (Raw Output)",
153 $"Compile (Verbose)",
154 $"Compile (Just Print Commands)"
157 class ProjectView : Window
160 //hasMinimize = true;
162 borderStyle = sizable;
163 hasHorzScroll = true;
164 hasVertScroll = true;
167 anchor = Anchor { left = 0, top = 0, bottom = 0 };
170 //hasMinimize = true;
171 saveDialog = projectFileDialog;
174 BuildState buildInProgress;
175 BitmapResource icons[NodeIcons];
178 property Workspace workspace
184 for(prj : workspace.projects)
186 DeleteNode(prj.topNode);
188 workspace.projects.Free();
189 ide.debugger.CleanUp();
194 fileDialog.currentDirectory = workspace.workspaceDir;
195 resourceFileDialog.currentDirectory = workspace.workspaceDir;
196 for(prj : workspace.projects)
197 AddNode(prj.topNode, null);
198 ide.statusBar.text = $"Generating Makefile & Dependencies...";
200 for(prj : workspace.projects)
201 prj.ModifiedAllConfigs(true, false, false, false);
202 ide.statusBar.text = $"Initializing Debugger"; app.UpdateDisplay();
203 ide.statusBar.text = null;
207 get { return workspace; }
210 bool drawingInProjectSettingsDialog;
211 bool drawingInProjectSettingsDialogHeader;
212 ProjectSettings projectSettingsDialog;
220 for(c = 0; c < NodeIcons::enumSize; c++)
222 icons[c] = BitmapResource { iconNames[c], alphaBlend = true };
223 AddResource(icons[c]);
225 fileList.AddField(DataField { dataType = class(ProjectNode), freeData = false, userData = this });
231 ide.DestroyTemporaryProjectDir();
241 multiSelect = true, fullRowSelect = false, hasVertScroll = true, hasHorzScroll = true;
242 borderStyle = deep, parent = this, collapseControl = true, treeBranches = true;
243 anchor = Anchor { left = 0, right = 0, top = 0 , bottom = 0 };
245 background = projectViewBackground;
246 foreground = projectViewText;
247 selectionColor = selectionColor, selectionText = selectionText;
248 stippleColor = skyBlue;
250 bool OnActivate(bool active, Window previous, bool * goOnWithActivation, bool direct)
252 if(!active) Update(null);
253 return ListBox::OnActivate(active, previous, goOnWithActivation, direct);
256 bool NotifyDoubleClick(ListBox listBox, int x, int y, Modifiers mods)
258 // Prevent the double click from reactivating the project view (returns false if we opened something)
259 return !OpenSelectedNodes(mods.ctrl && mods.shift);
262 bool NotifyRightClick(ListBox listBox, int x, int y, Modifiers mods)
264 DataRow row = listBox.currentRow;
267 bool showDebuggingMenuItems = mods.ctrl && mods.shift;
268 bool showInstallMenuItem = mods.ctrl && mods.shift;
269 BuildOutputMode outputMode = (mods.ctrl && mods.shift) ? justPrint : mods.ctrl ? verbose : mods.shift ? raw : normal;
270 ProjectNode node = (ProjectNode)(intptr)row.tag;
271 #ifdef IDE_SHOW_INSTALL_MENU_BUTTON
272 showInstallMenuItem = true;
274 if(node.type == NodeTypes::project || node.type == resources || node.type == file || node.type == folder)
276 bool na = buildInProgress; // N/A - buildMenuUnavailable
279 if(node.type == NodeTypes::project)
282 mi = ide.projectBuildItem;
283 MenuItem { pop, bldMnuStrBuild[outputMode] , b, f7 , id = outputMode, NotifySelect = ProjectBuild , bitmap = mi.bitmap }.disabled = na; mi = ide.projectLinkItem;
284 MenuItem { pop, bldMnuStrRelink[outputMode] , l , id = outputMode, NotifySelect = ProjectLink , bitmap = mi.bitmap }.disabled = na; mi = ide.projectRebuildItem;
285 MenuItem { pop, bldMnuStrRebuild[outputMode] , r, shiftF7, id = outputMode, NotifySelect = ProjectRebuild , bitmap = mi.bitmap }.disabled = na; mi = ide.projectCleanTargetItem;
286 MenuItem { pop, bldMnuStrCleanTarget[outputMode], g , id = outputMode, NotifySelect = ProjectCleanTarget, bitmap = mi.bitmap }.disabled = na; mi = ide.projectCleanItem;
287 MenuItem { pop, bldMnuStrClean[outputMode] , c , id = outputMode, NotifySelect = ProjectClean , bitmap = mi.bitmap }.disabled = na; mi = ide.projectRealCleanItem;
288 MenuItem { pop, bldMnuStrRealClean[outputMode] , id = outputMode, NotifySelect = ProjectRealClean , bitmap = mi.bitmap }.disabled = na; mi = ide.projectRegenerateItem;
289 MenuItem { pop, $"Regenerate Makefile" , m , NotifySelect = ProjectRegenerate , bitmap = mi.bitmap }.disabled = na;
290 if(showInstallMenuItem)
292 mi = ide.projectInstallItem;
293 MenuItem { pop, $"Install" , t , NotifySelect = ProjectInstall , bitmap = mi.bitmap }.disabled = na;
295 if(showDebuggingMenuItems && node.ContainsFilesWithExtension("ec", node.project.config))
298 MenuItem { pop, $"Debug Generate Symbols", l, NotifySelect = FileDebugGenerateSymbols }.disabled = na;
299 MenuItem { pop, $"Debug Precompile", l, NotifySelect = FileDebugPrecompile }.disabled = na;
300 MenuItem { pop, $"Debug Compile", l, NotifySelect = FileDebugCompile }.disabled = na;
303 MenuItem { pop, $"New File...", l, Key { l, ctrl = true }, NotifySelect = ProjectNewFile };
304 MenuItem { pop, $"New Folder...", n, Key { f, ctrl = true }, NotifySelect = ProjectNewFolder };
305 MenuItem { pop, $"Import Folder...", i, NotifySelect = ProjectImportFolder };
306 MenuItem { pop, $"Add Files to Project...", f, NotifySelect = ProjectAddFiles };
308 MenuItem { pop, $"Add New Form...", o, NotifySelect = ProjectAddNewForm };
309 // MenuItem { pop, "Add New Behavior Graph...", g, NotifySelect = ProjectAddNewGraph };
311 if(node != ((Project)workspace.projects.first).topNode)
313 MenuItem { pop, $"Remove project from workspace", r, NotifySelect = ProjectRemove }.disabled = na;
316 MenuItem { pop, $"Settings...", s, Key { f7, alt = true } , NotifySelect = MenuSettings };
318 MenuItem { pop, $"Browse Folder", w, NotifySelect = MenuBrowseFolder };
320 MenuItem { pop, $"Save", v, Key { s, ctrl = true }, NotifySelect = ProjectSave }.disabled = !node.modified;
322 MenuItem { pop, $"Properties...", p, Key { enter, alt = true }, NotifySelect = FileProperties };
324 else if(node.type == resources)
326 MenuItem { pop, $"New File...", l, Key { l, ctrl = true }, NotifySelect = ProjectNewFile };
327 MenuItem { pop, $"New Folder...", n, Key { f, ctrl = true }, NotifySelect = ProjectNewFolder };
328 MenuItem { pop, $"Import Folder...", i, NotifySelect = ProjectImportFolder };
329 MenuItem { pop, $"Add Resources to Project...", f, NotifySelect = ResourcesAddFiles };
330 MenuItem { pop, $"Browse Folder", w, NotifySelect = MenuBrowseFolder };
332 MenuItem { pop, $"Settings...", s, Key { f7, alt = true } , NotifySelect = MenuSettings };
333 MenuItem { pop, $"Properties...", p, Key { enter, alt = true }, NotifySelect = FileProperties };
335 else if(node.type == file)
337 MenuItem { pop, $"Open", o, NotifySelect = FileOpenFile };
339 MenuItem { pop, bldMnuStrClean[outputMode], l, id = outputMode, NotifySelect = FileClean, bitmap = ide.projectCleanItem.bitmap }.disabled = na;
340 MenuItem { pop, bldMnuStrCompile[outputMode], c, Key { f7, ctrl = true}, id = outputMode, NotifySelect = FileCompile, bitmap = ide.projectBuildItem.bitmap }.disabled = na;
341 if(showDebuggingMenuItems)
343 char extension[MAX_EXTENSION];
344 GetExtension(node.name, extension);
345 if(!strcmpi(extension, "ec"))
348 MenuItem { pop, $"Debug Precompile", l, NotifySelect = FileDebugPrecompile }.disabled = na;
349 MenuItem { pop, $"Debug Compile", l, NotifySelect = FileDebugCompile }.disabled = na;
353 MenuItem { pop, $"Remove", r, NotifySelect = FileRemoveFile };
355 MenuItem { pop, $"Browse Folder", w, NotifySelect = MenuBrowseFolder };
357 MenuItem { pop, $"Settings...", s, Key { f7, alt = true } , NotifySelect = MenuSettings };
358 MenuItem { pop, $"Properties..", p, Key { enter, alt = true }, NotifySelect = FileProperties };
360 else if(node.type == folder)
362 bool isInResources = node.isInResources;
364 MenuItem { pop, $"New File...", l, Key { l, ctrl = true }, NotifySelect = ProjectNewFile };
365 MenuItem { pop, $"New Folder...", n, Key { f, ctrl = true }, NotifySelect = ProjectNewFolder };
366 MenuItem { pop, $"Import Folder...", i, NotifySelect = ProjectImportFolder };
369 MenuItem { pop, $"Add Resources to Folder...", f, NotifySelect = ResourcesAddFiles };
373 MenuItem { pop, $"Add Files to Folder...", f, NotifySelect = ProjectAddFiles };
378 MenuItem { pop, $"Add New Form...", o, NotifySelect = ProjectAddNewForm };
379 // MenuItem { pop, $"Add New Behavior Graph...", g, NotifySelect = ProjectAddNewGraph };
382 MenuItem { pop, bldMnuStrClean[outputMode], l, id = outputMode, NotifySelect = FileClean, bitmap = ide.projectCleanItem.bitmap }.disabled = na;
383 MenuItem { pop, bldMnuStrCompile[outputMode], c, Key { f7, ctrl = true}, id = outputMode, NotifySelect = FileCompile, bitmap = ide.projectBuildItem.bitmap }.disabled = na;
385 MenuItem { pop, $"Remove", r, NotifySelect = FileRemoveFile };
387 MenuItem { pop, $"Browse Folder", w, NotifySelect = MenuBrowseFolder };
389 MenuItem { pop, $"Settings...", s, Key { f7, alt = true } , NotifySelect = MenuSettings };
390 MenuItem { pop, $"Properties...", p, Key { enter, alt = true }, NotifySelect = FileProperties };
395 master = this, menu = pop;
397 x + clientStart.x + absPosition.x - app.desktop.position.x,
398 y + clientStart.y + absPosition.y - app.desktop.position.y };
400 void NotifyDestroyed(Window window, DialogResult result)
406 ide.rightClickMenuBuildOutputMode = outputMode;
407 ide.AdjustPopupBuildMenus();
413 bool NotifyKeyHit(ListBox listBox, DataRow row, Key key, unichar ch)
415 if(key == altUp || key == altDown)
417 SelectNextProject(key == altUp);
423 bool NotifyKeyDown(ListBox listBox, DataRow row, Key key, unichar ch)
427 ProjectNode node = (ProjectNode)(intptr)row.tag;
430 case altEnter: case Key { keyPadEnter, alt = true }:
432 NodeProperties { parent = parent, master = this,
433 position = { position.x + 100, position.y + 100 }, node = node }.Create();
436 case enter: case keyPadEnter:
439 for(resNode = node.parent; resNode; resNode = resNode.parent)
440 if(resNode.type == resources)
442 if(node.type == project || (node.type == folder && !resNode))
447 else if(node.type == resources || node.type == folder)
456 if(node.type == project || node.type == folder || node.type == resources)
465 if(node.type == project || node.type == folder || node.type == resources)
467 NewFolder(node, null, true);
474 if(node.type == file)
476 FileCompile(null, (Modifiers)key);
481 case Key { space, false, true }:
482 case Key { space, true, true }:
483 case Key { space, true }:
486 if(node.type == NodeTypes::project)
489 for(p : workspace.projects)
491 if(p.topNode == node)
497 prj.RotateActiveConfig(!key.shift, key.ctrl);
508 case Key { enter, true, true }: OpenSelectedNodes(true); break;
509 case Key { keyPadEnter, true, true }: OpenSelectedNodes(true); break;
510 case enter: case keyPadEnter: OpenSelectedNodes(false); break;
511 case del: RemoveSelectedNodes(); break;
514 Window activeClient = ide.activeClient;
516 activeClient.Activate();
518 ide.RepositionWindows(true);
525 bool NotifyCollapse(ListBox listBox, DataRow row, bool collapsed)
527 ProjectNode node = (ProjectNode)(intptr)row.tag;
528 if(node.type == folder)
529 node.icon = collapsed ? folder : openFolder;
534 FileDialog importFileDialog { autoCreate = false, type = selectDir, text = $"Import Folder" };
535 FileDialog projectFileDialog
537 autoCreate = false, filters = projectFilters.array, sizeFilters = projectFilters.count * sizeof(FileFilter);
538 types = projectTypes.array, sizeTypes = projectTypes.count * sizeof(FileType);
540 FileDialog fileDialog
542 autoCreate = false, mayNotExist = true, filters = fileFilters.array, sizeFilters = fileFilters.count * sizeof(FileFilter);
544 FileDialog resourceFileDialog
546 autoCreate = false, mayNotExist = true, filters = resourceFilters.array, sizeFilters = resourceFilters.count * sizeof(FileFilter);
549 Menu fileMenu { menu, $"File", f };
550 MenuItem { fileMenu, $"Save", s, Key { s, ctrl = true }, NotifySelect = MenuFileSave };
551 // MenuItem { fileMenu, "Save As...", a, NotifySelect = MenuFileSaveAs };
553 bool OnClose(bool parentClosing)
555 if(!parentClosing && visible)
565 DialogResult dialogRes;
568 strcpy(msg, $"You have modified projects.\nSave changes to ");
569 for(p : ide.workspace.projects)
571 if(p.topNode.modified)
573 if(!first) strcat(msg, ", ");
580 dialogRes = MessageBox { master = master, type = yesNoCancel, text = parent.caption ? parent.caption : rootWindow.caption, contents = msg }.Modal();
584 // TOFIX: Precomp error if brackets are taken out
585 return (DialogResult)MenuFileSave(null, 0) != cancel;
587 else if(dialogRes == cancel)
589 modifiedDocument = false;
596 //if(ide.findInFilesDialog && ide.findInFilesDialog.created && ide.findInFilesDialog.mode != directory)
597 // ide.findInFilesDialog.SearchStop();
599 ide.outputView.buildBox.Clear();
600 ide.outputView.debugBox.Clear();
601 //ide.outputView.findBox.Clear();
602 ide.callStackView.Clear();
603 ide.watchesView.Clear();
604 ide.threadsView.Clear();
605 ide.breakpointsView.Clear();
606 ide.outputView.ShowClearSelectTab(find); // why this?
609 bool OnSaveFile(const char * fileName)
611 for(prj : ide.workspace.projects)
613 if(prj.topNode.modified)
615 prj.StopMonitoring();
616 if(prj.Save(prj.filePath))
617 prj.topNode.modified = false;
618 prj.StartMonitoring();
621 modifiedDocument = false;
626 bool IsModuleInProject(const char * filePath)
628 char moduleName[MAX_FILENAME]; //, modulePath[MAX_LOCATION];
629 GetLastDirectory(filePath, moduleName);
630 return project.topNode.Find(moduleName, false) != null;
633 ProjectNode GetNodeForCompilationFromWindow(Window document, bool nonExcludedFirst, bool * isExcluded, bool * isCObject)
635 ProjectNode node = null;
637 node = GetNodeFromWindow(document, null, false, false, isExcluded);
639 node = GetNodeFromWindow(document, null, true, false, isExcluded);
640 if(!node && nonExcludedFirst)
642 node = GetNodeFromWindow(document, null, false, true, isExcluded);
643 if(isCObject && node) *isCObject = true;
647 node = GetNodeFromWindow(document, null, true, true, isExcluded);
648 if(isCObject && node) *isCObject = true;
653 ProjectNode GetNodeFromWindow(Window document, Project project, bool allNodes, bool isCObject, bool * isNodeExcluded)
655 ProjectNode node = null;
656 if(document.fileName)
658 bool excluded = false;
659 char winFileName[MAX_LOCATION];
660 char * documentFileName = GetSlashPathBuffer(winFileName, document.fileName);
665 char name[MAX_FILENAME];
666 GetLastDirectory(documentFileName, name);
667 ChangeExtension(name, "ec", name);
668 for(p : ide.workspace.projects)
670 prj = project ? project : p;
671 if((n = prj.topNode.Find(name, false)))
673 if(allNodes || !(excluded = n.GetIsExcluded(prj.config)))
684 for(p : ide.workspace.projects)
686 prj = project ? project : p;
687 if((n = prj.topNode.FindByFullPath(documentFileName, false)))
689 if(allNodes || !(excluded = n.GetIsExcluded(prj.config)))
698 if(node && isNodeExcluded)
699 *isNodeExcluded = excluded;
704 // ((( UTILITY FUNCTIONS )))
706 // ************************************************************************
707 // *** These methods below are part of a sequence of events, and as ***
708 // *** such they must be passed the current compiler and project config ***
709 // ************************************************************************
710 bool DisplayCompiler(CompilerConfig compiler, bool cleanLog)
712 ide.outputView.buildBox.Logf($"%s Compiler\n", compiler ? compiler.name : $"{problem with compiler selection}");
716 bool ProjectPrepareForToolchain(Project project, PrepareMakefileMethod method, bool cleanLog, bool displayCompiler,
717 CompilerConfig compiler, ProjectConfig config)
719 ShowOutputBuildLog(cleanLog);
722 DisplayCompiler(compiler, false);
724 ProjectPrepareCompiler(project, compiler, false);
725 ProjectPrepareMakefile(project, method, compiler, config);
729 bool ProjectPrepareCompiler(Project project, CompilerConfig compiler, bool silent)
731 if((!project.GenerateCrossPlatformMk(app.includeFile) ||
732 !project.GenerateCompilerCf(compiler, project.topNode.ContainsFilesWithExtension("ec", project.config))) && !silent)
733 ide.outputView.buildBox.Logf($"Error generating compiler configuration (Is the project/config directory writable?)\n");
737 // Note: Compiler is only passed in to for VisualStudio support
738 bool ProjectPrepareMakefile(Project project, PrepareMakefileMethod method, CompilerConfig compiler, ProjectConfig config)
740 if(compiler.type.isVC)
742 // I'm guessing we'll want to support generating VS files on Linux as well...
743 ide.statusBar.text = $"Generating Visual Studio Solution...";
745 //GenerateVSSolutionFile(project, compiler);
746 ide.statusBar.text = $"Generating Visual Studio Project...";
748 //GenerateVCProjectFile(project, compiler, bitDepth);
749 ide.statusBar.text = null;
755 char makefilePath[MAX_LOCATION];
756 char makefileName[MAX_LOCATION];
758 LogBox logBox = ide.outputView.buildBox;
760 strcpy(makefilePath, project.topNode.path);
761 project.CatMakeFileName(makefileName, config);
762 PathCatSlash(makefilePath, makefileName);
764 exists = FileExists(makefilePath);
765 if(method == force ||
766 (method == forceExists && exists) ||
767 (method == normal && (!exists || (config && config.makingModified))))
771 ide.statusBar.text = $"Generating Makefile & Dependencies..."; // Dependencies?
774 if((method == normal && !exists) || (method == force && !exists))
775 action = $"Generating ";
776 else if(method == force)
777 action = $"Regenerating ";
778 else if(method == normal || method == forceExists)
779 action = $"Updating ";
783 reason = $"Makefile doesn't exist. ";
784 else if(project.topNode.modified)
785 reason = $"Project has been modified. ";
789 //logBox.Logf("%s\n", makefileName);
790 logBox.Logf($"%s - %s%smakefile for %s config...\n", makefileName, reason, action, GetConfigName(config));
792 if(!project.GenerateMakefile(null, false, null, config))
793 ide.outputView.buildBox.Logf($"Error generating makefile (Is the project directory writable?)\n");
795 ide.statusBar.text = null;
803 bool BuildInterrim(Project prj, BuildType buildType, CompilerConfig compiler, ProjectConfig config, int bitDepth, BuildOutputMode outputMode)
805 if(ProjectPrepareForToolchain(prj, normal, true, true, compiler, config))
807 ide.outputView.buildBox.Logf($"Building project %s using the %s configuration...\n", prj.name, GetConfigName(config));
808 return Build(prj, buildType, compiler, config, bitDepth, outputMode);
813 bool DebugStopForMake(Project prj, BuildType buildType, CompilerConfig compiler, ProjectConfig config)
816 // TOFIX: DebugStop is being abused and backfiring on us.
817 // It's supposed to be the 'Debug/Stop' item, not unloading executable or anything else
819 // configIsInDebugSession seems to be used for two OPPOSITE things:
820 // If we're debugging another config, we need to unload the executable!
821 // In building, we want to stop if we're debugging the 'same' executable
822 if(buildType != run) ///* && prj == project*/ && prj.configIsInDebugSession)
824 if(buildType == start || buildType == restart)
826 if(ide.debugger && ide.debugger.isPrepared)
827 result = DebugStop();
831 if(ide.project == prj && ide.debugger && ide.debugger.prjConfig == config && ide.debugger.isPrepared)
832 result = DebugStop();
835 app.ProcessInput(true);
836 ShowOutputBuildLog(false);
840 bool Build(Project prj, BuildType buildType, CompilerConfig compiler, ProjectConfig config, int bitDepth, BuildOutputMode outputMode)
846 for(document = master.firstChild; document; document = document.next)
848 if(document.modifiedDocument)
850 ProjectNode node = GetNodeFromWindow(document, prj, true, false, null);
851 if(node && !document.MenuFileSave(null, 0))
860 DirExpression targetDir = prj.GetTargetDir(compiler, config, bitDepth);
862 DebugStopForMake(prj, buildType, compiler, config);
864 // TODO: Disabled until problems fixed... is it fixed?
865 if(buildType == rebuild || (config && config.compilingModified))
866 prj.Clean(compiler, config, bitDepth, clean, outputMode);
869 if(buildType == relink || (config && config.linkingModified))
870 prj.Clean(compiler, config, bitDepth, cleanTarget, outputMode);
871 if(config && config.symbolGenModified)
873 DirExpression objDir = prj.GetObjDir(compiler, config, bitDepth);
874 char fileName[MAX_LOCATION];
875 char moduleName[MAX_FILENAME];
876 strcpy(fileName, prj.topNode.path);
877 PathCatSlash(fileName, objDir.dir);
878 ReplaceSpaces(moduleName, prj.moduleName);
879 strcat(moduleName, ".main.ec");
880 PathCatSlash(fileName, moduleName);
881 if(FileExists(fileName))
882 DeleteFile(fileName);
883 ChangeExtension(fileName, "c", fileName);
884 if(FileExists(fileName))
885 DeleteFile(fileName);
886 ChangeExtension(fileName, "o", fileName);
887 if(FileExists(fileName))
888 DeleteFile(fileName);
893 buildInProgress = prj == project ? buildingMainProject : buildingSecondaryProject;
894 ide.AdjustBuildMenus();
895 ide.AdjustDebugMenus();
897 result = prj.Build(buildType, null, compiler, config, bitDepth, outputMode, normal);
901 config.compilingModified = false;
903 config.linkingModified = false;
905 config.symbolGenModified = false;
907 buildInProgress = none;
908 ide.AdjustBuildMenus();
909 ide.AdjustDebugMenus();
911 ide.workspace.modified = true;
918 // ((( USER ACTIONS )))
920 // ************************************************************************
921 // *** Methods below should atomically start a process, and as such ***
922 // *** they can query compiler and config directly from ide and project ***
923 // *** but ONLY ONCE!!! ***
924 // ************************************************************************
926 bool ProjectBuild(MenuItem selection, Modifiers mods)
928 BuildOutputMode mode = selection ? (BuildOutputMode)selection.id : normal;
929 if(buildInProgress == none)
931 Project prj = project;
932 CompilerConfig compiler = ideConfig.compilers.GetCompilerConfig(ide.workspace.activeCompiler);
933 int bitDepth = ide.workspace.bitDepth;
934 ProjectConfig config;
935 if(selection || !ide.activeClient)
937 DataRow row = fileList.currentRow;
938 ProjectNode node = row ? (ProjectNode)(intptr)row.tag : null;
939 if(node) prj = node.project;
943 ProjectNode node = GetNodeForCompilationFromWindow(ide.activeClient, true, null, null);
948 if(/*prj != project || */!prj.GetConfigIsInDebugSession(config) || !ide.DontTerminateDebugSession($"Project Build"))
950 BuildInterrim(prj, build, compiler, config, bitDepth, mode);
959 bool ProjectInstall(MenuItem selection, Modifiers mods)
961 Project prj = project;
962 CompilerConfig compiler = ideConfig.compilers.GetCompilerConfig(ide.workspace.activeCompiler);
963 int bitDepth = ide.workspace.bitDepth;
964 BuildOutputMode mode = selection ? (BuildOutputMode)selection.id : normal;
965 ProjectConfig config;
966 if(selection || !ide.activeClient)
968 DataRow row = fileList.currentRow;
969 ProjectNode node = row ? (ProjectNode)(intptr)row.tag : null;
970 if(node) prj = node.project;
974 ProjectNode node = GetNodeFromWindow(ide.activeClient, null, true, false, null);
979 if(!prj.GetConfigIsInDebugSession(config) ||
980 (!ide.DontTerminateDebugSession($"Project Install") && DebugStopForMake(prj, relink, compiler, config)))
982 BuildInterrim(prj, build, compiler, config, bitDepth, mode);
983 if(ProjectPrepareForToolchain(prj, normal, false, false, compiler, config))
985 ide.outputView.buildBox.Logf($"\nInstalling project %s using the %s configuration...\n", prj.name, GetConfigName(config));
986 Build(prj, install, compiler, config, bitDepth, mode);
993 bool ProjectLink(MenuItem selection, Modifiers mods)
995 Project prj = project;
996 CompilerConfig compiler = ideConfig.compilers.GetCompilerConfig(ide.workspace.activeCompiler);
997 int bitDepth = ide.workspace.bitDepth;
998 BuildOutputMode mode = selection ? (BuildOutputMode)selection.id : normal;
999 ProjectConfig config;
1000 if(selection || !ide.activeClient)
1002 DataRow row = fileList.currentRow;
1003 ProjectNode node = row ? (ProjectNode)(intptr)row.tag : null;
1004 if(node) prj = node.project;
1008 ProjectNode node = GetNodeFromWindow(ide.activeClient, null, true, false, null);
1012 config = prj.config;
1013 if(!prj.GetConfigIsInDebugSession(config) ||
1014 (!ide.DontTerminateDebugSession($"Project Link") && DebugStopForMake(prj, relink, compiler, config)))
1016 if(ProjectPrepareForToolchain(prj, normal, true, true, compiler, config))
1018 ide.outputView.buildBox.Logf($"Relinking project %s using the %s configuration...\n", prj.name, GetConfigName(config));
1020 config.linkingModified = true;
1021 Build(prj, relink, compiler, config, bitDepth, mode);
1028 bool ProjectRebuild(MenuItem selection, Modifiers mods)
1030 CompilerConfig compiler = ideConfig.compilers.GetCompilerConfig(ide.workspace.activeCompiler);
1031 int bitDepth = ide.workspace.bitDepth;
1032 Project prj = project;
1033 BuildOutputMode mode = selection ? (BuildOutputMode)selection.id : normal;
1034 ProjectConfig config;
1035 if(selection || !ide.activeClient)
1037 DataRow row = fileList.currentRow;
1038 ProjectNode node = row ? (ProjectNode)(intptr)row.tag : null;
1039 if(node) prj = node.project;
1043 ProjectNode node = GetNodeFromWindow(ide.activeClient, null, true, false, null);
1047 config = prj.config;
1048 if(!prj.GetConfigIsInDebugSession(config) ||
1049 (!ide.DontTerminateDebugSession($"Project Rebuild") && DebugStopForMake(prj, rebuild, compiler, config)))
1051 if(ProjectPrepareForToolchain(prj, normal, true, true, compiler, config))
1053 ide.outputView.buildBox.Logf($"Rebuilding project %s using the %s configuration...\n", prj.name, GetConfigName(config));
1056 config.compilingModified = true;
1057 config.makingModified = true;
1058 }*/ // -- should this still be used depite the new solution of BuildType?
1059 Build(prj, rebuild, compiler, config, bitDepth, mode);
1066 bool ProjectCleanTarget(MenuItem selection, Modifiers mods)
1068 BuildOutputMode mode = selection ? (BuildOutputMode)selection.id : normal;
1069 CleanProject($"Project Clean Target", $"Cleaning project %s target using the %s configuration...\n", selection, cleanTarget, mode);
1073 bool ProjectClean(MenuItem selection, Modifiers mods)
1075 BuildOutputMode mode = selection ? (BuildOutputMode)selection.id : normal;
1076 CleanProject($"Project Clean", $"Cleaning project %s using the %s configuration...\n", selection, clean, mode);
1080 bool ProjectRealClean(MenuItem selection, Modifiers mods)
1082 BuildOutputMode mode = selection ? (BuildOutputMode)selection.id : normal;
1083 CleanProject($"Project Real Clean", $"Removing intermediate objects directory for project %s using the %s configuration...\n", selection, realClean, mode);
1087 void CleanProject(const char * terminateDebugSessionMessage, const char * cleaningMessageLogFormat, MenuItem selection, CleanType cleanType, BuildOutputMode outputMode)
1089 Project prj = project;
1090 Array<Project> projects { };
1091 CompilerConfig compiler = ideConfig.compilers.GetCompilerConfig(ide.workspace.activeCompiler);
1092 int bitDepth = ide.workspace.bitDepth;
1096 OldList selectedRows;
1097 fileList.GetMultiSelection(selectedRows);
1098 for(item = selectedRows.first; item; item = item.next)
1100 DataRow row = item.data;
1101 ProjectNode node = (ProjectNode)(intptr)row.tag;
1102 if(node.type == project)
1103 projects.Add(node.project);
1105 selectedRows.Free(null);
1107 if(selection || !ide.activeClient)
1109 DataRow row = fileList.currentRow;
1110 ProjectNode node = row ? (ProjectNode)(intptr)row.tag : null;
1111 if(node) prj = node.project;
1112 if(projects.count == 0)
1117 // TODO: a file can belong to more than one project, ask which one to clean
1118 ProjectNode node = GetNodeFromWindow(ide.activeClient, null, true, false, null);
1119 if(node) prj = node.project;
1120 if(projects.count == 0)
1125 ProjectConfig config = prj.config;
1126 if(!prj.GetConfigIsInDebugSession(config) ||
1127 (!ide.DontTerminateDebugSession(terminateDebugSessionMessage) && DebugStopForMake(prj, clean, compiler, config)))
1129 if(ProjectPrepareForToolchain(prj, normal, true, true, compiler, config))
1131 ide.outputView.buildBox.Logf(cleaningMessageLogFormat, prj.name, GetConfigName(config));
1133 buildInProgress = prj == project ? buildingMainProject : buildingSecondaryProject;
1134 ide.AdjustBuildMenus();
1135 ide.AdjustDebugMenus();
1137 prj.Clean(compiler, config, bitDepth, cleanType, outputMode);
1138 buildInProgress = none;
1139 ide.AdjustBuildMenus();
1140 ide.AdjustDebugMenus();
1148 bool ProjectRegenerate(MenuItem selection, Modifiers mods)
1150 Project prj = project;
1151 CompilerConfig compiler = ideConfig.compilers.GetCompilerConfig(ide.workspace.activeCompiler);
1152 ShowOutputBuildLog(true);
1153 if(selection || !ide.activeClient)
1155 DataRow row = fileList.currentRow;
1156 ProjectNode node = row ? (ProjectNode)(intptr)row.tag : null;
1162 ProjectNode node = GetNodeFromWindow(ide.activeClient, null, true, false, null);
1167 DisplayCompiler(compiler, false);
1168 ProjectPrepareCompiler(project, compiler, false);
1169 ProjectPrepareMakefile(prj, force, compiler, prj.config);
1174 bool Compile(Project project, List<ProjectNode> nodes, BuildOutputMode outputMode, SingleFileCompileMode mode)
1178 ProjectConfig config = project.config;
1182 for(document = ide.firstChild; document; document = document.next)
1184 if(document.modifiedDocument)
1186 ProjectNode n = GetNodeFromWindow(document, project, true, mode == cObject ? true : false, null);
1189 if(n && n.IsInNode(node) && !document.MenuFileSave(null, 0))
1191 ide.outputView.buildBox.Logf($"Unable to save %s file.\n", node.name);
1201 CompilerConfig compiler = ideConfig.compilers.GetCompilerConfig(ide.workspace.activeCompiler);
1202 int bitDepth = ide.workspace.bitDepth;
1204 if(ProjectPrepareForToolchain(project, normal, true, true, compiler, config))
1207 ide.outputView.buildBox.Logf($"%s specific file(s) in project %s using the %s configuration...\n",
1208 (mode == normal || mode == cObject) ? $"Compiling" : $"Debug compiling", project.name, config.name);
1210 ide.outputView.buildBox.Logf($"%s specific file(s) in project %s...\n",
1211 (mode == normal || mode == cObject) ? $"Compiling" : $"Debug compiling", project.name);
1213 buildInProgress = compilingFile;
1214 ide.AdjustBuildMenus();
1215 result = project.Compile(nodes, compiler, config, bitDepth, outputMode, mode);
1216 buildInProgress = none;
1217 ide.AdjustBuildMenus();
1224 bool Clean(Project project, List<ProjectNode> nodes)
1228 ProjectConfig config = project.config;
1232 for(document = ide.firstChild; document; document = document.next)
1234 if(document.modifiedDocument)
1236 ProjectNode n = GetNodeFromWindow(document, project, true, false, null);
1239 if(n && n.IsInNode(node) && !document.MenuFileSave(null, 0))
1241 ide.outputView.buildBox.Logf($"Unable to save %s file.\n", node.name);
1251 CompilerConfig compiler = ideConfig.compilers.GetCompilerConfig(ide.workspace.activeCompiler);
1252 int bitDepth = ide.workspace.bitDepth;
1254 if(ProjectPrepareForToolchain(project, normal, true, true, compiler, config))
1256 Map<String, NameCollisionInfo> namesInfo { };
1257 project.topNode.GenMakefileGetNameCollisionInfo(namesInfo, config);
1260 if(node.GetIsExcluded(config))
1261 ide.outputView.buildBox.Logf($"File %s is excluded from current build configuration.\n", node.name);
1265 ide.outputView.buildBox.Logf($"Deleting intermediate objects for %s %s in project %s using the %s configuration...\n",
1266 node.type == file ? $"single file" : $"folder", node.name, project.name, config.name);
1268 ide.outputView.buildBox.Logf($"Deleting intermediate objects for %s %s in project %s...\n",
1269 node.type == file ? $"single file" : $"folder", node.name, project.name);
1271 node.DeleteIntermediateFiles(compiler, config, bitDepth, namesInfo, false);
1283 bool ProjectNewFile(MenuItem selection, Modifiers mods)
1285 DataRow row = fileList.currentRow;
1288 char fileName[1024];
1289 char filePath[MAX_LOCATION];
1290 ProjectNode parentNode = (ProjectNode)(intptr)row.tag;
1291 ProjectNode n, fileNode;
1292 parentNode.GetFileSysMatchingPath(filePath);
1293 MakePathRelative(filePath, parentNode.project.topNode.path, filePath);
1294 for(n = parentNode; n && n != parentNode.project.resNode; n = n.parent);
1295 sprintf(fileName, $"Untitled %d", documentID);
1296 fileNode = AddFile(parentNode, fileName, n != null, true);
1297 fileNode.path = CopyUnixPath(filePath);
1300 NodeProperties nodeProperties
1303 position = { position.x + 100, position.y + 100 };
1307 nodeProperties.Create(); // not modal?
1313 bool ProjectNewFolder(MenuItem selection, Modifiers mods)
1315 DataRow row = fileList.currentRow;
1318 ProjectNode parentNode = (ProjectNode)(intptr)row.tag;
1319 NewFolder(parentNode, null, true);
1324 bool ResourcesAddFiles(MenuItem selection, Modifiers mods)
1330 bool ProjectAddFiles(MenuItem selection, Modifiers mods)
1336 bool ProjectImportFolder(MenuItem selection, Modifiers mods)
1338 DataRow row = fileList.currentRow;
1341 ProjectNode toNode = (ProjectNode)(intptr)row.tag;
1342 ImportFolder(toNode);
1347 bool ProjectAddNewForm(MenuItem selection, Modifiers mods)
1349 CodeEditor codeEditor = CreateNew("Form", "form", "Window", null);
1350 codeEditor.EnsureUpToDate();
1351 ide.RepositionWindows(false);
1355 bool ProjectAddNewGraph(MenuItem selection, Modifiers mods)
1357 CodeEditor codeEditor = CreateNew("Graph", "graph", "Block", null);
1359 codeEditor.EnsureUpToDate();
1363 bool ProjectRemove(MenuItem selection, Modifiers mods)
1365 DataRow row = fileList.currentRow;
1368 ProjectNode node = (ProjectNode)(intptr)row.tag;
1369 if(node.type == project)
1370 RemoveSelectedNodes();
1375 bool ProjectUpdateMakefileForAllConfigs(Project project)
1377 CompilerConfig compiler = ideConfig.compilers.GetCompilerConfig(ide.workspace.activeCompiler);
1379 // This call really does not belong here:
1380 ide.UpdateToolBarActiveConfigs(false);
1381 for(config : project.configurations)
1382 ProjectPrepareMakefile(project, forceExists, compiler, config);
1389 bool MenuSettings(MenuItem selection, Modifiers mods)
1391 ProjectNode node = GetSelectedNode(true);
1392 Project prj = node ? node.project : project;
1393 projectSettingsDialog = ProjectSettings { master = parent, project = prj, projectNode = node };
1394 incref projectSettingsDialog;
1395 projectSettingsDialog.Modal();
1396 delete projectSettingsDialog;
1397 ide.UpdateToolBarActiveConfigs(false);
1403 bool FileProperties(MenuItem selection, Modifiers mods)
1405 DataRow row = fileList.currentRow;
1408 ProjectNode node = (ProjectNode)(intptr)row.tag;
1409 NodeProperties { parent = parent, master = this, node = node,
1410 position = { position.x + 100, position.y + 100 } }.Create();
1415 bool FileOpenFile(MenuItem selection, Modifiers mods)
1417 OpenSelectedNodes(mods.ctrl && mods.shift);
1421 bool FileRemoveFile(MenuItem selection, Modifiers mods)
1423 RemoveSelectedNodes();
1427 bool FileCompile(MenuItem selection, Modifiers mods)
1430 OldList selectedRows;
1431 Project project = null;
1432 BuildOutputMode mode = selection ? (BuildOutputMode)selection.id : normal;
1433 List<ProjectNode> nodes { };
1434 fileList.GetMultiSelection(selectedRows);
1435 for(item = selectedRows.first; item; item = item.next)
1437 DataRow row = item.data;
1438 ProjectNode node = (ProjectNode)(intptr)row.tag;
1440 project = node.project;
1441 else if(node.project != project)
1448 selectedRows.Free(null);
1450 Compile(project, nodes, mode, normal);
1452 ide.outputView.buildBox.Logf($"Please select files from a single project.\n");
1457 bool FileClean(MenuItem selection, Modifiers mods)
1460 OldList selectedRows;
1461 Project project = null;
1462 List<ProjectNode> nodes { };
1463 fileList.GetMultiSelection(selectedRows);
1464 for(item = selectedRows.first; item; item = item.next)
1466 DataRow row = item.data;
1467 ProjectNode node = (ProjectNode)(intptr)row.tag;
1469 project = node.project;
1470 else if(node.project != project)
1477 selectedRows.Free(null);
1479 Clean(project, nodes);
1481 ide.outputView.buildBox.Logf($"Please select files from a single project.\n");
1486 bool FileDebugPrecompile(MenuItem selection, Modifiers mods)
1488 DataRow row = fileList.currentRow;
1489 ProjectNode node = row ? (ProjectNode)(intptr)row.tag : null;
1490 BuildOutputMode mode = selection ? (BuildOutputMode)selection.id : normal;
1493 List<ProjectNode> nodes { };
1495 if(node.type == project)
1496 ProjectBuild(selection, mods);
1499 Compile(node.project, nodes, mode, debugPrecompile);
1505 bool FileDebugCompile(MenuItem selection, Modifiers mods)
1507 DataRow row = fileList.currentRow;
1508 ProjectNode node = row ? (ProjectNode)(intptr)row.tag : null;
1509 BuildOutputMode mode = selection ? (BuildOutputMode)selection.id : normal;
1512 List<ProjectNode> nodes { };
1514 if(node.type == project)
1515 ProjectBuild(selection, mods);
1517 Compile(node.project, nodes, mode, normal);
1519 Compile(node.project, nodes, mode, debugCompile);
1525 bool FileDebugGenerateSymbols(MenuItem selection, Modifiers mods)
1527 DataRow row = fileList.currentRow;
1528 ProjectNode node = row ? (ProjectNode)(intptr)row.tag : null;
1529 BuildOutputMode mode = selection ? (BuildOutputMode)selection.id : normal;
1532 List<ProjectNode> nodes { };
1534 if(node.type == project)
1535 ProjectBuild(selection, mods);
1537 Compile(node.project, nodes, mode, normal);
1539 Compile(node.project, nodes, mode, debugGenerateSymbols);
1545 Project GetSelectedProject(bool useSelection)
1547 Project prj = project;
1550 DataRow row = fileList.currentRow;
1551 ProjectNode node = row ? (ProjectNode)(intptr)row.tag : null;
1558 void SelectNextProject(bool backwards)
1560 DataRow row = fileList.currentRow;
1562 row = backwards ? fileList.lastRow : fileList.firstRow;
1566 DataRow currentRow = row;
1567 ProjectNode node = (ProjectNode)(intptr)row.tag;
1568 if(node.type != project)
1569 row = node.project.topNode.row;
1571 row = row.previous ? row.previous : fileList.lastRow;
1573 row = row.next ? row.next : fileList.firstRow;
1574 if(row && row != currentRow)
1576 fileList.SelectRow(row);
1577 fileList.currentRow = row;
1582 ProjectNode GetSelectedNode(bool useSelection)
1584 ProjectNode node = null;
1587 DataRow row = fileList.currentRow;
1589 node = (ProjectNode)(intptr)row.tag;
1594 bool MenuBrowseFolder(MenuItem selection, Modifiers mods)
1596 char folder[MAX_LOCATION];
1598 ProjectNode node = GetSelectedNode(true);
1600 node = project.topNode;
1603 strcpy(folder, prj.topNode.path);
1604 if(node != prj.topNode)
1605 PathCatSlash(folder, node.path);
1610 bool Run(MenuItem selection, Modifiers mods)
1612 CompilerConfig compiler = ideConfig.compilers.GetCompilerConfig(ide.workspace.activeCompiler);
1613 ProjectConfig config = project.config;
1614 int bitDepth = ide.workspace.bitDepth;
1615 bool shellOpen = compiler.hasDocumentOutput;
1616 String args = new char[maxPathLen];
1618 if(ide.workspace.commandLineArgs)
1619 //ide.debugger.GetCommandLineArgs(args);
1620 strcpy(args, ide.workspace.commandLineArgs);
1621 if(ide.debugger.isActive)
1622 project.Run(args, compiler, config, bitDepth, shellOpen);
1623 /*else if(config.targetType == sharedLibrary || config.targetType == staticLibrary)
1624 MessageBox { master = ide, type = ok, text = "Run", contents = "Shared and static libraries cannot be run like executables." }.Modal();*/
1625 else if(BuildInterrim(project, run, compiler, config, bitDepth, normal))
1626 project.Run(args, compiler, config, bitDepth, shellOpen);
1634 bool result = false;
1635 CompilerConfig compiler = ideConfig.compilers.GetCompilerConfig(ide.workspace.activeCompiler);
1636 ProjectConfig config = project.config;
1637 int bitDepth = ide.workspace.bitDepth;
1638 bool useValgrind = ide.workspace.useValgrind;
1639 TargetTypes targetType = project.GetTargetType(config);
1640 if(targetType == sharedLibrary || targetType == staticLibrary)
1641 MessageBox { master = ide, type = ok, text = $"Run", contents = $"Shared and static libraries cannot be run like executables." }.Modal();
1642 else if(project.GetCompress(config))
1643 MessageBox { master = ide, text = $"Starting Debug", contents = $"Debugging compressed applications is not supported\n" }.Modal();
1644 else if(project.GetDebug(config) ||
1645 MessageBox { master = ide, type = okCancel, text = $"Starting Debug", contents = $"Attempting to debug non-debug configuration\nProceed anyways?" }.Modal() == ok)
1647 if(/*!IsProjectModified() ||*/ BuildInterrim(project, start, compiler, config, bitDepth, normal))
1649 if(compiler.type.isVC)
1651 //bool result = false;
1652 char oldwd[MAX_LOCATION];
1653 PathBackup pathBackup { };
1654 char command[MAX_LOCATION];
1656 ide.SetPath(false, compiler, config, bitDepth);
1658 GetWorkingDir(oldwd, sizeof(oldwd));
1659 ChangeWorkingDir(project.topNode.path);
1661 sprintf(command, "%s /useenv %s.sln /projectconfig \"%s|Win32\" /command \"%s\"" , "devenv", project.name, config.name, "Debug.Start");
1663 ChangeWorkingDir(oldwd);
1667 else if(compiler.hasDocumentOutput)
1669 project.Run("", compiler, config, bitDepth, true);
1673 ide.debugger.Start(compiler, config, bitDepth, useValgrind);
1682 void GoToError(const char * line, const bool noParsing, const char * objectFileExt)
1686 while(isspace(*line)) line++;
1687 colon = strstr(line, ":");
1692 bool lookForLineNumber = true;
1694 // deal with linking error
1695 if(colon && colon[1] == ' ')
1697 colon = strstr(colon + 1, ":");
1698 if(colon && !colon[1])
1700 colon = strstr(line, ":");
1701 lookForLineNumber = false;
1703 else if(colon && !isdigit(colon[1]))
1706 colon = strstr(line, ":");
1709 // Don't be mistaken by the drive letter colon
1710 if(colon && (colon[1] == '/' || colon[1] == '\\'))
1711 colon = strstr(colon + 1, ":");
1712 if(colon && lookForLineNumber)
1715 #if 0 // MSVS Errors -- todo fix this later
1716 char * par = RSearchString(line, "(", colon - line, true, false);
1717 if(par && strstr(par, ")"))
1719 else if((colon+1)[0] == ' ')
1721 // NOTE: This is the same thing as saying 'colon = line'
1722 for( ; colon != line; colon--)
1727 lineNumber = atoi(colon + 1);
1729 comma = strchr(colon, ',');
1731 col = atoi(comma+1);
1733 comma = strchr(colon+1, ':');
1735 col = atoi(comma+1);
1739 char moduleName[MAX_LOCATION], filePath[MAX_LOCATION] = "";
1740 char ext[MAX_EXTENSION] = "";
1741 ProjectNode node = null;
1744 const char * inFileIncludedFrom = strstr(line, stringInFileIncludedFrom);
1745 const char * from = strstr(line, "from ");
1746 const char * start = inFileIncludedFrom ? inFileIncludedFrom + strlen(stringInFileIncludedFrom) : from ? from + strlen("from ") : line;
1750 len = Min((int)(colon - start), MAX_LOCATION-1);
1752 strncpy(moduleName, start, len);
1753 moduleName[len] = '\0';
1756 strcpy(moduleName, line);
1761 if((msg = strstr(moduleName, " - ")))
1765 for(prj : ide.workspace.projects)
1767 strcpy(filePath, prj.topNode.path);
1768 PathCatSlash(filePath, moduleName);
1769 if(FileExists(filePath).isFile)
1781 else if((msg = strstr(moduleName, "...")) && (colon = strchr(moduleName, ' ')) && (++colon)[0])
1785 for(prj : ide.workspace.projects)
1787 if((node = prj.resNode.Find(colon, true)))
1789 strcpy(filePath, prj.topNode.path);
1790 PathCatSlash(filePath, node.path);
1791 PathCatSlash(filePath, node.name);
1805 // Remove stuff in brackets
1807 bracket = strstr(moduleName, "(");
1808 if(bracket) *bracket = '\0';
1810 MakeSlashPath(moduleName);
1811 GetExtension(moduleName, ext);
1813 if(!colon && !filePath[0])
1815 // Check if it's one of our modules
1816 node = project.topNode.Find(moduleName, false);
1819 strcpy(filePath, node.path);
1820 PathCatSlash(filePath, node.name);
1824 char ext[MAX_EXTENSION];
1825 GetExtension(moduleName, ext);
1827 DotMain dotMain = DotMain::FromFileName(moduleName);
1828 IntermediateFileType type = IntermediateFileType::FromExtension(ext);
1829 ProjectConfig config = null;
1832 for(prj : ide.workspace.projects; prj.lastBuildConfigName)
1834 if((config = prj.GetConfig(prj.lastBuildConfigName)))
1835 node = prj.FindNodeByObjectFileName(moduleName, type, dotMain, config, objectFileExt);
1842 char name[MAX_FILENAME];
1843 Project project = node.project;
1844 CompilerConfig compiler = ideConfig.compilers.GetCompilerConfig(project.lastBuildCompilerName);
1847 int bitDepth = ide.workspace.bitDepth;
1848 const char * objectFileExt = compiler ? compiler.objectFileExt : objectDefaultFileExt;
1849 DirExpression objDir = project.GetObjDir(compiler, config, bitDepth);
1850 strcpy(filePath, project.topNode.path);
1851 PathCatSlash(filePath, objDir.dir);
1852 node.GetObjectFileName(name, project.configsNameCollisions[config ? config.name : ""], type, dotMain, objectFileExt);
1853 PathCatSlash(filePath, name);
1863 if(!strcmp(ext, "a") || !strcmp(ext, "o") || !strcmp(ext, "lib") ||
1864 !strcmp(ext, "dll") || !strcmp(ext, "exe") || !strcmp(ext, "mo"))
1865 moduleName[0] = 0; // Avoid opening binary files
1868 CodeEditor codeEditor = null;
1870 if(ide.GoToCodeSelectFile(moduleName, null, project, null, filePath, objectFileExt))
1872 codeEditor = (CodeEditor)ide.OpenFile(filePath, false, true, null, no, normal, noParsing);
1879 strcpy(filePath, project.topNode.path);
1880 PathCatSlash(filePath, moduleName);
1883 codeEditor = (CodeEditor)ide.OpenFile(filePath, false, true, null, no, normal, noParsing);
1884 if(!codeEditor && !strcmp(ext, "c"))
1886 char ecName[MAX_LOCATION];
1887 ChangeExtension(filePath, "ec", ecName);
1888 codeEditor = (CodeEditor)ide.OpenFile(ecName, false, true, null, no, normal, noParsing);
1892 char path[MAX_LOCATION];
1893 // TOFIX: Improve on this, don't use only filename, make a function
1894 if(ide && ide.workspace)
1896 for(prj : ide.workspace.projects)
1899 MakePathRelative(filePath, prj.topNode.path, path);
1901 if((node = prj.topNode.FindWithPath(path, false)))
1903 strcpy(filePath, prj.topNode.path);
1904 PathCatSlash(filePath, node.path);
1905 PathCatSlash(filePath, node.name);
1906 codeEditor = (CodeEditor)ide.OpenFile(filePath, false, true, null, no, normal, noParsing);
1911 if(!codeEditor && (strchr(moduleName, '/') || strchr(moduleName, '\\')))
1913 for(prj : ide.workspace.projects)
1916 if((node = prj.topNode.FindWithPath(moduleName, false)))
1918 strcpy(filePath, prj.topNode.path);
1919 PathCatSlash(filePath, node.path);
1920 PathCatSlash(filePath, node.name);
1921 codeEditor = (CodeEditor)ide.OpenFile(filePath, false, true, null, no, normal, noParsing);
1929 GetLastDirectory(moduleName, moduleName);
1930 for(prj : ide.workspace.projects)
1933 if((node = prj.topNode.Find(moduleName, false)))
1935 strcpy(filePath, prj.topNode.path);
1936 PathCatSlash(filePath, node.path);
1937 PathCatSlash(filePath, node.name);
1938 codeEditor = (CodeEditor)ide.OpenFile(filePath, false, true, null, no, normal, noParsing);
1947 if(codeEditor && lineNumber)
1949 EditBox editBox = codeEditor.editBox;
1950 if(editBox.GoToLineNum(lineNumber - 1))
1951 editBox.GoToPosition(editBox.line, lineNumber - 1, col ? (col - 1) : 0);
1958 bool OpenNode(ProjectNode node, bool noParsing)
1960 char filePath[MAX_LOCATION];
1961 char ext[MAX_EXTENSION];
1962 node.GetFullFilePath(filePath, true);
1964 GetExtension(filePath, ext);
1965 if(binaryDocExt.Find(ext))
1967 ShellOpen(filePath);
1971 return ide.OpenFile(filePath, false, true/*false Why was it opening hidden?*/, null, something, normal, noParsing) ? true : false;
1974 void AddNode(ProjectNode node, DataRow addTo)
1976 DataRow row = addTo ? addTo.AddRow() : fileList.AddRow();
1978 row.tag = (int64)(intptr)node;
1981 if(node.type == resources)
1984 row.SetData(null, node);
1986 if(node.files && node.files.first && node.parent &&
1987 !(!node.parent.parent &&
1988 (!strcmpi(node.name, "notes") || !strcmpi(node.name, "sources") ||
1989 !strcmpi(node.name, "src") || !strcmpi(node.name, "tools"))))
1990 row.collapsed = true;
1991 else if(node.type == folder)
1992 node.icon = openFolder;
1996 for(child : node.files)
1997 AddNode(child, row);
2001 void DeleteNode(ProjectNode projectNode)
2003 if(projectNode.files)
2006 while((child = projectNode.files.first))
2009 fileList.DeleteRow(projectNode.row);
2010 projectNode.Delete();
2013 bool ProjectSave(MenuItem selection, Modifiers mods)
2015 DataRow row = fileList.currentRow;
2016 ProjectNode node = row ? (ProjectNode)(intptr)row.tag : null;
2017 Project prj = node ? node.project : null;
2020 prj.StopMonitoring();
2021 if(prj.Save(prj.filePath))
2023 Project modPrj = null;
2024 prj.topNode.modified = false;
2025 for(p : ide.workspace.projects)
2027 if(p.topNode.modified)
2034 modifiedDocument = false;
2037 prj.StartMonitoring();
2042 bool ShowOutputBuildLog(bool cleanLog)
2044 OutputView output = ide.outputView;
2046 output.ShowClearSelectTab(build);
2049 output.SelectTab(build);
2057 CompilerConfig compiler = ideConfig.compilers.GetCompilerConfig(ide.workspace.activeCompiler);
2058 ProjectConfig config = project.config;
2059 int bitDepth = ide.workspace.bitDepth;
2060 bool useValgrind = ide.workspace.useValgrind;
2062 bool result = false;
2063 if(/*!IsProjectModified() ||*/ BuildInterrim(project, restart, compiler, config, bitDepth, normal))
2065 // For Restart, compiler and config will only be used if for
2066 // whatever reason (if at all possible) the Debugger is in a
2067 // 'terminated' or 'none' state
2068 ide.debugger.Restart(compiler, config, bitDepth, useValgrind);
2078 ide.debugger.Resume();
2084 ide.debugger.Break();
2090 ide.debugger.Stop();
2094 bool DebugStepInto()
2096 CompilerConfig compiler = ideConfig.compilers.GetCompilerConfig(ide.workspace.activeCompiler);
2097 ProjectConfig config = project.config;
2098 int bitDepth = ide.workspace.bitDepth;
2099 bool useValgrind = ide.workspace.useValgrind;
2101 if((ide.debugger.isActive) || (!buildInProgress && BuildInterrim(project, start, compiler, config, bitDepth, normal)))
2102 ide.debugger.StepInto(compiler, config, bitDepth, useValgrind);
2107 bool DebugStepOver(bool skip)
2109 CompilerConfig compiler = ideConfig.compilers.GetCompilerConfig(ide.workspace.activeCompiler);
2110 ProjectConfig config = project.config;
2111 int bitDepth = ide.workspace.bitDepth;
2112 bool useValgrind = ide.workspace.useValgrind;
2114 if((ide.debugger.isActive) || (!buildInProgress && BuildInterrim(project, start, compiler, config, bitDepth, normal)))
2115 ide.debugger.StepOver(compiler, config, bitDepth, useValgrind, skip);
2121 bool DebugStepUntil(bool skip)
2123 CompilerConfig compiler = ideConfig.compilers.GetCompilerConfig(ide.workspace.activeCompiler);
2124 ProjectConfig config = project.config;
2125 int bitDepth = ide.workspace.bitDepth;
2126 bool useValgrind = ide.workspace.useValgrind;
2128 if((ide.debugger.isActive) || (!buildInProgress && BuildInterrim(project, start, compiler, config, bitDepth, normal)))
2129 ide.debugger.StepUntil(compiler, config, bitDepth, useValgrind, skip);
2135 bool DebugStepOut(bool skip)
2137 ide.debugger.StepOut(skip);
2141 void ImportFolder(ProjectNode toNode)
2145 char path[MAX_LOCATION];
2146 char currentDir[MAX_LOCATION];
2147 ProjectNode node = toNode;
2148 FileDialog fileDialog = importFileDialog;
2149 fileDialog.master = parent;
2152 node.GetFullFilePath(path, true);
2155 StripLastDirectory(path, path);
2156 if(FileExists(path).isDirectory) break;
2158 if(path[0] || node == toNode.project.topNode)
2161 node = toNode.project.topNode;
2163 MakeSystemPath(path);
2164 StripLastDirectory(path, currentDir);
2165 fileDialog.currentDirectory = currentDir[0] ? currentDir : path;
2166 fileDialog.filePath = path;
2167 if(fileDialog.Modal() == ok)
2169 ImportFolderFSI fsi { projectView = this };
2170 fsi.stack.Add(toNode);
2171 fsi.Iterate(fileDialog.filePath);
2177 ProjectNode NewFolder(ProjectNode parentNode, char * name, bool showProperties)
2181 ProjectNode folderNode;
2182 Project prj = parentNode.project;
2184 ProjectNode after = null;
2185 for(node : parentNode.files)
2187 if(node.type != folder)
2193 folderNode = parentNode.Add(prj, name, after, folder, folder, true);
2196 for(c = 0; c < 100; c++)
2199 sprintf(string, c ? "New Folder (%d)" : "New Folder", c);
2200 if((folderNode = parentNode.Add(prj, string, after, folder, folder, true)))
2207 NodeProperties nodeProperties;
2210 modifiedDocument = true;
2211 prj.topNode.modified = true;
2214 folderNode.row = parentNode.row.AddRowAfter(after ? after.row : null);
2215 folderNode.row.tag = (int64)(intptr)folderNode;
2217 folderNode.row.SetData(null, folderNode);
2218 fileList.currentRow = folderNode.row;
2222 nodeProperties = NodeProperties
2224 parent, this, mode = newFolder, node = folderNode;
2225 position = { position.x + 100, position.y + 100 };
2227 nodeProperties.Create(); // Modal?
2235 void AddFiles(bool resources)
2237 FileDialog fileDialog = (!resources) ? this.fileDialog : resourceFileDialog;
2238 fileDialog.type = multiOpen;
2239 fileDialog.text = !resources ? $"Add Files to Project" : $"Add Resources to Project";
2240 fileDialog.master = parent;
2242 if(fileDialog.Modal() == ok)
2245 DataRow row = fileList.currentRow;
2246 ProjectNode parentNode = (ProjectNode)(intptr)row.tag;
2247 bool addFailed = false;
2248 int numSelections = fileDialog.numSelections;
2249 const char * const * multiFilePaths = fileDialog.multiFilePaths;
2251 Array<String> nameConflictFiles { };
2253 for(c = 0; c < numSelections; c++)
2255 const char * filePath = multiFilePaths[c];
2256 FileAttribs exists = FileExists(filePath);
2257 bool addThisFile = true;
2259 if(exists.isDirectory)
2260 addThisFile = false;
2263 if(MessageBox { master = ide, type = yesNo, text = filePath,
2264 contents = $"File doesn't exist. Create?" }.Modal() == yes)
2266 File f = FileOpen(filePath, write);
2274 MessageBox { master = ide, type = ok, text = filePath,
2275 contents = $"Couldn't create file."}.Modal();
2276 addThisFile = false;
2283 /*addFailed = */if(!AddFile(parentNode, filePath, resources, false))//;
2285 nameConflictFiles.Add(CopyString(filePath));
2293 const char * part1 = $"The following file";
2294 const char * opt1 = $" was ";
2295 const char * opt2 = $"s were ";
2296 const char * part2 = $"not added because of identical file name conflict within the project.\n\n";
2298 len += strlen(part1);
2299 len += strlen(part2);
2300 len += nameConflictFiles.count > 1 ? strlen(opt2) : strlen(opt1);
2301 for(s : nameConflictFiles)
2302 len += strlen(s) + 1;
2303 message = new char[len + 1];
2304 strcpy(message, part1);
2305 strcat(message, nameConflictFiles.count > 1 ? opt2 : opt1);
2306 strcat(message, part2);
2307 for(s : nameConflictFiles)
2310 strcat(message, "\n");
2312 MessageBox { master = ide, type = ok, text = $"Name Conflict",
2313 contents = message }.Modal();
2316 nameConflictFiles.Free();
2317 delete nameConflictFiles;
2321 ProjectNode AddFile(ProjectNode parentNode, const char * filePath, bool resources, bool isTemporary)
2323 ProjectNode result = null;
2324 ProjectNode after = null;
2325 for(node : parentNode.files)
2327 if(node.type != folder && node.type != file && node.type)
2332 result = parentNode.Add(parentNode.project, filePath, after, file, NodeIcons::SelectFileIcon(filePath), !resources);
2338 modifiedDocument = true;
2339 parentNode.project.topNode.modified = true;
2340 parentNode.project.ModifiedAllConfigs(true, false, true, true);
2343 result.row = parentNode.row.AddRowAfter(after ? after.row : null);
2344 result.row.tag = (int64)(intptr)result;
2345 result.row.SetData(null, result);
2350 CodeEditor CreateNew(const char * upper, const char * lower, const char * base, char * className)
2352 CodeEditor codeEditor = null;
2353 ProjectNode projectNode;
2354 ProjectNode after = null;
2355 DataRow row = fileList.currentRow;
2356 ProjectNode parentNode;
2359 if(!row) row = project.topNode.row;
2361 parentNode = (ProjectNode)(intptr)row.tag;
2363 for(node : parentNode.files)
2365 if(node.type != folder && node.type != file && node.type)
2369 for(c = 1; c < 100; c++)
2372 sprintf(string, "%s%d.ec", lower, c);
2373 if((projectNode = parentNode.Add(project, string, after, file, genFile, true)))
2379 char filePath[MAX_LOCATION];
2381 modifiedDocument = true;
2382 project.topNode.modified = true;
2384 project.ModifiedAllConfigs(true, false, false, true);
2385 projectNode.row = parentNode.row.AddRowAfter(after ? after.row : null);
2386 projectNode.row.tag = (int64)(intptr)projectNode;
2388 projectNode.row.SetData(null, projectNode);
2389 fileList.currentRow = projectNode.row;
2391 strcpy(filePath, project.topNode.path);
2392 PathCat(filePath, projectNode.path);
2393 PathCat(filePath, projectNode.name);
2395 codeEditor = (CodeEditor)ide.FindWindow(filePath);
2398 Class baseClass = eSystem_FindClass(__thisModule, base);
2399 subclass(ClassDesignerBase) designerClass = eClass_GetDesigner(baseClass);
2402 codeEditor = (CodeEditor)ide.OpenFile(filePath, false, false, null, whatever, normal, false);
2403 strcpy(name, projectNode.name);
2404 sprintf(name, "%s%d", upper, c);
2406 strcpy(className, name);
2408 designerClass.CreateNew(codeEditor.editBox, codeEditor.clientSize, name, base);
2410 //codeEditor.modifiedDocument = false;
2411 //codeEditor.designer.modifiedDocument = false;
2413 //Code_EnsureUpToDate(codeEditor);
2416 else // TODO: fix no symbols generated when ommiting {} for following else
2418 codeEditor = (CodeEditor)ide.OpenFile(filePath, false, false, null, whatever, normal, false);
2420 ide.sheet.visible = true;
2421 ide.sheet.Activate();
2424 codeEditor.ViewDesigner();
2425 codeEditor.codeModified = true;
2432 // Returns true if we opened something
2433 bool OpenSelectedNodes(bool noParsing)
2435 bool result = false;
2439 fileList.GetMultiSelection(selection);
2440 for(item = selection.first; item; item = item.next)
2442 DataRow row = item.data;
2443 ProjectNode node = (ProjectNode)(intptr)row.tag;
2444 if(node.type == file)
2446 OpenNode(node, noParsing);
2450 selection.Free(null);
2451 ide.RepositionWindows(false);
2455 void RemoveSelectedNodes()
2460 fileList.GetMultiSelection(selection);
2462 // Remove children of parents we're deleting
2463 for(item = selection.first; item; item = next)
2466 DataRow row = item.data;
2467 ProjectNode n, node = (ProjectNode)(intptr)row.tag;
2468 bool remove = false;
2471 for(i = selection.first; i && !remove; i = i.next)
2473 ProjectNode iNode = (ProjectNode)(intptr)((DataRow)i.data).tag;
2474 for(n = node.parent; n; n = n.parent)
2484 selection.Delete(item);
2487 for(item = selection.first; item; item = item.next)
2489 DataRow row = item.data;
2490 ProjectNode node = (ProjectNode)(intptr)row.tag;
2491 ProjectNode resNode;
2492 for(resNode = node.parent; resNode; resNode = resNode.parent)
2493 if(resNode.type == resources)
2495 if(node.type == file)
2497 Project prj = node.project;
2499 modifiedDocument = true;
2500 prj.topNode.modified = true;
2502 prj.ModifiedAllConfigs(true, false, true, true);
2504 else if(node.type == folder)
2507 sprintf(message, $"Are you sure you want to remove the folder \"%s\"\n"
2508 "and all of its contents from the project?", node.name);
2509 if(MessageBox { master = ide, type = yesNo, text = $"Delete Folder", contents = message }.Modal() == yes)
2511 Project prj = node.project;
2512 if(node.containsFile)
2513 prj.ModifiedAllConfigs(true, false, true, true);
2515 modifiedDocument = true;
2516 prj.topNode.modified = true;
2519 else if(node.type == project && node != project.topNode && !buildInProgress)
2523 for(p : workspace.projects)
2525 if(p.topNode == node)
2531 sprintf(message, $"Are you sure you want to remove the \"%s\" project\n" "from this workspace?", node.name);
2532 if(MessageBox { master = ide, type = yesNo, text = $"Remove Project", contents = message }.Modal() == yes)
2537 workspace.RemoveProject(prj);
2538 //modifiedDocument = true; // when project view is a workspace view
2542 selection.Free(null);