65967b0d8f949e4175f74be70856c34c4edc76e4
[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          if(!strcmp(ext, "mp3") || !strcmp(ext, "flac") || !strcmp(ext, "ogg") || !strcmp(ext, "avi") || !strcmp(ext, "mkv"))
2741             ShellOpen(path);
2742          else if(!strcmp(ext, "a") || !strcmp(ext, "o") || !strcmp(ext, "lib") || !strcmp(ext, "dll") || !strcmp(ext, "exe"))
2743          {
2744             char dirPath[MAX_LOCATION];
2745             StripLastDirectory(path, dirPath);
2746             ShellOpen(dirPath);
2747          }
2748          else
2749          {
2750             CodeEditor codeEditor = (CodeEditor)OpenFile(path, normal, true, ext, no, normal, false);
2751             if(codeEditor && line)
2752             {
2753                EditBox editBox = codeEditor.editBox;
2754                editBox.GoToLineNum(line - 1);
2755                editBox.GoToPosition(editBox.line, line - 1, col ? (col - 1) : 0);
2756             }
2757          }
2758       }
2759       else if(fileAttribs.isDirectory)
2760          ShellOpen(path);
2761    }
2762
2763    void OnRedraw(Surface surface)
2764    {
2765       Bitmap bitmap = back.bitmap;
2766       if(bitmap)
2767          surface.Blit(bitmap, (clientSize.w - bitmap.width) / 2, (clientSize.h - bitmap.height) / 2, 0, 0, bitmap.width, bitmap.height);
2768    }
2769
2770    void SheetSelected(SheetType sheetSelected)
2771    {
2772       if(activeChild == sheet)
2773       {
2774          if(sheetSelected == methods)
2775          {
2776             viewPropertiesItem.accelerator = f4;
2777             viewPropertiesItem.parent = viewMenu;
2778             viewMethodsItem.parent = null;
2779          }
2780          else
2781          {
2782             viewMethodsItem.accelerator = f4;
2783             viewMethodsItem.parent = viewMenu;
2784             viewPropertiesItem.parent = null;
2785          }
2786       }
2787       else
2788       {
2789          viewMethodsItem.parent = viewMenu;
2790          viewPropertiesItem.parent = viewMenu;
2791          if(sheetSelected == methods)
2792          {
2793             viewMethodsItem.accelerator = f4;
2794             viewPropertiesItem.accelerator = 0;
2795          }
2796          else
2797          {
2798             viewMethodsItem.accelerator = 0;
2799             viewPropertiesItem.accelerator = f4;
2800          }
2801       }
2802    }
2803
2804    void OnActivateClient(Window client, Window previous)
2805    {
2806       //if(!client || client != previous)
2807       {
2808          Class dataType;
2809          if(!client || client != previous)
2810          {
2811             if(previous)
2812                dataType = previous._class;
2813             if(previous && !strcmp(dataType.name, "CodeEditor"))
2814             {
2815                ((CodeEditor)previous).UpdateFormCode();
2816             }
2817             else if(previous && !strcmp(dataType.name, "Designer"))
2818             {
2819                ((Designer)previous).codeEditor.UpdateFormCode();
2820             }
2821          }
2822
2823          if(client)
2824             dataType = client._class;
2825          if(client && !strcmp(dataType.name, "CodeEditor"))
2826          {
2827             CodeEditor codeEditor = (CodeEditor)client;
2828             SetPrivateModule(codeEditor.privateModule);
2829             SetCurrentContext(codeEditor.globalContext);
2830             SetTopContext(codeEditor.globalContext);
2831             SetGlobalContext(codeEditor.globalContext);
2832
2833             SetDefines(&codeEditor.defines);
2834             SetImports(&codeEditor.imports);
2835
2836             SetActiveDesigner(codeEditor.designer);
2837
2838             sheet.codeEditor = codeEditor;
2839             toolBox.codeEditor = codeEditor;
2840
2841             viewDesignerItem.parent = viewMenu;
2842             if(activeChild != codeEditor)
2843             {
2844                viewCodeItem.parent = viewMenu;
2845                viewDesignerItem.accelerator = 0;
2846                viewCodeItem.accelerator = f8;
2847             }
2848             else
2849             {
2850                viewCodeItem.parent = null;
2851                viewDesignerItem.accelerator = f8;
2852             }
2853          }
2854          else if(client && !strcmp(dataType.name, "Designer"))
2855          {
2856             CodeEditor codeEditor = ((Designer)client).codeEditor;
2857             if(codeEditor)
2858             {
2859                SetPrivateModule(codeEditor.privateModule);
2860                SetCurrentContext(codeEditor.globalContext);
2861                SetTopContext(codeEditor.globalContext);
2862                SetGlobalContext(codeEditor.globalContext);
2863                SetDefines(&codeEditor.defines);
2864                SetImports(&codeEditor.imports);
2865             }
2866             else
2867             {
2868                SetPrivateModule(null);
2869                SetCurrentContext(null);
2870                SetTopContext(null);
2871                SetGlobalContext(null);
2872                SetDefines(null);
2873                SetImports(null);
2874             }
2875
2876             SetActiveDesigner((Designer)client);
2877
2878             sheet.codeEditor = codeEditor;
2879             toolBox.codeEditor = codeEditor;
2880
2881             viewCodeItem.parent = viewMenu;
2882             if(activeChild != client)
2883             {
2884                viewDesignerItem.parent = viewMenu;
2885                viewDesignerItem.accelerator = f8;
2886                viewCodeItem.accelerator = 0;
2887             }
2888             else
2889             {
2890                viewDesignerItem.parent = null;
2891                viewCodeItem.accelerator = f8;
2892             }
2893          }
2894          else
2895          {
2896             if(sheet)
2897                sheet.codeEditor = null;
2898             toolBox.codeEditor = null;
2899             SetActiveDesigner(null);
2900
2901             viewDesignerItem.parent = null;
2902             viewCodeItem.parent = null;
2903          }
2904          if(sheet)
2905             SheetSelected(sheet.sheetSelected);
2906       }
2907
2908       projectCompileItem = null;
2909
2910       if(statusBar)
2911       {
2912          statusBar.Clear();
2913          if(client && client._class == eSystem_FindClass(__thisModule, "CodeEditor")) // !strcmp(client._class.name, "CodeEditor")
2914          {
2915             CodeEditor codeEditor = (CodeEditor)client;
2916             EditBox editBox = codeEditor.editBox;
2917
2918             statusBar.AddField(pos);
2919
2920             caps = { width = 40, text = $"CAPS" };
2921             statusBar.AddField(caps);
2922             UpdateStateLight(caps, app.GetKeyState(capsState));
2923
2924             ovr = { width = 36, text = $"OVR" };
2925             statusBar.AddField(ovr);
2926             UpdateStateLight(ovr, (editBox && editBox.overwrite));
2927
2928             num = { width = 36, text = $"NUM" };
2929             statusBar.AddField(num);
2930             UpdateStateLight(num, app.GetKeyState(numState));
2931
2932             //statusBar.text = "Ready";
2933
2934             if(projectView && projectView.project)
2935             {
2936                bool isCObject = false;
2937                ProjectNode node = projectView.GetNodeFromWindow(client, null, true, false, null);
2938                if(!node && (node = projectView.GetNodeFromWindow(client, null, true, true, null)))
2939                   isCObject = true;
2940                if(node)
2941                {
2942                   char nodeName[MAX_FILENAME];
2943                   char name[MAX_FILENAME+96];
2944                   if(isCObject)
2945                      ChangeExtension(node.name, "c", nodeName);
2946                   sprintf(name, $"Compile %s", isCObject ? nodeName : node.name);
2947                   projectCompileItem =
2948                   {
2949                      copyText = true, text = name, c, ctrlF7, disabled = projectView.buildInProgress;
2950
2951                      bool NotifySelect(MenuItem selection, Modifiers mods)
2952                      {
2953                         if(projectView)
2954                         {
2955                            bool isCObject = false;
2956                            bool isExcluded = false;
2957                            ProjectNode node = projectView.GetNodeForCompilationFromWindow(activeClient, true, &isExcluded, &isCObject);
2958                            if(node)
2959                            {
2960                               if(isExcluded)
2961                                  ide.outputView.buildBox.Logf($"%s %s is excluded from current build configuration.\n", isCObject ? "Object file" : "File", node.name);
2962                               else
2963                               {
2964                                  List<ProjectNode> nodes { };
2965                                  nodes.Add(node);
2966                                  projectView.Compile(node.project, nodes, mods.ctrl && mods.shift, isCObject ? cObject : normal);
2967                                  delete nodes;
2968                               }
2969                            }
2970                         }
2971                         return true;
2972                      }
2973                   };
2974                   projectMenu.AddDynamic(projectCompileItem, ide, false);
2975                }
2976             }
2977          }
2978          else
2979          {
2980             caps = ovr = num = null;
2981          }
2982       }
2983    }
2984
2985    bool OnClose(bool parentClosing)
2986    {
2987       //return !projectView.buildInProgress;
2988       if(projectView && projectView.buildInProgress)
2989          return false;
2990       if(DontTerminateDebugSession($"Close IDE"))
2991          return false;
2992       if(findInFilesDialog)
2993          findInFilesDialog.SearchStop();
2994       if(workspace)
2995       {
2996          workspace.timer.Stop();
2997          workspace.Save();
2998       }
2999       ideMainFrame.Destroy(0);
3000       return true;
3001    }
3002
3003    bool OnPostCreate()
3004    {
3005       int c;
3006       bool passThrough = false;
3007       bool debugStart = false;
3008       bool debugWorkDir = false;
3009       char * passDebugWorkDir = null;
3010       bool openAsText = false;
3011       DynamicString passArgs { };
3012       int ptArg = 0;
3013
3014       for(c = 1; c<app.argc; c++)
3015       {
3016          if(passThrough)
3017          {
3018             char * arg = app.argv[c];
3019             char * buf = new char[strlen(arg)*2+1];
3020             if(ptArg++ > 0)
3021                passArgs.concat(" ");
3022             PassArg(buf, arg);
3023             passArgs.concat(buf);
3024             delete buf;
3025          }
3026          else if(debugWorkDir)
3027          {
3028             passDebugWorkDir = CopyString(app.argv[c]);
3029             StripQuotes(passDebugWorkDir, passDebugWorkDir);
3030             debugWorkDir = false;
3031          }
3032          else if(!strcmp(app.argv[c], "-t"))
3033             openAsText = true;
3034          else if(!strcmp(app.argv[c], "-no-parsing"))
3035             ide.noParsing = true;
3036          else if(!strcmp(app.argv[c], "-debug-start"))
3037             debugStart = true;
3038          else if(!strcmp(app.argv[c], "-debug-work-dir"))
3039             debugWorkDir = true;
3040          else if(!strcmp(app.argv[c], "-@"))
3041             passThrough = true;
3042          else
3043          {
3044             char fullPath[MAX_LOCATION];
3045             char parentPath[MAX_LOCATION];
3046             char ext[MAX_EXTENSION];
3047             bool isProject;
3048             FileAttribs dirAttribs;
3049             GetWorkingDir(fullPath, MAX_LOCATION);
3050             PathCat(fullPath, app.argv[c]);
3051             StripLastDirectory(fullPath, parentPath);
3052             GetExtension(app.argv[c], ext);
3053             isProject = !openAsText && !strcmpi(ext, "epj");
3054
3055             if(isProject && c > (debugStart ? 2 : 1)) continue;
3056
3057             // Create directory for projects (only)
3058             if(((dirAttribs = FileExists(parentPath)) && dirAttribs.isDirectory) || isProject)
3059             {
3060                if(isProject && !FileExists(fullPath))
3061                {
3062                   char name[MAX_LOCATION];
3063                   NewProjectDialog newProjectDialog;
3064
3065                   if(projectView)
3066                   {
3067                      projectView.visible = false;
3068                      if(!projectView.Destroy(0))
3069                         return true;
3070                   }
3071
3072                   newProjectDialog = { master = this };
3073
3074                   strcpy(name, app.argv[c]);
3075                   StripExtension(name);
3076                   GetLastDirectory(name, name);
3077                   newProjectDialog.projectName.contents = name;
3078                   newProjectDialog.projectName.NotifyModified(newProjectDialog, newProjectDialog.projectName);
3079                   newProjectDialog.locationEditBox.path = parentPath;
3080                   newProjectDialog.NotifyModifiedLocation(newProjectDialog.locationEditBox);
3081
3082                   incref newProjectDialog;
3083                   newProjectDialog.Modal();
3084                   if(projectView)
3085                   {
3086                      ideSettings.AddRecentProject(projectView.fileName);
3087                      ide.UpdateRecentMenus();
3088                      settingsContainer.Save();
3089                   }
3090                   delete newProjectDialog;
3091                   // Open only one project
3092                   break;
3093                }
3094                else
3095                   ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, openAsText ? "txt" : null, yes, normal, false);
3096             }
3097             else if(strstr(fullPath, "http://") == fullPath)
3098                ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, openAsText ? "txt" : null, yes, normal, false);
3099          }
3100       }
3101       if(passThrough && projectView && projectView.project && workspace)
3102          workspace.commandLineArgs = passArgs;
3103       if(passDebugWorkDir && projectView && projectView.project && workspace)
3104       {
3105          workspace.debugDir = passDebugWorkDir;
3106          delete passDebugWorkDir;
3107       }
3108       if(debugStart)
3109          ;//MenuDebugStart(debugStartResumeItem, 0); // <-- how TODO this without getting into the app.Wait lock
3110
3111       UpdateToolBarActiveConfigs(false);
3112       UpdateToolBarActiveCompilers();
3113       delete passArgs;
3114       return true;
3115    }
3116
3117    void OnDestroy()
3118    {
3119       // IS THIS NEEDED? WASN'T HERE BEFORE...  Crashes on getting node's projectView otherwise
3120       if(projectView)
3121       {
3122          projectView.visible = false;
3123          projectView.Destroy(0);
3124          projectView = null;
3125       }
3126 #ifdef GDB_DEBUG_GUI
3127       gdbDialog.Destroy(0);
3128       delete gdbDialog;
3129 #endif
3130    }
3131
3132    void SetPath(bool projectsDirs, CompilerConfig compiler, ProjectConfig config, int bitDepth)
3133    {
3134       int c, len, count;
3135       char * newList;
3136       char * oldPaths[128];
3137       String oldList = new char[maxPathLen];
3138       Array<String> newExePaths { };
3139       //Map<String, bool> exePathExists { };
3140       bool found = false;
3141 #if defined(__unix__) || defined(__APPLE__)
3142       Array<String> newLibPaths { };
3143       Map<String, bool> libPathExists { };
3144 #endif
3145
3146       if(projectsDirs)
3147       {
3148          for(prj : workspace.projects)
3149          {
3150             DirExpression targetDirExp;
3151
3152             // SKIP FIRST PROJECT...
3153             if(prj == workspace.projects.firstIterator.data) continue;
3154
3155             // NOTE: Right now the additional project config dir will be
3156             //       obtained when the debugger is started, so toggling it
3157             //       while building will change which library gets used.
3158             //       To go with the initial state, e.g. when F5 was pressed,
3159             //       we nould need to keep a list of all project's active
3160             //       config upon startup.
3161             targetDirExp = prj.GetTargetDir(compiler, prj.config, bitDepth);
3162
3163             /*if(prj.config.targetType == sharedLibrary && prj.config.debug)
3164                cfg = prj.config;
3165             else
3166             {
3167                for(cfg = prj.configurations.first; cfg; cfg = cfg.next)
3168                   if(cfg.targetType == sharedLibrary && cfg.debug && !strcmpi(cfg.name, "Debug"))
3169                      break;
3170                if(!cfg)
3171                {
3172                   for(cfg = prj.configurations.first; cfg; cfg = cfg.next)
3173                      if(cfg.targetType == sharedLibrary && cfg.debug)
3174                         break;
3175                }
3176             }*/
3177             if(targetDirExp.dir)
3178             {
3179                char buffer[MAX_LOCATION];
3180 #if defined(__WIN32__)
3181                Array<String> paths = newExePaths;
3182 #else
3183                Array<String> paths = newLibPaths;
3184 #endif
3185                GetSystemPathBuffer(buffer, prj.topNode.path);
3186                PathCat(buffer, targetDirExp.dir);
3187                for(p : paths)
3188                {
3189                   if(!fstrcmp(p, buffer))
3190                   {
3191                      found = true;
3192                      break;
3193                   }
3194                }
3195                if(!found)
3196                   paths.Add(CopyString(buffer));
3197             }
3198             delete targetDirExp;
3199          }
3200       }
3201
3202       for(item : compiler.executableDirs)
3203       {
3204          found = false;
3205          for(p : newExePaths)
3206          {
3207             if(!fstrcmp(p, item))
3208             {
3209                found = true;
3210                break;
3211             }
3212          }
3213          if(!found)
3214             newExePaths.Add(CopySystemPath(item));
3215       }
3216
3217       GetEnvironment("PATH", oldList, maxPathLen);
3218 /*#ifdef _DEBUG
3219       printf("Old value of PATH: %s\n", oldList);
3220 #endif*/
3221       count = TokenizeWith(oldList, sizeof(oldPaths) / sizeof(char *), oldPaths, pathListSep, false);
3222       for(c = 0; c < count; c++)
3223       {
3224          found = false;
3225          for(p : newExePaths)
3226          {
3227             if(!fstrcmp(p, oldPaths[c]))
3228             {
3229                found = true;
3230                break;
3231             }
3232          }
3233          if(!found)
3234             newExePaths.Add(CopySystemPath(oldPaths[c]));
3235       }
3236
3237       len = 0;
3238       for(path : newExePaths)
3239          len += strlen(path) + 1;
3240       newList = new char[len + 1];
3241       newList[0] = '\0';
3242       for(path : newExePaths)
3243       {
3244          strcat(newList, path);
3245          strcat(newList, pathListSep);
3246       }
3247       newList[len - 1] = '\0';
3248       SetEnvironment("PATH", newList);
3249 /*#ifdef _DEBUG
3250       printf("New value of PATH: %s\n", newList);
3251 #endif*/
3252       delete newList;
3253
3254       newExePaths.Free();
3255       delete newExePaths;
3256
3257 #if defined(__unix__) || defined(__APPLE__)
3258
3259       for(item : compiler.libraryDirs)
3260       {
3261          if(!libPathExists[item])  // fstrcmp should be used
3262          {
3263             String s = CopyString(item);
3264             newLibPaths.Add(s);
3265             libPathExists[s] = true;
3266          }
3267       }
3268
3269 #if defined(__APPLE__)
3270       GetEnvironment("DYLD_LIBRARY_PATH", oldList, maxPathLen);
3271 #else
3272       GetEnvironment("LD_LIBRARY_PATH", oldList, maxPathLen);
3273 #endif
3274 /*#ifdef _DEBUG
3275       printf("Old value of [DY]LD_LIBRARY_PATH: %s\n", oldList);
3276 #endif*/
3277       count = TokenizeWith(oldList, sizeof(oldPaths) / sizeof(char *), oldPaths, pathListSep, false);
3278       for(c = 0; c < count; c++)
3279       {
3280          if(!libPathExists[oldPaths[c]])  // fstrcmp should be used
3281          {
3282             String s = CopyString(oldPaths[c]);
3283             newLibPaths.Add(s);
3284             libPathExists[s] = true;
3285          }
3286       }
3287
3288       len = 0;
3289       for(path : newLibPaths)
3290          len += strlen(path) + 1;
3291       newList = new char[len + 1];
3292       newList[0] = '\0';
3293       for(path : newLibPaths)
3294       {
3295          strcat(newList, path);
3296          strcat(newList, pathListSep);
3297       }
3298       newList[len - 1] = '\0';
3299 #if defined(__APPLE__)
3300       SetEnvironment("DYLD_LIBRARY_PATH", newList);
3301 #else
3302       SetEnvironment("LD_LIBRARY_PATH", newList);
3303 #endif
3304 /*#ifdef _DEBUG
3305       printf("New value of [DY]LD_LIBRARY_PATH: %s\n", newList);
3306 #endif*/
3307       delete newList;
3308
3309       newLibPaths.Free();
3310       delete newLibPaths;
3311       delete libPathExists;
3312 #endif
3313
3314       if(compiler.distccEnabled && compiler.distccHosts)
3315          SetEnvironment("DISTCC_HOSTS", compiler.distccHosts);
3316
3317       delete oldList;
3318    }
3319
3320    void DestroyTemporaryProjectDir()
3321    {
3322       if(tmpPrjDir && tmpPrjDir[0])
3323       {
3324          if(FileExists(tmpPrjDir).isDirectory)
3325             DestroyDir(tmpPrjDir);
3326          property::tmpPrjDir = null;
3327       }
3328    }
3329
3330    IDEWorkSpace()
3331    {
3332       // Graphics Driver Menu
3333       int c;
3334
3335       /*
3336       app.currentSkin.selectionColor = selectionColor;
3337       app.currentSkin.selectionText = selectionText;
3338       */
3339
3340 /*
3341       driverItems = new MenuItem[app.numDrivers];
3342       for(c = 0; c < app.numDrivers; c++)
3343       {
3344          driverItems[c] = MenuItem { driversMenu, app.drivers[c], NotifySelect = NotifySelectDisplayDriver };
3345          driverItems[c].id = c;
3346          driverItems[c].isRadio = true;
3347       }
3348 */
3349       driverItems = new MenuItem[2];
3350 #if defined(__unix__)
3351          driverItems[0] = MenuItem { driversMenu, "X", NotifySelect = NotifySelectDisplayDriver };
3352          driverItems[0].id = 0;
3353          driverItems[0].isRadio = true;
3354 #else
3355          driverItems[0] = MenuItem { driversMenu, "GDI", NotifySelect = NotifySelectDisplayDriver };
3356          driverItems[0].id = 0;
3357          driverItems[0].isRadio = true;
3358 #endif
3359          driverItems[1] = MenuItem { driversMenu, "OpenGL", NotifySelect = NotifySelectDisplayDriver };
3360          driverItems[1].id = 1;
3361          driverItems[1].isRadio = true;
3362
3363 /*      skinItems = new MenuItem[app.numSkins];
3364       for(c = 0; c < app.numSkins; c++)
3365       {
3366          skinItems[c] = MenuItem {skinsMenu, app.skins[c], NotifySelect = NotifySelectDisplaySkin };
3367          skinItems[c].id = c;
3368          skinItems[c].isRadio = true;
3369       }
3370 */
3371       ideFileDialog.master = this;
3372       ideProjectFileDialog.master = this;
3373
3374       //SetDriverAndSkin();
3375       return true;
3376    }
3377
3378    void UpdateRecentMenus()
3379    {
3380       int c;
3381       Menu fileMenu = menu.FindMenu($"File");
3382       Menu recentFiles = fileMenu.FindMenu($"Recent Files");
3383       Menu recentProjects = fileMenu.FindMenu($"Recent Projects");
3384       char * itemPath = new char[MAX_LOCATION];
3385       char * itemName = new char[MAX_LOCATION+4];
3386       MenuItem item;
3387
3388       recentFiles.Clear();
3389       c = 0;
3390
3391       for(recent : ideSettings.recentFiles)
3392       {
3393          strncpy(itemPath, recent, MAX_LOCATION); itemPath[MAX_LOCATION-1] = '\0';
3394          MakeSystemPath(itemPath);
3395          snprintf(itemName, MAX_LOCATION+4, "%d %s", 1 + c, itemPath); itemName[MAX_LOCATION+4-1] = '\0';
3396          recentFiles.AddDynamic(MenuItem { copyText = true, text = itemName, (Key)k1 + c, id = c, NotifySelect = ide.FileRecentFile }, ide, true);
3397          c++;
3398       }
3399
3400       recentProjects.Clear();
3401       c = 0;
3402       for(recent : ideSettings.recentProjects)
3403       {
3404          strncpy(itemPath, recent, MAX_LOCATION); itemPath[MAX_LOCATION-1] = '\0';
3405          MakeSystemPath(itemPath);
3406          snprintf(itemName, MAX_LOCATION+4, "%d %s", 1 + c, itemPath); itemName[MAX_LOCATION+4-1] = '\0';
3407          recentProjects.AddDynamic(MenuItem { copyText = true, text = itemName, (Key)k1 + c, id = c, NotifySelect = ide.FileRecentProject }, ide, true);
3408          c++;
3409       }
3410
3411       delete itemPath;
3412       delete itemName;
3413    }
3414
3415    ~IDEWorkSpace()
3416    {
3417       delete driverItems;
3418       delete skinItems;
3419       delete ideSettings;
3420       if(documentor)
3421       {
3422          documentor.Puts("Quit\n");
3423          documentor.Wait();
3424          delete documentor;
3425       }
3426    }
3427 }
3428
3429 void DestroyDir(char * path)
3430 {
3431    RecursiveDeleteFolderFSI fsi { };
3432    fsi.Iterate(path);
3433    delete fsi;
3434 }
3435
3436 #if defined(__WIN32__)
3437 define sdkDirName = "Ecere SDK";
3438 #else
3439 define sdkDirName = "ecere";
3440 #endif
3441
3442 void FindAndShellOpenInstalledFolder(char * name)
3443 {
3444    char * p = new char[MAX_LOCATION];
3445    char * v = new char[maxPathLen];
3446    byte * tokens[256];
3447    int c, numTokens;
3448    Array<String> paths { };
3449    p[0] = v[0] = '\0';
3450    strncpy(p, settingsContainer.moduleLocation, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3451    StripLastDirectory(p, p);
3452    PathCat(p, name);
3453    paths.Add(CopyString(p));
3454 #if defined(__WIN32__)
3455    GetEnvironment("ECERE_SDK_SRC", v, maxPathLen);
3456    if(v[0])
3457    {
3458       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3459       PathCat(p, name); paths.Add(CopyString(p));
3460    }
3461    GetEnvironment("AppData", v, maxPathLen);
3462    if(v[0])
3463    {
3464       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3465       PathCat(p, sdkDirName); PathCat(p, name); paths.Add(CopyString(p));
3466    }
3467    GetEnvironment("ProgramFiles", v, maxPathLen);
3468    if(v[0])
3469    {
3470       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3471       PathCat(p, sdkDirName); PathCat(p, name); paths.Add(CopyString(p));
3472    }
3473    GetEnvironment("ProgramFiles(x86)", v, maxPathLen);
3474    if(v[0])
3475    {
3476       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3477       PathCat(p, sdkDirName); PathCat(p, name); paths.Add(CopyString(p));
3478    }
3479    GetEnvironment("SystemDrive", v, maxPathLen);
3480    if(v[0])
3481    {
3482       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3483       PathCat(p, "Program Files"); PathCat(p, sdkDirName); PathCat(p, name); paths.Add(CopyString(p));
3484    }
3485 #else
3486    GetEnvironment("XDG_DATA_DIRS", v, maxPathLen);
3487    numTokens = TokenizeWith(v, sizeof(tokens) / sizeof(byte *), tokens, ":", false);
3488    for(c=0; c<numTokens; c++)
3489    {
3490       strncpy(p, tokens[c], MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3491       PathCat(p, sdkDirName); PathCat(p, name); paths.Add(CopyString(p));
3492    }
3493 #endif
3494    for(path : paths)
3495    {
3496       strncpy(p, path, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3497       if(FileExists(p).isDirectory)
3498       {
3499          ShellOpen(p);
3500          break;
3501       }
3502    }
3503    delete p;
3504    delete v;
3505    paths.Free();
3506    delete paths;
3507 }
3508
3509 void FindAndShellOpenInstalledFile(char * subdir, char * name)
3510 {
3511    char * p = new char[MAX_LOCATION];
3512    char * v = new char[maxPathLen];
3513    byte * tokens[256];
3514    int c, numTokens;
3515    Array<String> paths { };
3516    p[0] = v[0] = '\0';
3517    strncpy(p, settingsContainer.moduleLocation, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3518    paths.Add(CopyString(p));
3519    StripLastDirectory(p, p);
3520    PathCat(p, subdir);
3521    paths.Add(CopyString(p));
3522 #if defined(__WIN32__)
3523    GetEnvironment("ECERE_SDK_SRC", v, maxPathLen);
3524    if(v[0])
3525    {
3526       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3527       PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
3528    }
3529    GetEnvironment("AppData", v, maxPathLen);
3530    if(v[0])
3531    {
3532       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3533       PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
3534    }
3535    GetEnvironment("ProgramFiles", v, maxPathLen);
3536    if(v[0])
3537    {
3538       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3539       PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
3540    }
3541    GetEnvironment("ProgramFiles(x86)", v, maxPathLen);
3542    if(v[0])
3543    {
3544       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3545       PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
3546    }
3547    GetEnvironment("SystemDrive", v, maxPathLen);
3548    if(v[0])
3549    {
3550       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3551       PathCat(p, "Program Files"); PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
3552    }
3553 #else
3554    GetEnvironment("XDG_DATA_DIRS", v, maxPathLen);
3555    numTokens = TokenizeWith(v, sizeof(tokens) / sizeof(byte *), tokens, ":", false);
3556    for(c=0; c<numTokens; c++)
3557    {
3558       strncpy(p, tokens[c], MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3559       PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
3560    }
3561 #endif
3562    for(path : paths)
3563    {
3564       strncpy(p, path, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3565       PathCat(p, name);
3566       if(FileExists(p).isFile)
3567       {
3568          ShellOpen(p);
3569          break;
3570       }
3571    }
3572    delete p;
3573    delete v;
3574    paths.Free();
3575    delete paths;
3576 }
3577
3578 class RecursiveDeleteFolderFSI : NormalFileSystemIterator
3579 {
3580    bool preserveRootFolder;
3581
3582    void OutFolder(char * folderPath, bool isRoot)
3583    {
3584       if(!(preserveRootFolder && isRoot))
3585          RemoveDir(folderPath);
3586    }
3587
3588    bool OnFile(char * filePath)
3589    {
3590       DeleteFile(filePath);
3591       return true;
3592    }
3593 }
3594
3595 class IDEApp : GuiApplication
3596 {
3597    //driver = "Win32Console";
3598    // driver = "OpenGL";
3599    // skin = "Aqua";
3600    //skin = "TVision";
3601
3602    TempFile includeFile { };
3603
3604    bool Init()
3605    {
3606       char ext[MAX_EXTENSION];
3607       SetLoggingMode(stdOut, null);
3608       //SetLoggingMode(debug, null);
3609
3610       settingsContainer.Load();
3611       if(argc > 1 && !strcmpi(GetExtension(argv[1], ext), "3ds"))
3612       {
3613          app.driver = "OpenGL";
3614          ide.driverItems[1].checked = true;
3615       }
3616       else
3617       {
3618 #if defined(__unix__) || defined(__APPLE__)
3619          app.driver = (ideSettings.displayDriver && !strcmp(ideSettings.displayDriver, "OpenGL")) ? ideSettings.displayDriver : "X";
3620 #else
3621          app.driver = (ideSettings.displayDriver && !strcmp(ideSettings.displayDriver, "OpenGL")) ? ideSettings.displayDriver : "GDI";
3622 #endif
3623          ide.driverItems[ideSettings.displayDriver && !strcmp(ideSettings.displayDriver,"OpenGL")].checked = true;
3624       }
3625
3626       SetInIDE(true);
3627
3628       desktop.text = titleECEREIDE;
3629       /*
3630       int c;
3631       for(c = 1; c<app.argc; c++)
3632       {
3633          char fullPath[MAX_LOCATION];
3634          GetWorkingDir(fullPath, MAX_LOCATION);
3635          PathCat(fullPath, app.argv[c]);
3636          ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, null, yes, normal, false);
3637       }
3638       */
3639
3640       if(!LoadIncludeFile())
3641          PrintLn("error: unable to load :crossplatform.mk file inside ide binary.");
3642
3643       return true;
3644    }
3645
3646    bool Cycle(bool idle)
3647    {
3648       if(ide.documentor)
3649       {
3650          if(ide.documentor.Peek())
3651          {
3652             char line[1024];
3653             ide.documentor.GetLine(line, sizeof(line));
3654             if(!strcmpi(line, "Exited"))
3655             {
3656                ide.documentor.CloseInput();
3657                ide.documentor.CloseOutput();
3658                ide.documentor.Wait();
3659                delete ide.documentor;
3660             }
3661          }
3662          if(ide.documentor && ide.documentor.eof)
3663          {
3664             ide.documentor.CloseInput();
3665             ide.documentor.CloseOutput();
3666             ide.documentor.Wait();
3667             delete ide.documentor;
3668          }
3669       }
3670       return true;
3671    }
3672
3673    bool LoadIncludeFile()
3674    {
3675       bool result = false;
3676       File include = FileOpen(":crossplatform.mk", read);
3677       if(include)
3678       {
3679          File f = includeFile;
3680          if(f)
3681          {
3682             for(; !include.Eof(); )
3683             {
3684                char buffer[4096];
3685                int count = include.Read(buffer, 1, 4096);
3686                f.Write(buffer, 1, count);
3687             }
3688             result = true;
3689          }
3690          delete include;
3691       }
3692       return result;
3693    }
3694 }
3695
3696 IDEMainFrame ideMainFrame { };
3697
3698 define app = ((IDEApp)__thisModule);
3699 #ifdef _DEBUG
3700 define titleECEREIDE = $"ECERE IDE (Debug)";
3701 #else
3702 define titleECEREIDE = $"ECERE IDE";
3703 #endif