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