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