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