ide;debugger; (#999) added support for gdb's until and advance commands.
[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 isDebuggerExecuting { 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 active = debugger.isActive;
2076       bool bpNoToggle = isBreakpointTogglingUnavailable;
2077       bool executing = isDebuggerExecuting;
2078       //bool holding = debugger.state == stopped;
2079
2080       debugStartResumeItem.disabled       = unavailable || executing;
2081       debugStartResumeItem.text           = active ? $"Resume" : $"Start";
2082       debugStartResumeItem.NotifySelect   = active ? MenuDebugResume : MenuDebugStart;
2083       if(toolBar)
2084       {
2085          toolBar.buttonDebugStartResume.disabled      = unavailable || executing;
2086          toolBar.buttonDebugStartResume.toolTip       = active ? $"Resume" : $"Start";
2087       }
2088
2089       debugBreakItem.disabled             = unavailable || !executing;
2090       debugStopItem.disabled              = unavailable || !active;
2091       debugRestartItem.disabled           = unavailable || !active;
2092       if(toolBar)
2093       {
2094          toolBar.buttonDebugPause.disabled            = unavailable || !executing;
2095          toolBar.buttonDebugStop.disabled             = unavailable || !active;
2096          toolBar.buttonDebugRestart.disabled          = unavailable || !active;
2097       }
2098
2099       debugStepIntoItem.disabled          = unavailable || executing;
2100       debugStepOverItem.disabled          = unavailable || executing;
2101       debugSkipStepOverItem.disabled      = unavailable || executing;
2102       debugStepOutItem.disabled           = unavailable || executing || !active;
2103       debugSkipStepOutItem.disabled       = unavailable || executing || !active;
2104       if(toolBar)
2105       {
2106          toolBar.buttonDebugStepInto.disabled         = unavailable || executing;
2107          toolBar.buttonDebugStepOver.disabled         = unavailable || executing;
2108          toolBar.buttonDebugSkipStepOver.disabled     = unavailable || executing;
2109          toolBar.buttonDebugStepOut.disabled          = unavailable || executing || !active;
2110          //toolBar.buttonDebugSkipStepOutItem.disabled  = unavailable || executing;
2111       }
2112       if((Designer)GetActiveDesigner())
2113       {
2114          CodeEditor codeEditor = ((Designer)GetActiveDesigner()).codeEditor;
2115          if(codeEditor)
2116             codeEditor.AdjustDebugMenus(unavailable, bpNoToggle, executing, isDebuggerStopped);
2117       }
2118    }
2119
2120    void ChangeFileDialogsDirectory(char * directory, bool saveSettings)
2121    {
2122       char tempString[MAX_LOCATION];
2123       strcpy(tempString, directory);
2124       if(saveSettings && !projectView)
2125       {
2126          ideSettings.ideFileDialogLocation = directory;
2127          settingsContainer.Save();
2128       }
2129
2130       ideFileDialog.currentDirectory = tempString;
2131       codeEditorFileDialog.currentDirectory = tempString;
2132       codeEditorFormFileDialog.currentDirectory = tempString;
2133    }
2134
2135    void ChangeProjectFileDialogDirectory(char * directory)
2136    {
2137       ideSettings.ideProjectFileDialogLocation = directory;
2138       settingsContainer.Save();
2139    }
2140
2141    Window FindWindow(char * filePath)
2142    {
2143       Window document = null;
2144
2145       // TOCHECK: Do we need to change slashes here?
2146       for(document = firstChild; document; document = document.next)
2147       {
2148          char * fileName = document.fileName;
2149          if(document.isDocument && fileName && !fstrcmp(fileName, filePath))
2150          {
2151             document.visible = true;
2152             document.Activate();
2153             return document;
2154          }
2155       }
2156       return null;
2157    }
2158
2159    bool DontTerminateDebugSession(char * title)
2160    {
2161       if(debugger.isActive)
2162       {
2163          if(MessageBox { type = yesNo, master = ide, 
2164                            contents = $"Do you want to terminate the debugging session in progress?", 
2165                            text = title }.Modal() == no)
2166             return true;
2167          /*
2168          MessageBox msg { type = yesNo, master = ide, 
2169                            contents = "Do you want to terminate the debugging session in progress?", 
2170                            text = title };
2171          if(msg.Modal() == no)
2172          {
2173             msg.Destroy(0);
2174             delete msg;
2175             return true;
2176          }
2177          msg.Destroy(0);
2178          delete msg;*/
2179       }
2180       return false;
2181    }
2182
2183    Window OpenFile(char * origFilePath, WindowState state, bool visible, char * type, OpenCreateIfFails createIfFails, OpenMethod openMethod, bool noParsing)
2184    {
2185       char extension[MAX_EXTENSION] = "";
2186       Window document = null;
2187       bool isProject = false;
2188       bool needFileModified = true;
2189       char winFilePath[MAX_LOCATION];
2190       char * filePath = strstr(origFilePath, "http://") == origFilePath ? strcpy(winFilePath, origFilePath) : GetSystemPathBuffer(winFilePath, origFilePath);
2191
2192       if(!type)
2193       {
2194          GetExtension(filePath, extension);
2195          strlwr(extension);
2196       }
2197       else
2198          strcpy(extension, type);
2199
2200       if(strcmp(extension, ProjectExtension))
2201       {
2202          for(document = firstChild; document; document = document.next)
2203          {
2204             char * fileName = document.fileName;
2205             if(document.isDocument && fileName && !fstrcmp(fileName, filePath) && document.created)
2206             {
2207                document.visible = true;
2208                if(visible)
2209                   document.Activate();
2210                return document;
2211             }
2212          }
2213       }
2214
2215       if(createIfFails == whatever)
2216          ;
2217       else if(!strcmp(extension, ProjectExtension) || !strcmp(extension, WorkspaceExtension))
2218       {
2219          needFileModified = false;
2220          if(openMethod == normal)
2221          {
2222             if(DontTerminateDebugSession($"Open Project"))
2223                return null;
2224             isProject = true;
2225             if(ProjectClose())
2226             {
2227                if(!projectView)
2228                {
2229                   for(;;)
2230                   {
2231                      Project project;
2232                      Workspace workspace = null;
2233                      
2234                      if(FileExists(filePath))
2235                      {
2236                         if(!strcmp(extension, ProjectExtension))
2237                         {
2238                            char workspaceFile[MAX_LOCATION];
2239                            strcpy(workspaceFile, filePath);
2240                            ChangeExtension(workspaceFile, WorkspaceExtension, workspaceFile);
2241                            workspace = LoadWorkspace(workspaceFile, filePath);
2242                         }
2243                         else if(!strcmp(extension, WorkspaceExtension))
2244                            workspace = LoadWorkspace(filePath, null);
2245                         else
2246                            return null;
2247                         //project = LoadProject(filePath, null);
2248                      }
2249
2250                      if(workspace)
2251                      {
2252                         char absolutePath[MAX_LOCATION];
2253                         CreateProjectView(workspace, filePath);
2254                         document = projectView;
2255
2256                         workspace.DropInvalidBreakpoints();
2257                         workspace.Save();
2258
2259                         ide.projectView.ShowOutputBuildLog(true);
2260                         {
2261                            CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
2262                            ide.projectView.DisplayCompiler(compiler, false);
2263                            delete compiler;
2264                         }
2265                         UpdateCompilerConfigs(false);
2266                         UpdateMakefiles();
2267                         {
2268                            char newWorkingDir[MAX_LOCATION];
2269                            StripLastDirectory(filePath, newWorkingDir);
2270                            ChangeFileDialogsDirectory(newWorkingDir, false);
2271                         }
2272                         if(document)
2273                            document.fileName = filePath;
2274
2275                         ideMainFrame.SetText("%s - %s", filePath, titleECEREIDE);
2276
2277                         // this crashes on starting ide with epj file, solution please?
2278                         // app.UpdateDisplay();
2279
2280                         workspace.holdTracking = true;
2281                         for(ofi : workspace.openedFiles)
2282                         {
2283                            if(ofi.state != closed)
2284                            {
2285                               Window file = OpenFile(ofi.path, normal, true, null, no, normal, noParsing);
2286                               if(file)
2287                               {
2288                                  char fileName[MAX_LOCATION];
2289                                  ProjectNode node;
2290                                  GetLastDirectory(ofi.path, fileName);
2291                                  node = projectView.project.topNode.Find(fileName, true);
2292                                  if(node)
2293                                     node.EnsureVisible();
2294                               }
2295                            }
2296                         }
2297                         ide.RepositionWindows(false);
2298                         workspace.holdTracking = false;
2299
2300                         workspace.timer.Start();
2301
2302 #if !defined(__WIN32__)
2303                         // Valgrind Debug menu updates
2304                         debugUseValgrindItem.checked = workspace.useValgrind;
2305
2306                         debugValgrindNoLeakCheckItem.checked      = workspace.vgLeakCheck == no;
2307                         debugValgrindSummaryLeakCheckItem.checked = workspace.vgLeakCheck == summary;
2308                         debugValgrindYesLeakCheckItem.checked     = workspace.vgLeakCheck == yes;
2309                         debugValgrindFullLeakCheckItem.checked    = workspace.vgLeakCheck == full;
2310
2311                         debugValgrindRSDefaultItem.checked = workspace.vgRedzoneSize == -1;
2312                         debugValgrindRS0Item.checked       = workspace.vgRedzoneSize == 0;
2313                         debugValgrindRS16Item.checked      = workspace.vgRedzoneSize == 16;
2314                         debugValgrindRS32Item.checked      = workspace.vgRedzoneSize == 32;
2315                         debugValgrindRS64Item.checked      = workspace.vgRedzoneSize == 64;
2316                         debugValgrindRS128Item.checked     = workspace.vgRedzoneSize == 128;
2317                         debugValgrindRS256Item.checked     = workspace.vgRedzoneSize == 256;
2318                         debugValgrindRS512Item.checked     = workspace.vgRedzoneSize == 512;
2319
2320                         debugValgrindTrackOriginsItem.checked = workspace.vgTrackOrigins;
2321 #endif
2322
2323                         findInFilesDialog.mode = FindInFilesMode::project;
2324                         findInFilesDialog.currentDirectory = ide.project.topNode.path;
2325                         
2326                         {
2327                            char location[MAX_LOCATION];
2328                            StripLastDirectory(ide.project.topNode.path, location);
2329                            ChangeProjectFileDialogDirectory(location);
2330                         }
2331                         
2332                         /*
2333                         if(projectView.debugger)
2334                            projectView.debugger.EvaluateWatches();
2335                         */
2336                         
2337                         break;
2338                      }
2339                      else 
2340                      {
2341                         if(MessageBox { type = yesNo, master = this, text = $"Error opening project", contents = $"Open a different project?" }.Modal() == yes)
2342                         {
2343                            ideProjectFileDialog.text = openProjectFileDialogTitle;
2344                            if(ideProjectFileDialog.Modal() == cancel)
2345                               return null;
2346                            filePath = ideProjectFileDialog.filePath;
2347                            GetExtension(filePath, extension);
2348                         }
2349                         else
2350                            return null;
2351                      }
2352                   }
2353                }
2354             }
2355             else
2356                return null;
2357          }
2358          else if(openMethod == add)
2359          {
2360             if(workspace)
2361             {
2362                Project prj = null;
2363                char slashFilePath[MAX_LOCATION];
2364                GetSlashPathBuffer(slashFilePath, filePath);
2365                for(p : workspace.projects)
2366                {
2367                   if(!fstrcmp(p.filePath, slashFilePath))
2368                   {
2369                      prj = p;
2370                      break;
2371                   }
2372                }
2373                if(prj)
2374                {
2375                   MessageBox { type = ok, parent = parent, master = this, text = $"Same Project", 
2376                         contents = $"This project is already present in workspace." }.Modal();
2377                }
2378                else
2379                {
2380                   prj = LoadProject(filePath, null);
2381                   if(prj)
2382                   {
2383                      char * activeConfigName = null;
2384                      CompilerConfig compiler = ideSettings.GetCompilerConfig(workspace.compiler);
2385                      prj.StartMonitoring();
2386                      workspace.projects.Add(prj);
2387                      if(toolBar.activeConfig.currentRow && toolBar.activeConfig.currentRow != toolBar.activeConfig.firstRow &&
2388                            toolBar.activeConfig.currentRow.string && toolBar.activeConfig.currentRow.string[0])
2389                         activeConfigName = toolBar.activeConfig.currentRow.string;
2390                      if(activeConfigName)
2391                      {
2392                         for(cfg : prj.configurations)
2393                         {
2394                            if(cfg.name && !strcmp(cfg.name, activeConfigName))
2395                            {
2396                               prj.config = cfg;
2397                               break;
2398                            }
2399                         }
2400                      }
2401                      if(projectView)
2402                         projectView.AddNode(prj.topNode, null);
2403                      workspace.modified = true;
2404                      workspace.Save();
2405                      findInFilesDialog.AddProjectItem(prj);
2406                      projectView.ShowOutputBuildLog(true);
2407                      projectView.DisplayCompiler(compiler, false);
2408                      projectView.ProjectUpdateMakefileForAllConfigs(prj);
2409                      delete compiler;
2410
2411                      {
2412                         char location[MAX_LOCATION];
2413                         StripLastDirectory(prj.topNode.path, location);
2414                         ChangeProjectFileDialogDirectory(location);
2415                      }
2416
2417                      // projectView is associated with the main project and not with the one just added but
2418                      return projectView; // just to let the caller know something was opened
2419                   }
2420                }
2421             }
2422             else
2423                return null;
2424          }
2425       }
2426       else if(!strcmp(extension, "bmp") || !strcmp(extension, "pcx") ||
2427             !strcmp(extension, "jpg") || !strcmp(extension, "gif") ||
2428             !strcmp(extension, "jpeg") || !strcmp(extension, "png"))
2429       {
2430          if(FileExists(filePath))
2431             document = PictureEdit { hasMaximize = true, hasMinimize = true, hasClose = true, borderStyle = sizable, 
2432                                        hasVertScroll = true, hasHorzScroll = true, parent = this, state = state, 
2433                                        visible = visible, bitmapFile = filePath, OnClose = PictureEditOnClose/*why?--GenericDocumentOnClose*/;
2434                                     };
2435          if(!document)
2436             MessageBox { type = ok, master = this, text = filePath, contents = $"File doesn't exist." }.Modal();
2437       }
2438 #ifndef NO3D
2439       else if(!strcmp(extension, "3ds"))
2440       {
2441          if(FileExists(filePath))
2442             document = ModelView { hasMaximize = true, hasMinimize = true, hasClose = true, borderStyle = sizable, 
2443                                     hasVertScroll = true, hasHorzScroll = true, parent = this, state = state, 
2444                                     visible = visible, modelFile = filePath, OnClose = ModelViewOnClose/*why?--GenericDocumentOnClose*/
2445                                     };
2446
2447          if(!document)
2448             MessageBox { type = ok, master = this, text = filePath, contents = $"File doesn't exist." }.Modal();
2449       }
2450 #endif
2451       else if(!strcmp(extension, "txt") || !strcmp(extension, "text") ||
2452             !strcmp(extension, "nfo") || !strcmp(extension, "info") ||
2453             !strcmp(extension, "htm") || !strcmp(extension, "html") ||
2454             !strcmp(extension, "css") || !strcmp(extension, "php") ||
2455             !strcmp(extension, "js"))
2456       {
2457          CodeEditor editor { parent = this, state = state, visible = false, noParsing = noParsing };
2458          editor.updatingCode = true;
2459          if(editor.LoadFile(filePath))
2460          {
2461             document = editor;
2462             editor.visible = true;
2463          }
2464          else
2465             delete editor;
2466          needFileModified = false;
2467       }
2468       else
2469       {
2470          CodeEditor editor { parent = this, state = state, visible = false, noParsing = noParsing };
2471          if(editor.LoadFile(filePath))
2472          {
2473             document = editor;
2474             editor.visible = true;
2475          }
2476          else
2477             delete editor;
2478          needFileModified = false;
2479       }
2480
2481       if(document && (document._class == class(PictureEdit) ||
2482             document._class == class(ModelView)))
2483       {
2484          document.Create();
2485          if(document)
2486          {
2487             document.fileName = filePath;
2488             if(workspace && !workspace.holdTracking)
2489                workspace.UpdateOpenedFileInfo(filePath, opened);
2490          }
2491       }
2492       
2493       if(!document && createIfFails != no)
2494       {
2495          if(createIfFails != yes && !needFileModified && 
2496                MessageBox { type = yesNo, master = this, text = filePath, contents = $"File doesn't exist. Create?" }.Modal() == yes)
2497             createIfFails = yes;
2498          if(createIfFails == yes || createIfFails == whatever)
2499          {
2500             document = (Window)NewCodeEditor(this, state, true);
2501             if(document)
2502                document.fileName = filePath;
2503          }
2504       }
2505
2506       if(document)
2507       {
2508          if(projectView && document._class == class(CodeEditor) && workspace)
2509          {
2510             int lineNumber, position;
2511             Point scroll;
2512             CodeEditor editor = (CodeEditor)document;
2513             editor.openedFileInfo = workspace.UpdateOpenedFileInfo(filePath, opened);
2514             editor.openedFileInfo.holdTracking = true;
2515             lineNumber = Max(editor.openedFileInfo.lineNumber - 1, 0);
2516             position = Max(editor.openedFileInfo.position - 1, 0);
2517             editor.editBox.GoToLineNum(lineNumber);
2518             editor.editBox.GoToPosition(editor.editBox.line, lineNumber, position);
2519             scroll.x = Max(editor.openedFileInfo.scroll.x, 0);
2520             scroll.y = Max(editor.openedFileInfo.scroll.y, 0);
2521             editor.editBox.scroll = scroll;
2522             editor.openedFileInfo.holdTracking = false;
2523          }
2524          
2525          if(needFileModified)
2526             document.OnFileModified = OnFileModified;
2527          document.NotifySaved = DocumentSaved;
2528          
2529          if(isProject)
2530             ideSettings.AddRecentProject(document.fileName);
2531          else
2532             ideSettings.AddRecentFile(document.fileName);
2533          ide.UpdateRecentMenus();
2534          ide.AdjustFileMenus();
2535          settingsContainer.Save();
2536          
2537          return document;
2538       }
2539       else
2540          return null;
2541    }
2542
2543    // TOCHECK: I can't use a generic one for both ModelView and PictureEdit both derived from Window
2544    /*bool Window::GenericDocumentOnClose(bool parentClosing)
2545    {
2546       if(!parentClosing && ide.workspace)
2547          ide.workspace.UpdateOpenedFileInfo(fileName, unknown);
2548       return true;
2549    }*/
2550    bool ModelView::ModelViewOnClose(bool parentClosing)
2551    {
2552       if(!parentClosing && ide.workspace)
2553          ide.workspace.UpdateOpenedFileInfo(fileName, unknown);
2554       return true;
2555    }
2556    bool PictureEdit::PictureEditOnClose(bool parentClosing)
2557    {
2558       if(!parentClosing && ide.workspace)
2559          ide.workspace.UpdateOpenedFileInfo(fileName, unknown);
2560       return true;
2561    }
2562
2563    /*
2564    void OnUnloadGraphics(Window window)
2565    {
2566       display.ClearMaterials();
2567       display.ClearTextures();
2568       display.ClearMeshes();
2569    }
2570    */
2571
2572    bool OnActivate(bool active, Window swap, bool * goOnWithActivation, bool direct)
2573    {
2574       caps.color = app.GetKeyState(capsState) ? black : Color { 128,128,128 };
2575       num.color = app.GetKeyState(numState) ? black : Color { 128,128,128 };
2576       return true;
2577    }
2578
2579    bool OnKeyDown(Key key, unichar ch)
2580    {
2581       switch(key)
2582       {
2583          case b:
2584             projectView.Update(null);
2585             break;
2586          case capsLock:
2587             caps.color = app.GetKeyState(capsState) ? black : Color { 128,128,128 };
2588             break;
2589          case numLock:
2590             num.color = app.GetKeyState(numState) ? black : Color { 128,128,128 };
2591             break;
2592       }
2593       return true;
2594    }
2595
2596    void GoToError(const char * line, bool noParsing)
2597    {
2598       if(projectView)
2599          projectView.GoToError(line, noParsing);
2600    }
2601
2602    void CodeLocationParseAndGoTo(const char * text, Project project, const char * dir)
2603    {
2604       char *s = null;
2605       char *path = text;
2606       char *colon = strchr(text, ':');
2607       char filePath[MAX_LOCATION] = "";
2608       char completePath[MAX_LOCATION];
2609       int line = 0, col = 0;
2610       int len = strlen(text);
2611       Project prj = null;
2612       FileAttribs fileAttribs;
2613
2614       // support for valgrind output
2615       if((s = strstr(text, "==")) && (s = strstr(s+2, "==")) && (s = strstr(s+2, ":")) && (s = strstr(s+1, ":")))
2616       {
2617          colon = s;
2618          for(; s>text; s--)
2619          {
2620             if(*s == '(')
2621             {
2622                path = s+1;
2623                break;
2624             }
2625          }
2626          /*for(s=colon; *s; s++)
2627          {
2628             if(*s == ')')
2629             {
2630                *s = '\0';;
2631                break;
2632             }
2633          }*/
2634          //*colon = '\0';
2635          //line = atoi(colon+1);
2636       }
2637       // support for "Found n match(es) in "file/path";
2638       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)
2639       {
2640          path = s+1;
2641       }
2642       else
2643       {
2644          if(colon && (colon[1] == '/' || colon[1] == '\\'))
2645          {
2646             path = (colon - 1 > path) ? colon - 1 : path;
2647             colon = strstr(colon + 1, ":");
2648          }
2649          if(*path == '*' && (s = strchr(path+1, '*')))
2650             path = s+1;
2651          while(isspace(*path)) path++;
2652       }
2653       if(*path == '(')
2654       {
2655          char * close = strchr(path, ')');
2656          if(close)
2657          {
2658             char name[256];
2659             strncpy(name, path+1, close - path - 1);
2660             name[close - path - 1] = '\0';
2661             for(p : ide.workspace.projects)
2662             {
2663                if(!strcmp(p.name, name))
2664                {
2665                   path = close + 1;
2666                   prj = p;
2667                   break;
2668                }
2669             }
2670          }
2671       }
2672       if(!prj)
2673          prj = project ? project : (dir ? null : ide.project);
2674       if(colon)
2675       {
2676          strncpy(filePath, path, colon - path);
2677          filePath[colon - path] = '\0';
2678          line = atoi(colon + 1);
2679          colon = strstr(colon + 1, ":");
2680          if(colon)
2681             col = atoi(colon + 1);
2682       }
2683       else if(path - 1 >= text && *(path - 1) == '\"')
2684       {
2685          colon = strchr(path, '\"');
2686          if(colon)
2687          {
2688             strncpy(filePath, path, colon - path);
2689             filePath[colon - path] = '\0';
2690          }
2691       }
2692       else if(path && !colon)
2693       {
2694          strcpy(filePath, path);
2695       }
2696
2697       if(filePath[0])
2698       {
2699          if(prj)
2700             strcpy(completePath, prj.topNode.path);
2701          else if(dir && dir[0])
2702             strcpy(completePath, dir);
2703          else
2704             completePath[0] = '\0';
2705          PathCat(completePath, filePath);
2706
2707          if((fileAttribs = FileExists(completePath)))
2708             CodeLocationGoTo(completePath, fileAttribs, line, col);
2709          else if(ide.workspace)
2710          {
2711             bool done = false;
2712             for(p : ide.workspace.projects)
2713             {
2714                strcpy(completePath, p.topNode.path);
2715                PathCat(completePath, filePath);
2716                if((fileAttribs = FileExists(completePath)).isFile)
2717                {
2718                   CodeLocationGoTo(completePath, fileAttribs, line, col);
2719                   done = true;
2720                   break;
2721                }
2722             }
2723             if(!done)
2724             {
2725                for(p : ide.workspace.projects)
2726                {
2727                   ProjectNode node = p.topNode.Find(filePath, false);
2728                   if(node)
2729                   {
2730                      node.GetFullFilePath(completePath);
2731                      if((fileAttribs = FileExists(completePath)).isFile)
2732                      {
2733                         CodeLocationGoTo(completePath, fileAttribs, line, col);
2734                         break;
2735                      }
2736                   }
2737                }
2738             }
2739          }
2740       }
2741    }
2742
2743    void CodeLocationGoTo(const char * path, const FileAttribs fileAttribs, int line, int col)
2744    {
2745       if(fileAttribs.isFile)
2746       {
2747          char ext[MAX_EXTENSION];
2748          GetExtension(path, ext);
2749          if(!strcmp(ext, "mp3") || !strcmp(ext, "flac") || !strcmp(ext, "ogg") || !strcmp(ext, "avi") || !strcmp(ext, "mkv"))
2750             ShellOpen(path);
2751          else if(!strcmp(ext, "a") || !strcmp(ext, "o") || !strcmp(ext, "lib") || !strcmp(ext, "dll") || !strcmp(ext, "exe"))
2752          {
2753             char dirPath[MAX_LOCATION];
2754             StripLastDirectory(path, dirPath);
2755             ShellOpen(dirPath);
2756          }
2757          else
2758          {
2759             CodeEditor codeEditor = (CodeEditor)OpenFile(path, normal, true, ext, no, normal, false);
2760             if(codeEditor && line)
2761             {
2762                EditBox editBox = codeEditor.editBox;
2763                editBox.GoToLineNum(line - 1);
2764                editBox.GoToPosition(editBox.line, line - 1, col ? (col - 1) : 0);
2765             }
2766          }
2767       }
2768       else if(fileAttribs.isDirectory)
2769          ShellOpen(path);
2770    }
2771
2772    void OnRedraw(Surface surface)
2773    {
2774       Bitmap bitmap = back.bitmap;
2775       if(bitmap)
2776          surface.Blit(bitmap, (clientSize.w - bitmap.width) / 2, (clientSize.h - bitmap.height) / 2, 0, 0, bitmap.width, bitmap.height);
2777    }
2778
2779    void SheetSelected(SheetType sheetSelected)
2780    {
2781       if(activeChild == sheet)
2782       {
2783          if(sheetSelected == methods)
2784          {
2785             viewPropertiesItem.accelerator = f4;
2786             viewPropertiesItem.parent = viewMenu;
2787             viewMethodsItem.parent = null;
2788          }
2789          else
2790          {
2791             viewMethodsItem.accelerator = f4;
2792             viewMethodsItem.parent = viewMenu;
2793             viewPropertiesItem.parent = null;
2794          }
2795       }
2796       else
2797       {
2798          viewMethodsItem.parent = viewMenu;
2799          viewPropertiesItem.parent = viewMenu;
2800          if(sheetSelected == methods)
2801          {
2802             viewMethodsItem.accelerator = f4;
2803             viewPropertiesItem.accelerator = 0;
2804          }
2805          else
2806          {
2807             viewMethodsItem.accelerator = 0;
2808             viewPropertiesItem.accelerator = f4;
2809          }
2810       }
2811    }
2812
2813    void OnActivateClient(Window client, Window previous)
2814    {
2815       //if(!client || client != previous)
2816       {
2817          Class dataType;
2818          if(!client || client != previous)
2819          {
2820             if(previous)
2821                dataType = previous._class;
2822             if(previous && !strcmp(dataType.name, "CodeEditor"))
2823             {
2824                ((CodeEditor)previous).UpdateFormCode();
2825             }
2826             else if(previous && !strcmp(dataType.name, "Designer"))
2827             {
2828                ((Designer)previous).codeEditor.UpdateFormCode();
2829             }
2830          }
2831
2832          if(client)
2833             dataType = client._class;
2834          if(client && !strcmp(dataType.name, "CodeEditor"))
2835          {
2836             CodeEditor codeEditor = (CodeEditor)client;
2837             SetPrivateModule(codeEditor.privateModule);
2838             SetCurrentContext(codeEditor.globalContext);
2839             SetTopContext(codeEditor.globalContext);
2840             SetGlobalContext(codeEditor.globalContext);
2841             
2842             SetDefines(&codeEditor.defines);
2843             SetImports(&codeEditor.imports);
2844
2845             SetActiveDesigner(codeEditor.designer);
2846             
2847             sheet.codeEditor = codeEditor;
2848             toolBox.codeEditor = codeEditor;
2849
2850             viewDesignerItem.parent = viewMenu;
2851             if(activeChild != codeEditor)
2852             {
2853                viewCodeItem.parent = viewMenu;
2854                viewDesignerItem.accelerator = 0;
2855                viewCodeItem.accelerator = f8;
2856             }
2857             else
2858             {
2859                viewCodeItem.parent = null;
2860                viewDesignerItem.accelerator = f8;
2861             }
2862          }
2863          else if(client && !strcmp(dataType.name, "Designer"))
2864          {
2865             CodeEditor codeEditor = ((Designer)client).codeEditor;
2866             if(codeEditor)
2867             {
2868                SetPrivateModule(codeEditor.privateModule);
2869                SetCurrentContext(codeEditor.globalContext);
2870                SetTopContext(codeEditor.globalContext);
2871                SetGlobalContext(codeEditor.globalContext);
2872                SetDefines(&codeEditor.defines);
2873                SetImports(&codeEditor.imports);
2874             }
2875             else
2876             {
2877                SetPrivateModule(null);
2878                SetCurrentContext(null);
2879                SetTopContext(null);
2880                SetGlobalContext(null);
2881                SetDefines(null);
2882                SetImports(null);
2883             }
2884
2885             SetActiveDesigner((Designer)client);
2886
2887             sheet.codeEditor = codeEditor;
2888             toolBox.codeEditor = codeEditor;
2889
2890             viewCodeItem.parent = viewMenu;
2891             if(activeChild != client)
2892             {
2893                viewDesignerItem.parent = viewMenu;
2894                viewDesignerItem.accelerator = f8;
2895                viewCodeItem.accelerator = 0;
2896             }
2897             else
2898             {
2899                viewDesignerItem.parent = null;
2900                viewCodeItem.accelerator = f8;
2901             }
2902          }
2903          else
2904          {
2905             if(sheet)
2906                sheet.codeEditor = null;
2907             toolBox.codeEditor = null;
2908             SetActiveDesigner(null);
2909
2910             viewDesignerItem.parent = null;
2911             viewCodeItem.parent = null;
2912          }
2913          if(sheet)
2914             SheetSelected(sheet.sheetSelected);
2915       }
2916
2917       projectCompileItem = null;
2918
2919       if(statusBar)
2920       {
2921          statusBar.Clear();
2922          if(client && client._class == eSystem_FindClass(__thisModule, "CodeEditor")) // !strcmp(client._class.name, "CodeEditor")
2923          {
2924             CodeEditor codeEditor = (CodeEditor)client;
2925             EditBox editBox = codeEditor.editBox;
2926
2927             statusBar.AddField(pos);
2928
2929             caps = { width = 40, text = $"CAPS", color = app.GetKeyState(capsState) ? black : Color { 128, 128, 128 } };
2930             statusBar.AddField(caps);
2931
2932             ovr = { width = 30, text = $"OVR", color = (editBox && editBox.overwrite) ? black : Color { 128, 128, 128 } };
2933             statusBar.AddField(ovr);
2934
2935             num = { width = 30, text = $"NUM", color = app.GetKeyState(numState) ? black : Color { 128, 128, 128 } };
2936             statusBar.AddField(num);
2937
2938             //statusBar.text = "Ready";
2939
2940             if(projectView && projectView.project)
2941             {
2942                bool isCObject = false;
2943                ProjectNode node = projectView.GetNodeFromWindow(client, null, false, false);
2944                if(!node && (node = projectView.GetNodeFromWindow(client, null, false, true)))
2945                   isCObject = true;
2946                if(node)
2947                {
2948                   char nodeName[MAX_FILENAME];
2949                   char name[MAX_FILENAME+96];
2950                   if(isCObject)
2951                      ChangeExtension(node.name, "c", nodeName);
2952                   sprintf(name, $"Compile %s", isCObject ? nodeName : node.name);
2953                   projectCompileItem = 
2954                   {
2955                      copyText = true, text = name, c, ctrlF7, disabled = projectView.buildInProgress;
2956
2957                      bool NotifySelect(MenuItem selection, Modifiers mods)
2958                      {
2959                         if(projectView)
2960                         {
2961                            bool result = false;
2962                            bool isCObject = false;
2963                            ProjectNode node = null;
2964                            for(p : ide.workspace.projects)
2965                            {
2966                               node = projectView.GetNodeFromWindow(activeClient, p, true, false);
2967                               if(node) break;
2968                            }
2969                            if(!node && (node = projectView.GetNodeFromWindow(activeClient, null, true, true)))
2970                               isCObject = true;
2971                            if(node)
2972                            {
2973                               List<ProjectNode> nodes { };
2974                               nodes.Add(node);
2975                               projectView.Compile(node.project, nodes, mods.ctrl && mods.shift, isCObject ? cObject : normal);
2976                               delete nodes;
2977                               result = true;
2978                            }
2979                            if(!result && node)
2980                               ide.outputView.buildBox.Logf($"File %s is excluded from current build configuration.\n", node.name);
2981                         }
2982                         return true;
2983                      }
2984                   };
2985                   projectMenu.AddDynamic(projectCompileItem, ide, false);
2986                }
2987             }
2988          }
2989          else
2990          {
2991             caps = ovr = num = null;
2992          }
2993       }
2994    }
2995
2996    bool OnClose(bool parentClosing)
2997    {
2998       //return !projectView.buildInProgress;
2999       if(projectView && projectView.buildInProgress)
3000          return false;
3001       if(DontTerminateDebugSession($"Close IDE"))
3002          return false;
3003       if(findInFilesDialog)
3004          findInFilesDialog.SearchStop();
3005       if(workspace)
3006       {
3007          workspace.timer.Stop();
3008          workspace.Save();
3009       }
3010       ideMainFrame.Destroy(0);
3011       return true;
3012    }
3013
3014    bool OnPostCreate()
3015    {
3016       int c;
3017       bool passThrough = false;
3018       bool debugStart = false;
3019       bool debugWorkDir = false;
3020       char * passDebugWorkDir = null;
3021       bool openAsText = false;
3022       DynamicString passArgs { };
3023       int ptArg = 0;
3024
3025       for(c = 1; c<app.argc; c++)
3026       {
3027          if(passThrough)
3028          {
3029             char * arg = app.argv[c];
3030             char * buf = new char[strlen(arg)*2+1];
3031             if(ptArg++ > 0)
3032                passArgs.concat(" ");
3033             PassArg(buf, arg);
3034             passArgs.concat(buf);
3035             delete buf;
3036          }
3037          else if(debugWorkDir)
3038          {
3039             passDebugWorkDir = CopyString(app.argv[c]);
3040             StripQuotes(passDebugWorkDir, passDebugWorkDir);
3041             debugWorkDir = false;
3042          }
3043          else if(!strcmp(app.argv[c], "-t"))
3044             openAsText = true;
3045          else if(!strcmp(app.argv[c], "-no-parsing"))
3046             ide.noParsing = true;
3047          else if(!strcmp(app.argv[c], "-debug-start"))
3048             debugStart = true;
3049          else if(!strcmp(app.argv[c], "-debug-work-dir"))
3050             debugWorkDir = true;
3051          else if(!strcmp(app.argv[c], "-@"))
3052             passThrough = true;
3053          else
3054          {
3055             char fullPath[MAX_LOCATION];
3056             char parentPath[MAX_LOCATION];
3057             char ext[MAX_EXTENSION];
3058             bool isProject;
3059             FileAttribs dirAttribs;
3060             GetWorkingDir(fullPath, MAX_LOCATION);
3061             PathCat(fullPath, app.argv[c]);
3062             StripLastDirectory(fullPath, parentPath);
3063             GetExtension(app.argv[c], ext);
3064             isProject = !openAsText && !strcmpi(ext, "epj");
3065
3066             if(isProject && c > (debugStart ? 2 : 1)) continue;
3067
3068             // Create directory for projects (only)
3069             if(((dirAttribs = FileExists(parentPath)) && dirAttribs.isDirectory) || isProject)
3070             {
3071                if(isProject && !FileExists(fullPath))
3072                {
3073                   char name[MAX_LOCATION];
3074                   NewProjectDialog newProjectDialog;
3075
3076                   if(projectView)
3077                   {
3078                      projectView.visible = false;
3079                      if(!projectView.Destroy(0))
3080                         return true;
3081                   }
3082
3083                   newProjectDialog = { master = this };
3084
3085                   strcpy(name, app.argv[c]);
3086                   StripExtension(name);
3087                   GetLastDirectory(name, name);
3088                   newProjectDialog.projectName.contents = name;
3089                   newProjectDialog.projectName.NotifyModified(newProjectDialog, newProjectDialog.projectName);
3090                   newProjectDialog.locationEditBox.path = parentPath;
3091                   newProjectDialog.NotifyModifiedLocation(newProjectDialog.locationEditBox);
3092
3093                   incref newProjectDialog;
3094                   newProjectDialog.Modal();
3095                   if(projectView)
3096                   {
3097                      ideSettings.AddRecentProject(projectView.fileName);
3098                      ide.UpdateRecentMenus();
3099                      settingsContainer.Save();
3100                   }
3101                   delete newProjectDialog;
3102                   // Open only one project
3103                   break;
3104                }
3105                else
3106                   ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, openAsText ? "txt" : null, yes, normal, false);
3107             }
3108             else if(strstr(fullPath, "http://") == fullPath)
3109                ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, openAsText ? "txt" : null, yes, normal, false);
3110          }
3111       }
3112       if(passThrough && projectView && projectView.project && workspace)
3113          workspace.commandLineArgs = passArgs;
3114       if(passDebugWorkDir && projectView && projectView.project && workspace)
3115       {
3116          workspace.debugDir = passDebugWorkDir;
3117          delete passDebugWorkDir;
3118       }
3119       if(debugStart)
3120          ;//MenuDebugStart(debugStartResumeItem, 0); // <-- how TODO this without getting into the app.Wait lock
3121
3122       UpdateToolBarActiveConfigs(false);
3123       UpdateToolBarActiveCompilers();
3124       delete passArgs;
3125       return true;
3126    }
3127
3128    void OnDestroy()
3129    {
3130       // IS THIS NEEDED? WASN'T HERE BEFORE...  Crashes on getting node's projectView otherwise
3131       if(projectView)
3132       {
3133          projectView.visible = false;
3134          projectView.Destroy(0);
3135          projectView = null;
3136       }
3137 #ifdef GDB_DEBUG_GUI
3138       gdbDialog.Destroy(0);
3139       delete gdbDialog;
3140 #endif
3141    }
3142
3143    void SetPath(bool projectsDirs, CompilerConfig compiler, ProjectConfig config, int bitDepth)
3144    {
3145       int c, len, count;
3146       char * newList;
3147       char * oldPaths[128];
3148       String oldList = new char[maxPathLen];
3149       Array<String> newExePaths { };
3150       //Map<String, bool> exePathExists { };
3151       bool found = false;
3152 #if defined(__unix__) || defined(__APPLE__)
3153       Array<String> newLibPaths { };
3154       Map<String, bool> libPathExists { };
3155 #endif
3156
3157       if(projectsDirs)
3158       {
3159          for(prj : workspace.projects)
3160          {
3161             DirExpression targetDirExp;
3162
3163             // SKIP FIRST PROJECT...
3164             if(prj == workspace.projects.firstIterator.data) continue;
3165
3166             // NOTE: Right now the additional project config dir will be
3167             //       obtained when the debugger is started, so toggling it
3168             //       while building will change which library gets used.
3169             //       To go with the initial state, e.g. when F5 was pressed,
3170             //       we nould need to keep a list of all project's active
3171             //       config upon startup.
3172             targetDirExp = prj.GetTargetDir(compiler, prj.config, bitDepth);
3173
3174             /*if(prj.config.targetType == sharedLibrary && prj.config.debug)
3175                cfg = prj.config;
3176             else
3177             {
3178                for(cfg = prj.configurations.first; cfg; cfg = cfg.next)
3179                   if(cfg.targetType == sharedLibrary && cfg.debug && !strcmpi(cfg.name, "Debug"))
3180                      break;
3181                if(!cfg)
3182                {
3183                   for(cfg = prj.configurations.first; cfg; cfg = cfg.next)
3184                      if(cfg.targetType == sharedLibrary && cfg.debug)
3185                         break;
3186                }
3187             }*/
3188             if(targetDirExp.dir)
3189             {
3190                char buffer[MAX_LOCATION];
3191 #if defined(__WIN32__)
3192                Array<String> paths = newExePaths;
3193 #else
3194                Array<String> paths = newLibPaths;
3195 #endif
3196                GetSystemPathBuffer(buffer, prj.topNode.path);
3197                PathCat(buffer, targetDirExp.dir);
3198                for(p : paths)
3199                {
3200                   if(!fstrcmp(p, buffer))
3201                   {
3202                      found = true;
3203                      break;
3204                   }
3205                }
3206                if(!found)
3207                   paths.Add(CopyString(buffer));
3208             }
3209             delete targetDirExp;
3210          }
3211       }
3212
3213       for(item : compiler.executableDirs)
3214       {
3215          found = false;
3216          for(p : newExePaths)
3217          {
3218             if(!fstrcmp(p, item))
3219             {
3220                found = true;
3221                break;
3222             }
3223          }
3224          if(!found)
3225             newExePaths.Add(CopySystemPath(item));
3226       }
3227
3228       GetEnvironment("PATH", oldList, maxPathLen);
3229 /*#ifdef _DEBUG
3230       printf("Old value of PATH: %s\n", oldList);
3231 #endif*/
3232       count = TokenizeWith(oldList, sizeof(oldPaths) / sizeof(char *), oldPaths, pathListSep, false);
3233       for(c = 0; c < count; c++)
3234       {
3235          found = false;
3236          for(p : newExePaths)
3237          {
3238             if(!fstrcmp(p, oldPaths[c]))
3239             {
3240                found = true;
3241                break;
3242             }
3243          }
3244          if(!found)
3245             newExePaths.Add(CopySystemPath(oldPaths[c]));
3246       }
3247
3248       len = 0;
3249       for(path : newExePaths)
3250          len += strlen(path) + 1;
3251       newList = new char[len + 1];
3252       newList[0] = '\0';
3253       for(path : newExePaths)
3254       {
3255          strcat(newList, path);
3256          strcat(newList, pathListSep);
3257       }
3258       newList[len - 1] = '\0';
3259       SetEnvironment("PATH", newList);
3260 /*#ifdef _DEBUG
3261       printf("New value of PATH: %s\n", newList);
3262 #endif*/
3263       delete newList;
3264
3265       newExePaths.Free();
3266       delete newExePaths;
3267
3268 #if defined(__unix__) || defined(__APPLE__)
3269
3270       for(item : compiler.libraryDirs)
3271       {
3272          if(!libPathExists[item])  // fstrcmp should be used
3273          {
3274             String s = CopyString(item);
3275             newLibPaths.Add(s);
3276             libPathExists[s] = true;
3277          }
3278       }
3279
3280 #if defined(__APPLE__)
3281       GetEnvironment("DYLD_LIBRARY_PATH", oldList, maxPathLen);
3282 #else
3283       GetEnvironment("LD_LIBRARY_PATH", oldList, maxPathLen);
3284 #endif
3285 /*#ifdef _DEBUG
3286       printf("Old value of [DY]LD_LIBRARY_PATH: %s\n", oldList);
3287 #endif*/
3288       count = TokenizeWith(oldList, sizeof(oldPaths) / sizeof(char *), oldPaths, pathListSep, false);
3289       for(c = 0; c < count; c++)
3290       {
3291          if(!libPathExists[oldPaths[c]])  // fstrcmp should be used
3292          {
3293             String s = CopyString(oldPaths[c]);
3294             newLibPaths.Add(s);
3295             libPathExists[s] = true;
3296          }
3297       }
3298
3299       len = 0;
3300       for(path : newLibPaths)
3301          len += strlen(path) + 1;
3302       newList = new char[len + 1];
3303       newList[0] = '\0';
3304       for(path : newLibPaths)
3305       {
3306          strcat(newList, path);
3307          strcat(newList, pathListSep);
3308       }
3309       newList[len - 1] = '\0';
3310 #if defined(__APPLE__)
3311       SetEnvironment("DYLD_LIBRARY_PATH", newList);
3312 #else
3313       SetEnvironment("LD_LIBRARY_PATH", newList);
3314 #endif
3315 /*#ifdef _DEBUG
3316       printf("New value of [DY]LD_LIBRARY_PATH: %s\n", newList);
3317 #endif*/
3318       delete newList;
3319
3320       newLibPaths.Free();
3321       delete newLibPaths;
3322       delete libPathExists;
3323 #endif
3324
3325       if(compiler.distccEnabled && compiler.distccHosts)
3326          SetEnvironment("DISTCC_HOSTS", compiler.distccHosts);
3327
3328       delete oldList;
3329    }
3330
3331    void DestroyTemporaryProjectDir()
3332    {
3333       if(tmpPrjDir && tmpPrjDir[0])
3334       {
3335          if(FileExists(tmpPrjDir).isDirectory)
3336             DestroyDir(tmpPrjDir);
3337          property::tmpPrjDir = null;
3338       }
3339    }
3340
3341    IDEWorkSpace()
3342    {
3343       // Graphics Driver Menu
3344       int c;
3345
3346       /*
3347       app.currentSkin.selectionColor = selectionColor;
3348       app.currentSkin.selectionText = selectionText;
3349       */
3350
3351 /*
3352       driverItems = new MenuItem[app.numDrivers];
3353       for(c = 0; c < app.numDrivers; c++)
3354       {
3355          driverItems[c] = MenuItem { driversMenu, app.drivers[c], NotifySelect = NotifySelectDisplayDriver };
3356          driverItems[c].id = c;
3357          driverItems[c].isRadio = true;         
3358       }
3359 */
3360       driverItems = new MenuItem[2];
3361 #if defined(__unix__)
3362          driverItems[0] = MenuItem { driversMenu, "X", NotifySelect = NotifySelectDisplayDriver };
3363          driverItems[0].id = 0;
3364          driverItems[0].isRadio = true;         
3365 #else
3366          driverItems[0] = MenuItem { driversMenu, "GDI", NotifySelect = NotifySelectDisplayDriver };
3367          driverItems[0].id = 0;
3368          driverItems[0].isRadio = true;         
3369 #endif
3370          driverItems[1] = MenuItem { driversMenu, "OpenGL", NotifySelect = NotifySelectDisplayDriver };
3371          driverItems[1].id = 1;
3372          driverItems[1].isRadio = true;         
3373
3374 /*      skinItems = new MenuItem[app.numSkins];
3375       for(c = 0; c < app.numSkins; c++)
3376       {
3377          skinItems[c] = MenuItem {skinsMenu, app.skins[c], NotifySelect = NotifySelectDisplaySkin };
3378          skinItems[c].id = c;
3379          skinItems[c].isRadio = true;         
3380       }
3381 */
3382       ideFileDialog.master = this;
3383       ideProjectFileDialog.master = this;
3384
3385       //SetDriverAndSkin();
3386       return true;
3387    }
3388
3389    void UpdateRecentMenus()
3390    {
3391       int c;
3392       Menu fileMenu = menu.FindMenu($"File");
3393       Menu recentFiles = fileMenu.FindMenu($"Recent Files");
3394       Menu recentProjects = fileMenu.FindMenu($"Recent Projects");
3395       char * itemPath = new char[MAX_LOCATION];
3396       char * itemName = new char[MAX_LOCATION+4];
3397       MenuItem item;
3398
3399       recentFiles.Clear();
3400       c = 0;
3401
3402       for(recent : ideSettings.recentFiles)
3403       {
3404          strncpy(itemPath, recent, MAX_LOCATION); itemPath[MAX_LOCATION-1] = '\0';
3405          MakeSystemPath(itemPath);
3406          snprintf(itemName, MAX_LOCATION+4, "%d %s", 1 + c, itemPath); itemName[MAX_LOCATION+4-1] = '\0';
3407          recentFiles.AddDynamic(MenuItem { copyText = true, text = itemName, (Key)k1 + c, id = c, NotifySelect = ide.FileRecentFile }, ide, true);
3408          c++;
3409       }
3410
3411       recentProjects.Clear();
3412       c = 0;
3413       for(recent : ideSettings.recentProjects)
3414       {
3415          strncpy(itemPath, recent, MAX_LOCATION); itemPath[MAX_LOCATION-1] = '\0';
3416          MakeSystemPath(itemPath);
3417          snprintf(itemName, MAX_LOCATION+4, "%d %s", 1 + c, itemPath); itemName[MAX_LOCATION+4-1] = '\0';
3418          recentProjects.AddDynamic(MenuItem { copyText = true, text = itemName, (Key)k1 + c, id = c, NotifySelect = ide.FileRecentProject }, ide, true);
3419          c++;
3420       }
3421
3422       delete itemPath;
3423       delete itemName;
3424    }
3425
3426    ~IDEWorkSpace()
3427    {
3428       delete driverItems;
3429       delete skinItems;
3430       delete ideSettings;
3431       if(documentor)
3432       {
3433          documentor.Puts("Quit\n");
3434          documentor.Wait();
3435          delete documentor;
3436       }
3437    }
3438 }
3439
3440 void DestroyDir(char * path)
3441 {
3442    RecursiveDeleteFolderFSI fsi { };
3443    fsi.Iterate(path);
3444    delete fsi;
3445 }
3446
3447 #if defined(__WIN32__)
3448 define sdkDirName = "Ecere SDK";
3449 #else
3450 define sdkDirName = "ecere";
3451 #endif
3452
3453 void FindAndShellOpenInstalledFolder(char * name)
3454 {
3455    char * p = new char[MAX_LOCATION];
3456    char * v = new char[maxPathLen];
3457    byte * tokens[256];
3458    int c, numTokens;
3459    Array<String> paths { };
3460    p[0] = v[0] = '\0';
3461    strncpy(p, settingsContainer.moduleLocation, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3462    StripLastDirectory(p, p);
3463    PathCat(p, name);
3464    paths.Add(CopyString(p));
3465 #if defined(__WIN32__)
3466    GetEnvironment("ECERE_SDK_SRC", v, maxPathLen);
3467    if(v[0])
3468    {
3469       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3470       PathCat(p, name); paths.Add(CopyString(p));
3471    }
3472    GetEnvironment("AppData", v, maxPathLen);
3473    if(v[0])
3474    {
3475       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3476       PathCat(p, sdkDirName); PathCat(p, name); paths.Add(CopyString(p));
3477    }
3478    GetEnvironment("ProgramFiles", v, maxPathLen);
3479    if(v[0])
3480    {
3481       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3482       PathCat(p, sdkDirName); PathCat(p, name); paths.Add(CopyString(p));
3483    }
3484    GetEnvironment("ProgramFiles(x86)", v, maxPathLen);
3485    if(v[0])
3486    {
3487       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3488       PathCat(p, sdkDirName); PathCat(p, name); paths.Add(CopyString(p));
3489    }
3490    GetEnvironment("SystemDrive", v, maxPathLen);
3491    if(v[0])
3492    {
3493       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3494       PathCat(p, "Program Files"); PathCat(p, sdkDirName); PathCat(p, name); paths.Add(CopyString(p));
3495    }
3496 #else
3497    GetEnvironment("XDG_DATA_DIRS", v, maxPathLen);
3498    numTokens = TokenizeWith(v, sizeof(tokens) / sizeof(byte *), tokens, ":", false);
3499    for(c=0; c<numTokens; c++)
3500    {
3501       strncpy(p, tokens[c], MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3502       PathCat(p, sdkDirName); PathCat(p, name); paths.Add(CopyString(p));
3503    }
3504 #endif
3505    for(path : paths)
3506    {
3507       strncpy(p, path, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3508       if(FileExists(p).isDirectory)
3509       {
3510          ShellOpen(p);
3511          break;
3512       }
3513    }
3514    delete p;
3515    delete v;
3516    paths.Free();
3517    delete paths;
3518 }
3519
3520 void FindAndShellOpenInstalledFile(char * subdir, char * name)
3521 {
3522    char * p = new char[MAX_LOCATION];
3523    char * v = new char[maxPathLen];
3524    byte * tokens[256];
3525    int c, numTokens;
3526    Array<String> paths { };
3527    p[0] = v[0] = '\0';
3528    strncpy(p, settingsContainer.moduleLocation, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3529    paths.Add(CopyString(p));
3530    StripLastDirectory(p, p);
3531    PathCat(p, subdir);
3532    paths.Add(CopyString(p));
3533 #if defined(__WIN32__)
3534    GetEnvironment("ECERE_SDK_SRC", v, maxPathLen);
3535    if(v[0])
3536    {
3537       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3538       PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
3539    }
3540    GetEnvironment("AppData", v, maxPathLen);
3541    if(v[0])
3542    {
3543       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3544       PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
3545    }
3546    GetEnvironment("ProgramFiles", v, maxPathLen);
3547    if(v[0])
3548    {
3549       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3550       PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
3551    }
3552    GetEnvironment("ProgramFiles(x86)", v, maxPathLen);
3553    if(v[0])
3554    {
3555       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3556       PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
3557    }
3558    GetEnvironment("SystemDrive", v, maxPathLen);
3559    if(v[0])
3560    {
3561       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3562       PathCat(p, "Program Files"); PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
3563    }
3564 #else
3565    GetEnvironment("XDG_DATA_DIRS", v, maxPathLen);
3566    numTokens = TokenizeWith(v, sizeof(tokens) / sizeof(byte *), tokens, ":", false);
3567    for(c=0; c<numTokens; c++)
3568    {
3569       strncpy(p, tokens[c], MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3570       PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
3571    }
3572 #endif
3573    for(path : paths)
3574    {
3575       strncpy(p, path, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3576       PathCat(p, name);
3577       if(FileExists(p).isFile)
3578       {
3579          ShellOpen(p);
3580          break;
3581       }
3582    }
3583    delete p;
3584    delete v;
3585    paths.Free();
3586    delete paths;
3587 }
3588
3589 class RecursiveDeleteFolderFSI : NormalFileSystemIterator
3590 {
3591    bool preserveRootFolder;
3592
3593    void OutFolder(char * folderPath, bool isRoot)
3594    {
3595       if(!(preserveRootFolder && isRoot))
3596          RemoveDir(folderPath);
3597    }
3598
3599    bool OnFile(char * filePath)
3600    {
3601       DeleteFile(filePath);
3602       return true;
3603    }
3604 }
3605
3606 class IDEApp : GuiApplication
3607 {
3608    //driver = "Win32Console";
3609    // driver = "OpenGL";
3610    // skin = "Aqua";
3611    //skin = "TVision";
3612
3613    TempFile includeFile { };
3614
3615    bool Init()
3616    {
3617       char ext[MAX_EXTENSION];
3618       SetLoggingMode(stdOut, null);
3619       //SetLoggingMode(debug, null);
3620
3621       settingsContainer.Load();
3622       if(argc > 1 && !strcmpi(GetExtension(argv[1], ext), "3ds"))
3623       {
3624          app.driver = "OpenGL";
3625          ide.driverItems[1].checked = true;
3626       }
3627       else
3628       {
3629 #if defined(__unix__) || defined(__APPLE__)
3630          app.driver = (ideSettings.displayDriver && !strcmp(ideSettings.displayDriver, "OpenGL")) ? ideSettings.displayDriver : "X";
3631 #else
3632          app.driver = (ideSettings.displayDriver && !strcmp(ideSettings.displayDriver, "OpenGL")) ? ideSettings.displayDriver : "GDI";
3633 #endif
3634          ide.driverItems[ideSettings.displayDriver && !strcmp(ideSettings.displayDriver,"OpenGL")].checked = true;
3635       }
3636
3637       SetInIDE(true);
3638
3639       desktop.text = titleECEREIDE;
3640       /*
3641       int c;
3642       for(c = 1; c<app.argc; c++)
3643       {
3644          char fullPath[MAX_LOCATION];
3645          GetWorkingDir(fullPath, MAX_LOCATION);
3646          PathCat(fullPath, app.argv[c]);
3647          ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, null, yes, normal, false);
3648       }
3649       */
3650
3651       if(!LoadIncludeFile())
3652          PrintLn("error: unable to load :crossplatform.mk file inside ide binary.");
3653
3654       return true;
3655    }
3656
3657    bool Cycle(bool idle)
3658    {
3659       if(ide.documentor)
3660       {
3661          if(ide.documentor.Peek())
3662          {
3663             char line[1024];
3664             ide.documentor.GetLine(line, sizeof(line));
3665             if(!strcmpi(line, "Exited"))
3666             {
3667                ide.documentor.CloseInput();
3668                ide.documentor.CloseOutput();
3669                ide.documentor.Wait();
3670                delete ide.documentor;
3671             }
3672          }
3673          if(ide.documentor && ide.documentor.eof)
3674          {
3675             ide.documentor.CloseInput();
3676             ide.documentor.CloseOutput();
3677             ide.documentor.Wait();
3678             delete ide.documentor;
3679          }
3680       }
3681       return true;
3682    }
3683
3684    bool LoadIncludeFile()
3685    {
3686       bool result = false;
3687       File include = FileOpen(":crossplatform.mk", read);
3688       if(include)
3689       {
3690          File f = includeFile;
3691          if(f)
3692          {
3693             for(; !include.Eof(); )
3694             {
3695                char buffer[4096];
3696                int count = include.Read(buffer, 1, 4096);
3697                f.Write(buffer, 1, count);
3698             }
3699             result = true;
3700          }
3701          delete include;
3702       }
3703       return result;
3704    }
3705 }
3706
3707 IDEMainFrame ideMainFrame { };
3708
3709 define app = ((IDEApp)__thisModule);
3710 #ifdef _DEBUG
3711 define titleECEREIDE = $"ECERE IDE (Debug)";
3712 #else
3713 define titleECEREIDE = $"ECERE IDE";
3714 #endif