ide; fixed both ways: in a main project, if target is set to Shared Library, can...
[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, bool noParsing)
443       {
444          ide.GoToError(line, noParsing);
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, mods.ctrl && mods.shift))
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                bool isProjectFile;
803                char extension[MAX_EXTENSION] = "";
804                GetExtension(file, extension);
805                isProjectFile = (!strcmpi(extension, "epj") || !strcmpi(extension, "ews"));
806                if(mods.ctrl)
807                {
808                   char * command = PrintString("ide ", isProjectFile ? "-t " : "", file);
809                   Execute(command);
810                   delete command;
811                }
812                else
813                   OpenFile(file, normal, true, isProjectFile ? "txt" : null, no, normal, mods.ctrl && mods.shift);
814                break;
815             }
816             id++;
817          }
818          return true;
819       }
820
821       bool FileRecentProject(MenuItem selection, Modifiers mods)
822       {
823          int id = 0;
824          for(file : ideSettings.recentProjects)
825          {
826             if(id == selection.id)
827             {
828                if(mods.ctrl)
829                {
830                   char * command = PrintString("ide ", file);
831                   Execute(command);
832                   delete command;
833                }
834                else
835                   OpenFile(file, normal, true, null, no, normal, mods.ctrl && mods.shift);
836                break;
837             }
838             id++;
839          }
840          return true;
841       }
842
843    MenuPlacement editMenu { menu, $"Edit", e };
844    
845    Menu projectMenu { menu, $"Menu"."Project", p, hasMargin = true };
846       MenuItem projectNewItem
847       {
848          projectMenu, $"New...", n, Key { n, true, true };
849          bitmap = { ":actions/projNew.png" };
850          bool NotifySelect(MenuItem selection, Modifiers mods)
851          {
852             if(!DontTerminateDebugSession($"New Project"))
853                if(MenuWindowCloseAll(null, 0))
854                {
855                   NewProjectDialog newProjectDialog;
856
857                   if(projectView)
858                   {
859                      projectView.visible = false;
860                      if(!projectView.Destroy(0))
861                         return true;
862                   }
863                   
864                   newProjectDialog = { master = this };
865                   newProjectDialog.Modal();
866                   if(projectView)
867                   {
868                      ideSettings.AddRecentProject(projectView.fileName);
869                      ide.UpdateRecentMenus();
870                      settingsContainer.Save();
871                   }
872                }
873             return true;
874          }
875       }
876       MenuItem projectOpenItem
877       {
878          projectMenu, $"Open...", o, Key { o, true, true };
879          bitmap = { ":actions/projOpen.png" };
880          bool NotifySelect(MenuItem selection, Modifiers mods)
881          {
882             if(ideSettings.ideProjectFileDialogLocation)
883                ideProjectFileDialog.currentDirectory = ideSettings.ideProjectFileDialogLocation;
884
885             ideProjectFileDialog.text = openProjectFileDialogTitle;
886             if(ideProjectFileDialog.Modal() == ok)
887             {
888                OpenFile(ideProjectFileDialog.filePath, normal, true, projectTypes[ideProjectFileDialog.fileType].typeExtension, no, normal, mods.ctrl && mods.shift);
889                //ChangeProjectFileDialogDirectory(ideProjectFileDialog.currentDirectory);
890             }
891             return true;
892          }
893       }
894       MenuItem projectQuickItem
895       {
896          projectMenu, $"Quick...", q, f7, disabled = true;
897          bool NotifySelect(MenuItem selection, Modifiers mods)
898          {
899             if(!projectView)
900                QuickProjectDialog { this }.Modal();
901             return true;
902          }
903       }
904       MenuItem projectAddItem
905       {
906          projectMenu, $"Add project to workspace...", a, Key { a, true, true };
907          bitmap = { ":actions/projAdd.png" };
908          disabled = true;
909          bool NotifySelect(MenuItem selection, Modifiers mods)
910          {
911             if(ideSettings.ideProjectFileDialogLocation)
912                ideProjectFileDialog.currentDirectory = ideSettings.ideProjectFileDialogLocation;
913
914             ideProjectFileDialog.text = addProjectFileDialogTitle;
915             for(;;)
916             {
917                if(ideProjectFileDialog.Modal() == ok)
918                {
919                   if(OpenFile(ideProjectFileDialog.filePath, normal, true, projectTypes[ideProjectFileDialog.fileType].typeExtension, no, add, mods.ctrl && mods.shift))
920                      break;
921                   if(MessageBox { type = yesNo, master = this, text = $"Error opening project file", 
922                         contents = $"Add a different project?" }.Modal() == no)
923                   {
924                      break;
925                   }
926                }
927                else
928                   break;
929             }
930             return true;
931          }
932       }
933       MenuItem projectCloseItem
934       {
935          projectMenu, $"Close", c, disabled = true;
936          bool NotifySelect(MenuItem selection, Modifiers mods)
937          {
938             if(projectView)
939             {
940                if(!ide.DontTerminateDebugSession($"Project Close"))
941                {
942                   if(findInFilesDialog)
943                      findInFilesDialog.SearchStop();
944                   projectView.visible = false;
945                   if(projectView.Destroy(0))
946                      MenuWindowCloseAll(null, 0);
947                   {
948                      char workingDir[MAX_LOCATION];
949                      GetWorkingDir(workingDir, MAX_LOCATION);
950                      findInFilesDialog.currentDirectory = workingDir;
951                      ideMainFrame.text = titleECEREIDE;
952                   }
953                   ide.AdjustMenus();
954                }
955             }
956             return true;
957          }
958       }
959       MenuDivider { projectMenu };
960       MenuItem projectSettingsItem
961       {
962          projectMenu, $"Settings...", s, altF7, disabled = true;
963          bool NotifySelect(MenuItem selection, Modifiers mods)
964          {
965             projectView.MenuSettings(projectView.active ? selection : null, mods);
966             return true;
967          }
968       }
969       MenuDivider { projectMenu };
970       MenuItem projectBrowseFolderItem
971       {
972          projectMenu, $"Browse Project Folder", p, disabled = true;
973          bool NotifySelect(MenuItem selection, Modifiers mods)
974          {
975             if(projectView)
976                projectView.MenuBrowseFolder(null, mods);
977             return true;
978          }
979       }
980       MenuDivider { projectMenu };
981       MenuItem projectRunItem
982       {
983          projectMenu, $"Run", r, ctrlF5, disabled = true;
984          bitmap = { ":actions/run.png" };
985          bool NotifySelect(MenuItem selection, Modifiers mods)
986          {
987             if(projectView)
988                projectView.Run(null, mods);
989             return true;
990          }
991       }
992       MenuItem projectBuildItem
993       {
994          projectMenu, $"Build", b, f7, disabled = true;
995          bitmap = { ":actions/build.png" };
996          bool NotifySelect(MenuItem selection, Modifiers mods)
997          {
998             if(projectView)
999             {
1000                if(projectView.buildInProgress == none)
1001                   projectView.ProjectBuild(projectView.active ? selection : null, mods);
1002                else
1003                   projectView.stopBuild = true;
1004             }
1005             return true;
1006          }
1007       }
1008       MenuItem projectLinkItem
1009       {
1010          projectMenu, $"Relink", l, disabled = true;
1011          bitmap = { ":actions/relink.png" };
1012          bool NotifySelect(MenuItem selection, Modifiers mods)
1013          {
1014             if(projectView)
1015                projectView.ProjectLink(projectView.active ? selection : null, mods);
1016             return true;
1017          }
1018       }
1019       MenuItem projectRebuildItem
1020       {
1021          projectMenu, $"Rebuild", d, shiftF7, disabled = true;
1022          bitmap = { ":actions/rebuild.png" };
1023          bool NotifySelect(MenuItem selection, Modifiers mods)
1024          {
1025             if(projectView)
1026                projectView.ProjectRebuild(projectView.active ? selection : null, mods);
1027             return true;
1028          }
1029       }
1030       MenuItem projectCleanTargetItem
1031       {
1032          projectMenu, $"Clean Target", g, disabled = true;
1033          bitmap = { ":actions/clean.png" };
1034          bool NotifySelect(MenuItem selection, Modifiers mods)
1035          {
1036             if(projectView)
1037             {
1038                debugger.Stop();
1039                projectView.ProjectCleanTarget(projectView.active ? selection : null, mods);
1040             }
1041             return true;
1042          }
1043       }
1044       MenuItem projectCleanItem
1045       {
1046          projectMenu, $"Clean", e, disabled = true;
1047          bitmap = { ":actions/clean.png" };
1048          bool NotifySelect(MenuItem selection, Modifiers mods)
1049          {
1050             if(projectView)
1051             {
1052                debugger.Stop();
1053                projectView.ProjectClean(projectView.active ? selection : null, mods);
1054             }
1055             return true;
1056          }
1057       }
1058       MenuItem projectRealCleanItem
1059       {
1060          projectMenu, $"Real Clean", disabled = true;
1061          bitmap = { ":actions/clean.png" };
1062          bool NotifySelect(MenuItem selection, Modifiers mods)
1063          {
1064             if(projectView)
1065             {
1066                debugger.Stop();
1067                projectView.ProjectRealClean(projectView.active ? selection : null, mods);
1068             }
1069             return true;
1070          }
1071       }
1072       MenuItem projectRegenerateItem
1073       {
1074          projectMenu, $"Regenerate Makefile", m, disabled = true;
1075          bitmap = { ":actions/regMakefile.png" };
1076          bool NotifySelect(MenuItem selection, Modifiers mods)
1077          {
1078             if(projectView)
1079                projectView.ProjectRegenerate(projectView.active ? selection : null, mods);
1080             return true;
1081          }
1082       }
1083       MenuItem projectCompileItem;
1084    Menu debugMenu { menu, $"Debug", d, hasMargin = true };
1085       MenuItem debugStartResumeItem
1086       {
1087          debugMenu, $"Start", s, f5, disabled = true;
1088          bitmap = { ":actions/debug.png" };
1089          NotifySelect = MenuDebugStart;
1090       }
1091       bool MenuDebugStart(MenuItem selection, Modifiers mods)
1092       {
1093          if(projectView)
1094          {
1095             debugStartResumeItem.disabled = true; // a very rare exception to calling AdjustDebugMenus
1096             if(!projectView.DebugStart())
1097                debugStartResumeItem.disabled = false; // same exception
1098          }
1099          return true;
1100       }
1101       bool MenuDebugResume(MenuItem selection, Modifiers mods)
1102       {
1103          if(projectView)
1104             projectView.DebugResume();
1105          return true;
1106       }
1107       MenuItem debugRestartItem
1108       {
1109          debugMenu, $"Restart", r, Key { f5, ctrl = true, shift = true }, disabled = true;
1110          bitmap = { ":actions/restart.png" };
1111          bool NotifySelect(MenuItem selection, Modifiers mods)
1112          {
1113             if(projectView)
1114                projectView.DebugRestart();
1115             return true;
1116          }
1117       }
1118       MenuItem debugBreakItem
1119       {
1120          debugMenu, $"Break", b, Key { pauseBreak, ctrl = true }, disabled = true;
1121          bitmap = { ":actions/pause.png" };
1122          bool NotifySelect(MenuItem selection, Modifiers mods)
1123          {
1124             if(projectView && projectView.buildInProgress != none)
1125                return true;
1126             if(projectView)
1127                projectView.DebugBreak();
1128             return true;
1129          }
1130       }
1131       MenuItem debugStopItem
1132       {
1133          debugMenu, $"Stop", p, shiftF5, disabled = true;
1134          bitmap = { ":actions/stopDebug.png" };
1135          bool NotifySelect(MenuItem selection, Modifiers mods)
1136          {
1137             if(projectView)
1138                projectView.DebugStop();
1139             return true;
1140          }
1141       }
1142       MenuDivider { debugMenu };
1143       MenuItem debugStepIntoItem
1144       {
1145          debugMenu, $"Step Into", i, f11, disabled = true;
1146          bitmap = { ":actions/stepInto.png" };
1147          bool NotifySelect(MenuItem selection, Modifiers mods)
1148          {
1149             if(projectView)
1150                projectView.DebugStepInto();
1151             return true;
1152          }
1153       }
1154       MenuItem debugStepOverItem
1155       {
1156          debugMenu, $"Step Over", v, f10, disabled = true;
1157          bitmap = { ":actions/stepOver.png" };
1158          bool NotifySelect(MenuItem selection, Modifiers mods)
1159          {
1160             if(projectView)
1161                projectView.DebugStepOver(false);
1162             return true;
1163          }
1164       }
1165       MenuItem debugStepOutItem
1166       {
1167          debugMenu, $"Step Out", o, shiftF11, disabled = true;
1168          bitmap = { ":actions/stepOut.png" };
1169          bool NotifySelect(MenuItem selection, Modifiers mods)
1170          {
1171             if(projectView)
1172                projectView.DebugStepOut(false);
1173             return true;
1174          }
1175       }
1176       MenuPlacement debugRunToCursorItem { debugMenu, $"Run To Cursor", c };
1177       MenuItem debugSkipStepOverItem
1178       {
1179          debugMenu, $"Step Over Skipping Breakpoints", e, shiftF10, disabled = true;
1180          bool NotifySelect(MenuItem selection, Modifiers mods)
1181          {
1182             if(projectView)
1183                projectView.DebugStepOver(true);
1184             return true;
1185          }
1186       }
1187       MenuItem debugSkipStepOutItem
1188       {
1189          debugMenu, $"Step Out Skipping Breakpoints", t, Key { f11, ctrl = true, shift = true }, disabled = true;
1190          bitmap = { ":actions/skipBreaks.png" };
1191          bool NotifySelect(MenuItem selection, Modifiers mods)
1192          {
1193             if(projectView)
1194                projectView.DebugStepOut(true);
1195             return true;
1196          }
1197       }
1198       MenuPlacement debugSkipRunToCursorItem { debugMenu, $"Run To Cursor Skipping Breakpoints", u };
1199       //MenuDivider { debugMenu };
1200       //MenuPlacement debugToggleBreakpoint { debugMenu, "Toggle Breakpoint", t };
1201    MenuPlacement imageMenu { menu, $"Image", i };
1202    Menu viewMenu { menu, $"View", v };
1203       MenuItem viewProjectItem
1204       {
1205          viewMenu, $"Project View", j, alt0, disabled = true;
1206          bool NotifySelect(MenuItem selection, Modifiers mods)
1207          {
1208             if(projectView)
1209             {
1210                projectView.visible = true;
1211                projectView.Activate();
1212             }
1213             return true;
1214          }
1215       }
1216       MenuPlacement { viewMenu, $"View Designer" };
1217       MenuPlacement { viewMenu, $"View Code" };
1218       MenuPlacement { viewMenu, $"View Properties" };
1219       MenuPlacement { viewMenu, $"View Methods" };
1220       MenuItem viewDesignerItem
1221       {
1222          viewMenu, $"View Designer", d, f8;
1223          bool NotifySelect(MenuItem selection, Modifiers mods)
1224          {
1225             Window client = activeClient;
1226             Class dataType = client._class;
1227             if(!strcmp(dataType.name, "Designer"))
1228             {
1229                client.visible = true;
1230                client.Activate();
1231             }
1232             else
1233                ((CodeEditor)client).ViewDesigner();
1234             return true;
1235          }
1236       }
1237       MenuItem viewCodeItem
1238       {
1239          viewMenu, $"View Code", c, f8;
1240          bool NotifySelect(MenuItem selection, Modifiers mods)
1241          {
1242             Window client = activeClient;
1243             Class dataType = client._class;
1244             if(!strcmp(dataType.name, "Designer"))
1245                client = ((Designer)client).codeEditor;
1246
1247             client.Activate();
1248             // Do this after so the caret isn't moved yet...
1249             client.visible = true;
1250             return true;
1251          }
1252       }
1253       MenuItem viewPropertiesItem
1254       {
1255          viewMenu, $"View Properties", p, f4;
1256          bool NotifySelect(MenuItem selection, Modifiers mods)
1257          {
1258             sheet.visible = true;
1259             sheet.sheetSelected = properties;
1260             sheet.Activate();
1261             return true;
1262          }
1263       }
1264       MenuItem viewMethodsItem
1265       {
1266          viewMenu, $"View Methods", m, f4;
1267          bool NotifySelect(MenuItem selection, Modifiers mods)
1268          {
1269             sheet.visible = true;
1270             sheet.sheetSelected = methods;
1271             sheet.Activate();
1272             return true;
1273          }
1274       }
1275       MenuItem viewToolBoxItem
1276       {
1277          viewMenu, $"View Toolbox", x, f12;
1278          bool NotifySelect(MenuItem selection, Modifiers mods)
1279          {
1280             toolBox.visible = true;
1281             toolBox.Activate();
1282             return true;
1283          }
1284       }
1285       MenuItem viewOutputItem
1286       {
1287          viewMenu, $"Output", o, alt2;
1288          bool NotifySelect(MenuItem selection, Modifiers mods)
1289          {
1290             outputView.Show();
1291             return true;
1292          }
1293       }
1294       MenuItem viewWatchesItem
1295       {
1296          viewMenu, $"Watches", w, alt3;
1297          bool NotifySelect(MenuItem selection, Modifiers mods)
1298          {
1299             watchesView.Show();
1300             return true;
1301          }
1302       }
1303       MenuItem viewThreadsItem
1304       {
1305          viewMenu, $"Threads", t, alt4;
1306          bool NotifySelect(MenuItem selection, Modifiers mods)
1307          {
1308             threadsView.Show();
1309             return true;
1310          }
1311       }
1312       MenuItem viewBreakpointsItem
1313       {
1314          viewMenu, $"Breakpoints", b, alt5;
1315          bool NotifySelect(MenuItem selection, Modifiers mods)
1316          {
1317             breakpointsView.Show();
1318             return true;
1319          }
1320       }
1321       MenuItem viewCallStackItem
1322       {
1323          viewMenu, $"Call Stack", s, alt7;
1324          bool NotifySelect(MenuItem selection, Modifiers mods)
1325          {
1326             callStackView.Show();
1327             return true;
1328          }
1329       }
1330       MenuItem viewAllDebugViews
1331       {
1332          viewMenu, $"All Debug Views", a, alt9;
1333          bool NotifySelect(MenuItem selection, Modifiers mods)
1334          {
1335             outputView.Show();
1336             watchesView.Show();
1337             threadsView.Show();
1338             callStackView.Show();
1339             breakpointsView.Show();
1340             return true;
1341          }
1342       }
1343 #ifdef GDB_DEBUG_GUI
1344       MenuDivider { viewMenu };
1345       MenuItem viewGDBItem
1346       {
1347          viewMenu, $"GDB Dialog", g, Key { f9, shift = true, ctrl = true };
1348          bool NotifySelect(MenuItem selection, Modifiers mods)
1349          {
1350             gdbDialog.Show();
1351             return true;
1352          }
1353       }
1354 #endif
1355       MenuDivider { viewMenu };
1356       MenuItem viewColorPicker
1357       {
1358          viewMenu, $"Color Picker...", c, Key { c, ctrl = true , shift = true };
1359          bool NotifySelect(MenuItem selection, Modifiers mods)
1360          {
1361             ColorPicker colorPicker { master = this };
1362             colorPicker.Modal();
1363             return true;
1364          }
1365       }
1366       MenuDivider { viewMenu };
1367       /*
1368       MenuItem
1369       {
1370          viewMenu, "Full Screen", f, checkable = true;
1371
1372          bool NotifySelect(MenuItem selection, Modifiers mods)
1373          {
1374             app.fullScreen ^= true;
1375             SetDriverAndSkin();
1376             anchor = { 0, 0, 0, 0 };
1377             return true;
1378          }
1379       };
1380       */
1381       Menu driversMenu { viewMenu, $"Graphics Driver", v };
1382       //Menu skinsMenu { viewMenu, "GUI Skins", k };
1383    Menu windowMenu { menu, $"Window", w };
1384       MenuItem { windowMenu, $"Close All", l, NotifySelect = MenuWindowCloseAll };
1385       MenuDivider { windowMenu };
1386       MenuItem { windowMenu, $"Next", n, f6, NotifySelect = MenuWindowNext };
1387       MenuItem { windowMenu, $"Previous", p, shiftF6, NotifySelect = MenuWindowPrevious };
1388       MenuDivider { windowMenu };
1389       MenuItem { windowMenu, $"Cascade", c, NotifySelect = MenuWindowCascade };
1390       MenuItem { windowMenu, $"Tile Horizontally", h, NotifySelect = MenuWindowTileHorz };
1391       MenuItem { windowMenu, $"Tile Vertically", v, NotifySelect = MenuWindowTileVert };
1392       MenuItem { windowMenu, $"Arrange Icons", a, NotifySelect = MenuWindowArrangeIcons };
1393       MenuDivider { windowMenu };
1394       MenuItem { windowMenu, $"Windows...", w, NotifySelect = MenuWindowWindows };
1395    Menu helpMenu { menu, $"Help", h };
1396       MenuItem
1397       {
1398          helpMenu, $"API Reference", r, f1;
1399          bool NotifySelect(MenuItem selection, Modifiers mods)
1400          {
1401             Execute("documentor");
1402             return true;
1403          }
1404       }
1405       MenuDivider { helpMenu };
1406       MenuItem
1407       {
1408          helpMenu, $"About...", a;
1409          bool NotifySelect(MenuItem selection, Modifiers mods)
1410          {
1411             AboutIDE { master = this }.Modal();
1412             return true;
1413          }
1414       }
1415
1416    property ToolBox toolBox
1417    {
1418       get { return toolBox; }
1419    }
1420
1421    property Sheet sheet
1422    {
1423       get { return sheet; }
1424    }
1425
1426    property Project project
1427    {
1428       get { return projectView ? projectView.project : null; }
1429    }
1430
1431    property Workspace workspace
1432    {
1433       get { return projectView ? projectView.workspace : null; }
1434    }
1435
1436    FindInFilesDialog findInFilesDialog
1437    {
1438       master = this,
1439       filters = findInFilesFileFilters.array, sizeFilters = findInFilesFileFilters.count * sizeof(FileFilter);
1440       filter = 1;
1441    };
1442
1443    bool noParsing;
1444
1445 #ifdef GDB_DEBUG_GUI
1446    GDBDialog gdbDialog
1447    {
1448       master = this, parent = this;
1449       anchor = { left = 100, top = 100, right = 100, bottom = 100 };
1450
1451       void OnCommand(char * string)
1452       {
1453          if(ide)
1454             ide.debugger.SendGDBCommand(string);
1455       }
1456    };
1457 #endif
1458    
1459    bool NotifySelectDisplayDriver(MenuItem selection, Modifiers mods)
1460    {
1461       //app.driver = app.drivers[selection.id];
1462 #if defined(__unix__) || defined(__APPLE__)
1463       app.driver = selection.id ? "OpenGL" : "X";
1464 #else
1465       app.driver = selection.id ? "OpenGL" : "GDI";
1466 #endif
1467       delete ideSettings.displayDriver;
1468       ideSettings.displayDriver = CopyString(selection.id ? "OpenGL" : "Default");
1469
1470       settingsContainer.Save();
1471       //SetDriverAndSkin();
1472       return true;
1473    }
1474
1475    bool NotifySelectDisplaySkin(MenuItem selection, Modifiers mods)
1476    {
1477       app.skin = app.skins[selection.id];
1478       SetDriverAndSkin();
1479       return true;
1480    }
1481
1482    void SetDriverAndSkin()
1483    {
1484       int c;
1485       for(c = 0; c < app.numSkins; c++)
1486          if(!strcmp(app.skins[c], app.skin))
1487          {
1488             skinItems[c].checked = true;
1489             break;
1490          }
1491       for(c = 0; c < app.numDrivers; c++)
1492          if(!strcmp(app.drivers[c], app.driver))
1493          {
1494             driverItems[c].checked = true;
1495             break;
1496          }
1497    }
1498
1499    ProjectView CreateProjectView(Workspace workspace, char * fileName)
1500    {
1501       Project project = workspace.projects.firstIterator.data;
1502       projectView = ProjectView
1503       {
1504          this;
1505          fileName = fileName;
1506          
1507          void NotifyDestroyed(Window window, DialogResult result)
1508          {
1509             projectView = null;
1510             text = titleECEREIDE;
1511             
1512             AdjustMenus();
1513          }
1514       };
1515       projectView.Create();
1516       RepositionWindows(false);
1517
1518       // Leave it after Create to avoid flicker due to seeing IDE without a project view
1519       projectView.workspace = workspace;
1520       projectView.project = project;
1521       ideMainFrame.SetText("%s - %s", project.topNode.name, titleECEREIDE);
1522
1523       AdjustMenus();
1524
1525       ide.breakpointsView.LoadFromWorkspace();
1526       ide.watchesView.LoadFromWorkspace();
1527
1528       findInFilesDialog.projectNodeField.userData = projectView;
1529
1530       {
1531          char fileName[MAX_LOCATION];
1532          strcpy(fileName, project.topNode.path);
1533          PathCat(fileName, project.topNode.name);
1534       }
1535       return projectView;
1536    }
1537
1538    void RepositionWindows(bool expand)
1539    {
1540       if(this)
1541       {
1542          Window child;
1543          bool inDebugMode = debugger.isActive;
1544          bool callStackVisible = expand ? false : callStackView.visible;
1545          bool threadsVisible = expand ? false : threadsView.visible;
1546          bool watchesVisible = expand ? false : watchesView.visible;
1547          bool breakpointsVisible = expand ? false : breakpointsView.visible;
1548          bool toolBoxVisible = toolBox.visible;
1549          bool outputVisible = expand ? false : outputView.visible;
1550          int topDistance = (callStackVisible || threadsVisible) ? 200 : 0;
1551          int bottomDistance = (outputVisible || watchesVisible || breakpointsVisible) ? 240 : 0;
1552          
1553          for(child = firstChild; child; child = child.next)
1554          {
1555             if(child._class == class(CodeEditor) || child._class == class(Designer) || 
1556                child._class == class(Sheet) || child._class == class(ProjectView))
1557             {
1558                Anchor anchor = child.anchor;
1559                anchor.top = topDistance;
1560                anchor.bottom = bottomDistance;
1561                if(child._class == class(CodeEditor) || child._class == class(Designer))
1562                {
1563                   anchor.right = toolBoxVisible ? 150 : 0;
1564                }
1565                child.anchor = anchor;
1566             }
1567             else if(expand)
1568             {
1569                if(child._class == class(OutputView) || child._class == class(CallStackView) || child._class == class(ThreadsView) || child._class == class(WatchesView) || 
1570                   child._class == class(BreakpointsView))
1571                   child.visible = false;
1572             }
1573          }
1574          // If this is not here, the IDE is not updated when doing Debug/Break then Alt-4 to show call stack (IDE not updated)
1575          Update(null);
1576       }
1577    }
1578
1579    bool ShowCodeEditor()
1580    {
1581       if(activeClient)
1582          activeClient.Activate();
1583       else if(projectView)
1584       { 
1585          projectView.visible = true;
1586          projectView.Activate();
1587       }
1588       else
1589       {
1590          sheet.visible = true;
1591          sheet.Activate();
1592       }
1593       return false;
1594    }
1595
1596    void DocumentSaved(Window document, char * fileName)
1597    {
1598       ideSettings.AddRecentFile(fileName);
1599       ide.UpdateRecentMenus();
1600       ide.AdjustFileMenus();
1601       settingsContainer.Save();
1602    }
1603
1604    bool Window::OnFileModified(FileChange fileChange, char * param)
1605    {
1606       char temp[4096];
1607       sprintf(temp, $"The document %s was modified by another application.\n"
1608             "Would you like to reload it and lose your changes?", this.fileName);
1609       if(MessageBox { type = yesNo, master = this/*.parent*/,
1610             text = $"Document has been modified", contents = temp }.Modal() == yes)
1611       {
1612          bool noParsing = (this._class == class(CodeEditor) && ((CodeEditor)this).noParsing) ? true : false;
1613          char * fileName = CopyString(this.fileName);
1614          WindowState state = this.state;
1615          Anchor anchor = this.anchor;
1616          Size size = this.size;
1617
1618          this.modifiedDocument = false;
1619          this.Destroy(0);
1620          this = ide.OpenFile(fileName, normal, true, null, no, normal, noParsing);
1621          if(this)
1622          {
1623             this.anchor = anchor;
1624             this.size = size;
1625             this.SetState(state, true, 0);
1626          }
1627          delete fileName;
1628          return true;
1629       }
1630       return true;
1631    }
1632
1633    void UpdateMakefiles()
1634    {
1635       if(workspace)
1636       {
1637          CompilerConfig compiler = ideSettings.GetCompilerConfig(workspace.compiler);
1638          for(prj : workspace.projects)
1639             projectView.ProjectUpdateMakefileForAllConfigs(prj);
1640          delete compiler;
1641       }
1642    }
1643
1644    void UpdateCompilerConfigs(bool mute)
1645    {
1646       UpdateToolBarActiveCompilers();
1647       if(workspace)
1648       {
1649          bool silent = mute || (ide.projectView.buildInProgress == none ? false : true);
1650          CompilerConfig compiler = ideSettings.GetCompilerConfig(workspace.compiler);
1651          if(!silent)
1652          {
1653             projectView.ShowOutputBuildLog(true);
1654             projectView.DisplayCompiler(compiler, false);
1655          }
1656          for(prj : workspace.projects)
1657             projectView.ProjectPrepareCompiler(prj, compiler, silent);
1658          delete compiler;
1659       }
1660    }
1661
1662    void UpdateToolBarActiveCompilers()
1663    {
1664       toolBar.activeCompiler.Clear();
1665       for(compiler : ideSettings.compilerConfigs)
1666       {
1667          DataRow row = toolBar.activeCompiler.AddString(compiler.name);
1668          if(workspace && workspace.compiler && !strcmp(compiler.name, workspace.compiler))
1669             toolBar.activeCompiler.currentRow = row;
1670       }
1671       if(!toolBar.activeCompiler.currentRow && toolBar.activeCompiler.firstRow)
1672          toolBar.activeCompiler.SelectRow(toolBar.activeCompiler.firstRow);
1673    }
1674
1675    void UpdateToolBarActiveConfigs(bool selectionOnly)
1676    {
1677       bool commonSelected = false;
1678       DataRow row = toolBar.activeConfig.currentRow;
1679       if(selectionOnly)
1680          row = toolBar.activeConfig.FindRow(1);
1681       else
1682       {
1683          toolBar.activeConfig.Clear();
1684          row = toolBar.activeConfig.AddString("(Mixed)");
1685          row.tag = 1;
1686       }
1687       if(workspace)
1688       {
1689          char * configName = null;
1690          if(!selectionOnly)
1691          {
1692             Map<String, int> configs { }; // TOIMP: just need sort but using map until containers have sort
1693             for(prj : workspace.projects)
1694             {
1695                for(cfg : prj.configurations)
1696                {
1697                   if(cfg.name)
1698                      configs[cfg.name] = 1;
1699                }
1700             }
1701             for(name : configs)
1702             {
1703                toolBar.activeConfig.AddString(&name);
1704             }
1705             delete configs;
1706          }
1707          if(projectView && projectView.project)
1708          {
1709             for(prj : workspace.projects)
1710             {
1711                if(prj.config && prj.config.name)
1712                {
1713                   configName = prj.config.name;
1714                   break;
1715                }
1716             }
1717             if(configName)
1718             {
1719                commonSelected = true;
1720                for(prj : workspace.projects)
1721                {
1722                   if(prj.config && (!prj.config.name || strcmp(prj.config.name, configName)))
1723                   {
1724                      commonSelected = false;
1725                      break;
1726                   }
1727                }
1728             }
1729          }
1730          if(commonSelected)
1731          {
1732             commonSelected = false;
1733             for(row = toolBar.activeConfig.firstRow; row; row = row.next)
1734             {
1735                if(!strcmp(row.string, configName))
1736                {
1737                   toolBar.activeConfig.currentRow = row;
1738                   commonSelected = true;
1739                   break;
1740                }
1741             }
1742          }
1743       }
1744       if(!selectionOnly)
1745          toolBar.activeConfig.Sort(null, 0);
1746       if(!commonSelected)
1747          toolBar.activeConfig.currentRow = row;
1748    }
1749
1750    void AdjustMenus()
1751    {
1752       bool unavailable = !project;
1753
1754       projectAddItem.disabled             = unavailable;
1755       toolBar.buttonAddProject.disabled   = unavailable;
1756
1757       projectSettingsItem.disabled        = unavailable;
1758
1759       projectBrowseFolderItem.disabled    = unavailable;
1760
1761       viewProjectItem.disabled            = unavailable;
1762
1763       toolBar.activeConfig.disabled       = unavailable;
1764       toolBar.activeCompiler.disabled     = unavailable;
1765       toolBar.activeBitDepth.disabled     = unavailable;
1766
1767       AdjustFileMenus();
1768       AdjustBuildMenus();
1769       AdjustDebugMenus();
1770    }
1771
1772    property bool hasOpenedCodeEditors
1773    {
1774       get
1775       {
1776          Window w;
1777          for(w = firstChild; w; w = w.next)
1778             if(w._class == class(CodeEditor) &&
1779                   w.isDocument && !w.closing && w.visible && w.created &&
1780                   w.fileName && w.fileName[0])
1781                return true;
1782          return false;
1783       }
1784    }
1785
1786    void AdjustFileMenus()
1787    {
1788       bool unavailable = project != null || !hasOpenedCodeEditors; // are they supported source code (ec, c, cpp, etc) ?
1789
1790       projectQuickItem.disabled           = unavailable;
1791    }
1792
1793    void AdjustBuildMenus()
1794    {
1795       bool unavailable = project && projectView.buildInProgress;
1796
1797       projectNewItem.disabled             = unavailable;
1798       toolBar.buttonNewProject.disabled   = unavailable;
1799       projectOpenItem.disabled            = unavailable;
1800       toolBar.buttonOpenProject.disabled  = unavailable;
1801
1802       unavailable = !project || projectView.buildInProgress;
1803
1804       projectCloseItem.disabled           = unavailable;
1805       // toolBar.buttonCloseProject.disabled = unavailable;
1806
1807       projectRunItem.disabled    = unavailable || project.GetTargetType(project.config) != executable;
1808       toolBar.buttonRun.disabled = unavailable || project.GetTargetType(project.config) != executable;
1809
1810       projectBuildItem.disabled = false;
1811       projectBuildItem.text     = unavailable ? $"Stop Build" : $"Build";
1812       projectBuildItem.accelerator = unavailable ? Key { pauseBreak, ctrl = true } : f7;
1813
1814       projectLinkItem.disabled                  = unavailable;
1815       toolBar.buttonReLink.disabled             = unavailable;
1816       projectRebuildItem.disabled               = unavailable;
1817       toolBar.buttonRebuild.disabled            = unavailable;
1818       projectCleanItem.disabled                 = unavailable;
1819       toolBar.buttonClean.disabled              = unavailable;
1820       projectCleanTargetItem.disabled           = unavailable;
1821       projectRealCleanItem.disabled             = unavailable;
1822       // toolBar.buttonRealClean.disabled          = unavailable;
1823       projectRegenerateItem.disabled            = unavailable;
1824       toolBar.buttonRegenerateMakefile.disabled = unavailable;
1825       projectCompileItem.disabled               = unavailable;
1826
1827       AdjustPopupBuildMenus();
1828    }
1829
1830    void AdjustPopupBuildMenus()
1831    {
1832       bool unavailable = !project || projectView.buildInProgress;
1833
1834       if(projectView && projectView.popupMenu && projectView.popupMenu.menu && projectView.popupMenu.created)
1835       {
1836          MenuItem menu;
1837          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectBuild, 0);
1838          if(menu)
1839          {
1840             menu.disabled = false;
1841             menu.text   = unavailable ? $"Stop Build" : $"Build";
1842             menu.accelerator = unavailable ? Key { pauseBreak, ctrl = true } : f7;
1843          }
1844
1845          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectLink, 0);              if(menu) menu.disabled = unavailable;
1846          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectRebuild, 0);           if(menu) menu.disabled = unavailable;
1847          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectCleanTarget, 0);       if(menu) menu.disabled = unavailable;
1848          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectClean, 0);             if(menu) menu.disabled = unavailable;
1849          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectRealClean, 0);         if(menu) menu.disabled = unavailable;
1850          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectRegenerate, 0);        if(menu) menu.disabled = unavailable;
1851          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectRemove, 0);            if(menu) menu.disabled = unavailable;
1852          menu = projectView.popupMenu.menu.FindItem(ProjectView::FileClean, 0);                if(menu) menu.disabled = unavailable;
1853          menu = projectView.popupMenu.menu.FindItem(ProjectView::FileCompile, 0);              if(menu) menu.disabled = unavailable;
1854          menu = projectView.popupMenu.menu.FindItem(ProjectView::FileDebugPrecompile, 0);      if(menu) menu.disabled = unavailable;
1855          menu = projectView.popupMenu.menu.FindItem(ProjectView::FileDebugCompile, 0);         if(menu) menu.disabled = unavailable;
1856          menu = projectView.popupMenu.menu.FindItem(ProjectView::FileDebugGenerateSymbols, 0); if(menu) menu.disabled = unavailable;
1857          projectView.popupMenu.Update(null);
1858       }
1859    }
1860
1861    property bool areDebugMenusUnavailable { get {
1862       return !project ||
1863             project.GetTargetType(project.config) != executable ||
1864             projectView.buildInProgress == buildingMainProject;
1865    } }
1866
1867    property bool isBreakpointTogglingUnavailable { get {
1868       return !project;
1869    } }
1870
1871    property bool isDebuggerExecuting { get {
1872       if(!ide.debugger)
1873          return false;
1874       else
1875          return ide.debugger.state == running;
1876    } }
1877
1878    void AdjustDebugMenus()
1879    {
1880       bool unavailable = areDebugMenusUnavailable;
1881       bool active = debugger.isActive;
1882       bool bpNoToggle = isBreakpointTogglingUnavailable;
1883       bool executing = isDebuggerExecuting;
1884       //bool holding = debugger.state == stopped;
1885
1886       debugStartResumeItem.disabled       = unavailable || executing;
1887       debugStartResumeItem.text           = active ? $"Resume" : $"Start";
1888       debugStartResumeItem.NotifySelect   = active ? MenuDebugResume : MenuDebugStart;
1889       if(toolBar)
1890       {
1891          toolBar.buttonDebugStartResume.disabled      = unavailable || executing;
1892          toolBar.buttonDebugStartResume.toolTip       = active ? $"Resume" : $"Start";
1893       }
1894
1895       debugBreakItem.disabled             = unavailable || !executing;
1896       debugStopItem.disabled              = unavailable || !active;
1897       debugRestartItem.disabled           = unavailable || !active;
1898       if(toolBar)
1899       {
1900          toolBar.buttonDebugPause.disabled            = unavailable || !executing;
1901          toolBar.buttonDebugStop.disabled             = unavailable || !active;
1902          toolBar.buttonDebugRestart.disabled          = unavailable || !active;
1903       }
1904
1905       debugStepIntoItem.disabled          = unavailable || executing;
1906       debugStepOverItem.disabled          = unavailable || executing;
1907       debugStepOutItem.disabled           = unavailable || executing || !active;
1908       debugSkipStepOverItem.disabled      = unavailable || executing;
1909       debugSkipStepOutItem.disabled       = unavailable || executing || !active;
1910       if(toolBar)
1911       {
1912          toolBar.buttonDebugStepInto.disabled         = unavailable || executing;
1913          toolBar.buttonDebugStepOver.disabled         = unavailable || executing;
1914          toolBar.buttonDebugStepOut.disabled          = unavailable || executing || !active;
1915          toolBar.buttonDebugSkipStepOver.disabled     = unavailable || executing;
1916          // toolBar.buttonDebugSkipStepOutItem.disabled  = unavailable || executing;
1917       }
1918       if((Designer)GetActiveDesigner())
1919       {
1920          CodeEditor codeEditor = ((Designer)GetActiveDesigner()).codeEditor;
1921          if(codeEditor)
1922             codeEditor.AdjustDebugMenus(unavailable, bpNoToggle, executing);
1923       }
1924    }
1925
1926    void ChangeFileDialogsDirectory(char * directory, bool saveSettings)
1927    {
1928       char tempString[MAX_LOCATION];
1929       strcpy(tempString, directory);
1930       if(saveSettings && !projectView)
1931       {
1932          ideSettings.ideFileDialogLocation = directory;
1933          settingsContainer.Save();
1934       }
1935
1936       ideFileDialog.currentDirectory = tempString;
1937       codeEditorFileDialog.currentDirectory = tempString;
1938       codeEditorFormFileDialog.currentDirectory = tempString;
1939    }
1940
1941    void ChangeProjectFileDialogDirectory(char * directory)
1942    {
1943       ideSettings.ideProjectFileDialogLocation = directory;
1944       settingsContainer.Save();
1945    }
1946
1947    Window FindWindow(char * filePath)
1948    {
1949       Window document = null;
1950
1951       // TOCHECK: Do we need to change slashes here?
1952       for(document = firstChild; document; document = document.next)
1953       {
1954          char * fileName = document.fileName;
1955          if(document.isDocument && fileName && !fstrcmp(fileName, filePath))
1956          {
1957             document.visible = true;
1958             document.Activate();
1959             return document;
1960          }
1961       }
1962       return null;
1963    }
1964
1965    bool DontTerminateDebugSession(char * title)
1966    {
1967       if(debugger.isActive)
1968       {
1969          if(MessageBox { type = yesNo, master = ide, 
1970                            contents = $"Do you want to terminate the debugging session in progress?", 
1971                            text = title }.Modal() == no)
1972             return true;
1973          /*
1974          MessageBox msg { type = yesNo, master = ide, 
1975                            contents = "Do you want to terminate the debugging session in progress?", 
1976                            text = title };
1977          if(msg.Modal() == no)
1978          {
1979             msg.Destroy(0);
1980             delete msg;
1981             return true;
1982          }
1983          msg.Destroy(0);
1984          delete msg;*/
1985       }
1986       return false;
1987    }
1988
1989    Window OpenFile(char * origFilePath, WindowState state, bool visible, char * type, OpenCreateIfFails createIfFails, OpenMethod openMethod, bool noParsing)
1990    {
1991       char extension[MAX_EXTENSION] = "";
1992       Window document = null;
1993       bool isProject = false;
1994       bool needFileModified = true;
1995       char winFilePath[MAX_LOCATION];
1996       char * filePath = strstr(origFilePath, "http://") == origFilePath ? strcpy(winFilePath, origFilePath) : GetSystemPathBuffer(winFilePath, origFilePath);
1997
1998       if(!type)
1999       {
2000          GetExtension(filePath, extension);
2001          strlwr(extension);
2002       }
2003       else
2004          strcpy(extension, type);
2005
2006       if(strcmp(extension, ProjectExtension))
2007       {
2008          for(document = firstChild; document; document = document.next)
2009          {
2010             char * fileName = document.fileName;
2011             if(document.isDocument && fileName && !fstrcmp(fileName, filePath) && document.created)
2012             {
2013                document.visible = true;
2014                if(visible)
2015                   document.Activate();
2016                return document;
2017             }
2018          }
2019       }
2020
2021       if(createIfFails == whatever)
2022          ;
2023       else if(!strcmp(extension, ProjectExtension) || !strcmp(extension, WorkspaceExtension))
2024       {
2025          needFileModified = false;
2026          if(openMethod == normal)
2027          {
2028             if(DontTerminateDebugSession($"Open Project"))
2029                return null;
2030             isProject = true;
2031             if(MenuWindowCloseAll(null, 0))
2032             {
2033                if(projectView)
2034                {
2035                   projectView.visible = false;
2036                   projectView.Destroy(0);
2037                   // Where did this come from? projectView = null;
2038                }
2039                if(!projectView)
2040                {
2041                   for(;;)
2042                   {
2043                      Project project;
2044                      Workspace workspace = null;
2045                      
2046                      if(FileExists(filePath))
2047                      {
2048                         if(!strcmp(extension, ProjectExtension))
2049                         {
2050                            char workspaceFile[MAX_LOCATION];
2051                            strcpy(workspaceFile, filePath);
2052                            ChangeExtension(workspaceFile, WorkspaceExtension, workspaceFile);
2053                            workspace = LoadWorkspace(workspaceFile, filePath);
2054                         }
2055                         else if(!strcmp(extension, WorkspaceExtension))
2056                            workspace = LoadWorkspace(filePath, null);
2057                         else
2058                            return null;
2059                         //project = LoadProject(filePath, null);
2060                      }
2061                      
2062                      if(workspace)
2063                      {
2064                         char absolutePath[MAX_LOCATION];
2065                         CreateProjectView(workspace, filePath);
2066                         document = projectView;
2067
2068                         workspace.DropInvalidBreakpoints();
2069                         workspace.Save();
2070
2071                         ide.projectView.ShowOutputBuildLog(true);
2072                         {
2073                            CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
2074                            ide.projectView.DisplayCompiler(compiler, false);
2075                            delete compiler;
2076                         }
2077                         UpdateCompilerConfigs(false);
2078                         UpdateMakefiles();
2079                         {
2080                            char newWorkingDir[MAX_LOCATION];
2081                            StripLastDirectory(filePath, newWorkingDir);
2082                            ChangeFileDialogsDirectory(newWorkingDir, false);
2083                         }
2084                         if(document)
2085                            document.fileName = filePath;
2086
2087                         ideMainFrame.SetText("%s - %s", filePath, titleECEREIDE);
2088
2089                         // this crashes on starting ide with epj file, solution please?
2090                         // app.UpdateDisplay();
2091
2092                         workspace.holdTracking = true;
2093                         for(ofi : workspace.openedFiles)
2094                         {
2095                            if(ofi.state != closed)
2096                            {
2097                               Window file = OpenFile(ofi.path, normal, true, null, no, normal, noParsing);
2098                               if(file)
2099                               {
2100                                  char fileName[MAX_LOCATION];
2101                                  ProjectNode node;
2102                                  GetLastDirectory(ofi.path, fileName);
2103                                  node = projectView.project.topNode.Find(fileName, true);
2104                                  if(node)
2105                                     node.EnsureVisible();
2106                               }
2107                            }
2108                         }
2109                         workspace.holdTracking = false;
2110
2111                         workspace.timer.Start();
2112
2113                         findInFilesDialog.mode = FindInFilesMode::project;
2114                         findInFilesDialog.currentDirectory = ide.project.topNode.path;
2115                         
2116                         {
2117                            char location[MAX_LOCATION];
2118                            StripLastDirectory(ide.project.topNode.path, location);
2119                            ChangeProjectFileDialogDirectory(location);
2120                         }
2121                         
2122                         /*
2123                         if(projectView.debugger)
2124                            projectView.debugger.EvaluateWatches();
2125                         */
2126                         
2127                         break;
2128                      }
2129                      else 
2130                      {
2131                         if(MessageBox { type = yesNo, master = this, text = $"Error opening project", contents = $"Open a different project?" }.Modal() == yes)
2132                         {
2133                            ideProjectFileDialog.text = openProjectFileDialogTitle;
2134                            if(ideProjectFileDialog.Modal() == cancel)
2135                               return null;
2136                            filePath = ideProjectFileDialog.filePath;
2137                            GetExtension(filePath, extension);
2138                         }
2139                         else
2140                            return null;
2141                      }
2142                   }
2143                }
2144             }
2145             else
2146                return null;
2147          }
2148          else if(openMethod == add)
2149          {
2150             if(workspace)
2151             {
2152                Project prj = null;
2153                char slashFilePath[MAX_LOCATION];
2154                GetSlashPathBuffer(slashFilePath, filePath);
2155                for(p : workspace.projects)
2156                {
2157                   if(!fstrcmp(p.filePath, slashFilePath))
2158                   {
2159                      prj = p;
2160                      break;
2161                   }
2162                }
2163                if(prj)
2164                {
2165                   MessageBox { type = ok, parent = parent, master = this, text = $"Same Project", 
2166                         contents = $"This project is already present in workspace." }.Modal();
2167                }
2168                else
2169                {
2170                   prj = LoadProject(filePath, null);
2171                   if(prj)
2172                   {
2173                      CompilerConfig compiler = ideSettings.GetCompilerConfig(workspace.compiler);
2174                      prj.StartMonitoring();
2175                      workspace.projects.Add(prj);
2176                      if(projectView)
2177                         projectView.AddNode(prj.topNode, null);
2178                      workspace.modified = true;
2179                      workspace.Save();
2180                      findInFilesDialog.AddProjectItem(prj);
2181                      projectView.ShowOutputBuildLog(true);
2182                      projectView.DisplayCompiler(compiler, false);
2183                      projectView.ProjectUpdateMakefileForAllConfigs(prj);
2184                      delete compiler;
2185
2186                      {
2187                         char location[MAX_LOCATION];
2188                         StripLastDirectory(prj.topNode.path, location);
2189                         ChangeProjectFileDialogDirectory(location);
2190                      }
2191
2192                      // projectView is associated with the main project and not with the one just added but
2193                      return projectView; // just to let the caller know something was opened
2194                   }
2195                }
2196             }
2197             else
2198                return null;
2199          }
2200       }
2201       else if(!strcmp(extension, "bmp") || !strcmp(extension, "pcx") ||
2202             !strcmp(extension, "jpg") || !strcmp(extension, "gif") ||
2203             !strcmp(extension, "jpeg") || !strcmp(extension, "png"))
2204       {
2205          if(FileExists(filePath))
2206             document = PictureEdit { hasMaximize = true, hasMinimize = true, hasClose = true, borderStyle = sizable, 
2207                                        hasVertScroll = true, hasHorzScroll = true, parent = this, state = state, 
2208                                        visible = visible, bitmapFile = filePath, OnClose = PictureEditOnClose/*why?--GenericDocumentOnClose*/;
2209                                     };
2210          if(!document)
2211             MessageBox { type = ok, master = this, text = filePath, contents = $"File doesn't exist." }.Modal();
2212       }
2213 #ifndef NO3D
2214       else if(!strcmp(extension, "3ds"))
2215       {
2216          if(FileExists(filePath))
2217             document = ModelView { hasMaximize = true, hasMinimize = true, hasClose = true, borderStyle = sizable, 
2218                                     hasVertScroll = true, hasHorzScroll = true, parent = this, state = state, 
2219                                     visible = visible, modelFile = filePath, OnClose = ModelViewOnClose/*why?--GenericDocumentOnClose*/
2220                                     };
2221
2222          if(!document)
2223             MessageBox { type = ok, master = this, text = filePath, contents = $"File doesn't exist." }.Modal();
2224       }
2225 #endif
2226       else if(!strcmp(extension, "txt") || !strcmp(extension, "text") ||
2227             !strcmp(extension, "nfo") || !strcmp(extension, "info") ||
2228             !strcmp(extension, "htm") || !strcmp(extension, "html") ||
2229             !strcmp(extension, "css") || !strcmp(extension, "php") ||
2230             !strcmp(extension, "js"))
2231       {
2232          CodeEditor editor { parent = this, state = state, visible = false, noParsing = noParsing };
2233          editor.updatingCode = true;
2234          if(editor.LoadFile(filePath))
2235          {
2236             document = editor;
2237             editor.visible = true;
2238          }
2239          else
2240             delete editor;
2241          needFileModified = false;
2242       }
2243       else
2244       {
2245          CodeEditor editor { parent = this, state = state, visible = false, noParsing = noParsing };
2246          if(editor.LoadFile(filePath))
2247          {
2248             document = editor;
2249             editor.visible = true;
2250          }
2251          else
2252             delete editor;
2253          needFileModified = false;
2254       }
2255
2256       if(document && (document._class == class(PictureEdit) ||
2257             document._class == class(ModelView)))
2258       {
2259          document.Create();
2260          if(document)
2261          {
2262             document.fileName = filePath;
2263             if(workspace && !workspace.holdTracking)
2264                workspace.UpdateOpenedFileInfo(filePath, opened);
2265          }
2266       }
2267       
2268       if(!document && createIfFails != no)
2269       {
2270          if(createIfFails != yes && !needFileModified && 
2271                MessageBox { type = yesNo, master = this, text = filePath, contents = $"File doesn't exist. Create?" }.Modal() == yes)
2272             createIfFails = yes;
2273          if(createIfFails == yes || createIfFails == whatever)
2274          {
2275             document = (Window)NewCodeEditor(this, state, true);
2276             if(document)
2277                document.fileName = filePath;
2278          }
2279       }
2280
2281       if(document)
2282       {
2283          if(projectView && document._class == class(CodeEditor) && workspace)
2284          {
2285             int lineNumber, position;
2286             Point scroll;
2287             CodeEditor editor = (CodeEditor)document;
2288             editor.openedFileInfo = workspace.UpdateOpenedFileInfo(filePath, opened);
2289             editor.openedFileInfo.holdTracking = true;
2290             lineNumber = Max(editor.openedFileInfo.lineNumber - 1, 0);
2291             position = Max(editor.openedFileInfo.position - 1, 0);
2292             editor.editBox.GoToLineNum(lineNumber);
2293             editor.editBox.GoToPosition(editor.editBox.line, lineNumber, position);
2294             scroll.x = Max(editor.openedFileInfo.scroll.x, 0);
2295             scroll.y = Max(editor.openedFileInfo.scroll.y, 0);
2296             editor.editBox.scroll = scroll;
2297             editor.openedFileInfo.holdTracking = false;
2298          }
2299          
2300          if(needFileModified)
2301             document.OnFileModified = OnFileModified;
2302          document.NotifySaved = DocumentSaved;
2303          
2304          if(isProject)
2305             ideSettings.AddRecentProject(document.fileName);
2306          else
2307             ideSettings.AddRecentFile(document.fileName);
2308          ide.UpdateRecentMenus();
2309          ide.AdjustFileMenus();
2310          settingsContainer.Save();
2311          
2312          return document;
2313       }
2314       else
2315          return null;
2316    }
2317
2318    // TOCHECK: I can't use a generic one for both ModelView and PictureEdit both derived from Window
2319    /*bool Window::GenericDocumentOnClose(bool parentClosing)
2320    {
2321       if(!parentClosing && ide.workspace)
2322          ide.workspace.UpdateOpenedFileInfo(fileName, unknown);
2323       return true;
2324    }*/
2325    bool ModelView::ModelViewOnClose(bool parentClosing)
2326    {
2327       if(!parentClosing && ide.workspace)
2328          ide.workspace.UpdateOpenedFileInfo(fileName, unknown);
2329       return true;
2330    }
2331    bool PictureEdit::PictureEditOnClose(bool parentClosing)
2332    {
2333       if(!parentClosing && ide.workspace)
2334          ide.workspace.UpdateOpenedFileInfo(fileName, unknown);
2335       return true;
2336    }
2337
2338    /*
2339    void OnUnloadGraphics(Window window)
2340    {
2341       display.ClearMaterials();
2342       display.ClearTextures();
2343       display.ClearMeshes();
2344    }
2345    */
2346
2347    bool OnActivate(bool active, Window swap, bool * goOnWithActivation, bool direct)
2348    {
2349       caps.color = app.GetKeyState(capsState) ? black : Color { 128,128,128 };
2350       num.color = app.GetKeyState(numState) ? black : Color { 128,128,128 };
2351       return true;
2352    }
2353
2354    bool OnKeyDown(Key key, unichar ch)
2355    {
2356       switch(key)
2357       {
2358          case b:
2359             projectView.Update(null);
2360             break;
2361          case capsLock:
2362             caps.color = app.GetKeyState(capsState) ? black : Color { 128,128,128 };
2363             break;
2364          case numLock:
2365             num.color = app.GetKeyState(numState) ? black : Color { 128,128,128 };
2366             break;
2367       }
2368       return true;
2369    }
2370
2371    void GoToError(const char * line, bool noParsing)
2372    {
2373       if(projectView)
2374          projectView.GoToError(line, noParsing);
2375    }
2376
2377    void CodeLocationParseAndGoTo(const char * text, Project project, const char * dir)
2378    {
2379       char *path = text;
2380       char *colon = strchr(text, ':');
2381       char filePath[MAX_LOCATION];
2382       char completePath[MAX_LOCATION];
2383       int line = 0, col = 0;
2384       Project prj = null;
2385       FileAttribs fileAttribs;
2386
2387       if(colon && (colon[1] == '/' || colon[1] == '\\'))
2388       {
2389          path = (colon - 1 > path) ? colon - 1 : path;
2390          colon = strstr(colon + 1, ":");
2391       }
2392       while(isspace(*path)) path++;
2393       if(*path == '(')
2394       {
2395          char * close = strchr(path, ')');
2396          if(close)
2397          {
2398             char name[256];
2399             strncpy(name, path+1, close - path - 1);
2400             name[close - path - 1] = '\0';
2401             for(p : ide.workspace.projects)
2402             {
2403                if(!strcmp(p.name, name))
2404                {
2405                   path = close + 1;
2406                   prj = p;
2407                   break;
2408                }
2409             }
2410          }
2411       }
2412       if(!prj)
2413          prj = project ? project : (dir ? null : ide.project);
2414       if(colon)
2415       {
2416          strncpy(filePath, path, colon - path);
2417          filePath[colon - path] = '\0';
2418          line = atoi(colon + 1);
2419          colon = strstr(colon + 1, ":");
2420          if(colon)
2421             col = atoi(colon + 1);
2422       }
2423       else if(path - 1 >= text && *(path - 1) == '\"')
2424       {
2425          colon = strchr(path, '\"');
2426          if(colon)
2427          {
2428             strncpy(filePath, path, colon - path);
2429             filePath[colon - path] = '\0';
2430          }
2431       }
2432       else if(path && !colon)
2433       {
2434          strcpy(filePath, path);
2435       }
2436
2437       if(prj)
2438          strcpy(completePath, prj.topNode.path);
2439       else if(dir && dir[0])
2440          strcpy(completePath, dir);
2441       else
2442          completePath[0] = '\0';
2443       PathCat(completePath, filePath);
2444
2445       fileAttribs = FileExists(completePath);
2446       if(fileAttribs.isFile)
2447       {
2448          CodeEditor codeEditor = (CodeEditor)OpenFile(completePath, normal, true, "", no, normal, false);
2449          if(codeEditor && line)
2450          {
2451             EditBox editBox = codeEditor.editBox;
2452             editBox.GoToLineNum(line - 1);
2453             editBox.GoToPosition(editBox.line, line - 1, col ? (col - 1) : 0);
2454          }
2455       }
2456       else if(fileAttribs.isDirectory)
2457          ShellOpen(completePath);
2458    }
2459
2460    void OnRedraw(Surface surface)
2461    {
2462       Bitmap bitmap = back.bitmap;
2463       if(bitmap)
2464          surface.Blit(bitmap, (clientSize.w - bitmap.width) / 2, (clientSize.h - bitmap.height) / 2, 0, 0, bitmap.width, bitmap.height);
2465    }
2466
2467    void SheetSelected(SheetType sheetSelected)
2468    {
2469       if(activeChild == sheet)
2470       {
2471          if(sheetSelected == methods)
2472          {
2473             viewPropertiesItem.accelerator = f4;
2474             viewPropertiesItem.parent = viewMenu;
2475             viewMethodsItem.parent = null;
2476          }
2477          else
2478          {
2479             viewMethodsItem.accelerator = f4;
2480             viewMethodsItem.parent = viewMenu;
2481             viewPropertiesItem.parent = null;
2482          }
2483       }
2484       else
2485       {
2486          viewMethodsItem.parent = viewMenu;
2487          viewPropertiesItem.parent = viewMenu;
2488          if(sheetSelected == methods)
2489          {
2490             viewMethodsItem.accelerator = f4;
2491             viewPropertiesItem.accelerator = 0;
2492          }
2493          else
2494          {
2495             viewMethodsItem.accelerator = 0;
2496             viewPropertiesItem.accelerator = f4;
2497          }
2498       }
2499    }
2500
2501    void OnActivateClient(Window client, Window previous)
2502    {
2503       //if(!client || client != previous)
2504       {
2505          Class dataType;
2506          if(!client || client != previous)
2507          {
2508             if(previous)
2509                dataType = previous._class;
2510             if(previous && !strcmp(dataType.name, "CodeEditor"))
2511             {
2512                ((CodeEditor)previous).UpdateFormCode();
2513             }
2514             else if(previous && !strcmp(dataType.name, "Designer"))
2515             {
2516                ((Designer)previous).codeEditor.UpdateFormCode();
2517             }
2518          }
2519
2520          if(client)
2521             dataType = client._class;
2522          if(client && !strcmp(dataType.name, "CodeEditor"))
2523          {
2524             CodeEditor codeEditor = (CodeEditor)client;
2525             SetPrivateModule(codeEditor.privateModule);
2526             SetCurrentContext(codeEditor.globalContext);
2527             SetTopContext(codeEditor.globalContext);
2528             SetGlobalContext(codeEditor.globalContext);
2529             
2530             SetDefines(&codeEditor.defines);
2531             SetImports(&codeEditor.imports);
2532
2533             SetActiveDesigner(codeEditor.designer);
2534             
2535             sheet.codeEditor = codeEditor;
2536             toolBox.codeEditor = codeEditor;
2537
2538             viewDesignerItem.parent = viewMenu;
2539             if(activeChild != codeEditor)
2540             {
2541                viewCodeItem.parent = viewMenu;
2542                viewDesignerItem.accelerator = 0;
2543                viewCodeItem.accelerator = f8;
2544             }
2545             else
2546             {
2547                viewCodeItem.parent = null;
2548                viewDesignerItem.accelerator = f8;
2549             }
2550          }
2551          else if(client && !strcmp(dataType.name, "Designer"))
2552          {
2553             CodeEditor codeEditor = ((Designer)client).codeEditor;
2554             if(codeEditor)
2555             {
2556                SetPrivateModule(codeEditor.privateModule);
2557                SetCurrentContext(codeEditor.globalContext);
2558                SetTopContext(codeEditor.globalContext);
2559                SetGlobalContext(codeEditor.globalContext);
2560                SetDefines(&codeEditor.defines);
2561                SetImports(&codeEditor.imports);
2562             }
2563             else
2564             {
2565                SetPrivateModule(null);
2566                SetCurrentContext(null);
2567                SetTopContext(null);
2568                SetGlobalContext(null);
2569                SetDefines(null);
2570                SetImports(null);
2571             }
2572
2573             SetActiveDesigner((Designer)client);
2574
2575             sheet.codeEditor = codeEditor;
2576             toolBox.codeEditor = codeEditor;
2577
2578             viewCodeItem.parent = viewMenu;
2579             if(activeChild != client)
2580             {
2581                viewDesignerItem.parent = viewMenu;
2582                viewDesignerItem.accelerator = f8;
2583                viewCodeItem.accelerator = 0;
2584             }
2585             else
2586             {
2587                viewDesignerItem.parent = null;
2588                viewCodeItem.accelerator = f8;
2589             }
2590          }
2591          else
2592          {
2593             if(sheet)
2594                sheet.codeEditor = null;
2595             toolBox.codeEditor = null;
2596             SetActiveDesigner(null);
2597
2598             viewDesignerItem.parent = null;
2599             viewCodeItem.parent = null;
2600          }
2601          if(sheet)
2602             SheetSelected(sheet.sheetSelected);
2603       }
2604
2605       projectCompileItem = null;
2606
2607       if(statusBar)
2608       {
2609          statusBar.Clear();
2610          if(client && client._class == eSystem_FindClass(__thisModule, "CodeEditor")) // !strcmp(client._class.name, "CodeEditor")
2611          {
2612             CodeEditor codeEditor = (CodeEditor)client;
2613             EditBox editBox = codeEditor.editBox;
2614
2615             statusBar.AddField(pos);
2616
2617             caps = { width = 40, text = $"CAPS", color = app.GetKeyState(capsState) ? black : Color { 128, 128, 128 } };
2618             statusBar.AddField(caps);
2619
2620             ovr = { width = 30, text = $"OVR", color = (editBox && editBox.overwrite) ? black : Color { 128, 128, 128 } };
2621             statusBar.AddField(ovr);
2622
2623             num = { width = 30, text = $"NUM", color = app.GetKeyState(numState) ? black : Color { 128, 128, 128 } };
2624             statusBar.AddField(num);
2625
2626             //statusBar.text = "Ready";
2627
2628             if(projectView && projectView.project)
2629             {
2630                bool isCObject = false;
2631                ProjectNode node = projectView.GetNodeFromWindow(client, null, false);
2632                if(!node && (node = projectView.GetNodeFromWindow(client, null, true)))
2633                   isCObject = true;
2634                if(node)
2635                {
2636                   char nodeName[MAX_FILENAME];
2637                   char name[MAX_FILENAME+96];
2638                   if(isCObject)
2639                      ChangeExtension(node.name, "c", nodeName);
2640                   sprintf(name, $"Compile %s", isCObject ? nodeName : node.name);
2641                   projectCompileItem = 
2642                   {
2643                      copyText = true, text = name, c, ctrlF7, disabled = projectView.buildInProgress;
2644
2645                      bool NotifySelect(MenuItem selection, Modifiers mods)
2646                      {
2647                         if(projectView)
2648                         {
2649                            bool result = false;
2650                            bool isCObject = false;
2651                            ProjectNode node = null;
2652                            for(p : ide.workspace.projects)
2653                            {
2654                               node = projectView.GetNodeFromWindow(activeClient, p, false);
2655                               if(node) break;
2656                            }
2657                            if(!node && (node = projectView.GetNodeFromWindow(activeClient, null, true)))
2658                               isCObject = true;
2659                            if(node)
2660                            {
2661                               List<ProjectNode> nodes { };
2662                               nodes.Add(node);
2663                               projectView.Compile(node.project, nodes, mods.ctrl && mods.shift, isCObject ? cObject : normal);
2664                               delete nodes;
2665                               result = true;
2666                            }
2667                            if(!result && node)
2668                               ide.outputView.buildBox.Logf($"File %s is excluded from current build configuration.\n", node.name);
2669                         }
2670                         return true;
2671                      }
2672                   };
2673                   projectMenu.AddDynamic(projectCompileItem, ide, false);
2674                }
2675             }
2676          }
2677          else
2678          {
2679             caps = ovr = num = null;
2680          }
2681       }
2682    }
2683
2684    bool OnClose(bool parentClosing)
2685    {
2686       //return !projectView.buildInProgress;
2687       if(projectView && projectView.buildInProgress)
2688          return false;
2689       if(DontTerminateDebugSession($"Close IDE"))
2690          return false;
2691       if(findInFilesDialog)
2692          findInFilesDialog.SearchStop();
2693       if(workspace)
2694       {
2695          workspace.timer.Stop();
2696          workspace.Save();
2697       }
2698       ideMainFrame.Destroy(0);
2699       return true;
2700    }
2701
2702    bool OnPostCreate()
2703    {
2704       int c;
2705       bool passThrough = false;
2706       bool debugStart = false;
2707       bool debugWorkDir = false;
2708       char * passDebugWorkDir = null;
2709       bool openAsText = false;
2710       DynamicString passArgs { };
2711       for(c = 1; c<app.argc; c++)
2712       {
2713          if(!strcmp(app.argv[c], "-t"))
2714             openAsText = true;
2715          else if(!strcmp(app.argv[c], "-no-parsing"))
2716             ide.noParsing = true;
2717          else if(!strcmp(app.argv[c], "-debug-start"))
2718             debugStart = true;
2719          else if(!strcmp(app.argv[c], "-debug-work-dir"))
2720             debugWorkDir = true;
2721          else if(!passThrough && !strcmp(app.argv[c], "-@"))
2722             passThrough = true;
2723          else if(passThrough)
2724          {
2725             passArgs.concat(" ");
2726             passArgs.concat(app.argv[c]);
2727          }
2728          else if(debugWorkDir)
2729          {
2730             passDebugWorkDir = CopyString(app.argv[c]);
2731             StripQuotes(passDebugWorkDir, passDebugWorkDir);
2732             debugWorkDir = false;
2733          }
2734          else
2735          {
2736             char fullPath[MAX_LOCATION];
2737             char parentPath[MAX_LOCATION];
2738             char ext[MAX_EXTENSION];
2739             bool isProject;
2740             FileAttribs dirAttribs;
2741             GetWorkingDir(fullPath, MAX_LOCATION);
2742             PathCat(fullPath, app.argv[c]);
2743             StripLastDirectory(fullPath, parentPath);
2744             GetExtension(app.argv[c], ext);
2745             isProject = !openAsText && !strcmpi(ext, "epj");
2746
2747             if(isProject && c > (debugStart ? 2 : 1)) continue;
2748
2749             // Create directory for projects (only)
2750             if(((dirAttribs = FileExists(parentPath)) && dirAttribs.isDirectory) || isProject)
2751             {
2752                if(isProject && !FileExists(fullPath))
2753                {
2754                   // The NewProject will handle directory creation
2755                   /*if(!dirAttribs.isDirectory)
2756                   {
2757                      MakeDir(parentPath);
2758                      dirAttribs = FileExists(parentPath);
2759                   }
2760                   if(dirAttribs.isDirectory)*/
2761                   {
2762                      char name[MAX_LOCATION];
2763                      NewProjectDialog newProjectDialog;
2764
2765                      if(projectView)
2766                      {
2767                         projectView.visible = false;
2768                         if(!projectView.Destroy(0))
2769                            return true;
2770                      }
2771
2772                      newProjectDialog = { master = this };
2773
2774                      strcpy(name, app.argv[c]);
2775                      StripExtension(name);
2776                      GetLastDirectory(name, name);
2777                      newProjectDialog.projectName.contents = name;
2778                      newProjectDialog.projectName.NotifyModified(newProjectDialog, newProjectDialog.projectName);
2779                      newProjectDialog.locationEditBox.path = parentPath;
2780                      newProjectDialog.NotifyModifiedLocation(newProjectDialog.locationEditBox);
2781
2782                      newProjectDialog.Modal();
2783                      if(projectView)
2784                      {
2785                         ideSettings.AddRecentProject(projectView.fileName);
2786                         ide.UpdateRecentMenus();
2787                         settingsContainer.Save();
2788                      }
2789                   }
2790                   // Open only one project
2791                   break;
2792                }
2793                else
2794                   ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, openAsText ? "txt" : null, yes, normal, false);
2795             }
2796             else if(strstr(fullPath, "http://") == fullPath)
2797                ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, openAsText ? "txt" : null, yes, normal, false);
2798          }
2799       }
2800       if(passThrough && projectView && projectView.project && workspace)
2801       {
2802          char * fixSpacing = new char[Max(passArgs.size * 1.5, 32)];
2803          {
2804             int c, d = 0;
2805             char j = 0;
2806             char k = ' ';
2807             char l = 0;
2808             bool inQuote = false;
2809             for(c=0; c<passArgs.size; c++)
2810             {
2811                l = passArgs[c];
2812                if(inQuote && k != '\\' && l == ' ')
2813                {
2814                   fixSpacing[d++] = '\"';
2815                   fixSpacing[d++] = ' ';
2816                   inQuote = false;
2817                }
2818                else if(inQuote && l == '\0')
2819                {
2820                   fixSpacing[d++] = '\"';
2821                   fixSpacing[d++] = '\0';
2822                   inQuote = false;
2823                }
2824                else if(!inQuote && j != '\\' && k == ' ' && l != '-' && l != ' ')
2825                {
2826                   fixSpacing[d++] = '\"';
2827                   fixSpacing[d++] = l;
2828                   inQuote = true;
2829                }
2830                else if(k == '\\' && l == ' ')
2831                   fixSpacing[d++] = ' ';
2832                else if(k == '\\' && l == '\\')
2833                   fixSpacing[d++] = '\\';
2834                else if(l != '\\')
2835                   fixSpacing[d++] = l;
2836                j = k;
2837                k = l;
2838             }
2839             fixSpacing[d] = '\0';
2840          }
2841          workspace.commandLineArgs = fixSpacing;
2842       }
2843       if(passDebugWorkDir)
2844       {
2845          workspace.debugDir = passDebugWorkDir;
2846          delete passDebugWorkDir;
2847       }
2848       if(debugStart)
2849          ;//MenuDebugStart(debugStartResumeItem, 0); // <-- how TODO this without getting into the app.Wait lock
2850
2851       UpdateToolBarActiveConfigs(false);
2852       UpdateToolBarActiveCompilers();
2853       delete passArgs;
2854       return true;
2855    }
2856
2857    void OnDestroy()
2858    {
2859       // IS THIS NEEDED? WASN'T HERE BEFORE...  Crashes on getting node's projectView otherwise
2860       if(projectView)
2861       {
2862          projectView.visible = false;
2863          projectView.Destroy(0);
2864          projectView = null;
2865       }
2866 #ifdef GDB_DEBUG_GUI
2867       gdbDialog.Destroy(0);
2868       delete gdbDialog;
2869 #endif
2870    }
2871
2872    void SetPath(bool projectsDirs, CompilerConfig compiler, ProjectConfig config, int bitDepth)
2873    {
2874       int c, len, count;
2875       char * newList;
2876       char * oldPaths[128];
2877       String oldList = new char[maxPathLen];
2878       Array<String> newExePaths { };
2879       //Map<String, bool> exePathExists { };
2880       bool found = false;
2881 #if defined(__unix__) || defined(__APPLE__)
2882       Array<String> newLibPaths { };
2883       Map<String, bool> libPathExists { };
2884 #endif
2885
2886       if(projectsDirs)
2887       {
2888          for(prj : workspace.projects)
2889          {
2890             DirExpression targetDirExp;
2891
2892             // SKIP FIRST PROJECT...
2893             if(prj == workspace.projects.firstIterator.data) continue;
2894
2895             // NOTE: Right now the additional project config dir will be
2896             //       obtained when the debugger is started, so toggling it
2897             //       while building will change which library gets used.
2898             //       To go with the initial state, e.g. when F5 was pressed,
2899             //       we nould need to keep a list of all project's active
2900             //       config upon startup.
2901             targetDirExp = prj.GetTargetDir(compiler, prj.config, bitDepth);
2902
2903             /*if(prj.config.targetType == sharedLibrary && prj.config.debug)
2904                cfg = prj.config;
2905             else
2906             {
2907                for(cfg = prj.configurations.first; cfg; cfg = cfg.next)
2908                   if(cfg.targetType == sharedLibrary && cfg.debug && !strcmpi(cfg.name, "Debug"))
2909                      break;
2910                if(!cfg)
2911                {
2912                   for(cfg = prj.configurations.first; cfg; cfg = cfg.next)
2913                      if(cfg.targetType == sharedLibrary && cfg.debug)
2914                         break;
2915                }
2916             }*/
2917             if(targetDirExp.dir)
2918             {
2919                char buffer[MAX_LOCATION];
2920 #if defined(__WIN32__)
2921                Array<String> paths = newExePaths;
2922 #else
2923                Array<String> paths = newLibPaths;
2924 #endif
2925                GetSystemPathBuffer(buffer, prj.topNode.path);
2926                PathCat(buffer, targetDirExp.dir);
2927                for(p : paths)
2928                {
2929                   if(!fstrcmp(p, buffer))
2930                   {
2931                      found = true;
2932                      break;
2933                   }
2934                }
2935                if(!found)
2936                   paths.Add(CopyString(buffer));
2937             }
2938             delete targetDirExp;
2939          }
2940       }
2941
2942       for(item : compiler.executableDirs)
2943       {
2944          found = false;
2945          for(p : newExePaths)
2946          {
2947             if(!fstrcmp(p, item))
2948             {
2949                found = true;
2950                break;
2951             }
2952          }
2953          if(!found)
2954             newExePaths.Add(CopySystemPath(item));
2955       }
2956
2957       GetEnvironment("PATH", oldList, maxPathLen);
2958 /*#ifdef _DEBUG
2959       printf("Old value of PATH: %s\n", oldList);
2960 #endif*/
2961       count = TokenizeWith(oldList, sizeof(oldPaths) / sizeof(char *), oldPaths, pathListSep, false);
2962       for(c = 0; c < count; c++)
2963       {
2964          found = false;
2965          for(p : newExePaths)
2966          {
2967             if(!fstrcmp(p, oldPaths[c]))
2968             {
2969                found = true;
2970                break;
2971             }
2972          }
2973          if(!found)
2974             newExePaths.Add(CopySystemPath(oldPaths[c]));
2975       }
2976
2977       len = 0;
2978       for(path : newExePaths)
2979          len += strlen(path) + 1;
2980       newList = new char[len + 1];
2981       newList[0] = '\0';
2982       for(path : newExePaths)
2983       {
2984          strcat(newList, path);
2985          strcat(newList, pathListSep);
2986       }
2987       newList[len - 1] = '\0';
2988       SetEnvironment("PATH", newList);
2989 /*#ifdef _DEBUG
2990       printf("New value of PATH: %s\n", newList);
2991 #endif*/
2992       delete newList;
2993
2994       newExePaths.Free();
2995       delete newExePaths;
2996
2997 #if defined(__unix__) || defined(__APPLE__)
2998
2999       for(item : compiler.libraryDirs)
3000       {
3001          if(!libPathExists[item])  // fstrcmp should be used
3002          {
3003             newLibPaths.Add(item);
3004             libPathExists[item] = true;
3005          }
3006       }
3007
3008 #if defined(__APPLE__)
3009       GetEnvironment("DYLD_LIBRARY_PATH", oldList, maxPathLen);
3010 #else
3011       GetEnvironment("LD_LIBRARY_PATH", oldList, maxPathLen);
3012 #endif
3013 /*#ifdef _DEBUG
3014       printf("Old value of [DY]LD_LIBRARY_PATH: %s\n", oldList);
3015 #endif*/
3016       count = TokenizeWith(oldList, sizeof(oldPaths) / sizeof(char *), oldPaths, pathListSep, false);
3017       for(c = 0; c < count; c++)
3018       {
3019          if(!libPathExists[oldPaths[c]])  // fstrcmp should be used
3020          {
3021             newLibPaths.Add(oldPaths[c]);
3022             libPathExists[oldPaths[c]] = true;
3023          }
3024       }
3025
3026       len = 0;
3027       for(path : newLibPaths)
3028          len += strlen(path) + 1;
3029       newList = new char[len + 1];
3030       newList[0] = '\0';
3031       for(path : newLibPaths)
3032       {
3033          strcat(newList, path);
3034          strcat(newList, pathListSep);
3035       }
3036       newList[len - 1] = '\0';
3037 #if defined(__APPLE__)
3038       SetEnvironment("DYLD_LIBRARY_PATH", newList);
3039 #else
3040       SetEnvironment("LD_LIBRARY_PATH", newList);
3041 #endif
3042 /*#ifdef _DEBUG
3043       printf("New value of [DY]LD_LIBRARY_PATH: %s\n", newList);
3044 #endif*/
3045       delete newList;
3046
3047       delete newLibPaths;
3048       delete libPathExists;
3049 #endif
3050
3051       if(compiler.distccEnabled && compiler.distccHosts)
3052          SetEnvironment("DISTCC_HOSTS", compiler.distccHosts);
3053
3054       delete oldList;
3055    }
3056
3057    void DestroyTemporaryProjectDir()
3058    {
3059       if(tmpPrjDir && tmpPrjDir[0])
3060       {
3061          if(FileExists(tmpPrjDir).isDirectory)
3062             DestroyDir(tmpPrjDir);
3063          property::tmpPrjDir = null;
3064       }
3065    }
3066
3067    IDEWorkSpace()
3068    {
3069       // Graphics Driver Menu
3070       int c;
3071
3072       /*
3073       app.currentSkin.selectionColor = selectionColor;
3074       app.currentSkin.selectionText = selectionText;
3075       */
3076
3077 /*
3078       driverItems = new MenuItem[app.numDrivers];
3079       for(c = 0; c < app.numDrivers; c++)
3080       {
3081          driverItems[c] = MenuItem { driversMenu, app.drivers[c], NotifySelect = NotifySelectDisplayDriver };
3082          driverItems[c].id = c;
3083          driverItems[c].isRadio = true;         
3084       }
3085 */
3086       driverItems = new MenuItem[2];
3087 #if defined(__unix__)
3088          driverItems[0] = MenuItem { driversMenu, "X", NotifySelect = NotifySelectDisplayDriver };
3089          driverItems[0].id = 0;
3090          driverItems[0].isRadio = true;         
3091 #else
3092          driverItems[0] = MenuItem { driversMenu, "GDI", NotifySelect = NotifySelectDisplayDriver };
3093          driverItems[0].id = 0;
3094          driverItems[0].isRadio = true;         
3095 #endif
3096          driverItems[1] = MenuItem { driversMenu, "OpenGL", NotifySelect = NotifySelectDisplayDriver };
3097          driverItems[1].id = 1;
3098          driverItems[1].isRadio = true;         
3099
3100 /*      skinItems = new MenuItem[app.numSkins];
3101       for(c = 0; c < app.numSkins; c++)
3102       {
3103          skinItems[c] = MenuItem {skinsMenu, app.skins[c], NotifySelect = NotifySelectDisplaySkin };
3104          skinItems[c].id = c;
3105          skinItems[c].isRadio = true;         
3106       }
3107 */
3108       ideFileDialog.master = this;
3109       ideProjectFileDialog.master = this;
3110
3111       //SetDriverAndSkin();
3112       return true;
3113    }
3114
3115    void UpdateRecentMenus()
3116    {
3117       int c;
3118       Menu fileMenu = menu.FindMenu($"File");
3119       Menu recentFiles = fileMenu.FindMenu($"Recent Files");
3120       Menu recentProjects = fileMenu.FindMenu($"Recent Projects");
3121       char * itemPath = new char[MAX_LOCATION];
3122       char * itemName = new char[MAX_LOCATION+4];
3123       MenuItem item;
3124
3125       recentFiles.Clear();
3126       c = 0;
3127
3128       for(recent : ideSettings.recentFiles)
3129       {
3130          strncpy(itemPath, recent, MAX_LOCATION); itemPath[MAX_LOCATION-1] = '\0';
3131          MakeSystemPath(itemPath);
3132          snprintf(itemName, MAX_LOCATION+4, "%d %s", 1 + c, itemPath); itemName[MAX_LOCATION+4-1] = '\0';
3133          recentFiles.AddDynamic(MenuItem { copyText = true, text = itemName, (Key)k1 + c, id = c, NotifySelect = ide.FileRecentFile }, ide, true);
3134          c++;
3135       }
3136
3137       recentProjects.Clear();
3138       c = 0;
3139       for(recent : ideSettings.recentProjects)
3140       {
3141          strncpy(itemPath, recent, MAX_LOCATION); itemPath[MAX_LOCATION-1] = '\0';
3142          MakeSystemPath(itemPath);
3143          snprintf(itemName, MAX_LOCATION+4, "%d %s", 1 + c, itemPath); itemName[MAX_LOCATION+4-1] = '\0';
3144          recentProjects.AddDynamic(MenuItem { copyText = true, text = itemName, (Key)k1 + c, id = c, NotifySelect = ide.FileRecentProject }, ide, true);
3145          c++;
3146       }
3147
3148       delete itemPath;
3149       delete itemName;
3150    }
3151
3152    ~IDEWorkSpace()
3153    {
3154       delete driverItems;
3155       delete skinItems;
3156       delete ideSettings;
3157    }
3158 }
3159
3160 void DestroyDir(char * path)
3161 {
3162    RecursiveDeleteFolderFSI fsi { };
3163    fsi.Iterate(path);
3164    delete fsi;
3165 }
3166
3167 class RecursiveDeleteFolderFSI : NormalFileSystemIterator
3168 {
3169    bool preserveRootFolder;
3170
3171    void OutFolder(char * folderPath, bool isRoot)
3172    {
3173       if(!(preserveRootFolder && isRoot))
3174          RemoveDir(folderPath);
3175    }
3176
3177    bool OnFile(char * filePath)
3178    {
3179       DeleteFile(filePath);
3180       return true;
3181    }
3182 }
3183
3184 class IDEApp : GuiApplication
3185 {
3186    //driver = "Win32Console";
3187    // driver = "OpenGL";
3188    // skin = "Aqua";
3189    //skin = "TVision";
3190
3191    TempFile includeFile { };
3192
3193    bool Init()
3194    {
3195       SetLoggingMode(stdOut, null);
3196       //SetLoggingMode(debug, null);
3197
3198       settingsContainer.Load();
3199 #if defined(__unix__) || defined(__APPLE__)
3200       app.driver = (ideSettings.displayDriver && !strcmp(ideSettings.displayDriver, "OpenGL")) ? ideSettings.displayDriver : "X";
3201 #else
3202       app.driver = (ideSettings.displayDriver && !strcmp(ideSettings.displayDriver, "OpenGL")) ? ideSettings.displayDriver : "GDI";
3203 #endif
3204       ide.driverItems[ideSettings.displayDriver && !strcmp(ideSettings.displayDriver,"OpenGL")].checked = true;
3205
3206       SetInIDE(true);
3207
3208       desktop.text = titleECEREIDE;
3209       /*
3210       int c;
3211       for(c = 1; c<app.argc; c++)
3212       {
3213          char fullPath[MAX_LOCATION];
3214          GetWorkingDir(fullPath, MAX_LOCATION);
3215          PathCat(fullPath, app.argv[c]);
3216          ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, null, yes, normal, false);
3217       }
3218       */
3219
3220       if(!LoadIncludeFile())
3221          PrintLn("error: unable to load :crossplatform.mk file inside ide binary.");
3222
3223       return true;
3224    }
3225
3226    bool LoadIncludeFile()
3227    {
3228       bool result = false;
3229       File include = FileOpen(":crossplatform.mk", read);
3230       if(include)
3231       {
3232          File f = includeFile;
3233          if(f)
3234          {
3235             for(; !include.Eof(); )
3236             {
3237                char buffer[4096];
3238                int count = include.Read(buffer, 1, 4096);
3239                f.Write(buffer, 1, count);
3240             }
3241             result = true;
3242          }
3243          delete include;
3244       }
3245       return result;
3246    }
3247 }
3248
3249 IDEMainFrame ideMainFrame { };
3250
3251 define app = ((IDEApp)__thisModule);
3252 #ifdef _DEBUG
3253 define titleECEREIDE = $"ECERE IDE (Debug)";
3254 #else
3255 define titleECEREIDE = $"ECERE IDE";
3256 #endif