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