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