2ad63ca007dccf9eeeebcbf85e65f4c66234bd54
[sdk] / ide / src / ide.ec
1 #ifdef ECERE_STATIC
2 public import static "ecere"
3 public import static "ec"
4 #else
5 public import "ecere"
6 public import "ec"
7 #endif
8
9 import "GlobalSettingsDialog"
10 import "NewProjectDialog"
11 import "FindInFilesDialog"
12
13 #ifdef GDB_DEBUG_GUI
14 import "GDBDialog"
15 #endif
16
17 import "Project"
18 import "ProjectConfig"
19 import "ProjectNode"
20 import "NodeProperties"
21 import "ProjectSettings"
22 import "ProjectView"
23 import "Workspace"
24
25 import "CodeEditor"
26 import "Designer"
27 import "ToolBox"
28 import "Sheet"
29
30 import "Debugger"
31
32 import "OutputView"
33 import "BreakpointsView"
34 import "CallStackView"
35 import "ThreadsView"
36 import "WatchesView"
37
38 #ifndef NO3D
39 import "ModelView"
40 #endif
41 import "PictureEdit"
42
43 import "about"
44
45 import "FileSystemIterator"
46
47 AVLTree<const String> binaryDocExt
48 { [
49    "wav", "mp3", "flac", "ogg",
50    "mid",
51    "avi", "mkv", "mpg", "mpeg",
52    "7z", "zip", "gz", "bz2", "xz", "rar", "z", "tar", "ear",
53    "pdf", "odp", "ods", "odt", "ppt", "doc", "xls", "pptx", "docx", "xlsx"
54 ] };
55
56 #if defined(__WIN32__)
57 define pathListSep = ";";
58 #else
59 define pathListSep = ":";
60 #endif
61
62 define maxPathLen = 65 * MAX_LOCATION;
63
64 class PathBackup : struct
65 {
66    String oldLDPath;
67    String oldPath;
68
69    PathBackup()
70    {
71       oldPath = new char[maxPathLen];
72       oldLDPath = new char[maxPathLen];
73
74       GetEnvironment("PATH", oldPath, maxPathLen);
75 #if defined(__APPLE__)
76       GetEnvironment("DYLD_LIBRARY_PATH", oldLDPath, maxPathLen);
77 #else
78       GetEnvironment("LD_LIBRARY_PATH", oldLDPath, maxPathLen);
79 #endif
80    }
81
82    ~PathBackup()
83    {
84       SetEnvironment("PATH", oldPath);
85 #if defined(__APPLE__)
86       SetEnvironment("DYLD_LIBRARY_PATH", oldLDPath);
87 #else
88       SetEnvironment("LD_LIBRARY_PATH", oldLDPath);
89 #endif
90       delete oldPath;
91       delete oldLDPath;
92    }
93 };
94
95 enum OpenCreateIfFails { no, yes, something, whatever };
96 enum OpenMethod { normal, add };
97
98 static Array<FileFilter> fileFilters
99 { [
100    { $"C/C++/eC Files (*.ec, *.eh, *.c, *.cpp, *.cc, *.cxx, *.h, *.hpp, *.hh, *.hxx)", "ec, eh, c, cpp, cc, cxx, h, hpp, hh, hxx" },
101    { $"Header Files for eC/C/C++ (*.eh, *.h, *.hpp, *.hh, *.hxx)", "eh, h, hpp, hh, hxx" },
102    { $"C/C++/eC Source Files (*.ec, *.c, *.cpp, *.cc, *.cxx)", "ec, c, cpp, cc, cxx" },
103    { $"Text files (*.txt, *.text, *.nfo, *.info)", "txt, text, nfo, info" },
104    { $"Web files (*.html, *.htm, *.xhtml, *.css, *.php, *.js, *.jsi, *.rb, *.xml)", "html, htm, xhtml, css, php, js, jsi, rb, xml" },
105    { $"Image Files (*.jpg, *.jpeg, *.bmp, *.pcx, *.png, *.gif)", "jpg, jpeg, bmp, pcx, png, gif" },
106    { $"3D Studio Model Files (*.3ds)", "3ds" },
107    { $"All files", null }
108 ] };
109
110 static Array<FileType> fileTypes
111 { [
112    { $"Based on extension", null },
113    { $"Text",               "txt" },
114    { $"Image",              "jpg" },
115    { $"3D Studio Model",    "3ds" }
116 ] };
117
118 static Array<FileFilter> projectFilters
119 { [
120    { $"Project Files (*.epj)", ProjectExtension }
121 ] };
122
123 static Array<FileType> projectTypes
124 { [
125    { $"Project File", ProjectExtension }
126 ] };
127
128 static Array<FileFilter> findInFilesFileFilters
129 { [
130    { $"eC Files (*.ec, *.eh)", "ec, eh" },
131    { $"C/C++/eC Files (*.ec, *.eh, *.c, *.cpp, *.cc, *.cxx, *.h, *.hpp, *.hh, *.hxx)", "ec, eh, c, cpp, cc, cxx, h, hpp, hh, hxx" },
132    { $"Header Files for eC/C/C++ (*.eh, *.h, *.hpp, *.hh, *.hxx)", "eh, h, hpp, hh, hxx" },
133    { $"C/C++/eC Source Files (*.ec, *.c, *.cpp, *.cc, *.cxx)", "ec, c, cpp, cc, cxx" },
134    { $"Text files (*.txt)", "txt" },
135    { $"All files", null }
136 ] };
137
138 FileDialog ideFileDialog
139 {
140    type = multiOpen, text = $"Open";
141    types = fileTypes.array, sizeTypes = fileTypes.count * sizeof(FileType), filters = fileFilters.array, sizeFilters = fileFilters.count * sizeof(FileFilter);
142 };
143
144 define openProjectFileDialogTitle = $"Open Project";
145 define addProjectFileDialogTitle = $"Open Additional Project";
146 FileDialog ideProjectFileDialog
147 {
148    type = open;
149    types = projectTypes.array, sizeTypes = projectTypes.count * sizeof(FileType), filters = projectFilters.array, sizeFilters = projectFilters.count * sizeof(FileFilter);
150 };
151
152 GlobalSettingsDialog globalSettingsDialog
153 {
154    ideSettings = ideSettings;
155    settingsContainer = settingsContainer;
156
157    void OnGlobalSettingChange(GlobalSettingsChange globalSettingsChange)
158    {
159       switch(globalSettingsChange)
160       {
161          case editorSettings:
162          {
163             Window child;
164             for(child = ide.firstChild; child; child = child.next)
165             {
166                if(child._class == class(CodeEditor))
167                {
168                   CodeEditor codeEditor = (CodeEditor) child;
169                   codeEditor.editBox.freeCaret = ideSettings.useFreeCaret;
170                   // codeEditor.editBox.lineNumbers = ideSettings.showLineNumbers;
171                   codeEditor.editBox.caretFollowsScrolling = ideSettings.caretFollowsScrolling;
172                   codeEditor.OnPostCreate(); // Update editBox margin size
173                }
174             }
175             break;
176          }
177          case projectOptions:
178             break;
179          case compilerSettings:
180          {
181             ide.UpdateCompilerConfigs(true);
182             break;
183          }
184       }
185    }
186 };
187
188 void DrawLineMarginIcon(Surface surface, BitmapResource resource, int line, int lineH, int scrollY, int boxH)
189 {
190    int lineY;
191    if(line)
192    {
193       lineY = (line - 1) * lineH;
194       if(lineY + lineH > scrollY && lineY + lineH < scrollY + boxH)
195       {
196          Bitmap bitmap = resource.bitmap;
197          if(bitmap)
198             surface.Blit(bitmap, 0, lineY - scrollY + (lineH - bitmap.height) / 2 + 1, 0, 0, bitmap.width, bitmap.height);
199       }
200    }
201 }
202
203 #define IDEItem(x)   (&((IDEWorkSpace)0).x)
204
205 class IDEToolbar : ToolBar
206 {
207    // File options
208    // New
209    ToolButton buttonNewFile { this, toolTip = $"New file", menuItemPtr = IDEItem(fileNewItem) };
210    // Open
211    ToolButton buttonOpenFile { this, toolTip = $"Open file", menuItemPtr = IDEItem(fileOpenItem) };
212    // Close
213    // ToolButton buttonCloseFile { this, toolTip = $"Close file", menuItemPtr = IDEItem(fileCloseItem) };
214    // Save
215    ToolButton buttonSaveFile { this, toolTip = $"Save file", menuItemPtr = IDEItem(fileSaveItem) };
216    // Save All
217    ToolButton buttonSaveAllFile { this, toolTip = $"Save all", menuItemPtr = IDEItem(fileSaveAllItem) };
218
219    ToolSeparator separator1 { this };
220
221    // Edit options
222    // Cut
223    // Copy
224    // Paste
225    // Undo
226    // Redo
227
228    // ToolSeparator separator2 { this };
229
230    // Project options
231    // New project
232    ToolButton buttonNewProject { this, toolTip = $"New project", menuItemPtr = IDEItem(projectNewItem) };
233    // Open project
234    ToolButton buttonOpenProject { this, toolTip = $"Open project", menuItemPtr = IDEItem(projectOpenItem) };
235    // Add project to workspace
236    ToolButton buttonAddProject { this, toolTip = $"Add project to workspace", menuItemPtr = IDEItem(projectAddItem), disabled = true; };
237    // Close project
238    // ToolButton buttonCloseProject { this, toolTip = $"Close project", menuItemPtr = IDEItem(projectCloseItem), disabled = true; };
239
240    ToolSeparator separator3 { this };
241
242    // Build/Execution options
243    // Build
244    ToolButton buttonBuild { this, toolTip = $"Build project", menuItemPtr = IDEItem(projectBuildItem), disabled = true; };
245    // Re-link
246    ToolButton buttonReLink { this, toolTip = $"Relink project", menuItemPtr = IDEItem(projectLinkItem), disabled = true; };
247    // Rebuild
248    ToolButton buttonRebuild { this, toolTip = $"Rebuild project", menuItemPtr = IDEItem(projectRebuildItem), disabled = true; };
249    // Clean
250    ToolButton buttonClean { this, toolTip = $"Clean project", menuItemPtr = IDEItem(projectCleanItem), disabled = true; };
251    // Real Clean
252    // ToolButton buttonRealClean { this, toolTip = $"Real clean project", menuItemPtr = IDEItem(projectRealCleanItem), disabled = true; };
253    // Regenerate Makefile
254    ToolButton buttonRegenerateMakefile { this, toolTip = $"Regenerate Makefile", menuItemPtr = IDEItem(projectRegenerateItem), disabled = true; };
255    // Compile actual file
256    // Execute
257    ToolButton buttonRun { this, toolTip = $"Run", menuItemPtr = IDEItem(projectRunItem), disabled = true; };
258 #ifdef IDE_SHOW_INSTALL_MENU_BUTTON
259    ToolButton buttonInstall { this, toolTip = $"Install", menuItemPtr = IDEItem(projectInstallItem), disabled = true; };
260 #endif
261
262    ToolSeparator separator4 { this };
263
264    // Debug options
265    // Start/Resume
266    ToolButton buttonDebugStartResume { this, toolTip = $"Start", menuItemPtr = IDEItem(debugStartResumeItem), disabled = true; };
267    // Restart
268    ToolButton buttonDebugRestart { this, toolTip = $"Restart", menuItemPtr = IDEItem(debugRestartItem), disabled = true; };
269    // Pause
270    ToolButton buttonDebugPause { this, toolTip = $"Break", menuItemPtr = IDEItem(debugBreakItem), disabled = true; };
271    // Stop
272    ToolButton buttonDebugStop { this, toolTip = $"Stop", menuItemPtr = IDEItem(debugStopItem), disabled = true; };
273    // Breakpoints
274    //ToolButton buttonRun { this, toolTip = $"Run", menuItemPtr = IDEItem(projectRunItem) };
275    // F11
276    ToolButton buttonDebugStepInto { this, toolTip = $"Step Into", menuItemPtr = IDEItem(debugStepIntoItem), disabled = true; };
277    // F10
278    ToolButton buttonDebugStepOver { this, toolTip = $"Step Over", menuItemPtr = IDEItem(debugStepOverItem), disabled = true; };
279    // Shift+F11
280    ToolButton buttonDebugStepOut { this, toolTip = $"Step Out", menuItemPtr = IDEItem(debugStepOutItem), disabled = true; };
281    // Shift+F10
282    ToolButton buttonDebugSkipStepOver { this, toolTip = $"Step Over Skipping Breakpoints", menuItemPtr = IDEItem(debugSkipStepOverItem), disabled = true; };
283
284    ToolSeparator separator5 { this };
285
286    Window spacer5 { this, size = { 4 } };
287
288    DropBox activeConfig
289    {
290       this, toolTip = $"Active Configuration(s)", size = { 160 }, disabled = true;
291       bool NotifySelect(DropBox dropBox, DataRow row, Modifiers mods)
292       {
293          if(row)
294             ide.workspace.SelectActiveConfig(row.string);
295          return true;
296       }
297    };
298
299    Window spacer6 { this, size = { 4 } };
300
301    DropBox activeCompiler
302    {
303       this, toolTip = $"Active Compiler", size = { 160 }, disabled = true;
304       bool NotifySelect(DropBox dropBox, DataRow row, Modifiers mods)
305       {
306          if(ide.workspace && ide.projectView && row && strcmp(row.string, ide.workspace.activeCompiler))
307          {
308             bool silent = ide.projectView.buildInProgress == none ? false : true;
309             CompilerConfig compiler = ideSettings.GetCompilerConfig(row.string);
310             ide.workspace.activeCompiler = row.string;
311             ide.projectView.ShowOutputBuildLog(!silent);
312             if(!silent)
313                ide.projectView.DisplayCompiler(compiler, false);
314             for(prj : ide.workspace.projects)
315                ide.projectView.ProjectPrepareCompiler(prj, compiler, silent);
316             delete compiler;
317             ide.workspace.Save();
318          }
319          return true;
320       }
321    };
322
323    DropBox activeBitDepth
324    {
325       this, toolTip = $"Active Bit Length", size = { 60 }, disabled = true;
326       bool NotifySelect(DropBox dropBox, DataRow row, Modifiers mods)
327       {
328          if(ide.workspace && ide.projectView && row)
329          {
330             bool silent = ide.projectView.buildInProgress == none ? false : true;
331             CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.activeCompiler);
332             ide.workspace.bitDepth = (int)row.tag;
333             ide.projectView.ShowOutputBuildLog(!silent);
334             if(!silent)
335                ide.projectView.DisplayCompiler(compiler, false);
336             for(prj : ide.workspace.projects)
337                ide.projectView.ProjectPrepareCompiler(prj, compiler, silent);
338             delete compiler;
339             ide.workspace.Save();
340          }
341          return true;
342       }
343    };
344
345    Window spacer7 { this, size = { 4 } };
346
347    void IDEToolbar()
348    {
349       DataRow row;
350       row = activeBitDepth.AddString("Auto");
351       row.tag = 0;
352       activeBitDepth.AddString("32 bit").tag = 32;
353       activeBitDepth.AddString("64 bit").tag = 64;
354       activeBitDepth.currentRow = row;
355    }
356 }
357
358 class IDEMainFrame : Window
359 {
360    background = formColor;
361    borderStyle = sizable;
362    hasMaximize = true;
363    hasMinimize = true;
364    hasClose = true;
365    minClientSize = { 600, 300 };
366    hasMenuBar = true;
367    icon = { ":icon.png" };
368    text = titleECEREIDE;
369 #if 0 //def _DEBUG
370    //stayOnTop = true;
371    size = { 800, 600 };
372    anchor = { top = 0, right = 0, bottom = 0 };
373 #else
374    state = maximized;
375    anchor = { left = 0, top = 0, right = 0, bottom = 0 };
376 #endif
377
378    Stacker stack
379    {
380       this;
381       menu = { };
382       isActiveClient = true;
383       gap = 0;
384       direction = vertical;
385       background = formColor;
386       anchor = { left = 0, top = 0, right = 0, bottom = 0 };
387    };
388    IDEToolbar toolBar
389    {
390       stack, ideWorkSpace;
391
392       void OnDestroy(void)
393       {
394          ((IDEWorkSpace)master).toolBar = null;
395       }
396    };
397    IDEWorkSpace ideWorkSpace { stack, this, toolBar = toolBar };
398 }
399
400 define ide = ideMainFrame.ideWorkSpace;
401
402 class IDEWorkSpace : Window
403 {
404    background = Color { 85, 85, 85 };
405
406    //tabCycle = true;
407    hasVertScroll = true;
408    hasHorzScroll = true;
409    hasStatusBar = true;
410    isActiveClient = true;
411    anchor = { left = 0, top = 0, right = 0, bottom = 0 };
412    menu = Menu {  };
413    IDEToolbar toolBar;
414
415    MenuItem * driverItems, * skinItems, * languageItems;
416    StatusField pos { width = 150 };
417    StatusField ovr, caps, num;
418    DualPipe documentor;
419
420    BitmapResource back                 { ":ecereBack.jpg", window = this };
421    BitmapResource bmpBp                { ":codeMarks/breakpoint.png", window = this };
422    BitmapResource bmpBpDisabled        { ":codeMarks/breakpointDisabled.png", window = this };
423    BitmapResource bmpBpHalf            { ":codeMarks/breakpointHalf.png", window = this };
424    BitmapResource bmpBpHalfDisabled    { ":codeMarks/breakpointHalfDisabled.png", window = this };
425    BitmapResource bmpCursor            { ":codeMarks/cursor.png", window = this };
426    BitmapResource bmpCursorError       { ":codeMarks/cursorError.png", window = this };
427    BitmapResource bmpTopFrame          { ":codeMarks/topFrame.png", window = this };
428    BitmapResource bmpTopFrameError     { ":codeMarks/topFrameError.png", window = this };
429    BitmapResource bmpTopFrameHalf      { ":codeMarks/topFrameHalf.png", window = this };
430    BitmapResource bmpTopFrameHalfError { ":codeMarks/topFrameHalfError.png", window = this };
431
432    BuildOutputMode rightClickMenuBuildOutputMode;
433
434    Debugger debugger { };
435
436    ProjectView projectView;
437
438    OutputView outputView
439    {
440       parent = this;
441
442       void OnGotoError(const char * line, bool noParsing)
443       {
444          CompilerConfig compiler = ide.workspace ? ideSettings.GetCompilerConfig(ide.workspace.activeCompiler) : null;
445          const char * objectFileExt = compiler ? compiler.objectFileExt : objectDefaultFileExt;
446          ide.GoToError(line, noParsing, objectFileExt);
447       }
448
449       void OnCodeLocationParseAndGoTo(const char * line)
450       {
451          CompilerConfig compiler = ide.workspace ? ideSettings.GetCompilerConfig(ide.workspace.activeCompiler) : null;
452          const char * objectFileExt = compiler ? compiler.objectFileExt : objectDefaultFileExt;
453          ide.CodeLocationParseAndGoTo(line, ide.findInFilesDialog.findProject, ide.findInFilesDialog.findDir, objectFileExt);
454       }
455
456       bool OnKeyDown(Key key, unichar ch)
457       {
458          switch(key)
459          {
460             case escape:
461                if(activeBox != findBox || !ide.findInFilesDialog || !ide.findInFilesDialog.SearchAbort())
462                   ide.ShowCodeEditor();
463                break;
464             default:
465             {
466                OutputView::OnKeyDown(key, ch);
467                break;
468             }
469          }
470          return true;
471       }
472
473       bool OnClose(bool parentClosing)
474       {
475          visible = false;
476          if(!parentClosing)
477             ide.RepositionWindows(false);
478          return parentClosing;
479       }
480    };
481
482    CallStackView callStackView
483    {
484       parent = this, font = { panelFont.faceName, panelFont.size, panelFont.bold, panelFont.italic };
485
486       void OnSelectFrame(int frameIndex)
487       {
488          ide.debugger.GoToStackFrameLine(frameIndex, true, true);
489          if(frameIndex >= 0)
490             ide.debugger.SelectFrame(frameIndex);
491       }
492
493       void OnToggleBreakpoint()
494       {
495          Debugger debugger = ide.debugger;
496          if(debugger.activeFrame && debugger.activeFrame.absoluteFile)
497          {
498             int line = debugger.activeFrame.line;
499             debugger.ToggleBreakpoint(debugger.activeFrame.absoluteFile, line);
500             Update(null);
501             {
502                CodeEditor codeEditor = (CodeEditor)ide.FindWindow(debugger.activeFrame.absoluteFile);
503                if(codeEditor) { codeEditor.Update(null); Activate(); }
504             }
505          }
506       }
507
508       bool OnKeyDown(Key key, unichar ch)
509       {
510          switch(key)
511          {
512             case escape: ide.ShowCodeEditor(); break;
513          }
514          return true;
515       }
516
517       bool OnClose(bool parentClosing)
518       {
519          visible = false;
520          if(!parentClosing)
521             ide.RepositionWindows(false);
522          return parentClosing;
523       }
524
525       void OnRedraw(Surface surface)
526       {
527          Debugger debugger = ide.debugger;
528          Frame activeFrame = debugger.activeFrame;
529          if(activeFrame)
530          {
531             bool error;
532             int lineCursor, lineTopFrame;
533             int lineH, scrollY, boxH;
534             BitmapResource bmp;
535             Breakpoint bp = null;
536
537             boxH = clientSize.h;
538             scrollY = editBox.scroll.y;
539             displaySystem.FontExtent(editBox.font.font, " ", 1, null, &lineH);
540             //activeThread = debugger.activeThread;
541             //hitThread = debugger.hitThread;
542             debugger.GetCallStackCursorLine(&error, &lineCursor, &lineTopFrame);
543
544             // TODO: improve bp drawing... it should be visible even if it's not on the activeFrame
545             if(activeFrame.absoluteFile)
546             {
547                for(i : ide.workspace.breakpoints; i.type == user)
548                {
549                   if(i.absoluteFilePath && i.absoluteFilePath[0] &&
550                      !fstrcmp(i.absoluteFilePath, activeFrame.absoluteFile) &&
551                      activeFrame.line == i.line)
552                   {
553                      bp = i;
554                      break;
555                   }
556                }
557             }
558             if(bp)
559                DrawLineMarginIcon(surface,
560                      /*(lineCursor == 1 || lineTopFrame == 1) ? */ide.bmpBpHalf/* : ide.bmpBp*/,
561                      lineCursor /*1*/, lineH, scrollY, boxH);
562             /*
563             if(activeThread && activeThread == hitThread && debugger.bpHit && debugger.bpHit.type == user)
564                DrawLineMarginIcon(surface,
565                      (lineCursor == 1 || lineTopFrame == 1) ? ide.bmpBpHalf : ide.bmpBp,
566                      1, lineH, scrollY, boxH);
567             */
568             DrawLineMarginIcon(surface, error ? ide.bmpCursorError : ide.bmpCursor, lineCursor, lineH, scrollY, boxH);
569             if(bp && lineCursor == 1) //activeThread && activeThread == hitThread && debugger.bpHit && debugger.bpHit.type == user)
570                bmp = error ? ide.bmpTopFrameHalfError : ide.bmpTopFrameHalf;
571             else
572                bmp = error ? ide.bmpTopFrameError : ide.bmpTopFrame;
573             DrawLineMarginIcon(surface, bmp, lineTopFrame, lineH, scrollY, boxH);
574          }
575          if(editBox.horzScroll && editBox.horzScroll.visible)
576          {
577             surface.SetBackground(control);
578             surface.Area(0, editBox.clientSize.h, 20 - 1, clientSize.h - 1);
579          }
580       }
581    };
582
583    WatchesView watchesView { parent = this };
584    ThreadsView threadsView
585    {
586       parent = this, font = { panelFont.faceName, panelFont.size, panelFont.bold, panelFont.italic };
587
588       bool OnKeyDown(Key key, unichar ch)
589       {
590          switch(key)
591          {
592             case escape: ide.ShowCodeEditor(); break;
593          }
594          return true;
595       }
596
597       bool OnClose(bool parentClosing)
598       {
599          visible = false;
600          if(!parentClosing)
601             ide.RepositionWindows(false);
602          return parentClosing;
603       }
604
605       void OnSelectThread(int threadId)
606       {
607          if(threadId)
608             ide.debugger.SelectThread(threadId);
609       }
610
611       bool OnGetThreadsInfo(int * activeThread, int * hitThread, int * signalThread)
612       {
613          bool result = false;
614          Debugger debugger = ide.debugger;
615          *activeThread = debugger.activeThread;
616          *hitThread = debugger.hitThread;
617          *signalThread = debugger.signalThread;
618          result = true;
619          return result;
620       }
621    };
622    BreakpointsView breakpointsView { parent = this };
623
624    ToolBox toolBox { parent = this, visible = false };
625    Sheet sheet { parent = this, visible = false };
626
627    char * tmpPrjDir;
628    property char * tmpPrjDir { set { delete tmpPrjDir; if(value) tmpPrjDir = CopyString(value); } get { return tmpPrjDir; } };
629
630    Menu fileMenu { menu, $"File", f, hasMargin = true };
631       MenuItem fileNewItem
632       {
633          fileMenu, $"New", n, ctrlN;
634          bitmap = { ":actions/docNew.png" };
635          bool NotifySelect(MenuItem selection, Modifiers mods)
636          {
637             Window currentDoc = activeClient;
638             bool maximizeDoc = ((currentDoc && currentDoc.state == maximized) || (!currentDoc && !projectView));
639             Window document = (Window)NewCodeEditor(this, maximizeDoc ? maximized : normal, false);
640             RepositionWindows(false);
641             document.NotifySaved = DocumentSaved;
642             return true;
643          }
644       };
645       MenuItem fileOpenItem
646       {
647          fileMenu, $"Open...", o, ctrlO;
648          bitmap = { ":actions/docOpen.png" };
649          bool NotifySelect(MenuItem selection, Modifiers mods)
650          {
651             if(!projectView && ideSettings.ideFileDialogLocation)
652                ideFileDialog.currentDirectory = ideSettings.ideFileDialogLocation;
653             for(;;)
654             {
655                if(ideFileDialog.Modal() == ok)
656                {
657                   bool gotWhatWeWant = false;
658                   int c;
659                   int numSelections = ideFileDialog.numSelections;
660                   const char * const * multiFilePaths = ideFileDialog.multiFilePaths;
661
662                   for(c = 0; c < numSelections; c++)
663                   {
664                      if(OpenFile(multiFilePaths[c], false, true, fileTypes[ideFileDialog.fileType].typeExtension, no, normal, mods.ctrl && mods.shift))
665                         gotWhatWeWant = true;
666                   }
667                   if(gotWhatWeWant ||
668                      MessageBox { type = yesNo, master = this, text = $"Error opening file",
669                      contents = $"Open a different file?" }.Modal() == no)
670                   {
671                      if(!projectView && gotWhatWeWant)
672                         ChangeFileDialogsDirectory(ideFileDialog.currentDirectory, true);
673                      ide.RepositionWindows(false);
674                      break;
675                   }
676                }
677                else
678                   break;
679             }
680             return true;
681          }
682       };
683       MenuItem fileCloseItem { fileMenu, $"Close", c, ctrlF4, NotifySelect = MenuFileClose };
684       MenuDivider { fileMenu };
685       MenuItem fileSaveItem
686       {
687          fileMenu, $"Save", s, ctrlS, bitmap = { ":actions/docSave.png" };
688
689          // For the toolbar button; clients can still override that for the menu item
690          bool Window::NotifySelect(MenuItem selection, Modifiers mods)
691          {
692             Window w = activeClient;
693             if(w)
694                w.MenuFileSave(null, 0);
695             return true;
696          }
697       };
698       MenuItem fileSaveAsItem { fileMenu, $"Save As...", a };
699       MenuItem fileSaveAllItem { fileMenu, $"Save All", l, NotifySelect = MenuFileSaveAll, bitmap = { ":actions/docSaveAll.png" } };
700       MenuDivider { fileMenu };
701       MenuItem findInFiles
702       {
703          fileMenu, $"Find In Files...", f, Key { f, ctrl = true , shift = true };
704          bool NotifySelect(MenuItem selection, Modifiers mods)
705          {
706             findInFilesDialog.replaceMode = false;
707             findInFilesDialog.Show();
708             return true;
709          }
710       };
711       MenuItem replaceInFiles
712       {
713          fileMenu, $"Replace In Files...", e, Key { r, ctrl = true , shift = true };
714          bool NotifySelect(MenuItem selection, Modifiers mods)
715          {
716             findInFilesDialog.replaceMode = true;
717             findInFilesDialog.Show();
718             return true;
719          }
720       };
721       MenuDivider { fileMenu };
722       MenuItem globalSettingsItem
723       {
724          fileMenu, $"Global Settings...", g;
725          bool NotifySelect(MenuItem selection, Modifiers mods)
726          {
727             globalSettingsDialog.master = this;
728             if(ide.workspace && ide.workspace.activeCompiler)
729                globalSettingsDialog.workspaceActiveCompiler = ide.workspace.activeCompiler;
730             else if(ideSettings.defaultCompiler)
731                globalSettingsDialog.workspaceActiveCompiler = ideSettings.defaultCompiler;
732             globalSettingsDialog.Modal();
733             return true;
734          }
735       };
736       MenuDivider { fileMenu };
737       Menu 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 && !mods.shift)
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 && !mods.shift)
788                {
789                   char * command = PrintString("ecere-ide ", file);
790                   Execute(command);
791                   delete command;
792                }
793                else
794                   OpenFile(file, false, true, (mods.ctrl && mods.shift) ? "txt" : 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.activeCompiler);
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.activeCompiler);
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.activeCompiler && !strcmp(compiler.name, workspace.activeCompiler))
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.activeCompiler);
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.OpenPreviouslyOpenedFiles(noParsing);
2352                         workspace.holdTracking = true;
2353                         ide.RepositionWindows(false);
2354                         workspace.holdTracking = false;
2355
2356                         workspace.timer.Start();
2357
2358 #if !defined(__WIN32__)
2359                         // Valgrind Debug menu updates
2360                         debugUseValgrindItem.checked = workspace.useValgrind;
2361
2362                         debugValgrindNoLeakCheckItem.checked      = workspace.vgLeakCheck == no;
2363                         debugValgrindSummaryLeakCheckItem.checked = workspace.vgLeakCheck == summary;
2364                         debugValgrindYesLeakCheckItem.checked     = workspace.vgLeakCheck == yes;
2365                         debugValgrindFullLeakCheckItem.checked    = workspace.vgLeakCheck == full;
2366
2367                         debugValgrindRSDefaultItem.checked = workspace.vgRedzoneSize == -1;
2368                         debugValgrindRS0Item.checked       = workspace.vgRedzoneSize == 0;
2369                         debugValgrindRS16Item.checked      = workspace.vgRedzoneSize == 16;
2370                         debugValgrindRS32Item.checked      = workspace.vgRedzoneSize == 32;
2371                         debugValgrindRS64Item.checked      = workspace.vgRedzoneSize == 64;
2372                         debugValgrindRS128Item.checked     = workspace.vgRedzoneSize == 128;
2373                         debugValgrindRS256Item.checked     = workspace.vgRedzoneSize == 256;
2374                         debugValgrindRS512Item.checked     = workspace.vgRedzoneSize == 512;
2375
2376                         debugValgrindTrackOriginsItem.checked = workspace.vgTrackOrigins;
2377 #endif
2378
2379                         findInFilesDialog.mode = FindInFilesMode::project;
2380                         findInFilesDialog.currentDirectory = ide.project.topNode.path;
2381
2382                         {
2383                            char location[MAX_LOCATION];
2384                            StripLastDirectory(ide.project.topNode.path, location);
2385                            ChangeProjectFileDialogDirectory(location);
2386                         }
2387
2388                         break;
2389                      }
2390                      else
2391                      {
2392                         if(MessageBox { type = yesNo, master = this, text = $"Error opening project", contents = $"Open a different project?" }.Modal() == yes)
2393                         {
2394                            ideProjectFileDialog.text = openProjectFileDialogTitle;
2395                            if(ideProjectFileDialog.Modal() == cancel)
2396                               return null;
2397                            filePath = ideProjectFileDialog.filePath;
2398                            GetExtension(filePath, extension);
2399                         }
2400                         else
2401                            return null;
2402                      }
2403                   }
2404                }
2405             }
2406             else
2407                return null;
2408          }
2409          else if(openMethod == add)
2410          {
2411             if(workspace)
2412             {
2413                Project prj = null;
2414                char slashFilePath[MAX_LOCATION];
2415                GetSlashPathBuffer(slashFilePath, filePath);
2416                for(p : workspace.projects)
2417                {
2418                   if(!fstrcmp(p.filePath, slashFilePath))
2419                   {
2420                      prj = p;
2421                      break;
2422                   }
2423                }
2424                if(prj)
2425                {
2426                   MessageBox { type = ok, parent = parent, master = this, text = $"Same Project",
2427                         contents = $"This project is already present in workspace." }.Modal();
2428                }
2429                else
2430                {
2431                   prj = LoadProject(filePath, null);
2432                   if(prj)
2433                   {
2434                      const char * activeConfigName = null;
2435                      CompilerConfig compiler = ideSettings.GetCompilerConfig(workspace.activeCompiler);
2436                      prj.StartMonitoring();
2437                      workspace.AddProject(prj, null);
2438                      if(toolBar.activeConfig.currentRow && toolBar.activeConfig.currentRow != toolBar.activeConfig.firstRow &&
2439                            toolBar.activeConfig.currentRow.string && toolBar.activeConfig.currentRow.string[0])
2440                         activeConfigName = toolBar.activeConfig.currentRow.string;
2441                      if(activeConfigName)
2442                      {
2443                         for(cfg : prj.configurations)
2444                         {
2445                            if(cfg.name && !strcmp(cfg.name, activeConfigName))
2446                            {
2447                               prj.config = cfg;
2448                               break;
2449                            }
2450                         }
2451                      }
2452                      if(projectView)
2453                         projectView.AddNode(prj.topNode, null);
2454                      workspace.modified = true;
2455                      workspace.Save();
2456                      findInFilesDialog.AddProjectItem(prj);
2457                      projectView.ShowOutputBuildLog(true);
2458                      projectView.DisplayCompiler(compiler, false);
2459                      projectView.ProjectUpdateMakefileForAllConfigs(prj);
2460                      delete compiler;
2461
2462                      {
2463                         char location[MAX_LOCATION];
2464                         StripLastDirectory(prj.topNode.path, location);
2465                         ChangeProjectFileDialogDirectory(location);
2466                      }
2467
2468                      // projectView is associated with the main project and not with the one just added but
2469                      return projectView; // just to let the caller know something was opened
2470                   }
2471                }
2472             }
2473             else
2474                return null;
2475          }
2476       }
2477       else if(!strcmp(extension, "bmp") || !strcmp(extension, "pcx") ||
2478             !strcmp(extension, "jpg") || !strcmp(extension, "gif") ||
2479             !strcmp(extension, "jpeg") || !strcmp(extension, "png"))
2480       {
2481          if(FileExists(filePath))
2482             document = PictureEdit { hasMaximize = true, hasMinimize = true, hasClose = true, borderStyle = sizable,
2483                                        hasVertScroll = true, hasHorzScroll = true, parent = this, state = state,
2484                                        visible = visible, bitmapFile = filePath, OnClose = PictureEditOnClose/*why?--GenericDocumentOnClose*/;
2485                                     };
2486          if(!document)
2487             MessageBox { type = ok, master = this, text = filePath, contents = $"File doesn't exist." }.Modal();
2488       }
2489 #ifndef NO3D
2490       else if(!strcmp(extension, "3ds"))
2491       {
2492          if(FileExists(filePath))
2493             document = ModelView { hasMaximize = true, hasMinimize = true, hasClose = true, borderStyle = sizable,
2494                                     hasVertScroll = true, hasHorzScroll = true, parent = this, state = state,
2495                                     visible = visible, modelFile = filePath, OnClose = ModelViewOnClose/*why?--GenericDocumentOnClose*/
2496                                     };
2497
2498          if(!document)
2499             MessageBox { type = ok, master = this, text = filePath, contents = $"File doesn't exist." }.Modal();
2500       }
2501 #endif
2502       else if(!strcmp(extension, "txt") || !strcmp(extension, "text") ||
2503             !strcmp(extension, "nfo") || !strcmp(extension, "info") ||
2504             !strcmp(extension, "htm") || !strcmp(extension, "html") ||
2505             !strcmp(extension, "css") || !strcmp(extension, "php") ||
2506             !strcmp(extension, "js"))
2507       {
2508          CodeEditor editor { parent = this, state = state, visible = false, noParsing = noParsing };
2509          editor.updatingCode = true;
2510          if(editor.LoadFile(filePath))
2511          {
2512             document = editor;
2513             editor.visible = true;
2514          }
2515          else
2516             delete editor;
2517          needFileModified = false;
2518       }
2519       else
2520       {
2521          CodeEditor editor { parent = this, state = state, visible = false, noParsing = noParsing };
2522          if(editor.LoadFile(filePath))
2523          {
2524             document = editor;
2525             editor.visible = true;
2526          }
2527          else
2528             delete editor;
2529          needFileModified = false;
2530       }
2531
2532       if(document && (document._class == class(PictureEdit) ||
2533             document._class == class(ModelView)))
2534       {
2535          document.Create();
2536          if(document)
2537          {
2538             document.fileName = filePath;
2539             if(workspace && !workspace.holdTracking)
2540                workspace.UpdateOpenedFileInfo(filePath, opened);
2541          }
2542       }
2543
2544       if(!document && createIfFails != no)
2545       {
2546          if(createIfFails != yes && !needFileModified &&
2547                MessageBox { type = yesNo, master = this, text = filePath, contents = $"File doesn't exist. Create?" }.Modal() == yes)
2548             createIfFails = yes;
2549          if(createIfFails == yes || createIfFails == whatever)
2550          {
2551             document = (Window)NewCodeEditor(this, maximizeDoc ? maximized : normal, true);
2552             if(document)
2553                document.fileName = filePath;
2554          }
2555       }
2556
2557       if(document)
2558       {
2559          if(projectView && document._class == class(CodeEditor) && workspace)
2560             workspace.RestorePreviouslyOpenedFileState((CodeEditor)document);
2561
2562          if(needFileModified)
2563             document.OnFileModified = OnFileModified;
2564          document.NotifySaved = DocumentSaved;
2565          if(maximizeDoc && document.hasMaximize)
2566             document.state = maximized;
2567
2568          if(isProject)
2569             ideSettings.AddRecentProject(document.fileName);
2570          else
2571             ideSettings.AddRecentFile(document.fileName);
2572          ide.UpdateRecentMenus();
2573          ide.AdjustFileMenus();
2574          settingsContainer.Save();
2575
2576          return document;
2577       }
2578       else
2579          return null;
2580    }
2581
2582    // TOCHECK: I can't use a generic one for both ModelView and PictureEdit both derived from Window
2583    /*bool Window::GenericDocumentOnClose(bool parentClosing)
2584    {
2585       if(!parentClosing && ide.workspace)
2586          ide.workspace.UpdateOpenedFileInfo(fileName, unknown);
2587       return true;
2588    }*/
2589    bool ModelView::ModelViewOnClose(bool parentClosing)
2590    {
2591       if(!parentClosing && ide.workspace)
2592          ide.workspace.UpdateOpenedFileInfo(fileName, unknown);
2593       return true;
2594    }
2595    bool PictureEdit::PictureEditOnClose(bool parentClosing)
2596    {
2597       if(!parentClosing && ide.workspace)
2598          ide.workspace.UpdateOpenedFileInfo(fileName, unknown);
2599       return true;
2600    }
2601
2602    /*
2603    void OnUnloadGraphics(Window window)
2604    {
2605       display.ClearMaterials();
2606       display.ClearTextures();
2607       display.ClearMeshes();
2608    }
2609    */
2610
2611    void UpdateStateLight(StatusField fld, bool on)
2612    {
2613       fld.color = on ? lime : Color { 128,128,128 };
2614       fld.backColor = on ? dimGray : 0;
2615       fld.bold = on;
2616    }
2617
2618    bool OnActivate(bool active, Window swap, bool * goOnWithActivation, bool direct)
2619    {
2620       UpdateStateLight(caps, app.GetKeyState(capsState));
2621       UpdateStateLight(num, app.GetKeyState(numState));
2622       return true;
2623    }
2624
2625    bool OnKeyDown(Key key, unichar ch)
2626    {
2627       switch(key)
2628       {
2629          case b: projectView.Update(null); break;
2630          case capsLock: UpdateStateLight(caps, app.GetKeyState(capsState)); break;
2631          case numLock:  UpdateStateLight(num, app.GetKeyState(numState)); break;
2632       }
2633       return true;
2634    }
2635
2636    bool OnKeyUp(Key key, unichar ch)
2637    {
2638       switch(key)
2639       {
2640          case capsLock: UpdateStateLight(caps, app.GetKeyState(capsState)); break;
2641          case numLock:  UpdateStateLight(num, app.GetKeyState(numState)); break;
2642       }
2643       return true;
2644    }
2645
2646    void GoToError(const char * line, bool noParsing, const char * objectFileExt)
2647    {
2648       if(projectView)
2649          projectView.GoToError(line, noParsing, objectFileExt);
2650    }
2651
2652    FileAttribs GoToCodeSelectFile(const char * filePath, const char * dir, Project prj, ProjectNode * node, char * selectedPath, const char * objectFileExt)
2653    {
2654       FileAttribs result { };
2655       FileAttribs fileAttribs;
2656       if(filePath[0])
2657       {
2658          if(prj)
2659             strcpy(selectedPath, prj.topNode.path);
2660          else if(dir && dir[0])
2661             strcpy(selectedPath, dir);
2662          else
2663             selectedPath[0] = '\0';
2664          PathCat(selectedPath, filePath);
2665
2666          if((fileAttribs = FileExists(selectedPath)).isFile)
2667             result = fileAttribs;
2668          else if(workspace)
2669          {
2670             bool done = false;
2671             for(p : workspace.projects)
2672             {
2673                strcpy(selectedPath, p.topNode.path);
2674                PathCat(selectedPath, filePath);
2675                if((fileAttribs = FileExists(selectedPath)).isFile)
2676                {
2677                   done = true;
2678                   result = fileAttribs;
2679                   break;
2680                }
2681             }
2682             if(!done)
2683             {
2684                Project project;
2685                ProjectNode n = null;
2686                for(p : workspace.projects)
2687                {
2688                   if((n = p.topNode.Find(filePath, false)))
2689                   {
2690                      n.GetFullFilePath(selectedPath, true);
2691                      if((fileAttribs = FileExists(selectedPath)).isFile)
2692                      {
2693                         if(node) *node = n;
2694                         result = fileAttribs;
2695                         break;
2696                      }
2697                   }
2698                }
2699                if(!n && (n = workspace.GetObjectFileNode(filePath, &project, selectedPath, objectFileExt)) && project &&
2700                      (fileAttribs = FileExists(selectedPath)).isFile)
2701                {
2702                   if(node) *node = n;
2703                   result = fileAttribs;
2704                }
2705             }
2706          }
2707       }
2708       return result;
2709    }
2710
2711    void CodeLocationParseAndGoTo(const char * text, Project project, const char * dir, const char * objectFileExt)
2712    {
2713       char *s = null;
2714       const char *path = text;
2715       char *colon = strchr(text, ':');
2716       char filePath[MAX_LOCATION] = "";
2717       char completePath[MAX_LOCATION];
2718       int line = 0, col = 0;
2719       int len = strlen(text);
2720       Project prj = null;
2721       FileAttribs fileAttribs;
2722
2723       // support for valgrind output
2724       if((s = strstr(text, "==")) && s == text && (s = strstr(s+2, "==")) && (s = strstr(s+2, ":")) && (s = strstr(s+1, ":")))
2725       {
2726          colon = s;
2727          for(; s>text; s--)
2728          {
2729             if(*s == '(')
2730             {
2731                path = s+1;
2732                break;
2733             }
2734          }
2735          /*for(s=colon; *s; s++)
2736          {
2737             if(*s == ')')
2738             {
2739                *s = '\0';;
2740                break;
2741             }
2742          }*/
2743          //*colon = '\0';
2744          //line = atoi(colon+1);
2745       }
2746       // support for "Found n match(es) in "file/path";
2747       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)
2748       {
2749          path = s+1;
2750       }
2751       else
2752       {
2753          if(colon && (colon[1] == '/' || colon[1] == '\\'))
2754          {
2755             path = (colon - 1 > path) ? colon - 1 : path;
2756             colon = strstr(colon + 1, ":");
2757          }
2758          if(*path == '*' && (s = strchr(path+1, '*')))
2759             path = s+1;
2760          while(isspace(*path)) path++;
2761       }
2762       if(*path == '(')
2763       {
2764          char * close = strchr(path, ')');
2765          if(close)
2766          {
2767             char name[256];
2768             strncpy(name, path+1, close - path - 1);
2769             name[close - path - 1] = '\0';
2770             for(p : ide.workspace.projects)
2771             {
2772                if(!strcmp(p.name, name))
2773                {
2774                   path = close + 1;
2775                   prj = p;
2776                   break;
2777                }
2778             }
2779          }
2780       }
2781       if(!prj)
2782          prj = project ? project : (dir ? null : ide.project);
2783       if(colon)
2784       {
2785          strncpy(filePath, path, colon - path);
2786          filePath[colon - path] = '\0';
2787          line = atoi(colon + 1);
2788          colon = strstr(colon + 1, ":");
2789          if(colon)
2790             col = atoi(colon + 1);
2791       }
2792       else if(path - 1 >= text && *(path - 1) == '\"')
2793       {
2794          colon = strchr(path, '\"');
2795          if(colon)
2796          {
2797             strncpy(filePath, path, colon - path);
2798             filePath[colon - path] = '\0';
2799          }
2800       }
2801       else if(path && !colon)
2802       {
2803          strcpy(filePath, path);
2804       }
2805
2806       if((fileAttribs = GoToCodeSelectFile(filePath, dir, prj, null, completePath, objectFileExt)))
2807          CodeLocationGoTo(completePath, fileAttribs, line, col);
2808    }
2809
2810    void CodeLocationGoTo(const char * path, const FileAttribs fileAttribs, int line, int col)
2811    {
2812       if(fileAttribs.isFile)
2813       {
2814          char ext[MAX_EXTENSION];
2815          GetExtension(path, ext);
2816          strlwr(ext);
2817          if(binaryDocExt.Find(ext))
2818             ShellOpen(path);
2819          else if(!strcmp(ext, "a") || !strcmp(ext, "o") || !strcmp(ext, "bc") ||
2820                !strcmp(ext, "lib") || !strcmp(ext, "dll") || !strcmp(ext, "exe"))
2821          {
2822             char dirPath[MAX_LOCATION];
2823             StripLastDirectory(path, dirPath);
2824             ShellOpen(dirPath);
2825          }
2826          else
2827          {
2828             CodeEditor codeEditor = (CodeEditor)OpenFile(path, false, true, !strcmpi(ext, "epj") ? "txt" : ext, no, normal, false);
2829             if(codeEditor && codeEditor._class == class(CodeEditor) && line)
2830             {
2831                EditBox editBox = codeEditor.editBox;
2832                editBox.GoToLineNum(line - 1);
2833                editBox.GoToPosition(editBox.line, line - 1, col ? (col - 1) : 0);
2834             }
2835          }
2836       }
2837       else if(fileAttribs.isDirectory)
2838          ShellOpen(path);
2839    }
2840
2841    void OnRedraw(Surface surface)
2842    {
2843       Bitmap bitmap = back.bitmap;
2844       if(bitmap)
2845          surface.Blit(bitmap, (clientSize.w - bitmap.width) / 2, (clientSize.h - bitmap.height) / 2, 0, 0, bitmap.width, bitmap.height);
2846    }
2847
2848    void SheetSelected(SheetType sheetSelected)
2849    {
2850       if(activeChild == sheet)
2851       {
2852          if(sheetSelected == methods)
2853          {
2854             viewPropertiesItem.accelerator = f4;
2855             viewPropertiesItem.parent = viewMenu;
2856             viewMethodsItem.parent = null;
2857          }
2858          else
2859          {
2860             viewMethodsItem.accelerator = f4;
2861             viewMethodsItem.parent = viewMenu;
2862             viewPropertiesItem.parent = null;
2863          }
2864       }
2865       else
2866       {
2867          viewMethodsItem.parent = viewMenu;
2868          viewPropertiesItem.parent = viewMenu;
2869          if(sheetSelected == methods)
2870          {
2871             viewMethodsItem.accelerator = f4;
2872             viewPropertiesItem.accelerator = 0;
2873          }
2874          else
2875          {
2876             viewMethodsItem.accelerator = 0;
2877             viewPropertiesItem.accelerator = f4;
2878          }
2879       }
2880    }
2881
2882    void OnActivateClient(Window client, Window previous)
2883    {
2884       //if(!client || client != previous)
2885       {
2886          Class dataType;
2887          if(!client || client != previous)
2888          {
2889             if(previous)
2890                dataType = previous._class;
2891             if(previous && !strcmp(dataType.name, "CodeEditor"))
2892             {
2893                ((CodeEditor)previous).UpdateFormCode();
2894             }
2895             else if(previous && !strcmp(dataType.name, "Designer"))
2896             {
2897                ((Designer)previous).codeEditor.UpdateFormCode();
2898             }
2899          }
2900
2901          if(client)
2902             dataType = client._class;
2903          if(client && !strcmp(dataType.name, "CodeEditor"))
2904          {
2905             CodeEditor codeEditor = (CodeEditor)client;
2906             SetPrivateModule(codeEditor.privateModule);
2907             SetCurrentContext(codeEditor.globalContext);
2908             SetTopContext(codeEditor.globalContext);
2909             SetGlobalContext(codeEditor.globalContext);
2910
2911             SetDefines(&codeEditor.defines);
2912             SetImports(&codeEditor.imports);
2913
2914             SetActiveDesigner(codeEditor.designer);
2915
2916             sheet.codeEditor = codeEditor;
2917             toolBox.codeEditor = codeEditor;
2918
2919             viewDesignerItem.parent = viewMenu;
2920             if(activeChild != codeEditor)
2921             {
2922                viewCodeItem.parent = viewMenu;
2923                viewDesignerItem.accelerator = 0;
2924                viewCodeItem.accelerator = f8;
2925             }
2926             else
2927             {
2928                viewCodeItem.parent = null;
2929                viewDesignerItem.accelerator = f8;
2930             }
2931          }
2932          else if(client && !strcmp(dataType.name, "Designer"))
2933          {
2934             CodeEditor codeEditor = ((Designer)client).codeEditor;
2935             if(codeEditor)
2936             {
2937                SetPrivateModule(codeEditor.privateModule);
2938                SetCurrentContext(codeEditor.globalContext);
2939                SetTopContext(codeEditor.globalContext);
2940                SetGlobalContext(codeEditor.globalContext);
2941                SetDefines(&codeEditor.defines);
2942                SetImports(&codeEditor.imports);
2943             }
2944             else
2945             {
2946                SetPrivateModule(null);
2947                SetCurrentContext(null);
2948                SetTopContext(null);
2949                SetGlobalContext(null);
2950                SetDefines(null);
2951                SetImports(null);
2952             }
2953
2954             SetActiveDesigner((Designer)client);
2955
2956             sheet.codeEditor = codeEditor;
2957             toolBox.codeEditor = codeEditor;
2958
2959             viewCodeItem.parent = viewMenu;
2960             if(activeChild != client)
2961             {
2962                viewDesignerItem.parent = viewMenu;
2963                viewDesignerItem.accelerator = f8;
2964                viewCodeItem.accelerator = 0;
2965             }
2966             else
2967             {
2968                viewDesignerItem.parent = null;
2969                viewCodeItem.accelerator = f8;
2970             }
2971          }
2972          else
2973          {
2974             if(!client && !projectView && sheet.visible)
2975             {
2976                if(sheet)
2977                   sheet.visible = false;
2978                toolBox.visible = false;
2979             }
2980             if(sheet)
2981                sheet.codeEditor = null;
2982             toolBox.codeEditor = null;
2983             SetActiveDesigner(null);
2984
2985             viewDesignerItem.parent = null;
2986             viewCodeItem.parent = null;
2987          }
2988          if(sheet)
2989             SheetSelected(sheet.sheetSelected);
2990       }
2991
2992       projectCompileItem = null;
2993
2994       if(statusBar)
2995       {
2996          statusBar.Clear();
2997          if(client && client._class == eSystem_FindClass(__thisModule, "CodeEditor")) // !strcmp(client._class.name, "CodeEditor")
2998          {
2999             CodeEditor codeEditor = (CodeEditor)client;
3000             EditBox editBox = codeEditor.editBox;
3001
3002             statusBar.AddField(pos);
3003
3004             caps = { width = 40, text = $"CAPS" };
3005             statusBar.AddField(caps);
3006             UpdateStateLight(caps, app.GetKeyState(capsState));
3007
3008             ovr = { width = 36, text = $"OVR" };
3009             statusBar.AddField(ovr);
3010             UpdateStateLight(ovr, (editBox && editBox.overwrite));
3011
3012             num = { width = 36, text = $"NUM" };
3013             statusBar.AddField(num);
3014             UpdateStateLight(num, app.GetKeyState(numState));
3015
3016             //statusBar.text = "Ready";
3017
3018             if(projectView && projectView.project)
3019             {
3020                bool isCObject = false;
3021                ProjectNode node = projectView.GetNodeFromWindow(client, null, true, false, null);
3022                if(!node && (node = projectView.GetNodeFromWindow(client, null, true, true, null)))
3023                   isCObject = true;
3024                if(node)
3025                {
3026                   char nodeName[MAX_FILENAME];
3027                   char name[MAX_FILENAME+96];
3028                   if(isCObject)
3029                      ChangeExtension(node.name, "c", nodeName);
3030                   sprintf(name, $"Compile %s", isCObject ? nodeName : node.name);
3031                   projectCompileItem =
3032                   {
3033                      copyText = true, text = name, c, ctrlF7, disabled = projectView.buildInProgress;
3034
3035                      bool NotifySelect(MenuItem selection, Modifiers mods)
3036                      {
3037                         if(projectView)
3038                         {
3039                            bool isCObject = false;
3040                            bool isExcluded = false;
3041                            ProjectNode node = projectView.GetNodeForCompilationFromWindow(activeClient, true, &isExcluded, &isCObject);
3042                            if(node)
3043                            {
3044                               if(isExcluded)
3045                                  ide.outputView.buildBox.Logf($"%s %s is excluded from current build configuration.\n", isCObject ? "Object file" : "File", node.name);
3046                               else
3047                               {
3048                                  List<ProjectNode> nodes { };
3049                                  nodes.Add(node);
3050                                  projectView.Compile(node.project, nodes, normal, isCObject ? cObject : normal);
3051                                  delete nodes;
3052                               }
3053                            }
3054                         }
3055                         return true;
3056                      }
3057                   };
3058                   projectMenu.AddDynamic(projectCompileItem, ide, false);
3059                }
3060             }
3061          }
3062          else
3063          {
3064             caps = ovr = num = null;
3065          }
3066       }
3067    }
3068
3069    bool OnClose(bool parentClosing)
3070    {
3071       //return !projectView.buildInProgress;
3072       if(projectView && projectView.buildInProgress)
3073          return false;
3074       if(DontTerminateDebugSession($"Close IDE"))
3075          return false;
3076       if(findInFilesDialog)
3077          findInFilesDialog.SearchStop();
3078       if(workspace)
3079       {
3080          workspace.timer.Stop();
3081          workspace.Save();
3082       }
3083       ideMainFrame.Destroy(0);
3084       return true;
3085    }
3086
3087    bool OnPostCreate()
3088    {
3089       int c;
3090       bool passThrough = false;
3091       bool debugWorkDir = false;
3092       char * passDebugWorkDir = null;
3093       bool openAsText = false;
3094       DynamicString passArgs { };
3095       int ptArg = 0;
3096
3097       for(c = 1; c<app.argc; c++)
3098       {
3099          if(passThrough)
3100          {
3101             const char * arg = app.argv[c];
3102             char * buf = new char[strlen(arg)*2+1];
3103             if(ptArg++ > 0)
3104                passArgs.concat(" ");
3105             PassArg(buf, arg);
3106             passArgs.concat(buf);
3107             delete buf;
3108          }
3109          else if(debugWorkDir)
3110          {
3111             passDebugWorkDir = CopyString(app.argv[c]);
3112             StripQuotes(passDebugWorkDir, passDebugWorkDir);
3113             debugWorkDir = false;
3114          }
3115          else if(!strcmp(app.argv[c], "-t"))
3116             openAsText = true;
3117          else if(!strcmp(app.argv[c], "-no-parsing"))
3118             ide.noParsing = true;
3119          else if(!strcmp(app.argv[c], "-debug-start"))
3120             ide.debugStart = true;
3121          else if(!strcmp(app.argv[c], "-debug-work-dir"))
3122             debugWorkDir = true;
3123          else if(!strcmp(app.argv[c], "-@"))
3124             passThrough = true;
3125          else
3126          {
3127             char fullPath[MAX_LOCATION];
3128             char parentPath[MAX_LOCATION];
3129             char ext[MAX_EXTENSION];
3130             bool isProject;
3131             FileAttribs dirAttribs;
3132             GetWorkingDir(fullPath, MAX_LOCATION);
3133             PathCat(fullPath, app.argv[c]);
3134             StripLastDirectory(fullPath, parentPath);
3135             GetExtension(app.argv[c], ext);
3136             isProject = !openAsText && !strcmpi(ext, "epj");
3137
3138             if(isProject && c > 1 + (ide.debugStart ? 1 : 0)) continue;
3139
3140             // Create directory for projects (only)
3141             if(((dirAttribs = FileExists(parentPath)) && dirAttribs.isDirectory) || isProject)
3142             {
3143                if(isProject && !FileExists(fullPath))
3144                {
3145                   char name[MAX_LOCATION];
3146                   NewProjectDialog newProjectDialog;
3147
3148                   if(projectView)
3149                   {
3150                      projectView.visible = false;
3151                      if(!projectView.Destroy(0))
3152                         return true;
3153                   }
3154
3155                   newProjectDialog = { master = this };
3156
3157                   strcpy(name, app.argv[c]);
3158                   StripExtension(name);
3159                   GetLastDirectory(name, name);
3160                   newProjectDialog.projectName.contents = name;
3161                   newProjectDialog.projectName.NotifyModified(newProjectDialog, newProjectDialog.projectName);
3162                   newProjectDialog.locationEditBox.path = parentPath;
3163                   newProjectDialog.NotifyModifiedLocation(newProjectDialog.locationEditBox);
3164
3165                   incref newProjectDialog;
3166                   newProjectDialog.Modal();
3167                   if(projectView)
3168                   {
3169                      ideSettings.AddRecentProject(projectView.fileName);
3170                      ide.UpdateRecentMenus();
3171                      settingsContainer.Save();
3172                   }
3173                   delete newProjectDialog;
3174                   // Open only one project
3175                   break;
3176                }
3177                else
3178                   ide.OpenFile(fullPath, app.argFilesCount > 1, true, openAsText ? "txt" : null, yes, normal, false);
3179             }
3180             else if(strstr(fullPath, "http://") == fullPath)
3181                ide.OpenFile(fullPath, app.argFilesCount > 1, true, openAsText ? "txt" : null, yes, normal, false);
3182          }
3183       }
3184       if(passThrough && projectView && projectView.project && workspace)
3185          workspace.commandLineArgs = passArgs;
3186       if(passDebugWorkDir && projectView && projectView.project && workspace)
3187       {
3188          workspace.debugDir = passDebugWorkDir;
3189          delete passDebugWorkDir;
3190       }
3191
3192       UpdateToolBarActiveConfigs(false);
3193       UpdateToolBarActiveCompilers();
3194       delete passArgs;
3195       return true;
3196    }
3197
3198    void OnDestroy()
3199    {
3200       // IS THIS NEEDED? WASN'T HERE BEFORE...  Crashes on getting node's projectView otherwise
3201       if(projectView)
3202       {
3203          projectView.visible = false;
3204          projectView.Destroy(0);
3205          projectView = null;
3206       }
3207 #ifdef GDB_DEBUG_GUI
3208       gdbDialog.Destroy(0);
3209       delete gdbDialog;
3210 #endif
3211    }
3212
3213    void SetPath(bool projectsDirs, CompilerConfig compiler, ProjectConfig config, int bitDepth)
3214    {
3215       int c, len, count;
3216       char * newList;
3217       char * oldPaths[128];
3218       String oldList = new char[maxPathLen];
3219       Array<String> newExePaths { };
3220       //Map<String, bool> exePathExists { };
3221       bool found = false;
3222 #if defined(__unix__) || defined(__APPLE__)
3223       Array<String> newLibPaths { };
3224       Map<String, bool> libPathExists { };
3225 #endif
3226
3227       if(projectsDirs)
3228       {
3229          for(prj : workspace.projects)
3230          {
3231             DirExpression targetDirExp;
3232
3233             // SKIP FIRST PROJECT...
3234             if(prj == workspace.projects.firstIterator.data) continue;
3235
3236             // NOTE: Right now the additional project config dir will be
3237             //       obtained when the debugger is started, so toggling it
3238             //       while building will change which library gets used.
3239             //       To go with the initial state, e.g. when F5 was pressed,
3240             //       we nould need to keep a list of all project's active
3241             //       config upon startup.
3242             targetDirExp = prj.GetTargetDir(compiler, prj.config, bitDepth);
3243
3244             /*if(prj.config.targetType == sharedLibrary && prj.config.debug)
3245                cfg = prj.config;
3246             else
3247             {
3248                for(cfg = prj.configurations.first; cfg; cfg = cfg.next)
3249                   if(cfg.targetType == sharedLibrary && cfg.debug && !strcmpi(cfg.name, "Debug"))
3250                      break;
3251                if(!cfg)
3252                {
3253                   for(cfg = prj.configurations.first; cfg; cfg = cfg.next)
3254                      if(cfg.targetType == sharedLibrary && cfg.debug)
3255                         break;
3256                }
3257             }*/
3258             if(targetDirExp.dir)
3259             {
3260                char buffer[MAX_LOCATION];
3261 #if defined(__WIN32__)
3262                Array<String> paths = newExePaths;
3263 #else
3264                Array<String> paths = newLibPaths;
3265 #endif
3266                GetSystemPathBuffer(buffer, prj.topNode.path);
3267                PathCat(buffer, targetDirExp.dir);
3268                for(p : paths)
3269                {
3270                   if(!fstrcmp(p, buffer))
3271                   {
3272                      found = true;
3273                      break;
3274                   }
3275                }
3276                if(!found)
3277                   paths.Add(CopyString(buffer));
3278             }
3279             delete targetDirExp;
3280          }
3281       }
3282
3283       for(item : compiler.executableDirs)
3284       {
3285          DirExpression dirExpr { };
3286          dirExpr.Evaluate(item, null, compiler, null, 0);
3287          found = false;
3288
3289          for(p : newExePaths)
3290          {
3291             if(!fstrcmp(p, dirExpr.dir))
3292             {
3293                found = true;
3294                break;
3295             }
3296          }
3297          if(!found)
3298             newExePaths.Add(CopySystemPath(dirExpr.dir));
3299          delete dirExpr;
3300       }
3301
3302       GetEnvironment("PATH", oldList, maxPathLen);
3303 /*#ifdef _DEBUG
3304       printf("Old value of PATH: %s\n", oldList);
3305 #endif*/
3306       count = TokenizeWith(oldList, sizeof(oldPaths) / sizeof(char *), oldPaths, pathListSep, false);
3307       for(c = 0; c < count; c++)
3308       {
3309          found = false;
3310          for(p : newExePaths)
3311          {
3312             if(!fstrcmp(p, oldPaths[c]))
3313             {
3314                found = true;
3315                break;
3316             }
3317          }
3318          if(!found)
3319             newExePaths.Add(CopySystemPath(oldPaths[c]));
3320       }
3321
3322       len = 0;
3323       for(path : newExePaths)
3324          len += strlen(path) + 1;
3325       newList = new char[len + 1];
3326       newList[0] = '\0';
3327       for(path : newExePaths)
3328       {
3329          strcat(newList, path);
3330          strcat(newList, pathListSep);
3331       }
3332       newList[len - 1] = '\0';
3333       SetEnvironment("PATH", newList);
3334 /*#ifdef _DEBUG
3335       printf("New value of PATH: %s\n", newList);
3336 #endif*/
3337       delete newList;
3338
3339       newExePaths.Free();
3340       delete newExePaths;
3341
3342 #if defined(__unix__) || defined(__APPLE__)
3343
3344       for(item : compiler.libraryDirs)
3345       {
3346          if(!libPathExists[item])  // fstrcmp should be used
3347          {
3348             String s = CopyString(item);
3349             newLibPaths.Add(s);
3350             libPathExists[s] = true;
3351          }
3352       }
3353
3354 #if defined(__APPLE__)
3355       GetEnvironment("DYLD_LIBRARY_PATH", oldList, maxPathLen);
3356 #else
3357       GetEnvironment("LD_LIBRARY_PATH", oldList, maxPathLen);
3358 #endif
3359 /*#ifdef _DEBUG
3360       printf("Old value of [DY]LD_LIBRARY_PATH: %s\n", oldList);
3361 #endif*/
3362       count = TokenizeWith(oldList, sizeof(oldPaths) / sizeof(char *), oldPaths, pathListSep, false);
3363       for(c = 0; c < count; c++)
3364       {
3365          if(!libPathExists[oldPaths[c]])  // fstrcmp should be used
3366          {
3367             String s = CopyString(oldPaths[c]);
3368             newLibPaths.Add(s);
3369             libPathExists[s] = true;
3370          }
3371       }
3372
3373       len = 0;
3374       for(path : newLibPaths)
3375          len += strlen(path) + 1;
3376       newList = new char[len + 1];
3377       newList[0] = '\0';
3378       for(path : newLibPaths)
3379       {
3380          strcat(newList, path);
3381          strcat(newList, pathListSep);
3382       }
3383       newList[len - 1] = '\0';
3384 #if defined(__APPLE__)
3385       SetEnvironment("DYLD_LIBRARY_PATH", newList);
3386 #else
3387       SetEnvironment("LD_LIBRARY_PATH", newList);
3388 #endif
3389 /*#ifdef _DEBUG
3390       printf("New value of [DY]LD_LIBRARY_PATH: %s\n", newList);
3391 #endif*/
3392       delete newList;
3393
3394       newLibPaths.Free();
3395       delete newLibPaths;
3396       delete libPathExists;
3397 #endif
3398
3399       if(compiler.distccEnabled && compiler.distccHosts)
3400          SetEnvironment("DISTCC_HOSTS", compiler.distccHosts);
3401
3402       delete oldList;
3403    }
3404
3405    void DestroyTemporaryProjectDir()
3406    {
3407       if(tmpPrjDir && tmpPrjDir[0])
3408       {
3409          if(FileExists(tmpPrjDir).isDirectory)
3410             DestroyDir(tmpPrjDir);
3411          property::tmpPrjDir = null;
3412       }
3413    }
3414
3415    IDEWorkSpace()
3416    {
3417       // Graphics Driver Menu
3418
3419       /*
3420       app.currentSkin.selectionColor = selectionColor;
3421       app.currentSkin.selectionText = selectionText;
3422       */
3423
3424 /*
3425       driverItems = new MenuItem[app.numDrivers];
3426       for(c = 0; c < app.numDrivers; c++)
3427       {
3428          driverItems[c] = MenuItem { driversMenu, app.drivers[c], NotifySelect = NotifySelectDisplayDriver };
3429          driverItems[c].id = c;
3430          driverItems[c].isRadio = true;
3431       }
3432 */
3433       driverItems = new MenuItem[2];
3434 #if defined(__unix__)
3435          driverItems[0] = MenuItem { driversMenu, "X", NotifySelect = NotifySelectDisplayDriver };
3436          driverItems[0].id = 0;
3437          driverItems[0].isRadio = true;
3438 #else
3439          driverItems[0] = MenuItem { driversMenu, "GDI", NotifySelect = NotifySelectDisplayDriver };
3440          driverItems[0].id = 0;
3441          driverItems[0].isRadio = true;
3442 #endif
3443          driverItems[1] = MenuItem { driversMenu, "OpenGL", NotifySelect = NotifySelectDisplayDriver };
3444          driverItems[1].id = 1;
3445          driverItems[1].isRadio = true;
3446
3447 /*      skinItems = new MenuItem[app.numSkins];
3448       for(c = 0; c < app.numSkins; c++)
3449       {
3450          skinItems[c] = MenuItem {skinsMenu, app.skins[c], NotifySelect = NotifySelectDisplaySkin };
3451          skinItems[c].id = c;
3452          skinItems[c].isRadio = true;
3453       }
3454 */
3455       ideFileDialog.master = this;
3456       ideProjectFileDialog.master = this;
3457
3458       //SetDriverAndSkin();
3459       return true;
3460    }
3461
3462    void UpdateRecentMenus()
3463    {
3464       int c;
3465       Menu fileMenu = menu.FindMenu($"File");
3466       Menu recentFiles = fileMenu.FindMenu($"Recent Files");
3467       Menu recentProjects = fileMenu.FindMenu($"Recent Projects");
3468       char * itemPath = new char[MAX_LOCATION];
3469       char * itemName = new char[MAX_LOCATION+4];
3470
3471       recentFiles.Clear();
3472       c = 0;
3473
3474       for(recent : ideSettings.recentFiles)
3475       {
3476          strncpy(itemPath, recent, MAX_LOCATION); itemPath[MAX_LOCATION-1] = '\0';
3477          MakeSystemPath(itemPath);
3478          snprintf(itemName, MAX_LOCATION+4, "%d %s", 1 + c, itemPath); itemName[MAX_LOCATION+4-1] = '\0';
3479          recentFiles.AddDynamic(MenuItem { copyText = true, text = itemName, (Key)k1 + c, id = c, NotifySelect = ide.FileRecentFile }, ide, true);
3480          c++;
3481       }
3482
3483       recentProjects.Clear();
3484       c = 0;
3485       for(recent : ideSettings.recentProjects)
3486       {
3487          strncpy(itemPath, recent, MAX_LOCATION); itemPath[MAX_LOCATION-1] = '\0';
3488          MakeSystemPath(itemPath);
3489          snprintf(itemName, MAX_LOCATION+4, "%d %s", 1 + c, itemPath); itemName[MAX_LOCATION+4-1] = '\0';
3490          recentProjects.AddDynamic(MenuItem { copyText = true, text = itemName, (Key)k1 + c, id = c, NotifySelect = ide.FileRecentProject }, ide, true);
3491          c++;
3492       }
3493
3494       delete itemPath;
3495       delete itemName;
3496    }
3497
3498    ~IDEWorkSpace()
3499    {
3500       delete driverItems;
3501       delete skinItems;
3502       delete languageItems;
3503       delete ideSettings;
3504       if(documentor)
3505       {
3506          documentor.Puts("Quit\n");
3507          documentor.Wait();
3508          delete documentor;
3509       }
3510    }
3511 }
3512
3513 void DestroyDir(char * path)
3514 {
3515    RecursiveDeleteFolderFSI fsi { };
3516    fsi.Iterate(path);
3517    delete fsi;
3518 }
3519
3520 #if defined(__WIN32__)
3521 define sdkDirName = "Ecere SDK";
3522 #else
3523 define sdkDirName = "ecere";
3524 #endif
3525
3526 bool GetInstalledFileOrFolder(const char * subDir, const char * name, char * path, FileAttribs attribs)
3527 {
3528    bool found = false;
3529    char * v = new char[maxPathLen];
3530    v[0] = '\0';
3531    if(!found)
3532    {
3533       strncpy(path, settingsContainer.moduleLocation, MAX_LOCATION); path[MAX_LOCATION-1] = '\0';
3534       StripLastDirectory(path, path);
3535       PathCat(path, subDir);
3536       if(name) PathCat(path, name);
3537       if(FileExists(path) & attribs) found = true;
3538    }
3539 #if defined(__WIN32__)
3540    if(!found)
3541    {
3542       for(s : [ "ECERE_SDK_SRC", "AppData", "ALLUSERSPROFILE", "USERPROFILE", "HOMEPATH", "ProgramData", "ProgramFiles", "ProgramFiles(x86)", "SystemDrive" ])
3543       {
3544          GetEnvironment(s, v, maxPathLen);
3545          if(v[0])
3546          {
3547             strncpy(path, v, MAX_LOCATION); path[MAX_LOCATION-1] = '\0';
3548             if(!strcmp(s, "SystemDrive"))
3549                PathCat(path, "Program Files");
3550             if(strcmp(s, "ECERE_SDK_SRC"))
3551                PathCat(path, sdkDirName);
3552             PathCat(path, subDir);
3553             if(name) PathCat(path, name);
3554             if(FileExists(path) & attribs)
3555             {
3556                found = true;
3557                break;
3558             }
3559          }
3560       }
3561    }
3562 #else
3563    if(!found)
3564    {
3565       char * p = null;
3566       char * tokens[256];
3567       int c, numTokens;
3568
3569       GetEnvironment("XDG_DATA_DIRS", v, maxPathLen);
3570       numTokens = TokenizeWith(v, sizeof(tokens) / sizeof(byte *), tokens, ":", false);
3571       if(!numTokens)
3572       {
3573          p = new char[MAX_LOCATION];
3574          p[0] = '\0';
3575          strcat(p, "/usr/share");
3576          tokens[0] = p;
3577          numTokens = 1;
3578       }
3579       for(c=0; c<numTokens; c++)
3580       {
3581          strncpy(path, tokens[c], MAX_LOCATION); path[MAX_LOCATION-1] = '\0';
3582          PathCat(path, sdkDirName);
3583          PathCat(path, subDir);
3584          if(name)
3585             PathCat(path, name);
3586          if(FileExists(path) & attribs)
3587          {
3588             found = true;
3589             break;
3590          }
3591       }
3592       delete p;
3593    }
3594 #endif
3595    delete v;
3596    return found;
3597 }
3598
3599 void FindAndShellOpenInstalledFolder(const char * name)
3600 {
3601    char path[MAX_LOCATION];
3602    if(GetInstalledFileOrFolder(name, null, path, { isDirectory = true }))
3603       ShellOpen(path);
3604 }
3605
3606 void FindAndShellOpenInstalledFile(const char * subdir, const char * name)
3607 {
3608    char path[MAX_LOCATION];
3609    if(GetInstalledFileOrFolder(subdir, name, path, { isFile = true }))
3610       ShellOpen(path);
3611 }
3612
3613 class RecursiveDeleteFolderFSI : NormalFileSystemIterator
3614 {
3615    bool preserveRootFolder;
3616
3617    void OutFolder(const char * folderPath, bool isRoot)
3618    {
3619       if(!(preserveRootFolder && isRoot))
3620          RemoveDir(folderPath);
3621    }
3622
3623    bool OnFile(const char * filePath)
3624    {
3625       DeleteFile(filePath);
3626       return true;
3627    }
3628 }
3629
3630 class IDEApp : GuiApplication
3631 {
3632    //driver = "Win32Console";
3633    // driver = "OpenGL";
3634    // skin = "Aqua";
3635    //skin = "TVision";
3636
3637    TempFile includeFile { };
3638    int argFilesCount;
3639
3640    bool Init()
3641    {
3642       char ext[MAX_EXTENSION];
3643       SetLoggingMode(stdOut, null);
3644       //SetLoggingMode(debug, null);
3645
3646       settingsContainer.Load();
3647
3648       if(ideSettings.language)
3649       {
3650          const String language = GetLanguageString();
3651          if(ideSettings.language.OnCompare(language))
3652          {
3653             LanguageRestart(ideSettings.language, app, null, null, null, null, true);
3654             return false;
3655          }
3656       }
3657
3658       // First count files arg to decide whether to maximize
3659       {
3660          bool passThrough = false, debugWorkDir = false;
3661          int c;
3662          argFilesCount = 0;
3663          for(c = 1; c<app.argc; c++)
3664          {
3665             if(passThrough);
3666             else if(debugWorkDir)
3667                debugWorkDir = false;
3668             else if(!strcmp(app.argv[c], "-t"));
3669             else if(!strcmp(app.argv[c], "-no-parsing"));
3670             else if(!strcmp(app.argv[c], "-debug-start"));
3671             else if(!strcmp(app.argv[c], "-debug-work-dir"))
3672                debugWorkDir = true;
3673             else if(!strcmp(app.argv[c], "-@"))
3674                passThrough = true;
3675             else
3676                argFilesCount++;
3677          }
3678       }
3679
3680       if(app.argFilesCount > 0 && !strcmpi(GetExtension(argv[1], ext), "3ds"))
3681       {
3682          app.driver = "OpenGL";
3683          ide.driverItems[1].checked = true;
3684       }
3685       else
3686       {
3687 #if defined(__unix__) || defined(__APPLE__)
3688          app.driver = (ideSettings.displayDriver && !strcmp(ideSettings.displayDriver, "OpenGL")) ? ideSettings.displayDriver : "X";
3689 #else
3690          app.driver = (ideSettings.displayDriver && !strcmp(ideSettings.displayDriver, "OpenGL")) ? ideSettings.displayDriver : "GDI";
3691 #endif
3692          ide.driverItems[ideSettings.displayDriver && !strcmp(ideSettings.displayDriver,"OpenGL")].checked = true;
3693       }
3694
3695       {
3696          char model[MAX_LOCATION];
3697          if(GetInstalledFileOrFolder("samples", "3D/ModelViewer/models/duck/duck.3DS", model, { isFile = true }))
3698          {
3699             ide.duck.modelFile = model;
3700             ide.duck.parent = ideMainFrame;
3701          }
3702       }
3703       if(ide.duck.modelFile && !strcmpi(app.driver, "OpenGL"))
3704          ide.debugRubberDuck.disabled = false;
3705
3706       SetInIDE(true);
3707
3708       desktop.caption = titleECEREIDE;
3709       /*
3710       int c;
3711       for(c = 1; c<app.argc; c++)
3712       {
3713          char fullPath[MAX_LOCATION];
3714          GetWorkingDir(fullPath, MAX_LOCATION);
3715          PathCat(fullPath, app.argv[c]);
3716          ide.OpenFile(fullPath, app.argFilesCount > 1, true, null, yes, normal, false);
3717       }
3718       */
3719
3720       // Default to language specified by environment if no language selected
3721       if(!ideSettings.language)
3722       {
3723          ideSettings.language = GetLanguageString();
3724          settingsContainer.Save();
3725       }
3726
3727       // Default to home directory if no directory yet set up
3728       if(!ideSettings.ideProjectFileDialogLocation[0])
3729       {
3730          bool found = false;
3731          char location[MAX_LOCATION];
3732          char * home = getenv("HOME");
3733          char * homeDrive = getenv("HOMEDRIVE");
3734          char * homePath = getenv("HOMEPATH");
3735          char * userProfile = getenv("USERPROFILE");
3736          char * systemDrive = getenv("SystemDrive");
3737          if(home && FileExists(home).isDirectory)
3738          {
3739             strcpy(location, home);
3740             found = true;
3741          }
3742          if(!found && homeDrive && homePath)
3743          {
3744             strcpy(location, homeDrive);
3745             PathCat(location, homePath);
3746             if(FileExists(location).isDirectory)
3747                found = true;
3748          }
3749          if(!found && FileExists(userProfile).isDirectory)
3750          {
3751             strcpy(location, userProfile);
3752             found = true;
3753          }
3754          if(!found && FileExists(systemDrive).isDirectory)
3755          {
3756             strcpy(location, systemDrive);
3757             found = true;
3758          }
3759          if(found)
3760          {
3761             ideSettings.ideProjectFileDialogLocation = location;
3762             if(!ideSettings.ideFileDialogLocation[0])
3763                ideSettings.ideFileDialogLocation = location;
3764          }
3765       }
3766
3767       if(!LoadIncludeFile())
3768          PrintLn($"error: unable to load :crossplatform.mk file inside ide binary.");
3769
3770       // Create language menu
3771       {
3772          String language = ideSettings.language;
3773          int i = 0;
3774          bool found = false;
3775
3776          ide.languageItems = new MenuItem[languages.count];
3777          for(l : languages)
3778          {
3779             ide.languageItems[i] =
3780             {
3781                ide.languageMenu, l.name;
3782                bitmap = { l.bitmap };
3783                id = i;
3784                isRadio = true;
3785
3786                bool Window::NotifySelect(MenuItem selection, Modifiers mods)
3787                {
3788                   if(!LanguageRestart(languages[(int)selection.id].code, app, ideSettings, settingsContainer, ide, ide.projectView, false))
3789                   {
3790                      // Re-select previous selected language if aborted
3791                      String language = ideSettings.language;
3792                      int i = 0;
3793                      for(l : languages)
3794                      {
3795                         if(((!language || !language[0]) && i == 0) ||
3796                            (language && !strcmpi(l.code, language)))
3797                         {
3798                            ide.languageItems[i].checked = true;
3799                            break;
3800                         }
3801                         i++;
3802                      }
3803                   }
3804                   return true;
3805                }
3806             };
3807             i++;
3808          }
3809
3810          // Try to find country-specific language first
3811          if(language)
3812          {
3813             i = 0;
3814             for(l : languages)
3815             {
3816                if(!strcmpi(l.code, language) || (i == 0 && !strcmpi("en", language)))
3817                {
3818                   ide.languageItems[i].checked = true;
3819                   found = true;
3820                   break;
3821                }
3822                i++;
3823             }
3824          }
3825
3826          // Try generalizing locale
3827          if(!found && language)
3828          {
3829             char * under;
3830             char genericLocale[256];
3831             i = 0;
3832             strncpy(genericLocale, language, sizeof(genericLocale));
3833             genericLocale[sizeof(genericLocale)-1] = 0;
3834
3835             under = strchr(genericLocale, '_');
3836             if(under)
3837                *under = 0;
3838             if(!strcmpi(genericLocale, "zh"))
3839                strcpy(genericLocale, "zh_CN");
3840             if(strcmp(genericLocale, language))
3841             {
3842                for(l : languages)
3843                {
3844                   if(!strcmpi(l.code, genericLocale) || (i == 0 && !strcmpi("en", genericLocale)))
3845                   {
3846                      ide.languageItems[i].checked = true;
3847                      found = true;
3848                      break;
3849                   }
3850                   i++;
3851                }
3852             }
3853          }
3854
3855          if(!found)
3856             ide.languageItems[0].checked = true;
3857
3858          MenuDivider { ide.languageMenu };
3859          MenuItem
3860          {
3861             ide.languageMenu, "Help Translate";
3862
3863             bool Window::NotifySelect(MenuItem selection, Modifiers mods)
3864             {
3865                ShellOpen("http://translations.launchpad.net/ecere");
3866                return true;
3867             }
3868          };
3869       }
3870
3871       ideMainFrame.Create();
3872       if(app.argFilesCount > 1)
3873          ide.MenuWindowTileVert(null, 0);
3874       return true;
3875    }
3876
3877    bool Cycle(bool idle)
3878    {
3879       if(ide.documentor)
3880       {
3881          if(ide.documentor.Peek())
3882          {
3883             char line[1024];
3884             ide.documentor.GetLine(line, sizeof(line));
3885             if(!strcmpi(line, "Exited"))
3886             {
3887                ide.documentor.CloseInput();
3888                ide.documentor.CloseOutput();
3889                ide.documentor.Wait();
3890                delete ide.documentor;
3891             }
3892          }
3893          if(ide.documentor && ide.documentor.eof)
3894          {
3895             ide.documentor.CloseInput();
3896             ide.documentor.CloseOutput();
3897             ide.documentor.Wait();
3898             delete ide.documentor;
3899          }
3900       }
3901       return true;
3902    }
3903
3904    bool LoadIncludeFile()
3905    {
3906       bool result = false;
3907       File include = FileOpen(":crossplatform.mk", read);
3908       if(include)
3909       {
3910          File f = includeFile;
3911          if(f)
3912          {
3913             for(; !include.Eof(); )
3914             {
3915                char buffer[4096];
3916                int count = include.Read(buffer, 1, 4096);
3917                f.Write(buffer, 1, count);
3918             }
3919             result = true;
3920          }
3921          delete include;
3922       }
3923       return result;
3924    }
3925 }
3926
3927 IDEMainFrame ideMainFrame { };
3928
3929 define app = ((IDEApp)__thisModule);
3930 #ifdef _DEBUG
3931 define titleECEREIDE = $"Ecere IDE (Debug)";
3932 #else
3933 define titleECEREIDE = $"Ecere IDE";
3934 #endif