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