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