ide;debugger; (#968) handle breakpoints by project correctly.
[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.ParseLoadedBreakpoints();
2266                         workspace.DropInvalidBreakpoints(null);
2267                         workspace.Save();
2268
2269                         ide.projectView.ShowOutputBuildLog(true);
2270                         {
2271                            CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
2272                            ide.projectView.DisplayCompiler(compiler, false);
2273                            delete compiler;
2274                         }
2275                         UpdateCompilerConfigs(false);
2276                         UpdateMakefiles();
2277                         {
2278                            char newWorkingDir[MAX_LOCATION];
2279                            StripLastDirectory(filePath, newWorkingDir);
2280                            ChangeFileDialogsDirectory(newWorkingDir, false);
2281                         }
2282                         if(document)
2283                            document.fileName = filePath;
2284
2285                         ideMainFrame.SetText("%s - %s", filePath, titleECEREIDE);
2286
2287                         // this crashes on starting ide with epj file, solution please?
2288                         // app.UpdateDisplay();
2289
2290                         workspace.holdTracking = true;
2291                         for(ofi : workspace.openedFiles)
2292                         {
2293                            if(ofi.state != closed)
2294                            {
2295                               Window file = OpenFile(ofi.path, normal, true, null, no, normal, noParsing);
2296                               if(file)
2297                               {
2298                                  char fileName[MAX_LOCATION];
2299                                  ProjectNode node;
2300                                  GetLastDirectory(ofi.path, fileName);
2301                                  node = projectView.project.topNode.Find(fileName, true);
2302                                  if(node)
2303                                     node.EnsureVisible();
2304                               }
2305                            }
2306                         }
2307                         ide.RepositionWindows(false);
2308                         workspace.holdTracking = false;
2309
2310                         workspace.timer.Start();
2311
2312 #if !defined(__WIN32__)
2313                         // Valgrind Debug menu updates
2314                         debugUseValgrindItem.checked = workspace.useValgrind;
2315
2316                         debugValgrindNoLeakCheckItem.checked      = workspace.vgLeakCheck == no;
2317                         debugValgrindSummaryLeakCheckItem.checked = workspace.vgLeakCheck == summary;
2318                         debugValgrindYesLeakCheckItem.checked     = workspace.vgLeakCheck == yes;
2319                         debugValgrindFullLeakCheckItem.checked    = workspace.vgLeakCheck == full;
2320
2321                         debugValgrindRSDefaultItem.checked = workspace.vgRedzoneSize == -1;
2322                         debugValgrindRS0Item.checked       = workspace.vgRedzoneSize == 0;
2323                         debugValgrindRS16Item.checked      = workspace.vgRedzoneSize == 16;
2324                         debugValgrindRS32Item.checked      = workspace.vgRedzoneSize == 32;
2325                         debugValgrindRS64Item.checked      = workspace.vgRedzoneSize == 64;
2326                         debugValgrindRS128Item.checked     = workspace.vgRedzoneSize == 128;
2327                         debugValgrindRS256Item.checked     = workspace.vgRedzoneSize == 256;
2328                         debugValgrindRS512Item.checked     = workspace.vgRedzoneSize == 512;
2329
2330                         debugValgrindTrackOriginsItem.checked = workspace.vgTrackOrigins;
2331 #endif
2332
2333                         findInFilesDialog.mode = FindInFilesMode::project;
2334                         findInFilesDialog.currentDirectory = ide.project.topNode.path;
2335                         
2336                         {
2337                            char location[MAX_LOCATION];
2338                            StripLastDirectory(ide.project.topNode.path, location);
2339                            ChangeProjectFileDialogDirectory(location);
2340                         }
2341                         
2342                         /*
2343                         if(projectView.debugger)
2344                            projectView.debugger.EvaluateWatches();
2345                         */
2346                         
2347                         break;
2348                      }
2349                      else 
2350                      {
2351                         if(MessageBox { type = yesNo, master = this, text = $"Error opening project", contents = $"Open a different project?" }.Modal() == yes)
2352                         {
2353                            ideProjectFileDialog.text = openProjectFileDialogTitle;
2354                            if(ideProjectFileDialog.Modal() == cancel)
2355                               return null;
2356                            filePath = ideProjectFileDialog.filePath;
2357                            GetExtension(filePath, extension);
2358                         }
2359                         else
2360                            return null;
2361                      }
2362                   }
2363                }
2364             }
2365             else
2366                return null;
2367          }
2368          else if(openMethod == add)
2369          {
2370             if(workspace)
2371             {
2372                Project prj = null;
2373                char slashFilePath[MAX_LOCATION];
2374                GetSlashPathBuffer(slashFilePath, filePath);
2375                for(p : workspace.projects)
2376                {
2377                   if(!fstrcmp(p.filePath, slashFilePath))
2378                   {
2379                      prj = p;
2380                      break;
2381                   }
2382                }
2383                if(prj)
2384                {
2385                   MessageBox { type = ok, parent = parent, master = this, text = $"Same Project", 
2386                         contents = $"This project is already present in workspace." }.Modal();
2387                }
2388                else
2389                {
2390                   prj = LoadProject(filePath, null);
2391                   if(prj)
2392                   {
2393                      char * activeConfigName = null;
2394                      CompilerConfig compiler = ideSettings.GetCompilerConfig(workspace.compiler);
2395                      prj.StartMonitoring();
2396                      workspace.projects.Add(prj);
2397                      if(toolBar.activeConfig.currentRow && toolBar.activeConfig.currentRow != toolBar.activeConfig.firstRow &&
2398                            toolBar.activeConfig.currentRow.string && toolBar.activeConfig.currentRow.string[0])
2399                         activeConfigName = toolBar.activeConfig.currentRow.string;
2400                      if(activeConfigName)
2401                      {
2402                         for(cfg : prj.configurations)
2403                         {
2404                            if(cfg.name && !strcmp(cfg.name, activeConfigName))
2405                            {
2406                               prj.config = cfg;
2407                               break;
2408                            }
2409                         }
2410                      }
2411                      if(projectView)
2412                         projectView.AddNode(prj.topNode, null);
2413                      workspace.modified = true;
2414                      workspace.Save();
2415                      findInFilesDialog.AddProjectItem(prj);
2416                      projectView.ShowOutputBuildLog(true);
2417                      projectView.DisplayCompiler(compiler, false);
2418                      projectView.ProjectUpdateMakefileForAllConfigs(prj);
2419                      delete compiler;
2420
2421                      {
2422                         char location[MAX_LOCATION];
2423                         StripLastDirectory(prj.topNode.path, location);
2424                         ChangeProjectFileDialogDirectory(location);
2425                      }
2426
2427                      // projectView is associated with the main project and not with the one just added but
2428                      return projectView; // just to let the caller know something was opened
2429                   }
2430                }
2431             }
2432             else
2433                return null;
2434          }
2435       }
2436       else if(!strcmp(extension, "bmp") || !strcmp(extension, "pcx") ||
2437             !strcmp(extension, "jpg") || !strcmp(extension, "gif") ||
2438             !strcmp(extension, "jpeg") || !strcmp(extension, "png"))
2439       {
2440          if(FileExists(filePath))
2441             document = PictureEdit { hasMaximize = true, hasMinimize = true, hasClose = true, borderStyle = sizable, 
2442                                        hasVertScroll = true, hasHorzScroll = true, parent = this, state = state, 
2443                                        visible = visible, bitmapFile = filePath, OnClose = PictureEditOnClose/*why?--GenericDocumentOnClose*/;
2444                                     };
2445          if(!document)
2446             MessageBox { type = ok, master = this, text = filePath, contents = $"File doesn't exist." }.Modal();
2447       }
2448 #ifndef NO3D
2449       else if(!strcmp(extension, "3ds"))
2450       {
2451          if(FileExists(filePath))
2452             document = ModelView { hasMaximize = true, hasMinimize = true, hasClose = true, borderStyle = sizable, 
2453                                     hasVertScroll = true, hasHorzScroll = true, parent = this, state = state, 
2454                                     visible = visible, modelFile = filePath, OnClose = ModelViewOnClose/*why?--GenericDocumentOnClose*/
2455                                     };
2456
2457          if(!document)
2458             MessageBox { type = ok, master = this, text = filePath, contents = $"File doesn't exist." }.Modal();
2459       }
2460 #endif
2461       else if(!strcmp(extension, "txt") || !strcmp(extension, "text") ||
2462             !strcmp(extension, "nfo") || !strcmp(extension, "info") ||
2463             !strcmp(extension, "htm") || !strcmp(extension, "html") ||
2464             !strcmp(extension, "css") || !strcmp(extension, "php") ||
2465             !strcmp(extension, "js"))
2466       {
2467          CodeEditor editor { parent = this, state = state, visible = false, noParsing = noParsing };
2468          editor.updatingCode = true;
2469          if(editor.LoadFile(filePath))
2470          {
2471             document = editor;
2472             editor.visible = true;
2473          }
2474          else
2475             delete editor;
2476          needFileModified = false;
2477       }
2478       else
2479       {
2480          CodeEditor editor { parent = this, state = state, visible = false, noParsing = noParsing };
2481          if(editor.LoadFile(filePath))
2482          {
2483             document = editor;
2484             editor.visible = true;
2485          }
2486          else
2487             delete editor;
2488          needFileModified = false;
2489       }
2490
2491       if(document && (document._class == class(PictureEdit) ||
2492             document._class == class(ModelView)))
2493       {
2494          document.Create();
2495          if(document)
2496          {
2497             document.fileName = filePath;
2498             if(workspace && !workspace.holdTracking)
2499                workspace.UpdateOpenedFileInfo(filePath, opened);
2500          }
2501       }
2502       
2503       if(!document && createIfFails != no)
2504       {
2505          if(createIfFails != yes && !needFileModified && 
2506                MessageBox { type = yesNo, master = this, text = filePath, contents = $"File doesn't exist. Create?" }.Modal() == yes)
2507             createIfFails = yes;
2508          if(createIfFails == yes || createIfFails == whatever)
2509          {
2510             document = (Window)NewCodeEditor(this, state, true);
2511             if(document)
2512                document.fileName = filePath;
2513          }
2514       }
2515
2516       if(document)
2517       {
2518          if(projectView && document._class == class(CodeEditor) && workspace)
2519          {
2520             int lineNumber, position;
2521             Point scroll;
2522             CodeEditor editor = (CodeEditor)document;
2523             editor.openedFileInfo = workspace.UpdateOpenedFileInfo(filePath, opened);
2524             editor.openedFileInfo.holdTracking = true;
2525             lineNumber = Max(editor.openedFileInfo.lineNumber - 1, 0);
2526             position = Max(editor.openedFileInfo.position - 1, 0);
2527             editor.editBox.GoToLineNum(lineNumber);
2528             editor.editBox.GoToPosition(editor.editBox.line, lineNumber, position);
2529             scroll.x = Max(editor.openedFileInfo.scroll.x, 0);
2530             scroll.y = Max(editor.openedFileInfo.scroll.y, 0);
2531             editor.editBox.scroll = scroll;
2532             editor.openedFileInfo.holdTracking = false;
2533          }
2534          
2535          if(needFileModified)
2536             document.OnFileModified = OnFileModified;
2537          document.NotifySaved = DocumentSaved;
2538          
2539          if(isProject)
2540             ideSettings.AddRecentProject(document.fileName);
2541          else
2542             ideSettings.AddRecentFile(document.fileName);
2543          ide.UpdateRecentMenus();
2544          ide.AdjustFileMenus();
2545          settingsContainer.Save();
2546          
2547          return document;
2548       }
2549       else
2550          return null;
2551    }
2552
2553    // TOCHECK: I can't use a generic one for both ModelView and PictureEdit both derived from Window
2554    /*bool Window::GenericDocumentOnClose(bool parentClosing)
2555    {
2556       if(!parentClosing && ide.workspace)
2557          ide.workspace.UpdateOpenedFileInfo(fileName, unknown);
2558       return true;
2559    }*/
2560    bool ModelView::ModelViewOnClose(bool parentClosing)
2561    {
2562       if(!parentClosing && ide.workspace)
2563          ide.workspace.UpdateOpenedFileInfo(fileName, unknown);
2564       return true;
2565    }
2566    bool PictureEdit::PictureEditOnClose(bool parentClosing)
2567    {
2568       if(!parentClosing && ide.workspace)
2569          ide.workspace.UpdateOpenedFileInfo(fileName, unknown);
2570       return true;
2571    }
2572
2573    /*
2574    void OnUnloadGraphics(Window window)
2575    {
2576       display.ClearMaterials();
2577       display.ClearTextures();
2578       display.ClearMeshes();
2579    }
2580    */
2581
2582    bool OnActivate(bool active, Window swap, bool * goOnWithActivation, bool direct)
2583    {
2584       caps.color = app.GetKeyState(capsState) ? black : Color { 128,128,128 };
2585       num.color = app.GetKeyState(numState) ? black : Color { 128,128,128 };
2586       return true;
2587    }
2588
2589    bool OnKeyDown(Key key, unichar ch)
2590    {
2591       switch(key)
2592       {
2593          case b:
2594             projectView.Update(null);
2595             break;
2596          case capsLock:
2597             caps.color = app.GetKeyState(capsState) ? black : Color { 128,128,128 };
2598             break;
2599          case numLock:
2600             num.color = app.GetKeyState(numState) ? black : Color { 128,128,128 };
2601             break;
2602       }
2603       return true;
2604    }
2605
2606    void GoToError(const char * line, bool noParsing)
2607    {
2608       if(projectView)
2609          projectView.GoToError(line, noParsing);
2610    }
2611
2612    void CodeLocationParseAndGoTo(const char * text, Project project, const char * dir)
2613    {
2614       char *s = null;
2615       char *path = text;
2616       char *colon = strchr(text, ':');
2617       char filePath[MAX_LOCATION] = "";
2618       char completePath[MAX_LOCATION];
2619       int line = 0, col = 0;
2620       int len = strlen(text);
2621       Project prj = null;
2622       FileAttribs fileAttribs;
2623
2624       // support for valgrind output
2625       if((s = strstr(text, "==")) && (s = strstr(s+2, "==")) && (s = strstr(s+2, ":")) && (s = strstr(s+1, ":")))
2626       {
2627          colon = s;
2628          for(; s>text; s--)
2629          {
2630             if(*s == '(')
2631             {
2632                path = s+1;
2633                break;
2634             }
2635          }
2636          /*for(s=colon; *s; s++)
2637          {
2638             if(*s == ')')
2639             {
2640                *s = '\0';;
2641                break;
2642             }
2643          }*/
2644          //*colon = '\0';
2645          //line = atoi(colon+1);
2646       }
2647       // support for "Found n match(es) in "file/path";
2648       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)
2649       {
2650          path = s+1;
2651       }
2652       else
2653       {
2654          if(colon && (colon[1] == '/' || colon[1] == '\\'))
2655          {
2656             path = (colon - 1 > path) ? colon - 1 : path;
2657             colon = strstr(colon + 1, ":");
2658          }
2659          if(*path == '*' && (s = strchr(path+1, '*')))
2660             path = s+1;
2661          while(isspace(*path)) path++;
2662       }
2663       if(*path == '(')
2664       {
2665          char * close = strchr(path, ')');
2666          if(close)
2667          {
2668             char name[256];
2669             strncpy(name, path+1, close - path - 1);
2670             name[close - path - 1] = '\0';
2671             for(p : ide.workspace.projects)
2672             {
2673                if(!strcmp(p.name, name))
2674                {
2675                   path = close + 1;
2676                   prj = p;
2677                   break;
2678                }
2679             }
2680          }
2681       }
2682       if(!prj)
2683          prj = project ? project : (dir ? null : ide.project);
2684       if(colon)
2685       {
2686          strncpy(filePath, path, colon - path);
2687          filePath[colon - path] = '\0';
2688          line = atoi(colon + 1);
2689          colon = strstr(colon + 1, ":");
2690          if(colon)
2691             col = atoi(colon + 1);
2692       }
2693       else if(path - 1 >= text && *(path - 1) == '\"')
2694       {
2695          colon = strchr(path, '\"');
2696          if(colon)
2697          {
2698             strncpy(filePath, path, colon - path);
2699             filePath[colon - path] = '\0';
2700          }
2701       }
2702       else if(path && !colon)
2703       {
2704          strcpy(filePath, path);
2705       }
2706
2707       if(filePath[0])
2708       {
2709          if(prj)
2710             strcpy(completePath, prj.topNode.path);
2711          else if(dir && dir[0])
2712             strcpy(completePath, dir);
2713          else
2714             completePath[0] = '\0';
2715          PathCat(completePath, filePath);
2716
2717          if((fileAttribs = FileExists(completePath)))
2718             CodeLocationGoTo(completePath, fileAttribs, line, col);
2719          else if(ide.workspace)
2720          {
2721             bool done = false;
2722             for(p : ide.workspace.projects)
2723             {
2724                strcpy(completePath, p.topNode.path);
2725                PathCat(completePath, filePath);
2726                if((fileAttribs = FileExists(completePath)).isFile)
2727                {
2728                   CodeLocationGoTo(completePath, fileAttribs, line, col);
2729                   done = true;
2730                   break;
2731                }
2732             }
2733             if(!done)
2734             {
2735                for(p : ide.workspace.projects)
2736                {
2737                   ProjectNode node = p.topNode.Find(filePath, false);
2738                   if(node)
2739                   {
2740                      node.GetFullFilePath(completePath);
2741                      if((fileAttribs = FileExists(completePath)).isFile)
2742                      {
2743                         CodeLocationGoTo(completePath, fileAttribs, line, col);
2744                         break;
2745                      }
2746                   }
2747                }
2748             }
2749          }
2750       }
2751    }
2752
2753    void CodeLocationGoTo(const char * path, const FileAttribs fileAttribs, int line, int col)
2754    {
2755       if(fileAttribs.isFile)
2756       {
2757          char ext[MAX_EXTENSION];
2758          GetExtension(path, ext);
2759          if(!strcmp(ext, "mp3") || !strcmp(ext, "flac") || !strcmp(ext, "ogg") || !strcmp(ext, "avi") || !strcmp(ext, "mkv"))
2760             ShellOpen(path);
2761          else if(!strcmp(ext, "a") || !strcmp(ext, "o") || !strcmp(ext, "lib") || !strcmp(ext, "dll") || !strcmp(ext, "exe"))
2762          {
2763             char dirPath[MAX_LOCATION];
2764             StripLastDirectory(path, dirPath);
2765             ShellOpen(dirPath);
2766          }
2767          else
2768          {
2769             CodeEditor codeEditor = (CodeEditor)OpenFile(path, normal, true, ext, no, normal, false);
2770             if(codeEditor && line)
2771             {
2772                EditBox editBox = codeEditor.editBox;
2773                editBox.GoToLineNum(line - 1);
2774                editBox.GoToPosition(editBox.line, line - 1, col ? (col - 1) : 0);
2775             }
2776          }
2777       }
2778       else if(fileAttribs.isDirectory)
2779          ShellOpen(path);
2780    }
2781
2782    void OnRedraw(Surface surface)
2783    {
2784       Bitmap bitmap = back.bitmap;
2785       if(bitmap)
2786          surface.Blit(bitmap, (clientSize.w - bitmap.width) / 2, (clientSize.h - bitmap.height) / 2, 0, 0, bitmap.width, bitmap.height);
2787    }
2788
2789    void SheetSelected(SheetType sheetSelected)
2790    {
2791       if(activeChild == sheet)
2792       {
2793          if(sheetSelected == methods)
2794          {
2795             viewPropertiesItem.accelerator = f4;
2796             viewPropertiesItem.parent = viewMenu;
2797             viewMethodsItem.parent = null;
2798          }
2799          else
2800          {
2801             viewMethodsItem.accelerator = f4;
2802             viewMethodsItem.parent = viewMenu;
2803             viewPropertiesItem.parent = null;
2804          }
2805       }
2806       else
2807       {
2808          viewMethodsItem.parent = viewMenu;
2809          viewPropertiesItem.parent = viewMenu;
2810          if(sheetSelected == methods)
2811          {
2812             viewMethodsItem.accelerator = f4;
2813             viewPropertiesItem.accelerator = 0;
2814          }
2815          else
2816          {
2817             viewMethodsItem.accelerator = 0;
2818             viewPropertiesItem.accelerator = f4;
2819          }
2820       }
2821    }
2822
2823    void OnActivateClient(Window client, Window previous)
2824    {
2825       //if(!client || client != previous)
2826       {
2827          Class dataType;
2828          if(!client || client != previous)
2829          {
2830             if(previous)
2831                dataType = previous._class;
2832             if(previous && !strcmp(dataType.name, "CodeEditor"))
2833             {
2834                ((CodeEditor)previous).UpdateFormCode();
2835             }
2836             else if(previous && !strcmp(dataType.name, "Designer"))
2837             {
2838                ((Designer)previous).codeEditor.UpdateFormCode();
2839             }
2840          }
2841
2842          if(client)
2843             dataType = client._class;
2844          if(client && !strcmp(dataType.name, "CodeEditor"))
2845          {
2846             CodeEditor codeEditor = (CodeEditor)client;
2847             SetPrivateModule(codeEditor.privateModule);
2848             SetCurrentContext(codeEditor.globalContext);
2849             SetTopContext(codeEditor.globalContext);
2850             SetGlobalContext(codeEditor.globalContext);
2851             
2852             SetDefines(&codeEditor.defines);
2853             SetImports(&codeEditor.imports);
2854
2855             SetActiveDesigner(codeEditor.designer);
2856             
2857             sheet.codeEditor = codeEditor;
2858             toolBox.codeEditor = codeEditor;
2859
2860             viewDesignerItem.parent = viewMenu;
2861             if(activeChild != codeEditor)
2862             {
2863                viewCodeItem.parent = viewMenu;
2864                viewDesignerItem.accelerator = 0;
2865                viewCodeItem.accelerator = f8;
2866             }
2867             else
2868             {
2869                viewCodeItem.parent = null;
2870                viewDesignerItem.accelerator = f8;
2871             }
2872          }
2873          else if(client && !strcmp(dataType.name, "Designer"))
2874          {
2875             CodeEditor codeEditor = ((Designer)client).codeEditor;
2876             if(codeEditor)
2877             {
2878                SetPrivateModule(codeEditor.privateModule);
2879                SetCurrentContext(codeEditor.globalContext);
2880                SetTopContext(codeEditor.globalContext);
2881                SetGlobalContext(codeEditor.globalContext);
2882                SetDefines(&codeEditor.defines);
2883                SetImports(&codeEditor.imports);
2884             }
2885             else
2886             {
2887                SetPrivateModule(null);
2888                SetCurrentContext(null);
2889                SetTopContext(null);
2890                SetGlobalContext(null);
2891                SetDefines(null);
2892                SetImports(null);
2893             }
2894
2895             SetActiveDesigner((Designer)client);
2896
2897             sheet.codeEditor = codeEditor;
2898             toolBox.codeEditor = codeEditor;
2899
2900             viewCodeItem.parent = viewMenu;
2901             if(activeChild != client)
2902             {
2903                viewDesignerItem.parent = viewMenu;
2904                viewDesignerItem.accelerator = f8;
2905                viewCodeItem.accelerator = 0;
2906             }
2907             else
2908             {
2909                viewDesignerItem.parent = null;
2910                viewCodeItem.accelerator = f8;
2911             }
2912          }
2913          else
2914          {
2915             if(sheet)
2916                sheet.codeEditor = null;
2917             toolBox.codeEditor = null;
2918             SetActiveDesigner(null);
2919
2920             viewDesignerItem.parent = null;
2921             viewCodeItem.parent = null;
2922          }
2923          if(sheet)
2924             SheetSelected(sheet.sheetSelected);
2925       }
2926
2927       projectCompileItem = null;
2928
2929       if(statusBar)
2930       {
2931          statusBar.Clear();
2932          if(client && client._class == eSystem_FindClass(__thisModule, "CodeEditor")) // !strcmp(client._class.name, "CodeEditor")
2933          {
2934             CodeEditor codeEditor = (CodeEditor)client;
2935             EditBox editBox = codeEditor.editBox;
2936
2937             statusBar.AddField(pos);
2938
2939             caps = { width = 40, text = $"CAPS", color = app.GetKeyState(capsState) ? black : Color { 128, 128, 128 } };
2940             statusBar.AddField(caps);
2941
2942             ovr = { width = 30, text = $"OVR", color = (editBox && editBox.overwrite) ? black : Color { 128, 128, 128 } };
2943             statusBar.AddField(ovr);
2944
2945             num = { width = 30, text = $"NUM", color = app.GetKeyState(numState) ? black : Color { 128, 128, 128 } };
2946             statusBar.AddField(num);
2947
2948             //statusBar.text = "Ready";
2949
2950             if(projectView && projectView.project)
2951             {
2952                bool isCObject = false;
2953                ProjectNode node = projectView.GetNodeFromWindow(client, null, false, false);
2954                if(!node && (node = projectView.GetNodeFromWindow(client, null, false, true)))
2955                   isCObject = true;
2956                if(node)
2957                {
2958                   char nodeName[MAX_FILENAME];
2959                   char name[MAX_FILENAME+96];
2960                   if(isCObject)
2961                      ChangeExtension(node.name, "c", nodeName);
2962                   sprintf(name, $"Compile %s", isCObject ? nodeName : node.name);
2963                   projectCompileItem = 
2964                   {
2965                      copyText = true, text = name, c, ctrlF7, disabled = projectView.buildInProgress;
2966
2967                      bool NotifySelect(MenuItem selection, Modifiers mods)
2968                      {
2969                         if(projectView)
2970                         {
2971                            bool result = false;
2972                            bool isCObject = false;
2973                            ProjectNode node = null;
2974                            for(p : ide.workspace.projects)
2975                            {
2976                               node = projectView.GetNodeFromWindow(activeClient, p, true, false);
2977                               if(node) break;
2978                            }
2979                            if(!node && (node = projectView.GetNodeFromWindow(activeClient, null, true, true)))
2980                               isCObject = true;
2981                            if(node)
2982                            {
2983                               List<ProjectNode> nodes { };
2984                               nodes.Add(node);
2985                               projectView.Compile(node.project, nodes, mods.ctrl && mods.shift, isCObject ? cObject : normal);
2986                               delete nodes;
2987                               result = true;
2988                            }
2989                            if(!result && node)
2990                               ide.outputView.buildBox.Logf($"File %s is excluded from current build configuration.\n", node.name);
2991                         }
2992                         return true;
2993                      }
2994                   };
2995                   projectMenu.AddDynamic(projectCompileItem, ide, false);
2996                }
2997             }
2998          }
2999          else
3000          {
3001             caps = ovr = num = null;
3002          }
3003       }
3004    }
3005
3006    bool OnClose(bool parentClosing)
3007    {
3008       //return !projectView.buildInProgress;
3009       if(projectView && projectView.buildInProgress)
3010          return false;
3011       if(DontTerminateDebugSession($"Close IDE"))
3012          return false;
3013       if(findInFilesDialog)
3014          findInFilesDialog.SearchStop();
3015       if(workspace)
3016       {
3017          workspace.timer.Stop();
3018          workspace.Save();
3019       }
3020       ideMainFrame.Destroy(0);
3021       return true;
3022    }
3023
3024    bool OnPostCreate()
3025    {
3026       int c;
3027       bool passThrough = false;
3028       bool debugStart = false;
3029       bool debugWorkDir = false;
3030       char * passDebugWorkDir = null;
3031       bool openAsText = false;
3032       DynamicString passArgs { };
3033       int ptArg = 0;
3034
3035       for(c = 1; c<app.argc; c++)
3036       {
3037          if(passThrough)
3038          {
3039             char * arg = app.argv[c];
3040             char * buf = new char[strlen(arg)*2+1];
3041             if(ptArg++ > 0)
3042                passArgs.concat(" ");
3043             PassArg(buf, arg);
3044             passArgs.concat(buf);
3045             delete buf;
3046          }
3047          else if(debugWorkDir)
3048          {
3049             passDebugWorkDir = CopyString(app.argv[c]);
3050             StripQuotes(passDebugWorkDir, passDebugWorkDir);
3051             debugWorkDir = false;
3052          }
3053          else if(!strcmp(app.argv[c], "-t"))
3054             openAsText = true;
3055          else if(!strcmp(app.argv[c], "-no-parsing"))
3056             ide.noParsing = true;
3057          else if(!strcmp(app.argv[c], "-debug-start"))
3058             debugStart = true;
3059          else if(!strcmp(app.argv[c], "-debug-work-dir"))
3060             debugWorkDir = true;
3061          else if(!strcmp(app.argv[c], "-@"))
3062             passThrough = true;
3063          else
3064          {
3065             char fullPath[MAX_LOCATION];
3066             char parentPath[MAX_LOCATION];
3067             char ext[MAX_EXTENSION];
3068             bool isProject;
3069             FileAttribs dirAttribs;
3070             GetWorkingDir(fullPath, MAX_LOCATION);
3071             PathCat(fullPath, app.argv[c]);
3072             StripLastDirectory(fullPath, parentPath);
3073             GetExtension(app.argv[c], ext);
3074             isProject = !openAsText && !strcmpi(ext, "epj");
3075
3076             if(isProject && c > (debugStart ? 2 : 1)) continue;
3077
3078             // Create directory for projects (only)
3079             if(((dirAttribs = FileExists(parentPath)) && dirAttribs.isDirectory) || isProject)
3080             {
3081                if(isProject && !FileExists(fullPath))
3082                {
3083                   char name[MAX_LOCATION];
3084                   NewProjectDialog newProjectDialog;
3085
3086                   if(projectView)
3087                   {
3088                      projectView.visible = false;
3089                      if(!projectView.Destroy(0))
3090                         return true;
3091                   }
3092
3093                   newProjectDialog = { master = this };
3094
3095                   strcpy(name, app.argv[c]);
3096                   StripExtension(name);
3097                   GetLastDirectory(name, name);
3098                   newProjectDialog.projectName.contents = name;
3099                   newProjectDialog.projectName.NotifyModified(newProjectDialog, newProjectDialog.projectName);
3100                   newProjectDialog.locationEditBox.path = parentPath;
3101                   newProjectDialog.NotifyModifiedLocation(newProjectDialog.locationEditBox);
3102
3103                   incref newProjectDialog;
3104                   newProjectDialog.Modal();
3105                   if(projectView)
3106                   {
3107                      ideSettings.AddRecentProject(projectView.fileName);
3108                      ide.UpdateRecentMenus();
3109                      settingsContainer.Save();
3110                   }
3111                   delete newProjectDialog;
3112                   // Open only one project
3113                   break;
3114                }
3115                else
3116                   ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, openAsText ? "txt" : null, yes, normal, false);
3117             }
3118             else if(strstr(fullPath, "http://") == fullPath)
3119                ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, openAsText ? "txt" : null, yes, normal, false);
3120          }
3121       }
3122       if(passThrough && projectView && projectView.project && workspace)
3123          workspace.commandLineArgs = passArgs;
3124       if(passDebugWorkDir && projectView && projectView.project && workspace)
3125       {
3126          workspace.debugDir = passDebugWorkDir;
3127          delete passDebugWorkDir;
3128       }
3129       if(debugStart)
3130          ;//MenuDebugStart(debugStartResumeItem, 0); // <-- how TODO this without getting into the app.Wait lock
3131
3132       UpdateToolBarActiveConfigs(false);
3133       UpdateToolBarActiveCompilers();
3134       delete passArgs;
3135       return true;
3136    }
3137
3138    void OnDestroy()
3139    {
3140       // IS THIS NEEDED? WASN'T HERE BEFORE...  Crashes on getting node's projectView otherwise
3141       if(projectView)
3142       {
3143          projectView.visible = false;
3144          projectView.Destroy(0);
3145          projectView = null;
3146       }
3147 #ifdef GDB_DEBUG_GUI
3148       gdbDialog.Destroy(0);
3149       delete gdbDialog;
3150 #endif
3151    }
3152
3153    void SetPath(bool projectsDirs, CompilerConfig compiler, ProjectConfig config, int bitDepth)
3154    {
3155       int c, len, count;
3156       char * newList;
3157       char * oldPaths[128];
3158       String oldList = new char[maxPathLen];
3159       Array<String> newExePaths { };
3160       //Map<String, bool> exePathExists { };
3161       bool found = false;
3162 #if defined(__unix__) || defined(__APPLE__)
3163       Array<String> newLibPaths { };
3164       Map<String, bool> libPathExists { };
3165 #endif
3166
3167       if(projectsDirs)
3168       {
3169          for(prj : workspace.projects)
3170          {
3171             DirExpression targetDirExp;
3172
3173             // SKIP FIRST PROJECT...
3174             if(prj == workspace.projects.firstIterator.data) continue;
3175
3176             // NOTE: Right now the additional project config dir will be
3177             //       obtained when the debugger is started, so toggling it
3178             //       while building will change which library gets used.
3179             //       To go with the initial state, e.g. when F5 was pressed,
3180             //       we nould need to keep a list of all project's active
3181             //       config upon startup.
3182             targetDirExp = prj.GetTargetDir(compiler, prj.config, bitDepth);
3183
3184             /*if(prj.config.targetType == sharedLibrary && prj.config.debug)
3185                cfg = prj.config;
3186             else
3187             {
3188                for(cfg = prj.configurations.first; cfg; cfg = cfg.next)
3189                   if(cfg.targetType == sharedLibrary && cfg.debug && !strcmpi(cfg.name, "Debug"))
3190                      break;
3191                if(!cfg)
3192                {
3193                   for(cfg = prj.configurations.first; cfg; cfg = cfg.next)
3194                      if(cfg.targetType == sharedLibrary && cfg.debug)
3195                         break;
3196                }
3197             }*/
3198             if(targetDirExp.dir)
3199             {
3200                char buffer[MAX_LOCATION];
3201 #if defined(__WIN32__)
3202                Array<String> paths = newExePaths;
3203 #else
3204                Array<String> paths = newLibPaths;
3205 #endif
3206                GetSystemPathBuffer(buffer, prj.topNode.path);
3207                PathCat(buffer, targetDirExp.dir);
3208                for(p : paths)
3209                {
3210                   if(!fstrcmp(p, buffer))
3211                   {
3212                      found = true;
3213                      break;
3214                   }
3215                }
3216                if(!found)
3217                   paths.Add(CopyString(buffer));
3218             }
3219             delete targetDirExp;
3220          }
3221       }
3222
3223       for(item : compiler.executableDirs)
3224       {
3225          found = false;
3226          for(p : newExePaths)
3227          {
3228             if(!fstrcmp(p, item))
3229             {
3230                found = true;
3231                break;
3232             }
3233          }
3234          if(!found)
3235             newExePaths.Add(CopySystemPath(item));
3236       }
3237
3238       GetEnvironment("PATH", oldList, maxPathLen);
3239 /*#ifdef _DEBUG
3240       printf("Old value of PATH: %s\n", oldList);
3241 #endif*/
3242       count = TokenizeWith(oldList, sizeof(oldPaths) / sizeof(char *), oldPaths, pathListSep, false);
3243       for(c = 0; c < count; c++)
3244       {
3245          found = false;
3246          for(p : newExePaths)
3247          {
3248             if(!fstrcmp(p, oldPaths[c]))
3249             {
3250                found = true;
3251                break;
3252             }
3253          }
3254          if(!found)
3255             newExePaths.Add(CopySystemPath(oldPaths[c]));
3256       }
3257
3258       len = 0;
3259       for(path : newExePaths)
3260          len += strlen(path) + 1;
3261       newList = new char[len + 1];
3262       newList[0] = '\0';
3263       for(path : newExePaths)
3264       {
3265          strcat(newList, path);
3266          strcat(newList, pathListSep);
3267       }
3268       newList[len - 1] = '\0';
3269       SetEnvironment("PATH", newList);
3270 /*#ifdef _DEBUG
3271       printf("New value of PATH: %s\n", newList);
3272 #endif*/
3273       delete newList;
3274
3275       newExePaths.Free();
3276       delete newExePaths;
3277
3278 #if defined(__unix__) || defined(__APPLE__)
3279
3280       for(item : compiler.libraryDirs)
3281       {
3282          if(!libPathExists[item])  // fstrcmp should be used
3283          {
3284             String s = CopyString(item);
3285             newLibPaths.Add(s);
3286             libPathExists[s] = true;
3287          }
3288       }
3289
3290 #if defined(__APPLE__)
3291       GetEnvironment("DYLD_LIBRARY_PATH", oldList, maxPathLen);
3292 #else
3293       GetEnvironment("LD_LIBRARY_PATH", oldList, maxPathLen);
3294 #endif
3295 /*#ifdef _DEBUG
3296       printf("Old value of [DY]LD_LIBRARY_PATH: %s\n", oldList);
3297 #endif*/
3298       count = TokenizeWith(oldList, sizeof(oldPaths) / sizeof(char *), oldPaths, pathListSep, false);
3299       for(c = 0; c < count; c++)
3300       {
3301          if(!libPathExists[oldPaths[c]])  // fstrcmp should be used
3302          {
3303             String s = CopyString(oldPaths[c]);
3304             newLibPaths.Add(s);
3305             libPathExists[s] = true;
3306          }
3307       }
3308
3309       len = 0;
3310       for(path : newLibPaths)
3311          len += strlen(path) + 1;
3312       newList = new char[len + 1];
3313       newList[0] = '\0';
3314       for(path : newLibPaths)
3315       {
3316          strcat(newList, path);
3317          strcat(newList, pathListSep);
3318       }
3319       newList[len - 1] = '\0';
3320 #if defined(__APPLE__)
3321       SetEnvironment("DYLD_LIBRARY_PATH", newList);
3322 #else
3323       SetEnvironment("LD_LIBRARY_PATH", newList);
3324 #endif
3325 /*#ifdef _DEBUG
3326       printf("New value of [DY]LD_LIBRARY_PATH: %s\n", newList);
3327 #endif*/
3328       delete newList;
3329
3330       newLibPaths.Free();
3331       delete newLibPaths;
3332       delete libPathExists;
3333 #endif
3334
3335       if(compiler.distccEnabled && compiler.distccHosts)
3336          SetEnvironment("DISTCC_HOSTS", compiler.distccHosts);
3337
3338       delete oldList;
3339    }
3340
3341    void DestroyTemporaryProjectDir()
3342    {
3343       if(tmpPrjDir && tmpPrjDir[0])
3344       {
3345          if(FileExists(tmpPrjDir).isDirectory)
3346             DestroyDir(tmpPrjDir);
3347          property::tmpPrjDir = null;
3348       }
3349    }
3350
3351    IDEWorkSpace()
3352    {
3353       // Graphics Driver Menu
3354       int c;
3355
3356       /*
3357       app.currentSkin.selectionColor = selectionColor;
3358       app.currentSkin.selectionText = selectionText;
3359       */
3360
3361 /*
3362       driverItems = new MenuItem[app.numDrivers];
3363       for(c = 0; c < app.numDrivers; c++)
3364       {
3365          driverItems[c] = MenuItem { driversMenu, app.drivers[c], NotifySelect = NotifySelectDisplayDriver };
3366          driverItems[c].id = c;
3367          driverItems[c].isRadio = true;         
3368       }
3369 */
3370       driverItems = new MenuItem[2];
3371 #if defined(__unix__)
3372          driverItems[0] = MenuItem { driversMenu, "X", NotifySelect = NotifySelectDisplayDriver };
3373          driverItems[0].id = 0;
3374          driverItems[0].isRadio = true;         
3375 #else
3376          driverItems[0] = MenuItem { driversMenu, "GDI", NotifySelect = NotifySelectDisplayDriver };
3377          driverItems[0].id = 0;
3378          driverItems[0].isRadio = true;         
3379 #endif
3380          driverItems[1] = MenuItem { driversMenu, "OpenGL", NotifySelect = NotifySelectDisplayDriver };
3381          driverItems[1].id = 1;
3382          driverItems[1].isRadio = true;         
3383
3384 /*      skinItems = new MenuItem[app.numSkins];
3385       for(c = 0; c < app.numSkins; c++)
3386       {
3387          skinItems[c] = MenuItem {skinsMenu, app.skins[c], NotifySelect = NotifySelectDisplaySkin };
3388          skinItems[c].id = c;
3389          skinItems[c].isRadio = true;         
3390       }
3391 */
3392       ideFileDialog.master = this;
3393       ideProjectFileDialog.master = this;
3394
3395       //SetDriverAndSkin();
3396       return true;
3397    }
3398
3399    void UpdateRecentMenus()
3400    {
3401       int c;
3402       Menu fileMenu = menu.FindMenu($"File");
3403       Menu recentFiles = fileMenu.FindMenu($"Recent Files");
3404       Menu recentProjects = fileMenu.FindMenu($"Recent Projects");
3405       char * itemPath = new char[MAX_LOCATION];
3406       char * itemName = new char[MAX_LOCATION+4];
3407       MenuItem item;
3408
3409       recentFiles.Clear();
3410       c = 0;
3411
3412       for(recent : ideSettings.recentFiles)
3413       {
3414          strncpy(itemPath, recent, MAX_LOCATION); itemPath[MAX_LOCATION-1] = '\0';
3415          MakeSystemPath(itemPath);
3416          snprintf(itemName, MAX_LOCATION+4, "%d %s", 1 + c, itemPath); itemName[MAX_LOCATION+4-1] = '\0';
3417          recentFiles.AddDynamic(MenuItem { copyText = true, text = itemName, (Key)k1 + c, id = c, NotifySelect = ide.FileRecentFile }, ide, true);
3418          c++;
3419       }
3420
3421       recentProjects.Clear();
3422       c = 0;
3423       for(recent : ideSettings.recentProjects)
3424       {
3425          strncpy(itemPath, recent, MAX_LOCATION); itemPath[MAX_LOCATION-1] = '\0';
3426          MakeSystemPath(itemPath);
3427          snprintf(itemName, MAX_LOCATION+4, "%d %s", 1 + c, itemPath); itemName[MAX_LOCATION+4-1] = '\0';
3428          recentProjects.AddDynamic(MenuItem { copyText = true, text = itemName, (Key)k1 + c, id = c, NotifySelect = ide.FileRecentProject }, ide, true);
3429          c++;
3430       }
3431
3432       delete itemPath;
3433       delete itemName;
3434    }
3435
3436    ~IDEWorkSpace()
3437    {
3438       delete driverItems;
3439       delete skinItems;
3440       delete ideSettings;
3441       if(documentor)
3442       {
3443          documentor.Puts("Quit\n");
3444          documentor.Wait();
3445          delete documentor;
3446       }
3447    }
3448 }
3449
3450 void DestroyDir(char * path)
3451 {
3452    RecursiveDeleteFolderFSI fsi { };
3453    fsi.Iterate(path);
3454    delete fsi;
3455 }
3456
3457 #if defined(__WIN32__)
3458 define sdkDirName = "Ecere SDK";
3459 #else
3460 define sdkDirName = "ecere";
3461 #endif
3462
3463 void FindAndShellOpenInstalledFolder(char * name)
3464 {
3465    char * p = new char[MAX_LOCATION];
3466    char * v = new char[maxPathLen];
3467    byte * tokens[256];
3468    int c, numTokens;
3469    Array<String> paths { };
3470    p[0] = v[0] = '\0';
3471    strncpy(p, settingsContainer.moduleLocation, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3472    StripLastDirectory(p, p);
3473    PathCat(p, name);
3474    paths.Add(CopyString(p));
3475 #if defined(__WIN32__)
3476    GetEnvironment("ECERE_SDK_SRC", v, maxPathLen);
3477    if(v[0])
3478    {
3479       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3480       PathCat(p, name); paths.Add(CopyString(p));
3481    }
3482    GetEnvironment("AppData", v, maxPathLen);
3483    if(v[0])
3484    {
3485       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3486       PathCat(p, sdkDirName); PathCat(p, name); paths.Add(CopyString(p));
3487    }
3488    GetEnvironment("ProgramFiles", v, maxPathLen);
3489    if(v[0])
3490    {
3491       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3492       PathCat(p, sdkDirName); PathCat(p, name); paths.Add(CopyString(p));
3493    }
3494    GetEnvironment("ProgramFiles(x86)", v, maxPathLen);
3495    if(v[0])
3496    {
3497       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3498       PathCat(p, sdkDirName); PathCat(p, name); paths.Add(CopyString(p));
3499    }
3500    GetEnvironment("SystemDrive", v, maxPathLen);
3501    if(v[0])
3502    {
3503       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3504       PathCat(p, "Program Files"); PathCat(p, sdkDirName); PathCat(p, name); paths.Add(CopyString(p));
3505    }
3506 #else
3507    GetEnvironment("XDG_DATA_DIRS", v, maxPathLen);
3508    numTokens = TokenizeWith(v, sizeof(tokens) / sizeof(byte *), tokens, ":", false);
3509    for(c=0; c<numTokens; c++)
3510    {
3511       strncpy(p, tokens[c], MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3512       PathCat(p, sdkDirName); PathCat(p, name); paths.Add(CopyString(p));
3513    }
3514 #endif
3515    for(path : paths)
3516    {
3517       strncpy(p, path, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3518       if(FileExists(p).isDirectory)
3519       {
3520          ShellOpen(p);
3521          break;
3522       }
3523    }
3524    delete p;
3525    delete v;
3526    paths.Free();
3527    delete paths;
3528 }
3529
3530 void FindAndShellOpenInstalledFile(char * subdir, char * name)
3531 {
3532    char * p = new char[MAX_LOCATION];
3533    char * v = new char[maxPathLen];
3534    byte * tokens[256];
3535    int c, numTokens;
3536    Array<String> paths { };
3537    p[0] = v[0] = '\0';
3538    strncpy(p, settingsContainer.moduleLocation, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3539    paths.Add(CopyString(p));
3540    StripLastDirectory(p, p);
3541    PathCat(p, subdir);
3542    paths.Add(CopyString(p));
3543 #if defined(__WIN32__)
3544    GetEnvironment("ECERE_SDK_SRC", v, maxPathLen);
3545    if(v[0])
3546    {
3547       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3548       PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
3549    }
3550    GetEnvironment("AppData", v, maxPathLen);
3551    if(v[0])
3552    {
3553       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3554       PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
3555    }
3556    GetEnvironment("ProgramFiles", v, maxPathLen);
3557    if(v[0])
3558    {
3559       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3560       PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
3561    }
3562    GetEnvironment("ProgramFiles(x86)", v, maxPathLen);
3563    if(v[0])
3564    {
3565       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3566       PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
3567    }
3568    GetEnvironment("SystemDrive", v, maxPathLen);
3569    if(v[0])
3570    {
3571       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3572       PathCat(p, "Program Files"); PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
3573    }
3574 #else
3575    GetEnvironment("XDG_DATA_DIRS", v, maxPathLen);
3576    numTokens = TokenizeWith(v, sizeof(tokens) / sizeof(byte *), tokens, ":", false);
3577    for(c=0; c<numTokens; c++)
3578    {
3579       strncpy(p, tokens[c], MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3580       PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
3581    }
3582 #endif
3583    for(path : paths)
3584    {
3585       strncpy(p, path, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3586       PathCat(p, name);
3587       if(FileExists(p).isFile)
3588       {
3589          ShellOpen(p);
3590          break;
3591       }
3592    }
3593    delete p;
3594    delete v;
3595    paths.Free();
3596    delete paths;
3597 }
3598
3599 class RecursiveDeleteFolderFSI : NormalFileSystemIterator
3600 {
3601    bool preserveRootFolder;
3602
3603    void OutFolder(char * folderPath, bool isRoot)
3604    {
3605       if(!(preserveRootFolder && isRoot))
3606          RemoveDir(folderPath);
3607    }
3608
3609    bool OnFile(char * filePath)
3610    {
3611       DeleteFile(filePath);
3612       return true;
3613    }
3614 }
3615
3616 class IDEApp : GuiApplication
3617 {
3618    //driver = "Win32Console";
3619    // driver = "OpenGL";
3620    // skin = "Aqua";
3621    //skin = "TVision";
3622
3623    TempFile includeFile { };
3624
3625    bool Init()
3626    {
3627       char ext[MAX_EXTENSION];
3628       SetLoggingMode(stdOut, null);
3629       //SetLoggingMode(debug, null);
3630
3631       settingsContainer.Load();
3632       if(argc > 1 && !strcmpi(GetExtension(argv[1], ext), "3ds"))
3633       {
3634          app.driver = "OpenGL";
3635          ide.driverItems[1].checked = true;
3636       }
3637       else
3638       {
3639 #if defined(__unix__) || defined(__APPLE__)
3640          app.driver = (ideSettings.displayDriver && !strcmp(ideSettings.displayDriver, "OpenGL")) ? ideSettings.displayDriver : "X";
3641 #else
3642          app.driver = (ideSettings.displayDriver && !strcmp(ideSettings.displayDriver, "OpenGL")) ? ideSettings.displayDriver : "GDI";
3643 #endif
3644          ide.driverItems[ideSettings.displayDriver && !strcmp(ideSettings.displayDriver,"OpenGL")].checked = true;
3645       }
3646
3647       SetInIDE(true);
3648
3649       desktop.text = titleECEREIDE;
3650       /*
3651       int c;
3652       for(c = 1; c<app.argc; c++)
3653       {
3654          char fullPath[MAX_LOCATION];
3655          GetWorkingDir(fullPath, MAX_LOCATION);
3656          PathCat(fullPath, app.argv[c]);
3657          ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, null, yes, normal, false);
3658       }
3659       */
3660
3661       if(!LoadIncludeFile())
3662          PrintLn("error: unable to load :crossplatform.mk file inside ide binary.");
3663
3664       return true;
3665    }
3666
3667    bool Cycle(bool idle)
3668    {
3669       if(ide.documentor)
3670       {
3671          if(ide.documentor.Peek())
3672          {
3673             char line[1024];
3674             ide.documentor.GetLine(line, sizeof(line));
3675             if(!strcmpi(line, "Exited"))
3676             {
3677                ide.documentor.CloseInput();
3678                ide.documentor.CloseOutput();
3679                ide.documentor.Wait();
3680                delete ide.documentor;
3681             }
3682          }
3683          if(ide.documentor && ide.documentor.eof)
3684          {
3685             ide.documentor.CloseInput();
3686             ide.documentor.CloseOutput();
3687             ide.documentor.Wait();
3688             delete ide.documentor;
3689          }
3690       }
3691       return true;
3692    }
3693
3694    bool LoadIncludeFile()
3695    {
3696       bool result = false;
3697       File include = FileOpen(":crossplatform.mk", read);
3698       if(include)
3699       {
3700          File f = includeFile;
3701          if(f)
3702          {
3703             for(; !include.Eof(); )
3704             {
3705                char buffer[4096];
3706                int count = include.Read(buffer, 1, 4096);
3707                f.Write(buffer, 1, count);
3708             }
3709             result = true;
3710          }
3711          delete include;
3712       }
3713       return result;
3714    }
3715 }
3716
3717 IDEMainFrame ideMainFrame { };
3718
3719 define app = ((IDEApp)__thisModule);
3720 #ifdef _DEBUG
3721 define titleECEREIDE = $"ECERE IDE (Debug)";
3722 #else
3723 define titleECEREIDE = $"ECERE IDE";
3724 #endif