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