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