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