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