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