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