documentor, ide: (#773) bidirectional communication
[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 debugValgrindRS0Item   { debugValgrindRedzoneSizeItem, $"0"  , f, id =   0, checkable = true, disabled = true; NotifySelect = ValgrindRSSelect, checked = true; }
1154          MenuItem debugValgrindRS16Item  { debugValgrindRedzoneSizeItem, $"16" , f, id =  16, checkable = true, disabled = true; NotifySelect = ValgrindRSSelect; }
1155          MenuItem debugValgrindRS32Item  { debugValgrindRedzoneSizeItem, $"32" , f, id =  32, checkable = true, disabled = true; NotifySelect = ValgrindRSSelect; }
1156          MenuItem debugValgrindRS64Item  { debugValgrindRedzoneSizeItem, $"64" , f, id =  64, checkable = true, disabled = true; NotifySelect = ValgrindRSSelect; }
1157          MenuItem debugValgrindRS128Item { debugValgrindRedzoneSizeItem, $"128", f, id = 128, checkable = true, disabled = true; NotifySelect = ValgrindRSSelect; }
1158          MenuItem debugValgrindRS256Item { debugValgrindRedzoneSizeItem, $"256", f, id = 256, checkable = true, disabled = true; NotifySelect = ValgrindRSSelect; }
1159          MenuItem debugValgrindRS512Item { debugValgrindRedzoneSizeItem, $"512", f, id = 512, checkable = true, disabled = true; NotifySelect = ValgrindRSSelect; }
1160          bool ValgrindRSSelect(MenuItem selection, Modifiers mods)
1161          {
1162             if(ide.workspace)
1163             {
1164                if(selection.checked)
1165                {
1166                   int vgRedzoneSize = (int)selection.id;
1167
1168                   debugValgrindRS0Item.checked   = debugValgrindRS0Item.id   == vgRedzoneSize;
1169                   debugValgrindRS16Item.checked  = debugValgrindRS16Item.id  == vgRedzoneSize;
1170                   debugValgrindRS32Item.checked  = debugValgrindRS32Item.id  == vgRedzoneSize;
1171                   debugValgrindRS64Item.checked  = debugValgrindRS64Item.id  == vgRedzoneSize;
1172                   debugValgrindRS128Item.checked = debugValgrindRS128Item.id == vgRedzoneSize;
1173                   debugValgrindRS256Item.checked = debugValgrindRS256Item.id == vgRedzoneSize;
1174                   debugValgrindRS512Item.checked = debugValgrindRS512Item.id == vgRedzoneSize;
1175
1176                   ide.workspace.vgRedzoneSize = vgRedzoneSize;
1177                   ide.workspace.Save();
1178                }
1179                else
1180                   selection.checked = true;
1181             }
1182             return true;
1183          }
1184       MenuItem debugValgrindTrackOriginsItem
1185       {
1186          debugMenu, $"Valgrind Track Origins", k, checkable = true, disabled = true;
1187          bool NotifySelect(MenuItem selection, Modifiers mods)
1188          {
1189             if(ide.workspace)
1190             {
1191                ide.workspace.vgTrackOrigins = selection.checked;
1192                ide.workspace.Save();
1193             }
1194             return true;
1195          }
1196       };
1197 #endif
1198       MenuDivider { debugMenu };
1199       MenuItem debugStepIntoItem
1200       {
1201          debugMenu, $"Step Into", i, f11, disabled = true;
1202          bitmap = { ":actions/stepInto.png" };
1203          bool NotifySelect(MenuItem selection, Modifiers mods)
1204          {
1205             if(projectView)
1206                projectView.DebugStepInto();
1207             return true;
1208          }
1209       }
1210       MenuItem debugStepOverItem
1211       {
1212          debugMenu, $"Step Over", v, f10, disabled = true;
1213          bitmap = { ":actions/stepOver.png" };
1214          bool NotifySelect(MenuItem selection, Modifiers mods)
1215          {
1216             if(projectView)
1217                projectView.DebugStepOver(false);
1218             return true;
1219          }
1220       }
1221       MenuItem debugStepOutItem
1222       {
1223          debugMenu, $"Step Out", o, shiftF11, disabled = true;
1224          bitmap = { ":actions/stepOut.png" };
1225          bool NotifySelect(MenuItem selection, Modifiers mods)
1226          {
1227             if(projectView)
1228                projectView.DebugStepOut(false);
1229             return true;
1230          }
1231       }
1232       MenuPlacement debugRunToCursorItem { debugMenu, $"Run To Cursor", c };
1233       MenuItem debugSkipStepOverItem
1234       {
1235          debugMenu, $"Step Over Skipping Breakpoints", e, shiftF10, disabled = true;
1236          bool NotifySelect(MenuItem selection, Modifiers mods)
1237          {
1238             if(projectView)
1239                projectView.DebugStepOver(true);
1240             return true;
1241          }
1242       }
1243       MenuItem debugSkipStepOutItem
1244       {
1245          debugMenu, $"Step Out Skipping Breakpoints", t, Key { f11, ctrl = true, shift = true }, disabled = true;
1246          bitmap = { ":actions/skipBreaks.png" };
1247          bool NotifySelect(MenuItem selection, Modifiers mods)
1248          {
1249             if(projectView)
1250                projectView.DebugStepOut(true);
1251             return true;
1252          }
1253       }
1254       MenuPlacement debugSkipRunToCursorItem { debugMenu, $"Run To Cursor Skipping Breakpoints", u };
1255       MenuPlacement debugSkipRunToCursorAtSameLevelItem { debugMenu, $"Run To Cursor At Same Level Skipping Breakpoints", l };
1256       //MenuDivider { debugMenu };
1257       //MenuPlacement debugToggleBreakpoint { debugMenu, "Toggle Breakpoint", t };
1258    MenuPlacement imageMenu { menu, $"Image", i };
1259    Menu viewMenu { menu, $"View", v };
1260       MenuItem viewProjectItem
1261       {
1262          viewMenu, $"Project View", j, alt0, disabled = true;
1263          bool NotifySelect(MenuItem selection, Modifiers mods)
1264          {
1265             if(projectView)
1266             {
1267                projectView.visible = true;
1268                projectView.Activate();
1269             }
1270             return true;
1271          }
1272       }
1273       MenuPlacement { viewMenu, $"View Designer" };
1274       MenuPlacement { viewMenu, $"View Code" };
1275       MenuPlacement { viewMenu, $"View Properties" };
1276       MenuPlacement { viewMenu, $"View Methods" };
1277       MenuItem viewDesignerItem
1278       {
1279          viewMenu, $"View Designer", d, f8;
1280          bool NotifySelect(MenuItem selection, Modifiers mods)
1281          {
1282             Window client = activeClient;
1283             Class dataType = client._class;
1284             if(!strcmp(dataType.name, "Designer"))
1285             {
1286                client.visible = true;
1287                client.Activate();
1288             }
1289             else
1290                ((CodeEditor)client).ViewDesigner();
1291             return true;
1292          }
1293       }
1294       MenuItem viewCodeItem
1295       {
1296          viewMenu, $"View Code", c, f8;
1297          bool NotifySelect(MenuItem selection, Modifiers mods)
1298          {
1299             Window client = activeClient;
1300             Class dataType = client._class;
1301             if(!strcmp(dataType.name, "Designer"))
1302                client = ((Designer)client).codeEditor;
1303
1304             client.Activate();
1305             // Do this after so the caret isn't moved yet...
1306             client.visible = true;
1307             return true;
1308          }
1309       }
1310       MenuItem viewPropertiesItem
1311       {
1312          viewMenu, $"View Properties", p, f4;
1313          bool NotifySelect(MenuItem selection, Modifiers mods)
1314          {
1315             sheet.visible = true;
1316             sheet.sheetSelected = properties;
1317             sheet.Activate();
1318             return true;
1319          }
1320       }
1321       MenuItem viewMethodsItem
1322       {
1323          viewMenu, $"View Methods", m, f4;
1324          bool NotifySelect(MenuItem selection, Modifiers mods)
1325          {
1326             sheet.visible = true;
1327             sheet.sheetSelected = methods;
1328             sheet.Activate();
1329             return true;
1330          }
1331       }
1332       MenuItem viewToolBoxItem
1333       {
1334          viewMenu, $"View Toolbox", x, f12;
1335          bool NotifySelect(MenuItem selection, Modifiers mods)
1336          {
1337             toolBox.visible = true;
1338             toolBox.Activate();
1339             return true;
1340          }
1341       }
1342       MenuItem viewOutputItem
1343       {
1344          viewMenu, $"Output", o, alt2;
1345          bool NotifySelect(MenuItem selection, Modifiers mods)
1346          {
1347             outputView.Show();
1348             return true;
1349          }
1350       }
1351       MenuItem viewWatchesItem
1352       {
1353          viewMenu, $"Watches", w, alt3;
1354          bool NotifySelect(MenuItem selection, Modifiers mods)
1355          {
1356             watchesView.Show();
1357             return true;
1358          }
1359       }
1360       MenuItem viewThreadsItem
1361       {
1362          viewMenu, $"Threads", t, alt4;
1363          bool NotifySelect(MenuItem selection, Modifiers mods)
1364          {
1365             threadsView.Show();
1366             return true;
1367          }
1368       }
1369       MenuItem viewBreakpointsItem
1370       {
1371          viewMenu, $"Breakpoints", b, alt5;
1372          bool NotifySelect(MenuItem selection, Modifiers mods)
1373          {
1374             breakpointsView.Show();
1375             return true;
1376          }
1377       }
1378       MenuItem viewCallStackItem
1379       {
1380          viewMenu, $"Call Stack", s, alt7;
1381          bool NotifySelect(MenuItem selection, Modifiers mods)
1382          {
1383             callStackView.Show();
1384             return true;
1385          }
1386       }
1387       MenuItem viewAllDebugViews
1388       {
1389          viewMenu, $"All Debug Views", a, alt9;
1390          bool NotifySelect(MenuItem selection, Modifiers mods)
1391          {
1392             outputView.Show();
1393             watchesView.Show();
1394             threadsView.Show();
1395             callStackView.Show();
1396             breakpointsView.Show();
1397             return true;
1398          }
1399       }
1400 #ifdef GDB_DEBUG_GUI
1401       MenuDivider { viewMenu };
1402       MenuItem viewGDBItem
1403       {
1404          viewMenu, $"GDB Dialog", g, Key { f9, shift = true, ctrl = true };
1405          bool NotifySelect(MenuItem selection, Modifiers mods)
1406          {
1407             gdbDialog.Show();
1408             return true;
1409          }
1410       }
1411 #endif
1412       MenuDivider { viewMenu };
1413       MenuItem viewColorPicker
1414       {
1415          viewMenu, $"Color Picker...", c, Key { c, ctrl = true , shift = true };
1416          bool NotifySelect(MenuItem selection, Modifiers mods)
1417          {
1418             ColorPicker colorPicker { master = this };
1419             colorPicker.Modal();
1420             return true;
1421          }
1422       }
1423       MenuDivider { viewMenu };
1424       /*
1425       MenuItem
1426       {
1427          viewMenu, "Full Screen", f, checkable = true;
1428
1429          bool NotifySelect(MenuItem selection, Modifiers mods)
1430          {
1431             app.fullScreen ^= true;
1432             SetDriverAndSkin();
1433             anchor = { 0, 0, 0, 0 };
1434             return true;
1435          }
1436       };
1437       */
1438       Menu driversMenu { viewMenu, $"Graphics Driver", v };
1439       //Menu skinsMenu { viewMenu, "GUI Skins", k };
1440    Menu windowMenu { menu, $"Window", w };
1441       MenuItem { windowMenu, $"Close All", l, NotifySelect = MenuWindowCloseAll };
1442       MenuDivider { windowMenu };
1443       MenuItem { windowMenu, $"Next", n, f6, NotifySelect = MenuWindowNext };
1444       MenuItem { windowMenu, $"Previous", p, shiftF6, NotifySelect = MenuWindowPrevious };
1445       MenuDivider { windowMenu };
1446       MenuItem { windowMenu, $"Cascade", c, NotifySelect = MenuWindowCascade };
1447       MenuItem { windowMenu, $"Tile Horizontally", h, NotifySelect = MenuWindowTileHorz };
1448       MenuItem { windowMenu, $"Tile Vertically", v, NotifySelect = MenuWindowTileVert };
1449       MenuItem { windowMenu, $"Arrange Icons", a, NotifySelect = MenuWindowArrangeIcons };
1450       MenuDivider { windowMenu };
1451       MenuItem { windowMenu, $"Windows...", w, NotifySelect = MenuWindowWindows };
1452    Menu helpMenu { menu, $"Help", h };
1453       MenuItem
1454       {
1455          helpMenu, $"API Reference", r, f1;
1456          bool NotifySelect(MenuItem selection, Modifiers mods)
1457          {
1458             if(!documentor)
1459             {
1460                char * p = new char[MAX_LOCATION];
1461                p[0] = '\0';
1462                strncpy(p, settingsContainer.moduleLocation, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
1463                PathCat(p, "documentor");
1464    #if defined(__WIN32__)
1465                ChangeExtension(p, "exe", p);
1466    #endif
1467                if(!FileExists(p).isFile)
1468                   strcpy(p, "documentor");
1469
1470                documentor = DualPipeOpen({ input = true, output = true, showWindow = true }, p);
1471                delete p;
1472             }
1473             else
1474             {
1475                Process_ShowWindows(documentor.GetProcessID());
1476                // documentor.Puts("Activate\n");
1477             }
1478             return true;
1479          }
1480       }
1481       MenuDivider { helpMenu };
1482       MenuItem
1483       {
1484          helpMenu, $"Ecere Tao of Programming [work in progress]", t;
1485          bool NotifySelect(MenuItem selection, Modifiers mods)
1486          {
1487             FindAndShellOpenInstalledFile("doc", "Ecere Tao of Programming [work in progress].pdf");
1488             return true;
1489          }
1490       }
1491       MenuDivider { helpMenu };
1492       MenuItem
1493       {
1494          helpMenu, $"Documentation Folder", d;
1495          bool NotifySelect(MenuItem selection, Modifiers mods)
1496          {
1497             FindAndShellOpenInstalledFolder("doc");
1498             return true;
1499          }
1500       }
1501       MenuItem
1502       {
1503          helpMenu, $"Samples Folder", s;
1504          bool NotifySelect(MenuItem selection, Modifiers mods)
1505          {
1506             FindAndShellOpenInstalledFolder("samples");
1507             return true;
1508          }
1509       }
1510       MenuItem
1511       {
1512          helpMenu, $"Extras Folder", x;
1513          bool NotifySelect(MenuItem selection, Modifiers mods)
1514          {
1515             FindAndShellOpenInstalledFolder("extras");
1516             return true;
1517          }
1518       }
1519       MenuDivider { helpMenu };
1520       MenuItem
1521       {
1522          helpMenu, $"Community Forums", f;
1523          bool NotifySelect(MenuItem selection, Modifiers mods)
1524          {
1525             ShellOpen("http://ecere.com/forums");
1526             return true;
1527          }
1528       }
1529       MenuDivider { helpMenu };
1530       MenuItem
1531       {
1532          helpMenu, $"About...", a;
1533          bool NotifySelect(MenuItem selection, Modifiers mods)
1534          {
1535             AboutIDE { master = this }.Modal();
1536             return true;
1537          }
1538       }
1539
1540    property ToolBox toolBox
1541    {
1542       get { return toolBox; }
1543    }
1544
1545    property Sheet sheet
1546    {
1547       get { return sheet; }
1548    }
1549
1550    property Project project
1551    {
1552       get { return projectView ? projectView.project : null; }
1553    }
1554
1555    property Workspace workspace
1556    {
1557       get { return projectView ? projectView.workspace : null; }
1558    }
1559
1560    FindInFilesDialog findInFilesDialog
1561    {
1562       master = this,
1563       filters = findInFilesFileFilters.array, sizeFilters = findInFilesFileFilters.count * sizeof(FileFilter);
1564       filter = 1;
1565    };
1566
1567    bool noParsing;
1568
1569 #ifdef GDB_DEBUG_GUI
1570    GDBDialog gdbDialog
1571    {
1572       master = this, parent = this;
1573       //anchor = { left = 100, top = 100, right = 100, bottom = 100 };
1574
1575       void OnCommand(char * string)
1576       {
1577          if(ide)
1578             ide.debugger.SendGDBCommand(string);
1579       }
1580    };
1581 #endif
1582    
1583    bool NotifySelectDisplayDriver(MenuItem selection, Modifiers mods)
1584    {
1585       //app.driver = app.drivers[selection.id];
1586 #if defined(__unix__) || defined(__APPLE__)
1587       app.driver = selection.id ? "OpenGL" : "X";
1588 #else
1589       app.driver = selection.id ? "OpenGL" : "GDI";
1590 #endif
1591       delete ideSettings.displayDriver;
1592       ideSettings.displayDriver = CopyString(selection.id ? "OpenGL" : "Default");
1593
1594       settingsContainer.Save();
1595       //SetDriverAndSkin();
1596       return true;
1597    }
1598
1599    bool NotifySelectDisplaySkin(MenuItem selection, Modifiers mods)
1600    {
1601       app.skin = app.skins[selection.id];
1602       SetDriverAndSkin();
1603       return true;
1604    }
1605
1606    void SetDriverAndSkin()
1607    {
1608       int c;
1609       for(c = 0; c < app.numSkins; c++)
1610          if(!strcmp(app.skins[c], app.skin))
1611          {
1612             skinItems[c].checked = true;
1613             break;
1614          }
1615       for(c = 0; c < app.numDrivers; c++)
1616          if(!strcmp(app.drivers[c], app.driver))
1617          {
1618             driverItems[c].checked = true;
1619             break;
1620          }
1621    }
1622
1623    ProjectView CreateProjectView(Workspace workspace, char * fileName)
1624    {
1625       Project project = workspace.projects.firstIterator.data;
1626       projectView = ProjectView
1627       {
1628          this;
1629          fileName = fileName;
1630          
1631          void NotifyDestroyed(Window window, DialogResult result)
1632          {
1633             projectView = null;
1634             text = titleECEREIDE;
1635             
1636             AdjustMenus();
1637          }
1638       };
1639       projectView.Create();
1640       RepositionWindows(false);
1641
1642       // Leave it after Create to avoid flicker due to seeing IDE without a project view
1643       projectView.workspace = workspace;
1644       projectView.project = project;
1645       ideMainFrame.SetText("%s - %s", project.topNode.name, titleECEREIDE);
1646
1647       AdjustMenus();
1648
1649       ide.breakpointsView.LoadFromWorkspace();
1650       ide.watchesView.LoadFromWorkspace();
1651
1652       findInFilesDialog.projectNodeField.userData = projectView;
1653
1654       {
1655          char fileName[MAX_LOCATION];
1656          strcpy(fileName, project.topNode.path);
1657          PathCat(fileName, project.topNode.name);
1658       }
1659       return projectView;
1660    }
1661
1662    bool ProjectClose()
1663    {
1664       projectView.visible = false;
1665       if((!projectView || projectView.created == false || projectView.Destroy(0)) && MenuWindowCloseAll(null, 0))
1666       {
1667          if(findInFilesDialog)
1668          {
1669             char workingDir[MAX_LOCATION];
1670             GetWorkingDir(workingDir, MAX_LOCATION);
1671             findInFilesDialog.SearchStop();
1672             findInFilesDialog.currentDirectory = workingDir;
1673          }
1674          ideMainFrame.text = titleECEREIDE;
1675          ide.AdjustMenus();
1676          return true;
1677       }
1678       return false;
1679    }
1680
1681    void RepositionWindows(bool expand)
1682    {
1683       if(this)
1684       {
1685          Window child;
1686          bool inDebugMode = debugger.isActive;
1687          bool callStackVisible = expand ? false : callStackView.visible;
1688          bool threadsVisible = expand ? false : threadsView.visible;
1689          bool watchesVisible = expand ? false : watchesView.visible;
1690          bool breakpointsVisible = expand ? false : breakpointsView.visible;
1691          bool toolBoxVisible = toolBox.visible;
1692          bool outputVisible = expand ? false : outputView.visible;
1693          int topDistance = (callStackVisible || threadsVisible) ? 200 : 0;
1694          int bottomDistance = (outputVisible || watchesVisible || breakpointsVisible) ? 240 : 0;
1695          
1696          for(child = firstChild; child; child = child.next)
1697          {
1698             if(child._class == class(CodeEditor) || child._class == class(Designer) || 
1699                child._class == class(Sheet) || child._class == class(ProjectView))
1700             {
1701                Anchor anchor = child.anchor;
1702                anchor.top = topDistance;
1703                anchor.bottom = bottomDistance;
1704                if(child._class == class(CodeEditor) || child._class == class(Designer))
1705                {
1706                   anchor.left = 300;
1707                   anchor.right = toolBoxVisible ? 150 : 0;
1708                }
1709                child.anchor = anchor;
1710             }
1711             else if(expand)
1712             {
1713                if(child._class == class(OutputView) || child._class == class(CallStackView) || child._class == class(ThreadsView) || child._class == class(WatchesView) || 
1714                   child._class == class(BreakpointsView))
1715                   child.visible = false;
1716             }
1717          }
1718          // If this is not here, the IDE is not updated when doing Debug/Break then Alt-4 to show call stack (IDE not updated)
1719          Update(null);
1720       }
1721    }
1722
1723    bool ShowCodeEditor()
1724    {
1725       if(activeClient)
1726          activeClient.Activate();
1727       else if(projectView)
1728       { 
1729          projectView.visible = true;
1730          projectView.Activate();
1731       }
1732       else
1733       {
1734          sheet.visible = true;
1735          sheet.Activate();
1736       }
1737       return false;
1738    }
1739
1740    void DocumentSaved(Window document, char * fileName)
1741    {
1742       ideSettings.AddRecentFile(fileName);
1743       ide.UpdateRecentMenus();
1744       ide.AdjustFileMenus();
1745       settingsContainer.Save();
1746    }
1747
1748    bool Window::OnFileModified(FileChange fileChange, char * param)
1749    {
1750       char temp[4096];
1751       sprintf(temp, $"The document %s was modified by another application.\n"
1752             "Would you like to reload it and lose your changes?", this.fileName);
1753       if(MessageBox { type = yesNo, master = this/*.parent*/,
1754             text = $"Document has been modified", contents = temp }.Modal() == yes)
1755       {
1756          bool noParsing = (this._class == class(CodeEditor) && ((CodeEditor)this).noParsing) ? true : false;
1757          char * fileName = CopyString(this.fileName);
1758          WindowState state = this.state;
1759          Anchor anchor = this.anchor;
1760          Size size = this.size;
1761
1762          this.modifiedDocument = false;
1763          this.Destroy(0);
1764          this = ide.OpenFile(fileName, normal, true, null, no, normal, noParsing);
1765          if(this)
1766          {
1767             this.anchor = anchor;
1768             this.size = size;
1769             this.SetState(state, true, 0);
1770          }
1771          delete fileName;
1772          return true;
1773       }
1774       return true;
1775    }
1776
1777    void UpdateMakefiles()
1778    {
1779       if(workspace)
1780       {
1781          CompilerConfig compiler = ideSettings.GetCompilerConfig(workspace.compiler);
1782          for(prj : workspace.projects)
1783             projectView.ProjectUpdateMakefileForAllConfigs(prj);
1784          delete compiler;
1785       }
1786    }
1787
1788    void UpdateCompilerConfigs(bool mute)
1789    {
1790       UpdateToolBarActiveCompilers();
1791       if(workspace)
1792       {
1793          bool silent = mute || (ide.projectView.buildInProgress == none ? false : true);
1794          CompilerConfig compiler = ideSettings.GetCompilerConfig(workspace.compiler);
1795          if(!silent)
1796          {
1797             projectView.ShowOutputBuildLog(true);
1798             projectView.DisplayCompiler(compiler, false);
1799          }
1800          for(prj : workspace.projects)
1801             projectView.ProjectPrepareCompiler(prj, compiler, silent);
1802          delete compiler;
1803       }
1804    }
1805
1806    void UpdateToolBarActiveCompilers()
1807    {
1808       toolBar.activeCompiler.Clear();
1809       for(compiler : ideSettings.compilerConfigs)
1810       {
1811          DataRow row = toolBar.activeCompiler.AddString(compiler.name);
1812          if(workspace && workspace.compiler && !strcmp(compiler.name, workspace.compiler))
1813             toolBar.activeCompiler.currentRow = row;
1814       }
1815       if(!toolBar.activeCompiler.currentRow && toolBar.activeCompiler.firstRow)
1816          toolBar.activeCompiler.SelectRow(toolBar.activeCompiler.firstRow);
1817    }
1818
1819    void UpdateToolBarActiveConfigs(bool selectionOnly)
1820    {
1821       bool commonSelected = false;
1822       DataRow row = toolBar.activeConfig.currentRow;
1823       if(selectionOnly)
1824          row = toolBar.activeConfig.FindRow(1);
1825       else
1826       {
1827          toolBar.activeConfig.Clear();
1828          row = toolBar.activeConfig.AddString($"(Mixed)");
1829          row.tag = 1;
1830       }
1831       if(workspace)
1832       {
1833          char * configName = null;
1834          if(!selectionOnly)
1835          {
1836             Map<String, int> configs { }; // TOIMP: just need sort but using map until containers have sort
1837             for(prj : workspace.projects)
1838             {
1839                for(cfg : prj.configurations)
1840                {
1841                   if(cfg.name)
1842                      configs[cfg.name] = 1;
1843                }
1844             }
1845             for(name : configs)
1846             {
1847                toolBar.activeConfig.AddString(&name);
1848             }
1849             delete configs;
1850          }
1851          if(projectView && projectView.project)
1852          {
1853             for(prj : workspace.projects)
1854             {
1855                if(prj.config && prj.config.name)
1856                {
1857                   configName = prj.config.name;
1858                   break;
1859                }
1860             }
1861             if(configName)
1862             {
1863                commonSelected = true;
1864                for(prj : workspace.projects)
1865                {
1866                   if(prj.config && (!prj.config.name || strcmp(prj.config.name, configName)))
1867                   {
1868                      commonSelected = false;
1869                      break;
1870                   }
1871                }
1872             }
1873          }
1874          if(commonSelected)
1875          {
1876             commonSelected = false;
1877             for(row = toolBar.activeConfig.firstRow; row; row = row.next)
1878             {
1879                if(!strcmp(row.string, configName))
1880                {
1881                   toolBar.activeConfig.currentRow = row;
1882                   commonSelected = true;
1883                   break;
1884                }
1885             }
1886          }
1887       }
1888       if(!selectionOnly)
1889          toolBar.activeConfig.Sort(null, 0);
1890       if(!commonSelected)
1891          toolBar.activeConfig.currentRow = row;
1892    }
1893
1894    void AdjustMenus()
1895    {
1896       bool unavailable = !project;
1897
1898       projectAddItem.disabled             = unavailable;
1899       toolBar.buttonAddProject.disabled   = unavailable;
1900
1901       projectSettingsItem.disabled        = unavailable;
1902
1903       projectBrowseFolderItem.disabled    = unavailable;
1904
1905       viewProjectItem.disabled            = unavailable;
1906
1907       toolBar.activeConfig.disabled       = unavailable;
1908       toolBar.activeCompiler.disabled     = unavailable;
1909       toolBar.activeBitDepth.disabled     = unavailable;
1910
1911 #ifndef __WIN32__
1912       debugUseValgrindItem.disabled       = unavailable;
1913       AdjustValgrindMenus();
1914 #endif
1915
1916       AdjustFileMenus();
1917       AdjustBuildMenus();
1918       AdjustDebugMenus();
1919    }
1920
1921 #ifndef __WIN32__
1922    void AdjustValgrindMenus()
1923    {
1924       bool unavailable = !project || !debugUseValgrindItem.checked;
1925       debugValgrindNoLeakCheckItem.disabled        = unavailable;
1926       debugValgrindSummaryLeakCheckItem.disabled   = unavailable;
1927       debugValgrindYesLeakCheckItem.disabled       = unavailable;
1928       debugValgrindFullLeakCheckItem.disabled      = unavailable;
1929
1930       debugValgrindTrackOriginsItem.disabled       = unavailable;
1931
1932       debugValgrindRS0Item.disabled   = unavailable;
1933       debugValgrindRS16Item.disabled  = unavailable;
1934       debugValgrindRS32Item.disabled  = unavailable;
1935       debugValgrindRS64Item.disabled  = unavailable;
1936       debugValgrindRS128Item.disabled = unavailable;
1937       debugValgrindRS256Item.disabled = unavailable;
1938       debugValgrindRS512Item.disabled = unavailable;
1939    }
1940 #endif
1941
1942    property bool hasOpenedCodeEditors
1943    {
1944       get
1945       {
1946          Window w;
1947          for(w = firstChild; w; w = w.next)
1948             if(w._class == class(CodeEditor) &&
1949                   w.isDocument && !w.closing && w.visible && w.created &&
1950                   w.fileName && w.fileName[0])
1951                return true;
1952          return false;
1953       }
1954    }
1955
1956    void AdjustFileMenus()
1957    {
1958       bool unavailable = project != null || !hasOpenedCodeEditors; // are they supported source code (ec, c, cpp, etc) ?
1959
1960       projectQuickItem.disabled           = unavailable;
1961    }
1962
1963    void AdjustBuildMenus()
1964    {
1965       bool unavailable = project && projectView.buildInProgress;
1966       bool naForRun = unavailable || !project || project.GetTargetType(project.config) != executable;
1967
1968       projectNewItem.disabled             = unavailable;
1969       toolBar.buttonNewProject.disabled   = unavailable;
1970       projectOpenItem.disabled            = unavailable;
1971       toolBar.buttonOpenProject.disabled  = unavailable;
1972
1973       unavailable = !project || projectView.buildInProgress;
1974
1975       projectCloseItem.disabled           = unavailable;
1976       // toolBar.buttonCloseProject.disabled = unavailable;
1977
1978       projectRunItem.disabled    = naForRun;
1979       toolBar.buttonRun.disabled = naForRun;
1980
1981       projectBuildItem.disabled = false;
1982       projectBuildItem.text     = unavailable ? $"Stop Build" : $"Build";
1983       projectBuildItem.accelerator = unavailable ? Key { pauseBreak, ctrl = true } : f7;
1984
1985       projectLinkItem.disabled                  = unavailable;
1986       toolBar.buttonReLink.disabled             = unavailable;
1987       projectRebuildItem.disabled               = unavailable;
1988       toolBar.buttonRebuild.disabled            = unavailable;
1989       projectCleanItem.disabled                 = unavailable;
1990       toolBar.buttonClean.disabled              = unavailable;
1991       projectCleanTargetItem.disabled           = unavailable;
1992       projectRealCleanItem.disabled             = unavailable;
1993       // toolBar.buttonRealClean.disabled          = unavailable;
1994       projectRegenerateItem.disabled            = unavailable;
1995       toolBar.buttonRegenerateMakefile.disabled = unavailable;
1996 #ifdef IDE_SHOW_INSTALL_MENU_BUTTON
1997       projectInstallItem.disabled               = unavailable;
1998       toolBar.buttonInstall.disabled            = unavailable;
1999 #endif
2000       projectCompileItem.disabled               = unavailable;
2001
2002       AdjustPopupBuildMenus();
2003    }
2004
2005    void AdjustPopupBuildMenus()
2006    {
2007       bool unavailable = !project || projectView.buildInProgress;
2008
2009       if(projectView && projectView.popupMenu && projectView.popupMenu.menu && projectView.popupMenu.created)
2010       {
2011          MenuItem menu;
2012          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectBuild, 0);
2013          if(menu)
2014          {
2015             menu.disabled = false;
2016             menu.text   = unavailable ? $"Stop Build" : $"Build";
2017             menu.accelerator = unavailable ? Key { pauseBreak, ctrl = true } : f7;
2018          }
2019
2020          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectLink, 0);              if(menu) menu.disabled = unavailable;
2021          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectRebuild, 0);           if(menu) menu.disabled = unavailable;
2022          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectCleanTarget, 0);       if(menu) menu.disabled = unavailable;
2023          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectClean, 0);             if(menu) menu.disabled = unavailable;
2024          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectRealClean, 0);         if(menu) menu.disabled = unavailable;
2025          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectRegenerate, 0);        if(menu) menu.disabled = unavailable;
2026          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectInstall, 0);           if(menu) menu.disabled = unavailable;
2027          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectRemove, 0);            if(menu) menu.disabled = unavailable;
2028          menu = projectView.popupMenu.menu.FindItem(ProjectView::FileClean, 0);                if(menu) menu.disabled = unavailable;
2029          menu = projectView.popupMenu.menu.FindItem(ProjectView::FileCompile, 0);              if(menu) menu.disabled = unavailable;
2030          menu = projectView.popupMenu.menu.FindItem(ProjectView::FileDebugPrecompile, 0);      if(menu) menu.disabled = unavailable;
2031          menu = projectView.popupMenu.menu.FindItem(ProjectView::FileDebugCompile, 0);         if(menu) menu.disabled = unavailable;
2032          menu = projectView.popupMenu.menu.FindItem(ProjectView::FileDebugGenerateSymbols, 0); if(menu) menu.disabled = unavailable;
2033          projectView.popupMenu.Update(null);
2034       }
2035    }
2036
2037    property bool areDebugMenusUnavailable { get {
2038       return !project ||
2039             project.GetTargetType(project.config) != executable ||
2040             projectView.buildInProgress == buildingMainProject;
2041    } }
2042
2043    property bool isBreakpointTogglingUnavailable { get {
2044       return !project;
2045    } }
2046
2047    property bool isDebuggerExecuting { get {
2048       if(!ide.debugger)
2049          return false;
2050       else
2051          return ide.debugger.state == running;
2052    } }
2053
2054    void AdjustDebugMenus()
2055    {
2056       bool unavailable = areDebugMenusUnavailable;
2057       bool active = debugger.isActive;
2058       bool bpNoToggle = isBreakpointTogglingUnavailable;
2059       bool executing = isDebuggerExecuting;
2060       //bool holding = debugger.state == stopped;
2061
2062       debugStartResumeItem.disabled       = unavailable || executing;
2063       debugStartResumeItem.text           = active ? $"Resume" : $"Start";
2064       debugStartResumeItem.NotifySelect   = active ? MenuDebugResume : MenuDebugStart;
2065       if(toolBar)
2066       {
2067          toolBar.buttonDebugStartResume.disabled      = unavailable || executing;
2068          toolBar.buttonDebugStartResume.toolTip       = active ? $"Resume" : $"Start";
2069       }
2070
2071       debugBreakItem.disabled             = unavailable || !executing;
2072       debugStopItem.disabled              = unavailable || !active;
2073       debugRestartItem.disabled           = unavailable || !active;
2074       if(toolBar)
2075       {
2076          toolBar.buttonDebugPause.disabled            = unavailable || !executing;
2077          toolBar.buttonDebugStop.disabled             = unavailable || !active;
2078          toolBar.buttonDebugRestart.disabled          = unavailable || !active;
2079       }
2080
2081       debugStepIntoItem.disabled          = unavailable || executing;
2082       debugStepOverItem.disabled          = unavailable || executing;
2083       debugStepOutItem.disabled           = unavailable || executing || !active;
2084       debugSkipStepOverItem.disabled      = unavailable || executing;
2085       debugSkipStepOutItem.disabled       = unavailable || executing || !active;
2086       if(toolBar)
2087       {
2088          toolBar.buttonDebugStepInto.disabled         = unavailable || executing;
2089          toolBar.buttonDebugStepOver.disabled         = unavailable || executing;
2090          toolBar.buttonDebugStepOut.disabled          = unavailable || executing || !active;
2091          toolBar.buttonDebugSkipStepOver.disabled     = unavailable || executing;
2092          // toolBar.buttonDebugSkipStepOutItem.disabled  = unavailable || executing;
2093       }
2094       if((Designer)GetActiveDesigner())
2095       {
2096          CodeEditor codeEditor = ((Designer)GetActiveDesigner()).codeEditor;
2097          if(codeEditor)
2098             codeEditor.AdjustDebugMenus(unavailable, bpNoToggle, executing);
2099       }
2100    }
2101
2102    void ChangeFileDialogsDirectory(char * directory, bool saveSettings)
2103    {
2104       char tempString[MAX_LOCATION];
2105       strcpy(tempString, directory);
2106       if(saveSettings && !projectView)
2107       {
2108          ideSettings.ideFileDialogLocation = directory;
2109          settingsContainer.Save();
2110       }
2111
2112       ideFileDialog.currentDirectory = tempString;
2113       codeEditorFileDialog.currentDirectory = tempString;
2114       codeEditorFormFileDialog.currentDirectory = tempString;
2115    }
2116
2117    void ChangeProjectFileDialogDirectory(char * directory)
2118    {
2119       ideSettings.ideProjectFileDialogLocation = directory;
2120       settingsContainer.Save();
2121    }
2122
2123    Window FindWindow(char * filePath)
2124    {
2125       Window document = null;
2126
2127       // TOCHECK: Do we need to change slashes here?
2128       for(document = firstChild; document; document = document.next)
2129       {
2130          char * fileName = document.fileName;
2131          if(document.isDocument && fileName && !fstrcmp(fileName, filePath))
2132          {
2133             document.visible = true;
2134             document.Activate();
2135             return document;
2136          }
2137       }
2138       return null;
2139    }
2140
2141    bool DontTerminateDebugSession(char * title)
2142    {
2143       if(debugger.isActive)
2144       {
2145          if(MessageBox { type = yesNo, master = ide, 
2146                            contents = $"Do you want to terminate the debugging session in progress?", 
2147                            text = title }.Modal() == no)
2148             return true;
2149          /*
2150          MessageBox msg { type = yesNo, master = ide, 
2151                            contents = "Do you want to terminate the debugging session in progress?", 
2152                            text = title };
2153          if(msg.Modal() == no)
2154          {
2155             msg.Destroy(0);
2156             delete msg;
2157             return true;
2158          }
2159          msg.Destroy(0);
2160          delete msg;*/
2161       }
2162       return false;
2163    }
2164
2165    Window OpenFile(char * origFilePath, WindowState state, bool visible, char * type, OpenCreateIfFails createIfFails, OpenMethod openMethod, bool noParsing)
2166    {
2167       char extension[MAX_EXTENSION] = "";
2168       Window document = null;
2169       bool isProject = false;
2170       bool needFileModified = true;
2171       char winFilePath[MAX_LOCATION];
2172       char * filePath = strstr(origFilePath, "http://") == origFilePath ? strcpy(winFilePath, origFilePath) : GetSystemPathBuffer(winFilePath, origFilePath);
2173
2174       if(!type)
2175       {
2176          GetExtension(filePath, extension);
2177          strlwr(extension);
2178       }
2179       else
2180          strcpy(extension, type);
2181
2182       if(strcmp(extension, ProjectExtension))
2183       {
2184          for(document = firstChild; document; document = document.next)
2185          {
2186             char * fileName = document.fileName;
2187             if(document.isDocument && fileName && !fstrcmp(fileName, filePath) && document.created)
2188             {
2189                document.visible = true;
2190                if(visible)
2191                   document.Activate();
2192                return document;
2193             }
2194          }
2195       }
2196
2197       if(createIfFails == whatever)
2198          ;
2199       else if(!strcmp(extension, ProjectExtension) || !strcmp(extension, WorkspaceExtension))
2200       {
2201          needFileModified = false;
2202          if(openMethod == normal)
2203          {
2204             if(DontTerminateDebugSession($"Open Project"))
2205                return null;
2206             isProject = true;
2207             if(ProjectClose())
2208             {
2209                if(!projectView)
2210                {
2211                   for(;;)
2212                   {
2213                      Project project;
2214                      Workspace workspace = null;
2215                      
2216                      if(FileExists(filePath))
2217                      {
2218                         if(!strcmp(extension, ProjectExtension))
2219                         {
2220                            char workspaceFile[MAX_LOCATION];
2221                            strcpy(workspaceFile, filePath);
2222                            ChangeExtension(workspaceFile, WorkspaceExtension, workspaceFile);
2223                            workspace = LoadWorkspace(workspaceFile, filePath);
2224                         }
2225                         else if(!strcmp(extension, WorkspaceExtension))
2226                            workspace = LoadWorkspace(filePath, null);
2227                         else
2228                            return null;
2229                         //project = LoadProject(filePath, null);
2230                      }
2231
2232                      if(workspace)
2233                      {
2234                         char absolutePath[MAX_LOCATION];
2235                         CreateProjectView(workspace, filePath);
2236                         document = projectView;
2237
2238                         workspace.DropInvalidBreakpoints();
2239                         workspace.Save();
2240
2241                         ide.projectView.ShowOutputBuildLog(true);
2242                         {
2243                            CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
2244                            ide.projectView.DisplayCompiler(compiler, false);
2245                            delete compiler;
2246                         }
2247                         UpdateCompilerConfigs(false);
2248                         UpdateMakefiles();
2249                         {
2250                            char newWorkingDir[MAX_LOCATION];
2251                            StripLastDirectory(filePath, newWorkingDir);
2252                            ChangeFileDialogsDirectory(newWorkingDir, false);
2253                         }
2254                         if(document)
2255                            document.fileName = filePath;
2256
2257                         ideMainFrame.SetText("%s - %s", filePath, titleECEREIDE);
2258
2259                         // this crashes on starting ide with epj file, solution please?
2260                         // app.UpdateDisplay();
2261
2262                         workspace.holdTracking = true;
2263                         for(ofi : workspace.openedFiles)
2264                         {
2265                            if(ofi.state != closed)
2266                            {
2267                               Window file = OpenFile(ofi.path, normal, true, null, no, normal, noParsing);
2268                               if(file)
2269                               {
2270                                  char fileName[MAX_LOCATION];
2271                                  ProjectNode node;
2272                                  GetLastDirectory(ofi.path, fileName);
2273                                  node = projectView.project.topNode.Find(fileName, true);
2274                                  if(node)
2275                                     node.EnsureVisible();
2276                               }
2277                            }
2278                         }
2279                         ide.RepositionWindows(false);
2280                         workspace.holdTracking = false;
2281
2282                         workspace.timer.Start();
2283
2284                         findInFilesDialog.mode = FindInFilesMode::project;
2285                         findInFilesDialog.currentDirectory = ide.project.topNode.path;
2286                         
2287                         {
2288                            char location[MAX_LOCATION];
2289                            StripLastDirectory(ide.project.topNode.path, location);
2290                            ChangeProjectFileDialogDirectory(location);
2291                         }
2292                         
2293                         /*
2294                         if(projectView.debugger)
2295                            projectView.debugger.EvaluateWatches();
2296                         */
2297                         
2298                         break;
2299                      }
2300                      else 
2301                      {
2302                         if(MessageBox { type = yesNo, master = this, text = $"Error opening project", contents = $"Open a different project?" }.Modal() == yes)
2303                         {
2304                            ideProjectFileDialog.text = openProjectFileDialogTitle;
2305                            if(ideProjectFileDialog.Modal() == cancel)
2306                               return null;
2307                            filePath = ideProjectFileDialog.filePath;
2308                            GetExtension(filePath, extension);
2309                         }
2310                         else
2311                            return null;
2312                      }
2313                   }
2314                }
2315             }
2316             else
2317                return null;
2318          }
2319          else if(openMethod == add)
2320          {
2321             if(workspace)
2322             {
2323                Project prj = null;
2324                char slashFilePath[MAX_LOCATION];
2325                GetSlashPathBuffer(slashFilePath, filePath);
2326                for(p : workspace.projects)
2327                {
2328                   if(!fstrcmp(p.filePath, slashFilePath))
2329                   {
2330                      prj = p;
2331                      break;
2332                   }
2333                }
2334                if(prj)
2335                {
2336                   MessageBox { type = ok, parent = parent, master = this, text = $"Same Project", 
2337                         contents = $"This project is already present in workspace." }.Modal();
2338                }
2339                else
2340                {
2341                   prj = LoadProject(filePath, null);
2342                   if(prj)
2343                   {
2344                      char * activeConfigName = null;
2345                      CompilerConfig compiler = ideSettings.GetCompilerConfig(workspace.compiler);
2346                      prj.StartMonitoring();
2347                      workspace.projects.Add(prj);
2348                      if(toolBar.activeConfig.currentRow && toolBar.activeConfig.currentRow != toolBar.activeConfig.firstRow &&
2349                            toolBar.activeConfig.currentRow.string && toolBar.activeConfig.currentRow.string[0])
2350                         activeConfigName = toolBar.activeConfig.currentRow.string;
2351                      if(activeConfigName)
2352                      {
2353                         for(cfg : prj.configurations)
2354                         {
2355                            if(cfg.name && !strcmp(cfg.name, activeConfigName))
2356                            {
2357                               prj.config = cfg;
2358                               break;
2359                            }
2360                         }
2361                      }
2362                      if(projectView)
2363                         projectView.AddNode(prj.topNode, null);
2364                      workspace.modified = true;
2365                      workspace.Save();
2366                      findInFilesDialog.AddProjectItem(prj);
2367                      projectView.ShowOutputBuildLog(true);
2368                      projectView.DisplayCompiler(compiler, false);
2369                      projectView.ProjectUpdateMakefileForAllConfigs(prj);
2370                      delete compiler;
2371
2372                      {
2373                         char location[MAX_LOCATION];
2374                         StripLastDirectory(prj.topNode.path, location);
2375                         ChangeProjectFileDialogDirectory(location);
2376                      }
2377
2378                      // projectView is associated with the main project and not with the one just added but
2379                      return projectView; // just to let the caller know something was opened
2380                   }
2381                }
2382             }
2383             else
2384                return null;
2385          }
2386       }
2387       else if(!strcmp(extension, "bmp") || !strcmp(extension, "pcx") ||
2388             !strcmp(extension, "jpg") || !strcmp(extension, "gif") ||
2389             !strcmp(extension, "jpeg") || !strcmp(extension, "png"))
2390       {
2391          if(FileExists(filePath))
2392             document = PictureEdit { hasMaximize = true, hasMinimize = true, hasClose = true, borderStyle = sizable, 
2393                                        hasVertScroll = true, hasHorzScroll = true, parent = this, state = state, 
2394                                        visible = visible, bitmapFile = filePath, OnClose = PictureEditOnClose/*why?--GenericDocumentOnClose*/;
2395                                     };
2396          if(!document)
2397             MessageBox { type = ok, master = this, text = filePath, contents = $"File doesn't exist." }.Modal();
2398       }
2399 #ifndef NO3D
2400       else if(!strcmp(extension, "3ds"))
2401       {
2402          if(FileExists(filePath))
2403             document = ModelView { hasMaximize = true, hasMinimize = true, hasClose = true, borderStyle = sizable, 
2404                                     hasVertScroll = true, hasHorzScroll = true, parent = this, state = state, 
2405                                     visible = visible, modelFile = filePath, OnClose = ModelViewOnClose/*why?--GenericDocumentOnClose*/
2406                                     };
2407
2408          if(!document)
2409             MessageBox { type = ok, master = this, text = filePath, contents = $"File doesn't exist." }.Modal();
2410       }
2411 #endif
2412       else if(!strcmp(extension, "txt") || !strcmp(extension, "text") ||
2413             !strcmp(extension, "nfo") || !strcmp(extension, "info") ||
2414             !strcmp(extension, "htm") || !strcmp(extension, "html") ||
2415             !strcmp(extension, "css") || !strcmp(extension, "php") ||
2416             !strcmp(extension, "js"))
2417       {
2418          CodeEditor editor { parent = this, state = state, visible = false, noParsing = noParsing };
2419          editor.updatingCode = true;
2420          if(editor.LoadFile(filePath))
2421          {
2422             document = editor;
2423             editor.visible = true;
2424          }
2425          else
2426             delete editor;
2427          needFileModified = false;
2428       }
2429       else
2430       {
2431          CodeEditor editor { parent = this, state = state, visible = false, noParsing = noParsing };
2432          if(editor.LoadFile(filePath))
2433          {
2434             document = editor;
2435             editor.visible = true;
2436          }
2437          else
2438             delete editor;
2439          needFileModified = false;
2440       }
2441
2442       if(document && (document._class == class(PictureEdit) ||
2443             document._class == class(ModelView)))
2444       {
2445          document.Create();
2446          if(document)
2447          {
2448             document.fileName = filePath;
2449             if(workspace && !workspace.holdTracking)
2450                workspace.UpdateOpenedFileInfo(filePath, opened);
2451          }
2452       }
2453       
2454       if(!document && createIfFails != no)
2455       {
2456          if(createIfFails != yes && !needFileModified && 
2457                MessageBox { type = yesNo, master = this, text = filePath, contents = $"File doesn't exist. Create?" }.Modal() == yes)
2458             createIfFails = yes;
2459          if(createIfFails == yes || createIfFails == whatever)
2460          {
2461             document = (Window)NewCodeEditor(this, state, true);
2462             if(document)
2463                document.fileName = filePath;
2464          }
2465       }
2466
2467       if(document)
2468       {
2469          if(projectView && document._class == class(CodeEditor) && workspace)
2470          {
2471             int lineNumber, position;
2472             Point scroll;
2473             CodeEditor editor = (CodeEditor)document;
2474             editor.openedFileInfo = workspace.UpdateOpenedFileInfo(filePath, opened);
2475             editor.openedFileInfo.holdTracking = true;
2476             lineNumber = Max(editor.openedFileInfo.lineNumber - 1, 0);
2477             position = Max(editor.openedFileInfo.position - 1, 0);
2478             editor.editBox.GoToLineNum(lineNumber);
2479             editor.editBox.GoToPosition(editor.editBox.line, lineNumber, position);
2480             scroll.x = Max(editor.openedFileInfo.scroll.x, 0);
2481             scroll.y = Max(editor.openedFileInfo.scroll.y, 0);
2482             editor.editBox.scroll = scroll;
2483             editor.openedFileInfo.holdTracking = false;
2484          }
2485          
2486          if(needFileModified)
2487             document.OnFileModified = OnFileModified;
2488          document.NotifySaved = DocumentSaved;
2489          
2490          if(isProject)
2491             ideSettings.AddRecentProject(document.fileName);
2492          else
2493             ideSettings.AddRecentFile(document.fileName);
2494          ide.UpdateRecentMenus();
2495          ide.AdjustFileMenus();
2496          settingsContainer.Save();
2497          
2498          return document;
2499       }
2500       else
2501          return null;
2502    }
2503
2504    // TOCHECK: I can't use a generic one for both ModelView and PictureEdit both derived from Window
2505    /*bool Window::GenericDocumentOnClose(bool parentClosing)
2506    {
2507       if(!parentClosing && ide.workspace)
2508          ide.workspace.UpdateOpenedFileInfo(fileName, unknown);
2509       return true;
2510    }*/
2511    bool ModelView::ModelViewOnClose(bool parentClosing)
2512    {
2513       if(!parentClosing && ide.workspace)
2514          ide.workspace.UpdateOpenedFileInfo(fileName, unknown);
2515       return true;
2516    }
2517    bool PictureEdit::PictureEditOnClose(bool parentClosing)
2518    {
2519       if(!parentClosing && ide.workspace)
2520          ide.workspace.UpdateOpenedFileInfo(fileName, unknown);
2521       return true;
2522    }
2523
2524    /*
2525    void OnUnloadGraphics(Window window)
2526    {
2527       display.ClearMaterials();
2528       display.ClearTextures();
2529       display.ClearMeshes();
2530    }
2531    */
2532
2533    bool OnActivate(bool active, Window swap, bool * goOnWithActivation, bool direct)
2534    {
2535       caps.color = app.GetKeyState(capsState) ? black : Color { 128,128,128 };
2536       num.color = app.GetKeyState(numState) ? black : Color { 128,128,128 };
2537       return true;
2538    }
2539
2540    bool OnKeyDown(Key key, unichar ch)
2541    {
2542       switch(key)
2543       {
2544          case b:
2545             projectView.Update(null);
2546             break;
2547          case capsLock:
2548             caps.color = app.GetKeyState(capsState) ? black : Color { 128,128,128 };
2549             break;
2550          case numLock:
2551             num.color = app.GetKeyState(numState) ? black : Color { 128,128,128 };
2552             break;
2553       }
2554       return true;
2555    }
2556
2557    void GoToError(const char * line, bool noParsing)
2558    {
2559       if(projectView)
2560          projectView.GoToError(line, noParsing);
2561    }
2562
2563    void CodeLocationParseAndGoTo(const char * text, Project project, const char * dir)
2564    {
2565       char *s = null;
2566       char *path = text;
2567       char *colon = strchr(text, ':');
2568       char filePath[MAX_LOCATION] = "";
2569       char completePath[MAX_LOCATION];
2570       int line = 0, col = 0;
2571       int len = strlen(text);
2572       Project prj = null;
2573       FileAttribs fileAttribs;
2574
2575       // support for valgrind output
2576       if((s = strstr(text, "==")) && (s = strstr(s+2, "==")) && (s = strstr(s+2, ":")) && (s = strstr(s+1, ":")))
2577       {
2578          colon = s;
2579          for(; s>text; s--)
2580          {
2581             if(*s == '(')
2582             {
2583                path = s+1;
2584                break;
2585             }
2586          }
2587          /*for(s=colon; *s; s++)
2588          {
2589             if(*s == ')')
2590             {
2591                *s = '\0';;
2592                break;
2593             }
2594          }*/
2595          //*colon = '\0';
2596          //line = atoi(colon+1);
2597       }
2598       // support for "Found n match(es) in "file/path";
2599       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)
2600       {
2601          path = s+1;
2602       }
2603       else
2604       {
2605          if(colon && (colon[1] == '/' || colon[1] == '\\'))
2606          {
2607             path = (colon - 1 > path) ? colon - 1 : path;
2608             colon = strstr(colon + 1, ":");
2609          }
2610          if(*path == '*' && (s = strchr(path+1, '*')))
2611             path = s+1;
2612          while(isspace(*path)) path++;
2613       }
2614       if(*path == '(')
2615       {
2616          char * close = strchr(path, ')');
2617          if(close)
2618          {
2619             char name[256];
2620             strncpy(name, path+1, close - path - 1);
2621             name[close - path - 1] = '\0';
2622             for(p : ide.workspace.projects)
2623             {
2624                if(!strcmp(p.name, name))
2625                {
2626                   path = close + 1;
2627                   prj = p;
2628                   break;
2629                }
2630             }
2631          }
2632       }
2633       if(!prj)
2634          prj = project ? project : (dir ? null : ide.project);
2635       if(colon)
2636       {
2637          strncpy(filePath, path, colon - path);
2638          filePath[colon - path] = '\0';
2639          line = atoi(colon + 1);
2640          colon = strstr(colon + 1, ":");
2641          if(colon)
2642             col = atoi(colon + 1);
2643       }
2644       else if(path - 1 >= text && *(path - 1) == '\"')
2645       {
2646          colon = strchr(path, '\"');
2647          if(colon)
2648          {
2649             strncpy(filePath, path, colon - path);
2650             filePath[colon - path] = '\0';
2651          }
2652       }
2653       else if(path && !colon)
2654       {
2655          strcpy(filePath, path);
2656       }
2657
2658       if(filePath[0])
2659       {
2660          if(prj)
2661             strcpy(completePath, prj.topNode.path);
2662          else if(dir && dir[0])
2663             strcpy(completePath, dir);
2664          else
2665             completePath[0] = '\0';
2666          PathCat(completePath, filePath);
2667
2668          if((fileAttribs = FileExists(completePath)))
2669             CodeLocationGoTo(completePath, fileAttribs, line, col);
2670          else if(ide.workspace)
2671          {
2672             bool done = false;
2673             for(p : ide.workspace.projects)
2674             {
2675                strcpy(completePath, p.topNode.path);
2676                PathCat(completePath, filePath);
2677                if((fileAttribs = FileExists(completePath)).isFile)
2678                {
2679                   CodeLocationGoTo(completePath, fileAttribs, line, col);
2680                   done = true;
2681                   break;
2682                }
2683             }
2684             if(!done)
2685             {
2686                for(p : ide.workspace.projects)
2687                {
2688                   ProjectNode node = p.topNode.Find(filePath, false);
2689                   if(node)
2690                   {
2691                      node.GetFullFilePath(completePath);
2692                      if((fileAttribs = FileExists(completePath)).isFile)
2693                      {
2694                         CodeLocationGoTo(completePath, fileAttribs, line, col);
2695                         break;
2696                      }
2697                   }
2698                }
2699             }
2700          }
2701       }
2702    }
2703
2704    void CodeLocationGoTo(const char * path, const FileAttribs fileAttribs, int line, int col)
2705    {
2706       if(fileAttribs.isFile)
2707       {
2708          char ext[MAX_EXTENSION];
2709          GetExtension(path, ext);
2710          if(!strcmp(ext, "mp3") || !strcmp(ext, "flac") || !strcmp(ext, "ogg") || !strcmp(ext, "avi") || !strcmp(ext, "mkv"))
2711             ShellOpen(path);
2712          else if(!strcmp(ext, "a") || !strcmp(ext, "o") || !strcmp(ext, "lib") || !strcmp(ext, "dll") || !strcmp(ext, "exe"))
2713          {
2714             char dirPath[MAX_LOCATION];
2715             StripLastDirectory(path, dirPath);
2716             ShellOpen(dirPath);
2717          }
2718          else
2719          {
2720             CodeEditor codeEditor = (CodeEditor)OpenFile(path, normal, true, ext, no, normal, false);
2721             if(codeEditor && line)
2722             {
2723                EditBox editBox = codeEditor.editBox;
2724                editBox.GoToLineNum(line - 1);
2725                editBox.GoToPosition(editBox.line, line - 1, col ? (col - 1) : 0);
2726             }
2727          }
2728       }
2729       else if(fileAttribs.isDirectory)
2730          ShellOpen(path);
2731    }
2732
2733    void OnRedraw(Surface surface)
2734    {
2735       Bitmap bitmap = back.bitmap;
2736       if(bitmap)
2737          surface.Blit(bitmap, (clientSize.w - bitmap.width) / 2, (clientSize.h - bitmap.height) / 2, 0, 0, bitmap.width, bitmap.height);
2738    }
2739
2740    void SheetSelected(SheetType sheetSelected)
2741    {
2742       if(activeChild == sheet)
2743       {
2744          if(sheetSelected == methods)
2745          {
2746             viewPropertiesItem.accelerator = f4;
2747             viewPropertiesItem.parent = viewMenu;
2748             viewMethodsItem.parent = null;
2749          }
2750          else
2751          {
2752             viewMethodsItem.accelerator = f4;
2753             viewMethodsItem.parent = viewMenu;
2754             viewPropertiesItem.parent = null;
2755          }
2756       }
2757       else
2758       {
2759          viewMethodsItem.parent = viewMenu;
2760          viewPropertiesItem.parent = viewMenu;
2761          if(sheetSelected == methods)
2762          {
2763             viewMethodsItem.accelerator = f4;
2764             viewPropertiesItem.accelerator = 0;
2765          }
2766          else
2767          {
2768             viewMethodsItem.accelerator = 0;
2769             viewPropertiesItem.accelerator = f4;
2770          }
2771       }
2772    }
2773
2774    void OnActivateClient(Window client, Window previous)
2775    {
2776       //if(!client || client != previous)
2777       {
2778          Class dataType;
2779          if(!client || client != previous)
2780          {
2781             if(previous)
2782                dataType = previous._class;
2783             if(previous && !strcmp(dataType.name, "CodeEditor"))
2784             {
2785                ((CodeEditor)previous).UpdateFormCode();
2786             }
2787             else if(previous && !strcmp(dataType.name, "Designer"))
2788             {
2789                ((Designer)previous).codeEditor.UpdateFormCode();
2790             }
2791          }
2792
2793          if(client)
2794             dataType = client._class;
2795          if(client && !strcmp(dataType.name, "CodeEditor"))
2796          {
2797             CodeEditor codeEditor = (CodeEditor)client;
2798             SetPrivateModule(codeEditor.privateModule);
2799             SetCurrentContext(codeEditor.globalContext);
2800             SetTopContext(codeEditor.globalContext);
2801             SetGlobalContext(codeEditor.globalContext);
2802             
2803             SetDefines(&codeEditor.defines);
2804             SetImports(&codeEditor.imports);
2805
2806             SetActiveDesigner(codeEditor.designer);
2807             
2808             sheet.codeEditor = codeEditor;
2809             toolBox.codeEditor = codeEditor;
2810
2811             viewDesignerItem.parent = viewMenu;
2812             if(activeChild != codeEditor)
2813             {
2814                viewCodeItem.parent = viewMenu;
2815                viewDesignerItem.accelerator = 0;
2816                viewCodeItem.accelerator = f8;
2817             }
2818             else
2819             {
2820                viewCodeItem.parent = null;
2821                viewDesignerItem.accelerator = f8;
2822             }
2823          }
2824          else if(client && !strcmp(dataType.name, "Designer"))
2825          {
2826             CodeEditor codeEditor = ((Designer)client).codeEditor;
2827             if(codeEditor)
2828             {
2829                SetPrivateModule(codeEditor.privateModule);
2830                SetCurrentContext(codeEditor.globalContext);
2831                SetTopContext(codeEditor.globalContext);
2832                SetGlobalContext(codeEditor.globalContext);
2833                SetDefines(&codeEditor.defines);
2834                SetImports(&codeEditor.imports);
2835             }
2836             else
2837             {
2838                SetPrivateModule(null);
2839                SetCurrentContext(null);
2840                SetTopContext(null);
2841                SetGlobalContext(null);
2842                SetDefines(null);
2843                SetImports(null);
2844             }
2845
2846             SetActiveDesigner((Designer)client);
2847
2848             sheet.codeEditor = codeEditor;
2849             toolBox.codeEditor = codeEditor;
2850
2851             viewCodeItem.parent = viewMenu;
2852             if(activeChild != client)
2853             {
2854                viewDesignerItem.parent = viewMenu;
2855                viewDesignerItem.accelerator = f8;
2856                viewCodeItem.accelerator = 0;
2857             }
2858             else
2859             {
2860                viewDesignerItem.parent = null;
2861                viewCodeItem.accelerator = f8;
2862             }
2863          }
2864          else
2865          {
2866             if(sheet)
2867                sheet.codeEditor = null;
2868             toolBox.codeEditor = null;
2869             SetActiveDesigner(null);
2870
2871             viewDesignerItem.parent = null;
2872             viewCodeItem.parent = null;
2873          }
2874          if(sheet)
2875             SheetSelected(sheet.sheetSelected);
2876       }
2877
2878       projectCompileItem = null;
2879
2880       if(statusBar)
2881       {
2882          statusBar.Clear();
2883          if(client && client._class == eSystem_FindClass(__thisModule, "CodeEditor")) // !strcmp(client._class.name, "CodeEditor")
2884          {
2885             CodeEditor codeEditor = (CodeEditor)client;
2886             EditBox editBox = codeEditor.editBox;
2887
2888             statusBar.AddField(pos);
2889
2890             caps = { width = 40, text = $"CAPS", color = app.GetKeyState(capsState) ? black : Color { 128, 128, 128 } };
2891             statusBar.AddField(caps);
2892
2893             ovr = { width = 30, text = $"OVR", color = (editBox && editBox.overwrite) ? black : Color { 128, 128, 128 } };
2894             statusBar.AddField(ovr);
2895
2896             num = { width = 30, text = $"NUM", color = app.GetKeyState(numState) ? black : Color { 128, 128, 128 } };
2897             statusBar.AddField(num);
2898
2899             //statusBar.text = "Ready";
2900
2901             if(projectView && projectView.project)
2902             {
2903                bool isCObject = false;
2904                ProjectNode node = projectView.GetNodeFromWindow(client, null, false);
2905                if(!node && (node = projectView.GetNodeFromWindow(client, null, true)))
2906                   isCObject = true;
2907                if(node)
2908                {
2909                   char nodeName[MAX_FILENAME];
2910                   char name[MAX_FILENAME+96];
2911                   if(isCObject)
2912                      ChangeExtension(node.name, "c", nodeName);
2913                   sprintf(name, $"Compile %s", isCObject ? nodeName : node.name);
2914                   projectCompileItem = 
2915                   {
2916                      copyText = true, text = name, c, ctrlF7, disabled = projectView.buildInProgress;
2917
2918                      bool NotifySelect(MenuItem selection, Modifiers mods)
2919                      {
2920                         if(projectView)
2921                         {
2922                            bool result = false;
2923                            bool isCObject = false;
2924                            ProjectNode node = null;
2925                            for(p : ide.workspace.projects)
2926                            {
2927                               node = projectView.GetNodeFromWindow(activeClient, p, false);
2928                               if(node) break;
2929                            }
2930                            if(!node && (node = projectView.GetNodeFromWindow(activeClient, null, true)))
2931                               isCObject = true;
2932                            if(node)
2933                            {
2934                               List<ProjectNode> nodes { };
2935                               nodes.Add(node);
2936                               projectView.Compile(node.project, nodes, mods.ctrl && mods.shift, isCObject ? cObject : normal);
2937                               delete nodes;
2938                               result = true;
2939                            }
2940                            if(!result && node)
2941                               ide.outputView.buildBox.Logf($"File %s is excluded from current build configuration.\n", node.name);
2942                         }
2943                         return true;
2944                      }
2945                   };
2946                   projectMenu.AddDynamic(projectCompileItem, ide, false);
2947                }
2948             }
2949          }
2950          else
2951          {
2952             caps = ovr = num = null;
2953          }
2954       }
2955    }
2956
2957    bool OnClose(bool parentClosing)
2958    {
2959       //return !projectView.buildInProgress;
2960       if(projectView && projectView.buildInProgress)
2961          return false;
2962       if(DontTerminateDebugSession($"Close IDE"))
2963          return false;
2964       if(findInFilesDialog)
2965          findInFilesDialog.SearchStop();
2966       if(workspace)
2967       {
2968          workspace.timer.Stop();
2969          workspace.Save();
2970       }
2971       ideMainFrame.Destroy(0);
2972       return true;
2973    }
2974
2975    bool OnPostCreate()
2976    {
2977       int c;
2978       bool passThrough = false;
2979       bool debugStart = false;
2980       bool debugWorkDir = false;
2981       char * passDebugWorkDir = null;
2982       bool openAsText = false;
2983       DynamicString passArgs { };
2984       int ptArg = 0;
2985
2986       for(c = 1; c<app.argc; c++)
2987       {
2988          if(passThrough)
2989          {
2990             char * arg = app.argv[c];
2991             char * buf = new char[strlen(arg)*2+1];
2992             if(ptArg++ > 0)
2993                passArgs.concat(" ");
2994             PassArg(buf, arg);
2995             passArgs.concat(buf);
2996             delete buf;
2997          }
2998          else if(debugWorkDir)
2999          {
3000             passDebugWorkDir = CopyString(app.argv[c]);
3001             StripQuotes(passDebugWorkDir, passDebugWorkDir);
3002             debugWorkDir = false;
3003          }
3004          else if(!strcmp(app.argv[c], "-t"))
3005             openAsText = true;
3006          else if(!strcmp(app.argv[c], "-no-parsing"))
3007             ide.noParsing = true;
3008          else if(!strcmp(app.argv[c], "-debug-start"))
3009             debugStart = true;
3010          else if(!strcmp(app.argv[c], "-debug-work-dir"))
3011             debugWorkDir = true;
3012          else if(!strcmp(app.argv[c], "-@"))
3013             passThrough = true;
3014          else
3015          {
3016             char fullPath[MAX_LOCATION];
3017             char parentPath[MAX_LOCATION];
3018             char ext[MAX_EXTENSION];
3019             bool isProject;
3020             FileAttribs dirAttribs;
3021             GetWorkingDir(fullPath, MAX_LOCATION);
3022             PathCat(fullPath, app.argv[c]);
3023             StripLastDirectory(fullPath, parentPath);
3024             GetExtension(app.argv[c], ext);
3025             isProject = !openAsText && !strcmpi(ext, "epj");
3026
3027             if(isProject && c > (debugStart ? 2 : 1)) continue;
3028
3029             // Create directory for projects (only)
3030             if(((dirAttribs = FileExists(parentPath)) && dirAttribs.isDirectory) || isProject)
3031             {
3032                if(isProject && !FileExists(fullPath))
3033                {
3034                   char name[MAX_LOCATION];
3035                   NewProjectDialog newProjectDialog;
3036
3037                   if(projectView)
3038                   {
3039                      projectView.visible = false;
3040                      if(!projectView.Destroy(0))
3041                         return true;
3042                   }
3043
3044                   newProjectDialog = { master = this };
3045
3046                   strcpy(name, app.argv[c]);
3047                   StripExtension(name);
3048                   GetLastDirectory(name, name);
3049                   newProjectDialog.projectName.contents = name;
3050                   newProjectDialog.projectName.NotifyModified(newProjectDialog, newProjectDialog.projectName);
3051                   newProjectDialog.locationEditBox.path = parentPath;
3052                   newProjectDialog.NotifyModifiedLocation(newProjectDialog.locationEditBox);
3053
3054                   incref newProjectDialog;
3055                   newProjectDialog.Modal();
3056                   if(projectView)
3057                   {
3058                      ideSettings.AddRecentProject(projectView.fileName);
3059                      ide.UpdateRecentMenus();
3060                      settingsContainer.Save();
3061                   }
3062                   delete newProjectDialog;
3063                   // Open only one project
3064                   break;
3065                }
3066                else
3067                   ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, openAsText ? "txt" : null, yes, normal, false);
3068             }
3069             else if(strstr(fullPath, "http://") == fullPath)
3070                ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, openAsText ? "txt" : null, yes, normal, false);
3071          }
3072       }
3073       if(passThrough && projectView && projectView.project && workspace)
3074          workspace.commandLineArgs = passArgs;
3075       if(passDebugWorkDir && projectView && projectView.project && workspace)
3076       {
3077          workspace.debugDir = passDebugWorkDir;
3078          delete passDebugWorkDir;
3079       }
3080       if(debugStart)
3081          ;//MenuDebugStart(debugStartResumeItem, 0); // <-- how TODO this without getting into the app.Wait lock
3082
3083       UpdateToolBarActiveConfigs(false);
3084       UpdateToolBarActiveCompilers();
3085       delete passArgs;
3086       return true;
3087    }
3088
3089    void OnDestroy()
3090    {
3091       // IS THIS NEEDED? WASN'T HERE BEFORE...  Crashes on getting node's projectView otherwise
3092       if(projectView)
3093       {
3094          projectView.visible = false;
3095          projectView.Destroy(0);
3096          projectView = null;
3097       }
3098 #ifdef GDB_DEBUG_GUI
3099       gdbDialog.Destroy(0);
3100       delete gdbDialog;
3101 #endif
3102    }
3103
3104    void SetPath(bool projectsDirs, CompilerConfig compiler, ProjectConfig config, int bitDepth)
3105    {
3106       int c, len, count;
3107       char * newList;
3108       char * oldPaths[128];
3109       String oldList = new char[maxPathLen];
3110       Array<String> newExePaths { };
3111       //Map<String, bool> exePathExists { };
3112       bool found = false;
3113 #if defined(__unix__) || defined(__APPLE__)
3114       Array<String> newLibPaths { };
3115       Map<String, bool> libPathExists { };
3116 #endif
3117
3118       if(projectsDirs)
3119       {
3120          for(prj : workspace.projects)
3121          {
3122             DirExpression targetDirExp;
3123
3124             // SKIP FIRST PROJECT...
3125             if(prj == workspace.projects.firstIterator.data) continue;
3126
3127             // NOTE: Right now the additional project config dir will be
3128             //       obtained when the debugger is started, so toggling it
3129             //       while building will change which library gets used.
3130             //       To go with the initial state, e.g. when F5 was pressed,
3131             //       we nould need to keep a list of all project's active
3132             //       config upon startup.
3133             targetDirExp = prj.GetTargetDir(compiler, prj.config, bitDepth);
3134
3135             /*if(prj.config.targetType == sharedLibrary && prj.config.debug)
3136                cfg = prj.config;
3137             else
3138             {
3139                for(cfg = prj.configurations.first; cfg; cfg = cfg.next)
3140                   if(cfg.targetType == sharedLibrary && cfg.debug && !strcmpi(cfg.name, "Debug"))
3141                      break;
3142                if(!cfg)
3143                {
3144                   for(cfg = prj.configurations.first; cfg; cfg = cfg.next)
3145                      if(cfg.targetType == sharedLibrary && cfg.debug)
3146                         break;
3147                }
3148             }*/
3149             if(targetDirExp.dir)
3150             {
3151                char buffer[MAX_LOCATION];
3152 #if defined(__WIN32__)
3153                Array<String> paths = newExePaths;
3154 #else
3155                Array<String> paths = newLibPaths;
3156 #endif
3157                GetSystemPathBuffer(buffer, prj.topNode.path);
3158                PathCat(buffer, targetDirExp.dir);
3159                for(p : paths)
3160                {
3161                   if(!fstrcmp(p, buffer))
3162                   {
3163                      found = true;
3164                      break;
3165                   }
3166                }
3167                if(!found)
3168                   paths.Add(CopyString(buffer));
3169             }
3170             delete targetDirExp;
3171          }
3172       }
3173
3174       for(item : compiler.executableDirs)
3175       {
3176          found = false;
3177          for(p : newExePaths)
3178          {
3179             if(!fstrcmp(p, item))
3180             {
3181                found = true;
3182                break;
3183             }
3184          }
3185          if(!found)
3186             newExePaths.Add(CopySystemPath(item));
3187       }
3188
3189       GetEnvironment("PATH", oldList, maxPathLen);
3190 /*#ifdef _DEBUG
3191       printf("Old value of PATH: %s\n", oldList);
3192 #endif*/
3193       count = TokenizeWith(oldList, sizeof(oldPaths) / sizeof(char *), oldPaths, pathListSep, false);
3194       for(c = 0; c < count; c++)
3195       {
3196          found = false;
3197          for(p : newExePaths)
3198          {
3199             if(!fstrcmp(p, oldPaths[c]))
3200             {
3201                found = true;
3202                break;
3203             }
3204          }
3205          if(!found)
3206             newExePaths.Add(CopySystemPath(oldPaths[c]));
3207       }
3208
3209       len = 0;
3210       for(path : newExePaths)
3211          len += strlen(path) + 1;
3212       newList = new char[len + 1];
3213       newList[0] = '\0';
3214       for(path : newExePaths)
3215       {
3216          strcat(newList, path);
3217          strcat(newList, pathListSep);
3218       }
3219       newList[len - 1] = '\0';
3220       SetEnvironment("PATH", newList);
3221 /*#ifdef _DEBUG
3222       printf("New value of PATH: %s\n", newList);
3223 #endif*/
3224       delete newList;
3225
3226       newExePaths.Free();
3227       delete newExePaths;
3228
3229 #if defined(__unix__) || defined(__APPLE__)
3230
3231       for(item : compiler.libraryDirs)
3232       {
3233          if(!libPathExists[item])  // fstrcmp should be used
3234          {
3235             String s = CopyString(item);
3236             newLibPaths.Add(s);
3237             libPathExists[s] = true;
3238          }
3239       }
3240
3241 #if defined(__APPLE__)
3242       GetEnvironment("DYLD_LIBRARY_PATH", oldList, maxPathLen);
3243 #else
3244       GetEnvironment("LD_LIBRARY_PATH", oldList, maxPathLen);
3245 #endif
3246 /*#ifdef _DEBUG
3247       printf("Old value of [DY]LD_LIBRARY_PATH: %s\n", oldList);
3248 #endif*/
3249       count = TokenizeWith(oldList, sizeof(oldPaths) / sizeof(char *), oldPaths, pathListSep, false);
3250       for(c = 0; c < count; c++)
3251       {
3252          if(!libPathExists[oldPaths[c]])  // fstrcmp should be used
3253          {
3254             String s = CopyString(oldPaths[c]);
3255             newLibPaths.Add(s);
3256             libPathExists[s] = true;
3257          }
3258       }
3259
3260       len = 0;
3261       for(path : newLibPaths)
3262          len += strlen(path) + 1;
3263       newList = new char[len + 1];
3264       newList[0] = '\0';
3265       for(path : newLibPaths)
3266       {
3267          strcat(newList, path);
3268          strcat(newList, pathListSep);
3269       }
3270       newList[len - 1] = '\0';
3271 #if defined(__APPLE__)
3272       SetEnvironment("DYLD_LIBRARY_PATH", newList);
3273 #else
3274       SetEnvironment("LD_LIBRARY_PATH", newList);
3275 #endif
3276 /*#ifdef _DEBUG
3277       printf("New value of [DY]LD_LIBRARY_PATH: %s\n", newList);
3278 #endif*/
3279       delete newList;
3280
3281       newLibPaths.Free();
3282       delete newLibPaths;
3283       delete libPathExists;
3284 #endif
3285
3286       if(compiler.distccEnabled && compiler.distccHosts)
3287          SetEnvironment("DISTCC_HOSTS", compiler.distccHosts);
3288
3289       delete oldList;
3290    }
3291
3292    void DestroyTemporaryProjectDir()
3293    {
3294       if(tmpPrjDir && tmpPrjDir[0])
3295       {
3296          if(FileExists(tmpPrjDir).isDirectory)
3297             DestroyDir(tmpPrjDir);
3298          property::tmpPrjDir = null;
3299       }
3300    }
3301
3302    IDEWorkSpace()
3303    {
3304       // Graphics Driver Menu
3305       int c;
3306
3307       /*
3308       app.currentSkin.selectionColor = selectionColor;
3309       app.currentSkin.selectionText = selectionText;
3310       */
3311
3312 /*
3313       driverItems = new MenuItem[app.numDrivers];
3314       for(c = 0; c < app.numDrivers; c++)
3315       {
3316          driverItems[c] = MenuItem { driversMenu, app.drivers[c], NotifySelect = NotifySelectDisplayDriver };
3317          driverItems[c].id = c;
3318          driverItems[c].isRadio = true;         
3319       }
3320 */
3321       driverItems = new MenuItem[2];
3322 #if defined(__unix__)
3323          driverItems[0] = MenuItem { driversMenu, "X", NotifySelect = NotifySelectDisplayDriver };
3324          driverItems[0].id = 0;
3325          driverItems[0].isRadio = true;         
3326 #else
3327          driverItems[0] = MenuItem { driversMenu, "GDI", NotifySelect = NotifySelectDisplayDriver };
3328          driverItems[0].id = 0;
3329          driverItems[0].isRadio = true;         
3330 #endif
3331          driverItems[1] = MenuItem { driversMenu, "OpenGL", NotifySelect = NotifySelectDisplayDriver };
3332          driverItems[1].id = 1;
3333          driverItems[1].isRadio = true;         
3334
3335 /*      skinItems = new MenuItem[app.numSkins];
3336       for(c = 0; c < app.numSkins; c++)
3337       {
3338          skinItems[c] = MenuItem {skinsMenu, app.skins[c], NotifySelect = NotifySelectDisplaySkin };
3339          skinItems[c].id = c;
3340          skinItems[c].isRadio = true;         
3341       }
3342 */
3343       ideFileDialog.master = this;
3344       ideProjectFileDialog.master = this;
3345
3346       //SetDriverAndSkin();
3347       return true;
3348    }
3349
3350    void UpdateRecentMenus()
3351    {
3352       int c;
3353       Menu fileMenu = menu.FindMenu($"File");
3354       Menu recentFiles = fileMenu.FindMenu($"Recent Files");
3355       Menu recentProjects = fileMenu.FindMenu($"Recent Projects");
3356       char * itemPath = new char[MAX_LOCATION];
3357       char * itemName = new char[MAX_LOCATION+4];
3358       MenuItem item;
3359
3360       recentFiles.Clear();
3361       c = 0;
3362
3363       for(recent : ideSettings.recentFiles)
3364       {
3365          strncpy(itemPath, recent, MAX_LOCATION); itemPath[MAX_LOCATION-1] = '\0';
3366          MakeSystemPath(itemPath);
3367          snprintf(itemName, MAX_LOCATION+4, "%d %s", 1 + c, itemPath); itemName[MAX_LOCATION+4-1] = '\0';
3368          recentFiles.AddDynamic(MenuItem { copyText = true, text = itemName, (Key)k1 + c, id = c, NotifySelect = ide.FileRecentFile }, ide, true);
3369          c++;
3370       }
3371
3372       recentProjects.Clear();
3373       c = 0;
3374       for(recent : ideSettings.recentProjects)
3375       {
3376          strncpy(itemPath, recent, MAX_LOCATION); itemPath[MAX_LOCATION-1] = '\0';
3377          MakeSystemPath(itemPath);
3378          snprintf(itemName, MAX_LOCATION+4, "%d %s", 1 + c, itemPath); itemName[MAX_LOCATION+4-1] = '\0';
3379          recentProjects.AddDynamic(MenuItem { copyText = true, text = itemName, (Key)k1 + c, id = c, NotifySelect = ide.FileRecentProject }, ide, true);
3380          c++;
3381       }
3382
3383       delete itemPath;
3384       delete itemName;
3385    }
3386
3387    ~IDEWorkSpace()
3388    {
3389       delete driverItems;
3390       delete skinItems;
3391       delete ideSettings;
3392       if(documentor)
3393       {
3394          documentor.Puts("Quit\n");
3395          documentor.Wait();
3396          delete documentor;
3397       }
3398    }
3399 }
3400
3401 void DestroyDir(char * path)
3402 {
3403    RecursiveDeleteFolderFSI fsi { };
3404    fsi.Iterate(path);
3405    delete fsi;
3406 }
3407
3408 #if defined(__WIN32__)
3409 define sdkDirName = "Ecere SDK";
3410 #else
3411 define sdkDirName = "ecere";
3412 #endif
3413
3414 void FindAndShellOpenInstalledFolder(char * name)
3415 {
3416    char * p = new char[MAX_LOCATION];
3417    char * v = new char[maxPathLen];
3418    byte * tokens[256];
3419    int c, numTokens;
3420    Array<String> paths { };
3421    p[0] = v[0] = '\0';
3422    strncpy(p, settingsContainer.moduleLocation, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3423    StripLastDirectory(p, p);
3424    PathCat(p, name);
3425    paths.Add(CopyString(p));
3426 #if defined(__WIN32__)
3427    GetEnvironment("ECERE_SDK_SRC", v, maxPathLen);
3428    if(v[0])
3429    {
3430       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3431       PathCat(p, name); paths.Add(CopyString(p));
3432    }
3433    GetEnvironment("AppData", v, maxPathLen);
3434    if(v[0])
3435    {
3436       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3437       PathCat(p, sdkDirName); PathCat(p, name); paths.Add(CopyString(p));
3438    }
3439    GetEnvironment("ProgramFiles", v, maxPathLen);
3440    if(v[0])
3441    {
3442       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3443       PathCat(p, sdkDirName); PathCat(p, name); paths.Add(CopyString(p));
3444    }
3445    GetEnvironment("ProgramFiles(x86)", v, maxPathLen);
3446    if(v[0])
3447    {
3448       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3449       PathCat(p, sdkDirName); PathCat(p, name); paths.Add(CopyString(p));
3450    }
3451    GetEnvironment("SystemDrive", v, maxPathLen);
3452    if(v[0])
3453    {
3454       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3455       PathCat(p, "Program Files"); PathCat(p, sdkDirName); PathCat(p, name); paths.Add(CopyString(p));
3456    }
3457 #else
3458    GetEnvironment("XDG_DATA_DIRS", v, maxPathLen);
3459    numTokens = TokenizeWith(v, sizeof(tokens) / sizeof(byte *), tokens, ":", false);
3460    for(c=0; c<numTokens; c++)
3461    {
3462       strncpy(p, tokens[c], MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3463       PathCat(p, sdkDirName); PathCat(p, name); paths.Add(CopyString(p));
3464    }
3465 #endif
3466    for(path : paths)
3467    {
3468       strncpy(p, path, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3469       if(FileExists(p).isDirectory)
3470       {
3471          ShellOpen(p);
3472          break;
3473       }
3474    }
3475    delete p;
3476    delete v;
3477    paths.Free();
3478    delete paths;
3479 }
3480
3481 void FindAndShellOpenInstalledFile(char * subdir, char * name)
3482 {
3483    char * p = new char[MAX_LOCATION];
3484    char * v = new char[maxPathLen];
3485    byte * tokens[256];
3486    int c, numTokens;
3487    Array<String> paths { };
3488    p[0] = v[0] = '\0';
3489    strncpy(p, settingsContainer.moduleLocation, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3490    paths.Add(CopyString(p));
3491    StripLastDirectory(p, p);
3492    PathCat(p, subdir);
3493    paths.Add(CopyString(p));
3494 #if defined(__WIN32__)
3495    GetEnvironment("ECERE_SDK_SRC", v, maxPathLen);
3496    if(v[0])
3497    {
3498       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3499       PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
3500    }
3501    GetEnvironment("AppData", v, maxPathLen);
3502    if(v[0])
3503    {
3504       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3505       PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
3506    }
3507    GetEnvironment("ProgramFiles", v, maxPathLen);
3508    if(v[0])
3509    {
3510       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3511       PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
3512    }
3513    GetEnvironment("ProgramFiles(x86)", v, maxPathLen);
3514    if(v[0])
3515    {
3516       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3517       PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
3518    }
3519    GetEnvironment("SystemDrive", v, maxPathLen);
3520    if(v[0])
3521    {
3522       strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3523       PathCat(p, "Program Files"); PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
3524    }
3525 #else
3526    GetEnvironment("XDG_DATA_DIRS", v, maxPathLen);
3527    numTokens = TokenizeWith(v, sizeof(tokens) / sizeof(byte *), tokens, ":", false);
3528    for(c=0; c<numTokens; c++)
3529    {
3530       strncpy(p, tokens[c], MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3531       PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
3532    }
3533 #endif
3534    for(path : paths)
3535    {
3536       strncpy(p, path, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
3537       PathCat(p, name);
3538       if(FileExists(p).isFile)
3539       {
3540          ShellOpen(p);
3541          break;
3542       }
3543    }
3544    delete p;
3545    delete v;
3546    paths.Free();
3547    delete paths;
3548 }
3549
3550 class RecursiveDeleteFolderFSI : NormalFileSystemIterator
3551 {
3552    bool preserveRootFolder;
3553
3554    void OutFolder(char * folderPath, bool isRoot)
3555    {
3556       if(!(preserveRootFolder && isRoot))
3557          RemoveDir(folderPath);
3558    }
3559
3560    bool OnFile(char * filePath)
3561    {
3562       DeleteFile(filePath);
3563       return true;
3564    }
3565 }
3566
3567 class IDEApp : GuiApplication
3568 {
3569    //driver = "Win32Console";
3570    // driver = "OpenGL";
3571    // skin = "Aqua";
3572    //skin = "TVision";
3573
3574    TempFile includeFile { };
3575
3576    bool Init()
3577    {
3578       char ext[MAX_EXTENSION];
3579       SetLoggingMode(stdOut, null);
3580       //SetLoggingMode(debug, null);
3581
3582       settingsContainer.Load();
3583       if(argc > 1 && !strcmpi(GetExtension(argv[1], ext), "3ds"))
3584       {
3585          app.driver = "OpenGL";
3586          ide.driverItems[1].checked = true;
3587       }
3588       else
3589       {
3590 #if defined(__unix__) || defined(__APPLE__)
3591          app.driver = (ideSettings.displayDriver && !strcmp(ideSettings.displayDriver, "OpenGL")) ? ideSettings.displayDriver : "X";
3592 #else
3593          app.driver = (ideSettings.displayDriver && !strcmp(ideSettings.displayDriver, "OpenGL")) ? ideSettings.displayDriver : "GDI";
3594 #endif
3595          ide.driverItems[ideSettings.displayDriver && !strcmp(ideSettings.displayDriver,"OpenGL")].checked = true;
3596       }
3597
3598       SetInIDE(true);
3599
3600       desktop.text = titleECEREIDE;
3601       /*
3602       int c;
3603       for(c = 1; c<app.argc; c++)
3604       {
3605          char fullPath[MAX_LOCATION];
3606          GetWorkingDir(fullPath, MAX_LOCATION);
3607          PathCat(fullPath, app.argv[c]);
3608          ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, null, yes, normal, false);
3609       }
3610       */
3611
3612       if(!LoadIncludeFile())
3613          PrintLn("error: unable to load :crossplatform.mk file inside ide binary.");
3614
3615       return true;
3616    }
3617
3618    bool Cycle(bool idle)
3619    {
3620       if(ide.documentor)
3621       {
3622          if(ide.documentor.Peek())
3623          {
3624             char line[1024];
3625             ide.documentor.GetLine(line, sizeof(line));
3626             if(!strcmpi(line, "Exited"))
3627                delete ide.documentor;
3628          }
3629          if(ide.documentor && ide.documentor.eof)
3630             delete ide.documentor;
3631       }
3632       return true;
3633    }
3634
3635    bool LoadIncludeFile()
3636    {
3637       bool result = false;
3638       File include = FileOpen(":crossplatform.mk", read);
3639       if(include)
3640       {
3641          File f = includeFile;
3642          if(f)
3643          {
3644             for(; !include.Eof(); )
3645             {
3646                char buffer[4096];
3647                int count = include.Read(buffer, 1, 4096);
3648                f.Write(buffer, 1, count);
3649             }
3650             result = true;
3651          }
3652          delete include;
3653       }
3654       return result;
3655    }
3656 }
3657
3658 IDEMainFrame ideMainFrame { };
3659
3660 define app = ((IDEApp)__thisModule);
3661 #ifdef _DEBUG
3662 define titleECEREIDE = $"ECERE IDE (Debug)";
3663 #else
3664 define titleECEREIDE = $"ECERE IDE";
3665 #endif