ide: Fixed DynamicString memory leak on 'passArgs'
[sdk] / ide / src / ide.ec
1 #ifdef ECERE_STATIC
2 public import static "ecere"
3 public import static "ec"
4 #else
5 public import "ecere"
6 public import "ec"
7 #endif
8
9 import "GlobalSettingsDialog"
10 import "NewProjectDialog"
11 import "FindInFilesDialog"
12
13 #ifdef GDB_DEBUG_GUI
14 import "GDBDialog"
15 #endif
16
17 import "Project"
18 import "ProjectConfig"
19 import "ProjectNode"
20 import "NodeProperties"
21 import "ProjectSettings"
22 import "ProjectView"
23 import "Workspace"
24
25 import "CodeEditor"
26 import "Designer"
27 import "ToolBox"
28 import "Sheet"
29
30 import "Debugger"
31
32 import "OutputView"
33 import "BreakpointsView"
34 import "CallStackView"
35 import "ThreadsView"
36 import "WatchesView"
37
38 #ifndef NO3D
39 import "ModelView"
40 #endif
41 import "PictureEdit"
42
43 import "about"
44
45 import "FileSystemIterator"
46
47 #if defined(__WIN32__)
48 define pathListSep = ";";
49 #else
50 define pathListSep = ":";
51 #endif
52
53 define maxPathLen = 65 * MAX_LOCATION;
54
55 class PathBackup : struct
56 {
57    String oldLDPath;
58    String oldPath;
59
60    PathBackup()
61    {
62       oldPath = new char[maxPathLen];
63       oldLDPath = new char[maxPathLen];
64
65       GetEnvironment("PATH", oldPath, maxPathLen);
66 #if defined(__APPLE__)
67       GetEnvironment("DYLD_LIBRARY_PATH", oldLDPath, maxPathLen);
68 #else
69       GetEnvironment("LD_LIBRARY_PATH", oldLDPath, maxPathLen);
70 #endif
71    }
72
73    ~PathBackup()
74    {
75       SetEnvironment("PATH", oldPath);
76 #if defined(__APPLE__)
77       SetEnvironment("DYLD_LIBRARY_PATH", oldLDPath);
78 #else
79       SetEnvironment("LD_LIBRARY_PATH", oldLDPath);
80 #endif
81       delete oldPath;
82       delete oldLDPath;
83    }
84 };
85
86 enum OpenCreateIfFails { no, yes, something, whatever };
87 enum OpenMethod { normal, add };
88
89 static Array<FileFilter> fileFilters
90 { [
91    { $"C/C++/eC Files (*.ec, *.eh, *.c, *.cpp, *.cc, *.cxx, *.h, *.hpp, *.hh, *.hxx)", "ec, eh, c, cpp, cc, cxx, h, hpp, hh, hxx" },
92    { $"Header Files for eC/C/C++ (*.eh, *.h, *.hpp, *.hh, *.hxx)", "eh, h, hpp, hh, hxx" },
93    { $"C/C++/eC Source Files (*.ec, *.c, *.cpp, *.cc, *.cxx)", "ec, c, cpp, cc, cxx" },
94    { $"Text files (*.txt, *.text, *.nfo, *.info)", "txt, text, nfo, info" },
95    { $"Web files (*.html, *.htm, *.xhtml, *.css, *.php, *.js, *.jsi, *.rb, *.xml)", "html, htm, xhtml, css, php, js, jsi, rb, xml" },
96    { $"Image Files (*.jpg, *.jpeg, *.bmp, *.pcx, *.png, *.gif)", "jpg, jpeg, bmp, pcx, png, gif" },
97    { $"3D Studio Model Files (*.3ds)", "3ds" },
98    { $"All files", null }
99 ] };
100
101 static Array<FileType> fileTypes
102 { [
103    { $"Based on extension", null },
104    { $"Text",               "txt" },
105    { $"Image",              "jpg" },
106    { $"3D Studio Model",    "3ds" }
107 ] };
108
109 static Array<FileFilter> projectFilters
110 { [
111    { $"Project Files (*.epj)", ProjectExtension }
112 ] };
113
114 static Array<FileType> projectTypes
115 { [
116    { $"Project File", ProjectExtension }
117 ] };
118
119 static Array<FileFilter> findInFilesFileFilters
120 { [
121    { $"eC Files (*.ec, *.eh)", "ec, eh" },
122    { $"C/C++/eC Files (*.ec, *.eh, *.c, *.cpp, *.cc, *.cxx, *.h, *.hpp, *.hh, *.hxx)", "ec, eh, c, cpp, cc, cxx, h, hpp, hh, hxx" },
123    { $"Header Files for eC/C/C++ (*.eh, *.h, *.hpp, *.hh, *.hxx)", "eh, h, hpp, hh, hxx" },
124    { $"C/C++/eC Source Files (*.ec, *.c, *.cpp, *.cc, *.cxx)", "ec, c, cpp, cc, cxx" },
125    { $"Text files (*.txt)", "txt" },
126    { $"All files", null }
127 ] };
128
129 FileDialog ideFileDialog
130 {
131    type = multiOpen, text = $"Open";
132    types = fileTypes.array, sizeTypes = fileTypes.count * sizeof(FileType), filters = fileFilters.array, sizeFilters = fileFilters.count * sizeof(FileFilter);
133 };
134
135 define openProjectFileDialogTitle = $"Open Project";
136 define addProjectFileDialogTitle = $"Open Additional Project";
137 FileDialog ideProjectFileDialog
138 {
139    type = open;
140    types = projectTypes.array, sizeTypes = projectTypes.count * sizeof(FileType), filters = projectFilters.array, sizeFilters = projectFilters.count * sizeof(FileFilter);
141 };
142
143 GlobalSettingsDialog globalSettingsDialog
144 {
145    ideSettings = ideSettings;
146    settingsContainer = settingsContainer;
147
148    void OnGlobalSettingChange(GlobalSettingsChange globalSettingsChange)
149    {
150       switch(globalSettingsChange)
151       {
152          case editorSettings:
153          {
154             Window child;
155             for(child = ide.firstChild; child; child = child.next)
156             {
157                if(child._class == class(CodeEditor))
158                {
159                   CodeEditor codeEditor = (CodeEditor) child;
160                   codeEditor.editBox.freeCaret = ideSettings.useFreeCaret;
161                   // codeEditor.editBox.lineNumbers = ideSettings.showLineNumbers;
162                   codeEditor.editBox.caretFollowsScrolling = ideSettings.caretFollowsScrolling;
163                   codeEditor.OnPostCreate(); // Update editBox margin size
164                }
165             }
166             break;
167          }
168          case projectOptions:
169             break;
170          case compilerSettings:
171          {
172             ide.UpdateCompilerConfigs(true);
173             break;
174          }
175       }
176    }
177 };
178
179 void DrawLineMarginIcon(Surface surface, BitmapResource resource, int line, int lineH, int scrollY, int boxH)
180 {
181    int lineY;
182    if(line)
183    {
184       lineY = (line - 1) * lineH;
185       if(lineY + lineH > scrollY && lineY + lineH < scrollY + boxH)
186       {
187          Bitmap bitmap = resource.bitmap;
188          if(bitmap)
189             surface.Blit(bitmap, 0, lineY - scrollY + (lineH - bitmap.height) / 2 + 1, 0, 0, bitmap.width, bitmap.height);
190       }
191    }
192 }
193
194 #define IDEItem(x)   (&((IDEWorkSpace)0).x)
195
196 class IDEToolbar : ToolBar
197 {
198    /* File options */
199    // New
200    ToolButton buttonNewFile { this, toolTip = $"New file", menuItemPtr = IDEItem(fileNewItem) };
201    // Open
202    ToolButton buttonOpenFile { this, toolTip = $"Open file", menuItemPtr = IDEItem(fileOpenItem) };
203    // Close
204    // ToolButton buttonCloseFile { this, toolTip = $"Close file", menuItemPtr = IDEItem(fileCloseItem) };
205    // Save
206    ToolButton buttonSaveFile { this, toolTip = $"Save file", menuItemPtr = IDEItem(fileSaveItem) };
207    // Save All
208    ToolButton buttonSaveAllFile { this, toolTip = $"Save all", menuItemPtr = IDEItem(fileSaveAllItem) };
209
210    ToolSeparator separator1 { this };
211
212    /* Edit options */
213    // Cut
214    // Copy 
215    // Paste
216    // Undo
217    // Redo
218
219    // ToolSeparator separator2 { this };
220
221    /* Project  options */
222    // New project
223    ToolButton buttonNewProject { this, toolTip = $"New project", menuItemPtr = IDEItem(projectNewItem) };
224    // Open project
225    ToolButton buttonOpenProject { this, toolTip = $"Open project", menuItemPtr = IDEItem(projectOpenItem) };
226    // Add project to workspace
227    ToolButton buttonAddProject { this, toolTip = $"Add project to workspace", menuItemPtr = IDEItem(projectAddItem), disabled = true; };
228    // Close project
229    // ToolButton buttonCloseProject { this, toolTip = $"Close project", menuItemPtr = IDEItem(projectCloseItem), disabled = true; };
230
231    ToolSeparator separator3 { this };
232
233    /* Build/Execution options */
234    // Build
235    ToolButton buttonBuild { this, toolTip = $"Build project", menuItemPtr = IDEItem(projectBuildItem), disabled = true; };
236    // Re-link
237    ToolButton buttonReLink { this, toolTip = $"Relink project", menuItemPtr = IDEItem(projectLinkItem), disabled = true; };
238    // Rebuild
239    ToolButton buttonRebuild { this, toolTip = $"Rebuild project", menuItemPtr = IDEItem(projectRebuildItem), disabled = true; };
240    // Clean
241    ToolButton buttonClean { this, toolTip = $"Clean project", menuItemPtr = IDEItem(projectCleanItem), disabled = true; };
242    // Real Clean
243    // ToolButton buttonRealClean { this, toolTip = $"Real clean project", menuItemPtr = IDEItem(projectRealCleanItem), disabled = true; };
244    // Regenerate Makefile
245    ToolButton buttonRegenerateMakefile { this, toolTip = $"Regenerate Makefile", menuItemPtr = IDEItem(projectRegenerateItem), disabled = true; };
246    // Compile actual file
247    // Execute
248    ToolButton buttonRun { this, toolTip = $"Run", menuItemPtr = IDEItem(projectRunItem), disabled = true; };
249
250    ToolSeparator separator4 { this };
251
252    /* Debug options */
253    // Start/Resume
254    ToolButton buttonDebugStartResume { this, toolTip = $"Start", menuItemPtr = IDEItem(debugStartResumeItem), disabled = true; };
255    // Restart
256    ToolButton buttonDebugRestart { this, toolTip = $"Restart", menuItemPtr = IDEItem(debugRestartItem), disabled = true; };
257    // Pause
258    ToolButton buttonDebugPause { this, toolTip = $"Break", menuItemPtr = IDEItem(debugBreakItem), disabled = true; };
259    // Stop
260    ToolButton buttonDebugStop { this, toolTip = $"Stop", menuItemPtr = IDEItem(debugStopItem), disabled = true; };
261    // Breakpoints
262    //ToolButton buttonRun { this, toolTip = $"Run", menuItemPtr = IDEItem(projectRunItem) };
263    // F11
264    ToolButton buttonDebugStepInto { this, toolTip = $"Step Into", menuItemPtr = IDEItem(debugStepIntoItem), disabled = true; };
265    // F10
266    ToolButton buttonDebugStepOver { this, toolTip = $"Step Over", menuItemPtr = IDEItem(debugStepOverItem), disabled = true; };
267    // Shift+F11
268    ToolButton buttonDebugStepOut { this, toolTip = $"Step Out", menuItemPtr = IDEItem(debugStepOutItem), disabled = true; };
269    // Shift+F10
270    ToolButton buttonDebugSkipStepOver { this, toolTip = $"Step Over Skipping Breakpoints", menuItemPtr = IDEItem(debugSkipStepOverItem), disabled = true; };
271
272    ToolSeparator separator5 { this };
273
274    Window spacer5 { this, size = { 4 } };
275
276    DropBox activeConfig
277    {
278       this, toolTip = $"Active Configuration(s)", size = { 160 }, disabled = true;
279       bool NotifySelect(DropBox dropBox, DataRow row, Modifiers mods)
280       {
281          if(row)
282          {
283             for(prj : ide.workspace.projects)
284             {
285                for(cfg : prj.configurations)
286                {
287                   if(cfg.name && !strcmp(cfg.name, row.string))
288                   {
289                      prj.config = cfg;
290                      break;
291                   }
292                }
293             }
294             ide.UpdateToolBarActiveConfigs(true);
295             ide.projectView.Update(null);
296          }
297          return true;
298       }
299    };
300
301    Window spacer6 { this, size = { 4 } };
302
303    DropBox activeCompiler
304    {
305       this, toolTip = $"Active Compiler", size = { 160 }, disabled = true;
306       bool NotifySelect(DropBox dropBox, DataRow row, Modifiers mods)
307       {
308          if(ide.workspace && ide.projectView && row && strcmp(row.string, ide.workspace.compiler))
309          {
310             bool silent = ide.projectView.buildInProgress == none ? false : true;
311             CompilerConfig compiler = ideSettings.GetCompilerConfig(row.string);
312             ide.workspace.compiler = row.string;
313             ide.projectView.ShowOutputBuildLog(!silent);
314             if(!silent)
315                ide.projectView.DisplayCompiler(compiler, false);
316             for(prj : ide.workspace.projects)
317                ide.projectView.ProjectPrepareCompiler(prj, compiler, silent);
318             delete compiler;
319             ide.workspace.Save();
320          }
321          return true;
322       }
323    };
324
325    DropBox activeBitDepth
326    {
327       this, toolTip = $"Active Bit Depth", size = { 60 }, disabled = true;
328       bool NotifySelect(DropBox dropBox, DataRow row, Modifiers mods)
329       {
330          if(ide.workspace && ide.projectView && row)
331          {
332             bool silent = ide.projectView.buildInProgress == none ? false : true;
333             CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
334             ide.workspace.bitDepth = (int)row.tag;
335             ide.projectView.ShowOutputBuildLog(!silent);
336             if(!silent)
337                ide.projectView.DisplayCompiler(compiler, false);
338             for(prj : ide.workspace.projects)
339                ide.projectView.ProjectPrepareCompiler(prj, compiler, silent);
340             delete compiler;
341             ide.workspace.Save();
342          }
343          return true;
344       }
345    };
346
347    Window spacer7 { this, size = { 4 } };
348
349    void IDEToolbar()
350    {
351       DataRow row;
352       row = activeBitDepth.AddString("Auto");
353       row.tag = 0;
354       activeBitDepth.AddString("32 bit").tag = 32;
355       activeBitDepth.AddString("64 bit").tag = 64;
356       activeBitDepth.currentRow = row;
357    }
358
359 }
360
361 class IDEMainFrame : Window
362 {
363    background = formColor;
364    borderStyle = sizable;
365    hasMaximize = true;
366    hasMinimize = true;
367    hasClose = true;
368    minClientSize = { 600, 300 };
369    hasMenuBar = true;
370    icon = { ":icon.png" };
371    text = titleECEREIDE;
372 #if 0 //def _DEBUG
373    //stayOnTop = true;
374    size = { 800, 600 };
375    anchor = { top = 0, right = 0, bottom = 0 };
376 #else
377    state = maximized;
378    anchor = { left = 0, top = 0, right = 0, bottom = 0 };
379 #endif
380
381    Stacker stack
382    {
383       this;
384       menu = { };
385       isActiveClient = true;
386       gap = 0;
387       direction = vertical;
388       background = formColor;
389       anchor = { left = 0, top = 0, right = 0, bottom = 0 };
390    };
391    IDEToolbar toolBar
392    {
393       stack, ideWorkSpace;
394
395       void OnDestroy(void)
396       {
397          ((IDEWorkSpace)master).toolBar = null;
398       }
399    };
400    IDEWorkSpace ideWorkSpace { stack, this, toolBar = toolBar };
401 }
402
403 define ide = ideMainFrame.ideWorkSpace;
404
405 class IDEWorkSpace : Window
406 {
407    background = Color { 85, 85, 85 };
408
409    //tabCycle = true;
410    hasVertScroll = true;
411    hasHorzScroll = true;
412    hasStatusBar = true;
413    isActiveClient = true;
414    anchor = { left = 0, top = 0, right = 0, bottom = 0 };
415    menu = Menu {  };
416    IDEToolbar toolBar;
417
418    MenuItem * driverItems, * skinItems;
419    StatusField pos { width = 150 };
420    StatusField ovr, caps, num;
421
422    BitmapResource back                 { ":ecereBack.jpg", window = this };
423    BitmapResource bmpBp                { ":codeMarks/breakpoint.png", window = this };
424    BitmapResource bmpBpDisabled        { ":codeMarks/breakpointDisabled.png", window = this };
425    BitmapResource bmpBpHalf            { ":codeMarks/breakpointHalf.png", window = this };
426    BitmapResource bmpBpHalfDisabled    { ":codeMarks/breakpointHalfDisabled.png", window = this };
427    BitmapResource bmpCursor            { ":codeMarks/cursor.png", window = this };
428    BitmapResource bmpCursorError       { ":codeMarks/cursorError.png", window = this };
429    BitmapResource bmpTopFrame          { ":codeMarks/topFrame.png", window = this };
430    BitmapResource bmpTopFrameError     { ":codeMarks/topFrameError.png", window = this };
431    BitmapResource bmpTopFrameHalf      { ":codeMarks/topFrameHalf.png", window = this };
432    BitmapResource bmpTopFrameHalfError { ":codeMarks/topFrameHalfError.png", window = this };
433    
434    Debugger debugger { };
435
436    ProjectView projectView;
437
438    OutputView outputView
439    {
440       parent = this;
441
442       void OnGotoError(char * line)
443       {
444          ide.GoToError(line);
445       }
446
447       void OnCodeLocationParseAndGoTo(char * line)
448       {
449          ide.CodeLocationParseAndGoTo(line, ide.findInFilesDialog.findProject, ide.findInFilesDialog.findDir);
450       }
451
452       bool OnKeyDown(Key key, unichar ch)
453       {
454          switch(key)
455          {
456             case escape: 
457                if(activeBox != findBox || !ide.findInFilesDialog || !ide.findInFilesDialog.SearchAbort())
458                   ide.ShowCodeEditor(); 
459                break;
460             default:
461             {
462                OutputView::OnKeyDown(key, ch);
463                break;
464             }
465          }
466          return true;
467       }
468
469       bool OnActivate(bool active, Window previous, bool * goOnWithActivation, bool direct)
470       {
471          if(active)
472             ide.RepositionWindows(false);
473          return true;
474       }
475
476       bool OnClose(bool parentClosing)
477       {
478          visible = false;
479          if(!parentClosing)
480             ide.RepositionWindows(false);
481          return parentClosing;
482       }
483    };
484
485    CallStackView callStackView
486    {
487       parent = this, font = { panelFont.faceName, panelFont.size };
488
489       void OnGotoLine(char * line)
490       {
491          int stackLvl;
492          stackLvl = atoi(line);
493          ide.debugger.GoToStackFrameLine(stackLvl, true);
494       }
495
496       void OnSelectFrame(int lineNumber)
497       {
498          ide.debugger.SelectFrame(lineNumber);
499       }
500
501       void OnToggleBreakpoint()
502       {
503          Debugger debugger = ide.debugger;
504          if(debugger.activeFrame && debugger.activeFrame.absoluteFile)
505          {
506             int line = debugger.activeFrame.line;
507             char name[MAX_LOCATION];
508             Project prj = null;
509             // TOFIX: Improve on this, don't use only filename, make a function
510             GetLastDirectory(debugger.activeFrame.absoluteFile, name);
511             if(ide && ide.workspace)
512             {
513                for(p : ide.workspace.projects)
514                {
515                   if(p.topNode.Find(name, false))
516                   {
517                      prj = p;
518                      break;
519                   }
520                }
521                if(!prj)
522                {
523                   for(p : ide.workspace.projects)
524                   {
525                      if(IsPathInsideOf(debugger.activeFrame.absoluteFile, p.topNode.path))
526                      {
527                         prj = p;
528                         break;
529                      }
530                   }
531                }
532             }
533             debugger.ToggleBreakpoint(debugger.activeFrame.absoluteFile, line, prj);
534             Update(null);
535             {
536                CodeEditor codeEditor = (CodeEditor)ide.FindWindow(debugger.activeFrame.absoluteFile);
537                if(codeEditor) { codeEditor.Update(null); Activate(); }
538             }
539          }
540       }
541
542       bool OnKeyDown(Key key, unichar ch)
543       {
544          switch(key)
545          {
546             case escape: ide.ShowCodeEditor(); break;
547          }
548          return true;
549       }
550
551       bool OnActivate(bool active, Window previous, bool * goOnWithActivation, bool direct)
552       {
553          if(active)
554             ide.RepositionWindows(false);
555          return true;
556       }
557
558       bool OnClose(bool parentClosing)
559       {
560          visible = false;
561          if(!parentClosing)
562             ide.RepositionWindows(false);
563          return parentClosing;
564       }
565
566       void OnRedraw(Surface surface)
567       {
568          Debugger debugger = ide.debugger;
569          Frame activeFrame = debugger.activeFrame;
570          if(activeFrame)
571          {
572             bool error;
573             int lineCursor, lineTopFrame, activeThread, hitThread;
574             int lineH, scrollY, boxH;
575             BitmapResource bmp;
576             Breakpoint bp = null;
577
578             boxH = clientSize.h;
579             scrollY = editBox.scroll.y;
580             displaySystem.FontExtent(editBox.font.font, " ", 1, null, &lineH);
581             activeThread = debugger.activeThread;
582             hitThread = debugger.hitThread;
583             debugger.GetCallStackCursorLine(&error, &lineCursor, &lineTopFrame);
584
585             // TODO: improve bp drawing... it should be visible even if it's not on the activeFrame
586             if(activeFrame.absoluteFile)
587             {
588                for(i : ide.workspace.breakpoints; i.type == user)
589                {
590                   if(i.absoluteFilePath && i.absoluteFilePath[0] &&
591                      !fstrcmp(i.absoluteFilePath, activeFrame.absoluteFile) &&
592                      activeFrame.line == i.line)
593                   {
594                      bp = i;
595                      break;
596                   }
597                }
598             }
599             if(bp)
600                DrawLineMarginIcon(surface,
601                      /*(lineCursor == 1 || lineTopFrame == 1) ? */ide.bmpBpHalf/* : ide.bmpBp*/,
602                      lineCursor /*1*/, lineH, scrollY, boxH);
603             /*
604             if(activeThread && activeThread == hitThread && debugger.bpHit && debugger.bpHit.type == user)
605                DrawLineMarginIcon(surface,
606                      (lineCursor == 1 || lineTopFrame == 1) ? ide.bmpBpHalf : ide.bmpBp,
607                      1, lineH, scrollY, boxH);
608             */
609             DrawLineMarginIcon(surface, error ? ide.bmpCursorError : ide.bmpCursor, lineCursor, lineH, scrollY, boxH);
610             if(bp && lineCursor == 1) //activeThread && activeThread == hitThread && debugger.bpHit && debugger.bpHit.type == user)
611                bmp = error ? ide.bmpTopFrameHalfError : ide.bmpTopFrameHalf;
612             else
613                bmp = error ? ide.bmpTopFrameError : ide.bmpTopFrame;
614             DrawLineMarginIcon(surface, bmp, lineTopFrame, lineH, scrollY, boxH);
615          }
616          if(editBox.horzScroll && editBox.horzScroll.visible)
617          {
618             surface.SetBackground(control);
619             surface.Area(0, editBox.clientSize.h, 20 - 1, clientSize.h - 1);
620          }
621       }
622    };
623    
624    WatchesView watchesView { parent = this };
625    ThreadsView threadsView
626    {
627       parent = this, font = { panelFont.faceName, panelFont.size };
628
629       bool OnKeyDown(Key key, unichar ch)
630       {
631          switch(key)
632          {
633             case escape: ide.ShowCodeEditor(); break;
634          }
635          return true;
636       }
637
638       bool OnActivate(bool active, Window previous, bool * goOnWithActivation, bool direct)
639       {
640          if(active)
641             ide.RepositionWindows(false);
642          return true;
643       }
644
645       bool OnClose(bool parentClosing)
646       {
647          visible = false;
648          if(!parentClosing)
649             ide.RepositionWindows(false);
650          return parentClosing;
651       }
652
653       void OnSelectThread(int threadId)
654       {
655          if(threadId)
656             ide.debugger.SelectThread(threadId);
657       }
658
659       bool OnGetThreadsInfo(int * activeThread, int * hitThread, int * signalThread)
660       {
661          bool result = false;
662          Debugger debugger = ide.debugger;
663          *activeThread = debugger.activeThread;
664          *hitThread = debugger.hitThread;
665          *signalThread = debugger.signalThread;
666          result = true;
667          return result;
668       }
669    };
670    BreakpointsView breakpointsView { parent = this };
671
672    ToolBox toolBox { parent = this };
673    Sheet sheet { parent = this };
674
675    char * tmpPrjDir;
676    property char * tmpPrjDir { set { delete tmpPrjDir; if(value) tmpPrjDir = CopyString(value); } get { return tmpPrjDir; } };
677
678    Menu fileMenu { menu, $"File", f, hasMargin = true };
679       MenuItem fileNewItem
680       {
681          fileMenu, $"New", n, ctrlN;
682          bitmap = { ":actions/docNew.png" };
683          bool NotifySelect(MenuItem selection, Modifiers mods)
684          {
685             Window document = (Window)NewCodeEditor(this, normal, false);
686             document.NotifySaved = DocumentSaved;
687             return true;
688          }
689       }
690       MenuItem fileOpenItem
691       {
692          fileMenu, $"Open...", o, ctrlO;
693          bitmap = { ":actions/docOpen.png" };
694          bool NotifySelect(MenuItem selection, Modifiers mods)
695          {
696             if(!projectView && ideSettings.ideFileDialogLocation)
697                ideFileDialog.currentDirectory = ideSettings.ideFileDialogLocation;
698             for(;;)
699             {
700                if(ideFileDialog.Modal() == ok)
701                {
702                   bool gotWhatWeWant = false;
703                   int c;
704                   int numSelections = ideFileDialog.numSelections;
705                   char ** multiFilePaths = ideFileDialog.multiFilePaths;
706
707                   for(c = 0; c < numSelections; c++)
708                   {
709                      if(OpenFile(multiFilePaths[c], normal, true, fileTypes[ideFileDialog.fileType].typeExtension, no, normal))
710                         gotWhatWeWant = true;
711                   }
712                   if(gotWhatWeWant ||
713                      MessageBox { type = yesNo, master = this, text = $"Error opening file", 
714                      contents = $"Open a different file?" }.Modal() == no)
715                   {
716                      if(!projectView && gotWhatWeWant)
717                         ChangeFileDialogsDirectory(ideFileDialog.currentDirectory, true);
718                      break;
719                   }
720                }
721                else
722                   break;
723             }
724             return true;
725          }
726       }
727       MenuItem fileCloseItem { fileMenu, $"Close", c, ctrlF4, NotifySelect = MenuFileClose };
728       MenuDivider { fileMenu };
729       MenuItem fileSaveItem
730       {
731          fileMenu, $"Save", s, ctrlS, bitmap = { ":actions/docSave.png" };
732
733          // For the toolbar button; clients can still override that for the menu item
734          bool Window::NotifySelect(MenuItem selection, Modifiers mods)
735          {
736             Window w = activeClient;
737             if(w)
738                w.MenuFileSave(null, 0);
739             return true;
740          }
741       };
742       MenuItem fileSaveAsItem { fileMenu, $"Save As...", a };
743       MenuItem fileSaveAllItem { fileMenu, $"Save All", l, NotifySelect = MenuFileSaveAll, bitmap = { ":actions/docSaveAll.png" } };
744       MenuDivider { fileMenu };
745       MenuItem findInFiles
746       {
747          fileMenu, $"Find In Files...", f, Key { f, ctrl = true , shift = true };
748          bool NotifySelect(MenuItem selection, Modifiers mods)
749          {
750             findInFilesDialog.replaceMode = false;
751             findInFilesDialog.Show();
752             return true;
753          }
754       }
755       MenuItem replaceInFiles
756       {
757          fileMenu, $"Replace In Files...", e, Key { r, ctrl = true , shift = true };
758          bool NotifySelect(MenuItem selection, Modifiers mods)
759          {
760             findInFilesDialog.replaceMode = true;
761             findInFilesDialog.Show();
762             return true;
763          }
764       }
765       MenuDivider { fileMenu };
766       MenuItem globalSettingsItem
767       {
768          fileMenu, $"Global Settings...", g;
769          bool NotifySelect(MenuItem selection, Modifiers mods)
770          {
771             globalSettingsDialog.master = this;
772             if(ide.workspace && ide.workspace.compiler)
773                globalSettingsDialog.workspaceActiveCompiler = ide.workspace.compiler;
774             else if(ideSettings.defaultCompiler)
775                globalSettingsDialog.workspaceActiveCompiler = ideSettings.defaultCompiler;
776             globalSettingsDialog.Modal();
777             return true;
778          }
779       }
780       MenuDivider { fileMenu };
781       Menu recentFiles { fileMenu, $"Recent Files", r };
782       Menu recentProjects { fileMenu, $"Recent Projects", p };
783       MenuDivider { fileMenu };
784       MenuItem exitItem
785       {
786          fileMenu, $"Exit", x, altF4, NotifySelect = MenuFileExit;
787
788          bool NotifySelect(MenuItem selection, Modifiers mods)
789          {
790             ideMainFrame.Destroy(0);
791             return true;
792          }
793       };
794
795       bool FileRecentFile(MenuItem selection, Modifiers mods)
796       {
797          int id = 0;
798          for(file : ideSettings.recentFiles)
799          {
800             if(id == selection.id)
801             {
802                if(mods.ctrl)
803                {
804                   char * command = PrintString("ide ", file);
805                   Execute(command);
806                   delete command;
807                }
808                else
809                   OpenFile(file, normal, true, null, no, normal);
810                break;
811             }
812             id++;
813          }
814          return true;
815       }
816
817       bool FileRecentProject(MenuItem selection, Modifiers mods)
818       {
819          int id = 0;
820          for(file : ideSettings.recentProjects)
821          {
822             if(id == selection.id)
823             {
824                if(mods.ctrl)
825                {
826                   char * command = PrintString("ide ", file);
827                   Execute(command);
828                   delete command;
829                }
830                else
831                   OpenFile(file, normal, true, null, no, normal);
832                break;
833             }
834             id++;
835          }
836          return true;
837       }
838
839    MenuPlacement editMenu { menu, $"Edit", e };
840    
841    Menu projectMenu { menu, $"Menu"."Project", p, hasMargin = true };
842       MenuItem projectNewItem
843       {
844          projectMenu, $"New...", n, Key { n, true, true };
845          bitmap = { ":actions/projNew.png" };
846          bool NotifySelect(MenuItem selection, Modifiers mods)
847          {
848             if(!DontTerminateDebugSession($"New Project"))
849                if(MenuWindowCloseAll(null, 0))
850                {
851                   NewProjectDialog newProjectDialog;
852
853                   if(projectView)
854                   {
855                      projectView.visible = false;
856                      if(!projectView.Destroy(0))
857                         return true;
858                   }
859                   
860                   newProjectDialog = { master = this };
861                   newProjectDialog.Modal();
862                   if(projectView)
863                   {
864                      ideSettings.AddRecentProject(projectView.fileName);
865                      ide.UpdateRecentMenus();
866                      settingsContainer.Save();
867                   }
868                }
869             return true;
870          }
871       }
872       MenuItem projectOpenItem
873       {
874          projectMenu, $"Open...", o, Key { o, true, true };
875          bitmap = { ":actions/projOpen.png" };
876          bool NotifySelect(MenuItem selection, Modifiers mods)
877          {
878             if(ideSettings.ideProjectFileDialogLocation)
879                ideProjectFileDialog.currentDirectory = ideSettings.ideProjectFileDialogLocation;
880
881             ideProjectFileDialog.text = openProjectFileDialogTitle;
882             if(ideProjectFileDialog.Modal() == ok)
883             {
884                OpenFile(ideProjectFileDialog.filePath, normal, true, projectTypes[ideProjectFileDialog.fileType].typeExtension, no, normal);
885                //ChangeProjectFileDialogDirectory(ideProjectFileDialog.currentDirectory);
886             }
887             return true;
888          }
889       }
890       MenuItem projectQuickItem
891       {
892          projectMenu, $"Quick...", q, f7, disabled = true;
893          bool NotifySelect(MenuItem selection, Modifiers mods)
894          {
895             if(!projectView)
896                QuickProjectDialog { this }.Modal();
897             return true;
898          }
899       }
900       MenuItem projectAddItem
901       {
902          projectMenu, $"Add project to workspace...", a, Key { a, true, true };
903          bitmap = { ":actions/projAdd.png" };
904          disabled = true;
905          bool NotifySelect(MenuItem selection, Modifiers mods)
906          {
907             if(ideSettings.ideProjectFileDialogLocation)
908                ideProjectFileDialog.currentDirectory = ideSettings.ideProjectFileDialogLocation;
909
910             ideProjectFileDialog.text = addProjectFileDialogTitle;
911             for(;;)
912             {
913                if(ideProjectFileDialog.Modal() == ok)
914                {
915                   if(OpenFile(ideProjectFileDialog.filePath, normal, true, projectTypes[ideProjectFileDialog.fileType].typeExtension, no, add))
916                      break;
917                   if(MessageBox { type = yesNo, master = this, text = $"Error opening project file", 
918                         contents = $"Add a different project?" }.Modal() == no)
919                   {
920                      break;
921                   }
922                }
923                else
924                   break;
925             }
926             return true;
927          }
928       }
929       MenuItem projectCloseItem
930       {
931          projectMenu, $"Close", c, disabled = true;
932          bool NotifySelect(MenuItem selection, Modifiers mods)
933          {
934             if(projectView)
935             {
936                if(!ide.DontTerminateDebugSession($"Project Close"))
937                {
938                   if(findInFilesDialog)
939                      findInFilesDialog.SearchStop();
940                   projectView.visible = false;
941                   if(projectView.Destroy(0))
942                      MenuWindowCloseAll(null, 0);
943                   {
944                      char workingDir[MAX_LOCATION];
945                      GetWorkingDir(workingDir, MAX_LOCATION);
946                      findInFilesDialog.currentDirectory = workingDir;
947                      ideMainFrame.text = titleECEREIDE;
948                   }
949                   ide.AdjustMenus();
950                }
951             }
952             return true;
953          }
954       }
955       MenuDivider { projectMenu };
956       MenuItem projectSettingsItem
957       {
958          projectMenu, $"Settings...", s, altF7, disabled = true;
959          bool NotifySelect(MenuItem selection, Modifiers mods)
960          {
961             projectView.MenuSettings(projectView.active ? selection : null, mods);
962             return true;
963          }
964       }
965       MenuDivider { projectMenu };
966       MenuItem projectBrowseFolderItem
967       {
968          projectMenu, $"Browse Project Folder", p, disabled = true;
969          bool NotifySelect(MenuItem selection, Modifiers mods)
970          {
971             if(projectView)
972                projectView.MenuBrowseFolder(null, mods);
973             return true;
974          }
975       }
976       MenuDivider { projectMenu };
977       MenuItem projectRunItem
978       {
979          projectMenu, $"Run", r, ctrlF5, disabled = true;
980          bitmap = { ":actions/run.png" };
981          bool NotifySelect(MenuItem selection, Modifiers mods)
982          {
983             if(projectView)
984                projectView.Run(null, mods);
985             return true;
986          }
987       }
988       MenuItem projectBuildItem
989       {
990          projectMenu, $"Build", b, f7, disabled = true;
991          bitmap = { ":actions/build.png" };
992          bool NotifySelect(MenuItem selection, Modifiers mods)
993          {
994             if(projectView)
995             {
996                if(projectView.buildInProgress == none)
997                   projectView.ProjectBuild(projectView.active ? selection : null, mods);
998                else
999                   projectView.stopBuild = true;
1000             }
1001             return true;
1002          }
1003       }
1004       MenuItem projectLinkItem
1005       {
1006          projectMenu, $"Relink", l, disabled = true;
1007          bitmap = { ":actions/relink.png" };
1008          bool NotifySelect(MenuItem selection, Modifiers mods)
1009          {
1010             if(projectView)
1011                projectView.ProjectLink(projectView.active ? selection : null, mods);
1012             return true;
1013          }
1014       }
1015       MenuItem projectRebuildItem
1016       {
1017          projectMenu, $"Rebuild", d, shiftF7, disabled = true;
1018          bitmap = { ":actions/rebuild.png" };
1019          bool NotifySelect(MenuItem selection, Modifiers mods)
1020          {
1021             if(projectView)
1022                projectView.ProjectRebuild(projectView.active ? selection : null, mods);
1023             return true;
1024          }
1025       }
1026       MenuItem projectCleanItem
1027       {
1028          projectMenu, $"Clean", e, disabled = true;
1029          bitmap = { ":actions/clean.png" };
1030          bool NotifySelect(MenuItem selection, Modifiers mods)
1031          {
1032             if(projectView)
1033             {
1034                debugger.Stop();
1035                projectView.ProjectClean(projectView.active ? selection : null, mods);
1036             }
1037             return true;
1038          }
1039       }
1040       MenuItem projectRealCleanItem
1041       {
1042          projectMenu, $"Real Clean", disabled = true;
1043          bitmap = { ":actions/clean.png" };
1044          bool NotifySelect(MenuItem selection, Modifiers mods)
1045          {
1046             if(projectView)
1047             {
1048                debugger.Stop();
1049                projectView.ProjectRealClean(projectView.active ? selection : null, mods);
1050             }
1051             return true;
1052          }
1053       }
1054       MenuItem projectRegenerateItem
1055       {
1056          projectMenu, $"Regenerate Makefile", m, disabled = true;
1057          bitmap = { ":actions/regMakefile.png" };
1058          bool NotifySelect(MenuItem selection, Modifiers mods)
1059          {
1060             if(projectView)
1061                projectView.ProjectRegenerate(projectView.active ? selection : null, mods);
1062             return true;
1063          }
1064       }
1065       MenuItem projectCompileItem;
1066    Menu debugMenu { menu, $"Debug", d, hasMargin = true };
1067       MenuItem debugStartResumeItem
1068       {
1069          debugMenu, $"Start", s, f5, disabled = true;
1070          bitmap = { ":actions/debug.png" };
1071          NotifySelect = MenuDebugStart;
1072       }
1073       bool MenuDebugStart(MenuItem selection, Modifiers mods)
1074       {
1075          if(projectView)
1076          {
1077             debugStartResumeItem.disabled = true; // a very rare exception to calling AdjustDebugMenus
1078             if(!projectView.DebugStart())
1079                debugStartResumeItem.disabled = false; // same exception
1080          }
1081          return true;
1082       }
1083       bool MenuDebugResume(MenuItem selection, Modifiers mods)
1084       {
1085          if(projectView)
1086             projectView.DebugResume();
1087          return true;
1088       }
1089       MenuItem debugRestartItem
1090       {
1091          debugMenu, $"Restart", r, Key { f5, ctrl = true, shift = true }, disabled = true;
1092          bitmap = { ":actions/restart.png" };
1093          bool NotifySelect(MenuItem selection, Modifiers mods)
1094          {
1095             if(projectView)
1096                projectView.DebugRestart();
1097             return true;
1098          }
1099       }
1100       MenuItem debugBreakItem
1101       {
1102          debugMenu, $"Break", b, Key { pauseBreak, ctrl = true }, disabled = true;
1103          bitmap = { ":actions/pause.png" };
1104          bool NotifySelect(MenuItem selection, Modifiers mods)
1105          {
1106             if(projectView && projectView.buildInProgress != none)
1107                return true;
1108             if(projectView)
1109                projectView.DebugBreak();
1110             return true;
1111          }
1112       }
1113       MenuItem debugStopItem
1114       {
1115          debugMenu, $"Stop", p, shiftF5, disabled = true;
1116          bitmap = { ":actions/stopDebug.png" };
1117          bool NotifySelect(MenuItem selection, Modifiers mods)
1118          {
1119             if(projectView)
1120                projectView.DebugStop();
1121             return true;
1122          }
1123       }
1124       MenuDivider { debugMenu };
1125       MenuItem debugStepIntoItem
1126       {
1127          debugMenu, $"Step Into", i, f11, disabled = true;
1128          bitmap = { ":actions/stepInto.png" };
1129          bool NotifySelect(MenuItem selection, Modifiers mods)
1130          {
1131             if(projectView)
1132                projectView.DebugStepInto();
1133             return true;
1134          }
1135       }
1136       MenuItem debugStepOverItem
1137       {
1138          debugMenu, $"Step Over", v, f10, disabled = true;
1139          bitmap = { ":actions/stepOver.png" };
1140          bool NotifySelect(MenuItem selection, Modifiers mods)
1141          {
1142             if(projectView)
1143                projectView.DebugStepOver(false);
1144             return true;
1145          }
1146       }
1147       MenuItem debugStepOutItem
1148       {
1149          debugMenu, $"Step Out", o, shiftF11, disabled = true;
1150          bitmap = { ":actions/stepOut.png" };
1151          bool NotifySelect(MenuItem selection, Modifiers mods)
1152          {
1153             if(projectView)
1154                projectView.DebugStepOut(false);
1155             return true;
1156          }
1157       }
1158       MenuPlacement debugRunToCursorItem { debugMenu, $"Run To Cursor", c };
1159       MenuItem debugSkipStepOverItem
1160       {
1161          debugMenu, $"Step Over Skipping Breakpoints", e, shiftF10, disabled = true;
1162          bool NotifySelect(MenuItem selection, Modifiers mods)
1163          {
1164             if(projectView)
1165                projectView.DebugStepOver(true);
1166             return true;
1167          }
1168       }
1169       MenuItem debugSkipStepOutItem
1170       {
1171          debugMenu, $"Step Out Skipping Breakpoints", t, Key { f11, ctrl = true, shift = true }, disabled = true;
1172          bitmap = { ":actions/skipBreaks.png" };
1173          bool NotifySelect(MenuItem selection, Modifiers mods)
1174          {
1175             if(projectView)
1176                projectView.DebugStepOut(true);
1177             return true;
1178          }
1179       }
1180       MenuPlacement debugSkipRunToCursorItem { debugMenu, $"Run To Cursor Skipping Breakpoints", u };
1181       //MenuDivider { debugMenu };
1182       //MenuPlacement debugToggleBreakpoint { debugMenu, "Toggle Breakpoint", t };
1183    MenuPlacement imageMenu { menu, $"Image", i };
1184    Menu viewMenu { menu, $"View", v };
1185       MenuItem viewProjectItem
1186       {
1187          viewMenu, $"Project View", j, alt0, disabled = true;
1188          bool NotifySelect(MenuItem selection, Modifiers mods)
1189          {
1190             if(projectView)
1191             {
1192                projectView.visible = true;
1193                projectView.Activate();
1194             }
1195             return true;
1196          }
1197       }
1198       MenuPlacement { viewMenu, $"View Designer" };
1199       MenuPlacement { viewMenu, $"View Code" };
1200       MenuPlacement { viewMenu, $"View Properties" };
1201       MenuPlacement { viewMenu, $"View Methods" };
1202       MenuItem viewDesignerItem
1203       {
1204          viewMenu, $"View Designer", d, f8;
1205          bool NotifySelect(MenuItem selection, Modifiers mods)
1206          {
1207             Window client = activeClient;
1208             Class dataType = client._class;
1209             if(!strcmp(dataType.name, "Designer"))
1210             {
1211                client.visible = true;
1212                client.Activate();
1213             }
1214             else
1215                ((CodeEditor)client).ViewDesigner();
1216             return true;
1217          }
1218       }
1219       MenuItem viewCodeItem
1220       {
1221          viewMenu, $"View Code", c, f8;
1222          bool NotifySelect(MenuItem selection, Modifiers mods)
1223          {
1224             Window client = activeClient;
1225             Class dataType = client._class;
1226             if(!strcmp(dataType.name, "Designer"))
1227                client = ((Designer)client).codeEditor;
1228
1229             client.Activate();
1230             // Do this after so the caret isn't moved yet...
1231             client.visible = true;
1232             return true;
1233          }
1234       }
1235       MenuItem viewPropertiesItem
1236       {
1237          viewMenu, $"View Properties", p, f4;
1238          bool NotifySelect(MenuItem selection, Modifiers mods)
1239          {
1240             sheet.visible = true;
1241             sheet.sheetSelected = properties;
1242             sheet.Activate();
1243             return true;
1244          }
1245       }
1246       MenuItem viewMethodsItem
1247       {
1248          viewMenu, $"View Methods", m, f4;
1249          bool NotifySelect(MenuItem selection, Modifiers mods)
1250          {
1251             sheet.visible = true;
1252             sheet.sheetSelected = methods;
1253             sheet.Activate();
1254             return true;
1255          }
1256       }
1257       MenuItem viewToolBoxItem
1258       {
1259          viewMenu, $"View Toolbox", x, f12;
1260          bool NotifySelect(MenuItem selection, Modifiers mods)
1261          {
1262             toolBox.visible = true;
1263             toolBox.Activate();
1264             return true;
1265          }
1266       }
1267       MenuItem viewOutputItem
1268       {
1269          viewMenu, $"Output", o, alt2;
1270          bool NotifySelect(MenuItem selection, Modifiers mods)
1271          {
1272             outputView.Show();
1273             return true;
1274          }
1275       }
1276       MenuItem viewWatchesItem
1277       {
1278          viewMenu, $"Watches", w, alt3;
1279          bool NotifySelect(MenuItem selection, Modifiers mods)
1280          {
1281             watchesView.Show();
1282             return true;
1283          }
1284       }
1285       MenuItem viewThreadsItem
1286       {
1287          viewMenu, $"Threads", t, alt4;
1288          bool NotifySelect(MenuItem selection, Modifiers mods)
1289          {
1290             threadsView.Show();
1291             return true;
1292          }
1293       }
1294       MenuItem viewBreakpointsItem
1295       {
1296          viewMenu, $"Breakpoints", b, alt5;
1297          bool NotifySelect(MenuItem selection, Modifiers mods)
1298          {
1299             breakpointsView.Show();
1300             return true;
1301          }
1302       }
1303       MenuItem viewCallStackItem
1304       {
1305          viewMenu, $"Call Stack", s, alt7;
1306          bool NotifySelect(MenuItem selection, Modifiers mods)
1307          {
1308             callStackView.Show();
1309             return true;
1310          }
1311       }
1312       MenuItem viewAllDebugViews
1313       {
1314          viewMenu, $"All Debug Views", a, alt9;
1315          bool NotifySelect(MenuItem selection, Modifiers mods)
1316          {
1317             outputView.Show();
1318             watchesView.Show();
1319             threadsView.Show();
1320             callStackView.Show();
1321             breakpointsView.Show();
1322             return true;
1323          }
1324       }
1325 #ifdef GDB_DEBUG_GUI
1326       MenuDivider { viewMenu };
1327       MenuItem viewGDBItem
1328       {
1329          viewMenu, $"GDB Dialog", g, Key { f9, shift = true, ctrl = true };
1330          bool NotifySelect(MenuItem selection, Modifiers mods)
1331          {
1332             gdbDialog.Show();
1333             return true;
1334          }
1335       }
1336 #endif
1337       MenuDivider { viewMenu };
1338       MenuItem viewColorPicker
1339       {
1340          viewMenu, $"Color Picker...", c, Key { c, ctrl = true , shift = true };
1341          bool NotifySelect(MenuItem selection, Modifiers mods)
1342          {
1343             ColorPicker colorPicker { master = this };
1344             colorPicker.Modal();
1345             return true;
1346          }
1347       }
1348       MenuDivider { viewMenu };
1349       /*
1350       MenuItem
1351       {
1352          viewMenu, "Full Screen", f, checkable = true;
1353
1354          bool NotifySelect(MenuItem selection, Modifiers mods)
1355          {
1356             app.fullScreen ^= true;
1357             SetDriverAndSkin();
1358             anchor = { 0, 0, 0, 0 };
1359             return true;
1360          }
1361       };
1362       */
1363       Menu driversMenu { viewMenu, $"Graphics Driver", v };
1364       //Menu skinsMenu { viewMenu, "GUI Skins", k };
1365    Menu windowMenu { menu, $"Window", w };
1366       MenuItem { windowMenu, $"Close All", l, NotifySelect = MenuWindowCloseAll };
1367       MenuDivider { windowMenu };
1368       MenuItem { windowMenu, $"Next", n, f6, NotifySelect = MenuWindowNext };
1369       MenuItem { windowMenu, $"Previous", p, shiftF6, NotifySelect = MenuWindowPrevious };
1370       MenuDivider { windowMenu };
1371       MenuItem { windowMenu, $"Cascade", c, NotifySelect = MenuWindowCascade };
1372       MenuItem { windowMenu, $"Tile Horizontally", h, NotifySelect = MenuWindowTileHorz };
1373       MenuItem { windowMenu, $"Tile Vertically", v, NotifySelect = MenuWindowTileVert };
1374       MenuItem { windowMenu, $"Arrange Icons", a, NotifySelect = MenuWindowArrangeIcons };
1375       MenuDivider { windowMenu };
1376       MenuItem { windowMenu, $"Windows...", w, NotifySelect = MenuWindowWindows };
1377    Menu helpMenu { menu, $"Help", h };
1378       MenuItem
1379       {
1380          helpMenu, $"API Reference", r, f1;
1381          bool NotifySelect(MenuItem selection, Modifiers mods)
1382          {
1383             Execute("documentor");
1384             return true;
1385          }
1386       }
1387       MenuDivider { helpMenu };
1388       MenuItem
1389       {
1390          helpMenu, $"About...", a;
1391          bool NotifySelect(MenuItem selection, Modifiers mods)
1392          {
1393             AboutIDE { master = this }.Modal();
1394             return true;
1395          }
1396       }
1397
1398    property ToolBox toolBox
1399    {
1400       get { return toolBox; }
1401    }
1402
1403    property Sheet sheet
1404    {
1405       get { return sheet; }
1406    }
1407
1408    property Project project
1409    {
1410       get { return projectView ? projectView.project : null; }
1411    }
1412
1413    property Workspace workspace
1414    {
1415       get { return projectView ? projectView.workspace : null; }
1416    }
1417
1418    FindInFilesDialog findInFilesDialog
1419    {
1420       master = this,
1421       filters = findInFilesFileFilters.array, sizeFilters = findInFilesFileFilters.count * sizeof(FileFilter);
1422       filter = 1;
1423    };
1424
1425 #ifdef GDB_DEBUG_GUI
1426    GDBDialog gdbDialog
1427    {
1428       master = this, parent = this;
1429       anchor = { left = 100, top = 100, right = 100, bottom = 100 };
1430
1431       void OnCommand(char * string)
1432       {
1433          if(ide)
1434             ide.debugger.SendGDBCommand(string);
1435       }
1436    };
1437 #endif
1438    
1439    bool NotifySelectDisplayDriver(MenuItem selection, Modifiers mods)
1440    {
1441       //app.driver = app.drivers[selection.id];
1442 #if defined(__unix__) || defined(__APPLE__)
1443       app.driver = selection.id ? "OpenGL" : "X";
1444 #else
1445       app.driver = selection.id ? "OpenGL" : "GDI";
1446 #endif
1447       delete ideSettings.displayDriver;
1448       ideSettings.displayDriver = CopyString(selection.id ? "OpenGL" : "Default");
1449
1450       settingsContainer.Save();
1451       //SetDriverAndSkin();
1452       return true;
1453    }
1454
1455    bool NotifySelectDisplaySkin(MenuItem selection, Modifiers mods)
1456    {
1457       app.skin = app.skins[selection.id];
1458       SetDriverAndSkin();
1459       return true;
1460    }
1461
1462    void SetDriverAndSkin()
1463    {
1464       int c;
1465       for(c = 0; c < app.numSkins; c++)
1466          if(!strcmp(app.skins[c], app.skin))
1467          {
1468             skinItems[c].checked = true;
1469             break;
1470          }
1471       for(c = 0; c < app.numDrivers; c++)
1472          if(!strcmp(app.drivers[c], app.driver))
1473          {
1474             driverItems[c].checked = true;
1475             break;
1476          }
1477    }
1478
1479    ProjectView CreateProjectView(Workspace workspace, char * fileName)
1480    {
1481       Project project = workspace.projects.firstIterator.data;
1482       projectView = ProjectView
1483       {
1484          this;
1485          fileName = fileName;
1486          
1487          void NotifyDestroyed(Window window, DialogResult result)
1488          {
1489             projectView = null;
1490             text = titleECEREIDE;
1491             
1492             AdjustMenus();
1493          }
1494       };
1495       projectView.Create();
1496       RepositionWindows(false);
1497
1498       // Leave it after Create to avoid flicker due to seeing IDE without a project view
1499       projectView.workspace = workspace;
1500       projectView.project = project;
1501       ideMainFrame.SetText("%s - %s", project.topNode.name, titleECEREIDE);
1502
1503       AdjustMenus();
1504
1505       ide.breakpointsView.LoadFromWorkspace();
1506       ide.watchesView.LoadFromWorkspace();
1507
1508       findInFilesDialog.projectNodeField.userData = projectView;
1509
1510       {
1511          char fileName[MAX_LOCATION];
1512          strcpy(fileName, project.topNode.path);
1513          PathCat(fileName, project.topNode.name);
1514       }
1515       return projectView;
1516    }
1517
1518    bool GetDebugMenusDisabled()
1519    {
1520       if(projectView)
1521       {
1522          Project project = projectView.project;
1523          if(project)
1524             if(project.GetTargetType(project.config) == executable)
1525                return false;
1526            
1527       }
1528       return true;
1529    }
1530
1531    void RepositionWindows(bool expand)
1532    {
1533       if(this)
1534       {
1535          Window child;
1536          bool inDebugMode = debugger.isActive;
1537          bool callStackVisible = expand ? false : callStackView.visible;
1538          bool threadsVisible = expand ? false : threadsView.visible;
1539          bool watchesVisible = expand ? false : watchesView.visible;
1540          bool breakpointsVisible = expand ? false : breakpointsView.visible;
1541          bool toolBoxVisible = toolBox.visible;
1542          bool outputVisible = expand ? false : outputView.visible;
1543          int topDistance = (callStackVisible || threadsVisible) ? 200 : 0;
1544          int bottomDistance = (outputVisible || watchesVisible || breakpointsVisible) ? 240 : 0;
1545          
1546          for(child = firstChild; child; child = child.next)
1547          {
1548             if(child._class == class(CodeEditor) || child._class == class(Designer) || 
1549                child._class == class(Sheet) || child._class == class(ProjectView))
1550             {
1551                Anchor anchor = child.anchor;
1552                anchor.top = topDistance;
1553                anchor.bottom = bottomDistance;
1554                if(child._class == class(CodeEditor) || child._class == class(Designer))
1555                {
1556                   anchor.right = toolBoxVisible ? 150 : 0;
1557                }
1558                child.anchor = anchor;
1559             }
1560             else if(expand)
1561             {
1562                if(child._class == class(OutputView) || child._class == class(CallStackView) || child._class == class(ThreadsView) || child._class == class(WatchesView) || 
1563                   child._class == class(BreakpointsView))
1564                   child.visible = false;
1565             }
1566          }
1567          // If this is not here, the IDE is not updated when doing Debug/Break then Alt-4 to show call stack (IDE not updated)
1568          Update(null);
1569       }
1570    }
1571
1572    bool ShowCodeEditor()
1573    {
1574       if(activeClient)
1575          activeClient.Activate();
1576       else if(projectView)
1577       { 
1578          projectView.visible = true;
1579          projectView.Activate();
1580       }
1581       else
1582       {
1583          sheet.visible = true;
1584          sheet.Activate();
1585       }
1586       return false;
1587    }
1588
1589    bool ShouldStopBuild()
1590    {
1591       return projectView.stopBuild;
1592    }
1593
1594    void DocumentSaved(Window document, char * fileName)
1595    {
1596       ideSettings.AddRecentFile(fileName);
1597       ide.UpdateRecentMenus();
1598       ide.AdjustFileMenus();
1599       settingsContainer.Save();
1600    }
1601
1602    bool Window::OnFileModified(FileChange fileChange, char * param)
1603    {
1604       char temp[4096];
1605       sprintf(temp, $"The document %s was modified by another application.\n"
1606             "Would you like to reload it and lose your changes?", this.fileName);
1607       if(MessageBox { type = yesNo, master = this/*.parent*/,
1608             text = $"Document has been modified", contents = temp }.Modal() == yes)
1609       {
1610          char * fileName = CopyString(this.fileName);
1611          WindowState state = this.state;
1612          Anchor anchor = this.anchor;
1613          Size size = this.size;
1614
1615          this.modifiedDocument = false;
1616          this.Destroy(0);
1617          this = ide.OpenFile(fileName, normal, true, null, no, normal);
1618          if(this)
1619          {
1620             this.anchor = anchor;
1621             this.size = size;
1622             this.SetState(state, true, 0);
1623          }
1624          delete fileName;
1625          return true;
1626       }
1627       return true;
1628    }
1629
1630    void UpdateMakefiles()
1631    {
1632       if(workspace)
1633       {
1634          CompilerConfig compiler = ideSettings.GetCompilerConfig(workspace.compiler);
1635          for(prj : workspace.projects)
1636             projectView.ProjectUpdateMakefileForAllConfigs(prj);
1637          delete compiler;
1638       }
1639    }
1640
1641    void UpdateCompilerConfigs(bool mute)
1642    {
1643       UpdateToolBarActiveCompilers();
1644       if(workspace)
1645       {
1646          bool silent = mute || (ide.projectView.buildInProgress == none ? false : true);
1647          CompilerConfig compiler = ideSettings.GetCompilerConfig(workspace.compiler);
1648          if(!silent)
1649          {
1650             projectView.ShowOutputBuildLog(true);
1651             projectView.DisplayCompiler(compiler, false);
1652          }
1653          for(prj : workspace.projects)
1654             projectView.ProjectPrepareCompiler(prj, compiler, silent);
1655          delete compiler;
1656       }
1657    }
1658
1659    void UpdateToolBarActiveCompilers()
1660    {
1661       toolBar.activeCompiler.Clear();
1662       for(compiler : ideSettings.compilerConfigs)
1663       {
1664          DataRow row = toolBar.activeCompiler.AddString(compiler.name);
1665          if(workspace && workspace.compiler && !strcmp(compiler.name, workspace.compiler))
1666             toolBar.activeCompiler.currentRow = row;
1667       }
1668       if(!toolBar.activeCompiler.currentRow && toolBar.activeCompiler.firstRow)
1669          toolBar.activeCompiler.SelectRow(toolBar.activeCompiler.firstRow);
1670    }
1671
1672    void UpdateToolBarActiveConfigs(bool selectionOnly)
1673    {
1674       bool commonSelected = false;
1675       DataRow row = toolBar.activeConfig.currentRow;
1676       if(selectionOnly)
1677          row = toolBar.activeConfig.FindRow(1);
1678       else
1679       {
1680          toolBar.activeConfig.Clear();
1681          row = toolBar.activeConfig.AddString("(Mixed)");
1682          row.tag = 1;
1683       }
1684       if(workspace)
1685       {
1686          char * configName = null;
1687          if(!selectionOnly)
1688          {
1689             Map<String, int> configs { }; // TOIMP: just need sort but using map until containers have sort
1690             for(prj : workspace.projects)
1691             {
1692                for(cfg : prj.configurations)
1693                {
1694                   if(cfg.name)
1695                      configs[cfg.name] = 1;
1696                }
1697             }
1698             for(name : configs)
1699             {
1700                toolBar.activeConfig.AddString(&name);
1701             }
1702             delete configs;
1703          }
1704          if(projectView && projectView.project)
1705          {
1706             for(prj : workspace.projects)
1707             {
1708                if(prj.config && prj.config.name)
1709                {
1710                   configName = prj.config.name;
1711                   break;
1712                }
1713             }
1714             if(configName)
1715             {
1716                commonSelected = true;
1717                for(prj : workspace.projects)
1718                {
1719                   if(prj.config && (!prj.config.name || strcmp(prj.config.name, configName)))
1720                   {
1721                      commonSelected = false;
1722                      break;
1723                   }
1724                }
1725             }
1726          }
1727          if(commonSelected)
1728          {
1729             commonSelected = false;
1730             for(row = toolBar.activeConfig.firstRow; row; row = row.next)
1731             {
1732                if(!strcmp(row.string, configName))
1733                {
1734                   toolBar.activeConfig.currentRow = row;
1735                   commonSelected = true;
1736                   break;
1737                }
1738             }
1739          }
1740       }
1741       if(!selectionOnly)
1742          toolBar.activeConfig.Sort(null, 0);
1743       if(!commonSelected)
1744          toolBar.activeConfig.currentRow = row;
1745    }
1746
1747    void AdjustMenus()
1748    {
1749       bool unavailable = !project;
1750
1751       projectAddItem.disabled             = unavailable;
1752       toolBar.buttonAddProject.disabled   = unavailable;
1753
1754       projectSettingsItem.disabled        = unavailable;
1755
1756       projectBrowseFolderItem.disabled    = unavailable;
1757
1758       viewProjectItem.disabled            = unavailable;
1759
1760       toolBar.activeConfig.disabled       = unavailable;
1761       toolBar.activeCompiler.disabled     = unavailable;
1762       toolBar.activeBitDepth.disabled     = unavailable;
1763
1764       AdjustFileMenus();
1765       AdjustBuildMenus();
1766       AdjustDebugMenus();
1767    }
1768
1769    property bool hasOpenedCodeEditors
1770    {
1771       get
1772       {
1773          Window w;
1774          for(w = firstChild; w; w = w.next)
1775             if(w._class == class(CodeEditor) &&
1776                   w.isDocument && !w.closing && w.visible && w.created &&
1777                   w.fileName && w.fileName[0])
1778                return true;
1779          return false;
1780       }
1781    }
1782
1783    void AdjustFileMenus()
1784    {
1785       bool unavailable = project != null || !hasOpenedCodeEditors; // are they supported source code (ec, c, cpp, etc) ?
1786
1787       projectQuickItem.disabled           = unavailable;
1788    }
1789
1790    void AdjustBuildMenus()
1791    {
1792       bool unavailable = project && projectView.buildInProgress;
1793
1794       projectNewItem.disabled             = unavailable;
1795       toolBar.buttonNewProject.disabled   = unavailable;
1796       projectOpenItem.disabled            = unavailable;
1797       toolBar.buttonOpenProject.disabled  = unavailable;
1798
1799       unavailable = !project || projectView.buildInProgress;
1800
1801       projectCloseItem.disabled           = unavailable;
1802       // toolBar.buttonCloseProject.disabled = unavailable;
1803
1804       projectRunItem.disabled    = unavailable || project.GetTargetType(project.config) != executable;
1805       toolBar.buttonRun.disabled = unavailable || project.GetTargetType(project.config) != executable;
1806
1807       projectBuildItem.disabled = false;
1808       projectBuildItem.text     = unavailable ? $"Stop Build" : $"Build";
1809       projectBuildItem.accelerator = unavailable ? Key { pauseBreak, ctrl = true } : f7;
1810
1811       projectLinkItem.disabled                  = unavailable;
1812       toolBar.buttonReLink.disabled             = unavailable;
1813       projectRebuildItem.disabled               = unavailable;
1814       toolBar.buttonRebuild.disabled            = unavailable;
1815       projectCleanItem.disabled                 = unavailable;
1816       toolBar.buttonClean.disabled              = unavailable;
1817       projectRealCleanItem.disabled             = unavailable;
1818       // toolBar.buttonRealClean.disabled          = unavailable;
1819       projectRegenerateItem.disabled            = unavailable;
1820       toolBar.buttonRegenerateMakefile.disabled = unavailable;
1821       projectCompileItem.disabled               = unavailable;
1822
1823       AdjustPopupBuildMenus();
1824    }
1825
1826    void AdjustPopupBuildMenus()
1827    {
1828       bool unavailable = !project || projectView.buildInProgress;
1829
1830       if(projectView && projectView.popupMenu && projectView.popupMenu.menu && projectView.popupMenu.created)
1831       {
1832          MenuItem menu;
1833          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectBuild, 0);
1834          if(menu)
1835          {
1836             menu.disabled = false;
1837             menu.text   = unavailable ? $"Stop Build" : $"Build";
1838             menu.accelerator = unavailable ? Key { pauseBreak, ctrl = true } : f7;
1839          }
1840
1841          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectLink, 0);              if(menu) menu.disabled = unavailable;
1842          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectRebuild, 0);           if(menu) menu.disabled = unavailable;
1843          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectClean, 0);             if(menu) menu.disabled = unavailable;
1844          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectRealClean, 0);         if(menu) menu.disabled = unavailable;
1845          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectRegenerate, 0);        if(menu) menu.disabled = unavailable;
1846          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectRemove, 0);            if(menu) menu.disabled = unavailable;
1847          menu = projectView.popupMenu.menu.FindItem(ProjectView::FileClean, 0);                if(menu) menu.disabled = unavailable;
1848          menu = projectView.popupMenu.menu.FindItem(ProjectView::FileCompile, 0);              if(menu) menu.disabled = unavailable;
1849          menu = projectView.popupMenu.menu.FindItem(ProjectView::FileDebugPrecompile, 0);      if(menu) menu.disabled = unavailable;
1850          menu = projectView.popupMenu.menu.FindItem(ProjectView::FileDebugCompile, 0);         if(menu) menu.disabled = unavailable;
1851          menu = projectView.popupMenu.menu.FindItem(ProjectView::FileDebugGenerateSymbols, 0); if(menu) menu.disabled = unavailable;
1852          projectView.popupMenu.Update(null);
1853       }
1854    }
1855
1856    void AdjustDebugMenus()
1857    {
1858       bool unavailable = !project || project.GetTargetType(project.config) != executable ||
1859                projectView.buildInProgress == buildingMainProject;
1860       bool active = ide.debugger.isActive;
1861       bool executing = ide.debugger.state == running;
1862       //bool holding = ide.debugger.state == stopped;
1863
1864       debugStartResumeItem.disabled       = unavailable || executing;
1865       debugStartResumeItem.text           = active ? $"Resume" : $"Start";
1866       debugStartResumeItem.NotifySelect   = active ? MenuDebugResume : MenuDebugStart;
1867       if(toolBar)
1868       {
1869          toolBar.buttonDebugStartResume.disabled      = unavailable || executing;
1870          toolBar.buttonDebugStartResume.toolTip       = active ? $"Resume" : $"Start";
1871       }
1872
1873       debugBreakItem.disabled             = unavailable || !executing;
1874       debugStopItem.disabled              = unavailable || !active;
1875       debugRestartItem.disabled           = unavailable || !active;
1876       if(toolBar)
1877       {
1878          toolBar.buttonDebugPause.disabled            = unavailable || !executing;
1879          toolBar.buttonDebugStop.disabled             = unavailable || !active;
1880          toolBar.buttonDebugRestart.disabled          = unavailable || !active;
1881       }
1882
1883       debugStepIntoItem.disabled          = unavailable || executing;
1884       debugStepOverItem.disabled          = unavailable || executing;
1885       debugStepOutItem.disabled           = unavailable || executing || !active;
1886       debugSkipStepOverItem.disabled      = unavailable || executing;
1887       debugSkipStepOutItem.disabled       = unavailable || executing || !active;
1888       if(toolBar)
1889       {
1890          toolBar.buttonDebugStepInto.disabled         = unavailable || executing;
1891          toolBar.buttonDebugStepOver.disabled         = unavailable || executing;
1892          toolBar.buttonDebugStepOut.disabled          = unavailable || executing || !active;
1893          toolBar.buttonDebugSkipStepOver.disabled     = unavailable || executing;
1894          // toolBar.buttonDebugSkipStepOutItem.disabled  = unavailable || executing;
1895       }
1896       if((Designer)GetActiveDesigner())
1897       {
1898          CodeEditor codeEditor = ((Designer)GetActiveDesigner()).codeEditor;
1899          if(codeEditor)
1900          {
1901             codeEditor.debugRunToCursor.disabled      = unavailable || executing;
1902             codeEditor.debugSkipRunToCursor.disabled  = unavailable || executing;
1903          }
1904       }
1905    }
1906
1907    void ChangeFileDialogsDirectory(char * directory, bool saveSettings)
1908    {
1909       char tempString[MAX_LOCATION];
1910       strcpy(tempString, directory);
1911       if(saveSettings && !projectView)
1912       {
1913          ideSettings.ideFileDialogLocation = directory;
1914          settingsContainer.Save();
1915       }
1916
1917       ideFileDialog.currentDirectory = tempString;
1918       codeEditorFileDialog.currentDirectory = tempString;
1919       codeEditorFormFileDialog.currentDirectory = tempString;
1920    }
1921
1922    void ChangeProjectFileDialogDirectory(char * directory)
1923    {
1924       ideSettings.ideProjectFileDialogLocation = directory;
1925       settingsContainer.Save();
1926    }
1927
1928    Window FindWindow(char * filePath)
1929    {
1930       Window document = null;
1931
1932       // TOCHECK: Do we need to change slashes here?
1933       for(document = firstChild; document; document = document.next)
1934       {
1935          char * fileName = document.fileName;
1936          if(document.isDocument && fileName && !fstrcmp(fileName, filePath))
1937          {
1938             document.visible = true;
1939             document.Activate();
1940             return document;
1941          }
1942       }
1943       return null;
1944    }
1945
1946    bool DontTerminateDebugSession(char * title)
1947    {
1948       if(debugger.isActive)
1949       {
1950          if(MessageBox { type = yesNo, master = ide, 
1951                            contents = $"Do you want to terminate the debugging session in progress?", 
1952                            text = title }.Modal() == no)
1953             return true;
1954          /*
1955          MessageBox msg { type = yesNo, master = ide, 
1956                            contents = "Do you want to terminate the debugging session in progress?", 
1957                            text = title };
1958          if(msg.Modal() == no)
1959          {
1960             msg.Destroy(0);
1961             delete msg;
1962             return true;
1963          }
1964          msg.Destroy(0);
1965          delete msg;*/
1966       }
1967       return false;
1968    }
1969
1970    Window OpenFile(char * origFilePath, WindowState state, bool visible, char * type, OpenCreateIfFails createIfFails, OpenMethod openMethod)
1971    {
1972       char extension[MAX_EXTENSION] = "";
1973       Window document = null;
1974       bool isProject = false;
1975       bool needFileModified = true;
1976       char winFilePath[MAX_LOCATION];
1977       char * filePath = strstr(origFilePath, "http://") == origFilePath ? strcpy(winFilePath, origFilePath) : GetSystemPathBuffer(winFilePath, origFilePath);
1978
1979       if(!type)
1980       {
1981          GetExtension(filePath, extension);
1982          strlwr(extension);
1983       }
1984       else
1985          strcpy(extension, type);
1986
1987       if(strcmp(extension, ProjectExtension))
1988       {
1989          for(document = firstChild; document; document = document.next)
1990          {
1991             char * fileName = document.fileName;
1992             if(document.isDocument && fileName && !fstrcmp(fileName, filePath) && document.created)
1993             {
1994                document.visible = true;
1995                if(visible)
1996                   document.Activate();
1997                return document;
1998             }
1999          }
2000       }
2001
2002       if(createIfFails == whatever)
2003          ;
2004       else if(!strcmp(extension, ProjectExtension) || !strcmp(extension, WorkspaceExtension))
2005       {
2006          needFileModified = false;
2007          if(openMethod == normal)
2008          {
2009             if(DontTerminateDebugSession($"Open Project"))
2010                return null;
2011             isProject = true;
2012             if(MenuWindowCloseAll(null, 0))
2013             {
2014                if(projectView)
2015                {
2016                   projectView.visible = false;
2017                   projectView.Destroy(0);
2018                   // Where did this come from? projectView = null;
2019                }
2020                if(!projectView)
2021                {
2022                   for(;;)
2023                   {
2024                      Project project;
2025                      Workspace workspace = null;
2026                      
2027                      if(FileExists(filePath))
2028                      {
2029                         if(!strcmp(extension, ProjectExtension))
2030                         {
2031                            char workspaceFile[MAX_LOCATION];
2032                            strcpy(workspaceFile, filePath);
2033                            ChangeExtension(workspaceFile, WorkspaceExtension, workspaceFile);
2034                            workspace = LoadWorkspace(workspaceFile, filePath);
2035                         }
2036                         else if(!strcmp(extension, WorkspaceExtension))
2037                            workspace = LoadWorkspace(filePath, null);
2038                         else
2039                            return null;
2040                         //project = LoadProject(filePath, null);
2041                      }
2042                      
2043                      if(workspace)
2044                      {
2045                         char absolutePath[MAX_LOCATION];
2046                         CreateProjectView(workspace, filePath);
2047                         document = projectView;
2048
2049                         workspace.DropInvalidBreakpoints();
2050                         workspace.Save();
2051
2052                         ide.projectView.ShowOutputBuildLog(true);
2053                         {
2054                            CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
2055                            ide.projectView.DisplayCompiler(compiler, false);
2056                            delete compiler;
2057                         }
2058                         UpdateCompilerConfigs(false);
2059                         UpdateMakefiles();
2060                         {
2061                            char newWorkingDir[MAX_LOCATION];
2062                            StripLastDirectory(filePath, newWorkingDir);
2063                            ChangeFileDialogsDirectory(newWorkingDir, false);
2064                         }
2065                         if(document)
2066                            document.fileName = filePath;
2067
2068                         ideMainFrame.SetText("%s - %s", filePath, titleECEREIDE);
2069
2070                         // this crashes on starting ide with epj file, solution please?
2071                         // app.UpdateDisplay();
2072
2073                         workspace.holdTracking = true;
2074                         for(ofi : workspace.openedFiles)
2075                         {
2076                            if(ofi.state != closed)
2077                            {
2078                               Window file = OpenFile(ofi.path, normal, true, null, no, normal);
2079                               if(file)
2080                               {
2081                                  char fileName[MAX_LOCATION];
2082                                  ProjectNode node;
2083                                  GetLastDirectory(ofi.path, fileName);
2084                                  node = projectView.project.topNode.Find(fileName, true);
2085                                  if(node)
2086                                     node.EnsureVisible();
2087                               }
2088                            }
2089                         }
2090                         workspace.holdTracking = false;
2091
2092                         workspace.timer.Start();
2093
2094                         findInFilesDialog.mode = FindInFilesMode::project;
2095                         findInFilesDialog.currentDirectory = ide.project.topNode.path;
2096                         
2097                         {
2098                            char location[MAX_LOCATION];
2099                            StripLastDirectory(ide.project.topNode.path, location);
2100                            ChangeProjectFileDialogDirectory(location);
2101                         }
2102                         
2103                         /*
2104                         if(projectView.debugger)
2105                            projectView.debugger.EvaluateWatches();
2106                         */
2107                         
2108                         break;
2109                      }
2110                      else 
2111                      {
2112                         if(MessageBox { type = yesNo, master = this, text = $"Error opening project", contents = $"Open a different project?" }.Modal() == yes)
2113                         {
2114                            ideProjectFileDialog.text = openProjectFileDialogTitle;
2115                            if(ideProjectFileDialog.Modal() == cancel)
2116                               return null;
2117                            filePath = ideProjectFileDialog.filePath;
2118                            GetExtension(filePath, extension);
2119                         }
2120                         else
2121                            return null;
2122                      }
2123                   }
2124                }
2125             }
2126             else
2127                return null;
2128          }
2129          else if(openMethod == add)
2130          {
2131             if(workspace)
2132             {
2133                Project prj = null;
2134                char slashFilePath[MAX_LOCATION];
2135                GetSlashPathBuffer(slashFilePath, filePath);
2136                for(p : workspace.projects)
2137                {
2138                   if(!fstrcmp(p.filePath, slashFilePath))
2139                   {
2140                      prj = p;
2141                      break;
2142                   }
2143                }
2144                if(prj)
2145                {
2146                   MessageBox { type = ok, parent = parent, master = this, text = $"Same Project", 
2147                         contents = $"This project is already present in workspace." }.Modal();
2148                }
2149                else
2150                {
2151                   prj = LoadProject(filePath, null);
2152                   if(prj)
2153                   {
2154                      CompilerConfig compiler = ideSettings.GetCompilerConfig(workspace.compiler);
2155                      prj.StartMonitoring();
2156                      workspace.projects.Add(prj);
2157                      if(projectView)
2158                         projectView.AddNode(prj.topNode, null);
2159                      workspace.modified = true;
2160                      workspace.Save();
2161                      findInFilesDialog.AddProjectItem(prj);
2162                      projectView.ShowOutputBuildLog(true);
2163                      projectView.DisplayCompiler(compiler, false);
2164                      projectView.ProjectUpdateMakefileForAllConfigs(prj);
2165                      delete compiler;
2166
2167                      {
2168                         char location[MAX_LOCATION];
2169                         StripLastDirectory(prj.topNode.path, location);
2170                         ChangeProjectFileDialogDirectory(location);
2171                      }
2172
2173                      // projectView is associated with the main project and not with the one just added but
2174                      return projectView; // just to let the caller know something was opened
2175                   }
2176                }
2177             }
2178             else
2179                return null;
2180          }
2181       }
2182       else if(!strcmp(extension, "bmp") || !strcmp(extension, "pcx") ||
2183             !strcmp(extension, "jpg") || !strcmp(extension, "gif") ||
2184             !strcmp(extension, "jpeg") || !strcmp(extension, "png"))
2185       {
2186          if(FileExists(filePath))
2187             document = PictureEdit { hasMaximize = true, hasMinimize = true, hasClose = true, borderStyle = sizable, 
2188                                        hasVertScroll = true, hasHorzScroll = true, parent = this, state = state, 
2189                                        visible = visible, bitmapFile = filePath, OnClose = PictureEditOnClose/*why?--GenericDocumentOnClose*/;
2190                                     };
2191          if(!document)
2192             MessageBox { type = ok, master = this, text = filePath, contents = $"File doesn't exist." }.Modal();
2193       }
2194 #ifndef NO3D
2195       else if(!strcmp(extension, "3ds"))
2196       {
2197          if(FileExists(filePath))
2198             document = ModelView { hasMaximize = true, hasMinimize = true, hasClose = true, borderStyle = sizable, 
2199                                     hasVertScroll = true, hasHorzScroll = true, parent = this, state = state, 
2200                                     visible = visible, modelFile = filePath, OnClose = ModelViewOnClose/*why?--GenericDocumentOnClose*/
2201                                     };
2202
2203          if(!document)
2204             MessageBox { type = ok, master = this, text = filePath, contents = $"File doesn't exist." }.Modal();
2205       }
2206 #endif
2207       else if(!strcmp(extension, "txt") || !strcmp(extension, "text") ||
2208             !strcmp(extension, "nfo") || !strcmp(extension, "info") ||
2209             !strcmp(extension, "htm") || !strcmp(extension, "html") ||
2210             !strcmp(extension, "css") || !strcmp(extension, "php") ||
2211             !strcmp(extension, "js"))
2212       {
2213          CodeEditor editor { parent = this, state = state, visible = false };
2214          editor.updatingCode = true;
2215          if(editor.LoadFile(filePath))
2216          {
2217             document = editor;
2218             editor.visible = true;
2219          }
2220          else
2221             delete editor;
2222          needFileModified = false;
2223       }
2224       else
2225       {
2226          CodeEditor editor { parent = this, state = state, visible = false };
2227          if(editor.LoadFile(filePath))
2228          {
2229             document = editor;
2230             editor.visible = true;
2231          }
2232          else
2233             delete editor;
2234          needFileModified = false;
2235       }
2236
2237       if(document && (document._class == class(PictureEdit) ||
2238             document._class == class(ModelView)))
2239       {
2240          document.Create();
2241          if(document)
2242          {
2243             document.fileName = filePath;
2244             if(workspace && !workspace.holdTracking)
2245                workspace.UpdateOpenedFileInfo(filePath, opened);
2246          }
2247       }
2248       
2249       if(!document && createIfFails != no)
2250       {
2251          if(createIfFails != yes && !needFileModified && 
2252                MessageBox { type = yesNo, master = this, text = filePath, contents = $"File doesn't exist. Create?" }.Modal() == yes)
2253             createIfFails = yes;
2254          if(createIfFails == yes || createIfFails == whatever)
2255          {
2256             document = (Window)NewCodeEditor(this, state, true);
2257             if(document)
2258                document.fileName = filePath;
2259          }
2260       }
2261
2262       if(document)
2263       {
2264          if(projectView && document._class == class(CodeEditor) && workspace)
2265          {
2266             int lineNumber, position;
2267             Point scroll;
2268             CodeEditor editor = (CodeEditor)document;
2269             editor.openedFileInfo = workspace.UpdateOpenedFileInfo(filePath, opened);
2270             editor.openedFileInfo.holdTracking = true;
2271             lineNumber = Max(editor.openedFileInfo.lineNumber - 1, 0);
2272             position = Max(editor.openedFileInfo.position - 1, 0);
2273             editor.editBox.GoToLineNum(lineNumber);
2274             editor.editBox.GoToPosition(editor.editBox.line, lineNumber, position);
2275             scroll.x = Max(editor.openedFileInfo.scroll.x, 0);
2276             scroll.y = Max(editor.openedFileInfo.scroll.y, 0);
2277             editor.editBox.scroll = scroll;
2278             editor.openedFileInfo.holdTracking = false;
2279          }
2280          
2281          if(needFileModified)
2282             document.OnFileModified = OnFileModified;
2283          document.NotifySaved = DocumentSaved;
2284          
2285          if(isProject)
2286             ideSettings.AddRecentProject(document.fileName);
2287          else
2288             ideSettings.AddRecentFile(document.fileName);
2289          ide.UpdateRecentMenus();
2290          ide.AdjustFileMenus();
2291          settingsContainer.Save();
2292          
2293          return document;
2294       }
2295       else
2296          return null;
2297    }
2298
2299    // TOCHECK: I can't use a generic one for both ModelView and PictureEdit both derived from Window
2300    /*bool Window::GenericDocumentOnClose(bool parentClosing)
2301    {
2302       if(!parentClosing && ide.workspace)
2303          ide.workspace.UpdateOpenedFileInfo(fileName, unknown);
2304       return true;
2305    }*/
2306    bool ModelView::ModelViewOnClose(bool parentClosing)
2307    {
2308       if(!parentClosing && ide.workspace)
2309          ide.workspace.UpdateOpenedFileInfo(fileName, unknown);
2310       return true;
2311    }
2312    bool PictureEdit::PictureEditOnClose(bool parentClosing)
2313    {
2314       if(!parentClosing && ide.workspace)
2315          ide.workspace.UpdateOpenedFileInfo(fileName, unknown);
2316       return true;
2317    }
2318
2319    /*
2320    void OnUnloadGraphics(Window window)
2321    {
2322       display.ClearMaterials();
2323       display.ClearTextures();
2324       display.ClearMeshes();
2325    }
2326    */
2327
2328    bool OnActivate(bool active, Window swap, bool * goOnWithActivation, bool direct)
2329    {
2330       caps.color = app.GetKeyState(capsState) ? black : Color { 128,128,128 };
2331       num.color = app.GetKeyState(numState) ? black : Color { 128,128,128 };
2332       return true;
2333    }
2334
2335    bool OnKeyDown(Key key, unichar ch)
2336    {
2337       switch(key)
2338       {
2339          case b:
2340             projectView.Update(null);
2341             break;
2342          case capsLock:
2343             caps.color = app.GetKeyState(capsState) ? black : Color { 128,128,128 };
2344             break;
2345          case numLock:
2346             num.color = app.GetKeyState(numState) ? black : Color { 128,128,128 };
2347             break;
2348       }
2349       return true;
2350    }
2351
2352    void GoToError(const char * line)
2353    {
2354       if(projectView)
2355          projectView.GoToError(line);
2356    }
2357
2358    void CodeLocationParseAndGoTo(const char * text, Project project, const char * dir)
2359    {
2360       char *path = text;
2361       char *colon = strchr(text, ':');
2362       char filePath[MAX_LOCATION];
2363       char completePath[MAX_LOCATION];
2364       int line = 0, col = 0;
2365       Project prj = null;
2366       FileAttribs fileAttribs;
2367
2368       if(colon && (colon[1] == '/' || colon[1] == '\\'))
2369       {
2370          path = (colon - 1 > path) ? colon - 1 : path;
2371          colon = strstr(colon + 1, ":");
2372       }
2373       while(isspace(*path)) path++;
2374       if(*path == '(')
2375       {
2376          char * close = strchr(path, ')');
2377          if(close)
2378          {
2379             char name[256];
2380             strncpy(name, path+1, close - path - 1);
2381             name[close - path - 1] = '\0';
2382             for(p : ide.workspace.projects)
2383             {
2384                if(!strcmp(p.name, name))
2385                {
2386                   path = close + 1;
2387                   prj = p;
2388                   break;
2389                }
2390             }
2391          }
2392       }
2393       if(!prj)
2394          prj = project ? project : (dir ? null : ide.project);
2395       if(colon)
2396       {
2397          strncpy(filePath, path, colon - path);
2398          filePath[colon - path] = '\0';
2399          line = atoi(colon + 1);
2400          colon = strstr(colon + 1, ":");
2401          if(colon)
2402             col = atoi(colon + 1);
2403       }
2404       else if(path - 1 >= text && *(path - 1) == '\"')
2405       {
2406          colon = strchr(path, '\"');
2407          if(colon)
2408          {
2409             strncpy(filePath, path, colon - path);
2410             filePath[colon - path] = '\0';
2411          }
2412       }
2413       else if(path && !colon)
2414       {
2415          strcpy(filePath, path);
2416       }
2417
2418       if(prj)
2419          strcpy(completePath, prj.topNode.path);
2420       else if(dir && dir[0])
2421          strcpy(completePath, dir);
2422       else
2423          completePath[0] = '\0';
2424       PathCat(completePath, filePath);
2425
2426       fileAttribs = FileExists(completePath);
2427       if(fileAttribs.isFile)
2428       {
2429          CodeEditor codeEditor = (CodeEditor)OpenFile(completePath, normal, true, "", no, normal);
2430          if(codeEditor && line)
2431          {
2432             EditBox editBox = codeEditor.editBox;
2433             editBox.GoToLineNum(line - 1);
2434             editBox.GoToPosition(editBox.line, line - 1, col ? (col - 1) : 0);
2435          }
2436       }
2437       else if(fileAttribs.isDirectory)
2438          ShellOpen(completePath);
2439    }
2440
2441    void OnRedraw(Surface surface)
2442    {
2443       Bitmap bitmap = back.bitmap;
2444       if(bitmap)
2445          surface.Blit(bitmap, (clientSize.w - bitmap.width) / 2, (clientSize.h - bitmap.height) / 2, 0, 0, bitmap.width, bitmap.height);
2446    }
2447
2448    void SheetSelected(SheetType sheetSelected)
2449    {
2450       if(activeChild == sheet)
2451       {
2452          if(sheetSelected == methods)
2453          {
2454             viewPropertiesItem.accelerator = f4;
2455             viewPropertiesItem.parent = viewMenu;
2456             viewMethodsItem.parent = null;
2457          }
2458          else
2459          {
2460             viewMethodsItem.accelerator = f4;
2461             viewMethodsItem.parent = viewMenu;
2462             viewPropertiesItem.parent = null;
2463          }
2464       }
2465       else
2466       {
2467          viewMethodsItem.parent = viewMenu;
2468          viewPropertiesItem.parent = viewMenu;
2469          if(sheetSelected == methods)
2470          {
2471             viewMethodsItem.accelerator = f4;
2472             viewPropertiesItem.accelerator = 0;
2473          }
2474          else
2475          {
2476             viewMethodsItem.accelerator = 0;
2477             viewPropertiesItem.accelerator = f4;
2478          }
2479       }
2480    }
2481
2482    void OnActivateClient(Window client, Window previous)
2483    {
2484       //if(!client || client != previous)
2485       {
2486          Class dataType;
2487          if(!client || client != previous)
2488          {
2489             if(previous)
2490                dataType = previous._class;
2491             if(previous && !strcmp(dataType.name, "CodeEditor"))
2492             {
2493                ((CodeEditor)previous).UpdateFormCode();
2494             }
2495             else if(previous && !strcmp(dataType.name, "Designer"))
2496             {
2497                ((Designer)previous).codeEditor.UpdateFormCode();
2498             }
2499          }
2500
2501          if(client)
2502             dataType = client._class;
2503          if(client && !strcmp(dataType.name, "CodeEditor"))
2504          {
2505             CodeEditor codeEditor = (CodeEditor)client;
2506             SetPrivateModule(codeEditor.privateModule);
2507             SetCurrentContext(codeEditor.globalContext);
2508             SetTopContext(codeEditor.globalContext);
2509             SetGlobalContext(codeEditor.globalContext);
2510             
2511             SetDefines(&codeEditor.defines);
2512             SetImports(&codeEditor.imports);
2513
2514             SetActiveDesigner(codeEditor.designer);
2515             
2516             sheet.codeEditor = codeEditor;
2517             toolBox.codeEditor = codeEditor;
2518
2519             viewDesignerItem.parent = viewMenu;
2520             if(activeChild != codeEditor)
2521             {
2522                viewCodeItem.parent = viewMenu;
2523                viewDesignerItem.accelerator = 0;
2524                viewCodeItem.accelerator = f8;
2525             }
2526             else
2527             {
2528                viewCodeItem.parent = null;
2529                viewDesignerItem.accelerator = f8;
2530             }
2531          }
2532          else if(client && !strcmp(dataType.name, "Designer"))
2533          {
2534             CodeEditor codeEditor = ((Designer)client).codeEditor;
2535             if(codeEditor)
2536             {
2537                SetPrivateModule(codeEditor.privateModule);
2538                SetCurrentContext(codeEditor.globalContext);
2539                SetTopContext(codeEditor.globalContext);
2540                SetGlobalContext(codeEditor.globalContext);
2541                SetDefines(&codeEditor.defines);
2542                SetImports(&codeEditor.imports);
2543             }
2544             else
2545             {
2546                SetPrivateModule(null);
2547                SetCurrentContext(null);
2548                SetTopContext(null);
2549                SetGlobalContext(null);
2550                SetDefines(null);
2551                SetImports(null);
2552             }
2553
2554             SetActiveDesigner((Designer)client);
2555
2556             sheet.codeEditor = codeEditor;
2557             toolBox.codeEditor = codeEditor;
2558
2559             viewCodeItem.parent = viewMenu;
2560             if(activeChild != client)
2561             {
2562                viewDesignerItem.parent = viewMenu;
2563                viewDesignerItem.accelerator = f8;
2564                viewCodeItem.accelerator = 0;
2565             }
2566             else
2567             {
2568                viewDesignerItem.parent = null;
2569                viewCodeItem.accelerator = f8;
2570             }
2571          }
2572          else
2573          {
2574             if(sheet)
2575                sheet.codeEditor = null;
2576             toolBox.codeEditor = null;
2577             SetActiveDesigner(null);
2578
2579             viewDesignerItem.parent = null;
2580             viewCodeItem.parent = null;
2581          }
2582          if(sheet)
2583             SheetSelected(sheet.sheetSelected);
2584       }
2585
2586       projectCompileItem = null;
2587
2588       if(statusBar)
2589       {
2590          statusBar.Clear();
2591          if(client && client._class == eSystem_FindClass(__thisModule, "CodeEditor")) // !strcmp(client._class.name, "CodeEditor")
2592          {
2593             CodeEditor codeEditor = (CodeEditor)client;
2594             EditBox editBox = codeEditor.editBox;
2595
2596             statusBar.AddField(pos);
2597
2598             caps = { width = 40, text = $"CAPS", color = app.GetKeyState(capsState) ? black : Color { 128, 128, 128 } };
2599             statusBar.AddField(caps);
2600
2601             ovr = { width = 30, text = $"OVR", color = editBox.overwrite ? black : Color { 128, 128, 128 } };
2602             statusBar.AddField(ovr);
2603
2604             num = { width = 30, text = $"NUM", color = app.GetKeyState(numState) ? black : Color { 128, 128, 128 } };
2605             statusBar.AddField(num);
2606
2607             //statusBar.text = "Ready";
2608
2609             if(projectView && projectView.project)
2610             {
2611                bool isCObject = false;
2612                ProjectNode node = projectView.GetNodeFromWindow(client, null, false);
2613                if(!node && (node = projectView.GetNodeFromWindow(client, null, true)))
2614                   isCObject = true;
2615                if(node)
2616                {
2617                   char nodeName[MAX_FILENAME];
2618                   char name[MAX_FILENAME+96];
2619                   if(isCObject)
2620                      ChangeExtension(node.name, "c", nodeName);
2621                   sprintf(name, $"Compile %s", isCObject ? nodeName : node.name);
2622                   projectCompileItem = 
2623                   {
2624                      copyText = true, text = name, c, ctrlF7, disabled = projectView.buildInProgress;
2625
2626                      bool NotifySelect(MenuItem selection, Modifiers mods)
2627                      {
2628                         if(projectView)
2629                         {
2630                            bool result = false;
2631                            bool isCObject = false;
2632                            ProjectNode node = null;
2633                            for(p : ide.workspace.projects)
2634                            {
2635                               node = projectView.GetNodeFromWindow(activeClient, p, false);
2636                               if(node) break;
2637                            }
2638                            if(!node && (node = projectView.GetNodeFromWindow(activeClient, null, true)))
2639                               isCObject = true;
2640                            if(node)
2641                            {
2642                               List<ProjectNode> nodes { };
2643                               nodes.Add(node);
2644                               projectView.Compile(node.project, nodes, mods.ctrl && mods.shift, isCObject ? cObject : normal);
2645                               delete nodes;
2646                               result = true;
2647                            }
2648                            if(!result && node)
2649                               ide.outputView.buildBox.Logf($"File %s is excluded from current build configuration.\n", node.name);
2650                         }
2651                         return true;
2652                      }
2653                   };
2654                   projectMenu.AddDynamic(projectCompileItem, ide, false);
2655                }
2656             }
2657          }
2658          else
2659          {
2660             caps = ovr = num = null;
2661          }
2662       }
2663    }
2664
2665    bool OnClose(bool parentClosing)
2666    {
2667       //return !projectView.buildInProgress;
2668       if(projectView && projectView.buildInProgress)
2669          return false;
2670       if(DontTerminateDebugSession($"Close IDE"))
2671          return false;
2672       if(findInFilesDialog)
2673          findInFilesDialog.SearchStop();
2674       if(workspace)
2675       {
2676          workspace.timer.Stop();
2677          workspace.Save();
2678       }
2679       ideMainFrame.Destroy(0);
2680       return true;
2681    }
2682
2683    bool OnPostCreate()
2684    {
2685       int c;
2686       bool passThrough = false;
2687       bool debugStart = false;
2688       bool debugWorkDir = false;
2689       char * passDebugWorkDir = null;
2690       bool openAsText = false;
2691       DynamicString passArgs { };
2692       for(c = 1; c<app.argc; c++)
2693       {
2694          if(!strcmp(app.argv[c], "-debug-start"))
2695             debugStart = true;
2696          else if(!strcmp(app.argv[c], "-debug-work-dir"))
2697             debugWorkDir = true;
2698          else if(!passThrough && !strcmp(app.argv[c], "-@"))
2699             passThrough = true;
2700          else if(passThrough)
2701          {
2702             passArgs.concat(" ");
2703             passArgs.concat(app.argv[c]);
2704          }
2705          else if(debugWorkDir)
2706          {
2707             passDebugWorkDir = CopyString(app.argv[c]);
2708             StripQuotes(passDebugWorkDir, passDebugWorkDir);
2709             debugWorkDir = false;
2710          }
2711          else
2712          {
2713             char fullPath[MAX_LOCATION];
2714             char parentPath[MAX_LOCATION];
2715             char ext[MAX_EXTENSION];
2716             bool isProject;
2717             FileAttribs dirAttribs;
2718             GetWorkingDir(fullPath, MAX_LOCATION);
2719             PathCat(fullPath, app.argv[c]);
2720             StripLastDirectory(fullPath, parentPath);
2721             GetExtension(app.argv[c], ext);
2722             isProject = !strcmpi(ext, "epj");
2723
2724             if(isProject && c > (debugStart ? 2 : 1)) continue;
2725
2726             // Create directory for projects (only)
2727             if(((dirAttribs = FileExists(parentPath)) && dirAttribs.isDirectory) || isProject)
2728             {
2729                if(isProject && !FileExists(fullPath))
2730                {
2731                   // The NewProject will handle directory creation
2732                   /*if(!dirAttribs.isDirectory)
2733                   {
2734                      MakeDir(parentPath);
2735                      dirAttribs = FileExists(parentPath);
2736                   }
2737                   if(dirAttribs.isDirectory)*/
2738                   {
2739                      char name[MAX_LOCATION];
2740                      NewProjectDialog newProjectDialog;
2741
2742                      if(projectView)
2743                      {
2744                         projectView.visible = false;
2745                         if(!projectView.Destroy(0))
2746                            return true;
2747                      }
2748
2749                      newProjectDialog = { master = this };
2750
2751                      strcpy(name, app.argv[c]);
2752                      StripExtension(name);
2753                      GetLastDirectory(name, name);
2754                      newProjectDialog.projectName.contents = name;
2755                      newProjectDialog.projectName.NotifyModified(newProjectDialog, newProjectDialog.projectName);
2756                      newProjectDialog.locationEditBox.path = parentPath;
2757                      newProjectDialog.NotifyModifiedLocation(newProjectDialog.locationEditBox);
2758
2759                      newProjectDialog.Modal();
2760                      if(projectView)
2761                      {
2762                         ideSettings.AddRecentProject(projectView.fileName);
2763                         ide.UpdateRecentMenus();
2764                         settingsContainer.Save();
2765                      }
2766                   }
2767                   // Open only one project
2768                   break;
2769                }
2770                else
2771                   ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, null, yes, normal);
2772             }
2773             else if(strstr(fullPath, "http://") == fullPath)
2774                ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, null, yes, normal);
2775          }
2776       }
2777       if(passThrough && projectView && projectView.project && workspace)
2778       {
2779          char * fixSpacing = new char[Max(passArgs.size * 1.5, 32)];
2780          {
2781             int c, d = 0;
2782             char j = 0;
2783             char k = ' ';
2784             char l = 0;
2785             bool inQuote = false;
2786             for(c=0; c<passArgs.size; c++)
2787             {
2788                l = passArgs[c];
2789                if(inQuote && k != '\\' && l == ' ')
2790                {
2791                   fixSpacing[d++] = '\"';
2792                   fixSpacing[d++] = ' ';
2793                   inQuote = false;
2794                }
2795                else if(inQuote && l == '\0')
2796                {
2797                   fixSpacing[d++] = '\"';
2798                   fixSpacing[d++] = '\0';
2799                   inQuote = false;
2800                }
2801                else if(!inQuote && j != '\\' && k == ' ' && l != '-' && l != ' ')
2802                {
2803                   fixSpacing[d++] = '\"';
2804                   fixSpacing[d++] = l;
2805                   inQuote = true;
2806                }
2807                else if(k == '\\' && l == ' ')
2808                   fixSpacing[d++] = ' ';
2809                else if(k == '\\' && l == '\\')
2810                   fixSpacing[d++] = '\\';
2811                else if(l != '\\')
2812                   fixSpacing[d++] = l;
2813                j = k;
2814                k = l;
2815             }
2816             fixSpacing[d] = '\0';
2817          }
2818          workspace.commandLineArgs = fixSpacing;
2819       }
2820       if(passDebugWorkDir)
2821       {
2822          workspace.debugDir = passDebugWorkDir;
2823          delete passDebugWorkDir;
2824       }
2825       if(debugStart)
2826          ;//MenuDebugStart(debugStartResumeItem, 0); // <-- how TODO this without getting into the app.Wait lock
2827
2828       UpdateToolBarActiveConfigs(false);
2829       UpdateToolBarActiveCompilers();
2830       delete passArgs;
2831       return true;
2832    }
2833
2834    void OnDestroy()
2835    {
2836       // IS THIS NEEDED? WASN'T HERE BEFORE...  Crashes on getting node's projectView otherwise
2837       if(projectView)
2838       {
2839          projectView.visible = false;
2840          projectView.Destroy(0);
2841          projectView = null;
2842       }
2843 #ifdef GDB_DEBUG_GUI
2844       gdbDialog.Destroy(0);
2845       delete gdbDialog;
2846 #endif
2847    }
2848
2849    void SetPath(bool projectsDirs, CompilerConfig compiler, ProjectConfig config, int bitDepth)
2850    {
2851       int c, len, count;
2852       char * newList;
2853       char * oldPaths[128];
2854       String oldList = new char[maxPathLen];
2855       Array<String> newExePaths { };
2856       //Map<String, bool> exePathExists { };
2857       bool found = false;
2858 #if defined(__unix__) || defined(__APPLE__)
2859       Array<String> newLibPaths { };
2860       Map<String, bool> libPathExists { };
2861 #endif
2862
2863       if(projectsDirs)
2864       {
2865          for(prj : workspace.projects)
2866          {
2867             DirExpression targetDirExp;
2868
2869             // SKIP FIRST PROJECT...
2870             if(prj == workspace.projects.firstIterator.data) continue;
2871
2872             // NOTE: Right now the additional project config dir will be
2873             //       obtained when the debugger is started, so toggling it
2874             //       while building will change which library gets used.
2875             //       To go with the initial state, e.g. when F5 was pressed,
2876             //       we nould need to keep a list of all project's active
2877             //       config upon startup.
2878             targetDirExp = prj.GetTargetDir(compiler, prj.config, bitDepth);
2879
2880             /*if(prj.config.targetType == sharedLibrary && prj.config.debug)
2881                cfg = prj.config;
2882             else
2883             {
2884                for(cfg = prj.configurations.first; cfg; cfg = cfg.next)
2885                   if(cfg.targetType == sharedLibrary && cfg.debug && !strcmpi(cfg.name, "Debug"))
2886                      break;
2887                if(!cfg)
2888                {
2889                   for(cfg = prj.configurations.first; cfg; cfg = cfg.next)
2890                      if(cfg.targetType == sharedLibrary && cfg.debug)
2891                         break;
2892                }
2893             }*/
2894             if(targetDirExp.dir)
2895             {
2896                char buffer[MAX_LOCATION];
2897 #if defined(__WIN32__)
2898                Array<String> paths = newExePaths;
2899 #else
2900                Array<String> paths = newLibPaths;
2901 #endif
2902                GetSystemPathBuffer(buffer, prj.topNode.path);
2903                PathCat(buffer, targetDirExp.dir);
2904                for(p : paths)
2905                {
2906                   if(!fstrcmp(p, buffer))
2907                   {
2908                      found = true;
2909                      break;
2910                   }
2911                }
2912                if(!found)
2913                   paths.Add(CopyString(buffer));
2914             }
2915             delete targetDirExp;
2916          }
2917       }
2918
2919       for(item : compiler.executableDirs)
2920       {
2921          found = false;
2922          for(p : newExePaths)
2923          {
2924             if(!fstrcmp(p, item))
2925             {
2926                found = true;
2927                break;
2928             }
2929          }
2930          if(!found)
2931             newExePaths.Add(CopySystemPath(item));
2932       }
2933
2934       GetEnvironment("PATH", oldList, maxPathLen);
2935 /*#ifdef _DEBUG
2936       printf("Old value of PATH: %s\n", oldList);
2937 #endif*/
2938       count = TokenizeWith(oldList, sizeof(oldPaths) / sizeof(char *), oldPaths, pathListSep, false);
2939       for(c = 0; c < count; c++)
2940       {
2941          found = false;
2942          for(p : newExePaths)
2943          {
2944             if(!fstrcmp(p, oldPaths[c]))
2945             {
2946                found = true;
2947                break;
2948             }
2949          }
2950          if(!found)
2951             newExePaths.Add(CopySystemPath(oldPaths[c]));
2952       }
2953
2954       len = 0;
2955       for(path : newExePaths)
2956          len += strlen(path) + 1;
2957       newList = new char[len + 1];
2958       newList[0] = '\0';
2959       for(path : newExePaths)
2960       {
2961          strcat(newList, path);
2962          strcat(newList, pathListSep);
2963       }
2964       newList[len - 1] = '\0';
2965       SetEnvironment("PATH", newList);
2966 /*#ifdef _DEBUG
2967       printf("New value of PATH: %s\n", newList);
2968 #endif*/
2969       delete newList;
2970
2971       newExePaths.Free();
2972       delete newExePaths;
2973
2974 #if defined(__unix__) || defined(__APPLE__)
2975
2976       for(item : compiler.libraryDirs)
2977       {
2978          if(!libPathExists[item])  // fstrcmp should be used
2979          {
2980             newLibPaths.Add(item);
2981             libPathExists[item] = true;
2982          }
2983       }
2984
2985 #if defined(__APPLE__)
2986       GetEnvironment("DYLD_LIBRARY_PATH", oldList, maxPathLen);
2987 #else
2988       GetEnvironment("LD_LIBRARY_PATH", oldList, maxPathLen);
2989 #endif
2990 /*#ifdef _DEBUG
2991       printf("Old value of [DY]LD_LIBRARY_PATH: %s\n", oldList);
2992 #endif*/
2993       count = TokenizeWith(oldList, sizeof(oldPaths) / sizeof(char *), oldPaths, pathListSep, false);
2994       for(c = 0; c < count; c++)
2995       {
2996          if(!libPathExists[oldPaths[c]])  // fstrcmp should be used
2997          {
2998             newLibPaths.Add(oldPaths[c]);
2999             libPathExists[oldPaths[c]] = true;
3000          }
3001       }
3002
3003       len = 0;
3004       for(path : newLibPaths)
3005          len += strlen(path) + 1;
3006       newList = new char[len + 1];
3007       newList[0] = '\0';
3008       for(path : newLibPaths)
3009       {
3010          strcat(newList, path);
3011          strcat(newList, pathListSep);
3012       }
3013       newList[len - 1] = '\0';
3014 #if defined(__APPLE__)
3015       SetEnvironment("DYLD_LIBRARY_PATH", newList);
3016 #else
3017       SetEnvironment("LD_LIBRARY_PATH", newList);
3018 #endif
3019 /*#ifdef _DEBUG
3020       printf("New value of [DY]LD_LIBRARY_PATH: %s\n", newList);
3021 #endif*/
3022       delete newList;
3023
3024       delete newLibPaths;
3025       delete libPathExists;
3026 #endif
3027
3028       if(compiler.distccEnabled && compiler.distccHosts)
3029          SetEnvironment("DISTCC_HOSTS", compiler.distccHosts);
3030
3031       delete oldList;
3032    }
3033
3034    void DestroyTemporaryProjectDir()
3035    {
3036       if(tmpPrjDir && tmpPrjDir[0])
3037       {
3038          if(FileExists(tmpPrjDir).isDirectory)
3039             DestroyDir(tmpPrjDir);
3040          property::tmpPrjDir = null;
3041       }
3042    }
3043
3044    IDEWorkSpace()
3045    {
3046       // Graphics Driver Menu
3047       int c;
3048
3049       /*
3050       app.currentSkin.selectionColor = selectionColor;
3051       app.currentSkin.selectionText = selectionText;
3052       */
3053
3054 /*
3055       driverItems = new MenuItem[app.numDrivers];
3056       for(c = 0; c < app.numDrivers; c++)
3057       {
3058          driverItems[c] = MenuItem { driversMenu, app.drivers[c], NotifySelect = NotifySelectDisplayDriver };
3059          driverItems[c].id = c;
3060          driverItems[c].isRadio = true;         
3061       }
3062 */
3063       driverItems = new MenuItem[2];
3064 #if defined(__unix__)
3065          driverItems[0] = MenuItem { driversMenu, "X", NotifySelect = NotifySelectDisplayDriver };
3066          driverItems[0].id = 0;
3067          driverItems[0].isRadio = true;         
3068 #else
3069          driverItems[0] = MenuItem { driversMenu, "GDI", NotifySelect = NotifySelectDisplayDriver };
3070          driverItems[0].id = 0;
3071          driverItems[0].isRadio = true;         
3072 #endif
3073          driverItems[1] = MenuItem { driversMenu, "OpenGL", NotifySelect = NotifySelectDisplayDriver };
3074          driverItems[1].id = 1;
3075          driverItems[1].isRadio = true;         
3076
3077 /*      skinItems = new MenuItem[app.numSkins];
3078       for(c = 0; c < app.numSkins; c++)
3079       {
3080          skinItems[c] = MenuItem {skinsMenu, app.skins[c], NotifySelect = NotifySelectDisplaySkin };
3081          skinItems[c].id = c;
3082          skinItems[c].isRadio = true;         
3083       }
3084 */
3085       ideFileDialog.master = this;
3086       ideProjectFileDialog.master = this;
3087
3088       //SetDriverAndSkin();
3089       return true;
3090    }
3091
3092    void UpdateRecentMenus()
3093    {
3094       int c;
3095       Menu fileMenu = menu.FindMenu($"File");
3096       Menu recentFiles = fileMenu.FindMenu($"Recent Files");
3097       Menu recentProjects = fileMenu.FindMenu($"Recent Projects");
3098       char itemName[MAX_LOCATION + 4];
3099       MenuItem item;
3100
3101       recentFiles.Clear();
3102       c = 0;
3103
3104       for(recent : ideSettings.recentFiles)
3105       {
3106          sprintf(itemName, "%d %s", 1 + c, recent);
3107          MakeSystemPath(itemName);
3108          recentFiles.AddDynamic(MenuItem { copyText = true, text = itemName, (Key)k1 + c, id = c, NotifySelect = ide.FileRecentFile }, ide, true);
3109          c++;
3110       }
3111
3112       recentProjects.Clear();
3113       c = 0;
3114       for(recent : ideSettings.recentProjects)
3115       {
3116          sprintf(itemName, "%d %s", 1 + c, recent);
3117          MakeSystemPath(itemName);
3118          recentProjects.AddDynamic(MenuItem { copyText = true, text = itemName, (Key)k1 + c, id = c, NotifySelect = ide.FileRecentProject }, ide, true);
3119          c++;
3120       }
3121    }
3122
3123    ~IDEWorkSpace()
3124    {
3125       delete driverItems;
3126       delete skinItems;
3127       delete ideSettings;
3128    }
3129 }
3130
3131 void DestroyDir(char * path)
3132 {
3133    RecursiveDeleteFolderFSI fsi { };
3134    fsi.Iterate(path);
3135    delete fsi;
3136 }
3137
3138 class RecursiveDeleteFolderFSI : NormalFileSystemIterator
3139 {
3140    bool preserveRootFolder;
3141
3142    void OutFolder(char * folderPath, bool isRoot)
3143    {
3144       if(!(preserveRootFolder && isRoot))
3145          RemoveDir(folderPath);
3146    }
3147
3148    bool OnFile(char * filePath)
3149    {
3150       DeleteFile(filePath);
3151       return true;
3152    }
3153 }
3154
3155 class IDEApp : GuiApplication
3156 {
3157    //driver = "Win32Console";
3158    // driver = "OpenGL";
3159    // skin = "Aqua";
3160    //skin = "TVision";
3161
3162    TempFile includeFile { };
3163
3164    bool Init()
3165    {
3166       SetLoggingMode(stdOut, null);
3167       //SetLoggingMode(debug, null);
3168
3169       settingsContainer.Load();
3170 #if defined(__unix__) || defined(__APPLE__)
3171       app.driver = (ideSettings.displayDriver && !strcmp(ideSettings.displayDriver, "OpenGL")) ? ideSettings.displayDriver : "X";
3172 #else
3173       app.driver = (ideSettings.displayDriver && !strcmp(ideSettings.displayDriver, "OpenGL")) ? ideSettings.displayDriver : "GDI";
3174 #endif
3175       ide.driverItems[ideSettings.displayDriver && !strcmp(ideSettings.displayDriver,"OpenGL")].checked = true;
3176
3177       SetInIDE(true);
3178
3179       desktop.text = titleECEREIDE;
3180       /*
3181       int c;
3182       for(c = 1; c<app.argc; c++)
3183       {
3184          char fullPath[MAX_LOCATION];
3185          GetWorkingDir(fullPath, MAX_LOCATION);
3186          PathCat(fullPath, app.argv[c]);
3187          ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, null, yes, normal);
3188       }
3189       */
3190
3191       if(!LoadIncludeFile())
3192          PrintLn("error: unable to load :crossplatform.mk file inside ide binary.");
3193
3194       return true;
3195    }
3196
3197    bool LoadIncludeFile()
3198    {
3199       bool result = false;
3200       File include = FileOpen(":crossplatform.mk", read);
3201       if(include)
3202       {
3203          File f = includeFile;
3204          if(f)
3205          {
3206             for(; !include.Eof(); )
3207             {
3208                char buffer[4096];
3209                int count = include.Read(buffer, 1, 4096);
3210                f.Write(buffer, 1, count);
3211             }
3212             result = true;
3213          }
3214          delete include;
3215       }
3216       return result;
3217    }
3218 }
3219
3220 IDEMainFrame ideMainFrame { };
3221
3222 define app = ((IDEApp)__thisModule);
3223 #ifdef _DEBUG
3224 define titleECEREIDE = $"ECERE IDE (Debug)";
3225 #else
3226 define titleECEREIDE = $"ECERE IDE";
3227 #endif