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