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