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