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