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