ide: Fixed crashes with Quick Project
[sdk] / ide / src / dialogs / NewProjectDialog.ec
1 import "ide"
2
3 FileDialog fileDialog { type = selectDir, text = $"Select project directory" };
4
5 class NewProjectDialog : Window
6 {
7    background = formColor;
8    minClientSize = { 316, 170 };
9    maxClientSize = { 640, 170 };
10    borderStyle = sizable;
11    tabCycle = true;
12    hasClose = true;
13    text = $"New Project";
14
15    char path[MAX_LOCATION];
16    char name[MAX_FILENAME];
17
18    Project project;
19    Workspace workspace;
20
21    char projectLocation[MAX_LOCATION];
22    bool createFormOption;
23
24    PathBox locationEditBox
25    {
26       this, position = { 10, 80 }, size = { 120, 22 }, anchor = { left = 10, top = 80, right = 10 };
27       hotKey = altL, text = $"Location";
28       typeExpected = directory, browseDialog = fileDialog;
29
30       NotifyModified = NotifyModifiedLocation;
31    };
32    Label { this, position = { 10, 60 }, labeledWindow = locationEditBox };
33
34    DropBox targetType { this, position = { 10, 130 }, size = { 130 }, hotKey = altT, text = $"Target Type" };
35
36    Label { this, position = { 10, 110 }, labeledWindow = targetType };
37
38    Button okBtn
39    {
40       parent = this, isDefault = true, disabled = true, position = { 170, 130 }, size = { 60 }, text = $"OK";
41       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
42       {
43          const char * prjName = projectName.contents;
44          char filePath[MAX_LOCATION];
45          char extension[MAX_EXTENSION];
46          ProjectConfig debug, release;
47
48          strcpy(projectLocation, locationEditBox.slashPath);
49          strcpy(filePath, projectLocation);
50          PathCatSlash(filePath, prjName);
51          GetExtension(filePath, extension);
52          //if(!extension[0])
53             ChangeExtension(filePath, ProjectExtension, filePath);
54
55          debug = ProjectConfig
56          {
57             name = CopyString("Debug");
58             options =
59             {
60                optimization = none;
61                fastMath = false;
62                debug = true;
63                preprocessorDefinitions = { [ CopyString("_DEBUG") ] };
64             };
65             makingModified = true;
66             compilingModified = true;
67             linkingModified = true;
68          };
69
70          release = ProjectConfig
71          {
72             name = CopyString("Release");
73             options =
74             {
75                optimization = speed;
76                fastMath = true;
77                debug = false;
78             };
79             makingModified = true;
80             compilingModified = true;
81             linkingModified = true;
82          };
83
84          /* error: too few arguments to function ‘__ecereProp_DirExpression_Set_char__PTR_’ -- debug.objDir = "debug";*/
85
86          project = Project
87          {
88             property::filePath = filePath;
89             moduleName = CopyString(name);
90             topNode.type = NodeTypes::project;
91             config = debug;
92          };
93          /*project.topNode.options = */project.options =
94          {
95             warnings = all;
96             // TOFIX: Precomp problems withou the extra ( )
97             targetType = ((TargetTypes)targetType.GetTag());
98             targetFileName = /*CopyString(*/name/*)*/;
99          };
100          if(project.options.targetType != staticLibrary)
101          {
102             project.options.libraries = { [ CopyString("ecere") ] };
103          }
104
105          project.topNode.configurations = /*project.configurations = */{ [ debug, release ] };
106          project.resNode = project.topNode.Add(project, "Resources", null, resources, archiveFile, false);
107
108          createFormOption = createForm.checked;
109
110          Destroy(DialogResult::ok);
111          return true;
112       }
113    };
114
115    void CreateNewProject()
116    {
117       char fileName[MAX_LOCATION];  // Windows Friendly path
118       ProjectView projectWindow;
119       Project prj = project;
120
121       if(!FileExists(projectLocation).isDirectory)
122       {
123          if(MessageBox { type = yesNo, master = ide,
124                text = $"Directory doesn't exist", contents = $"Create directory?" }.Modal() == yes)
125          {
126             if(!MakeDir(projectLocation))
127             {
128                MessageBox { type = ok, master = ide, text = projectLocation, contents = $"Error creating directory" }.Modal();
129                return;
130             }
131          }
132          else
133             return;
134       }
135
136       GetSystemPathBuffer(fileName, prj.filePath);
137
138       if(FileExists(prj.filePath))
139       {
140          if(MessageBox { type = yesNo, master = ide,
141                text = $"Project Already Exists", contents = $"Replace existing project?" }.Modal() == no)
142             return;
143       }
144
145       {
146          char workspaceFile[MAX_LOCATION];
147          strcpy(workspaceFile, prj.filePath);
148          ChangeExtension(workspaceFile, WorkspaceExtension, workspaceFile);
149          workspace = Workspace { activeCompiler = ideSettings.defaultCompiler, workspaceFile = workspaceFile };
150          workspace.Init();
151       }
152       workspace.AddProject(prj, null);
153       ide.findInFilesDialog.AddProjectItem(prj);
154       ide.findInFilesDialog.mode = FindInFilesMode::project;
155       ide.findInFilesDialog.currentDirectory = prj.topNode.path;
156
157       if(!prj.Save(prj.filePath))
158       {
159          MessageBox { type = ok, master = this, text = prj.filePath, contents = $"Error writing project file" }.Modal();
160          delete prj;
161          return;
162       }
163
164       projectWindow = ide.CreateProjectView(workspace, fileName);
165
166       {
167          char newWorkingDir[MAX_LOCATION];
168          StripLastDirectory(prj.filePath, newWorkingDir);
169
170          ide.ChangeFileDialogsDirectory(newWorkingDir, false);
171
172          StripLastDirectory(newWorkingDir, newWorkingDir);
173          ide.ChangeProjectFileDialogDirectory(newWorkingDir);
174       }
175       ide.toolBox.visible = true;
176
177       if(createFormOption)
178       {
179          char className[256];
180          char varName[256];
181          CodeEditor codeEditor = projectWindow.CreateNew("Form", "form", "Window", className);
182          EditBox editBox = codeEditor.editBox;
183          strcpy(varName, className);
184          if(islower(varName[0]))
185          {
186             memmove(varName+1, varName, strlen(varName)+1);
187             varName[0] = '_';
188          }
189          else
190             varName[0] = (char)tolower(varName[0]);
191
192          editBox.End();
193          editBox.Printf("\n%s %s {};\n", className, varName);
194
195          codeEditor.EnsureUpToDate();
196
197          prj.Save(prj.filePath);
198
199          projectWindow.modifiedDocument = false;
200          prj.topNode.modified = false;
201
202          /*
203          editBox.Printf("class FormApplication : GuiApplication\n");
204          editBox.Printf("{\n");
205          editBox.Printf("   %s %s {};\n", className, varName);
206          editBox.Printf("}\n");
207          editBox.Home();
208          */
209       }
210       prj.StartMonitoring();
211
212       if(prj && projectWindow)
213       {
214          CompilerConfig compiler = ideConfig.compilers.GetCompilerConfig(ide.workspace.activeCompiler);
215          ProjectConfig config = prj.config;
216          projectWindow.ShowOutputBuildLog(true);
217          projectWindow.DisplayCompiler(compiler, false);
218          projectWindow.ProjectPrepareCompiler(prj, compiler, false);
219          projectWindow.ProjectPrepareMakefile(prj, force, compiler, config);
220          delete compiler;
221
222          ide.UpdateToolBarActiveConfigs(false);
223       }
224    }
225
226    Button
227    {
228       parent = this, position = { 240, 130 }, size = { 60 }, hotKey = escape, text = $"Cancel";
229       NotifyClicked = ButtonCloseDialog;
230    };
231    Button createForm
232    {
233       parent = this, text = $"Create Form", hotKey = altF, position = { 200, 30 };
234       isCheckbox = true, checked = true;
235    };
236
237    EditBox projectName
238    {
239       parent = this, textHorzScroll = true, position = { 10, 30 }, size = { 160 };
240       hotKey = altP, text = $"Project Name";
241       NotifyUpdate = EditBoxUpdate;
242
243       bool NotifyModified(EditBox editBox)
244       {
245          char name[MAX_FILENAME];
246          char tmp[MAX_FILENAME];
247          char lastPart[MAX_LOCATION];
248          const char * text = editBox.contents;
249
250          // drop leading path stuff that has no business here
251          GetLastDirectory(text, tmp);
252          ValidProjectNameBufCopy(name, tmp);
253
254          GetLastDirectory(path, lastPart);
255          if(name[0] && (!strcmp(path, lastPart) || !path[0] || !strcmp(this.name, lastPart)))
256          {
257             if(strcmp(path, lastPart))
258                StripLastDirectory(path, path);
259             PathCatSlash(path, name);
260             locationEditBox.path = path;
261          }
262          else if(!name[0] && !strcmp(this.name, lastPart))
263          {
264             if(strcmp(path, lastPart))
265                StripLastDirectory(path, path);
266             locationEditBox.path = path;
267          }
268          else if(strcmp(name, lastPart))
269          {
270             PathCatSlash(path, name);
271             locationEditBox.path = path;
272          }
273          strcpy(this.name, name);
274          editBox.contents = name;
275          return true;
276       }
277    };
278    Label { this, position = { 10, 10 }, labeledWindow = projectName };
279
280    void EditBoxUpdate(EditBox editBox)
281    {
282       okBtn.disabled = !(locationEditBox.path[0] && projectName.contents[0]);
283    }
284
285    bool NotifyModifiedLocation(PathBox pathBox)
286    {
287       char location[MAX_LOCATION];
288       char lastPart[MAX_FILENAME];
289       char * text;
290
291       BasicValidatePathBoxPath(pathBox);
292
293       text = pathBox.slashPath;
294
295       //replacing this: NotifyUpdate = EditBoxUpdate;
296       okBtn.disabled = !(text[0] && projectName.contents[0]);
297
298       strcpy(location, ideSettings.ideProjectFileDialogLocation);
299       PathCatSlash(location, text);
300
301       GetLastDirectory(path, lastPart);
302       /*if(text[0] && (!name[0] || !strcmp(lastPart, name)))
303       {
304          char newName[MAX_FILENAME];
305          GetLastDirectory(location, newName);
306          if(strcmp(newName, location))
307          {
308             strcpy(name, newName);
309             projectName.contents = name;
310          }
311       }*/
312       strcpy(path, location);
313       pathBox.path = path;
314       return true;
315    }
316
317    NewProjectDialog()
318    {
319       char location[MAX_LOCATION];
320
321       strcpy(location, ideSettings.ideProjectFileDialogLocation);
322
323       locationEditBox.path = location;
324       strcpy(path, location);
325
326       {
327          DataRow row;
328
329          //targetType.AddField("String", null, 0);
330
331          row = targetType.AddRow();
332          row.tag = TargetTypes::executable;
333          row.SetData(null, $"Executable");
334
335          row = targetType.AddRow();
336          row.tag = TargetTypes::sharedLibrary;
337          row.SetData(null, $"Shared Library");
338
339          row = targetType.AddRow();
340          row.tag = TargetTypes::staticLibrary;
341          row.SetData(null, $"Static Library");
342
343          targetType.currentRow = targetType.FindRow(TargetTypes::executable);
344       }
345    }
346 }
347
348 class QuickProjectDialog : Window
349 {
350    background = formColor;
351    minClientSize = { 316, 110 };
352    maxClientSize = { 640, 110 };
353    borderStyle = sizable;
354    tabCycle = true;
355    hasClose = true;
356    text = $"Quick Project";
357
358    char path[MAX_LOCATION];
359    char name[MAX_FILENAME];
360
361    Label message { this, position = { 10, 10 }, text = $"Do you want to quickly create a temporary project?" };
362
363    DropBox targetType { this, position = { 10, 70 }, size = { 130 }, hotKey = altT, text = $"Target Type" };
364    Label { this, position = { 10, 50 }, labeledWindow = targetType };
365
366    Button okBtn
367    {
368       parent = this, isDefault = true, position = { 170, 70 }, size = { 60 }, text = $"OK";
369       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
370       {
371          char tempDir[MAX_LOCATION] = "";
372          char filePath[MAX_LOCATION];
373          char prjName[MAX_LOCATION] = "quick_project";
374          Project project;
375          Workspace workspace;
376          ProjectView projectWindow;
377          ProjectConfig debug, release;
378
379          /*if(!*/CreateTemporaryDir(tempDir, "ecereide");/*)
380          {
381             MessageBox { type = ok, master = this, text = tempDir, contents = "Error creating temporary directory." }.Modal();
382             return true;
383          }*/
384
385          GetLastDirectory(tempDir, prjName);
386          {
387             char * dot;
388             if((dot = strstr(prjName, ".")))
389                dot[0] = '_';
390          }
391
392          if(!FileExists(tempDir).isDirectory)
393          {
394             MessageBox { type = ok, master = this, text = tempDir, contents = $"Temporary directory does not exist." }.Modal();
395             return true;
396          }
397
398          ide.tmpPrjDir = tempDir;
399
400          strcpy(filePath, tempDir);
401          PathCatSlash(filePath, prjName);
402          //GetExtension(filePath, extension);
403          //if(!extension[0])
404             ChangeExtension(filePath, ProjectExtension, filePath);
405          GetSystemPathBuffer(filePath, filePath);
406
407          debug = ProjectConfig
408          {
409             name = CopyString("Debug");
410             options =
411             {
412                optimization = none;
413                fastMath = false;
414                debug = true;
415                preprocessorDefinitions = { [ CopyString("_DEBUG") ] };
416             };
417             makingModified = true;
418             compilingModified = true;
419             linkingModified = true;
420          };
421
422          release = ProjectConfig
423          {
424             name = CopyString("Release");
425             options =
426             {
427                optimization = speed;
428                fastMath = true;
429                debug = false;
430             };
431             makingModified = true;
432             compilingModified = true;
433             linkingModified = true;
434          };
435
436          project = Project
437          {
438             filePath = filePath;
439             moduleName = CopyString(prjName);
440             topNode.type = NodeTypes::project;
441             config = debug;
442          };
443          /*project.topNode.options = */project.options =
444          {
445             warnings = all;
446             // TOFIX: Precomp problems withou the extra ( )
447             targetType = ((TargetTypes)targetType.GetTag());
448             targetFileName = /*CopyString(*/prjName/*)*/;
449          };
450
451          if(project.options.targetType != staticLibrary)
452          {
453             project.options.libraries = { [ CopyString("ecere") ] };
454          }
455
456          {
457             char workspaceFile[MAX_LOCATION];
458             strcpy(workspaceFile, filePath);
459             ChangeExtension(workspaceFile, WorkspaceExtension, workspaceFile);
460             workspace = Workspace { activeCompiler = ideSettings.defaultCompiler, workspaceFile = workspaceFile };
461          }
462
463          workspace.Init();
464          workspace.AddProject(project, null);
465          ide.findInFilesDialog.AddProjectItem(project);
466          ide.findInFilesDialog.mode = FindInFilesMode::project;
467          ide.findInFilesDialog.currentDirectory = project.topNode.path;
468
469          // *** Don't set this directly on project, it must be set on top ProjectNode ***
470          project.topNode.configurations = { [ debug, release ] };
471
472          project.resNode = project.topNode.Add(project, "Resources", null, resources, archiveFile, false);
473
474          if(!project.Save(filePath))
475          {
476             MessageBox { type = ok, master = this, text = filePath, contents = $"Error writing project file" }.Modal();
477             delete project;
478             delete workspace;
479             ide.DestroyTemporaryProjectDir();
480             return true;
481          }
482
483          projectWindow = ide.CreateProjectView(workspace, filePath);
484
485          {
486             Window document = null;
487             for(document = ide.firstChild; document; document = document.next)
488             {
489                if(document.created && document.isDocument && document._class == class(CodeEditor))
490                {
491                   const char * fileName = document.fileName;
492                   if(strstr(fileName, "http://") == fileName)
493                   {
494                      char name[MAX_LOCATION];
495                      char newFileName[MAX_LOCATION];
496                      GetLastDirectory(fileName, name);
497                      strcpy(newFileName, tempDir);
498                      PathCat(newFileName, name);
499
500                      // TODO: this should be in Windows::SaveAs(char* asFileName)
501                      // start
502                      //document.saving = true;
503                      if(document.OnSaveFile(newFileName))
504                      {
505                         document.fileName = newFileName;
506                         document.NotifySaved(document.master, /*this*/document, newFileName);
507                      }
508                      //document.saving = false;
509                      // end
510                      // TODO else MessageBox unable to save and cancel the whole project creation thing
511                      fileName = document.fileName;
512                   }
513                   if(fileName)
514                   {
515                      CodeEditor codeEditor = (CodeEditor)document;
516                      ide.projectView.AddFile(project.topNode, fileName, false, false);
517                      codeEditor.AdjustDebugMenus();
518                   }
519                }
520             }
521          }
522
523          ide.toolBox.visible = true;
524
525          if(project.topNode.modified)
526          {
527             project.Save(filePath);
528             projectWindow.modifiedDocument = false;
529             project.topNode.modified = false;
530          }
531          else
532          {
533             // TOCHECK: Why is the Quick project creating a form???
534             char className[256];
535             char varName[256];
536             CodeEditor codeEditor = projectWindow.CreateNew("Form", "form", "Window", className);
537             EditBox editBox = codeEditor.editBox;
538             strcpy(varName, className);
539             if(islower(varName[0]))
540             {
541                memmove(varName+1, varName, strlen(varName)+1);
542                varName[0] = '_';
543             }
544             else
545                varName[0] = (char)tolower(varName[0]);
546
547             editBox.End();
548             editBox.Printf("\n%s %s {};\n", className, varName);
549
550             codeEditor.EnsureUpToDate();
551
552             // TODO: how to save that new file?
553
554             project.Save(filePath);
555             projectWindow.modifiedDocument = false;
556             project.topNode.modified = false;
557          }
558          project.StartMonitoring();
559
560          visible = false;
561
562          if(project && projectWindow)
563          {
564             CompilerConfig compiler = ideConfig.compilers.GetCompilerConfig(ide.workspace.activeCompiler);
565             ProjectConfig config = project.config;
566             projectWindow.ShowOutputBuildLog(true);
567             projectWindow.DisplayCompiler(compiler, false);
568             projectWindow.ProjectPrepareCompiler(project, compiler, false);
569             projectWindow.ProjectPrepareMakefile(project, force, compiler, config);
570             delete compiler;
571          }
572
573          ide.projectView.ProjectBuild(null, Modifiers { });
574
575          Destroy(0);
576          return true;
577       }
578    };
579
580    Button
581    {
582       parent = this, position = { 240, 70 }, size = { 60 }, hotKey = escape, text = $"Cancel";
583       NotifyClicked = ButtonCloseDialog;
584    };
585
586    QuickProjectDialog()
587    {
588       DataRow row;
589
590       row = targetType.AddRow();
591       row.tag = TargetTypes::executable;
592       row.SetData(null, $"Executable");
593
594       row = targetType.AddRow();
595       row.tag = TargetTypes::sharedLibrary;
596       row.SetData(null, $"Shared Library");
597
598       row = targetType.AddRow();
599       row.tag = TargetTypes::staticLibrary;
600       row.SetData(null, $"Static Library");
601
602       targetType.currentRow = targetType.FindRow(TargetTypes::executable);
603    }
604
605    bool OnPostCreate()
606    {
607       okBtn.Activate();
608       return true;
609    }
610 }
611
612 void ValidProjectNameBufCopy(char *output, const char *input)
613 {
614    strcpy(output, input);
615    TrimLSpaces(output, output);
616    TrimRSpaces(output, output);
617    {
618       // todo: support '&', '.' and ' ' in project name on windows so that it may be used by all platforms.
619       const char * chars = "*|:\",<>?\\/&. ";
620       char ch, * s = output, * o = output;
621       while((ch = *s++)) { if(!strchr(chars, ch)) *o++ = ch; }
622       *o = '\0';
623    }
624 }