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