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