39ecb664adc7413f08f201714697c95942838e7f
[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       int len = strlen(text);
2454       Project prj = null;
2455       FileAttribs fileAttribs;
2456
2457       // support for valgrind output
2458       if((s = strstr(text, "==")) && (s = strstr(s+2, "==")) && (s = strstr(s+2, ":")) && (s = strstr(s+1, ":")))
2459       {
2460          colon = s;
2461          for(; s>text; s--)
2462          {
2463             if(*s == '(')
2464             {
2465                path = s+1;
2466                break;
2467             }
2468          }
2469          /*for(s=colon; *s; s++)
2470          {
2471             if(*s == ')')
2472             {
2473                *s = '\0';;
2474                break;
2475             }
2476          }*/
2477          //*colon = '\0';
2478          //line = atoi(colon+1);
2479       }
2480       // support for "Found n match(es) in "file/path";
2481       else if(path[len-1] == '\"' && strstr(path, $"Found %d match%s in \"%s\"%s\n\n"."Found") && strstr(path, $"match") && strstr(path, $"in") && (s = strstr(path, "\"")) && s != path+len-1)
2482       {
2483          path = s+1;
2484       }
2485       else
2486       {
2487          if(colon && (colon[1] == '/' || colon[1] == '\\'))
2488          {
2489             path = (colon - 1 > path) ? colon - 1 : path;
2490             colon = strstr(colon + 1, ":");
2491          }
2492          if(*path == '*' && (s = strchr(path+1, '*')))
2493             path = s+1;
2494          while(isspace(*path)) path++;
2495       }
2496       if(*path == '(')
2497       {
2498          char * close = strchr(path, ')');
2499          if(close)
2500          {
2501             char name[256];
2502             strncpy(name, path+1, close - path - 1);
2503             name[close - path - 1] = '\0';
2504             for(p : ide.workspace.projects)
2505             {
2506                if(!strcmp(p.name, name))
2507                {
2508                   path = close + 1;
2509                   prj = p;
2510                   break;
2511                }
2512             }
2513          }
2514       }
2515       if(!prj)
2516          prj = project ? project : (dir ? null : ide.project);
2517       if(colon)
2518       {
2519          strncpy(filePath, path, colon - path);
2520          filePath[colon - path] = '\0';
2521          line = atoi(colon + 1);
2522          colon = strstr(colon + 1, ":");
2523          if(colon)
2524             col = atoi(colon + 1);
2525       }
2526       else if(path - 1 >= text && *(path - 1) == '\"')
2527       {
2528          colon = strchr(path, '\"');
2529          if(colon)
2530          {
2531             strncpy(filePath, path, colon - path);
2532             filePath[colon - path] = '\0';
2533          }
2534       }
2535       else if(path && !colon)
2536       {
2537          strcpy(filePath, path);
2538       }
2539
2540       if(filePath[0])
2541       {
2542          if(prj)
2543             strcpy(completePath, prj.topNode.path);
2544          else if(dir && dir[0])
2545             strcpy(completePath, dir);
2546          else
2547             completePath[0] = '\0';
2548          PathCat(completePath, filePath);
2549
2550          if((fileAttribs = FileExists(completePath)))
2551             CodeLocationGoTo(completePath, fileAttribs, line, col);
2552          else if(ide.workspace)
2553          {
2554             bool done = false;
2555             for(p : ide.workspace.projects)
2556             {
2557                strcpy(completePath, p.topNode.path);
2558                PathCat(completePath, filePath);
2559                if((fileAttribs = FileExists(completePath)).isFile)
2560                {
2561                   CodeLocationGoTo(completePath, fileAttribs, line, col);
2562                   done = true;
2563                   break;
2564                }
2565             }
2566             if(!done)
2567             {
2568                for(p : ide.workspace.projects)
2569                {
2570                   ProjectNode node = p.topNode.Find(filePath, false);
2571                   if(node)
2572                   {
2573                      node.GetFullFilePath(completePath);
2574                      if((fileAttribs = FileExists(completePath)).isFile)
2575                      {
2576                         CodeLocationGoTo(completePath, fileAttribs, line, col);
2577                         break;
2578                      }
2579                   }
2580                }
2581             }
2582          }
2583       }
2584    }
2585
2586    void CodeLocationGoTo(const char * path, const FileAttribs fileAttribs, int line, int col)
2587    {
2588       if(fileAttribs.isFile)
2589       {
2590          char ext[MAX_EXTENSION];
2591          GetExtension(path, ext);
2592          if(!strcmp(ext, "mp3") || !strcmp(ext, "flac") || !strcmp(ext, "ogg") || !strcmp(ext, "avi") || !strcmp(ext, "mkv"))
2593             ShellOpen(path);
2594          else if(!strcmp(ext, "a") || !strcmp(ext, "o") || !strcmp(ext, "lib") || !strcmp(ext, "dll") || !strcmp(ext, "exe"))
2595          {
2596             char dirPath[MAX_LOCATION];
2597             StripLastDirectory(path, dirPath);
2598             ShellOpen(dirPath);
2599          }
2600          else
2601          {
2602             CodeEditor codeEditor = (CodeEditor)OpenFile(path, normal, true, ext, no, normal, false);
2603             if(codeEditor && line)
2604             {
2605                EditBox editBox = codeEditor.editBox;
2606                editBox.GoToLineNum(line - 1);
2607                editBox.GoToPosition(editBox.line, line - 1, col ? (col - 1) : 0);
2608             }
2609          }
2610       }
2611       else if(fileAttribs.isDirectory)
2612          ShellOpen(path);
2613    }
2614
2615    void OnRedraw(Surface surface)
2616    {
2617       Bitmap bitmap = back.bitmap;
2618       if(bitmap)
2619          surface.Blit(bitmap, (clientSize.w - bitmap.width) / 2, (clientSize.h - bitmap.height) / 2, 0, 0, bitmap.width, bitmap.height);
2620    }
2621
2622    void SheetSelected(SheetType sheetSelected)
2623    {
2624       if(activeChild == sheet)
2625       {
2626          if(sheetSelected == methods)
2627          {
2628             viewPropertiesItem.accelerator = f4;
2629             viewPropertiesItem.parent = viewMenu;
2630             viewMethodsItem.parent = null;
2631          }
2632          else
2633          {
2634             viewMethodsItem.accelerator = f4;
2635             viewMethodsItem.parent = viewMenu;
2636             viewPropertiesItem.parent = null;
2637          }
2638       }
2639       else
2640       {
2641          viewMethodsItem.parent = viewMenu;
2642          viewPropertiesItem.parent = viewMenu;
2643          if(sheetSelected == methods)
2644          {
2645             viewMethodsItem.accelerator = f4;
2646             viewPropertiesItem.accelerator = 0;
2647          }
2648          else
2649          {
2650             viewMethodsItem.accelerator = 0;
2651             viewPropertiesItem.accelerator = f4;
2652          }
2653       }
2654    }
2655
2656    void OnActivateClient(Window client, Window previous)
2657    {
2658       //if(!client || client != previous)
2659       {
2660          Class dataType;
2661          if(!client || client != previous)
2662          {
2663             if(previous)
2664                dataType = previous._class;
2665             if(previous && !strcmp(dataType.name, "CodeEditor"))
2666             {
2667                ((CodeEditor)previous).UpdateFormCode();
2668             }
2669             else if(previous && !strcmp(dataType.name, "Designer"))
2670             {
2671                ((Designer)previous).codeEditor.UpdateFormCode();
2672             }
2673          }
2674
2675          if(client)
2676             dataType = client._class;
2677          if(client && !strcmp(dataType.name, "CodeEditor"))
2678          {
2679             CodeEditor codeEditor = (CodeEditor)client;
2680             SetPrivateModule(codeEditor.privateModule);
2681             SetCurrentContext(codeEditor.globalContext);
2682             SetTopContext(codeEditor.globalContext);
2683             SetGlobalContext(codeEditor.globalContext);
2684             
2685             SetDefines(&codeEditor.defines);
2686             SetImports(&codeEditor.imports);
2687
2688             SetActiveDesigner(codeEditor.designer);
2689             
2690             sheet.codeEditor = codeEditor;
2691             toolBox.codeEditor = codeEditor;
2692
2693             viewDesignerItem.parent = viewMenu;
2694             if(activeChild != codeEditor)
2695             {
2696                viewCodeItem.parent = viewMenu;
2697                viewDesignerItem.accelerator = 0;
2698                viewCodeItem.accelerator = f8;
2699             }
2700             else
2701             {
2702                viewCodeItem.parent = null;
2703                viewDesignerItem.accelerator = f8;
2704             }
2705          }
2706          else if(client && !strcmp(dataType.name, "Designer"))
2707          {
2708             CodeEditor codeEditor = ((Designer)client).codeEditor;
2709             if(codeEditor)
2710             {
2711                SetPrivateModule(codeEditor.privateModule);
2712                SetCurrentContext(codeEditor.globalContext);
2713                SetTopContext(codeEditor.globalContext);
2714                SetGlobalContext(codeEditor.globalContext);
2715                SetDefines(&codeEditor.defines);
2716                SetImports(&codeEditor.imports);
2717             }
2718             else
2719             {
2720                SetPrivateModule(null);
2721                SetCurrentContext(null);
2722                SetTopContext(null);
2723                SetGlobalContext(null);
2724                SetDefines(null);
2725                SetImports(null);
2726             }
2727
2728             SetActiveDesigner((Designer)client);
2729
2730             sheet.codeEditor = codeEditor;
2731             toolBox.codeEditor = codeEditor;
2732
2733             viewCodeItem.parent = viewMenu;
2734             if(activeChild != client)
2735             {
2736                viewDesignerItem.parent = viewMenu;
2737                viewDesignerItem.accelerator = f8;
2738                viewCodeItem.accelerator = 0;
2739             }
2740             else
2741             {
2742                viewDesignerItem.parent = null;
2743                viewCodeItem.accelerator = f8;
2744             }
2745          }
2746          else
2747          {
2748             if(sheet)
2749                sheet.codeEditor = null;
2750             toolBox.codeEditor = null;
2751             SetActiveDesigner(null);
2752
2753             viewDesignerItem.parent = null;
2754             viewCodeItem.parent = null;
2755          }
2756          if(sheet)
2757             SheetSelected(sheet.sheetSelected);
2758       }
2759
2760       projectCompileItem = null;
2761
2762       if(statusBar)
2763       {
2764          statusBar.Clear();
2765          if(client && client._class == eSystem_FindClass(__thisModule, "CodeEditor")) // !strcmp(client._class.name, "CodeEditor")
2766          {
2767             CodeEditor codeEditor = (CodeEditor)client;
2768             EditBox editBox = codeEditor.editBox;
2769
2770             statusBar.AddField(pos);
2771
2772             caps = { width = 40, text = $"CAPS", color = app.GetKeyState(capsState) ? black : Color { 128, 128, 128 } };
2773             statusBar.AddField(caps);
2774
2775             ovr = { width = 30, text = $"OVR", color = (editBox && editBox.overwrite) ? black : Color { 128, 128, 128 } };
2776             statusBar.AddField(ovr);
2777
2778             num = { width = 30, text = $"NUM", color = app.GetKeyState(numState) ? black : Color { 128, 128, 128 } };
2779             statusBar.AddField(num);
2780
2781             //statusBar.text = "Ready";
2782
2783             if(projectView && projectView.project)
2784             {
2785                bool isCObject = false;
2786                ProjectNode node = projectView.GetNodeFromWindow(client, null, false);
2787                if(!node && (node = projectView.GetNodeFromWindow(client, null, true)))
2788                   isCObject = true;
2789                if(node)
2790                {
2791                   char nodeName[MAX_FILENAME];
2792                   char name[MAX_FILENAME+96];
2793                   if(isCObject)
2794                      ChangeExtension(node.name, "c", nodeName);
2795                   sprintf(name, $"Compile %s", isCObject ? nodeName : node.name);
2796                   projectCompileItem = 
2797                   {
2798                      copyText = true, text = name, c, ctrlF7, disabled = projectView.buildInProgress;
2799
2800                      bool NotifySelect(MenuItem selection, Modifiers mods)
2801                      {
2802                         if(projectView)
2803                         {
2804                            bool result = false;
2805                            bool isCObject = false;
2806                            ProjectNode node = null;
2807                            for(p : ide.workspace.projects)
2808                            {
2809                               node = projectView.GetNodeFromWindow(activeClient, p, false);
2810                               if(node) break;
2811                            }
2812                            if(!node && (node = projectView.GetNodeFromWindow(activeClient, null, true)))
2813                               isCObject = true;
2814                            if(node)
2815                            {
2816                               List<ProjectNode> nodes { };
2817                               nodes.Add(node);
2818                               projectView.Compile(node.project, nodes, mods.ctrl && mods.shift, isCObject ? cObject : normal);
2819                               delete nodes;
2820                               result = true;
2821                            }
2822                            if(!result && node)
2823                               ide.outputView.buildBox.Logf($"File %s is excluded from current build configuration.\n", node.name);
2824                         }
2825                         return true;
2826                      }
2827                   };
2828                   projectMenu.AddDynamic(projectCompileItem, ide, false);
2829                }
2830             }
2831          }
2832          else
2833          {
2834             caps = ovr = num = null;
2835          }
2836       }
2837    }
2838
2839    bool OnClose(bool parentClosing)
2840    {
2841       //return !projectView.buildInProgress;
2842       if(projectView && projectView.buildInProgress)
2843          return false;
2844       if(DontTerminateDebugSession($"Close IDE"))
2845          return false;
2846       if(findInFilesDialog)
2847          findInFilesDialog.SearchStop();
2848       if(workspace)
2849       {
2850          workspace.timer.Stop();
2851          workspace.Save();
2852       }
2853       ideMainFrame.Destroy(0);
2854       return true;
2855    }
2856
2857    bool OnPostCreate()
2858    {
2859       int c;
2860       bool passThrough = false;
2861       bool debugStart = false;
2862       bool debugWorkDir = false;
2863       char * passDebugWorkDir = null;
2864       bool openAsText = false;
2865       DynamicString passArgs { };
2866       int ptArg = 0;
2867
2868       for(c = 1; c<app.argc; c++)
2869       {
2870          if(passThrough)
2871          {
2872             char * arg = app.argv[c];
2873             char * buf = new char[strlen(arg)*2+1];
2874             if(ptArg++ > 0)
2875                passArgs.concat(" ");
2876             PassArg(buf, arg);
2877             passArgs.concat(buf);
2878             delete buf;
2879          }
2880          else if(debugWorkDir)
2881          {
2882             passDebugWorkDir = CopyString(app.argv[c]);
2883             StripQuotes(passDebugWorkDir, passDebugWorkDir);
2884             debugWorkDir = false;
2885          }
2886          else if(!strcmp(app.argv[c], "-t"))
2887             openAsText = true;
2888          else if(!strcmp(app.argv[c], "-no-parsing"))
2889             ide.noParsing = true;
2890          else if(!strcmp(app.argv[c], "-debug-start"))
2891             debugStart = true;
2892          else if(!strcmp(app.argv[c], "-debug-work-dir"))
2893             debugWorkDir = true;
2894          else if(!strcmp(app.argv[c], "-@"))
2895             passThrough = true;
2896          else
2897          {
2898             char fullPath[MAX_LOCATION];
2899             char parentPath[MAX_LOCATION];
2900             char ext[MAX_EXTENSION];
2901             bool isProject;
2902             FileAttribs dirAttribs;
2903             GetWorkingDir(fullPath, MAX_LOCATION);
2904             PathCat(fullPath, app.argv[c]);
2905             StripLastDirectory(fullPath, parentPath);
2906             GetExtension(app.argv[c], ext);
2907             isProject = !openAsText && !strcmpi(ext, "epj");
2908
2909             if(isProject && c > (debugStart ? 2 : 1)) continue;
2910
2911             // Create directory for projects (only)
2912             if(((dirAttribs = FileExists(parentPath)) && dirAttribs.isDirectory) || isProject)
2913             {
2914                if(isProject && !FileExists(fullPath))
2915                {
2916                   char name[MAX_LOCATION];
2917                   NewProjectDialog newProjectDialog;
2918
2919                   if(projectView)
2920                   {
2921                      projectView.visible = false;
2922                      if(!projectView.Destroy(0))
2923                         return true;
2924                   }
2925
2926                   newProjectDialog = { master = this };
2927
2928                   strcpy(name, app.argv[c]);
2929                   StripExtension(name);
2930                   GetLastDirectory(name, name);
2931                   newProjectDialog.projectName.contents = name;
2932                   newProjectDialog.projectName.NotifyModified(newProjectDialog, newProjectDialog.projectName);
2933                   newProjectDialog.locationEditBox.path = parentPath;
2934                   newProjectDialog.NotifyModifiedLocation(newProjectDialog.locationEditBox);
2935
2936                   incref newProjectDialog;
2937                   newProjectDialog.Modal();
2938                   if(projectView)
2939                   {
2940                      ideSettings.AddRecentProject(projectView.fileName);
2941                      ide.UpdateRecentMenus();
2942                      settingsContainer.Save();
2943                   }
2944                   delete newProjectDialog;
2945                   // Open only one project
2946                   break;
2947                }
2948                else
2949                   ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, openAsText ? "txt" : null, yes, normal, false);
2950             }
2951             else if(strstr(fullPath, "http://") == fullPath)
2952                ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, openAsText ? "txt" : null, yes, normal, false);
2953          }
2954       }
2955       if(passThrough && projectView && projectView.project && workspace)
2956          workspace.commandLineArgs = passArgs;
2957       if(passDebugWorkDir && projectView && projectView.project && workspace)
2958       {
2959          workspace.debugDir = passDebugWorkDir;
2960          delete passDebugWorkDir;
2961       }
2962       if(debugStart)
2963          ;//MenuDebugStart(debugStartResumeItem, 0); // <-- how TODO this without getting into the app.Wait lock
2964
2965       UpdateToolBarActiveConfigs(false);
2966       UpdateToolBarActiveCompilers();
2967       delete passArgs;
2968       return true;
2969    }
2970
2971    void OnDestroy()
2972    {
2973       // IS THIS NEEDED? WASN'T HERE BEFORE...  Crashes on getting node's projectView otherwise
2974       if(projectView)
2975       {
2976          projectView.visible = false;
2977          projectView.Destroy(0);
2978          projectView = null;
2979       }
2980 #ifdef GDB_DEBUG_GUI
2981       gdbDialog.Destroy(0);
2982       delete gdbDialog;
2983 #endif
2984    }
2985
2986    void SetPath(bool projectsDirs, CompilerConfig compiler, ProjectConfig config, int bitDepth)
2987    {
2988       int c, len, count;
2989       char * newList;
2990       char * oldPaths[128];
2991       String oldList = new char[maxPathLen];
2992       Array<String> newExePaths { };
2993       //Map<String, bool> exePathExists { };
2994       bool found = false;
2995 #if defined(__unix__) || defined(__APPLE__)
2996       Array<String> newLibPaths { };
2997       Map<String, bool> libPathExists { };
2998 #endif
2999
3000       if(projectsDirs)
3001       {
3002          for(prj : workspace.projects)
3003          {
3004             DirExpression targetDirExp;
3005
3006             // SKIP FIRST PROJECT...
3007             if(prj == workspace.projects.firstIterator.data) continue;
3008
3009             // NOTE: Right now the additional project config dir will be
3010             //       obtained when the debugger is started, so toggling it
3011             //       while building will change which library gets used.
3012             //       To go with the initial state, e.g. when F5 was pressed,
3013             //       we nould need to keep a list of all project's active
3014             //       config upon startup.
3015             targetDirExp = prj.GetTargetDir(compiler, prj.config, bitDepth);
3016
3017             /*if(prj.config.targetType == sharedLibrary && prj.config.debug)
3018                cfg = prj.config;
3019             else
3020             {
3021                for(cfg = prj.configurations.first; cfg; cfg = cfg.next)
3022                   if(cfg.targetType == sharedLibrary && cfg.debug && !strcmpi(cfg.name, "Debug"))
3023                      break;
3024                if(!cfg)
3025                {
3026                   for(cfg = prj.configurations.first; cfg; cfg = cfg.next)
3027                      if(cfg.targetType == sharedLibrary && cfg.debug)
3028                         break;
3029                }
3030             }*/
3031             if(targetDirExp.dir)
3032             {
3033                char buffer[MAX_LOCATION];
3034 #if defined(__WIN32__)
3035                Array<String> paths = newExePaths;
3036 #else
3037                Array<String> paths = newLibPaths;
3038 #endif
3039                GetSystemPathBuffer(buffer, prj.topNode.path);
3040                PathCat(buffer, targetDirExp.dir);
3041                for(p : paths)
3042                {
3043                   if(!fstrcmp(p, buffer))
3044                   {
3045                      found = true;
3046                      break;
3047                   }
3048                }
3049                if(!found)
3050                   paths.Add(CopyString(buffer));
3051             }
3052             delete targetDirExp;
3053          }
3054       }
3055
3056       for(item : compiler.executableDirs)
3057       {
3058          found = false;
3059          for(p : newExePaths)
3060          {
3061             if(!fstrcmp(p, item))
3062             {
3063                found = true;
3064                break;
3065             }
3066          }
3067          if(!found)
3068             newExePaths.Add(CopySystemPath(item));
3069       }
3070
3071       GetEnvironment("PATH", oldList, maxPathLen);
3072 /*#ifdef _DEBUG
3073       printf("Old value of PATH: %s\n", oldList);
3074 #endif*/
3075       count = TokenizeWith(oldList, sizeof(oldPaths) / sizeof(char *), oldPaths, pathListSep, false);
3076       for(c = 0; c < count; c++)
3077       {
3078          found = false;
3079          for(p : newExePaths)
3080          {
3081             if(!fstrcmp(p, oldPaths[c]))
3082             {
3083                found = true;
3084                break;
3085             }
3086          }
3087          if(!found)
3088             newExePaths.Add(CopySystemPath(oldPaths[c]));
3089       }
3090
3091       len = 0;
3092       for(path : newExePaths)
3093          len += strlen(path) + 1;
3094       newList = new char[len + 1];
3095       newList[0] = '\0';
3096       for(path : newExePaths)
3097       {
3098          strcat(newList, path);
3099          strcat(newList, pathListSep);
3100       }
3101       newList[len - 1] = '\0';
3102       SetEnvironment("PATH", newList);
3103 /*#ifdef _DEBUG
3104       printf("New value of PATH: %s\n", newList);
3105 #endif*/
3106       delete newList;
3107
3108       newExePaths.Free();
3109       delete newExePaths;
3110
3111 #if defined(__unix__) || defined(__APPLE__)
3112
3113       for(item : compiler.libraryDirs)
3114       {
3115          if(!libPathExists[item])  // fstrcmp should be used
3116          {
3117             String s = CopyString(item);
3118             newLibPaths.Add(s);
3119             libPathExists[s] = true;
3120          }
3121       }
3122
3123 #if defined(__APPLE__)
3124       GetEnvironment("DYLD_LIBRARY_PATH", oldList, maxPathLen);
3125 #else
3126       GetEnvironment("LD_LIBRARY_PATH", oldList, maxPathLen);
3127 #endif
3128 /*#ifdef _DEBUG
3129       printf("Old value of [DY]LD_LIBRARY_PATH: %s\n", oldList);
3130 #endif*/
3131       count = TokenizeWith(oldList, sizeof(oldPaths) / sizeof(char *), oldPaths, pathListSep, false);
3132       for(c = 0; c < count; c++)
3133       {
3134          if(!libPathExists[oldPaths[c]])  // fstrcmp should be used
3135          {
3136             String s = CopyString(oldPaths[c]);
3137             newLibPaths.Add(s);
3138             libPathExists[s] = true;
3139          }
3140       }
3141
3142       len = 0;
3143       for(path : newLibPaths)
3144          len += strlen(path) + 1;
3145       newList = new char[len + 1];
3146       newList[0] = '\0';
3147       for(path : newLibPaths)
3148       {
3149          strcat(newList, path);
3150          strcat(newList, pathListSep);
3151       }
3152       newList[len - 1] = '\0';
3153 #if defined(__APPLE__)
3154       SetEnvironment("DYLD_LIBRARY_PATH", newList);
3155 #else
3156       SetEnvironment("LD_LIBRARY_PATH", newList);
3157 #endif
3158 /*#ifdef _DEBUG
3159       printf("New value of [DY]LD_LIBRARY_PATH: %s\n", newList);
3160 #endif*/
3161       delete newList;
3162
3163       newLibPaths.Free();
3164       delete newLibPaths;
3165       delete libPathExists;
3166 #endif
3167
3168       if(compiler.distccEnabled && compiler.distccHosts)
3169          SetEnvironment("DISTCC_HOSTS", compiler.distccHosts);
3170
3171       delete oldList;
3172    }
3173
3174    void DestroyTemporaryProjectDir()
3175    {
3176       if(tmpPrjDir && tmpPrjDir[0])
3177       {
3178          if(FileExists(tmpPrjDir).isDirectory)
3179             DestroyDir(tmpPrjDir);
3180          property::tmpPrjDir = null;
3181       }
3182    }
3183
3184    IDEWorkSpace()
3185    {
3186       // Graphics Driver Menu
3187       int c;
3188
3189       /*
3190       app.currentSkin.selectionColor = selectionColor;
3191       app.currentSkin.selectionText = selectionText;
3192       */
3193
3194 /*
3195       driverItems = new MenuItem[app.numDrivers];
3196       for(c = 0; c < app.numDrivers; c++)
3197       {
3198          driverItems[c] = MenuItem { driversMenu, app.drivers[c], NotifySelect = NotifySelectDisplayDriver };
3199          driverItems[c].id = c;
3200          driverItems[c].isRadio = true;         
3201       }
3202 */
3203       driverItems = new MenuItem[2];
3204 #if defined(__unix__)
3205          driverItems[0] = MenuItem { driversMenu, "X", NotifySelect = NotifySelectDisplayDriver };
3206          driverItems[0].id = 0;
3207          driverItems[0].isRadio = true;         
3208 #else
3209          driverItems[0] = MenuItem { driversMenu, "GDI", NotifySelect = NotifySelectDisplayDriver };
3210          driverItems[0].id = 0;
3211          driverItems[0].isRadio = true;         
3212 #endif
3213          driverItems[1] = MenuItem { driversMenu, "OpenGL", NotifySelect = NotifySelectDisplayDriver };
3214          driverItems[1].id = 1;
3215          driverItems[1].isRadio = true;         
3216
3217 /*      skinItems = new MenuItem[app.numSkins];
3218       for(c = 0; c < app.numSkins; c++)
3219       {
3220          skinItems[c] = MenuItem {skinsMenu, app.skins[c], NotifySelect = NotifySelectDisplaySkin };
3221          skinItems[c].id = c;
3222          skinItems[c].isRadio = true;         
3223       }
3224 */
3225       ideFileDialog.master = this;
3226       ideProjectFileDialog.master = this;
3227
3228       //SetDriverAndSkin();
3229       return true;
3230    }
3231
3232    void UpdateRecentMenus()
3233    {
3234       int c;
3235       Menu fileMenu = menu.FindMenu($"File");
3236       Menu recentFiles = fileMenu.FindMenu($"Recent Files");
3237       Menu recentProjects = fileMenu.FindMenu($"Recent Projects");
3238       char * itemPath = new char[MAX_LOCATION];
3239       char * itemName = new char[MAX_LOCATION+4];
3240       MenuItem item;
3241
3242       recentFiles.Clear();
3243       c = 0;
3244
3245       for(recent : ideSettings.recentFiles)
3246       {
3247          strncpy(itemPath, recent, MAX_LOCATION); itemPath[MAX_LOCATION-1] = '\0';
3248          MakeSystemPath(itemPath);
3249          snprintf(itemName, MAX_LOCATION+4, "%d %s", 1 + c, itemPath); itemName[MAX_LOCATION+4-1] = '\0';
3250          recentFiles.AddDynamic(MenuItem { copyText = true, text = itemName, (Key)k1 + c, id = c, NotifySelect = ide.FileRecentFile }, ide, true);
3251          c++;
3252       }
3253
3254       recentProjects.Clear();
3255       c = 0;
3256       for(recent : ideSettings.recentProjects)
3257       {
3258          strncpy(itemPath, recent, MAX_LOCATION); itemPath[MAX_LOCATION-1] = '\0';
3259          MakeSystemPath(itemPath);
3260          snprintf(itemName, MAX_LOCATION+4, "%d %s", 1 + c, itemPath); itemName[MAX_LOCATION+4-1] = '\0';
3261          recentProjects.AddDynamic(MenuItem { copyText = true, text = itemName, (Key)k1 + c, id = c, NotifySelect = ide.FileRecentProject }, ide, true);
3262          c++;
3263       }
3264
3265       delete itemPath;
3266       delete itemName;
3267    }
3268
3269    ~IDEWorkSpace()
3270    {
3271       delete driverItems;
3272       delete skinItems;
3273       delete ideSettings;
3274    }
3275 }
3276
3277 void DestroyDir(char * path)
3278 {
3279    RecursiveDeleteFolderFSI fsi { };
3280    fsi.Iterate(path);
3281    delete fsi;
3282 }
3283
3284 #if defined(__WIN32__)
3285 define sdkDirName = "Ecere SDK";
3286 #else
3287 define sdkDirName = "ecere";
3288 #endif
3289
3290 void FindAndShellOpenInstalledFolder(char * name)
3291 {
3292    char * p = new char[MAX_LOCATION];
3293    char * v = new char[maxPathLen];
3294    byte * tokens[256];
3295    int c, numTokens;
3296    Array<String> paths { };
3297    p[0] = v[0] = '\0';
3298    strncpy(p, settingsContainer.moduleLocation, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3299    StripLastDirectory(p, p);
3300    PathCat(p, name);
3301    paths.Add(CopyString(p));
3302 #if defined(__WIN32__)
3303    GetEnvironment("ECERE_SDK_SRC", v, maxPathLen);
3304    if(v[0])
3305    {
3306       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3307       PathCat(p, name); paths.Add(CopyString(p));
3308    }
3309    GetEnvironment("AppData", 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", 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("ProgramFiles(x86)", v, maxPathLen);
3322    if(v[0])
3323    {
3324       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3325       PathCat(p, sdkDirName); PathCat(p, name); paths.Add(CopyString(p));
3326    }
3327    GetEnvironment("SystemDrive", v, maxPathLen);
3328    if(v[0])
3329    {
3330       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3331       PathCat(p, "Program Files"); PathCat(p, sdkDirName); PathCat(p, name); paths.Add(CopyString(p));
3332    }
3333 #else
3334    GetEnvironment("XDG_DATA_DIRS", v, maxPathLen);
3335    numTokens = TokenizeWith(v, sizeof(tokens) / sizeof(byte *), tokens, ":", false);
3336    for(c=0; c<numTokens; c++)
3337    {
3338       strncpy(p, tokens[c], MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3339       PathCat(p, sdkDirName); PathCat(p, name); paths.Add(CopyString(p));
3340    }
3341 #endif
3342    for(path : paths)
3343    {
3344       strncpy(p, path, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3345       if(FileExists(p).isDirectory)
3346       {
3347          ShellOpen(p);
3348          break;
3349       }
3350    }
3351    delete p;
3352    delete v;
3353    paths.Free();
3354    delete paths;
3355 }
3356
3357 void FindAndShellOpenInstalledFile(char * subdir, char * name)
3358 {
3359    char * p = new char[MAX_LOCATION];
3360    char * v = new char[maxPathLen];
3361    byte * tokens[256];
3362    int c, numTokens;
3363    Array<String> paths { };
3364    p[0] = v[0] = '\0';
3365    strncpy(p, settingsContainer.moduleLocation, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3366    paths.Add(CopyString(p));
3367    StripLastDirectory(p, p);
3368    PathCat(p, subdir);
3369    paths.Add(CopyString(p));
3370 #if defined(__WIN32__)
3371    GetEnvironment("ECERE_SDK_SRC", 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("AppData", 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", 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("ProgramFiles(x86)", v, maxPathLen);
3390    if(v[0])
3391    {
3392       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3393       PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
3394    }
3395    GetEnvironment("SystemDrive", v, maxPathLen);
3396    if(v[0])
3397    {
3398       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3399       PathCat(p, "Program Files"); PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
3400    }
3401 #else
3402    GetEnvironment("XDG_DATA_DIRS", v, maxPathLen);
3403    numTokens = TokenizeWith(v, sizeof(tokens) / sizeof(byte *), tokens, ":", false);
3404    for(c=0; c<numTokens; c++)
3405    {
3406       strncpy(p, tokens[c], MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3407       PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
3408    }
3409 #endif
3410    for(path : paths)
3411    {
3412       strncpy(p, path, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3413       PathCat(p, name);
3414       if(FileExists(p).isFile)
3415       {
3416          ShellOpen(p);
3417          break;
3418       }
3419    }
3420    delete p;
3421    delete v;
3422    paths.Free();
3423    delete paths;
3424 }
3425
3426 class RecursiveDeleteFolderFSI : NormalFileSystemIterator
3427 {
3428    bool preserveRootFolder;
3429
3430    void OutFolder(char * folderPath, bool isRoot)
3431    {
3432       if(!(preserveRootFolder && isRoot))
3433          RemoveDir(folderPath);
3434    }
3435
3436    bool OnFile(char * filePath)
3437    {
3438       DeleteFile(filePath);
3439       return true;
3440    }
3441 }
3442
3443 class IDEApp : GuiApplication
3444 {
3445    //driver = "Win32Console";
3446    // driver = "OpenGL";
3447    // skin = "Aqua";
3448    //skin = "TVision";
3449
3450    TempFile includeFile { };
3451
3452    bool Init()
3453    {
3454       char ext[MAX_EXTENSION];
3455       SetLoggingMode(stdOut, null);
3456       //SetLoggingMode(debug, null);
3457
3458       settingsContainer.Load();
3459       if(argc > 1 && !strcmpi(GetExtension(argv[1], ext), "3ds"))
3460       {
3461          app.driver = "OpenGL";
3462          ide.driverItems[1].checked = true;
3463       }
3464       else
3465       {
3466 #if defined(__unix__) || defined(__APPLE__)
3467          app.driver = (ideSettings.displayDriver && !strcmp(ideSettings.displayDriver, "OpenGL")) ? ideSettings.displayDriver : "X";
3468 #else
3469          app.driver = (ideSettings.displayDriver && !strcmp(ideSettings.displayDriver, "OpenGL")) ? ideSettings.displayDriver : "GDI";
3470 #endif
3471          ide.driverItems[ideSettings.displayDriver && !strcmp(ideSettings.displayDriver,"OpenGL")].checked = true;
3472       }
3473
3474       SetInIDE(true);
3475
3476       desktop.text = titleECEREIDE;
3477       /*
3478       int c;
3479       for(c = 1; c<app.argc; c++)
3480       {
3481          char fullPath[MAX_LOCATION];
3482          GetWorkingDir(fullPath, MAX_LOCATION);
3483          PathCat(fullPath, app.argv[c]);
3484          ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, null, yes, normal, false);
3485       }
3486       */
3487
3488       if(!LoadIncludeFile())
3489          PrintLn("error: unable to load :crossplatform.mk file inside ide binary.");
3490
3491       return true;
3492    }
3493
3494    bool LoadIncludeFile()
3495    {
3496       bool result = false;
3497       File include = FileOpen(":crossplatform.mk", read);
3498       if(include)
3499       {
3500          File f = includeFile;
3501          if(f)
3502          {
3503             for(; !include.Eof(); )
3504             {
3505                char buffer[4096];
3506                int count = include.Read(buffer, 1, 4096);
3507                f.Write(buffer, 1, count);
3508             }
3509             result = true;
3510          }
3511          delete include;
3512       }
3513       return result;
3514    }
3515 }
3516
3517 IDEMainFrame ideMainFrame { };
3518
3519 define app = ((IDEApp)__thisModule);
3520 #ifdef _DEBUG
3521 define titleECEREIDE = $"ECERE IDE (Debug)";
3522 #else
3523 define titleECEREIDE = $"ECERE IDE";
3524 #endif