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