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